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