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