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