4d598b72a49dee9c602d7fb227c52b6b477b1b14
[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 "device.h"
36 #include "disk.h"
37 #include "mount.h"
38 #include "bus.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
49 static
50 disk__io_decl (
51   io
52  )
53 {
54   disk__type_ptr disk_ptr;
55   filedisk__type_ptr filedisk_ptr;
56   LARGE_INTEGER offset;
57   NTSTATUS status;
58   IO_STATUS_BLOCK io_status;
59
60   /*
61    * Establish pointers to the disk and filedisk
62    */
63   disk_ptr = disk__get_ptr ( dev_ptr );
64   filedisk_ptr = filedisk__get_ptr ( dev_ptr );
65
66   if ( sector_count < 1 )
67     {
68       /*
69        * A silly request 
70        */
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;
76     }
77   /*
78    * Calculate the offset
79    */
80   offset.QuadPart = start_sector * disk_ptr->SectorSize;
81   offset.QuadPart += filedisk_ptr->offset.QuadPart;
82
83   if ( mode == disk__io_mode_write )
84     status =
85       ZwWriteFile ( filedisk_ptr->file, NULL, NULL, NULL, &io_status, buffer,
86                     sector_count * disk_ptr->SectorSize, &offset, NULL );
87   else
88     status =
89       ZwReadFile ( filedisk_ptr->file, NULL, NULL, NULL, &io_status, buffer,
90                    sector_count * disk_ptr->SectorSize, &offset, NULL );
91   if ( !start_sector )
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 );
96   return status;
97 }
98
99 static winvblock__uint32 STDCALL query_id(
100     IN WV_SP_DEV_T dev,
101     IN BUS_QUERY_ID_TYPE query_type,
102     IN OUT WCHAR (*buf)[512]
103   ) {
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"
110       };
111
112     switch (query_type) {
113         case BusQueryDeviceID:
114           return swprintf(*buf, hw_ids[disk->Media]) + 1;
115
116         case BusQueryInstanceID:
117           return swprintf(*buf, L"Hash_%08X", filedisk->hash) + 1;
118
119         case BusQueryHardwareIDs: {
120             winvblock__uint32 tmp;
121
122             tmp = swprintf(*buf, hw_ids[disk->Media]) + 1;
123             tmp += swprintf(*buf + tmp, WvDiskCompatIds[disk->Media]) + 4;
124             return tmp;
125           }
126
127         case BusQueryCompatibleIDs:
128           return swprintf(*buf, WvDiskCompatIds[disk->Media]) + 4;
129
130         default:
131           return 0;
132       }
133   }
134
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;
141   NTSTATUS status;
142   HANDLE file = NULL;
143   IO_STATUS_BLOCK io_status;
144   FILE_STANDARD_INFORMATION info;
145   filedisk__type_ptr filedisk_ptr;
146
147   filedisk_ptr = filedisk__create (  );
148   if ( filedisk_ptr == NULL )
149     {
150       DBG ( "Could not create file-backed disk!\n" );
151       return STATUS_INSUFFICIENT_RESOURCES;
152     }
153
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,
161                                NULL );
162   /*
163    * Open the file.  The handle is closed by close()
164    */
165   status =
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,
169                    FILE_OPEN,
170                    FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
171                    FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
172   RtlFreeUnicodeString ( &file_path2 );
173   if ( !NT_SUCCESS ( status ) )
174     goto err_file_open;
175
176   filedisk_ptr->file = file;
177
178   switch ( params->type )
179     {
180       case 'f':
181         filedisk_ptr->disk->Media = WvDiskMediaTypeFloppy;
182         filedisk_ptr->disk->SectorSize = 512;
183         break;
184       case 'c':
185         filedisk_ptr->disk->Media = WvDiskMediaTypeOptical;
186         filedisk_ptr->disk->SectorSize = 2048;
187         break;
188       default:
189         filedisk_ptr->disk->Media = WvDiskMediaTypeHard;
190         filedisk_ptr->disk->SectorSize = 512;
191         break;
192     }
193   DBG ( "File-backed disk is type: %d\n", filedisk_ptr->disk->Media );
194   /*
195    * Determine the disk's size
196    */
197   status =
198     ZwQueryInformationFile ( file, &io_status, &info, sizeof ( info ),
199                              FileStandardInformation );
200   if ( !NT_SUCCESS ( status ) )
201     goto err_query_info;
202
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;
208   /*
209    * A really stupid "hash".  RtlHashUnicodeString() would have been
210    * good, but is only available >= Windows XP
211    */
212   filedisk_ptr->hash = ( winvblock__uint32 ) filedisk_ptr->disk->LBADiskSize;
213   {
214     char *path_iterator = file_path1.Buffer;
215
216     while ( *path_iterator )
217       filedisk_ptr->hash += *path_iterator++;
218   }
219   /* Add the filedisk to the bus. */
220   if (!WvBusAddChild(driver__bus(), filedisk_ptr->disk->device)) {
221       status = STATUS_UNSUCCESSFUL;
222       goto err_add_child;
223     }
224   return STATUS_SUCCESS;
225
226   err_add_child:
227
228 err_query_info:
229
230   ZwClose ( file );
231 err_file_open:
232
233 err_ansi_to_unicode:
234
235   free_filedisk ( filedisk_ptr->disk->device );
236   return status;
237 }
238
239 static
240 disk__close_decl (
241   close
242  )
243 {
244   filedisk__type_ptr filedisk_ptr = filedisk__get_ptr ( disk_ptr->device );
245   ZwClose ( filedisk_ptr->file );
246   return;
247 }
248
249 /**
250  * Create a new file-backed disk
251  *
252  * @ret filedisk_ptr    The address of a new filedisk, or NULL for failure
253  *
254  * See the header file for additional details
255  */
256 filedisk__type_ptr
257 filedisk__create (
258   void
259  )
260 {
261   disk__type_ptr disk_ptr;
262   filedisk__type_ptr filedisk_ptr;
263
264   /*
265    * Try to create a disk
266    */
267   disk_ptr = disk__create (  );
268   if ( disk_ptr == NULL )
269     goto err_nodisk;
270   /*
271    * File-backed disk devices might be used for booting and should
272    * not be allocated from a paged memory pool
273    */
274   filedisk_ptr = wv_mallocz(sizeof *filedisk_ptr);
275   if ( filedisk_ptr == NULL )
276     goto err_nofiledisk;
277   /*
278    * Track the new file-backed disk in our global list
279    */
280   ExInterlockedInsertTailList ( &filedisk_list, &filedisk_ptr->tracking,
281                                 &filedisk_list_lock );
282   /*
283    * Populate non-zero device defaults
284    */
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;
292
293   return filedisk_ptr;
294
295 err_nofiledisk:
296
297   WvDevFree(disk_ptr->device);
298 err_nodisk:
299
300   return NULL;
301 }
302
303 /**
304  * Initialize the global, file-backed disk-common environment.
305  *
306  * @ret ntstatus        STATUS_SUCCESS or the NTSTATUS for a failure.
307  */
308 NTSTATUS filedisk__module_init(void) {
309     /* Initialize the global list of file-backed disks. */
310     InitializeListHead(&filedisk_list);
311     KeInitializeSpinLock(&filedisk_list_lock);
312
313     return STATUS_SUCCESS;
314   }
315
316 /**
317  * Default file-backed disk deletion operation.
318  *
319  * @v dev_ptr           Points to the file-backed disk device to delete.
320  */
321 static void STDCALL free_filedisk(IN WV_SP_DEV_T dev_ptr)
322   {
323     disk__type_ptr disk_ptr = disk__get_ptr(dev_ptr);
324     filedisk__type_ptr filedisk_ptr = filedisk__get_ptr(dev_ptr);
325     /*
326      * Free the "inherited class".
327      */
328     filedisk_ptr->prev_free(dev_ptr);
329     /*
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...
333      */
334     ExInterlockedRemoveHeadList(
335         filedisk_ptr->tracking.Blink,
336         &filedisk_list_lock
337       );
338   
339     wv_free(filedisk_ptr);
340   }
341
342 /* Threaded read/write request */
343 winvblock__def_struct ( thread_req )
344 {
345   LIST_ENTRY list_entry;
346   WV_SP_DEV_T dev_ptr;
347   disk__io_mode mode;
348   LONGLONG start_sector;
349   winvblock__uint32 sector_count;
350   winvblock__uint8_ptr buffer;
351   PIRP irp;
352 };
353
354 /**
355  * A threaded, file-backed disk's worker thread
356  *
357  * @v StartContext      Points to a file-backed disk
358  */
359 static void STDCALL
360 thread (
361   IN void *StartContext
362  )
363 {
364   filedisk__type_ptr filedisk_ptr = StartContext;
365   LARGE_INTEGER timeout;
366   PLIST_ENTRY walker;
367
368   /*
369    * Wake up at least every second
370    */
371   timeout.QuadPart = -10000000LL;
372   /*
373    * The read/write request processing loop
374    */
375   while ( TRUE )
376     {
377       /*
378        * Wait for work-to-do signal or the timeout
379        */
380       KeWaitForSingleObject ( &filedisk_ptr->signal, Executive, KernelMode,
381                               FALSE, &timeout );
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)
385         break;
386       /* Process each read/write request in the list. */
387       while ( walker =
388               ExInterlockedRemoveHeadList ( &filedisk_ptr->req_list,
389                                             &filedisk_ptr->req_list_lock ) )
390         {
391           thread_req_ptr req;
392
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 );
396     wv_free(req);
397         }
398     }
399   /*
400    * Time to tear things down
401    */
402   free_filedisk ( filedisk_ptr->disk->device );
403 }
404
405 static
406 disk__io_decl (
407   threaded_io
408  )
409 {
410   filedisk__type_ptr filedisk_ptr;
411   thread_req_ptr req;
412
413   filedisk_ptr = filedisk__get_ptr ( dev_ptr );
414   /*
415    * Allocate the request
416    */
417   req = wv_malloc(sizeof *req);
418   if ( req == NULL )
419     {
420       irp->IoStatus.Information = 0;
421       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
422       IoCompleteRequest ( irp, IO_NO_INCREMENT );
423       return STATUS_INSUFFICIENT_RESOURCES;
424     }
425   /*
426    * Remember the request
427    */
428   req->dev_ptr = dev_ptr;
429   req->mode = mode;
430   req->start_sector = start_sector;
431   req->sector_count = sector_count;
432   req->buffer = buffer;
433   req->irp = irp;
434   ExInterlockedInsertTailList ( &filedisk_ptr->req_list, &req->list_entry,
435                                 &filedisk_ptr->req_list_lock );
436   /*
437    * Signal worker thread and return
438    */
439   KeSetEvent ( &filedisk_ptr->signal, 0, FALSE );
440   return STATUS_PENDING;
441 }
442
443 /**
444  * Threaded, file-backed disk deletion operation.
445  *
446  * @v dev_ptr           Points to the file-backed disk device to delete.
447  */
448 static void STDCALL free_threaded_filedisk(IN WV_SP_DEV_T dev_ptr)
449   {
450     /*
451      * Queue the tear-down and return.  The thread will catch this on timeout.
452      */
453     dev_ptr->Ops.Free = NULL;
454   }
455
456 /**
457  * Create a new threaded, file-backed disk
458  *
459  * @ret filedisk_ptr    The address of a new filedisk, or NULL for failure
460  *
461  * See the header file for additional details
462  */
463 filedisk__type_ptr
464 filedisk__create_threaded (
465   void
466  )
467 {
468   filedisk__type_ptr filedisk_ptr;
469   OBJECT_ATTRIBUTES obj_attrs;
470   HANDLE thread_handle;
471
472   /*
473    * Try to create a filedisk
474    */
475   filedisk_ptr = filedisk__create (  );
476   if ( filedisk_ptr == NULL )
477     goto err_nofiledisk;
478   /*
479    * Use threaded routines
480    */
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;
484   /*
485    * Initialize threading parameters and start the filedisk's thread
486    */
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,
491                                NULL );
492   PsCreateSystemThread ( &thread_handle, THREAD_ALL_ACCESS, &obj_attrs, NULL,
493                          NULL, thread, filedisk_ptr );
494
495   return filedisk_ptr;
496
497 err_nofiledisk:
498
499   return NULL;
500 }
501
502 /**
503  * Find and hot-swap to a backing file
504  *
505  * @v filedisk_ptr      Points to the filedisk needing to hot-swap
506  * @ret                 TRUE once the new file has been established, or FALSE
507  *
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
513  * will keep trying.
514  */
515 static winvblock__bool STDCALL
516 hot_swap (
517   filedisk__type_ptr filedisk_ptr
518  )
519 {
520   NTSTATUS status;
521   GUID vol_guid = GUID_DEVINTERFACE_VOLUME;
522   PWSTR sym_links;
523   PWCHAR pos;
524
525   /*
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
528    */
529   status = IoGetDeviceInterfaces ( &vol_guid, NULL, 0, &sym_links );
530   if ( !NT_SUCCESS ( status ) )
531     return FALSE;
532   pos = sym_links;
533   status = STATUS_UNSUCCESSFUL;
534   while ( *pos != UNICODE_NULL )
535     {
536       UNICODE_STRING path;
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;
544       HANDLE file = 0;
545       IO_STATUS_BLOCK io_status;
546
547       RtlInitUnicodeString ( &path, pos );
548       /*
549        * Get some object pointers for the volume
550        */
551       status =
552         IoGetDeviceObjectPointer ( &path, FILE_READ_DATA, &vol_file_obj,
553                                    &vol_dev_obj );
554       if ( !NT_SUCCESS ( status ) )
555         goto err_obj_ptrs;
556       /*
557        * Get the DOS name
558        */
559       vol_dos_name.Buffer = NULL;
560       vol_dos_name.Length = vol_dos_name.MaximumLength = 0;
561       status =
562         RtlVolumeDeviceToDosName ( vol_file_obj->DeviceObject, &vol_dos_name );
563       if ( !NT_SUCCESS ( status ) )
564         goto err_dos_name;
565       /*
566        * Build the file path.  Ugh, what a mess
567        */
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 )
574         {
575           status = STATUS_UNSUCCESSFUL;
576           goto err_alloc_buf;
577         }
578       {
579         char *buf = ( char * )filepath.Buffer;
580
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 );
590       }
591       InitializeObjectAttributes ( &obj_attrs, &filepath,
592                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
593                                    NULL, NULL );
594       /*
595        * Look for the file on this volume
596        */
597       status =
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,
601                        FILE_OPEN,
602                        FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
603                        FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
604       if ( !NT_SUCCESS ( status ) )
605         goto err_open;
606       /*
607        * We could open it.  Do the hot-swap
608        */
609       {
610         HANDLE old = filedisk_ptr->file;
611
612         filedisk_ptr->file = 0;
613         filedisk_ptr->offset.QuadPart = 0;
614         filedisk_ptr->file = file;
615         ZwClose ( old );
616       }
617       RtlFreeUnicodeString ( &filedisk_ptr->filepath_unicode );
618       filedisk_ptr->filepath_unicode.Length = 0;
619
620     err_open:
621
622       wv_free(filepath.Buffer);
623     err_alloc_buf:
624
625       wv_free(vol_dos_name.Buffer);
626     err_dos_name:
627
628       ObDereferenceObject ( vol_file_obj );
629     err_obj_ptrs:
630       /*
631        * Walk to the next terminator
632        */
633       while ( *pos != UNICODE_NULL )
634         pos++;
635       /*
636        * If everything succeeded, stop.  Otherwise try the next volume
637        */
638       if ( !NT_SUCCESS ( status ) )
639         pos++;
640     }
641   wv_free(sym_links);
642   return NT_SUCCESS ( status ) ? TRUE : FALSE;
643 }
644
645 void STDCALL
646 filedisk__hot_swap_thread (
647   IN void *StartContext
648  )
649 {
650   filedisk__type_ptr filedisk_ptr = StartContext;
651   KEVENT signal;
652   LARGE_INTEGER timeout;
653
654   KeInitializeEvent ( &signal, SynchronizationEvent, FALSE );
655   /*
656    * Wake up at least every second
657    */
658   timeout.QuadPart = -10000000LL;
659   /*
660    * The hot-swap loop
661    */
662   while ( TRUE )
663     {
664       /*
665        * Wait for work-to-do signal or the timeout
666        */
667       KeWaitForSingleObject ( &signal, Executive, KernelMode, FALSE,
668                               &timeout );
669       if ( filedisk_ptr->file == NULL )
670         continue;
671       /*
672        * Are we supposed to hot-swap to a file?  Check ANSI filepath
673        */
674       if ( filedisk_ptr->filepath != NULL )
675         {
676           ANSI_STRING tmp;
677           NTSTATUS status;
678
679           RtlInitAnsiString ( &tmp, filedisk_ptr->filepath );
680           filedisk_ptr->filepath_unicode.Buffer = NULL;
681           status =
682             RtlAnsiStringToUnicodeString ( &filedisk_ptr->filepath_unicode,
683                                            &tmp, TRUE );
684           if ( NT_SUCCESS ( status ) )
685             {
686         wv_free(filedisk_ptr->filepath);
687               filedisk_ptr->filepath = NULL;
688             }
689         }
690       /*
691        * Are we supposed to hot-swap to a file?  Check unicode filepath
692        */
693       if ( filedisk_ptr->filepath_unicode.Length )
694         if ( hot_swap ( filedisk_ptr ) )
695           break;
696     }
697 }