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