2bf43895d577f431a5561e88a560a17f618a8a5d
[people/sha0/winvblock.git] / src / winvblock / filedisk / filedisk.c
1 /**
2  * Copyright (C) 2009-2011, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  *
4  * This file is part of WinVBlock, derived from WinAoE.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 /**
21  * @file
22  *
23  * File-backed disk specifics.
24  */
25
26 #include <stdio.h>
27 #include <ntddk.h>
28 #include <initguid.h>
29 #include <ntddstor.h>
30
31 #include "portable.h"
32 #include "winvblock.h"
33 #include "wv_stdlib.h"
34 #include "driver.h"
35 #include "bus.h"
36 #include "device.h"
37 #include "disk.h"
38 #include "mount.h"
39 #include "filedisk.h"
40 #include "debug.h"
41
42 /* Globals. */
43 static LIST_ENTRY filedisk_list;
44 static KSPIN_LOCK filedisk_list_lock;
45
46 /* Forward declarations. */
47 static WV_F_DEV_FREE free_filedisk;
48 static WV_F_DISK_IO io;
49 static WV_F_DISK_IO threaded_io;
50 static WV_F_DISK_CLOSE close;
51
52 static NTSTATUS STDCALL io(
53     IN WV_SP_DEV_T dev_ptr,
54     IN WV_E_DISK_IO_MODE mode,
55     IN LONGLONG start_sector,
56     IN UINT32 sector_count,
57     IN PUCHAR buffer,
58     IN PIRP irp
59   ) {
60   WV_SP_DISK_T disk_ptr;
61   WV_SP_FILEDISK_T filedisk_ptr;
62   LARGE_INTEGER offset;
63   NTSTATUS status;
64   IO_STATUS_BLOCK io_status;
65
66   /*
67    * Establish pointers to the disk and filedisk
68    */
69   disk_ptr = disk__get_ptr ( dev_ptr );
70   filedisk_ptr = filedisk__get_ptr ( dev_ptr );
71
72   if ( sector_count < 1 )
73     {
74       /*
75        * A silly request 
76        */
77       DBG ( "sector_count < 1; cancelling\n" );
78       irp->IoStatus.Information = 0;
79       irp->IoStatus.Status = STATUS_CANCELLED;
80       IoCompleteRequest ( irp, IO_NO_INCREMENT );
81       return STATUS_CANCELLED;
82     }
83   /*
84    * Calculate the offset
85    */
86   offset.QuadPart = start_sector * disk_ptr->SectorSize;
87   offset.QuadPart += filedisk_ptr->offset.QuadPart;
88
89   if (mode == WvDiskIoModeWrite)
90     status =
91       ZwWriteFile ( filedisk_ptr->file, NULL, NULL, NULL, &io_status, buffer,
92                     sector_count * disk_ptr->SectorSize, &offset, NULL );
93   else
94     status =
95       ZwReadFile ( filedisk_ptr->file, NULL, NULL, NULL, &io_status, buffer,
96                    sector_count * disk_ptr->SectorSize, &offset, NULL );
97   if ( !start_sector )
98     disk__guess_geometry((WV_AP_DISK_BOOT_SECT) buffer, disk_ptr);
99   irp->IoStatus.Information = sector_count * disk_ptr->SectorSize;
100   irp->IoStatus.Status = status;
101   IoCompleteRequest ( irp, IO_NO_INCREMENT );
102   return status;
103 }
104
105 static UINT32 STDCALL query_id(
106     IN WV_SP_DEV_T dev,
107     IN BUS_QUERY_ID_TYPE query_type,
108     IN OUT WCHAR (*buf)[512]
109   ) {
110     WV_SP_DISK_T disk = disk__get_ptr(dev);
111     WV_SP_FILEDISK_T filedisk = filedisk__get_ptr(dev);
112     static PWCHAR hw_ids[WvDiskMediaTypes] = {
113         WVL_M_WLIT L"\\FileFloppyDisk",
114         WVL_M_WLIT L"\\FileHardDisk",
115         WVL_M_WLIT L"\\FileOpticalDisc"
116       };
117
118     switch (query_type) {
119         case BusQueryDeviceID:
120           return swprintf(*buf, hw_ids[disk->Media]) + 1;
121
122         case BusQueryInstanceID:
123           return swprintf(*buf, L"Hash_%08X", filedisk->hash) + 1;
124
125         case BusQueryHardwareIDs: {
126             UINT32 tmp;
127
128             tmp = swprintf(*buf, hw_ids[disk->Media]) + 1;
129             tmp += swprintf(*buf + tmp, WvDiskCompatIds[disk->Media]) + 4;
130             return tmp;
131           }
132
133         case BusQueryCompatibleIDs:
134           return swprintf(*buf, WvDiskCompatIds[disk->Media]) + 4;
135
136         default:
137           return 0;
138       }
139   }
140
141 NTSTATUS STDCALL WvFilediskAttach(IN WV_SP_DEV_T dev, IN PIRP irp) {
142   ANSI_STRING file_path1;
143   PUCHAR buf = irp->AssociatedIrp.SystemBuffer;
144   WV_SP_MOUNT_DISK params = (WV_SP_MOUNT_DISK) buf;
145   UNICODE_STRING file_path2;
146   OBJECT_ATTRIBUTES obj_attrs;
147   NTSTATUS status;
148   HANDLE file = NULL;
149   IO_STATUS_BLOCK io_status;
150   FILE_STANDARD_INFORMATION info;
151   WV_SP_FILEDISK_T filedisk_ptr;
152
153   filedisk_ptr = filedisk__create (  );
154   if ( filedisk_ptr == NULL )
155     {
156       DBG ( "Could not create file-backed disk!\n" );
157       return STATUS_INSUFFICIENT_RESOURCES;
158     }
159
160   RtlInitAnsiString ( &file_path1,
161                       ( char * )&buf[sizeof ( WV_S_MOUNT_DISK )] );
162   status = RtlAnsiStringToUnicodeString ( &file_path2, &file_path1, TRUE );
163   if ( !NT_SUCCESS ( status ) )
164     goto err_ansi_to_unicode;
165   InitializeObjectAttributes ( &obj_attrs, &file_path2,
166                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL,
167                                NULL );
168   /*
169    * Open the file.  The handle is closed by close()
170    */
171   status =
172     ZwCreateFile ( &file, GENERIC_READ | GENERIC_WRITE, &obj_attrs, &io_status,
173                    NULL, FILE_ATTRIBUTE_NORMAL,
174                    FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
175                    FILE_OPEN,
176                    FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
177                    FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
178   RtlFreeUnicodeString ( &file_path2 );
179   if ( !NT_SUCCESS ( status ) )
180     goto err_file_open;
181
182   filedisk_ptr->file = file;
183
184   switch ( params->type )
185     {
186       case 'f':
187         filedisk_ptr->disk->Media = WvDiskMediaTypeFloppy;
188         filedisk_ptr->disk->SectorSize = 512;
189         break;
190       case 'c':
191         filedisk_ptr->disk->Media = WvDiskMediaTypeOptical;
192         filedisk_ptr->disk->SectorSize = 2048;
193         break;
194       default:
195         filedisk_ptr->disk->Media = WvDiskMediaTypeHard;
196         filedisk_ptr->disk->SectorSize = 512;
197         break;
198     }
199   DBG ( "File-backed disk is type: %d\n", filedisk_ptr->disk->Media );
200   /*
201    * Determine the disk's size
202    */
203   status =
204     ZwQueryInformationFile ( file, &io_status, &info, sizeof ( info ),
205                              FileStandardInformation );
206   if ( !NT_SUCCESS ( status ) )
207     goto err_query_info;
208
209   filedisk_ptr->disk->LBADiskSize =
210     info.EndOfFile.QuadPart / filedisk_ptr->disk->SectorSize;
211   filedisk_ptr->disk->Cylinders = params->cylinders;
212   filedisk_ptr->disk->Heads = params->heads;
213   filedisk_ptr->disk->Sectors = params->sectors;
214   /*
215    * A really stupid "hash".  RtlHashUnicodeString() would have been
216    * good, but is only available >= Windows XP
217    */
218   filedisk_ptr->hash = ( UINT32 ) filedisk_ptr->disk->LBADiskSize;
219   {
220     char *path_iterator = file_path1.Buffer;
221
222     while ( *path_iterator )
223       filedisk_ptr->hash += *path_iterator++;
224   }
225   /* Add the filedisk to the bus. */
226   if (!WvBusAddDev(filedisk_ptr->disk->Dev)) {
227       status = STATUS_UNSUCCESSFUL;
228       goto err_add_child;
229     }
230   return STATUS_SUCCESS;
231
232   err_add_child:
233
234 err_query_info:
235
236   ZwClose ( file );
237 err_file_open:
238
239 err_ansi_to_unicode:
240
241   free_filedisk(filedisk_ptr->disk->Dev);
242   return status;
243 }
244
245 static VOID STDCALL close(IN WV_SP_DISK_T disk_ptr) {
246     WV_SP_FILEDISK_T filedisk_ptr = filedisk__get_ptr(disk_ptr->Dev);
247
248     ZwClose(filedisk_ptr->file);
249     return;
250   }
251
252 /**
253  * Create a new file-backed disk
254  *
255  * @ret filedisk_ptr    The address of a new filedisk, or NULL for failure
256  *
257  * See the header file for additional details
258  */
259 WV_SP_FILEDISK_T filedisk__create(void) {
260   WV_SP_DISK_T disk_ptr;
261   WV_SP_FILEDISK_T filedisk_ptr;
262
263   /*
264    * Try to create a disk
265    */
266   disk_ptr = disk__create (  );
267   if ( disk_ptr == NULL )
268     goto err_nodisk;
269   /*
270    * File-backed disk devices might be used for booting and should
271    * not be allocated from a paged memory pool
272    */
273   filedisk_ptr = wv_mallocz(sizeof *filedisk_ptr);
274   if ( filedisk_ptr == NULL )
275     goto err_nofiledisk;
276   /*
277    * Track the new file-backed disk in our global list
278    */
279   ExInterlockedInsertTailList ( &filedisk_list, &filedisk_ptr->tracking,
280                                 &filedisk_list_lock );
281   /*
282    * Populate non-zero device defaults
283    */
284   filedisk_ptr->disk = disk_ptr;
285   filedisk_ptr->prev_free = disk_ptr->Dev->Ops.Free;
286   disk_ptr->Dev->Ops.Free = free_filedisk;
287   disk_ptr->Dev->Ops.PnpId = query_id;
288   disk_ptr->disk_ops.Io = io;
289   disk_ptr->disk_ops.Close = close;
290   disk_ptr->ext = filedisk_ptr;
291
292   return filedisk_ptr;
293
294 err_nofiledisk:
295
296   WvDevFree(disk_ptr->Dev);
297 err_nodisk:
298
299   return NULL;
300 }
301
302 /**
303  * Initialize the global, file-backed disk-common environment.
304  *
305  * @ret ntstatus        STATUS_SUCCESS or the NTSTATUS for a failure.
306  */
307 NTSTATUS WvFilediskModuleInit(void) {
308     /* Initialize the global list of file-backed disks. */
309     InitializeListHead(&filedisk_list);
310     KeInitializeSpinLock(&filedisk_list_lock);
311
312     return STATUS_SUCCESS;
313   }
314
315 /**
316  * Default file-backed disk deletion operation.
317  *
318  * @v dev_ptr           Points to the file-backed disk device to delete.
319  */
320 static VOID STDCALL free_filedisk(IN WV_SP_DEV_T dev_ptr)
321   {
322     WV_SP_DISK_T disk_ptr = disk__get_ptr(dev_ptr);
323     WV_SP_FILEDISK_T filedisk_ptr = filedisk__get_ptr(dev_ptr);
324     /*
325      * Free the "inherited class".
326      */
327     filedisk_ptr->prev_free(dev_ptr);
328     /*
329      * Track the file-backed disk deletion in our global list.  Unfortunately,
330      * for now we have faith that a file-backed disk won't be deleted twice and
331      * result in a race condition.  Something to keep in mind...
332      */
333     ExInterlockedRemoveHeadList(
334         filedisk_ptr->tracking.Blink,
335         &filedisk_list_lock
336       );
337   
338     wv_free(filedisk_ptr);
339   }
340
341 /* Threaded read/write request. */
342 typedef struct WV_FILEDISK_THREAD_REQ {
343     LIST_ENTRY list_entry;
344     WV_SP_DEV_T dev_ptr;
345     WV_E_DISK_IO_MODE mode;
346     LONGLONG start_sector;
347     UINT32 sector_count;
348     PUCHAR buffer;
349     PIRP irp;
350   } WV_S_FILEDISK_THREAD_REQ, * WV_SP_FILEDISK_THREAD_REQ;
351
352 /**
353  * A threaded, file-backed disk's worker thread
354  *
355  * @v StartContext      Points to a file-backed disk
356  */
357 static VOID STDCALL
358 thread (
359   IN PVOID StartContext
360  )
361 {
362   WV_SP_FILEDISK_T filedisk_ptr = StartContext;
363   LARGE_INTEGER timeout;
364   PLIST_ENTRY walker;
365
366   /*
367    * Wake up at least every second
368    */
369   timeout.QuadPart = -10000000LL;
370   /*
371    * The read/write request processing loop
372    */
373   while ( TRUE )
374     {
375       /*
376        * Wait for work-to-do signal or the timeout
377        */
378       KeWaitForSingleObject ( &filedisk_ptr->signal, Executive, KernelMode,
379                               FALSE, &timeout );
380       KeResetEvent ( &filedisk_ptr->signal );
381       /* Are we being torn down?  We abuse the device's Free() member. */
382       if (filedisk_ptr->disk->Dev->Ops.Free == NULL)
383         break;
384       /* Process each read/write request in the list. */
385       while ( walker =
386               ExInterlockedRemoveHeadList ( &filedisk_ptr->req_list,
387                                             &filedisk_ptr->req_list_lock ) )
388         {
389           WV_SP_FILEDISK_THREAD_REQ req;
390
391           req = CONTAINING_RECORD(walker, WV_S_FILEDISK_THREAD_REQ, list_entry);
392           filedisk_ptr->sync_io ( req->dev_ptr, req->mode, req->start_sector,
393                                   req->sector_count, req->buffer, req->irp );
394     wv_free(req);
395         }
396     }
397   /* Time to tear things down. */
398   free_filedisk(filedisk_ptr->disk->Dev);
399 }
400
401 static NTSTATUS STDCALL threaded_io(
402     IN WV_SP_DEV_T dev_ptr,
403     IN WV_E_DISK_IO_MODE mode,
404     IN LONGLONG start_sector,
405     IN UINT32 sector_count,
406     IN PUCHAR buffer,
407     IN PIRP irp
408   ) {
409   WV_SP_FILEDISK_T filedisk_ptr;
410   WV_SP_FILEDISK_THREAD_REQ req;
411
412   filedisk_ptr = filedisk__get_ptr ( dev_ptr );
413   /*
414    * Allocate the request
415    */
416   req = wv_malloc(sizeof *req);
417   if ( req == NULL )
418     {
419       irp->IoStatus.Information = 0;
420       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
421       IoCompleteRequest ( irp, IO_NO_INCREMENT );
422       return STATUS_INSUFFICIENT_RESOURCES;
423     }
424   /*
425    * Remember the request
426    */
427   req->dev_ptr = dev_ptr;
428   req->mode = mode;
429   req->start_sector = start_sector;
430   req->sector_count = sector_count;
431   req->buffer = buffer;
432   req->irp = irp;
433   ExInterlockedInsertTailList ( &filedisk_ptr->req_list, &req->list_entry,
434                                 &filedisk_ptr->req_list_lock );
435   /*
436    * Signal worker thread and return
437    */
438   KeSetEvent ( &filedisk_ptr->signal, 0, FALSE );
439   return STATUS_PENDING;
440 }
441
442 /**
443  * Threaded, file-backed disk deletion operation.
444  *
445  * @v dev_ptr           Points to the file-backed disk device to delete.
446  */
447 static VOID STDCALL free_threaded_filedisk(IN WV_SP_DEV_T dev_ptr)
448   {
449     /*
450      * Queue the tear-down and return.  The thread will catch this on timeout.
451      */
452     dev_ptr->Ops.Free = NULL;
453   }
454
455 /**
456  * Create a new threaded, file-backed disk
457  *
458  * @ret filedisk_ptr    The address of a new filedisk, or NULL for failure
459  *
460  * See the header file for additional details
461  */
462 WV_SP_FILEDISK_T WvFilediskCreateThreaded(void) {
463   WV_SP_FILEDISK_T filedisk_ptr;
464   OBJECT_ATTRIBUTES obj_attrs;
465   HANDLE thread_handle;
466
467   /*
468    * Try to create a filedisk
469    */
470   filedisk_ptr = filedisk__create (  );
471   if ( filedisk_ptr == NULL )
472     goto err_nofiledisk;
473   /*
474    * Use threaded routines
475    */
476   filedisk_ptr->sync_io = io;
477   filedisk_ptr->disk->disk_ops.Io = threaded_io;
478   filedisk_ptr->disk->Dev->Ops.Free = free_threaded_filedisk;
479   /*
480    * Initialize threading parameters and start the filedisk's thread
481    */
482   InitializeListHead ( &filedisk_ptr->req_list );
483   KeInitializeSpinLock ( &filedisk_ptr->req_list_lock );
484   KeInitializeEvent ( &filedisk_ptr->signal, SynchronizationEvent, FALSE );
485   InitializeObjectAttributes ( &obj_attrs, NULL, OBJ_KERNEL_HANDLE, NULL,
486                                NULL );
487   PsCreateSystemThread ( &thread_handle, THREAD_ALL_ACCESS, &obj_attrs, NULL,
488                          NULL, thread, filedisk_ptr );
489
490   return filedisk_ptr;
491
492 err_nofiledisk:
493
494   return NULL;
495 }
496
497 /**
498  * Find and hot-swap to a backing file
499  *
500  * @v filedisk_ptr      Points to the filedisk needing to hot-swap
501  * @ret                 TRUE once the new file has been established, or FALSE
502  *
503  * We search all filesystems for a particular filename, then swap
504  * to using it as the backing store.  This is currently useful for
505  * sector-mapped disks which should really have a file-in-use lock
506  * for the file they represent.  Once the backing file is established,
507  * we return TRUE.  Otherwise, we return FALSE and the hot-swap thread
508  * will keep trying.
509  */
510 static BOOLEAN STDCALL
511 hot_swap (
512   WV_SP_FILEDISK_T filedisk_ptr
513  )
514 {
515   NTSTATUS status;
516   GUID vol_guid = GUID_DEVINTERFACE_VOLUME;
517   PWSTR sym_links;
518   PWCHAR pos;
519
520   /*
521    * Find the backing volume and use it.  We walk a list
522    * of unicode volume device names and check each one for the file
523    */
524   status = IoGetDeviceInterfaces ( &vol_guid, NULL, 0, &sym_links );
525   if ( !NT_SUCCESS ( status ) )
526     return FALSE;
527   pos = sym_links;
528   status = STATUS_UNSUCCESSFUL;
529   while ( *pos != UNICODE_NULL )
530     {
531       UNICODE_STRING path;
532       PFILE_OBJECT vol_file_obj;
533       PDEVICE_OBJECT vol_dev_obj;
534       UNICODE_STRING vol_dos_name;
535       UNICODE_STRING filepath;
536       static const WCHAR obj_path_prefix[] = L"\\??\\";
537       static const WCHAR path_sep = L'\\';
538       OBJECT_ATTRIBUTES obj_attrs;
539       HANDLE file = 0;
540       IO_STATUS_BLOCK io_status;
541
542       RtlInitUnicodeString ( &path, pos );
543       /*
544        * Get some object pointers for the volume
545        */
546       status =
547         IoGetDeviceObjectPointer ( &path, FILE_READ_DATA, &vol_file_obj,
548                                    &vol_dev_obj );
549       if ( !NT_SUCCESS ( status ) )
550         goto err_obj_ptrs;
551       /*
552        * Get the DOS name
553        */
554       vol_dos_name.Buffer = NULL;
555       vol_dos_name.Length = vol_dos_name.MaximumLength = 0;
556       status =
557         RtlVolumeDeviceToDosName ( vol_file_obj->DeviceObject, &vol_dos_name );
558       if ( !NT_SUCCESS ( status ) )
559         goto err_dos_name;
560       /*
561        * Build the file path.  Ugh, what a mess
562        */
563       filepath.Length = filepath.MaximumLength =
564         sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL ) +
565         vol_dos_name.Length + sizeof ( path_sep ) +
566         filedisk_ptr->filepath_unicode.Length;
567       filepath.Buffer = wv_malloc(filepath.Length);
568       if ( filepath.Buffer == NULL )
569         {
570           status = STATUS_UNSUCCESSFUL;
571           goto err_alloc_buf;
572         }
573       {
574         char *buf = ( char * )filepath.Buffer;
575
576         RtlCopyMemory ( buf, obj_path_prefix,
577                         sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL ) );
578         buf += sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL );
579         RtlCopyMemory ( buf, vol_dos_name.Buffer, vol_dos_name.Length );
580         buf += vol_dos_name.Length;
581         RtlCopyMemory ( buf, &path_sep, sizeof ( path_sep ) );
582         buf += sizeof ( path_sep );
583         RtlCopyMemory ( buf, filedisk_ptr->filepath_unicode.Buffer,
584                         filedisk_ptr->filepath_unicode.Length );
585       }
586       InitializeObjectAttributes ( &obj_attrs, &filepath,
587                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
588                                    NULL, NULL );
589       /*
590        * Look for the file on this volume
591        */
592       status =
593         ZwCreateFile ( &file, GENERIC_READ | GENERIC_WRITE, &obj_attrs,
594                        &io_status, NULL, FILE_ATTRIBUTE_NORMAL,
595                        FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
596                        FILE_OPEN,
597                        FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
598                        FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
599       if ( !NT_SUCCESS ( status ) )
600         goto err_open;
601       /*
602        * We could open it.  Do the hot-swap
603        */
604       {
605         HANDLE old = filedisk_ptr->file;
606
607         filedisk_ptr->file = 0;
608         filedisk_ptr->offset.QuadPart = 0;
609         filedisk_ptr->file = file;
610         ZwClose ( old );
611       }
612       RtlFreeUnicodeString ( &filedisk_ptr->filepath_unicode );
613       filedisk_ptr->filepath_unicode.Length = 0;
614
615     err_open:
616
617       wv_free(filepath.Buffer);
618     err_alloc_buf:
619
620       wv_free(vol_dos_name.Buffer);
621     err_dos_name:
622
623       ObDereferenceObject ( vol_file_obj );
624     err_obj_ptrs:
625       /*
626        * Walk to the next terminator
627        */
628       while ( *pos != UNICODE_NULL )
629         pos++;
630       /*
631        * If everything succeeded, stop.  Otherwise try the next volume
632        */
633       if ( !NT_SUCCESS ( status ) )
634         pos++;
635     }
636   wv_free(sym_links);
637   return NT_SUCCESS ( status ) ? TRUE : FALSE;
638 }
639
640 VOID STDCALL
641 filedisk__hot_swap_thread (
642   IN PVOID StartContext
643  )
644 {
645   WV_SP_FILEDISK_T filedisk_ptr = StartContext;
646   KEVENT signal;
647   LARGE_INTEGER timeout;
648
649   KeInitializeEvent ( &signal, SynchronizationEvent, FALSE );
650   /*
651    * Wake up at least every second
652    */
653   timeout.QuadPart = -10000000LL;
654   /*
655    * The hot-swap loop
656    */
657   while ( TRUE )
658     {
659       /*
660        * Wait for work-to-do signal or the timeout
661        */
662       KeWaitForSingleObject ( &signal, Executive, KernelMode, FALSE,
663                               &timeout );
664       if ( filedisk_ptr->file == NULL )
665         continue;
666       /*
667        * Are we supposed to hot-swap to a file?  Check ANSI filepath
668        */
669       if ( filedisk_ptr->filepath != NULL )
670         {
671           ANSI_STRING tmp;
672           NTSTATUS status;
673
674           RtlInitAnsiString ( &tmp, filedisk_ptr->filepath );
675           filedisk_ptr->filepath_unicode.Buffer = NULL;
676           status =
677             RtlAnsiStringToUnicodeString ( &filedisk_ptr->filepath_unicode,
678                                            &tmp, TRUE );
679           if ( NT_SUCCESS ( status ) )
680             {
681         wv_free(filedisk_ptr->filepath);
682               filedisk_ptr->filepath = NULL;
683             }
684         }
685       /*
686        * Are we supposed to hot-swap to a file?  Check unicode filepath
687        */
688       if ( filedisk_ptr->filepath_unicode.Length )
689         if ( hot_swap ( filedisk_ptr ) )
690           break;
691     }
692 }