Support of PIN_IF_BCB is now non-fatal-reporting.
[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
37
38 /* CONFIG: */
39
40 #define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45  /* FIXME: unknown, undocumented */
41
42
43 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
44 {
45         g_return_val_if_fail(PublicBcb!=NULL,FALSE);
46         g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
47         g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
48         g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
49         g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
50
51         return TRUE;
52 }
53
54
55 /* position in file */
56 struct page_position {
57         FILE_OBJECT *FileObject;
58         LARGE_INTEGER FileOffset;       /* always PAGE_SIZE aligned */
59         int shmid;
60         GList *privbcb_list;    /* each mapped page has its one private_bcb */
61         };
62
63 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
64 static GHashTable *page_position_hash;
65
66 static gboolean validate_page_position(const struct page_position *pagepos)
67 {
68 int errint;
69 struct shmid_ds shmid_ds;
70
71         g_return_val_if_fail(pagepos!=NULL,FALSE);
72         g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
73         g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
74         g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
75         /* 'pagepos->shmid' may be -1 */
76         /* 'pagepos->privbcb_list' may be empty */
77         g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL),FALSE);        /* either deleted or alive */
78
79         if (pagepos->shmid!=-1) {
80                 errint=shmctl(pagepos->shmid,
81                                 IPC_STAT,       /* cmd */
82                                 &shmid_ds);     /* buf */
83                 g_return_val_if_fail(errint==0,FALSE);
84
85                 g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
86                 g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
87                 g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
88                 g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
89                 /* 'shm_perm.mode' was seen with sticky bit 01000: */
90                 g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
91                 g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
92                 g_return_val_if_fail(shmid_ds.shm_cpid==getpid(),FALSE);
93                 g_return_val_if_fail(shmid_ds.shm_lpid==getpid(),FALSE);
94                 g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
95                 }
96
97         return TRUE;
98 }
99
100 static guint page_position_hash_hash_func(const struct page_position *key)
101 {
102         g_return_val_if_fail(validate_page_position(key),0);
103
104         return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
105 }
106
107 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
108 {
109         g_return_val_if_fail(validate_page_position(a),FALSE);
110         g_return_val_if_fail(validate_page_position(b),FALSE);
111
112         return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
113 }
114
115 static void page_position_hash_key_destroy_func(struct page_position *key)
116 {
117         g_return_if_fail(validate_page_position(key));
118         g_assert(key->privbcb_list==NULL);
119         g_assert(key->shmid==-1);
120
121         g_free(key);
122 }
123
124 static void page_position_hash_init(void)
125 {
126         if (page_position_hash)
127                 return;
128         page_position_hash=g_hash_table_new_full(
129                         (GHashFunc)page_position_hash_hash_func,        /* hash_func */
130                         (GEqualFunc)page_position_hash_key_equal_func,  /* key_equal_func */
131                         (GDestroyNotify)page_position_hash_key_destroy_func,    /* key_destroy_func */
132                         NULL);  /* value_destroy_func */
133 }
134
135
136 struct private_bcb {
137         PUBLIC_BCB *PublicBcb;  /* ->MappedLength, ->MappedFileOffset */
138         FILE_OBJECT *FileObject;
139         guint ref_count;
140         /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
141         ULONG MappedLength;     /* It is the real requested size; it is not PAGE_SIZE aligned. */
142         /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
143         LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
144         gpointer base;  /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
145         gboolean dirty;
146         };
147
148 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
149 static GHashTable *private_bcb_hash;
150
151 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
152 {
153         g_return_if_fail(validate_Bcb(key));
154
155         g_free(key);
156 }
157
158 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
159 {
160 struct page_position pagepos_local;
161 gboolean errbool;
162 int errint;
163 size_t offset;
164 gpointer base_aligned;
165
166         g_return_if_fail(value!=NULL);
167         /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
168          * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
169          */
170         g_return_if_fail(value->PublicBcb!=NULL);
171         g_return_if_fail(value->FileObject!=NULL);
172         g_return_if_fail(value->ref_count==0);
173         g_return_if_fail(value->MappedLength>0);
174         g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
175         g_return_if_fail(value->base!=NULL);
176         g_return_if_fail(value->dirty==FALSE);
177
178         page_position_hash_init();
179
180         base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
181
182         pagepos_local.FileObject=value->FileObject;
183         pagepos_local.privbcb_list=NULL;
184         pagepos_local.shmid=-1;
185         for (
186                         offset=0;
187                         offset<value->MappedLength;
188                         offset+=PAGE_SIZE) {
189 struct page_position *pagepos;
190
191                 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
192                 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
193                 g_assert(validate_page_position(pagepos));
194                 g_assert(pagepos->privbcb_list!=NULL);
195                 errint=shmdt(((char *)base_aligned)+offset);
196                 g_assert(errint==0);
197
198                 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
199                 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
200                 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
201
202                 if (!pagepos->privbcb_list) {
203                         /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
204                         pagepos->shmid=-1;
205                         errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
206                         g_assert(errbool==TRUE);
207                         }
208                 else
209                         g_assert(validate_page_position(pagepos));
210                 }
211
212         g_free(value);
213 }
214
215 static void private_bcb_hash_init(void)
216 {
217         if (private_bcb_hash)
218                 return;
219         private_bcb_hash=g_hash_table_new_full(
220                         g_direct_hash,  /* hash_func */
221                         g_direct_equal, /* key_equal_func */
222                         (GDestroyNotify)private_bcb_hash_key_destroy_func,      /* key_destroy_func */
223                         (GDestroyNotify)private_bcb_hash_value_destroy_func);   /* value_destroy_func */
224 }
225
226
227 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
228 {
229 MDL *Mdl;
230 KEVENT Event;
231 IO_STATUS_BLOCK IoStatus;
232 NTSTATUS err;
233
234         g_return_val_if_fail(FileObject!=NULL,0);
235         g_return_val_if_fail(address!=0,0);
236         g_return_val_if_fail(length!=0,0);
237         g_return_val_if_fail(FileOffset!=NULL,0);
238
239         Mdl=MmCreateMdl(NULL,address,PAGE_SIZE);        /* FIXME: Depreacted in favor of IoAllocateMdl() */
240         g_assert(Mdl!=NULL);
241         KeInitializeEvent(&Event,NotificationEvent,FALSE);
242         /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
243         err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
244         g_assert(NT_SUCCESS(err));
245         g_assert(NT_SUCCESS(IoStatus.Status));
246         /* It is not == as the file may be shorter than requested */
247         g_assert(IoStatus.Information<=length);
248         IoFreeMdl(Mdl);
249
250         /* zero the rest of the buffer */
251         memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
252
253         return IoStatus.Information;
254 }
255
256
257 /**
258  * CcMapData:
259  * @FileObject: Initialized open #FileObject to map.
260  * %NULL value is forbidden.
261  * @FileOffset: The @FileObject file offset from where to map the region from.
262  * Negative value is forbidden.
263  * @Length: Requested length of the region to map from @FileObject.
264  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
265  * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
266  * %MAP_NO_READ currently ignored; we map the data always.
267  * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
268  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
269  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
270  * %NULL pointer is forbidden.
271  * @Buffer: Returns the mapped memory region start address.
272  * This address may not be %PAGE_SIZE aligned.
273  * %NULL pointer is forbidden.
274  *
275  * Maps the specified region of @FileObject to automatically chosen address space.
276  * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
277  *
278  * WARNING: You are forbidden to access the returned @Buffer; you must enforce
279  * the memory accessibility by either CcPinRead() or CcPinMappedData().
280  *
281  * Returns: %TRUE if the region was successfuly mapped.
282  * @Bcb with the initialized new memory region.
283  * @Buffer with the address of the exact byte specified by @FileOffset.
284  */
285 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
286                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
287 {
288 PUBLIC_BCB **PublicBcbp,*PublicBcb;
289 struct page_position pagepos_local,*pagepos;
290 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
291 gpointer base_aligned;
292 size_t offset,length_mapped_aligned;
293 int errint;
294 gpointer errptr;
295 struct private_bcb *privbcb;
296 gboolean after_eof=FALSE;       /* Did we reached the end of file already? */
297
298         g_return_val_if_fail(FileObject!=NULL,FALSE);
299         g_return_val_if_fail(FileOffset!=NULL,FALSE);
300         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
301         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
302         g_return_val_if_fail(Flags&MAP_WAIT,FALSE);     /* FIXME: on-demand loading not yet implemented */
303         g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE);   /* unknown flags? */
304         g_return_val_if_fail(Bcb!=NULL,FALSE);
305         g_return_val_if_fail(Buffer!=NULL,FALSE);
306
307         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
308                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
309
310         g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
311         g_return_val_if_fail(FileObject->SectionObjectPointers->SharedCacheMap==NULL,FALSE);
312         g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
313         /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
314          * 'SectorSize' may not yet be filled in.
315          */
316         g_return_val_if_fail(!FileObject->DeviceObject->SectorSize
317                         || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),FALSE);
318
319         page_position_hash_init();
320         private_bcb_hash_init();
321
322         PublicBcbp=(PUBLIC_BCB **)Bcb;
323         /* extend 'FileOffset' and 'Length' to page boundaries */
324         FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
325         FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
326         length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
327
328         /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
329         /* TODO: on-demand loading */
330         /* Although we do zeroed-page mapping here we just reserve the linears
331          * space by it - all the page will be overriden by shmat(2) afterwards anyway.
332          */
333         base_aligned=mmap(
334                         NULL,   /* start */
335                         length_mapped_aligned,  /* length */
336                         PROT_READ|PROT_WRITE,   /* prot; FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
337                         MAP_PRIVATE|MAP_ANONYMOUS,      /* flags */
338                         -1,     /* fd; ignored due to MAP_ANONYMOUS */
339                         0);     /* offset; ignored due to MAP_ANONYMOUS */
340         g_assert(base_aligned!=NULL);
341
342         /* Create 'PublicBcb'; referenced by 'privbcb'. */
343         captive_new(PublicBcb);
344         PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
345         PublicBcb->NodeByteSize=sizeof(*PublicBcb);     /* we have no extensions there */
346         PublicBcb->MappedLength=Length;
347         PublicBcb->MappedFileOffset=*FileOffset;
348
349         /* Create 'privbcb'; referenced by created 'pagepos'es. */
350         captive_new(privbcb);
351         privbcb->PublicBcb=PublicBcb;
352         privbcb->FileObject=FileObject;
353         privbcb->ref_count=1;
354         privbcb->MappedLength=PublicBcb->MappedLength;
355         privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
356         privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
357         privbcb->dirty=FALSE;
358         g_hash_table_insert(private_bcb_hash,
359                         PublicBcb,      /* key */
360                         privbcb);       /* value */
361
362         pagepos_local.FileObject=FileObject;
363         pagepos_local.shmid=-1;
364         pagepos_local.privbcb_list=NULL;
365         for (
366                         offset=0;
367                         offset<length_mapped_aligned;
368                         offset+=PAGE_SIZE) {
369 gpointer pageaddr;
370
371                 pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
372                 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
373                         captive_new(pagepos);
374                         /* FIXME: Reference 'FileObject' to not to leave broken references to it here */
375                         *pagepos=pagepos_local;
376                         pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600);
377                         g_assert(pagepos->shmid!=-1);
378                         pagepos->privbcb_list=NULL;
379                         }
380                 pageaddr=(gpointer)(((char *)base_aligned)+offset);
381                 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
382                  * mmap(2) is still needed to get linear block of memory assignment.
383                  */
384                 /* TODO:thread; munmap()..shmat() window */
385                 errint=munmap(pageaddr,PAGE_SIZE);
386                 g_assert(errint==0);
387                 errptr=shmat(pagepos->shmid,
388                                 pageaddr,       /* shmaddr */
389                                 0);     /* shmflg; !SHM_RDONLY==r/w FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
390                 g_assert(errptr==pageaddr);
391
392                 g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
393                 pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb);    /* order not important */
394                 g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
395                 if (pagepos->privbcb_list->next==NULL) {        /* exactly one item (we just added it now) */
396 ULONG got;
397
398                         errint=shmctl(pagepos->shmid,
399                                         IPC_RMID,       /* cmd */
400                                         NULL);  /* buf */
401                         g_assert(errint==0);
402
403                         /* Read the page content: */
404                         got=captive_Cc_IoPageRead(FileObject,pageaddr,PAGE_SIZE,&pagepos_local.FileOffset);
405                         if (after_eof)
406                                 g_assert(got==0);
407                         else
408                                 g_assert(got<=PAGE_SIZE);
409                         after_eof=(got<PAGE_SIZE);
410
411                         g_hash_table_insert(page_position_hash,
412                                         pagepos,        /* key */
413                                         pagepos);       /* value */
414                         }
415                 g_assert(validate_page_position(pagepos));
416                 }
417
418         /* offset _into_ page, may not be PAGE_SIZE aligned: */
419         *Buffer=privbcb->base;
420         *PublicBcbp=PublicBcb;
421         g_assert(validate_Bcb(PublicBcb)==TRUE);
422         FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
423
424         return TRUE;
425 }
426
427
428 /**
429  * CcPinMappedData:
430  * @FileObject: Initialized open #FileObject to map.
431  * %NULL value is forbidden.
432  * @MappedFileOffset: The @FileObject file offset from where to map the region from.
433  * Negative value is forbidden.
434  * @MappedLength: Requested length of the region to map from @FileObject.
435  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
436  * @Wait: Whether disk waiting is permitted for this function.
437  * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
438  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
439  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
440  * %PIN_NO_READ currently ignored; we map the data always.
441  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
442  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
443  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
444  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
445  * %NULL pointer is forbidden.
446  * @Buffer: Returns the mapped memory region start address.
447  * This address may not be %PAGE_SIZE aligned.
448  * %NULL pointer is forbidden.
449  *
450  * This function should map the memory from CcMapData() as CcMapData() does not provide
451  * you any access to the reserved memory space.
452  * libcaptive will map all the data by CcMapData() already and this function does not
453  * have any effect there.
454  *
455  * Returns: %TRUE if the region was successfuly mapped.
456  * @Bcb with the initialized new memory region.
457  * @Buffer with the address of the exact byte specified by @FileOffset.
458  */
459 BOOLEAN CcPinMappedData
460                 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
461 {
462 struct page_position pagepos_local,*pagepos;
463 struct private_bcb *privbcb,*privbcb_listitem;
464 GList *privbcb_list;
465 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
466 size_t length_mapped_aligned;
467 gpointer base_aligned;
468 ULONG got;
469
470         g_return_val_if_fail(FileObject!=NULL,FALSE);
471         g_return_val_if_fail(FileOffset!=NULL,FALSE);
472         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
473         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
474         /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
475         g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
476         g_return_val_if_fail(Bcb!=NULL,FALSE);
477
478         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
479                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
480
481         /* extend 'FileOffset' and 'Length' to page boundaries */
482         FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
483         FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
484         length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
485
486         page_position_hash_init();
487
488         pagepos_local.FileObject=FileObject;
489         pagepos_local.FileOffset=FileOffset_bottom;
490         pagepos_local.privbcb_list=NULL;
491         pagepos_local.shmid=-1;
492         pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
493         if (!pagepos && (Flags & PIN_IF_BCB))   /* BCB does not exist */
494                 return FALSE;
495         g_assert(validate_page_position(pagepos));
496         g_assert(pagepos->privbcb_list!=NULL);
497
498         privbcb=NULL;
499         for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
500                 privbcb_listitem=privbcb_list->data;
501                 if (1
502                                 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
503                                 && privbcb_listitem->MappedLength==Length) {
504                         g_assert(privbcb==NULL);        /* appropriate privbcb found twice */
505                         privbcb=privbcb_listitem;
506                         }
507                 }
508         if (!privbcb && (Flags & PIN_IF_BCB))   /* BCB does not exist */
509                 return FALSE;
510         g_return_val_if_fail(privbcb!=NULL,FALSE);      /* appropriate privbcb not found */
511
512         /* Sanity check 'privbcb': */
513         g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
514
515         base_aligned=CAPTIVE_ROUND_DOWN(privbcb->base,PAGE_SIZE);
516
517         got=captive_Cc_IoPageRead(
518                         privbcb->FileObject,    /* FILE_OBJECT */
519                         base_aligned,   /* address */
520                         PAGE_SIZE,      /* length */
521                         &pagepos_local.FileOffset);     /* FileOffset */
522         /* FIXME: sanitycheck 'got' agains CcMapData() state */
523
524         /* Memory already mapped by CcMapData(). */
525         *Bcb=privbcb->PublicBcb;
526         return TRUE;
527 }
528
529
530 /**
531  * CcPinRead:
532  * @FileObject: Initialized open #FileObject to map.
533  * %NULL value is forbidden.
534  * @FileOffset: The @FileObject file offset from where to map the region from.
535  * Negative value is forbidden.
536  * @Length: Requested length of the region to map from @FileObject.
537  * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
538  * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
539  * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
540  * %PIN_NO_READ currently ignored; we map the data always.
541  * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
542  * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
543  * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
544  * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
545  * %NULL pointer is forbidden.
546  * @Buffer: Returns the mapped memory region start address.
547  * This address may not be %PAGE_SIZE aligned.
548  * %NULL pointer is forbidden.
549  *
550  * Just a shortcut call for CcMapData() and CcPinMappedData() afterwards.
551  * See these two functions for the details.
552  *
553  * Returns: %TRUE if the region was successfuly mapped.
554  * @Bcb with the initialized new memory region.
555  * @Buffer with the address of the exact byte specified by @FileOffset.
556  */
557 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
558                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
559 {
560 PVOID Bcb_CcPinMappedData;
561 gboolean errbool;
562
563         g_return_val_if_fail(FileObject!=NULL,FALSE);
564         g_return_val_if_fail(FileOffset!=NULL,FALSE);
565         g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
566         g_return_val_if_fail(Length>0,FALSE);   /* FIXME: not handled below; 0 should be allowed */
567         /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
568         g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE);  /* unknown flags? */
569         g_return_val_if_fail(Bcb!=NULL,FALSE);
570         g_return_val_if_fail(Buffer!=NULL,FALSE);
571
572         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
573                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
574
575         if (!(Flags&PIN_IF_BCB)) {
576                 errbool=CcMapData(FileObject,FileOffset,Length,
577                                 0               /* Flags */
578                                                 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
579                                 Bcb,Buffer);
580                 g_return_val_if_fail(errbool==TRUE,FALSE);
581                 }
582
583         errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
584         if (!(Flags&PIN_IF_BCB)) {
585                 g_return_val_if_fail(errbool==TRUE,FALSE);
586                 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
587                 }
588         else {
589                 if (errbool==FALSE)     /* FALSE permitted; We may fail if Bcb does not exist yet. */
590                         return FALSE;
591                 *Bcb=Bcb_CcPinMappedData;
592                 }
593
594         return TRUE;
595 }
596
597
598 static void captive_privbcb_flush(struct private_bcb *privbcb)
599 {
600 MDL *Mdl;
601 KEVENT Event;
602 IO_STATUS_BLOCK IoStatus;
603 NTSTATUS err;
604 gpointer base_sectoraligned;
605 gsize length_sectoraligned;
606 LARGE_INTEGER FileOffset_sectoraligned;
607
608         if (!privbcb->dirty)
609                 return;
610
611         g_assert(privbcb->FileObject->DeviceObject!=NULL);
612         /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
613         g_assert(privbcb->FileObject->DeviceObject->SectorSize>0);
614         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,privbcb->FileObject->DeviceObject->SectorSize));
615         /* We align here directly the 'privbcb->base' which is not correct.
616          * We should rather aligned according to 'privbcb->MappedOffset' but
617          * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
618          * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
619          */
620         g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,privbcb->FileObject->DeviceObject->SectorSize)
621                                  ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,privbcb->FileObject->DeviceObject->SectorSize));
622         base_sectoraligned  =CAPTIVE_ROUND_DOWN(privbcb->base,privbcb->FileObject->DeviceObject->SectorSize);
623         length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,privbcb->FileObject->DeviceObject->SectorSize)
624                         -((char *)base_sectoraligned);
625         g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,privbcb->FileObject->DeviceObject->SectorSize));
626         FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,privbcb->FileObject->DeviceObject->SectorSize);
627
628         Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
629         g_assert(Mdl!=NULL);
630
631         KeInitializeEvent(&Event,NotificationEvent,FALSE);
632
633         /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
634         /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
635         err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
636         g_assert(NT_SUCCESS(err));
637         g_assert(NT_SUCCESS(IoStatus.Status));
638         /* We should write at least the unaligned mapped data although we
639          * do not need to successfuly write the whole aligned amount.
640          */
641         g_assert(IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,privbcb->FileObject->DeviceObject->SectorSize)
642                         +privbcb->MappedLength);
643         g_assert(IoStatus.Information<=length_sectoraligned);
644
645         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
646                         "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
647                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
648                         base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
649                         (gulong)IoStatus.Information);
650
651         privbcb->dirty=FALSE;
652 }
653
654
655 /**
656  * CcUnpinData:
657  * @Bcb: Initialized #PUBLIC_BCB structure.
658  * %NULL value is forbidden.
659  *
660  * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
661  */
662 VOID CcUnpinData(IN PVOID Bcb)
663 {
664 PUBLIC_BCB *PublicBcb;
665 struct private_bcb *privbcb;
666 gboolean errbool;
667
668         g_return_if_fail(validate_Bcb(Bcb));
669
670         private_bcb_hash_init();
671
672         PublicBcb=(PUBLIC_BCB *)Bcb;
673         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
674         g_return_if_fail(privbcb!=NULL);
675
676         g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
677         g_assert(privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb);
678
679         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
680                         "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX",G_STRLOC,
681                         privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
682
683         g_assert(privbcb->ref_count>0);
684         /* Do not write back the contents if this is not the final unpin.
685          * FIXME: Is it correct?
686          */
687         if (--privbcb->ref_count)
688                 return;
689
690         captive_privbcb_flush(privbcb);
691
692         g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
693         g_assert(privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb);
694         privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
695
696         errbool=g_hash_table_remove(private_bcb_hash,PublicBcb);
697         g_assert(errbool==TRUE);
698 }
699
700
701 VOID CcRepinBcb(IN PVOID Bcb)
702 {
703 PUBLIC_BCB *PublicBcb;
704 struct private_bcb *privbcb;
705
706         g_return_if_fail(validate_Bcb(Bcb));
707
708         private_bcb_hash_init();
709
710         PublicBcb=(PUBLIC_BCB *)Bcb;
711         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
712         g_return_if_fail(privbcb!=NULL);
713
714         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
715                         Bcb,privbcb->FileObject);
716
717         privbcb->ref_count++;
718 }
719
720
721 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
722 {
723 PUBLIC_BCB *PublicBcb;
724 struct private_bcb *privbcb;
725
726         g_return_if_fail(validate_Bcb(Bcb));
727         g_return_if_fail(IoStatus!=NULL);
728
729         private_bcb_hash_init();
730
731         PublicBcb=(PUBLIC_BCB *)Bcb;
732         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
733         g_return_if_fail(privbcb!=NULL);
734
735         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
736                         Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
737
738         IoStatus->Status=STATUS_SUCCESS;
739         IoStatus->Information=privbcb->MappedLength;
740
741         CcUnpinData(Bcb);
742 }
743
744
745 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
746 {
747 PUBLIC_BCB *PublicBcb;
748 struct private_bcb *privbcb;
749
750         g_return_if_fail(validate_Bcb(Bcb));
751         g_return_if_fail(Lsn==NULL);    /* TODO: NOT IMPLEMENTED YET */
752
753         private_bcb_hash_init();
754
755         PublicBcb=(PUBLIC_BCB *)Bcb;
756         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
757         g_return_if_fail(privbcb!=NULL);
758
759         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
760                         Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
761
762         privbcb->dirty=TRUE;
763 }
764
765
766 /**
767  * CcSetFileSizes:
768  * @FileObject: Initialized open #FileObject to update file sizes of.
769  * %NULL value is forbidden.
770  * @FileSizes: New file sizes to update cache to.
771  * %NULL value is forbidden.
772  * 
773  * Update cache properties after file sizes were updated.
774  * Probably only the exceeding pages need to be unmapped and BCBs updated
775  * if FileSizes->AllocationSize gets shrunk.
776  *
777  * FIXME: Currently a NOP with no effect by libcaptive.
778  */
779 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
780 {
781         g_return_if_fail(FileObject!=NULL);
782         g_return_if_fail(FileSizes!=NULL);
783
784         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
785                         "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
786                         FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
787                         (guint64)FileSizes->ValidDataLength.QuadPart);
788
789         /* FIXME: check BCB && 'struct page_position' invalidities */
790 }
791
792
793 /**
794  * CcPurgeCacheSection:
795  * @SectionObjectPointer: Pointer specifying file to purge;
796  * %NULL value is forbidden.
797  * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
798  * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %FALSE return code in such case.
799  * @FileOffset: Starting offset of the ranger to purge.
800  * %NULL pointer is permitted and it means to purge the whole whole.
801  * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
802  * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
803  * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
804  *
805  * Drop any caching for shrunken file which is not being deleted.
806  * libcaptive will no longer consider such #BCB as dirty.
807  *
808  * Returns: %TRUE if the range was purged successfuly.
809  */
810 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
811                 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
812 {
813 PUBLIC_BCB *PublicBcb;
814 struct private_bcb *privbcb;
815
816         g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
817         if (SectionObjectPointer->SharedCacheMap==NULL)
818                 return FALSE;   /* failed - nothing to purge */
819         g_return_val_if_fail(FileOffset==NULL,FALSE);   /* NOT IMPLEMENTED YET */
820
821         PublicBcb=SectionObjectPointer->SharedCacheMap;
822         g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
823
824         private_bcb_hash_init();
825
826         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
827         g_return_val_if_fail(privbcb!=NULL,FALSE);
828
829         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
830                         "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
831                         SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
832                         (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
833
834         privbcb->dirty=FALSE;   /* purge it */
835
836         return TRUE;
837 }
838
839
840 /**
841  * CcCopyRead:
842  * @FileObject: Initialized open #FileObject to map.
843  * %NULL value is forbidden.
844  * @FileOffset: The @FileObject file offset from where to map the region from.
845  * Negative value is forbidden.
846  * @Length: Requested length of the region to map from @FileObject.
847  * Value %0 is permitted (no effect of this function call).
848  * @Wait: Whether disk waiting is permitted for this function.
849  * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
850  * @Buffer: Address of memory region with already allocated memory of size @Length.
851  * This address may not be %PAGE_SIZE aligned.
852  * %NULL pointer is forbidden.
853  * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
854  * %NULL pointer is forbidden.
855  *
856  * Reads the specified region of @FileObject to the given @Buffer.
857  * No on-demand loading is in effect.
858  *
859  * Returns: %TRUE if the region was successfuly filled with @Length bytes.
860  * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
861  * @IoStatus.Information initialized by @Length if successful.
862  */
863 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
864                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
865 {
866 PVOID MappedBcb;
867 PVOID MappedBuffer;
868 gboolean errbool;
869
870         g_return_val_if_fail(FileObject!=NULL,FALSE);
871         g_return_val_if_fail(FileOffset!=NULL,FALSE);
872         g_return_val_if_fail(Buffer!=NULL,FALSE);
873         g_return_val_if_fail(IoStatus!=NULL,FALSE);
874
875         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
876                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
877
878         IoStatus->Status=STATUS_UNSUCCESSFUL;
879         IoStatus->Information=0;
880
881         if (Length) {
882                 errbool=CcPinRead(
883                                 FileObject,     /* FileObject */
884                                 FileOffset,     /* FileOffset */
885                                 Length, /* Length */
886                                 Wait,   /* Wait */
887                                 &MappedBcb,     /* Bcb */
888                                 &MappedBuffer); /* Buffer */
889                 g_return_val_if_fail(errbool==TRUE,FALSE);
890
891                 memcpy(Buffer,MappedBuffer,Length);
892
893                 CcUnpinData(MappedBcb); /* no error code */
894                 }
895
896         IoStatus->Status=STATUS_SUCCESS;
897         IoStatus->Information=Length;
898
899         return TRUE;
900 }
901
902
903 /**
904  * CcCopyWrite:
905  * @FileObject: Initialized open #FileObject to map.
906  * %NULL value is forbidden.
907  * @FileOffset: The @FileObject file offset from where to map the region from.
908  * Negative value is forbidden.
909  * @Length: Requested length of the region to map from @FileObject.
910  * Value %0 is permitted (no effect of this function call).
911  * @Wait: Whether disk waiting is permitted for this function.
912  * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
913  * @Buffer: Address of memory region with already allocated memory of size @Length.
914  * This address may not be %PAGE_SIZE aligned.
915  * %NULL pointer is forbidden.
916  *
917  * Writes the specified region of the given @Buffer to @FileObject.
918  *
919  * Returns: %TRUE if the region was successfuly written with @Length bytes.
920  */
921 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
922                 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
923 {
924 PVOID MappedBcb;
925 PVOID MappedBuffer;
926 gboolean errbool;
927
928         g_return_val_if_fail(FileObject!=NULL,FALSE);
929         g_return_val_if_fail(FileOffset!=NULL,FALSE);
930         g_return_val_if_fail(Buffer!=NULL,FALSE);
931
932         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
933                         FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
934
935         if (Length) {
936                 errbool=CcPinRead(
937                                 FileObject,     /* FileObject */
938                                 FileOffset,     /* FileOffset */
939                                 Length, /* Length */
940                                 Wait,   /* Wait */
941                                 &MappedBcb,     /* Bcb */
942                                 &MappedBuffer); /* Buffer */
943                 g_return_val_if_fail(errbool==TRUE,FALSE);
944
945                 memcpy(MappedBuffer,Buffer,Length);
946
947                 CcSetDirtyPinnedData(
948                                 MappedBcb,      /* Bcb */
949                                 NULL);  /* Lsn */
950                 CcUnpinData(MappedBcb); /* no error code */
951                 }
952
953         return TRUE;
954 }
955
956
957 /**
958  * CcCanIWrite:
959  * @FileObject: Initialized open #FileObject to map.
960  * %NULL value is forbidden.
961  * @BytesToWrite: Amount of data to be asked whether it will be accepted.
962  * Value %0 is permitted.
963  * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
964  * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
965  *
966  * Asks cache manager if it would currently accept write request to @FileObject
967  * of @BytesToWrite bytes with @Wait condition.
968  * libcaptive will always accept any writes. This function is a NOP.
969  *
970  * Returns: libcaptive always returns %TRUE.
971  */
972 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
973 {
974         g_return_val_if_fail(FileObject!=NULL,FALSE);
975
976         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
977                         FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
978
979         return TRUE;
980 }
981
982
983 /**
984  * CcSetReadAheadGranularity:
985  * @FileObject: Initialized open #FileObject to map.
986  * %NULL value is forbidden.
987  * @Granularity: Suggested size of the cache element.
988  * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
989  *
990  * libcaptive does not implement any caching and therefore this function
991  * is a NOP there.
992  */
993 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
994 {
995         g_return_if_fail(FileObject!=NULL);
996         g_return_if_fail(Granularity>=PAGE_SIZE);
997         g_return_if_fail((Granularity&(Granularity-1))==0);     /* Power of two */
998
999         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
1000                         FileObject,(gulong)Granularity);
1001
1002         /* NOP; no caching by libcaptive */
1003 }
1004
1005
1006 /**
1007  * CcFlushCache:
1008  * @SectionObjectPointer: Pointer specifying file to flush;
1009  * %NULL value is forbidden.
1010  * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1011  * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
1012  * @FileOffset: Optional starting point of the range to flush.
1013  * %NULL value is permitted.
1014  * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
1015  * @IoStatus: Optionally returns the resulting operation status.
1016  * #Information field will contain the number of bytes flushed.
1017  * %NULL value is permitted.
1018  *
1019  * Flushes out any pending dirty data in cache manager BCB mapping.
1020  * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
1021  */
1022 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1023                 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
1024 {
1025 PUBLIC_BCB *PublicBcb;
1026 struct private_bcb *privbcb;
1027
1028         g_return_if_fail(SectionObjectPointer!=NULL);
1029
1030         if (SectionObjectPointer->SharedCacheMap==NULL) {
1031                 if (IoStatus) {
1032                         IoStatus->Status=STATUS_SUCCESS;
1033                         IoStatus->Information=0;
1034                         }
1035                 return;
1036                 }
1037
1038         PublicBcb=SectionObjectPointer->SharedCacheMap;
1039         g_return_if_fail(validate_Bcb(PublicBcb));
1040
1041         private_bcb_hash_init();
1042
1043         privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1044         g_return_if_fail(privbcb!=NULL);
1045
1046         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1047                         "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
1048                         SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1049                         (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
1050
1051         if (FileOffset) {
1052                 g_assert(FileOffset->QuadPart       >=privbcb->MappedFileOffset.QuadPart);
1053                 g_assert(FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
1054                 }
1055
1056         /* FIXME: Flush just FileOffset..FileOfset+Length part */
1057         captive_privbcb_flush(privbcb);
1058
1059         if (IoStatus) {
1060                 IoStatus->Status=STATUS_SUCCESS;
1061                 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
1062                 }
1063 }