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