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