Report user-readable error for not yet supported large non-mappable areas.
[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_parent(G_TYPE_FROM_CLASS(class)));
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         if (size_new!=size64_new) {
179 size_new_big:
180                 g_error("Mapped size %" G_GUINT64_FORMAT " is too big; FIXME: non-mmap(2)-able areas not yet supported",size64_new);
181                 g_assert_not_reached();
182                 }
183
184         if (size_old!=size_new) {
185 gpointer buffer_new;
186
187                 /* These two assertions should be already catched by pin/map signal handlers. */
188                 g_assert(!captive_shared_cache_map_object->map);
189                 g_assert(!g_hash_table_size(captive_shared_cache_map_object->pin_hash));
190
191                 if (AllocationSize) {
192 gpointer base;
193 int errint;
194
195                         base=mmap(
196                                         NULL,   /* start */
197                                         PAGE_SIZE+size_new+PAGE_SIZE,   /* length; leading and trailing boundary check pages */
198                                         PROT_READ|PROT_WRITE,   /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
199                                         MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
200                                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
201                                         0);     /* offset; ignored due to MAP_ANONYMOUS */
202                         if (base==MAP_FAILED)
203                                 goto size_new_big;
204                         g_assert(base!=NULL);
205
206                         base+=PAGE_SIZE;
207                         errint=munmap(base-PAGE_SIZE,PAGE_SIZE);        /* unmap leading boundary check page */
208                         g_assert(errint==0);
209                         errint=munmap(base+size_new,PAGE_SIZE); /* unmap trailing boundary check page */
210                         g_assert(errint==0);
211
212                         buffer_new=base;
213                         }
214                 else
215                         buffer_new=NULL;
216
217                 memcpy(buffer_new,captive_shared_cache_map_object->buffer,
218                                 MIN(AllocationSize,captive_shared_cache_map_object->AllocationSize));
219
220                 if (captive_shared_cache_map_object->AllocationSize) {
221 int errint;
222
223                         errint=munmap(captive_shared_cache_map_object->buffer,size_old);
224                         g_assert(errint==0);
225                         }
226
227                 captive_shared_cache_map_object->buffer=buffer_new;
228
229 #if 0   /* It appears it is valid to squeeze out 'dirty' blocks. FIXME: Flush them? */
230                 if (size_old>size_new) {
231 guint64 now;
232
233                         for (now=size_new;now<size_old;now+=PAGE_SIZE) {
234                                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
235                                         continue;
236                                 g_assert(!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty);
237                                 }
238                         }
239 #endif
240                 captive_shared_cache_map_object->pages=g_realloc(captive_shared_cache_map_object->pages,
241                                 size_new/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
242                 if (size_new>size_old)  /* prevent 'size_new-size_old' as it is unsigned! */
243                         memset(captive_shared_cache_map_object->pages+(size_old/PAGE_SIZE),0,
244                                         (size_new-size_old)/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
245                 }
246
247         captive_shared_cache_map_object->AllocationSize=AllocationSize;
248         captive_shared_cache_map_object->FileSize=FileSize;
249         captive_shared_cache_map_object->ValidDataLength=ValidDataLength;
250
251         g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
252         g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
253 }
254
255 static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
256 {
257         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
258
259         /* NOP; just to provide slot for checking by Bcbs */
260 }
261
262 CaptiveSharedCacheMapObject *captive_shared_cache_map_get_ref(FILE_OBJECT *FileObject,
263                 const CC_FILE_SIZES *FileSizes,BOOLEAN PinAccess,const CACHE_MANAGER_CALLBACKS *CallBacks,VOID *LazyWriterContext)
264 {
265 CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
266
267         g_return_val_if_fail(FileObject!=NULL,NULL);
268         g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,NULL);
269         g_return_val_if_fail(FileSizes!=NULL,NULL);
270         g_return_val_if_fail(CallBacks!=NULL,NULL);
271
272         if ((captive_shared_cache_map_object=FileObject->SectionObjectPointers->SharedCacheMap)) {
273                 captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
274                 }
275         else {
276                 captive_shared_cache_map_object=g_object_new(
277                                 CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT,   /* object_type */
278                                 NULL);  /* first_property_name; FIXME: support properties */
279
280                 /* FIXME: When to drop SharedCacheMap?
281                  * Currently we never close it.
282                  * Fix also CcZeroData() workaround.
283                  */
284                 g_object_ref(captive_shared_cache_map_object);
285
286                 /* W32 references twice. */
287                 ObReferenceObject(FileObject);
288                 captive_shared_cache_map_object->FileObject=FileObject;
289                 captive_shared_cache_map_object->SectionObjectPointers=FileObject->SectionObjectPointers;
290                 captive_shared_cache_map_object->AllocationSize=0;
291                 captive_shared_cache_map_object->FileSize=0;
292                 captive_shared_cache_map_object->ValidDataLength=0;
293                 captive_shared_cache_map_object->PinAccess=PinAccess;
294                 captive_shared_cache_map_object->CallBacks=*CallBacks;
295                 captive_shared_cache_map_object->LazyWriterContext=LazyWriterContext;
296
297                 FileObject->SectionObjectPointers->SharedCacheMap=captive_shared_cache_map_object;
298                 }
299
300         g_assert(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
301         /* FileObject may differ - we can have a different reference to the same FCB. */
302         g_assert(FileObject->SectionObjectPointers==captive_shared_cache_map_object->SectionObjectPointers);
303         g_assert(PinAccess==captive_shared_cache_map_object->PinAccess);
304         g_assert(CallBacks->AcquireForLazyWrite==captive_shared_cache_map_object->CallBacks.AcquireForLazyWrite);
305         g_assert(CallBacks->ReleaseFromLazyWrite==captive_shared_cache_map_object->CallBacks.ReleaseFromLazyWrite);
306         g_assert(CallBacks->AcquireForReadAhead==captive_shared_cache_map_object->CallBacks.AcquireForReadAhead);
307         g_assert(CallBacks->ReleaseFromReadAhead==captive_shared_cache_map_object->CallBacks.ReleaseFromReadAhead);
308         g_assert(LazyWriterContext==captive_shared_cache_map_object->LazyWriterContext);
309
310         captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,FileSizes);
311
312         return captive_shared_cache_map_object;
313 }
314
315 void captive_shared_cache_map_FileSizes_set(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
316                 const CC_FILE_SIZES *FileSizes)
317 {
318 guint64 AllocationSize,FileSize,ValidDataLength;
319
320         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
321         g_return_if_fail(FileSizes!=NULL);
322
323         AllocationSize=FileSizes->AllocationSize.QuadPart;
324         FileSize=FileSizes->FileSize.QuadPart;
325         ValidDataLength=FileSizes->ValidDataLength.QuadPart;
326
327         if (ValidDataLength==G_MAXINT64)
328                 ValidDataLength=FileSize;
329
330         g_assert(AllocationSize>=0);
331         g_assert(FileSize>=0);
332         g_assert(ValidDataLength>=0);
333
334         g_assert(ValidDataLength<=FileSize);
335         /* Do not: g_assert(0==(AllocationSize%0x200));
336          * as it is true for ntfs.sys of NT-5.1sp1 but it fails for fastfat.sys of NT-5.1sp1.
337          */
338         /* AllocationSize can be much higher: */
339         g_assert(FileSize<=AllocationSize);
340
341         /* Prevent signalling if not needed. */
342         if (0
343                         || captive_shared_cache_map_object->AllocationSize!=AllocationSize
344                         || captive_shared_cache_map_object->FileSize!=FileSize
345                         || captive_shared_cache_map_object->ValidDataLength!=ValidDataLength) {
346                 /* Signalling is forbidden in captive_shared_cache_map_object_finalize(). */
347                 if (G_OBJECT(captive_shared_cache_map_object)->ref_count==0) {
348                         (*CAPTIVE_SHARED_CACHE_MAP_OBJECT_GET_CLASS(captive_shared_cache_map_object)->FileSizes_changed)
349                                         (captive_shared_cache_map_object,AllocationSize,FileSize,ValidDataLength);
350                         }
351                 else {
352                                 g_signal_emit(captive_shared_cache_map_object,FileSizes_changed_signal,0,
353                                                 AllocationSize,FileSize,ValidDataLength);
354                         }
355                 }
356
357         g_assert(captive_shared_cache_map_object->AllocationSize==AllocationSize);
358         g_assert(captive_shared_cache_map_object->FileSize==FileSize);
359         g_assert(captive_shared_cache_map_object->ValidDataLength==ValidDataLength);
360 }
361
362 CaptiveSharedCacheMapObject *captive_SectionObjectPointers_to_SharedCacheMap(SECTION_OBJECT_POINTERS *SectionObjectPointers)
363 {
364         g_return_val_if_fail(SectionObjectPointers!=NULL,NULL);
365         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(SectionObjectPointers->SharedCacheMap),NULL);
366
367         return SectionObjectPointers->SharedCacheMap;
368 }
369
370 CaptiveSharedCacheMapObject *captive_FileObject_to_SharedCacheMap(FILE_OBJECT *FileObject)
371 {
372         g_return_val_if_fail(FileObject!=NULL,NULL);
373
374         return captive_SectionObjectPointers_to_SharedCacheMap(FileObject->SectionObjectPointers);
375 }
376
377 void captive_shared_cache_map_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
378 {
379         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
380
381         g_object_ref(captive_shared_cache_map_object);
382         captive_shared_cache_map_object->w32_ref_count++;
383 }
384
385 void captive_shared_cache_map_w32_unref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
386 {
387         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
388         g_return_if_fail(G_OBJECT(captive_shared_cache_map_object)->ref_count>0);
389
390         captive_shared_cache_map_object->w32_ref_count--;
391         g_object_unref(captive_shared_cache_map_object);
392 }
393
394 void captive_shared_cache_map_data_validate_read(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
395                 FILE_OBJECT *FileObject,guint64 start,guint64 end)
396 {
397 guint64 now;
398 gboolean after_eof=FALSE;       /* Did we reached the end of file already? */
399
400         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
401         g_return_if_fail(captive_shared_cache_map_object==captive_FileObject_to_SharedCacheMap(FileObject));
402         g_return_if_fail(start<=end);
403         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
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 LARGE_INTEGER now_LargeInteger;
410 ULONG got;
411
412                 if (captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
413                         continue;
414                 now_LargeInteger.QuadPart=now;
415                 got=captive_Cc_IoPageRead(FileObject,captive_shared_cache_map_object->buffer+now,PAGE_SIZE,&now_LargeInteger);
416                 if (after_eof)
417                         g_assert(got==0);
418                 else
419                         g_assert(got<=PAGE_SIZE);
420                 after_eof=(got<PAGE_SIZE);
421                 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
422                 }
423 }
424
425 void captive_shared_cache_map_data_validate_noread(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
426                 guint64 start,guint64 end)
427 {
428 guint64 now;
429
430         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
431         g_return_if_fail(start<=end);
432         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
433
434         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
435         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
436
437         for (now=start;now<end;now+=PAGE_SIZE) {
438                 g_assert(captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid);
439                 }
440 }
441
442 void captive_shared_cache_map_set_data_valid(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
443                 guint64 start,guint64 end)
444 {
445 guint64 now;
446
447         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
448         g_return_if_fail(start<=end);
449         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
450
451         start=CAPTIVE_ROUND_UP64(start,PAGE_SIZE);
452         if (end<captive_shared_cache_map_object->FileSize)
453                 end=CAPTIVE_ROUND_DOWN64(end,PAGE_SIZE);
454         else {
455                 /* We can validate the last page of the file
456                  * even if it does not end on PAGE_SIZE boundary.
457                  */
458                 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
459                 }
460         /* We may get end<start here - it is valid to not to validate anything. */
461
462         for (now=start;now<end;now+=PAGE_SIZE) {
463                 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
464                 }
465 }
466
467 void captive_shared_cache_map_set_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
468                 guint64 start,guint64 end)
469 {
470 guint64 now;
471 CaptiveSharedCacheMapObject_page *page;
472
473         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
474         g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
475
476         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
477         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
478
479         for (now=start;now<end;now+=PAGE_SIZE) {
480                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
481                 g_assert(page->data_valid);
482                 if (!page->dirty) {
483                         page->dirty=TRUE;
484                         page->lsn_oldest=0;
485                         page->lsn_newest=0;
486                         }
487                 }
488 }
489
490 gboolean captive_shared_cache_map_is_page_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
491                 guint64 offset)
492 {
493 CaptiveSharedCacheMapObject_page *page;
494
495         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),FALSE);
496         g_return_val_if_fail(offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE),FALSE);
497         g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE),FALSE);
498         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
499         g_return_val_if_fail(page->data_valid,FALSE);
500
501         return page->dirty;
502 }
503
504 void captive_shared_cache_map_page_set_lsn(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
505                 guint64 offset,gint64 lsn)
506 {
507 CaptiveSharedCacheMapObject_page *page;
508
509         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
510         g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
511         g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
512         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
513         g_return_if_fail(page->data_valid);
514         g_return_if_fail(page->dirty);
515         g_return_if_fail(page->lsn_oldest<=page->lsn_newest);
516         g_return_if_fail(!page->lsn_newest || lsn>=page->lsn_newest);
517         g_return_if_fail(captive_shared_cache_map_object->LogHandle_set);
518         g_return_if_fail(captive_shared_cache_map_object->FlushToLsnRoutine_set);
519
520         if (!page->lsn_oldest)
521                 page->lsn_oldest=lsn;
522         page->lsn_newest=lsn;
523 }
524
525 void captive_shared_cache_map_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
526 {
527 guint64 offset;
528 CaptiveSharedCacheMapObject_page *page;
529
530         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
531
532         g_signal_emit(captive_shared_cache_map_object,purge_signal,0);
533
534         for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
535                 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
536                 if (!page->data_valid)
537                         continue;
538                 g_assert(!page->dirty); /* FIXME: Is it allowed by W32? */
539                 page->data_valid=FALSE;
540                 }
541 }
542
543 static VOID *captive_LogHandle;
544 static PFLUSH_TO_LSN captive_FlushToLsnRoutine;
545
546 void captive_shared_cache_map_set_LogHandle(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,VOID *LogHandle)
547 {
548         g_return_if_fail(!captive_shared_cache_map_object || CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
549         /* FIXME: 'captive_shared_cache_map_object->LogHandle_set' may be set.
550          * Does it mean 'LogHandle_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
551          */
552
553         if (!LogHandle)
554                 return;
555         g_assert(!captive_LogHandle || captive_LogHandle==LogHandle);
556         captive_LogHandle=LogHandle;
557         if (LogHandle && captive_shared_cache_map_object)
558                 captive_shared_cache_map_object->LogHandle_set=TRUE;
559 }
560
561 void captive_shared_cache_map_set_FlushToLsnRoutine
562                 (CaptiveSharedCacheMapObject *captive_shared_cache_map_object,PFLUSH_TO_LSN FlushToLsnRoutine)
563 {
564         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
565         /* FIXME: 'captive_shared_cache_map_object->FlushToLsnRoutine_set' may be set.
566          * Does it mean 'FlushToLsnRoutine_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
567          */
568
569         if (!FlushToLsnRoutine)
570                 return;
571         g_assert(!captive_FlushToLsnRoutine || captive_FlushToLsnRoutine==FlushToLsnRoutine);
572         captive_FlushToLsnRoutine=FlushToLsnRoutine;
573         if (FlushToLsnRoutine)
574                 captive_shared_cache_map_object->FlushToLsnRoutine_set=TRUE;
575 }
576
577 static void captive_shared_cache_map_page_write(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
578                 guint64 offset)
579 {
580 LARGE_INTEGER offset_LargeInteger;
581 static gint64 lsn_last;
582 CaptiveSharedCacheMapObject_page *page;
583
584         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
585         g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
586         g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
587         g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
588         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
589         g_return_if_fail(page->data_valid);
590         g_return_if_fail(page->dirty);
591
592         if (page->lsn_newest) {
593 LARGE_INTEGER lsn_newest_LargeInteger;
594
595                 /* sanity check */
596                 g_assert(!lsn_last || lsn_last<=page->lsn_newest);
597                 lsn_last=page->lsn_newest;
598
599                 lsn_newest_LargeInteger.QuadPart=page->lsn_newest;
600                 (*captive_FlushToLsnRoutine)(captive_LogHandle,lsn_newest_LargeInteger);
601                 }
602
603         offset_LargeInteger.QuadPart=offset;
604         captive_Cc_IoPageWrite(captive_shared_cache_map_object->FileObject,
605                         captive_shared_cache_map_object->buffer+offset,PAGE_SIZE,&offset_LargeInteger);
606
607         page->dirty=FALSE;
608         page->lsn_oldest=0;
609         page->lsn_newest=0;
610 }
611
612 typedef struct _captive_shared_cache_map_page_write_lsn_foreach_param captive_shared_cache_map_page_write_lsn_foreach_param;
613 struct _captive_shared_cache_map_page_write_lsn_foreach_param {
614         gint64 lsn_target;
615         gint64 lsn_best;
616         CaptiveSharedCacheMapObject *captive_shared_cache_map_object_best;
617         guint64 offset_best;
618         };
619
620 static void captive_shared_cache_map_page_write_lsn_foreach(
621                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
622                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
623                 captive_shared_cache_map_page_write_lsn_foreach_param *param)   /* user_data */
624 {
625 guint64 now;
626 CaptiveSharedCacheMapObject_page *page;
627
628         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
629         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
630         g_return_if_fail(param!=NULL);
631
632         for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
633                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
634                 if (!page->data_valid)
635                         continue;
636                 if (!page->dirty)
637                         continue;
638                 if (!page->lsn_newest)
639                         continue;
640                 if (page->lsn_newest>=param->lsn_target)
641                         continue;
642                 if (param->lsn_best && page->lsn_newest>param->lsn_best)
643                         continue;
644                 param->lsn_best=page->lsn_newest;
645                 param->captive_shared_cache_map_object_best=captive_shared_cache_map_object;
646                 param->offset_best=now;
647                 }
648 }
649
650 static void captive_shared_cache_map_page_write_lsn(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
651                 guint64 offset)
652 {
653 CaptiveSharedCacheMapObject_page *page;
654
655         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
656         g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
657         g_return_if_fail(offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
658         g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
659         page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
660         g_return_if_fail(page->data_valid);
661         g_return_if_fail(page->dirty);
662
663         if (page->lsn_newest) {
664                 CaptiveSharedCacheMapObject_hash_init();
665                 for (;;) {
666 captive_shared_cache_map_page_write_lsn_foreach_param param;
667
668                         param.lsn_target=page->lsn_newest;
669                         param.lsn_best=0;
670                         g_hash_table_foreach(
671                                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
672                                         (GHFunc)captive_shared_cache_map_page_write_lsn_foreach,        /* func */
673                                         &param);        /* user_data */
674                         if (!param.lsn_best)
675                                 break;
676                         captive_shared_cache_map_page_write(param.captive_shared_cache_map_object_best,param.offset_best);
677                         }
678                 }
679
680         captive_shared_cache_map_page_write(captive_shared_cache_map_object,offset);
681 }
682
683 guint64 captive_shared_cache_map_flush(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
684                 guint64 start,guint64 end)
685 {
686 guint64 flushed;
687 guint64 now;
688
689         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),0);
690         g_return_val_if_fail(start<=end,0);
691
692         end=MIN(end,CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
693
694         start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
695         end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
696
697         flushed=0;
698         for (now=start;now<end;now+=PAGE_SIZE) {
699                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
700                         continue;
701                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty)
702                         continue;
703                 captive_shared_cache_map_page_write_lsn(captive_shared_cache_map_object,now);
704                 flushed+=PAGE_SIZE;
705                 }
706
707         /* We were calling W32 code - recheck our task completion. */
708         for (now=start;now<end;now+=PAGE_SIZE) {
709                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
710                         continue;
711                 g_assert(!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty);
712                 }
713
714         return flushed;
715 }
716
717 static void captive_shared_cache_map_is_any_dirty_foreach(
718                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
719                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
720                 gboolean *dirty_foundp) /* user_data */
721 {
722 guint64 now;
723
724         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
725         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
726         g_return_if_fail(dirty_foundp!=NULL);
727
728         for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
729                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
730                         continue;
731                 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty)
732                         continue;
733                 *dirty_foundp=TRUE;     /* FIXME: stop the traversal. */
734                 break;
735                 }
736 }
737
738
739 gboolean captive_shared_cache_map_is_any_dirty(void)
740 {
741 gboolean dirty_found;
742
743         CaptiveSharedCacheMapObject_hash_init();
744         dirty_found=FALSE;
745         g_hash_table_foreach(
746                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
747                         (GHFunc)captive_shared_cache_map_is_any_dirty_foreach,  /* func */
748                         &dirty_found);  /* user_data */
749
750         return dirty_found;
751 }
752
753
754 typedef struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param captive_shared_cache_map_CcGetDirtyPages_foreach_param;
755 struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param {
756         PDIRTY_PAGE_ROUTINE DirtyPageRoutine;
757         VOID *Context1;
758         VOID *Context2;
759         gint64 result;
760         };
761
762 static void captive_shared_cache_map_CcGetDirtyPages_foreach(
763                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
764                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
765                 captive_shared_cache_map_CcGetDirtyPages_foreach_param *param)  /* user_data */
766 {
767 gint64 now;
768 CaptiveSharedCacheMapObject_page *page;
769
770         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
771         g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
772         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
773         g_return_if_fail(param!=NULL);
774
775         for (now=CAPTIVE_ROUND_DOWN64(captive_shared_cache_map_object->AllocationSize-1,PAGE_SIZE);now>=0;now-=PAGE_SIZE) {
776 LARGE_INTEGER now_LargeInteger,lsn_oldest_LargeInteger,lsn_newest_LargeInteger;
777
778                 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
779                 if (!page->data_valid)
780                         continue;
781                 if (!page->dirty)
782                         continue;
783                 if (page->lsn_oldest && (!param->result || param->result>page->lsn_oldest))
784                         param->result=page->lsn_oldest;
785
786                 now_LargeInteger.QuadPart=now;
787                 lsn_oldest_LargeInteger.QuadPart=page->lsn_oldest;
788                 lsn_newest_LargeInteger.QuadPart=page->lsn_newest;
789                 (*param->DirtyPageRoutine)(
790                                 captive_shared_cache_map_object->FileObject,    /* FileObject */
791                                 &now_LargeInteger,      /* FileOffset */
792                                 PAGE_SIZE,      /* Length */
793                                 &lsn_oldest_LargeInteger,       /* OldestLsn */
794                                 &lsn_newest_LargeInteger,       /* NewestLsn */
795                                 param->Context1,        /* Context1 */
796                                 param->Context2);       /* Context2 */
797                 }
798 }
799
800 gint64 captive_shared_cache_map_CcGetDirtyPages(PDIRTY_PAGE_ROUTINE DirtyPageRoutine,VOID *Context1,VOID *Context2)
801 {
802 captive_shared_cache_map_CcGetDirtyPages_foreach_param param;
803
804         g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
805
806         param.DirtyPageRoutine=DirtyPageRoutine;
807         param.Context1=Context1;
808         param.Context2=Context2;
809         param.result=0;
810
811         CaptiveSharedCacheMapObject_hash_init();
812         g_hash_table_foreach(
813                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
814                         (GHFunc)captive_shared_cache_map_CcGetDirtyPages_foreach,       /* func */
815                         &param);        /* user_data */
816
817         return param.result;
818 }
819
820 gpointer captive_shared_cache_map_get_buffer(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
821 {
822         g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
823         g_return_val_if_fail(captive_shared_cache_map_object->buffer!=NULL,NULL);
824
825         return captive_shared_cache_map_object->buffer;
826 }
827
828 static void captive_cc_FileObject_delete_foreach(
829                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object,  /* key */
830                 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value,  /* value */
831                 FILE_OBJECT *FileObject)        /* user_data */
832 {
833         g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
834         g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
835         g_return_if_fail(FileObject!=NULL);
836
837         g_assert(FileObject!=captive_shared_cache_map_object->FileObject);
838 }
839
840 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
841 {
842         g_return_val_if_fail(FileObject!=NULL,FALSE);
843
844         CaptiveSharedCacheMapObject_hash_init();
845         g_hash_table_foreach(
846                         CaptiveSharedCacheMapObject_hash,       /* hash_table */
847                         (GHFunc)captive_cc_FileObject_delete_foreach,   /* func */
848                         FileObject);    /* user_data */
849
850         return FALSE;   /* FIXME: remove useless return code. */
851 }