7ef808f8c2b7e0df9b6130dc6c4f569d2f82c18b
[reactos.git] / drivers / filesystems / ntfs / rw.c
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 DPRINT1("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 = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
126 return Status;
127 }
128
129 StreamSize = AttributeDataLength(&DataContext->Record);
130 if (ReadOffset >= StreamSize)
131 {
132 DPRINT1("Reading beyond stream end!\n");
133 ReleaseAttributeContext(DataContext);
134 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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->Record))
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
175 if (AllocatedBuffer)
176 {
177 ExFreePoolWithTag(ReadBuffer, TAG_NTFS);
178 }
179 return Status;
180 }
181
182 ReleaseAttributeContext(DataContext);
183 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
412 return Status;
413 }
414
415 // Get the size of the stream on disk
416 StreamSize = AttributeDataLength(&DataContext->Record);
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 AllocationSize = ROUND_UP(DataSize.QuadPart, Fcb->Vcb->NtfsInfo.BytesPerCluster);
436
437 // set the attribute data length
438 Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, &DataSize);
439
440 if (!NT_SUCCESS(Status))
441 {
442 ReleaseAttributeContext(DataContext);
443 ExFreePoolWithTag(FileRecord, TAG_NTFS);
444 *LengthWritten = 0;
445 return Status;
446 }
447
448 // now we need to update this file's size in every directory index entry that references it
449 // TODO: put this code in its own function and adapt it to work with every filename / hardlink
450 // stored in the file record.
451 fileNameAttribute = GetBestFileNameFromRecord(Fcb->Vcb, FileRecord);
452 ASSERT(fileNameAttribute);
453
454 ParentMFTId = fileNameAttribute->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
455
456 filename.Buffer = fileNameAttribute->Name;
457 filename.Length = fileNameAttribute->NameLength * sizeof(WCHAR);
458 filename.MaximumLength = filename.Length;
459
460 Status = UpdateFileNameRecord(Fcb->Vcb,
461 ParentMFTId,
462 &filename,
463 FALSE,
464 DataSize.QuadPart,
465 AllocationSize,
466 CaseSensitive);
467
468 }
469 else
470 {
471 // TODO - just fail for now
472 ReleaseAttributeContext(DataContext);
473 ExFreePoolWithTag(FileRecord, TAG_NTFS);
474 *LengthWritten = 0;
475 return STATUS_ACCESS_DENIED;
476 }
477 }
478
479 DPRINT("Length: %lu\tWriteOffset: %lu\tStreamSize: %I64u\n", Length, WriteOffset, StreamSize);
480
481 // Write the data to the attribute
482 Status = WriteAttribute(DeviceExt, DataContext, WriteOffset, Buffer, Length, LengthWritten);
483
484 // Did the write fail?
485 if (!NT_SUCCESS(Status))
486 {
487 DPRINT1("Write failure!\n");
488 ReleaseAttributeContext(DataContext);
489 ExFreePoolWithTag(FileRecord, TAG_NTFS);
490
491 return Status;
492 }
493
494 // This should never happen:
495 if (*LengthWritten != Length)
496 {
497 DPRINT1("\a\tNTFS DRIVER ERROR: length written (%lu) differs from requested (%lu), but no error was indicated!\n",
498 *LengthWritten, Length);
499 Status = STATUS_UNEXPECTED_IO_ERROR;
500 }
501
502 ReleaseAttributeContext(DataContext);
503 ExFreePoolWithTag(FileRecord, TAG_NTFS);
504
505 return Status;
506 }
507
508 /**
509 * @name NtfsWrite
510 * @implemented
511 *
512 * Handles IRP_MJ_WRITE I/O Request Packets for NTFS. This code borrows a lot from
513 * VfatWrite, and needs a lot of cleaning up. It also needs a lot more of the code
514 * from VfatWrite integrated.
515 *
516 * @param IrpContext
517 * Points to an NTFS_IRP_CONTEXT which describes the write
518 *
519 * @return
520 * STATUS_SUCCESS if successful,
521 * STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
522 * STATUS_INVALID_DEVICE_REQUEST if called on the main device object,
523 * STATUS_NOT_IMPLEMENTED or STATUS_ACCESS_DENIED if a required feature isn't implemented.
524 * STATUS_PARTIAL_COPY, STATUS_UNSUCCESSFUL, or STATUS_OBJECT_NAME_NOT_FOUND if NtfsWriteFile() fails.
525 *
526 * @remarks Called by NtfsDispatch() in response to an IRP_MJ_WRITE request. Page files are not implemented.
527 * Support for large files (>4gb) is not implemented. Cached writes, file locks, transactions, etc - not implemented.
528 *
529 */
530 NTSTATUS
531 NtfsWrite(PNTFS_IRP_CONTEXT IrpContext)
532 {
533 PNTFS_FCB Fcb;
534 PERESOURCE Resource = NULL;
535 LARGE_INTEGER ByteOffset;
536 PUCHAR Buffer;
537 NTSTATUS Status = STATUS_SUCCESS;
538 ULONG Length = 0;
539 ULONG ReturnedWriteLength = 0;
540 PDEVICE_OBJECT DeviceObject = NULL;
541 PDEVICE_EXTENSION DeviceExt = NULL;
542 PFILE_OBJECT FileObject = NULL;
543 PIRP Irp = NULL;
544 ULONG BytesPerSector;
545
546 DPRINT("NtfsWrite(IrpContext %p)\n", IrpContext);
547 ASSERT(IrpContext);
548
549 // This request is not allowed on the main device object
550 if (IrpContext->DeviceObject == NtfsGlobalData->DeviceObject)
551 {
552 DPRINT1("\t\t\t\tNtfsWrite is called with the main device object.\n");
553
554 Irp->IoStatus.Information = 0;
555 return STATUS_INVALID_DEVICE_REQUEST;
556 }
557
558 // get the I/O request packet
559 Irp = IrpContext->Irp;
560
561 // get the File control block
562 Fcb = (PNTFS_FCB)IrpContext->FileObject->FsContext;
563 ASSERT(Fcb);
564
565 DPRINT("About to write %wS\n", Fcb->ObjectName);
566 DPRINT("NTFS Version: %d.%d\n", Fcb->Vcb->NtfsInfo.MajorVersion, Fcb->Vcb->NtfsInfo.MinorVersion);
567
568 // setup some more locals
569 FileObject = IrpContext->FileObject;
570 DeviceObject = IrpContext->DeviceObject;
571 DeviceExt = DeviceObject->DeviceExtension;
572 BytesPerSector = DeviceExt->StorageDevice->SectorSize;
573 Length = IrpContext->Stack->Parameters.Write.Length;
574
575 // get the file offset we'll be writing to
576 ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
577 if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
578 ByteOffset.u.HighPart == -1)
579 {
580 ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
581 }
582
583 DPRINT("ByteOffset: %I64u\tLength: %lu\tBytes per sector: %lu\n", ByteOffset.QuadPart,
584 Length, BytesPerSector);
585
586 if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
587 {
588 // TODO: Support large files
589 DPRINT1("FIXME: Writing to large files is not yet supported at this time.\n");
590 return STATUS_INVALID_PARAMETER;
591 }
592
593 // Is this a non-cached write? A non-buffered write?
594 if (IrpContext->Irp->Flags & (IRP_PAGING_IO | IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME) ||
595 IrpContext->FileObject->Flags & FILE_NO_INTERMEDIATE_BUFFERING)
596 {
597 // non-cached and non-buffered writes must be sector aligned
598 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
599 {
600 DPRINT1("Non-cached writes and non-buffered writes must be sector aligned!\n");
601 return STATUS_INVALID_PARAMETER;
602 }
603 }
604
605 if (Length == 0)
606 {
607 DPRINT1("Null write!\n");
608
609 IrpContext->Irp->IoStatus.Information = 0;
610
611 // FIXME: Doesn't accurately detect when a user passes NULL to WriteFile() for the buffer
612 if (Irp->UserBuffer == NULL && Irp->MdlAddress == NULL)
613 {
614 // FIXME: Update last write time
615 return STATUS_SUCCESS;
616 }
617
618 return STATUS_INVALID_PARAMETER;
619 }
620
621 // get the Resource
622 if (Fcb->Flags & FCB_IS_VOLUME)
623 {
624 Resource = &DeviceExt->DirResource;
625 }
626 else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
627 {
628 Resource = &Fcb->PagingIoResource;
629 }
630 else
631 {
632 Resource = &Fcb->MainResource;
633 }
634
635 // acquire exclusive access to the Resource
636 if (!ExAcquireResourceExclusiveLite(Resource, BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
637 {
638 return STATUS_CANT_WAIT;
639 }
640
641 /* From VfatWrite(). Todo: Handle file locks
642 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
643 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
644 {
645 if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
646 {
647 Status = STATUS_FILE_LOCK_CONFLICT;
648 goto ByeBye;
649 }
650 }*/
651
652 // Is this an async request to a file?
653 if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
654 {
655 DPRINT1("FIXME: Async writes not supported in NTFS!\n");
656
657 ExReleaseResourceLite(Resource);
658 return STATUS_NOT_IMPLEMENTED;
659 }
660
661 // get the buffer of data the user is trying to write
662 Buffer = NtfsGetUserBuffer(Irp, BooleanFlagOn(Irp->Flags, IRP_PAGING_IO));
663 ASSERT(Buffer);
664
665 // lock the buffer
666 Status = NtfsLockUserBuffer(Irp, Length, IoReadAccess);
667
668 // were we unable to lock the buffer?
669 if (!NT_SUCCESS(Status))
670 {
671 DPRINT1("Unable to lock user buffer!\n");
672
673 ExReleaseResourceLite(Resource);
674 return Status;
675 }
676
677 DPRINT("Existing File Size(Fcb->RFCB.FileSize.QuadPart): %I64u\n", Fcb->RFCB.FileSize.QuadPart);
678 DPRINT("About to write the data. Length: %lu\n", Length);
679
680 // TODO: handle HighPart of ByteOffset (large files)
681
682 // write the file
683 Status = NtfsWriteFile(DeviceExt,
684 FileObject,
685 Buffer,
686 Length,
687 ByteOffset.LowPart,
688 Irp->Flags,
689 BooleanFlagOn(IrpContext->Stack->Flags, SL_CASE_SENSITIVE),
690 &ReturnedWriteLength);
691
692 IrpContext->Irp->IoStatus.Status = Status;
693
694 // was the write successful?
695 if (NT_SUCCESS(Status))
696 {
697 // TODO: Update timestamps
698
699 if (FileObject->Flags & FO_SYNCHRONOUS_IO)
700 {
701 // advance the file pointer
702 FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + ReturnedWriteLength;
703 }
704
705 IrpContext->PriorityBoost = IO_DISK_INCREMENT;
706 }
707 else
708 {
709 DPRINT1("Write not Succesful!\tReturned length: %lu\n", ReturnedWriteLength);
710 }
711
712 Irp->IoStatus.Information = ReturnedWriteLength;
713
714 // Note: We leave the user buffer that we locked alone, it's up to the I/O manager to unlock and free it
715
716 ExReleaseResourceLite(Resource);
717
718 return Status;
719 }
720
721 /* EOF */