#include "hammer.h"
static int prune_should_delete(struct hammer_ioc_prune *prune,
hammer_btree_leaf_elm_t elm);
static void prune_check_nlinks(hammer_cursor_t cursor,
hammer_btree_leaf_elm_t elm);
int
hammer_ioc_prune(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_prune *prune)
{
struct hammer_cursor cursor;
hammer_btree_leaf_elm_t elm;
struct hammer_ioc_prune_elm *copy_elms;
struct hammer_ioc_prune_elm *user_elms;
int error;
int isdir;
int elm_array_size;
int seq;
int64_t bytes;
uint32_t key_beg_localization;
if (prune->nelms < 0 || prune->nelms > HAMMER_MAX_PRUNE_ELMS)
return(EINVAL);
if ((prune->key_beg.localization | prune->key_end.localization) &
HAMMER_LOCALIZE_PSEUDOFS_MASK) {
return(EINVAL);
}
if (prune->key_beg.localization > prune->key_end.localization)
return(EINVAL);
if (prune->key_beg.localization == prune->key_end.localization) {
if (prune->key_beg.obj_id > prune->key_end.obj_id)
return(EINVAL);
}
if ((prune->head.flags & HAMMER_IOC_PRUNE_ALL) && prune->nelms)
return(EINVAL);
key_beg_localization = prune->key_beg.localization;
key_beg_localization &= HAMMER_LOCALIZE_MASK;
key_beg_localization |= ip->obj_localization;
prune->key_cur.localization = prune->key_end.localization;
prune->key_cur.localization &= HAMMER_LOCALIZE_MASK;
prune->key_cur.localization |= ip->obj_localization;
prune->key_cur.obj_id = prune->key_end.obj_id;
prune->key_cur.key = HAMMER_MAX_KEY;
elm_array_size = sizeof(*copy_elms) * prune->nelms;
user_elms = prune->elms;
copy_elms = kmalloc(elm_array_size, M_TEMP, M_WAITOK);
if ((error = copyin(user_elms, copy_elms, elm_array_size)) != 0)
goto failed;
prune->elms = copy_elms;
seq = trans->hmp->flusher.done;
retry:
error = hammer_init_cursor(trans, &cursor, NULL, NULL);
if (error) {
hammer_done_cursor(&cursor);
goto failed;
}
cursor.key_beg.localization = key_beg_localization;
cursor.key_beg.obj_id = prune->key_beg.obj_id;
cursor.key_beg.key = HAMMER_MIN_KEY;
cursor.key_beg.create_tid = 1;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.rec_type = HAMMER_MIN_RECTYPE;
cursor.key_beg.obj_type = 0;
cursor.key_end.localization = prune->key_cur.localization;
cursor.key_end.obj_id = prune->key_cur.obj_id;
cursor.key_end.key = prune->key_cur.key;
cursor.key_end.create_tid = HAMMER_MAX_TID - 1;
cursor.key_end.delete_tid = 0;
cursor.key_end.rec_type = HAMMER_MAX_RECTYPE;
cursor.key_end.obj_type = 0;
cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
cursor.flags |= HAMMER_CURSOR_BACKEND;
cursor.flags |= HAMMER_CURSOR_PRUNING;
hammer_sync_lock_sh(trans);
error = hammer_btree_last(&cursor);
hammer_sync_unlock(trans);
while (error == 0) {
elm = &cursor.node->ondisk->elms[cursor.index].leaf;
prune->key_cur = elm->base;
if (trans->hmp->ronly) {
error = EROFS;
break;
}
if ((error = hammer_signal_check(trans->hmp)) != 0)
break;
if (prune->stat_oldest_tid > elm->base.create_tid)
prune->stat_oldest_tid = elm->base.create_tid;
if (hammer_debug_general & 0x0200) {
hdkprintf("check %016jx %016jx cre=%016jx del=%016jx\n",
(intmax_t)elm->base.obj_id,
(intmax_t)elm->base.key,
(intmax_t)elm->base.create_tid,
(intmax_t)elm->base.delete_tid);
}
if (prune_should_delete(prune, elm)) {
if (hammer_debug_general & 0x0200) {
hdkprintf("check %016jx %016jx: DELETE\n",
(intmax_t)elm->base.obj_id,
(intmax_t)elm->base.key);
}
isdir = (elm->base.rec_type == HAMMER_RECTYPE_DIRENTRY);
hammer_sync_lock_sh(trans);
error = hammer_delete_at_cursor(&cursor,
HAMMER_DELETE_DESTROY,
cursor.trans->tid,
cursor.trans->time32,
0, &bytes);
hammer_sync_unlock(trans);
if (error)
break;
if (isdir)
++prune->stat_dirrecords;
else
++prune->stat_rawrecords;
prune->stat_bytes += bytes;
cursor.flags |= HAMMER_CURSOR_ATEDISK;
} else {
prune_check_nlinks(&cursor, elm);
cursor.flags |= HAMMER_CURSOR_ATEDISK;
if (hammer_debug_general & 0x0100) {
hdkprintf("check %016jx %016jx: SKIP\n",
(intmax_t)elm->base.obj_id,
(intmax_t)elm->base.key);
}
}
++prune->stat_scanrecords;
while (hammer_flusher_meta_halflimit(trans->hmp) ||
hammer_flusher_undo_exhausted(trans, 2)) {
hammer_unlock_cursor(&cursor);
hammer_flusher_wait(trans->hmp, seq);
hammer_lock_cursor(&cursor);
seq = hammer_flusher_async_one(trans->hmp);
}
hammer_sync_lock_sh(trans);
error = hammer_btree_iterate_reverse(&cursor);
hammer_sync_unlock(trans);
}
if (error == ENOENT)
error = 0;
hammer_done_cursor(&cursor);
if (error == EDEADLK)
goto retry;
if (error == EINTR) {
prune->head.flags |= HAMMER_IOC_HEAD_INTR;
error = 0;
}
failed:
prune->key_cur.localization &= HAMMER_LOCALIZE_MASK;
prune->elms = user_elms;
kfree(copy_elms, M_TEMP);
return(error);
}
static int
prune_should_delete(struct hammer_ioc_prune *prune, hammer_btree_leaf_elm_t elm)
{
struct hammer_ioc_prune_elm *scan;
int i;
if (prune->head.flags & HAMMER_IOC_PRUNE_ALL) {
if (elm->base.delete_tid != 0)
return(1);
return(0);
}
for (i = 0; i < prune->nelms; ++i) {
scan = &prune->elms[i];
if (elm->base.create_tid >= scan->end_tid ||
elm->base.delete_tid > scan->end_tid) {
break;
}
if (elm->base.delete_tid &&
elm->base.create_tid >= scan->beg_tid &&
elm->base.delete_tid <= scan->end_tid &&
(elm->base.create_tid - scan->beg_tid) / scan->mod_tid ==
(elm->base.delete_tid - scan->beg_tid) / scan->mod_tid) {
return(1);
}
}
return(0);
}
static
void
prune_check_nlinks(hammer_cursor_t cursor, hammer_btree_leaf_elm_t elm)
{
hammer_inode_t ip;
int error;
if (elm->base.rec_type != HAMMER_RECTYPE_INODE)
return;
if (elm->base.delete_tid != 0)
return;
if (hammer_btree_extract_data(cursor))
return;
if (cursor->data->inode.nlinks)
return;
hammer_cursor_downgrade(cursor);
ip = hammer_get_inode(cursor->trans, NULL, elm->base.obj_id,
HAMMER_MAX_TID,
elm->base.localization & HAMMER_LOCALIZE_PSEUDOFS_MASK,
0, &error);
if (ip) {
if (hammer_debug_general & 0x0001) {
hdkprintf("pruning disconnected inode %016jx\n",
(intmax_t)elm->base.obj_id);
}
hammer_rel_inode(ip, 0);
hammer_inode_waitreclaims(cursor->trans);
} else {
hkprintf("unable to prune disconnected inode %016jx\n",
(intmax_t)elm->base.obj_id);
}
}