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 #ifndef _MSC_VER 34 NTSTATUS RtlUnicodeStringPrintf(PUNICODE_STRING DestinationString, const WCHAR* pszFormat, ...); // not in mingw 35 #endif 36 37 // Not in any headers? Windbg knows about it though. 38 #define DOE_START_PENDING 0x10 39 40 // Just as much as we need - the version in mingw is truncated still further 41 typedef struct { 42 CSHORT Type; 43 USHORT Size; 44 PDEVICE_OBJECT DeviceObject; 45 ULONG PowerFlags; 46 void* Dope; 47 ULONG ExtensionFlags; 48 } DEVOBJ_EXTENSION2; 49 50 static bool get_system_root_partition(uint32_t* disk_num, uint32_t* partition_num) { 51 NTSTATUS Status; 52 HANDLE h; 53 UNICODE_STRING us, target; 54 OBJECT_ATTRIBUTES objatt; 55 WCHAR* s; 56 ULONG retlen = 0, left; 57 58 static const WCHAR system_root[] = L"\\SystemRoot"; 59 static const WCHAR arc_prefix[] = L"\\ArcName\\multi(0)disk(0)rdisk("; 60 static const WCHAR arc_middle[] = L")partition("; 61 62 us.Buffer = (WCHAR*)system_root; 63 us.Length = us.MaximumLength = sizeof(system_root) - sizeof(WCHAR); 64 65 InitializeObjectAttributes(&objatt, &us, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); 66 67 Status = ZwOpenSymbolicLinkObject(&h, GENERIC_READ, &objatt); 68 if (!NT_SUCCESS(Status)) { 69 ERR("ZwOpenSymbolicLinkObject returned %08x\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 %08x\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 %08x\n", Status); 99 NtClose(h); 100 ExFreePool(target.Buffer); 101 return false; 102 } 103 104 NtClose(h); 105 106 TRACE("system root is %.*S\n", target.Length / sizeof(WCHAR), target.Buffer); 107 108 if (target.Length <= sizeof(arc_prefix) - sizeof(WCHAR) || 109 RtlCompareMemory(target.Buffer, arc_prefix, sizeof(arc_prefix) - sizeof(WCHAR)) != sizeof(arc_prefix) - sizeof(WCHAR)) { 110 ExFreePool(target.Buffer); 111 return false; 112 } 113 114 s = &target.Buffer[(sizeof(arc_prefix) / sizeof(WCHAR)) - 1]; 115 left = ((target.Length - sizeof(arc_prefix)) / sizeof(WCHAR)) + 1; 116 117 if (left == 0 || s[0] < '0' || s[0] > '9') { 118 ExFreePool(target.Buffer); 119 return false; 120 } 121 122 *disk_num = 0; 123 124 while (left > 0 && s[0] >= '0' && s[0] <= '9') { 125 *disk_num *= 10; 126 *disk_num += s[0] - '0'; 127 s++; 128 left--; 129 } 130 131 if (left <= (sizeof(arc_middle) / sizeof(WCHAR)) - 1 || 132 RtlCompareMemory(s, arc_middle, sizeof(arc_middle) - sizeof(WCHAR)) != sizeof(arc_middle) - sizeof(WCHAR)) { 133 ExFreePool(target.Buffer); 134 return false; 135 } 136 137 s = &s[(sizeof(arc_middle) / sizeof(WCHAR)) - 1]; 138 left -= (sizeof(arc_middle) / sizeof(WCHAR)) - 1; 139 140 if (left == 0 || s[0] < '0' || s[0] > '9') { 141 ExFreePool(target.Buffer); 142 return false; 143 } 144 145 *partition_num = 0; 146 147 while (left > 0 && s[0] >= '0' && s[0] <= '9') { 148 *partition_num *= 10; 149 *partition_num += s[0] - '0'; 150 s++; 151 left--; 152 } 153 154 ExFreePool(target.Buffer); 155 156 return true; 157 } 158 159 static void change_symlink(uint32_t disk_num, uint32_t partition_num, BTRFS_UUID* uuid) { 160 NTSTATUS Status; 161 UNICODE_STRING us, us2; 162 WCHAR symlink[60], target[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) + 36], *w; 163 #ifdef __REACTOS__ 164 unsigned int i; 165 #endif 166 167 us.Buffer = symlink; 168 us.Length = 0; 169 us.MaximumLength = sizeof(symlink); 170 171 Status = RtlUnicodeStringPrintf(&us, L"\\Device\\Harddisk%u\\Partition%u", disk_num, partition_num); 172 if (!NT_SUCCESS(Status)) { 173 ERR("RtlUnicodeStringPrintf returned %08x\n", Status); 174 return; 175 } 176 177 Status = IoDeleteSymbolicLink(&us); 178 if (!NT_SUCCESS(Status)) 179 ERR("IoDeleteSymbolicLink returned %08x\n", Status); 180 181 RtlCopyMemory(target, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)); 182 183 w = &target[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1]; 184 185 #ifndef __REACTOS__ 186 for (unsigned int i = 0; i < 16; i++) { 187 #else 188 for (i = 0; i < 16; i++) { 189 #endif 190 *w = hex_digit(uuid->uuid[i] >> 4); w++; 191 *w = hex_digit(uuid->uuid[i] & 0xf); w++; 192 193 if (i == 3 || i == 5 || i == 7 || i == 9) { 194 *w = L'-'; 195 w++; 196 } 197 } 198 199 *w = L'}'; 200 201 us2.Buffer = target; 202 us2.Length = us2.MaximumLength = sizeof(target); 203 204 Status = IoCreateSymbolicLink(&us, &us2); 205 if (!NT_SUCCESS(Status)) 206 ERR("IoCreateSymbolicLink returned %08x\n", Status); 207 } 208 209 static void mountmgr_notification(BTRFS_UUID* uuid) { 210 UNICODE_STRING mmdevpath; 211 NTSTATUS Status; 212 PFILE_OBJECT FileObject; 213 PDEVICE_OBJECT mountmgr; 214 ULONG mmtnlen; 215 MOUNTMGR_TARGET_NAME* mmtn; 216 WCHAR* w; 217 #ifdef __REACTOS__ 218 unsigned int i; 219 #endif 220 221 RtlInitUnicodeString(&mmdevpath, MOUNTMGR_DEVICE_NAME); 222 Status = IoGetDeviceObjectPointer(&mmdevpath, FILE_READ_ATTRIBUTES, &FileObject, &mountmgr); 223 if (!NT_SUCCESS(Status)) { 224 ERR("IoGetDeviceObjectPointer returned %08x\n", Status); 225 return; 226 } 227 228 mmtnlen = offsetof(MOUNTMGR_TARGET_NAME, DeviceName[0]) + sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR)); 229 230 mmtn = ExAllocatePoolWithTag(NonPagedPool, mmtnlen, ALLOC_TAG); 231 if (!mmtn) { 232 ERR("out of memory\n"); 233 return; 234 } 235 236 mmtn->DeviceNameLength = sizeof(BTRFS_VOLUME_PREFIX) + (36 * sizeof(WCHAR)); 237 238 RtlCopyMemory(mmtn->DeviceName, BTRFS_VOLUME_PREFIX, sizeof(BTRFS_VOLUME_PREFIX) - sizeof(WCHAR)); 239 240 w = &mmtn->DeviceName[(sizeof(BTRFS_VOLUME_PREFIX) / sizeof(WCHAR)) - 1]; 241 242 #ifndef __REACTOS__ 243 for (unsigned int i = 0; i < 16; i++) { 244 #else 245 for (i = 0; i < 16; i++) { 246 #endif 247 *w = hex_digit(uuid->uuid[i] >> 4); w++; 248 *w = hex_digit(uuid->uuid[i] & 0xf); w++; 249 250 if (i == 3 || i == 5 || i == 7 || i == 9) { 251 *w = L'-'; 252 w++; 253 } 254 } 255 256 *w = L'}'; 257 258 Status = dev_ioctl(mountmgr, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, mmtn, mmtnlen, NULL, 0, false, NULL); 259 if (!NT_SUCCESS(Status)) { 260 ERR("IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION returned %08x\n", Status); 261 ExFreePool(mmtn); 262 return; 263 } 264 265 ExFreePool(mmtn); 266 } 267 268 /* If booting from Btrfs, Windows will pass the device object for the raw partition to 269 * mount_vol - which is no good to us, as we only use the \Device\Btrfs{} devices we 270 * create so that RAID works correctly. 271 * At the time check_system_root gets called, \SystemRoot is a symlink to the ARC device, 272 * e.g. \ArcName\multi(0)disk(0)rdisk(0)partition(1)\Windows. We can't change the symlink, 273 * as it gets clobbered by IopReassignSystemRoot shortly afterwards, and we can't touch 274 * the \ArcName symlinks as they haven't been created yet. Instead, we need to change the 275 * symlink \Device\HarddiskX\PartitionY, which is what the ArcName symlink will shortly 276 * point to. 277 */ 278 void __stdcall check_system_root(PDRIVER_OBJECT DriverObject, PVOID Context, ULONG Count) { 279 uint32_t disk_num, partition_num; 280 LIST_ENTRY* le; 281 bool done = false; 282 PDEVICE_OBJECT pdo_to_add = NULL; 283 284 TRACE("(%p, %p, %u)\n", DriverObject, Context, Count); 285 286 // wait for any PNP notifications in progress to finish 287 ExAcquireResourceExclusiveLite(&boot_lock, TRUE); 288 ExReleaseResourceLite(&boot_lock); 289 290 if (!get_system_root_partition(&disk_num, &partition_num)) 291 return; 292 293 TRACE("system boot partition is disk %u, partition %u\n", disk_num, partition_num); 294 295 ExAcquireResourceSharedLite(&pdo_list_lock, true); 296 297 le = pdo_list.Flink; 298 while (le != &pdo_list) { 299 LIST_ENTRY* le2; 300 pdo_device_extension* pdode = CONTAINING_RECORD(le, pdo_device_extension, list_entry); 301 302 ExAcquireResourceSharedLite(&pdode->child_lock, true); 303 304 le2 = pdode->children.Flink; 305 306 while (le2 != &pdode->children) { 307 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry); 308 309 if (vc->disk_num == disk_num && vc->part_num == partition_num) { 310 change_symlink(disk_num, partition_num, &pdode->uuid); 311 done = true; 312 313 if (!pdode->vde) 314 pdo_to_add = pdode->pdo; 315 316 break; 317 } 318 319 le2 = le2->Flink; 320 } 321 322 if (done) { 323 le2 = pdode->children.Flink; 324 325 while (le2 != &pdode->children) { 326 volume_child* vc = CONTAINING_RECORD(le2, volume_child, list_entry); 327 328 /* On Windows 7 we need to clear the DO_SYSTEM_BOOT_PARTITION flag of 329 * all of our underlying partition objects - otherwise IopMountVolume 330 * will bugcheck with UNMOUNTABLE_BOOT_VOLUME when it tries and fails 331 * to mount one. */ 332 if (vc->devobj) { 333 PDEVICE_OBJECT dev = vc->devobj; 334 335 ObReferenceObject(dev); 336 337 while (dev) { 338 PDEVICE_OBJECT dev2 = IoGetLowerDeviceObject(dev); 339 340 dev->Flags &= ~DO_SYSTEM_BOOT_PARTITION; 341 342 ObDereferenceObject(dev); 343 344 dev = dev2; 345 } 346 } 347 348 le2 = le2->Flink; 349 } 350 351 ExReleaseResourceLite(&pdode->child_lock); 352 353 break; 354 } 355 356 ExReleaseResourceLite(&pdode->child_lock); 357 358 le = le->Flink; 359 } 360 361 ExReleaseResourceLite(&pdo_list_lock); 362 363 // If our FS depends on volumes that aren't there when we do our IoRegisterPlugPlayNotification calls 364 // in DriverEntry, bus_query_device_relations won't get called until it's too late. We need to do our 365 // own call to AddDevice here as a result. We need to clear the DOE_START_PENDING bits, or NtOpenFile 366 // will return STATUS_NO_SUCH_DEVICE. 367 if (pdo_to_add) { 368 pdo_device_extension* pdode = pdo_to_add->DeviceExtension; 369 370 AddDevice(drvobj, pdo_to_add); 371 372 // To stop Windows sneakily setting DOE_START_PENDING 373 pdode->dont_report = true; 374 375 if (pdo_to_add->DeviceObjectExtension) { 376 ((DEVOBJ_EXTENSION2*)pdo_to_add->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING; 377 378 if (pdode && pdode->vde && pdode->vde->device) 379 ((DEVOBJ_EXTENSION2*)pdode->vde->device->DeviceObjectExtension)->ExtensionFlags &= ~DOE_START_PENDING; 380 } 381 382 mountmgr_notification(&pdode->uuid); 383 } 384 } 385