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