xref: /reactos/sdk/lib/fslib/btrfslib/btrfslib.c (revision 7e069ccd)
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 = BTRFS_ROOT_TREEDIR;
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 / sizeof(WCHAR), 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 / sizeof(WCHAR), 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 add_inode_ref(btrfs_root* r, uint64_t inode, uint64_t parent, uint64_t index, const char* name) {
933     uint16_t name_len = (uint16_t)strlen(name);
934 #ifndef __REACTOS__
935     INODE_REF* ir = malloc(offsetof(INODE_REF, name[0]) + name_len);
936 #else
937     INODE_REF* ir = RtlAllocateHeap(RtlGetProcessHeap(), 0, offsetof(INODE_REF, name[0]) + name_len);
938 #endif
939 
940     ir->index = 0;
941     ir->n = name_len;
942     memcpy(ir->name, name, name_len);
943 
944     add_item(r, inode, TYPE_INODE_REF, parent, ir, (uint16_t)offsetof(INODE_REF, name[0]) + ir->n);
945 
946 #ifndef __REACTOS__
947     free(ir);
948 #else
949     RtlFreeHeap(RtlGetProcessHeap(), 0, ir);
950 #endif
951 }
952 
953 static void init_fs_tree(btrfs_root* r, uint32_t node_size) {
954     INODE_ITEM ii;
955     FILETIME filetime;
956     LARGE_INTEGER time;
957 
958     memset(&ii, 0, sizeof(INODE_ITEM));
959 
960     ii.generation = 1;
961     ii.st_blocks = node_size;
962     ii.st_nlink = 1;
963     ii.st_mode = 040755;
964 
965     GetSystemTimeAsFileTime(&filetime);
966     time.LowPart = filetime.dwLowDateTime;
967     time.HighPart = filetime.dwHighDateTime;
968 
969     win_time_to_unix(time, &ii.st_atime);
970     ii.st_ctime = ii.st_mtime = ii.st_atime;
971 
972     add_item(r, SUBVOL_ROOT_INODE, TYPE_INODE_ITEM, 0, &ii, sizeof(INODE_ITEM));
973 
974     add_inode_ref(r, SUBVOL_ROOT_INODE, SUBVOL_ROOT_INODE, 0, "..");
975 }
976 
977 static void add_block_group_items(LIST_ENTRY* chunks, btrfs_root* extent_root) {
978     LIST_ENTRY* le;
979 
980     le = chunks->Flink;
981     while (le != chunks) {
982         btrfs_chunk* c = CONTAINING_RECORD(le, btrfs_chunk, list_entry);
983         BLOCK_GROUP_ITEM bgi;
984 
985         bgi.used = c->used;
986         bgi.chunk_tree = 0x100;
987         bgi.flags = c->chunk_item->type;
988         add_item(extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, &bgi, sizeof(BLOCK_GROUP_ITEM));
989 
990         le = le->Flink;
991     }
992 }
993 
994 static NTSTATUS clear_first_megabyte(HANDLE h) {
995     NTSTATUS Status;
996     IO_STATUS_BLOCK iosb;
997     LARGE_INTEGER zero;
998     uint8_t* mb;
999 
1000 #ifndef __REACTOS__
1001     mb = malloc(0x100000);
1002     memset(mb, 0, 0x100000);
1003 #else
1004     mb = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 0x100000);
1005 #endif
1006 
1007     zero.QuadPart = 0;
1008 
1009     Status = NtWriteFile(h, NULL, NULL, NULL, &iosb, mb, 0x100000, &zero, NULL);
1010 
1011 #ifndef __REACTOS__
1012     free(mb);
1013 #else
1014     RtlFreeHeap(RtlGetProcessHeap(), 0, mb);
1015 #endif
1016 
1017     return Status;
1018 }
1019 
1020 static BOOL is_ssd(HANDLE h) {
1021     ULONG aptelen;
1022     ATA_PASS_THROUGH_EX* apte;
1023     IO_STATUS_BLOCK iosb;
1024     NTSTATUS Status;
1025     IDENTIFY_DEVICE_DATA* idd;
1026 
1027     aptelen = sizeof(ATA_PASS_THROUGH_EX) + 512;
1028 #ifndef __REACTOS__
1029     apte = malloc(aptelen);
1030 
1031     RtlZeroMemory(apte, aptelen);
1032 #else
1033     apte = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, aptelen);
1034 #endif
1035 
1036     apte->Length = sizeof(ATA_PASS_THROUGH_EX);
1037     apte->AtaFlags = ATA_FLAGS_DATA_IN;
1038     apte->DataTransferLength = aptelen - sizeof(ATA_PASS_THROUGH_EX);
1039     apte->TimeOutValue = 3;
1040     apte->DataBufferOffset = apte->Length;
1041     apte->CurrentTaskFile[6] = IDE_COMMAND_IDENTIFY;
1042 
1043     Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_ATA_PASS_THROUGH, apte, aptelen, apte, aptelen);
1044 
1045     if (NT_SUCCESS(Status)) {
1046         idd = (IDENTIFY_DEVICE_DATA*)((uint8_t*)apte + sizeof(ATA_PASS_THROUGH_EX));
1047 
1048         if (idd->NominalMediaRotationRate == 1) {
1049 #ifndef __REACTOS__
1050             free(apte);
1051 #else
1052             RtlFreeHeap(RtlGetProcessHeap(), 0, apte);
1053 #endif
1054             return TRUE;
1055         }
1056     }
1057 
1058 #ifndef __REACTOS__
1059     free(apte);
1060 #else
1061     RtlFreeHeap(RtlGetProcessHeap(), 0, apte);
1062 #endif
1063 
1064     return FALSE;
1065 }
1066 
1067 static void add_dir_item(btrfs_root* root, uint64_t inode, uint32_t hash, uint64_t key_objid, uint8_t key_type,
1068                          uint64_t key_offset, uint64_t transid, uint8_t type, const char* name) {
1069     uint16_t name_len = (uint16_t)strlen(name);
1070 #ifndef __REACTOS__
1071     DIR_ITEM* di = malloc(offsetof(DIR_ITEM, name[0]) + name_len);
1072 #else
1073     DIR_ITEM* di = RtlAllocateHeap(RtlGetProcessHeap(), 0, offsetof(DIR_ITEM, name[0]) + name_len);
1074 #endif
1075 
1076     di->key.obj_id = key_objid;
1077     di->key.obj_type = key_type;
1078     di->key.offset = key_offset;
1079     di->transid = transid;
1080     di->m = 0;
1081     di->n = name_len;
1082     di->type = type;
1083     memcpy(di->name, name, name_len);
1084 
1085     add_item(root, inode, TYPE_DIR_ITEM, hash, di, (uint16_t)(offsetof(DIR_ITEM, name[0]) + di->m + di->n));
1086 
1087 #ifndef __REACTOS__
1088     free(di);
1089 #else
1090     RtlFreeHeap(RtlGetProcessHeap(), 0, di);
1091 #endif
1092 }
1093 
1094 static void set_default_subvol(btrfs_root* root_root, uint32_t node_size) {
1095     INODE_ITEM ii;
1096     FILETIME filetime;
1097     LARGE_INTEGER time;
1098 
1099     static const char default_subvol[] = "default";
1100     static const uint32_t default_hash = 0x8dbfc2d2;
1101 
1102     add_inode_ref(root_root, BTRFS_ROOT_FSTREE, BTRFS_ROOT_TREEDIR, 0, default_subvol);
1103 
1104     memset(&ii, 0, sizeof(INODE_ITEM));
1105 
1106     ii.generation = 1;
1107     ii.st_blocks = node_size;
1108     ii.st_nlink = 1;
1109     ii.st_mode = 040755;
1110 
1111     GetSystemTimeAsFileTime(&filetime);
1112     time.LowPart = filetime.dwLowDateTime;
1113     time.HighPart = filetime.dwHighDateTime;
1114 
1115     win_time_to_unix(time, &ii.st_atime);
1116     ii.st_ctime = ii.st_mtime = ii.otime = ii.st_atime;
1117 
1118     add_item(root_root, BTRFS_ROOT_TREEDIR, TYPE_INODE_ITEM, 0, &ii, sizeof(INODE_ITEM));
1119 
1120     add_inode_ref(root_root, BTRFS_ROOT_TREEDIR, BTRFS_ROOT_TREEDIR, 0, "..");
1121 
1122     add_dir_item(root_root, BTRFS_ROOT_TREEDIR, default_hash, BTRFS_ROOT_FSTREE, TYPE_ROOT_ITEM,
1123                  0xffffffffffffffff, 0, BTRFS_TYPE_DIRECTORY, default_subvol);
1124 }
1125 
1126 static NTSTATUS write_btrfs(HANDLE h, uint64_t size, PUNICODE_STRING label, uint32_t sector_size, uint32_t node_size, uint64_t incompat_flags) {
1127     NTSTATUS Status;
1128     LIST_ENTRY roots, chunks;
1129     btrfs_root *root_root, *chunk_root, *extent_root, *dev_root, *fs_root, *reloc_root;
1130     btrfs_chunk *sys_chunk, *metadata_chunk;
1131     btrfs_dev dev;
1132     BTRFS_UUID fsuuid, chunkuuid;
1133     BOOL ssd;
1134     uint64_t metadata_flags;
1135 #ifdef __REACTOS__
1136     ULONG seed;
1137 #endif
1138 
1139 #ifndef __REACTOS__
1140     srand((unsigned int)time(0));
1141     get_uuid(&fsuuid);
1142     get_uuid(&chunkuuid);
1143 #else
1144     seed = NtGetTickCount();
1145     get_uuid(&fsuuid, &seed);
1146     get_uuid(&chunkuuid, &seed);
1147 #endif
1148 
1149     InitializeListHead(&roots);
1150     InitializeListHead(&chunks);
1151 
1152     root_root = add_root(&roots, BTRFS_ROOT_ROOT);
1153     chunk_root = add_root(&roots, BTRFS_ROOT_CHUNK);
1154     extent_root = add_root(&roots, BTRFS_ROOT_EXTENT);
1155     dev_root = add_root(&roots, BTRFS_ROOT_DEVTREE);
1156     add_root(&roots, BTRFS_ROOT_CHECKSUM);
1157     fs_root = add_root(&roots, BTRFS_ROOT_FSTREE);
1158     reloc_root = add_root(&roots, BTRFS_ROOT_DATA_RELOC);
1159 
1160 #ifndef __REACTOS__
1161     init_device(&dev, 1, size, &fsuuid, sector_size);
1162 #else
1163     init_device(&dev, 1, size, &fsuuid, sector_size, &seed);
1164 #endif
1165 
1166     ssd = is_ssd(h);
1167 
1168     sys_chunk = add_chunk(&chunks, BLOCK_FLAG_SYSTEM | (ssd ? 0 : BLOCK_FLAG_DUPLICATE), chunk_root, &dev, dev_root, &chunkuuid, sector_size);
1169     if (!sys_chunk)
1170         return STATUS_INTERNAL_ERROR;
1171 
1172     metadata_flags = BLOCK_FLAG_METADATA;
1173 
1174     if (!ssd && !(incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS))
1175         metadata_flags |= BLOCK_FLAG_DUPLICATE;
1176 
1177     if (incompat_flags & BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS)
1178         metadata_flags |= BLOCK_FLAG_DATA;
1179 
1180     metadata_chunk = add_chunk(&chunks, metadata_flags, chunk_root, &dev, dev_root, &chunkuuid, sector_size);
1181     if (!metadata_chunk)
1182         return STATUS_INTERNAL_ERROR;
1183 
1184     add_item(chunk_root, 1, TYPE_DEV_ITEM, dev.dev_item.dev_id, &dev.dev_item, sizeof(DEV_ITEM));
1185 
1186     set_default_subvol(root_root, node_size);
1187 
1188     init_fs_tree(fs_root, node_size);
1189     init_fs_tree(reloc_root, node_size);
1190 
1191     assign_addresses(&roots, sys_chunk, metadata_chunk, node_size, root_root, extent_root, incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA);
1192 
1193     add_block_group_items(&chunks, extent_root);
1194 
1195     Status = write_roots(h, &roots, node_size, &fsuuid, &chunkuuid);
1196     if (!NT_SUCCESS(Status))
1197         return Status;
1198 
1199     Status = clear_first_megabyte(h);
1200     if (!NT_SUCCESS(Status))
1201         return Status;
1202 
1203     Status = write_superblocks(h, &dev, chunk_root, root_root, extent_root, sys_chunk, node_size, &fsuuid, sector_size, label, incompat_flags);
1204     if (!NT_SUCCESS(Status))
1205         return Status;
1206 
1207     free_roots(&roots);
1208     free_chunks(&chunks);
1209 
1210     return STATUS_SUCCESS;
1211 }
1212 
1213 static BOOL look_for_device(btrfs_filesystem* bfs, BTRFS_UUID* devuuid) {
1214     uint32_t i;
1215     btrfs_filesystem_device* dev;
1216 
1217     for (i = 0; i < bfs->num_devices; i++) {
1218         if (i == 0)
1219             dev = &bfs->device;
1220         else
1221             dev = (btrfs_filesystem_device*)((uint8_t*)dev + offsetof(btrfs_filesystem_device, name[0]) + dev->name_length);
1222 
1223         if (RtlCompareMemory(&dev->uuid, devuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID))
1224             return TRUE;
1225     }
1226 
1227     return FALSE;
1228 }
1229 
1230 static BOOL is_mounted_multi_device(HANDLE h, uint32_t sector_size) {
1231     NTSTATUS Status;
1232     superblock* sb;
1233     ULONG sblen;
1234     IO_STATUS_BLOCK iosb;
1235     LARGE_INTEGER off;
1236     BTRFS_UUID fsuuid, devuuid;
1237     uint32_t crc32;
1238     UNICODE_STRING us;
1239     OBJECT_ATTRIBUTES atts;
1240     HANDLE h2;
1241     btrfs_filesystem *bfs = NULL, *bfs2;
1242     ULONG bfssize;
1243     BOOL ret = FALSE;
1244 
1245     static WCHAR btrfs[] = L"\\Btrfs";
1246 
1247     sblen = sizeof(*sb);
1248     if (sblen & (sector_size - 1))
1249         sblen = (sblen & sector_size) + sector_size;
1250 
1251 #ifndef __REACTOS__
1252     sb = malloc(sblen);
1253 #else
1254     sb = RtlAllocateHeap(RtlGetProcessHeap(), 0, sblen);
1255 #endif
1256 
1257     off.QuadPart = superblock_addrs[0];
1258 
1259     Status = NtReadFile(h, NULL, NULL, NULL, &iosb, sb, sblen, &off, NULL);
1260     if (!NT_SUCCESS(Status)) {
1261 #ifndef __REACTOS__
1262         free(sb);
1263 #else
1264         RtlFreeHeap(RtlGetProcessHeap(), 0, sb);
1265 #endif
1266         return FALSE;
1267     }
1268 
1269     if (sb->magic != BTRFS_MAGIC) {
1270 #ifndef __REACTOS__
1271         free(sb);
1272 #else
1273         RtlFreeHeap(RtlGetProcessHeap(), 0, sb);
1274 #endif
1275         return FALSE;
1276     }
1277 
1278     crc32 = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum));
1279     if (crc32 != *((uint32_t*)sb)) {
1280 #ifndef __REACTOS__
1281         free(sb);
1282 #else
1283         RtlFreeHeap(RtlGetProcessHeap(), 0, sb);
1284 #endif
1285         return FALSE;
1286     }
1287 
1288     fsuuid = sb->uuid;
1289     devuuid = sb->dev_item.device_uuid;
1290 
1291 #ifndef __REACTOS__
1292     free(sb);
1293 #else
1294     RtlFreeHeap(RtlGetProcessHeap(), 0, sb);
1295 #endif
1296 
1297     us.Length = us.MaximumLength = (USHORT)(wcslen(btrfs) * sizeof(WCHAR));
1298     us.Buffer = btrfs;
1299 
1300     InitializeObjectAttributes(&atts, &us, 0, NULL, NULL);
1301 
1302     Status = NtOpenFile(&h2, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &atts, &iosb,
1303                         FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT);
1304     if (!NT_SUCCESS(Status)) // not a problem, it usually just means the driver isn't loaded
1305         return FALSE;
1306 
1307     bfssize = 0;
1308 
1309     do {
1310         bfssize += 1024;
1311 
1312 #ifndef __REACTOS__
1313         if (bfs) free(bfs);
1314         bfs = malloc(bfssize);
1315 #else
1316         if (bfs) RtlFreeHeap(RtlGetProcessHeap(), 0, bfs);
1317         bfs = RtlAllocateHeap(RtlGetProcessHeap(), 0, bfssize);
1318 #endif
1319 
1320         Status = NtDeviceIoControlFile(h2, NULL, NULL, NULL, &iosb, IOCTL_BTRFS_QUERY_FILESYSTEMS, NULL, 0, bfs, bfssize);
1321         if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
1322             NtClose(h2);
1323             return FALSE;
1324         }
1325     } while (Status == STATUS_BUFFER_OVERFLOW);
1326 
1327     if (!NT_SUCCESS(Status))
1328         goto end;
1329 
1330     if (bfs->num_devices != 0) {
1331         bfs2 = bfs;
1332         while (TRUE) {
1333             if (RtlCompareMemory(&bfs2->uuid, &fsuuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) {
1334                 if (bfs2->num_devices == 1)
1335                     ret = FALSE;
1336                 else
1337                     ret = look_for_device(bfs2, &devuuid);
1338 
1339                 goto end;
1340             }
1341 
1342             if (bfs2->next_entry == 0)
1343                 break;
1344             else
1345                 bfs2 = (btrfs_filesystem*)((uint8_t*)bfs2 + bfs2->next_entry);
1346         }
1347     }
1348 
1349 end:
1350     NtClose(h2);
1351 
1352     if (bfs)
1353 #ifndef __REACTOS__
1354         free(bfs);
1355 #else
1356         RtlFreeHeap(RtlGetProcessHeap(), 0, bfs);
1357 #endif
1358 
1359     return ret;
1360 }
1361 
1362 static void do_full_trim(HANDLE h) {
1363     IO_STATUS_BLOCK iosb;
1364     DEVICE_MANAGE_DATA_SET_ATTRIBUTES dmdsa;
1365 
1366     RtlZeroMemory(&dmdsa, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES));
1367 
1368     dmdsa.Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES);
1369     dmdsa.Action = DeviceDsmAction_Trim;
1370     dmdsa.Flags = DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE | DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED;
1371     dmdsa.ParameterBlockOffset = 0;
1372     dmdsa.ParameterBlockLength = 0;
1373     dmdsa.DataSetRangesOffset = 0;
1374     dmdsa.DataSetRangesLength = 0;
1375 
1376     NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES, &dmdsa, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), NULL, 0);
1377 }
1378 
1379 static BOOL is_power_of_two(ULONG i) {
1380     return ((i != 0) && !(i & (i - 1)));
1381 }
1382 
1383 #ifndef __REACTOS__
1384 static NTSTATUS NTAPI FormatEx2(PUNICODE_STRING DriveRoot, FMIFS_MEDIA_FLAG MediaFlag, PUNICODE_STRING Label,
1385 #else
1386 NTSTATUS NTAPI BtrfsFormatEx(PUNICODE_STRING DriveRoot, FMIFS_MEDIA_FLAG MediaFlag, PUNICODE_STRING Label,
1387 #endif
1388                                 BOOLEAN QuickFormat, ULONG ClusterSize, PFMIFSCALLBACK Callback)
1389 {
1390     NTSTATUS Status;
1391     HANDLE h, btrfsh;
1392     OBJECT_ATTRIBUTES attr;
1393     IO_STATUS_BLOCK iosb;
1394     GET_LENGTH_INFORMATION gli;
1395     DISK_GEOMETRY dg;
1396     uint32_t sector_size, node_size;
1397     UNICODE_STRING btrfsus;
1398 #ifndef __REACTOS__
1399     HANDLE token;
1400     TOKEN_PRIVILEGES tp;
1401     LUID luid;
1402 #endif
1403     uint64_t incompat_flags;
1404     UNICODE_STRING empty_label;
1405 
1406     static WCHAR btrfs[] = L"\\Btrfs";
1407 
1408 #ifndef __REACTOS__
1409     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
1410         return STATUS_PRIVILEGE_NOT_HELD;
1411 
1412     if (!LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &luid)) {
1413         CloseHandle(token);
1414         return STATUS_PRIVILEGE_NOT_HELD;
1415     }
1416 
1417     tp.PrivilegeCount = 1;
1418     tp.Privileges[0].Luid = luid;
1419     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1420 
1421     if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
1422         CloseHandle(token);
1423         return STATUS_PRIVILEGE_NOT_HELD;
1424     }
1425 
1426     CloseHandle(token);
1427 #endif
1428 
1429     InitializeObjectAttributes(&attr, DriveRoot, OBJ_CASE_INSENSITIVE, NULL, NULL);
1430 
1431     Status = NtOpenFile(&h, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb,
1432                         FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT);
1433 
1434     if (!NT_SUCCESS(Status))
1435         return Status;
1436 
1437     Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli));
1438     if (!NT_SUCCESS(Status)) {
1439         NtClose(h);
1440         return Status;
1441     }
1442 
1443     // MSDN tells us to use IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, but there are
1444     // some instances where it fails and IOCTL_DISK_GET_DRIVE_GEOMETRY succeeds -
1445     // such as with spanned volumes.
1446     Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(dg));
1447     if (!NT_SUCCESS(Status)) {
1448         NtClose(h);
1449         return Status;
1450     }
1451 
1452     if (def_sector_size == 0) {
1453         sector_size = dg.BytesPerSector;
1454 
1455         if (sector_size == 0x200 || sector_size == 0)
1456             sector_size = 0x1000;
1457     } else {
1458         if (dg.BytesPerSector != 0 && (def_sector_size < dg.BytesPerSector || def_sector_size % dg.BytesPerSector != 0 || !is_power_of_two(def_sector_size / dg.BytesPerSector))) {
1459             NtClose(h);
1460             return STATUS_INVALID_PARAMETER;
1461         }
1462 
1463         sector_size = def_sector_size;
1464     }
1465 
1466     if (def_node_size == 0)
1467         node_size = 0x4000;
1468     else {
1469         if (def_node_size < sector_size || def_node_size % sector_size != 0 || !is_power_of_two(def_node_size / sector_size)) {
1470             NtClose(h);
1471             return STATUS_INVALID_PARAMETER;
1472         }
1473 
1474         node_size = def_node_size;
1475     }
1476 
1477     if (Callback) {
1478         ULONG pc = 0;
1479         Callback(PROGRESS, 0, (PVOID)&pc);
1480     }
1481 
1482     NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0);
1483 
1484     if (is_mounted_multi_device(h, sector_size)) {
1485         Status = STATUS_ACCESS_DENIED;
1486         goto end;
1487     }
1488 
1489     do_full_trim(h);
1490 
1491     incompat_flags = def_incompat_flags;
1492     incompat_flags |= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_BIG_METADATA;
1493 
1494     if (!Label) {
1495         empty_label.Buffer = NULL;
1496         empty_label.Length = empty_label.MaximumLength = 0;
1497         Label = &empty_label;
1498     }
1499 
1500     Status = write_btrfs(h, gli.Length.QuadPart, Label, sector_size, node_size, incompat_flags);
1501 
1502     NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0);
1503 
1504 end:
1505     NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0);
1506 
1507     NtClose(h);
1508 
1509     if (NT_SUCCESS(Status)) {
1510         btrfsus.Buffer = btrfs;
1511         btrfsus.Length = btrfsus.MaximumLength = (USHORT)(wcslen(btrfs) * sizeof(WCHAR));
1512 
1513         InitializeObjectAttributes(&attr, &btrfsus, 0, NULL, NULL);
1514 
1515         Status = NtOpenFile(&btrfsh, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb,
1516                             FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT);
1517 
1518         if (NT_SUCCESS(Status)) {
1519             MOUNTDEV_NAME* mdn;
1520             ULONG mdnsize;
1521 
1522             mdnsize = offsetof(MOUNTDEV_NAME, Name[0]) + DriveRoot->Length;
1523 #ifndef __REACTOS__
1524             mdn = malloc(mdnsize);
1525 #else
1526             mdn = RtlAllocateHeap(RtlGetProcessHeap(), 0, mdnsize);
1527 #endif
1528 
1529             mdn->NameLength = DriveRoot->Length;
1530             memcpy(mdn->Name, DriveRoot->Buffer, DriveRoot->Length);
1531 
1532             NtDeviceIoControlFile(btrfsh, NULL, NULL, NULL, &iosb, IOCTL_BTRFS_PROBE_VOLUME, mdn, mdnsize, NULL, 0);
1533 
1534 #ifndef __REACTOS__
1535             free(mdn);
1536 #else
1537             RtlFreeHeap(RtlGetProcessHeap(), 0, mdn);
1538 #endif
1539 
1540             NtClose(btrfsh);
1541         }
1542 
1543         Status = STATUS_SUCCESS;
1544     }
1545 
1546     if (Callback) {
1547         BOOL success = NT_SUCCESS(Status);
1548         Callback(DONE, 0, (PVOID)&success);
1549     }
1550 
1551     return Status;
1552 }
1553 
1554 BOOL __stdcall FormatEx(DSTRING* root, STREAM_MESSAGE* message, options* opts, uint32_t unk1) {
1555     UNICODE_STRING DriveRoot, Label;
1556     NTSTATUS Status;
1557 
1558     if (!root || !root->string)
1559         return FALSE;
1560 
1561     DriveRoot.Length = DriveRoot.MaximumLength = (USHORT)(wcslen(root->string) * sizeof(WCHAR));
1562     DriveRoot.Buffer = root->string;
1563 
1564     if (opts && opts->label && opts->label->string) {
1565         Label.Length = Label.MaximumLength = (USHORT)(wcslen(opts->label->string) * sizeof(WCHAR));
1566         Label.Buffer = opts->label->string;
1567     } else {
1568         Label.Length = Label.MaximumLength = 0;
1569         Label.Buffer = NULL;
1570     }
1571 
1572 #ifndef __REACTOS__
1573     Status = FormatEx2(&DriveRoot, FMIFS_HARDDISK, &Label, opts && opts->flags & FORMAT_FLAG_QUICK_FORMAT, 0, NULL);
1574 #else
1575     Status = BtrfsFormatEx(&DriveRoot, FMIFS_HARDDISK, &Label, opts && opts->flags & FORMAT_FLAG_QUICK_FORMAT, 0, NULL);
1576 #endif
1577 
1578     return NT_SUCCESS(Status);
1579 }
1580 
1581 void __stdcall SetSizes(ULONG sector, ULONG node) {
1582     if (sector != 0)
1583         def_sector_size = sector;
1584 
1585     if (node != 0)
1586         def_node_size = node;
1587 }
1588 
1589 void __stdcall SetIncompatFlags(uint64_t incompat_flags) {
1590     def_incompat_flags = incompat_flags;
1591 }
1592 
1593 BOOL __stdcall GetFilesystemInformation(uint32_t unk1, uint32_t unk2, void* unk3) {
1594     // STUB - undocumented
1595 
1596     return TRUE;
1597 }
1598 
1599 #ifndef __REACTOS__
1600 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) {
1601     if (dwReason == DLL_PROCESS_ATTACH)
1602         module = (HMODULE)hModule;
1603 
1604     return TRUE;
1605 }
1606 #endif
1607