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