1 /*
2 vfdmnt.c
3
4 Virtual Floppy Drive for Windows NT platform
5 Kernel mode driver mount manager functions
6
7 Copyright (C) 2003-2005 Ken Kato
8 */
9
10 #ifndef VFD_MOUNT_MANAGER
11 /*
12 Not in working order for the time being
13 so DO NOT define VFD_MOUNT_MANAGER macro
14 unless you know exactly what you are doing...
15 */
16 #if !defined(__REACTOS__) || defined(_MSC_VER)
17 // suppress empty compile unit warning
18 #pragma warning (disable: 4206)
19 #pragma message ("Mount Manager support feature is disabled.")
20 #endif
21
22 #else // VFD_MOUNT_MANAGER
23 /*
24 The flow of the drive letter assignment via the Mount Manager
25 during the VFD driver start up
26
27 1) IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION VFD -> MM
28 notifies the mount manager of VFD devices.
29
30 2) IOCTL_MOUNTDEV_QUERY_DEVICE_NAME VFD <- MM
31 device name (\Device\Floppy<x>) VFD -> MM
32
33 3) IOCTL_MOUNTDEV_QUERY_UNIQUE_ID VFD <- MM
34 device unique ID (\??\VirtualFD<x>) VFD -> MM
35
36 4) IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME VFD <- MM
37 drive letter link (\DosDevices\<x>:) VFD -> MM
38
39 5) The mount manager creates the drive letter link
40
41 6) IOCTL_MOUNTDEV_LINK_CREATED VFD <- MM
42 The driver stores the created drive letter
43
44 The flow of the drive letter operation with IOCTL_VFD_SET_LINK
45
46 1) IOCTL_MOUNTMGR_CREATE_POINT or
47 IOCTL_MOUNTMGR_DELETE_POINTS VFD -> MM
48
49 2) The mount manager creates/deletes the drive letter link
50
51 3) IOCTL_MOUNTDEV_LINK_CREATED or
52 IOCTL_MOUNTDEV_LINK_DELETED VFD <- MM
53 The driver stores the created/deleted drive letter
54 */
55
56 #include "imports.h"
57 #include "vfddrv.h"
58 #include "vfddbg.h"
59
60 //
61 // Call the mount manager with an IO control IRP
62 //
63 static NTSTATUS
64 VfdMountMgrSendRequest(
65 ULONG ControlCode,
66 PVOID InputBuffer,
67 ULONG InputLength,
68 PVOID OutputBuffer,
69 ULONG OutputLength);
70
71 #ifdef ALLOC_PRAGMA
72 //#pragma alloc_text(PAGE, VfdRegisterMountManager)
73 #pragma alloc_text(PAGE, VfdMountMgrNotifyVolume)
74 #pragma alloc_text(PAGE, VfdMountMgrMountPoint)
75 #pragma alloc_text(PAGE, VfdMountMgrSendRequest)
76 #pragma alloc_text(PAGE, VfdMountDevUniqueId)
77 #pragma alloc_text(PAGE, VfdMountDevDeviceName)
78 #pragma alloc_text(PAGE, VfdMountDevSuggestedLink)
79 #pragma alloc_text(PAGE, VfdMountDevLinkModified)
80 #endif // ALLOC_PRAGMA
81
82 /*
83 #include <initguid.h>
84 #include <mountmgr.h>
85 //
86 // register a device to the mount manager interface
87 // does not work...
88 //
89 NTSTATUS
90 VfdRegisterMountManager(
91 PDEVICE_EXTENSION DeviceExtension)
92 {
93 NTSTATUS status = STATUS_SUCCESS;
94 UNICODE_STRING interface;
95 UNICODE_STRING interface2;
96
97 VFDTRACE(VFDINFO,
98 ("[VFD] Registering %ws to the Mount Manager Interface\n",
99 DeviceExtension->DeviceName.Buffer));
100
101 RtlInitUnicodeString(&interface, NULL);
102
103 status = IoRegisterDeviceInterface(
104 DeviceExtension->DeviceObject,
105 (LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID,
106 NULL,
107 &interface);
108
109 if (!NT_SUCCESS(status)) {
110 VFDTRACE(0,
111 ("[VFD] IoRegisterDeviceInterface - %s\n",
112 GetStatusName(status)));
113 return status;
114 }
115
116 status = IoSetDeviceInterfaceState(&interface, TRUE);
117
118 if (NT_SUCCESS(status)) {
119 if (VfdCopyUnicode(&interface2, &interface)) {
120 VFDTRACE(VFDINFO,
121 ("[VFD] Interface: %ws\n", interface2.Buffer));
122 }
123 else {
124 VFDTRACE(0,
125 ("[VFD] Failed to allocate an interface name buffer\n"));
126 status = STATUS_INSUFFICIENT_RESOURCES;
127 }
128 }
129 else {
130 VFDTRACE(0,
131 ("[VFD] IoSetDeviceInterfaceState - %s\n",
132 GetStatusName(status)));
133 }
134
135 RtlFreeUnicodeString(&interface);
136 VfdFreeUnicode(&interface2);
137
138 return status;
139 }
140 */
141
142 //
143 // informs the Mount Manager of a new VFD device
144 //
145 NTSTATUS
VfdMountMgrNotifyVolume(PDEVICE_EXTENSION DeviceExtension)146 VfdMountMgrNotifyVolume(
147 PDEVICE_EXTENSION DeviceExtension)
148 {
149 PMOUNTMGR_TARGET_NAME target_name;
150 USHORT target_name_buf[MAXIMUM_FILENAME_LENGTH];
151 NTSTATUS status;
152
153 VFDTRACE(VFDINFO,
154 ("[VFD] VfdMountMgrNotifyVolume - %ws\n",
155 DeviceExtension->DeviceName.Buffer));
156
157 target_name = (PMOUNTMGR_TARGET_NAME)target_name_buf;
158
159 target_name->DeviceNameLength =
160 DeviceExtension->DeviceName.Length;
161
162 RtlCopyMemory(
163 target_name->DeviceName,
164 DeviceExtension->DeviceName.Buffer,
165 DeviceExtension->DeviceName.Length);
166
167 status = VfdMountMgrSendRequest(
168 IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
169 target_name,
170 sizeof(target_name->DeviceNameLength) + target_name->DeviceNameLength,
171 NULL,
172 0);
173
174 VFDTRACE(VFDINFO,
175 ("[VFD] VfdMountMgrNotifyVolume - %s\n",
176 GetStatusName(status)));
177
178 return status;
179 }
180
181 //
182 // Create / remove a drive letter via the Mount Manager
183 //
184 NTSTATUS
VfdMountMgrMountPoint(PDEVICE_EXTENSION DeviceExtension,CHAR DriveLetter)185 VfdMountMgrMountPoint(
186 PDEVICE_EXTENSION DeviceExtension,
187 CHAR DriveLetter)
188 {
189 ULONG alloc_size;
190 UNICODE_STRING link_name;
191 WCHAR link_buf[20];
192 NTSTATUS status;
193
194 VFDTRACE(VFDINFO, ("[VFD] VfdMountMgrMountPoint - IN\n"));
195
196 // convert lower case into upper case
197
198 if (DriveLetter >= 'a' && DriveLetter <= 'z') {
199 DriveLetter -= ('a' - 'A');
200 }
201
202 if (DriveLetter >= 'A' && DriveLetter <= 'Z') {
203
204 // Create a new drive letter
205
206 PMOUNTMGR_CREATE_POINT_INPUT create;
207
208 swprintf(link_buf, L"\\DosDevices\\%wc:", DriveLetter);
209
210 RtlInitUnicodeString(&link_name, link_buf);
211
212 VFDTRACE(VFDINFO,
213 ("[VFD] Creating a link: %ws => %ws\n",
214 link_buf, DeviceExtension->DeviceName.Buffer));
215
216 // allocate buffer for MOUNTMGR_CREATE_POINT_INPUT
217
218 alloc_size = sizeof(MOUNTMGR_CREATE_POINT_INPUT) +
219 link_name.Length + DeviceExtension->DeviceName.Length;
220
221 create = (PMOUNTMGR_CREATE_POINT_INPUT)ExAllocatePoolWithTag(
222 NonPagedPool, alloc_size, VFD_POOL_TAG);
223
224 if (!create) {
225 VFDTRACE(0, ("[VFD] Failed to allocate mount point input\n"));
226 return STATUS_INSUFFICIENT_RESOURCES;
227 }
228
229 // set the symbolic link name
230
231 create->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
232 create->SymbolicLinkNameLength = link_name.Length;
233
234 RtlCopyMemory(
235 (PCHAR)create + create->SymbolicLinkNameOffset,
236 link_name.Buffer,
237 link_name.Length);
238
239 // set the target device name
240
241 create->DeviceNameOffset = (USHORT)
242 (create->SymbolicLinkNameOffset + create->SymbolicLinkNameLength);
243 create->DeviceNameLength = DeviceExtension->DeviceName.Length;
244
245 RtlCopyMemory(
246 (PCHAR)create + create->DeviceNameOffset,
247 DeviceExtension->DeviceName.Buffer,
248 DeviceExtension->DeviceName.Length);
249
250 // call the mount manager with the IO control request
251
252 status = VfdMountMgrSendRequest(
253 IOCTL_MOUNTMGR_CREATE_POINT,
254 create, alloc_size, NULL, 0);
255
256 ExFreePool(create);
257
258 // no need to set the new drive letter into the
259 // DeviceExtension because the mount manager will issue an
260 // IOCTL_MOUNTDEV_LINK_CREATED and it will be processed then
261 }
262 else if (DriveLetter == 0) {
263
264 // Delete the existing drive letter
265
266 PMOUNTMGR_MOUNT_POINT mount;
267 PMOUNTMGR_MOUNT_POINTS points;
268 UNICODE_STRING unique_id;
269 WCHAR unique_buf[20];
270
271 swprintf(link_buf, L"\\DosDevices\\%wc:",
272 DeviceExtension->DriveLetter);
273
274 VFDTRACE(VFDINFO,
275 ("[VFD] Deleting link: %ws\n", link_buf));
276
277 RtlInitUnicodeString(&link_name, link_buf);
278
279 swprintf(unique_buf, L"\\??\\" VFD_DEVICE_BASENAME L"%lu",
280 DeviceExtension->DeviceNumber);
281
282 RtlInitUnicodeString(&unique_id, unique_buf);
283
284 // allocate buffer for MOUNTMGR_MOUNT_POINT
285
286 alloc_size = sizeof(MOUNTMGR_MOUNT_POINT) +
287 link_name.Length +
288 unique_id.Length +
289 DeviceExtension->DeviceName.Length;
290
291 mount = (PMOUNTMGR_MOUNT_POINT)ExAllocatePoolWithTag(
292 NonPagedPool, alloc_size, VFD_POOL_TAG);
293
294 if (!mount) {
295 VFDTRACE(0, ("[VFD] Failed to allocate mount point input\n"));
296 return STATUS_INSUFFICIENT_RESOURCES;
297 }
298
299 RtlZeroMemory(mount, alloc_size + sizeof(WCHAR));
300
301 // set the symbolic link name
302
303 mount->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
304 mount->SymbolicLinkNameLength = link_name.Length;
305
306 RtlCopyMemory(
307 (PCHAR)mount + mount->SymbolicLinkNameOffset,
308 link_name.Buffer, link_name.Length);
309
310 // set the unique id
311
312 mount->UniqueIdOffset =
313 mount->SymbolicLinkNameOffset +
314 mount->SymbolicLinkNameLength;
315 mount->UniqueIdLength = unique_id.Length;
316
317 RtlCopyMemory(
318 (PCHAR)mount + mount->UniqueIdOffset,
319 unique_id.Buffer, unique_id.Length);
320
321 // set the target device name
322
323 mount->DeviceNameOffset =
324 mount->UniqueIdOffset +
325 mount->UniqueIdLength;
326 mount->DeviceNameLength =
327 DeviceExtension->DeviceName.Length;
328
329 RtlCopyMemory(
330 (PCHAR)mount + mount->DeviceNameOffset,
331 DeviceExtension->DeviceName.Buffer,
332 DeviceExtension->DeviceName.Length);
333
334 // prepare the output buffer
335
336 points = (PMOUNTMGR_MOUNT_POINTS)ExAllocatePoolWithTag(
337 NonPagedPool, alloc_size * 2, VFD_POOL_TAG);
338
339 status = VfdMountMgrSendRequest(
340 IOCTL_MOUNTMGR_DELETE_POINTS,
341 mount, alloc_size, points, alloc_size * 2);
342
343 ExFreePool(mount);
344 ExFreePool(points);
345
346 if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
347 // the drive letter did not exist in the first place
348 DeviceExtension->DriveLetter = 0;
349 }
350
351 // no need to clear the drive letter in the
352 // DeviceExtension because the mount manager will issue an
353 // IOCTL_MOUNTDEV_LINK_DELETED and it will be processed then
354 }
355 else {
356 return STATUS_INVALID_PARAMETER;
357 }
358
359 VFDTRACE(VFDINFO, ("[VFD] VfdMountMgrMountPoint - %s\n",
360 GetStatusName(status)));
361
362 return status;
363 }
364
365 //
366 // send a request to the Mount Manager
367 //
368 NTSTATUS
VfdMountMgrSendRequest(ULONG ControlCode,PVOID InputBuffer,ULONG InputLength,PVOID OutputBuffer,ULONG OutputLength)369 VfdMountMgrSendRequest(
370 ULONG ControlCode,
371 PVOID InputBuffer,
372 ULONG InputLength,
373 PVOID OutputBuffer,
374 ULONG OutputLength)
375 {
376 NTSTATUS status = STATUS_SUCCESS;
377 UNICODE_STRING mntmgr_name;
378 PDEVICE_OBJECT mntmgr_dev;
379 PFILE_OBJECT mntmgr_file;
380 IO_STATUS_BLOCK io_status;
381 KEVENT event;
382 PIRP irp;
383
384 // Obtain a pointer to the Mount Manager device object
385
386 RtlInitUnicodeString(
387 &mntmgr_name,
388 MOUNTMGR_DEVICE_NAME);
389
390 status = IoGetDeviceObjectPointer(
391 &mntmgr_name,
392 FILE_READ_ATTRIBUTES,
393 &mntmgr_file,
394 &mntmgr_dev);
395
396 if (!NT_SUCCESS(status)) {
397 VFDTRACE(VFDWARN,
398 ("[VFD] IoGetDeviceObjectPointer - %s\n", GetStatusName(status)));
399 return status;
400 }
401
402 KeInitializeEvent(&event, NotificationEvent, FALSE);
403
404 // Create an IRP request block
405
406 irp = IoBuildDeviceIoControlRequest(
407 ControlCode,
408 mntmgr_dev,
409 InputBuffer,
410 InputLength,
411 OutputBuffer,
412 OutputLength,
413 FALSE,
414 &event,
415 &io_status);
416
417 if (!irp) {
418 VFDTRACE(VFDWARN,
419 ("[VFD] IoBuildDeviceIoControlRequest\n"));
420 ObDereferenceObject(mntmgr_file);
421 return STATUS_DRIVER_INTERNAL_ERROR;
422 }
423
424 // Call the mount manager
425
426 status = IoCallDriver(mntmgr_dev, irp);
427
428 if (!NT_SUCCESS(status)) {
429 VFDTRACE(VFDWARN,
430 ("[VFD] IoCallDriver - %s\n", GetStatusName(status)));
431 }
432
433 if (status == STATUS_PENDING) {
434
435 // Wait for the operation to complete
436
437 KeWaitForSingleObject(
438 &event, Executive, KernelMode, FALSE, NULL);
439
440 status = io_status.Status;
441
442 if (!NT_SUCCESS(status)) {
443 VFDTRACE(VFDWARN,
444 ("[VFD] IoCallDriver - %s\n", GetStatusName(status)));
445 }
446 }
447
448 ObDereferenceObject(mntmgr_file);
449
450 return status;
451 }
452
453 //
454 // IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
455 // -- use the device interface link (\??\VirtualFD<x>) as the unique ID
456 //
457 NTSTATUS
VfdMountDevUniqueId(PDEVICE_EXTENSION DeviceExtension,PMOUNTDEV_UNIQUE_ID UniqueId,ULONG OutputLength,PIO_STATUS_BLOCK IoStatus)458 VfdMountDevUniqueId(
459 PDEVICE_EXTENSION DeviceExtension,
460 PMOUNTDEV_UNIQUE_ID UniqueId,
461 ULONG OutputLength,
462 PIO_STATUS_BLOCK IoStatus)
463 {
464 WCHAR buf[20];
465 UNICODE_STRING unicode;
466
467 if (OutputLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
468 return STATUS_INVALID_PARAMETER;
469 }
470
471 swprintf(buf,
472 L"\\??\\" VFD_DEVICE_BASENAME L"%lu",
473 DeviceExtension->DeviceNumber);
474
475 RtlInitUnicodeString(&unicode, buf);
476
477 UniqueId->UniqueIdLength = unicode.Length;
478
479 if (OutputLength <
480 sizeof(UniqueId->UniqueIdLength) + UniqueId->UniqueIdLength) {
481
482 IoStatus->Information = sizeof(MOUNTDEV_UNIQUE_ID);
483 return STATUS_BUFFER_OVERFLOW;
484 }
485
486 RtlCopyMemory(
487 UniqueId->UniqueId, buf, unicode.Length);
488
489 IoStatus->Information =
490 sizeof(UniqueId->UniqueIdLength) + UniqueId->UniqueIdLength;
491
492 return STATUS_SUCCESS;
493 }
494
495 //
496 // IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
497 // Returns the device name of the target device (\Device\Floppy<n>)
498 //
499 NTSTATUS
VfdMountDevDeviceName(PDEVICE_EXTENSION DeviceExtension,PMOUNTDEV_NAME DeviceName,ULONG OutputLength,PIO_STATUS_BLOCK IoStatus)500 VfdMountDevDeviceName(
501 PDEVICE_EXTENSION DeviceExtension,
502 PMOUNTDEV_NAME DeviceName,
503 ULONG OutputLength,
504 PIO_STATUS_BLOCK IoStatus)
505 {
506 if (OutputLength < sizeof(MOUNTDEV_NAME)) {
507 return STATUS_INVALID_PARAMETER;
508 }
509
510 DeviceName->NameLength = DeviceExtension->DeviceName.Length;
511
512 if (OutputLength <
513 sizeof(DeviceName->NameLength) + DeviceName->NameLength) {
514
515 IoStatus->Information = sizeof(MOUNTDEV_NAME);
516 return STATUS_BUFFER_OVERFLOW;
517 }
518
519 RtlCopyMemory(
520 DeviceName->Name,
521 DeviceExtension->DeviceName.Buffer,
522 DeviceName->NameLength);
523
524 IoStatus->Information =
525 sizeof(DeviceName->NameLength) + DeviceName->NameLength;
526
527 return STATUS_SUCCESS;
528 }
529
530 //
531 // IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
532 // Returns the drive letter link which we want the mount manager
533 // to create. This request is issued in response to the volume
534 // arrival notification, and the mount manager will create the
535 // symbolic link.
536 //
537 NTSTATUS
VfdMountDevSuggestedLink(PDEVICE_EXTENSION DeviceExtension,PMOUNTDEV_SUGGESTED_LINK_NAME LinkName,ULONG OutputLength,PIO_STATUS_BLOCK IoStatus)538 VfdMountDevSuggestedLink(
539 PDEVICE_EXTENSION DeviceExtension,
540 PMOUNTDEV_SUGGESTED_LINK_NAME LinkName,
541 ULONG OutputLength,
542 PIO_STATUS_BLOCK IoStatus)
543 {
544 WCHAR buf[20];
545 UNICODE_STRING unicode;
546
547 if (OutputLength < sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
548 return STATUS_INVALID_PARAMETER;
549 }
550
551 LinkName->UseOnlyIfThereAreNoOtherLinks = TRUE;
552
553 if (!DeviceExtension->DriveLetter) {
554
555 // No persistent drive letter stored in the registry
556 VFDTRACE(VFDINFO, ("[VFD] suggested link : none\n"));
557
558 LinkName->NameLength = 0;
559
560 IoStatus->Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
561 return STATUS_SUCCESS;
562 }
563
564 // A persistent drive letter exists
565
566 swprintf(buf, L"\\DosDevices\\%wc:",
567 DeviceExtension->DriveLetter);
568
569 VFDTRACE(VFDINFO, ("[VFD] suggested link : %ws\n", buf));
570
571 RtlInitUnicodeString(&unicode, buf);
572
573 LinkName->NameLength = unicode.Length;
574
575 if (OutputLength <
576 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME) +
577 LinkName->NameLength - sizeof(WCHAR)) {
578
579 IoStatus->Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
580 return STATUS_BUFFER_OVERFLOW;
581 }
582
583 RtlCopyMemory(LinkName->Name, buf, unicode.Length);
584
585 IoStatus->Information =
586 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME) +
587 LinkName->NameLength - sizeof(WCHAR);
588
589 return STATUS_SUCCESS;
590 }
591
592 //
593 // IOCTL_MOUNTDEV_LINK_CREATED / IOCTL_MOUNTDEV_LINK_DELETED
594 // Issued after the mount manager created/deleted a symbolic link
595 // If the link is a drive letter, store the new value into the
596 // registry as the new drive letter
597 //
598 NTSTATUS
VfdMountDevLinkModified(PDEVICE_EXTENSION DeviceExtension,PMOUNTDEV_NAME LinkName,ULONG InputLength,ULONG ControlCode)599 VfdMountDevLinkModified(
600 PDEVICE_EXTENSION DeviceExtension,
601 PMOUNTDEV_NAME LinkName,
602 ULONG InputLength,
603 ULONG ControlCode)
604 {
605 if (InputLength < sizeof(MOUNTDEV_NAME)) {
606 return STATUS_INVALID_PARAMETER;
607 }
608
609 if (InputLength < sizeof(MOUNTDEV_NAME) +
610 LinkName->NameLength - sizeof(WCHAR)) {
611
612 return STATUS_INVALID_PARAMETER;
613 }
614
615 #if DBG
616 { // Print the reported link name
617 PWSTR buf = ExAllocatePoolWithTag(
618 PagedPool, LinkName->NameLength + sizeof(WCHAR), VFD_POOL_TAG);
619
620 if (buf) {
621 RtlZeroMemory(buf, LinkName->NameLength + sizeof(WCHAR));
622 RtlCopyMemory(buf, LinkName->Name, LinkName->NameLength);
623 VFDTRACE(VFDINFO, ("[VFD] %ws\n", buf));
624 ExFreePool(buf);
625 }
626 }
627 #endif // DBG
628
629 if (LinkName->NameLength == 28 &&
630 LinkName->Name[0] == L'\\' &&
631 LinkName->Name[1] == L'D' &&
632 LinkName->Name[2] == L'o' &&
633 LinkName->Name[3] == L's' &&
634 LinkName->Name[4] == L'D' &&
635 LinkName->Name[5] == L'e' &&
636 LinkName->Name[6] == L'v' &&
637 LinkName->Name[7] == L'i' &&
638 LinkName->Name[8] == L'c' &&
639 LinkName->Name[9] == L'e' &&
640 LinkName->Name[10] == L's' &&
641 LinkName->Name[11] == L'\\' &&
642 LinkName->Name[12] >= L'A' &&
643 LinkName->Name[12] <= L'Z' &&
644 LinkName->Name[13] == L':') {
645
646 // The link is a drive letter
647
648 if (ControlCode == IOCTL_MOUNTDEV_LINK_CREATED) {
649 // link is created - store the new drive letter
650 DeviceExtension->DriveLetter = (CHAR)LinkName->Name[12];
651 }
652 else {
653 // link is deleted - clear the drive letter
654 DeviceExtension->DriveLetter = 0;
655 }
656
657 // Store the value into the registry
658
659 VfdStoreLink(DeviceExtension);
660 }
661
662 return STATUS_SUCCESS;
663 }
664
665 #endif // VFD_MOUNT_MANAGER
666