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