Integrate static packaing into the CVS HEAD; make it even default.
[captive.git] / src / libcaptive / client / init.c
index 202f38c..9e9466c 100644 (file)
@@ -1,6 +1,6 @@
 /* $Id$
  * Init and cleanup code of libcaptive to be called by client application
- * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
+ * Copyright (C) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
 
 #include "config.h"
 
-#include "captive/client.h"    /* self */
+#include "init.h"      /* self */
 #include "captive/ldr.h"
 #include "captive/ldr_exports.h"
 #include "captive/unicode.h"
@@ -32,7 +32,7 @@
 #include <fcntl.h>
 #include <sys/mman.h>  /* for PROT_READ, MAP_SHARED */
 #include "reactos/ddk/kefuncs.h"       /* for KeInitializeSpinLock() */
-#include "reactos/internal/ntoskrnl.h" /* for RtlpInitNlsTables() and IoInit() */
+#include "reactos/internal/ntoskrnl.h" /* for IoInit() */
 #include "reactos/internal/ps.h"       /* for PsInitProcessManagment() and PsInitThreadManagment() */
 #include "reactos/ddk/iofuncs.h"       /* for IoCreateFile() */
 #include "captive/storage.h"
 #include <popt.h>
 #include <glib/gstrfuncs.h>
 #include <glib/glist.h>
-#include <glib/gutils.h>       /* for g_atexit() */
 #include "giochannel-blind.h"
-
+#include <glib-object.h>
+#include "reactos/internal/se.h"       /* for SeInit2() */
+#include "captive/leave.h"
+#include "captive/options.h"
+#include <libgnomevfs/gnome-vfs-result.h>
+#include "lib.h"
+#include <reactos/ddk/obfuncs.h>
+#include <syslog.h>
+#include "captive/macros.h"
+#include "../storage/relastblock.h"    /* for captive_storage_relastblock() */
+#include "../cc/sharedcachemap.h"      /* for captive_shared_cache_map_flush_all() */
+#include "standalone.h"
+
+
+struct captive_options *captive_options;
 
 /* Are we initialized? */
 static gboolean active;
@@ -53,151 +66,31 @@ static gboolean active;
 static PMODULE_OBJECT ModuleObject;
 
 /* Driver in fs module loaded by captive_w32_init() */
-static DRIVER_OBJECT DriverObject;
+DRIVER_OBJECT captive_DriverObject;
+PDRIVER_REINITIALIZE captive_DriverObject_ReinitRoutine;
+PVOID captive_DriverObject_ReinitRoutine_Context;
 
 /* Structure holding the pointer to the toplevel IRP */
 static TOP_LEVEL_IRP TopLevelIrp;      /* TODO:thread */
 
 
-gchar *captive_option_filesystem;
-enum captive_option_rwmode captive_option_rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
-enum captive_option_media captive_option_media=CAPTIVE_OPTION_MEDIA_DISK;
-GIOChannel *captive_image_iochannel;
-/* Non-blinded original GIOChannel for flushing during shutdown. */
-static GIOChannel *captive_image_iochannel_orig;
-guint64 captive_image_size;
-static GList *captive_load_module;
-
-
-static gchar *captive_popt_optarg;
-
-
-static void arg_filesystem(void)
-{
-       g_free(captive_option_filesystem);
-       captive_option_filesystem=g_strdup(captive_popt_optarg);
-}
-
-static void arg_load_module(void)
-{
-       captive_load_module=g_list_append(captive_load_module,g_strdup(captive_popt_optarg));
-}
-
-static void arg_ro(void)
-{
-       captive_option_rwmode=CAPTIVE_OPTION_RWMODE_RO;
-}
-static void arg_blind(void)
-{
-       captive_option_rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
-}
-static void arg_rw(void)
-{
-       captive_option_rwmode=CAPTIVE_OPTION_RWMODE_RW;
-}
-
-static void arg_cdrom(void)
-{
-       captive_option_media=CAPTIVE_OPTION_MEDIA_CDROM;
-}
-static void arg_disk(void)
-{
-       captive_option_media=CAPTIVE_OPTION_MEDIA_DISK;
-}
-
+void *_local_unwind2_addr;
 
