1 /* Copyright (c) Mark Harmstone 2016-17 2 * 3 * This file is part of WinBtrfs. 4 * 5 * WinBtrfs is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public Licence as published by 7 * the Free Software Foundation, either version 3 of the Licence, or 8 * (at your option) any later version. 9 * 10 * WinBtrfs 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 Lesser General Public Licence for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public Licence 16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include "btrfs_drv.h" 19 #include "crc32c.h" 20 21 #if (NTDDI_VERSION >= NTDDI_WIN10) 22 // not currently in mingw - introduced with Windows 10 23 #ifndef _MSC_VER 24 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59 25 #define FileHardLinkFullIdInformation (enum _FILE_INFORMATION_CLASS)62 26 #define FileDispositionInformationEx (enum _FILE_INFORMATION_CLASS)64 27 #define FileRenameInformationEx (enum _FILE_INFORMATION_CLASS)65 28 #define FileStatInformation (enum _FILE_INFORMATION_CLASS)68 29 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70 30 #define FileCaseSensitiveInformation (enum _FILE_INFORMATION_CLASS)71 31 #define FileLinkInformationEx (enum _FILE_INFORMATION_CLASS)72 32 #define FileStorageReserveIdInformation (enum _FILE_INFORMATION_CLASS)74 33 34 typedef struct _FILE_ID_INFORMATION { 35 ULONGLONG VolumeSerialNumber; 36 FILE_ID_128 FileId; 37 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION; 38 39 typedef struct _FILE_STAT_INFORMATION { 40 LARGE_INTEGER FileId; 41 LARGE_INTEGER CreationTime; 42 LARGE_INTEGER LastAccessTime; 43 LARGE_INTEGER LastWriteTime; 44 LARGE_INTEGER ChangeTime; 45 LARGE_INTEGER AllocationSize; 46 LARGE_INTEGER EndOfFile; 47 ULONG FileAttributes; 48 ULONG ReparseTag; 49 ULONG NumberOfLinks; 50 ACCESS_MASK EffectiveAccess; 51 } FILE_STAT_INFORMATION, *PFILE_STAT_INFORMATION; 52 53 typedef struct _FILE_STAT_LX_INFORMATION { 54 LARGE_INTEGER FileId; 55 LARGE_INTEGER CreationTime; 56 LARGE_INTEGER LastAccessTime; 57 LARGE_INTEGER LastWriteTime; 58 LARGE_INTEGER ChangeTime; 59 LARGE_INTEGER AllocationSize; 60 LARGE_INTEGER EndOfFile; 61 ULONG FileAttributes; 62 ULONG ReparseTag; 63 ULONG NumberOfLinks; 64 ACCESS_MASK EffectiveAccess; 65 ULONG LxFlags; 66 ULONG LxUid; 67 ULONG LxGid; 68 ULONG LxMode; 69 ULONG LxDeviceIdMajor; 70 ULONG LxDeviceIdMinor; 71 } FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION; 72 73 #define LX_FILE_METADATA_HAS_UID 0x01 74 #define LX_FILE_METADATA_HAS_GID 0x02 75 #define LX_FILE_METADATA_HAS_MODE 0x04 76 #define LX_FILE_METADATA_HAS_DEVICE_ID 0x08 77 #define LX_FILE_CASE_SENSITIVE_DIR 0x10 78 79 typedef struct _FILE_RENAME_INFORMATION_EX { 80 union { 81 BOOLEAN ReplaceIfExists; 82 ULONG Flags; 83 }; 84 HANDLE RootDirectory; 85 ULONG FileNameLength; 86 WCHAR FileName[1]; 87 } FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX; 88 89 typedef struct _FILE_DISPOSITION_INFORMATION_EX { 90 ULONG Flags; 91 } FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX; 92 93 typedef struct _FILE_LINK_INFORMATION_EX { 94 union { 95 BOOLEAN ReplaceIfExists; 96 ULONG Flags; 97 }; 98 HANDLE RootDirectory; 99 ULONG FileNameLength; 100 WCHAR FileName[1]; 101 } FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX; 102 103 typedef struct _FILE_CASE_SENSITIVE_INFORMATION { 104 ULONG Flags; 105 } FILE_CASE_SENSITIVE_INFORMATION, *PFILE_CASE_SENSITIVE_INFORMATION; 106 107 typedef struct _FILE_LINK_ENTRY_FULL_ID_INFORMATION { 108 ULONG NextEntryOffset; 109 FILE_ID_128 ParentFileId; 110 ULONG FileNameLength; 111 WCHAR FileName[1]; 112 } FILE_LINK_ENTRY_FULL_ID_INFORMATION, *PFILE_LINK_ENTRY_FULL_ID_INFORMATION; 113 114 typedef struct _FILE_LINKS_FULL_ID_INFORMATION { 115 ULONG BytesNeeded; 116 ULONG EntriesReturned; 117 FILE_LINK_ENTRY_FULL_ID_INFORMATION Entry; 118 } FILE_LINKS_FULL_ID_INFORMATION, *PFILE_LINKS_FULL_ID_INFORMATION; 119 120 #define FILE_RENAME_REPLACE_IF_EXISTS 0x001 121 #define FILE_RENAME_POSIX_SEMANTICS 0x002 122 #define FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE 0x004 123 #define FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE 0x008 124 #define FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE 0x010 125 #define FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE 0x020 126 #define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE 0x040 127 #define FILE_RENAME_FORCE_RESIZE_TARGET_SR 0x080 128 #define FILE_RENAME_FORCE_RESIZE_SOURCE_SR 0x100 129 130 #define FILE_DISPOSITION_DELETE 0x1 131 #define FILE_DISPOSITION_POSIX_SEMANTICS 0x2 132 #define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK 0x4 133 #define FILE_DISPOSITION_ON_CLOSE 0x8 134 135 #define FILE_LINK_REPLACE_IF_EXISTS 0x001 136 #define FILE_LINK_POSIX_SEMANTICS 0x002 137 #define FILE_LINK_SUPPRESS_STORAGE_RESERVE_INHERITANCE 0x008 138 #define FILE_LINK_NO_INCREASE_AVAILABLE_SPACE 0x010 139 #define FILE_LINK_NO_DECREASE_AVAILABLE_SPACE 0x020 140 #define FILE_LINK_IGNORE_READONLY_ATTRIBUTE 0x040 141 #define FILE_LINK_FORCE_RESIZE_TARGET_SR 0x080 142 #define FILE_LINK_FORCE_RESIZE_SOURCE_SR 0x100 143 144 #else 145 146 #define FILE_RENAME_INFORMATION_EX FILE_RENAME_INFORMATION 147 #define FILE_LINK_INFORMATION_EX FILE_LINK_INFORMATION 148 149 #endif 150 #endif 151 152 #ifdef __REACTOS__ 153 typedef struct _FILE_RENAME_INFORMATION_EX { 154 union { 155 BOOLEAN ReplaceIfExists; 156 ULONG Flags; 157 }; 158 HANDLE RootDirectory; 159 ULONG FileNameLength; 160 WCHAR FileName[1]; 161 } FILE_RENAME_INFORMATION_EX, *PFILE_RENAME_INFORMATION_EX; 162 163 typedef struct _FILE_DISPOSITION_INFORMATION_EX { 164 ULONG Flags; 165 } FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX; 166 167 typedef struct _FILE_LINK_INFORMATION_EX { 168 union { 169 BOOLEAN ReplaceIfExists; 170 ULONG Flags; 171 }; 172 HANDLE RootDirectory; 173 ULONG FileNameLength; 174 WCHAR FileName[1]; 175 } FILE_LINK_INFORMATION_EX, *PFILE_LINK_INFORMATION_EX; 176 177 #define FILE_RENAME_REPLACE_IF_EXISTS 0x001 178 #define FILE_RENAME_POSIX_SEMANTICS 0x002 179 #define FILE_RENAME_IGNORE_READONLY_ATTRIBUTE 0x040 180 181 #define FILE_DISPOSITION_DELETE 0x1 182 #define FILE_DISPOSITION_POSIX_SEMANTICS 0x2 183 #define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK 0x4 184 185 #define FILE_LINK_REPLACE_IF_EXISTS 0x001 186 #define FILE_LINK_POSIX_SEMANTICS 0x002 187 #define FILE_LINK_IGNORE_READONLY_ATTRIBUTE 0x040 188 #endif 189 190 static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { 191 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer; 192 fcb* fcb = FileObject->FsContext; 193 ccb* ccb = FileObject->FsContext2; 194 file_ref* fileref = ccb ? ccb->fileref : NULL; 195 ULONG defda, filter = 0; 196 bool inode_item_changed = false; 197 NTSTATUS Status; 198 199 if (fcb->ads) { 200 if (fileref && fileref->parent) 201 fcb = fileref->parent->fcb; 202 else { 203 ERR("stream did not have fileref\n"); 204 return STATUS_INTERNAL_ERROR; 205 } 206 } 207 208 if (!ccb) { 209 ERR("ccb was NULL\n"); 210 return STATUS_INVALID_PARAMETER; 211 } 212 213 TRACE("file = %p, attributes = %lx\n", FileObject, fbi->FileAttributes); 214 215 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 216 217 if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) { 218 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n"); 219 Status = STATUS_INVALID_PARAMETER; 220 goto end; 221 } 222 223 if (fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fcb->subvol, Irp) && 224 (fbi->FileAttributes == 0 || fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)) { 225 Status = STATUS_ACCESS_DENIED; 226 goto end; 227 } 228 229 // don't allow readonly subvol to be made r/w if send operation running on it 230 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && 231 fcb->subvol->send_ops > 0) { 232 Status = STATUS_DEVICE_NOT_READY; 233 goto end; 234 } 235 236 // times of -2 are some sort of undocumented behaviour to do with LXSS 237 238 if (fbi->CreationTime.QuadPart == -2) 239 fbi->CreationTime.QuadPart = 0; 240 241 if (fbi->LastAccessTime.QuadPart == -2) 242 fbi->LastAccessTime.QuadPart = 0; 243 244 if (fbi->LastWriteTime.QuadPart == -2) 245 fbi->LastWriteTime.QuadPart = 0; 246 247 if (fbi->ChangeTime.QuadPart == -2) 248 fbi->ChangeTime.QuadPart = 0; 249 250 if (fbi->CreationTime.QuadPart == -1) 251 ccb->user_set_creation_time = true; 252 else if (fbi->CreationTime.QuadPart != 0) { 253 win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime); 254 inode_item_changed = true; 255 filter |= FILE_NOTIFY_CHANGE_CREATION; 256 257 ccb->user_set_creation_time = true; 258 } 259 260 if (fbi->LastAccessTime.QuadPart == -1) 261 ccb->user_set_access_time = true; 262 else if (fbi->LastAccessTime.QuadPart != 0) { 263 win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime); 264 inode_item_changed = true; 265 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 266 267 ccb->user_set_access_time = true; 268 } 269 270 if (fbi->LastWriteTime.QuadPart == -1) 271 ccb->user_set_write_time = true; 272 else if (fbi->LastWriteTime.QuadPart != 0) { 273 win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime); 274 inode_item_changed = true; 275 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 276 277 ccb->user_set_write_time = true; 278 } 279 280 if (fbi->ChangeTime.QuadPart == -1) 281 ccb->user_set_change_time = true; 282 else if (fbi->ChangeTime.QuadPart != 0) { 283 win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime); 284 inode_item_changed = true; 285 // no filter for this 286 287 ccb->user_set_change_time = true; 288 } 289 290 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat 291 if (fbi->FileAttributes != 0) { 292 LARGE_INTEGER time; 293 BTRFS_TIME now; 294 295 fbi->FileAttributes &= ~FILE_ATTRIBUTE_NORMAL; 296 297 defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', 298 true, Irp); 299 300 if (fcb->type == BTRFS_TYPE_DIRECTORY) 301 fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; 302 else if (fcb->type == BTRFS_TYPE_SYMLINK) 303 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; 304 305 fcb->atts_changed = true; 306 307 if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) 308 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; 309 310 if (defda == fbi->FileAttributes) 311 fcb->atts_deleted = true; 312 else if (fcb->inode == SUBVOL_ROOT_INODE && (defda | FILE_ATTRIBUTE_READONLY) == (fbi->FileAttributes | FILE_ATTRIBUTE_READONLY)) 313 fcb->atts_deleted = true; 314 315 fcb->atts = fbi->FileAttributes; 316 317 KeQuerySystemTime(&time); 318 win_time_to_unix(time, &now); 319 320 if (!ccb->user_set_change_time) 321 fcb->inode_item.st_ctime = now; 322 323 fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 324 fcb->subvol->root_item.ctime = now; 325 326 if (fcb->inode == SUBVOL_ROOT_INODE) { 327 if (fbi->FileAttributes & FILE_ATTRIBUTE_READONLY) 328 fcb->subvol->root_item.flags |= BTRFS_SUBVOL_READONLY; 329 else 330 fcb->subvol->root_item.flags &= ~BTRFS_SUBVOL_READONLY; 331 } 332 333 inode_item_changed = true; 334 335 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 336 } 337 338 if (inode_item_changed) { 339 fcb->inode_item.transid = Vcb->superblock.generation; 340 fcb->inode_item.sequence++; 341 fcb->inode_item_changed = true; 342 343 mark_fcb_dirty(fcb); 344 } 345 346 if (filter != 0) 347 queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); 348 349 Status = STATUS_SUCCESS; 350 351 end: 352 ExReleaseResourceLite(fcb->Header.Resource); 353 354 return Status; 355 } 356 357 static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, bool ex) { 358 fcb* fcb = FileObject->FsContext; 359 ccb* ccb = FileObject->FsContext2; 360 file_ref* fileref = ccb ? ccb->fileref : NULL; 361 ULONG atts, flags; 362 NTSTATUS Status; 363 364 if (!fileref) 365 return STATUS_INVALID_PARAMETER; 366 367 if (ex) { 368 FILE_DISPOSITION_INFORMATION_EX* fdi = Irp->AssociatedIrp.SystemBuffer; 369 370 flags = fdi->Flags; 371 } else { 372 FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer; 373 374 flags = fdi->DeleteFile ? FILE_DISPOSITION_DELETE : 0; 375 } 376 377 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 378 379 TRACE("changing delete_on_close to %s for fcb %p\n", flags & FILE_DISPOSITION_DELETE ? "true" : "false", fcb); 380 381 if (fcb->ads) { 382 if (fileref->parent) 383 atts = fileref->parent->fcb->atts; 384 else { 385 ERR("no fileref for stream\n"); 386 Status = STATUS_INTERNAL_ERROR; 387 goto end; 388 } 389 } else 390 atts = fcb->atts; 391 392 TRACE("atts = %lx\n", atts); 393 394 if (atts & FILE_ATTRIBUTE_READONLY) { 395 TRACE("not allowing readonly file to be deleted\n"); 396 Status = STATUS_CANNOT_DELETE; 397 goto end; 398 } 399 400 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE) { 401 WARN("not allowing \\$Root to be deleted\n"); 402 Status = STATUS_ACCESS_DENIED; 403 goto end; 404 } 405 406 // FIXME - can we skip this bit for subvols? 407 if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && (!fileref || fileref->fcb != Vcb->dummy_fcb)) { 408 TRACE("directory not empty\n"); 409 Status = STATUS_DIRECTORY_NOT_EMPTY; 410 goto end; 411 } 412 413 if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) { 414 if (!ex || flags & FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK) { 415 TRACE("trying to delete file which is being mapped as an image\n"); 416 Status = STATUS_CANNOT_DELETE; 417 goto end; 418 } 419 } 420 421 ccb->fileref->delete_on_close = flags & FILE_DISPOSITION_DELETE; 422 423 FileObject->DeletePending = flags & FILE_DISPOSITION_DELETE; 424 425 if (flags & FILE_DISPOSITION_DELETE && flags & FILE_DISPOSITION_POSIX_SEMANTICS) 426 ccb->fileref->posix_delete = true; 427 428 Status = STATUS_SUCCESS; 429 430 end: 431 ExReleaseResourceLite(fcb->Header.Resource); 432 433 // send notification that directory is about to be deleted 434 if (NT_SUCCESS(Status) && flags & FILE_DISPOSITION_DELETE && fcb->type == BTRFS_TYPE_DIRECTORY) { 435 FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext, 436 NULL, false, false, 0, NULL, NULL, NULL); 437 } 438 439 return Status; 440 } 441 442 bool has_open_children(file_ref* fileref) { 443 LIST_ENTRY* le = fileref->children.Flink; 444 445 if (IsListEmpty(&fileref->children)) 446 return false; 447 448 while (le != &fileref->children) { 449 file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry); 450 451 if (c->open_count > 0) 452 return true; 453 454 if (has_open_children(c)) 455 return true; 456 457 le = le->Flink; 458 } 459 460 return false; 461 } 462 463 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { 464 device_extension* Vcb = oldfcb->Vcb; 465 fcb* fcb; 466 LIST_ENTRY* le; 467 468 // FIXME - we can skip a lot of this if the inode is about to be deleted 469 470 fcb = create_fcb(Vcb, PagedPool); // FIXME - what if we duplicate the paging file? 471 if (!fcb) { 472 ERR("out of memory\n"); 473 return STATUS_INSUFFICIENT_RESOURCES; 474 } 475 476 fcb->Vcb = Vcb; 477 478 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 479 fcb->Header.AllocationSize = oldfcb->Header.AllocationSize; 480 fcb->Header.FileSize = oldfcb->Header.FileSize; 481 fcb->Header.ValidDataLength = oldfcb->Header.ValidDataLength; 482 483 fcb->type = oldfcb->type; 484 485 if (oldfcb->ads) { 486 fcb->ads = true; 487 fcb->adshash = oldfcb->adshash; 488 fcb->adsmaxlen = oldfcb->adsmaxlen; 489 490 if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) { 491 fcb->adsxattr.Length = oldfcb->adsxattr.Length; 492 fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1; 493 fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG); 494 495 if (!fcb->adsxattr.Buffer) { 496 ERR("out of memory\n"); 497 free_fcb(fcb); 498 return STATUS_INSUFFICIENT_RESOURCES; 499 } 500 501 RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length); 502 fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0; 503 } 504 505 if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) { 506 fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length; 507 fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG); 508 509 if (!fcb->adsdata.Buffer) { 510 ERR("out of memory\n"); 511 free_fcb(fcb); 512 return STATUS_INSUFFICIENT_RESOURCES; 513 } 514 515 RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length); 516 } 517 518 goto end; 519 } 520 521 RtlCopyMemory(&fcb->inode_item, &oldfcb->inode_item, sizeof(INODE_ITEM)); 522 fcb->inode_item_changed = true; 523 524 if (oldfcb->sd && RtlLengthSecurityDescriptor(oldfcb->sd) > 0) { 525 fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG); 526 if (!fcb->sd) { 527 ERR("out of memory\n"); 528 free_fcb(fcb); 529 return STATUS_INSUFFICIENT_RESOURCES; 530 } 531 532 RtlCopyMemory(fcb->sd, oldfcb->sd, RtlLengthSecurityDescriptor(oldfcb->sd)); 533 } 534 535 fcb->atts = oldfcb->atts; 536 537 le = oldfcb->extents.Flink; 538 while (le != &oldfcb->extents) { 539 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 540 541 if (!ext->ignore) { 542 extent* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 543 544 if (!ext2) { 545 ERR("out of memory\n"); 546 free_fcb(fcb); 547 return STATUS_INSUFFICIENT_RESOURCES; 548 } 549 550 ext2->offset = ext->offset; 551 ext2->datalen = ext->datalen; 552 553 if (ext2->datalen > 0) 554 RtlCopyMemory(&ext2->extent_data, &ext->extent_data, ext2->datalen); 555 556 ext2->unique = false; 557 ext2->ignore = false; 558 ext2->inserted = true; 559 560 if (ext->csum) { 561 ULONG len; 562 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 563 564 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) 565 len = (ULONG)ed2->num_bytes; 566 else 567 len = (ULONG)ed2->size; 568 569 len = (len * sizeof(uint32_t)) >> Vcb->sector_shift; 570 571 ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG); 572 if (!ext2->csum) { 573 ERR("out of memory\n"); 574 free_fcb(fcb); 575 return STATUS_INSUFFICIENT_RESOURCES; 576 } 577 578 RtlCopyMemory(ext2->csum, ext->csum, len); 579 } else 580 ext2->csum = NULL; 581 582 InsertTailList(&fcb->extents, &ext2->list_entry); 583 } 584 585 le = le->Flink; 586 } 587 588 le = oldfcb->hardlinks.Flink; 589 while (le != &oldfcb->hardlinks) { 590 hardlink *hl = CONTAINING_RECORD(le, hardlink, list_entry), *hl2; 591 592 hl2 = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 593 594 if (!hl2) { 595 ERR("out of memory\n"); 596 free_fcb(fcb); 597 return STATUS_INSUFFICIENT_RESOURCES; 598 } 599 600 hl2->parent = hl->parent; 601 hl2->index = hl->index; 602 603 hl2->name.Length = hl2->name.MaximumLength = hl->name.Length; 604 hl2->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->name.MaximumLength, ALLOC_TAG); 605 606 if (!hl2->name.Buffer) { 607 ERR("out of memory\n"); 608 ExFreePool(hl2); 609 free_fcb(fcb); 610 return STATUS_INSUFFICIENT_RESOURCES; 611 } 612 613 RtlCopyMemory(hl2->name.Buffer, hl->name.Buffer, hl->name.Length); 614 615 hl2->utf8.Length = hl2->utf8.MaximumLength = hl->utf8.Length; 616 hl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->utf8.MaximumLength, ALLOC_TAG); 617 618 if (!hl2->utf8.Buffer) { 619 ERR("out of memory\n"); 620 ExFreePool(hl2->name.Buffer); 621 ExFreePool(hl2); 622 free_fcb(fcb); 623 return STATUS_INSUFFICIENT_RESOURCES; 624 } 625 626 RtlCopyMemory(hl2->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length); 627 628 InsertTailList(&fcb->hardlinks, &hl2->list_entry); 629 630 le = le->Flink; 631 } 632 633 if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) { 634 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length; 635 636 fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG); 637 if (!fcb->reparse_xattr.Buffer) { 638 ERR("out of memory\n"); 639 free_fcb(fcb); 640 return STATUS_INSUFFICIENT_RESOURCES; 641 } 642 643 RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length); 644 } 645 646 if (oldfcb->ea_xattr.Buffer && oldfcb->ea_xattr.Length > 0) { 647 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = oldfcb->ea_xattr.Length; 648 649 fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG); 650 if (!fcb->ea_xattr.Buffer) { 651 ERR("out of memory\n"); 652 free_fcb(fcb); 653 return STATUS_INSUFFICIENT_RESOURCES; 654 } 655 656 RtlCopyMemory(fcb->ea_xattr.Buffer, oldfcb->ea_xattr.Buffer, fcb->ea_xattr.Length); 657 } 658 659 fcb->prop_compression = oldfcb->prop_compression; 660 661 le = oldfcb->xattrs.Flink; 662 while (le != &oldfcb->xattrs) { 663 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry); 664 665 if (xa->valuelen > 0) { 666 xattr* xa2; 667 668 xa2 = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + xa->namelen + xa->valuelen, ALLOC_TAG); 669 670 if (!xa2) { 671 ERR("out of memory\n"); 672 free_fcb(fcb); 673 return STATUS_INSUFFICIENT_RESOURCES; 674 } 675 676 xa2->namelen = xa->namelen; 677 xa2->valuelen = xa->valuelen; 678 xa2->dirty = xa->dirty; 679 memcpy(xa2->data, xa->data, xa->namelen + xa->valuelen); 680 681 InsertTailList(&fcb->xattrs, &xa2->list_entry); 682 } 683 684 le = le->Flink; 685 } 686 687 end: 688 *pfcb = fcb; 689 690 return STATUS_SUCCESS; 691 } 692 693 typedef struct _move_entry { 694 file_ref* fileref; 695 fcb* dummyfcb; 696 file_ref* dummyfileref; 697 struct _move_entry* parent; 698 LIST_ENTRY list_entry; 699 } move_entry; 700 701 static NTSTATUS add_children_to_move_list(device_extension* Vcb, move_entry* me, PIRP Irp) { 702 NTSTATUS Status; 703 LIST_ENTRY* le; 704 705 ExAcquireResourceSharedLite(&me->fileref->fcb->nonpaged->dir_children_lock, true); 706 707 le = me->fileref->fcb->dir_children_index.Flink; 708 709 while (le != &me->fileref->fcb->dir_children_index) { 710 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); 711 file_ref* fr; 712 move_entry* me2; 713 714 Status = open_fileref_child(Vcb, me->fileref, &dc->name, true, true, dc->index == 0 ? true : false, PagedPool, &fr, Irp); 715 716 if (!NT_SUCCESS(Status)) { 717 ERR("open_fileref_child returned %08lx\n", Status); 718 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock); 719 return Status; 720 } 721 722 me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG); 723 if (!me2) { 724 ERR("out of memory\n"); 725 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock); 726 return STATUS_INSUFFICIENT_RESOURCES; 727 } 728 729 me2->fileref = fr; 730 me2->dummyfcb = NULL; 731 me2->dummyfileref = NULL; 732 me2->parent = me; 733 734 InsertHeadList(&me->list_entry, &me2->list_entry); 735 736 le = le->Flink; 737 } 738 739 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock); 740 741 return STATUS_SUCCESS; 742 } 743 744 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc) { 745 uint8_t c; 746 747 c = dc->hash >> 24; 748 749 if (fcb->hash_ptrs[c] == &dc->list_entry_hash) { 750 if (dc->list_entry_hash.Flink == &fcb->dir_children_hash) 751 fcb->hash_ptrs[c] = NULL; 752 else { 753 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash.Flink, dir_child, list_entry_hash); 754 755 if (dc2->hash >> 24 == c) 756 fcb->hash_ptrs[c] = &dc2->list_entry_hash; 757 else 758 fcb->hash_ptrs[c] = NULL; 759 } 760 } 761 762 RemoveEntryList(&dc->list_entry_hash); 763 764 c = dc->hash_uc >> 24; 765 766 if (fcb->hash_ptrs_uc[c] == &dc->list_entry_hash_uc) { 767 if (dc->list_entry_hash_uc.Flink == &fcb->dir_children_hash_uc) 768 fcb->hash_ptrs_uc[c] = NULL; 769 else { 770 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash_uc.Flink, dir_child, list_entry_hash_uc); 771 772 if (dc2->hash_uc >> 24 == c) 773 fcb->hash_ptrs_uc[c] = &dc2->list_entry_hash_uc; 774 else 775 fcb->hash_ptrs_uc[c] = NULL; 776 } 777 } 778 779 RemoveEntryList(&dc->list_entry_hash_uc); 780 } 781 782 static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb, fcb** pfcb) { 783 NTSTATUS Status; 784 fcb* fcb; 785 SECURITY_SUBJECT_CONTEXT subjcont; 786 PSID owner; 787 BOOLEAN defaulted; 788 LARGE_INTEGER time; 789 BTRFS_TIME now; 790 791 fcb = create_fcb(Vcb, PagedPool); 792 if (!fcb) { 793 ERR("out of memory\n"); 794 return STATUS_INSUFFICIENT_RESOURCES; 795 } 796 797 KeQuerySystemTime(&time); 798 win_time_to_unix(time, &now); 799 800 fcb->Vcb = Vcb; 801 802 fcb->subvol = r; 803 fcb->inode = InterlockedIncrement64(&r->lastinode); 804 fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&fcb->inode, sizeof(uint64_t)); 805 fcb->type = BTRFS_TYPE_DIRECTORY; 806 807 fcb->inode_item.generation = Vcb->superblock.generation; 808 fcb->inode_item.transid = Vcb->superblock.generation; 809 fcb->inode_item.st_nlink = 1; 810 fcb->inode_item.st_mode = __S_IFDIR | inherit_mode(parfcb, true); 811 fcb->inode_item.st_atime = fcb->inode_item.st_ctime = fcb->inode_item.st_mtime = fcb->inode_item.otime = now; 812 fcb->inode_item.st_gid = GID_NOBODY; 813 814 fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, false, true, NULL); 815 816 SeCaptureSubjectContext(&subjcont); 817 818 Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, true, &subjcont, IoGetFileObjectGenericMapping(), PagedPool); 819 820 if (!NT_SUCCESS(Status)) { 821 reap_fcb(fcb); 822 ERR("SeAssignSecurity returned %08lx\n", Status); 823 return Status; 824 } 825 826 if (!fcb->sd) { 827 reap_fcb(fcb); 828 ERR("SeAssignSecurity returned NULL security descriptor\n"); 829 return STATUS_INTERNAL_ERROR; 830 } 831 832 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted); 833 if (!NT_SUCCESS(Status)) { 834 ERR("RtlGetOwnerSecurityDescriptor returned %08lx\n", Status); 835 fcb->inode_item.st_uid = UID_NOBODY; 836 fcb->sd_dirty = true; 837 } else { 838 fcb->inode_item.st_uid = sid_to_uid(owner); 839 fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY; 840 } 841 842 find_gid(fcb, parfcb, &subjcont); 843 844 fcb->inode_item_changed = true; 845 846 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 847 fcb->Header.AllocationSize.QuadPart = 0; 848 fcb->Header.FileSize.QuadPart = 0; 849 fcb->Header.ValidDataLength.QuadPart = 0; 850 851 fcb->created = true; 852 853 if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS) 854 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS; 855 856 fcb->prop_compression = parfcb->prop_compression; 857 fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None; 858 859 fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); 860 if (!fcb->hash_ptrs) { 861 ERR("out of memory\n"); 862 return STATUS_INSUFFICIENT_RESOURCES; 863 } 864 865 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256); 866 867 fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); 868 if (!fcb->hash_ptrs_uc) { 869 ERR("out of memory\n"); 870 return STATUS_INSUFFICIENT_RESOURCES; 871 } 872 873 RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256); 874 875 acquire_fcb_lock_exclusive(Vcb); 876 add_fcb_to_subvol(fcb); 877 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); 878 r->fcbs_version++; 879 release_fcb_lock(Vcb); 880 881 mark_fcb_dirty(fcb); 882 883 *pfcb = fcb; 884 885 return STATUS_SUCCESS; 886 } 887 888 void add_fcb_to_subvol(_In_ _Requires_exclusive_lock_held_(_Curr_->Vcb->fcb_lock) fcb* fcb) { 889 LIST_ENTRY* lastle = NULL; 890 uint32_t hash = fcb->hash; 891 892 if (fcb->subvol->fcbs_ptrs[hash >> 24]) { 893 LIST_ENTRY* le = fcb->subvol->fcbs_ptrs[hash >> 24]; 894 895 while (le != &fcb->subvol->fcbs) { 896 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry); 897 898 if (fcb2->hash > hash) { 899 lastle = le->Blink; 900 break; 901 } 902 903 le = le->Flink; 904 } 905 } 906 907 if (!lastle) { 908 uint8_t c = hash >> 24; 909 910 if (c != 0xff) { 911 uint8_t d = c + 1; 912 913 do { 914 if (fcb->subvol->fcbs_ptrs[d]) { 915 lastle = fcb->subvol->fcbs_ptrs[d]->Blink; 916 break; 917 } 918 919 d++; 920 } while (d != 0); 921 } 922 } 923 924 if (lastle) { 925 InsertHeadList(lastle, &fcb->list_entry); 926 927 if (lastle == &fcb->subvol->fcbs || (CONTAINING_RECORD(lastle, struct _fcb, list_entry)->hash >> 24) != (hash >> 24)) 928 fcb->subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry; 929 } else { 930 InsertTailList(&fcb->subvol->fcbs, &fcb->list_entry); 931 932 if (fcb->list_entry.Blink == &fcb->subvol->fcbs || (CONTAINING_RECORD(fcb->list_entry.Blink, struct _fcb, list_entry)->hash >> 24) != (hash >> 24)) 933 fcb->subvol->fcbs_ptrs[hash >> 24] = &fcb->list_entry; 934 } 935 } 936 937 void remove_fcb_from_subvol(_In_ _Requires_exclusive_lock_held_(_Curr_->Vcb->fcb_lock) fcb* fcb) { 938 uint8_t c = fcb->hash >> 24; 939 940 if (fcb->subvol->fcbs_ptrs[c] == &fcb->list_entry) { 941 if (fcb->list_entry.Flink != &fcb->subvol->fcbs && (CONTAINING_RECORD(fcb->list_entry.Flink, struct _fcb, list_entry)->hash >> 24) == c) 942 fcb->subvol->fcbs_ptrs[c] = fcb->list_entry.Flink; 943 else 944 fcb->subvol->fcbs_ptrs[c] = NULL; 945 } 946 947 RemoveEntryList(&fcb->list_entry); 948 } 949 950 static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) { 951 NTSTATUS Status; 952 LIST_ENTRY move_list, *le; 953 move_entry* me; 954 LARGE_INTEGER time; 955 BTRFS_TIME now; 956 file_ref* origparent; 957 958 // FIXME - make sure me->dummyfileref and me->dummyfcb get freed properly 959 960 InitializeListHead(&move_list); 961 962 KeQuerySystemTime(&time); 963 win_time_to_unix(time, &now); 964 965 acquire_fcb_lock_exclusive(fileref->fcb->Vcb); 966 967 me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG); 968 969 if (!me) { 970 ERR("out of memory\n"); 971 Status = STATUS_INSUFFICIENT_RESOURCES; 972 goto end; 973 } 974 975 origparent = fileref->parent; 976 977 me->fileref = fileref; 978 increase_fileref_refcount(me->fileref); 979 me->dummyfcb = NULL; 980 me->dummyfileref = NULL; 981 me->parent = NULL; 982 983 InsertTailList(&move_list, &me->list_entry); 984 985 le = move_list.Flink; 986 while (le != &move_list) { 987 me = CONTAINING_RECORD(le, move_entry, list_entry); 988 989 ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, true); 990 991 if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) { 992 Status = add_children_to_move_list(fileref->fcb->Vcb, me, Irp); 993 994 if (!NT_SUCCESS(Status)) { 995 ERR("add_children_to_move_list returned %08lx\n", Status); 996 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 997 goto end; 998 } 999 } 1000 1001 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 1002 1003 le = le->Flink; 1004 } 1005 1006 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL); 1007 1008 // loop through list and create new inodes 1009 1010 le = move_list.Flink; 1011 while (le != &move_list) { 1012 me = CONTAINING_RECORD(le, move_entry, list_entry); 1013 1014 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE && me->fileref->fcb != fileref->fcb->Vcb->dummy_fcb) { 1015 if (!me->dummyfcb) { 1016 ULONG defda; 1017 1018 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, true); 1019 1020 Status = duplicate_fcb(me->fileref->fcb, &me->dummyfcb); 1021 if (!NT_SUCCESS(Status)) { 1022 ERR("duplicate_fcb returned %08lx\n", Status); 1023 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 1024 goto end; 1025 } 1026 1027 me->dummyfcb->subvol = me->fileref->fcb->subvol; 1028 me->dummyfcb->inode = me->fileref->fcb->inode; 1029 me->dummyfcb->hash = me->fileref->fcb->hash; 1030 1031 if (!me->dummyfcb->ads) { 1032 me->dummyfcb->sd_dirty = me->fileref->fcb->sd_dirty; 1033 me->dummyfcb->atts_changed = me->fileref->fcb->atts_changed; 1034 me->dummyfcb->atts_deleted = me->fileref->fcb->atts_deleted; 1035 me->dummyfcb->extents_changed = me->fileref->fcb->extents_changed; 1036 me->dummyfcb->reparse_xattr_changed = me->fileref->fcb->reparse_xattr_changed; 1037 me->dummyfcb->ea_changed = me->fileref->fcb->ea_changed; 1038 } 1039 1040 me->dummyfcb->created = me->fileref->fcb->created; 1041 me->dummyfcb->deleted = me->fileref->fcb->deleted; 1042 mark_fcb_dirty(me->dummyfcb); 1043 1044 if (!me->fileref->fcb->ads) { 1045 LIST_ENTRY* le2; 1046 1047 me->fileref->fcb->subvol = destdir->fcb->subvol; 1048 me->fileref->fcb->inode = InterlockedIncrement64(&destdir->fcb->subvol->lastinode); 1049 me->fileref->fcb->hash = calc_crc32c(0xffffffff, (uint8_t*)&me->fileref->fcb->inode, sizeof(uint64_t)); 1050 me->fileref->fcb->inode_item.st_nlink = 1; 1051 1052 defda = get_file_attributes(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode, 1053 me->fileref->fcb->type, me->fileref->dc && me->fileref->dc->name.Length >= sizeof(WCHAR) && me->fileref->dc->name.Buffer[0] == '.', 1054 true, Irp); 1055 1056 me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd; 1057 me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts; 1058 me->fileref->fcb->extents_changed = !IsListEmpty(&me->fileref->fcb->extents); 1059 me->fileref->fcb->reparse_xattr_changed = !!me->fileref->fcb->reparse_xattr.Buffer; 1060 me->fileref->fcb->ea_changed = !!me->fileref->fcb->ea_xattr.Buffer; 1061 me->fileref->fcb->xattrs_changed = !IsListEmpty(&me->fileref->fcb->xattrs); 1062 me->fileref->fcb->inode_item_changed = true; 1063 1064 le2 = me->fileref->fcb->xattrs.Flink; 1065 while (le2 != &me->fileref->fcb->xattrs) { 1066 xattr* xa = CONTAINING_RECORD(le2, xattr, list_entry); 1067 1068 xa->dirty = true; 1069 1070 le2 = le2->Flink; 1071 } 1072 1073 if (le == move_list.Flink) { // first entry 1074 me->fileref->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation; 1075 me->fileref->fcb->inode_item.sequence++; 1076 1077 if (!ccb->user_set_change_time) 1078 me->fileref->fcb->inode_item.st_ctime = now; 1079 } 1080 1081 le2 = me->fileref->fcb->extents.Flink; 1082 while (le2 != &me->fileref->fcb->extents) { 1083 extent* ext = CONTAINING_RECORD(le2, extent, list_entry); 1084 1085 if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) { 1086 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 1087 1088 if (ed2->size != 0) { 1089 chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address); 1090 1091 if (!c) { 1092 ERR("get_chunk_from_address(%I64x) failed\n", ed2->address); 1093 } else { 1094 Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode, 1095 ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp); 1096 1097 if (!NT_SUCCESS(Status)) { 1098 ERR("update_changed_extent_ref returned %08lx\n", Status); 1099 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 1100 goto end; 1101 } 1102 } 1103 1104 } 1105 } 1106 1107 le2 = le2->Flink; 1108 } 1109 1110 add_fcb_to_subvol(me->dummyfcb); 1111 remove_fcb_from_subvol(me->fileref->fcb); 1112 add_fcb_to_subvol(me->fileref->fcb); 1113 } else { 1114 me->fileref->fcb->subvol = me->parent->fileref->fcb->subvol; 1115 me->fileref->fcb->inode = me->parent->fileref->fcb->inode; 1116 me->fileref->fcb->hash = me->parent->fileref->fcb->hash; 1117 1118 // put stream after parent in FCB list 1119 InsertHeadList(&me->parent->fileref->fcb->list_entry, &me->fileref->fcb->list_entry); 1120 } 1121 1122 me->fileref->fcb->created = true; 1123 1124 InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all); 1125 1126 while (!IsListEmpty(&me->fileref->fcb->hardlinks)) { 1127 hardlink* hl = CONTAINING_RECORD(RemoveHeadList(&me->fileref->fcb->hardlinks), hardlink, list_entry); 1128 1129 if (hl->name.Buffer) 1130 ExFreePool(hl->name.Buffer); 1131 1132 if (hl->utf8.Buffer) 1133 ExFreePool(hl->utf8.Buffer); 1134 1135 ExFreePool(hl); 1136 } 1137 1138 me->fileref->fcb->inode_item_changed = true; 1139 mark_fcb_dirty(me->fileref->fcb); 1140 1141 if ((!me->dummyfcb->ads && me->dummyfcb->inode_item.st_nlink > 1) || (me->dummyfcb->ads && me->parent->dummyfcb->inode_item.st_nlink > 1)) { 1142 LIST_ENTRY* le2 = le->Flink; 1143 1144 while (le2 != &move_list) { 1145 move_entry* me2 = CONTAINING_RECORD(le2, move_entry, list_entry); 1146 1147 if (me2->fileref->fcb == me->fileref->fcb && !me2->fileref->fcb->ads) { 1148 me2->dummyfcb = me->dummyfcb; 1149 InterlockedIncrement(&me->dummyfcb->refcount); 1150 } 1151 1152 le2 = le2->Flink; 1153 } 1154 } 1155 1156 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 1157 } else { 1158 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, true); 1159 me->fileref->fcb->inode_item.st_nlink++; 1160 me->fileref->fcb->inode_item_changed = true; 1161 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 1162 } 1163 } 1164 1165 le = le->Flink; 1166 } 1167 1168 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation; 1169 fileref->fcb->subvol->root_item.ctime = now; 1170 1171 // loop through list and create new filerefs 1172 1173 le = move_list.Flink; 1174 while (le != &move_list) { 1175 hardlink* hl; 1176 bool name_changed = false; 1177 1178 me = CONTAINING_RECORD(le, move_entry, list_entry); 1179 1180 me->dummyfileref = create_fileref(fileref->fcb->Vcb); 1181 if (!me->dummyfileref) { 1182 ERR("out of memory\n"); 1183 Status = STATUS_INSUFFICIENT_RESOURCES; 1184 goto end; 1185 } 1186 1187 if (me->fileref->fcb == me->fileref->fcb->Vcb->dummy_fcb) { 1188 root* r = me->parent ? me->parent->fileref->fcb->subvol : destdir->fcb->subvol; 1189 1190 Status = create_directory_fcb(me->fileref->fcb->Vcb, r, me->fileref->parent->fcb, &me->fileref->fcb); 1191 if (!NT_SUCCESS(Status)) { 1192 ERR("create_directory_fcb returned %08lx\n", Status); 1193 goto end; 1194 } 1195 1196 me->fileref->dc->key.obj_id = me->fileref->fcb->inode; 1197 me->fileref->dc->key.obj_type = TYPE_INODE_ITEM; 1198 1199 me->dummyfileref->fcb = me->fileref->fcb->Vcb->dummy_fcb; 1200 } else if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) { 1201 me->dummyfileref->fcb = me->fileref->fcb; 1202 1203 me->fileref->fcb->subvol->parent = le == move_list.Flink ? destdir->fcb->subvol->id : me->parent->fileref->fcb->subvol->id; 1204 } else 1205 me->dummyfileref->fcb = me->dummyfcb; 1206 1207 InterlockedIncrement(&me->dummyfileref->fcb->refcount); 1208 1209 me->dummyfileref->oldutf8 = me->fileref->oldutf8; 1210 me->dummyfileref->oldindex = me->fileref->dc->index; 1211 1212 if (le == move_list.Flink && (me->fileref->dc->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length)) 1213 name_changed = true; 1214 1215 if (!me->dummyfileref->oldutf8.Buffer) { 1216 me->dummyfileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->dc->utf8.Length, ALLOC_TAG); 1217 if (!me->dummyfileref->oldutf8.Buffer) { 1218 ERR("out of memory\n"); 1219 Status = STATUS_INSUFFICIENT_RESOURCES; 1220 goto end; 1221 } 1222 1223 RtlCopyMemory(me->dummyfileref->oldutf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length); 1224 1225 me->dummyfileref->oldutf8.Length = me->dummyfileref->oldutf8.MaximumLength = me->fileref->dc->utf8.Length; 1226 } 1227 1228 me->dummyfileref->delete_on_close = me->fileref->delete_on_close; 1229 me->dummyfileref->deleted = me->fileref->deleted; 1230 1231 me->dummyfileref->created = me->fileref->created; 1232 me->fileref->created = true; 1233 1234 me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent; 1235 increase_fileref_refcount(me->dummyfileref->parent); 1236 1237 ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock, true); 1238 InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry); 1239 ExReleaseResourceLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock); 1240 1241 if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY) 1242 me->dummyfileref->fcb->fileref = me->dummyfileref; 1243 1244 if (!me->parent) { 1245 RemoveEntryList(&me->fileref->list_entry); 1246 1247 increase_fileref_refcount(destdir); 1248 1249 if (me->fileref->dc) { 1250 // remove from old parent 1251 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true); 1252 RemoveEntryList(&me->fileref->dc->list_entry_index); 1253 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc); 1254 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock); 1255 1256 me->fileref->parent->fcb->inode_item.st_size -= me->fileref->dc->utf8.Length * 2; 1257 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation; 1258 me->fileref->parent->fcb->inode_item.sequence++; 1259 me->fileref->parent->fcb->inode_item.st_ctime = now; 1260 me->fileref->parent->fcb->inode_item.st_mtime = now; 1261 me->fileref->parent->fcb->inode_item_changed = true; 1262 mark_fcb_dirty(me->fileref->parent->fcb); 1263 1264 if (name_changed) { 1265 ExFreePool(me->fileref->dc->utf8.Buffer); 1266 ExFreePool(me->fileref->dc->name.Buffer); 1267 ExFreePool(me->fileref->dc->name_uc.Buffer); 1268 1269 me->fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG); 1270 if (!me->fileref->dc->utf8.Buffer) { 1271 ERR("out of memory\n"); 1272 Status = STATUS_INSUFFICIENT_RESOURCES; 1273 goto end; 1274 } 1275 1276 me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length; 1277 RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length); 1278 1279 me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus->Length, ALLOC_TAG); 1280 if (!me->fileref->dc->name.Buffer) { 1281 ERR("out of memory\n"); 1282 Status = STATUS_INSUFFICIENT_RESOURCES; 1283 goto end; 1284 } 1285 1286 me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = fnus->Length; 1287 RtlCopyMemory(me->fileref->dc->name.Buffer, fnus->Buffer, fnus->Length); 1288 1289 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true); 1290 if (!NT_SUCCESS(Status)) { 1291 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); 1292 goto end; 1293 } 1294 1295 me->fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)me->fileref->dc->name.Buffer, me->fileref->dc->name.Length); 1296 me->fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)me->fileref->dc->name_uc.Buffer, me->fileref->dc->name_uc.Length); 1297 } 1298 1299 if (me->fileref->dc->key.obj_type == TYPE_INODE_ITEM) 1300 me->fileref->dc->key.obj_id = me->fileref->fcb->inode; 1301 1302 // add to new parent 1303 1304 ExAcquireResourceExclusiveLite(&destdir->fcb->nonpaged->dir_children_lock, true); 1305 1306 if (IsListEmpty(&destdir->fcb->dir_children_index)) 1307 me->fileref->dc->index = 2; 1308 else { 1309 dir_child* dc2 = CONTAINING_RECORD(destdir->fcb->dir_children_index.Blink, dir_child, list_entry_index); 1310 1311 me->fileref->dc->index = max(2, dc2->index + 1); 1312 } 1313 1314 InsertTailList(&destdir->fcb->dir_children_index, &me->fileref->dc->list_entry_index); 1315 insert_dir_child_into_hash_lists(destdir->fcb, me->fileref->dc); 1316 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock); 1317 } 1318 1319 free_fileref(me->fileref->parent); 1320 me->fileref->parent = destdir; 1321 1322 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true); 1323 InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry); 1324 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock); 1325 1326 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size); 1327 me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2; 1328 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size); 1329 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation; 1330 me->fileref->parent->fcb->inode_item.sequence++; 1331 me->fileref->parent->fcb->inode_item.st_ctime = now; 1332 me->fileref->parent->fcb->inode_item.st_mtime = now; 1333 me->fileref->parent->fcb->inode_item_changed = true; 1334 mark_fcb_dirty(me->fileref->parent->fcb); 1335 } else { 1336 if (me->fileref->dc) { 1337 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, true); 1338 RemoveEntryList(&me->fileref->dc->list_entry_index); 1339 1340 if (!me->fileref->fcb->ads) 1341 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc); 1342 1343 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock); 1344 1345 ExAcquireResourceExclusiveLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock, true); 1346 1347 if (me->fileref->fcb->ads) 1348 InsertHeadList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index); 1349 else { 1350 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE) 1351 me->fileref->dc->key.obj_id = me->fileref->fcb->inode; 1352 1353 if (IsListEmpty(&me->parent->fileref->fcb->dir_children_index)) 1354 me->fileref->dc->index = 2; 1355 else { 1356 dir_child* dc2 = CONTAINING_RECORD(me->parent->fileref->fcb->dir_children_index.Blink, dir_child, list_entry_index); 1357 1358 me->fileref->dc->index = max(2, dc2->index + 1); 1359 } 1360 1361 InsertTailList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index); 1362 insert_dir_child_into_hash_lists(me->parent->fileref->fcb, me->fileref->dc); 1363 } 1364 1365 ExReleaseResourceLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock); 1366 } 1367 } 1368 1369 if (!me->dummyfileref->fcb->ads) { 1370 Status = delete_fileref(me->dummyfileref, NULL, false, Irp, rollback); 1371 if (!NT_SUCCESS(Status)) { 1372 ERR("delete_fileref returned %08lx\n", Status); 1373 goto end; 1374 } 1375 } 1376 1377 if (me->fileref->fcb->inode_item.st_nlink > 1) { 1378 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 1379 if (!hl) { 1380 ERR("out of memory\n"); 1381 Status = STATUS_INSUFFICIENT_RESOURCES; 1382 goto end; 1383 } 1384 1385 hl->parent = me->fileref->parent->fcb->inode; 1386 hl->index = me->fileref->dc->index; 1387 1388 hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->dc->utf8.Length; 1389 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 1390 if (!hl->utf8.Buffer) { 1391 ERR("out of memory\n"); 1392 Status = STATUS_INSUFFICIENT_RESOURCES; 1393 ExFreePool(hl); 1394 goto end; 1395 } 1396 1397 RtlCopyMemory(hl->utf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length); 1398 1399 hl->name.Length = hl->name.MaximumLength = me->fileref->dc->name.Length; 1400 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); 1401 if (!hl->name.Buffer) { 1402 ERR("out of memory\n"); 1403 Status = STATUS_INSUFFICIENT_RESOURCES; 1404 ExFreePool(hl->utf8.Buffer); 1405 ExFreePool(hl); 1406 goto end; 1407 } 1408 1409 RtlCopyMemory(hl->name.Buffer, me->fileref->dc->name.Buffer, me->fileref->dc->name.Length); 1410 1411 InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry); 1412 } 1413 1414 mark_fileref_dirty(me->fileref); 1415 1416 le = le->Flink; 1417 } 1418 1419 // loop through, and only mark streams as deleted if their parent inodes are also deleted 1420 1421 le = move_list.Flink; 1422 while (le != &move_list) { 1423 me = CONTAINING_RECORD(le, move_entry, list_entry); 1424 1425 if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) { 1426 Status = delete_fileref(me->dummyfileref, NULL, false, Irp, rollback); 1427 if (!NT_SUCCESS(Status)) { 1428 ERR("delete_fileref returned %08lx\n", Status); 1429 goto end; 1430 } 1431 } 1432 1433 le = le->Flink; 1434 } 1435 1436 destdir->fcb->subvol->root_item.ctransid = destdir->fcb->Vcb->superblock.generation; 1437 destdir->fcb->subvol->root_item.ctime = now; 1438 1439 me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry); 1440 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); 1441 send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 1442 send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 1443 1444 Status = STATUS_SUCCESS; 1445 1446 end: 1447 while (!IsListEmpty(&move_list)) { 1448 le = RemoveHeadList(&move_list); 1449 me = CONTAINING_RECORD(le, move_entry, list_entry); 1450 1451 if (me->dummyfcb) 1452 free_fcb(me->dummyfcb); 1453 1454 if (me->dummyfileref) 1455 free_fileref(me->dummyfileref); 1456 1457 free_fileref(me->fileref); 1458 1459 ExFreePool(me); 1460 } 1461 1462 destdir->fcb->subvol->fcbs_version++; 1463 fileref->fcb->subvol->fcbs_version++; 1464 1465 release_fcb_lock(fileref->fcb->Vcb); 1466 1467 return Status; 1468 } 1469 1470 void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) { 1471 bool inserted; 1472 LIST_ENTRY* le; 1473 uint8_t c, d; 1474 1475 c = dc->hash >> 24; 1476 1477 inserted = false; 1478 1479 d = c; 1480 do { 1481 le = fcb->hash_ptrs[d]; 1482 1483 if (d == 0) 1484 break; 1485 1486 d--; 1487 } while (!le); 1488 1489 if (!le) 1490 le = fcb->dir_children_hash.Flink; 1491 1492 while (le != &fcb->dir_children_hash) { 1493 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash); 1494 1495 if (dc2->hash > dc->hash) { 1496 InsertHeadList(le->Blink, &dc->list_entry_hash); 1497 inserted = true; 1498 break; 1499 } 1500 1501 le = le->Flink; 1502 } 1503 1504 if (!inserted) 1505 InsertTailList(&fcb->dir_children_hash, &dc->list_entry_hash); 1506 1507 if (!fcb->hash_ptrs[c]) 1508 fcb->hash_ptrs[c] = &dc->list_entry_hash; 1509 else { 1510 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs[c], dir_child, list_entry_hash); 1511 1512 if (dc2->hash > dc->hash) 1513 fcb->hash_ptrs[c] = &dc->list_entry_hash; 1514 } 1515 1516 c = dc->hash_uc >> 24; 1517 1518 inserted = false; 1519 1520 d = c; 1521 do { 1522 le = fcb->hash_ptrs_uc[d]; 1523 1524 if (d == 0) 1525 break; 1526 1527 d--; 1528 } while (!le); 1529 1530 if (!le) 1531 le = fcb->dir_children_hash_uc.Flink; 1532 1533 while (le != &fcb->dir_children_hash_uc) { 1534 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc); 1535 1536 if (dc2->hash_uc > dc->hash_uc) { 1537 InsertHeadList(le->Blink, &dc->list_entry_hash_uc); 1538 inserted = true; 1539 break; 1540 } 1541 1542 le = le->Flink; 1543 } 1544 1545 if (!inserted) 1546 InsertTailList(&fcb->dir_children_hash_uc, &dc->list_entry_hash_uc); 1547 1548 if (!fcb->hash_ptrs_uc[c]) 1549 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc; 1550 else { 1551 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs_uc[c], dir_child, list_entry_hash_uc); 1552 1553 if (dc2->hash_uc > dc->hash_uc) 1554 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc; 1555 } 1556 } 1557 1558 static NTSTATUS rename_stream_to_file(device_extension* Vcb, file_ref* fileref, ccb* ccb, ULONG flags, 1559 PIRP Irp, LIST_ENTRY* rollback) { 1560 NTSTATUS Status; 1561 file_ref* ofr; 1562 ANSI_STRING adsdata; 1563 dir_child* dc; 1564 fcb* dummyfcb; 1565 1566 if (fileref->fcb->type != BTRFS_TYPE_FILE) 1567 return STATUS_INVALID_PARAMETER; 1568 1569 if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) { 1570 WARN("trying to rename stream on readonly file\n"); 1571 return STATUS_ACCESS_DENIED; 1572 } 1573 1574 if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) { 1575 WARN("insufficient permissions\n"); 1576 return STATUS_ACCESS_DENIED; 1577 } 1578 1579 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) // file will always exist 1580 return STATUS_OBJECT_NAME_COLLISION; 1581 1582 // FIXME - POSIX overwrites of stream? 1583 1584 ofr = fileref->parent; 1585 1586 if (ofr->open_count > 0) { 1587 WARN("trying to overwrite open file\n"); 1588 return STATUS_ACCESS_DENIED; 1589 } 1590 1591 if (ofr->fcb->inode_item.st_size > 0) { 1592 WARN("can only overwrite existing stream if it is zero-length\n"); 1593 return STATUS_INVALID_PARAMETER; 1594 } 1595 1596 dummyfcb = create_fcb(Vcb, PagedPool); 1597 if (!dummyfcb) { 1598 ERR("out of memory\n"); 1599 return STATUS_INSUFFICIENT_RESOURCES; 1600 } 1601 1602 // copy parent fcb onto this one 1603 1604 fileref->fcb->subvol = ofr->fcb->subvol; 1605 fileref->fcb->inode = ofr->fcb->inode; 1606 fileref->fcb->hash = ofr->fcb->hash; 1607 fileref->fcb->type = ofr->fcb->type; 1608 fileref->fcb->inode_item = ofr->fcb->inode_item; 1609 1610 fileref->fcb->sd = ofr->fcb->sd; 1611 ofr->fcb->sd = NULL; 1612 1613 fileref->fcb->deleted = ofr->fcb->deleted; 1614 fileref->fcb->atts = ofr->fcb->atts; 1615 1616 fileref->fcb->reparse_xattr = ofr->fcb->reparse_xattr; 1617 ofr->fcb->reparse_xattr.Buffer = NULL; 1618 ofr->fcb->reparse_xattr.Length = ofr->fcb->reparse_xattr.MaximumLength = 0; 1619 1620 fileref->fcb->ea_xattr = ofr->fcb->ea_xattr; 1621 ofr->fcb->ea_xattr.Buffer = NULL; 1622 ofr->fcb->ea_xattr.Length = ofr->fcb->ea_xattr.MaximumLength = 0; 1623 1624 fileref->fcb->ealen = ofr->fcb->ealen; 1625 1626 while (!IsListEmpty(&ofr->fcb->hardlinks)) { 1627 InsertTailList(&fileref->fcb->hardlinks, RemoveHeadList(&ofr->fcb->hardlinks)); 1628 } 1629 1630 fileref->fcb->inode_item_changed = true; 1631 fileref->fcb->prop_compression = ofr->fcb->prop_compression; 1632 1633 while (!IsListEmpty(&ofr->fcb->xattrs)) { 1634 InsertTailList(&fileref->fcb->xattrs, RemoveHeadList(&ofr->fcb->xattrs)); 1635 } 1636 1637 fileref->fcb->marked_as_orphan = ofr->fcb->marked_as_orphan; 1638 fileref->fcb->case_sensitive = ofr->fcb->case_sensitive; 1639 fileref->fcb->case_sensitive_set = ofr->fcb->case_sensitive_set; 1640 1641 while (!IsListEmpty(&ofr->fcb->dir_children_index)) { 1642 InsertTailList(&fileref->fcb->dir_children_index, RemoveHeadList(&ofr->fcb->dir_children_index)); 1643 } 1644 1645 while (!IsListEmpty(&ofr->fcb->dir_children_hash)) { 1646 InsertTailList(&fileref->fcb->dir_children_hash, RemoveHeadList(&ofr->fcb->dir_children_hash)); 1647 } 1648 1649 while (!IsListEmpty(&ofr->fcb->dir_children_hash_uc)) { 1650 InsertTailList(&fileref->fcb->dir_children_hash_uc, RemoveHeadList(&ofr->fcb->dir_children_hash_uc)); 1651 } 1652 1653 fileref->fcb->hash_ptrs = ofr->fcb->hash_ptrs; 1654 fileref->fcb->hash_ptrs_uc = ofr->fcb->hash_ptrs_uc; 1655 1656 ofr->fcb->hash_ptrs = NULL; 1657 ofr->fcb->hash_ptrs_uc = NULL; 1658 1659 fileref->fcb->sd_dirty = ofr->fcb->sd_dirty; 1660 fileref->fcb->sd_deleted = ofr->fcb->sd_deleted; 1661 fileref->fcb->atts_changed = ofr->fcb->atts_changed; 1662 fileref->fcb->atts_deleted = ofr->fcb->atts_deleted; 1663 fileref->fcb->extents_changed = true; 1664 fileref->fcb->reparse_xattr_changed = ofr->fcb->reparse_xattr_changed; 1665 fileref->fcb->ea_changed = ofr->fcb->ea_changed; 1666 fileref->fcb->prop_compression_changed = ofr->fcb->prop_compression_changed; 1667 fileref->fcb->xattrs_changed = ofr->fcb->xattrs_changed; 1668 fileref->fcb->created = ofr->fcb->created; 1669 fileref->fcb->ads = false; 1670 1671 if (fileref->fcb->adsxattr.Buffer) { 1672 ExFreePool(fileref->fcb->adsxattr.Buffer); 1673 fileref->fcb->adsxattr.Length = fileref->fcb->adsxattr.MaximumLength = 0; 1674 fileref->fcb->adsxattr.Buffer = NULL; 1675 } 1676 1677 adsdata = fileref->fcb->adsdata; 1678 1679 fileref->fcb->adsdata.Buffer = NULL; 1680 fileref->fcb->adsdata.Length = fileref->fcb->adsdata.MaximumLength = 0; 1681 1682 acquire_fcb_lock_exclusive(Vcb); 1683 1684 RemoveEntryList(&fileref->fcb->list_entry); 1685 InsertHeadList(ofr->fcb->list_entry.Blink, &fileref->fcb->list_entry); 1686 1687 if (fileref->fcb->subvol->fcbs_ptrs[fileref->fcb->hash >> 24] == &ofr->fcb->list_entry) 1688 fileref->fcb->subvol->fcbs_ptrs[fileref->fcb->hash >> 24] = &fileref->fcb->list_entry; 1689 1690 RemoveEntryList(&ofr->fcb->list_entry); 1691 1692 release_fcb_lock(Vcb); 1693 1694 ofr->fcb->list_entry.Flink = ofr->fcb->list_entry.Blink = NULL; 1695 1696 mark_fcb_dirty(fileref->fcb); 1697 1698 // mark old parent fcb so it gets ignored by flush_fcb 1699 ofr->fcb->created = true; 1700 ofr->fcb->deleted = true; 1701 1702 mark_fcb_dirty(ofr->fcb); 1703 1704 // copy parent fileref onto this one 1705 1706 fileref->oldutf8 = ofr->oldutf8; 1707 ofr->oldutf8.Buffer = NULL; 1708 ofr->oldutf8.Length = ofr->oldutf8.MaximumLength = 0; 1709 1710 fileref->oldindex = ofr->oldindex; 1711 fileref->delete_on_close = ofr->delete_on_close; 1712 fileref->posix_delete = ofr->posix_delete; 1713 fileref->deleted = ofr->deleted; 1714 fileref->created = ofr->created; 1715 1716 fileref->parent = ofr->parent; 1717 1718 RemoveEntryList(&fileref->list_entry); 1719 InsertHeadList(ofr->list_entry.Blink, &fileref->list_entry); 1720 RemoveEntryList(&ofr->list_entry); 1721 ofr->list_entry.Flink = ofr->list_entry.Blink = NULL; 1722 1723 while (!IsListEmpty(&ofr->children)) { 1724 file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&ofr->children), file_ref, list_entry); 1725 1726 free_fileref(fr->parent); 1727 1728 fr->parent = fileref; 1729 InterlockedIncrement(&fileref->refcount); 1730 1731 InsertTailList(&fileref->children, &fr->list_entry); 1732 } 1733 1734 dc = fileref->dc; 1735 1736 fileref->dc = ofr->dc; 1737 fileref->dc->fileref = fileref; 1738 1739 mark_fileref_dirty(fileref); 1740 1741 // mark old parent fileref so it gets ignored by flush_fileref 1742 ofr->created = true; 1743 ofr->deleted = true; 1744 1745 // write file data 1746 1747 fileref->fcb->inode_item.st_size = adsdata.Length; 1748 1749 if (adsdata.Length > 0) { 1750 bool make_inline = adsdata.Length <= Vcb->options.max_inline; 1751 1752 if (make_inline) { 1753 EXTENT_DATA* ed = ExAllocatePoolWithTag(PagedPool, (uint16_t)(offsetof(EXTENT_DATA, data[0]) + adsdata.Length), ALLOC_TAG); 1754 if (!ed) { 1755 ERR("out of memory\n"); 1756 ExFreePool(adsdata.Buffer); 1757 reap_fcb(dummyfcb); 1758 return STATUS_INSUFFICIENT_RESOURCES; 1759 } 1760 1761 ed->generation = Vcb->superblock.generation; 1762 ed->decoded_size = adsdata.Length; 1763 ed->compression = BTRFS_COMPRESSION_NONE; 1764 ed->encryption = BTRFS_ENCRYPTION_NONE; 1765 ed->encoding = BTRFS_ENCODING_NONE; 1766 ed->type = EXTENT_TYPE_INLINE; 1767 1768 RtlCopyMemory(ed->data, adsdata.Buffer, adsdata.Length); 1769 1770 ExFreePool(adsdata.Buffer); 1771 1772 Status = add_extent_to_fcb(fileref->fcb, 0, ed, (uint16_t)(offsetof(EXTENT_DATA, data[0]) + adsdata.Length), false, NULL, rollback); 1773 if (!NT_SUCCESS(Status)) { 1774 ERR("add_extent_to_fcb returned %08lx\n", Status); 1775 ExFreePool(ed); 1776 reap_fcb(dummyfcb); 1777 return Status; 1778 } 1779 1780 ExFreePool(ed); 1781 } else if (adsdata.Length & (Vcb->superblock.sector_size - 1)) { 1782 char* newbuf = ExAllocatePoolWithTag(PagedPool, (uint16_t)sector_align(adsdata.Length, Vcb->superblock.sector_size), ALLOC_TAG); 1783 if (!newbuf) { 1784 ERR("out of memory\n"); 1785 ExFreePool(adsdata.Buffer); 1786 reap_fcb(dummyfcb); 1787 return STATUS_INSUFFICIENT_RESOURCES; 1788 } 1789 1790 RtlCopyMemory(newbuf, adsdata.Buffer, adsdata.Length); 1791 RtlZeroMemory(newbuf + adsdata.Length, (uint16_t)(sector_align(adsdata.Length, Vcb->superblock.sector_size) - adsdata.Length)); 1792 1793 ExFreePool(adsdata.Buffer); 1794 1795 adsdata.Buffer = newbuf; 1796 adsdata.Length = adsdata.MaximumLength = (uint16_t)sector_align(adsdata.Length, Vcb->superblock.sector_size); 1797 } 1798 1799 if (!make_inline) { 1800 Status = do_write_file(fileref->fcb, 0, adsdata.Length, adsdata.Buffer, Irp, false, 0, rollback); 1801 if (!NT_SUCCESS(Status)) { 1802 ERR("do_write_file returned %08lx\n", Status); 1803 ExFreePool(adsdata.Buffer); 1804 reap_fcb(dummyfcb); 1805 return Status; 1806 } 1807 1808 ExFreePool(adsdata.Buffer); 1809 } 1810 1811 fileref->fcb->inode_item.st_blocks = adsdata.Length; 1812 fileref->fcb->inode_item_changed = true; 1813 } 1814 1815 RemoveEntryList(&dc->list_entry_index); 1816 1817 if (dc->utf8.Buffer) 1818 ExFreePool(dc->utf8.Buffer); 1819 1820 if (dc->name.Buffer) 1821 ExFreePool(dc->name.Buffer); 1822 1823 if (dc->name_uc.Buffer) 1824 ExFreePool(dc->name_uc.Buffer); 1825 1826 ExFreePool(dc); 1827 1828 // FIXME - csums? 1829 1830 // add dummy deleted xattr with old name 1831 1832 dummyfcb->Vcb = Vcb; 1833 dummyfcb->subvol = fileref->fcb->subvol; 1834 dummyfcb->inode = fileref->fcb->inode; 1835 dummyfcb->hash = fileref->fcb->hash; 1836 dummyfcb->adsxattr = fileref->fcb->adsxattr; 1837 dummyfcb->adshash = fileref->fcb->adshash; 1838 dummyfcb->ads = true; 1839 dummyfcb->deleted = true; 1840 1841 acquire_fcb_lock_exclusive(Vcb); 1842 add_fcb_to_subvol(dummyfcb); 1843 InsertTailList(&Vcb->all_fcbs, &dummyfcb->list_entry_all); 1844 dummyfcb->subvol->fcbs_version++; 1845 release_fcb_lock(Vcb); 1846 1847 // FIXME - dummyfileref as well? 1848 1849 mark_fcb_dirty(dummyfcb); 1850 1851 free_fcb(dummyfcb); 1852 1853 return STATUS_SUCCESS; 1854 } 1855 1856 static NTSTATUS rename_stream(device_extension* Vcb, file_ref* fileref, ccb* ccb, FILE_RENAME_INFORMATION_EX* fri, 1857 ULONG flags, PIRP Irp, LIST_ENTRY* rollback) { 1858 NTSTATUS Status; 1859 UNICODE_STRING fn; 1860 file_ref* sf = NULL; 1861 uint16_t newmaxlen; 1862 ULONG utf8len; 1863 ANSI_STRING utf8; 1864 UNICODE_STRING utf16, utf16uc; 1865 ANSI_STRING adsxattr; 1866 uint32_t crc32; 1867 fcb* dummyfcb; 1868 1869 static const WCHAR datasuf[] = L":$DATA"; 1870 static const char xapref[] = "user."; 1871 1872 if (!fileref) { 1873 ERR("fileref not set\n"); 1874 return STATUS_INVALID_PARAMETER; 1875 } 1876 1877 if (!fileref->parent) { 1878 ERR("fileref->parent not set\n"); 1879 return STATUS_INVALID_PARAMETER; 1880 } 1881 1882 if (fri->FileNameLength < sizeof(WCHAR)) { 1883 WARN("filename too short\n"); 1884 return STATUS_OBJECT_NAME_INVALID; 1885 } 1886 1887 if (fri->FileName[0] != ':') { 1888 WARN("destination filename must begin with a colon\n"); 1889 return STATUS_INVALID_PARAMETER; 1890 } 1891 1892 if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) { 1893 WARN("insufficient permissions\n"); 1894 return STATUS_ACCESS_DENIED; 1895 } 1896 1897 fn.Buffer = &fri->FileName[1]; 1898 fn.Length = fn.MaximumLength = (USHORT)(fri->FileNameLength - sizeof(WCHAR)); 1899 1900 // remove :$DATA suffix 1901 if (fn.Length >= sizeof(datasuf) - sizeof(WCHAR) && 1902 RtlCompareMemory(&fn.Buffer[(fn.Length - sizeof(datasuf) + sizeof(WCHAR))/sizeof(WCHAR)], datasuf, sizeof(datasuf) - sizeof(WCHAR)) == sizeof(datasuf) - sizeof(WCHAR)) 1903 fn.Length -= sizeof(datasuf) - sizeof(WCHAR); 1904 1905 if (fn.Length == 0) 1906 return rename_stream_to_file(Vcb, fileref, ccb, flags, Irp, rollback); 1907 1908 Status = check_file_name_valid(&fn, false, true); 1909 if (!NT_SUCCESS(Status)) { 1910 WARN("invalid stream name %.*S\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer); 1911 return Status; 1912 } 1913 1914 if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->parent->fcb->atts & FILE_ATTRIBUTE_READONLY) { 1915 WARN("trying to rename stream on readonly file\n"); 1916 return STATUS_ACCESS_DENIED; 1917 } 1918 1919 Status = open_fileref_child(Vcb, fileref->parent, &fn, fileref->parent->fcb->case_sensitive, true, true, PagedPool, &sf, Irp); 1920 if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { 1921 if (Status == STATUS_SUCCESS) { 1922 if (fileref == sf || sf->deleted) { 1923 free_fileref(sf); 1924 sf = NULL; 1925 } else { 1926 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) { 1927 Status = STATUS_OBJECT_NAME_COLLISION; 1928 goto end; 1929 } 1930 1931 // FIXME - POSIX overwrites of stream? 1932 1933 if (sf->open_count > 0) { 1934 WARN("trying to overwrite open file\n"); 1935 Status = STATUS_ACCESS_DENIED; 1936 goto end; 1937 } 1938 1939 if (sf->fcb->adsdata.Length > 0) { 1940 WARN("can only overwrite existing stream if it is zero-length\n"); 1941 Status = STATUS_INVALID_PARAMETER; 1942 goto end; 1943 } 1944 1945 Status = delete_fileref(sf, NULL, false, Irp, rollback); 1946 if (!NT_SUCCESS(Status)) { 1947 ERR("delete_fileref returned %08lx\n", Status); 1948 goto end; 1949 } 1950 } 1951 } else { 1952 ERR("open_fileref_child returned %08lx\n", Status); 1953 goto end; 1954 } 1955 } 1956 1957 Status = utf16_to_utf8(NULL, 0, &utf8len, fn.Buffer, fn.Length); 1958 if (!NT_SUCCESS(Status)) 1959 goto end; 1960 1961 utf8.MaximumLength = utf8.Length = (uint16_t)utf8len; 1962 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); 1963 if (!utf8.Buffer) { 1964 ERR("out of memory\n"); 1965 Status = STATUS_INSUFFICIENT_RESOURCES; 1966 goto end; 1967 } 1968 1969 Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn.Buffer, fn.Length); 1970 if (!NT_SUCCESS(Status)) { 1971 ExFreePool(utf8.Buffer); 1972 goto end; 1973 } 1974 1975 adsxattr.Length = adsxattr.MaximumLength = sizeof(xapref) - 1 + utf8.Length; 1976 adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, adsxattr.MaximumLength, ALLOC_TAG); 1977 if (!adsxattr.Buffer) { 1978 ERR("out of memory\n"); 1979 Status = STATUS_INSUFFICIENT_RESOURCES; 1980 ExFreePool(utf8.Buffer); 1981 goto end; 1982 } 1983 1984 RtlCopyMemory(adsxattr.Buffer, xapref, sizeof(xapref) - 1); 1985 RtlCopyMemory(&adsxattr.Buffer[sizeof(xapref) - 1], utf8.Buffer, utf8.Length); 1986 1987 // don't allow if it's one of our reserved names 1988 1989 if ((adsxattr.Length == sizeof(EA_DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_DOSATTRIB, adsxattr.Length) == adsxattr.Length) || 1990 (adsxattr.Length == sizeof(EA_EA) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_EA, adsxattr.Length) == adsxattr.Length) || 1991 (adsxattr.Length == sizeof(EA_REPARSE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_REPARSE, adsxattr.Length) == adsxattr.Length) || 1992 (adsxattr.Length == sizeof(EA_CASE_SENSITIVE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_CASE_SENSITIVE, adsxattr.Length) == adsxattr.Length)) { 1993 Status = STATUS_OBJECT_NAME_INVALID; 1994 ExFreePool(utf8.Buffer); 1995 ExFreePool(adsxattr.Buffer); 1996 goto end; 1997 } 1998 1999 utf16.Length = utf16.MaximumLength = fn.Length; 2000 utf16.Buffer = ExAllocatePoolWithTag(PagedPool, utf16.MaximumLength, ALLOC_TAG); 2001 if (!utf16.Buffer) { 2002 ERR("out of memory\n"); 2003 Status = STATUS_INSUFFICIENT_RESOURCES; 2004 ExFreePool(utf8.Buffer); 2005 ExFreePool(adsxattr.Buffer); 2006 goto end; 2007 } 2008 2009 RtlCopyMemory(utf16.Buffer, fn.Buffer, fn.Length); 2010 2011 newmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - 2012 offsetof(DIR_ITEM, name[0]); 2013 2014 if (newmaxlen < adsxattr.Length) { 2015 WARN("cannot rename as data too long\n"); 2016 Status = STATUS_INVALID_PARAMETER; 2017 ExFreePool(utf8.Buffer); 2018 ExFreePool(utf16.Buffer); 2019 ExFreePool(adsxattr.Buffer); 2020 goto end; 2021 } 2022 2023 newmaxlen -= adsxattr.Length; 2024 2025 if (newmaxlen < fileref->fcb->adsdata.Length) { 2026 WARN("cannot rename as data too long\n"); 2027 Status = STATUS_INVALID_PARAMETER; 2028 ExFreePool(utf8.Buffer); 2029 ExFreePool(utf16.Buffer); 2030 ExFreePool(adsxattr.Buffer); 2031 goto end; 2032 } 2033 2034 Status = RtlUpcaseUnicodeString(&utf16uc, &fn, true); 2035 if (!NT_SUCCESS(Status)) { 2036 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); 2037 ExFreePool(utf8.Buffer); 2038 ExFreePool(utf16.Buffer); 2039 ExFreePool(adsxattr.Buffer); 2040 goto end; 2041 } 2042 2043 // add dummy deleted xattr with old name 2044 2045 dummyfcb = create_fcb(Vcb, PagedPool); 2046 if (!dummyfcb) { 2047 ERR("out of memory\n"); 2048 Status = STATUS_INSUFFICIENT_RESOURCES; 2049 ExFreePool(utf8.Buffer); 2050 ExFreePool(utf16.Buffer); 2051 ExFreePool(utf16uc.Buffer); 2052 ExFreePool(adsxattr.Buffer); 2053 goto end; 2054 } 2055 2056 dummyfcb->Vcb = Vcb; 2057 dummyfcb->subvol = fileref->fcb->subvol; 2058 dummyfcb->inode = fileref->fcb->inode; 2059 dummyfcb->hash = fileref->fcb->hash; 2060 dummyfcb->adsxattr = fileref->fcb->adsxattr; 2061 dummyfcb->adshash = fileref->fcb->adshash; 2062 dummyfcb->ads = true; 2063 dummyfcb->deleted = true; 2064 2065 acquire_fcb_lock_exclusive(Vcb); 2066 add_fcb_to_subvol(dummyfcb); 2067 InsertTailList(&Vcb->all_fcbs, &dummyfcb->list_entry_all); 2068 dummyfcb->subvol->fcbs_version++; 2069 release_fcb_lock(Vcb); 2070 2071 mark_fcb_dirty(dummyfcb); 2072 2073 free_fcb(dummyfcb); 2074 2075 // change fcb values 2076 2077 fileref->dc->utf8 = utf8; 2078 fileref->dc->name = utf16; 2079 fileref->dc->name_uc = utf16uc; 2080 2081 crc32 = calc_crc32c(0xfffffffe, (uint8_t*)adsxattr.Buffer, adsxattr.Length); 2082 2083 fileref->fcb->adsxattr = adsxattr; 2084 fileref->fcb->adshash = crc32; 2085 fileref->fcb->adsmaxlen = newmaxlen; 2086 2087 fileref->fcb->created = true; 2088 2089 mark_fcb_dirty(fileref->fcb); 2090 2091 Status = STATUS_SUCCESS; 2092 2093 end: 2094 if (sf) 2095 free_fileref(sf); 2096 2097 return Status; 2098 } 2099 2100 static NTSTATUS rename_file_to_stream(device_extension* Vcb, file_ref* fileref, ccb* ccb, FILE_RENAME_INFORMATION_EX* fri, 2101 ULONG flags, PIRP Irp, LIST_ENTRY* rollback) { 2102 NTSTATUS Status; 2103 UNICODE_STRING fn; 2104 file_ref* sf = NULL; 2105 uint16_t newmaxlen; 2106 ULONG utf8len; 2107 ANSI_STRING utf8; 2108 UNICODE_STRING utf16, utf16uc; 2109 ANSI_STRING adsxattr, adsdata; 2110 uint32_t crc32; 2111 fcb* dummyfcb; 2112 file_ref* dummyfileref; 2113 dir_child* dc; 2114 LIST_ENTRY* le; 2115 2116 static const WCHAR datasuf[] = L":$DATA"; 2117 static const char xapref[] = "user."; 2118 2119 if (!fileref) { 2120 ERR("fileref not set\n"); 2121 return STATUS_INVALID_PARAMETER; 2122 } 2123 2124 if (fri->FileNameLength < sizeof(WCHAR)) { 2125 WARN("filename too short\n"); 2126 return STATUS_OBJECT_NAME_INVALID; 2127 } 2128 2129 if (fri->FileName[0] != ':') { 2130 WARN("destination filename must begin with a colon\n"); 2131 return STATUS_INVALID_PARAMETER; 2132 } 2133 2134 if (Irp->RequestorMode == UserMode && ccb && !(ccb->access & DELETE)) { 2135 WARN("insufficient permissions\n"); 2136 return STATUS_ACCESS_DENIED; 2137 } 2138 2139 if (fileref->fcb->type != BTRFS_TYPE_FILE) 2140 return STATUS_INVALID_PARAMETER; 2141 2142 fn.Buffer = &fri->FileName[1]; 2143 fn.Length = fn.MaximumLength = (USHORT)(fri->FileNameLength - sizeof(WCHAR)); 2144 2145 // remove :$DATA suffix 2146 if (fn.Length >= sizeof(datasuf) - sizeof(WCHAR) && 2147 RtlCompareMemory(&fn.Buffer[(fn.Length - sizeof(datasuf) + sizeof(WCHAR))/sizeof(WCHAR)], datasuf, sizeof(datasuf) - sizeof(WCHAR)) == sizeof(datasuf) - sizeof(WCHAR)) 2148 fn.Length -= sizeof(datasuf) - sizeof(WCHAR); 2149 2150 if (fn.Length == 0) { 2151 WARN("not allowing overwriting file with itself\n"); 2152 return STATUS_INVALID_PARAMETER; 2153 } 2154 2155 Status = check_file_name_valid(&fn, false, true); 2156 if (!NT_SUCCESS(Status)) { 2157 WARN("invalid stream name %.*S\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer); 2158 return Status; 2159 } 2160 2161 if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && fileref->fcb->atts & FILE_ATTRIBUTE_READONLY) { 2162 WARN("trying to rename stream on readonly file\n"); 2163 return STATUS_ACCESS_DENIED; 2164 } 2165 2166 Status = open_fileref_child(Vcb, fileref, &fn, fileref->fcb->case_sensitive, true, true, PagedPool, &sf, Irp); 2167 if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { 2168 if (Status == STATUS_SUCCESS) { 2169 if (fileref == sf || sf->deleted) { 2170 free_fileref(sf); 2171 sf = NULL; 2172 } else { 2173 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) { 2174 Status = STATUS_OBJECT_NAME_COLLISION; 2175 goto end; 2176 } 2177 2178 // FIXME - POSIX overwrites of stream? 2179 2180 if (sf->open_count > 0) { 2181 WARN("trying to overwrite open file\n"); 2182 Status = STATUS_ACCESS_DENIED; 2183 goto end; 2184 } 2185 2186 if (sf->fcb->adsdata.Length > 0) { 2187 WARN("can only overwrite existing stream if it is zero-length\n"); 2188 Status = STATUS_INVALID_PARAMETER; 2189 goto end; 2190 } 2191 2192 Status = delete_fileref(sf, NULL, false, Irp, rollback); 2193 if (!NT_SUCCESS(Status)) { 2194 ERR("delete_fileref returned %08lx\n", Status); 2195 goto end; 2196 } 2197 } 2198 } else { 2199 ERR("open_fileref_child returned %08lx\n", Status); 2200 goto end; 2201 } 2202 } 2203 2204 Status = utf16_to_utf8(NULL, 0, &utf8len, fn.Buffer, fn.Length); 2205 if (!NT_SUCCESS(Status)) 2206 goto end; 2207 2208 utf8.MaximumLength = utf8.Length = (uint16_t)utf8len; 2209 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); 2210 if (!utf8.Buffer) { 2211 ERR("out of memory\n"); 2212 Status = STATUS_INSUFFICIENT_RESOURCES; 2213 goto end; 2214 } 2215 2216 Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn.Buffer, fn.Length); 2217 if (!NT_SUCCESS(Status)) { 2218 ExFreePool(utf8.Buffer); 2219 goto end; 2220 } 2221 2222 adsxattr.Length = adsxattr.MaximumLength = sizeof(xapref) - 1 + utf8.Length; 2223 adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, adsxattr.MaximumLength, ALLOC_TAG); 2224 if (!adsxattr.Buffer) { 2225 ERR("out of memory\n"); 2226 Status = STATUS_INSUFFICIENT_RESOURCES; 2227 ExFreePool(utf8.Buffer); 2228 goto end; 2229 } 2230 2231 RtlCopyMemory(adsxattr.Buffer, xapref, sizeof(xapref) - 1); 2232 RtlCopyMemory(&adsxattr.Buffer[sizeof(xapref) - 1], utf8.Buffer, utf8.Length); 2233 2234 // don't allow if it's one of our reserved names 2235 2236 if ((adsxattr.Length == sizeof(EA_DOSATTRIB) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_DOSATTRIB, adsxattr.Length) == adsxattr.Length) || 2237 (adsxattr.Length == sizeof(EA_EA) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_EA, adsxattr.Length) == adsxattr.Length) || 2238 (adsxattr.Length == sizeof(EA_REPARSE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_REPARSE, adsxattr.Length) == adsxattr.Length) || 2239 (adsxattr.Length == sizeof(EA_CASE_SENSITIVE) - sizeof(WCHAR) && RtlCompareMemory(adsxattr.Buffer, EA_CASE_SENSITIVE, adsxattr.Length) == adsxattr.Length)) { 2240 Status = STATUS_OBJECT_NAME_INVALID; 2241 ExFreePool(utf8.Buffer); 2242 ExFreePool(adsxattr.Buffer); 2243 goto end; 2244 } 2245 2246 utf16.Length = utf16.MaximumLength = fn.Length; 2247 utf16.Buffer = ExAllocatePoolWithTag(PagedPool, utf16.MaximumLength, ALLOC_TAG); 2248 if (!utf16.Buffer) { 2249 ERR("out of memory\n"); 2250 Status = STATUS_INSUFFICIENT_RESOURCES; 2251 ExFreePool(utf8.Buffer); 2252 ExFreePool(adsxattr.Buffer); 2253 goto end; 2254 } 2255 2256 RtlCopyMemory(utf16.Buffer, fn.Buffer, fn.Length); 2257 2258 newmaxlen = Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - 2259 offsetof(DIR_ITEM, name[0]); 2260 2261 if (newmaxlen < adsxattr.Length) { 2262 WARN("cannot rename as data too long\n"); 2263 Status = STATUS_INVALID_PARAMETER; 2264 ExFreePool(utf8.Buffer); 2265 ExFreePool(utf16.Buffer); 2266 ExFreePool(adsxattr.Buffer); 2267 goto end; 2268 } 2269 2270 newmaxlen -= adsxattr.Length; 2271 2272 if (newmaxlen < fileref->fcb->inode_item.st_size) { 2273 WARN("cannot rename as data too long\n"); 2274 Status = STATUS_INVALID_PARAMETER; 2275 ExFreePool(utf8.Buffer); 2276 ExFreePool(utf16.Buffer); 2277 ExFreePool(adsxattr.Buffer); 2278 goto end; 2279 } 2280 2281 Status = RtlUpcaseUnicodeString(&utf16uc, &fn, true); 2282 if (!NT_SUCCESS(Status)) { 2283 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); 2284 ExFreePool(utf8.Buffer); 2285 ExFreePool(utf16.Buffer); 2286 ExFreePool(adsxattr.Buffer); 2287 goto end; 2288 } 2289 2290 // read existing file data 2291 2292 if (fileref->fcb->inode_item.st_size > 0) { 2293 ULONG bytes_read; 2294 2295 adsdata.Length = adsdata.MaximumLength = (uint16_t)fileref->fcb->inode_item.st_size; 2296 2297 adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, adsdata.MaximumLength, ALLOC_TAG); 2298 if (!adsdata.Buffer) { 2299 ERR("out of memory\n"); 2300 Status = STATUS_INSUFFICIENT_RESOURCES; 2301 ExFreePool(utf8.Buffer); 2302 ExFreePool(utf16.Buffer); 2303 ExFreePool(utf16uc.Buffer); 2304 ExFreePool(adsxattr.Buffer); 2305 goto end; 2306 } 2307 2308 Status = read_file(fileref->fcb, (uint8_t*)adsdata.Buffer, 0, adsdata.Length, &bytes_read, Irp); 2309 if (!NT_SUCCESS(Status)) { 2310 ERR("out of memory\n"); 2311 Status = STATUS_INSUFFICIENT_RESOURCES; 2312 ExFreePool(utf8.Buffer); 2313 ExFreePool(utf16.Buffer); 2314 ExFreePool(utf16uc.Buffer); 2315 ExFreePool(adsxattr.Buffer); 2316 ExFreePool(adsdata.Buffer); 2317 goto end; 2318 } 2319 2320 if (bytes_read < fileref->fcb->inode_item.st_size) { 2321 ERR("short read\n"); 2322 Status = STATUS_INTERNAL_ERROR; 2323 ExFreePool(utf8.Buffer); 2324 ExFreePool(utf16.Buffer); 2325 ExFreePool(utf16uc.Buffer); 2326 ExFreePool(adsxattr.Buffer); 2327 ExFreePool(adsdata.Buffer); 2328 goto end; 2329 } 2330 } else 2331 adsdata.Buffer = NULL; 2332 2333 dc = ExAllocatePoolWithTag(PagedPool, sizeof(dir_child), ALLOC_TAG); 2334 if (!dc) { 2335 ERR("out of memory\n"); 2336 Status = STATUS_INSUFFICIENT_RESOURCES;; 2337 ExFreePool(utf8.Buffer); 2338 ExFreePool(utf16.Buffer); 2339 ExFreePool(utf16uc.Buffer); 2340 ExFreePool(adsxattr.Buffer); 2341 2342 if (adsdata.Buffer) 2343 ExFreePool(adsdata.Buffer); 2344 2345 goto end; 2346 } 2347 2348 // add dummy deleted fcb with old name 2349 2350 Status = duplicate_fcb(fileref->fcb, &dummyfcb); 2351 if (!NT_SUCCESS(Status)) { 2352 ERR("duplicate_fcb returned %08lx\n", Status); 2353 ExFreePool(utf8.Buffer); 2354 ExFreePool(utf16.Buffer); 2355 ExFreePool(utf16uc.Buffer); 2356 ExFreePool(adsxattr.Buffer); 2357 2358 if (adsdata.Buffer) 2359 ExFreePool(adsdata.Buffer); 2360 2361 ExFreePool(dc); 2362 2363 goto end; 2364 } 2365 2366 dummyfileref = create_fileref(Vcb); 2367 if (!dummyfileref) { 2368 ERR("out of memory\n"); 2369 Status = STATUS_INSUFFICIENT_RESOURCES; 2370 ExFreePool(utf8.Buffer); 2371 ExFreePool(utf16.Buffer); 2372 ExFreePool(utf16uc.Buffer); 2373 ExFreePool(adsxattr.Buffer); 2374 2375 if (adsdata.Buffer) 2376 ExFreePool(adsdata.Buffer); 2377 2378 ExFreePool(dc); 2379 2380 reap_fcb(dummyfcb); 2381 2382 goto end; 2383 } 2384 2385 dummyfileref->fcb = dummyfcb; 2386 2387 dummyfcb->Vcb = Vcb; 2388 dummyfcb->subvol = fileref->fcb->subvol; 2389 dummyfcb->inode = fileref->fcb->inode; 2390 dummyfcb->hash = fileref->fcb->hash; 2391 2392 if (fileref->fcb->inode_item.st_size > 0) { 2393 Status = excise_extents(Vcb, dummyfcb, 0, sector_align(fileref->fcb->inode_item.st_size, Vcb->superblock.sector_size), 2394 Irp, rollback); 2395 if (!NT_SUCCESS(Status)) { 2396 ERR("excise_extents returned %08lx\n", Status); 2397 ExFreePool(utf8.Buffer); 2398 ExFreePool(utf16.Buffer); 2399 ExFreePool(utf16uc.Buffer); 2400 ExFreePool(adsxattr.Buffer); 2401 ExFreePool(adsdata.Buffer); 2402 ExFreePool(dc); 2403 2404 reap_fileref(Vcb, dummyfileref); 2405 reap_fcb(dummyfcb); 2406 2407 goto end; 2408 } 2409 2410 dummyfcb->inode_item.st_size = 0; 2411 dummyfcb->Header.AllocationSize.QuadPart = 0; 2412 dummyfcb->Header.FileSize.QuadPart = 0; 2413 dummyfcb->Header.ValidDataLength.QuadPart = 0; 2414 } 2415 2416 dummyfcb->hash_ptrs = fileref->fcb->hash_ptrs; 2417 dummyfcb->hash_ptrs_uc = fileref->fcb->hash_ptrs_uc; 2418 dummyfcb->created = fileref->fcb->created; 2419 2420 le = fileref->fcb->extents.Flink; 2421 while (le != &fileref->fcb->extents) { 2422 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 2423 2424 ext->ignore = true; 2425 2426 le = le->Flink; 2427 } 2428 2429 while (!IsListEmpty(&fileref->fcb->dir_children_index)) { 2430 InsertTailList(&dummyfcb->dir_children_index, RemoveHeadList(&fileref->fcb->dir_children_index)); 2431 } 2432 2433 while (!IsListEmpty(&fileref->fcb->dir_children_hash)) { 2434 InsertTailList(&dummyfcb->dir_children_hash, RemoveHeadList(&fileref->fcb->dir_children_hash)); 2435 } 2436 2437 while (!IsListEmpty(&fileref->fcb->dir_children_hash_uc)) { 2438 InsertTailList(&dummyfcb->dir_children_hash_uc, RemoveHeadList(&fileref->fcb->dir_children_hash_uc)); 2439 } 2440 2441 InsertTailList(&Vcb->all_fcbs, &dummyfcb->list_entry_all); 2442 2443 InsertHeadList(fileref->fcb->list_entry.Blink, &dummyfcb->list_entry); 2444 2445 if (fileref->fcb->subvol->fcbs_ptrs[dummyfcb->hash >> 24] == &fileref->fcb->list_entry) 2446 fileref->fcb->subvol->fcbs_ptrs[dummyfcb->hash >> 24] = &dummyfcb->list_entry; 2447 2448 RemoveEntryList(&fileref->fcb->list_entry); 2449 fileref->fcb->list_entry.Flink = fileref->fcb->list_entry.Blink = NULL; 2450 2451 mark_fcb_dirty(dummyfcb); 2452 2453 // create dummy fileref 2454 2455 dummyfileref->oldutf8 = fileref->oldutf8; 2456 dummyfileref->oldindex = fileref->oldindex; 2457 dummyfileref->delete_on_close = fileref->delete_on_close; 2458 dummyfileref->posix_delete = fileref->posix_delete; 2459 dummyfileref->deleted = fileref->deleted; 2460 dummyfileref->created = fileref->created; 2461 dummyfileref->parent = fileref->parent; 2462 2463 while (!IsListEmpty(&fileref->children)) { 2464 file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&fileref->children), file_ref, list_entry); 2465 2466 free_fileref(fr->parent); 2467 2468 fr->parent = dummyfileref; 2469 InterlockedIncrement(&dummyfileref->refcount); 2470 2471 InsertTailList(&dummyfileref->children, &fr->list_entry); 2472 } 2473 2474 InsertTailList(fileref->list_entry.Blink, &dummyfileref->list_entry); 2475 2476 RemoveEntryList(&fileref->list_entry); 2477 InsertTailList(&dummyfileref->children, &fileref->list_entry); 2478 2479 dummyfileref->dc = fileref->dc; 2480 dummyfileref->dc->fileref = dummyfileref; 2481 2482 mark_fileref_dirty(dummyfileref); 2483 2484 // change fcb values 2485 2486 fileref->fcb->hash_ptrs = NULL; 2487 fileref->fcb->hash_ptrs_uc = NULL; 2488 2489 fileref->fcb->ads = true; 2490 2491 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0; 2492 fileref->oldutf8.Buffer = NULL; 2493 2494 RtlZeroMemory(dc, sizeof(dir_child)); 2495 2496 dc->utf8 = utf8; 2497 dc->name = utf16; 2498 dc->hash = calc_crc32c(0xffffffff, (uint8_t*)dc->name.Buffer, dc->name.Length); 2499 dc->name_uc = utf16uc; 2500 dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)dc->name_uc.Buffer, dc->name_uc.Length); 2501 dc->fileref = fileref; 2502 InsertTailList(&dummyfcb->dir_children_index, &dc->list_entry_index); 2503 2504 fileref->dc = dc; 2505 fileref->parent = dummyfileref; 2506 2507 crc32 = calc_crc32c(0xfffffffe, (uint8_t*)adsxattr.Buffer, adsxattr.Length); 2508 2509 fileref->fcb->adsxattr = adsxattr; 2510 fileref->fcb->adshash = crc32; 2511 fileref->fcb->adsmaxlen = newmaxlen; 2512 fileref->fcb->adsdata = adsdata; 2513 2514 fileref->fcb->created = true; 2515 2516 mark_fcb_dirty(fileref->fcb); 2517 2518 Status = STATUS_SUCCESS; 2519 2520 end: 2521 if (sf) 2522 free_fileref(sf); 2523 2524 return Status; 2525 } 2526 2527 static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, bool ex) { 2528 FILE_RENAME_INFORMATION_EX* fri = Irp->AssociatedIrp.SystemBuffer; 2529 fcb* fcb = FileObject->FsContext; 2530 ccb* ccb = FileObject->FsContext2; 2531 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL; 2532 WCHAR* fn; 2533 ULONG fnlen, utf8len, origutf8len; 2534 UNICODE_STRING fnus; 2535 ANSI_STRING utf8; 2536 NTSTATUS Status; 2537 LARGE_INTEGER time; 2538 BTRFS_TIME now; 2539 LIST_ENTRY rollback, *le; 2540 hardlink* hl; 2541 SECURITY_SUBJECT_CONTEXT subjcont; 2542 ACCESS_MASK access; 2543 ULONG flags; 2544 2545 InitializeListHead(&rollback); 2546 2547 if (ex) 2548 flags = fri->Flags; 2549 else 2550 flags = fri->ReplaceIfExists ? FILE_RENAME_REPLACE_IF_EXISTS : 0; 2551 2552 TRACE("tfo = %p\n", tfo); 2553 TRACE("Flags = %lx\n", flags); 2554 TRACE("RootDirectory = %p\n", fri->RootDirectory); 2555 TRACE("FileName = %.*S\n", (int)(fri->FileNameLength / sizeof(WCHAR)), fri->FileName); 2556 2557 fn = fri->FileName; 2558 fnlen = fri->FileNameLength / sizeof(WCHAR); 2559 2560 if (!tfo) { 2561 if (!fileref || !fileref->parent) { 2562 ERR("no fileref set and no directory given\n"); 2563 return STATUS_INVALID_PARAMETER; 2564 } 2565 } else { 2566 LONG i; 2567 2568 while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\')) { 2569 fnlen--; 2570 } 2571 2572 if (fnlen == 0) 2573 return STATUS_INVALID_PARAMETER; 2574 2575 for (i = fnlen - 1; i >= 0; i--) { 2576 if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') { 2577 fn = &fri->FileName[i+1]; 2578 fnlen -= i + 1; 2579 break; 2580 } 2581 } 2582 } 2583 2584 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 2585 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true); 2586 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 2587 2588 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->id == BTRFS_ROOT_FSTREE) { 2589 WARN("not allowing \\$Root to be renamed\n"); 2590 Status = STATUS_ACCESS_DENIED; 2591 goto end; 2592 } 2593 2594 if (fcb->ads) { 2595 if (FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) { 2596 IO_STATUS_BLOCK iosb; 2597 2598 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb); 2599 if (!NT_SUCCESS(iosb.Status)) { 2600 ERR("CcFlushCache returned %08lx\n", iosb.Status); 2601 Status = iosb.Status; 2602 goto end; 2603 } 2604 } 2605 2606 Status = rename_stream(Vcb, fileref, ccb, fri, flags, Irp, &rollback); 2607 goto end; 2608 } else if (fnlen >= 1 && fn[0] == ':') { 2609 if (FileObject->SectionObjectPointer && FileObject->SectionObjectPointer->DataSectionObject) { 2610 IO_STATUS_BLOCK iosb; 2611 2612 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb); 2613 if (!NT_SUCCESS(iosb.Status)) { 2614 ERR("CcFlushCache returned %08lx\n", iosb.Status); 2615 Status = iosb.Status; 2616 goto end; 2617 } 2618 } 2619 2620 Status = rename_file_to_stream(Vcb, fileref, ccb, fri, flags, Irp, &rollback); 2621 goto end; 2622 } 2623 2624 fnus.Buffer = fn; 2625 fnus.Length = fnus.MaximumLength = (uint16_t)(fnlen * sizeof(WCHAR)); 2626 2627 TRACE("fnus = %.*S\n", (int)(fnus.Length / sizeof(WCHAR)), fnus.Buffer); 2628 2629 Status = check_file_name_valid(&fnus, false, false); 2630 if (!NT_SUCCESS(Status)) 2631 goto end; 2632 2633 origutf8len = fileref->dc->utf8.Length; 2634 2635 Status = utf16_to_utf8(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 2636 if (!NT_SUCCESS(Status)) 2637 goto end; 2638 2639 utf8.MaximumLength = utf8.Length = (uint16_t)utf8len; 2640 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); 2641 if (!utf8.Buffer) { 2642 ERR("out of memory\n"); 2643 Status = STATUS_INSUFFICIENT_RESOURCES; 2644 goto end; 2645 } 2646 2647 Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 2648 if (!NT_SUCCESS(Status)) 2649 goto end; 2650 2651 if (tfo && tfo->FsContext2) { 2652 struct _ccb* relatedccb = tfo->FsContext2; 2653 2654 related = relatedccb->fileref; 2655 increase_fileref_refcount(related); 2656 } else if (fnus.Length >= sizeof(WCHAR) && fnus.Buffer[0] != '\\') { 2657 related = fileref->parent; 2658 increase_fileref_refcount(related); 2659 } 2660 2661 Status = open_fileref(Vcb, &oldfileref, &fnus, related, false, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 2662 2663 if (NT_SUCCESS(Status)) { 2664 TRACE("destination file already exists\n"); 2665 2666 if (fileref != oldfileref && !oldfileref->deleted) { 2667 if (!(flags & FILE_RENAME_REPLACE_IF_EXISTS)) { 2668 Status = STATUS_OBJECT_NAME_COLLISION; 2669 goto end; 2670 } else if (fileref == oldfileref) { 2671 Status = STATUS_ACCESS_DENIED; 2672 goto end; 2673 } else if (!(flags & FILE_RENAME_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) { 2674 WARN("trying to overwrite open file\n"); 2675 Status = STATUS_ACCESS_DENIED; 2676 goto end; 2677 } else if (!(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) { 2678 WARN("trying to overwrite readonly file\n"); 2679 Status = STATUS_ACCESS_DENIED; 2680 goto end; 2681 } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) { 2682 WARN("trying to overwrite directory\n"); 2683 Status = STATUS_ACCESS_DENIED; 2684 goto end; 2685 } 2686 } 2687 2688 if (fileref == oldfileref || oldfileref->deleted) { 2689 free_fileref(oldfileref); 2690 oldfileref = NULL; 2691 } 2692 } 2693 2694 if (!related) { 2695 Status = open_fileref(Vcb, &related, &fnus, NULL, true, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 2696 2697 if (!NT_SUCCESS(Status)) { 2698 ERR("open_fileref returned %08lx\n", Status); 2699 goto end; 2700 } 2701 } 2702 2703 if (related->fcb == Vcb->dummy_fcb) { 2704 Status = STATUS_ACCESS_DENIED; 2705 goto end; 2706 } 2707 2708 SeCaptureSubjectContext(&subjcont); 2709 2710 if (!SeAccessCheck(related->fcb->sd, &subjcont, false, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL, 2711 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 2712 SeReleaseSubjectContext(&subjcont); 2713 TRACE("SeAccessCheck failed, returning %08lx\n", Status); 2714 goto end; 2715 } 2716 2717 SeReleaseSubjectContext(&subjcont); 2718 2719 if (has_open_children(fileref)) { 2720 WARN("trying to rename file with open children\n"); 2721 Status = STATUS_ACCESS_DENIED; 2722 goto end; 2723 } 2724 2725 if (oldfileref) { 2726 SeCaptureSubjectContext(&subjcont); 2727 2728 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, false, DELETE, 0, NULL, 2729 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 2730 SeReleaseSubjectContext(&subjcont); 2731 TRACE("SeAccessCheck failed, returning %08lx\n", Status); 2732 goto end; 2733 } 2734 2735 SeReleaseSubjectContext(&subjcont); 2736 2737 if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) { 2738 oldfileref->delete_on_close = true; 2739 oldfileref->posix_delete = true; 2740 } 2741 2742 Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback); 2743 if (!NT_SUCCESS(Status)) { 2744 ERR("delete_fileref returned %08lx\n", Status); 2745 goto end; 2746 } 2747 } 2748 2749 if (fileref->parent->fcb->subvol != related->fcb->subvol && (fileref->fcb->subvol == fileref->parent->fcb->subvol || fileref->fcb == Vcb->dummy_fcb)) { 2750 Status = move_across_subvols(fileref, ccb, related, &utf8, &fnus, Irp, &rollback); 2751 if (!NT_SUCCESS(Status)) { 2752 ERR("move_across_subvols returned %08lx\n", Status); 2753 } 2754 goto end; 2755 } 2756 2757 if (related == fileref->parent) { // keeping file in same directory 2758 UNICODE_STRING oldfn, newfn; 2759 USHORT name_offset; 2760 ULONG reqlen, oldutf8len; 2761 2762 oldfn.Length = oldfn.MaximumLength = 0; 2763 2764 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen); 2765 if (Status != STATUS_BUFFER_OVERFLOW) { 2766 ERR("fileref_get_filename returned %08lx\n", Status); 2767 goto end; 2768 } 2769 2770 oldfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG); 2771 if (!oldfn.Buffer) { 2772 ERR("out of memory\n"); 2773 Status = STATUS_INSUFFICIENT_RESOURCES; 2774 goto end; 2775 } 2776 2777 oldfn.MaximumLength = (uint16_t)reqlen; 2778 2779 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen); 2780 if (!NT_SUCCESS(Status)) { 2781 ERR("fileref_get_filename returned %08lx\n", Status); 2782 ExFreePool(oldfn.Buffer); 2783 goto end; 2784 } 2785 2786 oldutf8len = fileref->dc->utf8.Length; 2787 2788 if (!fileref->created && !fileref->oldutf8.Buffer) { 2789 fileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG); 2790 if (!fileref->oldutf8.Buffer) { 2791 ERR("out of memory\n"); 2792 Status = STATUS_INSUFFICIENT_RESOURCES; 2793 goto end; 2794 } 2795 2796 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = fileref->dc->utf8.Length; 2797 RtlCopyMemory(fileref->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 2798 } 2799 2800 TRACE("renaming %.*S to %.*S\n", (int)(fileref->dc->name.Length / sizeof(WCHAR)), fileref->dc->name.Buffer, (int)(fnus.Length / sizeof(WCHAR)), fnus.Buffer); 2801 2802 mark_fileref_dirty(fileref); 2803 2804 if (fileref->dc) { 2805 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true); 2806 2807 ExFreePool(fileref->dc->utf8.Buffer); 2808 ExFreePool(fileref->dc->name.Buffer); 2809 ExFreePool(fileref->dc->name_uc.Buffer); 2810 2811 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG); 2812 if (!fileref->dc->utf8.Buffer) { 2813 ERR("out of memory\n"); 2814 Status = STATUS_INSUFFICIENT_RESOURCES; 2815 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 2816 ExFreePool(oldfn.Buffer); 2817 goto end; 2818 } 2819 2820 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length; 2821 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length); 2822 2823 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); 2824 if (!fileref->dc->name.Buffer) { 2825 ERR("out of memory\n"); 2826 Status = STATUS_INSUFFICIENT_RESOURCES; 2827 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 2828 ExFreePool(oldfn.Buffer); 2829 goto end; 2830 } 2831 2832 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length; 2833 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length); 2834 2835 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true); 2836 if (!NT_SUCCESS(Status)) { 2837 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); 2838 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 2839 ExFreePool(oldfn.Buffer); 2840 goto end; 2841 } 2842 2843 remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc); 2844 2845 fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name.Buffer, fileref->dc->name.Length); 2846 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length); 2847 2848 insert_dir_child_into_hash_lists(fileref->parent->fcb, fileref->dc); 2849 2850 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 2851 } 2852 2853 newfn.Length = newfn.MaximumLength = 0; 2854 2855 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen); 2856 if (Status != STATUS_BUFFER_OVERFLOW) { 2857 ERR("fileref_get_filename returned %08lx\n", Status); 2858 ExFreePool(oldfn.Buffer); 2859 goto end; 2860 } 2861 2862 newfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG); 2863 if (!newfn.Buffer) { 2864 ERR("out of memory\n"); 2865 Status = STATUS_INSUFFICIENT_RESOURCES; 2866 ExFreePool(oldfn.Buffer); 2867 goto end; 2868 } 2869 2870 newfn.MaximumLength = (uint16_t)reqlen; 2871 2872 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen); 2873 if (!NT_SUCCESS(Status)) { 2874 ERR("fileref_get_filename returned %08lx\n", Status); 2875 ExFreePool(oldfn.Buffer); 2876 ExFreePool(newfn.Buffer); 2877 goto end; 2878 } 2879 2880 KeQuerySystemTime(&time); 2881 win_time_to_unix(time, &now); 2882 2883 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) { 2884 fcb->inode_item.transid = Vcb->superblock.generation; 2885 fcb->inode_item.sequence++; 2886 2887 if (!ccb->user_set_change_time) 2888 fcb->inode_item.st_ctime = now; 2889 2890 fcb->inode_item_changed = true; 2891 mark_fcb_dirty(fcb); 2892 } 2893 2894 // update parent's INODE_ITEM 2895 2896 related->fcb->inode_item.transid = Vcb->superblock.generation; 2897 TRACE("related->fcb->inode_item.st_size (inode %I64x) was %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size); 2898 related->fcb->inode_item.st_size = related->fcb->inode_item.st_size + (2 * utf8.Length) - (2* oldutf8len); 2899 TRACE("related->fcb->inode_item.st_size (inode %I64x) now %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size); 2900 related->fcb->inode_item.sequence++; 2901 related->fcb->inode_item.st_ctime = now; 2902 related->fcb->inode_item.st_mtime = now; 2903 2904 related->fcb->inode_item_changed = true; 2905 mark_fcb_dirty(related->fcb); 2906 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 2907 2908 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&oldfn, name_offset, NULL, NULL, 2909 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME, NULL, NULL); 2910 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&newfn, name_offset, NULL, NULL, 2911 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME, NULL, NULL); 2912 2913 ExFreePool(oldfn.Buffer); 2914 ExFreePool(newfn.Buffer); 2915 2916 Status = STATUS_SUCCESS; 2917 goto end; 2918 } 2919 2920 // We move files by moving the existing fileref to the new directory, and 2921 // replacing it with a dummy fileref with the same original values, but marked as deleted. 2922 2923 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL); 2924 2925 fr2 = create_fileref(Vcb); 2926 2927 fr2->fcb = fileref->fcb; 2928 fr2->fcb->refcount++; 2929 2930 fr2->oldutf8 = fileref->oldutf8; 2931 fr2->oldindex = fileref->dc->index; 2932 fr2->delete_on_close = fileref->delete_on_close; 2933 fr2->deleted = true; 2934 fr2->created = fileref->created; 2935 fr2->parent = fileref->parent; 2936 fr2->dc = NULL; 2937 2938 if (!fr2->oldutf8.Buffer) { 2939 fr2->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG); 2940 if (!fr2->oldutf8.Buffer) { 2941 ERR("out of memory\n"); 2942 Status = STATUS_INSUFFICIENT_RESOURCES; 2943 goto end; 2944 } 2945 2946 RtlCopyMemory(fr2->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 2947 2948 fr2->oldutf8.Length = fr2->oldutf8.MaximumLength = fileref->dc->utf8.Length; 2949 } 2950 2951 if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY) 2952 fr2->fcb->fileref = fr2; 2953 2954 if (fileref->fcb->inode == SUBVOL_ROOT_INODE) 2955 fileref->fcb->subvol->parent = related->fcb->subvol->id; 2956 2957 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0; 2958 fileref->oldutf8.Buffer = NULL; 2959 fileref->deleted = false; 2960 fileref->created = true; 2961 fileref->parent = related; 2962 2963 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, true); 2964 InsertHeadList(&fileref->list_entry, &fr2->list_entry); 2965 RemoveEntryList(&fileref->list_entry); 2966 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 2967 2968 mark_fileref_dirty(fr2); 2969 mark_fileref_dirty(fileref); 2970 2971 if (fileref->dc) { 2972 // remove from old parent 2973 ExAcquireResourceExclusiveLite(&fr2->parent->fcb->nonpaged->dir_children_lock, true); 2974 RemoveEntryList(&fileref->dc->list_entry_index); 2975 remove_dir_child_from_hash_lists(fr2->parent->fcb, fileref->dc); 2976 ExReleaseResourceLite(&fr2->parent->fcb->nonpaged->dir_children_lock); 2977 2978 if (fileref->dc->utf8.Length != utf8.Length || RtlCompareMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length) != utf8.Length) { 2979 // handle changed name 2980 2981 ExFreePool(fileref->dc->utf8.Buffer); 2982 ExFreePool(fileref->dc->name.Buffer); 2983 ExFreePool(fileref->dc->name_uc.Buffer); 2984 2985 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG); 2986 if (!fileref->dc->utf8.Buffer) { 2987 ERR("out of memory\n"); 2988 Status = STATUS_INSUFFICIENT_RESOURCES; 2989 goto end; 2990 } 2991 2992 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length; 2993 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length); 2994 2995 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); 2996 if (!fileref->dc->name.Buffer) { 2997 ERR("out of memory\n"); 2998 Status = STATUS_INSUFFICIENT_RESOURCES; 2999 goto end; 3000 } 3001 3002 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length; 3003 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length); 3004 3005 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, true); 3006 if (!NT_SUCCESS(Status)) { 3007 ERR("RtlUpcaseUnicodeString returned %08lx\n", Status); 3008 goto end; 3009 } 3010 3011 fileref->dc->hash = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name.Buffer, fileref->dc->name.Length); 3012 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (uint8_t*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length); 3013 } 3014 3015 // add to new parent 3016 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true); 3017 3018 if (IsListEmpty(&related->fcb->dir_children_index)) 3019 fileref->dc->index = 2; 3020 else { 3021 dir_child* dc2 = CONTAINING_RECORD(related->fcb->dir_children_index.Blink, dir_child, list_entry_index); 3022 3023 fileref->dc->index = max(2, dc2->index + 1); 3024 } 3025 3026 InsertTailList(&related->fcb->dir_children_index, &fileref->dc->list_entry_index); 3027 insert_dir_child_into_hash_lists(related->fcb, fileref->dc); 3028 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock); 3029 } 3030 3031 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true); 3032 InsertTailList(&related->children, &fileref->list_entry); 3033 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock); 3034 3035 if (fcb->inode_item.st_nlink > 1) { 3036 // add new hardlink entry to fcb 3037 3038 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 3039 if (!hl) { 3040 ERR("out of memory\n"); 3041 Status = STATUS_INSUFFICIENT_RESOURCES; 3042 goto end; 3043 } 3044 3045 hl->parent = related->fcb->inode; 3046 hl->index = fileref->dc->index; 3047 3048 hl->name.Length = hl->name.MaximumLength = fnus.Length; 3049 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); 3050 3051 if (!hl->name.Buffer) { 3052 ERR("out of memory\n"); 3053 ExFreePool(hl); 3054 Status = STATUS_INSUFFICIENT_RESOURCES; 3055 goto end; 3056 } 3057 3058 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); 3059 3060 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length; 3061 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 3062 3063 if (!hl->utf8.Buffer) { 3064 ERR("out of memory\n"); 3065 ExFreePool(hl->name.Buffer); 3066 ExFreePool(hl); 3067 Status = STATUS_INSUFFICIENT_RESOURCES; 3068 goto end; 3069 } 3070 3071 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 3072 3073 InsertTailList(&fcb->hardlinks, &hl->list_entry); 3074 } 3075 3076 // delete old hardlink entry from fcb 3077 3078 le = fcb->hardlinks.Flink; 3079 while (le != &fcb->hardlinks) { 3080 hl = CONTAINING_RECORD(le, hardlink, list_entry); 3081 3082 if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->oldindex) { 3083 RemoveEntryList(&hl->list_entry); 3084 3085 if (hl->utf8.Buffer) 3086 ExFreePool(hl->utf8.Buffer); 3087 3088 if (hl->name.Buffer) 3089 ExFreePool(hl->name.Buffer); 3090 3091 ExFreePool(hl); 3092 break; 3093 } 3094 3095 le = le->Flink; 3096 } 3097 3098 // update inode's INODE_ITEM 3099 3100 KeQuerySystemTime(&time); 3101 win_time_to_unix(time, &now); 3102 3103 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) { 3104 fcb->inode_item.transid = Vcb->superblock.generation; 3105 fcb->inode_item.sequence++; 3106 3107 if (!ccb->user_set_change_time) 3108 fcb->inode_item.st_ctime = now; 3109 3110 fcb->inode_item_changed = true; 3111 mark_fcb_dirty(fcb); 3112 } 3113 3114 // update new parent's INODE_ITEM 3115 3116 related->fcb->inode_item.transid = Vcb->superblock.generation; 3117 TRACE("related->fcb->inode_item.st_size (inode %I64x) was %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size); 3118 related->fcb->inode_item.st_size += 2 * utf8len; 3119 TRACE("related->fcb->inode_item.st_size (inode %I64x) now %I64x\n", related->fcb->inode, related->fcb->inode_item.st_size); 3120 related->fcb->inode_item.sequence++; 3121 related->fcb->inode_item.st_ctime = now; 3122 related->fcb->inode_item.st_mtime = now; 3123 3124 related->fcb->inode_item_changed = true; 3125 mark_fcb_dirty(related->fcb); 3126 3127 // update old parent's INODE_ITEM 3128 3129 fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation; 3130 TRACE("fr2->parent->fcb->inode_item.st_size (inode %I64x) was %I64x\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size); 3131 fr2->parent->fcb->inode_item.st_size -= 2 * origutf8len; 3132 TRACE("fr2->parent->fcb->inode_item.st_size (inode %I64x) now %I64x\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size); 3133 fr2->parent->fcb->inode_item.sequence++; 3134 fr2->parent->fcb->inode_item.st_ctime = now; 3135 fr2->parent->fcb->inode_item.st_mtime = now; 3136 3137 free_fileref(fr2); 3138 3139 fr2->parent->fcb->inode_item_changed = true; 3140 mark_fcb_dirty(fr2->parent->fcb); 3141 3142 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); 3143 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 3144 send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 3145 3146 Status = STATUS_SUCCESS; 3147 3148 end: 3149 if (oldfileref) 3150 free_fileref(oldfileref); 3151 3152 if (!NT_SUCCESS(Status) && related) 3153 free_fileref(related); 3154 3155 if (!NT_SUCCESS(Status) && fr2) 3156 free_fileref(fr2); 3157 3158 if (NT_SUCCESS(Status)) 3159 clear_rollback(&rollback); 3160 else 3161 do_rollback(Vcb, &rollback); 3162 3163 ExReleaseResourceLite(fcb->Header.Resource); 3164 ExReleaseResourceLite(&Vcb->fileref_lock); 3165 ExReleaseResourceLite(&Vcb->tree_lock); 3166 3167 return Status; 3168 } 3169 3170 NTSTATUS stream_set_end_of_file_information(device_extension* Vcb, uint16_t end, fcb* fcb, file_ref* fileref, bool advance_only) { 3171 LARGE_INTEGER time; 3172 BTRFS_TIME now; 3173 3174 TRACE("setting new end to %x bytes (currently %x)\n", end, fcb->adsdata.Length); 3175 3176 if (!fileref || !fileref->parent) { 3177 ERR("no fileref for stream\n"); 3178 return STATUS_INTERNAL_ERROR; 3179 } 3180 3181 if (end < fcb->adsdata.Length) { 3182 if (advance_only) 3183 return STATUS_SUCCESS; 3184 3185 TRACE("truncating stream to %x bytes\n", end); 3186 3187 fcb->adsdata.Length = end; 3188 } else if (end > fcb->adsdata.Length) { 3189 TRACE("extending stream to %x bytes\n", end); 3190 3191 if (end > fcb->adsmaxlen) { 3192 ERR("error - xattr too long (%u > %lu)\n", end, fcb->adsmaxlen); 3193 return STATUS_DISK_FULL; 3194 } 3195 3196 if (end > fcb->adsdata.MaximumLength) { 3197 char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG); 3198 if (!data) { 3199 ERR("out of memory\n"); 3200 ExFreePool(data); 3201 return STATUS_INSUFFICIENT_RESOURCES; 3202 } 3203 3204 if (fcb->adsdata.Buffer) { 3205 RtlCopyMemory(data, fcb->adsdata.Buffer, fcb->adsdata.Length); 3206 ExFreePool(fcb->adsdata.Buffer); 3207 } 3208 3209 fcb->adsdata.Buffer = data; 3210 fcb->adsdata.MaximumLength = end; 3211 } 3212 3213 RtlZeroMemory(&fcb->adsdata.Buffer[fcb->adsdata.Length], end - fcb->adsdata.Length); 3214 3215 fcb->adsdata.Length = end; 3216 } 3217 3218 mark_fcb_dirty(fcb); 3219 3220 fcb->Header.AllocationSize.QuadPart = end; 3221 fcb->Header.FileSize.QuadPart = end; 3222 fcb->Header.ValidDataLength.QuadPart = end; 3223 3224 KeQuerySystemTime(&time); 3225 win_time_to_unix(time, &now); 3226 3227 fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation; 3228 fileref->parent->fcb->inode_item.sequence++; 3229 fileref->parent->fcb->inode_item.st_ctime = now; 3230 3231 fileref->parent->fcb->inode_item_changed = true; 3232 mark_fcb_dirty(fileref->parent->fcb); 3233 3234 fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 3235 fileref->parent->fcb->subvol->root_item.ctime = now; 3236 3237 return STATUS_SUCCESS; 3238 } 3239 3240 static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, bool advance_only, bool prealloc) { 3241 FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer; 3242 fcb* fcb = FileObject->FsContext; 3243 ccb* ccb = FileObject->FsContext2; 3244 file_ref* fileref = ccb ? ccb->fileref : NULL; 3245 NTSTATUS Status; 3246 LARGE_INTEGER time; 3247 CC_FILE_SIZES ccfs; 3248 LIST_ENTRY rollback; 3249 bool set_size = false; 3250 ULONG filter; 3251 uint64_t new_end_of_file; 3252 3253 if (!fileref) { 3254 ERR("fileref is NULL\n"); 3255 return STATUS_INVALID_PARAMETER; 3256 } 3257 3258 InitializeListHead(&rollback); 3259 3260 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 3261 3262 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 3263 3264 if (fileref ? fileref->deleted : fcb->deleted) { 3265 Status = STATUS_FILE_CLOSED; 3266 goto end; 3267 } 3268 3269 if (fcb->ads) { 3270 if (feofi->EndOfFile.QuadPart > 0xffff) { 3271 Status = STATUS_DISK_FULL; 3272 goto end; 3273 } 3274 3275 if (feofi->EndOfFile.QuadPart < 0) { 3276 Status = STATUS_INVALID_PARAMETER; 3277 goto end; 3278 } 3279 3280 Status = stream_set_end_of_file_information(Vcb, (uint16_t)feofi->EndOfFile.QuadPart, fcb, fileref, advance_only); 3281 3282 if (NT_SUCCESS(Status)) { 3283 ccfs.AllocationSize = fcb->Header.AllocationSize; 3284 ccfs.FileSize = fcb->Header.FileSize; 3285 ccfs.ValidDataLength = fcb->Header.ValidDataLength; 3286 set_size = true; 3287 } 3288 3289 filter = FILE_NOTIFY_CHANGE_STREAM_SIZE; 3290 3291 if (!ccb->user_set_write_time) { 3292 KeQuerySystemTime(&time); 3293 win_time_to_unix(time, &fileref->parent->fcb->inode_item.st_mtime); 3294 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 3295 3296 fileref->parent->fcb->inode_item_changed = true; 3297 mark_fcb_dirty(fileref->parent->fcb); 3298 } 3299 3300 queue_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name); 3301 3302 goto end; 3303 } 3304 3305 TRACE("file: %p\n", FileObject); 3306 TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "true" : "false"); 3307 TRACE("FileObject: AllocationSize = %I64x, FileSize = %I64x, ValidDataLength = %I64x\n", 3308 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart); 3309 3310 new_end_of_file = feofi->EndOfFile.QuadPart; 3311 3312 /* The lazy writer sometimes tries to round files to the next page size through CcSetValidData - 3313 * ignore these. See fastfat!FatSetEndOfFileInfo, where Microsoft does the same as we're 3314 * doing below. */ 3315 if (advance_only && new_end_of_file >= (uint64_t)fcb->Header.FileSize.QuadPart) 3316 new_end_of_file = fcb->Header.FileSize.QuadPart; 3317 3318 TRACE("setting new end to %I64x bytes (currently %I64x)\n", new_end_of_file, fcb->inode_item.st_size); 3319 3320 if (new_end_of_file < fcb->inode_item.st_size) { 3321 if (advance_only) { 3322 Status = STATUS_SUCCESS; 3323 goto end; 3324 } 3325 3326 TRACE("truncating file to %I64x bytes\n", new_end_of_file); 3327 3328 if (!MmCanFileBeTruncated(&fcb->nonpaged->segment_object, &feofi->EndOfFile)) { 3329 Status = STATUS_USER_MAPPED_FILE; 3330 goto end; 3331 } 3332 3333 Status = truncate_file(fcb, new_end_of_file, Irp, &rollback); 3334 if (!NT_SUCCESS(Status)) { 3335 ERR("error - truncate_file failed\n"); 3336 goto end; 3337 } 3338 } else if (new_end_of_file > fcb->inode_item.st_size) { 3339 TRACE("extending file to %I64x bytes\n", new_end_of_file); 3340 3341 Status = extend_file(fcb, fileref, new_end_of_file, prealloc, NULL, &rollback); 3342 if (!NT_SUCCESS(Status)) { 3343 ERR("error - extend_file failed\n"); 3344 goto end; 3345 } 3346 } else if (new_end_of_file == fcb->inode_item.st_size && advance_only) { 3347 Status = STATUS_SUCCESS; 3348 goto end; 3349 } 3350 3351 ccfs.AllocationSize = fcb->Header.AllocationSize; 3352 ccfs.FileSize = fcb->Header.FileSize; 3353 ccfs.ValidDataLength = fcb->Header.ValidDataLength; 3354 set_size = true; 3355 3356 filter = FILE_NOTIFY_CHANGE_SIZE; 3357 3358 if (!ccb->user_set_write_time) { 3359 KeQuerySystemTime(&time); 3360 win_time_to_unix(time, &fcb->inode_item.st_mtime); 3361 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 3362 } 3363 3364 fcb->inode_item_changed = true; 3365 mark_fcb_dirty(fcb); 3366 queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); 3367 3368 Status = STATUS_SUCCESS; 3369 3370 end: 3371 if (NT_SUCCESS(Status)) 3372 clear_rollback(&rollback); 3373 else 3374 do_rollback(Vcb, &rollback); 3375 3376 ExReleaseResourceLite(fcb->Header.Resource); 3377 3378 if (set_size) { 3379 _SEH2_TRY { 3380 CcSetFileSizes(FileObject, &ccfs); 3381 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 3382 Status = _SEH2_GetExceptionCode(); 3383 } _SEH2_END; 3384 3385 if (!NT_SUCCESS(Status)) 3386 ERR("CcSetFileSizes threw exception %08lx\n", Status); 3387 } 3388 3389 ExReleaseResourceLite(&Vcb->tree_lock); 3390 3391 return Status; 3392 } 3393 3394 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) { 3395 FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; 3396 3397 TRACE("setting the position on %p to %I64x\n", FileObject, fpi->CurrentByteOffset.QuadPart); 3398 3399 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING 3400 3401 FileObject->CurrentByteOffset = fpi->CurrentByteOffset; 3402 3403 return STATUS_SUCCESS; 3404 } 3405 3406 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo, bool ex) { 3407 FILE_LINK_INFORMATION_EX* fli = Irp->AssociatedIrp.SystemBuffer; 3408 fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb; 3409 ccb* ccb = FileObject->FsContext2; 3410 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL; 3411 WCHAR* fn; 3412 ULONG fnlen, utf8len; 3413 UNICODE_STRING fnus; 3414 ANSI_STRING utf8; 3415 NTSTATUS Status; 3416 LARGE_INTEGER time; 3417 BTRFS_TIME now; 3418 LIST_ENTRY rollback; 3419 hardlink* hl; 3420 ACCESS_MASK access; 3421 SECURITY_SUBJECT_CONTEXT subjcont; 3422 dir_child* dc = NULL; 3423 ULONG flags; 3424 3425 InitializeListHead(&rollback); 3426 3427 // FIXME - check fli length 3428 // FIXME - don't ignore fli->RootDirectory 3429 3430 if (ex) 3431 flags = fli->Flags; 3432 else 3433 flags = fli->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0; 3434 3435 TRACE("flags = %lx\n", flags); 3436 TRACE("RootDirectory = %p\n", fli->RootDirectory); 3437 TRACE("FileNameLength = %lx\n", fli->FileNameLength); 3438 TRACE("FileName = %.*S\n", (int)(fli->FileNameLength / sizeof(WCHAR)), fli->FileName); 3439 3440 fn = fli->FileName; 3441 fnlen = fli->FileNameLength / sizeof(WCHAR); 3442 3443 if (!tfo) { 3444 if (!fileref || !fileref->parent) { 3445 ERR("no fileref set and no directory given\n"); 3446 return STATUS_INVALID_PARAMETER; 3447 } 3448 3449 parfcb = fileref->parent->fcb; 3450 tfofcb = NULL; 3451 } else { 3452 LONG i; 3453 3454 tfofcb = tfo->FsContext; 3455 parfcb = tfofcb; 3456 3457 while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\')) { 3458 fnlen--; 3459 } 3460 3461 if (fnlen == 0) 3462 return STATUS_INVALID_PARAMETER; 3463 3464 for (i = fnlen - 1; i >= 0; i--) { 3465 if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') { 3466 fn = &fli->FileName[i+1]; 3467 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1; 3468 break; 3469 } 3470 } 3471 } 3472 3473 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 3474 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true); 3475 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 3476 3477 if (fcb->type == BTRFS_TYPE_DIRECTORY) { 3478 WARN("tried to create hard link on directory\n"); 3479 Status = STATUS_FILE_IS_A_DIRECTORY; 3480 goto end; 3481 } 3482 3483 if (fcb->ads) { 3484 WARN("tried to create hard link on stream\n"); 3485 Status = STATUS_INVALID_PARAMETER; 3486 goto end; 3487 } 3488 3489 if (fcb->inode_item.st_nlink >= 65535) { 3490 Status = STATUS_TOO_MANY_LINKS; 3491 goto end; 3492 } 3493 3494 fnus.Buffer = fn; 3495 fnus.Length = fnus.MaximumLength = (uint16_t)(fnlen * sizeof(WCHAR)); 3496 3497 TRACE("fnus = %.*S\n", (int)(fnus.Length / sizeof(WCHAR)), fnus.Buffer); 3498 3499 Status = check_file_name_valid(&fnus, false, false); 3500 if (!NT_SUCCESS(Status)) 3501 goto end; 3502 3503 Status = utf16_to_utf8(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 3504 if (!NT_SUCCESS(Status)) 3505 goto end; 3506 3507 utf8.MaximumLength = utf8.Length = (uint16_t)utf8len; 3508 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); 3509 if (!utf8.Buffer) { 3510 ERR("out of memory\n"); 3511 Status = STATUS_INSUFFICIENT_RESOURCES; 3512 goto end; 3513 } 3514 3515 Status = utf16_to_utf8(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 3516 if (!NT_SUCCESS(Status)) 3517 goto end; 3518 3519 if (tfo && tfo->FsContext2) { 3520 struct _ccb* relatedccb = tfo->FsContext2; 3521 3522 related = relatedccb->fileref; 3523 increase_fileref_refcount(related); 3524 } 3525 3526 Status = open_fileref(Vcb, &oldfileref, &fnus, related, false, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 3527 3528 if (NT_SUCCESS(Status)) { 3529 if (!oldfileref->deleted) { 3530 WARN("destination file already exists\n"); 3531 3532 if (!(flags & FILE_LINK_REPLACE_IF_EXISTS)) { 3533 Status = STATUS_OBJECT_NAME_COLLISION; 3534 goto end; 3535 } else if (fileref == oldfileref) { 3536 Status = STATUS_ACCESS_DENIED; 3537 goto end; 3538 } else if (!(flags & FILE_LINK_POSIX_SEMANTICS) && (oldfileref->open_count > 0 || has_open_children(oldfileref)) && !oldfileref->deleted) { 3539 WARN("trying to overwrite open file\n"); 3540 Status = STATUS_ACCESS_DENIED; 3541 goto end; 3542 } else if (!(flags & FILE_LINK_IGNORE_READONLY_ATTRIBUTE) && oldfileref->fcb->atts & FILE_ATTRIBUTE_READONLY) { 3543 WARN("trying to overwrite readonly file\n"); 3544 Status = STATUS_ACCESS_DENIED; 3545 goto end; 3546 } else if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) { 3547 WARN("trying to overwrite directory\n"); 3548 Status = STATUS_ACCESS_DENIED; 3549 goto end; 3550 } 3551 } else { 3552 free_fileref(oldfileref); 3553 oldfileref = NULL; 3554 } 3555 } 3556 3557 if (!related) { 3558 Status = open_fileref(Vcb, &related, &fnus, NULL, true, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 3559 3560 if (!NT_SUCCESS(Status)) { 3561 ERR("open_fileref returned %08lx\n", Status); 3562 goto end; 3563 } 3564 } 3565 3566 SeCaptureSubjectContext(&subjcont); 3567 3568 if (!SeAccessCheck(related->fcb->sd, &subjcont, false, FILE_ADD_FILE, 0, NULL, 3569 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 3570 SeReleaseSubjectContext(&subjcont); 3571 TRACE("SeAccessCheck failed, returning %08lx\n", Status); 3572 goto end; 3573 } 3574 3575 SeReleaseSubjectContext(&subjcont); 3576 3577 if (fcb->subvol != parfcb->subvol) { 3578 WARN("can't create hard link over subvolume boundary\n"); 3579 Status = STATUS_INVALID_PARAMETER; 3580 goto end; 3581 } 3582 3583 if (oldfileref) { 3584 SeCaptureSubjectContext(&subjcont); 3585 3586 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, false, DELETE, 0, NULL, 3587 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 3588 SeReleaseSubjectContext(&subjcont); 3589 TRACE("SeAccessCheck failed, returning %08lx\n", Status); 3590 goto end; 3591 } 3592 3593 SeReleaseSubjectContext(&subjcont); 3594 3595 if (oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS) { 3596 oldfileref->delete_on_close = true; 3597 oldfileref->posix_delete = true; 3598 } 3599 3600 Status = delete_fileref(oldfileref, NULL, oldfileref->open_count > 0 && flags & FILE_RENAME_POSIX_SEMANTICS, Irp, &rollback); 3601 if (!NT_SUCCESS(Status)) { 3602 ERR("delete_fileref returned %08lx\n", Status); 3603 goto end; 3604 } 3605 } 3606 3607 fr2 = create_fileref(Vcb); 3608 3609 fr2->fcb = fcb; 3610 fcb->refcount++; 3611 3612 fr2->created = true; 3613 fr2->parent = related; 3614 3615 Status = add_dir_child(related->fcb, fcb->inode, false, &utf8, &fnus, fcb->type, &dc); 3616 if (!NT_SUCCESS(Status)) 3617 WARN("add_dir_child returned %08lx\n", Status); 3618 3619 fr2->dc = dc; 3620 dc->fileref = fr2; 3621 3622 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, true); 3623 InsertTailList(&related->children, &fr2->list_entry); 3624 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock); 3625 3626 // add hardlink for existing fileref, if it's not there already 3627 if (IsListEmpty(&fcb->hardlinks)) { 3628 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 3629 if (!hl) { 3630 ERR("out of memory\n"); 3631 Status = STATUS_INSUFFICIENT_RESOURCES; 3632 goto end; 3633 } 3634 3635 hl->parent = fileref->parent->fcb->inode; 3636 hl->index = fileref->dc->index; 3637 3638 hl->name.Length = hl->name.MaximumLength = fnus.Length; 3639 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); 3640 3641 if (!hl->name.Buffer) { 3642 ERR("out of memory\n"); 3643 ExFreePool(hl); 3644 Status = STATUS_INSUFFICIENT_RESOURCES; 3645 goto end; 3646 } 3647 3648 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); 3649 3650 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length; 3651 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 3652 3653 if (!hl->utf8.Buffer) { 3654 ERR("out of memory\n"); 3655 ExFreePool(hl->name.Buffer); 3656 ExFreePool(hl); 3657 Status = STATUS_INSUFFICIENT_RESOURCES; 3658 goto end; 3659 } 3660 3661 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 3662 3663 InsertTailList(&fcb->hardlinks, &hl->list_entry); 3664 } 3665 3666 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 3667 if (!hl) { 3668 ERR("out of memory\n"); 3669 Status = STATUS_INSUFFICIENT_RESOURCES; 3670 goto end; 3671 } 3672 3673 hl->parent = related->fcb->inode; 3674 hl->index = dc->index; 3675 3676 hl->name.Length = hl->name.MaximumLength = fnus.Length; 3677 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); 3678 3679 if (!hl->name.Buffer) { 3680 ERR("out of memory\n"); 3681 ExFreePool(hl); 3682 Status = STATUS_INSUFFICIENT_RESOURCES; 3683 goto end; 3684 } 3685 3686 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); 3687 3688 hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length; 3689 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 3690 3691 if (!hl->utf8.Buffer) { 3692 ERR("out of memory\n"); 3693 ExFreePool(hl->name.Buffer); 3694 ExFreePool(hl); 3695 Status = STATUS_INSUFFICIENT_RESOURCES; 3696 goto end; 3697 } 3698 3699 RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length); 3700 ExFreePool(utf8.Buffer); 3701 3702 InsertTailList(&fcb->hardlinks, &hl->list_entry); 3703 3704 mark_fileref_dirty(fr2); 3705 free_fileref(fr2); 3706 3707 // update inode's INODE_ITEM 3708 3709 KeQuerySystemTime(&time); 3710 win_time_to_unix(time, &now); 3711 3712 fcb->inode_item.transid = Vcb->superblock.generation; 3713 fcb->inode_item.sequence++; 3714 fcb->inode_item.st_nlink++; 3715 3716 if (!ccb->user_set_change_time) 3717 fcb->inode_item.st_ctime = now; 3718 3719 fcb->inode_item_changed = true; 3720 mark_fcb_dirty(fcb); 3721 3722 // update parent's INODE_ITEM 3723 3724 parfcb->inode_item.transid = Vcb->superblock.generation; 3725 TRACE("parfcb->inode_item.st_size (inode %I64x) was %I64x\n", parfcb->inode, parfcb->inode_item.st_size); 3726 parfcb->inode_item.st_size += 2 * utf8len; 3727 TRACE("parfcb->inode_item.st_size (inode %I64x) now %I64x\n", parfcb->inode, parfcb->inode_item.st_size); 3728 parfcb->inode_item.sequence++; 3729 parfcb->inode_item.st_ctime = now; 3730 3731 parfcb->inode_item_changed = true; 3732 mark_fcb_dirty(parfcb); 3733 3734 send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); 3735 3736 Status = STATUS_SUCCESS; 3737 3738 end: 3739 if (oldfileref) 3740 free_fileref(oldfileref); 3741 3742 if (!NT_SUCCESS(Status) && related) 3743 free_fileref(related); 3744 3745 if (!NT_SUCCESS(Status) && fr2) 3746 free_fileref(fr2); 3747 3748 if (NT_SUCCESS(Status)) 3749 clear_rollback(&rollback); 3750 else 3751 do_rollback(Vcb, &rollback); 3752 3753 ExReleaseResourceLite(fcb->Header.Resource); 3754 ExReleaseResourceLite(&Vcb->fileref_lock); 3755 ExReleaseResourceLite(&Vcb->tree_lock); 3756 3757 return Status; 3758 } 3759 3760 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { 3761 FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer; 3762 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 3763 fcb* fcb = FileObject->FsContext; 3764 ccb* ccb = FileObject->FsContext2; 3765 file_ref* fileref = ccb ? ccb->fileref : NULL; 3766 NTSTATUS Status; 3767 LARGE_INTEGER time; 3768 CC_FILE_SIZES ccfs; 3769 LIST_ENTRY rollback; 3770 bool set_size = false; 3771 ULONG filter; 3772 3773 if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) { 3774 ERR("input buffer length was %lu, expected %Iu\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)); 3775 return STATUS_INVALID_PARAMETER; 3776 } 3777 3778 if (!fileref) { 3779 ERR("fileref is NULL\n"); 3780 return STATUS_INVALID_PARAMETER; 3781 } 3782 3783 InitializeListHead(&rollback); 3784 3785 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 3786 3787 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 3788 3789 if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) { 3790 Status = STATUS_INVALID_PARAMETER; 3791 goto end; 3792 } 3793 3794 if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) { 3795 TRACE("invalid VDL of %I64u (current VDL = %I64u, file size = %I64u)\n", fvdli->ValidDataLength.QuadPart, 3796 fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart); 3797 Status = STATUS_INVALID_PARAMETER; 3798 goto end; 3799 } 3800 3801 if (fileref ? fileref->deleted : fcb->deleted) { 3802 Status = STATUS_FILE_CLOSED; 3803 goto end; 3804 } 3805 3806 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength, 3807 // and we set it to the max anyway. 3808 3809 ccfs.AllocationSize = fcb->Header.AllocationSize; 3810 ccfs.FileSize = fcb->Header.FileSize; 3811 ccfs.ValidDataLength = fvdli->ValidDataLength; 3812 set_size = true; 3813 3814 filter = FILE_NOTIFY_CHANGE_SIZE; 3815 3816 if (!ccb->user_set_write_time) { 3817 KeQuerySystemTime(&time); 3818 win_time_to_unix(time, &fcb->inode_item.st_mtime); 3819 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 3820 } 3821 3822 fcb->inode_item_changed = true; 3823 mark_fcb_dirty(fcb); 3824 3825 queue_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); 3826 3827 Status = STATUS_SUCCESS; 3828 3829 end: 3830 if (NT_SUCCESS(Status)) 3831 clear_rollback(&rollback); 3832 else 3833 do_rollback(Vcb, &rollback); 3834 3835 ExReleaseResourceLite(fcb->Header.Resource); 3836 3837 if (set_size) { 3838 _SEH2_TRY { 3839 CcSetFileSizes(FileObject, &ccfs); 3840 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 3841 Status = _SEH2_GetExceptionCode(); 3842 } _SEH2_END; 3843 3844 if (!NT_SUCCESS(Status)) 3845 ERR("CcSetFileSizes threw exception %08lx\n", Status); 3846 else 3847 fcb->Header.AllocationSize = ccfs.AllocationSize; 3848 } 3849 3850 ExReleaseResourceLite(&Vcb->tree_lock); 3851 3852 return Status; 3853 } 3854 3855 #ifndef __REACTOS__ 3856 static NTSTATUS set_case_sensitive_information(PIRP Irp) { 3857 FILE_CASE_SENSITIVE_INFORMATION* fcsi = (FILE_CASE_SENSITIVE_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; 3858 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 3859 3860 if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(FILE_CASE_SENSITIVE_INFORMATION)) 3861 return STATUS_INFO_LENGTH_MISMATCH; 3862 3863 PFILE_OBJECT FileObject = IrpSp->FileObject; 3864 3865 if (!FileObject) 3866 return STATUS_INVALID_PARAMETER; 3867 3868 fcb* fcb = FileObject->FsContext; 3869 3870 if (!fcb) 3871 return STATUS_INVALID_PARAMETER; 3872 3873 if (!(fcb->atts & FILE_ATTRIBUTE_DIRECTORY)) { 3874 WARN("cannot set case-sensitive flag on anything other than directory\n"); 3875 return STATUS_INVALID_PARAMETER; 3876 } 3877 3878 ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, true); 3879 3880 fcb->case_sensitive = fcsi->Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR; 3881 mark_fcb_dirty(fcb); 3882 3883 ExReleaseResourceLite(&fcb->Vcb->tree_lock); 3884 3885 return STATUS_SUCCESS; 3886 } 3887 #endif 3888 3889 _Dispatch_type_(IRP_MJ_SET_INFORMATION) 3890 _Function_class_(DRIVER_DISPATCH) 3891 NTSTATUS __stdcall drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 3892 NTSTATUS Status; 3893 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 3894 device_extension* Vcb = DeviceObject->DeviceExtension; 3895 fcb* fcb = IrpSp->FileObject->FsContext; 3896 ccb* ccb = IrpSp->FileObject->FsContext2; 3897 bool top_level; 3898 3899 FsRtlEnterFileSystem(); 3900 3901 top_level = is_top_level(Irp); 3902 3903 Irp->IoStatus.Information = 0; 3904 3905 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 3906 Status = STATUS_INVALID_DEVICE_REQUEST; 3907 goto end; 3908 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 3909 Status = STATUS_INVALID_PARAMETER; 3910 goto end; 3911 } 3912 3913 if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) { 3914 Status = STATUS_ACCESS_DENIED; 3915 goto end; 3916 } 3917 3918 if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) { 3919 Status = STATUS_MEDIA_WRITE_PROTECTED; 3920 goto end; 3921 } 3922 3923 if (!fcb) { 3924 ERR("no fcb\n"); 3925 Status = STATUS_INVALID_PARAMETER; 3926 goto end; 3927 } 3928 3929 if (!ccb) { 3930 ERR("no ccb\n"); 3931 Status = STATUS_INVALID_PARAMETER; 3932 goto end; 3933 } 3934 3935 if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation && 3936 #ifndef __REACTOS__ 3937 (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformationEx))) { 3938 #else 3939 (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) { 3940 #endif 3941 Status = STATUS_ACCESS_DENIED; 3942 goto end; 3943 } 3944 3945 Status = STATUS_NOT_IMPLEMENTED; 3946 3947 TRACE("set information\n"); 3948 3949 FsRtlCheckOplock(fcb_oplock(fcb), Irp, NULL, NULL, NULL); 3950 3951 switch (IrpSp->Parameters.SetFile.FileInformationClass) { 3952 case FileAllocationInformation: 3953 { 3954 TRACE("FileAllocationInformation\n"); 3955 3956 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) { 3957 WARN("insufficient privileges\n"); 3958 Status = STATUS_ACCESS_DENIED; 3959 break; 3960 } 3961 3962 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, false, true); 3963 break; 3964 } 3965 3966 case FileBasicInformation: 3967 { 3968 TRACE("FileBasicInformation\n"); 3969 3970 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { 3971 WARN("insufficient privileges\n"); 3972 Status = STATUS_ACCESS_DENIED; 3973 break; 3974 } 3975 3976 Status = set_basic_information(Vcb, Irp, IrpSp->FileObject); 3977 3978 break; 3979 } 3980 3981 case FileDispositionInformation: 3982 { 3983 TRACE("FileDispositionInformation\n"); 3984 3985 if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) { 3986 WARN("insufficient privileges\n"); 3987 Status = STATUS_ACCESS_DENIED; 3988 break; 3989 } 3990 3991 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, false); 3992 3993 break; 3994 } 3995 3996 case FileEndOfFileInformation: 3997 { 3998 TRACE("FileEndOfFileInformation\n"); 3999 4000 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { 4001 WARN("insufficient privileges\n"); 4002 Status = STATUS_ACCESS_DENIED; 4003 break; 4004 } 4005 4006 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, false); 4007 4008 break; 4009 } 4010 4011 case FileLinkInformation: 4012 TRACE("FileLinkInformation\n"); 4013 Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false); 4014 break; 4015 4016 case FilePositionInformation: 4017 TRACE("FilePositionInformation\n"); 4018 Status = set_position_information(IrpSp->FileObject, Irp); 4019 break; 4020 4021 case FileRenameInformation: 4022 TRACE("FileRenameInformation\n"); 4023 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, false); 4024 break; 4025 4026 case FileValidDataLengthInformation: 4027 { 4028 TRACE("FileValidDataLengthInformation\n"); 4029 4030 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { 4031 WARN("insufficient privileges\n"); 4032 Status = STATUS_ACCESS_DENIED; 4033 break; 4034 } 4035 4036 Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject); 4037 4038 break; 4039 } 4040 4041 #ifndef __REACTOS__ 4042 #ifndef _MSC_VER 4043 #pragma GCC diagnostic push 4044 #pragma GCC diagnostic ignored "-Wswitch" 4045 #endif 4046 case FileDispositionInformationEx: 4047 { 4048 TRACE("FileDispositionInformationEx\n"); 4049 4050 if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) { 4051 WARN("insufficient privileges\n"); 4052 Status = STATUS_ACCESS_DENIED; 4053 break; 4054 } 4055 4056 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject, true); 4057 4058 break; 4059 } 4060 4061 case FileRenameInformationEx: 4062 TRACE("FileRenameInformationEx\n"); 4063 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, true); 4064 break; 4065 4066 case FileLinkInformationEx: 4067 TRACE("FileLinkInformationEx\n"); 4068 Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject, true); 4069 break; 4070 4071 case FileCaseSensitiveInformation: 4072 TRACE("FileCaseSensitiveInformation\n"); 4073 4074 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { 4075 WARN("insufficient privileges\n"); 4076 Status = STATUS_ACCESS_DENIED; 4077 break; 4078 } 4079 4080 Status = set_case_sensitive_information(Irp); 4081 break; 4082 4083 case FileStorageReserveIdInformation: 4084 WARN("unimplemented FileInformationClass FileStorageReserveIdInformation\n"); 4085 break; 4086 4087 #ifndef _MSC_VER 4088 #pragma GCC diagnostic pop 4089 #endif 4090 #endif 4091 4092 default: 4093 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass); 4094 } 4095 4096 end: 4097 Irp->IoStatus.Status = Status; 4098 4099 TRACE("returning %08lx\n", Status); 4100 4101 IoCompleteRequest(Irp, IO_NO_INCREMENT); 4102 4103 if (top_level) 4104 IoSetTopLevelIrp(NULL); 4105 4106 FsRtlExitFileSystem(); 4107 4108 return Status; 4109 } 4110 4111 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) { 4112 RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION)); 4113 4114 *length -= sizeof(FILE_BASIC_INFORMATION); 4115 4116 if (fcb == fcb->Vcb->dummy_fcb) { 4117 LARGE_INTEGER time; 4118 4119 KeQuerySystemTime(&time); 4120 fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time; 4121 } else { 4122 fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 4123 fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 4124 fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 4125 fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 4126 } 4127 4128 if (fcb->ads) { 4129 if (!fileref || !fileref->parent) { 4130 ERR("no fileref for stream\n"); 4131 return STATUS_INTERNAL_ERROR; 4132 } else 4133 fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts; 4134 } else 4135 fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 4136 4137 return STATUS_SUCCESS; 4138 } 4139 4140 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) { 4141 INODE_ITEM* ii; 4142 4143 if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) { 4144 WARN("overflow\n"); 4145 return STATUS_BUFFER_OVERFLOW; 4146 } 4147 4148 RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION)); 4149 4150 *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION); 4151 4152 if (fcb->ads) { 4153 if (!fileref || !fileref->parent) { 4154 ERR("no fileref for stream\n"); 4155 return STATUS_INTERNAL_ERROR; 4156 } 4157 4158 ii = &fileref->parent->fcb->inode_item; 4159 } else 4160 ii = &fcb->inode_item; 4161 4162 if (fcb == fcb->Vcb->dummy_fcb) { 4163 LARGE_INTEGER time; 4164 4165 KeQuerySystemTime(&time); 4166 fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time; 4167 } else { 4168 fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 4169 fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 4170 fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 4171 fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 4172 } 4173 4174 if (fcb->ads) { 4175 fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length; 4176 fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts; 4177 } else { 4178 fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 4179 fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 4180 fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 4181 } 4182 4183 return STATUS_SUCCESS; 4184 } 4185 4186 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) { 4187 RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION)); 4188 4189 *length -= sizeof(FILE_STANDARD_INFORMATION); 4190 4191 if (fcb->ads) { 4192 if (!fileref || !fileref->parent) { 4193 ERR("no fileref for stream\n"); 4194 return STATUS_INTERNAL_ERROR; 4195 } 4196 4197 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length; 4198 fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink; 4199 fsi->Directory = false; 4200 } else { 4201 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 4202 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 4203 fsi->NumberOfLinks = fcb->inode_item.st_nlink; 4204 fsi->Directory = S_ISDIR(fcb->inode_item.st_mode); 4205 } 4206 4207 TRACE("length = %I64u\n", fsi->EndOfFile.QuadPart); 4208 4209 fsi->DeletePending = fileref ? fileref->delete_on_close : false; 4210 4211 return STATUS_SUCCESS; 4212 } 4213 4214 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) { 4215 *length -= sizeof(FILE_INTERNAL_INFORMATION); 4216 4217 fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode); 4218 4219 return STATUS_SUCCESS; 4220 } 4221 4222 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) { 4223 *length -= sizeof(FILE_EA_INFORMATION); 4224 4225 /* This value appears to be the size of the structure NTFS stores on disk, and not, 4226 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store). 4227 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each 4228 * item. */ 4229 4230 eai->EaSize = fcb->ealen; 4231 4232 return STATUS_SUCCESS; 4233 } 4234 4235 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) { 4236 RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION)); 4237 4238 *length -= sizeof(FILE_POSITION_INFORMATION); 4239 4240 fpi->CurrentByteOffset = FileObject->CurrentByteOffset; 4241 4242 return STATUS_SUCCESS; 4243 } 4244 4245 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) { 4246 file_ref* fr; 4247 NTSTATUS Status; 4248 ULONG reqlen = 0; 4249 USHORT offset; 4250 bool overflow = false; 4251 4252 // FIXME - we need a lock on filerefs' filepart 4253 4254 if (fileref == fileref->fcb->Vcb->root_fileref) { 4255 if (fn->MaximumLength >= sizeof(WCHAR)) { 4256 fn->Buffer[0] = '\\'; 4257 fn->Length = sizeof(WCHAR); 4258 4259 if (name_offset) 4260 *name_offset = 0; 4261 4262 return STATUS_SUCCESS; 4263 } else { 4264 if (preqlen) 4265 *preqlen = sizeof(WCHAR); 4266 fn->Length = 0; 4267 4268 return STATUS_BUFFER_OVERFLOW; 4269 } 4270 } 4271 4272 fr = fileref; 4273 offset = 0; 4274 4275 while (fr->parent) { 4276 USHORT movelen; 4277 4278 if (!fr->dc) 4279 return STATUS_INTERNAL_ERROR; 4280 4281 if (!overflow) { 4282 if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength) 4283 overflow = true; 4284 } 4285 4286 if (overflow) 4287 movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR); 4288 else 4289 movelen = fn->Length; 4290 4291 if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) { 4292 RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen); 4293 offset += fr->dc->name.Length + sizeof(WCHAR); 4294 } 4295 4296 if (fn->MaximumLength >= sizeof(WCHAR)) { 4297 fn->Buffer[0] = fr->fcb->ads ? ':' : '\\'; 4298 fn->Length += sizeof(WCHAR); 4299 4300 if (fn->MaximumLength > sizeof(WCHAR)) { 4301 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR))); 4302 fn->Length += fr->dc->name.Length; 4303 } 4304 4305 if (fn->Length > fn->MaximumLength) { 4306 fn->Length = fn->MaximumLength; 4307 overflow = true; 4308 } 4309 } 4310 4311 reqlen += sizeof(WCHAR) + fr->dc->name.Length; 4312 4313 fr = fr->parent; 4314 } 4315 4316 offset += sizeof(WCHAR); 4317 4318 if (overflow) { 4319 if (preqlen) 4320 *preqlen = reqlen; 4321 Status = STATUS_BUFFER_OVERFLOW; 4322 } else { 4323 if (name_offset) 4324 *name_offset = offset; 4325 4326 Status = STATUS_SUCCESS; 4327 } 4328 4329 return Status; 4330 } 4331 4332 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) { 4333 ULONG reqlen; 4334 UNICODE_STRING fn; 4335 NTSTATUS Status; 4336 static const WCHAR datasuf[] = {':','$','D','A','T','A',0}; 4337 uint16_t datasuflen = sizeof(datasuf) - sizeof(WCHAR); 4338 4339 if (!fileref) { 4340 ERR("called without fileref\n"); 4341 return STATUS_INVALID_PARAMETER; 4342 } 4343 4344 *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]); 4345 4346 TRACE("maximum length is %li\n", *length); 4347 fni->FileNameLength = 0; 4348 4349 fni->FileName[0] = 0; 4350 4351 fn.Buffer = fni->FileName; 4352 fn.Length = 0; 4353 fn.MaximumLength = (uint16_t)*length; 4354 4355 Status = fileref_get_filename(fileref, &fn, NULL, &reqlen); 4356 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) { 4357 ERR("fileref_get_filename returned %08lx\n", Status); 4358 return Status; 4359 } 4360 4361 if (fcb->ads) { 4362 if (Status == STATUS_BUFFER_OVERFLOW) 4363 reqlen += datasuflen; 4364 else { 4365 if (fn.Length + datasuflen > fn.MaximumLength) { 4366 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length); 4367 reqlen += datasuflen; 4368 Status = STATUS_BUFFER_OVERFLOW; 4369 } else { 4370 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen); 4371 fn.Length += datasuflen; 4372 } 4373 } 4374 } 4375 4376 if (Status == STATUS_BUFFER_OVERFLOW) { 4377 *length = -1; 4378 fni->FileNameLength = reqlen; 4379 TRACE("%.*S (truncated)\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer); 4380 } else { 4381 *length -= fn.Length; 4382 fni->FileNameLength = fn.Length; 4383 TRACE("%.*S\n", (int)(fn.Length / sizeof(WCHAR)), fn.Buffer); 4384 } 4385 4386 return Status; 4387 } 4388 4389 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, LONG* length) { 4390 *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION); 4391 4392 if (fcb->ads) { 4393 if (!ccb->fileref || !ccb->fileref->parent) { 4394 ERR("no fileref for stream\n"); 4395 return STATUS_INTERNAL_ERROR; 4396 } 4397 4398 ati->FileAttributes = ccb->fileref->parent->fcb->atts; 4399 } else 4400 ati->FileAttributes = fcb->atts; 4401 4402 if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 4403 ati->ReparseTag = 0; 4404 else 4405 ati->ReparseTag = get_reparse_tag_fcb(fcb); 4406 4407 return STATUS_SUCCESS; 4408 } 4409 4410 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) { 4411 LONG reqsize; 4412 LIST_ENTRY* le; 4413 FILE_STREAM_INFORMATION *entry, *lastentry; 4414 NTSTATUS Status; 4415 4416 static const WCHAR datasuf[] = L":$DATA"; 4417 UNICODE_STRING suf; 4418 4419 if (!fileref) { 4420 ERR("fileref was NULL\n"); 4421 return STATUS_INVALID_PARAMETER; 4422 } 4423 4424 suf.Buffer = (WCHAR*)datasuf; 4425 suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR); 4426 4427 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) 4428 reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR); 4429 else 4430 reqsize = 0; 4431 4432 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, true); 4433 4434 le = fileref->fcb->dir_children_index.Flink; 4435 while (le != &fileref->fcb->dir_children_index) { 4436 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); 4437 4438 if (dc->index == 0) { 4439 reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG)); 4440 reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length; 4441 } else 4442 break; 4443 4444 le = le->Flink; 4445 } 4446 4447 TRACE("length = %li, reqsize = %lu\n", *length, reqsize); 4448 4449 if (reqsize > *length) { 4450 Status = STATUS_BUFFER_OVERFLOW; 4451 goto end; 4452 } 4453 4454 entry = fsi; 4455 lastentry = NULL; 4456 4457 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) { 4458 ULONG off; 4459 4460 entry->NextEntryOffset = 0; 4461 entry->StreamNameLength = suf.Length + sizeof(WCHAR); 4462 entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size; 4463 entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb); 4464 4465 entry->StreamName[0] = ':'; 4466 RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length); 4467 4468 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG)); 4469 4470 lastentry = entry; 4471 entry = (FILE_STREAM_INFORMATION*)((uint8_t*)entry + off); 4472 } 4473 4474 le = fileref->fcb->dir_children_index.Flink; 4475 while (le != &fileref->fcb->dir_children_index) { 4476 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); 4477 4478 if (dc->index == 0) { 4479 ULONG off; 4480 4481 entry->NextEntryOffset = 0; 4482 entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR); 4483 4484 if (dc->fileref) 4485 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length; 4486 else 4487 entry->StreamSize.QuadPart = dc->size; 4488 4489 entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart; 4490 4491 entry->StreamName[0] = ':'; 4492 4493 RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length); 4494 RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length); 4495 4496 if (lastentry) 4497 lastentry->NextEntryOffset = (uint32_t)((uint8_t*)entry - (uint8_t*)lastentry); 4498 4499 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG)); 4500 4501 lastentry = entry; 4502 entry = (FILE_STREAM_INFORMATION*)((uint8_t*)entry + off); 4503 } else 4504 break; 4505 4506 le = le->Flink; 4507 } 4508 4509 *length -= reqsize; 4510 4511 Status = STATUS_SUCCESS; 4512 4513 end: 4514 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock); 4515 4516 return Status; 4517 } 4518 4519 #ifndef __REACTOS__ 4520 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) { 4521 TRACE("FileStandardLinkInformation\n"); 4522 4523 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close 4524 4525 fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink; 4526 fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink; 4527 fsli->DeletePending = fileref ? fileref->delete_on_close : false; 4528 fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? true : false; 4529 4530 *length -= sizeof(FILE_STANDARD_LINK_INFORMATION); 4531 4532 return STATUS_SUCCESS; 4533 } 4534 4535 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) { 4536 NTSTATUS Status; 4537 LIST_ENTRY* le; 4538 LONG bytes_needed; 4539 FILE_LINK_ENTRY_INFORMATION* feli; 4540 bool overflow = false; 4541 fcb* fcb = fileref->fcb; 4542 ULONG len; 4543 4544 if (fcb->ads) 4545 return STATUS_INVALID_PARAMETER; 4546 4547 if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry)) 4548 return STATUS_INVALID_PARAMETER; 4549 4550 RtlZeroMemory(fli, *length); 4551 4552 bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry); 4553 len = bytes_needed; 4554 feli = NULL; 4555 4556 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 4557 4558 if (fcb->inode == SUBVOL_ROOT_INODE) { 4559 ULONG namelen; 4560 4561 if (fcb == fcb->Vcb->root_fileref->fcb) 4562 namelen = sizeof(WCHAR); 4563 else 4564 namelen = fileref->dc->name.Length; 4565 4566 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen; 4567 4568 if (bytes_needed > *length) 4569 overflow = true; 4570 4571 if (!overflow) { 4572 feli = &fli->Entry; 4573 4574 feli->NextEntryOffset = 0; 4575 feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume 4576 4577 if (fcb == fcb->Vcb->root_fileref->fcb) { 4578 feli->FileNameLength = 1; 4579 feli->FileName[0] = '.'; 4580 } else { 4581 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR); 4582 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length); 4583 } 4584 4585 fli->EntriesReturned++; 4586 4587 len = bytes_needed; 4588 } 4589 } else { 4590 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true); 4591 4592 if (IsListEmpty(&fcb->hardlinks)) { 4593 if (!fileref->dc) { 4594 ExReleaseResourceLite(&fcb->Vcb->fileref_lock); 4595 Status = STATUS_INVALID_PARAMETER; 4596 goto end; 4597 } 4598 4599 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR); 4600 4601 if (bytes_needed > *length) 4602 overflow = true; 4603 4604 if (!overflow) { 4605 feli = &fli->Entry; 4606 4607 feli->NextEntryOffset = 0; 4608 feli->ParentFileId = fileref->parent->fcb->inode; 4609 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR); 4610 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length); 4611 4612 fli->EntriesReturned++; 4613 4614 len = bytes_needed; 4615 } 4616 } else { 4617 le = fcb->hardlinks.Flink; 4618 while (le != &fcb->hardlinks) { 4619 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); 4620 file_ref* parfr; 4621 4622 TRACE("parent %I64x, index %I64x, name %.*S\n", hl->parent, hl->index, (int)(hl->name.Length / sizeof(WCHAR)), hl->name.Buffer); 4623 4624 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp); 4625 4626 if (!NT_SUCCESS(Status)) { 4627 ERR("open_fileref_by_inode returned %08lx\n", Status); 4628 } else if (!parfr->deleted) { 4629 LIST_ENTRY* le2; 4630 bool found = false, deleted = false; 4631 UNICODE_STRING* fn = NULL; 4632 4633 le2 = parfr->children.Flink; 4634 while (le2 != &parfr->children) { 4635 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry); 4636 4637 if (fr2->dc && fr2->dc->index == hl->index) { 4638 found = true; 4639 deleted = fr2->deleted; 4640 4641 if (!deleted) 4642 fn = &fr2->dc->name; 4643 4644 break; 4645 } 4646 4647 le2 = le2->Flink; 4648 } 4649 4650 if (!found) 4651 fn = &hl->name; 4652 4653 if (!deleted) { 4654 TRACE("fn = %.*S (found = %u)\n", (int)(fn->Length / sizeof(WCHAR)), fn->Buffer, found); 4655 4656 if (feli) 4657 bytes_needed = (LONG)sector_align(bytes_needed, 8); 4658 4659 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR); 4660 4661 if (bytes_needed > *length) 4662 overflow = true; 4663 4664 if (!overflow) { 4665 if (feli) { 4666 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8); 4667 feli = (FILE_LINK_ENTRY_INFORMATION*)((uint8_t*)feli + feli->NextEntryOffset); 4668 } else 4669 feli = &fli->Entry; 4670 4671 feli->NextEntryOffset = 0; 4672 feli->ParentFileId = parfr->fcb->inode; 4673 feli->FileNameLength = fn->Length / sizeof(WCHAR); 4674 RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length); 4675 4676 fli->EntriesReturned++; 4677 4678 len = bytes_needed; 4679 } 4680 } 4681 4682 free_fileref(parfr); 4683 } 4684 4685 le = le->Flink; 4686 } 4687 } 4688 4689 ExReleaseResourceLite(&fcb->Vcb->fileref_lock); 4690 } 4691 4692 fli->BytesNeeded = bytes_needed; 4693 4694 *length -= len; 4695 4696 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; 4697 4698 end: 4699 ExReleaseResourceLite(fcb->Header.Resource); 4700 4701 return Status; 4702 } 4703 4704 static NTSTATUS fill_in_hard_link_full_id_information(FILE_LINKS_FULL_ID_INFORMATION* flfii, file_ref* fileref, PIRP Irp, LONG* length) { 4705 NTSTATUS Status; 4706 LIST_ENTRY* le; 4707 LONG bytes_needed; 4708 FILE_LINK_ENTRY_FULL_ID_INFORMATION* flefii; 4709 bool overflow = false; 4710 fcb* fcb = fileref->fcb; 4711 ULONG len; 4712 4713 if (fcb->ads) 4714 return STATUS_INVALID_PARAMETER; 4715 4716 if (*length < (LONG)offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry)) 4717 return STATUS_INVALID_PARAMETER; 4718 4719 RtlZeroMemory(flfii, *length); 4720 4721 bytes_needed = offsetof(FILE_LINKS_FULL_ID_INFORMATION, Entry); 4722 len = bytes_needed; 4723 flefii = NULL; 4724 4725 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 4726 4727 if (fcb->inode == SUBVOL_ROOT_INODE) { 4728 ULONG namelen; 4729 4730 if (fcb == fcb->Vcb->root_fileref->fcb) 4731 namelen = sizeof(WCHAR); 4732 else 4733 namelen = fileref->dc->name.Length; 4734 4735 bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + namelen; 4736 4737 if (bytes_needed > *length) 4738 overflow = true; 4739 4740 if (!overflow) { 4741 flefii = &flfii->Entry; 4742 4743 flefii->NextEntryOffset = 0; 4744 4745 if (fcb == fcb->Vcb->root_fileref->fcb) { 4746 RtlZeroMemory(&flefii->ParentFileId.Identifier[0], sizeof(FILE_ID_128)); 4747 flefii->FileNameLength = 1; 4748 flefii->FileName[0] = '.'; 4749 } else { 4750 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(uint64_t)); 4751 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &fileref->parent->fcb->subvol->id, sizeof(uint64_t)); 4752 4753 flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR); 4754 RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length); 4755 } 4756 4757 flfii->EntriesReturned++; 4758 4759 len = bytes_needed; 4760 } 4761 } else { 4762 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, true); 4763 4764 if (IsListEmpty(&fcb->hardlinks)) { 4765 bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fileref->dc->name.Length; 4766 4767 if (bytes_needed > *length) 4768 overflow = true; 4769 4770 if (!overflow) { 4771 flefii = &flfii->Entry; 4772 4773 flefii->NextEntryOffset = 0; 4774 4775 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &fileref->parent->fcb->inode, sizeof(uint64_t)); 4776 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &fileref->parent->fcb->subvol->id, sizeof(uint64_t)); 4777 4778 flefii->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR); 4779 RtlCopyMemory(flefii->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length); 4780 4781 flfii->EntriesReturned++; 4782 4783 len = bytes_needed; 4784 } 4785 } else { 4786 le = fcb->hardlinks.Flink; 4787 while (le != &fcb->hardlinks) { 4788 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); 4789 file_ref* parfr; 4790 4791 TRACE("parent %I64x, index %I64x, name %.*S\n", hl->parent, hl->index, (int)(hl->name.Length / sizeof(WCHAR)), hl->name.Buffer); 4792 4793 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp); 4794 4795 if (!NT_SUCCESS(Status)) { 4796 ERR("open_fileref_by_inode returned %08lx\n", Status); 4797 } else if (!parfr->deleted) { 4798 LIST_ENTRY* le2; 4799 bool found = false, deleted = false; 4800 UNICODE_STRING* fn = NULL; 4801 4802 le2 = parfr->children.Flink; 4803 while (le2 != &parfr->children) { 4804 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry); 4805 4806 if (fr2->dc->index == hl->index) { 4807 found = true; 4808 deleted = fr2->deleted; 4809 4810 if (!deleted) 4811 fn = &fr2->dc->name; 4812 4813 break; 4814 } 4815 4816 le2 = le2->Flink; 4817 } 4818 4819 if (!found) 4820 fn = &hl->name; 4821 4822 if (!deleted) { 4823 TRACE("fn = %.*S (found = %u)\n", (int)(fn->Length / sizeof(WCHAR)), fn->Buffer, found); 4824 4825 if (flefii) 4826 bytes_needed = (LONG)sector_align(bytes_needed, 8); 4827 4828 bytes_needed += offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + fn->Length; 4829 4830 if (bytes_needed > *length) 4831 overflow = true; 4832 4833 if (!overflow) { 4834 if (flefii) { 4835 flefii->NextEntryOffset = (ULONG)sector_align(offsetof(FILE_LINK_ENTRY_FULL_ID_INFORMATION, FileName[0]) + (flefii->FileNameLength * sizeof(WCHAR)), 8); 4836 flefii = (FILE_LINK_ENTRY_FULL_ID_INFORMATION*)((uint8_t*)flefii + flefii->NextEntryOffset); 4837 } else 4838 flefii = &flfii->Entry; 4839 4840 flefii->NextEntryOffset = 0; 4841 4842 RtlCopyMemory(&flefii->ParentFileId.Identifier[0], &parfr->fcb->inode, sizeof(uint64_t)); 4843 RtlCopyMemory(&flefii->ParentFileId.Identifier[sizeof(uint64_t)], &parfr->fcb->subvol->id, sizeof(uint64_t)); 4844 4845 flefii->FileNameLength = fn->Length / sizeof(WCHAR); 4846 RtlCopyMemory(flefii->FileName, fn->Buffer, fn->Length); 4847 4848 flfii->EntriesReturned++; 4849 4850 len = bytes_needed; 4851 } 4852 } 4853 4854 free_fileref(parfr); 4855 } 4856 4857 le = le->Flink; 4858 } 4859 } 4860 4861 ExReleaseResourceLite(&fcb->Vcb->fileref_lock); 4862 } 4863 4864 flfii->BytesNeeded = bytes_needed; 4865 4866 *length -= len; 4867 4868 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; 4869 4870 ExReleaseResourceLite(fcb->Header.Resource); 4871 4872 return Status; 4873 } 4874 4875 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) { 4876 RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(uint64_t)); 4877 RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(uint64_t)); 4878 RtlCopyMemory(&fii->FileId.Identifier[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t)); 4879 4880 *length -= sizeof(FILE_ID_INFORMATION); 4881 4882 return STATUS_SUCCESS; 4883 } 4884 4885 static NTSTATUS fill_in_file_stat_information(FILE_STAT_INFORMATION* fsi, fcb* fcb, ccb* ccb, LONG* length) { 4886 INODE_ITEM* ii; 4887 4888 fsi->FileId.QuadPart = make_file_id(fcb->subvol, fcb->inode); 4889 4890 if (fcb->ads) 4891 ii = &ccb->fileref->parent->fcb->inode_item; 4892 else 4893 ii = &fcb->inode_item; 4894 4895 if (fcb == fcb->Vcb->dummy_fcb) { 4896 LARGE_INTEGER time; 4897 4898 KeQuerySystemTime(&time); 4899 fsi->CreationTime = fsi->LastAccessTime = fsi->LastWriteTime = fsi->ChangeTime = time; 4900 } else { 4901 fsi->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 4902 fsi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 4903 fsi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 4904 fsi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 4905 } 4906 4907 if (fcb->ads) { 4908 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length; 4909 fsi->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts; 4910 } else { 4911 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 4912 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 4913 fsi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 4914 } 4915 4916 if (fcb->type == BTRFS_TYPE_SOCKET) 4917 fsi->ReparseTag = IO_REPARSE_TAG_AF_UNIX; 4918 else if (fcb->type == BTRFS_TYPE_FIFO) 4919 fsi->ReparseTag = IO_REPARSE_TAG_LX_FIFO; 4920 else if (fcb->type == BTRFS_TYPE_CHARDEV) 4921 fsi->ReparseTag = IO_REPARSE_TAG_LX_CHR; 4922 else if (fcb->type == BTRFS_TYPE_BLOCKDEV) 4923 fsi->ReparseTag = IO_REPARSE_TAG_LX_BLK; 4924 else if (!(fsi->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 4925 fsi->ReparseTag = 0; 4926 else 4927 fsi->ReparseTag = get_reparse_tag_fcb(fcb); 4928 4929 if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV) 4930 fsi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; 4931 4932 if (fcb->ads) 4933 fsi->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink; 4934 else 4935 fsi->NumberOfLinks = fcb->inode_item.st_nlink; 4936 4937 fsi->EffectiveAccess = ccb->access; 4938 4939 *length -= sizeof(FILE_STAT_INFORMATION); 4940 4941 return STATUS_SUCCESS; 4942 } 4943 4944 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) { 4945 INODE_ITEM* ii; 4946 4947 fsli->FileId.QuadPart = make_file_id(fcb->subvol, fcb->inode); 4948 4949 if (fcb->ads) 4950 ii = &ccb->fileref->parent->fcb->inode_item; 4951 else 4952 ii = &fcb->inode_item; 4953 4954 if (fcb == fcb->Vcb->dummy_fcb) { 4955 LARGE_INTEGER time; 4956 4957 KeQuerySystemTime(&time); 4958 fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime = fsli->ChangeTime = time; 4959 } else { 4960 fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 4961 fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 4962 fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 4963 fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 4964 } 4965 4966 if (fcb->ads) { 4967 fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart = fcb->adsdata.Length; 4968 fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts; 4969 } else { 4970 fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb); 4971 fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 4972 fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 4973 } 4974 4975 if (fcb->type == BTRFS_TYPE_SOCKET) 4976 fsli->ReparseTag = IO_REPARSE_TAG_AF_UNIX; 4977 else if (fcb->type == BTRFS_TYPE_FIFO) 4978 fsli->ReparseTag = IO_REPARSE_TAG_LX_FIFO; 4979 else if (fcb->type == BTRFS_TYPE_CHARDEV) 4980 fsli->ReparseTag = IO_REPARSE_TAG_LX_CHR; 4981 else if (fcb->type == BTRFS_TYPE_BLOCKDEV) 4982 fsli->ReparseTag = IO_REPARSE_TAG_LX_BLK; 4983 else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 4984 fsli->ReparseTag = 0; 4985 else 4986 fsli->ReparseTag = get_reparse_tag_fcb(fcb); 4987 4988 if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV) 4989 fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; 4990 4991 if (fcb->ads) 4992 fsli->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink; 4993 else 4994 fsli->NumberOfLinks = fcb->inode_item.st_nlink; 4995 4996 fsli->EffectiveAccess = ccb->access; 4997 fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID; 4998 4999 if (fcb->case_sensitive) 5000 fsli->LxFlags |= LX_FILE_CASE_SENSITIVE_DIR; 5001 5002 fsli->LxUid = ii->st_uid; 5003 fsli->LxGid = ii->st_gid; 5004 fsli->LxMode = ii->st_mode; 5005 5006 if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) { 5007 fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20; 5008 fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF); 5009 } else { 5010 fsli->LxDeviceIdMajor = 0; 5011 fsli->LxDeviceIdMinor = 0; 5012 } 5013 5014 *length -= sizeof(FILE_STAT_LX_INFORMATION); 5015 5016 return STATUS_SUCCESS; 5017 } 5018 5019 static NTSTATUS fill_in_file_case_sensitive_information(FILE_CASE_SENSITIVE_INFORMATION* fcsi, fcb* fcb, LONG* length) { 5020 fcsi->Flags = fcb->case_sensitive ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0; 5021 5022 *length -= sizeof(FILE_CASE_SENSITIVE_INFORMATION); 5023 5024 return STATUS_SUCCESS; 5025 } 5026 5027 #endif // __REACTOS__ 5028 5029 static NTSTATUS fill_in_file_compression_information(FILE_COMPRESSION_INFORMATION* fci, LONG* length, fcb* fcb) { 5030 *length -= sizeof(FILE_COMPRESSION_INFORMATION); 5031 5032 memset(fci, 0, sizeof(FILE_COMPRESSION_INFORMATION)); 5033 5034 if (fcb->ads) 5035 fci->CompressedFileSize.QuadPart = fcb->adsdata.Length; 5036 else if (!S_ISDIR(fcb->inode_item.st_mode)) 5037 fci->CompressedFileSize.QuadPart = fcb->inode_item.st_size; 5038 5039 return STATUS_SUCCESS; 5040 } 5041 5042 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) { 5043 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 5044 LONG length = IrpSp->Parameters.QueryFile.Length; 5045 fcb* fcb = FileObject->FsContext; 5046 ccb* ccb = FileObject->FsContext2; 5047 file_ref* fileref = ccb ? ccb->fileref : NULL; 5048 NTSTATUS Status; 5049 5050 TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp); 5051 TRACE("fcb = %p\n", fcb); 5052 5053 if (fcb == Vcb->volume_fcb) 5054 return STATUS_INVALID_PARAMETER; 5055 5056 if (!ccb) { 5057 ERR("ccb is NULL\n"); 5058 return STATUS_INVALID_PARAMETER; 5059 } 5060 5061 switch (IrpSp->Parameters.QueryFile.FileInformationClass) { 5062 case FileAllInformation: 5063 { 5064 FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer; 5065 INODE_ITEM* ii; 5066 5067 TRACE("FileAllInformation\n"); 5068 5069 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 5070 WARN("insufficient privileges\n"); 5071 Status = STATUS_ACCESS_DENIED; 5072 goto exit; 5073 } 5074 5075 if (fcb->ads) { 5076 if (!fileref || !fileref->parent) { 5077 ERR("no fileref for stream\n"); 5078 Status = STATUS_INTERNAL_ERROR; 5079 goto exit; 5080 } 5081 5082 ii = &fileref->parent->fcb->inode_item; 5083 } else 5084 ii = &fcb->inode_item; 5085 5086 // Access, mode, and alignment are all filled in by the kernel 5087 5088 if (length > 0) 5089 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref); 5090 5091 if (length > 0) 5092 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length); 5093 5094 if (length > 0) 5095 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length); 5096 5097 if (length > 0) 5098 fill_in_file_ea_information(&fai->EaInformation, fcb, &length); 5099 5100 length -= sizeof(FILE_ACCESS_INFORMATION); 5101 5102 if (length > 0) 5103 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length); 5104 5105 length -= sizeof(FILE_MODE_INFORMATION); 5106 5107 length -= sizeof(FILE_ALIGNMENT_INFORMATION); 5108 5109 if (length > 0) 5110 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length); 5111 5112 Status = STATUS_SUCCESS; 5113 5114 break; 5115 } 5116 5117 case FileAttributeTagInformation: 5118 { 5119 FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer; 5120 5121 TRACE("FileAttributeTagInformation\n"); 5122 5123 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 5124 WARN("insufficient privileges\n"); 5125 Status = STATUS_ACCESS_DENIED; 5126 goto exit; 5127 } 5128 5129 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 5130 Status = fill_in_file_attribute_information(ati, fcb, ccb, &length); 5131 ExReleaseResourceLite(&Vcb->tree_lock); 5132 5133 break; 5134 } 5135 5136 case FileBasicInformation: 5137 { 5138 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer; 5139 INODE_ITEM* ii; 5140 5141 TRACE("FileBasicInformation\n"); 5142 5143 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 5144 WARN("insufficient privileges\n"); 5145 Status = STATUS_ACCESS_DENIED; 5146 goto exit; 5147 } 5148 5149 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) { 5150 WARN("overflow\n"); 5151 Status = STATUS_BUFFER_OVERFLOW; 5152 goto exit; 5153 } 5154 5155 if (fcb->ads) { 5156 if (!fileref || !fileref->parent) { 5157 ERR("no fileref for stream\n"); 5158 Status = STATUS_INTERNAL_ERROR; 5159 goto exit; 5160 } 5161 5162 ii = &fileref->parent->fcb->inode_item; 5163 } else 5164 ii = &fcb->inode_item; 5165 5166 Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref); 5167 break; 5168 } 5169 5170 case FileCompressionInformation: 5171 { 5172 FILE_COMPRESSION_INFORMATION* fci = Irp->AssociatedIrp.SystemBuffer; 5173 5174 TRACE("FileCompressionInformation\n"); 5175 5176 Status = fill_in_file_compression_information(fci, &length, fcb); 5177 break; 5178 } 5179 5180 case FileEaInformation: 5181 { 5182 FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer; 5183 5184 TRACE("FileEaInformation\n"); 5185 5186 Status = fill_in_file_ea_information(eai, fcb, &length); 5187 5188 break; 5189 } 5190 5191 case FileInternalInformation: 5192 { 5193 FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer; 5194 5195 TRACE("FileInternalInformation\n"); 5196 5197 Status = fill_in_file_internal_information(fii, fcb, &length); 5198 5199 break; 5200 } 5201 5202 case FileNameInformation: 5203 { 5204 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer; 5205 5206 TRACE("FileNameInformation\n"); 5207 5208 Status = fill_in_file_name_information(fni, fcb, fileref, &length); 5209 5210 break; 5211 } 5212 5213 case FileNetworkOpenInformation: 5214 { 5215 FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer; 5216 5217 TRACE("FileNetworkOpenInformation\n"); 5218 5219 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 5220 WARN("insufficient privileges\n"); 5221 Status = STATUS_ACCESS_DENIED; 5222 goto exit; 5223 } 5224 5225 Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length); 5226 5227 break; 5228 } 5229 5230 case FilePositionInformation: 5231 { 5232 FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer; 5233 5234 TRACE("FilePositionInformation\n"); 5235 5236 Status = fill_in_file_position_information(fpi, FileObject, &length); 5237 5238 break; 5239 } 5240 5241 case FileStandardInformation: 5242 { 5243 FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer; 5244 5245 TRACE("FileStandardInformation\n"); 5246 5247 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) { 5248 WARN("overflow\n"); 5249 Status = STATUS_BUFFER_OVERFLOW; 5250 goto exit; 5251 } 5252 5253 Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length); 5254 5255 break; 5256 } 5257 5258 case FileStreamInformation: 5259 { 5260 FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer; 5261 5262 TRACE("FileStreamInformation\n"); 5263 5264 Status = fill_in_file_stream_information(fsi, fileref, &length); 5265 5266 break; 5267 } 5268 5269 #if (NTDDI_VERSION >= NTDDI_VISTA) 5270 case FileHardLinkInformation: 5271 { 5272 FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer; 5273 5274 TRACE("FileHardLinkInformation\n"); 5275 5276 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 5277 Status = fill_in_hard_link_information(fli, fileref, Irp, &length); 5278 ExReleaseResourceLite(&Vcb->tree_lock); 5279 5280 break; 5281 } 5282 5283 case FileNormalizedNameInformation: 5284 { 5285 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer; 5286 5287 TRACE("FileNormalizedNameInformation\n"); 5288 5289 Status = fill_in_file_name_information(fni, fcb, fileref, &length); 5290 5291 break; 5292 } 5293 5294 case FileStandardLinkInformation: 5295 { 5296 FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer; 5297 5298 TRACE("FileStandardLinkInformation\n"); 5299 5300 Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length); 5301 5302 break; 5303 } 5304 5305 case FileRemoteProtocolInformation: 5306 TRACE("FileRemoteProtocolInformation\n"); 5307 Status = STATUS_INVALID_PARAMETER; 5308 goto exit; 5309 5310 #ifndef _MSC_VER 5311 #pragma GCC diagnostic push 5312 #pragma GCC diagnostic ignored "-Wswitch" 5313 #endif 5314 case FileIdInformation: 5315 { 5316 FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer; 5317 5318 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) { 5319 WARN("overflow\n"); 5320 Status = STATUS_BUFFER_OVERFLOW; 5321 goto exit; 5322 } 5323 5324 TRACE("FileIdInformation\n"); 5325 5326 Status = fill_in_file_id_information(fii, fcb, &length); 5327 5328 break; 5329 } 5330 5331 case FileStatInformation: 5332 { 5333 FILE_STAT_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer; 5334 5335 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_INFORMATION)) { 5336 WARN("overflow\n"); 5337 Status = STATUS_BUFFER_OVERFLOW; 5338 goto exit; 5339 } 5340 5341 TRACE("FileStatInformation\n"); 5342 5343 Status = fill_in_file_stat_information(fsi, fcb, ccb, &length); 5344 5345 break; 5346 } 5347 5348 case FileStatLxInformation: 5349 { 5350 FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer; 5351 5352 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) { 5353 WARN("overflow\n"); 5354 Status = STATUS_BUFFER_OVERFLOW; 5355 goto exit; 5356 } 5357 5358 TRACE("FileStatLxInformation\n"); 5359 5360 Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length); 5361 5362 break; 5363 } 5364 5365 case FileCaseSensitiveInformation: 5366 { 5367 FILE_CASE_SENSITIVE_INFORMATION* fcsi = Irp->AssociatedIrp.SystemBuffer; 5368 5369 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_CASE_SENSITIVE_INFORMATION)) { 5370 WARN("overflow\n"); 5371 Status = STATUS_BUFFER_OVERFLOW; 5372 goto exit; 5373 } 5374 5375 TRACE("FileCaseSensitiveInformation\n"); 5376 5377 Status = fill_in_file_case_sensitive_information(fcsi, fcb, &length); 5378 5379 break; 5380 } 5381 5382 case FileHardLinkFullIdInformation: 5383 { 5384 FILE_LINKS_FULL_ID_INFORMATION* flfii = Irp->AssociatedIrp.SystemBuffer; 5385 5386 TRACE("FileHardLinkFullIdInformation\n"); 5387 5388 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 5389 Status = fill_in_hard_link_full_id_information(flfii, fileref, Irp, &length); 5390 ExReleaseResourceLite(&Vcb->tree_lock); 5391 5392 break; 5393 } 5394 #ifndef _MSC_VER 5395 #pragma GCC diagnostic pop 5396 #endif 5397 #endif 5398 5399 default: 5400 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass); 5401 Status = STATUS_INVALID_PARAMETER; 5402 goto exit; 5403 } 5404 5405 if (length < 0) { 5406 length = 0; 5407 Status = STATUS_BUFFER_OVERFLOW; 5408 } 5409 5410 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length; 5411 5412 exit: 5413 TRACE("query_info returning %08lx\n", Status); 5414 5415 return Status; 5416 } 5417 5418 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION) 5419 _Function_class_(DRIVER_DISPATCH) 5420 NTSTATUS __stdcall drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 5421 PIO_STACK_LOCATION IrpSp; 5422 NTSTATUS Status; 5423 fcb* fcb; 5424 device_extension* Vcb = DeviceObject->DeviceExtension; 5425 bool top_level; 5426 5427 FsRtlEnterFileSystem(); 5428 5429 top_level = is_top_level(Irp); 5430 5431 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 5432 Status = STATUS_INVALID_DEVICE_REQUEST; 5433 goto end; 5434 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 5435 Status = STATUS_INVALID_PARAMETER; 5436 goto end; 5437 } 5438 5439 Irp->IoStatus.Information = 0; 5440 5441 TRACE("query information\n"); 5442 5443 IrpSp = IoGetCurrentIrpStackLocation(Irp); 5444 5445 fcb = IrpSp->FileObject->FsContext; 5446 TRACE("fcb = %p\n", fcb); 5447 TRACE("fcb->subvol = %p\n", fcb->subvol); 5448 5449 Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp); 5450 5451 end: 5452 TRACE("returning %08lx\n", Status); 5453 5454 Irp->IoStatus.Status = Status; 5455 5456 IoCompleteRequest( Irp, IO_NO_INCREMENT ); 5457 5458 if (top_level) 5459 IoSetTopLevelIrp(NULL); 5460 5461 FsRtlExitFileSystem(); 5462 5463 return Status; 5464 } 5465 5466 _Dispatch_type_(IRP_MJ_QUERY_EA) 5467 _Function_class_(DRIVER_DISPATCH) 5468 NTSTATUS __stdcall drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 5469 NTSTATUS Status; 5470 bool top_level; 5471 device_extension* Vcb = DeviceObject->DeviceExtension; 5472 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 5473 PFILE_OBJECT FileObject = IrpSp->FileObject; 5474 fcb* fcb; 5475 ccb* ccb; 5476 FILE_FULL_EA_INFORMATION* ffei; 5477 ULONG retlen = 0; 5478 5479 FsRtlEnterFileSystem(); 5480 5481 TRACE("(%p, %p)\n", DeviceObject, Irp); 5482 5483 top_level = is_top_level(Irp); 5484 5485 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 5486 Status = STATUS_INVALID_DEVICE_REQUEST; 5487 goto end; 5488 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 5489 Status = STATUS_INVALID_PARAMETER; 5490 goto end; 5491 } 5492 5493 ffei = map_user_buffer(Irp, NormalPagePriority); 5494 if (!ffei) { 5495 ERR("could not get output buffer\n"); 5496 Status = STATUS_INVALID_PARAMETER; 5497 goto end; 5498 } 5499 5500 if (!FileObject) { 5501 ERR("no file object\n"); 5502 Status = STATUS_INVALID_PARAMETER; 5503 goto end; 5504 } 5505 5506 fcb = FileObject->FsContext; 5507 5508 if (!fcb) { 5509 ERR("no fcb\n"); 5510 Status = STATUS_INVALID_PARAMETER; 5511 goto end; 5512 } 5513 5514 if (fcb == fcb->Vcb->volume_fcb) { 5515 Status = STATUS_INVALID_PARAMETER; 5516 goto end; 5517 } 5518 5519 ccb = FileObject->FsContext2; 5520 5521 if (!ccb) { 5522 ERR("no ccb\n"); 5523 Status = STATUS_INVALID_PARAMETER; 5524 goto end; 5525 } 5526 5527 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) { 5528 WARN("insufficient privileges\n"); 5529 Status = STATUS_ACCESS_DENIED; 5530 goto end; 5531 } 5532 5533 if (fcb->ads) 5534 fcb = ccb->fileref->parent->fcb; 5535 5536 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 5537 5538 if (fcb->ea_xattr.Length == 0) { 5539 Status = STATUS_NO_EAS_ON_FILE; 5540 goto end2; 5541 } 5542 5543 Status = STATUS_SUCCESS; 5544 5545 if (IrpSp->Parameters.QueryEa.EaList) { 5546 FILE_FULL_EA_INFORMATION *ea, *out; 5547 FILE_GET_EA_INFORMATION* in; 5548 5549 in = IrpSp->Parameters.QueryEa.EaList; 5550 do { 5551 STRING s; 5552 5553 s.Length = s.MaximumLength = in->EaNameLength; 5554 s.Buffer = in->EaName; 5555 5556 RtlUpperString(&s, &s); 5557 5558 if (in->NextEntryOffset == 0) 5559 break; 5560 5561 in = (FILE_GET_EA_INFORMATION*)(((uint8_t*)in) + in->NextEntryOffset); 5562 } while (true); 5563 5564 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 5565 out = NULL; 5566 5567 do { 5568 bool found = false; 5569 5570 in = IrpSp->Parameters.QueryEa.EaList; 5571 do { 5572 if (in->EaNameLength == ea->EaNameLength && 5573 RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) { 5574 found = true; 5575 break; 5576 } 5577 5578 if (in->NextEntryOffset == 0) 5579 break; 5580 5581 in = (FILE_GET_EA_INFORMATION*)(((uint8_t*)in) + in->NextEntryOffset); 5582 } while (true); 5583 5584 if (found) { 5585 uint8_t padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0; 5586 5587 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) { 5588 Status = STATUS_BUFFER_OVERFLOW; 5589 retlen = 0; 5590 goto end2; 5591 } 5592 5593 retlen += padding; 5594 5595 if (out) { 5596 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding; 5597 out = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)out) + out->NextEntryOffset); 5598 } else 5599 out = ffei; 5600 5601 out->NextEntryOffset = 0; 5602 out->Flags = ea->Flags; 5603 out->EaNameLength = ea->EaNameLength; 5604 out->EaValueLength = ea->EaValueLength; 5605 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1); 5606 5607 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength; 5608 5609 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) 5610 break; 5611 } 5612 5613 if (ea->NextEntryOffset == 0) 5614 break; 5615 5616 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset); 5617 } while (true); 5618 } else { 5619 FILE_FULL_EA_INFORMATION *ea, *out; 5620 ULONG index; 5621 5622 if (IrpSp->Flags & SL_INDEX_SPECIFIED) { 5623 // The index is 1-based 5624 if (IrpSp->Parameters.QueryEa.EaIndex == 0) { 5625 Status = STATUS_NONEXISTENT_EA_ENTRY; 5626 goto end2; 5627 } else 5628 index = IrpSp->Parameters.QueryEa.EaIndex - 1; 5629 } else if (IrpSp->Flags & SL_RESTART_SCAN) 5630 index = ccb->ea_index = 0; 5631 else 5632 index = ccb->ea_index; 5633 5634 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 5635 5636 if (index > 0) { 5637 ULONG i; 5638 5639 for (i = 0; i < index; i++) { 5640 if (ea->NextEntryOffset == 0) { // last item 5641 Status = STATUS_NO_MORE_EAS; 5642 goto end2; 5643 } 5644 5645 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset); 5646 } 5647 } 5648 5649 out = NULL; 5650 5651 do { 5652 uint8_t padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0; 5653 5654 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) { 5655 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW; 5656 goto end2; 5657 } 5658 5659 retlen += padding; 5660 5661 if (out) { 5662 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding; 5663 out = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)out) + out->NextEntryOffset); 5664 } else 5665 out = ffei; 5666 5667 out->NextEntryOffset = 0; 5668 out->Flags = ea->Flags; 5669 out->EaNameLength = ea->EaNameLength; 5670 out->EaValueLength = ea->EaValueLength; 5671 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1); 5672 5673 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength; 5674 5675 if (!(IrpSp->Flags & SL_INDEX_SPECIFIED)) 5676 ccb->ea_index++; 5677 5678 if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) 5679 break; 5680 5681 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset); 5682 } while (true); 5683 } 5684 5685 end2: 5686 ExReleaseResourceLite(fcb->Header.Resource); 5687 5688 end: 5689 TRACE("returning %08lx\n", Status); 5690 5691 Irp->IoStatus.Status = Status; 5692 Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0; 5693 5694 IoCompleteRequest( Irp, IO_NO_INCREMENT ); 5695 5696 if (top_level) 5697 IoSetTopLevelIrp(NULL); 5698 5699 FsRtlExitFileSystem(); 5700 5701 return Status; 5702 } 5703 5704 _Dispatch_type_(IRP_MJ_SET_EA) 5705 _Function_class_(DRIVER_DISPATCH) 5706 NTSTATUS __stdcall drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 5707 device_extension* Vcb = DeviceObject->DeviceExtension; 5708 NTSTATUS Status; 5709 bool top_level; 5710 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 5711 PFILE_OBJECT FileObject = IrpSp->FileObject; 5712 fcb* fcb; 5713 ccb* ccb; 5714 file_ref* fileref; 5715 FILE_FULL_EA_INFORMATION* ffei; 5716 ULONG offset; 5717 LIST_ENTRY ealist; 5718 ea_item* item; 5719 FILE_FULL_EA_INFORMATION* ea; 5720 LIST_ENTRY* le; 5721 LARGE_INTEGER time; 5722 BTRFS_TIME now; 5723 5724 FsRtlEnterFileSystem(); 5725 5726 TRACE("(%p, %p)\n", DeviceObject, Irp); 5727 5728 top_level = is_top_level(Irp); 5729 5730 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 5731 Status = STATUS_INVALID_DEVICE_REQUEST; 5732 goto end; 5733 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 5734 Status = STATUS_INVALID_PARAMETER; 5735 goto end; 5736 } 5737 5738 if (Vcb->readonly) { 5739 Status = STATUS_MEDIA_WRITE_PROTECTED; 5740 goto end; 5741 } 5742 5743 ffei = map_user_buffer(Irp, NormalPagePriority); 5744 if (!ffei) { 5745 ERR("could not get output buffer\n"); 5746 Status = STATUS_INVALID_PARAMETER; 5747 goto end; 5748 } 5749 5750 Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset); 5751 if (!NT_SUCCESS(Status)) { 5752 ERR("IoCheckEaBufferValidity returned %08lx (error at offset %lu)\n", Status, offset); 5753 goto end; 5754 } 5755 5756 if (!FileObject) { 5757 ERR("no file object\n"); 5758 Status = STATUS_INVALID_PARAMETER; 5759 goto end; 5760 } 5761 5762 fcb = FileObject->FsContext; 5763 5764 if (!fcb) { 5765 ERR("no fcb\n"); 5766 Status = STATUS_INVALID_PARAMETER; 5767 goto end; 5768 } 5769 5770 if (fcb == fcb->Vcb->volume_fcb) { 5771 Status = STATUS_INVALID_PARAMETER; 5772 goto end; 5773 } 5774 5775 ccb = FileObject->FsContext2; 5776 5777 if (!ccb) { 5778 ERR("no ccb\n"); 5779 Status = STATUS_INVALID_PARAMETER; 5780 goto end; 5781 } 5782 5783 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) { 5784 WARN("insufficient privileges\n"); 5785 Status = STATUS_ACCESS_DENIED; 5786 goto end; 5787 } 5788 5789 if (fcb->ads) { 5790 fileref = ccb->fileref->parent; 5791 fcb = fileref->fcb; 5792 } else 5793 fileref = ccb->fileref; 5794 5795 InitializeListHead(&ealist); 5796 5797 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 5798 5799 if (fcb->ea_xattr.Length > 0) { 5800 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 5801 5802 do { 5803 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG); 5804 if (!item) { 5805 ERR("out of memory\n"); 5806 Status = STATUS_INSUFFICIENT_RESOURCES; 5807 goto end2; 5808 } 5809 5810 item->name.Length = item->name.MaximumLength = ea->EaNameLength; 5811 item->name.Buffer = ea->EaName; 5812 5813 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 5814 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 5815 5816 item->flags = ea->Flags; 5817 5818 InsertTailList(&ealist, &item->list_entry); 5819 5820 if (ea->NextEntryOffset == 0) 5821 break; 5822 5823 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset); 5824 } while (true); 5825 } 5826 5827 ea = ffei; 5828 5829 do { 5830 STRING s; 5831 bool found = false; 5832 5833 s.Length = s.MaximumLength = ea->EaNameLength; 5834 s.Buffer = ea->EaName; 5835 5836 RtlUpperString(&s, &s); 5837 5838 le = ealist.Flink; 5839 while (le != &ealist) { 5840 item = CONTAINING_RECORD(le, ea_item, list_entry); 5841 5842 if (item->name.Length == s.Length && 5843 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) { 5844 item->flags = ea->Flags; 5845 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 5846 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 5847 found = true; 5848 break; 5849 } 5850 5851 le = le->Flink; 5852 } 5853 5854 if (!found) { 5855 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG); 5856 if (!item) { 5857 ERR("out of memory\n"); 5858 Status = STATUS_INSUFFICIENT_RESOURCES; 5859 goto end2; 5860 } 5861 5862 item->name.Length = item->name.MaximumLength = ea->EaNameLength; 5863 item->name.Buffer = ea->EaName; 5864 5865 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 5866 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 5867 5868 item->flags = ea->Flags; 5869 5870 InsertTailList(&ealist, &item->list_entry); 5871 } 5872 5873 if (ea->NextEntryOffset == 0) 5874 break; 5875 5876 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset); 5877 } while (true); 5878 5879 // remove entries with zero-length value 5880 le = ealist.Flink; 5881 while (le != &ealist) { 5882 LIST_ENTRY* le2 = le->Flink; 5883 5884 item = CONTAINING_RECORD(le, ea_item, list_entry); 5885 5886 if (item->value.Length == 0) { 5887 RemoveEntryList(&item->list_entry); 5888 ExFreePool(item); 5889 } 5890 5891 le = le2; 5892 } 5893 5894 // handle LXSS values 5895 le = ealist.Flink; 5896 while (le != &ealist) { 5897 LIST_ENTRY* le2 = le->Flink; 5898 5899 item = CONTAINING_RECORD(le, ea_item, list_entry); 5900 5901 if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) { 5902 if (item->value.Length < sizeof(uint32_t)) { 5903 ERR("uid value was shorter than expected\n"); 5904 Status = STATUS_INVALID_PARAMETER; 5905 goto end2; 5906 } 5907 5908 if (Irp->RequestorMode == KernelMode || ccb->access & FILE_WRITE_ATTRIBUTES) { 5909 RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(uint32_t)); 5910 fcb->sd_dirty = true; 5911 fcb->sd_deleted = false; 5912 } 5913 5914 RemoveEntryList(&item->list_entry); 5915 ExFreePool(item); 5916 } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) { 5917 if (item->value.Length < sizeof(uint32_t)) { 5918 ERR("gid value was shorter than expected\n"); 5919 Status = STATUS_INVALID_PARAMETER; 5920 goto end2; 5921 } 5922 5923 if (Irp->RequestorMode == KernelMode || ccb->access & FILE_WRITE_ATTRIBUTES) 5924 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(uint32_t)); 5925 5926 RemoveEntryList(&item->list_entry); 5927 ExFreePool(item); 5928 } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) { 5929 if (item->value.Length < sizeof(uint32_t)) { 5930 ERR("mode value was shorter than expected\n"); 5931 Status = STATUS_INVALID_PARAMETER; 5932 goto end2; 5933 } 5934 5935 if (Irp->RequestorMode == KernelMode || ccb->access & FILE_WRITE_ATTRIBUTES) { 5936 uint32_t allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | S_ISGID | S_ISVTX | S_ISUID; 5937 uint32_t val; 5938 5939 RtlCopyMemory(&val, item->value.Buffer, sizeof(uint32_t)); 5940 5941 fcb->inode_item.st_mode &= ~allowed; 5942 fcb->inode_item.st_mode |= val & allowed; 5943 } 5944 5945 RemoveEntryList(&item->list_entry); 5946 ExFreePool(item); 5947 } 5948 5949 le = le2; 5950 } 5951 5952 if (IsListEmpty(&ealist)) { 5953 fcb->ealen = 0; 5954 5955 if (fcb->ea_xattr.Buffer) 5956 ExFreePool(fcb->ea_xattr.Buffer); 5957 5958 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0; 5959 fcb->ea_xattr.Buffer = NULL; 5960 } else { 5961 uint16_t size = 0; 5962 char *buf, *oldbuf; 5963 5964 le = ealist.Flink; 5965 while (le != &ealist) { 5966 item = CONTAINING_RECORD(le, ea_item, list_entry); 5967 5968 if (size % 4 > 0) 5969 size += 4 - (size % 4); 5970 5971 size += (uint16_t)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length; 5972 5973 le = le->Flink; 5974 } 5975 5976 buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG); 5977 if (!buf) { 5978 ERR("out of memory\n"); 5979 Status = STATUS_INSUFFICIENT_RESOURCES; 5980 goto end2; 5981 } 5982 5983 oldbuf = fcb->ea_xattr.Buffer; 5984 5985 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size; 5986 fcb->ea_xattr.Buffer = buf; 5987 5988 fcb->ealen = 4; 5989 ea = NULL; 5990 5991 le = ealist.Flink; 5992 while (le != &ealist) { 5993 item = CONTAINING_RECORD(le, ea_item, list_entry); 5994 5995 if (ea) { 5996 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength; 5997 5998 if (ea->NextEntryOffset % 4 > 0) 5999 ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4); 6000 6001 ea = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)ea) + ea->NextEntryOffset); 6002 } else 6003 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 6004 6005 ea->NextEntryOffset = 0; 6006 ea->Flags = item->flags; 6007 ea->EaNameLength = (UCHAR)item->name.Length; 6008 ea->EaValueLength = item->value.Length; 6009 6010 RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length); 6011 ea->EaName[item->name.Length] = 0; 6012 RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length); 6013 6014 fcb->ealen += 5 + item->name.Length + item->value.Length; 6015 6016 le = le->Flink; 6017 } 6018 6019 if (oldbuf) 6020 ExFreePool(oldbuf); 6021 } 6022 6023 fcb->ea_changed = true; 6024 6025 KeQuerySystemTime(&time); 6026 win_time_to_unix(time, &now); 6027 6028 fcb->inode_item.transid = Vcb->superblock.generation; 6029 fcb->inode_item.sequence++; 6030 6031 if (!ccb->user_set_change_time) 6032 fcb->inode_item.st_ctime = now; 6033 6034 fcb->inode_item_changed = true; 6035 mark_fcb_dirty(fcb); 6036 6037 send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL); 6038 6039 Status = STATUS_SUCCESS; 6040 6041 end2: 6042 ExReleaseResourceLite(fcb->Header.Resource); 6043 6044 while (!IsListEmpty(&ealist)) { 6045 le = RemoveHeadList(&ealist); 6046 6047 item = CONTAINING_RECORD(le, ea_item, list_entry); 6048 6049 ExFreePool(item); 6050 } 6051 6052 end: 6053 TRACE("returning %08lx\n", Status); 6054 6055 Irp->IoStatus.Status = Status; 6056 Irp->IoStatus.Information = 0; 6057 6058 IoCompleteRequest(Irp, IO_NO_INCREMENT); 6059 6060 if (top_level) 6061 IoSetTopLevelIrp(NULL); 6062 6063 FsRtlExitFileSystem(); 6064 6065 return Status; 6066 } 6067