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