Prevent 'FSCTL_LOCK_VOLUME' for dismount as we may get 'STATUS_ACCESS_DENIED'.
[captive.git] / src / libcaptive / client / init.c
index d49eed3..f3c35c9 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "config.h"
 
+#include "init.h"      /* self */
 #include "captive/ldr.h"
 #include "captive/ldr_exports.h"
 #include "captive/unicode.h"
@@ -31,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 <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() */
 
 
 struct captive_options *captive_options;
@@ -71,14 +76,7 @@ static TOP_LEVEL_IRP TopLevelIrp;    /* TODO:thread */
 void *_local_unwind2_addr;
 
 
-/* Acceleration hack for ntoskrnl/dbg/print.c/DbgPrint() */
-gboolean captive_get_debug_messages(void)
-{
-       g_return_val_if_fail(captive_options!=NULL,TRUE);
-
-       return captive_options->debug_messages;
-}
-
+gboolean captive_debug_messages_disabled=FALSE;
 
 static gboolean captive_w32_init(void)
 {
@@ -103,8 +101,8 @@ gboolean errbool;
                        /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() ends. */
                KeLowerIrql(DISPATCH_LEVEL);
                /*...*/
-               /* create default nls tables */
-               RtlpInitNlsTables();
+               /* FIXME: create default nls tables? Really still needed? */
+               /* Obsolete: RtlpInitNlsTables(); */
                /*...*/
                /* KeInit2() */
                        /*...*/
@@ -171,14 +169,17 @@ NTSTATUS err;
                *KeNumberProcessorsp=KeNumberProcessors;
                g_assert(*KeNumberProcessorsp==1);
                }
-       /* Apply captive_kernel_patches() AFTER any symbols sanity checks above! */
-       captive_kernel_patches();
+       /* Apply AFTER any symbols sanity checks above! */
+       if (captive_options->debug_messages)
+               captive_kernel_patches_debug();
+       else
+               captive_kernel_patches_nondebug();
 
        _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() should not be needed.
+        * captive_kernel_patches_debug()/captive_kernel_patches_nondebug() should not be needed.
         */
        captive_FsRtlLegalAnsiCharacterArray_init();
 
@@ -202,21 +203,108 @@ NTSTATUS err;
                        captive_utf8_to_UnicodeString_alloca("\\captive\\filesystem")); /* DriverEntry_RegistryPath */
        g_return_val_if_fail(NT_SUCCESS(err),FALSE);
        if (captive_DriverObject_ReinitRoutine) {
-               (*captive_DriverObject_ReinitRoutine)(
+               captive_stdcall_call_12((CaptiveStdCallFunc12)captive_DriverObject_ReinitRoutine,
                                &captive_DriverObject,  /* DriverObject */
                                captive_DriverObject_ReinitRoutine_Context,     /* Context */
-                               1);     /* Count: # of calls of ReinitRoutine incl. the current one */
+                               (gpointer)1);   /* Count: # of calls of ReinitRoutine incl. the current one */
                }
 
        return TRUE;
 }
 
 
-static void    log_discard_func(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
+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:
@@ -247,29 +335,21 @@ gboolean errbool;
        /* Initialize GObject subsystem of GLib. */
        g_type_init();
 
-       if (!captive_options->debug_messages) {
-               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
-                                                * libcaptive/client/vfs.c
-                                                */
-                                               | G_LOG_LEVEL_MESSAGE
-                                               | G_LOG_LEVEL_INFO
-                                               | G_LOG_LEVEL_DEBUG,
-                               log_discard_func,       /* log_func */
-                               NULL);  /* user_data */
-               }
+       captive_log_init(captive_options);
+
+       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;
 
