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
ChangeUniqueIdRoutine(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
MountMgrUniqueIdChangeRoutine(IN PDEVICE_EXTENSION DeviceExtension,IN PMOUNTDEV_UNIQUE_ID OldUniqueId,IN PMOUNTDEV_UNIQUE_ID NewUniqueId)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
IsUniqueIdPresent(IN PDEVICE_EXTENSION DeviceExtension,IN PDATABASE_ENTRY DatabaseEntry)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
CreateNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)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
CheckForNoDriveLetterEntry(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)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
HasNoDriveLetterEntry(IN PMOUNTDEV_UNIQUE_ID UniqueId)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
UpdateReplicatedUniqueIds(IN PDEVICE_INFORMATION DeviceInformation,IN PDATABASE_ENTRY DatabaseEntry)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