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