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