Rewritten Cache Manager to better match its W32 original behaviour
authorshort <>
Tue, 12 Aug 2003 17:43:22 +0000 (17:43 +0000)
committershort <>
Tue, 12 Aug 2003 17:43:22 +0000 (17:43 +0000)
28 files changed:
NEWS
src/libcaptive/cc/Makefile.am
src/libcaptive/cc/bcbmap.c [new file with mode: 0644]
src/libcaptive/cc/bcbowner.c [new file with mode: 0644]
src/libcaptive/cc/bcbpin.c [new file with mode: 0644]
src/libcaptive/cc/bcbrepin.c [new file with mode: 0644]
src/libcaptive/cc/bcbunpin.c [new file with mode: 0644]
src/libcaptive/cc/cache.c [new file with mode: 0644]
src/libcaptive/cc/copy.c [new file with mode: 0644]
src/libcaptive/cc/dirtypages.c [new file with mode: 0644]
src/libcaptive/cc/init.c [new file with mode: 0644]
src/libcaptive/cc/io.c [new file with mode: 0644]
src/libcaptive/cc/io.h [new file with mode: 0644]
src/libcaptive/cc/loghandle.c [new file with mode: 0644]
src/libcaptive/cc/map.c [deleted file]
src/libcaptive/cc/marshallers.list [new file with mode: 0644]
src/libcaptive/cc/misc.c [new file with mode: 0644]
src/libcaptive/cc/privatebcb-priv.h [new file with mode: 0644]
src/libcaptive/cc/privatebcb.c [new file with mode: 0644]
src/libcaptive/cc/privatebcb.h [new file with mode: 0644]
src/libcaptive/cc/privatebcbmap.c [new file with mode: 0644]
src/libcaptive/cc/privatebcbmap.h [new file with mode: 0644]
src/libcaptive/cc/privatebcbpin.c [new file with mode: 0644]
src/libcaptive/cc/privatebcbpin.h [new file with mode: 0644]
src/libcaptive/cc/sharedcachemap-priv.h [new file with mode: 0644]
src/libcaptive/cc/sharedcachemap.c [new file with mode: 0644]
src/libcaptive/cc/sharedcachemap.h [new file with mode: 0644]
src/libcaptive/client/init.c

diff --git a/NEWS b/NEWS
index 00b9745..17aa844 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,7 @@ NEWS for captive-0.9
 * Implemented filesystem unmount to successfuly remount ntfs volume
 * Generates --bug-pathname resources file for sandbox crash bugreport
 * Implemented TraceFS W32 Cache Manager debug tracer
+* Rewritten Cache Manager to better match its W32 original behaviour
 
 
 NEWS for captive-0.8 (2003-05-02)
index b0e9162..91d52e3 100644 (file)
@@ -21,4 +21,45 @@ include $(top_srcdir)/src/libcaptive/Makefile-libcaptive.am
 
 noinst_LTLIBRARIES=libcc.la
 libcc_la_SOURCES= \
