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