#include "hammer.h"
static int hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_history *hist);
static int hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_synctid *std);
static int hammer_ioc_get_version(hammer_transaction_t trans,
hammer_inode_t ip,
struct hammer_ioc_version *ver);
static int hammer_ioc_set_version(hammer_transaction_t trans,
hammer_inode_t ip,
struct hammer_ioc_version *ver);
static int hammer_ioc_get_info(hammer_transaction_t trans,
struct hammer_ioc_info *info);
static int hammer_ioc_add_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_snapshot *snap);
static int hammer_ioc_del_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_snapshot *snap);
static int hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_snapshot *snap);
static int hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_config *snap);
static int hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_config *snap);
static int hammer_ioc_get_data(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_data *data);
int
hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
struct ucred *cred)
{
struct hammer_transaction trans;
struct hammer_mount *hmp;
int error;
error = caps_priv_check(cred, SYSCAP_NOVFS_IOCTL);
hmp = ip->hmp;
hammer_start_transaction(&trans, hmp);
switch(com) {
case HAMMERIOC_PRUNE:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_prune(&trans, ip,
(struct hammer_ioc_prune *)data);
}
break;
case HAMMERIOC_GETHISTORY:
error = hammer_ioc_gethistory(&trans, ip,
(struct hammer_ioc_history *)data);
break;
case HAMMERIOC_REBLOCK:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_reblock(&trans, ip,
(struct hammer_ioc_reblock *)data);
}
break;
case HAMMERIOC_REBALANCE:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0 && nbuf < HAMMER_REBALANCE_MIN_BUFS) {
hkprintf("System has insufficient buffers "
"to rebalance the tree. nbuf < %d\n",
HAMMER_REBALANCE_MIN_BUFS);
error = ENOSPC;
}
if (error == 0) {
error = hammer_ioc_rebalance(&trans, ip,
(struct hammer_ioc_rebalance *)data);
}
break;
case HAMMERIOC_SYNCTID:
error = hammer_ioc_synctid(&trans, ip,
(struct hammer_ioc_synctid *)data);
break;
case HAMMERIOC_GET_PSEUDOFS:
error = hammer_ioc_get_pseudofs(&trans, ip,
(struct hammer_ioc_pseudofs_rw *)data);
break;
case HAMMERIOC_SET_PSEUDOFS:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_set_pseudofs(&trans, ip, cred,
(struct hammer_ioc_pseudofs_rw *)data);
}
break;
case HAMMERIOC_UPG_PSEUDOFS:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_upgrade_pseudofs(&trans, ip,
(struct hammer_ioc_pseudofs_rw *)data);
}
break;
case HAMMERIOC_DGD_PSEUDOFS:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_downgrade_pseudofs(&trans, ip,
(struct hammer_ioc_pseudofs_rw *)data);
}
break;
case HAMMERIOC_RMR_PSEUDOFS:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_destroy_pseudofs(&trans, ip,
(struct hammer_ioc_pseudofs_rw *)data);
}
break;
case HAMMERIOC_WAI_PSEUDOFS:
if (error == 0) {
error = hammer_ioc_wait_pseudofs(&trans, ip,
(struct hammer_ioc_pseudofs_rw *)data);
}
break;
case HAMMERIOC_MIRROR_READ:
if (error == 0) {
error = hammer_ioc_mirror_read(&trans, ip,
(struct hammer_ioc_mirror_rw *)data);
}
break;
case HAMMERIOC_MIRROR_WRITE:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_mirror_write(&trans, ip,
(struct hammer_ioc_mirror_rw *)data);
}
break;
case HAMMERIOC_GET_VERSION:
error = hammer_ioc_get_version(&trans, ip,
(struct hammer_ioc_version *)data);
break;
case HAMMERIOC_GET_INFO:
error = hammer_ioc_get_info(&trans,
(struct hammer_ioc_info *)data);
break;
case HAMMERIOC_SET_VERSION:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_set_version(&trans, ip,
(struct hammer_ioc_version *)data);
}
break;
case HAMMERIOC_ADD_VOLUME:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = caps_priv_check(cred, SYSCAP_NOVFS_IOCTL);
if (error == 0)
error = hammer_ioc_volume_add(&trans, ip,
(struct hammer_ioc_volume *)data);
}
break;
case HAMMERIOC_DEL_VOLUME:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = caps_priv_check(cred, SYSCAP_NOVFS_IOCTL);
if (error == 0)
error = hammer_ioc_volume_del(&trans, ip,
(struct hammer_ioc_volume *)data);
}
break;
case HAMMERIOC_LIST_VOLUMES:
error = hammer_ioc_volume_list(&trans, ip,
(struct hammer_ioc_volume_list *)data);
break;
case HAMMERIOC_ADD_SNAPSHOT:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_add_snapshot(
&trans, ip, (struct hammer_ioc_snapshot *)data);
}
break;
case HAMMERIOC_DEL_SNAPSHOT:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_del_snapshot(
&trans, ip, (struct hammer_ioc_snapshot *)data);
}
break;
case HAMMERIOC_GET_SNAPSHOT:
error = hammer_ioc_get_snapshot(
&trans, ip, (struct hammer_ioc_snapshot *)data);
break;
case HAMMERIOC_GET_CONFIG:
error = hammer_ioc_get_config(
&trans, ip, (struct hammer_ioc_config *)data);
break;
case HAMMERIOC_SET_CONFIG:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_set_config(
&trans, ip, (struct hammer_ioc_config *)data);
}
break;
case HAMMERIOC_DEDUP:
if (error == 0 && hmp->ronly)
error = EROFS;
if (error == 0) {
error = hammer_ioc_dedup(
&trans, ip, (struct hammer_ioc_dedup *)data);
}
break;
case HAMMERIOC_GET_DATA:
if (error == 0) {
error = hammer_ioc_get_data(
&trans, ip, (struct hammer_ioc_data *)data);
}
break;
case HAMMERIOC_SCAN_PSEUDOFS:
error = hammer_ioc_scan_pseudofs(
&trans, ip, (struct hammer_ioc_pseudofs_rw *)data);
break;
default:
error = EOPNOTSUPP;
break;
}
hammer_done_transaction(&trans);
return (error);
}
static void add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
hammer_btree_elm_t elm);
static
int
hammer_ioc_gethistory(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_history *hist)
{
struct hammer_cursor cursor;
hammer_btree_elm_t elm;
int error;
if (hist->beg_tid > hist->end_tid)
return(EINVAL);
if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
if (hist->key > hist->nxt_key)
return(EINVAL);
}
hist->obj_id = ip->obj_id;
hist->count = 0;
hist->nxt_tid = hist->end_tid;
hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_TID;
hist->head.flags &= ~HAMMER_IOC_HISTORY_NEXT_KEY;
hist->head.flags &= ~HAMMER_IOC_HISTORY_EOF;
hist->head.flags &= ~HAMMER_IOC_HISTORY_UNSYNCED;
if ((ip->flags & HAMMER_INODE_MODMASK) &
~(HAMMER_INODE_ATIME | HAMMER_INODE_MTIME)) {
hist->head.flags |= HAMMER_IOC_HISTORY_UNSYNCED;
}
error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
if (error) {
hammer_done_cursor(&cursor);
return(error);
}
cursor.key_beg.obj_id = hist->obj_id;
cursor.key_beg.create_tid = hist->beg_tid;
cursor.key_beg.delete_tid = 0;
cursor.key_beg.obj_type = 0;
if (cursor.key_beg.create_tid == HAMMER_MIN_TID)
cursor.key_beg.create_tid = 1;
cursor.key_end.obj_id = hist->obj_id;
cursor.key_end.create_tid = hist->end_tid;
cursor.key_end.delete_tid = 0;
cursor.key_end.obj_type = 0;
cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE;
if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
cursor.key_beg.key = hist->key;
cursor.key_end.key = HAMMER_MAX_KEY;
cursor.key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
cursor.key_end.localization = ip->obj_localization |
HAMMER_LOCALIZE_MISC;
switch(ip->ino_data.obj_type) {
case HAMMER_OBJTYPE_REGFILE:
++cursor.key_beg.key;
cursor.key_beg.rec_type = HAMMER_RECTYPE_DATA;
break;
case HAMMER_OBJTYPE_DIRECTORY:
cursor.key_beg.rec_type = HAMMER_RECTYPE_DIRENTRY;
cursor.key_beg.localization = ip->obj_localization |
hammer_dir_localization(ip);
cursor.key_end.localization = ip->obj_localization |
hammer_dir_localization(ip);
break;
case HAMMER_OBJTYPE_DBFILE:
cursor.key_beg.rec_type = HAMMER_RECTYPE_DB;
break;
default:
error = EINVAL;
break;
}
cursor.key_end.rec_type = cursor.key_beg.rec_type;
} else {
cursor.key_beg.key = 0;
cursor.key_end.key = 0;
cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
cursor.key_end.rec_type = HAMMER_RECTYPE_INODE;
cursor.key_beg.localization = ip->obj_localization |
HAMMER_LOCALIZE_INODE;
cursor.key_end.localization = ip->obj_localization |
HAMMER_LOCALIZE_INODE;
}
error = hammer_btree_first(&cursor);
while (error == 0) {
elm = &cursor.node->ondisk->elms[cursor.index];
add_history(ip, hist, elm);
if (hist->head.flags & (HAMMER_IOC_HISTORY_NEXT_TID |
HAMMER_IOC_HISTORY_NEXT_KEY |
HAMMER_IOC_HISTORY_EOF)) {
break;
}
error = hammer_btree_iterate(&cursor);
}
if (error == ENOENT) {
hist->head.flags |= HAMMER_IOC_HISTORY_EOF;
error = 0;
}
hammer_done_cursor(&cursor);
return(error);
}
static void
add_history(hammer_inode_t ip, struct hammer_ioc_history *hist,
hammer_btree_elm_t elm)
{
int i;
if (elm->base.btype != HAMMER_BTREE_TYPE_RECORD)
return;
if ((hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) &&
ip->ino_data.obj_type == HAMMER_OBJTYPE_REGFILE) {
if (hist->nxt_key > elm->leaf.base.key - elm->leaf.data_len &&
hist->key < elm->leaf.base.key - elm->leaf.data_len) {
hist->nxt_key = elm->leaf.base.key - elm->leaf.data_len;
}
if (hist->nxt_key > elm->leaf.base.key)
hist->nxt_key = elm->leaf.base.key;
if (elm->leaf.base.key >= MAXPHYS &&
elm->leaf.base.key - MAXPHYS > hist->key) {
hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
}
if (elm->leaf.base.key - elm->leaf.data_len > hist->key)
return;
} else if (hist->head.flags & HAMMER_IOC_HISTORY_ATKEY) {
if (hist->nxt_key > elm->leaf.base.key &&
hist->key < elm->leaf.base.key) {
hist->nxt_key = elm->leaf.base.key;
}
if (elm->leaf.base.key > hist->key)
hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_KEY;
}
i = hist->count;
if ((i == 0 ||
elm->leaf.base.create_tid != hist->hist_ary[i - 1].tid) &&
elm->leaf.base.create_tid >= hist->beg_tid &&
elm->leaf.base.create_tid < hist->end_tid) {
if (hist->count == HAMMER_MAX_HISTORY_ELMS) {
hist->nxt_tid = elm->leaf.base.create_tid;
hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
return;
}
hist->hist_ary[i].tid = elm->leaf.base.create_tid;
hist->hist_ary[i].time32 = elm->leaf.create_ts;
++hist->count;
}
i = hist->count;
if (elm->leaf.base.delete_tid &&
elm->leaf.base.delete_tid >= hist->beg_tid &&
elm->leaf.base.delete_tid < hist->end_tid) {
if (i == HAMMER_MAX_HISTORY_ELMS) {
hist->nxt_tid = elm->leaf.base.delete_tid;
hist->head.flags |= HAMMER_IOC_HISTORY_NEXT_TID;
return;
}
hist->hist_ary[i].tid = elm->leaf.base.delete_tid;
hist->hist_ary[i].time32 = elm->leaf.delete_ts;
++hist->count;
}
}
static
int
hammer_ioc_synctid(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_synctid *std)
{
hammer_mount_t hmp = ip->hmp;
int error = 0;
switch(std->op) {
case HAMMER_SYNCTID_NONE:
std->tid = hmp->flusher.tid;
break;
case HAMMER_SYNCTID_ASYNC:
hammer_queue_inodes_flusher(hmp, MNT_NOWAIT);
hammer_flusher_async(hmp, NULL);
std->tid = hmp->flusher.tid;
break;
case HAMMER_SYNCTID_SYNC1:
hammer_queue_inodes_flusher(hmp, MNT_WAIT);
hammer_flusher_sync(hmp);
std->tid = hmp->flusher.tid;
break;
case HAMMER_SYNCTID_SYNC2:
hammer_queue_inodes_flusher(hmp, MNT_WAIT);
hammer_flusher_sync(hmp);
std->tid = hmp->flusher.tid;
hammer_flusher_sync(hmp);
break;
default:
error = EOPNOTSUPP;
break;
}
return(error);
}
static
int
hammer_ioc_get_version(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_version *ver)
{
int error = 0;
ver->min_version = HAMMER_VOL_VERSION_MIN;
ver->wip_version = HAMMER_VOL_VERSION_WIP;
ver->max_version = HAMMER_VOL_VERSION_MAX;
if (ver->cur_version == 0)
ver->cur_version = trans->hmp->version;
switch(ver->cur_version) {
case 1:
ksnprintf(ver->description, sizeof(ver->description),
"First HAMMER release (DragonFly 2.0+)");
break;
case 2:
ksnprintf(ver->description, sizeof(ver->description),
"New directory entry layout (DragonFly 2.3+)");
break;
case 3:
ksnprintf(ver->description, sizeof(ver->description),
"New snapshot management (DragonFly 2.5+)");
break;
case 4:
ksnprintf(ver->description, sizeof(ver->description),
"New undo/flush, faster flush/sync (DragonFly 2.5+)");
break;
case 5:
ksnprintf(ver->description, sizeof(ver->description),
"Adjustments for dedup support (DragonFly 2.9+)");
break;
case 6:
ksnprintf(ver->description, sizeof(ver->description),
"Directory Hash ALG1 (tmp/rename resistance)");
break;
case 7:
ksnprintf(ver->description, sizeof(ver->description),
"Use ISCSI CRC (faster than original crc32)");
break;
default:
ksnprintf(ver->description, sizeof(ver->description),
"Unknown");
error = EINVAL;
break;
}
return(error);
};
static
int
hammer_ioc_set_version(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_version *ver)
{
hammer_mount_t hmp = trans->hmp;
struct hammer_cursor cursor;
hammer_volume_t volume;
int error;
int over = hmp->version;
if (ver->cur_version < hmp->version) {
if (!(ver->cur_version == 3 && hmp->version == 4))
return(EINVAL);
}
if (ver->cur_version == hmp->version)
return(0);
if (ver->cur_version > HAMMER_VOL_VERSION_MAX)
return(EINVAL);
if (hmp->ronly)
return(EROFS);
error = hammer_init_cursor(trans, &cursor, NULL, NULL);
if (error)
goto failed;
hammer_lock_ex(&hmp->flusher.finalize_lock);
hammer_sync_lock_ex(trans);
hmp->version = ver->cur_version;
if (over < HAMMER_VOL_VERSION_FOUR &&
ver->cur_version >= HAMMER_VOL_VERSION_FOUR) {
hkprintf("upgrade undo to version 4\n");
error = hammer_upgrade_undo_4(trans);
if (error)
goto failed;
}
volume = hammer_get_root_volume(hmp, &error);
KKASSERT(error == 0);
hammer_modify_volume_field(cursor.trans, volume, vol_version);
volume->ondisk->vol_version = ver->cur_version;
hammer_modify_volume_done(volume);
hammer_rel_volume(volume, 0);
hammer_sync_unlock(trans);
hammer_unlock(&hmp->flusher.finalize_lock);
failed:
ver->head.error = error;
hammer_done_cursor(&cursor);
return(0);
}
static
int
hammer_ioc_get_info(hammer_transaction_t trans, struct hammer_ioc_info *info)
{
hammer_volume_ondisk_t ondisk = trans->hmp->rootvol->ondisk;
hammer_mount_t hmp = trans->hmp;
_hammer_checkspace(hmp, HAMMER_CHKSPC_WRITE, &info->rsvbigblocks);
info->rsvbigblocks = info->rsvbigblocks >> HAMMER_BIGBLOCK_BITS;
strlcpy(info->vol_label, ondisk->vol_label, sizeof(ondisk->vol_label));
info->vol_fsid = hmp->fsid;
info->vol_fstype = ondisk->vol_fstype;
info->version = hmp->version;
info->inodes = ondisk->vol0_stat_inodes;
info->bigblocks = ondisk->vol0_stat_bigblocks;
info->freebigblocks = ondisk->vol0_stat_freebigblocks;
info->nvolumes = hmp->nvolumes;
info->rootvol = ondisk->vol_rootvol;
return 0;
}
static
int
hammer_ioc_add_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_snapshot *snap)
{
hammer_mount_t hmp = ip->hmp;
struct hammer_btree_leaf_elm leaf;
struct hammer_cursor cursor;
int error;
if (snap->count > HAMMER_SNAPS_PER_IOCTL)
return (EINVAL);
if (snap->index >= snap->count)
return (EINVAL);
hammer_lock_ex(&hmp->snapshot_lock);
again:
error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
if (error) {
hammer_done_cursor(&cursor);
return(error);
}
cursor.asof = HAMMER_MAX_TID;
cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF;
bzero(&leaf, sizeof(leaf));
leaf.base.obj_id = HAMMER_OBJID_ROOT;
leaf.base.rec_type = HAMMER_RECTYPE_SNAPSHOT;
leaf.base.create_tid = hammer_alloc_tid(hmp, 1);
leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
leaf.base.localization = ip->obj_localization | HAMMER_LOCALIZE_INODE;
leaf.data_len = sizeof(struct hammer_snapshot_data);
while (snap->index < snap->count) {
leaf.base.key = (int64_t)snap->snaps[snap->index].tid;
cursor.key_beg = leaf.base;
error = hammer_btree_lookup(&cursor);
if (error == 0) {
error = EEXIST;
break;
}
cursor.flags &= ~HAMMER_CURSOR_ASOF;
cursor.key_beg = leaf.base;
error = hammer_create_at_cursor(&cursor, &leaf,
&snap->snaps[snap->index],
HAMMER_CREATE_MODE_SYS);
if (error == EDEADLK) {
hammer_done_cursor(&cursor);
goto again;
}
cursor.flags |= HAMMER_CURSOR_ASOF;
if (error)
break;
++snap->index;
}
snap->head.error = error;
hammer_done_cursor(&cursor);
hammer_unlock(&hmp->snapshot_lock);
return(0);
}
static
int
hammer_ioc_del_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_snapshot *snap)
{
hammer_mount_t hmp = ip->hmp;
struct hammer_cursor cursor;
int error;
if (snap->count > HAMMER_SNAPS_PER_IOCTL)
return (EINVAL);
if (snap->index >= snap->count)
return (EINVAL);
hammer_lock_ex(&hmp->snapshot_lock);
again:
error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
if (error) {
hammer_done_cursor(&cursor);
return(error);
}
cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
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_SNAPSHOT;
cursor.key_beg.localization = ip->obj_localization | HAMMER_LOCALIZE_INODE;
cursor.asof = HAMMER_MAX_TID;
cursor.flags |= HAMMER_CURSOR_ASOF;
while (snap->index < snap->count) {
cursor.key_beg.key = (int64_t)snap->snaps[snap->index].tid;
error = hammer_btree_lookup(&cursor);
if (error)
break;
error = hammer_btree_extract_leaf(&cursor);
if (error)
break;
error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY,
0, 0, 0, NULL);
if (error == EDEADLK) {
hammer_done_cursor(&cursor);
goto again;
}
if (error)
break;
++snap->index;
}
snap->head.error = error;
hammer_done_cursor(&cursor);
hammer_unlock(&hmp->snapshot_lock);
return(0);
}
static
int
hammer_ioc_get_snapshot(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_snapshot *snap)
{
struct hammer_cursor cursor;
int error;
if (snap->index != 0)
return (EINVAL);
if (snap->count > HAMMER_SNAPS_PER_IOCTL)
return (EINVAL);
error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
if (error) {
hammer_done_cursor(&cursor);
return(error);
}
cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
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_SNAPSHOT;
cursor.key_beg.localization = ip->obj_localization | HAMMER_LOCALIZE_INODE;
if (snap->count == 0)
cursor.key_beg.key = HAMMER_MIN_KEY;
else
cursor.key_beg.key = (int64_t)snap->snaps[snap->count - 1].tid + 1;
cursor.key_end = cursor.key_beg;
cursor.key_end.key = HAMMER_MAX_KEY;
cursor.asof = HAMMER_MAX_TID;
cursor.flags |= HAMMER_CURSOR_END_EXCLUSIVE | HAMMER_CURSOR_ASOF;
snap->count = 0;
error = hammer_btree_first(&cursor);
while (error == 0 && snap->count < HAMMER_SNAPS_PER_IOCTL) {
error = hammer_btree_extract_leaf(&cursor);
if (error)
break;
if (cursor.leaf->base.rec_type == HAMMER_RECTYPE_SNAPSHOT) {
error = hammer_btree_extract_data(&cursor);
snap->snaps[snap->count] = cursor.data->snap;
if (cursor.data->snap.tid !=
(hammer_tid_t)cursor.leaf->base.key) {
hkprintf("lo=%08x snapshot key "
"0x%016jx data mismatch 0x%016jx\n",
cursor.key_beg.localization,
(uintmax_t)cursor.data->snap.tid,
cursor.leaf->base.key);
hkprintf("Probably left over from the "
"original v3 conversion, hammer "
"cleanup should get it eventually\n");
snap->snaps[snap->count].tid =
cursor.leaf->base.key;
}
++snap->count;
}
error = hammer_btree_iterate(&cursor);
}
if (error == ENOENT) {
snap->head.flags |= HAMMER_IOC_SNAPSHOT_EOF;
error = 0;
}
snap->head.error = error;
hammer_done_cursor(&cursor);
return(0);
}
static
int
hammer_ioc_get_config(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_config *config)
{
struct hammer_cursor cursor;
int error;
error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
if (error) {
hammer_done_cursor(&cursor);
return(error);
}
cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
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_CONFIG;
cursor.key_beg.localization = ip->obj_localization | HAMMER_LOCALIZE_INODE;
cursor.key_beg.key = 0;
cursor.asof = HAMMER_MAX_TID;
cursor.flags |= HAMMER_CURSOR_ASOF;
error = hammer_btree_lookup(&cursor);
if (error == 0) {
error = hammer_btree_extract_data(&cursor);
if (error == 0)
config->config = cursor.data->config;
}
config->head.error = error;
hammer_done_cursor(&cursor);
return(0);
}
static
int
hammer_ioc_set_config(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_config *config)
{
struct hammer_btree_leaf_elm leaf;
struct hammer_cursor cursor;
hammer_mount_t hmp = ip->hmp;
int error;
again:
error = hammer_init_cursor(trans, &cursor, &ip->cache[0], NULL);
if (error) {
hammer_done_cursor(&cursor);
return(error);
}
bzero(&leaf, sizeof(leaf));
leaf.base.obj_id = HAMMER_OBJID_ROOT;
leaf.base.rec_type = HAMMER_RECTYPE_CONFIG;
leaf.base.create_tid = hammer_alloc_tid(hmp, 1);
leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
leaf.base.localization = ip->obj_localization | HAMMER_LOCALIZE_INODE;
leaf.base.key = 0;
leaf.data_len = sizeof(struct hammer_config_data);
cursor.key_beg = leaf.base;
cursor.asof = HAMMER_MAX_TID;
cursor.flags |= HAMMER_CURSOR_BACKEND | HAMMER_CURSOR_ASOF;
error = hammer_btree_lookup(&cursor);
if (error == 0) {
error = hammer_btree_extract_data(&cursor);
error = hammer_delete_at_cursor(&cursor, HAMMER_DELETE_DESTROY,
0, 0, 0, NULL);
if (error == EDEADLK) {
hammer_done_cursor(&cursor);
goto again;
}
}
if (error == ENOENT)
error = 0;
if (error == 0) {
cursor.flags &= ~HAMMER_CURSOR_ASOF;
cursor.key_beg = leaf.base;
error = hammer_create_at_cursor(&cursor, &leaf,
&config->config,
HAMMER_CREATE_MODE_SYS);
if (error == EDEADLK) {
hammer_done_cursor(&cursor);
goto again;
}
}
config->head.error = error;
hammer_done_cursor(&cursor);
return(0);
}
static
int
hammer_ioc_get_data(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_data *data)
{
struct hammer_cursor cursor;
int bytes;
int error;
error = hammer_init_cursor(trans, &cursor, NULL, NULL);
if (error)
goto failed;
cursor.key_beg = data->elm;
cursor.flags |= HAMMER_CURSOR_BACKEND;
error = hammer_btree_lookup(&cursor);
if (error == 0) {
error = hammer_btree_extract_data(&cursor);
if (error == 0) {
data->leaf = *cursor.leaf;
bytes = cursor.leaf->data_len;
if (bytes > data->size)
bytes = data->size;
error = copyout(cursor.data, data->ubuf, bytes);
}
}
failed:
hammer_done_cursor(&cursor);
return (error);
}