#include <sys/diskslice.h>
#include "hammer2.h"
#include "makefs.h"
#if 0
#include <sys/kern_syscall.h>
static int hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_debug_dump(hammer2_inode_t *ip, u_int flags);
static int hammer2_ioctl_emerg_mode(hammer2_inode_t *ip, u_int mode);
static int hammer2_ioctl_growfs(hammer2_inode_t *ip, void *data,
struct ucred *cred);
static int hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_destroy(hammer2_inode_t *ip, void *data);
static int hammer2_ioctl_volume_list(hammer2_inode_t *ip, void *data);
int
hammer2_ioctl(hammer2_inode_t *ip, u_long com, void *data, int fflag,
struct ucred *cred)
{
int error;
error = caps_priv_check(cred, SYSCAP_NOVFS_IOCTL);
switch(com) {
case HAMMER2IOC_VERSION_GET:
error = hammer2_ioctl_version_get(ip, data);
break;
case HAMMER2IOC_RECLUSTER:
if (error == 0)
error = hammer2_ioctl_recluster(ip, data);
break;
case HAMMER2IOC_REMOTE_SCAN:
if (error == 0)
error = hammer2_ioctl_remote_scan(ip, data);
break;
case HAMMER2IOC_REMOTE_ADD:
if (error == 0)
error = hammer2_ioctl_remote_add(ip, data);
break;
case HAMMER2IOC_REMOTE_DEL:
if (error == 0)
error = hammer2_ioctl_remote_del(ip, data);
break;
case HAMMER2IOC_REMOTE_REP:
if (error == 0)
error = hammer2_ioctl_remote_rep(ip, data);
break;
case HAMMER2IOC_SOCKET_GET:
if (error == 0)
error = hammer2_ioctl_socket_get(ip, data);
break;
case HAMMER2IOC_SOCKET_SET:
if (error == 0)
error = hammer2_ioctl_socket_set(ip, data);
break;
case HAMMER2IOC_PFS_GET:
if (error == 0)
error = hammer2_ioctl_pfs_get(ip, data);
break;
case HAMMER2IOC_PFS_LOOKUP:
if (error == 0)
error = hammer2_ioctl_pfs_lookup(ip, data);
break;
case HAMMER2IOC_PFS_CREATE:
if (error == 0)
error = hammer2_ioctl_pfs_create(ip, data);
break;
case HAMMER2IOC_PFS_DELETE:
if (error == 0)
error = hammer2_ioctl_pfs_delete(ip, data);
break;
case HAMMER2IOC_PFS_SNAPSHOT:
if (error == 0)
error = hammer2_ioctl_pfs_snapshot(ip, data);
break;
case HAMMER2IOC_INODE_GET:
error = hammer2_ioctl_inode_get(ip, data);
break;
case HAMMER2IOC_INODE_SET:
if (error == 0)
error = hammer2_ioctl_inode_set(ip, data);
break;
case HAMMER2IOC_BULKFREE_SCAN:
error = hammer2_ioctl_bulkfree_scan(ip, data);
break;
case HAMMER2IOC_BULKFREE_ASYNC:
error = hammer2_ioctl_bulkfree_scan(ip, NULL);
break;
case HAMMER2IOC_DESTROY:
if (error == 0)
error = hammer2_ioctl_destroy(ip, data);
break;
case HAMMER2IOC_DEBUG_DUMP:
error = hammer2_ioctl_debug_dump(ip, *(u_int *)data);
break;
case HAMMER2IOC_EMERG_MODE:
if (error == 0)
error = hammer2_ioctl_emerg_mode(ip, *(u_int *)data);
break;
case HAMMER2IOC_GROWFS:
if (error == 0)
error = hammer2_ioctl_growfs(ip, data, cred);
break;
case HAMMER2IOC_VOLUME_LIST:
if (error == 0)
error = hammer2_ioctl_volume_list(ip, data);
break;
case FIOSEEKDATA:
case FIOSEEKHOLE:
if (error == 0)
error = EOPNOTSUPP;
#if 0
error = vn_bmap_seekhole(ip->vp, com, (off_t *)data,
cred);
#endif
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
#endif
int
hammer2_ioctl_version_get(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_version_t *version = data;
hammer2_dev_t *hmp;
hmp = ip->pmp->pfs_hmps[0];
if (hmp)
version->version = hmp->voldata.version;
else
version->version = -1;
return 0;
}
#if 0
static int
hammer2_ioctl_recluster(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_recluster_t *recl = data;
struct vnode *vproot;
struct file *fp;
hammer2_cluster_t *cluster;
int error;
fp = holdfp(curthread, recl->fd, -1);
if (fp) {
error = VFS_ROOT(ip->pmp->mp, &vproot);
if (error == 0) {
cluster = &ip->pmp->iroot->cluster;
kprintf("reconnect to cluster: nc=%d focus=%p\n",
cluster->nchains, cluster->focus);
if (cluster->nchains != 1 || cluster->focus == NULL) {
kprintf("not a local device mount\n");
error = EINVAL;
} else {
hammer2_cluster_reconnect(cluster->focus->hmp,
fp);
kprintf("ok\n");
error = 0;
}
vput(vproot);
}
} else {
error = EINVAL;
}
return error;
}
static int
hammer2_ioctl_remote_scan(hammer2_inode_t *ip, void *data)
{
hammer2_dev_t *hmp;
hammer2_ioc_remote_t *remote = data;
int copyid = remote->copyid;
hmp = ip->pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
return (EINVAL);
hammer2_voldata_lock(hmp);
remote->copy1 = hmp->voldata.copyinfo[copyid];
hammer2_voldata_unlock(hmp);
while (++copyid < HAMMER2_COPYID_COUNT &&
hmp->voldata.copyinfo[copyid].copyid == 0) {
;
}
if (copyid == HAMMER2_COPYID_COUNT)
remote->nextid = -1;
else
remote->nextid = copyid;
return(0);
}
static int
hammer2_ioctl_remote_add(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_remote_t *remote = data;
hammer2_pfs_t *pmp = ip->pmp;
hammer2_dev_t *hmp;
int copyid = remote->copyid;
int error = 0;
hmp = pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
if (copyid >= HAMMER2_COPYID_COUNT)
return (EINVAL);
hammer2_voldata_lock(hmp);
if (copyid < 0) {
for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
if (hmp->voldata.copyinfo[copyid].copyid == 0)
break;
}
if (copyid == HAMMER2_COPYID_COUNT) {
error = ENOSPC;
goto failed;
}
}
hammer2_voldata_modify(hmp);
remote->copy1.copyid = copyid;
hmp->voldata.copyinfo[copyid] = remote->copy1;
hammer2_volconf_update(hmp, copyid);
failed:
hammer2_voldata_unlock(hmp);
return (error);
}
static int
hammer2_ioctl_remote_del(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_remote_t *remote = data;
hammer2_pfs_t *pmp = ip->pmp;
hammer2_dev_t *hmp;
int copyid = remote->copyid;
int error = 0;
hmp = pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
if (copyid >= HAMMER2_COPYID_COUNT)
return (EINVAL);
remote->copy1.path[sizeof(remote->copy1.path) - 1] = 0;
hammer2_voldata_lock(hmp);
if (copyid < 0) {
for (copyid = 1; copyid < HAMMER2_COPYID_COUNT; ++copyid) {
if (hmp->voldata.copyinfo[copyid].copyid == 0)
continue;
if (strcmp(remote->copy1.path,
hmp->voldata.copyinfo[copyid].path) == 0) {
break;
}
}
if (copyid == HAMMER2_COPYID_COUNT) {
error = ENOENT;
goto failed;
}
}
hammer2_voldata_modify(hmp);
hmp->voldata.copyinfo[copyid].copyid = 0;
hammer2_volconf_update(hmp, copyid);
failed:
hammer2_voldata_unlock(hmp);
return (error);
}
static int
hammer2_ioctl_remote_rep(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_remote_t *remote = data;
hammer2_dev_t *hmp;
int copyid = remote->copyid;
hmp = ip->pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
return (EINVAL);
hammer2_voldata_lock(hmp);
hammer2_voldata_modify(hmp);
hammer2_voldata_unlock(hmp);
return(0);
}
static int
hammer2_ioctl_socket_get(hammer2_inode_t *ip, void *data)
{
return (EOPNOTSUPP);
}
static int
hammer2_ioctl_socket_set(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_remote_t *remote = data;
hammer2_dev_t *hmp;
int copyid = remote->copyid;
hmp = ip->pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
if (copyid < 0 || copyid >= HAMMER2_COPYID_COUNT)
return (EINVAL);
hammer2_voldata_lock(hmp);
hammer2_voldata_unlock(hmp);
return(0);
}
#endif
int
hammer2_ioctl_pfs_get(hammer2_inode_t *ip, void *data)
{
const hammer2_inode_data_t *ripdata;
hammer2_dev_t *hmp;
hammer2_ioc_pfs_t *pfs;
hammer2_chain_t *parent;
hammer2_chain_t *chain;
hammer2_key_t key_next;
hammer2_key_t save_key;
int error;
hmp = ip->pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
pfs = data;
save_key = pfs->name_key;
error = 0;
if (save_key == (hammer2_key_t)-1) {
hammer2_inode_lock(ip->pmp->iroot, 0);
parent = NULL;
chain = hammer2_inode_chain(ip->pmp->iroot, 0,
HAMMER2_RESOLVE_ALWAYS |
HAMMER2_RESOLVE_SHARED);
} else {
hammer2_inode_lock(hmp->spmp->iroot, 0);
parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
HAMMER2_RESOLVE_ALWAYS |
HAMMER2_RESOLVE_SHARED);
chain = hammer2_chain_lookup(&parent, &key_next,
pfs->name_key, HAMMER2_KEY_MAX,
&error,
HAMMER2_LOOKUP_SHARED);
}
while (chain) {
if (chain->bref.type == HAMMER2_BREF_TYPE_INODE)
break;
if (parent == NULL) {
hammer2_chain_unlock(chain);
hammer2_chain_drop(chain);
chain = NULL;
break;
}
chain = hammer2_chain_next(&parent, chain, &key_next,
key_next, HAMMER2_KEY_MAX,
&error,
HAMMER2_LOOKUP_SHARED);
}
error = hammer2_error_to_errno(error);
if (chain && chain->error == 0) {
ripdata = &chain->data->ipdata;
pfs->name_key = ripdata->meta.name_key;
pfs->pfs_type = ripdata->meta.pfs_type;
pfs->pfs_subtype = ripdata->meta.pfs_subtype;
pfs->pfs_clid = ripdata->meta.pfs_clid;
pfs->pfs_fsid = ripdata->meta.pfs_fsid;
KKASSERT(ripdata->meta.name_len < sizeof(pfs->name));
bcopy(ripdata->filename, pfs->name, ripdata->meta.name_len);
pfs->name[ripdata->meta.name_len] = 0;
ripdata = NULL;
if (parent == NULL) {
pfs->name_next = (hammer2_key_t)-1;
} else {
chain = hammer2_chain_next(&parent, chain, &key_next,
key_next, HAMMER2_KEY_MAX,
&error,
HAMMER2_LOOKUP_SHARED);
if (chain)
pfs->name_next = chain->bref.key;
else
pfs->name_next = (hammer2_key_t)-1;
}
} else {
pfs->name_next = (hammer2_key_t)-1;
error = ENOENT;
}
if (chain) {
hammer2_chain_unlock(chain);
hammer2_chain_drop(chain);
}
if (parent) {
hammer2_chain_unlock(parent);
hammer2_chain_drop(parent);
}
if (save_key == (hammer2_key_t)-1) {
hammer2_inode_unlock(ip->pmp->iroot);
} else {
hammer2_inode_unlock(hmp->spmp->iroot);
}
return (error);
}
int
hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data)
{
const hammer2_inode_data_t *ripdata;
hammer2_dev_t *hmp;
hammer2_ioc_pfs_t *pfs;
hammer2_chain_t *parent;
hammer2_chain_t *chain;
hammer2_key_t key_next;
hammer2_key_t lhc;
int error;
size_t len;
hmp = ip->pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
pfs = data;
error = 0;
hammer2_inode_lock(hmp->spmp->iroot, HAMMER2_RESOLVE_SHARED);
parent = hammer2_inode_chain(hmp->spmp->iroot, 0,
HAMMER2_RESOLVE_ALWAYS |
HAMMER2_RESOLVE_SHARED);
pfs->name[sizeof(pfs->name) - 1] = 0;
len = strlen(pfs->name);
lhc = hammer2_dirhash(pfs->name, len);
chain = hammer2_chain_lookup(&parent, &key_next,
lhc, lhc + HAMMER2_DIRHASH_LOMASK,
&error, HAMMER2_LOOKUP_SHARED);
while (chain) {
if (hammer2_chain_dirent_test(chain, pfs->name, len))
break;
chain = hammer2_chain_next(&parent, chain, &key_next,
key_next,
lhc + HAMMER2_DIRHASH_LOMASK,
&error, HAMMER2_LOOKUP_SHARED);
}
error = hammer2_error_to_errno(error);
if (chain && chain->error == 0) {
KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
ripdata = &chain->data->ipdata;
pfs->name_key = ripdata->meta.name_key;
pfs->pfs_type = ripdata->meta.pfs_type;
pfs->pfs_subtype = ripdata->meta.pfs_subtype;
pfs->pfs_clid = ripdata->meta.pfs_clid;
pfs->pfs_fsid = ripdata->meta.pfs_fsid;
ripdata = NULL;
hammer2_chain_unlock(chain);
hammer2_chain_drop(chain);
} else if (error == 0) {
error = ENOENT;
}
if (parent) {
hammer2_chain_unlock(parent);
hammer2_chain_drop(parent);
}
hammer2_inode_unlock(hmp->spmp->iroot);
return (error);
}
int
hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data)
{
hammer2_inode_data_t *nipdata;
hammer2_chain_t *nchain;
hammer2_dev_t *hmp;
hammer2_dev_t *force_local;
hammer2_ioc_pfs_t *pfs;
hammer2_inode_t *nip;
hammer2_tid_t mtid;
int error;
hmp = ip->pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
pfs = data;
nip = NULL;
if (pfs->name[0] == 0)
return(EINVAL);
pfs->name[sizeof(pfs->name) - 1] = 0;
if (hammer2_ioctl_pfs_lookup(ip, pfs) == 0)
return(EEXIST);
hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
mtid = hammer2_trans_sub(hmp->spmp);
nip = hammer2_inode_create_pfs(hmp->spmp, pfs->name, strlen(pfs->name),
&error);
if (error == 0) {
atomic_set_int(&nip->flags, HAMMER2_INODE_NOSIDEQ);
hammer2_inode_modify(nip);
nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
error = hammer2_chain_modify(nchain, mtid, 0, 0);
KKASSERT(error == 0);
nipdata = &nchain->data->ipdata;
nip->meta.pfs_type = pfs->pfs_type;
nip->meta.pfs_subtype = pfs->pfs_subtype;
nip->meta.pfs_clid = pfs->pfs_clid;
nip->meta.pfs_fsid = pfs->pfs_fsid;
nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
nip->meta.comp_algo =
HAMMER2_ENC_ALGO(HAMMER2_COMP_DEFAULT);
nip->meta.check_algo =
HAMMER2_ENC_ALGO(HAMMER2_CHECK_DEFAULT);
if (strcasecmp(pfs->name, "boot") == 0) {
nip->meta.comp_algo =
HAMMER2_ENC_ALGO(HAMMER2_COMP_AUTOZERO);
}
hammer2_chain_unlock(nchain);
hammer2_inode_ref(nip);
hammer2_inode_unlock(nip);
hammer2_inode_chain_sync(nip);
hammer2_inode_chain_flush(nip, HAMMER2_XOP_INODE_STOP |
HAMMER2_XOP_FSSYNC);
hammer2_inode_drop(nip);
force_local = (hmp->hflags & HMNT2_LOCAL) ? hmp : NULL;
hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS);
nipdata = &nchain->data->ipdata;
kprintf("ADD LOCAL PFS (IOCTL): %s\n", nipdata->filename);
hammer2_pfsalloc(nchain, nipdata, force_local);
hammer2_chain_unlock(nchain);
hammer2_chain_drop(nchain);
}
hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
HAMMER2_TRANS_SIDEQ);
return (error);
}
int
hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_pfs_t *pfs = data;
hammer2_dev_t *hmp;
hammer2_pfs_t *spmp;
hammer2_pfs_t *pmp;
hammer2_xop_unlink_t *xop;
hammer2_inode_t *dip;
int error;
int i;
hmp = ip->pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
pfs->name[sizeof(pfs->name) - 1] = 0;
lockmgr(&hammer2_mntlk, LK_EXCLUSIVE);
TAILQ_FOREACH(pmp, &hammer2_pfslist, mntentry) {
for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
if (pmp->pfs_hmps[i] != hmp)
continue;
if (pmp->pfs_names[i] &&
strcmp(pmp->pfs_names[i], pfs->name) == 0) {
break;
}
}
if (i != HAMMER2_MAXCLUSTER)
break;
}
if (pmp == NULL) {
lockmgr(&hammer2_mntlk, LK_RELEASE);
return ENOENT;
}
if (pmp->mp) {
lockmgr(&hammer2_mntlk, LK_RELEASE);
return EBUSY;
}
kprintf("FOUND PFS %s CLINDEX %d\n", pfs->name, i);
hammer2_pfsdealloc(pmp, i, 1);
lockmgr(&hammer2_mntlk, LK_RELEASE);
spmp = hmp->spmp;
dip = spmp->iroot;
hammer2_trans_init(spmp, 0);
hammer2_inode_lock(dip, 0);
xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING);
hammer2_xop_setname(&xop->head, pfs->name, strlen(pfs->name));
xop->isdir = 2;
xop->dopermanent = H2DOPERM_PERMANENT | H2DOPERM_FORCE;
hammer2_xop_start(&xop->head, &hammer2_unlink_desc);
error = hammer2_xop_collect(&xop->head, 0);
hammer2_inode_unlock(dip);
#if 0
if (error == 0) {
ip = hammer2_inode_get(dip->pmp, &xop->head, -1, -1);
hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
if (ip) {
hammer2_inode_unlink_finisher(ip, NULL);
hammer2_inode_unlock(ip);
}
} else {
hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
}
#endif
hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
hammer2_trans_done(spmp, HAMMER2_TRANS_SIDEQ);
return (hammer2_error_to_errno(error));
}
int
hammer2_ioctl_pfs_snapshot(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_pfs_t *pfs = data;
hammer2_dev_t *hmp;
hammer2_pfs_t *pmp;
hammer2_chain_t *chain;
hammer2_inode_t *nip;
hammer2_tid_t mtid;
size_t name_len;
int error;
#if 0
uuid_t opfs_clid;
#endif
if (pfs->name[0] == 0)
return(EINVAL);
if (pfs->name[sizeof(pfs->name)-1] != 0)
return(EINVAL);
pmp = ip->pmp;
ip = pmp->iroot;
hmp = pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
lockmgr(&hmp->bulklk, LK_EXCLUSIVE);
if (pfs->pfs_flags & HAMMER2_PFSFLAGS_NOSYNC) {
hammer2_trans_init(pmp, 0);
} else {
hammer2_vfs_sync(pmp->mp, MNT_WAIT);
hammer2_trans_init(pmp, HAMMER2_TRANS_ISFLUSH);
}
mtid = hammer2_trans_sub(pmp);
hammer2_inode_lock(ip, 0);
hammer2_inode_modify(ip);
ip->meta.pfs_lsnap_tid = mtid;
chain = hammer2_inode_chain(ip, 0, HAMMER2_RESOLVE_ALWAYS);
name_len = strlen(pfs->name);
hmp = chain->hmp;
hammer2_chain_unlock(chain);
nip = hammer2_inode_create_pfs(hmp->spmp, pfs->name, name_len, &error);
hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS);
if (nip) {
hammer2_dev_t *force_local;
hammer2_chain_t *nchain;
hammer2_inode_data_t *wipdata;
hammer2_tid_t starting_inum;
atomic_set_int(&nip->flags, HAMMER2_INODE_NOSIDEQ);
hammer2_inode_modify(nip);
nchain = hammer2_inode_chain(nip, 0, HAMMER2_RESOLVE_ALWAYS);
error = hammer2_chain_modify(nchain, mtid, 0, 0);
KKASSERT(error == 0);
wipdata = &nchain->data->ipdata;
starting_inum = ip->pmp->inode_tid + 1;
nip->meta.pfs_inum = starting_inum;
nip->meta.pfs_type = HAMMER2_PFSTYPE_MASTER;
nip->meta.pfs_subtype = HAMMER2_PFSSUBTYPE_SNAPSHOT;
nip->meta.op_flags |= HAMMER2_OPFLAG_PFSROOT;
nip->meta.pfs_lsnap_tid = mtid;
nchain->bref.embed.stats = chain->bref.embed.stats;
uuid_create(&nip->meta.pfs_fsid, NULL);
#if 0
if (chain->flags & HAMMER2_CHAIN_PFSBOUNDARY)
nip->meta.pfs_clid = opfs_clid;
else
uuid_create(&nip->meta.pfs_clid, NULL);
#endif
uuid_create(&nip->meta.pfs_clid, NULL);
nchain->bref.flags |= HAMMER2_BREF_FLAG_PFSROOT;
wipdata->meta = nip->meta;
hammer2_spin_ex(&pmp->blockset_spin);
wipdata->u.blockset = pmp->pfs_iroot_blocksets[0];
hammer2_spin_unex(&pmp->blockset_spin);
KKASSERT(wipdata == &nchain->data->ipdata);
hammer2_chain_unlock(nchain);
hammer2_inode_ref(nip);
hammer2_inode_unlock(nip);
hammer2_inode_chain_sync(nip);
hammer2_inode_chain_flush(nip, HAMMER2_XOP_INODE_STOP |
HAMMER2_XOP_FSSYNC);
hammer2_inode_drop(nip);
force_local = (hmp->hflags & HMNT2_LOCAL) ? hmp : NULL;
hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS);
wipdata = &nchain->data->ipdata;
kprintf("SNAPSHOT LOCAL PFS (IOCTL): %s\n", wipdata->filename);
hammer2_pfsalloc(nchain, wipdata, force_local);
nchain->pmp->inode_tid = starting_inum;
hammer2_chain_unlock(nchain);
hammer2_chain_drop(nchain);
}
hammer2_chain_unlock(chain);
hammer2_chain_drop(chain);
hammer2_inode_unlock(ip);
if (pfs->pfs_flags & HAMMER2_PFSFLAGS_NOSYNC) {
hammer2_trans_done(pmp, 0);
} else {
hammer2_trans_done(pmp, HAMMER2_TRANS_ISFLUSH |
HAMMER2_TRANS_SIDEQ);
}
lockmgr(&hmp->bulklk, LK_RELEASE);
return (hammer2_error_to_errno(error));
}
int
hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_inode_t *ino = data;
hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED);
ino->data_count = hammer2_inode_data_count(ip);
ino->inode_count = hammer2_inode_inode_count(ip);
bzero(&ino->ip_data, sizeof(ino->ip_data));
ino->ip_data.meta = ip->meta;
hammer2_inode_unlock(ip);
return 0;
}
int
hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_inode_t *ino = data;
hammer2_trans_init(ip->pmp, 0);
hammer2_inode_lock(ip, 0);
if ((ino->flags & HAMMER2IOC_INODE_FLAG_CHECK) &&
ip->meta.check_algo != ino->ip_data.meta.check_algo) {
hammer2_inode_modify(ip);
ip->meta.check_algo = ino->ip_data.meta.check_algo;
}
if ((ino->flags & HAMMER2IOC_INODE_FLAG_COMP) &&
ip->meta.comp_algo != ino->ip_data.meta.comp_algo) {
hammer2_inode_modify(ip);
ip->meta.comp_algo = ino->ip_data.meta.comp_algo;
}
if ((ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) &&
ip->meta.inode_quota != ino->ip_data.meta.inode_quota) {
hammer2_inode_modify(ip);
ip->meta.inode_quota = ino->ip_data.meta.inode_quota;
}
if ((ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) &&
ip->meta.data_quota != ino->ip_data.meta.data_quota) {
hammer2_inode_modify(ip);
ip->meta.data_quota = ino->ip_data.meta.data_quota;
}
if ((ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) &&
ip->meta.ncopies != ino->ip_data.meta.ncopies) {
hammer2_inode_modify(ip);
ip->meta.ncopies = ino->ip_data.meta.ncopies;
}
hammer2_inode_unlock(ip);
hammer2_trans_done(ip->pmp, HAMMER2_TRANS_SIDEQ);
return (0);
}
#if 0
static
int
hammer2_ioctl_debug_dump(hammer2_inode_t *ip, u_int flags)
{
hammer2_chain_t *chain;
int count = 100000;
int i;
for (i = 0; i < ip->cluster.nchains; ++i) {
chain = ip->cluster.array[i].chain;
if (chain == NULL)
continue;
kprintf("cluster #%d\n", i);
hammer2_dump_chain(chain, 0, 0, &count, 'i', flags);
}
return 0;
}
#endif
int
hammer2_ioctl_emerg_mode(hammer2_inode_t *ip, u_int mode)
{
hammer2_pfs_t *pmp;
hammer2_dev_t *hmp;
int i;
pmp = ip->pmp;
if (mode) {
kprintf("hammer2: WARNING: Emergency mode enabled\n");
atomic_set_int(&pmp->flags, HAMMER2_PMPF_EMERG);
} else {
kprintf("hammer2: WARNING: Emergency mode disabled\n");
atomic_clear_int(&pmp->flags, HAMMER2_PMPF_EMERG);
}
for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
hmp = pmp->pfs_hmps[i];
if (hmp == NULL)
continue;
if (mode)
atomic_set_int(&hmp->hflags, HMNT2_EMERG);
else
atomic_clear_int(&hmp->hflags, HMNT2_EMERG);
}
return 0;
}
int
hammer2_ioctl_bulkfree_scan(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_bulkfree_t *bfi = data;
hammer2_dev_t *hmp;
hammer2_pfs_t *pmp;
hammer2_chain_t *vchain;
int error;
int didsnap;
pmp = ip->pmp;
ip = pmp->iroot;
hmp = pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
if (bfi == NULL)
return (EINVAL);
error = lockmgr(&hmp->bflock, LK_EXCLUSIVE | LK_PCATCH);
if (error)
return error;
lockmgr(&hammer2_mntlk, LK_EXCLUSIVE);
TAILQ_FOREACH(pmp, &hammer2_pfslist, mntentry) {
int etmp;
int i;
for (i = 0; i < HAMMER2_MAXCLUSTER; ++i) {
if (pmp->pfs_hmps[i] != hmp)
continue;
etmp = hammer2_vfs_sync_pmp(pmp, MNT_WAIT);
if (etmp && (error == 0 || error == ENOSPC))
error = etmp;
break;
}
}
lockmgr(&hammer2_mntlk, LK_RELEASE);
if (error && error != ENOSPC)
goto failed;
if (error) {
kprintf("hammer2: WARNING! Bulkfree forced to use live "
"topology due to ENOSPC\n");
vchain = &hmp->vchain;
hammer2_chain_ref(vchain);
didsnap = 0;
} else {
vchain = hammer2_chain_bulksnap(hmp);
didsnap = 1;
}
if (didsnap == 0)
hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
if (bfi) {
hammer2_thr_freeze(&hmp->bfthr);
error = hammer2_bulkfree_pass(hmp, vchain, bfi);
hammer2_thr_unfreeze(&hmp->bfthr);
}
if (didsnap) {
hammer2_chain_bulkdrop(vchain);
} else {
hammer2_chain_drop(vchain);
hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
HAMMER2_TRANS_SIDEQ);
}
error = hammer2_error_to_errno(error);
failed:
lockmgr(&hmp->bflock, LK_RELEASE);
return error;
}
int
hammer2_ioctl_destroy(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_destroy_t *iocd = data;
hammer2_pfs_t *pmp = ip->pmp;
int error;
if (pmp->ronly) {
error = EROFS;
return error;
}
switch(iocd->cmd) {
case HAMMER2_DELETE_FILE:
{
hammer2_xop_unlink_t *xop;
if (iocd->path[sizeof(iocd->path)-1]) {
error = EINVAL;
break;
}
if (ip->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
error = EINVAL;
break;
}
hammer2_pfs_memory_wait(pmp);
hammer2_trans_init(pmp, 0);
hammer2_inode_lock(ip, 0);
xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING);
hammer2_xop_setname(&xop->head, iocd->path, strlen(iocd->path));
xop->isdir = -1;
xop->dopermanent = H2DOPERM_PERMANENT |
H2DOPERM_FORCE |
H2DOPERM_IGNINO;
hammer2_xop_start(&xop->head, &hammer2_unlink_desc);
error = hammer2_xop_collect(&xop->head, 0);
error = hammer2_error_to_errno(error);
hammer2_inode_unlock(ip);
hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
hammer2_trans_done(pmp, HAMMER2_TRANS_SIDEQ);
}
break;
case HAMMER2_DELETE_INUM:
{
hammer2_xop_lookup_t *xop;
if (iocd->inum < 1) {
error = EINVAL;
break;
}
hammer2_pfs_memory_wait(pmp);
hammer2_trans_init(pmp, 0);
xop = hammer2_xop_alloc(pmp->iroot, HAMMER2_XOP_MODIFYING);
xop->lhc = iocd->inum;
hammer2_xop_start(&xop->head, &hammer2_delete_desc);
error = hammer2_xop_collect(&xop->head, 0);
error = hammer2_error_to_errno(error);
hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP);
hammer2_trans_done(pmp, HAMMER2_TRANS_SIDEQ);
}
break;
default:
error = EINVAL;
break;
}
return error;
}
int
hammer2_ioctl_growfs(hammer2_inode_t *ip, void *data, struct ucred *cred)
{
hammer2_ioc_growfs_t *grow = data;
hammer2_dev_t *hmp;
hammer2_off_t size, delta;
hammer2_tid_t mtid;
struct partinfo part;
struct stat st;
struct m_buf *bp;
int error;
int i;
hmp = ip->pmp->pfs_hmps[0];
if (hmp->nvolumes > 1) {
kprintf("hammer2: growfs currently unsupported "
"with multiple volumes\n");
return EOPNOTSUPP;
}
KKASSERT(hmp->total_size == hmp->voldata.volu_size);
if (ioctl(hmp->devvp->fs->fd, DIOCGPART, &part) == 0) {
size = part.media_size;
kprintf("hammer2: growfs partition-auto to %016jx\n",
(intmax_t)size);
} else if (fstat(hmp->devvp->fs->fd, &st) == 0) {
size = st.st_size;
kprintf("hammer2: growfs fstat-auto to %016jx\n",
(intmax_t)size);
} else {
return EINVAL;
}
grow->modified = 0;
if (grow->size == 0) {
grow->size = size;
} else if (grow->size > size) {
kprintf("hammer2: growfs size %016jx exceeds device size "
"%016jx\n",
(intmax_t)grow->size, (intmax_t)size);
return EINVAL;
}
grow->size &= ~HAMMER2_VOLUME_ALIGNMASK64;
delta = grow->size - hmp->voldata.volu_size;
if (grow->size > 0x7FFFFFFFFFFFFFFFLU) {
kprintf("hammer2: growfs failure, limit is 2^63 - 1 bytes\n");
return EINVAL;
}
if (grow->size < hmp->voldata.volu_size) {
kprintf("hammer2: growfs failure, "
"would shrink from %016jx to %016jx\n",
(intmax_t)hmp->voldata.volu_size,
(intmax_t)grow->size);
return EINVAL;
}
if (delta == 0) {
kprintf("hammer2: growfs - size did not change\n");
return 0;
}
for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
if (i * HAMMER2_ZONE_BYTES64 < hmp->voldata.volu_size)
continue;
if (i * HAMMER2_ZONE_BYTES64 >= grow->size)
break;
kprintf("hammer2: growfs - clear volhdr %d ", i);
error = breadx(hmp->devvp, i * HAMMER2_ZONE_BYTES64,
HAMMER2_VOLUME_BYTES, &bp);
if (error) {
brelse(bp);
kprintf("I/O error %d\n", error);
return EINVAL;
}
bzero(bp->b_data, HAMMER2_VOLUME_BYTES);
error = bwrite(bp);
if (error) {
kprintf("I/O error %d\n", error);
return EINVAL;
}
kprintf("\n");
}
hammer2_trans_init(hmp->spmp, HAMMER2_TRANS_ISFLUSH);
mtid = hammer2_trans_sub(hmp->spmp);
kprintf("hammer2: growfs - expand by %016jx to %016jx mtid %016jx\n",
(intmax_t)delta, (intmax_t)grow->size, (intmax_t)mtid);
hammer2_voldata_lock(hmp);
hammer2_voldata_modify(hmp);
hmp->voldata.volu_size = grow->size;
hmp->voldata.total_size += delta;
hmp->voldata.allocator_size += delta;
hmp->voldata.allocator_free += delta;
hmp->total_size += delta;
hmp->volumes[0].size += delta;
hammer2_voldata_unlock(hmp);
hammer2_trans_done(hmp->spmp, HAMMER2_TRANS_ISFLUSH |
HAMMER2_TRANS_SIDEQ);
grow->modified = 1;
hammer2_vfs_sync(ip->pmp->mp, MNT_WAIT);
return 0;
}
#if 0
static int
hammer2_ioctl_volume_list(hammer2_inode_t *ip, void *data)
{
hammer2_ioc_volume_list_t *vollist = data;
hammer2_ioc_volume_t entry;
hammer2_volume_t *vol;
hammer2_dev_t *hmp;
hammer2_pfs_t *pmp;
int i, error = 0, cnt = 0;
pmp = ip->pmp;
hmp = pmp->pfs_hmps[0];
if (hmp == NULL)
return (EINVAL);
hammer2_voldata_lock(hmp);
for (i = 0; i < hmp->nvolumes; ++i) {
if (cnt >= vollist->nvolumes)
break;
vol = &hmp->volumes[i];
bzero(&entry, sizeof(entry));
entry.id = vol->id;
bcopy(vol->dev->path, entry.path, sizeof(entry.path));
entry.offset = vol->offset;
entry.size = vol->size;
error = copyout(&entry, &vollist->volumes[cnt], sizeof(entry));
if (error)
goto failed;
cnt++;
}
vollist->nvolumes = cnt;
vollist->version = hmp->voldata.version;
bcopy(pmp->pfs_names[0], vollist->pfs_name, sizeof(vollist->pfs_name));
failed:
hammer2_voldata_unlock(hmp);
return error;
}
#endif