1 /*
2 * ReactOS kernel
3 * Copyright (C) 2011-2012 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/symlink.c
22 * PURPOSE: Mount Manager - Symbolic links functions
23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
24 */
25
26 #include "mntmgr.h"
27
28 #define NDEBUG
29 #include <debug.h>
30
31 /* Deprecated Windows 2000/XP versions of IOCTL_MOUNTDEV_LINK_[CREATED|DELETED]
32 * without access protection, that were updated in Windows 2003.
33 * They are sent to MountMgr clients if they do not recognize the new IOCTLs
34 * (e.g. they are for an older NT version). */
35 #if (NTDDI_VERSION >= NTDDI_WS03)
36 #define IOCTL_MOUNTDEV_LINK_CREATED_UNSECURE_DEPRECATED CTL_CODE(MOUNTDEVCONTROLTYPE, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
37 #define IOCTL_MOUNTDEV_LINK_DELETED_UNSECURE_DEPRECATED CTL_CODE(MOUNTDEVCONTROLTYPE, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
38 #endif
39
40 UNICODE_STRING DeviceMount = RTL_CONSTANT_STRING(MOUNTMGR_DEVICE_NAME);
41 UNICODE_STRING DosDevicesMount = RTL_CONSTANT_STRING(L"\\DosDevices\\MountPointManager");
42 UNICODE_STRING DosDevices = RTL_CONSTANT_STRING(L"\\DosDevices\\");
43 UNICODE_STRING DeviceFloppy = RTL_CONSTANT_STRING(L"\\Device\\Floppy");
44 UNICODE_STRING DeviceCdRom = RTL_CONSTANT_STRING(L"\\Device\\CdRom");
45 UNICODE_STRING DosGlobal = RTL_CONSTANT_STRING(L"\\GLOBAL??\\");
46 UNICODE_STRING Global = RTL_CONSTANT_STRING(L"\\??\\");
47 UNICODE_STRING SafeVolumes = RTL_CONSTANT_STRING(L"\\Device\\VolumesSafeForWriteAccess");
48 UNICODE_STRING Volume = RTL_CONSTANT_STRING(L"\\??\\Volume");
49 UNICODE_STRING ReparseIndex = RTL_CONSTANT_STRING(L"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION");
50
51 /*
52 * @implemented
53 */
54 NTSTATUS
CreateStringWithGlobal(IN PUNICODE_STRING DosName,OUT PUNICODE_STRING GlobalString)55 CreateStringWithGlobal(IN PUNICODE_STRING DosName,
56 OUT PUNICODE_STRING GlobalString)
57 {
58 UNICODE_STRING IntGlobal;
59
60 if (RtlPrefixUnicodeString(&DosDevices, DosName, TRUE))
61 {
62 /* DOS device - use DOS global */
63 IntGlobal.Length = DosName->Length - DosDevices.Length + DosGlobal.Length;
64 IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR);
65 IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
66 if (!IntGlobal.Buffer)
67 {
68 return STATUS_INSUFFICIENT_RESOURCES;
69 }
70
71 RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length);
72 RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)),
73 DosName->Buffer + (DosDevices.Length / sizeof(WCHAR)),
74 DosName->Length - DosDevices.Length);
75 IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL;
76 }
77 else if (RtlPrefixUnicodeString(&Global, DosName, TRUE))
78 {
79 /* Switch to DOS global */
80 IntGlobal.Length = DosName->Length - Global.Length + DosGlobal.Length;
81 IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR);
82 IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
83 if (!IntGlobal.Buffer)
84 {
85 return STATUS_INSUFFICIENT_RESOURCES;
86 }
87
88 RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length);
89 RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)),
90 DosName->Buffer + (Global.Length / sizeof(WCHAR)),
91 DosName->Length - Global.Length);
92 IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL;
93 }
94 else
95 {
96 /* Simply duplicate string */
97 IntGlobal.Length = DosName->Length;
98 IntGlobal.MaximumLength = DosName->MaximumLength;
99 IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
100 if (!IntGlobal.Buffer)
101 {
102 return STATUS_INSUFFICIENT_RESOURCES;
103 }
104
105 RtlCopyMemory(IntGlobal.Buffer, DosName->Buffer, IntGlobal.MaximumLength);
106 }
107
108 /* Return string */
109 GlobalString->Length = IntGlobal.Length;
110 GlobalString->MaximumLength = IntGlobal.MaximumLength;
111 GlobalString->Buffer = IntGlobal.Buffer;
112
113 return STATUS_SUCCESS;
114 }
115
116 /*
117 * @implemented
118 */
119 NTSTATUS
GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName,IN PUNICODE_STRING DeviceName)120 GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName,
121 IN PUNICODE_STRING DeviceName)
122 {
123 NTSTATUS Status;
124 UNICODE_STRING GlobalName;
125
126 /* First create the global string */
127 Status = CreateStringWithGlobal(DosName, &GlobalName);
128 if (!NT_SUCCESS(Status))
129 {
130 return Status;
131 }
132
133 /* Then, create the symlink */
134 Status = IoCreateSymbolicLink(&GlobalName, DeviceName);
135
136 FreePool(GlobalName.Buffer);
137
138 return Status;
139 }
140
141 /*
142 * @implemented
143 */
144 NTSTATUS
GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName)145 GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName)
146 {
147 NTSTATUS Status;
148 UNICODE_STRING GlobalName;
149
150 /* Recreate the string (to find the link) */
151 Status = CreateStringWithGlobal(DosName, &GlobalName);
152 if (!NT_SUCCESS(Status))
153 {
154 return Status;
155 }
156
157 /* And delete the link */
158 Status = IoDeleteSymbolicLink(&GlobalName);
159
160 FreePool(GlobalName.Buffer);
161
162 return Status;
163 }
164
165 /*
166 * @implemented
167 */
168 VOID
SendLinkCreated(IN PUNICODE_STRING SymbolicName)169 SendLinkCreated(IN PUNICODE_STRING SymbolicName)
170 {
171 NTSTATUS Status;
172 ULONG NameSize;
173 PFILE_OBJECT FileObject;
174 PMOUNTDEV_NAME Name = NULL;
175 PDEVICE_OBJECT DeviceObject;
176
177 /* Get the device associated with the name */
178 Status = IoGetDeviceObjectPointer(SymbolicName,
179 FILE_READ_ATTRIBUTES,
180 &FileObject,
181 &DeviceObject);
182 if (!NT_SUCCESS(Status))
183 return;
184
185 /* Get attached device (will notify it) */
186 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
187
188 /* NameSize is the size of the whole MOUNTDEV_NAME structure */
189 NameSize = sizeof(USHORT) + SymbolicName->Length;
190 Name = AllocatePool(NameSize);
191 if (!Name)
192 goto Cleanup;
193
194 /* Initialize structure */
195 Name->NameLength = SymbolicName->Length;
196 RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length);
197
198 /* Send the notification, using the new (Windows 2003+) IOCTL definition
199 * (with limited access) first. If this fails, the called driver may be
200 * for an older NT version, and so, we send again the notification using
201 * the old (Windows 2000) IOCTL definition (with any access). */
202 Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_LINK_CREATED,
203 DeviceObject,
204 Name,
205 NameSize,
206 NULL,
207 0,
208 FileObject);
209 /* This one can fail, no one matters */
210 UNREFERENCED_PARAMETER(Status);
211 #if (NTDDI_VERSION >= NTDDI_WS03)
212 /* Then, second one */
213 Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_LINK_CREATED_UNSECURE_DEPRECATED,
214 DeviceObject,
215 Name,
216 NameSize,
217 NULL,
218 0,
219 FileObject);
220 UNREFERENCED_PARAMETER(Status);
221 #endif // (NTDDI_VERSION >= NTDDI_WS03)
222
223 Cleanup:
224 if (Name)
225 FreePool(Name);
226
227 ObDereferenceObject(DeviceObject);
228 ObDereferenceObject(FileObject);
229
230 return;
231 }
232
233 /*
234 * @implemented
235 */
236 VOID
SendLinkDeleted(IN PUNICODE_STRING DeviceName,IN PUNICODE_STRING SymbolicName)237 SendLinkDeleted(IN PUNICODE_STRING DeviceName,
238 IN PUNICODE_STRING SymbolicName)
239 {
240 NTSTATUS Status;
241 ULONG NameSize;
242 PFILE_OBJECT FileObject;
243 PMOUNTDEV_NAME Name = NULL;
244 PDEVICE_OBJECT DeviceObject;
245
246 /* Get the device associated with the name */
247 Status = IoGetDeviceObjectPointer(DeviceName,
248 FILE_READ_ATTRIBUTES,
249 &FileObject,
250 &DeviceObject);
251 if (!NT_SUCCESS(Status))
252 return;
253
254 /* Get attached device (will notify it) */
255 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
256
257 /* NameSize is the size of the whole MOUNTDEV_NAME structure */
258 NameSize = sizeof(USHORT) + SymbolicName->Length;
259 Name = AllocatePool(NameSize);
260 if (!Name)
261 goto Cleanup;
262
263 /* Initialize structure */
264 Name->NameLength = SymbolicName->Length;
265 RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length);
266
267 /* Send the notification, using the new (Windows 2003+) IOCTL definition
268 * (with limited access) first. If this fails, the called driver may be
269 * for an older NT version, and so, we send again the notification using
270 * the old (Windows 2000) IOCTL definition (with any access). */
271 Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_LINK_DELETED,
272 DeviceObject,
273 Name,
274 NameSize,
275 NULL,
276 0,
277 FileObject);
278 /* This one can fail, no one matters */
279 UNREFERENCED_PARAMETER(Status);
280 #if (NTDDI_VERSION >= NTDDI_WS03)
281 /* Then, second one */
282 Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_LINK_DELETED_UNSECURE_DEPRECATED,
283 DeviceObject,
284 Name,
285 NameSize,
286 NULL,
287 0,
288 FileObject);
289 UNREFERENCED_PARAMETER(Status);
290 #endif // (NTDDI_VERSION >= NTDDI_WS03)
291
292 Cleanup:
293 if (Name)
294 FreePool(Name);
295
296 ObDereferenceObject(DeviceObject);
297 ObDereferenceObject(FileObject);
298
299 return;
300 }
301
302 /*
303 * @implemented
304 */
305 NTSTATUS
306 NTAPI
SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)307 SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName,
308 IN ULONG ValueType,
309 IN PVOID ValueData,
310 IN ULONG ValueLength,
311 IN PVOID Context,
312 IN PVOID EntryContext)
313 {
314 UNICODE_STRING ValueNameString;
315 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
316
317 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
318 (UniqueId->UniqueIdLength != ValueLength))
319 {
320 return STATUS_SUCCESS;
321 }
322
323 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
324 {
325 return STATUS_SUCCESS;
326 }
327
328 /* That one matched, increase count */
329 RtlInitUnicodeString(&ValueNameString, ValueName);
330 if (ValueNameString.Length)
331 {
332 (*((PULONG)EntryContext))++;
333 }
334
335 return STATUS_SUCCESS;
336 }
337
338 /*
339 * @implemented
340 */
341 NTSTATUS
342 NTAPI
SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)343 SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName,
344 IN ULONG ValueType,
345 IN PVOID ValueData,
346 IN ULONG ValueLength,
347 IN PVOID Context,
348 IN PVOID EntryContext)
349 {
350 UNICODE_STRING ValueNameString;
351 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
352 /* Unicode strings table */
353 PUNICODE_STRING ReturnString = EntryContext;
354
355 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
356 (UniqueId->UniqueIdLength != ValueLength))
357 {
358 return STATUS_SUCCESS;
359 }
360
361 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
362 {
363 return STATUS_SUCCESS;
364 }
365
366 /* Unique ID matches, let's put the symlink */
367 RtlInitUnicodeString(&ValueNameString, ValueName);
368 if (!ValueNameString.Length)
369 {
370 return STATUS_SUCCESS;
371 }
372
373 /* Allocate string to copy */
374 ValueNameString.Buffer = AllocatePool(ValueNameString.MaximumLength);
375 if (!ValueNameString.Buffer)
376 {
377 return STATUS_SUCCESS;
378 }
379
380 /* Copy */
381 RtlCopyMemory(ValueNameString.Buffer, ValueName, ValueNameString.Length);
382 ValueNameString.Buffer[ValueNameString.Length / sizeof(WCHAR)] = UNICODE_NULL;
383
384 while (ReturnString->Length)
385 {
386 ReturnString++;
387 }
388
389 /* And return that string */
390 *ReturnString = ValueNameString;
391
392 return STATUS_SUCCESS;
393 }
394
395 /*
396 * @implemented
397 */
398 NTSTATUS
CreateNewVolumeName(OUT PUNICODE_STRING VolumeName,IN PGUID VolumeGuid OPTIONAL)399 CreateNewVolumeName(OUT PUNICODE_STRING VolumeName,
400 IN PGUID VolumeGuid OPTIONAL)
401 {
402 GUID Guid;
403 NTSTATUS Status;
404 UNICODE_STRING GuidString;
405
406 /* If no GUID was provided, then create one */
407 if (!VolumeGuid)
408 {
409 Status = ExUuidCreate(&Guid);
410 if (!NT_SUCCESS(Status))
411 {
412 return Status;
413 }
414 }
415 else
416 {
417 RtlCopyMemory(&Guid, VolumeGuid, sizeof(GUID));
418 }
419
420 /* Convert GUID to string */
421 Status = RtlStringFromGUID(&Guid, &GuidString);
422 if (!NT_SUCCESS(Status))
423 {
424 return Status;
425 }
426
427 /* Size for volume namespace, literal GUID, and null char */
428 VolumeName->MaximumLength = 0x14 + 0x4C + sizeof(UNICODE_NULL);
429 VolumeName->Buffer = AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL));
430 if (!VolumeName->Buffer)
431 {
432 Status = STATUS_INSUFFICIENT_RESOURCES;
433 }
434 else
435 {
436 RtlCopyUnicodeString(VolumeName, &Volume);
437 RtlAppendUnicodeStringToString(VolumeName, &GuidString);
438 VolumeName->Buffer[VolumeName->Length / sizeof(WCHAR)] = UNICODE_NULL;
439 Status = STATUS_SUCCESS;
440 }
441
442 ExFreePoolWithTag(GuidString.Buffer, 0);
443
444 return Status;
445 }
446
447 /*
448 * @implemented
449 */
450 NTSTATUS
QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension,IN PDEVICE_INFORMATION DeviceInformation,IN PUNICODE_STRING SuggestedLinkName,IN BOOLEAN UseOnlyIfThereAreNoOtherLinks,OUT PUNICODE_STRING * SymLinks,OUT PULONG SymLinkCount,IN BOOLEAN HasGuid,IN LPGUID Guid)451 QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension,
452 IN PDEVICE_INFORMATION DeviceInformation,
453 IN PUNICODE_STRING SuggestedLinkName,
454 IN BOOLEAN UseOnlyIfThereAreNoOtherLinks,
455 OUT PUNICODE_STRING * SymLinks,
456 OUT PULONG SymLinkCount,
457 IN BOOLEAN HasGuid,
458 IN LPGUID Guid)
459 {
460 NTSTATUS Status;
461 BOOLEAN WriteNew;
462 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
463
464 UNREFERENCED_PARAMETER(DeviceExtension);
465
466 /* First of all, count links */
467 RtlZeroMemory(QueryTable, sizeof(QueryTable));
468 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
469 QueryTable[0].EntryContext = SymLinkCount;
470 *SymLinkCount = 0;
471
472 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
473 DatabasePath,
474 QueryTable,
475 DeviceInformation->UniqueId,
476 NULL);
477 if (!NT_SUCCESS(Status))
478 {
479 *SymLinkCount = 0;
480 }
481
482 /* Check if we have to write a new one first */
483 if (SuggestedLinkName && !IsDriveLetter(SuggestedLinkName) &&
484 UseOnlyIfThereAreNoOtherLinks && *SymLinkCount == 0)
485 {
486 WriteNew = TRUE;
487 }
488 else
489 {
490 WriteNew = FALSE;
491 }
492
493 /* If has GUID, it makes one more link */
494 if (HasGuid)
495 {
496 (*SymLinkCount)++;
497 }
498
499 if (WriteNew)
500 {
501 /* Write link */
502 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
503 DatabasePath,
504 SuggestedLinkName->Buffer,
505 REG_BINARY,
506 DeviceInformation->UniqueId->UniqueId,
507 DeviceInformation->UniqueId->UniqueIdLength);
508
509 /* And recount all the needed links */
510 RtlZeroMemory(QueryTable, sizeof(QueryTable));
511 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
512 QueryTable[0].EntryContext = SymLinkCount;
513 *SymLinkCount = 0;
514
515 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
516 DatabasePath,
517 QueryTable,
518 DeviceInformation->UniqueId,
519 NULL);
520 if (!NT_SUCCESS(Status))
521 {
522 return STATUS_NOT_FOUND;
523 }
524 }
525
526 /* Not links found? */
527 if (!*SymLinkCount)
528 {
529 return STATUS_NOT_FOUND;
530 }
531
532 /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */
533 *SymLinks = AllocatePool(*SymLinkCount * sizeof(UNICODE_STRING));
534 if (!*SymLinks)
535 {
536 return STATUS_INSUFFICIENT_RESOURCES;
537 }
538
539 /* Prepare to query links */
540 RtlZeroMemory(*SymLinks, *SymLinkCount * sizeof(UNICODE_STRING));
541 RtlZeroMemory(QueryTable, sizeof(QueryTable));
542 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdQuery;
543
544 /* No GUID? Keep it that way */
545 if (!HasGuid)
546 {
547 QueryTable[0].EntryContext = *SymLinks;
548 }
549 /* Otherwise, first create volume name */
550 else
551 {
552 Status = CreateNewVolumeName(SymLinks[0], Guid);
553 if (!NT_SUCCESS(Status))
554 {
555 FreePool(*SymLinks);
556 return Status;
557 }
558
559 /* Skip first link (ours) */
560 QueryTable[0].EntryContext = *SymLinks + 1;
561 }
562
563 /* Now, query */
564 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
565 DatabasePath,
566 QueryTable,
567 DeviceInformation->UniqueId,
568 NULL);
569
570 return STATUS_SUCCESS;
571 }
572
573 /*
574 * @implemented
575 */
576 PSAVED_LINK_INFORMATION
RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension,IN PMOUNTDEV_UNIQUE_ID UniqueId)577 RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension,
578 IN PMOUNTDEV_UNIQUE_ID UniqueId)
579 {
580 PLIST_ENTRY NextEntry;
581 PSAVED_LINK_INFORMATION SavedLinkInformation;
582
583 /* No saved links? Easy! */
584 if (IsListEmpty(&(DeviceExtension->SavedLinksListHead)))
585 {
586 return NULL;
587 }
588
589 /* Now, browse saved links */
590 for (NextEntry = DeviceExtension->SavedLinksListHead.Flink;
591 NextEntry != &(DeviceExtension->SavedLinksListHead);
592 NextEntry = NextEntry->Flink)
593 {
594 SavedLinkInformation = CONTAINING_RECORD(NextEntry,
595 SAVED_LINK_INFORMATION,
596 SavedLinksListEntry);
597
598 /* Find the one that matches */
599 if (SavedLinkInformation->UniqueId->UniqueIdLength == UniqueId->UniqueIdLength)
600 {
601 if (RtlCompareMemory(SavedLinkInformation->UniqueId->UniqueId,
602 UniqueId->UniqueId,
603 UniqueId->UniqueIdLength) ==
604 UniqueId->UniqueIdLength)
605 {
606 /* Remove it and return it */
607 RemoveEntryList(&(SavedLinkInformation->SavedLinksListEntry));
608 return SavedLinkInformation;
609 }
610 }
611 }
612
613 /* None found (none removed) */
614 return NULL;
615 }
616
617 /*
618 * @implemented
619 */
620 NTSTATUS
QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName,OUT PUNICODE_STRING SuggestedLinkName,OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks)621 QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName,
622 OUT PUNICODE_STRING SuggestedLinkName,
623 OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks)
624 {
625 NTSTATUS Status;
626 USHORT NameLength;
627 PFILE_OBJECT FileObject;
628 PDEVICE_OBJECT DeviceObject;
629 PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested;
630
631 /* First, get device */
632 Status = IoGetDeviceObjectPointer(SymbolicName,
633 FILE_READ_ATTRIBUTES,
634 &FileObject,
635 &DeviceObject);
636 if (!NT_SUCCESS(Status))
637 {
638 return Status;
639 }
640
641 /* Then, get attached device */
642 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
643
644 /* Then, prepare buffer to query suggested name */
645 IoCtlSuggested = AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME));
646 if (!IoCtlSuggested)
647 {
648 Status = STATUS_INSUFFICIENT_RESOURCES;
649 goto Dereference;
650 }
651
652 /* Prepare request */
653 Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,
654 DeviceObject,
655 NULL,
656 0,
657 IoCtlSuggested,
658 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME),
659 FileObject);
660 /* Retry with appropriate length */
661 if (Status == STATUS_BUFFER_OVERFLOW)
662 {
663 /* Reallocate big enough buffer */
664 NameLength = IoCtlSuggested->NameLength + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
665 FreePool(IoCtlSuggested);
666
667 IoCtlSuggested = AllocatePool(NameLength);
668 if (!IoCtlSuggested)
669 {
670 Status = STATUS_INSUFFICIENT_RESOURCES;
671 goto Dereference;
672 }
673
674 /* And re-ask */
675 Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,
676 DeviceObject,
677 NULL,
678 0,
679 IoCtlSuggested,
680 NameLength,
681 FileObject);
682 }
683 if (!NT_SUCCESS(Status))
684 goto Release;
685
686 /* Now we have suggested name, copy it */
687 SuggestedLinkName->Length = IoCtlSuggested->NameLength;
688 SuggestedLinkName->MaximumLength = IoCtlSuggested->NameLength + sizeof(UNICODE_NULL);
689 SuggestedLinkName->Buffer = AllocatePool(IoCtlSuggested->NameLength + sizeof(UNICODE_NULL));
690 if (!SuggestedLinkName->Buffer)
691 {
692 Status = STATUS_INSUFFICIENT_RESOURCES;
693 }
694 else
695 {
696 RtlCopyMemory(SuggestedLinkName->Buffer, IoCtlSuggested->Name, IoCtlSuggested->NameLength);
697 SuggestedLinkName->Buffer[SuggestedLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL;
698 }
699
700 /* Also return its priority */
701 *UseOnlyIfThereAreNoOtherLinks = IoCtlSuggested->UseOnlyIfThereAreNoOtherLinks;
702
703 Release:
704 FreePool(IoCtlSuggested);
705
706 Dereference:
707 ObDereferenceObject(DeviceObject);
708 ObDereferenceObject(FileObject);
709
710 return Status;
711 }
712
713 /*
714 * @implemented
715 */
716 BOOLEAN
RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation,IN PUNICODE_STRING DosName,IN PUNICODE_STRING NewLink)717 RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation,
718 IN PUNICODE_STRING DosName,
719 IN PUNICODE_STRING NewLink)
720 {
721 PLIST_ENTRY NextEntry;
722 PSYMLINK_INFORMATION SymlinkInformation;
723
724 /* Find the link */
725 for (NextEntry = SavedLinkInformation->SymbolicLinksListHead.Flink;
726 NextEntry != &(SavedLinkInformation->SymbolicLinksListHead);
727 NextEntry = NextEntry->Flink)
728 {
729 SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
730
731 if (!RtlEqualUnicodeString(DosName, &(SymlinkInformation->Name), TRUE))
732 {
733 /* Delete old link */
734 GlobalDeleteSymbolicLink(DosName);
735 /* Set its new location */
736 GlobalCreateSymbolicLink(DosName, NewLink);
737
738 /* And remove it from the list (not valid any more) */
739 RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry));
740 FreePool(SymlinkInformation->Name.Buffer);
741 FreePool(SymlinkInformation);
742
743 return TRUE;
744 }
745 }
746
747 return FALSE;
748 }
749
750 /*
751 * @implemented
752 */
753 VOID
DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension,IN PUNICODE_STRING SymbolicLink,IN BOOLEAN MarkOffline)754 DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension,
755 IN PUNICODE_STRING SymbolicLink,
756 IN BOOLEAN MarkOffline)
757 {
758 PLIST_ENTRY DeviceEntry, SymbolEntry;
759 PDEVICE_INFORMATION DeviceInformation;
760 PSYMLINK_INFORMATION SymlinkInformation;
761
762 /* First of all, ensure we have devices */
763 if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
764 {
765 return;
766 }
767
768 /* Then, look for the symbolic name */
769 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
770 DeviceEntry != &(DeviceExtension->DeviceListHead);
771 DeviceEntry = DeviceEntry->Flink)
772 {
773 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
774
775 for (SymbolEntry = DeviceInformation->SymbolicLinksListHead.Flink;
776 SymbolEntry != &(DeviceInformation->SymbolicLinksListHead);
777 SymbolEntry = SymbolEntry->Flink)
778 {
779 SymlinkInformation = CONTAINING_RECORD(SymbolEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
780
781 /* One we have found it */
782 if (RtlCompareUnicodeString(SymbolicLink, &(SymlinkInformation->Name), TRUE) == 0)
783 {
784 /* Check if caller just want it to be offline */
785 if (MarkOffline)
786 {
787 SymlinkInformation->Online = FALSE;
788 }
789 else
790 {
791 /* If not, delete it & notify */
792 SendLinkDeleted(&(DeviceInformation->SymbolicName), SymbolicLink);
793 RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry));
794
795 FreePool(SymlinkInformation->Name.Buffer);
796 FreePool(SymlinkInformation);
797 }
798
799 /* No need to go farther */
800 return;
801 }
802 }
803 }
804
805 return;
806 }
807
808 /*
809 * @implemented
810 */
811 BOOLEAN
IsDriveLetter(PUNICODE_STRING SymbolicName)812 IsDriveLetter(PUNICODE_STRING SymbolicName)
813 {
814 WCHAR Letter, Colon;
815
816 /* We must have a precise length */
817 if (SymbolicName->Length != DosDevices.Length + 2 * sizeof(WCHAR))
818 {
819 return FALSE;
820 }
821
822 /* Must start with the DosDevices prefix */
823 if (!RtlPrefixUnicodeString(&DosDevices, SymbolicName, TRUE))
824 {
825 return FALSE;
826 }
827
828 /* Check if letter is correct */
829 Letter = SymbolicName->Buffer[DosDevices.Length / sizeof(WCHAR)];
830 if ((Letter < L'A' || Letter > L'Z') && Letter != (WCHAR)-1)
831 {
832 return FALSE;
833 }
834
835 /* And finally it must end with a colon */
836 Colon = SymbolicName->Buffer[DosDevices.Length / sizeof(WCHAR) + 1];
837 if (Colon != L':')
838 {
839 return FALSE;
840 }
841
842 return TRUE;
843 }
844
845 /*
846 * @implemented
847 */
848 NTSTATUS
MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName,IN OUT PUNICODE_STRING LinkTarget)849 MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName,
850 IN OUT PUNICODE_STRING LinkTarget)
851 {
852 NTSTATUS Status;
853 HANDLE LinkHandle;
854 OBJECT_ATTRIBUTES ObjectAttributes;
855
856 /* Open the symbolic link */
857 InitializeObjectAttributes(&ObjectAttributes,
858 SymbolicName,
859 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
860 NULL,
861 NULL);
862
863 Status = ZwOpenSymbolicLinkObject(&LinkHandle,
864 GENERIC_READ,
865 &ObjectAttributes);
866 if (!NT_SUCCESS(Status))
867 {
868 return Status;
869 }
870
871 /* Query its target */
872 Status = ZwQuerySymbolicLinkObject(LinkHandle,
873 LinkTarget,
874 NULL);
875
876 ZwClose(LinkHandle);
877
878 if (!NT_SUCCESS(Status))
879 {
880 return Status;
881 }
882
883 if (LinkTarget->Length <= sizeof(WCHAR))
884 {
885 return Status;
886 }
887
888 /* If it's not finished by \, just return */
889 if (LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR) - 1] != L'\\')
890 {
891 return Status;
892 }
893
894 /* Otherwise, ensure to drop the tailing \ */
895 LinkTarget->Length -= sizeof(WCHAR);
896 LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR)] = UNICODE_NULL;
897
898 return Status;
899 }
900