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