Fixed message typo.
[captive.git] / src / libcaptive / cc / sharedcachemap.c
1 /* $Id$
2  * reactos Cache Manager (Cc*) SharedCacheMap structure of libcaptive
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "sharedcachemap.h"     /* self */
23 #include "sharedcachemap-priv.h"        /* self */
24 #include "io.h"
25 #include "marshallers.h"
26 #include <glib-object.h>
27 #include "privatebcbpin.h"
28 #include "captive/macros.h"
29 #include <sys/mman.h>
30 #include "reactos/ddk/obfuncs.h"
31 #include <stdlib.h>
32
33
34 /* Config: */
35 /* Maximum allocatable (without filling in data) on test: 0xA8000000 */
36 #define TOTAL_MAPPED_MAX 0x30000000                     /* Maximum mapped area; FIXME: Fix Cache Manager. */
37
38 static size_t total_mapped=0;
39
40
41 gboolean captive_shared_cache_map_restart_needed(void)
42 {
43         return total_mapped>=TOTAL_MAPPED_MAX;
44 }
45
46
47 static GHashTable *CaptiveSharedCacheMapObject_hash;
48
49 static void CaptiveSharedCacheMapObject_hash_init(void)
50 {
51         if (CaptiveSharedCacheMapObject_hash)
52                 return;
53         CaptiveSharedCacheMapObject_hash=g_hash_table_new(
54                         g_direct_hash,  /* hash_func */
55                         g_direct_equal);        /* key_equal_func */
56 }
57
58
59 static gpointer captive_shared_cache_map_object_parent_class=NULL;
60
61
62 static void captive_shared_cache_map_object_finalize(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
63 {
64 static const CC_FILE_SIZES FileSizes_zero;
65 gboolean errbool;
66 guint64 offset;
67 CaptiveSharedCacheMapObject_page *page;
68
69         g_return_if_fail(captive_shared_cache_map_object!=NULL);
70
71         for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
72                 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
73                 if (!page->data_valid)
74                         continue;
75                 g_assert(!page->dirty); /* FIXME: Is it allowed by W32? */
76                 }
77
78         CaptiveSharedCacheMapObject_hash_init();
79         errbool=g_hash_table_remove(CaptiveSharedCacheMapObject_hash,captive_shared_cache_map_object);
80         g_assert(errbool==TRUE);
81
82         captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,&FileSizes_zero);
83         g_assert(captive_shared_cache_map_object->buffer==NULL);
84         g_assert(captive_shared_cache_map_object->pages==NULL);
85
86         if (captive_shared_cache_map_object->pin_hash) {
87                 captive_private_bcb_pin_object_hash_destroy(captive_shared_cache_map_object->pin_hash);
88                 captive_shared_cache_map_object->pin_hash=NULL;
89                 }
90         g_assert(captive_shared_cache_map_object->map==NULL);
91         if (captive_shared_cache_map_object->SectionObjectPointer) {
92                 g_assert(captive_shared_cache_map_object==captive_shared_cache_map_object->SectionObjectPointer->SharedCacheMap);
93                 captive_shared_cache_map_object->SectionObjectPointer->SharedCacheMap=NULL;
94                 }
95
96         if (captive_shared_cache_map_object->FileObject) {
97                 /* W32 dereferences twice. */
98                 ObDereferenceObject(captive_shared_cache_map_object->FileObject);
99                 captive_shared_cache_map_object->FileObject=NULL;
100                 }
101
102         G_OBJECT_CLASS(captive_shared_cache_map_object_parent_class)->finalize((GObject *)captive_shared_cache_map_object);
103 }
104
105 static guint FileSizes_changed_signal;
106 static guint purge_signal;
107
108 static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
109                 guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength);
110 static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
111
112 static void captive_shared_cache_map_object_class_init(CaptiveSharedCacheMapObjectClass *class)
113 {
114 GObjectClass *gobject_class=G_OBJECT_CLASS(class);
115
116         captive_shared_cache_map_object_parent_class=g_type_class_ref(g_type_parent(G_TYPE_FROM_CLASS(class)));
117         gobject_class->finalize=(void (*)(GObject *object))captive_shared_cache_map_object_finalize;
118
119         class->FileSizes_changed=captive_shared_cache_map_object_FileSizes_changed;
120         class->purge=captive_shared_cache_map_object_purge;
121
122         FileSizes_changed_signal=g_signal_new("FileSizes_changed",
123                         G_TYPE_FROM_CLASS(gobject_class),
124                         G_SIGNAL_RUN_LAST,
125                         G_STRUCT_OFFSET(CaptiveSharedCacheMapObjectClass,FileSizes_changed),
126                         NULL,NULL,
127                         captive_cc_VOID__UINT64_UINT64_UINT64,
128                         G_TYPE_NONE,3,G_TYPE_UINT64,G_TYPE_UINT64,G_TYPE_UINT64);
129         purge_signal=g_signal_new("purge",
130                         G_TYPE_FROM_CLASS(gobject_class),
131                         G_SIGNAL_RUN_LAST,
132                         G_STRUCT_OFFSET(CaptiveSharedCacheMapObjectClass,purge),
133                         NULL,NULL,
134                         captive_cc_VOID__VOID,
135                         G_TYPE_NONE,0);
136 }
137
138
139 static void captive_shared_cache_map_object_init(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
140 {
141         captive_shared_cache_map_object->pin_hash=captive_private_bcb_pin_object_hash_new();
142
143         captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
144         g_object_unref(captive_shared_cache_map_object);        /* ==sink */
145
146         CaptiveSharedCacheMapObject_hash_init();
147         g_hash_table_insert(CaptiveSharedCacheMapObject_hash,
148                         captive_shared_cache_map_object,captive_shared_cache_map_object);
149 }
150
151
152 GType captive_shared_cache_map_object_get_type(void)
153 {
154 static GType captive_shared_cache_map_object_type=0;
155
156         if (!captive_shared_cache_map_object_type) {
157 static const GTypeInfo captive_shared_cache_map_object_info={
158                                 sizeof(CaptiveSharedCacheMapObjectClass),
159                                 NULL,   /* base_init */
160                                 NULL,   /* base_finalize */
161                                 (GClassInitFunc)captive_shared_cache_map_object_class_init,
162                                 NULL,   /* class_finalize */
163                                 NULL,   /* class_data */
164                                 sizeof(CaptiveSharedCacheMapObject),
165                                 5,      /* n_preallocs */
166                                 (GInstanceInitFunc)captive_shared_cache_map_object_init,
167                                 };
168
169                 captive_shared_cache_map_object_type=g_type_register_static(G_TYPE_OBJECT,
170                                 "CaptiveSharedCacheMapObject",&captive_shared_cache_map_object_info,0);
171                 }
172
173         return captive_shared_cache_map_object_type;
174 }
175
176 static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
177                 guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength)
178 {
179 size_t size_old,size_new;
180 guint64 size64_old,size64_new;
181
182         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
183
184         g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
185         g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
186
187         size64_old=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);       
188         size_old=size64_old;
189         g_assert(size_old==size64_old);
190         size64_new=CAPTIVE_ROUND_UP64(AllocationSize,PAGE_SIZE);        
191         size_new=size64_new;
192         if (size_new!=size64_new) {
193 size_new_big:
194                 g_error("Mapped size %" G_GUINT64_FORMAT " is too big; FIXME: non-mmap(2)-able areas not yet supported",size64_new);
195                 g_assert_not_reached();
196                 }
197
198         if (size_old!=size_new) {
199                 /* ntfs.sys of NT-5.1sp1 may extend StreamFileObject while dirty pins exist.
200                  * How to extend SharedCacheMap size without changing the memory location?
201                  * I hope ntfs.sys does not expect long-term absolute position of its
202                  * StreamFileObject:
203                  */
204                 if (!(captive_shared_cache_map_object->FileObject->Flags&FO_STREAM_FILE)) {
205                         /* These two assertions should be already catched by pin/map signal handlers. */
206                         g_assert(!captive_shared_cache_map_object->map);
207                         g_assert(!g_hash_table_size(captive_shared_cache_map_object->pin_hash));
208                         }
209                 }
210
211         if (!size_new || size_new > captive_shared_cache_map_object->alloc_length) {
212 size_t alloc_new;
213 guint64 alloc64_new;
214 gpointer buffer_new;
215
216                 alloc64_new=CAPTIVE_ROUND_UP64((!size64_new ? 0 : MAX(size64_new*2,0x10000)),PAGE_SIZE);
217                 alloc_new=alloc64_new;
218                 if (alloc_new!=alloc64_new)
219                         goto size_new_big;
220
221                 if (!alloc_new)
222                         buffer_new=NULL;
223                 else {
224 gpointer base;
225 int errint;
226
227                         base=mmap(
228                                         NULL,   /* start */
229                                         PAGE_SIZE+alloc_new+PAGE_SIZE,  /* length; leading and trailing boundary check pages */
230                                         PROT_READ|PROT_WRITE,   /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
231                                         MAP_PRIVATE|MAP_ANONYMOUS       /* flags */
232                                                         |MAP_NORESERVE, /* At least ext2fsd maps the whole disk. */
233                                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
234                                         0);     /* offset; ignored due to MAP_ANONYMOUS */
235                         if (base==MAP_FAILED)
236                                 goto size_new_big;
237                         g_assert(base!=NULL);
238                         total_mapped+=alloc_new;
239
240                         base+=PAGE_SIZE;
241                         errint=munmap(base-PAGE_SIZE,PAGE_SIZE);        /* unmap leading boundary check page */
242                         g_assert(errint==0);
243                         errint=munmap(base+alloc_new,PAGE_SIZE);        /* unmap trailing boundary check page */
244                         g_assert(errint==0);
245
246                         buffer_new=base;
247                         }
248
249                 memcpy(buffer_new,captive_shared_cache_map_object->buffer,
250                                 MIN(AllocationSize,captive_shared_cache_map_object->AllocationSize));
251
252                 if (captive_shared_cache_map_object->alloc_length) {
253 int errint;
254
255                         errint=munmap(captive_shared_cache_map_object->buffer,captive_shared_cache_map_object->alloc_length);
256                         g_assert(errint==0);
257                         total_mapped-=captive_shared_cache_map_object->alloc_length;
258                         }
259
260                 captive_shared_cache_map_object->buffer=buffer_new;
261
262 #if 0   /* It appears it is valid to squeeze out 'dirty' blocks. FIXME: Flush them? */
263                 /* FIXME: The code may be no longer valid with the 'alloc_new' introduction! */
264                 if (size_old>size_new) {
265 guint64 now;
266
267                         for (now=size_new;now<size_old;now+=PAGE_SIZE) {
268                                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
269                                         continue;
270                                 g_assert(!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty);
271                                 }
272                         }
273 #endif
274
275                 captive_shared_cache_map_object->pages=g_realloc(captive_shared_cache_map_object->pages,
276                                 alloc_new/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
277
278                 captive_shared_cache_map_object->alloc_length=alloc_new;
279                 }
280
281         if (size_new>size_old)  /* prevent 'size_new-size_old' as it is unsigned! */
282                 memset(captive_shared_cache_map_object->pages+(size_old/PAGE_SIZE),0,
283                                 (size_new-size_old)/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
284
285         captive_shared_cache_map_object->AllocationSize=AllocationSize;
286         captive_shared_cache_map_object->FileSize=FileSize;
287         captive_shared_cache_map_object->ValidDataLength=ValidDataLength;
288
289         g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
290         g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
291 }
292
293 static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
294 {
295         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
296
297         /* NOP; just to provide slot for checking by Bcbs */
298 }
299
300 CaptiveSharedCacheMapObject *captive_shared_cache_map_get_ref(FILE_OBJECT *FileObject,
301                 const CC_FILE_SIZES *FileSizes,BOOLEAN PinAccess,const CACHE_MANAGER_CALLBACKS *CallBacks,VOID *LazyWriterContext)
302 {
303 CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
304
305         g_return_val_if_fail(FileObject!=NULL,NULL);
306         g_return_val_if_fail(FileObject->SectionObjectPointer!=NULL,NULL);
307         g_return_val_if_fail(FileSizes!=NULL,NULL);
308         g_return_val_if_fail(CallBacks!=NULL,NULL);
309
310         if ((captive_shared_cache_map_object=FileObject->SectionObjectPointer->SharedCacheMap)) {
311                 captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
312                 }
313         else {
314                 captive_shared_cache_map_object=g_object_new(
315                                 CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT,   /* object_type */
316                                 NULL);  /* first_property_name; FIXME: support properties */
317
318                 /* FIXME: When to drop SharedCacheMap?
319                  * Currently we never close it.
320                  * Fix also CcZeroData() workaround.
321                  */
322                 g_object_ref(captive_shared_cache_map_object);
323
324                 /* W32 references twice. */
325                 ObReferenceObject(FileObject);
326                 captive_shared_cache_map_object->FileObject=FileObject;
327                 captive_shared_cache_map_object->SectionObjectPointer=FileObject->SectionObjectPointer;
328                 captive_shared_cache_map_object->AllocationSize=0;
329                 captive_shared_cache_map_object->FileSize=0;
330                 captive_shared_cache_map_object->ValidDataLength=0;
331                 captive_shared_cache_map_object->PinAccess=PinAccess;
332                 captive_shared_cache_map_object->CallBacks=*CallBacks;
333                 captive_shared_cache_map_object->LazyWriterContext=LazyWriterContext;
334
335                 FileObject->SectionObjectPointer->SharedCacheMap=captive_shared_cache_map_object;
336                 }
337
338         g_assert(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
339         /* FileObject may differ - we can have a different reference to the same FCB. */
340         g_assert(FileObject->SectionObjectPointer==captive_shared_cache_map_object->SectionObjectPointer);
341         g_assert(PinAccess==captive_shared_cache_map_object->PinAccess);
342         g_assert(CallBacks->AcquireForLazyWrite==captive_shared_cache_map_object->CallBacks.AcquireForLazyWrite);
343         g_assert(CallBacks->ReleaseFromLazyWrite==captive_shared_cache_map_object->CallBacks.ReleaseFromLazyWrite);
344         g_assert(CallBacks->AcquireForReadAhead==captive_shared_cache_map_object->CallBacks.AcquireForReadAhead);
345         g_assert(CallBacks->ReleaseFromReadAhead==captive_shared_cache_map_object->CallBacks.ReleaseFromReadAhead);
346         g_assert(LazyWriterContext==captive_shared_cache_map_object->LazyWriterContext);
347
348         captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,FileSizes);
349
350         return captive_shared_cache_map_object;
351 }
352
353 void captive_shared_cache_map_FileSizes_set(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
354                 const CC_FILE_SIZES *FileSizes)
355 {
356 guint64 AllocationSize,FileSize,ValidDataLength;
357
358         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
359         g_return_if_fail(FileSizes!=NULL);
360
361         AllocationSize=FileSizes->AllocationSize.QuadPart;
362         FileSize=FileSizes->FileSize.QuadPart;
363         ValidDataLength=FileSizes->ValidDataLength.QuadPart;
364
365         /* Do not: if (ValidDataLength==G_MAXINT64)
366          *              ValidDataLength=FileSize;
367          * In some cases (during NTFS mount) there may be also invalid 'ValidDataLength' at all:
368          *      CcSetFileSizes(AllocationSize=0x1000000,FileSize=0xf80208,ValidDataLength=0x23b801a0)
369          */
370         ValidDataLength=FileSize;
371
372         g_assert(AllocationSize>=0);
373         g_assert(FileSize>=0);
374         g_assert(ValidDataLength>=0);
375
376         g_assert(ValidDataLength<=FileSize);
377         /* Do not: g_assert(0==(AllocationSize%0x200));
378          * as it is true for ntfs.sys of NT-5.1sp1 but it fails for fastfat.sys of NT-5.1sp1.
379          */
380         /* AllocationSize can be much higher: */
381         g_assert(FileSize<=AllocationSize);
382
383         /* Prevent signalling if not needed. */
384         if (0
385                         || captive_shared_cache_map_object->AllocationSize!=AllocationSize
386                         || captive_shared_cache_map_object->FileSize!=FileSize
387                         || captive_shared_cache_map_object->ValidDataLength!=ValidDataLength) {
388                 /* Signalling is forbidden in captive_shared_cache_map_object_finalize(). */
389                 if (G_OBJECT(captive_shared_cache_map_object)->ref_count==0) {
390                         (*CAPTIVE_SHARED_CACHE_MAP_OBJECT_GET_CLASS(captive_shared_cache_map_object)->FileSizes_changed)
391                                         (captive_shared_cache_map_object,AllocationSize,FileSize,ValidDataLength);
392                         }
393                 else {
394                                 g_signal_emit(captive_shared_cache_map_object,FileSizes_changed_signal,0,
395                                                 AllocationSize,FileSize,ValidDataLength);
396                         }
397                 }
398
399         g_assert(captive_shared_cache_map_object->AllocationSize==AllocationSize);
400         g_assert(captive_shared_cache_map_object->FileSize==FileSize);
401         g_assert(captive_shared_cache_map_object->ValidDataLength==ValidDataLength);
402 }
403
404 CaptiveSharedCacheMapObject *captive_SectionObjectPointers_to_SharedCacheMap(SECTION_OBJECT_POINTERS *SectionObjectPointers)
405 {
406         g_return_val_if_fail(SectionObjectPointers!=NULL,NULL);
407         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(SectionObjectPointers->SharedCacheMap),NULL);
408
409         return SectionObjectPointers->SharedCacheMap;
410 }
411
412 CaptiveSharedCacheMapObject *captive_FileObject_to_SharedCacheMap(FILE_OBJECT *FileObject)
413 {
414         g_return_val_if_fail(FileObject!=NULL,NULL);
415
416         return captive_SectionObjectPointers_to_SharedCacheMap(FileObject->SectionObjectPointer);
417 }
418
419 void captive_shared_cache_map_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
420 {
421         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
422
423         g_object_ref(captive_shared_cache_map_object);
424         captive_shared_cache_map_object->w32_ref_count++;
425 }
426
427 void captive_shared_cache_map_w32_unref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
428 {
429         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
430         g_return_if_fail(G_OBJECT(captive_shared_cache_map_object)->ref_count>0);
431
432         captive_shared_cache_map_object->w32_ref_count--;
433         g_object_unref(captive_shared_cache_map_object);
434 }
435
436 gint captive_shared_cache_map_query_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
437 {
438         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),0);
439
440         return captive_shared_cache_map_object->w32_ref_count;
441 }
442
443 void captive_shared_cache_map_data_validate_read(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
444                 FILE_OBJECT *FileObject,guint64 start,guint64 end)
445 {
446 guint64 now;
447 gboolean after_eof=FALSE;       /* Did we reached the end of file already? */
448
449         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
450         g_return_if_fail(captive_shared_cache_map_object==captive_FileObject_to_SharedCacheMap(FileObject));
451         g_return_if_fail(start<=end);
452         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
453
454         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
455         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
456
457         for (now=start;now<end;now+=PAGE_SIZE) {
458 LARGE_INTEGER now_LargeInteger;
459 ULONG got;
460
461                 if (captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
462                         continue;
463                 now_LargeInteger.QuadPart=now;
464                 got=captive_Cc_IoPageRead(FileObject,captive_shared_cache_map_object->buffer+now,PAGE_SIZE,&now_LargeInteger);
465                 if (after_eof)
466                         g_assert(got==0);
467                 else
468                         g_assert(got<=PAGE_SIZE);
469                 after_eof=(got<PAGE_SIZE);
470                 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
471                 captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty=FALSE;
472                 }
473 }
474
475 void captive_shared_cache_map_data_validate_noread(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
476                 guint64 start,guint64 end)
477 {
478 guint64 now;
479
480         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
481         g_return_if_fail(start<=end);
482         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
483
484         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
485         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
486
487         for (now=start;now<end;now+=PAGE_SIZE) {
488                 g_assert(captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid);
489                 }
490 }
491
492 void captive_shared_cache_map_set_data_valid(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
493                 guint64 start,guint64 end)
494 {
495 guint64 now;
496
497         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
498         g_return_if_fail(start<=end);
499         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
500
501         start=CAPTIVE_ROUND_UP64(start,PAGE_SIZE);
502         if (end<captive_shared_cache_map_object->FileSize)
503                 end=CAPTIVE_ROUND_DOWN64(end,PAGE_SIZE);
504         else {
505                 /* We can validate the last page of the file
506                  * even if it does not end on PAGE_SIZE boundary.
507                  */
508                 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
509                 }
510         /* We may get end<start here - it is valid to not to validate anything. */
511
512         for (now=start;now<end;now+=PAGE_SIZE) {
513                 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
514                 /* .dirty is undefined */
515                 }
516 }
517
518 void captive_shared_cache_map_set_data_invalid(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
519                 guint64 start,guint64 end)
520 {
521 guint64 now;
522
523         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
524         g_return_if_fail(start<=end);
525         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
526
527         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
528         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
529
530         for (now=start;now<end;now+=PAGE_SIZE) {
531                 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=FALSE;
532                 }
533 }
534
535 void captive_shared_cache_map_set_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
536                 guint64 start,guint64 end)
537 {
538 guint64 now;
539 CaptiveSharedCacheMapObject_page *page;
540
541         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
542         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
543
544         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
545         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
546
547         for (now=start;now<end;now+=PAGE_SIZE) {
548                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
549                 g_assert(page->data_valid);
550                 if (!page->dirty) {
551                         page->dirty=TRUE;
552                         page->lsn_oldest=0;
553                         page->lsn_newest=0;
554                         }
555                 }
556 }
557
558 typedef struct _captive_shared_cache_map_memory_range_set_dirty_param
559                 captive_shared_cache_map_memory_range_set_dirty_param;
560 struct _captive_shared_cache_map_memory_range_set_dirty_param {
561         gpointer address_start;
562         gpointer address_end;
563         guint64 bytes_set;
564         };
565
566 static void captive_shared_cache_map_memory_range_set_dirty_foreach(
567                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
568                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
569                 captive_shared_cache_map_memory_range_set_dirty_param *param)   /* user_data */
570 {
571 gpointer address_start_local,address_end_local;
572
573         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
574         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
575         g_return_if_fail(param!=NULL);
576
577         address_start_local=MAX(param->address_start,captive_shared_cache_map_object->buffer);
578         address_end_local=MIN(param->address_end,captive_shared_cache_map_object->buffer+captive_shared_cache_map_object->AllocationSize);
579
580         if (address_start_local>address_end_local)
581                 return;
582
583         captive_shared_cache_map_set_dirty(captive_shared_cache_map_object,
584                         address_start_local-captive_shared_cache_map_object->buffer,    /* start */
585                         address_end_local  -captive_shared_cache_map_object->buffer);   /* end */
586
587         param->bytes_set+=(address_end_local-address_start_local);
588 }
589
590 guint64 captive_shared_cache_map_memory_range_set_dirty(gpointer address_start,gpointer address_end)
591 {
592 captive_shared_cache_map_memory_range_set_dirty_param set_dirty_param;
593
594         g_return_val_if_fail(address_start!=NULL,0);
595         g_return_val_if_fail(address_end!=NULL,0);
596         g_return_val_if_fail(address_start<=address_end,0);
597
598         address_start=CAPTIVE_ROUND_DOWN(address_start,PAGE_SIZE);
599         address_end=CAPTIVE_ROUND_UP(address_end,PAGE_SIZE);
600
601         CaptiveSharedCacheMapObject_hash_init();
602         set_dirty_param.address_start=address_start;
603         set_dirty_param.address_end=address_end;
604         set_dirty_param.bytes_set=0;
605         g_hash_table_foreach(
606                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
607                         (GHFunc)captive_shared_cache_map_memory_range_set_dirty_foreach,        /* func */
608                         &set_dirty_param);      /* user_data */
609
610         return set_dirty_param.bytes_set;
611 }
612
613 gboolean captive_shared_cache_map_is_page_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
614                 guint64 offset)
615 {
616 CaptiveSharedCacheMapObject_page *page;
617
618         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),FALSE);
619         g_return_val_if_fail(offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE),FALSE);
620         g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE),FALSE);
621         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
622         g_return_val_if_fail(page->data_valid,FALSE);
623
624         return page->dirty;
625 }
626
627 void captive_shared_cache_map_page_set_lsn(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
628                 guint64 offset,gint64 lsn)
629 {
630 CaptiveSharedCacheMapObject_page *page;
631
632         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
633         g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
634         g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
635         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
636         g_return_if_fail(page->data_valid);
637         g_return_if_fail(page->dirty);
638         g_return_if_fail(page->lsn_oldest<=page->lsn_newest);
639         g_return_if_fail(!page->lsn_newest || lsn>=page->lsn_newest);
640         g_return_if_fail(captive_shared_cache_map_object->LogHandle_set);
641         g_return_if_fail(captive_shared_cache_map_object->FlushToLsnRoutine_set);
642
643         if (!page->lsn_oldest)
644                 page->lsn_oldest=lsn;
645         page->lsn_newest=lsn;
646 }
647
648 void captive_shared_cache_map_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
649 {
650 guint64 offset;
651 CaptiveSharedCacheMapObject_page *page;
652
653         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
654
655         g_signal_emit(captive_shared_cache_map_object,purge_signal,0);
656
657         /* Needed by fastfat.sys of NT-5.1sp1 during FAT32 unmount
658          * otherwise it fails on: g_assert(!page->dirty);
659          * It corrupts the disk if the buffer is dropped instead.
660          */
661         captive_shared_cache_map_flush(captive_shared_cache_map_object,
662                         0,CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
663
664         for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
665                 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
666                 if (!page->data_valid)
667                         continue;
668                 g_assert(!page->dirty);
669                 page->data_valid=FALSE;
670                 }
671 }
672
673 static VOID *captive_LogHandle;
674 static PFLUSH_TO_LSN captive_FlushToLsnRoutine;
675
676 void captive_shared_cache_map_set_LogHandle(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,VOID *LogHandle)
677 {
678         g_return_if_fail(!captive_shared_cache_map_object || CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
679         /* FIXME: 'captive_shared_cache_map_object->LogHandle_set' may be set.
680          * Does it mean 'LogHandle_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
681          */
682
683         /* Do not: if (!LogHandle)
684          *              return;
685          * See CcGetDirtyPages()/"W32 undocumented: What does mean LogHandle==NULL?"
686          */
687         g_assert(!captive_LogHandle || !LogHandle || captive_LogHandle==LogHandle);
688         captive_LogHandle=LogHandle;
689         if (captive_shared_cache_map_object)
690                 captive_shared_cache_map_object->LogHandle_set=!!LogHandle;
691 }
692
693 void captive_shared_cache_map_set_FlushToLsnRoutine
694                 (CaptiveSharedCacheMapObject *captive_shared_cache_map_object,PFLUSH_TO_LSN FlushToLsnRoutine)
695 {
696         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
697         /* FIXME: 'captive_shared_cache_map_object->FlushToLsnRoutine_set' may be set.
698          * Does it mean 'FlushToLsnRoutine_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
699          */
700
701         if (!FlushToLsnRoutine)
702                 return;
703         g_assert(!captive_FlushToLsnRoutine || captive_FlushToLsnRoutine==FlushToLsnRoutine);
704         captive_FlushToLsnRoutine=FlushToLsnRoutine;
705         if (FlushToLsnRoutine)
706                 captive_shared_cache_map_object->FlushToLsnRoutine_set=TRUE;
707 }
708
709 static void captive_shared_cache_map_page_write(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
710                 guint64 offset)
711 {
712 LARGE_INTEGER offset_LargeInteger;
713 static gint64 lsn_last;
714 CaptiveSharedCacheMapObject_page *page;
715
716         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
717         g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
718         g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
719         g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
720         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
721         g_return_if_fail(page->data_valid);
722         g_return_if_fail(page->dirty);
723
724         if (page->lsn_newest) {
725                 /* sanity check */
726                 g_assert(!lsn_last || lsn_last<=page->lsn_newest);
727                 lsn_last=page->lsn_newest;
728
729                 captive_stdcall_call_12((CaptiveStdCallFunc12)captive_FlushToLsnRoutine,
730                                 captive_LogHandle,
731                                 (gpointer)(guint32)(page->lsn_newest>> 0U),     /* 'LARGE_INTEGER' argument */
732                                 (gpointer)(guint32)(page->lsn_newest>>32U));
733                 }
734
735         offset_LargeInteger.QuadPart=offset;
736         captive_Cc_IoPageWrite(captive_shared_cache_map_object->FileObject,
737                         captive_shared_cache_map_object->buffer+offset,PAGE_SIZE,&offset_LargeInteger);
738
739         page->dirty=FALSE;
740         page->lsn_oldest=0;
741         page->lsn_newest=0;
742 }
743
744 typedef struct _captive_shared_cache_map_flush_lsn_sort captive_shared_cache_map_flush_lsn_sort;
745 struct _captive_shared_cache_map_flush_lsn_sort {
746         gint64 lsn;
747         CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
748         guint64 offset;
749         };
750
751 typedef struct _captive_shared_cache_map_flush_lsn_pages_foreach_param
752                 captive_shared_cache_map_flush_lsn_pages_foreach_param;
753 struct _captive_shared_cache_map_flush_lsn_pages_foreach_param {
754         gint64 lsn_target;
755         guint lsn_pages_count;
756         captive_shared_cache_map_flush_lsn_sort *lsn_pages_pointer;     /* Not filled in if NULL */
757         };
758
759 static void captive_shared_cache_map_flush_lsn_pages_foreach(
760                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
761                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
762                 captive_shared_cache_map_flush_lsn_pages_foreach_param *param)  /* user_data */
763 {
764 guint64 now;
765 CaptiveSharedCacheMapObject_page *page;
766
767 #if 0   /* acceleration */
768         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
769         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
770         g_return_if_fail(param!=NULL);
771 #endif
772
773         for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
774                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
775                 if (!page->data_valid)
776                         continue;
777                 if (!page->dirty)
778                         continue;
779                 if (!page->lsn_newest)
780                         continue;
781                 if (page->lsn_newest>param->lsn_target)
782                         continue;
783                 param->lsn_pages_count++;
784                 if (!param->lsn_pages_pointer)
785                         continue;
786                 param->lsn_pages_pointer->lsn=page->lsn_newest;
787                 param->lsn_pages_pointer->captive_shared_cache_map_object=captive_shared_cache_map_object;
788                 param->lsn_pages_pointer->offset=now;
789                 param->lsn_pages_pointer++;
790                 }
791 }
792
793 static int captive_shared_cache_map_flush_lsn_pages_compar
794                 (const captive_shared_cache_map_flush_lsn_sort *a,const captive_shared_cache_map_flush_lsn_sort *b)
795 {
796 #if 0   /* acceleration */
797         g_return_val_if_fail(a!=NULL,0);
798         g_return_val_if_fail(b!=NULL,0);
799 #endif
800
801         return (a->lsn>b->lsn)-(b->lsn>a->lsn);
802 }
803
804 guint64 captive_shared_cache_map_flush(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
805                 guint64 start,guint64 end)
806 {
807 guint64 flushed;
808 guint64 now;
809 gint64 lsn_target;
810 captive_shared_cache_map_flush_lsn_pages_foreach_param lsn_pages_foreach_param;
811 captive_shared_cache_map_flush_lsn_sort *lsn_pages_pointer;
812 const captive_shared_cache_map_flush_lsn_sort *lsn_page;
813 guint lsn_pages_count;
814
815         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),0);
816         g_return_val_if_fail(start<=end,0);
817
818         end=MIN(end,CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
819
820         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
821         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
822
823         lsn_target=0;
824         for (now=start;now<end;now+=PAGE_SIZE) {
825 CaptiveSharedCacheMapObject_page *page;
826
827                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
828                 if (!page->data_valid)
829                         continue;
830                 if (!page->dirty)
831                         continue;
832                 if (!page->lsn_newest)
833                         continue;
834                 if (!lsn_target || lsn_target<page->lsn_newest)
835                         lsn_target=page->lsn_newest;
836                 }
837
838         CaptiveSharedCacheMapObject_hash_init();
839
840         lsn_pages_foreach_param.lsn_target=lsn_target;
841         lsn_pages_foreach_param.lsn_pages_count=0;
842         lsn_pages_foreach_param.lsn_pages_pointer=NULL; /* Not yet filling */
843         g_hash_table_foreach(
844                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
845                         (GHFunc)captive_shared_cache_map_flush_lsn_pages_foreach,       /* func */
846                         &lsn_pages_foreach_param);      /* user_data */
847
848         lsn_pages_count=lsn_pages_foreach_param.lsn_pages_count;
849         captive_newn(lsn_pages_pointer,lsn_pages_count);
850         g_assert(lsn_pages_foreach_param.lsn_target==lsn_target);
851         lsn_pages_foreach_param.lsn_pages_count=0;
852         lsn_pages_foreach_param.lsn_pages_pointer=lsn_pages_pointer;
853         g_hash_table_foreach(
854                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
855                         (GHFunc)captive_shared_cache_map_flush_lsn_pages_foreach,       /* func */
856                         &lsn_pages_foreach_param);      /* user_data */
857
858         g_assert(lsn_pages_foreach_param.lsn_target==lsn_target);
859         g_assert(lsn_pages_foreach_param.lsn_pages_count==lsn_pages_count);
860         g_assert(lsn_pages_foreach_param.lsn_pages_pointer==lsn_pages_pointer+lsn_pages_count);
861
862         qsort(lsn_pages_pointer,lsn_pages_count,sizeof(*lsn_pages_pointer),
863                         (int (*)(const void *,const void *))captive_shared_cache_map_flush_lsn_pages_compar);
864
865         flushed=0;
866
867         for (lsn_page=lsn_pages_pointer;lsn_page<lsn_pages_pointer+lsn_pages_count;lsn_page++) {
868                 captive_shared_cache_map_page_write(lsn_page->captive_shared_cache_map_object,lsn_page->offset);
869                 if (lsn_page->captive_shared_cache_map_object==captive_shared_cache_map_object
870                                 && lsn_page->offset>=start && lsn_page->offset<end)
871                 flushed+=PAGE_SIZE;
872                 }
873
874         g_free(lsn_pages_pointer);
875
876         for (now=start;now<end;now+=PAGE_SIZE) {
877 CaptiveSharedCacheMapObject_page *page;
878
879                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
880                 if (!page->data_valid)
881                         continue;
882                 if (!page->dirty)
883                         continue;
884                 captive_shared_cache_map_page_write(captive_shared_cache_map_object,now);
885                 flushed+=PAGE_SIZE;
886                 }
887
888         /* We were calling W32 code - recheck our task completion. */
889         for (now=start;now<end;now+=PAGE_SIZE) {
890 CaptiveSharedCacheMapObject_page *page;
891
892                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
893                 if (!page->data_valid)
894                         continue;
895                 g_assert(!page->dirty);
896                 }
897
898         return flushed;
899 }
900
901 static void captive_shared_cache_map_flush_all_foreach_flush(
902                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
903                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
904                 gboolean user_data)     /* unused */
905 {
906         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
907         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
908
909         captive_shared_cache_map_flush(captive_shared_cache_map_object,0,G_MAXUINT64-1);        /* '-1' for overflow safety */
910 }
911
912 static void captive_shared_cache_map_flush_all_foreach_check(
913                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
914                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
915                 gboolean user_data)     /* unused */
916 {
917 guint64 start,end;
918 guint64 now;
919
920         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
921         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
922
923         start=0;
924         end=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);
925
926         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
927         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
928
929         /* We were calling W32 code - recheck our task completion. */
930         for (now=start;now<end;now+=PAGE_SIZE) {
931 CaptiveSharedCacheMapObject_page *page;
932
933                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
934                 if (!page->data_valid)
935                         continue;
936                 g_assert(!page->dirty);
937                 }
938 }
939
940 void captive_shared_cache_map_flush_all(void)
941 {
942         CaptiveSharedCacheMapObject_hash_init();
943         g_hash_table_foreach(
944                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
945                         (GHFunc)captive_shared_cache_map_flush_all_foreach_flush,       /* func */
946                         NULL);  /* user_data; unused */
947         g_hash_table_foreach(
948                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
949                         (GHFunc)captive_shared_cache_map_flush_all_foreach_check,       /* func */
950                         NULL);  /* user_data; unused */
951 }
952
953 static void captive_shared_cache_map_is_any_dirty_foreach(
954                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
955                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
956                 gboolean *dirty_foundp) /* user_data */
957 {
958 guint64 now;
959
960         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
961         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
962         g_return_if_fail(dirty_foundp!=NULL);
963
964         for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
965 CaptiveSharedCacheMapObject_page *page;
966
967                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
968                 if (!page->data_valid)
969                         continue;
970                 if (!page->dirty)
971                         continue;
972                 *dirty_foundp=TRUE;     /* FIXME: stop the traversal. */
973                 break;
974                 }
975 }
976
977
978 gboolean captive_shared_cache_map_is_any_dirty(void)
979 {
980 gboolean dirty_found;
981
982         CaptiveSharedCacheMapObject_hash_init();
983         dirty_found=FALSE;
984         g_hash_table_foreach(
985                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
986                         (GHFunc)captive_shared_cache_map_is_any_dirty_foreach,  /* func */
987                         &dirty_found);  /* user_data */
988
989         return dirty_found;
990 }
991
992
993 typedef struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param captive_shared_cache_map_CcGetDirtyPages_foreach_param;
994 struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param {
995         PDIRTY_PAGE_ROUTINE DirtyPageRoutine;
996         VOID *Context1;
997         VOID *Context2;
998         gint64 result;
999         };
1000
1001 static void captive_shared_cache_map_CcGetDirtyPages_foreach(
1002                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
1003                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
1004                 captive_shared_cache_map_CcGetDirtyPages_foreach_param *param)  /* user_data */
1005 {
1006 gint64 now;
1007 CaptiveSharedCacheMapObject_page *page;
1008
1009         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
1010         g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
1011         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
1012         g_return_if_fail(param!=NULL);
1013
1014         for (now=CAPTIVE_ROUND_DOWN64(captive_shared_cache_map_object->AllocationSize-1,PAGE_SIZE);now>=0;now-=PAGE_SIZE) {
1015 LARGE_INTEGER now_LargeInteger,lsn_oldest_LargeInteger,lsn_newest_LargeInteger;
1016
1017                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
1018                 if (!page->data_valid)
1019                         continue;
1020                 if (!page->dirty)
1021                         continue;
1022                 if (page->lsn_oldest && (!param->result || param->result>page->lsn_oldest))
1023                         param->result=page->lsn_oldest;
1024
1025                 now_LargeInteger.QuadPart=now;
1026                 lsn_oldest_LargeInteger.QuadPart=page->lsn_oldest;
1027                 lsn_newest_LargeInteger.QuadPart=page->lsn_newest;
1028                 (*param->DirtyPageRoutine)(
1029                                 captive_shared_cache_map_object->FileObject,    /* FileObject */
1030                                 &now_LargeInteger,      /* FileOffset */
1031                                 PAGE_SIZE,      /* Length */
1032                                 &lsn_oldest_LargeInteger,       /* OldestLsn */
1033                                 &lsn_newest_LargeInteger,       /* NewestLsn */
1034                                 param->Context1,        /* Context1 */
1035                                 param->Context2);       /* Context2 */
1036                 }
1037 }
1038
1039 gint64 captive_shared_cache_map_CcGetDirtyPages(PDIRTY_PAGE_ROUTINE DirtyPageRoutine,VOID *Context1,VOID *Context2)
1040 {
1041 captive_shared_cache_map_CcGetDirtyPages_foreach_param param;
1042
1043         g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
1044
1045         param.DirtyPageRoutine=DirtyPageRoutine;
1046         param.Context1=Context1;
1047         param.Context2=Context2;
1048         param.result=0;
1049
1050         CaptiveSharedCacheMapObject_hash_init();
1051         g_hash_table_foreach(
1052                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
1053                         (GHFunc)captive_shared_cache_map_CcGetDirtyPages_foreach,       /* func */
1054                         &param);        /* user_data */
1055
1056         return param.result;
1057 }
1058
1059 gpointer captive_shared_cache_map_get_buffer(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
1060 {
1061         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
1062         g_return_val_if_fail(captive_shared_cache_map_object->buffer!=NULL,NULL);
1063
1064         return captive_shared_cache_map_object->buffer;
1065 }
1066
1067 static void captive_cc_FileObject_delete_foreach(
1068                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
1069                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
1070                 FILE_OBJECT *FileObject)        /* user_data */
1071 {
1072         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
1073         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
1074         g_return_if_fail(FileObject!=NULL);
1075
1076         g_assert(FileObject!=captive_shared_cache_map_object->FileObject);
1077 }
1078
1079 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
1080 {
1081         g_return_val_if_fail(FileObject!=NULL,FALSE);
1082
1083         CaptiveSharedCacheMapObject_hash_init();
1084         g_hash_table_foreach(
1085                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
1086                         (GHFunc)captive_cc_FileObject_delete_foreach,   /* func */
1087                         FileObject);    /* user_data */
1088
1089         return FALSE;   /* FIXME: remove useless return code. */
1090 }