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