1 /* Copyright (c) Mark Harmstone 2019 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 20 #ifndef __REACTOS__ 21 #ifdef _MSC_VER 22 #include <ntstrsafe.h> 23 #endif 24 #else 25 #include <ntstrsafe.h> 26 #endif 27 28 extern ERESOURCE pdo_list_lock; 29 extern LIST_ENTRY pdo_list; 30 extern ERESOURCE boot_lock; 31 extern PDRIVER_OBJECT drvobj; 32 33 BTRFS_UUID boot_uuid; // initialized to 0 34 uint64_t boot_subvol = 0; 35 36 // Not in any headers? Windbg knows about it though. 37 #define DOE_START_PENDING 0x10 38 39 // Just as much as we need - the version in mingw is truncated still further 40 typedef struct { 41 CSHORT Type; 42 USHORT Size; 43 PDEVICE_OBJECT DeviceObject; 44 ULONG PowerFlags; 45 void* Dope; 46 ULONG ExtensionFlags; 47 } DEVOBJ_EXTENSION2; 48 49 static bool get_system_root() { 50 NTSTATUS Status; 51 HANDLE h; 52 UNICODE_STRING us, target; 53 OBJECT_ATTRIBUTES objatt; 54 ULONG retlen = 0; 55 bool second_time = false; 56 57 static const WCHAR system_root[] = L"\\SystemRoot"; 58 static const WCHAR boot_device[] = L"\\Device\\BootDevice"; 59 static const WCHAR arc_btrfs_prefix[] = L"\\ArcName\\btrfs("; 60 61 us.Buffer = (WCHAR*)system_root; 62 us.Length = us.MaximumLength = sizeof(system_root) - sizeof(WCHAR); 63 64 InitializeObjectAttributes(&objatt, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); 65 66 while (true) { 67 Status = ZwOpenSymbolicLinkObject(&h, GENERIC_READ, &objatt); 68 if (!NT_SUCCESS(Status)) { 69 ERR("ZwOpenSymbolicLinkObject returned %08lx\n", Status); 70 return false; 71 } 72 73 target.Length = target.MaximumLength = 0; 74 75 Status = ZwQuerySymbolicLinkObject(h, &target, &retlen); 76 if (Status != STATUS_BUFFER_TOO_SMALL) { 77 ERR("ZwQuerySymbolicLinkObject returned %08lx\n", Status); 78 NtClose(h); 79 return false; 80 } 81 82 if (retlen == 0) { 83 NtClose(h); 84 return false; 85 } 86 87 target.Buffer = ExAllocatePoolWithTag(NonPagedPool, retlen, ALLOC_TAG); 88 if (!target.Buffer) { 89 ERR("out of memory\n"); 90 NtClose(h); 91 return false; 92 } 93 94 target.Length = target.MaximumLength = (USHORT)retlen; 95 96 Status = ZwQuerySymbolicLinkObject(h, &target, NULL); 97 if (!NT_SUCCESS(Status)) { 98 ERR("ZwQuerySymbolicLinkObject returned %08lx\n", Status); 99 NtClose(h); 100 ExFreePool(target.Buffer); 101 return false; 102 } 103 104 NtClose(h); 105 106 if (second_time) { 107 TRACE("boot device is %.*S\n", (int)(target.Length / sizeof(WCHAR)), target.Buffer); 108 } else { 109 TRACE("system root is %.*S\n", (int)(target.Length / sizeof(WCHAR)), target.Buffer); 110 } 111 112 if (!second_time && target.Length >= sizeof(boot_device) - sizeof(WCHAR) && 113 RtlCompareMemory(target.Buffer, boot_device, sizeof(boot_device) - sizeof(WCHAR)) == sizeof(boot_device) - sizeof(WCHAR)) { 114 ExFreePool(target.Buffer); 115 116 us.Buffer = (WCHAR*)boot_device; 117 us.Length = us.MaximumLength = sizeof(boot_device) - sizeof(WCHAR); 118 119 second_time = true; 120 } else 121 break; 122 } 123 124 if (target.Length >= sizeof(arc_btrfs_prefix) - sizeof(WCHAR) && 125 RtlCompareMemory(target.Buffer, arc_btrfs_prefix, sizeof(arc_btrfs_prefix) - sizeof(WCHAR)) == sizeof(arc_btrfs_prefix) - sizeof(WCHAR)) { 126 WCHAR* s = &target.Buffer[(sizeof(arc_btrfs_prefix) / sizeof(WCHAR)) - 1]; 127 128 for (unsigned int i = 0; i < 16; i++) { 129 if (*s >= '0' && *s <= '9') 130 boot_uuid.uuid[i] = (*s - '0') << 4; 131 else if (*s >= 'a' && *s <= 'f') 132 boot_uuid.uuid[i] = (*s - 'a' + 0xa) << 4; 133 else if (*s >= 'A' && *s <= 'F') 134 boot_uuid.uuid[i] = (*s - 'A' + 0xa) << 4; 135 else { 136 ExFreePool(target.Buffer); 137 return false; 138 } 139 140 s++; 141 142 if (*s >= '0' && *s <= '9') 143 boot_uuid.uuid[i] |= *s - '0'; 144 else if (*s >= 'a' && *s <= 'f') 145 boot_uuid.uuid[i] |= *s - 'a' + 0xa; 146 else if (*s >= 'A' && *s <= 'F') 147 boot_uuid.uuid[i] |= *s - 'A' + 0xa; 148 else { 149 ExFreePool(target.Buffer); 150 return false; 151 } 152 153 s++; 154 155 if (i == 3 || i == 5 || i == 7 || i == 9) { 156 if (*s != '-') { 157 ExFreePool(target.Buffer); 158 return false; 159 } 160 161 s++; 162 } 163 } 164 165 if (*s != ')') { 166 ExFreePool(target.Buffer); 167 return false; 168 } 169 170 ExFreePool(target.Buffer); 171 172 return true; 173 } 174 175 ExFreePool(target.Buffer); 176 177 return false; 178 } 179 180 static void mountmgr_notification(BTRFS_UUID* uuid) { 181 UNICODE_STRING mmdevpath; 182 NTSTATUS Status; 183 PFILE_OBJECT FileObject; 184 PDEVICE_OBJECT mountmgr; 185 ULONG mmtnlen; 186 MOUNTMGR_TARGET_NAME* mmtn; 187 WCHAR* w; 188 189 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); 190 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr); 191 if (!NT_SUCCESS(Status)) { 192 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status); 193 return; 194 } 195 196 mmtnlen = offsetof(MOUNTMGR_TARGET_NAME, DeviceName[0]) + sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR)); 197 198 mmtn = ExAllocatePoolWithTag(NonPagedPool, mmtnlen, ALLOC_TAG); 199 if (!mmtn) { 200 ERR("out of memory\n"); 201 return; 202 } 203 204 mmtn->DeviceNameLength = sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR)); 205 206 RtlCopyMemory(mmtn->DeviceName, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)); 207 208 w = &mmtn->DeviceName[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1]; 209 210 for (unsigned int i = 0; i < 16; i++) { 211 *w = hex_digit(uuid->uuid[i] >> 4); w++; 212 *w = hex_digit(uuid->uuid[i] & 0xf); w++; 213 214 if (i == 3 || i == 5 || i == 7 || i == 9) { 215 *w = L'-'; 216 w++; 217 } 218 } 219 220 *w = L'}'; 221 222 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, mmtn, mmtnlen, NULL, 0, false, NULL); 223 if (!NT_SUCCESS(Status)) { 224 ERR("IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION returned %08lx\n", Status); 225 ExFreePool(mmtn); 226 return; 227 } 228 229 ExFreePool(mmtn); 230 } 231 232 static void check_boot_options() { 233 NTSTATUS Status; 234 WCHAR* s; 235 236 static const WCHAR pathw[] = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control"; 237 static const WCHAR namew[] = L"SystemStartOptions"; 238 static const WCHAR subvol[] = L"SUBVOL="; 239 240 _SEH2_TRY { 241 HANDLE control; 242 OBJECT_ATTRIBUTES oa; 243 UNICODE_STRING path; 244 ULONG kvfilen = sizeof(KEY_VALUE_FULL_INFORMATION) - sizeof(WCHAR) + (255 * sizeof(WCHAR)); 245 KEY_VALUE_FULL_INFORMATION* kvfi; 246 UNICODE_STRING name; 247 WCHAR* options; 248 249 path.Buffer = (WCHAR*)pathw; 250 path.Length = path.MaximumLength = sizeof(pathw) - sizeof(WCHAR); 251 252 InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); 253 254 Status = ZwOpenKey(&control, KEY_QUERY_VALUE, &oa); 255 if (!NT_SUCCESS(Status)) { 256 ERR("ZwOpenKey returned %08lx\n", Status); 257 return; 258 } 259 260 // FIXME - don't fail if value too long (can we query for the length?) 261 262 kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); 263 if (!kvfi) { 264 ERR("out of memory\n"); 265 NtClose(control); 266 return; 267 } 268 269 name.Buffer = (WCHAR*)namew; 270 name.Length = name.MaximumLength = sizeof(namew) - sizeof(WCHAR); 271 272 Status = ZwQueryValueKey(control, &name, KeyValueFullInformation, kvfi, 273 kvfilen, &kvfilen); 274 if (!NT_SUCCESS(Status)) { 275 ERR("ZwQueryValueKey returned %08lx\n", Status); 276 NtClose(control); 277 return; 278 } 279 280 NtClose(control); 281 282 options = (WCHAR*)((uint8_t*)kvfi + kvfi->DataOffset); 283 options[kvfi->DataLength / sizeof(WCHAR)] = 0; // FIXME - make sure buffer long enough to allow this 284 285 s = wcsstr(options, subvol); 286 287 if (!s) 288 return; 289 290 s += (sizeof(subvol) / sizeof(WCHAR)) - 1; 291 292 boot_subvol = 0; 293 294 while (true) { 295 if (*s >= '0' && *s <= '9') { 296 boot_subvol <<= 4; 297 boot_subvol |= *s - '0'; 298 } else if (*s >= 'a' && *s <= 'f') { 299 boot_subvol <<= 4; 300 boot_subvol |= *s - 'a' + 0xa; 301 } else if (*s >= 'A' && *s <= 'F') { 302 boot_subvol <<= 4; 303 boot_subvol |= *s - 'A' + 0xa; 304 } else 305 break; 306 307 s++; 308 } 309 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 310 return; 311 } _SEH2_END; 312 313 if (boot_subvol != 0) { 314 TRACE("passed subvol %I64x in boot options\n", boot_subvol); 315 } 316 } 317 318 void boot_add_device(DEVICE_OBJECT* pdo) { 319 pdo_device_extension* pdode = pdo->DeviceExtension; 320 321 AddDevice(drvobj, pdo); 322 323 // To stop Windows sneakily setting DOE_START_PENDING 324 pdode->dont_report = true; 325 326 if (pdo->DeviceObjectExtension) { 327 ((DEVOBJ_EXTENSION2*)pdo->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING; 328 329 if (pdode && pdode->vde && pdode->vde->device) 330 ((DEVOBJ_EXTENSION2*)pdode->vde->device->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING; 331 } 332 333 mountmgr_notification(&pdode->uuid); 334 } 335 336 void check_system_root() { 337 LIST_ENTRY* le; 338 PDEVICE_OBJECT pdo_to_add = NULL; 339 340 TRACE("()\n"); 341 342 // wait for any PNP notifications in progress to finish 343 ExAcquireResourceExclusiveLite(&boot_lock, TRUE); 344 ExReleaseResourceLite(&boot_lock); 345 346 if (!get_system_root()) 347 return; 348 349 ExAcquireResourceSharedLite(&pdo_list_lock, true); 350 351 le = pdo_list.Flink; 352 while (le != &pdo_list) { 353 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry); 354 355 if (RtlCompareMemory(&pdode->uuid, &boot_uuid, sizeof(BTRFS_UUID)) == sizeof(BTRFS_UUID)) { 356 if (!pdode->vde) 357 pdo_to_add = pdode->pdo; 358 else if (pdode->vde->device && !(pdode->vde->device->Flags & DO_SYSTEM_BOOT_PARTITION)) { // AddDevice has beaten us to it 359 NTSTATUS Status; 360 361 pdode->vde->device->Flags |= DO_SYSTEM_BOOT_PARTITION; 362 pdode->pdo->Flags |= DO_SYSTEM_BOOT_PARTITION; 363 364 Status = IoSetDeviceInterfaceState(&pdode->vde->bus_name, false); 365 if (!NT_SUCCESS(Status)) 366 ERR("IoSetDeviceInterfaceState returned %08lx\n", Status); 367 368 Status = IoSetDeviceInterfaceState(&pdode->vde->bus_name, true); 369 if (!NT_SUCCESS(Status)) 370 ERR("IoSetDeviceInterfaceState returned %08lx\n", Status); 371 } 372 373 break; 374 } 375 376 le = le->Flink; 377 } 378 379 ExReleaseResourceLite(&pdo_list_lock); 380 381 check_boot_options(); 382 383 // If our FS depends on volumes that aren't there when we do our IoRegisterPlugPlayNotification calls 384 // in DriverEntry, bus_query_device_relations won't get called until it's too late. We need to do our 385 // own call to AddDevice here as a result. We need to clear the DOE_START_PENDING bits, or NtOpenFile 386 // will return STATUS_NO_SUCH_DEVICE. 387 if (pdo_to_add) 388 boot_add_device(pdo_to_add); 389 } 390