xref: /reactos/drivers/storage/mountmgr/uniqueid.c (revision ba447018)
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