2 * reactos Cache Manager mapper emulation of libcaptive
3 * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
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
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.
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
22 #include "reactos/ddk/ccfuncs.h" /* self */
23 #include <glib/gmessages.h>
24 #include <glib/ghash.h>
25 #include <glib/gmem.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>
34 #include <glib/glist.h>
35 #include "reactos/internal/io.h" /* for IoSynchronousPageWrite() */
36 #include <glib/garray.h>
41 #define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45 /* FIXME: unknown, undocumented */
44 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
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);
56 /* position in file */
57 struct page_position {
58 FILE_OBJECT *FileObject;
59 LARGE_INTEGER FileOffset; /* always PAGE_SIZE aligned */
61 GList *privbcb_list; /* each mapped page has its one private_bcb */
64 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
65 static GHashTable *page_position_hash;
67 static gboolean validate_page_position(const struct page_position *pagepos)
70 struct shmid_ds shmid_ds;
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 */
80 if (pagepos->shmid!=-1) {
81 errint=shmctl(pagepos->shmid,
84 g_return_val_if_fail(errint==0,FALSE);
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);
101 static guint page_position_hash_hash_func(const struct page_position *key)
103 g_return_val_if_fail(validate_page_position(key),0);
105 return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
108 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
110 g_return_val_if_fail(validate_page_position(a),FALSE);
111 g_return_val_if_fail(validate_page_position(b),FALSE);
113 return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
116 static void page_position_hash_key_destroy_func(struct page_position *key)
118 g_return_if_fail(validate_page_position(key));
119 g_assert(key->privbcb_list==NULL);
120 g_assert(key->shmid==-1);
125 static void page_position_hash_init(void)
127 if (page_position_hash)
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 */
138 PUBLIC_BCB *PublicBcb; /* ->MappedLength, ->MappedFileOffset */
139 FILE_OBJECT *FileObject;
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. */
149 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
150 static GHashTable *private_bcb_hash;
152 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
154 g_return_if_fail(validate_Bcb(key));
159 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
161 struct page_position pagepos_local;
165 gpointer base_aligned;
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)'
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);
179 page_position_hash_init();
181 base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
183 pagepos_local.FileObject=value->FileObject;
184 pagepos_local.privbcb_list=NULL;
185 pagepos_local.shmid=-1;
188 offset<value->MappedLength;
190 struct page_position *pagepos;
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);
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);
203 if (!pagepos->privbcb_list) {
204 /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
206 errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
207 g_assert(errbool==TRUE);
210 g_assert(validate_page_position(pagepos));
216 static void private_bcb_hash_init(void)
218 if (private_bcb_hash)
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 */
228 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
232 IO_STATUS_BLOCK IoStatus;
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);
240 Mdl=MmCreateMdl(NULL,address,PAGE_SIZE); /* FIXME: Depreacted in favor of IoAllocateMdl() */
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);
251 /* zero the rest of the buffer */
252 memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
254 return IoStatus.Information;
258 static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length)
260 struct page_position pagepos_local,*pagepos;
261 struct private_bcb *privbcb,*privbcb_listitem;
264 page_position_hash_init();
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)))
272 g_assert(validate_page_position(pagepos));
273 g_assert(pagepos->privbcb_list!=NULL);
276 for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
277 privbcb_listitem=privbcb_list->data;
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;
288 /* Sanity check 'privbcb': */
289 g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
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.
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.
316 * WARNING: You are forbidden to access the returned @Buffer; you must enforce
317 * the memory accessibility by either CcPinRead() or CcPinMappedData().
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.
323 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
324 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
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;
333 struct private_bcb *privbcb;
334 gboolean after_eof=FALSE; /* Did we reached the end of file already? */
335 GPtrArray *read_array;
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);
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);
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.
355 FileObject->SectionObjectPointers->SharedCacheMap=NULL;
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.
361 g_return_val_if_fail(!FileObject->DeviceObject->SectorSize
362 || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),FALSE);
364 page_position_hash_init();
365 private_bcb_hash_init();
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);
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?
376 if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length))) {
377 PublicBcb=privbcb->PublicBcb;
378 privbcb->ref_count++;
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.
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);
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;
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,
414 privbcb); /* value */
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.
423 read_array=g_ptr_array_new();
425 pagepos_local.FileObject=FileObject;
426 pagepos_local.shmid=-1;
427 pagepos_local.privbcb_list=NULL;
430 offset<length_mapped_aligned;
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 */
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.
450 /* TODO:thread; munmap()..shmat() window */
451 errint=munmap(pageaddr,PAGE_SIZE);
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);
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) */
463 errint=shmctl(pagepos->shmid,
467 g_hash_table_insert(page_position_hash,
469 pagepos); /* value */
471 g_assert(validate_page_position(pagepos));
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,
479 struct pagepos *pagepos_next;
480 gpointer pageaddr,pageaddr_next;
482 pageaddr=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
484 /* Coalescence of the requests */
485 while (read_array->len) {
486 pagepos_next=g_ptr_array_index(read_array,
488 pageaddr_next=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
489 if (pageaddr_next!=((char *)pageaddr)+read_size)
491 read_size+=PAGE_SIZE;
492 g_assert(pagepos_next==g_ptr_array_remove_index(read_array,
495 /* Read the range content: */
496 got=captive_Cc_IoPageRead(FileObject,pageaddr,read_size,&pagepos_local.FileOffset);
500 g_assert(got<=PAGE_SIZE);
501 after_eof=(got<PAGE_SIZE);
503 g_ptr_array_free(read_array,
504 TRUE); /* free_seg; free the array of gpointer(==struct pagepos *) items */
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;
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.
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.
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 .
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.
552 BOOLEAN CcPinMappedData
553 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
555 struct private_bcb *privbcb;
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);
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);
568 privbcb=captive_privbcb_find(FileObject,FileOffset,Length);
569 if (!privbcb && (Flags & PIN_IF_BCB)) /* BCB does not exist */
571 g_return_val_if_fail(privbcb!=NULL,FALSE); /* appropriate privbcb not found */
573 /* NEVER re-read any memory from FileObject here! */
575 /* Memory already mapped by CcMapData(). */
576 *Bcb=privbcb->PublicBcb;
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.
601 * Just a shortcut call for CcMapData() and CcPinMappedData() afterwards.
602 * See these two functions for the details.
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.
608 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
609 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
611 PVOID Bcb_CcPinMappedData;
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);
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);
626 if (!(Flags&PIN_IF_BCB)) {
627 errbool=CcMapData(FileObject,FileOffset,Length,
629 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
631 g_return_val_if_fail(errbool==TRUE,FALSE);
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);
640 if (errbool==FALSE) /* FALSE permitted; We may fail if Bcb does not exist yet. */
642 *Bcb=Bcb_CcPinMappedData;
649 static void captive_privbcb_flush(struct private_bcb *privbcb)
653 IO_STATUS_BLOCK IoStatus;
655 gpointer base_sectoraligned;
656 gsize length_sectoraligned;
657 LARGE_INTEGER FileOffset_sectoraligned;
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.
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);
679 Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
682 KeInitializeEvent(&Event,NotificationEvent,FALSE);
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.
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);
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);
704 privbcb->dirty=FALSE;
710 * @Bcb: Initialized #PUBLIC_BCB structure.
711 * %NULL value is forbidden.
713 * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
715 VOID CcUnpinData(IN PVOID Bcb)
717 PUBLIC_BCB *PublicBcb;
718 struct private_bcb *privbcb;
721 g_return_if_fail(validate_Bcb(Bcb));
723 private_bcb_hash_init();
725 PublicBcb=(PUBLIC_BCB *)Bcb;
726 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
727 g_return_if_fail(privbcb!=NULL);
729 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
730 /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
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);
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?
740 if (--privbcb->ref_count)
743 captive_privbcb_flush(privbcb);
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;
750 errbool=g_hash_table_remove(private_bcb_hash,PublicBcb);
751 g_assert(errbool==TRUE);
755 VOID CcRepinBcb(IN PVOID Bcb)
757 PUBLIC_BCB *PublicBcb;
758 struct private_bcb *privbcb;
760 g_return_if_fail(validate_Bcb(Bcb));
762 private_bcb_hash_init();
764 PublicBcb=(PUBLIC_BCB *)Bcb;
765 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
766 g_return_if_fail(privbcb!=NULL);
768 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
769 Bcb,privbcb->FileObject);
771 privbcb->ref_count++;
775 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
777 PUBLIC_BCB *PublicBcb;
778 struct private_bcb *privbcb;
780 g_return_if_fail(validate_Bcb(Bcb));
781 g_return_if_fail(IoStatus!=NULL);
783 private_bcb_hash_init();
785 PublicBcb=(PUBLIC_BCB *)Bcb;
786 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
787 g_return_if_fail(privbcb!=NULL);
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);
792 IoStatus->Status=STATUS_SUCCESS;
793 IoStatus->Information=privbcb->MappedLength;
799 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
801 PUBLIC_BCB *PublicBcb;
802 struct private_bcb *privbcb;
804 g_return_if_fail(validate_Bcb(Bcb));
805 g_return_if_fail(Lsn==NULL); /* TODO: NOT IMPLEMENTED YET */
807 private_bcb_hash_init();
809 PublicBcb=(PUBLIC_BCB *)Bcb;
810 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
811 g_return_if_fail(privbcb!=NULL);
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);
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.
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.
831 * FIXME: Currently a NOP with no effect by libcaptive.
833 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
835 g_return_if_fail(FileObject!=NULL);
836 g_return_if_fail(FileSizes!=NULL);
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);
843 /* FIXME: check BCB && 'struct page_position' invalidities */
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: ???).
859 * Drop any caching for shrunken file which is not being deleted.
860 * libcaptive will no longer consider such #BCB as dirty.
862 * Returns: %TRUE if the range was purged successfuly.
864 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
865 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
867 PUBLIC_BCB *PublicBcb;
868 struct private_bcb *privbcb;
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 */
875 PublicBcb=SectionObjectPointer->SharedCacheMap;
876 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
878 private_bcb_hash_init();
880 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
881 g_return_val_if_fail(privbcb!=NULL,FALSE);
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);
888 privbcb->dirty=FALSE; /* purge it */
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.
910 * Reads the specified region of @FileObject to the given @Buffer.
911 * No on-demand loading is in effect.
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.
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)
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);
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);
932 IoStatus->Status=STATUS_UNSUCCESSFUL;
933 IoStatus->Information=0;
937 FileObject, /* FileObject */
938 FileOffset, /* FileOffset */
941 &MappedBcb, /* Bcb */
942 &MappedBuffer); /* Buffer */
943 g_return_val_if_fail(errbool==TRUE,FALSE);
945 memcpy(Buffer,MappedBuffer,Length);
947 CcUnpinData(MappedBcb); /* no error code */
950 IoStatus->Status=STATUS_SUCCESS;
951 IoStatus->Information=Length;
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.
971 * Writes the specified region of the given @Buffer to @FileObject.
973 * Returns: %TRUE if the region was successfuly written with @Length bytes.
975 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
976 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
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);
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);
991 FileObject, /* FileObject */
992 FileOffset, /* FileOffset */
995 &MappedBcb, /* Bcb */
996 &MappedBuffer); /* Buffer */
997 g_return_val_if_fail(errbool==TRUE,FALSE);
999 memcpy(MappedBuffer,Buffer,Length);
1001 CcSetDirtyPinnedData(
1002 MappedBcb, /* Bcb */
1004 CcUnpinData(MappedBcb); /* no error code */
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.
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.
1024 * Returns: libcaptive always returns %TRUE.
1026 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
1028 g_return_val_if_fail(FileObject!=NULL,FALSE);
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);
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.
1044 * libcaptive does not implement any caching and therefore this function
1047 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
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 */
1053 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
1054 FileObject,(gulong)Granularity);
1056 /* NOP; no caching by libcaptive */
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.
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.
1076 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1077 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
1079 PUBLIC_BCB *PublicBcb;
1080 struct private_bcb *privbcb;
1082 g_return_if_fail(SectionObjectPointer!=NULL);
1084 if (SectionObjectPointer->SharedCacheMap==NULL) {
1086 IoStatus->Status=STATUS_SUCCESS;
1087 IoStatus->Information=0;
1092 PublicBcb=SectionObjectPointer->SharedCacheMap;
1093 g_return_if_fail(validate_Bcb(PublicBcb));
1095 private_bcb_hash_init();
1097 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1098 g_return_if_fail(privbcb!=NULL);
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);
1106 g_assert(FileOffset->QuadPart >=privbcb->MappedFileOffset.QuadPart);
1107 g_assert(FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
1110 /* FIXME: Flush just FileOffset..FileOfset+Length part */
1111 captive_privbcb_flush(privbcb);
1114 IoStatus->Status=STATUS_SUCCESS;
1115 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
1120 static gboolean captive_FileObject_ZeroData(PFILE_OBJECT FileObject,guint64 window_bottom,guint64 window_top)
1124 gpointer zerobuffer;
1125 LARGE_INTEGER window_bottom_LargeInteger;
1127 IO_STATUS_BLOCK IoStatus;
1129 g_return_val_if_fail(FileObject!=NULL,FALSE);
1130 g_return_val_if_fail(window_bottom<=window_top,FALSE);
1132 if (window_bottom==window_top) /* trivia */
1135 g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
1137 /* Such requirement is W32 documented for CcZeroData()
1138 * and it is really a requirement at least for the underlying fastfat.sys.
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));
1143 zerobuffer=g_malloc0(window_top-window_bottom);
1144 window_bottom_LargeInteger.QuadPart=window_bottom;
1146 Mdl=MmCreateMdl(NULL,zerobuffer,window_top-window_bottom);
1147 g_assert(Mdl!=NULL);
1149 KeInitializeEvent(&Event,NotificationEvent,FALSE);
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.
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 */
1170 BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
1172 guint64 window_lower_bottom,window_lower_top;
1173 guint64 window_higher_bottom,window_higher_top;
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);
1180 g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
1182 window_lower_bottom=StartOffset->QuadPart;
1183 window_lower_top=EndOffset->QuadPart;
1184 window_higher_bottom=window_higher_top=0;
1186 if (FileObject->SectionObjectPointers->SharedCacheMap!=NULL) {
1187 PUBLIC_BCB *PublicBcb;
1188 struct private_bcb *privbcb;
1189 guint64 window_cached_bottom,window_cached_top;
1191 PublicBcb=FileObject->SectionObjectPointers->SharedCacheMap;
1192 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1194 private_bcb_hash_init();
1196 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1197 g_return_val_if_fail(privbcb!=NULL,FALSE);
1199 window_cached_bottom=MAX(StartOffset->QuadPart,privbcb->MappedFileOffset.QuadPart);
1200 window_cached_top=MIN(EndOffset->QuadPart,privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
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;
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);
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);