-static void captive_popt_callback
-               (poptContext con,enum poptCallbackReason reason,const struct poptOption *opt,const char *arg,const void *data);
-
-const struct poptOption captive_popt[]={
-               { argInfo:POPT_ARG_INTL_DOMAIN, arg:(void *)PACKAGE },
-               { argInfo:POPT_ARG_CALLBACK,    arg:(void *)captive_popt_callback },
-#define POPT_OFFSET 2
-#define CAPTIVE_POPT(longname,argInfoP,argP,descripP,argDescripP) \
-               { \
-                       longName: (longname), \
-                       shortName: 0, \
-                       argInfo: (argInfoP), \
-                       arg: (void *)argP, \
-                       val: 0, \
-                       descrip: (descripP), \
-                       argDescrip: (argDescripP), \
-               }
-#define CAPTIVE_POPT_STRING(longname,descripP,argDescripP) \
-               CAPTIVE_POPT(longname,POPT_ARG_STRING,&captive_popt_optarg,descripP,argDescripP)
-#define CAPTIVE_POPT_NONE(longname,descripP) \
-               CAPTIVE_POPT(longname,POPT_ARG_NONE  ,NULL                ,descripP,NULL       )
-
-               CAPTIVE_POPT_STRING("filesystem" ,N_("Path to .sys or .so filesystem module file"),N_("pathname")),
-               CAPTIVE_POPT_STRING("load-module",N_("Path to any W32 module to load w/o initialization"),N_("pathname")),
-               CAPTIVE_POPT_NONE(  "ro"         ,N_("Read/write mode: Any write access will be forbidden")),
-               CAPTIVE_POPT_NONE(  "blind"      ,N_("Read/write mode: All writes are just simulated in memory (default)")),
-               CAPTIVE_POPT_NONE(  "rw"         ,N_("Read/write mode: Write directly to the image file/device")),
-               CAPTIVE_POPT_NONE(  "cdrom"      ,N_("Media type: CD-ROM")),
-               CAPTIVE_POPT_NONE(  "disk"       ,N_("Media type: Disk (default)")),
-
-#undef CAPTIVE_POPT_NONE
-#undef CAPTIVE_POPT_STRING
-#undef CAPTIVE_POPT
-               POPT_TABLEEND
-               };
-
-static const struct poptOption captive_popt_standalone[]={
-               CAPTIVE_POPT_INCLUDE,
-               POPT_AUTOHELP
-               POPT_TABLEEND
-               };
-
-
-static void (*const popt_func_table[])(void)={
-               arg_filesystem,
-               arg_load_module,
-               arg_ro,
-               arg_blind,
-               arg_rw,
-               arg_cdrom,
-               arg_disk,
-               };
-
-
-/* poptCallbackType captive_popt_callback */
-static void captive_popt_callback
-               (poptContext con,enum poptCallbackReason reason,const struct poptOption *opt,const char *arg,const void *data)
-{
-gint funci;
-
-       g_return_if_fail(reason==POPT_CALLBACK_REASON_OPTION);
-
-       funci=(opt-(captive_popt+POPT_OFFSET));
-       g_return_if_fail(funci>=0);
-       g_return_if_fail(funci<(gint)G_N_ELEMENTS(popt_func_table));
-       if (popt_func_table[funci])
-               (*popt_func_table[funci])();
-       free(captive_popt_optarg);
-       captive_popt_optarg=NULL;       /* sanity, shouldn't be needed */
-}
 
