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