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