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 20 #if (NTDDI_VERSION >= NTDDI_WIN10) 21 // not currently in mingw - introduced with Windows 10 22 #ifndef _MSC_VER 23 #define FileIdInformation (enum _FILE_INFORMATION_CLASS)59 24 #define FileStatLxInformation (enum _FILE_INFORMATION_CLASS)70 25 26 typedef struct _FILE_STAT_LX_INFORMATION { 27 LARGE_INTEGER FileId; 28 LARGE_INTEGER CreationTime; 29 LARGE_INTEGER LastAccessTime; 30 LARGE_INTEGER LastWriteTime; 31 LARGE_INTEGER ChangeTime; 32 LARGE_INTEGER AllocationSize; 33 LARGE_INTEGER EndOfFile; 34 ULONG FileAttributes; 35 ULONG ReparseTag; 36 ULONG NumberOfLinks; 37 ACCESS_MASK EffectiveAccess; 38 ULONG LxFlags; 39 ULONG LxUid; 40 ULONG LxGid; 41 ULONG LxMode; 42 ULONG LxDeviceIdMajor; 43 ULONG LxDeviceIdMinor; 44 } FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION; 45 46 #define LX_FILE_METADATA_HAS_UID 0x01 47 #define LX_FILE_METADATA_HAS_GID 0x02 48 #define LX_FILE_METADATA_HAS_MODE 0x04 49 #define LX_FILE_METADATA_HAS_DEVICE_ID 0x08 50 #define LX_FILE_CASE_SENSITIVE_DIR 0x10 51 52 #endif 53 #endif 54 55 static NTSTATUS set_basic_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { 56 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer; 57 fcb* fcb = FileObject->FsContext; 58 ccb* ccb = FileObject->FsContext2; 59 file_ref* fileref = ccb ? ccb->fileref : NULL; 60 ULONG defda, filter = 0; 61 BOOL inode_item_changed = FALSE; 62 NTSTATUS Status; 63 64 if (fcb->ads) { 65 if (fileref && fileref->parent) 66 fcb = fileref->parent->fcb; 67 else { 68 ERR("stream did not have fileref\n"); 69 return STATUS_INTERNAL_ERROR; 70 } 71 } 72 73 if (!ccb) { 74 ERR("ccb was NULL\n"); 75 return STATUS_INVALID_PARAMETER; 76 } 77 78 TRACE("file = %S, attributes = %x\n", file_desc(FileObject), fbi->FileAttributes); 79 80 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 81 82 if (fbi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY && fcb->type != BTRFS_TYPE_DIRECTORY) { 83 WARN("attempted to set FILE_ATTRIBUTE_DIRECTORY on non-directory\n"); 84 Status = STATUS_INVALID_PARAMETER; 85 goto end; 86 } 87 88 if (fcb->inode == SUBVOL_ROOT_INODE && is_subvol_readonly(fcb->subvol, Irp) && 89 (fbi->FileAttributes == 0 || fbi->FileAttributes & FILE_ATTRIBUTE_READONLY)) { 90 Status = STATUS_ACCESS_DENIED; 91 goto end; 92 } 93 94 // don't allow readonly subvol to be made r/w if send operation running on it 95 if (fcb->inode == SUBVOL_ROOT_INODE && fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY && 96 fcb->subvol->send_ops > 0) { 97 Status = STATUS_DEVICE_NOT_READY; 98 goto end; 99 } 100 101 // times of -2 are some sort of undocumented behaviour to do with LXSS 102 103 if (fbi->CreationTime.QuadPart == -2) 104 fbi->CreationTime.QuadPart = 0; 105 106 if (fbi->LastAccessTime.QuadPart == -2) 107 fbi->LastAccessTime.QuadPart = 0; 108 109 if (fbi->LastWriteTime.QuadPart == -2) 110 fbi->LastWriteTime.QuadPart = 0; 111 112 if (fbi->ChangeTime.QuadPart == -2) 113 fbi->ChangeTime.QuadPart = 0; 114 115 if (fbi->CreationTime.QuadPart == -1) 116 ccb->user_set_creation_time = TRUE; 117 else if (fbi->CreationTime.QuadPart != 0) { 118 win_time_to_unix(fbi->CreationTime, &fcb->inode_item.otime); 119 inode_item_changed = TRUE; 120 filter |= FILE_NOTIFY_CHANGE_CREATION; 121 122 ccb->user_set_creation_time = TRUE; 123 } 124 125 if (fbi->LastAccessTime.QuadPart == -1) 126 ccb->user_set_access_time = TRUE; 127 else if (fbi->LastAccessTime.QuadPart != 0) { 128 win_time_to_unix(fbi->LastAccessTime, &fcb->inode_item.st_atime); 129 inode_item_changed = TRUE; 130 filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; 131 132 ccb->user_set_access_time = TRUE; 133 } 134 135 if (fbi->LastWriteTime.QuadPart == -1) 136 ccb->user_set_write_time = TRUE; 137 else if (fbi->LastWriteTime.QuadPart != 0) { 138 win_time_to_unix(fbi->LastWriteTime, &fcb->inode_item.st_mtime); 139 inode_item_changed = TRUE; 140 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 141 142 ccb->user_set_write_time = TRUE; 143 } 144 145 if (fbi->ChangeTime.QuadPart == -1) 146 ccb->user_set_change_time = TRUE; 147 else if (fbi->ChangeTime.QuadPart != 0) { 148 win_time_to_unix(fbi->ChangeTime, &fcb->inode_item.st_ctime); 149 inode_item_changed = TRUE; 150 // no filter for this 151 152 ccb->user_set_change_time = TRUE; 153 } 154 155 // FileAttributes == 0 means don't set - undocumented, but seen in fastfat 156 if (fbi->FileAttributes != 0) { 157 LARGE_INTEGER time; 158 BTRFS_TIME now; 159 160 fbi->FileAttributes &= ~FILE_ATTRIBUTE_NORMAL; 161 162 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] == '.', 163 TRUE, Irp); 164 165 if (fcb->type == BTRFS_TYPE_DIRECTORY) 166 fbi->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; 167 else if (fcb->type == BTRFS_TYPE_SYMLINK) 168 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; 169 170 fcb->atts_changed = TRUE; 171 172 if (fcb->atts & FILE_ATTRIBUTE_REPARSE_POINT) 173 fbi->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; 174 175 if (defda == fbi->FileAttributes) 176 fcb->atts_deleted = TRUE; 177 else if (fcb->inode == SUBVOL_ROOT_INODE && (defda | FILE_ATTRIBUTE_READONLY) == (fbi->FileAttributes | FILE_ATTRIBUTE_READONLY)) 178 fcb->atts_deleted = TRUE; 179 180 fcb->atts = fbi->FileAttributes; 181 182 KeQuerySystemTime(&time); 183 win_time_to_unix(time, &now); 184 185 if (!ccb->user_set_change_time) 186 fcb->inode_item.st_ctime = now; 187 188 fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 189 fcb->subvol->root_item.ctime = now; 190 191 if (fcb->inode == SUBVOL_ROOT_INODE) { 192 if (fbi->FileAttributes & FILE_ATTRIBUTE_READONLY) 193 fcb->subvol->root_item.flags |= BTRFS_SUBVOL_READONLY; 194 else 195 fcb->subvol->root_item.flags &= ~BTRFS_SUBVOL_READONLY; 196 } 197 198 inode_item_changed = TRUE; 199 200 filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; 201 } 202 203 if (inode_item_changed) { 204 fcb->inode_item.transid = Vcb->superblock.generation; 205 fcb->inode_item.sequence++; 206 fcb->inode_item_changed = TRUE; 207 208 mark_fcb_dirty(fcb); 209 } 210 211 if (filter != 0) 212 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); 213 214 Status = STATUS_SUCCESS; 215 216 end: 217 ExReleaseResourceLite(fcb->Header.Resource); 218 219 return Status; 220 } 221 222 static NTSTATUS set_disposition_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { 223 FILE_DISPOSITION_INFORMATION* fdi = Irp->AssociatedIrp.SystemBuffer; 224 fcb* fcb = FileObject->FsContext; 225 ccb* ccb = FileObject->FsContext2; 226 file_ref* fileref = ccb ? ccb->fileref : NULL; 227 ULONG atts; 228 NTSTATUS Status; 229 230 if (!fileref) 231 return STATUS_INVALID_PARAMETER; 232 233 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 234 235 TRACE("changing delete_on_close to %s for %S (fcb %p)\n", fdi->DeleteFile ? "TRUE" : "FALSE", file_desc(FileObject), fcb); 236 237 if (fcb->ads) { 238 if (fileref->parent) 239 atts = fileref->parent->fcb->atts; 240 else { 241 ERR("no fileref for stream\n"); 242 Status = STATUS_INTERNAL_ERROR; 243 goto end; 244 } 245 } else 246 atts = fcb->atts; 247 248 TRACE("atts = %x\n", atts); 249 250 if (atts & FILE_ATTRIBUTE_READONLY) { 251 TRACE("not allowing readonly file to be deleted\n"); 252 Status = STATUS_CANNOT_DELETE; 253 goto end; 254 } 255 256 // FIXME - can we skip this bit for subvols? 257 if (fcb->type == BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && (!fileref || fileref->fcb != Vcb->dummy_fcb)) { 258 TRACE("directory not empty\n"); 259 Status = STATUS_DIRECTORY_NOT_EMPTY; 260 goto end; 261 } 262 263 if (!MmFlushImageSection(&fcb->nonpaged->segment_object, MmFlushForDelete)) { 264 TRACE("trying to delete file which is being mapped as an image\n"); 265 Status = STATUS_CANNOT_DELETE; 266 goto end; 267 } 268 269 ccb->fileref->delete_on_close = fdi->DeleteFile; 270 271 FileObject->DeletePending = fdi->DeleteFile; 272 273 Status = STATUS_SUCCESS; 274 275 end: 276 ExReleaseResourceLite(fcb->Header.Resource); 277 278 // send notification that directory is about to be deleted 279 if (NT_SUCCESS(Status) && fdi->DeleteFile && fcb->type == BTRFS_TYPE_DIRECTORY) { 280 FsRtlNotifyFullChangeDirectory(Vcb->NotifySync, &Vcb->DirNotifyList, FileObject->FsContext, 281 NULL, FALSE, FALSE, 0, NULL, NULL, NULL); 282 } 283 284 return Status; 285 } 286 287 BOOL has_open_children(file_ref* fileref) { 288 LIST_ENTRY* le = fileref->children.Flink; 289 290 if (IsListEmpty(&fileref->children)) 291 return FALSE; 292 293 while (le != &fileref->children) { 294 file_ref* c = CONTAINING_RECORD(le, file_ref, list_entry); 295 296 if (c->open_count > 0) 297 return TRUE; 298 299 if (has_open_children(c)) 300 return TRUE; 301 302 le = le->Flink; 303 } 304 305 return FALSE; 306 } 307 308 static NTSTATUS duplicate_fcb(fcb* oldfcb, fcb** pfcb) { 309 device_extension* Vcb = oldfcb->Vcb; 310 fcb* fcb; 311 LIST_ENTRY* le; 312 313 // FIXME - we can skip a lot of this if the inode is about to be deleted 314 315 fcb = create_fcb(Vcb, PagedPool); // FIXME - what if we duplicate the paging file? 316 if (!fcb) { 317 ERR("out of memory\n"); 318 return STATUS_INSUFFICIENT_RESOURCES; 319 } 320 321 fcb->Vcb = Vcb; 322 323 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 324 fcb->Header.AllocationSize = oldfcb->Header.AllocationSize; 325 fcb->Header.FileSize = oldfcb->Header.FileSize; 326 fcb->Header.ValidDataLength = oldfcb->Header.ValidDataLength; 327 328 fcb->type = oldfcb->type; 329 330 if (oldfcb->ads) { 331 fcb->ads = TRUE; 332 fcb->adshash = oldfcb->adshash; 333 fcb->adsmaxlen = oldfcb->adsmaxlen; 334 335 if (oldfcb->adsxattr.Buffer && oldfcb->adsxattr.Length > 0) { 336 fcb->adsxattr.Length = oldfcb->adsxattr.Length; 337 fcb->adsxattr.MaximumLength = fcb->adsxattr.Length + 1; 338 fcb->adsxattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsxattr.MaximumLength, ALLOC_TAG); 339 340 if (!fcb->adsxattr.Buffer) { 341 ERR("out of memory\n"); 342 free_fcb(fcb); 343 return STATUS_INSUFFICIENT_RESOURCES; 344 } 345 346 RtlCopyMemory(fcb->adsxattr.Buffer, oldfcb->adsxattr.Buffer, fcb->adsxattr.Length); 347 fcb->adsxattr.Buffer[fcb->adsxattr.Length] = 0; 348 } 349 350 if (oldfcb->adsdata.Buffer && oldfcb->adsdata.Length > 0) { 351 fcb->adsdata.Length = fcb->adsdata.MaximumLength = oldfcb->adsdata.Length; 352 fcb->adsdata.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->adsdata.MaximumLength, ALLOC_TAG); 353 354 if (!fcb->adsdata.Buffer) { 355 ERR("out of memory\n"); 356 free_fcb(fcb); 357 return STATUS_INSUFFICIENT_RESOURCES; 358 } 359 360 RtlCopyMemory(fcb->adsdata.Buffer, oldfcb->adsdata.Buffer, fcb->adsdata.Length); 361 } 362 363 goto end; 364 } 365 366 RtlCopyMemory(&fcb->inode_item, &oldfcb->inode_item, sizeof(INODE_ITEM)); 367 fcb->inode_item_changed = TRUE; 368 369 if (oldfcb->sd && RtlLengthSecurityDescriptor(oldfcb->sd) > 0) { 370 fcb->sd = ExAllocatePoolWithTag(PagedPool, RtlLengthSecurityDescriptor(oldfcb->sd), ALLOC_TAG); 371 if (!fcb->sd) { 372 ERR("out of memory\n"); 373 free_fcb(fcb); 374 return STATUS_INSUFFICIENT_RESOURCES; 375 } 376 377 RtlCopyMemory(fcb->sd, oldfcb->sd, RtlLengthSecurityDescriptor(oldfcb->sd)); 378 } 379 380 fcb->atts = oldfcb->atts; 381 382 le = oldfcb->extents.Flink; 383 while (le != &oldfcb->extents) { 384 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 385 386 if (!ext->ignore) { 387 extent* ext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 388 389 if (!ext2) { 390 ERR("out of memory\n"); 391 free_fcb(fcb); 392 return STATUS_INSUFFICIENT_RESOURCES; 393 } 394 395 ext2->offset = ext->offset; 396 ext2->datalen = ext->datalen; 397 398 if (ext2->datalen > 0) 399 RtlCopyMemory(&ext2->extent_data, &ext->extent_data, ext2->datalen); 400 401 ext2->unique = FALSE; 402 ext2->ignore = FALSE; 403 ext2->inserted = TRUE; 404 405 if (ext->csum) { 406 ULONG len; 407 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 408 409 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) 410 len = (ULONG)ed2->num_bytes; 411 else 412 len = (ULONG)ed2->size; 413 414 len = len * sizeof(UINT32) / Vcb->superblock.sector_size; 415 416 ext2->csum = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG); 417 if (!ext2->csum) { 418 ERR("out of memory\n"); 419 free_fcb(fcb); 420 return STATUS_INSUFFICIENT_RESOURCES; 421 } 422 423 RtlCopyMemory(ext2->csum, ext->csum, len); 424 } else 425 ext2->csum = NULL; 426 427 InsertTailList(&fcb->extents, &ext2->list_entry); 428 } 429 430 le = le->Flink; 431 } 432 433 le = oldfcb->hardlinks.Flink; 434 while (le != &oldfcb->hardlinks) { 435 hardlink *hl = CONTAINING_RECORD(le, hardlink, list_entry), *hl2; 436 437 hl2 = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 438 439 if (!hl2) { 440 ERR("out of memory\n"); 441 free_fcb(fcb); 442 return STATUS_INSUFFICIENT_RESOURCES; 443 } 444 445 hl2->parent = hl->parent; 446 hl2->index = hl->index; 447 448 hl2->name.Length = hl2->name.MaximumLength = hl->name.Length; 449 hl2->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->name.MaximumLength, ALLOC_TAG); 450 451 if (!hl2->name.Buffer) { 452 ERR("out of memory\n"); 453 ExFreePool(hl2); 454 free_fcb(fcb); 455 return STATUS_INSUFFICIENT_RESOURCES; 456 } 457 458 RtlCopyMemory(hl2->name.Buffer, hl->name.Buffer, hl->name.Length); 459 460 hl2->utf8.Length = hl2->utf8.MaximumLength = hl->utf8.Length; 461 hl2->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl2->utf8.MaximumLength, ALLOC_TAG); 462 463 if (!hl2->utf8.Buffer) { 464 ERR("out of memory\n"); 465 ExFreePool(hl2->name.Buffer); 466 ExFreePool(hl2); 467 free_fcb(fcb); 468 return STATUS_INSUFFICIENT_RESOURCES; 469 } 470 471 RtlCopyMemory(hl2->utf8.Buffer, hl->utf8.Buffer, hl->utf8.Length); 472 473 InsertTailList(&fcb->hardlinks, &hl2->list_entry); 474 475 le = le->Flink; 476 } 477 478 if (oldfcb->reparse_xattr.Buffer && oldfcb->reparse_xattr.Length > 0) { 479 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = oldfcb->reparse_xattr.Length; 480 481 fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->reparse_xattr.MaximumLength, ALLOC_TAG); 482 if (!fcb->reparse_xattr.Buffer) { 483 ERR("out of memory\n"); 484 free_fcb(fcb); 485 return STATUS_INSUFFICIENT_RESOURCES; 486 } 487 488 RtlCopyMemory(fcb->reparse_xattr.Buffer, oldfcb->reparse_xattr.Buffer, fcb->reparse_xattr.Length); 489 } 490 491 if (oldfcb->ea_xattr.Buffer && oldfcb->ea_xattr.Length > 0) { 492 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = oldfcb->ea_xattr.Length; 493 494 fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, fcb->ea_xattr.MaximumLength, ALLOC_TAG); 495 if (!fcb->ea_xattr.Buffer) { 496 ERR("out of memory\n"); 497 free_fcb(fcb); 498 return STATUS_INSUFFICIENT_RESOURCES; 499 } 500 501 RtlCopyMemory(fcb->ea_xattr.Buffer, oldfcb->ea_xattr.Buffer, fcb->ea_xattr.Length); 502 } 503 504 fcb->prop_compression = oldfcb->prop_compression; 505 506 le = oldfcb->xattrs.Flink; 507 while (le != &oldfcb->xattrs) { 508 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry); 509 510 if (xa->valuelen > 0) { 511 xattr* xa2; 512 513 xa2 = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + xa->namelen + xa->valuelen, ALLOC_TAG); 514 515 if (!xa2) { 516 ERR("out of memory\n"); 517 free_fcb(fcb); 518 return STATUS_INSUFFICIENT_RESOURCES; 519 } 520 521 xa2->namelen = xa->namelen; 522 xa2->valuelen = xa->valuelen; 523 xa2->dirty = xa->dirty; 524 memcpy(xa2->data, xa->data, xa->namelen + xa->valuelen); 525 526 InsertTailList(&fcb->xattrs, &xa2->list_entry); 527 } 528 529 le = le->Flink; 530 } 531 532 end: 533 *pfcb = fcb; 534 535 return STATUS_SUCCESS; 536 } 537 538 typedef struct _move_entry { 539 file_ref* fileref; 540 fcb* dummyfcb; 541 file_ref* dummyfileref; 542 struct _move_entry* parent; 543 LIST_ENTRY list_entry; 544 } move_entry; 545 546 static NTSTATUS add_children_to_move_list(device_extension* Vcb, move_entry* me, PIRP Irp) { 547 NTSTATUS Status; 548 LIST_ENTRY* le; 549 550 ExAcquireResourceSharedLite(&me->fileref->fcb->nonpaged->dir_children_lock, TRUE); 551 552 le = me->fileref->fcb->dir_children_index.Flink; 553 554 while (le != &me->fileref->fcb->dir_children_index) { 555 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); 556 file_ref* fr; 557 move_entry* me2; 558 559 Status = open_fileref_child(Vcb, me->fileref, &dc->name, TRUE, TRUE, dc->index == 0 ? TRUE : FALSE, PagedPool, &fr, Irp); 560 561 if (!NT_SUCCESS(Status)) { 562 ERR("open_fileref_child returned %08x\n", Status); 563 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock); 564 return Status; 565 } 566 567 me2 = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG); 568 if (!me2) { 569 ERR("out of memory\n"); 570 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock); 571 return STATUS_INSUFFICIENT_RESOURCES; 572 } 573 574 me2->fileref = fr; 575 me2->dummyfcb = NULL; 576 me2->dummyfileref = NULL; 577 me2->parent = me; 578 579 InsertHeadList(&me->list_entry, &me2->list_entry); 580 581 le = le->Flink; 582 } 583 584 ExReleaseResourceLite(&me->fileref->fcb->nonpaged->dir_children_lock); 585 586 return STATUS_SUCCESS; 587 } 588 589 void remove_dir_child_from_hash_lists(fcb* fcb, dir_child* dc) { 590 UINT8 c; 591 592 c = dc->hash >> 24; 593 594 if (fcb->hash_ptrs[c] == &dc->list_entry_hash) { 595 if (dc->list_entry_hash.Flink == &fcb->dir_children_hash) 596 fcb->hash_ptrs[c] = NULL; 597 else { 598 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash.Flink, dir_child, list_entry_hash); 599 600 if (dc2->hash >> 24 == c) 601 fcb->hash_ptrs[c] = &dc2->list_entry_hash; 602 else 603 fcb->hash_ptrs[c] = NULL; 604 } 605 } 606 607 RemoveEntryList(&dc->list_entry_hash); 608 609 c = dc->hash_uc >> 24; 610 611 if (fcb->hash_ptrs_uc[c] == &dc->list_entry_hash_uc) { 612 if (dc->list_entry_hash_uc.Flink == &fcb->dir_children_hash_uc) 613 fcb->hash_ptrs_uc[c] = NULL; 614 else { 615 dir_child* dc2 = CONTAINING_RECORD(dc->list_entry_hash_uc.Flink, dir_child, list_entry_hash_uc); 616 617 if (dc2->hash_uc >> 24 == c) 618 fcb->hash_ptrs_uc[c] = &dc2->list_entry_hash_uc; 619 else 620 fcb->hash_ptrs_uc[c] = NULL; 621 } 622 } 623 624 RemoveEntryList(&dc->list_entry_hash_uc); 625 } 626 627 static NTSTATUS create_directory_fcb(device_extension* Vcb, root* r, fcb* parfcb, fcb** pfcb) { 628 NTSTATUS Status; 629 fcb* fcb; 630 SECURITY_SUBJECT_CONTEXT subjcont; 631 PSID owner; 632 BOOLEAN defaulted; 633 LARGE_INTEGER time; 634 BTRFS_TIME now; 635 636 fcb = create_fcb(Vcb, PagedPool); 637 if (!fcb) { 638 ERR("out of memory\n"); 639 return STATUS_INSUFFICIENT_RESOURCES; 640 } 641 642 KeQuerySystemTime(&time); 643 win_time_to_unix(time, &now); 644 645 fcb->Vcb = Vcb; 646 647 fcb->subvol = r; 648 fcb->inode = InterlockedIncrement64(&r->lastinode); 649 fcb->type = BTRFS_TYPE_DIRECTORY; 650 651 fcb->inode_item.generation = Vcb->superblock.generation; 652 fcb->inode_item.transid = Vcb->superblock.generation; 653 fcb->inode_item.st_nlink = 1; 654 fcb->inode_item.st_mode = __S_IFDIR | inherit_mode(parfcb, TRUE); 655 fcb->inode_item.st_atime = fcb->inode_item.st_ctime = fcb->inode_item.st_mtime = fcb->inode_item.otime = now; 656 fcb->inode_item.st_gid = GID_NOBODY; 657 658 fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, FALSE, TRUE, NULL); 659 660 SeCaptureSubjectContext(&subjcont); 661 662 Status = SeAssignSecurity(parfcb->sd, NULL, (void**)&fcb->sd, TRUE, &subjcont, IoGetFileObjectGenericMapping(), PagedPool); 663 664 if (!NT_SUCCESS(Status)) { 665 reap_fcb(fcb); 666 ERR("SeAssignSecurity returned %08x\n", Status); 667 return Status; 668 } 669 670 if (!fcb->sd) { 671 reap_fcb(fcb); 672 ERR("SeAssignSecurity returned NULL security descriptor\n"); 673 return STATUS_INTERNAL_ERROR; 674 } 675 676 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted); 677 if (!NT_SUCCESS(Status)) { 678 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status); 679 fcb->inode_item.st_uid = UID_NOBODY; 680 fcb->sd_dirty = TRUE; 681 } else { 682 fcb->inode_item.st_uid = sid_to_uid(owner); 683 fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY; 684 } 685 686 find_gid(fcb, parfcb, &subjcont); 687 688 fcb->inode_item_changed = TRUE; 689 690 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 691 fcb->Header.AllocationSize.QuadPart = 0; 692 fcb->Header.FileSize.QuadPart = 0; 693 fcb->Header.ValidDataLength.QuadPart = 0; 694 695 fcb->created = TRUE; 696 697 if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS) 698 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS; 699 700 fcb->prop_compression = parfcb->prop_compression; 701 fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None; 702 703 fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); 704 if (!fcb->hash_ptrs) { 705 ERR("out of memory\n"); 706 return STATUS_INSUFFICIENT_RESOURCES; 707 } 708 709 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256); 710 711 fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); 712 if (!fcb->hash_ptrs_uc) { 713 ERR("out of memory\n"); 714 return STATUS_INSUFFICIENT_RESOURCES; 715 } 716 717 RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256); 718 719 acquire_fcb_lock_exclusive(Vcb); 720 InsertTailList(&r->fcbs, &fcb->list_entry); 721 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); 722 r->fcbs_version++; 723 release_fcb_lock(Vcb); 724 725 mark_fcb_dirty(fcb); 726 727 *pfcb = fcb; 728 729 return STATUS_SUCCESS; 730 } 731 732 static NTSTATUS move_across_subvols(file_ref* fileref, ccb* ccb, file_ref* destdir, PANSI_STRING utf8, PUNICODE_STRING fnus, PIRP Irp, LIST_ENTRY* rollback) { 733 NTSTATUS Status; 734 LIST_ENTRY move_list, *le; 735 move_entry* me; 736 LARGE_INTEGER time; 737 BTRFS_TIME now; 738 file_ref* origparent; 739 740 // FIXME - make sure me->dummyfileref and me->dummyfcb get freed properly 741 742 InitializeListHead(&move_list); 743 744 KeQuerySystemTime(&time); 745 win_time_to_unix(time, &now); 746 747 acquire_fcb_lock_exclusive(fileref->fcb->Vcb); 748 749 me = ExAllocatePoolWithTag(PagedPool, sizeof(move_entry), ALLOC_TAG); 750 751 if (!me) { 752 ERR("out of memory\n"); 753 Status = STATUS_INSUFFICIENT_RESOURCES; 754 goto end; 755 } 756 757 origparent = fileref->parent; 758 759 me->fileref = fileref; 760 increase_fileref_refcount(me->fileref); 761 me->dummyfcb = NULL; 762 me->dummyfileref = NULL; 763 me->parent = NULL; 764 765 InsertTailList(&move_list, &me->list_entry); 766 767 le = move_list.Flink; 768 while (le != &move_list) { 769 me = CONTAINING_RECORD(le, move_entry, list_entry); 770 771 ExAcquireResourceSharedLite(me->fileref->fcb->Header.Resource, TRUE); 772 773 if (!me->fileref->fcb->ads && me->fileref->fcb->subvol == origparent->fcb->subvol) { 774 Status = add_children_to_move_list(fileref->fcb->Vcb, me, Irp); 775 776 if (!NT_SUCCESS(Status)) { 777 ERR("add_children_to_move_list returned %08x\n", Status); 778 goto end; 779 } 780 } 781 782 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 783 784 le = le->Flink; 785 } 786 787 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL); 788 789 // loop through list and create new inodes 790 791 le = move_list.Flink; 792 while (le != &move_list) { 793 me = CONTAINING_RECORD(le, move_entry, list_entry); 794 795 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE && me->fileref->fcb != fileref->fcb->Vcb->dummy_fcb) { 796 if (!me->dummyfcb) { 797 ULONG defda; 798 BOOL inserted = FALSE; 799 LIST_ENTRY* le3; 800 801 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE); 802 803 Status = duplicate_fcb(me->fileref->fcb, &me->dummyfcb); 804 if (!NT_SUCCESS(Status)) { 805 ERR("duplicate_fcb returned %08x\n", Status); 806 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 807 goto end; 808 } 809 810 me->dummyfcb->subvol = me->fileref->fcb->subvol; 811 me->dummyfcb->inode = me->fileref->fcb->inode; 812 813 if (!me->dummyfcb->ads) { 814 me->dummyfcb->sd_dirty = me->fileref->fcb->sd_dirty; 815 me->dummyfcb->atts_changed = me->fileref->fcb->atts_changed; 816 me->dummyfcb->atts_deleted = me->fileref->fcb->atts_deleted; 817 me->dummyfcb->extents_changed = me->fileref->fcb->extents_changed; 818 me->dummyfcb->reparse_xattr_changed = me->fileref->fcb->reparse_xattr_changed; 819 me->dummyfcb->ea_changed = me->fileref->fcb->ea_changed; 820 } 821 822 me->dummyfcb->created = me->fileref->fcb->created; 823 me->dummyfcb->deleted = me->fileref->fcb->deleted; 824 mark_fcb_dirty(me->dummyfcb); 825 826 if (!me->fileref->fcb->ads) { 827 LIST_ENTRY* le2; 828 829 me->fileref->fcb->subvol = destdir->fcb->subvol; 830 me->fileref->fcb->inode = InterlockedIncrement64(&destdir->fcb->subvol->lastinode); 831 me->fileref->fcb->inode_item.st_nlink = 1; 832 833 defda = get_file_attributes(me->fileref->fcb->Vcb, me->fileref->fcb->subvol, me->fileref->fcb->inode, 834 me->fileref->fcb->type, me->fileref->dc && me->fileref->dc->name.Length >= sizeof(WCHAR) && me->fileref->dc->name.Buffer[0] == '.', 835 TRUE, Irp); 836 837 me->fileref->fcb->sd_dirty = !!me->fileref->fcb->sd; 838 me->fileref->fcb->atts_changed = defda != me->fileref->fcb->atts; 839 me->fileref->fcb->extents_changed = !IsListEmpty(&me->fileref->fcb->extents); 840 me->fileref->fcb->reparse_xattr_changed = !!me->fileref->fcb->reparse_xattr.Buffer; 841 me->fileref->fcb->ea_changed = !!me->fileref->fcb->ea_xattr.Buffer; 842 me->fileref->fcb->xattrs_changed = !IsListEmpty(&me->fileref->fcb->xattrs); 843 me->fileref->fcb->inode_item_changed = TRUE; 844 845 le2 = me->fileref->fcb->xattrs.Flink; 846 while (le2 != &me->fileref->fcb->xattrs) { 847 xattr* xa = CONTAINING_RECORD(le2, xattr, list_entry); 848 849 xa->dirty = TRUE; 850 851 le2 = le2->Flink; 852 } 853 854 if (le == move_list.Flink) { // first entry 855 me->fileref->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation; 856 me->fileref->fcb->inode_item.sequence++; 857 858 if (!ccb->user_set_change_time) 859 me->fileref->fcb->inode_item.st_ctime = now; 860 } 861 862 le2 = me->fileref->fcb->extents.Flink; 863 while (le2 != &me->fileref->fcb->extents) { 864 extent* ext = CONTAINING_RECORD(le2, extent, list_entry); 865 866 if (!ext->ignore && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) { 867 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 868 869 if (ed2->size != 0) { 870 chunk* c = get_chunk_from_address(me->fileref->fcb->Vcb, ed2->address); 871 872 if (!c) { 873 ERR("get_chunk_from_address(%llx) failed\n", ed2->address); 874 } else { 875 Status = update_changed_extent_ref(me->fileref->fcb->Vcb, c, ed2->address, ed2->size, me->fileref->fcb->subvol->id, me->fileref->fcb->inode, 876 ext->offset - ed2->offset, 1, me->fileref->fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp); 877 878 if (!NT_SUCCESS(Status)) { 879 ERR("update_changed_extent_ref returned %08x\n", Status); 880 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 881 goto end; 882 } 883 } 884 885 } 886 } 887 888 le2 = le2->Flink; 889 } 890 } else { 891 me->fileref->fcb->subvol = me->parent->fileref->fcb->subvol; 892 me->fileref->fcb->inode = me->parent->fileref->fcb->inode; 893 } 894 895 me->fileref->fcb->created = TRUE; 896 897 InsertHeadList(&me->fileref->fcb->list_entry, &me->dummyfcb->list_entry); 898 RemoveEntryList(&me->fileref->fcb->list_entry); 899 900 le3 = destdir->fcb->subvol->fcbs.Flink; 901 while (le3 != &destdir->fcb->subvol->fcbs) { 902 fcb* fcb = CONTAINING_RECORD(le3, struct _fcb, list_entry); 903 904 if (fcb->inode > me->fileref->fcb->inode) { 905 InsertHeadList(le3->Blink, &me->fileref->fcb->list_entry); 906 inserted = TRUE; 907 break; 908 } 909 910 le3 = le3->Flink; 911 } 912 913 if (!inserted) 914 InsertTailList(&destdir->fcb->subvol->fcbs, &me->fileref->fcb->list_entry); 915 916 InsertTailList(&me->fileref->fcb->Vcb->all_fcbs, &me->dummyfcb->list_entry_all); 917 918 while (!IsListEmpty(&me->fileref->fcb->hardlinks)) { 919 hardlink* hl = CONTAINING_RECORD(RemoveHeadList(&me->fileref->fcb->hardlinks), hardlink, list_entry); 920 921 if (hl->name.Buffer) 922 ExFreePool(hl->name.Buffer); 923 924 if (hl->utf8.Buffer) 925 ExFreePool(hl->utf8.Buffer); 926 927 ExFreePool(hl); 928 } 929 930 me->fileref->fcb->inode_item_changed = TRUE; 931 mark_fcb_dirty(me->fileref->fcb); 932 933 if ((!me->dummyfcb->ads && me->dummyfcb->inode_item.st_nlink > 1) || (me->dummyfcb->ads && me->parent->dummyfcb->inode_item.st_nlink > 1)) { 934 LIST_ENTRY* le2 = le->Flink; 935 936 while (le2 != &move_list) { 937 move_entry* me2 = CONTAINING_RECORD(le2, move_entry, list_entry); 938 939 if (me2->fileref->fcb == me->fileref->fcb && !me2->fileref->fcb->ads) { 940 me2->dummyfcb = me->dummyfcb; 941 InterlockedIncrement(&me->dummyfcb->refcount); 942 } 943 944 le2 = le2->Flink; 945 } 946 } 947 948 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 949 } else { 950 ExAcquireResourceExclusiveLite(me->fileref->fcb->Header.Resource, TRUE); 951 me->fileref->fcb->inode_item.st_nlink++; 952 me->fileref->fcb->inode_item_changed = TRUE; 953 ExReleaseResourceLite(me->fileref->fcb->Header.Resource); 954 } 955 } 956 957 le = le->Flink; 958 } 959 960 fileref->fcb->subvol->root_item.ctransid = fileref->fcb->Vcb->superblock.generation; 961 fileref->fcb->subvol->root_item.ctime = now; 962 963 // loop through list and create new filerefs 964 965 le = move_list.Flink; 966 while (le != &move_list) { 967 hardlink* hl; 968 BOOL name_changed = FALSE; 969 970 me = CONTAINING_RECORD(le, move_entry, list_entry); 971 972 me->dummyfileref = create_fileref(fileref->fcb->Vcb); 973 if (!me->dummyfileref) { 974 ERR("out of memory\n"); 975 Status = STATUS_INSUFFICIENT_RESOURCES; 976 goto end; 977 } 978 979 if (me->fileref->fcb == me->fileref->fcb->Vcb->dummy_fcb) { 980 root* r = me->parent ? me->parent->fileref->fcb->subvol : destdir->fcb->subvol; 981 982 Status = create_directory_fcb(me->fileref->fcb->Vcb, r, me->fileref->parent->fcb, &me->fileref->fcb); 983 if (!NT_SUCCESS(Status)) { 984 ERR("create_directory_fcb returnd %08x\n", Status); 985 goto end; 986 } 987 988 me->fileref->dc->key.obj_id = me->fileref->fcb->inode; 989 me->fileref->dc->key.obj_type = TYPE_INODE_ITEM; 990 991 me->dummyfileref->fcb = me->fileref->fcb->Vcb->dummy_fcb; 992 } else if (me->fileref->fcb->inode == SUBVOL_ROOT_INODE) { 993 me->dummyfileref->fcb = me->fileref->fcb; 994 995 me->fileref->fcb->subvol->parent = le == move_list.Flink ? destdir->fcb->subvol->id : me->parent->fileref->fcb->subvol->id; 996 } else 997 me->dummyfileref->fcb = me->dummyfcb; 998 999 InterlockedIncrement(&me->dummyfileref->fcb->refcount); 1000 1001 me->dummyfileref->oldutf8 = me->fileref->oldutf8; 1002 me->dummyfileref->oldindex = me->fileref->dc->index; 1003 1004 if (le == move_list.Flink && (me->fileref->dc->utf8.Length != utf8->Length || RtlCompareMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length) != utf8->Length)) 1005 name_changed = TRUE; 1006 1007 if ((le == move_list.Flink || me->fileref->fcb->inode == SUBVOL_ROOT_INODE) && !me->dummyfileref->oldutf8.Buffer) { 1008 me->dummyfileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, me->fileref->dc->utf8.Length, ALLOC_TAG); 1009 if (!me->dummyfileref->oldutf8.Buffer) { 1010 ERR("out of memory\n"); 1011 Status = STATUS_INSUFFICIENT_RESOURCES; 1012 goto end; 1013 } 1014 1015 RtlCopyMemory(me->dummyfileref->oldutf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length); 1016 1017 me->dummyfileref->oldutf8.Length = me->dummyfileref->oldutf8.MaximumLength = me->fileref->dc->utf8.Length; 1018 } 1019 1020 me->dummyfileref->delete_on_close = me->fileref->delete_on_close; 1021 me->dummyfileref->deleted = me->fileref->deleted; 1022 1023 me->dummyfileref->created = me->fileref->created; 1024 me->fileref->created = TRUE; 1025 1026 me->dummyfileref->parent = me->parent ? me->parent->dummyfileref : origparent; 1027 increase_fileref_refcount(me->dummyfileref->parent); 1028 1029 ExAcquireResourceExclusiveLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock, TRUE); 1030 InsertTailList(&me->dummyfileref->parent->children, &me->dummyfileref->list_entry); 1031 ExReleaseResourceLite(&me->dummyfileref->parent->fcb->nonpaged->dir_children_lock); 1032 1033 me->dummyfileref->debug_desc = me->fileref->debug_desc; 1034 1035 if (me->dummyfileref->fcb->type == BTRFS_TYPE_DIRECTORY) 1036 me->dummyfileref->fcb->fileref = me->dummyfileref; 1037 1038 if (!me->parent) { 1039 RemoveEntryList(&me->fileref->list_entry); 1040 1041 increase_fileref_refcount(destdir); 1042 1043 if (me->fileref->dc) { 1044 // remove from old parent 1045 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE); 1046 RemoveEntryList(&me->fileref->dc->list_entry_index); 1047 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc); 1048 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock); 1049 1050 me->fileref->parent->fcb->inode_item.st_size -= me->fileref->dc->utf8.Length * 2; 1051 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation; 1052 me->fileref->parent->fcb->inode_item.sequence++; 1053 me->fileref->parent->fcb->inode_item.st_ctime = now; 1054 me->fileref->parent->fcb->inode_item.st_mtime = now; 1055 me->fileref->parent->fcb->inode_item_changed = TRUE; 1056 mark_fcb_dirty(me->fileref->parent->fcb); 1057 1058 if (name_changed) { 1059 ExFreePool(me->fileref->dc->utf8.Buffer); 1060 ExFreePool(me->fileref->dc->name.Buffer); 1061 ExFreePool(me->fileref->dc->name_uc.Buffer); 1062 1063 me->fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8->Length, ALLOC_TAG); 1064 if (!me->fileref->dc->utf8.Buffer) { 1065 ERR("out of memory\n"); 1066 Status = STATUS_INSUFFICIENT_RESOURCES; 1067 goto end; 1068 } 1069 1070 me->fileref->dc->utf8.Length = me->fileref->dc->utf8.MaximumLength = utf8->Length; 1071 RtlCopyMemory(me->fileref->dc->utf8.Buffer, utf8->Buffer, utf8->Length); 1072 1073 me->fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus->Length, ALLOC_TAG); 1074 if (!me->fileref->dc->name.Buffer) { 1075 ERR("out of memory\n"); 1076 Status = STATUS_INSUFFICIENT_RESOURCES; 1077 goto end; 1078 } 1079 1080 me->fileref->dc->name.Length = me->fileref->dc->name.MaximumLength = fnus->Length; 1081 RtlCopyMemory(me->fileref->dc->name.Buffer, fnus->Buffer, fnus->Length); 1082 1083 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE); 1084 if (!NT_SUCCESS(Status)) { 1085 ERR("RtlUpcaseUnicodeString returned %08x\n", Status); 1086 goto end; 1087 } 1088 1089 me->fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name.Buffer, me->fileref->dc->name.Length); 1090 me->fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)me->fileref->dc->name_uc.Buffer, me->fileref->dc->name_uc.Length); 1091 } 1092 1093 if (me->fileref->dc->key.obj_type == TYPE_INODE_ITEM) 1094 me->fileref->dc->key.obj_id = me->fileref->fcb->inode; 1095 1096 // add to new parent 1097 1098 ExAcquireResourceExclusiveLite(&destdir->fcb->nonpaged->dir_children_lock, TRUE); 1099 1100 if (IsListEmpty(&destdir->fcb->dir_children_index)) 1101 me->fileref->dc->index = 2; 1102 else { 1103 dir_child* dc2 = CONTAINING_RECORD(destdir->fcb->dir_children_index.Blink, dir_child, list_entry_index); 1104 1105 me->fileref->dc->index = max(2, dc2->index + 1); 1106 } 1107 1108 InsertTailList(&destdir->fcb->dir_children_index, &me->fileref->dc->list_entry_index); 1109 insert_dir_child_into_hash_lists(destdir->fcb, me->fileref->dc); 1110 ExReleaseResourceLite(&destdir->fcb->nonpaged->dir_children_lock); 1111 } 1112 1113 free_fileref(me->fileref->parent); 1114 me->fileref->parent = destdir; 1115 1116 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE); 1117 InsertTailList(&me->fileref->parent->children, &me->fileref->list_entry); 1118 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock); 1119 1120 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size); 1121 me->fileref->parent->fcb->inode_item.st_size += me->fileref->dc->utf8.Length * 2; 1122 TRACE("me->fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", me->fileref->parent->fcb->inode, me->fileref->parent->fcb->inode_item.st_size); 1123 me->fileref->parent->fcb->inode_item.transid = me->fileref->fcb->Vcb->superblock.generation; 1124 me->fileref->parent->fcb->inode_item.sequence++; 1125 me->fileref->parent->fcb->inode_item.st_ctime = now; 1126 me->fileref->parent->fcb->inode_item.st_mtime = now; 1127 me->fileref->parent->fcb->inode_item_changed = TRUE; 1128 mark_fcb_dirty(me->fileref->parent->fcb); 1129 } else { 1130 if (me->fileref->dc) { 1131 ExAcquireResourceExclusiveLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock, TRUE); 1132 RemoveEntryList(&me->fileref->dc->list_entry_index); 1133 1134 if (!me->fileref->fcb->ads) 1135 remove_dir_child_from_hash_lists(me->fileref->parent->fcb, me->fileref->dc); 1136 1137 ExReleaseResourceLite(&me->fileref->parent->fcb->nonpaged->dir_children_lock); 1138 1139 ExAcquireResourceExclusiveLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock, TRUE); 1140 1141 if (me->fileref->fcb->ads) 1142 InsertHeadList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index); 1143 else { 1144 if (me->fileref->fcb->inode != SUBVOL_ROOT_INODE) 1145 me->fileref->dc->key.obj_id = me->fileref->fcb->inode; 1146 1147 if (IsListEmpty(&me->parent->fileref->fcb->dir_children_index)) 1148 me->fileref->dc->index = 2; 1149 else { 1150 dir_child* dc2 = CONTAINING_RECORD(me->parent->fileref->fcb->dir_children_index.Blink, dir_child, list_entry_index); 1151 1152 me->fileref->dc->index = max(2, dc2->index + 1); 1153 } 1154 1155 InsertTailList(&me->parent->fileref->fcb->dir_children_index, &me->fileref->dc->list_entry_index); 1156 insert_dir_child_into_hash_lists(me->parent->fileref->fcb, me->fileref->dc); 1157 } 1158 1159 ExReleaseResourceLite(&me->parent->fileref->fcb->nonpaged->dir_children_lock); 1160 } 1161 } 1162 1163 if (!me->dummyfileref->fcb->ads) { 1164 Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback); 1165 if (!NT_SUCCESS(Status)) { 1166 ERR("delete_fileref returned %08x\n", Status); 1167 goto end; 1168 } 1169 } 1170 1171 if (me->fileref->fcb->inode_item.st_nlink > 1) { 1172 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 1173 if (!hl) { 1174 ERR("out of memory\n"); 1175 Status = STATUS_INSUFFICIENT_RESOURCES; 1176 goto end; 1177 } 1178 1179 hl->parent = me->fileref->parent->fcb->inode; 1180 hl->index = me->fileref->dc->index; 1181 1182 hl->utf8.Length = hl->utf8.MaximumLength = me->fileref->dc->utf8.Length; 1183 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 1184 if (!hl->utf8.Buffer) { 1185 ERR("out of memory\n"); 1186 Status = STATUS_INSUFFICIENT_RESOURCES; 1187 ExFreePool(hl); 1188 goto end; 1189 } 1190 1191 RtlCopyMemory(hl->utf8.Buffer, me->fileref->dc->utf8.Buffer, me->fileref->dc->utf8.Length); 1192 1193 hl->name.Length = hl->name.MaximumLength = me->fileref->dc->name.Length; 1194 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); 1195 if (!hl->name.Buffer) { 1196 ERR("out of memory\n"); 1197 Status = STATUS_INSUFFICIENT_RESOURCES; 1198 ExFreePool(hl->utf8.Buffer); 1199 ExFreePool(hl); 1200 goto end; 1201 } 1202 1203 RtlCopyMemory(hl->name.Buffer, me->fileref->dc->name.Buffer, me->fileref->dc->name.Length); 1204 1205 InsertTailList(&me->fileref->fcb->hardlinks, &hl->list_entry); 1206 } 1207 1208 mark_fileref_dirty(me->fileref); 1209 1210 le = le->Flink; 1211 } 1212 1213 // loop through, and only mark streams as deleted if their parent inodes are also deleted 1214 1215 le = move_list.Flink; 1216 while (le != &move_list) { 1217 me = CONTAINING_RECORD(le, move_entry, list_entry); 1218 1219 if (me->dummyfileref->fcb->ads && me->parent->dummyfileref->fcb->deleted) { 1220 Status = delete_fileref(me->dummyfileref, NULL, Irp, rollback); 1221 if (!NT_SUCCESS(Status)) { 1222 ERR("delete_fileref returned %08x\n", Status); 1223 goto end; 1224 } 1225 } 1226 1227 le = le->Flink; 1228 } 1229 1230 destdir->fcb->subvol->root_item.ctransid = destdir->fcb->Vcb->superblock.generation; 1231 destdir->fcb->subvol->root_item.ctime = now; 1232 1233 me = CONTAINING_RECORD(move_list.Flink, move_entry, list_entry); 1234 send_notification_fileref(fileref, fileref->fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); 1235 send_notification_fileref(me->dummyfileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 1236 send_notification_fileref(fileref->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 1237 1238 Status = STATUS_SUCCESS; 1239 1240 end: 1241 while (!IsListEmpty(&move_list)) { 1242 le = RemoveHeadList(&move_list); 1243 me = CONTAINING_RECORD(le, move_entry, list_entry); 1244 1245 if (me->dummyfcb) 1246 free_fcb(me->dummyfcb); 1247 1248 if (me->dummyfileref) 1249 free_fileref(me->dummyfileref); 1250 1251 free_fileref(me->fileref); 1252 1253 ExFreePool(me); 1254 } 1255 1256 destdir->fcb->subvol->fcbs_version++; 1257 fileref->fcb->subvol->fcbs_version++; 1258 1259 release_fcb_lock(fileref->fcb->Vcb); 1260 1261 return Status; 1262 } 1263 1264 void insert_dir_child_into_hash_lists(fcb* fcb, dir_child* dc) { 1265 BOOL inserted; 1266 LIST_ENTRY* le; 1267 UINT8 c, d; 1268 1269 c = dc->hash >> 24; 1270 1271 inserted = FALSE; 1272 1273 d = c; 1274 do { 1275 le = fcb->hash_ptrs[d]; 1276 1277 if (d == 0) 1278 break; 1279 1280 d--; 1281 } while (!le); 1282 1283 if (!le) 1284 le = fcb->dir_children_hash.Flink; 1285 1286 while (le != &fcb->dir_children_hash) { 1287 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash); 1288 1289 if (dc2->hash > dc->hash) { 1290 InsertHeadList(le->Blink, &dc->list_entry_hash); 1291 inserted = TRUE; 1292 break; 1293 } 1294 1295 le = le->Flink; 1296 } 1297 1298 if (!inserted) 1299 InsertTailList(&fcb->dir_children_hash, &dc->list_entry_hash); 1300 1301 if (!fcb->hash_ptrs[c]) 1302 fcb->hash_ptrs[c] = &dc->list_entry_hash; 1303 else { 1304 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs[c], dir_child, list_entry_hash); 1305 1306 if (dc2->hash > dc->hash) 1307 fcb->hash_ptrs[c] = &dc->list_entry_hash; 1308 } 1309 1310 c = dc->hash_uc >> 24; 1311 1312 inserted = FALSE; 1313 1314 d = c; 1315 do { 1316 le = fcb->hash_ptrs_uc[d]; 1317 1318 if (d == 0) 1319 break; 1320 1321 d--; 1322 } while (!le); 1323 1324 if (!le) 1325 le = fcb->dir_children_hash_uc.Flink; 1326 1327 while (le != &fcb->dir_children_hash_uc) { 1328 dir_child* dc2 = CONTAINING_RECORD(le, dir_child, list_entry_hash_uc); 1329 1330 if (dc2->hash_uc > dc->hash_uc) { 1331 InsertHeadList(le->Blink, &dc->list_entry_hash_uc); 1332 inserted = TRUE; 1333 break; 1334 } 1335 1336 le = le->Flink; 1337 } 1338 1339 if (!inserted) 1340 InsertTailList(&fcb->dir_children_hash_uc, &dc->list_entry_hash_uc); 1341 1342 if (!fcb->hash_ptrs_uc[c]) 1343 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc; 1344 else { 1345 dir_child* dc2 = CONTAINING_RECORD(fcb->hash_ptrs_uc[c], dir_child, list_entry_hash_uc); 1346 1347 if (dc2->hash_uc > dc->hash_uc) 1348 fcb->hash_ptrs_uc[c] = &dc->list_entry_hash_uc; 1349 } 1350 } 1351 1352 static NTSTATUS set_rename_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) { 1353 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 1354 FILE_RENAME_INFORMATION* fri = Irp->AssociatedIrp.SystemBuffer; 1355 fcb *fcb = FileObject->FsContext; 1356 ccb* ccb = FileObject->FsContext2; 1357 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL; 1358 WCHAR* fn; 1359 ULONG fnlen, utf8len, origutf8len; 1360 UNICODE_STRING fnus; 1361 ANSI_STRING utf8; 1362 NTSTATUS Status; 1363 LARGE_INTEGER time; 1364 BTRFS_TIME now; 1365 LIST_ENTRY rollback, *le; 1366 hardlink* hl; 1367 SECURITY_SUBJECT_CONTEXT subjcont; 1368 ACCESS_MASK access; 1369 1370 InitializeListHead(&rollback); 1371 1372 TRACE("tfo = %p\n", tfo); 1373 TRACE("ReplaceIfExists = %u\n", IrpSp->Parameters.SetFile.ReplaceIfExists); 1374 TRACE("RootDirectory = %p\n", fri->RootDirectory); 1375 TRACE("FileName = %.*S\n", fri->FileNameLength / sizeof(WCHAR), fri->FileName); 1376 1377 fn = fri->FileName; 1378 fnlen = fri->FileNameLength / sizeof(WCHAR); 1379 1380 if (!tfo) { 1381 if (!fileref || !fileref->parent) { 1382 ERR("no fileref set and no directory given\n"); 1383 return STATUS_INVALID_PARAMETER; 1384 } 1385 } else { 1386 LONG i; 1387 1388 while (fnlen > 0 && (fri->FileName[fnlen - 1] == '/' || fri->FileName[fnlen - 1] == '\\')) 1389 fnlen--; 1390 1391 if (fnlen == 0) 1392 return STATUS_INVALID_PARAMETER; 1393 1394 for (i = fnlen - 1; i >= 0; i--) { 1395 if (fri->FileName[i] == '\\' || fri->FileName[i] == '/') { 1396 fn = &fri->FileName[i+1]; 1397 fnlen = (fri->FileNameLength / sizeof(WCHAR)) - i - 1; 1398 break; 1399 } 1400 } 1401 } 1402 1403 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 1404 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE); 1405 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 1406 1407 if (fcb->ads) { 1408 // MSDN says that NTFS data streams can be renamed (https://msdn.microsoft.com/en-us/library/windows/hardware/ff540344.aspx), 1409 // but if you try it always seems to return STATUS_INVALID_PARAMETER. There is a function in ntfs.sys called NtfsStreamRename, 1410 // but it never seems to get invoked... If you know what's going on here, I'd appreciate it if you let me know. 1411 Status = STATUS_INVALID_PARAMETER; 1412 goto end; 1413 } 1414 1415 fnus.Buffer = fn; 1416 fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR)); 1417 1418 TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer); 1419 1420 origutf8len = fileref->dc->utf8.Length; 1421 1422 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 1423 if (!NT_SUCCESS(Status)) 1424 goto end; 1425 1426 utf8.MaximumLength = utf8.Length = (UINT16)utf8len; 1427 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); 1428 if (!utf8.Buffer) { 1429 ERR("out of memory\n"); 1430 Status = STATUS_INSUFFICIENT_RESOURCES; 1431 goto end; 1432 } 1433 1434 Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 1435 if (!NT_SUCCESS(Status)) 1436 goto end; 1437 1438 if (tfo && tfo->FsContext2) { 1439 struct _ccb* relatedccb = tfo->FsContext2; 1440 1441 related = relatedccb->fileref; 1442 increase_fileref_refcount(related); 1443 } else if (fnus.Length >= sizeof(WCHAR) && fnus.Buffer[0] != '\\') { 1444 related = fileref->parent; 1445 increase_fileref_refcount(related); 1446 } 1447 1448 Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 1449 1450 if (NT_SUCCESS(Status)) { 1451 TRACE("destination file %S already exists\n", file_desc_fileref(oldfileref)); 1452 1453 if (fileref != oldfileref && !oldfileref->deleted) { 1454 if (!IrpSp->Parameters.SetFile.ReplaceIfExists) { 1455 Status = STATUS_OBJECT_NAME_COLLISION; 1456 goto end; 1457 } else if ((oldfileref->open_count >= 1 || has_open_children(oldfileref)) && !oldfileref->deleted) { 1458 WARN("trying to overwrite open file\n"); 1459 Status = STATUS_ACCESS_DENIED; 1460 goto end; 1461 } 1462 1463 if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) { 1464 WARN("trying to overwrite directory\n"); 1465 Status = STATUS_ACCESS_DENIED; 1466 goto end; 1467 } 1468 } 1469 1470 if (fileref == oldfileref || oldfileref->deleted) { 1471 free_fileref(oldfileref); 1472 oldfileref = NULL; 1473 } 1474 } 1475 1476 if (!related) { 1477 Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 1478 1479 if (!NT_SUCCESS(Status)) { 1480 ERR("open_fileref returned %08x\n", Status); 1481 goto end; 1482 } 1483 } 1484 1485 if (related->fcb == Vcb->dummy_fcb) { 1486 Status = STATUS_ACCESS_DENIED; 1487 goto end; 1488 } 1489 1490 SeCaptureSubjectContext(&subjcont); 1491 1492 if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0, NULL, 1493 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 1494 SeReleaseSubjectContext(&subjcont); 1495 TRACE("SeAccessCheck failed, returning %08x\n", Status); 1496 goto end; 1497 } 1498 1499 SeReleaseSubjectContext(&subjcont); 1500 1501 if (has_open_children(fileref)) { 1502 WARN("trying to rename file with open children\n"); 1503 Status = STATUS_ACCESS_DENIED; 1504 goto end; 1505 } 1506 1507 if (oldfileref) { 1508 SeCaptureSubjectContext(&subjcont); 1509 1510 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL, 1511 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 1512 SeReleaseSubjectContext(&subjcont); 1513 TRACE("SeAccessCheck failed, returning %08x\n", Status); 1514 goto end; 1515 } 1516 1517 SeReleaseSubjectContext(&subjcont); 1518 1519 Status = delete_fileref(oldfileref, NULL, Irp, &rollback); 1520 if (!NT_SUCCESS(Status)) { 1521 ERR("delete_fileref returned %08x\n", Status); 1522 goto end; 1523 } 1524 } 1525 1526 if (fileref->parent->fcb->subvol != related->fcb->subvol && (fileref->fcb->subvol == fileref->parent->fcb->subvol || fileref->fcb == Vcb->dummy_fcb)) { 1527 Status = move_across_subvols(fileref, ccb, related, &utf8, &fnus, Irp, &rollback); 1528 if (!NT_SUCCESS(Status)) { 1529 ERR("move_across_subvols returned %08x\n", Status); 1530 } 1531 goto end; 1532 } 1533 1534 if (related == fileref->parent) { // keeping file in same directory 1535 UNICODE_STRING oldfn, newfn; 1536 USHORT name_offset; 1537 ULONG reqlen, oldutf8len; 1538 1539 oldfn.Length = oldfn.MaximumLength = 0; 1540 1541 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen); 1542 if (Status != STATUS_BUFFER_OVERFLOW) { 1543 ERR("fileref_get_filename returned %08x\n", Status); 1544 goto end; 1545 } 1546 1547 oldfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG); 1548 if (!oldfn.Buffer) { 1549 ERR("out of memory\n"); 1550 Status = STATUS_INSUFFICIENT_RESOURCES; 1551 goto end; 1552 } 1553 1554 oldfn.MaximumLength = (UINT16)reqlen; 1555 1556 Status = fileref_get_filename(fileref, &oldfn, &name_offset, &reqlen); 1557 if (!NT_SUCCESS(Status)) { 1558 ERR("fileref_get_filename returned %08x\n", Status); 1559 ExFreePool(oldfn.Buffer); 1560 goto end; 1561 } 1562 1563 oldutf8len = fileref->dc->utf8.Length; 1564 1565 if (!fileref->created && !fileref->oldutf8.Buffer) { 1566 fileref->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG); 1567 if (!fileref->oldutf8.Buffer) { 1568 ERR("out of memory\n"); 1569 Status = STATUS_INSUFFICIENT_RESOURCES; 1570 goto end; 1571 } 1572 1573 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = fileref->dc->utf8.Length; 1574 RtlCopyMemory(fileref->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 1575 } 1576 1577 TRACE("renaming %.*S to %.*S\n", fileref->dc->name.Length / sizeof(WCHAR), fileref->dc->name.Buffer, fnus.Length / sizeof(WCHAR), fnus.Buffer); 1578 1579 mark_fileref_dirty(fileref); 1580 1581 if (fileref->dc) { 1582 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE); 1583 1584 ExFreePool(fileref->dc->utf8.Buffer); 1585 ExFreePool(fileref->dc->name.Buffer); 1586 ExFreePool(fileref->dc->name_uc.Buffer); 1587 1588 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG); 1589 if (!fileref->dc->utf8.Buffer) { 1590 ERR("out of memory\n"); 1591 Status = STATUS_INSUFFICIENT_RESOURCES; 1592 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 1593 ExFreePool(oldfn.Buffer); 1594 goto end; 1595 } 1596 1597 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length; 1598 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length); 1599 1600 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); 1601 if (!fileref->dc->name.Buffer) { 1602 ERR("out of memory\n"); 1603 Status = STATUS_INSUFFICIENT_RESOURCES; 1604 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 1605 ExFreePool(oldfn.Buffer); 1606 goto end; 1607 } 1608 1609 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length; 1610 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length); 1611 1612 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE); 1613 if (!NT_SUCCESS(Status)) { 1614 ERR("RtlUpcaseUnicodeString returned %08x\n", Status); 1615 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 1616 ExFreePool(oldfn.Buffer); 1617 goto end; 1618 } 1619 1620 remove_dir_child_from_hash_lists(fileref->parent->fcb, fileref->dc); 1621 1622 fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length); 1623 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length); 1624 1625 insert_dir_child_into_hash_lists(fileref->parent->fcb, fileref->dc); 1626 1627 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 1628 } 1629 1630 newfn.Length = newfn.MaximumLength = 0; 1631 1632 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen); 1633 if (Status != STATUS_BUFFER_OVERFLOW) { 1634 ERR("fileref_get_filename returned %08x\n", Status); 1635 ExFreePool(oldfn.Buffer); 1636 goto end; 1637 } 1638 1639 newfn.Buffer = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG); 1640 if (!newfn.Buffer) { 1641 ERR("out of memory\n"); 1642 Status = STATUS_INSUFFICIENT_RESOURCES; 1643 ExFreePool(oldfn.Buffer); 1644 goto end; 1645 } 1646 1647 newfn.MaximumLength = (UINT16)reqlen; 1648 1649 Status = fileref_get_filename(fileref, &newfn, &name_offset, &reqlen); 1650 if (!NT_SUCCESS(Status)) { 1651 ERR("fileref_get_filename returned %08x\n", Status); 1652 ExFreePool(oldfn.Buffer); 1653 ExFreePool(newfn.Buffer); 1654 goto end; 1655 } 1656 1657 KeQuerySystemTime(&time); 1658 win_time_to_unix(time, &now); 1659 1660 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) { 1661 fcb->inode_item.transid = Vcb->superblock.generation; 1662 fcb->inode_item.sequence++; 1663 1664 if (!ccb->user_set_change_time) 1665 fcb->inode_item.st_ctime = now; 1666 1667 fcb->inode_item_changed = TRUE; 1668 mark_fcb_dirty(fcb); 1669 } 1670 1671 // update parent's INODE_ITEM 1672 1673 related->fcb->inode_item.transid = Vcb->superblock.generation; 1674 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size); 1675 related->fcb->inode_item.st_size = related->fcb->inode_item.st_size + (2 * utf8.Length) - (2* oldutf8len); 1676 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size); 1677 related->fcb->inode_item.sequence++; 1678 related->fcb->inode_item.st_ctime = now; 1679 related->fcb->inode_item.st_mtime = now; 1680 1681 related->fcb->inode_item_changed = TRUE; 1682 mark_fcb_dirty(related->fcb); 1683 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 1684 1685 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&oldfn, name_offset, NULL, NULL, 1686 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_OLD_NAME, NULL, NULL); 1687 FsRtlNotifyFilterReportChange(fcb->Vcb->NotifySync, &fcb->Vcb->DirNotifyList, (PSTRING)&newfn, name_offset, NULL, NULL, 1688 fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_RENAMED_NEW_NAME, NULL, NULL); 1689 1690 ExFreePool(oldfn.Buffer); 1691 ExFreePool(newfn.Buffer); 1692 1693 Status = STATUS_SUCCESS; 1694 goto end; 1695 } 1696 1697 // We move files by moving the existing fileref to the new directory, and 1698 // replacing it with a dummy fileref with the same original values, but marked as deleted. 1699 1700 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED, NULL); 1701 1702 fr2 = create_fileref(Vcb); 1703 1704 fr2->fcb = fileref->fcb; 1705 fr2->fcb->refcount++; 1706 1707 fr2->oldutf8 = fileref->oldutf8; 1708 fr2->oldindex = fileref->dc->index; 1709 fr2->delete_on_close = fileref->delete_on_close; 1710 fr2->deleted = TRUE; 1711 fr2->created = fileref->created; 1712 fr2->parent = fileref->parent; 1713 fr2->dc = NULL; 1714 1715 if (!fr2->oldutf8.Buffer) { 1716 fr2->oldutf8.Buffer = ExAllocatePoolWithTag(PagedPool, fileref->dc->utf8.Length, ALLOC_TAG); 1717 if (!fr2->oldutf8.Buffer) { 1718 ERR("out of memory\n"); 1719 Status = STATUS_INSUFFICIENT_RESOURCES; 1720 goto end; 1721 } 1722 1723 RtlCopyMemory(fr2->oldutf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 1724 1725 fr2->oldutf8.Length = fr2->oldutf8.MaximumLength = fileref->dc->utf8.Length; 1726 } 1727 1728 if (fr2->fcb->type == BTRFS_TYPE_DIRECTORY) 1729 fr2->fcb->fileref = fr2; 1730 1731 if (fileref->fcb->inode == SUBVOL_ROOT_INODE) 1732 fileref->fcb->subvol->parent = related->fcb->subvol->id; 1733 1734 fileref->oldutf8.Length = fileref->oldutf8.MaximumLength = 0; 1735 fileref->oldutf8.Buffer = NULL; 1736 fileref->deleted = FALSE; 1737 fileref->created = TRUE; 1738 fileref->parent = related; 1739 1740 ExAcquireResourceExclusiveLite(&fileref->parent->fcb->nonpaged->dir_children_lock, TRUE); 1741 InsertHeadList(&fileref->list_entry, &fr2->list_entry); 1742 RemoveEntryList(&fileref->list_entry); 1743 ExReleaseResourceLite(&fileref->parent->fcb->nonpaged->dir_children_lock); 1744 1745 mark_fileref_dirty(fr2); 1746 mark_fileref_dirty(fileref); 1747 1748 if (fileref->dc) { 1749 // remove from old parent 1750 ExAcquireResourceExclusiveLite(&fr2->parent->fcb->nonpaged->dir_children_lock, TRUE); 1751 RemoveEntryList(&fileref->dc->list_entry_index); 1752 remove_dir_child_from_hash_lists(fr2->parent->fcb, fileref->dc); 1753 ExReleaseResourceLite(&fr2->parent->fcb->nonpaged->dir_children_lock); 1754 1755 if (fileref->dc->utf8.Length != utf8.Length || RtlCompareMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length) != utf8.Length) { 1756 // handle changed name 1757 1758 ExFreePool(fileref->dc->utf8.Buffer); 1759 ExFreePool(fileref->dc->name.Buffer); 1760 ExFreePool(fileref->dc->name_uc.Buffer); 1761 1762 fileref->dc->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG); 1763 if (!fileref->dc->utf8.Buffer) { 1764 ERR("out of memory\n"); 1765 Status = STATUS_INSUFFICIENT_RESOURCES; 1766 goto end; 1767 } 1768 1769 fileref->dc->utf8.Length = fileref->dc->utf8.MaximumLength = utf8.Length; 1770 RtlCopyMemory(fileref->dc->utf8.Buffer, utf8.Buffer, utf8.Length); 1771 1772 fileref->dc->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); 1773 if (!fileref->dc->name.Buffer) { 1774 ERR("out of memory\n"); 1775 Status = STATUS_INSUFFICIENT_RESOURCES; 1776 goto end; 1777 } 1778 1779 fileref->dc->name.Length = fileref->dc->name.MaximumLength = fnus.Length; 1780 RtlCopyMemory(fileref->dc->name.Buffer, fnus.Buffer, fnus.Length); 1781 1782 Status = RtlUpcaseUnicodeString(&fileref->dc->name_uc, &fileref->dc->name, TRUE); 1783 if (!NT_SUCCESS(Status)) { 1784 ERR("RtlUpcaseUnicodeString returned %08x\n", Status); 1785 goto end; 1786 } 1787 1788 fileref->dc->hash = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name.Buffer, fileref->dc->name.Length); 1789 fileref->dc->hash_uc = calc_crc32c(0xffffffff, (UINT8*)fileref->dc->name_uc.Buffer, fileref->dc->name_uc.Length); 1790 } 1791 1792 // add to new parent 1793 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE); 1794 1795 if (IsListEmpty(&related->fcb->dir_children_index)) 1796 fileref->dc->index = 2; 1797 else { 1798 dir_child* dc2 = CONTAINING_RECORD(related->fcb->dir_children_index.Blink, dir_child, list_entry_index); 1799 1800 fileref->dc->index = max(2, dc2->index + 1); 1801 } 1802 1803 InsertTailList(&related->fcb->dir_children_index, &fileref->dc->list_entry_index); 1804 insert_dir_child_into_hash_lists(related->fcb, fileref->dc); 1805 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock); 1806 } 1807 1808 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE); 1809 InsertTailList(&related->children, &fileref->list_entry); 1810 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock); 1811 1812 if (fcb->inode_item.st_nlink > 1) { 1813 // add new hardlink entry to fcb 1814 1815 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 1816 if (!hl) { 1817 ERR("out of memory\n"); 1818 Status = STATUS_INSUFFICIENT_RESOURCES; 1819 goto end; 1820 } 1821 1822 hl->parent = related->fcb->inode; 1823 hl->index = fileref->dc->index; 1824 1825 hl->name.Length = hl->name.MaximumLength = fnus.Length; 1826 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); 1827 1828 if (!hl->name.Buffer) { 1829 ERR("out of memory\n"); 1830 ExFreePool(hl); 1831 Status = STATUS_INSUFFICIENT_RESOURCES; 1832 goto end; 1833 } 1834 1835 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); 1836 1837 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length; 1838 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 1839 1840 if (!hl->utf8.Buffer) { 1841 ERR("out of memory\n"); 1842 ExFreePool(hl->name.Buffer); 1843 ExFreePool(hl); 1844 Status = STATUS_INSUFFICIENT_RESOURCES; 1845 goto end; 1846 } 1847 1848 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 1849 1850 InsertTailList(&fcb->hardlinks, &hl->list_entry); 1851 } 1852 1853 // delete old hardlink entry from fcb 1854 1855 le = fcb->hardlinks.Flink; 1856 while (le != &fcb->hardlinks) { 1857 hl = CONTAINING_RECORD(le, hardlink, list_entry); 1858 1859 if (hl->parent == fr2->parent->fcb->inode && hl->index == fr2->oldindex) { 1860 RemoveEntryList(&hl->list_entry); 1861 1862 if (hl->utf8.Buffer) 1863 ExFreePool(hl->utf8.Buffer); 1864 1865 if (hl->name.Buffer) 1866 ExFreePool(hl->name.Buffer); 1867 1868 ExFreePool(hl); 1869 break; 1870 } 1871 1872 le = le->Flink; 1873 } 1874 1875 // update inode's INODE_ITEM 1876 1877 KeQuerySystemTime(&time); 1878 win_time_to_unix(time, &now); 1879 1880 if (fcb != Vcb->dummy_fcb && (fileref->parent->fcb->subvol == fcb->subvol || !is_subvol_readonly(fcb->subvol, Irp))) { 1881 fcb->inode_item.transid = Vcb->superblock.generation; 1882 fcb->inode_item.sequence++; 1883 1884 if (!ccb->user_set_change_time) 1885 fcb->inode_item.st_ctime = now; 1886 1887 fcb->inode_item_changed = TRUE; 1888 mark_fcb_dirty(fcb); 1889 } 1890 1891 // update new parent's INODE_ITEM 1892 1893 related->fcb->inode_item.transid = Vcb->superblock.generation; 1894 TRACE("related->fcb->inode_item.st_size (inode %llx) was %llx\n", related->fcb->inode, related->fcb->inode_item.st_size); 1895 related->fcb->inode_item.st_size += 2 * utf8len; 1896 TRACE("related->fcb->inode_item.st_size (inode %llx) now %llx\n", related->fcb->inode, related->fcb->inode_item.st_size); 1897 related->fcb->inode_item.sequence++; 1898 related->fcb->inode_item.st_ctime = now; 1899 related->fcb->inode_item.st_mtime = now; 1900 1901 related->fcb->inode_item_changed = TRUE; 1902 mark_fcb_dirty(related->fcb); 1903 1904 // update old parent's INODE_ITEM 1905 1906 fr2->parent->fcb->inode_item.transid = Vcb->superblock.generation; 1907 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size); 1908 fr2->parent->fcb->inode_item.st_size -= 2 * origutf8len; 1909 TRACE("fr2->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fr2->parent->fcb->inode, fr2->parent->fcb->inode_item.st_size); 1910 fr2->parent->fcb->inode_item.sequence++; 1911 fr2->parent->fcb->inode_item.st_ctime = now; 1912 fr2->parent->fcb->inode_item.st_mtime = now; 1913 1914 free_fileref(fr2); 1915 1916 fr2->parent->fcb->inode_item_changed = TRUE; 1917 mark_fcb_dirty(fr2->parent->fcb); 1918 1919 send_notification_fileref(fileref, fcb->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); 1920 send_notification_fileref(related, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 1921 send_notification_fileref(fr2->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 1922 1923 Status = STATUS_SUCCESS; 1924 1925 end: 1926 if (oldfileref) 1927 free_fileref(oldfileref); 1928 1929 if (!NT_SUCCESS(Status) && related) 1930 free_fileref(related); 1931 1932 if (!NT_SUCCESS(Status) && fr2) 1933 free_fileref(fr2); 1934 1935 if (NT_SUCCESS(Status)) 1936 clear_rollback(&rollback); 1937 else 1938 do_rollback(Vcb, &rollback); 1939 1940 ExReleaseResourceLite(fcb->Header.Resource); 1941 ExReleaseResourceLite(&Vcb->fileref_lock); 1942 ExReleaseResourceLite(&Vcb->tree_lock); 1943 1944 return Status; 1945 } 1946 1947 NTSTATUS stream_set_end_of_file_information(device_extension* Vcb, UINT16 end, fcb* fcb, file_ref* fileref, BOOL advance_only) { 1948 LARGE_INTEGER time; 1949 BTRFS_TIME now; 1950 1951 TRACE("setting new end to %llx bytes (currently %x)\n", end, fcb->adsdata.Length); 1952 1953 if (!fileref || !fileref->parent) { 1954 ERR("no fileref for stream\n"); 1955 return STATUS_INTERNAL_ERROR; 1956 } 1957 1958 if (end < fcb->adsdata.Length) { 1959 if (advance_only) 1960 return STATUS_SUCCESS; 1961 1962 TRACE("truncating stream to %llx bytes\n", end); 1963 1964 fcb->adsdata.Length = end; 1965 } else if (end > fcb->adsdata.Length) { 1966 TRACE("extending stream to %llx bytes\n", end); 1967 1968 if (end > fcb->adsmaxlen) { 1969 ERR("error - xattr too long (%u > %u)\n", end, fcb->adsmaxlen); 1970 return STATUS_DISK_FULL; 1971 } 1972 1973 if (end > fcb->adsdata.MaximumLength) { 1974 char* data = ExAllocatePoolWithTag(PagedPool, end, ALLOC_TAG); 1975 if (!data) { 1976 ERR("out of memory\n"); 1977 ExFreePool(data); 1978 return STATUS_INSUFFICIENT_RESOURCES; 1979 } 1980 1981 if (fcb->adsdata.Buffer) { 1982 RtlCopyMemory(data, fcb->adsdata.Buffer, fcb->adsdata.Length); 1983 ExFreePool(fcb->adsdata.Buffer); 1984 } 1985 1986 fcb->adsdata.Buffer = data; 1987 fcb->adsdata.MaximumLength = end; 1988 } 1989 1990 RtlZeroMemory(&fcb->adsdata.Buffer[fcb->adsdata.Length], end - fcb->adsdata.Length); 1991 1992 fcb->adsdata.Length = end; 1993 } 1994 1995 mark_fcb_dirty(fcb); 1996 1997 fcb->Header.AllocationSize.QuadPart = end; 1998 fcb->Header.FileSize.QuadPart = end; 1999 fcb->Header.ValidDataLength.QuadPart = end; 2000 2001 KeQuerySystemTime(&time); 2002 win_time_to_unix(time, &now); 2003 2004 fileref->parent->fcb->inode_item.transid = Vcb->superblock.generation; 2005 fileref->parent->fcb->inode_item.sequence++; 2006 fileref->parent->fcb->inode_item.st_ctime = now; 2007 2008 fileref->parent->fcb->inode_item_changed = TRUE; 2009 mark_fcb_dirty(fileref->parent->fcb); 2010 2011 fileref->parent->fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 2012 fileref->parent->fcb->subvol->root_item.ctime = now; 2013 2014 return STATUS_SUCCESS; 2015 } 2016 2017 static NTSTATUS set_end_of_file_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, BOOL advance_only, BOOL prealloc) { 2018 FILE_END_OF_FILE_INFORMATION* feofi = Irp->AssociatedIrp.SystemBuffer; 2019 fcb* fcb = FileObject->FsContext; 2020 ccb* ccb = FileObject->FsContext2; 2021 file_ref* fileref = ccb ? ccb->fileref : NULL; 2022 NTSTATUS Status; 2023 LARGE_INTEGER time; 2024 CC_FILE_SIZES ccfs; 2025 LIST_ENTRY rollback; 2026 BOOL set_size = FALSE; 2027 ULONG filter; 2028 2029 if (!fileref) { 2030 ERR("fileref is NULL\n"); 2031 return STATUS_INVALID_PARAMETER; 2032 } 2033 2034 InitializeListHead(&rollback); 2035 2036 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 2037 2038 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 2039 2040 if (fileref ? fileref->deleted : fcb->deleted) { 2041 Status = STATUS_FILE_CLOSED; 2042 goto end; 2043 } 2044 2045 if (fcb->ads) { 2046 if (feofi->EndOfFile.QuadPart > 0xffff) { 2047 Status = STATUS_DISK_FULL; 2048 goto end; 2049 } 2050 2051 if (feofi->EndOfFile.QuadPart < 0) { 2052 Status = STATUS_INVALID_PARAMETER; 2053 goto end; 2054 } 2055 2056 Status = stream_set_end_of_file_information(Vcb, (UINT16)feofi->EndOfFile.QuadPart, fcb, fileref, advance_only); 2057 2058 if (NT_SUCCESS(Status)) { 2059 ccfs.AllocationSize = fcb->Header.AllocationSize; 2060 ccfs.FileSize = fcb->Header.FileSize; 2061 ccfs.ValidDataLength = fcb->Header.ValidDataLength; 2062 set_size = TRUE; 2063 } 2064 2065 filter = FILE_NOTIFY_CHANGE_STREAM_SIZE; 2066 2067 if (!ccb->user_set_write_time) { 2068 KeQuerySystemTime(&time); 2069 win_time_to_unix(time, &fileref->parent->fcb->inode_item.st_mtime); 2070 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 2071 2072 fileref->parent->fcb->inode_item_changed = TRUE; 2073 mark_fcb_dirty(fileref->parent->fcb); 2074 } 2075 2076 send_notification_fcb(fileref->parent, filter, FILE_ACTION_MODIFIED_STREAM, &fileref->dc->name); 2077 2078 goto end; 2079 } 2080 2081 TRACE("file: %S\n", file_desc(FileObject)); 2082 TRACE("paging IO: %s\n", Irp->Flags & IRP_PAGING_IO ? "TRUE" : "FALSE"); 2083 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n", 2084 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart); 2085 2086 TRACE("setting new end to %llx bytes (currently %llx)\n", feofi->EndOfFile.QuadPart, fcb->inode_item.st_size); 2087 2088 if ((UINT64)feofi->EndOfFile.QuadPart < fcb->inode_item.st_size) { 2089 if (advance_only) { 2090 Status = STATUS_SUCCESS; 2091 goto end; 2092 } 2093 2094 TRACE("truncating file to %llx bytes\n", feofi->EndOfFile.QuadPart); 2095 2096 if (!MmCanFileBeTruncated(&fcb->nonpaged->segment_object, &feofi->EndOfFile)) { 2097 Status = STATUS_USER_MAPPED_FILE; 2098 goto end; 2099 } 2100 2101 Status = truncate_file(fcb, feofi->EndOfFile.QuadPart, Irp, &rollback); 2102 if (!NT_SUCCESS(Status)) { 2103 ERR("error - truncate_file failed\n"); 2104 goto end; 2105 } 2106 } else if ((UINT64)feofi->EndOfFile.QuadPart > fcb->inode_item.st_size) { 2107 if (Irp->Flags & IRP_PAGING_IO) { 2108 TRACE("paging IO tried to extend file size\n"); 2109 Status = STATUS_SUCCESS; 2110 goto end; 2111 } 2112 2113 TRACE("extending file to %llx bytes\n", feofi->EndOfFile.QuadPart); 2114 2115 Status = extend_file(fcb, fileref, feofi->EndOfFile.QuadPart, prealloc, NULL, &rollback); 2116 if (!NT_SUCCESS(Status)) { 2117 ERR("error - extend_file failed\n"); 2118 goto end; 2119 } 2120 } else if ((UINT64)feofi->EndOfFile.QuadPart == fcb->inode_item.st_size && advance_only) { 2121 Status = STATUS_SUCCESS; 2122 goto end; 2123 } 2124 2125 ccfs.AllocationSize = fcb->Header.AllocationSize; 2126 ccfs.FileSize = fcb->Header.FileSize; 2127 ccfs.ValidDataLength = fcb->Header.ValidDataLength; 2128 set_size = TRUE; 2129 2130 filter = FILE_NOTIFY_CHANGE_SIZE; 2131 2132 if (!ccb->user_set_write_time) { 2133 KeQuerySystemTime(&time); 2134 win_time_to_unix(time, &fcb->inode_item.st_mtime); 2135 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 2136 } 2137 2138 fcb->inode_item_changed = TRUE; 2139 mark_fcb_dirty(fcb); 2140 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); 2141 2142 Status = STATUS_SUCCESS; 2143 2144 end: 2145 if (NT_SUCCESS(Status)) 2146 clear_rollback(&rollback); 2147 else 2148 do_rollback(Vcb, &rollback); 2149 2150 ExReleaseResourceLite(fcb->Header.Resource); 2151 2152 if (set_size) { 2153 _SEH2_TRY { 2154 CcSetFileSizes(FileObject, &ccfs); 2155 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 2156 Status = _SEH2_GetExceptionCode(); 2157 } _SEH2_END; 2158 2159 if (!NT_SUCCESS(Status)) 2160 ERR("CcSetFileSizes threw exception %08x\n", Status); 2161 } 2162 2163 ExReleaseResourceLite(&Vcb->tree_lock); 2164 2165 return Status; 2166 } 2167 2168 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) { 2169 FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; 2170 2171 TRACE("setting the position on %S to %llx\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart); 2172 2173 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING 2174 2175 FileObject->CurrentByteOffset = fpi->CurrentByteOffset; 2176 2177 return STATUS_SUCCESS; 2178 } 2179 2180 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) { 2181 FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer; 2182 fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb; 2183 ccb* ccb = FileObject->FsContext2; 2184 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL; 2185 WCHAR* fn; 2186 ULONG fnlen, utf8len; 2187 UNICODE_STRING fnus; 2188 ANSI_STRING utf8; 2189 NTSTATUS Status; 2190 LARGE_INTEGER time; 2191 BTRFS_TIME now; 2192 LIST_ENTRY rollback; 2193 hardlink* hl; 2194 ACCESS_MASK access; 2195 SECURITY_SUBJECT_CONTEXT subjcont; 2196 dir_child* dc = NULL; 2197 2198 InitializeListHead(&rollback); 2199 2200 // FIXME - check fli length 2201 // FIXME - don't ignore fli->RootDirectory 2202 2203 TRACE("ReplaceIfExists = %x\n", fli->ReplaceIfExists); 2204 TRACE("RootDirectory = %p\n", fli->RootDirectory); 2205 TRACE("FileNameLength = %x\n", fli->FileNameLength); 2206 TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName); 2207 2208 fn = fli->FileName; 2209 fnlen = fli->FileNameLength / sizeof(WCHAR); 2210 2211 if (!tfo) { 2212 if (!fileref || !fileref->parent) { 2213 ERR("no fileref set and no directory given\n"); 2214 return STATUS_INVALID_PARAMETER; 2215 } 2216 2217 parfcb = fileref->parent->fcb; 2218 tfofcb = NULL; 2219 } else { 2220 LONG i; 2221 2222 tfofcb = tfo->FsContext; 2223 parfcb = tfofcb; 2224 2225 while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\')) 2226 fnlen--; 2227 2228 if (fnlen == 0) 2229 return STATUS_INVALID_PARAMETER; 2230 2231 for (i = fnlen - 1; i >= 0; i--) { 2232 if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') { 2233 fn = &fli->FileName[i+1]; 2234 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1; 2235 break; 2236 } 2237 } 2238 } 2239 2240 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 2241 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, TRUE); 2242 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 2243 2244 if (fcb->type == BTRFS_TYPE_DIRECTORY) { 2245 WARN("tried to create hard link on directory\n"); 2246 Status = STATUS_FILE_IS_A_DIRECTORY; 2247 goto end; 2248 } 2249 2250 if (fcb->ads) { 2251 WARN("tried to create hard link on stream\n"); 2252 Status = STATUS_INVALID_PARAMETER; 2253 goto end; 2254 } 2255 2256 if (fcb->inode_item.st_nlink >= 65535) { 2257 Status = STATUS_TOO_MANY_LINKS; 2258 goto end; 2259 } 2260 2261 fnus.Buffer = fn; 2262 fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR)); 2263 2264 TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer); 2265 2266 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 2267 if (!NT_SUCCESS(Status)) 2268 goto end; 2269 2270 utf8.MaximumLength = utf8.Length = (UINT16)utf8len; 2271 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); 2272 if (!utf8.Buffer) { 2273 ERR("out of memory\n"); 2274 Status = STATUS_INSUFFICIENT_RESOURCES; 2275 goto end; 2276 } 2277 2278 Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 2279 if (!NT_SUCCESS(Status)) 2280 goto end; 2281 2282 if (tfo && tfo->FsContext2) { 2283 struct _ccb* relatedccb = tfo->FsContext2; 2284 2285 related = relatedccb->fileref; 2286 increase_fileref_refcount(related); 2287 } 2288 2289 Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 2290 2291 if (NT_SUCCESS(Status)) { 2292 if (!oldfileref->deleted) { 2293 WARN("destination file %S already exists\n", file_desc_fileref(oldfileref)); 2294 2295 if (!fli->ReplaceIfExists) { 2296 Status = STATUS_OBJECT_NAME_COLLISION; 2297 goto end; 2298 } else if (oldfileref->open_count >= 1 && !oldfileref->deleted) { 2299 WARN("trying to overwrite open file\n"); 2300 Status = STATUS_ACCESS_DENIED; 2301 goto end; 2302 } else if (fileref == oldfileref) { 2303 Status = STATUS_ACCESS_DENIED; 2304 goto end; 2305 } 2306 2307 if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) { 2308 WARN("trying to overwrite directory\n"); 2309 Status = STATUS_ACCESS_DENIED; 2310 goto end; 2311 } 2312 } else { 2313 free_fileref(oldfileref); 2314 oldfileref = NULL; 2315 } 2316 } 2317 2318 if (!related) { 2319 Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 2320 2321 if (!NT_SUCCESS(Status)) { 2322 ERR("open_fileref returned %08x\n", Status); 2323 goto end; 2324 } 2325 } 2326 2327 SeCaptureSubjectContext(&subjcont); 2328 2329 if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, FILE_ADD_FILE, 0, NULL, 2330 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 2331 SeReleaseSubjectContext(&subjcont); 2332 TRACE("SeAccessCheck failed, returning %08x\n", Status); 2333 goto end; 2334 } 2335 2336 SeReleaseSubjectContext(&subjcont); 2337 2338 if (fcb->subvol != parfcb->subvol) { 2339 WARN("can't create hard link over subvolume boundary\n"); 2340 Status = STATUS_INVALID_PARAMETER; 2341 goto end; 2342 } 2343 2344 if (oldfileref) { 2345 SeCaptureSubjectContext(&subjcont); 2346 2347 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL, 2348 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 2349 SeReleaseSubjectContext(&subjcont); 2350 TRACE("SeAccessCheck failed, returning %08x\n", Status); 2351 goto end; 2352 } 2353 2354 SeReleaseSubjectContext(&subjcont); 2355 2356 Status = delete_fileref(oldfileref, NULL, Irp, &rollback); 2357 if (!NT_SUCCESS(Status)) { 2358 ERR("delete_fileref returned %08x\n", Status); 2359 goto end; 2360 } 2361 } 2362 2363 fr2 = create_fileref(Vcb); 2364 2365 fr2->fcb = fcb; 2366 fcb->refcount++; 2367 2368 fr2->created = TRUE; 2369 fr2->parent = related; 2370 2371 Status = add_dir_child(related->fcb, fcb->inode, FALSE, &utf8, &fnus, fcb->type, &dc); 2372 if (!NT_SUCCESS(Status)) 2373 WARN("add_dir_child returned %08x\n", Status); 2374 2375 fr2->dc = dc; 2376 dc->fileref = fr2; 2377 2378 ExAcquireResourceExclusiveLite(&related->fcb->nonpaged->dir_children_lock, TRUE); 2379 InsertTailList(&related->children, &fr2->list_entry); 2380 ExReleaseResourceLite(&related->fcb->nonpaged->dir_children_lock); 2381 2382 // add hardlink for existing fileref, if it's not there already 2383 if (IsListEmpty(&fcb->hardlinks)) { 2384 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 2385 if (!hl) { 2386 ERR("out of memory\n"); 2387 Status = STATUS_INSUFFICIENT_RESOURCES; 2388 goto end; 2389 } 2390 2391 hl->parent = fileref->parent->fcb->inode; 2392 hl->index = fileref->dc->index; 2393 2394 hl->name.Length = hl->name.MaximumLength = fnus.Length; 2395 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); 2396 2397 if (!hl->name.Buffer) { 2398 ERR("out of memory\n"); 2399 ExFreePool(hl); 2400 Status = STATUS_INSUFFICIENT_RESOURCES; 2401 goto end; 2402 } 2403 2404 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); 2405 2406 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length; 2407 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 2408 2409 if (!hl->utf8.Buffer) { 2410 ERR("out of memory\n"); 2411 ExFreePool(hl->name.Buffer); 2412 ExFreePool(hl); 2413 Status = STATUS_INSUFFICIENT_RESOURCES; 2414 goto end; 2415 } 2416 2417 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 2418 2419 InsertTailList(&fcb->hardlinks, &hl->list_entry); 2420 } 2421 2422 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 2423 if (!hl) { 2424 ERR("out of memory\n"); 2425 Status = STATUS_INSUFFICIENT_RESOURCES; 2426 goto end; 2427 } 2428 2429 hl->parent = related->fcb->inode; 2430 hl->index = dc->index; 2431 2432 hl->name.Length = hl->name.MaximumLength = fnus.Length; 2433 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); 2434 2435 if (!hl->name.Buffer) { 2436 ERR("out of memory\n"); 2437 ExFreePool(hl); 2438 Status = STATUS_INSUFFICIENT_RESOURCES; 2439 goto end; 2440 } 2441 2442 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); 2443 2444 hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length; 2445 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 2446 2447 if (!hl->utf8.Buffer) { 2448 ERR("out of memory\n"); 2449 ExFreePool(hl->name.Buffer); 2450 ExFreePool(hl); 2451 Status = STATUS_INSUFFICIENT_RESOURCES; 2452 goto end; 2453 } 2454 2455 RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length); 2456 ExFreePool(utf8.Buffer); 2457 2458 InsertTailList(&fcb->hardlinks, &hl->list_entry); 2459 2460 mark_fileref_dirty(fr2); 2461 free_fileref(fr2); 2462 2463 // update inode's INODE_ITEM 2464 2465 KeQuerySystemTime(&time); 2466 win_time_to_unix(time, &now); 2467 2468 fcb->inode_item.transid = Vcb->superblock.generation; 2469 fcb->inode_item.sequence++; 2470 fcb->inode_item.st_nlink++; 2471 2472 if (!ccb->user_set_change_time) 2473 fcb->inode_item.st_ctime = now; 2474 2475 fcb->inode_item_changed = TRUE; 2476 mark_fcb_dirty(fcb); 2477 2478 // update parent's INODE_ITEM 2479 2480 parfcb->inode_item.transid = Vcb->superblock.generation; 2481 TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb->inode, parfcb->inode_item.st_size); 2482 parfcb->inode_item.st_size += 2 * utf8len; 2483 TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb->inode, parfcb->inode_item.st_size); 2484 parfcb->inode_item.sequence++; 2485 parfcb->inode_item.st_ctime = now; 2486 2487 parfcb->inode_item_changed = TRUE; 2488 mark_fcb_dirty(parfcb); 2489 2490 send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); 2491 2492 Status = STATUS_SUCCESS; 2493 2494 end: 2495 if (oldfileref) 2496 free_fileref(oldfileref); 2497 2498 if (!NT_SUCCESS(Status) && related) 2499 free_fileref(related); 2500 2501 if (!NT_SUCCESS(Status) && fr2) 2502 free_fileref(fr2); 2503 2504 if (NT_SUCCESS(Status)) 2505 clear_rollback(&rollback); 2506 else 2507 do_rollback(Vcb, &rollback); 2508 2509 ExReleaseResourceLite(fcb->Header.Resource); 2510 ExReleaseResourceLite(&Vcb->fileref_lock); 2511 ExReleaseResourceLite(&Vcb->tree_lock); 2512 2513 return Status; 2514 } 2515 2516 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { 2517 FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer; 2518 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2519 fcb* fcb = FileObject->FsContext; 2520 ccb* ccb = FileObject->FsContext2; 2521 file_ref* fileref = ccb ? ccb->fileref : NULL; 2522 NTSTATUS Status; 2523 LARGE_INTEGER time; 2524 CC_FILE_SIZES ccfs; 2525 LIST_ENTRY rollback; 2526 BOOL set_size = FALSE; 2527 ULONG filter; 2528 2529 if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) { 2530 ERR("input buffer length was %u, expected %u\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)); 2531 return STATUS_INVALID_PARAMETER; 2532 } 2533 2534 if (!fileref) { 2535 ERR("fileref is NULL\n"); 2536 return STATUS_INVALID_PARAMETER; 2537 } 2538 2539 InitializeListHead(&rollback); 2540 2541 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 2542 2543 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 2544 2545 if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) { 2546 Status = STATUS_INVALID_PARAMETER; 2547 goto end; 2548 } 2549 2550 if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) { 2551 TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli->ValidDataLength.QuadPart, 2552 fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart); 2553 Status = STATUS_INVALID_PARAMETER; 2554 goto end; 2555 } 2556 2557 if (fileref ? fileref->deleted : fcb->deleted) { 2558 Status = STATUS_FILE_CLOSED; 2559 goto end; 2560 } 2561 2562 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength, 2563 // and we set it to the max anyway. 2564 2565 ccfs.AllocationSize = fcb->Header.AllocationSize; 2566 ccfs.FileSize = fcb->Header.FileSize; 2567 ccfs.ValidDataLength = fvdli->ValidDataLength; 2568 set_size = TRUE; 2569 2570 filter = FILE_NOTIFY_CHANGE_SIZE; 2571 2572 if (!ccb->user_set_write_time) { 2573 KeQuerySystemTime(&time); 2574 win_time_to_unix(time, &fcb->inode_item.st_mtime); 2575 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 2576 } 2577 2578 fcb->inode_item_changed = TRUE; 2579 mark_fcb_dirty(fcb); 2580 2581 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); 2582 2583 Status = STATUS_SUCCESS; 2584 2585 end: 2586 if (NT_SUCCESS(Status)) 2587 clear_rollback(&rollback); 2588 else 2589 do_rollback(Vcb, &rollback); 2590 2591 ExReleaseResourceLite(fcb->Header.Resource); 2592 2593 if (set_size) { 2594 _SEH2_TRY { 2595 CcSetFileSizes(FileObject, &ccfs); 2596 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 2597 Status = _SEH2_GetExceptionCode(); 2598 } _SEH2_END; 2599 2600 if (!NT_SUCCESS(Status)) 2601 ERR("CcSetFileSizes threw exception %08x\n", Status); 2602 else 2603 fcb->Header.AllocationSize = ccfs.AllocationSize; 2604 } 2605 2606 ExReleaseResourceLite(&Vcb->tree_lock); 2607 2608 return Status; 2609 } 2610 2611 _Dispatch_type_(IRP_MJ_SET_INFORMATION) 2612 _Function_class_(DRIVER_DISPATCH) 2613 NTSTATUS NTAPI drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 2614 NTSTATUS Status; 2615 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2616 device_extension* Vcb = DeviceObject->DeviceExtension; 2617 fcb* fcb = IrpSp->FileObject->FsContext; 2618 ccb* ccb = IrpSp->FileObject->FsContext2; 2619 BOOL top_level; 2620 2621 FsRtlEnterFileSystem(); 2622 2623 top_level = is_top_level(Irp); 2624 2625 Irp->IoStatus.Information = 0; 2626 2627 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 2628 Status = vol_set_information(DeviceObject, Irp); 2629 goto end; 2630 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 2631 Status = STATUS_INVALID_PARAMETER; 2632 goto end; 2633 } 2634 2635 if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) { 2636 Status = STATUS_ACCESS_DENIED; 2637 goto end; 2638 } 2639 2640 if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) { 2641 Status = STATUS_MEDIA_WRITE_PROTECTED; 2642 goto end; 2643 } 2644 2645 if (!fcb) { 2646 ERR("no fcb\n"); 2647 Status = STATUS_INVALID_PARAMETER; 2648 goto end; 2649 } 2650 2651 if (!ccb) { 2652 ERR("no ccb\n"); 2653 Status = STATUS_INVALID_PARAMETER; 2654 goto end; 2655 } 2656 2657 if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation && 2658 (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) { 2659 Status = STATUS_ACCESS_DENIED; 2660 goto end; 2661 } 2662 2663 Status = STATUS_NOT_IMPLEMENTED; 2664 2665 TRACE("set information\n"); 2666 2667 switch (IrpSp->Parameters.SetFile.FileInformationClass) { 2668 case FileAllocationInformation: 2669 { 2670 TRACE("FileAllocationInformation\n"); 2671 2672 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) { 2673 WARN("insufficient privileges\n"); 2674 Status = STATUS_ACCESS_DENIED; 2675 break; 2676 } 2677 2678 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, TRUE); 2679 break; 2680 } 2681 2682 case FileBasicInformation: 2683 { 2684 TRACE("FileBasicInformation\n"); 2685 2686 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { 2687 WARN("insufficient privileges\n"); 2688 Status = STATUS_ACCESS_DENIED; 2689 break; 2690 } 2691 2692 Status = set_basic_information(Vcb, Irp, IrpSp->FileObject); 2693 2694 break; 2695 } 2696 2697 case FileDispositionInformation: 2698 { 2699 TRACE("FileDispositionInformation\n"); 2700 2701 if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) { 2702 WARN("insufficient privileges\n"); 2703 Status = STATUS_ACCESS_DENIED; 2704 break; 2705 } 2706 2707 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject); 2708 2709 break; 2710 } 2711 2712 case FileEndOfFileInformation: 2713 { 2714 TRACE("FileEndOfFileInformation\n"); 2715 2716 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { 2717 WARN("insufficient privileges\n"); 2718 Status = STATUS_ACCESS_DENIED; 2719 break; 2720 } 2721 2722 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, FALSE); 2723 2724 break; 2725 } 2726 2727 case FileLinkInformation: 2728 TRACE("FileLinkInformation\n"); 2729 Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject); 2730 break; 2731 2732 case FilePositionInformation: 2733 TRACE("FilePositionInformation\n"); 2734 Status = set_position_information(IrpSp->FileObject, Irp); 2735 break; 2736 2737 case FileRenameInformation: 2738 TRACE("FileRenameInformation\n"); 2739 // FIXME - make this work with streams 2740 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject); 2741 break; 2742 2743 case FileValidDataLengthInformation: 2744 { 2745 TRACE("FileValidDataLengthInformation\n"); 2746 2747 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { 2748 WARN("insufficient privileges\n"); 2749 Status = STATUS_ACCESS_DENIED; 2750 break; 2751 } 2752 2753 Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject); 2754 2755 break; 2756 } 2757 2758 default: 2759 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass); 2760 } 2761 2762 end: 2763 Irp->IoStatus.Status = Status; 2764 2765 TRACE("returning %08x\n", Status); 2766 2767 IoCompleteRequest(Irp, IO_NO_INCREMENT); 2768 2769 if (top_level) 2770 IoSetTopLevelIrp(NULL); 2771 2772 FsRtlExitFileSystem(); 2773 2774 return Status; 2775 } 2776 2777 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) { 2778 RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION)); 2779 2780 *length -= sizeof(FILE_BASIC_INFORMATION); 2781 2782 if (fcb == fcb->Vcb->dummy_fcb) { 2783 LARGE_INTEGER time; 2784 2785 KeQuerySystemTime(&time); 2786 fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time; 2787 } else { 2788 fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 2789 fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 2790 fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 2791 fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 2792 } 2793 2794 if (fcb->ads) { 2795 if (!fileref || !fileref->parent) { 2796 ERR("no fileref for stream\n"); 2797 return STATUS_INTERNAL_ERROR; 2798 } else 2799 fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts; 2800 } else 2801 fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 2802 2803 return STATUS_SUCCESS; 2804 } 2805 2806 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) { 2807 INODE_ITEM* ii; 2808 2809 if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) { 2810 WARN("overflow\n"); 2811 return STATUS_BUFFER_OVERFLOW; 2812 } 2813 2814 RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION)); 2815 2816 *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION); 2817 2818 if (fcb->ads) { 2819 if (!fileref || !fileref->parent) { 2820 ERR("no fileref for stream\n"); 2821 return STATUS_INTERNAL_ERROR; 2822 } 2823 2824 ii = &fileref->parent->fcb->inode_item; 2825 } else 2826 ii = &fcb->inode_item; 2827 2828 if (fcb == fcb->Vcb->dummy_fcb) { 2829 LARGE_INTEGER time; 2830 2831 KeQuerySystemTime(&time); 2832 fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time; 2833 } else { 2834 fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 2835 fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 2836 fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 2837 fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 2838 } 2839 2840 if (fcb->ads) { 2841 fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length; 2842 fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts; 2843 } else { 2844 fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 2845 fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 2846 fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 2847 } 2848 2849 return STATUS_SUCCESS; 2850 } 2851 2852 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) { 2853 RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION)); 2854 2855 *length -= sizeof(FILE_STANDARD_INFORMATION); 2856 2857 if (fcb->ads) { 2858 if (!fileref || !fileref->parent) { 2859 ERR("no fileref for stream\n"); 2860 return STATUS_INTERNAL_ERROR; 2861 } 2862 2863 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length; 2864 fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink; 2865 fsi->Directory = FALSE; 2866 } else { 2867 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 2868 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 2869 fsi->NumberOfLinks = fcb->inode_item.st_nlink; 2870 fsi->Directory = S_ISDIR(fcb->inode_item.st_mode); 2871 } 2872 2873 TRACE("length = %llu\n", fsi->EndOfFile.QuadPart); 2874 2875 fsi->DeletePending = fileref ? fileref->delete_on_close : FALSE; 2876 2877 return STATUS_SUCCESS; 2878 } 2879 2880 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) { 2881 *length -= sizeof(FILE_INTERNAL_INFORMATION); 2882 2883 fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode); 2884 2885 return STATUS_SUCCESS; 2886 } 2887 2888 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) { 2889 *length -= sizeof(FILE_EA_INFORMATION); 2890 2891 /* This value appears to be the size of the structure NTFS stores on disk, and not, 2892 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store). 2893 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each 2894 * item. */ 2895 2896 eai->EaSize = fcb->ealen; 2897 2898 return STATUS_SUCCESS; 2899 } 2900 2901 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) { 2902 RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION)); 2903 2904 *length -= sizeof(FILE_POSITION_INFORMATION); 2905 2906 fpi->CurrentByteOffset = FileObject->CurrentByteOffset; 2907 2908 return STATUS_SUCCESS; 2909 } 2910 2911 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) { 2912 file_ref* fr; 2913 NTSTATUS Status; 2914 ULONG reqlen = 0; 2915 USHORT offset; 2916 BOOL overflow = FALSE; 2917 2918 // FIXME - we need a lock on filerefs' filepart 2919 2920 if (fileref == fileref->fcb->Vcb->root_fileref) { 2921 if (fn->MaximumLength >= sizeof(WCHAR)) { 2922 fn->Buffer[0] = '\\'; 2923 fn->Length = sizeof(WCHAR); 2924 2925 if (name_offset) 2926 *name_offset = 0; 2927 2928 return STATUS_SUCCESS; 2929 } else { 2930 if (preqlen) 2931 *preqlen = sizeof(WCHAR); 2932 fn->Length = 0; 2933 2934 return STATUS_BUFFER_OVERFLOW; 2935 } 2936 } 2937 2938 fr = fileref; 2939 offset = 0; 2940 2941 while (fr->parent) { 2942 USHORT movelen; 2943 2944 if (!fr->dc) 2945 return STATUS_INTERNAL_ERROR; 2946 2947 if (!overflow) { 2948 if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength) 2949 overflow = TRUE; 2950 } 2951 2952 if (overflow) 2953 movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR); 2954 else 2955 movelen = fn->Length; 2956 2957 if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) { 2958 RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen); 2959 offset += fr->dc->name.Length + sizeof(WCHAR); 2960 } 2961 2962 if (fn->MaximumLength >= sizeof(WCHAR)) { 2963 fn->Buffer[0] = fr->fcb->ads ? ':' : '\\'; 2964 fn->Length += sizeof(WCHAR); 2965 2966 if (fn->MaximumLength > sizeof(WCHAR)) { 2967 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR))); 2968 fn->Length += fr->dc->name.Length; 2969 } 2970 2971 if (fn->Length > fn->MaximumLength) { 2972 fn->Length = fn->MaximumLength; 2973 overflow = TRUE; 2974 } 2975 } 2976 2977 reqlen += sizeof(WCHAR) + fr->dc->name.Length; 2978 2979 fr = fr->parent; 2980 } 2981 2982 offset += sizeof(WCHAR); 2983 2984 if (overflow) { 2985 if (preqlen) 2986 *preqlen = reqlen; 2987 Status = STATUS_BUFFER_OVERFLOW; 2988 } else { 2989 if (name_offset) 2990 *name_offset = offset; 2991 2992 Status = STATUS_SUCCESS; 2993 } 2994 2995 return Status; 2996 } 2997 2998 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) { 2999 ULONG reqlen; 3000 UNICODE_STRING fn; 3001 NTSTATUS Status; 3002 static const WCHAR datasuf[] = {':','$','D','A','T','A',0}; 3003 UINT16 datasuflen = sizeof(datasuf) - sizeof(WCHAR); 3004 3005 if (!fileref) { 3006 ERR("called without fileref\n"); 3007 return STATUS_INVALID_PARAMETER; 3008 } 3009 3010 *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]); 3011 3012 TRACE("maximum length is %u\n", *length); 3013 fni->FileNameLength = 0; 3014 3015 fni->FileName[0] = 0; 3016 3017 fn.Buffer = fni->FileName; 3018 fn.Length = 0; 3019 fn.MaximumLength = (UINT16)*length; 3020 3021 Status = fileref_get_filename(fileref, &fn, NULL, &reqlen); 3022 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) { 3023 ERR("fileref_get_filename returned %08x\n", Status); 3024 return Status; 3025 } 3026 3027 if (fcb->ads) { 3028 if (Status == STATUS_BUFFER_OVERFLOW) 3029 reqlen += datasuflen; 3030 else { 3031 if (fn.Length + datasuflen > fn.MaximumLength) { 3032 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length); 3033 reqlen += datasuflen; 3034 Status = STATUS_BUFFER_OVERFLOW; 3035 } else { 3036 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen); 3037 fn.Length += datasuflen; 3038 } 3039 } 3040 } 3041 3042 if (Status == STATUS_BUFFER_OVERFLOW) { 3043 *length = -1; 3044 fni->FileNameLength = reqlen; 3045 TRACE("%.*S (truncated)\n", fn.Length / sizeof(WCHAR), fn.Buffer); 3046 } else { 3047 *length -= fn.Length; 3048 fni->FileNameLength = fn.Length; 3049 TRACE("%.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer); 3050 } 3051 3052 return Status; 3053 } 3054 3055 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, LONG* length) { 3056 *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION); 3057 3058 if (fcb->ads) { 3059 if (!ccb->fileref || !ccb->fileref->parent) { 3060 ERR("no fileref for stream\n"); 3061 return STATUS_INTERNAL_ERROR; 3062 } 3063 3064 ati->FileAttributes = ccb->fileref->parent->fcb->atts; 3065 } else 3066 ati->FileAttributes = fcb->atts; 3067 3068 if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 3069 ati->ReparseTag = 0; 3070 else 3071 ati->ReparseTag = get_reparse_tag_fcb(fcb); 3072 3073 return STATUS_SUCCESS; 3074 } 3075 3076 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) { 3077 LONG reqsize; 3078 LIST_ENTRY* le; 3079 FILE_STREAM_INFORMATION *entry, *lastentry; 3080 NTSTATUS Status; 3081 3082 static const WCHAR datasuf[] = L":$DATA"; 3083 UNICODE_STRING suf; 3084 3085 if (!fileref) { 3086 ERR("fileref was NULL\n"); 3087 return STATUS_INVALID_PARAMETER; 3088 } 3089 3090 suf.Buffer = (WCHAR*)datasuf; 3091 suf.Length = suf.MaximumLength = sizeof(datasuf) - sizeof(WCHAR); 3092 3093 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) 3094 reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR); 3095 else 3096 reqsize = 0; 3097 3098 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE); 3099 3100 le = fileref->fcb->dir_children_index.Flink; 3101 while (le != &fileref->fcb->dir_children_index) { 3102 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); 3103 3104 if (dc->index == 0) { 3105 reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG)); 3106 reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length; 3107 } else 3108 break; 3109 3110 le = le->Flink; 3111 } 3112 3113 TRACE("length = %i, reqsize = %u\n", *length, reqsize); 3114 3115 if (reqsize > *length) { 3116 Status = STATUS_BUFFER_OVERFLOW; 3117 goto end; 3118 } 3119 3120 entry = fsi; 3121 lastentry = NULL; 3122 3123 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) { 3124 ULONG off; 3125 3126 entry->NextEntryOffset = 0; 3127 entry->StreamNameLength = suf.Length + sizeof(WCHAR); 3128 entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size; 3129 entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb); 3130 3131 entry->StreamName[0] = ':'; 3132 RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length); 3133 3134 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG)); 3135 3136 lastentry = entry; 3137 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off); 3138 } 3139 3140 le = fileref->fcb->dir_children_index.Flink; 3141 while (le != &fileref->fcb->dir_children_index) { 3142 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); 3143 3144 if (dc->index == 0) { 3145 ULONG off; 3146 3147 entry->NextEntryOffset = 0; 3148 entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR); 3149 3150 if (dc->fileref) 3151 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length; 3152 else 3153 entry->StreamSize.QuadPart = dc->size; 3154 3155 entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart; 3156 3157 entry->StreamName[0] = ':'; 3158 3159 RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length); 3160 RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length); 3161 3162 if (lastentry) 3163 lastentry->NextEntryOffset = (UINT32)((UINT8*)entry - (UINT8*)lastentry); 3164 3165 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG)); 3166 3167 lastentry = entry; 3168 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off); 3169 } else 3170 break; 3171 3172 le = le->Flink; 3173 } 3174 3175 *length -= reqsize; 3176 3177 Status = STATUS_SUCCESS; 3178 3179 end: 3180 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock); 3181 3182 return Status; 3183 } 3184 3185 #ifndef __REACTOS__ 3186 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) { 3187 TRACE("FileStandardLinkInformation\n"); 3188 3189 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close 3190 3191 fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink; 3192 fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink; 3193 fsli->DeletePending = fileref ? fileref->delete_on_close : FALSE; 3194 fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? TRUE : FALSE; 3195 3196 *length -= sizeof(FILE_STANDARD_LINK_INFORMATION); 3197 3198 return STATUS_SUCCESS; 3199 } 3200 3201 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) { 3202 NTSTATUS Status; 3203 LIST_ENTRY* le; 3204 LONG bytes_needed; 3205 FILE_LINK_ENTRY_INFORMATION* feli; 3206 BOOL overflow = FALSE; 3207 fcb* fcb = fileref->fcb; 3208 ULONG len; 3209 3210 if (fcb->ads) 3211 return STATUS_INVALID_PARAMETER; 3212 3213 if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry)) 3214 return STATUS_INVALID_PARAMETER; 3215 3216 RtlZeroMemory(fli, *length); 3217 3218 bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry); 3219 len = bytes_needed; 3220 feli = NULL; 3221 3222 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); 3223 3224 if (fcb->inode == SUBVOL_ROOT_INODE) { 3225 ULONG namelen; 3226 3227 if (fcb == fcb->Vcb->root_fileref->fcb) 3228 namelen = sizeof(WCHAR); 3229 else 3230 namelen = fileref->dc->name.Length; 3231 3232 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen; 3233 3234 if (bytes_needed > *length) 3235 overflow = TRUE; 3236 3237 if (!overflow) { 3238 feli = &fli->Entry; 3239 3240 feli->NextEntryOffset = 0; 3241 feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume 3242 3243 if (fcb == fcb->Vcb->root_fileref->fcb) { 3244 feli->FileNameLength = 1; 3245 feli->FileName[0] = '.'; 3246 } else { 3247 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR); 3248 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length); 3249 } 3250 3251 fli->EntriesReturned++; 3252 3253 len = bytes_needed; 3254 } 3255 } else { 3256 ExAcquireResourceExclusiveLite(&fcb->Vcb->fileref_lock, TRUE); 3257 3258 if (IsListEmpty(&fcb->hardlinks)) { 3259 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR); 3260 3261 if (bytes_needed > *length) 3262 overflow = TRUE; 3263 3264 if (!overflow) { 3265 feli = &fli->Entry; 3266 3267 feli->NextEntryOffset = 0; 3268 feli->ParentFileId = fileref->parent->fcb->inode; 3269 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR); 3270 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length); 3271 3272 fli->EntriesReturned++; 3273 3274 len = bytes_needed; 3275 } 3276 } else { 3277 le = fcb->hardlinks.Flink; 3278 while (le != &fcb->hardlinks) { 3279 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); 3280 file_ref* parfr; 3281 3282 TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer); 3283 3284 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp); 3285 3286 if (!NT_SUCCESS(Status)) { 3287 ERR("open_fileref_by_inode returned %08x\n", Status); 3288 } else if (!parfr->deleted) { 3289 LIST_ENTRY* le2; 3290 BOOL found = FALSE, deleted = FALSE; 3291 UNICODE_STRING* fn = NULL; 3292 3293 le2 = parfr->children.Flink; 3294 while (le2 != &parfr->children) { 3295 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry); 3296 3297 if (fr2->dc->index == hl->index) { 3298 found = TRUE; 3299 deleted = fr2->deleted; 3300 3301 if (!deleted) 3302 fn = &fr2->dc->name; 3303 3304 break; 3305 } 3306 3307 le2 = le2->Flink; 3308 } 3309 3310 if (!found) 3311 fn = &hl->name; 3312 3313 if (!deleted) { 3314 TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found); 3315 3316 if (feli) 3317 bytes_needed = (LONG)sector_align(bytes_needed, 8); 3318 3319 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR); 3320 3321 if (bytes_needed > *length) 3322 overflow = TRUE; 3323 3324 if (!overflow) { 3325 if (feli) { 3326 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8); 3327 feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset); 3328 } else 3329 feli = &fli->Entry; 3330 3331 feli->NextEntryOffset = 0; 3332 feli->ParentFileId = parfr->fcb->inode; 3333 feli->FileNameLength = fn->Length / sizeof(WCHAR); 3334 RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length); 3335 3336 fli->EntriesReturned++; 3337 3338 len = bytes_needed; 3339 } 3340 } 3341 3342 free_fileref(parfr); 3343 } 3344 3345 le = le->Flink; 3346 } 3347 } 3348 3349 ExReleaseResourceLite(&fcb->Vcb->fileref_lock); 3350 } 3351 3352 fli->BytesNeeded = bytes_needed; 3353 3354 *length -= len; 3355 3356 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; 3357 3358 ExReleaseResourceLite(fcb->Header.Resource); 3359 3360 return Status; 3361 } 3362 #endif /* __REACTOS__ */ 3363 3364 #if (NTDDI_VERSION >= NTDDI_WIN10) 3365 #ifdef __MINGW32__ 3366 typedef struct _FILE_ID_128 { 3367 UCHAR Identifier[16]; 3368 } FILE_ID_128, *PFILE_ID_128; 3369 3370 typedef struct _FILE_ID_INFORMATION { 3371 ULONGLONG VolumeSerialNumber; 3372 FILE_ID_128 FileId; 3373 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION; 3374 #endif 3375 3376 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) { 3377 RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(UINT64)); 3378 RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(UINT64)); 3379 RtlCopyMemory(&fii->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64)); 3380 3381 *length -= sizeof(FILE_ID_INFORMATION); 3382 3383 return STATUS_SUCCESS; 3384 } 3385 #endif 3386 3387 #ifndef __REACTOS__ 3388 static NTSTATUS fill_in_file_stat_lx_information(FILE_STAT_LX_INFORMATION* fsli, fcb* fcb, ccb* ccb, LONG* length) { 3389 INODE_ITEM* ii; 3390 3391 fsli->FileId.LowPart = (UINT32)fcb->inode; 3392 fsli->FileId.HighPart = (UINT32)fcb->subvol->id; 3393 3394 if (fcb->ads) 3395 ii = &ccb->fileref->parent->fcb->inode_item; 3396 else 3397 ii = &fcb->inode_item; 3398 3399 if (fcb == fcb->Vcb->dummy_fcb) { 3400 LARGE_INTEGER time; 3401 3402 KeQuerySystemTime(&time); 3403 fsli->CreationTime = fsli->LastAccessTime = fsli->LastWriteTime = fsli->ChangeTime = time; 3404 } else { 3405 fsli->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 3406 fsli->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 3407 fsli->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 3408 fsli->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 3409 } 3410 3411 if (fcb->ads) { 3412 fsli->AllocationSize.QuadPart = fsli->EndOfFile.QuadPart = fcb->adsdata.Length; 3413 fsli->FileAttributes = ccb->fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : ccb->fileref->parent->fcb->atts; 3414 } else { 3415 fsli->AllocationSize.QuadPart = fcb_alloc_size(fcb); 3416 fsli->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 3417 fsli->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 3418 } 3419 3420 if (fcb->type == BTRFS_TYPE_SOCKET) 3421 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_SOCKET; 3422 else if (fcb->type == BTRFS_TYPE_FIFO) 3423 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_FIFO; 3424 else if (fcb->type == BTRFS_TYPE_CHARDEV) 3425 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_CHARDEV; 3426 else if (fcb->type == BTRFS_TYPE_BLOCKDEV) 3427 fsli->ReparseTag = IO_REPARSE_TAG_LXSS_BLOCKDEV; 3428 else if (!(fsli->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 3429 fsli->ReparseTag = 0; 3430 else 3431 fsli->ReparseTag = get_reparse_tag_fcb(fcb); 3432 3433 if (fcb->type == BTRFS_TYPE_SOCKET || fcb->type == BTRFS_TYPE_FIFO || fcb->type == BTRFS_TYPE_CHARDEV || fcb->type == BTRFS_TYPE_BLOCKDEV) 3434 fsli->FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; 3435 3436 if (fcb->ads) 3437 fsli->NumberOfLinks = ccb->fileref->parent->fcb->inode_item.st_nlink; 3438 else 3439 fsli->NumberOfLinks = fcb->inode_item.st_nlink; 3440 3441 fsli->EffectiveAccess = ccb->access; 3442 fsli->LxFlags = LX_FILE_METADATA_HAS_UID | LX_FILE_METADATA_HAS_GID | LX_FILE_METADATA_HAS_MODE | LX_FILE_METADATA_HAS_DEVICE_ID; // FIXME - LX_FILE_CASE_SENSITIVE_DIR 3443 fsli->LxUid = ii->st_uid; 3444 fsli->LxGid = ii->st_gid; 3445 fsli->LxMode = ii->st_mode; 3446 3447 if (ii->st_mode & __S_IFBLK || ii->st_mode & __S_IFCHR) { 3448 fsli->LxDeviceIdMajor = (ii->st_rdev & 0xFFFFFFFFFFF00000) >> 20; 3449 fsli->LxDeviceIdMinor = (ii->st_rdev & 0xFFFFF); 3450 } else { 3451 fsli->LxDeviceIdMajor = 0; 3452 fsli->LxDeviceIdMinor = 0; 3453 } 3454 3455 *length -= sizeof(FILE_STAT_LX_INFORMATION); 3456 3457 return STATUS_SUCCESS; 3458 } 3459 #endif 3460 3461 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) { 3462 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 3463 LONG length = IrpSp->Parameters.QueryFile.Length; 3464 fcb* fcb = FileObject->FsContext; 3465 ccb* ccb = FileObject->FsContext2; 3466 file_ref* fileref = ccb ? ccb->fileref : NULL; 3467 NTSTATUS Status; 3468 3469 TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp); 3470 TRACE("fcb = %p\n", fcb); 3471 3472 if (fcb == Vcb->volume_fcb) 3473 return STATUS_INVALID_PARAMETER; 3474 3475 if (!ccb) { 3476 ERR("ccb is NULL\n"); 3477 return STATUS_INVALID_PARAMETER; 3478 } 3479 3480 switch (IrpSp->Parameters.QueryFile.FileInformationClass) { 3481 case FileAllInformation: 3482 { 3483 FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer; 3484 INODE_ITEM* ii; 3485 3486 TRACE("FileAllInformation\n"); 3487 3488 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 3489 WARN("insufficient privileges\n"); 3490 Status = STATUS_ACCESS_DENIED; 3491 goto exit; 3492 } 3493 3494 if (fcb->ads) { 3495 if (!fileref || !fileref->parent) { 3496 ERR("no fileref for stream\n"); 3497 Status = STATUS_INTERNAL_ERROR; 3498 goto exit; 3499 } 3500 3501 ii = &fileref->parent->fcb->inode_item; 3502 } else 3503 ii = &fcb->inode_item; 3504 3505 // Access, mode, and alignment are all filled in by the kernel 3506 3507 if (length > 0) 3508 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref); 3509 3510 if (length > 0) 3511 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length); 3512 3513 if (length > 0) 3514 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length); 3515 3516 if (length > 0) 3517 fill_in_file_ea_information(&fai->EaInformation, fcb, &length); 3518 3519 length -= sizeof(FILE_ACCESS_INFORMATION); 3520 3521 if (length > 0) 3522 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length); 3523 3524 length -= sizeof(FILE_MODE_INFORMATION); 3525 3526 length -= sizeof(FILE_ALIGNMENT_INFORMATION); 3527 3528 if (length > 0) 3529 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length); 3530 3531 Status = STATUS_SUCCESS; 3532 3533 break; 3534 } 3535 3536 case FileAttributeTagInformation: 3537 { 3538 FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer; 3539 3540 TRACE("FileAttributeTagInformation\n"); 3541 3542 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 3543 WARN("insufficient privileges\n"); 3544 Status = STATUS_ACCESS_DENIED; 3545 goto exit; 3546 } 3547 3548 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 3549 Status = fill_in_file_attribute_information(ati, fcb, ccb, &length); 3550 ExReleaseResourceLite(&Vcb->tree_lock); 3551 3552 break; 3553 } 3554 3555 case FileBasicInformation: 3556 { 3557 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer; 3558 INODE_ITEM* ii; 3559 3560 TRACE("FileBasicInformation\n"); 3561 3562 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 3563 WARN("insufficient privileges\n"); 3564 Status = STATUS_ACCESS_DENIED; 3565 goto exit; 3566 } 3567 3568 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) { 3569 WARN("overflow\n"); 3570 Status = STATUS_BUFFER_OVERFLOW; 3571 goto exit; 3572 } 3573 3574 if (fcb->ads) { 3575 if (!fileref || !fileref->parent) { 3576 ERR("no fileref for stream\n"); 3577 Status = STATUS_INTERNAL_ERROR; 3578 goto exit; 3579 } 3580 3581 ii = &fileref->parent->fcb->inode_item; 3582 } else 3583 ii = &fcb->inode_item; 3584 3585 Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref); 3586 break; 3587 } 3588 3589 case FileCompressionInformation: 3590 FIXME("STUB: FileCompressionInformation\n"); 3591 Status = STATUS_INVALID_PARAMETER; 3592 goto exit; 3593 3594 case FileEaInformation: 3595 { 3596 FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer; 3597 3598 TRACE("FileEaInformation\n"); 3599 3600 Status = fill_in_file_ea_information(eai, fcb, &length); 3601 3602 break; 3603 } 3604 3605 case FileInternalInformation: 3606 { 3607 FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer; 3608 3609 TRACE("FileInternalInformation\n"); 3610 3611 Status = fill_in_file_internal_information(fii, fcb, &length); 3612 3613 break; 3614 } 3615 3616 case FileNameInformation: 3617 { 3618 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer; 3619 3620 TRACE("FileNameInformation\n"); 3621 3622 Status = fill_in_file_name_information(fni, fcb, fileref, &length); 3623 3624 break; 3625 } 3626 3627 case FileNetworkOpenInformation: 3628 { 3629 FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer; 3630 3631 TRACE("FileNetworkOpenInformation\n"); 3632 3633 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 3634 WARN("insufficient privileges\n"); 3635 Status = STATUS_ACCESS_DENIED; 3636 goto exit; 3637 } 3638 3639 Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length); 3640 3641 break; 3642 } 3643 3644 case FilePositionInformation: 3645 { 3646 FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer; 3647 3648 TRACE("FilePositionInformation\n"); 3649 3650 Status = fill_in_file_position_information(fpi, FileObject, &length); 3651 3652 break; 3653 } 3654 3655 case FileStandardInformation: 3656 { 3657 FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer; 3658 3659 TRACE("FileStandardInformation\n"); 3660 3661 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) { 3662 WARN("overflow\n"); 3663 Status = STATUS_BUFFER_OVERFLOW; 3664 goto exit; 3665 } 3666 3667 Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length); 3668 3669 break; 3670 } 3671 3672 case FileStreamInformation: 3673 { 3674 FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer; 3675 3676 TRACE("FileStreamInformation\n"); 3677 3678 Status = fill_in_file_stream_information(fsi, fileref, &length); 3679 3680 break; 3681 } 3682 3683 #if (NTDDI_VERSION >= NTDDI_VISTA) 3684 case FileHardLinkInformation: 3685 { 3686 FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer; 3687 3688 TRACE("FileHardLinkInformation\n"); 3689 3690 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 3691 Status = fill_in_hard_link_information(fli, fileref, Irp, &length); 3692 ExReleaseResourceLite(&Vcb->tree_lock); 3693 3694 break; 3695 } 3696 3697 case FileNormalizedNameInformation: 3698 { 3699 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer; 3700 3701 TRACE("FileNormalizedNameInformation\n"); 3702 3703 Status = fill_in_file_name_information(fni, fcb, fileref, &length); 3704 3705 break; 3706 } 3707 #endif 3708 3709 #if (NTDDI_VERSION >= NTDDI_WIN7) 3710 case FileStandardLinkInformation: 3711 { 3712 FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer; 3713 3714 TRACE("FileStandardLinkInformation\n"); 3715 3716 Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length); 3717 3718 break; 3719 } 3720 3721 case FileRemoteProtocolInformation: 3722 TRACE("FileRemoteProtocolInformation\n"); 3723 Status = STATUS_INVALID_PARAMETER; 3724 goto exit; 3725 #endif 3726 3727 #if (NTDDI_VERSION >= NTDDI_WIN10) 3728 #ifndef _MSC_VER 3729 #pragma GCC diagnostic push 3730 #pragma GCC diagnostic ignored "-Wswitch" 3731 #endif 3732 case FileIdInformation: 3733 { 3734 FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer; 3735 3736 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) { 3737 WARN("overflow\n"); 3738 Status = STATUS_BUFFER_OVERFLOW; 3739 goto exit; 3740 } 3741 3742 TRACE("FileIdInformation\n"); 3743 3744 Status = fill_in_file_id_information(fii, fcb, &length); 3745 3746 break; 3747 } 3748 3749 case FileStatLxInformation: 3750 { 3751 FILE_STAT_LX_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer; 3752 3753 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STAT_LX_INFORMATION)) { 3754 WARN("overflow\n"); 3755 Status = STATUS_BUFFER_OVERFLOW; 3756 goto exit; 3757 } 3758 3759 TRACE("FileStatLxInformation\n"); 3760 3761 Status = fill_in_file_stat_lx_information(fsli, fcb, ccb, &length); 3762 3763 break; 3764 } 3765 #ifndef _MSC_VER 3766 #pragma GCC diagnostic pop 3767 #endif 3768 #endif 3769 3770 default: 3771 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass); 3772 Status = STATUS_INVALID_PARAMETER; 3773 goto exit; 3774 } 3775 3776 if (length < 0) { 3777 length = 0; 3778 Status = STATUS_BUFFER_OVERFLOW; 3779 } 3780 3781 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length; 3782 3783 exit: 3784 TRACE("query_info returning %08x\n", Status); 3785 3786 return Status; 3787 } 3788 3789 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION) 3790 _Function_class_(DRIVER_DISPATCH) 3791 NTSTATUS NTAPI drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 3792 PIO_STACK_LOCATION IrpSp; 3793 NTSTATUS Status; 3794 fcb* fcb; 3795 device_extension* Vcb = DeviceObject->DeviceExtension; 3796 BOOL top_level; 3797 3798 FsRtlEnterFileSystem(); 3799 3800 top_level = is_top_level(Irp); 3801 3802 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 3803 Status = vol_query_information(DeviceObject, Irp); 3804 goto end; 3805 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 3806 Status = STATUS_INVALID_PARAMETER; 3807 goto end; 3808 } 3809 3810 Irp->IoStatus.Information = 0; 3811 3812 TRACE("query information\n"); 3813 3814 IrpSp = IoGetCurrentIrpStackLocation(Irp); 3815 3816 fcb = IrpSp->FileObject->FsContext; 3817 TRACE("fcb = %p\n", fcb); 3818 TRACE("fcb->subvol = %p\n", fcb->subvol); 3819 3820 Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp); 3821 3822 end: 3823 TRACE("returning %08x\n", Status); 3824 3825 Irp->IoStatus.Status = Status; 3826 3827 IoCompleteRequest( Irp, IO_NO_INCREMENT ); 3828 3829 if (top_level) 3830 IoSetTopLevelIrp(NULL); 3831 3832 FsRtlExitFileSystem(); 3833 3834 return Status; 3835 } 3836 3837 _Dispatch_type_(IRP_MJ_QUERY_EA) 3838 _Function_class_(DRIVER_DISPATCH) 3839 NTSTATUS NTAPI drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 3840 NTSTATUS Status; 3841 BOOL top_level; 3842 device_extension* Vcb = DeviceObject->DeviceExtension; 3843 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 3844 PFILE_OBJECT FileObject = IrpSp->FileObject; 3845 fcb* fcb; 3846 ccb* ccb; 3847 FILE_FULL_EA_INFORMATION* ffei; 3848 ULONG retlen = 0; 3849 3850 FsRtlEnterFileSystem(); 3851 3852 TRACE("(%p, %p)\n", DeviceObject, Irp); 3853 3854 top_level = is_top_level(Irp); 3855 3856 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 3857 Status = vol_query_ea(DeviceObject, Irp); 3858 goto end; 3859 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 3860 Status = STATUS_INVALID_PARAMETER; 3861 goto end; 3862 } 3863 3864 ffei = map_user_buffer(Irp, NormalPagePriority); 3865 if (!ffei) { 3866 ERR("could not get output buffer\n"); 3867 Status = STATUS_INVALID_PARAMETER; 3868 goto end; 3869 } 3870 3871 if (!FileObject) { 3872 ERR("no file object\n"); 3873 Status = STATUS_INVALID_PARAMETER; 3874 goto end; 3875 } 3876 3877 fcb = FileObject->FsContext; 3878 3879 if (!fcb) { 3880 ERR("no fcb\n"); 3881 Status = STATUS_INVALID_PARAMETER; 3882 goto end; 3883 } 3884 3885 ccb = FileObject->FsContext2; 3886 3887 if (!ccb) { 3888 ERR("no ccb\n"); 3889 Status = STATUS_INVALID_PARAMETER; 3890 goto end; 3891 } 3892 3893 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) { 3894 WARN("insufficient privileges\n"); 3895 Status = STATUS_ACCESS_DENIED; 3896 goto end; 3897 } 3898 3899 if (fcb->ads) 3900 fcb = ccb->fileref->parent->fcb; 3901 3902 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); 3903 3904 Status = STATUS_SUCCESS; 3905 3906 if (fcb->ea_xattr.Length == 0) 3907 goto end2; 3908 3909 if (IrpSp->Parameters.QueryEa.EaList) { 3910 FILE_FULL_EA_INFORMATION *ea, *out; 3911 FILE_GET_EA_INFORMATION* in; 3912 3913 in = IrpSp->Parameters.QueryEa.EaList; 3914 do { 3915 STRING s; 3916 3917 s.Length = s.MaximumLength = in->EaNameLength; 3918 s.Buffer = in->EaName; 3919 3920 RtlUpperString(&s, &s); 3921 3922 if (in->NextEntryOffset == 0) 3923 break; 3924 3925 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset); 3926 } while (TRUE); 3927 3928 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 3929 out = NULL; 3930 3931 do { 3932 BOOL found = FALSE; 3933 3934 in = IrpSp->Parameters.QueryEa.EaList; 3935 do { 3936 if (in->EaNameLength == ea->EaNameLength && 3937 RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) { 3938 found = TRUE; 3939 break; 3940 } 3941 3942 if (in->NextEntryOffset == 0) 3943 break; 3944 3945 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset); 3946 } while (TRUE); 3947 3948 if (found) { 3949 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0; 3950 3951 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) { 3952 Status = STATUS_BUFFER_OVERFLOW; 3953 retlen = 0; 3954 goto end2; 3955 } 3956 3957 retlen += padding; 3958 3959 if (out) { 3960 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding; 3961 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset); 3962 } else 3963 out = ffei; 3964 3965 out->NextEntryOffset = 0; 3966 out->Flags = ea->Flags; 3967 out->EaNameLength = ea->EaNameLength; 3968 out->EaValueLength = ea->EaValueLength; 3969 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1); 3970 3971 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength; 3972 3973 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) 3974 break; 3975 } 3976 3977 if (ea->NextEntryOffset == 0) 3978 break; 3979 3980 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 3981 } while (TRUE); 3982 } else { 3983 FILE_FULL_EA_INFORMATION *ea, *out; 3984 ULONG index; 3985 3986 if (IrpSp->Flags & SL_INDEX_SPECIFIED) { 3987 // The index is 1-based 3988 if (IrpSp->Parameters.QueryEa.EaIndex == 0) { 3989 Status = STATUS_NONEXISTENT_EA_ENTRY; 3990 goto end2; 3991 } else 3992 index = IrpSp->Parameters.QueryEa.EaIndex - 1; 3993 } else if (IrpSp->Flags & SL_RESTART_SCAN) 3994 index = ccb->ea_index = 0; 3995 else 3996 index = ccb->ea_index; 3997 3998 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 3999 4000 if (index > 0) { 4001 ULONG i; 4002 4003 for (i = 0; i < index; i++) { 4004 if (ea->NextEntryOffset == 0) // last item 4005 goto end2; 4006 4007 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4008 } 4009 } 4010 4011 out = NULL; 4012 4013 do { 4014 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0; 4015 4016 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) { 4017 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW; 4018 goto end2; 4019 } 4020 4021 retlen += padding; 4022 4023 if (out) { 4024 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding; 4025 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset); 4026 } else 4027 out = ffei; 4028 4029 out->NextEntryOffset = 0; 4030 out->Flags = ea->Flags; 4031 out->EaNameLength = ea->EaNameLength; 4032 out->EaValueLength = ea->EaValueLength; 4033 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1); 4034 4035 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength; 4036 4037 if (!(IrpSp->Flags & SL_INDEX_SPECIFIED)) 4038 ccb->ea_index++; 4039 4040 if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) 4041 break; 4042 4043 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4044 } while (TRUE); 4045 } 4046 4047 end2: 4048 ExReleaseResourceLite(fcb->Header.Resource); 4049 4050 end: 4051 TRACE("returning %08x\n", Status); 4052 4053 Irp->IoStatus.Status = Status; 4054 Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0; 4055 4056 IoCompleteRequest( Irp, IO_NO_INCREMENT ); 4057 4058 if (top_level) 4059 IoSetTopLevelIrp(NULL); 4060 4061 FsRtlExitFileSystem(); 4062 4063 return Status; 4064 } 4065 4066 _Dispatch_type_(IRP_MJ_SET_EA) 4067 _Function_class_(DRIVER_DISPATCH) 4068 NTSTATUS NTAPI drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 4069 device_extension* Vcb = DeviceObject->DeviceExtension; 4070 NTSTATUS Status; 4071 BOOL top_level; 4072 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 4073 PFILE_OBJECT FileObject = IrpSp->FileObject; 4074 fcb* fcb; 4075 ccb* ccb; 4076 file_ref* fileref; 4077 FILE_FULL_EA_INFORMATION* ffei; 4078 ULONG offset; 4079 LIST_ENTRY ealist; 4080 ea_item* item; 4081 FILE_FULL_EA_INFORMATION* ea; 4082 LIST_ENTRY* le; 4083 LARGE_INTEGER time; 4084 BTRFS_TIME now; 4085 4086 FsRtlEnterFileSystem(); 4087 4088 TRACE("(%p, %p)\n", DeviceObject, Irp); 4089 4090 top_level = is_top_level(Irp); 4091 4092 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 4093 Status = vol_set_ea(DeviceObject, Irp); 4094 goto end; 4095 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 4096 Status = STATUS_INVALID_PARAMETER; 4097 goto end; 4098 } 4099 4100 if (Vcb->readonly) { 4101 Status = STATUS_MEDIA_WRITE_PROTECTED; 4102 goto end; 4103 } 4104 4105 ffei = map_user_buffer(Irp, NormalPagePriority); 4106 if (!ffei) { 4107 ERR("could not get output buffer\n"); 4108 Status = STATUS_INVALID_PARAMETER; 4109 goto end; 4110 } 4111 4112 Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset); 4113 if (!NT_SUCCESS(Status)) { 4114 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset); 4115 goto end; 4116 } 4117 4118 if (!FileObject) { 4119 ERR("no file object\n"); 4120 Status = STATUS_INVALID_PARAMETER; 4121 goto end; 4122 } 4123 4124 fcb = FileObject->FsContext; 4125 4126 if (!fcb) { 4127 ERR("no fcb\n"); 4128 Status = STATUS_INVALID_PARAMETER; 4129 goto end; 4130 } 4131 4132 ccb = FileObject->FsContext2; 4133 4134 if (!ccb) { 4135 ERR("no ccb\n"); 4136 Status = STATUS_INVALID_PARAMETER; 4137 goto end; 4138 } 4139 4140 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) { 4141 WARN("insufficient privileges\n"); 4142 Status = STATUS_ACCESS_DENIED; 4143 goto end; 4144 } 4145 4146 if (fcb->ads) { 4147 fileref = ccb->fileref->parent; 4148 fcb = fileref->fcb; 4149 } else 4150 fileref = ccb->fileref; 4151 4152 InitializeListHead(&ealist); 4153 4154 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 4155 4156 if (fcb->ea_xattr.Length > 0) { 4157 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 4158 4159 do { 4160 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG); 4161 if (!item) { 4162 ERR("out of memory\n"); 4163 Status = STATUS_INSUFFICIENT_RESOURCES; 4164 goto end2; 4165 } 4166 4167 item->name.Length = item->name.MaximumLength = ea->EaNameLength; 4168 item->name.Buffer = ea->EaName; 4169 4170 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 4171 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 4172 4173 item->flags = ea->Flags; 4174 4175 InsertTailList(&ealist, &item->list_entry); 4176 4177 if (ea->NextEntryOffset == 0) 4178 break; 4179 4180 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4181 } while (TRUE); 4182 } 4183 4184 ea = ffei; 4185 4186 do { 4187 STRING s; 4188 BOOL found = FALSE; 4189 4190 s.Length = s.MaximumLength = ea->EaNameLength; 4191 s.Buffer = ea->EaName; 4192 4193 RtlUpperString(&s, &s); 4194 4195 le = ealist.Flink; 4196 while (le != &ealist) { 4197 item = CONTAINING_RECORD(le, ea_item, list_entry); 4198 4199 if (item->name.Length == s.Length && 4200 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) { 4201 item->flags = ea->Flags; 4202 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 4203 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 4204 found = TRUE; 4205 break; 4206 } 4207 4208 le = le->Flink; 4209 } 4210 4211 if (!found) { 4212 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG); 4213 if (!item) { 4214 ERR("out of memory\n"); 4215 Status = STATUS_INSUFFICIENT_RESOURCES; 4216 goto end2; 4217 } 4218 4219 item->name.Length = item->name.MaximumLength = ea->EaNameLength; 4220 item->name.Buffer = ea->EaName; 4221 4222 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 4223 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 4224 4225 item->flags = ea->Flags; 4226 4227 InsertTailList(&ealist, &item->list_entry); 4228 } 4229 4230 if (ea->NextEntryOffset == 0) 4231 break; 4232 4233 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4234 } while (TRUE); 4235 4236 // remove entries with zero-length value 4237 le = ealist.Flink; 4238 while (le != &ealist) { 4239 LIST_ENTRY* le2 = le->Flink; 4240 4241 item = CONTAINING_RECORD(le, ea_item, list_entry); 4242 4243 if (item->value.Length == 0) { 4244 RemoveEntryList(&item->list_entry); 4245 ExFreePool(item); 4246 } 4247 4248 le = le2; 4249 } 4250 4251 // handle LXSS values 4252 le = ealist.Flink; 4253 while (le != &ealist) { 4254 LIST_ENTRY* le2 = le->Flink; 4255 4256 item = CONTAINING_RECORD(le, ea_item, list_entry); 4257 4258 if (item->name.Length == sizeof(lxuid) - 1 && RtlCompareMemory(item->name.Buffer, lxuid, item->name.Length) == item->name.Length) { 4259 if (item->value.Length < sizeof(UINT32)) { 4260 ERR("uid value was shorter than expected\n"); 4261 Status = STATUS_INVALID_PARAMETER; 4262 goto end2; 4263 } 4264 4265 if (Irp->RequestorMode == KernelMode) { 4266 RtlCopyMemory(&fcb->inode_item.st_uid, item->value.Buffer, sizeof(UINT32)); 4267 fcb->sd_dirty = TRUE; 4268 fcb->sd_deleted = FALSE; 4269 } 4270 4271 RemoveEntryList(&item->list_entry); 4272 ExFreePool(item); 4273 } else if (item->name.Length == sizeof(lxgid) - 1 && RtlCompareMemory(item->name.Buffer, lxgid, item->name.Length) == item->name.Length) { 4274 if (item->value.Length < sizeof(UINT32)) { 4275 ERR("gid value was shorter than expected\n"); 4276 Status = STATUS_INVALID_PARAMETER; 4277 goto end2; 4278 } 4279 4280 if (Irp->RequestorMode == KernelMode) 4281 RtlCopyMemory(&fcb->inode_item.st_gid, item->value.Buffer, sizeof(UINT32)); 4282 4283 RemoveEntryList(&item->list_entry); 4284 ExFreePool(item); 4285 } else if (item->name.Length == sizeof(lxmod) - 1 && RtlCompareMemory(item->name.Buffer, lxmod, item->name.Length) == item->name.Length) { 4286 if (item->value.Length < sizeof(UINT32)) { 4287 ERR("mode value was shorter than expected\n"); 4288 Status = STATUS_INVALID_PARAMETER; 4289 goto end2; 4290 } 4291 4292 if (Irp->RequestorMode == KernelMode) { 4293 UINT32 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; 4294 UINT32 val; 4295 4296 RtlCopyMemory(&val, item->value.Buffer, sizeof(UINT32)); 4297 4298 fcb->inode_item.st_mode &= ~allowed; 4299 fcb->inode_item.st_mode |= val & allowed; 4300 } 4301 4302 RemoveEntryList(&item->list_entry); 4303 ExFreePool(item); 4304 } 4305 4306 le = le2; 4307 } 4308 4309 if (IsListEmpty(&ealist)) { 4310 fcb->ealen = 0; 4311 4312 if (fcb->ea_xattr.Buffer) 4313 ExFreePool(fcb->ea_xattr.Buffer); 4314 4315 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0; 4316 fcb->ea_xattr.Buffer = NULL; 4317 } else { 4318 UINT16 size = 0; 4319 char *buf, *oldbuf; 4320 4321 le = ealist.Flink; 4322 while (le != &ealist) { 4323 item = CONTAINING_RECORD(le, ea_item, list_entry); 4324 4325 if (size % 4 > 0) 4326 size += 4 - (size % 4); 4327 4328 size += (UINT16)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length; 4329 4330 le = le->Flink; 4331 } 4332 4333 buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG); 4334 if (!buf) { 4335 ERR("out of memory\n"); 4336 Status = STATUS_INSUFFICIENT_RESOURCES; 4337 goto end2; 4338 } 4339 4340 oldbuf = fcb->ea_xattr.Buffer; 4341 4342 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size; 4343 fcb->ea_xattr.Buffer = buf; 4344 4345 fcb->ealen = 4; 4346 ea = NULL; 4347 4348 le = ealist.Flink; 4349 while (le != &ealist) { 4350 item = CONTAINING_RECORD(le, ea_item, list_entry); 4351 4352 if (ea) { 4353 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength; 4354 4355 if (ea->NextEntryOffset % 4 > 0) 4356 ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4); 4357 4358 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4359 } else 4360 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 4361 4362 ea->NextEntryOffset = 0; 4363 ea->Flags = item->flags; 4364 ea->EaNameLength = (UCHAR)item->name.Length; 4365 ea->EaValueLength = item->value.Length; 4366 4367 RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length); 4368 ea->EaName[item->name.Length] = 0; 4369 RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length); 4370 4371 fcb->ealen += 5 + item->name.Length + item->value.Length; 4372 4373 le = le->Flink; 4374 } 4375 4376 if (oldbuf) 4377 ExFreePool(oldbuf); 4378 } 4379 4380 fcb->ea_changed = TRUE; 4381 4382 KeQuerySystemTime(&time); 4383 win_time_to_unix(time, &now); 4384 4385 fcb->inode_item.transid = Vcb->superblock.generation; 4386 fcb->inode_item.sequence++; 4387 4388 if (!ccb->user_set_change_time) 4389 fcb->inode_item.st_ctime = now; 4390 4391 fcb->inode_item_changed = TRUE; 4392 mark_fcb_dirty(fcb); 4393 4394 send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL); 4395 4396 Status = STATUS_SUCCESS; 4397 4398 end2: 4399 ExReleaseResourceLite(fcb->Header.Resource); 4400 4401 while (!IsListEmpty(&ealist)) { 4402 le = RemoveHeadList(&ealist); 4403 4404 item = CONTAINING_RECORD(le, ea_item, list_entry); 4405 4406 ExFreePool(item); 4407 } 4408 4409 end: 4410 TRACE("returning %08x\n", Status); 4411 4412 Irp->IoStatus.Status = Status; 4413 Irp->IoStatus.Information = 0; 4414 4415 IoCompleteRequest(Irp, IO_NO_INCREMENT); 4416 4417 if (top_level) 4418 IoSetTopLevelIrp(NULL); 4419 4420 FsRtlExitFileSystem(); 4421 4422 return Status; 4423 } 4424