e20eda659d86e51ab64a172c3c5088c1a396964f
[captive.git] / src / libcaptive / client / init.c
1 /* $Id$
2  * Init and cleanup code of libcaptive to be called by client application
3  * Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "init.h"       /* self */
23 #include "captive/ldr.h"
24 #include "captive/ldr_exports.h"
25 #include "captive/unicode.h"
26 #include "captive/rtl-file.h"
27 #include <glib/gtypes.h>
28 #include <glib/gmessages.h>
29 #include "reactos/internal/ldr.h"
30 #include "reactos/napi/types.h"
31 #include "reactos/internal/kd.h"        /* for KDB_LOADDRIVER_HOOK */
32 #include <fcntl.h>
33 #include <sys/mman.h>   /* for PROT_READ, MAP_SHARED */
34 #include "reactos/ddk/kefuncs.h"        /* for KeInitializeSpinLock() */
35 #include "reactos/internal/ntoskrnl.h"  /* for IoInit() */
36 #include "reactos/internal/ps.h"        /* for PsInitProcessManagment() and PsInitThreadManagment() */
37 #include "reactos/ddk/iofuncs.h"        /* for IoCreateFile() */
38 #include "captive/storage.h"
39 #include "captive/signal.h"     /* for captive_signal_init() */
40 #include "reactos/ddk/psfuncs.h"        /* for PsGetCurrentThread() */
41 #include <stdio.h>
42 #include <popt.h>
43 #include <glib/gstrfuncs.h>
44 #include <glib/glist.h>
45 #include "giochannel-blind.h"
46 #include <glib-object.h>
47 #include "reactos/internal/se.h"        /* for SeInit2() */
48 #include "captive/leave.h"
49 #include "captive/options.h"
50 #include <libgnomevfs/gnome-vfs-result.h>
51 #include "lib.h"
52 #include <reactos/ddk/obfuncs.h>
53 #include <syslog.h>
54 #include "captive/macros.h"
55
56
57 struct captive_options *captive_options;
58
59 /* Are we initialized? */
60 static gboolean active;
61
62 /* Module of fs module itself loaded by captive_w32_init() */
63 static PMODULE_OBJECT ModuleObject;
64
65 /* Driver in fs module loaded by captive_w32_init() */
66 DRIVER_OBJECT captive_DriverObject;
67 PDRIVER_REINITIALIZE captive_DriverObject_ReinitRoutine;
68 PVOID captive_DriverObject_ReinitRoutine_Context;
69
70 /* Structure holding the pointer to the toplevel IRP */
71 static TOP_LEVEL_IRP TopLevelIrp;       /* TODO:thread */
72
73
74 void *_local_unwind2_addr;
75
76
77 gboolean captive_debug_messages_disabled=FALSE;
78
79 static gboolean captive_w32_init(void)
80 {
81 NTSTATUS err;
82 gboolean errbool;
83
84         g_return_val_if_fail(captive_options!=NULL,FALSE);
85         g_return_val_if_fail(captive_options->filesystem.type!=CAPTIVE_OPTIONS_MODULE_TYPE_EMPTY,FALSE);
86
87         /* captive_giochannel_size() only _after_ g_io_channel_set_encoding() ! */
88         captive_image_size=captive_giochannel_size(captive_image_iochannel);
89         g_return_val_if_fail(captive_image_size>0,FALSE);
90
91         /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() begins. */
92         /* ExpInitializeExecutive(); */
93                 /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() begins
94                  * here as the rest of the function does a lot of hardware initializations.
95                  */
96                 /* LdrInit1(); */
97                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() begins. */
98                         InitializeListHead(&ModuleTextListHead);
99                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() ends. */
100                 KeLowerIrql(DISPATCH_LEVEL);
101                 /*...*/
102                 /* FIXME: create default nls tables? Really still needed? */
103                 /* Obsolete: RtlpInitNlsTables(); */
104                 /*...*/
105                 /* KeInit2() */
106                         /*...*/
107                         KeInitializeDispatcher();
108                         /*...*/
109                 KeLowerIrql(PASSIVE_LEVEL);
110                 errbool=SeInit1();
111                 g_assert(errbool==TRUE);
112                 ObInit();
113                 errbool=SeInit2();
114                 g_assert(errbool==TRUE);
115                 /* PiInitProcessManager(); */
116                         /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() begins. */
117                         PsInitProcessManagment();
118                         PsInitThreadManagment();
119                         /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() ends. */
120                 /*...*/
121                 IoInit();
122                 /*...*/
123                 /* LdrInitModuleManagement(); */
124                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement() begins
125                          * here as the rest "Create module object for {NTOSKRNL,HAL}"
126                          * is dependent on {NTOSKRNL,HAL} PE image headers not provided by libcaptive.
127                          */
128                         /* Initialize the module list and spinlock */
129                         InitializeListHead(&ModuleListHead);
130                         KeInitializeSpinLock(&ModuleListLock);
131                         /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement ends. */
132                 /*...*/
133                 /* Ntinit(); */
134                         NtInitializeEventImplementation();
135                         /*...*/
136                 /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() ends. */
137         /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() ends. */
138
139         /* Simulate our PE headers and export the symbols of our complete libraries */
140         captive_kernel_exports();
141
142         errbool=captive_cdrom_init();
143         g_return_val_if_fail(errbool==TRUE,FALSE);
144         errbool=captive_disk_init();
145         g_return_val_if_fail(errbool==TRUE,FALSE);
146
147         while (captive_options->load_module) {
148 PMODULE_OBJECT ModuleObject_tmp;
149 NTSTATUS err;
150
151                 /* load the module */
152                 err=captive_LdrLoadModule(
153                                 captive_options->load_module->data,
154                                 &ModuleObject_tmp);     /* ModuleObjectp */
155                 g_return_val_if_fail(NT_SUCCESS(err),FALSE);
156
157                 captive_options_module_free(captive_options->load_module->data);
158                 /* also frees 'options->load_module->data' */
159                 captive_options->load_module=g_list_delete_link(captive_options->load_module,captive_options->load_module);
160                 }
161
162         /* Patch 'ntoskrnl.exe' loaded by 'captive_options->load_module' above. */
163         {
164         CHAR *KeNumberProcessorsp=captive_Module_GetExportAddress("ntoskrnl.exe","KeNumberProcessors");
165         
166                 g_assert(*KeNumberProcessorsp==0);
167                 *KeNumberProcessorsp=KeNumberProcessors;
168                 g_assert(*KeNumberProcessorsp==1);
169                 }
170         /* Apply AFTER any symbols sanity checks above! */
171         if (captive_options->debug_messages)
172                 captive_kernel_patches_debug();
173         else
174                 captive_kernel_patches_nondebug();
175
176         _local_unwind2_addr=captive_Module_GetExportAddress("ntoskrnl.exe","_local_unwind2");
177
178         /* Initialize 'FsRtlLegalAnsiCharacterArray'.
179          * It requires 'ntoskrnl.exe' loaded by 'captive_options->load_module' above;
180          * captive_kernel_patches_debug()/captive_kernel_patches_nondebug() should not be needed.
181          */
182         captive_FsRtlLegalAnsiCharacterArray_init();
183
184         /* set TopLevelIrp() - FIXME: where is it set by native reactos? */
185         PsGetCurrentThread()->TopLevelIrp=&TopLevelIrp; /* otherwise Io{Get,Set}TopLevelIrp() would SIGSEGV */
186
187         /* Begin possible handling of foreign W32 binary code here */
188         /* If you want to disable SIGSEGV handler if not needed:
189          *      if (ModuleObject->Flags & MODULE_FLAG_PE)
190          */
191         captive_signal_init();
192
193         /* You must have already captive_signal_init() passed here as the module may
194          * call some functions from W32 ntoskrnl.exe.
195          */
196         captive_DriverObject_ReinitRoutine=NULL;
197         err=captive_LdrpLoadAndCallImage(
198                         &ModuleObject,  /* ModuleObjectp */
199                         &captive_options->filesystem,   /* options_module */
200                         &captive_DriverObject,  /* DriverEntry_DriverObject */
201                         captive_utf8_to_UnicodeString_alloca("\\captive\\filesystem")); /* DriverEntry_RegistryPath */
202         g_return_val_if_fail(NT_SUCCESS(err),FALSE);
203         if (captive_DriverObject_ReinitRoutine) {
204                 captive_stdcall_call_12((CaptiveStdCallFunc12)captive_DriverObject_ReinitRoutine,
205                                 &captive_DriverObject,  /* DriverObject */
206                                 captive_DriverObject_ReinitRoutine_Context,     /* Context */
207                                 (gpointer)1);   /* Count: # of calls of ReinitRoutine incl. the current one */
208                 }
209
210         return TRUE;
211 }
212
213
214 static void     captive_log_init_g_log_func_discard
215                 (const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
216 {
217         g_return_if_fail(message!=NULL);
218
219         /* NOP */
220 }
221
222 static void captive_log_init_g_log_func
223                 (const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data /* unused */)
224 {
225 int priority;
226
227         g_return_if_fail(message!=NULL);
228
229                 /* unused: LOG_EMERG */
230                 /* unused: LOG_ALERT */
231         /**/ if (log_level&G_LOG_LEVEL_ERROR)
232                 priority=LOG_CRIT;
233         else if (log_level&G_LOG_LEVEL_CRITICAL)
234                 priority=LOG_ERR;
235         else if (log_level&G_LOG_LEVEL_WARNING)
236                 priority=LOG_WARNING;
237         else if (log_level&G_LOG_LEVEL_MESSAGE)
238                 priority=LOG_NOTICE;
239         else if (log_level&G_LOG_LEVEL_INFO)
240                 priority=LOG_INFO;
241         else if (log_level&G_LOG_LEVEL_DEBUG)
242                 priority=LOG_DEBUG;
243         else /* bogus? */
244                 priority=LOG_WARNING;
245
246         syslog(priority,"%s%s%s",
247                         (!(log_level&G_LOG_FLAG_RECURSION) ? "" : "RECURSION: "),
248                         (!(log_level&G_LOG_FLAG_FATAL    ) ? "" : "FATAL: "),
249                         message);
250 }
251
252 void captive_log_init(const struct captive_options *captive_options)
253 {
254         g_return_if_fail(captive_options!=NULL);
255
256         /* FIXME: Fix sharing of different 'debug_messages' for various sandboxes. */
257         captive_debug_messages_disabled=!captive_options->debug_messages;
258
259         /* FIXME: Fix sharing of different 'debug_messages' for various sandboxes. */
260         if (!captive_options->debug_messages) {
261                 /* FIXME: Save handler_id and destroy it in captive_vfs_close(). */
262                 g_log_set_handler(
263                                 G_LOG_DOMAIN,   /* log_domain; "Captive" */
264                                 0       /* log_levels */
265                                                 | G_LOG_FLAG_RECURSION
266                                                 | G_LOG_FLAG_FATAL
267                                                 /* The same mask is in:
268                                                  * libcaptive/sandbox/server-GLogFunc.c
269                                                  * libcaptive/client/init.c
270                                                  */
271                                                 | G_LOG_LEVEL_MESSAGE
272                                                 | G_LOG_LEVEL_INFO
273                                                 | G_LOG_LEVEL_DEBUG,
274                                 captive_log_init_g_log_func_discard,    /* log_func */
275                                 NULL);  /* user_data */
276                 }
277         /* We are not the sandboxed slave;
278          * 'syslog_facility' would be '-1' in slave anyway as it is not transferred through CORBA.
279          */
280         if (!captive_options->sandbox || (captive_options->sandbox_server_argv || captive_options->sandbox_server_ior)) {
281                 if (captive_options->syslog_facility!=-1) {
282                         openlog(
283                                         /* FIXME: Prefix 'ident' by device/mountpoint. */
284                                         G_LOG_DOMAIN,   /* ident; "Captive"; FIXME: lowercase it for syslog(3)? */
285                                         LOG_CONS|LOG_PID,       /* options */
286                                         captive_options->syslog_facility);      /* facility */
287                         /* FIXME: Save handler_id and destroy it in captive_vfs_close(). */
288                         g_log_set_handler(
289                                         G_LOG_DOMAIN,   /* log_domain; "Captive" */
290                                         0       /* log_levels */
291                                                         | 0     /* !G_LOG_FLAG_RECURSION */
292                                                         | G_LOG_FLAG_FATAL
293                                                         | (G_LOG_LEVEL_MASK & ~(captive_options->debug_messages ? 0 : 0
294                                                                         /* The same mask is in:
295                                                                          * libcaptive/sandbox/server-GLogFunc.c
296                                                                          * libcaptive/client/init.c
297                                                                          */
298                                                                         | G_LOG_LEVEL_MESSAGE
299                                                                         | G_LOG_LEVEL_INFO
300                                                                         | G_LOG_LEVEL_DEBUG)),
301                                         (GLogFunc)captive_log_init_g_log_func,  /* log_func */
302                                         NULL);  /* user_data */
303                         }
304                 }
305 }
306
307 /**
308  * captive_init:
309  *
310  * Expects #captive_options: Parsed by captive_options_parse().
311  * %NULL value is forbidden. Field #image_iochannel %NULL value is forbidden.
312  *
313  * Initializes %libcaptive and loads the specified filesystem.
314  *
315  * Returns: %TRUE if successfuly initialized.
316  */
317 gboolean captive_init(void)
318 {
319 gboolean errbool;
320
321         /* We are in sandbox child and we have the right to fail. */
322         g_log_set_always_fatal(~(0
323                         |G_LOG_LEVEL_MESSAGE
324                         |G_LOG_LEVEL_INFO
325                         |G_LOG_LEVEL_DEBUG
326                         ));
327
328         g_return_val_if_fail(active==FALSE,FALSE);
329
330         g_return_val_if_fail(captive_options!=NULL,FALSE);
331         g_return_val_if_fail(captive_options->image_iochannel!=NULL,FALSE);
332
333         /* Initialize GObject subsystem of GLib. */
334         g_type_init();
335
336         captive_log_init(captive_options);
337
338         if (captive_options->rwmode==CAPTIVE_OPTION_RWMODE_BLIND)
339                 captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(captive_options->image_iochannel,
340                                 TRUE);  /* writeable */
341         else
342                 captive_image_iochannel=captive_options->image_iochannel;
343
344         /* Do not initialize 'captive_image_size' by captive_giochannel_size() here
345          * as we yet need to do g_io_channel_set_encoding().
346          */
347
348         errbool=captive_w32_init();
349         g_return_val_if_fail(errbool==TRUE,FALSE);
350
351         active=TRUE;
352         return TRUE;
353 }
354
355
356 static void dismount_volume(void)
357 {
358 IO_STATUS_BLOCK IoStatusBlock;
359 PIO_STACK_LOCATION StackPtr;
360 PIRP Irp;
361 NTSTATUS Status;
362 DEVICE_OBJECT *DeviceObject=captive_DriverObject.DeviceObject;
363 OBJECT_ATTRIBUTES dir_ObjectAttributes;
364 HANDLE dir_Handle;
365 FILE_OBJECT *FileObject;
366 GnomeVFSResult errvfsresult;
367 NTSTATUS err;
368 IO_STATUS_BLOCK dir_IoStatusBlock;
369 #if 0
370 static const ULONG fsctls[2]={ FSCTL_LOCK_VOLUME,FSCTL_DISMOUNT_VOLUME };
371 int fsctlsi;
372 #endif
373 WCHAR wzero;
374
375         errvfsresult=captive_ObjectAttributes_init("/!Captive!del",&dir_ObjectAttributes);
376         g_return_if_fail(errvfsresult==GNOME_VFS_OK);
377         
378         /* wanted: * IoCreateFile()->ObCreateObject(,,,IoFileObjectType)->
379          * ->(IoFileObjectType->Create==IopCreateFile)()->IoMountVolume()
380          */
381         CAPTIVE_MEMZERO(&dir_IoStatusBlock);    /* FIXME: Try to pre-clear it - uninitialized otherwise? */
382         dir_IoStatusBlock.Information=FILE_OPENED;      /* FIXME: Try to pre-set it - uninitialized otherwise? */
383         err=IoCreateFile(
384                         &dir_Handle,    /* FileHandle */
385                         GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE|0x80,    /* DesiredAccess; 0xC0100080=GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE|0x80 */
386                         &dir_ObjectAttributes,  /* ObjectAttributes */
387                         &dir_IoStatusBlock,     /* IoStatusBlock */
388                         NULL,   /* AllocationSize; ignored for open */
389                         FILE_ATTRIBUTE_NORMAL,  /* FileAttributes; ignored for open */
390                         (FILE_SHARE_READ | FILE_SHARE_WRITE),   /* ShareAccess; 0 means exclusive */
391                         FILE_OPEN,      /* CreateDisposition */
392                         /* FILE_SYNCHRONOUS_IO_{,NON}ALERT: We need to allow W32 filesystem
393                          * any waits to not to let it return STATUS_CANT_WAIT us.
394                          * Alertability should have only effect on asynchronous events
395                          * from KeWaitForSingleObject() by setting/clearing its parameter 'Alertable'.
396                          */
397                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, /* CreateOptions; FILE_DIRECTORY_FILE is forbidden */
398                         NULL,   /* EaBuffer */
399                         0,      /* EaLength */
400                         CreateFileTypeNone,     /* CreateFileType */
401                         NULL,   /* ExtraCreateParameters */
402                         0);     /* Options */
403         g_free(dir_ObjectAttributes.ObjectName);        /* left from captive_gnomevfs_uri_parent_init() */
404         g_return_if_fail(NT_SUCCESS(err));
405         g_return_if_fail(NT_SUCCESS(err)==NT_SUCCESS(dir_IoStatusBlock.Status));
406         g_return_if_fail(dir_IoStatusBlock.Information==FILE_OPENED);
407
408         Status=ObReferenceObjectByHandle(dir_Handle,FILE_LIST_DIRECTORY,IoFileObjectType,UserMode,(PVOID *)&FileObject,NULL);
409         g_assert(NT_SUCCESS(Status));
410
411         g_assert(FileObject->FileName.Length==0);
412         /* 'FileObject->FileName.MaximumLength' is not reset by IoCreateFile(). */
413         g_assert(FileObject->FileName.Buffer==NULL);
414         FileObject->FileName.MaximumLength=2;
415         FileObject->FileName.Buffer=&wzero;
416
417 #if 0
418         for (fsctlsi=0;fsctlsi<2;fsctlsi++)
419 #endif
420         {
421                 Irp=IoAllocateIrp(DeviceObject->StackSize,TRUE);
422                 g_return_if_fail(Irp!=NULL);
423
424                 Irp->UserIosb=&IoStatusBlock;
425                 Irp->UserEvent=&FileObject->Event;
426                 Irp->Tail.Overlay.Thread=PsGetCurrentThread();
427
428                 StackPtr=IoGetNextIrpStackLocation(Irp);
429 #if 0
430                 StackPtr->MajorFunction=IRP_MJ_FILE_SYSTEM_CONTROL;
431                 StackPtr->MinorFunction=IRP_MN_USER_FS_REQUEST;
432 #else
433                 StackPtr->MajorFunction=IRP_MJ_FLUSH_BUFFERS;
434 #endif
435                 StackPtr->Flags=0;
436                 StackPtr->Control=0;
437                 StackPtr->DeviceObject=DeviceObject;    /* FIXME: FileObject->Vpb->DeviceObject ? */
438                 StackPtr->FileObject=FileObject;
439                 StackPtr->CompletionRoutine=NULL;
440
441 #if 0
442                 StackPtr->Parameters.FileSystemControl.OutputBufferLength=0;
443                 StackPtr->Parameters.FileSystemControl.InputBufferLength=0;
444                 StackPtr->Parameters.FileSystemControl.FsControlCode=fsctls[fsctlsi];
445                 StackPtr->Parameters.FileSystemControl.Type3InputBuffer=NULL;
446 #endif
447
448                 /* IoCallDriver() will do one ObDereferenceObject(FileObject)
449                  * in its IoSecondStageCompletion().
450                  * Do not leave to dereference it itself as we need its 'FileObject->Event'.
451                  */
452                 ObReferenceObject(FileObject);
453
454                 Status=IoCallDriver(DeviceObject,Irp);
455                 if (Status==STATUS_PENDING) {
456                         KeWaitForSingleObject(&FileObject->Event,Executive,KernelMode,FALSE,NULL);
457                         Status=IoStatusBlock.Status;
458                         }
459                 g_assert(NT_SUCCESS(Status)
460                                 || (Status==STATUS_MEDIA_WRITE_PROTECTED && captive_options->rwmode==CAPTIVE_OPTION_RWMODE_RO));
461         }
462
463         ObDereferenceObject(FileObject);
464 }
465
466
467 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject);
468 void captive_cc_flush(void);
469
470 /**
471  * captive_shutdown:
472  *
473  * Closes down %libcaptive. It should flush all pending buffers and successfuly
474  * close the filesystem. Variable #captive_options->image_iochannel will not be set to %NULL,
475  * you should close such channel yourself.
476  *
477  * Returns: %TRUE if successfuly shutdown.
478  */
479 gboolean captive_shutdown(void)
480 {
481 GIOStatus erriostatus;
482
483         g_return_val_if_fail(active==TRUE,FALSE);
484         g_return_val_if_fail(captive_image_iochannel!=NULL,FALSE);
485         g_return_val_if_fail(captive_options->image_iochannel!=NULL,FALSE);
486
487         /* Invoke all pending idle functions just to not to forget for anything... */
488         while (g_main_context_iteration(
489                         NULL,   /* context; NULL means default one */
490                         FALSE)) /* may_block */
491                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_main_context_iteration() proceeded",G_STRLOC);
492
493         /* Do not: captive_cc_flush();  * based on captive_leave(), not g_main idle *
494          * replaced by IRP_MJ_FLUSH_BUFFERS.
495          */
496
497         dismount_volume();
498
499         /* Invoke all pending idle functions just to not to forget for anything... */
500         while (g_main_context_iteration(
501                         NULL,   /* context; NULL means default one */
502                         FALSE)) /* may_block */
503                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_main_context_iteration() proceeded",G_STRLOC);
504
505 #if 0
506         captive_PoQueueShutdownWorkItem_hooklist_invoke();
507 #endif
508
509         /* Do not: captive_cc_flush();
510          * as the dirty blocks should have been already commited by dismount_volume(),
511          * any further commits would get us just STATUS_VOLUME_DISMOUNTED.
512          */
513
514         /* Do not: captive_cc_unmounting=TRUE;
515          * Without dismount_volume() it was:
516          *      During IoShutdownRegisteredFileSystems() - IRP_MJ_SHUTDOWN to be specific
517          *      some buffers will be written but after the IofCallDriver() it will be
518          *      no longer possible to flush such buffers to their DeviceVolumeFile.
519          *      Therefore we must flush such buffers on the fly although such behaviour
520          *      would crash us in regular case as filesystems access BCBs even after their
521          *      CcUnpinData().
522          * Currently the dirty blocks should have been already commited by dismount_volume(),
523          */
524
525         /* FIXME: ntoskrnl/ex/power.c/NtShutdownSystem() does
526          * IoShutdownRegistered{Devices,FileSystems} order; is it correct?
527          */
528         IoShutdownRegisteredFileSystems();
529
530         /* Do not: captive_cc_FileObject_delete(NULL);
531          * as the dirty blocks should have been already commited by dismount_volume(),
532          * any further commits would get us just STATUS_VOLUME_DISMOUNTED.
533          */
534
535         IoShutdownRegisteredDevices();
536
537         /* libcaptive is not authorized to shutdown 'captive_image_channel'. */
538         erriostatus=g_io_channel_flush(
539                         captive_image_iochannel,        /* channel */
540                         NULL);  /* error */
541         g_assert(erriostatus==G_IO_STATUS_NORMAL);
542
543         /* 'captive_image_iochannel' may be blinded wrapper of 'captive_options->image_iochannel'. */
544         if (captive_image_iochannel!=captive_options->image_iochannel) {
545                 erriostatus=g_io_channel_flush(
546                                 captive_options->image_iochannel,       /* channel */
547                                 NULL);  /* error */
548                 g_assert(erriostatus==G_IO_STATUS_NORMAL);
549                 g_io_channel_unref(captive_image_iochannel);
550                 }
551
552         captive_image_iochannel=NULL;
553
554         active=FALSE;
555         return TRUE;
556 }