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 #include "btrfs_drv.h" 19 #include "btrfsioctl.h" 20 #include <ntddstor.h> 21 #include <ntdddisk.h> 22 #ifndef __REACTOS__ 23 #include <sys/stat.h> 24 #endif 25 26 #ifndef FSCTL_CSV_CONTROL 27 #define FSCTL_CSV_CONTROL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 181, METHOD_BUFFERED, FILE_ANY_ACCESS) 28 #endif 29 30 #ifndef FSCTL_QUERY_VOLUME_CONTAINER_STATE 31 #define FSCTL_QUERY_VOLUME_CONTAINER_STATE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 228, METHOD_BUFFERED, FILE_ANY_ACCESS) 32 #endif 33 34 #define DOTDOT ".." 35 36 #define SEF_AVOID_PRIVILEGE_CHECK 0x08 // on MSDN but not in any header files(?) 37 38 #ifndef _MSC_VER // not in mingw yet 39 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000 40 #endif 41 42 #define SEF_SACL_AUTO_INHERIT 0x02 43 44 extern LIST_ENTRY VcbList; 45 extern ERESOURCE global_loading_lock; 46 extern PDRIVER_OBJECT drvobj; 47 48 static void mark_subvol_dirty(device_extension* Vcb, root* r); 49 50 static NTSTATUS get_file_ids(PFILE_OBJECT FileObject, void* data, ULONG length) { 51 btrfs_get_file_ids* bgfi; 52 fcb* fcb; 53 54 if (length < sizeof(btrfs_get_file_ids)) 55 return STATUS_BUFFER_OVERFLOW; 56 57 if (!FileObject) 58 return STATUS_INVALID_PARAMETER; 59 60 fcb = FileObject->FsContext; 61 62 if (!fcb) 63 return STATUS_INVALID_PARAMETER; 64 65 bgfi = data; 66 67 bgfi->subvol = fcb->subvol->id; 68 bgfi->inode = fcb->inode; 69 bgfi->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false; 70 71 return STATUS_SUCCESS; 72 } 73 74 static void get_uuid(BTRFS_UUID* uuid) { 75 LARGE_INTEGER seed; 76 uint8_t i; 77 78 seed = KeQueryPerformanceCounter(NULL); 79 80 for (i = 0; i < 16; i+=2) { 81 ULONG rand = RtlRandomEx(&seed.LowPart); 82 83 uuid->uuid[i] = (rand & 0xff00) >> 8; 84 uuid->uuid[i+1] = rand & 0xff; 85 } 86 } 87 88 static NTSTATUS snapshot_tree_copy(device_extension* Vcb, uint64_t addr, root* subvol, uint64_t* newaddr, PIRP Irp, LIST_ENTRY* rollback) { 89 uint8_t* buf; 90 NTSTATUS Status; 91 write_data_context wtc; 92 LIST_ENTRY* le; 93 tree t; 94 tree_header* th; 95 chunk* c; 96 97 buf = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG); 98 if (!buf) { 99 ERR("out of memory\n"); 100 return STATUS_INSUFFICIENT_RESOURCES; 101 } 102 103 wtc.parity1 = wtc.parity2 = wtc.scratch = NULL; 104 wtc.mdl = wtc.parity1_mdl = wtc.parity2_mdl = NULL; 105 106 Status = read_data(Vcb, addr, Vcb->superblock.node_size, NULL, true, buf, NULL, NULL, Irp, 0, false, NormalPagePriority); 107 if (!NT_SUCCESS(Status)) { 108 ERR("read_data returned %08x\n", Status); 109 goto end; 110 } 111 112 th = (tree_header*)buf; 113 114 RtlZeroMemory(&t, sizeof(tree)); 115 t.root = subvol; 116 t.header.level = th->level; 117 t.header.tree_id = t.root->id; 118 119 Status = get_tree_new_address(Vcb, &t, Irp, rollback); 120 if (!NT_SUCCESS(Status)) { 121 ERR("get_tree_new_address returned %08x\n", Status); 122 goto end; 123 } 124 125 if (!t.has_new_address) { 126 ERR("tree new address not set\n"); 127 Status = STATUS_INTERNAL_ERROR; 128 goto end; 129 } 130 131 c = get_chunk_from_address(Vcb, t.new_address); 132 133 if (c) 134 c->used += Vcb->superblock.node_size; 135 else { 136 ERR("could not find chunk for address %I64x\n", t.new_address); 137 Status = STATUS_INTERNAL_ERROR; 138 goto end; 139 } 140 141 th->address = t.new_address; 142 th->tree_id = subvol->id; 143 th->generation = Vcb->superblock.generation; 144 th->fs_uuid = Vcb->superblock.uuid; 145 146 if (th->level == 0) { 147 uint32_t i; 148 leaf_node* ln = (leaf_node*)&th[1]; 149 150 for (i = 0; i < th->num_items; i++) { 151 if (ln[i].key.obj_type == TYPE_EXTENT_DATA && ln[i].size >= sizeof(EXTENT_DATA) && ln[i].offset + ln[i].size <= Vcb->superblock.node_size - sizeof(tree_header)) { 152 EXTENT_DATA* ed = (EXTENT_DATA*)(((uint8_t*)&th[1]) + ln[i].offset); 153 154 if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ln[i].size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { 155 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ed->data[0]; 156 157 if (ed2->size != 0) { // not sparse 158 Status = increase_extent_refcount_data(Vcb, ed2->address, ed2->size, subvol->id, ln[i].key.obj_id, ln[i].key.offset - ed2->offset, 1, Irp); 159 160 if (!NT_SUCCESS(Status)) { 161 ERR("increase_extent_refcount_data returned %08x\n", Status); 162 goto end; 163 } 164 } 165 } 166 } 167 } 168 } else { 169 uint32_t i; 170 internal_node* in = (internal_node*)&th[1]; 171 172 for (i = 0; i < th->num_items; i++) { 173 TREE_BLOCK_REF tbr; 174 175 tbr.offset = subvol->id; 176 177 Status = increase_extent_refcount(Vcb, in[i].address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, NULL, th->level - 1, Irp); 178 if (!NT_SUCCESS(Status)) { 179 ERR("increase_extent_refcount returned %08x\n", Status); 180 goto end; 181 } 182 } 183 } 184 185 *((uint32_t*)buf) = ~calc_crc32c(0xffffffff, (uint8_t*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum)); 186 187 KeInitializeEvent(&wtc.Event, NotificationEvent, false); 188 InitializeListHead(&wtc.stripes); 189 wtc.stripes_left = 0; 190 191 Status = write_data(Vcb, t.new_address, buf, Vcb->superblock.node_size, &wtc, NULL, NULL, false, 0, NormalPagePriority); 192 if (!NT_SUCCESS(Status)) { 193 ERR("write_data returned %08x\n", Status); 194 goto end; 195 } 196 197 if (wtc.stripes.Flink != &wtc.stripes) { 198 bool need_wait = false; 199 200 // launch writes and wait 201 le = wtc.stripes.Flink; 202 while (le != &wtc.stripes) { 203 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); 204 205 if (stripe->status != WriteDataStatus_Ignore) { 206 need_wait = true; 207 IoCallDriver(stripe->device->devobj, stripe->Irp); 208 } 209 210 le = le->Flink; 211 } 212 213 if (need_wait) 214 KeWaitForSingleObject(&wtc.Event, Executive, KernelMode, false, NULL); 215 216 le = wtc.stripes.Flink; 217 while (le != &wtc.stripes) { 218 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); 219 220 if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) { 221 Status = stripe->iosb.Status; 222 log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS); 223 break; 224 } 225 226 le = le->Flink; 227 } 228 229 free_write_data_stripes(&wtc); 230 buf = NULL; 231 } 232 233 if (NT_SUCCESS(Status)) 234 *newaddr = t.new_address; 235 236 end: 237 238 if (buf) 239 ExFreePool(buf); 240 241 return Status; 242 } 243 244 void flush_subvol_fcbs(root* subvol) { 245 LIST_ENTRY* le = subvol->fcbs.Flink; 246 247 if (IsListEmpty(&subvol->fcbs)) 248 return; 249 250 while (le != &subvol->fcbs) { 251 struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry); 252 IO_STATUS_BLOCK iosb; 253 254 if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted) 255 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb); 256 257 le = le->Flink; 258 } 259 } 260 261 static NTSTATUS do_create_snapshot(device_extension* Vcb, PFILE_OBJECT parent, fcb* subvol_fcb, PANSI_STRING utf8, PUNICODE_STRING name, bool readonly, PIRP Irp) { 262 LIST_ENTRY rollback; 263 uint64_t id; 264 NTSTATUS Status; 265 root *r, *subvol = subvol_fcb->subvol; 266 KEY searchkey; 267 traverse_ptr tp; 268 uint64_t address, *root_num; 269 LARGE_INTEGER time; 270 BTRFS_TIME now; 271 fcb* fcb = parent->FsContext; 272 ccb* ccb = parent->FsContext2; 273 LIST_ENTRY* le; 274 file_ref *fileref, *fr; 275 dir_child* dc = NULL; 276 277 if (!ccb) { 278 ERR("error - ccb was NULL\n"); 279 return STATUS_INTERNAL_ERROR; 280 } 281 282 if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) { 283 WARN("insufficient privileges\n"); 284 return STATUS_ACCESS_DENIED; 285 } 286 287 fileref = ccb->fileref; 288 289 if (fileref->fcb == Vcb->dummy_fcb) 290 return STATUS_ACCESS_DENIED; 291 292 // flush open files on this subvol 293 294 flush_subvol_fcbs(subvol); 295 296 // flush metadata 297 298 if (Vcb->need_write) 299 Status = do_write(Vcb, Irp); 300 else 301 Status = STATUS_SUCCESS; 302 303 free_trees(Vcb); 304 305 if (!NT_SUCCESS(Status)) { 306 ERR("do_write returned %08x\n", Status); 307 return Status; 308 } 309 310 InitializeListHead(&rollback); 311 312 // create new root 313 314 id = InterlockedIncrement64(&Vcb->root_root->lastinode); 315 Status = create_root(Vcb, id, &r, true, Vcb->superblock.generation, Irp); 316 317 if (!NT_SUCCESS(Status)) { 318 ERR("create_root returned %08x\n", Status); 319 goto end; 320 } 321 322 r->lastinode = subvol->lastinode; 323 324 if (!Vcb->uuid_root) { 325 root* uuid_root; 326 327 TRACE("uuid root doesn't exist, creating it\n"); 328 329 Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp); 330 331 if (!NT_SUCCESS(Status)) { 332 ERR("create_root returned %08x\n", Status); 333 goto end; 334 } 335 336 Vcb->uuid_root = uuid_root; 337 } 338 339 root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG); 340 if (!root_num) { 341 ERR("out of memory\n"); 342 Status = STATUS_INSUFFICIENT_RESOURCES; 343 goto end; 344 } 345 346 tp.tree = NULL; 347 348 do { 349 get_uuid(&r->root_item.uuid); 350 351 RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(uint64_t)); 352 searchkey.obj_type = TYPE_SUBVOL_UUID; 353 RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t)); 354 355 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp); 356 } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key)); 357 358 *root_num = r->id; 359 360 Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp); 361 if (!NT_SUCCESS(Status)) { 362 ERR("insert_tree_item returned %08x\n", Status); 363 ExFreePool(root_num); 364 goto end; 365 } 366 367 searchkey.obj_id = r->id; 368 searchkey.obj_type = TYPE_ROOT_ITEM; 369 searchkey.offset = 0xffffffffffffffff; 370 371 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 372 if (!NT_SUCCESS(Status)) { 373 ERR("error - find_item returned %08x\n", Status); 374 goto end; 375 } 376 377 Status = snapshot_tree_copy(Vcb, subvol->root_item.block_number, r, &address, Irp, &rollback); 378 if (!NT_SUCCESS(Status)) { 379 ERR("snapshot_tree_copy returned %08x\n", Status); 380 goto end; 381 } 382 383 KeQuerySystemTime(&time); 384 win_time_to_unix(time, &now); 385 386 r->root_item.inode.generation = 1; 387 r->root_item.inode.st_size = 3; 388 r->root_item.inode.st_blocks = subvol->root_item.inode.st_blocks; 389 r->root_item.inode.st_nlink = 1; 390 r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755 391 r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean 392 r->root_item.generation = Vcb->superblock.generation; 393 r->root_item.objid = subvol->root_item.objid; 394 r->root_item.block_number = address; 395 r->root_item.bytes_used = subvol->root_item.bytes_used; 396 r->root_item.last_snapshot_generation = Vcb->superblock.generation; 397 r->root_item.root_level = subvol->root_item.root_level; 398 r->root_item.generation2 = Vcb->superblock.generation; 399 r->root_item.parent_uuid = subvol->root_item.uuid; 400 r->root_item.ctransid = subvol->root_item.ctransid; 401 r->root_item.otransid = Vcb->superblock.generation; 402 r->root_item.ctime = subvol->root_item.ctime; 403 r->root_item.otime = now; 404 405 if (readonly) 406 r->root_item.flags |= BTRFS_SUBVOL_READONLY; 407 408 r->treeholder.address = address; 409 410 // FIXME - do we need to copy over the send and receive fields too? 411 412 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { 413 ERR("error - could not find ROOT_ITEM for subvol %I64x\n", r->id); 414 Status = STATUS_INTERNAL_ERROR; 415 goto end; 416 } 417 418 RtlCopyMemory(tp.item->data, &r->root_item, sizeof(ROOT_ITEM)); 419 420 // update ROOT_ITEM of original subvol 421 422 subvol->root_item.last_snapshot_generation = Vcb->superblock.generation; 423 424 mark_subvol_dirty(Vcb, subvol); 425 426 // create fileref for entry in other subvolume 427 428 fr = create_fileref(Vcb); 429 if (!fr) { 430 ERR("out of memory\n"); 431 Status = STATUS_INSUFFICIENT_RESOURCES; 432 goto end; 433 } 434 435 Status = open_fcb(Vcb, r, r->root_item.objid, BTRFS_TYPE_DIRECTORY, utf8, false, fcb, &fr->fcb, PagedPool, Irp); 436 if (!NT_SUCCESS(Status)) { 437 ERR("open_fcb returned %08x\n", Status); 438 free_fileref(fr); 439 goto end; 440 } 441 442 fr->parent = fileref; 443 444 Status = add_dir_child(fileref->fcb, r->id, true, utf8, name, BTRFS_TYPE_DIRECTORY, &dc); 445 if (!NT_SUCCESS(Status)) 446 WARN("add_dir_child returned %08x\n", Status); 447 448 fr->dc = dc; 449 dc->fileref = fr; 450 451 ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true); 452 InsertTailList(&fileref->children, &fr->list_entry); 453 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock); 454 455 increase_fileref_refcount(fileref); 456 457 fr->created = true; 458 mark_fileref_dirty(fr); 459 460 if (fr->fcb->type == BTRFS_TYPE_DIRECTORY) 461 fr->fcb->fileref = fr; 462 463 fr->fcb->subvol->parent = fileref->fcb->subvol->id; 464 465 free_fileref(fr); 466 467 // change fcb's INODE_ITEM 468 469 fcb->inode_item.transid = Vcb->superblock.generation; 470 fcb->inode_item.sequence++; 471 fcb->inode_item.st_size += utf8->Length * 2; 472 473 if (!ccb->user_set_change_time) 474 fcb->inode_item.st_ctime = now; 475 476 if (!ccb->user_set_write_time) 477 fcb->inode_item.st_mtime = now; 478 479 fcb->inode_item_changed = true; 480 mark_fcb_dirty(fcb); 481 482 fcb->subvol->root_item.ctime = now; 483 fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 484 485 send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL); 486 send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 487 488 le = subvol->fcbs.Flink; 489 while (le != &subvol->fcbs) { 490 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry); 491 LIST_ENTRY* le2 = fcb2->extents.Flink; 492 493 while (le2 != &fcb2->extents) { 494 extent* ext = CONTAINING_RECORD(le2, extent, list_entry); 495 496 if (!ext->ignore) 497 ext->unique = false; 498 499 le2 = le2->Flink; 500 } 501 502 le = le->Flink; 503 } 504 505 Status = do_write(Vcb, Irp); 506 507 free_trees(Vcb); 508 509 if (!NT_SUCCESS(Status)) 510 ERR("do_write returned %08x\n", Status); 511 512 end: 513 if (NT_SUCCESS(Status)) 514 clear_rollback(&rollback); 515 else 516 do_rollback(Vcb, &rollback); 517 518 return Status; 519 } 520 521 static NTSTATUS create_snapshot(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) { 522 PFILE_OBJECT subvol_obj; 523 NTSTATUS Status; 524 btrfs_create_snapshot* bcs = data; 525 fcb* subvol_fcb; 526 HANDLE subvolh; 527 bool readonly, posix; 528 ANSI_STRING utf8; 529 UNICODE_STRING nameus; 530 ULONG len; 531 fcb* fcb; 532 ccb* ccb; 533 file_ref *fileref, *fr2; 534 535 #if defined(_WIN64) 536 if (IoIs32bitProcess(Irp)) { 537 btrfs_create_snapshot32* bcs32 = data; 538 539 if (length < offsetof(btrfs_create_snapshot32, name)) 540 return STATUS_INVALID_PARAMETER; 541 542 if (length < offsetof(btrfs_create_snapshot32, name) + bcs32->namelen) 543 return STATUS_INVALID_PARAMETER; 544 545 subvolh = Handle32ToHandle(bcs32->subvol); 546 547 nameus.Buffer = bcs32->name; 548 nameus.Length = nameus.MaximumLength = bcs32->namelen; 549 550 readonly = bcs32->readonly; 551 posix = bcs32->posix; 552 } else { 553 #endif 554 if (length < offsetof(btrfs_create_snapshot, name)) 555 return STATUS_INVALID_PARAMETER; 556 557 if (length < offsetof(btrfs_create_snapshot, name) + bcs->namelen) 558 return STATUS_INVALID_PARAMETER; 559 560 subvolh = bcs->subvol; 561 562 nameus.Buffer = bcs->name; 563 nameus.Length = nameus.MaximumLength = bcs->namelen; 564 565 readonly = bcs->readonly; 566 posix = bcs->posix; 567 #if defined(_WIN64) 568 } 569 #endif 570 571 if (!subvolh) 572 return STATUS_INVALID_PARAMETER; 573 574 if (!FileObject || !FileObject->FsContext) 575 return STATUS_INVALID_PARAMETER; 576 577 fcb = FileObject->FsContext; 578 ccb = FileObject->FsContext2; 579 580 if (!fcb || !ccb || fcb->type != BTRFS_TYPE_DIRECTORY) 581 return STATUS_INVALID_PARAMETER; 582 583 fileref = ccb->fileref; 584 585 if (!fileref) { 586 ERR("fileref was NULL\n"); 587 return STATUS_INVALID_PARAMETER; 588 } 589 590 if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) { 591 WARN("insufficient privileges\n"); 592 return STATUS_ACCESS_DENIED; 593 } 594 595 if (Vcb->readonly) 596 return STATUS_MEDIA_WRITE_PROTECTED; 597 598 if (is_subvol_readonly(fcb->subvol, Irp)) 599 return STATUS_ACCESS_DENIED; 600 601 if (!is_file_name_valid(&nameus, posix)) 602 return STATUS_OBJECT_NAME_INVALID; 603 604 utf8.Buffer = NULL; 605 606 Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length); 607 if (!NT_SUCCESS(Status)) { 608 ERR("utf16_to_utf8 failed with error %08x\n", Status); 609 return Status; 610 } 611 612 if (len == 0) { 613 ERR("utf16_to_utf8 returned a length of 0\n"); 614 return STATUS_INTERNAL_ERROR; 615 } 616 617 if (len > 0xffff) { 618 ERR("len was too long\n"); 619 return STATUS_INVALID_PARAMETER; 620 } 621 622 utf8.MaximumLength = utf8.Length = (USHORT)len; 623 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG); 624 625 if (!utf8.Buffer) { 626 ERR("out of memory\n"); 627 return STATUS_INSUFFICIENT_RESOURCES; 628 } 629 630 Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length); 631 if (!NT_SUCCESS(Status)) { 632 ERR("utf16_to_utf8 failed with error %08x\n", Status); 633 goto end2; 634 } 635 636 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 637 638 // no need for fcb_lock as we have tree_lock exclusively 639 Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || posix, Irp); 640 641 if (NT_SUCCESS(Status)) { 642 if (!fr2->deleted) { 643 WARN("file already exists\n"); 644 free_fileref(fr2); 645 Status = STATUS_OBJECT_NAME_COLLISION; 646 goto end3; 647 } else 648 free_fileref(fr2); 649 } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { 650 ERR("open_fileref returned %08x\n", Status); 651 goto end3; 652 } 653 654 Status = ObReferenceObjectByHandle(subvolh, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&subvol_obj, NULL); 655 if (!NT_SUCCESS(Status)) { 656 ERR("ObReferenceObjectByHandle returned %08x\n", Status); 657 goto end3; 658 } 659 660 if (subvol_obj->DeviceObject != FileObject->DeviceObject) { 661 Status = STATUS_INVALID_PARAMETER; 662 goto end; 663 } 664 665 subvol_fcb = subvol_obj->FsContext; 666 if (!subvol_fcb) { 667 Status = STATUS_INVALID_PARAMETER; 668 goto end; 669 } 670 671 if (subvol_fcb->inode != subvol_fcb->subvol->root_item.objid) { 672 WARN("handle inode was %I64x, expected %I64x\n", subvol_fcb->inode, subvol_fcb->subvol->root_item.objid); 673 Status = STATUS_INVALID_PARAMETER; 674 goto end; 675 } 676 677 ccb = subvol_obj->FsContext2; 678 679 if (!ccb) { 680 Status = STATUS_INVALID_PARAMETER; 681 goto end; 682 } 683 684 if (!(ccb->access & FILE_TRAVERSE)) { 685 WARN("insufficient privileges\n"); 686 Status = STATUS_ACCESS_DENIED; 687 goto end; 688 } 689 690 if (fcb == Vcb->dummy_fcb) { 691 Status = STATUS_ACCESS_DENIED; 692 goto end; 693 } 694 695 // clear unique flag on extents of open files in subvol 696 if (!IsListEmpty(&subvol_fcb->subvol->fcbs)) { 697 LIST_ENTRY* le = subvol_fcb->subvol->fcbs.Flink; 698 699 while (le != &subvol_fcb->subvol->fcbs) { 700 struct _fcb* openfcb = CONTAINING_RECORD(le, struct _fcb, list_entry); 701 LIST_ENTRY* le2; 702 703 le2 = openfcb->extents.Flink; 704 705 while (le2 != &openfcb->extents) { 706 extent* ext = CONTAINING_RECORD(le2, extent, list_entry); 707 708 ext->unique = false; 709 710 le2 = le2->Flink; 711 } 712 713 le = le->Flink; 714 } 715 } 716 717 Status = do_create_snapshot(Vcb, FileObject, subvol_fcb, &utf8, &nameus, readonly, Irp); 718 719 if (NT_SUCCESS(Status)) { 720 file_ref* fr; 721 722 Status = open_fileref(Vcb, &fr, &nameus, fileref, false, NULL, NULL, PagedPool, false, Irp); 723 724 if (!NT_SUCCESS(Status)) { 725 ERR("open_fileref returned %08x\n", Status); 726 Status = STATUS_SUCCESS; 727 } else { 728 send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL); 729 free_fileref(fr); 730 } 731 } 732 733 end: 734 ObDereferenceObject(subvol_obj); 735 736 end3: 737 ExReleaseResourceLite(&Vcb->tree_lock); 738 739 end2: 740 ExFreePool(utf8.Buffer); 741 742 return Status; 743 } 744 745 static NTSTATUS create_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) { 746 btrfs_create_subvol* bcs; 747 fcb *fcb, *rootfcb = NULL; 748 ccb* ccb; 749 file_ref* fileref; 750 NTSTATUS Status; 751 uint64_t id; 752 root* r = NULL; 753 LARGE_INTEGER time; 754 BTRFS_TIME now; 755 ULONG len; 756 uint16_t irsize; 757 UNICODE_STRING nameus; 758 ANSI_STRING utf8; 759 INODE_REF* ir; 760 KEY searchkey; 761 traverse_ptr tp; 762 SECURITY_SUBJECT_CONTEXT subjcont; 763 PSID owner; 764 BOOLEAN defaulted; 765 uint64_t* root_num; 766 file_ref *fr = NULL, *fr2; 767 dir_child* dc = NULL; 768 769 fcb = FileObject->FsContext; 770 if (!fcb) { 771 ERR("error - fcb was NULL\n"); 772 return STATUS_INTERNAL_ERROR; 773 } 774 775 ccb = FileObject->FsContext2; 776 if (!ccb) { 777 ERR("error - ccb was NULL\n"); 778 return STATUS_INTERNAL_ERROR; 779 } 780 781 fileref = ccb->fileref; 782 783 if (fcb->type != BTRFS_TYPE_DIRECTORY) { 784 ERR("parent FCB was not a directory\n"); 785 return STATUS_NOT_A_DIRECTORY; 786 } 787 788 if (!fileref) { 789 ERR("fileref was NULL\n"); 790 return STATUS_INVALID_PARAMETER; 791 } 792 793 if (fileref->deleted || fcb->deleted) { 794 ERR("parent has been deleted\n"); 795 return STATUS_FILE_DELETED; 796 } 797 798 if (!(ccb->access & FILE_ADD_SUBDIRECTORY)) { 799 WARN("insufficient privileges\n"); 800 return STATUS_ACCESS_DENIED; 801 } 802 803 if (Vcb->readonly) 804 return STATUS_MEDIA_WRITE_PROTECTED; 805 806 if (is_subvol_readonly(fcb->subvol, Irp)) 807 return STATUS_ACCESS_DENIED; 808 809 if (fcb == Vcb->dummy_fcb) 810 return STATUS_ACCESS_DENIED; 811 812 if (!data || datalen < sizeof(btrfs_create_subvol)) 813 return STATUS_INVALID_PARAMETER; 814 815 bcs = (btrfs_create_subvol*)data; 816 817 if (offsetof(btrfs_create_subvol, name[0]) + bcs->namelen > datalen) 818 return STATUS_INVALID_PARAMETER; 819 820 nameus.Length = nameus.MaximumLength = bcs->namelen; 821 nameus.Buffer = bcs->name; 822 823 if (!is_file_name_valid(&nameus, bcs->posix)) 824 return STATUS_OBJECT_NAME_INVALID; 825 826 utf8.Buffer = NULL; 827 828 Status = utf16_to_utf8(NULL, 0, &len, nameus.Buffer, nameus.Length); 829 if (!NT_SUCCESS(Status)) { 830 ERR("utf16_to_utf8 failed with error %08x\n", Status); 831 return Status; 832 } 833 834 if (len == 0) { 835 ERR("utf16_to_utf8 returned a length of 0\n"); 836 return STATUS_INTERNAL_ERROR; 837 } 838 839 if (len > 0xffff) { 840 ERR("len was too long\n"); 841 return STATUS_INVALID_PARAMETER; 842 } 843 844 utf8.MaximumLength = utf8.Length = (USHORT)len; 845 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.Length, ALLOC_TAG); 846 847 if (!utf8.Buffer) { 848 ERR("out of memory\n"); 849 return STATUS_INSUFFICIENT_RESOURCES; 850 } 851 852 Status = utf16_to_utf8(utf8.Buffer, len, &len, nameus.Buffer, nameus.Length); 853 if (!NT_SUCCESS(Status)) { 854 ERR("utf16_to_utf8 failed with error %08x\n", Status); 855 goto end2; 856 } 857 858 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 859 860 KeQuerySystemTime(&time); 861 win_time_to_unix(time, &now); 862 863 // no need for fcb_lock as we have tree_lock exclusively 864 Status = open_fileref(fcb->Vcb, &fr2, &nameus, fileref, false, NULL, NULL, PagedPool, ccb->case_sensitive || bcs->posix, Irp); 865 866 if (NT_SUCCESS(Status)) { 867 if (!fr2->deleted) { 868 WARN("file already exists\n"); 869 free_fileref(fr2); 870 Status = STATUS_OBJECT_NAME_COLLISION; 871 goto end; 872 } else 873 free_fileref(fr2); 874 } else if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { 875 ERR("open_fileref returned %08x\n", Status); 876 goto end; 877 } 878 879 id = InterlockedIncrement64(&Vcb->root_root->lastinode); 880 Status = create_root(Vcb, id, &r, false, 0, Irp); 881 882 if (!NT_SUCCESS(Status)) { 883 ERR("create_root returned %08x\n", Status); 884 goto end; 885 } 886 887 TRACE("created root %I64x\n", id); 888 889 if (!Vcb->uuid_root) { 890 root* uuid_root; 891 892 TRACE("uuid root doesn't exist, creating it\n"); 893 894 Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp); 895 896 if (!NT_SUCCESS(Status)) { 897 ERR("create_root returned %08x\n", Status); 898 goto end; 899 } 900 901 Vcb->uuid_root = uuid_root; 902 } 903 904 root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG); 905 if (!root_num) { 906 ERR("out of memory\n"); 907 Status = STATUS_INSUFFICIENT_RESOURCES; 908 goto end; 909 } 910 911 tp.tree = NULL; 912 913 do { 914 get_uuid(&r->root_item.uuid); 915 916 RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid, sizeof(uint64_t)); 917 searchkey.obj_type = TYPE_SUBVOL_UUID; 918 RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t)); 919 920 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp); 921 } while (NT_SUCCESS(Status) && !keycmp(searchkey, tp.item->key)); 922 923 *root_num = r->id; 924 925 Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp); 926 if (!NT_SUCCESS(Status)) { 927 ERR("insert_tree_item returned %08x\n", Status); 928 ExFreePool(root_num); 929 goto end; 930 } 931 932 r->root_item.inode.generation = 1; 933 r->root_item.inode.st_size = 3; 934 r->root_item.inode.st_blocks = Vcb->superblock.node_size; 935 r->root_item.inode.st_nlink = 1; 936 r->root_item.inode.st_mode = __S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; // 40755 937 r->root_item.inode.flags = 0xffffffff80000000; // FIXME - find out what these mean 938 939 if (bcs->readonly) 940 r->root_item.flags |= BTRFS_SUBVOL_READONLY; 941 942 r->root_item.objid = SUBVOL_ROOT_INODE; 943 r->root_item.bytes_used = Vcb->superblock.node_size; 944 r->root_item.ctransid = Vcb->superblock.generation; 945 r->root_item.otransid = Vcb->superblock.generation; 946 r->root_item.ctime = now; 947 r->root_item.otime = now; 948 949 // add .. inode to new subvol 950 951 rootfcb = create_fcb(Vcb, PagedPool); 952 if (!rootfcb) { 953 ERR("out of memory\n"); 954 Status = STATUS_INSUFFICIENT_RESOURCES; 955 goto end; 956 } 957 958 rootfcb->Vcb = Vcb; 959 960 rootfcb->subvol = r; 961 rootfcb->inode = SUBVOL_ROOT_INODE; 962 rootfcb->type = BTRFS_TYPE_DIRECTORY; 963 964 rootfcb->inode_item.generation = Vcb->superblock.generation; 965 rootfcb->inode_item.transid = Vcb->superblock.generation; 966 rootfcb->inode_item.st_nlink = 1; 967 rootfcb->inode_item.st_mode = __S_IFDIR | inherit_mode(fileref->fcb, true); 968 rootfcb->inode_item.st_atime = rootfcb->inode_item.st_ctime = rootfcb->inode_item.st_mtime = rootfcb->inode_item.otime = now; 969 rootfcb->inode_item.st_gid = GID_NOBODY; 970 971 rootfcb->atts = get_file_attributes(Vcb, rootfcb->subvol, rootfcb->inode, rootfcb->type, false, true, Irp); 972 973 if (r->root_item.flags & BTRFS_SUBVOL_READONLY) 974 rootfcb->atts |= FILE_ATTRIBUTE_READONLY; 975 976 SeCaptureSubjectContext(&subjcont); 977 978 Status = SeAssignSecurity(fcb->sd, NULL, (void**)&rootfcb->sd, true, &subjcont, IoGetFileObjectGenericMapping(), PagedPool); 979 980 if (!NT_SUCCESS(Status)) { 981 ERR("SeAssignSecurity returned %08x\n", Status); 982 goto end; 983 } 984 985 if (!rootfcb->sd) { 986 ERR("SeAssignSecurity returned NULL security descriptor\n"); 987 Status = STATUS_INTERNAL_ERROR; 988 goto end; 989 } 990 991 Status = RtlGetOwnerSecurityDescriptor(rootfcb->sd, &owner, &defaulted); 992 if (!NT_SUCCESS(Status)) { 993 ERR("RtlGetOwnerSecurityDescriptor returned %08x\n", Status); 994 rootfcb->inode_item.st_uid = UID_NOBODY; 995 rootfcb->sd_dirty = true; 996 } else { 997 rootfcb->inode_item.st_uid = sid_to_uid(owner); 998 rootfcb->sd_dirty = rootfcb->inode_item.st_uid == UID_NOBODY; 999 } 1000 1001 find_gid(rootfcb, fileref->fcb, &subjcont); 1002 1003 rootfcb->inode_item_changed = true; 1004 1005 acquire_fcb_lock_exclusive(Vcb); 1006 InsertTailList(&r->fcbs, &rootfcb->list_entry); 1007 InsertTailList(&Vcb->all_fcbs, &rootfcb->list_entry_all); 1008 r->fcbs_version++; 1009 release_fcb_lock(Vcb); 1010 1011 rootfcb->Header.IsFastIoPossible = fast_io_possible(rootfcb); 1012 rootfcb->Header.AllocationSize.QuadPart = 0; 1013 rootfcb->Header.FileSize.QuadPart = 0; 1014 rootfcb->Header.ValidDataLength.QuadPart = 0; 1015 1016 rootfcb->created = true; 1017 1018 if (fileref->fcb->inode_item.flags & BTRFS_INODE_COMPRESS) 1019 rootfcb->inode_item.flags |= BTRFS_INODE_COMPRESS; 1020 1021 rootfcb->prop_compression = fileref->fcb->prop_compression; 1022 rootfcb->prop_compression_changed = rootfcb->prop_compression != PropCompression_None; 1023 1024 r->lastinode = rootfcb->inode; 1025 1026 // add INODE_REF 1027 1028 irsize = (uint16_t)(offsetof(INODE_REF, name[0]) + sizeof(DOTDOT) - 1); 1029 ir = ExAllocatePoolWithTag(PagedPool, irsize, ALLOC_TAG); 1030 if (!ir) { 1031 ERR("out of memory\n"); 1032 Status = STATUS_INSUFFICIENT_RESOURCES; 1033 goto end; 1034 } 1035 1036 ir->index = 0; 1037 ir->n = sizeof(DOTDOT) - 1; 1038 RtlCopyMemory(ir->name, DOTDOT, ir->n); 1039 1040 Status = insert_tree_item(Vcb, r, r->root_item.objid, TYPE_INODE_REF, r->root_item.objid, ir, irsize, NULL, Irp); 1041 if (!NT_SUCCESS(Status)) { 1042 ERR("insert_tree_item returned %08x\n", Status); 1043 ExFreePool(ir); 1044 goto end; 1045 } 1046 1047 // create fileref for entry in other subvolume 1048 1049 fr = create_fileref(Vcb); 1050 if (!fr) { 1051 ERR("out of memory\n"); 1052 1053 reap_fcb(rootfcb); 1054 1055 Status = STATUS_INSUFFICIENT_RESOURCES; 1056 goto end; 1057 } 1058 1059 fr->fcb = rootfcb; 1060 1061 mark_fcb_dirty(rootfcb); 1062 1063 fr->parent = fileref; 1064 1065 Status = add_dir_child(fileref->fcb, r->id, true, &utf8, &nameus, BTRFS_TYPE_DIRECTORY, &dc); 1066 if (!NT_SUCCESS(Status)) 1067 WARN("add_dir_child returned %08x\n", Status); 1068 1069 fr->dc = dc; 1070 dc->fileref = fr; 1071 1072 fr->fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); 1073 if (!fr->fcb->hash_ptrs) { 1074 ERR("out of memory\n"); 1075 free_fileref(fr); 1076 Status = STATUS_INSUFFICIENT_RESOURCES; 1077 goto end; 1078 } 1079 1080 RtlZeroMemory(fr->fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256); 1081 1082 fr->fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); 1083 if (!fr->fcb->hash_ptrs_uc) { 1084 ERR("out of memory\n"); 1085 free_fileref(fr); 1086 Status = STATUS_INSUFFICIENT_RESOURCES; 1087 goto end; 1088 } 1089 1090 RtlZeroMemory(fr->fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256); 1091 1092 ExAcquireResourceExclusiveLite(&fileref->fcb->nonpaged->dir_children_lock, true); 1093 InsertTailList(&fileref->children, &fr->list_entry); 1094 ExReleaseResourceLite(&fileref->fcb->nonpaged->dir_children_lock); 1095 1096 increase_fileref_refcount(fileref); 1097 1098 if (fr->fcb->type == BTRFS_TYPE_DIRECTORY) 1099 fr->fcb->fileref = fr; 1100 1101 fr->created = true; 1102 mark_fileref_dirty(fr); 1103 1104 // change fcb->subvol's ROOT_ITEM 1105 1106 fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 1107 fcb->subvol->root_item.ctime = now; 1108 1109 // change fcb's INODE_ITEM 1110 1111 fcb->inode_item.transid = Vcb->superblock.generation; 1112 fcb->inode_item.st_size += utf8.Length * 2; 1113 fcb->inode_item.sequence++; 1114 1115 if (!ccb->user_set_change_time) 1116 fcb->inode_item.st_ctime = now; 1117 1118 if (!ccb->user_set_write_time) 1119 fcb->inode_item.st_mtime = now; 1120 1121 fcb->inode_item_changed = true; 1122 mark_fcb_dirty(fcb); 1123 1124 fr->fcb->subvol->parent = fcb->subvol->id; 1125 1126 Status = STATUS_SUCCESS; 1127 1128 end: 1129 if (!NT_SUCCESS(Status)) { 1130 if (fr) { 1131 fr->deleted = true; 1132 mark_fileref_dirty(fr); 1133 } else if (rootfcb) { 1134 rootfcb->deleted = true; 1135 mark_fcb_dirty(rootfcb); 1136 } 1137 1138 if (r) { 1139 RemoveEntryList(&r->list_entry); 1140 InsertTailList(&Vcb->drop_roots, &r->list_entry); 1141 } 1142 } 1143 1144 ExReleaseResourceLite(&Vcb->tree_lock); 1145 1146 if (NT_SUCCESS(Status)) { 1147 send_notification_fileref(fr, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED, NULL); 1148 send_notification_fileref(fr->parent, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 1149 } 1150 1151 end2: 1152 if (fr) 1153 free_fileref(fr); 1154 1155 return Status; 1156 } 1157 1158 static NTSTATUS get_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length) { 1159 btrfs_inode_info* bii = data; 1160 fcb* fcb; 1161 ccb* ccb; 1162 bool old_style; 1163 1164 if (length < offsetof(btrfs_inode_info, disk_size_zstd)) 1165 return STATUS_BUFFER_OVERFLOW; 1166 1167 if (!FileObject) 1168 return STATUS_INVALID_PARAMETER; 1169 1170 fcb = FileObject->FsContext; 1171 1172 if (!fcb) 1173 return STATUS_INVALID_PARAMETER; 1174 1175 ccb = FileObject->FsContext2; 1176 1177 if (!ccb) 1178 return STATUS_INVALID_PARAMETER; 1179 1180 if (!(ccb->access & FILE_READ_ATTRIBUTES)) { 1181 WARN("insufficient privileges\n"); 1182 return STATUS_ACCESS_DENIED; 1183 } 1184 1185 if (fcb->ads) 1186 fcb = ccb->fileref->parent->fcb; 1187 1188 old_style = length < offsetof(btrfs_inode_info, sparse_size) + sizeof(((btrfs_inode_info*)NULL)->sparse_size); 1189 1190 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 1191 1192 bii->subvol = fcb->subvol->id; 1193 bii->inode = fcb->inode; 1194 bii->top = fcb->Vcb->root_fileref->fcb == fcb ? true : false; 1195 bii->type = fcb->type; 1196 bii->st_uid = fcb->inode_item.st_uid; 1197 bii->st_gid = fcb->inode_item.st_gid; 1198 bii->st_mode = fcb->inode_item.st_mode; 1199 1200 if (fcb->inode_item.st_rdev == 0) 1201 bii->st_rdev = 0; 1202 else 1203 bii->st_rdev = makedev((fcb->inode_item.st_rdev & 0xFFFFFFFFFFF) >> 20, fcb->inode_item.st_rdev & 0xFFFFF); 1204 1205 bii->flags = fcb->inode_item.flags; 1206 1207 bii->inline_length = 0; 1208 bii->disk_size_uncompressed = 0; 1209 bii->disk_size_zlib = 0; 1210 bii->disk_size_lzo = 0; 1211 1212 if (!old_style) { 1213 bii->disk_size_zstd = 0; 1214 bii->sparse_size = 0; 1215 } 1216 1217 if (fcb->type != BTRFS_TYPE_DIRECTORY) { 1218 uint64_t last_end = 0; 1219 LIST_ENTRY* le; 1220 bool extents_inline = false; 1221 1222 le = fcb->extents.Flink; 1223 while (le != &fcb->extents) { 1224 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 1225 1226 if (!ext->ignore) { 1227 if (!old_style && ext->offset > last_end) 1228 bii->sparse_size += ext->offset - last_end; 1229 1230 if (ext->extent_data.type == EXTENT_TYPE_INLINE) { 1231 bii->inline_length += ext->datalen - (uint16_t)offsetof(EXTENT_DATA, data[0]); 1232 last_end = ext->offset + ext->extent_data.decoded_size; 1233 extents_inline = true; 1234 } else { 1235 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 1236 1237 // FIXME - compressed extents with a hole in them are counted more than once 1238 if (ed2->size != 0) { 1239 switch (ext->extent_data.compression) { 1240 case BTRFS_COMPRESSION_NONE: 1241 bii->disk_size_uncompressed += ed2->num_bytes; 1242 break; 1243 1244 case BTRFS_COMPRESSION_ZLIB: 1245 bii->disk_size_zlib += ed2->size; 1246 break; 1247 1248 case BTRFS_COMPRESSION_LZO: 1249 bii->disk_size_lzo += ed2->size; 1250 break; 1251 1252 case BTRFS_COMPRESSION_ZSTD: 1253 if (!old_style) 1254 bii->disk_size_zstd += ed2->size; 1255 break; 1256 } 1257 } 1258 1259 last_end = ext->offset + ed2->num_bytes; 1260 } 1261 } 1262 1263 le = le->Flink; 1264 } 1265 1266 if (!extents_inline && !old_style && sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end) 1267 bii->sparse_size += sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end; 1268 1269 if (length >= offsetof(btrfs_inode_info, num_extents) + sizeof(((btrfs_inode_info*)NULL)->num_extents)) { 1270 EXTENT_DATA2* last_ed2 = NULL; 1271 1272 le = fcb->extents.Flink; 1273 1274 bii->num_extents = 0; 1275 1276 while (le != &fcb->extents) { 1277 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 1278 1279 if (!ext->ignore && ext->extent_data.type != EXTENT_TYPE_INLINE) { 1280 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 1281 1282 if (ed2->size != 0) { 1283 if (!last_ed2 || ed2->offset != last_ed2->offset + last_ed2->num_bytes) 1284 bii->num_extents++; 1285 1286 last_ed2 = ed2; 1287 } else 1288 last_ed2 = NULL; 1289 } 1290 1291 le = le->Flink; 1292 } 1293 } 1294 } 1295 1296 switch (fcb->prop_compression) { 1297 case PropCompression_Zlib: 1298 bii->compression_type = BTRFS_COMPRESSION_ZLIB; 1299 break; 1300 1301 case PropCompression_LZO: 1302 bii->compression_type = BTRFS_COMPRESSION_LZO; 1303 break; 1304 1305 case PropCompression_ZSTD: 1306 bii->compression_type = BTRFS_COMPRESSION_ZSTD; 1307 break; 1308 1309 default: 1310 bii->compression_type = BTRFS_COMPRESSION_ANY; 1311 break; 1312 } 1313 1314 ExReleaseResourceLite(fcb->Header.Resource); 1315 1316 return STATUS_SUCCESS; 1317 } 1318 1319 static NTSTATUS set_inode_info(PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) { 1320 btrfs_set_inode_info* bsii = data; 1321 NTSTATUS Status; 1322 fcb* fcb; 1323 ccb* ccb; 1324 1325 if (length < sizeof(btrfs_set_inode_info)) 1326 return STATUS_INVALID_PARAMETER; 1327 1328 if (!FileObject) 1329 return STATUS_INVALID_PARAMETER; 1330 1331 fcb = FileObject->FsContext; 1332 1333 if (!fcb) 1334 return STATUS_INVALID_PARAMETER; 1335 1336 ccb = FileObject->FsContext2; 1337 1338 if (!ccb) 1339 return STATUS_INVALID_PARAMETER; 1340 1341 if (bsii->flags_changed && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { 1342 WARN("insufficient privileges\n"); 1343 return STATUS_ACCESS_DENIED; 1344 } 1345 1346 if ((bsii->mode_changed || bsii->uid_changed || bsii->gid_changed) && !(ccb->access & WRITE_DAC)) { 1347 WARN("insufficient privileges\n"); 1348 return STATUS_ACCESS_DENIED; 1349 } 1350 1351 if (bsii->compression_type_changed && bsii->compression_type > BTRFS_COMPRESSION_ZSTD) 1352 return STATUS_INVALID_PARAMETER; 1353 1354 if (fcb->ads) 1355 fcb = ccb->fileref->parent->fcb; 1356 1357 if (is_subvol_readonly(fcb->subvol, Irp)) { 1358 WARN("trying to change inode on readonly subvolume\n"); 1359 return STATUS_ACCESS_DENIED; 1360 } 1361 1362 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 1363 1364 if (bsii->flags_changed) { 1365 if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0 && 1366 (bsii->flags & BTRFS_INODE_NODATACOW) != (fcb->inode_item.flags & BTRFS_INODE_NODATACOW)) { 1367 WARN("trying to change nocow flag on non-empty file\n"); 1368 Status = STATUS_INVALID_PARAMETER; 1369 goto end; 1370 } 1371 1372 fcb->inode_item.flags = bsii->flags; 1373 1374 if (fcb->inode_item.flags & BTRFS_INODE_NODATACOW) 1375 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM; 1376 else 1377 fcb->inode_item.flags &= ~(uint64_t)BTRFS_INODE_NODATASUM; 1378 } 1379 1380 if (bsii->mode_changed) { 1381 uint32_t allowed = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH | 1382 S_ISGID | S_ISVTX; 1383 1384 if (ccb->access & WRITE_OWNER) 1385 allowed |= S_ISUID; 1386 1387 fcb->inode_item.st_mode &= ~allowed; 1388 fcb->inode_item.st_mode |= bsii->st_mode & allowed; 1389 } 1390 1391 if (bsii->uid_changed && fcb->inode_item.st_uid != bsii->st_uid) { 1392 fcb->inode_item.st_uid = bsii->st_uid; 1393 1394 fcb->sd_dirty = true; 1395 fcb->sd_deleted = false; 1396 } 1397 1398 if (bsii->gid_changed) 1399 fcb->inode_item.st_gid = bsii->st_gid; 1400 1401 if (bsii->compression_type_changed) { 1402 switch (bsii->compression_type) { 1403 case BTRFS_COMPRESSION_ANY: 1404 fcb->prop_compression = PropCompression_None; 1405 break; 1406 1407 case BTRFS_COMPRESSION_ZLIB: 1408 fcb->prop_compression = PropCompression_Zlib; 1409 break; 1410 1411 case BTRFS_COMPRESSION_LZO: 1412 fcb->prop_compression = PropCompression_LZO; 1413 break; 1414 1415 case BTRFS_COMPRESSION_ZSTD: 1416 fcb->prop_compression = PropCompression_ZSTD; 1417 break; 1418 } 1419 1420 fcb->prop_compression_changed = true; 1421 } 1422 1423 if (bsii->flags_changed || bsii->mode_changed || bsii->uid_changed || bsii->gid_changed || bsii->compression_type_changed) { 1424 fcb->inode_item_changed = true; 1425 mark_fcb_dirty(fcb); 1426 } 1427 1428 Status = STATUS_SUCCESS; 1429 1430 end: 1431 ExReleaseResourceLite(fcb->Header.Resource); 1432 1433 return Status; 1434 } 1435 1436 static NTSTATUS get_devices(device_extension* Vcb, void* data, ULONG length) { 1437 btrfs_device* dev = NULL; 1438 NTSTATUS Status; 1439 LIST_ENTRY* le; 1440 1441 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 1442 1443 le = Vcb->devices.Flink; 1444 while (le != &Vcb->devices) { 1445 device* dev2 = CONTAINING_RECORD(le, device, list_entry); 1446 ULONG structlen; 1447 1448 if (length < sizeof(btrfs_device) - sizeof(WCHAR)) { 1449 Status = STATUS_BUFFER_OVERFLOW; 1450 goto end; 1451 } 1452 1453 if (!dev) 1454 dev = data; 1455 else { 1456 dev->next_entry = sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen; 1457 dev = (btrfs_device*)((uint8_t*)dev + dev->next_entry); 1458 } 1459 1460 structlen = length - offsetof(btrfs_device, namelen); 1461 1462 if (dev2->devobj) { 1463 Status = dev_ioctl(dev2->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &dev->namelen, structlen, true, NULL); 1464 if (!NT_SUCCESS(Status)) 1465 goto end; 1466 1467 dev->missing = false; 1468 } else { 1469 dev->namelen = 0; 1470 dev->missing = true; 1471 } 1472 1473 dev->next_entry = 0; 1474 dev->dev_id = dev2->devitem.dev_id; 1475 dev->readonly = (Vcb->readonly || dev2->readonly) ? true : false; 1476 dev->device_number = dev2->disk_num; 1477 dev->partition_number = dev2->part_num; 1478 dev->size = dev2->devitem.num_bytes; 1479 1480 if (dev2->devobj) { 1481 GET_LENGTH_INFORMATION gli; 1482 1483 Status = dev_ioctl(dev2->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), true, NULL); 1484 if (!NT_SUCCESS(Status)) 1485 goto end; 1486 1487 dev->max_size = gli.Length.QuadPart; 1488 } else 1489 dev->max_size = dev->size; 1490 1491 RtlCopyMemory(dev->stats, dev2->stats, sizeof(uint64_t) * 5); 1492 1493 length -= sizeof(btrfs_device) - sizeof(WCHAR) + dev->namelen; 1494 1495 le = le->Flink; 1496 } 1497 1498 end: 1499 ExReleaseResourceLite(&Vcb->tree_lock); 1500 1501 return Status; 1502 } 1503 1504 static NTSTATUS get_usage(device_extension* Vcb, void* data, ULONG length, PIRP Irp) { 1505 btrfs_usage* usage = (btrfs_usage*)data; 1506 btrfs_usage* lastbue = NULL; 1507 NTSTATUS Status; 1508 LIST_ENTRY* le; 1509 1510 if (length < sizeof(btrfs_usage)) 1511 return STATUS_BUFFER_OVERFLOW; 1512 1513 if (!Vcb->chunk_usage_found) { 1514 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 1515 1516 if (!Vcb->chunk_usage_found) 1517 Status = find_chunk_usage(Vcb, Irp); 1518 else 1519 Status = STATUS_SUCCESS; 1520 1521 ExReleaseResourceLite(&Vcb->tree_lock); 1522 1523 if (!NT_SUCCESS(Status)) { 1524 ERR("find_chunk_usage returned %08x\n", Status); 1525 return Status; 1526 } 1527 } 1528 1529 length -= offsetof(btrfs_usage, devices); 1530 1531 ExAcquireResourceSharedLite(&Vcb->chunk_lock, true); 1532 1533 le = Vcb->chunks.Flink; 1534 while (le != &Vcb->chunks) { 1535 bool addnew = false; 1536 1537 chunk* c = CONTAINING_RECORD(le, chunk, list_entry); 1538 1539 if (!lastbue) // first entry 1540 addnew = true; 1541 else { 1542 btrfs_usage* bue = usage; 1543 1544 addnew = true; 1545 1546 while (true) { 1547 if (bue->type == c->chunk_item->type) { 1548 addnew = false; 1549 break; 1550 } 1551 1552 if (bue->next_entry == 0) 1553 break; 1554 else 1555 bue = (btrfs_usage*)((uint8_t*)bue + bue->next_entry); 1556 } 1557 } 1558 1559 if (addnew) { 1560 btrfs_usage* bue; 1561 LIST_ENTRY* le2; 1562 uint64_t factor; 1563 1564 if (!lastbue) { 1565 bue = usage; 1566 } else { 1567 if (length < offsetof(btrfs_usage, devices)) { 1568 Status = STATUS_BUFFER_OVERFLOW; 1569 goto end; 1570 } 1571 1572 length -= offsetof(btrfs_usage, devices); 1573 1574 lastbue->next_entry = offsetof(btrfs_usage, devices) + (ULONG)(lastbue->num_devices * sizeof(btrfs_usage_device)); 1575 1576 bue = (btrfs_usage*)((uint8_t*)lastbue + lastbue->next_entry); 1577 } 1578 1579 bue->next_entry = 0; 1580 bue->type = c->chunk_item->type; 1581 bue->size = 0; 1582 bue->used = 0; 1583 bue->num_devices = 0; 1584 1585 if (c->chunk_item->type & BLOCK_FLAG_RAID0) 1586 factor = c->chunk_item->num_stripes; 1587 else if (c->chunk_item->type & BLOCK_FLAG_RAID10) 1588 factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes; 1589 else if (c->chunk_item->type & BLOCK_FLAG_RAID5) 1590 factor = c->chunk_item->num_stripes - 1; 1591 else if (c->chunk_item->type & BLOCK_FLAG_RAID6) 1592 factor = c->chunk_item->num_stripes - 2; 1593 else 1594 factor = 1; 1595 1596 le2 = le; 1597 while (le2 != &Vcb->chunks) { 1598 chunk* c2 = CONTAINING_RECORD(le2, chunk, list_entry); 1599 1600 if (c2->chunk_item->type == c->chunk_item->type) { 1601 uint16_t i; 1602 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c2->chunk_item[1]; 1603 uint64_t stripesize; 1604 1605 bue->size += c2->chunk_item->size; 1606 bue->used += c2->used; 1607 1608 stripesize = c2->chunk_item->size / factor; 1609 1610 for (i = 0; i < c2->chunk_item->num_stripes; i++) { 1611 uint64_t j; 1612 bool found = false; 1613 1614 for (j = 0; j < bue->num_devices; j++) { 1615 if (bue->devices[j].dev_id == cis[i].dev_id) { 1616 bue->devices[j].alloc += stripesize; 1617 found = true; 1618 break; 1619 } 1620 } 1621 1622 if (!found) { 1623 if (length < sizeof(btrfs_usage_device)) { 1624 Status = STATUS_BUFFER_OVERFLOW; 1625 goto end; 1626 } 1627 1628 length -= sizeof(btrfs_usage_device); 1629 1630 bue->devices[bue->num_devices].dev_id = cis[i].dev_id; 1631 bue->devices[bue->num_devices].alloc = stripesize; 1632 bue->num_devices++; 1633 } 1634 } 1635 } 1636 1637 le2 = le2->Flink; 1638 } 1639 1640 lastbue = bue; 1641 } 1642 1643 le = le->Flink; 1644 } 1645 1646 Status = STATUS_SUCCESS; 1647 1648 end: 1649 ExReleaseResourceLite(&Vcb->chunk_lock); 1650 1651 return Status; 1652 } 1653 1654 static NTSTATUS is_volume_mounted(device_extension* Vcb, PIRP Irp) { 1655 NTSTATUS Status; 1656 ULONG cc; 1657 IO_STATUS_BLOCK iosb; 1658 bool verify = false; 1659 LIST_ENTRY* le; 1660 1661 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 1662 1663 le = Vcb->devices.Flink; 1664 while (le != &Vcb->devices) { 1665 device* dev = CONTAINING_RECORD(le, device, list_entry); 1666 1667 if (dev->devobj && dev->removable) { 1668 Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, &cc, sizeof(ULONG), false, &iosb); 1669 1670 if (iosb.Information != sizeof(ULONG)) 1671 cc = 0; 1672 1673 if (Status == STATUS_VERIFY_REQUIRED || (NT_SUCCESS(Status) && cc != dev->change_count)) { 1674 dev->devobj->Flags |= DO_VERIFY_VOLUME; 1675 verify = true; 1676 } 1677 1678 if (NT_SUCCESS(Status) && iosb.Information == sizeof(ULONG)) 1679 dev->change_count = cc; 1680 1681 if (!NT_SUCCESS(Status) || verify) { 1682 IoSetHardErrorOrVerifyDevice(Irp, dev->devobj); 1683 ExReleaseResourceLite(&Vcb->tree_lock); 1684 1685 return verify ? STATUS_VERIFY_REQUIRED : Status; 1686 } 1687 } 1688 1689 le = le->Flink; 1690 } 1691 1692 ExReleaseResourceLite(&Vcb->tree_lock); 1693 1694 return STATUS_SUCCESS; 1695 } 1696 1697 static NTSTATUS fs_get_statistics(void* buffer, DWORD buflen, ULONG_PTR* retlen) { 1698 FILESYSTEM_STATISTICS* fss; 1699 1700 WARN("STUB: FSCTL_FILESYSTEM_GET_STATISTICS\n"); 1701 1702 // This is hideously wrong, but at least it stops SMB from breaking 1703 1704 if (buflen < sizeof(FILESYSTEM_STATISTICS)) 1705 return STATUS_BUFFER_TOO_SMALL; 1706 1707 fss = buffer; 1708 RtlZeroMemory(fss, sizeof(FILESYSTEM_STATISTICS)); 1709 1710 fss->Version = 1; 1711 fss->FileSystemType = FILESYSTEM_STATISTICS_TYPE_NTFS; 1712 fss->SizeOfCompleteStructure = sizeof(FILESYSTEM_STATISTICS); 1713 1714 *retlen = sizeof(FILESYSTEM_STATISTICS); 1715 1716 return STATUS_SUCCESS; 1717 } 1718 1719 static NTSTATUS set_sparse(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) { 1720 FILE_SET_SPARSE_BUFFER* fssb = data; 1721 NTSTATUS Status; 1722 bool set; 1723 fcb* fcb; 1724 ccb* ccb = FileObject->FsContext2; 1725 file_ref* fileref = ccb ? ccb->fileref : NULL; 1726 1727 if (data && length < sizeof(FILE_SET_SPARSE_BUFFER)) 1728 return STATUS_INVALID_PARAMETER; 1729 1730 if (!FileObject) { 1731 ERR("FileObject was NULL\n"); 1732 return STATUS_INVALID_PARAMETER; 1733 } 1734 1735 fcb = FileObject->FsContext; 1736 1737 if (!fcb) { 1738 ERR("FCB was NULL\n"); 1739 return STATUS_INVALID_PARAMETER; 1740 } 1741 1742 if (!ccb) { 1743 ERR("CCB was NULL\n"); 1744 return STATUS_INVALID_PARAMETER; 1745 } 1746 1747 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_ATTRIBUTES)) { 1748 WARN("insufficient privileges\n"); 1749 return STATUS_ACCESS_DENIED; 1750 } 1751 1752 if (!fileref) { 1753 ERR("no fileref\n"); 1754 return STATUS_INVALID_PARAMETER; 1755 } 1756 1757 if (fcb->ads) { 1758 fileref = fileref->parent; 1759 fcb = fileref->fcb; 1760 } 1761 1762 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 1763 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 1764 1765 if (fcb->type != BTRFS_TYPE_FILE) { 1766 WARN("FileObject did not point to a file\n"); 1767 Status = STATUS_INVALID_PARAMETER; 1768 goto end; 1769 } 1770 1771 if (fssb) 1772 set = fssb->SetSparse; 1773 else 1774 set = true; 1775 1776 if (set) { 1777 fcb->atts |= FILE_ATTRIBUTE_SPARSE_FILE; 1778 fcb->atts_changed = true; 1779 } else { 1780 ULONG defda; 1781 1782 fcb->atts &= ~FILE_ATTRIBUTE_SPARSE_FILE; 1783 fcb->atts_changed = true; 1784 1785 defda = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, 1786 fileref && fileref->dc && fileref->dc->name.Length >= sizeof(WCHAR) && fileref->dc->name.Buffer[0] == '.', true, Irp); 1787 1788 fcb->atts_deleted = defda == fcb->atts; 1789 } 1790 1791 mark_fcb_dirty(fcb); 1792 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED, NULL); 1793 1794 Status = STATUS_SUCCESS; 1795 1796 end: 1797 ExReleaseResourceLite(fcb->Header.Resource); 1798 ExReleaseResourceLite(&Vcb->tree_lock); 1799 1800 return Status; 1801 } 1802 1803 static NTSTATUS zero_data(device_extension* Vcb, fcb* fcb, uint64_t start, uint64_t length, PIRP Irp, LIST_ENTRY* rollback) { 1804 NTSTATUS Status; 1805 bool make_inline, compress; 1806 uint64_t start_data, end_data; 1807 ULONG buf_head; 1808 uint8_t* data; 1809 1810 make_inline = fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb); 1811 1812 if (!make_inline) 1813 compress = write_fcb_compressed(fcb); 1814 1815 if (make_inline) { 1816 start_data = 0; 1817 end_data = fcb->inode_item.st_size; 1818 buf_head = (ULONG)offsetof(EXTENT_DATA, data[0]); 1819 } else if (compress) { 1820 start_data = start & ~(uint64_t)(COMPRESSED_EXTENT_SIZE - 1); 1821 end_data = min(sector_align(start + length, COMPRESSED_EXTENT_SIZE), 1822 sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size)); 1823 buf_head = 0; 1824 } else { 1825 start_data = start & ~(uint64_t)(Vcb->superblock.sector_size - 1); 1826 end_data = sector_align(start + length, Vcb->superblock.sector_size); 1827 buf_head = 0; 1828 } 1829 1830 data = ExAllocatePoolWithTag(PagedPool, (ULONG)(buf_head + end_data - start_data), ALLOC_TAG); 1831 if (!data) { 1832 ERR("out of memory\n"); 1833 return STATUS_INSUFFICIENT_RESOURCES; 1834 } 1835 1836 RtlZeroMemory(data + buf_head, (ULONG)(end_data - start_data)); 1837 1838 if (start > start_data || start + length < end_data) { 1839 Status = read_file(fcb, data + buf_head, start_data, end_data - start_data, NULL, Irp); 1840 1841 if (!NT_SUCCESS(Status)) { 1842 ERR("read_file returned %08x\n", Status); 1843 ExFreePool(data); 1844 return Status; 1845 } 1846 } 1847 1848 RtlZeroMemory(data + buf_head + start - start_data, (ULONG)length); 1849 1850 if (make_inline) { 1851 uint16_t edsize; 1852 EXTENT_DATA* ed = (EXTENT_DATA*)data; 1853 1854 Status = excise_extents(Vcb, fcb, 0, sector_align(end_data, Vcb->superblock.sector_size), Irp, rollback); 1855 if (!NT_SUCCESS(Status)) { 1856 ERR("excise_extents returned %08x\n", Status); 1857 ExFreePool(data); 1858 return Status; 1859 } 1860 1861 edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + end_data); 1862 1863 ed->generation = Vcb->superblock.generation; 1864 ed->decoded_size = end_data; 1865 ed->compression = BTRFS_COMPRESSION_NONE; 1866 ed->encryption = BTRFS_ENCRYPTION_NONE; 1867 ed->encoding = BTRFS_ENCODING_NONE; 1868 ed->type = EXTENT_TYPE_INLINE; 1869 1870 Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, rollback); 1871 if (!NT_SUCCESS(Status)) { 1872 ERR("add_extent_to_fcb returned %08x\n", Status); 1873 ExFreePool(data); 1874 return Status; 1875 } 1876 1877 ExFreePool(data); 1878 1879 fcb->inode_item.st_blocks += end_data; 1880 } else if (compress) { 1881 Status = write_compressed(fcb, start_data, end_data, data, Irp, rollback); 1882 1883 ExFreePool(data); 1884 1885 if (!NT_SUCCESS(Status)) { 1886 ERR("write_compressed returned %08x\n", Status); 1887 return Status; 1888 } 1889 } else { 1890 Status = do_write_file(fcb, start_data, end_data, data, Irp, false, 0, rollback); 1891 1892 ExFreePool(data); 1893 1894 if (!NT_SUCCESS(Status)) { 1895 ERR("do_write_file returned %08x\n", Status); 1896 return Status; 1897 } 1898 } 1899 1900 return STATUS_SUCCESS; 1901 } 1902 1903 static NTSTATUS set_zero_data(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG length, PIRP Irp) { 1904 FILE_ZERO_DATA_INFORMATION* fzdi = data; 1905 NTSTATUS Status; 1906 fcb* fcb; 1907 ccb* ccb; 1908 file_ref* fileref; 1909 LIST_ENTRY rollback, *le; 1910 LARGE_INTEGER time; 1911 BTRFS_TIME now; 1912 uint64_t start, end; 1913 extent* ext; 1914 IO_STATUS_BLOCK iosb; 1915 1916 if (!data || length < sizeof(FILE_ZERO_DATA_INFORMATION)) 1917 return STATUS_INVALID_PARAMETER; 1918 1919 if (!FileObject) { 1920 ERR("FileObject was NULL\n"); 1921 return STATUS_INVALID_PARAMETER; 1922 } 1923 1924 if (fzdi->BeyondFinalZero.QuadPart <= fzdi->FileOffset.QuadPart) { 1925 WARN("BeyondFinalZero was less than or equal to FileOffset (%I64x <= %I64x)\n", fzdi->BeyondFinalZero.QuadPart, fzdi->FileOffset.QuadPart); 1926 return STATUS_INVALID_PARAMETER; 1927 } 1928 1929 fcb = FileObject->FsContext; 1930 1931 if (!fcb) { 1932 ERR("FCB was NULL\n"); 1933 return STATUS_INVALID_PARAMETER; 1934 } 1935 1936 ccb = FileObject->FsContext2; 1937 1938 if (!ccb) { 1939 ERR("ccb was NULL\n"); 1940 return STATUS_INVALID_PARAMETER; 1941 } 1942 1943 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) { 1944 WARN("insufficient privileges\n"); 1945 return STATUS_ACCESS_DENIED; 1946 } 1947 1948 fileref = ccb->fileref; 1949 1950 if (!fileref) { 1951 ERR("fileref was NULL\n"); 1952 return STATUS_INVALID_PARAMETER; 1953 } 1954 1955 InitializeListHead(&rollback); 1956 1957 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 1958 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 1959 1960 CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, &iosb); 1961 1962 if (fcb->type != BTRFS_TYPE_FILE) { 1963 WARN("FileObject did not point to a file\n"); 1964 Status = STATUS_INVALID_PARAMETER; 1965 goto end; 1966 } 1967 1968 if (fcb->ads) { 1969 ERR("FileObject is stream\n"); 1970 Status = STATUS_INVALID_PARAMETER; 1971 goto end; 1972 } 1973 1974 if ((uint64_t)fzdi->FileOffset.QuadPart >= fcb->inode_item.st_size) { 1975 Status = STATUS_SUCCESS; 1976 goto end; 1977 } 1978 1979 ext = NULL; 1980 le = fcb->extents.Flink; 1981 while (le != &fcb->extents) { 1982 extent* ext2 = CONTAINING_RECORD(le, extent, list_entry); 1983 1984 if (!ext2->ignore) { 1985 ext = ext2; 1986 break; 1987 } 1988 1989 le = le->Flink; 1990 } 1991 1992 if (!ext) { 1993 Status = STATUS_SUCCESS; 1994 goto end; 1995 } 1996 1997 if (ext->extent_data.type == EXTENT_TYPE_INLINE) { 1998 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback); 1999 if (!NT_SUCCESS(Status)) { 2000 ERR("zero_data returned %08x\n", Status); 2001 goto end; 2002 } 2003 } else { 2004 start = sector_align(fzdi->FileOffset.QuadPart, Vcb->superblock.sector_size); 2005 2006 if ((uint64_t)fzdi->BeyondFinalZero.QuadPart > fcb->inode_item.st_size) 2007 end = sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size); 2008 else 2009 end = (fzdi->BeyondFinalZero.QuadPart / Vcb->superblock.sector_size) * Vcb->superblock.sector_size; 2010 2011 if (end <= start) { 2012 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart, Irp, &rollback); 2013 if (!NT_SUCCESS(Status)) { 2014 ERR("zero_data returned %08x\n", Status); 2015 goto end; 2016 } 2017 } else { 2018 if (start > (uint64_t)fzdi->FileOffset.QuadPart) { 2019 Status = zero_data(Vcb, fcb, fzdi->FileOffset.QuadPart, start - fzdi->FileOffset.QuadPart, Irp, &rollback); 2020 if (!NT_SUCCESS(Status)) { 2021 ERR("zero_data returned %08x\n", Status); 2022 goto end; 2023 } 2024 } 2025 2026 if (end < (uint64_t)fzdi->BeyondFinalZero.QuadPart) { 2027 Status = zero_data(Vcb, fcb, end, fzdi->BeyondFinalZero.QuadPart - end, Irp, &rollback); 2028 if (!NT_SUCCESS(Status)) { 2029 ERR("zero_data returned %08x\n", Status); 2030 goto end; 2031 } 2032 } 2033 2034 if (end > start) { 2035 Status = excise_extents(Vcb, fcb, start, end, Irp, &rollback); 2036 if (!NT_SUCCESS(Status)) { 2037 ERR("excise_extents returned %08x\n", Status); 2038 goto end; 2039 } 2040 } 2041 } 2042 } 2043 2044 CcPurgeCacheSection(FileObject->SectionObjectPointer, &fzdi->FileOffset, (ULONG)(fzdi->BeyondFinalZero.QuadPart - fzdi->FileOffset.QuadPart), false); 2045 2046 KeQuerySystemTime(&time); 2047 win_time_to_unix(time, &now); 2048 2049 fcb->inode_item.transid = Vcb->superblock.generation; 2050 fcb->inode_item.sequence++; 2051 2052 if (!ccb->user_set_change_time) 2053 fcb->inode_item.st_ctime = now; 2054 2055 if (!ccb->user_set_write_time) 2056 fcb->inode_item.st_mtime = now; 2057 2058 fcb->extents_changed = true; 2059 fcb->inode_item_changed = true; 2060 mark_fcb_dirty(fcb); 2061 2062 send_notification_fcb(fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 2063 2064 fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 2065 fcb->subvol->root_item.ctime = now; 2066 2067 Status = STATUS_SUCCESS; 2068 2069 end: 2070 if (!NT_SUCCESS(Status)) 2071 do_rollback(Vcb, &rollback); 2072 else 2073 clear_rollback(&rollback); 2074 2075 ExReleaseResourceLite(fcb->Header.Resource); 2076 ExReleaseResourceLite(&Vcb->tree_lock); 2077 2078 return Status; 2079 } 2080 2081 static NTSTATUS query_ranges(PFILE_OBJECT FileObject, FILE_ALLOCATED_RANGE_BUFFER* inbuf, ULONG inbuflen, void* outbuf, ULONG outbuflen, ULONG_PTR* retlen) { 2082 NTSTATUS Status; 2083 fcb* fcb; 2084 LIST_ENTRY* le; 2085 FILE_ALLOCATED_RANGE_BUFFER* ranges = outbuf; 2086 ULONG i = 0; 2087 uint64_t last_start, last_end; 2088 2089 TRACE("FSCTL_QUERY_ALLOCATED_RANGES\n"); 2090 2091 if (!FileObject) { 2092 ERR("FileObject was NULL\n"); 2093 return STATUS_INVALID_PARAMETER; 2094 } 2095 2096 if (!inbuf || inbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER) || !outbuf) 2097 return STATUS_INVALID_PARAMETER; 2098 2099 fcb = FileObject->FsContext; 2100 2101 if (!fcb) { 2102 ERR("FCB was NULL\n"); 2103 return STATUS_INVALID_PARAMETER; 2104 } 2105 2106 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 2107 2108 // If file is not marked as sparse, claim the whole thing as an allocated range 2109 2110 if (!(fcb->atts & FILE_ATTRIBUTE_SPARSE_FILE)) { 2111 if (fcb->inode_item.st_size == 0) 2112 Status = STATUS_SUCCESS; 2113 else if (outbuflen < sizeof(FILE_ALLOCATED_RANGE_BUFFER)) 2114 Status = STATUS_BUFFER_TOO_SMALL; 2115 else { 2116 ranges[i].FileOffset.QuadPart = 0; 2117 ranges[i].Length.QuadPart = fcb->inode_item.st_size; 2118 i++; 2119 Status = STATUS_SUCCESS; 2120 } 2121 2122 goto end; 2123 2124 } 2125 2126 le = fcb->extents.Flink; 2127 2128 last_start = 0; 2129 last_end = 0; 2130 2131 while (le != &fcb->extents) { 2132 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 2133 2134 if (!ext->ignore) { 2135 EXTENT_DATA2* ed2 = (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ext->extent_data.data : NULL; 2136 uint64_t len = ed2 ? ed2->num_bytes : ext->extent_data.decoded_size; 2137 2138 if (ext->offset > last_end) { // first extent after a hole 2139 if (last_end > last_start) { 2140 if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) { 2141 ranges[i].FileOffset.QuadPart = last_start; 2142 ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start; 2143 i++; 2144 } else { 2145 Status = STATUS_BUFFER_TOO_SMALL; 2146 goto end; 2147 } 2148 } 2149 2150 last_start = ext->offset; 2151 } 2152 2153 last_end = ext->offset + len; 2154 } 2155 2156 le = le->Flink; 2157 } 2158 2159 if (last_end > last_start) { 2160 if ((i + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) <= outbuflen) { 2161 ranges[i].FileOffset.QuadPart = last_start; 2162 ranges[i].Length.QuadPart = min(fcb->inode_item.st_size, last_end) - last_start; 2163 i++; 2164 } else { 2165 Status = STATUS_BUFFER_TOO_SMALL; 2166 goto end; 2167 } 2168 } 2169 2170 Status = STATUS_SUCCESS; 2171 2172 end: 2173 *retlen = i * sizeof(FILE_ALLOCATED_RANGE_BUFFER); 2174 2175 ExReleaseResourceLite(fcb->Header.Resource); 2176 2177 return Status; 2178 } 2179 2180 static NTSTATUS get_object_id(device_extension* Vcb, PFILE_OBJECT FileObject, FILE_OBJECTID_BUFFER* buf, ULONG buflen, ULONG_PTR* retlen) { 2181 fcb* fcb; 2182 2183 TRACE("(%p, %p, %p, %x, %p)\n", Vcb, FileObject, buf, buflen, retlen); 2184 2185 if (!FileObject) { 2186 ERR("FileObject was NULL\n"); 2187 return STATUS_INVALID_PARAMETER; 2188 } 2189 2190 if (!buf || buflen < sizeof(FILE_OBJECTID_BUFFER)) 2191 return STATUS_INVALID_PARAMETER; 2192 2193 fcb = FileObject->FsContext; 2194 2195 if (!fcb) { 2196 ERR("FCB was NULL\n"); 2197 return STATUS_INVALID_PARAMETER; 2198 } 2199 2200 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 2201 2202 RtlCopyMemory(&buf->ObjectId[0], &fcb->inode, sizeof(uint64_t)); 2203 RtlCopyMemory(&buf->ObjectId[sizeof(uint64_t)], &fcb->subvol->id, sizeof(uint64_t)); 2204 2205 ExReleaseResourceLite(fcb->Header.Resource); 2206 2207 RtlZeroMemory(&buf->ExtendedInfo, sizeof(buf->ExtendedInfo)); 2208 2209 *retlen = sizeof(FILE_OBJECTID_BUFFER); 2210 2211 return STATUS_SUCCESS; 2212 } 2213 2214 static void flush_fcb_caches(device_extension* Vcb) { 2215 LIST_ENTRY* le; 2216 2217 le = Vcb->all_fcbs.Flink; 2218 while (le != &Vcb->all_fcbs) { 2219 struct _fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_all); 2220 IO_STATUS_BLOCK iosb; 2221 2222 if (fcb->type != BTRFS_TYPE_DIRECTORY && !fcb->deleted) 2223 CcFlushCache(&fcb->nonpaged->segment_object, NULL, 0, &iosb); 2224 2225 le = le->Flink; 2226 } 2227 } 2228 2229 static NTSTATUS lock_volume(device_extension* Vcb, PIRP Irp) { 2230 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2231 NTSTATUS Status; 2232 KIRQL irql; 2233 bool lock_paused_balance = false; 2234 2235 TRACE("FSCTL_LOCK_VOLUME\n"); 2236 2237 if (Vcb->scrub.thread) { 2238 WARN("cannot lock while scrub running\n"); 2239 return STATUS_DEVICE_NOT_READY; 2240 } 2241 2242 if (Vcb->balance.thread) { 2243 WARN("cannot lock while balance running\n"); 2244 return STATUS_DEVICE_NOT_READY; 2245 } 2246 2247 TRACE("locking volume\n"); 2248 2249 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK); 2250 2251 if (Vcb->locked) 2252 return STATUS_SUCCESS; 2253 2254 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true); 2255 2256 if (Vcb->root_fileref && Vcb->root_fileref->fcb && (Vcb->root_fileref->open_count > 0 || has_open_children(Vcb->root_fileref))) { 2257 Status = STATUS_ACCESS_DENIED; 2258 ExReleaseResourceLite(&Vcb->fileref_lock); 2259 goto end; 2260 } 2261 2262 ExReleaseResourceLite(&Vcb->fileref_lock); 2263 2264 if (Vcb->balance.thread && KeReadStateEvent(&Vcb->balance.event)) { 2265 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 2266 KeClearEvent(&Vcb->balance.event); 2267 ExReleaseResourceLite(&Vcb->tree_lock); 2268 2269 lock_paused_balance = true; 2270 } 2271 2272 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 2273 2274 flush_fcb_caches(Vcb); 2275 2276 if (Vcb->need_write && !Vcb->readonly) 2277 Status = do_write(Vcb, Irp); 2278 else 2279 Status = STATUS_SUCCESS; 2280 2281 free_trees(Vcb); 2282 2283 ExReleaseResourceLite(&Vcb->tree_lock); 2284 2285 if (!NT_SUCCESS(Status)) { 2286 ERR("do_write returned %08x\n", Status); 2287 goto end; 2288 } 2289 2290 IoAcquireVpbSpinLock(&irql); 2291 2292 if (!(Vcb->Vpb->Flags & VPB_LOCKED)) { 2293 Vcb->Vpb->Flags |= VPB_LOCKED; 2294 Vcb->locked = true; 2295 Vcb->locked_fileobj = IrpSp->FileObject; 2296 Vcb->lock_paused_balance = lock_paused_balance; 2297 } else { 2298 Status = STATUS_ACCESS_DENIED; 2299 IoReleaseVpbSpinLock(irql); 2300 2301 if (lock_paused_balance) 2302 KeSetEvent(&Vcb->balance.event, 0, false); 2303 2304 goto end; 2305 } 2306 2307 IoReleaseVpbSpinLock(irql); 2308 2309 Status = STATUS_SUCCESS; 2310 2311 end: 2312 if (!NT_SUCCESS(Status)) 2313 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED); 2314 2315 return Status; 2316 } 2317 2318 void do_unlock_volume(device_extension* Vcb) { 2319 KIRQL irql; 2320 2321 IoAcquireVpbSpinLock(&irql); 2322 2323 Vcb->locked = false; 2324 Vcb->Vpb->Flags &= ~VPB_LOCKED; 2325 Vcb->locked_fileobj = NULL; 2326 2327 IoReleaseVpbSpinLock(irql); 2328 2329 if (Vcb->lock_paused_balance) 2330 KeSetEvent(&Vcb->balance.event, 0, false); 2331 } 2332 2333 static NTSTATUS unlock_volume(device_extension* Vcb, PIRP Irp) { 2334 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2335 2336 TRACE("FSCTL_UNLOCK_VOLUME\n"); 2337 2338 if (!Vcb->locked || IrpSp->FileObject != Vcb->locked_fileobj) 2339 return STATUS_NOT_LOCKED; 2340 2341 TRACE("unlocking volume\n"); 2342 2343 do_unlock_volume(Vcb); 2344 2345 FsRtlNotifyVolumeEvent(IrpSp->FileObject, FSRTL_VOLUME_UNLOCK); 2346 2347 return STATUS_SUCCESS; 2348 } 2349 2350 static NTSTATUS invalidate_volumes(PIRP Irp) { 2351 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2352 LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0}; 2353 NTSTATUS Status; 2354 HANDLE h; 2355 PFILE_OBJECT fileobj; 2356 PDEVICE_OBJECT devobj; 2357 LIST_ENTRY* le; 2358 2359 TRACE("FSCTL_INVALIDATE_VOLUMES\n"); 2360 2361 if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) 2362 return STATUS_PRIVILEGE_NOT_HELD; 2363 2364 #if defined(_WIN64) 2365 if (IoIs32bitProcess(Irp)) { 2366 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t)) 2367 return STATUS_INVALID_PARAMETER; 2368 2369 h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer)); 2370 } else { 2371 #endif 2372 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) 2373 return STATUS_INVALID_PARAMETER; 2374 2375 h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; 2376 #if defined(_WIN64) 2377 } 2378 #endif 2379 2380 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL); 2381 2382 if (!NT_SUCCESS(Status)) { 2383 ERR("ObReferenceObjectByHandle returned %08x\n", Status); 2384 return Status; 2385 } 2386 2387 devobj = fileobj->DeviceObject; 2388 2389 ExAcquireResourceSharedLite(&global_loading_lock, true); 2390 2391 le = VcbList.Flink; 2392 2393 while (le != &VcbList) { 2394 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry); 2395 2396 if (Vcb->Vpb && Vcb->Vpb->RealDevice == devobj) { 2397 if (Vcb->Vpb == devobj->Vpb) { 2398 KIRQL irql; 2399 PVPB newvpb; 2400 bool free_newvpb = false; 2401 2402 newvpb = ExAllocatePoolWithTag(NonPagedPool, sizeof(VPB), ALLOC_TAG); 2403 if (!newvpb) { 2404 ERR("out of memory\n"); 2405 Status = STATUS_INSUFFICIENT_RESOURCES; 2406 goto end; 2407 } 2408 2409 RtlZeroMemory(newvpb, sizeof(VPB)); 2410 2411 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 2412 2413 Vcb->removing = true; 2414 2415 ExReleaseResourceLite(&Vcb->tree_lock); 2416 2417 CcWaitForCurrentLazyWriterActivity(); 2418 2419 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 2420 2421 flush_fcb_caches(Vcb); 2422 2423 if (Vcb->need_write && !Vcb->readonly) 2424 Status = do_write(Vcb, Irp); 2425 else 2426 Status = STATUS_SUCCESS; 2427 2428 free_trees(Vcb); 2429 2430 if (!NT_SUCCESS(Status)) { 2431 ERR("do_write returned %08x\n", Status); 2432 ExReleaseResourceLite(&Vcb->tree_lock); 2433 ExFreePool(newvpb); 2434 goto end; 2435 } 2436 2437 flush_fcb_caches(Vcb); 2438 2439 ExReleaseResourceLite(&Vcb->tree_lock); 2440 2441 IoAcquireVpbSpinLock(&irql); 2442 2443 if (devobj->Vpb->Flags & VPB_MOUNTED) { 2444 newvpb->Type = IO_TYPE_VPB; 2445 newvpb->Size = sizeof(VPB); 2446 newvpb->RealDevice = devobj; 2447 newvpb->Flags = devobj->Vpb->Flags & VPB_REMOVE_PENDING; 2448 2449 devobj->Vpb = newvpb; 2450 } else 2451 free_newvpb = true; 2452 2453 IoReleaseVpbSpinLock(irql); 2454 2455 if (free_newvpb) 2456 ExFreePool(newvpb); 2457 2458 if (Vcb->open_files == 0) 2459 uninit(Vcb); 2460 } 2461 2462 break; 2463 } 2464 2465 le = le->Flink; 2466 } 2467 2468 Status = STATUS_SUCCESS; 2469 2470 end: 2471 ExReleaseResourceLite(&global_loading_lock); 2472 2473 ObDereferenceObject(fileobj); 2474 2475 return Status; 2476 } 2477 2478 static NTSTATUS is_volume_dirty(device_extension* Vcb, PIRP Irp) { 2479 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2480 ULONG* volstate; 2481 2482 if (Irp->AssociatedIrp.SystemBuffer) { 2483 volstate = Irp->AssociatedIrp.SystemBuffer; 2484 } else if (Irp->MdlAddress != NULL) { 2485 volstate = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority); 2486 2487 if (!volstate) 2488 return STATUS_INSUFFICIENT_RESOURCES; 2489 } else 2490 return STATUS_INVALID_USER_BUFFER; 2491 2492 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) 2493 return STATUS_INVALID_PARAMETER; 2494 2495 *volstate = 0; 2496 2497 if (IrpSp->FileObject->FsContext != Vcb->volume_fcb) 2498 return STATUS_INVALID_PARAMETER; 2499 2500 Irp->IoStatus.Information = sizeof(ULONG); 2501 2502 return STATUS_SUCCESS; 2503 } 2504 2505 static NTSTATUS get_compression(PIRP Irp) { 2506 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2507 USHORT* compression; 2508 2509 TRACE("FSCTL_GET_COMPRESSION\n"); 2510 2511 if (Irp->AssociatedIrp.SystemBuffer) { 2512 compression = Irp->AssociatedIrp.SystemBuffer; 2513 } else if (Irp->MdlAddress != NULL) { 2514 compression = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority); 2515 2516 if (!compression) 2517 return STATUS_INSUFFICIENT_RESOURCES; 2518 } else 2519 return STATUS_INVALID_USER_BUFFER; 2520 2521 if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(USHORT)) 2522 return STATUS_INVALID_PARAMETER; 2523 2524 *compression = COMPRESSION_FORMAT_NONE; 2525 2526 Irp->IoStatus.Information = sizeof(USHORT); 2527 2528 return STATUS_SUCCESS; 2529 } 2530 2531 static NTSTATUS set_compression(PIRP Irp) { 2532 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2533 USHORT* compression; 2534 2535 TRACE("FSCTL_SET_COMPRESSION\n"); 2536 2537 if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(USHORT)) 2538 return STATUS_INVALID_PARAMETER; 2539 2540 compression = Irp->AssociatedIrp.SystemBuffer; 2541 2542 if (*compression != COMPRESSION_FORMAT_NONE) 2543 return STATUS_INVALID_PARAMETER; 2544 2545 return STATUS_SUCCESS; 2546 } 2547 2548 static void update_volumes(device_extension* Vcb) { 2549 LIST_ENTRY* le; 2550 volume_device_extension* vde = Vcb->vde; 2551 pdo_device_extension* pdode = vde->pdode; 2552 2553 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 2554 2555 ExAcquireResourceExclusiveLite(&pdode->child_lock, true); 2556 2557 le = pdode->children.Flink; 2558 while (le != &pdode->children) { 2559 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 2560 2561 vc->generation = Vcb->superblock.generation - 1; 2562 2563 le = le->Flink; 2564 } 2565 2566 ExReleaseResourceLite(&pdode->child_lock); 2567 2568 ExReleaseResourceLite(&Vcb->tree_lock); 2569 } 2570 2571 static NTSTATUS dismount_volume(device_extension* Vcb, PIRP Irp) { 2572 NTSTATUS Status; 2573 2574 TRACE("FSCTL_DISMOUNT_VOLUME\n"); 2575 2576 if (!(Vcb->Vpb->Flags & VPB_MOUNTED)) 2577 return STATUS_SUCCESS; 2578 2579 if (Vcb->disallow_dismount || Vcb->page_file_count != 0) { 2580 WARN("attempting to dismount boot volume or one containing a pagefile\n"); 2581 return STATUS_ACCESS_DENIED; 2582 } 2583 2584 Status = FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_DISMOUNT); 2585 if (!NT_SUCCESS(Status)) { 2586 WARN("FsRtlNotifyVolumeEvent returned %08x\n", Status); 2587 } 2588 2589 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 2590 2591 if (!Vcb->locked) { 2592 flush_fcb_caches(Vcb); 2593 2594 if (Vcb->need_write && !Vcb->readonly) { 2595 Status = do_write(Vcb, Irp); 2596 2597 if (!NT_SUCCESS(Status)) 2598 ERR("do_write returned %08x\n", Status); 2599 } 2600 } 2601 2602 free_trees(Vcb); 2603 2604 Vcb->removing = true; 2605 2606 if (Vcb->vde) { 2607 update_volumes(Vcb); 2608 Vcb->vde->mounted_device = NULL; 2609 } 2610 2611 ExReleaseResourceLite(&Vcb->tree_lock); 2612 2613 return STATUS_SUCCESS; 2614 } 2615 2616 static NTSTATUS is_device_part_of_mounted_btrfs_raid(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj) { 2617 NTSTATUS Status; 2618 ULONG to_read; 2619 superblock* sb; 2620 uint32_t crc32; 2621 BTRFS_UUID fsuuid, devuuid; 2622 LIST_ENTRY* le; 2623 2624 to_read = devobj->SectorSize == 0 ? sizeof(superblock) : (ULONG)sector_align(sizeof(superblock), devobj->SectorSize); 2625 2626 sb = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG); 2627 if (!sb) { 2628 ERR("out of memory\n"); 2629 return STATUS_INSUFFICIENT_RESOURCES; 2630 } 2631 2632 Status = sync_read_phys(devobj, fileobj, superblock_addrs[0], to_read, (uint8_t*)sb, true); 2633 if (!NT_SUCCESS(Status)) { 2634 ERR("sync_read_phys returned %08x\n", Status); 2635 ExFreePool(sb); 2636 return Status; 2637 } 2638 2639 if (sb->magic != BTRFS_MAGIC) { 2640 TRACE("device is not Btrfs\n"); 2641 ExFreePool(sb); 2642 return STATUS_SUCCESS; 2643 } 2644 2645 crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum)); 2646 2647 if (crc32 != *((uint32_t*)sb->checksum)) { 2648 TRACE("device has Btrfs magic, but invalid superblock checksum\n"); 2649 ExFreePool(sb); 2650 return STATUS_SUCCESS; 2651 } 2652 2653 fsuuid = sb->uuid; 2654 devuuid = sb->dev_item.device_uuid; 2655 2656 ExFreePool(sb); 2657 2658 ExAcquireResourceSharedLite(&global_loading_lock, true); 2659 2660 le = VcbList.Flink; 2661 2662 while (le != &VcbList) { 2663 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry); 2664 2665 if (RtlCompareMemory(&Vcb->superblock.uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { 2666 LIST_ENTRY* le2; 2667 2668 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 2669 2670 if (Vcb->superblock.num_devices > 1) { 2671 le2 = Vcb->devices.Flink; 2672 while (le2 != &Vcb->devices) { 2673 device* dev = CONTAINING_RECORD(le2, device, list_entry); 2674 2675 if (RtlCompareMemory(&dev->devitem.device_uuid, &devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { 2676 ExReleaseResourceLite(&Vcb->tree_lock); 2677 ExReleaseResourceLite(&global_loading_lock); 2678 return STATUS_DEVICE_NOT_READY; 2679 } 2680 2681 le2 = le2->Flink; 2682 } 2683 } 2684 2685 ExReleaseResourceLite(&Vcb->tree_lock); 2686 ExReleaseResourceLite(&global_loading_lock); 2687 return STATUS_SUCCESS; 2688 } 2689 2690 le = le->Flink; 2691 } 2692 2693 ExReleaseResourceLite(&global_loading_lock); 2694 2695 return STATUS_SUCCESS; 2696 } 2697 2698 void trim_whole_device(device* dev) { 2699 DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa; 2700 NTSTATUS Status; 2701 2702 // FIXME - avoid "bootloader area"?? 2703 2704 dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES); 2705 dmdsa.Action = DeviceDsmAction_Trim; 2706 dmdsa.Flags = DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE | DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED; 2707 dmdsa.ParameterBlockOffset = 0; 2708 dmdsa.ParameterBlockLength = 0; 2709 dmdsa.DataSetRangesOffset = 0; 2710 dmdsa.DataSetRangesLength = 0; 2711 2712 Status = dev_ioctl(dev->devobj, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, &dmdsa, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), NULL, 0, true, NULL); 2713 if (!NT_SUCCESS(Status)) 2714 WARN("IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES returned %08x\n", Status); 2715 } 2716 2717 static NTSTATUS add_device(device_extension* Vcb, PIRP Irp, KPROCESSOR_MODE processor_mode) { 2718 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 2719 NTSTATUS Status; 2720 PFILE_OBJECT fileobj, mountmgrfo; 2721 PDEVICE_OBJECT DeviceObject; 2722 HANDLE h; 2723 LIST_ENTRY* le; 2724 device* dev; 2725 DEV_ITEM* di; 2726 uint64_t dev_id, size; 2727 uint8_t* mb; 2728 uint64_t* stats; 2729 UNICODE_STRING mmdevpath, pnp_name, pnp_name2; 2730 volume_child* vc; 2731 PDEVICE_OBJECT mountmgr; 2732 KEY searchkey; 2733 traverse_ptr tp; 2734 STORAGE_DEVICE_NUMBER sdn; 2735 volume_device_extension* vde; 2736 pdo_device_extension* pdode; 2737 const GUID* pnp_guid; 2738 GET_LENGTH_INFORMATION gli; 2739 2740 pnp_name.Buffer = NULL; 2741 2742 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode)) 2743 return STATUS_PRIVILEGE_NOT_HELD; 2744 2745 if (!Vcb->vde) { 2746 WARN("not allowing second device to be added to non-PNP device\n"); 2747 return STATUS_NOT_SUPPORTED; 2748 } 2749 2750 if (Vcb->readonly) // FIXME - handle adding R/W device to seeding device 2751 return STATUS_MEDIA_WRITE_PROTECTED; 2752 2753 #if defined(_WIN64) 2754 if (IoIs32bitProcess(Irp)) { 2755 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(uint32_t)) 2756 return STATUS_INVALID_PARAMETER; 2757 2758 h = (HANDLE)LongToHandle((*(uint32_t*)Irp->AssociatedIrp.SystemBuffer)); 2759 } else { 2760 #endif 2761 if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) 2762 return STATUS_INVALID_PARAMETER; 2763 2764 h = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; 2765 #if defined(_WIN64) 2766 } 2767 #endif 2768 2769 Status = ObReferenceObjectByHandle(h, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&fileobj, NULL); 2770 2771 if (!NT_SUCCESS(Status)) { 2772 ERR("ObReferenceObjectByHandle returned %08x\n", Status); 2773 return Status; 2774 } 2775 2776 DeviceObject = fileobj->DeviceObject; 2777 2778 Status = get_device_pnp_name(DeviceObject, &pnp_name, &pnp_guid); 2779 if (!NT_SUCCESS(Status)) { 2780 ERR("get_device_pnp_name returned %08x\n", Status); 2781 ObDereferenceObject(fileobj); 2782 return Status; 2783 } 2784 2785 // If this is a disk, we have been handed the PDO, so need to go up to find something we can use 2786 if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID) && DeviceObject->AttachedDevice) 2787 DeviceObject = DeviceObject->AttachedDevice; 2788 2789 Status = dev_ioctl(DeviceObject, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0, true, NULL); 2790 if (!NT_SUCCESS(Status)) { 2791 ERR("IOCTL_DISK_IS_WRITABLE returned %08x\n", Status); 2792 ObDereferenceObject(fileobj); 2793 return Status; 2794 } 2795 2796 Status = is_device_part_of_mounted_btrfs_raid(DeviceObject, fileobj); 2797 if (!NT_SUCCESS(Status)) { 2798 ERR("is_device_part_of_mounted_btrfs_raid returned %08x\n", Status); 2799 ObDereferenceObject(fileobj); 2800 return Status; 2801 } 2802 2803 // if disk, check it has no partitions 2804 if (RtlCompareMemory(pnp_guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID)) { 2805 ULONG dlisize; 2806 DRIVE_LAYOUT_INFORMATION_EX* dli = NULL; 2807 2808 dlisize = 0; 2809 2810 do { 2811 dlisize += 1024; 2812 2813 if (dli) 2814 ExFreePool(dli); 2815 2816 dli = ExAllocatePoolWithTag(PagedPool, dlisize, ALLOC_TAG); 2817 if (!dli) { 2818 ERR("out of memory\n"); 2819 Status = STATUS_INSUFFICIENT_RESOURCES; 2820 goto end2; 2821 } 2822 2823 Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, dli, dlisize, true, NULL); 2824 } while (Status == STATUS_BUFFER_TOO_SMALL); 2825 2826 if (NT_SUCCESS(Status) && dli->PartitionCount > 0) { 2827 ExFreePool(dli); 2828 ERR("not adding disk which has partitions\n"); 2829 Status = STATUS_DEVICE_NOT_READY; 2830 goto end2; 2831 } 2832 2833 ExFreePool(dli); 2834 } 2835 2836 Status = dev_ioctl(DeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, 2837 &sdn, sizeof(STORAGE_DEVICE_NUMBER), true, NULL); 2838 if (NT_SUCCESS(Status)) { 2839 if (sdn.DeviceType != FILE_DEVICE_DISK) { // FIXME - accept floppies and CDs? 2840 WARN("device was not disk\n"); 2841 ObDereferenceObject(fileobj); 2842 return STATUS_INVALID_PARAMETER; 2843 } 2844 } else { 2845 sdn.DeviceNumber = 0xffffffff; 2846 sdn.PartitionNumber = 0xffffffff; 2847 } 2848 2849 Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, 2850 &gli, sizeof(gli), true, NULL); 2851 if (!NT_SUCCESS(Status)) { 2852 ERR("error reading length information: %08x\n", Status); 2853 ObDereferenceObject(fileobj); 2854 return Status; 2855 } 2856 2857 size = gli.Length.QuadPart; 2858 2859 if (size < 0x100000) { 2860 ERR("device was not large enough to hold FS (%I64x bytes, need at least 1 MB)\n", size); 2861 ObDereferenceObject(fileobj); 2862 return STATUS_INTERNAL_ERROR; 2863 } 2864 2865 volume_removal(drvobj, &pnp_name); 2866 2867 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 2868 2869 if (Vcb->need_write) 2870 Status = do_write(Vcb, Irp); 2871 else 2872 Status = STATUS_SUCCESS; 2873 2874 free_trees(Vcb); 2875 2876 if (!NT_SUCCESS(Status)) { 2877 ERR("do_write returned %08x\n", Status); 2878 goto end; 2879 } 2880 2881 dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device), ALLOC_TAG); 2882 if (!dev) { 2883 ERR("out of memory\n"); 2884 Status = STATUS_INSUFFICIENT_RESOURCES; 2885 goto end; 2886 } 2887 2888 RtlZeroMemory(dev, sizeof(device)); 2889 2890 dev->devobj = DeviceObject; 2891 dev->fileobj = fileobj; 2892 dev->seeding = false; 2893 init_device(Vcb, dev, true); 2894 2895 InitializeListHead(&dev->space); 2896 2897 if (size > 0x100000) { // add disk hole - the first MB is marked as used 2898 Status = add_space_entry(&dev->space, NULL, 0x100000, size - 0x100000); 2899 if (!NT_SUCCESS(Status)) { 2900 ERR("add_space_entry returned %08x\n", Status); 2901 goto end; 2902 } 2903 } 2904 2905 dev_id = 0; 2906 2907 le = Vcb->devices.Flink; 2908 while (le != &Vcb->devices) { 2909 device* dev2 = CONTAINING_RECORD(le, device, list_entry); 2910 2911 if (dev2->devitem.dev_id > dev_id) 2912 dev_id = dev2->devitem.dev_id; 2913 2914 le = le->Flink; 2915 } 2916 2917 dev_id++; 2918 2919 dev->devitem.dev_id = dev_id; 2920 dev->devitem.num_bytes = size; 2921 dev->devitem.bytes_used = 0; 2922 dev->devitem.optimal_io_align = Vcb->superblock.sector_size; 2923 dev->devitem.optimal_io_width = Vcb->superblock.sector_size; 2924 dev->devitem.minimal_io_size = Vcb->superblock.sector_size; 2925 dev->devitem.type = 0; 2926 dev->devitem.generation = 0; 2927 dev->devitem.start_offset = 0; 2928 dev->devitem.dev_group = 0; 2929 dev->devitem.seek_speed = 0; 2930 dev->devitem.bandwidth = 0; 2931 get_uuid(&dev->devitem.device_uuid); 2932 dev->devitem.fs_uuid = Vcb->superblock.uuid; 2933 2934 di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG); 2935 if (!di) { 2936 ERR("out of memory\n"); 2937 goto end; 2938 } 2939 2940 RtlCopyMemory(di, &dev->devitem, sizeof(DEV_ITEM)); 2941 2942 Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, di->dev_id, di, sizeof(DEV_ITEM), NULL, Irp); 2943 if (!NT_SUCCESS(Status)) { 2944 ERR("insert_tree_item returned %08x\n", Status); 2945 ExFreePool(di); 2946 goto end; 2947 } 2948 2949 // add stats entry to dev tree 2950 stats = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t) * 5, ALLOC_TAG); 2951 if (!stats) { 2952 ERR("out of memory\n"); 2953 Status = STATUS_INSUFFICIENT_RESOURCES; 2954 goto end; 2955 } 2956 2957 RtlZeroMemory(stats, sizeof(uint64_t) * 5); 2958 2959 searchkey.obj_id = 0; 2960 searchkey.obj_type = TYPE_DEV_STATS; 2961 searchkey.offset = di->dev_id; 2962 2963 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp); 2964 if (!NT_SUCCESS(Status)) { 2965 ERR("error - find_item returned %08x\n", Status); 2966 ExFreePool(stats); 2967 goto end; 2968 } 2969 2970 if (!keycmp(tp.item->key, searchkey)) { 2971 Status = delete_tree_item(Vcb, &tp); 2972 if (!NT_SUCCESS(Status)) { 2973 ERR("delete_tree_item returned %08x\n", Status); 2974 ExFreePool(stats); 2975 goto end; 2976 } 2977 } 2978 2979 Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, di->dev_id, stats, sizeof(uint64_t) * 5, NULL, Irp); 2980 if (!NT_SUCCESS(Status)) { 2981 ERR("insert_tree_item returned %08x\n", Status); 2982 ExFreePool(stats); 2983 goto end; 2984 } 2985 2986 if (dev->trim && !dev->readonly && !Vcb->options.no_trim) 2987 trim_whole_device(dev); 2988 2989 // We clear the first megabyte of the device, so Windows doesn't identify it as another FS 2990 mb = ExAllocatePoolWithTag(PagedPool, 0x100000, ALLOC_TAG); 2991 if (!mb) { 2992 ERR("out of memory\n"); 2993 Status = STATUS_INSUFFICIENT_RESOURCES; 2994 goto end; 2995 } 2996 2997 RtlZeroMemory(mb, 0x100000); 2998 2999 Status = write_data_phys(DeviceObject, fileobj, 0, mb, 0x100000); 3000 if (!NT_SUCCESS(Status)) { 3001 ERR("write_data_phys returned %08x\n", Status); 3002 ExFreePool(mb); 3003 goto end; 3004 } 3005 3006 ExFreePool(mb); 3007 3008 vde = Vcb->vde; 3009 pdode = vde->pdode; 3010 3011 vc = ExAllocatePoolWithTag(NonPagedPool, sizeof(volume_child), ALLOC_TAG); 3012 if (!vc) { 3013 ERR("out of memory\n"); 3014 Status = STATUS_INSUFFICIENT_RESOURCES; 3015 goto end; 3016 } 3017 3018 vc->uuid = dev->devitem.device_uuid; 3019 vc->devid = dev_id; 3020 vc->generation = Vcb->superblock.generation; 3021 vc->devobj = DeviceObject; 3022 vc->fileobj = fileobj; 3023 vc->notification_entry = NULL; 3024 3025 Status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0, fileobj, 3026 drvobj, pnp_removal, vde->pdode, &vc->notification_entry); 3027 if (!NT_SUCCESS(Status)) 3028 WARN("IoRegisterPlugPlayNotification returned %08x\n", Status); 3029 3030 pnp_name2 = pnp_name; 3031 3032 if (pnp_name.Length > 4 * sizeof(WCHAR) && pnp_name.Buffer[0] == '\\' && (pnp_name.Buffer[1] == '\\' || pnp_name.Buffer[1] == '?') && 3033 pnp_name.Buffer[2] == '?' && pnp_name.Buffer[3] == '\\') { 3034 pnp_name2.Buffer = &pnp_name2.Buffer[3]; 3035 pnp_name2.Length -= 3 * sizeof(WCHAR); 3036 pnp_name2.MaximumLength -= 3 * sizeof(WCHAR); 3037 } 3038 3039 vc->pnp_name.Length = vc->pnp_name.MaximumLength = pnp_name2.Length; 3040 3041 if (pnp_name2.Length == 0) 3042 vc->pnp_name.Buffer = NULL; 3043 else { 3044 vc->pnp_name.Buffer = ExAllocatePoolWithTag(PagedPool, pnp_name2.Length, ALLOC_TAG); 3045 if (!vc->pnp_name.Buffer) { 3046 ERR("out of memory\n"); 3047 Status = STATUS_INSUFFICIENT_RESOURCES; 3048 goto end; 3049 } 3050 3051 RtlCopyMemory(vc->pnp_name.Buffer, pnp_name2.Buffer, pnp_name2.Length); 3052 } 3053 3054 vc->size = size; 3055 vc->seeding = false; 3056 vc->disk_num = sdn.DeviceNumber; 3057 vc->part_num = sdn.PartitionNumber; 3058 vc->had_drive_letter = false; 3059 3060 ExAcquireResourceExclusiveLite(&pdode->child_lock, true); 3061 InsertTailList(&pdode->children, &vc->list_entry); 3062 pdode->num_children++; 3063 pdode->children_loaded++; 3064 ExReleaseResourceLite(&pdode->child_lock); 3065 3066 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); 3067 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &mountmgrfo, &mountmgr); 3068 if (!NT_SUCCESS(Status)) 3069 ERR("IoGetDeviceObjectPointer returned %08x\n", Status); 3070 else { 3071 Status = remove_drive_letter(mountmgr, &pnp_name); 3072 if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) 3073 WARN("remove_drive_letter returned %08x\n", Status); 3074 3075 vc->had_drive_letter = NT_SUCCESS(Status); 3076 3077 ObDereferenceObject(mountmgrfo); 3078 } 3079 3080 Vcb->superblock.num_devices++; 3081 Vcb->superblock.total_bytes += size; 3082 Vcb->devices_loaded++; 3083 InsertTailList(&Vcb->devices, &dev->list_entry); 3084 3085 // FIXME - send notification that volume size has increased 3086 3087 ObReferenceObject(DeviceObject); // for Vcb 3088 3089 Status = do_write(Vcb, Irp); 3090 if (!NT_SUCCESS(Status)) 3091 ERR("do_write returned %08x\n", Status); 3092 3093 ObReferenceObject(fileobj); 3094 3095 end: 3096 free_trees(Vcb); 3097 3098 ExReleaseResourceLite(&Vcb->tree_lock); 3099 3100 end2: 3101 ObDereferenceObject(fileobj); 3102 3103 if (pnp_name.Buffer) 3104 ExFreePool(pnp_name.Buffer); 3105 3106 if (NT_SUCCESS(Status)) 3107 FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE); 3108 3109 return Status; 3110 } 3111 3112 static NTSTATUS allow_extended_dasd_io(device_extension* Vcb, PFILE_OBJECT FileObject) { 3113 fcb* fcb; 3114 ccb* ccb; 3115 3116 TRACE("FSCTL_ALLOW_EXTENDED_DASD_IO\n"); 3117 3118 if (!FileObject) 3119 return STATUS_INVALID_PARAMETER; 3120 3121 fcb = FileObject->FsContext; 3122 ccb = FileObject->FsContext2; 3123 3124 if (!fcb) 3125 return STATUS_INVALID_PARAMETER; 3126 3127 if (fcb != Vcb->volume_fcb) 3128 return STATUS_INVALID_PARAMETER; 3129 3130 if (!ccb) 3131 return STATUS_INVALID_PARAMETER; 3132 3133 ccb->allow_extended_dasd_io = true; 3134 3135 return STATUS_SUCCESS; 3136 } 3137 3138 static NTSTATUS query_uuid(device_extension* Vcb, void* data, ULONG length) { 3139 if (length < sizeof(BTRFS_UUID)) 3140 return STATUS_BUFFER_OVERFLOW; 3141 3142 RtlCopyMemory(data, &Vcb->superblock.uuid, sizeof(BTRFS_UUID)); 3143 3144 return STATUS_SUCCESS; 3145 } 3146 3147 static NTSTATUS reset_stats(device_extension* Vcb, void* data, ULONG length, KPROCESSOR_MODE processor_mode) { 3148 uint64_t devid; 3149 NTSTATUS Status; 3150 LIST_ENTRY* le; 3151 3152 if (length < sizeof(uint64_t)) 3153 return STATUS_INVALID_PARAMETER; 3154 3155 if (Vcb->readonly) 3156 return STATUS_MEDIA_WRITE_PROTECTED; 3157 3158 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode)) 3159 return STATUS_PRIVILEGE_NOT_HELD; 3160 3161 devid = *((uint64_t*)data); 3162 3163 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 3164 3165 le = Vcb->devices.Flink; 3166 3167 while (le != &Vcb->devices) { 3168 device* dev = CONTAINING_RECORD(le, device, list_entry); 3169 3170 if (dev->devitem.dev_id == devid) { 3171 RtlZeroMemory(dev->stats, sizeof(uint64_t) * 5); 3172 dev->stats_changed = true; 3173 Vcb->stats_changed = true; 3174 Vcb->need_write = true; 3175 Status = STATUS_SUCCESS; 3176 goto end; 3177 } 3178 3179 le = le->Flink; 3180 } 3181 3182 WARN("device %I64x not found\n", devid); 3183 Status = STATUS_INVALID_PARAMETER; 3184 3185 end: 3186 ExReleaseResourceLite(&Vcb->tree_lock); 3187 3188 return Status; 3189 } 3190 3191 static NTSTATUS get_integrity_information(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen) { 3192 FSCTL_GET_INTEGRITY_INFORMATION_BUFFER* fgiib = (FSCTL_GET_INTEGRITY_INFORMATION_BUFFER*)data; 3193 3194 TRACE("FSCTL_GET_INTEGRITY_INFORMATION\n"); 3195 3196 // STUB 3197 3198 if (!FileObject) 3199 return STATUS_INVALID_PARAMETER; 3200 3201 if (!data || datalen < sizeof(FSCTL_GET_INTEGRITY_INFORMATION_BUFFER)) 3202 return STATUS_INVALID_PARAMETER; 3203 3204 fgiib->ChecksumAlgorithm = 0; 3205 fgiib->Reserved = 0; 3206 fgiib->Flags = 0; 3207 fgiib->ChecksumChunkSizeInBytes = Vcb->superblock.sector_size; 3208 fgiib->ClusterSizeInBytes = Vcb->superblock.sector_size; 3209 3210 return STATUS_SUCCESS; 3211 } 3212 3213 static NTSTATUS set_integrity_information(PFILE_OBJECT FileObject, void* data, ULONG datalen) { 3214 TRACE("FSCTL_SET_INTEGRITY_INFORMATION\n"); 3215 3216 // STUB 3217 3218 if (!FileObject) 3219 return STATUS_INVALID_PARAMETER; 3220 3221 if (!data || datalen < sizeof(FSCTL_SET_INTEGRITY_INFORMATION_BUFFER)) 3222 return STATUS_INVALID_PARAMETER; 3223 3224 return STATUS_SUCCESS; 3225 } 3226 3227 bool fcb_is_inline(fcb* fcb) { 3228 LIST_ENTRY* le; 3229 3230 le = fcb->extents.Flink; 3231 while (le != &fcb->extents) { 3232 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 3233 3234 if (!ext->ignore) 3235 return ext->extent_data.type == EXTENT_TYPE_INLINE; 3236 3237 le = le->Flink; 3238 } 3239 3240 return false; 3241 } 3242 3243 static NTSTATUS duplicate_extents(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) { 3244 DUPLICATE_EXTENTS_DATA* ded = (DUPLICATE_EXTENTS_DATA*)data; 3245 fcb *fcb = FileObject ? FileObject->FsContext : NULL, *sourcefcb; 3246 ccb *ccb = FileObject ? FileObject->FsContext2 : NULL, *sourceccb; 3247 NTSTATUS Status; 3248 PFILE_OBJECT sourcefo; 3249 uint64_t sourcelen, nbytes = 0; 3250 LIST_ENTRY rollback, *le, newexts; 3251 LARGE_INTEGER time; 3252 BTRFS_TIME now; 3253 bool make_inline; 3254 3255 if (!ded || datalen < sizeof(DUPLICATE_EXTENTS_DATA)) 3256 return STATUS_BUFFER_TOO_SMALL; 3257 3258 if (Vcb->readonly) 3259 return STATUS_MEDIA_WRITE_PROTECTED; 3260 3261 if (ded->ByteCount.QuadPart == 0) 3262 return STATUS_SUCCESS; 3263 3264 if (!fcb || !ccb || fcb == Vcb->volume_fcb) 3265 return STATUS_INVALID_PARAMETER; 3266 3267 if (is_subvol_readonly(fcb->subvol, Irp)) 3268 return STATUS_ACCESS_DENIED; 3269 3270 if (Irp->RequestorMode == UserMode && !(ccb->access & FILE_WRITE_DATA)) { 3271 WARN("insufficient privileges\n"); 3272 return STATUS_ACCESS_DENIED; 3273 } 3274 3275 if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK) 3276 return STATUS_INVALID_PARAMETER; 3277 3278 Status = ObReferenceObjectByHandle(ded->FileHandle, 0, *IoFileObjectType, Irp->RequestorMode, (void**)&sourcefo, NULL); 3279 if (!NT_SUCCESS(Status)) { 3280 ERR("ObReferenceObjectByHandle returned %08x\n", Status); 3281 return Status; 3282 } 3283 3284 if (sourcefo->DeviceObject != FileObject->DeviceObject) { 3285 WARN("source and destination are on different volumes\n"); 3286 ObDereferenceObject(sourcefo); 3287 return STATUS_INVALID_PARAMETER; 3288 } 3289 3290 sourcefcb = sourcefo->FsContext; 3291 sourceccb = sourcefo->FsContext2; 3292 3293 if (!sourcefcb || !sourceccb || sourcefcb == Vcb->volume_fcb) { 3294 ObDereferenceObject(sourcefo); 3295 return STATUS_INVALID_PARAMETER; 3296 } 3297 3298 if (!sourcefcb->ads && !fcb->ads) { 3299 if ((ded->SourceFileOffset.QuadPart & (Vcb->superblock.sector_size - 1)) || (ded->TargetFileOffset.QuadPart & (Vcb->superblock.sector_size - 1))) { 3300 ObDereferenceObject(sourcefo); 3301 return STATUS_INVALID_PARAMETER; 3302 } 3303 3304 if (ded->ByteCount.QuadPart & (Vcb->superblock.sector_size - 1)) { 3305 ObDereferenceObject(sourcefo); 3306 return STATUS_INVALID_PARAMETER; 3307 } 3308 } 3309 3310 if (Irp->RequestorMode == UserMode && (!(sourceccb->access & FILE_READ_DATA) || !(sourceccb->access & FILE_READ_ATTRIBUTES))) { 3311 WARN("insufficient privileges\n"); 3312 ObDereferenceObject(sourcefo); 3313 return STATUS_ACCESS_DENIED; 3314 } 3315 3316 if (!sourcefcb->ads && sourcefcb->type != BTRFS_TYPE_FILE && sourcefcb->type != BTRFS_TYPE_SYMLINK) { 3317 ObDereferenceObject(sourcefo); 3318 return STATUS_INVALID_PARAMETER; 3319 } 3320 3321 sourcelen = sourcefcb->ads ? sourcefcb->adsdata.Length : sourcefcb->inode_item.st_size; 3322 3323 if (sector_align(sourcelen, Vcb->superblock.sector_size) < (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart) { 3324 ObDereferenceObject(sourcefo); 3325 return STATUS_NOT_SUPPORTED; 3326 } 3327 3328 if (fcb == sourcefcb && 3329 ((ded->SourceFileOffset.QuadPart >= ded->TargetFileOffset.QuadPart && ded->SourceFileOffset.QuadPart < ded->TargetFileOffset.QuadPart + ded->ByteCount.QuadPart) || 3330 (ded->TargetFileOffset.QuadPart >= ded->SourceFileOffset.QuadPart && ded->TargetFileOffset.QuadPart < ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart))) { 3331 WARN("source and destination are the same, and the ranges overlap\n"); 3332 ObDereferenceObject(sourcefo); 3333 return STATUS_INVALID_PARAMETER; 3334 } 3335 3336 // fail if nocsum flag set on one file but not the other 3337 if (!fcb->ads && !sourcefcb->ads && (fcb->inode_item.flags & BTRFS_INODE_NODATASUM) != (sourcefcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { 3338 ObDereferenceObject(sourcefo); 3339 return STATUS_INVALID_PARAMETER; 3340 } 3341 3342 InitializeListHead(&rollback); 3343 InitializeListHead(&newexts); 3344 3345 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 3346 3347 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 3348 3349 if (fcb != sourcefcb) 3350 ExAcquireResourceSharedLite(sourcefcb->Header.Resource, true); 3351 3352 if (!FsRtlFastCheckLockForWrite(&fcb->lock, &ded->TargetFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) { 3353 Status = STATUS_FILE_LOCK_CONFLICT; 3354 goto end; 3355 } 3356 3357 if (!FsRtlFastCheckLockForRead(&sourcefcb->lock, &ded->SourceFileOffset, &ded->ByteCount, 0, FileObject, PsGetCurrentProcess())) { 3358 Status = STATUS_FILE_LOCK_CONFLICT; 3359 goto end; 3360 } 3361 3362 make_inline = fcb->ads ? false : (fcb->inode_item.st_size <= Vcb->options.max_inline || fcb_is_inline(fcb)); 3363 3364 if (fcb->ads || sourcefcb->ads || make_inline || fcb_is_inline(sourcefcb)) { 3365 uint8_t* data2; 3366 ULONG bytes_read, dataoff, datalen2; 3367 3368 if (make_inline) { 3369 dataoff = (ULONG)ded->TargetFileOffset.QuadPart; 3370 datalen2 = (ULONG)fcb->inode_item.st_size; 3371 } else if (fcb->ads) { 3372 dataoff = 0; 3373 datalen2 = (ULONG)ded->ByteCount.QuadPart; 3374 } else { 3375 dataoff = ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size; 3376 datalen2 = (ULONG)sector_align(ded->ByteCount.QuadPart + dataoff, Vcb->superblock.sector_size); 3377 } 3378 3379 data2 = ExAllocatePoolWithTag(PagedPool, datalen2, ALLOC_TAG); 3380 if (!data2) { 3381 ERR("out of memory\n"); 3382 Status = STATUS_INSUFFICIENT_RESOURCES; 3383 goto end; 3384 } 3385 3386 if (dataoff > 0) { 3387 if (make_inline) 3388 Status = read_file(fcb, data2, 0, datalen2, NULL, Irp); 3389 else 3390 Status = read_file(fcb, data2, ded->TargetFileOffset.QuadPart - dataoff, dataoff, NULL, Irp); 3391 3392 if (!NT_SUCCESS(Status)) { 3393 ERR("read_file returned %08x\n", Status); 3394 ExFreePool(data2); 3395 goto end; 3396 } 3397 } 3398 3399 if (sourcefcb->ads) { 3400 Status = read_stream(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, (ULONG)ded->ByteCount.QuadPart, &bytes_read); 3401 if (!NT_SUCCESS(Status)) { 3402 ERR("read_stream returned %08x\n", Status); 3403 ExFreePool(data2); 3404 goto end; 3405 } 3406 } else { 3407 Status = read_file(sourcefcb, data2 + dataoff, ded->SourceFileOffset.QuadPart, ded->ByteCount.QuadPart, &bytes_read, Irp); 3408 if (!NT_SUCCESS(Status)) { 3409 ERR("read_file returned %08x\n", Status); 3410 ExFreePool(data2); 3411 goto end; 3412 } 3413 } 3414 3415 if (dataoff + bytes_read < datalen2) 3416 RtlZeroMemory(data2 + dataoff + bytes_read, datalen2 - bytes_read); 3417 3418 if (fcb->ads) 3419 RtlCopyMemory(&fcb->adsdata.Buffer[ded->TargetFileOffset.QuadPart], data2, (USHORT)min(ded->ByteCount.QuadPart, fcb->adsdata.Length - ded->TargetFileOffset.QuadPart)); 3420 else if (make_inline) { 3421 uint16_t edsize; 3422 EXTENT_DATA* ed; 3423 3424 Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback); 3425 if (!NT_SUCCESS(Status)) { 3426 ERR("excise_extents returned %08x\n", Status); 3427 ExFreePool(data2); 3428 goto end; 3429 } 3430 3431 edsize = (uint16_t)(offsetof(EXTENT_DATA, data[0]) + datalen2); 3432 3433 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG); 3434 if (!ed) { 3435 ERR("out of memory\n"); 3436 ExFreePool(data2); 3437 Status = STATUS_INSUFFICIENT_RESOURCES; 3438 goto end; 3439 } 3440 3441 ed->generation = Vcb->superblock.generation; 3442 ed->decoded_size = fcb->inode_item.st_size; 3443 ed->compression = BTRFS_COMPRESSION_NONE; 3444 ed->encryption = BTRFS_ENCRYPTION_NONE; 3445 ed->encoding = BTRFS_ENCODING_NONE; 3446 ed->type = EXTENT_TYPE_INLINE; 3447 3448 RtlCopyMemory(ed->data, data2, datalen2); 3449 3450 Status = add_extent_to_fcb(fcb, 0, ed, edsize, false, NULL, &rollback); 3451 if (!NT_SUCCESS(Status)) { 3452 ERR("add_extent_to_fcb returned %08x\n", Status); 3453 ExFreePool(data2); 3454 goto end; 3455 } 3456 3457 fcb->inode_item.st_blocks += datalen2; 3458 } else { 3459 uint64_t start = ded->TargetFileOffset.QuadPart - (ded->TargetFileOffset.QuadPart % Vcb->superblock.sector_size); 3460 3461 Status = do_write_file(fcb, start, start + datalen2, data2, Irp, false, 0, &rollback); 3462 if (!NT_SUCCESS(Status)) { 3463 ERR("do_write_file returned %08x\n", Status); 3464 ExFreePool(data2); 3465 goto end; 3466 } 3467 } 3468 3469 ExFreePool(data2); 3470 } else { 3471 LIST_ENTRY* lastextle; 3472 3473 le = sourcefcb->extents.Flink; 3474 while (le != &sourcefcb->extents) { 3475 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 3476 3477 if (!ext->ignore) { 3478 if (ext->offset >= (uint64_t)ded->SourceFileOffset.QuadPart + (uint64_t)ded->ByteCount.QuadPart) 3479 break; 3480 3481 if (ext->extent_data.type != EXTENT_TYPE_INLINE) { 3482 ULONG extlen = offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); 3483 extent* ext2; 3484 EXTENT_DATA2 *ed2s, *ed2d; 3485 chunk* c; 3486 3487 ed2s = (EXTENT_DATA2*)ext->extent_data.data; 3488 3489 if (ext->offset + ed2s->num_bytes <= (uint64_t)ded->SourceFileOffset.QuadPart) { 3490 le = le->Flink; 3491 continue; 3492 } 3493 3494 ext2 = ExAllocatePoolWithTag(PagedPool, extlen, ALLOC_TAG); 3495 if (!ext2) { 3496 ERR("out of memory\n"); 3497 Status = STATUS_INSUFFICIENT_RESOURCES; 3498 goto end; 3499 } 3500 3501 if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart) 3502 ext2->offset = ded->TargetFileOffset.QuadPart; 3503 else 3504 ext2->offset = ext->offset - ded->SourceFileOffset.QuadPart + ded->TargetFileOffset.QuadPart; 3505 3506 ext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); 3507 ext2->unique = false; 3508 ext2->ignore = false; 3509 ext2->inserted = true; 3510 3511 ext2->extent_data.generation = Vcb->superblock.generation; 3512 ext2->extent_data.decoded_size = ext->extent_data.decoded_size; 3513 ext2->extent_data.compression = ext->extent_data.compression; 3514 ext2->extent_data.encryption = ext->extent_data.encryption; 3515 ext2->extent_data.encoding = ext->extent_data.encoding; 3516 ext2->extent_data.type = ext->extent_data.type; 3517 3518 ed2d = (EXTENT_DATA2*)ext2->extent_data.data; 3519 3520 ed2d->address = ed2s->address; 3521 ed2d->size = ed2s->size; 3522 3523 if (ext->offset < (uint64_t)ded->SourceFileOffset.QuadPart) { 3524 ed2d->offset = ed2s->offset + ded->SourceFileOffset.QuadPart - ext->offset; 3525 ed2d->num_bytes = min((uint64_t)ded->ByteCount.QuadPart, ed2s->num_bytes + ext->offset - ded->SourceFileOffset.QuadPart); 3526 } else { 3527 ed2d->offset = ed2s->offset; 3528 ed2d->num_bytes = min(ded->SourceFileOffset.QuadPart + ded->ByteCount.QuadPart - ext->offset, ed2s->num_bytes); 3529 } 3530 3531 if (ext->csum) { 3532 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) { 3533 ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->num_bytes * sizeof(uint32_t) / Vcb->superblock.sector_size), ALLOC_TAG); 3534 if (!ext2->csum) { 3535 ERR("out of memory\n"); 3536 Status = STATUS_INSUFFICIENT_RESOURCES; 3537 ExFreePool(ext2); 3538 goto end; 3539 } 3540 3541 RtlCopyMemory(ext2->csum, &ext->csum[(ed2d->offset - ed2s->offset) / Vcb->superblock.sector_size], 3542 (ULONG)(ed2d->num_bytes * sizeof(uint32_t) / Vcb->superblock.sector_size)); 3543 } else { 3544 ext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2d->size * sizeof(uint32_t) / Vcb->superblock.sector_size), ALLOC_TAG); 3545 if (!ext2->csum) { 3546 ERR("out of memory\n"); 3547 Status = STATUS_INSUFFICIENT_RESOURCES; 3548 ExFreePool(ext2); 3549 goto end; 3550 } 3551 3552 RtlCopyMemory(ext2->csum, ext->csum, (ULONG)(ed2s->size * sizeof(uint32_t) / Vcb->superblock.sector_size)); 3553 } 3554 } else 3555 ext2->csum = NULL; 3556 3557 InsertTailList(&newexts, &ext2->list_entry); 3558 3559 c = get_chunk_from_address(Vcb, ed2s->address); 3560 if (!c) { 3561 ERR("get_chunk_from_address(%I64x) failed\n", ed2s->address); 3562 Status = STATUS_INTERNAL_ERROR; 3563 goto end; 3564 } 3565 3566 Status = update_changed_extent_ref(Vcb, c, ed2s->address, ed2s->size, fcb->subvol->id, fcb->inode, ext2->offset - ed2d->offset, 3567 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp); 3568 if (!NT_SUCCESS(Status)) { 3569 ERR("update_changed_extent_ref returned %08x\n", Status); 3570 goto end; 3571 } 3572 3573 nbytes += ed2d->num_bytes; 3574 } 3575 } 3576 3577 le = le->Flink; 3578 } 3579 3580 Status = excise_extents(Vcb, fcb, ded->TargetFileOffset.QuadPart, ded->TargetFileOffset.QuadPart + ded->ByteCount.QuadPart, Irp, &rollback); 3581 if (!NT_SUCCESS(Status)) { 3582 ERR("excise_extents returned %08x\n", Status); 3583 3584 while (!IsListEmpty(&newexts)) { 3585 extent* ext = CONTAINING_RECORD(RemoveHeadList(&newexts), extent, list_entry); 3586 ExFreePool(ext); 3587 } 3588 3589 goto end; 3590 } 3591 3592 // clear unique flags in source fcb 3593 le = sourcefcb->extents.Flink; 3594 while (le != &sourcefcb->extents) { 3595 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 3596 3597 if (!ext->ignore && ext->unique && (ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC)) { 3598 EXTENT_DATA2* ed2s = (EXTENT_DATA2*)ext->extent_data.data; 3599 LIST_ENTRY* le2; 3600 3601 le2 = newexts.Flink; 3602 while (le2 != &newexts) { 3603 extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry); 3604 3605 if (ext2->extent_data.type == EXTENT_TYPE_REGULAR || ext2->extent_data.type == EXTENT_TYPE_PREALLOC) { 3606 EXTENT_DATA2* ed2d = (EXTENT_DATA2*)ext2->extent_data.data; 3607 3608 if (ed2d->address == ed2s->address && ed2d->size == ed2s->size) { 3609 ext->unique = false; 3610 break; 3611 } 3612 } 3613 3614 le2 = le2->Flink; 3615 } 3616 } 3617 3618 le = le->Flink; 3619 } 3620 3621 lastextle = &fcb->extents; 3622 while (!IsListEmpty(&newexts)) { 3623 extent* ext = CONTAINING_RECORD(RemoveHeadList(&newexts), extent, list_entry); 3624 3625 add_extent(fcb, lastextle, ext); 3626 lastextle = &ext->list_entry; 3627 } 3628 } 3629 3630 KeQuerySystemTime(&time); 3631 win_time_to_unix(time, &now); 3632 3633 if (fcb->ads) { 3634 ccb->fileref->parent->fcb->inode_item.sequence++; 3635 3636 if (!ccb->user_set_change_time) 3637 ccb->fileref->parent->fcb->inode_item.st_ctime = now; 3638 3639 ccb->fileref->parent->fcb->inode_item_changed = true; 3640 mark_fcb_dirty(ccb->fileref->parent->fcb); 3641 } else { 3642 fcb->inode_item.st_blocks += nbytes; 3643 fcb->inode_item.sequence++; 3644 3645 if (!ccb->user_set_change_time) 3646 fcb->inode_item.st_ctime = now; 3647 3648 if (!ccb->user_set_write_time) { 3649 fcb->inode_item.st_mtime = now; 3650 send_notification_fcb(ccb->fileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 3651 } 3652 3653 fcb->inode_item_changed = true; 3654 fcb->extents_changed = true; 3655 } 3656 3657 mark_fcb_dirty(fcb); 3658 3659 if (FileObject->SectionObjectPointer->DataSectionObject) 3660 CcPurgeCacheSection(FileObject->SectionObjectPointer, &ded->TargetFileOffset, (ULONG)ded->ByteCount.QuadPart, false); 3661 3662 Status = STATUS_SUCCESS; 3663 3664 end: 3665 ObDereferenceObject(sourcefo); 3666 3667 if (NT_SUCCESS(Status)) 3668 clear_rollback(&rollback); 3669 else 3670 do_rollback(Vcb, &rollback); 3671 3672 if (fcb != sourcefcb) 3673 ExReleaseResourceLite(sourcefcb->Header.Resource); 3674 3675 ExReleaseResourceLite(fcb->Header.Resource); 3676 3677 ExReleaseResourceLite(&Vcb->tree_lock); 3678 3679 return Status; 3680 } 3681 3682 static NTSTATUS mknod(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) { 3683 NTSTATUS Status; 3684 btrfs_mknod* bmn; 3685 fcb *parfcb, *fcb; 3686 ccb* parccb; 3687 file_ref *parfileref, *fileref; 3688 UNICODE_STRING name; 3689 root* subvol; 3690 uint64_t inode; 3691 dir_child* dc; 3692 LARGE_INTEGER time; 3693 BTRFS_TIME now; 3694 LIST_ENTRY* lastle; 3695 ANSI_STRING utf8; 3696 ULONG len, i; 3697 SECURITY_SUBJECT_CONTEXT subjcont; 3698 PSID owner; 3699 BOOLEAN defaulted; 3700 3701 TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen); 3702 3703 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb) 3704 return STATUS_INVALID_PARAMETER; 3705 3706 if (Vcb->readonly) 3707 return STATUS_MEDIA_WRITE_PROTECTED; 3708 3709 parfcb = FileObject->FsContext; 3710 3711 if (parfcb->type != BTRFS_TYPE_DIRECTORY) { 3712 WARN("trying to create file in something other than a directory\n"); 3713 return STATUS_INVALID_PARAMETER; 3714 } 3715 3716 if (is_subvol_readonly(parfcb->subvol, Irp)) 3717 return STATUS_ACCESS_DENIED; 3718 3719 parccb = FileObject->FsContext2; 3720 parfileref = parccb->fileref; 3721 3722 if (!parfileref) 3723 return STATUS_INVALID_PARAMETER; 3724 3725 if (datalen < sizeof(btrfs_mknod)) 3726 return STATUS_INVALID_PARAMETER; 3727 3728 bmn = (btrfs_mknod*)data; 3729 3730 if (datalen < offsetof(btrfs_mknod, name[0]) + bmn->namelen || bmn->namelen < sizeof(WCHAR)) 3731 return STATUS_INVALID_PARAMETER; 3732 3733 if (bmn->type == BTRFS_TYPE_UNKNOWN || bmn->type > BTRFS_TYPE_SYMLINK) 3734 return STATUS_INVALID_PARAMETER; 3735 3736 if ((bmn->type == BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_SUBDIRECTORY)) || 3737 (bmn->type != BTRFS_TYPE_DIRECTORY && !(parccb->access & FILE_ADD_FILE))) { 3738 WARN("insufficient privileges\n"); 3739 return STATUS_ACCESS_DENIED; 3740 } 3741 3742 if (bmn->inode != 0) { 3743 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) 3744 return STATUS_PRIVILEGE_NOT_HELD; 3745 } 3746 3747 for (i = 0; i < bmn->namelen / sizeof(WCHAR); i++) { 3748 if (bmn->name[i] == 0 || bmn->name[i] == '/') 3749 return STATUS_OBJECT_NAME_INVALID; 3750 } 3751 3752 // don't allow files called . or .. 3753 if (bmn->name[0] == '.' && (bmn->namelen == sizeof(WCHAR) || (bmn->namelen == 2 * sizeof(WCHAR) && bmn->name[1] == '.'))) 3754 return STATUS_OBJECT_NAME_INVALID; 3755 3756 Status = utf16_to_utf8(NULL, 0, &len, bmn->name, bmn->namelen); 3757 if (!NT_SUCCESS(Status)) { 3758 ERR("utf16_to_utf8 return %08x\n", Status); 3759 return Status; 3760 } 3761 3762 if (len == 0) { 3763 ERR("utf16_to_utf8 returned a length of 0\n"); 3764 return STATUS_INTERNAL_ERROR; 3765 } 3766 3767 if (len > 0xffff) { 3768 ERR("len was too long (%x)\n", len); 3769 return STATUS_INVALID_PARAMETER; 3770 } 3771 3772 utf8.MaximumLength = utf8.Length = (USHORT)len; 3773 utf8.Buffer = ExAllocatePoolWithTag(PagedPool, utf8.MaximumLength, ALLOC_TAG); 3774 3775 if (!utf8.Buffer) { 3776 ERR("out of memory\n"); 3777 return STATUS_INSUFFICIENT_RESOURCES; 3778 } 3779 3780 Status = utf16_to_utf8(utf8.Buffer, len, &len, bmn->name, bmn->namelen); 3781 if (!NT_SUCCESS(Status)) { 3782 ERR("utf16_to_utf8 failed with error %08x\n", Status); 3783 ExFreePool(utf8.Buffer); 3784 return Status; 3785 } 3786 3787 name.Length = name.MaximumLength = bmn->namelen; 3788 name.Buffer = bmn->name; 3789 3790 Status = find_file_in_dir(&name, parfcb, &subvol, &inode, &dc, true); 3791 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) { 3792 ERR("find_file_in_dir returned %08x\n", Status); 3793 goto end; 3794 } 3795 3796 if (NT_SUCCESS(Status)) { 3797 WARN("filename already exists\n"); 3798 Status = STATUS_OBJECT_NAME_COLLISION; 3799 goto end; 3800 } 3801 3802 KeQuerySystemTime(&time); 3803 win_time_to_unix(time, &now); 3804 3805 fcb = create_fcb(Vcb, PagedPool); 3806 if (!fcb) { 3807 ERR("out of memory\n"); 3808 Status = STATUS_INSUFFICIENT_RESOURCES; 3809 goto end; 3810 } 3811 3812 fcb->Vcb = Vcb; 3813 3814 fcb->inode_item.generation = Vcb->superblock.generation; 3815 fcb->inode_item.transid = Vcb->superblock.generation; 3816 fcb->inode_item.st_size = 0; 3817 fcb->inode_item.st_blocks = 0; 3818 fcb->inode_item.block_group = 0; 3819 fcb->inode_item.st_nlink = 1; 3820 fcb->inode_item.st_uid = UID_NOBODY; 3821 fcb->inode_item.st_gid = GID_NOBODY; 3822 fcb->inode_item.st_mode = inherit_mode(parfcb, bmn->type == BTRFS_TYPE_DIRECTORY); 3823 3824 if (bmn->type == BTRFS_TYPE_BLOCKDEV || bmn->type == BTRFS_TYPE_CHARDEV) 3825 fcb->inode_item.st_rdev = (minor(bmn->st_rdev) & 0xFFFFF) | ((major(bmn->st_rdev) & 0xFFFFFFFFFFF) << 20); 3826 else 3827 fcb->inode_item.st_rdev = 0; 3828 3829 fcb->inode_item.flags = 0; 3830 fcb->inode_item.sequence = 1; 3831 fcb->inode_item.st_atime = now; 3832 fcb->inode_item.st_ctime = now; 3833 fcb->inode_item.st_mtime = now; 3834 fcb->inode_item.otime = now; 3835 3836 if (bmn->type == BTRFS_TYPE_DIRECTORY) 3837 fcb->inode_item.st_mode |= __S_IFDIR; 3838 else if (bmn->type == BTRFS_TYPE_CHARDEV) 3839 fcb->inode_item.st_mode |= __S_IFCHR; 3840 else if (bmn->type == BTRFS_TYPE_BLOCKDEV) 3841 fcb->inode_item.st_mode |= __S_IFBLK; 3842 else if (bmn->type == BTRFS_TYPE_FIFO) 3843 fcb->inode_item.st_mode |= __S_IFIFO; 3844 else if (bmn->type == BTRFS_TYPE_SOCKET) 3845 fcb->inode_item.st_mode |= __S_IFSOCK; 3846 else if (bmn->type == BTRFS_TYPE_SYMLINK) 3847 fcb->inode_item.st_mode |= __S_IFLNK; 3848 else 3849 fcb->inode_item.st_mode |= __S_IFREG; 3850 3851 if (bmn->type != BTRFS_TYPE_DIRECTORY) 3852 fcb->inode_item.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); // remove executable bit if not directory 3853 3854 // inherit nodatacow flag from parent directory 3855 if (parfcb->inode_item.flags & BTRFS_INODE_NODATACOW) { 3856 fcb->inode_item.flags |= BTRFS_INODE_NODATACOW; 3857 3858 if (bmn->type != BTRFS_TYPE_DIRECTORY) 3859 fcb->inode_item.flags |= BTRFS_INODE_NODATASUM; 3860 } 3861 3862 if (parfcb->inode_item.flags & BTRFS_INODE_COMPRESS) 3863 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS; 3864 3865 fcb->prop_compression = parfcb->prop_compression; 3866 fcb->prop_compression_changed = fcb->prop_compression != PropCompression_None; 3867 3868 fcb->inode_item_changed = true; 3869 3870 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 3871 fcb->Header.AllocationSize.QuadPart = 0; 3872 fcb->Header.FileSize.QuadPart = 0; 3873 fcb->Header.ValidDataLength.QuadPart = 0; 3874 3875 fcb->atts = 0; 3876 3877 if (bmn->name[0] == '.') 3878 fcb->atts |= FILE_ATTRIBUTE_HIDDEN; 3879 3880 if (bmn->type == BTRFS_TYPE_DIRECTORY) 3881 fcb->atts |= FILE_ATTRIBUTE_DIRECTORY; 3882 3883 fcb->atts_changed = false; 3884 3885 InterlockedIncrement(&parfcb->refcount); 3886 fcb->subvol = parfcb->subvol; 3887 3888 SeCaptureSubjectContext(&subjcont); 3889 3890 Status = SeAssignSecurityEx(parfileref ? parfileref->fcb->sd : NULL, NULL, (void**)&fcb->sd, NULL, fcb->type == BTRFS_TYPE_DIRECTORY, 3891 SEF_SACL_AUTO_INHERIT, &subjcont, IoGetFileObjectGenericMapping(), PagedPool); 3892 3893 if (!NT_SUCCESS(Status)) { 3894 ERR("SeAssignSecurityEx returned %08x\n", Status); 3895 reap_fcb(fcb); 3896 goto end; 3897 } 3898 3899 Status = RtlGetOwnerSecurityDescriptor(fcb->sd, &owner, &defaulted); 3900 if (!NT_SUCCESS(Status)) { 3901 WARN("RtlGetOwnerSecurityDescriptor returned %08x\n", Status); 3902 fcb->sd_dirty = true; 3903 } else { 3904 fcb->inode_item.st_uid = sid_to_uid(owner); 3905 fcb->sd_dirty = fcb->inode_item.st_uid == UID_NOBODY; 3906 } 3907 3908 find_gid(fcb, parfcb, &subjcont); 3909 3910 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true); 3911 acquire_fcb_lock_exclusive(Vcb); 3912 3913 if (bmn->inode == 0) { 3914 inode = InterlockedIncrement64(&parfcb->subvol->lastinode); 3915 lastle = parfcb->subvol->fcbs.Blink; 3916 } else { 3917 if (bmn->inode > (uint64_t)parfcb->subvol->lastinode) { 3918 inode = parfcb->subvol->lastinode = bmn->inode; 3919 lastle = parfcb->subvol->fcbs.Blink; 3920 } else { 3921 LIST_ENTRY* le = parfcb->subvol->fcbs.Flink; 3922 3923 lastle = parfcb->subvol->fcbs.Blink;; 3924 while (le != &parfcb->subvol->fcbs) { 3925 struct _fcb* fcb2 = CONTAINING_RECORD(le, struct _fcb, list_entry); 3926 3927 if (fcb2->inode == bmn->inode && !fcb2->deleted) { 3928 release_fcb_lock(Vcb); 3929 ExReleaseResourceLite(&Vcb->fileref_lock); 3930 3931 WARN("inode collision\n"); 3932 Status = STATUS_INVALID_PARAMETER; 3933 goto end; 3934 } else if (fcb2->inode > bmn->inode) { 3935 lastle = fcb2->list_entry.Blink; 3936 break; 3937 } 3938 3939 le = le->Flink; 3940 } 3941 3942 inode = bmn->inode; 3943 } 3944 } 3945 3946 fcb->inode = inode; 3947 fcb->type = bmn->type; 3948 3949 fileref = create_fileref(Vcb); 3950 if (!fileref) { 3951 release_fcb_lock(Vcb); 3952 ExReleaseResourceLite(&Vcb->fileref_lock); 3953 3954 ERR("out of memory\n"); 3955 reap_fcb(fcb); 3956 Status = STATUS_INSUFFICIENT_RESOURCES; 3957 goto end; 3958 } 3959 3960 fileref->fcb = fcb; 3961 3962 fcb->created = true; 3963 fileref->created = true; 3964 3965 fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 3966 fcb->subvol->root_item.ctime = now; 3967 3968 fileref->parent = parfileref; 3969 3970 mark_fcb_dirty(fcb); 3971 mark_fileref_dirty(fileref); 3972 3973 Status = add_dir_child(fileref->parent->fcb, fcb->inode, false, &utf8, &name, fcb->type, &dc); 3974 if (!NT_SUCCESS(Status)) 3975 WARN("add_dir_child returned %08x\n", Status); 3976 3977 fileref->dc = dc; 3978 dc->fileref = fileref; 3979 3980 ExAcquireResourceExclusiveLite(&parfileref->fcb->nonpaged->dir_children_lock, true); 3981 InsertTailList(&parfileref->children, &fileref->list_entry); 3982 ExReleaseResourceLite(&parfileref->fcb->nonpaged->dir_children_lock); 3983 3984 increase_fileref_refcount(parfileref); 3985 3986 if (fcb->type == BTRFS_TYPE_DIRECTORY) { 3987 fcb->hash_ptrs = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); 3988 if (!fcb->hash_ptrs) { 3989 release_fcb_lock(Vcb); 3990 ExReleaseResourceLite(&Vcb->fileref_lock); 3991 3992 ERR("out of memory\n"); 3993 free_fileref(fileref); 3994 Status = STATUS_INSUFFICIENT_RESOURCES; 3995 goto end; 3996 } 3997 3998 RtlZeroMemory(fcb->hash_ptrs, sizeof(LIST_ENTRY*) * 256); 3999 4000 fcb->hash_ptrs_uc = ExAllocatePoolWithTag(PagedPool, sizeof(LIST_ENTRY*) * 256, ALLOC_TAG); 4001 if (!fcb->hash_ptrs_uc) { 4002 release_fcb_lock(Vcb); 4003 ExReleaseResourceLite(&Vcb->fileref_lock); 4004 4005 ERR("out of memory\n"); 4006 free_fileref(fileref); 4007 Status = STATUS_INSUFFICIENT_RESOURCES; 4008 goto end; 4009 } 4010 4011 RtlZeroMemory(fcb->hash_ptrs_uc, sizeof(LIST_ENTRY*) * 256); 4012 } 4013 4014 InsertHeadList(lastle, &fcb->list_entry); 4015 InsertTailList(&Vcb->all_fcbs, &fcb->list_entry_all); 4016 4017 if (bmn->type == BTRFS_TYPE_DIRECTORY) 4018 fileref->fcb->fileref = fileref; 4019 4020 ExAcquireResourceExclusiveLite(parfcb->Header.Resource, true); 4021 parfcb->inode_item.st_size += utf8.Length * 2; 4022 parfcb->inode_item.transid = Vcb->superblock.generation; 4023 parfcb->inode_item.sequence++; 4024 4025 if (!parccb->user_set_change_time) 4026 parfcb->inode_item.st_ctime = now; 4027 4028 if (!parccb->user_set_write_time) 4029 parfcb->inode_item.st_mtime = now; 4030 4031 parfcb->subvol->fcbs_version++; 4032 4033 ExReleaseResourceLite(parfcb->Header.Resource); 4034 release_fcb_lock(Vcb); 4035 ExReleaseResourceLite(&Vcb->fileref_lock); 4036 4037 parfcb->inode_item_changed = true; 4038 mark_fcb_dirty(parfcb); 4039 4040 send_notification_fileref(fileref, bmn->type == BTRFS_TYPE_DIRECTORY ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED, NULL); 4041 4042 if (!parccb->user_set_write_time) 4043 send_notification_fcb(parfileref, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED, NULL); 4044 4045 Status = STATUS_SUCCESS; 4046 4047 end: 4048 4049 ExFreePool(utf8.Buffer); 4050 4051 return Status; 4052 } 4053 4054 static void mark_subvol_dirty(device_extension* Vcb, root* r) { 4055 if (!r->dirty) { 4056 r->dirty = true; 4057 4058 ExAcquireResourceExclusiveLite(&Vcb->dirty_subvols_lock, true); 4059 InsertTailList(&Vcb->dirty_subvols, &r->list_entry_dirty); 4060 ExReleaseResourceLite(&Vcb->dirty_subvols_lock); 4061 } 4062 4063 Vcb->need_write = true; 4064 } 4065 4066 static NTSTATUS recvd_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, KPROCESSOR_MODE processor_mode) { 4067 btrfs_received_subvol* brs = (btrfs_received_subvol*)data; 4068 fcb* fcb; 4069 NTSTATUS Status; 4070 LARGE_INTEGER time; 4071 BTRFS_TIME now; 4072 4073 TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen); 4074 4075 if (!data || datalen < sizeof(btrfs_received_subvol)) 4076 return STATUS_INVALID_PARAMETER; 4077 4078 if (!FileObject || !FileObject->FsContext || FileObject->FsContext == Vcb->volume_fcb) 4079 return STATUS_INVALID_PARAMETER; 4080 4081 fcb = FileObject->FsContext; 4082 4083 if (!fcb->subvol) 4084 return STATUS_INVALID_PARAMETER; 4085 4086 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode)) 4087 return STATUS_PRIVILEGE_NOT_HELD; 4088 4089 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 4090 4091 if (fcb->subvol->root_item.rtransid != 0) { 4092 WARN("subvol already has received information set\n"); 4093 Status = STATUS_INVALID_PARAMETER; 4094 goto end; 4095 } 4096 4097 KeQuerySystemTime(&time); 4098 win_time_to_unix(time, &now); 4099 4100 RtlCopyMemory(&fcb->subvol->root_item.received_uuid, &brs->uuid, sizeof(BTRFS_UUID)); 4101 fcb->subvol->root_item.stransid = brs->generation; 4102 fcb->subvol->root_item.rtransid = Vcb->superblock.generation; 4103 fcb->subvol->root_item.rtime = now; 4104 4105 fcb->subvol->received = true; 4106 mark_subvol_dirty(Vcb, fcb->subvol); 4107 4108 Status = STATUS_SUCCESS; 4109 4110 end: 4111 ExReleaseResourceLite(&Vcb->tree_lock); 4112 4113 return Status; 4114 } 4115 4116 static NTSTATUS fsctl_get_xattrs(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, KPROCESSOR_MODE processor_mode) { 4117 LIST_ENTRY* le; 4118 btrfs_set_xattr* bsxa; 4119 ULONG reqlen = (ULONG)offsetof(btrfs_set_xattr, data[0]); 4120 fcb* fcb; 4121 ccb* ccb; 4122 4123 if (!data || datalen < reqlen) 4124 return STATUS_INVALID_PARAMETER; 4125 4126 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb) 4127 return STATUS_INVALID_PARAMETER; 4128 4129 fcb = FileObject->FsContext; 4130 ccb = FileObject->FsContext2; 4131 4132 if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)) && processor_mode == UserMode) { 4133 WARN("insufficient privileges\n"); 4134 return STATUS_ACCESS_DENIED; 4135 } 4136 4137 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 4138 4139 le = fcb->xattrs.Flink; 4140 while (le != &fcb->xattrs) { 4141 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry); 4142 4143 if (xa->valuelen > 0) 4144 reqlen += (ULONG)offsetof(btrfs_set_xattr, data[0]) + xa->namelen + xa->valuelen; 4145 4146 le = le->Flink; 4147 } 4148 4149 if (datalen < reqlen) { 4150 ExReleaseResourceLite(fcb->Header.Resource); 4151 return STATUS_BUFFER_OVERFLOW; 4152 } 4153 4154 bsxa = (btrfs_set_xattr*)data; 4155 4156 if (reqlen > 0) { 4157 le = fcb->xattrs.Flink; 4158 while (le != &fcb->xattrs) { 4159 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry); 4160 4161 if (xa->valuelen > 0) { 4162 bsxa->namelen = xa->namelen; 4163 bsxa->valuelen = xa->valuelen; 4164 memcpy(bsxa->data, xa->data, xa->namelen + xa->valuelen); 4165 4166 bsxa = (btrfs_set_xattr*)&bsxa->data[xa->namelen + xa->valuelen]; 4167 } 4168 4169 le = le->Flink; 4170 } 4171 } 4172 4173 bsxa->namelen = 0; 4174 bsxa->valuelen = 0; 4175 4176 ExReleaseResourceLite(fcb->Header.Resource); 4177 4178 return STATUS_SUCCESS; 4179 } 4180 4181 static NTSTATUS fsctl_set_xattr(device_extension* Vcb, PFILE_OBJECT FileObject, void* data, ULONG datalen, PIRP Irp) { 4182 NTSTATUS Status; 4183 btrfs_set_xattr* bsxa; 4184 xattr* xa; 4185 fcb* fcb; 4186 ccb* ccb; 4187 LIST_ENTRY* le; 4188 4189 static const char stream_pref[] = "user."; 4190 4191 TRACE("(%p, %p, %p, %u)\n", Vcb, FileObject, data, datalen); 4192 4193 if (!data || datalen < sizeof(btrfs_set_xattr)) 4194 return STATUS_INVALID_PARAMETER; 4195 4196 bsxa = (btrfs_set_xattr*)data; 4197 4198 if (datalen < offsetof(btrfs_set_xattr, data[0]) + bsxa->namelen + bsxa->valuelen) 4199 return STATUS_INVALID_PARAMETER; 4200 4201 if (bsxa->namelen + bsxa->valuelen + sizeof(tree_header) + sizeof(leaf_node) + offsetof(DIR_ITEM, name[0]) > Vcb->superblock.node_size) 4202 return STATUS_INVALID_PARAMETER; 4203 4204 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb) 4205 return STATUS_INVALID_PARAMETER; 4206 4207 if (Vcb->readonly) 4208 return STATUS_MEDIA_WRITE_PROTECTED; 4209 4210 fcb = FileObject->FsContext; 4211 ccb = FileObject->FsContext2; 4212 4213 if (is_subvol_readonly(fcb->subvol, Irp)) 4214 return STATUS_ACCESS_DENIED; 4215 4216 if (!(ccb->access & FILE_WRITE_ATTRIBUTES) && Irp->RequestorMode == UserMode) { 4217 WARN("insufficient privileges\n"); 4218 return STATUS_ACCESS_DENIED; 4219 } 4220 4221 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 4222 4223 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 4224 4225 if (bsxa->namelen == sizeof(EA_NTACL) - 1 && RtlCompareMemory(bsxa->data, EA_NTACL, sizeof(EA_NTACL) - 1) == sizeof(EA_NTACL) - 1) { 4226 if ((!(ccb->access & WRITE_DAC) || !(ccb->access & WRITE_OWNER)) && Irp->RequestorMode == UserMode) { 4227 WARN("insufficient privileges\n"); 4228 Status = STATUS_ACCESS_DENIED; 4229 goto end; 4230 } 4231 4232 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) { 4233 Status = STATUS_PRIVILEGE_NOT_HELD; 4234 goto end; 4235 } 4236 4237 if (fcb->sd) 4238 ExFreePool(fcb->sd); 4239 4240 if (bsxa->valuelen > 0 && RtlValidRelativeSecurityDescriptor(bsxa->data + bsxa->namelen, bsxa->valuelen, 0)) { 4241 fcb->sd = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG); 4242 if (!fcb->sd) { 4243 ERR("out of memory\n"); 4244 Status = STATUS_INSUFFICIENT_RESOURCES; 4245 goto end; 4246 } 4247 4248 RtlCopyMemory(fcb->sd, bsxa->data + bsxa->namelen, bsxa->valuelen); 4249 } else if (fcb->sd) 4250 fcb->sd = NULL; 4251 4252 fcb->sd_dirty = true; 4253 4254 if (!fcb->sd) { 4255 fcb_get_sd(fcb, ccb->fileref->parent->fcb, false, Irp); 4256 fcb->sd_deleted = true; 4257 } 4258 4259 mark_fcb_dirty(fcb); 4260 4261 Status = STATUS_SUCCESS; 4262 goto end; 4263 } else if (bsxa->namelen == sizeof(EA_DOSATTRIB) - 1 && RtlCompareMemory(bsxa->data, EA_DOSATTRIB, sizeof(EA_DOSATTRIB) - 1) == sizeof(EA_DOSATTRIB) - 1) { 4264 ULONG atts; 4265 4266 if (bsxa->valuelen > 0 && get_file_attributes_from_xattr(bsxa->data + bsxa->namelen, bsxa->valuelen, &atts)) { 4267 fcb->atts = atts; 4268 4269 if (fcb->type == BTRFS_TYPE_DIRECTORY) 4270 fcb->atts |= FILE_ATTRIBUTE_DIRECTORY; 4271 else if (fcb->type == BTRFS_TYPE_SYMLINK) 4272 fcb->atts |= FILE_ATTRIBUTE_REPARSE_POINT; 4273 4274 if (fcb->inode == SUBVOL_ROOT_INODE) { 4275 if (fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY) 4276 fcb->atts |= FILE_ATTRIBUTE_READONLY; 4277 else 4278 fcb->atts &= ~FILE_ATTRIBUTE_READONLY; 4279 } 4280 4281 fcb->atts_deleted = false; 4282 } else { 4283 bool hidden = ccb->fileref && ccb->fileref->dc && ccb->fileref->dc->utf8.Buffer && ccb->fileref->dc->utf8.Buffer[0] == '.'; 4284 4285 fcb->atts = get_file_attributes(Vcb, fcb->subvol, fcb->inode, fcb->type, hidden, true, Irp); 4286 fcb->atts_deleted = true; 4287 } 4288 4289 fcb->atts_changed = true; 4290 mark_fcb_dirty(fcb); 4291 4292 Status = STATUS_SUCCESS; 4293 goto end; 4294 } else if (bsxa->namelen == sizeof(EA_REPARSE) - 1 && RtlCompareMemory(bsxa->data, EA_REPARSE, sizeof(EA_REPARSE) - 1) == sizeof(EA_REPARSE) - 1) { 4295 if (fcb->reparse_xattr.Buffer) { 4296 ExFreePool(fcb->reparse_xattr.Buffer); 4297 fcb->reparse_xattr.Buffer = NULL; 4298 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = 0; 4299 } 4300 4301 if (bsxa->valuelen > 0) { 4302 fcb->reparse_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG); 4303 if (!fcb->reparse_xattr.Buffer) { 4304 ERR("out of memory\n"); 4305 Status = STATUS_INSUFFICIENT_RESOURCES; 4306 goto end; 4307 } 4308 4309 RtlCopyMemory(fcb->reparse_xattr.Buffer, bsxa->data + bsxa->namelen, bsxa->valuelen); 4310 fcb->reparse_xattr.Length = fcb->reparse_xattr.MaximumLength = bsxa->valuelen; 4311 } 4312 4313 fcb->reparse_xattr_changed = true; 4314 mark_fcb_dirty(fcb); 4315 4316 Status = STATUS_SUCCESS; 4317 goto end; 4318 } else if (bsxa->namelen == sizeof(EA_EA) - 1 && RtlCompareMemory(bsxa->data, EA_EA, sizeof(EA_EA) - 1) == sizeof(EA_EA) - 1) { 4319 if (!(ccb->access & FILE_WRITE_EA) && Irp->RequestorMode == UserMode) { 4320 WARN("insufficient privileges\n"); 4321 Status = STATUS_ACCESS_DENIED; 4322 goto end; 4323 } 4324 4325 if (fcb->ea_xattr.Buffer) { 4326 ExFreePool(fcb->ea_xattr.Buffer); 4327 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = 0; 4328 fcb->ea_xattr.Buffer = NULL; 4329 } 4330 4331 fcb->ealen = 0; 4332 4333 if (bsxa->valuelen > 0) { 4334 ULONG offset; 4335 4336 Status = IoCheckEaBufferValidity((FILE_FULL_EA_INFORMATION*)(bsxa->data + bsxa->namelen), bsxa->valuelen, &offset); 4337 4338 if (!NT_SUCCESS(Status)) 4339 WARN("IoCheckEaBufferValidity returned %08x (error at offset %u)\n", Status, offset); 4340 else { 4341 FILE_FULL_EA_INFORMATION* eainfo; 4342 4343 fcb->ea_xattr.Buffer = ExAllocatePoolWithTag(PagedPool, bsxa->valuelen, ALLOC_TAG); 4344 if (!fcb->ea_xattr.Buffer) { 4345 ERR("out of memory\n"); 4346 Status = STATUS_INSUFFICIENT_RESOURCES; 4347 goto end; 4348 } 4349 4350 RtlCopyMemory(fcb->ea_xattr.Buffer, bsxa->data + bsxa->namelen, bsxa->valuelen); 4351 4352 fcb->ea_xattr.Length = fcb->ea_xattr.MaximumLength = bsxa->valuelen; 4353 4354 fcb->ealen = 4; 4355 4356 // calculate ealen 4357 eainfo = (FILE_FULL_EA_INFORMATION*)(bsxa->data + bsxa->namelen); 4358 do { 4359 fcb->ealen += 5 + eainfo->EaNameLength + eainfo->EaValueLength; 4360 4361 if (eainfo->NextEntryOffset == 0) 4362 break; 4363 4364 eainfo = (FILE_FULL_EA_INFORMATION*)(((uint8_t*)eainfo) + eainfo->NextEntryOffset); 4365 } while (true); 4366 } 4367 } 4368 4369 fcb->ea_changed = true; 4370 mark_fcb_dirty(fcb); 4371 4372 Status = STATUS_SUCCESS; 4373 goto end; 4374 } else if (bsxa->namelen == sizeof(EA_CASE_SENSITIVE) - 1 && RtlCompareMemory(bsxa->data, EA_CASE_SENSITIVE, sizeof(EA_CASE_SENSITIVE) - 1) == sizeof(EA_CASE_SENSITIVE) - 1) { 4375 if (bsxa->valuelen > 0 && bsxa->data[bsxa->namelen] == '1') { 4376 fcb->case_sensitive = true; 4377 mark_fcb_dirty(fcb); 4378 } 4379 4380 Status = STATUS_SUCCESS; 4381 goto end; 4382 } else if (bsxa->namelen == sizeof(EA_PROP_COMPRESSION) - 1 && RtlCompareMemory(bsxa->data, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1) == sizeof(EA_PROP_COMPRESSION) - 1) { 4383 static const char lzo[] = "lzo"; 4384 static const char zlib[] = "zlib"; 4385 static const char zstd[] = "zstd"; 4386 4387 if (bsxa->valuelen == sizeof(zstd) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, zstd, bsxa->valuelen) == bsxa->valuelen) 4388 fcb->prop_compression = PropCompression_ZSTD; 4389 else if (bsxa->valuelen == sizeof(lzo) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, lzo, bsxa->valuelen) == bsxa->valuelen) 4390 fcb->prop_compression = PropCompression_LZO; 4391 else if (bsxa->valuelen == sizeof(zlib) - 1 && RtlCompareMemory(bsxa->data + bsxa->namelen, zlib, bsxa->valuelen) == bsxa->valuelen) 4392 fcb->prop_compression = PropCompression_Zlib; 4393 else 4394 fcb->prop_compression = PropCompression_None; 4395 4396 if (fcb->prop_compression != PropCompression_None) { 4397 fcb->inode_item.flags |= BTRFS_INODE_COMPRESS; 4398 fcb->inode_item_changed = true; 4399 } 4400 4401 fcb->prop_compression_changed = true; 4402 mark_fcb_dirty(fcb); 4403 4404 Status = STATUS_SUCCESS; 4405 goto end; 4406 } else if (bsxa->namelen >= (sizeof(stream_pref) - 1) && RtlCompareMemory(bsxa->data, stream_pref, sizeof(stream_pref) - 1) == sizeof(stream_pref) - 1) { 4407 // don't allow xattrs beginning with user., as these appear as streams instead 4408 Status = STATUS_OBJECT_NAME_INVALID; 4409 goto end; 4410 } 4411 4412 xa = ExAllocatePoolWithTag(PagedPool, offsetof(xattr, data[0]) + bsxa->namelen + bsxa->valuelen, ALLOC_TAG); 4413 if (!xa) { 4414 ERR("out of memory\n"); 4415 Status = STATUS_INSUFFICIENT_RESOURCES; 4416 goto end; 4417 } 4418 4419 le = fcb->xattrs.Flink; 4420 while (le != &fcb->xattrs) { 4421 xattr* xa2 = CONTAINING_RECORD(le, xattr, list_entry); 4422 4423 if (xa2->namelen == bsxa->namelen && RtlCompareMemory(xa2->data, bsxa->data, xa2->namelen) == xa2->namelen) { 4424 RemoveEntryList(&xa2->list_entry); 4425 ExFreePool(xa2); 4426 break; 4427 } 4428 4429 le = le->Flink; 4430 } 4431 4432 xa->namelen = bsxa->namelen; 4433 xa->valuelen = bsxa->valuelen; 4434 xa->dirty = true; 4435 RtlCopyMemory(xa->data, bsxa->data, bsxa->namelen + bsxa->valuelen); 4436 4437 InsertTailList(&fcb->xattrs, &xa->list_entry); 4438 4439 fcb->xattrs_changed = true; 4440 mark_fcb_dirty(fcb); 4441 4442 Status = STATUS_SUCCESS; 4443 4444 end: 4445 ExReleaseResourceLite(fcb->Header.Resource); 4446 4447 ExReleaseResourceLite(&Vcb->tree_lock); 4448 4449 return Status; 4450 } 4451 4452 static NTSTATUS reserve_subvol(device_extension* Vcb, PFILE_OBJECT FileObject, PIRP Irp) { 4453 fcb* fcb; 4454 ccb* ccb; 4455 4456 TRACE("(%p, %p)\n", Vcb, FileObject); 4457 4458 // "Reserving" a readonly subvol allows the calling process to write into it until the handle is closed. 4459 4460 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) 4461 return STATUS_PRIVILEGE_NOT_HELD; 4462 4463 if (!FileObject || !FileObject->FsContext || !FileObject->FsContext2 || FileObject->FsContext == Vcb->volume_fcb) 4464 return STATUS_INVALID_PARAMETER; 4465 4466 fcb = FileObject->FsContext; 4467 ccb = FileObject->FsContext2; 4468 4469 if (!(fcb->subvol->root_item.flags & BTRFS_SUBVOL_READONLY)) 4470 return STATUS_INVALID_PARAMETER; 4471 4472 if (fcb->subvol->reserved) 4473 return STATUS_INVALID_PARAMETER; 4474 4475 fcb->subvol->reserved = PsGetCurrentProcess(); 4476 ccb->reserving = true; 4477 4478 return STATUS_SUCCESS; 4479 } 4480 4481 static NTSTATUS get_subvol_path(device_extension* Vcb, uint64_t id, WCHAR* out, ULONG outlen, PIRP Irp) { 4482 LIST_ENTRY* le; 4483 root* r = NULL; 4484 NTSTATUS Status; 4485 file_ref* fr; 4486 UNICODE_STRING us; 4487 4488 le = Vcb->roots.Flink; 4489 while (le != &Vcb->roots) { 4490 root* r2 = CONTAINING_RECORD(le, root, list_entry); 4491 4492 if (r2->id == id) { 4493 r = r2; 4494 break; 4495 } 4496 4497 le = le->Flink; 4498 } 4499 4500 if (!r) { 4501 ERR("couldn't find subvol %I64x\n", id); 4502 return STATUS_INTERNAL_ERROR; 4503 } 4504 4505 ExAcquireResourceExclusiveLite(&Vcb->fileref_lock, true); 4506 4507 Status = open_fileref_by_inode(Vcb, r, r->root_item.objid, &fr, Irp); 4508 if (!NT_SUCCESS(Status)) { 4509 ExReleaseResourceLite(&Vcb->fileref_lock); 4510 ERR("open_fileref_by_inode returned %08x\n", Status); 4511 return Status; 4512 } 4513 4514 us.Buffer = out; 4515 us.Length = 0; 4516 us.MaximumLength = (USHORT)min(0xffff, outlen) - sizeof(WCHAR); 4517 4518 Status = fileref_get_filename(fr, &us, NULL, NULL); 4519 4520 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW) 4521 out[us.Length / sizeof(WCHAR)] = 0; 4522 else 4523 ERR("fileref_get_filename returned %08x\n", Status); 4524 4525 free_fileref(fr); 4526 4527 ExReleaseResourceLite(&Vcb->fileref_lock); 4528 4529 return Status; 4530 } 4531 4532 static NTSTATUS find_subvol(device_extension* Vcb, void* in, ULONG inlen, void* out, ULONG outlen, PIRP Irp) { 4533 btrfs_find_subvol* bfs; 4534 NTSTATUS Status; 4535 traverse_ptr tp; 4536 KEY searchkey; 4537 4538 if (!in || inlen < sizeof(btrfs_find_subvol)) 4539 return STATUS_INVALID_PARAMETER; 4540 4541 if (!out || outlen < sizeof(WCHAR)) 4542 return STATUS_INVALID_PARAMETER; 4543 4544 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) 4545 return STATUS_PRIVILEGE_NOT_HELD; 4546 4547 bfs = (btrfs_find_subvol*)in; 4548 4549 ExAcquireResourceSharedLite(&Vcb->tree_lock, true); 4550 4551 if (!Vcb->uuid_root) { 4552 ERR("couldn't find uuid root\n"); 4553 Status = STATUS_NOT_FOUND; 4554 goto end; 4555 } 4556 4557 RtlCopyMemory(&searchkey.obj_id, &bfs->uuid, sizeof(uint64_t)); 4558 searchkey.obj_type = TYPE_SUBVOL_UUID; 4559 RtlCopyMemory(&searchkey.offset, &bfs->uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t)); 4560 4561 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp); 4562 4563 if (!NT_SUCCESS(Status)) { 4564 ERR("find_item returned %08x\n", Status); 4565 goto end; 4566 } 4567 4568 if (!keycmp(searchkey, tp.item->key) && tp.item->size >= sizeof(uint64_t)) { 4569 uint64_t* id = (uint64_t*)tp.item->data; 4570 4571 if (bfs->ctransid != 0) { 4572 KEY searchkey2; 4573 traverse_ptr tp2; 4574 4575 searchkey2.obj_id = *id; 4576 searchkey2.obj_type = TYPE_ROOT_ITEM; 4577 searchkey2.offset = 0xffffffffffffffff; 4578 4579 Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey2, false, Irp); 4580 if (!NT_SUCCESS(Status)) { 4581 ERR("find_item returned %08x\n", Status); 4582 goto end; 4583 } 4584 4585 if (tp2.item->key.obj_id == searchkey2.obj_id && tp2.item->key.obj_type == searchkey2.obj_type && 4586 tp2.item->size >= offsetof(ROOT_ITEM, otransid)) { 4587 ROOT_ITEM* ri = (ROOT_ITEM*)tp2.item->data; 4588 4589 if (ri->ctransid == bfs->ctransid) { 4590 TRACE("found subvol %I64x\n", *id); 4591 Status = get_subvol_path(Vcb, *id, out, outlen, Irp); 4592 goto end; 4593 } 4594 } 4595 } else { 4596 TRACE("found subvol %I64x\n", *id); 4597 Status = get_subvol_path(Vcb, *id, out, outlen, Irp); 4598 goto end; 4599 } 4600 } 4601 4602 searchkey.obj_type = TYPE_SUBVOL_REC_UUID; 4603 4604 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp); 4605 4606 if (!NT_SUCCESS(Status)) { 4607 ERR("find_item returned %08x\n", Status); 4608 goto end; 4609 } 4610 4611 if (!keycmp(searchkey, tp.item->key) && tp.item->size >= sizeof(uint64_t)) { 4612 uint64_t* ids = (uint64_t*)tp.item->data; 4613 ULONG i; 4614 4615 for (i = 0; i < tp.item->size / sizeof(uint64_t); i++) { 4616 if (bfs->ctransid != 0) { 4617 KEY searchkey2; 4618 traverse_ptr tp2; 4619 4620 searchkey2.obj_id = ids[i]; 4621 searchkey2.obj_type = TYPE_ROOT_ITEM; 4622 searchkey2.offset = 0xffffffffffffffff; 4623 4624 Status = find_item(Vcb, Vcb->root_root, &tp2, &searchkey2, false, Irp); 4625 if (!NT_SUCCESS(Status)) { 4626 ERR("find_item returned %08x\n", Status); 4627 goto end; 4628 } 4629 4630 if (tp2.item->key.obj_id == searchkey2.obj_id && tp2.item->key.obj_type == searchkey2.obj_type && 4631 tp2.item->size >= offsetof(ROOT_ITEM, otransid)) { 4632 ROOT_ITEM* ri = (ROOT_ITEM*)tp2.item->data; 4633 4634 if (ri->ctransid == bfs->ctransid) { 4635 TRACE("found subvol %I64x\n", ids[i]); 4636 Status = get_subvol_path(Vcb, ids[i], out, outlen, Irp); 4637 goto end; 4638 } 4639 } 4640 } else { 4641 TRACE("found subvol %I64x\n", ids[i]); 4642 Status = get_subvol_path(Vcb, ids[i], out, outlen, Irp); 4643 goto end; 4644 } 4645 } 4646 } 4647 4648 Status = STATUS_NOT_FOUND; 4649 4650 end: 4651 ExReleaseResourceLite(&Vcb->tree_lock); 4652 4653 return Status; 4654 } 4655 4656 static NTSTATUS resize_device(device_extension* Vcb, void* data, ULONG len, PIRP Irp) { 4657 btrfs_resize* br = (btrfs_resize*)data; 4658 NTSTATUS Status; 4659 LIST_ENTRY* le; 4660 device* dev = NULL; 4661 4662 TRACE("(%p, %p, %u)\n", Vcb, data, len); 4663 4664 if (!data || len < sizeof(btrfs_resize) || (br->size % Vcb->superblock.sector_size) != 0) 4665 return STATUS_INVALID_PARAMETER; 4666 4667 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), Irp->RequestorMode)) 4668 return STATUS_PRIVILEGE_NOT_HELD; 4669 4670 if (Vcb->readonly) 4671 return STATUS_MEDIA_WRITE_PROTECTED; 4672 4673 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 4674 4675 le = Vcb->devices.Flink; 4676 while (le != &Vcb->devices) { 4677 device* dev2 = CONTAINING_RECORD(le, device, list_entry); 4678 4679 if (dev2->devitem.dev_id == br->device) { 4680 dev = dev2; 4681 break; 4682 } 4683 4684 le = le->Flink; 4685 } 4686 4687 if (!dev) { 4688 ERR("could not find device %I64x\n", br->device); 4689 Status = STATUS_INVALID_PARAMETER; 4690 goto end; 4691 } 4692 4693 if (!dev->devobj) { 4694 ERR("trying to resize missing device\n"); 4695 Status = STATUS_INVALID_PARAMETER; 4696 goto end; 4697 } 4698 4699 if (dev->readonly) { 4700 ERR("trying to resize readonly device\n"); 4701 Status = STATUS_INVALID_PARAMETER; 4702 goto end; 4703 } 4704 4705 if (br->size > 0 && dev->devitem.num_bytes == br->size) { 4706 TRACE("size unchanged, returning STATUS_SUCCESS\n"); 4707 Status = STATUS_SUCCESS; 4708 goto end; 4709 } 4710 4711 if (br->size > 0 && dev->devitem.num_bytes > br->size) { // shrink device 4712 bool need_balance = true; 4713 uint64_t old_size, delta; 4714 4715 le = dev->space.Flink; 4716 while (le != &dev->space) { 4717 space* s = CONTAINING_RECORD(le, space, list_entry); 4718 4719 if (s->address <= br->size && s->address + s->size >= dev->devitem.num_bytes) { 4720 need_balance = false; 4721 break; 4722 } 4723 4724 le = le->Flink; 4725 } 4726 4727 delta = dev->devitem.num_bytes - br->size; 4728 4729 if (need_balance) { 4730 int i; 4731 4732 if (Vcb->balance.thread) { 4733 WARN("balance already running\n"); 4734 Status = STATUS_DEVICE_NOT_READY; 4735 goto end; 4736 } 4737 4738 RtlZeroMemory(Vcb->balance.opts, sizeof(btrfs_balance_opts) * 3); 4739 4740 for (i = 0; i < 3; i++) { 4741 Vcb->balance.opts[i].flags = BTRFS_BALANCE_OPTS_ENABLED | BTRFS_BALANCE_OPTS_DEVID | BTRFS_BALANCE_OPTS_DRANGE; 4742 Vcb->balance.opts[i].devid = dev->devitem.dev_id; 4743 Vcb->balance.opts[i].drange_start = br->size; 4744 Vcb->balance.opts[i].drange_end = dev->devitem.num_bytes; 4745 } 4746 4747 Vcb->balance.paused = false; 4748 Vcb->balance.shrinking = true; 4749 Vcb->balance.status = STATUS_SUCCESS; 4750 KeInitializeEvent(&Vcb->balance.event, NotificationEvent, !Vcb->balance.paused); 4751 4752 space_list_subtract2(&dev->space, NULL, br->size, delta, NULL, NULL); 4753 4754 Status = PsCreateSystemThread(&Vcb->balance.thread, 0, NULL, NULL, NULL, balance_thread, Vcb); 4755 if (!NT_SUCCESS(Status)) { 4756 ERR("PsCreateSystemThread returned %08x\n", Status); 4757 goto end; 4758 } 4759 4760 Status = STATUS_MORE_PROCESSING_REQUIRED; 4761 4762 goto end; 4763 } 4764 4765 old_size = dev->devitem.num_bytes; 4766 dev->devitem.num_bytes = br->size; 4767 4768 Status = update_dev_item(Vcb, dev, Irp); 4769 if (!NT_SUCCESS(Status)) { 4770 ERR("update_dev_item returned %08x\n", Status); 4771 dev->devitem.num_bytes = old_size; 4772 goto end; 4773 } 4774 4775 space_list_subtract2(&dev->space, NULL, br->size, delta, NULL, NULL); 4776 4777 Vcb->superblock.total_bytes -= delta; 4778 } else { // extend device 4779 GET_LENGTH_INFORMATION gli; 4780 uint64_t old_size, delta; 4781 4782 Status = dev_ioctl(dev->devobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, 4783 &gli, sizeof(gli), true, NULL); 4784 if (!NT_SUCCESS(Status)) { 4785 ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status); 4786 goto end; 4787 } 4788 4789 if (br->size == 0) { 4790 br->size = gli.Length.QuadPart; 4791 4792 if (dev->devitem.num_bytes == br->size) { 4793 TRACE("size unchanged, returning STATUS_SUCCESS\n"); 4794 Status = STATUS_SUCCESS; 4795 goto end; 4796 } 4797 4798 if (br->size == 0) { 4799 ERR("IOCTL_DISK_GET_LENGTH_INFO returned 0 length\n"); 4800 Status = STATUS_INTERNAL_ERROR; 4801 goto end; 4802 } 4803 } else if ((uint64_t)gli.Length.QuadPart < br->size) { 4804 ERR("device was %I64x bytes, trying to extend to %I64x\n", gli.Length.QuadPart, br->size); 4805 Status = STATUS_INVALID_PARAMETER; 4806 goto end; 4807 } 4808 4809 delta = br->size - dev->devitem.num_bytes; 4810 4811 old_size = dev->devitem.num_bytes; 4812 dev->devitem.num_bytes = br->size; 4813 4814 Status = update_dev_item(Vcb, dev, Irp); 4815 if (!NT_SUCCESS(Status)) { 4816 ERR("update_dev_item returned %08x\n", Status); 4817 dev->devitem.num_bytes = old_size; 4818 goto end; 4819 } 4820 4821 space_list_add2(&dev->space, NULL, dev->devitem.num_bytes, delta, NULL, NULL); 4822 4823 Vcb->superblock.total_bytes += delta; 4824 } 4825 4826 Status = STATUS_SUCCESS; 4827 Vcb->need_write = true; 4828 4829 end: 4830 ExReleaseResourceLite(&Vcb->tree_lock); 4831 4832 if (NT_SUCCESS(Status)) 4833 FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_CHANGE_SIZE); 4834 4835 return Status; 4836 } 4837 4838 NTSTATUS fsctl_request(PDEVICE_OBJECT DeviceObject, PIRP* Pirp, uint32_t type) { 4839 PIRP Irp = *Pirp; 4840 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 4841 NTSTATUS Status; 4842 4843 switch (type) { 4844 #if (NTDDI_VERSION >= NTDDI_WIN7) 4845 case FSCTL_REQUEST_OPLOCK: 4846 WARN("STUB: FSCTL_REQUEST_OPLOCK\n"); 4847 Status = STATUS_INVALID_DEVICE_REQUEST; 4848 break; 4849 #endif 4850 4851 case FSCTL_REQUEST_OPLOCK_LEVEL_1: 4852 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_1\n"); 4853 Status = STATUS_INVALID_DEVICE_REQUEST; 4854 break; 4855 4856 case FSCTL_REQUEST_OPLOCK_LEVEL_2: 4857 WARN("STUB: FSCTL_REQUEST_OPLOCK_LEVEL_2\n"); 4858 Status = STATUS_INVALID_DEVICE_REQUEST; 4859 break; 4860 4861 case FSCTL_REQUEST_BATCH_OPLOCK: 4862 WARN("STUB: FSCTL_REQUEST_BATCH_OPLOCK\n"); 4863 Status = STATUS_INVALID_DEVICE_REQUEST; 4864 break; 4865 4866 case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: 4867 WARN("STUB: FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n"); 4868 Status = STATUS_INVALID_DEVICE_REQUEST; 4869 break; 4870 4871 case FSCTL_OPLOCK_BREAK_ACK_NO_2: 4872 WARN("STUB: FSCTL_OPLOCK_BREAK_ACK_NO_2\n"); 4873 Status = STATUS_INVALID_DEVICE_REQUEST; 4874 break; 4875 4876 case FSCTL_OPBATCH_ACK_CLOSE_PENDING: 4877 WARN("STUB: FSCTL_OPBATCH_ACK_CLOSE_PENDING\n"); 4878 Status = STATUS_INVALID_DEVICE_REQUEST; 4879 break; 4880 4881 case FSCTL_OPLOCK_BREAK_NOTIFY: 4882 WARN("STUB: FSCTL_OPLOCK_BREAK_NOTIFY\n"); 4883 Status = STATUS_INVALID_DEVICE_REQUEST; 4884 break; 4885 4886 case FSCTL_REQUEST_FILTER_OPLOCK: 4887 WARN("STUB: FSCTL_REQUEST_FILTER_OPLOCK\n"); 4888 Status = STATUS_INVALID_DEVICE_REQUEST; 4889 break; 4890 4891 case FSCTL_LOCK_VOLUME: 4892 Status = lock_volume(DeviceObject->DeviceExtension, Irp); 4893 break; 4894 4895 case FSCTL_UNLOCK_VOLUME: 4896 Status = unlock_volume(DeviceObject->DeviceExtension, Irp); 4897 break; 4898 4899 case FSCTL_DISMOUNT_VOLUME: 4900 Status = dismount_volume(DeviceObject->DeviceExtension, Irp); 4901 break; 4902 4903 case FSCTL_IS_VOLUME_MOUNTED: 4904 Status = is_volume_mounted(DeviceObject->DeviceExtension, Irp); 4905 break; 4906 4907 case FSCTL_IS_PATHNAME_VALID: 4908 WARN("STUB: FSCTL_IS_PATHNAME_VALID\n"); 4909 Status = STATUS_INVALID_DEVICE_REQUEST; 4910 break; 4911 4912 case FSCTL_MARK_VOLUME_DIRTY: 4913 WARN("STUB: FSCTL_MARK_VOLUME_DIRTY\n"); 4914 Status = STATUS_INVALID_DEVICE_REQUEST; 4915 break; 4916 4917 case FSCTL_QUERY_RETRIEVAL_POINTERS: 4918 WARN("STUB: FSCTL_QUERY_RETRIEVAL_POINTERS\n"); 4919 Status = STATUS_INVALID_DEVICE_REQUEST; 4920 break; 4921 4922 case FSCTL_GET_COMPRESSION: 4923 Status = get_compression(Irp); 4924 break; 4925 4926 case FSCTL_SET_COMPRESSION: 4927 Status = set_compression(Irp); 4928 break; 4929 4930 case FSCTL_SET_BOOTLOADER_ACCESSED: 4931 WARN("STUB: FSCTL_SET_BOOTLOADER_ACCESSED\n"); 4932 Status = STATUS_INVALID_DEVICE_REQUEST; 4933 break; 4934 4935 case FSCTL_INVALIDATE_VOLUMES: 4936 Status = invalidate_volumes(Irp); 4937 break; 4938 4939 case FSCTL_QUERY_FAT_BPB: 4940 WARN("STUB: FSCTL_QUERY_FAT_BPB\n"); 4941 Status = STATUS_INVALID_DEVICE_REQUEST; 4942 break; 4943 4944 case FSCTL_FILESYSTEM_GET_STATISTICS: 4945 Status = fs_get_statistics(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); 4946 break; 4947 4948 case FSCTL_GET_NTFS_VOLUME_DATA: 4949 WARN("STUB: FSCTL_GET_NTFS_VOLUME_DATA\n"); 4950 Status = STATUS_INVALID_DEVICE_REQUEST; 4951 break; 4952 4953 case FSCTL_GET_NTFS_FILE_RECORD: 4954 WARN("STUB: FSCTL_GET_NTFS_FILE_RECORD\n"); 4955 Status = STATUS_INVALID_DEVICE_REQUEST; 4956 break; 4957 4958 case FSCTL_GET_VOLUME_BITMAP: 4959 WARN("STUB: FSCTL_GET_VOLUME_BITMAP\n"); 4960 Status = STATUS_INVALID_DEVICE_REQUEST; 4961 break; 4962 4963 case FSCTL_GET_RETRIEVAL_POINTERS: 4964 WARN("STUB: FSCTL_GET_RETRIEVAL_POINTERS\n"); 4965 Status = STATUS_INVALID_DEVICE_REQUEST; 4966 break; 4967 4968 case FSCTL_MOVE_FILE: 4969 WARN("STUB: FSCTL_MOVE_FILE\n"); 4970 Status = STATUS_INVALID_DEVICE_REQUEST; 4971 break; 4972 4973 case FSCTL_IS_VOLUME_DIRTY: 4974 Status = is_volume_dirty(DeviceObject->DeviceExtension, Irp); 4975 break; 4976 4977 case FSCTL_ALLOW_EXTENDED_DASD_IO: 4978 Status = allow_extended_dasd_io(DeviceObject->DeviceExtension, IrpSp->FileObject); 4979 break; 4980 4981 case FSCTL_FIND_FILES_BY_SID: 4982 WARN("STUB: FSCTL_FIND_FILES_BY_SID\n"); 4983 Status = STATUS_INVALID_DEVICE_REQUEST; 4984 break; 4985 4986 case FSCTL_SET_OBJECT_ID: 4987 WARN("STUB: FSCTL_SET_OBJECT_ID\n"); 4988 Status = STATUS_INVALID_DEVICE_REQUEST; 4989 break; 4990 4991 case FSCTL_GET_OBJECT_ID: 4992 Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer, 4993 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); 4994 break; 4995 4996 case FSCTL_DELETE_OBJECT_ID: 4997 WARN("STUB: FSCTL_DELETE_OBJECT_ID\n"); 4998 Status = STATUS_INVALID_DEVICE_REQUEST; 4999 break; 5000 5001 case FSCTL_SET_REPARSE_POINT: 5002 Status = set_reparse_point(DeviceObject, Irp); 5003 break; 5004 5005 case FSCTL_GET_REPARSE_POINT: 5006 Status = get_reparse_point(DeviceObject, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, 5007 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); 5008 break; 5009 5010 case FSCTL_DELETE_REPARSE_POINT: 5011 Status = delete_reparse_point(DeviceObject, Irp); 5012 break; 5013 5014 case FSCTL_ENUM_USN_DATA: 5015 WARN("STUB: FSCTL_ENUM_USN_DATA\n"); 5016 Status = STATUS_INVALID_DEVICE_REQUEST; 5017 break; 5018 5019 case FSCTL_SECURITY_ID_CHECK: 5020 WARN("STUB: FSCTL_SECURITY_ID_CHECK\n"); 5021 Status = STATUS_INVALID_DEVICE_REQUEST; 5022 break; 5023 5024 case FSCTL_READ_USN_JOURNAL: 5025 WARN("STUB: FSCTL_READ_USN_JOURNAL\n"); 5026 Status = STATUS_INVALID_DEVICE_REQUEST; 5027 break; 5028 5029 case FSCTL_SET_OBJECT_ID_EXTENDED: 5030 WARN("STUB: FSCTL_SET_OBJECT_ID_EXTENDED\n"); 5031 Status = STATUS_INVALID_DEVICE_REQUEST; 5032 break; 5033 5034 case FSCTL_CREATE_OR_GET_OBJECT_ID: 5035 Status = get_object_id(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer, 5036 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); 5037 break; 5038 5039 case FSCTL_SET_SPARSE: 5040 Status = set_sparse(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, 5041 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5042 break; 5043 5044 case FSCTL_SET_ZERO_DATA: 5045 Status = set_zero_data(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, 5046 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5047 break; 5048 5049 case FSCTL_QUERY_ALLOCATED_RANGES: 5050 Status = query_ranges(IrpSp->FileObject, IrpSp->Parameters.FileSystemControl.Type3InputBuffer, 5051 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->UserBuffer, 5052 IrpSp->Parameters.FileSystemControl.OutputBufferLength, &Irp->IoStatus.Information); 5053 break; 5054 5055 case FSCTL_ENABLE_UPGRADE: 5056 WARN("STUB: FSCTL_ENABLE_UPGRADE\n"); 5057 Status = STATUS_INVALID_DEVICE_REQUEST; 5058 break; 5059 5060 case FSCTL_SET_ENCRYPTION: 5061 WARN("STUB: FSCTL_SET_ENCRYPTION\n"); 5062 Status = STATUS_INVALID_DEVICE_REQUEST; 5063 break; 5064 5065 case FSCTL_ENCRYPTION_FSCTL_IO: 5066 WARN("STUB: FSCTL_ENCRYPTION_FSCTL_IO\n"); 5067 Status = STATUS_INVALID_DEVICE_REQUEST; 5068 break; 5069 5070 case FSCTL_WRITE_RAW_ENCRYPTED: 5071 WARN("STUB: FSCTL_WRITE_RAW_ENCRYPTED\n"); 5072 Status = STATUS_INVALID_DEVICE_REQUEST; 5073 break; 5074 5075 case FSCTL_READ_RAW_ENCRYPTED: 5076 WARN("STUB: FSCTL_READ_RAW_ENCRYPTED\n"); 5077 Status = STATUS_INVALID_DEVICE_REQUEST; 5078 break; 5079 5080 case FSCTL_CREATE_USN_JOURNAL: 5081 WARN("STUB: FSCTL_CREATE_USN_JOURNAL\n"); 5082 Status = STATUS_INVALID_DEVICE_REQUEST; 5083 break; 5084 5085 case FSCTL_READ_FILE_USN_DATA: 5086 WARN("STUB: FSCTL_READ_FILE_USN_DATA\n"); 5087 Status = STATUS_INVALID_DEVICE_REQUEST; 5088 break; 5089 5090 case FSCTL_WRITE_USN_CLOSE_RECORD: 5091 WARN("STUB: FSCTL_WRITE_USN_CLOSE_RECORD\n"); 5092 Status = STATUS_INVALID_DEVICE_REQUEST; 5093 break; 5094 5095 case FSCTL_EXTEND_VOLUME: 5096 WARN("STUB: FSCTL_EXTEND_VOLUME\n"); 5097 Status = STATUS_INVALID_DEVICE_REQUEST; 5098 break; 5099 5100 case FSCTL_QUERY_USN_JOURNAL: 5101 WARN("STUB: FSCTL_QUERY_USN_JOURNAL\n"); 5102 Status = STATUS_INVALID_DEVICE_REQUEST; 5103 break; 5104 5105 case FSCTL_DELETE_USN_JOURNAL: 5106 WARN("STUB: FSCTL_DELETE_USN_JOURNAL\n"); 5107 Status = STATUS_INVALID_DEVICE_REQUEST; 5108 break; 5109 5110 case FSCTL_MARK_HANDLE: 5111 WARN("STUB: FSCTL_MARK_HANDLE\n"); 5112 Status = STATUS_INVALID_DEVICE_REQUEST; 5113 break; 5114 5115 case FSCTL_SIS_COPYFILE: 5116 WARN("STUB: FSCTL_SIS_COPYFILE\n"); 5117 Status = STATUS_INVALID_DEVICE_REQUEST; 5118 break; 5119 5120 case FSCTL_SIS_LINK_FILES: 5121 WARN("STUB: FSCTL_SIS_LINK_FILES\n"); 5122 Status = STATUS_INVALID_DEVICE_REQUEST; 5123 break; 5124 5125 case FSCTL_RECALL_FILE: 5126 WARN("STUB: FSCTL_RECALL_FILE\n"); 5127 Status = STATUS_INVALID_DEVICE_REQUEST; 5128 break; 5129 5130 case FSCTL_READ_FROM_PLEX: 5131 WARN("STUB: FSCTL_READ_FROM_PLEX\n"); 5132 Status = STATUS_INVALID_DEVICE_REQUEST; 5133 break; 5134 5135 case FSCTL_FILE_PREFETCH: 5136 WARN("STUB: FSCTL_FILE_PREFETCH\n"); 5137 Status = STATUS_INVALID_DEVICE_REQUEST; 5138 break; 5139 5140 #if _WIN32_WINNT >= 0x0600 5141 case FSCTL_MAKE_MEDIA_COMPATIBLE: 5142 WARN("STUB: FSCTL_MAKE_MEDIA_COMPATIBLE\n"); 5143 Status = STATUS_INVALID_DEVICE_REQUEST; 5144 break; 5145 5146 case FSCTL_SET_DEFECT_MANAGEMENT: 5147 WARN("STUB: FSCTL_SET_DEFECT_MANAGEMENT\n"); 5148 Status = STATUS_INVALID_DEVICE_REQUEST; 5149 break; 5150 5151 case FSCTL_QUERY_SPARING_INFO: 5152 WARN("STUB: FSCTL_QUERY_SPARING_INFO\n"); 5153 Status = STATUS_INVALID_DEVICE_REQUEST; 5154 break; 5155 5156 case FSCTL_QUERY_ON_DISK_VOLUME_INFO: 5157 WARN("STUB: FSCTL_QUERY_ON_DISK_VOLUME_INFO\n"); 5158 Status = STATUS_INVALID_DEVICE_REQUEST; 5159 break; 5160 5161 case FSCTL_SET_VOLUME_COMPRESSION_STATE: 5162 WARN("STUB: FSCTL_SET_VOLUME_COMPRESSION_STATE\n"); 5163 Status = STATUS_INVALID_DEVICE_REQUEST; 5164 break; 5165 5166 case FSCTL_TXFS_MODIFY_RM: 5167 WARN("STUB: FSCTL_TXFS_MODIFY_RM\n"); 5168 Status = STATUS_INVALID_DEVICE_REQUEST; 5169 break; 5170 5171 case FSCTL_TXFS_QUERY_RM_INFORMATION: 5172 WARN("STUB: FSCTL_TXFS_QUERY_RM_INFORMATION\n"); 5173 Status = STATUS_INVALID_DEVICE_REQUEST; 5174 break; 5175 5176 case FSCTL_TXFS_ROLLFORWARD_REDO: 5177 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_REDO\n"); 5178 Status = STATUS_INVALID_DEVICE_REQUEST; 5179 break; 5180 5181 case FSCTL_TXFS_ROLLFORWARD_UNDO: 5182 WARN("STUB: FSCTL_TXFS_ROLLFORWARD_UNDO\n"); 5183 Status = STATUS_INVALID_DEVICE_REQUEST; 5184 break; 5185 5186 case FSCTL_TXFS_START_RM: 5187 WARN("STUB: FSCTL_TXFS_START_RM\n"); 5188 Status = STATUS_INVALID_DEVICE_REQUEST; 5189 break; 5190 5191 case FSCTL_TXFS_SHUTDOWN_RM: 5192 WARN("STUB: FSCTL_TXFS_SHUTDOWN_RM\n"); 5193 Status = STATUS_INVALID_DEVICE_REQUEST; 5194 break; 5195 5196 case FSCTL_TXFS_READ_BACKUP_INFORMATION: 5197 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION\n"); 5198 Status = STATUS_INVALID_DEVICE_REQUEST; 5199 break; 5200 5201 case FSCTL_TXFS_WRITE_BACKUP_INFORMATION: 5202 WARN("STUB: FSCTL_TXFS_WRITE_BACKUP_INFORMATION\n"); 5203 Status = STATUS_INVALID_DEVICE_REQUEST; 5204 break; 5205 5206 case FSCTL_TXFS_CREATE_SECONDARY_RM: 5207 WARN("STUB: FSCTL_TXFS_CREATE_SECONDARY_RM\n"); 5208 Status = STATUS_INVALID_DEVICE_REQUEST; 5209 break; 5210 5211 case FSCTL_TXFS_GET_METADATA_INFO: 5212 WARN("STUB: FSCTL_TXFS_GET_METADATA_INFO\n"); 5213 Status = STATUS_INVALID_DEVICE_REQUEST; 5214 break; 5215 5216 case FSCTL_TXFS_GET_TRANSACTED_VERSION: 5217 WARN("STUB: FSCTL_TXFS_GET_TRANSACTED_VERSION\n"); 5218 Status = STATUS_INVALID_DEVICE_REQUEST; 5219 break; 5220 5221 case FSCTL_TXFS_SAVEPOINT_INFORMATION: 5222 WARN("STUB: FSCTL_TXFS_SAVEPOINT_INFORMATION\n"); 5223 Status = STATUS_INVALID_DEVICE_REQUEST; 5224 break; 5225 5226 case FSCTL_TXFS_CREATE_MINIVERSION: 5227 WARN("STUB: FSCTL_TXFS_CREATE_MINIVERSION\n"); 5228 Status = STATUS_INVALID_DEVICE_REQUEST; 5229 break; 5230 5231 case FSCTL_TXFS_TRANSACTION_ACTIVE: 5232 WARN("STUB: FSCTL_TXFS_TRANSACTION_ACTIVE\n"); 5233 Status = STATUS_INVALID_DEVICE_REQUEST; 5234 break; 5235 5236 case FSCTL_SET_ZERO_ON_DEALLOCATION: 5237 WARN("STUB: FSCTL_SET_ZERO_ON_DEALLOCATION\n"); 5238 Status = STATUS_INVALID_DEVICE_REQUEST; 5239 break; 5240 5241 case FSCTL_SET_REPAIR: 5242 WARN("STUB: FSCTL_SET_REPAIR\n"); 5243 Status = STATUS_INVALID_DEVICE_REQUEST; 5244 break; 5245 5246 case FSCTL_GET_REPAIR: 5247 WARN("STUB: FSCTL_GET_REPAIR\n"); 5248 Status = STATUS_INVALID_DEVICE_REQUEST; 5249 break; 5250 5251 case FSCTL_WAIT_FOR_REPAIR: 5252 WARN("STUB: FSCTL_WAIT_FOR_REPAIR\n"); 5253 Status = STATUS_INVALID_DEVICE_REQUEST; 5254 break; 5255 5256 case FSCTL_INITIATE_REPAIR: 5257 WARN("STUB: FSCTL_INITIATE_REPAIR\n"); 5258 Status = STATUS_INVALID_DEVICE_REQUEST; 5259 break; 5260 5261 case FSCTL_CSC_INTERNAL: 5262 WARN("STUB: FSCTL_CSC_INTERNAL\n"); 5263 Status = STATUS_INVALID_DEVICE_REQUEST; 5264 break; 5265 5266 case FSCTL_SHRINK_VOLUME: 5267 WARN("STUB: FSCTL_SHRINK_VOLUME\n"); 5268 Status = STATUS_INVALID_DEVICE_REQUEST; 5269 break; 5270 5271 case FSCTL_SET_SHORT_NAME_BEHAVIOR: 5272 WARN("STUB: FSCTL_SET_SHORT_NAME_BEHAVIOR\n"); 5273 Status = STATUS_INVALID_DEVICE_REQUEST; 5274 break; 5275 5276 case FSCTL_DFSR_SET_GHOST_HANDLE_STATE: 5277 WARN("STUB: FSCTL_DFSR_SET_GHOST_HANDLE_STATE\n"); 5278 Status = STATUS_INVALID_DEVICE_REQUEST; 5279 break; 5280 5281 case FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES: 5282 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTION_LOCKED_FILES\n"); 5283 Status = STATUS_INVALID_DEVICE_REQUEST; 5284 break; 5285 5286 case FSCTL_TXFS_LIST_TRANSACTIONS: 5287 WARN("STUB: FSCTL_TXFS_LIST_TRANSACTIONS\n"); 5288 Status = STATUS_INVALID_DEVICE_REQUEST; 5289 break; 5290 5291 case FSCTL_QUERY_PAGEFILE_ENCRYPTION: 5292 WARN("STUB: FSCTL_QUERY_PAGEFILE_ENCRYPTION\n"); 5293 Status = STATUS_INVALID_DEVICE_REQUEST; 5294 break; 5295 5296 case FSCTL_RESET_VOLUME_ALLOCATION_HINTS: 5297 WARN("STUB: FSCTL_RESET_VOLUME_ALLOCATION_HINTS\n"); 5298 Status = STATUS_INVALID_DEVICE_REQUEST; 5299 break; 5300 5301 case FSCTL_TXFS_READ_BACKUP_INFORMATION2: 5302 WARN("STUB: FSCTL_TXFS_READ_BACKUP_INFORMATION2\n"); 5303 Status = STATUS_INVALID_DEVICE_REQUEST; 5304 break; 5305 5306 case FSCTL_CSV_CONTROL: 5307 WARN("STUB: FSCTL_CSV_CONTROL\n"); 5308 Status = STATUS_INVALID_DEVICE_REQUEST; 5309 break; 5310 #endif 5311 // TRACE rather than WARN because Windows 10 spams this undocumented fsctl 5312 case FSCTL_QUERY_VOLUME_CONTAINER_STATE: 5313 TRACE("STUB: FSCTL_QUERY_VOLUME_CONTAINER_STATE\n"); 5314 Status = STATUS_INVALID_DEVICE_REQUEST; 5315 break; 5316 5317 case FSCTL_GET_INTEGRITY_INFORMATION: 5318 Status = get_integrity_information(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), 5319 IrpSp->Parameters.FileSystemControl.OutputBufferLength); 5320 break; 5321 5322 case FSCTL_SET_INTEGRITY_INFORMATION: 5323 Status = set_integrity_information(IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength); 5324 break; 5325 5326 case FSCTL_DUPLICATE_EXTENTS_TO_FILE: 5327 Status = duplicate_extents(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, 5328 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5329 break; 5330 5331 case FSCTL_BTRFS_GET_FILE_IDS: 5332 Status = get_file_ids(IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength); 5333 break; 5334 5335 case FSCTL_BTRFS_CREATE_SUBVOL: 5336 Status = create_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5337 break; 5338 5339 case FSCTL_BTRFS_CREATE_SNAPSHOT: 5340 Status = create_snapshot(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5341 break; 5342 5343 case FSCTL_BTRFS_GET_INODE_INFO: 5344 Status = get_inode_info(IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength); 5345 break; 5346 5347 case FSCTL_BTRFS_SET_INODE_INFO: 5348 Status = set_inode_info(IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5349 break; 5350 5351 case FSCTL_BTRFS_GET_DEVICES: 5352 Status = get_devices(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength); 5353 break; 5354 5355 case FSCTL_BTRFS_GET_USAGE: 5356 Status = get_usage(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp); 5357 break; 5358 5359 case FSCTL_BTRFS_START_BALANCE: 5360 Status = start_balance(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode); 5361 break; 5362 5363 case FSCTL_BTRFS_QUERY_BALANCE: 5364 Status = query_balance(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength); 5365 break; 5366 5367 case FSCTL_BTRFS_PAUSE_BALANCE: 5368 Status = pause_balance(DeviceObject->DeviceExtension, Irp->RequestorMode); 5369 break; 5370 5371 case FSCTL_BTRFS_RESUME_BALANCE: 5372 Status = resume_balance(DeviceObject->DeviceExtension, Irp->RequestorMode); 5373 break; 5374 5375 case FSCTL_BTRFS_STOP_BALANCE: 5376 Status = stop_balance(DeviceObject->DeviceExtension, Irp->RequestorMode); 5377 break; 5378 5379 case FSCTL_BTRFS_ADD_DEVICE: 5380 Status = add_device(DeviceObject->DeviceExtension, Irp, Irp->RequestorMode); 5381 break; 5382 5383 case FSCTL_BTRFS_REMOVE_DEVICE: 5384 Status = remove_device(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode); 5385 break; 5386 5387 case FSCTL_BTRFS_GET_UUID: 5388 Status = query_uuid(DeviceObject->DeviceExtension, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength); 5389 break; 5390 5391 case FSCTL_BTRFS_START_SCRUB: 5392 Status = start_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode); 5393 break; 5394 5395 case FSCTL_BTRFS_QUERY_SCRUB: 5396 Status = query_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength); 5397 break; 5398 5399 case FSCTL_BTRFS_PAUSE_SCRUB: 5400 Status = pause_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode); 5401 break; 5402 5403 case FSCTL_BTRFS_RESUME_SCRUB: 5404 Status = resume_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode); 5405 break; 5406 5407 case FSCTL_BTRFS_STOP_SCRUB: 5408 Status = stop_scrub(DeviceObject->DeviceExtension, Irp->RequestorMode); 5409 break; 5410 5411 case FSCTL_BTRFS_RESET_STATS: 5412 Status = reset_stats(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode); 5413 break; 5414 5415 case FSCTL_BTRFS_MKNOD: 5416 Status = mknod(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5417 break; 5418 5419 case FSCTL_BTRFS_RECEIVED_SUBVOL: 5420 Status = recvd_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, 5421 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode); 5422 break; 5423 5424 case FSCTL_BTRFS_GET_XATTRS: 5425 Status = fsctl_get_xattrs(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->UserBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp->RequestorMode); 5426 break; 5427 5428 case FSCTL_BTRFS_SET_XATTR: 5429 Status = fsctl_set_xattr(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp->AssociatedIrp.SystemBuffer, 5430 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5431 break; 5432 5433 case FSCTL_BTRFS_RESERVE_SUBVOL: 5434 Status = reserve_subvol(DeviceObject->DeviceExtension, IrpSp->FileObject, Irp); 5435 break; 5436 5437 case FSCTL_BTRFS_FIND_SUBVOL: 5438 Status = find_subvol(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, 5439 Irp->UserBuffer, IrpSp->Parameters.FileSystemControl.OutputBufferLength, Irp); 5440 break; 5441 5442 case FSCTL_BTRFS_SEND_SUBVOL: 5443 Status = send_subvol(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, 5444 IrpSp->FileObject, Irp); 5445 break; 5446 5447 case FSCTL_BTRFS_READ_SEND_BUFFER: 5448 Status = read_send_buffer(DeviceObject->DeviceExtension, IrpSp->FileObject, map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength, 5449 &Irp->IoStatus.Information, Irp->RequestorMode); 5450 break; 5451 5452 case FSCTL_BTRFS_RESIZE: 5453 Status = resize_device(DeviceObject->DeviceExtension, Irp->AssociatedIrp.SystemBuffer, 5454 IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp); 5455 break; 5456 5457 default: 5458 WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n", 5459 IrpSp->Parameters.FileSystemControl.FsControlCode, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xff0000) >> 16, 5460 (IrpSp->Parameters.FileSystemControl.FsControlCode & 0xc000) >> 14, (IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3ffc) >> 2, 5461 IrpSp->Parameters.FileSystemControl.FsControlCode & 0x3); 5462 Status = STATUS_INVALID_DEVICE_REQUEST; 5463 break; 5464 } 5465 5466 return Status; 5467 } 5468