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(¬rimus, 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(¬rimus, &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(®istry_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