1 /* 2 vfdimg.c 3 4 Virtual Floppy Drive for Windows NT platform 5 Kernel mode driver: Image handling functions 6 7 Copyright (C) 2003-2005 Ken Kato 8 */ 9 10 #include "imports.h" 11 #include "vfddrv.h" 12 #include "vfddbg.h" 13 14 #ifdef ALLOC_PRAGMA 15 #pragma alloc_text(PAGE, VfdOpenCheck) 16 #pragma alloc_text(PAGE, VfdOpenImage) 17 #pragma alloc_text(PAGE, VfdCloseImage) 18 #pragma alloc_text(PAGE, VfdQueryImage) 19 #endif // ALLOC_PRAGMA 20 21 // 22 // Check IOCTL_VFD_OPEN_IMAGE input parameters 23 // 24 NTSTATUS 25 VfdOpenCheck( 26 PDEVICE_EXTENSION DeviceExtension, 27 PVFD_IMAGE_INFO ImageInfo, 28 ULONG InputLength) 29 { 30 // Check media status 31 32 if (DeviceExtension->FileHandle || 33 DeviceExtension->FileBuffer) { 34 35 VFDTRACE(VFDWARN, ("[VFD] image already opened.\n")); 36 37 return STATUS_DEVICE_BUSY; 38 } 39 40 // Check input parameter length 41 42 if (InputLength < sizeof(VFD_IMAGE_INFO) || 43 InputLength < sizeof(VFD_IMAGE_INFO) + ImageInfo->NameLength) 44 { 45 return STATUS_INVALID_PARAMETER; 46 } 47 48 // Check input parameters 49 50 if (ImageInfo->MediaType == VFD_MEDIA_NONE || 51 ImageInfo->MediaType >= VFD_MEDIA_MAX) { 52 53 VFDTRACE(VFDWARN, ("[VFD] invalid MediaType - %u.\n", 54 ImageInfo->MediaType)); 55 56 return STATUS_INVALID_PARAMETER; 57 } 58 59 if (ImageInfo->DiskType == VFD_DISKTYPE_FILE && 60 ImageInfo->NameLength == 0) { 61 62 VFDTRACE(VFDWARN, 63 ("[VFD] File name required for VFD_DISKTYPE_FILE.\n")); 64 65 return STATUS_INVALID_PARAMETER; 66 } 67 68 // create a security context to match the calling process' context 69 // the driver thread uses this context to impersonate the client 70 // to open the specified image file 71 72 // if (ImageInfo->DiskType == VFD_DISKTYPE_FILE) 73 { 74 SECURITY_QUALITY_OF_SERVICE sqos; 75 76 if (DeviceExtension->SecurityContext != NULL) { 77 SeDeleteClientSecurity(DeviceExtension->SecurityContext); 78 } 79 else { 80 DeviceExtension->SecurityContext = 81 (PSECURITY_CLIENT_CONTEXT)ExAllocatePoolWithTag( 82 NonPagedPool, sizeof(SECURITY_CLIENT_CONTEXT), VFD_POOL_TAG); 83 } 84 85 RtlZeroMemory(&sqos, sizeof(SECURITY_QUALITY_OF_SERVICE)); 86 87 sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); 88 sqos.ImpersonationLevel = SecurityImpersonation; 89 sqos.ContextTrackingMode = SECURITY_STATIC_TRACKING; 90 sqos.EffectiveOnly = FALSE; 91 92 SeCreateClientSecurity( 93 PsGetCurrentThread(), &sqos, FALSE, 94 DeviceExtension->SecurityContext); 95 } 96 97 return STATUS_SUCCESS; 98 } 99 100 // 101 // Open a virtual floppy image file or create an empty ram disk 102 // 103 NTSTATUS 104 VfdOpenImage ( 105 IN PDEVICE_EXTENSION DeviceExtension, 106 IN PVFD_IMAGE_INFO ImageInfo) 107 { 108 IO_STATUS_BLOCK io_status; 109 NTSTATUS status = STATUS_SUCCESS; 110 const DISK_GEOMETRY *geometry; 111 ULONG sectors; 112 ULONG alignment; 113 114 VFDTRACE(0, ("[VFD] VfdOpenImage - IN\n")); 115 116 // 117 // Store file name in the device extension 118 // 119 if (ImageInfo->NameLength) { 120 121 if (ImageInfo->NameLength + 1 > 122 DeviceExtension->FileName.MaximumLength) { 123 124 // expand the filename buffer 125 126 if (DeviceExtension->FileName.Buffer) { 127 ExFreePool(DeviceExtension->FileName.Buffer); 128 RtlZeroMemory( 129 &DeviceExtension->FileName, 130 sizeof(ANSI_STRING)); 131 } 132 133 DeviceExtension->FileName.Buffer = (PCHAR)ExAllocatePoolWithTag( 134 NonPagedPool, ImageInfo->NameLength + 1, VFD_POOL_TAG); 135 136 if (!DeviceExtension->FileName.Buffer) { 137 VFDTRACE(0, ("[VFD] Can't allocate memory for image path\n")); 138 return STATUS_INSUFFICIENT_RESOURCES; 139 } 140 141 DeviceExtension->FileName.MaximumLength 142 = (USHORT)(ImageInfo->NameLength + 1); 143 144 RtlZeroMemory( 145 DeviceExtension->FileName.Buffer, 146 DeviceExtension->FileName.MaximumLength); 147 } 148 149 if (DeviceExtension->FileName.Buffer) { 150 RtlCopyMemory( 151 DeviceExtension->FileName.Buffer, 152 ImageInfo->FileName, 153 ImageInfo->NameLength); 154 155 DeviceExtension->FileName.Buffer[ImageInfo->NameLength] = '\0'; 156 } 157 } 158 159 DeviceExtension->FileName.Length = ImageInfo->NameLength; 160 161 // 162 // Get DISK_GEOMETRY and calculate the media capacity 163 // -- validity of the ImageInfo->MediaType value is assured in 164 // the VfdOpenCheck function 165 // 166 geometry = &geom_tbl[ImageInfo->MediaType]; 167 168 sectors = 169 geometry->Cylinders.LowPart * 170 geometry->TracksPerCylinder * 171 geometry->SectorsPerTrack; 172 173 if (ImageInfo->ImageSize != 0 && 174 ImageInfo->ImageSize < VFD_SECTOR_TO_BYTE(sectors)) { 175 176 VFDTRACE(0, ("[VFD] Image is smaller than the media\n")); 177 return STATUS_INVALID_PARAMETER; 178 } 179 180 // 181 // Prepare a virtual media according to the ImageInfo 182 // 183 if (ImageInfo->DiskType == VFD_DISKTYPE_FILE) { 184 // 185 // open an existing image file 186 // 187 HANDLE file_handle; 188 OBJECT_ATTRIBUTES attributes; 189 UNICODE_STRING unicode_name; 190 FILE_STANDARD_INFORMATION file_standard; 191 FILE_BASIC_INFORMATION file_basic; 192 FILE_ALIGNMENT_INFORMATION file_alignment; 193 PFILE_OBJECT file_object; 194 BOOLEAN network_drive; 195 196 // convert the filename into a unicode string 197 198 status = RtlAnsiStringToUnicodeString( 199 &unicode_name, &DeviceExtension->FileName, TRUE); 200 201 if (!NT_SUCCESS(status)) { 202 VFDTRACE(0, ("[VFD] Failed to convert filename to UNICODE\n")); 203 return status; 204 } 205 206 VFDTRACE(VFDINFO, 207 ("[VFD] Opening %s\n", DeviceExtension->FileName.Buffer)); 208 209 // prepare an object attribute to open 210 211 InitializeObjectAttributes( 212 &attributes, 213 &unicode_name, 214 OBJ_CASE_INSENSITIVE, 215 NULL, 216 NULL); 217 218 // open the target file 219 220 status = ZwCreateFile( 221 &file_handle, 222 GENERIC_READ | GENERIC_WRITE, 223 &attributes, 224 &io_status, 225 NULL, 226 FILE_ATTRIBUTE_NORMAL, 227 0, 228 FILE_OPEN, 229 FILE_NON_DIRECTORY_FILE | 230 FILE_RANDOM_ACCESS | 231 FILE_NO_INTERMEDIATE_BUFFERING | 232 FILE_SYNCHRONOUS_IO_NONALERT, 233 NULL, 234 0); 235 236 RtlFreeUnicodeString(&unicode_name); 237 238 if (!NT_SUCCESS(status)) { 239 VFDTRACE(0, ("[VFD] ZwCreateFile - %s\n", 240 GetStatusName(status))); 241 return status; 242 } 243 244 // Check the file size 245 246 status = ZwQueryInformationFile( 247 file_handle, 248 &io_status, 249 &file_standard, 250 sizeof(FILE_STANDARD_INFORMATION), 251 FileStandardInformation); 252 253 if (!NT_SUCCESS(status)) { 254 VFDTRACE(0, 255 ("[VFD] ZwQueryInformationFile - FILE_STANDARD_INFORMATION\n")); 256 257 ZwClose(file_handle); 258 goto exit_func; 259 } 260 261 // Actual file size can be larger than the media capacity 262 263 if (file_standard.EndOfFile.QuadPart < VFD_SECTOR_TO_BYTE(sectors)) { 264 265 VFDTRACE(0, ("[VFD] file is smaller than the media.\n")); 266 267 status = STATUS_INVALID_PARAMETER; 268 269 ZwClose(file_handle); 270 goto exit_func; 271 } 272 273 DeviceExtension->ImageSize = file_standard.EndOfFile.LowPart; 274 275 // Find out whether the file is on a local disk or a network drive 276 277 network_drive = FALSE; 278 279 status = ObReferenceObjectByHandle( 280 file_handle, 281 GENERIC_READ, 282 NULL, 283 KernelMode, 284 #ifndef __REACTOS__ 285 &file_object, 286 #else 287 (PVOID *)&file_object, 288 #endif 289 NULL); 290 291 if (NT_SUCCESS(status)) { 292 if (file_object && file_object->DeviceObject) { 293 VFDTRACE(VFDINFO, ("[VFD] Device type is 0x%08x\n", 294 file_object->DeviceObject->DeviceType)); 295 296 if (file_object->DeviceObject->DeviceType 297 == FILE_DEVICE_NETWORK_FILE_SYSTEM) { 298 network_drive = TRUE; 299 } 300 301 // how about these types ? 302 // FILE_DEVICE_NETWORK 303 // FILE_DEVICE_NETWORK_BROWSER 304 // FILE_DEVICE_NETWORK_REDIRECTOR 305 } 306 else { 307 VFDTRACE(VFDWARN, ("[VFD Cannot decide the device type\n")); 308 } 309 ObDereferenceObject(file_object); 310 } 311 else { 312 VFDTRACE(0, ("[VFD] ObReferenceObjectByHandle - %s\n", 313 GetStatusName(status))); 314 } 315 316 if (!network_drive) { 317 // The NT cache manager can deadlock if a filesystem that is using 318 // the cache manager is used in a virtual disk that stores its file 319 // on a file systemthat is also using the cache manager, this is 320 // why we open the file with FILE_NO_INTERMEDIATE_BUFFERING above, 321 // however if the file is compressed or encrypted NT will not honor 322 // this request and cache it anyway since it need to store the 323 // decompressed/unencrypted data somewhere, therefor we put an 324 // extra check here and don't alow disk images to be compressed/ 325 // encrypted. 326 327 status = ZwQueryInformationFile( 328 file_handle, 329 &io_status, 330 &file_basic, 331 sizeof(FILE_BASIC_INFORMATION), 332 FileBasicInformation); 333 334 if (!NT_SUCCESS(status)) { 335 VFDTRACE(0, 336 ("[VFD] ZwQueryInformationFile - FILE_BASIC_INFORMATION\n")); 337 338 ZwClose(file_handle); 339 goto exit_func; 340 } 341 342 if (file_basic.FileAttributes 343 & (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED)) 344 { 345 VFDTRACE(0, 346 ("[VFD] Image file is compressed and/or encrypted\n")); 347 348 status = STATUS_ACCESS_DENIED; 349 350 ZwClose(file_handle); 351 goto exit_func; 352 } 353 } 354 355 // Retrieve the file alignment requirement 356 357 status = ZwQueryInformationFile( 358 file_handle, 359 &io_status, 360 &file_alignment, 361 sizeof(FILE_ALIGNMENT_INFORMATION), 362 FileAlignmentInformation); 363 364 if (!NT_SUCCESS(status)) { 365 VFDTRACE(0, 366 ("[VFD] ZwQueryInformationFile - FILE_ALIGNMENT_INFORMATION\n")); 367 368 ZwClose(file_handle); 369 goto exit_func; 370 } 371 372 DeviceExtension->FileHandle = file_handle; 373 374 alignment = file_alignment.AlignmentRequirement; 375 376 VFDTRACE(0, ("[VFD] Opened an image file\n")); 377 } 378 else { 379 // 380 // Create an empty RAM disk 381 // 382 DeviceExtension->FileBuffer = (PUCHAR)ExAllocatePoolWithTag( 383 NonPagedPool, 384 VFD_SECTOR_TO_BYTE(sectors), 385 VFD_POOL_TAG); 386 387 if (!DeviceExtension->FileBuffer) { 388 VFDTRACE(0, ("[VFD] Can't allocate memory for RAM disk\n")); 389 return STATUS_INSUFFICIENT_RESOURCES; 390 } 391 392 RtlZeroMemory( 393 DeviceExtension->FileBuffer, 394 VFD_SECTOR_TO_BYTE(sectors)); 395 396 if (ImageInfo->ImageSize) { 397 DeviceExtension->ImageSize = ImageInfo->ImageSize; 398 } 399 else { 400 DeviceExtension->ImageSize = VFD_SECTOR_TO_BYTE(sectors); 401 } 402 403 alignment = FILE_WORD_ALIGNMENT; 404 405 VFDTRACE(0, ("[VFD] Created an empty RAM disk\n")); 406 } 407 408 DeviceExtension->MediaChangeCount++; 409 410 DeviceExtension->MediaType = ImageInfo->MediaType; 411 DeviceExtension->MediaFlags = ImageInfo->MediaFlags; 412 DeviceExtension->FileType = ImageInfo->FileType; 413 DeviceExtension->Geometry = geometry; 414 DeviceExtension->Sectors = sectors; 415 416 VFDTRACE(0, ("[VFD] Media:%d Flag:0x%02x Size:%lu Capacity:%lu\n", 417 DeviceExtension->MediaType, 418 DeviceExtension->MediaFlags, 419 DeviceExtension->ImageSize, 420 DeviceExtension->Sectors)); 421 422 DeviceExtension->DeviceObject->AlignmentRequirement 423 = alignment; 424 425 exit_func: 426 VFDTRACE(0, ("[VFD] VfdOpenImage - %s\n", GetStatusName(status))); 427 428 return status; 429 } 430 431 // 432 // Close the current image 433 // 434 VOID 435 VfdCloseImage ( 436 IN PDEVICE_EXTENSION DeviceExtension) 437 { 438 VFDTRACE(0, ("[VFD] VfdCloseImage - IN\n")); 439 440 ASSERT(DeviceExtension); 441 442 DeviceExtension->MediaType = VFD_MEDIA_NONE; 443 DeviceExtension->MediaFlags = 0; 444 DeviceExtension->FileType = 0; 445 DeviceExtension->ImageSize = 0; 446 DeviceExtension->FileName.Length = 0; 447 DeviceExtension->Sectors = 0; 448 449 if (DeviceExtension->FileHandle) { 450 ZwClose(DeviceExtension->FileHandle); 451 DeviceExtension->FileHandle = NULL; 452 } 453 454 if (DeviceExtension->FileBuffer) { 455 ExFreePool(DeviceExtension->FileBuffer); 456 DeviceExtension->FileBuffer = NULL; 457 } 458 459 VFDTRACE(0, ("[VFD] VfdCloseImage - OUT\n")); 460 } 461 462 // 463 // Return information about the current image 464 // 465 NTSTATUS 466 VfdQueryImage( 467 IN PDEVICE_EXTENSION DeviceExtension, 468 OUT PVFD_IMAGE_INFO ImageInfo, 469 IN ULONG BufferLength, 470 #ifndef __REACTOS__ 471 OUT PULONG ReturnLength) 472 #else 473 OUT PSIZE_T ReturnLength) 474 #endif 475 { 476 // Check output buffer length 477 478 if (BufferLength < sizeof(VFD_IMAGE_INFO)) { 479 return STATUS_BUFFER_TOO_SMALL; 480 } 481 482 RtlZeroMemory(ImageInfo, BufferLength); 483 484 // Store fixed length image information 485 486 ImageInfo->MediaType = DeviceExtension->MediaType; 487 488 if (DeviceExtension->MediaType == VFD_MEDIA_NONE) { 489 *ReturnLength = sizeof(VFD_IMAGE_INFO); 490 return STATUS_SUCCESS; 491 } 492 493 if (DeviceExtension->FileBuffer) { 494 ImageInfo->DiskType = VFD_DISKTYPE_RAM; 495 } 496 else { 497 ImageInfo->DiskType = VFD_DISKTYPE_FILE; 498 } 499 500 ImageInfo->MediaFlags = DeviceExtension->MediaFlags; 501 ImageInfo->FileType = DeviceExtension->FileType; 502 ImageInfo->ImageSize = DeviceExtension->ImageSize; 503 504 ImageInfo->NameLength = DeviceExtension->FileName.Length; 505 506 // output buffer is large enough to hold the file name? 507 508 if (BufferLength < sizeof(VFD_IMAGE_INFO) + 509 DeviceExtension->FileName.Length) 510 { 511 *ReturnLength = sizeof(VFD_IMAGE_INFO); 512 return STATUS_BUFFER_OVERFLOW; 513 } 514 515 // copy file name 516 517 if (DeviceExtension->FileName.Length && 518 DeviceExtension->FileName.Buffer) { 519 520 RtlCopyMemory(ImageInfo->FileName, 521 DeviceExtension->FileName.Buffer, 522 DeviceExtension->FileName.Length); 523 } 524 525 // store the actually returned data length 526 527 *ReturnLength = sizeof(VFD_IMAGE_INFO) + 528 DeviceExtension->FileName.Length; 529 530 return STATUS_SUCCESS; 531 } 532