xref: /reactos/modules/rosapps/drivers/vfd/vfdimg.c (revision 80733143)
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