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>
37 #include <glib/gmain.h>
42 #define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45 /* FIXME: unknown, undocumented */
45 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
47 g_return_val_if_fail(PublicBcb!=NULL,FALSE);
48 g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
49 g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
50 g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
51 g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
57 /* position in file */
58 struct page_position {
59 FILE_OBJECT *FileObject;
60 LARGE_INTEGER FileOffset; /* always PAGE_SIZE aligned */
62 GList *privbcb_list; /* each mapped page has its one private_bcb */
65 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
66 static GHashTable *page_position_hash;
68 static gboolean validate_page_position(const struct page_position *pagepos)
71 struct shmid_ds shmid_ds;
73 g_return_val_if_fail(pagepos!=NULL,FALSE);
74 g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
75 g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
76 g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
77 /* 'pagepos->shmid' may be -1 */
78 /* 'pagepos->privbcb_list' may be empty */
79 g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL),FALSE); /* either deleted or alive */
81 if (pagepos->shmid!=-1) {
82 errint=shmctl(pagepos->shmid,
85 g_return_val_if_fail(errint==0,FALSE);
87 g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
88 g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
89 g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
90 g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
91 /* 'shm_perm.mode' was seen with sticky bit 01000: */
92 g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
93 g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
94 g_return_val_if_fail(shmid_ds.shm_cpid==getpid(),FALSE);
95 g_return_val_if_fail(shmid_ds.shm_lpid==getpid(),FALSE);
96 g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
102 static guint page_position_hash_hash_func(const struct page_position *key)
104 g_return_val_if_fail(validate_page_position(key),0);
106 return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
109 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
111 g_return_val_if_fail(validate_page_position(a),FALSE);
112 g_return_val_if_fail(validate_page_position(b),FALSE);
114 return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
117 static void page_position_hash_key_destroy_func(struct page_position *key)
119 g_return_if_fail(validate_page_position(key));
120 g_assert(key->privbcb_list==NULL);
121 g_assert(key->shmid==-1);
126 static void page_position_hash_init(void)
128 if (page_position_hash)
130 page_position_hash=g_hash_table_new_full(
131 (GHashFunc)page_position_hash_hash_func, /* hash_func */
132 (GEqualFunc)page_position_hash_key_equal_func, /* key_equal_func */
133 (GDestroyNotify)page_position_hash_key_destroy_func, /* key_destroy_func */
134 NULL); /* value_destroy_func */
139 PUBLIC_BCB *PublicBcb; /* ->MappedLength, ->MappedFileOffset */
140 FILE_OBJECT *FileObject;
142 /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
143 ULONG MappedLength; /* It is the real requested size; it is not PAGE_SIZE aligned. */
144 /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
145 LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
146 gpointer base; /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
148 LARGE_INTEGER lsn; gboolean lsn_valid;
151 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
152 static GHashTable *private_bcb_hash;
154 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
156 g_return_if_fail(validate_Bcb(key));
161 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
163 struct page_position pagepos_local;
167 gpointer base_aligned;
169 g_return_if_fail(value!=NULL);
170 /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
171 * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
173 g_return_if_fail(value->PublicBcb!=NULL);
174 g_return_if_fail(value->FileObject!=NULL);
175 g_return_if_fail(value->ref_count==0);
176 g_return_if_fail(value->MappedLength>0);
177 g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
178 g_return_if_fail(value->base!=NULL);
179 g_return_if_fail(value->dirty==FALSE);
181 page_position_hash_init();
183 base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
185 pagepos_local.FileObject=value->FileObject;
186 pagepos_local.privbcb_list=NULL;
187 pagepos_local.shmid=-1;
190 offset<value->MappedLength;
192 struct page_position *pagepos;
194 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
195 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
196 g_assert(validate_page_position(pagepos));
197 g_assert(pagepos->privbcb_list!=NULL);
198 errint=shmdt(((char *)base_aligned)+offset);
201 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
202 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
203 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
205 if (!pagepos->privbcb_list) {
206 /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
208 errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
209 g_assert(errbool==TRUE);
212 g_assert(validate_page_position(pagepos));
218 static void private_bcb_hash_init(void)
220 if (private_bcb_hash)
222 private_bcb_hash=g_hash_table_new_full(
223 g_direct_hash, /* hash_func */
224 g_direct_equal, /* key_equal_func */
225 (GDestroyNotify)private_bcb_hash_key_destroy_func, /* key_destroy_func */
226 (GDestroyNotify)private_bcb_hash_value_destroy_func); /* value_destroy_func */
230 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
234 IO_STATUS_BLOCK IoStatus;
237 g_return_val_if_fail(FileObject!=NULL,0);
238 g_return_val_if_fail(address!=0,0);
239 g_return_val_if_fail(length!=0,0);
240 g_return_val_if_fail(FileOffset!=NULL,0);
242 /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
243 * successfuly read the data. Workaround it - preclear (not postclear) the
244 * buffer and do not make any other assumptions about the data read.
246 memset(address,0,PAGE_SIZE); /* pre-clear the buffer */
247 Mdl=MmCreateMdl(NULL,address,PAGE_SIZE); /* FIXME: Deprecated in favor of IoAllocateMdl() */
249 KeInitializeEvent(&Event,NotificationEvent,FALSE);
250 /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
251 IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
252 err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
253 g_assert(NT_SUCCESS(err));
254 g_assert(NT_SUCCESS(IoStatus.Status));
255 /* It is not == as the file may be shorter than requested */
256 g_assert(IoStatus.Information<=length);
259 /* Forbidden, see the comment above about ext2fsd.sys.
260 * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
263 return IoStatus.Information; /* may be shorter than real! */
267 static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length)
269 struct page_position pagepos_local,*pagepos;
270 struct private_bcb *privbcb,*privbcb_listitem;
273 page_position_hash_init();
275 pagepos_local.FileObject=FileObject;
276 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
277 pagepos_local.privbcb_list=NULL;
278 pagepos_local.shmid=-1;
279 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local)))
281 g_assert(validate_page_position(pagepos));
282 g_assert(pagepos->privbcb_list!=NULL);
285 for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
286 privbcb_listitem=privbcb_list->data;
288 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
289 && privbcb_listitem->MappedLength==Length) {
290 g_assert(privbcb==NULL); /* appropriate privbcb found twice */
291 privbcb=privbcb_listitem;
297 /* Sanity check 'privbcb': */
298 g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
306 * @FileObject: Initialized open #FileObject to map.
307 * %NULL value is forbidden.
308 * @FileOffset: The @FileObject file offset from where to map the region from.
309 * Negative value is forbidden.
310 * @Length: Requested length of the region to map from @FileObject.
311 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
312 * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
313 * %MAP_NO_READ currently ignored; we map the data always.
314 * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
315 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
316 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
317 * %NULL pointer is forbidden.
318 * @Buffer: Returns the mapped memory region start address.
319 * This address may not be %PAGE_SIZE aligned.
320 * %NULL pointer is forbidden.
322 * Maps the specified region of @FileObject to automatically chosen address space.
323 * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
325 * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
326 * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
327 * but W32 filesystems apparently do not conform to it. :-) If you do not take care of the
328 * modified data by some dirty-marking facility such data will be carelessly dropped without
329 * their commit to the disk.
331 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
333 * Returns: %TRUE if the region was successfuly mapped.
334 * @Bcb with the initialized new memory region.
335 * @Buffer with the address of the exact byte specified by @FileOffset.
337 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
338 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
340 PUBLIC_BCB **PublicBcbp,*PublicBcb;
341 struct page_position pagepos_local,*pagepos;
342 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
343 gpointer base_aligned;
344 size_t offset,length_mapped_aligned;
347 struct private_bcb *privbcb;
348 gboolean after_eof=FALSE; /* Did we reached the end of file already? */
349 GPtrArray *read_array;
351 g_return_val_if_fail(FileObject!=NULL,FALSE);
352 g_return_val_if_fail(FileOffset!=NULL,FALSE);
353 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
354 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
355 g_return_val_if_fail(Flags&MAP_WAIT,FALSE); /* FIXME: on-demand loading not yet implemented */
356 g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE); /* unknown flags? */
357 g_return_val_if_fail(Bcb!=NULL,FALSE);
358 g_return_val_if_fail(Buffer!=NULL,FALSE);
360 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
361 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
363 g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
364 if (FileObject->SectionObjectPointers->SharedCacheMap) {
365 /* FIXME: (NOTE*1) Weird but it appears as fastfat.sys during make_directory()
366 * can pass us an already cached 'FileObject' (with different offset/size).
367 * We must not CcUnpinData() it as its BCB is still referenced (CcUnpinData()ed) by fastfat.sys.
369 FileObject->SectionObjectPointers->SharedCacheMap=NULL;
371 g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
372 /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
373 * 'SectorSize' may not yet be initialized during mount operation
374 * and such state can be detected by missing 'FileObject->DeviceObject->Vpb'.
376 g_return_val_if_fail(0
377 || !FileObject->DeviceObject->Vpb /* not yet much ready */
378 || (FileObject->DeviceObject->SectorSize>0 /* prevent division by 0 */
379 && 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize)),
382 page_position_hash_init();
383 private_bcb_hash_init();
385 PublicBcbp=(PUBLIC_BCB **)Bcb;
386 /* extend 'FileOffset' and 'Length' to page boundaries */
387 FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
388 FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
389 length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
391 /* FIXME: We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
392 * even if such mapping for such file already exists. How to behave in such case?
394 if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length))) {
395 PublicBcb=privbcb->PublicBcb;
396 privbcb->ref_count++;
400 /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
401 /* TODO: on-demand loading */
402 /* Although we do zeroed-page mapping here we just reserve the linears
403 * space by it - all the page will be overriden by shmat(2) afterwards anyway.
407 length_mapped_aligned, /* length */
408 PROT_READ|PROT_WRITE, /* prot; FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
409 MAP_PRIVATE|MAP_ANONYMOUS, /* flags */
410 -1, /* fd; ignored due to MAP_ANONYMOUS */
411 0); /* offset; ignored due to MAP_ANONYMOUS */
412 g_assert(base_aligned!=NULL);
414 /* Create 'PublicBcb'; referenced by 'privbcb'. */
415 captive_new(PublicBcb);
416 PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
417 PublicBcb->NodeByteSize=sizeof(*PublicBcb); /* we have no extensions there */
418 PublicBcb->MappedLength=Length;
419 PublicBcb->MappedFileOffset=*FileOffset;
421 /* Create 'privbcb'; referenced by created 'pagepos'es. */
422 captive_new(privbcb);
423 privbcb->PublicBcb=PublicBcb;
424 privbcb->FileObject=FileObject;
425 privbcb->ref_count=1;
426 privbcb->MappedLength=PublicBcb->MappedLength;
427 privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
428 privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
429 privbcb->dirty=FALSE;
430 privbcb->lsn_valid=FALSE;
431 g_hash_table_insert(private_bcb_hash,
433 privbcb); /* value */
435 /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
436 * below as captive_Cc_IoPageRead() has very big consequences as it calls
437 * the filesystem code and we may get reentrancy.
438 * Therefore we store all missing page read requests to 'read_array' and we
439 * fill them when all the memory structures are in consistent state.
440 * We can also coalescence the requests more easily this way.
442 read_array=g_ptr_array_new();
444 pagepos_local.FileObject=FileObject;
445 pagepos_local.shmid=-1;
446 pagepos_local.privbcb_list=NULL;
449 offset<length_mapped_aligned;
453 pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
454 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
455 captive_new(pagepos);
456 /* FIXME: Reference 'FileObject' to not to leave broken references to it here */
457 *pagepos=pagepos_local;
458 pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600);
459 pagepos->privbcb_list=NULL;
460 g_ptr_array_add(read_array,pagepos); /* enlist this 'pagepos' as todo read item */
463 g_assert(pagepos->privbcb_list!=NULL);
464 g_assert(pagepos->shmid!=-1);
465 pageaddr=(gpointer)(((char *)base_aligned)+offset);
466 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
467 * mmap(2) is still needed to get linear block of memory assignment.
469 /* TODO:thread; munmap()..shmat() window */
470 errint=munmap(pageaddr,PAGE_SIZE);
472 errptr=shmat(pagepos->shmid,
473 pageaddr, /* shmaddr */
474 0); /* shmflg; !SHM_RDONLY==r/w FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
475 g_assert(errptr==pageaddr);
477 g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
478 pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb); /* order not important */
479 g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
480 if (pagepos->privbcb_list->next==NULL) { /* exactly one item (we just added it now) */
482 errint=shmctl(pagepos->shmid,
486 g_hash_table_insert(page_position_hash,
488 pagepos); /* value */
490 g_assert(validate_page_position(pagepos));
493 /* Fill in the missing pages content todo-list stored in 'read_array': */
494 while (read_array->len && (pagepos=g_ptr_array_remove_index(read_array,
498 struct pagepos *pagepos_next;
499 gpointer pageaddr,pageaddr_next;
501 pageaddr=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
503 /* Coalescence of the requests */
504 while (read_array->len) {
505 pagepos_next=g_ptr_array_index(read_array,
507 pageaddr_next=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
508 if (pageaddr_next!=((char *)pageaddr)+read_size)
510 read_size+=PAGE_SIZE;
511 g_assert(pagepos_next==g_ptr_array_remove_index(read_array,
514 /* Read the range content: */
515 got=captive_Cc_IoPageRead(FileObject,pageaddr,read_size,&pagepos->FileOffset);
519 g_assert(got<=PAGE_SIZE);
520 after_eof=(got<PAGE_SIZE);
522 g_ptr_array_free(read_array,
523 TRUE); /* free_seg; free the array of gpointer(==struct pagepos *) items */
526 /* offset _into_ page, may not be PAGE_SIZE aligned: */
527 *Buffer=privbcb->base;
528 *PublicBcbp=PublicBcb;
529 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
530 "%s: result: privbcb->base=%p,privbcb->base+privbcb->MappedLength=%p,privbcb->MappedLength=0x%lX",
531 G_STRLOC,privbcb->base,((char *)privbcb->base)+privbcb->MappedLength,(unsigned long)privbcb->MappedLength);
532 g_assert(validate_Bcb(PublicBcb)==TRUE);
533 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
541 * @FileObject: Initialized open #FileObject to map.
542 * %NULL value is forbidden.
543 * @MappedFileOffset: The @FileObject file offset from where to map the region from.
544 * Negative value is forbidden.
545 * @MappedLength: Requested length of the region to map from @FileObject.
546 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
547 * @Wait: Whether disk waiting is permitted for this function.
548 * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
549 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
550 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
551 * %PIN_NO_READ currently ignored; we map the data always.
552 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
553 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
554 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
555 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
556 * %NULL pointer is forbidden.
557 * @Buffer: Returns the mapped memory region start address.
558 * This address may not be %PAGE_SIZE aligned.
559 * %NULL pointer is forbidden.
561 * This function will allow you to modify the data mapped by CcMapData().
562 * libcaptive does not differentiate this function with CcMapData().
564 * NEVER re-read any memory from FileObject here!
565 * at least fastfat.sys directory create relies on the fact of CcPinRead()
566 * with already modified buffers to be left intact.
568 * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
569 * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
571 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
573 * Returns: %TRUE if the region was successfuly mapped.
574 * @Bcb with the initialized new memory region.
575 * @Buffer with the address of the exact byte specified by @FileOffset.
577 BOOLEAN CcPinMappedData
578 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
580 struct private_bcb *privbcb;
582 g_return_val_if_fail(FileObject!=NULL,FALSE);
583 g_return_val_if_fail(FileOffset!=NULL,FALSE);
584 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
585 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
586 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
587 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
588 g_return_val_if_fail(Bcb!=NULL,FALSE);
590 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
591 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
593 privbcb=captive_privbcb_find(FileObject,FileOffset,Length);
594 if (!privbcb && (Flags & PIN_IF_BCB)) /* BCB does not exist */
596 /* Appropriate privbcb not found.
597 * This IMO should not happen and it is a bug in the client.
598 * Unfortuantely fastfat.sys FatLocateVolumeLabel() will CcPinMappedData()
599 * the volume label dirent without any previous CcMapData() or CcPinRead() !
613 /* NEVER re-read any memory from FileObject here! */
615 privbcb->ref_count++;
617 /* Memory already mapped by CcMapData(). */
618 *Bcb=privbcb->PublicBcb;
625 * @FileObject: Initialized open #FileObject to map.
626 * %NULL value is forbidden.
627 * @FileOffset: The @FileObject file offset from where to map the region from.
628 * Negative value is forbidden.
629 * @Length: Requested length of the region to map from @FileObject.
630 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
631 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
632 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
633 * %PIN_NO_READ currently ignored; we map the data always.
634 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
635 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
636 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
637 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
638 * %NULL pointer is forbidden.
639 * @Buffer: Returns the mapped memory region start address.
640 * This address may not be %PAGE_SIZE aligned.
641 * %NULL pointer is forbidden.
643 * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
644 * See these two functions for the details. It has a difference to subsequent
645 * calling of CcMapData() and CcPinMappedData() instead as this call counts
646 * only as one function for a corresponding CcUnpinData() call.
648 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
650 * Returns: %TRUE if the region was successfuly mapped.
651 * @Bcb with the initialized new memory region.
652 * @Buffer with the address of the exact byte specified by @FileOffset.
654 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
655 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
657 PVOID Bcb_CcPinMappedData;
659 gboolean count_CcMapData;
660 struct private_bcb *privbcb;
662 g_return_val_if_fail(FileObject!=NULL,FALSE);
663 g_return_val_if_fail(FileOffset!=NULL,FALSE);
664 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
665 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
666 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
667 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
668 g_return_val_if_fail(Bcb!=NULL,FALSE);
669 g_return_val_if_fail(Buffer!=NULL,FALSE);
671 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
672 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
674 if (!(Flags&PIN_IF_BCB)) {
675 errbool=CcMapData(FileObject,FileOffset,Length,
677 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
679 g_return_val_if_fail(errbool==TRUE,FALSE);
680 count_CcMapData=TRUE;
683 count_CcMapData=FALSE;
685 errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
686 if (!(Flags&PIN_IF_BCB)) {
687 g_return_val_if_fail(errbool==TRUE,FALSE);
688 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
691 if (errbool==FALSE) /* FALSE permitted; We may fail if Bcb does not exist yet. */
693 *Bcb=Bcb_CcPinMappedData;
696 privbcb=captive_privbcb_find(FileObject,FileOffset,Length);
697 g_assert(privbcb!=NULL);
698 g_assert(privbcb->ref_count>=1);
699 if (count_CcMapData) {
700 /* CcPinRead() must always reference-count only by 1 despite any sub-called functions! */
701 g_assert(privbcb->ref_count>=2);
702 privbcb->ref_count--;
709 static void logging_notify_privbcb_flush(struct private_bcb *privbcb);
711 static void captive_privbcb_flush(struct private_bcb *privbcb)
715 IO_STATUS_BLOCK IoStatus;
717 gpointer base_sectoraligned;
718 gsize length_sectoraligned;
719 LARGE_INTEGER FileOffset_sectoraligned;
725 logging_notify_privbcb_flush(privbcb);
727 g_assert(privbcb->FileObject->DeviceObject!=NULL);
729 /* We can get 0=='privbcb->FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
730 * FIXME: We are unable to find the correct sectorsize for a filesystem as
731 * even the 'CommonFcb' below contains invalid information.
732 * As we need to have 'sectorsize' <=filesystem_blocksize at least for ext2fsd.sys
733 * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
734 * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
736 sectorsize=PAGE_SIZE;
738 sectorsize=privbcb->FileObject->DeviceObject->SectorSize;
739 if (privbcb->FileObject->FsContext) {
740 REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileObject->FsContext;
742 /* FIXME: Check CommonFcb->Type */
743 /* 'AllocationSize' can be less than 'sectorsize' if the total file length is smaller.
744 * Observed with ext2fsd.sys volume-file.
746 if (sectorsize<CommonFcb->AllocationSize.QuadPart)
747 sectorsize=CommonFcb->AllocationSize.QuadPart;
751 /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
752 g_assert(sectorsize>0);
753 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,sectorsize));
754 /* We align here directly the 'privbcb->base' which is not correct.
755 * We should rather aligned according to 'privbcb->MappedOffset' but
756 * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
757 * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
759 g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
760 ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,sectorsize));
761 base_sectoraligned =CAPTIVE_ROUND_DOWN(privbcb->base,sectorsize);
762 length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,sectorsize)
763 -((char *)base_sectoraligned);
764 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,sectorsize));
765 FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,sectorsize);
767 Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
770 KeInitializeEvent(&Event,NotificationEvent,FALSE);
772 /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
773 /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
774 err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
775 g_assert(NT_SUCCESS(err));
776 g_assert(NT_SUCCESS(IoStatus.Status));
777 /* We should write at least the unaligned mapped data although we
778 * do not need to successfuly write the whole aligned amount.
779 * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
780 * during FAT write by fastfat.sys.
782 g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
783 +privbcb->MappedLength);
784 g_assert(IoStatus.Information<=length_sectoraligned);
786 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
787 "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
788 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
789 base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
790 (gulong)IoStatus.Information);
792 privbcb->dirty=FALSE;
796 static gboolean CcUnpinData_idle_func(struct private_bcb *privbcb /*data*/)
800 g_return_val_if_fail(privbcb!=NULL,FALSE); /* remove-me */
801 g_return_val_if_fail(privbcb->PublicBcb!=NULL,FALSE); /* remove-me */
803 g_assert(privbcb->ref_count>0);
805 if (--privbcb->ref_count)
806 return FALSE; /* remove-me */
808 errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
809 g_assert(errbool==TRUE);
811 return FALSE; /* remove-me */
816 * @Bcb: Initialized #PUBLIC_BCB structure.
817 * %NULL value is forbidden.
819 * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
821 VOID CcUnpinData(IN PVOID Bcb)
823 PUBLIC_BCB *PublicBcb;
824 struct private_bcb *privbcb;
826 g_return_if_fail(validate_Bcb(Bcb));
828 private_bcb_hash_init();
830 PublicBcb=(PUBLIC_BCB *)Bcb;
831 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
832 g_return_if_fail(privbcb!=NULL);
834 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
835 /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
837 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
838 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d",G_STRLOC,
839 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count);
841 g_assert(privbcb->ref_count>0);
842 /* Do not write back the contents if this is not the final unpin.
843 * FIXME: Is it correct?
845 if (--privbcb->ref_count)
847 privbcb->ref_count++; /* will get decreased again in CcUnpinData_idle_func() */
849 captive_privbcb_flush(privbcb);
851 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
852 /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
853 if (privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb)
854 privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
856 /* Caboom: lfs (log file system) of ntfs.sys-NT5.1sp1 will do:
857 * CcPinRead(); CcUnpinData(); access Buffer (wanna-crash);
858 * Therefore we must postpone the buffer unmapping to some idle function...
859 * I expect it a bug in ntfs.sys.
862 G_PRIORITY_DEFAULT_IDLE, /* priority */
863 (GSourceFunc)CcUnpinData_idle_func, /* function */
869 VOID CcRepinBcb(IN PVOID Bcb)
871 PUBLIC_BCB *PublicBcb;
872 struct private_bcb *privbcb;
874 g_return_if_fail(validate_Bcb(Bcb));
876 private_bcb_hash_init();
878 PublicBcb=(PUBLIC_BCB *)Bcb;
879 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
880 g_return_if_fail(privbcb!=NULL);
882 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
883 Bcb,privbcb->FileObject);
885 privbcb->ref_count++;
889 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
891 PUBLIC_BCB *PublicBcb;
892 struct private_bcb *privbcb;
894 g_return_if_fail(validate_Bcb(Bcb));
895 g_return_if_fail(IoStatus!=NULL);
897 private_bcb_hash_init();
899 PublicBcb=(PUBLIC_BCB *)Bcb;
900 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
901 g_return_if_fail(privbcb!=NULL);
903 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
904 Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
906 IoStatus->Status=STATUS_SUCCESS;
907 IoStatus->Information=privbcb->MappedLength;
913 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
915 PUBLIC_BCB *PublicBcb;
916 struct private_bcb *privbcb;
918 g_return_if_fail(validate_Bcb(Bcb));
920 private_bcb_hash_init();
922 PublicBcb=(PUBLIC_BCB *)Bcb;
923 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
924 g_return_if_fail(privbcb!=NULL);
926 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
927 Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
929 /* 'privbcb->ref_count' not to be increased by this function. */
933 privbcb->lsn_valid=TRUE;
942 * @FileObject: Initialized open #FileObject to update file sizes of.
943 * %NULL value is forbidden.
944 * @FileSizes: New file sizes to update cache to.
945 * %NULL value is forbidden.
947 * Update cache properties after file sizes were updated.
948 * Probably only the exceeding pages need to be unmapped and BCBs updated
949 * if FileSizes->AllocationSize gets shrunk.
951 * FIXME: Currently a NOP with no effect by libcaptive.
953 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
955 g_return_if_fail(FileObject!=NULL);
956 g_return_if_fail(FileSizes!=NULL);
958 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
959 "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
960 FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
961 (guint64)FileSizes->ValidDataLength.QuadPart);
963 /* FIXME: check BCB && 'struct page_position' invalidities */
968 * CcPurgeCacheSection:
969 * @SectionObjectPointer: Pointer specifying file to purge;
970 * %NULL value is forbidden.
971 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
972 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %FALSE return code in such case.
973 * @FileOffset: Starting offset of the ranger to purge.
974 * %NULL pointer is permitted and it means to purge the whole whole.
975 * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
976 * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
977 * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
979 * Drop any caching for shrunken file which is not being deleted.
980 * libcaptive will no longer consider such #BCB as dirty.
982 * Returns: %TRUE if the range was purged successfuly.
984 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
985 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
987 PUBLIC_BCB *PublicBcb;
988 struct private_bcb *privbcb;
990 g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
991 if (SectionObjectPointer->SharedCacheMap==NULL)
992 return FALSE; /* failed - nothing to purge */
993 g_return_val_if_fail(FileOffset==NULL,FALSE); /* NOT IMPLEMENTED YET */
995 PublicBcb=SectionObjectPointer->SharedCacheMap;
996 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
998 private_bcb_hash_init();
1000 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1001 g_return_val_if_fail(privbcb!=NULL,FALSE);
1003 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1004 "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
1005 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1006 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
1008 privbcb->dirty=FALSE; /* purge it */
1016 * @FileObject: Initialized open #FileObject to map.
1017 * %NULL value is forbidden.
1018 * @FileOffset: The @FileObject file offset from where to map the region from.
1019 * Negative value is forbidden.
1020 * @Length: Requested length of the region to map from @FileObject.
1021 * Value %0 is permitted (no effect of this function call).
1022 * @Wait: Whether disk waiting is permitted for this function.
1023 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1024 * @Buffer: Address of memory region with already allocated memory of size @Length.
1025 * This address may not be %PAGE_SIZE aligned.
1026 * %NULL pointer is forbidden.
1027 * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1028 * %NULL pointer is forbidden.
1030 * Reads the specified region of @FileObject to the given @Buffer.
1031 * No on-demand loading is in effect.
1033 * Returns: %TRUE if the region was successfuly filled with @Length bytes.
1034 * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
1035 * @IoStatus.Information initialized by @Length if successful.
1037 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
1038 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
1044 g_return_val_if_fail(FileObject!=NULL,FALSE);
1045 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1046 g_return_val_if_fail(Buffer!=NULL,FALSE);
1047 g_return_val_if_fail(IoStatus!=NULL,FALSE);
1049 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1050 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1052 IoStatus->Status=STATUS_UNSUCCESSFUL;
1053 IoStatus->Information=0;
1057 FileObject, /* FileObject */
1058 FileOffset, /* FileOffset */
1059 Length, /* Length */
1061 &MappedBcb, /* Bcb */
1062 &MappedBuffer); /* Buffer */
1063 g_return_val_if_fail(errbool==TRUE,FALSE);
1065 memcpy(Buffer,MappedBuffer,Length);
1067 CcUnpinData(MappedBcb); /* no error code */
1070 IoStatus->Status=STATUS_SUCCESS;
1071 IoStatus->Information=Length;
1079 * @FileObject: Initialized open #FileObject to map.
1080 * %NULL value is forbidden.
1081 * @FileOffset: The @FileObject file offset from where to map the region from.
1082 * Negative value is forbidden.
1083 * @Length: Requested length of the region to map from @FileObject.
1084 * Value %0 is permitted (no effect of this function call).
1085 * @Wait: Whether disk waiting is permitted for this function.
1086 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1087 * @Buffer: Address of memory region with already allocated memory of size @Length.
1088 * This address may not be %PAGE_SIZE aligned.
1089 * %NULL pointer is forbidden.
1091 * Writes the specified region of the given @Buffer to @FileObject.
1093 * Returns: %TRUE if the region was successfuly written with @Length bytes.
1095 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
1096 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
1102 g_return_val_if_fail(FileObject!=NULL,FALSE);
1103 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1104 g_return_val_if_fail(Buffer!=NULL,FALSE);
1106 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1107 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1111 FileObject, /* FileObject */
1112 FileOffset, /* FileOffset */
1113 Length, /* Length */
1115 &MappedBcb, /* Bcb */
1116 &MappedBuffer); /* Buffer */
1117 g_return_val_if_fail(errbool==TRUE,FALSE);
1119 memcpy(MappedBuffer,Buffer,Length);
1121 CcSetDirtyPinnedData(
1122 MappedBcb, /* Bcb */
1124 CcUnpinData(MappedBcb); /* no error code */
1133 * @FileObject: Initialized open #FileObject to map.
1134 * %NULL value is forbidden.
1135 * @BytesToWrite: Amount of data to be asked whether it will be accepted.
1136 * Value %0 is permitted.
1137 * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
1138 * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
1140 * Asks cache manager if it would currently accept write request to @FileObject
1141 * of @BytesToWrite bytes with @Wait condition.
1142 * libcaptive will always accept any writes. This function is a NOP.
1144 * Returns: libcaptive always returns %TRUE.
1146 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
1148 g_return_val_if_fail(FileObject!=NULL,FALSE);
1150 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
1151 FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
1158 * CcSetReadAheadGranularity:
1159 * @FileObject: Initialized open #FileObject to map.
1160 * %NULL value is forbidden.
1161 * @Granularity: Suggested size of the cache element.
1162 * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
1164 * libcaptive does not implement any caching and therefore this function
1167 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
1169 g_return_if_fail(FileObject!=NULL);
1170 g_return_if_fail(Granularity>=PAGE_SIZE);
1171 g_return_if_fail((Granularity&(Granularity-1))==0); /* Power of two */
1173 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
1174 FileObject,(gulong)Granularity);
1176 /* NOP; no caching by libcaptive */
1182 * @SectionObjectPointer: Pointer specifying file to flush;
1183 * %NULL value is forbidden.
1184 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1185 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
1186 * @FileOffset: Optional starting point of the range to flush.
1187 * %NULL value is permitted.
1188 * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
1189 * @IoStatus: Optionally returns the resulting operation status.
1190 * #Information field will contain the number of bytes flushed.
1191 * %NULL value is permitted.
1193 * Flushes out any pending dirty data in cache manager BCB mapping.
1194 * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
1196 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1197 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
1199 PUBLIC_BCB *PublicBcb;
1200 struct private_bcb *privbcb;
1201 IO_STATUS_BLOCK IoStatus_CcUnpinRepinnedBcb_local;
1203 g_return_if_fail(SectionObjectPointer!=NULL);
1205 if (SectionObjectPointer->SharedCacheMap==NULL) {
1207 IoStatus->Status=STATUS_SUCCESS;
1208 IoStatus->Information=0;
1213 PublicBcb=SectionObjectPointer->SharedCacheMap;
1214 g_return_if_fail(validate_Bcb(PublicBcb));
1216 private_bcb_hash_init();
1218 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1219 g_return_if_fail(privbcb!=NULL);
1221 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1222 "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
1223 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1224 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
1227 g_assert(FileOffset->QuadPart >=privbcb->MappedFileOffset.QuadPart);
1228 g_assert(FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
1231 /* We may find some 'privbcb' being already sheduled to be destroyed.
1232 * We need to reference it to not to loose it in the middle of our flush operation.
1234 CcRepinBcb(PublicBcb);
1236 /* FIXME: Flush just FileOffset..FileOfset+Length part */
1237 captive_privbcb_flush(privbcb);
1240 PublicBcb, /* Bcb */
1241 TRUE, /* WriteThrough; ignored by libcaptive */
1242 &IoStatus_CcUnpinRepinnedBcb_local); /* IoStatus; ignored here */
1245 IoStatus->Status=STATUS_SUCCESS;
1246 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
1251 static gboolean captive_FileObject_ZeroData(PFILE_OBJECT FileObject,guint64 window_bottom,guint64 window_top)
1255 gpointer zerobuffer;
1256 LARGE_INTEGER window_bottom_LargeInteger;
1258 IO_STATUS_BLOCK IoStatus;
1260 g_return_val_if_fail(FileObject!=NULL,FALSE);
1261 g_return_val_if_fail(window_bottom<=window_top,FALSE);
1263 if (window_bottom==window_top) /* trivia */
1266 g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
1268 /* Such requirement is W32 documented for CcZeroData()
1269 * and it is really a requirement at least for the underlying fastfat.sys.
1271 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(window_bottom,FileObject->DeviceObject->SectorSize));
1272 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(window_top ,FileObject->DeviceObject->SectorSize));
1274 zerobuffer=g_malloc0(window_top-window_bottom);
1275 window_bottom_LargeInteger.QuadPart=window_bottom;
1277 Mdl=MmCreateMdl(NULL,zerobuffer,window_top-window_bottom);
1278 g_assert(Mdl!=NULL);
1280 KeInitializeEvent(&Event,NotificationEvent,FALSE);
1282 /* FIXME: Notify logging subsystem by LSNs? */
1284 /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
1285 err=IoSynchronousPageWrite(FileObject,Mdl,&window_bottom_LargeInteger,&Event,&IoStatus);
1286 g_assert(NT_SUCCESS(err));
1287 g_assert(NT_SUCCESS(IoStatus.Status));
1288 /* We should write up to the last sector touched by the range although we
1289 * do not need to successfuly write the whole aligned amount.
1290 * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
1291 * during FAT write by fastfat.sys.
1293 g_assert(IoStatus.Information==0
1294 || (window_top-window_bottom)==CAPTIVE_ROUND_UP(IoStatus.Information,FileObject->DeviceObject->SectorSize));
1295 g_assert(IoStatus.Information<=window_top-window_bottom); /* redundant */
1303 BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
1305 guint64 window_lower_bottom,window_lower_top;
1306 guint64 window_higher_bottom,window_higher_top;
1308 g_return_val_if_fail(FileObject!=NULL,FALSE);
1309 g_return_val_if_fail(StartOffset!=NULL,FALSE);
1310 g_return_val_if_fail(EndOffset!=NULL,FALSE);
1311 g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
1313 g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
1315 window_lower_bottom=StartOffset->QuadPart;
1316 window_lower_top=EndOffset->QuadPart;
1317 window_higher_bottom=window_higher_top=0;
1319 if (FileObject->SectionObjectPointers->SharedCacheMap!=NULL) {
1320 PUBLIC_BCB *PublicBcb;
1321 struct private_bcb *privbcb;
1322 guint64 window_cached_bottom,window_cached_top;
1324 PublicBcb=FileObject->SectionObjectPointers->SharedCacheMap;
1325 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1327 private_bcb_hash_init();
1329 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1330 g_return_val_if_fail(privbcb!=NULL,FALSE);
1332 window_cached_bottom=MAX(StartOffset->QuadPart,privbcb->MappedFileOffset.QuadPart);
1333 window_cached_top=MIN(EndOffset->QuadPart,privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
1335 if (window_cached_bottom<window_cached_top) {
1336 memset(((char *)privbcb->base)+window_cached_bottom-privbcb->MappedFileOffset.QuadPart,0,window_cached_top-window_cached_bottom);
1337 privbcb->dirty=TRUE;
1338 window_higher_top=window_lower_top;
1339 window_lower_top=window_cached_bottom;
1340 window_higher_bottom=window_cached_top;
1343 if (window_lower_bottom<window_lower_top) {
1344 g_return_val_if_fail(Wait==TRUE,FALSE);
1345 if (!captive_FileObject_ZeroData(FileObject,window_lower_bottom,window_lower_top))
1346 g_return_val_if_reached(FALSE);
1348 if (window_higher_bottom<window_higher_top) {
1349 g_return_val_if_fail(Wait==TRUE,FALSE);
1350 if (!captive_FileObject_ZeroData(FileObject,window_higher_bottom,window_higher_top))
1351 g_return_val_if_reached(FALSE);
1358 /* map (PVOID LogHandle) -> (GList (of FILE_OBJECT *) *FileObject_list) */
1359 static GHashTable *log_handle_hash;
1361 static void log_handle_hash_init(void)
1363 if (log_handle_hash)
1365 log_handle_hash=g_hash_table_new(
1366 g_direct_hash, /* hash_func */
1367 g_direct_equal); /* key_equal_func */
1370 /* map (FILE_OBJECT *FileObject) -> (struct FileObject_logging *) */
1371 struct FileObject_logging {
1373 PFLUSH_TO_LSN FlushToLsnRoutine;
1376 static GHashTable *FileObject_logging_hash;
1378 static void FileObject_logging_hash_init(void)
1380 if (FileObject_logging_hash)
1382 FileObject_logging_hash=g_hash_table_new(
1383 g_direct_hash, /* hash_func */
1384 g_direct_equal); /* key_equal_func */
1387 static void logging_notify_privbcb_flush(struct private_bcb *privbcb)
1389 struct FileObject_logging *FileObject_logging;
1391 g_return_if_fail(privbcb!=NULL);
1393 if (!privbcb->lsn_valid) /* nothing to report anyway */
1396 FileObject_logging_hash_init();
1398 if (!(FileObject_logging=g_hash_table_lookup(FileObject_logging_hash,privbcb->FileObject)))
1400 (*FileObject_logging->FlushToLsnRoutine)(FileObject_logging->LogHandle,privbcb->lsn);
1403 VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
1405 GList *LogHandle_list;
1406 struct FileObject_logging *FileObject_logging;
1408 g_return_if_fail(FileObject!=NULL);
1409 /* 'LogHandle' may be NULL */
1410 g_return_if_fail(FlushToLsnRoutine!=NULL);
1412 log_handle_hash_init();
1413 FileObject_logging_hash_init();
1415 if (!(FileObject_logging=g_hash_table_lookup(
1416 FileObject_logging_hash,FileObject))) {
1417 captive_new(FileObject_logging);
1418 g_hash_table_insert(FileObject_logging_hash,
1419 FileObject,FileObject_logging);
1422 LogHandle_list=g_hash_table_lookup(log_handle_hash,FileObject_logging->LogHandle);
1423 g_assert(NULL!=g_list_find(LogHandle_list,FileObject));
1424 LogHandle_list=g_list_remove(LogHandle_list,FileObject);
1425 g_assert(NULL==g_list_find(LogHandle_list,FileObject));
1428 FileObject_logging->LogHandle=LogHandle;
1429 FileObject_logging->FlushToLsnRoutine=FlushToLsnRoutine;
1431 LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
1432 LogHandle_list=g_list_prepend(LogHandle_list,FileObject);
1433 g_hash_table_insert(log_handle_hash,LogHandle,LogHandle_list);
1437 struct CcGetDirtyPages_param {
1438 PDIRTY_PAGE_ROUTINE DirtyPageRoutine; /* arg of CcGetDirtyPages() */
1439 IN PVOID Context1; /* arg of CcGetDirtyPages() */
1440 IN PVOID Context2; /* arg of CcGetDirtyPages() */
1441 FILE_OBJECT *FileObject; /* search through 'page_position_hash' for 'FileObject' */
1442 LARGE_INTEGER OldestLsn; gboolean OldestLsn_found; /* intermediate return value of CcGetDirtyPages() */
1445 static void CcGetDirtyPages_page_position_hash_foreach(
1446 struct page_position *pagepos, /* key */
1447 struct page_position *pagepos2, /* value */
1448 struct CcGetDirtyPages_param *CcGetDirtyPages_param) /* user_data */
1450 LARGE_INTEGER FileOffset_local;
1451 LARGE_INTEGER OldestLsn,NewestLsn;
1452 LARGE_INTEGER OldestLsn_check,NewestLsn_check;
1453 gboolean lsn_found=FALSE;
1454 GList *privbcb_list;
1456 g_return_if_fail(pagepos!=NULL);
1457 g_return_if_fail(pagepos==pagepos2);
1458 g_return_if_fail(CcGetDirtyPages_param!=NULL);
1460 FileOffset_local=pagepos->FileOffset;
1463 privbcb_list=pagepos->privbcb_list;
1465 privbcb_list=privbcb_list->next) {
1466 struct private_bcb *privbcb=privbcb_list->data;
1468 if (!privbcb->lsn_valid)
1471 OldestLsn=NewestLsn=privbcb->lsn;
1474 OldestLsn.QuadPart=MIN(OldestLsn.QuadPart,privbcb->lsn.QuadPart);
1475 NewestLsn.QuadPart=MAX(NewestLsn.QuadPart,privbcb->lsn.QuadPart);
1481 if (!CcGetDirtyPages_param->OldestLsn_found) {
1482 CcGetDirtyPages_param->OldestLsn=OldestLsn;
1483 CcGetDirtyPages_param->OldestLsn_found=TRUE;
1486 CcGetDirtyPages_param->OldestLsn.QuadPart=MIN(CcGetDirtyPages_param->OldestLsn.QuadPart,OldestLsn.QuadPart);
1488 OldestLsn_check=OldestLsn;
1489 NewestLsn_check=NewestLsn;
1491 (*CcGetDirtyPages_param->DirtyPageRoutine)(
1492 pagepos->FileObject, /* FileObject */
1493 &FileOffset_local, /* FileOffset */
1494 PAGE_SIZE, /* Length */
1495 &OldestLsn, /* OldestLsn */
1496 &NewestLsn, /* NewestLsn */
1497 CcGetDirtyPages_param->Context1, /* Context1 */
1498 CcGetDirtyPages_param->Context2); /* Context2 */
1500 /* just unconfirmed sanity: */
1501 g_assert(FileOffset_local.QuadPart==pagepos->FileOffset.QuadPart); /* check for possible corruption */
1502 g_assert(OldestLsn_check.QuadPart==OldestLsn.QuadPart);
1503 g_assert(NewestLsn_check.QuadPart==NewestLsn.QuadPart);
1506 /* libcaptive must return #gint64 instead of the official #LARGE_INTEGER
1507 * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
1509 gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
1510 IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
1512 GList *LogHandle_list;
1513 struct CcGetDirtyPages_param CcGetDirtyPages_param;
1515 /* 'LogHandle' may be NULL */
1516 g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
1518 log_handle_hash_init();
1519 page_position_hash_init();
1521 CcGetDirtyPages_param.DirtyPageRoutine=DirtyPageRoutine;
1522 CcGetDirtyPages_param.Context1=Context1;
1523 CcGetDirtyPages_param.Context2=Context2;
1524 CcGetDirtyPages_param.OldestLsn_found=FALSE;
1527 LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
1529 LogHandle_list=LogHandle_list->next) {
1530 CcGetDirtyPages_param.FileObject=LogHandle_list->data;
1531 g_hash_table_foreach(
1532 page_position_hash, /* hash_table */
1533 (GHFunc)CcGetDirtyPages_page_position_hash_foreach, /* func */
1534 &CcGetDirtyPages_param); /* user_data */
1537 if (!CcGetDirtyPages_param.OldestLsn_found)
1539 return CcGetDirtyPages_param.OldestLsn.QuadPart;
1544 * CcSetAdditionalCacheAttributes:
1545 * @FileObject: Initialized open #FileObject to map.
1546 * %NULL value is forbidden.
1547 * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
1548 * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
1550 * libcaptive does not implement any caching and therefore this function
1553 VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
1555 g_return_if_fail(FileObject!=NULL);
1557 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
1558 FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
1560 /* NOP; no caching by libcaptive */
1564 VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
1566 g_return_if_fail(Bcb!=NULL);
1567 g_return_if_fail(Owner!=NULL);
1569 /* FIXME:thread; NOP if no threads present */