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 <stdlib.h> 19 #include <stddef.h> 20 #include <time.h> 21 #include <ntstatus.h> 22 #define WIN32_NO_STATUS 23 #include <windef.h> 24 #include <winbase.h> 25 #ifndef __REACTOS__ 26 #include <winternl.h> 27 #include <devioctl.h> 28 #include <ntdddisk.h> 29 #else 30 #include <ndk/iofuncs.h> 31 #include <ndk/obfuncs.h> 32 #include <ndk/rtlfuncs.h> 33 #endif 34 #include <ntddscsi.h> 35 #include <ntddstor.h> 36 #include <ata.h> 37 #include <mountmgr.h> 38 #ifdef __REACTOS__ 39 #include <winnls.h> 40 #include <stdbool.h> 41 #include "btrfs.h" 42 #include "btrfsioctl.h" 43 #include "crc32c.h" 44 #include "xxhash.h" 45 #else 46 #include <stringapiset.h> 47 #include <stdbool.h> 48 #include "../btrfs.h" 49 #include "../btrfsioctl.h" 50 #include "../crc32c.h" 51 #include "../xxhash.h" 52 53 #if defined(_X86_) || defined(_AMD64_) 54 #ifndef _MSC_VER 55 #include <cpuid.h> 56 #else 57 #include <intrin.h> 58 #endif 59 #endif 60 #endif // __REACTOS__ 61 62 #ifdef __REACTOS__ 63 #define malloc(size) RtlAllocateHeap(RtlGetProcessHeap(), 0, (size)) 64 #define free(ptr) RtlFreeHeap(RtlGetProcessHeap(), 0, (ptr)) 65 #endif 66 67 #define SHA256_HASH_SIZE 32 68 void calc_sha256(uint8_t* hash, const void* input, size_t len); 69 70 #define BLAKE2_HASH_SIZE 32 71 void blake2b(void *out, size_t outlen, const void* in, size_t inlen); 72 73 #ifndef __REACTOS__ 74 #define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) 75 #define FSCTL_UNLOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 7, METHOD_BUFFERED, FILE_ANY_ACCESS) 76 #define FSCTL_DISMOUNT_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8, METHOD_BUFFERED, FILE_ANY_ACCESS) 77 78 #ifndef _MSC_VER // not in mingw yet 79 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000 80 #endif 81 82 #ifdef __cplusplus 83 extern "C" { 84 #endif 85 NTSTATUS NTAPI NtWriteFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, 86 ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key); 87 88 NTSTATUS NTAPI NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, 89 ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key); 90 #ifdef __cplusplus 91 } 92 #endif 93 #endif // __REACTOS__ 94 95 // These are undocumented, and what comes from format.exe 96 typedef struct { 97 void* table; 98 void* unk1; 99 WCHAR* string; 100 } DSTRING; 101 102 typedef struct { 103 void* table; 104 } STREAM_MESSAGE; 105 106 #define FORMAT_FLAG_QUICK_FORMAT 0x00000001 107 #define FORMAT_FLAG_UNKNOWN1 0x00000002 108 #define FORMAT_FLAG_DISMOUNT_FIRST 0x00000004 109 #define FORMAT_FLAG_UNKNOWN2 0x00000040 110 #define FORMAT_FLAG_LARGE_RECORDS 0x00000100 111 #define FORMAT_FLAG_INTEGRITY_DISABLE 0x00000100 112 113 typedef struct { 114 uint16_t unk1; 115 uint16_t unk2; 116 uint32_t flags; 117 DSTRING* label; 118 } options; 119 120 #ifndef __REACTOS__ 121 FORCEINLINE VOID InitializeListHead(PLIST_ENTRY ListHead) { 122 ListHead->Flink = ListHead->Blink = ListHead; 123 } 124 125 FORCEINLINE VOID InsertTailList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry) { 126 PLIST_ENTRY Blink; 127 128 Blink = ListHead->Blink; 129 Entry->Flink = ListHead; 130 Entry->Blink = Blink; 131 Blink->Flink = Entry; 132 ListHead->Blink = Entry; 133 } 134 #endif 135 136 #ifdef __REACTOS__ 137 ULONG NTAPI NtGetTickCount(VOID); 138 #endif 139 140 typedef struct { 141 KEY key; 142 uint16_t size; 143 void* data; 144 LIST_ENTRY list_entry; 145 } btrfs_item; 146 147 typedef struct { 148 uint64_t offset; 149 CHUNK_ITEM* chunk_item; 150 uint64_t lastoff; 151 uint64_t used; 152 LIST_ENTRY list_entry; 153 } btrfs_chunk; 154 155 typedef struct { 156 uint64_t id; 157 tree_header header; 158 btrfs_chunk* c; 159 LIST_ENTRY items; 160 LIST_ENTRY list_entry; 161 } btrfs_root; 162 163 typedef struct { 164 DEV_ITEM dev_item; 165 uint64_t last_alloc; 166 } btrfs_dev; 167 168 #define keycmp(key1, key2)\ 169 ((key1.obj_id < key2.obj_id) ? -1 :\ 170 ((key1.obj_id > key2.obj_id) ? 1 :\ 171 ((key1.obj_type < key2.obj_type) ? -1 :\ 172 ((key1.obj_type > key2.obj_type) ? 1 :\ 173 ((key1.offset < key2.offset) ? -1 :\ 174 ((key1.offset > key2.offset) ? 1 :\ 175 0)))))) 176 177 HMODULE module; 178 ULONG def_sector_size = 0, def_node_size = 0; 179 uint64_t def_incompat_flags = BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA; 180 uint16_t def_csum_type = CSUM_TYPE_CRC32C; 181 182 #ifndef __REACTOS__ 183 // the following definitions come from fmifs.h in ReactOS 184 185 typedef struct { 186 ULONG Lines; 187 PCHAR Output; 188 } TEXTOUTPUT, *PTEXTOUTPUT; 189 190 typedef enum { 191 FMIFS_UNKNOWN0, 192 FMIFS_UNKNOWN1, 193 FMIFS_UNKNOWN2, 194 FMIFS_UNKNOWN3, 195 FMIFS_UNKNOWN4, 196 FMIFS_UNKNOWN5, 197 FMIFS_UNKNOWN6, 198 FMIFS_UNKNOWN7, 199 FMIFS_FLOPPY, 200 FMIFS_UNKNOWN9, 201 FMIFS_UNKNOWN10, 202 FMIFS_REMOVABLE, 203 FMIFS_HARDDISK, 204 FMIFS_UNKNOWN13, 205 FMIFS_UNKNOWN14, 206 FMIFS_UNKNOWN15, 207 FMIFS_UNKNOWN16, 208 FMIFS_UNKNOWN17, 209 FMIFS_UNKNOWN18, 210 FMIFS_UNKNOWN19, 211 FMIFS_UNKNOWN20, 212 FMIFS_UNKNOWN21, 213 FMIFS_UNKNOWN22, 214 FMIFS_UNKNOWN23, 215 } FMIFS_MEDIA_FLAG; 216 217 typedef enum { 218 PROGRESS, 219 DONEWITHSTRUCTURE, 220 UNKNOWN2, 221 UNKNOWN3, 222 UNKNOWN4, 223 UNKNOWN5, 224 INSUFFICIENTRIGHTS, 225 FSNOTSUPPORTED, 226 VOLUMEINUSE, 227 UNKNOWN9, 228 UNKNOWNA, 229 DONE, 230 UNKNOWNC, 231 UNKNOWND, 232 OUTPUT, 233 STRUCTUREPROGRESS, 234 CLUSTERSIZETOOSMALL, 235 } CALLBACKCOMMAND; 236 237 typedef BOOLEAN (NTAPI* PFMIFSCALLBACK)(CALLBACKCOMMAND Command, ULONG SubAction, PVOID ActionInfo); 238 239 #else 240 241 #include <fmifs/fmifs.h> 242 243 #endif // __REACTOS__ 244 245 #ifndef __REACTOS__ 246 NTSTATUS WINAPI ChkdskEx(PUNICODE_STRING DriveRoot, BOOLEAN FixErrors, BOOLEAN Verbose, BOOLEAN CheckOnlyIfDirty, 247 BOOLEAN ScanDrive, PFMIFSCALLBACK Callback) { 248 #else 249 BOOLEAN 250 NTAPI 251 BtrfsChkdsk( 252 IN PUNICODE_STRING DriveRoot, 253 IN PFMIFSCALLBACK Callback, 254 IN BOOLEAN FixErrors, 255 IN BOOLEAN Verbose, 256 IN BOOLEAN CheckOnlyIfDirty, 257 IN BOOLEAN ScanDrive, 258 IN PVOID pUnknown1, 259 IN PVOID pUnknown2, 260 IN PVOID pUnknown3, 261 IN PVOID pUnknown4, 262 IN PULONG ExitStatus) 263 { 264 #endif 265 // STUB 266 267 if (Callback) { 268 TEXTOUTPUT TextOut; 269 270 TextOut.Lines = 1; 271 TextOut.Output = "stub, not implemented"; 272 273 Callback(OUTPUT, 0, &TextOut); 274 } 275 276 #ifndef __REACTOS__ 277 return STATUS_SUCCESS; 278 #else 279 *ExitStatus = (ULONG)STATUS_SUCCESS; 280 return TRUE; 281 #endif 282 } 283 284 static btrfs_root* add_root(LIST_ENTRY* roots, uint64_t id) { 285 btrfs_root* root; 286 287 root = malloc(sizeof(btrfs_root)); 288 289 root->id = id; 290 RtlZeroMemory(&root->header, sizeof(tree_header)); 291 InitializeListHead(&root->items); 292 InsertTailList(roots, &root->list_entry); 293 294 return root; 295 } 296 297 static void free_roots(LIST_ENTRY* roots) { 298 LIST_ENTRY* le; 299 300 le = roots->Flink; 301 while (le != roots) { 302 LIST_ENTRY *le2 = le->Flink, *le3; 303 btrfs_root* r = CONTAINING_RECORD(le, btrfs_root, list_entry); 304 305 le3 = r->items.Flink; 306 while (le3 != &r->items) { 307 LIST_ENTRY* le4 = le3->Flink; 308 btrfs_item* item = CONTAINING_RECORD(le3, btrfs_item, list_entry); 309 310 if (item->data) 311 free(item->data); 312 313 free(item); 314 315 le3 = le4; 316 } 317 318 free(r); 319 320 le = le2; 321 } 322 } 323 324 static void free_chunks(LIST_ENTRY* chunks) { 325 LIST_ENTRY* le; 326 327 le = chunks->Flink; 328 while (le != chunks) { 329 LIST_ENTRY *le2 = le->Flink; 330 btrfs_chunk* c = CONTAINING_RECORD(le, btrfs_chunk, list_entry); 331 332 free(c->chunk_item); 333 free(c); 334 335 le = le2; 336 } 337 } 338 339 static void add_item(btrfs_root* r, uint64_t obj_id, uint8_t obj_type, uint64_t offset, void* data, uint16_t size) { 340 LIST_ENTRY* le; 341 btrfs_item* item; 342 343 item = malloc(sizeof(btrfs_item)); 344 345 item->key.obj_id = obj_id; 346 item->key.obj_type = obj_type; 347 item->key.offset = offset; 348 item->size = size; 349 350 if (size == 0) 351 item->data = NULL; 352 else { 353 item->data = malloc(size); 354 memcpy(item->data, data, size); 355 } 356 357 le = r->items.Flink; 358 while (le != &r->items) { 359 btrfs_item* i2 = CONTAINING_RECORD(le, btrfs_item, list_entry); 360 361 if (keycmp(item->key, i2->key) != 1) { 362 InsertTailList(le, &item->list_entry); 363 return; 364 } 365 366 le = le->Flink; 367 } 368 369 InsertTailList(&r->items, &item->list_entry); 370 } 371 372 static uint64_t find_chunk_offset(uint64_t size, uint64_t offset, btrfs_dev* dev, btrfs_root* dev_root, BTRFS_UUID* chunkuuid) { 373 uint64_t off; 374 DEV_EXTENT de; 375 376 off = dev->last_alloc; 377 dev->last_alloc += size; 378 379 dev->dev_item.bytes_used += size; 380 381 de.chunktree = BTRFS_ROOT_CHUNK; 382 de.objid = 0x100; 383 de.address = offset; 384 de.length = size; 385 de.chunktree_uuid = *chunkuuid; 386 387 add_item(dev_root, dev->dev_item.dev_id, TYPE_DEV_EXTENT, off, &de, sizeof(DEV_EXTENT)); 388 389 return off; 390 } 391 392 static btrfs_chunk* add_chunk(LIST_ENTRY* chunks, uint64_t flags, btrfs_root* chunk_root, btrfs_dev* dev, btrfs_root* dev_root, BTRFS_UUID* chunkuuid, uint32_t sector_size) { 393 uint64_t off, size; 394 uint16_t stripes, i; 395 btrfs_chunk* c; 396 LIST_ENTRY* le; 397 CHUNK_ITEM_STRIPE* cis; 398 399 off = 0xc00000; 400 le = chunks->Flink; 401 while (le != chunks) { 402 c = CONTAINING_RECORD(le, btrfs_chunk, list_entry); 403 404 if (c->offset + c->chunk_item->size > off) 405 off = c->offset + c->chunk_item->size; 406 407 le = le->Flink; 408 } 409 410 if (flags & BLOCK_FLAG_METADATA) { 411 if (dev->dev_item.num_bytes > 0xC80000000) // 50 GB 412 size = 0x40000000; // 1 GB 413 else 414 size = 0x10000000; // 256 MB 415 } else if (flags & BLOCK_FLAG_SYSTEM) 416 size = 0x800000; 417 418 size = min(size, dev->dev_item.num_bytes / 10); // cap at 10% 419 size &= ~(sector_size - 1); 420 421 stripes = flags & BLOCK_FLAG_DUPLICATE ? 2 : 1; 422 423 if (dev->dev_item.num_bytes - dev->dev_item.bytes_used < stripes * size) // not enough space 424 return NULL; 425 426 c = malloc(sizeof(btrfs_chunk)); 427 c->offset = off; 428 c->lastoff = off; 429 c->used = 0; 430 431 c->chunk_item = malloc(sizeof(CHUNK_ITEM) + (stripes * sizeof(CHUNK_ITEM_STRIPE))); 432 433 c->chunk_item->size = size; 434 c->chunk_item->root_id = BTRFS_ROOT_EXTENT; 435 c->chunk_item->stripe_length = max(sector_size, 0x10000); 436 c->chunk_item->type = flags; 437 c->chunk_item->opt_io_alignment = max(sector_size, 0x10000); 438 c->chunk_item->opt_io_width = max(sector_size, 0x10000); 439 c->chunk_item->sector_size = sector_size; 440 c->chunk_item->num_stripes = stripes; 441 c->chunk_item->sub_stripes = 0; 442 443 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 444 445 for (i = 0; i < stripes; i++) { 446 cis[i].dev_id = dev->dev_item.dev_id; 447 cis[i].offset = find_chunk_offset(size, c->offset, dev, dev_root, chunkuuid); 448 cis[i].dev_uuid = dev->dev_item.device_uuid; 449 } 450 451 add_item(chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, c->chunk_item, sizeof(CHUNK_ITEM) + (stripes * sizeof(CHUNK_ITEM_STRIPE))); 452 453 InsertTailList(chunks, &c->list_entry); 454 455 return c; 456 } 457 458 static bool superblock_collision(btrfs_chunk* c, uint64_t address) { 459 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 460 uint64_t stripe = (address - c->offset) / c->chunk_item->stripe_length; 461 uint16_t i, j; 462 463 for (i = 0; i < c->chunk_item->num_stripes; i++) { 464 j = 0; 465 while (superblock_addrs[j] != 0) { 466 if (superblock_addrs[j] >= cis[i].offset) { 467 uint64_t stripe2 = (superblock_addrs[j] - cis[i].offset) / c->chunk_item->stripe_length; 468 469 if (stripe2 == stripe) 470 return true; 471 } 472 j++; 473 } 474 } 475 476 return false; 477 } 478 479 static uint64_t get_next_address(btrfs_chunk* c) { 480 uint64_t addr; 481 482 addr = c->lastoff; 483 484 while (superblock_collision(c, addr)) { 485 addr = addr - ((addr - c->offset) % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 486 487 if (addr >= c->offset + c->chunk_item->size) // chunk has been exhausted 488 return 0; 489 } 490 491 return addr; 492 } 493 494 typedef struct { 495 EXTENT_ITEM ei; 496 uint8_t type; 497 TREE_BLOCK_REF tbr; 498 } EXTENT_ITEM_METADATA; 499 500 typedef struct { 501 EXTENT_ITEM ei; 502 EXTENT_ITEM2 ei2; 503 uint8_t type; 504 TREE_BLOCK_REF tbr; 505 } EXTENT_ITEM_METADATA2; 506 507 static void assign_addresses(LIST_ENTRY* roots, btrfs_chunk* sys_chunk, btrfs_chunk* metadata_chunk, uint32_t node_size, 508 btrfs_root* root_root, btrfs_root* extent_root, bool skinny) { 509 LIST_ENTRY* le; 510 511 le = roots->Flink; 512 while (le != roots) { 513 btrfs_root* r = CONTAINING_RECORD(le, btrfs_root, list_entry); 514 btrfs_chunk* c = r->id == BTRFS_ROOT_CHUNK ? sys_chunk : metadata_chunk; 515 516 r->header.address = get_next_address(c); 517 r->c = c; 518 c->lastoff = r->header.address + node_size; 519 c->used += node_size; 520 521 if (skinny) { 522 EXTENT_ITEM_METADATA eim; 523 524 eim.ei.refcount = 1; 525 eim.ei.generation = 1; 526 eim.ei.flags = EXTENT_ITEM_TREE_BLOCK; 527 eim.type = TYPE_TREE_BLOCK_REF; 528 eim.tbr.offset = r->id; 529 530 add_item(extent_root, r->header.address, TYPE_METADATA_ITEM, 0, &eim, sizeof(EXTENT_ITEM_METADATA)); 531 } else { 532 EXTENT_ITEM_METADATA2 eim2; 533 KEY firstitem; 534 535 if (r->items.Flink == &r->items) { 536 firstitem.obj_id = 0; 537 firstitem.obj_type = 0; 538 firstitem.offset = 0; 539 } else { 540 btrfs_item* bi = CONTAINING_RECORD(r->items.Flink, btrfs_item, list_entry); 541 542 firstitem = bi->key; 543 } 544 545 eim2.ei.refcount = 1; 546 eim2.ei.generation = 1; 547 eim2.ei.flags = EXTENT_ITEM_TREE_BLOCK; 548 eim2.ei2.firstitem = firstitem; 549 eim2.ei2.level = 0; 550 eim2.type = TYPE_TREE_BLOCK_REF; 551 eim2.tbr.offset = r->id; 552 553 add_item(extent_root, r->header.address, TYPE_EXTENT_ITEM, node_size, &eim2, sizeof(EXTENT_ITEM_METADATA2)); 554 } 555 556 if (r->id != BTRFS_ROOT_ROOT && r->id != BTRFS_ROOT_CHUNK) { 557 ROOT_ITEM ri; 558 559 memset(&ri, 0, sizeof(ROOT_ITEM)); 560 561 ri.inode.generation = 1; 562 ri.inode.st_size = 3; 563 ri.inode.st_blocks = node_size; 564 ri.inode.st_nlink = 1; 565 ri.inode.st_mode = 040755; 566 ri.generation = 1; 567 ri.objid = r->id == 5 || r->id >= 0x100 ? SUBVOL_ROOT_INODE : 0; 568 ri.block_number = r->header.address; 569 ri.bytes_used = node_size; 570 ri.num_references = 1; 571 ri.generation2 = ri.generation; 572 573 add_item(root_root, r->id, TYPE_ROOT_ITEM, 0, &ri, sizeof(ROOT_ITEM)); 574 } 575 576 le = le->Flink; 577 } 578 } 579 580 static NTSTATUS write_data(HANDLE h, uint64_t address, btrfs_chunk* c, void* data, ULONG size) { 581 NTSTATUS Status; 582 uint16_t i; 583 IO_STATUS_BLOCK iosb; 584 LARGE_INTEGER off; 585 CHUNK_ITEM_STRIPE* cis; 586 587 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 588 589 for (i = 0; i < c->chunk_item->num_stripes; i++) { 590 off.QuadPart = cis[i].offset + address - c->offset; 591 592 Status = NtWriteFile(h, NULL, NULL, NULL, &iosb, data, size, &off, NULL); 593 if (!NT_SUCCESS(Status)) 594 return Status; 595 } 596 597 return STATUS_SUCCESS; 598 } 599 600 static void calc_tree_checksum(tree_header* th, uint32_t node_size) { 601 switch (def_csum_type) { 602 case CSUM_TYPE_CRC32C: 603 *(uint32_t*)th = ~calc_crc32c(0xffffffff, (uint8_t*)&th->fs_uuid, node_size - sizeof(th->csum)); 604 break; 605 606 case CSUM_TYPE_XXHASH: 607 *(uint64_t*)th = XXH64((uint8_t*)&th->fs_uuid, node_size - sizeof(th->csum), 0); 608 break; 609 610 case CSUM_TYPE_SHA256: 611 calc_sha256((uint8_t*)th, &th->fs_uuid, node_size - sizeof(th->csum)); 612 break; 613 614 case CSUM_TYPE_BLAKE2: 615 blake2b((uint8_t*)th, BLAKE2_HASH_SIZE, &th->fs_uuid, node_size - sizeof(th->csum)); 616 break; 617 } 618 } 619 620 static NTSTATUS write_roots(HANDLE h, LIST_ENTRY* roots, uint32_t node_size, BTRFS_UUID* fsuuid, BTRFS_UUID* chunkuuid) { 621 LIST_ENTRY *le, *le2; 622 NTSTATUS Status; 623 uint8_t* tree; 624 625 tree = malloc(node_size); 626 627 le = roots->Flink; 628 while (le != roots) { 629 btrfs_root* r = CONTAINING_RECORD(le, btrfs_root, list_entry); 630 uint8_t* dp; 631 leaf_node* ln; 632 633 memset(tree, 0, node_size); 634 635 r->header.num_items = 0; 636 r->header.fs_uuid = *fsuuid; 637 r->header.flags = HEADER_FLAG_MIXED_BACKREF | HEADER_FLAG_WRITTEN; 638 r->header.chunk_tree_uuid = *chunkuuid; 639 r->header.generation = 1; 640 r->header.tree_id = r->id; 641 642 ln = (leaf_node*)(tree + sizeof(tree_header)); 643 644 dp = tree + node_size; 645 646 le2 = r->items.Flink; 647 while (le2 != &r->items) { 648 btrfs_item* item = CONTAINING_RECORD(le2, btrfs_item, list_entry); 649 650 ln->key = item->key; 651 ln->size = item->size; 652 653 if (item->size > 0) { 654 dp -= item->size; 655 memcpy(dp, item->data, item->size); 656 657 ln->offset = (uint32_t)(dp - tree - sizeof(tree_header)); 658 } else 659 ln->offset = 0; 660 661 ln = &ln[1]; 662 663 r->header.num_items++; 664 665 le2 = le2->Flink; 666 } 667 668 memcpy(tree, &r->header, sizeof(tree_header)); 669 670 calc_tree_checksum((tree_header*)tree, node_size); 671 672 Status = write_data(h, r->header.address, r->c, tree, node_size); 673 if (!NT_SUCCESS(Status)) { 674 free(tree); 675 return Status; 676 } 677 678 le = le->Flink; 679 } 680 681 free(tree); 682 683 return STATUS_SUCCESS; 684 } 685 686 #ifndef __REACTOS__ 687 static void get_uuid(BTRFS_UUID* uuid) { 688 #else 689 static void get_uuid(BTRFS_UUID* uuid, ULONG* seed) { 690 #endif 691 uint8_t i; 692 693 for (i = 0; i < 16; i+=2) { 694 #ifndef __REACTOS__ 695 ULONG r = rand(); 696 #else 697 ULONG r = RtlRandom(seed); 698 #endif 699 700 uuid->uuid[i] = (r & 0xff00) >> 8; 701 uuid->uuid[i+1] = r & 0xff; 702 } 703 } 704 705 #ifndef __REACTOS__ 706 static void init_device(btrfs_dev* dev, uint64_t id, uint64_t size, BTRFS_UUID* fsuuid, uint32_t sector_size) { 707 #else 708 static void init_device(btrfs_dev* dev, uint64_t id, uint64_t size, BTRFS_UUID* fsuuid, uint32_t sector_size, ULONG* seed) { 709 #endif 710 dev->dev_item.dev_id = id; 711 dev->dev_item.num_bytes = size; 712 dev->dev_item.bytes_used = 0; 713 dev->dev_item.optimal_io_align = sector_size; 714 dev->dev_item.optimal_io_width = sector_size; 715 dev->dev_item.minimal_io_size = sector_size; 716 dev->dev_item.type = 0; 717 dev->dev_item.generation = 0; 718 dev->dev_item.start_offset = 0; 719 dev->dev_item.dev_group = 0; 720 dev->dev_item.seek_speed = 0; 721 dev->dev_item.bandwidth = 0; 722 #ifndef __REACTOS__ 723 get_uuid(&dev->dev_item.device_uuid); 724 #else 725 get_uuid(&dev->dev_item.device_uuid, seed); 726 #endif 727 dev->dev_item.fs_uuid = *fsuuid; 728 729 dev->last_alloc = 0x100000; // skip first megabyte 730 } 731 732 static void calc_superblock_checksum(superblock* sb) { 733 switch (def_csum_type) { 734 case CSUM_TYPE_CRC32C: 735 *(uint32_t*)sb = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum)); 736 break; 737 738 case CSUM_TYPE_XXHASH: 739 *(uint64_t*)sb = XXH64(&sb->uuid, sizeof(superblock) - sizeof(sb->checksum), 0); 740 break; 741 742 case CSUM_TYPE_SHA256: 743 calc_sha256((uint8_t*)sb, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum)); 744 break; 745 746 case CSUM_TYPE_BLAKE2: 747 blake2b((uint8_t*)sb, BLAKE2_HASH_SIZE, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum)); 748 break; 749 } 750 } 751 752 static NTSTATUS write_superblocks(HANDLE h, btrfs_dev* dev, btrfs_root* chunk_root, btrfs_root* root_root, btrfs_root* extent_root, 753 btrfs_chunk* sys_chunk, uint32_t node_size, BTRFS_UUID* fsuuid, uint32_t sector_size, PUNICODE_STRING label, uint64_t incompat_flags) { 754 NTSTATUS Status; 755 IO_STATUS_BLOCK iosb; 756 ULONG sblen; 757 int i; 758 superblock* sb; 759 KEY* key; 760 uint64_t bytes_used; 761 LIST_ENTRY* le; 762 763 sblen = sizeof(*sb); 764 if (sblen & (sector_size - 1)) 765 sblen = (sblen & sector_size) + sector_size; 766 767 bytes_used = 0; 768 769 le = extent_root->items.Flink; 770 while (le != &extent_root->items) { 771 btrfs_item* item = CONTAINING_RECORD(le, btrfs_item, list_entry); 772 773 if (item->key.obj_type == TYPE_EXTENT_ITEM) 774 bytes_used += item->key.offset; 775 else if (item->key.obj_type == TYPE_METADATA_ITEM) 776 bytes_used += node_size; 777 778 le = le->Flink; 779 } 780 781 sb = malloc(sblen); 782 memset(sb, 0, sblen); 783 784 sb->uuid = *fsuuid; 785 sb->flags = 1; 786 sb->magic = BTRFS_MAGIC; 787 sb->generation = 1; 788 sb->root_tree_addr = root_root->header.address; 789 sb->chunk_tree_addr = chunk_root->header.address; 790 sb->total_bytes = dev->dev_item.num_bytes; 791 sb->bytes_used = bytes_used; 792 sb->root_dir_objectid = BTRFS_ROOT_TREEDIR; 793 sb->num_devices = 1; 794 sb->sector_size = sector_size; 795 sb->node_size = node_size; 796 sb->leaf_size = node_size; 797 sb->stripe_size = sector_size; 798 sb->n = sizeof(KEY) + sizeof(CHUNK_ITEM) + (sys_chunk->chunk_item->num_stripes * sizeof(CHUNK_ITEM_STRIPE)); 799 sb->chunk_root_generation = 1; 800 sb->incompat_flags = incompat_flags; 801 sb->csum_type = def_csum_type; 802 memcpy(&sb->dev_item, &dev->dev_item, sizeof(DEV_ITEM)); 803 804 if (label->Length > 0) { 805 #ifdef __REACTOS__ 806 ANSI_STRING as; 807 unsigned int i; 808 809 for (i = 0; i < label->Length / sizeof(WCHAR); i++) { 810 #else 811 ULONG utf8len; 812 813 for (unsigned int i = 0; i < label->Length / sizeof(WCHAR); i++) { 814 #endif 815 if (label->Buffer[i] == '/' || label->Buffer[i] == '\\') { 816 free(sb); 817 return STATUS_INVALID_VOLUME_LABEL; 818 } 819 } 820 821 #ifndef __REACTOS__ 822 utf8len = WideCharToMultiByte(CP_UTF8, 0, label->Buffer, label->Length / sizeof(WCHAR), NULL, 0, NULL, NULL); 823 824 if (utf8len == 0 || utf8len > MAX_LABEL_SIZE) { 825 free(sb); 826 return STATUS_INVALID_VOLUME_LABEL; 827 } 828 829 if (WideCharToMultiByte(CP_UTF8, 0, label->Buffer, label->Length / sizeof(WCHAR), sb->label, utf8len, NULL, NULL) == 0) { 830 #else 831 as.Buffer = sb->label; 832 as.Length = 0; 833 as.MaximumLength = MAX_LABEL_SIZE; 834 835 if (!NT_SUCCESS(RtlUnicodeStringToAnsiString(&as, label, FALSE))) 836 { 837 #endif 838 free(sb); 839 return STATUS_INVALID_VOLUME_LABEL; 840 } 841 } 842 sb->cache_generation = 0xffffffffffffffff; 843 844 key = (KEY*)sb->sys_chunk_array; 845 key->obj_id = 0x100; 846 key->obj_type = TYPE_CHUNK_ITEM; 847 key->offset = sys_chunk->offset; 848 memcpy(&key[1], sys_chunk->chunk_item, sizeof(CHUNK_ITEM) + (sys_chunk->chunk_item->num_stripes * sizeof(CHUNK_ITEM_STRIPE))); 849 850 i = 0; 851 while (superblock_addrs[i] != 0) { 852 LARGE_INTEGER off; 853 854 if (superblock_addrs[i] > dev->dev_item.num_bytes) 855 break; 856 857 sb->sb_phys_addr = superblock_addrs[i]; 858 859 calc_superblock_checksum(sb); 860 861 off.QuadPart = superblock_addrs[i]; 862 863 Status = NtWriteFile(h, NULL, NULL, NULL, &iosb, sb, sblen, &off, NULL); 864 if (!NT_SUCCESS(Status)) { 865 free(sb); 866 return Status; 867 } 868 869 i++; 870 } 871 872 free(sb); 873 874 return STATUS_SUCCESS; 875 } 876 877 static __inline void win_time_to_unix(LARGE_INTEGER t, BTRFS_TIME* out) { 878 ULONGLONG l = t.QuadPart - 116444736000000000; 879 880 out->seconds = l / 10000000; 881 out->nanoseconds = (l % 10000000) * 100; 882 } 883 884 #ifdef __REACTOS__ 885 VOID 886 WINAPI 887 GetSystemTimeAsFileTime(OUT PFILETIME lpFileTime) 888 { 889 LARGE_INTEGER SystemTime; 890 891 do 892 { 893 SystemTime.HighPart = SharedUserData->SystemTime.High1Time; 894 SystemTime.LowPart = SharedUserData->SystemTime.LowPart; 895 } 896 while (SystemTime.HighPart != SharedUserData->SystemTime.High2Time); 897 898 lpFileTime->dwLowDateTime = SystemTime.LowPart; 899 lpFileTime->dwHighDateTime = SystemTime.HighPart; 900 } 901 #endif 902 903 static void add_inode_ref(btrfs_root* r, uint64_t inode, uint64_t parent, uint64_t index, const char* name) { 904 uint16_t name_len = (uint16_t)strlen(name); 905 INODE_REF* ir = malloc(offsetof(INODE_REF, name[0]) + name_len); 906 907 ir->index = 0; 908 ir->n = name_len; 909 memcpy(ir->name, name, name_len); 910 911 add_item(r, inode, TYPE_INODE_REF, parent, ir, (uint16_t)offsetof(INODE_REF, name[0]) + ir->n); 912 913 free(ir); 914 } 915 916 static void init_fs_tree(btrfs_root* r, uint32_t node_size) { 917 INODE_ITEM ii; 918 FILETIME filetime; 919 LARGE_INTEGER time; 920 921 memset(&ii, 0, sizeof(INODE_ITEM)); 922 923 ii.generation = 1; 924 ii.st_blocks = node_size; 925 ii.st_nlink = 1; 926 ii.st_mode = 040755; 927 928 GetSystemTimeAsFileTime(&filetime); 929 time.LowPart = filetime.dwLowDateTime; 930 time.HighPart = filetime.dwHighDateTime; 931 932 win_time_to_unix(time, &ii.st_atime); 933 ii.st_ctime = ii.st_mtime = ii.st_atime; 934 935 add_item(r, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, &ii, sizeof(INODE_ITEM)); 936 937 add_inode_ref(r, SUBVOL_ROOT_INODE, SUBVOL_ROOT_INODE, 0, ".."); 938 } 939 940 static void add_block_group_items(LIST_ENTRY* chunks, btrfs_root* extent_root) { 941 LIST_ENTRY* le; 942 943 le = chunks->Flink; 944 while (le != chunks) { 945 btrfs_chunk* c = CONTAINING_RECORD(le, btrfs_chunk, list_entry); 946 BLOCK_GROUP_ITEM bgi; 947 948 bgi.used = c->used; 949 bgi.chunk_tree = 0x100; 950 bgi.flags = c->chunk_item->type; 951 add_item(extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, &bgi, sizeof(BLOCK_GROUP_ITEM)); 952 953 le = le->Flink; 954 } 955 } 956 957 static NTSTATUS clear_first_megabyte(HANDLE h) { 958 NTSTATUS Status; 959 IO_STATUS_BLOCK iosb; 960 LARGE_INTEGER zero; 961 uint8_t* mb; 962 963 mb = malloc(0x100000); 964 memset(mb, 0, 0x100000); 965 966 zero.QuadPart = 0; 967 968 Status = NtWriteFile(h, NULL, NULL, NULL, &iosb, mb, 0x100000, &zero, NULL); 969 970 free(mb); 971 972 return Status; 973 } 974 975 static bool is_ssd(HANDLE h) { 976 ULONG aptelen; 977 ATA_PASS_THROUGH_EX* apte; 978 IO_STATUS_BLOCK iosb; 979 NTSTATUS Status; 980 IDENTIFY_DEVICE_DATA* idd; 981 982 aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512; 983 apte = malloc(aptelen); 984 985 RtlZeroMemory(apte, aptelen); 986 987 apte->Length = sizeof(ATA_PASS_THROUGH_EX); 988 apte->AtaFlags = ATA_FLAGS_DATA_IN; 989 apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX); 990 apte->TimeOutValue = 3; 991 apte->DataBufferOffset = apte->Length; 992 apte->CurrentTaskFile[6] = IDE_COMMAND_IDENTIFY; 993 994 Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_ATA_PASS_THROUGH, apte, aptelen, apte, aptelen); 995 996 if (NT_SUCCESS(Status)) { 997 idd = (IDENTIFY_DEVICE_DATA*)((uint8_t*)apte + sizeof(ATA_PASS_THROUGH_EX)); 998 999 if (idd->NominalMediaRotationRate == 1) { 1000 free(apte); 1001 return true; 1002 } 1003 } 1004 1005 free(apte); 1006 1007 return false; 1008 } 1009 1010 static void add_dir_item(btrfs_root* root, uint64_t inode, uint32_t hash, uint64_t key_objid, uint8_t key_type, 1011 uint64_t key_offset, uint64_t transid, uint8_t type, const char* name) { 1012 uint16_t name_len = (uint16_t)strlen(name); 1013 DIR_ITEM* di = malloc(offsetof(DIR_ITEM, name[0]) + name_len); 1014 1015 di->key.obj_id = key_objid; 1016 di->key.obj_type = key_type; 1017 di->key.offset = key_offset; 1018 di->transid = transid; 1019 di->m = 0; 1020 di->n = name_len; 1021 di->type = type; 1022 memcpy(di->name, name, name_len); 1023 1024 add_item(root, inode, TYPE_DIR_ITEM, hash, di, (uint16_t)(offsetof(DIR_ITEM, name[0]) + di->m + di->n)); 1025 1026 free(di); 1027 } 1028 1029 static void set_default_subvol(btrfs_root* root_root, uint32_t node_size) { 1030 INODE_ITEM ii; 1031 FILETIME filetime; 1032 LARGE_INTEGER time; 1033 1034 static const char default_subvol[] = "default"; 1035 static const uint32_t default_hash = 0x8dbfc2d2; 1036 1037 add_inode_ref(root_root, BTRFS_ROOT_FSTREE, BTRFS_ROOT_TREEDIR, 0, default_subvol); 1038 1039 memset(&ii, 0, sizeof(INODE_ITEM)); 1040 1041 ii.generation = 1; 1042 ii.st_blocks = node_size; 1043 ii.st_nlink = 1; 1044 ii.st_mode = 040755; 1045 1046 GetSystemTimeAsFileTime(&filetime); 1047 time.LowPart = filetime.dwLowDateTime; 1048 time.HighPart = filetime.dwHighDateTime; 1049 1050 win_time_to_unix(time, &ii.st_atime); 1051 ii.st_ctime = ii.st_mtime = ii.otime = ii.st_atime; 1052 1053 add_item(root_root, BTRFS_ROOT_TREEDIR, TYPE_INODE_ITEM, 0, &ii, sizeof(INODE_ITEM)); 1054 1055 add_inode_ref(root_root, BTRFS_ROOT_TREEDIR, BTRFS_ROOT_TREEDIR, 0, ".."); 1056 1057 add_dir_item(root_root, BTRFS_ROOT_TREEDIR, default_hash, BTRFS_ROOT_FSTREE, TYPE_ROOT_ITEM, 1058 0xffffffffffffffff, 0, BTRFS_TYPE_DIRECTORY, default_subvol); 1059 } 1060 1061 static NTSTATUS write_btrfs(HANDLE h, uint64_t size, PUNICODE_STRING label, uint32_t sector_size, uint32_t node_size, uint64_t incompat_flags) { 1062 NTSTATUS Status; 1063 LIST_ENTRY roots, chunks; 1064 btrfs_root *root_root, *chunk_root, *extent_root, *dev_root, *fs_root, *reloc_root; 1065 btrfs_chunk *sys_chunk, *metadata_chunk; 1066 btrfs_dev dev; 1067 BTRFS_UUID fsuuid, chunkuuid; 1068 bool ssd; 1069 uint64_t metadata_flags; 1070 #ifdef __REACTOS__ 1071 ULONG seed; 1072 #endif 1073 1074 #ifndef __REACTOS__ 1075 srand((unsigned int)time(0)); 1076 get_uuid(&fsuuid); 1077 get_uuid(&chunkuuid); 1078 #else 1079 seed = NtGetTickCount(); 1080 get_uuid(&fsuuid, &seed); 1081 get_uuid(&chunkuuid, &seed); 1082 #endif 1083 1084 InitializeListHead(&roots); 1085 InitializeListHead(&chunks); 1086 1087 root_root = add_root(&roots, BTRFS_ROOT_ROOT); 1088 chunk_root = add_root(&roots, BTRFS_ROOT_CHUNK); 1089 extent_root = add_root(&roots, BTRFS_ROOT_EXTENT); 1090 dev_root = add_root(&roots, BTRFS_ROOT_DEVTREE); 1091 add_root(&roots, BTRFS_ROOT_CHECKSUM); 1092 fs_root = add_root(&roots, BTRFS_ROOT_FSTREE); 1093 reloc_root = add_root(&roots, BTRFS_ROOT_DATA_RELOC); 1094 1095 #ifndef __REACTOS__ 1096 init_device(&dev, 1, size, &fsuuid, sector_size); 1097 #else 1098 init_device(&dev, 1, size, &fsuuid, sector_size, &seed); 1099 #endif 1100 1101 ssd = is_ssd(h); 1102 1103 sys_chunk = add_chunk(&chunks, BLOCK_FLAG_SYSTEM | (ssd ? 0 : BLOCK_FLAG_DUPLICATE), chunk_root, &dev, dev_root, &chunkuuid, sector_size); 1104 if (!sys_chunk) 1105 return STATUS_INTERNAL_ERROR; 1106 1107 metadata_flags = BLOCK_FLAG_METADATA; 1108 1109 if (!ssd && !(incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS)) 1110 metadata_flags |= BLOCK_FLAG_DUPLICATE; 1111 1112 if (incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS) 1113 metadata_flags |= BLOCK_FLAG_DATA; 1114 1115 metadata_chunk = add_chunk(&chunks, metadata_flags, chunk_root, &dev, dev_root, &chunkuuid, sector_size); 1116 if (!metadata_chunk) 1117 return STATUS_INTERNAL_ERROR; 1118 1119 add_item(chunk_root, 1, TYPE_DEV_ITEM, dev.dev_item.dev_id, &dev.dev_item, sizeof(DEV_ITEM)); 1120 1121 set_default_subvol(root_root, node_size); 1122 1123 init_fs_tree(fs_root, node_size); 1124 init_fs_tree(reloc_root, node_size); 1125 1126 assign_addresses(&roots, sys_chunk, metadata_chunk, node_size, root_root, extent_root, incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA); 1127 1128 add_block_group_items(&chunks, extent_root); 1129 1130 Status = write_roots(h, &roots, node_size, &fsuuid, &chunkuuid); 1131 if (!NT_SUCCESS(Status)) 1132 return Status; 1133 1134 Status = clear_first_megabyte(h); 1135 if (!NT_SUCCESS(Status)) 1136 return Status; 1137 1138 Status = write_superblocks(h, &dev, chunk_root, root_root, extent_root, sys_chunk, node_size, &fsuuid, sector_size, label, incompat_flags); 1139 if (!NT_SUCCESS(Status)) 1140 return Status; 1141 1142 free_roots(&roots); 1143 free_chunks(&chunks); 1144 1145 return STATUS_SUCCESS; 1146 } 1147 1148 static bool look_for_device(btrfs_filesystem* bfs, BTRFS_UUID* devuuid) { 1149 uint32_t i; 1150 btrfs_filesystem_device* dev; 1151 1152 for (i = 0; i < bfs->num_devices; i++) { 1153 if (i == 0) 1154 dev = &bfs->device; 1155 else 1156 dev = (btrfs_filesystem_device*)((uint8_t*)dev + offsetof(btrfs_filesystem_device, name[0]) + dev->name_length); 1157 1158 if (RtlCompareMemory(&dev->uuid, devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) 1159 return true; 1160 } 1161 1162 return false; 1163 } 1164 1165 static bool check_superblock_checksum(superblock* sb) { 1166 switch (sb->csum_type) { 1167 case CSUM_TYPE_CRC32C: { 1168 uint32_t crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum)); 1169 1170 return crc32 == *(uint32_t*)sb; 1171 } 1172 1173 case CSUM_TYPE_XXHASH: { 1174 uint64_t hash = XXH64(&sb->uuid, sizeof(superblock) - sizeof(sb->checksum), 0); 1175 1176 return hash == *(uint64_t*)sb; 1177 } 1178 1179 case CSUM_TYPE_SHA256: { 1180 uint8_t hash[SHA256_HASH_SIZE]; 1181 1182 calc_sha256(hash, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum)); 1183 1184 return !memcmp(hash, sb, SHA256_HASH_SIZE); 1185 } 1186 1187 case CSUM_TYPE_BLAKE2: { 1188 uint8_t hash[BLAKE2_HASH_SIZE]; 1189 1190 blake2b(hash, sizeof(hash), &sb->uuid, sizeof(superblock) - sizeof(sb->checksum)); 1191 1192 return !memcmp(hash, sb, BLAKE2_HASH_SIZE); 1193 } 1194 1195 default: 1196 return false; 1197 } 1198 } 1199 1200 static bool is_mounted_multi_device(HANDLE h, uint32_t sector_size) { 1201 NTSTATUS Status; 1202 superblock* sb; 1203 ULONG sblen; 1204 IO_STATUS_BLOCK iosb; 1205 LARGE_INTEGER off; 1206 BTRFS_UUID fsuuid, devuuid; 1207 UNICODE_STRING us; 1208 OBJECT_ATTRIBUTES atts; 1209 HANDLE h2; 1210 btrfs_filesystem *bfs = NULL, *bfs2; 1211 ULONG bfssize; 1212 bool ret = false; 1213 1214 static WCHAR btrfs[] = L"\\Btrfs"; 1215 1216 sblen = sizeof(*sb); 1217 if (sblen & (sector_size - 1)) 1218 sblen = (sblen & sector_size) + sector_size; 1219 1220 sb = malloc(sblen); 1221 1222 off.QuadPart = superblock_addrs[0]; 1223 1224 Status = NtReadFile(h, NULL, NULL, NULL, &iosb, sb, sblen, &off, NULL); 1225 if (!NT_SUCCESS(Status)) { 1226 free(sb); 1227 return false; 1228 } 1229 1230 if (sb->magic != BTRFS_MAGIC) { 1231 free(sb); 1232 return false; 1233 } 1234 1235 if (!check_superblock_checksum(sb)) { 1236 free(sb); 1237 return false; 1238 } 1239 1240 fsuuid = sb->uuid; 1241 devuuid = sb->dev_item.device_uuid; 1242 1243 free(sb); 1244 1245 us.Length = us.MaximumLength = (USHORT)(wcslen(btrfs) * sizeof(WCHAR)); 1246 us.Buffer = btrfs; 1247 1248 InitializeObjectAttributes(&atts, &us, 0, NULL, NULL); 1249 1250 Status = NtOpenFile(&h2, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &atts, &iosb, 1251 FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); 1252 if (!NT_SUCCESS(Status)) // not a problem, it usually just means the driver isn't loaded 1253 return false; 1254 1255 bfssize = 0; 1256 1257 do { 1258 bfssize += 1024; 1259 1260 if (bfs) free(bfs); 1261 bfs = malloc(bfssize); 1262 1263 Status = NtDeviceIoControlFile(h2, NULL, NULL, NULL, &iosb, IOCTL_BTRFS_QUERY_FILESYSTEMS, NULL, 0, bfs, bfssize); 1264 } while (Status == STATUS_BUFFER_OVERFLOW); 1265 1266 if (!NT_SUCCESS(Status)) 1267 goto end; 1268 1269 if (bfs->num_devices != 0) { 1270 bfs2 = bfs; 1271 while (true) { 1272 if (RtlCompareMemory(&bfs2->uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { 1273 if (bfs2->num_devices == 1) 1274 ret = false; 1275 else 1276 ret = look_for_device(bfs2, &devuuid); 1277 1278 goto end; 1279 } 1280 1281 if (bfs2->next_entry == 0) 1282 break; 1283 else 1284 bfs2 = (btrfs_filesystem*)((uint8_t*)bfs2 + bfs2->next_entry); 1285 } 1286 } 1287 1288 end: 1289 NtClose(h2); 1290 1291 if (bfs) 1292 free(bfs); 1293 1294 return ret; 1295 } 1296 1297 static void do_full_trim(HANDLE h) { 1298 IO_STATUS_BLOCK iosb; 1299 DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa; 1300 1301 RtlZeroMemory(&dmdsa, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)); 1302 1303 dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES); 1304 dmdsa.Action = DeviceDsmAction_Trim; 1305 dmdsa.Flags = DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE | DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED; 1306 dmdsa.ParameterBlockOffset = 0; 1307 dmdsa.ParameterBlockLength = 0; 1308 dmdsa.DataSetRangesOffset = 0; 1309 dmdsa.DataSetRangesLength = 0; 1310 1311 NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, &dmdsa, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), NULL, 0); 1312 } 1313 1314 static bool is_power_of_two(ULONG i) { 1315 return ((i != 0) && !(i & (i - 1))); 1316 } 1317 1318 #if !defined(__REACTOS__) && (defined(_X86_) || defined(_AMD64_)) 1319 static void check_cpu() { 1320 unsigned int cpuInfo[4]; 1321 bool have_sse42; 1322 1323 #ifndef _MSC_VER 1324 __get_cpuid(1, &cpuInfo[0], &cpuInfo[1], &cpuInfo[2], &cpuInfo[3]); 1325 have_sse42 = cpuInfo[2] & bit_SSE4_2; 1326 #else 1327 __cpuid(cpuInfo, 1); 1328 have_sse42 = cpuInfo[2] & (1 << 20); 1329 #endif 1330 1331 if (have_sse42) 1332 calc_crc32c = calc_crc32c_hw; 1333 } 1334 #endif 1335 1336 static NTSTATUS NTAPI FormatEx2(PUNICODE_STRING DriveRoot, FMIFS_MEDIA_FLAG MediaFlag, PUNICODE_STRING Label, 1337 BOOLEAN QuickFormat, ULONG ClusterSize, PFMIFSCALLBACK Callback) 1338 { 1339 NTSTATUS Status; 1340 HANDLE h, btrfsh; 1341 OBJECT_ATTRIBUTES attr; 1342 IO_STATUS_BLOCK iosb; 1343 GET_LENGTH_INFORMATION gli; 1344 DISK_GEOMETRY dg; 1345 uint32_t sector_size, node_size; 1346 UNICODE_STRING btrfsus; 1347 #ifndef __REACTOS__ 1348 HANDLE token; 1349 TOKEN_PRIVILEGES tp; 1350 LUID luid; 1351 #endif 1352 uint64_t incompat_flags; 1353 UNICODE_STRING empty_label; 1354 1355 static WCHAR btrfs[] = L"\\Btrfs"; 1356 1357 #ifndef __REACTOS__ 1358 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) 1359 return STATUS_PRIVILEGE_NOT_HELD; 1360 1361 if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) { 1362 CloseHandle(token); 1363 return STATUS_PRIVILEGE_NOT_HELD; 1364 } 1365 1366 tp.PrivilegeCount = 1; 1367 tp.Privileges[0].Luid = luid; 1368 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 1369 1370 if (!AdjustTokenPrivileges(token, false, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { 1371 CloseHandle(token); 1372 return STATUS_PRIVILEGE_NOT_HELD; 1373 } 1374 1375 CloseHandle(token); 1376 1377 #if defined(_X86_) || defined(_AMD64_) 1378 check_cpu(); 1379 #endif 1380 #endif 1381 1382 if (def_csum_type != CSUM_TYPE_CRC32C && def_csum_type != CSUM_TYPE_XXHASH && def_csum_type != CSUM_TYPE_SHA256 && 1383 def_csum_type != CSUM_TYPE_BLAKE2) 1384 return STATUS_INVALID_PARAMETER; 1385 1386 InitializeObjectAttributes(&attr, DriveRoot, OBJ_CASE_INSENSITIVE, NULL, NULL); 1387 1388 Status = NtOpenFile(&h, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb, 1389 FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT); 1390 1391 if (!NT_SUCCESS(Status)) 1392 return Status; 1393 1394 Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli)); 1395 if (!NT_SUCCESS(Status)) { 1396 NtClose(h); 1397 return Status; 1398 } 1399 1400 // MSDN tells us to use IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, but there are 1401 // some instances where it fails and IOCTL_DISK_GET_DRIVE_GEOMETRY succeeds - 1402 // such as with spanned volumes. 1403 Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(dg)); 1404 if (!NT_SUCCESS(Status)) { 1405 NtClose(h); 1406 return Status; 1407 } 1408 1409 if (def_sector_size == 0) { 1410 sector_size = dg.BytesPerSector; 1411 1412 if (sector_size == 0x200 || sector_size == 0) 1413 sector_size = 0x1000; 1414 } else { 1415 if (dg.BytesPerSector != 0 && (def_sector_size < dg.BytesPerSector || def_sector_size % dg.BytesPerSector != 0 || !is_power_of_two(def_sector_size / dg.BytesPerSector))) { 1416 NtClose(h); 1417 return STATUS_INVALID_PARAMETER; 1418 } 1419 1420 sector_size = def_sector_size; 1421 } 1422 1423 if (def_node_size == 0) 1424 node_size = 0x4000; 1425 else { 1426 if (def_node_size < sector_size || def_node_size % sector_size != 0 || !is_power_of_two(def_node_size / sector_size)) { 1427 NtClose(h); 1428 return STATUS_INVALID_PARAMETER; 1429 } 1430 1431 node_size = def_node_size; 1432 } 1433 1434 if (Callback) { 1435 ULONG pc = 0; 1436 Callback(PROGRESS, 0, (PVOID)&pc); 1437 } 1438 1439 NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0); 1440 1441 if (is_mounted_multi_device(h, sector_size)) { 1442 Status = STATUS_ACCESS_DENIED; 1443 goto end; 1444 } 1445 1446 do_full_trim(h); 1447 1448 incompat_flags = def_incompat_flags; 1449 incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_BIG_METADATA; 1450 1451 if (!Label) { 1452 empty_label.Buffer = NULL; 1453 empty_label.Length = empty_label.MaximumLength = 0; 1454 Label = &empty_label; 1455 } 1456 1457 Status = write_btrfs(h, gli.Length.QuadPart, Label, sector_size, node_size, incompat_flags); 1458 1459 NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0); 1460 1461 end: 1462 NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0); 1463 1464 NtClose(h); 1465 1466 if (NT_SUCCESS(Status)) { 1467 btrfsus.Buffer = btrfs; 1468 btrfsus.Length = btrfsus.MaximumLength = (USHORT)(wcslen(btrfs) * sizeof(WCHAR)); 1469 1470 InitializeObjectAttributes(&attr, &btrfsus, 0, NULL, NULL); 1471 1472 Status = NtOpenFile(&btrfsh, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb, 1473 FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT); 1474 1475 if (NT_SUCCESS(Status)) { 1476 MOUNTDEV_NAME* mdn; 1477 ULONG mdnsize; 1478 1479 mdnsize = (ULONG)(offsetof(MOUNTDEV_NAME, Name[0]) + DriveRoot->Length); 1480 mdn = malloc(mdnsize); 1481 1482 mdn->NameLength = DriveRoot->Length; 1483 memcpy(mdn->Name, DriveRoot->Buffer, DriveRoot->Length); 1484 1485 NtDeviceIoControlFile(btrfsh, NULL, NULL, NULL, &iosb, IOCTL_BTRFS_PROBE_VOLUME, mdn, mdnsize, NULL, 0); 1486 1487 free(mdn); 1488 1489 NtClose(btrfsh); 1490 } 1491 1492 Status = STATUS_SUCCESS; 1493 } 1494 1495 #ifndef __REACTOS__ 1496 if (Callback) { 1497 bool success = NT_SUCCESS(Status); 1498 Callback(DONE, 0, (PVOID)&success); 1499 } 1500 #endif 1501 1502 return Status; 1503 } 1504 1505 #ifdef __REACTOS__ 1506 1507 BOOLEAN 1508 NTAPI 1509 BtrfsFormat( 1510 IN PUNICODE_STRING DriveRoot, 1511 IN PFMIFSCALLBACK Callback, 1512 IN BOOLEAN QuickFormat, 1513 IN BOOLEAN BackwardCompatible, 1514 IN MEDIA_TYPE MediaType, 1515 IN PUNICODE_STRING Label, 1516 IN ULONG ClusterSize) 1517 { 1518 NTSTATUS Status; 1519 1520 if (BackwardCompatible) 1521 { 1522 // DPRINT1("BackwardCompatible == TRUE is unsupported!\n"); 1523 return FALSE; 1524 } 1525 1526 Status = FormatEx2(DriveRoot, (FMIFS_MEDIA_FLAG)MediaType, Label, QuickFormat, ClusterSize, Callback); 1527 1528 return NT_SUCCESS(Status); 1529 } 1530 1531 #else 1532 1533 BOOL __stdcall FormatEx(DSTRING* root, STREAM_MESSAGE* message, options* opts, uint32_t unk1) { 1534 UNICODE_STRING DriveRoot, Label; 1535 NTSTATUS Status; 1536 1537 if (!root || !root->string) 1538 return false; 1539 1540 DriveRoot.Length = DriveRoot.MaximumLength = (USHORT)(wcslen(root->string) * sizeof(WCHAR)); 1541 DriveRoot.Buffer = root->string; 1542 1543 if (opts && opts->label && opts->label->string) { 1544 Label.Length = Label.MaximumLength = (USHORT)(wcslen(opts->label->string) * sizeof(WCHAR)); 1545 Label.Buffer = opts->label->string; 1546 } else { 1547 Label.Length = Label.MaximumLength = 0; 1548 Label.Buffer = NULL; 1549 } 1550 1551 Status = FormatEx2(&DriveRoot, FMIFS_HARDDISK, &Label, opts && opts->flags & FORMAT_FLAG_QUICK_FORMAT, 0, NULL); 1552 1553 return NT_SUCCESS(Status); 1554 } 1555 1556 #endif // __REACTOS__ 1557 1558 void __stdcall SetSizes(ULONG sector, ULONG node) { 1559 if (sector != 0) 1560 def_sector_size = sector; 1561 1562 if (node != 0) 1563 def_node_size = node; 1564 } 1565 1566 void __stdcall SetIncompatFlags(uint64_t incompat_flags) { 1567 def_incompat_flags = incompat_flags; 1568 } 1569 1570 void __stdcall SetCsumType(uint16_t csum_type) { 1571 def_csum_type = csum_type; 1572 } 1573 1574 BOOL __stdcall GetFilesystemInformation(uint32_t unk1, uint32_t unk2, void* unk3) { 1575 // STUB - undocumented 1576 1577 return true; 1578 } 1579 1580 #ifndef __REACTOS__ 1581 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) { 1582 if (dwReason == DLL_PROCESS_ATTACH) 1583 module = (HMODULE)hModule; 1584 1585 return true; 1586 } 1587 #endif 1588