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