xref: /reactos/sdk/lib/fslib/btrfslib/btrfslib.c (revision 7b1049c8)
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