xref: /reactos/modules/rosapps/drivers/vfd/vfdioctl.c (revision 8a978a17)
1 /*
2 	vfdioctl.c
3 
4 	Virtual Floppy Drive for Windows NT platform
5 	Kernel mode driver: I/O control request handling
6 
7 	Copyright (C) 2003-2005 Ken Kato
8 */
9 
10 #include "imports.h"
11 #include "vfddrv.h"
12 #include "vfddbg.h"
13 
14 /*
15 #include <initguid.h>
16 DEFINE_GUID(VFD_GUID, 0x4563b3d8L, 0x936a, 0x4692,
17 	0xb6, 0x0c, 0x16, 0xd3, 0xb2, 0x57, 0xbb, 0xf2);
18 */
19 
20 #define IO_INPUTLEN(p)	(p)->Parameters.DeviceIoControl.InputBufferLength
21 #define IO_OUTPUTLEN(p)	(p)->Parameters.DeviceIoControl.OutputBufferLength
22 #define IO_CTRLCODE(p)	(p)->Parameters.DeviceIoControl.IoControlCode
23 
24 //
25 //	IOCTL commands handler
26 //
27 NTSTATUS
28 NTAPI
29 VfdDeviceControl (
30 	IN PDEVICE_OBJECT			DeviceObject,
31 	IN PIRP						Irp)
32 {
33 	PDEVICE_EXTENSION			device_extension;
34 	PIO_STACK_LOCATION			io_stack;
35 	NTSTATUS					status;
36 
37 	device_extension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
38 	io_stack = IoGetCurrentIrpStackLocation(Irp);
39 
40 	Irp->IoStatus.Information = 0;
41 
42 	VFDTRACE(VFDINFO, ("[VFD] %-40s %ws\n",
43 		GetIoControlName(IO_CTRLCODE(io_stack)),
44 		device_extension->DeviceName.Buffer));
45 
46 #ifdef VFD_PNP
47 	status = IoAcquireRemoveLock(&device_extension->RemoveLock, Irp);
48 
49 	if (!NT_SUCCESS(status)) {
50 		VFDTRACE(0,
51 			("Acquire RemoveLock failed %s\n", NtStatusToStr(status)));
52 
53 		Irp->IoStatus.Status = status;
54 		IoCompleteRequest(Irp, IO_NO_INCREMENT);
55 		return status;
56 	}
57 #endif	// VFD_PNP
58 
59 /*
60 	//	Check if volume verification is required
61 
62 	if ((DeviceObject->Flags & DO_VERIFY_VOLUME) &&
63 		!(io_stack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) {
64 
65 		VFDTRACE(VFDWARN,
66 			("[VFD] %-40s - %s\n",
67 				GetIoControlName(IO_CTRLCODE(io_stack)),
68 				GetStatusName(STATUS_VERIFY_REQUIRED)));
69 
70 		Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
71 
72 		IoCompleteRequest(Irp, IO_NO_INCREMENT);
73 
74 		return STATUS_VERIFY_REQUIRED;
75 	}
76 */
77 
78 	switch (IO_CTRLCODE(io_stack)) {
79 	case IOCTL_VFD_OPEN_IMAGE:
80 		//	Open an image file or create an empty RAM disk.
81 		//	Only a few checks are done here.
82 		//	Actual operation is done in device thread
83 
84 		status = VfdOpenCheck(
85 			device_extension,
86 			(PVFD_IMAGE_INFO)Irp->AssociatedIrp.SystemBuffer,
87 			IO_INPUTLEN(io_stack));
88 
89 		if (!NT_SUCCESS(status)) {
90 			break;
91 		}
92 
93 		// Pass the task to the device thread
94 		status = STATUS_PENDING;
95 		break;
96 
97 	case IOCTL_VFD_CLOSE_IMAGE:
98 	case IOCTL_DISK_EJECT_MEDIA:
99 	case IOCTL_STORAGE_EJECT_MEDIA:
100 		//	Close the current image file or delete the RAM disk
101 		//	Only status check is done here.
102 		//	Actual operation is done in device thread.
103 
104 		if (!device_extension->FileHandle &&
105 			!device_extension->FileBuffer) {
106 			status = STATUS_NO_MEDIA_IN_DEVICE;
107 			break;
108 		}
109 
110 		// Pass the task to the device thread
111 		status = STATUS_PENDING;
112 		break;
113 
114 	case IOCTL_VFD_QUERY_IMAGE:
115 		//	Returns current image file information
116 
117 		status = VfdQueryImage(
118 			device_extension,
119 			(PVFD_IMAGE_INFO)Irp->AssociatedIrp.SystemBuffer,
120 			IO_OUTPUTLEN(io_stack),
121 			&Irp->IoStatus.Information);
122 
123 		break;
124 
125 	case IOCTL_VFD_SET_LINK:
126 		// Create / remove a persistent drive letter
127 		// and store it in the registry
128 
129 		if (IO_INPUTLEN(io_stack) < sizeof(CHAR)) {
130 			status = STATUS_INVALID_PARAMETER;
131 			break;
132 		}
133 
134 #ifdef VFD_MOUNT_MANAGER
135 		if (OsMajorVersion >= 5) {
136 			//	Windows 2000/XP
137 			//	Create a drive letter via the mount manager
138 
139 			status = VfdMountMgrMountPoint(device_extension,
140 				*(PCHAR)Irp->AssociatedIrp.SystemBuffer);
141 
142 			//	The new drive letter will be stored in the device extension
143 			//	and the registry when IOCTL_MOUNTDEV_LINK_CREATED or
144 			//	IOCTL_MOUNTDEV_LINK_DELETED is issued from the mount manager.
145 		}
146 		else
147 #else	//	VFD_MOUNT_MANAGER
148 		{
149 			//	Windows NT style drive letter assignment
150 			//	Simply create a symbolic link and store the new value
151 
152 			status = VfdSetLink(device_extension,
153 				*(PCHAR)Irp->AssociatedIrp.SystemBuffer);
154 
155 			if (NT_SUCCESS(status)) {
156 				//	Store the new drive letter into the registry
157 				status = VfdStoreLink(device_extension);
158 			}
159 		}
160 #endif	//	VFD_MOUNT_MANAGER
161 		break;
162 
163 	case IOCTL_VFD_QUERY_LINK:
164 		//	Return the current persistent drive letter
165 
166 		if (IO_OUTPUTLEN(io_stack) < sizeof(CHAR)) {
167 			status = STATUS_BUFFER_TOO_SMALL;
168 			break;
169 		}
170 
171 		*(PCHAR)Irp->AssociatedIrp.SystemBuffer =
172 			device_extension->DriveLetter;
173 
174 		Irp->IoStatus.Information = sizeof(CHAR);
175 		status = STATUS_SUCCESS;
176 		break;
177 
178 	case IOCTL_VFD_SET_PROTECT:
179 		//	Set media protect flag
180 
181 		if (!device_extension->FileHandle &&
182 			!device_extension->FileBuffer) {
183 			status = STATUS_NO_MEDIA_IN_DEVICE;
184 			break;
185 		}
186 
187 		device_extension->MediaFlags |= VFD_FLAG_WRITE_PROTECTED;
188 		status = STATUS_SUCCESS;
189 		break;
190 
191 	case IOCTL_VFD_CLEAR_PROTECT:
192 		//	Clear media protect flag
193 
194 		if (!device_extension->FileHandle &&
195 			!device_extension->FileBuffer) {
196 			status = STATUS_NO_MEDIA_IN_DEVICE;
197 			break;
198 		}
199 
200 		device_extension->MediaFlags &= ~VFD_FLAG_WRITE_PROTECTED;
201 		status = STATUS_SUCCESS;
202 		break;
203 
204 	case IOCTL_VFD_RESET_MODIFY:
205 		//	Reset the data modify flag
206 
207 		if (!device_extension->FileHandle &&
208 			!device_extension->FileBuffer) {
209 			status = STATUS_NO_MEDIA_IN_DEVICE;
210 			break;
211 		}
212 
213 		device_extension->MediaFlags &= ~VFD_FLAG_DATA_MODIFIED;
214 		status = STATUS_SUCCESS;
215 		break;
216 
217 	case IOCTL_VFD_QUERY_NUMBER:
218 		//	Return VFD device number (\??\VirtualFD<n>)
219 
220 		if (IO_OUTPUTLEN(io_stack) < sizeof(ULONG)) {
221 			status = STATUS_BUFFER_TOO_SMALL;
222 			break;
223 		}
224 
225 		*(PULONG)Irp->AssociatedIrp.SystemBuffer=
226 			device_extension->DeviceNumber;
227 
228 		Irp->IoStatus.Information = sizeof(ULONG);
229 		status = STATUS_SUCCESS;
230 		break;
231 
232 	case IOCTL_VFD_QUERY_NAME:
233 		//	Return VFD device name (\Device\Floppy<n>)
234 		//	counted unicode string (not null terminated)
235 
236 		if (IO_OUTPUTLEN(io_stack) < sizeof(USHORT)) {
237 			status = STATUS_BUFFER_TOO_SMALL;
238 			break;
239 		}
240 
241 		{
242 			PUSHORT p = (PUSHORT)Irp->AssociatedIrp.SystemBuffer;
243 
244 			*p = device_extension->DeviceName.Length;
245 
246 			if (IO_OUTPUTLEN(io_stack) < sizeof(USHORT) + *p) {
247 
248 				Irp->IoStatus.Information = sizeof(USHORT);
249 				status = STATUS_BUFFER_OVERFLOW;
250 				break;
251 			}
252 
253 			RtlCopyMemory(p + 1, device_extension->DeviceName.Buffer, *p);
254 
255 			Irp->IoStatus.Information = sizeof(USHORT) + *p;
256 		}
257 
258 		status = STATUS_SUCCESS;
259 		break;
260 
261 	case IOCTL_VFD_QUERY_VERSION:
262 		//	Return the VFD driver version
263 
264 		if (IO_OUTPUTLEN(io_stack) < sizeof(ULONG)) {
265 			status = STATUS_BUFFER_TOO_SMALL;
266 			break;
267 		}
268 
269 		*(PULONG)Irp->AssociatedIrp.SystemBuffer =
270 			(VFD_DRIVER_MAJOR << 16) | VFD_DRIVER_MINOR | VFD_DEBUG_FLAG;
271 
272 		Irp->IoStatus.Information = sizeof(ULONG);
273 		status = STATUS_SUCCESS;
274 		break;
275 
276 	//
277 	//	standard disk and storage I/O control requests
278 	//
279 
280 	case IOCTL_DISK_CHECK_VERIFY:
281 	case IOCTL_STORAGE_CHECK_VERIFY:
282 	case IOCTL_STORAGE_CHECK_VERIFY2:
283 
284 		if (IO_OUTPUTLEN(io_stack) >= sizeof(ULONG)) {
285 
286 			*(PULONG)Irp->AssociatedIrp.SystemBuffer =
287 				device_extension->MediaChangeCount;
288 
289 			Irp->IoStatus.Information = sizeof(ULONG);
290 		}
291 
292 		status = STATUS_SUCCESS;
293 		break;
294 
295 	case IOCTL_DISK_FORMAT_TRACKS:
296 	case IOCTL_DISK_FORMAT_TRACKS_EX:
297 		//	Only parameter checks are performed here
298 		//	Actual operation is done by the device thread
299 
300 		status = VfdFormatCheck(
301 			device_extension,
302 			(PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer,
303 			IO_INPUTLEN(io_stack),
304 			IO_CTRLCODE(io_stack));
305 
306 		if (!NT_SUCCESS(status)) {
307 			break;
308 		}
309 
310 		// Pass the task to the device thread
311 		status = STATUS_PENDING;
312 		break;
313 
314 	case IOCTL_DISK_GET_DRIVE_GEOMETRY:
315 		//	Returns the geometry of current media
316 
317 		if (!device_extension->FileHandle &&
318 			!device_extension->FileBuffer) {
319 			status = STATUS_NO_MEDIA_IN_DEVICE;
320 			break;
321 		}
322 		//	fall through
323 
324 	case IOCTL_DISK_GET_MEDIA_TYPES:
325 	case IOCTL_STORAGE_GET_MEDIA_TYPES:
326 		//	Return *the last mounted* disk geometry, although xxx_GET_MEDIA_TYPES
327 		//	commands are supposed to return all supported media types.
328 		//	This makes the matter much simpler...;-)
329 		//	If no image has been mounted yet, 1.44MB media is assumed.
330 
331 		if (IO_OUTPUTLEN(io_stack) < sizeof(DISK_GEOMETRY)) {
332 			return STATUS_BUFFER_TOO_SMALL;
333 		}
334 
335 		//	Copy appropriate DISK_GEOMETRY into the output buffer
336 
337 		if (device_extension->Geometry) {
338 			RtlCopyMemory(
339 				Irp->AssociatedIrp.SystemBuffer,
340 				device_extension->Geometry,
341 				sizeof(DISK_GEOMETRY));
342 		}
343 		else {
344 			//	default = 3.5" 1.44 MB media
345 			RtlCopyMemory(
346 				Irp->AssociatedIrp.SystemBuffer,
347 				&geom_tbl[VFD_MEDIA_F3_1P4],
348 				sizeof(DISK_GEOMETRY));
349 		}
350 		Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
351 
352 		status = STATUS_SUCCESS;
353 		break;
354 
355 	case IOCTL_DISK_GET_LENGTH_INFO:
356 		//	Return disk length information
357 		//	(Windows XP requires this request to be handled)
358 
359 		if (!device_extension->FileHandle &&
360 			!device_extension->FileBuffer) {
361 			status = STATUS_NO_MEDIA_IN_DEVICE;
362 			break;
363 		}
364 
365 		if (IO_OUTPUTLEN(io_stack) < sizeof(GET_LENGTH_INFORMATION)) {
366 			status = STATUS_BUFFER_TOO_SMALL;
367 			break;
368 		}
369 
370 		((PGET_LENGTH_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->Length.QuadPart =
371 			VFD_SECTOR_TO_BYTE(device_extension->Sectors);
372 
373 		Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
374 
375 		status = STATUS_SUCCESS;
376 		break;
377 
378 	case IOCTL_DISK_IS_WRITABLE:
379 		//	Checks if current media is writable
380 
381 		if (!device_extension->FileHandle &&
382 			!device_extension->FileBuffer) {
383 			status = STATUS_NO_MEDIA_IN_DEVICE;
384 		}
385 		else if (device_extension->MediaFlags & VFD_FLAG_WRITE_PROTECTED) {
386 			status = STATUS_MEDIA_WRITE_PROTECTED;
387 		}
388 		else {
389 			status = STATUS_SUCCESS;
390 		}
391 		break;
392 
393 /*
394 	case IOCTL_DISK_MEDIA_REMOVAL:
395 	case IOCTL_STORAGE_MEDIA_REMOVAL:
396 		//	Since removal lock is irrelevant for virtual disks,
397 		//	there's really nothing to do here...
398 
399 		status = STATUS_SUCCESS;
400 		break;
401 
402 	case IOCTL_STORAGE_GET_HOTPLUG_INFO:
403 		{
404 			PSTORAGE_HOTPLUG_INFO hotplug;
405 
406 			if (IO_OUTPUTLEN(io_stack) < sizeof(STORAGE_HOTPLUG_INFO)) {
407 				status = STATUS_BUFFER_TOO_SMALL;
408 				break;
409 			}
410 
411 			hotplug = (PSTORAGE_HOTPLUG_INFO)Irp->AssociatedIrp.SystemBuffer;
412 
413 			RtlZeroMemory(hotplug, sizeof(STORAGE_HOTPLUG_INFO));
414 
415 			hotplug->Size = sizeof(STORAGE_HOTPLUG_INFO);
416 			hotplug->MediaRemovable = 1;
417 
418 			Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
419 			status = STATUS_SUCCESS;
420 		}
421 		break;
422 */
423 
424 #ifdef VFD_MOUNT_MANAGER
425 	//
426 	//	IO control requests received from the mount manager
427 	//	(on Windows 2000 / XP)
428 	//
429 
430 	case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
431 		//	Returns a unique ID for the target device
432 		status = VfdMountDevUniqueId(
433 			device_extension,
434 			Irp->AssociatedIrp.SystemBuffer,
435 			IO_OUTPUTLEN(io_stack),
436 			&Irp->IoStatus);
437 		break;
438 
439 //	case IOCTL_MOUNTDEV_UNIQUE_ID_CHANGE_NOTIFY:
440 
441 	case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
442 		//	Returns the device name of the target device
443 		status = VfdMountDevDeviceName(
444 			device_extension,
445 			Irp->AssociatedIrp.SystemBuffer,
446 			IO_OUTPUTLEN(io_stack),
447 			&Irp->IoStatus);
448 		break;
449 
450 	case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
451 		//	Returns the drive letter link which we want the mount manager
452 		//	to create.	This request is issued in response to the volume
453 		//	arrival notification, and the mount manager will create the
454 		//	symbolic link.
455 		status = VfdMountDevSuggestedLink(
456 			device_extension,
457 			Irp->AssociatedIrp.SystemBuffer,
458 			IO_OUTPUTLEN(io_stack),
459 			&Irp->IoStatus);
460 		break;
461 
462 	case IOCTL_MOUNTDEV_LINK_CREATED:
463 	case IOCTL_MOUNTDEV_LINK_DELETED:
464 		//	Issued after the mount manager created/deleted a symbolic link
465 		status = VfdMountDevLinkModified(
466 			device_extension,
467 			Irp->AssociatedIrp.SystemBuffer,
468 			IO_INPUTLEN(io_stack),
469 			IO_CTRLCODE(io_stack));
470 		break;
471 
472 /*
473 	case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
474 		{
475 			PMOUNTDEV_STABLE_GUID guid;
476 
477 			if (IO_OUTPUTLEN(io_stack) < sizeof(MOUNTDEV_STABLE_GUID)) {
478 				status = STATUS_INVALID_PARAMETER;
479 				break;
480 			}
481 
482 			guid = Irp->AssociatedIrp.SystemBuffer;
483 
484 			RtlCopyMemory(
485 				&guid->StableGuid, &VFD_GUID, sizeof(GUID));
486 
487 			Irp->IoStatus.Information = sizeof(guid);
488 			status = STATUS_SUCCESS;
489 		}
490 		break;
491 */
492 #endif	//	VFD_MOUNT_MANAGER
493 
494 	default:
495 		//	Unknown IOCTL request
496 		status = STATUS_INVALID_DEVICE_REQUEST;
497 		break;
498 	}
499 
500 #if DBG
501 	if ((NT_SUCCESS(status) && (TraceFlags & VFDINFO) == VFDINFO) ||
502 		(TraceFlags & VFDWARN) == VFDWARN) {
503 		VFDTRACE(0,("[VFD] %-40s - %s\n",
504 			GetIoControlName(IO_CTRLCODE(io_stack)),
505 			GetStatusName(status)));
506 	}
507 #endif
508 
509 	if (status == STATUS_PENDING) {
510 		//	Let the device thread perform the operation
511 
512 		IoMarkIrpPending(Irp);
513 
514 		ExInterlockedInsertTailList(
515 			&device_extension->ListHead,
516 			&Irp->Tail.Overlay.ListEntry,
517 			&device_extension->ListLock);
518 
519 		KeSetEvent(
520 			&device_extension->RequestEvent,
521 			(KPRIORITY) 0,
522 			FALSE);
523 	}
524 	else {
525 		//	complete the operation
526 
527 		Irp->IoStatus.Status = status;
528 		IoCompleteRequest(Irp, IO_NO_INCREMENT);
529 
530 #ifdef VFD_PNP
531 		IoReleaseRemoveLock(&device_extension->RemoveLock, Irp);
532 #endif	// VFD_PNP
533 	}
534 
535 	return status;
536 }
537 
538 //
539 //	Handle IO control requests in the device thread
540 //
541 VOID
542 VfdIoCtlThread(
543 	IN	PDEVICE_EXTENSION		DeviceExtension,
544 	IN	PIRP					Irp,
545 	IN	ULONG					ControlCode)
546 {
547 	switch (ControlCode) {
548 	case IOCTL_VFD_OPEN_IMAGE:
549 		//	open the file from the caller's security context
550 		//	-- this allows this driver to open network files
551 		if (DeviceExtension->SecurityContext) {
552 			SeImpersonateClient(DeviceExtension->SecurityContext, NULL);
553 		}
554 
555 		Irp->IoStatus.Status = VfdOpenImage(DeviceExtension,
556 			(PVFD_IMAGE_INFO)Irp->AssociatedIrp.SystemBuffer);
557 
558 		PsRevertToSelf();
559 		break;
560 
561 	case IOCTL_VFD_CLOSE_IMAGE:
562 	case IOCTL_DISK_EJECT_MEDIA:
563 	case IOCTL_STORAGE_EJECT_MEDIA:
564 		VfdCloseImage(DeviceExtension);
565 		Irp->IoStatus.Status = STATUS_SUCCESS;
566 		break;
567 
568 	case IOCTL_DISK_FORMAT_TRACKS:
569 	case IOCTL_DISK_FORMAT_TRACKS_EX:
570 		Irp->IoStatus.Status = VfdFormatTrack(DeviceExtension,
571 			(PFORMAT_PARAMETERS)Irp->AssociatedIrp.SystemBuffer);
572 		break;
573 
574 	default:
575 		//	This shouldn't happen...
576 		VFDTRACE(0,
577 			("[VFD] %s passed to the device thread\n",
578 			GetIoControlName(ControlCode)));
579 
580 		Irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
581 	}
582 
583 #if DBG
584 	if ((NT_SUCCESS(Irp->IoStatus.Status) && (TraceFlags & VFDINFO) == VFDINFO) ||
585 		(TraceFlags & VFDWARN) == VFDWARN) {
586 		VFDTRACE(0,("[VFD] %-40s - %s\n",
587 			GetIoControlName(ControlCode),
588 			GetStatusName(Irp->IoStatus.Status)));
589 	}
590 #endif
591 }
592