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