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