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