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