1 /* 2 * ReactOS kernel 3 * Copyright (C) 2011 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program 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 General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 18 * 19 * COPYRIGHT: See COPYING in the top level directory 20 * PROJECT: ReactOS kernel 21 * FILE: drivers/filesystem/mountmgr/uniqueid.c 22 * PURPOSE: Mount Manager - Unique ID 23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org) 24 */ 25 26 #include "mntmgr.h" 27 28 #define NDEBUG 29 #include <debug.h> 30 31 /* 32 * @implemented 33 */ 34 NTSTATUS 35 NTAPI 36 ChangeUniqueIdRoutine(IN PWSTR ValueName, 37 IN ULONG ValueType, 38 IN PVOID ValueData, 39 IN ULONG ValueLength, 40 IN PVOID Context, 41 IN PVOID EntryContext) 42 { 43 PMOUNTDEV_UNIQUE_ID OldUniqueId = Context; 44 PMOUNTDEV_UNIQUE_ID NewUniqueId = EntryContext; 45 46 /* Validate parameters not to corrupt registry */ 47 if ((ValueType != REG_BINARY) || 48 (OldUniqueId->UniqueIdLength != ValueLength)) 49 { 50 return STATUS_SUCCESS; 51 } 52 53 if (RtlCompareMemory(OldUniqueId->UniqueId, ValueData, ValueLength) == ValueLength) 54 { 55 /* Write new data */ 56 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, 57 DatabasePath, 58 ValueName, 59 REG_BINARY, 60 NewUniqueId, 61 NewUniqueId->UniqueIdLength); 62 } 63 64 return STATUS_SUCCESS; 65 } 66 67 /* 68 * @implemented 69 */ 70 VOID 71 MountMgrUniqueIdChangeRoutine(IN PDEVICE_EXTENSION DeviceExtension, 72 IN PMOUNTDEV_UNIQUE_ID OldUniqueId, 73 IN PMOUNTDEV_UNIQUE_ID NewUniqueId) 74 { 75 NTSTATUS Status; 76 BOOLEAN ResyncNeeded; 77 PUNIQUE_ID_REPLICATE DuplicateId; 78 PDEVICE_INFORMATION DeviceInformation; 79 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 80 PMOUNTDEV_UNIQUE_ID UniqueId, NewDuplicateId; 81 PLIST_ENTRY ListHead, NextEntry, ReplicatedHead, NextReplicated; 82 83 /* Synchronise with remote databases */ 84 Status = WaitForRemoteDatabaseSemaphore(DeviceExtension); 85 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL); 86 87 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 88 QueryTable[0].QueryRoutine = ChangeUniqueIdRoutine; 89 QueryTable[0].EntryContext = NewUniqueId; 90 91 /* Write new data */ 92 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 93 DatabasePath, 94 QueryTable, 95 OldUniqueId, 96 NULL); 97 98 /* Browse all the devices to find the one that 99 * owns the old unique ID 100 */ 101 ListHead = &(DeviceExtension->DeviceListHead); 102 NextEntry = ListHead->Flink; 103 while (ListHead != NextEntry) 104 { 105 DeviceInformation = CONTAINING_RECORD(NextEntry, 106 DEVICE_INFORMATION, 107 DeviceListEntry); 108 109 if (DeviceInformation->UniqueId->UniqueIdLength == OldUniqueId->UniqueIdLength && 110 RtlCompareMemory(OldUniqueId->UniqueId, 111 DeviceInformation->UniqueId->UniqueId, 112 OldUniqueId->UniqueIdLength) == OldUniqueId->UniqueIdLength) 113 { 114 break; 115 } 116 117 NextEntry = NextEntry->Flink; 118 } 119 120 /* If we didn't find any release everything and quit */ 121 if (ListHead == NextEntry) 122 { 123 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 124 1, FALSE); 125 126 if (NT_SUCCESS(Status)) 127 { 128 ReleaseRemoteDatabaseSemaphore(DeviceExtension); 129 } 130 131 return; 132 } 133 134 /* If lock failed, then, just update this database */ 135 if (!NT_SUCCESS(Status)) 136 { 137 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation); 138 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 139 1, FALSE); 140 return; 141 } 142 143 /* Allocate new unique ID */ 144 UniqueId = AllocatePool(NewUniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); 145 if (!UniqueId) 146 { 147 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 148 1, FALSE); 149 ReleaseRemoteDatabaseSemaphore(DeviceExtension); 150 return; 151 } 152 153 /* Release old one */ 154 FreePool(DeviceInformation->UniqueId); 155 /* And set new one */ 156 DeviceInformation->UniqueId = UniqueId; 157 UniqueId->UniqueIdLength = NewUniqueId->UniqueIdLength; 158 RtlCopyMemory(UniqueId->UniqueId, NewUniqueId->UniqueId, NewUniqueId->UniqueIdLength); 159 160 /* Now, check if it's required to update replicated unique IDs as well */ 161 ListHead = &(DeviceExtension->DeviceListHead); 162 NextEntry = ListHead->Flink; 163 while (ListHead != NextEntry) 164 { 165 DeviceInformation = CONTAINING_RECORD(NextEntry, 166 DEVICE_INFORMATION, 167 DeviceListEntry); 168 ResyncNeeded = FALSE; 169 170 ReplicatedHead = &(DeviceInformation->ReplicatedUniqueIdsListHead); 171 NextReplicated = ReplicatedHead->Flink; 172 while (ReplicatedHead != NextReplicated) 173 { 174 DuplicateId = CONTAINING_RECORD(NextReplicated, 175 UNIQUE_ID_REPLICATE, 176 ReplicatedUniqueIdsListEntry); 177 178 if (DuplicateId->UniqueId->UniqueIdLength == OldUniqueId->UniqueIdLength) 179 { 180 if (RtlCompareMemory(DuplicateId->UniqueId->UniqueId, 181 OldUniqueId->UniqueId, 182 OldUniqueId->UniqueIdLength) == OldUniqueId->UniqueIdLength) 183 { 184 /* It was our old unique ID */ 185 NewDuplicateId = AllocatePool(NewUniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); 186 if (NewDuplicateId) 187 { 188 /* Update it */ 189 ResyncNeeded = TRUE; 190 FreePool(DuplicateId->UniqueId); 191 192 DuplicateId->UniqueId = NewDuplicateId; 193 DuplicateId->UniqueId->UniqueIdLength = NewUniqueId->UniqueIdLength; 194 RtlCopyMemory(NewDuplicateId->UniqueId, NewUniqueId->UniqueId, NewUniqueId->UniqueIdLength); 195 } 196 } 197 } 198 199 NextReplicated = NextReplicated->Flink; 200 } 201 202 /* If resync is required on this device, do it */ 203 if (ResyncNeeded) 204 { 205 ChangeRemoteDatabaseUniqueId(DeviceInformation, OldUniqueId, NewUniqueId); 206 } 207 208 NextEntry = NextEntry->Flink; 209 } 210 211 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE); 212 ReleaseRemoteDatabaseSemaphore(DeviceExtension); 213 214 return; 215 } 216 217 /* 218 * @implemented 219 */ 220 BOOLEAN 221 IsUniqueIdPresent(IN PDEVICE_EXTENSION DeviceExtension, 222 IN PDATABASE_ENTRY DatabaseEntry) 223 { 224 PLIST_ENTRY NextEntry; 225 PDEVICE_INFORMATION DeviceInformation; 226 227 /* If no device, no unique ID (O'rly?!) 228 * ./)/). 229 * (°-°) 230 * (___) ORLY? 231 * " " 232 */ 233 if (IsListEmpty(&(DeviceExtension->DeviceListHead))) 234 { 235 return FALSE; 236 } 237 238 /* Now we know that we have devices, find the one */ 239 for (NextEntry = DeviceExtension->DeviceListHead.Flink; 240 NextEntry != &(DeviceExtension->DeviceListHead); 241 NextEntry = NextEntry->Flink) 242 { 243 DeviceInformation = CONTAINING_RECORD(NextEntry, 244 DEVICE_INFORMATION, 245 DeviceListEntry); 246 247 if (DeviceInformation->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength) 248 { 249 continue; 250 } 251 252 /* It's matching! */ 253 if (RtlCompareMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), 254 DeviceInformation->UniqueId->UniqueId, 255 DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength) 256 { 257 return TRUE; 258 } 259 } 260 261 /* No luck... */ 262 return FALSE; 263 } 264 265 /* 266 * @implemented 267 */ 268 VOID 269 CreateNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId) 270 { 271 UUID Guid; 272 PWCHAR String; 273 UNICODE_STRING GuidString; 274 275 /* Entry with no drive letter are made that way: 276 * Instead of having a path with the letter, 277 * you have GUID with the unique ID. 278 */ 279 if (!NT_SUCCESS(ExUuidCreate(&Guid))) 280 { 281 return; 282 } 283 284 /* Convert to string */ 285 if (!NT_SUCCESS(RtlStringFromGUID(&Guid, &GuidString))) 286 { 287 return; 288 } 289 290 /* No letter entries must start with #, so allocate a proper string */ 291 String = AllocatePool(GuidString.Length + 2 * sizeof(WCHAR)); 292 if (!String) 293 { 294 ExFreePoolWithTag(GuidString.Buffer, 0); 295 return; 296 } 297 298 /* Write the complete string */ 299 String[0] = L'#'; 300 RtlCopyMemory(String + 1, GuidString.Buffer, GuidString.Length); 301 String[GuidString.Length / sizeof(WCHAR)] = UNICODE_NULL; 302 303 /* Don't need that one anymore */ 304 ExFreePoolWithTag(GuidString.Buffer, 0); 305 306 /* Write the entry */ 307 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, 308 DatabasePath, 309 String, 310 REG_BINARY, 311 UniqueId->UniqueId, 312 UniqueId->UniqueIdLength); 313 314 FreePool(String); 315 316 return; 317 } 318 319 /* 320 * @implemented 321 */ 322 NTSTATUS 323 NTAPI 324 CheckForNoDriveLetterEntry(IN PWSTR ValueName, 325 IN ULONG ValueType, 326 IN PVOID ValueData, 327 IN ULONG ValueLength, 328 IN PVOID Context, 329 IN PVOID EntryContext) 330 { 331 PBOOLEAN EntryPresent = EntryContext; 332 PMOUNTDEV_UNIQUE_ID UniqueId = Context; 333 334 /* Check if matches no drive letter entry */ 335 if (ValueName[0] != L'#' || ValueType != REG_BINARY || 336 UniqueId->UniqueIdLength != ValueLength) 337 { 338 return STATUS_SUCCESS; 339 } 340 341 /* Compare unique ID */ 342 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) == ValueLength) 343 { 344 *EntryPresent = TRUE; 345 } 346 347 return STATUS_SUCCESS; 348 } 349 350 /* 351 * @implemented 352 */ 353 BOOLEAN 354 HasNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId) 355 { 356 BOOLEAN EntryPresent = FALSE; 357 RTL_QUERY_REGISTRY_TABLE QueryTable[2]; 358 359 RtlZeroMemory(QueryTable, sizeof(QueryTable)); 360 QueryTable[0].QueryRoutine = CheckForNoDriveLetterEntry; 361 QueryTable[0].EntryContext = &EntryPresent; 362 363 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, 364 DatabasePath, 365 QueryTable, 366 UniqueId, 367 NULL); 368 369 return EntryPresent; 370 } 371 372 /* 373 * @implemented 374 */ 375 VOID 376 UpdateReplicatedUniqueIds(IN PDEVICE_INFORMATION DeviceInformation, IN PDATABASE_ENTRY DatabaseEntry) 377 { 378 PLIST_ENTRY NextEntry; 379 PUNIQUE_ID_REPLICATE ReplicatedUniqueId, NewEntry; 380 381 /* Browse all the device replicated unique IDs */ 382 for (NextEntry = DeviceInformation->ReplicatedUniqueIdsListHead.Flink; 383 NextEntry != &(DeviceInformation->ReplicatedUniqueIdsListHead); 384 NextEntry = NextEntry->Flink) 385 { 386 ReplicatedUniqueId = CONTAINING_RECORD(NextEntry, 387 UNIQUE_ID_REPLICATE, 388 ReplicatedUniqueIdsListEntry); 389 390 if (ReplicatedUniqueId->UniqueId->UniqueIdLength != DatabaseEntry->UniqueIdLength) 391 { 392 continue; 393 } 394 395 /* If we find the UniqueId to update, break */ 396 if (RtlCompareMemory(ReplicatedUniqueId->UniqueId->UniqueId, 397 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), 398 ReplicatedUniqueId->UniqueId->UniqueIdLength) == ReplicatedUniqueId->UniqueId->UniqueIdLength) 399 { 400 break; 401 } 402 } 403 404 /* We found the unique ID, no need to continue */ 405 if (NextEntry != &(DeviceInformation->ReplicatedUniqueIdsListHead)) 406 { 407 return; 408 } 409 410 /* Allocate a new entry for unique ID */ 411 NewEntry = AllocatePool(sizeof(UNIQUE_ID_REPLICATE)); 412 if (!NewEntry) 413 { 414 return; 415 } 416 417 /* Allocate the unique ID */ 418 NewEntry->UniqueId = AllocatePool(DatabaseEntry->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID)); 419 if (!NewEntry->UniqueId) 420 { 421 FreePool(NewEntry); 422 return; 423 } 424 425 /* Copy */ 426 NewEntry->UniqueId->UniqueIdLength = DatabaseEntry->UniqueIdLength; 427 RtlCopyMemory(NewEntry->UniqueId->UniqueId, 428 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), 429 DatabaseEntry->UniqueIdLength); 430 /* And insert into replicated unique IDs list */ 431 InsertTailList(&DeviceInformation->ReplicatedUniqueIdsListHead, &NewEntry->ReplicatedUniqueIdsListEntry); 432 433 return; 434 } 435