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