2 * reactos Cache Manager mapper emulation of libcaptive
3 * Copyright (C) 2002-2003 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>
38 #include "captive/leave.h"
39 #include <glib/gtree.h>
44 #define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45 /* FIXME: unknown, undocumented */
45 /* Prepare linear mapped space by CcInitializeCacheMap()?
46 * NT-5.1sp will map the same pages of a file to the same memory location
47 * either by CcMap*() or CcPin*().
48 * We do a separate mapping for each Bcb protected by bounding unmapped pages
49 * with IPC shm shared pages if a file page is mapped multiple times.
50 * I am not aware how they can reserve enough virtual memory space during
51 * client with mapping requests of increasing range tendency.
52 * This define will try to pre-map the file as one linear are during
53 * CcInitializeCacheMap() according to its (CC_FILE_SIZES *)FileSizes
54 * but TODO the range is currently left unused in further mapping functions.
56 /* #define CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR 1 */
59 /* Flush all the buffers directly on the fly.
60 * For details see the comment in libcaptive/client/init.c/captive_shutdown().
62 gboolean captive_cc_unmounting=FALSE;
65 /* map: (FILE_OBJECT *)FileObject -> (struct fileobject_cached *) */
66 static GHashTable *fileobject_cached_hash;
68 struct fileobject_cached {
69 CACHE_MANAGER_CALLBACKS CallBacks;
70 VOID *LazyWriterContext;
71 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
76 static void fileobject_cached_hash_value_destroy_func(struct fileobject_cached *value)
78 g_return_if_fail(value!=NULL);
83 static void fileobject_cached_hash_init(void)
85 if (fileobject_cached_hash)
87 fileobject_cached_hash=g_hash_table_new_full(
88 g_direct_hash, /* hash_func */
89 g_direct_equal, /* key_equal_func */
90 NULL, /* key_destroy_func */
91 (GDestroyNotify)fileobject_cached_hash_value_destroy_func); /* value_destroy_func */
96 * CcInitializeCacheMap:
97 * @FileObject: Existing file to set callbacks for. Ignored by libcaptive.
98 * %NULL value is forbidden.
99 * @FileSizes: Some file sizes suggestions. Ignored by libcaptive.
100 * %NULL value is forbidden.
101 * @PinAccess: CcPin*() functions will be used with @FileObject? Ignored by libcaptive.
102 * @CallBacks: Provided callback functions for readahead/writebehind. Ignored by libcaptive.
103 * %NULL value is forbidden.
104 * @LazyWriterContext: Value passed to functions of @CallBacks to bind with @FileObject.
105 * %NULL value is permitted.
107 * Provides support of readahead/writebehind in W32. Function should be called
108 * by W32 filesystem to offer its functions to W32 kernel. These functions
109 * are never called by libcaptive, this call is a NOP in libcaptive.
111 VOID CcInitializeCacheMap(IN PFILE_OBJECT FileObject,
112 IN PCC_FILE_SIZES FileSizes,IN BOOLEAN PinAccess,IN PCACHE_MANAGER_CALLBACKS CallBacks,IN PVOID LazyWriterContext)
114 struct fileobject_cached *fileobject_cached;
115 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
117 LARGE_INTEGER FileOffset0;
122 g_return_if_fail(FileObject!=NULL);
123 g_return_if_fail(FileSizes!=NULL);
124 g_return_if_fail(CallBacks!=NULL);
126 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
127 "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX,"
128 "PinAccess=%d,CallBacks=%p,LazyWriterContext=%p",G_STRLOC,
129 FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
130 (guint64)FileSizes->ValidDataLength.QuadPart,(gint)PinAccess,CallBacks,LazyWriterContext);
132 fileobject_cached_hash_init();
136 /* Double reinitialization without CcUninitializeCacheMap()
137 * is done by both fastfat.sys and ntfs.sys of NT-5.1sp1.
140 g_assert(g_hash_table_lookup(fileobject_cached_hash,FileObject)==NULL);
143 captive_new(fileobject_cached);
144 fileobject_cached->CallBacks=*CallBacks;
145 fileobject_cached->LazyWriterContext=LazyWriterContext;
147 g_hash_table_insert(fileobject_cached_hash,
148 FileObject, /* key */
149 fileobject_cached); /* value */
151 #if CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
152 size64=MAX(MAX(FileSizes->AllocationSize.QuadPart,FileSizes->FileSize.QuadPart),
153 (FileSizes->ValidDataLength.QuadPart==G_MAXINT64 ? 0 : FileSizes->ValidDataLength.QuadPart));
154 g_assert(((ULONG)size64)==size64);
155 FileOffset0.QuadPart=0;
157 errboolean=CcMapData(FileObject,&FileOffset0,size64,MAP_WAIT,&fileobject_cached->Bcb,&Buffer_trash);
158 g_assert(errboolean==TRUE);
163 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject);
166 * CcUninitializeCacheMap:
167 * @FileObject: File to close caching for.
168 * %NULL value is forbidden.
169 * @TruncateSize: Current file size if @FileObject was truncated to it in the meantime.
170 * @UninitializeCompleteEvent: Optional event to signal after physical write to disk.
171 * %NULL value is permitted.
173 * Close the cachine facilities from CcInitializeCacheMap().
174 * It is valid to pass @FileObject without being registered by CcInitializeCacheMap().
176 * FIXME; What to do with files with dirty blocks? Currently we fail assertion on them
177 * although I think W32 would drop such buffers (purge them - not flush them).
179 * Returns: %TRUE if the caching was closed successfuly.
180 * %FALSE if @FileObject wasn't registered by CcInitializeCacheMap().
182 BOOLEAN CcUninitializeCacheMap(IN PFILE_OBJECT FileObject,
183 IN PLARGE_INTEGER TruncateSize OPTIONAL,IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
187 g_return_val_if_fail(FileObject!=NULL,FALSE);
188 /* assert current size ==*TruncateSize if TruncateSize */
190 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,TruncateSize=0x%llX,UninitializeCompleteEvent=%p",
191 G_STRLOC,FileObject,(guint64)(!TruncateSize ? -1 : TruncateSize->QuadPart),UninitializeCompleteEvent);
194 /* Here it will check for any dirty blocks and it will fail g_assert() on them.
195 * I think it may happen for BCBs to exist dirty for 'FileObject' and they
196 * should be silently discarded if CcUninitializeCacheMap() but currently
197 * the assertion is left here as some debugging aid.
199 r=captive_cc_FileObject_delete(FileObject);
201 /* FIXME: should we do KePulseEvent? Are we allowed to signal from inside CcUninitializeCacheMap() ? */
202 if (UninitializeCompleteEvent)
204 &UninitializeCompleteEvent->Event, /* Event */
205 IO_NO_INCREMENT, /* Increment */
212 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
214 g_return_val_if_fail(PublicBcb!=NULL,FALSE);
215 g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
216 g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
217 g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
218 g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
224 /* position in file */
225 struct page_position {
226 FILE_OBJECT *FileObject;
227 LARGE_INTEGER FileOffset; /* always PAGE_SIZE aligned */
229 GList *privbcb_list; /* each mapped page has its one private_bcb */
232 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
233 static GHashTable *page_position_hash;
235 static gboolean validate_page_position(const struct page_position *pagepos)
238 struct shmid_ds shmid_ds;
240 g_return_val_if_fail(pagepos!=NULL,FALSE);
241 g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
242 g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
243 g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
244 /* 'pagepos->shmid' may be -1 */
245 /* 'pagepos->privbcb_list' may be empty */
246 /* either deleted or alive */
247 /* shmid exists only for >=2 mappings, it is simple mmap() for single mapping */
248 g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL || pagepos->privbcb_list->next==NULL),FALSE);
250 if (pagepos->shmid!=-1) {
251 errint=shmctl(pagepos->shmid,
253 &shmid_ds); /* buf */
254 g_return_val_if_fail(errint==0,FALSE);
256 g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
257 g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
258 g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
259 g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
260 /* 'shm_perm.mode' was seen with sticky bit 01000: */
261 g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
262 g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
263 /* Do not check 'shmid_ds.shm_cpid' and 'shmid_ds.shm_lpid' against getpid()
264 * as we may run as different PIDs in different threads synchronized by client/ .
266 g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
272 static guint page_position_hash_hash_func(const struct page_position *key)
274 g_return_val_if_fail(validate_page_position(key),0);
276 return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
279 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
281 g_return_val_if_fail(validate_page_position(a),FALSE);
282 g_return_val_if_fail(validate_page_position(b),FALSE);
284 return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
287 static void page_position_hash_key_destroy_func(struct page_position *key)
289 g_return_if_fail(validate_page_position(key));
290 g_assert(key->privbcb_list==NULL);
291 g_assert(key->shmid==-1);
296 static void page_position_hash_init(void)
298 if (page_position_hash)
300 page_position_hash=g_hash_table_new_full(
301 (GHashFunc)page_position_hash_hash_func, /* hash_func */
302 (GEqualFunc)page_position_hash_key_equal_func, /* key_equal_func */
303 (GDestroyNotify)page_position_hash_key_destroy_func, /* key_destroy_func */
304 NULL); /* value_destroy_func */
309 PUBLIC_BCB *PublicBcb; /* ->MappedLength, ->MappedFileOffset */
310 FILE_OBJECT *FileObject;
312 gboolean leave_func_pending;
313 /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
314 ULONG MappedLength; /* It is the real requested size; it is not PAGE_SIZE aligned. */
315 /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
316 LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
317 gpointer base; /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
319 LARGE_INTEGER lsn; gboolean lsn_valid;
323 /* map: (struct private_bcb *)privbcb -> (struct private_bcb *)privbcb */
324 static GTree *private_bcb_flush_tree;
326 static gint private_bcb_flush_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
331 g_return_val_if_fail(a!=NULL,0);
332 g_return_val_if_fail(validate_Bcb(a->PublicBcb),0);
333 g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MININT64); /* Forbid defined LSN as G_MININT64 */
334 g_return_val_if_fail(b!=NULL,0);
335 g_return_val_if_fail(validate_Bcb(b->PublicBcb),0);
336 g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MININT64); /* Forbid defined LSN as G_MININT64 */
338 if (a==b) /* LSN would be apparently the same in such case :-) */
341 a_lsn=(!a->lsn_valid ? (LONGLONG)G_MININT64 : a->lsn.QuadPart);
342 b_lsn=(!b->lsn_valid ? (LONGLONG)G_MININT64 : b->lsn.QuadPart);
344 /* Forbid the same LSNs if both defined */
345 g_assert(a_lsn==(LONGLONG)G_MININT64 || a_lsn!=b_lsn);
347 if ((r=(b_lsn>a_lsn)-(b_lsn<a_lsn)))
353 static void private_bcb_flush_tree_init(void)
355 if (private_bcb_flush_tree)
357 private_bcb_flush_tree=g_tree_new(
358 (GCompareFunc)private_bcb_flush_tree_key_compare_func); /* key_compare_func */
361 #define PRIVBCB_WANTS_FLUSH(privbcb) ((privbcb)->lsn_valid || ((privbcb)->dirty && (0 \
362 || ((privbcb)->ref_count==0) \
363 || ((privbcb)->ref_count==1 && (privbcb)->leave_func_pending))))
366 PRIVBCB_ITEM_NOP, /* for sanity checks */
368 PRIVBCB_ITEM_LSN_VALID,
369 PRIVBCB_ITEM_LEAVE_FUNC_PENDING,
370 PRIVBCB_ITEM_REF_COUNT,
373 static void privbcb_set(struct private_bcb *privbcb,enum privbcb_item item,gint value)
375 g_return_if_fail(privbcb!=NULL);
377 private_bcb_flush_tree_init();
379 g_assert((PRIVBCB_WANTS_FLUSH(privbcb) ? privbcb : NULL)==g_tree_lookup(private_bcb_flush_tree,privbcb));
381 case PRIVBCB_ITEM_NOP:
383 case PRIVBCB_ITEM_DIRTY:
384 g_assert(TRUE==value || FALSE==value);
385 privbcb->dirty=value;
387 case PRIVBCB_ITEM_LSN_VALID:
388 g_assert(TRUE==value || FALSE==value);
389 privbcb->lsn_valid=value;
391 case PRIVBCB_ITEM_LEAVE_FUNC_PENDING:
392 g_assert(TRUE==value || FALSE==value);
393 privbcb->leave_func_pending=value;
395 g_assert(privbcb->ref_count==1);
397 case PRIVBCB_ITEM_REF_COUNT:
398 /* Forbid reincarnation of 'leave_func_pending' privbcb.
399 * Allow some of its 'ref_count' handling to properly clean it up.
401 if (privbcb->leave_func_pending)
402 g_assert((privbcb->ref_count==1 && value==-1)
403 || (privbcb->ref_count==0 && value==+1));
404 g_assert(privbcb->ref_count>=0);
405 privbcb->ref_count+=value;
406 g_assert(privbcb->ref_count>=0);
408 default: g_assert_not_reached();
410 if (!PRIVBCB_WANTS_FLUSH(privbcb))
411 g_tree_remove(private_bcb_flush_tree,privbcb);
413 g_tree_insert(private_bcb_flush_tree,
415 privbcb); /* value */
416 g_assert((PRIVBCB_WANTS_FLUSH(privbcb) ? privbcb : NULL)==g_tree_lookup(private_bcb_flush_tree,privbcb));
420 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
421 static GHashTable *private_bcb_hash;
423 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
425 g_return_if_fail(validate_Bcb(key));
430 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
432 struct page_position pagepos_local;
436 gpointer base_aligned;
438 g_return_if_fail(value!=NULL);
439 /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
440 * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
442 g_return_if_fail(value->PublicBcb!=NULL);
443 g_return_if_fail(value->FileObject!=NULL);
444 g_return_if_fail(value->ref_count==0);
445 g_return_if_fail(value->MappedLength>0);
446 g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
447 g_return_if_fail(value->base!=NULL);
448 g_return_if_fail(value->dirty==FALSE);
450 page_position_hash_init();
452 base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
454 pagepos_local.FileObject=value->FileObject;
455 pagepos_local.privbcb_list=NULL;
456 pagepos_local.shmid=-1;
459 offset<value->MappedLength;
461 struct page_position *pagepos;
463 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
464 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
465 g_assert(validate_page_position(pagepos));
466 g_assert(pagepos->privbcb_list!=NULL);
467 if (pagepos->privbcb_list->next==NULL) { /* single mapping by mmap(), no shm */
468 g_assert(pagepos->shmid==-1);
469 errint=munmap(((char *)base_aligned)+offset,PAGE_SIZE);
472 else if (pagepos->privbcb_list->next->next==NULL) { /* mapping 2 -> 1, convert shm to mmap() */
473 struct private_bcb *privbcb_other;
474 gint64 privbcb_other_offset_relative;
475 gpointer mapped_other,errptr;
477 g_assert(pagepos->shmid!=-1);
478 if (value==pagepos->privbcb_list->data)
479 privbcb_other=pagepos->privbcb_list->next->data;
481 privbcb_other=pagepos->privbcb_list->data;
482 g_assert(value==pagepos->privbcb_list->next->data);
484 privbcb_other_offset_relative=pagepos->FileOffset.QuadPart-privbcb_other->MappedFileOffset.QuadPart;
485 /* privbcb_other_offset_relative may be negative up to -PAGE_SIZE
486 * as 'MappedFileOffset' and 'base' are not page-aligned.
488 mapped_other=privbcb_other->base+privbcb_other_offset_relative;
489 /* TODO:thread; shmdt()..mmap() window */
490 errint=shmdt(mapped_other);
493 mapped_other, /* start */
494 PAGE_SIZE, /* length */
495 PROT_READ|PROT_WRITE, /* prot */
496 MAP_PRIVATE|MAP_ANONYMOUS, /* flags */
497 -1, /* fd; ignored due to MAP_ANONYMOUS */
498 0); /* offset; ignored due to MAP_ANONYMOUS */
499 g_assert(errptr==mapped_other);
500 memcpy(mapped_other,((char *)base_aligned)+offset,PAGE_SIZE);
501 errint=shmdt(((char *)base_aligned)+offset); /* destroys shm */
503 /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
506 else { /* mappings (>=3) -> (>=2) */
507 g_assert(pagepos->shmid!=-1);
508 errint=shmdt(((char *)base_aligned)+offset);
512 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
513 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
514 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
516 if (pagepos->privbcb_list==NULL) { /* last mapping by mmap() removed */
517 g_assert(pagepos->shmid==-1);
518 errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
519 g_assert(errbool==TRUE);
521 else /* mapping is now >=1, either by mmap() or shm */
522 g_assert(validate_page_position(pagepos));
528 static void private_bcb_hash_init(void)
530 if (private_bcb_hash)
532 private_bcb_hash=g_hash_table_new_full(
533 g_direct_hash, /* hash_func */
534 g_direct_equal, /* key_equal_func */
535 (GDestroyNotify)private_bcb_hash_key_destroy_func, /* key_destroy_func */
536 (GDestroyNotify)private_bcb_hash_value_destroy_func); /* value_destroy_func */
540 static gboolean captive_cc_FileObject_delete_private_bcb_hash_foreach(
541 PUBLIC_BCB *PublicBcb, /* key */
542 struct private_bcb *privbcb, /* value */
543 FILE_OBJECT *FileObject) /* user_data */
546 if (privbcb->FileObject!=FileObject)
547 return FALSE; /* do not remove this 'privbcb' */
549 /* FIXME: Such 'privbcb' can get possibly reincarnated!
550 * We disallow even already-deleted entries during !FileObject (unmount).
552 if (privbcb->ref_count==1 && privbcb->leave_func_pending)
553 return FALSE; /* do not remove this 'privbcb' */
556 g_assert(privbcb->dirty==FALSE); /* it would be fatal */
558 /* Force 'ref_count' drop to 0. */
559 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-privbcb->ref_count);
561 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: WARNING: Deleting file with pending (fortunately non-dirty) BCBs"
562 "; FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
563 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
565 return TRUE; /* remove this 'privbcb' */
568 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
570 struct fileobject_cached *fileobject_cached;
573 /* 'FileObject' may be NULL to check/clear the whole cache. */
575 fileobject_cached_hash_init();
576 private_bcb_hash_init();
578 if (!FileObject || !(fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,FileObject)))
581 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
582 CcUnpinData(fileobject_cached->Bcb);
584 g_hash_table_remove(fileobject_cached_hash,FileObject);
587 g_hash_table_foreach_remove(
588 private_bcb_hash, /* hash_table */
589 (GHRFunc)captive_cc_FileObject_delete_private_bcb_hash_foreach, /* func */
590 FileObject); /* user_data */
596 static void captive_privbcb_flush(struct private_bcb *privbcb);
598 static void captive_cc_flush_private_bcb_hash_foreach(
599 PUBLIC_BCB *PublicBcb, /* key */
600 struct private_bcb *privbcb, /* value */
601 gboolean *flushedp) /* user_data */
603 g_return_if_fail(validate_Bcb(PublicBcb));
604 g_return_if_fail(privbcb!=NULL);
605 g_return_if_fail(PublicBcb==privbcb->PublicBcb);
607 if (!privbcb->dirty) /* OK */
611 captive_privbcb_flush(privbcb);
614 static void CcUnpinData_leave_func(struct private_bcb *privbcb_stop_at);
616 void captive_cc_flush(void)
620 fileobject_cached_hash_init();
621 private_bcb_hash_init();
624 /* Trace it by g_tree first to attempt to keep LSN ordering */
625 CcUnpinData_leave_func(NULL);
627 g_hash_table_foreach(
628 private_bcb_hash, /* hash_table */
629 (GHFunc)captive_cc_flush_private_bcb_hash_foreach, /* func */
630 &flushed); /* user_data */
634 /* We must not call: captive_cc_FileObject_delete(NULL);
635 * here as it would destroy all the Bcbs needed for the forthcoming
636 * IoShutdownRegisteredFileSystems().
641 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
645 IO_STATUS_BLOCK IoStatus;
648 g_return_val_if_fail(FileObject!=NULL,0);
649 g_return_val_if_fail(address!=0,0);
650 g_return_val_if_fail(length!=0,0);
651 g_return_val_if_fail(FileOffset!=NULL,0);
653 /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
654 * successfuly read the data. Workaround it - preclear (not postclear) the
655 * buffer and do not make any other assumptions about the data read.
657 memset(address,0,PAGE_SIZE); /* pre-clear the buffer */
658 Mdl=MmCreateMdl(NULL,address,PAGE_SIZE); /* FIXME: Deprecated in favor of IoAllocateMdl() */
660 MmBuildMdlForNonPagedPool(Mdl);
661 KeInitializeEvent(&Event,NotificationEvent,FALSE);
662 IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
663 err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
664 g_assert(NT_SUCCESS(err));
665 g_assert(NT_SUCCESS(IoStatus.Status));
666 /* It is not == as the file may be shorter than requested */
667 g_assert(IoStatus.Information<=length);
670 /* Forbidden, see the comment above about ext2fsd.sys.
671 * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
674 return IoStatus.Information; /* may be shorter than real! */
678 static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,
679 PUBLIC_BCB *Bcb_find)
681 struct page_position pagepos_local,*pagepos;
682 struct private_bcb *privbcb,*privbcb_listitem;
685 g_return_val_if_fail(FileObject!=NULL,NULL);
686 g_return_val_if_fail(FileOffset!=NULL,NULL);
687 /* 'Bcb_find' may be NULL */
689 page_position_hash_init();
691 pagepos_local.FileObject=FileObject;
692 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
693 pagepos_local.privbcb_list=NULL;
694 pagepos_local.shmid=-1;
695 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local)))
697 g_assert(validate_page_position(pagepos));
698 g_assert(pagepos->privbcb_list!=NULL);
701 for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
702 privbcb_listitem=privbcb_list->data;
704 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
705 && privbcb_listitem->MappedLength==Length
706 && (!Bcb_find || Bcb_find==privbcb_listitem->PublicBcb)) {
707 g_assert(privbcb==NULL); /* appropriate 'Bcb_find'-matching privbcb found twice */
708 privbcb=privbcb_listitem;
716 /* Sanity check 'privbcb': */
717 g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
725 * @FileObject: Initialized open #FileObject to map.
726 * %NULL value is forbidden.
727 * @FileOffset: The @FileObject file offset from where to map the region from.
728 * Negative value is forbidden.
729 * @Length: Requested length of the region to map from @FileObject.
730 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
731 * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
732 * %MAP_NO_READ currently ignored; we map the data always.
733 * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
734 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
735 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
736 * %NULL pointer is forbidden.
737 * @Buffer: Returns the mapped memory region start address.
738 * This address may not be %PAGE_SIZE aligned.
739 * %NULL pointer is forbidden.
741 * Maps the specified region of @FileObject to automatically chosen address space.
742 * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
744 * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
745 * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
746 * but W32 filesystems apparently do not conform to it. :-) If you do not take care of the
747 * modified data by some dirty-marking facility such data will be carelessly dropped without
748 * their commit to the disk.
750 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
752 * We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
753 * even if such mapping for such file already exists.
754 * We should probably create a new #Bcb for the same space,
755 * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
757 * Returns: %TRUE if the region was successfuly mapped.
758 * @Bcb with the initialized new memory region.
759 * @Buffer with the address of the exact byte specified by @FileOffset.
761 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
762 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
764 PUBLIC_BCB **PublicBcbp,*PublicBcb;
765 struct page_position pagepos_local,*pagepos;
766 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
767 gpointer base_aligned;
768 size_t offset,length_mapped_aligned;
771 struct private_bcb *privbcb;
772 gboolean after_eof=FALSE; /* Did we reached the end of file already? */
773 GPtrArray *read_array;
775 g_return_val_if_fail(FileObject!=NULL,FALSE);
776 g_return_val_if_fail(FileOffset!=NULL,FALSE);
777 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
778 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
779 g_return_val_if_fail(Flags&MAP_WAIT,FALSE); /* FIXME: on-demand loading not yet implemented */
780 g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE); /* unknown flags? */
781 g_return_val_if_fail(Bcb!=NULL,FALSE);
782 g_return_val_if_fail(Buffer!=NULL,FALSE);
784 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
785 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
787 g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
788 g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
789 #if 0 /* SectorSize can be really weird but we do not use it anyway */
790 /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
791 * 'SectorSize' may not yet be initialized during mount operation
792 * and 'FileObject->DeviceObject->Vpb' may exist in such case.
794 g_return_val_if_fail(0
795 || FileObject->DeviceObject->SectorSize==0 /* prevent division by 0 */
796 || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),
800 page_position_hash_init();
801 private_bcb_hash_init();
803 PublicBcbp=(PUBLIC_BCB **)Bcb;
804 /* extend 'FileOffset' and 'Length' to page boundaries */
805 FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
806 FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
807 length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
809 /* We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
810 * even if such mapping for such file already exists.
811 * We should probably create a new Bcb for the same space,
812 * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
814 if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL)))
815 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX already mapped by privbcb %p",
816 G_STRLOC,FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,privbcb);
818 /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
819 /* TODO: on-demand loading */
820 /* Although we do zeroed-page mapping here we just reserve the linear
825 PAGE_SIZE+length_mapped_aligned+PAGE_SIZE, /* length; leading and trailing boundary check pages */
826 PROT_READ|PROT_WRITE, /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
827 MAP_PRIVATE|MAP_ANONYMOUS, /* flags */
828 -1, /* fd; ignored due to MAP_ANONYMOUS */
829 0); /* offset; ignored due to MAP_ANONYMOUS */
830 g_assert(base_aligned!=NULL);
832 base_aligned+=PAGE_SIZE;
833 errint=munmap(base_aligned-PAGE_SIZE,PAGE_SIZE); /* unmap leading boundary check page */
835 errint=munmap(base_aligned+length_mapped_aligned,PAGE_SIZE); /* unmap trailing boundary check page */
838 /* Create 'PublicBcb'; referenced by 'privbcb'. */
839 captive_new(PublicBcb);
840 PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
841 PublicBcb->NodeByteSize=sizeof(*PublicBcb); /* we have no extensions there */
842 PublicBcb->MappedLength=Length;
843 PublicBcb->MappedFileOffset=*FileOffset;
845 /* Create 'privbcb'; referenced by created 'pagepos'es. */
846 captive_new(privbcb);
847 privbcb->PublicBcb=PublicBcb;
848 privbcb->FileObject=FileObject;
849 privbcb->ref_count=1;
850 privbcb->leave_func_pending=FALSE;
851 privbcb->MappedLength=PublicBcb->MappedLength;
852 privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
853 privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
854 privbcb->dirty=FALSE;
855 privbcb->lsn_valid=FALSE;
856 g_hash_table_insert(private_bcb_hash,
858 privbcb); /* value */
860 privbcb_set(privbcb,PRIVBCB_ITEM_NOP,0); /* just for the assertions */
862 /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
863 * below as captive_Cc_IoPageRead() has very big consequences as it calls
864 * the filesystem code and we may get reentrancy.
865 * Therefore we store all missing page read requests to 'read_array' and we
866 * fill them when all the memory structures are in consistent state.
867 * We can also coalescence the requests more easily this way.
869 read_array=g_ptr_array_new();
871 pagepos_local.FileObject=FileObject;
872 pagepos_local.shmid=-1;
873 pagepos_local.privbcb_list=NULL;
876 offset<length_mapped_aligned;
880 pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
881 pageaddr=(gpointer)(((char *)base_aligned)+offset);
882 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
883 captive_new(pagepos);
884 *pagepos=pagepos_local;
886 pagepos->privbcb_list=NULL;
887 g_ptr_array_add(read_array,pagepos); /* enlist this 'pagepos' as todo read item */
888 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read of offset %llu to new pagepos %p of pure mmap() to address %p",
889 G_STRLOC,(unsigned long long)offset,pagepos,pageaddr);
891 else if (pagepos->privbcb_list->next==NULL) { /* exactly one item (no IPC shm yet) */
892 g_assert(pagepos->shmid==-1);
893 if (-1==(pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600)))
894 g_error("%s: Failed shmget(2), you may be out of maximum system IPC shared pages",G_STRLOC);
895 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: created shmid %d out of mmap() for offset %llu to existing pagepos %p to address %p",
896 G_STRLOC,pagepos->shmid,(unsigned long long)offset,pagepos,pageaddr);
898 else { /* already >=2 mappings, already IPC shared mem */
899 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: already shared shmid of offset %llu by existing pagepos %p shmid %d to address %p",
900 G_STRLOC,(unsigned long long)offset,pagepos,pagepos->shmid,pageaddr);
901 g_assert(pagepos->shmid!=-1);
902 g_assert(pagepos->privbcb_list!=NULL);
904 if (pagepos->privbcb_list) { /* Not the first one - some shm in use now? */
905 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
906 * mmap(2) is still needed to get linear block of memory assignment.
908 /* TODO:thread; munmap()..shmat() window */
909 errint=munmap(pageaddr,PAGE_SIZE);
911 errptr=shmat(pagepos->shmid,
912 pageaddr, /* shmaddr */
913 0); /* shmflg; !SHM_RDONLY==r/w */
914 g_assert(errptr==pageaddr);
917 g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
918 pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb); /* order not important */
919 g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
920 if (pagepos->privbcb_list->next==NULL) { /* exactly one item (we just added it now) */
922 g_assert(pagepos->shmid==-1);
923 g_hash_table_insert(page_position_hash,
925 pagepos); /* value */
927 else if (pagepos->privbcb_list->next->next==NULL) { /* second mapping - new shm */
928 struct private_bcb *privbcb_orig;
929 gint64 privbcb_orig_offset_relative;
930 gpointer mapped_orig;
932 g_assert(pagepos->shmid!=-1);
933 errint=shmctl(pagepos->shmid,
937 g_assert(pagepos->privbcb_list->data==privbcb); /* our privbcb should be the first */
938 g_assert(pagepos->privbcb_list->next!=NULL); /* we have exactly two items in the list */
939 g_assert(pagepos->privbcb_list->next->next==NULL); /* we have exactly two items in the list */
940 privbcb_orig=pagepos->privbcb_list->next->data;
941 privbcb_orig_offset_relative=pagepos->FileOffset.QuadPart-privbcb_orig->MappedFileOffset.QuadPart;
942 /* privbcb_orig_offset_relative may be negative up to -PAGE_SIZE
943 * as 'MappedFileOffset' and 'base' are not page-aligned.
945 mapped_orig=privbcb_orig->base+privbcb_orig_offset_relative;
946 memcpy(pageaddr,mapped_orig,PAGE_SIZE);
947 /* TODO:thread; munmap()..shmat() window */
948 errint=munmap(mapped_orig,PAGE_SIZE);
950 errptr=shmat(pagepos->shmid,
951 mapped_orig, /* shmaddr */
952 0); /* shmflg; !SHM_RDONLY==r/w */
953 g_assert(errptr==mapped_orig);
955 g_assert(validate_page_position(pagepos));
958 /* Fill in the missing pages content todo-list stored in 'read_array': */
959 while (read_array->len && (pagepos=g_ptr_array_remove_index(read_array,
963 struct pagepos *pagepos_next;
964 gpointer pageaddr,pageaddr_next;
966 pageaddr=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
968 /* Coalescence of the requests */
969 while (read_array->len) {
970 pagepos_next=g_ptr_array_index(read_array,
972 pageaddr_next=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
973 if (pageaddr_next!=((char *)pageaddr)+read_size)
975 read_size+=PAGE_SIZE;
976 g_assert(pagepos_next==g_ptr_array_remove_index(read_array,
979 /* Read the range content: */
980 got=captive_Cc_IoPageRead(FileObject,pageaddr,read_size,&pagepos->FileOffset);
984 g_assert(got<=PAGE_SIZE);
985 after_eof=(got<PAGE_SIZE);
987 g_ptr_array_free(read_array,
988 TRUE); /* free_seg; free the array of gpointer(==struct pagepos *) items */
990 /* offset _into_ page, may not be PAGE_SIZE aligned: */
991 *Buffer=privbcb->base;
992 *PublicBcbp=PublicBcb;
993 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
994 "%s: result: privbcb=%p,privbcb->base=%p,privbcb->base+privbcb->MappedLength=%p,privbcb->MappedLength=0x%lX",
995 G_STRLOC,privbcb,privbcb->base,((char *)privbcb->base)+privbcb->MappedLength,(unsigned long)privbcb->MappedLength);
996 g_assert(validate_Bcb(PublicBcb)==TRUE);
997 if (!FileObject->SectionObjectPointers->SharedCacheMap)
998 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
1000 /* FIXME: (NOTE*1) Weird but it appears as fastfat.sys during make_directory()
1001 * can pass us an already cached 'FileObject' (with different offset/size).
1002 * We must not CcUnpinData() it as its BCB is still referenced (CcUnpinData()ed) by fastfat.sys.
1004 FileObject->SectionObjectPointers->SharedCacheMap=NULL;
1006 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
1014 * @FileObject: Initialized open #FileObject to map.
1015 * %NULL value is forbidden.
1016 * @MappedFileOffset: The @FileObject file offset from where to map the region from.
1017 * Negative value is forbidden.
1018 * @MappedLength: Requested length of the region to map from @FileObject.
1019 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1020 * @Wait: Whether disk waiting is permitted for this function.
1021 * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
1022 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1023 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1024 * %PIN_NO_READ currently ignored; we map the data always.
1025 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1026 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1027 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1028 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1029 * %NULL pointer is forbidden.
1030 * @Buffer: Returns the mapped memory region start address.
1031 * This address may not be %PAGE_SIZE aligned.
1032 * %NULL pointer is forbidden.
1034 * This function will allow you to modify the data mapped by CcMapData().
1035 * libcaptive does not differentiate this function with CcMapData().
1037 * NEVER re-read any memory from FileObject here!
1038 * at least fastfat.sys directory create relies on the fact of CcPinRead()
1039 * with already modified buffers to be left intact.
1041 * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
1042 * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
1044 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
1046 * Returns: %TRUE if the region was successfuly mapped.
1047 * @Bcb with the initialized new memory region.
1048 * @Buffer with the address of the exact byte specified by @FileOffset.
1050 BOOLEAN CcPinMappedData
1051 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
1053 struct private_bcb *privbcb;
1055 g_return_val_if_fail(FileObject!=NULL,FALSE);
1056 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1057 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1058 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
1059 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
1060 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
1061 g_return_val_if_fail(Bcb!=NULL,FALSE);
1063 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
1064 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
1066 privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL);
1067 if (!privbcb && (Flags & PIN_IF_BCB)) /* BCB does not exist */
1069 /* Appropriate privbcb not found.
1070 * This IMO should not happen and it is a bug in the client.
1071 * Unfortuantely fastfat.sys FatLocateVolumeLabel() will CcPinMappedData()
1072 * the volume label dirent without any previous CcMapData() or CcPinRead() !
1086 /* NEVER re-read any memory from FileObject here! */
1088 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1090 /* Memory already mapped by CcMapData(). */
1091 *Bcb=privbcb->PublicBcb;
1098 * @FileObject: Initialized open #FileObject to map.
1099 * %NULL value is forbidden.
1100 * @FileOffset: The @FileObject file offset from where to map the region from.
1101 * Negative value is forbidden.
1102 * @Length: Requested length of the region to map from @FileObject.
1103 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1104 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1105 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1106 * %PIN_NO_READ currently ignored; we map the data always.
1107 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1108 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1109 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1110 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1111 * %NULL pointer is forbidden.
1112 * @Buffer: Returns the mapped memory region start address.
1113 * This address may not be %PAGE_SIZE aligned.
1114 * %NULL pointer is forbidden.
1116 * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
1117 * See these two functions for the details. It has a difference to subsequent
1118 * calling of CcMapData() and CcPinMappedData() instead as this call counts
1119 * only as one function for a corresponding CcUnpinData() call.
1121 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
1123 * Returns: %TRUE if the region was successfuly mapped.
1124 * @Bcb with the initialized new memory region.
1125 * @Buffer with the address of the exact byte specified by @FileOffset.
1127 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
1128 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
1130 PVOID Bcb_CcPinMappedData;
1132 gboolean count_CcMapData;
1133 struct private_bcb *privbcb;
1135 g_return_val_if_fail(FileObject!=NULL,FALSE);
1136 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1137 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1138 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
1139 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
1140 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
1141 g_return_val_if_fail(Bcb!=NULL,FALSE);
1142 g_return_val_if_fail(Buffer!=NULL,FALSE);
1144 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
1145 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
1147 if (!(Flags&PIN_IF_BCB)) {
1148 errbool=CcMapData(FileObject,FileOffset,Length,
1150 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
1152 g_return_val_if_fail(errbool==TRUE,FALSE);
1153 count_CcMapData=TRUE;
1156 count_CcMapData=FALSE;
1158 errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
1159 if (!(Flags&PIN_IF_BCB)) {
1160 g_return_val_if_fail(errbool==TRUE,FALSE);
1161 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
1164 if (errbool==FALSE) /* FALSE permitted; We may fail if Bcb does not exist yet. */
1166 *Bcb=Bcb_CcPinMappedData;
1169 privbcb=captive_privbcb_find(FileObject,FileOffset,Length,*Bcb);
1170 g_assert(privbcb!=NULL);
1171 g_assert(privbcb->ref_count>=1);
1172 g_assert(privbcb->PublicBcb==*Bcb);
1173 if (count_CcMapData) {
1174 /* CcPinRead() must always reference-count only by 1 despite any sub-called functions! */
1175 g_assert(privbcb->ref_count>=2);
1176 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
1183 static void logging_notify_privbcb_flush(struct private_bcb *privbcb);
1185 static void captive_privbcb_flush(struct private_bcb *privbcb)
1189 IO_STATUS_BLOCK IoStatus;
1191 gpointer base_sectoraligned;
1192 gsize length_sectoraligned;
1193 LARGE_INTEGER FileOffset_sectoraligned;
1195 struct fileobject_cached *fileobject_cached;
1196 IRP *saved_TopLevelIrp;
1197 static gint64 last_written_lsn=G_MININT64;
1199 g_assert(privbcb->ref_count>0);
1201 if (!privbcb->dirty)
1204 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1205 logging_notify_privbcb_flush(privbcb);
1206 g_assert(privbcb->ref_count>=2);
1207 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
1209 if (privbcb->lsn_valid) {
1210 if (!(last_written_lsn<privbcb->lsn.QuadPart))
1211 g_error("%s: last_written_lsn=%" G_GINT64_FORMAT " !< privbcb->lsn=%" G_GINT64_FORMAT,G_STRLOC,
1212 last_written_lsn,(gint64)privbcb->lsn.QuadPart);
1213 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LSN write: last_written_lsn was %" G_GINT64_FORMAT ", is %" G_GINT64_FORMAT,
1214 G_STRLOC,last_written_lsn,(gint64)privbcb->lsn.QuadPart);
1215 last_written_lsn=privbcb->lsn.QuadPart;
1218 /* Prepare NULL TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
1220 saved_TopLevelIrp=IoGetTopLevelIrp();
1221 IoSetTopLevelIrp(NULL);
1223 fileobject_cached_hash_init();
1224 if ((fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,privbcb->FileObject)))
1225 (*fileobject_cached->CallBacks.AcquireForLazyWrite)(
1226 fileobject_cached->LazyWriterContext, /* Context */
1229 g_assert(privbcb->FileObject->DeviceObject!=NULL);
1231 /* We can get 0=='privbcb->FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
1232 * FIXME: We are unable to find the correct sectorsize for a filesystem as
1233 * even the 'CommonFcb' below contains invalid information.
1234 * As we need to have 'sectorsize' <=filesystem_blocksize at least for ext2fsd.sys
1235 * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
1236 * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
1238 sectorsize=PAGE_SIZE;
1240 sectorsize=privbcb->FileObject->DeviceObject->SectorSize;
1241 if (privbcb->FileObject->FsContext) {
1242 REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileObject->FsContext;
1244 /* FIXME: Check CommonFcb->Type */
1245 /* 'AllocationSize' can be less than 'sectorsize' if the total file length is smaller.
1246 * Observed with ext2fsd.sys volume-file.
1248 if (sectorsize<CommonFcb->AllocationSize.QuadPart)
1249 sectorsize=CommonFcb->AllocationSize.QuadPart;
1253 /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
1254 g_assert(sectorsize>0);
1255 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,sectorsize));
1256 /* We align here directly the 'privbcb->base' which is not correct.
1257 * We should rather aligned according to 'privbcb->MappedOffset' but
1258 * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
1259 * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
1261 g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
1262 ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,sectorsize));
1263 base_sectoraligned =CAPTIVE_ROUND_DOWN(privbcb->base,sectorsize);
1264 length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,sectorsize)
1265 -((char *)base_sectoraligned);
1266 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,sectorsize));
1267 FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,sectorsize);
1269 Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
1270 g_assert(Mdl!=NULL);
1271 MmBuildMdlForNonPagedPool(Mdl);
1273 KeInitializeEvent(&Event,NotificationEvent,FALSE);
1275 /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
1276 err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
1277 g_assert(NT_SUCCESS(err));
1278 g_assert(NT_SUCCESS(IoStatus.Status));
1279 /* We should write at least the unaligned mapped data although we
1280 * do not need to successfuly write the whole aligned amount.
1281 * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
1282 * during FAT write by fastfat.sys.
1284 g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
1285 +privbcb->MappedLength);
1286 g_assert(IoStatus.Information<=length_sectoraligned);
1288 if (fileobject_cached)
1289 (*fileobject_cached->CallBacks.ReleaseFromLazyWrite)(
1290 fileobject_cached->LazyWriterContext); /* Context */
1292 /* Restore TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
1294 g_assert(NULL==IoGetTopLevelIrp());
1295 IoSetTopLevelIrp(saved_TopLevelIrp);
1297 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
1298 "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
1299 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
1300 base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
1301 (gulong)IoStatus.Information);
1303 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE);
1307 static gboolean CcUnpinData_leave_func_foreach_get_first(struct private_bcb *key,struct private_bcb *value,
1308 struct private_bcb **privbcbp /* data */)
1310 g_return_val_if_fail(key!=NULL,TRUE); /* meaning: stop the traversal */
1311 g_return_val_if_fail(value!=NULL,TRUE); /* meaning: stop the traversal */
1312 g_return_val_if_fail(key==value,TRUE); /* meaning: stop the traversal */
1313 g_return_val_if_fail(validate_Bcb(key->PublicBcb),TRUE); /* meaning: stop the traversal */
1314 g_return_val_if_fail(privbcbp!=NULL,TRUE); /* meaning: stop the traversal */
1315 g_return_val_if_fail(*privbcbp==NULL,TRUE); /* meaning: stop the traversal */
1319 return TRUE; /* stop the traversal */
1322 static void CcUnpinData_leave_func(struct private_bcb *privbcb_stop_at) /* NULL to traverse the whole tree */
1324 struct private_bcb *privbcb;
1327 private_bcb_flush_tree_init();
1331 g_tree_foreach(private_bcb_flush_tree,
1332 (GTraverseFunc)CcUnpinData_leave_func_foreach_get_first, /* func */
1333 &privbcb); /* user_data */
1334 g_assert((!privbcb)==!g_tree_nnodes(private_bcb_flush_tree));
1338 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1339 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
1340 ",privbcb->leave_func_pending=%d",G_STRLOC,
1341 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
1342 (int)privbcb->leave_func_pending);
1344 /* Do not stop at not yet modified buffers if not privbcb_stop_at==NULL,
1345 * privbcb_stop_at==NULL is used to flush the whole cache.
1347 if (privbcb_stop_at && !privbcb->leave_func_pending) {
1348 g_assert(privbcb->lsn_valid); /* cannout skip LSN-valid BCB */
1352 g_assert(!privbcb_stop_at || privbcb->leave_func_pending==TRUE);
1353 privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
1354 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
1356 /* Do not flush modified buffers if not privbcb_stop_at==NULL,
1357 * privbcb_stop_at==NULL is used to flush the whole cache.
1359 if (privbcb_stop_at && privbcb->ref_count) {
1360 if (privbcb->lsn_valid) /* cannout skip LSN-valid BCB */
1365 /* We were called as CcUnpinData()->captive_privbcb_flush()->CcUnpinData()->CcUnpinData_leave_func()
1367 if (privbcb->dirty) {
1368 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1369 captive_privbcb_flush(privbcb);
1373 /* Clear it out of 'private_bcb_flush_tree' during global buffers flush. */
1374 if (!privbcb_stop_at) {
1375 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-privbcb->ref_count);
1376 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
1379 g_assert(privbcb->leave_func_pending==FALSE);
1380 g_assert(privbcb->ref_count==0);
1381 g_assert(NULL==g_tree_lookup(private_bcb_flush_tree,privbcb));
1383 errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
1384 g_assert(errbool==TRUE);
1385 } while (!privbcb_stop_at || privbcb!=privbcb_stop_at);
1390 * @Bcb: Initialized #PUBLIC_BCB structure.
1391 * %NULL value is forbidden.
1393 * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
1395 VOID CcUnpinData(IN PVOID Bcb)
1397 PUBLIC_BCB *PublicBcb;
1398 struct private_bcb *privbcb;
1400 g_return_if_fail(validate_Bcb(Bcb));
1402 private_bcb_hash_init();
1404 PublicBcb=(PUBLIC_BCB *)Bcb;
1405 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1406 g_return_if_fail(privbcb!=NULL);
1408 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
1409 /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
1411 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1412 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
1413 ",privbcb->leave_func_pending=%d",G_STRLOC,
1414 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
1415 (int)privbcb->leave_func_pending);
1417 g_assert(privbcb->ref_count>0);
1418 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
1419 if (privbcb->ref_count>0)
1421 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1); /* it will get decreased again in CcUnpinData_leave_func() */
1423 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
1424 /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
1425 if (privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb)
1426 privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
1428 /* Caboom: lfs (log file system) of ntfs.sys-NT5.1sp1 will do:
1429 * CcPinRead(); CcUnpinData(); access Buffer (wanna-crash);
1430 * Therefore we must postpone the buffer unmapping to some idle function...
1431 * I expect it a bug in ntfs.sys.
1433 if (!privbcb->leave_func_pending) {
1434 privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,TRUE);
1435 captive_leave_register(
1436 (captive_leave_func)CcUnpinData_leave_func, /* func */
1437 privbcb); /* data; privbcb_stop_at */
1440 if (captive_cc_unmounting)
1445 VOID CcRepinBcb(IN PVOID Bcb)
1447 PUBLIC_BCB *PublicBcb;
1448 struct private_bcb *privbcb;
1450 g_return_if_fail(validate_Bcb(Bcb));
1452 private_bcb_hash_init();
1454 PublicBcb=(PUBLIC_BCB *)Bcb;
1455 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1456 g_return_if_fail(privbcb!=NULL);
1458 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
1459 Bcb,privbcb->FileObject);
1461 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1465 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
1467 PUBLIC_BCB *PublicBcb;
1468 struct private_bcb *privbcb;
1470 g_return_if_fail(validate_Bcb(Bcb));
1471 g_return_if_fail(IoStatus!=NULL);
1473 private_bcb_hash_init();
1475 PublicBcb=(PUBLIC_BCB *)Bcb;
1476 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1477 g_return_if_fail(privbcb!=NULL);
1479 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
1480 Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
1482 /* FIXME: Should we really flush the whole 'Bcb'?
1483 * Or maybe just some writes between CcRepinBcb(Bcb) and now? Who knows?
1486 captive_privbcb_flush(privbcb);
1488 IoStatus->Status=STATUS_SUCCESS;
1489 IoStatus->Information=privbcb->MappedLength;
1495 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
1497 PUBLIC_BCB *PublicBcb;
1498 struct private_bcb *privbcb;
1500 g_return_if_fail(validate_Bcb(Bcb));
1502 private_bcb_hash_init();
1504 PublicBcb=(PUBLIC_BCB *)Bcb;
1505 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1506 g_return_if_fail(privbcb!=NULL);
1508 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
1509 Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
1511 /* 'privbcb->ref_count' not to be increased by this function. */
1515 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,TRUE);
1518 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,TRUE);
1522 struct CcSetFileSizes_param {
1523 PFILE_OBJECT FileObject;
1524 PCC_FILE_SIZES FileSizes;
1527 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
1528 static void CcSetFileSizes_private_bcb_hash_foreach(
1529 PUBLIC_BCB *PublicBcb, /* key */
1530 struct private_bcb *privbcb, /* value */
1531 struct CcSetFileSizes_param *CcSetFileSizes_param) /* user_data */
1533 g_return_if_fail(PublicBcb!=NULL);
1534 g_return_if_fail(privbcb!=NULL);
1535 g_return_if_fail(CcSetFileSizes_param!=NULL);
1537 if (privbcb->FileObject!=CcSetFileSizes_param->FileObject)
1540 /* size changes behind our cached range? */
1542 && CcSetFileSizes_param->FileSizes->AllocationSize .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
1543 && CcSetFileSizes_param->FileSizes->FileSize .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
1544 && CcSetFileSizes_param->FileSizes->ValidDataLength.QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
1547 /* FIXME: check BCB && 'struct page_position' invalidities */
1548 g_assert_not_reached(); /* NOT IMPLEMENTED YET */
1553 * @FileObject: Initialized open #FileObject to update file sizes of.
1554 * %NULL value is forbidden.
1555 * @FileSizes: New file sizes to update cache to.
1556 * %NULL value is forbidden.
1558 * Update cache properties after file sizes were updated.
1559 * Probably only the exceeding pages need to be unmapped and BCBs updated
1560 * if FileSizes->AllocationSize gets shrunk.
1562 * FIXME: Currently a NOP with no effect by libcaptive.
1564 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
1566 struct CcSetFileSizes_param CcSetFileSizes_param;
1568 g_return_if_fail(FileObject!=NULL);
1569 g_return_if_fail(FileSizes!=NULL);
1571 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
1572 "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
1573 FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
1574 (guint64)FileSizes->ValidDataLength.QuadPart);
1576 CcSetFileSizes_param.FileObject=FileObject;
1577 CcSetFileSizes_param.FileSizes=FileSizes;
1578 g_hash_table_foreach(
1579 private_bcb_hash, /* hash_table */
1580 (GHFunc)CcSetFileSizes_private_bcb_hash_foreach, /* func */
1581 &CcSetFileSizes_param); /* user_data */
1586 * CcPurgeCacheSection:
1587 * @SectionObjectPointer: Pointer specifying file to purge;
1588 * %NULL value is forbidden.
1589 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1590 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %TRUE return code in such case.
1591 * @FileOffset: Starting offset of the ranger to purge.
1592 * %NULL pointer is permitted and it means to purge the whole whole.
1593 * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
1594 * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
1595 * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
1597 * Drop any caching for shrunken file which is not being deleted.
1598 * libcaptive will no longer consider such #BCB as dirty.
1600 * Undocumented: It is required during %FSCTL_LOCK_VOLUME by ntfs.sys of NT-5.1sp1
1601 * to return %TRUE value if #SharedCacheMap value is %NULL.
1603 * Returns: %TRUE if the range was purged successfuly.
1605 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1606 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
1608 PUBLIC_BCB *PublicBcb;
1609 struct private_bcb *privbcb;
1611 g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
1612 if (SectionObjectPointer->SharedCacheMap==NULL)
1613 return TRUE; /* nothing to purge; never return FALSE for ntfs.sys of NT-5.1sp1! */
1614 g_return_val_if_fail(FileOffset==NULL,FALSE); /* NOT IMPLEMENTED YET */
1616 PublicBcb=SectionObjectPointer->SharedCacheMap;
1617 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1619 private_bcb_hash_init();
1621 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1622 g_return_val_if_fail(privbcb!=NULL,FALSE);
1624 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1625 "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
1626 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1627 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
1629 g_assert_not_reached();
1631 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE); /* purge it */
1639 * @FileObject: Initialized open #FileObject to map.
1640 * %NULL value is forbidden.
1641 * @FileOffset: The @FileObject file offset from where to map the region from.
1642 * Negative value is forbidden.
1643 * @Length: Requested length of the region to map from @FileObject.
1644 * Value %0 is permitted (no effect of this function call).
1645 * @Wait: Whether disk waiting is permitted for this function.
1646 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1647 * @Buffer: Address of memory region with already allocated memory of size @Length.
1648 * This address may not be %PAGE_SIZE aligned.
1649 * %NULL pointer is forbidden.
1650 * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1651 * %NULL pointer is forbidden.
1653 * Reads the specified region of @FileObject to the given @Buffer.
1654 * No on-demand loading is in effect.
1656 * Returns: %TRUE if the region was successfuly filled with @Length bytes.
1657 * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
1658 * @IoStatus.Information initialized by @Length if successful.
1660 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
1661 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
1667 g_return_val_if_fail(FileObject!=NULL,FALSE);
1668 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1669 g_return_val_if_fail(Buffer!=NULL,FALSE);
1670 g_return_val_if_fail(IoStatus!=NULL,FALSE);
1672 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1673 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1675 IoStatus->Status=STATUS_UNSUCCESSFUL;
1676 IoStatus->Information=0;
1680 FileObject, /* FileObject */
1681 FileOffset, /* FileOffset */
1682 Length, /* Length */
1684 &MappedBcb, /* Bcb */
1685 &MappedBuffer); /* Buffer */
1686 g_return_val_if_fail(errbool==TRUE,FALSE);
1688 memcpy(Buffer,MappedBuffer,Length);
1690 CcUnpinData(MappedBcb); /* no error code */
1693 IoStatus->Status=STATUS_SUCCESS;
1694 IoStatus->Information=Length;
1702 * @FileObject: Initialized open #FileObject to map.
1703 * %NULL value is forbidden.
1704 * @FileOffset: The @FileObject file offset from where to map the region from.
1705 * Negative value is forbidden.
1706 * @Length: Requested length of the region to map from @FileObject.
1707 * Value %0 is permitted (no effect of this function call).
1708 * @Wait: Whether disk waiting is permitted for this function.
1709 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1710 * @Buffer: Address of memory region with already allocated memory of size @Length.
1711 * This address may not be %PAGE_SIZE aligned.
1712 * %NULL pointer is forbidden.
1714 * Writes the specified region of the given @Buffer to @FileObject.
1716 * Returns: %TRUE if the region was successfuly written with @Length bytes.
1718 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
1719 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
1725 g_return_val_if_fail(FileObject!=NULL,FALSE);
1726 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1727 g_return_val_if_fail(Buffer!=NULL,FALSE);
1729 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1730 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1734 FileObject, /* FileObject */
1735 FileOffset, /* FileOffset */
1736 Length, /* Length */
1738 &MappedBcb, /* Bcb */
1739 &MappedBuffer); /* Buffer */
1740 g_return_val_if_fail(errbool==TRUE,FALSE);
1742 memcpy(MappedBuffer,Buffer,Length);
1744 CcSetDirtyPinnedData(
1745 MappedBcb, /* Bcb */
1747 CcUnpinData(MappedBcb); /* no error code */
1756 * @FileObject: Initialized open #FileObject to map.
1757 * %NULL value is forbidden.
1758 * @BytesToWrite: Amount of data to be asked whether it will be accepted.
1759 * Value %0 is permitted.
1760 * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
1761 * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
1763 * Asks cache manager if it would currently accept write request to @FileObject
1764 * of @BytesToWrite bytes with @Wait condition.
1765 * libcaptive will always accept any writes. This function is a NOP.
1767 * Returns: libcaptive always returns %TRUE.
1769 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
1771 g_return_val_if_fail(FileObject!=NULL,FALSE);
1773 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
1774 FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
1781 * CcSetReadAheadGranularity:
1782 * @FileObject: Initialized open #FileObject to map.
1783 * %NULL value is forbidden.
1784 * @Granularity: Suggested size of the cache element.
1785 * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
1787 * libcaptive does not implement any caching and therefore this function
1790 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
1792 g_return_if_fail(FileObject!=NULL);
1793 g_return_if_fail(Granularity>=PAGE_SIZE);
1794 g_return_if_fail((Granularity&(Granularity-1))==0); /* Power of two */
1796 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
1797 FileObject,(gulong)Granularity);
1799 /* NOP; no caching by libcaptive */
1805 * @SectionObjectPointer: Pointer specifying file to flush;
1806 * %NULL value is forbidden.
1807 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1808 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
1809 * @FileOffset: Optional starting point of the range to flush.
1810 * %NULL value is permitted.
1811 * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
1812 * @IoStatus: Optionally returns the resulting operation status.
1813 * #Information field will contain the number of bytes flushed.
1814 * %NULL value is permitted.
1816 * Flushes out any pending dirty data in cache manager BCB mapping.
1817 * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
1819 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1820 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
1822 PUBLIC_BCB *PublicBcb;
1823 struct private_bcb *privbcb;
1824 IO_STATUS_BLOCK IoStatus_CcUnpinRepinnedBcb_local;
1826 g_return_if_fail(SectionObjectPointer!=NULL);
1828 if (SectionObjectPointer->SharedCacheMap==NULL) {
1831 IoStatus->Status=STATUS_SUCCESS;
1832 IoStatus->Information=0;
1837 PublicBcb=SectionObjectPointer->SharedCacheMap;
1838 g_return_if_fail(validate_Bcb(PublicBcb));
1840 private_bcb_hash_init();
1842 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1843 g_return_if_fail(privbcb!=NULL);
1845 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1846 "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
1847 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1848 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
1851 if (FileOffset->QuadPart >=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
1853 if (FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart)
1857 /* We may find some 'privbcb' being already sheduled to be destroyed.
1858 * We need to reference it to not to loose it in the middle of our flush operation.
1860 CcRepinBcb(PublicBcb);
1862 /* FIXME: Flush just FileOffset..FileOfset+Length part */
1863 captive_privbcb_flush(privbcb);
1866 PublicBcb, /* Bcb */
1867 TRUE, /* WriteThrough; ignored by libcaptive */
1868 &IoStatus_CcUnpinRepinnedBcb_local); /* IoStatus; ignored here */
1871 IoStatus->Status=STATUS_SUCCESS;
1872 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
1877 BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
1879 SECTION_OBJECT_POINTERS *SectionObjectPointers_orig;
1884 g_return_val_if_fail(FileObject!=NULL,FALSE);
1885 g_return_val_if_fail(StartOffset!=NULL,FALSE);
1886 g_return_val_if_fail(EndOffset!=NULL,FALSE);
1887 g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
1888 g_return_val_if_fail((EndOffset->QuadPart-StartOffset->QuadPart)
1889 ==(ULONG)(EndOffset->QuadPart-StartOffset->QuadPart),FALSE);
1891 SectionObjectPointers_orig=FileObject->SectionObjectPointers;
1893 errboolean=CcPreparePinWrite(
1894 FileObject, /* FileObject */
1895 StartOffset, /* FileOffset */
1896 EndOffset->QuadPart-StartOffset->QuadPart, /* Length */
1898 PIN_WAIT|PIN_NO_READ, /* Flags */
1900 &Buffer); /* Buffer */
1901 g_assert(errboolean==TRUE);
1905 FileObject->SectionObjectPointers=SectionObjectPointers_orig;
1911 /* map (PVOID LogHandle) -> (GList (of FILE_OBJECT *) *FileObject_list) */
1912 static GHashTable *log_handle_hash;
1914 static void log_handle_hash_init(void)
1916 if (log_handle_hash)
1918 log_handle_hash=g_hash_table_new(
1919 g_direct_hash, /* hash_func */
1920 g_direct_equal); /* key_equal_func */
1923 /* map (FILE_OBJECT *FileObject) -> (struct FileObject_logging *) */
1924 struct FileObject_logging {
1926 PFLUSH_TO_LSN FlushToLsnRoutine;
1929 static GHashTable *FileObject_logging_hash;
1931 static void FileObject_logging_hash_init(void)
1933 if (FileObject_logging_hash)
1935 FileObject_logging_hash=g_hash_table_new(
1936 g_direct_hash, /* hash_func */
1937 g_direct_equal); /* key_equal_func */
1940 static void logging_notify_privbcb_flush(struct private_bcb *privbcb)
1942 struct FileObject_logging *FileObject_logging;
1944 g_return_if_fail(privbcb!=NULL);
1946 if (!privbcb->lsn_valid) /* nothing to report anyway */
1949 FileObject_logging_hash_init();
1951 if (!(FileObject_logging=g_hash_table_lookup(FileObject_logging_hash,privbcb->FileObject)))
1954 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: call",G_STRLOC,
1955 privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
1957 (*FileObject_logging->FlushToLsnRoutine)(FileObject_logging->LogHandle,privbcb->lsn);
1959 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: finish",G_STRLOC,
1960 privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
1963 VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
1965 GList *LogHandle_list;
1966 struct FileObject_logging *FileObject_logging;
1968 g_return_if_fail(FileObject!=NULL);
1969 /* 'LogHandle' may be NULL */
1970 g_return_if_fail(FlushToLsnRoutine!=NULL);
1972 log_handle_hash_init();
1973 FileObject_logging_hash_init();
1975 if (!(FileObject_logging=g_hash_table_lookup(
1976 FileObject_logging_hash,FileObject))) {
1977 captive_new(FileObject_logging);
1978 g_hash_table_insert(FileObject_logging_hash,
1979 FileObject,FileObject_logging);
1982 LogHandle_list=g_hash_table_lookup(log_handle_hash,FileObject_logging->LogHandle);
1983 g_assert(NULL!=g_list_find(LogHandle_list,FileObject));
1984 LogHandle_list=g_list_remove(LogHandle_list,FileObject);
1985 g_assert(NULL==g_list_find(LogHandle_list,FileObject));
1986 g_hash_table_insert(log_handle_hash,FileObject_logging->LogHandle,LogHandle_list);
1989 FileObject_logging->LogHandle=LogHandle;
1990 FileObject_logging->FlushToLsnRoutine=FlushToLsnRoutine;
1992 LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
1993 LogHandle_list=g_list_prepend(LogHandle_list,FileObject);
1994 g_hash_table_insert(log_handle_hash,LogHandle,LogHandle_list);
1998 struct CcGetDirtyPages_param {
1999 PDIRTY_PAGE_ROUTINE DirtyPageRoutine; /* arg of CcGetDirtyPages() */
2000 IN PVOID Context1; /* arg of CcGetDirtyPages() */
2001 IN PVOID Context2; /* arg of CcGetDirtyPages() */
2002 FILE_OBJECT *FileObject; /* search through 'page_position_hash' for 'FileObject' */
2003 LARGE_INTEGER OldestLsn; gboolean OldestLsn_found; /* intermediate return value of CcGetDirtyPages() */
2006 static void CcGetDirtyPages_page_position_hash_foreach(
2007 struct page_position *pagepos, /* key */
2008 struct page_position *pagepos2, /* value */
2009 struct CcGetDirtyPages_param *CcGetDirtyPages_param) /* user_data */
2011 LARGE_INTEGER FileOffset_local;
2012 LARGE_INTEGER OldestLsn,NewestLsn;
2013 LARGE_INTEGER OldestLsn_check,NewestLsn_check;
2014 gboolean lsn_found=FALSE;
2015 GList *privbcb_list;
2017 g_return_if_fail(pagepos!=NULL);
2018 g_return_if_fail(pagepos==pagepos2);
2019 g_return_if_fail(CcGetDirtyPages_param!=NULL);
2021 FileOffset_local=pagepos->FileOffset;
2024 privbcb_list=pagepos->privbcb_list;
2026 privbcb_list=privbcb_list->next) {
2027 struct private_bcb *privbcb=privbcb_list->data;
2029 if (!privbcb->lsn_valid)
2032 OldestLsn=NewestLsn=privbcb->lsn;
2035 OldestLsn.QuadPart=MIN(OldestLsn.QuadPart,privbcb->lsn.QuadPart);
2036 NewestLsn.QuadPart=MAX(NewestLsn.QuadPart,privbcb->lsn.QuadPart);
2042 if (!CcGetDirtyPages_param->OldestLsn_found) {
2043 CcGetDirtyPages_param->OldestLsn=OldestLsn;
2044 CcGetDirtyPages_param->OldestLsn_found=TRUE;
2047 CcGetDirtyPages_param->OldestLsn.QuadPart=MIN(CcGetDirtyPages_param->OldestLsn.QuadPart,OldestLsn.QuadPart);
2049 OldestLsn_check=OldestLsn;
2050 NewestLsn_check=NewestLsn;
2052 (*CcGetDirtyPages_param->DirtyPageRoutine)(
2053 pagepos->FileObject, /* FileObject */
2054 &FileOffset_local, /* FileOffset */
2055 PAGE_SIZE, /* Length */
2056 &OldestLsn, /* OldestLsn */
2057 &NewestLsn, /* NewestLsn */
2058 CcGetDirtyPages_param->Context1, /* Context1 */
2059 CcGetDirtyPages_param->Context2); /* Context2 */
2061 /* just unconfirmed sanity: */
2062 g_assert(FileOffset_local.QuadPart==pagepos->FileOffset.QuadPart); /* check for possible corruption */
2063 g_assert(OldestLsn_check.QuadPart==OldestLsn.QuadPart);
2064 g_assert(NewestLsn_check.QuadPart==NewestLsn.QuadPart);
2067 /* libcaptive must return #gint64 instead of the official #LARGE_INTEGER
2068 * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
2070 gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
2071 IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
2073 GList *LogHandle_list;
2074 struct CcGetDirtyPages_param CcGetDirtyPages_param;
2076 /* 'LogHandle' may be NULL */
2077 g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
2079 log_handle_hash_init();
2080 page_position_hash_init();
2082 CcGetDirtyPages_param.DirtyPageRoutine=DirtyPageRoutine;
2083 CcGetDirtyPages_param.Context1=Context1;
2084 CcGetDirtyPages_param.Context2=Context2;
2085 CcGetDirtyPages_param.OldestLsn_found=FALSE;
2088 LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
2090 LogHandle_list=LogHandle_list->next) {
2091 CcGetDirtyPages_param.FileObject=LogHandle_list->data;
2092 g_hash_table_foreach(
2093 page_position_hash, /* hash_table */
2094 (GHFunc)CcGetDirtyPages_page_position_hash_foreach, /* func */
2095 &CcGetDirtyPages_param); /* user_data */
2098 if (!CcGetDirtyPages_param.OldestLsn_found)
2100 return CcGetDirtyPages_param.OldestLsn.QuadPart;
2105 * CcSetAdditionalCacheAttributes:
2106 * @FileObject: Initialized open #FileObject to map.
2107 * %NULL value is forbidden.
2108 * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
2109 * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
2111 * libcaptive does not implement any caching and therefore this function
2114 VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
2116 g_return_if_fail(FileObject!=NULL);
2118 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
2119 FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
2121 /* NOP; no caching by libcaptive */
2126 * CcSetBcbOwnerPointer:
2127 * @Bcb: Initialized #PUBLIC_BCB structure.
2128 * %NULL value is forbidden.
2129 * @Owner: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
2130 * %NULL value is forbidden (FIXME: Is it W32 compliant?).
2132 * Set thread-specific pointer for a pinned @Bcb. Use CcUnpinDataForThread()
2133 * when @Bcb is no longer needed. CcUnpinDataForThread() is NOT a reverse
2134 * operation for this single call CcSetBcbOwnerPointer(), see CcUnpinDataForThread().
2136 * libcaptive implements this function as no-operation as it does not yet
2137 * support any threading.
2139 VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
2141 g_return_if_fail(Bcb!=NULL);
2142 g_return_if_fail(Owner!=NULL);
2144 /* FIXME:thread; NOP if no threads present */
2149 * CcUnpinDataForThread:
2150 * @Bcb: Initialized #PUBLIC_BCB structure.
2151 * %NULL value is forbidden.
2152 * @ResourceThreadId: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
2153 * This pointer had to be passed to CcSetBcbOwnerPointer() #Owner parameter previously.
2154 * %NULL value is forbidden (FIXME: is it W32 compliant?).
2156 * CcUnpinData() for a thread specified by @ResourceThreadId.
2157 * Reverse operation for a pair of CcMapData() and CcSetBcbOwnerPointer().
2159 * libcaptive implements this function as a simple pass to CcUnpinData() as it does not yet
2160 * support any threading.
2162 VOID CcUnpinDataForThread(IN PVOID Bcb,IN ERESOURCE_THREAD ResourceThreadId)
2164 g_return_if_fail(Bcb!=NULL);
2165 g_return_if_fail(ResourceThreadId!=0);
2175 * @Bcb: Initialized #PUBLIC_BCB structure.
2176 * %NULL value is forbidden.
2178 * Create a copy of @Bcb for the exactly same file contents as is @Bcb.
2179 * The returned copy has the same attributes as the result of CcMapData()
2180 * notwithstanding the current state of input @Bcb, therefore it is only
2181 * for read/only access etc.
2183 * libcaptive calls CcMapData() internally with @Bcb parameters.
2185 * Returns: Copy of @Bcb. This _pointer_ never equals to @Bcb.
2186 * It should be some different
2187 * #PUBLIC_BCB structure according to W32 doc.
2189 PVOID CcRemapBcb(IN PVOID Bcb)
2192 PVOID Buffer_unused;
2194 PUBLIC_BCB *PublicBcb;
2195 struct private_bcb *privbcb;
2197 g_return_val_if_fail(validate_Bcb(Bcb),NULL);
2199 private_bcb_hash_init();
2201 PublicBcb=(PUBLIC_BCB *)Bcb;
2202 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
2203 g_return_val_if_fail(privbcb!=NULL,NULL);
2205 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
2206 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d",G_STRLOC,
2207 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count);
2210 privbcb->FileObject, /* FileObject */
2211 &privbcb->MappedFileOffset, /* FileOffset */
2212 privbcb->MappedLength, /* Length */
2213 MAP_WAIT, /* Flags; && !MAP_NO_READ */
2215 &Buffer_unused); /* Buffer */
2216 g_return_val_if_fail(errbool==TRUE,NULL);
2217 g_return_val_if_fail(r!=NULL,NULL);
2218 g_return_val_if_fail(Buffer_unused!=NULL,NULL);
2226 * CcPreparePinWrite:
2227 * @FileObject: Initialized open #FileObject to map.
2228 * %NULL value is forbidden.
2229 * @FileOffset: The @FileObject file offset from where to map the region from.
2230 * Negative value is forbidden.
2231 * @Length: Requested length of the region to map from @FileObject.
2232 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
2233 * @Zero: %TRUE if the area of @FileOffset...@FileOffset+@Length should be cleared.
2234 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
2235 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
2236 * %PIN_NO_READ currently ignored; we map the data always.
2237 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
2238 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
2239 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
2240 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
2241 * %NULL pointer is forbidden.
2242 * @Buffer: Returns the mapped memory region start address.
2243 * This address may not be %PAGE_SIZE aligned.
2244 * %NULL pointer is forbidden.
2246 * Wrapper for a pair of CcPinRead() and CcSetDirtyPinnedData().
2247 * The mapped range can be also optionally cleared if @Zero is specified.
2248 * See CcPinRead() for a more detailed documentation.
2250 * FIXME: libcaptive will read even the file pages completely not need
2251 * in the case @Zero was specified.
2253 * Returns: %TRUE if the mapping was successful.
2255 BOOLEAN CcPreparePinWrite(IN PFILE_OBJECT FileObject,
2256 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Zero,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
2260 g_return_val_if_fail(FileObject!=NULL,FALSE);
2261 g_return_val_if_fail(FileOffset!=NULL,FALSE);
2262 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
2263 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
2264 /* 'Flags' passed to CcPinRead() */
2265 g_return_val_if_fail(Bcb!=NULL,FALSE);
2266 g_return_val_if_fail(Buffer!=NULL,FALSE);
2268 errbool=CcPinRead(FileObject,FileOffset,Length,Flags,Bcb,Buffer);
2269 g_return_val_if_fail(errbool==TRUE,FALSE);
2271 CcSetDirtyPinnedData(
2273 NULL); /* Lsn; OPTIONAL */
2276 memset(*Buffer,0,Length);
2283 VOID FsRtlIncrementCcFastReadNoWait(VOID)
2285 /* FIXME: {{%fs:[0]}+0x4E0}:LONG++ */
2289 NTSTATUS CcWaitForCurrentLazyWriterActivity(VOID)
2291 return STATUS_SUCCESS;