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 "btrfs_drv.h"
19 #include "zstd/zstd.h"
20 
21 extern UNICODE_STRING log_device, log_file, registry_path;
22 extern LIST_ENTRY uid_map_list, gid_map_list;
23 extern ERESOURCE mapping_lock;
24 
25 #ifdef _DEBUG
26 extern HANDLE log_handle;
27 extern ERESOURCE log_lock;
28 extern PFILE_OBJECT comfo;
29 extern PDEVICE_OBJECT comdo;
30 #endif
31 
32 WORK_QUEUE_ITEM wqi;
33 
34 static const WCHAR option_mounted[] = L"Mounted";
35 
36 NTSTATUS registry_load_volume_options(device_extension* Vcb) {
37     BTRFS_UUID* uuid = &Vcb->superblock.uuid;
38     mount_options* options = &Vcb->options;
39     UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus, readonlyus, zliblevelus, flushintervalus,
40                    maxinlineus, subvolidus, skipbalanceus, nobarrierus, notrimus, clearcacheus, allowdegradedus, zstdlevelus,
41                    norootdirus;
42     OBJECT_ATTRIBUTES oa;
43     NTSTATUS Status;
44     ULONG i, j, kvfilen, index, retlen;
45     KEY_VALUE_FULL_INFORMATION* kvfi = NULL;
46     HANDLE h;
47 
48     options->compress = mount_compress;
49     options->compress_force = mount_compress_force;
50     options->compress_type = mount_compress_type > BTRFS_COMPRESSION_ZSTD ? 0 : mount_compress_type;
51     options->readonly = mount_readonly;
52     options->zlib_level = mount_zlib_level;
53     options->zstd_level = mount_zstd_level;
54     options->flush_interval = mount_flush_interval;
55     options->max_inline = min(mount_max_inline, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1);
56     options->skip_balance = mount_skip_balance;
57     options->no_barrier = mount_no_barrier;
58     options->no_trim = mount_no_trim;
59     options->clear_cache = mount_clear_cache;
60     options->allow_degraded = mount_allow_degraded;
61     options->subvol_id = 0;
62 
63     path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
64     path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
65 
66     if (!path.Buffer) {
67         ERR("out of memory\n");
68         return STATUS_INSUFFICIENT_RESOURCES;
69     }
70 
71     RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
72     i = registry_path.Length / sizeof(WCHAR);
73 
74     path.Buffer[i] = '\\';
75     i++;
76 
77     for (j = 0; j < 16; j++) {
78         path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
79         path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
80 
81         i += 2;
82 
83         if (j == 3 || j == 5 || j == 7 || j == 9) {
84             path.Buffer[i] = '-';
85             i++;
86         }
87     }
88 
89     kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR));
90     kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
91     if (!kvfi) {
92         ERR("out of memory\n");
93         Status = STATUS_INSUFFICIENT_RESOURCES;
94         goto end;
95     }
96 
97     InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
98 
99     Status = ZwOpenKey(&h, KEY_QUERY_VALUE, &oa);
100     if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
101         Status = STATUS_SUCCESS;
102         goto end;
103     } else if (!NT_SUCCESS(Status)) {
104         ERR("ZwOpenKey returned %08lx\n", Status);
105         goto end;
106     }
107 
108     index = 0;
109 
110     RtlInitUnicodeString(&ignoreus, L"Ignore");
111     RtlInitUnicodeString(&compressus, L"Compress");
112     RtlInitUnicodeString(&compressforceus, L"CompressForce");
113     RtlInitUnicodeString(&compresstypeus, L"CompressType");
114     RtlInitUnicodeString(&readonlyus, L"Readonly");
115     RtlInitUnicodeString(&zliblevelus, L"ZlibLevel");
116     RtlInitUnicodeString(&flushintervalus, L"FlushInterval");
117     RtlInitUnicodeString(&maxinlineus, L"MaxInline");
118     RtlInitUnicodeString(&subvolidus, L"SubvolId");
119     RtlInitUnicodeString(&skipbalanceus, L"SkipBalance");
120     RtlInitUnicodeString(&nobarrierus, L"NoBarrier");
121     RtlInitUnicodeString(&notrimus, L"NoTrim");
122     RtlInitUnicodeString(&clearcacheus, L"ClearCache");
123     RtlInitUnicodeString(&allowdegradedus, L"AllowDegraded");
124     RtlInitUnicodeString(&zstdlevelus, L"ZstdLevel");
125     RtlInitUnicodeString(&norootdirus, L"NoRootDir");
126 
127     do {
128         Status = ZwEnumerateValueKey(h, index, KeyValueFullInformation, kvfi, kvfilen, &retlen);
129 
130         index++;
131 
132         if (NT_SUCCESS(Status)) {
133             UNICODE_STRING us;
134 
135             us.Length = us.MaximumLength = (USHORT)kvfi->NameLength;
136             us.Buffer = kvfi->Name;
137 
138             if (FsRtlAreNamesEqual(&ignoreus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
139                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
140 
141                 options->ignore = *val != 0 ? true : false;
142             } else if (FsRtlAreNamesEqual(&compressus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
143                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
144 
145                 options->compress = *val != 0 ? true : false;
146             } else if (FsRtlAreNamesEqual(&compressforceus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
147                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
148 
149                 options->compress_force = *val != 0 ? true : false;
150             } else if (FsRtlAreNamesEqual(&compresstypeus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
151                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
152 
153                 options->compress_type = (uint8_t)(*val > BTRFS_COMPRESSION_ZSTD ? 0 : *val);
154             } else if (FsRtlAreNamesEqual(&readonlyus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
155                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
156 
157                 options->readonly = *val != 0 ? true : false;
158             } else if (FsRtlAreNamesEqual(&zliblevelus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
159                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
160 
161                 options->zlib_level = *val;
162             } else if (FsRtlAreNamesEqual(&flushintervalus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
163                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
164 
165                 options->flush_interval = *val;
166             } else if (FsRtlAreNamesEqual(&maxinlineus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
167                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
168 
169                 options->max_inline = min(*val, Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - sizeof(EXTENT_DATA) + 1);
170             } else if (FsRtlAreNamesEqual(&subvolidus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_QWORD) {
171                 uint64_t* val = (uint64_t*)((uint8_t*)kvfi + kvfi->DataOffset);
172 
173                 options->subvol_id = *val;
174             } else if (FsRtlAreNamesEqual(&skipbalanceus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
175                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
176 
177                 options->skip_balance = *val;
178             } else if (FsRtlAreNamesEqual(&nobarrierus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
179                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
180 
181                 options->no_barrier = *val;
182             } else if (FsRtlAreNamesEqual(&notrimus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
183                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
184 
185                 options->no_trim = *val;
186             } else if (FsRtlAreNamesEqual(&clearcacheus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
187                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
188 
189                 options->clear_cache = *val;
190             } else if (FsRtlAreNamesEqual(&allowdegradedus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
191                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
192 
193                 options->allow_degraded = *val;
194             } else if (FsRtlAreNamesEqual(&zstdlevelus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
195                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
196 
197                 options->zstd_level = *val;
198             } else if (FsRtlAreNamesEqual(&norootdirus, &us, true, NULL) && kvfi->DataOffset > 0 && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
199                 DWORD* val = (DWORD*)((uint8_t*)kvfi + kvfi->DataOffset);
200 
201                 options->no_root_dir = *val;
202             }
203         } else if (Status != STATUS_NO_MORE_ENTRIES) {
204             ERR("ZwEnumerateValueKey returned %08lx\n", Status);
205             goto end2;
206         }
207     } while (NT_SUCCESS(Status));
208 
209     if (!options->compress && options->compress_force)
210         options->compress = true;
211 
212     if (options->zlib_level > 9)
213         options->zlib_level = 9;
214 
215     if (options->zstd_level > (uint32_t)ZSTD_maxCLevel())
216         options->zstd_level = ZSTD_maxCLevel();
217 
218     if (options->flush_interval == 0)
219         options->flush_interval = mount_flush_interval;
220 
221     Status = STATUS_SUCCESS;
222 
223 end2:
224     ZwClose(h);
225 
226 end:
227     ExFreePool(path.Buffer);
228 
229     if (kvfi)
230         ExFreePool(kvfi);
231 
232     return Status;
233 }
234 
235 NTSTATUS registry_mark_volume_mounted(BTRFS_UUID* uuid) {
236     UNICODE_STRING path, mountedus;
237     ULONG i, j;
238     NTSTATUS Status;
239     OBJECT_ATTRIBUTES oa;
240     HANDLE h;
241     DWORD data;
242 
243     path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
244     path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
245 
246     if (!path.Buffer) {
247         ERR("out of memory\n");
248         return STATUS_INSUFFICIENT_RESOURCES;
249     }
250 
251     RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
252     i = registry_path.Length / sizeof(WCHAR);
253 
254     path.Buffer[i] = '\\';
255     i++;
256 
257     for (j = 0; j < 16; j++) {
258         path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
259         path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
260 
261         i += 2;
262 
263         if (j == 3 || j == 5 || j == 7 || j == 9) {
264             path.Buffer[i] = '-';
265             i++;
266         }
267     }
268 
269     InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
270 
271     Status = ZwCreateKey(&h, KEY_SET_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
272     if (!NT_SUCCESS(Status)) {
273         ERR("ZwCreateKey returned %08lx\n", Status);
274         goto end;
275     }
276 
277     mountedus.Buffer = (WCHAR*)option_mounted;
278     mountedus.Length = mountedus.MaximumLength = sizeof(option_mounted) - sizeof(WCHAR);
279 
280     data = 1;
281 
282     Status = ZwSetValueKey(h, &mountedus, 0, REG_DWORD, &data, sizeof(DWORD));
283     if (!NT_SUCCESS(Status)) {
284         ERR("ZwSetValueKey returned %08lx\n", Status);
285         goto end2;
286     }
287 
288     Status = STATUS_SUCCESS;
289 
290 end2:
291     ZwClose(h);
292 
293 end:
294     ExFreePool(path.Buffer);
295 
296     return Status;
297 }
298 
299 static NTSTATUS registry_mark_volume_unmounted_path(PUNICODE_STRING path) {
300     HANDLE h;
301     OBJECT_ATTRIBUTES oa;
302     NTSTATUS Status;
303     ULONG index, kvbilen = sizeof(KEY_VALUE_BASIC_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR)), retlen;
304     KEY_VALUE_BASIC_INFORMATION* kvbi;
305     bool has_options = false;
306     UNICODE_STRING mountedus;
307 
308     // If a volume key has any options in it, we set Mounted to 0 and return. Otherwise,
309     // we delete the whole thing.
310 
311     kvbi = ExAllocatePoolWithTag(PagedPool, kvbilen, ALLOC_TAG);
312     if (!kvbi) {
313         ERR("out of memory\n");
314         return STATUS_INSUFFICIENT_RESOURCES;
315     }
316 
317     InitializeObjectAttributes(&oa, path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
318 
319     Status = ZwOpenKey(&h, KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE, &oa);
320     if (!NT_SUCCESS(Status)) {
321         ERR("ZwOpenKey returned %08lx\n", Status);
322         goto end;
323     }
324 
325     index = 0;
326 
327     mountedus.Buffer = (WCHAR*)option_mounted;
328     mountedus.Length = mountedus.MaximumLength = sizeof(option_mounted) - sizeof(WCHAR);
329 
330     do {
331         Status = ZwEnumerateValueKey(h, index, KeyValueBasicInformation, kvbi, kvbilen, &retlen);
332 
333         index++;
334 
335         if (NT_SUCCESS(Status)) {
336             UNICODE_STRING us;
337 
338             us.Length = us.MaximumLength = (USHORT)kvbi->NameLength;
339             us.Buffer = kvbi->Name;
340 
341             if (!FsRtlAreNamesEqual(&mountedus, &us, true, NULL)) {
342                 has_options = true;
343                 break;
344             }
345         } else if (Status != STATUS_NO_MORE_ENTRIES) {
346             ERR("ZwEnumerateValueKey returned %08lx\n", Status);
347             goto end2;
348         }
349     } while (NT_SUCCESS(Status));
350 
351     if (has_options) {
352         DWORD data = 0;
353 
354         Status = ZwSetValueKey(h, &mountedus, 0, REG_DWORD, &data, sizeof(DWORD));
355         if (!NT_SUCCESS(Status)) {
356             ERR("ZwSetValueKey returned %08lx\n", Status);
357             goto end2;
358         }
359     } else {
360         Status = ZwDeleteKey(h);
361         if (!NT_SUCCESS(Status)) {
362             ERR("ZwDeleteKey returned %08lx\n", Status);
363             goto end2;
364         }
365     }
366 
367     Status = STATUS_SUCCESS;
368 
369 end2:
370     ZwClose(h);
371 
372 end:
373     ExFreePool(kvbi);
374 
375     return Status;
376 }
377 
378 NTSTATUS registry_mark_volume_unmounted(BTRFS_UUID* uuid) {
379     UNICODE_STRING path;
380     NTSTATUS Status;
381     ULONG i, j;
382 
383     path.Length = path.MaximumLength = registry_path.Length + (37 * sizeof(WCHAR));
384     path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
385 
386     if (!path.Buffer) {
387         ERR("out of memory\n");
388         return STATUS_INSUFFICIENT_RESOURCES;
389     }
390 
391     RtlCopyMemory(path.Buffer, registry_path.Buffer, registry_path.Length);
392     i = registry_path.Length / sizeof(WCHAR);
393 
394     path.Buffer[i] = '\\';
395     i++;
396 
397     for (j = 0; j < 16; j++) {
398         path.Buffer[i] = hex_digit((uuid->uuid[j] & 0xF0) >> 4);
399         path.Buffer[i+1] = hex_digit(uuid->uuid[j] & 0xF);
400 
401         i += 2;
402 
403         if (j == 3 || j == 5 || j == 7 || j == 9) {
404             path.Buffer[i] = '-';
405             i++;
406         }
407     }
408 
409     Status = registry_mark_volume_unmounted_path(&path);
410     if (!NT_SUCCESS(Status)) {
411         ERR("registry_mark_volume_unmounted_path returned %08lx\n", Status);
412         goto end;
413     }
414 
415     Status = STATUS_SUCCESS;
416 
417 end:
418     ExFreePool(path.Buffer);
419 
420     return Status;
421 }
422 
423 #define is_hex(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
424 
425 static bool is_uuid(ULONG namelen, WCHAR* name) {
426     ULONG i;
427 
428     if (namelen != 36 * sizeof(WCHAR))
429         return false;
430 
431     for (i = 0; i < 36; i++) {
432         if (i == 8 || i == 13 || i == 18 || i == 23) {
433             if (name[i] != '-')
434                 return false;
435         } else if (!is_hex(name[i]))
436             return false;
437     }
438 
439     return true;
440 }
441 
442 typedef struct {
443     UNICODE_STRING name;
444     LIST_ENTRY list_entry;
445 } key_name;
446 
447 static void reset_subkeys(HANDLE h, PUNICODE_STRING reg_path) {
448     NTSTATUS Status;
449     KEY_BASIC_INFORMATION* kbi;
450     ULONG kbilen = sizeof(KEY_BASIC_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR)), retlen, index = 0;
451     LIST_ENTRY key_names, *le;
452 
453     InitializeListHead(&key_names);
454 
455     kbi = ExAllocatePoolWithTag(PagedPool, kbilen, ALLOC_TAG);
456     if (!kbi) {
457         ERR("out of memory\n");
458         return;
459     }
460 
461     do {
462         Status = ZwEnumerateKey(h, index, KeyBasicInformation, kbi, kbilen, &retlen);
463 
464         index++;
465 
466         if (NT_SUCCESS(Status)) {
467             key_name* kn;
468 
469             TRACE("key: %.*S\n", (int)(kbi->NameLength / sizeof(WCHAR)), kbi->Name);
470 
471             if (is_uuid(kbi->NameLength, kbi->Name)) {
472                 kn = ExAllocatePoolWithTag(PagedPool, sizeof(key_name), ALLOC_TAG);
473                 if (!kn) {
474                     ERR("out of memory\n");
475                     goto end;
476                 }
477 
478                 kn->name.Length = kn->name.MaximumLength = (USHORT)min(0xffff, kbi->NameLength);
479                 kn->name.Buffer = ExAllocatePoolWithTag(PagedPool, kn->name.MaximumLength, ALLOC_TAG);
480 
481                 if (!kn->name.Buffer) {
482                     ERR("out of memory\n");
483                     ExFreePool(kn);
484                     goto end;
485                 }
486 
487                 RtlCopyMemory(kn->name.Buffer, kbi->Name, kn->name.Length);
488 
489                 InsertTailList(&key_names, &kn->list_entry);
490             }
491         } else if (Status != STATUS_NO_MORE_ENTRIES)
492             ERR("ZwEnumerateKey returned %08lx\n", Status);
493     } while (NT_SUCCESS(Status));
494 
495     le = key_names.Flink;
496     while (le != &key_names) {
497         key_name* kn = CONTAINING_RECORD(le, key_name, list_entry);
498         UNICODE_STRING path;
499 
500         path.Length = path.MaximumLength = reg_path->Length + sizeof(WCHAR) + kn->name.Length;
501         path.Buffer = ExAllocatePoolWithTag(PagedPool, path.Length, ALLOC_TAG);
502 
503         if (!path.Buffer) {
504             ERR("out of memory\n");
505             goto end;
506         }
507 
508         RtlCopyMemory(path.Buffer, reg_path->Buffer, reg_path->Length);
509         path.Buffer[reg_path->Length / sizeof(WCHAR)] = '\\';
510         RtlCopyMemory(&path.Buffer[(reg_path->Length / sizeof(WCHAR)) + 1], kn->name.Buffer, kn->name.Length);
511 
512         Status = registry_mark_volume_unmounted_path(&path);
513         if (!NT_SUCCESS(Status))
514             WARN("registry_mark_volume_unmounted_path returned %08lx\n", Status);
515 
516         ExFreePool(path.Buffer);
517 
518         le = le->Flink;
519     }
520 
521 end:
522     while (!IsListEmpty(&key_names)) {
523         key_name* kn;
524 
525         le = RemoveHeadList(&key_names);
526         kn = CONTAINING_RECORD(le, key_name, list_entry);
527 
528         if (kn->name.Buffer)
529             ExFreePool(kn->name.Buffer);
530 
531         ExFreePool(kn);
532     }
533 
534     ExFreePool(kbi);
535 }
536 
537 static void read_mappings(PUNICODE_STRING regpath) {
538     WCHAR* path;
539     UNICODE_STRING us;
540     HANDLE h;
541     OBJECT_ATTRIBUTES oa;
542     ULONG dispos;
543     NTSTATUS Status;
544 
545     static const WCHAR mappings[] = L"\\Mappings";
546 
547     while (!IsListEmpty(&uid_map_list)) {
548         uid_map* um = CONTAINING_RECORD(RemoveHeadList(&uid_map_list), uid_map, listentry);
549 
550         if (um->sid) ExFreePool(um->sid);
551         ExFreePool(um);
552     }
553 
554     path = ExAllocatePoolWithTag(PagedPool, regpath->Length + sizeof(mappings) - sizeof(WCHAR), ALLOC_TAG);
555     if (!path) {
556         ERR("out of memory\n");
557         return;
558     }
559 
560     RtlCopyMemory(path, regpath->Buffer, regpath->Length);
561     RtlCopyMemory((uint8_t*)path + regpath->Length, mappings, sizeof(mappings) - sizeof(WCHAR));
562 
563     us.Buffer = path;
564     us.Length = us.MaximumLength = regpath->Length + sizeof(mappings) - sizeof(WCHAR);
565 
566     InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
567 
568     Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
569 
570     if (!NT_SUCCESS(Status)) {
571         ERR("ZwCreateKey returned %08lx\n", Status);
572         ExFreePool(path);
573         return;
574     }
575 
576     if (dispos == REG_OPENED_EXISTING_KEY) {
577         KEY_VALUE_FULL_INFORMATION* kvfi;
578         ULONG kvfilen, retlen, i;
579 
580         kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256;
581         kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
582 
583         if (!kvfi) {
584             ERR("out of memory\n");
585             ExFreePool(path);
586             ZwClose(h);
587             return;
588         }
589 
590         i = 0;
591         do {
592             Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen);
593 
594             if (NT_SUCCESS(Status) && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
595                 uint32_t val = 0;
596 
597                 RtlCopyMemory(&val, (uint8_t*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(uint32_t)));
598 
599                 TRACE("entry %lu = %.*S = %u\n", i, (int)(kvfi->NameLength / sizeof(WCHAR)), kvfi->Name, val);
600 
601                 add_user_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val);
602             }
603 
604             i = i + 1;
605         } while (Status != STATUS_NO_MORE_ENTRIES);
606 
607         ExFreePool(kvfi);
608     }
609 
610     ZwClose(h);
611 
612     ExFreePool(path);
613 }
614 
615 static void read_group_mappings(PUNICODE_STRING regpath) {
616     WCHAR* path;
617     UNICODE_STRING us;
618     HANDLE h;
619     OBJECT_ATTRIBUTES oa;
620     ULONG dispos;
621     NTSTATUS Status;
622 
623     static const WCHAR mappings[] = L"\\GroupMappings";
624 
625     while (!IsListEmpty(&gid_map_list)) {
626         gid_map* gm = CONTAINING_RECORD(RemoveHeadList(&gid_map_list), gid_map, listentry);
627 
628         if (gm->sid) ExFreePool(gm->sid);
629         ExFreePool(gm);
630     }
631 
632     path = ExAllocatePoolWithTag(PagedPool, regpath->Length + sizeof(mappings) - sizeof(WCHAR), ALLOC_TAG);
633     if (!path) {
634         ERR("out of memory\n");
635         return;
636     }
637 
638     RtlCopyMemory(path, regpath->Buffer, regpath->Length);
639     RtlCopyMemory((uint8_t*)path + regpath->Length, mappings, sizeof(mappings) - sizeof(WCHAR));
640 
641     us.Buffer = path;
642     us.Length = us.MaximumLength = regpath->Length + sizeof(mappings) - sizeof(WCHAR);
643 
644     InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
645 
646     Status = ZwCreateKey(&h, KEY_QUERY_VALUE, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
647 
648     if (!NT_SUCCESS(Status)) {
649         ERR("ZwCreateKey returned %08lx\n", Status);
650         ExFreePool(path);
651         return;
652     }
653 
654     ExFreePool(path);
655 
656     if (dispos == REG_OPENED_EXISTING_KEY) {
657         KEY_VALUE_FULL_INFORMATION* kvfi;
658         ULONG kvfilen, retlen, i;
659 
660         kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) + 256;
661         kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
662 
663         if (!kvfi) {
664             ERR("out of memory\n");
665             ZwClose(h);
666             return;
667         }
668 
669         i = 0;
670         do {
671             Status = ZwEnumerateValueKey(h, i, KeyValueFullInformation, kvfi, kvfilen, &retlen);
672 
673             if (NT_SUCCESS(Status) && kvfi->DataLength > 0 && kvfi->Type == REG_DWORD) {
674                 uint32_t val = 0;
675 
676                 RtlCopyMemory(&val, (uint8_t*)kvfi + kvfi->DataOffset, min(kvfi->DataLength, sizeof(uint32_t)));
677 
678                 TRACE("entry %lu = %.*S = %u\n", i, (int)(kvfi->NameLength / sizeof(WCHAR)), kvfi->Name, val);
679 
680                 add_group_mapping(kvfi->Name, kvfi->NameLength / sizeof(WCHAR), val);
681             }
682 
683             i = i + 1;
684         } while (Status != STATUS_NO_MORE_ENTRIES);
685 
686         ExFreePool(kvfi);
687     } else if (dispos == REG_CREATED_NEW_KEY) {
688         static const WCHAR builtin_users[] = L"S-1-5-32-545";
689 
690         UNICODE_STRING us2;
691         DWORD val;
692 
693         // If we're creating the key for the first time, we add a default mapping of
694         // BUILTIN\Users to gid 100, which ought to correspond to the "users" group on Linux.
695 
696         us2.Length = us2.MaximumLength = sizeof(builtin_users) - sizeof(WCHAR);
697         us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.MaximumLength, ALLOC_TAG);
698 
699         if (us2.Buffer) {
700             RtlCopyMemory(us2.Buffer, builtin_users, us2.Length);
701 
702             val = 100;
703             Status = ZwSetValueKey(h, &us2, 0, REG_DWORD, &val, sizeof(DWORD));
704             if (!NT_SUCCESS(Status)) {
705                 ERR("ZwSetValueKey returned %08lx\n", Status);
706                 ZwClose(h);
707                 return;
708             }
709 
710             add_group_mapping(us2.Buffer, us2.Length / sizeof(WCHAR), val);
711 
712             ExFreePool(us2.Buffer);
713         }
714     }
715 
716     ZwClose(h);
717 }
718 
719 static void get_registry_value(HANDLE h, WCHAR* string, ULONG type, void* val, ULONG size) {
720     ULONG kvfilen;
721     KEY_VALUE_FULL_INFORMATION* kvfi;
722     UNICODE_STRING us;
723     NTSTATUS Status;
724 
725     RtlInitUnicodeString(&us, string);
726 
727     kvfi = NULL;
728     kvfilen = 0;
729     Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
730 
731     if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
732         kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
733 
734         if (!kvfi) {
735             ERR("out of memory\n");
736             ZwClose(h);
737             return;
738         }
739 
740         Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
741 
742         if (NT_SUCCESS(Status)) {
743             if (kvfi->Type == type && kvfi->DataLength >= size) {
744                 RtlCopyMemory(val, ((uint8_t*)kvfi) + kvfi->DataOffset, size);
745             } else {
746                 Status = ZwDeleteValueKey(h, &us);
747                 if (!NT_SUCCESS(Status)) {
748                     ERR("ZwDeleteValueKey returned %08lx\n", Status);
749                 }
750 
751                 Status = ZwSetValueKey(h, &us, 0, type, val, size);
752                 if (!NT_SUCCESS(Status)) {
753                     ERR("ZwSetValueKey returned %08lx\n", Status);
754                 }
755             }
756         }
757 
758         ExFreePool(kvfi);
759     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
760         Status = ZwSetValueKey(h, &us, 0, type, val, size);
761 
762         if (!NT_SUCCESS(Status)) {
763             ERR("ZwSetValueKey returned %08lx\n", Status);
764         }
765     } else {
766         ERR("ZwQueryValueKey returned %08lx\n", Status);
767     }
768 }
769 
770 void read_registry(PUNICODE_STRING regpath, bool refresh) {
771     OBJECT_ATTRIBUTES oa;
772     NTSTATUS Status;
773     HANDLE h;
774     ULONG dispos;
775 #ifdef _DEBUG
776     KEY_VALUE_FULL_INFORMATION* kvfi;
777     ULONG kvfilen, old_debug_log_level = debug_log_level;
778     UNICODE_STRING us, old_log_file, old_log_device;
779 
780     static const WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log";
781 #endif
782 
783     ExAcquireResourceExclusiveLite(&mapping_lock, true);
784 
785     read_mappings(regpath);
786     read_group_mappings(regpath);
787 
788     ExReleaseResourceLite(&mapping_lock);
789 
790     InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
791 
792     Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos);
793 
794     if (!NT_SUCCESS(Status)) {
795         ERR("ZwCreateKey returned %08lx\n", Status);
796         return;
797     }
798 
799     if (!refresh)
800         reset_subkeys(h, regpath);
801 
802     get_registry_value(h, L"Compress", REG_DWORD, &mount_compress, sizeof(mount_compress));
803     get_registry_value(h, L"CompressForce", REG_DWORD, &mount_compress_force, sizeof(mount_compress_force));
804     get_registry_value(h, L"CompressType", REG_DWORD, &mount_compress_type, sizeof(mount_compress_type));
805     get_registry_value(h, L"ZlibLevel", REG_DWORD, &mount_zlib_level, sizeof(mount_zlib_level));
806     get_registry_value(h, L"FlushInterval", REG_DWORD, &mount_flush_interval, sizeof(mount_flush_interval));
807     get_registry_value(h, L"MaxInline", REG_DWORD, &mount_max_inline, sizeof(mount_max_inline));
808     get_registry_value(h, L"SkipBalance", REG_DWORD, &mount_skip_balance, sizeof(mount_skip_balance));
809     get_registry_value(h, L"NoBarrier", REG_DWORD, &mount_no_barrier, sizeof(mount_no_barrier));
810     get_registry_value(h, L"NoTrim", REG_DWORD, &mount_no_trim, sizeof(mount_no_trim));
811     get_registry_value(h, L"ClearCache", REG_DWORD, &mount_clear_cache, sizeof(mount_clear_cache));
812     get_registry_value(h, L"AllowDegraded", REG_DWORD, &mount_allow_degraded, sizeof(mount_allow_degraded));
813     get_registry_value(h, L"Readonly", REG_DWORD, &mount_readonly, sizeof(mount_readonly));
814     get_registry_value(h, L"ZstdLevel", REG_DWORD, &mount_zstd_level, sizeof(mount_zstd_level));
815     get_registry_value(h, L"NoRootDir", REG_DWORD, &mount_no_root_dir, sizeof(mount_no_root_dir));
816 
817     if (!refresh)
818         get_registry_value(h, L"NoPNP", REG_DWORD, &no_pnp, sizeof(no_pnp));
819 
820     if (mount_flush_interval == 0)
821         mount_flush_interval = 1;
822 
823 #ifdef _DEBUG
824     get_registry_value(h, L"DebugLogLevel", REG_DWORD, &debug_log_level, sizeof(debug_log_level));
825 
826     RtlInitUnicodeString(&us, L"LogDevice");
827 
828     kvfi = NULL;
829     kvfilen = 0;
830     Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
831 
832     old_log_device = log_device;
833 
834     log_device.Length = log_device.MaximumLength = 0;
835     log_device.Buffer = NULL;
836 
837     if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
838         kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
839 
840         if (!kvfi) {
841             ERR("out of memory\n");
842             ZwClose(h);
843             return;
844         }
845 
846         Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
847 
848         if (NT_SUCCESS(Status)) {
849             if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) {
850                 log_device.Length = log_device.MaximumLength = (USHORT)min(0xffff, kvfi->DataLength);
851                 log_device.Buffer = ExAllocatePoolWithTag(PagedPool, log_device.MaximumLength, ALLOC_TAG);
852 
853                 if (!log_device.Buffer) {
854                     ERR("out of memory\n");
855                     ExFreePool(kvfi);
856                     ZwClose(h);
857                     return;
858                 }
859 
860                 RtlCopyMemory(log_device.Buffer, ((uint8_t*)kvfi) + kvfi->DataOffset, log_device.Length);
861 
862                 if (log_device.Buffer[(log_device.Length / sizeof(WCHAR)) - 1] == 0)
863                     log_device.Length -= sizeof(WCHAR);
864             } else {
865                 ERR("LogDevice was type %lu, length %lu\n", kvfi->Type, kvfi->DataLength);
866 
867                 Status = ZwDeleteValueKey(h, &us);
868                 if (!NT_SUCCESS(Status)) {
869                     ERR("ZwDeleteValueKey returned %08lx\n", Status);
870                 }
871             }
872         }
873 
874         ExFreePool(kvfi);
875     } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
876         ERR("ZwQueryValueKey returned %08lx\n", Status);
877     }
878 
879     ExAcquireResourceExclusiveLite(&log_lock, true);
880 
881     if (refresh && (log_device.Length != old_log_device.Length || RtlCompareMemory(log_device.Buffer, old_log_device.Buffer, log_device.Length) != log_device.Length ||
882         (!comfo && log_device.Length > 0) || (old_debug_log_level == 0 && debug_log_level > 0) || (old_debug_log_level > 0 && debug_log_level == 0))) {
883         if (comfo)
884             ObDereferenceObject(comfo);
885 
886         if (log_handle) {
887             ZwClose(log_handle);
888             log_handle = NULL;
889         }
890 
891         comfo = NULL;
892         comdo = NULL;
893 
894         if (log_device.Length > 0 && debug_log_level > 0) {
895             Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo);
896             if (!NT_SUCCESS(Status))
897                 DbgPrint("IoGetDeviceObjectPointer returned %08lx\n", Status);
898         }
899     }
900 
901     ExReleaseResourceLite(&log_lock);
902 
903     if (old_log_device.Buffer)
904         ExFreePool(old_log_device.Buffer);
905 
906     RtlInitUnicodeString(&us, L"LogFile");
907 
908     kvfi = NULL;
909     kvfilen = 0;
910     Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
911 
912     old_log_file = log_file;
913 
914     if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) {
915         kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG);
916 
917         if (!kvfi) {
918             ERR("out of memory\n");
919             ZwClose(h);
920             return;
921         }
922 
923         Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen);
924 
925         if (NT_SUCCESS(Status)) {
926             if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) {
927                 log_file.Length = log_file.MaximumLength = (USHORT)min(0xffff, kvfi->DataLength);
928                 log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG);
929 
930                 if (!log_file.Buffer) {
931                     ERR("out of memory\n");
932                     ExFreePool(kvfi);
933                     ZwClose(h);
934                     return;
935                 }
936 
937                 RtlCopyMemory(log_file.Buffer, ((uint8_t*)kvfi) + kvfi->DataOffset, log_file.Length);
938 
939                 if (log_file.Buffer[(log_file.Length / sizeof(WCHAR)) - 1] == 0)
940                     log_file.Length -= sizeof(WCHAR);
941             } else {
942                 ERR("LogFile was type %lu, length %lu\n", kvfi->Type, kvfi->DataLength);
943 
944                 Status = ZwDeleteValueKey(h, &us);
945                 if (!NT_SUCCESS(Status))
946                     ERR("ZwDeleteValueKey returned %08lx\n", Status);
947 
948                 log_file.Length = 0;
949             }
950         } else {
951             ERR("ZwQueryValueKey returned %08lx\n", Status);
952             log_file.Length = 0;
953         }
954 
955         ExFreePool(kvfi);
956     } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
957         Status = ZwSetValueKey(h, &us, 0, REG_SZ, (void*)def_log_file, sizeof(def_log_file));
958 
959         if (!NT_SUCCESS(Status))
960             ERR("ZwSetValueKey returned %08lx\n", Status);
961 
962         log_file.Length = 0;
963     } else {
964         ERR("ZwQueryValueKey returned %08lx\n", Status);
965         log_file.Length = 0;
966     }
967 
968     if (log_file.Length == 0) {
969         log_file.Length = log_file.MaximumLength = sizeof(def_log_file) - sizeof(WCHAR);
970         log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG);
971 
972         if (!log_file.Buffer) {
973             ERR("out of memory\n");
974             ZwClose(h);
975             return;
976         }
977 
978         RtlCopyMemory(log_file.Buffer, def_log_file, log_file.Length);
979     }
980 
981     ExAcquireResourceExclusiveLite(&log_lock, true);
982 
983     if (refresh && (log_file.Length != old_log_file.Length || RtlCompareMemory(log_file.Buffer, old_log_file.Buffer, log_file.Length) != log_file.Length ||
984         (!log_handle && log_file.Length > 0) || (old_debug_log_level == 0 && debug_log_level > 0) || (old_debug_log_level > 0 && debug_log_level == 0))) {
985         if (log_handle) {
986             ZwClose(log_handle);
987             log_handle = NULL;
988         }
989 
990         if (!comfo && log_file.Length > 0 && refresh && debug_log_level > 0) {
991             IO_STATUS_BLOCK iosb;
992 
993             InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
994 
995             Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
996                                   FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0);
997             if (!NT_SUCCESS(Status)) {
998                 DbgPrint("ZwCreateFile returned %08lx\n", Status);
999                 log_handle = NULL;
1000             }
1001         }
1002     }
1003 
1004     ExReleaseResourceLite(&log_lock);
1005 
1006     if (old_log_file.Buffer)
1007         ExFreePool(old_log_file.Buffer);
1008 #endif
1009 
1010     ZwClose(h);
1011 }
1012 
1013 _Function_class_(WORKER_THREAD_ROUTINE)
1014 static void __stdcall registry_work_item(PVOID Parameter) {
1015     NTSTATUS Status;
1016     HANDLE regh = (HANDLE)Parameter;
1017     IO_STATUS_BLOCK iosb;
1018 
1019     TRACE("registry changed\n");
1020 
1021     read_registry(&registry_path, true);
1022 
1023     Status = ZwNotifyChangeKey(regh, NULL, (PVOID)&wqi, (PVOID)DelayedWorkQueue, &iosb, REG_NOTIFY_CHANGE_LAST_SET, true, NULL, 0, true);
1024     if (!NT_SUCCESS(Status))
1025         ERR("ZwNotifyChangeKey returned %08lx\n", Status);
1026 }
1027 
1028 void watch_registry(HANDLE regh) {
1029     NTSTATUS Status;
1030     IO_STATUS_BLOCK iosb;
1031 
1032     ExInitializeWorkItem(&wqi, registry_work_item, regh);
1033 
1034     Status = ZwNotifyChangeKey(regh, NULL, (PVOID)&wqi, (PVOID)DelayedWorkQueue, &iosb, REG_NOTIFY_CHANGE_LAST_SET, true, NULL, 0, true);
1035     if (!NT_SUCCESS(Status))
1036         ERR("ZwNotifyChangeKey returned %08lx\n", Status);
1037 }
1038