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