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