3 * Copyright (C) 2002, 2014 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/ntfs/rw.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Art Yerkes
24 * Pierre Schweitzer (pierre@reactos.org)
28 /* INCLUDES *****************************************************************/
36 /* FUNCTIONS ****************************************************************/
39 * FUNCTION: Reads data from a file
43 NtfsReadFile(PDEVICE_EXTENSION DeviceExt
,
44 PFILE_OBJECT FileObject
,
51 NTSTATUS Status
= STATUS_SUCCESS
;
53 PFILE_RECORD_HEADER FileRecord
;
54 PNTFS_ATTR_CONTEXT DataContext
;
59 BOOLEAN AllocatedBuffer
= FALSE
;
60 PCHAR ReadBuffer
= (PCHAR
)Buffer
;
63 DPRINT1("NtfsReadFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt
, FileObject
, Buffer
, Length
, ReadOffset
, IrpFlags
, LengthRead
);
69 DPRINT1("Null read!\n");
70 return STATUS_SUCCESS
;
73 Fcb
= (PNTFS_FCB
)FileObject
->FsContext
;
75 if (NtfsFCBIsCompressed(Fcb
))
77 DPRINT1("Compressed file!\n");
79 return STATUS_NOT_IMPLEMENTED
;
82 FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, DeviceExt
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
83 if (FileRecord
== NULL
)
85 DPRINT1("Not enough memory!\n");
86 return STATUS_INSUFFICIENT_RESOURCES
;
89 Status
= ReadFileRecord(DeviceExt
, Fcb
->MFTIndex
, FileRecord
);
90 if (!NT_SUCCESS(Status
))
92 DPRINT1("Can't find record!\n");
93 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
98 Status
= FindAttribute(DeviceExt
, FileRecord
, AttributeData
, Fcb
->Stream
, wcslen(Fcb
->Stream
), &DataContext
, NULL
);
99 if (!NT_SUCCESS(Status
))
101 NTSTATUS BrowseStatus
;
102 FIND_ATTR_CONTXT Context
;
103 PNTFS_ATTR_RECORD Attribute
;
105 DPRINT1("No '%S' data stream associated with file!\n", Fcb
->Stream
);
107 BrowseStatus
= FindFirstAttribute(&Context
, DeviceExt
, FileRecord
, FALSE
, &Attribute
);
108 while (NT_SUCCESS(BrowseStatus
))
110 if (Attribute
->Type
== AttributeData
)
114 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
115 Name
.MaximumLength
= Name
.Length
;
116 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
117 DPRINT1("Data stream: '%wZ' available\n", &Name
);
120 BrowseStatus
= FindNextAttribute(&Context
, &Attribute
);
122 FindCloseAttribute(&Context
);
124 ReleaseAttributeContext(DataContext
);
125 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
129 StreamSize
= AttributeDataLength(&DataContext
->Record
);
130 if (ReadOffset
>= StreamSize
)
132 DPRINT1("Reading beyond stream end!\n");
133 ReleaseAttributeContext(DataContext
);
134 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
135 return STATUS_END_OF_FILE
;
139 if (ReadOffset
+ Length
> StreamSize
)
140 ToRead
= StreamSize
- ReadOffset
;
142 RealReadOffset
= ReadOffset
;
145 if ((ReadOffset
% DeviceExt
->NtfsInfo
.BytesPerSector
) != 0 || (ToRead
% DeviceExt
->NtfsInfo
.BytesPerSector
) != 0)
147 RealReadOffset
= ROUND_DOWN(ReadOffset
, DeviceExt
->NtfsInfo
.BytesPerSector
);
148 RealLength
= ROUND_UP(ToRead
, DeviceExt
->NtfsInfo
.BytesPerSector
);
149 /* do we need to extend RealLength by one sector? */
150 if (RealLength
+ RealReadOffset
< ReadOffset
+ Length
)
152 if (RealReadOffset
+ RealLength
+ DeviceExt
->NtfsInfo
.BytesPerSector
<= AttributeAllocatedLength(&DataContext
->Record
))
153 RealLength
+= DeviceExt
->NtfsInfo
.BytesPerSector
;
157 ReadBuffer
= ExAllocatePoolWithTag(NonPagedPool
, RealLength
, TAG_NTFS
);
158 if (ReadBuffer
== NULL
)
160 DPRINT1("Not enough memory!\n");
161 ReleaseAttributeContext(DataContext
);
162 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
163 return STATUS_INSUFFICIENT_RESOURCES
;
165 AllocatedBuffer
= TRUE
;
168 DPRINT1("Effective read: %lu at %lu for stream '%S'\n", RealLength
, RealReadOffset
, Fcb
->Stream
);
169 RealLengthRead
= ReadAttribute(DeviceExt
, DataContext
, RealReadOffset
, (PCHAR
)ReadBuffer
, RealLength
);
170 if (RealLengthRead
== 0)
172 DPRINT1("Read failure!\n");
173 ReleaseAttributeContext(DataContext
);
174 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
177 ExFreePoolWithTag(ReadBuffer
, TAG_NTFS
);
182 ReleaseAttributeContext(DataContext
);
183 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
185 *LengthRead
= ToRead
;
187 DPRINT1("%lu got read\n", *LengthRead
);
191 RtlCopyMemory(Buffer
, ReadBuffer
+ (ReadOffset
- RealReadOffset
), ToRead
);
194 if (ToRead
!= Length
)
196 RtlZeroMemory(Buffer
+ ToRead
, Length
- ToRead
);
201 ExFreePoolWithTag(ReadBuffer
, TAG_NTFS
);
204 return STATUS_SUCCESS
;
209 NtfsRead(PNTFS_IRP_CONTEXT IrpContext
)
211 PDEVICE_EXTENSION DeviceExt
;
212 PIO_STACK_LOCATION Stack
;
213 PFILE_OBJECT FileObject
;
216 LARGE_INTEGER ReadOffset
;
217 ULONG ReturnedReadLength
= 0;
218 NTSTATUS Status
= STATUS_SUCCESS
;
220 PDEVICE_OBJECT DeviceObject
;
222 DPRINT("NtfsRead(IrpContext %p)\n", IrpContext
);
224 DeviceObject
= IrpContext
->DeviceObject
;
225 Irp
= IrpContext
->Irp
;
226 Stack
= IrpContext
->Stack
;
227 FileObject
= IrpContext
->FileObject
;
229 DeviceExt
= DeviceObject
->DeviceExtension
;
230 ReadLength
= Stack
->Parameters
.Read
.Length
;
231 ReadOffset
= Stack
->Parameters
.Read
.ByteOffset
;
232 Buffer
= NtfsGetUserBuffer(Irp
, BooleanFlagOn(Irp
->Flags
, IRP_PAGING_IO
));
234 Status
= NtfsReadFile(DeviceExt
,
238 ReadOffset
.u
.LowPart
,
240 &ReturnedReadLength
);
241 if (NT_SUCCESS(Status
))
243 if (FileObject
->Flags
& FO_SYNCHRONOUS_IO
)
245 FileObject
->CurrentByteOffset
.QuadPart
=
246 ReadOffset
.QuadPart
+ ReturnedReadLength
;
249 Irp
->IoStatus
.Information
= ReturnedReadLength
;
253 Irp
->IoStatus
.Information
= 0;
260 * @name NtfsWriteFile
263 * Writes a file to the disk. It presently borrows a lot of code from NtfsReadFile() and
264 * VFatWriteFileData(). It needs some more work before it will be complete; it won't handle
265 * page files, asnyc io, cached writes, etc.
268 * Points to the target disk's DEVICE_EXTENSION
271 * Pointer to a FILE_OBJECT describing the target file
274 * The data that's being written to the file
277 * The size of the data buffer being written, in bytes
280 * Offset, in bytes, from the beginning of the file. Indicates where to start
284 * TODO: flags are presently ignored in code.
286 * @param LengthWritten
287 * Pointer to a ULONG. This ULONG will be set to the number of bytes successfully written.
290 * STATUS_SUCCESS if successful, STATUS_NOT_IMPLEMENTED if a required feature isn't implemented,
291 * STATUS_INSUFFICIENT_RESOURCES if an allocation failed, STATUS_ACCESS_DENIED if the write itself fails,
292 * STATUS_PARTIAL_COPY or STATUS_UNSUCCESSFUL if ReadFileRecord() fails, or
293 * STATUS_OBJECT_NAME_NOT_FOUND if the file's data stream could not be found.
295 * @remarks Called by NtfsWrite(). It may perform a read-modify-write operation if the requested write is
296 * not sector-aligned. LengthWritten only refers to how much of the requested data has been written;
297 * extra data that needs to be written to make the write sector-aligned will not affect it.
300 NTSTATUS
NtfsWriteFile(PDEVICE_EXTENSION DeviceExt
,
301 PFILE_OBJECT FileObject
,
306 PULONG LengthWritten
)
308 NTSTATUS Status
= STATUS_NOT_IMPLEMENTED
;
310 PFILE_RECORD_HEADER FileRecord
;
311 PNTFS_ATTR_CONTEXT DataContext
;
312 ULONG AttributeOffset
;
313 ULONGLONG StreamSize
;
315 DPRINT("NtfsWriteFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt
, FileObject
, Buffer
, Length
, WriteOffset
, IrpFlags
, LengthWritten
);
324 return STATUS_SUCCESS
;
326 return STATUS_INVALID_PARAMETER
;
329 // get the File control block
330 Fcb
= (PNTFS_FCB
)FileObject
->FsContext
;
333 DPRINT("Fcb->PathName: %wS\n", Fcb
->PathName
);
334 DPRINT("Fcb->ObjectName: %wS\n", Fcb
->ObjectName
);
336 // we don't yet handle compression
337 if (NtfsFCBIsCompressed(Fcb
))
339 DPRINT("Compressed file!\n");
341 return STATUS_NOT_IMPLEMENTED
;
344 // allocate non-paged memory for the FILE_RECORD_HEADER
345 FileRecord
= ExAllocatePoolWithTag(NonPagedPool
, DeviceExt
->NtfsInfo
.BytesPerFileRecord
, TAG_NTFS
);
346 if (FileRecord
== NULL
)
348 DPRINT1("Not enough memory! Can't write %wS!\n", Fcb
->PathName
);
349 return STATUS_INSUFFICIENT_RESOURCES
;
352 // read the FILE_RECORD_HEADER from the drive (or cache)
353 DPRINT("Reading file record...\n");
354 Status
= ReadFileRecord(DeviceExt
, Fcb
->MFTIndex
, FileRecord
);
355 if (!NT_SUCCESS(Status
))
357 // We couldn't get the file's record. Free the memory and return the error
358 DPRINT1("Can't find record for %wS!\n", Fcb
->ObjectName
);
359 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
363 DPRINT("Found record for %wS\n", Fcb
->ObjectName
);
365 // Find the attribute with the data stream for our file
366 DPRINT("Finding Data Attribute...\n");
367 Status
= FindAttribute(DeviceExt
, FileRecord
, AttributeData
, Fcb
->Stream
, wcslen(Fcb
->Stream
), &DataContext
,
370 // Did we fail to find the attribute?
371 if (!NT_SUCCESS(Status
))
373 NTSTATUS BrowseStatus
;
374 FIND_ATTR_CONTXT Context
;
375 PNTFS_ATTR_RECORD Attribute
;
377 DPRINT1("No '%S' data stream associated with file!\n", Fcb
->Stream
);
379 // Couldn't find the requested data stream; print a list of streams available
380 BrowseStatus
= FindFirstAttribute(&Context
, DeviceExt
, FileRecord
, FALSE
, &Attribute
);
381 while (NT_SUCCESS(BrowseStatus
))
383 if (Attribute
->Type
== AttributeData
)
387 Name
.Length
= Attribute
->NameLength
* sizeof(WCHAR
);
388 Name
.MaximumLength
= Name
.Length
;
389 Name
.Buffer
= (PWCHAR
)((ULONG_PTR
)Attribute
+ Attribute
->NameOffset
);
390 DPRINT1("Data stream: '%wZ' available\n", &Name
);
393 BrowseStatus
= FindNextAttribute(&Context
, &Attribute
);
395 FindCloseAttribute(&Context
);
397 ReleaseAttributeContext(DataContext
);
398 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
402 // Get the size of the stream on disk
403 StreamSize
= AttributeDataLength(&DataContext
->Record
);
405 DPRINT("WriteOffset: %lu\tStreamSize: %I64u\n", WriteOffset
, StreamSize
);
407 // Are we trying to write beyond the end of the stream?
408 if (WriteOffset
+ Length
> StreamSize
)
410 // is increasing the stream size allowed?
411 if (!(Fcb
->Flags
& FCB_IS_VOLUME
) &&
412 !(IrpFlags
& IRP_PAGING_IO
))
414 LARGE_INTEGER DataSize
;
415 ULONGLONG AllocationSize
;
417 DataSize
.QuadPart
= WriteOffset
+ Length
;
419 AllocationSize
= ROUND_UP(DataSize
.QuadPart
, Fcb
->Vcb
->NtfsInfo
.BytesPerCluster
);
421 // set the attribute data length
422 Status
= SetAttributeDataLength(FileObject
, Fcb
, DataContext
, AttributeOffset
, FileRecord
, DeviceExt
, &DataSize
);
424 if (!NT_SUCCESS(Status
))
426 ReleaseAttributeContext(DataContext
);
427 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
432 // now we need to update this file's size in every directory index entry that references it
433 // (saved for a later commit)
437 // TODO - just fail for now
438 ReleaseAttributeContext(DataContext
);
439 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
441 return STATUS_ACCESS_DENIED
;
445 DPRINT("Length: %lu\tWriteOffset: %lu\tStreamSize: %I64u\n", Length
, WriteOffset
, StreamSize
);
447 // Write the data to the attribute
448 Status
= WriteAttribute(DeviceExt
, DataContext
, WriteOffset
, Buffer
, Length
, LengthWritten
);
450 // Did the write fail?
451 if (!NT_SUCCESS(Status
))
453 DPRINT1("Write failure!\n");
454 ReleaseAttributeContext(DataContext
);
455 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
460 // This should never happen:
461 if (*LengthWritten
!= Length
)
463 DPRINT1("\a\tNTFS DRIVER ERROR: length written (%lu) differs from requested (%lu), but no error was indicated!\n",
464 *LengthWritten
, Length
);
465 Status
= STATUS_UNEXPECTED_IO_ERROR
;
468 ReleaseAttributeContext(DataContext
);
469 ExFreePoolWithTag(FileRecord
, TAG_NTFS
);
478 * Handles IRP_MJ_WRITE I/O Request Packets for NTFS. This code borrows a lot from
479 * VfatWrite, and needs a lot of cleaning up. It also needs a lot more of the code
480 * from VfatWrite integrated.
483 * Points to an NTFS_IRP_CONTEXT which describes the write
486 * STATUS_SUCCESS if successful,
487 * STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
488 * STATUS_INVALID_DEVICE_REQUEST if called on the main device object,
489 * STATUS_NOT_IMPLEMENTED or STATUS_ACCESS_DENIED if a required feature isn't implemented.
490 * STATUS_PARTIAL_COPY, STATUS_UNSUCCESSFUL, or STATUS_OBJECT_NAME_NOT_FOUND if NtfsWriteFile() fails.
492 * @remarks Called by NtfsDispatch() in response to an IRP_MJ_WRITE request. Page files are not implemented.
493 * Support for large files (>4gb) is not implemented. Cached writes, file locks, transactions, etc - not implemented.
497 NtfsWrite(PNTFS_IRP_CONTEXT IrpContext
)
500 PERESOURCE Resource
= NULL
;
501 LARGE_INTEGER ByteOffset
;
503 NTSTATUS Status
= STATUS_SUCCESS
;
505 ULONG ReturnedWriteLength
= 0;
506 PDEVICE_OBJECT DeviceObject
= NULL
;
507 PDEVICE_EXTENSION DeviceExt
= NULL
;
508 PFILE_OBJECT FileObject
= NULL
;
510 ULONG BytesPerSector
;
512 DPRINT("NtfsWrite(IrpContext %p)\n", IrpContext
);
515 // This request is not allowed on the main device object
516 if (IrpContext
->DeviceObject
== NtfsGlobalData
->DeviceObject
)
518 DPRINT1("\t\t\t\tNtfsWrite is called with the main device object.\n");
520 Irp
->IoStatus
.Information
= 0;
521 return STATUS_INVALID_DEVICE_REQUEST
;
524 // get the I/O request packet
525 Irp
= IrpContext
->Irp
;
527 // get the File control block
528 Fcb
= (PNTFS_FCB
)IrpContext
->FileObject
->FsContext
;
531 DPRINT("About to write %wS\n", Fcb
->ObjectName
);
532 DPRINT("NTFS Version: %d.%d\n", Fcb
->Vcb
->NtfsInfo
.MajorVersion
, Fcb
->Vcb
->NtfsInfo
.MinorVersion
);
534 // setup some more locals
535 FileObject
= IrpContext
->FileObject
;
536 DeviceObject
= IrpContext
->DeviceObject
;
537 DeviceExt
= DeviceObject
->DeviceExtension
;
538 BytesPerSector
= DeviceExt
->StorageDevice
->SectorSize
;
539 Length
= IrpContext
->Stack
->Parameters
.Write
.Length
;
541 // get the file offset we'll be writing to
542 ByteOffset
= IrpContext
->Stack
->Parameters
.Write
.ByteOffset
;
543 if (ByteOffset
.u
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&&
544 ByteOffset
.u
.HighPart
== -1)
546 ByteOffset
.QuadPart
= Fcb
->RFCB
.FileSize
.QuadPart
;
549 DPRINT("ByteOffset: %I64u\tLength: %lu\tBytes per sector: %lu\n", ByteOffset
.QuadPart
,
550 Length
, BytesPerSector
);
552 if (ByteOffset
.u
.HighPart
&& !(Fcb
->Flags
& FCB_IS_VOLUME
))
554 // TODO: Support large files
555 DPRINT1("FIXME: Writing to large files is not yet supported at this time.\n");
556 return STATUS_INVALID_PARAMETER
;
559 // Is this a non-cached write? A non-buffered write?
560 if (IrpContext
->Irp
->Flags
& (IRP_PAGING_IO
| IRP_NOCACHE
) || (Fcb
->Flags
& FCB_IS_VOLUME
) ||
561 IrpContext
->FileObject
->Flags
& FILE_NO_INTERMEDIATE_BUFFERING
)
563 // non-cached and non-buffered writes must be sector aligned
564 if (ByteOffset
.u
.LowPart
% BytesPerSector
!= 0 || Length
% BytesPerSector
!= 0)
566 DPRINT1("Non-cached writes and non-buffered writes must be sector aligned!\n");
567 return STATUS_INVALID_PARAMETER
;
573 DPRINT1("Null write!\n");
575 IrpContext
->Irp
->IoStatus
.Information
= 0;
577 // FIXME: Doesn't accurately detect when a user passes NULL to WriteFile() for the buffer
578 if (Irp
->UserBuffer
== NULL
&& Irp
->MdlAddress
== NULL
)
580 // FIXME: Update last write time
581 return STATUS_SUCCESS
;
584 return STATUS_INVALID_PARAMETER
;
588 if (Fcb
->Flags
& FCB_IS_VOLUME
)
590 Resource
= &DeviceExt
->DirResource
;
592 else if (IrpContext
->Irp
->Flags
& IRP_PAGING_IO
)
594 Resource
= &Fcb
->PagingIoResource
;
598 Resource
= &Fcb
->MainResource
;
601 // acquire exclusive access to the Resource
602 if (!ExAcquireResourceExclusiveLite(Resource
, BooleanFlagOn(IrpContext
->Flags
, IRPCONTEXT_CANWAIT
)))
604 return STATUS_CANT_WAIT
;
607 /* From VfatWrite(). Todo: Handle file locks
608 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
609 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
611 if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
613 Status = STATUS_FILE_LOCK_CONFLICT;
618 // Is this an async request to a file?
619 if (!(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
) && !(Fcb
->Flags
& FCB_IS_VOLUME
))
621 DPRINT1("FIXME: Async writes not supported in NTFS!\n");
623 ExReleaseResourceLite(Resource
);
624 return STATUS_NOT_IMPLEMENTED
;
627 // get the buffer of data the user is trying to write
628 Buffer
= NtfsGetUserBuffer(Irp
, BooleanFlagOn(Irp
->Flags
, IRP_PAGING_IO
));
632 Status
= NtfsLockUserBuffer(Irp
, Length
, IoReadAccess
);
634 // were we unable to lock the buffer?
635 if (!NT_SUCCESS(Status
))
637 DPRINT1("Unable to lock user buffer!\n");
639 ExReleaseResourceLite(Resource
);
643 DPRINT("Existing File Size(Fcb->RFCB.FileSize.QuadPart): %I64u\n", Fcb
->RFCB
.FileSize
.QuadPart
);
644 DPRINT("About to write the data. Length: %lu\n", Length
);
646 // TODO: handle HighPart of ByteOffset (large files)
649 Status
= NtfsWriteFile(DeviceExt
,
655 &ReturnedWriteLength
);
657 IrpContext
->Irp
->IoStatus
.Status
= Status
;
659 // was the write successful?
660 if (NT_SUCCESS(Status
))
662 // TODO: Update timestamps
664 if (FileObject
->Flags
& FO_SYNCHRONOUS_IO
)
666 // advance the file pointer
667 FileObject
->CurrentByteOffset
.QuadPart
= ByteOffset
.QuadPart
+ ReturnedWriteLength
;
670 IrpContext
->PriorityBoost
= IO_DISK_INCREMENT
;
674 DPRINT1("Write not Succesful!\tReturned length: %lu\n", ReturnedWriteLength
);
677 Irp
->IoStatus
.Information
= ReturnedWriteLength
;
679 // Note: We leave the user buffer that we locked alone, it's up to the I/O manager to unlock and free it
681 ExReleaseResourceLite(Resource
);