-               map.c
+               bcbmap.c \
+               bcbowner.c \
+               bcbpin.c \
+               bcbrepin.c \
+               bcbunpin.c \
+               cache.c \
+               copy.c \
+               dirtypages.c \
+               init.c \
+               io.c \
+               io.h \
+               loghandle.c \
+               marshallers.c \
+               marshallers.h \
+               misc.c \
+               privatebcb-priv.h \
+               privatebcb.c \
+               privatebcb.h \
+               privatebcbmap.c \
+               privatebcbmap.h \
+               privatebcbpin.c \
+               privatebcbpin.h \
+               sharedcachemap-priv.h \
+               sharedcachemap.c \
+               sharedcachemap.h
+
+marshallers.h: marshallers.list
+          glib-genmarshal --prefix=captive_cc $< --header >$@~ \
+       && (cmp -s $@~ $@ || cp -p $@~ $@)                      \
+       && $(RM) $@~
+
+marshallers.c: marshallers.list
+          glib-genmarshal --prefix=captive_cc $< --body >$@~ \
+       && (cmp -s $@~ $@ || cp -p $@~ $@)                    \
+       && $(RM) $@~
+
+CLEANFILES+= \
+               marshallers.c \
+               marshallers.h
+
+sharedcachemap.lo: marshallers.h
+
diff --git a/src/libcaptive/cc/bcbmap.c b/src/libcaptive/cc/bcbmap.c
new file mode 100644 (file)
index 0000000..4e37add
--- /dev/null
@@ -0,0 +1,143 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) map Bcb handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbmap.h"
+#include "reactos/ntos/types.h"
+#include "reactos/ddk/iotypes.h"
+#include "reactos/ddk/cctypes.h"
+#include "reactos/ddk/ccfuncs.h"
+#include "sharedcachemap.h"
+#include "privatebcb.h"
+
+
+/**
+ * CcMapData:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @FileOffset: The @FileObject file offset from where to map the region from.
+ * Negative value is forbidden.
+ * @Length: Requested length of the region to map from @FileObject.
+ * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
+ * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
+ * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
+ * %MAP_NO_READ will leave the pages unread (libcaptive: unread pages zeroed,
+ * already mapped pages shared with existing content) - needed by ntfs.sys of NT-5.1sp1
+ * as during file write it will %MAP_NO_READ and consequently push the data there;
+ * any request for the same file range read in the meantime will destroy the prepared data!
+ * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
+ * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ * @Buffer: Returns the mapped memory region start address.
+ * This address may not be %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ *
+ * Maps the specified region of @FileObject to automatically chosen address space.
+ * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
+ *
+ * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
+ * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
+ * but W32 filesystems apparently do not conform to it. If you do not take care of the
+ * modified data by some dirty-marking facility such data will be carelessly dropped without
+ * their commit to the disk.
+ *
+ * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
+ *
+ * Every call to this function must be matched by a one corresponding CcUnpinData() call.
+ *
+ * We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
+ * even if such mapping for such file already exists.
+ * We should probably create a new #Bcb for the same space,
+ * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
+ *
+ * Returns: %TRUE if the region was successfuly mapped.
+ * @Bcb with the initialized new memory region.
+ * @Buffer with the address of the exact byte specified by @FileOffset.
+ */
+BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
+               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+CaptivePrivateBcbMapObject *captive_private_bcb_map_object;
+
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
+       g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
+       g_return_val_if_fail(Flags==MAP_WAIT,FALSE);    /* FIXME */
+       g_return_val_if_fail(Bcb!=NULL,FALSE);
+       g_return_val_if_fail(Buffer!=NULL,FALSE);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
+                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+       captive_private_bcb_map_object=captive_private_bcb_map_object_get_ref(SharedCacheMap);
+
+       captive_shared_cache_map_data_validate_read(SharedCacheMap,FileObject,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+
+       *Bcb=captive_private_bcb_object_get_PublicBcb(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_map_object));
+       *Buffer=captive_shared_cache_map_get_buffer(SharedCacheMap)+FileOffset->QuadPart;
+
+       return TRUE;
+}
+
+
+/**
+ * CcRemapBcb:
+ * @Bcb: Initialized #PUBLIC_BCB structure.
+ * %NULL value is forbidden.
+ *
+ * Create a copy of @Bcb for the exactly same file contents as is @Bcb.
+ * The returned copy has the same attributes as the result of CcMapData()
+ * notwithstanding the current state of input @Bcb, therefore it is only
+ * for read/only access etc.
+ *
+ * libcaptive calls CcMapData() internally with @Bcb parameters.
+ *
+ * This function is called only by ntfs.sys of NT-5.1sp1 and it will perform
+ * these operations with the resulting #PUBLIC_BCB:
+ * CcRemapBcb(), CcSetDirtyPinnedData(), CcUnpinData()
+ *
+ * Untested: This call does not set the buffer as dirty - such buffer will not be flushed automatically.
+ *
+ * Returns: Copy of @Bcb. This _pointer_ never equals to @Bcb.
+ * It should be some different
+ * #PUBLIC_BCB structure according to W32 doc.
+ */
+PVOID CcRemapBcb(IN PVOID Bcb)
+{
+CaptivePrivateBcbObject *captive_private_bcb_object;
+CaptiveSharedCacheMapObject *SharedCacheMap;
+CaptivePrivateBcbMapObject *captive_private_bcb_map_object;
+PUBLIC_BCB *PublicBcb;
+
+       g_return_val_if_fail(Bcb!=NULL,NULL);
+
+       captive_private_bcb_object=captive_PublicBcb_to_PrivateBcbObject(Bcb);
+       SharedCacheMap=captive_private_bcb_object_get_SharedCacheMap(captive_private_bcb_object);
+
+       /* 'Bcb' may even belong to SharedCacheMap without any existing map Bcb (just pin Bcbs). */
+       captive_private_bcb_map_object=captive_private_bcb_map_object_get_ref(SharedCacheMap);
+
+       PublicBcb=captive_private_bcb_object_get_PublicBcb(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_map_object));
+
+       return PublicBcb;
+}
diff --git a/src/libcaptive/cc/bcbowner.c b/src/libcaptive/cc/bcbowner.c
new file mode 100644 (file)
index 0000000..0843a87
--- /dev/null
@@ -0,0 +1,77 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) owner handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbpin.h"
+#include "privatebcb.h"
+
+
+/**
+ * CcSetBcbOwnerPointer:
+ * @Bcb: Initialized #PUBLIC_BCB structure.
+ * %NULL value is forbidden.
+ * @Owner: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
+ * %NULL value is forbidden (FIXME: Is it W32 compliant?).
+ *
+ * Set thread-specific pointer for a pinned @Bcb. Use CcUnpinDataForThread()
+ * when @Bcb is no longer needed. CcUnpinDataForThread() is NOT a reverse
+ * operation for this single call CcSetBcbOwnerPointer(), see CcUnpinDataForThread().
+ *
+ * libcaptive implements this function as no-operation as it does not yet
+ * support any threading.
+ */
+VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
+{
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object;
+
+       g_return_if_fail(Bcb!=NULL);
+       g_return_if_fail(Owner!=NULL);
+
+       captive_private_bcb_pin_object=CAPTIVE_PRIVATE_BCB_PIN_OBJECT(captive_PublicBcb_to_PrivateBcbObject(Bcb));
+
+       captive_private_bcb_pin_object_detach_pin(captive_private_bcb_pin_object);
+}
+
+
+/**
+ * CcUnpinDataForThread:
+ * @Bcb: Initialized #PUBLIC_BCB structure.
+ * %NULL value is forbidden.
+ * @ResourceThreadId: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
+ * This pointer had to be passed to CcSetBcbOwnerPointer() #Owner parameter previously.
+ * %NULL value is forbidden (FIXME: is it W32 compliant?).
+ *
+ * CcUnpinData() for a thread specified by @ResourceThreadId.
+ * Reverse operation for a pair of CcMapData() and CcSetBcbOwnerPointer().
+ *
+ * libcaptive implements this function as a simple pass to CcUnpinData() as it does not yet
+ * support any threading.
+ */
+VOID CcUnpinDataForThread(IN PVOID Bcb,IN ERESOURCE_THREAD ResourceThreadId)
+{
+CaptivePrivateBcbObject *captive_private_bcb_object;
+
+       g_return_if_fail(Bcb!=NULL);
+       g_return_if_fail(ResourceThreadId!=0);
+
+       captive_private_bcb_object=captive_PublicBcb_to_PrivateBcbObject(Bcb);
+
+       g_object_unref(captive_private_bcb_object);
+}
diff --git a/src/libcaptive/cc/bcbpin.c b/src/libcaptive/cc/bcbpin.c
new file mode 100644 (file)
index 0000000..739ebea
--- /dev/null
@@ -0,0 +1,249 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) pin Bcb handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbpin.h"
+#include "reactos/ddk/ccfuncs.h"
+#include "privatebcb.h"
+#include "captive/macros.h"
+
+
+/**
+ * CcPinMappedData:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @MappedFileOffset: The @FileObject file offset from where to map the region from.
+ * Negative value is forbidden.
+ * @MappedLength: Requested length of the region to map from @FileObject.
+ * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
+ * @Wait: Whether disk waiting is permitted for this function.
+ * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
+ * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
+ * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
+ * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
+ * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
+ * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
+ * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
+ * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ * @Buffer: Returns the mapped memory region start address.
+ * This address may not be %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ *
+ * This function will allow you to modify the data mapped by CcMapData().
+ * libcaptive does not differentiate this function with CcMapData().
+ *
+ * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
+ *
+ * NEVER re-read any memory from FileObject here!
+ * at least fastfat.sys directory create relies on the fact of CcPinRead()
+ * with already modified buffers to be left intact.
+ *
+ * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
+ * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
+ *
+ * Every call to this function must be matched by a one corresponding CcUnpinData() call.
+ *
+ * Returns: %TRUE if the region was successfuly mapped.
+ * @Bcb with the initialized new memory region.
+ * @Buffer with the address of the exact byte specified by @FileOffset.
+ */
+BOOLEAN CcPinMappedData
+               (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object;
+
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
+       g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
+       g_return_val_if_fail(Flags==PIN_WAIT,FALSE);    /* FIXME */
+       g_return_val_if_fail(Bcb!=NULL,FALSE);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
+                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
+
+       g_assert(Length<=PAGE_SIZE);
+       /* Check PAGE_SIZE crossing */
+       g_assert((FileOffset->QuadPart&~(PAGE_SIZE-1))==((FileOffset->QuadPart+Length-1)&~(PAGE_SIZE-1)));
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+       captive_private_bcb_pin_object=captive_private_bcb_pin_object_get_ref(SharedCacheMap,FileOffset->QuadPart);
+
+       captive_shared_cache_map_data_validate_noread(SharedCacheMap,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+
+       *Bcb=captive_private_bcb_object_get_PublicBcb(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object));
+
+       return TRUE;
+}
+
+
+/**
+ * CcPinRead:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @FileOffset: The @FileObject file offset from where to map the region from.
+ * Negative value is forbidden.
+ * @Length: Requested length of the region to map from @FileObject.
+ * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
+ * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
+ * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
+ * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
+ * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
+ * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
+ * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
+ * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ * @Buffer: Returns the mapped memory region start address.
+ * This address may not be %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ *
+ * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
+ * See these two functions for the details. It has a difference to subsequent
+ * calling of CcMapData() and CcPinMappedData() instead as this call counts
+ * only as one function for a corresponding CcUnpinData() call.
+ *
+ * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
+ *
+ * Every call to this function must be matched by a one corresponding CcUnpinData() call.
+ *
+ * Returns: %TRUE if the region was successfuly mapped.
+ * @Bcb with the initialized new memory region.
+ * @Buffer with the address of the exact byte specified by @FileOffset.
+ */
+BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
+               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object;
+
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
+       g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
+       g_return_val_if_fail(Flags==PIN_WAIT,FALSE);    /* FIXME */
+       g_return_val_if_fail(Bcb!=NULL,FALSE);
+       g_return_val_if_fail(Buffer!=NULL,FALSE);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
+                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
+
+       g_assert(Length<=PAGE_SIZE);
+       /* Check PAGE_SIZE crossing */
+       g_assert((FileOffset->QuadPart&~(PAGE_SIZE-1))==((FileOffset->QuadPart+Length-1)&~(PAGE_SIZE-1)));
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+       captive_private_bcb_pin_object=captive_private_bcb_pin_object_get_ref(SharedCacheMap,FileOffset->QuadPart);
+
+       captive_shared_cache_map_data_validate_read(SharedCacheMap,FileObject,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+
+       *Bcb=captive_private_bcb_object_get_PublicBcb(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object));
+       *Buffer=captive_shared_cache_map_get_buffer(SharedCacheMap)+FileOffset->QuadPart;
+
+       return TRUE;
+}
+
+
+/**
+ * CcSetDirtyPinnedData:
+ * @Bcb: #PUBLIB_BCB to be unpinned from CcRepinBcb().
+ * %NULL value is forbidden.
+ * @Lsn: Optional LSN (Linear Sequence Number) to assign to @Bcb.
+ * %NULL pointer is permitted.
+ *
+ * This call will set the buffer as dirty - such buffer will be flushed automatically.
+ *
+ * You should call it only on CcPin*() buffers - not just CcMapData() buffers
+ * although libcaptive does not differentiate it.
+ */
+VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
+{
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object;
+
+       g_return_if_fail(Bcb!=NULL);
+
+       captive_private_bcb_pin_object=CAPTIVE_PRIVATE_BCB_PIN_OBJECT(captive_PublicBcb_to_PrivateBcbObject(Bcb));
+
+       captive_private_bcb_pin_object_set_dirty(captive_private_bcb_pin_object);
+       if (Lsn)
+               captive_private_bcb_pin_object_set_lsn(captive_private_bcb_pin_object,Lsn->QuadPart);
+}
+
+
+/**
+ * CcPreparePinWrite:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @FileOffset: The @FileObject file offset from where to map the region from.
+ * Negative value is forbidden.
+ * @Length: Requested length of the region to map from @FileObject.
+ * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
+ * @Zero: %TRUE if the area of @FileOffset...@FileOffset+@Length should be cleared.
+ * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
+ * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
+ * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
+ * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
+ * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
+ * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
+ * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ * @Buffer: Returns the mapped memory region start address.
+ * This address may not be %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ *
+ * Wrapper for a pair of CcPinRead() and CcSetDirtyPinnedData().
+ * The mapped range can be also optionally cleared if @Zero is specified.
+ * See CcPinRead() for a more detailed documentation.
+ *
+ * This call will set the buffer as dirty - such buffer will be flushed automatically.
+ *
+ * Returns: %TRUE if the mapping was successful.
+ */
+BOOLEAN CcPreparePinWrite(IN PFILE_OBJECT FileObject,
+               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Zero,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object;
+
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
+       g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
+       g_return_val_if_fail(Flags==PIN_WAIT,FALSE);    /* FIXME */
+       g_return_val_if_fail(Bcb!=NULL,FALSE);
+       g_return_val_if_fail(Buffer!=NULL,FALSE);
+
+       g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE));    /* NOT YET IMPLEMENTED */
+       g_assert(Length==PAGE_SIZE);    /* NOT YET IMPLEMENTED */
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+       captive_private_bcb_pin_object=captive_private_bcb_pin_object_get_ref(SharedCacheMap,FileOffset->QuadPart);
+
+       captive_shared_cache_map_set_data_valid(SharedCacheMap,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+       if (Zero)
+               memset(captive_shared_cache_map_get_buffer(SharedCacheMap)+FileOffset->QuadPart,0,Length);
+       captive_shared_cache_map_set_dirty(SharedCacheMap,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+
+       *Bcb=captive_private_bcb_object_get_PublicBcb(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object));
+       *Buffer=captive_shared_cache_map_get_buffer(SharedCacheMap)+FileOffset->QuadPart;
+
+       return TRUE;
+}
diff --git a/src/libcaptive/cc/bcbrepin.c b/src/libcaptive/cc/bcbrepin.c
new file mode 100644 (file)
index 0000000..18a390d
--- /dev/null
@@ -0,0 +1,93 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) map/pin Bcb CcRepin*() handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcb.h"
+#include "reactos/ddk/status.h"
+#include "privatebcbpin.h"
+
+
+/**
+ * CcRepinBcb:
+ * @Bcb: #PUBLIB_BCB to be repinned.
+ * %NULL value is forbidden.
+ *
+ * Increases usecount on @Bcb. You must call CcUnpinRepinnedBcb() for such @Bcb
+ * afterwards before returning from the #IRP handling.
+ *
+ * libcaptive does not differentiate between CcUnpinData() and CcUnpinRepinnedBcb().
+ * W32 differentiates.
+ *
+ * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
+ *
+ */
+VOID CcRepinBcb(IN PVOID Bcb)
+{
+CaptivePrivateBcbObject *captive_private_bcb_object;
+
+       g_return_if_fail(Bcb!=NULL);
+
+       captive_private_bcb_object=captive_PublicBcb_to_PrivateBcbObject(Bcb);
+
+       g_object_ref(captive_private_bcb_object);
+}
+
+
+/**
+ * CcUnpinRepinnedBcb:
+ * @Bcb: #PUBLIB_BCB to be unpinned from CcRepinBcb().
+ * %NULL value is forbidden.
+ * @WriteThrough: %TRUE if the buffer should be flushed before finishing this function.
+ * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
+ * %NULL value is forbidden. libcaptive always returns %STATUS_SUCCESS here.
+ *
+ * Dereferencing of @Bcb after application of CcRepinBcb().
+ *
+ * This call does not set the buffer as dirty although it will flush the buffers
+ * already set as dirty. Any flushes will be postponed after return from #IRP
+ * handling by the filesystem driver if not requested to be synchronous by @WriteThrough.
+ */
+VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
+{
+CaptivePrivateBcbObject *captive_private_bcb_object;
+
+       g_return_if_fail(Bcb!=NULL);
+       g_return_if_fail(IoStatus!=NULL);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p",G_STRLOC,
+                       Bcb,(gint)WriteThrough,IoStatus);
+
+       captive_private_bcb_object=captive_PublicBcb_to_PrivateBcbObject(Bcb);
+
+       IoStatus->Status=STATUS_SUCCESS;
+       IoStatus->Information=0;
+
+       if (WriteThrough) {     /* FIXME: WriteThrough even if we do not do the last unref? */
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object;
+
+               captive_private_bcb_pin_object=CAPTIVE_PRIVATE_BCB_PIN_OBJECT(captive_private_bcb_object);
+               if (captive_private_bcb_pin_object_is_dirty(captive_private_bcb_pin_object)) {
+                       captive_private_bcb_pin_object_flush(captive_private_bcb_pin_object);
+                       IoStatus->Information=PAGE_SIZE;
+                       }
+               }
+
+       g_object_unref(captive_private_bcb_object);
+}
diff --git a/src/libcaptive/cc/bcbunpin.c b/src/libcaptive/cc/bcbunpin.c
new file mode 100644 (file)
index 0000000..577d750
--- /dev/null
@@ -0,0 +1,45 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) map/pin Bcb CcUnpinData() handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcb.h"
+
+
+/**
+ * CcUnpinData:
+ * @Bcb: Initialized #PUBLIC_BCB structure.
+ * %NULL value is forbidden.
+ *
+ * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
+ *
+ * This call does not set the buffer as dirty although it will flush the buffers
+ * already set as dirty. Any flushes will be postponed after return from #IRP
+ * handling by the filesystem driver.
+ */
+VOID CcUnpinData(IN PVOID Bcb)
+{
+CaptivePrivateBcbObject *captive_private_bcb_object;
+
+       g_return_if_fail(Bcb!=NULL);
+
+       captive_private_bcb_object=captive_PublicBcb_to_PrivateBcbObject(Bcb);
+
+       g_object_unref(captive_private_bcb_object);
+}
diff --git a/src/libcaptive/cc/cache.c b/src/libcaptive/cc/cache.c
new file mode 100644 (file)
index 0000000..9f3fd9e
--- /dev/null
@@ -0,0 +1,109 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) cache handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbpin.h"
+#include "reactos/ddk/status.h"
+
+
+/**
+ * CcPurgeCacheSection:
+ * @SectionObjectPointer: Pointer specifying file to purge;
+ * %NULL value is forbidden.
+ * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
+ * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %TRUE return code in such case.
+ * @FileOffset: Starting offset of the ranger to purge.
+ * %NULL pointer is permitted and it means to purge the whole whole.
+ * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
+ * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
+ * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
+ *
+ * Drop any caching for shrunken file which is not being deleted.
+ * libcaptive will no longer consider such #BCB as dirty.
+ *
+ * Undocumented: It is required during %FSCTL_LOCK_VOLUME by ntfs.sys of NT-5.1sp1
+ * to return %TRUE value if #SharedCacheMap value is %NULL.
+ *
+ * Returns: %TRUE if the range was purged successfuly.
+ */
+BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
+               IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+
+       g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset==NULL,FALSE);   /* NOT IMPLEMENTED YET */
+       g_return_val_if_fail(UninitializeCacheMaps==0,FALSE);   /* NOT IMPLEMENTED YET */
+
+       SharedCacheMap=captive_SectionObjectPointers_to_SharedCacheMap(SectionObjectPointer);
+
+       captive_shared_cache_map_purge(SharedCacheMap);
+
+       return TRUE;
+}
+
+
+/**
+ * CcFlushCache:
+ * @SectionObjectPointer: Pointer specifying file to flush;
+ * %NULL value is forbidden.
+ * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
+ * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
+ * @FileOffset: Optional starting point of the range to flush.
+ * %NULL value is permitted.
+ * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
+ * @IoStatus: Optionally returns the resulting operation status.
+ * #Information field will contain the number of bytes flushed.
+ * %NULL value is permitted.
+ *
+ * Flushes out any pending dirty data in cache manager BCB mapping.
+ * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
+ *
+ * VERIFIED: Ranged flushes.
+ * VERIFIED: Synchronous write.
+ */
+VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
+               IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+guint64 flushed;
+
+       g_return_if_fail(SectionObjectPointer!=NULL);
+
+       SharedCacheMap=captive_SectionObjectPointers_to_SharedCacheMap(SectionObjectPointer);
+
+       if (FileOffset)
+               flushed=captive_shared_cache_map_flush(SharedCacheMap,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+       else
+               flushed=captive_shared_cache_map_flush(SharedCacheMap,0,G_MAXUINT64-1); /* '-1' for overflow safety */
+
+       if (IoStatus) {
+               IoStatus->Status=STATUS_SUCCESS;
+               IoStatus->Information=flushed;
+               }
+}
+
+
+BOOLEAN CcIsThereDirtyData(IN PVPB Vpb)
+{
+       g_return_val_if_fail(Vpb!=NULL,FALSE);  /* We have just one volume mounted anyway. */
+
+       return captive_shared_cache_map_is_any_dirty();
+}
diff --git a/src/libcaptive/cc/copy.c b/src/libcaptive/cc/copy.c
new file mode 100644 (file)
index 0000000..1e17f2e
--- /dev/null
@@ -0,0 +1,142 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) CcCopy*() handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbpin.h"
+#include "reactos/ddk/status.h"
+#include "captive/macros.h"
+
+
+/**
+ * CcCopyRead:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @FileOffset: The @FileObject file offset from where to map the region from.
+ * Negative value is forbidden.
+ * @Length: Requested length of the region to map from @FileObject.
+ * Value %0 is permitted (no effect of this function call).
+ * @Wait: Whether disk waiting is permitted for this function.
+ * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
+ * @Buffer: Address of memory region with already allocated memory of size @Length.
+ * This address may not be %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
+ * %NULL pointer is forbidden.
+ *
+ * Reads the specified region of @FileObject to the given @Buffer.
+ * No on-demand loading is in effect.
+ *
+ * Returns: %TRUE if the region was successfuly filled with @Length bytes.
+ * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
+ * @IoStatus.Information initialized by @Length if successful.
+ */
+BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
+               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset!=NULL,FALSE);
+       g_return_val_if_fail(Wait==TRUE,FALSE);
+       g_return_val_if_fail(Buffer!=NULL,FALSE);
+       g_return_val_if_fail(IoStatus!=NULL,FALSE);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
+                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+
+       captive_shared_cache_map_data_validate_read(SharedCacheMap,FileObject,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+       memcpy(Buffer,captive_shared_cache_map_get_buffer(SharedCacheMap)+FileOffset->QuadPart,Length);
+
+       IoStatus->Status=STATUS_SUCCESS;
+       IoStatus->Information=Length;
+
+       return TRUE;
+}
+
+
+/**
+ * CcCopyWrite:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @FileOffset: The @FileObject file offset from where to map the region from.
+ * Negative value is forbidden.
+ * @Length: Requested length of the region to map from @FileObject.
+ * Value %0 is permitted (no effect of this function call).
+ * @Wait: Whether disk waiting is permitted for this function.
+ * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
+ * @Buffer: Address of memory region with already allocated memory of size @Length.
+ * This address may not be %PAGE_SIZE aligned.
+ * %NULL pointer is forbidden.
+ *
+ * Writes the specified region of the given @Buffer to @FileObject.
+ *
+ * Returns: %TRUE if the region was successfuly written with @Length bytes.
+ */
+BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
+               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       g_return_val_if_fail(FileOffset!=NULL,FALSE);
+       g_return_val_if_fail(Wait==TRUE,FALSE);
+       g_return_val_if_fail(Buffer!=NULL,FALSE);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
+                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
+
+       g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE));    /* NOT YET IMPLEMENTED */
+       g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(Length,PAGE_SIZE));    /* NOT YET IMPLEMENTED */
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+
+       captive_shared_cache_map_set_data_valid(SharedCacheMap,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+       memcpy(captive_shared_cache_map_get_buffer(SharedCacheMap)+FileOffset->QuadPart,Buffer,Length);
+       captive_shared_cache_map_set_dirty(SharedCacheMap,FileOffset->QuadPart,FileOffset->QuadPart+Length);
+
+       return TRUE;
+}
+
+
+BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       g_return_val_if_fail(StartOffset!=NULL,FALSE);
+       g_return_val_if_fail(EndOffset!=NULL,FALSE);
+       g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
+       g_return_val_if_fail((EndOffset->QuadPart-StartOffset->QuadPart)
+                   ==(size_t)(EndOffset->QuadPart-StartOffset->QuadPart),FALSE);
+       g_return_val_if_fail(Wait==TRUE,FALSE);
+
+       g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(StartOffset->QuadPart,PAGE_SIZE));   /* NOT YET IMPLEMENTED */
+       g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(EndOffset->QuadPart,PAGE_SIZE));     /* NOT YET IMPLEMENTED */
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+
+       captive_shared_cache_map_set_data_valid(SharedCacheMap,StartOffset->QuadPart,EndOffset->QuadPart);
+       memset(captive_shared_cache_map_get_buffer(SharedCacheMap)+StartOffset->QuadPart,0,EndOffset->QuadPart-StartOffset->QuadPart);
+       captive_shared_cache_map_set_dirty(SharedCacheMap,StartOffset->QuadPart,EndOffset->QuadPart);
+
+       return TRUE;
+}
diff --git a/src/libcaptive/cc/dirtypages.c b/src/libcaptive/cc/dirtypages.c
new file mode 100644 (file)
index 0000000..1e2793b
--- /dev/null
@@ -0,0 +1,67 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) cache handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbpin.h"
+
+
+/**
+ * CcGetDirtyPages:
+ * @LogHandle: Arbitrary pointer to match with value passed to CcSetLogHandleForFile().
+ * %NULL value is permitted (considered as regular matching value by libcaptive).
+ * @DirtyPageRoutine: #PDIRTY_PAGE_ROUTINE type to call on each dirty page(s).
+ * %NULL value is forbidden.
+ * @Context1: User data to pass to @DirtyPageRoutine.
+ * %NULL value is permitted.
+ * @Context2: User data to pass to @DirtyPageRoutine.
+ *
+ * Searches through the list of dirty #PUBLIC_BCB s of files assigned to @LogHandle
+ * by CcSetLogHandleForFile(). Any clean #PUBLIC_BCB s are ignored
+ * by this function. Function will scan through all #PUBLIC_BCB mapping of each such page
+ * and it will detect its oldest and newest LSN (Logical Sequence Number). Unset
+ * LSN is considered as value 0. Value 0 is returned only if no other LSN is valid
+ * for such case, otherwise such void value 0 is ignored (and oldest and newest LSN
+ * returned are being equal in the case only single LSN was found).
+ *
+ * Found pages are coalesced as much as possible during calls of @DirtyPageRoutine.
+ * Coalescable ranges must have the same detected both oldest and newest LSNs.
+ *
+ * Returns: Oldest LSN across all the #FileObject s found for the given @LogHandle.
+ * Function returns value %0 if no appropriate LSN was found.
+ * libcaptive must return #gint64 instead of the official #LARGE_INTEGER
+ * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
+ *
+ * VERIFIED: func called in runs for each FileObject, FileOffset sorted desc.
+ */
+gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
+               IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
+{
+       /* 'LogHandle' may be NULL */
+       g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LogHandle=%p,DirtyPageRoutine=%p,Context1=%p,Context2=%p",G_STRLOC,
+                       LogHandle,DirtyPageRoutine,Context1,Context2);
+
+       /* FIXME: What does mean LogHandle==NULL? */
+
+       captive_shared_cache_map_set_LogHandle(NULL,LogHandle);
+
+       return captive_shared_cache_map_CcGetDirtyPages(DirtyPageRoutine,Context1,Context2);
+}
diff --git a/src/libcaptive/cc/init.c b/src/libcaptive/cc/init.c
new file mode 100644 (file)
index 0000000..a15060f
--- /dev/null
@@ -0,0 +1,144 @@
+/* $Id$
+ * reactos Cache Manager initialization functions of libcaptive
+ * 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
+ * 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 <glib/gmessages.h>
+#include "reactos/ntos/types.h"
+#include "reactos/ddk/iotypes.h"
+#include "reactos/ddk/cctypes.h"
+#include "sharedcachemap.h"
+#include "reactos/ddk/kefuncs.h"
+
+
+/**
+ * CcInitializeCacheMap:
+ * @FileObject: Existing file to set callbacks for. Ignored by libcaptive.
+ * %NULL value is forbidden.
+ * @FileSizes: Some file sizes suggestions. Ignored by libcaptive.
+ * %NULL value is forbidden.
+ * @PinAccess: CcPin*() functions will be used with @FileObject? Ignored by libcaptive.
+ * @CallBacks: Provided callback functions for readahead/writebehind. Ignored by libcaptive.
+ * %NULL value is forbidden.
+ * @LazyWriterContext: Value passed to functions of @CallBacks to bind with @FileObject.
+ * %NULL value is permitted.
+ *
+ * Provides support of readahead/writebehind in W32. Function should be called
+ * by W32 filesystem to offer its functions to W32 kernel. These functions
+ * are never called by libcaptive, this call is a NOP in libcaptive.
+ *
+ * VERIFIED: Double CcInitializeCacheMap() without CcUninitializeCacheMap().
+ */
+VOID CcInitializeCacheMap(IN PFILE_OBJECT FileObject,
+               IN PCC_FILE_SIZES FileSizes,IN BOOLEAN PinAccess,IN PCACHE_MANAGER_CALLBACKS CallBacks,IN PVOID LazyWriterContext)
+{
+       g_return_if_fail(FileObject!=NULL);
+       g_return_if_fail(FileSizes!=NULL);
+       g_return_if_fail(CallBacks!=NULL);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
+                       "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX,"
+                       "PinAccess=%d,CallBacks=%p,LazyWriterContext=%p",G_STRLOC,
+                       FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
+                       (guint64)FileSizes->ValidDataLength.QuadPart,(gint)PinAccess,CallBacks,LazyWriterContext);
+
+       /* VERIFIED: CcInitializeCacheMap() has already 'SectionObjectPointers' allocated. */
+       g_return_if_fail(FileObject->SectionObjectPointers!=NULL);
+
+       captive_shared_cache_map_get_ref(FileObject,FileSizes,PinAccess,CallBacks,LazyWriterContext);
+}
+
+
+/**
+ * CcUninitializeCacheMap:
+ * @FileObject: File to close caching for.
+ * %NULL value is forbidden.
+ * @TruncateSize: Current file size if @FileObject was truncated to it in the meantime.
+ * @UninitializeCompleteEvent: Optional event to signal after physical write to disk.
+ * %NULL value is permitted.
+ *
+ * Close the cachine facilities from CcInitializeCacheMap().
+ * It is valid to pass @FileObject without being registered by CcInitializeCacheMap().
+ *
+ * FIXME; What to do with files with dirty blocks? Currently we fail assertion on them
+ * although I think W32 would drop such buffers (purge them - not flush them).
+ *
+ * Returns: %TRUE if the caching was closed successfuly.
+ * %FALSE if @FileObject was not registered by CcInitializeCacheMap()
+ * or if its #SharedCacheMap is still in use (such as by other #FileObject reference of
+ * the same #FCB instance).
+ */
+BOOLEAN CcUninitializeCacheMap(IN PFILE_OBJECT FileObject,
+               IN PLARGE_INTEGER TruncateSize OPTIONAL,IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       /* assert current size ==*TruncateSize if TruncateSize */
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,TruncateSize=0x%llX,UninitializeCompleteEvent=%p",
+                       G_STRLOC,FileObject,(guint64)(!TruncateSize ? -1 : TruncateSize->QuadPart),UninitializeCompleteEvent);
+
+       if (FileObject->SectionObjectPointers && !FileObject->SectionObjectPointers->SharedCacheMap)
+               return FALSE;
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+
+       captive_shared_cache_map_w32_unref(SharedCacheMap);
+
+       /* FIXME: should we do KePulseEvent? Are we allowed to signal from inside CcUninitializeCacheMap() ? */
+       if (UninitializeCompleteEvent)
+               KeSetEvent(
+                               &UninitializeCompleteEvent->Event,      /* Event */
+                               IO_NO_INCREMENT,        /* Increment */
+                               FALSE); /* Wait */
+
+       return (FileObject->SectionObjectPointers->SharedCacheMap==NULL);
+}
+
+
+/**
+ * CcSetFileSizes:
+ * @FileObject: Initialized open #FileObject to update file sizes of.
+ * %NULL value is forbidden.
+ * @FileSizes: New file sizes to update cache to.
+ * %NULL value is forbidden.
+ * 
+ * Update cache properties after file sizes were updated.
+ * Probably only the exceeding pages need to be unmapped and BCBs updated
+ * if FileSizes->AllocationSize gets shrunk.
+ *
+ * #AllocationSize must not change if any map or pin Bcbs exist.
+ */
+VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+
+       g_return_if_fail(FileObject!=NULL);
+       g_return_if_fail(FileSizes!=NULL);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
+                       "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
+                       FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
+                       (guint64)FileSizes->ValidDataLength.QuadPart);
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+
+       captive_shared_cache_map_FileSizes_set(SharedCacheMap,FileSizes);
+}
diff --git a/src/libcaptive/cc/io.c b/src/libcaptive/cc/io.c
new file mode 100644 (file)
index 0000000..a3fd996
--- /dev/null
@@ -0,0 +1,102 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) I/O W32 interface of libcaptive
+ * Copyright (C) 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
+ * 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 "io.h"        /* self */
+#include <glib/gmessages.h>
+#include "reactos/ddk/mmfuncs.h"
+#include "reactos/ddk/kefuncs.h"
+#include "reactos/ddk/iofuncs.h"
+
+
+ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
+{
+MDL *Mdl;
+KEVENT Event;
+IO_STATUS_BLOCK IoStatus;
+NTSTATUS err;
+
+       g_return_val_if_fail(FileObject!=NULL,0);
+       g_return_val_if_fail(address!=0,0);
+       g_return_val_if_fail(length!=0,0);
+       g_return_val_if_fail(FileOffset!=NULL,0);
+
+       /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
+        * successfuly read the data. Workaround it - preclear (not postclear) the
+        * buffer and do not make any other assumptions about the data read.
+        */
+       memset(address,0,length);       /* pre-clear the buffer */
+       Mdl=MmCreateMdl(NULL,address,length);   /* FIXME: Deprecated in favor of IoAllocateMdl() */
+       g_assert(Mdl!=NULL);
+       MmBuildMdlForNonPagedPool(Mdl);
+       KeInitializeEvent(&Event,NotificationEvent,FALSE);
+       IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
+       err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
+       g_assert(NT_SUCCESS(err));
+       g_assert(NT_SUCCESS(IoStatus.Status));
+       /* It is not == as the file may be shorter than requested */
+       g_assert(IoStatus.Information<=length);
+       IoFreeMdl(Mdl);
+
+       /* Forbidden, see the comment above about ext2fsd.sys.
+        * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
+        */
+
+       return IoStatus.Information;    /* may be shorter than real! */
+}
+
+
+void captive_Cc_IoPageWrite(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
+{
+MDL *Mdl;
+KEVENT Event;
+IO_STATUS_BLOCK IoStatus;
+NTSTATUS err;
+
+       /* We can get 0=='FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
+        * We are unable to find the correct sectorsize for a filesystem as
+        * even the 'CommonFcb' below contains invalid information.
+        * As we need to have sectorsize <=filesystem_blocksize at least for ext2fsd.sys
+        * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
+        * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
+        */
+
+       Mdl=MmCreateMdl(NULL,address,length);
+       g_assert(Mdl!=NULL);
+       MmBuildMdlForNonPagedPool(Mdl);
+
+       KeInitializeEvent(&Event,NotificationEvent,FALSE);
+
+       /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
+       err=IoSynchronousPageWrite(FileObject,Mdl,FileOffset,&Event,&IoStatus);
+       g_assert(NT_SUCCESS(err));
+       g_assert(NT_SUCCESS(IoStatus.Status));
+
+       /* Also we can get just value 0 if the write is considered 'not dirty'
+        * during FAT write by fastfat.sys.
+        * We can also get just value 8 during write of PAGE_SIZE aligned block
+        * of MappedLength 512 during flush of LSNed buffer on captive_leave(),
+        * probably no assumptions can be made about the returned value at all.
+        */
+#if 0
+       g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(address,PAGE_SIZE)+length);
+#endif
+       g_assert(IoStatus.Information<=length);
+}
diff --git a/src/libcaptive/cc/io.h b/src/libcaptive/cc/io.h
new file mode 100644 (file)
index 0000000..8a46edd
--- /dev/null
@@ -0,0 +1,37 @@
+/* $Id$
+ * Include file for reactos Cache Manager (Cc*) I/O W32 interface of libcaptive
+ * Copyright (C) 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
+ * 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
+ */
+
+
+#ifndef _CAPTIVE_CC_IO_H
+#define _CAPTIVE_CC_IO_H 1
+
+
+#include <glib/gmacros.h>
+#include "reactos/ntos/types.h"
+#include "reactos/ddk/iotypes.h"
+
+
+G_BEGIN_DECLS
+
+ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset);
+void captive_Cc_IoPageWrite(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset);
+
+G_END_DECLS
+
+
+#endif /* _CAPTIVE_CC_IO_H */
diff --git a/src/libcaptive/cc/loghandle.c b/src/libcaptive/cc/loghandle.c
new file mode 100644 (file)
index 0000000..eedda89
--- /dev/null
@@ -0,0 +1,43 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) LogHandle handling of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbpin.h"
+
+
+/**
+ * CcSetLogHandleForFile:
+ *
+ * VERIFIED: LogHandle may be NULL, FlushToLsnRoutine is never called with LogHandle==NULL.
+ * VERIFIED: Only one !=NULL LogHandle and one FlushToLsnRoutine used in one session.
+ */
+VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+
+       g_return_if_fail(FileObject!=NULL);
+       /* 'LogHandle' may be NULL */
+       g_return_if_fail(FlushToLsnRoutine!=NULL);
+
+       SharedCacheMap=captive_FileObject_to_SharedCacheMap(FileObject);
+
+       captive_shared_cache_map_set_LogHandle(SharedCacheMap,LogHandle);
+       captive_shared_cache_map_set_FlushToLsnRoutine(SharedCacheMap,FlushToLsnRoutine);
+}
diff --git a/src/libcaptive/cc/map.c b/src/libcaptive/cc/map.c
deleted file mode 100644 (file)
index 7365564..0000000
+++ /dev/null
@@ -1,2821 +0,0 @@
-/* $Id$
- * reactos Cache Manager mapper emulation of libcaptive
- * 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
- * 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 "reactos/ddk/ccfuncs.h"       /* self */
-#include <glib/gmessages.h>
-#include <glib/ghash.h>
-#include <glib/gmem.h>
-#include <sys/mman.h>
-#include "reactos/ddk/mmfuncs.h"       /* for MmCreateMdl() */
-#include "reactos/ddk/kefuncs.h"       /* for KeInitializeEvent() */
-#include "reactos/ddk/iofuncs.h"       /* for IoPageRead() */
-#include "captive/macros.h"
-#include <sys/types.h>
-#include <sys/shm.h>
-#include <unistd.h>
-#include <glib/glist.h>
-#include "reactos/internal/io.h"       /* for IoSynchronousPageWrite() */
-#include <glib/gmain.h>
-#include "captive/leave.h"
-#include <glib/gtree.h>
-#include "reactos/ddk/obfuncs.h"       /* for ObReferenceObject() */
-
-
-/* CONFIG: */
-
-#define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45 /* FIXME: unknown, undocumented */
-/* Prepare linear mapped space by CcInitializeCacheMap()?
- * NT-5.1sp will map the same pages of a file to the same memory location
- * either by CcMap*() or CcPin*().
- * We do a separate mapping for each Bcb protected by bounding unmapped pages
- * with IPC shm shared pages if a file page is mapped multiple times.
- * I am not aware how they can reserve enough virtual memory space during
- * client with mapping requests of increasing range tendency.
- * This define will try to pre-map the file as one linear are during 
- * CcInitializeCacheMap() according to its (CC_FILE_SIZES *)FileSizes
- * but TODO the range is currently left unused in further mapping functions.
- */
-/* #define CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR 1 */
-
-
-/* Does each privbcb put one reference count on its FileObject?
- * Causes ntfs.sys of NT-5.1sp1:
- *     Assertion 'Header->ValidDataLength.QuadPart == Li0.QuadPart' failed at d:\xpsp1\base\fs\ntfs\write.c line 1749
- */
-/* #define CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT 1 */
-
-
-/* Flush all the buffers directly on the fly.
- * For details see the comment in libcaptive/client/init.c/captive_shutdown().
- */
-gboolean captive_cc_unmounting=FALSE;
-
-
-/* map: (FILE_OBJECT *)FileObject -> (struct fileobject_cached *) */
-static GHashTable *fileobject_cached_hash;
-
-struct fileobject_cached {
-       CACHE_MANAGER_CALLBACKS CallBacks;
-       VOID *LazyWriterContext;
-#ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
-       PVOID Bcb;
-#endif
-       };
-
-static void fileobject_cached_hash_value_destroy_func(struct fileobject_cached *value)
-{
-       g_return_if_fail(value!=NULL);
-
-       g_free(value);
-}
-
-static void fileobject_cached_hash_init(void)
-{
-       if (fileobject_cached_hash)
-               return;
-       fileobject_cached_hash=g_hash_table_new_full(
-                       g_direct_hash,  /* hash_func */
-                       g_direct_equal, /* key_equal_func */
-                       NULL,   /* key_destroy_func */
-                       (GDestroyNotify)fileobject_cached_hash_value_destroy_func);     /* value_destroy_func */
-}
-
-
-/**
- * CcInitializeCacheMap:
- * @FileObject: Existing file to set callbacks for. Ignored by libcaptive.
- * %NULL value is forbidden.
- * @FileSizes: Some file sizes suggestions. Ignored by libcaptive.
- * %NULL value is forbidden.
- * @PinAccess: CcPin*() functions will be used with @FileObject? Ignored by libcaptive.
- * @CallBacks: Provided callback functions for readahead/writebehind. Ignored by libcaptive.
- * %NULL value is forbidden.
- * @LazyWriterContext: Value passed to functions of @CallBacks to bind with @FileObject.
- * %NULL value is permitted.
- *
- * Provides support of readahead/writebehind in W32. Function should be called
- * by W32 filesystem to offer its functions to W32 kernel. These functions
- * are never called by libcaptive, this call is a NOP in libcaptive.
- *
- * VERIFIED: Double CcInitializeCacheMap() without CcUninitializeCacheMap().
- */
-VOID CcInitializeCacheMap(IN PFILE_OBJECT FileObject,
-               IN PCC_FILE_SIZES FileSizes,IN BOOLEAN PinAccess,IN PCACHE_MANAGER_CALLBACKS CallBacks,IN PVOID LazyWriterContext)
-{
-struct fileobject_cached *fileobject_cached;
-#ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
-guint64 size64;
-LARGE_INTEGER FileOffset0;
-PVOID Buffer_trash;
-BOOLEAN errboolean;
-#endif
-
-       g_return_if_fail(FileObject!=NULL);
-       g_return_if_fail(FileSizes!=NULL);
-       g_return_if_fail(CallBacks!=NULL);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
-                       "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX,"
-                       "PinAccess=%d,CallBacks=%p,LazyWriterContext=%p",G_STRLOC,
-                       FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
-                       (guint64)FileSizes->ValidDataLength.QuadPart,(gint)PinAccess,CallBacks,LazyWriterContext);
-
-       fileobject_cached_hash_init();
-
-       /* TODO:thread */
-
-       /* Double reinitialization without CcUninitializeCacheMap()
-        * is done by both fastfat.sys and ntfs.sys of NT-5.1sp1.
-        */
-#if 0
-       g_assert(g_hash_table_lookup(fileobject_cached_hash,FileObject)==NULL);
-#endif
-
-       captive_new(fileobject_cached);
-       fileobject_cached->CallBacks=*CallBacks;
-       fileobject_cached->LazyWriterContext=LazyWriterContext;
-
-       g_hash_table_insert(fileobject_cached_hash,
-                       FileObject,     /* key */
-                       fileobject_cached);     /* value */
-
-#if CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
-       size64=MAX(MAX(FileSizes->AllocationSize.QuadPart,FileSizes->FileSize.QuadPart),
-                       (FileSizes->ValidDataLength.QuadPart==G_MAXINT64 ? 0 : FileSizes->ValidDataLength.QuadPart));
-       g_assert(((ULONG)size64)==size64);
-       FileOffset0.QuadPart=0;
-
-       errboolean=CcMapData(FileObject,&FileOffset0,size64,MAP_WAIT,&fileobject_cached->Bcb,&Buffer_trash);
-       g_assert(errboolean==TRUE);
-#endif
-}
-
-
-BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject);
-struct private_bcb;
-static void CcUninitializeCacheMap_private_bcb_hash_foreach(
-               PUBLIC_BCB *PublicBcb,  /* key */
-               struct private_bcb *privbcb,    /* value */
-               FILE_OBJECT *FileObject);       /* user_data */
-extern GHashTable *private_bcb_hash;
-
-/**
- * CcUninitializeCacheMap:
- * @FileObject: File to close caching for.
- * %NULL value is forbidden.
- * @TruncateSize: Current file size if @FileObject was truncated to it in the meantime.
- * @UninitializeCompleteEvent: Optional event to signal after physical write to disk.
- * %NULL value is permitted.
- *
- * Close the cachine facilities from CcInitializeCacheMap().
- * It is valid to pass @FileObject without being registered by CcInitializeCacheMap().
- *
- * FIXME; What to do with files with dirty blocks? Currently we fail assertion on them
- * although I think W32 would drop such buffers (purge them - not flush them).
- *
- * Returns: %TRUE if the caching was closed successfuly.
- * %FALSE if @FileObject wasn't registered by CcInitializeCacheMap().
- */
-BOOLEAN CcUninitializeCacheMap(IN PFILE_OBJECT FileObject,
-               IN PLARGE_INTEGER TruncateSize OPTIONAL,IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
-{
-BOOLEAN r;
-
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       /* assert current size ==*TruncateSize if TruncateSize */
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,TruncateSize=0x%llX,UninitializeCompleteEvent=%p",
-                       G_STRLOC,FileObject,(guint64)(!TruncateSize ? -1 : TruncateSize->QuadPart),UninitializeCompleteEvent);
-
-       /* TODO:thread */
-       /* Here it will check for any dirty blocks and it will fail g_assert() on them.
-        * I think it may happen for BCBs to exist dirty for 'FileObject' and they
-        * should be silently discarded if CcUninitializeCacheMap() but currently
-        * the assertion is left here as some debugging aid.
-        */
-#if 0
-       r=captive_cc_FileObject_delete(FileObject);
-#else
-       r=g_hash_table_remove(fileobject_cached_hash,FileObject);
-       g_hash_table_foreach(
-                       private_bcb_hash,       /* hash_table */
-                       (GHFunc)CcUninitializeCacheMap_private_bcb_hash_foreach,        /* func */
-                       FileObject);    /* user_data */
-#endif
-
-       /* FIXME: should we do KePulseEvent? Are we allowed to signal from inside CcUninitializeCacheMap() ? */
-       if (UninitializeCompleteEvent)
-               KeSetEvent(
-                               &UninitializeCompleteEvent->Event,      /* Event */
-                               IO_NO_INCREMENT,        /* Increment */
-                               FALSE); /* Wait */
-
-       return r;
-}
-
-
-static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
-{
-       g_return_val_if_fail(PublicBcb!=NULL,FALSE);
-       g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
-       g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
-       g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
-       g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
-
-       return TRUE;
-}
-
-
-/* position in file */
-struct page_position {
-       FILE_OBJECT *FileObject;
-       LARGE_INTEGER FileOffset;       /* always PAGE_SIZE aligned */
-       int shmid;
-       GList *privbcb_list;    /* each mapped page has its one private_bcb */
-       gboolean building;      /* data are not yet read; prevention for CcMapData() reentrancy */
-       };
-
-/* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
-static GHashTable *page_position_hash;
-
-static gboolean validate_page_position(const struct page_position *pagepos)
-{
-int errint;
-struct shmid_ds shmid_ds;
-
-       g_return_val_if_fail(pagepos!=NULL,FALSE);
-       g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
-       g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
-       g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
-       /* 'pagepos->shmid' may be -1 */
-       /* 'pagepos->privbcb_list' may be empty */
-       /* either deleted or alive */
-       /* shmid exists only for >=2 mappings, it is simple mmap() for single mapping */
-       g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL || pagepos->privbcb_list->next==NULL),FALSE);
-
-       if (pagepos->shmid!=-1) {
-               errint=shmctl(pagepos->shmid,
-                               IPC_STAT,       /* cmd */
-                               &shmid_ds);     /* buf */
-               g_return_val_if_fail(errint==0,FALSE);
-
-               g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
-               g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
-               g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
-               g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
-               /* 'shm_perm.mode' was seen with sticky bit 01000: */
-               g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
-               g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
-               /* Do not check 'shmid_ds.shm_cpid' and 'shmid_ds.shm_lpid' against getpid()
-                * as we may run as different PIDs in different threads synchronized by client/ .
-                */
-               g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
-               }
-
-       return TRUE;
-}
-
-static guint page_position_hash_hash_func(const struct page_position *key)
-{
-       g_return_val_if_fail(validate_page_position(key),0);
-
-       return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
-}
-
-static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
-{
-       g_return_val_if_fail(validate_page_position(a),FALSE);
-       g_return_val_if_fail(validate_page_position(b),FALSE);
-
-       return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
-}
-
-static void page_position_hash_key_destroy_func(struct page_position *key)
-{
-       g_return_if_fail(validate_page_position(key));
-       g_assert(key->privbcb_list==NULL);
-       g_assert(key->shmid==-1);
-
-       g_free(key);
-}
-
-static void page_position_hash_init(void)
-{
-       if (page_position_hash)
-               return;
-       page_position_hash=g_hash_table_new_full(
-                       (GHashFunc)page_position_hash_hash_func,        /* hash_func */
-                       (GEqualFunc)page_position_hash_key_equal_func,  /* key_equal_func */
-                       (GDestroyNotify)page_position_hash_key_destroy_func,    /* key_destroy_func */
-                       NULL);  /* value_destroy_func */
-}
-
-
-struct private_bcb {
-       PUBLIC_BCB *PublicBcb;  /* ->MappedLength, ->MappedFileOffset */
-       FILE_OBJECT *FileObject;
-       gint ref_count;
-       gboolean leave_func_pending;
-       /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
-       ULONG MappedLength;     /* It is the real requested size; it is not PAGE_SIZE aligned. */
-       /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
-       LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
-       gpointer base;  /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
-       gboolean dirty;
-       LARGE_INTEGER lsn; gboolean lsn_valid;
-       gboolean forbid_CcUnpinData_leave_func_finalize_remove;
-       };
-
-
-/* map: (struct private_bcb *)privbcb -> (struct private_bcb *)privbcb */
-static GTree *private_bcb_lsn_tree;
-
-/* Such debugging messages are too much expensive.
- * Use to track for deallocated privbcb-s/PublicBcb-s.
- */
-/* #define DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC 1 */
-
-/* Although 'private_bcb_lsn_tree' contains only nodes with 'lsn_valid' TRUE
- * compare_func cannot expect such arguments as it may be used to lookup
- * privbcb unlisted in 'private_bcb_lsn_tree'.
- */
-static gint private_bcb_lsn_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
-{
-gint64 a_lsn,b_lsn;
-gint r;
-
-#ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a=%p,privbcb b=%p",G_STRLOC,a,b);
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb=%p",G_STRLOC,a->PublicBcb);
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb=%p",G_STRLOC,b->PublicBcb);
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)a->PublicBcb->MappedLength);
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)b->PublicBcb->MappedLength);
-#endif
-
-       g_return_val_if_fail(a!=NULL,0);
-       g_return_val_if_fail(validate_Bcb(a->PublicBcb),0);
-       g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MAXINT64);       /* Forbid defined LSN as G_MAXINT64 */
-       g_return_val_if_fail(b!=NULL,0);
-       g_return_val_if_fail(validate_Bcb(b->PublicBcb),0);
-       g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MAXINT64);       /* Forbid defined LSN as G_MAXINT64 */
-
-       if (a==b) {     /* LSN would be apparently the same in such case :-) */
-#ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return 0 (a==b)",G_STRLOC);
-#endif
-               return 0;
-               }
-
-       a_lsn=(!a->lsn_valid ? (LONGLONG)G_MAXINT64 : a->lsn.QuadPart);
-       b_lsn=(!b->lsn_valid ? (LONGLONG)G_MAXINT64 : b->lsn.QuadPart);
-
-       /* Forbid the same LSNs if both defined */
-       g_assert(a_lsn==(LONGLONG)G_MAXINT64 || a_lsn!=b_lsn);
-
-       if ((r=(a_lsn>b_lsn)-(a_lsn<b_lsn))) {
-#ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d (lsn !=)",G_STRLOC,r);
-#endif
-               return r;
-               }
-
-#ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return (ptr !=)",G_STRLOC);
-#endif
-       return (a>b)-(a<b);
-}
-
-static void private_bcb_lsn_tree_init(void)
-{
-       if (private_bcb_lsn_tree)
-               return;
-       private_bcb_lsn_tree=g_tree_new(
-                       (GCompareFunc)private_bcb_lsn_tree_key_compare_func);   /* key_compare_func */
-}
-
-
-static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req);
-
-static void CcUninitializeCacheMap_private_bcb_hash_foreach(
-               PUBLIC_BCB *PublicBcb,  /* key */
-               struct private_bcb *privbcb,    /* value */
-               FILE_OBJECT *FileObject)        /* user_data */
-{
-       g_return_if_fail(validate_Bcb(PublicBcb));
-       g_return_if_fail(privbcb!=NULL);
-       g_return_if_fail(PublicBcb==privbcb->PublicBcb);
-       g_return_if_fail(FileObject!=NULL);
-
-       if (privbcb->FileObject!=FileObject)
-               return;
-
-       captive_privbcb_flush_ordered(privbcb);
-}
-
-
-enum privbcb_item {
-       PRIVBCB_ITEM_NOP,       /* for sanity checks */
-       PRIVBCB_ITEM_DIRTY,
-       PRIVBCB_ITEM_LSN_VALID,
-       PRIVBCB_ITEM_LEAVE_FUNC_PENDING,
-       PRIVBCB_ITEM_REF_COUNT,
-       PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,      /* no protections against 'leave_func_pending' */
-       };
-
-static void privbcb_set(struct private_bcb *privbcb,enum privbcb_item item,gint value)
-{
-       g_return_if_fail(privbcb!=NULL);
-
-       private_bcb_lsn_tree_init();
-
-       g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
-       switch (item) {
-               case PRIVBCB_ITEM_NOP:
-                       break;
-               case PRIVBCB_ITEM_DIRTY:
-                       g_assert(TRUE==value || FALSE==value);
-                       privbcb->dirty=value;
-                       break;
-               case PRIVBCB_ITEM_LSN_VALID:
-                       /* Never change 'privbcb->lsn_valid' while it is linked in 'private_bcb_lsn_tree'
-                        * as it could become unreachable due to private_bcb_lsn_tree_key_compare_func()
-                        * behaviour wrt 'privbcb->lsn_valid'!
-                        */
-                       g_tree_remove(private_bcb_lsn_tree,privbcb);
-                       g_assert(TRUE==value || FALSE==value);
-                       privbcb->lsn_valid=value;
-                       if (privbcb->lsn_valid)
-                               g_tree_insert(private_bcb_lsn_tree,
-                                               privbcb,        /* key */
-                                               privbcb);       /* value */
-                       break;
-               case PRIVBCB_ITEM_LEAVE_FUNC_PENDING:
-                       g_assert(TRUE==value || FALSE==value);
-                       privbcb->leave_func_pending=value;
-                       if (value)
-                               g_assert(privbcb->ref_count==1);
-                       break;
-               case PRIVBCB_ITEM_REF_COUNT:
-                       /* Forbid reincarnation of 'leave_func_pending' privbcb. */
-                       g_assert(!privbcb->leave_func_pending);
-                       g_assert(privbcb->ref_count>=1);
-                       privbcb->ref_count+=value;
-                       g_assert(privbcb->ref_count>=1);
-                       break;
-               case PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR:
-                       g_assert(privbcb->ref_count>=0);
-                       privbcb->ref_count+=value;
-                       g_assert(privbcb->ref_count>=0);
-                       break;
-               default: g_assert_not_reached();
-               }
-       g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
-}
-
-
-/* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
-static GHashTable *private_bcb_hash;
-
-static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
-{
-       g_return_if_fail(validate_Bcb(key));
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: PublicBcb=%p",G_STRLOC,key);
-
-       g_free(key);
-}
-
-static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
-{
-struct page_position pagepos_local;
-gboolean errbool;
-int errint;
-size_t offset;
-gpointer base_aligned;
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: privbcb=%p (->PublicBcb=%p)",G_STRLOC,value,value->PublicBcb);
-
-       g_return_if_fail(value!=NULL);
-       /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
-        * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
-        */
-       g_return_if_fail(value->PublicBcb!=NULL);
-       g_return_if_fail(value->FileObject!=NULL);
-       g_return_if_fail(value->ref_count==0);
-       g_return_if_fail(value->MappedLength>0);
-       g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
-       g_return_if_fail(value->base!=NULL);
-       g_return_if_fail(value->dirty==FALSE);
-       /* Ensure we are not registered in 'private_bcb_lsn_tree'. */
-       g_return_if_fail(value->lsn_valid==FALSE);
-
-       page_position_hash_init();
-
-       base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
-
-       pagepos_local.FileObject=value->FileObject;
-       pagepos_local.privbcb_list=NULL;
-       pagepos_local.shmid=-1;
-       for (
-                       offset=0;
-                       offset<value->MappedLength;
-                       offset+=PAGE_SIZE) {
-struct page_position *pagepos;
-
-               pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
-               pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
-               g_assert(validate_page_position(pagepos));
-               g_assert(pagepos->privbcb_list!=NULL);
-               if (pagepos->privbcb_list->next==NULL) {        /* single mapping by mmap(), no shm */
-                       g_assert(pagepos->shmid==-1);
-                       errint=munmap(((char *)base_aligned)+offset,PAGE_SIZE);
-                       g_assert(errint==0);
-                       }
-               else if (pagepos->privbcb_list->next->next==NULL) {     /* mapping 2 -> 1, convert shm to mmap() */
-struct private_bcb *privbcb_other;
-gint64 privbcb_other_offset_relative;
-gpointer mapped_other,errptr;
-
-                       g_assert(pagepos->shmid!=-1);
-                       if (value==pagepos->privbcb_list->data)
-                               privbcb_other=pagepos->privbcb_list->next->data;
-                       else {
-                               privbcb_other=pagepos->privbcb_list->data;
-                               g_assert(value==pagepos->privbcb_list->next->data);
-                               }
-                       privbcb_other_offset_relative=pagepos->FileOffset.QuadPart-privbcb_other->MappedFileOffset.QuadPart;
-                       /* privbcb_other_offset_relative may be negative up to -PAGE_SIZE
-                        * as 'MappedFileOffset' and 'base' are not page-aligned.
-                        */
-                       mapped_other=privbcb_other->base+privbcb_other_offset_relative;
-                       /* TODO:thread; shmdt()..mmap() window */
-                       errint=shmdt(mapped_other);
-                       g_assert(errint==0);
-                       errptr=mmap(
-                                       mapped_other,   /* start */
-                                       PAGE_SIZE,      /* length */
-                                       PROT_READ|PROT_WRITE,   /* prot */
-                                       MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
-                                       -1,     /* fd; ignored due to MAP_ANONYMOUS */
-                                       0);     /* offset; ignored due to MAP_ANONYMOUS */
-                       g_assert(errptr==mapped_other);
-                       memcpy(mapped_other,((char *)base_aligned)+offset,PAGE_SIZE);
-                       errint=shmdt(((char *)base_aligned)+offset);    /* destroys shm */
-                       g_assert(errint==0);
-                       /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
-                       pagepos->shmid=-1;
-                       }
-               else {  /* mappings (>=3) -> (>=2) */
-                       g_assert(pagepos->shmid!=-1);
-                       errint=shmdt(((char *)base_aligned)+offset);
-                       g_assert(errint==0);
-                       }
-
-               g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
-               pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
-               g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
-
-               if (pagepos->privbcb_list==NULL) {      /* last mapping by mmap() removed */
-                       g_assert(pagepos->shmid==-1);
-                       errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
-                       g_assert(errbool==TRUE);
-                       }
-               else    /* mapping is now >=1, either by mmap() or shm */
-                       g_assert(validate_page_position(pagepos));
-               }
-
-#ifdef CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT
-       ObDereferenceObject(value->FileObject);
-#endif
-
-       g_free(value);
-}
-
-static void private_bcb_hash_init(void)
-{
-       if (private_bcb_hash)
-               return;
-       private_bcb_hash=g_hash_table_new_full(
-                       g_direct_hash,  /* hash_func */
-                       g_direct_equal, /* key_equal_func */
-                       (GDestroyNotify)private_bcb_hash_key_destroy_func,      /* key_destroy_func */
-                       (GDestroyNotify)private_bcb_hash_value_destroy_func);   /* value_destroy_func */
-}
-
-
-static void CcUnpinData_leave_unregister(struct private_bcb *privbcb);
-static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req);
-
-static gboolean captive_cc_FileObject_delete_private_bcb_hash_foreach(
-               PUBLIC_BCB *PublicBcb,  /* key */
-               struct private_bcb *privbcb,  /* value */
-               FILE_OBJECT *FileObject)  /* user_data */
-{
-       g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);    /* meaning: do not remove this 'privbcb' */
-       g_return_val_if_fail(privbcb!=NULL,FALSE);      /* meaning: do not remove this 'privbcb' */
-       g_return_val_if_fail(PublicBcb==privbcb->PublicBcb,FALSE);      /* meaning: do not remove this 'privbcb' */
-       g_return_val_if_fail(FileObject!=NULL,FALSE);   /* meaning: do not remove this 'privbcb' */
-
-       if (FileObject && privbcb->FileObject!=FileObject)
-               return FALSE;   /* do not remove this 'privbcb' */
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,PublicBcb=%p,privbcb=%p,privbcb->ref_count=%d"
-                                       ",privbcb->leave_func_pending=%d,privbcb->dirty=%d,privbcb->lsn=%" G_GINT64_FORMAT,G_STRLOC,
-                       FileObject,PublicBcb,privbcb,privbcb->ref_count,
-                                       privbcb->leave_func_pending,privbcb->dirty,(gint64)(!privbcb->lsn_valid ? -1 : privbcb->lsn.QuadPart));
-
-       /* It is not OK to leave it in 'private_bcb_hash' as although it is protected
-        * against privbcb reincarnation its pagepos items could be reincarnated.
-        */
-       if (privbcb->leave_func_pending) {
-#if 0
-               g_assert_not_reached();
-               captive_privbcb_flush_ordered(privbcb);
-               return FALSE;   /* do not remove this 'privbcb' */
-               g_assert_not_reached();
-               g_assert(privbcb->dirty==FALSE);
-#endif
-               CcUnpinData_leave_unregister(privbcb);
-               g_assert(privbcb->leave_func_pending==FALSE);
-               }
-
-       /* Do not reuse captive_privbcb_flush_ordered() destructor here as we cannot
-        * call g_hash_table_remove() from inside g_hash_table_foreach_remove() callback.
-        */
-
-       g_assert(privbcb->dirty==FALSE);        /* it would be fatal */
-
-       g_assert(privbcb->leave_func_pending==FALSE);
-       /* Force 'ref_count' drop to 0 to pass private_bcb_hash_value_destroy_func(). */
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-privbcb->ref_count);
-       /* Unset LSN to unlink from 'private_bcb_lsn_tree'. */
-       privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: WARNING: Deleting file with pending (fortunately non-dirty) BCBs"
-                       "; privbcb=%p,PublicBcb=%p,FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
-                       privbcb,PublicBcb,privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
-
-       return TRUE;    /* remove this 'privbcb' */
-}
-
-BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
-{
-struct fileobject_cached *fileobject_cached;
-BOOLEAN r=TRUE;
-
-       /* 'FileObject' may be NULL to check/clear the whole cache. */
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p",G_STRLOC,FileObject);
-
-       fileobject_cached_hash_init();
-       private_bcb_hash_init();
-
-       if (!FileObject || !(fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,FileObject)))
-               r=FALSE;
-       else {
-#ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
-               CcUnpinData(fileobject_cached->Bcb);
-#endif
-               g_hash_table_remove(fileobject_cached_hash,FileObject);
-               }
-
-       g_hash_table_foreach_remove(
-                       private_bcb_hash,       /* hash_table */
-                       (GHRFunc)captive_cc_FileObject_delete_private_bcb_hash_foreach, /* func */
-                       FileObject);    /* user_data */
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: returning r=%d",G_STRLOC,r);
-
-       return r;
-}
-
-
-static gboolean captive_privbcb_flush_unordered(struct private_bcb *privbcb);
-
-static void captive_cc_flush_private_bcb_hash_foreach(
-               PUBLIC_BCB *PublicBcb,  /* key */
-               struct private_bcb *privbcb,  /* value */
-               gboolean *flushedp)     /* user_data */
-{
-gboolean errbool;
-
-       g_return_if_fail(validate_Bcb(PublicBcb));
-       g_return_if_fail(privbcb!=NULL);
-       g_return_if_fail(PublicBcb==privbcb->PublicBcb);
-
-       if (!privbcb->dirty)    /* OK */
-               return;
-
-       /* Such privbcb should have been flushed by captive_privbcb_flush_ordered(NULL) before
-        * to ensure its LSN ordering.
-        */
-       g_assert(!privbcb->lsn_valid);
-
-       *flushedp=TRUE;
-       errbool=captive_privbcb_flush_unordered(privbcb);
-       g_assert(errbool==TRUE);
-}
-
-void captive_cc_flush(void)
-{
-gboolean flushed;
-
-       fileobject_cached_hash_init();
-       private_bcb_hash_init();
-
-       do {
-               /* Trace it by g_tree first to attempt to keep LSN ordering */
-               flushed=captive_privbcb_flush_ordered(NULL);
-               g_hash_table_foreach(
-                               private_bcb_hash,       /* hash_table */
-                               (GHFunc)captive_cc_flush_private_bcb_hash_foreach,      /* func */
-                               &flushed);      /* user_data */
-               captive_leave();
-               } while (flushed);
-
-       /* We must not call: captive_cc_FileObject_delete(NULL);
-        * here as it would destroy all the Bcbs needed for the forthcoming
-        * IoShutdownRegisteredFileSystems().
-        */
-}
-
-
-static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
-{
-MDL *Mdl;
-KEVENT Event;
-IO_STATUS_BLOCK IoStatus;
-NTSTATUS err;
-
-       g_return_val_if_fail(FileObject!=NULL,0);
-       g_return_val_if_fail(address!=0,0);
-       g_return_val_if_fail(length!=0,0);
-       g_return_val_if_fail(FileOffset!=NULL,0);
-
-       /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
-        * successfuly read the data. Workaround it - preclear (not postclear) the
-        * buffer and do not make any other assumptions about the data read.
-        */
-       memset(address,0,length);       /* pre-clear the buffer */
-       Mdl=MmCreateMdl(NULL,address,length);   /* FIXME: Deprecated in favor of IoAllocateMdl() */
-       g_assert(Mdl!=NULL);
-       MmBuildMdlForNonPagedPool(Mdl);
-       KeInitializeEvent(&Event,NotificationEvent,FALSE);
-       IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
-       err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
-       g_assert(NT_SUCCESS(err));
-       g_assert(NT_SUCCESS(IoStatus.Status));
-       /* It is not == as the file may be shorter than requested */
-       g_assert(IoStatus.Information<=length);
-       IoFreeMdl(Mdl);
-
-       /* Forbidden, see the comment above about ext2fsd.sys.
-        * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
-        */
-
-       return IoStatus.Information;    /* may be shorter than real! */
-}
-
-
-static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,
-               PUBLIC_BCB *Bcb_find)
-{
-struct page_position pagepos_local,*pagepos;
-struct private_bcb *privbcb,*privbcb_listitem;
-GList *privbcb_list;
-
-       g_return_val_if_fail(FileObject!=NULL,NULL);
-       g_return_val_if_fail(FileOffset!=NULL,NULL);
-       /* 'Bcb_find' may be NULL */
-
-       page_position_hash_init();
-
-       pagepos_local.FileObject=FileObject;
-       pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
-       pagepos_local.privbcb_list=NULL;
-       pagepos_local.shmid=-1;
-       if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local)))
-               return NULL;
-       g_assert(validate_page_position(pagepos));
-       g_assert(pagepos->privbcb_list!=NULL);
-
-       privbcb=NULL;
-       for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
-               privbcb_listitem=privbcb_list->data;
-               if (1
-                               && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
-                               && privbcb_listitem->MappedLength==Length
-                               && (!Bcb_find || Bcb_find==privbcb_listitem->PublicBcb)) {
-                       g_assert(privbcb==NULL);        /* appropriate 'Bcb_find'-matching privbcb found twice */
-                       privbcb=privbcb_listitem;
-                       if (!Bcb_find)
-                               break;
-                       }
-               }
-       if (!privbcb)
-               return NULL;
-
-       /* Sanity check 'privbcb': */
-       g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
-
-       return privbcb;
-}
-
-
-/**
- * CcMapData:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @FileOffset: The @FileObject file offset from where to map the region from.
- * Negative value is forbidden.
- * @Length: Requested length of the region to map from @FileObject.
- * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
- * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
- * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
- * %MAP_NO_READ will leave the pages unread (libcaptive: unread pages zeroed,
- * already mapped pages shared with existing content) - needed by ntfs.sys of NT-5.1sp1
- * as during file write it will %MAP_NO_READ and consequently push the data there;
- * any request for the same file range read in the meantime will destroy the prepared data!
- * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
- * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- * @Buffer: Returns the mapped memory region start address.
- * This address may not be %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- *
- * Maps the specified region of @FileObject to automatically chosen address space.
- * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
- *
- * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
- * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
- * but W32 filesystems apparently do not conform to it. If you do not take care of the
- * modified data by some dirty-marking facility such data will be carelessly dropped without
- * their commit to the disk.
- *
- * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
- *
- * Every call to this function must be matched by a one corresponding CcUnpinData() call.
- *
- * We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
- * even if such mapping for such file already exists.
- * We should probably create a new #Bcb for the same space,
- * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
- *
- * Returns: %TRUE if the region was successfuly mapped.
- * @Bcb with the initialized new memory region.
- * @Buffer with the address of the exact byte specified by @FileOffset.
- */
-BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
-               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
-{
-PUBLIC_BCB **PublicBcbp,*PublicBcb;
-struct page_position pagepos_local;
-LARGE_INTEGER FileOffset_bottom,FileOffset_top;
-gpointer base_aligned;
-size_t length_mapped_aligned;
-gint length_pages_aligned;
-struct page_position **pagepos_array;
-int errint;
-gpointer errptr;
-struct private_bcb *privbcb;
-gboolean after_eof=FALSE;      /* Did we reached the end of file already? */
-
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
-       g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
-       g_return_val_if_fail(Flags&MAP_WAIT,FALSE);     /* FIXME: on-demand loading not yet implemented */
-       g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE);   /* unknown flags? */
-       g_return_val_if_fail(Bcb!=NULL,FALSE);
-       g_return_val_if_fail(Buffer!=NULL,FALSE);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
-                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
-
-       if (Flags&MAP_NO_READ) {
-               g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE));    /* NOT IMPLEMENTED YET */
-               /* It may not be aligned as this function is prepared by 'after_eof' for incomplete tails.
-                * It is being used at least by ntfs.sys of NT-5.1sp1 CcCopyWrite().
-                */
-#if 0
-               g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart+Length,PAGE_SIZE));     /* NOT IMPLEMENTED YET */
-#endif
-               }
-
-       g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
-       g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
-#if 0  /* SectorSize can be really weird but we do not use it anyway */
-       /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
-        * 'SectorSize' may not yet be initialized during mount operation
-        * and 'FileObject->DeviceObject->Vpb' may exist in such case.
-        */
-       g_return_val_if_fail(0
-                                       || FileObject->DeviceObject->SectorSize==0      /* prevent division by 0 */
-                                       || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),
-                       FALSE);
-#endif
-
-       page_position_hash_init();
-       private_bcb_hash_init();
-
-       PublicBcbp=(PUBLIC_BCB **)Bcb;
-       /* extend 'FileOffset' and 'Length' to page boundaries */
-       FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
-       FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
-       length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
-       g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_mapped_aligned,PAGE_SIZE));
-       length_pages_aligned=length_mapped_aligned/PAGE_SIZE;
-
-       /* We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
-        * even if such mapping for such file already exists.
-        * We should probably create a new Bcb for the same space,
-        * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
-        */
-       if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL)))
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX already mapped by privbcb %p",
-                               G_STRLOC,FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,privbcb);
-
-       /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
-       /* TODO: on-demand loading */
-       /* Although we do zeroed-page mapping here we just reserve the linear
-        * space by it.
-        */
-       base_aligned=mmap(
-                       NULL,   /* start */
-                       PAGE_SIZE+length_mapped_aligned+PAGE_SIZE,      /* length; leading and trailing boundary check pages */
-                       PROT_READ|PROT_WRITE,   /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
-                       MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
-                       -1,     /* fd; ignored due to MAP_ANONYMOUS */
-                       0);     /* offset; ignored due to MAP_ANONYMOUS */
-       g_assert(base_aligned!=NULL);
-
-       base_aligned+=PAGE_SIZE;
-       errint=munmap(base_aligned-PAGE_SIZE,PAGE_SIZE);        /* unmap leading boundary check page */
-       g_assert(errint==0);
-       errint=munmap(base_aligned+length_mapped_aligned,PAGE_SIZE);    /* unmap trailing boundary check page */
-       g_assert(errint==0);
-
-       /* Create 'PublicBcb'; referenced by 'privbcb'. */
-       captive_new(PublicBcb);
-       PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
-       PublicBcb->NodeByteSize=sizeof(*PublicBcb);     /* we have no extensions there */
-       PublicBcb->MappedLength=Length;
-       PublicBcb->MappedFileOffset=*FileOffset;
-
-       /* Create 'privbcb'; referenced by created 'pagepos'es. */
-       captive_new(privbcb);
-       privbcb->PublicBcb=PublicBcb;
-       privbcb->FileObject=FileObject;
-       privbcb->ref_count=1;
-       privbcb->leave_func_pending=FALSE;
-       privbcb->MappedLength=PublicBcb->MappedLength;
-       privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
-       privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
-       privbcb->dirty=FALSE;
-       privbcb->lsn_valid=FALSE;
-       privbcb->forbid_CcUnpinData_leave_func_finalize_remove=FALSE;
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_hash_table_insert: PublicBcb=%p,privbcb=%p",G_STRLOC,
-                       PublicBcb,privbcb);
-       g_hash_table_insert(private_bcb_hash,
-                       PublicBcb,      /* key */
-                       privbcb);       /* value */
-
-#ifdef CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT
-       ObReferenceObject(FileObject);
-#endif
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_NOP,0);        /* just for the assertions */
-
-       /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
-        * below as captive_Cc_IoPageRead() has very big consequences as it calls
-        * the filesystem code and we may get reentrancy.
-        * Therefore we store all missing page read requests to 'pagepos_array' and we
-        * fill them when all the memory structures are in consistent state.
-        * We can also coalescence the requests more easily this way.
-        */
-       captive_newn_alloca(pagepos_array,length_pages_aligned);
-
-       pagepos_local.FileObject=FileObject;
-       pagepos_local.shmid=-1;
-       pagepos_local.privbcb_list=NULL;
-       {
-size_t offset;
-
-               for (
-                               offset=0;
-                               offset<length_mapped_aligned;
-                               offset+=PAGE_SIZE) {
-gpointer pageaddr;
-struct page_position *pagepos;
-
-                       pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
-                       pageaddr=(gpointer)(((char *)base_aligned)+offset);
-                       if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
-                               captive_new(pagepos);
-                               *pagepos=pagepos_local;
-                               pagepos->shmid=-1;
-                               pagepos->privbcb_list=NULL;
-                               pagepos->building=TRUE;
-                               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read of offset %llu to new pagepos %p of pure mmap() to address %p",
-                                               G_STRLOC,(unsigned long long)offset,pagepos,pageaddr);
-                               }
-                       else if (pagepos->privbcb_list->next==NULL) {  /* exactly one item (no IPC shm yet) */
-                               g_assert(pagepos->shmid==-1);
-                               if (-1==(pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600)))
-                                       g_error("%s: Failed shmget(2), you may be out of maximum system IPC shared pages",G_STRLOC);
-                               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: created shmid %d out of mmap() for offset %llu to existing pagepos %p to address %p",
-                                               G_STRLOC,pagepos->shmid,(unsigned long long)offset,pagepos,pageaddr);
-                               /* Reentrancy occured? */
-                               g_assert(Flags&MAP_NO_READ || !pagepos->building);
-                               }
-                       else {  /* already >=2 mappings, already IPC shared mem */
-                               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: already shared shmid of offset %llu by existing pagepos %p shmid %d to address %p",
-                                               G_STRLOC,(unsigned long long)offset,pagepos,pagepos->shmid,pageaddr);
-                               g_assert(pagepos->shmid!=-1);
-                               g_assert(pagepos->privbcb_list!=NULL);
-                               /* Reentrancy occured? */
-                               g_assert(Flags&MAP_NO_READ || !pagepos->building);
-                               }
-                       pagepos_array[offset/PAGE_SIZE]=pagepos;
-                       if (pagepos->privbcb_list) {    /* Not the first one - some shm in use now? */
-                               /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
-                                * mmap(2) is still needed to get linear block of memory assignment.
-                                */
-                               /* TODO:thread; munmap()..shmat() window */
-                               errint=munmap(pageaddr,PAGE_SIZE);
-                               g_assert(errint==0);
-                               errptr=shmat(pagepos->shmid,
-                                               pageaddr,       /* shmaddr */
-                                               0);     /* shmflg; !SHM_RDONLY==r/w */
-                               g_assert(errptr==pageaddr);
-                               }
-
-                       g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
-                       pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb);    /* order not important */
-                       g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
-                       if (pagepos->privbcb_list->next==NULL) {        /* exactly one item (we just added it now) */
-
-                               g_assert(pagepos->shmid==-1);
-                               g_hash_table_insert(page_position_hash,
-                                               pagepos,        /* key */
-                                               pagepos);       /* value */
-                               }
-                       else if (pagepos->privbcb_list->next->next==NULL) {     /* second mapping - new shm */
-struct private_bcb *privbcb_orig;
-gint64 privbcb_orig_offset_relative;
-gpointer mapped_orig;
-
-                               g_assert(pagepos->shmid!=-1);
-                               errint=shmctl(pagepos->shmid,
-                                               IPC_RMID,       /* cmd */
-                                               NULL);  /* buf */
-                               g_assert(errint==0);
-                               g_assert(pagepos->privbcb_list->data==privbcb); /* our privbcb should be the first */
-                               g_assert(pagepos->privbcb_list->next!=NULL);    /* we have exactly two items in the list */
-                               g_assert(pagepos->privbcb_list->next->next==NULL);      /* we have exactly two items in the list */
-                               privbcb_orig=pagepos->privbcb_list->next->data;
-                               privbcb_orig_offset_relative=pagepos->FileOffset.QuadPart-privbcb_orig->MappedFileOffset.QuadPart;
-                               /* privbcb_orig_offset_relative may be negative up to -PAGE_SIZE
-                                * as 'MappedFileOffset' and 'base' are not page-aligned.
-                                */
-                               mapped_orig=privbcb_orig->base+privbcb_orig_offset_relative;
-                               memcpy(pageaddr,mapped_orig,PAGE_SIZE);
-                               /* TODO:thread; munmap()..shmat() window */
-                               errint=munmap(mapped_orig,PAGE_SIZE);
-                               g_assert(errint==0);
-                               errptr=shmat(pagepos->shmid,
-                                               mapped_orig,    /* shmaddr */
-                                               0);     /* shmflg; !SHM_RDONLY==r/w */
-                               g_assert(errptr==mapped_orig);
-                               }
-                       g_assert(validate_page_position(pagepos));
-                       }
-               }
-
-       /* Fill in the missing pages content todo-list by stored 'pagepos_array' '->building' flags: */
-       if (!(Flags&MAP_NO_READ)) {
-size_t offset_start,offset_end;
-
-               for (
-                               offset_start=0;
-                               offset_start<length_mapped_aligned;
-                               offset_start=offset_end) {
-struct page_position *pagepos_start;
-size_t offset_built;
-gpointer pageaddr;
-ULONG got;
-
-                       offset_end=offset_start+PAGE_SIZE;
-                       pagepos_start=pagepos_array[offset_start/PAGE_SIZE];
-                       if (!pagepos_start->building)
-                               continue;
-                       g_assert(offset_start==pagepos_start->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
-                       pageaddr=(gpointer)(((char *)base_aligned)+offset_start);
-                       /* Coalescence of the requests. */
-                       for (
-                                       offset_end=offset_start+PAGE_SIZE;
-                                       offset_end<length_mapped_aligned;
-                                       offset_end+=PAGE_SIZE) {
-struct page_position *pagepos_end;
-
-                               pagepos_end=pagepos_array[offset_end/PAGE_SIZE];
-                               g_assert(offset_end==pagepos_end->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
-                               if (!pagepos_end->building)
-                                       break;
-                               }
-                       /* Read the range content: */
-                       got=captive_Cc_IoPageRead(FileObject,pageaddr,offset_end-offset_start,&pagepos_start->FileOffset);
-                       if (after_eof)
-                               g_assert(got==0);
-                       else
-                               g_assert(got<=offset_end-offset_start);
-                       after_eof=(got<offset_end-offset_start);
-                       /* Unmark 'building' flags. */
-                       for (
-                                       offset_built=offset_start;
-                                       offset_built<offset_end;
-                                       offset_built+=PAGE_SIZE) {
-struct page_position *pagepos_built;
-
-                               pagepos_built=pagepos_array[offset_built/PAGE_SIZE];
-                               g_assert(offset_built==pagepos_built->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
-                               g_assert(pagepos_built->building==TRUE);
-                               pagepos_built->building=FALSE;
-                               }
-                       }
-               }
-
-       /* offset _into_ page, may not be PAGE_SIZE aligned: */
-       *Buffer=privbcb->base;
-       *PublicBcbp=PublicBcb;
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
-                       "%s: result: privbcb=%p,privbcb->base=%p,privbcb->base+privbcb->MappedLength=%p,privbcb->MappedLength=0x%lX",
-                       G_STRLOC,privbcb,privbcb->base,((char *)privbcb->base)+privbcb->MappedLength,(unsigned long)privbcb->MappedLength);
-       g_assert(validate_Bcb(PublicBcb)==TRUE);
-       if (FileObject->Flags & FO_STREAM_FILE) {
-               /* IoCreateStreamFileObjectLite() result is mapped many times. */
-               g_assert(!FileObject->SectionObjectPointers->SharedCacheMap);
-               }
-       else {
-               g_assert(!FileObject->SectionObjectPointers->SharedCacheMap);
-               FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
-               }
-
-       return TRUE;
-}
-
-
-/**
- * CcPinMappedData:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @MappedFileOffset: The @FileObject file offset from where to map the region from.
- * Negative value is forbidden.
- * @MappedLength: Requested length of the region to map from @FileObject.
- * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
- * @Wait: Whether disk waiting is permitted for this function.
- * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
- * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
- * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
- * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
- * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
- * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
- * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
- * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- * @Buffer: Returns the mapped memory region start address.
- * This address may not be %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- *
- * This function will allow you to modify the data mapped by CcMapData().
- * libcaptive does not differentiate this function with CcMapData().
- *
- * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
- *
- * NEVER re-read any memory from FileObject here!
- * at least fastfat.sys directory create relies on the fact of CcPinRead()
- * with already modified buffers to be left intact.
- *
- * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
- * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
- *
- * Every call to this function must be matched by a one corresponding CcUnpinData() call.
- *
- * Returns: %TRUE if the region was successfuly mapped.
- * @Bcb with the initialized new memory region.
- * @Buffer with the address of the exact byte specified by @FileOffset.
- */
-BOOLEAN CcPinMappedData
-               (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
-{
-struct private_bcb *privbcb;
-
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
-       g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
-       /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
-       g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
-       g_return_val_if_fail(Bcb!=NULL,FALSE);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
-                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
-
-       privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL);
-       if (!privbcb && (Flags & PIN_IF_BCB))   /* BCB does not exist */
-               return FALSE;
-       /* Appropriate privbcb not found.
-        * This IMO should not happen and it is a bug in the client.
-        * Unfortuantely fastfat.sys FatLocateVolumeLabel() will CcPinMappedData()
-        * the volume label dirent without any previous CcMapData() or CcPinRead() !
-        */
-       if (!privbcb) {
-PVOID Buffer;
-
-               return CcPinRead(
-                               FileObject,
-                               FileOffset,
-                               Length,
-                               0               /* Flags */
-                                               | (Flags&PIN_NO_READ ? MAP_NO_READ : 0)
-                                               | (Flags&PIN_WAIT    ? MAP_WAIT    : 0),
-                               Bcb,
-                               &Buffer);
-               }
-
-       /* NEVER re-read any memory from FileObject here! */
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
-
-       /* Memory already mapped by CcMapData(). */
-       *Bcb=privbcb->PublicBcb;
-       return TRUE;
-}
-
-
-/**
- * CcPinRead:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @FileOffset: The @FileObject file offset from where to map the region from.
- * Negative value is forbidden.
- * @Length: Requested length of the region to map from @FileObject.
- * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
- * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
- * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
- * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
- * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
- * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
- * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
- * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- * @Buffer: Returns the mapped memory region start address.
- * This address may not be %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- *
- * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
- * See these two functions for the details. It has a difference to subsequent
- * calling of CcMapData() and CcPinMappedData() instead as this call counts
- * only as one function for a corresponding CcUnpinData() call.
- *
- * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
- *
- * Every call to this function must be matched by a one corresponding CcUnpinData() call.
- *
- * Returns: %TRUE if the region was successfuly mapped.
- * @Bcb with the initialized new memory region.
- * @Buffer with the address of the exact byte specified by @FileOffset.
- */
-BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
-               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
-{
-PVOID Bcb_CcPinMappedData;
-gboolean errbool;
-gboolean count_CcMapData;
-struct private_bcb *privbcb;
-
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
-       g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
-       /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
-       g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
-       g_return_val_if_fail(Bcb!=NULL,FALSE);
-       g_return_val_if_fail(Buffer!=NULL,FALSE);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
-                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
-
-       if (!(Flags&PIN_IF_BCB)) {
-               errbool=CcMapData(FileObject,FileOffset,Length,
-                               0               /* Flags */
-                                               | (Flags&PIN_NO_READ ? MAP_NO_READ : 0)
-                                               | (Flags&PIN_WAIT    ? MAP_WAIT    : 0),
-                               Bcb,Buffer);
-               g_return_val_if_fail(errbool==TRUE,FALSE);
-               count_CcMapData=TRUE;
-               }
-       else
-               count_CcMapData=FALSE;
-
-       errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
-       if (!(Flags&PIN_IF_BCB)) {
-               g_return_val_if_fail(errbool==TRUE,FALSE);
-               g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
-               }
-       else {
-               if (errbool==FALSE)     /* FALSE permitted; We may fail if Bcb does not exist yet. */
-                       return FALSE;
-               *Bcb=Bcb_CcPinMappedData;
-               }
-
-       privbcb=captive_privbcb_find(FileObject,FileOffset,Length,*Bcb);
-       g_assert(privbcb!=NULL);
-       g_assert(privbcb->ref_count>=1);
-       g_assert(privbcb->PublicBcb==*Bcb);
-       if (count_CcMapData) {
-               /* CcPinRead() must always reference-count only by 1 despite any sub-called functions! */
-               g_assert(privbcb->ref_count>=2);
-               privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
-               }
-
-       return TRUE;
-}
-
-
-static void logging_notify_privbcb_flush(struct private_bcb *privbcb);
-
-/* Returns: The block was dirty and it was flushed to disk.
- */
-static gboolean captive_privbcb_flush_unordered(struct private_bcb *privbcb)
-{
-MDL *Mdl;
-KEVENT Event;
-IO_STATUS_BLOCK IoStatus;
-NTSTATUS err;
-gpointer base_sectoraligned;
-gsize length_sectoraligned;
-LARGE_INTEGER FileOffset_sectoraligned;
-gsize sectorsize;
-struct fileobject_cached *fileobject_cached;
-IRP *saved_TopLevelIrp;
-static gint64 last_written_lsn=G_MININT64;
-
-       g_assert(privbcb->ref_count>0);
-
-       if (!privbcb->dirty)
-               return FALSE;
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);
-       logging_notify_privbcb_flush(privbcb);
-       g_assert(privbcb->ref_count>=2);
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
-
-       if (privbcb->lsn_valid) {
-               if (!(last_written_lsn<=privbcb->lsn.QuadPart))
-                       g_error("%s: last_written_lsn=%" G_GINT64_FORMAT " !<= privbcb->lsn=%" G_GINT64_FORMAT,G_STRLOC,
-                                       last_written_lsn,(gint64)privbcb->lsn.QuadPart);
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LSN write: last_written_lsn was %" G_GINT64_FORMAT ", is %" G_GINT64_FORMAT,
-                               G_STRLOC,last_written_lsn,(gint64)privbcb->lsn.QuadPart);
-               last_written_lsn=privbcb->lsn.QuadPart;
-               }
-
-       /* Prepare NULL TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
-        */
-       saved_TopLevelIrp=IoGetTopLevelIrp();
-       IoSetTopLevelIrp(NULL);
-
-       fileobject_cached_hash_init();
-       if ((fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,privbcb->FileObject)))
-               (*fileobject_cached->CallBacks.AcquireForLazyWrite)(
-                               fileobject_cached->LazyWriterContext,   /* Context */
-                               TRUE);  /* Wait */
-
-       g_assert(privbcb->FileObject->DeviceObject!=NULL);
-
-       /* We can get 0=='privbcb->FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
-        * FIXME: We are unable to find the correct sectorsize for a filesystem as
-        * even the 'CommonFcb' below contains invalid information.
-        * As we need to have 'sectorsize' <=filesystem_blocksize at least for ext2fsd.sys
-        * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
-        * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
-        */
-       sectorsize=PAGE_SIZE;
-#if 0
-       sectorsize=privbcb->FileObject->DeviceObject->SectorSize;
-       if (privbcb->FileObject->FsContext) {
-REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileObject->FsContext;
-
-               /* FIXME: Check CommonFcb->Type */
-               /* 'AllocationSize' can be less than 'sectorsize' if the total file length is smaller.
-                * Observed with ext2fsd.sys volume-file.
-                */
-               if (sectorsize<CommonFcb->AllocationSize.QuadPart)
-                       sectorsize=CommonFcb->AllocationSize.QuadPart;
-               }
-#endif
-
-       /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
-       g_assert(sectorsize>0);
-       g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,sectorsize));
-       /* We align here directly the 'privbcb->base' which is not correct.
-        * We should rather aligned according to 'privbcb->MappedOffset' but
-        * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
-        * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
-        */
-       g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
-                                ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,sectorsize));
-       base_sectoraligned  =CAPTIVE_ROUND_DOWN(privbcb->base,sectorsize);
-       length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,sectorsize)
-                       -((char *)base_sectoraligned);
-       g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,sectorsize));
-       FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,sectorsize);
-
-       Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
-       g_assert(Mdl!=NULL);
-       MmBuildMdlForNonPagedPool(Mdl);
-
-       KeInitializeEvent(&Event,NotificationEvent,FALSE);
-
-       /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
-       err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
-       g_assert(NT_SUCCESS(err));
-       g_assert(NT_SUCCESS(IoStatus.Status));
-       /* We should write at least the unaligned mapped data although we
-        * do not need to successfuly write the whole aligned amount.
-        * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
-        * during FAT write by fastfat.sys.
-        * We can also get just value 8 during write of PAGE_SIZE aligned block
-        * of privbcb->MappedLength 512 during flush of LSNed buffer on captive_leave(),
-        * probably no assumptions can be made about the returned value at all.
-        */
-#if 0
-       g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
-                       +privbcb->MappedLength);
-#endif
-       g_assert(IoStatus.Information<=length_sectoraligned);
-
-       if (fileobject_cached)
-               (*fileobject_cached->CallBacks.ReleaseFromLazyWrite)(
-                               fileobject_cached->LazyWriterContext);  /* Context */
-
-       /* Restore TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
-        */
-       g_assert(NULL==IoGetTopLevelIrp());
-       IoSetTopLevelIrp(saved_TopLevelIrp);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
-                       "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
-                       privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
-                       base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
-                       (gulong)IoStatus.Information);
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE);
-       return TRUE;
-}
-
-struct captive_privbcb_flush_ordered_param {
-       struct private_bcb *privbcb_req;
-       struct private_bcb *found;      /* return */
-       };
-
-static gboolean captive_privbcb_flush_ordered_foreach_get_first(struct private_bcb *key,struct private_bcb *value,
-               struct captive_privbcb_flush_ordered_param *captive_privbcb_flush_ordered_param /* data */)
-{
-       g_return_val_if_fail(key!=NULL,TRUE);   /* meaning: stop the traversal */
-       g_return_val_if_fail(value!=NULL,TRUE); /* meaning: stop the traversal */
-       g_return_val_if_fail(key==value,TRUE);  /* meaning: stop the traversal */
-       g_return_val_if_fail(validate_Bcb(key->PublicBcb),TRUE);        /* meaning: stop the traversal */
-       g_return_val_if_fail(captive_privbcb_flush_ordered_param!=NULL,TRUE);   /* meaning: stop the traversal */
-       g_return_val_if_fail(captive_privbcb_flush_ordered_param->found==NULL,TRUE);    /* meaning: stop the traversal */
-
-       g_assert(key->lsn_valid==TRUE);
-
-       if (key==captive_privbcb_flush_ordered_param->privbcb_req) {
-               captive_privbcb_flush_ordered_param->found=key;
-               return TRUE;    /* stop the traversal */
-               }
-
-       if (!key->dirty)
-               return FALSE;   /* continue the traversal */
-
-       captive_privbcb_flush_ordered_param->found=key; /* first dirty LSNed buffer */
-       return TRUE;    /* stop the traversal */
-}
-
-/* Use 'privbcb==NULL' to flush all LSNed entries of cache.
- * WARNING: Non-LSNed entries will NOT be flushed!
- * Returns: Anything was really flushed to disk.
- */
-static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req)
-{
-struct private_bcb *privbcb;
-struct captive_privbcb_flush_ordered_param captive_privbcb_flush_ordered_param;
-gboolean errbool;
-gboolean r=FALSE;
-
-       if ((privbcb=privbcb_req) && !privbcb->dirty)
-               return FALSE;
-
-       if ((privbcb=privbcb_req) && !privbcb->lsn_valid) {
-               r=captive_privbcb_flush_unordered(privbcb);
-               g_assert(r==TRUE);
-               return r;
-               }
-
-       private_bcb_lsn_tree_init();
-
-       for (;;) {
-               captive_privbcb_flush_ordered_param.privbcb_req=privbcb_req;
-               captive_privbcb_flush_ordered_param.found=NULL;
-               g_tree_foreach(private_bcb_lsn_tree,
-                               (GTraverseFunc)captive_privbcb_flush_ordered_foreach_get_first, /* func */
-                               &captive_privbcb_flush_ordered_param);  /* user_data */
-               if (captive_privbcb_flush_ordered_param.found==NULL) {
-                       g_assert(privbcb_req==NULL);    /* Global LSN-ordered flush has no more entries. */
-                       break;
-                       }
-               privbcb=captive_privbcb_flush_ordered_param.found;
-
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
-                               "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
-                               ",privbcb->leave_func_pending=%d",G_STRLOC,
-                               privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
-                               (int)privbcb->leave_func_pending);
-
-               if (privbcb->dirty) {
-                       errbool=captive_privbcb_flush_unordered(privbcb);
-                       g_assert(errbool==TRUE);
-                       r=TRUE;
-                       continue;       /* Restart as anything could change by calling W32 hassle. */
-                       }
-
-               if (privbcb!=privbcb_req)
-                       continue;       /* We just flushed a LSNed buffer not yet requested to be destroyed. */
-
-               g_assert(privbcb==g_tree_lookup(private_bcb_lsn_tree,privbcb));
-               break;
-               }
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d",G_STRLOC,(int)r);
-       return r;
-}
-
-
-static void CcUnpinData_leave_func_finalize(struct private_bcb *privbcb)
-{
-gboolean errbool;
-
-       g_assert(privbcb->dirty==FALSE);
-       g_assert(privbcb->lsn_valid==FALSE);
-       g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
-       g_assert(privbcb->leave_func_pending==TRUE);
-       g_assert(privbcb->ref_count==1);
-       privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
-       if (!privbcb->forbid_CcUnpinData_leave_func_finalize_remove) {
-               errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
-               g_assert(errbool==TRUE);
-               }
-}
-
-
-static void CcUnpinData_leave_func_privbcb_destroy_func(struct private_bcb *privbcb)   /* NULL to traverse the whole tree */
-{
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
-                       ",privbcb->lsn_valid=%d",G_STRLOC,
-                       privbcb,(!privbcb ? -1 : privbcb->ref_count),(!privbcb ? -1 : privbcb->leave_func_pending),
-                       (!privbcb ? -1 : privbcb->lsn_valid));
-
-       g_assert(privbcb->leave_func_pending==TRUE);
-       g_assert(privbcb->ref_count==1);
-       captive_privbcb_flush_ordered(privbcb);
-       privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
-       CcUnpinData_leave_func_finalize(privbcb);
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
-}
-
-
-/* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
-static GHashTable *private_bcb_unpin_leave_hash;
-
-static void private_bcb_unpin_leave_hash_init(void)
-{
-       if (private_bcb_unpin_leave_hash)
-               return;
-       private_bcb_unpin_leave_hash=g_hash_table_new_full(
-                       g_direct_hash,  /* hash_func */
-                       g_direct_equal, /* key_equal_func */
-                       NULL,   /* key_destroy_func */
-                       (GDestroyNotify)CcUnpinData_leave_func_privbcb_destroy_func);   /* value_destroy_func */
-}
-
-
-static void CcUnpinData_leave_unregister(struct private_bcb *privbcb)
-{
-gboolean errbool;
-
-       g_return_if_fail(privbcb!=NULL);
-
-       g_return_if_fail(private_bcb_unpin_leave_hash!=NULL);
-
-       g_assert(privbcb->forbid_CcUnpinData_leave_func_finalize_remove==FALSE);
-       privbcb->forbid_CcUnpinData_leave_func_finalize_remove=TRUE;
-       g_assert(privbcb==g_hash_table_lookup(private_bcb_unpin_leave_hash,privbcb->PublicBcb));
-       errbool=g_hash_table_remove(private_bcb_unpin_leave_hash,privbcb->PublicBcb);
-       g_assert(errbool==TRUE);
-       g_assert(NULL==g_hash_table_lookup(private_bcb_unpin_leave_hash,privbcb->PublicBcb));
-}
-
-
-static void CcUnpinData_leave_func(struct private_bcb *privbcb /* user_data; unused */)
-{
-GHashTable *private_bcb_unpin_leave_hash_local;
-
-       if (!private_bcb_unpin_leave_hash)
-               return;
-
-       private_bcb_unpin_leave_hash_local=private_bcb_unpin_leave_hash;
-       private_bcb_unpin_leave_hash=NULL;
-
-       g_hash_table_destroy(private_bcb_unpin_leave_hash_local);       /* utilize value_destroy_func */
-}
-
-
-/**
- * CcUnpinData:
- * @Bcb: Initialized #PUBLIC_BCB structure.
- * %NULL value is forbidden.
- *
- * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
- *
- * This call does not set the buffer as dirty although it will flush the buffers
- * already set as dirty. Any flushes will be postponed after return from #IRP
- * handling by the filesystem driver.
- */
-VOID CcUnpinData(IN PVOID Bcb)
-{
-PUBLIC_BCB *PublicBcb;
-struct private_bcb *privbcb;
-
-       g_return_if_fail(validate_Bcb(Bcb));
-
-       private_bcb_hash_init();
-
-       PublicBcb=(PUBLIC_BCB *)Bcb;
-       privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
-       g_return_if_fail(privbcb!=NULL);
-       g_assert(!privbcb->leave_func_pending);
-
-       g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
-       if (privbcb->FileObject->Flags & FO_STREAM_FILE) {
-               /* IoCreateStreamFileObjectLite() result is mapped many times. */
-               g_assert(!privbcb->FileObject->SectionObjectPointers->SharedCacheMap);
-               }
-       else
-               g_assert(privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
-                       "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
-                       ",privbcb->leave_func_pending=%d",G_STRLOC,
-                       privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
-                       (int)privbcb->leave_func_pending);
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
-       if (privbcb->ref_count>0)
-               return;
-
-       if (!(privbcb->FileObject->Flags & FO_STREAM_FILE))
-               privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
-
-       /* Caboom: lfs (log file system) of ntfs.sys-NT5.1sp1 will do:
-        * CcPinRead(); CcUnpinData(); access Buffer (wanna-crash);
-        * Therefore we must postpone the buffer unmapping to some idle function...
-        * I expect it a bug in ntfs.sys.
-        */
-       if (!privbcb->leave_func_pending) {
-               privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);      /* '+1' gets decreased by 'leave_func_pending'. */
-               privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,TRUE);
-               if (!private_bcb_unpin_leave_hash) {
-                       private_bcb_unpin_leave_hash_init();
-                       g_assert(private_bcb_unpin_leave_hash!=NULL);
-                       captive_leave_register(
-                                       (captive_leave_func)CcUnpinData_leave_func,     /* func */
-                                       NULL);  /* data; unused */
-                       }
-               g_hash_table_insert(private_bcb_unpin_leave_hash,PublicBcb,privbcb);
-               }
-
-       if (captive_cc_unmounting)
-               captive_leave();
-}
-
-
-/**
- * CcRepinBcb:
- * @Bcb: #PUBLIB_BCB to be repinned.
- * %NULL value is forbidden.
- *
- * Increases usecount on @Bcb. You must call CcUnpinRepinnedBcb() for such @Bcb
- * afterwards before returning from the #IRP handling.
- *
- * libcaptive does not differentiate between CcUnpinData() and CcUnpinRepinnedBcb().
- * W32 differentiates.
- *
- * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
- *
- */
-VOID CcRepinBcb(IN PVOID Bcb)
-{
-PUBLIC_BCB *PublicBcb;
-struct private_bcb *privbcb;
-
-       g_return_if_fail(validate_Bcb(Bcb));
-
-       private_bcb_hash_init();
-
-       PublicBcb=(PUBLIC_BCB *)Bcb;
-       privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
-       g_return_if_fail(privbcb!=NULL);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
-                       Bcb,privbcb->FileObject);
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
-}
-
-
-/**
- * CcUnpinRepinnedBcb:
- * @Bcb: #PUBLIB_BCB to be unpinned from CcRepinBcb().
- * %NULL value is forbidden.
- * @WriteThrough: %TRUE if the buffer should be flushed before finishing this function.
- * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
- * %NULL value is forbidden. libcaptive always returns %STATUS_SUCCESS here.
- *
- * Dereferencing of @Bcb after application of CcRepinBcb().
- *
- * This call does not set the buffer as dirty although it will flush the buffers
- * already set as dirty. Any flushes will be postponed after return from #IRP
- * handling by the filesystem driver if not requested to be synchronous by @WriteThrough.
- */
-VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
-{
-PUBLIC_BCB *PublicBcb;
-struct private_bcb *privbcb;
-
-       g_return_if_fail(validate_Bcb(Bcb));
-       g_return_if_fail(IoStatus!=NULL);
-
-       private_bcb_hash_init();
-
-       PublicBcb=(PUBLIC_BCB *)Bcb;
-       privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
-       g_return_if_fail(privbcb!=NULL);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
-                       Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
-
-       if (WriteThrough)
-               captive_privbcb_flush_ordered(privbcb);
-
-       IoStatus->Status=STATUS_SUCCESS;
-       IoStatus->Information=privbcb->MappedLength;
-
-       CcUnpinData(Bcb);
-}
-
-/**
- * CcSetDirtyPinnedData:
- * @Bcb: #PUBLIB_BCB to be unpinned from CcRepinBcb().
- * %NULL value is forbidden.
- * @Lsn: Optional LSN (Linear Sequence Number) to assign to @Bcb.
- * %NULL pointer is permitted.
- *
- * This call will set the buffer as dirty - such buffer will be flushed automatically.
- *
- * You should call it only on CcPin*() buffers - not just CcMapData() buffers
- * although libcaptive does not differentiate it.
- */
-VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
-{
-PUBLIC_BCB *PublicBcb;
-struct private_bcb *privbcb;
-
-       g_return_if_fail(validate_Bcb(Bcb));
-
-       private_bcb_hash_init();
-
-       PublicBcb=(PUBLIC_BCB *)Bcb;
-       privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
-       g_return_if_fail(privbcb!=NULL);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
-                       Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
-
-       /* 'privbcb->ref_count' not to be increased by this function. */
-
-       if (Lsn) {
-               /* Unset it first to not to get into unreachable privbcb in 'private_bcb_lsn_tree'. */
-               privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
-               privbcb->lsn=*Lsn;
-               privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,TRUE);
-               }
-       else {
-               /* FIXME: Unset 'lsn_valid' if !Lsn ? Undocumented by W32. */
-               g_assert(privbcb->lsn_valid==FALSE);    /* NOT IMPLEMENTED YET */
-               }
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,TRUE);
-}
-
-
-struct CcSetFileSizes_param {
-       PFILE_OBJECT FileObject;
-       PCC_FILE_SIZES FileSizes;
-       };
-
-/* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
-static void CcSetFileSizes_private_bcb_hash_foreach(
-               PUBLIC_BCB *PublicBcb,  /* key */
-               struct private_bcb *privbcb,    /* value */
-               struct CcSetFileSizes_param *CcSetFileSizes_param)      /* user_data */
-{
-       g_return_if_fail(validate_Bcb(PublicBcb));
-       g_return_if_fail(privbcb!=NULL);
-       g_return_if_fail(CcSetFileSizes_param!=NULL);
-
-       if (privbcb->FileObject!=CcSetFileSizes_param->FileObject)
-               return;
-
-       /* size changes behind our cached range? */
-       if (1
-                       && CcSetFileSizes_param->FileSizes->AllocationSize .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
-                       && CcSetFileSizes_param->FileSizes->FileSize       .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
-                       && CcSetFileSizes_param->FileSizes->ValidDataLength.QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
-               return;
-
-       /* FIXME: check BCB && 'struct page_position' invalidities */
-       g_assert_not_reached(); /* NOT IMPLEMENTED YET */
-}
-
-/**
- * CcSetFileSizes:
- * @FileObject: Initialized open #FileObject to update file sizes of.
- * %NULL value is forbidden.
- * @FileSizes: New file sizes to update cache to.
- * %NULL value is forbidden.
- * 
- * Update cache properties after file sizes were updated.
- * Probably only the exceeding pages need to be unmapped and BCBs updated
- * if FileSizes->AllocationSize gets shrunk.
- *
- * FIXME: Currently a NOP with no effect by libcaptive.
- */
-VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
-{
-struct CcSetFileSizes_param CcSetFileSizes_param;
-
-       g_return_if_fail(FileObject!=NULL);
-       g_return_if_fail(FileSizes!=NULL);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
-                       "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
-                       FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
-                       (guint64)FileSizes->ValidDataLength.QuadPart);
-
-       CcSetFileSizes_param.FileObject=FileObject;
-       CcSetFileSizes_param.FileSizes=FileSizes;
-       g_hash_table_foreach(
-                       private_bcb_hash,       /* hash_table */
-                       (GHFunc)CcSetFileSizes_private_bcb_hash_foreach,        /* func */
-                       &CcSetFileSizes_param); /* user_data */
-}
-
-
-/**
- * CcPurgeCacheSection:
- * @SectionObjectPointer: Pointer specifying file to purge;
- * %NULL value is forbidden.
- * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
- * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %TRUE return code in such case.
- * @FileOffset: Starting offset of the ranger to purge.
- * %NULL pointer is permitted and it means to purge the whole whole.
- * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
- * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
- * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
- *
- * Drop any caching for shrunken file which is not being deleted.
- * libcaptive will no longer consider such #BCB as dirty.
- *
- * Undocumented: It is required during %FSCTL_LOCK_VOLUME by ntfs.sys of NT-5.1sp1
- * to return %TRUE value if #SharedCacheMap value is %NULL.
- *
- * Returns: %TRUE if the range was purged successfuly.
- */
-BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
-               IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
-{
-PUBLIC_BCB *PublicBcb;
-struct private_bcb *privbcb;
-
-       g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
-       if (SectionObjectPointer->SharedCacheMap==NULL)
-               return TRUE;    /* nothing to purge; never return FALSE for ntfs.sys of NT-5.1sp1! */
-       g_return_val_if_fail(FileOffset==NULL,FALSE);   /* NOT IMPLEMENTED YET */
-
-       PublicBcb=SectionObjectPointer->SharedCacheMap;
-       g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
-
-       private_bcb_hash_init();
-
-       privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
-       g_return_val_if_fail(privbcb!=NULL,FALSE);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
-                       "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
-                       SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
-                       (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
-
-       g_assert_not_reached();
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE);  /* purge it */
-
-       return TRUE;
-}
-
-
-/**
- * CcCopyRead:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @FileOffset: The @FileObject file offset from where to map the region from.
- * Negative value is forbidden.
- * @Length: Requested length of the region to map from @FileObject.
- * Value %0 is permitted (no effect of this function call).
- * @Wait: Whether disk waiting is permitted for this function.
- * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
- * @Buffer: Address of memory region with already allocated memory of size @Length.
- * This address may not be %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
- * %NULL pointer is forbidden.
- *
- * Reads the specified region of @FileObject to the given @Buffer.
- * No on-demand loading is in effect.
- *
- * Returns: %TRUE if the region was successfuly filled with @Length bytes.
- * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
- * @IoStatus.Information initialized by @Length if successful.
- */
-BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
-               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
-{
-PVOID MappedBcb;
-PVOID MappedBuffer;
-gboolean errbool;
-
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset!=NULL,FALSE);
-       g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
-       g_return_val_if_fail(Buffer!=NULL,FALSE);
-       g_return_val_if_fail(IoStatus!=NULL,FALSE);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
-                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
-
-       IoStatus->Status=STATUS_UNSUCCESSFUL;
-       IoStatus->Information=0;
-
-       if (Length) {
-               errbool=CcPinRead(
-                               FileObject,     /* FileObject */
-                               FileOffset,     /* FileOffset */
-                               Length, /* Length */
-                               0               /* Flags; !PIN_NO_READ */
-                                               | (Wait ? PIN_WAIT : 0),
-                               &MappedBcb,     /* Bcb */
-                               &MappedBuffer); /* Buffer */
-               g_return_val_if_fail(errbool==TRUE,FALSE);
-
-               memcpy(Buffer,MappedBuffer,Length);
-
-               CcUnpinData(MappedBcb); /* no error code */
-               }
-
-       IoStatus->Status=STATUS_SUCCESS;
-       IoStatus->Information=Length;
-
-       return TRUE;
-}
-
-
-/**
- * CcCopyWrite:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @FileOffset: The @FileObject file offset from where to map the region from.
- * Negative value is forbidden.
- * @Length: Requested length of the region to map from @FileObject.
- * Value %0 is permitted (no effect of this function call).
- * @Wait: Whether disk waiting is permitted for this function.
- * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
- * @Buffer: Address of memory region with already allocated memory of size @Length.
- * This address may not be %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- *
- * Writes the specified region of the given @Buffer to @FileObject.
- *
- * Returns: %TRUE if the region was successfuly written with @Length bytes.
- */
-BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
-               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
-{
-PVOID MappedBcb;
-PVOID MappedBuffer;
-gboolean errbool;
-
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset!=NULL,FALSE);
-       g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
-       g_return_val_if_fail(Buffer!=NULL,FALSE);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
-                       FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
-
-       if (Length) {
-               errbool=CcPinRead(
-                               FileObject,     /* FileObject */
-                               FileOffset,     /* FileOffset */
-                               Length, /* Length */
-                               PIN_NO_READ             /* Flags */
-                                               | (Wait ? PIN_WAIT : 0),
-                               &MappedBcb,     /* Bcb */
-                               &MappedBuffer); /* Buffer */
-               g_return_val_if_fail(errbool==TRUE,FALSE);
-
-               memcpy(MappedBuffer,Buffer,Length);
-
-               CcSetDirtyPinnedData(
-                               MappedBcb,      /* Bcb */
-                               NULL);  /* Lsn */
-               CcUnpinData(MappedBcb); /* no error code */
-               }
-
-       return TRUE;
-}
-
-
-/**
- * CcCanIWrite:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @BytesToWrite: Amount of data to be asked whether it will be accepted.
- * Value %0 is permitted.
- * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
- * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
- *
- * Asks cache manager if it would currently accept write request to @FileObject
- * of @BytesToWrite bytes with @Wait condition.
- * libcaptive will always accept any writes. This function is a NOP.
- *
- * Returns: libcaptive always returns %TRUE.
- */
-BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
-{
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
-       g_return_val_if_fail(Retrying==TRUE || Retrying==FALSE,FALSE);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
-                       FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
-
-       return TRUE;
-}
-
-
-/**
- * CcSetReadAheadGranularity:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @Granularity: Suggested size of the cache element.
- * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
- *
- * libcaptive does not implement any caching and therefore this function
- * is a NOP there.
- */
-VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
-{
-       g_return_if_fail(FileObject!=NULL);
-       g_return_if_fail(Granularity>=PAGE_SIZE);
-       g_return_if_fail((Granularity&(Granularity-1))==0);     /* Power of two */
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
-                       FileObject,(gulong)Granularity);
-
-       /* NOP; no caching by libcaptive */
-}
-
-
-/**
- * CcFlushCache:
- * @SectionObjectPointer: Pointer specifying file to flush;
- * %NULL value is forbidden.
- * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
- * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
- * @FileOffset: Optional starting point of the range to flush.
- * %NULL value is permitted.
- * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
- * @IoStatus: Optionally returns the resulting operation status.
- * #Information field will contain the number of bytes flushed.
- * %NULL value is permitted.
- *
- * Flushes out any pending dirty data in cache manager BCB mapping.
- * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
- *
- * VERIFIED: Ranged flushes.
- * VERIFIED: Synchronous write.
- */
-VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
-               IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
-{
-PUBLIC_BCB *PublicBcb;
-struct private_bcb *privbcb;
-
-       g_return_if_fail(SectionObjectPointer!=NULL);
-
-       if (SectionObjectPointer->SharedCacheMap==NULL) {
-success:
-               if (IoStatus) {
-                       IoStatus->Status=STATUS_SUCCESS;
-                       IoStatus->Information=0;
-                       }
-               return;
-               }
-
-       PublicBcb=SectionObjectPointer->SharedCacheMap;
-       g_return_if_fail(validate_Bcb(PublicBcb));
-
-       private_bcb_hash_init();
-
-       privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
-       g_return_if_fail(privbcb!=NULL);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
-                       "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
-                       SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
-                       (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
-
-       if (FileOffset) {
-               if (FileOffset->QuadPart       >=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
-                       goto success;
-               if (FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart)
-                       goto success;
-               }
-
-       /* We may find some 'privbcb' being already sheduled to be destroyed.
-        * We need to reference it to not to loose it in the middle of our flush operation.
-        */
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
-
-       /* FIXME: Flush just FileOffset..FileOfset+Length part */
-       captive_privbcb_flush_ordered(privbcb);
-
-       privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
-
-       if (IoStatus) {
-               IoStatus->Status=STATUS_SUCCESS;
-               IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
-               }
-}
-
-
-BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
-{
-PVOID Bcb;
-PVOID Buffer;
-BOOLEAN errboolean;
-
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       g_return_val_if_fail(StartOffset!=NULL,FALSE);
-       g_return_val_if_fail(EndOffset!=NULL,FALSE);
-       g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
-       g_return_val_if_fail((EndOffset->QuadPart-StartOffset->QuadPart)
-                   ==(ULONG)(EndOffset->QuadPart-StartOffset->QuadPart),FALSE);
-       g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
-
-       errboolean=CcPreparePinWrite(
-                       FileObject,     /* FileObject */
-                       StartOffset,    /* FileOffset */
-                       EndOffset->QuadPart-StartOffset->QuadPart,      /* Length */
-                       TRUE,   /* Zero */
-                       PIN_WAIT|PIN_NO_READ,   /* Flags */
-                       &Bcb,   /* Bcb */
-                       &Buffer);       /* Buffer */
-       g_assert(errboolean==TRUE);
-
-       CcUnpinData(Bcb);
-
-       return TRUE;
-}
-
-
-/* map (PVOID LogHandle) -> (GList (of FILE_OBJECT *) *FileObject_list) */
-static GHashTable *log_handle_hash;
-
-static void log_handle_hash_init(void)
-{
-       if (log_handle_hash)
-               return;
-       log_handle_hash=g_hash_table_new(
-                       g_direct_hash,  /* hash_func */
-                       g_direct_equal);        /* key_equal_func */
-}
-
-/* map (FILE_OBJECT *FileObject) -> (struct FileObject_logging *) */
-struct FileObject_logging {
-       PVOID LogHandle;
-       PFLUSH_TO_LSN FlushToLsnRoutine;
-       };
-
-static GHashTable *FileObject_logging_hash;
-
-static void FileObject_logging_hash_init(void)
-{
-       if (FileObject_logging_hash)
-               return;
-       FileObject_logging_hash=g_hash_table_new(
-                       g_direct_hash,  /* hash_func */
-                       g_direct_equal);        /* key_equal_func */
-}
-
-static void logging_notify_privbcb_flush(struct private_bcb *privbcb)
-{
-struct FileObject_logging *FileObject_logging;
-
-       g_return_if_fail(privbcb!=NULL);
-
-       if (!privbcb->lsn_valid)        /* nothing to report anyway */
-               return;
-
-       FileObject_logging_hash_init();
-       
-       if (!(FileObject_logging=g_hash_table_lookup(FileObject_logging_hash,privbcb->FileObject)))
-               return;
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: call",G_STRLOC,
-                       privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
-
-       (*FileObject_logging->FlushToLsnRoutine)(FileObject_logging->LogHandle,privbcb->lsn);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: finish",G_STRLOC,
-                       privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
-}
-
-/**
- * CcSetLogHandleForFile:
- *
- * VERIFIED: LogHandle may be NULL, FlushToLsnRoutine is never called with LogHandle==NULL.
- * VERIFIED: Only one !=NULL LogHandle and one FlushToLsnRoutine used in one session.
- */
-VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
-{
-GList *LogHandle_list;
-struct FileObject_logging *FileObject_logging;
-
-       g_return_if_fail(FileObject!=NULL);
-       /* 'LogHandle' may be NULL */
-       g_return_if_fail(FlushToLsnRoutine!=NULL);
-
-       log_handle_hash_init();
-       FileObject_logging_hash_init();
-
-       if (!(FileObject_logging=g_hash_table_lookup(
-                       FileObject_logging_hash,FileObject))) {
-               captive_new(FileObject_logging);
-               g_hash_table_insert(FileObject_logging_hash,
-                               FileObject,FileObject_logging);
-               }
-       else {
-               LogHandle_list=g_hash_table_lookup(log_handle_hash,FileObject_logging->LogHandle);
-               g_assert(NULL!=g_list_find(LogHandle_list,FileObject));
-               LogHandle_list=g_list_remove(LogHandle_list,FileObject);
-               g_assert(NULL==g_list_find(LogHandle_list,FileObject));
-               g_hash_table_insert(log_handle_hash,FileObject_logging->LogHandle,LogHandle_list);
-               }
-
-       FileObject_logging->LogHandle=LogHandle;
-       FileObject_logging->FlushToLsnRoutine=FlushToLsnRoutine;
-
-       LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
-       LogHandle_list=g_list_prepend(LogHandle_list,FileObject);
-       g_hash_table_insert(log_handle_hash,LogHandle,LogHandle_list);
-}
-
-
-struct CcGetDirtyPages_param {
-       PDIRTY_PAGE_ROUTINE DirtyPageRoutine;   /* arg of CcGetDirtyPages() */
-       IN PVOID Context1;      /* arg of CcGetDirtyPages() */
-       IN PVOID Context2;      /* arg of CcGetDirtyPages() */
-       FILE_OBJECT *FileObject;        /* search through 'page_position_hash' for 'FileObject' */
-       LARGE_INTEGER OldestLsn; gboolean OldestLsn_found;      /* intermediate return value of CcGetDirtyPages() */
-       GTree *FileObject_pages_Tree;
-       struct CcGetDirtyPages_param_FileObject_pages_Tree_page *FileObject_pages_Tree_page_last;
-       };
-
-struct CcGetDirtyPages_param_FileObject_pages_Tree_page {
-       gint64 file_offset;
-       gint64 file_offset_end;
-       LARGE_INTEGER OldestLsn,NewestLsn;
-       };
-
-static gint CcGetDirtyPages_FileObject_pages_Tree_key_compare_func
-               (const struct CcGetDirtyPages_param_FileObject_pages_Tree_page *a,
-               const struct CcGetDirtyPages_param_FileObject_pages_Tree_page *b,
-               gpointer user_data /* unused */)
-{
-       g_return_val_if_fail(a!=NULL,0);
-       g_return_val_if_fail(b!=NULL,0);
-
-       return (a->file_offset>b->file_offset)-(a->file_offset<b->file_offset);
-}
-
-static void CcGetDirtyPages_FileObject_pages_Tree_key_destroy_func
-               (struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page)
-{
-       g_return_if_fail(tree_page!=NULL);
-
-       g_free(tree_page);
-}
-
-static void CcGetDirtyPages_page_position_hash_foreach(
-               struct page_position *pagepos,  /* key */
-               struct page_position *pagepos2, /* value */
-               struct CcGetDirtyPages_param *CcGetDirtyPages_param)    /* user_data */
-{
-LARGE_INTEGER OldestLsn,NewestLsn;
-gboolean lsn_found,dirty_found;
-GList *privbcb_list;
-struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page;
-
-       g_return_if_fail(pagepos!=NULL);
-       g_return_if_fail(pagepos==pagepos2);
-       g_return_if_fail(CcGetDirtyPages_param!=NULL);
-       g_return_if_fail(CcGetDirtyPages_param->FileObject_pages_Tree!=NULL);
-
-       if (pagepos->FileObject!=CcGetDirtyPages_param->FileObject)
-               return;
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: scan pagepos offset=0x%llX",G_STRLOC,
-                       (unsigned long long)pagepos->FileOffset.QuadPart);
-
-       dirty_found=FALSE;
-       lsn_found=FALSE;
-       for (
-                       privbcb_list=pagepos->privbcb_list;
-                       privbcb_list;
-                       privbcb_list=privbcb_list->next) {
-struct private_bcb *privbcb=privbcb_list->data;
-
-               if (!privbcb->dirty)
-                       continue;
-               dirty_found=TRUE;
-
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: scan page mapping privbcb=%p,MappedFileOffset=0x%llX,MappedLength=%lX"
-                                               ",lsn=%" G_GINT64_FORMAT,G_STRLOC,
-                               privbcb,(unsigned long long)privbcb->MappedFileOffset.QuadPart,(unsigned long)privbcb->MappedLength,
-                               (gint64)(!privbcb->lsn_valid ? -1 : privbcb->lsn.QuadPart));
-
-               if (privbcb->lsn_valid) {
-                       if (!lsn_found) {
-                               OldestLsn=NewestLsn=privbcb->lsn;
-                               lsn_found=TRUE;
-                               }
-                       else {
-                               OldestLsn.QuadPart=MIN(OldestLsn.QuadPart,privbcb->lsn.QuadPart);
-                               NewestLsn.QuadPart=MAX(NewestLsn.QuadPart,privbcb->lsn.QuadPart);
-                               }
-                       }
-               }
-       if (!dirty_found) {
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: no dirty pages found",G_STRLOC);
-               return;
-               }
-
-       if (lsn_found) {
-               if (!CcGetDirtyPages_param->OldestLsn_found) {
-                       CcGetDirtyPages_param->OldestLsn=OldestLsn;
-                       CcGetDirtyPages_param->OldestLsn_found=TRUE;
-                       }
-               else
-                       CcGetDirtyPages_param->OldestLsn.QuadPart=MIN(CcGetDirtyPages_param->OldestLsn.QuadPart,OldestLsn.QuadPart);
-               }
-       else {
-               OldestLsn.QuadPart=0;
-               NewestLsn.QuadPart=0;
-               }
-
-       captive_new(tree_page);
-       tree_page->file_offset=pagepos->FileOffset.QuadPart;
-       tree_page->file_offset_end=tree_page->file_offset+PAGE_SIZE;
-       tree_page->OldestLsn=OldestLsn;
-       tree_page->NewestLsn=NewestLsn;
-       g_tree_insert(CcGetDirtyPages_param->FileObject_pages_Tree,
-                       tree_page,      /* key */
-                       tree_page);     /* value */
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: new tree_page file_offset=0x%llX"
-                                       ",OldestLsn=%" G_GINT64_FORMAT ",NewestLsn=%" G_GINT64_FORMAT,G_STRLOC,
-                       (unsigned long long)tree_page->file_offset,
-                                       (gint64)tree_page->OldestLsn.QuadPart,(gint64)tree_page->NewestLsn.QuadPart);
-}
-
-static gboolean CcGetDirtyPages_FileObject_pages_Tree_traverse_func
-               (struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page /* key */,
-               struct CcGetDirtyPages_param_FileObject_pages_Tree_page *value,
-               struct CcGetDirtyPages_param *CcGetDirtyPages_param)    /* user_data */
-{
-struct CcGetDirtyPages_param_FileObject_pages_Tree_page *page_last;
-
-       /* 'tree_page' may be NULL for the last invocation after complete traversal. */
-       g_return_val_if_fail(tree_page==value,TRUE);    /* 'TRUE' meaning: stop the traversal. */
-       g_return_val_if_fail(CcGetDirtyPages_param!=NULL,TRUE); /* 'TRUE' meaning: stop the traversal. */
-
-       page_last=CcGetDirtyPages_param->FileObject_pages_Tree_page_last;
-
-       /* Concatenate two pages? */
-       if (page_last && tree_page
-                       && page_last->file_offset_end   ==tree_page->file_offset
-                       && page_last->OldestLsn.QuadPart==tree_page->OldestLsn.QuadPart
-                       && page_last->NewestLsn.QuadPart==tree_page->NewestLsn.QuadPart) {
-               page_last->file_offset_end=tree_page->file_offset_end;
-               /* Forget about the current 'tree_page' as it has been appended. */
-               return FALSE;   /* 'FALSE' meaning: continue the traversal. */
-               }
-
-       /* Flush 'page_last'. */
-       if (page_last) {
-LARGE_INTEGER FileOffset_local;
-LARGE_INTEGER OldestLsn_check,NewestLsn_check;
-
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: DirtyPageRoutine(): FileOffset=0x%llX,Length=0x%llX"
-                                               ",OldestLsn=%" G_GINT64_FORMAT ",NewestLsn=%" G_GINT64_FORMAT,G_STRLOC,
-                               (unsigned long long)page_last->file_offset,(unsigned long long)(page_last->file_offset_end-page_last->file_offset),
-                                               (gint64)page_last->OldestLsn.QuadPart,(gint64)page_last->NewestLsn.QuadPart);
-
-               OldestLsn_check=page_last->OldestLsn;
-               NewestLsn_check=page_last->NewestLsn;
-
-               FileOffset_local.QuadPart=page_last->file_offset;
-
-               (*CcGetDirtyPages_param->DirtyPageRoutine)(
-                               CcGetDirtyPages_param->FileObject,      /* FileObject */
-                               &FileOffset_local,      /* FileOffset */
-                               page_last->file_offset_end-page_last->file_offset,      /* Length */
-                               &page_last->OldestLsn,  /* OldestLsn */
-                               &page_last->NewestLsn,  /* NewestLsn */
-                               CcGetDirtyPages_param->Context1,        /* Context1 */
-                               CcGetDirtyPages_param->Context2);       /* Context2 */
-
-               /* just unconfirmed sanity: */
-               g_assert(FileOffset_local.QuadPart==page_last->file_offset);    /* check for possible corruption */
-               g_assert(OldestLsn_check.QuadPart==page_last->OldestLsn.QuadPart);
-               g_assert(NewestLsn_check.QuadPart==page_last->NewestLsn.QuadPart);
-
-               CcGetDirtyPages_param->FileObject_pages_Tree_page_last=NULL;
-               }
-
-       /* May be NULL during the last flush: */
-       CcGetDirtyPages_param->FileObject_pages_Tree_page_last=tree_page;
-
-       return FALSE;   /* 'FALSE' meaning: continue the traversal. */
-}
-
-/**
- * CcGetDirtyPages:
- * @LogHandle: Arbitrary pointer to match with value passed to CcSetLogHandleForFile().
- * %NULL value is permitted (considered as regular matching value by libcaptive).
- * @DirtyPageRoutine: #PDIRTY_PAGE_ROUTINE type to call on each dirty page(s).
- * %NULL value is forbidden.
- * @Context1: User data to pass to @DirtyPageRoutine.
- * %NULL value is permitted.
- * @Context2: User data to pass to @DirtyPageRoutine.
- *
- * Searches through the list of dirty #PUBLIC_BCB s of files assigned to @LogHandle
- * by CcSetLogHandleForFile(). Any clean #PUBLIC_BCB s are ignored
- * by this function. Function will scan through all #PUBLIC_BCB mapping of each such page
- * and it will detect its oldest and newest LSN (Logical Sequence Number). Unset
- * LSN is considered as value 0. Value 0 is returned only if no other LSN is valid
- * for such case, otherwise such void value 0 is ignored (and oldest and newest LSN
- * returned are being equal in the case only single LSN was found).
- *
- * Found pages are coalesced as much as possible during calls of @DirtyPageRoutine.
- * Coalescable ranges must have the same detected both oldest and newest LSNs.
- *
- * Returns: Oldest LSN across all the #FileObject s found for the given @LogHandle.
- * Function returns value %0 if no appropriate LSN was found.
- * libcaptive must return #gint64 instead of the official #LARGE_INTEGER
- * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
- *
- * VERIFIED: func called in runs for each FileObject, FileOffset sorted desc.
- */
-gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
-               IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
-{
-GList *LogHandle_list;
-struct CcGetDirtyPages_param CcGetDirtyPages_param;
-
-       /* 'LogHandle' may be NULL */
-       g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LogHandle=%p,DirtyPageRoutine=%p,Context1=%p,Context2=%p",G_STRLOC,
-                       LogHandle,DirtyPageRoutine,Context1,Context2);
-
-       log_handle_hash_init();
-       page_position_hash_init();
-
-       CcGetDirtyPages_param.DirtyPageRoutine=DirtyPageRoutine;
-       CcGetDirtyPages_param.Context1=Context1;
-       CcGetDirtyPages_param.Context2=Context2;
-       CcGetDirtyPages_param.OldestLsn_found=FALSE;
-
-       for (
-                       LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
-                       LogHandle_list;
-                       LogHandle_list=LogHandle_list->next) {
-               CcGetDirtyPages_param.FileObject=LogHandle_list->data;
-               g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: processing FileObject %p",G_STRLOC,CcGetDirtyPages_param.FileObject);
-
-               CcGetDirtyPages_param.FileObject_pages_Tree=g_tree_new_full(
-                               (GCompareDataFunc)CcGetDirtyPages_FileObject_pages_Tree_key_compare_func,       /* key_compare_func */
-                               NULL,   /* key_compare_data */
-                               (GDestroyNotify)CcGetDirtyPages_FileObject_pages_Tree_key_destroy_func, /* key_destroy_func */
-                               NULL);  /* value_destroy_func */
-               g_hash_table_foreach(
-                               page_position_hash,     /* hash_table */
-                               (GHFunc)CcGetDirtyPages_page_position_hash_foreach,     /* func */
-                               &CcGetDirtyPages_param);        /* user_data */
-               CcGetDirtyPages_param.FileObject_pages_Tree_page_last=NULL;
-               g_tree_foreach(CcGetDirtyPages_param.FileObject_pages_Tree,     /* tree */
-                               (GTraverseFunc)CcGetDirtyPages_FileObject_pages_Tree_traverse_func,     /* traverse_func */
-                               &CcGetDirtyPages_param);        /* user_data */
-               CcGetDirtyPages_FileObject_pages_Tree_traverse_func(
-                               NULL,   /* tree_page */
-                               NULL,   /* value */
-                               &CcGetDirtyPages_param);        /* user_data */
-               g_tree_destroy(CcGetDirtyPages_param.FileObject_pages_Tree);
-               }
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %" G_GINT64_FORMAT,G_STRLOC,
-                       (gint64)(!CcGetDirtyPages_param.OldestLsn_found ? 0 : CcGetDirtyPages_param.OldestLsn.QuadPart));
-
-       if (!CcGetDirtyPages_param.OldestLsn_found)
-               return 0;
-       return CcGetDirtyPages_param.OldestLsn.QuadPart;
-}
-
-
-/**
- * CcSetAdditionalCacheAttributes:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
- * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
- *
- * libcaptive does not implement any caching and therefore this function
- * is a NOP there.
- */
-VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
-{
-       g_return_if_fail(FileObject!=NULL);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
-                       FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
-
-       /* NOP; no caching by libcaptive */
-}
-
-
-/**
- * CcSetBcbOwnerPointer:
- * @Bcb: Initialized #PUBLIC_BCB structure.
- * %NULL value is forbidden.
- * @Owner: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
- * %NULL value is forbidden (FIXME: Is it W32 compliant?).
- *
- * Set thread-specific pointer for a pinned @Bcb. Use CcUnpinDataForThread()
- * when @Bcb is no longer needed. CcUnpinDataForThread() is NOT a reverse
- * operation for this single call CcSetBcbOwnerPointer(), see CcUnpinDataForThread().
- *
- * libcaptive implements this function as no-operation as it does not yet
- * support any threading.
- */
-VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
-{
-       g_return_if_fail(validate_Bcb(Bcb));
-       g_return_if_fail(Owner!=NULL);
-
-       /* FIXME:thread; NOP if no threads present */
-}
-
-
-/**
- * CcUnpinDataForThread:
- * @Bcb: Initialized #PUBLIC_BCB structure.
- * %NULL value is forbidden.
- * @ResourceThreadId: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
- * This pointer had to be passed to CcSetBcbOwnerPointer() #Owner parameter previously.
- * %NULL value is forbidden (FIXME: is it W32 compliant?).
- *
- * CcUnpinData() for a thread specified by @ResourceThreadId.
- * Reverse operation for a pair of CcMapData() and CcSetBcbOwnerPointer().
- *
- * libcaptive implements this function as a simple pass to CcUnpinData() as it does not yet
- * support any threading.
- */
-VOID CcUnpinDataForThread(IN PVOID Bcb,IN ERESOURCE_THREAD ResourceThreadId)
-{
-       g_return_if_fail(validate_Bcb(Bcb));
-       g_return_if_fail(ResourceThreadId!=0);
-
-       /* FIXME:thread */
-
-       CcUnpinData(Bcb);
-}
-
-
-/**
- * CcRemapBcb:
- * @Bcb: Initialized #PUBLIC_BCB structure.
- * %NULL value is forbidden.
- *
- * Create a copy of @Bcb for the exactly same file contents as is @Bcb.
- * The returned copy has the same attributes as the result of CcMapData()
- * notwithstanding the current state of input @Bcb, therefore it is only
- * for read/only access etc.
- *
- * libcaptive calls CcMapData() internally with @Bcb parameters.
- *
- * This function is called only by ntfs.sys of NT-5.1sp1 and it will perform
- * these operations with the resulting #PUBLIC_BCB:
- * CcRemapBcb(), CcSetDirtyPinnedData(), CcUnpinData()
- *
- * Untested: This call does not set the buffer as dirty - such buffer will not be flushed automatically.
- *
- * Returns: Copy of @Bcb. This _pointer_ never equals to @Bcb.
- * It should be some different
- * #PUBLIC_BCB structure according to W32 doc.
- */
-PVOID CcRemapBcb(IN PVOID Bcb)
-{
-PVOID r;
-PVOID Buffer_unused;
-BOOLEAN errbool;
-PUBLIC_BCB *PublicBcb;
-struct private_bcb *privbcb;
-
-       g_return_val_if_fail(validate_Bcb(Bcb),NULL);
-
-       private_bcb_hash_init();
-
-       PublicBcb=(PUBLIC_BCB *)Bcb;
-       privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
-       g_return_val_if_fail(privbcb!=NULL,NULL);
-
-       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
-                       "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d",G_STRLOC,
-                       privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count);
-
-       errbool=CcMapData(
-                       privbcb->FileObject,    /* FileObject */
-                       &privbcb->MappedFileOffset,     /* FileOffset */
-                       privbcb->MappedLength,  /* Length */
-                       MAP_WAIT,       /* Flags; FIXME: Is it OK to?: && !MAP_NO_READ */
-                       &r,     /* Bcb */
-                       &Buffer_unused); /* Buffer */
-       g_return_val_if_fail(errbool==TRUE,NULL);
-       g_return_val_if_fail(r!=NULL,NULL);
-       g_return_val_if_fail(Buffer_unused!=NULL,NULL);
-
-       g_assert(r!=Bcb);
-       return r;
-}
-
-
-/**
- * CcPreparePinWrite:
- * @FileObject: Initialized open #FileObject to map.
- * %NULL value is forbidden.
- * @FileOffset: The @FileObject file offset from where to map the region from.
- * Negative value is forbidden.
- * @Length: Requested length of the region to map from @FileObject.
- * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
- * @Zero: %TRUE if the area of @FileOffset...@FileOffset+@Length should be cleared.
- * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
- * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
- * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
- * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
- * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
- * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
- * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- * @Buffer: Returns the mapped memory region start address.
- * This address may not be %PAGE_SIZE aligned.
- * %NULL pointer is forbidden.
- *
- * Wrapper for a pair of CcPinRead() and CcSetDirtyPinnedData().
- * The mapped range can be also optionally cleared if @Zero is specified.
- * See CcPinRead() for a more detailed documentation.
- *
- * This call will set the buffer as dirty - such buffer will be flushed automatically.
- *
- * Returns: %TRUE if the mapping was successful.
- */
-BOOLEAN CcPreparePinWrite(IN PFILE_OBJECT FileObject,
-               IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Zero,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
-{
-BOOLEAN errbool;
-
-       g_return_val_if_fail(FileObject!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset!=NULL,FALSE);
-       g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
-       g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
-       /* 'Flags' passed to CcPinRead() */
-       g_return_val_if_fail(Bcb!=NULL,FALSE);
-       g_return_val_if_fail(Buffer!=NULL,FALSE);
-
-       errbool=CcPinRead(FileObject,FileOffset,Length,Flags|(Zero ? PIN_NO_READ : 0),Bcb,Buffer);
-       g_return_val_if_fail(errbool==TRUE,FALSE);
-
-       CcSetDirtyPinnedData(
-                       *Bcb,   /* Bcb */
-                       NULL);  /* Lsn; OPTIONAL */
-
-       if (Zero) {
-               memset(*Buffer,0,Length);
-               }
-
-       return TRUE;
-}
-
-
-VOID FsRtlIncrementCcFastReadNoWait(VOID)
-{
-       /* FIXME: {{%fs:[0]}+0x4E0}:LONG++ */
-}
-
-
-NTSTATUS CcWaitForCurrentLazyWriterActivity(VOID)
-{
-       return STATUS_SUCCESS;
-}
-
-static void CcIsThereDirtyData_private_bcb_hash_foreach(
-               PUBLIC_BCB *PublicBcb,  /* key */
-               struct private_bcb *privbcb,  /* value */
-               gboolean *dirty_foundp) /* user_data */
-{
-       g_return_if_fail(validate_Bcb(PublicBcb));
-       g_return_if_fail(privbcb!=NULL);
-       g_return_if_fail(PublicBcb==privbcb->PublicBcb);
-
-       if (!privbcb->dirty)
-               return;
-
-       *dirty_foundp=TRUE;     /* FIXME: stop the traversal. */
-}
-
-BOOLEAN CcIsThereDirtyData(IN PVPB Vpb)
-{
-gboolean dirty_found;
-
-       g_return_val_if_fail(Vpb!=NULL,FALSE);  /* We have just one volume mounted anyway. */
-
-       private_bcb_hash_init();
-
-       dirty_found=FALSE;
-       g_hash_table_foreach(
-                       private_bcb_hash,       /* hash_table */
-                       (GHFunc)CcIsThereDirtyData_private_bcb_hash_foreach,    /* func */
-                       &dirty_found);  /* user_data */
-
-       return dirty_found;
-}
diff --git a/src/libcaptive/cc/marshallers.list b/src/libcaptive/cc/marshallers.list
new file mode 100644 (file)
index 0000000..a9aadd0
--- /dev/null
@@ -0,0 +1,20 @@
+# $Id$
+# Mashallers for GObject based Cache Manager mapper emulation of libcaptive
+# Copyright (C) 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
+# 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
+
+
+VOID:UINT64,UINT64,UINT64
+VOID:VOID
diff --git a/src/libcaptive/cc/misc.c b/src/libcaptive/cc/misc.c
new file mode 100644 (file)
index 0000000..ad90a51
--- /dev/null
@@ -0,0 +1,115 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) misc of libcaptive
+ * Copyright (C) 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
+ * 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 <glib/gmessages.h>
+#include "reactos/ddk/status.h"
+#include "reactos/ddk/iotypes.h"
+#include "sharedcachemap.h"
+
+
+/**
+ * CcCanIWrite:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @BytesToWrite: Amount of data to be asked whether it will be accepted.
+ * Value %0 is permitted.
+ * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
+ * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
+ *
+ * Asks cache manager if it would currently accept write request to @FileObject
+ * of @BytesToWrite bytes with @Wait condition.
+ * libcaptive will always accept any writes. This function is a NOP.
+ *
+ * Returns: libcaptive always returns %TRUE.
+ */
+BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
+{
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+       g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
+       g_return_val_if_fail(Retrying==TRUE || Retrying==FALSE,FALSE);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
+                       FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
+
+       /* sanity check */
+       captive_FileObject_to_SharedCacheMap(FileObject);
+
+       return TRUE;
+}
+
+
+/**
+ * CcSetReadAheadGranularity:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @Granularity: Suggested size of the cache element.
+ * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
+ *
+ * libcaptive does not implement any caching and therefore this function
+ * is a NOP there.
+ */
+VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
+{
+       g_return_if_fail(FileObject!=NULL);
+       g_return_if_fail(Granularity>=PAGE_SIZE);
+       g_return_if_fail((Granularity&(Granularity-1))==0);     /* Power of two */
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
+                       FileObject,(gulong)Granularity);
+
+       /* NOP; no caching by libcaptive */
+
+       /* sanity check */
+       captive_FileObject_to_SharedCacheMap(FileObject);
+}
+
+
+/**
+ * CcSetAdditionalCacheAttributes:
+ * @FileObject: Initialized open #FileObject to map.
+ * %NULL value is forbidden.
+ * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
+ * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
+ *
+ * libcaptive does not implement any caching and therefore this function
+ * is a NOP there.
+ */
+VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
+{
+       g_return_if_fail(FileObject!=NULL);
+
+       g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
+                       FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
+
+       /* NOP; no caching by libcaptive */
+}
+
+
+VOID FsRtlIncrementCcFastReadNoWait(VOID)
+{
+       /* FIXME: {{%fs:[0]}+0x4E0}:LONG++ */
+}
+
+
+NTSTATUS CcWaitForCurrentLazyWriterActivity(VOID)
+{
+       return STATUS_SUCCESS;
+}
diff --git a/src/libcaptive/cc/privatebcb-priv.h b/src/libcaptive/cc/privatebcb-priv.h
new file mode 100644 (file)
index 0000000..e01d0c2
--- /dev/null
@@ -0,0 +1,45 @@
+/* $Id$
+ * Object-private include file for reactos Cache Manager (Cc*) PrivateBcb structure of libcaptive
+ * Copyright (C) 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
+ * 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
+ */
+
+
+#ifndef _CAPTIVE_CC_PRIVATE_BCB_PRIV_H
+#define _CAPTIVE_CC_PRIVATE_BCB_PRIV_H 1
+
+
+#include <glib/gmacros.h>
+#include <glib-object.h>
+#include "sharedcachemap.h"
+#include "reactos/ddk/cctypes.h"
+
+
+G_BEGIN_DECLS
+
+struct _CaptivePrivateBcbObject {
+       GObject parent_instance;
+
+       CaptiveSharedCacheMapObject *SharedCacheMap;
+       PUBLIC_BCB *PublicBcb;  /* content is not used by W32 code anyway */
+       };
+struct _CaptivePrivateBcbObjectClass {
+       GObjectClass parent_class;
+       };
+
+G_END_DECLS
+
+
+#endif /* _CAPTIVE_CC_PRIVATE_BCB_PRIV_H */
diff --git a/src/libcaptive/cc/privatebcb.c b/src/libcaptive/cc/privatebcb.c
new file mode 100644 (file)
index 0000000..50bd27e
--- /dev/null
@@ -0,0 +1,150 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) PrivateBcb structure of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcb.h"        /* self */
+#include "privatebcb-priv.h"   /* self */
+#include <glib-object.h>
+#include "captive/macros.h"
+#include <glib/ghash.h>
+
+
+/* CONFIG: */
+
+#define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45 /* FIXME: W32 undocumented */
+
+
+static GHashTable *CaptivePrivateBcbObject_hash;
+
+static void CaptivePrivateBcbObject_hash_init(void)
+{
+       if (CaptivePrivateBcbObject_hash)
+               return;
+       CaptivePrivateBcbObject_hash=g_hash_table_new(
+                       g_direct_hash,  /* hash_func */
+                       g_direct_equal);        /* key_equal_func */
+}
+
+static gpointer captive_private_bcb_object_parent_class=NULL;
+
+
+static void captive_private_bcb_object_finalize(CaptivePrivateBcbObject *captive_private_bcb_object)
+{
+       g_return_if_fail(captive_private_bcb_object!=NULL);
+
+       if (captive_private_bcb_object->SharedCacheMap) {
+               g_object_unref(captive_private_bcb_object->SharedCacheMap);
+               captive_private_bcb_object->SharedCacheMap=NULL;
+               }
+       if (captive_private_bcb_object->PublicBcb) {
+               g_free(captive_private_bcb_object->PublicBcb);
+               captive_private_bcb_object->PublicBcb=NULL;
+               }
+
+       G_OBJECT_CLASS(captive_private_bcb_object_parent_class)->finalize((GObject *)captive_private_bcb_object);
+}
+
+
+static void captive_private_bcb_object_class_init(CaptivePrivateBcbObjectClass *class)
+{
+GObjectClass *gobject_class=G_OBJECT_CLASS(class);
+
+       captive_private_bcb_object_parent_class=g_type_class_ref(G_TYPE_OBJECT);
+       gobject_class->finalize=(void (*)(GObject *object))captive_private_bcb_object_finalize;
+}
+
+
+static void captive_private_bcb_object_init(CaptivePrivateBcbObject *captive_private_bcb_object)
+{
+PUBLIC_BCB *PublicBcb;
+
+       captive_new(PublicBcb);
+       PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
+       PublicBcb->NodeByteSize=sizeof(*PublicBcb);     /* we have no extensions there */
+       PublicBcb->MappedLength=0;
+       PublicBcb->MappedFileOffset.QuadPart=0;
+       captive_private_bcb_object->PublicBcb=PublicBcb;
+       CaptivePrivateBcbObject_hash_init();
+       g_hash_table_insert(CaptivePrivateBcbObject_hash,PublicBcb,captive_private_bcb_object);
+}
+
+
+GType captive_private_bcb_object_get_type(void)
+{
+static GType captive_private_bcb_object_type=0;
+
+       if (!captive_private_bcb_object_type) {
+static const GTypeInfo captive_private_bcb_object_info={
+                               sizeof(CaptivePrivateBcbObjectClass),
+                               NULL,   /* base_init */
+                               NULL,   /* base_finalize */
+                               (GClassInitFunc)captive_private_bcb_object_class_init,
+                               NULL,   /* class_finalize */
+                               NULL,   /* class_data */
+                               sizeof(CaptivePrivateBcbObject),
+                               5,      /* n_preallocs */
+                               (GInstanceInitFunc)captive_private_bcb_object_init,
+                               };
+
+               captive_private_bcb_object_type=g_type_register_static(G_TYPE_OBJECT,
+                               "CaptivePrivateBcbObject",&captive_private_bcb_object_info,G_TYPE_FLAG_ABSTRACT);
+               }
+
+       return captive_private_bcb_object_type;
+}
+
+void _captive_private_bcb_object_connect_SharedCacheMap
+               (CaptivePrivateBcbObject *captive_private_bcb_object,CaptiveSharedCacheMapObject *SharedCacheMap)
+{
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_IS_OBJECT(captive_private_bcb_object));
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(SharedCacheMap));
+       g_return_if_fail(captive_private_bcb_object->SharedCacheMap==NULL);
+
+       g_object_ref(SharedCacheMap);
+       captive_private_bcb_object->SharedCacheMap=SharedCacheMap;
+}
+
+PUBLIC_BCB *captive_private_bcb_object_get_PublicBcb(CaptivePrivateBcbObject *captive_private_bcb_object)
+{
+       g_return_val_if_fail(CAPTIVE_PRIVATE_BCB_IS_OBJECT(captive_private_bcb_object),NULL);
+
+       return captive_private_bcb_object->PublicBcb;
+}
+
+CaptiveSharedCacheMapObject *captive_private_bcb_object_get_SharedCacheMap(CaptivePrivateBcbObject *captive_private_bcb_object)
+{
+       g_return_val_if_fail(CAPTIVE_PRIVATE_BCB_IS_OBJECT(captive_private_bcb_object),NULL);
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_private_bcb_object->SharedCacheMap),NULL);
+
+       return captive_private_bcb_object->SharedCacheMap;
+}
+
+CaptivePrivateBcbObject *captive_PublicBcb_to_PrivateBcbObject(const PUBLIC_BCB *PublicBcb)
+{
+CaptivePrivateBcbObject *captive_private_bcb_object;
+
+       g_return_val_if_fail(PublicBcb!=NULL,NULL);
+
+       CaptivePrivateBcbObject_hash_init();
+       captive_private_bcb_object=g_hash_table_lookup(CaptivePrivateBcbObject_hash,PublicBcb);
+       g_return_val_if_fail(CAPTIVE_PRIVATE_BCB_IS_OBJECT(captive_private_bcb_object),NULL);
+
+       return captive_private_bcb_object;
+}
diff --git a/src/libcaptive/cc/privatebcb.h b/src/libcaptive/cc/privatebcb.h
new file mode 100644 (file)
index 0000000..f7e2a35
--- /dev/null
@@ -0,0 +1,52 @@
+/* $Id$
+ * Include file for reactos Cache Manager (Cc*) PrivateBcb structure of libcaptive
+ * Copyright (C) 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
+ * 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
+ */
+
+
+#ifndef _CAPTIVE_CC_PRIVATE_BCB_H
+#define _CAPTIVE_CC_PRIVATE_BCB_H 1
+
+
+#include <glib/gmacros.h>
+#include <glib-object.h>
+#include "sharedcachemap.h"
+
+
+G_BEGIN_DECLS
+
+#define CAPTIVE_PRIVATE_BCB_TYPE_OBJECT            (captive_private_bcb_object_get_type())
+#define CAPTIVE_PRIVATE_BCB_OBJECT(object)         (G_TYPE_CHECK_INSTANCE_CAST((object),CAPTIVE_PRIVATE_BCB_TYPE_OBJECT,CaptivePrivateBcbObject))
+#define CAPTIVE_PRIVATE_BCB_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),CAPTIVE_PRIVATE_BCB_TYPE_OBJECT,CaptivePrivateBcbObjectClass))
+#define CAPTIVE_PRIVATE_BCB_IS_OBJECT(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object),CAPTIVE_PRIVATE_BCB_TYPE_OBJECT))
+#define CAPTIVE_PRIVATE_BCB_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),CAPTIVE_PRIVATE_BCB_TYPE_OBJECT))
+#define CAPTIVE_PRIVATE_BCB_OBJECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),CAPTIVE_PRIVATE_BCB_TYPE_OBJECT,CaptivePrivateBcbObjectClass))
+typedef struct _CaptivePrivateBcbObject      CaptivePrivateBcbObject;
+typedef struct _CaptivePrivateBcbObjectClass CaptivePrivateBcbObjectClass;
+
+
+GType captive_private_bcb_object_get_type(void);
+
+void _captive_private_bcb_object_connect_SharedCacheMap
+               (CaptivePrivateBcbObject *captive_private_bcb_object,CaptiveSharedCacheMapObject *SharedCacheMap);
+PUBLIC_BCB *captive_private_bcb_object_get_PublicBcb(CaptivePrivateBcbObject *captive_private_bcb_object);
+CaptiveSharedCacheMapObject *captive_private_bcb_object_get_SharedCacheMap(CaptivePrivateBcbObject *captive_private_bcb_object);
+CaptivePrivateBcbObject *captive_PublicBcb_to_PrivateBcbObject(const PUBLIC_BCB *PublicBcb);
+
+G_END_DECLS
+
+
+#endif /* _CAPTIVE_CC_PRIVATE_BCB_H */
diff --git a/src/libcaptive/cc/privatebcbmap.c b/src/libcaptive/cc/privatebcbmap.c
new file mode 100644 (file)
index 0000000..b3c20e7
--- /dev/null
@@ -0,0 +1,126 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) PrivateBcb Map type structure of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbmap.h"     /* self */
+#include "privatebcb.h"
+#include "privatebcb-priv.h"
+#include "sharedcachemap-priv.h"
+#include <glib-object.h>
+
+
+struct _CaptivePrivateBcbMapObject {
+       CaptivePrivateBcbObject parent_instance;
+       };
+struct _CaptivePrivateBcbMapObjectClass {
+       CaptivePrivateBcbObjectClass parent_class;
+       };
+
+
+static gpointer captive_private_bcb_map_object_parent_class=NULL;
+
+
+static void captive_private_bcb_map_object_finalize(CaptivePrivateBcbMapObject *captive_private_bcb_map_object)
+{
+CaptivePrivateBcbObject *captive_private_bcb_object;
+CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
+
+       g_return_if_fail(captive_private_bcb_map_object!=NULL);
+
+       captive_private_bcb_object=CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_map_object);
+
+       if ((captive_shared_cache_map_object=captive_private_bcb_object->SharedCacheMap)) {
+               g_assert(captive_shared_cache_map_object->map==captive_private_bcb_map_object);
+               captive_shared_cache_map_object->map=NULL;
+               g_object_unref(captive_shared_cache_map_object);
+               }
+
+       G_OBJECT_CLASS(captive_private_bcb_map_object_parent_class)->finalize((GObject *)captive_private_bcb_map_object);
+}
+
+
+static void captive_private_bcb_map_object_class_init(CaptivePrivateBcbMapObjectClass *class)
+{
+GObjectClass *gobject_class=G_OBJECT_CLASS(class);
+
+       captive_private_bcb_map_object_parent_class=g_type_class_ref(G_TYPE_OBJECT);
+       gobject_class->finalize=(void (*)(GObject *object))captive_private_bcb_map_object_finalize;
+}
+
+
+static void captive_private_bcb_map_object_init(CaptivePrivateBcbMapObject *captive_private_bcb_map_object)
+{
+}
+
+
+GType captive_private_bcb_map_object_get_type(void)
+{
+static GType captive_private_bcb_map_object_type=0;
+
+       if (!captive_private_bcb_map_object_type) {
+static const GTypeInfo captive_private_bcb_map_object_info={
+                               sizeof(CaptivePrivateBcbMapObjectClass),
+                               NULL,   /* base_init */
+                               NULL,   /* base_finalize */
+                               (GClassInitFunc)captive_private_bcb_map_object_class_init,
+                               NULL,   /* class_finalize */
+                               NULL,   /* class_data */
+                               sizeof(CaptivePrivateBcbMapObject),
+                               5,      /* n_preallocs */
+                               (GInstanceInitFunc)captive_private_bcb_map_object_init,
+                               };
+
+               captive_private_bcb_map_object_type=g_type_register_static(CAPTIVE_PRIVATE_BCB_TYPE_OBJECT,
+                               "CaptivePrivateBcbMapObject",&captive_private_bcb_map_object_info,0);
+               }
+
+       return captive_private_bcb_map_object_type;
+}
+
+CaptivePrivateBcbMapObject *captive_private_bcb_map_object_new(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+CaptivePrivateBcbMapObject *captive_private_bcb_map_object;
+
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
+
+       captive_private_bcb_map_object=g_object_new(
+                       CAPTIVE_PRIVATE_BCB_MAP_TYPE_OBJECT,    /* object_type */
+                       NULL);  /* first_property_name; FIXME: support properties */
+
+       _captive_private_bcb_object_connect_SharedCacheMap(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_map_object),
+                       captive_shared_cache_map_object);
+
+       g_assert(captive_shared_cache_map_object->map==NULL);
+       captive_shared_cache_map_object->map=captive_private_bcb_map_object;
+
+       return captive_private_bcb_map_object;
+}
+
+CaptivePrivateBcbMapObject *captive_private_bcb_map_object_get_ref
+               (CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
+
+       if (captive_shared_cache_map_object->map) {
+               g_object_ref(captive_shared_cache_map_object->map);
+               return captive_shared_cache_map_object->map;
+               }
+       return captive_private_bcb_map_object_new(captive_shared_cache_map_object);
+}
diff --git a/src/libcaptive/cc/privatebcbmap.h b/src/libcaptive/cc/privatebcbmap.h
new file mode 100644 (file)
index 0000000..04ee805
--- /dev/null
@@ -0,0 +1,50 @@
+/* $Id$
+ * Include file for reactos Cache Manager (Cc*) PrivateBcbMap structure of libcaptive
+ * Copyright (C) 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
+ * 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
+ */
+
+
+#ifndef _CAPTIVE_CC_PRIVATE_BCB_MAP_H
+#define _CAPTIVE_CC_PRIVATE_BCB_MAP_H 1
+
+
+#include <glib/gmacros.h>
+#include <glib-object.h>
+#include "sharedcachemap.h"
+
+
+G_BEGIN_DECLS
+
+#define CAPTIVE_PRIVATE_BCB_MAP_TYPE_OBJECT            (captive_private_bcb_map_object_get_type())
+#define CAPTIVE_PRIVATE_BCB_MAP_OBJECT(object)         (G_TYPE_CHECK_INSTANCE_CAST((object),CAPTIVE_PRIVATE_BCB_MAP_TYPE_OBJECT,CaptivePrivateBcbMapObject))
+#define CAPTIVE_PRIVATE_BCB_MAP_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),CAPTIVE_PRIVATE_BCB_MAP_TYPE_OBJECT,CaptivePrivateBcbMapObjectClass))
+#define CAPTIVE_PRIVATE_BCB_MAP_IS_OBJECT(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object),CAPTIVE_PRIVATE_BCB_MAP_TYPE_OBJECT))
+#define CAPTIVE_PRIVATE_BCB_MAP_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),CAPTIVE_PRIVATE_BCB_MAP_TYPE_OBJECT))
+#define CAPTIVE_PRIVATE_BCB_MAP_OBJECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),CAPTIVE_PRIVATE_BCB_MAP_TYPE_OBJECT,CaptivePrivateBcbMapObjectClass))
+typedef struct _CaptivePrivateBcbMapObject      CaptivePrivateBcbMapObject;
+typedef struct _CaptivePrivateBcbMapObjectClass CaptivePrivateBcbMapObjectClass;
+
+
+GType captive_private_bcb_map_object_get_type(void);
+
+CaptivePrivateBcbMapObject *captive_private_bcb_map_object_new(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
+CaptivePrivateBcbMapObject *captive_private_bcb_map_object_get_ref
+               (CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
+
+G_END_DECLS
+
+
+#endif /* _CAPTIVE_CC_PRIVATE_BCB_MAP_H */
diff --git a/src/libcaptive/cc/privatebcbpin.c b/src/libcaptive/cc/privatebcbpin.c
new file mode 100644 (file)
index 0000000..5b08c0a
--- /dev/null
@@ -0,0 +1,338 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) PrivateBcb Map type structure of libcaptive
+ * Copyright (C) 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
+ * 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 "privatebcbpin.h"     /* self */
+#include "privatebcb.h"
+#include "privatebcb-priv.h"
+#include "sharedcachemap-priv.h"
+#include <glib-object.h>
+
+
+struct _CaptivePrivateBcbPinObject {
+       CaptivePrivateBcbObject parent_instance;
+
+       gulong FileSizes_changed_handler_id;
+       gulong FileSizes_changed_after_handler_id;
+       gulong purge_handler_id;
+       guint64 offset;
+       guint32 length;
+       gpointer buffer;
+       };
+struct _CaptivePrivateBcbPinObjectClass {
+       CaptivePrivateBcbObjectClass parent_class;
+       };
+
+
+static gpointer captive_private_bcb_pin_object_parent_class=NULL;
+
+
+static void captive_private_bcb_pin_object_finalize(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object)
+{
+CaptivePrivateBcbObject *captive_private_bcb_object;
+CaptiveSharedCacheMapObject *SharedCacheMap;
+GHashTable *pin_hash;
+
+       g_return_if_fail(captive_private_bcb_pin_object!=NULL);
+
+       captive_private_bcb_object=CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object);
+
+       if (captive_private_bcb_pin_object->FileSizes_changed_handler_id) {
+               g_assert(captive_private_bcb_object->SharedCacheMap!=NULL);
+               g_signal_handler_disconnect(
+                               captive_private_bcb_object->SharedCacheMap,     /* instance */
+                               captive_private_bcb_pin_object->FileSizes_changed_handler_id);
+               captive_private_bcb_pin_object->FileSizes_changed_handler_id=0;
+               }
+       if (captive_private_bcb_pin_object->FileSizes_changed_after_handler_id) {
+               g_assert(captive_private_bcb_object->SharedCacheMap!=NULL);
+               g_signal_handler_disconnect(
+                               captive_private_bcb_object->SharedCacheMap,     /* instance */
+                               captive_private_bcb_pin_object->FileSizes_changed_after_handler_id);
+               captive_private_bcb_pin_object->FileSizes_changed_after_handler_id=0;
+               }
+       if (captive_private_bcb_pin_object->purge_handler_id) {
+               g_assert(captive_private_bcb_object->SharedCacheMap!=NULL);
+               g_signal_handler_disconnect(
+                               captive_private_bcb_object->SharedCacheMap,     /* instance */
+                               captive_private_bcb_pin_object->purge_handler_id);
+               captive_private_bcb_pin_object->purge_handler_id=0;
+               }
+       if ((SharedCacheMap=captive_private_bcb_object->SharedCacheMap)) {
+               if ((pin_hash=SharedCacheMap->pin_hash)) {
+                       /* Do not: g_assert(g_hash_table_lookup(pin_hash,&captive_private_bcb_object->offset));
+                        * as we may be captive_private_bcb_pin_object_detach_pin()ed.
+                        */
+                       if (captive_private_bcb_object==g_hash_table_lookup(pin_hash,&captive_private_bcb_pin_object->offset))
+                               g_hash_table_remove(pin_hash,&captive_private_bcb_pin_object->offset);
+                       }
+               }
+
+       G_OBJECT_CLASS(captive_private_bcb_pin_object_parent_class)->finalize((GObject *)captive_private_bcb_pin_object);
+}
+
+
+static void captive_private_bcb_pin_object_class_init(CaptivePrivateBcbPinObjectClass *class)
+{
+GObjectClass *gobject_class=G_OBJECT_CLASS(class);
+
+       captive_private_bcb_pin_object_parent_class=g_type_class_ref(G_TYPE_OBJECT);
+       gobject_class->finalize=(void (*)(GObject *object))captive_private_bcb_pin_object_finalize;
+}
+
+static void captive_private_bcb_pin_object_init(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object)
+{
+}
+
+GType captive_private_bcb_pin_object_get_type(void)
+{
+static GType captive_private_bcb_pin_object_type=0;
+
+       if (!captive_private_bcb_pin_object_type) {
+static const GTypeInfo captive_private_bcb_pin_object_info={
+                               sizeof(CaptivePrivateBcbPinObjectClass),
+                               NULL,   /* base_init */
+                               NULL,   /* base_finalize */
+                               (GClassInitFunc)captive_private_bcb_pin_object_class_init,
+                               NULL,   /* class_finalize */
+                               NULL,   /* class_data */
+                               sizeof(CaptivePrivateBcbPinObject),
+                               5,      /* n_preallocs */
+                               (GInstanceInitFunc)captive_private_bcb_pin_object_init,
+                               };
+
+               captive_private_bcb_pin_object_type=g_type_register_static(CAPTIVE_PRIVATE_BCB_TYPE_OBJECT,
+                               "CaptivePrivateBcbPinObject",&captive_private_bcb_pin_object_info,0);
+               }
+
+       return captive_private_bcb_pin_object_type;
+}
+
+
+static guint captive_private_bcb_pin_object_hash_new_hash_func(const guint64 *offsetp)
+{
+       return (*offsetp)^((*offsetp)>>32);
+}
+
+static gint captive_private_bcb_pin_object_hash_new_key_compare_func(const guint64 *offset_ap,const guint64 *offset_bp)
+{
+       return (*offset_bp>*offset_ap)-(*offset_bp<*offset_ap);
+}
+
+GHashTable *captive_private_bcb_pin_object_hash_new(void)
+{
+       return g_hash_table_new(
+                       (GHashFunc)captive_private_bcb_pin_object_hash_new_hash_func,
+                       (GEqualFunc)captive_private_bcb_pin_object_hash_new_key_compare_func);
+}
+
+void captive_private_bcb_pin_object_hash_destroy(GHashTable *pin_hash)
+{
+       g_return_if_fail(pin_hash!=NULL);
+
+       g_hash_table_destroy(pin_hash);
+}
+
+static void captive_private_bcb_pin_object_validate(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object)
+{
+guint64 start,end;
+
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(
+                       CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap));
+
+       start=captive_private_bcb_pin_object->offset;
+       end  =captive_private_bcb_pin_object->offset+captive_private_bcb_pin_object->length;
+
+       g_assert(end<=CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap->ValidDataLength);
+}
+
+static void captive_private_bcb_pin_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength,
+               CaptivePrivateBcbPinObject *captive_private_bcb_pin_object /* user_data */)
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap
+                       ==captive_shared_cache_map_object);
+
+       /* 'AllocationSize' must not change if any map/pin Bcbs exist. */
+       g_assert(AllocationSize==captive_shared_cache_map_object->AllocationSize);
+}
+
+static void captive_private_bcb_pin_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               CaptivePrivateBcbPinObject *captive_private_bcb_pin_object /* user_data */)
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap
+                       ==captive_shared_cache_map_object);
+
+       g_assert(!captive_shared_cache_map_is_page_dirty(captive_shared_cache_map_object,
+                       captive_private_bcb_pin_object->offset));
+}
+
+static void captive_private_bcb_pin_object_FileSizes_changed_after(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength,
+               CaptivePrivateBcbPinObject *captive_private_bcb_pin_object /* user_data */)
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap
+                       ==captive_shared_cache_map_object);
+
+       /* we are 'g_signal_connect_after' */
+       g_assert(AllocationSize ==captive_shared_cache_map_object->AllocationSize);
+       g_assert(FileSize       ==captive_shared_cache_map_object->FileSize);
+       g_assert(ValidDataLength==captive_shared_cache_map_object->ValidDataLength);
+
+       captive_private_bcb_pin_object_validate(captive_private_bcb_pin_object);
+}
+
+void _captive_private_bcb_pin_object_connect_SharedCacheMap
+               (CaptivePrivateBcbPinObject *captive_private_bcb_pin_object,CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap==NULL);
+
+       _captive_private_bcb_object_connect_SharedCacheMap(
+                       CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object),captive_shared_cache_map_object);
+
+       captive_private_bcb_pin_object->FileSizes_changed_handler_id=g_signal_connect(
+                       captive_shared_cache_map_object,"FileSizes_changed",
+                       G_CALLBACK(captive_private_bcb_pin_object_FileSizes_changed),
+                       captive_private_bcb_pin_object);
+       g_assert(captive_private_bcb_pin_object->FileSizes_changed_handler_id>=1);
+       captive_private_bcb_pin_object->FileSizes_changed_after_handler_id=g_signal_connect_after(
+                       captive_shared_cache_map_object,"FileSizes_changed",
+                       G_CALLBACK(captive_private_bcb_pin_object_FileSizes_changed_after),
+                       captive_private_bcb_pin_object);
+       g_assert(captive_private_bcb_pin_object->FileSizes_changed_after_handler_id>=1);
+       captive_private_bcb_pin_object->purge_handler_id=g_signal_connect(
+                       captive_shared_cache_map_object,"purge",
+                       G_CALLBACK(captive_private_bcb_pin_object_purge),
+                       captive_private_bcb_pin_object);
+       g_assert(captive_private_bcb_pin_object->purge_handler_id>=1);
+}
+
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object_new(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset)
+{
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object;
+
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
+
+       g_assert(captive_shared_cache_map_object->PinAccess);
+
+       captive_private_bcb_pin_object=g_object_new(
+                       CAPTIVE_PRIVATE_BCB_PIN_TYPE_OBJECT,    /* object_type */
+                       NULL);  /* first_property_name; FIXME: support properties */
+
+       captive_private_bcb_pin_object->offset=offset;
+       captive_private_bcb_pin_object->length=PAGE_SIZE;
+
+       _captive_private_bcb_pin_object_connect_SharedCacheMap(captive_private_bcb_pin_object,
+                       captive_shared_cache_map_object);
+
+       captive_private_bcb_pin_object->buffer=captive_shared_cache_map_object->buffer+offset;
+
+       g_assert(captive_shared_cache_map_object->pin_hash!=NULL);
+       g_assert(!g_hash_table_lookup(captive_shared_cache_map_object->pin_hash,
+                       &captive_private_bcb_pin_object->offset));
+       g_hash_table_insert(captive_shared_cache_map_object->pin_hash,
+                       &captive_private_bcb_pin_object->offset,
+                       captive_private_bcb_pin_object);
+
+       return captive_private_bcb_pin_object;
+}
+
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object_get_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset)
+{
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object;
+
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
+
+       if ((captive_private_bcb_pin_object=g_hash_table_lookup(captive_shared_cache_map_object->pin_hash,
+                       &offset))) {
+               g_assert(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+               g_object_ref(captive_private_bcb_pin_object);
+               return captive_private_bcb_pin_object;
+               }
+       return captive_private_bcb_pin_object_new(captive_shared_cache_map_object,offset);
+}
+
+gboolean captive_private_bcb_pin_object_is_dirty(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object)
+{
+       g_return_val_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object),FALSE);
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(
+                       CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap),FALSE);
+
+       return captive_shared_cache_map_is_page_dirty(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap,
+                       captive_private_bcb_pin_object->offset);
+}
+
+void captive_private_bcb_pin_object_flush(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object)
+{
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(
+                       CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap));
+       g_return_if_fail(captive_private_bcb_pin_object_is_dirty(captive_private_bcb_pin_object));
+
+       /* Flush synchronously here. */
+       captive_shared_cache_map_flush(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap,
+                       captive_private_bcb_pin_object->offset,
+                       captive_private_bcb_pin_object->offset+captive_private_bcb_pin_object->length);
+}
+
+void captive_private_bcb_pin_object_set_dirty(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object)
+{
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(
+                       CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap));
+
+       captive_shared_cache_map_set_dirty(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap,
+                       captive_private_bcb_pin_object->offset,
+                       captive_private_bcb_pin_object->offset+captive_private_bcb_pin_object->length);
+}
+
+void captive_private_bcb_pin_object_set_lsn(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object,gint64 lsn)
+{
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(
+                       CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap));
+
+       captive_shared_cache_map_page_set_lsn(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object)->SharedCacheMap,
+                       captive_private_bcb_pin_object->offset,lsn);
+}
+
+void captive_private_bcb_pin_object_detach_pin(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object)
+{
+CaptiveSharedCacheMapObject *SharedCacheMap;
+
+       g_return_if_fail(CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(captive_private_bcb_pin_object));
+
+       SharedCacheMap=captive_private_bcb_object_get_SharedCacheMap(CAPTIVE_PRIVATE_BCB_OBJECT(captive_private_bcb_pin_object));
+       g_assert(SharedCacheMap->pin_hash!=NULL);
+       g_assert(captive_private_bcb_pin_object==g_hash_table_lookup(SharedCacheMap->pin_hash,
+                       &captive_private_bcb_pin_object->offset));
+       g_hash_table_remove(SharedCacheMap->pin_hash,&captive_private_bcb_pin_object->offset);
+}
diff --git a/src/libcaptive/cc/privatebcbpin.h b/src/libcaptive/cc/privatebcbpin.h
new file mode 100644 (file)
index 0000000..cd7b429
--- /dev/null
@@ -0,0 +1,60 @@
+/* $Id$
+ * Include file for reactos Cache Manager (Cc*) PrivateBcbPin structure of libcaptive
+ * Copyright (C) 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
+ * 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
+ */
+
+
+#ifndef _CAPTIVE_CC_PRIVATE_BCB_PIN_H
+#define _CAPTIVE_CC_PRIVATE_BCB_PIN_H 1
+
+
+#include <glib/gmacros.h>
+#include <glib-object.h>
+#include "sharedcachemap.h"
+
+
+G_BEGIN_DECLS
+
+#define CAPTIVE_PRIVATE_BCB_PIN_TYPE_OBJECT            (captive_private_bcb_pin_object_get_type())
+#define CAPTIVE_PRIVATE_BCB_PIN_OBJECT(object)         (G_TYPE_CHECK_INSTANCE_CAST((object),CAPTIVE_PRIVATE_BCB_PIN_TYPE_OBJECT,CaptivePrivateBcbPinObject))
+#define CAPTIVE_PRIVATE_BCB_PIN_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),CAPTIVE_PRIVATE_BCB_PIN_TYPE_OBJECT,CaptivePrivateBcbPinObjectClass))
+#define CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object),CAPTIVE_PRIVATE_BCB_PIN_TYPE_OBJECT))
+#define CAPTIVE_PRIVATE_BCB_PIN_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),CAPTIVE_PRIVATE_BCB_PIN_TYPE_OBJECT))
+#define CAPTIVE_PRIVATE_BCB_PIN_OBJECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),CAPTIVE_PRIVATE_BCB_PIN_TYPE_OBJECT,CaptivePrivateBcbPinObjectClass))
+typedef struct _CaptivePrivateBcbPinObject      CaptivePrivateBcbPinObject;
+typedef struct _CaptivePrivateBcbPinObjectClass CaptivePrivateBcbPinObjectClass;
+
+
+GType captive_private_bcb_pin_object_get_type(void);
+
+GHashTable *captive_private_bcb_pin_object_hash_new(void);
+void captive_private_bcb_pin_object_hash_destroy(GHashTable *pin_hash);
+void _captive_private_bcb_pin_object_connect_SharedCacheMap
+               (CaptivePrivateBcbPinObject *captive_private_bcb_pin_object,CaptiveSharedCacheMapObject *SharedCacheMap);
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object_new(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset);
+CaptivePrivateBcbPinObject *captive_private_bcb_pin_object_get_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset);
+gboolean captive_private_bcb_pin_object_is_dirty(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object);
+void captive_private_bcb_pin_object_flush(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object);
+void captive_private_bcb_pin_object_set_dirty(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object);
+void captive_private_bcb_pin_object_set_lsn(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object,gint64 lsn);
+void captive_private_bcb_pin_object_detach_pin(CaptivePrivateBcbPinObject *captive_private_bcb_pin_object);
+
+G_END_DECLS
+
+
+#endif /* _CAPTIVE_CC_PRIVATE_BCB_PIN_H */
diff --git a/src/libcaptive/cc/sharedcachemap-priv.h b/src/libcaptive/cc/sharedcachemap-priv.h
new file mode 100644 (file)
index 0000000..0d69717
--- /dev/null
@@ -0,0 +1,72 @@
+/* $Id$
+ * Object-private include file for reactos Cache Manager (Cc*) SharedCacheMap structure of libcaptive
+ * Copyright (C) 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
+ * 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
+ */
+
+
+#ifndef _CAPTIVE_CC_SHARED_CACHE_MAP_PRIV_H
+#define _CAPTIVE_CC_SHARED_CACHE_MAP_PRIV_H 1
+
+
+#include <glib/gmacros.h>
+#include <glib-object.h>
+#include "reactos/ddk/cctypes.h"
+#include "privatebcbmap.h"
+
+
+G_BEGIN_DECLS
+
+typedef struct _CaptiveSharedCacheMapObject_page CaptiveSharedCacheMapObject_page;
+
+struct _CaptiveSharedCacheMapObject_page {
+       gboolean data_valid;
+       gboolean dirty;
+       gint64 lsn_oldest,lsn_newest;
+       };
+
+struct _CaptiveSharedCacheMapObject {
+       GObject parent_instance;
+
+       gint w32_ref_count;
+       FILE_OBJECT *FileObject;
+       SECTION_OBJECT_POINTERS *SectionObjectPointers;
+       /* ValidDataLength<=FileSize<=AllocationSize */
+       guint64 AllocationSize; /* ==CC_FILE_SIZES.AllocationSize.QuadPart */
+       guint64 FileSize;       /* ==CC_FILE_SIZES.FileSize.QuadPart */
+       guint64 ValidDataLength;        /* ==CC_FILE_SIZES.ValidDataLength.QuadPart */
+       CACHE_MANAGER_CALLBACKS CallBacks;
+       VOID *LazyWriterContext;
+       gboolean PinAccess;
+       CaptivePrivateBcbMapObject *map;
+       GHashTable *pin_hash;   /* map: (guint64 *) -> (CaptivePrivateBcbPinObject *) */
+       gboolean LogHandle_set;
+       gboolean FlushToLsnRoutine_set;
+
+       gpointer buffer;        /* FileOffset 0 based */
+       CaptiveSharedCacheMapObject_page *pages;        /* FileOffset 0 based, item corresponds to 'buffer' PAGE_SIZE */
+       };
+struct _CaptiveSharedCacheMapObjectClass {
+       GObjectClass parent_class;
+
+       void (*FileSizes_changed)(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+                       guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength);
+       void (*purge)(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
+       };
+
+G_END_DECLS
+
+
+#endif /* _CAPTIVE_CC_SHARED_CACHE_MAP_PRIV_H */
diff --git a/src/libcaptive/cc/sharedcachemap.c b/src/libcaptive/cc/sharedcachemap.c
new file mode 100644 (file)
index 0000000..ac49661
--- /dev/null
@@ -0,0 +1,721 @@
+/* $Id$
+ * reactos Cache Manager (Cc*) SharedCacheMap structure of libcaptive
+ * Copyright (C) 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
+ * 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 "sharedcachemap.h"    /* self */
+#include "sharedcachemap-priv.h"       /* self */
+#include "io.h"
+#include "marshallers.h"
+#include <glib-object.h>
+#include "privatebcbpin.h"
+#include "captive/macros.h"
+#include <sys/mman.h>
+
+
+/* CONFIG: */
+
+#define ALLOCATION_BLOCK_SIZE 0x200
+
+
+static GHashTable *CaptiveSharedCacheMapObject_hash;
+
+static void CaptiveSharedCacheMapObject_hash_init(void)
+{
+       if (CaptiveSharedCacheMapObject_hash)
+               return;
+       CaptiveSharedCacheMapObject_hash=g_hash_table_new(
+                       g_direct_hash,  /* hash_func */
+                       g_direct_equal);        /* key_equal_func */
+}
+
+
+static gpointer captive_shared_cache_map_object_parent_class=NULL;
+
+
+static void captive_shared_cache_map_object_finalize(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+static const CC_FILE_SIZES FileSizes_zero;
+gboolean errbool;
+guint64 offset;
+CaptiveSharedCacheMapObject_page *page;
+
+       g_return_if_fail(captive_shared_cache_map_object!=NULL);
+
+       for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
+               page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
+               if (!page->data_valid)
+                       continue;
+               g_assert(!page->dirty); /* FIXME: Is it allowed by W32? */
+               }
+
+       CaptiveSharedCacheMapObject_hash_init();
+       errbool=g_hash_table_remove(CaptiveSharedCacheMapObject_hash,captive_shared_cache_map_object);
+       g_assert(errbool==TRUE);
+
+       captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,&FileSizes_zero);
+       g_assert(captive_shared_cache_map_object->buffer==NULL);
+       g_assert(captive_shared_cache_map_object->pages==NULL);
+
+       if (captive_shared_cache_map_object->pin_hash) {
+               captive_private_bcb_pin_object_hash_destroy(captive_shared_cache_map_object->pin_hash);
+               captive_shared_cache_map_object->pin_hash=NULL;
+               }
+       g_assert(captive_shared_cache_map_object->map==NULL);
+       if (captive_shared_cache_map_object->SectionObjectPointers) {
+               g_assert(captive_shared_cache_map_object==captive_shared_cache_map_object->SectionObjectPointers->SharedCacheMap);
+               captive_shared_cache_map_object->SectionObjectPointers->SharedCacheMap=NULL;
+               }
+
+       G_OBJECT_CLASS(captive_shared_cache_map_object_parent_class)->finalize((GObject *)captive_shared_cache_map_object);
+}
+
+static guint FileSizes_changed_signal;
+static guint purge_signal;
+
+static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength);
+static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
+
+static void captive_shared_cache_map_object_class_init(CaptiveSharedCacheMapObjectClass *class)
+{
+GObjectClass *gobject_class=G_OBJECT_CLASS(class);
+
+       captive_shared_cache_map_object_parent_class=g_type_class_ref(G_TYPE_OBJECT);
+       gobject_class->finalize=(void (*)(GObject *object))captive_shared_cache_map_object_finalize;
+
+       class->FileSizes_changed=captive_shared_cache_map_object_FileSizes_changed;
+       class->purge=captive_shared_cache_map_object_purge;
+
+       FileSizes_changed_signal=g_signal_new("FileSizes_changed",
+                       G_TYPE_FROM_CLASS(gobject_class),
+                       G_SIGNAL_RUN_LAST,
+                       G_STRUCT_OFFSET(CaptiveSharedCacheMapObjectClass,FileSizes_changed),
+                       NULL,NULL,
+                       captive_cc_VOID__UINT64_UINT64_UINT64,
+                       G_TYPE_NONE,3,G_TYPE_UINT64,G_TYPE_UINT64,G_TYPE_UINT64);
+       purge_signal=g_signal_new("purge",
+                       G_TYPE_FROM_CLASS(gobject_class),
+                       G_SIGNAL_RUN_LAST,
+                       G_STRUCT_OFFSET(CaptiveSharedCacheMapObjectClass,purge),
+                       NULL,NULL,
+                       captive_cc_VOID__VOID,
+                       G_TYPE_NONE,0);
+}
+
+
+static void captive_shared_cache_map_object_init(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+       captive_shared_cache_map_object->pin_hash=captive_private_bcb_pin_object_hash_new();
+
+       captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
+       g_object_unref(captive_shared_cache_map_object);        /* ==sink */
+
+       CaptiveSharedCacheMapObject_hash_init();
+       g_hash_table_insert(CaptiveSharedCacheMapObject_hash,
+                       captive_shared_cache_map_object,captive_shared_cache_map_object);
+}
+
+
+GType captive_shared_cache_map_object_get_type(void)
+{
+static GType captive_shared_cache_map_object_type=0;
+
+       if (!captive_shared_cache_map_object_type) {
+static const GTypeInfo captive_shared_cache_map_object_info={
+                               sizeof(CaptiveSharedCacheMapObjectClass),
+                               NULL,   /* base_init */
+                               NULL,   /* base_finalize */
+                               (GClassInitFunc)captive_shared_cache_map_object_class_init,
+                               NULL,   /* class_finalize */
+                               NULL,   /* class_data */
+                               sizeof(CaptiveSharedCacheMapObject),
+                               5,      /* n_preallocs */
+                               (GInstanceInitFunc)captive_shared_cache_map_object_init,
+                               };
+
+               captive_shared_cache_map_object_type=g_type_register_static(G_TYPE_OBJECT,
+                               "CaptiveSharedCacheMapObject",&captive_shared_cache_map_object_info,0);
+               }
+
+       return captive_shared_cache_map_object_type;
+}
+
+static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength)
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+
+       g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
+       g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
+
+       if (captive_shared_cache_map_object->AllocationSize!=AllocationSize) {
+size_t size_old,size_new;
+guint64 size64_old,size64_new;
+gpointer buffer_new;
+
+               /* These two assertions should be already catched by pin/map signal handlers. */
+               g_assert(!captive_shared_cache_map_object->map);
+               g_assert(!g_hash_table_size(captive_shared_cache_map_object->pin_hash));
+
+               size64_old=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);       
+               size_old=size64_old;
+               g_assert(size_old==size64_old);
+               size64_new=CAPTIVE_ROUND_UP64(AllocationSize,PAGE_SIZE);        
+               size_new=size64_new;
+               g_assert(size_new==size64_new);
+
+               if (AllocationSize) {
+gpointer base;
+int errint;
+
+                       base=mmap(
+                                       NULL,   /* start */
+                                       PAGE_SIZE+size_new+PAGE_SIZE,   /* length; leading and trailing boundary check pages */
+                                       PROT_READ|PROT_WRITE,   /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
+                                       MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
+                                       -1,     /* fd; ignored due to MAP_ANONYMOUS */
+                                       0);     /* offset; ignored due to MAP_ANONYMOUS */
+                       g_assert(base!=NULL);
+
+                       base+=PAGE_SIZE;
+                       errint=munmap(base-PAGE_SIZE,PAGE_SIZE);        /* unmap leading boundary check page */
+                       g_assert(errint==0);
+                       errint=munmap(base+size_new,PAGE_SIZE); /* unmap trailing boundary check page */
+                       g_assert(errint==0);
+
+                       buffer_new=base;
+                       }
+               memcpy(buffer_new,captive_shared_cache_map_object->buffer,
+                               MIN(AllocationSize,captive_shared_cache_map_object->AllocationSize));
+               if (captive_shared_cache_map_object->AllocationSize) {
+int errint;
+
+                       errint=munmap(captive_shared_cache_map_object->buffer,size_old);
+                       g_assert(errint==0);
+                       }
+               captive_shared_cache_map_object->buffer=buffer_new;
+
+               captive_shared_cache_map_object->pages=g_realloc(captive_shared_cache_map_object->pages,
+                               size_new/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
+               memset(captive_shared_cache_map_object->pages+(size_old/PAGE_SIZE),0,
+                               MAX(0,size_new-size_old)/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
+               }
+
+       captive_shared_cache_map_object->AllocationSize=AllocationSize;
+       captive_shared_cache_map_object->FileSize=FileSize;
+       captive_shared_cache_map_object->ValidDataLength=ValidDataLength;
+
+       g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
+       g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
+}
+
+static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+
+       /* NOP; just to provide slot for checking by Bcbs */
+}
+
+CaptiveSharedCacheMapObject *captive_shared_cache_map_get_ref(FILE_OBJECT *FileObject,
+               const CC_FILE_SIZES *FileSizes,BOOLEAN PinAccess,CACHE_MANAGER_CALLBACKS *CallBacks,VOID *LazyWriterContext)
+{
+CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
+
+       g_return_val_if_fail(FileObject!=NULL,NULL);
+       g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,NULL);
+       g_return_val_if_fail(FileSizes!=NULL,NULL);
+       g_return_val_if_fail(CallBacks!=NULL,NULL);
+
+       if ((captive_shared_cache_map_object=FileObject->SectionObjectPointers->SharedCacheMap)) {
+               captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
+               }
+       else {
+               captive_shared_cache_map_object=g_object_new(
+                               CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT,   /* object_type */
+                               NULL);  /* first_property_name; FIXME: support properties */
+
+               captive_shared_cache_map_object->FileObject=FileObject;
+               captive_shared_cache_map_object->SectionObjectPointers=FileObject->SectionObjectPointers;
+               captive_shared_cache_map_object->AllocationSize=0;
+               captive_shared_cache_map_object->FileSize=0;
+               captive_shared_cache_map_object->ValidDataLength=0;
+               captive_shared_cache_map_object->PinAccess=PinAccess;
+               captive_shared_cache_map_object->CallBacks=*CallBacks;
+               captive_shared_cache_map_object->LazyWriterContext=LazyWriterContext;
+
+               FileObject->SectionObjectPointers->SharedCacheMap=captive_shared_cache_map_object;
+               }
+
+       g_assert(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       /* FileObject may differ - we can have a different reference to the same FCB. */
+       g_assert(FileObject->SectionObjectPointers==captive_shared_cache_map_object->SectionObjectPointers);
+       g_assert(PinAccess==captive_shared_cache_map_object->PinAccess);
+       g_assert(CallBacks->AcquireForLazyWrite==captive_shared_cache_map_object->CallBacks.AcquireForLazyWrite);
+       g_assert(CallBacks->ReleaseFromLazyWrite==captive_shared_cache_map_object->CallBacks.ReleaseFromLazyWrite);
+       g_assert(CallBacks->AcquireForReadAhead==captive_shared_cache_map_object->CallBacks.AcquireForReadAhead);
+       g_assert(CallBacks->ReleaseFromReadAhead==captive_shared_cache_map_object->CallBacks.ReleaseFromReadAhead);
+       g_assert(LazyWriterContext==captive_shared_cache_map_object->LazyWriterContext);
+
+       captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,FileSizes);
+
+       return captive_shared_cache_map_object;
+}
+
+void captive_shared_cache_map_FileSizes_set(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               const CC_FILE_SIZES *FileSizes)
+{
+guint64 AllocationSize,FileSize,ValidDataLength;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(FileSizes!=NULL);
+
+       AllocationSize=FileSizes->AllocationSize.QuadPart;
+       FileSize=FileSizes->FileSize.QuadPart;
+       ValidDataLength=FileSizes->ValidDataLength.QuadPart;
+
+       if (ValidDataLength==G_MAXINT64)
+               ValidDataLength=FileSize;
+
+       g_assert(AllocationSize>=0);
+       g_assert(FileSize>=0);
+       g_assert(ValidDataLength>=0);
+
+       g_assert(ValidDataLength<=FileSize);
+       g_assert(0==(AllocationSize%ALLOCATION_BLOCK_SIZE));
+       /* AllocationSize can be much higher: */
+       g_assert(FileSize<=AllocationSize);
+
+       g_signal_emit(captive_shared_cache_map_object,FileSizes_changed_signal,0,
+                       AllocationSize,FileSize,ValidDataLength);
+
+       g_assert(captive_shared_cache_map_object->AllocationSize==AllocationSize);
+       g_assert(captive_shared_cache_map_object->FileSize==FileSize);
+       g_assert(captive_shared_cache_map_object->ValidDataLength==ValidDataLength);
+}
+
+CaptiveSharedCacheMapObject *captive_SectionObjectPointers_to_SharedCacheMap(SECTION_OBJECT_POINTERS *SectionObjectPointers)
+{
+       g_return_val_if_fail(SectionObjectPointers!=NULL,NULL);
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(SectionObjectPointers->SharedCacheMap),NULL);
+
+       return SectionObjectPointers->SharedCacheMap;
+}
+
+CaptiveSharedCacheMapObject *captive_FileObject_to_SharedCacheMap(FILE_OBJECT *FileObject)
+{
+       g_return_val_if_fail(FileObject!=NULL,NULL);
+
+       return captive_SectionObjectPointers_to_SharedCacheMap(FileObject->SectionObjectPointers);
+}
+
+void captive_shared_cache_map_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+
+       g_object_ref(captive_shared_cache_map_object);
+       captive_shared_cache_map_object->w32_ref_count++;
+}
+
+void captive_shared_cache_map_w32_unref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(G_OBJECT(captive_shared_cache_map_object)->ref_count>0);
+
+       captive_shared_cache_map_object->w32_ref_count--;
+       if (!captive_shared_cache_map_object->w32_ref_count)
+               g_assert(G_OBJECT(captive_shared_cache_map_object)->ref_count==1);
+
+       g_object_unref(captive_shared_cache_map_object);
+}
+
+void captive_shared_cache_map_data_validate_read(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               FILE_OBJECT *FileObject,guint64 start,guint64 end)
+{
+guint64 now;
+gboolean after_eof=FALSE;      /* Did we reached the end of file already? */
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(captive_shared_cache_map_object==captive_FileObject_to_SharedCacheMap(FileObject));
+       g_return_if_fail(start<=end);
+       g_return_if_fail(end<=captive_shared_cache_map_object->ValidDataLength);
+
+       start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
+       end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
+
+       for (now=start;now<end;now+=PAGE_SIZE) {
+LARGE_INTEGER now_LargeInteger;
+ULONG got;
+
+               if (captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
+                       continue;
+               now_LargeInteger.QuadPart=now;
+               got=captive_Cc_IoPageRead(FileObject,captive_shared_cache_map_object->buffer+now,PAGE_SIZE,&now_LargeInteger);
+               if (after_eof)
+                       g_assert(got==0);
+               else
+                       g_assert(got<=PAGE_SIZE);
+               after_eof=(got<PAGE_SIZE);
+               captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
+               }
+}
+
+void captive_shared_cache_map_data_validate_noread(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 start,guint64 end)
+{
+guint64 now;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(start<=end);
+       g_return_if_fail(end<=captive_shared_cache_map_object->ValidDataLength);
+
+       start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
+       end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
+
+       for (now=start;now<end;now+=PAGE_SIZE) {
+               g_assert(captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid);
+               }
+}
+
+void captive_shared_cache_map_set_data_valid(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 start,guint64 end)
+{
+guint64 now;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(start<=end);
+       g_return_if_fail(end<=captive_shared_cache_map_object->ValidDataLength);
+
+       start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
+       end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
+
+       for (now=start;now<end;now+=PAGE_SIZE) {
+               captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
+               }
+}
+
+void captive_shared_cache_map_set_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 start,guint64 end)
+{
+guint64 now;
+CaptiveSharedCacheMapObject_page *page;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(end<=captive_shared_cache_map_object->ValidDataLength);
+
+       start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
+       end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
+
+       for (now=start;now<end;now+=PAGE_SIZE) {
+               page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
+               g_assert(page->data_valid);
+               if (!page->dirty) {
+                       page->dirty=TRUE;
+                       page->lsn_oldest=0;
+                       page->lsn_newest=0;
+                       }
+               }
+}
+
+gboolean captive_shared_cache_map_is_page_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset)
+{
+CaptiveSharedCacheMapObject_page *page;
+
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),FALSE);
+       g_return_val_if_fail(offset<captive_shared_cache_map_object->ValidDataLength,FALSE);
+       g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE),FALSE);
+       page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
+       g_return_val_if_fail(page->data_valid,FALSE);
+
+       return page->dirty;
+}
+
+void captive_shared_cache_map_page_set_lsn(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset,gint64 lsn)
+{
+CaptiveSharedCacheMapObject_page *page;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(offset<captive_shared_cache_map_object->ValidDataLength);
+       g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
+       page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
+       g_return_if_fail(page->data_valid);
+       g_return_if_fail(page->dirty);
+       g_return_if_fail(page->lsn_oldest<=page->lsn_newest);
+       g_return_if_fail(!page->lsn_newest || lsn>=page->lsn_newest);
+       g_return_if_fail(captive_shared_cache_map_object->LogHandle_set);
+       g_return_if_fail(captive_shared_cache_map_object->FlushToLsnRoutine_set);
+
+       if (!page->lsn_oldest)
+               page->lsn_oldest=lsn;
+       page->lsn_newest=lsn;
+}
+
+void captive_shared_cache_map_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+guint64 offset;
+CaptiveSharedCacheMapObject_page *page;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+
+       g_signal_emit(captive_shared_cache_map_object,purge_signal,0);
+
+       for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
+               page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
+               if (!page->data_valid)
+                       continue;
+               g_assert(!page->dirty); /* FIXME: Is it allowed by W32? */
+               page->data_valid=FALSE;
+               }
+}
+
+static VOID *captive_LogHandle;
+static PFLUSH_TO_LSN captive_FlushToLsnRoutine;
+
+void captive_shared_cache_map_set_LogHandle(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,VOID *LogHandle)
+{
+       g_return_if_fail(!captive_shared_cache_map_object || CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(!captive_shared_cache_map_object || !captive_shared_cache_map_object->LogHandle_set);
+
+       if (!LogHandle)
+               return;
+       g_assert(!captive_LogHandle || captive_LogHandle==LogHandle);
+       captive_LogHandle=LogHandle;
+       if (LogHandle && captive_shared_cache_map_object)
+               captive_shared_cache_map_object->LogHandle_set=TRUE;
+}
+
+void captive_shared_cache_map_set_FlushToLsnRoutine
+               (CaptiveSharedCacheMapObject *captive_shared_cache_map_object,PFLUSH_TO_LSN FlushToLsnRoutine)
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(!captive_shared_cache_map_object->FlushToLsnRoutine_set);
+
+       if (!FlushToLsnRoutine)
+               return;
+       g_assert(!captive_FlushToLsnRoutine || captive_FlushToLsnRoutine==FlushToLsnRoutine);
+       captive_FlushToLsnRoutine=FlushToLsnRoutine;
+       if (FlushToLsnRoutine)
+               captive_shared_cache_map_object->FlushToLsnRoutine_set=TRUE;
+}
+
+static void captive_shared_cache_map_page_write(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset)
+{
+LARGE_INTEGER offset_LargeInteger;
+static gint64 lsn_last;
+CaptiveSharedCacheMapObject_page *page;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(offset<captive_shared_cache_map_object->ValidDataLength);
+       g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
+       page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
+       g_return_if_fail(page->data_valid);
+       g_return_if_fail(page->dirty);
+
+       if (page->lsn_newest) {
+LARGE_INTEGER lsn_newest_LargeInteger;
+
+               /* sanity check */
+               g_assert(!lsn_last || lsn_last<=page->lsn_newest);
+               lsn_last=page->lsn_newest;
+
+               lsn_newest_LargeInteger.QuadPart=page->lsn_newest;
+               (*captive_FlushToLsnRoutine)(captive_LogHandle,lsn_newest_LargeInteger);
+               }
+
+       offset_LargeInteger.QuadPart=offset;
+       captive_Cc_IoPageWrite(captive_shared_cache_map_object->FileObject,
+                       captive_shared_cache_map_object->buffer+offset,PAGE_SIZE,&offset_LargeInteger);
+
+       page->dirty=FALSE;
+       page->lsn_oldest=0;
+       page->lsn_newest=0;
+}
+
+guint64 captive_shared_cache_map_flush(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 start,guint64 end)
+{
+guint64 flushed;
+guint64 now;
+
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),0);
+       g_return_val_if_fail(start<=end,0);
+
+       if (end>captive_shared_cache_map_object->ValidDataLength)
+               end=captive_shared_cache_map_object->ValidDataLength;
+
+       start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
+       end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
+
+       flushed=0;
+       for (now=start;now<end;now+=PAGE_SIZE) {
+               if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
+                       continue;
+               if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty)
+                       continue;
+               captive_shared_cache_map_page_write(captive_shared_cache_map_object,now);
+               flushed+=PAGE_SIZE;
+               }
+
+       /* We were calling W32 code - recheck our task completion. */
+       for (now=start;now<end;now+=PAGE_SIZE) {
+               if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
+                       continue;
+               g_assert(!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty);
+               }
+
+       return flushed;
+}
+
+static void captive_shared_cache_map_is_any_dirty_foreach(
+               CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
+               CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
+               gboolean *dirty_foundp) /* user_data */
+{
+guint64 now;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
+       g_return_if_fail(dirty_foundp!=NULL);
+
+       for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
+               if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
+                       continue;
+               if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty)
+                       continue;
+               *dirty_foundp=TRUE;     /* FIXME: stop the traversal. */
+               break;
+               }
+}
+
+
+gboolean captive_shared_cache_map_is_any_dirty(void)
+{
+gboolean dirty_found;
+
+       CaptiveSharedCacheMapObject_hash_init();
+       dirty_found=FALSE;
+       g_hash_table_foreach(
+                       CaptiveSharedCacheMapObject_hash,       /* hash_table */
+                       (GHFunc)captive_shared_cache_map_is_any_dirty_foreach,  /* func */
+                       &dirty_found);  /* user_data */
+
+       return dirty_found;
+}
+
+
+typedef struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param captive_shared_cache_map_CcGetDirtyPages_foreach_param;
+struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param {
+       PDIRTY_PAGE_ROUTINE DirtyPageRoutine;
+       VOID *Context1;
+       VOID *Context2;
+       gint64 result;
+       };
+
+static void captive_shared_cache_map_CcGetDirtyPages_foreach(
+               CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
+               CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
+               captive_shared_cache_map_CcGetDirtyPages_foreach_param *param)  /* user_data */
+{
+gint64 now;
+CaptiveSharedCacheMapObject_page *page;
+
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
+       g_return_if_fail(param!=NULL);
+
+       for (now=CAPTIVE_ROUND_DOWN64(captive_shared_cache_map_object->AllocationSize-1,PAGE_SIZE);now>=0;now-=PAGE_SIZE) {
+LARGE_INTEGER now_LargeInteger,lsn_oldest_LargeInteger,lsn_newest_LargeInteger;
+
+               page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
+               if (!page->data_valid)
+                       continue;
+               if (!page->dirty)
+                       continue;
+               if (page->lsn_oldest && (!param->result || param->result>page->lsn_oldest))
+                       param->result=page->lsn_oldest;
+
+               now_LargeInteger.QuadPart=now;
+               lsn_oldest_LargeInteger.QuadPart=page->lsn_oldest;
+               lsn_newest_LargeInteger.QuadPart=page->lsn_newest;
+               (*param->DirtyPageRoutine)(
+                               captive_shared_cache_map_object->FileObject,    /* FileObject */
+                               &now_LargeInteger,      /* FileOffset */
+                               PAGE_SIZE,      /* Length */
+                               &lsn_oldest_LargeInteger,       /* OldestLsn */
+                               &lsn_newest_LargeInteger,       /* NewestLsn */
+                               param->Context1,        /* Context1 */
+                               param->Context2);       /* Context2 */
+               }
+}
+
+gint64 captive_shared_cache_map_CcGetDirtyPages(PDIRTY_PAGE_ROUTINE DirtyPageRoutine,VOID *Context1,VOID *Context2)
+{
+captive_shared_cache_map_CcGetDirtyPages_foreach_param param;
+
+       g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
+
+       param.DirtyPageRoutine=DirtyPageRoutine;
+       param.Context1=Context1;
+       param.Context2=Context2;
+       param.result=0;
+
+       CaptiveSharedCacheMapObject_hash_init();
+       g_hash_table_foreach(
+                       CaptiveSharedCacheMapObject_hash,       /* hash_table */
+                       (GHFunc)captive_shared_cache_map_CcGetDirtyPages_foreach,       /* func */
+                       &param);        /* user_data */
+
+       return param.result;
+}
+
+gpointer captive_shared_cache_map_get_buffer(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
+{
+       g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
+       g_return_val_if_fail(captive_shared_cache_map_object->buffer!=NULL,NULL);
+
+       return captive_shared_cache_map_object->buffer;
+}
+
+static void captive_cc_FileObject_delete_foreach(
+               CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
+               CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
+               FILE_OBJECT *FileObject)        /* user_data */
+{
+       g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
+       g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
+       g_return_if_fail(FileObject!=NULL);
+
+       g_assert(FileObject!=captive_shared_cache_map_object->FileObject);
+}
+
+BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
+{
+       g_return_val_if_fail(FileObject!=NULL,FALSE);
+
+       CaptiveSharedCacheMapObject_hash_init();
+       g_hash_table_foreach(
+                       CaptiveSharedCacheMapObject_hash,       /* hash_table */
+                       (GHFunc)captive_cc_FileObject_delete_foreach,   /* func */
+                       FileObject);    /* user_data */
+
+       return FALSE;   /* FIXME: remove useless return code. */
+}
diff --git a/src/libcaptive/cc/sharedcachemap.h b/src/libcaptive/cc/sharedcachemap.h
new file mode 100644 (file)
index 0000000..2b65624
--- /dev/null
@@ -0,0 +1,76 @@
+/* $Id$
+ * Include file for reactos Cache Manager (Cc*) SharedCacheMap structure of libcaptive
+ * Copyright (C) 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
+ * 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
+ */
+
+
+#ifndef _CAPTIVE_CC_SHARED_CACHE_MAP_H
+#define _CAPTIVE_CC_SHARED_CACHE_MAP_H 1
+
+
+#include <glib/gmacros.h>
+#include <glib-object.h>
+#include "reactos/ddk/cctypes.h"
+
+
+G_BEGIN_DECLS
+
+#define CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT            (captive_shared_cache_map_object_get_type())
+#define CAPTIVE_SHARED_CACHE_MAP_OBJECT(object)         (G_TYPE_CHECK_INSTANCE_CAST((object),CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT,CaptiveSharedCacheMapObject))
+#define CAPTIVE_SHARED_CACHE_MAP_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT,CaptiveSharedCacheMapObjectClass))
+#define CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object),CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT))
+#define CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT))
+#define CAPTIVE_SHARED_CACHE_MAP_OBJECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT,CaptiveSharedCacheMapObjectClass))
+typedef struct _CaptiveSharedCacheMapObject      CaptiveSharedCacheMapObject;
+typedef struct _CaptiveSharedCacheMapObjectClass CaptiveSharedCacheMapObjectClass;
+
+
+GType captive_shared_cache_map_object_get_type(void);
+
+CaptiveSharedCacheMapObject *captive_shared_cache_map_get_ref(FILE_OBJECT *FileObject,
+               const CC_FILE_SIZES *FileSizes,BOOLEAN PinAccess,CACHE_MANAGER_CALLBACKS *CallBacks,VOID *LazyWriterContext);
+void captive_shared_cache_map_FileSizes_set(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               const CC_FILE_SIZES *FileSizes);
+CaptiveSharedCacheMapObject *captive_SectionObjectPointers_to_SharedCacheMap(SECTION_OBJECT_POINTERS *SectionObjectPointers);
+CaptiveSharedCacheMapObject *captive_FileObject_to_SharedCacheMap(FILE_OBJECT *FileObject);
+void captive_shared_cache_map_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
+void captive_shared_cache_map_w32_unref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
+void captive_shared_cache_map_data_validate_read(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               FILE_OBJECT *FileObject,guint64 validate_start,guint64 validate_end);
+void captive_shared_cache_map_data_validate_noread(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 validate_start,guint64 validate_end);
+void captive_shared_cache_map_set_data_valid(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 start,guint64 end);
+void captive_shared_cache_map_set_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 start,guint64 end);
+gboolean captive_shared_cache_map_is_page_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset);
+void captive_shared_cache_map_page_set_lsn(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 offset,gint64 lsn);
+void captive_shared_cache_map_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
+void captive_shared_cache_map_set_LogHandle(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,VOID *LogHandle);
+void captive_shared_cache_map_set_FlushToLsnRoutine
+               (CaptiveSharedCacheMapObject *captive_shared_cache_map_object,PFLUSH_TO_LSN FlushToLsnRoutine);
+guint64 captive_shared_cache_map_flush(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
+               guint64 start,guint64 end);
+gboolean captive_shared_cache_map_is_any_dirty(void);
+gint64 captive_shared_cache_map_CcGetDirtyPages(PDIRTY_PAGE_ROUTINE DirtyPageRoutine,VOID *Context1,VOID *Context2);
+gpointer captive_shared_cache_map_get_buffer(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
+
+G_END_DECLS
+
+
+#endif /* _CAPTIVE_CC_SHARED_CACHE_MAP_H */
index 5ac85e2..90a540a 100644 (file)
@@ -296,8 +296,11 @@ 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);
@@ -333,7 +336,16 @@ int fsctlsi;
        Status=ObReferenceObjectByHandle(dir_Handle,FILE_LIST_DIRECTORY,IoFileObjectType,UserMode,(PVOID *)&FileObject,NULL);
        g_assert(NT_SUCCESS(Status));
 
-       for (fsctlsi=0;fsctlsi<2;fsctlsi++) {
+       g_assert(FileObject->FileName.Length==0);
+       g_assert(FileObject->FileName.MaximumLength==0);
+       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);
 
@@ -342,18 +354,24 @@ int fsctlsi;
                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().
@@ -399,7 +417,9 @@ GIOStatus erriostatus;
                        FALSE)) /* may_block */
                g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_main_context_iteration() proceeded",G_STRLOC);
 
-       captive_cc_flush();     /* based on captive_leave(), not g_main idle */
+       /* Do not: captive_cc_flush();  * based on captive_leave(), not g_main idle *
+        * replaced by IRP_MJ_FLUSH_BUFFERS.
+        */
 
        dismount_volume();
 
@@ -409,7 +429,9 @@ GIOStatus erriostatus;
                        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(),