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 } 2068 2069 ccfs.AllocationSize = fcb->Header.AllocationSize; 2070 ccfs.FileSize = fcb->Header.FileSize; 2071 ccfs.ValidDataLength = fcb->Header.ValidDataLength; 2072 set_size = TRUE; 2073 2074 filter = FILE_NOTIFY_CHANGE_SIZE; 2075 2076 if (!ccb->user_set_write_time) { 2077 KeQuerySystemTime(&time); 2078 win_time_to_unix(time, &fcb->inode_item.st_mtime); 2079 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 2080 } 2081 2082 fcb->inode_item_changed = TRUE; 2083 mark_fcb_dirty(fcb); 2084 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); 2085 2086 Status = STATUS_SUCCESS; 2087 2088 end: 2089 if (NT_SUCCESS(Status)) 2090 clear_rollback(&rollback); 2091 else 2092 do_rollback(Vcb, &rollback); 2093 2094 ExReleaseResourceLite(fcb->Header.Resource); 2095 2096 if (set_size) { 2097 _SEH2_TRY { 2098 CcSetFileSizes(FileObject, &ccfs); 2099 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 2100 Status = _SEH2_GetExceptionCode(); 2101 } _SEH2_END; 2102 2103 if (!NT_SUCCESS(Status)) 2104 ERR("CcSetFileSizes threw exception %08x\n", Status); 2105 } 2106 2107 ExReleaseResourceLite(&Vcb->tree_lock); 2108 2109 return Status; 2110 } 2111 2112 static NTSTATUS set_position_information(PFILE_OBJECT FileObject, PIRP Irp) { 2113 FILE_POSITION_INFORMATION* fpi = (FILE_POSITION_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; 2114 2115 TRACE("setting the position on %S to %llx\n", file_desc(FileObject), fpi->CurrentByteOffset.QuadPart); 2116 2117 // FIXME - make sure aligned for FO_NO_INTERMEDIATE_BUFFERING 2118 2119 FileObject->CurrentByteOffset = fpi->CurrentByteOffset; 2120 2121 return STATUS_SUCCESS; 2122 } 2123 2124 static NTSTATUS set_link_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject, PFILE_OBJECT tfo) { 2125 FILE_LINK_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer; 2126 fcb *fcb = FileObject->FsContext, *tfofcb, *parfcb; 2127 ccb* ccb = FileObject->FsContext2; 2128 file_ref *fileref = ccb ? ccb->fileref : NULL, *oldfileref = NULL, *related = NULL, *fr2 = NULL; 2129 WCHAR* fn; 2130 ULONG fnlen, utf8len; 2131 UNICODE_STRING fnus; 2132 ANSI_STRING utf8; 2133 NTSTATUS Status; 2134 LARGE_INTEGER time; 2135 BTRFS_TIME now; 2136 LIST_ENTRY rollback; 2137 hardlink* hl; 2138 ACCESS_MASK access; 2139 SECURITY_SUBJECT_CONTEXT subjcont; 2140 dir_child* dc = NULL; 2141 2142 InitializeListHead(&rollback); 2143 2144 // FIXME - check fli length 2145 // FIXME - don't ignore fli->RootDirectory 2146 2147 TRACE("ReplaceIfExists = %x\n", fli->ReplaceIfExists); 2148 TRACE("RootDirectory = %p\n", fli->RootDirectory); 2149 TRACE("FileNameLength = %x\n", fli->FileNameLength); 2150 TRACE("FileName = %.*S\n", fli->FileNameLength / sizeof(WCHAR), fli->FileName); 2151 2152 fn = fli->FileName; 2153 fnlen = fli->FileNameLength / sizeof(WCHAR); 2154 2155 if (!tfo) { 2156 if (!fileref || !fileref->parent) { 2157 ERR("no fileref set and no directory given\n"); 2158 return STATUS_INVALID_PARAMETER; 2159 } 2160 2161 parfcb = fileref->parent->fcb; 2162 tfofcb = NULL; 2163 } else { 2164 LONG i; 2165 2166 tfofcb = tfo->FsContext; 2167 parfcb = tfofcb; 2168 2169 while (fnlen > 0 && (fli->FileName[fnlen - 1] == '/' || fli->FileName[fnlen - 1] == '\\')) 2170 fnlen--; 2171 2172 if (fnlen == 0) 2173 return STATUS_INVALID_PARAMETER; 2174 2175 for (i = fnlen - 1; i >= 0; i--) { 2176 if (fli->FileName[i] == '\\' || fli->FileName[i] == '/') { 2177 fn = &fli->FileName[i+1]; 2178 fnlen = (fli->FileNameLength / sizeof(WCHAR)) - i - 1; 2179 break; 2180 } 2181 } 2182 } 2183 2184 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 2185 acquire_fcb_lock_exclusive(Vcb); 2186 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 2187 2188 if (fcb->type == BTRFS_TYPE_DIRECTORY) { 2189 WARN("tried to create hard link on directory\n"); 2190 Status = STATUS_FILE_IS_A_DIRECTORY; 2191 goto end; 2192 } 2193 2194 if (fcb->ads) { 2195 WARN("tried to create hard link on stream\n"); 2196 Status = STATUS_INVALID_PARAMETER; 2197 goto end; 2198 } 2199 2200 if (fcb->inode_item.st_nlink >= 65535) { 2201 Status = STATUS_TOO_MANY_LINKS; 2202 goto end; 2203 } 2204 2205 fnus.Buffer = fn; 2206 fnus.Length = fnus.MaximumLength = (UINT16)(fnlen * sizeof(WCHAR)); 2207 2208 TRACE("fnus = %.*S\n", fnus.Length / sizeof(WCHAR), fnus.Buffer); 2209 2210 Status = RtlUnicodeToUTF8N(NULL, 0, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 2211 if (!NT_SUCCESS(Status)) 2212 goto end; 2213 2214 utf8.MaximumLength = utf8.Length = (UINT16)utf8len; 2215 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); 2216 if (!utf8.Buffer) { 2217 ERR("out of memory\n"); 2218 Status = STATUS_INSUFFICIENT_RESOURCES; 2219 goto end; 2220 } 2221 2222 Status = RtlUnicodeToUTF8N(utf8.Buffer, utf8len, &utf8len, fn, (ULONG)fnlen * sizeof(WCHAR)); 2223 if (!NT_SUCCESS(Status)) 2224 goto end; 2225 2226 if (tfo && tfo->FsContext2) { 2227 struct _ccb* relatedccb = tfo->FsContext2; 2228 2229 related = relatedccb->fileref; 2230 increase_fileref_refcount(related); 2231 } 2232 2233 Status = open_fileref(Vcb, &oldfileref, &fnus, related, FALSE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 2234 2235 if (NT_SUCCESS(Status)) { 2236 if (!oldfileref->deleted) { 2237 WARN("destination file %S already exists\n", file_desc_fileref(oldfileref)); 2238 2239 if (!fli->ReplaceIfExists) { 2240 Status = STATUS_OBJECT_NAME_COLLISION; 2241 goto end; 2242 } else if (oldfileref->open_count >= 1 && !oldfileref->deleted) { 2243 WARN("trying to overwrite open file\n"); 2244 Status = STATUS_ACCESS_DENIED; 2245 goto end; 2246 } else if (fileref == oldfileref) { 2247 Status = STATUS_ACCESS_DENIED; 2248 goto end; 2249 } 2250 2251 if (oldfileref->fcb->type == BTRFS_TYPE_DIRECTORY) { 2252 WARN("trying to overwrite directory\n"); 2253 Status = STATUS_ACCESS_DENIED; 2254 goto end; 2255 } 2256 } else { 2257 free_fileref(Vcb, oldfileref); 2258 oldfileref = NULL; 2259 } 2260 } 2261 2262 if (!related) { 2263 Status = open_fileref(Vcb, &related, &fnus, NULL, TRUE, NULL, NULL, PagedPool, ccb->case_sensitive, Irp); 2264 2265 if (!NT_SUCCESS(Status)) { 2266 ERR("open_fileref returned %08x\n", Status); 2267 goto end; 2268 } 2269 } 2270 2271 SeCaptureSubjectContext(&subjcont); 2272 2273 if (!SeAccessCheck(related->fcb->sd, &subjcont, FALSE, FILE_ADD_FILE, 0, NULL, 2274 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 2275 SeReleaseSubjectContext(&subjcont); 2276 TRACE("SeAccessCheck failed, returning %08x\n", Status); 2277 goto end; 2278 } 2279 2280 SeReleaseSubjectContext(&subjcont); 2281 2282 if (fcb->subvol != parfcb->subvol) { 2283 WARN("can't create hard link over subvolume boundary\n"); 2284 Status = STATUS_INVALID_PARAMETER; 2285 goto end; 2286 } 2287 2288 if (oldfileref) { 2289 SeCaptureSubjectContext(&subjcont); 2290 2291 if (!SeAccessCheck(oldfileref->fcb->sd, &subjcont, FALSE, DELETE, 0, NULL, 2292 IoGetFileObjectGenericMapping(), Irp->RequestorMode, &access, &Status)) { 2293 SeReleaseSubjectContext(&subjcont); 2294 TRACE("SeAccessCheck failed, returning %08x\n", Status); 2295 goto end; 2296 } 2297 2298 SeReleaseSubjectContext(&subjcont); 2299 2300 Status = delete_fileref(oldfileref, NULL, Irp, &rollback); 2301 if (!NT_SUCCESS(Status)) { 2302 ERR("delete_fileref returned %08x\n", Status); 2303 goto end; 2304 } 2305 } 2306 2307 fr2 = create_fileref(Vcb); 2308 2309 fr2->fcb = fcb; 2310 fcb->refcount++; 2311 2312 fr2->created = TRUE; 2313 fr2->parent = related; 2314 2315 Status = add_dir_child(related->fcb, fcb->inode, FALSE, &utf8, &fnus, fcb->type, &dc); 2316 if (!NT_SUCCESS(Status)) 2317 WARN("add_dir_child returned %08x\n", Status); 2318 2319 fr2->dc = dc; 2320 dc->fileref = fr2; 2321 2322 ExAcquireResourceExclusiveLite(&related->nonpaged->children_lock, TRUE); 2323 InsertTailList(&related->children, &fr2->list_entry); 2324 ExReleaseResourceLite(&related->nonpaged->children_lock); 2325 2326 // add hardlink for existing fileref, if it's not there already 2327 if (IsListEmpty(&fcb->hardlinks)) { 2328 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 2329 if (!hl) { 2330 ERR("out of memory\n"); 2331 Status = STATUS_INSUFFICIENT_RESOURCES; 2332 goto end; 2333 } 2334 2335 hl->parent = fileref->parent->fcb->inode; 2336 hl->index = fileref->dc->index; 2337 2338 hl->name.Length = hl->name.MaximumLength = fnus.Length; 2339 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, fnus.Length, ALLOC_TAG); 2340 2341 if (!hl->name.Buffer) { 2342 ERR("out of memory\n"); 2343 ExFreePool(hl); 2344 Status = STATUS_INSUFFICIENT_RESOURCES; 2345 goto end; 2346 } 2347 2348 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); 2349 2350 hl->utf8.Length = hl->utf8.MaximumLength = fileref->dc->utf8.Length; 2351 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 2352 2353 if (!hl->utf8.Buffer) { 2354 ERR("out of memory\n"); 2355 ExFreePool(hl->name.Buffer); 2356 ExFreePool(hl); 2357 Status = STATUS_INSUFFICIENT_RESOURCES; 2358 goto end; 2359 } 2360 2361 RtlCopyMemory(hl->utf8.Buffer, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 2362 2363 InsertTailList(&fcb->hardlinks, &hl->list_entry); 2364 } 2365 2366 hl = ExAllocatePoolWithTag(PagedPool, sizeof(hardlink), ALLOC_TAG); 2367 if (!hl) { 2368 ERR("out of memory\n"); 2369 Status = STATUS_INSUFFICIENT_RESOURCES; 2370 goto end; 2371 } 2372 2373 hl->parent = related->fcb->inode; 2374 hl->index = dc->index; 2375 2376 hl->name.Length = hl->name.MaximumLength = fnus.Length; 2377 hl->name.Buffer = ExAllocatePoolWithTag(PagedPool, hl->name.MaximumLength, ALLOC_TAG); 2378 2379 if (!hl->name.Buffer) { 2380 ERR("out of memory\n"); 2381 ExFreePool(hl); 2382 Status = STATUS_INSUFFICIENT_RESOURCES; 2383 goto end; 2384 } 2385 2386 RtlCopyMemory(hl->name.Buffer, fnus.Buffer, fnus.Length); 2387 2388 hl->utf8.Length = hl->utf8.MaximumLength = utf8.Length; 2389 hl->utf8.Buffer = ExAllocatePoolWithTag(PagedPool, hl->utf8.MaximumLength, ALLOC_TAG); 2390 2391 if (!hl->utf8.Buffer) { 2392 ERR("out of memory\n"); 2393 ExFreePool(hl->name.Buffer); 2394 ExFreePool(hl); 2395 Status = STATUS_INSUFFICIENT_RESOURCES; 2396 goto end; 2397 } 2398 2399 RtlCopyMemory(hl->utf8.Buffer, utf8.Buffer, utf8.Length); 2400 ExFreePool(utf8.Buffer); 2401 2402 InsertTailList(&fcb->hardlinks, &hl->list_entry); 2403 2404 mark_fileref_dirty(fr2); 2405 free_fileref(Vcb, fr2); 2406 2407 // update inode's INODE_ITEM 2408 2409 KeQuerySystemTime(&time); 2410 win_time_to_unix(time, &now); 2411 2412 fcb->inode_item.transid = Vcb->superblock.generation; 2413 fcb->inode_item.sequence++; 2414 fcb->inode_item.st_nlink++; 2415 2416 if (!ccb->user_set_change_time) 2417 fcb->inode_item.st_ctime = now; 2418 2419 fcb->inode_item_changed = TRUE; 2420 mark_fcb_dirty(fcb); 2421 2422 // update parent's INODE_ITEM 2423 2424 parfcb->inode_item.transid = Vcb->superblock.generation; 2425 TRACE("parfcb->inode_item.st_size (inode %llx) was %llx\n", parfcb->inode, parfcb->inode_item.st_size); 2426 parfcb->inode_item.st_size += 2 * utf8len; 2427 TRACE("parfcb->inode_item.st_size (inode %llx) now %llx\n", parfcb->inode, parfcb->inode_item.st_size); 2428 parfcb->inode_item.sequence++; 2429 parfcb->inode_item.st_ctime = now; 2430 2431 parfcb->inode_item_changed = TRUE; 2432 mark_fcb_dirty(parfcb); 2433 2434 send_notification_fileref(fr2, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); 2435 2436 Status = STATUS_SUCCESS; 2437 2438 end: 2439 if (oldfileref) 2440 free_fileref(Vcb, oldfileref); 2441 2442 if (!NT_SUCCESS(Status) && related) 2443 free_fileref(Vcb, related); 2444 2445 if (!NT_SUCCESS(Status) && fr2) 2446 free_fileref(Vcb, fr2); 2447 2448 if (NT_SUCCESS(Status)) 2449 clear_rollback(&rollback); 2450 else 2451 do_rollback(Vcb, &rollback); 2452 2453 ExReleaseResourceLite(fcb->Header.Resource); 2454 release_fcb_lock(Vcb); 2455 ExReleaseResourceLite(&Vcb->tree_lock); 2456 2457 return Status; 2458 } 2459 2460 static NTSTATUS set_valid_data_length_information(device_extension* Vcb, PIRP Irp, PFILE_OBJECT FileObject) { 2461 FILE_VALID_DATA_LENGTH_INFORMATION* fvdli = Irp->AssociatedIrp.SystemBuffer; 2462 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2463 fcb* fcb = FileObject->FsContext; 2464 ccb* ccb = FileObject->FsContext2; 2465 file_ref* fileref = ccb ? ccb->fileref : NULL; 2466 NTSTATUS Status; 2467 LARGE_INTEGER time; 2468 CC_FILE_SIZES ccfs; 2469 LIST_ENTRY rollback; 2470 BOOL set_size = FALSE; 2471 ULONG filter; 2472 2473 if (IrpSp->Parameters.SetFile.Length < sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)) { 2474 ERR("input buffer length was %u, expected %u\n", IrpSp->Parameters.SetFile.Length, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)); 2475 return STATUS_INVALID_PARAMETER; 2476 } 2477 2478 if (!fileref) { 2479 ERR("fileref is NULL\n"); 2480 return STATUS_INVALID_PARAMETER; 2481 } 2482 2483 InitializeListHead(&rollback); 2484 2485 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 2486 2487 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 2488 2489 if (fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE) { 2490 Status = STATUS_INVALID_PARAMETER; 2491 goto end; 2492 } 2493 2494 if (fvdli->ValidDataLength.QuadPart <= fcb->Header.ValidDataLength.QuadPart || fvdli->ValidDataLength.QuadPart > fcb->Header.FileSize.QuadPart) { 2495 TRACE("invalid VDL of %llu (current VDL = %llu, file size = %llu)\n", fvdli->ValidDataLength.QuadPart, 2496 fcb->Header.ValidDataLength.QuadPart, fcb->Header.FileSize.QuadPart); 2497 Status = STATUS_INVALID_PARAMETER; 2498 goto end; 2499 } 2500 2501 if (fileref ? fileref->deleted : fcb->deleted) { 2502 Status = STATUS_FILE_CLOSED; 2503 goto end; 2504 } 2505 2506 // This function doesn't really do anything - the fsctl can only increase the value of ValidDataLength, 2507 // and we set it to the max anyway. 2508 2509 ccfs.AllocationSize = fcb->Header.AllocationSize; 2510 ccfs.FileSize = fcb->Header.FileSize; 2511 ccfs.ValidDataLength = fvdli->ValidDataLength; 2512 set_size = TRUE; 2513 2514 filter = FILE_NOTIFY_CHANGE_SIZE; 2515 2516 if (!ccb->user_set_write_time) { 2517 KeQuerySystemTime(&time); 2518 win_time_to_unix(time, &fcb->inode_item.st_mtime); 2519 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 2520 } 2521 2522 fcb->inode_item_changed = TRUE; 2523 mark_fcb_dirty(fcb); 2524 2525 send_notification_fcb(fileref, filter, FILE_ACTION_MODIFIED, NULL); 2526 2527 Status = STATUS_SUCCESS; 2528 2529 end: 2530 if (NT_SUCCESS(Status)) 2531 clear_rollback(&rollback); 2532 else 2533 do_rollback(Vcb, &rollback); 2534 2535 ExReleaseResourceLite(fcb->Header.Resource); 2536 2537 if (set_size) { 2538 _SEH2_TRY { 2539 CcSetFileSizes(FileObject, &ccfs); 2540 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 2541 Status = _SEH2_GetExceptionCode(); 2542 } _SEH2_END; 2543 2544 if (!NT_SUCCESS(Status)) 2545 ERR("CcSetFileSizes threw exception %08x\n", Status); 2546 else 2547 fcb->Header.AllocationSize = ccfs.AllocationSize; 2548 } 2549 2550 ExReleaseResourceLite(&Vcb->tree_lock); 2551 2552 return Status; 2553 } 2554 2555 _Dispatch_type_(IRP_MJ_SET_INFORMATION) 2556 _Function_class_(DRIVER_DISPATCH) 2557 NTSTATUS drv_set_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 2558 NTSTATUS Status; 2559 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2560 device_extension* Vcb = DeviceObject->DeviceExtension; 2561 fcb* fcb = IrpSp->FileObject->FsContext; 2562 ccb* ccb = IrpSp->FileObject->FsContext2; 2563 BOOL top_level; 2564 2565 FsRtlEnterFileSystem(); 2566 2567 top_level = is_top_level(Irp); 2568 2569 Irp->IoStatus.Information = 0; 2570 2571 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 2572 Status = vol_set_information(DeviceObject, Irp); 2573 goto end; 2574 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 2575 Status = STATUS_INVALID_PARAMETER; 2576 goto end; 2577 } 2578 2579 if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) { 2580 Status = STATUS_ACCESS_DENIED; 2581 goto end; 2582 } 2583 2584 if (Vcb->readonly && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation) { 2585 Status = STATUS_MEDIA_WRITE_PROTECTED; 2586 goto end; 2587 } 2588 2589 if (!fcb) { 2590 ERR("no fcb\n"); 2591 Status = STATUS_INVALID_PARAMETER; 2592 goto end; 2593 } 2594 2595 if (!ccb) { 2596 ERR("no ccb\n"); 2597 Status = STATUS_INVALID_PARAMETER; 2598 goto end; 2599 } 2600 2601 if (fcb != Vcb->dummy_fcb && is_subvol_readonly(fcb->subvol, Irp) && IrpSp->Parameters.SetFile.FileInformationClass != FilePositionInformation && 2602 (fcb->inode != SUBVOL_ROOT_INODE || (IrpSp->Parameters.SetFile.FileInformationClass != FileBasicInformation && IrpSp->Parameters.SetFile.FileInformationClass != FileRenameInformation))) { 2603 Status = STATUS_ACCESS_DENIED; 2604 goto end; 2605 } 2606 2607 Status = STATUS_NOT_IMPLEMENTED; 2608 2609 TRACE("set information\n"); 2610 2611 switch (IrpSp->Parameters.SetFile.FileInformationClass) { 2612 case FileAllocationInformation: 2613 { 2614 TRACE("FileAllocationInformation\n"); 2615 2616 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) { 2617 WARN("insufficient privileges\n"); 2618 Status = STATUS_ACCESS_DENIED; 2619 break; 2620 } 2621 2622 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, FALSE, TRUE); 2623 break; 2624 } 2625 2626 case FileBasicInformation: 2627 { 2628 TRACE("FileBasicInformation\n"); 2629 2630 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { 2631 WARN("insufficient privileges\n"); 2632 Status = STATUS_ACCESS_DENIED; 2633 break; 2634 } 2635 2636 Status = set_basic_information(Vcb, Irp, IrpSp->FileObject); 2637 2638 break; 2639 } 2640 2641 case FileDispositionInformation: 2642 { 2643 TRACE("FileDispositionInformation\n"); 2644 2645 if (Irp->RequestorMode == UserMode && !(ccb->access & DELETE)) { 2646 WARN("insufficient privileges\n"); 2647 Status = STATUS_ACCESS_DENIED; 2648 break; 2649 } 2650 2651 Status = set_disposition_information(Vcb, Irp, IrpSp->FileObject); 2652 2653 break; 2654 } 2655 2656 case FileEndOfFileInformation: 2657 { 2658 TRACE("FileEndOfFileInformation\n"); 2659 2660 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { 2661 WARN("insufficient privileges\n"); 2662 Status = STATUS_ACCESS_DENIED; 2663 break; 2664 } 2665 2666 Status = set_end_of_file_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.AdvanceOnly, FALSE); 2667 2668 break; 2669 } 2670 2671 case FileLinkInformation: 2672 TRACE("FileLinkInformation\n"); 2673 Status = set_link_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject); 2674 break; 2675 2676 case FilePositionInformation: 2677 TRACE("FilePositionInformation\n"); 2678 Status = set_position_information(IrpSp->FileObject, Irp); 2679 break; 2680 2681 case FileRenameInformation: 2682 TRACE("FileRenameInformation\n"); 2683 // FIXME - make this work with streams 2684 Status = set_rename_information(Vcb, Irp, IrpSp->FileObject, IrpSp->Parameters.SetFile.FileObject); 2685 break; 2686 2687 case FileValidDataLengthInformation: 2688 { 2689 TRACE("FileValidDataLengthInformation\n"); 2690 2691 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { 2692 WARN("insufficient privileges\n"); 2693 Status = STATUS_ACCESS_DENIED; 2694 break; 2695 } 2696 2697 Status = set_valid_data_length_information(Vcb, Irp, IrpSp->FileObject); 2698 2699 break; 2700 } 2701 2702 default: 2703 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.SetFile.FileInformationClass); 2704 } 2705 2706 end: 2707 Irp->IoStatus.Status = Status; 2708 2709 TRACE("returning %08x\n", Status); 2710 2711 IoCompleteRequest(Irp, IO_NO_INCREMENT); 2712 2713 if (top_level) 2714 IoSetTopLevelIrp(NULL); 2715 2716 FsRtlExitFileSystem(); 2717 2718 return Status; 2719 } 2720 2721 static NTSTATUS fill_in_file_basic_information(FILE_BASIC_INFORMATION* fbi, INODE_ITEM* ii, LONG* length, fcb* fcb, file_ref* fileref) { 2722 RtlZeroMemory(fbi, sizeof(FILE_BASIC_INFORMATION)); 2723 2724 *length -= sizeof(FILE_BASIC_INFORMATION); 2725 2726 if (fcb == fcb->Vcb->dummy_fcb) { 2727 LARGE_INTEGER time; 2728 2729 KeQuerySystemTime(&time); 2730 fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time; 2731 } else { 2732 fbi->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 2733 fbi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 2734 fbi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 2735 fbi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 2736 } 2737 2738 if (fcb->ads) { 2739 if (!fileref || !fileref->parent) { 2740 ERR("no fileref for stream\n"); 2741 return STATUS_INTERNAL_ERROR; 2742 } else 2743 fbi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts; 2744 } else 2745 fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 2746 2747 return STATUS_SUCCESS; 2748 } 2749 2750 static NTSTATUS fill_in_file_network_open_information(FILE_NETWORK_OPEN_INFORMATION* fnoi, fcb* fcb, file_ref* fileref, LONG* length) { 2751 INODE_ITEM* ii; 2752 2753 if (*length < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) { 2754 WARN("overflow\n"); 2755 return STATUS_BUFFER_OVERFLOW; 2756 } 2757 2758 RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION)); 2759 2760 *length -= sizeof(FILE_NETWORK_OPEN_INFORMATION); 2761 2762 if (fcb->ads) { 2763 if (!fileref || !fileref->parent) { 2764 ERR("no fileref for stream\n"); 2765 return STATUS_INTERNAL_ERROR; 2766 } 2767 2768 ii = &fileref->parent->fcb->inode_item; 2769 } else 2770 ii = &fcb->inode_item; 2771 2772 if (fcb == fcb->Vcb->dummy_fcb) { 2773 LARGE_INTEGER time; 2774 2775 KeQuerySystemTime(&time); 2776 fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time; 2777 } else { 2778 fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 2779 fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 2780 fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 2781 fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 2782 } 2783 2784 if (fcb->ads) { 2785 fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length; 2786 fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts; 2787 } else { 2788 fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 2789 fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 2790 fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 2791 } 2792 2793 return STATUS_SUCCESS; 2794 } 2795 2796 static NTSTATUS fill_in_file_standard_information(FILE_STANDARD_INFORMATION* fsi, fcb* fcb, file_ref* fileref, LONG* length) { 2797 RtlZeroMemory(fsi, sizeof(FILE_STANDARD_INFORMATION)); 2798 2799 *length -= sizeof(FILE_STANDARD_INFORMATION); 2800 2801 if (fcb->ads) { 2802 if (!fileref || !fileref->parent) { 2803 ERR("no fileref for stream\n"); 2804 return STATUS_INTERNAL_ERROR; 2805 } 2806 2807 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = fcb->adsdata.Length; 2808 fsi->NumberOfLinks = fileref->parent->fcb->inode_item.st_nlink; 2809 fsi->Directory = FALSE; 2810 } else { 2811 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 2812 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 2813 fsi->NumberOfLinks = fcb->inode_item.st_nlink; 2814 fsi->Directory = S_ISDIR(fcb->inode_item.st_mode); 2815 } 2816 2817 TRACE("length = %llu\n", fsi->EndOfFile.QuadPart); 2818 2819 fsi->DeletePending = fileref ? fileref->delete_on_close : FALSE; 2820 2821 return STATUS_SUCCESS; 2822 } 2823 2824 static NTSTATUS fill_in_file_internal_information(FILE_INTERNAL_INFORMATION* fii, fcb* fcb, LONG* length) { 2825 *length -= sizeof(FILE_INTERNAL_INFORMATION); 2826 2827 fii->IndexNumber.QuadPart = make_file_id(fcb->subvol, fcb->inode); 2828 2829 return STATUS_SUCCESS; 2830 } 2831 2832 static NTSTATUS fill_in_file_ea_information(FILE_EA_INFORMATION* eai, fcb* fcb, LONG* length) { 2833 *length -= sizeof(FILE_EA_INFORMATION); 2834 2835 /* This value appears to be the size of the structure NTFS stores on disk, and not, 2836 * as might be expected, the size of FILE_FULL_EA_INFORMATION (which is what we store). 2837 * The formula is 4 bytes as a header, followed by 5 + NameLength + ValueLength for each 2838 * item. */ 2839 2840 eai->EaSize = fcb->ealen; 2841 2842 return STATUS_SUCCESS; 2843 } 2844 2845 static NTSTATUS fill_in_file_position_information(FILE_POSITION_INFORMATION* fpi, PFILE_OBJECT FileObject, LONG* length) { 2846 RtlZeroMemory(fpi, sizeof(FILE_POSITION_INFORMATION)); 2847 2848 *length -= sizeof(FILE_POSITION_INFORMATION); 2849 2850 fpi->CurrentByteOffset = FileObject->CurrentByteOffset; 2851 2852 return STATUS_SUCCESS; 2853 } 2854 2855 NTSTATUS fileref_get_filename(file_ref* fileref, PUNICODE_STRING fn, USHORT* name_offset, ULONG* preqlen) { 2856 file_ref* fr; 2857 NTSTATUS Status; 2858 ULONG reqlen = 0; 2859 USHORT offset; 2860 BOOL overflow = FALSE; 2861 2862 // FIXME - we need a lock on filerefs' filepart 2863 2864 if (fileref == fileref->fcb->Vcb->root_fileref) { 2865 if (fn->MaximumLength >= sizeof(WCHAR)) { 2866 fn->Buffer[0] = '\\'; 2867 fn->Length = sizeof(WCHAR); 2868 2869 if (name_offset) 2870 *name_offset = 0; 2871 2872 return STATUS_SUCCESS; 2873 } else { 2874 if (preqlen) 2875 *preqlen = sizeof(WCHAR); 2876 fn->Length = 0; 2877 2878 return STATUS_BUFFER_OVERFLOW; 2879 } 2880 } 2881 2882 fr = fileref; 2883 offset = 0; 2884 2885 while (fr->parent) { 2886 USHORT movelen; 2887 2888 if (!fr->dc) 2889 return STATUS_INTERNAL_ERROR; 2890 2891 if (!overflow) { 2892 if (fr->dc->name.Length + sizeof(WCHAR) + fn->Length > fn->MaximumLength) 2893 overflow = TRUE; 2894 } 2895 2896 if (overflow) 2897 movelen = fn->MaximumLength - fr->dc->name.Length - sizeof(WCHAR); 2898 else 2899 movelen = fn->Length; 2900 2901 if ((!overflow || fn->MaximumLength > fr->dc->name.Length + sizeof(WCHAR)) && movelen > 0) { 2902 RtlMoveMemory(&fn->Buffer[(fr->dc->name.Length / sizeof(WCHAR)) + 1], fn->Buffer, movelen); 2903 offset += fr->dc->name.Length + sizeof(WCHAR); 2904 } 2905 2906 if (fn->MaximumLength >= sizeof(WCHAR)) { 2907 fn->Buffer[0] = fr->fcb->ads ? ':' : '\\'; 2908 fn->Length += sizeof(WCHAR); 2909 2910 if (fn->MaximumLength > sizeof(WCHAR)) { 2911 RtlCopyMemory(&fn->Buffer[1], fr->dc->name.Buffer, min(fr->dc->name.Length, fn->MaximumLength - sizeof(WCHAR))); 2912 fn->Length += fr->dc->name.Length; 2913 } 2914 2915 if (fn->Length > fn->MaximumLength) { 2916 fn->Length = fn->MaximumLength; 2917 overflow = TRUE; 2918 } 2919 } 2920 2921 reqlen += sizeof(WCHAR) + fr->dc->name.Length; 2922 2923 fr = fr->parent; 2924 } 2925 2926 offset += sizeof(WCHAR); 2927 2928 if (overflow) { 2929 if (preqlen) 2930 *preqlen = reqlen; 2931 Status = STATUS_BUFFER_OVERFLOW; 2932 } else { 2933 if (name_offset) 2934 *name_offset = offset; 2935 2936 Status = STATUS_SUCCESS; 2937 } 2938 2939 return Status; 2940 } 2941 2942 static NTSTATUS fill_in_file_name_information(FILE_NAME_INFORMATION* fni, fcb* fcb, file_ref* fileref, LONG* length) { 2943 ULONG reqlen; 2944 UNICODE_STRING fn; 2945 NTSTATUS Status; 2946 static WCHAR datasuf[] = {':','$','D','A','T','A',0}; 2947 UINT16 datasuflen = (UINT16)wcslen(datasuf) * sizeof(WCHAR); 2948 2949 if (!fileref) { 2950 ERR("called without fileref\n"); 2951 return STATUS_INVALID_PARAMETER; 2952 } 2953 2954 *length -= (LONG)offsetof(FILE_NAME_INFORMATION, FileName[0]); 2955 2956 TRACE("maximum length is %u\n", *length); 2957 fni->FileNameLength = 0; 2958 2959 fni->FileName[0] = 0; 2960 2961 fn.Buffer = fni->FileName; 2962 fn.Length = 0; 2963 fn.MaximumLength = (UINT16)*length; 2964 2965 Status = fileref_get_filename(fileref, &fn, NULL, &reqlen); 2966 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) { 2967 ERR("fileref_get_filename returned %08x\n", Status); 2968 return Status; 2969 } 2970 2971 if (fcb->ads) { 2972 if (Status == STATUS_BUFFER_OVERFLOW) 2973 reqlen += datasuflen; 2974 else { 2975 if (fn.Length + datasuflen > fn.MaximumLength) { 2976 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, fn.MaximumLength - fn.Length); 2977 reqlen += datasuflen; 2978 Status = STATUS_BUFFER_OVERFLOW; 2979 } else { 2980 RtlCopyMemory(&fn.Buffer[fn.Length / sizeof(WCHAR)], datasuf, datasuflen); 2981 fn.Length += datasuflen; 2982 } 2983 } 2984 } 2985 2986 if (Status == STATUS_BUFFER_OVERFLOW) { 2987 *length = -1; 2988 fni->FileNameLength = reqlen; 2989 TRACE("%.*S (truncated)\n", fn.Length / sizeof(WCHAR), fn.Buffer); 2990 } else { 2991 *length -= fn.Length; 2992 fni->FileNameLength = fn.Length; 2993 TRACE("%.*S\n", fn.Length / sizeof(WCHAR), fn.Buffer); 2994 } 2995 2996 return Status; 2997 } 2998 2999 static NTSTATUS fill_in_file_attribute_information(FILE_ATTRIBUTE_TAG_INFORMATION* ati, fcb* fcb, ccb* ccb, PIRP Irp, LONG* length) { 3000 *length -= sizeof(FILE_ATTRIBUTE_TAG_INFORMATION); 3001 3002 if (fcb->ads) { 3003 if (!ccb->fileref || !ccb->fileref->parent) { 3004 ERR("no fileref for stream\n"); 3005 return STATUS_INTERNAL_ERROR; 3006 } 3007 3008 ati->FileAttributes = ccb->fileref->parent->fcb->atts; 3009 } else 3010 ati->FileAttributes = fcb->atts; 3011 3012 if (!(ati->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) 3013 ati->ReparseTag = 0; 3014 else 3015 ati->ReparseTag = get_reparse_tag(fcb->Vcb, fcb->subvol, fcb->inode, fcb->type, fcb->atts, ccb->lxss, Irp); 3016 3017 return STATUS_SUCCESS; 3018 } 3019 3020 static NTSTATUS fill_in_file_stream_information(FILE_STREAM_INFORMATION* fsi, file_ref* fileref, LONG* length) { 3021 LONG reqsize; 3022 LIST_ENTRY* le; 3023 FILE_STREAM_INFORMATION *entry, *lastentry; 3024 NTSTATUS Status; 3025 3026 static WCHAR datasuf[] = L":$DATA"; 3027 UNICODE_STRING suf; 3028 3029 if (!fileref) { 3030 ERR("fileref was NULL\n"); 3031 return STATUS_INVALID_PARAMETER; 3032 } 3033 3034 suf.Buffer = datasuf; 3035 suf.Length = suf.MaximumLength = (UINT16)wcslen(datasuf) * sizeof(WCHAR); 3036 3037 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) 3038 reqsize = sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR); 3039 else 3040 reqsize = 0; 3041 3042 ExAcquireResourceSharedLite(&fileref->fcb->nonpaged->dir_children_lock, TRUE); 3043 3044 le = fileref->fcb->dir_children_index.Flink; 3045 while (le != &fileref->fcb->dir_children_index) { 3046 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); 3047 3048 if (dc->index == 0) { 3049 reqsize = (ULONG)sector_align(reqsize, sizeof(LONGLONG)); 3050 reqsize += sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length; 3051 } else 3052 break; 3053 3054 le = le->Flink; 3055 } 3056 3057 TRACE("length = %i, reqsize = %u\n", *length, reqsize); 3058 3059 if (reqsize > *length) { 3060 Status = STATUS_BUFFER_OVERFLOW; 3061 goto end; 3062 } 3063 3064 entry = fsi; 3065 lastentry = NULL; 3066 3067 if (fileref->fcb->type != BTRFS_TYPE_DIRECTORY) { 3068 ULONG off; 3069 3070 entry->NextEntryOffset = 0; 3071 entry->StreamNameLength = suf.Length + sizeof(WCHAR); 3072 entry->StreamSize.QuadPart = fileref->fcb->inode_item.st_size; 3073 entry->StreamAllocationSize.QuadPart = fcb_alloc_size(fileref->fcb); 3074 3075 entry->StreamName[0] = ':'; 3076 RtlCopyMemory(&entry->StreamName[1], suf.Buffer, suf.Length); 3077 3078 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR), sizeof(LONGLONG)); 3079 3080 lastentry = entry; 3081 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off); 3082 } 3083 3084 le = fileref->fcb->dir_children_index.Flink; 3085 while (le != &fileref->fcb->dir_children_index) { 3086 dir_child* dc = CONTAINING_RECORD(le, dir_child, list_entry_index); 3087 3088 if (dc->index == 0) { 3089 ULONG off; 3090 3091 entry->NextEntryOffset = 0; 3092 entry->StreamNameLength = dc->name.Length + suf.Length + sizeof(WCHAR); 3093 3094 if (dc->fileref) 3095 entry->StreamSize.QuadPart = dc->fileref->fcb->adsdata.Length; 3096 else 3097 entry->StreamSize.QuadPart = dc->size; 3098 3099 entry->StreamAllocationSize.QuadPart = entry->StreamSize.QuadPart; 3100 3101 entry->StreamName[0] = ':'; 3102 3103 RtlCopyMemory(&entry->StreamName[1], dc->name.Buffer, dc->name.Length); 3104 RtlCopyMemory(&entry->StreamName[1 + (dc->name.Length / sizeof(WCHAR))], suf.Buffer, suf.Length); 3105 3106 if (lastentry) 3107 lastentry->NextEntryOffset = (UINT32)((UINT8*)entry - (UINT8*)lastentry); 3108 3109 off = (ULONG)sector_align(sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR) + suf.Length + sizeof(WCHAR) + dc->name.Length, sizeof(LONGLONG)); 3110 3111 lastentry = entry; 3112 entry = (FILE_STREAM_INFORMATION*)((UINT8*)entry + off); 3113 } else 3114 break; 3115 3116 le = le->Flink; 3117 } 3118 3119 *length -= reqsize; 3120 3121 Status = STATUS_SUCCESS; 3122 3123 end: 3124 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock); 3125 3126 return Status; 3127 } 3128 3129 #ifndef __REACTOS__ 3130 static NTSTATUS fill_in_file_standard_link_information(FILE_STANDARD_LINK_INFORMATION* fsli, fcb* fcb, file_ref* fileref, LONG* length) { 3131 TRACE("FileStandardLinkInformation\n"); 3132 3133 // FIXME - NumberOfAccessibleLinks should subtract open links which have been marked as delete_on_close 3134 3135 fsli->NumberOfAccessibleLinks = fcb->inode_item.st_nlink; 3136 fsli->TotalNumberOfLinks = fcb->inode_item.st_nlink; 3137 fsli->DeletePending = fileref ? fileref->delete_on_close : FALSE; 3138 fsli->Directory = (!fcb->ads && fcb->type == BTRFS_TYPE_DIRECTORY) ? TRUE : FALSE; 3139 3140 *length -= sizeof(FILE_STANDARD_LINK_INFORMATION); 3141 3142 return STATUS_SUCCESS; 3143 } 3144 #endif /* __REACTOS__ */ 3145 3146 NTSTATUS open_fileref_by_inode(_Requires_exclusive_lock_held_(_Curr_->fcb_lock) device_extension* Vcb, 3147 root* subvol, UINT64 inode, file_ref** pfr, PIRP Irp) { 3148 NTSTATUS Status; 3149 fcb* fcb; 3150 UINT64 parent = 0; 3151 UNICODE_STRING name; 3152 BOOL hl_alloc = FALSE; 3153 file_ref *parfr, *fr; 3154 3155 Status = open_fcb(Vcb, subvol, inode, 0, NULL, NULL, &fcb, PagedPool, Irp); 3156 if (!NT_SUCCESS(Status)) { 3157 ERR("open_fcb returned %08x\n", Status); 3158 return Status; 3159 } 3160 3161 if (fcb->fileref) { 3162 *pfr = fcb->fileref; 3163 increase_fileref_refcount(fcb->fileref); 3164 return STATUS_SUCCESS; 3165 } 3166 3167 // find hardlink if fcb doesn't have any loaded 3168 if (IsListEmpty(&fcb->hardlinks)) { 3169 KEY searchkey; 3170 traverse_ptr tp; 3171 3172 searchkey.obj_id = fcb->inode; 3173 searchkey.obj_type = TYPE_INODE_EXTREF; 3174 searchkey.offset = 0xffffffffffffffff; 3175 3176 Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE, Irp); 3177 if (!NT_SUCCESS(Status)) { 3178 ERR("find_item returned %08x\n", Status); 3179 free_fcb(Vcb, fcb); 3180 return Status; 3181 } 3182 3183 if (tp.item->key.obj_id == fcb->inode) { 3184 if (tp.item->key.obj_type == TYPE_INODE_REF) { 3185 INODE_REF* ir; 3186 ULONG stringlen; 3187 3188 ir = (INODE_REF*)tp.item->data; 3189 3190 parent = tp.item->key.offset; 3191 3192 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ir->name, ir->n); 3193 if (!NT_SUCCESS(Status)) { 3194 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); 3195 free_fcb(Vcb, fcb); 3196 return Status; 3197 } 3198 3199 name.Length = name.MaximumLength = (UINT16)stringlen; 3200 3201 if (stringlen == 0) 3202 name.Buffer = NULL; 3203 else { 3204 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG); 3205 3206 if (!name.Buffer) { 3207 ERR("out of memory\n"); 3208 free_fcb(Vcb, fcb); 3209 return STATUS_INSUFFICIENT_RESOURCES; 3210 } 3211 3212 Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ir->name, ir->n); 3213 if (!NT_SUCCESS(Status)) { 3214 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); 3215 ExFreePool(name.Buffer); 3216 free_fcb(Vcb, fcb); 3217 return Status; 3218 } 3219 3220 hl_alloc = TRUE; 3221 } 3222 } else if (tp.item->key.obj_type == TYPE_INODE_EXTREF) { 3223 INODE_EXTREF* ier; 3224 ULONG stringlen; 3225 3226 ier = (INODE_EXTREF*)tp.item->data; 3227 3228 parent = ier->dir; 3229 3230 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, ier->name, ier->n); 3231 if (!NT_SUCCESS(Status)) { 3232 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); 3233 free_fcb(Vcb, fcb); 3234 return Status; 3235 } 3236 3237 name.Length = name.MaximumLength = (UINT16)stringlen; 3238 3239 if (stringlen == 0) 3240 name.Buffer = NULL; 3241 else { 3242 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG); 3243 3244 if (!name.Buffer) { 3245 ERR("out of memory\n"); 3246 free_fcb(Vcb, fcb); 3247 return STATUS_INSUFFICIENT_RESOURCES; 3248 } 3249 3250 Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, ier->name, ier->n); 3251 if (!NT_SUCCESS(Status)) { 3252 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); 3253 ExFreePool(name.Buffer); 3254 free_fcb(Vcb, fcb); 3255 return Status; 3256 } 3257 3258 hl_alloc = TRUE; 3259 } 3260 3261 } 3262 } 3263 } else { 3264 hardlink* hl = CONTAINING_RECORD(fcb->hardlinks.Flink, hardlink, list_entry); 3265 3266 name = hl->name; 3267 parent = hl->parent; 3268 } 3269 3270 if (parent == 0) { 3271 ERR("subvol %llx, inode %llx has no hardlinks\n", subvol->id, inode); 3272 free_fcb(Vcb, fcb); 3273 if (hl_alloc) ExFreePool(name.Buffer); 3274 return STATUS_INVALID_PARAMETER; 3275 } 3276 3277 if (parent == inode) { // subvolume root 3278 KEY searchkey; 3279 traverse_ptr tp; 3280 3281 searchkey.obj_id = subvol->id; 3282 searchkey.obj_type = TYPE_ROOT_BACKREF; 3283 searchkey.offset = 0xffffffffffffffff; 3284 3285 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, FALSE, Irp); 3286 if (!NT_SUCCESS(Status)) { 3287 ERR("find_item returned %08x\n", Status); 3288 free_fcb(Vcb, fcb); 3289 if (hl_alloc) ExFreePool(name.Buffer); 3290 return Status; 3291 } 3292 3293 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 3294 ROOT_REF* rr = (ROOT_REF*)tp.item->data; 3295 LIST_ENTRY* le; 3296 root* r = NULL; 3297 ULONG stringlen; 3298 3299 if (tp.item->size < sizeof(ROOT_REF)) { 3300 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)); 3301 free_fcb(Vcb, fcb); 3302 if (hl_alloc) ExFreePool(name.Buffer); 3303 return STATUS_INTERNAL_ERROR; 3304 } 3305 3306 if (tp.item->size < offsetof(ROOT_REF, name[0]) + rr->n) { 3307 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); 3308 free_fcb(Vcb, fcb); 3309 if (hl_alloc) ExFreePool(name.Buffer); 3310 return STATUS_INTERNAL_ERROR; 3311 } 3312 3313 le = Vcb->roots.Flink; 3314 while (le != &Vcb->roots) { 3315 root* r2 = CONTAINING_RECORD(le, root, list_entry); 3316 3317 if (r2->id == tp.item->key.offset) { 3318 r = r2; 3319 break; 3320 } 3321 3322 le = le->Flink; 3323 } 3324 3325 if (!r) { 3326 ERR("couldn't find subvol %llx\n", tp.item->key.offset); 3327 free_fcb(Vcb, fcb); 3328 if (hl_alloc) ExFreePool(name.Buffer); 3329 return STATUS_INTERNAL_ERROR; 3330 } 3331 3332 Status = open_fileref_by_inode(Vcb, r, rr->dir, &parfr, Irp); 3333 if (!NT_SUCCESS(Status)) { 3334 ERR("open_fileref_by_inode returned %08x\n", Status); 3335 free_fcb(Vcb, fcb); 3336 if (hl_alloc) ExFreePool(name.Buffer); 3337 return Status; 3338 } 3339 3340 if (hl_alloc) { 3341 ExFreePool(name.Buffer); 3342 hl_alloc = FALSE; 3343 } 3344 3345 Status = RtlUTF8ToUnicodeN(NULL, 0, &stringlen, rr->name, rr->n); 3346 if (!NT_SUCCESS(Status)) { 3347 ERR("RtlUTF8ToUnicodeN 1 returned %08x\n", Status); 3348 free_fcb(Vcb, fcb); 3349 return Status; 3350 } 3351 3352 name.Length = name.MaximumLength = (UINT16)stringlen; 3353 3354 if (stringlen == 0) 3355 name.Buffer = NULL; 3356 else { 3357 name.Buffer = ExAllocatePoolWithTag(PagedPool, name.MaximumLength, ALLOC_TAG); 3358 3359 if (!name.Buffer) { 3360 ERR("out of memory\n"); 3361 free_fcb(Vcb, fcb); 3362 return STATUS_INSUFFICIENT_RESOURCES; 3363 } 3364 3365 Status = RtlUTF8ToUnicodeN(name.Buffer, stringlen, &stringlen, rr->name, rr->n); 3366 if (!NT_SUCCESS(Status)) { 3367 ERR("RtlUTF8ToUnicodeN 2 returned %08x\n", Status); 3368 ExFreePool(name.Buffer); 3369 free_fcb(Vcb, fcb); 3370 return Status; 3371 } 3372 3373 hl_alloc = TRUE; 3374 } 3375 } else { 3376 ERR("couldn't find parent for subvol %llx\n", subvol->id); 3377 free_fcb(Vcb, fcb); 3378 if (hl_alloc) ExFreePool(name.Buffer); 3379 return STATUS_INTERNAL_ERROR; 3380 } 3381 } else { 3382 Status = open_fileref_by_inode(Vcb, subvol, parent, &parfr, Irp); 3383 if (!NT_SUCCESS(Status)) { 3384 ERR("open_fileref_by_inode returned %08x\n", Status); 3385 free_fcb(Vcb, fcb); 3386 3387 if (hl_alloc) 3388 ExFreePool(name.Buffer); 3389 3390 return Status; 3391 } 3392 } 3393 3394 Status = open_fileref_child(Vcb, parfr, &name, TRUE, TRUE, FALSE, PagedPool, &fr, Irp); 3395 3396 if (!NT_SUCCESS(Status)) { 3397 ERR("open_fileref_child returned %08x\n", Status); 3398 3399 if (hl_alloc) 3400 ExFreePool(name.Buffer); 3401 3402 free_fcb(Vcb, fcb); 3403 free_fileref(Vcb, parfr); 3404 3405 return Status; 3406 } 3407 3408 *pfr = fr; 3409 3410 if (hl_alloc) 3411 ExFreePool(name.Buffer); 3412 3413 free_fcb(Vcb, fcb); 3414 free_fileref(Vcb, parfr); 3415 3416 return STATUS_SUCCESS; 3417 } 3418 3419 #ifndef __REACTOS__ 3420 static NTSTATUS fill_in_hard_link_information(FILE_LINKS_INFORMATION* fli, file_ref* fileref, PIRP Irp, LONG* length) { 3421 NTSTATUS Status; 3422 LIST_ENTRY* le; 3423 LONG bytes_needed; 3424 FILE_LINK_ENTRY_INFORMATION* feli; 3425 BOOL overflow = FALSE; 3426 fcb* fcb = fileref->fcb; 3427 ULONG len; 3428 3429 if (fcb->ads) 3430 return STATUS_INVALID_PARAMETER; 3431 3432 if (*length < (LONG)offsetof(FILE_LINKS_INFORMATION, Entry)) 3433 return STATUS_INVALID_PARAMETER; 3434 3435 RtlZeroMemory(fli, *length); 3436 3437 bytes_needed = offsetof(FILE_LINKS_INFORMATION, Entry); 3438 len = bytes_needed; 3439 feli = NULL; 3440 3441 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); 3442 3443 if (fcb->inode == SUBVOL_ROOT_INODE) { 3444 ULONG namelen; 3445 3446 if (fcb == fcb->Vcb->root_fileref->fcb) 3447 namelen = sizeof(WCHAR); 3448 else 3449 namelen = fileref->dc->name.Length; 3450 3451 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) - sizeof(WCHAR) + namelen; 3452 3453 if (bytes_needed > *length) 3454 overflow = TRUE; 3455 3456 if (!overflow) { 3457 feli = &fli->Entry; 3458 3459 feli->NextEntryOffset = 0; 3460 feli->ParentFileId = 0; // we use an inode of 0 to mean the parent of a subvolume 3461 3462 if (fcb == fcb->Vcb->root_fileref->fcb) { 3463 feli->FileNameLength = 1; 3464 feli->FileName[0] = '.'; 3465 } else { 3466 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR); 3467 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length); 3468 } 3469 3470 fli->EntriesReturned++; 3471 3472 len = bytes_needed; 3473 } 3474 } else { 3475 acquire_fcb_lock_exclusive(fcb->Vcb); 3476 3477 if (IsListEmpty(&fcb->hardlinks)) { 3478 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fileref->dc->name.Length - sizeof(WCHAR); 3479 3480 if (bytes_needed > *length) 3481 overflow = TRUE; 3482 3483 if (!overflow) { 3484 feli = &fli->Entry; 3485 3486 feli->NextEntryOffset = 0; 3487 feli->ParentFileId = fileref->parent->fcb->inode; 3488 feli->FileNameLength = fileref->dc->name.Length / sizeof(WCHAR); 3489 RtlCopyMemory(feli->FileName, fileref->dc->name.Buffer, fileref->dc->name.Length); 3490 3491 fli->EntriesReturned++; 3492 3493 len = bytes_needed; 3494 } 3495 } else { 3496 le = fcb->hardlinks.Flink; 3497 while (le != &fcb->hardlinks) { 3498 hardlink* hl = CONTAINING_RECORD(le, hardlink, list_entry); 3499 file_ref* parfr; 3500 3501 TRACE("parent %llx, index %llx, name %.*S\n", hl->parent, hl->index, hl->name.Length / sizeof(WCHAR), hl->name.Buffer); 3502 3503 Status = open_fileref_by_inode(fcb->Vcb, fcb->subvol, hl->parent, &parfr, Irp); 3504 3505 if (!NT_SUCCESS(Status)) { 3506 ERR("open_fileref_by_inode returned %08x\n", Status); 3507 } else if (!parfr->deleted) { 3508 LIST_ENTRY* le2; 3509 BOOL found = FALSE, deleted = FALSE; 3510 UNICODE_STRING* fn = NULL; 3511 3512 le2 = parfr->children.Flink; 3513 while (le2 != &parfr->children) { 3514 file_ref* fr2 = CONTAINING_RECORD(le2, file_ref, list_entry); 3515 3516 if (fr2->dc->index == hl->index) { 3517 found = TRUE; 3518 deleted = fr2->deleted; 3519 3520 if (!deleted) 3521 fn = &fr2->dc->name; 3522 3523 break; 3524 } 3525 3526 le2 = le2->Flink; 3527 } 3528 3529 if (!found) 3530 fn = &hl->name; 3531 3532 if (!deleted) { 3533 TRACE("fn = %.*S (found = %u)\n", fn->Length / sizeof(WCHAR), fn->Buffer, found); 3534 3535 if (feli) 3536 bytes_needed = (LONG)sector_align(bytes_needed, 8); 3537 3538 bytes_needed += sizeof(FILE_LINK_ENTRY_INFORMATION) + fn->Length - sizeof(WCHAR); 3539 3540 if (bytes_needed > *length) 3541 overflow = TRUE; 3542 3543 if (!overflow) { 3544 if (feli) { 3545 feli->NextEntryOffset = (ULONG)sector_align(sizeof(FILE_LINK_ENTRY_INFORMATION) + ((feli->FileNameLength - 1) * sizeof(WCHAR)), 8); 3546 feli = (FILE_LINK_ENTRY_INFORMATION*)((UINT8*)feli + feli->NextEntryOffset); 3547 } else 3548 feli = &fli->Entry; 3549 3550 feli->NextEntryOffset = 0; 3551 feli->ParentFileId = parfr->fcb->inode; 3552 feli->FileNameLength = fn->Length / sizeof(WCHAR); 3553 RtlCopyMemory(feli->FileName, fn->Buffer, fn->Length); 3554 3555 fli->EntriesReturned++; 3556 3557 len = bytes_needed; 3558 } 3559 } 3560 3561 free_fileref(fcb->Vcb, parfr); 3562 } 3563 3564 le = le->Flink; 3565 } 3566 } 3567 3568 release_fcb_lock(fcb->Vcb); 3569 } 3570 3571 fli->BytesNeeded = bytes_needed; 3572 3573 *length -= len; 3574 3575 Status = overflow ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; 3576 3577 ExReleaseResourceLite(fcb->Header.Resource); 3578 3579 return Status; 3580 } 3581 #endif /* __REACTOS__ */ 3582 3583 #if (NTDDI_VERSION >= NTDDI_WIN10) 3584 #ifdef __MINGW32__ 3585 typedef struct _FILE_ID_128 { 3586 UCHAR Identifier[16]; 3587 } FILE_ID_128, *PFILE_ID_128; 3588 3589 typedef struct _FILE_ID_INFORMATION { 3590 ULONGLONG VolumeSerialNumber; 3591 FILE_ID_128 FileId; 3592 } FILE_ID_INFORMATION, *PFILE_ID_INFORMATION; 3593 #endif 3594 3595 static NTSTATUS fill_in_file_id_information(FILE_ID_INFORMATION* fii, fcb* fcb, LONG* length) { 3596 RtlCopyMemory(&fii->VolumeSerialNumber, &fcb->Vcb->superblock.uuid.uuid[8], sizeof(UINT64)); 3597 RtlCopyMemory(&fii->FileId.Identifier[0], &fcb->inode, sizeof(UINT64)); 3598 RtlCopyMemory(&fii->FileId.Identifier[sizeof(UINT64)], &fcb->subvol->id, sizeof(UINT64)); 3599 3600 *length -= sizeof(FILE_ID_INFORMATION); 3601 3602 return STATUS_SUCCESS; 3603 } 3604 #endif 3605 3606 static NTSTATUS query_info(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) { 3607 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 3608 LONG length = IrpSp->Parameters.QueryFile.Length; 3609 fcb* fcb = FileObject->FsContext; 3610 ccb* ccb = FileObject->FsContext2; 3611 file_ref* fileref = ccb ? ccb->fileref : NULL; 3612 NTSTATUS Status; 3613 3614 TRACE("(%p, %p, %p)\n", Vcb, FileObject, Irp); 3615 TRACE("fcb = %p\n", fcb); 3616 3617 if (fcb == Vcb->volume_fcb) 3618 return STATUS_INVALID_PARAMETER; 3619 3620 if (!ccb) { 3621 ERR("ccb is NULL\n"); 3622 return STATUS_INVALID_PARAMETER; 3623 } 3624 3625 switch (IrpSp->Parameters.QueryFile.FileInformationClass) { 3626 case FileAllInformation: 3627 { 3628 FILE_ALL_INFORMATION* fai = Irp->AssociatedIrp.SystemBuffer; 3629 INODE_ITEM* ii; 3630 3631 TRACE("FileAllInformation\n"); 3632 3633 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 3634 WARN("insufficient privileges\n"); 3635 Status = STATUS_ACCESS_DENIED; 3636 goto exit; 3637 } 3638 3639 if (fcb->ads) { 3640 if (!fileref || !fileref->parent) { 3641 ERR("no fileref for stream\n"); 3642 Status = STATUS_INTERNAL_ERROR; 3643 goto exit; 3644 } 3645 3646 ii = &fileref->parent->fcb->inode_item; 3647 } else 3648 ii = &fcb->inode_item; 3649 3650 // Access, mode, and alignment are all filled in by the kernel 3651 3652 if (length > 0) 3653 fill_in_file_basic_information(&fai->BasicInformation, ii, &length, fcb, fileref); 3654 3655 if (length > 0) 3656 fill_in_file_standard_information(&fai->StandardInformation, fcb, fileref, &length); 3657 3658 if (length > 0) 3659 fill_in_file_internal_information(&fai->InternalInformation, fcb, &length); 3660 3661 if (length > 0) 3662 fill_in_file_ea_information(&fai->EaInformation, fcb, &length); 3663 3664 length -= sizeof(FILE_ACCESS_INFORMATION); 3665 3666 if (length > 0) 3667 fill_in_file_position_information(&fai->PositionInformation, FileObject, &length); 3668 3669 length -= sizeof(FILE_MODE_INFORMATION); 3670 3671 length -= sizeof(FILE_ALIGNMENT_INFORMATION); 3672 3673 if (length > 0) 3674 fill_in_file_name_information(&fai->NameInformation, fcb, fileref, &length); 3675 3676 Status = STATUS_SUCCESS; 3677 3678 break; 3679 } 3680 3681 case FileAttributeTagInformation: 3682 { 3683 FILE_ATTRIBUTE_TAG_INFORMATION* ati = Irp->AssociatedIrp.SystemBuffer; 3684 3685 TRACE("FileAttributeTagInformation\n"); 3686 3687 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 3688 WARN("insufficient privileges\n"); 3689 Status = STATUS_ACCESS_DENIED; 3690 goto exit; 3691 } 3692 3693 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 3694 Status = fill_in_file_attribute_information(ati, fcb, ccb, Irp, &length); 3695 ExReleaseResourceLite(&Vcb->tree_lock); 3696 3697 break; 3698 } 3699 3700 case FileBasicInformation: 3701 { 3702 FILE_BASIC_INFORMATION* fbi = Irp->AssociatedIrp.SystemBuffer; 3703 INODE_ITEM* ii; 3704 3705 TRACE("FileBasicInformation\n"); 3706 3707 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 3708 WARN("insufficient privileges\n"); 3709 Status = STATUS_ACCESS_DENIED; 3710 goto exit; 3711 } 3712 3713 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_BASIC_INFORMATION)) { 3714 WARN("overflow\n"); 3715 Status = STATUS_BUFFER_OVERFLOW; 3716 goto exit; 3717 } 3718 3719 if (fcb->ads) { 3720 if (!fileref || !fileref->parent) { 3721 ERR("no fileref for stream\n"); 3722 Status = STATUS_INTERNAL_ERROR; 3723 goto exit; 3724 } 3725 3726 ii = &fileref->parent->fcb->inode_item; 3727 } else 3728 ii = &fcb->inode_item; 3729 3730 Status = fill_in_file_basic_information(fbi, ii, &length, fcb, fileref); 3731 break; 3732 } 3733 3734 case FileCompressionInformation: 3735 FIXME("STUB: FileCompressionInformation\n"); 3736 Status = STATUS_INVALID_PARAMETER; 3737 goto exit; 3738 3739 case FileEaInformation: 3740 { 3741 FILE_EA_INFORMATION* eai = Irp->AssociatedIrp.SystemBuffer; 3742 3743 TRACE("FileEaInformation\n"); 3744 3745 Status = fill_in_file_ea_information(eai, fcb, &length); 3746 3747 break; 3748 } 3749 3750 case FileInternalInformation: 3751 { 3752 FILE_INTERNAL_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer; 3753 3754 TRACE("FileInternalInformation\n"); 3755 3756 Status = fill_in_file_internal_information(fii, fcb, &length); 3757 3758 break; 3759 } 3760 3761 case FileNameInformation: 3762 { 3763 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer; 3764 3765 TRACE("FileNameInformation\n"); 3766 3767 Status = fill_in_file_name_information(fni, fcb, fileref, &length); 3768 3769 break; 3770 } 3771 3772 case FileNetworkOpenInformation: 3773 { 3774 FILE_NETWORK_OPEN_INFORMATION* fnoi = Irp->AssociatedIrp.SystemBuffer; 3775 3776 TRACE("FileNetworkOpenInformation\n"); 3777 3778 if (Irp->RequestorMode != KernelMode && !(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 3779 WARN("insufficient privileges\n"); 3780 Status = STATUS_ACCESS_DENIED; 3781 goto exit; 3782 } 3783 3784 Status = fill_in_file_network_open_information(fnoi, fcb, fileref, &length); 3785 3786 break; 3787 } 3788 3789 case FilePositionInformation: 3790 { 3791 FILE_POSITION_INFORMATION* fpi = Irp->AssociatedIrp.SystemBuffer; 3792 3793 TRACE("FilePositionInformation\n"); 3794 3795 Status = fill_in_file_position_information(fpi, FileObject, &length); 3796 3797 break; 3798 } 3799 3800 case FileStandardInformation: 3801 { 3802 FILE_STANDARD_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer; 3803 3804 TRACE("FileStandardInformation\n"); 3805 3806 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_STANDARD_INFORMATION)) { 3807 WARN("overflow\n"); 3808 Status = STATUS_BUFFER_OVERFLOW; 3809 goto exit; 3810 } 3811 3812 Status = fill_in_file_standard_information(fsi, fcb, ccb->fileref, &length); 3813 3814 break; 3815 } 3816 3817 case FileStreamInformation: 3818 { 3819 FILE_STREAM_INFORMATION* fsi = Irp->AssociatedIrp.SystemBuffer; 3820 3821 TRACE("FileStreamInformation\n"); 3822 3823 Status = fill_in_file_stream_information(fsi, fileref, &length); 3824 3825 break; 3826 } 3827 3828 #if (NTDDI_VERSION >= NTDDI_VISTA) 3829 case FileHardLinkInformation: 3830 { 3831 FILE_LINKS_INFORMATION* fli = Irp->AssociatedIrp.SystemBuffer; 3832 3833 TRACE("FileHardLinkInformation\n"); 3834 3835 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE); 3836 Status = fill_in_hard_link_information(fli, fileref, Irp, &length); 3837 ExReleaseResourceLite(&Vcb->tree_lock); 3838 3839 break; 3840 } 3841 3842 case FileNormalizedNameInformation: 3843 { 3844 FILE_NAME_INFORMATION* fni = Irp->AssociatedIrp.SystemBuffer; 3845 3846 TRACE("FileNormalizedNameInformation\n"); 3847 3848 Status = fill_in_file_name_information(fni, fcb, fileref, &length); 3849 3850 break; 3851 } 3852 #endif 3853 3854 #if (NTDDI_VERSION >= NTDDI_WIN7) 3855 case FileStandardLinkInformation: 3856 { 3857 FILE_STANDARD_LINK_INFORMATION* fsli = Irp->AssociatedIrp.SystemBuffer; 3858 3859 TRACE("FileStandardLinkInformation\n"); 3860 3861 Status = fill_in_file_standard_link_information(fsli, fcb, ccb->fileref, &length); 3862 3863 break; 3864 } 3865 3866 case FileRemoteProtocolInformation: 3867 TRACE("FileRemoteProtocolInformation\n"); 3868 Status = STATUS_INVALID_PARAMETER; 3869 goto exit; 3870 #endif 3871 3872 #if (NTDDI_VERSION >= NTDDI_WIN10) 3873 #ifndef _MSC_VER 3874 #pragma GCC diagnostic push 3875 #pragma GCC diagnostic ignored "-Wswitch" 3876 #endif 3877 case FileIdInformation: 3878 { 3879 FILE_ID_INFORMATION* fii = Irp->AssociatedIrp.SystemBuffer; 3880 3881 if (IrpSp->Parameters.QueryFile.Length < sizeof(FILE_ID_INFORMATION)) { 3882 WARN("overflow\n"); 3883 Status = STATUS_BUFFER_OVERFLOW; 3884 goto exit; 3885 } 3886 3887 TRACE("FileIdInformation\n"); 3888 3889 Status = fill_in_file_id_information(fii, fcb, &length); 3890 3891 break; 3892 } 3893 #ifndef _MSC_VER 3894 #pragma GCC diagnostic pop 3895 #endif 3896 #endif 3897 3898 default: 3899 WARN("unknown FileInformationClass %u\n", IrpSp->Parameters.QueryFile.FileInformationClass); 3900 Status = STATUS_INVALID_PARAMETER; 3901 goto exit; 3902 } 3903 3904 if (length < 0) { 3905 length = 0; 3906 Status = STATUS_BUFFER_OVERFLOW; 3907 } 3908 3909 Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - length; 3910 3911 exit: 3912 TRACE("query_info returning %08x\n", Status); 3913 3914 return Status; 3915 } 3916 3917 _Dispatch_type_(IRP_MJ_QUERY_INFORMATION) 3918 _Function_class_(DRIVER_DISPATCH) 3919 NTSTATUS drv_query_information(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 3920 PIO_STACK_LOCATION IrpSp; 3921 NTSTATUS Status; 3922 fcb* fcb; 3923 device_extension* Vcb = DeviceObject->DeviceExtension; 3924 BOOL top_level; 3925 3926 FsRtlEnterFileSystem(); 3927 3928 top_level = is_top_level(Irp); 3929 3930 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 3931 Status = vol_query_information(DeviceObject, Irp); 3932 goto end; 3933 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 3934 Status = STATUS_INVALID_PARAMETER; 3935 goto end; 3936 } 3937 3938 Irp->IoStatus.Information = 0; 3939 3940 TRACE("query information\n"); 3941 3942 IrpSp = IoGetCurrentIrpStackLocation(Irp); 3943 3944 fcb = IrpSp->FileObject->FsContext; 3945 TRACE("fcb = %p\n", fcb); 3946 TRACE("fcb->subvol = %p\n", fcb->subvol); 3947 3948 Status = query_info(fcb->Vcb, IrpSp->FileObject, Irp); 3949 3950 end: 3951 TRACE("returning %08x\n", Status); 3952 3953 Irp->IoStatus.Status = Status; 3954 3955 IoCompleteRequest( Irp, IO_NO_INCREMENT ); 3956 3957 if (top_level) 3958 IoSetTopLevelIrp(NULL); 3959 3960 FsRtlExitFileSystem(); 3961 3962 return Status; 3963 } 3964 3965 _Dispatch_type_(IRP_MJ_QUERY_EA) 3966 _Function_class_(DRIVER_DISPATCH) 3967 NTSTATUS drv_query_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 3968 NTSTATUS Status; 3969 BOOL top_level; 3970 device_extension* Vcb = DeviceObject->DeviceExtension; 3971 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 3972 PFILE_OBJECT FileObject = IrpSp->FileObject; 3973 fcb* fcb; 3974 ccb* ccb; 3975 FILE_FULL_EA_INFORMATION* ffei; 3976 ULONG retlen = 0; 3977 3978 FsRtlEnterFileSystem(); 3979 3980 TRACE("(%p, %p)\n", DeviceObject, Irp); 3981 3982 top_level = is_top_level(Irp); 3983 3984 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 3985 Status = vol_query_ea(DeviceObject, Irp); 3986 goto end; 3987 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 3988 Status = STATUS_INVALID_PARAMETER; 3989 goto end; 3990 } 3991 3992 ffei = map_user_buffer(Irp, NormalPagePriority); 3993 if (!ffei) { 3994 ERR("could not get output buffer\n"); 3995 Status = STATUS_INVALID_PARAMETER; 3996 goto end; 3997 } 3998 3999 if (!FileObject) { 4000 ERR("no file object\n"); 4001 Status = STATUS_INVALID_PARAMETER; 4002 goto end; 4003 } 4004 4005 fcb = FileObject->FsContext; 4006 4007 if (!fcb) { 4008 ERR("no fcb\n"); 4009 Status = STATUS_INVALID_PARAMETER; 4010 goto end; 4011 } 4012 4013 ccb = FileObject->FsContext2; 4014 4015 if (!ccb) { 4016 ERR("no ccb\n"); 4017 Status = STATUS_INVALID_PARAMETER; 4018 goto end; 4019 } 4020 4021 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_READ_EA | FILE_WRITE_EA))) { 4022 WARN("insufficient privileges\n"); 4023 Status = STATUS_ACCESS_DENIED; 4024 goto end; 4025 } 4026 4027 if (fcb->ads) 4028 fcb = ccb->fileref->parent->fcb; 4029 4030 ExAcquireResourceSharedLite(fcb->Header.Resource, TRUE); 4031 4032 Status = STATUS_SUCCESS; 4033 4034 if (fcb->ea_xattr.Length == 0) 4035 goto end2; 4036 4037 if (IrpSp->Parameters.QueryEa.EaList) { 4038 FILE_FULL_EA_INFORMATION *ea, *out; 4039 FILE_GET_EA_INFORMATION* in; 4040 4041 in = IrpSp->Parameters.QueryEa.EaList; 4042 do { 4043 STRING s; 4044 4045 s.Length = s.MaximumLength = in->EaNameLength; 4046 s.Buffer = in->EaName; 4047 4048 RtlUpperString(&s, &s); 4049 4050 if (in->NextEntryOffset == 0) 4051 break; 4052 4053 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset); 4054 } while (TRUE); 4055 4056 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 4057 out = NULL; 4058 4059 do { 4060 BOOL found = FALSE; 4061 4062 in = IrpSp->Parameters.QueryEa.EaList; 4063 do { 4064 if (in->EaNameLength == ea->EaNameLength && 4065 RtlCompareMemory(in->EaName, ea->EaName, in->EaNameLength) == in->EaNameLength) { 4066 found = TRUE; 4067 break; 4068 } 4069 4070 if (in->NextEntryOffset == 0) 4071 break; 4072 4073 in = (FILE_GET_EA_INFORMATION*)(((UINT8*)in) + in->NextEntryOffset); 4074 } while (TRUE); 4075 4076 if (found) { 4077 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0; 4078 4079 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) { 4080 Status = STATUS_BUFFER_OVERFLOW; 4081 retlen = 0; 4082 goto end2; 4083 } 4084 4085 retlen += padding; 4086 4087 if (out) { 4088 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding; 4089 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset); 4090 } else 4091 out = ffei; 4092 4093 out->NextEntryOffset = 0; 4094 out->Flags = ea->Flags; 4095 out->EaNameLength = ea->EaNameLength; 4096 out->EaValueLength = ea->EaValueLength; 4097 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1); 4098 4099 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength; 4100 4101 if (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) 4102 break; 4103 } 4104 4105 if (ea->NextEntryOffset == 0) 4106 break; 4107 4108 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4109 } while (TRUE); 4110 } else { 4111 FILE_FULL_EA_INFORMATION *ea, *out; 4112 ULONG index; 4113 4114 if (IrpSp->Flags & SL_INDEX_SPECIFIED) { 4115 // The index is 1-based 4116 if (IrpSp->Parameters.QueryEa.EaIndex == 0) { 4117 Status = STATUS_NONEXISTENT_EA_ENTRY; 4118 goto end2; 4119 } else 4120 index = IrpSp->Parameters.QueryEa.EaIndex - 1; 4121 } else if (IrpSp->Flags & SL_RESTART_SCAN) 4122 index = ccb->ea_index = 0; 4123 else 4124 index = ccb->ea_index; 4125 4126 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 4127 4128 if (index > 0) { 4129 ULONG i; 4130 4131 for (i = 0; i < index; i++) { 4132 if (ea->NextEntryOffset == 0) // last item 4133 goto end2; 4134 4135 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4136 } 4137 } 4138 4139 out = NULL; 4140 4141 do { 4142 UINT8 padding = retlen % 4 > 0 ? (4 - (retlen % 4)) : 0; 4143 4144 if (offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength > IrpSp->Parameters.QueryEa.Length - retlen - padding) { 4145 Status = retlen == 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_BUFFER_OVERFLOW; 4146 goto end2; 4147 } 4148 4149 retlen += padding; 4150 4151 if (out) { 4152 out->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + out->EaNameLength + 1 + out->EaValueLength + padding; 4153 out = (FILE_FULL_EA_INFORMATION*)(((UINT8*)out) + out->NextEntryOffset); 4154 } else 4155 out = ffei; 4156 4157 out->NextEntryOffset = 0; 4158 out->Flags = ea->Flags; 4159 out->EaNameLength = ea->EaNameLength; 4160 out->EaValueLength = ea->EaValueLength; 4161 RtlCopyMemory(out->EaName, ea->EaName, ea->EaNameLength + ea->EaValueLength + 1); 4162 4163 retlen += (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + 1 + ea->EaValueLength; 4164 4165 if (!(IrpSp->Flags & SL_INDEX_SPECIFIED)) 4166 ccb->ea_index++; 4167 4168 if (ea->NextEntryOffset == 0 || IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) 4169 break; 4170 4171 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4172 } while (TRUE); 4173 } 4174 4175 end2: 4176 ExReleaseResourceLite(fcb->Header.Resource); 4177 4178 end: 4179 TRACE("returning %08x\n", Status); 4180 4181 Irp->IoStatus.Status = Status; 4182 Irp->IoStatus.Information = NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW ? retlen : 0; 4183 4184 IoCompleteRequest( Irp, IO_NO_INCREMENT ); 4185 4186 if (top_level) 4187 IoSetTopLevelIrp(NULL); 4188 4189 FsRtlExitFileSystem(); 4190 4191 return Status; 4192 } 4193 4194 typedef struct { 4195 ANSI_STRING name; 4196 ANSI_STRING value; 4197 UCHAR flags; 4198 LIST_ENTRY list_entry; 4199 } ea_item; 4200 4201 _Dispatch_type_(IRP_MJ_SET_EA) 4202 _Function_class_(DRIVER_DISPATCH) 4203 NTSTATUS drv_set_ea(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 4204 device_extension* Vcb = DeviceObject->DeviceExtension; 4205 NTSTATUS Status; 4206 BOOL top_level; 4207 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 4208 PFILE_OBJECT FileObject = IrpSp->FileObject; 4209 fcb* fcb; 4210 ccb* ccb; 4211 file_ref* fileref; 4212 FILE_FULL_EA_INFORMATION* ffei; 4213 ULONG offset; 4214 LIST_ENTRY ealist; 4215 ea_item* item; 4216 FILE_FULL_EA_INFORMATION* ea; 4217 LIST_ENTRY* le; 4218 LARGE_INTEGER time; 4219 BTRFS_TIME now; 4220 4221 FsRtlEnterFileSystem(); 4222 4223 TRACE("(%p, %p)\n", DeviceObject, Irp); 4224 4225 top_level = is_top_level(Irp); 4226 4227 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 4228 Status = vol_set_ea(DeviceObject, Irp); 4229 goto end; 4230 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 4231 Status = STATUS_INVALID_PARAMETER; 4232 goto end; 4233 } 4234 4235 if (Vcb->readonly) { 4236 Status = STATUS_MEDIA_WRITE_PROTECTED; 4237 goto end; 4238 } 4239 4240 ffei = map_user_buffer(Irp, NormalPagePriority); 4241 if (!ffei) { 4242 ERR("could not get output buffer\n"); 4243 Status = STATUS_INVALID_PARAMETER; 4244 goto end; 4245 } 4246 4247 Status = IoCheckEaBufferValidity(ffei, IrpSp->Parameters.SetEa.Length, &offset); 4248 if (!NT_SUCCESS(Status)) { 4249 ERR("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset); 4250 goto end; 4251 } 4252 4253 if (!FileObject) { 4254 ERR("no file object\n"); 4255 Status = STATUS_INVALID_PARAMETER; 4256 goto end; 4257 } 4258 4259 fcb = FileObject->FsContext; 4260 4261 if (!fcb) { 4262 ERR("no fcb\n"); 4263 Status = STATUS_INVALID_PARAMETER; 4264 goto end; 4265 } 4266 4267 ccb = FileObject->FsContext2; 4268 4269 if (!ccb) { 4270 ERR("no ccb\n"); 4271 Status = STATUS_INVALID_PARAMETER; 4272 goto end; 4273 } 4274 4275 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_EA)) { 4276 WARN("insufficient privileges\n"); 4277 Status = STATUS_ACCESS_DENIED; 4278 goto end; 4279 } 4280 4281 if (fcb->ads) { 4282 fileref = ccb->fileref->parent; 4283 fcb = fileref->fcb; 4284 } else 4285 fileref = ccb->fileref; 4286 4287 InitializeListHead(&ealist); 4288 4289 ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE); 4290 4291 if (fcb->ea_xattr.Length > 0) { 4292 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 4293 4294 do { 4295 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG); 4296 if (!item) { 4297 ERR("out of memory\n"); 4298 Status = STATUS_INSUFFICIENT_RESOURCES; 4299 goto end2; 4300 } 4301 4302 item->name.Length = item->name.MaximumLength = ea->EaNameLength; 4303 item->name.Buffer = ea->EaName; 4304 4305 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 4306 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 4307 4308 item->flags = ea->Flags; 4309 4310 InsertTailList(&ealist, &item->list_entry); 4311 4312 if (ea->NextEntryOffset == 0) 4313 break; 4314 4315 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4316 } while (TRUE); 4317 } 4318 4319 ea = ffei; 4320 4321 do { 4322 STRING s; 4323 BOOL found = FALSE; 4324 4325 s.Length = s.MaximumLength = ea->EaNameLength; 4326 s.Buffer = ea->EaName; 4327 4328 RtlUpperString(&s, &s); 4329 4330 le = ealist.Flink; 4331 while (le != &ealist) { 4332 item = CONTAINING_RECORD(le, ea_item, list_entry); 4333 4334 if (item->name.Length == s.Length && 4335 RtlCompareMemory(item->name.Buffer, s.Buffer, s.Length) == s.Length) { 4336 item->flags = ea->Flags; 4337 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 4338 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 4339 found = TRUE; 4340 break; 4341 } 4342 4343 le = le->Flink; 4344 } 4345 4346 if (!found) { 4347 item = ExAllocatePoolWithTag(PagedPool, sizeof(ea_item), ALLOC_TAG); 4348 if (!item) { 4349 ERR("out of memory\n"); 4350 Status = STATUS_INSUFFICIENT_RESOURCES; 4351 goto end2; 4352 } 4353 4354 item->name.Length = item->name.MaximumLength = ea->EaNameLength; 4355 item->name.Buffer = ea->EaName; 4356 4357 item->value.Length = item->value.MaximumLength = ea->EaValueLength; 4358 item->value.Buffer = &ea->EaName[ea->EaNameLength + 1]; 4359 4360 item->flags = ea->Flags; 4361 4362 InsertTailList(&ealist, &item->list_entry); 4363 } 4364 4365 if (ea->NextEntryOffset == 0) 4366 break; 4367 4368 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4369 } while (TRUE); 4370 4371 // remove entries with zero-length value 4372 le = ealist.Flink; 4373 while (le != &ealist) { 4374 LIST_ENTRY* le2 = le->Flink; 4375 4376 item = CONTAINING_RECORD(le, ea_item, list_entry); 4377 4378 if (item->value.Length == 0) { 4379 RemoveEntryList(&item->list_entry); 4380 ExFreePool(item); 4381 } 4382 4383 le = le2; 4384 } 4385 4386 if (IsListEmpty(&ealist)) { 4387 fcb->ealen = 0; 4388 4389 if (fcb->ea_xattr.Buffer) 4390 ExFreePool(fcb->ea_xattr.Buffer); 4391 4392 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0; 4393 fcb->ea_xattr.Buffer = NULL; 4394 } else { 4395 UINT16 size = 0; 4396 char *buf, *oldbuf; 4397 4398 le = ealist.Flink; 4399 while (le != &ealist) { 4400 item = CONTAINING_RECORD(le, ea_item, list_entry); 4401 4402 if (size % 4 > 0) 4403 size += 4 - (size % 4); 4404 4405 size += (UINT16)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length; 4406 4407 le = le->Flink; 4408 } 4409 4410 buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG); 4411 if (!buf) { 4412 ERR("out of memory\n"); 4413 Status = STATUS_INSUFFICIENT_RESOURCES; 4414 goto end2; 4415 } 4416 4417 oldbuf = fcb->ea_xattr.Buffer; 4418 4419 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = size; 4420 fcb->ea_xattr.Buffer = buf; 4421 4422 fcb->ealen = 4; 4423 ea = NULL; 4424 4425 le = ealist.Flink; 4426 while (le != &ealist) { 4427 item = CONTAINING_RECORD(le, ea_item, list_entry); 4428 4429 if (ea) { 4430 ea->NextEntryOffset = (ULONG)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + ea->EaNameLength + ea->EaValueLength; 4431 4432 if (ea->NextEntryOffset % 4 > 0) 4433 ea->NextEntryOffset += 4 - (ea->NextEntryOffset % 4); 4434 4435 ea = (FILE_FULL_EA_INFORMATION*)(((UINT8*)ea) + ea->NextEntryOffset); 4436 } else 4437 ea = (FILE_FULL_EA_INFORMATION*)fcb->ea_xattr.Buffer; 4438 4439 ea->NextEntryOffset = 0; 4440 ea->Flags = item->flags; 4441 ea->EaNameLength = (UCHAR)item->name.Length; 4442 ea->EaValueLength = item->value.Length; 4443 4444 RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length); 4445 ea->EaName[item->name.Length] = 0; 4446 RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length); 4447 4448 fcb->ealen += 5 + item->name.Length + item->value.Length; 4449 4450 le = le->Flink; 4451 } 4452 4453 if (oldbuf) 4454 ExFreePool(oldbuf); 4455 } 4456 4457 fcb->ea_changed = TRUE; 4458 4459 KeQuerySystemTime(&time); 4460 win_time_to_unix(time, &now); 4461 4462 fcb->inode_item.transid = Vcb->superblock.generation; 4463 fcb->inode_item.sequence++; 4464 4465 if (!ccb->user_set_change_time) 4466 fcb->inode_item.st_ctime = now; 4467 4468 fcb->inode_item_changed = TRUE; 4469 mark_fcb_dirty(fcb); 4470 4471 send_notification_fileref(fileref, FILE_NOTIFY_CHANGE_EA, FILE_ACTION_MODIFIED, NULL); 4472 4473 Status = STATUS_SUCCESS; 4474 4475 end2: 4476 ExReleaseResourceLite(fcb->Header.Resource); 4477 4478 while (!IsListEmpty(&ealist)) { 4479 le = RemoveHeadList(&ealist); 4480 4481 item = CONTAINING_RECORD(le, ea_item, list_entry); 4482 4483 ExFreePool(item); 4484 } 4485 4486 end: 4487 TRACE("returning %08x\n", Status); 4488 4489 Irp->IoStatus.Status = Status; 4490 Irp->IoStatus.Information = 0; 4491 4492 IoCompleteRequest(Irp, IO_NO_INCREMENT); 4493 4494 if (top_level) 4495 IoSetTopLevelIrp(NULL); 4496 4497 FsRtlExitFileSystem(); 4498 4499 return Status; 4500 } 4501