/* $Id$ * Init and cleanup code of libcaptive to be called by client application * Copyright (C) 2002-2003 Jan Kratochvil * * 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 * the Free Software Foundation; exactly version 2 of June 1991 is required * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "init.h" /* self */ #include "captive/ldr.h" #include "captive/ldr_exports.h" #include "captive/unicode.h" #include "captive/rtl-file.h" #include #include #include "reactos/internal/ldr.h" #include "reactos/napi/types.h" #include "reactos/internal/kd.h" /* for KDB_LOADDRIVER_HOOK */ #include #include /* for PROT_READ, MAP_SHARED */ #include "reactos/ddk/kefuncs.h" /* for KeInitializeSpinLock() */ #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 "captive/signal.h" /* for captive_signal_init() */ #include "reactos/ddk/psfuncs.h" /* for PsGetCurrentThread() */ #include #include #include #include #include "giochannel-blind.h" #include #include "reactos/internal/se.h" /* for SeInit2() */ #include "captive/leave.h" #include "captive/options.h" #include #include "lib.h" #include #include #include "captive/macros.h" #include "../storage/relastblock.h" /* for captive_storage_relastblock() */ struct captive_options *captive_options; /* Are we initialized? */ static gboolean active; /* Module of fs module itself loaded by captive_w32_init() */ static PMODULE_OBJECT ModuleObject; /* Driver in fs module loaded by captive_w32_init() */ 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 */ void *_local_unwind2_addr; gboolean captive_debug_messages_disabled=FALSE; static gboolean captive_w32_init(void) { NTSTATUS err; gboolean errbool; 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); /* Part of reactos/ntoskrnl/ke/main.c/KiSystemStartup() begins. */ /* ExpInitializeExecutive(); */ /* Part of reactos/ntoskrnl/ke/main.c/ExpInitializeExecutive() begins * here as the rest of the function does a lot of hardware initializations. */ /* LdrInit1(); */ /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() begins. */ InitializeListHead(&ModuleTextListHead); /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInit1() ends. */ KeLowerIrql(DISPATCH_LEVEL); /*...*/ /* 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(); PsInitThreadManagment(); /* Part of reactos/ntoskrnl/ps/psmgr.c/PiInitProcessManager() ends. */ /*...*/ IoInit(); /*...*/ /* LdrInitModuleManagement(); */ /* Part of reactos/ntoskrnl/ldr/loader.c/LdrInitModuleManagement() begins * here as the rest "Create module object for {NTOSKRNL,HAL}" * is dependent on {NTOSKRNL,HAL} PE image headers not provided by libcaptive. */ /* Initialize the module list and spinlock */ 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. */ /* Simulate our PE headers and export the symbols of our complete libraries */ captive_kernel_exports(); errbool=captive_cdrom_init(); g_return_val_if_fail(errbool==TRUE,FALSE); errbool=captive_disk_init(); g_return_val_if_fail(errbool==TRUE,FALSE); while (captive_options->load_module) { PMODULE_OBJECT ModuleObject_tmp; NTSTATUS err; /* load the module */ err=captive_LdrLoadModule( captive_options->load_module->data, &ModuleObject_tmp); /* ModuleObjectp */ g_return_val_if_fail(NT_SUCCESS(err),FALSE); 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(); _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 */ /* Begin possible handling of foreign W32 binary code here */ /* If you want to disable SIGSEGV handler if not needed: * if (ModuleObject->Flags & MODULE_FLAG_PE) */ captive_signal_init(); /* 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_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_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: * * Expects #captive_options: Parsed by captive_options_parse(). * %NULL value is forbidden. Field #image_iochannel %NULL value is forbidden. * * Initializes %libcaptive and loads the specified filesystem. * * Returns: %TRUE if successfuly initialized. */ gboolean captive_init(void) { gboolean errbool; /* 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 )); g_return_val_if_fail(active==FALSE,FALSE); g_return_val_if_fail(captive_options!=NULL,FALSE); g_return_val_if_fail(captive_options->image_iochannel!=NULL,FALSE); /* Initialize GObject subsystem of GLib. */ g_type_init(); 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; 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(). */ errbool=captive_w32_init(); g_return_val_if_fail(errbool==TRUE,FALSE); active=TRUE; return TRUE; } static void dismount_volume(void) { IO_STATUS_BLOCK IoStatusBlock; PIO_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; #if 0 static const ULONG fsctls[2]={ FSCTL_LOCK_VOLUME,FSCTL_DISMOUNT_VOLUME }; int fsctlsi; #endif 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; #if 0 for (fsctlsi=0;fsctlsi<2;fsctlsi++) #endif { Irp=IoAllocateIrp(DeviceObject->StackSize,TRUE); g_return_if_fail(Irp!=NULL); Irp->UserIosb=&IoStatusBlock; 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; #endif 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'. */ 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_options->image_iochannel will not be set to %NULL, * you should close such channel yourself. * * Returns: %TRUE if successfuly shutdown. */ gboolean captive_shutdown(void) { 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. */ dismount_volume(); /* 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(); /* 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); g_io_channel_unref(captive_image_iochannel); captive_image_iochannel=NULL; active=FALSE; return TRUE; }