xref: /reactos/modules/rosapps/lib/vfdlib/vfdctl.c (revision 84344399)
1 /*
2 	vfdctl.c
3 
4 	Virtual Floppy Drive for Windows
5 	Driver control library
6 	Driver and image control functions
7 
8 	Copyright (C) 2003-2005 Ken Kato
9 */
10 
11 #ifdef __cplusplus
12 #pragma message(__FILE__": Compiled as C++ for testing purpose.")
13 #endif	// __cplusplus
14 
15 #define WIN32_LEAN_AND_MEAN
16 #include <windows.h>
17 #include <dbt.h>
18 #if !defined(__REACTOS__) || defined(_MSC_VER)
19 #pragma warning (push, 3)
20 #endif
21 #include <shlobj.h>
22 #include <winioctl.h>
23 #if !defined(__REACTOS__) || defined(_MSC_VER)
24 #pragma warning (pop)
25 #endif
26 #include <stdio.h>
27 
28 #include "vfdtypes.h"
29 #include "vfdio.h"
30 #include "vfdapi.h"
31 #include "vfdlib.h"
32 #include "vfdver.h"
33 
34 #ifndef IOCTL_DISK_GET_LENGTH_INFO
35 //	Old winioctl.h header doesn't define the following
36 
37 #define IOCTL_DISK_GET_LENGTH_INFO			CTL_CODE(\
38 IOCTL_DISK_BASE, 0x0017, METHOD_BUFFERED, FILE_READ_ACCESS)
39 
40 typedef struct _GET_LENGTH_INFORMATION {
41 	LARGE_INTEGER	Length;
42 } GET_LENGTH_INFORMATION, *PGET_LENGTH_INFORMATION;
43 
44 #endif	// IOCTL_DISK_GET_LENGTH_INFO
45 
46 //
47 //	DOS device name (\\.\VirtualFD)
48 //
49 #ifndef __REACTOS__
50 #define VFD_DEVICE_TEMPLATE		"\\\\.\\" VFD_DEVICE_BASENAME "%u"
51 #else
52 #define VFD_DEVICE_TEMPLATE		"\\\\.\\" VFD_DEVICE_BASENAME "%lu"
53 #endif
54 #define VFD_VOLUME_TEMPLATE		"\\\\.\\%c:"
55 
56 #define VFD_INSTALL_DIRECTORY	"\\system32\\drivers\\"
57 
58 #ifdef _DEBUG
59 #ifndef __REACTOS__
60 extern ULONG TraceFlags = (ULONG)-1;//0;
61 extern CHAR *TraceFile	= NULL;
62 extern ULONG TraceLine	= 0;
63 #else
64 ULONG TraceFlags = (ULONG)-1;//0;
65 CHAR const * TraceFile	= NULL;
66 ULONG TraceLine	= 0;
67 #endif
68 #endif
69 
70 //
71 //	broadcast a WM_DEVICECHANGE system message to inform
72 //	a drive letter creation / removal
73 //
74 #define VFD_LINK_CREATED	0
75 #define VFD_LINK_REMOVED	1
76 
77 static void VfdBroadcastLink(
78 	CHAR			cLetter,
79 	BOOL			bRemoved)
80 {
81 	DWORD			receipients;
82 	DWORD			device_event;
83 	DEV_BROADCAST_VOLUME params;
84 
85 	if (!isalpha(cLetter)) {
86 		VFDTRACE(0,
87 			("VfdBroadcastLink: invalid parameter"))
88 		return;
89 	}
90 
91 	receipients = BSM_APPLICATIONS;
92 
93 	device_event = bRemoved ?
94 		DBT_DEVICEREMOVECOMPLETE : DBT_DEVICEARRIVAL;
95 
96 	ZeroMemory(&params, sizeof(params));
97 
98 	params.dbcv_size		= sizeof(params);
99 	params.dbcv_devicetype	= DBT_DEVTYP_VOLUME;
100 	params.dbcv_reserved	= 0;
101 	params.dbcv_unitmask	= (1 << (toupper(cLetter) - 'A'));
102 	params.dbcv_flags		= 0;
103 
104 	if (BroadcastSystemMessage(
105 		BSF_NOHANG | BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG,
106 		&receipients,
107 		WM_DEVICECHANGE,
108 		device_event,
109 		(LPARAM)&params) <= 0) {
110 
111 		VFDTRACE(0,
112 			("VfdBroadcastLink: BroadcastSystemMessage - %s",
113 			SystemMessage(GetLastError())));
114 	}
115 }
116 
117 //
118 //	Broadcast a VFD notify message
119 //
120 static __inline void VfdNotify(
121 	WPARAM			wParam,
122 	LPARAM			lParam)
123 {
124 	//	SendNotifyMessage causes volume locking conflict (I think)
125 	//	on Windows XP while closing an image with VfdWin
126 //	SendNotifyMessage(HWND_BROADCAST, uVfdMsg, wParam, lParam);
127 	PostMessage(HWND_BROADCAST, g_nNotifyMsg, wParam, lParam);
128 }
129 
130 #ifdef VFD_EMBED_DRIVER
131 //
132 //	Restore the VFD driver file in the system directory
133 //
134 
135 static DWORD VfdRestoreDriver(
136 	PCSTR			sPath)
137 {
138 #define FUNC		"VfdRestoreDriver"
139 	HRSRC			hRes;
140 	DWORD			size;
141 	HGLOBAL			hDrv;
142 	PVOID			pData;
143 	DWORD			result;
144 	HANDLE			hFile;
145 	DWORD			ret;
146 
147 	//
148 	//	Prepare driver binary
149 	//
150 
151 	// use embedded driver binary
152 
153 #define S(s) #s
154 	hRes = FindResource(g_hDllModule,
155 		S(VFD_DRIVER_NAME_ID), S(VFD_DRIVER_TYPE_ID));
156 #undef S
157 
158 	if (hRes == NULL) {
159 		ret = GetLastError();
160 
161 		VFDTRACE(0,
162 			(FUNC ": FindResource - %s",
163 			SystemMessage(ret)));
164 
165 		return ret;
166 	}
167 
168 	size = SizeofResource(g_hDllModule, hRes);
169 
170 	if (size == 0) {
171 		ret = GetLastError();
172 
173 		VFDTRACE(0,
174 			(FUNC ": SizeofResource - %s",
175 			SystemMessage(ret)));
176 
177 		return ret;
178 	}
179 
180 	hDrv = LoadResource(g_hDllModule, hRes);
181 
182 	if (hDrv == NULL) {
183 		ret = GetLastError();
184 
185 		VFDTRACE(0,
186 			(FUNC ": LoadResource - %s",
187 			SystemMessage(ret)));
188 
189 		return ret;
190 	}
191 
192 	pData = LockResource(hDrv);
193 
194 	if (pData == NULL) {
195 		ret = GetLastError();
196 
197 		VFDTRACE(0,
198 			(FUNC ": LockResource - %s",
199 			SystemMessage(ret)));
200 
201 		return ret;
202 	}
203 
204 	//	create the driver file
205 
206 	hFile = CreateFile(sPath, GENERIC_WRITE,
207 		0, NULL, OPEN_ALWAYS, 0, NULL);
208 
209 	if (hFile == INVALID_HANDLE_VALUE) {
210 		ret = GetLastError();
211 
212 		VFDTRACE(0,
213 			(FUNC ": CreateFile(%s) - %s",
214 			sPath, SystemMessage(ret)));
215 
216 		return ret;
217 	}
218 
219 	if (!WriteFile(hFile, pData, size, &result, NULL) ||
220 		size != result) {
221 		ret = GetLastError();
222 
223 		VFDTRACE(0,
224 			(FUNC ": CreateFile - %s",
225 			SystemMessage(ret)));
226 
227 		CloseHandle(hFile);
228 		return ret;
229 	}
230 
231 	SetEndOfFile(hFile);
232 	CloseHandle(hFile);
233 
234 	return ERROR_SUCCESS;
235 }
236 #endif	// VFD_EMBED_DRIVER
237 
238 //
239 //	Install the Virtual Floppy Driver
240 //
241 DWORD WINAPI VfdInstallDriver(
242 	PCSTR			sFileName,
243 	DWORD			nStart)
244 {
245 #undef	FUNC
246 #define FUNC		"VfdInstallDriver"
247 	SC_HANDLE		hScManager;				// Service Control Manager
248 	SC_HANDLE		hService = NULL;		// Service (= Driver)
249 #ifndef VFD_EMBED_DRIVER
250 	CHAR			file_path[MAX_PATH];
251 	PSTR			file_name;
252 #endif	//	VFD_EMBED_DRIVER
253 	CHAR			system_dir[MAX_PATH];
254 	PSTR			inst_path;
255 	DWORD			len;
256 	DWORD			ret = ERROR_SUCCESS;
257 
258 #ifdef __REACTOS__
259 	CHAR			full_file_path[MAX_PATH];
260 #endif
261 
262 	//	get SystemRoot directory path
263 
264 //	len = GetEnvironmentVariable(
265 //		"SystemRoot", system_dir, sizeof(system_dir));
266 	len = GetWindowsDirectory(system_dir, sizeof(system_dir));
267 
268 	if (len == 0 || len > sizeof(system_dir)) {
269 		VFDTRACE(0,
270 			(FUNC ": %%SystemRoot%% is empty or too long.\n"));
271 
272 		return ERROR_BAD_ENVIRONMENT;
273 	}
274 
275 	inst_path = &system_dir[len];
276 
277 #ifdef __REACTOS__
278 	strcpy(full_file_path, system_dir);
279 	strcat(full_file_path, VFD_INSTALL_DIRECTORY);
280 	strcat(full_file_path, VFD_DRIVER_FILENAME);
281 #endif
282 
283 #ifdef VFD_EMBED_DRIVER
284 	//
285 	//	use embedded driver file
286 	//
287 	strcpy(inst_path++, VFD_INSTALL_DIRECTORY VFD_DRIVER_FILENAME);
288 
289 	ret = VfdRestoreDriver(system_dir);
290 
291 	if (ret != ERROR_SUCCESS) {
292 		return ret;
293 	}
294 
295 #else	// VFD_EMBED_DRIVER
296 	//	Prepare driver binary's full path
297 
298 	if (sFileName == NULL || *sFileName == '\0') {
299 
300 		// default driver file is vfd.sys in the same directory as executable
301 
302 		len = GetModuleFileName(
303 			NULL, file_path, sizeof(file_path));
304 
305 		if (len == 0) {
306 			ret = GetLastError();
307 
308 			VFDTRACE(0,
309 				(FUNC ": GetModuleFileName - %s",
310 				SystemMessage(ret)));
311 
312 			return ret;
313 		}
314 
315 		//	search the last '\' character
316 
317 		while (len > 0 && file_path[len - 1] != '\\') {
318 			len --;
319 		}
320 
321 		//	supply the file name (vfd.sys)
322 
323 		file_name = &file_path[len];
324 		strcpy(file_name, VFD_DRIVER_FILENAME);
325 	}
326 	else {
327 
328 		//	ensure that tha path is an absolute full path
329 
330 		len = GetFullPathName(
331 			sFileName,
332 			sizeof(file_path),
333 			file_path,
334 			&file_name);
335 
336 		if (len == 0) {
337 			ret = GetLastError();
338 
339 			VFDTRACE(0,
340 				(FUNC ": GetFullPathName(%s) - %s\n",
341 				sFileName, SystemMessage(ret)));
342 
343 			return ret;
344 		}
345 
346 		if (GetFileAttributes(file_path) & FILE_ATTRIBUTE_DIRECTORY) {
347 			//	if the specified path is a directory,
348 			//	supply the file name (vfd.sys)
349 
350 			file_name = &file_path[len];
351 			strcpy(file_name++, "\\" VFD_DRIVER_FILENAME);
352 		}
353 	}
354 
355 #ifdef __REACTOS__
356 	//      Check install directory & file exist or use full_file_path
357 
358 	if (GetFileAttributesA(file_path) == INVALID_FILE_ATTRIBUTES) {
359 		strcpy(file_path, full_file_path);
360 	}
361 #endif
362 
363 	//	Check if the file is a valid Virtual Floppy driver
364 
365 	ret = VfdCheckDriverFile(file_path, NULL);
366 
367 	if (ret != ERROR_SUCCESS) {
368 		VFDTRACE(0,
369 			(FUNC ": VfdCheckDriverFile(%s)\n", file_path));
370 
371 		return ret;
372 	}
373 
374 	//	if the path is under the system directory, make it relative
375 	//	to the system directory
376 
377 	len = strlen(system_dir);
378 
379 	if (!_strnicmp(file_path, system_dir, len)) {
380 		inst_path = &file_path[len];
381 
382 		while (*inst_path == '\\') {
383 			inst_path++;
384 		}
385 	}
386 	else {
387 		inst_path = &file_path[0];
388 	}
389 #endif	//	VFD_EMBED_DRIVER
390 
391 	//	Connect to the Service Control Manager
392 
393 	hScManager = OpenSCManager(
394 		NULL,							// local machine
395 		NULL,							// local database
396 		SC_MANAGER_CREATE_SERVICE);		// access required
397 
398 	if (hScManager == NULL) {
399 		ret = GetLastError();
400 
401 		VFDTRACE(0,
402 			(FUNC ": OpenSCManager() - %s",
403 			SystemMessage(ret)));
404 
405 		goto cleanup;
406 	}
407 
408 	//	Create a new service object
409 
410 	hService = CreateService(
411 		hScManager,						// service control manager
412 		VFD_DEVICE_BASENAME,			// internal service name
413 		VFD_DEVICE_BASENAME,			// display name
414 		SERVICE_ALL_ACCESS,				// access mode
415 		SERVICE_KERNEL_DRIVER,			// service type
416 		nStart,							// service start type
417 		SERVICE_ERROR_NORMAL,			// start error sevirity
418 		inst_path,						// service image file path
419 		NULL,							// service group
420 		NULL,							// service tag
421 		NULL,							// service dependency
422 		NULL,							// use LocalSystem account
423 		NULL							// password for the account
424 	);
425 
426 	if (!hService) {
427 		// Failed to create a service object
428 		ret = GetLastError();
429 
430 		VFDTRACE(0,
431 			(FUNC ": CreateService() - %s",
432 			SystemMessage(ret)));
433 
434 		goto cleanup;
435 	}
436 
437 cleanup:
438 	//	Close the service object handle
439 
440 	if (hService) {
441 		CloseServiceHandle(hService);
442 	}
443 
444 	//	Close handle to the service control manager.
445 
446 	if (hScManager) {
447 		CloseServiceHandle(hScManager);
448 	}
449 
450 	if (ret == ERROR_SUCCESS) {
451 		//	Broadcast the successful operation
452 		VfdNotify(VFD_OPERATION_INSTALL, 0);
453 	}
454 #ifdef VFD_EMBED_DRIVER
455 	else {
456 		//	Delete the restored driver file
457 		DeleteFile(system_dir);
458 	}
459 #endif	// VFD_EMBED_DRIVER
460 
461 	return ret;
462 }
463 
464 //
465 //	Configure the Virtual Floppy Driver (change the start method)
466 //
467 
468 DWORD WINAPI VfdConfigDriver(
469 	DWORD			nStart)
470 {
471 #undef	FUNC
472 #define FUNC		"VfdConfigDriver"
473 	SC_HANDLE		hScManager;				// Service Control Manager
474 	SC_HANDLE		hService;				// Service (= Driver)
475 	DWORD			ret = ERROR_SUCCESS;
476 
477 	//	Connect to the Service Control Manager
478 
479 	hScManager = OpenSCManager(NULL, NULL, 0);
480 
481 	if (hScManager == NULL) {
482 		ret = GetLastError();
483 
484 		VFDTRACE(0,
485 			(FUNC ": OpenSCManager() - %s",
486 			SystemMessage(ret)));
487 
488 		return ret;
489 	}
490 
491 	//	Open the VFD driver entry in the service database
492 
493 	hService = OpenService(
494 		hScManager,						// Service control manager
495 		VFD_DEVICE_BASENAME,			// service name
496 		SERVICE_CHANGE_CONFIG);			// service access mode
497 
498 	if (hService == NULL) {
499 		ret = GetLastError();
500 
501 		VFDTRACE(0,
502 			(FUNC ": OpenService(SERVICE_CHANGE_CONFIG) - %s",
503 			SystemMessage(ret)));
504 
505 		goto cleanup;
506 	}
507 
508 	//	Change the start method of the VFD driver
509 
510 	if (!ChangeServiceConfig(
511 			hService,
512 			SERVICE_NO_CHANGE,
513 			nStart,
514 			SERVICE_NO_CHANGE,
515 			NULL,
516 			NULL,
517 			NULL,
518 			NULL,
519 			NULL,
520 			NULL,
521 			NULL)) {
522 
523 		ret = GetLastError();
524 
525 		VFDTRACE(0,
526 			(FUNC ": ChangeServiceConfig() - %s",
527 			SystemMessage(ret)));
528 
529 		goto cleanup;
530 	}
531 
532 cleanup:
533 	//	Close the service object handle
534 
535 	if (hService) {
536 		CloseServiceHandle(hService);
537 	}
538 
539 	//	Close handle to the service control manager.
540 
541 	if (hScManager) {
542 		CloseServiceHandle(hScManager);
543 	}
544 
545 	//	Broadcast the successful operation
546 
547 	if (ret == ERROR_SUCCESS) {
548 		VfdNotify(VFD_OPERATION_CONFIG, 0);
549 	}
550 
551 	return ret;
552 }
553 
554 //
555 //	Remove the Virtual Floppy Driver entry from the service database
556 //
557 DWORD WINAPI VfdRemoveDriver()
558 {
559 #undef	FUNC
560 #define FUNC		"VfdRemoveDriver"
561 	SC_HANDLE		hScManager;				// Service Control Manager
562 	SC_HANDLE		hService;				// Service (= Driver)
563 	CHAR			file_path[MAX_PATH];
564 	DWORD			ret = ERROR_SUCCESS;
565 
566 	//	Get the current driver path
567 
568 	ret = VfdGetDriverConfig(file_path, NULL);
569 
570 	if (ret != ERROR_SUCCESS) {
571 		return ret;
572 	}
573 
574 	//	Connect to the Service Control Manager
575 
576 	hScManager = OpenSCManager(NULL, NULL, 0);
577 
578 	if (hScManager == NULL) {
579 		ret = GetLastError();
580 
581 		VFDTRACE(0,
582 			(FUNC ": OpenSCManager() - %s",
583 			SystemMessage(ret)));
584 
585 		return ret;
586 	}
587 
588 	//	Open the VFD driver entry in the service database
589 
590 	hService = OpenService(
591 		hScManager,						// Service control manager
592 		VFD_DEVICE_BASENAME,			// service name
593 		DELETE);						// service access mode
594 
595 	if (hService == NULL) {
596 		ret = GetLastError();
597 
598 		VFDTRACE(0,
599 			(FUNC ": OpenService(DELETE) - %s",
600 			SystemMessage(ret)));
601 
602 		goto cleanup;
603 	}
604 
605 	//	Remove driver entry from registry
606 
607 	if (!DeleteService(hService)) {
608 		ret = GetLastError();
609 
610 		VFDTRACE(0,
611 			(FUNC ": DeleteService() - %s",
612 			SystemMessage(ret)));
613 
614 		goto cleanup;
615 	}
616 
617 cleanup:
618 	//	Close the service object handle
619 
620 	if (hService) {
621 		CloseServiceHandle(hService);
622 	}
623 
624 	//	Close handle to the service control manager.
625 
626 	if (hScManager) {
627 		CloseServiceHandle(hScManager);
628 	}
629 
630 	//	Broadcast the successful operation
631 
632 	if (ret == ERROR_SUCCESS) {
633 		VfdNotify(VFD_OPERATION_REMOVE, 0);
634 
635 #ifdef VFD_EMBED_DRIVER
636 		//	Remove the driver file
637 		DeleteFile(file_path);
638 #endif	//	VFD_EMBED_DRIVER
639 	}
640 
641 	return ret;
642 }
643 
644 //
645 //	Start the Virtual Floppy Driver
646 //
647 DWORD WINAPI VfdStartDriver(
648 	PDWORD			pState)
649 {
650 #undef	FUNC
651 #define FUNC		"VfdStartDriver"
652 	SC_HANDLE		hScManager;			// Service Control Manager
653 	SC_HANDLE		hService;			// Service (= Driver)
654 	SERVICE_STATUS	stat;
655 	DWORD			ret = ERROR_SUCCESS;
656 	HCURSOR			original;
657 	int				i;
658 
659 	if (pState) {
660 		*pState = 0;
661 	}
662 
663 	//	Connect to the Service Control Manager
664 
665 	hScManager = OpenSCManager(NULL, NULL, 0);
666 
667 	if (hScManager == NULL) {
668 		ret = GetLastError();
669 
670 		VFDTRACE(0,
671 			(FUNC ": OpenSCManager() - %s",
672 			SystemMessage(ret)));
673 
674 		return ret;
675 	}
676 
677 	//	show an hourglass cursor
678 
679 	original = SetCursor(LoadCursor(NULL, IDC_WAIT));
680 
681 	//	Open the VFD driver entry in the service database
682 
683 	hService = OpenService(
684 		hScManager,						// Service control manager
685 		VFD_DEVICE_BASENAME,			// service name
686 		SERVICE_START
687 		| SERVICE_QUERY_STATUS);		// service access mode
688 
689 	if (hService == NULL) {
690 		ret = GetLastError();
691 
692 		VFDTRACE(0,
693 			(FUNC ": OpenService(SERVICE_START) - %s",
694 			SystemMessage(ret)));
695 
696 		goto cleanup;
697 	}
698 
699 	//	Start the driver
700 
701 	if (!StartService(hService, 0, NULL)) {
702 		ret = GetLastError();
703 
704 		VFDTRACE(0,
705 			(FUNC ": StartService() - %s",
706 			SystemMessage(ret)));
707 
708 		goto cleanup;
709 	}
710 
711 	//	Wait until the driver is properly running
712 
713 	i = 0;
714 
715 	for (;;) {
716 		if (!QueryServiceStatus(hService, &stat)) {
717 			ret = GetLastError();
718 
719 			VFDTRACE(0,
720 				(FUNC ": QueryServiceStatus() - %s",
721 				SystemMessage(ret)));
722 
723 			break;
724 		}
725 
726 		if (stat.dwCurrentState == SERVICE_RUNNING || ++i == 5) {
727 			break;
728 		}
729 
730 		Sleep(1000);
731 	}
732 
733 	if (stat.dwCurrentState == SERVICE_RUNNING) {
734 
735 		//	Broadcast the successful operation
736 
737 		if (ret == ERROR_SUCCESS) {
738 			VfdNotify(VFD_OPERATION_START, 0);
739 		}
740 
741 		//	broadcast the arrival of VFD drives
742 		//	otherwise WinXP explorer doesn't recognize the VFD drives
743 
744 		for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
745 			HANDLE	hDevice;
746 			CHAR	letter = 0;
747 
748 			hDevice = VfdOpenDevice(i);
749 
750 			if (hDevice != INVALID_HANDLE_VALUE) {
751 
752 				VfdGetGlobalLink(hDevice, &letter);
753 
754 				CloseHandle(hDevice);
755 
756 				if (isalpha(letter)) {
757 					VfdBroadcastLink(letter, VFD_LINK_CREATED);
758 					VfdNotify(VFD_OPERATION_SETLINK, i);
759 				}
760 			}
761 			else {
762 				VFDTRACE(0,
763 					(FUNC ": VfdOpenDevice(%d) - %s",
764 					i, SystemMessage(GetLastError())));
765 			}
766 		}
767 	}
768 	else {
769 		//	somehow failed to start the driver
770 
771 		ret = ERROR_SERVICE_NOT_ACTIVE;
772 	}
773 
774 	if (pState) {
775 		*pState = stat.dwCurrentState;
776 	}
777 
778 cleanup:
779 	//	Close the service object handle
780 
781 	if (hService) {
782 		CloseServiceHandle(hService);
783 	}
784 
785 	//	Close handle to the service control manager.
786 
787 	if (hScManager) {
788 		CloseServiceHandle(hScManager);
789 	}
790 
791 	//	revert to the original cursor
792 
793 	SetCursor(original);
794 
795 	return ret;
796 }
797 
798 //
799 //	Stop the Virtual Floppy Driver
800 //
801 DWORD WINAPI VfdStopDriver(
802 	PDWORD			pState)
803 {
804 #undef	FUNC
805 #define FUNC		"VfdStopDriver"
806 	SC_HANDLE		hScManager;			// Service Control Manager
807 	SC_HANDLE		hService;			// Service (= Driver)
808 	SERVICE_STATUS	stat;
809 	CHAR			drive_letters[VFD_MAXIMUM_DEVICES];
810 	DWORD			ret = ERROR_SUCCESS;
811 	int				i;
812 	HCURSOR			original;
813 
814 	if (pState) {
815 		*pState = 0;
816 	}
817 
818 	//	Connect to the Service Control Manager
819 
820 	hScManager = OpenSCManager(NULL, NULL, 0);
821 
822 	if (hScManager == NULL) {
823 		ret = GetLastError();
824 
825 		VFDTRACE(0,
826 			(FUNC ": OpenSCManager() - %s",
827 			SystemMessage(ret)));
828 
829 		return ret;
830 	}
831 
832 	//	Show the hourglass cursor
833 
834 	original = SetCursor(LoadCursor(NULL, IDC_WAIT));
835 
836 	//	Open the VFD driver entry in the service database
837 
838 	hService = OpenService(
839 		hScManager,						// Service control manager
840 		VFD_DEVICE_BASENAME,			// service name
841 		SERVICE_STOP
842 		| SERVICE_QUERY_STATUS);		// service access mode
843 
844 	if (hService == NULL) {
845 		ret = GetLastError();
846 
847 		VFDTRACE(0,
848 			(FUNC ": OpenService(SERVICE_STOP) - %s",
849 			SystemMessage(ret)));
850 
851 		goto cleanup;
852 	}
853 
854 	//	Get assigned drive letters
855 
856 	for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
857 		HANDLE	hDevice;
858 		CHAR letter;
859 
860 		hDevice = VfdOpenDevice(i);
861 
862 		if (hDevice != INVALID_HANDLE_VALUE) {
863 
864 			//	remove all session local drive letters
865 
866 			while (VfdGetLocalLink(hDevice, &letter) == ERROR_SUCCESS &&
867 				isalpha(letter)) {
868 				VfdSetLocalLink(hDevice, 0);
869 			}
870 
871 			//	store existing persistent drive letters
872 
873 			VfdGetGlobalLink(hDevice, &drive_letters[i]);
874 
875 			CloseHandle(hDevice);
876 		}
877 		else {
878 			VFDTRACE(0,
879 				(FUNC ": VfdOpenDevice(%d) - %s",
880 				i, SystemMessage(GetLastError())));
881 		}
882 	}
883 
884 	//	Stop the driver
885 
886 	if (!ControlService(hService, SERVICE_CONTROL_STOP, &stat)) {
887 		ret = GetLastError();
888 
889 		VFDTRACE(0,
890 			(FUNC ": ControlService(SERVICE_CONTROL_STOP) - %s",
891 			SystemMessage(ret)));
892 
893 		goto cleanup;
894 	}
895 
896 	//	Wait until the driver is stopped
897 
898 	i = 0;
899 
900 	while (stat.dwCurrentState != SERVICE_STOPPED && ++i < 5) {
901 		Sleep(1000);
902 
903 		if (!QueryServiceStatus(hService, &stat)) {
904 			ret = GetLastError();
905 
906 			VFDTRACE(0,
907 				(FUNC ": QueryServiceStatus() - %s",
908 				SystemMessage(ret)));
909 
910 			break;
911 		}
912 	}
913 
914 	if (stat.dwCurrentState != SERVICE_RUNNING) {
915 
916 		//	broadcast the removal of persistent drive letters
917 
918 		for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
919 			if (isalpha(drive_letters[i])) {
920 				VfdBroadcastLink(drive_letters[i], VFD_LINK_REMOVED);
921 				VfdNotify(VFD_OPERATION_DELLINK, i);
922 			}
923 		}
924 	}
925 
926 	if (pState) {
927 		*pState = stat.dwCurrentState;
928 	}
929 
930 cleanup:
931 	//	Close the service object handle
932 
933 	if (hService) {
934 		CloseServiceHandle(hService);
935 	}
936 
937 	//	Close handle to the service control manager.
938 
939 	if (hScManager) {
940 		CloseServiceHandle(hScManager);
941 	}
942 
943 	//	Broadcast the successful operation
944 
945 	if (ret == ERROR_SUCCESS) {
946 		VfdNotify(VFD_OPERATION_STOP, 0);
947 	}
948 
949 	//	revert to the original cursor
950 
951 	SetCursor(original);
952 
953 	return ret;
954 }
955 
956 //
957 //	Get the Virtual Floppy Driver configuration
958 //
959 DWORD WINAPI VfdGetDriverConfig(
960 	PSTR			sFileName,
961 	PDWORD			pStart)
962 {
963 #undef	FUNC
964 #define FUNC		"VfdGetDriverConfig"
965 	SC_HANDLE		hScManager;				// Service Control Manager
966 	SC_HANDLE		hService;				// Service (= Driver)
967 	LPQUERY_SERVICE_CONFIG config = NULL;
968 	DWORD			result;
969 	DWORD			ret = ERROR_SUCCESS;
970 
971 	if (sFileName) {
972 		ZeroMemory(sFileName, MAX_PATH);
973 	}
974 
975 	if (pStart) {
976 		*pStart = 0;
977 	}
978 
979 	//	Connect to the Service Control Manager
980 
981 	hScManager = OpenSCManager(NULL, NULL, 0);
982 
983 	if (hScManager == NULL) {
984 		ret = GetLastError();
985 
986 		VFDTRACE(0,
987 			(FUNC ": OpenSCManager() - %s", SystemMessage(ret)));
988 
989 		return ret;
990 	}
991 
992 	//	Open the VFD driver entry in the service database
993 
994 	hService = OpenService(
995 		hScManager,						// Service control manager
996 		VFD_DEVICE_BASENAME,			// service name
997 		SERVICE_QUERY_CONFIG);			// service access mode
998 
999 	if (hService == NULL) {
1000 		ret = GetLastError();
1001 
1002 		VFDTRACE(0,
1003 			(FUNC ": OpenService(SERVICE_QUERY_CONFIG) - %s",
1004 			SystemMessage(ret)));
1005 
1006 		goto cleanup;
1007 	}
1008 
1009 	//	Get the length of config information
1010 
1011 	if (!QueryServiceConfig(hService, NULL, 0, &result)) {
1012 		ret = GetLastError();
1013 
1014 		if (ret == ERROR_INSUFFICIENT_BUFFER) {
1015 			ret = ERROR_SUCCESS;
1016 		}
1017 		else {
1018 			VFDTRACE(0,
1019 				(FUNC ": QueryServiceConfig() - %s",
1020 				SystemMessage(ret)));
1021 
1022 			goto cleanup;
1023 		}
1024 	}
1025 
1026 	//	allocate a required buffer
1027 
1028 	config = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, result);
1029 
1030 	if (config == NULL) {
1031 		ret = GetLastError();
1032 
1033 		VFDTRACE(0,
1034 			(FUNC ": LocalAlloc(%lu) - %s\n",
1035 			result, SystemMessage(ret)));
1036 
1037 		goto cleanup;
1038 	}
1039 
1040 	//	get the config information
1041 
1042 	if (!QueryServiceConfig(hService, config, result, &result)) {
1043 		ret = GetLastError();
1044 
1045 		VFDTRACE(0,
1046 			(FUNC ": QueryServiceConfig() - %s",
1047 			SystemMessage(ret)));
1048 
1049 		goto cleanup;
1050 	}
1051 
1052 	//	copy information to output buffer
1053 
1054 	if (sFileName) {
1055 		if (strncmp(config->lpBinaryPathName, "\\??\\", 4) == 0) {
1056 
1057 			//	driver path is an absolute UNC path
1058 			strncpy(
1059 				sFileName,
1060 				config->lpBinaryPathName + 4,
1061 				MAX_PATH);
1062 		}
1063 		else if (config->lpBinaryPathName[0] == '\\' ||
1064 			(isalpha(config->lpBinaryPathName[0]) &&
1065 			config->lpBinaryPathName[1] == ':')) {
1066 
1067 			//	driver path is an absolute path
1068 			strncpy(sFileName,
1069 				config->lpBinaryPathName,
1070 				MAX_PATH);
1071 		}
1072 		else {
1073 			//	driver path is relative to the SystemRoot
1074 //			DWORD len = GetEnvironmentVariable(
1075 //				"SystemRoot", sFileName, MAX_PATH);
1076 
1077 			DWORD len = GetWindowsDirectory(sFileName, MAX_PATH);
1078 
1079 			if (len == 0 || len > MAX_PATH) {
1080 				VFDTRACE(0,
1081 					(FUNC ": %%SystemRoot%% is empty or too long.\n"));
1082 
1083 				ret = ERROR_BAD_ENVIRONMENT;
1084 				goto cleanup;
1085 			}
1086 
1087 			sprintf((sFileName + len), "\\%s",
1088 				config->lpBinaryPathName);
1089 		}
1090 	}
1091 
1092 	if (pStart) {
1093 		*pStart = config->dwStartType;
1094 	}
1095 
1096 cleanup:
1097 	//	Free service config buffer
1098 
1099 	if (config) {
1100 		LocalFree(config);
1101 	}
1102 
1103 	//	Close the service object handle
1104 
1105 	if (hService) {
1106 		CloseServiceHandle(hService);
1107 	}
1108 
1109 	//	Close handle to the service control manager.
1110 
1111 	if (hScManager) {
1112 		CloseServiceHandle(hScManager);
1113 	}
1114 
1115 	return ret;
1116 }
1117 
1118 //
1119 //	Get the Virtual Floppy Driver running state
1120 //
1121 DWORD WINAPI VfdGetDriverState(
1122 	PDWORD			pState)
1123 {
1124 #undef	FUNC
1125 #define FUNC		"VfdGetDriverState"
1126 	SC_HANDLE		hScManager = NULL;	// Service Control Manager
1127 	SC_HANDLE		hService = NULL;	// Service (= Driver)
1128 	SERVICE_STATUS	status;
1129 	DWORD			ret = ERROR_SUCCESS;
1130 
1131 	if (pState) {
1132 		*pState = 0;
1133 	}
1134 
1135 	//	Connect to the Service Control Manager
1136 
1137 	hScManager = OpenSCManager(NULL, NULL, 0);
1138 
1139 	if (hScManager == NULL) {
1140 		ret = GetLastError();
1141 
1142 		VFDTRACE(0,
1143 			(FUNC ": OpenSCManager() - %s",
1144 			SystemMessage(ret)));
1145 
1146 		return ret;
1147 	}
1148 
1149 	//	Open the VFD driver entry in the service database
1150 
1151 	hService = OpenService(
1152 		hScManager,						// Service control manager
1153 		VFD_DEVICE_BASENAME,			// service name
1154 		SERVICE_QUERY_STATUS);			// service access mode
1155 
1156 	if (hService == NULL) {
1157 
1158 		ret = GetLastError();
1159 
1160 		if (ret == ERROR_SERVICE_DOES_NOT_EXIST) {
1161 
1162 			if (pState) {
1163 				*pState = VFD_NOT_INSTALLED;
1164 			}
1165 
1166 			ret = ERROR_SUCCESS;
1167 		}
1168 		else {
1169 			VFDTRACE(0,
1170 				(FUNC ": OpenService(SERVICE_QUERY_STATUS) - %s",
1171 				SystemMessage(ret)));
1172 		}
1173 
1174 		goto cleanup;
1175 	}
1176 
1177 	//	Get current driver status
1178 
1179 	ZeroMemory(&status, sizeof(status));
1180 
1181 	if (!QueryServiceStatus(hService, &status)) {
1182 		ret = GetLastError();
1183 
1184 		VFDTRACE(0,
1185 			(FUNC ": QueryServiceStatus() - %s",
1186 			SystemMessage(ret)));
1187 
1188 		goto cleanup;
1189 	}
1190 
1191 	if (pState) {
1192 		*pState = status.dwCurrentState;
1193 	}
1194 
1195 cleanup:
1196 	//	Close the service object handle
1197 
1198 	if (hService) {
1199 		CloseServiceHandle(hService);
1200 	}
1201 
1202 	//	Close handle to the service control manager.
1203 
1204 	if (hScManager) {
1205 		CloseServiceHandle(hScManager);
1206 	}
1207 
1208 	return ret;
1209 }
1210 
1211 //
1212 //	open a Virtual Floppy drive without showing the "Insert Floppy"
1213 //	dialog when the drive is empty.
1214 //
1215 HANDLE WINAPI VfdOpenDevice(
1216 	ULONG			nTarget)		// either a drive letter or a device number
1217 {
1218 #undef	FUNC
1219 #define FUNC		"VfdOpenDevice"
1220 	CHAR			dev_name[20];
1221 	UINT			err_mode;
1222 	HANDLE			hDevice;
1223 
1224 	//	format a device name string
1225 
1226 	if (isalpha(nTarget)) {
1227 		//	nTarget is a drive letter
1228 		//	\\.\<x>:
1229 #ifndef __REACTOS__
1230 		sprintf(dev_name, VFD_VOLUME_TEMPLATE, nTarget);
1231 #else
1232 		sprintf(dev_name, VFD_VOLUME_TEMPLATE, (CHAR)nTarget);
1233 #endif
1234 	}
1235 	else if (isdigit(nTarget)) {
1236 		//	nTarget is a device number in character
1237 		//	\\.\VirtualFD<n>
1238 		sprintf(dev_name, VFD_DEVICE_TEMPLATE, nTarget - '0');
1239 	}
1240 	else {
1241 		//	nTarget is a device number value
1242 		//	\\.\VirtualFD<n>
1243 		sprintf(dev_name, VFD_DEVICE_TEMPLATE, nTarget);
1244 	}
1245 
1246 	// change error mode in order to avoid "Insert Floppy" dialog
1247 
1248 	err_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
1249 
1250 	//	open the target drive
1251 
1252 	hDevice = CreateFile(
1253 		dev_name,
1254 		GENERIC_READ | GENERIC_WRITE,
1255 		FILE_SHARE_READ | FILE_SHARE_WRITE,
1256 		NULL,
1257 		OPEN_EXISTING,
1258 		FILE_FLAG_NO_BUFFERING,
1259 		NULL);
1260 
1261 	//	revert to the original error mode
1262 
1263 	SetErrorMode(err_mode);
1264 
1265 	if (hDevice != INVALID_HANDLE_VALUE) {
1266 
1267 		//	check if the target is a valid VFD drive
1268 
1269 		ULONG version;
1270 
1271 		if (VfdGetDriverVersion(hDevice, &version) != ERROR_SUCCESS) {
1272 
1273 			//	Failed to get the driver version
1274 
1275 			CloseHandle(hDevice);
1276 			hDevice = INVALID_HANDLE_VALUE;
1277 		}
1278 		else if ((version & ~0x80000000) !=
1279 			MAKELONG(VFD_DRIVER_MINOR, VFD_DRIVER_MAJOR)) {
1280 
1281 			//	the driver version mismatch
1282 
1283 //			CloseHandle(hDevice);
1284 //			hDevice = INVALID_HANDLE_VALUE;
1285 
1286 			SetLastError(ERROR_REVISION_MISMATCH);
1287 		}
1288 	}
1289 	else {
1290 		VFDTRACE(0,(
1291 			"CreateFile(%s) - %s", dev_name,
1292 			SystemMessage(GetLastError())));;
1293 	}
1294 
1295 	return hDevice;
1296 }
1297 
1298 //
1299 //	Open a Virtual Floppy Image
1300 //
1301 DWORD WINAPI VfdOpenImage(
1302 	HANDLE			hDevice,
1303 	PCSTR			sFileName,
1304 	VFD_DISKTYPE	nDiskType,
1305 	VFD_MEDIA		nMediaType,
1306 	VFD_FLAGS		nMediaFlags)
1307 {
1308 #undef	FUNC
1309 #define FUNC		"VfdOpenImage"
1310 	PCSTR			prefix;
1311 	CHAR			abspath[MAX_PATH];
1312 	DWORD			name_len;
1313 	DWORD			result;
1314 	DWORD			ret = ERROR_SUCCESS;
1315 
1316 	PVFD_IMAGE_INFO	image_info = NULL;
1317 	PUCHAR			image_buf = NULL;
1318 	ULONG			image_size;
1319 	VFD_FILETYPE	file_type;
1320 
1321 	//
1322 	//	Check parameters
1323 	//
1324 
1325 	if (hDevice == NULL ||
1326 		hDevice == INVALID_HANDLE_VALUE) {
1327 		return ERROR_INVALID_HANDLE;
1328 	}
1329 
1330 	if (nMediaType == VFD_MEDIA_NONE ||
1331 		nMediaType >= VFD_MEDIA_MAX) {
1332 
1333 		VFDTRACE(0,
1334 			(FUNC ": Invalid MediaType - %u\n", nMediaType));
1335 
1336 		return ERROR_INVALID_PARAMETER;
1337 	}
1338 
1339 
1340 	if (sFileName && *sFileName) {
1341 
1342 		//	check file contents and attributes
1343 
1344 		HANDLE hFile = CreateFile(sFileName, GENERIC_READ,
1345 			FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1346 
1347 		if (hFile == INVALID_HANDLE_VALUE) {
1348 			ret = GetLastError();
1349 
1350 			VFDTRACE(0,
1351 				(FUNC ": CreateFile(%s) - %s",
1352 				sFileName, SystemMessage(ret)));
1353 
1354 			return ret;
1355 		}
1356 
1357 		//	try extracting image data from zip compressed file
1358 
1359 		ExtractZipImage(hFile, &image_buf, &image_size);
1360 
1361 		if (image_buf) {
1362 
1363 			file_type = VFD_FILETYPE_ZIP;
1364 
1365 			//	imz file must be opened in RAM mode
1366 
1367 			if (nDiskType == VFD_DISKTYPE_FILE) {
1368 
1369 				VFDTRACE(0,
1370 					(FUNC ": %s is a zip compressed file",
1371 					sFileName));
1372 
1373 				CloseHandle(hFile);
1374 				ret = ERROR_INVALID_PARAMETER;
1375 
1376 				goto exit_func;
1377 			}
1378 		}
1379 		else {
1380 
1381 			file_type = VFD_FILETYPE_RAW;
1382 
1383 			if (nDiskType == VFD_DISKTYPE_FILE) {
1384 
1385 				//	direct image file must not be compressed or encrypted
1386 
1387 				BY_HANDLE_FILE_INFORMATION info;
1388 
1389 				if (!GetFileInformationByHandle(hFile, &info)) {
1390 					ret = GetLastError();
1391 
1392 					VFDTRACE(0,
1393 						(FUNC ": GetFileInformationByHandle - %s",
1394 						SystemMessage(ret)));
1395 
1396 					CloseHandle(hFile);
1397 
1398 					return ret;
1399 				}
1400 
1401 				if (info.dwFileAttributes &
1402 					(FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED)) {
1403 
1404 					VFDTRACE(0,
1405 						(FUNC ": file is compressed/encrypted"));
1406 
1407 					CloseHandle(hFile);
1408 
1409 					return ERROR_FILE_ENCRYPTED;
1410 				}
1411 
1412 				image_size	= info.nFileSizeLow;
1413 			}
1414 			else {
1415 
1416 				// prepare image data for a file based RAM disk
1417 
1418 				image_size = GetFileSize(hFile, NULL);
1419 
1420 				if (image_size == 0 || image_size == INVALID_FILE_SIZE) {
1421 					ret = GetLastError();
1422 
1423 					VFDTRACE(0,
1424 						(FUNC ": GetFileSize - %s",
1425 						SystemMessage(ret)));
1426 
1427 					CloseHandle(hFile);
1428 
1429 					return ret;
1430 				}
1431 
1432 				image_buf = (PUCHAR)LocalAlloc(LPTR, image_size);
1433 
1434 				if (image_buf == NULL) {
1435 					ret = GetLastError();
1436 
1437 					VFDTRACE(0,
1438 						(FUNC ": LocalAlloc - %s",
1439 						SystemMessage(ret)));
1440 
1441 					CloseHandle(hFile);
1442 
1443 					return ret;
1444 				}
1445 
1446 				if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
1447 					ret = GetLastError();
1448 
1449 					VFDTRACE(0,
1450 						(FUNC ": SetFilePointer - %s",
1451 						SystemMessage(ret)));
1452 
1453 					CloseHandle(hFile);
1454 
1455 					goto exit_func;
1456 				}
1457 
1458 				if (!ReadFile(hFile, image_buf, image_size, &result, NULL) ||
1459 					image_size != result) {
1460 
1461 					ret = GetLastError();
1462 
1463 					VFDTRACE(0,
1464 						(FUNC ": ReadFile - %s",
1465 						SystemMessage(ret)));
1466 
1467 					CloseHandle(hFile);
1468 
1469 					goto exit_func;
1470 				}
1471 			}
1472 		}
1473 
1474 		CloseHandle(hFile);
1475 
1476 		//	Prepare absolute path in the kernel namespace
1477 
1478 		if (*sFileName == '\\' && *(sFileName + 1) == '\\') {
1479 
1480 			// \\server\share\path\floppy.img
1481 
1482 			prefix = "\\??\\UNC";
1483 			sFileName++;			// drip the first '\'
1484 		}
1485 		else {
1486 
1487 			//	local path
1488 
1489 			PSTR file_part;
1490 
1491 			if (GetFullPathName(sFileName,
1492 				sizeof(abspath), abspath, &file_part) == 0) {
1493 
1494 				ret =  GetLastError();
1495 
1496 				VFDTRACE(0,
1497 					(FUNC ": GetFullPathName(%s) - %s\n",
1498 					sFileName, SystemMessage(ret)));
1499 
1500 				goto exit_func;
1501 			}
1502 
1503 			prefix = "\\??\\";
1504 			sFileName = abspath;
1505 		}
1506 
1507 		name_len = strlen(prefix) + strlen(sFileName);
1508 	}
1509 	else {
1510 
1511 		//	filename is not specified -- pure RAM disk
1512 
1513 		nDiskType = VFD_DISKTYPE_RAM;
1514 		file_type = VFD_FILETYPE_NONE;
1515 
1516 		prefix = NULL;
1517 		name_len = 0;
1518 
1519 		//	prepare a FAT formatted RAM image
1520 
1521 		image_size = VfdGetMediaSize(nMediaType);
1522 
1523 		image_buf = (PUCHAR)LocalAlloc(LPTR, image_size);
1524 
1525 		if (image_buf == NULL) {
1526 			ret = GetLastError();
1527 
1528 			VFDTRACE(0,
1529 				(FUNC ": LocalAlloc - %s",
1530 				SystemMessage(ret)));
1531 
1532 			return ret;
1533 		}
1534 
1535 		FormatBufferFat(image_buf, VFD_BYTE_TO_SECTOR(image_size));
1536 	}
1537 
1538 	if (image_size < VfdGetMediaSize(nMediaType)) {
1539 
1540 		//	image is too small for the specified media type
1541 
1542 		VFDTRACE(0,
1543 			(FUNC ": Image is too small for the specified media type\n"));
1544 
1545 		ret = ERROR_INVALID_PARAMETER;
1546 		goto exit_func;
1547 	}
1548 
1549 	//	prepare VFD_IMAGE_INFO structure
1550 
1551 	image_info = (PVFD_IMAGE_INFO)LocalAlloc(LPTR,
1552 		sizeof(VFD_IMAGE_INFO) + name_len + 1);
1553 
1554 	if (image_info == NULL) {
1555 		ret = GetLastError();
1556 
1557 		VFDTRACE(0,
1558 			(FUNC ": LocalAlloc(%lu) - %s\n",
1559 			sizeof(VFD_IMAGE_INFO) + name_len + 1,
1560 			SystemMessage(ret)));
1561 
1562 		goto exit_func;
1563 	}
1564 
1565 	ZeroMemory(image_info,
1566 		sizeof(VFD_IMAGE_INFO) + name_len + 1);
1567 
1568 	if (name_len) {
1569 		sprintf(image_info->FileName,
1570 			"%s%s", prefix, sFileName);
1571 	}
1572 
1573 	image_info->NameLength	= (USHORT)name_len;
1574 
1575 	image_info->DiskType	= nDiskType;
1576 	image_info->MediaType	= nMediaType;
1577 	image_info->MediaFlags	= nMediaFlags;
1578 	image_info->FileType	= file_type;
1579 	image_info->ImageSize	= image_size;
1580 
1581 	if (nDiskType != VFD_DISKTYPE_FILE) {
1582 		//	protect flag for a RAM disk is set after
1583 		//	initializing the image buffer
1584 		image_info->MediaFlags &= ~VFD_FLAG_WRITE_PROTECTED;
1585 	}
1586 
1587 	VFDTRACE(0,
1588 		(FUNC ": Opening file \"%s\" (%lu bytes) %s %s %s %s\n",
1589 		name_len ? image_info->FileName : "<RAM>",
1590 		image_info->ImageSize,
1591 		(file_type == VFD_FILETYPE_ZIP) ? "ZIP image" : "RAW image",
1592 		VfdMediaTypeName(nMediaType),
1593 		(nDiskType == VFD_DISKTYPE_FILE) ? "FILE disk" : "RAM disk",
1594 		(nMediaFlags & VFD_FLAG_WRITE_PROTECTED) ? "Protected" : "Writable"));
1595 
1596 	//	Open the image file / create a ram disk
1597 
1598 	if (!DeviceIoControl(
1599 		hDevice,
1600 		IOCTL_VFD_OPEN_IMAGE,
1601 		image_info,
1602 		sizeof(VFD_IMAGE_INFO) + name_len,
1603 		NULL,
1604 		0,
1605 		&result,
1606 		NULL))
1607 	{
1608 		ret = GetLastError();
1609 
1610 		VFDTRACE(0,
1611 			(FUNC ": DeviceIoControl(IOCTL_VFD_OPEN_FILE) - %s",
1612 			SystemMessage(ret)));
1613 
1614 		goto exit_func;
1615 	}
1616 
1617 	//	initialize the RAM disk image
1618 
1619 	if (nDiskType != VFD_DISKTYPE_FILE) {
1620 
1621 		image_size &= ~VFD_SECTOR_ALIGN_MASK;
1622 
1623 		if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
1624 			ret = GetLastError();
1625 
1626 			VFDTRACE(0,
1627 				(FUNC ": SetFilePointer - %s",
1628 				SystemMessage(ret)));
1629 
1630 			goto exit_func;
1631 		}
1632 
1633 		if (!WriteFile(hDevice, image_buf, image_size, &result, NULL) ||
1634 			image_size != result) {
1635 
1636 			ret = GetLastError();
1637 
1638 			VFDTRACE(0,
1639 				(FUNC ": WriteFile - %s",
1640 				SystemMessage(ret)));
1641 
1642 			goto exit_func;
1643 		}
1644 
1645 		if (nMediaFlags & VFD_FLAG_WRITE_PROTECTED) {
1646 			VfdWriteProtect(hDevice, TRUE);
1647 		}
1648 
1649 		if (!DeviceIoControl(
1650 			hDevice,
1651 			IOCTL_VFD_RESET_MODIFY,
1652 			NULL,
1653 			0,
1654 			NULL,
1655 			0,
1656 			&result,
1657 			NULL))
1658 		{
1659 			VFDTRACE(0,
1660 				(FUNC ": DeviceIoControl(IOCTL_VFD_RESET_MODIFY) - %s",
1661 				SystemMessage(GetLastError())));
1662 		}
1663 	}
1664 
1665 	//	Broadcast the successful operation
1666 
1667 	if (ret == ERROR_SUCCESS) {
1668 		ULONG	number;
1669 		CHAR	root[] = "A:\\";
1670 
1671 		if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
1672 			VfdNotify(VFD_OPERATION_OPEN, number);
1673 		}
1674 
1675 		VfdGetGlobalLink(hDevice, &root[0]);
1676 
1677 		if (isalpha(root[0])) {
1678 			SHChangeNotify(SHCNE_MEDIAINSERTED, SHCNF_PATH, root, NULL);
1679 		}
1680 
1681 		while (VfdGetLocalLink(hDevice, &root[0]) == ERROR_SUCCESS &&
1682 			isalpha(root[0])) {
1683 			SHChangeNotify(SHCNE_MEDIAINSERTED, SHCNF_PATH, root, NULL);
1684 		}
1685 	}
1686 
1687 exit_func:
1688 	if (image_info) {
1689 		LocalFree(image_info);
1690 	}
1691 
1692 	if (image_buf) {
1693 		LocalFree(image_buf);
1694 	}
1695 
1696 	return ret;
1697 }
1698 
1699 //
1700 //	Close the virtual floppy Image
1701 //
1702 DWORD WINAPI VfdCloseImage(
1703 	HANDLE			hDevice,
1704 	BOOL			bForce)
1705 {
1706 #undef	FUNC
1707 #define FUNC		"VfdCloseImage"
1708 	DWORD			result;
1709 	DWORD			ret = ERROR_SUCCESS;
1710 	int				retry = 0;
1711 
1712 lock_retry:
1713 	if (!DeviceIoControl(
1714 		hDevice,
1715 		FSCTL_LOCK_VOLUME,
1716 		NULL,
1717 		0,
1718 		NULL,
1719 		0,
1720 		&result,
1721 		NULL))
1722 	{
1723 		ret = GetLastError();
1724 
1725 		VFDTRACE(0,
1726 			(FUNC ": DeviceIoControl(FSCTL_LOCK_VOLUME) - %s",
1727 			SystemMessage(ret)));
1728 
1729 		if (ret != ERROR_ACCESS_DENIED || retry == 5) {
1730 			//	error other than access denied or
1731 			//	operation kept failing for 5 seconds
1732 			return ret;
1733 		}
1734 
1735 		if (!bForce) {
1736 			//	error is access denied and
1737 			//	the force flag is not set
1738 
1739 			if (retry == 0) {
1740 
1741 				//	send the MEDIAREMOVED notification to the shell and
1742 				//	see if the shell releases the target drive
1743 
1744 				CHAR root[] = "A:\\";
1745 
1746 				VfdGetGlobalLink(hDevice, &root[0]);
1747 
1748 				if (isalpha(root[0])) {
1749 					SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATH, root, NULL);
1750 				}
1751 
1752 				while (VfdGetLocalLink(hDevice, &root[0]) == ERROR_SUCCESS &&
1753 					isalpha(root[0])) {
1754 					SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATH, root, NULL);
1755 				}
1756 			}
1757 
1758 			Sleep(1000);
1759 			retry++;
1760 
1761 			goto lock_retry;
1762 		}
1763 	}
1764 
1765 	ret = ERROR_SUCCESS;
1766 
1767 	if (!DeviceIoControl(
1768 		hDevice,
1769 		FSCTL_DISMOUNT_VOLUME,
1770 		NULL,
1771 		0,
1772 		NULL,
1773 		0,
1774 		&result,
1775 		NULL))
1776 	{
1777 		ret = GetLastError();
1778 
1779 		VFDTRACE(0,
1780 			(FUNC ": DeviceIoControl(FSCTL_DISMOUNT_VOLUME) - %s",
1781 			SystemMessage(ret)));
1782 
1783 		return ret;
1784 	}
1785 
1786 	if (!DeviceIoControl(
1787 		hDevice,
1788 		IOCTL_VFD_CLOSE_IMAGE,
1789 		NULL,
1790 		0,
1791 		NULL,
1792 		0,
1793 		&result,
1794 		NULL))
1795 	{
1796 		ret = GetLastError();
1797 
1798 		if (ret != ERROR_NOT_READY) {
1799 			VFDTRACE(0,
1800 				(FUNC ": DeviceIoControl(IOCTL_VFD_CLOSE_FILE) - %s",
1801 				SystemMessage(ret)));
1802 		}
1803 
1804 		return ret;
1805 	}
1806 
1807 	if (!DeviceIoControl(
1808 		hDevice,
1809 		FSCTL_UNLOCK_VOLUME,
1810 		NULL,
1811 		0,
1812 		NULL,
1813 		0,
1814 		&result,
1815 		NULL))
1816 	{
1817 		//	This should not be fatal because the volume is unlocked
1818 		//	when the handle is closed anyway
1819 		VFDTRACE(0,
1820 			(FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
1821 			SystemMessage(GetLastError())));
1822 	}
1823 
1824 	//	Broadcast the successful operation
1825 	if (ret == ERROR_SUCCESS) {
1826 		ULONG	number;
1827 
1828 		if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
1829 			VfdNotify(VFD_OPERATION_CLOSE, number);
1830 		}
1831 	}
1832 
1833 	return ret;
1834 }
1835 
1836 //
1837 //	Get Virtual Floppy image info
1838 //
1839 DWORD WINAPI VfdGetImageInfo(
1840 	HANDLE			hDevice,
1841 	PSTR			sFileName,
1842 	PVFD_DISKTYPE	pDiskType,
1843 	PVFD_MEDIA		pMediaType,
1844 	PVFD_FLAGS		pMediaFlags,
1845 	PVFD_FILETYPE	pFileType,
1846 	PULONG			pImageSize)
1847 {
1848 #undef	FUNC
1849 #define FUNC		"VfdGetImageInfo"
1850 	PVFD_IMAGE_INFO	image_info;
1851 	DWORD			result;
1852 	DWORD			ret = ERROR_SUCCESS;
1853 
1854 	image_info = (PVFD_IMAGE_INFO)LocalAlloc(
1855 		LPTR, sizeof(VFD_IMAGE_INFO) + MAX_PATH);
1856 
1857 	if (image_info == NULL) {
1858 		ret = GetLastError();
1859 
1860 		VFDTRACE(0,
1861 			(FUNC ": LocalAlloc(%lu) - %s\n",
1862 			sizeof(VFD_IMAGE_INFO) + MAX_PATH, SystemMessage(ret)));
1863 
1864 		return ret;
1865 	}
1866 
1867 	ZeroMemory(image_info, sizeof(VFD_IMAGE_INFO) + MAX_PATH);
1868 
1869 	//	Query file information
1870 
1871 	if (!DeviceIoControl(
1872 		hDevice,
1873 		IOCTL_VFD_QUERY_IMAGE,
1874 		NULL,
1875 		0,
1876 		image_info,
1877 		sizeof(VFD_IMAGE_INFO) + MAX_PATH,
1878 		&result,
1879 		NULL))
1880 	{
1881 		ret = GetLastError();
1882 
1883 		if (ret != ERROR_MORE_DATA) {
1884 			VFDTRACE(0,
1885 				(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_FILE) - %s",
1886 				SystemMessage(ret)));
1887 
1888 			goto cleanup;
1889 		}
1890 	}
1891 
1892 	//	copy obtained information to output buffer
1893 
1894 	if (sFileName) {
1895 
1896 		//	if filename is too long, clip it
1897 
1898 		if (image_info->NameLength >= MAX_PATH) {
1899 			image_info->NameLength = MAX_PATH - 1;
1900 		}
1901 
1902 		// ensure the name is properly terminated
1903 
1904 		image_info->FileName[image_info->NameLength] = '\0';
1905 
1906 		if (strncmp(image_info->FileName, "\\??\\UNC", 7) == 0) {
1907 			*sFileName = '\\';
1908 			strcpy(sFileName + 1, image_info->FileName + 7);
1909 		}
1910 		else if (strncmp(image_info->FileName, "\\??\\", 4) == 0) {
1911 			strcpy(sFileName, image_info->FileName + 4);
1912 		}
1913 		else {
1914 			strcpy(sFileName, image_info->FileName);
1915 		}
1916 	}
1917 
1918 	if (pDiskType) {
1919 		*pDiskType = image_info->DiskType;
1920 	}
1921 
1922 	if (pMediaType) {
1923 		*pMediaType = image_info->MediaType;
1924 	}
1925 
1926 	if (pMediaFlags) {
1927 		*pMediaFlags = image_info->MediaFlags;
1928 	}
1929 
1930 	if (pFileType) {
1931 		*pFileType = image_info->FileType;
1932 	}
1933 
1934 	if (pImageSize) {
1935 		*pImageSize = image_info->ImageSize;
1936 	}
1937 
1938 cleanup:
1939 	if (image_info) {
1940 		LocalFree(image_info);
1941 	}
1942 
1943 	return ret;
1944 }
1945 
1946 //
1947 //	Get current media state (opened / write protected)
1948 //
1949 DWORD WINAPI VfdGetMediaState(
1950 	HANDLE			hDevice)
1951 {
1952 #undef	FUNC
1953 #define FUNC		"VfdGetMediaState"
1954 	DWORD			result;
1955 	DWORD			ret = ERROR_SUCCESS;
1956 
1957 	//	Query file information
1958 
1959 	if (!DeviceIoControl(
1960 		hDevice,
1961 		IOCTL_DISK_IS_WRITABLE,
1962 		NULL,
1963 		0,
1964 		NULL,
1965 		0,
1966 		&result,
1967 		NULL))
1968 	{
1969 		ret = GetLastError();
1970 
1971 		if (ret != ERROR_NOT_READY) {
1972 			VFDTRACE(0,
1973 				(FUNC ": DeviceIoControl(IOCTL_DISK_IS_WRITABLE) - %s",
1974 				SystemMessage(ret)));
1975 		}
1976 	}
1977 
1978 	return ret;
1979 }
1980 
1981 //
1982 //	Set or Delete a global drive letter
1983 //
1984 DWORD WINAPI VfdSetGlobalLink(
1985 	HANDLE			hDevice,
1986 	CHAR			cLetter)
1987 {
1988 #undef	FUNC
1989 #define FUNC		"VfdSetGlobalLink"
1990 	CHAR			letter;
1991 	ULONG			number;
1992 	DWORD			result;
1993 	DWORD			ret;
1994 
1995 	if (isalpha(cLetter)) {
1996 
1997 		//	make sure the drive does not have a drive letter
1998 
1999 		letter = 0;
2000 
2001 		VfdGetGlobalLink(hDevice, &letter);
2002 
2003 		if (isalpha(letter)) {
2004 			VFDTRACE(0,
2005 				(FUNC ": Drive already has a drive letter %c\n", letter));
2006 			return ERROR_ALREADY_ASSIGNED;
2007 		}
2008 
2009 		VfdGetLocalLink(hDevice, &letter);
2010 
2011 		if (isalpha(letter)) {
2012 			VFDTRACE(0,
2013 				(FUNC ": Drive already has a drive letter %c\n", letter));
2014 			return ERROR_ALREADY_ASSIGNED;
2015 		}
2016 
2017 		//	make sure drive letter is not in use
2018 
2019 		cLetter = (CHAR)toupper(cLetter);
2020 
2021 		if (GetLogicalDrives() & (1 << (cLetter - 'A'))) {
2022 			VFDTRACE(0,
2023 				(FUNC ": Drive letter %c already used\n", cLetter));
2024 			return ERROR_ALREADY_ASSIGNED;
2025 		}
2026 
2027 		//	Assign a new drive letter
2028 
2029 		if (!DeviceIoControl(
2030 			hDevice,
2031 			IOCTL_VFD_SET_LINK,
2032 			&cLetter,
2033 			sizeof(cLetter),
2034 			NULL,
2035 			0,
2036 			&result,
2037 			NULL))
2038 		{
2039 			ret = GetLastError();
2040 
2041 			VFDTRACE(0,
2042 				(FUNC ": DeviceIoControl(IOCTL_VFD_SET_LINK) - %s",
2043 				SystemMessage(ret)));
2044 
2045 			return ret;
2046 		}
2047 
2048 		//	broadcast system message
2049 
2050 		VfdBroadcastLink(cLetter, VFD_LINK_CREATED);
2051 
2052 		//	broadcast VFD message
2053 
2054 		if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
2055 			VfdNotify(VFD_OPERATION_SETLINK, number);
2056 		}
2057 
2058 		return ERROR_SUCCESS;
2059 	}
2060 	else if (!cLetter) {
2061 
2062 		//	make sure the drive has a global drive letter
2063 
2064 		letter = 0;
2065 
2066 		VfdGetGlobalLink(hDevice, &letter);
2067 
2068 		if (!isalpha(letter)) {
2069 			VFDTRACE(0,
2070 				(FUNC ": Drive does not have a drive letter\n"));
2071 			return ERROR_INVALID_FUNCTION;
2072 		}
2073 
2074 		//	Remove drive letters
2075 
2076 		if (!DeviceIoControl(
2077 			hDevice,
2078 			IOCTL_VFD_SET_LINK,
2079 			&cLetter,
2080 			sizeof(cLetter),
2081 			NULL,
2082 			0,
2083 			&result,
2084 			NULL))
2085 		{
2086 			ret = GetLastError();
2087 
2088 			VFDTRACE(0,
2089 				(FUNC ": DeviceIoControl(IOCTL_VFD_SET_LINK) - %s",
2090 				SystemMessage(ret)));
2091 
2092 			return ret;
2093 		}
2094 
2095 		//	broadcast system message
2096 
2097 		VfdBroadcastLink(letter, VFD_LINK_REMOVED);
2098 
2099 		//	broadcast VFD message
2100 		if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
2101 			VfdNotify(VFD_OPERATION_DELLINK, number);
2102 		}
2103 
2104 		return ERROR_SUCCESS;
2105 	}
2106 	else {
2107 		return ERROR_INVALID_PARAMETER;
2108 	}
2109 }
2110 
2111 //
2112 //	Get a global drive letter
2113 //
2114 DWORD WINAPI VfdGetGlobalLink(
2115 	HANDLE			hDevice,
2116 	PCHAR			pLetter)
2117 {
2118 #undef	FUNC
2119 #define FUNC		"VfdGetGlobalLinks"
2120 	DWORD			result;
2121 	DWORD			ret;
2122 
2123 	if (!pLetter) {
2124 		return ERROR_INVALID_PARAMETER;
2125 	}
2126 
2127 	*pLetter = 0;
2128 
2129 	if (!DeviceIoControl(
2130 		hDevice,
2131 		IOCTL_VFD_QUERY_LINK,
2132 		NULL,
2133 		0,
2134 		pLetter,
2135 		sizeof(*pLetter),
2136 		&result,
2137 		NULL))
2138 	{
2139 		ret = GetLastError();
2140 
2141 		VFDTRACE(0,
2142 			(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_LINK) - %s",
2143 			SystemMessage(ret)));
2144 
2145 		return ret;
2146 	}
2147 
2148 	return ERROR_SUCCESS;
2149 }
2150 
2151 //
2152 //	Set or remove a local drive letter
2153 //
2154 DWORD WINAPI VfdSetLocalLink(
2155 	HANDLE			hDevice,
2156 	CHAR			cLetter)
2157 {
2158 #undef	FUNC
2159 #define FUNC		"VfdSetLocalLink"
2160 	CHAR			letter;
2161 	CHAR			dos_name[] = "A:";
2162 	CHAR			dev_name[MAX_PATH];
2163 	ULONG			number;
2164 	DWORD			ret;
2165 
2166 	if (isalpha(cLetter)) {
2167 
2168 		//	make sure the drive does not have a drive letter
2169 
2170 		letter = 0;
2171 
2172 		VfdGetGlobalLink(hDevice, &letter);
2173 
2174 		if (isalpha(letter)) {
2175 			VFDTRACE(0,
2176 				(FUNC ": Drive already has a drive letter %c\n", letter));
2177 			return ERROR_ALREADY_ASSIGNED;
2178 		}
2179 
2180 		VfdGetLocalLink(hDevice, &letter);
2181 
2182 		if (isalpha(letter)) {
2183 			VFDTRACE(0,
2184 				(FUNC ": Drive already has a drive letter %c\n", letter));
2185 			return ERROR_ALREADY_ASSIGNED;
2186 		}
2187 
2188 		//	make sure drive letters are not in use
2189 
2190 		cLetter = (CHAR)toupper(cLetter);
2191 
2192 		if (GetLogicalDrives() & (1 << (cLetter - 'A'))) {
2193 			VFDTRACE(0,
2194 				(FUNC ": Drive letter already used\n"));
2195 
2196 			return ERROR_ALREADY_ASSIGNED;
2197 		}
2198 
2199 		//	get VFD device name
2200 
2201 		ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
2202 
2203 		if (ret != ERROR_SUCCESS) {
2204 			return ret;
2205 		}
2206 
2207 		//	assign a drive letter
2208 
2209 		dos_name[0] = cLetter;
2210 
2211 		if (!DefineDosDevice(DDD_RAW_TARGET_PATH, dos_name, dev_name)) {
2212 			ret = GetLastError();
2213 
2214 			VFDTRACE(0,
2215 				(FUNC ": DefineDosDevice(%s,%s) - %s",
2216 				dos_name, dev_name, SystemMessage(ret)));
2217 		}
2218 
2219 		if (ret == ERROR_SUCCESS) {
2220 			//	broadcast VFD message
2221 
2222 			if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
2223 				VfdNotify(VFD_OPERATION_SETLINK, number);
2224 			}
2225 		}
2226 
2227 		return ret;
2228 	}
2229 	else if (!cLetter) {
2230 
2231 		//	make sure the drive has a local drive letter
2232 
2233 		letter = 0;
2234 
2235 		VfdGetLocalLink(hDevice, &letter);
2236 
2237 		if (!isalpha(letter)) {
2238 			VFDTRACE(0,
2239 				(FUNC ": Drive letter is not assigned to this drive\n"));
2240 			return ERROR_INVALID_FUNCTION;
2241 		}
2242 
2243 		//	get VFD device name
2244 
2245 		ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
2246 
2247 		if (ret != ERROR_SUCCESS) {
2248 			return ret;
2249 		}
2250 
2251 		//	remove drive letters
2252 #define DDD_FLAGS	(DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH | DDD_EXACT_MATCH_ON_REMOVE)
2253 
2254 		dos_name[0] = (CHAR)toupper(letter);
2255 
2256 		if (!DefineDosDevice(DDD_FLAGS, dos_name, dev_name)) {
2257 			ret = GetLastError();
2258 
2259 			VFDTRACE(0,
2260 				(FUNC ": DefineDosDevice(%s,%s) - %s",
2261 				dos_name, dev_name, SystemMessage(ret)));
2262 		}
2263 
2264 		if (ret == ERROR_SUCCESS) {
2265 			//	broadcast VFD message
2266 			if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
2267 				VfdNotify(VFD_OPERATION_DELLINK, number);
2268 			}
2269 		}
2270 
2271 		return ret;
2272 	}
2273 	else {
2274 		return ERROR_INVALID_PARAMETER;
2275 	}
2276 }
2277 
2278 //
2279 //	Get local drive letters
2280 //
2281 DWORD WINAPI VfdGetLocalLink(
2282 	HANDLE			hDevice,
2283 	PCHAR			pLetter)
2284 {
2285 #undef	FUNC
2286 #define FUNC		"VfdGetLocalLinks"
2287 	CHAR			global;
2288 	ULONG			logical;
2289 	CHAR			dos_name[] = "A:";
2290 	CHAR			dev_name[MAX_PATH];
2291 	CHAR			dos_target[MAX_PATH * 2];
2292 	DWORD			ret;
2293 
2294 	if (!pLetter) {
2295 		return ERROR_INVALID_PARAMETER;
2296 	}
2297 
2298 	//	Get the VFD device name
2299 
2300 	ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
2301 
2302 	if (ret != ERROR_SUCCESS) {
2303 		return ret;
2304 	}
2305 
2306 	//	Get global drive letter
2307 
2308 	ret = VfdGetGlobalLink(hDevice, &global);
2309 
2310 	if (ret != ERROR_SUCCESS) {
2311 		return ret;
2312 	}
2313 
2314 	//	Get logical drives
2315 
2316 	logical = GetLogicalDrives();
2317 
2318 	//	exclude the global drive letter
2319 
2320 	if (isalpha(global)) {
2321 		logical &= ~(1 << (toupper(global) - 'A'));
2322 	}
2323 
2324 	//	start searching from the next drive letter
2325 
2326 	if (isalpha(*pLetter)) {
2327 		dos_name[0] = (CHAR)(toupper(*pLetter) + 1);
2328 		logical >>= (dos_name[0] - 'A');
2329 	}
2330 
2331 	//	Check dos device targets
2332 
2333 	*pLetter = '\0';
2334 
2335 	while (logical) {
2336 		if (logical & 0x01) {
2337 			if (QueryDosDevice(dos_name, dos_target, sizeof(dos_target))) {
2338 				if (_stricmp(dos_target, dev_name) == 0) {
2339 					*pLetter = dos_name[0];
2340 					break;
2341 				}
2342 			}
2343 			else {
2344 				VFDTRACE(0,
2345 					(FUNC ": QueryDosDevice(%s) - %s",
2346 					dos_name, SystemMessage(GetLastError())));
2347 			}
2348 		}
2349 		logical >>= 1;
2350 		dos_name[0]++;
2351 	}
2352 
2353 	return ERROR_SUCCESS;
2354 }
2355 
2356 //
2357 //	Get the Virtual Floppy device number
2358 //
2359 DWORD WINAPI VfdGetDeviceNumber(
2360 	HANDLE			hDevice,
2361 	PULONG			pNumber)
2362 {
2363 #undef	FUNC
2364 #define FUNC		"VfdGetDeviceNumber"
2365 	DWORD			result;
2366 	DWORD			ret = ERROR_SUCCESS;
2367 
2368 	if (!pNumber) {
2369 		return ERROR_INVALID_PARAMETER;
2370 	}
2371 
2372 	*pNumber = 0;
2373 
2374 	if (!DeviceIoControl(
2375 		hDevice,
2376 		IOCTL_VFD_QUERY_NUMBER,
2377 		NULL,
2378 		0,
2379 		pNumber,
2380 		sizeof(ULONG),
2381 		&result,
2382 		NULL))
2383 	{
2384 		ret = GetLastError();
2385 
2386 		VFDTRACE(0,
2387 			(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_NUMBER) - %s",
2388 			SystemMessage(ret)));
2389 	}
2390 
2391 	return ret;
2392 }
2393 
2394 //	Get the Virtual Floppy device name
2395 
2396 DWORD WINAPI VfdGetDeviceName(
2397 	HANDLE			hDevice,
2398 	PCHAR			pName,
2399 	ULONG			nLength)
2400 {
2401 #undef	FUNC
2402 #define FUNC		"VfdGetDeviceName"
2403 	DWORD			result;
2404 	WCHAR			wname[MAX_PATH];
2405 	DWORD			ret = ERROR_SUCCESS;
2406 
2407 	if (!pName || !nLength) {
2408 		return ERROR_INVALID_PARAMETER;
2409 	}
2410 
2411 	ZeroMemory(pName, nLength);
2412 
2413 	if (!DeviceIoControl(
2414 		hDevice,
2415 		IOCTL_VFD_QUERY_NAME,
2416 		NULL,
2417 		0,
2418 		wname,
2419 		sizeof(wname),
2420 		&result,
2421 		NULL))
2422 	{
2423 		ret = GetLastError();
2424 
2425 		VFDTRACE(0,
2426 			(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_NUMBER) - %s",
2427 			SystemMessage(ret)));
2428 	}
2429 
2430 	if (!WideCharToMultiByte(CP_OEMCP, 0, &wname[1],
2431 		wname[0] / sizeof(WCHAR), pName, nLength, NULL, NULL)) {
2432 
2433 		ret = GetLastError();
2434 
2435 		VFDTRACE(0,
2436 			(FUNC ": WideCharToMultiByte - %s",
2437 			SystemMessage(ret)));
2438 	}
2439 
2440 	return ret;
2441 }
2442 
2443 //
2444 //	Get Virtual Floppy driver version
2445 //
2446 DWORD WINAPI VfdGetDriverVersion(
2447 	HANDLE			hDevice,
2448 	PULONG			pVersion)
2449 {
2450 #undef	FUNC
2451 #define FUNC		"VfdGetDriverVersion"
2452 	DWORD			result;
2453 	DWORD			ret = ERROR_SUCCESS;
2454 
2455 	if (!pVersion) {
2456 		return ERROR_INVALID_PARAMETER;
2457 	}
2458 
2459 	*pVersion = '\0';
2460 
2461 	if (!DeviceIoControl(
2462 		hDevice,
2463 		IOCTL_VFD_QUERY_VERSION,
2464 		NULL,
2465 		0,
2466 		pVersion,
2467 		sizeof(ULONG),
2468 		&result,
2469 		NULL))
2470 	{
2471 		ret = GetLastError();
2472 
2473 		VFDTRACE(0,
2474 			(FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_VERSION) - %s",
2475 			SystemMessage(ret)));
2476 	}
2477 
2478 	return ret;
2479 }
2480 
2481 //
2482 //	Change the write protect state of the media
2483 //
2484 DWORD WINAPI VfdWriteProtect(
2485 	HANDLE			hDevice,
2486 	BOOL			bProtect)
2487 {
2488 #undef	FUNC
2489 #define FUNC		"VfdWriteProtect"
2490 	DWORD			result;
2491 	DWORD			ret = ERROR_SUCCESS;
2492 
2493 	if (!DeviceIoControl(
2494 		hDevice,
2495 		bProtect ? IOCTL_VFD_SET_PROTECT : IOCTL_VFD_CLEAR_PROTECT,
2496 		NULL,
2497 		0,
2498 		NULL,
2499 		0,
2500 		&result,
2501 		NULL))
2502 	{
2503 		ret = GetLastError();
2504 
2505 		VFDTRACE(0,
2506 			(FUNC ": DeviceIoControl(IOCTL_VFD_SET_PROTECT) - %s",
2507 			SystemMessage(ret)));
2508 	}
2509 
2510 	if (ret == ERROR_SUCCESS) {
2511 		ULONG number;
2512 
2513 		if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
2514 			VfdNotify(VFD_OPERATION_PROTECT, number);
2515 		}
2516 	}
2517 
2518 	return ret;
2519 }
2520 
2521 //	Format the current media with FAT12
2522 
2523 DWORD WINAPI VfdFormatMedia(
2524 	HANDLE			hDevice)
2525 {
2526 #undef	FUNC
2527 #define FUNC		"VfdFormatMedia"
2528 	DWORD			result;
2529 	DWORD			ret = ERROR_SUCCESS;
2530 	PUCHAR			buf = NULL;
2531 	GET_LENGTH_INFORMATION	length;
2532 
2533 	//	Get the media size
2534 
2535 	if (!DeviceIoControl(
2536 		hDevice,
2537 		IOCTL_DISK_GET_LENGTH_INFO,
2538 		NULL,
2539 		0,
2540 		&length,
2541 		sizeof(length),
2542 		&result,
2543 		NULL))
2544 	{
2545 		ret = GetLastError();
2546 
2547 		VFDTRACE(0,
2548 			(FUNC ": DeviceIoControl(IOCTL_DISK_GET_LENGTH_INFO) - %s",
2549 			SystemMessage(ret)));
2550 
2551 		goto exit_func;
2552 	}
2553 
2554 	//	Prepare a formatted image buffer
2555 
2556 	buf = (PUCHAR)LocalAlloc(LPTR, length.Length.LowPart);
2557 
2558 	if (buf == NULL) {
2559 		ret = GetLastError();
2560 
2561 		VFDTRACE(0,
2562 			(FUNC ": LocalAlloc - %s",
2563 			SystemMessage(ret)));
2564 
2565 		goto exit_func;
2566 	}
2567 
2568 	//	format the buffer
2569 
2570 	ret = FormatBufferFat(buf,
2571 		VFD_BYTE_TO_SECTOR(length.Length.LowPart));
2572 
2573 	if (ret != ERROR_SUCCESS) {
2574 		goto exit_func;
2575 	}
2576 
2577 	//	seek the top of the media
2578 
2579 	if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
2580 		ret = GetLastError();
2581 
2582 		VFDTRACE(0,
2583 			(FUNC ": SetFilePointer - %s",
2584 			SystemMessage(ret)));
2585 
2586 		goto exit_func;
2587 	}
2588 
2589 	//	write the image into the media
2590 
2591 	if (!WriteFile(hDevice, buf, length.Length.LowPart, &result, NULL) ||
2592 		result != length.Length.LowPart) {
2593 		ret = GetLastError();
2594 
2595 		VFDTRACE(0,
2596 			(FUNC ": WriteFile - %s",
2597 			SystemMessage(ret)));
2598 
2599 		goto exit_func;
2600 	}
2601 
2602 exit_func:
2603 	//	unlock the target volume
2604 	if (!DeviceIoControl(
2605 		hDevice,
2606 		FSCTL_UNLOCK_VOLUME,
2607 		NULL,
2608 		0,
2609 		NULL,
2610 		0,
2611 		&result,
2612 		NULL))
2613 	{
2614 		VFDTRACE(0,
2615 			(FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
2616 			SystemMessage(GetLastError())));
2617 	}
2618 
2619 	//	release the format image buffer
2620 	if (buf) {
2621 		LocalFree(buf);
2622 	}
2623 
2624 	return ret;
2625 }
2626 
2627 //	Dismount the volume (should be called before Save, Format)
2628 
2629 DWORD WINAPI VfdDismountVolume(
2630 	HANDLE			hDevice,
2631 	BOOL			bForce)
2632 {
2633 #undef	FUNC
2634 #define FUNC		"VfdDismountVolume"
2635 	DWORD			result;
2636 	DWORD			ret = ERROR_SUCCESS;
2637 
2638 	//	Lock the target volume
2639 
2640 	if (!DeviceIoControl(
2641 		hDevice,
2642 		FSCTL_LOCK_VOLUME,
2643 		NULL,
2644 		0,
2645 		NULL,
2646 		0,
2647 		&result,
2648 		NULL))
2649 	{
2650 		ret = GetLastError();
2651 
2652 		VFDTRACE(0,
2653 			(FUNC ": DeviceIoControl(FSCTL_LOCK_VOLUME) - %s",
2654 			SystemMessage(ret)));
2655 
2656 		if (ret != ERROR_ACCESS_DENIED || !bForce) {
2657 			return ret;
2658 		}
2659 	}
2660 
2661 	//	Dismount the target volume
2662 
2663 	if (!DeviceIoControl(
2664 		hDevice,
2665 		FSCTL_DISMOUNT_VOLUME,
2666 		NULL,
2667 		0,
2668 		NULL,
2669 		0,
2670 		&result,
2671 		NULL))
2672 	{
2673 		ret = GetLastError();
2674 
2675 		VFDTRACE(0,
2676 			(FUNC ": DeviceIoControl(FSCTL_DISMOUNT_VOLUME) - %s",
2677 			SystemMessage(ret)));
2678 	}
2679 
2680 	return ret;
2681 }
2682 
2683 //	Save the current image into a file
2684 
2685 DWORD WINAPI VfdSaveImage(
2686 	HANDLE			hDevice,
2687 	PCSTR			sFileName,
2688 	BOOL			bOverWrite,
2689 	BOOL			bTruncate)
2690 {
2691 #undef	FUNC
2692 #define FUNC		"VfdSaveImage"
2693 	HANDLE			hFile = INVALID_HANDLE_VALUE;
2694 	DWORD			result;
2695 	DWORD			ret = ERROR_SUCCESS;
2696 	PUCHAR			buf = NULL;
2697 	GET_LENGTH_INFORMATION	length;
2698 
2699 
2700 	ret = ERROR_SUCCESS;
2701 
2702 	//	Get the media size
2703 
2704 	if (!DeviceIoControl(
2705 		hDevice,
2706 		IOCTL_DISK_GET_LENGTH_INFO,
2707 		NULL,
2708 		0,
2709 		&length,
2710 		sizeof(length),
2711 		&result,
2712 		NULL))
2713 	{
2714 		ret = GetLastError();
2715 
2716 		VFDTRACE(0,
2717 			(FUNC ": DeviceIoControl(IOCTL_DISK_GET_LENGTH_INFO) - %s",
2718 			SystemMessage(ret)));
2719 
2720 		goto exit_func;
2721 	}
2722 
2723 	//	Prepare an intermediate image buffer
2724 
2725 	buf = (PUCHAR)LocalAlloc(LPTR, length.Length.LowPart);
2726 
2727 	if (buf == NULL) {
2728 		ret = GetLastError();
2729 
2730 		VFDTRACE(0,
2731 			(FUNC ": LocalAlloc - %s",
2732 			SystemMessage(ret)));
2733 
2734 		goto exit_func;
2735 	}
2736 
2737 	//	seek the top of the media
2738 
2739 	if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
2740 		ret = GetLastError();
2741 
2742 		VFDTRACE(0,
2743 			(FUNC ": SetFilePointer - %s",
2744 			SystemMessage(ret)));
2745 
2746 		goto exit_func;
2747 	}
2748 
2749 	//	read the image data
2750 
2751 	if (!ReadFile(hDevice, buf, length.Length.LowPart, &result, NULL) ||
2752 		result != length.Length.LowPart) {
2753 		ret = GetLastError();
2754 
2755 		VFDTRACE(0,
2756 			(FUNC ": ReadFile - %s",
2757 			SystemMessage(ret)));
2758 
2759 		goto exit_func;
2760 	}
2761 
2762 	//	open the destination file
2763 
2764 	hFile = CreateFile(sFileName, GENERIC_WRITE, 0, NULL,
2765 		bOverWrite ? OPEN_ALWAYS : CREATE_NEW, 0, NULL);
2766 
2767 	if (hFile == INVALID_HANDLE_VALUE) {
2768 		ret = GetLastError();
2769 
2770 		VFDTRACE(0,
2771 			(FUNC ": CreateFile - %s",
2772 			SystemMessage(ret)));
2773 
2774 		goto exit_func;
2775 	}
2776 
2777 	//	seek the top of the file
2778 
2779 	if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
2780 		ret = GetLastError();
2781 
2782 		VFDTRACE(0,
2783 			(FUNC ": SetFilePointer - %s",
2784 			SystemMessage(ret)));
2785 
2786 		goto exit_func;
2787 	}
2788 
2789 	//	write the image data
2790 
2791 	if (!WriteFile(hFile, buf, length.Length.LowPart, &result, NULL) ||
2792 		result != length.Length.LowPart) {
2793 		ret = GetLastError();
2794 
2795 		VFDTRACE(0,
2796 			(FUNC ": WriteFile - %s",
2797 			SystemMessage(ret)));
2798 
2799 		goto exit_func;
2800 	}
2801 
2802 	//	truncate the target file
2803 
2804 	if (bTruncate && !SetEndOfFile(hFile)) {
2805 		ret = GetLastError();
2806 
2807 		VFDTRACE(0,
2808 			(FUNC ": SetEndOfFile - %s",
2809 			SystemMessage(ret)));
2810 
2811 		goto exit_func;
2812 	}
2813 
2814 	//	reset the media modified flag
2815 
2816 	if (!DeviceIoControl(
2817 		hDevice,
2818 		IOCTL_VFD_RESET_MODIFY,
2819 		NULL,
2820 		0,
2821 		NULL,
2822 		0,
2823 		&result,
2824 		NULL))
2825 	{
2826 		VFDTRACE(0,
2827 			(FUNC ": DeviceIoControl(IOCTL_VFD_RESET_MODIFY) - %s",
2828 			SystemMessage(GetLastError())));
2829 	}
2830 
2831 exit_func:
2832 	//	unlock the target volume
2833 
2834 	if (!DeviceIoControl(
2835 		hDevice,
2836 		FSCTL_UNLOCK_VOLUME,
2837 		NULL,
2838 		0,
2839 		NULL,
2840 		0,
2841 		&result,
2842 		NULL))
2843 	{
2844 		VFDTRACE(0,
2845 			(FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
2846 			SystemMessage(GetLastError())));
2847 	}
2848 
2849 	//	release the format image buffer
2850 
2851 	if (buf) {
2852 		LocalFree(buf);
2853 	}
2854 
2855 	//	close the image file
2856 
2857 	if (hFile != INVALID_HANDLE_VALUE) {
2858 		CloseHandle(hFile);
2859 	}
2860 
2861 	return ret;
2862 }
2863 
2864 //
2865 //	Check if specified file is valid VFD driver
2866 //
2867 DWORD WINAPI VfdCheckDriverFile(
2868 	PCSTR			sFileName,
2869 	PULONG			pFileVersion)
2870 {
2871 #undef	FUNC
2872 #define FUNC			"VfdCheckDriverFile"
2873 	DWORD				result;
2874 	DWORD				dummy;
2875 	PVOID				info;
2876 	VS_FIXEDFILEINFO	*fixedinfo;
2877 	DWORD				ret = ERROR_SUCCESS;
2878 	PSTR				str;
2879 
2880 	//	Check parameter
2881 
2882 	if (!sFileName || !*sFileName) {
2883 		return ERROR_INVALID_PARAMETER;
2884 	}
2885 
2886 	if (pFileVersion) {
2887 		*pFileVersion = 0;
2888 	}
2889 
2890 	//	check file existence
2891 
2892 	if (GetFileAttributes(sFileName) == INVALID_FILE_ATTRIBUTES) {
2893 		ret = GetLastError();
2894 
2895 		VFDTRACE(0,
2896 			(FUNC ": GetFileAttributes - %s\n",
2897 			SystemMessage(ret)));
2898 
2899 		return ret;
2900 	}
2901 
2902 	//	check file version
2903 
2904 	result = GetFileVersionInfoSize((PSTR)sFileName, &dummy);
2905 
2906 	if (result == 0) {
2907 		VFDTRACE(0,
2908 			(FUNC ": GetFileVersionInfoSize == 0\n"));
2909 
2910 		return ERROR_BAD_DRIVER;
2911 	}
2912 
2913 	info = LocalAlloc(LPTR, result);
2914 
2915 	if (info == NULL) {
2916 		ret = GetLastError();
2917 
2918 		VFDTRACE(0,
2919 			(FUNC ": LocalAlloc(%lu) - %s\n",
2920 			result, SystemMessage(ret)));
2921 
2922 		return ret;
2923 	}
2924 
2925 	if (!GetFileVersionInfo((PSTR)sFileName, 0, result, info)) {
2926 		ret = GetLastError();
2927 
2928 		VFDTRACE(0,
2929 			(FUNC ": GetFileVersionInfo - %s", SystemMessage(ret)));
2930 
2931 		goto cleanup;
2932 	}
2933 
2934 	result = sizeof(fixedinfo);
2935 
2936 	if (!VerQueryValue(info, "\\", (PVOID *)&fixedinfo, (PUINT)&result)) {
2937 		ret = GetLastError();
2938 
2939 		VFDTRACE(0,
2940 			(FUNC ": VerQueryValue(\"\\\") - %s", SystemMessage(ret)));
2941 
2942 		goto cleanup;
2943 	}
2944 
2945 	if (fixedinfo->dwFileOS				!= VOS_NT_WINDOWS32 ||
2946 		fixedinfo->dwFileType			!= VFT_DRV			||
2947 		fixedinfo->dwFileSubtype		!= VFT2_DRV_SYSTEM) {
2948 
2949 		VFDTRACE(0,
2950 			(FUNC ": Invalid file type flags\n"));
2951 
2952 		ret = ERROR_BAD_DRIVER;
2953 
2954 		goto cleanup;
2955 	}
2956 
2957 	if (pFileVersion) {
2958 		*pFileVersion = fixedinfo->dwFileVersionMS;
2959 
2960 		if (fixedinfo->dwFileFlags & VS_FF_DEBUG) {
2961 			*pFileVersion |= 0x80000000;
2962 		}
2963 	}
2964 
2965 	if (!VerQueryValue(info,
2966 		"\\StringFileInfo\\" VFD_VERSIONINFO_LANG "\\OriginalFileName",
2967 		(PVOID *)&str, (PUINT)&result)) {
2968 		ret = GetLastError();
2969 
2970 		VFDTRACE(0,
2971 			(FUNC ": VerQueryValue(\"OriginalFileName\") - %s",
2972 			SystemMessage(ret)));
2973 
2974 		goto cleanup;
2975 	}
2976 
2977 	if (strcmp(str, VFD_DRIVER_FILENAME)) {
2978 		VFDTRACE(0,
2979 			(FUNC ": Invalid original file name\n"));
2980 
2981 		ret = ERROR_BAD_DRIVER;
2982 
2983 		goto cleanup;
2984 	}
2985 
2986 	if (fixedinfo->dwFileVersionMS		!= MAKELONG(VFD_DRIVER_MINOR, VFD_DRIVER_MAJOR) ||
2987 		fixedinfo->dwProductVersionMS	!= MAKELONG(VFD_PRODUCT_MINOR, VFD_PRODUCT_MAJOR)) {
2988 
2989 		VFDTRACE(0,
2990 			(FUNC ": Invalid version values - file:%08x, prod: %08x\n",
2991 			fixedinfo->dwFileVersionMS, fixedinfo->dwProductVersionMS));
2992 
2993 		ret = ERROR_BAD_DRIVER;
2994 
2995 		goto cleanup;
2996 	}
2997 
2998 	//	Ensure that the driver binary is located on a local drive
2999 	//	because device driver cannot be started on network drives.
3000 
3001 	if (*sFileName == '\\' && *(sFileName + 1) == '\\') {
3002 		//	full path is a UNC path -- \\server\dir\...
3003 
3004 		VFDTRACE(0,
3005 			(FUNC ": Driver is located on a network drive\n"));
3006 
3007 		return ERROR_NETWORK_ACCESS_DENIED;
3008 	}
3009 	else {
3010 		//	ensure that the drive letter is not a network drive
3011 
3012 		CHAR root[] = " :\\";
3013 
3014 		root[0] = *sFileName;
3015 
3016 		if (GetDriveType(root) == DRIVE_REMOTE) {
3017 			// the drive is a network drive
3018 
3019 			VFDTRACE(0,
3020 				(FUNC ": Driver is located on a network drive\n"));
3021 
3022 			return ERROR_NETWORK_ACCESS_DENIED;
3023 		}
3024 	}
3025 
3026 cleanup:
3027 	LocalFree(info);
3028 
3029 	return ret;
3030 }
3031 
3032 //
3033 //	check an image file
3034 //
3035 DWORD WINAPI VfdCheckImageFile(
3036 	PCSTR			sFileName,
3037 	PDWORD			pAttributes,
3038 	PVFD_FILETYPE	pFileType,
3039 	PULONG			pImageSize)
3040 {
3041 #undef	FUNC
3042 #define FUNC		"VfdCheckImageFile"
3043 	HANDLE			hFile;
3044 	DWORD			ret = ERROR_SUCCESS;
3045 
3046 	if (!sFileName || !*sFileName || !pAttributes || !pImageSize || !pFileType) {
3047 		return ERROR_INVALID_PARAMETER;
3048 	}
3049 
3050 	//	get file attributes
3051 
3052 	*pAttributes = GetFileAttributes(sFileName);
3053 
3054 	if (*pAttributes == INVALID_FILE_ATTRIBUTES) {
3055 		ret = GetLastError();
3056 
3057 		if (ret != ERROR_FILE_NOT_FOUND) {
3058 			VFDTRACE(0,
3059 				(FUNC ": GetFileAttributes(%s) - %s\n",
3060 				sFileName, SystemMessage(ret)));
3061 		}
3062 
3063 		return ret;
3064 	}
3065 
3066 	//	Open the target file
3067 
3068 	hFile = CreateFile(sFileName, GENERIC_READ | GENERIC_WRITE,
3069 		0, NULL, OPEN_EXISTING, 0, NULL);
3070 
3071 	if (hFile == INVALID_HANDLE_VALUE) {
3072 
3073 		//	failed to open
3074 
3075 		ret = GetLastError();
3076 
3077 		if (ret != ERROR_ACCESS_DENIED) {
3078 			VFDTRACE(0,
3079 				(FUNC ": CreateFile(%s) - %s\n",
3080 				sFileName, SystemMessage(ret)));
3081 
3082 			return ret;
3083 		}
3084 
3085 		// try opening it read-only
3086 
3087 		hFile = CreateFile(sFileName, GENERIC_READ,
3088 			FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
3089 
3090 		if (hFile == INVALID_HANDLE_VALUE) {
3091 
3092 			// cannot open even read-only
3093 
3094 			ret = GetLastError();
3095 
3096 			VFDTRACE(0,
3097 				(FUNC ": CreateFile(%s) - %s\n",
3098 				sFileName, SystemMessage(ret)));
3099 
3100 			return ret;
3101 		}
3102 
3103 		// file can be opened read-only
3104 		*pAttributes |= FILE_ATTRIBUTE_READONLY;
3105 		ret = ERROR_SUCCESS;
3106 	}
3107 
3108 	//	check if the image is an IMZ file
3109 
3110 	if (ExtractZipInfo(hFile, pImageSize) == ERROR_SUCCESS) {
3111 		*pFileType = VFD_FILETYPE_ZIP;
3112 	}
3113 	else {
3114 		*pImageSize = GetFileSize(hFile, NULL);
3115 		*pFileType	= VFD_FILETYPE_RAW;
3116 	}
3117 
3118 	CloseHandle(hFile);
3119 
3120 	return ret;
3121 }
3122 
3123 //
3124 //	Create a formatted new image file
3125 //
3126 DWORD WINAPI VfdCreateImageFile(
3127 	PCSTR			sFileName,
3128 	VFD_MEDIA		nMediaType,
3129 	VFD_FILETYPE	nFileType,
3130 	BOOL			bOverWrite)
3131 {
3132 #undef	FUNC
3133 #define FUNC		"VfdCreateImageFile"
3134 	HANDLE			hFile;
3135 	ULONG			file_size;
3136 	PUCHAR			image_buf = NULL;
3137 	DWORD			result;
3138 	DWORD			ret = ERROR_SUCCESS;
3139 
3140 	if (nFileType != VFD_FILETYPE_RAW) {
3141 		return ERROR_INVALID_PARAMETER;
3142 	}
3143 
3144 	file_size = VfdGetMediaSize(nMediaType);
3145 
3146 	if (file_size == 0) {
3147 		return ERROR_INVALID_PARAMETER;
3148 	}
3149 
3150 	hFile = CreateFile(sFileName, GENERIC_WRITE, 0, NULL,
3151 		bOverWrite ? CREATE_ALWAYS : CREATE_NEW, 0, NULL);
3152 
3153 	if (hFile == INVALID_HANDLE_VALUE) {
3154 		ret = GetLastError();
3155 
3156 		VFDTRACE(0,
3157 			(FUNC ": CreateFile - %s",
3158 			SystemMessage(ret)));
3159 
3160 		return ret;
3161 	}
3162 
3163 	image_buf = (PUCHAR)LocalAlloc(LPTR, file_size);
3164 
3165 	if (image_buf == NULL) {
3166 		ret = GetLastError();
3167 
3168 		VFDTRACE(0,
3169 			(FUNC ": LocalAlloc - %s",
3170 			SystemMessage(ret)));
3171 
3172 		goto exit_func;
3173 	}
3174 
3175 	FormatBufferFat(image_buf, VFD_BYTE_TO_SECTOR(file_size));
3176 
3177 	if (!WriteFile(hFile, image_buf, file_size, &result, NULL) ||
3178 		file_size != result) {
3179 
3180 		ret = GetLastError();
3181 
3182 		VFDTRACE(0,
3183 			(FUNC ": WriteFile - %s",
3184 			SystemMessage(ret)));
3185 
3186 		goto exit_func;
3187 	}
3188 
3189 	SetEndOfFile(hFile);
3190 
3191 exit_func:
3192 	CloseHandle(hFile);
3193 
3194 	if (image_buf) {
3195 		LocalFree(image_buf);
3196 	}
3197 
3198 	return ret;
3199 }
3200 
3201 
3202 //
3203 // choose first available drive letter
3204 //
3205 CHAR WINAPI VfdChooseLetter()
3206 {
3207 	DWORD	logical_drives = GetLogicalDrives();
3208 	CHAR	drive_letter = 'A';
3209 
3210 	if (logical_drives == 0) {
3211 		return '\0';
3212 	}
3213 
3214 	while (logical_drives & 0x1) {
3215 		logical_drives >>= 1;
3216 		drive_letter++;
3217 	}
3218 
3219 	if (drive_letter > 'Z') {
3220 		return '\0';
3221 	}
3222 
3223 	return drive_letter;
3224 }
3225 
3226 //
3227 //	media type functions
3228 //
3229 static const struct
3230 {
3231 	ULONG	Size;
3232 	PCSTR	Name;
3233 }
3234 media_tbl[VFD_MEDIA_MAX] =
3235 {
3236 	{ 0,						"" },					//	VFD_MEDIA_NONE,
3237 	{ VFD_SECTOR_TO_BYTE(320),	"5.25\" 160KB" },		//	VFD_MEDIA_F5_160
3238 	{ VFD_SECTOR_TO_BYTE(360),	"5.25\" 180KB" },		//	VFD_MEDIA_F5_180
3239 	{ VFD_SECTOR_TO_BYTE(640),	"5.25\" 320KB" },		//	VFD_MEDIA_F5_320
3240 	{ VFD_SECTOR_TO_BYTE(720),	"5.25\" 360KB" },		//	VFD_MEDIA_F5_360
3241 	{ VFD_SECTOR_TO_BYTE(1280),	"3.5\"  640KB" },		//	VFD_MEDIA_F3_640
3242 	{ VFD_SECTOR_TO_BYTE(1280),	"5.25\" 640KB" },		//	VFD_MEDIA_F5_640
3243 	{ VFD_SECTOR_TO_BYTE(1440),	"3.5\"  720KB" },		//	VFD_MEDIA_F3_720
3244 	{ VFD_SECTOR_TO_BYTE(1440),	"5.25\" 720KB" },		//	VFD_MEDIA_F5_720
3245 	{ VFD_SECTOR_TO_BYTE(1640),	"3.5\"  820KB" },		//	VFD_MEDIA_F3_820
3246 	{ VFD_SECTOR_TO_BYTE(2400),	"3.5\"	1.2MB" },		//	VFD_MEDIA_F3_1P2
3247 	{ VFD_SECTOR_TO_BYTE(2400),	"5.25\" 1.2MB" },		//	VFD_MEDIA_F5_1P2
3248 	{ VFD_SECTOR_TO_BYTE(2880),	"3.5\"  1.44MB" },		//	VFD_MEDIA_F3_1P4
3249 	{ VFD_SECTOR_TO_BYTE(3360),	"3.5\"  1.68MB DMF" },	//	VFD_MEDIA_F3_1P6
3250 	{ VFD_SECTOR_TO_BYTE(3444),	"3.5\"  1.72MB DMF" },	//	VFD_MEDIA_F3_1P7
3251 	{ VFD_SECTOR_TO_BYTE(5760),	"3.5\"  2.88MB"}		//	VFD_MEDIA_F3_2P8
3252 };
3253 
3254 //	Lookup the largest media to fit in a size
3255 
3256 VFD_MEDIA WINAPI VfdLookupMedia(
3257 	ULONG			nSize)
3258 {
3259 	VFD_MEDIA i;
3260 
3261 	for (i = 1; i < VFD_MEDIA_MAX; i++) {
3262 		if (nSize < media_tbl[i].Size) {
3263 			break;
3264 		}
3265 	}
3266 
3267 	return (--i);
3268 }
3269 
3270 //	Get media size (in bytes) of a media type
3271 
3272 ULONG WINAPI VfdGetMediaSize(
3273 	VFD_MEDIA		nMediaType)
3274 {
3275 	return nMediaType < VFD_MEDIA_MAX ? media_tbl[nMediaType].Size : 0;
3276 }
3277 
3278 //	Get media type name
3279 
3280 PCSTR WINAPI VfdMediaTypeName(
3281 	VFD_MEDIA		nMediaType)
3282 {
3283 	return nMediaType < VFD_MEDIA_MAX ? media_tbl[nMediaType].Name : NULL;
3284 }
3285