+gboolean captive_debug_messages_disabled=FALSE;
 
 static gboolean captive_w32_init(void)
 {
 NTSTATUS err;
 gboolean errbool;
-GIOStatus erriostatus;
 
-       g_return_val_if_fail(captive_option_filesystem!=NULL,FALSE);
-
-       erriostatus=g_io_channel_set_encoding(captive_image_iochannel,
-                       NULL,   /* encoding; force binary data */
-                       NULL);  /* error */
-       g_assert(erriostatus==G_IO_STATUS_NORMAL);
+       g_return_val_if_fail(captive_options!=NULL,FALSE);
+       g_return_val_if_fail(captive_options->filesystem.type!=CAPTIVE_OPTIONS_MODULE_TYPE_EMPTY,FALSE);
 
        /* captive_giochannel_size() only _after_ g_io_channel_set_encoding() ! */
        captive_image_size=captive_giochannel_size(captive_image_iochannel);
        g_return_val_if_fail(captive_image_size>0,FALSE);
 
-       /* Initialize 'FsRtlLegalAnsiCharacterArray'. */
-       captive_FsRtlLegalAnsiCharacterArray_init();
-
        /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() begins. */
        /* ExpInitializeExecutive(); */
                /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() begins
@@ -209,13 +102,19 @@ GIOStatus erriostatus;
                        /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() ends. */
                KeLowerIrql(DISPATCH_LEVEL);
                /*...*/
-               KeLowerIrql(PASSIVE_LEVEL);
-               /*...*/
-               /* create default nls tables */
-               RtlpInitNlsTables();
+               /* FIXME: create default nls tables? Really still needed? */
+               /* Obsolete: RtlpInitNlsTables(); */
                /*...*/
+               /* KeInit2() */
+                       /*...*/
+                       KeInitializeDispatcher();
+                       /*...*/
+               KeLowerIrql(PASSIVE_LEVEL);
+               errbool=SeInit1();
+               g_assert(errbool==TRUE);
                ObInit();
-               /*...*/
+               errbool=SeInit2();
+               g_assert(errbool==TRUE);
                /* PiInitProcessManager(); */
                        /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() begins. */
                        PsInitProcessManagment();
@@ -233,6 +132,10 @@ GIOStatus erriostatus;
                        InitializeListHead(&ModuleListHead);
                        KeInitializeSpinLock(&ModuleListLock);
                        /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement ends. */
+               /*...*/
+               /* Ntinit(); */
+                       NtInitializeEventImplementation();
+                       /*...*/
                /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() ends. */
        /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() ends. */
 
@@ -244,22 +147,42 @@ GIOStatus erriostatus;
        errbool=captive_disk_init();
        g_return_val_if_fail(errbool==TRUE,FALSE);
 
-       while (captive_load_module) {
-gchar *modulename=captive_load_module->data;
+       while (captive_options->load_module) {
 PMODULE_OBJECT ModuleObject_tmp;
 NTSTATUS err;
 
                /* load the module */
-               err=LdrLoadModule(
-                               captive_utf8_to_UnicodeString_alloca(modulename),       /* ModuleName */
+               err=captive_LdrLoadModule(
+                               captive_options->load_module->data,
                                &ModuleObject_tmp);     /* ModuleObjectp */
                g_return_val_if_fail(NT_SUCCESS(err),FALSE);
 
-               captive_load_module=g_list_remove(captive_load_module,modulename);
+               captive_options_module_free(captive_options->load_module->data);
+               /* also frees 'options->load_module->data' */
+               captive_options->load_module=g_list_delete_link(captive_options->load_module,captive_options->load_module);
+               }
+
+       /* Patch 'ntoskrnl.exe' loaded by 'captive_options->load_module' above. */
+       {
+       CHAR *KeNumberProcessorsp=captive_Module_GetExportAddress("ntoskrnl.exe","KeNumberProcessors");
+       
+               g_assert(*KeNumberProcessorsp==0);
+               *KeNumberProcessorsp=KeNumberProcessors;
+               g_assert(*KeNumberProcessorsp==1);
                }
+       /* Apply AFTER any symbols sanity checks above! */
+       if (captive_options->debug_messages)
+               captive_kernel_patches_debug();
+       else
+               captive_kernel_patches_nondebug();
 
-       /* Patch 'ntoskrnl.exe' loaded by 'captive_load_module' above. */
-       captive_kernel_patches();
+       _local_unwind2_addr=captive_Module_GetExportAddress("ntoskrnl.exe","_local_unwind2");
+
+       /* Initialize 'FsRtlLegalAnsiCharacterArray'.
+        * It requires 'ntoskrnl.exe' loaded by 'captive_options->load_module' above;
+        * captive_kernel_patches_debug()/captive_kernel_patches_nondebug() should not be needed.
+        */
+       captive_FsRtlLegalAnsiCharacterArray_init();
 
        /* set TopLevelIrp() - FIXME: where is it set by native reactos? */
        PsGetCurrentThread()->TopLevelIrp=&TopLevelIrp; /* otherwise Io{Get,Set}TopLevelIrp() would SIGSEGV */
@@ -273,108 +196,166 @@ NTSTATUS err;
        /* You must have already captive_signal_init() passed here as the module may
         * call some functions from W32 ntoskrnl.exe.
         */
+       captive_DriverObject_ReinitRoutine=NULL;
        err=captive_LdrpLoadAndCallImage(
                        &ModuleObject,  /* ModuleObjectp */
-                       captive_utf8_to_UnicodeString_alloca(captive_option_filesystem),        /* ModuleName */
-                       &DriverObject,  /* DriverEntry_DriverObject */
+                       &captive_options->filesystem,   /* options_module */
+                       &captive_DriverObject,  /* DriverEntry_DriverObject */
                        captive_utf8_to_UnicodeString_alloca("\\captive\\filesystem")); /* DriverEntry_RegistryPath */
        g_return_val_if_fail(NT_SUCCESS(err),FALSE);
+       if (captive_DriverObject_ReinitRoutine) {
+               captive_stdcall_call_12((CaptiveStdCallFunc12)captive_DriverObject_ReinitRoutine,
+                               &captive_DriverObject,  /* DriverObject */
+                               captive_DriverObject_ReinitRoutine_Context,     /* Context */
+                               (gpointer)1);   /* Count: # of calls of ReinitRoutine incl. the current one */
+               }
 
        return TRUE;
 }
 
 
-static void captive_shutdown_atexit(void);
+static void    captive_log_init_g_log_func_discard
+               (const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
+{
+       g_return_if_fail(message!=NULL);
+
+       /* NOP */
+}
+
+static void captive_log_init_g_log_func
+               (const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data /* unused */)
+{
+int priority;
+
+       g_return_if_fail(message!=NULL);
+
+               /* unused: LOG_EMERG */
+               /* unused: LOG_ALERT */
+       /**/ if (log_level&G_LOG_LEVEL_ERROR)
+               priority=LOG_CRIT;
+       else if (log_level&G_LOG_LEVEL_CRITICAL)
+               priority=LOG_ERR;
+       else if (log_level&G_LOG_LEVEL_WARNING)
+               priority=LOG_WARNING;
+       else if (log_level&G_LOG_LEVEL_MESSAGE)
+               priority=LOG_NOTICE;
+       else if (log_level&G_LOG_LEVEL_INFO)
+               priority=LOG_INFO;
+       else if (log_level&G_LOG_LEVEL_DEBUG)
+               priority=LOG_DEBUG;
+       else /* bogus? */
+               priority=LOG_WARNING;
+
+       syslog(priority,"%s%s%s",
+                       (!(log_level&G_LOG_FLAG_RECURSION) ? "" : "RECURSION: "),
+                       (!(log_level&G_LOG_FLAG_FATAL    ) ? "" : "FATAL: "),
+                       message);
+}
 
+void captive_log_init(const struct captive_options *captive_options)
+{
+       g_return_if_fail(captive_options!=NULL);
+
+       /* FIXME: Fix sharing of different 'debug_messages' for various sandboxes. */
+       captive_debug_messages_disabled=!captive_options->debug_messages;
+
+       /* FIXME: Fix sharing of different 'debug_messages' for various sandboxes. */
+       if (!captive_options->debug_messages) {
+               /* FIXME: Save handler_id and destroy it in captive_vfs_close(). */
+               g_log_set_handler(
+                               G_LOG_DOMAIN,   /* log_domain; "Captive" */
+                               0       /* log_levels */
+                                               | G_LOG_FLAG_RECURSION
+                                               | G_LOG_FLAG_FATAL
+                                               /* The same mask is in:
+                                                * libcaptive/sandbox/server-GLogFunc.c
+                                                * libcaptive/client/init.c
+                                                */
+                                               | G_LOG_LEVEL_MESSAGE
+                                               | G_LOG_LEVEL_INFO
+                                               | G_LOG_LEVEL_DEBUG,
+                               captive_log_init_g_log_func_discard,    /* log_func */
+                               NULL);  /* user_data */
+               }
+       /* We are not the sandboxed slave;
+        * 'syslog_facility' would be '-1' in slave anyway as it is not transferred through CORBA.
+        */
+       if (!captive_options->sandbox || (captive_options->sandbox_server_argv || captive_options->sandbox_server_ior)) {
+               if (captive_options->syslog_facility!=-1) {
+                       openlog(
+                                       /* FIXME: Prefix 'ident' by device/mountpoint. */
+                                       G_LOG_DOMAIN,   /* ident; "Captive"; FIXME: lowercase it for syslog(3)? */
+                                       LOG_CONS|LOG_PID,       /* options */
+                                       captive_options->syslog_facility);      /* facility */
+                       /* FIXME: Save handler_id and destroy it in captive_vfs_close(). */
+                       g_log_set_handler(
+                                       G_LOG_DOMAIN,   /* log_domain; "Captive" */
+                                       0       /* log_levels */
+                                                       | 0     /* !G_LOG_FLAG_RECURSION */
+                                                       | G_LOG_FLAG_FATAL
+                                                       | (G_LOG_LEVEL_MASK & ~(captive_options->debug_messages ? 0 : 0
+                                                                       /* The same mask is in:
+                                                                        * libcaptive/sandbox/server-GLogFunc.c
+                                                                        * libcaptive/client/init.c
+                                                                        */
+                                                                       | G_LOG_LEVEL_MESSAGE
+                                                                       | G_LOG_LEVEL_INFO
+                                                                       | G_LOG_LEVEL_DEBUG)),
+                                       (GLogFunc)captive_log_init_g_log_func,  /* log_func */
+                                       NULL);  /* user_data */
+                       }
+               }
+}
 
 /**
  * captive_init:
- * @captive_args: String with possible options to parse by popt.
- * %NULL value is permitted.
- * @image_iochannel: Host OS file of the disk image to mount.
- * %NULL value is permitted (initialization would not apply in such case).
  *
- * Initializes %libcaptive and loads the specified filesystem.
- * You can supply %NULL value for @image_iochannel - in such case no library
- * initialization is done; only passed @captive_args are parsed. Function
- * will return you %FALSE value as it is not yet initializied.
+ * Expects #captive_options: Parsed by captive_options_parse().
+ * %NULL value is forbidden. Field #image_iochannel %NULL value is forbidden.
  *
- * You should supply only @captive_args with %NULL @image_iochannel if you
- * need to parse+examine the arguments to properly initialize @image_iochannel.
+ * Initializes %libcaptive and loads the specified filesystem.
  *
  * Returns: %TRUE if successfuly initialized.
  */
-gboolean captive_init(const gchar *captive_args,GIOChannel *image_iochannel)
+gboolean captive_init(void)
 {
 gboolean errbool;
-int errint;
 
-#ifdef MAINTAINER_MODE
+       /* We are in sandbox child and we have the right to fail. */
        g_log_set_always_fatal(~(0
                        |G_LOG_LEVEL_MESSAGE
                        |G_LOG_LEVEL_INFO
                        |G_LOG_LEVEL_DEBUG
                        ));
-#endif
 
+       g_return_val_if_fail(captive_standalone_init_done==FALSE,FALSE);
        g_return_val_if_fail(active==FALSE,FALSE);
 
-       /* (optionally) parse the given 'captive_args' string */
-       if (captive_args) {
-int captive_args_argc;
-const char **captive_args_argv=NULL;
-poptContext context;
-gboolean r=FALSE;
-
-               errint=poptParseArgvString(captive_args,&captive_args_argc,&captive_args_argv);
-               if (errint!=0) {
-                       g_assert_not_reached(); /* argument parsing args_error */
-                       goto args_err;
-                       }
-               context=poptGetContext(
-                               PACKAGE,        /* name */
-                               captive_args_argc,captive_args_argv,    /* argc,argv */
-                               captive_popt_standalone,        /* options */
-                               POPT_CONTEXT_KEEP_FIRST);
-               if (context==NULL) {
-                       g_assert_not_reached(); /* argument recognization args_error */
-                       goto args_err_argv;
-                       }
-               errint=poptReadDefaultConfig(context,
-                               TRUE);  /* useEnv */
-               if (errint!=0) {
-                       g_assert_not_reached(); /* argument recognization args_error */
-                       goto args_err_context;
-                       }
-               errint=poptGetNextOpt(context);
-               if (errint!=-1) {
-                       g_assert_not_reached(); /* some non-callbacked argument reached */
-                       goto args_err_context;
-                       }
-               /* FIXME: reject non-"--"-prefixed arguments; how to detected them? */
-               r=TRUE; /* success */
-args_err_context:
-               poptFreeContext(context);
-args_err_argv:
-               free(captive_args_argv);        /* may be NULL here */
-args_err:
-               if (!r) {
-                       puts("FIXME: HELP HERE");
-                       g_return_val_if_reached(r);
-                       }
-               }
+       g_return_val_if_fail(captive_options!=NULL,FALSE);
+       g_return_val_if_fail(captive_options->image_iochannel!=NULL,FALSE);
 
-       /* If we were just requested to parse the arguments. */
-       if (!image_iochannel)
-               return FALSE;
+       /* Do not: g_type_init();
+        * as it is done by: captive_standalone_init()
+        */
 
-       g_return_val_if_fail(image_iochannel!=NULL,FALSE);
+       captive_log_init(captive_options);
 
-       if (captive_option_rwmode==CAPTIVE_OPTION_RWMODE_BLIND)
-               captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(image_iochannel);
-       else
-               captive_image_iochannel=image_iochannel;
-       captive_image_iochannel_orig=image_iochannel;
+       if (captive_options->rwmode==CAPTIVE_OPTION_RWMODE_RW && !captive_options->sandbox)
+               g_error(_("Rejecting --rw --no-sandbox operation as too dangerous - use --blind or --sandbox"));
+
+       captive_image_iochannel=captive_options->image_iochannel;
+       g_io_channel_ref(captive_image_iochannel);
+
+       captive_image_iochannel=captive_storage_relastblock(captive_image_iochannel);
+
+       if (captive_options->rwmode==CAPTIVE_OPTION_RWMODE_BLIND) {
+               GIOChannel *captive_image_iochannel_orig;
+
+               captive_image_iochannel_orig=captive_image_iochannel;
+               captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(captive_image_iochannel,
+                               TRUE);  /* writeable */
+               g_io_channel_unref(captive_image_iochannel_orig);       /* reffed by captive_giochannel_blind_new() */
+               }
 
        /* Do not initialize 'captive_image_size' by captive_giochannel_size() here
         * as we yet need to do g_io_channel_set_encoding().
@@ -383,18 +364,157 @@ args_err:
        errbool=captive_w32_init();
        g_return_val_if_fail(errbool==TRUE,FALSE);
 
-       g_atexit(captive_shutdown_atexit);
-
        active=TRUE;
        return TRUE;
 }
 
 
+static void dismount_volume(void)
+{
+IO_STATUS_BLOCK IoStatusBlock;
+PEXTENDED_IO_STACK_LOCATION StackPtr;
+PIRP Irp;
+NTSTATUS Status;
+DEVICE_OBJECT *DeviceObject=captive_DriverObject.DeviceObject;
+OBJECT_ATTRIBUTES dir_ObjectAttributes;
+HANDLE dir_Handle;
+FILE_OBJECT *FileObject;
+GnomeVFSResult errvfsresult;
+NTSTATUS err;
+IO_STATUS_BLOCK dir_IoStatusBlock;
+/*
+ * TraceFS reported only IRP_MJ_FLUSH_BUFFERS
+ * and IRP_MJ_SHUTDOWN.
+ * Apparently it is not enough, FSCTL_DISMOUNT_VOLUME is needed,
+ * otherwise NT-5.1 autochkdsks the disk and W2000 may give BSOD during boot.
+ */
+enum step {
+       /* First item value assumed to be 0. */
+       STEP_IRP_MJ_FLUSH_BUFFERS_PRE,
+#if 0
+       /* DISABLED: STATUS_ACCESS_DENIED; FIXME: Why?
+        * The official way of device modification is: LOCK,DISMOUNT
+        * but LOCK fails for Captive if any file was written (and closed).
+        */
+       STEP_FSCTL_LOCK_VOLUME,
+#endif
+       STEP_FSCTL_DISMOUNT_VOLUME,
+       STEP_IRP_MJ_FLUSH_BUFFERS_POST,
+       STEP_MAX=3,
+       } stepi;
+WCHAR wzero;
+
+       errvfsresult=captive_ObjectAttributes_init("/!Captive!del",&dir_ObjectAttributes);
+       g_return_if_fail(errvfsresult==GNOME_VFS_OK);
+       
+       /* wanted: * IoCreateFile()->ObCreateObject(,,,IoFileObjectType)->
+        * ->(IoFileObjectType->Create==IopCreateFile)()->IoMountVolume()
+        */
+       CAPTIVE_MEMZERO(&dir_IoStatusBlock);    /* FIXME: Try to pre-clear it - uninitialized otherwise? */
+       dir_IoStatusBlock.Information=FILE_OPENED;      /* FIXME: Try to pre-set it - uninitialized otherwise? */
+       err=IoCreateFile(
+                       &dir_Handle,    /* FileHandle */
+                       GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE|0x80,    /* DesiredAccess; 0xC0100080=GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE|0x80 */
+                       &dir_ObjectAttributes,  /* ObjectAttributes */
+                       &dir_IoStatusBlock,     /* IoStatusBlock */
+                       NULL,   /* AllocationSize; ignored for open */
+                       FILE_ATTRIBUTE_NORMAL,  /* FileAttributes; ignored for open */
+                       (FILE_SHARE_READ | FILE_SHARE_WRITE),   /* ShareAccess; 0 means exclusive */
+                       FILE_OPEN,      /* CreateDisposition */
+                       /* FILE_SYNCHRONOUS_IO_{,NON}ALERT: We need to allow W32 filesystem
+                        * any waits to not to let it return STATUS_CANT_WAIT us.
+                        * Alertability should have only effect on asynchronous events
+                        * from KeWaitForSingleObject() by setting/clearing its parameter 'Alertable'.
+                        */
+                       FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, /* CreateOptions; FILE_DIRECTORY_FILE is forbidden */
+                       NULL,   /* EaBuffer */
+                       0,      /* EaLength */
+                       CreateFileTypeNone,     /* CreateFileType */
+                       NULL,   /* ExtraCreateParameters */
+                       0);     /* Options */
+       g_free(dir_ObjectAttributes.ObjectName);        /* left from captive_gnomevfs_uri_parent_init() */
+       g_return_if_fail(NT_SUCCESS(err));
+       g_return_if_fail(NT_SUCCESS(err)==NT_SUCCESS(dir_IoStatusBlock.Status));
+       g_return_if_fail(dir_IoStatusBlock.Information==FILE_OPENED);
+
+       Status=ObReferenceObjectByHandle(dir_Handle,FILE_LIST_DIRECTORY,IoFileObjectType,UserMode,(PVOID *)&FileObject,NULL);
+       g_assert(NT_SUCCESS(Status));
+
+       g_assert(FileObject->FileName.Length==0);
+       /* 'FileObject->FileName.MaximumLength' is not reset by IoCreateFile(). */
+       g_assert(FileObject->FileName.Buffer==NULL);
+       FileObject->FileName.MaximumLength=2;
+       FileObject->FileName.Buffer=&wzero;
+
+       for (stepi=0;stepi<STEP_MAX;stepi++) {
+               Irp=IoAllocateIrp(DeviceObject->StackSize,TRUE);
+               g_return_if_fail(Irp!=NULL);
+
+               Irp->UserIosb=&IoStatusBlock;
+               Irp->UserEvent=&FileObject->Event;
+               Irp->Tail.Overlay.Thread=PsGetCurrentThread();
+
+               StackPtr=(EXTENDED_IO_STACK_LOCATION *)IoGetNextIrpStackLocation(Irp);
+               switch (stepi) {
+                       case STEP_IRP_MJ_FLUSH_BUFFERS_PRE:
+                               StackPtr->MajorFunction=IRP_MJ_FLUSH_BUFFERS;
+                               break;
+#if 0  /* Disabled, see 'STEP_FSCTL_LOCK_VOLUME'. */
+                       case STEP_FSCTL_LOCK_VOLUME:
+                               StackPtr->MajorFunction=IRP_MJ_FILE_SYSTEM_CONTROL;
+                               StackPtr->MinorFunction=IRP_MN_USER_FS_REQUEST;
+                               StackPtr->Parameters.FileSystemControl.OutputBufferLength=0;
+                               StackPtr->Parameters.FileSystemControl.InputBufferLength=0;
+                               StackPtr->Parameters.FileSystemControl.FsControlCode=FSCTL_LOCK_VOLUME;
+                               StackPtr->Parameters.FileSystemControl.Type3InputBuffer=NULL;
+                               break;
+#endif
+                       case STEP_FSCTL_DISMOUNT_VOLUME:
+                               StackPtr->MajorFunction=IRP_MJ_FILE_SYSTEM_CONTROL;
+                               StackPtr->MinorFunction=IRP_MN_USER_FS_REQUEST;
+                               StackPtr->Parameters.FileSystemControl.OutputBufferLength=0;
+                               StackPtr->Parameters.FileSystemControl.InputBufferLength=0;
+                               StackPtr->Parameters.FileSystemControl.FsControlCode=FSCTL_DISMOUNT_VOLUME;
+                               StackPtr->Parameters.FileSystemControl.Type3InputBuffer=NULL;
+                               break;
+                       case STEP_IRP_MJ_FLUSH_BUFFERS_POST:
+                               StackPtr->MajorFunction=IRP_MJ_FLUSH_BUFFERS;
+                               break;
+                       default: g_assert_not_reached();
+                       }
+               StackPtr->Flags=0;
+               StackPtr->Control=0;
+               StackPtr->DeviceObject=DeviceObject;    /* FIXME: FileObject->Vpb->DeviceObject ? */
+               StackPtr->FileObject=FileObject;
+               StackPtr->CompletionRoutine=NULL;
+
+               /* IoCallDriver() will do one ObDereferenceObject(FileObject)
+                * in its IoSecondStageCompletion().
+                * Do not leave to dereference it itself as we need its 'FileObject->Event'.
+                */
+               ObReferenceObject(FileObject);
+
+               Status=IoCallDriver(DeviceObject,Irp);
+               if (Status==STATUS_PENDING) {
+                       KeWaitForSingleObject(&FileObject->Event,Executive,KernelMode,FALSE,NULL);
+                       Status=IoStatusBlock.Status;
+                       }
+               g_assert(NT_SUCCESS(Status)
+                               || (Status==STATUS_MEDIA_WRITE_PROTECTED && captive_options->rwmode==CAPTIVE_OPTION_RWMODE_RO));
+               }
+
+       ObDereferenceObject(FileObject);
+}
+
+
+BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject);
+void captive_cc_flush(void);
+
 /**
  * captive_shutdown:
  *
  * Closes down %libcaptive. It should flush all pending buffers and successfuly
- * close the filesystem. Variable #captive_image_iochannel will be set to %NULL,
+ * close the filesystem. Variable #captive_options->image_iochannel will not be set to %NULL,
  * you should close such channel yourself.
  *
  * Returns: %TRUE if successfuly shutdown.
@@ -405,36 +525,73 @@ GIOStatus erriostatus;
 
        g_return_val_if_fail(active==TRUE,FALSE);
        g_return_val_if_fail(captive_image_iochannel!=NULL,FALSE);
+       g_return_val_if_fail(captive_options->image_iochannel!=NULL,FALSE);
+
+       /* Invoke all pending idle functions just to not to forget for anything... */
+       while (g_main_context_iteration(
+                       NULL,   /* context; NULL means default one */
+                       FALSE)) /* may_block */
+               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_main_context_iteration() proceeded",G_STRLOC);
+
+       /* Do not: captive_cc_flush();  * based on captive_leave(), not g_main idle *
+        * replaced by IRP_MJ_FLUSH_BUFFERS.
+        */
+
+       /* Probably not needed: captive_shared_cache_map_flush_all();
+        */
+
+       dismount_volume();
+
+       /* Probably not needed: captive_shared_cache_map_flush_all();
+        */
+
+       /* Invoke all pending idle functions just to not to forget for anything... */
+       while (g_main_context_iteration(
+                       NULL,   /* context; NULL means default one */
+                       FALSE)) /* may_block */
+               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_main_context_iteration() proceeded",G_STRLOC);
+
+#if 0
+       captive_PoQueueShutdownWorkItem_hooklist_invoke();
+#endif
+
+       /* Do not: captive_cc_flush();
+        * as the dirty blocks should have been already commited by dismount_volume(),
+        * any further commits would get us just STATUS_VOLUME_DISMOUNTED.
+        */
+
+       /* Do not: captive_cc_unmounting=TRUE;
+        * Without dismount_volume() it was:
+        *      During IoShutdownRegisteredFileSystems() - IRP_MJ_SHUTDOWN to be specific
+        *      some buffers will be written but after the IofCallDriver() it will be
+        *      no longer possible to flush such buffers to their DeviceVolumeFile.
+        *      Therefore we must flush such buffers on the fly although such behaviour
+        *      would crash us in regular case as filesystems access BCBs even after their
+        *      CcUnpinData().
+        * Currently the dirty blocks should have been already commited by dismount_volume(),
+        */
 
        /* FIXME: ntoskrnl/ex/power.c/NtShutdownSystem() does
         * IoShutdownRegistered{Devices,FileSystems} order; is it correct?
         */
        IoShutdownRegisteredFileSystems();
+
+       /* Do not: captive_cc_FileObject_delete(NULL);
+        * as the dirty blocks should have been already commited by dismount_volume(),
+        * any further commits would get us just STATUS_VOLUME_DISMOUNTED.
+        */
+
        IoShutdownRegisteredDevices();
 
-       /* libcaptive is not authorized to shutdown 'captive_image_channel'. */
+       /* Just a sanity if 'captive_image_iochannel' is already reffed a bit more... */
        erriostatus=g_io_channel_flush(
                        captive_image_iochannel,        /* channel */
                        NULL);  /* error */
        g_assert(erriostatus==G_IO_STATUS_NORMAL);
 
-       /* 'captive_image_iochannel' may be blinded wrapper. */
-       if (captive_image_iochannel!=captive_image_iochannel_orig) {
-               erriostatus=g_io_channel_flush(
-                               captive_image_iochannel_orig,   /* channel */
-                               NULL);  /* error */
-               g_assert(erriostatus==G_IO_STATUS_NORMAL);
-               }
-
+       g_io_channel_unref(captive_image_iochannel);
        captive_image_iochannel=NULL;
 
        active=FALSE;
        return TRUE;
 }
-
-
-static void captive_shutdown_atexit(void)
-{
-       if (active)
-               captive_shutdown();
-}