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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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