1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS FS utility tool
4  * FILE:            modules/rosapps/drivers/vcdrom/vcdrom.c
5  * PURPOSE:         Virtual CD-ROM class driver
6  * PROGRAMMERS:     Pierre Schweitzer <pierre@reactos.org>
7  */
8 
9 #include <ntddk.h>
10 #include <ntifs.h>
11 #include <ntdddisk.h>
12 #include <ntddcdrm.h>
13 #include <pseh/pseh2.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 #include "vcdioctl.h"
19 
20 typedef struct _DEVICE_EXTENSION
21 {
22     PDEVICE_OBJECT DeviceObject;
23     /* File object to the ISO when opened */
24     PFILE_OBJECT VolumeObject;
25     /* Size of the ISO */
26     LARGE_INTEGER VolumeSize;
27     /* Mandatory change count for verify */
28     ULONG ChangeCount;
29     /* Will contain the device name or the drive letter */
30     UNICODE_STRING GlobalName;
31     /* Will contain the image path */
32     UNICODE_STRING ImageName;
33     /* Flags on mount (supp. of UDF/Joliet) */
34     ULONG Flags;
35     /* Faking CD structure */
36     ULONG SectorSize;
37     ULONG SectorShift;
38 } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
39 
40 /* Taken from CDFS */
41 #define TOC_DATA_TRACK 0x04
42 #define TOC_LAST_TRACK 0xaa
43 
44 /* Should cover most of our usages */
45 #define DEFAULT_STRING_SIZE 50
46 
47 /* Increment on device creation, protection by the mutex */
48 FAST_MUTEX ViMutex;
49 ULONG ViDevicesCount;
50 
51 VOID
ViInitializeDeviceExtension(PDEVICE_OBJECT DeviceObject)52 ViInitializeDeviceExtension(PDEVICE_OBJECT DeviceObject)
53 {
54     PDEVICE_EXTENSION DeviceExtension;
55 
56     DeviceExtension = DeviceObject->DeviceExtension;
57 
58     /* Zero mandatory fields, the rest will be zeroed when needed */
59     DeviceExtension->DeviceObject = DeviceObject;
60     DeviceExtension->VolumeObject = NULL;
61     DeviceExtension->ChangeCount = 0;
62     DeviceExtension->GlobalName.Buffer = NULL;
63     DeviceExtension->GlobalName.MaximumLength = 0;
64     DeviceExtension->GlobalName.Length = 0;
65     DeviceExtension->ImageName.Buffer = NULL;
66     DeviceExtension->ImageName.MaximumLength = 0;
67     DeviceExtension->ImageName.Length = 0;
68 }
69 
70 NTSTATUS
ViAllocateUnicodeString(USHORT BufferLength,PUNICODE_STRING UnicodeString)71 ViAllocateUnicodeString(USHORT BufferLength, PUNICODE_STRING UnicodeString)
72 {
73     PVOID Buffer;
74 
75     /* Allocate the buffer */
76     Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferLength, ' dCV');
77     /* Initialize */
78     UnicodeString->Length = 0;
79     UnicodeString->MaximumLength = BufferLength;
80     UnicodeString->Buffer = Buffer;
81 
82     /* Return success if it went fine */
83     if (Buffer != NULL)
84     {
85         return STATUS_SUCCESS;
86     }
87 
88     return STATUS_NO_MEMORY;
89 }
90 
91 VOID
ViFreeUnicodeString(PUNICODE_STRING UnicodeString)92 ViFreeUnicodeString(PUNICODE_STRING UnicodeString)
93 {
94     /* Only free if allocate, that allows using this
95      * on cleanup in short memory situations
96      */
97     if (UnicodeString->Buffer != NULL)
98     {
99         ExFreePoolWithTag(UnicodeString->Buffer, 0);
100         UnicodeString->Buffer = NULL;
101     }
102 
103     /* Zero the rest */
104     UnicodeString->Length = 0;
105     UnicodeString->MaximumLength = 0;
106 }
107 
108 NTSTATUS
ViSetIoStatus(NTSTATUS Status,ULONG_PTR Information,PIRP Irp)109 ViSetIoStatus(NTSTATUS Status, ULONG_PTR Information, PIRP Irp)
110 {
111     /* Only set what we got */
112     Irp->IoStatus.Information = Information;
113     Irp->IoStatus.Status = Status;
114 
115     /* And return the status, so that caller can return with us */
116     return Status;
117 }
118 
119 NTSTATUS
120 NTAPI
VcdHandle(PDEVICE_OBJECT DeviceObject,PIRP Irp)121 VcdHandle(PDEVICE_OBJECT DeviceObject, PIRP Irp)
122 {
123     /* Stub for CREATE, CLEANUP, CLOSE, always a succes */
124     ViSetIoStatus(STATUS_SUCCESS, 0, Irp);
125     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
126 
127     return STATUS_SUCCESS;
128 }
129 
130 NTSTATUS
ViDeleteDevice(PDEVICE_OBJECT DeviceObject,PIRP Irp)131 ViDeleteDevice(PDEVICE_OBJECT DeviceObject, PIRP Irp)
132 {
133     NTSTATUS Status;
134     PDEVICE_EXTENSION DeviceExtension;
135 
136     DeviceExtension = DeviceObject->DeviceExtension;
137 
138     /* Delete the drive letter */
139     Status = IoDeleteSymbolicLink(&DeviceExtension->GlobalName);
140     ViFreeUnicodeString(&DeviceExtension->GlobalName);
141 
142     /* Close the ISO */
143     if (DeviceExtension->VolumeObject != NULL)
144     {
145         ObDereferenceObject(DeviceExtension->VolumeObject);
146         DeviceExtension->VolumeObject = NULL;
147     }
148 
149     /* Free the ISO name */
150     ViFreeUnicodeString(&DeviceExtension->ImageName);
151 
152     /* And delete the device */
153     IoDeleteDevice(DeviceObject);
154     return ViSetIoStatus(Status, 0, Irp);
155 }
156 
157 VOID
158 NTAPI
VcdUnload(PDRIVER_OBJECT DriverObject)159 VcdUnload(PDRIVER_OBJECT DriverObject)
160 {
161     IRP FakeIrp;
162     PDEVICE_OBJECT DeviceObject;
163 
164     /* No device, nothing to free */
165     DeviceObject = DriverObject->DeviceObject;
166     if (DeviceObject == NULL)
167     {
168         return;
169     }
170 
171     /* Delete any device we could have created */
172     do
173     {
174         PDEVICE_OBJECT NextDevice;
175 
176         NextDevice = DeviceObject->NextDevice;
177         /* This is normally called on IoCtl, so fake
178          * the IRP so that status can be dummily set
179          */
180         ViDeleteDevice(DeviceObject, &FakeIrp);
181         DeviceObject = NextDevice;
182     } while (DeviceObject != NULL);
183 }
184 
185 NTSTATUS
ViCreateDriveLetter(PDRIVER_OBJECT DriverObject,WCHAR Letter,WCHAR * EffectiveLetter,PDEVICE_OBJECT * DeviceObject)186 ViCreateDriveLetter(PDRIVER_OBJECT DriverObject, WCHAR Letter, WCHAR *EffectiveLetter, PDEVICE_OBJECT *DeviceObject)
187 {
188     NTSTATUS Status;
189     HANDLE LinkHandle;
190     WCHAR DriveLetter[3], CurLetter;
191     PDEVICE_OBJECT LocalDeviceObject;
192     PDEVICE_EXTENSION DeviceExtension;
193     OBJECT_ATTRIBUTES ObjectAttributes;
194     UNICODE_STRING DeviceCount, DeviceName;
195 
196     *DeviceObject = NULL;
197 
198     /* Allocate our buffers */
199     ViAllocateUnicodeString(DEFAULT_STRING_SIZE, &DeviceCount);
200     ViAllocateUnicodeString(DEFAULT_STRING_SIZE, &DeviceName);
201 
202     /* For easier cleanup */
203     _SEH2_TRY
204     {
205         /* Get our device number */
206         ExAcquireFastMutex(&ViMutex);
207         Status = RtlIntegerToUnicodeString(ViDevicesCount, 10, &DeviceCount);
208         ++ViDevicesCount;
209         ExReleaseFastMutex(&ViMutex);
210 
211         /* Conversion to string failed, bail out */
212         if (!NT_SUCCESS(Status))
213         {
214             _SEH2_LEAVE;
215         }
216 
217         /* Create our device name */
218         Status = RtlAppendUnicodeToString(&DeviceName, L"\\Device\\VirtualCdRom");
219         if (!NT_SUCCESS(Status))
220         {
221             _SEH2_LEAVE;
222         }
223 
224         Status = RtlAppendUnicodeStringToString(&DeviceName, &DeviceCount);
225         if (!NT_SUCCESS(Status))
226         {
227             _SEH2_LEAVE;
228         }
229 
230         /* And create the device! */
231         Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceName,
232                                 FILE_DEVICE_CD_ROM,
233                                 FILE_READ_ONLY_DEVICE | FILE_FLOPPY_DISKETTE,
234                                 FALSE, &LocalDeviceObject);
235         if (!NT_SUCCESS(Status))
236         {
237             _SEH2_LEAVE;
238         }
239 
240         /* Initialize our DE */
241         ViInitializeDeviceExtension(LocalDeviceObject);
242         DeviceExtension = LocalDeviceObject->DeviceExtension;
243         ViAllocateUnicodeString(DEFAULT_STRING_SIZE, &DeviceExtension->GlobalName);
244 
245         /* Now, we'll try to find a free drive letter
246          * We always start from Z and go backward
247          */
248         for (CurLetter = Letter; CurLetter >= 'A'; CurLetter--)
249         {
250             /* By default, no flags. These will be set
251              * on mount, by the caller
252              */
253             DeviceExtension->Flags = 0;
254 
255             /* Create a drive letter name */
256             DeviceExtension->GlobalName.Length = 0;
257             Status = RtlAppendUnicodeToString(&DeviceExtension->GlobalName, L"\\??\\");
258             if (!NT_SUCCESS(Status))
259             {
260                 _SEH2_LEAVE;
261             }
262 
263             DriveLetter[0] = CurLetter;
264             DriveLetter[1] = L':';
265             DriveLetter[2] = UNICODE_NULL;
266             Status = RtlAppendUnicodeToString(&DeviceExtension->GlobalName, DriveLetter);
267             if (!NT_SUCCESS(Status))
268             {
269                 _SEH2_LEAVE;
270             }
271 
272             /* And try to open it */
273             InitializeObjectAttributes(&ObjectAttributes, &DeviceExtension->GlobalName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL);
274             Status = ZwOpenSymbolicLinkObject(&LinkHandle, GENERIC_READ, &ObjectAttributes);
275             /* It failed; good news, that letter is free, jump on it! */
276             if (!NT_SUCCESS(Status))
277             {
278                 /* Create a symbolic link to our device */
279                 Status = IoCreateSymbolicLink(&DeviceExtension->GlobalName, &DeviceName);
280                 /* If created... */
281                 if (NT_SUCCESS(Status))
282                 {
283                     /* Return the drive letter to the caller */
284                     *EffectiveLetter = CurLetter;
285                     ClearFlag(LocalDeviceObject->Flags, DO_DEVICE_INITIALIZING);
286                     /* No caching! */
287                     SetFlag(LocalDeviceObject->Flags, DO_DIRECT_IO);
288 
289                     /* And return the DO */
290                     *DeviceObject = LocalDeviceObject;
291                     break;
292                 }
293             }
294             /* This letter is taken, try another one */
295             else
296             {
297                 ZwClose(LinkHandle);
298                 Status = STATUS_OBJECT_NAME_EXISTS;
299             }
300         }
301 
302         /* We're out of drive letters, so fail :-( */
303         if (CurLetter == (L'A' - 1))
304         {
305             Status = STATUS_INSUFFICIENT_RESOURCES;
306         }
307     }
308     _SEH2_FINALLY
309     {
310         /* No longer need these */
311         ViFreeUnicodeString(&DeviceName);
312         ViFreeUnicodeString(&DeviceCount);
313 
314         /* And delete in case of a failure */
315         if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED && LocalDeviceObject != NULL)
316         {
317             IoDeleteDevice(LocalDeviceObject);
318         }
319     }
320     _SEH2_END;
321 
322     return Status;
323 }
324 
325 NTSTATUS
ViMountImage(PDEVICE_OBJECT DeviceObject,PUNICODE_STRING Image)326 ViMountImage(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING Image)
327 {
328     NTSTATUS Status;
329     HANDLE ImgHandle;
330     IO_STATUS_BLOCK IoStatus;
331     ULONG Buffer[2], i, SectorShift;
332     PDEVICE_EXTENSION DeviceExtension;
333     OBJECT_ATTRIBUTES ObjectAttributes;
334     FILE_STANDARD_INFORMATION FileStdInfo;
335 
336     /* Try to open the image */
337     InitializeObjectAttributes(&ObjectAttributes, Image, OBJ_CASE_INSENSITIVE, NULL, NULL);
338     Status = ZwCreateFile(&ImgHandle, FILE_READ_DATA | SYNCHRONIZE, &ObjectAttributes,
339                           &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
340                           FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
341     if (!NT_SUCCESS(Status))
342     {
343         DPRINT1("Failed to open image: %wZ\n", Image);
344         return Status;
345     }
346 
347     /* Query its information */
348     Status = ZwQueryInformationFile(ImgHandle, &IoStatus, &FileStdInfo,
349                                     sizeof(FileStdInfo), FileStandardInformation);
350     if (!NT_SUCCESS(Status))
351     {
352         DPRINT1("Failed to query image information\n");
353         ZwClose(ImgHandle);
354         return Status;
355     }
356 
357     /* Set image size */
358     DeviceExtension = DeviceObject->DeviceExtension;
359     DeviceExtension->VolumeSize.QuadPart = FileStdInfo.EndOfFile.QuadPart;
360 
361     /* Read the first 8-bytes to determine our sector size */
362     Status = ZwReadFile(ImgHandle, NULL, NULL, NULL, &IoStatus, Buffer, sizeof(Buffer), NULL, NULL);
363     if (!NT_SUCCESS(Status) || Buffer[1] == 0 || (Buffer[1] & 0x1FF) != 0)
364     {
365         DeviceExtension->SectorSize = 2048;
366     }
367     else
368     {
369         DeviceExtension->SectorSize = Buffer[1];
370     }
371 
372     /* Now, compute the sector shift */
373     if (DeviceExtension->SectorSize == 512)
374     {
375         DeviceExtension->SectorShift = 9;
376     }
377     else
378     {
379         for (i = 512, SectorShift = 9; i < DeviceExtension->SectorSize; i = i * 2, ++SectorShift);
380         if (i == DeviceExtension->SectorSize)
381         {
382             DeviceExtension->SectorShift = SectorShift;
383         }
384         else
385         {
386             DeviceExtension->SectorShift = 11;
387         }
388     }
389 
390     /* We're good, get the image file object to close the handle */
391     Status = ObReferenceObjectByHandle(ImgHandle, 0, NULL, KernelMode,
392                                        (PVOID *)&DeviceExtension->VolumeObject, NULL);
393     if (!NT_SUCCESS(Status))
394     {
395         ZwClose(ImgHandle);
396         DeviceExtension->VolumeObject = NULL;
397         return Status;
398     }
399 
400     ZwClose(ImgHandle);
401     /* Increase change count to force a verify */
402     ++DeviceExtension->ChangeCount;
403 
404     /* And ask for a verify! */
405     SetFlag(DeviceObject->Flags, DO_VERIFY_VOLUME);
406 
407     /* Free old string (if any) */
408     ViFreeUnicodeString(&DeviceExtension->ImageName);
409     /* And save our new image name */
410     ViAllocateUnicodeString(Image->MaximumLength, &DeviceExtension->ImageName);
411     RtlCopyUnicodeString(&DeviceExtension->ImageName, Image);
412 
413     return STATUS_SUCCESS;
414 }
415 
416 VOID
ViCreateDriveAndMountImage(PDRIVER_OBJECT DriverObject,WCHAR Letter,LPCWSTR ImagePath)417 ViCreateDriveAndMountImage(PDRIVER_OBJECT DriverObject, WCHAR Letter, LPCWSTR ImagePath)
418 {
419     UNICODE_STRING Image;
420     WCHAR EffectiveLetter;
421     PDEVICE_OBJECT DeviceObject;
422 
423     /* Create string from path */
424     DeviceObject = NULL;
425     RtlInitUnicodeString(&Image, ImagePath);
426 
427     /* Create the drive letter (ignore output, it comes from registry, nothing to do */
428     ViCreateDriveLetter(DriverObject, Letter, &EffectiveLetter, &DeviceObject);
429     /* And mount the image on the created drive */
430     ViMountImage(DeviceObject, &Image);
431 }
432 
433 VOID
ViLoadImagesFromRegistry(PDRIVER_OBJECT DriverObject,LPCWSTR RegistryPath)434 ViLoadImagesFromRegistry(PDRIVER_OBJECT DriverObject, LPCWSTR RegistryPath)
435 {
436     WCHAR Letter[2];
437     WCHAR PathBuffer[100], ResBuffer[100];
438 
439     /* We'll browse the registry for
440      * DeviceX\IMAGE = {Path}
441      * When found, we create (or at least, attempt to)
442      * device X: and mount image Path
443      */
444     Letter[0] = L'A';
445     Letter[1] = UNICODE_NULL;
446     do
447     {
448         NTSTATUS Status;
449         UNICODE_STRING Path, ResString;
450         RTL_QUERY_REGISTRY_TABLE QueryTable[3];
451 
452         /* Our path is always Device + drive letter */
453         RtlZeroMemory(PathBuffer, sizeof(PathBuffer));
454         Path.Buffer = PathBuffer;
455         Path.Length = 0;
456         Path.MaximumLength = sizeof(PathBuffer);
457         RtlAppendUnicodeToString(&Path, L"Parameters\\Device");
458         RtlAppendUnicodeToString(&Path, Letter);
459 
460         ResString.Buffer = ResBuffer;
461         ResString.Length = 0;
462         ResString.MaximumLength = sizeof(ResBuffer);
463 
464         RtlZeroMemory(&QueryTable[0], sizeof(QueryTable));
465         QueryTable[0].Name = Path.Buffer;
466         QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
467         QueryTable[1].Name = L"IMAGE";
468         QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
469         QueryTable[1].EntryContext = &ResString;
470 
471         /* If query went fine, attempt to create and mount */
472         Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RegistryPath, QueryTable, NULL, NULL);
473         if (NT_SUCCESS(Status))
474         {
475             ViCreateDriveAndMountImage(DriverObject, Letter[0], ResString.Buffer);
476         }
477 
478         ++(Letter[0]);
479     } while (Letter[0] <= L'Z');
480 }
481 
482 NTSTATUS
ViVerifyVolume(PDEVICE_OBJECT DeviceObject,PIRP Irp)483 ViVerifyVolume(PDEVICE_OBJECT DeviceObject, PIRP Irp)
484 {
485     /* Force verify */
486     SetFlag(DeviceObject->Flags, DO_VERIFY_VOLUME);
487 
488     Irp->IoStatus.Information = 0;
489     Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
490 
491     IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
492 
493     return STATUS_VERIFY_REQUIRED;
494 }
495 
496 NTSTATUS
ViReadFile(PFILE_OBJECT File,PMDL Mdl,PLARGE_INTEGER Offset,PKEVENT Event,ULONG Length,PIO_STATUS_BLOCK IoStatusBlock)497 ViReadFile(PFILE_OBJECT File, PMDL Mdl, PLARGE_INTEGER Offset, PKEVENT Event, ULONG Length, PIO_STATUS_BLOCK IoStatusBlock)
498 {
499     PIRP LowerIrp;
500     PIO_STACK_LOCATION Stack;
501     PDEVICE_OBJECT DeviceObject;
502 
503     /* Get the lower DO */
504     DeviceObject = IoGetRelatedDeviceObject(File);
505     /* Allocate an IRP to deal with it */
506     LowerIrp = IoAllocateIrp(DeviceObject->StackSize, 0);
507     if (LowerIrp == NULL)
508     {
509         return STATUS_INSUFFICIENT_RESOURCES;
510     }
511 
512     /* Initialize it */
513     LowerIrp->RequestorMode = KernelMode;
514     /* Our status block */
515     LowerIrp->UserIosb = IoStatusBlock;
516     LowerIrp->MdlAddress = Mdl;
517     /* We don't cache! */
518     LowerIrp->Flags = IRP_NOCACHE | IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO;
519     /* Sync event for us to know when read is done */
520     LowerIrp->UserEvent = Event;
521     LowerIrp->UserBuffer = (PVOID)((ULONG_PTR)Mdl->StartVa + Mdl->ByteOffset);
522     /* The FO of our image */
523     LowerIrp->Tail.Overlay.OriginalFileObject = File;
524     LowerIrp->Tail.Overlay.Thread = PsGetCurrentThread();
525 
526     /* We basically perform a read operation, nothing complex here */
527     Stack = IoGetNextIrpStackLocation(LowerIrp);
528     Stack->Parameters.Read.Length = Length;
529     Stack->MajorFunction = IRP_MJ_READ;
530     Stack->FileObject = File;
531     Stack->Parameters.Read.ByteOffset.QuadPart = Offset->QuadPart;
532 
533     /* And call lower driver */
534     return IoCallDriver(DeviceObject, LowerIrp);
535 }
536 
537 NTSTATUS
538 NTAPI
VcdRead(PDEVICE_OBJECT DeviceObject,PIRP Irp)539 VcdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
540 {
541     KEVENT Event;
542     NTSTATUS Status;
543     IO_STATUS_BLOCK IoStatus;
544     PIO_STACK_LOCATION Stack;
545     PDEVICE_EXTENSION DeviceExtension;
546 
547     /* Catch any exception that could happen during read attempt */
548     _SEH2_TRY
549     {
550         /* First of all, check if there's an image mounted */
551         DeviceExtension = DeviceObject->DeviceExtension;
552         if (DeviceExtension->VolumeObject == NULL)
553         {
554             Status = ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp);
555             _SEH2_LEAVE;
556         }
557 
558         /* Check if volume has to be verified (or if we override, for instance
559          * FSD performing initial reads for mouting FS)
560          */
561         Stack = IoGetCurrentIrpStackLocation(Irp);
562         if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
563             !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
564         {
565             Status = ViVerifyVolume(DeviceObject, Irp);
566             _SEH2_LEAVE;
567         }
568 
569         /* Check if we have enough room for output */
570         if (Stack->Parameters.Read.Length > Irp->MdlAddress->ByteCount)
571         {
572             Status = ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, 0, Irp);
573             _SEH2_LEAVE;
574         }
575 
576         /* Initialize our event */
577         KeInitializeEvent(&Event, NotificationEvent, FALSE);
578 
579         /* Perform actual read */
580         Status = ViReadFile(DeviceExtension->VolumeObject, Irp->MdlAddress,
581                             &Stack->Parameters.Read.ByteOffset, &Event,
582                             Stack->Parameters.Read.Length, &IoStatus);
583         if (!NT_SUCCESS(Status))
584         {
585             Status = ViSetIoStatus(Status, 0, Irp);
586             _SEH2_LEAVE;
587         }
588 
589         /* Make sure we wait until its done */
590         Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
591         if (!NT_SUCCESS(Status))
592         {
593             Status = ViSetIoStatus(Status, 0, Irp);
594             _SEH2_LEAVE;
595         }
596 
597         /* Look whether we're reading first bytes of the volume, if so, our
598          * "suppression" might be asked for
599          */
600         if (Stack->Parameters.Read.ByteOffset.QuadPart / DeviceExtension->SectorSize < 0x20)
601         {
602             /* Check our output buffer is in a state where we can play with it */
603             if ((Irp->MdlAddress->MdlFlags & (MDL_ALLOCATED_FIXED_SIZE | MDL_PAGES_LOCKED | MDL_MAPPED_TO_SYSTEM_VA)) ==
604                 (MDL_ALLOCATED_FIXED_SIZE | MDL_PAGES_LOCKED | MDL_MAPPED_TO_SYSTEM_VA))
605             {
606                 PUCHAR Buffer;
607 
608                 /* Do we have to delete any UDF mark? */
609                 if (BooleanFlagOn(DeviceExtension->Flags, MOUNT_FLAG_SUPP_UDF))
610                 {
611                     /* Kill any NSR0X mark from UDF */
612                     Buffer = (PUCHAR)((ULONG_PTR)Irp->MdlAddress->StartVa + Irp->MdlAddress->ByteOffset);
613                     if (Buffer != NULL)
614                     {
615                         if (Buffer[0] == 0 && Buffer[1] == 'N' && Buffer[2] == 'S' &&
616                             Buffer[3] == 'R' && Buffer[4] == '0')
617                         {
618                             Buffer[5] = '0';
619                         }
620                     }
621                 }
622 
623                 /* Do we have to delete any Joliet mark? */
624                 if (BooleanFlagOn(DeviceExtension->Flags, MOUNT_FLAG_SUPP_JOLIET))
625                 {
626                     /* Kill the CD001 mark from Joliet */
627                     Buffer = (PUCHAR)((ULONG_PTR)Irp->MdlAddress->StartVa + Irp->MdlAddress->ByteOffset);
628                     if (Buffer != NULL)
629                     {
630                         if (Buffer[0] == 2 && Buffer[1] == 'C' && Buffer[2] == 'D' &&
631                             Buffer[3] == '0' && Buffer[4] == '0' && Buffer[5] == '1')
632                         {
633                             Buffer[5] = '0';
634                         }
635                     }
636                 }
637             }
638         }
639 
640         /* Set status */
641         Status = ViSetIoStatus(Status, Stack->Parameters.Read.Length, Irp);
642     }
643     _SEH2_FINALLY
644     {
645         /* And complete! */
646         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
647     } _SEH2_END;
648 
649     return Status;
650 }
651 
652 NTSTATUS
ViCreateDevice(PDRIVER_OBJECT DriverObject,PIRP Irp)653 ViCreateDevice(PDRIVER_OBJECT DriverObject, PIRP Irp)
654 {
655     NTSTATUS Status;
656     PIO_STACK_LOCATION Stack;
657     PDEVICE_OBJECT DeviceObject;
658 
659     Stack = IoGetCurrentIrpStackLocation(Irp);
660 
661     /* Make sure output buffer is big enough to receive the drive letter
662      * when we create the drive
663      */
664     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(WCHAR))
665     {
666         return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(WCHAR), Irp);
667     }
668 
669     /* And start creation. We always start from bottom */
670     Status = ViCreateDriveLetter(DriverObject, L'Z', Irp->AssociatedIrp.SystemBuffer, &DeviceObject);
671     return ViSetIoStatus(Status, sizeof(WCHAR), Irp);
672 }
673 
674 NTSTATUS
ViGetDriveGeometry(PDEVICE_OBJECT DeviceObject,PIRP Irp)675 ViGetDriveGeometry(PDEVICE_OBJECT DeviceObject, PIRP Irp)
676 {
677     PDISK_GEOMETRY DiskGeo;
678     PIO_STACK_LOCATION Stack;
679     PDEVICE_EXTENSION DeviceExtension;
680 
681     /* No geometry if no image mounted */
682     DeviceExtension = DeviceObject->DeviceExtension;
683     if (DeviceExtension->VolumeObject == NULL)
684     {
685         return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp);
686     }
687 
688     /* No geometry if volume is to be verified */
689     Stack = IoGetCurrentIrpStackLocation(Irp);
690     if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
691         !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
692     {
693         return ViVerifyVolume(DeviceObject, Irp);
694     }
695 
696     /* No geometry if too small output buffer */
697     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
698     {
699         return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(DISK_GEOMETRY), Irp);
700     }
701 
702     /* Finally, set what we're asked for */
703     DiskGeo = Irp->AssociatedIrp.SystemBuffer;
704     DiskGeo->MediaType = RemovableMedia;
705     DiskGeo->BytesPerSector = DeviceExtension->SectorSize;
706     DiskGeo->SectorsPerTrack = DeviceExtension->VolumeSize.QuadPart >> DeviceExtension->SectorShift;
707     DiskGeo->Cylinders.QuadPart = 1;
708     DiskGeo->TracksPerCylinder = 1;
709 
710     return ViSetIoStatus(STATUS_SUCCESS, sizeof(DISK_GEOMETRY), Irp);
711 }
712 
713 NTSTATUS
ViCheckVerify(PDEVICE_OBJECT DeviceObject,PIRP Irp)714 ViCheckVerify(PDEVICE_OBJECT DeviceObject, PIRP Irp)
715 {
716     PULONG Buffer;
717     ULONG_PTR Information;
718     PIO_STACK_LOCATION Stack;
719     PDEVICE_EXTENSION DeviceExtension;
720 
721     /* Nothing to verify if no mounted */
722     DeviceExtension = DeviceObject->DeviceExtension;
723     if (DeviceExtension->VolumeObject == NULL)
724     {
725         return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp);
726     }
727 
728     /* Do we have to verify? */
729     Stack = IoGetCurrentIrpStackLocation(Irp);
730     if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
731         !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
732     {
733         return ViSetIoStatus(STATUS_VERIFY_REQUIRED, 0, Irp);
734     }
735 
736     /* If caller provided a buffer, that's to get the change count */
737     Buffer = Irp->AssociatedIrp.SystemBuffer;
738     if (Buffer != NULL)
739     {
740         *Buffer = DeviceExtension->ChangeCount;
741         Information = sizeof(ULONG);
742     }
743     else
744     {
745         Information = 0;
746     }
747 
748     /* Done */
749     return ViSetIoStatus(STATUS_SUCCESS, Information, Irp);
750 }
751 
752 NTSTATUS
ViIssueMountImage(PDEVICE_OBJECT DeviceObject,PUNICODE_STRING Image,PIRP Irp)753 ViIssueMountImage(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING Image, PIRP Irp)
754 {
755     NTSTATUS Status;
756     PDEVICE_EXTENSION DeviceExtension;
757 
758     /* We cannot mount an image if there's already one mounted */
759     DeviceExtension = DeviceObject->DeviceExtension;
760     if (DeviceExtension->VolumeObject != NULL)
761     {
762         return ViSetIoStatus(STATUS_DEVICE_NOT_READY, 0, Irp);
763     }
764 
765     /* Perform the mount */
766     Status = ViMountImage(DeviceObject, Image);
767     return ViSetIoStatus(Status, 0, Irp);
768 }
769 
770 ULONG
ViComputeAddress(ULONG Address)771 ViComputeAddress(ULONG Address)
772 {
773     UCHAR Local[4];
774 
775     /* Convert LBA to MSF */
776     Local[0] = 0;
777     Local[1] = Address / 4500;
778     Local[2] = Address % 4500 / 75;
779     Local[3] = Address + 108 * Local[1] - 75 * Local[2];
780 
781     return *(ULONG *)(&Local[0]);
782 }
783 
784 VOID
ViFillInTrackData(PTRACK_DATA TrackData,UCHAR Control,UCHAR Adr,UCHAR TrackNumber,ULONG Address)785 ViFillInTrackData(PTRACK_DATA TrackData, UCHAR Control, UCHAR Adr, UCHAR TrackNumber, ULONG Address)
786 {
787     /* Fill in our track data with provided information */
788     TrackData->Reserved = 0;
789     TrackData->Reserved1 = 0;
790     TrackData->Control = Control & 0xF;
791     TrackData->Adr = Adr;
792     TrackData->TrackNumber = TrackNumber;
793     *(ULONG *)(&TrackData->Address[0]) = ViComputeAddress(Address);
794 }
795 
796 NTSTATUS
ViReadToc(PDEVICE_OBJECT DeviceObject,PIRP Irp)797 ViReadToc(PDEVICE_OBJECT DeviceObject, PIRP Irp)
798 {
799     PCDROM_TOC Toc;
800     PIO_STACK_LOCATION Stack;
801     PDEVICE_EXTENSION DeviceExtension;
802 
803     /* No image mounted, no TOC */
804     DeviceExtension = DeviceObject->DeviceExtension;
805     if (DeviceExtension->VolumeObject == NULL)
806     {
807         return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp);
808     }
809 
810     /* No TOC if we have to verify */
811     Stack = IoGetCurrentIrpStackLocation(Irp);
812     if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
813         !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
814     {
815         return ViVerifyVolume(DeviceObject, Irp);
816     }
817 
818     /* Check we have enough room for TOC */
819     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC))
820     {
821         return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(CDROM_TOC), Irp);
822     }
823 
824     /* Start filling the TOC */
825     Toc = Irp->AssociatedIrp.SystemBuffer;
826     Toc->Length[0] = 0;
827     Toc->Length[1] = 8;
828     Toc->FirstTrack = 1;
829     Toc->LastTrack = 1;
830     /* And fill our single (an ISO file always have a single track) track with 2sec gap */
831     ViFillInTrackData(Toc->TrackData, TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, 1, 150);
832     /* And add last track termination */
833     ViFillInTrackData(&Toc->TrackData[1], TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, TOC_LAST_TRACK, (DeviceExtension->VolumeSize.QuadPart >> DeviceExtension->SectorShift) + 150);
834 
835     return ViSetIoStatus(STATUS_SUCCESS, sizeof(CDROM_TOC), Irp);
836 }
837 
838 NTSTATUS
ViReadTocEx(PDEVICE_OBJECT DeviceObject,PIRP Irp)839 ViReadTocEx(PDEVICE_OBJECT DeviceObject, PIRP Irp)
840 {
841     PCDROM_TOC Toc;
842     PIO_STACK_LOCATION Stack;
843     PCDROM_READ_TOC_EX TocEx;
844     PDEVICE_EXTENSION DeviceExtension;
845 
846     /* No image mounted, no TOC */
847     DeviceExtension = DeviceObject->DeviceExtension;
848     if (DeviceExtension->VolumeObject == NULL)
849     {
850         return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp);
851     }
852 
853     /* No TOC if we have to verify */
854     Stack = IoGetCurrentIrpStackLocation(Irp);
855     if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
856         !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
857     {
858         return ViVerifyVolume(DeviceObject, Irp);
859     }
860 
861     /* We need an input buffer */
862     if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_READ_TOC_EX))
863     {
864         return ViSetIoStatus(STATUS_INFO_LENGTH_MISMATCH, sizeof(CDROM_READ_TOC_EX), Irp);
865     }
866 
867     /* Validate output buffer is big enough */
868     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < MAXIMUM_CDROM_SIZE)
869     {
870         return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, MAXIMUM_CDROM_SIZE, Irp);
871     }
872 
873     /* Validate the input buffer - see cdrom_new */
874     TocEx = Irp->AssociatedIrp.SystemBuffer;
875     if ((TocEx->Reserved1 != 0) || (TocEx->Reserved2 != 0) ||
876         (TocEx->Reserved3 != 0))
877     {
878         return ViSetIoStatus(STATUS_INVALID_PARAMETER, 0, Irp);
879     }
880 
881     if (((TocEx->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) ||
882          (TocEx->Format == CDROM_READ_TOC_EX_FORMAT_PMA) ||
883          (TocEx->Format == CDROM_READ_TOC_EX_FORMAT_ATIP)) &&
884         TocEx->SessionTrack != 0)
885     {
886         return ViSetIoStatus(STATUS_INVALID_PARAMETER, 0, Irp);
887     }
888 
889     if ((TocEx->Format != CDROM_READ_TOC_EX_FORMAT_TOC) &&
890         (TocEx->Format != CDROM_READ_TOC_EX_FORMAT_FULL_TOC) &&
891         (TocEx->Format != CDROM_READ_TOC_EX_FORMAT_CDTEXT) &&
892         (TocEx->Format != CDROM_READ_TOC_EX_FORMAT_SESSION) &&
893         (TocEx->Format != CDROM_READ_TOC_EX_FORMAT_PMA) &&
894         (TocEx->Format == CDROM_READ_TOC_EX_FORMAT_ATIP))
895     {
896         return ViSetIoStatus(STATUS_INVALID_PARAMETER, 0, Irp);
897     }
898 
899     /* Start filling the TOC */
900     Toc = Irp->AssociatedIrp.SystemBuffer;
901     Toc->Length[0] = 0;
902     Toc->Length[1] = 8;
903     Toc->FirstTrack = 1;
904     Toc->LastTrack = 1;
905     /* And fill our single (an ISO file always have a single track) track with 2sec gap */
906     ViFillInTrackData(Toc->TrackData, TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, 1, 150);
907     /* And add last track termination */
908     ViFillInTrackData(&Toc->TrackData[1], TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, TOC_LAST_TRACK, (DeviceExtension->VolumeSize.QuadPart >> DeviceExtension->SectorShift) + 150);
909 
910     return ViSetIoStatus(STATUS_SUCCESS, MAXIMUM_CDROM_SIZE, Irp);
911 }
912 
913 NTSTATUS
ViGetLastSession(PDEVICE_OBJECT DeviceObject,PIRP Irp)914 ViGetLastSession(PDEVICE_OBJECT DeviceObject, PIRP Irp)
915 {
916 
917     PIO_STACK_LOCATION Stack;
918     PCDROM_TOC_SESSION_DATA Toc;
919     PDEVICE_EXTENSION DeviceExtension;
920 
921     /* No image, no last session */
922     DeviceExtension = DeviceObject->DeviceExtension;
923     if (DeviceExtension->VolumeObject == NULL)
924     {
925         return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp);
926     }
927 
928     /* No last session if we have to verify */
929     Stack = IoGetCurrentIrpStackLocation(Irp);
930     if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
931         !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME))
932     {
933         return ViVerifyVolume(DeviceObject, Irp);
934     }
935 
936     /* Check we have enough room for last session data */
937     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC_SESSION_DATA))
938     {
939         return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(CDROM_TOC_SESSION_DATA), Irp);
940     }
941 
942     /* Fill in data */
943     Toc = Irp->AssociatedIrp.SystemBuffer;
944     Toc->Length[0] = 0;
945     Toc->Length[1] = 8;
946     Toc->FirstCompleteSession = 1;
947     Toc->LastCompleteSession = 1;
948     /* And return our track with 2sec gap (cf TOC function) */
949     ViFillInTrackData(Toc->TrackData, TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, 1, 150);
950 
951     return ViSetIoStatus(STATUS_SUCCESS, sizeof(CDROM_TOC_SESSION_DATA), Irp);
952 }
953 
954 NTSTATUS
ViEnumerateDrives(PDEVICE_OBJECT DeviceObject,PIRP Irp)955 ViEnumerateDrives(PDEVICE_OBJECT DeviceObject, PIRP Irp)
956 {
957     PDRIVES_LIST DrivesList;
958     PIO_STACK_LOCATION Stack;
959     PDEVICE_OBJECT CurrentDO;
960     PDEVICE_EXTENSION DeviceExtension;
961 
962     /* Check we have enough room for output */
963     Stack = IoGetCurrentIrpStackLocation(Irp);
964     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DRIVES_LIST))
965     {
966         return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(DRIVES_LIST), Irp);
967     }
968 
969     /* Get the output buffer */
970     DrivesList = Irp->AssociatedIrp.SystemBuffer;
971     DrivesList->Count = 0;
972 
973     /* And now, starting from our main DO, start browsing all the DO we created */
974     for (CurrentDO = DeviceObject->DriverObject->DeviceObject; CurrentDO != NULL;
975          CurrentDO = CurrentDO->NextDevice)
976     {
977         /* Check we won't output our main DO */
978         DeviceExtension = CurrentDO->DeviceExtension;
979         if (DeviceExtension->GlobalName.Length !=
980             RtlCompareMemory(DeviceExtension->GlobalName.Buffer,
981                              L"\\??\\VirtualCdRom",
982                              DeviceExtension->GlobalName.Length))
983         {
984             /* When we return, we extract the drive letter
985              * See ViCreateDriveLetter(), it's \??\Z:
986              */
987             DrivesList->Drives[DrivesList->Count++] = DeviceExtension->GlobalName.Buffer[4];
988         }
989     }
990 
991     return ViSetIoStatus(STATUS_SUCCESS, sizeof(DRIVES_LIST), Irp);
992 }
993 
994 NTSTATUS
ViGetImagePath(PDEVICE_OBJECT DeviceObject,PIRP Irp)995 ViGetImagePath(PDEVICE_OBJECT DeviceObject, PIRP Irp)
996 {
997     PIMAGE_PATH ImagePath;
998     PIO_STACK_LOCATION Stack;
999     PDEVICE_EXTENSION DeviceExtension;
1000 
1001     /* Check we have enough room for output */
1002     Stack = IoGetCurrentIrpStackLocation(Irp);
1003     if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(IMAGE_PATH))
1004     {
1005         return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(IMAGE_PATH), Irp);
1006     }
1007 
1008     /* Get our image path from DO */
1009     DeviceExtension = DeviceObject->DeviceExtension;
1010     ImagePath = Irp->AssociatedIrp.SystemBuffer;
1011     ImagePath->Mounted = (DeviceExtension->VolumeObject != NULL);
1012     ImagePath->Length = DeviceExtension->ImageName.Length;
1013     /* And if it's set, copy it back to the caller */
1014     if (DeviceExtension->ImageName.Length != 0)
1015     {
1016         RtlCopyMemory(ImagePath->Path, DeviceExtension->ImageName.Buffer, DeviceExtension->ImageName.Length);
1017     }
1018 
1019     return ViSetIoStatus(STATUS_SUCCESS, sizeof(IMAGE_PATH), Irp);
1020 }
1021 
1022 NTSTATUS
ViEjectMedia(PDEVICE_OBJECT DeviceObject,PIRP Irp)1023 ViEjectMedia(PDEVICE_OBJECT DeviceObject, PIRP Irp)
1024 {
1025     PDEVICE_EXTENSION DeviceExtension;
1026 
1027     /* Eject will force a verify */
1028     SetFlag(DeviceObject->Flags, DO_VERIFY_VOLUME);
1029 
1030     /* If we have an image mounted, unmount it
1031      * But don't free anything related, so that
1032      * we can perform quick remount.
1033      * See: IOCTL_STORAGE_LOAD_MEDIA
1034      */
1035     DeviceExtension = DeviceObject->DeviceExtension;
1036     if (DeviceExtension->VolumeObject != NULL)
1037     {
1038         ObDereferenceObject(DeviceExtension->VolumeObject);
1039         /* Device changed, so mandatory increment */
1040         ++DeviceExtension->ChangeCount;
1041         DeviceExtension->VolumeObject = NULL;
1042     }
1043 
1044     return ViSetIoStatus(STATUS_SUCCESS, 0, Irp);
1045 }
1046 
1047 NTSTATUS
ViRemountMedia(PDEVICE_OBJECT DeviceObject,PIRP Irp)1048 ViRemountMedia(PDEVICE_OBJECT DeviceObject, PIRP Irp)
1049 {
1050     NTSTATUS Status;
1051     UNICODE_STRING Image;
1052     PDEVICE_EXTENSION DeviceExtension;
1053 
1054     /* Get the device extension */
1055     DeviceExtension = DeviceObject->DeviceExtension;
1056     /* Allocate a new string as mount parameter */
1057     Status = ViAllocateUnicodeString(DeviceExtension->ImageName.MaximumLength, &Image);
1058     if (!NT_SUCCESS(Status))
1059     {
1060         return ViSetIoStatus(Status, 0, Irp);
1061     }
1062 
1063     /* To allow cleanup in case of troubles */
1064     _SEH2_TRY
1065     {
1066         /* Copy our current image name and mount */
1067         RtlCopyUnicodeString(&Image, &DeviceExtension->ImageName);
1068         Status = ViIssueMountImage(DeviceObject, &Image, Irp);
1069     }
1070     _SEH2_FINALLY
1071     {
1072         ViFreeUnicodeString(&Image);
1073     }
1074     _SEH2_END;
1075 
1076     return ViSetIoStatus(Status, 0, Irp);
1077 }
1078 
1079 NTSTATUS
1080 NTAPI
VcdDeviceControl(PDEVICE_OBJECT DeviceObject,PIRP Irp)1081 VcdDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
1082 {
1083     NTSTATUS Status;
1084     UNICODE_STRING Image;
1085     PIO_STACK_LOCATION Stack;
1086     PMOUNT_PARAMETERS MountParameters;
1087     PDEVICE_EXTENSION DeviceExtension;
1088 
1089     DeviceExtension = DeviceObject->DeviceExtension;
1090     Stack = IoGetCurrentIrpStackLocation(Irp);
1091 
1092     _SEH2_TRY
1093     {
1094         switch (Stack->Parameters.DeviceIoControl.IoControlCode)
1095         {
1096             /* First of all, our private IOCTLs */
1097             case IOCTL_VCDROM_CREATE_DRIVE:
1098                 Status = ViCreateDevice(DeviceObject->DriverObject, Irp);
1099                 break;
1100 
1101             case IOCTL_VCDROM_DELETE_DRIVE:
1102                 Status = ViDeleteDevice(DeviceObject, Irp);
1103                 break;
1104 
1105             case IOCTL_VCDROM_MOUNT_IMAGE:
1106                 MountParameters = Irp->AssociatedIrp.SystemBuffer;
1107                 Image.MaximumLength = 255 * sizeof(WCHAR);
1108                 Image.Length = MountParameters->Length;
1109                 Image.Buffer = MountParameters->Path;
1110                 DeviceExtension->Flags = MountParameters->Flags;
1111                 Status = ViIssueMountImage(DeviceObject, &Image, Irp);
1112                 break;
1113 
1114             case IOCTL_VCDROM_ENUMERATE_DRIVES:
1115                 Status = ViEnumerateDrives(DeviceObject, Irp);
1116                 break;
1117 
1118             case IOCTL_VCDROM_GET_IMAGE_PATH:
1119                 Status = ViGetImagePath(DeviceObject, Irp);
1120                 break;
1121 
1122             /* Now, IOCTLs we have to handle as class driver */
1123             case IOCTL_DISK_GET_DRIVE_GEOMETRY:
1124             case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
1125                 Status = ViGetDriveGeometry(DeviceObject, Irp);
1126                 break;
1127 
1128             case IOCTL_DISK_CHECK_VERIFY:
1129             case IOCTL_CDROM_CHECK_VERIFY:
1130                 Status = ViCheckVerify(DeviceObject, Irp);
1131                 break;
1132 
1133             case IOCTL_CDROM_READ_TOC:
1134                 Status = ViReadToc(DeviceObject, Irp);
1135                 break;
1136 
1137             case IOCTL_CDROM_READ_TOC_EX:
1138                 Status = ViReadTocEx(DeviceObject, Irp);
1139                 break;
1140 
1141             case IOCTL_CDROM_GET_LAST_SESSION:
1142                 Status = ViGetLastSession(DeviceObject, Irp);
1143                 break;
1144 
1145             case IOCTL_STORAGE_EJECT_MEDIA:
1146             case IOCTL_CDROM_EJECT_MEDIA:
1147                 Status = ViEjectMedia(DeviceObject, Irp);
1148                 break;
1149 
1150             /* That one is a bit specific
1151              * It gets unmounted image mounted again
1152              */
1153             case IOCTL_STORAGE_LOAD_MEDIA:
1154                 /* That means it can only be performed if:
1155                  * - We had an image previously
1156                  * - It's no longer mounted
1157                  * Otherwise, we just return success
1158                  */
1159                 if (DeviceExtension->ImageName.Buffer == NULL || DeviceExtension->VolumeObject != NULL)
1160                 {
1161                     Status = ViSetIoStatus(STATUS_SUCCESS, 0, Irp);
1162                 }
1163                 else
1164                 {
1165                     Status = ViRemountMedia(DeviceObject, Irp);
1166                 }
1167                 break;
1168 
1169             default:
1170                 Status = STATUS_INVALID_DEVICE_REQUEST;
1171                 DPRINT1("IOCTL: %x not supported\n", Stack->Parameters.DeviceIoControl.IoControlCode);
1172                 break;
1173         }
1174     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1175     {
1176         Status = _SEH2_GetExceptionCode();
1177     } _SEH2_END;
1178 
1179     /* Depending on the failure code, we may force a verify */
1180     if (!NT_SUCCESS(Status))
1181     {
1182         if (Status == STATUS_DEVICE_NOT_READY || Status == STATUS_IO_TIMEOUT ||
1183             Status == STATUS_MEDIA_WRITE_PROTECTED || Status == STATUS_NO_MEDIA_IN_DEVICE ||
1184             Status == STATUS_VERIFY_REQUIRED || Status == STATUS_UNRECOGNIZED_MEDIA ||
1185             Status == STATUS_WRONG_VOLUME)
1186         {
1187             IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
1188         }
1189     }
1190 
1191     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1192 
1193     return Status;
1194 }
1195 
1196 NTSTATUS
1197 NTAPI
DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)1198 DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
1199 {
1200     NTSTATUS Status;
1201     UNICODE_STRING DeviceName;
1202     PDEVICE_OBJECT DeviceObject;
1203     PDEVICE_EXTENSION DeviceExtension;
1204 
1205     /* Set our entry points (rather limited :-)) */
1206     DriverObject->DriverUnload = VcdUnload;
1207     DriverObject->MajorFunction[IRP_MJ_CREATE] = VcdHandle;
1208     DriverObject->MajorFunction[IRP_MJ_CLOSE] = VcdHandle;
1209     DriverObject->MajorFunction[IRP_MJ_CLEANUP] = VcdHandle;
1210     DriverObject->MajorFunction[IRP_MJ_READ] = VcdRead;
1211     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VcdDeviceControl;
1212 
1213     /* Create our main device to receive private IOCTLs */
1214     RtlInitUnicodeString(&DeviceName, L"\\Device\\VirtualCdRom");
1215     Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceName,
1216                             FILE_DEVICE_CD_ROM, FILE_READ_ONLY_DEVICE | FILE_FLOPPY_DISKETTE,
1217                             FALSE, &DeviceObject);
1218     if (!NT_SUCCESS(Status))
1219     {
1220         return Status;
1221     }
1222 
1223     /* Initialize our device extension */
1224     ViInitializeDeviceExtension(DeviceObject);
1225     DeviceExtension = DeviceObject->DeviceExtension;
1226 
1227     /* And create our accessible name from umode */
1228     ViAllocateUnicodeString(DEFAULT_STRING_SIZE, &DeviceExtension->GlobalName);
1229     RtlAppendUnicodeToString(&DeviceExtension->GlobalName, L"\\??\\VirtualCdRom");
1230     Status = IoCreateSymbolicLink(&DeviceExtension->GlobalName, &DeviceName);
1231     if (!NT_SUCCESS(Status))
1232     {
1233         IoDeleteDevice(DeviceObject);
1234         return Status;
1235     }
1236 
1237     /* Initialize our mutex for device count */
1238     ExInitializeFastMutex(&ViMutex);
1239 
1240     /* And try to load images that would have been stored in registry */
1241     ViLoadImagesFromRegistry(DriverObject, RegistryPath->Buffer);
1242 
1243     return STATUS_SUCCESS;
1244 }
1245