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