[MOUNTMGR] Don't kill Mm when a device has several symlinks
[reactos.git] / drivers / filters / mountmgr / point.c
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/point.c
22 * PURPOSE: Mount Manager - Mount points
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 MountMgrCreatePointWorker(IN PDEVICE_EXTENSION DeviceExtension,
36 IN PUNICODE_STRING SymbolicLinkName,
37 IN PUNICODE_STRING DeviceName)
38 {
39 NTSTATUS Status;
40 PLIST_ENTRY DeviceEntry;
41 PMOUNTDEV_UNIQUE_ID UniqueId;
42 PSYMLINK_INFORMATION SymlinkInformation;
43 UNICODE_STRING SymLink, TargetDeviceName;
44 PDEVICE_INFORMATION DeviceInformation = NULL, DeviceInfo;
45
46 /* Get device name */
47 Status = QueryDeviceInformation(DeviceName,
48 &TargetDeviceName,
49 NULL, NULL, NULL,
50 NULL, NULL, NULL);
51 if (!NT_SUCCESS(Status))
52 {
53 return Status;
54 }
55
56 /* First of all, try to find device */
57 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
58 DeviceEntry != &(DeviceExtension->DeviceListHead);
59 DeviceEntry = DeviceEntry->Flink)
60 {
61 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
62
63 if (RtlEqualUnicodeString(&TargetDeviceName, &(DeviceInformation->DeviceName), TRUE))
64 {
65 break;
66 }
67 }
68
69 /* Copy symbolic link name and null terminate it */
70 SymLink.Buffer = AllocatePool(SymbolicLinkName->Length + sizeof(UNICODE_NULL));
71 if (!SymLink.Buffer)
72 {
73 FreePool(TargetDeviceName.Buffer);
74 return STATUS_INSUFFICIENT_RESOURCES;
75 }
76
77 RtlCopyMemory(SymLink.Buffer, SymbolicLinkName->Buffer, SymbolicLinkName->Length);
78 SymLink.Buffer[SymbolicLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL;
79 SymLink.Length = SymbolicLinkName->Length;
80 SymLink.MaximumLength = SymbolicLinkName->Length + sizeof(UNICODE_NULL);
81
82 /* If we didn't find device */
83 if (DeviceEntry == &(DeviceExtension->DeviceListHead))
84 {
85 /* Then, try with unique ID */
86 Status = QueryDeviceInformation(SymbolicLinkName,
87 NULL, &UniqueId,
88 NULL, NULL, NULL,
89 NULL, NULL);
90 if (!NT_SUCCESS(Status))
91 {
92 FreePool(TargetDeviceName.Buffer);
93 FreePool(SymLink.Buffer);
94 return Status;
95 }
96
97 /* Create a link to the device */
98 Status = GlobalCreateSymbolicLink(&SymLink, &TargetDeviceName);
99 if (!NT_SUCCESS(Status))
100 {
101 FreePool(UniqueId);
102 FreePool(TargetDeviceName.Buffer);
103 FreePool(SymLink.Buffer);
104 return Status;
105 }
106
107 /* If caller provided driver letter, delete it */
108 if (IsDriveLetter(&SymLink))
109 {
110 DeleteRegistryDriveLetter(UniqueId);
111 }
112
113 /* Device will be identified with its unique ID */
114 Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
115 DatabasePath,
116 SymLink.Buffer,
117 REG_BINARY,
118 UniqueId->UniqueId,
119 UniqueId->UniqueIdLength);
120
121 FreePool(UniqueId);
122 FreePool(TargetDeviceName.Buffer);
123 FreePool(SymLink.Buffer);
124 return Status;
125 }
126
127 /* If call provided a driver letter whereas device already has one
128 * fail, this is not doable
129 */
130 if (IsDriveLetter(&SymLink) && HasDriveLetter(DeviceInformation))
131 {
132 FreePool(TargetDeviceName.Buffer);
133 FreePool(SymLink.Buffer);
134 return STATUS_INVALID_PARAMETER;
135 }
136
137 /* Now, create a link */
138 Status = GlobalCreateSymbolicLink(&SymLink, &TargetDeviceName);
139 FreePool(TargetDeviceName.Buffer);
140 if (!NT_SUCCESS(Status))
141 {
142 FreePool(SymLink.Buffer);
143 return Status;
144 }
145
146 /* Associate Unique ID <-> symbolic name */
147 UniqueId = DeviceInformation->UniqueId;
148 Status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
149 DatabasePath,
150 SymLink.Buffer,
151 REG_BINARY,
152 UniqueId->UniqueId,
153 UniqueId->UniqueIdLength);
154 if (!NT_SUCCESS(Status))
155 {
156 GlobalDeleteSymbolicLink(&SymLink);
157 FreePool(SymLink.Buffer);
158 return Status;
159 }
160
161 /* Now, prepare to save the link with the device */
162 SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
163 if (!SymlinkInformation)
164 {
165 Status = STATUS_INSUFFICIENT_RESOURCES;
166 GlobalDeleteSymbolicLink(&SymLink);
167 FreePool(SymLink.Buffer);
168 return Status;
169 }
170
171 SymlinkInformation->Name.Length = SymLink.Length;
172 SymlinkInformation->Name.MaximumLength = SymLink.Length + sizeof(UNICODE_NULL);
173 SymlinkInformation->Name.Buffer = AllocatePool(SymlinkInformation->Name.MaximumLength);
174 if (!SymlinkInformation->Name.Buffer)
175 {
176 Status = STATUS_INSUFFICIENT_RESOURCES;
177 FreePool(SymlinkInformation);
178 GlobalDeleteSymbolicLink(&SymLink);
179 FreePool(SymLink.Buffer);
180 return Status;
181 }
182
183 /* Save the link and mark it online */
184 RtlCopyMemory(SymlinkInformation->Name.Buffer, SymLink.Buffer, SymlinkInformation->Name.Length);
185 SymlinkInformation->Name.Buffer[SymlinkInformation->Name.Length / sizeof(WCHAR)] = UNICODE_NULL;
186 SymlinkInformation->Online = TRUE;
187 InsertTailList(&DeviceInformation->SymbolicLinksListHead, &SymlinkInformation->SymbolicLinksListEntry);
188 SendLinkCreated(&(SymlinkInformation->Name));
189
190 /* If we have a drive letter */
191 if (IsDriveLetter(&SymLink))
192 {
193 /* Then, delete the no drive letter entry */
194 DeleteNoDriveLetterEntry(UniqueId);
195
196 /* And post online notification if asked */
197 if (!DeviceInformation->SkipNotifications)
198 {
199 PostOnlineNotification(DeviceExtension, &DeviceInformation->SymbolicName);
200 }
201 }
202
203 /* If that's a volume with automatic drive letter, it's now time to resync databases */
204 if (MOUNTMGR_IS_VOLUME_NAME(&SymLink) && DeviceExtension->AutomaticDriveLetter)
205 {
206 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
207 DeviceEntry != &(DeviceExtension->DeviceListHead);
208 DeviceEntry = DeviceEntry->Flink)
209 {
210 DeviceInfo = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
211
212 /* If there's one, ofc! */
213 if (!DeviceInfo->NoDatabase)
214 {
215 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInfo);
216 }
217 }
218 }
219
220 /* Notify & quit */
221 FreePool(SymLink.Buffer);
222 MountMgrNotify(DeviceExtension);
223
224 if (!DeviceInformation->ManuallyRegistered)
225 {
226 MountMgrNotifyNameChange(DeviceExtension, DeviceName, FALSE);
227 }
228
229 return Status;
230 }
231
232 /*
233 * @implemented
234 */
235 NTSTATUS
236 QueryPointsFromMemory(IN PDEVICE_EXTENSION DeviceExtension,
237 IN PIRP Irp,
238 IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL,
239 IN PUNICODE_STRING SymbolicName OPTIONAL)
240 {
241 NTSTATUS Status;
242 PIO_STACK_LOCATION Stack;
243 UNICODE_STRING DeviceName;
244 PMOUNTMGR_MOUNT_POINTS MountPoints;
245 PDEVICE_INFORMATION DeviceInformation;
246 PLIST_ENTRY DeviceEntry, SymlinksEntry;
247 PSYMLINK_INFORMATION SymlinkInformation;
248 USHORT UniqueIdLength, DeviceNameLength;
249 ULONG TotalSize, TotalSymLinks, UniqueIdOffset, DeviceNameOffset;
250
251 /* If we got a symbolic link, query device */
252 if (SymbolicName)
253 {
254 Status = QueryDeviceInformation(SymbolicName,
255 &DeviceName,
256 NULL, NULL,
257 NULL, NULL,
258 NULL, NULL);
259 if (!NT_SUCCESS(Status))
260 {
261 return Status;
262 }
263 }
264
265 /* Browse all the links to count number of links & size used */
266 TotalSize = 0;
267 TotalSymLinks = 0;
268 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
269 DeviceEntry != &(DeviceExtension->DeviceListHead);
270 DeviceEntry = DeviceEntry->Flink)
271 {
272 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
273
274 /* If we were given an unique ID, it has to match */
275 if (UniqueId)
276 {
277 if (UniqueId->UniqueIdLength != DeviceInformation->UniqueId->UniqueIdLength)
278 {
279 continue;
280 }
281
282 if (RtlCompareMemory(UniqueId->UniqueId,
283 DeviceInformation->UniqueId->UniqueId,
284 UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength)
285 {
286 continue;
287 }
288 }
289 /* Or, if we had a symlink, it has to match */
290 else if (SymbolicName)
291 {
292 if (!RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE))
293 {
294 continue;
295 }
296 }
297
298 /* Once here, it matched, save device name & unique ID size */
299 TotalSize += DeviceInformation->DeviceName.Length + DeviceInformation->UniqueId->UniqueIdLength;
300
301 /* And count number of symlinks (and their size) */
302 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
303 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
304 SymlinksEntry = SymlinksEntry->Flink)
305 {
306 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
307
308 TotalSize += SymlinkInformation->Name.Length;
309 TotalSymLinks++;
310 }
311
312 /* We had a specific item to find
313 * if we reach that point, we found it, no need to continue
314 */
315 if (UniqueId || SymbolicName)
316 {
317 break;
318 }
319 }
320
321 /* If we were looking for specific item, ensure we found it */
322 if (UniqueId || SymbolicName)
323 {
324 if (DeviceEntry == &(DeviceExtension->DeviceListHead))
325 {
326 if (SymbolicName)
327 {
328 FreePool(DeviceName.Buffer);
329 }
330
331 return STATUS_INVALID_PARAMETER;
332 }
333 }
334
335 /* Now, ensure output buffer can hold everything */
336 Stack = IoGetCurrentIrpStackLocation(Irp);
337 MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
338
339 /* Ensure we set output to let user reallocate! */
340 MountPoints->Size = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalSymLinks * sizeof(MOUNTMGR_MOUNT_POINT) + TotalSize;
341 MountPoints->NumberOfMountPoints = TotalSymLinks;
342 Irp->IoStatus.Information = MountPoints->Size;
343
344 if (MountPoints->Size > Stack->Parameters.DeviceIoControl.OutputBufferLength)
345 {
346 Irp->IoStatus.Information = sizeof(MOUNTMGR_MOUNT_POINTS);
347
348 if (SymbolicName)
349 {
350 FreePool(DeviceName.Buffer);
351 }
352
353 return STATUS_BUFFER_OVERFLOW;
354 }
355
356 /* Now, start putting mount points */
357 TotalSize = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalSymLinks * sizeof(MOUNTMGR_MOUNT_POINT);
358 TotalSymLinks = 0;
359 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
360 DeviceEntry != &(DeviceExtension->DeviceListHead);
361 DeviceEntry = DeviceEntry->Flink)
362 {
363 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
364
365 /* Find back correct mount point */
366 if (UniqueId)
367 {
368 if (UniqueId->UniqueIdLength != DeviceInformation->UniqueId->UniqueIdLength)
369 {
370 continue;
371 }
372
373 if (RtlCompareMemory(UniqueId->UniqueId,
374 DeviceInformation->UniqueId->UniqueId,
375 UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength)
376 {
377 continue;
378 }
379 }
380 else if (SymbolicName)
381 {
382 if (!RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE))
383 {
384 continue;
385 }
386 }
387
388 /* Save our information about shared data */
389 UniqueIdOffset = TotalSize;
390 UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
391 DeviceNameOffset = TotalSize + UniqueIdLength;
392 DeviceNameLength = DeviceInformation->DeviceName.Length;
393
394 /* Initialize first symlink */
395 MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset = UniqueIdOffset;
396 MountPoints->MountPoints[TotalSymLinks].UniqueIdLength = UniqueIdLength;
397 MountPoints->MountPoints[TotalSymLinks].DeviceNameOffset = DeviceNameOffset;
398 MountPoints->MountPoints[TotalSymLinks].DeviceNameLength = DeviceNameLength;
399
400 /* And copy data */
401 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + UniqueIdOffset),
402 DeviceInformation->UniqueId->UniqueId, UniqueIdLength);
403 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + DeviceNameOffset),
404 DeviceInformation->DeviceName.Buffer, DeviceNameLength);
405
406 TotalSize += DeviceInformation->UniqueId->UniqueIdLength + DeviceInformation->DeviceName.Length;
407
408 /* Now we've got it, but all the data */
409 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
410 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
411 SymlinksEntry = SymlinksEntry->Flink)
412 {
413 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
414
415 /* First, set shared data */
416
417 /* Only put UniqueID if online */
418 if (SymlinkInformation->Online)
419 {
420 MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset = UniqueIdOffset;
421 MountPoints->MountPoints[TotalSymLinks].UniqueIdLength = UniqueIdLength;
422 }
423 else
424 {
425 MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset = 0;
426 MountPoints->MountPoints[TotalSymLinks].UniqueIdLength = 0;
427 }
428
429 MountPoints->MountPoints[TotalSymLinks].DeviceNameOffset = DeviceNameOffset;
430 MountPoints->MountPoints[TotalSymLinks].DeviceNameLength = DeviceNameLength;
431
432 /* And now, copy specific symlink info */
433 MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameOffset = TotalSize;
434 MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameLength = SymlinkInformation->Name.Length;
435
436 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameOffset),
437 SymlinkInformation->Name.Buffer, SymlinkInformation->Name.Length);
438
439 /* Update counters */
440 TotalSymLinks++;
441 TotalSize += SymlinkInformation->Name.Length;
442 }
443
444 if (UniqueId || SymbolicName)
445 {
446 break;
447 }
448 }
449
450 if (SymbolicName)
451 {
452 FreePool(DeviceName.Buffer);
453 }
454
455 return STATUS_SUCCESS;
456 }
457
458 /*
459 * @implemented
460 */
461 NTSTATUS
462 QueryPointsFromSymbolicLinkName(IN PDEVICE_EXTENSION DeviceExtension,
463 IN PUNICODE_STRING SymbolicName,
464 IN PIRP Irp)
465 {
466 NTSTATUS Status;
467 ULONG TotalLength;
468 PIO_STACK_LOCATION Stack;
469 UNICODE_STRING DeviceName;
470 PMOUNTMGR_MOUNT_POINTS MountPoints;
471 PDEVICE_INFORMATION DeviceInformation = NULL;
472 PLIST_ENTRY DeviceEntry, SymlinksEntry;
473 PSYMLINK_INFORMATION SymlinkInformation;
474
475 /* Find device */
476 Status = QueryDeviceInformation(SymbolicName, &DeviceName,
477 NULL, NULL, NULL,
478 NULL, NULL, NULL);
479 if (NT_SUCCESS(Status))
480 {
481 /* Look for the device information */
482 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
483 DeviceEntry != &(DeviceExtension->DeviceListHead);
484 DeviceEntry = DeviceEntry->Flink)
485 {
486 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
487
488 if (RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE))
489 {
490 break;
491 }
492 }
493
494 FreePool(DeviceName.Buffer);
495
496 if (DeviceEntry == &(DeviceExtension->DeviceListHead))
497 {
498 return STATUS_INVALID_PARAMETER;
499 }
500
501 /* Check for the link */
502 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
503 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
504 SymlinksEntry = SymlinksEntry->Flink)
505 {
506 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
507
508 if (RtlEqualUnicodeString(SymbolicName, &SymlinkInformation->Name, TRUE))
509 {
510 break;
511 }
512 }
513
514 if (SymlinksEntry == &(DeviceInformation->SymbolicLinksListHead))
515 {
516 return STATUS_INVALID_PARAMETER;
517 }
518 }
519 else
520 {
521 /* Browse all the devices to try to find the one
522 * that has the given link...
523 */
524 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
525 DeviceEntry != &(DeviceExtension->DeviceListHead);
526 DeviceEntry = DeviceEntry->Flink)
527 {
528 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
529
530 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
531 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
532 SymlinksEntry = SymlinksEntry->Flink)
533 {
534 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
535
536 if (RtlEqualUnicodeString(SymbolicName, &SymlinkInformation->Name, TRUE))
537 {
538 break;
539 }
540 }
541
542 if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead))
543 {
544 break;
545 }
546 }
547
548 /* Even that way we didn't find, give up! */
549 if (DeviceEntry == &(DeviceExtension->DeviceListHead))
550 {
551 return STATUS_OBJECT_NAME_NOT_FOUND;
552 }
553 }
554
555 /* Get output buffer */
556 Stack = IoGetCurrentIrpStackLocation(Irp);
557 MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
558
559 /* Compute output length */
560 TotalLength = DeviceInformation->UniqueId->UniqueIdLength +
561 SymlinkInformation->Name.Length + DeviceInformation->DeviceName.Length;
562
563 /* Give length to allow reallocation */
564 MountPoints->Size = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalLength;
565 MountPoints->NumberOfMountPoints = 1;
566 Irp->IoStatus.Information = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalLength;
567
568 if (MountPoints->Size > Stack->Parameters.DeviceIoControl.OutputBufferLength)
569 {
570 Irp->IoStatus.Information = sizeof(MOUNTMGR_MOUNT_POINTS);
571
572 return STATUS_BUFFER_OVERFLOW;
573 }
574
575 /* Write out data */
576 MountPoints->MountPoints[0].SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS);
577 MountPoints->MountPoints[0].SymbolicLinkNameLength = SymlinkInformation->Name.Length;
578 /* If link is online write it's unique ID, otherwise, forget about it */
579 if (SymlinkInformation->Online)
580 {
581 MountPoints->MountPoints[0].UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINTS) +
582 SymlinkInformation->Name.Length;
583 MountPoints->MountPoints[0].UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength;
584 }
585 else
586 {
587 MountPoints->MountPoints[0].UniqueIdOffset = 0;
588 MountPoints->MountPoints[0].UniqueIdLength = 0;
589 }
590
591 MountPoints->MountPoints[0].DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS) +
592 SymlinkInformation->Name.Length +
593 DeviceInformation->UniqueId->UniqueIdLength;
594 MountPoints->MountPoints[0].DeviceNameLength = DeviceInformation->DeviceName.Length;
595
596 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].SymbolicLinkNameOffset),
597 SymlinkInformation->Name.Buffer, SymlinkInformation->Name.Length);
598
599 if (SymlinkInformation->Online)
600 {
601 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].UniqueIdOffset),
602 DeviceInformation->UniqueId->UniqueId, DeviceInformation->UniqueId->UniqueIdLength);
603 }
604
605 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].DeviceNameOffset),
606 DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length);
607
608 return STATUS_SUCCESS;
609 }