reactos/modules/rosapps/drivers/vfd/vfdimg.c
Pierre Schweitzer 25c7e1a8d0
[VFD] Import the VFD project (Virtual Floppy Drive) which allows creating virtual
floppy drives in ReactOS and mount images on them.
Only the cmd got imported. The GUI interface may come later on.
Note that, as for vcdrom, the driver is left disabled and you need to explicitely
start it through vfd command line interface.

CORE-14090
2017-12-16 21:48:34 +01:00

528 lines
12 KiB
C

/*
vfdimg.c
Virtual Floppy Drive for Windows NT platform
Kernel mode driver: Image handling functions
Copyright (C) 2003-2005 Ken Kato
*/
#include "imports.h"
#include "vfddrv.h"
#include "vfddbg.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, VfdOpenCheck)
#pragma alloc_text(PAGE, VfdOpenImage)
#pragma alloc_text(PAGE, VfdCloseImage)
#pragma alloc_text(PAGE, VfdQueryImage)
#endif // ALLOC_PRAGMA
//
// Check IOCTL_VFD_OPEN_IMAGE input parameters
//
NTSTATUS
VfdOpenCheck(
PDEVICE_EXTENSION DeviceExtension,
PVFD_IMAGE_INFO ImageInfo,
ULONG InputLength)
{
// Check media status
if (DeviceExtension->FileHandle ||
DeviceExtension->FileBuffer) {
VFDTRACE(VFDWARN, ("[VFD] image already opened.\n"));
return STATUS_DEVICE_BUSY;
}
// Check input parameter length
if (InputLength < sizeof(VFD_IMAGE_INFO) ||
InputLength < sizeof(VFD_IMAGE_INFO) + ImageInfo->NameLength)
{
return STATUS_INVALID_PARAMETER;
}
// Check input parameters
if (ImageInfo->MediaType == VFD_MEDIA_NONE ||
ImageInfo->MediaType >= VFD_MEDIA_MAX) {
VFDTRACE(VFDWARN, ("[VFD] invalid MediaType - %u.\n",
ImageInfo->MediaType));
return STATUS_INVALID_PARAMETER;
}
if (ImageInfo->DiskType == VFD_DISKTYPE_FILE &&
ImageInfo->NameLength == 0) {
VFDTRACE(VFDWARN,
("[VFD] File name required for VFD_DISKTYPE_FILE.\n"));
return STATUS_INVALID_PARAMETER;
}
// create a security context to match the calling process' context
// the driver thread uses this context to impersonate the client
// to open the specified image file
// if (ImageInfo->DiskType == VFD_DISKTYPE_FILE)
{
SECURITY_QUALITY_OF_SERVICE sqos;
if (DeviceExtension->SecurityContext != NULL) {
SeDeleteClientSecurity(DeviceExtension->SecurityContext);
}
else {
DeviceExtension->SecurityContext =
(PSECURITY_CLIENT_CONTEXT)ExAllocatePoolWithTag(
NonPagedPool, sizeof(SECURITY_CLIENT_CONTEXT), VFD_POOL_TAG);
}
RtlZeroMemory(&sqos, sizeof(SECURITY_QUALITY_OF_SERVICE));
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sqos.ImpersonationLevel = SecurityImpersonation;
sqos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
sqos.EffectiveOnly = FALSE;
SeCreateClientSecurity(
PsGetCurrentThread(), &sqos, FALSE,
DeviceExtension->SecurityContext);
}
return STATUS_SUCCESS;
}
//
// Open a virtual floppy image file or create an empty ram disk
//
NTSTATUS
VfdOpenImage (
IN PDEVICE_EXTENSION DeviceExtension,
IN PVFD_IMAGE_INFO ImageInfo)
{
IO_STATUS_BLOCK io_status;
NTSTATUS status = STATUS_SUCCESS;
const DISK_GEOMETRY *geometry;
ULONG sectors;
ULONG alignment;
VFDTRACE(0, ("[VFD] VfdOpenImage - IN\n"));
//
// Store file name in the device extension
//
if (ImageInfo->NameLength) {
if (ImageInfo->NameLength + 1 >
DeviceExtension->FileName.MaximumLength) {
// expand the filename buffer
if (DeviceExtension->FileName.Buffer) {
ExFreePool(DeviceExtension->FileName.Buffer);
RtlZeroMemory(
&DeviceExtension->FileName,
sizeof(ANSI_STRING));
}
DeviceExtension->FileName.Buffer = (PCHAR)ExAllocatePoolWithTag(
NonPagedPool, ImageInfo->NameLength + 1, VFD_POOL_TAG);
if (!DeviceExtension->FileName.Buffer) {
VFDTRACE(0, ("[VFD] Can't allocate memory for image path\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
DeviceExtension->FileName.MaximumLength
= (USHORT)(ImageInfo->NameLength + 1);
RtlZeroMemory(
DeviceExtension->FileName.Buffer,
DeviceExtension->FileName.MaximumLength);
}
if (DeviceExtension->FileName.Buffer) {
RtlCopyMemory(
DeviceExtension->FileName.Buffer,
ImageInfo->FileName,
ImageInfo->NameLength);
DeviceExtension->FileName.Buffer[ImageInfo->NameLength] = '\0';
}
}
DeviceExtension->FileName.Length = ImageInfo->NameLength;
//
// Get DISK_GEOMETRY and calculate the media capacity
// -- validity of the ImageInfo->MediaType value is assured in
// the VfdOpenCheck function
//
geometry = &geom_tbl[ImageInfo->MediaType];
sectors =
geometry->Cylinders.LowPart *
geometry->TracksPerCylinder *
geometry->SectorsPerTrack;
if (ImageInfo->ImageSize != 0 &&
ImageInfo->ImageSize < VFD_SECTOR_TO_BYTE(sectors)) {
VFDTRACE(0, ("[VFD] Image is smaller than the media\n"));
return STATUS_INVALID_PARAMETER;
}
//
// Prepare a virtual media according to the ImageInfo
//
if (ImageInfo->DiskType == VFD_DISKTYPE_FILE) {
//
// open an existing image file
//
HANDLE file_handle;
OBJECT_ATTRIBUTES attributes;
UNICODE_STRING unicode_name;
FILE_STANDARD_INFORMATION file_standard;
FILE_BASIC_INFORMATION file_basic;
FILE_ALIGNMENT_INFORMATION file_alignment;
PFILE_OBJECT file_object;
BOOLEAN network_drive;
// convert the filename into a unicode string
status = RtlAnsiStringToUnicodeString(
&unicode_name, &DeviceExtension->FileName, TRUE);
if (!NT_SUCCESS(status)) {
VFDTRACE(0, ("[VFD] Failed to convert filename to UNICODE\n"));
return status;
}
VFDTRACE(VFDINFO,
("[VFD] Opening %s\n", DeviceExtension->FileName.Buffer));
// prepare an object attribute to open
InitializeObjectAttributes(
&attributes,
&unicode_name,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// open the target file
status = ZwCreateFile(
&file_handle,
GENERIC_READ | GENERIC_WRITE,
&attributes,
&io_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE |
FILE_RANDOM_ACCESS |
FILE_NO_INTERMEDIATE_BUFFERING |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
RtlFreeUnicodeString(&unicode_name);
if (!NT_SUCCESS(status)) {
VFDTRACE(0, ("[VFD] ZwCreateFile - %s\n",
GetStatusName(status)));
return status;
}
// Check the file size
status = ZwQueryInformationFile(
file_handle,
&io_status,
&file_standard,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
if (!NT_SUCCESS(status)) {
VFDTRACE(0,
("[VFD] ZwQueryInformationFile - FILE_STANDARD_INFORMATION\n"));
ZwClose(file_handle);
goto exit_func;
}
// Actual file size can be larger than the media capacity
if (file_standard.EndOfFile.QuadPart < VFD_SECTOR_TO_BYTE(sectors)) {
VFDTRACE(0, ("[VFD] file is smaller than the media.\n"));
status = STATUS_INVALID_PARAMETER;
ZwClose(file_handle);
goto exit_func;
}
DeviceExtension->ImageSize = file_standard.EndOfFile.LowPart;
// Find out whether the file is on a local disk or a network drive
network_drive = FALSE;
status = ObReferenceObjectByHandle(
file_handle,
GENERIC_READ,
NULL,
KernelMode,
#ifndef __REACTOS__
&file_object,
#else
(PVOID *)&file_object,
#endif
NULL);
if (NT_SUCCESS(status)) {
if (file_object && file_object->DeviceObject) {
VFDTRACE(VFDINFO, ("[VFD] Device type is 0x%08x\n",
file_object->DeviceObject->DeviceType));
if (file_object->DeviceObject->DeviceType
== FILE_DEVICE_NETWORK_FILE_SYSTEM) {
network_drive = TRUE;
}
// how about these types ?
// FILE_DEVICE_NETWORK
// FILE_DEVICE_NETWORK_BROWSER
// FILE_DEVICE_NETWORK_REDIRECTOR
}
else {
VFDTRACE(VFDWARN, ("[VFD Cannot decide the device type\n"));
}
ObDereferenceObject(file_object);
}
else {
VFDTRACE(0, ("[VFD] ObReferenceObjectByHandle - %s\n",
GetStatusName(status)));
}
if (!network_drive) {
// The NT cache manager can deadlock if a filesystem that is using
// the cache manager is used in a virtual disk that stores its file
// on a file systemthat is also using the cache manager, this is
// why we open the file with FILE_NO_INTERMEDIATE_BUFFERING above,
// however if the file is compressed or encrypted NT will not honor
// this request and cache it anyway since it need to store the
// decompressed/unencrypted data somewhere, therefor we put an
// extra check here and don't alow disk images to be compressed/
// encrypted.
status = ZwQueryInformationFile(
file_handle,
&io_status,
&file_basic,
sizeof(FILE_BASIC_INFORMATION),
FileBasicInformation);
if (!NT_SUCCESS(status)) {
VFDTRACE(0,
("[VFD] ZwQueryInformationFile - FILE_BASIC_INFORMATION\n"));
ZwClose(file_handle);
goto exit_func;
}
if (file_basic.FileAttributes
& (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED))
{
VFDTRACE(0,
("[VFD] Image file is compressed and/or encrypted\n"));
status = STATUS_ACCESS_DENIED;
ZwClose(file_handle);
goto exit_func;
}
}
// Retrieve the file alignment requirement
status = ZwQueryInformationFile(
file_handle,
&io_status,
&file_alignment,
sizeof(FILE_ALIGNMENT_INFORMATION),
FileAlignmentInformation);
if (!NT_SUCCESS(status)) {
VFDTRACE(0,
("[VFD] ZwQueryInformationFile - FILE_ALIGNMENT_INFORMATION\n"));
ZwClose(file_handle);
goto exit_func;
}
DeviceExtension->FileHandle = file_handle;
alignment = file_alignment.AlignmentRequirement;
VFDTRACE(0, ("[VFD] Opened an image file\n"));
}
else {
//
// Create an empty RAM disk
//
DeviceExtension->FileBuffer = (PUCHAR)ExAllocatePoolWithTag(
NonPagedPool,
VFD_SECTOR_TO_BYTE(sectors),
VFD_POOL_TAG);
if (!DeviceExtension->FileBuffer) {
VFDTRACE(0, ("[VFD] Can't allocate memory for RAM disk\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(
DeviceExtension->FileBuffer,
VFD_SECTOR_TO_BYTE(sectors));
if (ImageInfo->ImageSize) {
DeviceExtension->ImageSize = ImageInfo->ImageSize;
}
else {
DeviceExtension->ImageSize = VFD_SECTOR_TO_BYTE(sectors);
}
alignment = FILE_WORD_ALIGNMENT;
VFDTRACE(0, ("[VFD] Created an empty RAM disk\n"));
}
DeviceExtension->MediaChangeCount++;
DeviceExtension->MediaType = ImageInfo->MediaType;
DeviceExtension->MediaFlags = ImageInfo->MediaFlags;
DeviceExtension->FileType = ImageInfo->FileType;
DeviceExtension->Geometry = geometry;
DeviceExtension->Sectors = sectors;
VFDTRACE(0, ("[VFD] Media:%d Flag:0x%02x Size:%lu Capacity:%lu\n",
DeviceExtension->MediaType,
DeviceExtension->MediaFlags,
DeviceExtension->ImageSize,
DeviceExtension->Sectors));
DeviceExtension->DeviceObject->AlignmentRequirement
= alignment;
exit_func:
VFDTRACE(0, ("[VFD] VfdOpenImage - %s\n", GetStatusName(status)));
return status;
}
//
// Close the current image
//
VOID
VfdCloseImage (
IN PDEVICE_EXTENSION DeviceExtension)
{
VFDTRACE(0, ("[VFD] VfdCloseImage - IN\n"));
ASSERT(DeviceExtension);
DeviceExtension->MediaType = VFD_MEDIA_NONE;
DeviceExtension->MediaFlags = 0;
DeviceExtension->FileType = 0;
DeviceExtension->ImageSize = 0;
DeviceExtension->FileName.Length = 0;
DeviceExtension->Sectors = 0;
if (DeviceExtension->FileHandle) {
ZwClose(DeviceExtension->FileHandle);
DeviceExtension->FileHandle = NULL;
}
if (DeviceExtension->FileBuffer) {
ExFreePool(DeviceExtension->FileBuffer);
DeviceExtension->FileBuffer = NULL;
}
VFDTRACE(0, ("[VFD] VfdCloseImage - OUT\n"));
}
//
// Return information about the current image
//
NTSTATUS
VfdQueryImage(
IN PDEVICE_EXTENSION DeviceExtension,
OUT PVFD_IMAGE_INFO ImageInfo,
IN ULONG BufferLength,
OUT PULONG ReturnLength)
{
// Check output buffer length
if (BufferLength < sizeof(VFD_IMAGE_INFO)) {
return STATUS_BUFFER_TOO_SMALL;
}
RtlZeroMemory(ImageInfo, BufferLength);
// Store fixed length image information
ImageInfo->MediaType = DeviceExtension->MediaType;
if (DeviceExtension->MediaType == VFD_MEDIA_NONE) {
*ReturnLength = sizeof(VFD_IMAGE_INFO);
return STATUS_SUCCESS;
}
if (DeviceExtension->FileBuffer) {
ImageInfo->DiskType = VFD_DISKTYPE_RAM;
}
else {
ImageInfo->DiskType = VFD_DISKTYPE_FILE;
}
ImageInfo->MediaFlags = DeviceExtension->MediaFlags;
ImageInfo->FileType = DeviceExtension->FileType;
ImageInfo->ImageSize = DeviceExtension->ImageSize;
ImageInfo->NameLength = DeviceExtension->FileName.Length;
// output buffer is large enough to hold the file name?
if (BufferLength < sizeof(VFD_IMAGE_INFO) +
DeviceExtension->FileName.Length)
{
*ReturnLength = sizeof(VFD_IMAGE_INFO);
return STATUS_BUFFER_OVERFLOW;
}
// copy file name
if (DeviceExtension->FileName.Length &&
DeviceExtension->FileName.Buffer) {
RtlCopyMemory(ImageInfo->FileName,
DeviceExtension->FileName.Buffer,
DeviceExtension->FileName.Length);
}
// store the actually returned data length
*ReturnLength = sizeof(VFD_IMAGE_INFO) +
DeviceExtension->FileName.Length;
return STATUS_SUCCESS;
}