[project] Rename winvblock__uint32 back to UINT32
[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 "winvblock.h"
32 #include "wv_stdlib.h"
33 #include "portable.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 filedisk__attach(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 (!WvDriverBusAddDev(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
260 filedisk__create (
261   void
262  )
263 {
264   WV_SP_DISK_T disk_ptr;
265   WV_SP_FILEDISK_T filedisk_ptr;
266
267   /*
268    * Try to create a disk
269    */
270   disk_ptr = disk__create (  );
271   if ( disk_ptr == NULL )
272     goto err_nodisk;
273   /*
274    * File-backed disk devices might be used for booting and should
275    * not be allocated from a paged memory pool
276    */
277   filedisk_ptr = wv_mallocz(sizeof *filedisk_ptr);
278   if ( filedisk_ptr == NULL )
279     goto err_nofiledisk;
280   /*
281    * Track the new file-backed disk in our global list
282    */
283   ExInterlockedInsertTailList ( &filedisk_list, &filedisk_ptr->tracking,
284                                 &filedisk_list_lock );
285   /*
286    * Populate non-zero device defaults
287    */
288   filedisk_ptr->disk = disk_ptr;
289   filedisk_ptr->prev_free = disk_ptr->Dev->Ops.Free;
290   disk_ptr->Dev->Ops.Free = free_filedisk;
291   disk_ptr->Dev->Ops.PnpId = query_id;
292   disk_ptr->disk_ops.Io = io;
293   disk_ptr->disk_ops.Close = close;
294   disk_ptr->ext = filedisk_ptr;
295
296   return filedisk_ptr;
297
298 err_nofiledisk:
299
300   WvDevFree(disk_ptr->Dev);
301 err_nodisk:
302
303   return NULL;
304 }
305
306 /**
307  * Initialize the global, file-backed disk-common environment.
308  *
309  * @ret ntstatus        STATUS_SUCCESS or the NTSTATUS for a failure.
310  */
311 NTSTATUS filedisk__module_init(void) {
312     /* Initialize the global list of file-backed disks. */
313     InitializeListHead(&filedisk_list);
314     KeInitializeSpinLock(&filedisk_list_lock);
315
316     return STATUS_SUCCESS;
317   }
318
319 /**
320  * Default file-backed disk deletion operation.
321  *
322  * @v dev_ptr           Points to the file-backed disk device to delete.
323  */
324 static void STDCALL free_filedisk(IN WV_SP_DEV_T dev_ptr)
325   {
326     WV_SP_DISK_T disk_ptr = disk__get_ptr(dev_ptr);
327     WV_SP_FILEDISK_T filedisk_ptr = filedisk__get_ptr(dev_ptr);
328     /*
329      * Free the "inherited class".
330      */
331     filedisk_ptr->prev_free(dev_ptr);
332     /*
333      * Track the file-backed disk deletion in our global list.  Unfortunately,
334      * for now we have faith that a file-backed disk won't be deleted twice and
335      * result in a race condition.  Something to keep in mind...
336      */
337     ExInterlockedRemoveHeadList(
338         filedisk_ptr->tracking.Blink,
339         &filedisk_list_lock
340       );
341   
342     wv_free(filedisk_ptr);
343   }
344
345 /* Threaded read/write request. */
346 typedef struct WV_FILEDISK_THREAD_REQ {
347     LIST_ENTRY list_entry;
348     WV_SP_DEV_T dev_ptr;
349     WV_E_DISK_IO_MODE mode;
350     LONGLONG start_sector;
351     UINT32 sector_count;
352     PUCHAR buffer;
353     PIRP irp;
354   } WV_S_FILEDISK_THREAD_REQ, * WV_SP_FILEDISK_THREAD_REQ;
355
356 /**
357  * A threaded, file-backed disk's worker thread
358  *
359  * @v StartContext      Points to a file-backed disk
360  */
361 static void STDCALL
362 thread (
363   IN void *StartContext
364  )
365 {
366   WV_SP_FILEDISK_T filedisk_ptr = StartContext;
367   LARGE_INTEGER timeout;
368   PLIST_ENTRY walker;
369
370   /*
371    * Wake up at least every second
372    */
373   timeout.QuadPart = -10000000LL;
374   /*
375    * The read/write request processing loop
376    */
377   while ( TRUE )
378     {
379       /*
380        * Wait for work-to-do signal or the timeout
381        */
382       KeWaitForSingleObject ( &filedisk_ptr->signal, Executive, KernelMode,
383                               FALSE, &timeout );
384       KeResetEvent ( &filedisk_ptr->signal );
385       /* Are we being torn down?  We abuse the device's Free() member. */
386       if (filedisk_ptr->disk->Dev->Ops.Free == NULL)
387         break;
388       /* Process each read/write request in the list. */
389       while ( walker =
390               ExInterlockedRemoveHeadList ( &filedisk_ptr->req_list,
391                                             &filedisk_ptr->req_list_lock ) )
392         {
393           WV_SP_FILEDISK_THREAD_REQ req;
394
395           req = CONTAINING_RECORD(walker, WV_S_FILEDISK_THREAD_REQ, list_entry);
396           filedisk_ptr->sync_io ( req->dev_ptr, req->mode, req->start_sector,
397                                   req->sector_count, req->buffer, req->irp );
398     wv_free(req);
399         }
400     }
401   /* Time to tear things down. */
402   free_filedisk(filedisk_ptr->disk->Dev);
403 }
404
405 static NTSTATUS STDCALL threaded_io(
406     IN WV_SP_DEV_T dev_ptr,
407     IN WV_E_DISK_IO_MODE mode,
408     IN LONGLONG start_sector,
409     IN UINT32 sector_count,
410     IN PUCHAR buffer,
411     IN PIRP irp
412   ) {
413   WV_SP_FILEDISK_T filedisk_ptr;
414   WV_SP_FILEDISK_THREAD_REQ req;
415
416   filedisk_ptr = filedisk__get_ptr ( dev_ptr );
417   /*
418    * Allocate the request
419    */
420   req = wv_malloc(sizeof *req);
421   if ( req == NULL )
422     {
423       irp->IoStatus.Information = 0;
424       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
425       IoCompleteRequest ( irp, IO_NO_INCREMENT );
426       return STATUS_INSUFFICIENT_RESOURCES;
427     }
428   /*
429    * Remember the request
430    */
431   req->dev_ptr = dev_ptr;
432   req->mode = mode;
433   req->start_sector = start_sector;
434   req->sector_count = sector_count;
435   req->buffer = buffer;
436   req->irp = irp;
437   ExInterlockedInsertTailList ( &filedisk_ptr->req_list, &req->list_entry,
438                                 &filedisk_ptr->req_list_lock );
439   /*
440    * Signal worker thread and return
441    */
442   KeSetEvent ( &filedisk_ptr->signal, 0, FALSE );
443   return STATUS_PENDING;
444 }
445
446 /**
447  * Threaded, file-backed disk deletion operation.
448  *
449  * @v dev_ptr           Points to the file-backed disk device to delete.
450  */
451 static void STDCALL free_threaded_filedisk(IN WV_SP_DEV_T dev_ptr)
452   {
453     /*
454      * Queue the tear-down and return.  The thread will catch this on timeout.
455      */
456     dev_ptr->Ops.Free = NULL;
457   }
458
459 /**
460  * Create a new threaded, file-backed disk
461  *
462  * @ret filedisk_ptr    The address of a new filedisk, or NULL for failure
463  *
464  * See the header file for additional details
465  */
466 WV_SP_FILEDISK_T
467 filedisk__create_threaded (
468   void
469  )
470 {
471   WV_SP_FILEDISK_T filedisk_ptr;
472   OBJECT_ATTRIBUTES obj_attrs;
473   HANDLE thread_handle;
474
475   /*
476    * Try to create a filedisk
477    */
478   filedisk_ptr = filedisk__create (  );
479   if ( filedisk_ptr == NULL )
480     goto err_nofiledisk;
481   /*
482    * Use threaded routines
483    */
484   filedisk_ptr->sync_io = io;
485   filedisk_ptr->disk->disk_ops.Io = threaded_io;
486   filedisk_ptr->disk->Dev->Ops.Free = free_threaded_filedisk;
487   /*
488    * Initialize threading parameters and start the filedisk's thread
489    */
490   InitializeListHead ( &filedisk_ptr->req_list );
491   KeInitializeSpinLock ( &filedisk_ptr->req_list_lock );
492   KeInitializeEvent ( &filedisk_ptr->signal, SynchronizationEvent, FALSE );
493   InitializeObjectAttributes ( &obj_attrs, NULL, OBJ_KERNEL_HANDLE, NULL,
494                                NULL );
495   PsCreateSystemThread ( &thread_handle, THREAD_ALL_ACCESS, &obj_attrs, NULL,
496                          NULL, thread, filedisk_ptr );
497
498   return filedisk_ptr;
499
500 err_nofiledisk:
501
502   return NULL;
503 }
504
505 /**
506  * Find and hot-swap to a backing file
507  *
508  * @v filedisk_ptr      Points to the filedisk needing to hot-swap
509  * @ret                 TRUE once the new file has been established, or FALSE
510  *
511  * We search all filesystems for a particular filename, then swap
512  * to using it as the backing store.  This is currently useful for
513  * sector-mapped disks which should really have a file-in-use lock
514  * for the file they represent.  Once the backing file is established,
515  * we return TRUE.  Otherwise, we return FALSE and the hot-swap thread
516  * will keep trying.
517  */
518 static winvblock__bool STDCALL
519 hot_swap (
520   WV_SP_FILEDISK_T filedisk_ptr
521  )
522 {
523   NTSTATUS status;
524   GUID vol_guid = GUID_DEVINTERFACE_VOLUME;
525   PWSTR sym_links;
526   PWCHAR pos;
527
528   /*
529    * Find the backing volume and use it.  We walk a list
530    * of unicode volume device names and check each one for the file
531    */
532   status = IoGetDeviceInterfaces ( &vol_guid, NULL, 0, &sym_links );
533   if ( !NT_SUCCESS ( status ) )
534     return FALSE;
535   pos = sym_links;
536   status = STATUS_UNSUCCESSFUL;
537   while ( *pos != UNICODE_NULL )
538     {
539       UNICODE_STRING path;
540       PFILE_OBJECT vol_file_obj;
541       PDEVICE_OBJECT vol_dev_obj;
542       UNICODE_STRING vol_dos_name;
543       UNICODE_STRING filepath;
544       static const WCHAR obj_path_prefix[] = L"\\??\\";
545       static const WCHAR path_sep = L'\\';
546       OBJECT_ATTRIBUTES obj_attrs;
547       HANDLE file = 0;
548       IO_STATUS_BLOCK io_status;
549
550       RtlInitUnicodeString ( &path, pos );
551       /*
552        * Get some object pointers for the volume
553        */
554       status =
555         IoGetDeviceObjectPointer ( &path, FILE_READ_DATA, &vol_file_obj,
556                                    &vol_dev_obj );
557       if ( !NT_SUCCESS ( status ) )
558         goto err_obj_ptrs;
559       /*
560        * Get the DOS name
561        */
562       vol_dos_name.Buffer = NULL;
563       vol_dos_name.Length = vol_dos_name.MaximumLength = 0;
564       status =
565         RtlVolumeDeviceToDosName ( vol_file_obj->DeviceObject, &vol_dos_name );
566       if ( !NT_SUCCESS ( status ) )
567         goto err_dos_name;
568       /*
569        * Build the file path.  Ugh, what a mess
570        */
571       filepath.Length = filepath.MaximumLength =
572         sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL ) +
573         vol_dos_name.Length + sizeof ( path_sep ) +
574         filedisk_ptr->filepath_unicode.Length;
575       filepath.Buffer = wv_malloc(filepath.Length);
576       if ( filepath.Buffer == NULL )
577         {
578           status = STATUS_UNSUCCESSFUL;
579           goto err_alloc_buf;
580         }
581       {
582         char *buf = ( char * )filepath.Buffer;
583
584         RtlCopyMemory ( buf, obj_path_prefix,
585                         sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL ) );
586         buf += sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL );
587         RtlCopyMemory ( buf, vol_dos_name.Buffer, vol_dos_name.Length );
588         buf += vol_dos_name.Length;
589         RtlCopyMemory ( buf, &path_sep, sizeof ( path_sep ) );
590         buf += sizeof ( path_sep );
591         RtlCopyMemory ( buf, filedisk_ptr->filepath_unicode.Buffer,
592                         filedisk_ptr->filepath_unicode.Length );
593       }
594       InitializeObjectAttributes ( &obj_attrs, &filepath,
595                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
596                                    NULL, NULL );
597       /*
598        * Look for the file on this volume
599        */
600       status =
601         ZwCreateFile ( &file, GENERIC_READ | GENERIC_WRITE, &obj_attrs,
602                        &io_status, NULL, FILE_ATTRIBUTE_NORMAL,
603                        FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
604                        FILE_OPEN,
605                        FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
606                        FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
607       if ( !NT_SUCCESS ( status ) )
608         goto err_open;
609       /*
610        * We could open it.  Do the hot-swap
611        */
612       {
613         HANDLE old = filedisk_ptr->file;
614
615         filedisk_ptr->file = 0;
616         filedisk_ptr->offset.QuadPart = 0;
617         filedisk_ptr->file = file;
618         ZwClose ( old );
619       }
620       RtlFreeUnicodeString ( &filedisk_ptr->filepath_unicode );
621       filedisk_ptr->filepath_unicode.Length = 0;
622
623     err_open:
624
625       wv_free(filepath.Buffer);
626     err_alloc_buf:
627
628       wv_free(vol_dos_name.Buffer);
629     err_dos_name:
630
631       ObDereferenceObject ( vol_file_obj );
632     err_obj_ptrs:
633       /*
634        * Walk to the next terminator
635        */
636       while ( *pos != UNICODE_NULL )
637         pos++;
638       /*
639        * If everything succeeded, stop.  Otherwise try the next volume
640        */
641       if ( !NT_SUCCESS ( status ) )
642         pos++;
643     }
644   wv_free(sym_links);
645   return NT_SUCCESS ( status ) ? TRUE : FALSE;
646 }
647
648 void STDCALL
649 filedisk__hot_swap_thread (
650   IN void *StartContext
651  )
652 {
653   WV_SP_FILEDISK_T filedisk_ptr = StartContext;
654   KEVENT signal;
655   LARGE_INTEGER timeout;
656
657   KeInitializeEvent ( &signal, SynchronizationEvent, FALSE );
658   /*
659    * Wake up at least every second
660    */
661   timeout.QuadPart = -10000000LL;
662   /*
663    * The hot-swap loop
664    */
665   while ( TRUE )
666     {
667       /*
668        * Wait for work-to-do signal or the timeout
669        */
670       KeWaitForSingleObject ( &signal, Executive, KernelMode, FALSE,
671                               &timeout );
672       if ( filedisk_ptr->file == NULL )
673         continue;
674       /*
675        * Are we supposed to hot-swap to a file?  Check ANSI filepath
676        */
677       if ( filedisk_ptr->filepath != NULL )
678         {
679           ANSI_STRING tmp;
680           NTSTATUS status;
681
682           RtlInitAnsiString ( &tmp, filedisk_ptr->filepath );
683           filedisk_ptr->filepath_unicode.Buffer = NULL;
684           status =
685             RtlAnsiStringToUnicodeString ( &filedisk_ptr->filepath_unicode,
686                                            &tmp, TRUE );
687           if ( NT_SUCCESS ( status ) )
688             {
689         wv_free(filedisk_ptr->filepath);
690               filedisk_ptr->filepath = NULL;
691             }
692         }
693       /*
694        * Are we supposed to hot-swap to a file?  Check unicode filepath
695        */
696       if ( filedisk_ptr->filepath_unicode.Length )
697         if ( hot_swap ( filedisk_ptr ) )
698           break;
699     }
700 }