2 * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
4 * This file is part of WinVBlock, derived from WinAoE.
6 * WinVBlock is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * WinVBlock is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with WinVBlock. If not, see <http://www.gnu.org/licenses/>.
23 * File-backed disk specifics.
31 #include "winvblock.h"
32 #include "wv_stdlib.h"
43 static LIST_ENTRY filedisk_list;
44 static KSPIN_LOCK filedisk_list_lock;
46 /* Forward declarations. */
47 static WV_F_DEV_FREE free_filedisk;
54 disk__type_ptr disk_ptr;
55 filedisk__type_ptr filedisk_ptr;
58 IO_STATUS_BLOCK io_status;
61 * Establish pointers to the disk and filedisk
63 disk_ptr = disk__get_ptr ( dev_ptr );
64 filedisk_ptr = filedisk__get_ptr ( dev_ptr );
66 if ( sector_count < 1 )
71 DBG ( "sector_count < 1; cancelling\n" );
72 irp->IoStatus.Information = 0;
73 irp->IoStatus.Status = STATUS_CANCELLED;
74 IoCompleteRequest ( irp, IO_NO_INCREMENT );
75 return STATUS_CANCELLED;
78 * Calculate the offset
80 offset.QuadPart = start_sector * disk_ptr->SectorSize;
81 offset.QuadPart += filedisk_ptr->offset.QuadPart;
83 if ( mode == disk__io_mode_write )
85 ZwWriteFile ( filedisk_ptr->file, NULL, NULL, NULL, &io_status, buffer,
86 sector_count * disk_ptr->SectorSize, &offset, NULL );
89 ZwReadFile ( filedisk_ptr->file, NULL, NULL, NULL, &io_status, buffer,
90 sector_count * disk_ptr->SectorSize, &offset, NULL );
92 disk__guess_geometry((WV_AP_DISK_BOOT_SECT) buffer, disk_ptr);
93 irp->IoStatus.Information = sector_count * disk_ptr->SectorSize;
94 irp->IoStatus.Status = status;
95 IoCompleteRequest ( irp, IO_NO_INCREMENT );
99 static winvblock__uint32 STDCALL query_id(
101 IN BUS_QUERY_ID_TYPE query_type,
102 IN OUT WCHAR (*buf)[512]
104 disk__type_ptr disk = disk__get_ptr(dev);
105 filedisk__type_ptr filedisk = filedisk__get_ptr(dev);
106 static PWCHAR hw_ids[WvDiskMediaTypes] = {
107 winvblock__literal_w L"\\FileFloppyDisk",
108 winvblock__literal_w L"\\FileHardDisk",
109 winvblock__literal_w L"\\FileOpticalDisc"
112 switch (query_type) {
113 case BusQueryDeviceID:
114 return swprintf(*buf, hw_ids[disk->Media]) + 1;
116 case BusQueryInstanceID:
117 return swprintf(*buf, L"Hash_%08X", filedisk->hash) + 1;
119 case BusQueryHardwareIDs: {
120 winvblock__uint32 tmp;
122 tmp = swprintf(*buf, hw_ids[disk->Media]) + 1;
123 tmp += swprintf(*buf + tmp, WvDiskCompatIds[disk->Media]) + 4;
127 case BusQueryCompatibleIDs:
128 return swprintf(*buf, WvDiskCompatIds[disk->Media]) + 4;
135 NTSTATUS STDCALL filedisk__attach(IN WV_SP_DEV_T dev, IN PIRP irp) {
136 ANSI_STRING file_path1;
137 winvblock__uint8_ptr buf = irp->AssociatedIrp.SystemBuffer;
138 mount__filedisk_ptr params = ( mount__filedisk_ptr ) buf;
139 UNICODE_STRING file_path2;
140 OBJECT_ATTRIBUTES obj_attrs;
143 IO_STATUS_BLOCK io_status;
144 FILE_STANDARD_INFORMATION info;
145 filedisk__type_ptr filedisk_ptr;
147 filedisk_ptr = filedisk__create ( );
148 if ( filedisk_ptr == NULL )
150 DBG ( "Could not create file-backed disk!\n" );
151 return STATUS_INSUFFICIENT_RESOURCES;
154 RtlInitAnsiString ( &file_path1,
155 ( char * )&buf[sizeof ( mount__filedisk )] );
156 status = RtlAnsiStringToUnicodeString ( &file_path2, &file_path1, TRUE );
157 if ( !NT_SUCCESS ( status ) )
158 goto err_ansi_to_unicode;
159 InitializeObjectAttributes ( &obj_attrs, &file_path2,
160 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL,
163 * Open the file. The handle is closed by close()
166 ZwCreateFile ( &file, GENERIC_READ | GENERIC_WRITE, &obj_attrs, &io_status,
167 NULL, FILE_ATTRIBUTE_NORMAL,
168 FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
170 FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
171 FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
172 RtlFreeUnicodeString ( &file_path2 );
173 if ( !NT_SUCCESS ( status ) )
176 filedisk_ptr->file = file;
178 switch ( params->type )
181 filedisk_ptr->disk->Media = WvDiskMediaTypeFloppy;
182 filedisk_ptr->disk->SectorSize = 512;
185 filedisk_ptr->disk->Media = WvDiskMediaTypeOptical;
186 filedisk_ptr->disk->SectorSize = 2048;
189 filedisk_ptr->disk->Media = WvDiskMediaTypeHard;
190 filedisk_ptr->disk->SectorSize = 512;
193 DBG ( "File-backed disk is type: %d\n", filedisk_ptr->disk->Media );
195 * Determine the disk's size
198 ZwQueryInformationFile ( file, &io_status, &info, sizeof ( info ),
199 FileStandardInformation );
200 if ( !NT_SUCCESS ( status ) )
203 filedisk_ptr->disk->LBADiskSize =
204 info.EndOfFile.QuadPart / filedisk_ptr->disk->SectorSize;
205 filedisk_ptr->disk->Cylinders = params->cylinders;
206 filedisk_ptr->disk->Heads = params->heads;
207 filedisk_ptr->disk->Sectors = params->sectors;
209 * A really stupid "hash". RtlHashUnicodeString() would have been
210 * good, but is only available >= Windows XP
212 filedisk_ptr->hash = ( winvblock__uint32 ) filedisk_ptr->disk->LBADiskSize;
214 char *path_iterator = file_path1.Buffer;
216 while ( *path_iterator )
217 filedisk_ptr->hash += *path_iterator++;
219 /* Add the filedisk to the bus. */
220 if (!WvBusAddChild(driver__bus(), filedisk_ptr->disk->device)) {
221 status = STATUS_UNSUCCESSFUL;
224 return STATUS_SUCCESS;
235 free_filedisk ( filedisk_ptr->disk->device );
244 filedisk__type_ptr filedisk_ptr = filedisk__get_ptr ( disk_ptr->device );
245 ZwClose ( filedisk_ptr->file );
250 * Create a new file-backed disk
252 * @ret filedisk_ptr The address of a new filedisk, or NULL for failure
254 * See the header file for additional details
261 disk__type_ptr disk_ptr;
262 filedisk__type_ptr filedisk_ptr;
265 * Try to create a disk
267 disk_ptr = disk__create ( );
268 if ( disk_ptr == NULL )
271 * File-backed disk devices might be used for booting and should
272 * not be allocated from a paged memory pool
274 filedisk_ptr = wv_mallocz(sizeof *filedisk_ptr);
275 if ( filedisk_ptr == NULL )
278 * Track the new file-backed disk in our global list
280 ExInterlockedInsertTailList ( &filedisk_list, &filedisk_ptr->tracking,
281 &filedisk_list_lock );
283 * Populate non-zero device defaults
285 filedisk_ptr->disk = disk_ptr;
286 filedisk_ptr->prev_free = disk_ptr->device->Ops.Free;
287 disk_ptr->device->Ops.Free = free_filedisk;
288 disk_ptr->device->Ops.PnpId = query_id;
289 disk_ptr->disk_ops.io = io;
290 disk_ptr->disk_ops.close = close;
291 disk_ptr->ext = filedisk_ptr;
297 WvDevFree(disk_ptr->device);
304 * Initialize the global, file-backed disk-common environment.
306 * @ret ntstatus STATUS_SUCCESS or the NTSTATUS for a failure.
308 NTSTATUS filedisk__module_init(void) {
309 /* Initialize the global list of file-backed disks. */
310 InitializeListHead(&filedisk_list);
311 KeInitializeSpinLock(&filedisk_list_lock);
313 return STATUS_SUCCESS;
317 * Default file-backed disk deletion operation.
319 * @v dev_ptr Points to the file-backed disk device to delete.
321 static void STDCALL free_filedisk(IN WV_SP_DEV_T dev_ptr)
323 disk__type_ptr disk_ptr = disk__get_ptr(dev_ptr);
324 filedisk__type_ptr filedisk_ptr = filedisk__get_ptr(dev_ptr);
326 * Free the "inherited class".
328 filedisk_ptr->prev_free(dev_ptr);
330 * Track the file-backed disk deletion in our global list. Unfortunately,
331 * for now we have faith that a file-backed disk won't be deleted twice and
332 * result in a race condition. Something to keep in mind...
334 ExInterlockedRemoveHeadList(
335 filedisk_ptr->tracking.Blink,
339 wv_free(filedisk_ptr);
342 /* Threaded read/write request */
343 winvblock__def_struct ( thread_req )
345 LIST_ENTRY list_entry;
348 LONGLONG start_sector;
349 winvblock__uint32 sector_count;
350 winvblock__uint8_ptr buffer;
355 * A threaded, file-backed disk's worker thread
357 * @v StartContext Points to a file-backed disk
361 IN void *StartContext
364 filedisk__type_ptr filedisk_ptr = StartContext;
365 LARGE_INTEGER timeout;
369 * Wake up at least every second
371 timeout.QuadPart = -10000000LL;
373 * The read/write request processing loop
378 * Wait for work-to-do signal or the timeout
380 KeWaitForSingleObject ( &filedisk_ptr->signal, Executive, KernelMode,
382 KeResetEvent ( &filedisk_ptr->signal );
383 /* Are we being torn down? We abuse the device's Free() member. */
384 if (filedisk_ptr->disk->device->Ops.Free == NULL)
386 /* Process each read/write request in the list. */
388 ExInterlockedRemoveHeadList ( &filedisk_ptr->req_list,
389 &filedisk_ptr->req_list_lock ) )
393 req = CONTAINING_RECORD ( walker, thread_req, list_entry );
394 filedisk_ptr->sync_io ( req->dev_ptr, req->mode, req->start_sector,
395 req->sector_count, req->buffer, req->irp );
400 * Time to tear things down
402 free_filedisk ( filedisk_ptr->disk->device );
410 filedisk__type_ptr filedisk_ptr;
413 filedisk_ptr = filedisk__get_ptr ( dev_ptr );
415 * Allocate the request
417 req = wv_malloc(sizeof *req);
420 irp->IoStatus.Information = 0;
421 irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
422 IoCompleteRequest ( irp, IO_NO_INCREMENT );
423 return STATUS_INSUFFICIENT_RESOURCES;
426 * Remember the request
428 req->dev_ptr = dev_ptr;
430 req->start_sector = start_sector;
431 req->sector_count = sector_count;
432 req->buffer = buffer;
434 ExInterlockedInsertTailList ( &filedisk_ptr->req_list, &req->list_entry,
435 &filedisk_ptr->req_list_lock );
437 * Signal worker thread and return
439 KeSetEvent ( &filedisk_ptr->signal, 0, FALSE );
440 return STATUS_PENDING;
444 * Threaded, file-backed disk deletion operation.
446 * @v dev_ptr Points to the file-backed disk device to delete.
448 static void STDCALL free_threaded_filedisk(IN WV_SP_DEV_T dev_ptr)
451 * Queue the tear-down and return. The thread will catch this on timeout.
453 dev_ptr->Ops.Free = NULL;
457 * Create a new threaded, file-backed disk
459 * @ret filedisk_ptr The address of a new filedisk, or NULL for failure
461 * See the header file for additional details
464 filedisk__create_threaded (
468 filedisk__type_ptr filedisk_ptr;
469 OBJECT_ATTRIBUTES obj_attrs;
470 HANDLE thread_handle;
473 * Try to create a filedisk
475 filedisk_ptr = filedisk__create ( );
476 if ( filedisk_ptr == NULL )
479 * Use threaded routines
481 filedisk_ptr->sync_io = io;
482 filedisk_ptr->disk->disk_ops.io = threaded_io;
483 filedisk_ptr->disk->device->Ops.Free = free_threaded_filedisk;
485 * Initialize threading parameters and start the filedisk's thread
487 InitializeListHead ( &filedisk_ptr->req_list );
488 KeInitializeSpinLock ( &filedisk_ptr->req_list_lock );
489 KeInitializeEvent ( &filedisk_ptr->signal, SynchronizationEvent, FALSE );
490 InitializeObjectAttributes ( &obj_attrs, NULL, OBJ_KERNEL_HANDLE, NULL,
492 PsCreateSystemThread ( &thread_handle, THREAD_ALL_ACCESS, &obj_attrs, NULL,
493 NULL, thread, filedisk_ptr );
503 * Find and hot-swap to a backing file
505 * @v filedisk_ptr Points to the filedisk needing to hot-swap
506 * @ret TRUE once the new file has been established, or FALSE
508 * We search all filesystems for a particular filename, then swap
509 * to using it as the backing store. This is currently useful for
510 * sector-mapped disks which should really have a file-in-use lock
511 * for the file they represent. Once the backing file is established,
512 * we return TRUE. Otherwise, we return FALSE and the hot-swap thread
515 static winvblock__bool STDCALL
517 filedisk__type_ptr filedisk_ptr
521 GUID vol_guid = GUID_DEVINTERFACE_VOLUME;
526 * Find the backing volume and use it. We walk a list
527 * of unicode volume device names and check each one for the file
529 status = IoGetDeviceInterfaces ( &vol_guid, NULL, 0, &sym_links );
530 if ( !NT_SUCCESS ( status ) )
533 status = STATUS_UNSUCCESSFUL;
534 while ( *pos != UNICODE_NULL )
537 PFILE_OBJECT vol_file_obj;
538 PDEVICE_OBJECT vol_dev_obj;
539 UNICODE_STRING vol_dos_name;
540 UNICODE_STRING filepath;
541 static const WCHAR obj_path_prefix[] = L"\\??\\";
542 static const WCHAR path_sep = L'\\';
543 OBJECT_ATTRIBUTES obj_attrs;
545 IO_STATUS_BLOCK io_status;
547 RtlInitUnicodeString ( &path, pos );
549 * Get some object pointers for the volume
552 IoGetDeviceObjectPointer ( &path, FILE_READ_DATA, &vol_file_obj,
554 if ( !NT_SUCCESS ( status ) )
559 vol_dos_name.Buffer = NULL;
560 vol_dos_name.Length = vol_dos_name.MaximumLength = 0;
562 RtlVolumeDeviceToDosName ( vol_file_obj->DeviceObject, &vol_dos_name );
563 if ( !NT_SUCCESS ( status ) )
566 * Build the file path. Ugh, what a mess
568 filepath.Length = filepath.MaximumLength =
569 sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL ) +
570 vol_dos_name.Length + sizeof ( path_sep ) +
571 filedisk_ptr->filepath_unicode.Length;
572 filepath.Buffer = wv_malloc(filepath.Length);
573 if ( filepath.Buffer == NULL )
575 status = STATUS_UNSUCCESSFUL;
579 char *buf = ( char * )filepath.Buffer;
581 RtlCopyMemory ( buf, obj_path_prefix,
582 sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL ) );
583 buf += sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL );
584 RtlCopyMemory ( buf, vol_dos_name.Buffer, vol_dos_name.Length );
585 buf += vol_dos_name.Length;
586 RtlCopyMemory ( buf, &path_sep, sizeof ( path_sep ) );
587 buf += sizeof ( path_sep );
588 RtlCopyMemory ( buf, filedisk_ptr->filepath_unicode.Buffer,
589 filedisk_ptr->filepath_unicode.Length );
591 InitializeObjectAttributes ( &obj_attrs, &filepath,
592 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
595 * Look for the file on this volume
598 ZwCreateFile ( &file, GENERIC_READ | GENERIC_WRITE, &obj_attrs,
599 &io_status, NULL, FILE_ATTRIBUTE_NORMAL,
600 FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
602 FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
603 FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
604 if ( !NT_SUCCESS ( status ) )
607 * We could open it. Do the hot-swap
610 HANDLE old = filedisk_ptr->file;
612 filedisk_ptr->file = 0;
613 filedisk_ptr->offset.QuadPart = 0;
614 filedisk_ptr->file = file;
617 RtlFreeUnicodeString ( &filedisk_ptr->filepath_unicode );
618 filedisk_ptr->filepath_unicode.Length = 0;
622 wv_free(filepath.Buffer);
625 wv_free(vol_dos_name.Buffer);
628 ObDereferenceObject ( vol_file_obj );
631 * Walk to the next terminator
633 while ( *pos != UNICODE_NULL )
636 * If everything succeeded, stop. Otherwise try the next volume
638 if ( !NT_SUCCESS ( status ) )
642 return NT_SUCCESS ( status ) ? TRUE : FALSE;
646 filedisk__hot_swap_thread (
647 IN void *StartContext
650 filedisk__type_ptr filedisk_ptr = StartContext;
652 LARGE_INTEGER timeout;
654 KeInitializeEvent ( &signal, SynchronizationEvent, FALSE );
656 * Wake up at least every second
658 timeout.QuadPart = -10000000LL;
665 * Wait for work-to-do signal or the timeout
667 KeWaitForSingleObject ( &signal, Executive, KernelMode, FALSE,
669 if ( filedisk_ptr->file == NULL )
672 * Are we supposed to hot-swap to a file? Check ANSI filepath
674 if ( filedisk_ptr->filepath != NULL )
679 RtlInitAnsiString ( &tmp, filedisk_ptr->filepath );
680 filedisk_ptr->filepath_unicode.Buffer = NULL;
682 RtlAnsiStringToUnicodeString ( &filedisk_ptr->filepath_unicode,
684 if ( NT_SUCCESS ( status ) )
686 wv_free(filedisk_ptr->filepath);
687 filedisk_ptr->filepath = NULL;
691 * Are we supposed to hot-swap to a file? Check unicode filepath
693 if ( filedisk_ptr->filepath_unicode.Length )
694 if ( hot_swap ( filedisk_ptr ) )