xref: /reactos/drivers/filesystems/ntfs/rw.c (revision 84344399)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002, 2014 ReactOS Team
4  *
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.
9  *
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.
14  *
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.
18  *
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)
25  *                   Trevor Thompson
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #include <ntddk.h>
31 #include "ntfs.h"
32 
33 #define NDEBUG
34 #include <debug.h>
35 
36 /* FUNCTIONS ****************************************************************/
37 
38 /*
39  * FUNCTION: Reads data from a file
40  */
41 static
42 NTSTATUS
43 NtfsReadFile(PDEVICE_EXTENSION DeviceExt,
44              PFILE_OBJECT FileObject,
45              PUCHAR Buffer,
46              ULONG Length,
47              ULONG ReadOffset,
48              ULONG IrpFlags,
49              PULONG LengthRead)
50 {
51     NTSTATUS Status = STATUS_SUCCESS;
52     PNTFS_FCB Fcb;
53     PFILE_RECORD_HEADER FileRecord;
54     PNTFS_ATTR_CONTEXT DataContext;
55     ULONG RealLength;
56     ULONG RealReadOffset;
57     ULONG RealLengthRead;
58     ULONG ToRead;
59     BOOLEAN AllocatedBuffer = FALSE;
60     PCHAR ReadBuffer = (PCHAR)Buffer;
61     ULONGLONG StreamSize;
62 
63     DPRINT("NtfsReadFile(%p, %p, %p, %lu, %lu, %lx, %p)\n", DeviceExt, FileObject, Buffer, Length, ReadOffset, IrpFlags, LengthRead);
64 
65     *LengthRead = 0;
66 
67     if (Length == 0)
68     {
69         DPRINT1("Null read!\n");
70         return STATUS_SUCCESS;
71     }
72 
73     Fcb = (PNTFS_FCB)FileObject->FsContext;
74 
75     if (NtfsFCBIsCompressed(Fcb))
76     {
77         DPRINT1("Compressed file!\n");
78         UNIMPLEMENTED;
79         return STATUS_NOT_IMPLEMENTED;
80     }
81 
82     if (NtfsFCBIsEncrypted(Fcb))
83     {
84         DPRINT1("Encrypted file!\n");
85         UNIMPLEMENTED;
86         return STATUS_NOT_IMPLEMENTED;
87     }
88 
89     FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
90     if (FileRecord == NULL)
91     {
92         DPRINT1("Not enough memory!\n");
93         return STATUS_INSUFFICIENT_RESOURCES;
94     }
95 
96     Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
97     if (!NT_SUCCESS(Status))
98     {
99         DPRINT1("Can't find record!\n");
100         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
101         return Status;
102     }
103 
104 
105     Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext, NULL);
106     if (!NT_SUCCESS(Status))
107     {
108         NTSTATUS BrowseStatus;
109         FIND_ATTR_CONTXT Context;
110         PNTFS_ATTR_RECORD Attribute;
111 
112         DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream);
113 
114         BrowseStatus = FindFirstAttribute(&Context, DeviceExt, FileRecord, FALSE, &Attribute);
115         while (NT_SUCCESS(BrowseStatus))
116         {
117             if (Attribute->Type == AttributeData)
118             {
119                 UNICODE_STRING Name;
120 
121                 Name.Length = Attribute->NameLength * sizeof(WCHAR);
122                 Name.MaximumLength = Name.Length;
123                 Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
124                 DPRINT1("Data stream: '%wZ' available\n", &Name);
125             }
126 
127             BrowseStatus = FindNextAttribute(&Context, &Attribute);
128         }
129         FindCloseAttribute(&Context);
130 
131         ReleaseAttributeContext(DataContext);
132         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
133         return Status;
134     }
135 
136     StreamSize = AttributeDataLength(DataContext->pRecord);
137     if (ReadOffset >= StreamSize)
138     {
139         DPRINT1("Reading beyond stream end!\n");
140         ReleaseAttributeContext(DataContext);
141         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
142         return STATUS_END_OF_FILE;
143     }
144 
145     ToRead = Length;
146     if (ReadOffset + Length > StreamSize)
147         ToRead = StreamSize - ReadOffset;
148 
149     RealReadOffset = ReadOffset;
150     RealLength = ToRead;
151 
152     if ((ReadOffset % DeviceExt->NtfsInfo.BytesPerSector) != 0 || (ToRead % DeviceExt->NtfsInfo.BytesPerSector) != 0)
153     {
154         RealReadOffset = ROUND_DOWN(ReadOffset, DeviceExt->NtfsInfo.BytesPerSector);
155         RealLength = ROUND_UP(ToRead, DeviceExt->NtfsInfo.BytesPerSector);
156         /* do we need to extend RealLength by one sector? */
157         if (RealLength + RealReadOffset < ReadOffset + Length)
158         {
159             if (RealReadOffset + RealLength + DeviceExt->NtfsInfo.BytesPerSector <= AttributeAllocatedLength(DataContext->pRecord))
160                 RealLength += DeviceExt->NtfsInfo.BytesPerSector;
161         }
162 
163 
164         ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength, TAG_NTFS);
165         if (ReadBuffer == NULL)
166         {
167             DPRINT1("Not enough memory!\n");
168             ReleaseAttributeContext(DataContext);
169             ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
170             return STATUS_INSUFFICIENT_RESOURCES;
171         }
172         AllocatedBuffer = TRUE;
173     }
174 
175     DPRINT("Effective read: %lu at %lu for stream '%S'\n", RealLength, RealReadOffset, Fcb->Stream);
176     RealLengthRead = ReadAttribute(DeviceExt, DataContext, RealReadOffset, (PCHAR)ReadBuffer, RealLength);
177     if (RealLengthRead == 0)
178     {
179         DPRINT1("Read failure!\n");
180         ReleaseAttributeContext(DataContext);
181         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
182         if (AllocatedBuffer)
183         {
184             ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
185         }
186         return Status;
187     }
188 
189     ReleaseAttributeContext(DataContext);
190     ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
191 
192     *LengthRead = ToRead;
193 
194     DPRINT("%lu got read\n", *LengthRead);
195 
196     if (AllocatedBuffer)
197     {
198         RtlCopyMemory(Buffer, ReadBuffer + (ReadOffset - RealReadOffset), ToRead);
199     }
200 
201     if (ToRead != Length)
202     {
203         RtlZeroMemory(Buffer + ToRead, Length - ToRead);
204     }
205 
206     if (AllocatedBuffer)
207     {
208         ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
209     }
210 
211     return STATUS_SUCCESS;
212 }
213 
214 
215 NTSTATUS
216 NtfsRead(PNTFS_IRP_CONTEXT IrpContext)
217 {
218     PDEVICE_EXTENSION DeviceExt;
219     PIO_STACK_LOCATION Stack;
220     PFILE_OBJECT FileObject;
221     PVOID Buffer;
222     ULONG ReadLength;
223     LARGE_INTEGER ReadOffset;
224     ULONG ReturnedReadLength = 0;
225     NTSTATUS Status = STATUS_SUCCESS;
226     PIRP Irp;
227     PDEVICE_OBJECT DeviceObject;
228 
229     DPRINT("NtfsRead(IrpContext %p)\n", IrpContext);
230 
231     DeviceObject = IrpContext->DeviceObject;
232     Irp = IrpContext->Irp;
233     Stack = IrpContext->Stack;
234     FileObject = IrpContext->FileObject;
235 
236     DeviceExt = DeviceObject->DeviceExtension;
237     ReadLength = Stack->Parameters.Read.Length;
238     ReadOffset = Stack->Parameters.Read.ByteOffset;
239     Buffer = NtfsGetUserBuffer(Irp, BooleanFlagOn(Irp->Flags, IRP_PAGING_IO));
240 
241     Status = NtfsReadFile(DeviceExt,
242                           FileObject,
243                           Buffer,
244                           ReadLength,
245                           ReadOffset.u.LowPart,
246                           Irp->Flags,
247                           &ReturnedReadLength);
248     if (NT_SUCCESS(Status))
249     {
250         if (FileObject->Flags & FO_SYNCHRONOUS_IO)
251         {
252             FileObject->CurrentByteOffset.QuadPart =
253                 ReadOffset.QuadPart + ReturnedReadLength;
254         }
255 
256         Irp->IoStatus.Information = ReturnedReadLength;
257     }
258     else
259     {
260         Irp->IoStatus.Information = 0;
261     }
262 
263     return Status;
264 }
265 
266 /**
267 * @name NtfsWriteFile
268 * @implemented
269 *
270 * Writes a file to the disk. It presently borrows a lot of code from NtfsReadFile() and
271 * VFatWriteFileData(). It needs some more work before it will be complete; it won't handle
272 * page files, asnyc io, cached writes, etc.
273 *
274 * @param DeviceExt
275 * Points to the target disk's DEVICE_EXTENSION
276 *
277 * @param FileObject
278 * Pointer to a FILE_OBJECT describing the target file
279 *
280 * @param Buffer
281 * The data that's being written to the file
282 *
283 * @Param Length
284 * The size of the data buffer being written, in bytes
285 *
286 * @param WriteOffset
287 * Offset, in bytes, from the beginning of the file. Indicates where to start
288 * writing data.
289 *
290 * @param IrpFlags
291 * TODO: flags are presently ignored in code.
292 *
293 * @param CaseSensitive
294 * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
295 * if an application opened the file with the FILE_FLAG_POSIX_SEMANTICS flag.
296 *
297 * @param LengthWritten
298 * Pointer to a ULONG. This ULONG will be set to the number of bytes successfully written.
299 *
300 * @return
301 * STATUS_SUCCESS if successful, STATUS_NOT_IMPLEMENTED if a required feature isn't implemented,
302 * STATUS_INSUFFICIENT_RESOURCES if an allocation failed, STATUS_ACCESS_DENIED if the write itself fails,
303 * STATUS_PARTIAL_COPY or STATUS_UNSUCCESSFUL if ReadFileRecord() fails, or
304 * STATUS_OBJECT_NAME_NOT_FOUND if the file's data stream could not be found.
305 *
306 * @remarks Called by NtfsWrite(). It may perform a read-modify-write operation if the requested write is
307 * not sector-aligned. LengthWritten only refers to how much of the requested data has been written;
308 * extra data that needs to be written to make the write sector-aligned will not affect it.
309 *
310 */
311 NTSTATUS NtfsWriteFile(PDEVICE_EXTENSION DeviceExt,
312                        PFILE_OBJECT FileObject,
313                        const PUCHAR Buffer,
314                        ULONG Length,
315                        ULONG WriteOffset,
316                        ULONG IrpFlags,
317                        BOOLEAN CaseSensitive,
318                        PULONG LengthWritten)
319 {
320     NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
321     PNTFS_FCB Fcb;
322     PFILE_RECORD_HEADER FileRecord;
323     PNTFS_ATTR_CONTEXT DataContext;
324     ULONG AttributeOffset;
325     ULONGLONG StreamSize;
326 
327     DPRINT("NtfsWriteFile(%p, %p, %p, %lu, %lu, %x, %s, %p)\n",
328            DeviceExt,
329            FileObject,
330            Buffer,
331            Length,
332            WriteOffset,
333            IrpFlags,
334            (CaseSensitive ? "TRUE" : "FALSE"),
335            LengthWritten);
336 
337     *LengthWritten = 0;
338 
339     ASSERT(DeviceExt);
340 
341     if (Length == 0)
342     {
343         if (Buffer == NULL)
344             return STATUS_SUCCESS;
345         else
346             return STATUS_INVALID_PARAMETER;
347     }
348 
349     // get the File control block
350     Fcb = (PNTFS_FCB)FileObject->FsContext;
351     ASSERT(Fcb);
352 
353     DPRINT("Fcb->PathName: %wS\n", Fcb->PathName);
354     DPRINT("Fcb->ObjectName: %wS\n", Fcb->ObjectName);
355 
356     // we don't yet handle compression
357     if (NtfsFCBIsCompressed(Fcb))
358     {
359         DPRINT("Compressed file!\n");
360         UNIMPLEMENTED;
361         return STATUS_NOT_IMPLEMENTED;
362     }
363 
364     // allocate non-paged memory for the FILE_RECORD_HEADER
365     FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
366     if (FileRecord == NULL)
367     {
368         DPRINT1("Not enough memory! Can't write %wS!\n", Fcb->PathName);
369         return STATUS_INSUFFICIENT_RESOURCES;
370     }
371 
372     // read the FILE_RECORD_HEADER from the drive (or cache)
373     DPRINT("Reading file record...\n");
374     Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
375     if (!NT_SUCCESS(Status))
376     {
377         // We couldn't get the file's record. Free the memory and return the error
378         DPRINT1("Can't find record for %wS!\n", Fcb->ObjectName);
379         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
380         return Status;
381     }
382 
383     DPRINT("Found record for %wS\n", Fcb->ObjectName);
384 
385     // Find the attribute with the data stream for our file
386     DPRINT("Finding Data Attribute...\n");
387     Status = FindAttribute(DeviceExt, FileRecord, AttributeData, Fcb->Stream, wcslen(Fcb->Stream), &DataContext,
388                            &AttributeOffset);
389 
390     // Did we fail to find the attribute?
391     if (!NT_SUCCESS(Status))
392     {
393         NTSTATUS BrowseStatus;
394         FIND_ATTR_CONTXT Context;
395         PNTFS_ATTR_RECORD Attribute;
396 
397         DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream);
398 
399         // Couldn't find the requested data stream; print a list of streams available
400         BrowseStatus = FindFirstAttribute(&Context, DeviceExt, FileRecord, FALSE, &Attribute);
401         while (NT_SUCCESS(BrowseStatus))
402         {
403             if (Attribute->Type == AttributeData)
404             {
405                 UNICODE_STRING Name;
406 
407                 Name.Length = Attribute->NameLength * sizeof(WCHAR);
408                 Name.MaximumLength = Name.Length;
409                 Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
410                 DPRINT1("Data stream: '%wZ' available\n", &Name);
411             }
412 
413             BrowseStatus = FindNextAttribute(&Context, &Attribute);
414         }
415         FindCloseAttribute(&Context);
416 
417         ReleaseAttributeContext(DataContext);
418         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
419         return Status;
420     }
421 
422     // Get the size of the stream on disk
423     StreamSize = AttributeDataLength(DataContext->pRecord);
424 
425     DPRINT("WriteOffset: %lu\tStreamSize: %I64u\n", WriteOffset, StreamSize);
426 
427     // Are we trying to write beyond the end of the stream?
428     if (WriteOffset + Length > StreamSize)
429     {
430         // is increasing the stream size allowed?
431         if (!(Fcb->Flags & FCB_IS_VOLUME) &&
432             !(IrpFlags & IRP_PAGING_IO))
433         {
434             LARGE_INTEGER DataSize;
435             ULONGLONG AllocationSize;
436             PFILENAME_ATTRIBUTE fileNameAttribute;
437             ULONGLONG ParentMFTId;
438             UNICODE_STRING filename;
439 
440             DataSize.QuadPart = WriteOffset + Length;
441 
442             // set the attribute data length
443             Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, &DataSize);
444             if (!NT_SUCCESS(Status))
445             {
446                 ReleaseAttributeContext(DataContext);
447                 ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
448                 *LengthWritten = 0;
449                 return Status;
450             }
451 
452             AllocationSize = AttributeAllocatedLength(DataContext->pRecord);
453 
454             // now we need to update this file's size in every directory index entry that references it
455             // TODO: put this code in its own function and adapt it to work with every filename / hardlink
456             // stored in the file record.
457             fileNameAttribute = GetBestFileNameFromRecord(Fcb->Vcb, FileRecord);
458             ASSERT(fileNameAttribute);
459 
460             ParentMFTId = fileNameAttribute->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
461 
462             filename.Buffer = fileNameAttribute->Name;
463             filename.Length = fileNameAttribute->NameLength * sizeof(WCHAR);
464             filename.MaximumLength = filename.Length;
465 
466             Status = UpdateFileNameRecord(Fcb->Vcb,
467                                           ParentMFTId,
468                                           &filename,
469                                           FALSE,
470                                           DataSize.QuadPart,
471                                           AllocationSize,
472                                           CaseSensitive);
473 
474         }
475         else
476         {
477             // TODO - just fail for now
478             ReleaseAttributeContext(DataContext);
479             ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
480             *LengthWritten = 0;
481             return STATUS_ACCESS_DENIED;
482         }
483     }
484 
485     DPRINT("Length: %lu\tWriteOffset: %lu\tStreamSize: %I64u\n", Length, WriteOffset, StreamSize);
486 
487     // Write the data to the attribute
488     Status = WriteAttribute(DeviceExt, DataContext, WriteOffset, Buffer, Length, LengthWritten, FileRecord);
489 
490     // Did the write fail?
491     if (!NT_SUCCESS(Status))
492     {
493         DPRINT1("Write failure!\n");
494         ReleaseAttributeContext(DataContext);
495         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
496 
497         return Status;
498     }
499 
500     // This should never happen:
501     if (*LengthWritten != Length)
502     {
503         DPRINT1("\a\tNTFS DRIVER ERROR: length written (%lu) differs from requested (%lu), but no error was indicated!\n",
504             *LengthWritten, Length);
505         Status = STATUS_UNEXPECTED_IO_ERROR;
506     }
507 
508     ReleaseAttributeContext(DataContext);
509     ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
510 
511     return Status;
512 }
513 
514 /**
515 * @name NtfsWrite
516 * @implemented
517 *
518 * Handles IRP_MJ_WRITE I/O Request Packets for NTFS. This code borrows a lot from
519 * VfatWrite, and needs a lot of cleaning up. It also needs a lot more of the code
520 * from VfatWrite integrated.
521 *
522 * @param IrpContext
523 * Points to an NTFS_IRP_CONTEXT which describes the write
524 *
525 * @return
526 * STATUS_SUCCESS if successful,
527 * STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
528 * STATUS_INVALID_DEVICE_REQUEST if called on the main device object,
529 * STATUS_NOT_IMPLEMENTED or STATUS_ACCESS_DENIED if a required feature isn't implemented.
530 * STATUS_PARTIAL_COPY, STATUS_UNSUCCESSFUL, or STATUS_OBJECT_NAME_NOT_FOUND if NtfsWriteFile() fails.
531 *
532 * @remarks Called by NtfsDispatch() in response to an IRP_MJ_WRITE request. Page files are not implemented.
533 * Support for large files (>4gb) is not implemented. Cached writes, file locks, transactions, etc - not implemented.
534 *
535 */
536 NTSTATUS
537 NtfsWrite(PNTFS_IRP_CONTEXT IrpContext)
538 {
539     PNTFS_FCB Fcb;
540     PERESOURCE Resource = NULL;
541     LARGE_INTEGER ByteOffset;
542     PUCHAR Buffer;
543     NTSTATUS Status = STATUS_SUCCESS;
544     ULONG Length = 0;
545     ULONG ReturnedWriteLength = 0;
546     PDEVICE_OBJECT DeviceObject = NULL;
547     PDEVICE_EXTENSION DeviceExt = NULL;
548     PFILE_OBJECT FileObject = NULL;
549     PIRP Irp = NULL;
550     ULONG BytesPerSector;
551 
552     DPRINT("NtfsWrite(IrpContext %p)\n", IrpContext);
553     ASSERT(IrpContext);
554 
555     // get the I/O request packet
556     Irp = IrpContext->Irp;
557 
558     // This request is not allowed on the main device object
559     if (IrpContext->DeviceObject == NtfsGlobalData->DeviceObject)
560     {
561         DPRINT1("\t\t\t\tNtfsWrite is called with the main device object.\n");
562 
563         Irp->IoStatus.Information = 0;
564         return STATUS_INVALID_DEVICE_REQUEST;
565     }
566 
567     // get the File control block
568     Fcb = (PNTFS_FCB)IrpContext->FileObject->FsContext;
569     ASSERT(Fcb);
570 
571     DPRINT("About to write %wS\n", Fcb->ObjectName);
572     DPRINT("NTFS Version: %d.%d\n", Fcb->Vcb->NtfsInfo.MajorVersion, Fcb->Vcb->NtfsInfo.MinorVersion);
573 
574     // setup some more locals
575     FileObject = IrpContext->FileObject;
576     DeviceObject = IrpContext->DeviceObject;
577     DeviceExt = DeviceObject->DeviceExtension;
578     BytesPerSector = DeviceExt->StorageDevice->SectorSize;
579     Length = IrpContext->Stack->Parameters.Write.Length;
580 
581     // get the file offset we'll be writing to
582     ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
583     if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
584         ByteOffset.u.HighPart == -1)
585     {
586         ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
587     }
588 
589     DPRINT("ByteOffset: %I64u\tLength: %lu\tBytes per sector: %lu\n", ByteOffset.QuadPart,
590         Length, BytesPerSector);
591 
592     if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
593     {
594         // TODO: Support large files
595         DPRINT1("FIXME: Writing to large files is not yet supported at this time.\n");
596         return STATUS_INVALID_PARAMETER;
597     }
598 
599     // Is this a non-cached write? A non-buffered write?
600     if (IrpContext->Irp->Flags & (IRP_PAGING_IO | IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME) ||
601         IrpContext->FileObject->Flags & FILE_NO_INTERMEDIATE_BUFFERING)
602     {
603         // non-cached and non-buffered writes must be sector aligned
604         if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
605         {
606             DPRINT1("Non-cached writes and non-buffered writes must be sector aligned!\n");
607             return STATUS_INVALID_PARAMETER;
608         }
609     }
610 
611     if (Length == 0)
612     {
613         DPRINT1("Null write!\n");
614 
615         IrpContext->Irp->IoStatus.Information = 0;
616 
617         // FIXME: Doesn't accurately detect when a user passes NULL to WriteFile() for the buffer
618         if (Irp->UserBuffer == NULL && Irp->MdlAddress == NULL)
619         {
620             // FIXME: Update last write time
621             return STATUS_SUCCESS;
622         }
623 
624         return STATUS_INVALID_PARAMETER;
625     }
626 
627     // get the Resource
628     if (Fcb->Flags & FCB_IS_VOLUME)
629     {
630         Resource = &DeviceExt->DirResource;
631     }
632     else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
633     {
634         Resource = &Fcb->PagingIoResource;
635     }
636     else
637     {
638         Resource = &Fcb->MainResource;
639     }
640 
641     // acquire exclusive access to the Resource
642     if (!ExAcquireResourceExclusiveLite(Resource, BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
643     {
644         return STATUS_CANT_WAIT;
645     }
646 
647     /* From VfatWrite(). Todo: Handle file locks
648     if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
649     FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
650     {
651     if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
652     {
653     Status = STATUS_FILE_LOCK_CONFLICT;
654     goto ByeBye;
655     }
656     }*/
657 
658     // Is this an async request to a file?
659     if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
660     {
661         DPRINT1("FIXME: Async writes not supported in NTFS!\n");
662 
663         ExReleaseResourceLite(Resource);
664         return STATUS_NOT_IMPLEMENTED;
665     }
666 
667     // get the buffer of data the user is trying to write
668     Buffer = NtfsGetUserBuffer(Irp, BooleanFlagOn(Irp->Flags, IRP_PAGING_IO));
669     ASSERT(Buffer);
670 
671     // lock the buffer
672     Status = NtfsLockUserBuffer(Irp, Length, IoReadAccess);
673 
674     // were we unable to lock the buffer?
675     if (!NT_SUCCESS(Status))
676     {
677         DPRINT1("Unable to lock user buffer!\n");
678 
679         ExReleaseResourceLite(Resource);
680         return Status;
681     }
682 
683     DPRINT("Existing File Size(Fcb->RFCB.FileSize.QuadPart): %I64u\n", Fcb->RFCB.FileSize.QuadPart);
684     DPRINT("About to write the data. Length: %lu\n", Length);
685 
686     // TODO: handle HighPart of ByteOffset (large files)
687 
688     // write the file
689     Status = NtfsWriteFile(DeviceExt,
690                            FileObject,
691                            Buffer,
692                            Length,
693                            ByteOffset.LowPart,
694                            Irp->Flags,
695                            BooleanFlagOn(IrpContext->Stack->Flags, SL_CASE_SENSITIVE),
696                            &ReturnedWriteLength);
697 
698     IrpContext->Irp->IoStatus.Status = Status;
699 
700     // was the write successful?
701     if (NT_SUCCESS(Status))
702     {
703         // TODO: Update timestamps
704 
705         if (FileObject->Flags & FO_SYNCHRONOUS_IO)
706         {
707             // advance the file pointer
708             FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + ReturnedWriteLength;
709         }
710 
711         IrpContext->PriorityBoost = IO_DISK_INCREMENT;
712     }
713     else
714     {
715         DPRINT1("Write not Succesful!\tReturned length: %lu\n", ReturnedWriteLength);
716     }
717 
718     Irp->IoStatus.Information = ReturnedWriteLength;
719 
720     // Note: We leave the user buffer that we locked alone, it's up to the I/O manager to unlock and free it
721 
722     ExReleaseResourceLite(Resource);
723 
724     return Status;
725 }
726 
727 /* EOF */
728