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