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