-       if (captive_options->rwmode==CAPTIVE_OPTION_RWMODE_BLIND)
-               captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(captive_options->image_iochannel,
+               captive_image_iochannel_orig=captive_image_iochannel;
+               captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(captive_image_iochannel,
                                TRUE);  /* writeable */
-       else
-               captive_image_iochannel=captive_options->image_iochannel;
+               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().
@@ -286,7 +366,7 @@ gboolean errbool;
 static void dismount_volume(void)
 {
 IO_STATUS_BLOCK IoStatusBlock;
-PIO_STACK_LOCATION StackPtr;
+PEXTENDED_IO_STACK_LOCATION StackPtr;
 PIRP Irp;
 NTSTATUS Status;
 DEVICE_OBJECT *DeviceObject=captive_DriverObject.DeviceObject;
@@ -296,10 +376,26 @@ 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
-static const ULONG fsctls[2]={ FSCTL_LOCK_VOLUME,FSCTL_DISMOUNT_VOLUME };
-int fsctlsi;
+       /* 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);
@@ -308,6 +404,8 @@ WCHAR wzero;
        /* 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 */
@@ -329,8 +427,8 @@ WCHAR wzero;
                        NULL,   /* ExtraCreateParameters */
                        0);     /* Options */
        g_free(dir_ObjectAttributes.ObjectName);        /* left from captive_gnomevfs_uri_parent_init() */
-       g_return_if_fail(NT_SUCCESS(err)==NT_SUCCESS(dir_IoStatusBlock.Status));
        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);
@@ -342,10 +440,7 @@ WCHAR wzero;
        FileObject->FileName.MaximumLength=2;
        FileObject->FileName.Buffer=&wzero;
 
-#if 0
-       for (fsctlsi=0;fsctlsi<2;fsctlsi++)
-#endif
-       {
+       for (stepi=0;stepi<STEP_MAX;stepi++) {
                Irp=IoAllocateIrp(DeviceObject->StackSize,TRUE);
                g_return_if_fail(Irp!=NULL);
 
@@ -353,26 +448,40 @@ WCHAR wzero;
                Irp->UserEvent=&FileObject->Event;
                Irp->Tail.Overlay.Thread=PsGetCurrentThread();
 
-               StackPtr=IoGetNextIrpStackLocation(Irp);
-#if 0
-               StackPtr->MajorFunction=IRP_MJ_FILE_SYSTEM_CONTROL;
-               StackPtr->MinorFunction=IRP_MN_USER_FS_REQUEST;
-#else
-               StackPtr->MajorFunction=IRP_MJ_FLUSH_BUFFERS;
+               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;
 
-#if 0
-               StackPtr->Parameters.FileSystemControl.OutputBufferLength=0;
-               StackPtr->Parameters.FileSystemControl.InputBufferLength=0;
-               StackPtr->Parameters.FileSystemControl.FsControlCode=fsctls[fsctlsi];
-               StackPtr->Parameters.FileSystemControl.Type3InputBuffer=NULL;
-#endif
-
                /* IoCallDriver() will do one ObDereferenceObject(FileObject)
                 * in its IoSecondStageCompletion().
                 * Do not leave to dereference it itself as we need its 'FileObject->Event'.
@@ -384,8 +493,9 @@ WCHAR wzero;
                        KeWaitForSingleObject(&FileObject->Event,Executive,KernelMode,FALSE,NULL);
                        Status=IoStatusBlock.Status;
                        }
-               g_assert(NT_SUCCESS(Status));
-       }
+               g_assert(NT_SUCCESS(Status)
+                               || (Status==STATUS_MEDIA_WRITE_PROTECTED && captive_options->rwmode==CAPTIVE_OPTION_RWMODE_RO));
+               }
 
        ObDereferenceObject(FileObject);
 }
@@ -421,8 +531,14 @@ GIOStatus erriostatus;
         * 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 */
@@ -461,21 +577,13 @@ GIOStatus erriostatus;
 
        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 of 'captive_options->image_iochannel'. */
-       if (captive_image_iochannel!=captive_options->image_iochannel) {
-               erriostatus=g_io_channel_flush(
-                               captive_options->image_iochannel,       /* channel */
-                               NULL);  /* error */
-               g_assert(erriostatus==G_IO_STATUS_NORMAL);
-               g_io_channel_unref(captive_image_iochannel);
-               }
-
+       g_io_channel_unref(captive_image_iochannel);
        captive_image_iochannel=NULL;
 
        active=FALSE;