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