1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
18 #include "btrfs_drv.h"
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
;
25 extern HANDLE log_handle
;
26 extern ERESOURCE log_lock
;
27 extern PFILE_OBJECT comfo
;
28 extern PDEVICE_OBJECT comdo
;
33 static WCHAR option_mounted
[] = L
"Mounted";
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
;
42 ULONG i
, j
, kvfilen
, index
, retlen
;
43 KEY_VALUE_FULL_INFORMATION
* kvfi
= NULL
;
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;
60 path
.Length
= path
.MaximumLength
= registry_path
.Length
+ (37 * sizeof(WCHAR
));
61 path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, path
.Length
, ALLOC_TAG
);
64 ERR("out of memory\n");
65 return STATUS_INSUFFICIENT_RESOURCES
;
68 RtlCopyMemory(path
.Buffer
, registry_path
.Buffer
, registry_path
.Length
);
69 i
= registry_path
.Length
/ sizeof(WCHAR
);
71 path
.Buffer
[i
] = '\\';
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);
80 if (j
== 3 || j
== 5 || j
== 7 || j
== 9) {
86 kvfilen
= sizeof(KEY_VALUE_FULL_INFORMATION
) - sizeof(WCHAR
) + (255 * sizeof(WCHAR
));
87 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
89 ERR("out of memory\n");
90 Status
= STATUS_INSUFFICIENT_RESOURCES
;
94 InitializeObjectAttributes(&oa
, &path
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
96 Status
= ZwOpenKey(&h
, KEY_QUERY_VALUE
, &oa
);
97 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
98 Status
= STATUS_SUCCESS
;
100 } else if (!NT_SUCCESS(Status
)) {
101 ERR("ZwOpenKey returned %08x\n", Status
);
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(¬rimus
, L
"NoTrim");
119 RtlInitUnicodeString(&clearcacheus
, L
"ClearCache");
120 RtlInitUnicodeString(&allowdegradedus
, L
"AllowDegraded");
123 Status
= ZwEnumerateValueKey(h
, index
, KeyValueFullInformation
, kvfi
, kvfilen
, &retlen
);
127 if (NT_SUCCESS(Status
)) {
130 us
.Length
= us
.MaximumLength
= (USHORT
)kvfi
->NameLength
;
131 us
.Buffer
= kvfi
->Name
;
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
176 options
->no_barrier
= *val
;
177 } else if (FsRtlAreNamesEqual(¬rimus
, &us
, TRUE
, NULL
) && kvfi
->DataOffset
> 0 && kvfi
->DataLength
> 0 && kvfi
->Type
== REG_DWORD
) {
178 DWORD
* val
= (DWORD
*)((UINT8
*)kvfi
+ kvfi
->DataOffset
);
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
);
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
);
188 options
->allow_degraded
= *val
;
190 } else if (Status
!= STATUS_NO_MORE_ENTRIES
) {
191 ERR("ZwEnumerateValueKey returned %08x\n", Status
);
194 } while (NT_SUCCESS(Status
));
196 if (!options
->compress
&& options
->compress_force
)
197 options
->compress
= TRUE
;
199 if (options
->zlib_level
> 9)
200 options
->zlib_level
= 9;
202 if (options
->flush_interval
== 0)
203 options
->flush_interval
= mount_flush_interval
;
205 Status
= STATUS_SUCCESS
;
211 ExFreePool(path
.Buffer
);
219 NTSTATUS
registry_mark_volume_mounted(BTRFS_UUID
* uuid
) {
220 UNICODE_STRING path
, mountedus
;
223 OBJECT_ATTRIBUTES oa
;
227 path
.Length
= path
.MaximumLength
= registry_path
.Length
+ (37 * sizeof(WCHAR
));
228 path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, path
.Length
, ALLOC_TAG
);
231 ERR("out of memory\n");
232 return STATUS_INSUFFICIENT_RESOURCES
;
235 RtlCopyMemory(path
.Buffer
, registry_path
.Buffer
, registry_path
.Length
);
236 i
= registry_path
.Length
/ sizeof(WCHAR
);
238 path
.Buffer
[i
] = '\\';
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);
247 if (j
== 3 || j
== 5 || j
== 7 || j
== 9) {
248 path
.Buffer
[i
] = '-';
253 InitializeObjectAttributes(&oa
, &path
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
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
);
261 mountedus
.Buffer
= option_mounted
;
262 mountedus
.Length
= mountedus
.MaximumLength
= (USHORT
)wcslen(option_mounted
) * sizeof(WCHAR
);
266 Status
= ZwSetValueKey(h
, &mountedus
, 0, REG_DWORD
, &data
, sizeof(DWORD
));
267 if (!NT_SUCCESS(Status
)) {
268 ERR("ZwSetValueKey returned %08x\n", Status
);
272 Status
= STATUS_SUCCESS
;
278 ExFreePool(path
.Buffer
);
283 static NTSTATUS
registry_mark_volume_unmounted_path(PUNICODE_STRING path
) {
285 OBJECT_ATTRIBUTES oa
;
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
;
292 // If a volume key has any options in it, we set Mounted to 0 and return. Otherwise,
293 // we delete the whole thing.
295 kvbi
= ExAllocatePoolWithTag(PagedPool
, kvbilen
, ALLOC_TAG
);
297 ERR("out of memory\n");
298 return STATUS_INSUFFICIENT_RESOURCES
;
301 InitializeObjectAttributes(&oa
, path
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
303 Status
= ZwOpenKey(&h
, KEY_QUERY_VALUE
| KEY_SET_VALUE
| DELETE
, &oa
);
304 if (!NT_SUCCESS(Status
)) {
305 ERR("ZwOpenKey returned %08x\n", Status
);
311 mountedus
.Buffer
= option_mounted
;
312 mountedus
.Length
= mountedus
.MaximumLength
= (USHORT
)wcslen(option_mounted
) * sizeof(WCHAR
);
315 Status
= ZwEnumerateValueKey(h
, index
, KeyValueBasicInformation
, kvbi
, kvbilen
, &retlen
);
319 if (NT_SUCCESS(Status
)) {
322 us
.Length
= us
.MaximumLength
= (USHORT
)kvbi
->NameLength
;
323 us
.Buffer
= kvbi
->Name
;
325 if (!FsRtlAreNamesEqual(&mountedus
, &us
, TRUE
, NULL
)) {
329 } else if (Status
!= STATUS_NO_MORE_ENTRIES
) {
330 ERR("ZwEnumerateValueKey returned %08x\n", Status
);
333 } while (NT_SUCCESS(Status
));
338 Status
= ZwSetValueKey(h
, &mountedus
, 0, REG_DWORD
, &data
, sizeof(DWORD
));
339 if (!NT_SUCCESS(Status
)) {
340 ERR("ZwSetValueKey returned %08x\n", Status
);
344 Status
= ZwDeleteKey(h
);
345 if (!NT_SUCCESS(Status
)) {
346 ERR("ZwDeleteKey returned %08x\n", Status
);
351 Status
= STATUS_SUCCESS
;
362 NTSTATUS
registry_mark_volume_unmounted(BTRFS_UUID
* uuid
) {
367 path
.Length
= path
.MaximumLength
= registry_path
.Length
+ (37 * sizeof(WCHAR
));
368 path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, path
.Length
, ALLOC_TAG
);
371 ERR("out of memory\n");
372 return STATUS_INSUFFICIENT_RESOURCES
;
375 RtlCopyMemory(path
.Buffer
, registry_path
.Buffer
, registry_path
.Length
);
376 i
= registry_path
.Length
/ sizeof(WCHAR
);
378 path
.Buffer
[i
] = '\\';
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);
387 if (j
== 3 || j
== 5 || j
== 7 || j
== 9) {
388 path
.Buffer
[i
] = '-';
393 Status
= registry_mark_volume_unmounted_path(&path
);
394 if (!NT_SUCCESS(Status
)) {
395 ERR("registry_mark_volume_unmounted_path returned %08x\n", Status
);
399 Status
= STATUS_SUCCESS
;
402 ExFreePool(path
.Buffer
);
407 #define is_hex(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
409 static BOOL
is_uuid(ULONG namelen
, WCHAR
* name
) {
412 if (namelen
!= 36 * sizeof(WCHAR
))
415 for (i
= 0; i
< 36; i
++) {
416 if (i
== 8 || i
== 13 || i
== 18 || i
== 23) {
419 } else if (!is_hex(name
[i
]))
428 LIST_ENTRY list_entry
;
431 static void reset_subkeys(HANDLE h
, PUNICODE_STRING reg_path
) {
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
;
437 InitializeListHead(&key_names
);
439 kbi
= ExAllocatePoolWithTag(PagedPool
, kbilen
, ALLOC_TAG
);
441 ERR("out of memory\n");
446 Status
= ZwEnumerateKey(h
, index
, KeyBasicInformation
, kbi
, kbilen
, &retlen
);
450 if (NT_SUCCESS(Status
)) {
453 TRACE("key: %.*S\n", kbi
->NameLength
/ sizeof(WCHAR
), kbi
->Name
);
455 if (is_uuid(kbi
->NameLength
, kbi
->Name
)) {
456 kn
= ExAllocatePoolWithTag(PagedPool
, sizeof(key_name
), ALLOC_TAG
);
458 ERR("out of memory\n");
462 kn
->name
.Length
= kn
->name
.MaximumLength
= (USHORT
)min(0xffff, kbi
->NameLength
);
463 kn
->name
.Buffer
= ExAllocatePoolWithTag(PagedPool
, kn
->name
.MaximumLength
, ALLOC_TAG
);
465 if (!kn
->name
.Buffer
) {
466 ERR("out of memory\n");
471 RtlCopyMemory(kn
->name
.Buffer
, kbi
->Name
, kn
->name
.Length
);
473 InsertTailList(&key_names
, &kn
->list_entry
);
475 } else if (Status
!= STATUS_NO_MORE_ENTRIES
)
476 ERR("ZwEnumerateKey returned %08x\n", Status
);
477 } while (NT_SUCCESS(Status
));
479 le
= key_names
.Flink
;
480 while (le
!= &key_names
) {
481 key_name
* kn
= CONTAINING_RECORD(le
, key_name
, list_entry
);
484 path
.Length
= path
.MaximumLength
= reg_path
->Length
+ sizeof(WCHAR
) + kn
->name
.Length
;
485 path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, path
.Length
, ALLOC_TAG
);
488 ERR("out of memory\n");
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
);
496 Status
= registry_mark_volume_unmounted_path(&path
);
497 if (!NT_SUCCESS(Status
))
498 WARN("registry_mark_volume_unmounted_path returned %08x\n", Status
);
500 ExFreePool(path
.Buffer
);
506 while (!IsListEmpty(&key_names
)) {
509 le
= RemoveHeadList(&key_names
);
510 kn
= CONTAINING_RECORD(le
, key_name
, list_entry
);
513 ExFreePool(kn
->name
.Buffer
);
521 static void read_mappings(PUNICODE_STRING regpath
) {
525 OBJECT_ATTRIBUTES oa
;
529 const WCHAR mappings
[] = L
"\\Mappings";
531 while (!IsListEmpty(&uid_map_list
)) {
532 uid_map
* um
= CONTAINING_RECORD(RemoveHeadList(&uid_map_list
), uid_map
, listentry
);
534 if (um
->sid
) ExFreePool(um
->sid
);
538 path
= ExAllocatePoolWithTag(PagedPool
, regpath
->Length
+ (wcslen(mappings
) * sizeof(WCHAR
)), ALLOC_TAG
);
540 ERR("out of memory\n");
544 RtlCopyMemory(path
, regpath
->Buffer
, regpath
->Length
);
545 RtlCopyMemory((UINT8
*)path
+ regpath
->Length
, mappings
, wcslen(mappings
) * sizeof(WCHAR
));
548 us
.Length
= us
.MaximumLength
= regpath
->Length
+ ((USHORT
)wcslen(mappings
) * sizeof(WCHAR
));
550 InitializeObjectAttributes(&oa
, &us
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
552 Status
= ZwCreateKey(&h
, KEY_QUERY_VALUE
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
554 if (!NT_SUCCESS(Status
)) {
555 ERR("ZwCreateKey returned %08x\n", Status
);
560 if (dispos
== REG_OPENED_EXISTING_KEY
) {
561 KEY_VALUE_FULL_INFORMATION
* kvfi
;
562 ULONG kvfilen
, retlen
, i
;
564 kvfilen
= sizeof(KEY_VALUE_FULL_INFORMATION
) + 256;
565 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
568 ERR("out of memory\n");
576 Status
= ZwEnumerateValueKey(h
, i
, KeyValueFullInformation
, kvfi
, kvfilen
, &retlen
);
578 if (NT_SUCCESS(Status
) && kvfi
->DataLength
> 0 && kvfi
->Type
== REG_DWORD
) {
581 RtlCopyMemory(&val
, (UINT8
*)kvfi
+ kvfi
->DataOffset
, min(kvfi
->DataLength
, sizeof(UINT32
)));
583 TRACE("entry %u = %.*S = %u\n", i
, kvfi
->NameLength
/ sizeof(WCHAR
), kvfi
->Name
, val
);
585 add_user_mapping(kvfi
->Name
, kvfi
->NameLength
/ sizeof(WCHAR
), val
);
589 } while (Status
!= STATUS_NO_MORE_ENTRIES
);
599 static void read_group_mappings(PUNICODE_STRING regpath
) {
603 OBJECT_ATTRIBUTES oa
;
607 const WCHAR mappings
[] = L
"\\GroupMappings";
609 while (!IsListEmpty(&gid_map_list
)) {
610 gid_map
* gm
= CONTAINING_RECORD(RemoveHeadList(&gid_map_list
), gid_map
, listentry
);
612 if (gm
->sid
) ExFreePool(gm
->sid
);
616 path
= ExAllocatePoolWithTag(PagedPool
, regpath
->Length
+ (wcslen(mappings
) * sizeof(WCHAR
)), ALLOC_TAG
);
618 ERR("out of memory\n");
622 RtlCopyMemory(path
, regpath
->Buffer
, regpath
->Length
);
623 RtlCopyMemory((UINT8
*)path
+ regpath
->Length
, mappings
, wcslen(mappings
) * sizeof(WCHAR
));
626 us
.Length
= us
.MaximumLength
= regpath
->Length
+ ((USHORT
)wcslen(mappings
) * sizeof(WCHAR
));
628 InitializeObjectAttributes(&oa
, &us
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
630 Status
= ZwCreateKey(&h
, KEY_QUERY_VALUE
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
632 if (!NT_SUCCESS(Status
)) {
633 ERR("ZwCreateKey returned %08x\n", Status
);
640 if (dispos
== REG_OPENED_EXISTING_KEY
) {
641 KEY_VALUE_FULL_INFORMATION
* kvfi
;
642 ULONG kvfilen
, retlen
, i
;
644 kvfilen
= sizeof(KEY_VALUE_FULL_INFORMATION
) + 256;
645 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
648 ERR("out of memory\n");
655 Status
= ZwEnumerateValueKey(h
, i
, KeyValueFullInformation
, kvfi
, kvfilen
, &retlen
);
657 if (NT_SUCCESS(Status
) && kvfi
->DataLength
> 0 && kvfi
->Type
== REG_DWORD
) {
660 RtlCopyMemory(&val
, (UINT8
*)kvfi
+ kvfi
->DataOffset
, min(kvfi
->DataLength
, sizeof(UINT32
)));
662 TRACE("entry %u = %.*S = %u\n", i
, kvfi
->NameLength
/ sizeof(WCHAR
), kvfi
->Name
, val
);
664 add_group_mapping(kvfi
->Name
, kvfi
->NameLength
/ sizeof(WCHAR
), val
);
668 } while (Status
!= STATUS_NO_MORE_ENTRIES
);
671 } else if (dispos
== REG_CREATED_NEW_KEY
) {
672 WCHAR
* builtin_users
= L
"S-1-5-32-545";
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.
679 us2
.Length
= us2
.MaximumLength
= (USHORT
)wcslen(builtin_users
) * sizeof(WCHAR
);
680 us2
.Buffer
= ExAllocatePoolWithTag(PagedPool
, us2
.MaximumLength
, ALLOC_TAG
);
683 RtlCopyMemory(us2
.Buffer
, builtin_users
, us2
.Length
);
686 Status
= ZwSetValueKey(h
, &us2
, 0, REG_DWORD
, &val
, sizeof(DWORD
));
687 if (!NT_SUCCESS(Status
)) {
688 ERR("ZwSetValueKey returned %08x\n", Status
);
693 add_group_mapping(us2
.Buffer
, us2
.Length
/ sizeof(WCHAR
), val
);
695 ExFreePool(us2
.Buffer
);
702 static void get_registry_value(HANDLE h
, WCHAR
* string
, ULONG type
, void* val
, ULONG size
) {
704 KEY_VALUE_FULL_INFORMATION
* kvfi
;
708 RtlInitUnicodeString(&us
, string
);
712 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
714 if ((Status
== STATUS_BUFFER_TOO_SMALL
|| Status
== STATUS_BUFFER_OVERFLOW
) && kvfilen
> 0) {
715 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
718 ERR("out of memory\n");
723 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
725 if (NT_SUCCESS(Status
)) {
726 if (kvfi
->Type
== type
&& kvfi
->DataLength
>= size
) {
727 RtlCopyMemory(val
, ((UINT8
*)kvfi
) + kvfi
->DataOffset
, size
);
729 Status
= ZwDeleteValueKey(h
, &us
);
730 if (!NT_SUCCESS(Status
)) {
731 ERR("ZwDeleteValueKey returned %08x\n", Status
);
734 Status
= ZwSetValueKey(h
, &us
, 0, type
, val
, size
);
735 if (!NT_SUCCESS(Status
)) {
736 ERR("ZwSetValueKey returned %08x\n", Status
);
742 } else if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
743 Status
= ZwSetValueKey(h
, &us
, 0, type
, val
, size
);
745 if (!NT_SUCCESS(Status
)) {
746 ERR("ZwSetValueKey returned %08x\n", Status
);
749 ERR("ZwQueryValueKey returned %08x\n", Status
);
753 void read_registry(PUNICODE_STRING regpath
, BOOL refresh
) {
754 OBJECT_ATTRIBUTES oa
;
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
;
763 static WCHAR def_log_file
[] = L
"\\??\\C:\\btrfs.log";
766 ExAcquireResourceExclusiveLite(&mapping_lock
, TRUE
);
768 read_mappings(regpath
);
769 read_group_mappings(regpath
);
771 ExReleaseResourceLite(&mapping_lock
);
773 InitializeObjectAttributes(&oa
, regpath
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
775 Status
= ZwCreateKey(&h
, KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
777 if (!NT_SUCCESS(Status
)) {
778 ERR("ZwCreateKey returned %08x\n", Status
);
783 reset_subkeys(h
, regpath
);
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
));
799 get_registry_value(h
, L
"NoPNP", REG_DWORD
, &no_pnp
, sizeof(no_pnp
));
801 if (mount_flush_interval
== 0)
802 mount_flush_interval
= 1;
805 get_registry_value(h
, L
"DebugLogLevel", REG_DWORD
, &debug_log_level
, sizeof(debug_log_level
));
807 RtlInitUnicodeString(&us
, L
"LogDevice");
811 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
813 old_log_device
= log_device
;
815 log_device
.Length
= log_device
.MaximumLength
= 0;
816 log_device
.Buffer
= NULL
;
818 if ((Status
== STATUS_BUFFER_TOO_SMALL
|| Status
== STATUS_BUFFER_OVERFLOW
) && kvfilen
> 0) {
819 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
822 ERR("out of memory\n");
827 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
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
);
834 if (!log_device
.Buffer
) {
835 ERR("out of memory\n");
841 RtlCopyMemory(log_device
.Buffer
, ((UINT8
*)kvfi
) + kvfi
->DataOffset
, log_device
.Length
);
843 if (log_device
.Buffer
[(log_device
.Length
/ sizeof(WCHAR
)) - 1] == 0)
844 log_device
.Length
-= sizeof(WCHAR
);
846 ERR("LogDevice was type %u, length %u\n", kvfi
->Type
, kvfi
->DataLength
);
848 Status
= ZwDeleteValueKey(h
, &us
);
849 if (!NT_SUCCESS(Status
)) {
850 ERR("ZwDeleteValueKey returned %08x\n", Status
);
856 } else if (Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
857 ERR("ZwQueryValueKey returned %08x\n", Status
);
860 ExAcquireResourceExclusiveLite(&log_lock
, TRUE
);
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))) {
865 ObDereferenceObject(comfo
);
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
);
882 ExReleaseResourceLite(&log_lock
);
884 if (old_log_device
.Buffer
)
885 ExFreePool(old_log_device
.Buffer
);
887 RtlInitUnicodeString(&us
, L
"LogFile");
891 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
893 old_log_file
= log_file
;
895 if ((Status
== STATUS_BUFFER_TOO_SMALL
|| Status
== STATUS_BUFFER_OVERFLOW
) && kvfilen
> 0) {
896 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
899 ERR("out of memory\n");
904 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
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
);
911 if (!log_file
.Buffer
) {
912 ERR("out of memory\n");
918 RtlCopyMemory(log_file
.Buffer
, ((UINT8
*)kvfi
) + kvfi
->DataOffset
, log_file
.Length
);
920 if (log_file
.Buffer
[(log_file
.Length
/ sizeof(WCHAR
)) - 1] == 0)
921 log_file
.Length
-= sizeof(WCHAR
);
923 ERR("LogFile was type %u, length %u\n", kvfi
->Type
, kvfi
->DataLength
);
925 Status
= ZwDeleteValueKey(h
, &us
);
926 if (!NT_SUCCESS(Status
)) {
927 ERR("ZwDeleteValueKey returned %08x\n", Status
);
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
));
936 if (!NT_SUCCESS(Status
)) {
937 ERR("ZwSetValueKey returned %08x\n", Status
);
940 ERR("ZwQueryValueKey returned %08x\n", Status
);
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
);
947 if (!log_file
.Buffer
) {
948 ERR("out of memory\n");
953 RtlCopyMemory(log_file
.Buffer
, def_log_file
, log_file
.Length
);
956 ExAcquireResourceExclusiveLite(&log_lock
, TRUE
);
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))) {
965 if (!comfo
&& log_file
.Length
> 0 && refresh
&& debug_log_level
> 0) {
966 IO_STATUS_BLOCK iosb
;
968 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
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
);
979 ExReleaseResourceLite(&log_lock
);
981 if (old_log_file
.Buffer
)
982 ExFreePool(old_log_file
.Buffer
);
988 _Function_class_(WORKER_THREAD_ROUTINE
)
990 static void NTAPI
registry_work_item(PVOID Parameter
) {
992 static void registry_work_item(PVOID Parameter
) {
995 HANDLE regh
= (HANDLE
)Parameter
;
996 IO_STATUS_BLOCK iosb
;
998 TRACE("registry changed\n");
1000 read_registry(®istry_path
, TRUE
);
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
);
1007 void watch_registry(HANDLE regh
) {
1009 IO_STATUS_BLOCK iosb
;
1011 ExInitializeWorkItem(&wqi
, registry_work_item
, regh
);
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
);