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