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