+captive_shared_cache_map_set_data_invalid()
[captive.git] / src / libcaptive / cc / sharedcachemap.c
index ac49661..0224e3f 100644 (file)
 #include "privatebcbpin.h"
 #include "captive/macros.h"
 #include <sys/mman.h>
-
-
-/* CONFIG: */
-
-#define ALLOCATION_BLOCK_SIZE 0x200
+#include "reactos/ddk/obfuncs.h"
 
 
 static GHashTable *CaptiveSharedCacheMapObject_hash;
@@ -78,9 +74,15 @@ CaptiveSharedCacheMapObject_page *page;
                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;
+       if (captive_shared_cache_map_object->SectionObjectPointer) {
+               g_assert(captive_shared_cache_map_object==captive_shared_cache_map_object->SectionObjectPointer->SharedCacheMap);
+               captive_shared_cache_map_object->SectionObjectPointer->SharedCacheMap=NULL;
+               }
+
+       if (captive_shared_cache_map_object->FileObject) {
+               /* W32 dereferences twice. */
+               ObDereferenceObject(captive_shared_cache_map_object->FileObject);
+               captive_shared_cache_map_object->FileObject=NULL;
                }
 
        G_OBJECT_CLASS(captive_shared_cache_map_object_parent_class)->finalize((GObject *)captive_shared_cache_map_object);
@@ -97,7 +99,7 @@ static void captive_shared_cache_map_object_class_init(CaptiveSharedCacheMapObje
 {
 GObjectClass *gobject_class=G_OBJECT_CLASS(class);
 
-       captive_shared_cache_map_object_parent_class=g_type_class_ref(G_TYPE_OBJECT);
+       captive_shared_cache_map_object_parent_class=g_type_class_ref(g_type_parent(G_TYPE_FROM_CLASS(class)));
        gobject_class->finalize=(void (*)(GObject *object))captive_shared_cache_map_object_finalize;
 
        class->FileSizes_changed=captive_shared_cache_map_object_FileSizes_changed;
@@ -160,27 +162,32 @@ static const GTypeInfo captive_shared_cache_map_object_info={
 static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
                guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength)
 {
+size_t size_old,size_new;
+guint64 size64_old,size64_new;
+
        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;
+       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;
+       if (size_new!=size64_new) {
+size_new_big:
+               g_error("Mapped size %" G_GUINT64_FORMAT " is too big; FIXME: non-mmap(2)-able areas not yet supported",size64_new);
+               g_assert_not_reached();
+               }
+
+       if (size_old!=size_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;
@@ -189,9 +196,12 @@ int errint;
                                        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 */
+                                       MAP_PRIVATE|MAP_ANONYMOUS       /* flags */
+                                                       |MAP_NORESERVE, /* At least ext2fsd maps the whole disk. */
                                        -1,     /* fd; ignored due to MAP_ANONYMOUS */
                                        0);     /* offset; ignored due to MAP_ANONYMOUS */
+                       if (base==MAP_FAILED)
+                               goto size_new_big;
                        g_assert(base!=NULL);
 
                        base+=PAGE_SIZE;
@@ -202,20 +212,37 @@ int errint;
 
                        buffer_new=base;
                        }
+               else
+                       buffer_new=NULL;
+
                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;
 
+#if 0  /* It appears it is valid to squeeze out 'dirty' blocks. FIXME: Flush them? */
+               if (size_old>size_new) {
+guint64 now;
+
+                       for (now=size_new;now<size_old;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);
+                               }
+                       }
+#endif
                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));
+               if (size_new>size_old)  /* prevent 'size_new-size_old' as it is unsigned! */
+                       memset(captive_shared_cache_map_object->pages+(size_old/PAGE_SIZE),0,
+                                       (size_new-size_old)/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
                }
 
        captive_shared_cache_map_object->AllocationSize=AllocationSize;
