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