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