@@ -234,16 +261,16 @@ static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *c
 }
 
 CaptiveSharedCacheMapObject *captive_shared_cache_map_get_ref(FILE_OBJECT *FileObject,
-               const CC_FILE_SIZES *FileSizes,BOOLEAN PinAccess,CACHE_MANAGER_CALLBACKS *CallBacks,VOID *LazyWriterContext)
+               const CC_FILE_SIZES *FileSizes,BOOLEAN PinAccess,const 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(FileObject->SectionObjectPointer!=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)) {
+       if ((captive_shared_cache_map_object=FileObject->SectionObjectPointer->SharedCacheMap)) {
                captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
                }
        else {
@@ -251,8 +278,16 @@ CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
                                CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT,   /* object_type */
                                NULL);  /* first_property_name; FIXME: support properties */
 
+               /* FIXME: When to drop SharedCacheMap?
+                * Currently we never close it.
+                * Fix also CcZeroData() workaround.
+                */
+               g_object_ref(captive_shared_cache_map_object);
+
+               /* W32 references twice. */
+               ObReferenceObject(FileObject);
                captive_shared_cache_map_object->FileObject=FileObject;
-               captive_shared_cache_map_object->SectionObjectPointers=FileObject->SectionObjectPointers;
+               captive_shared_cache_map_object->SectionObjectPointer=FileObject->SectionObjectPointer;
                captive_shared_cache_map_object->AllocationSize=0;
                captive_shared_cache_map_object->FileSize=0;
                captive_shared_cache_map_object->ValidDataLength=0;
@@ -260,12 +295,12 @@ CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
                captive_shared_cache_map_object->CallBacks=*CallBacks;
                captive_shared_cache_map_object->LazyWriterContext=LazyWriterContext;
 
-               FileObject->SectionObjectPointers->SharedCacheMap=captive_shared_cache_map_object;
+               FileObject->SectionObjectPointer->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(FileObject->SectionObjectPointer==captive_shared_cache_map_object->SectionObjectPointer);
        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);
@@ -298,12 +333,27 @@ guint64 AllocationSize,FileSize,ValidDataLength;
        g_assert(ValidDataLength>=0);
 
        g_assert(ValidDataLength<=FileSize);
-       g_assert(0==(AllocationSize%ALLOCATION_BLOCK_SIZE));
+       /* Do not: g_assert(0==(AllocationSize%0x200));
+        * as it is true for ntfs.sys of NT-5.1sp1 but it fails for fastfat.sys of NT-5.1sp1.
+        */
        /* AllocationSize can be much higher: */
        g_assert(FileSize<=AllocationSize);
 
-       g_signal_emit(captive_shared_cache_map_object,FileSizes_changed_signal,0,
-                       AllocationSize,FileSize,ValidDataLength);
+       /* Prevent signalling if not needed. */
+       if (0
+                       || captive_shared_cache_map_object->AllocationSize!=AllocationSize
+                       || captive_shared_cache_map_object->FileSize!=FileSize
+                       || captive_shared_cache_map_object->ValidDataLength!=ValidDataLength) {
+               /* Signalling is forbidden in captive_shared_cache_map_object_finalize(). */
+               if (G_OBJECT(captive_shared_cache_map_object)->ref_count==0) {
+                       (*CAPTIVE_SHARED_CACHE_MAP_OBJECT_GET_CLASS(captive_shared_cache_map_object)->FileSizes_changed)
+                                       (captive_shared_cache_map_object,AllocationSize,FileSize,ValidDataLength);
+                       }
+               else {
+                               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);
@@ -322,7 +372,7 @@ CaptiveSharedCacheMapObject *captive_FileObject_to_SharedCacheMap(FILE_OBJECT *F
 {
        g_return_val_if_fail(FileObject!=NULL,NULL);
 
-       return captive_SectionObjectPointers_to_SharedCacheMap(FileObject->SectionObjectPointers);
+       return captive_SectionObjectPointers_to_SharedCacheMap(FileObject->SectionObjectPointer);
 }
 
 void captive_shared_cache_map_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
@@ -339,9 +389,6 @@ void captive_shared_cache_map_w32_unref(CaptiveSharedCacheMapObject *captive_sha
        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);
 }
 
@@ -354,7 +401,7 @@ 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);
+       g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
 
        start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
        end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
@@ -373,6 +420,7 @@ ULONG got;
                        g_assert(got<=PAGE_SIZE);
                after_eof=(got<PAGE_SIZE);
                captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
+               captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty=FALSE;
                }
 }
 
@@ -383,7 +431,7 @@ 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);
+       g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
 
        start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
        end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
@@ -400,13 +448,39 @@ 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);
+       g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
+
+       start=CAPTIVE_ROUND_UP64(start,PAGE_SIZE);
+       if (end<captive_shared_cache_map_object->FileSize)
+               end=CAPTIVE_ROUND_DOWN64(end,PAGE_SIZE);
+       else {
+               /* We can validate the last page of the file
+                * even if it does not end on PAGE_SIZE boundary.
+                */
+               end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
+               }
+       /* We may get end<start here - it is valid to not to validate anything. */
+
+       for (now=start;now<end;now+=PAGE_SIZE) {
+               captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
+               /* .dirty is undefined */
+               }
+}
+
+void captive_shared_cache_map_set_data_invalid(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_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
 
        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;
+               captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=FALSE;
                }
 }
 
@@ -417,7 +491,7 @@ 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);
+       g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
 
        start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
        end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
