mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
fc921423f3
Patch by Doug Lyons. CORE-14090
3284 lines
59 KiB
C
3284 lines
59 KiB
C
/*
|
|
vfdctl.c
|
|
|
|
Virtual Floppy Drive for Windows
|
|
Driver control library
|
|
Driver and image control functions
|
|
|
|
Copyright (C) 2003-2005 Ken Kato
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
#pragma message(__FILE__": Compiled as C++ for testing purpose.")
|
|
#endif // __cplusplus
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <dbt.h>
|
|
#ifdef _MSC_VER
|
|
#pragma warning (push, 3)
|
|
#endif
|
|
#include <shlobj.h>
|
|
#include <winioctl.h>
|
|
#ifdef _MSC_VER
|
|
#pragma warning (pop)
|
|
#endif
|
|
#include <stdio.h>
|
|
|
|
#include "vfdtypes.h"
|
|
#include "vfdio.h"
|
|
#include "vfdapi.h"
|
|
#include "vfdlib.h"
|
|
#include "vfdver.h"
|
|
|
|
#ifndef IOCTL_DISK_GET_LENGTH_INFO
|
|
// Old winioctl.h header doesn't define the following
|
|
|
|
#define IOCTL_DISK_GET_LENGTH_INFO CTL_CODE(\
|
|
IOCTL_DISK_BASE, 0x0017, METHOD_BUFFERED, FILE_READ_ACCESS)
|
|
|
|
typedef struct _GET_LENGTH_INFORMATION {
|
|
LARGE_INTEGER Length;
|
|
} GET_LENGTH_INFORMATION, *PGET_LENGTH_INFORMATION;
|
|
|
|
#endif // IOCTL_DISK_GET_LENGTH_INFO
|
|
|
|
//
|
|
// DOS device name (\\.\VirtualFD)
|
|
//
|
|
#ifndef __REACTOS__
|
|
#define VFD_DEVICE_TEMPLATE "\\\\.\\" VFD_DEVICE_BASENAME "%u"
|
|
#else
|
|
#define VFD_DEVICE_TEMPLATE "\\\\.\\" VFD_DEVICE_BASENAME "%lu"
|
|
#endif
|
|
#define VFD_VOLUME_TEMPLATE "\\\\.\\%c:"
|
|
|
|
#define VFD_INSTALL_DIRECTORY "\\system32\\drivers\\"
|
|
|
|
#ifdef _DEBUG
|
|
#ifndef __REACTOS__
|
|
extern ULONG TraceFlags = (ULONG)-1;//0;
|
|
extern CHAR *TraceFile = NULL;
|
|
extern ULONG TraceLine = 0;
|
|
#else
|
|
ULONG TraceFlags = (ULONG)-1;//0;
|
|
CHAR const * TraceFile = NULL;
|
|
ULONG TraceLine = 0;
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// broadcast a WM_DEVICECHANGE system message to inform
|
|
// a drive letter creation / removal
|
|
//
|
|
#define VFD_LINK_CREATED 0
|
|
#define VFD_LINK_REMOVED 1
|
|
|
|
static void VfdBroadcastLink(
|
|
CHAR cLetter,
|
|
BOOL bRemoved)
|
|
{
|
|
DWORD receipients;
|
|
DWORD device_event;
|
|
DEV_BROADCAST_VOLUME params;
|
|
|
|
if (!isalpha(cLetter)) {
|
|
VFDTRACE(0,
|
|
("VfdBroadcastLink: invalid parameter"))
|
|
return;
|
|
}
|
|
|
|
receipients = BSM_APPLICATIONS;
|
|
|
|
device_event = bRemoved ?
|
|
DBT_DEVICEREMOVECOMPLETE : DBT_DEVICEARRIVAL;
|
|
|
|
ZeroMemory(¶ms, sizeof(params));
|
|
|
|
params.dbcv_size = sizeof(params);
|
|
params.dbcv_devicetype = DBT_DEVTYP_VOLUME;
|
|
params.dbcv_reserved = 0;
|
|
params.dbcv_unitmask = (1 << (toupper(cLetter) - 'A'));
|
|
params.dbcv_flags = 0;
|
|
|
|
if (BroadcastSystemMessage(
|
|
BSF_NOHANG | BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG,
|
|
&receipients,
|
|
WM_DEVICECHANGE,
|
|
device_event,
|
|
(LPARAM)¶ms) <= 0) {
|
|
|
|
VFDTRACE(0,
|
|
("VfdBroadcastLink: BroadcastSystemMessage - %s",
|
|
SystemMessage(GetLastError())));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Broadcast a VFD notify message
|
|
//
|
|
static __inline void VfdNotify(
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
// SendNotifyMessage causes volume locking conflict (I think)
|
|
// on Windows XP while closing an image with VfdWin
|
|
// SendNotifyMessage(HWND_BROADCAST, uVfdMsg, wParam, lParam);
|
|
PostMessage(HWND_BROADCAST, g_nNotifyMsg, wParam, lParam);
|
|
}
|
|
|
|
#ifdef VFD_EMBED_DRIVER
|
|
//
|
|
// Restore the VFD driver file in the system directory
|
|
//
|
|
|
|
static DWORD VfdRestoreDriver(
|
|
PCSTR sPath)
|
|
{
|
|
#define FUNC "VfdRestoreDriver"
|
|
HRSRC hRes;
|
|
DWORD size;
|
|
HGLOBAL hDrv;
|
|
PVOID pData;
|
|
DWORD result;
|
|
HANDLE hFile;
|
|
DWORD ret;
|
|
|
|
//
|
|
// Prepare driver binary
|
|
//
|
|
|
|
// use embedded driver binary
|
|
|
|
#define S(s) #s
|
|
hRes = FindResource(g_hDllModule,
|
|
S(VFD_DRIVER_NAME_ID), S(VFD_DRIVER_TYPE_ID));
|
|
#undef S
|
|
|
|
if (hRes == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": FindResource - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
size = SizeofResource(g_hDllModule, hRes);
|
|
|
|
if (size == 0) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": SizeofResource - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
hDrv = LoadResource(g_hDllModule, hRes);
|
|
|
|
if (hDrv == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LoadResource - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
pData = LockResource(hDrv);
|
|
|
|
if (pData == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LockResource - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// create the driver file
|
|
|
|
hFile = CreateFile(sPath, GENERIC_WRITE,
|
|
0, NULL, OPEN_ALWAYS, 0, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": CreateFile(%s) - %s",
|
|
sPath, SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (!WriteFile(hFile, pData, size, &result, NULL) ||
|
|
size != result) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": CreateFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
CloseHandle(hFile);
|
|
return ret;
|
|
}
|
|
|
|
SetEndOfFile(hFile);
|
|
CloseHandle(hFile);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
#endif // VFD_EMBED_DRIVER
|
|
|
|
//
|
|
// Install the Virtual Floppy Driver
|
|
//
|
|
DWORD WINAPI VfdInstallDriver(
|
|
PCSTR sFileName,
|
|
DWORD nStart)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdInstallDriver"
|
|
SC_HANDLE hScManager; // Service Control Manager
|
|
SC_HANDLE hService = NULL; // Service (= Driver)
|
|
#ifndef VFD_EMBED_DRIVER
|
|
CHAR file_path[MAX_PATH];
|
|
PSTR file_name;
|
|
#endif // VFD_EMBED_DRIVER
|
|
CHAR system_dir[MAX_PATH];
|
|
PSTR inst_path;
|
|
DWORD len;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
#ifdef __REACTOS__
|
|
CHAR full_file_path[MAX_PATH];
|
|
#endif
|
|
|
|
// get SystemRoot directory path
|
|
|
|
// len = GetEnvironmentVariable(
|
|
// "SystemRoot", system_dir, sizeof(system_dir));
|
|
len = GetWindowsDirectory(system_dir, sizeof(system_dir));
|
|
|
|
if (len == 0 || len > sizeof(system_dir)) {
|
|
VFDTRACE(0,
|
|
(FUNC ": %%SystemRoot%% is empty or too long.\n"));
|
|
|
|
return ERROR_BAD_ENVIRONMENT;
|
|
}
|
|
|
|
inst_path = &system_dir[len];
|
|
|
|
#ifdef __REACTOS__
|
|
strcpy(full_file_path, system_dir);
|
|
strcat(full_file_path, VFD_INSTALL_DIRECTORY);
|
|
strcat(full_file_path, VFD_DRIVER_FILENAME);
|
|
#endif
|
|
|
|
#ifdef VFD_EMBED_DRIVER
|
|
//
|
|
// use embedded driver file
|
|
//
|
|
strcpy(inst_path++, VFD_INSTALL_DIRECTORY VFD_DRIVER_FILENAME);
|
|
|
|
ret = VfdRestoreDriver(system_dir);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
#else // VFD_EMBED_DRIVER
|
|
// Prepare driver binary's full path
|
|
|
|
if (sFileName == NULL || *sFileName == '\0') {
|
|
|
|
// default driver file is vfd.sys in the same directory as executable
|
|
|
|
len = GetModuleFileName(
|
|
NULL, file_path, sizeof(file_path));
|
|
|
|
if (len == 0) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": GetModuleFileName - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// search the last '\' character
|
|
|
|
while (len > 0 && file_path[len - 1] != '\\') {
|
|
len --;
|
|
}
|
|
|
|
// supply the file name (vfd.sys)
|
|
|
|
file_name = &file_path[len];
|
|
strcpy(file_name, VFD_DRIVER_FILENAME);
|
|
}
|
|
else {
|
|
|
|
// ensure that tha path is an absolute full path
|
|
|
|
len = GetFullPathName(
|
|
sFileName,
|
|
sizeof(file_path),
|
|
file_path,
|
|
&file_name);
|
|
|
|
if (len == 0) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": GetFullPathName(%s) - %s\n",
|
|
sFileName, SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (GetFileAttributes(file_path) & FILE_ATTRIBUTE_DIRECTORY) {
|
|
// if the specified path is a directory,
|
|
// supply the file name (vfd.sys)
|
|
|
|
file_name = &file_path[len];
|
|
strcpy(file_name++, "\\" VFD_DRIVER_FILENAME);
|
|
}
|
|
}
|
|
|
|
#ifdef __REACTOS__
|
|
// Check install directory & file exist or use full_file_path
|
|
|
|
if (GetFileAttributesA(file_path) == INVALID_FILE_ATTRIBUTES) {
|
|
strcpy(file_path, full_file_path);
|
|
}
|
|
#endif
|
|
|
|
// Check if the file is a valid Virtual Floppy driver
|
|
|
|
ret = VfdCheckDriverFile(file_path, NULL);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
VFDTRACE(0,
|
|
(FUNC ": VfdCheckDriverFile(%s)\n", file_path));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// if the path is under the system directory, make it relative
|
|
// to the system directory
|
|
|
|
len = strlen(system_dir);
|
|
|
|
if (!_strnicmp(file_path, system_dir, len)) {
|
|
inst_path = &file_path[len];
|
|
|
|
while (*inst_path == '\\') {
|
|
inst_path++;
|
|
}
|
|
}
|
|
else {
|
|
inst_path = &file_path[0];
|
|
}
|
|
#endif // VFD_EMBED_DRIVER
|
|
|
|
// Connect to the Service Control Manager
|
|
|
|
hScManager = OpenSCManager(
|
|
NULL, // local machine
|
|
NULL, // local database
|
|
SC_MANAGER_CREATE_SERVICE); // access required
|
|
|
|
if (hScManager == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenSCManager() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Create a new service object
|
|
|
|
hService = CreateService(
|
|
hScManager, // service control manager
|
|
VFD_DEVICE_BASENAME, // internal service name
|
|
VFD_DEVICE_BASENAME, // display name
|
|
SERVICE_ALL_ACCESS, // access mode
|
|
SERVICE_KERNEL_DRIVER, // service type
|
|
nStart, // service start type
|
|
SERVICE_ERROR_NORMAL, // start error sevirity
|
|
inst_path, // service image file path
|
|
NULL, // service group
|
|
NULL, // service tag
|
|
NULL, // service dependency
|
|
NULL, // use LocalSystem account
|
|
NULL // password for the account
|
|
);
|
|
|
|
if (!hService) {
|
|
// Failed to create a service object
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": CreateService() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
// Close the service object handle
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
// Close handle to the service control manager.
|
|
|
|
if (hScManager) {
|
|
CloseServiceHandle(hScManager);
|
|
}
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
// Broadcast the successful operation
|
|
VfdNotify(VFD_OPERATION_INSTALL, 0);
|
|
}
|
|
#ifdef VFD_EMBED_DRIVER
|
|
else {
|
|
// Delete the restored driver file
|
|
DeleteFile(system_dir);
|
|
}
|
|
#endif // VFD_EMBED_DRIVER
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Configure the Virtual Floppy Driver (change the start method)
|
|
//
|
|
|
|
DWORD WINAPI VfdConfigDriver(
|
|
DWORD nStart)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdConfigDriver"
|
|
SC_HANDLE hScManager; // Service Control Manager
|
|
SC_HANDLE hService; // Service (= Driver)
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
// Connect to the Service Control Manager
|
|
|
|
hScManager = OpenSCManager(NULL, NULL, 0);
|
|
|
|
if (hScManager == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenSCManager() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Open the VFD driver entry in the service database
|
|
|
|
hService = OpenService(
|
|
hScManager, // Service control manager
|
|
VFD_DEVICE_BASENAME, // service name
|
|
SERVICE_CHANGE_CONFIG); // service access mode
|
|
|
|
if (hService == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenService(SERVICE_CHANGE_CONFIG) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Change the start method of the VFD driver
|
|
|
|
if (!ChangeServiceConfig(
|
|
hService,
|
|
SERVICE_NO_CHANGE,
|
|
nStart,
|
|
SERVICE_NO_CHANGE,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL)) {
|
|
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": ChangeServiceConfig() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
// Close the service object handle
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
// Close handle to the service control manager.
|
|
|
|
if (hScManager) {
|
|
CloseServiceHandle(hScManager);
|
|
}
|
|
|
|
// Broadcast the successful operation
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_CONFIG, 0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Remove the Virtual Floppy Driver entry from the service database
|
|
//
|
|
DWORD WINAPI VfdRemoveDriver()
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdRemoveDriver"
|
|
SC_HANDLE hScManager; // Service Control Manager
|
|
SC_HANDLE hService; // Service (= Driver)
|
|
CHAR file_path[MAX_PATH];
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
// Get the current driver path
|
|
|
|
ret = VfdGetDriverConfig(file_path, NULL);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
// Connect to the Service Control Manager
|
|
|
|
hScManager = OpenSCManager(NULL, NULL, 0);
|
|
|
|
if (hScManager == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenSCManager() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Open the VFD driver entry in the service database
|
|
|
|
hService = OpenService(
|
|
hScManager, // Service control manager
|
|
VFD_DEVICE_BASENAME, // service name
|
|
DELETE); // service access mode
|
|
|
|
if (hService == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenService(DELETE) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Remove driver entry from registry
|
|
|
|
if (!DeleteService(hService)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeleteService() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
// Close the service object handle
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
// Close handle to the service control manager.
|
|
|
|
if (hScManager) {
|
|
CloseServiceHandle(hScManager);
|
|
}
|
|
|
|
// Broadcast the successful operation
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_REMOVE, 0);
|
|
|
|
#ifdef VFD_EMBED_DRIVER
|
|
// Remove the driver file
|
|
DeleteFile(file_path);
|
|
#endif // VFD_EMBED_DRIVER
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Start the Virtual Floppy Driver
|
|
//
|
|
DWORD WINAPI VfdStartDriver(
|
|
PDWORD pState)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdStartDriver"
|
|
SC_HANDLE hScManager; // Service Control Manager
|
|
SC_HANDLE hService; // Service (= Driver)
|
|
SERVICE_STATUS stat;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
HCURSOR original;
|
|
int i;
|
|
|
|
if (pState) {
|
|
*pState = 0;
|
|
}
|
|
|
|
// Connect to the Service Control Manager
|
|
|
|
hScManager = OpenSCManager(NULL, NULL, 0);
|
|
|
|
if (hScManager == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenSCManager() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// show an hourglass cursor
|
|
|
|
original = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
// Open the VFD driver entry in the service database
|
|
|
|
hService = OpenService(
|
|
hScManager, // Service control manager
|
|
VFD_DEVICE_BASENAME, // service name
|
|
SERVICE_START
|
|
| SERVICE_QUERY_STATUS); // service access mode
|
|
|
|
if (hService == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenService(SERVICE_START) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Start the driver
|
|
|
|
if (!StartService(hService, 0, NULL)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": StartService() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Wait until the driver is properly running
|
|
|
|
i = 0;
|
|
|
|
for (;;) {
|
|
if (!QueryServiceStatus(hService, &stat)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": QueryServiceStatus() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
break;
|
|
}
|
|
|
|
if (stat.dwCurrentState == SERVICE_RUNNING || ++i == 5) {
|
|
break;
|
|
}
|
|
|
|
Sleep(1000);
|
|
}
|
|
|
|
if (stat.dwCurrentState == SERVICE_RUNNING) {
|
|
|
|
// Broadcast the successful operation
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_START, 0);
|
|
}
|
|
|
|
// broadcast the arrival of VFD drives
|
|
// otherwise WinXP explorer doesn't recognize the VFD drives
|
|
|
|
for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
|
|
HANDLE hDevice;
|
|
CHAR letter = 0;
|
|
|
|
hDevice = VfdOpenDevice(i);
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE) {
|
|
|
|
VfdGetGlobalLink(hDevice, &letter);
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
if (isalpha(letter)) {
|
|
VfdBroadcastLink(letter, VFD_LINK_CREATED);
|
|
VfdNotify(VFD_OPERATION_SETLINK, i);
|
|
}
|
|
}
|
|
else {
|
|
VFDTRACE(0,
|
|
(FUNC ": VfdOpenDevice(%d) - %s",
|
|
i, SystemMessage(GetLastError())));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// somehow failed to start the driver
|
|
|
|
ret = ERROR_SERVICE_NOT_ACTIVE;
|
|
}
|
|
|
|
if (pState) {
|
|
*pState = stat.dwCurrentState;
|
|
}
|
|
|
|
cleanup:
|
|
// Close the service object handle
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
// Close handle to the service control manager.
|
|
|
|
if (hScManager) {
|
|
CloseServiceHandle(hScManager);
|
|
}
|
|
|
|
// revert to the original cursor
|
|
|
|
SetCursor(original);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Stop the Virtual Floppy Driver
|
|
//
|
|
DWORD WINAPI VfdStopDriver(
|
|
PDWORD pState)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdStopDriver"
|
|
SC_HANDLE hScManager; // Service Control Manager
|
|
SC_HANDLE hService; // Service (= Driver)
|
|
SERVICE_STATUS stat;
|
|
CHAR drive_letters[VFD_MAXIMUM_DEVICES];
|
|
DWORD ret = ERROR_SUCCESS;
|
|
int i;
|
|
HCURSOR original;
|
|
|
|
if (pState) {
|
|
*pState = 0;
|
|
}
|
|
|
|
// Connect to the Service Control Manager
|
|
|
|
hScManager = OpenSCManager(NULL, NULL, 0);
|
|
|
|
if (hScManager == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenSCManager() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Show the hourglass cursor
|
|
|
|
original = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
// Open the VFD driver entry in the service database
|
|
|
|
hService = OpenService(
|
|
hScManager, // Service control manager
|
|
VFD_DEVICE_BASENAME, // service name
|
|
SERVICE_STOP
|
|
| SERVICE_QUERY_STATUS); // service access mode
|
|
|
|
if (hService == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenService(SERVICE_STOP) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Get assigned drive letters
|
|
|
|
for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
|
|
HANDLE hDevice;
|
|
CHAR letter;
|
|
|
|
hDevice = VfdOpenDevice(i);
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE) {
|
|
|
|
// remove all session local drive letters
|
|
|
|
while (VfdGetLocalLink(hDevice, &letter) == ERROR_SUCCESS &&
|
|
isalpha(letter)) {
|
|
VfdSetLocalLink(hDevice, 0);
|
|
}
|
|
|
|
// store existing persistent drive letters
|
|
|
|
VfdGetGlobalLink(hDevice, &drive_letters[i]);
|
|
|
|
CloseHandle(hDevice);
|
|
}
|
|
else {
|
|
VFDTRACE(0,
|
|
(FUNC ": VfdOpenDevice(%d) - %s",
|
|
i, SystemMessage(GetLastError())));
|
|
}
|
|
}
|
|
|
|
// Stop the driver
|
|
|
|
if (!ControlService(hService, SERVICE_CONTROL_STOP, &stat)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": ControlService(SERVICE_CONTROL_STOP) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Wait until the driver is stopped
|
|
|
|
i = 0;
|
|
|
|
while (stat.dwCurrentState != SERVICE_STOPPED && ++i < 5) {
|
|
Sleep(1000);
|
|
|
|
if (!QueryServiceStatus(hService, &stat)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": QueryServiceStatus() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (stat.dwCurrentState != SERVICE_RUNNING) {
|
|
|
|
// broadcast the removal of persistent drive letters
|
|
|
|
for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
|
|
if (isalpha(drive_letters[i])) {
|
|
VfdBroadcastLink(drive_letters[i], VFD_LINK_REMOVED);
|
|
VfdNotify(VFD_OPERATION_DELLINK, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pState) {
|
|
*pState = stat.dwCurrentState;
|
|
}
|
|
|
|
cleanup:
|
|
// Close the service object handle
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
// Close handle to the service control manager.
|
|
|
|
if (hScManager) {
|
|
CloseServiceHandle(hScManager);
|
|
}
|
|
|
|
// Broadcast the successful operation
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_STOP, 0);
|
|
}
|
|
|
|
// revert to the original cursor
|
|
|
|
SetCursor(original);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Get the Virtual Floppy Driver configuration
|
|
//
|
|
DWORD WINAPI VfdGetDriverConfig(
|
|
PSTR sFileName,
|
|
PDWORD pStart)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetDriverConfig"
|
|
SC_HANDLE hScManager; // Service Control Manager
|
|
SC_HANDLE hService; // Service (= Driver)
|
|
LPQUERY_SERVICE_CONFIG config = NULL;
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
if (sFileName) {
|
|
ZeroMemory(sFileName, MAX_PATH);
|
|
}
|
|
|
|
if (pStart) {
|
|
*pStart = 0;
|
|
}
|
|
|
|
// Connect to the Service Control Manager
|
|
|
|
hScManager = OpenSCManager(NULL, NULL, 0);
|
|
|
|
if (hScManager == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenSCManager() - %s", SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Open the VFD driver entry in the service database
|
|
|
|
hService = OpenService(
|
|
hScManager, // Service control manager
|
|
VFD_DEVICE_BASENAME, // service name
|
|
SERVICE_QUERY_CONFIG); // service access mode
|
|
|
|
if (hService == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenService(SERVICE_QUERY_CONFIG) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Get the length of config information
|
|
|
|
if (!QueryServiceConfig(hService, NULL, 0, &result)) {
|
|
ret = GetLastError();
|
|
|
|
if (ret == ERROR_INSUFFICIENT_BUFFER) {
|
|
ret = ERROR_SUCCESS;
|
|
}
|
|
else {
|
|
VFDTRACE(0,
|
|
(FUNC ": QueryServiceConfig() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// allocate a required buffer
|
|
|
|
config = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, result);
|
|
|
|
if (config == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc(%lu) - %s\n",
|
|
result, SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// get the config information
|
|
|
|
if (!QueryServiceConfig(hService, config, result, &result)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": QueryServiceConfig() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// copy information to output buffer
|
|
|
|
if (sFileName) {
|
|
if (strncmp(config->lpBinaryPathName, "\\??\\", 4) == 0) {
|
|
|
|
// driver path is an absolute UNC path
|
|
strncpy(
|
|
sFileName,
|
|
config->lpBinaryPathName + 4,
|
|
MAX_PATH);
|
|
}
|
|
else if (config->lpBinaryPathName[0] == '\\' ||
|
|
(isalpha(config->lpBinaryPathName[0]) &&
|
|
config->lpBinaryPathName[1] == ':')) {
|
|
|
|
// driver path is an absolute path
|
|
strncpy(sFileName,
|
|
config->lpBinaryPathName,
|
|
MAX_PATH);
|
|
}
|
|
else {
|
|
// driver path is relative to the SystemRoot
|
|
// DWORD len = GetEnvironmentVariable(
|
|
// "SystemRoot", sFileName, MAX_PATH);
|
|
|
|
DWORD len = GetWindowsDirectory(sFileName, MAX_PATH);
|
|
|
|
if (len == 0 || len > MAX_PATH) {
|
|
VFDTRACE(0,
|
|
(FUNC ": %%SystemRoot%% is empty or too long.\n"));
|
|
|
|
ret = ERROR_BAD_ENVIRONMENT;
|
|
goto cleanup;
|
|
}
|
|
|
|
sprintf((sFileName + len), "\\%s",
|
|
config->lpBinaryPathName);
|
|
}
|
|
}
|
|
|
|
if (pStart) {
|
|
*pStart = config->dwStartType;
|
|
}
|
|
|
|
cleanup:
|
|
// Free service config buffer
|
|
|
|
if (config) {
|
|
LocalFree(config);
|
|
}
|
|
|
|
// Close the service object handle
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
// Close handle to the service control manager.
|
|
|
|
if (hScManager) {
|
|
CloseServiceHandle(hScManager);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Get the Virtual Floppy Driver running state
|
|
//
|
|
DWORD WINAPI VfdGetDriverState(
|
|
PDWORD pState)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetDriverState"
|
|
SC_HANDLE hScManager = NULL; // Service Control Manager
|
|
SC_HANDLE hService = NULL; // Service (= Driver)
|
|
SERVICE_STATUS status;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
if (pState) {
|
|
*pState = 0;
|
|
}
|
|
|
|
// Connect to the Service Control Manager
|
|
|
|
hScManager = OpenSCManager(NULL, NULL, 0);
|
|
|
|
if (hScManager == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenSCManager() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Open the VFD driver entry in the service database
|
|
|
|
hService = OpenService(
|
|
hScManager, // Service control manager
|
|
VFD_DEVICE_BASENAME, // service name
|
|
SERVICE_QUERY_STATUS); // service access mode
|
|
|
|
if (hService == NULL) {
|
|
|
|
ret = GetLastError();
|
|
|
|
if (ret == ERROR_SERVICE_DOES_NOT_EXIST) {
|
|
|
|
if (pState) {
|
|
*pState = VFD_NOT_INSTALLED;
|
|
}
|
|
|
|
ret = ERROR_SUCCESS;
|
|
}
|
|
else {
|
|
VFDTRACE(0,
|
|
(FUNC ": OpenService(SERVICE_QUERY_STATUS) - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Get current driver status
|
|
|
|
ZeroMemory(&status, sizeof(status));
|
|
|
|
if (!QueryServiceStatus(hService, &status)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": QueryServiceStatus() - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if (pState) {
|
|
*pState = status.dwCurrentState;
|
|
}
|
|
|
|
cleanup:
|
|
// Close the service object handle
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
// Close handle to the service control manager.
|
|
|
|
if (hScManager) {
|
|
CloseServiceHandle(hScManager);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// open a Virtual Floppy drive without showing the "Insert Floppy"
|
|
// dialog when the drive is empty.
|
|
//
|
|
HANDLE WINAPI VfdOpenDevice(
|
|
ULONG nTarget) // either a drive letter or a device number
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdOpenDevice"
|
|
CHAR dev_name[20];
|
|
UINT err_mode;
|
|
HANDLE hDevice;
|
|
|
|
// format a device name string
|
|
|
|
if (isalpha(nTarget)) {
|
|
// nTarget is a drive letter
|
|
// \\.\<x>:
|
|
#ifndef __REACTOS__
|
|
sprintf(dev_name, VFD_VOLUME_TEMPLATE, nTarget);
|
|
#else
|
|
sprintf(dev_name, VFD_VOLUME_TEMPLATE, (CHAR)nTarget);
|
|
#endif
|
|
}
|
|
else if (isdigit(nTarget)) {
|
|
// nTarget is a device number in character
|
|
// \\.\VirtualFD<n>
|
|
sprintf(dev_name, VFD_DEVICE_TEMPLATE, nTarget - '0');
|
|
}
|
|
else {
|
|
// nTarget is a device number value
|
|
// \\.\VirtualFD<n>
|
|
sprintf(dev_name, VFD_DEVICE_TEMPLATE, nTarget);
|
|
}
|
|
|
|
// change error mode in order to avoid "Insert Floppy" dialog
|
|
|
|
err_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
// open the target drive
|
|
|
|
hDevice = CreateFile(
|
|
dev_name,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_NO_BUFFERING,
|
|
NULL);
|
|
|
|
// revert to the original error mode
|
|
|
|
SetErrorMode(err_mode);
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE) {
|
|
|
|
// check if the target is a valid VFD drive
|
|
|
|
ULONG version;
|
|
|
|
if (VfdGetDriverVersion(hDevice, &version) != ERROR_SUCCESS) {
|
|
|
|
// Failed to get the driver version
|
|
|
|
CloseHandle(hDevice);
|
|
hDevice = INVALID_HANDLE_VALUE;
|
|
}
|
|
else if ((version & ~0x80000000) !=
|
|
MAKELONG(VFD_DRIVER_MINOR, VFD_DRIVER_MAJOR)) {
|
|
|
|
// the driver version mismatch
|
|
|
|
// CloseHandle(hDevice);
|
|
// hDevice = INVALID_HANDLE_VALUE;
|
|
|
|
SetLastError(ERROR_REVISION_MISMATCH);
|
|
}
|
|
}
|
|
else {
|
|
VFDTRACE(0,(
|
|
"CreateFile(%s) - %s", dev_name,
|
|
SystemMessage(GetLastError())));;
|
|
}
|
|
|
|
return hDevice;
|
|
}
|
|
|
|
//
|
|
// Open a Virtual Floppy Image
|
|
//
|
|
DWORD WINAPI VfdOpenImage(
|
|
HANDLE hDevice,
|
|
PCSTR sFileName,
|
|
VFD_DISKTYPE nDiskType,
|
|
VFD_MEDIA nMediaType,
|
|
VFD_FLAGS nMediaFlags)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdOpenImage"
|
|
PCSTR prefix;
|
|
CHAR abspath[MAX_PATH];
|
|
DWORD name_len;
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
PVFD_IMAGE_INFO image_info = NULL;
|
|
PUCHAR image_buf = NULL;
|
|
ULONG image_size;
|
|
VFD_FILETYPE file_type;
|
|
|
|
//
|
|
// Check parameters
|
|
//
|
|
|
|
if (hDevice == NULL ||
|
|
hDevice == INVALID_HANDLE_VALUE) {
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
if (nMediaType == VFD_MEDIA_NONE ||
|
|
nMediaType >= VFD_MEDIA_MAX) {
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": Invalid MediaType - %u\n", nMediaType));
|
|
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if (sFileName && *sFileName) {
|
|
|
|
// check file contents and attributes
|
|
|
|
HANDLE hFile = CreateFile(sFileName, GENERIC_READ,
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": CreateFile(%s) - %s",
|
|
sFileName, SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// try extracting image data from zip compressed file
|
|
|
|
ExtractZipImage(hFile, &image_buf, &image_size);
|
|
|
|
if (image_buf) {
|
|
|
|
file_type = VFD_FILETYPE_ZIP;
|
|
|
|
// imz file must be opened in RAM mode
|
|
|
|
if (nDiskType == VFD_DISKTYPE_FILE) {
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": %s is a zip compressed file",
|
|
sFileName));
|
|
|
|
CloseHandle(hFile);
|
|
ret = ERROR_INVALID_PARAMETER;
|
|
|
|
goto exit_func;
|
|
}
|
|
}
|
|
else {
|
|
|
|
file_type = VFD_FILETYPE_RAW;
|
|
|
|
if (nDiskType == VFD_DISKTYPE_FILE) {
|
|
|
|
// direct image file must not be compressed or encrypted
|
|
|
|
BY_HANDLE_FILE_INFORMATION info;
|
|
|
|
if (!GetFileInformationByHandle(hFile, &info)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": GetFileInformationByHandle - %s",
|
|
SystemMessage(ret)));
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (info.dwFileAttributes &
|
|
(FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED)) {
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": file is compressed/encrypted"));
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return ERROR_FILE_ENCRYPTED;
|
|
}
|
|
|
|
image_size = info.nFileSizeLow;
|
|
}
|
|
else {
|
|
|
|
// prepare image data for a file based RAM disk
|
|
|
|
image_size = GetFileSize(hFile, NULL);
|
|
|
|
if (image_size == 0 || image_size == INVALID_FILE_SIZE) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": GetFileSize - %s",
|
|
SystemMessage(ret)));
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return ret;
|
|
}
|
|
|
|
image_buf = (PUCHAR)LocalAlloc(LPTR, image_size);
|
|
|
|
if (image_buf == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc - %s",
|
|
SystemMessage(ret)));
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": SetFilePointer - %s",
|
|
SystemMessage(ret)));
|
|
|
|
CloseHandle(hFile);
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
if (!ReadFile(hFile, image_buf, image_size, &result, NULL) ||
|
|
image_size != result) {
|
|
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": ReadFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
CloseHandle(hFile);
|
|
|
|
goto exit_func;
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
// Prepare absolute path in the kernel namespace
|
|
|
|
if (*sFileName == '\\' && *(sFileName + 1) == '\\') {
|
|
|
|
// \\server\share\path\floppy.img
|
|
|
|
prefix = "\\??\\UNC";
|
|
sFileName++; // drip the first '\'
|
|
}
|
|
else {
|
|
|
|
// local path
|
|
|
|
PSTR file_part;
|
|
|
|
if (GetFullPathName(sFileName,
|
|
sizeof(abspath), abspath, &file_part) == 0) {
|
|
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": GetFullPathName(%s) - %s\n",
|
|
sFileName, SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
prefix = "\\??\\";
|
|
sFileName = abspath;
|
|
}
|
|
|
|
name_len = strlen(prefix) + strlen(sFileName);
|
|
}
|
|
else {
|
|
|
|
// filename is not specified -- pure RAM disk
|
|
|
|
nDiskType = VFD_DISKTYPE_RAM;
|
|
file_type = VFD_FILETYPE_NONE;
|
|
|
|
prefix = NULL;
|
|
name_len = 0;
|
|
|
|
// prepare a FAT formatted RAM image
|
|
|
|
image_size = VfdGetMediaSize(nMediaType);
|
|
|
|
image_buf = (PUCHAR)LocalAlloc(LPTR, image_size);
|
|
|
|
if (image_buf == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
FormatBufferFat(image_buf, VFD_BYTE_TO_SECTOR(image_size));
|
|
}
|
|
|
|
if (image_size < VfdGetMediaSize(nMediaType)) {
|
|
|
|
// image is too small for the specified media type
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": Image is too small for the specified media type\n"));
|
|
|
|
ret = ERROR_INVALID_PARAMETER;
|
|
goto exit_func;
|
|
}
|
|
|
|
// prepare VFD_IMAGE_INFO structure
|
|
|
|
image_info = (PVFD_IMAGE_INFO)LocalAlloc(LPTR,
|
|
sizeof(VFD_IMAGE_INFO) + name_len + 1);
|
|
|
|
if (image_info == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc(%lu) - %s\n",
|
|
sizeof(VFD_IMAGE_INFO) + name_len + 1,
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
ZeroMemory(image_info,
|
|
sizeof(VFD_IMAGE_INFO) + name_len + 1);
|
|
|
|
if (name_len) {
|
|
sprintf(image_info->FileName,
|
|
"%s%s", prefix, sFileName);
|
|
}
|
|
|
|
image_info->NameLength = (USHORT)name_len;
|
|
|
|
image_info->DiskType = nDiskType;
|
|
image_info->MediaType = nMediaType;
|
|
image_info->MediaFlags = nMediaFlags;
|
|
image_info->FileType = file_type;
|
|
image_info->ImageSize = image_size;
|
|
|
|
if (nDiskType != VFD_DISKTYPE_FILE) {
|
|
// protect flag for a RAM disk is set after
|
|
// initializing the image buffer
|
|
image_info->MediaFlags &= ~VFD_FLAG_WRITE_PROTECTED;
|
|
}
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": Opening file \"%s\" (%lu bytes) %s %s %s %s\n",
|
|
name_len ? image_info->FileName : "<RAM>",
|
|
image_info->ImageSize,
|
|
(file_type == VFD_FILETYPE_ZIP) ? "ZIP image" : "RAW image",
|
|
VfdMediaTypeName(nMediaType),
|
|
(nDiskType == VFD_DISKTYPE_FILE) ? "FILE disk" : "RAM disk",
|
|
(nMediaFlags & VFD_FLAG_WRITE_PROTECTED) ? "Protected" : "Writable"));
|
|
|
|
// Open the image file / create a ram disk
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_OPEN_IMAGE,
|
|
image_info,
|
|
sizeof(VFD_IMAGE_INFO) + name_len,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_OPEN_FILE) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// initialize the RAM disk image
|
|
|
|
if (nDiskType != VFD_DISKTYPE_FILE) {
|
|
|
|
image_size &= ~VFD_SECTOR_ALIGN_MASK;
|
|
|
|
if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": SetFilePointer - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
if (!WriteFile(hDevice, image_buf, image_size, &result, NULL) ||
|
|
image_size != result) {
|
|
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": WriteFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
if (nMediaFlags & VFD_FLAG_WRITE_PROTECTED) {
|
|
VfdWriteProtect(hDevice, TRUE);
|
|
}
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_RESET_MODIFY,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_RESET_MODIFY) - %s",
|
|
SystemMessage(GetLastError())));
|
|
}
|
|
}
|
|
|
|
// Broadcast the successful operation
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
ULONG number;
|
|
CHAR root[] = "A:\\";
|
|
|
|
if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_OPEN, number);
|
|
}
|
|
|
|
VfdGetGlobalLink(hDevice, &root[0]);
|
|
|
|
if (isalpha(root[0])) {
|
|
SHChangeNotify(SHCNE_MEDIAINSERTED, SHCNF_PATH, root, NULL);
|
|
}
|
|
|
|
while (VfdGetLocalLink(hDevice, &root[0]) == ERROR_SUCCESS &&
|
|
isalpha(root[0])) {
|
|
SHChangeNotify(SHCNE_MEDIAINSERTED, SHCNF_PATH, root, NULL);
|
|
}
|
|
}
|
|
|
|
exit_func:
|
|
if (image_info) {
|
|
LocalFree(image_info);
|
|
}
|
|
|
|
if (image_buf) {
|
|
LocalFree(image_buf);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Close the virtual floppy Image
|
|
//
|
|
DWORD WINAPI VfdCloseImage(
|
|
HANDLE hDevice,
|
|
BOOL bForce)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdCloseImage"
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
int retry = 0;
|
|
|
|
lock_retry:
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
FSCTL_LOCK_VOLUME,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(FSCTL_LOCK_VOLUME) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
if (ret != ERROR_ACCESS_DENIED || retry == 5) {
|
|
// error other than access denied or
|
|
// operation kept failing for 5 seconds
|
|
return ret;
|
|
}
|
|
|
|
if (!bForce) {
|
|
// error is access denied and
|
|
// the force flag is not set
|
|
|
|
if (retry == 0) {
|
|
|
|
// send the MEDIAREMOVED notification to the shell and
|
|
// see if the shell releases the target drive
|
|
|
|
CHAR root[] = "A:\\";
|
|
|
|
VfdGetGlobalLink(hDevice, &root[0]);
|
|
|
|
if (isalpha(root[0])) {
|
|
SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATH, root, NULL);
|
|
}
|
|
|
|
while (VfdGetLocalLink(hDevice, &root[0]) == ERROR_SUCCESS &&
|
|
isalpha(root[0])) {
|
|
SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATH, root, NULL);
|
|
}
|
|
}
|
|
|
|
Sleep(1000);
|
|
retry++;
|
|
|
|
goto lock_retry;
|
|
}
|
|
}
|
|
|
|
ret = ERROR_SUCCESS;
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
FSCTL_DISMOUNT_VOLUME,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(FSCTL_DISMOUNT_VOLUME) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_CLOSE_IMAGE,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
if (ret != ERROR_NOT_READY) {
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_CLOSE_FILE) - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
FSCTL_UNLOCK_VOLUME,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
// This should not be fatal because the volume is unlocked
|
|
// when the handle is closed anyway
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
|
|
SystemMessage(GetLastError())));
|
|
}
|
|
|
|
// Broadcast the successful operation
|
|
if (ret == ERROR_SUCCESS) {
|
|
ULONG number;
|
|
|
|
if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_CLOSE, number);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Get Virtual Floppy image info
|
|
//
|
|
DWORD WINAPI VfdGetImageInfo(
|
|
HANDLE hDevice,
|
|
PSTR sFileName,
|
|
PVFD_DISKTYPE pDiskType,
|
|
PVFD_MEDIA pMediaType,
|
|
PVFD_FLAGS pMediaFlags,
|
|
PVFD_FILETYPE pFileType,
|
|
PULONG pImageSize)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetImageInfo"
|
|
PVFD_IMAGE_INFO image_info;
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
image_info = (PVFD_IMAGE_INFO)LocalAlloc(
|
|
LPTR, sizeof(VFD_IMAGE_INFO) + MAX_PATH);
|
|
|
|
if (image_info == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc(%lu) - %s\n",
|
|
sizeof(VFD_IMAGE_INFO) + MAX_PATH, SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
ZeroMemory(image_info, sizeof(VFD_IMAGE_INFO) + MAX_PATH);
|
|
|
|
// Query file information
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_QUERY_IMAGE,
|
|
NULL,
|
|
0,
|
|
image_info,
|
|
sizeof(VFD_IMAGE_INFO) + MAX_PATH,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
if (ret != ERROR_MORE_DATA) {
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_FILE) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// copy obtained information to output buffer
|
|
|
|
if (sFileName) {
|
|
|
|
// if filename is too long, clip it
|
|
|
|
if (image_info->NameLength >= MAX_PATH) {
|
|
image_info->NameLength = MAX_PATH - 1;
|
|
}
|
|
|
|
// ensure the name is properly terminated
|
|
|
|
image_info->FileName[image_info->NameLength] = '\0';
|
|
|
|
if (strncmp(image_info->FileName, "\\??\\UNC", 7) == 0) {
|
|
*sFileName = '\\';
|
|
strcpy(sFileName + 1, image_info->FileName + 7);
|
|
}
|
|
else if (strncmp(image_info->FileName, "\\??\\", 4) == 0) {
|
|
strcpy(sFileName, image_info->FileName + 4);
|
|
}
|
|
else {
|
|
strcpy(sFileName, image_info->FileName);
|
|
}
|
|
}
|
|
|
|
if (pDiskType) {
|
|
*pDiskType = image_info->DiskType;
|
|
}
|
|
|
|
if (pMediaType) {
|
|
*pMediaType = image_info->MediaType;
|
|
}
|
|
|
|
if (pMediaFlags) {
|
|
*pMediaFlags = image_info->MediaFlags;
|
|
}
|
|
|
|
if (pFileType) {
|
|
*pFileType = image_info->FileType;
|
|
}
|
|
|
|
if (pImageSize) {
|
|
*pImageSize = image_info->ImageSize;
|
|
}
|
|
|
|
cleanup:
|
|
if (image_info) {
|
|
LocalFree(image_info);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Get current media state (opened / write protected)
|
|
//
|
|
DWORD WINAPI VfdGetMediaState(
|
|
HANDLE hDevice)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetMediaState"
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
// Query file information
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_DISK_IS_WRITABLE,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
if (ret != ERROR_NOT_READY) {
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_DISK_IS_WRITABLE) - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Set or Delete a global drive letter
|
|
//
|
|
DWORD WINAPI VfdSetGlobalLink(
|
|
HANDLE hDevice,
|
|
CHAR cLetter)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdSetGlobalLink"
|
|
CHAR letter;
|
|
ULONG number;
|
|
DWORD result;
|
|
DWORD ret;
|
|
|
|
if (isalpha(cLetter)) {
|
|
|
|
// make sure the drive does not have a drive letter
|
|
|
|
letter = 0;
|
|
|
|
VfdGetGlobalLink(hDevice, &letter);
|
|
|
|
if (isalpha(letter)) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Drive already has a drive letter %c\n", letter));
|
|
return ERROR_ALREADY_ASSIGNED;
|
|
}
|
|
|
|
VfdGetLocalLink(hDevice, &letter);
|
|
|
|
if (isalpha(letter)) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Drive already has a drive letter %c\n", letter));
|
|
return ERROR_ALREADY_ASSIGNED;
|
|
}
|
|
|
|
// make sure drive letter is not in use
|
|
|
|
cLetter = (CHAR)toupper(cLetter);
|
|
|
|
if (GetLogicalDrives() & (1 << (cLetter - 'A'))) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Drive letter %c already used\n", cLetter));
|
|
return ERROR_ALREADY_ASSIGNED;
|
|
}
|
|
|
|
// Assign a new drive letter
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_SET_LINK,
|
|
&cLetter,
|
|
sizeof(cLetter),
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_SET_LINK) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// broadcast system message
|
|
|
|
VfdBroadcastLink(cLetter, VFD_LINK_CREATED);
|
|
|
|
// broadcast VFD message
|
|
|
|
if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_SETLINK, number);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (!cLetter) {
|
|
|
|
// make sure the drive has a global drive letter
|
|
|
|
letter = 0;
|
|
|
|
VfdGetGlobalLink(hDevice, &letter);
|
|
|
|
if (!isalpha(letter)) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Drive does not have a drive letter\n"));
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
// Remove drive letters
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_SET_LINK,
|
|
&cLetter,
|
|
sizeof(cLetter),
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_SET_LINK) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// broadcast system message
|
|
|
|
VfdBroadcastLink(letter, VFD_LINK_REMOVED);
|
|
|
|
// broadcast VFD message
|
|
if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_DELLINK, number);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get a global drive letter
|
|
//
|
|
DWORD WINAPI VfdGetGlobalLink(
|
|
HANDLE hDevice,
|
|
PCHAR pLetter)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetGlobalLinks"
|
|
DWORD result;
|
|
DWORD ret;
|
|
|
|
if (!pLetter) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*pLetter = 0;
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_QUERY_LINK,
|
|
NULL,
|
|
0,
|
|
pLetter,
|
|
sizeof(*pLetter),
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_LINK) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set or remove a local drive letter
|
|
//
|
|
DWORD WINAPI VfdSetLocalLink(
|
|
HANDLE hDevice,
|
|
CHAR cLetter)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdSetLocalLink"
|
|
CHAR letter;
|
|
CHAR dos_name[] = "A:";
|
|
CHAR dev_name[MAX_PATH];
|
|
ULONG number;
|
|
DWORD ret;
|
|
|
|
if (isalpha(cLetter)) {
|
|
|
|
// make sure the drive does not have a drive letter
|
|
|
|
letter = 0;
|
|
|
|
VfdGetGlobalLink(hDevice, &letter);
|
|
|
|
if (isalpha(letter)) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Drive already has a drive letter %c\n", letter));
|
|
return ERROR_ALREADY_ASSIGNED;
|
|
}
|
|
|
|
VfdGetLocalLink(hDevice, &letter);
|
|
|
|
if (isalpha(letter)) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Drive already has a drive letter %c\n", letter));
|
|
return ERROR_ALREADY_ASSIGNED;
|
|
}
|
|
|
|
// make sure drive letters are not in use
|
|
|
|
cLetter = (CHAR)toupper(cLetter);
|
|
|
|
if (GetLogicalDrives() & (1 << (cLetter - 'A'))) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Drive letter already used\n"));
|
|
|
|
return ERROR_ALREADY_ASSIGNED;
|
|
}
|
|
|
|
// get VFD device name
|
|
|
|
ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
// assign a drive letter
|
|
|
|
dos_name[0] = cLetter;
|
|
|
|
if (!DefineDosDevice(DDD_RAW_TARGET_PATH, dos_name, dev_name)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DefineDosDevice(%s,%s) - %s",
|
|
dos_name, dev_name, SystemMessage(ret)));
|
|
}
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
// broadcast VFD message
|
|
|
|
if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_SETLINK, number);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
else if (!cLetter) {
|
|
|
|
// make sure the drive has a local drive letter
|
|
|
|
letter = 0;
|
|
|
|
VfdGetLocalLink(hDevice, &letter);
|
|
|
|
if (!isalpha(letter)) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Drive letter is not assigned to this drive\n"));
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
// get VFD device name
|
|
|
|
ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
// remove drive letters
|
|
#define DDD_FLAGS (DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH | DDD_EXACT_MATCH_ON_REMOVE)
|
|
|
|
dos_name[0] = (CHAR)toupper(letter);
|
|
|
|
if (!DefineDosDevice(DDD_FLAGS, dos_name, dev_name)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DefineDosDevice(%s,%s) - %s",
|
|
dos_name, dev_name, SystemMessage(ret)));
|
|
}
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
// broadcast VFD message
|
|
if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_DELLINK, number);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
else {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get local drive letters
|
|
//
|
|
DWORD WINAPI VfdGetLocalLink(
|
|
HANDLE hDevice,
|
|
PCHAR pLetter)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetLocalLinks"
|
|
CHAR global;
|
|
ULONG logical;
|
|
CHAR dos_name[] = "A:";
|
|
CHAR dev_name[MAX_PATH];
|
|
CHAR dos_target[MAX_PATH * 2];
|
|
DWORD ret;
|
|
|
|
if (!pLetter) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Get the VFD device name
|
|
|
|
ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
// Get global drive letter
|
|
|
|
ret = VfdGetGlobalLink(hDevice, &global);
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
return ret;
|
|
}
|
|
|
|
// Get logical drives
|
|
|
|
logical = GetLogicalDrives();
|
|
|
|
// exclude the global drive letter
|
|
|
|
if (isalpha(global)) {
|
|
logical &= ~(1 << (toupper(global) - 'A'));
|
|
}
|
|
|
|
// start searching from the next drive letter
|
|
|
|
if (isalpha(*pLetter)) {
|
|
dos_name[0] = (CHAR)(toupper(*pLetter) + 1);
|
|
logical >>= (dos_name[0] - 'A');
|
|
}
|
|
|
|
// Check dos device targets
|
|
|
|
*pLetter = '\0';
|
|
|
|
while (logical) {
|
|
if (logical & 0x01) {
|
|
if (QueryDosDevice(dos_name, dos_target, sizeof(dos_target))) {
|
|
if (_stricmp(dos_target, dev_name) == 0) {
|
|
*pLetter = dos_name[0];
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
VFDTRACE(0,
|
|
(FUNC ": QueryDosDevice(%s) - %s",
|
|
dos_name, SystemMessage(GetLastError())));
|
|
}
|
|
}
|
|
logical >>= 1;
|
|
dos_name[0]++;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Get the Virtual Floppy device number
|
|
//
|
|
DWORD WINAPI VfdGetDeviceNumber(
|
|
HANDLE hDevice,
|
|
PULONG pNumber)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetDeviceNumber"
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
if (!pNumber) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*pNumber = 0;
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_QUERY_NUMBER,
|
|
NULL,
|
|
0,
|
|
pNumber,
|
|
sizeof(ULONG),
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_NUMBER) - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Get the Virtual Floppy device name
|
|
|
|
DWORD WINAPI VfdGetDeviceName(
|
|
HANDLE hDevice,
|
|
PCHAR pName,
|
|
ULONG nLength)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetDeviceName"
|
|
DWORD result;
|
|
WCHAR wname[MAX_PATH];
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
if (!pName || !nLength) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
ZeroMemory(pName, nLength);
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_QUERY_NAME,
|
|
NULL,
|
|
0,
|
|
wname,
|
|
sizeof(wname),
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_NUMBER) - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
|
|
if (!WideCharToMultiByte(CP_OEMCP, 0, &wname[1],
|
|
wname[0] / sizeof(WCHAR), pName, nLength, NULL, NULL)) {
|
|
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": WideCharToMultiByte - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Get Virtual Floppy driver version
|
|
//
|
|
DWORD WINAPI VfdGetDriverVersion(
|
|
HANDLE hDevice,
|
|
PULONG pVersion)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdGetDriverVersion"
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
if (!pVersion) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*pVersion = '\0';
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_QUERY_VERSION,
|
|
NULL,
|
|
0,
|
|
pVersion,
|
|
sizeof(ULONG),
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_VERSION) - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Change the write protect state of the media
|
|
//
|
|
DWORD WINAPI VfdWriteProtect(
|
|
HANDLE hDevice,
|
|
BOOL bProtect)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdWriteProtect"
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
bProtect ? IOCTL_VFD_SET_PROTECT : IOCTL_VFD_CLEAR_PROTECT,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_SET_PROTECT) - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
|
|
if (ret == ERROR_SUCCESS) {
|
|
ULONG number;
|
|
|
|
if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
|
|
VfdNotify(VFD_OPERATION_PROTECT, number);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Format the current media with FAT12
|
|
|
|
DWORD WINAPI VfdFormatMedia(
|
|
HANDLE hDevice)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdFormatMedia"
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
PUCHAR buf = NULL;
|
|
GET_LENGTH_INFORMATION length;
|
|
|
|
// Get the media size
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_DISK_GET_LENGTH_INFO,
|
|
NULL,
|
|
0,
|
|
&length,
|
|
sizeof(length),
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_DISK_GET_LENGTH_INFO) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// Prepare a formatted image buffer
|
|
|
|
buf = (PUCHAR)LocalAlloc(LPTR, length.Length.LowPart);
|
|
|
|
if (buf == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// format the buffer
|
|
|
|
ret = FormatBufferFat(buf,
|
|
VFD_BYTE_TO_SECTOR(length.Length.LowPart));
|
|
|
|
if (ret != ERROR_SUCCESS) {
|
|
goto exit_func;
|
|
}
|
|
|
|
// seek the top of the media
|
|
|
|
if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": SetFilePointer - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// write the image into the media
|
|
|
|
if (!WriteFile(hDevice, buf, length.Length.LowPart, &result, NULL) ||
|
|
result != length.Length.LowPart) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": WriteFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
exit_func:
|
|
// unlock the target volume
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
FSCTL_UNLOCK_VOLUME,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
|
|
SystemMessage(GetLastError())));
|
|
}
|
|
|
|
// release the format image buffer
|
|
if (buf) {
|
|
LocalFree(buf);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Dismount the volume (should be called before Save, Format)
|
|
|
|
DWORD WINAPI VfdDismountVolume(
|
|
HANDLE hDevice,
|
|
BOOL bForce)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdDismountVolume"
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
// Lock the target volume
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
FSCTL_LOCK_VOLUME,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(FSCTL_LOCK_VOLUME) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
if (ret != ERROR_ACCESS_DENIED || !bForce) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
// Dismount the target volume
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
FSCTL_DISMOUNT_VOLUME,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(FSCTL_DISMOUNT_VOLUME) - %s",
|
|
SystemMessage(ret)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Save the current image into a file
|
|
|
|
DWORD WINAPI VfdSaveImage(
|
|
HANDLE hDevice,
|
|
PCSTR sFileName,
|
|
BOOL bOverWrite,
|
|
BOOL bTruncate)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdSaveImage"
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
PUCHAR buf = NULL;
|
|
GET_LENGTH_INFORMATION length;
|
|
|
|
|
|
ret = ERROR_SUCCESS;
|
|
|
|
// Get the media size
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_DISK_GET_LENGTH_INFO,
|
|
NULL,
|
|
0,
|
|
&length,
|
|
sizeof(length),
|
|
&result,
|
|
NULL))
|
|
{
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_DISK_GET_LENGTH_INFO) - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// Prepare an intermediate image buffer
|
|
|
|
buf = (PUCHAR)LocalAlloc(LPTR, length.Length.LowPart);
|
|
|
|
if (buf == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// seek the top of the media
|
|
|
|
if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": SetFilePointer - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// read the image data
|
|
|
|
if (!ReadFile(hDevice, buf, length.Length.LowPart, &result, NULL) ||
|
|
result != length.Length.LowPart) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": ReadFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// open the destination file
|
|
|
|
hFile = CreateFile(sFileName, GENERIC_WRITE, 0, NULL,
|
|
bOverWrite ? OPEN_ALWAYS : CREATE_NEW, 0, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": CreateFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// seek the top of the file
|
|
|
|
if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": SetFilePointer - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// write the image data
|
|
|
|
if (!WriteFile(hFile, buf, length.Length.LowPart, &result, NULL) ||
|
|
result != length.Length.LowPart) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": WriteFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// truncate the target file
|
|
|
|
if (bTruncate && !SetEndOfFile(hFile)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": SetEndOfFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
// reset the media modified flag
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
IOCTL_VFD_RESET_MODIFY,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(IOCTL_VFD_RESET_MODIFY) - %s",
|
|
SystemMessage(GetLastError())));
|
|
}
|
|
|
|
exit_func:
|
|
// unlock the target volume
|
|
|
|
if (!DeviceIoControl(
|
|
hDevice,
|
|
FSCTL_UNLOCK_VOLUME,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&result,
|
|
NULL))
|
|
{
|
|
VFDTRACE(0,
|
|
(FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
|
|
SystemMessage(GetLastError())));
|
|
}
|
|
|
|
// release the format image buffer
|
|
|
|
if (buf) {
|
|
LocalFree(buf);
|
|
}
|
|
|
|
// close the image file
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Check if specified file is valid VFD driver
|
|
//
|
|
DWORD WINAPI VfdCheckDriverFile(
|
|
PCSTR sFileName,
|
|
PULONG pFileVersion)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdCheckDriverFile"
|
|
DWORD result;
|
|
DWORD dummy;
|
|
PVOID info;
|
|
VS_FIXEDFILEINFO *fixedinfo;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
PSTR str;
|
|
|
|
// Check parameter
|
|
|
|
if (!sFileName || !*sFileName) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (pFileVersion) {
|
|
*pFileVersion = 0;
|
|
}
|
|
|
|
// check file existence
|
|
|
|
if (GetFileAttributes(sFileName) == INVALID_FILE_ATTRIBUTES) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": GetFileAttributes - %s\n",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// check file version
|
|
|
|
result = GetFileVersionInfoSize((PSTR)sFileName, &dummy);
|
|
|
|
if (result == 0) {
|
|
VFDTRACE(0,
|
|
(FUNC ": GetFileVersionInfoSize == 0\n"));
|
|
|
|
return ERROR_BAD_DRIVER;
|
|
}
|
|
|
|
info = LocalAlloc(LPTR, result);
|
|
|
|
if (info == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc(%lu) - %s\n",
|
|
result, SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (!GetFileVersionInfo((PSTR)sFileName, 0, result, info)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": GetFileVersionInfo - %s", SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
result = sizeof(fixedinfo);
|
|
|
|
if (!VerQueryValue(info, "\\", (PVOID *)&fixedinfo, (PUINT)&result)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": VerQueryValue(\"\\\") - %s", SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if (fixedinfo->dwFileOS != VOS_NT_WINDOWS32 ||
|
|
fixedinfo->dwFileType != VFT_DRV ||
|
|
fixedinfo->dwFileSubtype != VFT2_DRV_SYSTEM) {
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": Invalid file type flags\n"));
|
|
|
|
ret = ERROR_BAD_DRIVER;
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if (pFileVersion) {
|
|
*pFileVersion = fixedinfo->dwFileVersionMS;
|
|
|
|
if (fixedinfo->dwFileFlags & VS_FF_DEBUG) {
|
|
*pFileVersion |= 0x80000000;
|
|
}
|
|
}
|
|
|
|
if (!VerQueryValue(info,
|
|
"\\StringFileInfo\\" VFD_VERSIONINFO_LANG "\\OriginalFileName",
|
|
(PVOID *)&str, (PUINT)&result)) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": VerQueryValue(\"OriginalFileName\") - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if (strcmp(str, VFD_DRIVER_FILENAME)) {
|
|
VFDTRACE(0,
|
|
(FUNC ": Invalid original file name\n"));
|
|
|
|
ret = ERROR_BAD_DRIVER;
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if (fixedinfo->dwFileVersionMS != MAKELONG(VFD_DRIVER_MINOR, VFD_DRIVER_MAJOR) ||
|
|
fixedinfo->dwProductVersionMS != MAKELONG(VFD_PRODUCT_MINOR, VFD_PRODUCT_MAJOR)) {
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": Invalid version values - file:%08x, prod: %08x\n",
|
|
fixedinfo->dwFileVersionMS, fixedinfo->dwProductVersionMS));
|
|
|
|
ret = ERROR_BAD_DRIVER;
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// Ensure that the driver binary is located on a local drive
|
|
// because device driver cannot be started on network drives.
|
|
|
|
if (*sFileName == '\\' && *(sFileName + 1) == '\\') {
|
|
// full path is a UNC path -- \\server\dir\...
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": Driver is located on a network drive\n"));
|
|
|
|
return ERROR_NETWORK_ACCESS_DENIED;
|
|
}
|
|
else {
|
|
// ensure that the drive letter is not a network drive
|
|
|
|
CHAR root[] = " :\\";
|
|
|
|
root[0] = *sFileName;
|
|
|
|
if (GetDriveType(root) == DRIVE_REMOTE) {
|
|
// the drive is a network drive
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": Driver is located on a network drive\n"));
|
|
|
|
return ERROR_NETWORK_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
LocalFree(info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// check an image file
|
|
//
|
|
DWORD WINAPI VfdCheckImageFile(
|
|
PCSTR sFileName,
|
|
PDWORD pAttributes,
|
|
PVFD_FILETYPE pFileType,
|
|
PULONG pImageSize)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdCheckImageFile"
|
|
HANDLE hFile;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
if (!sFileName || !*sFileName || !pAttributes || !pImageSize || !pFileType) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// get file attributes
|
|
|
|
*pAttributes = GetFileAttributes(sFileName);
|
|
|
|
if (*pAttributes == INVALID_FILE_ATTRIBUTES) {
|
|
ret = GetLastError();
|
|
|
|
if (ret != ERROR_FILE_NOT_FOUND) {
|
|
VFDTRACE(0,
|
|
(FUNC ": GetFileAttributes(%s) - %s\n",
|
|
sFileName, SystemMessage(ret)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Open the target file
|
|
|
|
hFile = CreateFile(sFileName, GENERIC_READ | GENERIC_WRITE,
|
|
0, NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
|
|
// failed to open
|
|
|
|
ret = GetLastError();
|
|
|
|
if (ret != ERROR_ACCESS_DENIED) {
|
|
VFDTRACE(0,
|
|
(FUNC ": CreateFile(%s) - %s\n",
|
|
sFileName, SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// try opening it read-only
|
|
|
|
hFile = CreateFile(sFileName, GENERIC_READ,
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
|
|
// cannot open even read-only
|
|
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": CreateFile(%s) - %s\n",
|
|
sFileName, SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// file can be opened read-only
|
|
*pAttributes |= FILE_ATTRIBUTE_READONLY;
|
|
ret = ERROR_SUCCESS;
|
|
}
|
|
|
|
// check if the image is an IMZ file
|
|
|
|
if (ExtractZipInfo(hFile, pImageSize) == ERROR_SUCCESS) {
|
|
*pFileType = VFD_FILETYPE_ZIP;
|
|
}
|
|
else {
|
|
*pImageSize = GetFileSize(hFile, NULL);
|
|
*pFileType = VFD_FILETYPE_RAW;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Create a formatted new image file
|
|
//
|
|
DWORD WINAPI VfdCreateImageFile(
|
|
PCSTR sFileName,
|
|
VFD_MEDIA nMediaType,
|
|
VFD_FILETYPE nFileType,
|
|
BOOL bOverWrite)
|
|
{
|
|
#undef FUNC
|
|
#define FUNC "VfdCreateImageFile"
|
|
HANDLE hFile;
|
|
ULONG file_size;
|
|
PUCHAR image_buf = NULL;
|
|
DWORD result;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
if (nFileType != VFD_FILETYPE_RAW) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
file_size = VfdGetMediaSize(nMediaType);
|
|
|
|
if (file_size == 0) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
hFile = CreateFile(sFileName, GENERIC_WRITE, 0, NULL,
|
|
bOverWrite ? CREATE_ALWAYS : CREATE_NEW, 0, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": CreateFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
image_buf = (PUCHAR)LocalAlloc(LPTR, file_size);
|
|
|
|
if (image_buf == NULL) {
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": LocalAlloc - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
FormatBufferFat(image_buf, VFD_BYTE_TO_SECTOR(file_size));
|
|
|
|
if (!WriteFile(hFile, image_buf, file_size, &result, NULL) ||
|
|
file_size != result) {
|
|
|
|
ret = GetLastError();
|
|
|
|
VFDTRACE(0,
|
|
(FUNC ": WriteFile - %s",
|
|
SystemMessage(ret)));
|
|
|
|
goto exit_func;
|
|
}
|
|
|
|
SetEndOfFile(hFile);
|
|
|
|
exit_func:
|
|
CloseHandle(hFile);
|
|
|
|
if (image_buf) {
|
|
LocalFree(image_buf);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//
|
|
// choose first available drive letter
|
|
//
|
|
CHAR WINAPI VfdChooseLetter()
|
|
{
|
|
DWORD logical_drives = GetLogicalDrives();
|
|
CHAR drive_letter = 'A';
|
|
|
|
if (logical_drives == 0) {
|
|
return '\0';
|
|
}
|
|
|
|
while (logical_drives & 0x1) {
|
|
logical_drives >>= 1;
|
|
drive_letter++;
|
|
}
|
|
|
|
if (drive_letter > 'Z') {
|
|
return '\0';
|
|
}
|
|
|
|
return drive_letter;
|
|
}
|
|
|
|
//
|
|
// media type functions
|
|
//
|
|
static const struct
|
|
{
|
|
ULONG Size;
|
|
PCSTR Name;
|
|
}
|
|
media_tbl[VFD_MEDIA_MAX] =
|
|
{
|
|
{ 0, "" }, // VFD_MEDIA_NONE,
|
|
{ VFD_SECTOR_TO_BYTE(320), "5.25\" 160KB" }, // VFD_MEDIA_F5_160
|
|
{ VFD_SECTOR_TO_BYTE(360), "5.25\" 180KB" }, // VFD_MEDIA_F5_180
|
|
{ VFD_SECTOR_TO_BYTE(640), "5.25\" 320KB" }, // VFD_MEDIA_F5_320
|
|
{ VFD_SECTOR_TO_BYTE(720), "5.25\" 360KB" }, // VFD_MEDIA_F5_360
|
|
{ VFD_SECTOR_TO_BYTE(1280), "3.5\" 640KB" }, // VFD_MEDIA_F3_640
|
|
{ VFD_SECTOR_TO_BYTE(1280), "5.25\" 640KB" }, // VFD_MEDIA_F5_640
|
|
{ VFD_SECTOR_TO_BYTE(1440), "3.5\" 720KB" }, // VFD_MEDIA_F3_720
|
|
{ VFD_SECTOR_TO_BYTE(1440), "5.25\" 720KB" }, // VFD_MEDIA_F5_720
|
|
{ VFD_SECTOR_TO_BYTE(1640), "3.5\" 820KB" }, // VFD_MEDIA_F3_820
|
|
{ VFD_SECTOR_TO_BYTE(2400), "3.5\" 1.2MB" }, // VFD_MEDIA_F3_1P2
|
|
{ VFD_SECTOR_TO_BYTE(2400), "5.25\" 1.2MB" }, // VFD_MEDIA_F5_1P2
|
|
{ VFD_SECTOR_TO_BYTE(2880), "3.5\" 1.44MB" }, // VFD_MEDIA_F3_1P4
|
|
{ VFD_SECTOR_TO_BYTE(3360), "3.5\" 1.68MB DMF" }, // VFD_MEDIA_F3_1P6
|
|
{ VFD_SECTOR_TO_BYTE(3444), "3.5\" 1.72MB DMF" }, // VFD_MEDIA_F3_1P7
|
|
{ VFD_SECTOR_TO_BYTE(5760), "3.5\" 2.88MB"} // VFD_MEDIA_F3_2P8
|
|
};
|
|
|
|
// Lookup the largest media to fit in a size
|
|
|
|
VFD_MEDIA WINAPI VfdLookupMedia(
|
|
ULONG nSize)
|
|
{
|
|
VFD_MEDIA i;
|
|
|
|
for (i = 1; i < VFD_MEDIA_MAX; i++) {
|
|
if (nSize < media_tbl[i].Size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (--i);
|
|
}
|
|
|
|
// Get media size (in bytes) of a media type
|
|
|
|
ULONG WINAPI VfdGetMediaSize(
|
|
VFD_MEDIA nMediaType)
|
|
{
|
|
return nMediaType < VFD_MEDIA_MAX ? media_tbl[nMediaType].Size : 0;
|
|
}
|
|
|
|
// Get media type name
|
|
|
|
PCSTR WINAPI VfdMediaTypeName(
|
|
VFD_MEDIA nMediaType)
|
|
{
|
|
return nMediaType < VFD_MEDIA_MAX ? media_tbl[nMediaType].Name : NULL;
|
|
}
|