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