@@ -439,7 +513,7 @@ gboolean captive_shared_cache_map_is_page_dirty(CaptiveSharedCacheMapObject *cap
 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(offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE),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);
@@ -453,7 +527,7 @@ void captive_shared_cache_map_page_set_lsn(CaptiveSharedCacheMapObject *captive_
 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(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
        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);
@@ -477,11 +551,18 @@ CaptiveSharedCacheMapObject_page *page;
 
        g_signal_emit(captive_shared_cache_map_object,purge_signal,0);
 
+       /* Needed by fastfat.sys of NT-5.1sp1 during FAT32 unmount
+        * otherwise it fails on: g_assert(!page->dirty);
+        * It corrupts the disk if the buffer is dropped instead.
+        */
+       captive_shared_cache_map_flush(captive_shared_cache_map_object,
+                       0,CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
+
        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? */
+               g_assert(!page->dirty);
                page->data_valid=FALSE;
                }
 }
@@ -492,7 +573,9 @@ 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);
+       /* FIXME: 'captive_shared_cache_map_object->LogHandle_set' may be set.
+        * Does it mean 'LogHandle_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
+        */
 
        if (!LogHandle)
                return;
@@ -506,7 +589,9 @@ 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);
+       /* FIXME: 'captive_shared_cache_map_object->FlushToLsnRoutine_set' may be set.
+        * Does it mean 'FlushToLsnRoutine_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
+        */
 
        if (!FlushToLsnRoutine)
                return;
@@ -524,21 +609,22 @@ 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(captive_shared_cache_map_object->FileObject!=NULL);
+       g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
        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);
+               captive_stdcall_call_12((CaptiveStdCallFunc12)captive_FlushToLsnRoutine,
+                               captive_LogHandle,
+                               (gpointer)(guint32)(page->lsn_newest>> 0U),     /* 'LARGE_INTEGER' argument */
+                               (gpointer)(guint32)(page->lsn_newest>>32U));
                }
 
        offset_LargeInteger.QuadPart=offset;
@@ -550,6 +636,77 @@ LARGE_INTEGER lsn_newest_LargeInteger;
        page->lsn_newest=0;
 }
 
+typedef struct _captive_shared_cache_map_page_write_lsn_foreach_param captive_shared_cache_map_page_write_lsn_foreach_param;
+struct _captive_shared_cache_map_page_write_lsn_foreach_param {
+       gint64 lsn_target;
+       gint64 lsn_best;
+       CaptiveSharedCacheMapObject *captive_shared_cache_map_object_best;
+       guint64 offset_best;
+       };
+
+static void captive_shared_cache_map_page_write_lsn_foreach(
+               CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
+               CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
+               captive_shared_cache_map_page_write_lsn_foreach_param *param)   /* user_data */
+{
+guint64 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=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
+               page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
+               if (!page->data_valid)
+                       continue;
+               if (!page->dirty)
+                       continue;
+               if (!page->lsn_newest)
+                       continue;
+               if (page->lsn_newest>=param->lsn_target)
+                       continue;
+               if (param->lsn_best && page->lsn_newest>param->lsn_best)
+                       continue;
+               param->lsn_best=page->lsn_newest;
+               param->captive_shared_cache_map_object_best=captive_shared_cache_map_object;
+               param->offset_best=now;
+               }
+}
+
+static void captive_shared_cache_map_page_write_lsn(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_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
+       g_return_if_fail(offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
+       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) {
+               CaptiveSharedCacheMapObject_hash_init();
+               for (;;) {
+captive_shared_cache_map_page_write_lsn_foreach_param param;
+
+                       param.lsn_target=page->lsn_newest;
+                       param.lsn_best=0;
+                       g_hash_table_foreach(
+                                       CaptiveSharedCacheMapObject_hash,       /* hash_table */
+                                       (GHFunc)captive_shared_cache_map_page_write_lsn_foreach,        /* func */
+                                       &param);        /* user_data */
+                       if (!param.lsn_best)
+                               break;
+                       captive_shared_cache_map_page_write(param.captive_shared_cache_map_object_best,param.offset_best);
+                       }
+               }
+
+       captive_shared_cache_map_page_write(captive_shared_cache_map_object,offset);
+}
+
 guint64 captive_shared_cache_map_flush(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
                guint64 start,guint64 end)
 {
@@ -559,8 +716,7 @@ 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;
+       end=MIN(end,CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
 
        start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
        end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
@@ -571,7 +727,7 @@ guint64 now;
                        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);
+               captive_shared_cache_map_page_write_lsn(captive_shared_cache_map_object,now);
                flushed+=PAGE_SIZE;
                }
 
@@ -639,6 +795,7 @@ 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->FileObject!=NULL);
        g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
        g_return_if_fail(param!=NULL);