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/gmain.h>
37 #include "captive/leave.h"
38 #include <glib/gtree.h>
39 #include "reactos/ddk/obfuncs.h" /* for ObReferenceObject() */
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 /* Does each privbcb put one reference count on its FileObject?
60 * Causes ntfs.sys of NT-5.1sp1:
61 * Assertion 'Header->ValidDataLength.QuadPart == Li0.QuadPart' failed at d:\xpsp1\base\fs\ntfs\write.c line 1749
63 /* #define CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT 1 */
66 /* Flush all the buffers directly on the fly.
67 * For details see the comment in libcaptive/client/init.c/captive_shutdown().
69 gboolean captive_cc_unmounting=FALSE;
72 /* map: (FILE_OBJECT *)FileObject -> (struct fileobject_cached *) */
73 static GHashTable *fileobject_cached_hash;
75 struct fileobject_cached {
76 CACHE_MANAGER_CALLBACKS CallBacks;
77 VOID *LazyWriterContext;
78 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
83 static void fileobject_cached_hash_value_destroy_func(struct fileobject_cached *value)
85 g_return_if_fail(value!=NULL);
90 static void fileobject_cached_hash_init(void)
92 if (fileobject_cached_hash)
94 fileobject_cached_hash=g_hash_table_new_full(
95 g_direct_hash, /* hash_func */
96 g_direct_equal, /* key_equal_func */
97 NULL, /* key_destroy_func */
98 (GDestroyNotify)fileobject_cached_hash_value_destroy_func); /* value_destroy_func */
103 * CcInitializeCacheMap:
104 * @FileObject: Existing file to set callbacks for. Ignored by libcaptive.
105 * %NULL value is forbidden.
106 * @FileSizes: Some file sizes suggestions. Ignored by libcaptive.
107 * %NULL value is forbidden.
108 * @PinAccess: CcPin*() functions will be used with @FileObject? Ignored by libcaptive.
109 * @CallBacks: Provided callback functions for readahead/writebehind. Ignored by libcaptive.
110 * %NULL value is forbidden.
111 * @LazyWriterContext: Value passed to functions of @CallBacks to bind with @FileObject.
112 * %NULL value is permitted.
114 * Provides support of readahead/writebehind in W32. Function should be called
115 * by W32 filesystem to offer its functions to W32 kernel. These functions
116 * are never called by libcaptive, this call is a NOP in libcaptive.
118 * VERIFIED: Double CcInitializeCacheMap() without CcUninitializeCacheMap().
120 VOID CcInitializeCacheMap(IN PFILE_OBJECT FileObject,
121 IN PCC_FILE_SIZES FileSizes,IN BOOLEAN PinAccess,IN PCACHE_MANAGER_CALLBACKS CallBacks,IN PVOID LazyWriterContext)
123 struct fileobject_cached *fileobject_cached;
124 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
126 LARGE_INTEGER FileOffset0;
131 g_return_if_fail(FileObject!=NULL);
132 g_return_if_fail(FileSizes!=NULL);
133 g_return_if_fail(CallBacks!=NULL);
135 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
136 "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX,"
137 "PinAccess=%d,CallBacks=%p,LazyWriterContext=%p",G_STRLOC,
138 FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
139 (guint64)FileSizes->ValidDataLength.QuadPart,(gint)PinAccess,CallBacks,LazyWriterContext);
141 fileobject_cached_hash_init();
145 /* Double reinitialization without CcUninitializeCacheMap()
146 * is done by both fastfat.sys and ntfs.sys of NT-5.1sp1.
149 g_assert(g_hash_table_lookup(fileobject_cached_hash,FileObject)==NULL);
152 captive_new(fileobject_cached);
153 fileobject_cached->CallBacks=*CallBacks;
154 fileobject_cached->LazyWriterContext=LazyWriterContext;
156 g_hash_table_insert(fileobject_cached_hash,
157 FileObject, /* key */
158 fileobject_cached); /* value */
160 #if CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
161 size64=MAX(MAX(FileSizes->AllocationSize.QuadPart,FileSizes->FileSize.QuadPart),
162 (FileSizes->ValidDataLength.QuadPart==G_MAXINT64 ? 0 : FileSizes->ValidDataLength.QuadPart));
163 g_assert(((ULONG)size64)==size64);
164 FileOffset0.QuadPart=0;
166 errboolean=CcMapData(FileObject,&FileOffset0,size64,MAP_WAIT,&fileobject_cached->Bcb,&Buffer_trash);
167 g_assert(errboolean==TRUE);
172 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject);
174 static void CcUninitializeCacheMap_private_bcb_hash_foreach(
175 PUBLIC_BCB *PublicBcb, /* key */
176 struct private_bcb *privbcb, /* value */
177 FILE_OBJECT *FileObject); /* user_data */
178 extern GHashTable *private_bcb_hash;
181 * CcUninitializeCacheMap:
182 * @FileObject: File to close caching for.
183 * %NULL value is forbidden.
184 * @TruncateSize: Current file size if @FileObject was truncated to it in the meantime.
185 * @UninitializeCompleteEvent: Optional event to signal after physical write to disk.
186 * %NULL value is permitted.
188 * Close the cachine facilities from CcInitializeCacheMap().
189 * It is valid to pass @FileObject without being registered by CcInitializeCacheMap().
191 * FIXME; What to do with files with dirty blocks? Currently we fail assertion on them
192 * although I think W32 would drop such buffers (purge them - not flush them).
194 * Returns: %TRUE if the caching was closed successfuly.
195 * %FALSE if @FileObject wasn't registered by CcInitializeCacheMap().
197 BOOLEAN CcUninitializeCacheMap(IN PFILE_OBJECT FileObject,
198 IN PLARGE_INTEGER TruncateSize OPTIONAL,IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
202 g_return_val_if_fail(FileObject!=NULL,FALSE);
203 /* assert current size ==*TruncateSize if TruncateSize */
205 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,TruncateSize=0x%llX,UninitializeCompleteEvent=%p",
206 G_STRLOC,FileObject,(guint64)(!TruncateSize ? -1 : TruncateSize->QuadPart),UninitializeCompleteEvent);
209 /* Here it will check for any dirty blocks and it will fail g_assert() on them.
210 * I think it may happen for BCBs to exist dirty for 'FileObject' and they
211 * should be silently discarded if CcUninitializeCacheMap() but currently
212 * the assertion is left here as some debugging aid.
215 r=captive_cc_FileObject_delete(FileObject);
217 r=g_hash_table_remove(fileobject_cached_hash,FileObject);
218 g_hash_table_foreach(
219 private_bcb_hash, /* hash_table */
220 (GHFunc)CcUninitializeCacheMap_private_bcb_hash_foreach, /* func */
221 FileObject); /* user_data */
224 /* FIXME: should we do KePulseEvent? Are we allowed to signal from inside CcUninitializeCacheMap() ? */
225 if (UninitializeCompleteEvent)
227 &UninitializeCompleteEvent->Event, /* Event */
228 IO_NO_INCREMENT, /* Increment */
235 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
237 g_return_val_if_fail(PublicBcb!=NULL,FALSE);
238 g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
239 g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
240 g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
241 g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
247 /* position in file */
248 struct page_position {
249 FILE_OBJECT *FileObject;
250 LARGE_INTEGER FileOffset; /* always PAGE_SIZE aligned */
252 GList *privbcb_list; /* each mapped page has its one private_bcb */
253 gboolean building; /* data are not yet read; prevention for CcMapData() reentrancy */
256 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
257 static GHashTable *page_position_hash;
259 static gboolean validate_page_position(const struct page_position *pagepos)
262 struct shmid_ds shmid_ds;
264 g_return_val_if_fail(pagepos!=NULL,FALSE);
265 g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
266 g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
267 g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
268 /* 'pagepos->shmid' may be -1 */
269 /* 'pagepos->privbcb_list' may be empty */
270 /* either deleted or alive */
271 /* shmid exists only for >=2 mappings, it is simple mmap() for single mapping */
272 g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL || pagepos->privbcb_list->next==NULL),FALSE);
274 if (pagepos->shmid!=-1) {
275 errint=shmctl(pagepos->shmid,
277 &shmid_ds); /* buf */
278 g_return_val_if_fail(errint==0,FALSE);
280 g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
281 g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
282 g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
283 g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
284 /* 'shm_perm.mode' was seen with sticky bit 01000: */
285 g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
286 g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
287 /* Do not check 'shmid_ds.shm_cpid' and 'shmid_ds.shm_lpid' against getpid()
288 * as we may run as different PIDs in different threads synchronized by client/ .
290 g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
296 static guint page_position_hash_hash_func(const struct page_position *key)
298 g_return_val_if_fail(validate_page_position(key),0);
300 return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
303 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
305 g_return_val_if_fail(validate_page_position(a),FALSE);
306 g_return_val_if_fail(validate_page_position(b),FALSE);
308 return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
311 static void page_position_hash_key_destroy_func(struct page_position *key)
313 g_return_if_fail(validate_page_position(key));
314 g_assert(key->privbcb_list==NULL);
315 g_assert(key->shmid==-1);
320 static void page_position_hash_init(void)
322 if (page_position_hash)
324 page_position_hash=g_hash_table_new_full(
325 (GHashFunc)page_position_hash_hash_func, /* hash_func */
326 (GEqualFunc)page_position_hash_key_equal_func, /* key_equal_func */
327 (GDestroyNotify)page_position_hash_key_destroy_func, /* key_destroy_func */
328 NULL); /* value_destroy_func */
333 PUBLIC_BCB *PublicBcb; /* ->MappedLength, ->MappedFileOffset */
334 FILE_OBJECT *FileObject;
336 gboolean leave_func_pending;
337 /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
338 ULONG MappedLength; /* It is the real requested size; it is not PAGE_SIZE aligned. */
339 /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
340 LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
341 gpointer base; /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
343 LARGE_INTEGER lsn; gboolean lsn_valid;
344 gboolean forbid_CcUnpinData_leave_func_finalize_remove;
348 /* map: (struct private_bcb *)privbcb -> (struct private_bcb *)privbcb */
349 static GTree *private_bcb_lsn_tree;
351 /* Such debugging messages are too much expensive.
352 * Use to track for deallocated privbcb-s/PublicBcb-s.
354 /* #define DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC 1 */
356 /* Although 'private_bcb_lsn_tree' contains only nodes with 'lsn_valid' TRUE
357 * compare_func cannot expect such arguments as it may be used to lookup
358 * privbcb unlisted in 'private_bcb_lsn_tree'.
360 static gint private_bcb_lsn_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
365 #ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
366 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a=%p,privbcb b=%p",G_STRLOC,a,b);
367 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb=%p",G_STRLOC,a->PublicBcb);
368 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb=%p",G_STRLOC,b->PublicBcb);
369 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)a->PublicBcb->MappedLength);
370 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)b->PublicBcb->MappedLength);
373 g_return_val_if_fail(a!=NULL,0);
374 g_return_val_if_fail(validate_Bcb(a->PublicBcb),0);
375 g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MAXINT64); /* Forbid defined LSN as G_MAXINT64 */
376 g_return_val_if_fail(b!=NULL,0);
377 g_return_val_if_fail(validate_Bcb(b->PublicBcb),0);
378 g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MAXINT64); /* Forbid defined LSN as G_MAXINT64 */
380 if (a==b) { /* LSN would be apparently the same in such case :-) */
381 #ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
382 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return 0 (a==b)",G_STRLOC);
387 a_lsn=(!a->lsn_valid ? (LONGLONG)G_MAXINT64 : a->lsn.QuadPart);
388 b_lsn=(!b->lsn_valid ? (LONGLONG)G_MAXINT64 : b->lsn.QuadPart);
390 /* Forbid the same LSNs if both defined */
391 g_assert(a_lsn==(LONGLONG)G_MAXINT64 || a_lsn!=b_lsn);
393 if ((r=(a_lsn>b_lsn)-(a_lsn<b_lsn))) {
394 #ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
395 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d (lsn !=)",G_STRLOC,r);
400 #ifdef DEBUG_PRIVATE_BCB_LSN_TREE_KEY_COMPARE_FUNC
401 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return (ptr !=)",G_STRLOC);
406 static void private_bcb_lsn_tree_init(void)
408 if (private_bcb_lsn_tree)
410 private_bcb_lsn_tree=g_tree_new(
411 (GCompareFunc)private_bcb_lsn_tree_key_compare_func); /* key_compare_func */
415 static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req);
417 static void CcUninitializeCacheMap_private_bcb_hash_foreach(
418 PUBLIC_BCB *PublicBcb, /* key */
419 struct private_bcb *privbcb, /* value */
420 FILE_OBJECT *FileObject) /* user_data */
422 g_return_if_fail(validate_Bcb(PublicBcb));
423 g_return_if_fail(privbcb!=NULL);
424 g_return_if_fail(PublicBcb==privbcb->PublicBcb);
425 g_return_if_fail(FileObject!=NULL);
427 if (privbcb->FileObject!=FileObject)
430 captive_privbcb_flush_ordered(privbcb);
435 PRIVBCB_ITEM_NOP, /* for sanity checks */
437 PRIVBCB_ITEM_LSN_VALID,
438 PRIVBCB_ITEM_LEAVE_FUNC_PENDING,
439 PRIVBCB_ITEM_REF_COUNT,
440 PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR, /* no protections against 'leave_func_pending' */
443 static void privbcb_set(struct private_bcb *privbcb,enum privbcb_item item,gint value)
445 g_return_if_fail(privbcb!=NULL);
447 private_bcb_lsn_tree_init();
449 g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
451 case PRIVBCB_ITEM_NOP:
453 case PRIVBCB_ITEM_DIRTY:
454 g_assert(TRUE==value || FALSE==value);
455 privbcb->dirty=value;
457 case PRIVBCB_ITEM_LSN_VALID:
458 /* Never change 'privbcb->lsn_valid' while it is linked in 'private_bcb_lsn_tree'
459 * as it could become unreachable due to private_bcb_lsn_tree_key_compare_func()
460 * behaviour wrt 'privbcb->lsn_valid'!
462 g_tree_remove(private_bcb_lsn_tree,privbcb);
463 g_assert(TRUE==value || FALSE==value);
464 privbcb->lsn_valid=value;
465 if (privbcb->lsn_valid)
466 g_tree_insert(private_bcb_lsn_tree,
468 privbcb); /* value */
470 case PRIVBCB_ITEM_LEAVE_FUNC_PENDING:
471 g_assert(TRUE==value || FALSE==value);
472 privbcb->leave_func_pending=value;
474 g_assert(privbcb->ref_count==1);
476 case PRIVBCB_ITEM_REF_COUNT:
477 /* Forbid reincarnation of 'leave_func_pending' privbcb. */
478 g_assert(!privbcb->leave_func_pending);
479 g_assert(privbcb->ref_count>=1);
480 privbcb->ref_count+=value;
481 g_assert(privbcb->ref_count>=1);
483 case PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR:
484 g_assert(privbcb->ref_count>=0);
485 privbcb->ref_count+=value;
486 g_assert(privbcb->ref_count>=0);
488 default: g_assert_not_reached();
490 g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
494 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
495 static GHashTable *private_bcb_hash;
497 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
499 g_return_if_fail(validate_Bcb(key));
501 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: PublicBcb=%p",G_STRLOC,key);
506 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
508 struct page_position pagepos_local;
512 gpointer base_aligned;
514 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: privbcb=%p (->PublicBcb=%p)",G_STRLOC,value,value->PublicBcb);
516 g_return_if_fail(value!=NULL);
517 /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
518 * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
520 g_return_if_fail(value->PublicBcb!=NULL);
521 g_return_if_fail(value->FileObject!=NULL);
522 g_return_if_fail(value->ref_count==0);
523 g_return_if_fail(value->MappedLength>0);
524 g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
525 g_return_if_fail(value->base!=NULL);
526 g_return_if_fail(value->dirty==FALSE);
527 /* Ensure we are not registered in 'private_bcb_lsn_tree'. */
528 g_return_if_fail(value->lsn_valid==FALSE);
530 page_position_hash_init();
532 base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
534 pagepos_local.FileObject=value->FileObject;
535 pagepos_local.privbcb_list=NULL;
536 pagepos_local.shmid=-1;
539 offset<value->MappedLength;
541 struct page_position *pagepos;
543 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
544 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
545 g_assert(validate_page_position(pagepos));
546 g_assert(pagepos->privbcb_list!=NULL);
547 if (pagepos->privbcb_list->next==NULL) { /* single mapping by mmap(), no shm */
548 g_assert(pagepos->shmid==-1);
549 errint=munmap(((char *)base_aligned)+offset,PAGE_SIZE);
552 else if (pagepos->privbcb_list->next->next==NULL) { /* mapping 2 -> 1, convert shm to mmap() */
553 struct private_bcb *privbcb_other;
554 gint64 privbcb_other_offset_relative;
555 gpointer mapped_other,errptr;
557 g_assert(pagepos->shmid!=-1);
558 if (value==pagepos->privbcb_list->data)
559 privbcb_other=pagepos->privbcb_list->next->data;
561 privbcb_other=pagepos->privbcb_list->data;
562 g_assert(value==pagepos->privbcb_list->next->data);
564 privbcb_other_offset_relative=pagepos->FileOffset.QuadPart-privbcb_other->MappedFileOffset.QuadPart;
565 /* privbcb_other_offset_relative may be negative up to -PAGE_SIZE
566 * as 'MappedFileOffset' and 'base' are not page-aligned.
568 mapped_other=privbcb_other->base+privbcb_other_offset_relative;
569 /* TODO:thread; shmdt()..mmap() window */
570 errint=shmdt(mapped_other);
573 mapped_other, /* start */
574 PAGE_SIZE, /* length */
575 PROT_READ|PROT_WRITE, /* prot */
576 MAP_PRIVATE|MAP_ANONYMOUS, /* flags */
577 -1, /* fd; ignored due to MAP_ANONYMOUS */
578 0); /* offset; ignored due to MAP_ANONYMOUS */
579 g_assert(errptr==mapped_other);
580 memcpy(mapped_other,((char *)base_aligned)+offset,PAGE_SIZE);
581 errint=shmdt(((char *)base_aligned)+offset); /* destroys shm */
583 /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
586 else { /* mappings (>=3) -> (>=2) */
587 g_assert(pagepos->shmid!=-1);
588 errint=shmdt(((char *)base_aligned)+offset);
592 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
593 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
594 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
596 if (pagepos->privbcb_list==NULL) { /* last mapping by mmap() removed */
597 g_assert(pagepos->shmid==-1);
598 errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
599 g_assert(errbool==TRUE);
601 else /* mapping is now >=1, either by mmap() or shm */
602 g_assert(validate_page_position(pagepos));
605 #ifdef CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT
606 ObDereferenceObject(value->FileObject);
612 static void private_bcb_hash_init(void)
614 if (private_bcb_hash)
616 private_bcb_hash=g_hash_table_new_full(
617 g_direct_hash, /* hash_func */
618 g_direct_equal, /* key_equal_func */
619 (GDestroyNotify)private_bcb_hash_key_destroy_func, /* key_destroy_func */
620 (GDestroyNotify)private_bcb_hash_value_destroy_func); /* value_destroy_func */
624 static void CcUnpinData_leave_unregister(struct private_bcb *privbcb);
625 static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req);
627 static gboolean captive_cc_FileObject_delete_private_bcb_hash_foreach(
628 PUBLIC_BCB *PublicBcb, /* key */
629 struct private_bcb *privbcb, /* value */
630 FILE_OBJECT *FileObject) /* user_data */
632 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE); /* meaning: do not remove this 'privbcb' */
633 g_return_val_if_fail(privbcb!=NULL,FALSE); /* meaning: do not remove this 'privbcb' */
634 g_return_val_if_fail(PublicBcb==privbcb->PublicBcb,FALSE); /* meaning: do not remove this 'privbcb' */
635 g_return_val_if_fail(FileObject!=NULL,FALSE); /* meaning: do not remove this 'privbcb' */
637 if (FileObject && privbcb->FileObject!=FileObject)
638 return FALSE; /* do not remove this 'privbcb' */
640 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,PublicBcb=%p,privbcb=%p,privbcb->ref_count=%d"
641 ",privbcb->leave_func_pending=%d,privbcb->dirty=%d,privbcb->lsn=%" G_GINT64_FORMAT,G_STRLOC,
642 FileObject,PublicBcb,privbcb,privbcb->ref_count,
643 privbcb->leave_func_pending,privbcb->dirty,(gint64)(!privbcb->lsn_valid ? -1 : privbcb->lsn.QuadPart));
645 /* It is not OK to leave it in 'private_bcb_hash' as although it is protected
646 * against privbcb reincarnation its pagepos items could be reincarnated.
648 if (privbcb->leave_func_pending) {
650 g_assert_not_reached();
651 captive_privbcb_flush_ordered(privbcb);
652 return FALSE; /* do not remove this 'privbcb' */
653 g_assert_not_reached();
654 g_assert(privbcb->dirty==FALSE);
656 CcUnpinData_leave_unregister(privbcb);
657 g_assert(privbcb->leave_func_pending==FALSE);
660 /* Do not reuse captive_privbcb_flush_ordered() destructor here as we cannot
661 * call g_hash_table_remove() from inside g_hash_table_foreach_remove() callback.
664 g_assert(privbcb->dirty==FALSE); /* it would be fatal */
666 g_assert(privbcb->leave_func_pending==FALSE);
667 /* Force 'ref_count' drop to 0 to pass private_bcb_hash_value_destroy_func(). */
668 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-privbcb->ref_count);
669 /* Unset LSN to unlink from 'private_bcb_lsn_tree'. */
670 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
672 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: WARNING: Deleting file with pending (fortunately non-dirty) BCBs"
673 "; privbcb=%p,PublicBcb=%p,FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
674 privbcb,PublicBcb,privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
676 return TRUE; /* remove this 'privbcb' */
679 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
681 struct fileobject_cached *fileobject_cached;
684 /* 'FileObject' may be NULL to check/clear the whole cache. */
686 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p",G_STRLOC,FileObject);
688 fileobject_cached_hash_init();
689 private_bcb_hash_init();
691 if (!FileObject || !(fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,FileObject)))
694 #ifdef CAPTIVE_FILE_INITIALIZED_CACHE_IS_LINEAR
695 CcUnpinData(fileobject_cached->Bcb);
697 g_hash_table_remove(fileobject_cached_hash,FileObject);
700 g_hash_table_foreach_remove(
701 private_bcb_hash, /* hash_table */
702 (GHRFunc)captive_cc_FileObject_delete_private_bcb_hash_foreach, /* func */
703 FileObject); /* user_data */
705 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: returning r=%d",G_STRLOC,r);
711 static gboolean captive_privbcb_flush_unordered(struct private_bcb *privbcb);
713 static void captive_cc_flush_private_bcb_hash_foreach(
714 PUBLIC_BCB *PublicBcb, /* key */
715 struct private_bcb *privbcb, /* value */
716 gboolean *flushedp) /* user_data */
720 g_return_if_fail(validate_Bcb(PublicBcb));
721 g_return_if_fail(privbcb!=NULL);
722 g_return_if_fail(PublicBcb==privbcb->PublicBcb);
724 if (!privbcb->dirty) /* OK */
727 /* Such privbcb should have been flushed by captive_privbcb_flush_ordered(NULL) before
728 * to ensure its LSN ordering.
730 g_assert(!privbcb->lsn_valid);
733 errbool=captive_privbcb_flush_unordered(privbcb);
734 g_assert(errbool==TRUE);
737 void captive_cc_flush(void)
741 fileobject_cached_hash_init();
742 private_bcb_hash_init();
745 /* Trace it by g_tree first to attempt to keep LSN ordering */
746 flushed=captive_privbcb_flush_ordered(NULL);
747 g_hash_table_foreach(
748 private_bcb_hash, /* hash_table */
749 (GHFunc)captive_cc_flush_private_bcb_hash_foreach, /* func */
750 &flushed); /* user_data */
754 /* We must not call: captive_cc_FileObject_delete(NULL);
755 * here as it would destroy all the Bcbs needed for the forthcoming
756 * IoShutdownRegisteredFileSystems().
761 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
765 IO_STATUS_BLOCK IoStatus;
768 g_return_val_if_fail(FileObject!=NULL,0);
769 g_return_val_if_fail(address!=0,0);
770 g_return_val_if_fail(length!=0,0);
771 g_return_val_if_fail(FileOffset!=NULL,0);
773 /* VolumeRead on ext2fsd.sys will return IoStatus.Information==0 although it
774 * successfuly read the data. Workaround it - preclear (not postclear) the
775 * buffer and do not make any other assumptions about the data read.
777 memset(address,0,length); /* pre-clear the buffer */
778 Mdl=MmCreateMdl(NULL,address,length); /* FIXME: Deprecated in favor of IoAllocateMdl() */
780 MmBuildMdlForNonPagedPool(Mdl);
781 KeInitializeEvent(&Event,NotificationEvent,FALSE);
782 IoStatus.Information=0; /* preventive pre-clear for buggy filesystems */
783 err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
784 g_assert(NT_SUCCESS(err));
785 g_assert(NT_SUCCESS(IoStatus.Status));
786 /* It is not == as the file may be shorter than requested */
787 g_assert(IoStatus.Information<=length);
790 /* Forbidden, see the comment above about ext2fsd.sys.
791 * memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
794 return IoStatus.Information; /* may be shorter than real! */
798 static struct private_bcb *captive_privbcb_find(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,
799 PUBLIC_BCB *Bcb_find)
801 struct page_position pagepos_local,*pagepos;
802 struct private_bcb *privbcb,*privbcb_listitem;
805 g_return_val_if_fail(FileObject!=NULL,NULL);
806 g_return_val_if_fail(FileOffset!=NULL,NULL);
807 /* 'Bcb_find' may be NULL */
809 page_position_hash_init();
811 pagepos_local.FileObject=FileObject;
812 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
813 pagepos_local.privbcb_list=NULL;
814 pagepos_local.shmid=-1;
815 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local)))
817 g_assert(validate_page_position(pagepos));
818 g_assert(pagepos->privbcb_list!=NULL);
821 for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
822 privbcb_listitem=privbcb_list->data;
824 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
825 && privbcb_listitem->MappedLength==Length
826 && (!Bcb_find || Bcb_find==privbcb_listitem->PublicBcb)) {
827 g_assert(privbcb==NULL); /* appropriate 'Bcb_find'-matching privbcb found twice */
828 privbcb=privbcb_listitem;
836 /* Sanity check 'privbcb': */
837 g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
845 * @FileObject: Initialized open #FileObject to map.
846 * %NULL value is forbidden.
847 * @FileOffset: The @FileObject file offset from where to map the region from.
848 * Negative value is forbidden.
849 * @Length: Requested length of the region to map from @FileObject.
850 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
851 * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
852 * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
853 * %MAP_NO_READ will leave the pages unread (libcaptive: unread pages zeroed,
854 * already mapped pages shared with existing content) - needed by ntfs.sys of NT-5.1sp1
855 * as during file write it will %MAP_NO_READ and consequently push the data there;
856 * any request for the same file range read in the meantime will destroy the prepared data!
857 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
858 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
859 * %NULL pointer is forbidden.
860 * @Buffer: Returns the mapped memory region start address.
861 * This address may not be %PAGE_SIZE aligned.
862 * %NULL pointer is forbidden.
864 * Maps the specified region of @FileObject to automatically chosen address space.
865 * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
867 * WARNING: If you modify the data in the returned @Buffer you must call some CcPinMappedData()
868 * or CcPinRead() afterwards. W32 docs say you should never modify the data in any way from this function
869 * but W32 filesystems apparently do not conform to it. If you do not take care of the
870 * modified data by some dirty-marking facility such data will be carelessly dropped without
871 * their commit to the disk.
873 * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
875 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
877 * We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
878 * even if such mapping for such file already exists.
879 * We should probably create a new #Bcb for the same space,
880 * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
882 * Returns: %TRUE if the region was successfuly mapped.
883 * @Bcb with the initialized new memory region.
884 * @Buffer with the address of the exact byte specified by @FileOffset.
886 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
887 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
889 PUBLIC_BCB **PublicBcbp,*PublicBcb;
890 struct page_position pagepos_local;
891 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
892 gpointer base_aligned;
893 size_t length_mapped_aligned;
894 gint length_pages_aligned;
895 struct page_position **pagepos_array;
898 struct private_bcb *privbcb;
899 gboolean after_eof=FALSE; /* Did we reached the end of file already? */
901 g_return_val_if_fail(FileObject!=NULL,FALSE);
902 g_return_val_if_fail(FileOffset!=NULL,FALSE);
903 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
904 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
905 g_return_val_if_fail(Flags&MAP_WAIT,FALSE); /* FIXME: on-demand loading not yet implemented */
906 g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE); /* unknown flags? */
907 g_return_val_if_fail(Bcb!=NULL,FALSE);
908 g_return_val_if_fail(Buffer!=NULL,FALSE);
910 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
911 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
913 if (Flags&MAP_NO_READ) {
914 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE)); /* NOT IMPLEMENTED YET */
915 /* It may not be aligned as this function is prepared by 'after_eof' for incomplete tails.
916 * It is being used at least by ntfs.sys of NT-5.1sp1 CcCopyWrite().
919 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart+Length,PAGE_SIZE)); /* NOT IMPLEMENTED YET */
923 g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
924 g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
925 #if 0 /* SectorSize can be really weird but we do not use it anyway */
926 /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
927 * 'SectorSize' may not yet be initialized during mount operation
928 * and 'FileObject->DeviceObject->Vpb' may exist in such case.
930 g_return_val_if_fail(0
931 || FileObject->DeviceObject->SectorSize==0 /* prevent division by 0 */
932 || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),
936 page_position_hash_init();
937 private_bcb_hash_init();
939 PublicBcbp=(PUBLIC_BCB **)Bcb;
940 /* extend 'FileOffset' and 'Length' to page boundaries */
941 FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
942 FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
943 length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
944 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_mapped_aligned,PAGE_SIZE));
945 length_pages_aligned=length_mapped_aligned/PAGE_SIZE;
947 /* We can be called as full CcMapData() (e.g. CcPinRead() from fastfat.sys)
948 * even if such mapping for such file already exists.
949 * We should probably create a new Bcb for the same space,
950 * at least ntfs.sys of NT-5.1sp1 appears to expect it. Bleech.
952 if ((privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL)))
953 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX already mapped by privbcb %p",
954 G_STRLOC,FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,privbcb);
956 /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
957 /* TODO: on-demand loading */
958 /* Although we do zeroed-page mapping here we just reserve the linear
963 PAGE_SIZE+length_mapped_aligned+PAGE_SIZE, /* length; leading and trailing boundary check pages */
964 PROT_READ|PROT_WRITE, /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
965 MAP_PRIVATE|MAP_ANONYMOUS, /* flags */
966 -1, /* fd; ignored due to MAP_ANONYMOUS */
967 0); /* offset; ignored due to MAP_ANONYMOUS */
968 g_assert(base_aligned!=NULL);
970 base_aligned+=PAGE_SIZE;
971 errint=munmap(base_aligned-PAGE_SIZE,PAGE_SIZE); /* unmap leading boundary check page */
973 errint=munmap(base_aligned+length_mapped_aligned,PAGE_SIZE); /* unmap trailing boundary check page */
976 /* Create 'PublicBcb'; referenced by 'privbcb'. */
977 captive_new(PublicBcb);
978 PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
979 PublicBcb->NodeByteSize=sizeof(*PublicBcb); /* we have no extensions there */
980 PublicBcb->MappedLength=Length;
981 PublicBcb->MappedFileOffset=*FileOffset;
983 /* Create 'privbcb'; referenced by created 'pagepos'es. */
984 captive_new(privbcb);
985 privbcb->PublicBcb=PublicBcb;
986 privbcb->FileObject=FileObject;
987 privbcb->ref_count=1;
988 privbcb->leave_func_pending=FALSE;
989 privbcb->MappedLength=PublicBcb->MappedLength;
990 privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
991 privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
992 privbcb->dirty=FALSE;
993 privbcb->lsn_valid=FALSE;
994 privbcb->forbid_CcUnpinData_leave_func_finalize_remove=FALSE;
995 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_hash_table_insert: PublicBcb=%p,privbcb=%p",G_STRLOC,
997 g_hash_table_insert(private_bcb_hash,
999 privbcb); /* value */
1001 #ifdef CAPTIVE_PRIVBCB_REFERENCES_FILEOBJECT
1002 ObReferenceObject(FileObject);
1005 privbcb_set(privbcb,PRIVBCB_ITEM_NOP,0); /* just for the assertions */
1007 /* We MUST NOT call captive_Cc_IoPageRead() inside our pagepos filling loop
1008 * below as captive_Cc_IoPageRead() has very big consequences as it calls
1009 * the filesystem code and we may get reentrancy.
1010 * Therefore we store all missing page read requests to 'pagepos_array' and we
1011 * fill them when all the memory structures are in consistent state.
1012 * We can also coalescence the requests more easily this way.
1014 captive_newn_alloca(pagepos_array,length_pages_aligned);
1016 pagepos_local.FileObject=FileObject;
1017 pagepos_local.shmid=-1;
1018 pagepos_local.privbcb_list=NULL;
1024 offset<length_mapped_aligned;
1025 offset+=PAGE_SIZE) {
1027 struct page_position *pagepos;
1029 pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
1030 pageaddr=(gpointer)(((char *)base_aligned)+offset);
1031 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
1032 captive_new(pagepos);
1033 *pagepos=pagepos_local;
1035 pagepos->privbcb_list=NULL;
1036 pagepos->building=TRUE;
1037 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read of offset %llu to new pagepos %p of pure mmap() to address %p",
1038 G_STRLOC,(unsigned long long)offset,pagepos,pageaddr);
1040 else if (pagepos->privbcb_list->next==NULL) { /* exactly one item (no IPC shm yet) */
1041 g_assert(pagepos->shmid==-1);
1042 if (-1==(pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600)))
1043 g_error("%s: Failed shmget(2), you may be out of maximum system IPC shared pages",G_STRLOC);
1044 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",
1045 G_STRLOC,pagepos->shmid,(unsigned long long)offset,pagepos,pageaddr);
1046 /* Reentrancy occured? */
1047 g_assert(Flags&MAP_NO_READ || !pagepos->building);
1049 else { /* already >=2 mappings, already IPC shared mem */
1050 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",
1051 G_STRLOC,(unsigned long long)offset,pagepos,pagepos->shmid,pageaddr);
1052 g_assert(pagepos->shmid!=-1);
1053 g_assert(pagepos->privbcb_list!=NULL);
1054 /* Reentrancy occured? */
1055 g_assert(Flags&MAP_NO_READ || !pagepos->building);
1057 pagepos_array[offset/PAGE_SIZE]=pagepos;
1058 if (pagepos->privbcb_list) { /* Not the first one - some shm in use now? */
1059 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
1060 * mmap(2) is still needed to get linear block of memory assignment.
1062 /* TODO:thread; munmap()..shmat() window */
1063 errint=munmap(pageaddr,PAGE_SIZE);
1064 g_assert(errint==0);
1065 errptr=shmat(pagepos->shmid,
1066 pageaddr, /* shmaddr */
1067 0); /* shmflg; !SHM_RDONLY==r/w */
1068 g_assert(errptr==pageaddr);
1071 g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
1072 pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb); /* order not important */
1073 g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
1074 if (pagepos->privbcb_list->next==NULL) { /* exactly one item (we just added it now) */
1076 g_assert(pagepos->shmid==-1);
1077 g_hash_table_insert(page_position_hash,
1079 pagepos); /* value */
1081 else if (pagepos->privbcb_list->next->next==NULL) { /* second mapping - new shm */
1082 struct private_bcb *privbcb_orig;
1083 gint64 privbcb_orig_offset_relative;
1084 gpointer mapped_orig;
1086 g_assert(pagepos->shmid!=-1);
1087 errint=shmctl(pagepos->shmid,
1090 g_assert(errint==0);
1091 g_assert(pagepos->privbcb_list->data==privbcb); /* our privbcb should be the first */
1092 g_assert(pagepos->privbcb_list->next!=NULL); /* we have exactly two items in the list */
1093 g_assert(pagepos->privbcb_list->next->next==NULL); /* we have exactly two items in the list */
1094 privbcb_orig=pagepos->privbcb_list->next->data;
1095 privbcb_orig_offset_relative=pagepos->FileOffset.QuadPart-privbcb_orig->MappedFileOffset.QuadPart;
1096 /* privbcb_orig_offset_relative may be negative up to -PAGE_SIZE
1097 * as 'MappedFileOffset' and 'base' are not page-aligned.
1099 mapped_orig=privbcb_orig->base+privbcb_orig_offset_relative;
1100 memcpy(pageaddr,mapped_orig,PAGE_SIZE);
1101 /* TODO:thread; munmap()..shmat() window */
1102 errint=munmap(mapped_orig,PAGE_SIZE);
1103 g_assert(errint==0);
1104 errptr=shmat(pagepos->shmid,
1105 mapped_orig, /* shmaddr */
1106 0); /* shmflg; !SHM_RDONLY==r/w */
1107 g_assert(errptr==mapped_orig);
1109 g_assert(validate_page_position(pagepos));
1113 /* Fill in the missing pages content todo-list by stored 'pagepos_array' '->building' flags: */
1114 if (!(Flags&MAP_NO_READ)) {
1115 size_t offset_start,offset_end;
1119 offset_start<length_mapped_aligned;
1120 offset_start=offset_end) {
1121 struct page_position *pagepos_start;
1122 size_t offset_built;
1126 offset_end=offset_start+PAGE_SIZE;
1127 pagepos_start=pagepos_array[offset_start/PAGE_SIZE];
1128 if (!pagepos_start->building)
1130 g_assert(offset_start==pagepos_start->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
1131 pageaddr=(gpointer)(((char *)base_aligned)+offset_start);
1132 /* Coalescence of the requests. */
1134 offset_end=offset_start+PAGE_SIZE;
1135 offset_end<length_mapped_aligned;
1136 offset_end+=PAGE_SIZE) {
1137 struct page_position *pagepos_end;
1139 pagepos_end=pagepos_array[offset_end/PAGE_SIZE];
1140 g_assert(offset_end==pagepos_end->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
1141 if (!pagepos_end->building)
1144 /* Read the range content: */
1145 got=captive_Cc_IoPageRead(FileObject,pageaddr,offset_end-offset_start,&pagepos_start->FileOffset);
1149 g_assert(got<=offset_end-offset_start);
1150 after_eof=(got<offset_end-offset_start);
1151 /* Unmark 'building' flags. */
1153 offset_built=offset_start;
1154 offset_built<offset_end;
1155 offset_built+=PAGE_SIZE) {
1156 struct page_position *pagepos_built;
1158 pagepos_built=pagepos_array[offset_built/PAGE_SIZE];
1159 g_assert(offset_built==pagepos_built->FileOffset.QuadPart-FileOffset_bottom.QuadPart);
1160 g_assert(pagepos_built->building==TRUE);
1161 pagepos_built->building=FALSE;
1166 /* offset _into_ page, may not be PAGE_SIZE aligned: */
1167 *Buffer=privbcb->base;
1168 *PublicBcbp=PublicBcb;
1169 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1170 "%s: result: privbcb=%p,privbcb->base=%p,privbcb->base+privbcb->MappedLength=%p,privbcb->MappedLength=0x%lX",
1171 G_STRLOC,privbcb,privbcb->base,((char *)privbcb->base)+privbcb->MappedLength,(unsigned long)privbcb->MappedLength);
1172 g_assert(validate_Bcb(PublicBcb)==TRUE);
1173 if (FileObject->Flags & FO_STREAM_FILE) {
1174 /* IoCreateStreamFileObjectLite() result is mapped many times. */
1175 g_assert(!FileObject->SectionObjectPointers->SharedCacheMap);
1178 g_assert(!FileObject->SectionObjectPointers->SharedCacheMap);
1179 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
1188 * @FileObject: Initialized open #FileObject to map.
1189 * %NULL value is forbidden.
1190 * @MappedFileOffset: The @FileObject file offset from where to map the region from.
1191 * Negative value is forbidden.
1192 * @MappedLength: Requested length of the region to map from @FileObject.
1193 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1194 * @Wait: Whether disk waiting is permitted for this function.
1195 * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
1196 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1197 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1198 * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
1199 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1200 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1201 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1202 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1203 * %NULL pointer is forbidden.
1204 * @Buffer: Returns the mapped memory region start address.
1205 * This address may not be %PAGE_SIZE aligned.
1206 * %NULL pointer is forbidden.
1208 * This function will allow you to modify the data mapped by CcMapData().
1209 * libcaptive does not differentiate this function with CcMapData().
1211 * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
1213 * NEVER re-read any memory from FileObject here!
1214 * at least fastfat.sys directory create relies on the fact of CcPinRead()
1215 * with already modified buffers to be left intact.
1217 * This call will proceed as CcPinRead() if such #Bcb does not yet exist.
1218 * This is IMO just a bug workaround for a peruse by fastfat.sys FatLocateVolumeLabel().
1220 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
1222 * Returns: %TRUE if the region was successfuly mapped.
1223 * @Bcb with the initialized new memory region.
1224 * @Buffer with the address of the exact byte specified by @FileOffset.
1226 BOOLEAN CcPinMappedData
1227 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
1229 struct private_bcb *privbcb;
1231 g_return_val_if_fail(FileObject!=NULL,FALSE);
1232 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1233 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1234 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
1235 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
1236 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
1237 g_return_val_if_fail(Bcb!=NULL,FALSE);
1239 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
1240 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
1242 privbcb=captive_privbcb_find(FileObject,FileOffset,Length,NULL);
1243 if (!privbcb && (Flags & PIN_IF_BCB)) /* BCB does not exist */
1245 /* Appropriate privbcb not found.
1246 * This IMO should not happen and it is a bug in the client.
1247 * Unfortuantely fastfat.sys FatLocateVolumeLabel() will CcPinMappedData()
1248 * the volume label dirent without any previous CcMapData() or CcPinRead() !
1258 | (Flags&PIN_NO_READ ? MAP_NO_READ : 0)
1259 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
1264 /* NEVER re-read any memory from FileObject here! */
1266 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1268 /* Memory already mapped by CcMapData(). */
1269 *Bcb=privbcb->PublicBcb;
1276 * @FileObject: Initialized open #FileObject to map.
1277 * %NULL value is forbidden.
1278 * @FileOffset: The @FileObject file offset from where to map the region from.
1279 * Negative value is forbidden.
1280 * @Length: Requested length of the region to map from @FileObject.
1281 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
1282 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
1283 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
1284 * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
1285 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
1286 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
1287 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
1288 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
1289 * %NULL pointer is forbidden.
1290 * @Buffer: Returns the mapped memory region start address.
1291 * This address may not be %PAGE_SIZE aligned.
1292 * %NULL pointer is forbidden.
1294 * Merely a shortcut call for CcMapData() and CcPinMappedData() afterwards.
1295 * See these two functions for the details. It has a difference to subsequent
1296 * calling of CcMapData() and CcPinMappedData() instead as this call counts
1297 * only as one function for a corresponding CcUnpinData() call.
1299 * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
1301 * Every call to this function must be matched by a one corresponding CcUnpinData() call.
1303 * Returns: %TRUE if the region was successfuly mapped.
1304 * @Bcb with the initialized new memory region.
1305 * @Buffer with the address of the exact byte specified by @FileOffset.
1307 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
1308 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
1310 PVOID Bcb_CcPinMappedData;
1312 gboolean count_CcMapData;
1313 struct private_bcb *privbcb;
1315 g_return_val_if_fail(FileObject!=NULL,FALSE);
1316 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1317 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
1318 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
1319 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
1320 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
1321 g_return_val_if_fail(Bcb!=NULL,FALSE);
1322 g_return_val_if_fail(Buffer!=NULL,FALSE);
1324 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
1325 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
1327 if (!(Flags&PIN_IF_BCB)) {
1328 errbool=CcMapData(FileObject,FileOffset,Length,
1330 | (Flags&PIN_NO_READ ? MAP_NO_READ : 0)
1331 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
1333 g_return_val_if_fail(errbool==TRUE,FALSE);
1334 count_CcMapData=TRUE;
1337 count_CcMapData=FALSE;
1339 errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
1340 if (!(Flags&PIN_IF_BCB)) {
1341 g_return_val_if_fail(errbool==TRUE,FALSE);
1342 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
1345 if (errbool==FALSE) /* FALSE permitted; We may fail if Bcb does not exist yet. */
1347 *Bcb=Bcb_CcPinMappedData;
1350 privbcb=captive_privbcb_find(FileObject,FileOffset,Length,*Bcb);
1351 g_assert(privbcb!=NULL);
1352 g_assert(privbcb->ref_count>=1);
1353 g_assert(privbcb->PublicBcb==*Bcb);
1354 if (count_CcMapData) {
1355 /* CcPinRead() must always reference-count only by 1 despite any sub-called functions! */
1356 g_assert(privbcb->ref_count>=2);
1357 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
1364 static void logging_notify_privbcb_flush(struct private_bcb *privbcb);
1366 /* Returns: The block was dirty and it was flushed to disk.
1368 static gboolean captive_privbcb_flush_unordered(struct private_bcb *privbcb)
1372 IO_STATUS_BLOCK IoStatus;
1374 gpointer base_sectoraligned;
1375 gsize length_sectoraligned;
1376 LARGE_INTEGER FileOffset_sectoraligned;
1378 struct fileobject_cached *fileobject_cached;
1379 IRP *saved_TopLevelIrp;
1380 static gint64 last_written_lsn=G_MININT64;
1382 g_assert(privbcb->ref_count>0);
1384 if (!privbcb->dirty)
1387 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);
1388 logging_notify_privbcb_flush(privbcb);
1389 g_assert(privbcb->ref_count>=2);
1390 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1392 if (privbcb->lsn_valid) {
1393 if (!(last_written_lsn<=privbcb->lsn.QuadPart))
1394 g_error("%s: last_written_lsn=%" G_GINT64_FORMAT " !<= privbcb->lsn=%" G_GINT64_FORMAT,G_STRLOC,
1395 last_written_lsn,(gint64)privbcb->lsn.QuadPart);
1396 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LSN write: last_written_lsn was %" G_GINT64_FORMAT ", is %" G_GINT64_FORMAT,
1397 G_STRLOC,last_written_lsn,(gint64)privbcb->lsn.QuadPart);
1398 last_written_lsn=privbcb->lsn.QuadPart;
1401 /* Prepare NULL TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
1403 saved_TopLevelIrp=IoGetTopLevelIrp();
1404 IoSetTopLevelIrp(NULL);
1406 fileobject_cached_hash_init();
1407 if ((fileobject_cached=g_hash_table_lookup(fileobject_cached_hash,privbcb->FileObject)))
1408 (*fileobject_cached->CallBacks.AcquireForLazyWrite)(
1409 fileobject_cached->LazyWriterContext, /* Context */
1412 g_assert(privbcb->FileObject->DeviceObject!=NULL);
1414 /* We can get 0=='privbcb->FileObject->DeviceObject->SectorSize' during mount of ext2fsd.sys.
1415 * FIXME: We are unable to find the correct sectorsize for a filesystem as
1416 * even the 'CommonFcb' below contains invalid information.
1417 * As we need to have 'sectorsize' <=filesystem_blocksize at least for ext2fsd.sys
1418 * we choose PAGE_SIZE - the maximum libcaptive can with its design and also the maximum
1419 * size ever needed for ext2fsd.sys (PAGE_SIZE is the maximum ext2 block size).
1421 sectorsize=PAGE_SIZE;
1423 sectorsize=privbcb->FileObject->DeviceObject->SectorSize;
1424 if (privbcb->FileObject->FsContext) {
1425 REACTOS_COMMON_FCB_HEADER *CommonFcb=(REACTOS_COMMON_FCB_HEADER *)privbcb->FileObject->FsContext;
1427 /* FIXME: Check CommonFcb->Type */
1428 /* 'AllocationSize' can be less than 'sectorsize' if the total file length is smaller.
1429 * Observed with ext2fsd.sys volume-file.
1431 if (sectorsize<CommonFcb->AllocationSize.QuadPart)
1432 sectorsize=CommonFcb->AllocationSize.QuadPart;
1436 /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
1437 g_assert(sectorsize>0);
1438 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,sectorsize));
1439 /* We align here directly the 'privbcb->base' which is not correct.
1440 * We should rather aligned according to 'privbcb->MappedOffset' but
1441 * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
1442 * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
1444 g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
1445 ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,sectorsize));
1446 base_sectoraligned =CAPTIVE_ROUND_DOWN(privbcb->base,sectorsize);
1447 length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,sectorsize)
1448 -((char *)base_sectoraligned);
1449 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,sectorsize));
1450 FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,sectorsize);
1452 Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
1453 g_assert(Mdl!=NULL);
1454 MmBuildMdlForNonPagedPool(Mdl);
1456 KeInitializeEvent(&Event,NotificationEvent,FALSE);
1458 /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
1459 err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
1460 g_assert(NT_SUCCESS(err));
1461 g_assert(NT_SUCCESS(IoStatus.Status));
1462 /* We should write at least the unaligned mapped data although we
1463 * do not need to successfuly write the whole aligned amount.
1464 * FIXME: Also we can get just value 0 if the write is considered 'not dirty'
1465 * during FAT write by fastfat.sys.
1466 * We can also get just value 8 during write of PAGE_SIZE aligned block
1467 * of privbcb->MappedLength 512 during flush of LSNed buffer on captive_leave(),
1468 * probably no assumptions can be made about the returned value at all.
1471 g_assert(IoStatus.Information==0 || IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,sectorsize)
1472 +privbcb->MappedLength);
1474 g_assert(IoStatus.Information<=length_sectoraligned);
1476 if (fileobject_cached)
1477 (*fileobject_cached->CallBacks.ReleaseFromLazyWrite)(
1478 fileobject_cached->LazyWriterContext); /* Context */
1480 /* Restore TopLevelIrp for fileobject_cached->CallBacks.{AcquireForLazyWrite,ReleaseFromLazyWrite}
1482 g_assert(NULL==IoGetTopLevelIrp());
1483 IoSetTopLevelIrp(saved_TopLevelIrp);
1485 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
1486 "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
1487 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
1488 base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
1489 (gulong)IoStatus.Information);
1491 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE);
1495 struct captive_privbcb_flush_ordered_param {
1496 struct private_bcb *privbcb_req;
1497 struct private_bcb *found; /* return */
1500 static gboolean captive_privbcb_flush_ordered_foreach_get_first(struct private_bcb *key,struct private_bcb *value,
1501 struct captive_privbcb_flush_ordered_param *captive_privbcb_flush_ordered_param /* data */)
1503 g_return_val_if_fail(key!=NULL,TRUE); /* meaning: stop the traversal */
1504 g_return_val_if_fail(value!=NULL,TRUE); /* meaning: stop the traversal */
1505 g_return_val_if_fail(key==value,TRUE); /* meaning: stop the traversal */
1506 g_return_val_if_fail(validate_Bcb(key->PublicBcb),TRUE); /* meaning: stop the traversal */
1507 g_return_val_if_fail(captive_privbcb_flush_ordered_param!=NULL,TRUE); /* meaning: stop the traversal */
1508 g_return_val_if_fail(captive_privbcb_flush_ordered_param->found==NULL,TRUE); /* meaning: stop the traversal */
1510 g_assert(key->lsn_valid==TRUE);
1512 if (key==captive_privbcb_flush_ordered_param->privbcb_req) {
1513 captive_privbcb_flush_ordered_param->found=key;
1514 return TRUE; /* stop the traversal */
1518 return FALSE; /* continue the traversal */
1520 captive_privbcb_flush_ordered_param->found=key; /* first dirty LSNed buffer */
1521 return TRUE; /* stop the traversal */
1524 /* Use 'privbcb==NULL' to flush all LSNed entries of cache.
1525 * WARNING: Non-LSNed entries will NOT be flushed!
1526 * Returns: Anything was really flushed to disk.
1528 static gboolean captive_privbcb_flush_ordered(struct private_bcb *privbcb_req)
1530 struct private_bcb *privbcb;
1531 struct captive_privbcb_flush_ordered_param captive_privbcb_flush_ordered_param;
1535 if ((privbcb=privbcb_req) && !privbcb->dirty)
1538 if ((privbcb=privbcb_req) && !privbcb->lsn_valid) {
1539 r=captive_privbcb_flush_unordered(privbcb);
1544 private_bcb_lsn_tree_init();
1547 captive_privbcb_flush_ordered_param.privbcb_req=privbcb_req;
1548 captive_privbcb_flush_ordered_param.found=NULL;
1549 g_tree_foreach(private_bcb_lsn_tree,
1550 (GTraverseFunc)captive_privbcb_flush_ordered_foreach_get_first, /* func */
1551 &captive_privbcb_flush_ordered_param); /* user_data */
1552 if (captive_privbcb_flush_ordered_param.found==NULL) {
1553 g_assert(privbcb_req==NULL); /* Global LSN-ordered flush has no more entries. */
1556 privbcb=captive_privbcb_flush_ordered_param.found;
1558 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1559 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
1560 ",privbcb->leave_func_pending=%d",G_STRLOC,
1561 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
1562 (int)privbcb->leave_func_pending);
1564 if (privbcb->dirty) {
1565 errbool=captive_privbcb_flush_unordered(privbcb);
1566 g_assert(errbool==TRUE);
1568 continue; /* Restart as anything could change by calling W32 hassle. */
1571 if (privbcb!=privbcb_req)
1572 continue; /* We just flushed a LSNed buffer not yet requested to be destroyed. */
1574 g_assert(privbcb==g_tree_lookup(private_bcb_lsn_tree,privbcb));
1578 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d",G_STRLOC,(int)r);
1583 static void CcUnpinData_leave_func_finalize(struct private_bcb *privbcb)
1587 g_assert(privbcb->dirty==FALSE);
1588 g_assert(privbcb->lsn_valid==FALSE);
1589 g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
1590 g_assert(privbcb->leave_func_pending==TRUE);
1591 g_assert(privbcb->ref_count==1);
1592 privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
1593 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1594 if (!privbcb->forbid_CcUnpinData_leave_func_finalize_remove) {
1595 errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
1596 g_assert(errbool==TRUE);
1601 static void CcUnpinData_leave_func_privbcb_destroy_func(struct private_bcb *privbcb) /* NULL to traverse the whole tree */
1603 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
1604 ",privbcb->lsn_valid=%d",G_STRLOC,
1605 privbcb,(!privbcb ? -1 : privbcb->ref_count),(!privbcb ? -1 : privbcb->leave_func_pending),
1606 (!privbcb ? -1 : privbcb->lsn_valid));
1608 g_assert(privbcb->leave_func_pending==TRUE);
1609 g_assert(privbcb->ref_count==1);
1610 captive_privbcb_flush_ordered(privbcb);
1611 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
1612 CcUnpinData_leave_func_finalize(privbcb);
1613 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
1617 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
1618 static GHashTable *private_bcb_unpin_leave_hash;
1620 static void private_bcb_unpin_leave_hash_init(void)
1622 if (private_bcb_unpin_leave_hash)
1624 private_bcb_unpin_leave_hash=g_hash_table_new_full(
1625 g_direct_hash, /* hash_func */
1626 g_direct_equal, /* key_equal_func */
1627 NULL, /* key_destroy_func */
1628 (GDestroyNotify)CcUnpinData_leave_func_privbcb_destroy_func); /* value_destroy_func */
1632 static void CcUnpinData_leave_unregister(struct private_bcb *privbcb)
1636 g_return_if_fail(privbcb!=NULL);
1638 g_return_if_fail(private_bcb_unpin_leave_hash!=NULL);
1640 g_assert(privbcb->forbid_CcUnpinData_leave_func_finalize_remove==FALSE);
1641 privbcb->forbid_CcUnpinData_leave_func_finalize_remove=TRUE;
1642 g_assert(privbcb==g_hash_table_lookup(private_bcb_unpin_leave_hash,privbcb->PublicBcb));
1643 errbool=g_hash_table_remove(private_bcb_unpin_leave_hash,privbcb->PublicBcb);
1644 g_assert(errbool==TRUE);
1645 g_assert(NULL==g_hash_table_lookup(private_bcb_unpin_leave_hash,privbcb->PublicBcb));
1649 static void CcUnpinData_leave_func(struct private_bcb *privbcb /* user_data; unused */)
1651 GHashTable *private_bcb_unpin_leave_hash_local;
1653 if (!private_bcb_unpin_leave_hash)
1656 private_bcb_unpin_leave_hash_local=private_bcb_unpin_leave_hash;
1657 private_bcb_unpin_leave_hash=NULL;
1659 g_hash_table_destroy(private_bcb_unpin_leave_hash_local); /* utilize value_destroy_func */
1665 * @Bcb: Initialized #PUBLIC_BCB structure.
1666 * %NULL value is forbidden.
1668 * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
1670 * This call does not set the buffer as dirty although it will flush the buffers
1671 * already set as dirty. Any flushes will be postponed after return from #IRP
1672 * handling by the filesystem driver.
1674 VOID CcUnpinData(IN PVOID Bcb)
1676 PUBLIC_BCB *PublicBcb;
1677 struct private_bcb *privbcb;
1679 g_return_if_fail(validate_Bcb(Bcb));
1681 private_bcb_hash_init();
1683 PublicBcb=(PUBLIC_BCB *)Bcb;
1684 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1685 g_return_if_fail(privbcb!=NULL);
1686 g_assert(!privbcb->leave_func_pending);
1688 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
1689 if (privbcb->FileObject->Flags & FO_STREAM_FILE) {
1690 /* IoCreateStreamFileObjectLite() result is mapped many times. */
1691 g_assert(!privbcb->FileObject->SectionObjectPointers->SharedCacheMap);
1694 g_assert(privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb);
1696 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
1697 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
1698 ",privbcb->leave_func_pending=%d",G_STRLOC,
1699 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
1700 (int)privbcb->leave_func_pending);
1702 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
1703 if (privbcb->ref_count>0)
1706 if (!(privbcb->FileObject->Flags & FO_STREAM_FILE))
1707 privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
1709 /* Caboom: lfs (log file system) of ntfs.sys-NT5.1sp1 will do:
1710 * CcPinRead(); CcUnpinData(); access Buffer (wanna-crash);
1711 * Therefore we must postpone the buffer unmapping to some idle function...
1712 * I expect it a bug in ntfs.sys.
1714 if (!privbcb->leave_func_pending) {
1715 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1); /* '+1' gets decreased by 'leave_func_pending'. */
1716 privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,TRUE);
1717 if (!private_bcb_unpin_leave_hash) {
1718 private_bcb_unpin_leave_hash_init();
1719 g_assert(private_bcb_unpin_leave_hash!=NULL);
1720 captive_leave_register(
1721 (captive_leave_func)CcUnpinData_leave_func, /* func */
1722 NULL); /* data; unused */
1724 g_hash_table_insert(private_bcb_unpin_leave_hash,PublicBcb,privbcb);
1727 if (captive_cc_unmounting)
1734 * @Bcb: #PUBLIB_BCB to be repinned.
1735 * %NULL value is forbidden.
1737 * Increases usecount on @Bcb. You must call CcUnpinRepinnedBcb() for such @Bcb
1738 * afterwards before returning from the #IRP handling.
1740 * libcaptive does not differentiate between CcUnpinData() and CcUnpinRepinnedBcb().
1741 * W32 differentiates.
1743 * This call does not set the buffer as dirty - such buffer will not be flushed automatically.
1746 VOID CcRepinBcb(IN PVOID Bcb)
1748 PUBLIC_BCB *PublicBcb;
1749 struct private_bcb *privbcb;
1751 g_return_if_fail(validate_Bcb(Bcb));
1753 private_bcb_hash_init();
1755 PublicBcb=(PUBLIC_BCB *)Bcb;
1756 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1757 g_return_if_fail(privbcb!=NULL);
1759 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
1760 Bcb,privbcb->FileObject);
1762 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
1767 * CcUnpinRepinnedBcb:
1768 * @Bcb: #PUBLIB_BCB to be unpinned from CcRepinBcb().
1769 * %NULL value is forbidden.
1770 * @WriteThrough: %TRUE if the buffer should be flushed before finishing this function.
1771 * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1772 * %NULL value is forbidden. libcaptive always returns %STATUS_SUCCESS here.
1774 * Dereferencing of @Bcb after application of CcRepinBcb().
1776 * This call does not set the buffer as dirty although it will flush the buffers
1777 * already set as dirty. Any flushes will be postponed after return from #IRP
1778 * handling by the filesystem driver if not requested to be synchronous by @WriteThrough.
1780 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
1782 PUBLIC_BCB *PublicBcb;
1783 struct private_bcb *privbcb;
1785 g_return_if_fail(validate_Bcb(Bcb));
1786 g_return_if_fail(IoStatus!=NULL);
1788 private_bcb_hash_init();
1790 PublicBcb=(PUBLIC_BCB *)Bcb;
1791 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1792 g_return_if_fail(privbcb!=NULL);
1794 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
1795 Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
1798 captive_privbcb_flush_ordered(privbcb);
1800 IoStatus->Status=STATUS_SUCCESS;
1801 IoStatus->Information=privbcb->MappedLength;
1807 * CcSetDirtyPinnedData:
1808 * @Bcb: #PUBLIB_BCB to be unpinned from CcRepinBcb().
1809 * %NULL value is forbidden.
1810 * @Lsn: Optional LSN (Linear Sequence Number) to assign to @Bcb.
1811 * %NULL pointer is permitted.
1813 * This call will set the buffer as dirty - such buffer will be flushed automatically.
1815 * You should call it only on CcPin*() buffers - not just CcMapData() buffers
1816 * although libcaptive does not differentiate it.
1818 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
1820 PUBLIC_BCB *PublicBcb;
1821 struct private_bcb *privbcb;
1823 g_return_if_fail(validate_Bcb(Bcb));
1825 private_bcb_hash_init();
1827 PublicBcb=(PUBLIC_BCB *)Bcb;
1828 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1829 g_return_if_fail(privbcb!=NULL);
1831 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
1832 Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
1834 /* 'privbcb->ref_count' not to be increased by this function. */
1837 /* Unset it first to not to get into unreachable privbcb in 'private_bcb_lsn_tree'. */
1838 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
1840 privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,TRUE);
1843 /* FIXME: Unset 'lsn_valid' if !Lsn ? Undocumented by W32. */
1844 g_assert(privbcb->lsn_valid==FALSE); /* NOT IMPLEMENTED YET */
1847 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,TRUE);
1851 struct CcSetFileSizes_param {
1852 PFILE_OBJECT FileObject;
1853 PCC_FILE_SIZES FileSizes;
1856 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
1857 static void CcSetFileSizes_private_bcb_hash_foreach(
1858 PUBLIC_BCB *PublicBcb, /* key */
1859 struct private_bcb *privbcb, /* value */
1860 struct CcSetFileSizes_param *CcSetFileSizes_param) /* user_data */
1862 g_return_if_fail(validate_Bcb(PublicBcb));
1863 g_return_if_fail(privbcb!=NULL);
1864 g_return_if_fail(CcSetFileSizes_param!=NULL);
1866 if (privbcb->FileObject!=CcSetFileSizes_param->FileObject)
1869 /* size changes behind our cached range? */
1871 && CcSetFileSizes_param->FileSizes->AllocationSize .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
1872 && CcSetFileSizes_param->FileSizes->FileSize .QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength
1873 && CcSetFileSizes_param->FileSizes->ValidDataLength.QuadPart>=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
1876 /* FIXME: check BCB && 'struct page_position' invalidities */
1877 g_assert_not_reached(); /* NOT IMPLEMENTED YET */
1882 * @FileObject: Initialized open #FileObject to update file sizes of.
1883 * %NULL value is forbidden.
1884 * @FileSizes: New file sizes to update cache to.
1885 * %NULL value is forbidden.
1887 * Update cache properties after file sizes were updated.
1888 * Probably only the exceeding pages need to be unmapped and BCBs updated
1889 * if FileSizes->AllocationSize gets shrunk.
1891 * FIXME: Currently a NOP with no effect by libcaptive.
1893 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
1895 struct CcSetFileSizes_param CcSetFileSizes_param;
1897 g_return_if_fail(FileObject!=NULL);
1898 g_return_if_fail(FileSizes!=NULL);
1900 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
1901 "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
1902 FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
1903 (guint64)FileSizes->ValidDataLength.QuadPart);
1905 CcSetFileSizes_param.FileObject=FileObject;
1906 CcSetFileSizes_param.FileSizes=FileSizes;
1907 g_hash_table_foreach(
1908 private_bcb_hash, /* hash_table */
1909 (GHFunc)CcSetFileSizes_private_bcb_hash_foreach, /* func */
1910 &CcSetFileSizes_param); /* user_data */
1915 * CcPurgeCacheSection:
1916 * @SectionObjectPointer: Pointer specifying file to purge;
1917 * %NULL value is forbidden.
1918 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1919 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %TRUE return code in such case.
1920 * @FileOffset: Starting offset of the ranger to purge.
1921 * %NULL pointer is permitted and it means to purge the whole whole.
1922 * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
1923 * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
1924 * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
1926 * Drop any caching for shrunken file which is not being deleted.
1927 * libcaptive will no longer consider such #BCB as dirty.
1929 * Undocumented: It is required during %FSCTL_LOCK_VOLUME by ntfs.sys of NT-5.1sp1
1930 * to return %TRUE value if #SharedCacheMap value is %NULL.
1932 * Returns: %TRUE if the range was purged successfuly.
1934 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1935 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
1937 PUBLIC_BCB *PublicBcb;
1938 struct private_bcb *privbcb;
1940 g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
1941 if (SectionObjectPointer->SharedCacheMap==NULL)
1942 return TRUE; /* nothing to purge; never return FALSE for ntfs.sys of NT-5.1sp1! */
1943 g_return_val_if_fail(FileOffset==NULL,FALSE); /* NOT IMPLEMENTED YET */
1945 PublicBcb=SectionObjectPointer->SharedCacheMap;
1946 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
1948 private_bcb_hash_init();
1950 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1951 g_return_val_if_fail(privbcb!=NULL,FALSE);
1953 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1954 "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
1955 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1956 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
1958 g_assert_not_reached();
1960 privbcb_set(privbcb,PRIVBCB_ITEM_DIRTY,FALSE); /* purge it */
1968 * @FileObject: Initialized open #FileObject to map.
1969 * %NULL value is forbidden.
1970 * @FileOffset: The @FileObject file offset from where to map the region from.
1971 * Negative value is forbidden.
1972 * @Length: Requested length of the region to map from @FileObject.
1973 * Value %0 is permitted (no effect of this function call).
1974 * @Wait: Whether disk waiting is permitted for this function.
1975 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
1976 * @Buffer: Address of memory region with already allocated memory of size @Length.
1977 * This address may not be %PAGE_SIZE aligned.
1978 * %NULL pointer is forbidden.
1979 * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
1980 * %NULL pointer is forbidden.
1982 * Reads the specified region of @FileObject to the given @Buffer.
1983 * No on-demand loading is in effect.
1985 * Returns: %TRUE if the region was successfuly filled with @Length bytes.
1986 * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
1987 * @IoStatus.Information initialized by @Length if successful.
1989 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
1990 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
1996 g_return_val_if_fail(FileObject!=NULL,FALSE);
1997 g_return_val_if_fail(FileOffset!=NULL,FALSE);
1998 g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE); /* Prevent 'Wait' upgrade to 'Flags'. */
1999 g_return_val_if_fail(Buffer!=NULL,FALSE);
2000 g_return_val_if_fail(IoStatus!=NULL,FALSE);
2002 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
2003 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
2005 IoStatus->Status=STATUS_UNSUCCESSFUL;
2006 IoStatus->Information=0;
2010 FileObject, /* FileObject */
2011 FileOffset, /* FileOffset */
2012 Length, /* Length */
2013 0 /* Flags; !PIN_NO_READ */
2014 | (Wait ? PIN_WAIT : 0),
2015 &MappedBcb, /* Bcb */
2016 &MappedBuffer); /* Buffer */
2017 g_return_val_if_fail(errbool==TRUE,FALSE);
2019 memcpy(Buffer,MappedBuffer,Length);
2021 CcUnpinData(MappedBcb); /* no error code */
2024 IoStatus->Status=STATUS_SUCCESS;
2025 IoStatus->Information=Length;
2033 * @FileObject: Initialized open #FileObject to map.
2034 * %NULL value is forbidden.
2035 * @FileOffset: The @FileObject file offset from where to map the region from.
2036 * Negative value is forbidden.
2037 * @Length: Requested length of the region to map from @FileObject.
2038 * Value %0 is permitted (no effect of this function call).
2039 * @Wait: Whether disk waiting is permitted for this function.
2040 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
2041 * @Buffer: Address of memory region with already allocated memory of size @Length.
2042 * This address may not be %PAGE_SIZE aligned.
2043 * %NULL pointer is forbidden.
2045 * Writes the specified region of the given @Buffer to @FileObject.
2047 * Returns: %TRUE if the region was successfuly written with @Length bytes.
2049 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
2050 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
2056 g_return_val_if_fail(FileObject!=NULL,FALSE);
2057 g_return_val_if_fail(FileOffset!=NULL,FALSE);
2058 g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE); /* Prevent 'Wait' upgrade to 'Flags'. */
2059 g_return_val_if_fail(Buffer!=NULL,FALSE);
2061 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
2062 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
2066 FileObject, /* FileObject */
2067 FileOffset, /* FileOffset */
2068 Length, /* Length */
2069 PIN_NO_READ /* Flags */
2070 | (Wait ? PIN_WAIT : 0),
2071 &MappedBcb, /* Bcb */
2072 &MappedBuffer); /* Buffer */
2073 g_return_val_if_fail(errbool==TRUE,FALSE);
2075 memcpy(MappedBuffer,Buffer,Length);
2077 CcSetDirtyPinnedData(
2078 MappedBcb, /* Bcb */
2080 CcUnpinData(MappedBcb); /* no error code */
2089 * @FileObject: Initialized open #FileObject to map.
2090 * %NULL value is forbidden.
2091 * @BytesToWrite: Amount of data to be asked whether it will be accepted.
2092 * Value %0 is permitted.
2093 * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
2094 * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
2096 * Asks cache manager if it would currently accept write request to @FileObject
2097 * of @BytesToWrite bytes with @Wait condition.
2098 * libcaptive will always accept any writes. This function is a NOP.
2100 * Returns: libcaptive always returns %TRUE.
2102 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
2104 g_return_val_if_fail(FileObject!=NULL,FALSE);
2105 g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE); /* Prevent 'Wait' upgrade to 'Flags'. */
2106 g_return_val_if_fail(Retrying==TRUE || Retrying==FALSE,FALSE);
2108 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
2109 FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
2116 * CcSetReadAheadGranularity:
2117 * @FileObject: Initialized open #FileObject to map.
2118 * %NULL value is forbidden.
2119 * @Granularity: Suggested size of the cache element.
2120 * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
2122 * libcaptive does not implement any caching and therefore this function
2125 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
2127 g_return_if_fail(FileObject!=NULL);
2128 g_return_if_fail(Granularity>=PAGE_SIZE);
2129 g_return_if_fail((Granularity&(Granularity-1))==0); /* Power of two */
2131 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
2132 FileObject,(gulong)Granularity);
2134 /* NOP; no caching by libcaptive */
2140 * @SectionObjectPointer: Pointer specifying file to flush;
2141 * %NULL value is forbidden.
2142 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
2143 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
2144 * @FileOffset: Optional starting point of the range to flush.
2145 * %NULL value is permitted.
2146 * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
2147 * @IoStatus: Optionally returns the resulting operation status.
2148 * #Information field will contain the number of bytes flushed.
2149 * %NULL value is permitted.
2151 * Flushes out any pending dirty data in cache manager BCB mapping.
2152 * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
2154 * VERIFIED: Ranged flushes.
2155 * VERIFIED: Synchronous write.
2157 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2158 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
2160 PUBLIC_BCB *PublicBcb;
2161 struct private_bcb *privbcb;
2163 g_return_if_fail(SectionObjectPointer!=NULL);
2165 if (SectionObjectPointer->SharedCacheMap==NULL) {
2168 IoStatus->Status=STATUS_SUCCESS;
2169 IoStatus->Information=0;
2174 PublicBcb=SectionObjectPointer->SharedCacheMap;
2175 g_return_if_fail(validate_Bcb(PublicBcb));
2177 private_bcb_hash_init();
2179 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
2180 g_return_if_fail(privbcb!=NULL);
2182 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
2183 "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
2184 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
2185 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
2188 if (FileOffset->QuadPart >=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength)
2190 if (FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart)
2194 /* We may find some 'privbcb' being already sheduled to be destroyed.
2195 * We need to reference it to not to loose it in the middle of our flush operation.
2197 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
2199 /* FIXME: Flush just FileOffset..FileOfset+Length part */
2200 captive_privbcb_flush_ordered(privbcb);
2202 privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
2205 IoStatus->Status=STATUS_SUCCESS;
2206 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);
2211 BOOLEAN CcZeroData(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER StartOffset,IN PLARGE_INTEGER EndOffset,IN BOOLEAN Wait)
2217 g_return_val_if_fail(FileObject!=NULL,FALSE);
2218 g_return_val_if_fail(StartOffset!=NULL,FALSE);
2219 g_return_val_if_fail(EndOffset!=NULL,FALSE);
2220 g_return_val_if_fail(StartOffset->QuadPart<=EndOffset->QuadPart,FALSE);
2221 g_return_val_if_fail((EndOffset->QuadPart-StartOffset->QuadPart)
2222 ==(ULONG)(EndOffset->QuadPart-StartOffset->QuadPart),FALSE);
2223 g_return_val_if_fail(Wait==TRUE || Wait==FALSE,FALSE); /* Prevent 'Wait' upgrade to 'Flags'. */
2225 errboolean=CcPreparePinWrite(
2226 FileObject, /* FileObject */
2227 StartOffset, /* FileOffset */
2228 EndOffset->QuadPart-StartOffset->QuadPart, /* Length */
2230 PIN_WAIT|PIN_NO_READ, /* Flags */
2232 &Buffer); /* Buffer */
2233 g_assert(errboolean==TRUE);
2241 /* map (PVOID LogHandle) -> (GList (of FILE_OBJECT *) *FileObject_list) */
2242 static GHashTable *log_handle_hash;
2244 static void log_handle_hash_init(void)
2246 if (log_handle_hash)
2248 log_handle_hash=g_hash_table_new(
2249 g_direct_hash, /* hash_func */
2250 g_direct_equal); /* key_equal_func */
2253 /* map (FILE_OBJECT *FileObject) -> (struct FileObject_logging *) */
2254 struct FileObject_logging {
2256 PFLUSH_TO_LSN FlushToLsnRoutine;
2259 static GHashTable *FileObject_logging_hash;
2261 static void FileObject_logging_hash_init(void)
2263 if (FileObject_logging_hash)
2265 FileObject_logging_hash=g_hash_table_new(
2266 g_direct_hash, /* hash_func */
2267 g_direct_equal); /* key_equal_func */
2270 static void logging_notify_privbcb_flush(struct private_bcb *privbcb)
2272 struct FileObject_logging *FileObject_logging;
2274 g_return_if_fail(privbcb!=NULL);
2276 if (!privbcb->lsn_valid) /* nothing to report anyway */
2279 FileObject_logging_hash_init();
2281 if (!(FileObject_logging=g_hash_table_lookup(FileObject_logging_hash,privbcb->FileObject)))
2284 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: call",G_STRLOC,
2285 privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
2287 (*FileObject_logging->FlushToLsnRoutine)(FileObject_logging->LogHandle,privbcb->lsn);
2289 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb=%p,privbcb->FileObject=%p,privbcb->lsn=0x%llX: finish",G_STRLOC,
2290 privbcb,privbcb->FileObject,(guint64)privbcb->lsn.QuadPart);
2294 * CcSetLogHandleForFile:
2296 * VERIFIED: LogHandle may be NULL, FlushToLsnRoutine is never called with LogHandle==NULL.
2297 * VERIFIED: Only one !=NULL LogHandle and one FlushToLsnRoutine used in one session.
2299 VOID CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)
2301 GList *LogHandle_list;
2302 struct FileObject_logging *FileObject_logging;
2304 g_return_if_fail(FileObject!=NULL);
2305 /* 'LogHandle' may be NULL */
2306 g_return_if_fail(FlushToLsnRoutine!=NULL);
2308 log_handle_hash_init();
2309 FileObject_logging_hash_init();
2311 if (!(FileObject_logging=g_hash_table_lookup(
2312 FileObject_logging_hash,FileObject))) {
2313 captive_new(FileObject_logging);
2314 g_hash_table_insert(FileObject_logging_hash,
2315 FileObject,FileObject_logging);
2318 LogHandle_list=g_hash_table_lookup(log_handle_hash,FileObject_logging->LogHandle);
2319 g_assert(NULL!=g_list_find(LogHandle_list,FileObject));
2320 LogHandle_list=g_list_remove(LogHandle_list,FileObject);
2321 g_assert(NULL==g_list_find(LogHandle_list,FileObject));
2322 g_hash_table_insert(log_handle_hash,FileObject_logging->LogHandle,LogHandle_list);
2325 FileObject_logging->LogHandle=LogHandle;
2326 FileObject_logging->FlushToLsnRoutine=FlushToLsnRoutine;
2328 LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
2329 LogHandle_list=g_list_prepend(LogHandle_list,FileObject);
2330 g_hash_table_insert(log_handle_hash,LogHandle,LogHandle_list);
2334 struct CcGetDirtyPages_param {
2335 PDIRTY_PAGE_ROUTINE DirtyPageRoutine; /* arg of CcGetDirtyPages() */
2336 IN PVOID Context1; /* arg of CcGetDirtyPages() */
2337 IN PVOID Context2; /* arg of CcGetDirtyPages() */
2338 FILE_OBJECT *FileObject; /* search through 'page_position_hash' for 'FileObject' */
2339 LARGE_INTEGER OldestLsn; gboolean OldestLsn_found; /* intermediate return value of CcGetDirtyPages() */
2340 GTree *FileObject_pages_Tree;
2341 struct CcGetDirtyPages_param_FileObject_pages_Tree_page *FileObject_pages_Tree_page_last;
2344 struct CcGetDirtyPages_param_FileObject_pages_Tree_page {
2346 gint64 file_offset_end;
2347 LARGE_INTEGER OldestLsn,NewestLsn;
2350 static gint CcGetDirtyPages_FileObject_pages_Tree_key_compare_func
2351 (const struct CcGetDirtyPages_param_FileObject_pages_Tree_page *a,
2352 const struct CcGetDirtyPages_param_FileObject_pages_Tree_page *b,
2353 gpointer user_data /* unused */)
2355 g_return_val_if_fail(a!=NULL,0);
2356 g_return_val_if_fail(b!=NULL,0);
2358 return (a->file_offset>b->file_offset)-(a->file_offset<b->file_offset);
2361 static void CcGetDirtyPages_FileObject_pages_Tree_key_destroy_func
2362 (struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page)
2364 g_return_if_fail(tree_page!=NULL);
2369 static void CcGetDirtyPages_page_position_hash_foreach(
2370 struct page_position *pagepos, /* key */
2371 struct page_position *pagepos2, /* value */
2372 struct CcGetDirtyPages_param *CcGetDirtyPages_param) /* user_data */
2374 LARGE_INTEGER OldestLsn,NewestLsn;
2375 gboolean lsn_found,dirty_found;
2376 GList *privbcb_list;
2377 struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page;
2379 g_return_if_fail(pagepos!=NULL);
2380 g_return_if_fail(pagepos==pagepos2);
2381 g_return_if_fail(CcGetDirtyPages_param!=NULL);
2382 g_return_if_fail(CcGetDirtyPages_param->FileObject_pages_Tree!=NULL);
2384 if (pagepos->FileObject!=CcGetDirtyPages_param->FileObject)
2387 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: scan pagepos offset=0x%llX",G_STRLOC,
2388 (unsigned long long)pagepos->FileOffset.QuadPart);
2393 privbcb_list=pagepos->privbcb_list;
2395 privbcb_list=privbcb_list->next) {
2396 struct private_bcb *privbcb=privbcb_list->data;
2398 if (!privbcb->dirty)
2402 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: scan page mapping privbcb=%p,MappedFileOffset=0x%llX,MappedLength=%lX"
2403 ",lsn=%" G_GINT64_FORMAT,G_STRLOC,
2404 privbcb,(unsigned long long)privbcb->MappedFileOffset.QuadPart,(unsigned long)privbcb->MappedLength,
2405 (gint64)(!privbcb->lsn_valid ? -1 : privbcb->lsn.QuadPart));
2407 if (privbcb->lsn_valid) {
2409 OldestLsn=NewestLsn=privbcb->lsn;
2413 OldestLsn.QuadPart=MIN(OldestLsn.QuadPart,privbcb->lsn.QuadPart);
2414 NewestLsn.QuadPart=MAX(NewestLsn.QuadPart,privbcb->lsn.QuadPart);
2419 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: no dirty pages found",G_STRLOC);
2424 if (!CcGetDirtyPages_param->OldestLsn_found) {
2425 CcGetDirtyPages_param->OldestLsn=OldestLsn;
2426 CcGetDirtyPages_param->OldestLsn_found=TRUE;
2429 CcGetDirtyPages_param->OldestLsn.QuadPart=MIN(CcGetDirtyPages_param->OldestLsn.QuadPart,OldestLsn.QuadPart);
2432 OldestLsn.QuadPart=0;
2433 NewestLsn.QuadPart=0;
2436 captive_new(tree_page);
2437 tree_page->file_offset=pagepos->FileOffset.QuadPart;
2438 tree_page->file_offset_end=tree_page->file_offset+PAGE_SIZE;
2439 tree_page->OldestLsn=OldestLsn;
2440 tree_page->NewestLsn=NewestLsn;
2441 g_tree_insert(CcGetDirtyPages_param->FileObject_pages_Tree,
2442 tree_page, /* key */
2443 tree_page); /* value */
2445 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: new tree_page file_offset=0x%llX"
2446 ",OldestLsn=%" G_GINT64_FORMAT ",NewestLsn=%" G_GINT64_FORMAT,G_STRLOC,
2447 (unsigned long long)tree_page->file_offset,
2448 (gint64)tree_page->OldestLsn.QuadPart,(gint64)tree_page->NewestLsn.QuadPart);
2451 static gboolean CcGetDirtyPages_FileObject_pages_Tree_traverse_func
2452 (struct CcGetDirtyPages_param_FileObject_pages_Tree_page *tree_page /* key */,
2453 struct CcGetDirtyPages_param_FileObject_pages_Tree_page *value,
2454 struct CcGetDirtyPages_param *CcGetDirtyPages_param) /* user_data */
2456 struct CcGetDirtyPages_param_FileObject_pages_Tree_page *page_last;
2458 /* 'tree_page' may be NULL for the last invocation after complete traversal. */
2459 g_return_val_if_fail(tree_page==value,TRUE); /* 'TRUE' meaning: stop the traversal. */
2460 g_return_val_if_fail(CcGetDirtyPages_param!=NULL,TRUE); /* 'TRUE' meaning: stop the traversal. */
2462 page_last=CcGetDirtyPages_param->FileObject_pages_Tree_page_last;
2464 /* Concatenate two pages? */
2465 if (page_last && tree_page
2466 && page_last->file_offset_end ==tree_page->file_offset
2467 && page_last->OldestLsn.QuadPart==tree_page->OldestLsn.QuadPart
2468 && page_last->NewestLsn.QuadPart==tree_page->NewestLsn.QuadPart) {
2469 page_last->file_offset_end=tree_page->file_offset_end;
2470 /* Forget about the current 'tree_page' as it has been appended. */
2471 return FALSE; /* 'FALSE' meaning: continue the traversal. */
2474 /* Flush 'page_last'. */
2476 LARGE_INTEGER FileOffset_local;
2477 LARGE_INTEGER OldestLsn_check,NewestLsn_check;
2479 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: DirtyPageRoutine(): FileOffset=0x%llX,Length=0x%llX"
2480 ",OldestLsn=%" G_GINT64_FORMAT ",NewestLsn=%" G_GINT64_FORMAT,G_STRLOC,
2481 (unsigned long long)page_last->file_offset,(unsigned long long)(page_last->file_offset_end-page_last->file_offset),
2482 (gint64)page_last->OldestLsn.QuadPart,(gint64)page_last->NewestLsn.QuadPart);
2484 OldestLsn_check=page_last->OldestLsn;
2485 NewestLsn_check=page_last->NewestLsn;
2487 FileOffset_local.QuadPart=page_last->file_offset;
2489 (*CcGetDirtyPages_param->DirtyPageRoutine)(
2490 CcGetDirtyPages_param->FileObject, /* FileObject */
2491 &FileOffset_local, /* FileOffset */
2492 page_last->file_offset_end-page_last->file_offset, /* Length */
2493 &page_last->OldestLsn, /* OldestLsn */
2494 &page_last->NewestLsn, /* NewestLsn */
2495 CcGetDirtyPages_param->Context1, /* Context1 */
2496 CcGetDirtyPages_param->Context2); /* Context2 */
2498 /* just unconfirmed sanity: */
2499 g_assert(FileOffset_local.QuadPart==page_last->file_offset); /* check for possible corruption */
2500 g_assert(OldestLsn_check.QuadPart==page_last->OldestLsn.QuadPart);
2501 g_assert(NewestLsn_check.QuadPart==page_last->NewestLsn.QuadPart);
2503 CcGetDirtyPages_param->FileObject_pages_Tree_page_last=NULL;
2506 /* May be NULL during the last flush: */
2507 CcGetDirtyPages_param->FileObject_pages_Tree_page_last=tree_page;
2509 return FALSE; /* 'FALSE' meaning: continue the traversal. */
2514 * @LogHandle: Arbitrary pointer to match with value passed to CcSetLogHandleForFile().
2515 * %NULL value is permitted (considered as regular matching value by libcaptive).
2516 * @DirtyPageRoutine: #PDIRTY_PAGE_ROUTINE type to call on each dirty page(s).
2517 * %NULL value is forbidden.
2518 * @Context1: User data to pass to @DirtyPageRoutine.
2519 * %NULL value is permitted.
2520 * @Context2: User data to pass to @DirtyPageRoutine.
2522 * Searches through the list of dirty #PUBLIC_BCB s of files assigned to @LogHandle
2523 * by CcSetLogHandleForFile(). Any clean #PUBLIC_BCB s are ignored
2524 * by this function. Function will scan through all #PUBLIC_BCB mapping of each such page
2525 * and it will detect its oldest and newest LSN (Logical Sequence Number). Unset
2526 * LSN is considered as value 0. Value 0 is returned only if no other LSN is valid
2527 * for such case, otherwise such void value 0 is ignored (and oldest and newest LSN
2528 * returned are being equal in the case only single LSN was found).
2530 * Found pages are coalesced as much as possible during calls of @DirtyPageRoutine.
2531 * Coalescable ranges must have the same detected both oldest and newest LSNs.
2533 * Returns: Oldest LSN across all the #FileObject s found for the given @LogHandle.
2534 * Function returns value %0 if no appropriate LSN was found.
2535 * libcaptive must return #gint64 instead of the official #LARGE_INTEGER
2536 * as W32 expects it as value in EAX:EDX but GCC returns the structure address in EAX.
2538 * VERIFIED: func called in runs for each FileObject, FileOffset sorted desc.
2540 gint64 /* instead of LARGE_INTEGER */ CcGetDirtyPages(IN PVOID LogHandle,
2541 IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)
2543 GList *LogHandle_list;
2544 struct CcGetDirtyPages_param CcGetDirtyPages_param;
2546 /* 'LogHandle' may be NULL */
2547 g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
2549 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: LogHandle=%p,DirtyPageRoutine=%p,Context1=%p,Context2=%p",G_STRLOC,
2550 LogHandle,DirtyPageRoutine,Context1,Context2);
2552 log_handle_hash_init();
2553 page_position_hash_init();
2555 CcGetDirtyPages_param.DirtyPageRoutine=DirtyPageRoutine;
2556 CcGetDirtyPages_param.Context1=Context1;
2557 CcGetDirtyPages_param.Context2=Context2;
2558 CcGetDirtyPages_param.OldestLsn_found=FALSE;
2561 LogHandle_list=g_hash_table_lookup(log_handle_hash,LogHandle);
2563 LogHandle_list=LogHandle_list->next) {
2564 CcGetDirtyPages_param.FileObject=LogHandle_list->data;
2565 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: processing FileObject %p",G_STRLOC,CcGetDirtyPages_param.FileObject);
2567 CcGetDirtyPages_param.FileObject_pages_Tree=g_tree_new_full(
2568 (GCompareDataFunc)CcGetDirtyPages_FileObject_pages_Tree_key_compare_func, /* key_compare_func */
2569 NULL, /* key_compare_data */
2570 (GDestroyNotify)CcGetDirtyPages_FileObject_pages_Tree_key_destroy_func, /* key_destroy_func */
2571 NULL); /* value_destroy_func */
2572 g_hash_table_foreach(
2573 page_position_hash, /* hash_table */
2574 (GHFunc)CcGetDirtyPages_page_position_hash_foreach, /* func */
2575 &CcGetDirtyPages_param); /* user_data */
2576 CcGetDirtyPages_param.FileObject_pages_Tree_page_last=NULL;
2577 g_tree_foreach(CcGetDirtyPages_param.FileObject_pages_Tree, /* tree */
2578 (GTraverseFunc)CcGetDirtyPages_FileObject_pages_Tree_traverse_func, /* traverse_func */
2579 &CcGetDirtyPages_param); /* user_data */
2580 CcGetDirtyPages_FileObject_pages_Tree_traverse_func(
2581 NULL, /* tree_page */
2583 &CcGetDirtyPages_param); /* user_data */
2584 g_tree_destroy(CcGetDirtyPages_param.FileObject_pages_Tree);
2587 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %" G_GINT64_FORMAT,G_STRLOC,
2588 (gint64)(!CcGetDirtyPages_param.OldestLsn_found ? 0 : CcGetDirtyPages_param.OldestLsn.QuadPart));
2590 if (!CcGetDirtyPages_param.OldestLsn_found)
2592 return CcGetDirtyPages_param.OldestLsn.QuadPart;
2597 * CcSetAdditionalCacheAttributes:
2598 * @FileObject: Initialized open #FileObject to map.
2599 * %NULL value is forbidden.
2600 * @DisableReadAhead: Read-ahead should not be done by Cache Manager.
2601 * @DisableWriteBehind: Write-behind should not be done by Cache Manager.
2603 * libcaptive does not implement any caching and therefore this function
2606 VOID CcSetAdditionalCacheAttributes(IN PFILE_OBJECT FileObject,IN BOOLEAN DisableReadAhead,IN BOOLEAN DisableWriteBehind)
2608 g_return_if_fail(FileObject!=NULL);
2610 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,DisableReadAhead=%s,DisableWriteBehind=%s",G_STRLOC,
2611 FileObject,(DisableReadAhead ? "TRUE" : "FALSE"),(DisableWriteBehind ? "TRUE" : "FALSE"));
2613 /* NOP; no caching by libcaptive */
2618 * CcSetBcbOwnerPointer:
2619 * @Bcb: Initialized #PUBLIC_BCB structure.
2620 * %NULL value is forbidden.
2621 * @Owner: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
2622 * %NULL value is forbidden (FIXME: Is it W32 compliant?).
2624 * Set thread-specific pointer for a pinned @Bcb. Use CcUnpinDataForThread()
2625 * when @Bcb is no longer needed. CcUnpinDataForThread() is NOT a reverse
2626 * operation for this single call CcSetBcbOwnerPointer(), see CcUnpinDataForThread().
2628 * libcaptive implements this function as no-operation as it does not yet
2629 * support any threading.
2631 VOID CcSetBcbOwnerPointer(IN PVOID Bcb,IN PVOID Owner)
2633 g_return_if_fail(validate_Bcb(Bcb));
2634 g_return_if_fail(Owner!=NULL);
2636 /* FIXME:thread; NOP if no threads present */
2641 * CcUnpinDataForThread:
2642 * @Bcb: Initialized #PUBLIC_BCB structure.
2643 * %NULL value is forbidden.
2644 * @ResourceThreadId: Thread-specific pointer (FIXME: Is it KeGetCurrentThread()?).
2645 * This pointer had to be passed to CcSetBcbOwnerPointer() #Owner parameter previously.
2646 * %NULL value is forbidden (FIXME: is it W32 compliant?).
2648 * CcUnpinData() for a thread specified by @ResourceThreadId.
2649 * Reverse operation for a pair of CcMapData() and CcSetBcbOwnerPointer().
2651 * libcaptive implements this function as a simple pass to CcUnpinData() as it does not yet
2652 * support any threading.
2654 VOID CcUnpinDataForThread(IN PVOID Bcb,IN ERESOURCE_THREAD ResourceThreadId)
2656 g_return_if_fail(validate_Bcb(Bcb));
2657 g_return_if_fail(ResourceThreadId!=0);
2667 * @Bcb: Initialized #PUBLIC_BCB structure.
2668 * %NULL value is forbidden.
2670 * Create a copy of @Bcb for the exactly same file contents as is @Bcb.
2671 * The returned copy has the same attributes as the result of CcMapData()
2672 * notwithstanding the current state of input @Bcb, therefore it is only
2673 * for read/only access etc.
2675 * libcaptive calls CcMapData() internally with @Bcb parameters.
2677 * This function is called only by ntfs.sys of NT-5.1sp1 and it will perform
2678 * these operations with the resulting #PUBLIC_BCB:
2679 * CcRemapBcb(), CcSetDirtyPinnedData(), CcUnpinData()
2681 * Untested: This call does not set the buffer as dirty - such buffer will not be flushed automatically.
2683 * Returns: Copy of @Bcb. This _pointer_ never equals to @Bcb.
2684 * It should be some different
2685 * #PUBLIC_BCB structure according to W32 doc.
2687 PVOID CcRemapBcb(IN PVOID Bcb)
2690 PVOID Buffer_unused;
2692 PUBLIC_BCB *PublicBcb;
2693 struct private_bcb *privbcb;
2695 g_return_val_if_fail(validate_Bcb(Bcb),NULL);
2697 private_bcb_hash_init();
2699 PublicBcb=(PUBLIC_BCB *)Bcb;
2700 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
2701 g_return_val_if_fail(privbcb!=NULL,NULL);
2703 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
2704 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d",G_STRLOC,
2705 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count);
2708 privbcb->FileObject, /* FileObject */
2709 &privbcb->MappedFileOffset, /* FileOffset */
2710 privbcb->MappedLength, /* Length */
2711 MAP_WAIT, /* Flags; FIXME: Is it OK to?: && !MAP_NO_READ */
2713 &Buffer_unused); /* Buffer */
2714 g_return_val_if_fail(errbool==TRUE,NULL);
2715 g_return_val_if_fail(r!=NULL,NULL);
2716 g_return_val_if_fail(Buffer_unused!=NULL,NULL);
2724 * CcPreparePinWrite:
2725 * @FileObject: Initialized open #FileObject to map.
2726 * %NULL value is forbidden.
2727 * @FileOffset: The @FileObject file offset from where to map the region from.
2728 * Negative value is forbidden.
2729 * @Length: Requested length of the region to map from @FileObject.
2730 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
2731 * @Zero: %TRUE if the area of @FileOffset...@FileOffset+@Length should be cleared.
2732 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
2733 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
2734 * %PIN_NO_READ is the same as %MAP_NO_READ - see CcMapData().
2735 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
2736 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
2737 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
2738 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
2739 * %NULL pointer is forbidden.
2740 * @Buffer: Returns the mapped memory region start address.
2741 * This address may not be %PAGE_SIZE aligned.
2742 * %NULL pointer is forbidden.
2744 * Wrapper for a pair of CcPinRead() and CcSetDirtyPinnedData().
2745 * The mapped range can be also optionally cleared if @Zero is specified.
2746 * See CcPinRead() for a more detailed documentation.
2748 * This call will set the buffer as dirty - such buffer will be flushed automatically.
2750 * Returns: %TRUE if the mapping was successful.
2752 BOOLEAN CcPreparePinWrite(IN PFILE_OBJECT FileObject,
2753 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Zero,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
2757 g_return_val_if_fail(FileObject!=NULL,FALSE);
2758 g_return_val_if_fail(FileOffset!=NULL,FALSE);
2759 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
2760 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
2761 /* 'Flags' passed to CcPinRead() */
2762 g_return_val_if_fail(Bcb!=NULL,FALSE);
2763 g_return_val_if_fail(Buffer!=NULL,FALSE);
2765 errbool=CcPinRead(FileObject,FileOffset,Length,Flags|(Zero ? PIN_NO_READ : 0),Bcb,Buffer);
2766 g_return_val_if_fail(errbool==TRUE,FALSE);
2768 CcSetDirtyPinnedData(
2770 NULL); /* Lsn; OPTIONAL */
2773 memset(*Buffer,0,Length);
2780 VOID FsRtlIncrementCcFastReadNoWait(VOID)
2782 /* FIXME: {{%fs:[0]}+0x4E0}:LONG++ */
2786 NTSTATUS CcWaitForCurrentLazyWriterActivity(VOID)
2788 return STATUS_SUCCESS;
2791 static void CcIsThereDirtyData_private_bcb_hash_foreach(
2792 PUBLIC_BCB *PublicBcb, /* key */
2793 struct private_bcb *privbcb, /* value */
2794 gboolean *dirty_foundp) /* user_data */
2796 g_return_if_fail(validate_Bcb(PublicBcb));
2797 g_return_if_fail(privbcb!=NULL);
2798 g_return_if_fail(PublicBcb==privbcb->PublicBcb);
2800 if (!privbcb->dirty)
2803 *dirty_foundp=TRUE; /* FIXME: stop the traversal. */
2806 BOOLEAN CcIsThereDirtyData(IN PVPB Vpb)
2808 gboolean dirty_found;
2810 g_return_val_if_fail(Vpb!=NULL,FALSE); /* We have just one volume mounted anyway. */
2812 private_bcb_hash_init();
2815 g_hash_table_foreach(
2816 private_bcb_hash, /* hash_table */
2817 (GHFunc)CcIsThereDirtyData_private_bcb_hash_foreach, /* func */
2818 &dirty_found); /* user_data */