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 %08x\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 %08x\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 %08x\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 %08x\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 %08x\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 %08x\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 %08x\n", Status); 357 goto end2; 358 } 359 } else { 360 Status = ZwDeleteKey(h); 361 if (!NT_SUCCESS(Status)) { 362 ERR("ZwDeleteKey returned %08x\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 %08x\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", 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 %08x\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 %08x\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 %08x\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 %u = %.*S = %u\n", i, 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 %08x\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 %u = %.*S = %u\n", i, 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 WCHAR* builtin_users = L"S-1-5-32-545"; 689 UNICODE_STRING us2; 690 DWORD val; 691 692 // If we're creating the key for the first time, we add a default mapping of 693 // BUILTIN\Users to gid 100, which ought to correspond to the "users" group on Linux. 694 695 us2.Length = us2.MaximumLength = sizeof(builtin_users) - sizeof(WCHAR); 696 us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.MaximumLength, ALLOC_TAG); 697 698 if (us2.Buffer) { 699 RtlCopyMemory(us2.Buffer, builtin_users, us2.Length); 700 701 val = 100; 702 Status = ZwSetValueKey(h, &us2, 0, REG_DWORD, &val, sizeof(DWORD)); 703 if (!NT_SUCCESS(Status)) { 704 ERR("ZwSetValueKey returned %08x\n", Status); 705 ZwClose(h); 706 return; 707 } 708 709 add_group_mapping(us2.Buffer, us2.Length / sizeof(WCHAR), val); 710 711 ExFreePool(us2.Buffer); 712 } 713 } 714 715 ZwClose(h); 716 } 717 718 static void get_registry_value(HANDLE h, WCHAR* string, ULONG type, void* val, ULONG size) { 719 ULONG kvfilen; 720 KEY_VALUE_FULL_INFORMATION* kvfi; 721 UNICODE_STRING us; 722 NTSTATUS Status; 723 724 RtlInitUnicodeString(&us, string); 725 726 kvfi = NULL; 727 kvfilen = 0; 728 Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); 729 730 if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { 731 kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); 732 733 if (!kvfi) { 734 ERR("out of memory\n"); 735 ZwClose(h); 736 return; 737 } 738 739 Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); 740 741 if (NT_SUCCESS(Status)) { 742 if (kvfi->Type == type && kvfi->DataLength >= size) { 743 RtlCopyMemory(val, ((uint8_t*)kvfi) + kvfi->DataOffset, size); 744 } else { 745 Status = ZwDeleteValueKey(h, &us); 746 if (!NT_SUCCESS(Status)) { 747 ERR("ZwDeleteValueKey returned %08x\n", Status); 748 } 749 750 Status = ZwSetValueKey(h, &us, 0, type, val, size); 751 if (!NT_SUCCESS(Status)) { 752 ERR("ZwSetValueKey returned %08x\n", Status); 753 } 754 } 755 } 756 757 ExFreePool(kvfi); 758 } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { 759 Status = ZwSetValueKey(h, &us, 0, type, val, size); 760 761 if (!NT_SUCCESS(Status)) { 762 ERR("ZwSetValueKey returned %08x\n", Status); 763 } 764 } else { 765 ERR("ZwQueryValueKey returned %08x\n", Status); 766 } 767 } 768 769 void read_registry(PUNICODE_STRING regpath, bool refresh) { 770 OBJECT_ATTRIBUTES oa; 771 NTSTATUS Status; 772 HANDLE h; 773 ULONG dispos; 774 #ifdef _DEBUG 775 KEY_VALUE_FULL_INFORMATION* kvfi; 776 ULONG kvfilen, old_debug_log_level = debug_log_level; 777 UNICODE_STRING us, old_log_file, old_log_device; 778 779 static const WCHAR def_log_file[] = L"\\??\\C:\\btrfs.log"; 780 #endif 781 782 ExAcquireResourceExclusiveLite(&mapping_lock, true); 783 784 read_mappings(regpath); 785 read_group_mappings(regpath); 786 787 ExReleaseResourceLite(&mapping_lock); 788 789 InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); 790 791 Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos); 792 793 if (!NT_SUCCESS(Status)) { 794 ERR("ZwCreateKey returned %08x\n", Status); 795 return; 796 } 797 798 if (!refresh) 799 reset_subkeys(h, regpath); 800 801 get_registry_value(h, L"Compress", REG_DWORD, &mount_compress, sizeof(mount_compress)); 802 get_registry_value(h, L"CompressForce", REG_DWORD, &mount_compress_force, sizeof(mount_compress_force)); 803 get_registry_value(h, L"CompressType", REG_DWORD, &mount_compress_type, sizeof(mount_compress_type)); 804 get_registry_value(h, L"ZlibLevel", REG_DWORD, &mount_zlib_level, sizeof(mount_zlib_level)); 805 get_registry_value(h, L"FlushInterval", REG_DWORD, &mount_flush_interval, sizeof(mount_flush_interval)); 806 get_registry_value(h, L"MaxInline", REG_DWORD, &mount_max_inline, sizeof(mount_max_inline)); 807 get_registry_value(h, L"SkipBalance", REG_DWORD, &mount_skip_balance, sizeof(mount_skip_balance)); 808 get_registry_value(h, L"NoBarrier", REG_DWORD, &mount_no_barrier, sizeof(mount_no_barrier)); 809 get_registry_value(h, L"NoTrim", REG_DWORD, &mount_no_trim, sizeof(mount_no_trim)); 810 get_registry_value(h, L"ClearCache", REG_DWORD, &mount_clear_cache, sizeof(mount_clear_cache)); 811 get_registry_value(h, L"AllowDegraded", REG_DWORD, &mount_allow_degraded, sizeof(mount_allow_degraded)); 812 get_registry_value(h, L"Readonly", REG_DWORD, &mount_readonly, sizeof(mount_readonly)); 813 get_registry_value(h, L"ZstdLevel", REG_DWORD, &mount_zstd_level, sizeof(mount_zstd_level)); 814 get_registry_value(h, L"NoRootDir", REG_DWORD, &mount_no_root_dir, sizeof(mount_no_root_dir)); 815 816 if (!refresh) 817 get_registry_value(h, L"NoPNP", REG_DWORD, &no_pnp, sizeof(no_pnp)); 818 819 if (mount_flush_interval == 0) 820 mount_flush_interval = 1; 821 822 #ifdef _DEBUG 823 get_registry_value(h, L"DebugLogLevel", REG_DWORD, &debug_log_level, sizeof(debug_log_level)); 824 825 RtlInitUnicodeString(&us, L"LogDevice"); 826 827 kvfi = NULL; 828 kvfilen = 0; 829 Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); 830 831 old_log_device = log_device; 832 833 log_device.Length = log_device.MaximumLength = 0; 834 log_device.Buffer = NULL; 835 836 if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { 837 kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); 838 839 if (!kvfi) { 840 ERR("out of memory\n"); 841 ZwClose(h); 842 return; 843 } 844 845 Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); 846 847 if (NT_SUCCESS(Status)) { 848 if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) { 849 log_device.Length = log_device.MaximumLength = (USHORT)min(0xffff, kvfi->DataLength); 850 log_device.Buffer = ExAllocatePoolWithTag(PagedPool, log_device.MaximumLength, ALLOC_TAG); 851 852 if (!log_device.Buffer) { 853 ERR("out of memory\n"); 854 ExFreePool(kvfi); 855 ZwClose(h); 856 return; 857 } 858 859 RtlCopyMemory(log_device.Buffer, ((uint8_t*)kvfi) + kvfi->DataOffset, log_device.Length); 860 861 if (log_device.Buffer[(log_device.Length / sizeof(WCHAR)) - 1] == 0) 862 log_device.Length -= sizeof(WCHAR); 863 } else { 864 ERR("LogDevice was type %u, length %u\n", kvfi->Type, kvfi->DataLength); 865 866 Status = ZwDeleteValueKey(h, &us); 867 if (!NT_SUCCESS(Status)) { 868 ERR("ZwDeleteValueKey returned %08x\n", Status); 869 } 870 } 871 } 872 873 ExFreePool(kvfi); 874 } else if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { 875 ERR("ZwQueryValueKey returned %08x\n", Status); 876 } 877 878 ExAcquireResourceExclusiveLite(&log_lock, true); 879 880 if (refresh && (log_device.Length != old_log_device.Length || RtlCompareMemory(log_device.Buffer, old_log_device.Buffer, log_device.Length) != log_device.Length || 881 (!comfo && log_device.Length > 0) || (old_debug_log_level == 0 && debug_log_level > 0) || (old_debug_log_level > 0 && debug_log_level == 0))) { 882 if (comfo) 883 ObDereferenceObject(comfo); 884 885 if (log_handle) { 886 ZwClose(log_handle); 887 log_handle = NULL; 888 } 889 890 comfo = NULL; 891 comdo = NULL; 892 893 if (log_device.Length > 0 && debug_log_level > 0) { 894 Status = IoGetDeviceObjectPointer(&log_device, FILE_WRITE_DATA, &comfo, &comdo); 895 if (!NT_SUCCESS(Status)) 896 DbgPrint("IoGetDeviceObjectPointer returned %08x\n", Status); 897 } 898 } 899 900 ExReleaseResourceLite(&log_lock); 901 902 if (old_log_device.Buffer) 903 ExFreePool(old_log_device.Buffer); 904 905 RtlInitUnicodeString(&us, L"LogFile"); 906 907 kvfi = NULL; 908 kvfilen = 0; 909 Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); 910 911 old_log_file = log_file; 912 913 if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { 914 kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); 915 916 if (!kvfi) { 917 ERR("out of memory\n"); 918 ZwClose(h); 919 return; 920 } 921 922 Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); 923 924 if (NT_SUCCESS(Status)) { 925 if ((kvfi->Type == REG_SZ || kvfi->Type == REG_EXPAND_SZ) && kvfi->DataLength >= sizeof(WCHAR)) { 926 log_file.Length = log_file.MaximumLength = (USHORT)min(0xffff, kvfi->DataLength); 927 log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG); 928 929 if (!log_file.Buffer) { 930 ERR("out of memory\n"); 931 ExFreePool(kvfi); 932 ZwClose(h); 933 return; 934 } 935 936 RtlCopyMemory(log_file.Buffer, ((uint8_t*)kvfi) + kvfi->DataOffset, log_file.Length); 937 938 if (log_file.Buffer[(log_file.Length / sizeof(WCHAR)) - 1] == 0) 939 log_file.Length -= sizeof(WCHAR); 940 } else { 941 ERR("LogFile was type %u, length %u\n", kvfi->Type, kvfi->DataLength); 942 943 Status = ZwDeleteValueKey(h, &us); 944 if (!NT_SUCCESS(Status)) 945 ERR("ZwDeleteValueKey returned %08x\n", Status); 946 947 log_file.Length = 0; 948 } 949 } else { 950 ERR("ZwQueryValueKey returned %08\n", Status); 951 log_file.Length = 0; 952 } 953 954 ExFreePool(kvfi); 955 } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { 956 Status = ZwSetValueKey(h, &us, 0, REG_SZ, (void*)def_log_file, sizeof(def_log_file)); 957 958 if (!NT_SUCCESS(Status)) 959 ERR("ZwSetValueKey returned %08x\n", Status); 960 961 log_file.Length = 0; 962 } else { 963 ERR("ZwQueryValueKey returned %08x\n", Status); 964 log_file.Length = 0; 965 } 966 967 if (log_file.Length == 0) { 968 log_file.Length = log_file.MaximumLength = sizeof(def_log_file) - sizeof(WCHAR); 969 log_file.Buffer = ExAllocatePoolWithTag(PagedPool, log_file.MaximumLength, ALLOC_TAG); 970 971 if (!log_file.Buffer) { 972 ERR("out of memory\n"); 973 ZwClose(h); 974 return; 975 } 976 977 RtlCopyMemory(log_file.Buffer, def_log_file, log_file.Length); 978 } 979 980 ExAcquireResourceExclusiveLite(&log_lock, true); 981 982 if (refresh && (log_file.Length != old_log_file.Length || RtlCompareMemory(log_file.Buffer, old_log_file.Buffer, log_file.Length) != log_file.Length || 983 (!log_handle && log_file.Length > 0) || (old_debug_log_level == 0 && debug_log_level > 0) || (old_debug_log_level > 0 && debug_log_level == 0))) { 984 if (log_handle) { 985 ZwClose(log_handle); 986 log_handle = NULL; 987 } 988 989 if (!comfo && log_file.Length > 0 && refresh && debug_log_level > 0) { 990 IO_STATUS_BLOCK iosb; 991 992 InitializeObjectAttributes(&oa, &log_file, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); 993 994 Status = ZwCreateFile(&log_handle, FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, 995 FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_ALERT, NULL, 0); 996 if (!NT_SUCCESS(Status)) { 997 DbgPrint("ZwCreateFile returned %08x\n", Status); 998 log_handle = NULL; 999 } 1000 } 1001 } 1002 1003 ExReleaseResourceLite(&log_lock); 1004 1005 if (old_log_file.Buffer) 1006 ExFreePool(old_log_file.Buffer); 1007 #endif 1008 1009 ZwClose(h); 1010 } 1011 1012 _Function_class_(WORKER_THREAD_ROUTINE) 1013 static void __stdcall registry_work_item(PVOID Parameter) { 1014 NTSTATUS Status; 1015 HANDLE regh = (HANDLE)Parameter; 1016 IO_STATUS_BLOCK iosb; 1017 1018 TRACE("registry changed\n"); 1019 1020 read_registry(®istry_path, true); 1021 1022 Status = ZwNotifyChangeKey(regh, NULL, (PVOID)&wqi, (PVOID)DelayedWorkQueue, &iosb, REG_NOTIFY_CHANGE_LAST_SET, true, NULL, 0, true); 1023 if (!NT_SUCCESS(Status)) 1024 ERR("ZwNotifyChangeKey returned %08x\n", Status); 1025 } 1026 1027 void watch_registry(HANDLE regh) { 1028 NTSTATUS Status; 1029 IO_STATUS_BLOCK iosb; 1030 1031 ExInitializeWorkItem(&wqi, registry_work_item, regh); 1032 1033 Status = ZwNotifyChangeKey(regh, NULL, (PVOID)&wqi, (PVOID)DelayedWorkQueue, &iosb, REG_NOTIFY_CHANGE_LAST_SET, true, NULL, 0, true); 1034 if (!NT_SUCCESS(Status)) 1035 ERR("ZwNotifyChangeKey returned %08x\n", Status); 1036 } 1037