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 RtlZeroMemory(MountPoints, Stack->Parameters.DeviceIoControl.OutputBufferLength); 339 340 /* Ensure we set output to let user reallocate! */ 341 MountPoints->Size = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalSymLinks * sizeof(MOUNTMGR_MOUNT_POINT) + TotalSize; 342 MountPoints->NumberOfMountPoints = TotalSymLinks; 343 Irp->IoStatus.Information = MountPoints->Size; 344 345 if (MountPoints->Size > Stack->Parameters.DeviceIoControl.OutputBufferLength) 346 { 347 Irp->IoStatus.Information = sizeof(MOUNTMGR_MOUNT_POINTS); 348 349 if (SymbolicName) 350 { 351 FreePool(DeviceName.Buffer); 352 } 353 354 return STATUS_BUFFER_OVERFLOW; 355 } 356 357 /* Now, start putting mount points */ 358 TotalSize = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalSymLinks * sizeof(MOUNTMGR_MOUNT_POINT); 359 TotalSymLinks = 0; 360 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; 361 DeviceEntry != &(DeviceExtension->DeviceListHead); 362 DeviceEntry = DeviceEntry->Flink) 363 { 364 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); 365 366 /* Find back correct mount point */ 367 if (UniqueId) 368 { 369 if (UniqueId->UniqueIdLength != DeviceInformation->UniqueId->UniqueIdLength) 370 { 371 continue; 372 } 373 374 if (RtlCompareMemory(UniqueId->UniqueId, 375 DeviceInformation->UniqueId->UniqueId, 376 UniqueId->UniqueIdLength) != UniqueId->UniqueIdLength) 377 { 378 continue; 379 } 380 } 381 else if (SymbolicName) 382 { 383 if (!RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE)) 384 { 385 continue; 386 } 387 } 388 389 /* Save our information about shared data */ 390 UniqueIdOffset = TotalSize; 391 UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength; 392 DeviceNameOffset = TotalSize + UniqueIdLength; 393 DeviceNameLength = DeviceInformation->DeviceName.Length; 394 395 /* Initialize first symlink */ 396 MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset = UniqueIdOffset; 397 MountPoints->MountPoints[TotalSymLinks].UniqueIdLength = UniqueIdLength; 398 MountPoints->MountPoints[TotalSymLinks].DeviceNameOffset = DeviceNameOffset; 399 MountPoints->MountPoints[TotalSymLinks].DeviceNameLength = DeviceNameLength; 400 401 /* And copy data */ 402 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + UniqueIdOffset), 403 DeviceInformation->UniqueId->UniqueId, UniqueIdLength); 404 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + DeviceNameOffset), 405 DeviceInformation->DeviceName.Buffer, DeviceNameLength); 406 407 TotalSize += DeviceInformation->UniqueId->UniqueIdLength + DeviceInformation->DeviceName.Length; 408 409 /* Now we've got it, but all the data */ 410 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink; 411 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead); 412 SymlinksEntry = SymlinksEntry->Flink) 413 { 414 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); 415 416 /* First, set shared data */ 417 418 /* Only put UniqueID if online */ 419 if (SymlinkInformation->Online) 420 { 421 MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset = UniqueIdOffset; 422 MountPoints->MountPoints[TotalSymLinks].UniqueIdLength = UniqueIdLength; 423 } 424 else 425 { 426 MountPoints->MountPoints[TotalSymLinks].UniqueIdOffset = 0; 427 MountPoints->MountPoints[TotalSymLinks].UniqueIdLength = 0; 428 } 429 430 MountPoints->MountPoints[TotalSymLinks].DeviceNameOffset = DeviceNameOffset; 431 MountPoints->MountPoints[TotalSymLinks].DeviceNameLength = DeviceNameLength; 432 433 /* And now, copy specific symlink info */ 434 MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameOffset = TotalSize; 435 MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameLength = SymlinkInformation->Name.Length; 436 437 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[TotalSymLinks].SymbolicLinkNameOffset), 438 SymlinkInformation->Name.Buffer, SymlinkInformation->Name.Length); 439 440 /* Update counters */ 441 TotalSymLinks++; 442 TotalSize += SymlinkInformation->Name.Length; 443 } 444 445 if (UniqueId || SymbolicName) 446 { 447 break; 448 } 449 } 450 451 if (SymbolicName) 452 { 453 FreePool(DeviceName.Buffer); 454 } 455 456 return STATUS_SUCCESS; 457 } 458 459 /* 460 * @implemented 461 */ 462 NTSTATUS 463 QueryPointsFromSymbolicLinkName(IN PDEVICE_EXTENSION DeviceExtension, 464 IN PUNICODE_STRING SymbolicName, 465 IN PIRP Irp) 466 { 467 NTSTATUS Status; 468 ULONG TotalLength; 469 PIO_STACK_LOCATION Stack; 470 UNICODE_STRING DeviceName; 471 PMOUNTMGR_MOUNT_POINTS MountPoints; 472 PDEVICE_INFORMATION DeviceInformation = NULL; 473 PLIST_ENTRY DeviceEntry, SymlinksEntry; 474 PSYMLINK_INFORMATION SymlinkInformation; 475 476 /* Find device */ 477 Status = QueryDeviceInformation(SymbolicName, &DeviceName, 478 NULL, NULL, NULL, 479 NULL, NULL, NULL); 480 if (NT_SUCCESS(Status)) 481 { 482 /* Look for the device information */ 483 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; 484 DeviceEntry != &(DeviceExtension->DeviceListHead); 485 DeviceEntry = DeviceEntry->Flink) 486 { 487 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); 488 489 if (RtlEqualUnicodeString(&DeviceName, &(DeviceInformation->DeviceName), TRUE)) 490 { 491 break; 492 } 493 } 494 495 FreePool(DeviceName.Buffer); 496 497 if (DeviceEntry == &(DeviceExtension->DeviceListHead)) 498 { 499 return STATUS_INVALID_PARAMETER; 500 } 501 502 /* Check for the link */ 503 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink; 504 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead); 505 SymlinksEntry = SymlinksEntry->Flink) 506 { 507 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); 508 509 if (RtlEqualUnicodeString(SymbolicName, &SymlinkInformation->Name, TRUE)) 510 { 511 break; 512 } 513 } 514 515 if (SymlinksEntry == &(DeviceInformation->SymbolicLinksListHead)) 516 { 517 return STATUS_INVALID_PARAMETER; 518 } 519 } 520 else 521 { 522 /* Browse all the devices to try to find the one 523 * that has the given link... 524 */ 525 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink; 526 DeviceEntry != &(DeviceExtension->DeviceListHead); 527 DeviceEntry = DeviceEntry->Flink) 528 { 529 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry); 530 531 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink; 532 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead); 533 SymlinksEntry = SymlinksEntry->Flink) 534 { 535 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry); 536 537 if (RtlEqualUnicodeString(SymbolicName, &SymlinkInformation->Name, TRUE)) 538 { 539 break; 540 } 541 } 542 543 if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead)) 544 { 545 break; 546 } 547 } 548 549 /* Even that way we didn't find, give up! */ 550 if (DeviceEntry == &(DeviceExtension->DeviceListHead)) 551 { 552 return STATUS_OBJECT_NAME_NOT_FOUND; 553 } 554 } 555 556 /* Get output buffer */ 557 Stack = IoGetCurrentIrpStackLocation(Irp); 558 MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer; 559 560 /* Compute output length */ 561 TotalLength = DeviceInformation->UniqueId->UniqueIdLength + 562 SymlinkInformation->Name.Length + DeviceInformation->DeviceName.Length; 563 564 /* Give length to allow reallocation */ 565 MountPoints->Size = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalLength; 566 MountPoints->NumberOfMountPoints = 1; 567 Irp->IoStatus.Information = sizeof(MOUNTMGR_MOUNT_POINTS) + TotalLength; 568 569 if (MountPoints->Size > Stack->Parameters.DeviceIoControl.OutputBufferLength) 570 { 571 Irp->IoStatus.Information = sizeof(MOUNTMGR_MOUNT_POINTS); 572 573 return STATUS_BUFFER_OVERFLOW; 574 } 575 576 /* Write out data */ 577 MountPoints->MountPoints[0].SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS); 578 MountPoints->MountPoints[0].SymbolicLinkNameLength = SymlinkInformation->Name.Length; 579 /* If link is online write it's unique ID, otherwise, forget about it */ 580 if (SymlinkInformation->Online) 581 { 582 MountPoints->MountPoints[0].UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINTS) + 583 SymlinkInformation->Name.Length; 584 MountPoints->MountPoints[0].UniqueIdLength = DeviceInformation->UniqueId->UniqueIdLength; 585 } 586 else 587 { 588 MountPoints->MountPoints[0].UniqueIdOffset = 0; 589 MountPoints->MountPoints[0].UniqueIdLength = 0; 590 } 591 592 MountPoints->MountPoints[0].DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINTS) + 593 SymlinkInformation->Name.Length + 594 DeviceInformation->UniqueId->UniqueIdLength; 595 MountPoints->MountPoints[0].DeviceNameLength = DeviceInformation->DeviceName.Length; 596 597 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].SymbolicLinkNameOffset), 598 SymlinkInformation->Name.Buffer, SymlinkInformation->Name.Length); 599 600 if (SymlinkInformation->Online) 601 { 602 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].UniqueIdOffset), 603 DeviceInformation->UniqueId->UniqueId, DeviceInformation->UniqueId->UniqueIdLength); 604 } 605 606 RtlCopyMemory((PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[0].DeviceNameOffset), 607 DeviceInformation->DeviceName.Buffer, DeviceInformation->DeviceName.Length); 608 609 return STATUS_SUCCESS; 610 } 611