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_lsn_tree;
326 /* Although 'private_bcb_lsn_tree' contains only nodes with 'lsn_valid' TRUE
327 * compare_func cannot expect such arguments as it may be used to lookup
328 * privbcb unlisted in 'private_bcb_lsn_tree'.
330 static gint private_bcb_lsn_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
335 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a=%p,privbcb b=%p",G_STRLOC,a,b);
336 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb=%p",G_STRLOC,a->PublicBcb);
337 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb=%p",G_STRLOC,b->PublicBcb);
338 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)a->PublicBcb->MappedLength);
339 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)b->PublicBcb->MappedLength);
341 g_return_val_if_fail(a!=NULL,0);
342 g_return_val_if_fail(validate_Bcb(a->PublicBcb),0);
343 g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MAXINT64); /* Forbid defined LSN as G_MAXINT64 */
344 g_return_val_if_fail(b!=NULL,0);
345 g_return_val_if_fail(validate_Bcb(b->PublicBcb),0);
346 g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MAXINT64); /* Forbid defined LSN as G_MAXINT64 */
348 if (a==b) { /* LSN would be apparently the same in such case :-) */
349 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return 0 (a==b)",G_STRLOC);
353 a_lsn=(!a->lsn_valid ? (LONGLONG)G_MAXINT64 : a->lsn.QuadPart);
354 b_lsn=(!b->lsn_valid ? (LONGLONG)G_MAXINT64 : b->lsn.QuadPart);
356 /* Forbid the same LSNs if both defined */
357 g_assert(a_lsn==(LONGLONG)G_MAXINT64 || a_lsn!=b_lsn);
359 if ((r=(a_lsn>b_lsn)-(a_lsn<b_lsn))) {
360 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d (lsn !=)",G_STRLOC,r);
364 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return (ptr !=)",G_STRLOC);
368 static void private_bcb_lsn_tree_init(void)
370 if (private_bcb_lsn_tree)
372 private_bcb_lsn_tree=g_tree_new(
373 (GCompareFunc)private_bcb_lsn_tree_key_compare_func); /* key_compare_func */
377 PRIVBCB_ITEM_NOP, /* for sanity checks */
379 PRIVBCB_ITEM_LSN_VALID,
380 PRIVBCB_ITEM_LEAVE_FUNC_PENDING,
381 PRIVBCB_ITEM_REF_COUNT,
382 PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR, /* no protections against 'leave_func_pending' */
385 static void privbcb_set(struct private_bcb *privbcb,enum privbcb_item item,gint value)
387 g_return_if_fail(privbcb!=NULL);
389 private_bcb_lsn_tree_init();
391 g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
393 case PRIVBCB_ITEM_NOP:
395 case PRIVBCB_ITEM_DIRTY:
396 g_assert(TRUE==value || FALSE==value);
397 privbcb->dirty=value;
399 case PRIVBCB_ITEM_LSN_VALID:
400 /* Never change 'privbcb->lsn_valid' while it is linked in 'private_bcb_lsn_tree'
401 * as it could become unreachable due to private_bcb_lsn_tree_key_compare_func()
402 * behaviour wrt 'privbcb->lsn_valid'!
404 g_tree_remove(private_bcb_lsn_tree,privbcb);
405 g_assert(TRUE==value || FALSE==value);
406 privbcb->lsn_valid=value;
407 if (privbcb->lsn_valid)
408 g_tree_insert(private_bcb_lsn_tree,
410 privbcb); /* value */
412 case PRIVBCB_ITEM_LEAVE_FUNC_PENDING:
413 g_assert(TRUE==value || FALSE==value);
414 privbcb->leave_func_pending=value;
416 g_assert(privbcb->ref_count==1);
418 case PRIVBCB_ITEM_REF_COUNT:
419 /* Forbid reincarnation of 'leave_func_pending' privbcb. */
420 g_assert(!privbcb->leave_func_pending);
421 g_assert(privbcb->ref_count>=1);
422 privbcb->ref_count+=value;
423 g_assert(privbcb->ref_count>=1);
425 case PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR:
426 g_assert(privbcb->ref_count>=0);
427 privbcb->ref_count+=value;
428 g_assert(privbcb->ref_count>=0);
430 default: g_assert_not_reached();
432 g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
436 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
437 static GHashTable *private_bcb_hash;
439 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
441 g_return_if_fail(validate_Bcb(key));
443 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: PublicBcb=%p",G_STRLOC,key);
448 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
450 struct page_position pagepos_local;
454 gpointer base_aligned;
456 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: privbcb=%p (->PublicBcb=%p)",G_STRLOC,value,value->PublicBcb);
458 g_return_if_fail(value!=NULL);
459 /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
460 * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
462 g_return_if_fail(value->PublicBcb!=NULL);
463 g_return_if_fail(value->FileObject!=NULL);
464 g_return_if_fail(value->ref_count==0);
465 g_return_if_fail(value->MappedLength>0);
466 g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
467 g_return_if_fail(value->base!=NULL);
468 g_return_if_fail(value->dirty==FALSE);
469 /* Ensure we are not registered in 'private_bcb_lsn_tree'. */
470 g_return_if_fail(value->lsn_valid==FALSE);
472 page_position_hash_init();
474 base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
476 pagepos_local.FileObject=value->FileObject;
477 pagepos_local.privbcb_list=NULL;
478 pagepos_local.shmid=-1;
481 offset<value->MappedLength;
483 struct page_position *pagepos;
485 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
486 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
487 g_assert(validate_page_position(pagepos));
488 g_assert(pagepos->privbcb_list!=NULL);
489 if (pagepos->privbcb_list->next==NULL) { /* single mapping by mmap(), no shm */
490 g_assert(pagepos->shmid==-1);
491 errint=munmap(((char *)base_aligned)+offset,PAGE_SIZE);
494 else if (pagepos->privbcb_list->next->next==NULL) { /* mapping 2 -> 1, convert shm to mmap() */
495 struct private_bcb *privbcb_other;
496 gint64 privbcb_other_offset_relative;
497 gpointer mapped_other,errptr;
499 g_assert(pagepos->shmid!=-1);
500 if (value==pagepos->privbcb_list->data)
501 privbcb_other=pagepos->privbcb_list->next->data;
503 privbcb_other=pagepos->privbcb_list->data;
504 g_assert(value==pagepos->privbcb_list->next->data);
506 privbcb_other_offset_relative=pagepos->FileOffset.QuadPart-privbcb_other->MappedFileOffset.QuadPart;
507 /* privbcb_other_offset_relative may be negative up to -PAGE_SIZE
508 * as 'MappedFileOffset' and 'base' are not page-aligned.
510 mapped_other=privbcb_other->base+privbcb_other_offset_relative;
511 /* TODO:thread; shmdt()..mmap() window */
512 errint=shmdt(mapped_other);
515 mapped_other, /* start */
516 PAGE_SIZE, /* length */
517 PROT_READ|PROT_WRITE, /* prot */
518 MAP_PRIVATE|MAP_ANONYMOUS, /* flags */
519 -1, /* fd; ignored due to MAP_ANONYMOUS */
520 0); /* offset; ignored due to MAP_ANONYMOUS */
521 g_assert(errptr==mapped_other);
522 memcpy(mapped_other,((char *)base_aligned)+offset,PAGE_SIZE);
523 errint=shmdt(((char *)base_aligned)+offset); /* destroys shm */
525 /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
528 else { /* mappings (>=3) -> (>=2) */
529 g_assert(pagepos->shmid!=-1);
530 errint=shmdt(((char *)base_aligned)+offset);
534 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
535 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
536 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
538 if (pagepos->privbcb_list==NULL) { /* last mapping by mmap() removed */
539 g_assert(pagepos->shmid==-1);
540 errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
541 g_assert(errbool==TRUE);
543 else /* mapping is now >=1, either by mmap() or shm */
544 g_assert(validate_page_position(pagepos));
550 static void private_bcb_hash_init(void)
552 if (private_bcb_hash)
554 private_bcb_hash=g_hash_table_new_full(
555 g_direct_hash, /* hash_func */
556 g_direct_equal, /* key_equal_func */
557 (GDestroyNotify)private_bcb_hash_key_destroy_func, /* key_destroy_func */
558 (GDestroyNotify)private_bcb_hash_value_destroy_func); /* value_destroy_func */
562 static gboolean captive_cc_FileObject_delete_private_bcb_hash_foreach(
563 PUBLIC_BCB *PublicBcb, /* key */
564 struct private_bcb *privbcb, /* value */
565 FILE_OBJECT *FileObject) /* user_data */
567 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE); /* meaning: do not remove this 'privbcb' */
568 g_return_val_if_fail(privbcb!=NULL,FALSE); /* meaning: do not remove this 'privbcb' */
569 g_return_val_if_fail(PublicBcb==privbcb->PublicBcb,FALSE); /* meaning: do not remove this 'privbcb' */
570 g_return_val_if_fail(FileObject!=NULL,FALSE); /* meaning: do not remove this 'privbcb' */
572 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: PublicBcb=%p,privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
573 ",FileObject=%p",G_STRLOC,
574 PublicBcb,privbcb,privbcb->ref_count,privbcb->leave_func_pending,FileObject);
577 if (privbcb->FileObject!=FileObject)
578 return FALSE; /* do not remove this 'privbcb' */
580 /* It is OK to leave it in 'private_bcb_hash' as it is protected
581 * against reincarnation.
582 * Such check is here to allow it to keep 'dirty' data.
584 if (privbcb->leave_func_pending)
585 return FALSE; /* do not remove this 'privbcb' */
588 /* Do not reuse CcUnpinData_leave_func() destructor here as we cannot
589 * call g_hash_table_remove() from inside g_hash_table_foreach_remove() callback.
592 g_assert(privbcb->dirty==FALSE); /* it would be fatal */
594 g_assert(privbcb->leave_func_pending==FALSE);
595 /* Force 'ref_count' drop to 0 to pass private_bcb_hash_value_destroy_func(). */
596 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-privbcb->ref_count);
597 /* Unset LSN to unlink from 'private_bcb_lsn_tree'. */
598 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
600 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: WARNING: Deleting file with pending (fortunately non-dirty) BCBs"
601 "; privbcb=%p,PublicBcb=%p,FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
602 privbcb,PublicBcb,privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
604 return TRUE; /* remove this 'privbcb' */
607 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
609 struct fileobject_cached *fileobject_cached;
612 /* 'FileObject' may be NULL to check/clear the whole cache. */
614 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p",G_STRLOC,FileObject);
616 fileobject_cached_hash_init();
617 private_bcb_hash_init();
619 if (!FileObject || !(fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,FileObject)))
622 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
623 CcUnpinData(fileobject_cached->Bcb);
625 g_hash_table_remove(fileobject_cached_hash,FileObject);
628 g_hash_table_foreach_remove(
629 private_bcb_hash, /* hash_table */
630 (GHRFunc)captive_cc_FileObject_delete_private_bcb_hash_foreach, /* func */
631 FileObject); /* user_data */
633 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: returning r=%d",G_STRLOC,r);
639 static void captive_privbcb_flush(struct private_bcb *privbcb);
641 static void captive_cc_flush_private_bcb_hash_foreach(
642 PUBLIC_BCB *PublicBcb, /* key */
643 struct private_bcb *privbcb, /* value */
644 gboolean *flushedp) /* user_data */
646 g_return_if_fail(validate_Bcb(PublicBcb));
647 g_return_if_fail(privbcb!=NULL);
648 g_return_if_fail(PublicBcb==privbcb->PublicBcb);
650 if (!privbcb->dirty) /* OK */
654 captive_privbcb_flush(privbcb);
657 static void CcUnpinData_leave_func(struct private_bcb *privbcb_req);
659 void captive_cc_flush(void)
663 fileobject_cached_hash_init();
664 private_bcb_hash_init();
667 /* Trace it by g_tree first to attempt to keep LSN ordering */
668 CcUnpinData_leave_func(NULL);
670 g_hash_table_foreach(
671 private_bcb_hash, /* hash_table */
672 (GHFunc)captive_cc_flush_private_bcb_hash_foreach, /* func */
673 &flushed); /* user_data */
677 /* We must not call: captive_cc_FileObject_delete(NULL);
678 * here as it would destroy all the Bcbs needed for the forthcoming
679 * IoShutdownRegisteredFileSystems().
684 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
688 IO_STATUS_BLOCK IoStatus;
691 g_return_val_if_fail(FileObject!=NULL,0);
692 g_return_val_if_fail(address!=0,0);
693 g_return_val_if_fail(length!=0,0);
694 g_return_val_if_fail(FileOffset!=NULL,0);
696 /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
697 * successfuly read the data. Workaround it - preclear (not postclear) the
698 * buffer and do not make any other assumptions about the data read.
700 memset(address,0,PAGE_SIZE); /* pre-clear the buffer */
701 Mdl=MmCreateMdl(NULL,address,PAGE_SIZE); /* FIXME: Deprecated in favor of IoAllocateMdl() */
703 MmBuildMdlForNonPagedPool(Mdl);
704 KeInitializeEvent(&Event,NotificationEvent,FALSE);
705 IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
706 err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
707 g_assert(NT_SUCCESS(err));
708 g_assert(NT_SUCCESS(IoStatus.Status));
709 /* It is not == as the file may be shorter than requested */
710 g_assert(IoStatus.Information<=length);
713 /* Forbidden, see the comment above about ext2fsd.sys.
714 * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
717 return IoStatus.Information; /* may be shorter than real! */
721 static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,
722 PUBLIC_BCB *Bcb_find)
724 struct page_position pagepos_local,*pagepos;
725 struct private_bcb *privbcb,*privbcb_listitem;
728 g_return_val_if_fail(FileObject!=NULL,NULL);
729 g_return_val_if_fail(FileOffset!=NULL,NULL);
730 /* 'Bcb_find' may be NULL */
732 page_position_hash_init();
734 pagepos_local.FileObject=FileObject;
735 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
736 pagepos_local.privbcb_list=NULL;
737 pagepos_local.shmid=-1;
738 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local)))
740 g_assert(validate_page_position(pagepos));
741 g_assert(pagepos->privbcb_list!=NULL);
744 for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
745 privbcb_listitem=privbcb_list->data;
747 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
748 && privbcb_listitem->MappedLength==Length
749 && (!Bcb_find || Bcb_find==privbcb_listitem->PublicBcb)) {
750 g_assert(privbcb==NULL); /* appropriate 'Bcb_find'-matching privbcb found twice */
751 privbcb=privbcb_listitem;
759 /* Sanity check 'privbcb': */
760 g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
768 * @FileObject: Initialized open #FileObject to map.
769 * %NULL value is forbidden.
770 * @FileOffset: The @FileObject file offset from where to map the region from.
771 * Negative value is forbidden.
772 * @Length: Requested length of the region to map from @FileObject.
773 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
774 * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
775 * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
776 * %MAP_NO_READ will leave the pages unread (libcaptive: unread pages zeroed,
777 * already mapped pages shared with existing content) - needed by ntfs.sys of NT-5.1sp1
778 * as during file write it will %MAP_NO_READ and consequently push the data there;
779 * any request for the same file range read in the meantime will destroy the prepared data!
780 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
781 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
782 * %NULL pointer is forbidden.
783 * @Buffer: Returns the mapped memory region start address.
784 * This address may not be %PAGE_SIZE aligned.
785 * %NULL pointer is forbidden.
787 * Maps the specified region of @FileObject to automatically chosen address space.
788 * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
790 * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
791 * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
792 * but W32 filesystems apparently do not conform to it. :-) If you do not take care of the
793 * modified data by some dirty-marking facility such data will be carelessly dropped without
794 * their commit to the disk.
796 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
798 * We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
799 * even if such mapping for such file already exists.
800 * We should probably create a new #Bcb for the same space,
801 * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
803 * Returns: %TRUE if the region was successfuly mapped.
804 * @Bcb with the initialized new memory region.
805 * @Buffer with the address of the exact byte specified by @FileOffset.
807 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
808 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
810 PUBLIC_BCB **PublicBcbp,*PublicBcb;
811 struct page_position pagepos_local,*pagepos;
812 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
813 gpointer base_aligned;
814 size_t offset,length_mapped_aligned;
817 struct private_bcb *privbcb;
818 gboolean after_eof=FALSE; /* Did we reached the end of file already? */
819 GPtrArray *read_array;
821 g_return_val_if_fail(FileObject!=NULL,FALSE);
822 g_return_val_if_fail(FileOffset!=NULL,FALSE);
823 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
824 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
825 g_return_val_if_fail(Flags&MAP_WAIT,FALSE); /* FIXME: on-demand loading not yet implemented */
826 g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE); /* unknown flags? */
827 g_return_val_if_fail(Bcb!=NULL,FALSE);
828 g_return_val_if_fail(Buffer!=NULL,FALSE);
830 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
831 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
833 if (Flags&MAP_NO_READ) {
834 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE)); /* NOT IMPLEMENTED YET */
835 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(Length,PAGE_SIZE)); /* NOT IMPLEMENTED YET */
838 g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
839 g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
840 #if 0 /* SectorSize can be really weird but we do not use it anyway */
841 /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
842 * 'SectorSize' may not yet be initialized during mount operation
843 * and 'FileObject->DeviceObject->Vpb' may exist in such case.
845 g_return_val_if_fail(0
846 || FileObject->DeviceObject->SectorSize==0 /* prevent division by 0 */
847 || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),
851 page_position_hash_init();
852 private_bcb_hash_init();
854 PublicBcbp=(PUBLIC_BCB **)Bcb;
855 /* extend 'FileOffset' and 'Length' to page boundaries */
856 FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
857 FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
858 length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
860 /* We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
861 * even if such mapping for such file already exists.
862 * We should probably create a new Bcb for the same space,
863 * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
865 if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL)))
866 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX already mapped by privbcb %p",
867 G_STRLOC,FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,privbcb);
869 /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
870 /* TODO: on-demand loading */
871 /* Although we do zeroed-page mapping here we just reserve the linear
876 PAGE_SIZE+length_mapped_aligned+PAGE_SIZE, /* length; leading and trailing boundary check pages */
877 PROT_READ|PROT_WRITE, /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
878 MAP_PRIVATE|MAP_ANONYMOUS, /* flags */
879 -1, /* fd; ignored due to MAP_ANONYMOUS */
880 0); /* offset; ignored due to MAP_ANONYMOUS */
881 g_assert(base_aligned!=NULL);
883 base_aligned+=PAGE_SIZE;
884 errint=munmap(base_aligned-PAGE_SIZE,PAGE_SIZE); /* unmap leading boundary check page */
886 errint=munmap(base_aligned+length_mapped_aligned,PAGE_SIZE); /* unmap trailing boundary check page */
889 /* Create 'PublicBcb'; referenced by 'privbcb'. */
890 captive_new(PublicBcb);
891 PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
892 PublicBcb->NodeByteSize=sizeof(*PublicBcb); /* we have no extensions there */
893 PublicBcb->MappedLength=Length;
894 PublicBcb->MappedFileOffset=*FileOffset;
896 /* Create 'privbcb'; referenced by created 'pagepos'es. */
897 captive_new(privbcb);
898 privbcb->PublicBcb=PublicBcb;
899 privbcb->FileObject=FileObject;
900 privbcb->ref_count=1;
901 privbcb->leave_func_pending=FALSE;
902 privbcb->MappedLength=PublicBcb->MappedLength;
903 privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
904 privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
905 privbcb->dirty=FALSE;
906 privbcb->lsn_valid=FALSE;
907 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_hash_table_insert: PublicBcb=%p,privbcb=%p",G_STRLOC,
909 g_hash_table_insert(private_bcb_hash,
911 privbcb); /* value */
913 privbcb_set(privbcb,PRIVBCB_ITEM_NOP,0); /* just for the assertions */
915 /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
916 * below as captive_Cc_IoPageRead() has very big consequences as it calls
917 * the filesystem code and we may get reentrancy.
918 * Therefore we store all missing page read requests to 'read_array' and we
919 * fill them when all the memory structures are in consistent state.
920 * We can also coalescence the requests more easily this way.
922 read_array=g_ptr_array_new();
924 pagepos_local.FileObject=FileObject;
925 pagepos_local.shmid=-1;
926 pagepos_local.privbcb_list=NULL;
929 offset<length_mapped_aligned;
933 pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
934 pageaddr=(gpointer)(((char *)base_aligned)+offset);
935 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
936 captive_new(pagepos);
937 *pagepos=pagepos_local;
939 pagepos->privbcb_list=NULL;
940 g_ptr_array_add(read_array,pagepos); /* enlist this 'pagepos' as todo read item */
941 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read of offset %llu to new pagepos %p of pure mmap() to address %p",
942 G_STRLOC,(unsigned long long)offset,pagepos,pageaddr);
944 else if (pagepos->privbcb_list->next==NULL) { /* exactly one item (no IPC shm yet) */
945 g_assert(pagepos->shmid==-1);
946 if (-1==(pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600)))
947 g_error("%s: Failed shmget(2), you may be out of maximum system IPC shared pages",G_STRLOC);
948 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",
949 G_STRLOC,pagepos->shmid,(unsigned long long)offset,pagepos,pageaddr);
951 else { /* already >=2 mappings, already IPC shared mem */
952 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",
953 G_STRLOC,(unsigned long long)offset,pagepos,pagepos->shmid,pageaddr);
954 g_assert(pagepos->shmid!=-1);
955 g_assert(pagepos->privbcb_list!=NULL);
957 if (pagepos->privbcb_list) { /* Not the first one - some shm in use now? */
958 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
959 * mmap(2) is still needed to get linear block of memory assignment.
961 /* TODO:thread; munmap()..shmat() window */
962 errint=munmap(pageaddr,PAGE_SIZE);
964 errptr=shmat(pagepos->shmid,
965 pageaddr, /* shmaddr */
966 0); /* shmflg; !SHM_RDONLY==r/w */
967 g_assert(errptr==pageaddr);
970 g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
971 pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb); /* order not important */
972 g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
973 if (pagepos->privbcb_list->next==NULL) { /* exactly one item (we just added it now) */
975 g_assert(pagepos->shmid==-1);
976 g_hash_table_insert(page_position_hash,
978 pagepos); /* value */
980 else if (pagepos->privbcb_list->next->next==NULL) { /* second mapping - new shm */
981 struct private_bcb *privbcb_orig;
982 gint64 privbcb_orig_offset_relative;
983 gpointer mapped_orig;
985 g_assert(pagepos->shmid!=-1);
986 errint=shmctl(pagepos->shmid,
990 g_assert(pagepos->privbcb_list->data==privbcb); /* our privbcb should be the first */
991 g_assert(pagepos->privbcb_list->next!=NULL); /* we have exactly two items in the list */
992 g_assert(pagepos->privbcb_list->next->next==NULL); /* we have exactly two items in the list */
993 privbcb_orig=pagepos->privbcb_list->next->data;
994 privbcb_orig_offset_relative=pagepos->FileOffset.QuadPart-privbcb_orig->MappedFileOffset.QuadPart;
995 /* privbcb_orig_offset_relative may be negative up to -PAGE_SIZE
996 * as 'MappedFileOffset' and 'base' are not page-aligned.
998 mapped_orig=privbcb_orig->base+privbcb_orig_offset_relative;
999 memcpy(pageaddr,mapped_orig,PAGE_SIZE);
1000 /* TODO:thread; munmap()..shmat() window */
1001 errint=munmap(mapped_orig,PAGE_SIZE);
1002 g_assert(errint==0);
1003 errptr=shmat(pagepos->shmid,
1004 mapped_orig, /* shmaddr */
1005 0); /* shmflg; !SHM_RDONLY==r/w */
1006 g_assert(errptr==mapped_orig);
1008 g_assert(validate_page_position(pagepos));
1011 /* Fill in the missing pages content todo-list stored in 'read_array': */
1012 while (!(Flags&MAP_NO_READ) && read_array->len && (pagepos=g_ptr_array_remove_index(read_array,
1016 struct pagepos *pagepos_next;
1017 gpointer pageaddr,pageaddr_next;
1019 pageaddr=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
1020 read_size=PAGE_SIZE;
1021 /* Coalescence of the requests */
1022 while (read_array->len) {
1023 pagepos_next=g_ptr_array_index(read_array,
1025 pageaddr_next=(gpointer)(((char *)base_aligned)+(pagepos->FileOffset.QuadPart-FileOffset_bottom.QuadPart));
1026 if (pageaddr_next!=((char *)pageaddr)+read_size)
1028 read_size+=PAGE_SIZE;
1029 g_assert(pagepos_next==g_ptr_array_remove_index(read_array,
1032 /* Read the range content: */
1033 got=captive_Cc_IoPageRead(FileObject,pageaddr,read_size,&pagepos->FileOffset);
1037 g_assert(got<=PAGE_SIZE);
1038 after_eof=(got<PAGE_SIZE);
1040 g_ptr_array_free(read_array,
1041 TRUE); /* free_seg; free the array of gpointer(==struct pagepos *) items */
1043 /* offset _into_ page, may not be PAGE_SIZE aligned: */
1044 *Buffer=privbcb->base;
1045 *PublicBcbp=PublicBcb;
1046 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1047 "%s: result: privbcb=%p,privbcb->base=%p,privbcb->base+privbcb->MappedLength=%p,privbcb->MappedLength=0x%lX",
1048 G_STRLOC,privbcb,privbcb->base,((char *)privbcb->base)+privbcb->MappedLength,(unsigned long)privbcb->MappedLength);
1049 g_assert(validate_Bcb(PublicBcb)==TRUE);
1050 if (!FileObject->SectionObjectPointers->SharedCacheMap)
1051 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
1053 /* FIXME: (NOTE*1) Weird but it appears as fastfat.sys during make_directory()
1054 * can pass us an already cached 'FileObject' (with different offset/size).
1055 * We must not CcUnpinData() it as its BCB is still referenced (CcUnpinData()ed) by fastfat.sys.
1057 FileObject->SectionObjectPointers->SharedCacheMap=NULL;
1059 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
1067 * @FileObject: Initialized open #FileObject to map.
1068 * %NULL value is forbidden.
1069 * @MappedFileOffset: The @FileObject file offset from where to map the region from.
1070 * Negative value is forbidden.
1071 * @MappedLength: Requested length of the region to map from @FileObject.
1072 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1073 * @Wait: Whether disk waiting is permitted for this function.
1074 * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
1075 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1076 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1077 * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
1078 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1079 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1080 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1081 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1082 * %NULL pointer is forbidden.
1083 * @Buffer: Returns the mapped memory region start address.
1084 * This address may not be %PAGE_SIZE aligned.
1085 * %NULL pointer is forbidden.
1087 * This function will allow you to modify the data mapped by CcMapData().
1088 * libcaptive does not differentiate this function with CcMapData().
1090 * NEVER re-read any memory from FileObject here!
1091 * at least fastfat.sys directory create relies on the fact of CcPinRead()
1092 * with already modified buffers to be left intact.
1094 * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
1095 * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
1097 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
1099 * Returns: %TRUE if the region was successfuly mapped.
1100 * @Bcb with the initialized new memory region.
1101 * @Buffer with the address of the exact byte specified by @FileOffset.
1103 BOOLEAN CcPinMappedData
1104 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
1106 struct private_bcb *privbcb;
1108 g_return_val_if_fail(FileObject!=NULL,FALSE);
1109 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1110 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1111 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
1112 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
1113 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
1114 g_return_val_if_fail(Bcb!=NULL,FALSE);
1116 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
1117 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
1119 privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL);
1120 if (!privbcb && (Flags & PIN_IF_BCB)) /* BCB does not exist */
1122 /* Appropriate privbcb not found.
1123 * This IMO should not happen and it is a bug in the client.
1124 * Unfortuantely fastfat.sys FatLocateVolumeLabel() will CcPinMappedData()
1125 * the volume label dirent without any previous CcMapData() or CcPinRead() !
1135 | (Flags&PIN_NO_READ ? MAP_NO_READ : 0)
1136 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
1141 /* NEVER re-read any memory from FileObject here! */
1143 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1145 /* Memory already mapped by CcMapData(). */
1146 *Bcb=privbcb->PublicBcb;
1153 * @FileObject: Initialized open #FileObject to map.
1154 * %NULL value is forbidden.
1155 * @FileOffset: The @FileObject file offset from where to map the region from.
1156 * Negative value is forbidden.
1157 * @Length: Requested length of the region to map from @FileObject.
1158 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1159 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1160 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1161 * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
1162 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1163 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1164 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1165 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1166 * %NULL pointer is forbidden.
1167 * @Buffer: Returns the mapped memory region start address.
1168 * This address may not be %PAGE_SIZE aligned.
1169 * %NULL pointer is forbidden.
1171 * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
1172 * See these two functions for the details. It has a difference to subsequent
1173 * calling of CcMapData() and CcPinMappedData() instead as this call counts
1174 * only as one function for a corresponding CcUnpinData() call.
1176 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
1178 * Returns: %TRUE if the region was successfuly mapped.
1179 * @Bcb with the initialized new memory region.
1180 * @Buffer with the address of the exact byte specified by @FileOffset.
1182 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
1183 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
1185 PVOID Bcb_CcPinMappedData;
1187 gboolean count_CcMapData;
1188 struct private_bcb *privbcb;
1190 g_return_val_if_fail(FileObject!=NULL,FALSE);
1191 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1192 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1193 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
1194 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
1195 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
1196 g_return_val_if_fail(Bcb!=NULL,FALSE);
1197 g_return_val_if_fail(Buffer!=NULL,FALSE);
1199 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
1200 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
1202 if (!(Flags&PIN_IF_BCB)) {
1203 errbool=CcMapData(FileObject,FileOffset,Length,
1205 | (Flags&PIN_NO_READ ? MAP_NO_READ : 0)
1206 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
1208 g_return_val_if_fail(errbool==TRUE,FALSE);
1209 count_CcMapData=TRUE;
1212 count_CcMapData=FALSE;
1214 errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
1215 if (!(Flags&PIN_IF_BCB)) {
1216 g_return_val_if_fail(errbool==TRUE,FALSE);
1217 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
1220 if (errbool==FALSE) /* FALSE permitted; We may fail if Bcb does not exist yet. */
1222 *Bcb=Bcb_CcPinMappedData;
1225 privbcb=captive_privbcb_find(FileObject,FileOffset,Length,*Bcb);
1226 g_assert(privbcb!=NULL);
1227 g_assert(privbcb->ref_count>=1);
1228 g_assert(privbcb->PublicBcb==*Bcb);
1229 if (count_CcMapData) {
1230 /* CcPinRead() must always reference-count only by 1 despite any sub-called functions! */
1231 g_assert(privbcb->ref_count>=2);
1232 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
1239 static void logging_notify_privbcb_flush(struct private_bcb *privbcb);
1241 static void captive_privbcb_flush(struct private_bcb *privbcb)
1245 IO_STATUS_BLOCK IoStatus;
1247 gpointer base_sectoraligned;
1248 gsize length_sectoraligned;
1249 LARGE_INTEGER FileOffset_sectoraligned;
1251 struct fileobject_cached *fileobject_cached;
1252 IRP *saved_TopLevelIrp;
1253 static gint64 last_written_lsn=G_MININT64;
1255 g_assert(privbcb->ref_count>0);
1257 if (!privbcb->dirty)
1260 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);
1261 logging_notify_privbcb_flush(privbcb);
1262 g_assert(privbcb->ref_count>=2);
1263 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1265 if (privbcb->lsn_valid) {
1266 if (!(last_written_lsn<privbcb->lsn.QuadPart))
1267 g_error("%s: last_written_lsn=%" G_GINT64_FORMAT " !< privbcb->lsn=%" G_GINT64_FORMAT,G_STRLOC,
1268 last_written_lsn,(gint64)privbcb->lsn.QuadPart);
1269 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LSN write: last_written_lsn was %" G_GINT64_FORMAT ", is %" G_GINT64_FORMAT,
1270 G_STRLOC,last_written_lsn,(gint64)privbcb->lsn.QuadPart);
1271 last_written_lsn=privbcb->lsn.QuadPart;
1274 /* Prepare NULL TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
1276 saved_TopLevelIrp=IoGetTopLevelIrp();
1277 IoSetTopLevelIrp(NULL);
1279 fileobject_cached_hash_init();
1280 if ((fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,privbcb->FileObject)))
1281 (*fileobject_cached->CallBacks.AcquireForLazyWrite)(
1282 fileobject_cached->LazyWriterContext, /* Context */
1285 g_assert(privbcb->FileObject->DeviceObject!=NULL);
1287 /* We can get 0=='privbcb->FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
1288 * FIXME: We are unable to find the correct sectorsize for a filesystem as
1289 * even the 'CommonFcb' below contains invalid information.
1290 * As we need to have 'sectorsize' <=filesystem_blocksize at least for ext2fsd.sys
1291 * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
1292 * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
1294 sectorsize=PAGE_SIZE;
1296 sectorsize=privbcb->FileObject->DeviceObject->SectorSize;
1297 if (privbcb->FileObject->FsContext) {
1298 REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileObject->FsContext;
1300 /* FIXME: Check CommonFcb->Type */
1301 /* 'AllocationSize' can be less than 'sectorsize' if the total file length is smaller.
1302 * Observed with ext2fsd.sys volume-file.
1304 if (sectorsize<CommonFcb->AllocationSize.QuadPart)
1305 sectorsize=CommonFcb->AllocationSize.QuadPart;
1309 /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
1310 g_assert(sectorsize>0);
1311 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,sectorsize));
1312 /* We align here directly the 'privbcb->base' which is not correct.
1313 * We should rather aligned according to 'privbcb->MappedOffset' but
1314 * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
1315 * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
1317 g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
1318 ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,sectorsize));
1319 base_sectoraligned =CAPTIVE_ROUND_DOWN(privbcb->base,sectorsize);
1320 length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,sectorsize)
1321 -((char *)base_sectoraligned);
1322 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,sectorsize));
1323 FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,sectorsize);
1325 Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
1326 g_assert(Mdl!=NULL);
1327 MmBuildMdlForNonPagedPool(Mdl);
1329 KeInitializeEvent(&Event,NotificationEvent,FALSE);
1331 /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
1332 err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
1333 g_assert(NT_SUCCESS(err));
1334 g_assert(NT_SUCCESS(IoStatus.Status));
1335 /* We should write at least the unaligned mapped data although we
1336 * do not need to successfuly write the whole aligned amount.
1337 * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
1338 * during FAT write by fastfat.sys.
1340 g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
1341 +privbcb->MappedLength);
1342 g_assert(IoStatus.Information<=length_sectoraligned);
1344 if (fileobject_cached)
1345 (*fileobject_cached->CallBacks.ReleaseFromLazyWrite)(
1346 fileobject_cached->LazyWriterContext); /* Context */
1348 /* Restore TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
1350 g_assert(NULL==IoGetTopLevelIrp());
1351 IoSetTopLevelIrp(saved_TopLevelIrp);
1353 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
1354 "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
1355 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
1356 base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
1357 (gulong)IoStatus.Information);
1359 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE);
1363 static void CcUnpinData_leave_func_finalize(struct private_bcb *privbcb)
1367 g_assert(privbcb->dirty==FALSE);
1368 g_assert(privbcb->lsn_valid==FALSE);
1369 g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
1370 g_assert(privbcb->leave_func_pending==TRUE);
1371 g_assert(privbcb->ref_count==1);
1372 privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
1373 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1374 errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
1375 g_assert(errbool==TRUE);
1378 struct CcUnpinData_leave_func_param {
1379 struct private_bcb *privbcb_req;
1380 struct private_bcb *found; /* return */
1383 static gboolean CcUnpinData_leave_func_foreach_get_first(struct private_bcb *key,struct private_bcb *value,
1384 struct CcUnpinData_leave_func_param *CcUnpinData_leave_func_param /* data */)
1386 g_return_val_if_fail(key!=NULL,TRUE); /* meaning: stop the traversal */
1387 g_return_val_if_fail(value!=NULL,TRUE); /* meaning: stop the traversal */
1388 g_return_val_if_fail(key==value,TRUE); /* meaning: stop the traversal */
1389 g_return_val_if_fail(validate_Bcb(key->PublicBcb),TRUE); /* meaning: stop the traversal */
1390 g_return_val_if_fail(CcUnpinData_leave_func_param!=NULL,TRUE); /* meaning: stop the traversal */
1391 g_return_val_if_fail(CcUnpinData_leave_func_param->found==NULL,TRUE); /* meaning: stop the traversal */
1393 g_assert(key->lsn_valid==TRUE);
1395 if (key==CcUnpinData_leave_func_param->privbcb_req) {
1396 CcUnpinData_leave_func_param->found=key;
1397 return TRUE; /* stop the traversal */
1401 return FALSE; /* continue the traversal */
1403 CcUnpinData_leave_func_param->found=key; /* first dirty LSNed buffer */
1404 return TRUE; /* stop the traversal */
1407 static void CcUnpinData_leave_func(struct private_bcb *privbcb_req) /* NULL to traverse the whole tree */
1409 struct private_bcb *privbcb;
1410 struct CcUnpinData_leave_func_param CcUnpinData_leave_func_param;
1412 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb_req=%p,privbcb_req->ref_count=%d,privbcb_req->leave_func_pending=%d"
1413 ",privbcb_req->lsn_valid=%d",G_STRLOC,
1414 privbcb_req,(!privbcb_req ? -1 : privbcb_req->ref_count),(!privbcb_req ? -1 : privbcb_req->leave_func_pending),
1415 (!privbcb_req ? -1 : privbcb_req->lsn_valid));
1417 if ((privbcb=privbcb_req) && !privbcb_req->lsn_valid) {
1418 g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
1419 g_assert(privbcb->leave_func_pending==TRUE);
1420 g_assert(privbcb->ref_count==1);
1422 captive_privbcb_flush(privbcb);
1423 CcUnpinData_leave_func_finalize(privbcb);
1424 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
1427 g_assert(!privbcb_req || privbcb_req==g_tree_lookup(private_bcb_lsn_tree,privbcb_req));
1429 private_bcb_lsn_tree_init();
1432 CcUnpinData_leave_func_param.privbcb_req=privbcb_req;
1433 CcUnpinData_leave_func_param.found=NULL;
1434 g_tree_foreach(private_bcb_lsn_tree,
1435 (GTraverseFunc)CcUnpinData_leave_func_foreach_get_first, /* func */
1436 &CcUnpinData_leave_func_param); /* user_data */
1437 if (CcUnpinData_leave_func_param.found==NULL) {
1438 g_assert(privbcb_req==NULL); /* Global LSN-ordered flush has no more entries. */
1441 privbcb=CcUnpinData_leave_func_param.found;
1443 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1444 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
1445 ",privbcb->leave_func_pending=%d",G_STRLOC,
1446 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
1447 (int)privbcb->leave_func_pending);
1449 if (privbcb->dirty) {
1450 captive_privbcb_flush(privbcb);
1451 continue; /* Restart as anything could change by calling W32 hassle. */
1454 if (privbcb!=privbcb_req)
1455 continue; /* We just flushed a LSNed buffer not yet requested to be destroyed. */
1457 g_assert(privbcb==g_tree_lookup(private_bcb_lsn_tree,privbcb));
1458 g_assert(privbcb->leave_func_pending==TRUE);
1459 g_assert(privbcb->ref_count==1);
1460 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
1461 CcUnpinData_leave_func_finalize(privbcb);
1465 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
1470 * @Bcb: Initialized #PUBLIC_BCB structure.
1471 * %NULL value is forbidden.
1473 * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
1475 VOID CcUnpinData(IN PVOID Bcb)
1477 PUBLIC_BCB *PublicBcb;
1478 struct private_bcb *privbcb;
1480 g_return_if_fail(validate_Bcb(Bcb));
1482 private_bcb_hash_init();
1484 PublicBcb=(PUBLIC_BCB *)Bcb;
1485 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1486 g_return_if_fail(privbcb!=NULL);
1487 g_assert(!privbcb->leave_func_pending);
1489 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
1490 /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
1492 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1493 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
1494 ",privbcb->leave_func_pending=%d",G_STRLOC,
1495 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
1496 (int)privbcb->leave_func_pending);
1498 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1499 if (privbcb->ref_count>0)
1502 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
1503 /* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
1504 if (privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb)
1505 privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
1507 /* Caboom: lfs (log file system) of ntfs.sys-NT5.1sp1 will do:
1508 * CcPinRead(); CcUnpinData(); access Buffer (wanna-crash);
1509 * Therefore we must postpone the buffer unmapping to some idle function...
1510 * I expect it a bug in ntfs.sys.
1512 if (!privbcb->leave_func_pending) {
1513 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1); /* '+1' gets decreased by 'leave_func_pending'. */
1514 privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,TRUE);
1515 captive_leave_register(
1516 (captive_leave_func)CcUnpinData_leave_func, /* func */
1517 privbcb); /* data; privbcb_req */
1520 if (captive_cc_unmounting)
1525 VOID CcRepinBcb(IN PVOID Bcb)
1527 PUBLIC_BCB *PublicBcb;
1528 struct private_bcb *privbcb;
1530 g_return_if_fail(validate_Bcb(Bcb));
1532 private_bcb_hash_init();
1534 PublicBcb=(PUBLIC_BCB *)Bcb;
1535 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1536 g_return_if_fail(privbcb!=NULL);
1538 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
1539 Bcb,privbcb->FileObject);
1541 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1545 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
1547 PUBLIC_BCB *PublicBcb;
1548 struct private_bcb *privbcb;
1550 g_return_if_fail(validate_Bcb(Bcb));
1551 g_return_if_fail(IoStatus!=NULL);
1553 private_bcb_hash_init();
1555 PublicBcb=(PUBLIC_BCB *)Bcb;
1556 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1557 g_return_if_fail(privbcb!=NULL);
1559 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
1560 Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
1562 /* FIXME: Should we really flush the whole 'Bcb'?
1563 * Or maybe just some writes between CcRepinBcb(Bcb) and now? Who knows?
1566 captive_privbcb_flush(privbcb);
1568 IoStatus->Status=STATUS_SUCCESS;
1569 IoStatus->Information=privbcb->MappedLength;
1575 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
1577 PUBLIC_BCB *PublicBcb;
1578 struct private_bcb *privbcb;
1580 g_return_if_fail(validate_Bcb(Bcb));
1582 private_bcb_hash_init();
1584 PublicBcb=(PUBLIC_BCB *)Bcb;
1585 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1586 g_return_if_fail(privbcb!=NULL);
1588 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
1589 Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
1591 /* 'privbcb->ref_count' not to be increased by this function. */
1595 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,TRUE);
1598 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,TRUE);
1602 struct CcSetFileSizes_param {
1603 PFILE_OBJECT FileObject;
1604 PCC_FILE_SIZES FileSizes;
1607 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
1608 static void CcSetFileSizes_private_bcb_hash_foreach(
1609 PUBLIC_BCB *PublicBcb, /* key */
1610 struct private_bcb *privbcb, /* value */
1611 struct CcSetFileSizes_param *CcSetFileSizes_param) /* user_data */
1613 g_return_if_fail(PublicBcb!=NULL);
1614 g_return_if_fail(privbcb!=NULL);
1615 g_return_if_fail(CcSetFileSizes_param!=NULL);
1617 if (privbcb->FileObject!=CcSetFileSizes_param->FileObject)
1620 /* size changes behind our cached range? */
1622 && CcSetFileSizes_param->FileSizes->AllocationSize .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
1623 && CcSetFileSizes_param->FileSizes->FileSize .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
1624 && CcSetFileSizes_param->FileSizes->ValidDataLength.QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
1627 /* FIXME: check BCB && 'struct page_position' invalidities */
1628 g_assert_not_reached(); /* NOT IMPLEMENTED YET */
1633 * @FileObject: Initialized open #FileObject to update file sizes of.
1634 * %NULL value is forbidden.
1635 * @FileSizes: New file sizes to update cache to.
1636 * %NULL value is forbidden.
1638 * Update cache properties after file sizes were updated.
1639 * Probably only the exceeding pages need to be unmapped and BCBs updated
1640 * if FileSizes->AllocationSize gets shrunk.
1642 * FIXME: Currently a NOP with no effect by libcaptive.
1644 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
1646 struct CcSetFileSizes_param CcSetFileSizes_param;
1648 g_return_if_fail(FileObject!=NULL);
1649 g_return_if_fail(FileSizes!=NULL);
1651 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
1652 "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
1653 FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
1654 (guint64)FileSizes->ValidDataLength.QuadPart);
1656 CcSetFileSizes_param.FileObject=FileObject;
1657 CcSetFileSizes_param.FileSizes=FileSizes;
1658 g_hash_table_foreach(
1659 private_bcb_hash, /* hash_table */
1660 (GHFunc)CcSetFileSizes_private_bcb_hash_foreach, /* func */
1661 &CcSetFileSizes_param); /* user_data */
1666 * CcPurgeCacheSection:
1667 * @SectionObjectPointer: Pointer specifying file to purge;
1668 * %NULL value is forbidden.
1669 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1670 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %TRUE return code in such case.
1671 * @FileOffset: Starting offset of the ranger to purge.
1672 * %NULL pointer is permitted and it means to purge the whole whole.
1673 * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
1674 * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
1675 * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
1677 * Drop any caching for shrunken file which is not being deleted.
1678 * libcaptive will no longer consider such #BCB as dirty.
1680 * Undocumented: It is required during %FSCTL_LOCK_VOLUME by ntfs.sys of NT-5.1sp1
1681 * to return %TRUE value if #SharedCacheMap value is %NULL.
1683 * Returns: %TRUE if the range was purged successfuly.
1685 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1686 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
1688 PUBLIC_BCB *PublicBcb;
1689 struct private_bcb *privbcb;
1691 g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
1692 if (SectionObjectPointer->SharedCacheMap==NULL)
1693 return TRUE; /* nothing to purge; never return FALSE for ntfs.sys of NT-5.1sp1! */
1694 g_return_val_if_fail(FileOffset==NULL,FALSE); /* NOT IMPLEMENTED YET */
1696 PublicBcb=SectionObjectPointer->SharedCacheMap;
1697 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1699 private_bcb_hash_init();
1701 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1702 g_return_val_if_fail(privbcb!=NULL,FALSE);
1704 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1705 "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
1706 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1707 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
1709 g_assert_not_reached();
1711 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE); /* purge it */
1719 * @FileObject: Initialized open #FileObject to map.
1720 * %NULL value is forbidden.
1721 * @FileOffset: The @FileObject file offset from where to map the region from.
1722 * Negative value is forbidden.
1723 * @Length: Requested length of the region to map from @FileObject.
1724 * Value %0 is permitted (no effect of this function call).
1725 * @Wait: Whether disk waiting is permitted for this function.
1726 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1727 * @Buffer: Address of memory region with already allocated memory of size @Length.
1728 * This address may not be %PAGE_SIZE aligned.
1729 * %NULL pointer is forbidden.
1730 * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1731 * %NULL pointer is forbidden.
1733 * Reads the specified region of @FileObject to the given @Buffer.
1734 * No on-demand loading is in effect.
1736 * Returns: %TRUE if the region was successfuly filled with @Length bytes.
1737 * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
1738 * @IoStatus.Information initialized by @Length if successful.
1740 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
1741 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
1747 g_return_val_if_fail(FileObject!=NULL,FALSE);
1748 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1749 g_return_val_if_fail(Buffer!=NULL,FALSE);
1750 g_return_val_if_fail(IoStatus!=NULL,FALSE);
1752 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1753 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1755 IoStatus->Status=STATUS_UNSUCCESSFUL;
1756 IoStatus->Information=0;
1760 FileObject, /* FileObject */
1761 FileOffset, /* FileOffset */
1762 Length, /* Length */
1764 &MappedBcb, /* Bcb */
1765 &MappedBuffer); /* Buffer */
1766 g_return_val_if_fail(errbool==TRUE,FALSE);
1768 memcpy(Buffer,MappedBuffer,Length);
1770 CcUnpinData(MappedBcb); /* no error code */
1773 IoStatus->Status=STATUS_SUCCESS;
1774 IoStatus->Information=Length;
1782 * @FileObject: Initialized open #FileObject to map.
1783 * %NULL value is forbidden.
1784 * @FileOffset: The @FileObject file offset from where to map the region from.
1785 * Negative value is forbidden.
1786 * @Length: Requested length of the region to map from @FileObject.
1787 * Value %0 is permitted (no effect of this function call).
1788 * @Wait: Whether disk waiting is permitted for this function.
1789 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1790 * @Buffer: Address of memory region with already allocated memory of size @Length.
1791 * This address may not be %PAGE_SIZE aligned.
1792 * %NULL pointer is forbidden.
1794 * Writes the specified region of the given @Buffer to @FileObject.
1796 * Returns: %TRUE if the region was successfuly written with @Length bytes.
1798 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
1799 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
1805 g_return_val_if_fail(FileObject!=NULL,FALSE);
1806 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1807 g_return_val_if_fail(Buffer!=NULL,FALSE);
1809 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
1810 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
1814 FileObject, /* FileObject */
1815 FileOffset, /* FileOffset */
1816 Length, /* Length */
1818 &MappedBcb, /* Bcb */
1819 &MappedBuffer); /* Buffer */
1820 g_return_val_if_fail(errbool==TRUE,FALSE);
1822 memcpy(MappedBuffer,Buffer,Length);
1824 CcSetDirtyPinnedData(
1825 MappedBcb, /* Bcb */
1827 CcUnpinData(MappedBcb); /* no error code */
1836 * @FileObject: Initialized open #FileObject to map.
1837 * %NULL value is forbidden.
1838 * @BytesToWrite: Amount of data to be asked whether it will be accepted.
1839 * Value %0 is permitted.
1840 * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
1841 * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
1843 * Asks cache manager if it would currently accept write request to @FileObject
1844 * of @BytesToWrite bytes with @Wait condition.
1845 * libcaptive will always accept any writes. This function is a NOP.
1847 * Returns: libcaptive always returns %TRUE.
1849 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
1851 g_return_val_if_fail(FileObject!=NULL,FALSE);
1853 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
1854 FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
1861 * CcSetReadAheadGranularity:
1862 * @FileObject: Initialized open #FileObject to map.
1863 * %NULL value is forbidden.
1864 * @Granularity: Suggested size of the cache element.
1865 * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
1867 * libcaptive does not implement any caching and therefore this function
1870 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
1872 g_return_if_fail(FileObject!=NULL);
1873 g_return_if_fail(Granularity>=PAGE_SIZE);
1874 g_return_if_fail((Granularity&(Granularity-1))==0); /* Power of two */
1876 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
1877 FileObject,(gulong)Granularity);
1879 /* NOP; no caching by libcaptive */
1885 * @SectionObjectPointer: Pointer specifying file to flush;
1886 * %NULL value is forbidden.
1887 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1888 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
1889 * @FileOffset: Optional starting point of the range to flush.
1890 * %NULL value is permitted.
1891 * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
1892 * @IoStatus: Optionally returns the resulting operation status.
1893 * #Information field will contain the number of bytes flushed.
1894 * %NULL value is permitted.
1896 * Flushes out any pending dirty data in cache manager BCB mapping.
1897 * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
1899 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1900 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
1902 PUBLIC_BCB *PublicBcb;
1903 struct private_bcb *privbcb;
1904 IO_STATUS_BLOCK IoStatus_CcUnpinRepinnedBcb_local;
1906 g_return_if_fail(SectionObjectPointer!=NULL);
1908 if (SectionObjectPointer->SharedCacheMap==NULL) {
1911 IoStatus->Status=STATUS_SUCCESS;
1912 IoStatus->Information=0;
1917 PublicBcb=SectionObjectPointer->SharedCacheMap;
1918 g_return_if_fail(validate_Bcb(PublicBcb));
1920 private_bcb_hash_init();
1922 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1923 g_return_if_fail(privbcb!=NULL);
1925 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1926 "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
1927 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1928 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
1931 if (FileOffset->QuadPart >=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
1933 if (FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart)
1937 /* We may find some 'privbcb' being already sheduled to be destroyed.
1938 * We need to reference it to not to loose it in the middle of our flush operation.
1940 CcRepinBcb(PublicBcb);
1942 /* FIXME: Flush just FileOffset..FileOfset+Length part */
1943 captive_privbcb_flush(privbcb);
1946 PublicBcb, /* Bcb */
1947 TRUE, /* WriteThrough; ignored by libcaptive */
1948 &IoStatus_CcUnpinRepinnedBcb_local); /* IoStatus; ignored here */
1951 IoStatus->Status=STATUS_SUCCESS;
1952 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
1957 BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
1959 SECTION_OBJECT_POINTERS *SectionObjectPointers_orig;
1964 g_return_val_if_fail(FileObject!=NULL,FALSE);
1965 g_return_val_if_fail(StartOffset!=NULL,FALSE);
1966 g_return_val_if_fail(EndOffset!=NULL,FALSE);
1967 g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
1968 g_return_val_if_fail((EndOffset->QuadPart-StartOffset->QuadPart)
1969 ==(ULONG)(EndOffset->QuadPart-StartOffset->QuadPart),FALSE);
1971 SectionObjectPointers_orig=FileObject->SectionObjectPointers;
1973 errboolean=CcPreparePinWrite(
1974 FileObject, /* FileObject */
1975 StartOffset, /* FileOffset */
1976 EndOffset->QuadPart-StartOffset->QuadPart, /* Length */
1978 PIN_WAIT|PIN_NO_READ, /* Flags */
1980 &Buffer); /* Buffer */
1981 g_assert(errboolean==TRUE);
1985 FileObject->SectionObjectPointers=SectionObjectPointers_orig;
1991 /* map (PVOID LogHandle) -> (GList (of FILE_OBJECT *) *FileObject_list) */
1992 static GHashTable *log_handle_hash;
1994 static void log_handle_hash_init(void)
1996 if (log_handle_hash)
1998 log_handle_hash=g_hash_table_new(
1999 g_direct_hash, /* hash_func */
2000 g_direct_equal); /* key_equal_func */
2003 /* map (FILE_OBJECT *FileObject) -> (struct FileObject_logging *) */
2004 struct FileObject_logging {
2006 PFLUSH_TO_LSN FlushToLsnRoutine;
2009 static GHashTable *FileObject_logging_hash;
2011 static void FileObject_logging_hash_init(void)
2013 if (FileObject_logging_hash)
2015 FileObject_logging_hash=g_hash_table_new(
2016 g_direct_hash, /* hash_func */
2017 g_direct_equal); /* key_equal_func */
2020 static void logging_notify_privbcb_flush(struct private_bcb *privbcb)
2022 struct FileObject_logging *FileObject_logging;
2024 g_return_if_fail(privbcb!=NULL);
2026 if (!privbcb->lsn_valid) /* nothing to report anyway */
2029 FileObject_logging_hash_init();
2031 if (!(FileObject_logging=g_hash_table_lookup(FileObject_logging_hash,privbcb->FileObject)))
2034 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: call",G_STRLOC,
2035 privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
2037 (*FileObject_logging->FlushToLsnRoutine)(FileObject_logging->LogHandle,privbcb->lsn);
2039 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: finish",G_STRLOC,
2040 privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
2043 VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
2045 GList *LogHandle_list;
2046 struct FileObject_logging *FileObject_logging;
2048 g_return_if_fail(FileObject!=NULL);
2049 /* 'LogHandle' may be NULL */
2050 g_return_if_fail(FlushToLsnRoutine!=NULL);
2052 log_handle_hash_init();
2053 FileObject_logging_hash_init();
2055 if (!(FileObject_logging=g_hash_table_lookup(
2056 FileObject_logging_hash,FileObject))) {
2057 captive_new(FileObject_logging);
2058 g_hash_table_insert(FileObject_logging_hash,
2059 FileObject,FileObject_logging);
2062 LogHandle_list=g_hash_table_lookup(log_handle_hash,FileObject_logging->LogHandle);
2063 g_assert(NULL!=g_list_find(LogHandle_list,FileObject));
2064 LogHandle_list=g_list_remove(LogHandle_list,FileObject);
2065 g_assert(NULL==g_list_find(LogHandle_list,FileObject));
2066 g_hash_table_insert(log_handle_hash,FileObject_logging->LogHandle,LogHandle_list);
2069 FileObject_logging->LogHandle=LogHandle;
2070 FileObject_logging->FlushToLsnRoutine=FlushToLsnRoutine;
2072 LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
2073 LogHandle_list=g_list_prepend(LogHandle_list,FileObject);
2074 g_hash_table_insert(log_handle_hash,LogHandle,LogHandle_list);
2078 struct CcGetDirtyPages_param {
2079 PDIRTY_PAGE_ROUTINE DirtyPageRoutine; /* arg of CcGetDirtyPages() */
2080 IN PVOID Context1; /* arg of CcGetDirtyPages() */
2081 IN PVOID Context2; /* arg of CcGetDirtyPages() */
2082 FILE_OBJECT *FileObject; /* search through 'page_position_hash' for 'FileObject' */
2083 LARGE_INTEGER OldestLsn; gboolean OldestLsn_found; /* intermediate return value of CcGetDirtyPages() */
2086 static void CcGetDirtyPages_page_position_hash_foreach(
2087 struct page_position *pagepos, /* key */
2088 struct page_position *pagepos2, /* value */
2089 struct CcGetDirtyPages_param *CcGetDirtyPages_param) /* user_data */
2091 LARGE_INTEGER FileOffset_local;
2092 LARGE_INTEGER OldestLsn,NewestLsn;
2093 LARGE_INTEGER OldestLsn_check,NewestLsn_check;
2094 gboolean lsn_found=FALSE;
2095 GList *privbcb_list;
2097 g_return_if_fail(pagepos!=NULL);
2098 g_return_if_fail(pagepos==pagepos2);
2099 g_return_if_fail(CcGetDirtyPages_param!=NULL);
2101 FileOffset_local=pagepos->FileOffset;
2104 privbcb_list=pagepos->privbcb_list;
2106 privbcb_list=privbcb_list->next) {
2107 struct private_bcb *privbcb=privbcb_list->data;
2109 if (!privbcb->lsn_valid)
2112 OldestLsn=NewestLsn=privbcb->lsn;
2115 OldestLsn.QuadPart=MIN(OldestLsn.QuadPart,privbcb->lsn.QuadPart);
2116 NewestLsn.QuadPart=MAX(NewestLsn.QuadPart,privbcb->lsn.QuadPart);
2122 if (!CcGetDirtyPages_param->OldestLsn_found) {
2123 CcGetDirtyPages_param->OldestLsn=OldestLsn;
2124 CcGetDirtyPages_param->OldestLsn_found=TRUE;
2127 CcGetDirtyPages_param->OldestLsn.QuadPart=MIN(CcGetDirtyPages_param->OldestLsn.QuadPart,OldestLsn.QuadPart);
2129 OldestLsn_check=OldestLsn;
2130 NewestLsn_check=NewestLsn;
2132 (*CcGetDirtyPages_param->DirtyPageRoutine)(
2133 pagepos->FileObject, /* FileObject */
2134 &FileOffset_local, /* FileOffset */
2135 PAGE_SIZE, /* Length */
2136 &OldestLsn, /* OldestLsn */
2137 &NewestLsn, /* NewestLsn */
2138 CcGetDirtyPages_param->Context1, /* Context1 */
2139 CcGetDirtyPages_param->Context2); /* Context2 */
2141 /* just unconfirmed sanity: */
2142 g_assert(FileOffset_local.QuadPart==pagepos->FileOffset.QuadPart); /* check for possible corruption */
2143 g_assert(OldestLsn_check.QuadPart==OldestLsn.QuadPart);
2144 g_assert(NewestLsn_check.QuadPart==NewestLsn.QuadPart);
2147 /* libcaptive must return #gint64 instead of the official #LARGE_INTEGER
2148 * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
2150 gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
2151 IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
2153 GList *LogHandle_list;
2154 struct CcGetDirtyPages_param CcGetDirtyPages_param;
2156 /* 'LogHandle' may be NULL */
2157 g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
2159 log_handle_hash_init();
2160 page_position_hash_init();
2162 CcGetDirtyPages_param.DirtyPageRoutine=DirtyPageRoutine;
2163 CcGetDirtyPages_param.Context1=Context1;
2164 CcGetDirtyPages_param.Context2=Context2;
2165 CcGetDirtyPages_param.OldestLsn_found=FALSE;
2168 LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
2170 LogHandle_list=LogHandle_list->next) {
2171 CcGetDirtyPages_param.FileObject=LogHandle_list->data;
2172 g_hash_table_foreach(
2173 page_position_hash, /* hash_table */
2174 (GHFunc)CcGetDirtyPages_page_position_hash_foreach, /* func */
2175 &CcGetDirtyPages_param); /* user_data */
2178 if (!CcGetDirtyPages_param.OldestLsn_found)
2180 return CcGetDirtyPages_param.OldestLsn.QuadPart;
2185 * CcSetAdditionalCacheAttributes:
2186 * @FileObject: Initialized open #FileObject to map.
2187 * %NULL value is forbidden.
2188 * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
2189 * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
2191 * libcaptive does not implement any caching and therefore this function
2194 VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
2196 g_return_if_fail(FileObject!=NULL);
2198 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
2199 FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
2201 /* NOP; no caching by libcaptive */
2206 * CcSetBcbOwnerPointer:
2207 * @Bcb: Initialized #PUBLIC_BCB structure.
2208 * %NULL value is forbidden.
2209 * @Owner: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
2210 * %NULL value is forbidden (FIXME: Is it W32 compliant?).
2212 * Set thread-specific pointer for a pinned @Bcb. Use CcUnpinDataForThread()
2213 * when @Bcb is no longer needed. CcUnpinDataForThread() is NOT a reverse
2214 * operation for this single call CcSetBcbOwnerPointer(), see CcUnpinDataForThread().
2216 * libcaptive implements this function as no-operation as it does not yet
2217 * support any threading.
2219 VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
2221 g_return_if_fail(Bcb!=NULL);
2222 g_return_if_fail(Owner!=NULL);
2224 /* FIXME:thread; NOP if no threads present */
2229 * CcUnpinDataForThread:
2230 * @Bcb: Initialized #PUBLIC_BCB structure.
2231 * %NULL value is forbidden.
2232 * @ResourceThreadId: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
2233 * This pointer had to be passed to CcSetBcbOwnerPointer() #Owner parameter previously.
2234 * %NULL value is forbidden (FIXME: is it W32 compliant?).
2236 * CcUnpinData() for a thread specified by @ResourceThreadId.
2237 * Reverse operation for a pair of CcMapData() and CcSetBcbOwnerPointer().
2239 * libcaptive implements this function as a simple pass to CcUnpinData() as it does not yet
2240 * support any threading.
2242 VOID CcUnpinDataForThread(IN PVOID Bcb,IN ERESOURCE_THREAD ResourceThreadId)
2244 g_return_if_fail(Bcb!=NULL);
2245 g_return_if_fail(ResourceThreadId!=0);
2255 * @Bcb: Initialized #PUBLIC_BCB structure.
2256 * %NULL value is forbidden.
2258 * Create a copy of @Bcb for the exactly same file contents as is @Bcb.
2259 * The returned copy has the same attributes as the result of CcMapData()
2260 * notwithstanding the current state of input @Bcb, therefore it is only
2261 * for read/only access etc.
2263 * libcaptive calls CcMapData() internally with @Bcb parameters.
2265 * Returns: Copy of @Bcb. This _pointer_ never equals to @Bcb.
2266 * It should be some different
2267 * #PUBLIC_BCB structure according to W32 doc.
2269 PVOID CcRemapBcb(IN PVOID Bcb)
2272 PVOID Buffer_unused;
2274 PUBLIC_BCB *PublicBcb;
2275 struct private_bcb *privbcb;
2277 g_return_val_if_fail(validate_Bcb(Bcb),NULL);
2279 private_bcb_hash_init();
2281 PublicBcb=(PUBLIC_BCB *)Bcb;
2282 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
2283 g_return_val_if_fail(privbcb!=NULL,NULL);
2285 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
2286 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d",G_STRLOC,
2287 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count);
2290 privbcb->FileObject, /* FileObject */
2291 &privbcb->MappedFileOffset, /* FileOffset */
2292 privbcb->MappedLength, /* Length */
2293 MAP_WAIT, /* Flags; && !MAP_NO_READ */
2295 &Buffer_unused); /* Buffer */
2296 g_return_val_if_fail(errbool==TRUE,NULL);
2297 g_return_val_if_fail(r!=NULL,NULL);
2298 g_return_val_if_fail(Buffer_unused!=NULL,NULL);
2306 * CcPreparePinWrite:
2307 * @FileObject: Initialized open #FileObject to map.
2308 * %NULL value is forbidden.
2309 * @FileOffset: The @FileObject file offset from where to map the region from.
2310 * Negative value is forbidden.
2311 * @Length: Requested length of the region to map from @FileObject.
2312 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
2313 * @Zero: %TRUE if the area of @FileOffset...@FileOffset+@Length should be cleared.
2314 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
2315 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
2316 * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
2317 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
2318 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
2319 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
2320 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
2321 * %NULL pointer is forbidden.
2322 * @Buffer: Returns the mapped memory region start address.
2323 * This address may not be %PAGE_SIZE aligned.
2324 * %NULL pointer is forbidden.
2326 * Wrapper for a pair of CcPinRead() and CcSetDirtyPinnedData().
2327 * The mapped range can be also optionally cleared if @Zero is specified.
2328 * See CcPinRead() for a more detailed documentation.
2330 * FIXME: libcaptive will read even the file pages completely not need
2331 * in the case @Zero was specified.
2333 * Returns: %TRUE if the mapping was successful.
2335 BOOLEAN CcPreparePinWrite(IN PFILE_OBJECT FileObject,
2336 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Zero,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
2340 g_return_val_if_fail(FileObject!=NULL,FALSE);
2341 g_return_val_if_fail(FileOffset!=NULL,FALSE);
2342 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
2343 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
2344 /* 'Flags' passed to CcPinRead() */
2345 g_return_val_if_fail(Bcb!=NULL,FALSE);
2346 g_return_val_if_fail(Buffer!=NULL,FALSE);
2348 errbool=CcPinRead(FileObject,FileOffset,Length,Flags|(Zero ? PIN_NO_READ : 0),Bcb,Buffer);
2349 g_return_val_if_fail(errbool==TRUE,FALSE);
2351 CcSetDirtyPinnedData(
2353 NULL); /* Lsn; OPTIONAL */
2356 memset(*Buffer,0,Length);
2363 VOID FsRtlIncrementCcFastReadNoWait(VOID)
2365 /* FIXME: {{%fs:[0]}+0x4E0}:LONG++ */
2369 NTSTATUS CcWaitForCurrentLazyWriterActivity(VOID)
2371 return STATUS_SUCCESS;