1 /* Copyright (c) Mark Harmstone 2016-17 2 * 3 * This file is part of WinBtrfs. 4 * 5 * WinBtrfs is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public Licence as published by 7 * the Free Software Foundation, either version 3 of the Licence, or 8 * (at your option) any later version. 9 * 10 * WinBtrfs is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public Licence for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public Licence 16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include "btrfs_drv.h" 19 #include "zstd/zstd.h" 20 21 extern UNICODE_STRING log_device, log_file, registry_path; 22 extern LIST_ENTRY uid_map_list, gid_map_list; 23 extern ERESOURCE mapping_lock; 24 25 #ifdef _DEBUG 26 extern HANDLE log_handle; 27 extern ERESOURCE log_lock; 28 extern PFILE_OBJECT comfo; 29 extern PDEVICE_OBJECT comdo; 30 #endif 31 32 WORK_QUEUE_ITEM wqi; 33 34 static const WCHAR option_mounted[] = L"Mounted"; 35 36 NTSTATUS registry_load_volume_options(device_extension* Vcb) { 37 BTRFS_UUID* uuid = &Vcb->superblock.uuid; 38 mount_options* options = &Vcb->options; 39 UNICODE_STRING path, ignoreus, compressus, compressforceus, compresstypeus, readonlyus, zliblevelus, flushintervalus, 40 maxinlineus, subvolidus, skipbalanceus, nobarrierus, notrimus, clearcacheus, allowdegradedus, zstdlevelus, 41 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 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 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 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 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 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 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 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 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 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 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 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