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