331ad793992a38d4ccd793957be46e8666b330fb
[captive.git] / src / libcaptive / cc / map.c
1 /* $Id$
2  * reactos Cache Manager mapper emulation of libcaptive
3  * Copyright (C) 2002 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 "reactos/ddk/ccfuncs.h"        /* self */
23 #include <glib/gmessages.h>
24 #include <glib/ghash.h>
25 #include <glib/gmem.h>
26 #include <sys/mman.h>
27 #include "reactos/ddk/mmfuncs.h"        /* for MmCreateMdl() */
28 #include "reactos/ddk/kefuncs.h"        /* for KeInitializeEvent() */
29 #include "reactos/ddk/iofuncs.h"        /* for IoPageRead() */
30 #include "captive/macros.h"
31 #include <sys/types.h>
32 #include <sys/shm.h>
33 #include <unistd.h>
34 #include <glib/glist.h>
35 #include "reactos/internal/io.h"        /* for IoSynchronousPageWrite() */
36 #include <glib/garray.h>
37 #include <glib/gmain.h>
38
39
40 /* CONFIG: */
41
42 #define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45  /* FIXME: unknown, undocumented */
43
44
45 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
46 {
47         g_return_val_if_fail(PublicBcb!=NULL,FALSE);
48         g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
49         g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
50         g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
51         g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
52
53         return TRUE;
54 }
55
56
57 /* position in file */
58 struct page_position {
59         FILE_OBJECT *FileObject;
60         LARGE_INTEGER FileOffset;       /* always PAGE_SIZE aligned */
61         int shmid;
62         GList *privbcb_list;    /* each mapped page has its one private_bcb */
63         };
64
65 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
66 static GHashTable *page_position_hash;
67
68 static gboolean validate_page_position(const struct page_position *pagepos)
69 {
70 int errint;
71 struct shmid_ds shmid_ds;
72
73         g_return_val_if_fail(pagepos!=NULL,FALSE);
74         g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
75         g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
76         g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
77         /* 'pagepos->shmid' may be -1 */
78         /* 'pagepos->privbcb_list' may be empty */
79         g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL),FALSE);        /* either deleted or alive */
80
81         if (pagepos->shmid!=-1) {
82                 errint=shmctl(pagepos->shmid,
83                                 IPC_STAT,       /* cmd */
84                                 &shmid_ds);     /* buf */
85                 g_return_val_if_fail(errint==0,FALSE);
86
87                 g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
88                 g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
89                 g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
90                 g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
91                 /* 'shm_perm.mode' was seen with sticky bit 01000: */
92                 g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
93                 g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
94                 g_return_val_if_fail(shmid_ds.shm_cpid==getpid(),FALSE);
95                 g_return_val_if_fail(shmid_ds.shm_lpid==getpid(),FALSE);
96                 g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
97                 }
98
99         return TRUE;
100 }
101
102 static guint page_position_hash_hash_func(const struct page_position *key)
103 {
104         g_return_val_if_fail(validate_page_position(key),0);
105
106         return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
107 }
108
109 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
110 {
111         g_return_val_if_fail(validate_page_position(a),FALSE);
112         g_return_val_if_fail(validate_page_position(b),FALSE);
113
114         return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
115 }
116
117 static void page_position_hash_key_destroy_func(struct page_position *key)
118 {
119         g_return_if_fail(validate_page_position(key));
120         g_assert(key->privbcb_list==NULL);
121         g_assert(key->shmid==-1);
122
123         g_free(key);
124 }
125
126 static void page_position_hash_init(void)
127 {
128         if (page_position_hash)
129                 return;
130         page_position_hash=g_hash_table_new_full(
131                         (GHashFunc)page_position_hash_hash_func,        /* hash_func */
132                         (GEqualFunc)page_position_hash_key_equal_func,  /* key_equal_func */
133                         (GDestroyNotify)page_position_hash_key_destroy_func,    /* key_destroy_func */
134                         NULL);  /* value_destroy_func */
135 }
136
137
138 struct private_bcb {
139         PUBLIC_BCB *PublicBcb;  /* ->MappedLength, ->MappedFileOffset */
140         FILE_OBJECT *FileObject;
141         gint ref_count;
142         /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
143         ULONG MappedLength;     /* It is the real requested size; it is not PAGE_SIZE aligned. */
144         /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
145         LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
146         gpointer base;  /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
147         gboolean dirty;
148         LARGE_INTEGER lsn; gboolean lsn_valid;
149         };
150
151 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
152 static GHashTable *private_bcb_hash;
153
154 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
155 {
156         g_return_if_fail(validate_Bcb(key));
157
158         g_free(key);
159 }
160
161 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
162 {
163 struct page_position pagepos_local;
164 gboolean errbool;
165 int errint;
166 size_t offset;
167 gpointer base_aligned;
168
169         g_return_if_fail(value!=NULL);
170         /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
171          * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
172          */
173         g_return_if_fail(value->PublicBcb!=NULL);
174         g_return_if_fail(value->FileObject!=NULL);
175         g_return_if_fail(value->ref_count==0);
176         g_return_if_fail(value->MappedLength>0);
177         g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
178         g_return_if_fail(value->base!=NULL);
179         g_return_if_fail(value->dirty==FALSE);
180
181         page_position_hash_init();
182
183         base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
184
185         pagepos_local.FileObject=value->FileObject;
186         pagepos_local.privbcb_list=NULL;
187         pagepos_local.shmid=-1;
188         for (
189                         offset=0;
190                         offset<value->MappedLength;
191                         offset+=PAGE_SIZE) {
192 struct page_position *pagepos;
193
194                 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
195                 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
196                 g_assert(validate_page_position(pagepos));
197                 g_assert(pagepos->privbcb_list!=NULL);
198                 errint=shmdt(((char *)base_aligned)+offset);
199                 g_assert(errint==0);
200
201                 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
202                 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
203                 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
204
205                 if (!pagepos->privbcb_list) {
206                         /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
207                         pagepos->shmid=-1;
208                         errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
209                         g_assert(errbool==TRUE);
210                         }
211                 else
212                         g_assert(validate_page_position(pagepos));
213                 }
214
215         g_free(value);
216 }
217
218 static void private_bcb_hash_init(void)
219 {
220         if (private_bcb_hash)
221                 return;
222         private_bcb_hash=g_hash_table_new_full(
223                         g_direct_hash,  /* hash_func */
224                         g_direct_equal, /* key_equal_func */
225                         (GDestroyNotify)private_bcb_hash_key_destroy_func,      /* key_destroy_func */
226                         (GDestroyNotify)private_bcb_hash_value_destroy_func);   /* value_destroy_func */
227 }
228
229
230 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
231 {
232 MDL *Mdl;
233 KEVENT Event;
234 IO_STATUS_BLOCK IoStatus;
235 NTSTATUS err;
236
237         g_return_val_if_fail(FileObject!=NULL,0);
238         g_return_val_if_fail(address!=0,0);
239         g_return_val_if_fail(length!=0,0);
240         g_return_val_if_fail(FileOffset!=NULL,0);
241
242         /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
243          * successfuly read the data. Workaround it - preclear (not postclear) the
244          * buffer and do not make any other assumptions about the data read.
245          */
246         memset(address,0,PAGE_SIZE);    /* pre-clear the buffer */
247         Mdl=MmCreateMdl(NULL,address,PAGE_SIZE);        /* FIXME: Deprecated in favor of IoAllocateMdl() */
248         g_assert(Mdl!=NULL);
249         KeInitializeEvent(&Event,NotificationEvent,FALSE);
250         /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
251         IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
252         err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
253         g_assert(NT_SUCCESS(err));
254         g_assert(NT_SUCCESS(IoStatus.Status));
255         /* It is not == as the file may be shorter than requested */
256         g_assert(IoStatus.Information<=length);
257         IoFreeMdl(Mdl);
258
259         /* Forbidden, see the comment above about ext2fsd.sys.
260          * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
261          */
262
263         return IoStatus.Information;    /* may be shorter than real! */
264 }
265
266
267 static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length)
268 {
269 struct page_position pagepos_local,*pagepos;
270 struct private_bcb *privbcb,*privbcb_listitem;
271 GList *privbcb_list;
272
273         page_position_hash_init();
274
275         pagepos_local.FileObject=FileObject;
276         pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
277         pagepos_local.privbcb_list=NULL;
278         pagepos_local.shmid=-1;
279         if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local)))
280                 return NULL;
281         g_assert(validate_page_position(pagepos));
282         g_assert(pagepos->privbcb_list!=NULL);
283
284         privbcb=NULL;
285         for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
286                 privbcb_listitem=privbcb_list->data;
287                 if (1
288                                 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
289                                 && privbcb_listitem->MappedLength==Length) {
290                         g_assert(privbcb==NULL);        /* appropriate privbcb found twice */
291                         privbcb=privbcb_listitem;
292                         }
293                 }
294         if (!privbcb)
295                 return NULL;
296
297         /* Sanity check 'privbcb': */
298         g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
299
300         return privbcb;
301 }
302
303
304 /**
305  * CcMapData:
306  * @FileObject: Initialized open #FileObject to map.
307  * %NULL value is forbidden.
308  * @FileOffset: The @FileObject file offset from where to map the region from.
309  * Negative value is forbidden.
310  * @Length: Requested length of the region to map from @FileObject.
311  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
312  * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
313  * %MAP_NO_READ currently ignored; we map the data always.
314  * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
315  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
316  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
317  * %NULL pointer is forbidden.
318  * @Buffer: Returns the mapped memory region start address.
319  * This address may not be %PAGE_SIZE aligned.
320  * %NULL pointer is forbidden.
321  *
322  * Maps the specified region of @FileObject to automatically chosen address space.
323  * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
324  *
325  * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
326  * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
327  * but W32 filesystems apparently do not conform to it. :-)  If you do not take care of the
328  * modified data by some dirty-marking facility such data will be carelessly dropped without
329  * their commit to the disk.
330  *
331  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
332  *
333  * Returns: %TRUE if the region was successfuly mapped.
334  * @Bcb with the initialized new memory region.
335  * @Buffer with the address of the exact byte specified by @FileOffset.
336  */
337 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
338                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
339 {
340 PUBLIC_BCB **PublicBcbp,*PublicBcb;
341 struct page_position pagepos_local,*pagepos;
342 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
343 gpointer base_aligned;
344 size_t offset,length_mapped_aligned;
345 int errint;
346 gpointer errptr;
347 struct private_bcb *privbcb;
348 gboolean after_eof=FALSE;       /* Did we reached the end of file already? */
349 GPtrArray *read_array;
350
351         g_return_val_if_fail(FileObject!=NULL,FALSE);
352         g_return_val_if_fail(FileOffset!=NULL,FALSE);
353         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
354         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
355         g_return_val_if_fail(Flags&MAP_WAIT,FALSE);     /* FIXME: on-demand loading not yet implemented */
356         g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE);   /* unknown flags? */
357         g_return_val_if_fail(Bcb!=NULL,FALSE);
358         g_return_val_if_fail(Buffer!=NULL,FALSE);
359
360         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
361                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
362
363         g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
364         if (FileObject->SectionObjectPointers->SharedCacheMap) {
365                 /* FIXME: (NOTE*1) Weird but it appears as fastfat.sys during make_directory()
366                  * can pass us an already cached 'FileObject' (with different offset/size).
367                  * We must not CcUnpinData() it as its BCB is still referenced (CcUnpinData()ed) by fastfat.sys.
368                  */
369                 FileObject->SectionObjectPointers->SharedCacheMap=NULL;
370                 }
371         g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
372         /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
373          * 'SectorSize' may not yet be initialized during mount operation
374          * and such state can be detected by missing 'FileObject->DeviceObject->Vpb'.
375          */
376         g_return_val_if_fail(0
377                                         || !FileObject->DeviceObject->Vpb       /* not yet much ready */
378                                         || (FileObject->DeviceObject->SectorSize>0      /* prevent division by 0 */
379                                                         && 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize)),
380                         FALSE);
381
382         page_position_hash_init();
383         private_bcb_hash_init();
384
385         PublicBcbp=(PUBLIC_BCB **)Bcb;
386         /* extend 'FileOffset' and 'Length' to page boundaries */
387         FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
388         FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
389         length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
390
391         /* FIXME: We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
392          * even if such mapping for such file already exists. How to behave in such case?
393          */
394         if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length))) {
395                 PublicBcb=privbcb->PublicBcb;
396                 privbcb->ref_count++;
397                 goto done;
398                 }
399
400         /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
401         /* TODO: on-demand loading */
402         /* Although we do zeroed-page mapping here we just reserve the linears
403          * space by it - all the page will be overriden by shmat(2) afterwards anyway.
404          */
405         base_aligned=mmap(
406                         NULL,   /* start */
407                         length_mapped_aligned,  /* length */
408                         PROT_READ|PROT_WRITE,   /* prot; FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
409                         MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
410                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
411                         0);     /* offset; ignored due to MAP_ANONYMOUS */
412         g_assert(base_aligned!=NULL);
413
414         /* Create 'PublicBcb'; referenced by 'privbcb'. */
415         captive_new(PublicBcb);
416         PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
417         PublicBcb->NodeByteSize=sizeof(*PublicBcb);     /* we have no extensions there */
418         PublicBcb->MappedLength=Length;
419         PublicBcb->MappedFileOffset=*FileOffset;
420
421         /* Create 'privbcb'; referenced by created 'pagepos'es. */
422         captive_new(privbcb);
423         privbcb->PublicBcb=PublicBcb;
424         privbcb->FileObject=FileObject;
425         privbcb->ref_count=1;
426         privbcb->MappedLength=PublicBcb->MappedLength;
427         privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
428         privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
429         privbcb->dirty=FALSE;
430         privbcb->lsn_valid=FALSE;
431         g_hash_table_insert(private_bcb_hash,
432                         PublicBcb,      /* key */
433                         privbcb);       /* value */
434
435         /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
436          * below as captive_Cc_IoPageRead() has very big consequences as it calls
437          * the filesystem code and we may get reentrancy.
438          * Therefore we store all missing page read requests to 'read_array' and we
439          * fill them when all the memory structures are in consistent state.
440          * We can also coalescence the requests more easily this way.
441          */
442         read_array=g_ptr_array_new();
443
444         pagepos_local.FileObject=FileObject;
445         pagepos_local.shmid=-1;
446         pagepos_local.privbcb_list=NULL;
447         for (
448                         offset=0;
449                         offset<length_mapped_aligned;
450                         offset+=PAGE_SIZE) {
451 gpointer pageaddr;
452
453                 pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
454                 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
455                         captive_new(pagepos);
456                         /* FIXME: Reference 'FileObject' to not to leave broken references to it here */
457                         *pagepos=pagepos_local;
458                         pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600);
459                         pagepos->privbcb_list=NULL;
460                         g_ptr_array_add(read_array,pagepos);    /* enlist this 'pagepos' as todo read item */
461                         }
462                 else
463                         g_assert(pagepos->privbcb_list!=NULL);
464                 g_assert(pagepos->shmid!=-1);
465                 pageaddr=(gpointer)(((char *)base_aligned)+offset);
466                 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
467                  * mmap(2) is still needed to get linear block of memory assignment.
468                  */
469                 /* TODO:thread; munmap()..shmat() window */
470                 errint=munmap(pageaddr,PAGE_SIZE);
471                 g_assert(errint==0);
472                 errptr=shmat(pagepos->shmid,
473                                 pageaddr,       /* shmaddr */
474                                 0);     /* shmflg; !SHM_RDONLY==r/w FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
475                 g_assert(errptr==pageaddr);
476
477                 g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
478                 pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb);    /* order not important */
479                 g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
480                 if (pagepos->privbcb_list->next==NULL) {        /* exactly one item (we just added it now) */
481
482                         errint=shmctl(pagepos->shmid,
483                                         IPC_RMID,       /* cmd */
484                                         NULL);  /* buf */
485                         g_assert(errint==0);
486                         g_hash_table_insert(page_position_hash,
487                                         pagepos,        /* key */
488                                         pagepos);       /* value */
489                         }
490                 g_assert(validate_page_position(pagepos));
491                 }
492
493         /* Fill in the missing pages content todo-list stored in 'read_array': */
494         while (read_array->len && (pagepos=g_ptr_array_remove_index(read_array,
495                                         0))) {  /* index */
496 ULONG read_size;
497 ULONG got;
498 struct pagepos *pagepos_next;
499 gpointer pageaddr,pageaddr_next;
500
501                 pageaddr=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
502                 read_size=PAGE_SIZE;
503                 /* Coalescence of the requests */
504                 while (read_array->len) {
505                         pagepos_next=g_ptr_array_index(read_array,
506                                         0);     /* index */
507                         pageaddr_next=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
508                         if (pageaddr_next!=((char *)pageaddr)+read_size)
509                                 break;
510                         read_size+=PAGE_SIZE;
511                         g_assert(pagepos_next==g_ptr_array_remove_index(read_array,
512                                         0));    /* index */
513                         }
514                 /* Read the range content: */
515                 got=captive_Cc_IoPageRead(FileObject,pageaddr,read_size,&pagepos->FileOffset);
516                 if (after_eof)
517                         g_assert(got==0);
518                 else
519                         g_assert(got<=PAGE_SIZE);
520                 after_eof=(got<PAGE_SIZE);
521                 }
522         g_ptr_array_free(read_array,
523                         TRUE);  /* free_seg; free the array of gpointer(==struct pagepos *) items */
524
525 done:
526         /* offset _into_ page, may not be PAGE_SIZE aligned: */
527         *Buffer=privbcb->base;
528         *PublicBcbp=PublicBcb;
529         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
530                         "%s: result: privbcb->base=%p,privbcb->base+privbcb->MappedLength=%p,privbcb->MappedLength=0x%lX",
531                         G_STRLOC,privbcb->base,((char *)privbcb->base)+privbcb->MappedLength,(unsigned long)privbcb->MappedLength);
532         g_assert(validate_Bcb(PublicBcb)==TRUE);
533         FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
534
535         return TRUE;
536 }
537
538
539 /**
540  * CcPinMappedData:
541  * @FileObject: Initialized open #FileObject to map.
542  * %NULL value is forbidden.
543  * @MappedFileOffset: The @FileObject file offset from where to map the region from.
544  * Negative value is forbidden.
545  * @MappedLength: Requested length of the region to map from @FileObject.
546  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
547  * @Wait: Whether disk waiting is permitted for this function.
548  * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
549  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
550  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
551  * %PIN_NO_READ currently ignored; we map the data always.
552  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
553  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
554  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
555  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
556  * %NULL pointer is forbidden.
557  * @Buffer: Returns the mapped memory region start address.
558  * This address may not be %PAGE_SIZE aligned.
559  * %NULL pointer is forbidden.
560  *
561  * This function will allow you to modify the data mapped by CcMapData().
562  * libcaptive does not differentiate this function with CcMapData().
563  *
564  * NEVER re-read any memory from FileObject here!
565  * at least fastfat.sys directory create relies on the fact of CcPinRead()
566  * with already modified buffers to be left intact.
567  *
568  * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
569  * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
570  *
571  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
572  *
573  * Returns: %TRUE if the region was successfuly mapped.
574  * @Bcb with the initialized new memory region.
575  * @Buffer with the address of the exact byte specified by @FileOffset.
576  */
577 BOOLEAN CcPinMappedData
578                 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
579 {
580 struct private_bcb *privbcb;
581
582         g_return_val_if_fail(FileObject!=NULL,FALSE);
583         g_return_val_if_fail(FileOffset!=NULL,FALSE);
584         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
585         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
586         /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
587         g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
588         g_return_val_if_fail(Bcb!=NULL,FALSE);
589
590         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
591                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
592
593         privbcb=captive_privbcb_find(FileObject,FileOffset,Length);
594         if (!privbcb && (Flags & PIN_IF_BCB))   /* BCB does not exist */
595                 return FALSE;
596         /* Appropriate privbcb not found.
597          * This IMO should not happen and it is a bug in the client.
598          * Unfortuantely fastfat.sys FatLocateVolumeLabel() will CcPinMappedData()
599          * the volume label dirent without any previous CcMapData() or CcPinRead() !
600          */
601         if (!privbcb) {
602 PVOID Buffer;
603
604                 return CcPinRead(
605                                 FileObject,
606                                 FileOffset,
607                                 Length,
608                                 Flags,
609                                 Bcb,
610                                 &Buffer);
611                 }
612
613         /* NEVER re-read any memory from FileObject here! */
614
615         privbcb->ref_count++;
616
617         /* Memory already mapped by CcMapData(). */
618         *Bcb=privbcb->PublicBcb;
619         return TRUE;
620 }
621
622
623 /**
624  * CcPinRead:
625  * @FileObject: Initialized open #FileObject to map.
626  * %NULL value is forbidden.
627  * @FileOffset: The @FileObject file offset from where to map the region from.
628  * Negative value is forbidden.
629  * @Length: Requested length of the region to map from @FileObject.
630  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
631  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
632  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
633  * %PIN_NO_READ currently ignored; we map the data always.
634  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
635  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
636  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
637  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
638  * %NULL pointer is forbidden.
639  * @Buffer: Returns the mapped memory region start address.
640  * This address may not be %PAGE_SIZE aligned.
641  * %NULL pointer is forbidden.
642  *
643  * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
644  * See these two functions for the details. It has a difference to subsequent
645  * calling of CcMapData() and CcPinMappedData() instead as this call counts
646  * only as one function for a corresponding CcUnpinData() call.
647  *
648  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
649  *
650  * Returns: %TRUE if the region was successfuly mapped.
651  * @Bcb with the initialized new memory region.
652  * @Buffer with the address of the exact byte specified by @FileOffset.
653  */
654 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
655                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
656 {
657 PVOID Bcb_CcPinMappedData;
658 gboolean errbool;
659 gboolean count_CcMapData;
660 struct private_bcb *privbcb;
661
662         g_return_val_if_fail(FileObject!=NULL,FALSE);
663         g_return_val_if_fail(FileOffset!=NULL,FALSE);
664         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
665         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
666         /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
667         g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
668         g_return_val_if_fail(Bcb!=NULL,FALSE);
669         g_return_val_if_fail(Buffer!=NULL,FALSE);
670
671         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
672                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
673
674         if (!(Flags&PIN_IF_BCB)) {
675                 errbool=CcMapData(FileObject,FileOffset,Length,
676                                 0               /* Flags */
677                                                 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
678                                 Bcb,Buffer);
679                 g_return_val_if_fail(errbool==TRUE,FALSE);
680                 count_CcMapData=TRUE;
681                 }
682         else
683                 count_CcMapData=FALSE;
684
685         errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
686         if (!(Flags&PIN_IF_BCB)) {
687                 g_return_val_if_fail(errbool==TRUE,FALSE);
688                 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
689                 }
690         else {
691                 if (errbool==FALSE)     /* FALSE permitted; We may fail if Bcb does not exist yet. */
692                         return FALSE;
693                 *Bcb=Bcb_CcPinMappedData;
694                 }
695
696         privbcb=captive_privbcb_find(FileObject,FileOffset,Length);
697         g_assert(privbcb!=NULL);
698         g_assert(privbcb->ref_count>=1);
699         if (count_CcMapData) {
700                 /* CcPinRead() must always reference-count only by 1 despite any sub-called functions! */
701                 g_assert(privbcb->ref_count>=2);
702                 privbcb->ref_count--;
703                 }
704
705         return TRUE;
706 }
707
708
709 static void logging_notify_privbcb_flush(struct private_bcb *privbcb);
710
711 static void captive_privbcb_flush(struct private_bcb *privbcb)
712 {
713 MDL *Mdl;
714 KEVENT Event;
715 IO_STATUS_BLOCK IoStatus;
716 NTSTATUS err;
717 gpointer base_sectoraligned;
718 gsize length_sectoraligned;
719 LARGE_INTEGER FileOffset_sectoraligned;
720 gsize sectorsize;
721
722         if (!privbcb->dirty)
723                 return;
724
725         logging_notify_privbcb_flush(privbcb);
726
727         g_assert(privbcb->FileObject->DeviceObject!=NULL);
728
729         /* We can get 0=='privbcb->FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
730          * FIXME: We are unable to find the correct sectorsize for a filesystem as
731          * even the 'CommonFcb' below contains invalid information.
732          * As we need to have 'sectorsize' <=filesystem_blocksize at least for ext2fsd.sys
733          * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
734          * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
735          */
736         sectorsize=PAGE_SIZE;
737 #if 0
738         sectorsize=privbcb->FileObject->DeviceObject->SectorSize;
739         if (privbcb->FileObject->FsContext) {
740 REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileObject->FsContext;
741
742                 /* FIXME: Check CommonFcb->Type */
743                 /* 'AllocationSize' can be less than 'sectorsize' if the total file length is smaller.
744                  * Observed with ext2fsd.sys volume-file.
745                  */
746                 if (sectorsize<CommonFcb->AllocationSize.QuadPart)
747                         sectorsize=CommonFcb->AllocationSize.QuadPart;
748                 }
749 #endif
750
751         /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
752         g_assert(sectorsize>0);
753         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,sectorsize));
754         /* We align here directly the 'privbcb->base' which is not correct.
755          * We should rather aligned according to 'privbcb->MappedOffset' but
756          * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
757          * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
758          */
759         g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
760                                  ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,sectorsize));
761         base_sectoraligned  =CAPTIVE_ROUND_DOWN(privbcb->base,sectorsize);
762         length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,sectorsize)
763                         -((char *)base_sectoraligned);
764         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,sectorsize));
765         FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,sectorsize);
766
767         Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
768         g_assert(Mdl!=NULL);
769
770         KeInitializeEvent(&Event,NotificationEvent,FALSE);
771
772         /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
773         /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
774         err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
775         g_assert(NT_SUCCESS(err));
776         g_assert(NT_SUCCESS(IoStatus.Status));
777         /* We should write at least the unaligned mapped data although we
778          * do not need to successfuly write the whole aligned amount.
779          * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
780          * during FAT write by fastfat.sys.
781          */
782         g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
783                         +privbcb->MappedLength);
784         g_assert(IoStatus.Information<=length_sectoraligned);
785
786         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
787                         "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
788                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
789                         base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
790                         (gulong)IoStatus.Information);
791
792         privbcb->dirty=FALSE;
793 }
794
795
796 static gboolean CcUnpinData_idle_func(struct private_bcb *privbcb /*data*/)
797 {
798 gboolean errbool;
799
800         g_return_val_if_fail(privbcb!=NULL,FALSE);      /* remove-me */
801         g_return_val_if_fail(privbcb->PublicBcb!=NULL,FALSE);   /* remove-me */
802
803         g_assert(privbcb->ref_count>0);
804
805         if (--privbcb->ref_count)
806                 return FALSE;   /* remove-me */
807
808         errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
809         g_assert(errbool==TRUE);
810
811         return FALSE;   /* remove-me */
812 }
813
814 /**
815  * CcUnpinData:
816  * @Bcb: Initialized #PUBLIC_BCB structure.
817  * %NULL value is forbidden.
818  *
819  * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
820  */
821 VOID CcUnpinData(IN PVOID Bcb)
822 {
823 PUBLIC_BCB *PublicBcb;
824 struct private_bcb *privbcb;
825
826         g_return_if_fail(validate_Bcb(Bcb));
827
828         private_bcb_hash_init();
829
830         PublicBcb=(PUBLIC_BCB *)Bcb;
831         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
832         g_return_if_fail(privbcb!=NULL);
833
834         g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
835         /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
836
837         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
838                         "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d",G_STRLOC,
839                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count);
840
841         g_assert(privbcb->ref_count>0);
842         /* Do not write back the contents if this is not the final unpin.
843          * FIXME: Is it correct?
844          */
845         if (--privbcb->ref_count)
846                 return;
847         privbcb->ref_count++;   /* will get decreased again in CcUnpinData_idle_func() */
848
849         captive_privbcb_flush(privbcb);
850
851         g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
852         /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
853         if (privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb)
854                 privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
855
856         /* Caboom: lfs (log file system) of ntfs.sys-NT5.1sp1 will do:
857          * CcPinRead(); CcUnpinData(); access Buffer (wanna-crash);
858          * Therefore we must postpone the buffer unmapping to some idle function...
859          * I expect it a bug in ntfs.sys.
860          */
861         g_idle_add_full(
862                         G_PRIORITY_DEFAULT_IDLE,        /* priority */
863                         (GSourceFunc)CcUnpinData_idle_func,     /* function */
864                         privbcb,        /* data */
865                         NULL);  /* notify */
866 }
867
868
869 VOID CcRepinBcb(IN PVOID Bcb)
870 {
871 PUBLIC_BCB *PublicBcb;
872 struct private_bcb *privbcb;
873
874         g_return_if_fail(validate_Bcb(Bcb));
875
876         private_bcb_hash_init();
877
878         PublicBcb=(PUBLIC_BCB *)Bcb;
879         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
880         g_return_if_fail(privbcb!=NULL);
881
882         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
883                         Bcb,privbcb->FileObject);
884
885         privbcb->ref_count++;
886 }
887
888
889 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
890 {
891 PUBLIC_BCB *PublicBcb;
892 struct private_bcb *privbcb;
893
894         g_return_if_fail(validate_Bcb(Bcb));
895         g_return_if_fail(IoStatus!=NULL);
896
897         private_bcb_hash_init();
898
899         PublicBcb=(PUBLIC_BCB *)Bcb;
900         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
901         g_return_if_fail(privbcb!=NULL);
902
903         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
904                         Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
905
906         IoStatus->Status=STATUS_SUCCESS;
907         IoStatus->Information=privbcb->MappedLength;
908
909         CcUnpinData(Bcb);
910 }
911
912
913 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
914 {
915 PUBLIC_BCB *PublicBcb;
916 struct private_bcb *privbcb;
917
918         g_return_if_fail(validate_Bcb(Bcb));
919
920         private_bcb_hash_init();
921
922         PublicBcb=(PUBLIC_BCB *)Bcb;
923         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
924         g_return_if_fail(privbcb!=NULL);
925
926         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
927                         Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
928
929         /* 'privbcb->ref_count' not to be increased by this function. */
930
931         if (Lsn) {
932                 privbcb->lsn=*Lsn;
933                 privbcb->lsn_valid=TRUE;
934                 }
935
936         privbcb->dirty=TRUE;
937 }
938
939
940 /**
941  * CcSetFileSizes:
942  * @FileObject: Initialized open #FileObject to update file sizes of.
943  * %NULL value is forbidden.
944  * @FileSizes: New file sizes to update cache to.
945  * %NULL value is forbidden.
946  * 
947  * Update cache properties after file sizes were updated.
948  * Probably only the exceeding pages need to be unmapped and BCBs updated
949  * if FileSizes->AllocationSize gets shrunk.
950  *
951  * FIXME: Currently a NOP with no effect by libcaptive.
952  */
953 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
954 {
955         g_return_if_fail(FileObject!=NULL);
956         g_return_if_fail(FileSizes!=NULL);
957
958         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
959                         "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
960                         FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
961                         (guint64)FileSizes->ValidDataLength.QuadPart);
962
963         /* FIXME: check BCB && 'struct page_position' invalidities */
964 }
965
966
967 /**
968  * CcPurgeCacheSection:
969  * @SectionObjectPointer: Pointer specifying file to purge;
970  * %NULL value is forbidden.
971  * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
972  * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %FALSE return code in such case.
973  * @FileOffset: Starting offset of the ranger to purge.
974  * %NULL pointer is permitted and it means to purge the whole whole.
975  * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
976  * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
977  * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
978  *
979  * Drop any caching for shrunken file which is not being deleted.
980  * libcaptive will no longer consider such #BCB as dirty.
981  *
982  * Returns: %TRUE if the range was purged successfuly.
983  */
984 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
985                 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
986 {
987 PUBLIC_BCB *PublicBcb;
988 struct private_bcb *privbcb;
989
990         g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
991         if (SectionObjectPointer->SharedCacheMap==NULL)
992                 return FALSE;   /* failed - nothing to purge */
993         g_return_val_if_fail(FileOffset==NULL,FALSE);   /* NOT IMPLEMENTED YET */
994
995         PublicBcb=SectionObjectPointer->SharedCacheMap;
996         g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
997
998         private_bcb_hash_init();
999
1000         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1001         g_return_val_if_fail(privbcb!=NULL,FALSE);
1002
1003         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1004                         "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
1005                         SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1006                         (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
1007
1008         privbcb->dirty=FALSE;   /* purge it */
1009
1010         return TRUE;
1011 }
1012
1013
1014 /**
1015  * CcCopyRead:
1016  * @FileObject: Initialized open #FileObject to map.
1017  * %NULL value is forbidden.
1018  * @FileOffset: The @FileObject file offset from where to map the region from.
1019  * Negative value is forbidden.
1020  * @Length: Requested length of the region to map from @FileObject.
1021  * Value %0 is permitted (no effect of this function call).
1022  * @Wait: Whether disk waiting is permitted for this function.
1023  * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1024  * @Buffer: Address of memory region with already allocated memory of size @Length.
1025  * This address may not be %PAGE_SIZE aligned.
1026  * %NULL pointer is forbidden.
1027  * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1028  * %NULL pointer is forbidden.
1029  *
1030  * Reads the specified region of @FileObject to the given @Buffer.
1031  * No on-demand loading is in effect.
1032  *
1033  * Returns: %TRUE if the region was successfuly filled with @Length bytes.
1034  * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
1035  * @IoStatus.Information initialized by @Length if successful.
1036  */
1037 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
1038                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
1039 {
1040 PVOID MappedBcb;
1041 PVOID MappedBuffer;
1042 gboolean errbool;
1043
1044         g_return_val_if_fail(FileObject!=NULL,FALSE);
1045         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1046         g_return_val_if_fail(Buffer!=NULL,FALSE);
1047         g_return_val_if_fail(IoStatus!=NULL,FALSE);
1048
1049         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1050                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1051
1052         IoStatus->Status=STATUS_UNSUCCESSFUL;
1053         IoStatus->Information=0;
1054
1055         if (Length) {
1056                 errbool=CcPinRead(
1057                                 FileObject,     /* FileObject */
1058                                 FileOffset,     /* FileOffset */
1059                                 Length, /* Length */
1060                                 Wait,   /* Wait */
1061                                 &MappedBcb,     /* Bcb */
1062                                 &MappedBuffer); /* Buffer */
1063                 g_return_val_if_fail(errbool==TRUE,FALSE);
1064
1065                 memcpy(Buffer,MappedBuffer,Length);
1066
1067                 CcUnpinData(MappedBcb); /* no error code */
1068                 }
1069
1070         IoStatus->Status=STATUS_SUCCESS;
1071         IoStatus->Information=Length;
1072
1073         return TRUE;
1074 }
1075
1076
1077 /**
1078  * CcCopyWrite:
1079  * @FileObject: Initialized open #FileObject to map.
1080  * %NULL value is forbidden.
1081  * @FileOffset: The @FileObject file offset from where to map the region from.
1082  * Negative value is forbidden.
1083  * @Length: Requested length of the region to map from @FileObject.
1084  * Value %0 is permitted (no effect of this function call).
1085  * @Wait: Whether disk waiting is permitted for this function.
1086  * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1087  * @Buffer: Address of memory region with already allocated memory of size @Length.
1088  * This address may not be %PAGE_SIZE aligned.
1089  * %NULL pointer is forbidden.
1090  *
1091  * Writes the specified region of the given @Buffer to @FileObject.
1092  *
1093  * Returns: %TRUE if the region was successfuly written with @Length bytes.
1094  */
1095 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
1096                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
1097 {
1098 PVOID MappedBcb;
1099 PVOID MappedBuffer;
1100 gboolean errbool;
1101
1102         g_return_val_if_fail(FileObject!=NULL,FALSE);
1103         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1104         g_return_val_if_fail(Buffer!=NULL,FALSE);
1105
1106         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1107                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1108
1109         if (Length) {
1110                 errbool=CcPinRead(
1111                                 FileObject,     /* FileObject */
1112                                 FileOffset,     /* FileOffset */
1113                                 Length, /* Length */
1114                                 Wait,   /* Wait */
1115                                 &MappedBcb,     /* Bcb */
1116                                 &MappedBuffer); /* Buffer */
1117                 g_return_val_if_fail(errbool==TRUE,FALSE);
1118
1119                 memcpy(MappedBuffer,Buffer,Length);
1120
1121                 CcSetDirtyPinnedData(
1122                                 MappedBcb,      /* Bcb */
1123                                 NULL);  /* Lsn */
1124                 CcUnpinData(MappedBcb); /* no error code */
1125                 }
1126
1127         return TRUE;
1128 }
1129
1130
1131 /**
1132  * CcCanIWrite:
1133  * @FileObject: Initialized open #FileObject to map.
1134  * %NULL value is forbidden.
1135  * @BytesToWrite: Amount of data to be asked whether it will be accepted.
1136  * Value %0 is permitted.
1137  * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
1138  * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
1139  *
1140  * Asks cache manager if it would currently accept write request to @FileObject
1141  * of @BytesToWrite bytes with @Wait condition.
1142  * libcaptive will always accept any writes. This function is a NOP.
1143  *
1144  * Returns: libcaptive always returns %TRUE.
1145  */
1146 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
1147 {
1148         g_return_val_if_fail(FileObject!=NULL,FALSE);
1149
1150         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
1151                         FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
1152
1153         return TRUE;
1154 }
1155
1156
1157 /**
1158  * CcSetReadAheadGranularity:
1159  * @FileObject: Initialized open #FileObject to map.
1160  * %NULL value is forbidden.
1161  * @Granularity: Suggested size of the cache element.
1162  * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
1163  *
1164  * libcaptive does not implement any caching and therefore this function
1165  * is a NOP there.
1166  */
1167 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
1168 {
1169         g_return_if_fail(FileObject!=NULL);
1170         g_return_if_fail(Granularity>=PAGE_SIZE);
1171         g_return_if_fail((Granularity&(Granularity-1))==0);     /* Power of two */
1172
1173         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
1174                         FileObject,(gulong)Granularity);
1175
1176         /* NOP; no caching by libcaptive */
1177 }
1178
1179
1180 /**
1181  * CcFlushCache:
1182  * @SectionObjectPointer: Pointer specifying file to flush;
1183  * %NULL value is forbidden.
1184  * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1185  * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
1186  * @FileOffset: Optional starting point of the range to flush.
1187  * %NULL value is permitted.
1188  * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
1189  * @IoStatus: Optionally returns the resulting operation status.
1190  * #Information field will contain the number of bytes flushed.
1191  * %NULL value is permitted.
1192  *
1193  * Flushes out any pending dirty data in cache manager BCB mapping.
1194  * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
1195  */
1196 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1197                 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
1198 {
1199 PUBLIC_BCB *PublicBcb;
1200 struct private_bcb *privbcb;
1201 IO_STATUS_BLOCK IoStatus_CcUnpinRepinnedBcb_local;
1202
1203         g_return_if_fail(SectionObjectPointer!=NULL);
1204
1205         if (SectionObjectPointer->SharedCacheMap==NULL) {
1206                 if (IoStatus) {
1207                         IoStatus->Status=STATUS_SUCCESS;
1208                         IoStatus->Information=0;
1209                         }
1210                 return;
1211                 }
1212
1213         PublicBcb=SectionObjectPointer->SharedCacheMap;
1214         g_return_if_fail(validate_Bcb(PublicBcb));
1215
1216         private_bcb_hash_init();
1217
1218         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1219         g_return_if_fail(privbcb!=NULL);
1220
1221         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1222                         "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
1223                         SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1224                         (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
1225
1226         if (FileOffset) {
1227                 g_assert(FileOffset->QuadPart       >=privbcb->MappedFileOffset.QuadPart);
1228                 g_assert(FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
1229                 }
1230
1231         /* We may find some 'privbcb' being already sheduled to be destroyed.
1232          * We need to reference it to not to loose it in the middle of our flush operation.
1233          */
1234         CcRepinBcb(PublicBcb);
1235
1236         /* FIXME: Flush just FileOffset..FileOfset+Length part */
1237         captive_privbcb_flush(privbcb);
1238
1239         CcUnpinRepinnedBcb(
1240                         PublicBcb,      /* Bcb */
1241                         TRUE,   /* WriteThrough; ignored by libcaptive */
1242                         &IoStatus_CcUnpinRepinnedBcb_local);    /* IoStatus; ignored here */
1243
1244         if (IoStatus) {
1245                 IoStatus->Status=STATUS_SUCCESS;
1246                 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
1247                 }
1248 }
1249
1250
1251 static gboolean captive_FileObject_ZeroData(PFILE_OBJECT FileObject,guint64 window_bottom,guint64 window_top)
1252 {
1253 MDL *Mdl;
1254 KEVENT Event;
1255 gpointer zerobuffer;
1256 LARGE_INTEGER window_bottom_LargeInteger;
1257 NTSTATUS err;
1258 IO_STATUS_BLOCK IoStatus;
1259
1260         g_return_val_if_fail(FileObject!=NULL,FALSE);
1261         g_return_val_if_fail(window_bottom<=window_top,FALSE);
1262
1263         if (window_bottom==window_top)  /* trivia */
1264                 return TRUE;
1265
1266         g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
1267
1268         /* Such requirement is W32 documented for CcZeroData()
1269          * and it is really a requirement at least for the underlying fastfat.sys.
1270          */
1271         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(window_bottom,FileObject->DeviceObject->SectorSize));
1272         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(window_top   ,FileObject->DeviceObject->SectorSize));
1273
1274         zerobuffer=g_malloc0(window_top-window_bottom);
1275         window_bottom_LargeInteger.QuadPart=window_bottom;
1276
1277         Mdl=MmCreateMdl(NULL,zerobuffer,window_top-window_bottom);
1278         g_assert(Mdl!=NULL);
1279
1280         KeInitializeEvent(&Event,NotificationEvent,FALSE);
1281
1282         /* FIXME: Notify logging subsystem by LSNs? */
1283
1284         /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
1285         err=IoSynchronousPageWrite(FileObject,Mdl,&window_bottom_LargeInteger,&Event,&IoStatus);
1286         g_assert(NT_SUCCESS(err));
1287         g_assert(NT_SUCCESS(IoStatus.Status));
1288         /* We should write up to the last sector touched by the range although we
1289          * do not need to successfuly write the whole aligned amount.
1290          * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
1291          * during FAT write by fastfat.sys.
1292          */
1293         g_assert(IoStatus.Information==0
1294                         || (window_top-window_bottom)==CAPTIVE_ROUND_UP(IoStatus.Information,FileObject->DeviceObject->SectorSize));
1295         g_assert(IoStatus.Information<=window_top-window_bottom);       /* redundant */
1296
1297         g_free(zerobuffer);
1298
1299         return TRUE;
1300 }
1301
1302
1303 BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
1304 {
1305 guint64 window_lower_bottom,window_lower_top;
1306 guint64 window_higher_bottom,window_higher_top;
1307
1308         g_return_val_if_fail(FileObject!=NULL,FALSE);
1309         g_return_val_if_fail(StartOffset!=NULL,FALSE);
1310         g_return_val_if_fail(EndOffset!=NULL,FALSE);
1311         g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
1312
1313         g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
1314
1315         window_lower_bottom=StartOffset->QuadPart;
1316         window_lower_top=EndOffset->QuadPart;
1317         window_higher_bottom=window_higher_top=0;
1318
1319         if (FileObject->SectionObjectPointers->SharedCacheMap!=NULL) {
1320 PUBLIC_BCB *PublicBcb;
1321 struct private_bcb *privbcb;
1322 guint64 window_cached_bottom,window_cached_top;
1323
1324                 PublicBcb=FileObject->SectionObjectPointers->SharedCacheMap;
1325                 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1326
1327                 private_bcb_hash_init();
1328
1329                 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1330                 g_return_val_if_fail(privbcb!=NULL,FALSE);
1331
1332                 window_cached_bottom=MAX(StartOffset->QuadPart,privbcb->MappedFileOffset.QuadPart);
1333                 window_cached_top=MIN(EndOffset->QuadPart,privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
1334
1335                 if (window_cached_bottom<window_cached_top) {
1336                         memset(((char *)privbcb->base)+window_cached_bottom-privbcb->MappedFileOffset.QuadPart,0,window_cached_top-window_cached_bottom);
1337                         privbcb->dirty=TRUE;
1338                         window_higher_top=window_lower_top;
1339                         window_lower_top=window_cached_bottom;
1340                         window_higher_bottom=window_cached_top;
1341                         }
1342                 }
1343         if (window_lower_bottom<window_lower_top) {
1344                 g_return_val_if_fail(Wait==TRUE,FALSE);
1345                 if (!captive_FileObject_ZeroData(FileObject,window_lower_bottom,window_lower_top))
1346                         g_return_val_if_reached(FALSE);
1347                 }
1348         if (window_higher_bottom<window_higher_top) {
1349                 g_return_val_if_fail(Wait==TRUE,FALSE);
1350                 if (!captive_FileObject_ZeroData(FileObject,window_higher_bottom,window_higher_top))
1351                         g_return_val_if_reached(FALSE);
1352                 }
1353
1354         return TRUE;
1355 }
1356
1357
1358 /* map (PVOID LogHandle) -> (GList (of FILE_OBJECT *) *FileObject_list) */
1359 static GHashTable *log_handle_hash;
1360
1361 static void log_handle_hash_init(void)
1362 {
1363         if (log_handle_hash)
1364                 return;
1365         log_handle_hash=g_hash_table_new(
1366                         g_direct_hash,  /* hash_func */
1367                         g_direct_equal);        /* key_equal_func */
1368 }
1369
1370 /* map (FILE_OBJECT *FileObject) -> (struct FileObject_logging *) */
1371 struct FileObject_logging {
1372         PVOID LogHandle;
1373         PFLUSH_TO_LSN FlushToLsnRoutine;
1374         };
1375
1376 static GHashTable *FileObject_logging_hash;
1377
1378 static void FileObject_logging_hash_init(void)
1379 {
1380         if (FileObject_logging_hash)
1381                 return;
1382         FileObject_logging_hash=g_hash_table_new(
1383                         g_direct_hash,  /* hash_func */
1384                         g_direct_equal);        /* key_equal_func */
1385 }
1386
1387 static void logging_notify_privbcb_flush(struct private_bcb *privbcb)
1388 {
1389 struct FileObject_logging *FileObject_logging;
1390
1391         g_return_if_fail(privbcb!=NULL);
1392
1393         if (!privbcb->lsn_valid)        /* nothing to report anyway */
1394                 return;
1395
1396         FileObject_logging_hash_init();
1397         
1398         if (!(FileObject_logging=g_hash_table_lookup(FileObject_logging_hash,privbcb->FileObject)))
1399                 return;
1400         (*FileObject_logging->FlushToLsnRoutine)(FileObject_logging->LogHandle,privbcb->lsn);
1401 }
1402
1403 VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
1404 {
1405 GList *LogHandle_list;
1406 struct FileObject_logging *FileObject_logging;
1407
1408         g_return_if_fail(FileObject!=NULL);
1409         /* 'LogHandle' may be NULL */
1410         g_return_if_fail(FlushToLsnRoutine!=NULL);
1411
1412         log_handle_hash_init();
1413         FileObject_logging_hash_init();
1414
1415         if (!(FileObject_logging=g_hash_table_lookup(
1416                         FileObject_logging_hash,FileObject))) {
1417                 captive_new(FileObject_logging);
1418                 g_hash_table_insert(FileObject_logging_hash,
1419                                 FileObject,FileObject_logging);
1420                 }
1421         else {
1422                 LogHandle_list=g_hash_table_lookup(log_handle_hash,FileObject_logging->LogHandle);
1423                 g_assert(NULL!=g_list_find(LogHandle_list,FileObject));
1424                 LogHandle_list=g_list_remove(LogHandle_list,FileObject);
1425                 g_assert(NULL==g_list_find(LogHandle_list,FileObject));
1426                 }
1427
1428         FileObject_logging->LogHandle=LogHandle;
1429         FileObject_logging->FlushToLsnRoutine=FlushToLsnRoutine;
1430
1431         LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
1432         LogHandle_list=g_list_prepend(LogHandle_list,FileObject);
1433         g_hash_table_insert(log_handle_hash,LogHandle,LogHandle_list);
1434 }
1435
1436
1437 struct CcGetDirtyPages_param {
1438         PDIRTY_PAGE_ROUTINE DirtyPageRoutine;   /* arg of CcGetDirtyPages() */
1439         IN PVOID Context1;      /* arg of CcGetDirtyPages() */
1440         IN PVOID Context2;      /* arg of CcGetDirtyPages() */
1441         FILE_OBJECT *FileObject;        /* search through 'page_position_hash' for 'FileObject' */
1442         LARGE_INTEGER OldestLsn; gboolean OldestLsn_found;      /* intermediate return value of CcGetDirtyPages() */
1443         };
1444
1445 static void CcGetDirtyPages_page_position_hash_foreach(
1446                 struct page_position *pagepos,  /* key */
1447                 struct page_position *pagepos2, /* value */
1448                 struct CcGetDirtyPages_param *CcGetDirtyPages_param)    /* user_data */
1449 {
1450 LARGE_INTEGER FileOffset_local;
1451 LARGE_INTEGER OldestLsn,NewestLsn;
1452 LARGE_INTEGER OldestLsn_check,NewestLsn_check;
1453 gboolean lsn_found=FALSE;
1454 GList *privbcb_list;
1455
1456         g_return_if_fail(pagepos!=NULL);
1457         g_return_if_fail(pagepos==pagepos2);
1458         g_return_if_fail(CcGetDirtyPages_param!=NULL);
1459
1460         FileOffset_local=pagepos->FileOffset;
1461
1462         for (
1463                         privbcb_list=pagepos->privbcb_list;
1464                         privbcb_list;
1465                         privbcb_list=privbcb_list->next) {
1466 struct private_bcb *privbcb=privbcb_list->data;
1467
1468                 if (!privbcb->lsn_valid)
1469                         return;
1470                 if (!lsn_found) {
1471                         OldestLsn=NewestLsn=privbcb->lsn;
1472                         continue;
1473                         }
1474                 OldestLsn.QuadPart=MIN(OldestLsn.QuadPart,privbcb->lsn.QuadPart);
1475                 NewestLsn.QuadPart=MAX(NewestLsn.QuadPart,privbcb->lsn.QuadPart);
1476                 }
1477
1478         if (!lsn_found)
1479                 return;
1480
1481         if (!CcGetDirtyPages_param->OldestLsn_found) {
1482                 CcGetDirtyPages_param->OldestLsn=OldestLsn;
1483                 CcGetDirtyPages_param->OldestLsn_found=TRUE;
1484                 }
1485         else
1486                 CcGetDirtyPages_param->OldestLsn.QuadPart=MIN(CcGetDirtyPages_param->OldestLsn.QuadPart,OldestLsn.QuadPart);
1487
1488         OldestLsn_check=OldestLsn;
1489         NewestLsn_check=NewestLsn;
1490
1491         (*CcGetDirtyPages_param->DirtyPageRoutine)(
1492                         pagepos->FileObject,    /* FileObject */
1493                         &FileOffset_local,      /* FileOffset */
1494                         PAGE_SIZE,      /* Length */
1495                         &OldestLsn,     /* OldestLsn */
1496                         &NewestLsn,     /* NewestLsn */
1497                         CcGetDirtyPages_param->Context1,        /* Context1 */
1498                         CcGetDirtyPages_param->Context2);       /* Context2 */
1499
1500         /* just unconfirmed sanity: */
1501         g_assert(FileOffset_local.QuadPart==pagepos->FileOffset.QuadPart);      /* check for possible corruption */
1502         g_assert(OldestLsn_check.QuadPart==OldestLsn.QuadPart);
1503         g_assert(NewestLsn_check.QuadPart==NewestLsn.QuadPart);
1504 }
1505
1506 /* libcaptive must return #gint64 instead of the official #LARGE_INTEGER
1507  * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
1508  */
1509 gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
1510                 IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
1511 {
1512 GList *LogHandle_list;
1513 struct CcGetDirtyPages_param CcGetDirtyPages_param;
1514
1515         /* 'LogHandle' may be NULL */
1516         g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
1517
1518         log_handle_hash_init();
1519         page_position_hash_init();
1520
1521         CcGetDirtyPages_param.DirtyPageRoutine=DirtyPageRoutine;
1522         CcGetDirtyPages_param.Context1=Context1;
1523         CcGetDirtyPages_param.Context2=Context2;
1524         CcGetDirtyPages_param.OldestLsn_found=FALSE;
1525
1526         for (
1527                         LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
1528                         LogHandle_list;
1529                         LogHandle_list=LogHandle_list->next) {
1530                 CcGetDirtyPages_param.FileObject=LogHandle_list->data;
1531                 g_hash_table_foreach(
1532                                 page_position_hash,     /* hash_table */
1533                                 (GHFunc)CcGetDirtyPages_page_position_hash_foreach,     /* func */
1534                                 &CcGetDirtyPages_param);        /* user_data */
1535                 }
1536
1537         if (!CcGetDirtyPages_param.OldestLsn_found)
1538                 return 0;
1539         return CcGetDirtyPages_param.OldestLsn.QuadPart;
1540 }
1541
1542
1543 /**
1544  * CcSetAdditionalCacheAttributes:
1545  * @FileObject: Initialized open #FileObject to map.
1546  * %NULL value is forbidden.
1547  * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
1548  * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
1549  *
1550  * libcaptive does not implement any caching and therefore this function
1551  * is a NOP there.
1552  */
1553 VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
1554 {
1555         g_return_if_fail(FileObject!=NULL);
1556
1557         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
1558                         FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
1559
1560         /* NOP; no caching by libcaptive */
1561 }
1562
1563
1564 VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
1565 {
1566         g_return_if_fail(Bcb!=NULL);
1567         g_return_if_fail(Owner!=NULL);
1568
1569         /* FIXME:thread; NOP if no threads present */
1570 }