Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS FS utility tool * FILE: modules/rosapps/drivers/vcdrom/vcdrom.c * PURPOSE: Virtual CD-ROM class driver * PROGRAMMERS: Pierre Schweitzer <pierre@reactos.org> */
#include <ntddk.h>#include <ntifs.h>#include <ntdddisk.h>#include <ntddcdrm.h>#include <pseh/pseh2.h>
#define NDEBUG#include <debug.h>
#include "vcdioctl.h"
typedef struct _DEVICE_EXTENSION{ PDEVICE_OBJECT DeviceObject; /* File object to the ISO when opened */ PFILE_OBJECT VolumeObject; /* Size of the ISO */ LARGE_INTEGER VolumeSize; /* Mandatory change count for verify */ ULONG ChangeCount; /* Will contain the device name or the drive letter */ UNICODE_STRING GlobalName; /* Will contain the image path */ UNICODE_STRING ImageName; /* Flags on mount (supp. of UDF/Joliet) */ ULONG Flags; /* Faking CD structure */ ULONG SectorSize; ULONG SectorShift;} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
/* Taken from CDFS */#define TOC_DATA_TRACK 0x04#define TOC_LAST_TRACK 0xaa
/* Should cover most of our usages */#define DEFAULT_STRING_SIZE 50
/* Increment on device creation, protection by the mutex */FAST_MUTEX ViMutex;ULONG ViDevicesCount;
VOIDViInitializeDeviceExtension(PDEVICE_OBJECT DeviceObject){ PDEVICE_EXTENSION DeviceExtension;
DeviceExtension = DeviceObject->DeviceExtension;
/* Zero mandatory fields, the rest will be zeroed when needed */ DeviceExtension->DeviceObject = DeviceObject; DeviceExtension->VolumeObject = NULL; DeviceExtension->ChangeCount = 0; DeviceExtension->GlobalName.Buffer = NULL; DeviceExtension->GlobalName.MaximumLength = 0; DeviceExtension->GlobalName.Length = 0; DeviceExtension->ImageName.Buffer = NULL; DeviceExtension->ImageName.MaximumLength = 0; DeviceExtension->ImageName.Length = 0;}
NTSTATUSViAllocateUnicodeString(USHORT BufferLength, PUNICODE_STRING UnicodeString){ PVOID Buffer;
/* Allocate the buffer */ Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferLength, ' dCV'); /* Initialize */ UnicodeString->Length = 0; UnicodeString->MaximumLength = BufferLength; UnicodeString->Buffer = Buffer;
/* Return success if it went fine */ if (Buffer != NULL) { return STATUS_SUCCESS; }
return STATUS_NO_MEMORY;}
VOIDViFreeUnicodeString(PUNICODE_STRING UnicodeString){ /* Only free if allocate, that allows using this * on cleanup in short memory situations */ if (UnicodeString->Buffer != NULL) { ExFreePoolWithTag(UnicodeString->Buffer, 0); UnicodeString->Buffer = NULL; }
/* Zero the rest */ UnicodeString->Length = 0; UnicodeString->MaximumLength = 0;}
NTSTATUSViSetIoStatus(NTSTATUS Status, ULONG_PTR Information, PIRP Irp){ /* Only set what we got */ Irp->IoStatus.Information = Information; Irp->IoStatus.Status = Status;
/* And return the status, so that caller can return with us */ return Status;}
NTSTATUSNTAPIVcdHandle(PDEVICE_OBJECT DeviceObject, PIRP Irp){ /* Stub for CREATE, CLEANUP, CLOSE, always a succes */ ViSetIoStatus(STATUS_SUCCESS, 0, Irp); IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return STATUS_SUCCESS;}
NTSTATUSViDeleteDevice(PDEVICE_OBJECT DeviceObject, PIRP Irp){ NTSTATUS Status; PDEVICE_EXTENSION DeviceExtension;
DeviceExtension = DeviceObject->DeviceExtension;
/* Delete the drive letter */ Status = IoDeleteSymbolicLink(&DeviceExtension->GlobalName); ViFreeUnicodeString(&DeviceExtension->GlobalName);
/* Close the ISO */ if (DeviceExtension->VolumeObject != NULL) { ObDereferenceObject(DeviceExtension->VolumeObject); DeviceExtension->VolumeObject = NULL; }
/* Free the ISO name */ ViFreeUnicodeString(&DeviceExtension->ImageName);
/* And delete the device */ IoDeleteDevice(DeviceObject); return ViSetIoStatus(Status, 0, Irp);}
VOIDNTAPIVcdUnload(PDRIVER_OBJECT DriverObject){ IRP FakeIrp; PDEVICE_OBJECT DeviceObject;
/* No device, nothing to free */ DeviceObject = DriverObject->DeviceObject; if (DeviceObject == NULL) { return; }
/* Delete any device we could have created */ do { PDEVICE_OBJECT NextDevice;
NextDevice = DeviceObject->NextDevice; /* This is normally called on IoCtl, so fake * the IRP so that status can be dummily set */ ViDeleteDevice(DeviceObject, &FakeIrp); DeviceObject = NextDevice; } while (DeviceObject != NULL);}
NTSTATUSViCreateDriveLetter(PDRIVER_OBJECT DriverObject, WCHAR Letter, WCHAR *EffectiveLetter, PDEVICE_OBJECT *DeviceObject){ NTSTATUS Status; HANDLE LinkHandle; WCHAR DriveLetter[3], CurLetter; PDEVICE_OBJECT LocalDeviceObject; PDEVICE_EXTENSION DeviceExtension; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING DeviceCount, DeviceName;
*DeviceObject = NULL;
/* Allocate our buffers */ ViAllocateUnicodeString(DEFAULT_STRING_SIZE, &DeviceCount); ViAllocateUnicodeString(DEFAULT_STRING_SIZE, &DeviceName);
/* For easier cleanup */ _SEH2_TRY { /* Get our device number */ ExAcquireFastMutex(&ViMutex); Status = RtlIntegerToUnicodeString(ViDevicesCount, 10, &DeviceCount); ++ViDevicesCount; ExReleaseFastMutex(&ViMutex);
/* Conversion to string failed, bail out */ if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; }
/* Create our device name */ Status = RtlAppendUnicodeToString(&DeviceName, L"\\Device\\VirtualCdRom"); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; }
Status = RtlAppendUnicodeStringToString(&DeviceName, &DeviceCount); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; }
/* And create the device! */ Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_CD_ROM, FILE_READ_ONLY_DEVICE | FILE_FLOPPY_DISKETTE, FALSE, &LocalDeviceObject); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; }
/* Initialize our DE */ ViInitializeDeviceExtension(LocalDeviceObject); DeviceExtension = LocalDeviceObject->DeviceExtension; ViAllocateUnicodeString(DEFAULT_STRING_SIZE, &DeviceExtension->GlobalName);
/* Now, we'll try to find a free drive letter * We always start from Z and go backward */ for (CurLetter = Letter; CurLetter >= 'A'; CurLetter--) { /* By default, no flags. These will be set * on mount, by the caller */ DeviceExtension->Flags = 0;
/* Create a drive letter name */ DeviceExtension->GlobalName.Length = 0; Status = RtlAppendUnicodeToString(&DeviceExtension->GlobalName, L"\\??\\"); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; }
DriveLetter[0] = CurLetter; DriveLetter[1] = L':'; DriveLetter[2] = UNICODE_NULL; Status = RtlAppendUnicodeToString(&DeviceExtension->GlobalName, DriveLetter); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; }
/* And try to open it */ InitializeObjectAttributes(&ObjectAttributes, &DeviceExtension->GlobalName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL); Status = ZwOpenSymbolicLinkObject(&LinkHandle, GENERIC_READ, &ObjectAttributes); /* It failed; good news, that letter is free, jump on it! */ if (!NT_SUCCESS(Status)) { /* Create a symbolic link to our device */ Status = IoCreateSymbolicLink(&DeviceExtension->GlobalName, &DeviceName); /* If created... */ if (NT_SUCCESS(Status)) { /* Return the drive letter to the caller */ *EffectiveLetter = CurLetter; ClearFlag(LocalDeviceObject->Flags, DO_DEVICE_INITIALIZING); /* No caching! */ SetFlag(LocalDeviceObject->Flags, DO_DIRECT_IO);
/* And return the DO */ *DeviceObject = LocalDeviceObject; break; } } /* This letter is taken, try another one */ else { ZwClose(LinkHandle); Status = STATUS_OBJECT_NAME_EXISTS; } }
/* We're out of drive letters, so fail :-( */ if (CurLetter == (L'A' - 1)) { Status = STATUS_INSUFFICIENT_RESOURCES; } } _SEH2_FINALLY { /* No longer need these */ ViFreeUnicodeString(&DeviceName); ViFreeUnicodeString(&DeviceCount);
/* And delete in case of a failure */ if (!NT_SUCCESS(Status) && Status != STATUS_VERIFY_REQUIRED && LocalDeviceObject != NULL) { IoDeleteDevice(LocalDeviceObject); } } _SEH2_END;
return Status;}
NTSTATUSViMountImage(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING Image){ NTSTATUS Status; HANDLE ImgHandle; IO_STATUS_BLOCK IoStatus; ULONG Buffer[2], i, SectorShift; PDEVICE_EXTENSION DeviceExtension; OBJECT_ATTRIBUTES ObjectAttributes; FILE_STANDARD_INFORMATION FileStdInfo;
/* Try to open the image */ InitializeObjectAttributes(&ObjectAttributes, Image, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwCreateFile(&ImgHandle, FILE_READ_DATA | SYNCHRONIZE, &ObjectAttributes, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to open image: %wZ\n", Image); return Status; }
/* Query its information */ Status = ZwQueryInformationFile(ImgHandle, &IoStatus, &FileStdInfo, sizeof(FileStdInfo), FileStandardInformation); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to query image information\n"); ZwClose(ImgHandle); return Status; }
/* Set image size */ DeviceExtension = DeviceObject->DeviceExtension; DeviceExtension->VolumeSize.QuadPart = FileStdInfo.EndOfFile.QuadPart;
/* Read the first 8-bytes to determine our sector size */ Status = ZwReadFile(ImgHandle, NULL, NULL, NULL, &IoStatus, Buffer, sizeof(Buffer), NULL, NULL); if (!NT_SUCCESS(Status) || Buffer[1] == 0 || (Buffer[1] & 0x1FF) != 0) { DeviceExtension->SectorSize = 2048; } else { DeviceExtension->SectorSize = Buffer[1]; }
/* Now, compute the sector shift */ if (DeviceExtension->SectorSize == 512) { DeviceExtension->SectorShift = 9; } else { for (i = 512, SectorShift = 9; i < DeviceExtension->SectorSize; i = i * 2, ++SectorShift); if (i == DeviceExtension->SectorSize) { DeviceExtension->SectorShift = SectorShift; } else { DeviceExtension->SectorShift = 11; } }
/* We're good, get the image file object to close the handle */ Status = ObReferenceObjectByHandle(ImgHandle, 0, NULL, KernelMode, (PVOID *)&DeviceExtension->VolumeObject, NULL); if (!NT_SUCCESS(Status)) { ZwClose(ImgHandle); DeviceExtension->VolumeObject = NULL; return Status; }
ZwClose(ImgHandle); /* Increase change count to force a verify */ ++DeviceExtension->ChangeCount;
/* And ask for a verify! */ SetFlag(DeviceObject->Flags, DO_VERIFY_VOLUME);
/* Free old string (if any) */ ViFreeUnicodeString(&DeviceExtension->ImageName); /* And save our new image name */ ViAllocateUnicodeString(Image->MaximumLength, &DeviceExtension->ImageName); RtlCopyUnicodeString(&DeviceExtension->ImageName, Image);
return STATUS_SUCCESS;}
VOIDViCreateDriveAndMountImage(PDRIVER_OBJECT DriverObject, WCHAR Letter, LPCWSTR ImagePath){ UNICODE_STRING Image; WCHAR EffectiveLetter; PDEVICE_OBJECT DeviceObject;
/* Create string from path */ DeviceObject = NULL; RtlInitUnicodeString(&Image, ImagePath);
/* Create the drive letter (ignore output, it comes from registry, nothing to do */ ViCreateDriveLetter(DriverObject, Letter, &EffectiveLetter, &DeviceObject); /* And mount the image on the created drive */ ViMountImage(DeviceObject, &Image);}
VOIDViLoadImagesFromRegistry(PDRIVER_OBJECT DriverObject, LPCWSTR RegistryPath){ WCHAR Letter[2]; WCHAR PathBuffer[100], ResBuffer[100];
/* We'll browse the registry for * DeviceX\IMAGE = {Path} * When found, we create (or at least, attempt to) * device X: and mount image Path */ Letter[0] = L'A'; Letter[1] = UNICODE_NULL; do { NTSTATUS Status; UNICODE_STRING Path, ResString; RTL_QUERY_REGISTRY_TABLE QueryTable[3];
/* Our path is always Device + drive letter */ RtlZeroMemory(PathBuffer, sizeof(PathBuffer)); Path.Buffer = PathBuffer; Path.Length = 0; Path.MaximumLength = sizeof(PathBuffer); RtlAppendUnicodeToString(&Path, L"Parameters\\Device"); RtlAppendUnicodeToString(&Path, Letter);
ResString.Buffer = ResBuffer; ResString.Length = 0; ResString.MaximumLength = sizeof(ResBuffer);
RtlZeroMemory(&QueryTable[0], sizeof(QueryTable)); QueryTable[0].Name = Path.Buffer; QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; QueryTable[1].Name = L"IMAGE"; QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; QueryTable[1].EntryContext = &ResString;
/* If query went fine, attempt to create and mount */ Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, RegistryPath, QueryTable, NULL, NULL); if (NT_SUCCESS(Status)) { ViCreateDriveAndMountImage(DriverObject, Letter[0], ResString.Buffer); }
++(Letter[0]); } while (Letter[0] <= L'Z');}
NTSTATUSViVerifyVolume(PDEVICE_OBJECT DeviceObject, PIRP Irp){ /* Force verify */ SetFlag(DeviceObject->Flags, DO_VERIFY_VOLUME);
Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
return STATUS_VERIFY_REQUIRED;}
NTSTATUSViReadFile(PFILE_OBJECT File, PMDL Mdl, PLARGE_INTEGER Offset, PKEVENT Event, ULONG Length, PIO_STATUS_BLOCK IoStatusBlock){ PIRP LowerIrp; PIO_STACK_LOCATION Stack; PDEVICE_OBJECT DeviceObject;
/* Get the lower DO */ DeviceObject = IoGetRelatedDeviceObject(File); /* Allocate an IRP to deal with it */ LowerIrp = IoAllocateIrp(DeviceObject->StackSize, 0); if (LowerIrp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
/* Initialize it */ LowerIrp->RequestorMode = KernelMode; /* Our status block */ LowerIrp->UserIosb = IoStatusBlock; LowerIrp->MdlAddress = Mdl; /* We don't cache! */ LowerIrp->Flags = IRP_NOCACHE | IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO; /* Sync event for us to know when read is done */ LowerIrp->UserEvent = Event; LowerIrp->UserBuffer = (PVOID)((ULONG_PTR)Mdl->StartVa + Mdl->ByteOffset); /* The FO of our image */ LowerIrp->Tail.Overlay.OriginalFileObject = File; LowerIrp->Tail.Overlay.Thread = PsGetCurrentThread();
/* We basically perform a read operation, nothing complex here */ Stack = IoGetNextIrpStackLocation(LowerIrp); Stack->Parameters.Read.Length = Length; Stack->MajorFunction = IRP_MJ_READ; Stack->FileObject = File; Stack->Parameters.Read.ByteOffset.QuadPart = Offset->QuadPart;
/* And call lower driver */ return IoCallDriver(DeviceObject, LowerIrp);}
NTSTATUSNTAPIVcdRead(PDEVICE_OBJECT DeviceObject, PIRP Irp){ KEVENT Event; NTSTATUS Status; IO_STATUS_BLOCK IoStatus; PIO_STACK_LOCATION Stack; PDEVICE_EXTENSION DeviceExtension;
/* Catch any exception that could happen during read attempt */ _SEH2_TRY { /* First of all, check if there's an image mounted */ DeviceExtension = DeviceObject->DeviceExtension; if (DeviceExtension->VolumeObject == NULL) { Status = ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp); _SEH2_LEAVE; }
/* Check if volume has to be verified (or if we override, for instance * FSD performing initial reads for mouting FS) */ Stack = IoGetCurrentIrpStackLocation(Irp); if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) && !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { Status = ViVerifyVolume(DeviceObject, Irp); _SEH2_LEAVE; }
/* Check if we have enough room for output */ if (Stack->Parameters.Read.Length > Irp->MdlAddress->ByteCount) { Status = ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, 0, Irp); _SEH2_LEAVE; }
/* Initialize our event */ KeInitializeEvent(&Event, NotificationEvent, FALSE);
/* Perform actual read */ Status = ViReadFile(DeviceExtension->VolumeObject, Irp->MdlAddress, &Stack->Parameters.Read.ByteOffset, &Event, Stack->Parameters.Read.Length, &IoStatus); if (!NT_SUCCESS(Status)) { Status = ViSetIoStatus(Status, 0, Irp); _SEH2_LEAVE; }
/* Make sure we wait until its done */ Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); if (!NT_SUCCESS(Status)) { Status = ViSetIoStatus(Status, 0, Irp); _SEH2_LEAVE; }
/* Look whether we're reading first bytes of the volume, if so, our * "suppression" might be asked for */ if (Stack->Parameters.Read.ByteOffset.QuadPart / DeviceExtension->SectorSize < 0x20) { /* Check our output buffer is in a state where we can play with it */ if ((Irp->MdlAddress->MdlFlags & (MDL_ALLOCATED_FIXED_SIZE | MDL_PAGES_LOCKED | MDL_MAPPED_TO_SYSTEM_VA)) == (MDL_ALLOCATED_FIXED_SIZE | MDL_PAGES_LOCKED | MDL_MAPPED_TO_SYSTEM_VA)) { PUCHAR Buffer;
/* Do we have to delete any UDF mark? */ if (BooleanFlagOn(DeviceExtension->Flags, MOUNT_FLAG_SUPP_UDF)) { /* Kill any NSR0X mark from UDF */ Buffer = (PUCHAR)((ULONG_PTR)Irp->MdlAddress->StartVa + Irp->MdlAddress->ByteOffset); if (Buffer != NULL) { if (Buffer[0] == 0 && Buffer[1] == 'N' && Buffer[2] == 'S' && Buffer[3] == 'R' && Buffer[4] == '0') { Buffer[5] = '0'; } } }
/* Do we have to delete any Joliet mark? */ if (BooleanFlagOn(DeviceExtension->Flags, MOUNT_FLAG_SUPP_JOLIET)) { /* Kill the CD001 mark from Joliet */ Buffer = (PUCHAR)((ULONG_PTR)Irp->MdlAddress->StartVa + Irp->MdlAddress->ByteOffset); if (Buffer != NULL) { if (Buffer[0] == 2 && Buffer[1] == 'C' && Buffer[2] == 'D' && Buffer[3] == '0' && Buffer[4] == '0' && Buffer[5] == '1') { Buffer[5] = '0'; } } } } }
/* Set status */ Status = ViSetIoStatus(Status, Stack->Parameters.Read.Length, Irp); } _SEH2_FINALLY { /* And complete! */ IoCompleteRequest(Irp, IO_DISK_INCREMENT); } _SEH2_END;
return Status;}
NTSTATUSViCreateDevice(PDRIVER_OBJECT DriverObject, PIRP Irp){ NTSTATUS Status; PIO_STACK_LOCATION Stack; PDEVICE_OBJECT DeviceObject;
Stack = IoGetCurrentIrpStackLocation(Irp);
/* Make sure output buffer is big enough to receive the drive letter * when we create the drive */ if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(WCHAR)) { return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(WCHAR), Irp); }
/* And start creation. We always start from bottom */ Status = ViCreateDriveLetter(DriverObject, L'Z', Irp->AssociatedIrp.SystemBuffer, &DeviceObject); return ViSetIoStatus(Status, sizeof(WCHAR), Irp);}
NTSTATUSViGetDriveGeometry(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PDISK_GEOMETRY DiskGeo; PIO_STACK_LOCATION Stack; PDEVICE_EXTENSION DeviceExtension;
/* No geometry if no image mounted */ DeviceExtension = DeviceObject->DeviceExtension; if (DeviceExtension->VolumeObject == NULL) { return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp); }
/* No geometry if volume is to be verified */ Stack = IoGetCurrentIrpStackLocation(Irp); if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) && !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { return ViVerifyVolume(DeviceObject, Irp); }
/* No geometry if too small output buffer */ if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) { return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(DISK_GEOMETRY), Irp); }
/* Finally, set what we're asked for */ DiskGeo = Irp->AssociatedIrp.SystemBuffer; DiskGeo->MediaType = RemovableMedia; DiskGeo->BytesPerSector = DeviceExtension->SectorSize; DiskGeo->SectorsPerTrack = DeviceExtension->VolumeSize.QuadPart >> DeviceExtension->SectorShift; DiskGeo->Cylinders.QuadPart = 1; DiskGeo->TracksPerCylinder = 1;
return ViSetIoStatus(STATUS_SUCCESS, sizeof(DISK_GEOMETRY), Irp);}
NTSTATUSViCheckVerify(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PULONG Buffer; ULONG_PTR Information; PIO_STACK_LOCATION Stack; PDEVICE_EXTENSION DeviceExtension;
/* Nothing to verify if no mounted */ DeviceExtension = DeviceObject->DeviceExtension; if (DeviceExtension->VolumeObject == NULL) { return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp); }
/* Do we have to verify? */ Stack = IoGetCurrentIrpStackLocation(Irp); if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) && !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { return ViSetIoStatus(STATUS_VERIFY_REQUIRED, 0, Irp); }
/* If caller provided a buffer, that's to get the change count */ Buffer = Irp->AssociatedIrp.SystemBuffer; if (Buffer != NULL) { *Buffer = DeviceExtension->ChangeCount; Information = sizeof(ULONG); } else { Information = 0; }
/* Done */ return ViSetIoStatus(STATUS_SUCCESS, Information, Irp);}
NTSTATUSViIssueMountImage(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING Image, PIRP Irp){ NTSTATUS Status; PDEVICE_EXTENSION DeviceExtension;
/* We cannot mount an image if there's already one mounted */ DeviceExtension = DeviceObject->DeviceExtension; if (DeviceExtension->VolumeObject != NULL) { return ViSetIoStatus(STATUS_DEVICE_NOT_READY, 0, Irp); }
/* Perform the mount */ Status = ViMountImage(DeviceObject, Image); return ViSetIoStatus(Status, 0, Irp);}
ULONGViComputeAddress(ULONG Address){ UCHAR Local[4];
/* Convert LBA to MSF */ Local[0] = 0; Local[1] = Address / 4500; Local[2] = Address % 4500 / 75; Local[3] = Address + 108 * Local[1] - 75 * Local[2];
return *(ULONG *)(&Local[0]);}
VOIDViFillInTrackData(PTRACK_DATA TrackData, UCHAR Control, UCHAR Adr, UCHAR TrackNumber, ULONG Address){ /* Fill in our track data with provided information */ TrackData->Reserved = 0; TrackData->Reserved1 = 0; TrackData->Control = Control & 0xF; TrackData->Adr = Adr; TrackData->TrackNumber = TrackNumber; *(ULONG *)(&TrackData->Address[0]) = ViComputeAddress(Address);}
NTSTATUSViReadToc(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PCDROM_TOC Toc; PIO_STACK_LOCATION Stack; PDEVICE_EXTENSION DeviceExtension;
/* No image mounted, no TOC */ DeviceExtension = DeviceObject->DeviceExtension; if (DeviceExtension->VolumeObject == NULL) { return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp); }
/* No TOC if we have to verify */ Stack = IoGetCurrentIrpStackLocation(Irp); if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) && !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { return ViVerifyVolume(DeviceObject, Irp); }
/* Check we have enough room for TOC */ if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC)) { return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(CDROM_TOC), Irp); }
/* Start filling the TOC */ Toc = Irp->AssociatedIrp.SystemBuffer; Toc->Length[0] = 0; Toc->Length[1] = 8; Toc->FirstTrack = 1; Toc->LastTrack = 1; /* And fill our single (an ISO file always have a single track) track with 2sec gap */ ViFillInTrackData(Toc->TrackData, TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, 1, 150); /* And add last track termination */ ViFillInTrackData(&Toc->TrackData[1], TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, TOC_LAST_TRACK, (DeviceExtension->VolumeSize.QuadPart >> DeviceExtension->SectorShift) + 150);
return ViSetIoStatus(STATUS_SUCCESS, sizeof(CDROM_TOC), Irp);}
NTSTATUSViReadTocEx(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PCDROM_TOC Toc; PIO_STACK_LOCATION Stack; PCDROM_READ_TOC_EX TocEx; PDEVICE_EXTENSION DeviceExtension;
/* No image mounted, no TOC */ DeviceExtension = DeviceObject->DeviceExtension; if (DeviceExtension->VolumeObject == NULL) { return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp); }
/* No TOC if we have to verify */ Stack = IoGetCurrentIrpStackLocation(Irp); if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) && !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { return ViVerifyVolume(DeviceObject, Irp); }
/* We need an input buffer */ if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_READ_TOC_EX)) { return ViSetIoStatus(STATUS_INFO_LENGTH_MISMATCH, sizeof(CDROM_READ_TOC_EX), Irp); }
/* Validate output buffer is big enough */ if (Stack->Parameters.DeviceIoControl.OutputBufferLength < MAXIMUM_CDROM_SIZE) { return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, MAXIMUM_CDROM_SIZE, Irp); }
/* Validate the input buffer - see cdrom_new */ TocEx = Irp->AssociatedIrp.SystemBuffer; if ((TocEx->Reserved1 != 0) || (TocEx->Reserved2 != 0) || (TocEx->Reserved3 != 0)) { return ViSetIoStatus(STATUS_INVALID_PARAMETER, 0, Irp); }
if (((TocEx->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) || (TocEx->Format == CDROM_READ_TOC_EX_FORMAT_PMA) || (TocEx->Format == CDROM_READ_TOC_EX_FORMAT_ATIP)) && TocEx->SessionTrack != 0) { return ViSetIoStatus(STATUS_INVALID_PARAMETER, 0, Irp); }
if ((TocEx->Format != CDROM_READ_TOC_EX_FORMAT_TOC) && (TocEx->Format != CDROM_READ_TOC_EX_FORMAT_FULL_TOC) && (TocEx->Format != CDROM_READ_TOC_EX_FORMAT_CDTEXT) && (TocEx->Format != CDROM_READ_TOC_EX_FORMAT_SESSION) && (TocEx->Format != CDROM_READ_TOC_EX_FORMAT_PMA) && (TocEx->Format == CDROM_READ_TOC_EX_FORMAT_ATIP)) { return ViSetIoStatus(STATUS_INVALID_PARAMETER, 0, Irp); }
/* Start filling the TOC */ Toc = Irp->AssociatedIrp.SystemBuffer; Toc->Length[0] = 0; Toc->Length[1] = 8; Toc->FirstTrack = 1; Toc->LastTrack = 1; /* And fill our single (an ISO file always have a single track) track with 2sec gap */ ViFillInTrackData(Toc->TrackData, TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, 1, 150); /* And add last track termination */ ViFillInTrackData(&Toc->TrackData[1], TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, TOC_LAST_TRACK, (DeviceExtension->VolumeSize.QuadPart >> DeviceExtension->SectorShift) + 150);
return ViSetIoStatus(STATUS_SUCCESS, MAXIMUM_CDROM_SIZE, Irp);}
NTSTATUSViGetLastSession(PDEVICE_OBJECT DeviceObject, PIRP Irp){
PIO_STACK_LOCATION Stack; PCDROM_TOC_SESSION_DATA Toc; PDEVICE_EXTENSION DeviceExtension;
/* No image, no last session */ DeviceExtension = DeviceObject->DeviceExtension; if (DeviceExtension->VolumeObject == NULL) { return ViSetIoStatus(STATUS_NO_MEDIA_IN_DEVICE, 0, Irp); }
/* No last session if we have to verify */ Stack = IoGetCurrentIrpStackLocation(Irp); if (BooleanFlagOn(DeviceObject->Flags, DO_VERIFY_VOLUME) && !BooleanFlagOn(Stack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { return ViVerifyVolume(DeviceObject, Irp); }
/* Check we have enough room for last session data */ if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC_SESSION_DATA)) { return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(CDROM_TOC_SESSION_DATA), Irp); }
/* Fill in data */ Toc = Irp->AssociatedIrp.SystemBuffer; Toc->Length[0] = 0; Toc->Length[1] = 8; Toc->FirstCompleteSession = 1; Toc->LastCompleteSession = 1; /* And return our track with 2sec gap (cf TOC function) */ ViFillInTrackData(Toc->TrackData, TOC_DATA_TRACK, ADR_NO_MODE_INFORMATION, 1, 150);
return ViSetIoStatus(STATUS_SUCCESS, sizeof(CDROM_TOC_SESSION_DATA), Irp);}
NTSTATUSViEnumerateDrives(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PDRIVES_LIST DrivesList; PIO_STACK_LOCATION Stack; PDEVICE_OBJECT CurrentDO; PDEVICE_EXTENSION DeviceExtension;
/* Check we have enough room for output */ Stack = IoGetCurrentIrpStackLocation(Irp); if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DRIVES_LIST)) { return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(DRIVES_LIST), Irp); }
/* Get the output buffer */ DrivesList = Irp->AssociatedIrp.SystemBuffer; DrivesList->Count = 0;
/* And now, starting from our main DO, start browsing all the DO we created */ for (CurrentDO = DeviceObject->DriverObject->DeviceObject; CurrentDO != NULL; CurrentDO = CurrentDO->NextDevice) { /* Check we won't output our main DO */ DeviceExtension = CurrentDO->DeviceExtension; if (DeviceExtension->GlobalName.Length != RtlCompareMemory(DeviceExtension->GlobalName.Buffer, L"\\??\\VirtualCdRom", DeviceExtension->GlobalName.Length)) { /* When we return, we extract the drive letter * See ViCreateDriveLetter(), it's \??\Z: */ DrivesList->Drives[DrivesList->Count++] = DeviceExtension->GlobalName.Buffer[4]; } }
return ViSetIoStatus(STATUS_SUCCESS, sizeof(DRIVES_LIST), Irp);}
NTSTATUSViGetImagePath(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PIMAGE_PATH ImagePath; PIO_STACK_LOCATION Stack; PDEVICE_EXTENSION DeviceExtension;
/* Check we have enough room for output */ Stack = IoGetCurrentIrpStackLocation(Irp); if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(IMAGE_PATH)) { return ViSetIoStatus(STATUS_BUFFER_TOO_SMALL, sizeof(IMAGE_PATH), Irp); }
/* Get our image path from DO */ DeviceExtension = DeviceObject->DeviceExtension; ImagePath = Irp->AssociatedIrp.SystemBuffer; ImagePath->Mounted = (DeviceExtension->VolumeObject != NULL); ImagePath->Length = DeviceExtension->ImageName.Length; /* And if it's set, copy it back to the caller */ if (DeviceExtension->ImageName.Length != 0) { RtlCopyMemory(ImagePath->Path, DeviceExtension->ImageName.Buffer, DeviceExtension->ImageName.Length); }
return ViSetIoStatus(STATUS_SUCCESS, sizeof(IMAGE_PATH), Irp);}
NTSTATUSViEjectMedia(PDEVICE_OBJECT DeviceObject, PIRP Irp){ PDEVICE_EXTENSION DeviceExtension;
/* Eject will force a verify */ SetFlag(DeviceObject->Flags, DO_VERIFY_VOLUME);
/* If we have an image mounted, unmount it * But don't free anything related, so that * we can perform quick remount. * See: IOCTL_STORAGE_LOAD_MEDIA */ DeviceExtension = DeviceObject->DeviceExtension; if (DeviceExtension->VolumeObject != NULL) { ObDereferenceObject(DeviceExtension->VolumeObject); /* Device changed, so mandatory increment */ ++DeviceExtension->ChangeCount; DeviceExtension->VolumeObject = NULL; }
return ViSetIoStatus(STATUS_SUCCESS, 0, Irp);}
NTSTATUSViRemountMedia(PDEVICE_OBJECT DeviceObject, PIRP Irp){ NTSTATUS Status; UNICODE_STRING Image; PDEVICE_EXTENSION DeviceExtension;
/* Get the device extension */ DeviceExtension = DeviceObject->DeviceExtension; /* Allocate a new string as mount parameter */ Status = ViAllocateUnicodeString(DeviceExtension->ImageName.MaximumLength, &Image); if (!NT_SUCCESS(Status)) { return ViSetIoStatus(Status, 0, Irp); }
/* To allow cleanup in case of troubles */ _SEH2_TRY { /* Copy our current image name and mount */ RtlCopyUnicodeString(&Image, &DeviceExtension->ImageName); Status = ViIssueMountImage(DeviceObject, &Image, Irp); } _SEH2_FINALLY { ViFreeUnicodeString(&Image); } _SEH2_END;
return ViSetIoStatus(Status, 0, Irp);}
NTSTATUSNTAPIVcdDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp){ NTSTATUS Status; UNICODE_STRING Image; PIO_STACK_LOCATION Stack; PMOUNT_PARAMETERS MountParameters; PDEVICE_EXTENSION DeviceExtension;
DeviceExtension = DeviceObject->DeviceExtension; Stack = IoGetCurrentIrpStackLocation(Irp);
_SEH2_TRY { switch (Stack->Parameters.DeviceIoControl.IoControlCode) { /* First of all, our private IOCTLs */ case IOCTL_VCDROM_CREATE_DRIVE: Status = ViCreateDevice(DeviceObject->DriverObject, Irp); break;
case IOCTL_VCDROM_DELETE_DRIVE: Status = ViDeleteDevice(DeviceObject, Irp); break;
case IOCTL_VCDROM_MOUNT_IMAGE: MountParameters = Irp->AssociatedIrp.SystemBuffer; Image.MaximumLength = 255 * sizeof(WCHAR); Image.Length = MountParameters->Length; Image.Buffer = MountParameters->Path; DeviceExtension->Flags = MountParameters->Flags; Status = ViIssueMountImage(DeviceObject, &Image, Irp); break;
case IOCTL_VCDROM_ENUMERATE_DRIVES: Status = ViEnumerateDrives(DeviceObject, Irp); break;
case IOCTL_VCDROM_GET_IMAGE_PATH: Status = ViGetImagePath(DeviceObject, Irp); break;
/* Now, IOCTLs we have to handle as class driver */ case IOCTL_DISK_GET_DRIVE_GEOMETRY: case IOCTL_CDROM_GET_DRIVE_GEOMETRY: Status = ViGetDriveGeometry(DeviceObject, Irp); break;
case IOCTL_DISK_CHECK_VERIFY: case IOCTL_CDROM_CHECK_VERIFY: Status = ViCheckVerify(DeviceObject, Irp); break;
case IOCTL_CDROM_READ_TOC: Status = ViReadToc(DeviceObject, Irp); break;
case IOCTL_CDROM_READ_TOC_EX: Status = ViReadTocEx(DeviceObject, Irp); break;
case IOCTL_CDROM_GET_LAST_SESSION: Status = ViGetLastSession(DeviceObject, Irp); break;
case IOCTL_STORAGE_EJECT_MEDIA: case IOCTL_CDROM_EJECT_MEDIA: Status = ViEjectMedia(DeviceObject, Irp); break;
/* That one is a bit specific * It gets unmounted image mounted again */ case IOCTL_STORAGE_LOAD_MEDIA: /* That means it can only be performed if: * - We had an image previously * - It's no longer mounted * Otherwise, we just return success */ if (DeviceExtension->ImageName.Buffer == NULL || DeviceExtension->VolumeObject != NULL) { Status = ViSetIoStatus(STATUS_SUCCESS, 0, Irp); } else { Status = ViRemountMedia(DeviceObject, Irp); } break;
default: Status = STATUS_INVALID_DEVICE_REQUEST; DPRINT1("IOCTL: %x not supported\n", Stack->Parameters.DeviceIoControl.IoControlCode); break; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END;
/* Depending on the failure code, we may force a verify */ if (!NT_SUCCESS(Status)) { if (Status == STATUS_DEVICE_NOT_READY || Status == STATUS_IO_TIMEOUT || Status == STATUS_MEDIA_WRITE_PROTECTED || Status == STATUS_NO_MEDIA_IN_DEVICE || Status == STATUS_VERIFY_REQUIRED || Status == STATUS_UNRECOGNIZED_MEDIA || Status == STATUS_WRONG_VOLUME) { IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); } }
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
return Status;}
NTSTATUSNTAPIDriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){ NTSTATUS Status; UNICODE_STRING DeviceName; PDEVICE_OBJECT DeviceObject; PDEVICE_EXTENSION DeviceExtension;
/* Set our entry points (rather limited :-)) */ DriverObject->DriverUnload = VcdUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = VcdHandle; DriverObject->MajorFunction[IRP_MJ_CLOSE] = VcdHandle; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = VcdHandle; DriverObject->MajorFunction[IRP_MJ_READ] = VcdRead; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VcdDeviceControl;
/* Create our main device to receive private IOCTLs */ RtlInitUnicodeString(&DeviceName, L"\\Device\\VirtualCdRom"); Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_CD_ROM, FILE_READ_ONLY_DEVICE | FILE_FLOPPY_DISKETTE, FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) { return Status; }
/* Initialize our device extension */ ViInitializeDeviceExtension(DeviceObject); DeviceExtension = DeviceObject->DeviceExtension;
/* And create our accessible name from umode */ ViAllocateUnicodeString(DEFAULT_STRING_SIZE, &DeviceExtension->GlobalName); RtlAppendUnicodeToString(&DeviceExtension->GlobalName, L"\\??\\VirtualCdRom"); Status = IoCreateSymbolicLink(&DeviceExtension->GlobalName, &DeviceName); if (!NT_SUCCESS(Status)) { IoDeleteDevice(DeviceObject); return Status; }
/* Initialize our mutex for device count */ ExInitializeFastMutex(&ViMutex);
/* And try to load images that would have been stored in registry */ ViLoadImagesFromRegistry(DriverObject, RegistryPath->Buffer);
return STATUS_SUCCESS;}