/* map: (struct private_bcb *)privbcb -> (struct private_bcb *)privbcb */
-static GTree *private_bcb_flush_tree;
+static GTree *private_bcb_lsn_tree;
-static gint private_bcb_flush_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
+/* Although 'private_bcb_lsn_tree' contains only nodes with 'lsn_valid' TRUE
+ * compare_func cannot expect such arguments as it may be used to lookup
+ * privbcb unlisted in 'private_bcb_lsn_tree'.
+ */
+static gint private_bcb_lsn_tree_key_compare_func(struct private_bcb *a,struct private_bcb *b,gpointer user_data /* NULL */)
{
gint64 a_lsn,b_lsn;
gint r;
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a=%p,privbcb b=%p",G_STRLOC,a,b);
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb=%p",G_STRLOC,a->PublicBcb);
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb=%p",G_STRLOC,b->PublicBcb);
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb a->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)a->PublicBcb->MappedLength);
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb b->PublicBcb->MappedLength=%u",G_STRLOC,(unsigned)b->PublicBcb->MappedLength);
+
g_return_val_if_fail(a!=NULL,0);
g_return_val_if_fail(validate_Bcb(a->PublicBcb),0);
- g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MININT64); /* Forbid defined LSN as G_MININT64 */
+ g_assert(!a->lsn_valid || a->lsn.QuadPart!=(LONGLONG)G_MAXINT64); /* Forbid defined LSN as G_MAXINT64 */
g_return_val_if_fail(b!=NULL,0);
g_return_val_if_fail(validate_Bcb(b->PublicBcb),0);
- g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MININT64); /* Forbid defined LSN as G_MININT64 */
+ g_assert(!b->lsn_valid || b->lsn.QuadPart!=(LONGLONG)G_MAXINT64); /* Forbid defined LSN as G_MAXINT64 */
- if (a==b) /* LSN would be apparently the same in such case :-) */
+ if (a==b) { /* LSN would be apparently the same in such case :-) */
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return 0 (a==b)",G_STRLOC);
return 0;
+ }
- a_lsn=(!a->lsn_valid ? (LONGLONG)G_MININT64 : a->lsn.QuadPart);
- b_lsn=(!b->lsn_valid ? (LONGLONG)G_MININT64 : b->lsn.QuadPart);
+ a_lsn=(!a->lsn_valid ? (LONGLONG)G_MAXINT64 : a->lsn.QuadPart);
+ b_lsn=(!b->lsn_valid ? (LONGLONG)G_MAXINT64 : b->lsn.QuadPart);
/* Forbid the same LSNs if both defined */
- g_assert(a_lsn==(LONGLONG)G_MININT64 || a_lsn!=b_lsn);
+ g_assert(a_lsn==(LONGLONG)G_MAXINT64 || a_lsn!=b_lsn);
- if ((r=(a_lsn>b_lsn)-(a_lsn<b_lsn)))
+ if ((r=(a_lsn>b_lsn)-(a_lsn<b_lsn))) {
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return %d (lsn !=)",G_STRLOC,r);
return r;
+ }
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return (ptr !=)",G_STRLOC);
return (a>b)-(a<b);
}
-static void private_bcb_flush_tree_init(void)
+static void private_bcb_lsn_tree_init(void)
{
- if (private_bcb_flush_tree)
+ if (private_bcb_lsn_tree)
return;
- private_bcb_flush_tree=g_tree_new(
- (GCompareFunc)private_bcb_flush_tree_key_compare_func); /* key_compare_func */
+ private_bcb_lsn_tree=g_tree_new(
+ (GCompareFunc)private_bcb_lsn_tree_key_compare_func); /* key_compare_func */
}
-#define PRIVBCB_WANTS_FLUSH(privbcb) ((privbcb)->lsn_valid || ((privbcb)->dirty && (0 \
- || ((privbcb)->ref_count==0) \
- || ((privbcb)->ref_count==1 && (privbcb)->leave_func_pending))))
-
enum privbcb_item {
PRIVBCB_ITEM_NOP, /* for sanity checks */
PRIVBCB_ITEM_DIRTY,
PRIVBCB_ITEM_LSN_VALID,
PRIVBCB_ITEM_LEAVE_FUNC_PENDING,
PRIVBCB_ITEM_REF_COUNT,
+ PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR, /* no protections against 'leave_func_pending' */
};
static void privbcb_set(struct private_bcb *privbcb,enum privbcb_item item,gint value)
{
g_return_if_fail(privbcb!=NULL);
- private_bcb_flush_tree_init();
+ private_bcb_lsn_tree_init();
- g_assert((PRIVBCB_WANTS_FLUSH(privbcb) ? privbcb : NULL)==g_tree_lookup(private_bcb_flush_tree,privbcb));
+ g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
switch (item) {
case PRIVBCB_ITEM_NOP:
break;
g_assert(privbcb->ref_count==1);
break;
case PRIVBCB_ITEM_REF_COUNT:
- /* Forbid reincarnation of 'leave_func_pending' privbcb.
- * Allow some of its 'ref_count' handling to properly clean it up.
- */
- if (privbcb->leave_func_pending)
- g_assert((privbcb->ref_count==1 && value==-1)
- || (privbcb->ref_count==0 && value==+1));
+ /* Forbid reincarnation of 'leave_func_pending' privbcb. */
+ g_assert(!privbcb->leave_func_pending);
+ g_assert(privbcb->ref_count>=1);
+ privbcb->ref_count+=value;
+ g_assert(privbcb->ref_count>=1);
+ break;
+ case PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR:
g_assert(privbcb->ref_count>=0);
privbcb->ref_count+=value;
g_assert(privbcb->ref_count>=0);
break;
default: g_assert_not_reached();
}
- if (!PRIVBCB_WANTS_FLUSH(privbcb))
- g_tree_remove(private_bcb_flush_tree,privbcb);
+ if (!privbcb->lsn_valid)
+ g_tree_remove(private_bcb_lsn_tree,privbcb);
else
- g_tree_insert(private_bcb_flush_tree,
+ g_tree_insert(private_bcb_lsn_tree,
privbcb, /* key */
privbcb); /* value */
- g_assert((PRIVBCB_WANTS_FLUSH(privbcb) ? privbcb : NULL)==g_tree_lookup(private_bcb_flush_tree,privbcb));
+ g_assert((privbcb->lsn_valid ? privbcb : NULL)==g_tree_lookup(private_bcb_lsn_tree,privbcb));
}
{
g_return_if_fail(validate_Bcb(key));
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: PublicBcb=%p",G_STRLOC,key);
+
g_free(key);
}
size_t offset;
gpointer base_aligned;
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_free: privbcb=%p (->PublicBcb=%p)",G_STRLOC,value,value->PublicBcb);
+
g_return_if_fail(value!=NULL);
/* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
* may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
g_return_if_fail(value->base!=NULL);
g_return_if_fail(value->dirty==FALSE);
+ /* Ensure we are not registered in 'private_bcb_lsn_tree'. */
+ g_return_if_fail(value->lsn_valid==FALSE);
page_position_hash_init();
struct private_bcb *privbcb, /* value */
FILE_OBJECT *FileObject) /* user_data */
{
+ g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE); /* meaning: do not remove this 'privbcb' */
+ g_return_val_if_fail(privbcb!=NULL,FALSE); /* meaning: do not remove this 'privbcb' */
+ g_return_val_if_fail(PublicBcb==privbcb->PublicBcb,FALSE); /* meaning: do not remove this 'privbcb' */
+ g_return_val_if_fail(FileObject!=NULL,FALSE); /* meaning: do not remove this 'privbcb' */
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: PublicBcb=%p,privbcb=%p,privbcb->ref_count=%d,privbcb->leave_func_pending=%d"
+ ",FileObject=%p",G_STRLOC,
+ PublicBcb,privbcb,privbcb->ref_count,privbcb->leave_func_pending,FileObject);
+
if (FileObject) {
if (privbcb->FileObject!=FileObject)
return FALSE; /* do not remove this 'privbcb' */
- /* FIXME: Such 'privbcb' can get possibly reincarnated!
- * We disallow even already-deleted entries during !FileObject (unmount).
+ /* It is OK to leave it in 'private_bcb_hash' as it is protected
+ * against reincarnation.
+ * Such check is here to allow it to keep 'dirty' data.
*/
- if (privbcb->ref_count==1 && privbcb->leave_func_pending)
+ if (privbcb->leave_func_pending)
return FALSE; /* do not remove this 'privbcb' */
}
+ /* Do not reuse CcUnpinData_leave_func() destructor here as we cannot
+ * call g_hash_table_remove() from inside g_hash_table_foreach_remove() callback.
+ */
+
g_assert(privbcb->dirty==FALSE); /* it would be fatal */
- /* Force 'ref_count' drop to 0. */
- privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-privbcb->ref_count);
+ g_assert(privbcb->leave_func_pending==FALSE);
+ /* Force 'ref_count' drop to 0 to pass private_bcb_hash_value_destroy_func(). */
+ privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-privbcb->ref_count);
+ /* Unset LSN to unlink from 'private_bcb_lsn_tree'. */
+ privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: WARNING: Deleting file with pending (fortunately non-dirty) BCBs"
- "; FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
- privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
+ "; privbcb=%p,PublicBcb=%p,FileObject=%p,FileOffset=0x%llX,Length=0x%lX",G_STRLOC,
+ privbcb,PublicBcb,privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
return TRUE; /* remove this 'privbcb' */
}
/* 'FileObject' may be NULL to check/clear the whole cache. */
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p",G_STRLOC,FileObject);
+
fileobject_cached_hash_init();
private_bcb_hash_init();
(GHRFunc)captive_cc_FileObject_delete_private_bcb_hash_foreach, /* func */
FileObject); /* user_data */
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: returning r=%d",G_STRLOC,r);
+
return r;
}
captive_privbcb_flush(privbcb);
}
-static void CcUnpinData_leave_func(struct private_bcb *privbcb_stop_at);
+static void CcUnpinData_leave_func(struct private_bcb *privbcb_req);
void captive_cc_flush(void)
{
privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
privbcb->dirty=FALSE;
privbcb->lsn_valid=FALSE;
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: g_hash_table_insert: PublicBcb=%p,privbcb=%p",G_STRLOC,
+ PublicBcb,privbcb);
g_hash_table_insert(private_bcb_hash,
PublicBcb, /* key */
privbcb); /* value */
if (!privbcb->dirty)
return;
- privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
+ privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1);
logging_notify_privbcb_flush(privbcb);
g_assert(privbcb->ref_count>=2);
- privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
+ privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
if (privbcb->lsn_valid) {
if (!(last_written_lsn<privbcb->lsn.QuadPart))
}
+struct CcUnpinData_leave_func_foreach_get_first_args {
+ struct private_bcb *privbcb_req;
+ struct private_bcb *found; /* return */
+ };
+
static gboolean CcUnpinData_leave_func_foreach_get_first(struct private_bcb *key,struct private_bcb *value,
- struct private_bcb **privbcbp /* data */)
+ struct CcUnpinData_leave_func_foreach_get_first_args *args /* data */)
{
g_return_val_if_fail(key!=NULL,TRUE); /* meaning: stop the traversal */
g_return_val_if_fail(value!=NULL,TRUE); /* meaning: stop the traversal */
g_return_val_if_fail(key==value,TRUE); /* meaning: stop the traversal */
g_return_val_if_fail(validate_Bcb(key->PublicBcb),TRUE); /* meaning: stop the traversal */
- g_return_val_if_fail(privbcbp!=NULL,TRUE); /* meaning: stop the traversal */
- g_return_val_if_fail(*privbcbp==NULL,TRUE); /* meaning: stop the traversal */
+ g_return_val_if_fail(args!=NULL,TRUE); /* meaning: stop the traversal */
+ g_return_val_if_fail(args->found==NULL,TRUE); /* meaning: stop the traversal */
- *privbcbp=key;
+ g_assert(key->lsn_valid==TRUE);
+ if (key==args->privbcb_req) {
+ args->found=key;
+ return TRUE; /* stop the traversal */
+ }
+
+ if (!key->dirty)
+ return FALSE; /* continue the traversal */
+
+ args->found=key; /* first dirty LSNed buffer */
return TRUE; /* stop the traversal */
}
-static void CcUnpinData_leave_func(struct private_bcb *privbcb_stop_at) /* NULL to traverse the whole tree */
+static void CcUnpinData_leave_func_finalize(struct private_bcb *privbcb)
{
-struct private_bcb *privbcb;
gboolean errbool;
- private_bcb_flush_tree_init();
+ g_assert(privbcb->dirty==FALSE);
+ g_assert(privbcb->lsn_valid==FALSE);
+ g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
+ g_assert(privbcb->leave_func_pending==TRUE);
+ g_assert(privbcb->ref_count==1);
+ privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
+ privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
+ errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
+ g_assert(errbool==TRUE);
+}
- do {
- privbcb=NULL;
- g_tree_foreach(private_bcb_flush_tree,
+static void CcUnpinData_leave_func(struct private_bcb *privbcb_req) /* NULL to traverse the whole tree */
+{
+struct private_bcb *privbcb;
+struct CcUnpinData_leave_func_foreach_get_first_args args;
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: privbcb_req=%p,privbcb_req->ref_count=%d,privbcb_req->leave_func_pending=%d"
+ ",privbcb_req->lsn_valid=%d",G_STRLOC,
+ privbcb_req,(!privbcb_req ? -1 : privbcb_req->ref_count),(!privbcb_req ? -1 : privbcb_req->leave_func_pending),
+ (!privbcb_req ? -1 : privbcb_req->lsn_valid));
+
+ if ((privbcb=privbcb_req) && !privbcb_req->lsn_valid) {
+ g_assert(NULL==g_tree_lookup(private_bcb_lsn_tree,privbcb));
+ g_assert(privbcb->leave_func_pending==TRUE);
+ g_assert(privbcb->ref_count==1);
+ if (privbcb->dirty)
+ captive_privbcb_flush(privbcb);
+ CcUnpinData_leave_func_finalize(privbcb);
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
+ return;
+ }
+ g_assert(!privbcb_req || privbcb_req==g_tree_lookup(private_bcb_lsn_tree,privbcb_req));
+
+ private_bcb_lsn_tree_init();
+
+ for (;;) {
+ args.privbcb_req=privbcb_req;
+ args.found=NULL;
+ g_tree_foreach(private_bcb_lsn_tree,
(GTraverseFunc)CcUnpinData_leave_func_foreach_get_first, /* func */
- &privbcb); /* user_data */
- g_assert((!privbcb)==!g_tree_nnodes(private_bcb_flush_tree));
- if (!privbcb)
+ &args); /* user_data */
+ if (args.found==NULL) {
+ g_assert(privbcb_req==NULL); /* Global LSN-ordered flush has no more entries. */
break;
+ }
+ privbcb=args.found;
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
"%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX,privbcb->ref_count=%d"
privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
(int)privbcb->leave_func_pending);
- /* Do not stop at not yet modified buffers if not privbcb_stop_at==NULL,
- * privbcb_stop_at==NULL is used to flush the whole cache.
- */
- if (privbcb_stop_at && !privbcb->leave_func_pending) {
- g_assert(privbcb->lsn_valid); /* cannout skip LSN-valid BCB */
- break;
- }
-
- g_assert(!privbcb_stop_at || privbcb->leave_func_pending==TRUE);
- privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,FALSE);
- privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
-
- /* Do not flush modified buffers if not privbcb_stop_at==NULL,
- * privbcb_stop_at==NULL is used to flush the whole cache.
- */
- if (privbcb_stop_at && privbcb->ref_count) {
- if (privbcb->lsn_valid) /* cannout skip LSN-valid BCB */
- break;
- continue;
- }
-
- /* We were called as CcUnpinData()->captive_privbcb_flush()->CcUnpinData()->CcUnpinData_leave_func()
- */
if (privbcb->dirty) {
- privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1);
captive_privbcb_flush(privbcb);
- continue;
+ continue; /* Restart as anything could change by calling W32 hassle. */
}
- /* Clear it out of 'private_bcb_flush_tree' during global buffers flush. */
- if (!privbcb_stop_at) {
- privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-privbcb->ref_count);
- privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
- }
+ if (privbcb!=privbcb_req)
+ continue; /* We just flushed a LSNed buffer not yet requested to be destroyed. */
- g_assert(privbcb->leave_func_pending==FALSE);
- g_assert(privbcb->ref_count==0);
- g_assert(NULL==g_tree_lookup(private_bcb_flush_tree,privbcb));
+ g_assert(privbcb==g_tree_lookup(private_bcb_lsn_tree,privbcb));
+ g_assert(privbcb->leave_func_pending==TRUE);
+ g_assert(privbcb->ref_count==1);
+ privbcb_set(privbcb,PRIVBCB_ITEM_LSN_VALID,FALSE);
+ CcUnpinData_leave_func_finalize(privbcb);
+ break;
+ }
- errbool=g_hash_table_remove(private_bcb_hash,privbcb->PublicBcb);
- g_assert(errbool==TRUE);
- } while (!privbcb_stop_at || privbcb!=privbcb_stop_at);
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: return",G_STRLOC);
}
/**
PublicBcb=(PUBLIC_BCB *)Bcb;
privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
g_return_if_fail(privbcb!=NULL);
+ g_assert(!privbcb->leave_func_pending);
g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
/* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,(int)privbcb->ref_count,
(int)privbcb->leave_func_pending);
- g_assert(privbcb->ref_count>0);
- privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,-1);
+ privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,-1);
if (privbcb->ref_count>0)
return;
- privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT,+1); /* it will get decreased again in CcUnpinData_leave_func() */
g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
/* It may not 'privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb'; see (NOTE*1) */
* I expect it a bug in ntfs.sys.
*/
if (!privbcb->leave_func_pending) {
+ privbcb_set(privbcb,PRIVBCB_ITEM_REF_COUNT_DESTRUCTOR,+1); /* '+1' gets decreased by 'leave_func_pending'. */
privbcb_set(privbcb,PRIVBCB_ITEM_LEAVE_FUNC_PENDING,TRUE);
captive_leave_register(
(captive_leave_func)CcUnpinData_leave_func, /* func */
- privbcb); /* data; privbcb_stop_at */
+ privbcb); /* data; privbcb_req */
}
if (captive_cc_unmounting)