f5319daef76a05e3a2993f0f3558ee993d47145c
[captive.git] / src / libcaptive / cc / map.c
1 /* $Id$
2  * reactos Cache Manager mapper emulation of libcaptive
3  * Copyright (C) 2002-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 "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/gmain.h>
37 #include "captive/leave.h"
38 #include <glib/gtree.h>
39
40
41 /* CONFIG: */
42
43 #define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45  /* FIXME: unknown, undocumented */
44 /* Prepare linear mapped space by CcInitializeCacheMap()?
45  * NT-5.1sp will map the same pages of a file to the same memory location
46  * either by CcMap*() or CcPin*().
47  * We do a separate mapping for each Bcb protected by bounding unmapped pages
48  * with IPC shm shared pages if a file page is mapped multiple times.
49  * I am not aware how they can reserve enough virtual memory space during
50  * client with mapping requests of increasing range tendency.
51  * This define will try to pre-map the file as one linear are during 
52  * CcInitializeCacheMap() according to its (CC_FILE_SIZES *)FileSizes
53  * but TODO the range is currently left unused in further mapping functions.
54  */
55 /* #define CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR 1 */
56
57
58 /* Flush all the buffers directly on the fly.
59  * For details see the comment in libcaptive/client/init.c/captive_shutdown().
60  */
61 gboolean captive_cc_unmounting=FALSE;
62
63
64 /* map: (FILE_OBJECT *)FileObject -> (struct fileobject_cached *) */
65 static GHashTable *fileobject_cached_hash;
66
67 struct fileobject_cached {
68         CACHE_MANAGER_CALLBACKS CallBacks;
69         VOID *LazyWriterContext;
70 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
71         PVOID Bcb;
72 #endif
73         };
74
75 static void fileobject_cached_hash_value_destroy_func(struct fileobject_cached *value)
76 {
77         g_return_if_fail(value!=NULL);
78
79         g_free(value);
80 }
81
82 static void fileobject_cached_hash_init(void)
83 {
84         if (fileobject_cached_hash)
85                 return;
86         fileobject_cached_hash=g_hash_table_new_full(
87                         g_direct_hash,  /* hash_func */
88                         g_direct_equal, /* key_equal_func */
89                         NULL,   /* key_destroy_func */
90                         (GDestroyNotify)fileobject_cached_hash_value_destroy_func);     /* value_destroy_func */
91 }
92
93
94 /**
95  * CcInitializeCacheMap:
96  * @FileObject: Existing file to set callbacks for. Ignored by libcaptive.
97  * %NULL value is forbidden.
98  * @FileSizes: Some file sizes suggestions. Ignored by libcaptive.
99  * %NULL value is forbidden.
100  * @PinAccess: CcPin*() functions will be used with @FileObject? Ignored by libcaptive.
101  * @CallBacks: Provided callback functions for readahead/writebehind. Ignored by libcaptive.
102  * %NULL value is forbidden.
103  * @LazyWriterContext: Value passed to functions of @CallBacks to bind with @FileObject.
104  * %NULL value is permitted.
105  *
106  * Provides support of readahead/writebehind in W32. Function should be called
107  * by W32 filesystem to offer its functions to W32 kernel. These functions
108  * are never called by libcaptive, this call is a NOP in libcaptive.
109  */
110 VOID CcInitializeCacheMap(IN PFILE_OBJECT FileObject,
111                 IN PCC_FILE_SIZES FileSizes,IN BOOLEAN PinAccess,IN PCACHE_MANAGER_CALLBACKS CallBacks,IN PVOID LazyWriterContext)
112 {
113 struct fileobject_cached *fileobject_cached;
114 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
115 guint64 size64;
116 LARGE_INTEGER FileOffset0;
117 PVOID Buffer_trash;
118 BOOLEAN errboolean;
119 #endif
120
121         g_return_if_fail(FileObject!=NULL);
122         g_return_if_fail(FileSizes!=NULL);
123         g_return_if_fail(CallBacks!=NULL);
124
125         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
126                         "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX,"
127                         "PinAccess=%d,CallBacks=%p,LazyWriterContext=%p",G_STRLOC,
128                         FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
129                         (guint64)FileSizes->ValidDataLength.QuadPart,(gint)PinAccess,CallBacks,LazyWriterContext);
130
131         fileobject_cached_hash_init();
132
133         /* TODO:thread */
134
135         /* Double reinitialization without CcUninitializeCacheMap()
136          * is done by both fastfat.sys and ntfs.sys of NT-5.1sp1.
137          */
138 #if 0
139         g_assert(g_hash_table_lookup(fileobject_cached_hash,FileObject)==NULL);
140 #endif
141
142         captive_new(fileobject_cached);
143         fileobject_cached->CallBacks=*CallBacks;
144         fileobject_cached->LazyWriterContext=LazyWriterContext;
145
146         g_hash_table_insert(fileobject_cached_hash,
147                         FileObject,     /* key */
148                         fileobject_cached);     /* value */
149
150 #if CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
151         size64=MAX(MAX(FileSizes->AllocationSize.QuadPart,FileSizes->FileSize.QuadPart),
152                         (FileSizes->ValidDataLength.QuadPart==G_MAXINT64 ? 0 : FileSizes->ValidDataLength.QuadPart));
153         g_assert(((ULONG)size64)==size64);
154         FileOffset0.QuadPart=0;
155
156         errboolean=CcMapData(FileObject,&FileOffset0,size64,MAP_WAIT,&fileobject_cached->Bcb,&Buffer_trash);
157         g_assert(errboolean==TRUE);
158 #endif
159 }
160
161
162 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject);
163
164 /**
165  * CcUninitializeCacheMap:
166  * @FileObject: File to close caching for.
167  * %NULL value is forbidden.
168  * @TruncateSize: Current file size if @FileObject was truncated to it in the meantime.
169  * @UninitializeCompleteEvent: Optional event to signal after physical write to disk.
170  * %NULL value is permitted.
171  *
172  * Close the cachine facilities from CcInitializeCacheMap().
173  * It is valid to pass @FileObject without being registered by CcInitializeCacheMap().
174  *
175  * FIXME; What to do with files with dirty blocks? Currently we fail assertion on them
176  * although I think W32 would drop such buffers (purge them - not flush them).
177  *
178  * Returns: %TRUE if the caching was closed successfuly.
179  * %FALSE if @FileObject wasn't registered by CcInitializeCacheMap().
180  */
181 BOOLEAN CcUninitializeCacheMap(IN PFILE_OBJECT FileObject,
182                 IN PLARGE_INTEGER TruncateSize OPTIONAL,IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
183 {
184 BOOLEAN r;
185
186         g_return_val_if_fail(FileObject!=NULL,FALSE);
187         /* assert current size ==*TruncateSize if TruncateSize */
188
189         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,TruncateSize=0x%llX,UninitializeCompleteEvent=%p",
190                         G_STRLOC,FileObject,(guint64)(!TruncateSize ? -1 : TruncateSize->QuadPart),UninitializeCompleteEvent);
191
192         /* TODO:thread */
193         /* Here it will check for any dirty blocks and it will fail g_assert() on them.
194          * I think it may happen for BCBs to exist dirty for 'FileObject' and they
195          * should be silently discarded if CcUninitializeCacheMap() but currently
196          * the assertion is left here as some debugging aid.
197          */
198         r=captive_cc_FileObject_delete(FileObject);
199
200         /* FIXME: should we do KePulseEvent? Are we allowed to signal from inside CcUninitializeCacheMap() ? */
201         if (UninitializeCompleteEvent)
202                 KeSetEvent(
203                                 &UninitializeCompleteEvent->Event,      /* Event */
204                                 IO_NO_INCREMENT,        /* Increment */
205                                 FALSE); /* Wait */
206
207         return r;
208 }
209
210
211 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
212 {
213         g_return_val_if_fail(PublicBcb!=NULL,FALSE);
214         g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
215         g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
216         g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
217         g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
218
219         return TRUE;
220 }
221
222
223 /* position in file */
224 struct page_position {
225         FILE_OBJECT *FileObject;
226         LARGE_INTEGER FileOffset;       /* always PAGE_SIZE aligned */
227         int shmid;
228         GList *privbcb_list;    /* each mapped page has its one private_bcb */
229         gboolean building;      /* data are not yet read; prevention for CcMapData() reentrancy */
230         };
231
232 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
233 static GHashTable *page_position_hash;
234
235 static gboolean validate_page_position(const struct page_position *pagepos)
236 {
237 int errint;
238 struct shmid_ds shmid_ds;
239
240         g_return_val_if_fail(pagepos!=NULL,FALSE);
241         g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
242         g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
243         g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
244         /* 'pagepos->shmid' may be -1 */
245         /* 'pagepos->privbcb_list' may be empty */
246         /* either deleted or alive */
247         /* shmid exists only for >=2 mappings, it is simple mmap() for single mapping */
248         g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL || pagepos->privbcb_list->next==NULL),FALSE);
249
250         if (pagepos->shmid!=-1) {
251                 errint=shmctl(pagepos->shmid,
252                                 IPC_STAT,       /* cmd */
253                                 &shmid_ds);     /* buf */
254                 g_return_val_if_fail(errint==0,FALSE);
255
256                 g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
257                 g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
258                 g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
259                 g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
260                 /* 'shm_perm.mode' was seen with sticky bit 01000: */
261                 g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
262                 g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
263                 /* Do not check 'shmid_ds.shm_cpid' and 'shmid_ds.shm_lpid' against getpid()
264                  * as we may run as different PIDs in different threads synchronized by client/ .
265                  */
266                 g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
267                 }
268
269         return TRUE;
270 }
271
272 static guint page_position_hash_hash_func(const struct page_position *key)
273 {
274         g_return_val_if_fail(validate_page_position(key),0);
275
276         return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
277 }
278
279 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
280 {
281         g_return_val_if_fail(validate_page_position(a),FALSE);
282         g_return_val_if_fail(validate_page_position(b),FALSE);
283
284         return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
285 }
286
287 static void page_position_hash_key_destroy_func(struct page_position *key)
288 {
289         g_return_if_fail(validate_page_position(key));
290         g_assert(key->privbcb_list==NULL);
291         g_assert(key->shmid==-1);
292
293         g_free(key);
294 }
295
296 static void page_position_hash_init(void)
297 {
298         if (page_position_hash)
299                 return;
300         page_position_hash=g_hash_table_new_full(
301                         (GHashFunc)page_position_hash_hash_func,        /* hash_func */
302                         (GEqualFunc)page_position_hash_key_equal_func,  /* key_equal_func */
303                         (GDestroyNotify)page_position_hash_key_destroy_func,    /* key_destroy_func */
304                         NULL);  /* value_destroy_func */
305 }
306
307
308 struct private_bcb {
309         PUBLIC_BCB *PublicBcb;  /* ->MappedLength, ->MappedFileOffset */
310         FILE_OBJECT *FileObject;
311         gint ref_count;
312         gboolean leave_func_pending;
313         /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
314         ULONG MappedLength;     /* It is the real requested size; it is not PAGE_SIZE aligned. */
315         /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
316         LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
317         gpointer base;  /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
318         gboolean dirty;
319         LARGE_INTEGER lsn; gboolean lsn_valid;
320         };
321
322
323 /* map: (struct private_bcb *)privbcb -> (struct private_bcb *)privbcb */
324 static GTree *private_bcb_lsn_tree;
325
326 /* Such debugging messages are too much expensive.
327  * Use to track for deallocated privbcb-s/PublicBcb-s.
328  */
329 /* #define DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC 1 */
330
331 /* Although 'private_bcb_lsn_tree' contains only nodes with 'lsn_valid' TRUE
332  * compare_func cannot expect such arguments as it may be used to lookup
333  * privbcb unlisted in 'private_bcb_lsn_tree'.
334  */
335 static gint private_bcb_lsn_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
336 {
337 gint64 a_lsn,b_lsn;
338 gint r;
339
340 #ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
341         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a=%p,privbcb b=%p",G_STRLOC,a,b);
342         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb=%p",G_STRLOC,a->PublicBcb);
343         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb=%p",G_STRLOC,b->PublicBcb);
344         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)a->PublicBcb->MappedLength);
345         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)b->PublicBcb->MappedLength);
346 #endif
347
348         g_return_val_if_fail(a!=NULL,0);
349         g_return_val_if_fail(validate_Bcb(a->PublicBcb),0);
350         g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MAXINT64);       /* Forbid defined LSN as G_MAXINT64 */
351         g_return_val_if_fail(b!=NULL,0);
352         g_return_val_if_fail(validate_Bcb(b->PublicBcb),0);
353         g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MAXINT64);       /* Forbid defined LSN as G_MAXINT64 */
354
355         if (a==b) {     /* LSN would be apparently the same in such case :-) */
356 #ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
357                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return 0 (a==b)",G_STRLOC);
358 #endif
359                 return 0;
360                 }
361
362         a_lsn=(!a->lsn_valid ? (LONGLONG)G_MAXINT64 : a->lsn.QuadPart);
363         b_lsn=(!b->lsn_valid ? (LONGLONG)G_MAXINT64 : b->lsn.QuadPart);
364
365         /* Forbid the same LSNs if both defined */
366         g_assert(a_lsn==(LONGLONG)G_MAXINT64 || a_lsn!=b_lsn);
367
368         if ((r=(a_lsn>b_lsn)-(a_lsn<b_lsn))) {
369 #ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
370                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d (lsn !=)",G_STRLOC,r);
371 #endif
372                 return r;
373                 }
374
375 #ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
376         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return (ptr !=)",G_STRLOC);
377 #endif
378         return (a>b)-(a<b);
379 }
380
381 static void private_bcb_lsn_tree_init(void)
382 {
383         if (private_bcb_lsn_tree)
384                 return;
385         private_bcb_lsn_tree=g_tree_new(
386                         (GCompareFunc)private_bcb_lsn_tree_key_compare_func);   /* key_compare_func */
387 }
388
389 enum privbcb_item {
390         PRIVBCB_ITEM_NOP,       /* for sanity checks */
391         PRIVBCB_ITEM_DIRTY,
392         PRIVBCB_ITEM_LSN_VALID,
393         PRIVBCB_ITEM_LEAVE_FUNC_PENDING,
394         PRIVBCB_ITEM_REF_COUNT,
395         PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,      /* no protections against 'leave_func_pending' */
396         };
397
398 static void privbcb_set(struct private_bcb *privbcb,enum privbcb_item item,gint value)
399 {
400         g_return_if_fail(privbcb!=NULL);
401
402         private_bcb_lsn_tree_init();
403
404         g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
405         switch (item) {
406                 case PRIVBCB_ITEM_NOP:
407                         break;
408                 case PRIVBCB_ITEM_DIRTY:
409                         g_assert(TRUE==value || FALSE==value);
410                         privbcb->dirty=value;
411                         break;
412                 case PRIVBCB_ITEM_LSN_VALID:
413                         /* Never change 'privbcb->lsn_valid' while it is linked in 'private_bcb_lsn_tree'
414                          * as it could become unreachable due to private_bcb_lsn_tree_key_compare_func()
415                          * behaviour wrt 'privbcb->lsn_valid'!
416                          */
417                         g_tree_remove(private_bcb_lsn_tree,privbcb);
418                         g_assert(TRUE==value || FALSE==value);
419                         privbcb->lsn_valid=value;
420                         if (privbcb->lsn_valid)
421                                 g_tree_insert(private_bcb_lsn_tree,
422                                                 privbcb,        /* key */
423                                                 privbcb);       /* value */
424                         break;
425                 case PRIVBCB_ITEM_LEAVE_FUNC_PENDING:
426                         g_assert(TRUE==value || FALSE==value);
427                         privbcb->leave_func_pending=value;
428                         if (value)
429                                 g_assert(privbcb->ref_count==1);
430                         break;
431                 case PRIVBCB_ITEM_REF_COUNT:
432                         /* Forbid reincarnation of 'leave_func_pending' privbcb. */
433                         g_assert(!privbcb->leave_func_pending);
434                         g_assert(privbcb->ref_count>=1);
435                         privbcb->ref_count+=value;
436                         g_assert(privbcb->ref_count>=1);
437                         break;
438                 case PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR:
439                         g_assert(privbcb->ref_count>=0);
440                         privbcb->ref_count+=value;
441                         g_assert(privbcb->ref_count>=0);
442                         break;
443                 default: g_assert_not_reached();
444                 }
445         g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
446 }
447
448
449 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
450 static GHashTable *private_bcb_hash;
451
452 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
453 {
454         g_return_if_fail(validate_Bcb(key));
455
456         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: PublicBcb=%p",G_STRLOC,key);
457
458         g_free(key);
459 }
460
461 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
462 {
463 struct page_position pagepos_local;
464 gboolean errbool;
465 int errint;
466 size_t offset;
467 gpointer base_aligned;
468
469         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: privbcb=%p (->PublicBcb=%p)",G_STRLOC,value,value->PublicBcb);
470
471         g_return_if_fail(value!=NULL);
472         /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
473          * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
474          */
475         g_return_if_fail(value->PublicBcb!=NULL);
476         g_return_if_fail(value->FileObject!=NULL);
477         g_return_if_fail(value->ref_count==0);
478         g_return_if_fail(value->MappedLength>0);
479         g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
480         g_return_if_fail(value->base!=NULL);
481         g_return_if_fail(value->dirty==FALSE);
482         /* Ensure we are not registered in 'private_bcb_lsn_tree'. */
483         g_return_if_fail(value->lsn_valid==FALSE);
484
485         page_position_hash_init();
486
487         base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
488
489         pagepos_local.FileObject=value->FileObject;
490         pagepos_local.privbcb_list=NULL;
491         pagepos_local.shmid=-1;
492         for (
493                         offset=0;
494                         offset<value->MappedLength;
495                         offset+=PAGE_SIZE) {
496 struct page_position *pagepos;
497
498                 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
499                 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
500                 g_assert(validate_page_position(pagepos));
501                 g_assert(pagepos->privbcb_list!=NULL);
502                 if (pagepos->privbcb_list->next==NULL) {        /* single mapping by mmap(), no shm */
503                         g_assert(pagepos->shmid==-1);
504                         errint=munmap(((char *)base_aligned)+offset,PAGE_SIZE);
505                         g_assert(errint==0);
506                         }
507                 else if (pagepos->privbcb_list->next->next==NULL) {     /* mapping 2 -> 1, convert shm to mmap() */
508 struct private_bcb *privbcb_other;
509 gint64 privbcb_other_offset_relative;
510 gpointer mapped_other,errptr;
511
512                         g_assert(pagepos->shmid!=-1);
513                         if (value==pagepos->privbcb_list->data)
514                                 privbcb_other=pagepos->privbcb_list->next->data;
515                         else {
516                                 privbcb_other=pagepos->privbcb_list->data;
517                                 g_assert(value==pagepos->privbcb_list->next->data);
518                                 }
519                         privbcb_other_offset_relative=pagepos->FileOffset.QuadPart-privbcb_other->MappedFileOffset.QuadPart;
520                         /* privbcb_other_offset_relative may be negative up to -PAGE_SIZE
521                          * as 'MappedFileOffset' and 'base' are not page-aligned.
522                          */
523                         mapped_other=privbcb_other->base+privbcb_other_offset_relative;
524                         /* TODO:thread; shmdt()..mmap() window */
525                         errint=shmdt(mapped_other);
526                         g_assert(errint==0);
527                         errptr=mmap(
528                                         mapped_other,   /* start */
529                                         PAGE_SIZE,      /* length */
530                                         PROT_READ|PROT_WRITE,   /* prot */
531                                         MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
532                                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
533                                         0);     /* offset; ignored due to MAP_ANONYMOUS */
534                         g_assert(errptr==mapped_other);
535                         memcpy(mapped_other,((char *)base_aligned)+offset,PAGE_SIZE);
536                         errint=shmdt(((char *)base_aligned)+offset);    /* destroys shm */
537                         g_assert(errint==0);
538                         /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
539                         pagepos->shmid=-1;
540                         }
541                 else {  /* mappings (>=3) -> (>=2) */
542                         g_assert(pagepos->shmid!=-1);
543                         errint=shmdt(((char *)base_aligned)+offset);
544                         g_assert(errint==0);
545                         }
546
547                 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
548                 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
549                 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
550
551                 if (pagepos->privbcb_list==NULL) {      /* last mapping by mmap() removed */
552                         g_assert(pagepos->shmid==-1);
553                         errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
554                         g_assert(errbool==TRUE);
555                         }
556                 else    /* mapping is now >=1, either by mmap() or shm */
557                         g_assert(validate_page_position(pagepos));
558                 }
559
560         g_free(value);
561 }
562
563 static void private_bcb_hash_init(void)
564 {
565         if (private_bcb_hash)
566                 return;
567         private_bcb_hash=g_hash_table_new_full(
568                         g_direct_hash,  /* hash_func */
569                         g_direct_equal, /* key_equal_func */
570                         (GDestroyNotify)private_bcb_hash_key_destroy_func,      /* key_destroy_func */
571                         (GDestroyNotify)private_bcb_hash_value_destroy_func);   /* value_destroy_func */
572 }
573
574
575 static gboolean captive_cc_FileObject_delete_private_bcb_hash_foreach(
576                 PUBLIC_BCB *PublicBcb,  /* key */
577                 struct private_bcb *privbcb,  /* value */
578                 FILE_OBJECT *FileObject)  /* user_data */
579 {
580         g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);    /* meaning: do not remove this 'privbcb' */
581         g_return_val_if_fail(privbcb!=NULL,FALSE);      /* meaning: do not remove this 'privbcb' */
582         g_return_val_if_fail(PublicBcb==privbcb->PublicBcb,FALSE);      /* meaning: do not remove this 'privbcb' */
583         g_return_val_if_fail(FileObject!=NULL,FALSE);   /* meaning: do not remove this 'privbcb' */
584
585         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: PublicBcb=%p,privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
586                         ",FileObject=%p",G_STRLOC,
587                         PublicBcb,privbcb,privbcb->ref_count,privbcb->leave_func_pending,FileObject);
588
589         if (FileObject) {
590                 if (privbcb->FileObject!=FileObject)
591                         return FALSE;   /* do not remove this 'privbcb' */
592
593                 /* It is OK to leave it in 'private_bcb_hash' as it is protected
594                  * against reincarnation.
595                  * Such check is here to allow it to keep 'dirty' data.
596                  */
597                 if (privbcb->leave_func_pending)
598                         return FALSE;   /* do not remove this 'privbcb' */
599                 }
600
601         /* Do not reuse captive_privbcb_flush_ordered() destructor here as we cannot
602          * call g_hash_table_remove() from inside g_hash_table_foreach_remove() callback.
603          */
604
605         g_assert(privbcb->dirty==FALSE);        /* it would be fatal */
606
607         g_assert(privbcb->leave_func_pending==FALSE);
608         /* Force 'ref_count' drop to 0 to pass private_bcb_hash_value_destroy_func(). */
609         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-privbcb->ref_count);
610         /* Unset LSN to unlink from 'private_bcb_lsn_tree'. */
611         privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
612
613         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: WARNING: Deleting file with pending (fortunately non-dirty) BCBs"
614                         "; privbcb=%p,PublicBcb=%p,FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
615                         privbcb,PublicBcb,privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
616
617         return TRUE;    /* remove this 'privbcb' */
618 }
619
620 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
621 {
622 struct fileobject_cached *fileobject_cached;
623 BOOLEAN r=TRUE;
624
625         /* 'FileObject' may be NULL to check/clear the whole cache. */
626
627         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p",G_STRLOC,FileObject);
628
629         fileobject_cached_hash_init();
630         private_bcb_hash_init();
631
632         if (!FileObject || !(fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,FileObject)))
633                 r=FALSE;
634         else {
635 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
636                 CcUnpinData(fileobject_cached->Bcb);
637 #endif
638                 g_hash_table_remove(fileobject_cached_hash,FileObject);
639                 }
640
641         g_hash_table_foreach_remove(
642                         private_bcb_hash,       /* hash_table */
643                         (GHRFunc)captive_cc_FileObject_delete_private_bcb_hash_foreach, /* func */
644                         FileObject);    /* user_data */
645
646         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: returning r=%d",G_STRLOC,r);
647
648         return r;
649 }
650
651
652 static gboolean captive_privbcb_flush_unordered(struct private_bcb *privbcb);
653
654 static void captive_cc_flush_private_bcb_hash_foreach(
655                 PUBLIC_BCB *PublicBcb,  /* key */
656                 struct private_bcb *privbcb,  /* value */
657                 gboolean *flushedp)     /* user_data */
658 {
659 gboolean errbool;
660
661         g_return_if_fail(validate_Bcb(PublicBcb));
662         g_return_if_fail(privbcb!=NULL);
663         g_return_if_fail(PublicBcb==privbcb->PublicBcb);
664
665         if (!privbcb->dirty)    /* OK */
666                 return;
667
668         /* Such privbcb should have been flushed by captive_privbcb_flush_ordered(NULL) before
669          * to ensure its LSN ordering.
670          */
671         g_assert(!privbcb->lsn_valid);
672
673         *flushedp=TRUE;
674         errbool=captive_privbcb_flush_unordered(privbcb);
675         g_assert(errbool==TRUE);
676 }
677
678 static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req);
679
680 void captive_cc_flush(void)
681 {
682 gboolean flushed;
683
684         fileobject_cached_hash_init();
685         private_bcb_hash_init();
686
687         do {
688                 /* Trace it by g_tree first to attempt to keep LSN ordering */
689                 flushed=captive_privbcb_flush_ordered(NULL);
690                 g_hash_table_foreach(
691                                 private_bcb_hash,       /* hash_table */
692                                 (GHFunc)captive_cc_flush_private_bcb_hash_foreach,      /* func */
693                                 &flushed);      /* user_data */
694                 captive_leave();
695                 } while (flushed);
696
697         /* We must not call: captive_cc_FileObject_delete(NULL);
698          * here as it would destroy all the Bcbs needed for the forthcoming
699          * IoShutdownRegisteredFileSystems().
700          */
701 }
702
703
704 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
705 {
706 MDL *Mdl;
707 KEVENT Event;
708 IO_STATUS_BLOCK IoStatus;
709 NTSTATUS err;
710
711         g_return_val_if_fail(FileObject!=NULL,0);
712         g_return_val_if_fail(address!=0,0);
713         g_return_val_if_fail(length!=0,0);
714         g_return_val_if_fail(FileOffset!=NULL,0);
715
716         /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
717          * successfuly read the data. Workaround it - preclear (not postclear) the
718          * buffer and do not make any other assumptions about the data read.
719          */
720         memset(address,0,PAGE_SIZE);    /* pre-clear the buffer */
721         Mdl=MmCreateMdl(NULL,address,PAGE_SIZE);        /* FIXME: Deprecated in favor of IoAllocateMdl() */
722         g_assert(Mdl!=NULL);
723         MmBuildMdlForNonPagedPool(Mdl);
724         KeInitializeEvent(&Event,NotificationEvent,FALSE);
725         IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
726         err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
727         g_assert(NT_SUCCESS(err));
728         g_assert(NT_SUCCESS(IoStatus.Status));
729         /* It is not == as the file may be shorter than requested */
730         g_assert(IoStatus.Information<=length);
731         IoFreeMdl(Mdl);
732
733         /* Forbidden, see the comment above about ext2fsd.sys.
734          * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
735          */
736
737         return IoStatus.Information;    /* may be shorter than real! */
738 }
739
740
741 static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,
742                 PUBLIC_BCB *Bcb_find)
743 {
744 struct page_position pagepos_local,*pagepos;
745 struct private_bcb *privbcb,*privbcb_listitem;
746 GList *privbcb_list;
747
748         g_return_val_if_fail(FileObject!=NULL,NULL);
749         g_return_val_if_fail(FileOffset!=NULL,NULL);
750         /* 'Bcb_find' may be NULL */
751
752         page_position_hash_init();
753
754         pagepos_local.FileObject=FileObject;
755         pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
756         pagepos_local.privbcb_list=NULL;
757         pagepos_local.shmid=-1;
758         if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local)))
759                 return NULL;
760         g_assert(validate_page_position(pagepos));
761         g_assert(pagepos->privbcb_list!=NULL);
762
763         privbcb=NULL;
764         for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
765                 privbcb_listitem=privbcb_list->data;
766                 if (1
767                                 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
768                                 && privbcb_listitem->MappedLength==Length
769                                 && (!Bcb_find || Bcb_find==privbcb_listitem->PublicBcb)) {
770                         g_assert(privbcb==NULL);        /* appropriate 'Bcb_find'-matching privbcb found twice */
771                         privbcb=privbcb_listitem;
772                         if (!Bcb_find)
773                                 break;
774                         }
775                 }
776         if (!privbcb)
777                 return NULL;
778
779         /* Sanity check 'privbcb': */
780         g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
781
782         return privbcb;
783 }
784
785
786 /**
787  * CcMapData:
788  * @FileObject: Initialized open #FileObject to map.
789  * %NULL value is forbidden.
790  * @FileOffset: The @FileObject file offset from where to map the region from.
791  * Negative value is forbidden.
792  * @Length: Requested length of the region to map from @FileObject.
793  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
794  * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
795  * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
796  * %MAP_NO_READ will leave the pages unread (libcaptive: unread pages zeroed,
797  * already mapped pages shared with existing content) - needed by ntfs.sys of NT-5.1sp1
798  * as during file write it will %MAP_NO_READ and consequently push the data there;
799  * any request for the same file range read in the meantime will destroy the prepared data!
800  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
801  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
802  * %NULL pointer is forbidden.
803  * @Buffer: Returns the mapped memory region start address.
804  * This address may not be %PAGE_SIZE aligned.
805  * %NULL pointer is forbidden.
806  *
807  * Maps the specified region of @FileObject to automatically chosen address space.
808  * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
809  *
810  * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
811  * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
812  * but W32 filesystems apparently do not conform to it. If you do not take care of the
813  * modified data by some dirty-marking facility such data will be carelessly dropped without
814  * their commit to the disk.
815  *
816  * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
817  *
818  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
819  *
820  * We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
821  * even if such mapping for such file already exists.
822  * We should probably create a new #Bcb for the same space,
823  * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
824  *
825  * Returns: %TRUE if the region was successfuly mapped.
826  * @Bcb with the initialized new memory region.
827  * @Buffer with the address of the exact byte specified by @FileOffset.
828  */
829 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
830                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
831 {
832 PUBLIC_BCB **PublicBcbp,*PublicBcb;
833 struct page_position pagepos_local;
834 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
835 gpointer base_aligned;
836 size_t length_mapped_aligned;
837 gint length_pages_aligned;
838 struct page_position **pagepos_array;
839 int errint;
840 gpointer errptr;
841 struct private_bcb *privbcb;
842 gboolean after_eof=FALSE;       /* Did we reached the end of file already? */
843
844         g_return_val_if_fail(FileObject!=NULL,FALSE);
845         g_return_val_if_fail(FileOffset!=NULL,FALSE);
846         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
847         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
848         g_return_val_if_fail(Flags&MAP_WAIT,FALSE);     /* FIXME: on-demand loading not yet implemented */
849         g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE);   /* unknown flags? */
850         g_return_val_if_fail(Bcb!=NULL,FALSE);
851         g_return_val_if_fail(Buffer!=NULL,FALSE);
852
853         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
854                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
855
856         if (Flags&MAP_NO_READ) {
857                 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE));    /* NOT IMPLEMENTED YET */
858                 /* It may not be aligned as this function is prepared by 'after_eof' for incomplete tails.
859                  * It is being used at least by ntfs.sys of NT-5.1sp1 CcCopyWrite().
860                  */
861 #if 0
862                 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart+Length,PAGE_SIZE));     /* NOT IMPLEMENTED YET */
863 #endif
864                 }
865
866         g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
867         g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
868 #if 0   /* SectorSize can be really weird but we do not use it anyway */
869         /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
870          * 'SectorSize' may not yet be initialized during mount operation
871          * and 'FileObject->DeviceObject->Vpb' may exist in such case.
872          */
873         g_return_val_if_fail(0
874                                         || FileObject->DeviceObject->SectorSize==0      /* prevent division by 0 */
875                                         || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),
876                         FALSE);
877 #endif
878
879         page_position_hash_init();
880         private_bcb_hash_init();
881
882         PublicBcbp=(PUBLIC_BCB **)Bcb;
883         /* extend 'FileOffset' and 'Length' to page boundaries */
884         FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
885         FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
886         length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
887         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_mapped_aligned,PAGE_SIZE));
888         length_pages_aligned=length_mapped_aligned/PAGE_SIZE;
889
890         /* We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
891          * even if such mapping for such file already exists.
892          * We should probably create a new Bcb for the same space,
893          * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
894          */
895         if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL)))
896                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX already mapped by privbcb %p",
897                                 G_STRLOC,FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,privbcb);
898
899         /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
900         /* TODO: on-demand loading */
901         /* Although we do zeroed-page mapping here we just reserve the linear
902          * space by it.
903          */
904         base_aligned=mmap(
905                         NULL,   /* start */
906                         PAGE_SIZE+length_mapped_aligned+PAGE_SIZE,      /* length; leading and trailing boundary check pages */
907                         PROT_READ|PROT_WRITE,   /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
908                         MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
909                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
910                         0);     /* offset; ignored due to MAP_ANONYMOUS */
911         g_assert(base_aligned!=NULL);
912
913         base_aligned+=PAGE_SIZE;
914         errint=munmap(base_aligned-PAGE_SIZE,PAGE_SIZE);        /* unmap leading boundary check page */
915         g_assert(errint==0);
916         errint=munmap(base_aligned+length_mapped_aligned,PAGE_SIZE);    /* unmap trailing boundary check page */
917         g_assert(errint==0);
918
919         /* Create 'PublicBcb'; referenced by 'privbcb'. */
920         captive_new(PublicBcb);
921         PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
922         PublicBcb->NodeByteSize=sizeof(*PublicBcb);     /* we have no extensions there */
923         PublicBcb->MappedLength=Length;
924         PublicBcb->MappedFileOffset=*FileOffset;
925
926         /* Create 'privbcb'; referenced by created 'pagepos'es. */
927         captive_new(privbcb);
928         privbcb->PublicBcb=PublicBcb;
929         privbcb->FileObject=FileObject;
930         privbcb->ref_count=1;
931         privbcb->leave_func_pending=FALSE;
932         privbcb->MappedLength=PublicBcb->MappedLength;
933         privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
934         privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
935         privbcb->dirty=FALSE;
936         privbcb->lsn_valid=FALSE;
937         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_hash_table_insert: PublicBcb=%p,privbcb=%p",G_STRLOC,
938                         PublicBcb,privbcb);
939         g_hash_table_insert(private_bcb_hash,
940                         PublicBcb,      /* key */
941                         privbcb);       /* value */
942
943         privbcb_set(privbcb,PRIVBCB_ITEM_NOP,0);        /* just for the assertions */
944
945         /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
946          * below as captive_Cc_IoPageRead() has very big consequences as it calls
947          * the filesystem code and we may get reentrancy.
948          * Therefore we store all missing page read requests to 'pagepos_array' and we
949          * fill them when all the memory structures are in consistent state.
950          * We can also coalescence the requests more easily this way.
951          */
952         captive_newn_alloca(pagepos_array,length_pages_aligned);
953
954         pagepos_local.FileObject=FileObject;
955         pagepos_local.shmid=-1;
956         pagepos_local.privbcb_list=NULL;
957         {
958 size_t offset;
959
960                 for (
961                                 offset=0;
962                                 offset<length_mapped_aligned;
963                                 offset+=PAGE_SIZE) {
964 gpointer pageaddr;
965 struct page_position *pagepos;
966
967                         pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
968                         pageaddr=(gpointer)(((char *)base_aligned)+offset);
969                         if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
970                                 captive_new(pagepos);
971                                 *pagepos=pagepos_local;
972                                 pagepos->shmid=-1;
973                                 pagepos->privbcb_list=NULL;
974                                 pagepos->building=TRUE;
975                                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read of offset %llu to new pagepos %p of pure mmap() to address %p",
976                                                 G_STRLOC,(unsigned long long)offset,pagepos,pageaddr);
977                                 }
978                         else if (pagepos->privbcb_list->next==NULL) {  /* exactly one item (no IPC shm yet) */
979                                 g_assert(pagepos->shmid==-1);
980                                 if (-1==(pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600)))
981                                         g_error("%s: Failed shmget(2), you may be out of maximum system IPC shared pages",G_STRLOC);
982                                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: created shmid %d out of mmap() for offset %llu to existing pagepos %p to address %p",
983                                                 G_STRLOC,pagepos->shmid,(unsigned long long)offset,pagepos,pageaddr);
984                                 /* Reentrancy occured? */
985                                 g_assert(Flags&MAP_NO_READ || !pagepos->building);
986                                 }
987                         else {  /* already >=2 mappings, already IPC shared mem */
988                                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: already shared shmid of offset %llu by existing pagepos %p shmid %d to address %p",
989                                                 G_STRLOC,(unsigned long long)offset,pagepos,pagepos->shmid,pageaddr);
990                                 g_assert(pagepos->shmid!=-1);
991                                 g_assert(pagepos->privbcb_list!=NULL);
992                                 /* Reentrancy occured? */
993                                 g_assert(Flags&MAP_NO_READ || !pagepos->building);
994                                 }
995                         pagepos_array[offset/PAGE_SIZE]=pagepos;
996                         if (pagepos->privbcb_list) {    /* Not the first one - some shm in use now? */
997                                 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
998                                  * mmap(2) is still needed to get linear block of memory assignment.
999                                  */
1000                                 /* TODO:thread; munmap()..shmat() window */
1001                                 errint=munmap(pageaddr,PAGE_SIZE);
1002                                 g_assert(errint==0);
1003                                 errptr=shmat(pagepos->shmid,
1004                                                 pageaddr,       /* shmaddr */
1005                                                 0);     /* shmflg; !SHM_RDONLY==r/w */
1006                                 g_assert(errptr==pageaddr);
1007                                 }
1008
1009                         g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
1010                         pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb);    /* order not important */
1011                         g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
1012                         if (pagepos->privbcb_list->next==NULL) {        /* exactly one item (we just added it now) */
1013
1014                                 g_assert(pagepos->shmid==-1);
1015                                 g_hash_table_insert(page_position_hash,
1016                                                 pagepos,        /* key */
1017                                                 pagepos);       /* value */
1018                                 }
1019                         else if (pagepos->privbcb_list->next->next==NULL) {     /* second mapping - new shm */
1020 struct private_bcb *privbcb_orig;
1021 gint64 privbcb_orig_offset_relative;
1022 gpointer mapped_orig;
1023
1024                                 g_assert(pagepos->shmid!=-1);
1025                                 errint=shmctl(pagepos->shmid,
1026                                                 IPC_RMID,       /* cmd */
1027                                                 NULL);  /* buf */
1028                                 g_assert(errint==0);
1029                                 g_assert(pagepos->privbcb_list->data==privbcb); /* our privbcb should be the first */
1030                                 g_assert(pagepos->privbcb_list->next!=NULL);    /* we have exactly two items in the list */
1031                                 g_assert(pagepos->privbcb_list->next->next==NULL);      /* we have exactly two items in the list */
1032                                 privbcb_orig=pagepos->privbcb_list->next->data;
1033                                 privbcb_orig_offset_relative=pagepos->FileOffset.QuadPart-privbcb_orig->MappedFileOffset.QuadPart;
1034                                 /* privbcb_orig_offset_relative may be negative up to -PAGE_SIZE
1035                                  * as 'MappedFileOffset' and 'base' are not page-aligned.
1036                                  */
1037                                 mapped_orig=privbcb_orig->base+privbcb_orig_offset_relative;
1038                                 memcpy(pageaddr,mapped_orig,PAGE_SIZE);
1039                                 /* TODO:thread; munmap()..shmat() window */
1040                                 errint=munmap(mapped_orig,PAGE_SIZE);
1041                                 g_assert(errint==0);
1042                                 errptr=shmat(pagepos->shmid,
1043                                                 mapped_orig,    /* shmaddr */
1044                                                 0);     /* shmflg; !SHM_RDONLY==r/w */
1045                                 g_assert(errptr==mapped_orig);
1046                                 }
1047                         g_assert(validate_page_position(pagepos));
1048                         }
1049                 }
1050
1051         /* Fill in the missing pages content todo-list by stored 'pagepos_array' '->building' flags: */
1052         if (!(Flags&MAP_NO_READ)) {
1053 size_t offset_start,offset_end;
1054
1055                 for (
1056                                 offset_start=0;
1057                                 offset_start<length_mapped_aligned;
1058                                 offset_start=offset_end) {
1059 struct page_position *pagepos_start;
1060 size_t offset_built;
1061 gpointer pageaddr;
1062 ULONG got;
1063
1064                         offset_end=offset_start+PAGE_SIZE;
1065                         pagepos_start=pagepos_array[offset_start/PAGE_SIZE];
1066                         if (!pagepos_start->building)
1067                                 continue;
1068                         g_assert(offset_start==pagepos_start->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
1069                         pageaddr=(gpointer)(((char *)base_aligned)+offset_start);
1070                         /* Coalescence of the requests. */
1071                         for (
1072                                         offset_end=offset_start+PAGE_SIZE;
1073                                         offset_end<length_mapped_aligned;
1074                                         offset_end+=PAGE_SIZE) {
1075 struct page_position *pagepos_end;
1076
1077                                 pagepos_end=pagepos_array[offset_end/PAGE_SIZE];
1078                                 g_assert(offset_end==pagepos_end->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
1079                                 if (!pagepos_end->building)
1080                                         break;
1081                                 }
1082                         /* Read the range content: */
1083                         got=captive_Cc_IoPageRead(FileObject,pageaddr,offset_end-offset_start,&pagepos_start->FileOffset);
1084                         if (after_eof)
1085                                 g_assert(got==0);
1086                         else
1087                                 g_assert(got<=PAGE_SIZE);
1088                         after_eof=(got<PAGE_SIZE);
1089                         /* Unmark 'building' flags. */
1090                         for (
1091                                         offset_built=offset_start;
1092                                         offset_built<offset_end;
1093                                         offset_built+=PAGE_SIZE) {
1094 struct page_position *pagepos_built;
1095
1096                                 pagepos_built=pagepos_array[offset_built/PAGE_SIZE];
1097                                 g_assert(offset_built==pagepos_built->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
1098                                 g_assert(pagepos_built->building==TRUE);
1099                                 pagepos_built->building=FALSE;
1100                                 }
1101                         }
1102                 }
1103
1104         /* offset _into_ page, may not be PAGE_SIZE aligned: */
1105         *Buffer=privbcb->base;
1106         *PublicBcbp=PublicBcb;
1107         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1108                         "%s: result: privbcb=%p,privbcb->base=%p,privbcb->base+privbcb->MappedLength=%p,privbcb->MappedLength=0x%lX",
1109                         G_STRLOC,privbcb,privbcb->base,((char *)privbcb->base)+privbcb->MappedLength,(unsigned long)privbcb->MappedLength);
1110         g_assert(validate_Bcb(PublicBcb)==TRUE);
1111         if (FileObject->Flags & FO_STREAM_FILE) {
1112                 /* IoCreateStreamFileObjectLite() result is mapped many times. */
1113                 g_assert(!FileObject->SectionObjectPointers->SharedCacheMap);
1114                 }
1115         else {
1116                 g_assert(!FileObject->SectionObjectPointers->SharedCacheMap);
1117                 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
1118                 }
1119
1120         return TRUE;
1121 }
1122
1123
1124 /**
1125  * CcPinMappedData:
1126  * @FileObject: Initialized open #FileObject to map.
1127  * %NULL value is forbidden.
1128  * @MappedFileOffset: The @FileObject file offset from where to map the region from.
1129  * Negative value is forbidden.
1130  * @MappedLength: Requested length of the region to map from @FileObject.
1131  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1132  * @Wait: Whether disk waiting is permitted for this function.
1133  * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
1134  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1135  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1136  * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
1137  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1138  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1139  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1140  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1141  * %NULL pointer is forbidden.
1142  * @Buffer: Returns the mapped memory region start address.
1143  * This address may not be %PAGE_SIZE aligned.
1144  * %NULL pointer is forbidden.
1145  *
1146  * This function will allow you to modify the data mapped by CcMapData().
1147  * libcaptive does not differentiate this function with CcMapData().
1148  *
1149  * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
1150  *
1151  * NEVER re-read any memory from FileObject here!
1152  * at least fastfat.sys directory create relies on the fact of CcPinRead()
1153  * with already modified buffers to be left intact.
1154  *
1155  * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
1156  * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
1157  *
1158  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
1159  *
1160  * Returns: %TRUE if the region was successfuly mapped.
1161  * @Bcb with the initialized new memory region.
1162  * @Buffer with the address of the exact byte specified by @FileOffset.
1163  */
1164 BOOLEAN CcPinMappedData
1165                 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
1166 {
1167 struct private_bcb *privbcb;
1168
1169         g_return_val_if_fail(FileObject!=NULL,FALSE);
1170         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1171         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1172         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
1173         /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
1174         g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
1175         g_return_val_if_fail(Bcb!=NULL,FALSE);
1176
1177         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
1178                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
1179
1180         privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL);
1181         if (!privbcb && (Flags & PIN_IF_BCB))   /* BCB does not exist */
1182                 return FALSE;
1183         /* Appropriate privbcb not found.
1184          * This IMO should not happen and it is a bug in the client.
1185          * Unfortuantely fastfat.sys FatLocateVolumeLabel() will CcPinMappedData()
1186          * the volume label dirent without any previous CcMapData() or CcPinRead() !
1187          */
1188         if (!privbcb) {
1189 PVOID Buffer;
1190
1191                 return CcPinRead(
1192                                 FileObject,
1193                                 FileOffset,
1194                                 Length,
1195                                 0               /* Flags */
1196                                                 | (Flags&PIN_NO_READ ? MAP_NO_READ : 0)
1197                                                 | (Flags&PIN_WAIT    ? MAP_WAIT    : 0),
1198                                 Bcb,
1199                                 &Buffer);
1200                 }
1201
1202         /* NEVER re-read any memory from FileObject here! */
1203
1204         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1205
1206         /* Memory already mapped by CcMapData(). */
1207         *Bcb=privbcb->PublicBcb;
1208         return TRUE;
1209 }
1210
1211
1212 /**
1213  * CcPinRead:
1214  * @FileObject: Initialized open #FileObject to map.
1215  * %NULL value is forbidden.
1216  * @FileOffset: The @FileObject file offset from where to map the region from.
1217  * Negative value is forbidden.
1218  * @Length: Requested length of the region to map from @FileObject.
1219  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1220  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1221  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1222  * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
1223  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1224  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1225  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1226  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1227  * %NULL pointer is forbidden.
1228  * @Buffer: Returns the mapped memory region start address.
1229  * This address may not be %PAGE_SIZE aligned.
1230  * %NULL pointer is forbidden.
1231  *
1232  * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
1233  * See these two functions for the details. It has a difference to subsequent
1234  * calling of CcMapData() and CcPinMappedData() instead as this call counts
1235  * only as one function for a corresponding CcUnpinData() call.
1236  *
1237  * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
1238  *
1239  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
1240  *
1241  * Returns: %TRUE if the region was successfuly mapped.
1242  * @Bcb with the initialized new memory region.
1243  * @Buffer with the address of the exact byte specified by @FileOffset.
1244  */
1245 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
1246                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
1247 {
1248 PVOID Bcb_CcPinMappedData;
1249 gboolean errbool;
1250 gboolean count_CcMapData;
1251 struct private_bcb *privbcb;
1252
1253         g_return_val_if_fail(FileObject!=NULL,FALSE);
1254         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1255         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1256         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
1257         /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
1258         g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
1259         g_return_val_if_fail(Bcb!=NULL,FALSE);
1260         g_return_val_if_fail(Buffer!=NULL,FALSE);
1261
1262         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
1263                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
1264
1265         if (!(Flags&PIN_IF_BCB)) {
1266                 errbool=CcMapData(FileObject,FileOffset,Length,
1267                                 0               /* Flags */
1268                                                 | (Flags&PIN_NO_READ ? MAP_NO_READ : 0)
1269                                                 | (Flags&PIN_WAIT    ? MAP_WAIT    : 0),
1270                                 Bcb,Buffer);
1271                 g_return_val_if_fail(errbool==TRUE,FALSE);
1272                 count_CcMapData=TRUE;
1273                 }
1274         else
1275                 count_CcMapData=FALSE;
1276
1277         errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
1278         if (!(Flags&PIN_IF_BCB)) {
1279                 g_return_val_if_fail(errbool==TRUE,FALSE);
1280                 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
1281                 }
1282         else {
1283                 if (errbool==FALSE)     /* FALSE permitted; We may fail if Bcb does not exist yet. */
1284                         return FALSE;
1285                 *Bcb=Bcb_CcPinMappedData;
1286                 }
1287
1288         privbcb=captive_privbcb_find(FileObject,FileOffset,Length,*Bcb);
1289         g_assert(privbcb!=NULL);
1290         g_assert(privbcb->ref_count>=1);
1291         g_assert(privbcb->PublicBcb==*Bcb);
1292         if (count_CcMapData) {
1293                 /* CcPinRead() must always reference-count only by 1 despite any sub-called functions! */
1294                 g_assert(privbcb->ref_count>=2);
1295                 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
1296                 }
1297
1298         return TRUE;
1299 }
1300
1301
1302 static void logging_notify_privbcb_flush(struct private_bcb *privbcb);
1303
1304 /* Returns: The block was dirty and it was flushed to disk.
1305  */
1306 static gboolean captive_privbcb_flush_unordered(struct private_bcb *privbcb)
1307 {
1308 MDL *Mdl;
1309 KEVENT Event;
1310 IO_STATUS_BLOCK IoStatus;
1311 NTSTATUS err;
1312 gpointer base_sectoraligned;
1313 gsize length_sectoraligned;
1314 LARGE_INTEGER FileOffset_sectoraligned;
1315 gsize sectorsize;
1316 struct fileobject_cached *fileobject_cached;
1317 IRP *saved_TopLevelIrp;
1318 static gint64 last_written_lsn=G_MININT64;
1319
1320         g_assert(privbcb->ref_count>0);
1321
1322         if (!privbcb->dirty)
1323                 return FALSE;
1324
1325         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);
1326         logging_notify_privbcb_flush(privbcb);
1327         g_assert(privbcb->ref_count>=2);
1328         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1329
1330         if (privbcb->lsn_valid) {
1331                 if (!(last_written_lsn<=privbcb->lsn.QuadPart))
1332                         g_error("%s: last_written_lsn=%" G_GINT64_FORMAT " !<= privbcb->lsn=%" G_GINT64_FORMAT,G_STRLOC,
1333                                         last_written_lsn,(gint64)privbcb->lsn.QuadPart);
1334                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LSN write: last_written_lsn was %" G_GINT64_FORMAT ", is %" G_GINT64_FORMAT,
1335                                 G_STRLOC,last_written_lsn,(gint64)privbcb->lsn.QuadPart);
1336                 last_written_lsn=privbcb->lsn.QuadPart;
1337                 }
1338
1339         /* Prepare NULL TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
1340          */
1341         saved_TopLevelIrp=IoGetTopLevelIrp();
1342         IoSetTopLevelIrp(NULL);
1343
1344         fileobject_cached_hash_init();
1345         if ((fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,privbcb->FileObject)))
1346                 (*fileobject_cached->CallBacks.AcquireForLazyWrite)(
1347                                 fileobject_cached->LazyWriterContext,   /* Context */
1348                                 TRUE);  /* Wait */
1349
1350         g_assert(privbcb->FileObject->DeviceObject!=NULL);
1351
1352         /* We can get 0=='privbcb->FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
1353          * FIXME: We are unable to find the correct sectorsize for a filesystem as
1354          * even the 'CommonFcb' below contains invalid information.
1355          * As we need to have 'sectorsize' <=filesystem_blocksize at least for ext2fsd.sys
1356          * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
1357          * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
1358          */
1359         sectorsize=PAGE_SIZE;
1360 #if 0
1361         sectorsize=privbcb->FileObject->DeviceObject->SectorSize;
1362         if (privbcb->FileObject->FsContext) {
1363 REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileObject->FsContext;
1364
1365                 /* FIXME: Check CommonFcb->Type */
1366                 /* 'AllocationSize' can be less than 'sectorsize' if the total file length is smaller.
1367                  * Observed with ext2fsd.sys volume-file.
1368                  */
1369                 if (sectorsize<CommonFcb->AllocationSize.QuadPart)
1370                         sectorsize=CommonFcb->AllocationSize.QuadPart;
1371                 }
1372 #endif
1373
1374         /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
1375         g_assert(sectorsize>0);
1376         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,sectorsize));
1377         /* We align here directly the 'privbcb->base' which is not correct.
1378          * We should rather aligned according to 'privbcb->MappedOffset' but
1379          * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
1380          * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
1381          */
1382         g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
1383                                  ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,sectorsize));
1384         base_sectoraligned  =CAPTIVE_ROUND_DOWN(privbcb->base,sectorsize);
1385         length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,sectorsize)
1386                         -((char *)base_sectoraligned);
1387         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,sectorsize));
1388         FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,sectorsize);
1389
1390         Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
1391         g_assert(Mdl!=NULL);
1392         MmBuildMdlForNonPagedPool(Mdl);
1393
1394         KeInitializeEvent(&Event,NotificationEvent,FALSE);
1395
1396         /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
1397         err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
1398         g_assert(NT_SUCCESS(err));
1399         g_assert(NT_SUCCESS(IoStatus.Status));
1400         /* We should write at least the unaligned mapped data although we
1401          * do not need to successfuly write the whole aligned amount.
1402          * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
1403          * during FAT write by fastfat.sys.
1404          */
1405         g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
1406                         +privbcb->MappedLength);
1407         g_assert(IoStatus.Information<=length_sectoraligned);
1408
1409         if (fileobject_cached)
1410                 (*fileobject_cached->CallBacks.ReleaseFromLazyWrite)(
1411                                 fileobject_cached->LazyWriterContext);  /* Context */
1412
1413         /* Restore TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
1414          */
1415         g_assert(NULL==IoGetTopLevelIrp());
1416         IoSetTopLevelIrp(saved_TopLevelIrp);
1417
1418         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
1419                         "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
1420                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
1421                         base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
1422                         (gulong)IoStatus.Information);
1423
1424         privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE);
1425         return TRUE;
1426 }
1427
1428 struct captive_privbcb_flush_ordered_param {
1429         struct private_bcb *privbcb_req;
1430         struct private_bcb *found;      /* return */
1431         };
1432
1433 static gboolean captive_privbcb_flush_ordered_foreach_get_first(struct private_bcb *key,struct private_bcb *value,
1434                 struct captive_privbcb_flush_ordered_param *captive_privbcb_flush_ordered_param /* data */)
1435 {
1436         g_return_val_if_fail(key!=NULL,TRUE);   /* meaning: stop the traversal */
1437         g_return_val_if_fail(value!=NULL,TRUE); /* meaning: stop the traversal */
1438         g_return_val_if_fail(key==value,TRUE);  /* meaning: stop the traversal */
1439         g_return_val_if_fail(validate_Bcb(key->PublicBcb),TRUE);        /* meaning: stop the traversal */
1440         g_return_val_if_fail(captive_privbcb_flush_ordered_param!=NULL,TRUE);   /* meaning: stop the traversal */
1441         g_return_val_if_fail(captive_privbcb_flush_ordered_param->found==NULL,TRUE);    /* meaning: stop the traversal */
1442
1443         g_assert(key->lsn_valid==TRUE);
1444
1445         if (key==captive_privbcb_flush_ordered_param->privbcb_req) {
1446                 captive_privbcb_flush_ordered_param->found=key;
1447                 return TRUE;    /* stop the traversal */
1448                 }
1449
1450         if (!key->dirty)
1451                 return FALSE;   /* continue the traversal */
1452
1453         captive_privbcb_flush_ordered_param->found=key; /* first dirty LSNed buffer */
1454         return TRUE;    /* stop the traversal */
1455 }
1456
1457 /* Use 'privbcb==NULL' to flush all LSNed entries of cache.
1458  * WARNING: Non-LSNed entries will NOT be flushed!
1459  * Returns: Anything was really flushed to disk.
1460  */
1461 static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req)
1462 {
1463 struct private_bcb *privbcb;
1464 struct captive_privbcb_flush_ordered_param captive_privbcb_flush_ordered_param;
1465 gboolean errbool;
1466 gboolean r=FALSE;
1467
1468         if ((privbcb=privbcb_req) && !privbcb->dirty)
1469                 return FALSE;
1470
1471         if ((privbcb=privbcb_req) && !privbcb->lsn_valid) {
1472                 r=captive_privbcb_flush_unordered(privbcb);
1473                 g_assert(r==TRUE);
1474                 return r;
1475                 }
1476
1477         private_bcb_lsn_tree_init();
1478
1479         for (;;) {
1480                 captive_privbcb_flush_ordered_param.privbcb_req=privbcb_req;
1481                 captive_privbcb_flush_ordered_param.found=NULL;
1482                 g_tree_foreach(private_bcb_lsn_tree,
1483                                 (GTraverseFunc)captive_privbcb_flush_ordered_foreach_get_first, /* func */
1484                                 &captive_privbcb_flush_ordered_param);  /* user_data */
1485                 if (captive_privbcb_flush_ordered_param.found==NULL) {
1486                         g_assert(privbcb_req==NULL);    /* Global LSN-ordered flush has no more entries. */
1487                         break;
1488                         }
1489                 privbcb=captive_privbcb_flush_ordered_param.found;
1490
1491                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1492                                 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
1493                                 ",privbcb->leave_func_pending=%d",G_STRLOC,
1494                                 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
1495                                 (int)privbcb->leave_func_pending);
1496
1497                 if (privbcb->dirty) {
1498                         errbool=captive_privbcb_flush_unordered(privbcb);
1499                         g_assert(errbool==TRUE);
1500                         r=TRUE;
1501                         continue;       /* Restart as anything could change by calling W32 hassle. */
1502                         }
1503
1504                 if (privbcb!=privbcb_req)
1505                         continue;       /* We just flushed a LSNed buffer not yet requested to be destroyed. */
1506
1507                 g_assert(privbcb==g_tree_lookup(private_bcb_lsn_tree,privbcb));
1508                 break;
1509                 }
1510
1511         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d",G_STRLOC,(int)r);
1512         return r;
1513 }
1514
1515
1516 static void CcUnpinData_leave_func_finalize(struct private_bcb *privbcb)
1517 {
1518 gboolean errbool;
1519
1520         g_assert(privbcb->dirty==FALSE);
1521         g_assert(privbcb->lsn_valid==FALSE);
1522         g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
1523         g_assert(privbcb->leave_func_pending==TRUE);
1524         g_assert(privbcb->ref_count==1);
1525         privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
1526         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1527         errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
1528         g_assert(errbool==TRUE);
1529 }
1530
1531 static void CcUnpinData_leave_func(struct private_bcb *privbcb) /* NULL to traverse the whole tree */
1532 {
1533         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
1534                         ",privbcb->lsn_valid=%d",G_STRLOC,
1535                         privbcb,(!privbcb ? -1 : privbcb->ref_count),(!privbcb ? -1 : privbcb->leave_func_pending),
1536                         (!privbcb ? -1 : privbcb->lsn_valid));
1537
1538         g_assert(privbcb->leave_func_pending==TRUE);
1539         g_assert(privbcb->ref_count==1);
1540         captive_privbcb_flush_ordered(privbcb);
1541         privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
1542         CcUnpinData_leave_func_finalize(privbcb);
1543         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
1544 }
1545
1546 /**
1547  * CcUnpinData:
1548  * @Bcb: Initialized #PUBLIC_BCB structure.
1549  * %NULL value is forbidden.
1550  *
1551  * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
1552  *
1553  * This call does not set the buffer as dirty although it will flush the buffers
1554  * already set as dirty. Any flushes will be postponed after return from #IRP
1555  * handling by the filesystem driver.
1556  */
1557 VOID CcUnpinData(IN PVOID Bcb)
1558 {
1559 PUBLIC_BCB *PublicBcb;
1560 struct private_bcb *privbcb;
1561
1562         g_return_if_fail(validate_Bcb(Bcb));
1563
1564         private_bcb_hash_init();
1565
1566         PublicBcb=(PUBLIC_BCB *)Bcb;
1567         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1568         g_return_if_fail(privbcb!=NULL);
1569         g_assert(!privbcb->leave_func_pending);
1570
1571         g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
1572         if (privbcb->FileObject->Flags & FO_STREAM_FILE) {
1573                 /* IoCreateStreamFileObjectLite() result is mapped many times. */
1574                 g_assert(!privbcb->FileObject->SectionObjectPointers->SharedCacheMap);
1575                 }
1576         else
1577                 g_assert(privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb);
1578
1579         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1580                         "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
1581                         ",privbcb->leave_func_pending=%d",G_STRLOC,
1582                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
1583                         (int)privbcb->leave_func_pending);
1584
1585         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1586         if (privbcb->ref_count>0)
1587                 return;
1588
1589         if (!(privbcb->FileObject->Flags & FO_STREAM_FILE))
1590                 privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
1591
1592         /* Caboom: lfs (log file system) of ntfs.sys-NT5.1sp1 will do:
1593          * CcPinRead(); CcUnpinData(); access Buffer (wanna-crash);
1594          * Therefore we must postpone the buffer unmapping to some idle function...
1595          * I expect it a bug in ntfs.sys.
1596          */
1597         if (!privbcb->leave_func_pending) {
1598                 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);      /* '+1' gets decreased by 'leave_func_pending'. */
1599                 privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,TRUE);
1600                 captive_leave_register(
1601                                 (captive_leave_func)CcUnpinData_leave_func,     /* func */
1602                                 privbcb);       /* data; privbcb */
1603                 }
1604
1605         if (captive_cc_unmounting)
1606                 captive_leave();
1607 }
1608
1609
1610 /**
1611  * CcRepinBcb:
1612  * @Bcb: #PUBLIB_BCB to be repinned.
1613  * %NULL value is forbidden.
1614  *
1615  * Increases usecount on @Bcb. You must call CcUnpinRepinnedBcb() for such @Bcb
1616  * afterwards before returning from the #IRP handling.
1617  *
1618  * libcaptive does not differentiate between CcUnpinData() and CcUnpinRepinnedBcb().
1619  * W32 differentiates.
1620  *
1621  * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
1622  *
1623  */
1624 VOID CcRepinBcb(IN PVOID Bcb)
1625 {
1626 PUBLIC_BCB *PublicBcb;
1627 struct private_bcb *privbcb;
1628
1629         g_return_if_fail(validate_Bcb(Bcb));
1630
1631         private_bcb_hash_init();
1632
1633         PublicBcb=(PUBLIC_BCB *)Bcb;
1634         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1635         g_return_if_fail(privbcb!=NULL);
1636
1637         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
1638                         Bcb,privbcb->FileObject);
1639
1640         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1641 }
1642
1643
1644 /**
1645  * CcUnpinRepinnedBcb:
1646  * @Bcb: #PUBLIB_BCB to be unpinned from CcRepinBcb().
1647  * %NULL value is forbidden.
1648  * @WriteThrough: %TRUE if the buffer should be flushed before finishing this function.
1649  * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1650  * %NULL value is forbidden. libcaptive always returns %STATUS_SUCCESS here.
1651  *
1652  * Dereferencing of @Bcb after application of CcRepinBcb().
1653  *
1654  * This call does not set the buffer as dirty although it will flush the buffers
1655  * already set as dirty. Any flushes will be postponed after return from #IRP
1656  * handling by the filesystem driver if not requested to be synchronous by @WriteThrough.
1657  */
1658 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
1659 {
1660 PUBLIC_BCB *PublicBcb;
1661 struct private_bcb *privbcb;
1662
1663         g_return_if_fail(validate_Bcb(Bcb));
1664         g_return_if_fail(IoStatus!=NULL);
1665
1666         private_bcb_hash_init();
1667
1668         PublicBcb=(PUBLIC_BCB *)Bcb;
1669         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1670         g_return_if_fail(privbcb!=NULL);
1671
1672         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
1673                         Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
1674
1675         if (WriteThrough)
1676                 captive_privbcb_flush_ordered(privbcb);
1677
1678         IoStatus->Status=STATUS_SUCCESS;
1679         IoStatus->Information=privbcb->MappedLength;
1680
1681         CcUnpinData(Bcb);
1682 }
1683
1684 /**
1685  * CcSetDirtyPinnedData:
1686  * @Bcb: #PUBLIB_BCB to be unpinned from CcRepinBcb().
1687  * %NULL value is forbidden.
1688  * @Lsn: Optional LSN (Linear Sequence Number) to assign to @Bcb.
1689  * %NULL pointer is permitted.
1690  *
1691  * This call will set the buffer as dirty - such buffer will be flushed automatically.
1692  *
1693  * You should call it only on CcPin*() buffers - not just CcMapData() buffers
1694  * although libcaptive does not differentiate it.
1695  */
1696 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
1697 {
1698 PUBLIC_BCB *PublicBcb;
1699 struct private_bcb *privbcb;
1700
1701         g_return_if_fail(validate_Bcb(Bcb));
1702
1703         private_bcb_hash_init();
1704
1705         PublicBcb=(PUBLIC_BCB *)Bcb;
1706         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1707         g_return_if_fail(privbcb!=NULL);
1708
1709         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
1710                         Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
1711
1712         /* 'privbcb->ref_count' not to be increased by this function. */
1713
1714         if (Lsn) {
1715                 /* Unset it first to not to get into unreachable privbcb in 'private_bcb_lsn_tree'. */
1716                 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
1717                 privbcb->lsn=*Lsn;
1718                 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,TRUE);
1719                 }
1720         else {
1721                 /* FIXME: Unset 'lsn_valid' if !Lsn ? Undocumented by W32. */
1722                 g_assert(privbcb->lsn_valid==FALSE);    /* NOT IMPLEMENTED YET */
1723                 }
1724
1725         privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,TRUE);
1726 }
1727
1728
1729 struct CcSetFileSizes_param {
1730         PFILE_OBJECT FileObject;
1731         PCC_FILE_SIZES FileSizes;
1732         };
1733
1734 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
1735 static void CcSetFileSizes_private_bcb_hash_foreach(
1736                 PUBLIC_BCB *PublicBcb,  /* key */
1737                 struct private_bcb *privbcb,    /* value */
1738                 struct CcSetFileSizes_param *CcSetFileSizes_param)      /* user_data */
1739 {
1740         g_return_if_fail(validate_Bcb(PublicBcb));
1741         g_return_if_fail(privbcb!=NULL);
1742         g_return_if_fail(CcSetFileSizes_param!=NULL);
1743
1744         if (privbcb->FileObject!=CcSetFileSizes_param->FileObject)
1745                 return;
1746
1747         /* size changes behind our cached range? */
1748         if (1
1749                         && CcSetFileSizes_param->FileSizes->AllocationSize .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
1750                         && CcSetFileSizes_param->FileSizes->FileSize       .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
1751                         && CcSetFileSizes_param->FileSizes->ValidDataLength.QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
1752                 return;
1753
1754         /* FIXME: check BCB && 'struct page_position' invalidities */
1755         g_assert_not_reached(); /* NOT IMPLEMENTED YET */
1756 }
1757
1758 /**
1759  * CcSetFileSizes:
1760  * @FileObject: Initialized open #FileObject to update file sizes of.
1761  * %NULL value is forbidden.
1762  * @FileSizes: New file sizes to update cache to.
1763  * %NULL value is forbidden.
1764  * 
1765  * Update cache properties after file sizes were updated.
1766  * Probably only the exceeding pages need to be unmapped and BCBs updated
1767  * if FileSizes->AllocationSize gets shrunk.
1768  *
1769  * FIXME: Currently a NOP with no effect by libcaptive.
1770  */
1771 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
1772 {
1773 struct CcSetFileSizes_param CcSetFileSizes_param;
1774
1775         g_return_if_fail(FileObject!=NULL);
1776         g_return_if_fail(FileSizes!=NULL);
1777
1778         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
1779                         "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
1780                         FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
1781                         (guint64)FileSizes->ValidDataLength.QuadPart);
1782
1783         CcSetFileSizes_param.FileObject=FileObject;
1784         CcSetFileSizes_param.FileSizes=FileSizes;
1785         g_hash_table_foreach(
1786                         private_bcb_hash,       /* hash_table */
1787                         (GHFunc)CcSetFileSizes_private_bcb_hash_foreach,        /* func */
1788                         &CcSetFileSizes_param); /* user_data */
1789 }
1790
1791
1792 /**
1793  * CcPurgeCacheSection:
1794  * @SectionObjectPointer: Pointer specifying file to purge;
1795  * %NULL value is forbidden.
1796  * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1797  * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %TRUE return code in such case.
1798  * @FileOffset: Starting offset of the ranger to purge.
1799  * %NULL pointer is permitted and it means to purge the whole whole.
1800  * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
1801  * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
1802  * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
1803  *
1804  * Drop any caching for shrunken file which is not being deleted.
1805  * libcaptive will no longer consider such #BCB as dirty.
1806  *
1807  * Undocumented: It is required during %FSCTL_LOCK_VOLUME by ntfs.sys of NT-5.1sp1
1808  * to return %TRUE value if #SharedCacheMap value is %NULL.
1809  *
1810  * Returns: %TRUE if the range was purged successfuly.
1811  */
1812 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1813                 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
1814 {
1815 PUBLIC_BCB *PublicBcb;
1816 struct private_bcb *privbcb;
1817
1818         g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
1819         if (SectionObjectPointer->SharedCacheMap==NULL)
1820                 return TRUE;    /* nothing to purge; never return FALSE for ntfs.sys of NT-5.1sp1! */
1821         g_return_val_if_fail(FileOffset==NULL,FALSE);   /* NOT IMPLEMENTED YET */
1822
1823         PublicBcb=SectionObjectPointer->SharedCacheMap;
1824         g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1825
1826         private_bcb_hash_init();
1827
1828         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1829         g_return_val_if_fail(privbcb!=NULL,FALSE);
1830
1831         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1832                         "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
1833                         SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1834                         (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
1835
1836         g_assert_not_reached();
1837
1838         privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE);  /* purge it */
1839
1840         return TRUE;
1841 }
1842
1843
1844 /**
1845  * CcCopyRead:
1846  * @FileObject: Initialized open #FileObject to map.
1847  * %NULL value is forbidden.
1848  * @FileOffset: The @FileObject file offset from where to map the region from.
1849  * Negative value is forbidden.
1850  * @Length: Requested length of the region to map from @FileObject.
1851  * Value %0 is permitted (no effect of this function call).
1852  * @Wait: Whether disk waiting is permitted for this function.
1853  * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1854  * @Buffer: Address of memory region with already allocated memory of size @Length.
1855  * This address may not be %PAGE_SIZE aligned.
1856  * %NULL pointer is forbidden.
1857  * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1858  * %NULL pointer is forbidden.
1859  *
1860  * Reads the specified region of @FileObject to the given @Buffer.
1861  * No on-demand loading is in effect.
1862  *
1863  * Returns: %TRUE if the region was successfuly filled with @Length bytes.
1864  * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
1865  * @IoStatus.Information initialized by @Length if successful.
1866  */
1867 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
1868                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
1869 {
1870 PVOID MappedBcb;
1871 PVOID MappedBuffer;
1872 gboolean errbool;
1873
1874         g_return_val_if_fail(FileObject!=NULL,FALSE);
1875         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1876         g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
1877         g_return_val_if_fail(Buffer!=NULL,FALSE);
1878         g_return_val_if_fail(IoStatus!=NULL,FALSE);
1879
1880         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1881                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1882
1883         IoStatus->Status=STATUS_UNSUCCESSFUL;
1884         IoStatus->Information=0;
1885
1886         if (Length) {
1887                 errbool=CcPinRead(
1888                                 FileObject,     /* FileObject */
1889                                 FileOffset,     /* FileOffset */
1890                                 Length, /* Length */
1891                                 0               /* Flags; !PIN_NO_READ */
1892                                                 | (Wait ? PIN_WAIT : 0),
1893                                 &MappedBcb,     /* Bcb */
1894                                 &MappedBuffer); /* Buffer */
1895                 g_return_val_if_fail(errbool==TRUE,FALSE);
1896
1897                 memcpy(Buffer,MappedBuffer,Length);
1898
1899                 CcUnpinData(MappedBcb); /* no error code */
1900                 }
1901
1902         IoStatus->Status=STATUS_SUCCESS;
1903         IoStatus->Information=Length;
1904
1905         return TRUE;
1906 }
1907
1908
1909 /**
1910  * CcCopyWrite:
1911  * @FileObject: Initialized open #FileObject to map.
1912  * %NULL value is forbidden.
1913  * @FileOffset: The @FileObject file offset from where to map the region from.
1914  * Negative value is forbidden.
1915  * @Length: Requested length of the region to map from @FileObject.
1916  * Value %0 is permitted (no effect of this function call).
1917  * @Wait: Whether disk waiting is permitted for this function.
1918  * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1919  * @Buffer: Address of memory region with already allocated memory of size @Length.
1920  * This address may not be %PAGE_SIZE aligned.
1921  * %NULL pointer is forbidden.
1922  *
1923  * Writes the specified region of the given @Buffer to @FileObject.
1924  *
1925  * Returns: %TRUE if the region was successfuly written with @Length bytes.
1926  */
1927 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
1928                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
1929 {
1930 PVOID MappedBcb;
1931 PVOID MappedBuffer;
1932 gboolean errbool;
1933
1934         g_return_val_if_fail(FileObject!=NULL,FALSE);
1935         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1936         g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
1937         g_return_val_if_fail(Buffer!=NULL,FALSE);
1938
1939         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1940                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1941
1942         if (Length) {
1943                 errbool=CcPinRead(
1944                                 FileObject,     /* FileObject */
1945                                 FileOffset,     /* FileOffset */
1946                                 Length, /* Length */
1947                                 PIN_NO_READ             /* Flags */
1948                                                 | (Wait ? PIN_WAIT : 0),
1949                                 &MappedBcb,     /* Bcb */
1950                                 &MappedBuffer); /* Buffer */
1951                 g_return_val_if_fail(errbool==TRUE,FALSE);
1952
1953                 memcpy(MappedBuffer,Buffer,Length);
1954
1955                 CcSetDirtyPinnedData(
1956                                 MappedBcb,      /* Bcb */
1957                                 NULL);  /* Lsn */
1958                 CcUnpinData(MappedBcb); /* no error code */
1959                 }
1960
1961         return TRUE;
1962 }
1963
1964
1965 /**
1966  * CcCanIWrite:
1967  * @FileObject: Initialized open #FileObject to map.
1968  * %NULL value is forbidden.
1969  * @BytesToWrite: Amount of data to be asked whether it will be accepted.
1970  * Value %0 is permitted.
1971  * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
1972  * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
1973  *
1974  * Asks cache manager if it would currently accept write request to @FileObject
1975  * of @BytesToWrite bytes with @Wait condition.
1976  * libcaptive will always accept any writes. This function is a NOP.
1977  *
1978  * Returns: libcaptive always returns %TRUE.
1979  */
1980 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
1981 {
1982         g_return_val_if_fail(FileObject!=NULL,FALSE);
1983         g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
1984         g_return_val_if_fail(Retrying==TRUE || Retrying==FALSE,FALSE);
1985
1986         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
1987                         FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
1988
1989         return TRUE;
1990 }
1991
1992
1993 /**
1994  * CcSetReadAheadGranularity:
1995  * @FileObject: Initialized open #FileObject to map.
1996  * %NULL value is forbidden.
1997  * @Granularity: Suggested size of the cache element.
1998  * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
1999  *
2000  * libcaptive does not implement any caching and therefore this function
2001  * is a NOP there.
2002  */
2003 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
2004 {
2005         g_return_if_fail(FileObject!=NULL);
2006         g_return_if_fail(Granularity>=PAGE_SIZE);
2007         g_return_if_fail((Granularity&(Granularity-1))==0);     /* Power of two */
2008
2009         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
2010                         FileObject,(gulong)Granularity);
2011
2012         /* NOP; no caching by libcaptive */
2013 }
2014
2015
2016 /**
2017  * CcFlushCache:
2018  * @SectionObjectPointer: Pointer specifying file to flush;
2019  * %NULL value is forbidden.
2020  * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
2021  * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
2022  * @FileOffset: Optional starting point of the range to flush.
2023  * %NULL value is permitted.
2024  * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
2025  * @IoStatus: Optionally returns the resulting operation status.
2026  * #Information field will contain the number of bytes flushed.
2027  * %NULL value is permitted.
2028  *
2029  * Flushes out any pending dirty data in cache manager BCB mapping.
2030  * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
2031  */
2032 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2033                 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
2034 {
2035 PUBLIC_BCB *PublicBcb;
2036 struct private_bcb *privbcb;
2037
2038         g_return_if_fail(SectionObjectPointer!=NULL);
2039
2040         if (SectionObjectPointer->SharedCacheMap==NULL) {
2041 success:
2042                 if (IoStatus) {
2043                         IoStatus->Status=STATUS_SUCCESS;
2044                         IoStatus->Information=0;
2045                         }
2046                 return;
2047                 }
2048
2049         PublicBcb=SectionObjectPointer->SharedCacheMap;
2050         g_return_if_fail(validate_Bcb(PublicBcb));
2051
2052         private_bcb_hash_init();
2053
2054         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
2055         g_return_if_fail(privbcb!=NULL);
2056
2057         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
2058                         "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
2059                         SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
2060                         (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
2061
2062         if (FileOffset) {
2063                 if (FileOffset->QuadPart       >=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
2064                         goto success;
2065                 if (FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart)
2066                         goto success;
2067                 }
2068
2069         /* We may find some 'privbcb' being already sheduled to be destroyed.
2070          * We need to reference it to not to loose it in the middle of our flush operation.
2071          */
2072         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
2073
2074         /* FIXME: Flush just FileOffset..FileOfset+Length part */
2075         captive_privbcb_flush_ordered(privbcb);
2076
2077         privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
2078
2079         if (IoStatus) {
2080                 IoStatus->Status=STATUS_SUCCESS;
2081                 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
2082                 }
2083 }
2084
2085
2086 BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
2087 {
2088 PVOID Bcb;
2089 PVOID Buffer;
2090 BOOLEAN errboolean;
2091
2092         g_return_val_if_fail(FileObject!=NULL,FALSE);
2093         g_return_val_if_fail(StartOffset!=NULL,FALSE);
2094         g_return_val_if_fail(EndOffset!=NULL,FALSE);
2095         g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
2096         g_return_val_if_fail((EndOffset->QuadPart-StartOffset->QuadPart)
2097                     ==(ULONG)(EndOffset->QuadPart-StartOffset->QuadPart),FALSE);
2098         g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE);  /* Prevent 'Wait' upgrade to 'Flags'. */
2099
2100         errboolean=CcPreparePinWrite(
2101                         FileObject,     /* FileObject */
2102                         StartOffset,    /* FileOffset */
2103                         EndOffset->QuadPart-StartOffset->QuadPart,      /* Length */
2104                         TRUE,   /* Zero */
2105                         PIN_WAIT|PIN_NO_READ,   /* Flags */
2106                         &Bcb,   /* Bcb */
2107                         &Buffer);       /* Buffer */
2108         g_assert(errboolean==TRUE);
2109
2110         CcUnpinData(Bcb);
2111
2112         return TRUE;
2113 }
2114
2115
2116 /* map (PVOID LogHandle) -> (GList (of FILE_OBJECT *) *FileObject_list) */
2117 static GHashTable *log_handle_hash;
2118
2119 static void log_handle_hash_init(void)
2120 {
2121         if (log_handle_hash)
2122                 return;
2123         log_handle_hash=g_hash_table_new(
2124                         g_direct_hash,  /* hash_func */
2125                         g_direct_equal);        /* key_equal_func */
2126 }
2127
2128 /* map (FILE_OBJECT *FileObject) -> (struct FileObject_logging *) */
2129 struct FileObject_logging {
2130         PVOID LogHandle;
2131         PFLUSH_TO_LSN FlushToLsnRoutine;
2132         };
2133
2134 static GHashTable *FileObject_logging_hash;
2135
2136 static void FileObject_logging_hash_init(void)
2137 {
2138         if (FileObject_logging_hash)
2139                 return;
2140         FileObject_logging_hash=g_hash_table_new(
2141                         g_direct_hash,  /* hash_func */
2142                         g_direct_equal);        /* key_equal_func */
2143 }
2144
2145 static void logging_notify_privbcb_flush(struct private_bcb *privbcb)
2146 {
2147 struct FileObject_logging *FileObject_logging;
2148
2149         g_return_if_fail(privbcb!=NULL);
2150
2151         if (!privbcb->lsn_valid)        /* nothing to report anyway */
2152                 return;
2153
2154         FileObject_logging_hash_init();
2155         
2156         if (!(FileObject_logging=g_hash_table_lookup(FileObject_logging_hash,privbcb->FileObject)))
2157                 return;
2158
2159         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: call",G_STRLOC,
2160                         privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
2161
2162         (*FileObject_logging->FlushToLsnRoutine)(FileObject_logging->LogHandle,privbcb->lsn);
2163
2164         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: finish",G_STRLOC,
2165                         privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
2166 }
2167
2168 VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
2169 {
2170 GList *LogHandle_list;
2171 struct FileObject_logging *FileObject_logging;
2172
2173         g_return_if_fail(FileObject!=NULL);
2174         /* 'LogHandle' may be NULL */
2175         g_return_if_fail(FlushToLsnRoutine!=NULL);
2176
2177         log_handle_hash_init();
2178         FileObject_logging_hash_init();
2179
2180         if (!(FileObject_logging=g_hash_table_lookup(
2181                         FileObject_logging_hash,FileObject))) {
2182                 captive_new(FileObject_logging);
2183                 g_hash_table_insert(FileObject_logging_hash,
2184                                 FileObject,FileObject_logging);
2185                 }
2186         else {
2187                 LogHandle_list=g_hash_table_lookup(log_handle_hash,FileObject_logging->LogHandle);
2188                 g_assert(NULL!=g_list_find(LogHandle_list,FileObject));
2189                 LogHandle_list=g_list_remove(LogHandle_list,FileObject);
2190                 g_assert(NULL==g_list_find(LogHandle_list,FileObject));
2191                 g_hash_table_insert(log_handle_hash,FileObject_logging->LogHandle,LogHandle_list);
2192                 }
2193
2194         FileObject_logging->LogHandle=LogHandle;
2195         FileObject_logging->FlushToLsnRoutine=FlushToLsnRoutine;
2196
2197         LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
2198         LogHandle_list=g_list_prepend(LogHandle_list,FileObject);
2199         g_hash_table_insert(log_handle_hash,LogHandle,LogHandle_list);
2200 }
2201
2202
2203 struct CcGetDirtyPages_param {
2204         PDIRTY_PAGE_ROUTINE DirtyPageRoutine;   /* arg of CcGetDirtyPages() */
2205         IN PVOID Context1;      /* arg of CcGetDirtyPages() */
2206         IN PVOID Context2;      /* arg of CcGetDirtyPages() */
2207         FILE_OBJECT *FileObject;        /* search through 'page_position_hash' for 'FileObject' */
2208         LARGE_INTEGER OldestLsn; gboolean OldestLsn_found;      /* intermediate return value of CcGetDirtyPages() */
2209         GTree *FileObject_pages_Tree;
2210         struct CcGetDirtyPages_param_FileObject_pages_Tree_page *FileObject_pages_Tree_page_last;
2211         };
2212
2213 struct CcGetDirtyPages_param_FileObject_pages_Tree_page {
2214         gint64 file_offset;
2215         gint64 file_offset_end;
2216         LARGE_INTEGER OldestLsn,NewestLsn;
2217         };
2218
2219 static gint CcGetDirtyPages_FileObject_pages_Tree_key_compare_func
2220                 (const struct CcGetDirtyPages_param_FileObject_pages_Tree_page *a,
2221                 const struct CcGetDirtyPages_param_FileObject_pages_Tree_page *b,
2222                 gpointer user_data /* unused */)
2223 {
2224         g_return_val_if_fail(a!=NULL,0);
2225         g_return_val_if_fail(b!=NULL,0);
2226
2227         return (a->file_offset>b->file_offset)-(a->file_offset<b->file_offset);
2228 }
2229
2230 static void CcGetDirtyPages_FileObject_pages_Tree_key_destroy_func
2231                 (struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page)
2232 {
2233         g_return_if_fail(tree_page!=NULL);
2234
2235         g_free(tree_page);
2236 }
2237
2238 static void CcGetDirtyPages_page_position_hash_foreach(
2239                 struct page_position *pagepos,  /* key */
2240                 struct page_position *pagepos2, /* value */
2241                 struct CcGetDirtyPages_param *CcGetDirtyPages_param)    /* user_data */
2242 {
2243 LARGE_INTEGER OldestLsn,NewestLsn;
2244 gboolean lsn_found,dirty_found;
2245 GList *privbcb_list;
2246 struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page;
2247
2248         g_return_if_fail(pagepos!=NULL);
2249         g_return_if_fail(pagepos==pagepos2);
2250         g_return_if_fail(CcGetDirtyPages_param!=NULL);
2251         g_return_if_fail(CcGetDirtyPages_param->FileObject_pages_Tree!=NULL);
2252
2253         if (pagepos->FileObject!=CcGetDirtyPages_param->FileObject)
2254                 return;
2255
2256         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: scan pagepos offset=0x%llX",G_STRLOC,
2257                         (unsigned long long)pagepos->FileOffset.QuadPart);
2258
2259         dirty_found=FALSE;
2260         lsn_found=FALSE;
2261         for (
2262                         privbcb_list=pagepos->privbcb_list;
2263                         privbcb_list;
2264                         privbcb_list=privbcb_list->next) {
2265 struct private_bcb *privbcb=privbcb_list->data;
2266
2267                 if (!privbcb->dirty)
2268                         continue;
2269                 dirty_found=TRUE;
2270
2271                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: scan page mapping privbcb=%p,MappedFileOffset=0x%llX,MappedLength=%lX"
2272                                                 ",lsn=%" G_GINT64_FORMAT,G_STRLOC,
2273                                 privbcb,(unsigned long long)privbcb->MappedFileOffset.QuadPart,(unsigned long)privbcb->MappedLength,
2274                                 (gint64)(!privbcb->lsn_valid ? -1 : privbcb->lsn.QuadPart));
2275
2276                 if (privbcb->lsn_valid) {
2277                         if (!lsn_found) {
2278                                 OldestLsn=NewestLsn=privbcb->lsn;
2279                                 lsn_found=TRUE;
2280                                 }
2281                         else {
2282                                 OldestLsn.QuadPart=MIN(OldestLsn.QuadPart,privbcb->lsn.QuadPart);
2283                                 NewestLsn.QuadPart=MAX(NewestLsn.QuadPart,privbcb->lsn.QuadPart);
2284                                 }
2285                         }
2286                 }
2287         if (!dirty_found) {
2288                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: no dirty pages found",G_STRLOC);
2289                 return;
2290                 }
2291
2292         if (lsn_found) {
2293                 if (!CcGetDirtyPages_param->OldestLsn_found) {
2294                         CcGetDirtyPages_param->OldestLsn=OldestLsn;
2295                         CcGetDirtyPages_param->OldestLsn_found=TRUE;
2296                         }
2297                 else
2298                         CcGetDirtyPages_param->OldestLsn.QuadPart=MIN(CcGetDirtyPages_param->OldestLsn.QuadPart,OldestLsn.QuadPart);
2299                 }
2300         else {
2301                 OldestLsn.QuadPart=0;
2302                 NewestLsn.QuadPart=0;
2303                 }
2304
2305         captive_new(tree_page);
2306         tree_page->file_offset=pagepos->FileOffset.QuadPart;
2307         tree_page->file_offset_end=tree_page->file_offset+PAGE_SIZE;
2308         tree_page->OldestLsn=OldestLsn;
2309         tree_page->NewestLsn=NewestLsn;
2310         g_tree_insert(CcGetDirtyPages_param->FileObject_pages_Tree,
2311                         tree_page,      /* key */
2312                         tree_page);     /* value */
2313
2314         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: new tree_page file_offset=0x%llX"
2315                                         ",OldestLsn=%" G_GINT64_FORMAT ",NewestLsn=%" G_GINT64_FORMAT,G_STRLOC,
2316                         (unsigned long long)tree_page->file_offset,
2317                                         (gint64)tree_page->OldestLsn.QuadPart,(gint64)tree_page->NewestLsn.QuadPart);
2318 }
2319
2320 static gboolean CcGetDirtyPages_FileObject_pages_Tree_traverse_func
2321                 (struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page /* key */,
2322                 struct CcGetDirtyPages_param_FileObject_pages_Tree_page *value,
2323                 struct CcGetDirtyPages_param *CcGetDirtyPages_param)    /* user_data */
2324 {
2325 struct CcGetDirtyPages_param_FileObject_pages_Tree_page *page_last;
2326
2327         /* 'tree_page' may be NULL for the last invocation after complete traversal. */
2328         g_return_val_if_fail(tree_page==value,TRUE);    /* 'TRUE' meaning: stop the traversal. */
2329         g_return_val_if_fail(CcGetDirtyPages_param!=NULL,TRUE); /* 'TRUE' meaning: stop the traversal. */
2330
2331         page_last=CcGetDirtyPages_param->FileObject_pages_Tree_page_last;
2332
2333         /* Concatenate two pages? */
2334         if (page_last && tree_page
2335                         && page_last->file_offset_end   ==tree_page->file_offset
2336                         && page_last->OldestLsn.QuadPart==tree_page->OldestLsn.QuadPart
2337                         && page_last->NewestLsn.QuadPart==tree_page->NewestLsn.QuadPart) {
2338                 page_last->file_offset_end=tree_page->file_offset_end;
2339                 /* Forget about the current 'tree_page' as it has been appended. */
2340                 return FALSE;   /* 'FALSE' meaning: continue the traversal. */
2341                 }
2342
2343         /* Flush 'page_last'. */
2344         if (page_last) {
2345 LARGE_INTEGER FileOffset_local;
2346 LARGE_INTEGER OldestLsn_check,NewestLsn_check;
2347
2348                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: DirtyPageRoutine(): FileOffset=0x%llX,Length=0x%llX"
2349                                                 ",OldestLsn=%" G_GINT64_FORMAT ",NewestLsn=%" G_GINT64_FORMAT,G_STRLOC,
2350                                 (unsigned long long)page_last->file_offset,(unsigned long long)(page_last->file_offset_end-page_last->file_offset),
2351                                                 (gint64)page_last->OldestLsn.QuadPart,(gint64)page_last->NewestLsn.QuadPart);
2352
2353                 OldestLsn_check=page_last->OldestLsn;
2354                 NewestLsn_check=page_last->NewestLsn;
2355
2356                 FileOffset_local.QuadPart=page_last->file_offset;
2357
2358                 (*CcGetDirtyPages_param->DirtyPageRoutine)(
2359                                 CcGetDirtyPages_param->FileObject,      /* FileObject */
2360                                 &FileOffset_local,      /* FileOffset */
2361                                 page_last->file_offset_end-page_last->file_offset,      /* Length */
2362                                 &page_last->OldestLsn,  /* OldestLsn */
2363                                 &page_last->NewestLsn,  /* NewestLsn */
2364                                 CcGetDirtyPages_param->Context1,        /* Context1 */
2365                                 CcGetDirtyPages_param->Context2);       /* Context2 */
2366
2367                 /* just unconfirmed sanity: */
2368                 g_assert(FileOffset_local.QuadPart==page_last->file_offset);    /* check for possible corruption */
2369                 g_assert(OldestLsn_check.QuadPart==page_last->OldestLsn.QuadPart);
2370                 g_assert(NewestLsn_check.QuadPart==page_last->NewestLsn.QuadPart);
2371
2372                 CcGetDirtyPages_param->FileObject_pages_Tree_page_last=NULL;
2373                 }
2374
2375         /* May be NULL during the last flush: */
2376         CcGetDirtyPages_param->FileObject_pages_Tree_page_last=tree_page;
2377
2378         return FALSE;   /* 'FALSE' meaning: continue the traversal. */
2379 }
2380
2381 /**
2382  * CcGetDirtyPages:
2383  * @LogHandle: Arbitrary pointer to match with value passed to CcSetLogHandleForFile().
2384  * %NULL value is permitted (considered as regular matching value by libcaptive).
2385  * @DirtyPageRoutine: #PDIRTY_PAGE_ROUTINE type to call on each dirty page(s).
2386  * %NULL value is forbidden.
2387  * @Context1: User data to pass to @DirtyPageRoutine.
2388  * %NULL value is permitted.
2389  * @Context2: User data to pass to @DirtyPageRoutine.
2390  *
2391  * Searches through the list of dirty #PUBLIC_BCB s of files assigned to @LogHandle
2392  * by CcSetLogHandleForFile(). Any clean #PUBLIC_BCB s are ignored
2393  * by this function. Function will scan through all #PUBLIC_BCB mapping of each such page
2394  * and it will detect its oldest and newest LSN (Logical Sequence Number). Unset
2395  * LSN is considered as value 0. Value 0 is returned only if no other LSN is valid
2396  * for such case, otherwise such void value 0 is ignored (and oldest and newest LSN
2397  * returned are being equal in the case only single LSN was found).
2398  *
2399  * Found pages are coalesced as much as possible during calls of @DirtyPageRoutine.
2400  * Coalescable ranges must have the same detected both oldest and newest LSNs.
2401  *
2402  * Returns: Oldest LSN across all the #FileObject s found for the given @LogHandle.
2403  * Function returns value %0 if no appropriate LSN was found.
2404  * libcaptive must return #gint64 instead of the official #LARGE_INTEGER
2405  * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
2406  */
2407 gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
2408                 IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
2409 {
2410 GList *LogHandle_list;
2411 struct CcGetDirtyPages_param CcGetDirtyPages_param;
2412
2413         /* 'LogHandle' may be NULL */
2414         g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
2415
2416         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LogHandle=%p,DirtyPageRoutine=%p,Context1=%p,Context2=%p",G_STRLOC,
2417                         LogHandle,DirtyPageRoutine,Context1,Context2);
2418
2419         log_handle_hash_init();
2420         page_position_hash_init();
2421
2422         CcGetDirtyPages_param.DirtyPageRoutine=DirtyPageRoutine;
2423         CcGetDirtyPages_param.Context1=Context1;
2424         CcGetDirtyPages_param.Context2=Context2;
2425         CcGetDirtyPages_param.OldestLsn_found=FALSE;
2426
2427         for (
2428                         LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
2429                         LogHandle_list;
2430                         LogHandle_list=LogHandle_list->next) {
2431                 CcGetDirtyPages_param.FileObject=LogHandle_list->data;
2432                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: processing FileObject %p",G_STRLOC,CcGetDirtyPages_param.FileObject);
2433
2434                 CcGetDirtyPages_param.FileObject_pages_Tree=g_tree_new_full(
2435                                 (GCompareDataFunc)CcGetDirtyPages_FileObject_pages_Tree_key_compare_func,       /* key_compare_func */
2436                                 NULL,   /* key_compare_data */
2437                                 (GDestroyNotify)CcGetDirtyPages_FileObject_pages_Tree_key_destroy_func, /* key_destroy_func */
2438                                 NULL);  /* value_destroy_func */
2439                 g_hash_table_foreach(
2440                                 page_position_hash,     /* hash_table */
2441                                 (GHFunc)CcGetDirtyPages_page_position_hash_foreach,     /* func */
2442                                 &CcGetDirtyPages_param);        /* user_data */
2443                 CcGetDirtyPages_param.FileObject_pages_Tree_page_last=NULL;
2444                 g_tree_foreach(CcGetDirtyPages_param.FileObject_pages_Tree,     /* tree */
2445                                 (GTraverseFunc)CcGetDirtyPages_FileObject_pages_Tree_traverse_func,     /* traverse_func */
2446                                 &CcGetDirtyPages_param);        /* user_data */
2447                 CcGetDirtyPages_FileObject_pages_Tree_traverse_func(
2448                                 NULL,   /* tree_page */
2449                                 NULL,   /* value */
2450                                 &CcGetDirtyPages_param);        /* user_data */
2451                 g_tree_destroy(CcGetDirtyPages_param.FileObject_pages_Tree);
2452                 }
2453
2454         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %" G_GINT64_FORMAT,G_STRLOC,
2455                         (gint64)(!CcGetDirtyPages_param.OldestLsn_found ? 0 : CcGetDirtyPages_param.OldestLsn.QuadPart));
2456
2457         if (!CcGetDirtyPages_param.OldestLsn_found)
2458                 return 0;
2459         return CcGetDirtyPages_param.OldestLsn.QuadPart;
2460 }
2461
2462
2463 /**
2464  * CcSetAdditionalCacheAttributes:
2465  * @FileObject: Initialized open #FileObject to map.
2466  * %NULL value is forbidden.
2467  * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
2468  * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
2469  *
2470  * libcaptive does not implement any caching and therefore this function
2471  * is a NOP there.
2472  */
2473 VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
2474 {
2475         g_return_if_fail(FileObject!=NULL);
2476
2477         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
2478                         FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
2479
2480         /* NOP; no caching by libcaptive */
2481 }
2482
2483
2484 /**
2485  * CcSetBcbOwnerPointer:
2486  * @Bcb: Initialized #PUBLIC_BCB structure.
2487  * %NULL value is forbidden.
2488  * @Owner: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
2489  * %NULL value is forbidden (FIXME: Is it W32 compliant?).
2490  *
2491  * Set thread-specific pointer for a pinned @Bcb. Use CcUnpinDataForThread()
2492  * when @Bcb is no longer needed. CcUnpinDataForThread() is NOT a reverse
2493  * operation for this single call CcSetBcbOwnerPointer(), see CcUnpinDataForThread().
2494  *
2495  * libcaptive implements this function as no-operation as it does not yet
2496  * support any threading.
2497  */
2498 VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
2499 {
2500         g_return_if_fail(validate_Bcb(Bcb));
2501         g_return_if_fail(Owner!=NULL);
2502
2503         /* FIXME:thread; NOP if no threads present */
2504 }
2505
2506
2507 /**
2508  * CcUnpinDataForThread:
2509  * @Bcb: Initialized #PUBLIC_BCB structure.
2510  * %NULL value is forbidden.
2511  * @ResourceThreadId: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
2512  * This pointer had to be passed to CcSetBcbOwnerPointer() #Owner parameter previously.
2513  * %NULL value is forbidden (FIXME: is it W32 compliant?).
2514  *
2515  * CcUnpinData() for a thread specified by @ResourceThreadId.
2516  * Reverse operation for a pair of CcMapData() and CcSetBcbOwnerPointer().
2517  *
2518  * libcaptive implements this function as a simple pass to CcUnpinData() as it does not yet
2519  * support any threading.
2520  */
2521 VOID CcUnpinDataForThread(IN PVOID Bcb,IN ERESOURCE_THREAD ResourceThreadId)
2522 {
2523         g_return_if_fail(validate_Bcb(Bcb));
2524         g_return_if_fail(ResourceThreadId!=0);
2525
2526         /* FIXME:thread */
2527
2528         CcUnpinData(Bcb);
2529 }
2530
2531
2532 /**
2533  * CcRemapBcb:
2534  * @Bcb: Initialized #PUBLIC_BCB structure.
2535  * %NULL value is forbidden.
2536  *
2537  * Create a copy of @Bcb for the exactly same file contents as is @Bcb.
2538  * The returned copy has the same attributes as the result of CcMapData()
2539  * notwithstanding the current state of input @Bcb, therefore it is only
2540  * for read/only access etc.
2541  *
2542  * libcaptive calls CcMapData() internally with @Bcb parameters.
2543  *
2544  * Returns: Copy of @Bcb. This _pointer_ never equals to @Bcb.
2545  * It should be some different
2546  * #PUBLIC_BCB structure according to W32 doc.
2547  */
2548 PVOID CcRemapBcb(IN PVOID Bcb)
2549 {
2550 PVOID r;
2551 PVOID Buffer_unused;
2552 BOOLEAN errbool;
2553 PUBLIC_BCB *PublicBcb;
2554 struct private_bcb *privbcb;
2555
2556         g_return_val_if_fail(validate_Bcb(Bcb),NULL);
2557
2558         private_bcb_hash_init();
2559
2560         PublicBcb=(PUBLIC_BCB *)Bcb;
2561         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
2562         g_return_val_if_fail(privbcb!=NULL,NULL);
2563
2564         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
2565                         "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d",G_STRLOC,
2566                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count);
2567
2568         errbool=CcMapData(
2569                         privbcb->FileObject,    /* FileObject */
2570                         &privbcb->MappedFileOffset,     /* FileOffset */
2571                         privbcb->MappedLength,  /* Length */
2572                         MAP_WAIT,       /* Flags; && !MAP_NO_READ */
2573                         &r,     /* Bcb */
2574                         &Buffer_unused); /* Buffer */
2575         g_return_val_if_fail(errbool==TRUE,NULL);
2576         g_return_val_if_fail(r!=NULL,NULL);
2577         g_return_val_if_fail(Buffer_unused!=NULL,NULL);
2578
2579         g_assert(r!=Bcb);
2580         return r;
2581 }
2582
2583
2584 /**
2585  * CcPreparePinWrite:
2586  * @FileObject: Initialized open #FileObject to map.
2587  * %NULL value is forbidden.
2588  * @FileOffset: The @FileObject file offset from where to map the region from.
2589  * Negative value is forbidden.
2590  * @Length: Requested length of the region to map from @FileObject.
2591  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
2592  * @Zero: %TRUE if the area of @FileOffset...@FileOffset+@Length should be cleared.
2593  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
2594  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
2595  * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
2596  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
2597  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
2598  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
2599  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
2600  * %NULL pointer is forbidden.
2601  * @Buffer: Returns the mapped memory region start address.
2602  * This address may not be %PAGE_SIZE aligned.
2603  * %NULL pointer is forbidden.
2604  *
2605  * Wrapper for a pair of CcPinRead() and CcSetDirtyPinnedData().
2606  * The mapped range can be also optionally cleared if @Zero is specified.
2607  * See CcPinRead() for a more detailed documentation.
2608  *
2609  * This call will set the buffer as dirty - such buffer will be flushed automatically.
2610  *
2611  * Returns: %TRUE if the mapping was successful.
2612  */
2613 BOOLEAN CcPreparePinWrite(IN PFILE_OBJECT FileObject,
2614                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Zero,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
2615 {
2616 BOOLEAN errbool;
2617
2618         g_return_val_if_fail(FileObject!=NULL,FALSE);
2619         g_return_val_if_fail(FileOffset!=NULL,FALSE);
2620         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
2621         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
2622         /* 'Flags' passed to CcPinRead() */
2623         g_return_val_if_fail(Bcb!=NULL,FALSE);
2624         g_return_val_if_fail(Buffer!=NULL,FALSE);
2625
2626         errbool=CcPinRead(FileObject,FileOffset,Length,Flags|(Zero ? PIN_NO_READ : 0),Bcb,Buffer);
2627         g_return_val_if_fail(errbool==TRUE,FALSE);
2628
2629         CcSetDirtyPinnedData(
2630                         *Bcb,   /* Bcb */
2631                         NULL);  /* Lsn; OPTIONAL */
2632
2633         if (Zero) {
2634                 memset(*Buffer,0,Length);
2635                 }
2636
2637         return TRUE;
2638 }
2639
2640
2641 VOID FsRtlIncrementCcFastReadNoWait(VOID)
2642 {
2643         /* FIXME: {{%fs:[0]}+0x4E0}:LONG++ */
2644 }
2645
2646
2647 NTSTATUS CcWaitForCurrentLazyWriterActivity(VOID)
2648 {
2649         return STATUS_SUCCESS;
2650 }