#include "hammer.h"
static int hammer_mem_lookup(hammer_cursor_t cursor);
static int hammer_mem_first(hammer_cursor_t cursor);
static int hammer_frontend_trunc_callback(hammer_record_t record,
void *data __unused);
static int hammer_bulk_scan_callback(hammer_record_t record, void *data);
static int hammer_record_needs_overwrite_delete(hammer_record_t record);
static int hammer_delete_general(hammer_cursor_t cursor, hammer_inode_t ip,
hammer_btree_leaf_elm_t leaf);
static int hammer_cursor_localize_data(hammer_mount_t hmp, hammer_data_ondisk_t data,
hammer_btree_leaf_elm_t leaf);
struct rec_trunc_info {
uint16_t rec_type;
int64_t trunc_off;
};
struct hammer_bulk_info {
hammer_record_t record;
hammer_record_t conflict;
};
static int
hammer_rec_rb_compare(hammer_record_t rec1, hammer_record_t rec2)
{
if (rec1->leaf.base.rec_type < rec2->leaf.base.rec_type)
return(-1);
if (rec1->leaf.base.rec_type > rec2->leaf.base.rec_type)
return(1);
if (rec1->leaf.base.key < rec2->leaf.base.key)
return(-1);
if (rec1->leaf.base.key > rec2->leaf.base.key)
return(1);
if (rec1->flags & (HAMMER_RECF_DELETED_FE | HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
return(1);
}
if (rec2->flags & (HAMMER_RECF_DELETED_FE | HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
return(-1);
}
return(0);
}
static int
hammer_rec_cmp(hammer_base_elm_t elm, hammer_record_t rec)
{
if (elm->rec_type < rec->leaf.base.rec_type)
return(-3);
if (elm->rec_type > rec->leaf.base.rec_type)
return(3);
if (elm->key < rec->leaf.base.key)
return(-2);
if (elm->key > rec->leaf.base.key)
return(2);
if (rec->flags & (HAMMER_RECF_DELETED_FE | HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
return(-1);
}
return(0);
}
static int
hammer_rec_overlap_cmp(hammer_record_t rec, void *data)
{
struct hammer_bulk_info *info = data;
hammer_btree_leaf_elm_t leaf = &info->record->leaf;
if (rec->leaf.base.rec_type < leaf->base.rec_type)
return(-3);
if (rec->leaf.base.rec_type > leaf->base.rec_type)
return(3);
if (leaf->base.rec_type == HAMMER_RECTYPE_DATA) {
if (rec->leaf.base.key - rec->leaf.data_len >= leaf->base.key)
return(2);
if (rec->leaf.base.key <= leaf->base.key - leaf->data_len)
return(-2);
} else {
if (rec->leaf.base.key < leaf->base.key)
return(-2);
if (rec->leaf.base.key > leaf->base.key)
return(2);
}
return(0);
}
static
int
hammer_rec_scan_cmp(hammer_record_t rec, void *data)
{
hammer_cursor_t cursor = data;
int r;
r = hammer_rec_cmp(&cursor->key_beg, rec);
if (r > 1)
return(-1);
r = hammer_rec_cmp(&cursor->key_end, rec);
if (r < -1)
return(1);
return(0);
}
static
int
hammer_rec_find_cmp(hammer_record_t rec, void *data)
{
hammer_cursor_t cursor = data;
int r;
r = hammer_rec_cmp(&cursor->key_beg, rec);
if (r > 1)
return(-1);
if (r < -1)
return(1);
return(0);
}
static
int
hammer_rec_trunc_cmp(hammer_record_t rec, void *data)
{
struct rec_trunc_info *info = data;
if (rec->leaf.base.rec_type < info->rec_type)
return(-1);
if (rec->leaf.base.rec_type > info->rec_type)
return(1);
switch(rec->leaf.base.rec_type) {
case HAMMER_RECTYPE_DB:
if (rec->leaf.base.key < info->trunc_off)
return(-1);
break;
case HAMMER_RECTYPE_DATA:
if (rec->leaf.base.key - rec->leaf.data_len < info->trunc_off)
return(-1);
break;
default:
hpanic("unexpected record type");
}
return(0);
}
RB_GENERATE(hammer_rec_rb_tree, hammer_record, rb_node, hammer_rec_rb_compare);
hammer_record_t
hammer_alloc_mem_record(hammer_inode_t ip, int data_len)
{
hammer_record_t record;
hammer_mount_t hmp;
hmp = ip->hmp;
++hammer_count_records;
record = kmalloc(sizeof(*record), hmp->m_misc,
M_WAITOK | M_ZERO | M_USE_RESERVE);
record->flush_state = HAMMER_FST_IDLE;
record->ip = ip;
record->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
record->leaf.data_len = data_len;
hammer_ref(&record->lock);
if (data_len) {
record->data = kmalloc(data_len, hmp->m_misc, M_WAITOK | M_ZERO);
record->flags |= HAMMER_RECF_ALLOCDATA;
++hammer_count_record_datas;
}
return (record);
}
void
hammer_wait_mem_record_ident(hammer_record_t record, const char *ident)
{
while (record->flush_state == HAMMER_FST_FLUSH) {
record->flags |= HAMMER_RECF_WANTED;
tsleep(record, 0, ident, 0);
}
}
void
hammer_flush_record_done(hammer_record_t record, int error)
{
hammer_inode_t target_ip;
KKASSERT(record->flush_state == HAMMER_FST_FLUSH);
KKASSERT(record->flags & HAMMER_RECF_INTERLOCK_BE);
if (error) {
hammer_critical_error(record->ip->hmp, record->ip, error,
"while flushing record");
}
--record->flush_group->refs;
record->flush_group = NULL;
if (record->flags & (HAMMER_RECF_DELETED_BE | HAMMER_RECF_COMMITTED)) {
if ((target_ip = record->target_ip) != NULL) {
TAILQ_REMOVE(&target_ip->target_list, record,
target_entry);
record->target_ip = NULL;
hammer_test_inode(target_ip);
}
record->flush_state = HAMMER_FST_IDLE;
} else {
if (record->target_ip) {
record->flush_state = HAMMER_FST_SETUP;
hammer_test_inode(record->ip);
hammer_test_inode(record->target_ip);
} else {
record->flush_state = HAMMER_FST_IDLE;
}
}
record->flags &= ~HAMMER_RECF_INTERLOCK_BE;
if (record->flags & HAMMER_RECF_WANTED) {
record->flags &= ~HAMMER_RECF_WANTED;
wakeup(record);
}
hammer_rel_mem_record(record);
}
void
hammer_rel_mem_record(hammer_record_t record)
{
hammer_mount_t hmp;
hammer_reserve_t resv;
hammer_inode_t ip;
hammer_inode_t target_ip;
int diddrop;
hammer_rel(&record->lock);
if (hammer_norefs(&record->lock)) {
ip = record->ip;
hmp = ip->hmp;
if (record->flags & (HAMMER_RECF_DELETED_FE |
HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
KKASSERT(hammer_isactive(&ip->lock) > 0);
KKASSERT(record->flush_state != HAMMER_FST_FLUSH);
if ((target_ip = record->target_ip) != NULL) {
TAILQ_REMOVE(&target_ip->target_list,
record, target_entry);
record->target_ip = NULL;
hammer_ref(&target_ip->lock);
}
if (record->flags & HAMMER_RECF_ONRBTREE) {
RB_REMOVE(hammer_rec_rb_tree,
&ip->rec_tree,
record);
record->flags &= ~HAMMER_RECF_ONRBTREE;
KKASSERT(ip->rsv_recs > 0);
if (RB_EMPTY(&ip->rec_tree)) {
ip->flags &= ~HAMMER_INODE_XDIRTY;
ip->sync_flags &= ~HAMMER_INODE_XDIRTY;
}
diddrop = 1;
} else {
diddrop = 0;
}
if (record->gflags &
(HAMMER_RECG_DIRECT_IO | HAMMER_RECG_DIRECT_INVAL)) {
hammer_io_direct_wait(record);
}
if (diddrop) {
--hmp->rsv_recs;
--ip->rsv_recs;
hmp->rsv_databytes -= record->leaf.data_len;
if (RB_EMPTY(&ip->rec_tree))
hammer_test_inode(ip);
if ((ip->flags & HAMMER_INODE_RECSW) &&
ip->rsv_recs <= hammer_limit_inode_recs/2) {
ip->flags &= ~HAMMER_INODE_RECSW;
wakeup(&ip->rsv_recs);
}
}
if (target_ip) {
hammer_test_inode(target_ip);
hammer_rel_inode(target_ip, 0);
}
if (record->flags & HAMMER_RECF_ALLOCDATA) {
--hammer_count_record_datas;
kfree(record->data, hmp->m_misc);
record->flags &= ~HAMMER_RECF_ALLOCDATA;
}
if ((resv = record->resv) != NULL) {
hammer_blockmap_reserve_complete(hmp, resv);
record->resv = NULL;
}
record->data = NULL;
--hammer_count_records;
kfree(record, hmp->m_misc);
}
}
}
static __inline
int
hammer_ip_iterate_mem_good(hammer_cursor_t cursor, hammer_record_t record)
{
if (cursor->flags & HAMMER_CURSOR_DELETE_VISIBILITY)
return(1);
if (cursor->flags & HAMMER_CURSOR_BACKEND) {
if (record->flags & (HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
return(0);
}
} else {
if (record->flags & (HAMMER_RECF_DELETED_FE |
HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
return(0);
}
}
return(1);
}
static
int
hammer_rec_scan_callback(hammer_record_t rec, void *data)
{
hammer_cursor_t cursor = data;
KKASSERT(cursor->iprec == NULL);
if (hammer_ip_iterate_mem_good(cursor, rec) == 0)
return(0);
if (cursor->flags & HAMMER_CURSOR_ASOF) {
if (cursor->asof < rec->leaf.base.create_tid)
return(0);
if (rec->leaf.base.delete_tid &&
cursor->asof >= rec->leaf.base.delete_tid) {
return(0);
}
}
hammer_ref(&rec->lock);
if (hammer_ip_iterate_mem_good(cursor, rec) == 0) {
hammer_rel_mem_record(rec);
return(0);
}
cursor->iprec = rec;
return(-1);
}
static
int
hammer_mem_lookup(hammer_cursor_t cursor)
{
KKASSERT(cursor->ip != NULL);
if (cursor->iprec) {
hammer_rel_mem_record(cursor->iprec);
cursor->iprec = NULL;
}
hammer_rec_rb_tree_RB_SCAN(&cursor->ip->rec_tree, hammer_rec_find_cmp,
hammer_rec_scan_callback, cursor);
return (cursor->iprec ? 0 : ENOENT);
}
static
int
hammer_mem_first(hammer_cursor_t cursor)
{
KKASSERT(cursor->ip != NULL);
if (cursor->iprec) {
hammer_rel_mem_record(cursor->iprec);
cursor->iprec = NULL;
}
hammer_rec_rb_tree_RB_SCAN(&cursor->ip->rec_tree, hammer_rec_scan_cmp,
hammer_rec_scan_callback, cursor);
if (cursor->iprec)
cursor->flags &= ~(HAMMER_CURSOR_MEMEOF | HAMMER_CURSOR_ATEMEM);
else
cursor->flags |= HAMMER_CURSOR_MEMEOF | HAMMER_CURSOR_ATEMEM;
return (cursor->iprec ? 0 : ENOENT);
}
int
hammer_ip_add_direntry(hammer_transaction_t trans,
hammer_inode_t dip, const char *name, int bytes,
hammer_inode_t ip)
{
struct hammer_cursor cursor;
hammer_record_t record;
int error;
uint32_t max_iterations;
KKASSERT(dip->ino_data.obj_type == HAMMER_OBJTYPE_DIRECTORY);
record = hammer_alloc_mem_record(dip, HAMMER_ENTRY_SIZE(bytes));
record->type = HAMMER_MEM_RECORD_ADD;
record->leaf.base.localization = dip->obj_localization |
hammer_dir_localization(dip);
record->leaf.base.obj_id = dip->obj_id;
record->leaf.base.key = hammer_direntry_namekey(dip, name, bytes,
&max_iterations);
record->leaf.base.rec_type = HAMMER_RECTYPE_DIRENTRY;
record->leaf.base.obj_type = ip->ino_leaf.base.obj_type;
record->data->entry.obj_id = ip->obj_id;
record->data->entry.localization = ip->obj_localization;
bcopy(name, record->data->entry.name, bytes);
++ip->ino_data.nlinks;
ip->ino_data.ctime = trans->time;
hammer_modify_inode(trans, ip, HAMMER_INODE_DDIRTY);
hammer_init_cursor(trans, &cursor, &dip->cache[1], dip);
cursor.key_beg = record->leaf.base;
cursor.flags |= HAMMER_CURSOR_ASOF;
cursor.flags |= HAMMER_CURSOR_DELETE_VISIBILITY;
cursor.asof = ip->obj_asof;
while (hammer_ip_lookup(&cursor) == 0) {
++record->leaf.base.key;
KKASSERT(record->leaf.base.key > 0);
cursor.key_beg.key = record->leaf.base.key;
if (--max_iterations == 0) {
hammer_rel_mem_record(record);
hmkprintf(trans->hmp, "Failed to find an unused namekey\n");
error = ENOSPC;
goto failed;
}
}
record->target_ip = ip;
record->flush_state = HAMMER_FST_SETUP;
TAILQ_INSERT_TAIL(&ip->target_list, record, target_entry);
if (ip->flush_state == HAMMER_FST_IDLE) {
hammer_ref(&ip->lock);
ip->flush_state = HAMMER_FST_SETUP;
if (ip->flags & HAMMER_INODE_FLUSHW)
ip->flags |= HAMMER_INODE_REFLUSH;
}
error = hammer_mem_add(record);
if (error == 0) {
dip->ino_data.mtime = trans->time;
dip->ino_data.ctime = trans->time;
hammer_modify_inode(trans, dip, HAMMER_INODE_MTIME | HAMMER_INODE_DDIRTY);
}
failed:
hammer_done_cursor(&cursor);
return(error);
}
int
hammer_ip_del_direntry(hammer_transaction_t trans,
hammer_cursor_t cursor, hammer_inode_t dip,
hammer_inode_t ip)
{
hammer_record_t record;
int error;
if (hammer_cursor_inmem(cursor)) {
record = cursor->iprec;
if (record->flags & (HAMMER_RECF_INTERLOCK_BE |
HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
KKASSERT(cursor->deadlk_rec == NULL);
hammer_ref(&record->lock);
cursor->deadlk_rec = record;
error = EDEADLK;
} else {
KKASSERT(record->type == HAMMER_MEM_RECORD_ADD);
record->flags |= HAMMER_RECF_DELETED_FE;
error = 0;
}
} else {
KKASSERT(dip->flags &
(HAMMER_INODE_ONDISK | HAMMER_INODE_DONDISK));
record = hammer_alloc_mem_record(dip, 0);
record->type = HAMMER_MEM_RECORD_DEL;
record->leaf.base = cursor->leaf->base;
KKASSERT(dip->obj_id == record->leaf.base.obj_id);
record->target_ip = ip;
if (ip) {
record->flush_state = HAMMER_FST_SETUP;
TAILQ_INSERT_TAIL(&ip->target_list, record,
target_entry);
} else {
record->flush_state = HAMMER_FST_IDLE;
}
if (ip && ip->flush_state == HAMMER_FST_IDLE) {
hammer_ref(&ip->lock);
ip->flush_state = HAMMER_FST_SETUP;
if (ip->flags & HAMMER_INODE_FLUSHW)
ip->flags |= HAMMER_INODE_REFLUSH;
}
error = hammer_mem_add(record);
}
if (error == 0) {
if (ip) {
--ip->ino_data.nlinks;
ip->ino_data.ctime = trans->time;
}
dip->ino_data.mtime = trans->time;
hammer_modify_inode(trans, dip, HAMMER_INODE_MTIME);
if (ip) {
hammer_modify_inode(trans, ip, HAMMER_INODE_DDIRTY);
if (ip->ino_data.nlinks == 0 &&
(ip->vp == NULL || (ip->vp->v_flag & VINACTIVE))) {
hammer_done_cursor(cursor);
hammer_inode_unloadable_check(ip, 1);
hammer_flush_inode(ip, 0);
}
}
}
return(error);
}
int
hammer_ip_add_record(hammer_transaction_t trans, hammer_record_t record)
{
hammer_inode_t ip = record->ip;
int error;
KKASSERT(record->leaf.base.localization != 0);
record->leaf.base.obj_id = ip->obj_id;
record->leaf.base.obj_type = ip->ino_leaf.base.obj_type;
error = hammer_mem_add(record);
return(error);
}
static hammer_record_t
hammer_ip_get_bulk(hammer_record_t record)
{
struct hammer_bulk_info info;
hammer_inode_t ip = record->ip;
info.record = record;
info.conflict = NULL;
hammer_rec_rb_tree_RB_SCAN(&ip->rec_tree, hammer_rec_overlap_cmp,
hammer_bulk_scan_callback, &info);
return(info.conflict);
}
static int
hammer_bulk_scan_callback(hammer_record_t record, void *data)
{
struct hammer_bulk_info *info = data;
if (record->flags & (HAMMER_RECF_DELETED_FE | HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED)) {
return(0);
}
hammer_ref(&record->lock);
info->conflict = record;
return(-1);
}
hammer_record_t
hammer_ip_add_bulk(hammer_inode_t ip, off_t file_offset, void *data, int bytes,
int *errorp)
{
hammer_record_t record;
int zone;
record = hammer_alloc_mem_record(ip, 0);
zone = hammer_data_zone_index(bytes);
record->resv = hammer_blockmap_reserve(ip->hmp, zone, bytes,
&record->leaf.data_offset,
errorp);
if (record->resv == NULL) {
hdkprintf("reservation failed\n");
hammer_rel_mem_record(record);
return(NULL);
}
record->type = HAMMER_MEM_RECORD_DATA;
record->leaf.base.rec_type = HAMMER_RECTYPE_DATA;
record->leaf.base.obj_type = ip->ino_leaf.base.obj_type;
record->leaf.base.obj_id = ip->obj_id;
record->leaf.base.key = file_offset + bytes;
record->leaf.base.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
record->leaf.data_len = bytes;
hammer_crc_set_leaf(ip->hmp->version, data, &record->leaf);
KKASSERT(*errorp == 0);
return(record);
}
void
hammer_ip_replace_bulk(hammer_mount_t hmp, hammer_record_t record)
{
hammer_record_t conflict;
int error __debugvar;
while ((conflict = hammer_ip_get_bulk(record)) != NULL) {
if ((conflict->flags & HAMMER_RECF_INTERLOCK_BE) == 0) {
conflict->flags |= HAMMER_RECF_DELETED_FE;
break;
}
conflict->flags |= HAMMER_RECF_WANTED;
tsleep(conflict, 0, "hmrrc3", 0);
hammer_rel_mem_record(conflict);
}
error = hammer_mem_add(record);
if (conflict)
hammer_rel_mem_record(conflict);
KKASSERT(error == 0);
}
int
hammer_ip_frontend_trunc(hammer_inode_t ip, off_t file_size)
{
struct rec_trunc_info info;
switch(ip->ino_data.obj_type) {
case HAMMER_OBJTYPE_REGFILE:
info.rec_type = HAMMER_RECTYPE_DATA;
break;
case HAMMER_OBJTYPE_DBFILE:
info.rec_type = HAMMER_RECTYPE_DB;
break;
default:
return(EINVAL);
}
info.trunc_off = file_size;
hammer_rec_rb_tree_RB_SCAN(&ip->rec_tree, hammer_rec_trunc_cmp,
hammer_frontend_trunc_callback, &info);
return(0);
}
static int
hammer_frontend_trunc_callback(hammer_record_t record, void *data __unused)
{
if (record->flags & HAMMER_RECF_DELETED_FE)
return(0);
#if 0
if (record->flush_state == HAMMER_FST_FLUSH)
return(0);
#endif
hammer_ref(&record->lock);
while (record->flags & HAMMER_RECF_INTERLOCK_BE)
hammer_wait_mem_record_ident(record, "hmmtrr");
record->flags |= HAMMER_RECF_DELETED_FE;
hammer_rel_mem_record(record);
return(0);
}
static int
hammer_record_needs_overwrite_delete(hammer_record_t record)
{
hammer_inode_t ip = record->ip;
int64_t file_offset;
int r;
if (ip->ino_data.obj_type == HAMMER_OBJTYPE_DBFILE)
file_offset = record->leaf.base.key;
else
file_offset = record->leaf.base.key - record->leaf.data_len;
r = (file_offset < ip->save_trunc_off);
if (ip->ino_data.obj_type == HAMMER_OBJTYPE_DBFILE) {
if (ip->save_trunc_off <= record->leaf.base.key)
ip->save_trunc_off = record->leaf.base.key + 1;
} else {
if (ip->save_trunc_off < record->leaf.base.key)
ip->save_trunc_off = record->leaf.base.key;
}
return(r);
}
int
hammer_ip_sync_record_cursor(hammer_cursor_t cursor, hammer_record_t record)
{
hammer_transaction_t trans = cursor->trans;
hammer_mount_t hmp = trans->hmp;
int64_t file_offset;
int bytes;
void *bdata;
int error;
int doprop;
KKASSERT(record->flush_state == HAMMER_FST_FLUSH);
KKASSERT(record->flags & HAMMER_RECF_INTERLOCK_BE);
KKASSERT(record->leaf.base.localization != 0);
if (record->gflags & (HAMMER_RECG_DIRECT_IO | HAMMER_RECG_DIRECT_INVAL))
hammer_io_direct_wait(record);
if (record->type == HAMMER_MEM_RECORD_DATA &&
hammer_record_needs_overwrite_delete(record)) {
file_offset = record->leaf.base.key - record->leaf.data_len;
bytes = HAMMER_BUFSIZE_DOALIGN(record->leaf.data_len);
KKASSERT((file_offset & HAMMER_BUFMASK) == 0);
error = hammer_ip_delete_range(
cursor, record->ip,
file_offset, file_offset + bytes - 1,
1);
if (error && error != ENOENT)
goto done;
}
if (record->type == HAMMER_MEM_RECORD_GENERAL) {
error = hammer_delete_general(cursor, record->ip, &record->leaf);
if (error && error != ENOENT)
goto done;
}
hammer_normalize_cursor(cursor);
cursor->key_beg = record->leaf.base;
cursor->flags &= ~HAMMER_CURSOR_INITMASK;
cursor->flags |= HAMMER_CURSOR_BACKEND;
cursor->flags &= ~HAMMER_CURSOR_INSERT;
record->ip->flags |= HAMMER_INODE_DONDISK;
if (record->type == HAMMER_MEM_RECORD_DEL) {
error = hammer_btree_lookup(cursor);
if (error == 0) {
KKASSERT(cursor->iprec == NULL);
error = hammer_ip_delete_record(cursor, record->ip,
trans->tid);
if (error == 0) {
record->flags |= HAMMER_RECF_DELETED_BE |
HAMMER_RECF_COMMITTED;
++record->ip->rec_generation;
}
}
goto done;
}
hammer_sync_lock_sh(trans);
cursor->flags |= HAMMER_CURSOR_INSERT;
error = hammer_btree_lookup(cursor);
if (hammer_debug_inode)
hdkprintf("DOINSERT LOOKUP %d\n", error);
if (error == 0) {
hdkprintf("duplicate rec at (%016jx)\n",
(intmax_t)record->leaf.base.key);
if (hammer_debug_critical)
Debugger("duplicate record1");
error = EIO;
}
if (error != ENOENT)
goto done_unlock;
if (record->type == HAMMER_MEM_RECORD_DATA) {
KKASSERT(record->leaf.data_offset != 0);
error = hammer_blockmap_finalize(trans,
record->resv,
record->leaf.data_offset,
record->leaf.data_len);
} else if (record->data && record->leaf.data_len) {
bdata = hammer_alloc_data(trans, record->leaf.data_len,
record->leaf.base.rec_type,
&record->leaf.data_offset,
&cursor->data_buffer,
0, &error);
if (bdata == NULL)
goto done_unlock;
hammer_crc_set_leaf(hmp->version, record->data, &record->leaf);
hammer_modify_buffer_noundo(trans, cursor->data_buffer);
bcopy(record->data, bdata, record->leaf.data_len);
hammer_modify_buffer_done(cursor->data_buffer);
} else {
record->leaf.data_offset = 0;
record->leaf.data_crc = 0;
}
error = hammer_btree_insert(cursor, &record->leaf, &doprop);
if (hammer_debug_inode && error) {
hdkprintf("BTREE INSERT error %d @ %016jx:%d key %016jx\n",
error,
(intmax_t)cursor->node->node_offset,
cursor->index,
(intmax_t)record->leaf.base.key);
}
if (error == 0) {
if (doprop) {
hammer_btree_do_propagation(cursor, &record->leaf);
}
if (record->flags & HAMMER_RECF_CONVERT_DELETE) {
KKASSERT(record->type == HAMMER_MEM_RECORD_ADD);
record->flags &= ~HAMMER_RECF_DELETED_FE;
record->type = HAMMER_MEM_RECORD_DEL;
KKASSERT(record->ip->obj_id == record->leaf.base.obj_id);
KKASSERT(record->flush_state == HAMMER_FST_FLUSH);
record->flags &= ~HAMMER_RECF_CONVERT_DELETE;
KKASSERT((record->flags & (HAMMER_RECF_COMMITTED |
HAMMER_RECF_DELETED_BE)) == 0);
} else {
record->flags |= HAMMER_RECF_COMMITTED;
++record->ip->rec_generation;
}
} else {
if (record->leaf.data_offset) {
hammer_blockmap_free(trans, record->leaf.data_offset,
record->leaf.data_len);
}
}
done_unlock:
hammer_sync_unlock(trans);
done:
return(error);
}
int
hammer_mem_add(hammer_record_t record)
{
hammer_mount_t hmp = record->ip->hmp;
if (record->data)
KKASSERT(record->flags & HAMMER_RECF_ALLOCDATA);
if (RB_INSERT(hammer_rec_rb_tree, &record->ip->rec_tree, record)) {
record->flags |= HAMMER_RECF_DELETED_FE;
hammer_rel_mem_record(record);
return (EEXIST);
}
++hmp->rsv_recs;
++record->ip->rsv_recs;
record->ip->hmp->rsv_databytes += record->leaf.data_len;
record->flags |= HAMMER_RECF_ONRBTREE;
hammer_modify_inode(NULL, record->ip, HAMMER_INODE_XDIRTY);
hammer_rel_mem_record(record);
return(0);
}
int
hammer_ip_lookup(hammer_cursor_t cursor)
{
int error;
KKASSERT(cursor->ip);
error = hammer_mem_lookup(cursor);
if (error == 0) {
cursor->leaf = &cursor->iprec->leaf;
return(error);
}
if (error != ENOENT)
return(error);
if ((cursor->ip->flags & (HAMMER_INODE_ONDISK|HAMMER_INODE_DONDISK)) == 0)
return(error);
error = hammer_btree_lookup(cursor);
if (error == 0)
error = hammer_btree_extract_leaf(cursor);
return(error);
}
static __inline
int
_hammer_ip_seek_btree(hammer_cursor_t cursor)
{
hammer_inode_t ip = cursor->ip;
int error;
if (ip->flags & (HAMMER_INODE_ONDISK|HAMMER_INODE_DONDISK)) {
error = hammer_btree_lookup(cursor);
if (error == ENOENT || error == EDEADLK) {
if (hammer_debug_general & 0x2000) {
hdkprintf("error %d node %p %016jx index %d\n",
error, cursor->node,
(intmax_t)cursor->node->node_offset,
cursor->index);
}
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
error = hammer_btree_iterate(cursor);
}
if (error == 0) {
cursor->flags &= ~(HAMMER_CURSOR_DISKEOF |
HAMMER_CURSOR_ATEDISK);
} else {
cursor->flags |= HAMMER_CURSOR_DISKEOF |
HAMMER_CURSOR_ATEDISK;
if (error == ENOENT)
error = 0;
}
} else {
cursor->flags |= HAMMER_CURSOR_DISKEOF | HAMMER_CURSOR_ATEDISK;
error = 0;
}
return(error);
}
static
int
_hammer_ip_reseek(hammer_cursor_t cursor)
{
struct hammer_base_elm save;
hammer_btree_elm_t elm;
int error __debugvar;
int r;
int again = 0;
hkprintf("Debug: re-seeked during scan @ino=%016jx\n",
(intmax_t)cursor->ip->obj_id);
save = cursor->key_beg;
cursor->key_beg = cursor->iprec->leaf.base;
error = _hammer_ip_seek_btree(cursor);
KKASSERT(error == 0);
cursor->key_beg = save;
if ((cursor->flags & HAMMER_CURSOR_ATEDISK) == 0 &&
(cursor->flags & HAMMER_CURSOR_LASTWASMEM)) {
elm = &cursor->node->ondisk->elms[cursor->index];
r = hammer_btree_cmp(&elm->base, &cursor->iprec->leaf.base);
if (cursor->flags & HAMMER_CURSOR_ASOF) {
if (r >= -1 && r <= 1) {
hkprintf("Debug: iterated after "
"re-seek (asof r=%d)\n", r);
cursor->flags |= HAMMER_CURSOR_ATEDISK;
again = 1;
}
} else {
if (r == 0) {
hkprintf("Debug: iterated after "
"re-seek\n");
cursor->flags |= HAMMER_CURSOR_ATEDISK;
again = 1;
}
}
}
return(again);
}
int
hammer_ip_first(hammer_cursor_t cursor)
{
hammer_inode_t ip __debugvar = cursor->ip;
int error;
KKASSERT(ip != NULL);
cursor->flags &= ~HAMMER_CURSOR_RETEST;
hammer_mem_first(cursor);
cursor->rec_generation = cursor->ip->rec_generation;
error = _hammer_ip_seek_btree(cursor);
if (error == 0)
error = hammer_ip_next(cursor);
return (error);
}
int
hammer_ip_next(hammer_cursor_t cursor)
{
hammer_btree_elm_t elm;
hammer_record_t rec;
hammer_record_t tmprec;
int error;
int r;
again:
error = 0;
if ((cursor->flags & HAMMER_CURSOR_DISKEOF) == 0) {
if (cursor->flags & (HAMMER_CURSOR_ATEDISK |
HAMMER_CURSOR_RETEST)) {
error = hammer_btree_iterate(cursor);
cursor->flags &= ~HAMMER_CURSOR_RETEST;
if (error == 0) {
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
hammer_cache_node(&cursor->ip->cache[1],
cursor->node);
} else if (error == ENOENT) {
cursor->flags |= HAMMER_CURSOR_DISKEOF |
HAMMER_CURSOR_ATEDISK;
error = 0;
}
}
}
while (cursor->rec_generation != cursor->ip->rec_generation &&
error == 0) {
hkprintf("Debug: generation changed during scan @ino=%016jx\n",
(intmax_t)cursor->ip->obj_id);
cursor->rec_generation = cursor->ip->rec_generation;
if (cursor->flags & HAMMER_CURSOR_MEMEOF)
break;
if (cursor->flags & HAMMER_CURSOR_DISKEOF) {
r = 1;
} else {
KKASSERT((cursor->flags & HAMMER_CURSOR_ATEDISK) == 0);
elm = &cursor->node->ondisk->elms[cursor->index];
r = hammer_btree_cmp(&elm->base,
&cursor->iprec->leaf.base);
}
if (r > 0) {
if (_hammer_ip_reseek(cursor))
goto again;
}
}
tmprec = NULL;
if ((cursor->flags & HAMMER_CURSOR_MEMEOF) == 0) {
if (cursor->flags & HAMMER_CURSOR_ATEMEM) {
tmprec = cursor->iprec;
cursor->iprec = NULL;
rec = hammer_rec_rb_tree_RB_NEXT(tmprec);
while (rec) {
if (hammer_rec_scan_cmp(rec, cursor) != 0)
break;
if (hammer_rec_scan_callback(rec, cursor) != 0)
break;
rec = hammer_rec_rb_tree_RB_NEXT(rec);
}
if (cursor->iprec) {
KKASSERT(cursor->iprec == rec);
cursor->flags &= ~HAMMER_CURSOR_ATEMEM;
} else {
cursor->flags |= HAMMER_CURSOR_MEMEOF;
}
cursor->flags &= ~HAMMER_CURSOR_LASTWASMEM;
}
}
if (error == 0 && (cursor->flags & HAMMER_CURSOR_MEMEOF) == 0) {
KKASSERT(cursor->iprec);
KKASSERT((cursor->flags & HAMMER_CURSOR_ATEMEM) == 0);
if (!hammer_ip_iterate_mem_good(cursor, cursor->iprec)) {
cursor->flags |= HAMMER_CURSOR_ATEMEM;
if (tmprec)
hammer_rel_mem_record(tmprec);
goto again;
}
}
if (tmprec)
hammer_rel_mem_record(tmprec);
error = 0;
switch(cursor->flags & (HAMMER_CURSOR_ATEDISK | HAMMER_CURSOR_ATEMEM)) {
case 0:
elm = &cursor->node->ondisk->elms[cursor->index];
r = hammer_btree_cmp(&elm->base, &cursor->iprec->leaf.base);
if (r <= 2 && r >= -2 && r != 0 &&
cursor->ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE &&
cursor->iprec->type == HAMMER_MEM_RECORD_DATA) {
int64_t base1 = elm->leaf.base.key - elm->leaf.data_len;
int64_t base2 = cursor->iprec->leaf.base.key -
cursor->iprec->leaf.data_len;
if (base1 == base2)
r = 0;
}
if (r < 0) {
error = hammer_btree_extract_leaf(cursor);
cursor->flags |= HAMMER_CURSOR_ATEDISK;
cursor->flags &= ~HAMMER_CURSOR_LASTWASMEM;
break;
}
if (r == 0) {
if (cursor->iprec->type == HAMMER_MEM_RECORD_DEL) {
if ((cursor->flags & HAMMER_CURSOR_DELETE_VISIBILITY) == 0) {
cursor->flags |= HAMMER_CURSOR_ATEDISK;
cursor->flags |= HAMMER_CURSOR_ATEMEM;
goto again;
}
} else if (cursor->iprec->type == HAMMER_MEM_RECORD_DATA) {
if ((cursor->flags & HAMMER_CURSOR_DELETE_VISIBILITY) == 0) {
cursor->flags |= HAMMER_CURSOR_ATEDISK;
}
} else {
hpanic("duplicate mem/B-Tree entry %p %d %08x",
cursor->iprec,
cursor->iprec->type,
cursor->iprec->flags);
cursor->flags |= HAMMER_CURSOR_ATEMEM;
goto again;
}
}
case HAMMER_CURSOR_ATEDISK:
cursor->leaf = &cursor->iprec->leaf;
cursor->flags |= HAMMER_CURSOR_ATEMEM;
cursor->flags |= HAMMER_CURSOR_LASTWASMEM;
if (cursor->iprec->type == HAMMER_MEM_RECORD_DEL &&
(cursor->flags & HAMMER_CURSOR_DELETE_VISIBILITY) == 0) {
hpanic("del-on-disk with no B-Tree entry iprec %p flags %08x",
cursor->iprec,
cursor->iprec->flags);
}
break;
case HAMMER_CURSOR_ATEMEM:
error = hammer_btree_extract_leaf(cursor);
cursor->flags |= HAMMER_CURSOR_ATEDISK;
cursor->flags &= ~HAMMER_CURSOR_LASTWASMEM;
break;
default:
cursor->flags &= ~HAMMER_CURSOR_LASTWASMEM;
cursor->leaf = NULL;
error = ENOENT;
break;
}
return(error);
}
int
hammer_ip_resolve_data(hammer_cursor_t cursor)
{
hammer_record_t record;
int error;
if (hammer_cursor_inmem(cursor)) {
record = cursor->iprec;
cursor->data = record->data;
error = 0;
if (cursor->data == NULL) {
hammer_io_direct_wait(record);
KKASSERT(record->leaf.base.rec_type ==
HAMMER_RECTYPE_DATA);
cursor->data = hammer_bread_ext(cursor->trans->hmp,
record->leaf.data_offset,
record->leaf.data_len,
&error,
&cursor->data_buffer);
}
} else {
cursor->leaf = &cursor->node->ondisk->elms[cursor->index].leaf;
error = hammer_btree_extract_data(cursor);
}
return(error);
}
int
hammer_ip_delete_range(hammer_cursor_t cursor, hammer_inode_t ip,
int64_t ran_beg, int64_t ran_end, int truncating)
{
hammer_transaction_t trans = cursor->trans;
hammer_btree_leaf_elm_t leaf;
int error;
int64_t off;
int64_t tmp64;
KKASSERT(trans->type == HAMMER_TRANS_FLS);
retry:
hammer_normalize_cursor(cursor);
cursor->key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
cursor->key_beg.obj_id = ip->obj_id;
cursor->key_beg.create_tid = 0;
cursor->key_beg.delete_tid = 0;
cursor->key_beg.obj_type = 0;
if (ip->ino_data.obj_type == HAMMER_OBJTYPE_DBFILE) {
cursor->key_beg.key = ran_beg;
cursor->key_beg.rec_type = HAMMER_RECTYPE_DB;
} else {
cursor->key_beg.key = ran_beg + 1;
cursor->key_beg.rec_type = HAMMER_RECTYPE_DATA;
}
cursor->key_end = cursor->key_beg;
if (ip->ino_data.obj_type == HAMMER_OBJTYPE_DBFILE) {
cursor->key_end.key = ran_end;
} else {
tmp64 = ran_end + MAXPHYS + 1;
if (tmp64 < ran_end)
cursor->key_end.key = HAMMER_MAX_KEY;
else
cursor->key_end.key = ran_end + MAXPHYS + 1;
}
cursor->asof = ip->obj_asof;
cursor->flags &= ~HAMMER_CURSOR_INITMASK;
cursor->flags |= HAMMER_CURSOR_ASOF;
cursor->flags |= HAMMER_CURSOR_DELETE_VISIBILITY;
cursor->flags |= HAMMER_CURSOR_BACKEND;
cursor->flags |= HAMMER_CURSOR_END_INCLUSIVE;
error = hammer_ip_first(cursor);
while (error == 0) {
leaf = cursor->leaf;
KKASSERT(leaf->base.delete_tid == 0);
KKASSERT(leaf->base.obj_id == ip->obj_id);
if (leaf->base.rec_type == HAMMER_RECTYPE_DATA) {
off = leaf->base.key - leaf->data_len;
if (off < ran_beg && leaf->base.key > ran_beg) {
hpanic("hammer left edge case %016jx %d",
(intmax_t)leaf->base.key,
leaf->data_len);
}
if (leaf->base.key - 1 > ran_end) {
if (leaf->base.key - leaf->data_len > ran_end)
break;
hpanic("hammer right edge case");
}
} else {
off = leaf->base.key;
}
if (truncating == 0 || hammer_cursor_ondisk(cursor)) {
error = hammer_ip_delete_record(cursor, ip, trans->tid);
if (truncating > 1 && error == 0 &&
hammer_flusher_meta_limit(ip->hmp)) {
ip->sync_trunc_off = off;
error = EWOULDBLOCK;
}
}
if (error)
break;
ran_beg = off;
error = hammer_ip_next(cursor);
}
if (cursor->node)
hammer_cache_node(&ip->cache[1], cursor->node);
if (error == EDEADLK) {
hammer_done_cursor(cursor);
error = hammer_init_cursor(trans, cursor, &ip->cache[1], ip);
if (error == 0)
goto retry;
}
if (error == ENOENT)
error = 0;
return(error);
}
static int
hammer_delete_general(hammer_cursor_t cursor, hammer_inode_t ip,
hammer_btree_leaf_elm_t leaf)
{
hammer_transaction_t trans = cursor->trans;
int error;
KKASSERT(trans->type == HAMMER_TRANS_FLS);
retry:
hammer_normalize_cursor(cursor);
cursor->key_beg = leaf->base;
cursor->asof = HAMMER_MAX_TID;
cursor->flags &= ~HAMMER_CURSOR_INITMASK;
cursor->flags |= HAMMER_CURSOR_ASOF;
cursor->flags |= HAMMER_CURSOR_BACKEND;
cursor->flags &= ~HAMMER_CURSOR_INSERT;
error = hammer_btree_lookup(cursor);
if (error == 0) {
error = hammer_ip_delete_record(cursor, ip, trans->tid);
}
if (error == EDEADLK) {
hammer_done_cursor(cursor);
error = hammer_init_cursor(trans, cursor, &ip->cache[1], ip);
if (error == 0)
goto retry;
}
return(error);
}
int
hammer_ip_delete_clean(hammer_cursor_t cursor, hammer_inode_t ip, int *countp)
{
hammer_transaction_t trans = cursor->trans;
hammer_btree_leaf_elm_t leaf __debugvar;
int error;
KKASSERT(trans->type == HAMMER_TRANS_FLS);
retry:
hammer_normalize_cursor(cursor);
cursor->key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
cursor->key_beg.obj_id = ip->obj_id;
cursor->key_beg.create_tid = 0;
cursor->key_beg.delete_tid = 0;
cursor->key_beg.obj_type = 0;
cursor->key_beg.rec_type = HAMMER_RECTYPE_CLEAN_START;
cursor->key_beg.key = HAMMER_MIN_KEY;
cursor->key_end = cursor->key_beg;
cursor->key_end.rec_type = HAMMER_RECTYPE_MAX;
cursor->key_end.key = HAMMER_MAX_KEY;
cursor->asof = ip->obj_asof;
cursor->flags &= ~HAMMER_CURSOR_INITMASK;
cursor->flags |= HAMMER_CURSOR_END_INCLUSIVE | HAMMER_CURSOR_ASOF;
cursor->flags |= HAMMER_CURSOR_DELETE_VISIBILITY;
cursor->flags |= HAMMER_CURSOR_BACKEND;
error = hammer_ip_first(cursor);
while (error == 0) {
leaf = cursor->leaf;
KKASSERT(leaf->base.delete_tid == 0);
error = hammer_ip_delete_record(cursor, ip, trans->tid);
++*countp;
if (error)
break;
error = hammer_ip_next(cursor);
}
if (cursor->node)
hammer_cache_node(&ip->cache[1], cursor->node);
if (error == EDEADLK) {
hammer_done_cursor(cursor);
error = hammer_init_cursor(trans, cursor, &ip->cache[1], ip);
if (error == 0)
goto retry;
}
if (error == ENOENT)
error = 0;
return(error);
}
int
hammer_ip_delete_record(hammer_cursor_t cursor, hammer_inode_t ip,
hammer_tid_t tid)
{
hammer_record_t iprec;
int error;
KKASSERT(cursor->flags & HAMMER_CURSOR_BACKEND);
KKASSERT(tid != 0);
if (hammer_cursor_inmem(cursor)) {
iprec = cursor->iprec;
KKASSERT((iprec->flags & HAMMER_RECF_INTERLOCK_BE) ==0);
iprec->flags |= HAMMER_RECF_DELETED_FE;
iprec->flags |= HAMMER_RECF_DELETED_BE;
KKASSERT(iprec->ip == ip);
++ip->rec_generation;
return(0);
}
error = hammer_btree_extract_leaf(cursor);
if (error == 0) {
error = hammer_delete_at_cursor(
cursor,
HAMMER_DELETE_ADJUST | hammer_nohistory(ip),
cursor->trans->tid,
cursor->trans->time32,
0, NULL);
}
return(error);
}
int
hammer_create_at_cursor(hammer_cursor_t cursor, hammer_btree_leaf_elm_t leaf,
void *udata, int mode)
{
hammer_transaction_t trans;
hammer_mount_t hmp;
hammer_buffer_t data_buffer;
hammer_off_t ndata_offset;
hammer_tid_t high_tid;
void *ndata;
int error;
int doprop;
trans = cursor->trans;
hmp = trans->hmp;
data_buffer = NULL;
ndata_offset = 0;
doprop = 0;
KKASSERT((cursor->flags &
(HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF)) ==
(HAMMER_CURSOR_BACKEND));
hammer_sync_lock_sh(trans);
if (leaf->data_len) {
ndata = hammer_alloc_data(trans, leaf->data_len,
leaf->base.rec_type,
&ndata_offset, &data_buffer,
0, &error);
if (ndata == NULL) {
hammer_sync_unlock(trans);
return (error);
}
leaf->data_offset = ndata_offset;
hammer_modify_buffer_noundo(trans, data_buffer);
switch(mode) {
case HAMMER_CREATE_MODE_UMIRROR:
error = copyin(udata, ndata, leaf->data_len);
if (error == 0) {
if (hammer_crc_test_leaf(hmp->version, ndata, leaf) == 0) {
hdkprintf("CRC DATA @ %016jx/%d MISMATCH ON PIPE\n",
(intmax_t)ndata_offset,
leaf->data_len);
error = EINVAL;
} else {
error = hammer_cursor_localize_data(
hmp, ndata, leaf);
}
}
break;
case HAMMER_CREATE_MODE_SYS:
bcopy(udata, ndata, leaf->data_len);
error = 0;
hammer_crc_set_leaf(hmp->version, ndata, leaf);
break;
default:
hpanic("bad mode %d", mode);
break;
}
hammer_modify_buffer_done(data_buffer);
} else {
leaf->data_offset = 0;
error = 0;
ndata = NULL;
}
if (error)
goto failed;
cursor->flags |= HAMMER_CURSOR_INSERT;
error = hammer_btree_lookup(cursor);
if (error != ENOENT) {
if (error == 0)
error = EALREADY;
goto failed;
}
error = hammer_btree_insert(cursor, leaf, &doprop);
cursor->flags |= HAMMER_CURSOR_ATEDISK;
cursor->flags &= ~HAMMER_CURSOR_INSERT;
if (error == 0 &&
leaf->base.rec_type == HAMMER_RECTYPE_INODE &&
leaf->base.delete_tid == 0) {
hammer_modify_volume_field(trans, trans->rootvol,
vol0_stat_inodes);
++trans->hmp->rootvol->ondisk->vol0_stat_inodes;
hammer_modify_volume_done(trans->rootvol);
}
high_tid = leaf->base.create_tid;
if (high_tid < leaf->base.delete_tid)
high_tid = leaf->base.delete_tid;
if (trans->rootvol->ondisk->vol0_next_tid < high_tid) {
hammer_modify_volume_noundo(trans, trans->rootvol);
trans->rootvol->ondisk->vol0_next_tid = high_tid;
hammer_modify_volume_done(trans->rootvol);
}
if (error == 0 && doprop)
hammer_btree_do_propagation(cursor, leaf);
failed:
if (error && leaf->data_offset) {
hammer_blockmap_free(trans, leaf->data_offset, leaf->data_len);
}
hammer_sync_unlock(trans);
if (data_buffer)
hammer_rel_buffer(data_buffer, 0);
return (error);
}
int
hammer_delete_at_cursor(hammer_cursor_t cursor, int delete_flags,
hammer_tid_t delete_tid, uint32_t delete_ts,
int track, int64_t *stat_bytes)
{
struct hammer_btree_leaf_elm save_leaf;
hammer_transaction_t trans;
hammer_btree_leaf_elm_t leaf;
hammer_node_t node;
hammer_btree_elm_t elm;
hammer_off_t data_offset;
int32_t data_len;
int64_t bytes;
int ndelete;
int error;
int icount;
int doprop;
error = hammer_cursor_upgrade(cursor);
if (error)
return(error);
trans = cursor->trans;
node = cursor->node;
elm = &node->ondisk->elms[cursor->index];
leaf = &elm->leaf;
KKASSERT(elm->base.btype == HAMMER_BTREE_TYPE_RECORD);
hammer_sync_lock_sh(trans);
bytes = 0;
doprop = 0;
icount = 0;
if (delete_flags & HAMMER_DELETE_ADJUST) {
if (elm->base.rec_type == HAMMER_RECTYPE_INODE) {
if (elm->leaf.base.delete_tid == 0 && delete_tid)
icount = -1;
if (elm->leaf.base.delete_tid && delete_tid == 0)
icount = 1;
}
hammer_modify_node(trans, node, elm, sizeof(*elm));
elm->leaf.base.delete_tid = delete_tid;
elm->leaf.delete_ts = delete_ts;
hammer_modify_node_done(node);
if (elm->leaf.base.delete_tid > node->ondisk->mirror_tid) {
hammer_modify_node_field(trans, node, mirror_tid);
node->ondisk->mirror_tid = elm->leaf.base.delete_tid;
hammer_modify_node_done(node);
doprop = 1;
if (hammer_debug_general & 0x0002) {
hdkprintf("propagate %016jx @%016jx\n",
(intmax_t)elm->leaf.base.delete_tid,
(intmax_t)node->node_offset);
}
}
if ((cursor->flags & HAMMER_CURSOR_DISKEOF) == 0) {
cursor->flags |= HAMMER_CURSOR_RETEST;
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
}
KKASSERT(elm->leaf.base.delete_tid !=
elm->leaf.base.create_tid);
}
if (delete_flags & HAMMER_DELETE_DESTROY) {
data_offset = elm->leaf.data_offset;
data_len = elm->leaf.data_len;
if (doprop) {
save_leaf = elm->leaf;
leaf = &save_leaf;
}
if (elm->base.rec_type == HAMMER_RECTYPE_INODE &&
elm->leaf.base.delete_tid == 0) {
icount = -1;
}
error = hammer_btree_delete(cursor, &ndelete);
if (error == 0) {
if ((cursor->flags & HAMMER_CURSOR_DISKEOF) == 0) {
cursor->flags |= HAMMER_CURSOR_RETEST;
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
}
bytes += (ndelete * sizeof(struct hammer_node_ondisk));
switch(HAMMER_ZONE(data_offset)) {
case HAMMER_ZONE_LARGE_DATA:
case HAMMER_ZONE_SMALL_DATA:
case HAMMER_ZONE_META:
hammer_blockmap_free(trans,
data_offset, data_len);
bytes += data_len;
break;
default:
break;
}
}
}
if (error == 0 && track) {
if (icount) {
hammer_modify_volume_field(trans, trans->rootvol,
vol0_stat_inodes);
trans->rootvol->ondisk->vol0_stat_inodes += icount;
hammer_modify_volume_done(trans->rootvol);
}
if (trans->rootvol->ondisk->vol0_next_tid < delete_tid) {
hammer_modify_volume_noundo(trans, trans->rootvol);
trans->rootvol->ondisk->vol0_next_tid = delete_tid;
hammer_modify_volume_done(trans->rootvol);
}
}
if (doprop) {
if (cursor->ip)
hammer_btree_do_propagation(cursor, leaf);
else
hammer_btree_do_propagation(cursor, leaf);
}
if (stat_bytes)
*stat_bytes = bytes;
hammer_sync_unlock(trans);
return (error);
}
int
hammer_ip_check_directory_empty(hammer_transaction_t trans, hammer_inode_t ip)
{
struct hammer_cursor cursor;
int error;
hammer_init_cursor(trans, &cursor, &ip->cache[1], ip);
cursor.key_beg.localization = ip->obj_localization |
hammer_dir_localization(ip);
cursor.key_beg.obj_id = ip->obj_id;
cursor.key_beg.create_tid = 0;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.obj_type = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_ENTRY_START;
cursor.key_beg.key = HAMMER_MIN_KEY;
cursor.key_end = cursor.key_beg;
cursor.key_end.rec_type = HAMMER_RECTYPE_MAX;
cursor.key_end.key = HAMMER_MAX_KEY;
cursor.asof = ip->obj_asof;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE | HAMMER_CURSOR_ASOF;
error = hammer_ip_first(&cursor);
if (error == ENOENT)
error = 0;
else if (error == 0)
error = ENOTEMPTY;
hammer_done_cursor(&cursor);
return(error);
}
static
int
hammer_cursor_localize_data(hammer_mount_t hmp, hammer_data_ondisk_t data,
hammer_btree_leaf_elm_t leaf)
{
uint32_t localization;
if (leaf->base.rec_type == HAMMER_RECTYPE_DIRENTRY) {
localization = leaf->base.localization &
HAMMER_LOCALIZE_PSEUDOFS_MASK;
if (data->entry.localization != localization) {
data->entry.localization = localization;
hammer_crc_set_leaf(hmp->version, data, leaf);
}
}
return(0);
}