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