captive_shared_cache_map_set_data_valid() validates the subset of the range.
[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
32
33 static GHashTable *CaptiveSharedCacheMapObject_hash;
34
35 static void CaptiveSharedCacheMapObject_hash_init(void)
36 {
37         if (CaptiveSharedCacheMapObject_hash)
38                 return;
39         CaptiveSharedCacheMapObject_hash=g_hash_table_new(
40                         g_direct_hash,  /* hash_func */
41                         g_direct_equal);        /* key_equal_func */
42 }
43
44
45 static gpointer captive_shared_cache_map_object_parent_class=NULL;
46
47
48 static void captive_shared_cache_map_object_finalize(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
49 {
50 static const CC_FILE_SIZES FileSizes_zero;
51 gboolean errbool;
52 guint64 offset;
53 CaptiveSharedCacheMapObject_page *page;
54
55         g_return_if_fail(captive_shared_cache_map_object!=NULL);
56
57         for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
58                 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
59                 if (!page->data_valid)
60                         continue;
61                 g_assert(!page->dirty); /* FIXME: Is it allowed by W32? */
62                 }
63
64         CaptiveSharedCacheMapObject_hash_init();
65         errbool=g_hash_table_remove(CaptiveSharedCacheMapObject_hash,captive_shared_cache_map_object);
66         g_assert(errbool==TRUE);
67
68         captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,&FileSizes_zero);
69         g_assert(captive_shared_cache_map_object->buffer==NULL);
70         g_assert(captive_shared_cache_map_object->pages==NULL);
71
72         if (captive_shared_cache_map_object->pin_hash) {
73                 captive_private_bcb_pin_object_hash_destroy(captive_shared_cache_map_object->pin_hash);
74                 captive_shared_cache_map_object->pin_hash=NULL;
75                 }
76         g_assert(captive_shared_cache_map_object->map==NULL);
77         if (captive_shared_cache_map_object->SectionObjectPointers) {
78                 g_assert(captive_shared_cache_map_object==captive_shared_cache_map_object->SectionObjectPointers->SharedCacheMap);
79                 captive_shared_cache_map_object->SectionObjectPointers->SharedCacheMap=NULL;
80                 }
81
82         if (captive_shared_cache_map_object->FileObject) {
83                 /* W32 dereferences twice. */
84                 ObDereferenceObject(captive_shared_cache_map_object->FileObject);
85                 captive_shared_cache_map_object->FileObject=NULL;
86                 }
87
88         G_OBJECT_CLASS(captive_shared_cache_map_object_parent_class)->finalize((GObject *)captive_shared_cache_map_object);
89 }
90
91 static guint FileSizes_changed_signal;
92 static guint purge_signal;
93
94 static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
95                 guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength);
96 static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
97
98 static void captive_shared_cache_map_object_class_init(CaptiveSharedCacheMapObjectClass *class)
99 {
100 GObjectClass *gobject_class=G_OBJECT_CLASS(class);
101
102         captive_shared_cache_map_object_parent_class=g_type_class_ref(G_TYPE_OBJECT);
103         gobject_class->finalize=(void (*)(GObject *object))captive_shared_cache_map_object_finalize;
104
105         class->FileSizes_changed=captive_shared_cache_map_object_FileSizes_changed;
106         class->purge=captive_shared_cache_map_object_purge;
107
108         FileSizes_changed_signal=g_signal_new("FileSizes_changed",
109                         G_TYPE_FROM_CLASS(gobject_class),
110                         G_SIGNAL_RUN_LAST,
111                         G_STRUCT_OFFSET(CaptiveSharedCacheMapObjectClass,FileSizes_changed),
112                         NULL,NULL,
113                         captive_cc_VOID__UINT64_UINT64_UINT64,
114                         G_TYPE_NONE,3,G_TYPE_UINT64,G_TYPE_UINT64,G_TYPE_UINT64);
115         purge_signal=g_signal_new("purge",
116                         G_TYPE_FROM_CLASS(gobject_class),
117                         G_SIGNAL_RUN_LAST,
118                         G_STRUCT_OFFSET(CaptiveSharedCacheMapObjectClass,purge),
119                         NULL,NULL,
120                         captive_cc_VOID__VOID,
121                         G_TYPE_NONE,0);
122 }
123
124
125 static void captive_shared_cache_map_object_init(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
126 {
127         captive_shared_cache_map_object->pin_hash=captive_private_bcb_pin_object_hash_new();
128
129         captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
130         g_object_unref(captive_shared_cache_map_object);        /* ==sink */
131
132         CaptiveSharedCacheMapObject_hash_init();
133         g_hash_table_insert(CaptiveSharedCacheMapObject_hash,
134                         captive_shared_cache_map_object,captive_shared_cache_map_object);
135 }
136
137
138 GType captive_shared_cache_map_object_get_type(void)
139 {
140 static GType captive_shared_cache_map_object_type=0;
141
142         if (!captive_shared_cache_map_object_type) {
143 static const GTypeInfo captive_shared_cache_map_object_info={
144                                 sizeof(CaptiveSharedCacheMapObjectClass),
145                                 NULL,   /* base_init */
146                                 NULL,   /* base_finalize */
147                                 (GClassInitFunc)captive_shared_cache_map_object_class_init,
148                                 NULL,   /* class_finalize */
149                                 NULL,   /* class_data */
150                                 sizeof(CaptiveSharedCacheMapObject),
151                                 5,      /* n_preallocs */
152                                 (GInstanceInitFunc)captive_shared_cache_map_object_init,
153                                 };
154
155                 captive_shared_cache_map_object_type=g_type_register_static(G_TYPE_OBJECT,
156                                 "CaptiveSharedCacheMapObject",&captive_shared_cache_map_object_info,0);
157                 }
158
159         return captive_shared_cache_map_object_type;
160 }
161
162 static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
163                 guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength)
164 {
165 size_t size_old,size_new;
166 guint64 size64_old,size64_new;
167
168         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
169
170         g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
171         g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
172
173         size64_old=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);       
174         size_old=size64_old;
175         g_assert(size_old==size64_old);
176         size64_new=CAPTIVE_ROUND_UP64(AllocationSize,PAGE_SIZE);        
177         size_new=size64_new;
178         g_assert(size_new==size64_new);
179
180         if (size_old!=size_new) {
181 gpointer buffer_new;
182
183                 /* These two assertions should be already catched by pin/map signal handlers. */
184                 g_assert(!captive_shared_cache_map_object->map);
185                 g_assert(!g_hash_table_size(captive_shared_cache_map_object->pin_hash));
186
187                 if (AllocationSize) {
188 gpointer base;
189 int errint;
190
191                         base=mmap(
192                                         NULL,   /* start */
193                                         PAGE_SIZE+size_new+PAGE_SIZE,   /* length; leading and trailing boundary check pages */
194                                         PROT_READ|PROT_WRITE,   /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
195                                         MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
196                                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
197                                         0);     /* offset; ignored due to MAP_ANONYMOUS */
198                         g_assert(base!=NULL);
199
200                         base+=PAGE_SIZE;
201                         errint=munmap(base-PAGE_SIZE,PAGE_SIZE);        /* unmap leading boundary check page */
202                         g_assert(errint==0);
203                         errint=munmap(base+size_new,PAGE_SIZE); /* unmap trailing boundary check page */
204                         g_assert(errint==0);
205
206                         buffer_new=base;
207                         }
208                 else
209                         buffer_new=NULL;
210
211                 memcpy(buffer_new,captive_shared_cache_map_object->buffer,
212                                 MIN(AllocationSize,captive_shared_cache_map_object->AllocationSize));
213
214                 if (captive_shared_cache_map_object->AllocationSize) {
215 int errint;
216
217                         errint=munmap(captive_shared_cache_map_object->buffer,size_old);
218                         g_assert(errint==0);
219                         }
220
221                 captive_shared_cache_map_object->buffer=buffer_new;
222
223 #if 0   /* It appears it is valid to squeeze out 'dirty' blocks. FIXME: Flush them? */
224                 if (size_old>size_new) {
225 guint64 now;
226
227                         for (now=size_new;now<size_old;now+=PAGE_SIZE) {
228                                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
229                                         continue;
230                                 g_assert(!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty);
231                                 }
232                         }
233 #endif
234                 captive_shared_cache_map_object->pages=g_realloc(captive_shared_cache_map_object->pages,
235                                 size_new/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
236                 if (size_new>size_old)  /* prevent 'size_new-size_old' as it is unsigned! */
237                         memset(captive_shared_cache_map_object->pages+(size_old/PAGE_SIZE),0,
238                                         (size_new-size_old)/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
239                 }
240
241         captive_shared_cache_map_object->AllocationSize=AllocationSize;
242         captive_shared_cache_map_object->FileSize=FileSize;
243         captive_shared_cache_map_object->ValidDataLength=ValidDataLength;
244
245         g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
246         g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
247 }
248
249 static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
250 {
251         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
252
253         /* NOP; just to provide slot for checking by Bcbs */
254 }
255
256 CaptiveSharedCacheMapObject *captive_shared_cache_map_get_ref(FILE_OBJECT *FileObject,
257                 const CC_FILE_SIZES *FileSizes,BOOLEAN PinAccess,CACHE_MANAGER_CALLBACKS *CallBacks,VOID *LazyWriterContext)
258 {
259 CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
260
261         g_return_val_if_fail(FileObject!=NULL,NULL);
262         g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,NULL);
263         g_return_val_if_fail(FileSizes!=NULL,NULL);
264         g_return_val_if_fail(CallBacks!=NULL,NULL);
265
266         if ((captive_shared_cache_map_object=FileObject->SectionObjectPointers->SharedCacheMap)) {
267                 captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
268                 }
269         else {
270                 captive_shared_cache_map_object=g_object_new(
271                                 CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT,   /* object_type */
272                                 NULL);  /* first_property_name; FIXME: support properties */
273
274                 /* W32 references twice. */
275                 ObReferenceObject(FileObject);
276                 captive_shared_cache_map_object->FileObject=FileObject;
277                 captive_shared_cache_map_object->SectionObjectPointers=FileObject->SectionObjectPointers;
278                 captive_shared_cache_map_object->AllocationSize=0;
279                 captive_shared_cache_map_object->FileSize=0;
280                 captive_shared_cache_map_object->ValidDataLength=0;
281                 captive_shared_cache_map_object->PinAccess=PinAccess;
282                 captive_shared_cache_map_object->CallBacks=*CallBacks;
283                 captive_shared_cache_map_object->LazyWriterContext=LazyWriterContext;
284
285                 FileObject->SectionObjectPointers->SharedCacheMap=captive_shared_cache_map_object;
286                 }
287
288         g_assert(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
289         /* FileObject may differ - we can have a different reference to the same FCB. */
290         g_assert(FileObject->SectionObjectPointers==captive_shared_cache_map_object->SectionObjectPointers);
291         g_assert(PinAccess==captive_shared_cache_map_object->PinAccess);
292         g_assert(CallBacks->AcquireForLazyWrite==captive_shared_cache_map_object->CallBacks.AcquireForLazyWrite);
293         g_assert(CallBacks->ReleaseFromLazyWrite==captive_shared_cache_map_object->CallBacks.ReleaseFromLazyWrite);
294         g_assert(CallBacks->AcquireForReadAhead==captive_shared_cache_map_object->CallBacks.AcquireForReadAhead);
295         g_assert(CallBacks->ReleaseFromReadAhead==captive_shared_cache_map_object->CallBacks.ReleaseFromReadAhead);
296         g_assert(LazyWriterContext==captive_shared_cache_map_object->LazyWriterContext);
297
298         captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,FileSizes);
299
300         return captive_shared_cache_map_object;
301 }
302
303 void captive_shared_cache_map_FileSizes_set(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
304                 const CC_FILE_SIZES *FileSizes)
305 {
306 guint64 AllocationSize,FileSize,ValidDataLength;
307
308         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
309         g_return_if_fail(FileSizes!=NULL);
310
311         AllocationSize=FileSizes->AllocationSize.QuadPart;
312         FileSize=FileSizes->FileSize.QuadPart;
313         ValidDataLength=FileSizes->ValidDataLength.QuadPart;
314
315         if (ValidDataLength==G_MAXINT64)
316                 ValidDataLength=FileSize;
317
318         g_assert(AllocationSize>=0);
319         g_assert(FileSize>=0);
320         g_assert(ValidDataLength>=0);
321
322         g_assert(ValidDataLength<=FileSize);
323         /* Do not: g_assert(0==(AllocationSize%0x200));
324          * as it is true for ntfs.sys of NT-5.1sp1 but it fails for fastfat.sys of NT-5.1sp1.
325          */
326         /* AllocationSize can be much higher: */
327         g_assert(FileSize<=AllocationSize);
328
329         /* Prevent signalling if not needed as signal is forbidden in captive_shared_cache_map_object_finalize() */
330         if (0
331                         || captive_shared_cache_map_object->AllocationSize!=AllocationSize
332                         || captive_shared_cache_map_object->FileSize!=FileSize
333                         || captive_shared_cache_map_object->ValidDataLength!=ValidDataLength)
334                 g_signal_emit(captive_shared_cache_map_object,FileSizes_changed_signal,0,
335                                 AllocationSize,FileSize,ValidDataLength);
336
337         g_assert(captive_shared_cache_map_object->AllocationSize==AllocationSize);
338         g_assert(captive_shared_cache_map_object->FileSize==FileSize);
339         g_assert(captive_shared_cache_map_object->ValidDataLength==ValidDataLength);
340 }
341
342 CaptiveSharedCacheMapObject *captive_SectionObjectPointers_to_SharedCacheMap(SECTION_OBJECT_POINTERS *SectionObjectPointers)
343 {
344         g_return_val_if_fail(SectionObjectPointers!=NULL,NULL);
345         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(SectionObjectPointers->SharedCacheMap),NULL);
346
347         return SectionObjectPointers->SharedCacheMap;
348 }
349
350 CaptiveSharedCacheMapObject *captive_FileObject_to_SharedCacheMap(FILE_OBJECT *FileObject)
351 {
352         g_return_val_if_fail(FileObject!=NULL,NULL);
353
354         return captive_SectionObjectPointers_to_SharedCacheMap(FileObject->SectionObjectPointers);
355 }
356
357 void captive_shared_cache_map_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
358 {
359         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
360
361         g_object_ref(captive_shared_cache_map_object);
362         captive_shared_cache_map_object->w32_ref_count++;
363 }
364
365 void captive_shared_cache_map_w32_unref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
366 {
367         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
368         g_return_if_fail(G_OBJECT(captive_shared_cache_map_object)->ref_count>0);
369
370         if (captive_shared_cache_map_object->w32_ref_count==1) {
371 static const CC_FILE_SIZES FileSizes_zero;
372
373                 g_assert(G_OBJECT(captive_shared_cache_map_object)->ref_count==1);
374
375                 /* Prevent such truncation in captive_shared_cache_map_object_finalize()
376                  * as it would do forbidden g_object_ref() there.
377                  */
378                 captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,&FileSizes_zero);
379                 g_assert(captive_shared_cache_map_object->buffer==NULL);
380                 g_assert(captive_shared_cache_map_object->pages==NULL);
381                 }
382
383         captive_shared_cache_map_object->w32_ref_count--;
384         g_object_unref(captive_shared_cache_map_object);
385 }
386
387 void captive_shared_cache_map_data_validate_read(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
388                 FILE_OBJECT *FileObject,guint64 start,guint64 end)
389 {
390 guint64 now;
391 gboolean after_eof=FALSE;       /* Did we reached the end of file already? */
392
393         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
394         g_return_if_fail(captive_shared_cache_map_object==captive_FileObject_to_SharedCacheMap(FileObject));
395         g_return_if_fail(start<=end);
396         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
397
398         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
399         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
400
401         for (now=start;now<end;now+=PAGE_SIZE) {
402 LARGE_INTEGER now_LargeInteger;
403 ULONG got;
404
405                 if (captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
406                         continue;
407                 now_LargeInteger.QuadPart=now;
408                 got=captive_Cc_IoPageRead(FileObject,captive_shared_cache_map_object->buffer+now,PAGE_SIZE,&now_LargeInteger);
409                 if (after_eof)
410                         g_assert(got==0);
411                 else
412                         g_assert(got<=PAGE_SIZE);
413                 after_eof=(got<PAGE_SIZE);
414                 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
415                 }
416 }
417
418 void captive_shared_cache_map_data_validate_noread(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
419                 guint64 start,guint64 end)
420 {
421 guint64 now;
422
423         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
424         g_return_if_fail(start<=end);
425         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
426
427         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
428         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
429
430         for (now=start;now<end;now+=PAGE_SIZE) {
431                 g_assert(captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid);
432                 }
433 }
434
435 void captive_shared_cache_map_set_data_valid(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
436                 guint64 start,guint64 end)
437 {
438 guint64 now;
439
440         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
441         g_return_if_fail(start<=end);
442         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
443
444         start=CAPTIVE_ROUND_UP64(start,PAGE_SIZE);
445         end=CAPTIVE_ROUND_DOWN64(end,PAGE_SIZE);
446         /* We may get end<start here - it is valid to not to validate anything. */
447
448         for (now=start;now<end;now+=PAGE_SIZE) {
449                 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
450                 }
451 }
452
453 void captive_shared_cache_map_set_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
454                 guint64 start,guint64 end)
455 {
456 guint64 now;
457 CaptiveSharedCacheMapObject_page *page;
458
459         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
460         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
461
462         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
463         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
464
465         for (now=start;now<end;now+=PAGE_SIZE) {
466                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
467                 g_assert(page->data_valid);
468                 if (!page->dirty) {
469                         page->dirty=TRUE;
470                         page->lsn_oldest=0;
471                         page->lsn_newest=0;
472                         }
473                 }
474 }
475
476 gboolean captive_shared_cache_map_is_page_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
477                 guint64 offset)
478 {
479 CaptiveSharedCacheMapObject_page *page;
480
481         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),FALSE);
482         g_return_val_if_fail(offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE),FALSE);
483         g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE),FALSE);
484         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
485         g_return_val_if_fail(page->data_valid,FALSE);
486
487         return page->dirty;
488 }
489
490 void captive_shared_cache_map_page_set_lsn(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
491                 guint64 offset,gint64 lsn)
492 {
493 CaptiveSharedCacheMapObject_page *page;
494
495         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
496         g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
497         g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
498         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
499         g_return_if_fail(page->data_valid);
500         g_return_if_fail(page->dirty);
501         g_return_if_fail(page->lsn_oldest<=page->lsn_newest);
502         g_return_if_fail(!page->lsn_newest || lsn>=page->lsn_newest);
503         g_return_if_fail(captive_shared_cache_map_object->LogHandle_set);
504         g_return_if_fail(captive_shared_cache_map_object->FlushToLsnRoutine_set);
505
506         if (!page->lsn_oldest)
507                 page->lsn_oldest=lsn;
508         page->lsn_newest=lsn;
509 }
510
511 void captive_shared_cache_map_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
512 {
513 guint64 offset;
514 CaptiveSharedCacheMapObject_page *page;
515
516         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
517
518         g_signal_emit(captive_shared_cache_map_object,purge_signal,0);
519
520         for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
521                 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
522                 if (!page->data_valid)
523                         continue;
524                 g_assert(!page->dirty); /* FIXME: Is it allowed by W32? */
525                 page->data_valid=FALSE;
526                 }
527 }
528
529 static VOID *captive_LogHandle;
530 static PFLUSH_TO_LSN captive_FlushToLsnRoutine;
531
532 void captive_shared_cache_map_set_LogHandle(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,VOID *LogHandle)
533 {
534         g_return_if_fail(!captive_shared_cache_map_object || CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
535         /* FIXME: 'captive_shared_cache_map_object->LogHandle_set' may be set.
536          * Does it mean 'LogHandle_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
537          */
538
539         if (!LogHandle)
540                 return;
541         g_assert(!captive_LogHandle || captive_LogHandle==LogHandle);
542         captive_LogHandle=LogHandle;
543         if (LogHandle && captive_shared_cache_map_object)
544                 captive_shared_cache_map_object->LogHandle_set=TRUE;
545 }
546
547 void captive_shared_cache_map_set_FlushToLsnRoutine
548                 (CaptiveSharedCacheMapObject *captive_shared_cache_map_object,PFLUSH_TO_LSN FlushToLsnRoutine)
549 {
550         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
551         /* FIXME: 'captive_shared_cache_map_object->FlushToLsnRoutine_set' may be set.
552          * Does it mean 'FlushToLsnRoutine_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
553          */
554
555         if (!FlushToLsnRoutine)
556                 return;
557         g_assert(!captive_FlushToLsnRoutine || captive_FlushToLsnRoutine==FlushToLsnRoutine);
558         captive_FlushToLsnRoutine=FlushToLsnRoutine;
559         if (FlushToLsnRoutine)
560                 captive_shared_cache_map_object->FlushToLsnRoutine_set=TRUE;
561 }
562
563 static void captive_shared_cache_map_page_write(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
564                 guint64 offset)
565 {
566 LARGE_INTEGER offset_LargeInteger;
567 static gint64 lsn_last;
568 CaptiveSharedCacheMapObject_page *page;
569
570         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
571         g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
572         g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
573         g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
574         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
575         g_return_if_fail(page->data_valid);
576         g_return_if_fail(page->dirty);
577
578         if (page->lsn_newest) {
579 LARGE_INTEGER lsn_newest_LargeInteger;
580
581                 /* sanity check */
582                 g_assert(!lsn_last || lsn_last<=page->lsn_newest);
583                 lsn_last=page->lsn_newest;
584
585                 lsn_newest_LargeInteger.QuadPart=page->lsn_newest;
586                 (*captive_FlushToLsnRoutine)(captive_LogHandle,lsn_newest_LargeInteger);
587                 }
588
589         offset_LargeInteger.QuadPart=offset;
590         captive_Cc_IoPageWrite(captive_shared_cache_map_object->FileObject,
591                         captive_shared_cache_map_object->buffer+offset,PAGE_SIZE,&offset_LargeInteger);
592
593         page->dirty=FALSE;
594         page->lsn_oldest=0;
595         page->lsn_newest=0;
596 }
597
598 typedef struct _captive_shared_cache_map_page_write_lsn_foreach_param captive_shared_cache_map_page_write_lsn_foreach_param;
599 struct _captive_shared_cache_map_page_write_lsn_foreach_param {
600         gint64 lsn_target;
601         gint64 lsn_best;
602         CaptiveSharedCacheMapObject *captive_shared_cache_map_object_best;
603         guint64 offset_best;
604         };
605
606 static void captive_shared_cache_map_page_write_lsn_foreach(
607                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
608                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
609                 captive_shared_cache_map_page_write_lsn_foreach_param *param)   /* user_data */
610 {
611 guint64 now;
612 CaptiveSharedCacheMapObject_page *page;
613
614         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
615         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
616         g_return_if_fail(param!=NULL);
617
618         for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
619                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
620                 if (!page->data_valid)
621                         continue;
622                 if (!page->dirty)
623                         continue;
624                 if (!page->lsn_newest)
625                         continue;
626                 if (page->lsn_newest>=param->lsn_target)
627                         continue;
628                 if (param->lsn_best && page->lsn_newest>param->lsn_best)
629                         continue;
630                 param->lsn_best=page->lsn_newest;
631                 param->captive_shared_cache_map_object_best=captive_shared_cache_map_object;
632                 param->offset_best=now;
633                 }
634 }
635
636 static void captive_shared_cache_map_page_write_lsn(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
637                 guint64 offset)
638 {
639 CaptiveSharedCacheMapObject_page *page;
640
641         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
642         g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
643         g_return_if_fail(offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
644         g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
645         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
646         g_return_if_fail(page->data_valid);
647         g_return_if_fail(page->dirty);
648
649         if (page->lsn_newest) {
650                 CaptiveSharedCacheMapObject_hash_init();
651                 for (;;) {
652 captive_shared_cache_map_page_write_lsn_foreach_param param;
653
654                         param.lsn_target=page->lsn_newest;
655                         param.lsn_best=0;
656                         g_hash_table_foreach(
657                                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
658                                         (GHFunc)captive_shared_cache_map_page_write_lsn_foreach,        /* func */
659                                         &param);        /* user_data */
660                         if (!param.lsn_best)
661                                 break;
662                         captive_shared_cache_map_page_write(param.captive_shared_cache_map_object_best,param.offset_best);
663                         }
664                 }
665
666         captive_shared_cache_map_page_write(captive_shared_cache_map_object,offset);
667 }
668
669 guint64 captive_shared_cache_map_flush(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
670                 guint64 start,guint64 end)
671 {
672 guint64 flushed;
673 guint64 now;
674
675         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),0);
676         g_return_val_if_fail(start<=end,0);
677
678         end=MIN(end,CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
679
680         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
681         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
682
683         flushed=0;
684         for (now=start;now<end;now+=PAGE_SIZE) {
685                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
686                         continue;
687                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty)
688                         continue;
689                 captive_shared_cache_map_page_write_lsn(captive_shared_cache_map_object,now);
690                 flushed+=PAGE_SIZE;
691                 }
692
693         /* We were calling W32 code - recheck our task completion. */
694         for (now=start;now<end;now+=PAGE_SIZE) {
695                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
696                         continue;
697                 g_assert(!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty);
698                 }
699
700         return flushed;
701 }
702
703 static void captive_shared_cache_map_is_any_dirty_foreach(
704                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
705                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
706                 gboolean *dirty_foundp) /* user_data */
707 {
708 guint64 now;
709
710         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
711         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
712         g_return_if_fail(dirty_foundp!=NULL);
713
714         for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
715                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
716                         continue;
717                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty)
718                         continue;
719                 *dirty_foundp=TRUE;     /* FIXME: stop the traversal. */
720                 break;
721                 }
722 }
723
724
725 gboolean captive_shared_cache_map_is_any_dirty(void)
726 {
727 gboolean dirty_found;
728
729         CaptiveSharedCacheMapObject_hash_init();
730         dirty_found=FALSE;
731         g_hash_table_foreach(
732                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
733                         (GHFunc)captive_shared_cache_map_is_any_dirty_foreach,  /* func */
734                         &dirty_found);  /* user_data */
735
736         return dirty_found;
737 }
738
739
740 typedef struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param captive_shared_cache_map_CcGetDirtyPages_foreach_param;
741 struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param {
742         PDIRTY_PAGE_ROUTINE DirtyPageRoutine;
743         VOID *Context1;
744         VOID *Context2;
745         gint64 result;
746         };
747
748 static void captive_shared_cache_map_CcGetDirtyPages_foreach(
749                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
750                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
751                 captive_shared_cache_map_CcGetDirtyPages_foreach_param *param)  /* user_data */
752 {
753 gint64 now;
754 CaptiveSharedCacheMapObject_page *page;
755
756         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
757         g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
758         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
759         g_return_if_fail(param!=NULL);
760
761         for (now=CAPTIVE_ROUND_DOWN64(captive_shared_cache_map_object->AllocationSize-1,PAGE_SIZE);now>=0;now-=PAGE_SIZE) {
762 LARGE_INTEGER now_LargeInteger,lsn_oldest_LargeInteger,lsn_newest_LargeInteger;
763
764                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
765                 if (!page->data_valid)
766                         continue;
767                 if (!page->dirty)
768                         continue;
769                 if (page->lsn_oldest && (!param->result || param->result>page->lsn_oldest))
770                         param->result=page->lsn_oldest;
771
772                 now_LargeInteger.QuadPart=now;
773                 lsn_oldest_LargeInteger.QuadPart=page->lsn_oldest;
774                 lsn_newest_LargeInteger.QuadPart=page->lsn_newest;
775                 (*param->DirtyPageRoutine)(
776                                 captive_shared_cache_map_object->FileObject,    /* FileObject */
777                                 &now_LargeInteger,      /* FileOffset */
778                                 PAGE_SIZE,      /* Length */
779                                 &lsn_oldest_LargeInteger,       /* OldestLsn */
780                                 &lsn_newest_LargeInteger,       /* NewestLsn */
781                                 param->Context1,        /* Context1 */
782                                 param->Context2);       /* Context2 */
783                 }
784 }
785
786 gint64 captive_shared_cache_map_CcGetDirtyPages(PDIRTY_PAGE_ROUTINE DirtyPageRoutine,VOID *Context1,VOID *Context2)
787 {
788 captive_shared_cache_map_CcGetDirtyPages_foreach_param param;
789
790         g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
791
792         param.DirtyPageRoutine=DirtyPageRoutine;
793         param.Context1=Context1;
794         param.Context2=Context2;
795         param.result=0;
796
797         CaptiveSharedCacheMapObject_hash_init();
798         g_hash_table_foreach(
799                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
800                         (GHFunc)captive_shared_cache_map_CcGetDirtyPages_foreach,       /* func */
801                         &param);        /* user_data */
802
803         return param.result;
804 }
805
806 gpointer captive_shared_cache_map_get_buffer(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
807 {
808         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
809         g_return_val_if_fail(captive_shared_cache_map_object->buffer!=NULL,NULL);
810
811         return captive_shared_cache_map_object->buffer;
812 }
813
814 static void captive_cc_FileObject_delete_foreach(
815                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
816                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
817                 FILE_OBJECT *FileObject)        /* user_data */
818 {
819         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
820         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
821         g_return_if_fail(FileObject!=NULL);
822
823         g_assert(FileObject!=captive_shared_cache_map_object->FileObject);
824 }
825
826 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
827 {
828         g_return_val_if_fail(FileObject!=NULL,FALSE);
829
830         CaptiveSharedCacheMapObject_hash_init();
831         g_hash_table_foreach(
832                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
833                         (GHFunc)captive_cc_FileObject_delete_foreach,   /* func */
834                         FileObject);    /* user_data */
835
836         return FALSE;   /* FIXME: remove useless return code. */
837 }