Relax 'SectorSize' sanity check as sometimes it may not be set before mount
[captive.git] / src / libcaptive / cc / map.c
1 /* $Id$
2  * reactos Cache Manager mapper emulation of libcaptive
3  * Copyright (C) 2002 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
39
40 /* CONFIG: */
41
42 #define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45  /* FIXME: unknown, undocumented */
43
44
45 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
46 {
47         g_return_val_if_fail(PublicBcb!=NULL,FALSE);
48         g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
49         g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
50         g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
51         g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
52
53         return TRUE;
54 }
55
56
57 /* position in file */
58 struct page_position {
59         FILE_OBJECT *FileObject;
60         LARGE_INTEGER FileOffset;       /* always PAGE_SIZE aligned */
61         int shmid;
62         GList *privbcb_list;    /* each mapped page has its one private_bcb */
63         };
64
65 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
66 static GHashTable *page_position_hash;
67
68 static gboolean validate_page_position(const struct page_position *pagepos)
69 {
70 int errint;
71 struct shmid_ds shmid_ds;
72
73         g_return_val_if_fail(pagepos!=NULL,FALSE);
74         g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
75         g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
76         g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
77         /* 'pagepos->shmid' may be -1 */
78         /* 'pagepos->privbcb_list' may be empty */
79         g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL),FALSE);        /* either deleted or alive */
80
81         if (pagepos->shmid!=-1) {
82                 errint=shmctl(pagepos->shmid,
83                                 IPC_STAT,       /* cmd */
84                                 &shmid_ds);     /* buf */
85                 g_return_val_if_fail(errint==0,FALSE);
86
87                 g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
88                 g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
89                 g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
90                 g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
91                 /* 'shm_perm.mode' was seen with sticky bit 01000: */
92                 g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
93                 g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
94                 g_return_val_if_fail(shmid_ds.shm_cpid==getpid(),FALSE);
95                 g_return_val_if_fail(shmid_ds.shm_lpid==getpid(),FALSE);
96                 g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
97                 }
98
99         return TRUE;
100 }
101
102 static guint page_position_hash_hash_func(const struct page_position *key)
103 {
104         g_return_val_if_fail(validate_page_position(key),0);
105
106         return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
107 }
108
109 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
110 {
111         g_return_val_if_fail(validate_page_position(a),FALSE);
112         g_return_val_if_fail(validate_page_position(b),FALSE);
113
114         return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
115 }
116
117 static void page_position_hash_key_destroy_func(struct page_position *key)
118 {
119         g_return_if_fail(validate_page_position(key));
120         g_assert(key->privbcb_list==NULL);
121         g_assert(key->shmid==-1);
122
123         g_free(key);
124 }
125
126 static void page_position_hash_init(void)
127 {
128         if (page_position_hash)
129                 return;
130         page_position_hash=g_hash_table_new_full(
131                         (GHashFunc)page_position_hash_hash_func,        /* hash_func */
132                         (GEqualFunc)page_position_hash_key_equal_func,  /* key_equal_func */
133                         (GDestroyNotify)page_position_hash_key_destroy_func,    /* key_destroy_func */
134                         NULL);  /* value_destroy_func */
135 }
136
137
138 struct private_bcb {
139         PUBLIC_BCB *PublicBcb;  /* ->MappedLength, ->MappedFileOffset */
140         FILE_OBJECT *FileObject;
141         gint ref_count;
142         gboolean idle_func_pending;
143         /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
144         ULONG MappedLength;     /* It is the real requested size; it is not PAGE_SIZE aligned. */
145         /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
146         LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
147         gpointer base;  /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
148         gboolean dirty;
149         LARGE_INTEGER lsn; gboolean lsn_valid;
150         };
151
152 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
153 static GHashTable *private_bcb_hash;
154
155 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
156 {
157         g_return_if_fail(validate_Bcb(key));
158
159         g_free(key);
160 }
161
162 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
163 {
164 struct page_position pagepos_local;
165 gboolean errbool;
166 int errint;
167 size_t offset;
168 gpointer base_aligned;
169
170         g_return_if_fail(value!=NULL);
171         /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
172          * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
173          */
174         g_return_if_fail(value->PublicBcb!=NULL);
175         g_return_if_fail(value->FileObject!=NULL);
176         g_return_if_fail(value->ref_count==0);
177         g_return_if_fail(value->MappedLength>0);
178         g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
179         g_return_if_fail(value->base!=NULL);
180         g_return_if_fail(value->dirty==FALSE);
181
182         page_position_hash_init();
183
184         base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
185
186         pagepos_local.FileObject=value->FileObject;
187         pagepos_local.privbcb_list=NULL;
188         pagepos_local.shmid=-1;
189         for (
190                         offset=0;
191                         offset<value->MappedLength;
192                         offset+=PAGE_SIZE) {
193 struct page_position *pagepos;
194
195                 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
196                 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
197                 g_assert(validate_page_position(pagepos));
198                 g_assert(pagepos->privbcb_list!=NULL);
199                 errint=shmdt(((char *)base_aligned)+offset);
200                 g_assert(errint==0);
201
202                 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
203                 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
204                 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
205
206                 if (!pagepos->privbcb_list) {
207                         /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
208                         pagepos->shmid=-1;
209                         errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
210                         g_assert(errbool==TRUE);
211                         }
212                 else
213                         g_assert(validate_page_position(pagepos));
214                 }
215
216         g_free(value);
217 }
218
219 static void private_bcb_hash_init(void)
220 {
221         if (private_bcb_hash)
222                 return;
223         private_bcb_hash=g_hash_table_new_full(
224                         g_direct_hash,  /* hash_func */
225                         g_direct_equal, /* key_equal_func */
226                         (GDestroyNotify)private_bcb_hash_key_destroy_func,      /* key_destroy_func */
227                         (GDestroyNotify)private_bcb_hash_value_destroy_func);   /* value_destroy_func */
228 }
229
230
231 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
232 {
233 MDL *Mdl;
234 KEVENT Event;
235 IO_STATUS_BLOCK IoStatus;
236 NTSTATUS err;
237
238         g_return_val_if_fail(FileObject!=NULL,0);
239         g_return_val_if_fail(address!=0,0);
240         g_return_val_if_fail(length!=0,0);
241         g_return_val_if_fail(FileOffset!=NULL,0);
242
243         /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
244          * successfuly read the data. Workaround it - preclear (not postclear) the
245          * buffer and do not make any other assumptions about the data read.
246          */
247         memset(address,0,PAGE_SIZE);    /* pre-clear the buffer */
248         Mdl=MmCreateMdl(NULL,address,PAGE_SIZE);        /* FIXME: Deprecated in favor of IoAllocateMdl() */
249         g_assert(Mdl!=NULL);
250         KeInitializeEvent(&Event,NotificationEvent,FALSE);
251         /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
252         IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
253         err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
254         g_assert(NT_SUCCESS(err));
255         g_assert(NT_SUCCESS(IoStatus.Status));
256         /* It is not == as the file may be shorter than requested */
257         g_assert(IoStatus.Information<=length);
258         IoFreeMdl(Mdl);
259
260         /* Forbidden, see the comment above about ext2fsd.sys.
261          * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
262          */
263
264         return IoStatus.Information;    /* may be shorter than real! */
265 }
266
267
268 static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,
269                 PUBLIC_BCB *Bcb_find)
270 {
271 struct page_position pagepos_local,*pagepos;
272 struct private_bcb *privbcb,*privbcb_listitem;
273 GList *privbcb_list;
274
275         g_return_val_if_fail(FileObject!=NULL,NULL);
276         g_return_val_if_fail(FileOffset!=NULL,NULL);
277         /* 'Bcb_find' may be NULL */
278
279         page_position_hash_init();
280
281         pagepos_local.FileObject=FileObject;
282         pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
283         pagepos_local.privbcb_list=NULL;
284         pagepos_local.shmid=-1;
285         if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local)))
286                 return NULL;
287         g_assert(validate_page_position(pagepos));
288         g_assert(pagepos->privbcb_list!=NULL);
289
290         privbcb=NULL;
291         for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
292                 privbcb_listitem=privbcb_list->data;
293                 if (1
294                                 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
295                                 && privbcb_listitem->MappedLength==Length
296                                 && (!Bcb_find || Bcb_find==privbcb_listitem->PublicBcb)) {
297                         g_assert(privbcb==NULL);        /* appropriate 'Bcb_find'-matching privbcb found twice */
298                         privbcb=privbcb_listitem;
299                         if (!Bcb_find)
300                                 break;
301                         }
302                 }
303         if (!privbcb)
304                 return NULL;
305
306         /* Sanity check 'privbcb': */
307         g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
308
309         return privbcb;
310 }
311
312
313 /**
314  * CcMapData:
315  * @FileObject: Initialized open #FileObject to map.
316  * %NULL value is forbidden.
317  * @FileOffset: The @FileObject file offset from where to map the region from.
318  * Negative value is forbidden.
319  * @Length: Requested length of the region to map from @FileObject.
320  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
321  * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
322  * %MAP_NO_READ currently ignored; we map the data always.
323  * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
324  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
325  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
326  * %NULL pointer is forbidden.
327  * @Buffer: Returns the mapped memory region start address.
328  * This address may not be %PAGE_SIZE aligned.
329  * %NULL pointer is forbidden.
330  *
331  * Maps the specified region of @FileObject to automatically chosen address space.
332  * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
333  *
334  * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
335  * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
336  * but W32 filesystems apparently do not conform to it. :-)  If you do not take care of the
337  * modified data by some dirty-marking facility such data will be carelessly dropped without
338  * their commit to the disk.
339  *
340  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
341  *
342  * We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
343  * even if such mapping for such file already exists.
344  * We should probably create a new #Bcb for the same space,
345  * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
346  *
347  * Returns: %TRUE if the region was successfuly mapped.
348  * @Bcb with the initialized new memory region.
349  * @Buffer with the address of the exact byte specified by @FileOffset.
350  */
351 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
352                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
353 {
354 PUBLIC_BCB **PublicBcbp,*PublicBcb;
355 struct page_position pagepos_local,*pagepos;
356 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
357 gpointer base_aligned;
358 size_t offset,length_mapped_aligned;
359 int errint;
360 gpointer errptr;
361 struct private_bcb *privbcb;
362 gboolean after_eof=FALSE;       /* Did we reached the end of file already? */
363 GPtrArray *read_array;
364
365         g_return_val_if_fail(FileObject!=NULL,FALSE);
366         g_return_val_if_fail(FileOffset!=NULL,FALSE);
367         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
368         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
369         g_return_val_if_fail(Flags&MAP_WAIT,FALSE);     /* FIXME: on-demand loading not yet implemented */
370         g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE);   /* unknown flags? */
371         g_return_val_if_fail(Bcb!=NULL,FALSE);
372         g_return_val_if_fail(Buffer!=NULL,FALSE);
373
374         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
375                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
376
377         g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
378         g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
379         /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
380          * 'SectorSize' may not yet be initialized during mount operation
381          * and 'FileObject->DeviceObject->Vpb' may exist in such case.
382          */
383         g_return_val_if_fail(0
384                                         || FileObject->DeviceObject->SectorSize==0      /* prevent division by 0 */
385                                         || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),
386                         FALSE);
387
388         page_position_hash_init();
389         private_bcb_hash_init();
390
391         PublicBcbp=(PUBLIC_BCB **)Bcb;
392         /* extend 'FileOffset' and 'Length' to page boundaries */
393         FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
394         FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
395         length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
396
397         /* We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
398          * even if such mapping for such file already exists.
399          * We should probably create a new Bcb for the same space,
400          * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
401          */
402         if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL)))
403                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX already mapped by privbcb %p",
404                                 G_STRLOC,FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,privbcb);
405
406         /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
407         /* TODO: on-demand loading */
408         /* Although we do zeroed-page mapping here we just reserve the linears
409          * space by it - all the page will be overriden by shmat(2) afterwards anyway.
410          */
411         base_aligned=mmap(
412                         NULL,   /* start */
413                         length_mapped_aligned,  /* length */
414                         PROT_READ|PROT_WRITE,   /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
415                         MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
416                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
417                         0);     /* offset; ignored due to MAP_ANONYMOUS */
418         g_assert(base_aligned!=NULL);
419
420         /* Create 'PublicBcb'; referenced by 'privbcb'. */
421         captive_new(PublicBcb);
422         PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
423         PublicBcb->NodeByteSize=sizeof(*PublicBcb);     /* we have no extensions there */
424         PublicBcb->MappedLength=Length;
425         PublicBcb->MappedFileOffset=*FileOffset;
426
427         /* Create 'privbcb'; referenced by created 'pagepos'es. */
428         captive_new(privbcb);
429         privbcb->PublicBcb=PublicBcb;
430         privbcb->FileObject=FileObject;
431         privbcb->ref_count=1;
432         privbcb->idle_func_pending=FALSE;
433         privbcb->MappedLength=PublicBcb->MappedLength;
434         privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
435         privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
436         privbcb->dirty=FALSE;
437         privbcb->lsn_valid=FALSE;
438         g_hash_table_insert(private_bcb_hash,
439                         PublicBcb,      /* key */
440                         privbcb);       /* value */
441
442         /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
443          * below as captive_Cc_IoPageRead() has very big consequences as it calls
444          * the filesystem code and we may get reentrancy.
445          * Therefore we store all missing page read requests to 'read_array' and we
446          * fill them when all the memory structures are in consistent state.
447          * We can also coalescence the requests more easily this way.
448          */
449         read_array=g_ptr_array_new();
450
451         pagepos_local.FileObject=FileObject;
452         pagepos_local.shmid=-1;
453         pagepos_local.privbcb_list=NULL;
454         for (
455                         offset=0;
456                         offset<length_mapped_aligned;
457                         offset+=PAGE_SIZE) {
458 gpointer pageaddr;
459
460                 pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
461                 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
462                         captive_new(pagepos);
463                         /* FIXME: Reference 'FileObject' to not to leave broken references to it here */
464                         *pagepos=pagepos_local;
465                         pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600);
466                         pagepos->privbcb_list=NULL;
467                         g_ptr_array_add(read_array,pagepos);    /* enlist this 'pagepos' as todo read item */
468                         }
469                 else
470                         g_assert(pagepos->privbcb_list!=NULL);
471                 g_assert(pagepos->shmid!=-1);
472                 pageaddr=(gpointer)(((char *)base_aligned)+offset);
473                 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
474                  * mmap(2) is still needed to get linear block of memory assignment.
475                  */
476                 /* TODO:thread; munmap()..shmat() window */
477                 errint=munmap(pageaddr,PAGE_SIZE);
478                 g_assert(errint==0);
479                 errptr=shmat(pagepos->shmid,
480                                 pageaddr,       /* shmaddr */
481                                 0);     /* shmflg; !SHM_RDONLY==r/w FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
482                 g_assert(errptr==pageaddr);
483
484                 g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
485                 pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb);    /* order not important */
486                 g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
487                 if (pagepos->privbcb_list->next==NULL) {        /* exactly one item (we just added it now) */
488
489                         errint=shmctl(pagepos->shmid,
490                                         IPC_RMID,       /* cmd */
491                                         NULL);  /* buf */
492                         g_assert(errint==0);
493                         g_hash_table_insert(page_position_hash,
494                                         pagepos,        /* key */
495                                         pagepos);       /* value */
496                         }
497                 g_assert(validate_page_position(pagepos));
498                 }
499
500         /* Fill in the missing pages content todo-list stored in 'read_array': */
501         while (read_array->len && (pagepos=g_ptr_array_remove_index(read_array,
502                                         0))) {  /* index */
503 ULONG read_size;
504 ULONG got;
505 struct pagepos *pagepos_next;
506 gpointer pageaddr,pageaddr_next;
507
508                 pageaddr=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
509                 read_size=PAGE_SIZE;
510                 /* Coalescence of the requests */
511                 while (read_array->len) {
512                         pagepos_next=g_ptr_array_index(read_array,
513                                         0);     /* index */
514                         pageaddr_next=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
515                         if (pageaddr_next!=((char *)pageaddr)+read_size)
516                                 break;
517                         read_size+=PAGE_SIZE;
518                         g_assert(pagepos_next==g_ptr_array_remove_index(read_array,
519                                         0));    /* index */
520                         }
521                 /* Read the range content: */
522                 got=captive_Cc_IoPageRead(FileObject,pageaddr,read_size,&pagepos->FileOffset);
523                 if (after_eof)
524                         g_assert(got==0);
525                 else
526                         g_assert(got<=PAGE_SIZE);
527                 after_eof=(got<PAGE_SIZE);
528                 }
529         g_ptr_array_free(read_array,
530                         TRUE);  /* free_seg; free the array of gpointer(==struct pagepos *) items */
531
532         /* offset _into_ page, may not be PAGE_SIZE aligned: */
533         *Buffer=privbcb->base;
534         *PublicBcbp=PublicBcb;
535         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
536                         "%s: result: privbcb->base=%p,privbcb->base+privbcb->MappedLength=%p,privbcb->MappedLength=0x%lX",
537                         G_STRLOC,privbcb->base,((char *)privbcb->base)+privbcb->MappedLength,(unsigned long)privbcb->MappedLength);
538         g_assert(validate_Bcb(PublicBcb)==TRUE);
539         if (!FileObject->SectionObjectPointers->SharedCacheMap)
540                 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
541         else {
542                 /* FIXME: (NOTE*1) Weird but it appears as fastfat.sys during make_directory()
543                  * can pass us an already cached 'FileObject' (with different offset/size).
544                  * We must not CcUnpinData() it as its BCB is still referenced (CcUnpinData()ed) by fastfat.sys.
545                  */
546                 FileObject->SectionObjectPointers->SharedCacheMap=NULL;
547                 }
548         FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
549
550         return TRUE;
551 }
552
553
554 /**
555  * CcPinMappedData:
556  * @FileObject: Initialized open #FileObject to map.
557  * %NULL value is forbidden.
558  * @MappedFileOffset: The @FileObject file offset from where to map the region from.
559  * Negative value is forbidden.
560  * @MappedLength: Requested length of the region to map from @FileObject.
561  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
562  * @Wait: Whether disk waiting is permitted for this function.
563  * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
564  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
565  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
566  * %PIN_NO_READ currently ignored; we map the data always.
567  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
568  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
569  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
570  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
571  * %NULL pointer is forbidden.
572  * @Buffer: Returns the mapped memory region start address.
573  * This address may not be %PAGE_SIZE aligned.
574  * %NULL pointer is forbidden.
575  *
576  * This function will allow you to modify the data mapped by CcMapData().
577  * libcaptive does not differentiate this function with CcMapData().
578  *
579  * NEVER re-read any memory from FileObject here!
580  * at least fastfat.sys directory create relies on the fact of CcPinRead()
581  * with already modified buffers to be left intact.
582  *
583  * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
584  * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
585  *
586  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
587  *
588  * Returns: %TRUE if the region was successfuly mapped.
589  * @Bcb with the initialized new memory region.
590  * @Buffer with the address of the exact byte specified by @FileOffset.
591  */
592 BOOLEAN CcPinMappedData
593                 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
594 {
595 struct private_bcb *privbcb;
596
597         g_return_val_if_fail(FileObject!=NULL,FALSE);
598         g_return_val_if_fail(FileOffset!=NULL,FALSE);
599         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
600         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
601         /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
602         g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
603         g_return_val_if_fail(Bcb!=NULL,FALSE);
604
605         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
606                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
607
608         privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL);
609         if (!privbcb && (Flags & PIN_IF_BCB))   /* BCB does not exist */
610                 return FALSE;
611         /* Appropriate privbcb not found.
612          * This IMO should not happen and it is a bug in the client.
613          * Unfortuantely fastfat.sys FatLocateVolumeLabel() will CcPinMappedData()
614          * the volume label dirent without any previous CcMapData() or CcPinRead() !
615          */
616         if (!privbcb) {
617 PVOID Buffer;
618
619                 return CcPinRead(
620                                 FileObject,
621                                 FileOffset,
622                                 Length,
623                                 Flags,
624                                 Bcb,
625                                 &Buffer);
626                 }
627
628         /* NEVER re-read any memory from FileObject here! */
629
630         privbcb->ref_count++;
631
632         /* Memory already mapped by CcMapData(). */
633         *Bcb=privbcb->PublicBcb;
634         return TRUE;
635 }
636
637
638 /**
639  * CcPinRead:
640  * @FileObject: Initialized open #FileObject to map.
641  * %NULL value is forbidden.
642  * @FileOffset: The @FileObject file offset from where to map the region from.
643  * Negative value is forbidden.
644  * @Length: Requested length of the region to map from @FileObject.
645  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
646  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
647  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
648  * %PIN_NO_READ currently ignored; we map the data always.
649  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
650  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
651  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
652  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
653  * %NULL pointer is forbidden.
654  * @Buffer: Returns the mapped memory region start address.
655  * This address may not be %PAGE_SIZE aligned.
656  * %NULL pointer is forbidden.
657  *
658  * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
659  * See these two functions for the details. It has a difference to subsequent
660  * calling of CcMapData() and CcPinMappedData() instead as this call counts
661  * only as one function for a corresponding CcUnpinData() call.
662  *
663  * Every call to this function must be matched by a one corresponding CcUnpinData() call.
664  *
665  * Returns: %TRUE if the region was successfuly mapped.
666  * @Bcb with the initialized new memory region.
667  * @Buffer with the address of the exact byte specified by @FileOffset.
668  */
669 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
670                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
671 {
672 PVOID Bcb_CcPinMappedData;
673 gboolean errbool;
674 gboolean count_CcMapData;
675 struct private_bcb *privbcb;
676
677         g_return_val_if_fail(FileObject!=NULL,FALSE);
678         g_return_val_if_fail(FileOffset!=NULL,FALSE);
679         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
680         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
681         /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
682         g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
683         g_return_val_if_fail(Bcb!=NULL,FALSE);
684         g_return_val_if_fail(Buffer!=NULL,FALSE);
685
686         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
687                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
688
689         if (!(Flags&PIN_IF_BCB)) {
690                 errbool=CcMapData(FileObject,FileOffset,Length,
691                                 0               /* Flags */
692                                                 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
693                                 Bcb,Buffer);
694                 g_return_val_if_fail(errbool==TRUE,FALSE);
695                 count_CcMapData=TRUE;
696                 }
697         else
698                 count_CcMapData=FALSE;
699
700         errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
701         if (!(Flags&PIN_IF_BCB)) {
702                 g_return_val_if_fail(errbool==TRUE,FALSE);
703                 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
704                 }
705         else {
706                 if (errbool==FALSE)     /* FALSE permitted; We may fail if Bcb does not exist yet. */
707                         return FALSE;
708                 *Bcb=Bcb_CcPinMappedData;
709                 }
710
711         privbcb=captive_privbcb_find(FileObject,FileOffset,Length,*Bcb);
712         g_assert(privbcb!=NULL);
713         g_assert(privbcb->ref_count>=1);
714         g_assert(privbcb->PublicBcb==*Bcb);
715         if (count_CcMapData) {
716                 /* CcPinRead() must always reference-count only by 1 despite any sub-called functions! */
717                 g_assert(privbcb->ref_count>=2);
718                 privbcb->ref_count--;
719                 }
720
721         return TRUE;
722 }
723
724
725 static void logging_notify_privbcb_flush(struct private_bcb *privbcb);
726
727 static void captive_privbcb_flush(struct private_bcb *privbcb)
728 {
729 MDL *Mdl;
730 KEVENT Event;
731 IO_STATUS_BLOCK IoStatus;
732 NTSTATUS err;
733 gpointer base_sectoraligned;
734 gsize length_sectoraligned;
735 LARGE_INTEGER FileOffset_sectoraligned;
736 gsize sectorsize;
737
738         if (!privbcb->dirty)
739                 return;
740
741         logging_notify_privbcb_flush(privbcb);
742
743         g_assert(privbcb->FileObject->DeviceObject!=NULL);
744
745         /* We can get 0=='privbcb->FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
746          * FIXME: We are unable to find the correct sectorsize for a filesystem as
747          * even the 'CommonFcb' below contains invalid information.
748          * As we need to have 'sectorsize' <=filesystem_blocksize at least for ext2fsd.sys
749          * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
750          * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
751          */
752         sectorsize=PAGE_SIZE;
753 #if 0
754         sectorsize=privbcb->FileObject->DeviceObject->SectorSize;
755         if (privbcb->FileObject->FsContext) {
756 REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileObject->FsContext;
757
758                 /* FIXME: Check CommonFcb->Type */
759                 /* 'AllocationSize' can be less than 'sectorsize' if the total file length is smaller.
760                  * Observed with ext2fsd.sys volume-file.
761                  */
762                 if (sectorsize<CommonFcb->AllocationSize.QuadPart)
763                         sectorsize=CommonFcb->AllocationSize.QuadPart;
764                 }
765 #endif
766
767         /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
768         g_assert(sectorsize>0);
769         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,sectorsize));
770         /* We align here directly the 'privbcb->base' which is not correct.
771          * We should rather aligned according to 'privbcb->MappedOffset' but
772          * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
773          * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
774          */
775         g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
776                                  ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,sectorsize));
777         base_sectoraligned  =CAPTIVE_ROUND_DOWN(privbcb->base,sectorsize);
778         length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,sectorsize)
779                         -((char *)base_sectoraligned);
780         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,sectorsize));
781         FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,sectorsize);
782
783         Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
784         g_assert(Mdl!=NULL);
785
786         KeInitializeEvent(&Event,NotificationEvent,FALSE);
787
788         /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
789         /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
790         err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
791         g_assert(NT_SUCCESS(err));
792         g_assert(NT_SUCCESS(IoStatus.Status));
793         /* We should write at least the unaligned mapped data although we
794          * do not need to successfuly write the whole aligned amount.
795          * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
796          * during FAT write by fastfat.sys.
797          */
798         g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
799                         +privbcb->MappedLength);
800         g_assert(IoStatus.Information<=length_sectoraligned);
801
802         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
803                         "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
804                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
805                         base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
806                         (gulong)IoStatus.Information);
807
808         privbcb->dirty=FALSE;
809 }
810
811
812 static gboolean CcUnpinData_idle_func(struct private_bcb *privbcb /*data*/)
813 {
814 gboolean errbool;
815
816         g_return_val_if_fail(privbcb!=NULL,FALSE);      /* remove-me */
817         g_return_val_if_fail(privbcb->PublicBcb!=NULL,FALSE);   /* remove-me */
818
819         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
820                         "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
821                         ",privbcb->idle_func_pending=%d",G_STRLOC,
822                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
823                         (int)privbcb->idle_func_pending);
824
825         g_assert(privbcb->idle_func_pending==TRUE);
826         privbcb->idle_func_pending=FALSE;
827
828         g_assert(privbcb->ref_count>0);
829
830         if (--privbcb->ref_count)
831                 return FALSE;   /* remove-me */
832
833         /* We were called as CcUnpinData()->captive_privbcb_flush()->CcUnpinData()->CcUnpinData_idle_func()
834          */
835         if (privbcb->dirty) {
836                 privbcb->ref_count++;
837                 return FALSE;   /* remove-me */
838                 }
839
840         errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
841         g_assert(errbool==TRUE);
842
843         return FALSE;   /* remove-me */
844 }
845
846 /**
847  * CcUnpinData:
848  * @Bcb: Initialized #PUBLIC_BCB structure.
849  * %NULL value is forbidden.
850  *
851  * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
852  */
853 VOID CcUnpinData(IN PVOID Bcb)
854 {
855 PUBLIC_BCB *PublicBcb;
856 struct private_bcb *privbcb;
857
858         g_return_if_fail(validate_Bcb(Bcb));
859
860         private_bcb_hash_init();
861
862         PublicBcb=(PUBLIC_BCB *)Bcb;
863         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
864         g_return_if_fail(privbcb!=NULL);
865
866         g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
867         /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
868
869         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
870                         "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
871                         ",privbcb->idle_func_pending=%d",G_STRLOC,
872                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
873                         (int)privbcb->idle_func_pending);
874
875 idle_retry:
876         g_assert(privbcb->ref_count>0);
877         /* Do not write back the contents if this is not the final unpin.
878          * FIXME: Is it correct?
879          */
880         if (--privbcb->ref_count) {
881                 /* CcUnpinData() end of the cycle for the second time while CcUnpinData_idle_func()
882                  * was not yet invoked.
883                  */
884                 if (privbcb->ref_count==1 && privbcb->idle_func_pending) {
885                         captive_privbcb_flush(privbcb);
886                         goto idle_retry;
887                         }
888                 return;
889                 }
890         privbcb->ref_count++;   /* it will get decreased again in CcUnpinData_idle_func() */
891
892         captive_privbcb_flush(privbcb);
893
894         g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
895         /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
896         if (privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb)
897                 privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
898
899         /* Caboom: lfs (log file system) of ntfs.sys-NT5.1sp1 will do:
900          * CcPinRead(); CcUnpinData(); access Buffer (wanna-crash);
901          * Therefore we must postpone the buffer unmapping to some idle function...
902          * I expect it a bug in ntfs.sys.
903          */
904         if (!privbcb->idle_func_pending) {
905                 privbcb->idle_func_pending=TRUE;
906                 g_idle_add_full(
907                                 G_PRIORITY_DEFAULT_IDLE,        /* priority */
908                                 (GSourceFunc)CcUnpinData_idle_func,     /* function */
909                                 privbcb,        /* data */
910                                 NULL);  /* notify */
911                 }
912 }
913
914
915 VOID CcRepinBcb(IN PVOID Bcb)
916 {
917 PUBLIC_BCB *PublicBcb;
918 struct private_bcb *privbcb;
919
920         g_return_if_fail(validate_Bcb(Bcb));
921
922         private_bcb_hash_init();
923
924         PublicBcb=(PUBLIC_BCB *)Bcb;
925         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
926         g_return_if_fail(privbcb!=NULL);
927
928         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
929                         Bcb,privbcb->FileObject);
930
931         privbcb->ref_count++;
932 }
933
934
935 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
936 {
937 PUBLIC_BCB *PublicBcb;
938 struct private_bcb *privbcb;
939
940         g_return_if_fail(validate_Bcb(Bcb));
941         g_return_if_fail(IoStatus!=NULL);
942
943         private_bcb_hash_init();
944
945         PublicBcb=(PUBLIC_BCB *)Bcb;
946         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
947         g_return_if_fail(privbcb!=NULL);
948
949         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
950                         Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
951
952         IoStatus->Status=STATUS_SUCCESS;
953         IoStatus->Information=privbcb->MappedLength;
954
955         CcUnpinData(Bcb);
956 }
957
958
959 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
960 {
961 PUBLIC_BCB *PublicBcb;
962 struct private_bcb *privbcb;
963
964         g_return_if_fail(validate_Bcb(Bcb));
965
966         private_bcb_hash_init();
967
968         PublicBcb=(PUBLIC_BCB *)Bcb;
969         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
970         g_return_if_fail(privbcb!=NULL);
971
972         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
973                         Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
974
975         /* 'privbcb->ref_count' not to be increased by this function. */
976
977         if (Lsn) {
978                 privbcb->lsn=*Lsn;
979                 privbcb->lsn_valid=TRUE;
980                 }
981
982         privbcb->dirty=TRUE;
983 }
984
985
986 /**
987  * CcSetFileSizes:
988  * @FileObject: Initialized open #FileObject to update file sizes of.
989  * %NULL value is forbidden.
990  * @FileSizes: New file sizes to update cache to.
991  * %NULL value is forbidden.
992  * 
993  * Update cache properties after file sizes were updated.
994  * Probably only the exceeding pages need to be unmapped and BCBs updated
995  * if FileSizes->AllocationSize gets shrunk.
996  *
997  * FIXME: Currently a NOP with no effect by libcaptive.
998  */
999 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
1000 {
1001         g_return_if_fail(FileObject!=NULL);
1002         g_return_if_fail(FileSizes!=NULL);
1003
1004         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
1005                         "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
1006                         FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
1007                         (guint64)FileSizes->ValidDataLength.QuadPart);
1008
1009         /* FIXME: check BCB && 'struct page_position' invalidities */
1010 }
1011
1012
1013 /**
1014  * CcPurgeCacheSection:
1015  * @SectionObjectPointer: Pointer specifying file to purge;
1016  * %NULL value is forbidden.
1017  * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1018  * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %FALSE return code in such case.
1019  * @FileOffset: Starting offset of the ranger to purge.
1020  * %NULL pointer is permitted and it means to purge the whole whole.
1021  * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
1022  * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
1023  * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
1024  *
1025  * Drop any caching for shrunken file which is not being deleted.
1026  * libcaptive will no longer consider such #BCB as dirty.
1027  *
1028  * Returns: %TRUE if the range was purged successfuly.
1029  */
1030 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1031                 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
1032 {
1033 PUBLIC_BCB *PublicBcb;
1034 struct private_bcb *privbcb;
1035
1036         g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
1037         if (SectionObjectPointer->SharedCacheMap==NULL)
1038                 return FALSE;   /* failed - nothing to purge */
1039         g_return_val_if_fail(FileOffset==NULL,FALSE);   /* NOT IMPLEMENTED YET */
1040
1041         PublicBcb=SectionObjectPointer->SharedCacheMap;
1042         g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1043
1044         private_bcb_hash_init();
1045
1046         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1047         g_return_val_if_fail(privbcb!=NULL,FALSE);
1048
1049         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1050                         "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
1051                         SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1052                         (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
1053
1054         privbcb->dirty=FALSE;   /* purge it */
1055
1056         return TRUE;
1057 }
1058
1059
1060 /**
1061  * CcCopyRead:
1062  * @FileObject: Initialized open #FileObject to map.
1063  * %NULL value is forbidden.
1064  * @FileOffset: The @FileObject file offset from where to map the region from.
1065  * Negative value is forbidden.
1066  * @Length: Requested length of the region to map from @FileObject.
1067  * Value %0 is permitted (no effect of this function call).
1068  * @Wait: Whether disk waiting is permitted for this function.
1069  * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1070  * @Buffer: Address of memory region with already allocated memory of size @Length.
1071  * This address may not be %PAGE_SIZE aligned.
1072  * %NULL pointer is forbidden.
1073  * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1074  * %NULL pointer is forbidden.
1075  *
1076  * Reads the specified region of @FileObject to the given @Buffer.
1077  * No on-demand loading is in effect.
1078  *
1079  * Returns: %TRUE if the region was successfuly filled with @Length bytes.
1080  * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
1081  * @IoStatus.Information initialized by @Length if successful.
1082  */
1083 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
1084                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
1085 {
1086 PVOID MappedBcb;
1087 PVOID MappedBuffer;
1088 gboolean errbool;
1089
1090         g_return_val_if_fail(FileObject!=NULL,FALSE);
1091         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1092         g_return_val_if_fail(Buffer!=NULL,FALSE);
1093         g_return_val_if_fail(IoStatus!=NULL,FALSE);
1094
1095         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1096                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1097
1098         IoStatus->Status=STATUS_UNSUCCESSFUL;
1099         IoStatus->Information=0;
1100
1101         if (Length) {
1102                 errbool=CcPinRead(
1103                                 FileObject,     /* FileObject */
1104                                 FileOffset,     /* FileOffset */
1105                                 Length, /* Length */
1106                                 Wait,   /* Wait */
1107                                 &MappedBcb,     /* Bcb */
1108                                 &MappedBuffer); /* Buffer */
1109                 g_return_val_if_fail(errbool==TRUE,FALSE);
1110
1111                 memcpy(Buffer,MappedBuffer,Length);
1112
1113                 CcUnpinData(MappedBcb); /* no error code */
1114                 }
1115
1116         IoStatus->Status=STATUS_SUCCESS;
1117         IoStatus->Information=Length;
1118
1119         return TRUE;
1120 }
1121
1122
1123 /**
1124  * CcCopyWrite:
1125  * @FileObject: Initialized open #FileObject to map.
1126  * %NULL value is forbidden.
1127  * @FileOffset: The @FileObject file offset from where to map the region from.
1128  * Negative value is forbidden.
1129  * @Length: Requested length of the region to map from @FileObject.
1130  * Value %0 is permitted (no effect of this function call).
1131  * @Wait: Whether disk waiting is permitted for this function.
1132  * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1133  * @Buffer: Address of memory region with already allocated memory of size @Length.
1134  * This address may not be %PAGE_SIZE aligned.
1135  * %NULL pointer is forbidden.
1136  *
1137  * Writes the specified region of the given @Buffer to @FileObject.
1138  *
1139  * Returns: %TRUE if the region was successfuly written with @Length bytes.
1140  */
1141 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
1142                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
1143 {
1144 PVOID MappedBcb;
1145 PVOID MappedBuffer;
1146 gboolean errbool;
1147
1148         g_return_val_if_fail(FileObject!=NULL,FALSE);
1149         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1150         g_return_val_if_fail(Buffer!=NULL,FALSE);
1151
1152         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1153                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1154
1155         if (Length) {
1156                 errbool=CcPinRead(
1157                                 FileObject,     /* FileObject */
1158                                 FileOffset,     /* FileOffset */
1159                                 Length, /* Length */
1160                                 Wait,   /* Wait */
1161                                 &MappedBcb,     /* Bcb */
1162                                 &MappedBuffer); /* Buffer */
1163                 g_return_val_if_fail(errbool==TRUE,FALSE);
1164
1165                 memcpy(MappedBuffer,Buffer,Length);
1166
1167                 CcSetDirtyPinnedData(
1168                                 MappedBcb,      /* Bcb */
1169                                 NULL);  /* Lsn */
1170                 CcUnpinData(MappedBcb); /* no error code */
1171                 }
1172
1173         return TRUE;
1174 }
1175
1176
1177 /**
1178  * CcCanIWrite:
1179  * @FileObject: Initialized open #FileObject to map.
1180  * %NULL value is forbidden.
1181  * @BytesToWrite: Amount of data to be asked whether it will be accepted.
1182  * Value %0 is permitted.
1183  * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
1184  * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
1185  *
1186  * Asks cache manager if it would currently accept write request to @FileObject
1187  * of @BytesToWrite bytes with @Wait condition.
1188  * libcaptive will always accept any writes. This function is a NOP.
1189  *
1190  * Returns: libcaptive always returns %TRUE.
1191  */
1192 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
1193 {
1194         g_return_val_if_fail(FileObject!=NULL,FALSE);
1195
1196         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
1197                         FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
1198
1199         return TRUE;
1200 }
1201
1202
1203 /**
1204  * CcSetReadAheadGranularity:
1205  * @FileObject: Initialized open #FileObject to map.
1206  * %NULL value is forbidden.
1207  * @Granularity: Suggested size of the cache element.
1208  * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
1209  *
1210  * libcaptive does not implement any caching and therefore this function
1211  * is a NOP there.
1212  */
1213 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
1214 {
1215         g_return_if_fail(FileObject!=NULL);
1216         g_return_if_fail(Granularity>=PAGE_SIZE);
1217         g_return_if_fail((Granularity&(Granularity-1))==0);     /* Power of two */
1218
1219         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
1220                         FileObject,(gulong)Granularity);
1221
1222         /* NOP; no caching by libcaptive */
1223 }
1224
1225
1226 /**
1227  * CcFlushCache:
1228  * @SectionObjectPointer: Pointer specifying file to flush;
1229  * %NULL value is forbidden.
1230  * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1231  * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
1232  * @FileOffset: Optional starting point of the range to flush.
1233  * %NULL value is permitted.
1234  * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
1235  * @IoStatus: Optionally returns the resulting operation status.
1236  * #Information field will contain the number of bytes flushed.
1237  * %NULL value is permitted.
1238  *
1239  * Flushes out any pending dirty data in cache manager BCB mapping.
1240  * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
1241  */
1242 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1243                 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
1244 {
1245 PUBLIC_BCB *PublicBcb;
1246 struct private_bcb *privbcb;
1247 IO_STATUS_BLOCK IoStatus_CcUnpinRepinnedBcb_local;
1248
1249         g_return_if_fail(SectionObjectPointer!=NULL);
1250
1251         if (SectionObjectPointer->SharedCacheMap==NULL) {
1252 success:
1253                 if (IoStatus) {
1254                         IoStatus->Status=STATUS_SUCCESS;
1255                         IoStatus->Information=0;
1256                         }
1257                 return;
1258                 }
1259
1260         PublicBcb=SectionObjectPointer->SharedCacheMap;
1261         g_return_if_fail(validate_Bcb(PublicBcb));
1262
1263         private_bcb_hash_init();
1264
1265         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1266         g_return_if_fail(privbcb!=NULL);
1267
1268         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1269                         "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
1270                         SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1271                         (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
1272
1273         if (FileOffset) {
1274                 if (FileOffset->QuadPart       >=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
1275                         goto success;
1276                 if (FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart)
1277                         goto success;
1278                 }
1279
1280         /* We may find some 'privbcb' being already sheduled to be destroyed.
1281          * We need to reference it to not to loose it in the middle of our flush operation.
1282          */
1283         CcRepinBcb(PublicBcb);
1284
1285         /* FIXME: Flush just FileOffset..FileOfset+Length part */
1286         captive_privbcb_flush(privbcb);
1287
1288         CcUnpinRepinnedBcb(
1289                         PublicBcb,      /* Bcb */
1290                         TRUE,   /* WriteThrough; ignored by libcaptive */
1291                         &IoStatus_CcUnpinRepinnedBcb_local);    /* IoStatus; ignored here */
1292
1293         if (IoStatus) {
1294                 IoStatus->Status=STATUS_SUCCESS;
1295                 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
1296                 }
1297 }
1298
1299
1300 static gboolean captive_FileObject_ZeroData(PFILE_OBJECT FileObject,guint64 window_bottom,guint64 window_top)
1301 {
1302 MDL *Mdl;
1303 KEVENT Event;
1304 gpointer zerobuffer;
1305 LARGE_INTEGER window_bottom_LargeInteger;
1306 NTSTATUS err;
1307 IO_STATUS_BLOCK IoStatus;
1308
1309         g_return_val_if_fail(FileObject!=NULL,FALSE);
1310         g_return_val_if_fail(window_bottom<=window_top,FALSE);
1311
1312         if (window_bottom==window_top)  /* trivia */
1313                 return TRUE;
1314
1315         g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
1316
1317         /* Such requirement is W32 documented for CcZeroData()
1318          * and it is really a requirement at least for the underlying fastfat.sys.
1319          */
1320         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(window_bottom,FileObject->DeviceObject->SectorSize));
1321         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(window_top   ,FileObject->DeviceObject->SectorSize));
1322
1323         zerobuffer=g_malloc0(window_top-window_bottom);
1324         window_bottom_LargeInteger.QuadPart=window_bottom;
1325
1326         Mdl=MmCreateMdl(NULL,zerobuffer,window_top-window_bottom);
1327         g_assert(Mdl!=NULL);
1328
1329         KeInitializeEvent(&Event,NotificationEvent,FALSE);
1330
1331         /* FIXME: Notify logging subsystem by LSNs? */
1332
1333         /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
1334         err=IoSynchronousPageWrite(FileObject,Mdl,&window_bottom_LargeInteger,&Event,&IoStatus);
1335         g_assert(NT_SUCCESS(err));
1336         g_assert(NT_SUCCESS(IoStatus.Status));
1337         /* We should write up to the last sector touched by the range although we
1338          * do not need to successfuly write the whole aligned amount.
1339          * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
1340          * during FAT write by fastfat.sys.
1341          */
1342         g_assert(IoStatus.Information==0
1343                         || (window_top-window_bottom)==CAPTIVE_ROUND_UP(IoStatus.Information,FileObject->DeviceObject->SectorSize));
1344         g_assert(IoStatus.Information<=window_top-window_bottom);       /* redundant */
1345
1346         g_free(zerobuffer);
1347
1348         return TRUE;
1349 }
1350
1351
1352 BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
1353 {
1354 guint64 window_lower_bottom,window_lower_top;
1355 guint64 window_higher_bottom,window_higher_top;
1356
1357         g_return_val_if_fail(FileObject!=NULL,FALSE);
1358         g_return_val_if_fail(StartOffset!=NULL,FALSE);
1359         g_return_val_if_fail(EndOffset!=NULL,FALSE);
1360         g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
1361
1362         g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
1363
1364         window_lower_bottom=StartOffset->QuadPart;
1365         window_lower_top=EndOffset->QuadPart;
1366         window_higher_bottom=window_higher_top=0;
1367
1368         if (FileObject->SectionObjectPointers->SharedCacheMap!=NULL) {
1369 PUBLIC_BCB *PublicBcb;
1370 struct private_bcb *privbcb;
1371 guint64 window_cached_bottom,window_cached_top;
1372
1373                 PublicBcb=FileObject->SectionObjectPointers->SharedCacheMap;
1374                 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1375
1376                 private_bcb_hash_init();
1377
1378                 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1379                 g_return_val_if_fail(privbcb!=NULL,FALSE);
1380
1381                 window_cached_bottom=MAX(StartOffset->QuadPart,privbcb->MappedFileOffset.QuadPart);
1382                 window_cached_top=MIN(EndOffset->QuadPart,privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
1383
1384                 if (window_cached_bottom<window_cached_top) {
1385                         memset(((char *)privbcb->base)+window_cached_bottom-privbcb->MappedFileOffset.QuadPart,0,window_cached_top-window_cached_bottom);
1386                         privbcb->dirty=TRUE;
1387                         window_higher_top=window_lower_top;
1388                         window_lower_top=window_cached_bottom;
1389                         window_higher_bottom=window_cached_top;
1390                         }
1391                 }
1392         if (window_lower_bottom<window_lower_top) {
1393                 g_return_val_if_fail(Wait==TRUE,FALSE);
1394                 if (!captive_FileObject_ZeroData(FileObject,window_lower_bottom,window_lower_top))
1395                         g_return_val_if_reached(FALSE);
1396                 }
1397         if (window_higher_bottom<window_higher_top) {
1398                 g_return_val_if_fail(Wait==TRUE,FALSE);
1399                 if (!captive_FileObject_ZeroData(FileObject,window_higher_bottom,window_higher_top))
1400                         g_return_val_if_reached(FALSE);
1401                 }
1402
1403         return TRUE;
1404 }
1405
1406
1407 /* map (PVOID LogHandle) -> (GList (of FILE_OBJECT *) *FileObject_list) */
1408 static GHashTable *log_handle_hash;
1409
1410 static void log_handle_hash_init(void)
1411 {
1412         if (log_handle_hash)
1413                 return;
1414         log_handle_hash=g_hash_table_new(
1415                         g_direct_hash,  /* hash_func */
1416                         g_direct_equal);        /* key_equal_func */
1417 }
1418
1419 /* map (FILE_OBJECT *FileObject) -> (struct FileObject_logging *) */
1420 struct FileObject_logging {
1421         PVOID LogHandle;
1422         PFLUSH_TO_LSN FlushToLsnRoutine;
1423         };
1424
1425 static GHashTable *FileObject_logging_hash;
1426
1427 static void FileObject_logging_hash_init(void)
1428 {
1429         if (FileObject_logging_hash)
1430                 return;
1431         FileObject_logging_hash=g_hash_table_new(
1432                         g_direct_hash,  /* hash_func */
1433                         g_direct_equal);        /* key_equal_func */
1434 }
1435
1436 static void logging_notify_privbcb_flush(struct private_bcb *privbcb)
1437 {
1438 struct FileObject_logging *FileObject_logging;
1439
1440         g_return_if_fail(privbcb!=NULL);
1441
1442         if (!privbcb->lsn_valid)        /* nothing to report anyway */
1443                 return;
1444
1445         FileObject_logging_hash_init();
1446         
1447         if (!(FileObject_logging=g_hash_table_lookup(FileObject_logging_hash,privbcb->FileObject)))
1448                 return;
1449         (*FileObject_logging->FlushToLsnRoutine)(FileObject_logging->LogHandle,privbcb->lsn);
1450 }
1451
1452 VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
1453 {
1454 GList *LogHandle_list;
1455 struct FileObject_logging *FileObject_logging;
1456
1457         g_return_if_fail(FileObject!=NULL);
1458         /* 'LogHandle' may be NULL */
1459         g_return_if_fail(FlushToLsnRoutine!=NULL);
1460
1461         log_handle_hash_init();
1462         FileObject_logging_hash_init();
1463
1464         if (!(FileObject_logging=g_hash_table_lookup(
1465                         FileObject_logging_hash,FileObject))) {
1466                 captive_new(FileObject_logging);
1467                 g_hash_table_insert(FileObject_logging_hash,
1468                                 FileObject,FileObject_logging);
1469                 }
1470         else {
1471                 LogHandle_list=g_hash_table_lookup(log_handle_hash,FileObject_logging->LogHandle);
1472                 g_assert(NULL!=g_list_find(LogHandle_list,FileObject));
1473                 LogHandle_list=g_list_remove(LogHandle_list,FileObject);
1474                 g_assert(NULL==g_list_find(LogHandle_list,FileObject));
1475                 g_hash_table_insert(log_handle_hash,FileObject_logging->LogHandle,LogHandle_list);
1476                 }
1477
1478         FileObject_logging->LogHandle=LogHandle;
1479         FileObject_logging->FlushToLsnRoutine=FlushToLsnRoutine;
1480
1481         LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
1482         LogHandle_list=g_list_prepend(LogHandle_list,FileObject);
1483         g_hash_table_insert(log_handle_hash,LogHandle,LogHandle_list);
1484 }
1485
1486
1487 struct CcGetDirtyPages_param {
1488         PDIRTY_PAGE_ROUTINE DirtyPageRoutine;   /* arg of CcGetDirtyPages() */
1489         IN PVOID Context1;      /* arg of CcGetDirtyPages() */
1490         IN PVOID Context2;      /* arg of CcGetDirtyPages() */
1491         FILE_OBJECT *FileObject;        /* search through 'page_position_hash' for 'FileObject' */
1492         LARGE_INTEGER OldestLsn; gboolean OldestLsn_found;      /* intermediate return value of CcGetDirtyPages() */
1493         };
1494
1495 static void CcGetDirtyPages_page_position_hash_foreach(
1496                 struct page_position *pagepos,  /* key */
1497                 struct page_position *pagepos2, /* value */
1498                 struct CcGetDirtyPages_param *CcGetDirtyPages_param)    /* user_data */
1499 {
1500 LARGE_INTEGER FileOffset_local;
1501 LARGE_INTEGER OldestLsn,NewestLsn;
1502 LARGE_INTEGER OldestLsn_check,NewestLsn_check;
1503 gboolean lsn_found=FALSE;
1504 GList *privbcb_list;
1505
1506         g_return_if_fail(pagepos!=NULL);
1507         g_return_if_fail(pagepos==pagepos2);
1508         g_return_if_fail(CcGetDirtyPages_param!=NULL);
1509
1510         FileOffset_local=pagepos->FileOffset;
1511
1512         for (
1513                         privbcb_list=pagepos->privbcb_list;
1514                         privbcb_list;
1515                         privbcb_list=privbcb_list->next) {
1516 struct private_bcb *privbcb=privbcb_list->data;
1517
1518                 if (!privbcb->lsn_valid)
1519                         return;
1520                 if (!lsn_found) {
1521                         OldestLsn=NewestLsn=privbcb->lsn;
1522                         continue;
1523                         }
1524                 OldestLsn.QuadPart=MIN(OldestLsn.QuadPart,privbcb->lsn.QuadPart);
1525                 NewestLsn.QuadPart=MAX(NewestLsn.QuadPart,privbcb->lsn.QuadPart);
1526                 }
1527
1528         if (!lsn_found)
1529                 return;
1530
1531         if (!CcGetDirtyPages_param->OldestLsn_found) {
1532                 CcGetDirtyPages_param->OldestLsn=OldestLsn;
1533                 CcGetDirtyPages_param->OldestLsn_found=TRUE;
1534                 }
1535         else
1536                 CcGetDirtyPages_param->OldestLsn.QuadPart=MIN(CcGetDirtyPages_param->OldestLsn.QuadPart,OldestLsn.QuadPart);
1537
1538         OldestLsn_check=OldestLsn;
1539         NewestLsn_check=NewestLsn;
1540
1541         (*CcGetDirtyPages_param->DirtyPageRoutine)(
1542                         pagepos->FileObject,    /* FileObject */
1543                         &FileOffset_local,      /* FileOffset */
1544                         PAGE_SIZE,      /* Length */
1545                         &OldestLsn,     /* OldestLsn */
1546                         &NewestLsn,     /* NewestLsn */
1547                         CcGetDirtyPages_param->Context1,        /* Context1 */
1548                         CcGetDirtyPages_param->Context2);       /* Context2 */
1549
1550         /* just unconfirmed sanity: */
1551         g_assert(FileOffset_local.QuadPart==pagepos->FileOffset.QuadPart);      /* check for possible corruption */
1552         g_assert(OldestLsn_check.QuadPart==OldestLsn.QuadPart);
1553         g_assert(NewestLsn_check.QuadPart==NewestLsn.QuadPart);
1554 }
1555
1556 /* libcaptive must return #gint64 instead of the official #LARGE_INTEGER
1557  * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
1558  */
1559 gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
1560                 IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
1561 {
1562 GList *LogHandle_list;
1563 struct CcGetDirtyPages_param CcGetDirtyPages_param;
1564
1565         /* 'LogHandle' may be NULL */
1566         g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
1567
1568         log_handle_hash_init();
1569         page_position_hash_init();
1570
1571         CcGetDirtyPages_param.DirtyPageRoutine=DirtyPageRoutine;
1572         CcGetDirtyPages_param.Context1=Context1;
1573         CcGetDirtyPages_param.Context2=Context2;
1574         CcGetDirtyPages_param.OldestLsn_found=FALSE;
1575
1576         for (
1577                         LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
1578                         LogHandle_list;
1579                         LogHandle_list=LogHandle_list->next) {
1580                 CcGetDirtyPages_param.FileObject=LogHandle_list->data;
1581                 g_hash_table_foreach(
1582                                 page_position_hash,     /* hash_table */
1583                                 (GHFunc)CcGetDirtyPages_page_position_hash_foreach,     /* func */
1584                                 &CcGetDirtyPages_param);        /* user_data */
1585                 }
1586
1587         if (!CcGetDirtyPages_param.OldestLsn_found)
1588                 return 0;
1589         return CcGetDirtyPages_param.OldestLsn.QuadPart;
1590 }
1591
1592
1593 /**
1594  * CcSetAdditionalCacheAttributes:
1595  * @FileObject: Initialized open #FileObject to map.
1596  * %NULL value is forbidden.
1597  * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
1598  * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
1599  *
1600  * libcaptive does not implement any caching and therefore this function
1601  * is a NOP there.
1602  */
1603 VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
1604 {
1605         g_return_if_fail(FileObject!=NULL);
1606
1607         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
1608                         FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
1609
1610         /* NOP; no caching by libcaptive */
1611 }
1612
1613
1614 /**
1615  * CcSetBcbOwnerPointer:
1616  * @Bcb: Initialized #PUBLIC_BCB structure.
1617  * %NULL value is forbidden.
1618  * @Owner: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
1619  * %NULL value is forbidden (FIXME: is it W32 compliant?).
1620  *
1621  * Set thread-specific pointer for a pinned @Bcb. Use CcUnpinDataForThread()
1622  * when @Bcb is no longer needed. CcUnpinDataForThread() is NOT a reverse
1623  * operation for this single call CcSetBcbOwnerPointer(), see CcUnpinDataForThread().
1624  *
1625  * libcaptive implements this function as no-operation as it does not yet
1626  * support any threading.
1627  */
1628 VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
1629 {
1630         g_return_if_fail(Bcb!=NULL);
1631         g_return_if_fail(Owner!=NULL);
1632
1633         /* FIXME:thread; NOP if no threads present */
1634 }
1635
1636
1637 /**
1638  * CcUnpinDataForThread:
1639  * @Bcb: Initialized #PUBLIC_BCB structure.
1640  * %NULL value is forbidden.
1641  * @ResourceThreadId: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
1642  * This pointer had to be passed to CcSetBcbOwnerPointer() #Owner parameter previously.
1643  * %NULL value is forbidden (FIXME: is it W32 compliant?).
1644  *
1645  * CcUnpinData() for a thread specified by @ResourceThreadId.
1646  * Reverse operation for a pair of CcMapData() and CcSetBcbOwnerPointer().
1647  *
1648  * libcaptive implements this function as a simple pass to CcUnpinData() as it does not yet
1649  * support any threading.
1650  */
1651 VOID CcUnpinDataForThread(IN PVOID Bcb,IN ERESOURCE_THREAD ResourceThreadId)
1652 {
1653         g_return_if_fail(Bcb!=NULL);
1654         g_return_if_fail(ResourceThreadId!=0);
1655
1656         /* FIXME:thread */
1657
1658         CcUnpinData(Bcb);
1659 }
1660
1661
1662 /**
1663  * CcRemapBcb:
1664  * @Bcb: Initialized #PUBLIC_BCB structure.
1665  * %NULL value is forbidden.
1666  *
1667  * Create a copy of @Bcb for the exactly same file contents as is @Bcb.
1668  * The returned copy has the same attributes as the result of CcMapData()
1669  * notwithstanding the current state of input @Bcb, therefore it is only
1670  * for read/only access etc.
1671  *
1672  * libcaptive calls CcMapData() internally with @Bcb parameters.
1673  *
1674  * Returns: Copy of @Bcb. This _pointer_ never equals to @Bcb.
1675  * It should be some different
1676  * #PUBLIC_BCB structure according to W32 doc.
1677  */
1678 PVOID CcRemapBcb(IN PVOID Bcb)
1679 {
1680 PVOID r;
1681 PVOID Buffer_unused;
1682 BOOLEAN errbool;
1683 PUBLIC_BCB *PublicBcb;
1684 struct private_bcb *privbcb;
1685
1686         g_return_val_if_fail(validate_Bcb(Bcb),NULL);
1687
1688         private_bcb_hash_init();
1689
1690         PublicBcb=(PUBLIC_BCB *)Bcb;
1691         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1692         g_return_val_if_fail(privbcb!=NULL,NULL);
1693
1694         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1695                         "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d",G_STRLOC,
1696                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count);
1697
1698         errbool=CcMapData(
1699                         privbcb->FileObject,    /* FileObject */
1700                         &privbcb->MappedFileOffset,     /* FileOffset */
1701                         privbcb->MappedLength,  /* Length */
1702                         MAP_WAIT,       /* Flags; && !MAP_NO_READ */
1703                         &r,     /* Bcb */
1704                         &Buffer_unused); /* Buffer */
1705         g_return_val_if_fail(errbool==TRUE,NULL);
1706         g_return_val_if_fail(r!=NULL,NULL);
1707         g_return_val_if_fail(Buffer_unused!=NULL,NULL);
1708
1709         g_assert(r!=Bcb);
1710         return r;
1711 }
1712
1713
1714 /**
1715  * CcPreparePinWrite:
1716  * @FileObject: Initialized open #FileObject to map.
1717  * %NULL value is forbidden.
1718  * @FileOffset: The @FileObject file offset from where to map the region from.
1719  * Negative value is forbidden.
1720  * @Length: Requested length of the region to map from @FileObject.
1721  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1722  * @Zero: %TRUE if the area of @FileOffset...@FileOffset+@Length should be cleared.
1723  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1724  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1725  * %PIN_NO_READ currently ignored; we map the data always.
1726  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1727  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1728  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1729  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1730  * %NULL pointer is forbidden.
1731  * @Buffer: Returns the mapped memory region start address.
1732  * This address may not be %PAGE_SIZE aligned.
1733  * %NULL pointer is forbidden.
1734  *
1735  * Wrapper for a pair of CcPinRead() and CcSetDirtyPinnedData().
1736  * The mapped range can be also optionally cleared if @Zero is specified.
1737  * See CcPinRead() for a more detailed documentation.
1738  *
1739  * FIXME: libcaptive will read even the file pages completely not need
1740  * in the case @Zero was specified.
1741  *
1742  * Returns: %TRUE if the mapping was successful.
1743  */
1744 BOOLEAN CcPreparePinWrite(IN PFILE_OBJECT FileObject,
1745                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Zero,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
1746 {
1747 BOOLEAN errbool;
1748
1749         g_return_val_if_fail(FileObject!=NULL,FALSE);
1750         g_return_val_if_fail(FileOffset!=NULL,FALSE);
1751         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1752         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
1753         /* 'Flags' passed to CcPinRead() */
1754         g_return_val_if_fail(Bcb!=NULL,FALSE);
1755         g_return_val_if_fail(Buffer!=NULL,FALSE);
1756
1757         errbool=CcPinRead(FileObject,FileOffset,Length,Flags,Bcb,Buffer);
1758         g_return_val_if_fail(errbool==TRUE,FALSE);
1759
1760         CcSetDirtyPinnedData(
1761                         *Bcb,   /* Bcb */
1762                         NULL);  /* Lsn; OPTIONAL */
1763
1764         if (Zero) {
1765                 memset(*Buffer,0,Length);
1766                 }
1767
1768         return TRUE;
1769 }