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