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