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