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