b6cabe04ab12aef9e93f84368393f9f7790298a4
[reactos.git] / modules / rosapps / drivers / vfd / vfdimg.c
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 OUT PULONG ReturnLength)
471 {
472 // Check output buffer length
473
474 if (BufferLength < sizeof(VFD_IMAGE_INFO)) {
475 return STATUS_BUFFER_TOO_SMALL;
476 }
477
478 RtlZeroMemory(ImageInfo, BufferLength);
479
480 // Store fixed length image information
481
482 ImageInfo->MediaType = DeviceExtension->MediaType;
483
484 if (DeviceExtension->MediaType == VFD_MEDIA_NONE) {
485 *ReturnLength = sizeof(VFD_IMAGE_INFO);
486 return STATUS_SUCCESS;
487 }
488
489 if (DeviceExtension->FileBuffer) {
490 ImageInfo->DiskType = VFD_DISKTYPE_RAM;
491 }
492 else {
493 ImageInfo->DiskType = VFD_DISKTYPE_FILE;
494 }
495
496 ImageInfo->MediaFlags = DeviceExtension->MediaFlags;
497 ImageInfo->FileType = DeviceExtension->FileType;
498 ImageInfo->ImageSize = DeviceExtension->ImageSize;
499
500 ImageInfo->NameLength = DeviceExtension->FileName.Length;
501
502 // output buffer is large enough to hold the file name?
503
504 if (BufferLength < sizeof(VFD_IMAGE_INFO) +
505 DeviceExtension->FileName.Length)
506 {
507 *ReturnLength = sizeof(VFD_IMAGE_INFO);
508 return STATUS_BUFFER_OVERFLOW;
509 }
510
511 // copy file name
512
513 if (DeviceExtension->FileName.Length &&
514 DeviceExtension->FileName.Buffer) {
515
516 RtlCopyMemory(ImageInfo->FileName,
517 DeviceExtension->FileName.Buffer,
518 DeviceExtension->FileName.Length);
519 }
520
521 // store the actually returned data length
522
523 *ReturnLength = sizeof(VFD_IMAGE_INFO) +
524 DeviceExtension->FileName.Length;
525
526 return STATUS_SUCCESS;
527 }