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