1b6e5f776d16cb26d96d93b36ee45e739de98c56
[people/sha0/winvblock.git] / src / winvblock / filedisk / filedisk.c
1 /**
2  * Copyright (C) 2009-2010, 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 winvblock__uint32 sector_count,
57     IN winvblock__uint8_ptr buffer,
58     IN PIRP irp
59   ) {
60   WV_SP_DISK_T disk_ptr;
61   filedisk__type_ptr 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 winvblock__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     filedisk__type_ptr filedisk = filedisk__get_ptr(dev);
112     static PWCHAR hw_ids[WvDiskMediaTypes] = {
113         winvblock__literal_w L"\\FileFloppyDisk",
114         winvblock__literal_w L"\\FileHardDisk",
115         winvblock__literal_w 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             winvblock__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   winvblock__uint8_ptr buf = irp->AssociatedIrp.SystemBuffer;
144   mount__filedisk_ptr params = ( mount__filedisk_ptr ) 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   filedisk__type_ptr 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 ( mount__filedisk )] );
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 = ( winvblock__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     filedisk__type_ptr 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 filedisk__type_ptr
260 filedisk__create (
261   void
262  )
263 {
264   WV_SP_DISK_T disk_ptr;
265   filedisk__type_ptr 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     filedisk__type_ptr 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 winvblock__def_struct ( thread_req )
347 {
348   LIST_ENTRY list_entry;
349   WV_SP_DEV_T dev_ptr;
350   WV_E_DISK_IO_MODE mode;
351   LONGLONG start_sector;
352   winvblock__uint32 sector_count;
353   winvblock__uint8_ptr buffer;
354   PIRP irp;
355 };
356
357 /**
358  * A threaded, file-backed disk's worker thread
359  *
360  * @v StartContext      Points to a file-backed disk
361  */
362 static void STDCALL
363 thread (
364   IN void *StartContext
365  )
366 {
367   filedisk__type_ptr filedisk_ptr = StartContext;
368   LARGE_INTEGER timeout;
369   PLIST_ENTRY walker;
370
371   /*
372    * Wake up at least every second
373    */
374   timeout.QuadPart = -10000000LL;
375   /*
376    * The read/write request processing loop
377    */
378   while ( TRUE )
379     {
380       /*
381        * Wait for work-to-do signal or the timeout
382        */
383       KeWaitForSingleObject ( &filedisk_ptr->signal, Executive, KernelMode,
384                               FALSE, &timeout );
385       KeResetEvent ( &filedisk_ptr->signal );
386       /* Are we being torn down?  We abuse the device's Free() member. */
387       if (filedisk_ptr->disk->Dev->Ops.Free == NULL)
388         break;
389       /* Process each read/write request in the list. */
390       while ( walker =
391               ExInterlockedRemoveHeadList ( &filedisk_ptr->req_list,
392                                             &filedisk_ptr->req_list_lock ) )
393         {
394           thread_req_ptr req;
395
396           req = CONTAINING_RECORD ( walker, thread_req, list_entry );
397           filedisk_ptr->sync_io ( req->dev_ptr, req->mode, req->start_sector,
398                                   req->sector_count, req->buffer, req->irp );
399     wv_free(req);
400         }
401     }
402   /* Time to tear things down. */
403   free_filedisk(filedisk_ptr->disk->Dev);
404 }
405
406 static NTSTATUS STDCALL threaded_io(
407     IN WV_SP_DEV_T dev_ptr,
408     IN WV_E_DISK_IO_MODE mode,
409     IN LONGLONG start_sector,
410     IN winvblock__uint32 sector_count,
411     IN winvblock__uint8_ptr buffer,
412     IN PIRP irp
413   ) {
414   filedisk__type_ptr filedisk_ptr;
415   thread_req_ptr req;
416
417   filedisk_ptr = filedisk__get_ptr ( dev_ptr );
418   /*
419    * Allocate the request
420    */
421   req = wv_malloc(sizeof *req);
422   if ( req == NULL )
423     {
424       irp->IoStatus.Information = 0;
425       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
426       IoCompleteRequest ( irp, IO_NO_INCREMENT );
427       return STATUS_INSUFFICIENT_RESOURCES;
428     }
429   /*
430    * Remember the request
431    */
432   req->dev_ptr = dev_ptr;
433   req->mode = mode;
434   req->start_sector = start_sector;
435   req->sector_count = sector_count;
436   req->buffer = buffer;
437   req->irp = irp;
438   ExInterlockedInsertTailList ( &filedisk_ptr->req_list, &req->list_entry,
439                                 &filedisk_ptr->req_list_lock );
440   /*
441    * Signal worker thread and return
442    */
443   KeSetEvent ( &filedisk_ptr->signal, 0, FALSE );
444   return STATUS_PENDING;
445 }
446
447 /**
448  * Threaded, file-backed disk deletion operation.
449  *
450  * @v dev_ptr           Points to the file-backed disk device to delete.
451  */
452 static void STDCALL free_threaded_filedisk(IN WV_SP_DEV_T dev_ptr)
453   {
454     /*
455      * Queue the tear-down and return.  The thread will catch this on timeout.
456      */
457     dev_ptr->Ops.Free = NULL;
458   }
459
460 /**
461  * Create a new threaded, file-backed disk
462  *
463  * @ret filedisk_ptr    The address of a new filedisk, or NULL for failure
464  *
465  * See the header file for additional details
466  */
467 filedisk__type_ptr
468 filedisk__create_threaded (
469   void
470  )
471 {
472   filedisk__type_ptr filedisk_ptr;
473   OBJECT_ATTRIBUTES obj_attrs;
474   HANDLE thread_handle;
475
476   /*
477    * Try to create a filedisk
478    */
479   filedisk_ptr = filedisk__create (  );
480   if ( filedisk_ptr == NULL )
481     goto err_nofiledisk;
482   /*
483    * Use threaded routines
484    */
485   filedisk_ptr->sync_io = io;
486   filedisk_ptr->disk->disk_ops.Io = threaded_io;
487   filedisk_ptr->disk->Dev->Ops.Free = free_threaded_filedisk;
488   /*
489    * Initialize threading parameters and start the filedisk's thread
490    */
491   InitializeListHead ( &filedisk_ptr->req_list );
492   KeInitializeSpinLock ( &filedisk_ptr->req_list_lock );
493   KeInitializeEvent ( &filedisk_ptr->signal, SynchronizationEvent, FALSE );
494   InitializeObjectAttributes ( &obj_attrs, NULL, OBJ_KERNEL_HANDLE, NULL,
495                                NULL );
496   PsCreateSystemThread ( &thread_handle, THREAD_ALL_ACCESS, &obj_attrs, NULL,
497                          NULL, thread, filedisk_ptr );
498
499   return filedisk_ptr;
500
501 err_nofiledisk:
502
503   return NULL;
504 }
505
506 /**
507  * Find and hot-swap to a backing file
508  *
509  * @v filedisk_ptr      Points to the filedisk needing to hot-swap
510  * @ret                 TRUE once the new file has been established, or FALSE
511  *
512  * We search all filesystems for a particular filename, then swap
513  * to using it as the backing store.  This is currently useful for
514  * sector-mapped disks which should really have a file-in-use lock
515  * for the file they represent.  Once the backing file is established,
516  * we return TRUE.  Otherwise, we return FALSE and the hot-swap thread
517  * will keep trying.
518  */
519 static winvblock__bool STDCALL
520 hot_swap (
521   filedisk__type_ptr filedisk_ptr
522  )
523 {
524   NTSTATUS status;
525   GUID vol_guid = GUID_DEVINTERFACE_VOLUME;
526   PWSTR sym_links;
527   PWCHAR pos;
528
529   /*
530    * Find the backing volume and use it.  We walk a list
531    * of unicode volume device names and check each one for the file
532    */
533   status = IoGetDeviceInterfaces ( &vol_guid, NULL, 0, &sym_links );
534   if ( !NT_SUCCESS ( status ) )
535     return FALSE;
536   pos = sym_links;
537   status = STATUS_UNSUCCESSFUL;
538   while ( *pos != UNICODE_NULL )
539     {
540       UNICODE_STRING path;
541       PFILE_OBJECT vol_file_obj;
542       PDEVICE_OBJECT vol_dev_obj;
543       UNICODE_STRING vol_dos_name;
544       UNICODE_STRING filepath;
545       static const WCHAR obj_path_prefix[] = L"\\??\\";
546       static const WCHAR path_sep = L'\\';
547       OBJECT_ATTRIBUTES obj_attrs;
548       HANDLE file = 0;
549       IO_STATUS_BLOCK io_status;
550
551       RtlInitUnicodeString ( &path, pos );
552       /*
553        * Get some object pointers for the volume
554        */
555       status =
556         IoGetDeviceObjectPointer ( &path, FILE_READ_DATA, &vol_file_obj,
557                                    &vol_dev_obj );
558       if ( !NT_SUCCESS ( status ) )
559         goto err_obj_ptrs;
560       /*
561        * Get the DOS name
562        */
563       vol_dos_name.Buffer = NULL;
564       vol_dos_name.Length = vol_dos_name.MaximumLength = 0;
565       status =
566         RtlVolumeDeviceToDosName ( vol_file_obj->DeviceObject, &vol_dos_name );
567       if ( !NT_SUCCESS ( status ) )
568         goto err_dos_name;
569       /*
570        * Build the file path.  Ugh, what a mess
571        */
572       filepath.Length = filepath.MaximumLength =
573         sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL ) +
574         vol_dos_name.Length + sizeof ( path_sep ) +
575         filedisk_ptr->filepath_unicode.Length;
576       filepath.Buffer = wv_malloc(filepath.Length);
577       if ( filepath.Buffer == NULL )
578         {
579           status = STATUS_UNSUCCESSFUL;
580           goto err_alloc_buf;
581         }
582       {
583         char *buf = ( char * )filepath.Buffer;
584
585         RtlCopyMemory ( buf, obj_path_prefix,
586                         sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL ) );
587         buf += sizeof ( obj_path_prefix ) - sizeof ( UNICODE_NULL );
588         RtlCopyMemory ( buf, vol_dos_name.Buffer, vol_dos_name.Length );
589         buf += vol_dos_name.Length;
590         RtlCopyMemory ( buf, &path_sep, sizeof ( path_sep ) );
591         buf += sizeof ( path_sep );
592         RtlCopyMemory ( buf, filedisk_ptr->filepath_unicode.Buffer,
593                         filedisk_ptr->filepath_unicode.Length );
594       }
595       InitializeObjectAttributes ( &obj_attrs, &filepath,
596                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
597                                    NULL, NULL );
598       /*
599        * Look for the file on this volume
600        */
601       status =
602         ZwCreateFile ( &file, GENERIC_READ | GENERIC_WRITE, &obj_attrs,
603                        &io_status, NULL, FILE_ATTRIBUTE_NORMAL,
604                        FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
605                        FILE_OPEN,
606                        FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
607                        FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
608       if ( !NT_SUCCESS ( status ) )
609         goto err_open;
610       /*
611        * We could open it.  Do the hot-swap
612        */
613       {
614         HANDLE old = filedisk_ptr->file;
615
616         filedisk_ptr->file = 0;
617         filedisk_ptr->offset.QuadPart = 0;
618         filedisk_ptr->file = file;
619         ZwClose ( old );
620       }
621       RtlFreeUnicodeString ( &filedisk_ptr->filepath_unicode );
622       filedisk_ptr->filepath_unicode.Length = 0;
623
624     err_open:
625
626       wv_free(filepath.Buffer);
627     err_alloc_buf:
628
629       wv_free(vol_dos_name.Buffer);
630     err_dos_name:
631
632       ObDereferenceObject ( vol_file_obj );
633     err_obj_ptrs:
634       /*
635        * Walk to the next terminator
636        */
637       while ( *pos != UNICODE_NULL )
638         pos++;
639       /*
640        * If everything succeeded, stop.  Otherwise try the next volume
641        */
642       if ( !NT_SUCCESS ( status ) )
643         pos++;
644     }
645   wv_free(sym_links);
646   return NT_SUCCESS ( status ) ? TRUE : FALSE;
647 }
648
649 void STDCALL
650 filedisk__hot_swap_thread (
651   IN void *StartContext
652  )
653 {
654   filedisk__type_ptr filedisk_ptr = StartContext;
655   KEVENT signal;
656   LARGE_INTEGER timeout;
657
658   KeInitializeEvent ( &signal, SynchronizationEvent, FALSE );
659   /*
660    * Wake up at least every second
661    */
662   timeout.QuadPart = -10000000LL;
663   /*
664    * The hot-swap loop
665    */
666   while ( TRUE )
667     {
668       /*
669        * Wait for work-to-do signal or the timeout
670        */
671       KeWaitForSingleObject ( &signal, Executive, KernelMode, FALSE,
672                               &timeout );
673       if ( filedisk_ptr->file == NULL )
674         continue;
675       /*
676        * Are we supposed to hot-swap to a file?  Check ANSI filepath
677        */
678       if ( filedisk_ptr->filepath != NULL )
679         {
680           ANSI_STRING tmp;
681           NTSTATUS status;
682
683           RtlInitAnsiString ( &tmp, filedisk_ptr->filepath );
684           filedisk_ptr->filepath_unicode.Buffer = NULL;
685           status =
686             RtlAnsiStringToUnicodeString ( &filedisk_ptr->filepath_unicode,
687                                            &tmp, TRUE );
688           if ( NT_SUCCESS ( status ) )
689             {
690         wv_free(filedisk_ptr->filepath);
691               filedisk_ptr->filepath = NULL;
692             }
693         }
694       /*
695        * Are we supposed to hot-swap to a file?  Check unicode filepath
696        */
697       if ( filedisk_ptr->filepath_unicode.Length )
698         if ( hot_swap ( filedisk_ptr ) )
699           break;
700     }
701 }