#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/user.h>
#include <sys/fstyp.h>
#include <sys/kmem.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/vfs.h>
#include <sys/vfs_opreg.h>
#include <sys/fem.h>
#include <sys/mntent.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/statfs.h>
#include <sys/cred.h>
#include <sys/vnode.h>
#include <sys/rwstlock.h>
#include <sys/dnlc.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/atomic.h>
#include <sys/cmn_err.h>
#include <sys/buf.h>
#include <sys/debug.h>
#include <sys/vnode.h>
#include <sys/ddi.h>
#include <sys/pathname.h>
#include <sys/poll.h>
#include <sys/sunddi.h>
#include <sys/sysmacros.h>
#include <sys/zone.h>
#include <sys/policy.h>
#include <sys/attr.h>
#include <fs/fs_subr.h>
#include <libfksmbfs.h>
static void vfs_clearmntopt_nolock(mntopts_t *, const char *, int);
static void vfs_setmntopt_nolock(mntopts_t *, const char *,
const char *, int, int);
static int vfs_optionisset_nolock(const mntopts_t *, const char *, char **);
static void vfs_freeopt(mntopt_t *);
static void vfs_swapopttbl_nolock(mntopts_t *, mntopts_t *);
static void vfs_swapopttbl(mntopts_t *, mntopts_t *);
static void vfs_copyopttbl_extend(const mntopts_t *, mntopts_t *, int);
static void vfs_freecancelopt(char **);
vnode_t *rootdir;
struct vfs *rootvfs = NULL;
static krwlock_t vfslist;
struct vfs *zone_vfslist;
const int nfstype = 5;
struct vfssw vfssw[10] = {
{ "BADVFS" },
{ "" },
{ "" },
{ "" },
{ "" },
};
static char *ro_cancel[] = { MNTOPT_RW, NULL };
static char *rw_cancel[] = { MNTOPT_RO, NULL };
static char *suid_cancel[] = { MNTOPT_NOSUID, NULL };
static char *nosuid_cancel[] = { MNTOPT_SUID, MNTOPT_DEVICES, MNTOPT_NODEVICES,
MNTOPT_NOSETUID, MNTOPT_SETUID, NULL };
static char *devices_cancel[] = { MNTOPT_NODEVICES, NULL };
static char *nodevices_cancel[] = { MNTOPT_DEVICES, NULL };
static char *setuid_cancel[] = { MNTOPT_NOSETUID, NULL };
static char *nosetuid_cancel[] = { MNTOPT_SETUID, NULL };
static char *nbmand_cancel[] = { MNTOPT_NONBMAND, NULL };
static char *nonbmand_cancel[] = { MNTOPT_NBMAND, NULL };
static char *exec_cancel[] = { MNTOPT_NOEXEC, NULL };
static char *noexec_cancel[] = { MNTOPT_EXEC, NULL };
static const mntopt_t mntopts[] = {
{ MNTOPT_REMOUNT, NULL, NULL,
MO_NODISPLAY, (void *)0 },
{ MNTOPT_RO, ro_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_RW, rw_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_SUID, suid_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_NOSUID, nosuid_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_DEVICES, devices_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_NODEVICES, nodevices_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_SETUID, setuid_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_NOSETUID, nosetuid_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_NBMAND, nbmand_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_NONBMAND, nonbmand_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_EXEC, exec_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_NOEXEC, noexec_cancel, NULL, 0,
(void *)0 },
};
const mntopts_t vfs_mntopts = {
sizeof (mntopts) / sizeof (mntopt_t),
(mntopt_t *)&mntopts[0]
};
int
fsop_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
{
return (*(vfsp)->vfs_op->vfs_mount)(vfsp, mvp, uap, cr);
}
int
fsop_unmount(vfs_t *vfsp, int flag, cred_t *cr)
{
return (*(vfsp)->vfs_op->vfs_unmount)(vfsp, flag, cr);
}
int
fsop_root(vfs_t *vfsp, vnode_t **vpp)
{
return ((*(vfsp)->vfs_op->vfs_root)(vfsp, vpp));
}
int
fsop_statfs(vfs_t *vfsp, statvfs64_t *sp)
{
return (*(vfsp)->vfs_op->vfs_statvfs)(vfsp, sp);
}
int
fsop_sync(vfs_t *vfsp, short flag, cred_t *cr)
{
return (*(vfsp)->vfs_op->vfs_sync)(vfsp, flag, cr);
}
int
fsop_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
{
return (*(vfsp)->vfs_op->vfs_vget)(vfsp, vpp, fidp);
}
int
fsop_mountroot(vfs_t *vfsp, enum whymountroot reason)
{
return (*(vfsp)->vfs_op->vfs_mountroot)(vfsp, reason);
}
void
fsop_freefs(vfs_t *vfsp)
{
(*(vfsp)->vfs_op->vfs_freevfs)(vfsp);
}
int
fsop_vnstate(vfs_t *vfsp, vnode_t *vp, vntrans_t nstate)
{
return ((*(vfsp)->vfs_op->vfs_vnstate)(vfsp, vp, nstate));
}
int
fsop_sync_by_kind(int fstype, short flag, cred_t *cr)
{
ASSERT((fstype >= 0) && (fstype < nfstype));
if (ALLOCATED_VFSSW(&vfssw[fstype]) && VFS_INSTALLED(&vfssw[fstype]))
return (*vfssw[fstype].vsw_vfsops.vfs_sync) (NULL, flag, cr);
else
return (ENOTSUP);
}
static int
fs_copyfsops(const fs_operation_def_t *template, vfsops_t *actual,
int *unused_ops)
{
static const fs_operation_trans_def_t vfs_ops_table[] = {
VFSNAME_MOUNT, offsetof(vfsops_t, vfs_mount),
fs_nosys, fs_nosys,
VFSNAME_UNMOUNT, offsetof(vfsops_t, vfs_unmount),
fs_nosys, fs_nosys,
VFSNAME_ROOT, offsetof(vfsops_t, vfs_root),
fs_nosys, fs_nosys,
VFSNAME_STATVFS, offsetof(vfsops_t, vfs_statvfs),
fs_nosys, fs_nosys,
VFSNAME_SYNC, offsetof(vfsops_t, vfs_sync),
(fs_generic_func_p) fs_sync,
(fs_generic_func_p) fs_sync,
VFSNAME_VGET, offsetof(vfsops_t, vfs_vget),
fs_nosys, fs_nosys,
VFSNAME_MOUNTROOT, offsetof(vfsops_t, vfs_mountroot),
fs_nosys, fs_nosys,
VFSNAME_FREEVFS, offsetof(vfsops_t, vfs_freevfs),
(fs_generic_func_p)(uintptr_t)fs_freevfs,
(fs_generic_func_p)(uintptr_t)
fs_freevfs,
VFSNAME_VNSTATE, offsetof(vfsops_t, vfs_vnstate),
(fs_generic_func_p)fs_nosys,
(fs_generic_func_p)fs_nosys,
NULL, 0, NULL, NULL
};
return (fs_build_vector(actual, unused_ops, vfs_ops_table, template));
}
int
vfs_setfsops(int fstype, const fs_operation_def_t *template, vfsops_t **actual)
{
int error;
int unused_ops;
if ((fstype < 0) || (fstype >= nfstype))
return (EINVAL);
if (!ALLOCATED_VFSSW(&vfssw[fstype]))
return (EINVAL);
error = fs_copyfsops(template, &vfssw[fstype].vsw_vfsops, &unused_ops);
if (error != 0)
return (error);
vfssw[fstype].vsw_flag |= VSW_INSTALLED;
if (actual != NULL)
*actual = &vfssw[fstype].vsw_vfsops;
#if DEBUG
if (unused_ops != 0)
cmn_err(CE_WARN, "vfs_setfsops: %s: %d operations supplied "
"but not used", vfssw[fstype].vsw_name, unused_ops);
#endif
return (0);
}
int
vfs_makefsops(const fs_operation_def_t *template, vfsops_t **actual)
{
int error;
int unused_ops;
*actual = (vfsops_t *)kmem_alloc(sizeof (vfsops_t), KM_SLEEP);
error = fs_copyfsops(template, *actual, &unused_ops);
if (error != 0) {
kmem_free(*actual, sizeof (vfsops_t));
*actual = NULL;
return (error);
}
return (0);
}
void
vfs_freevfsops(vfsops_t *vfsops)
{
kmem_free(vfsops, sizeof (vfsops_t));
}
int
vfs_freevfsops_by_type(int fstype)
{
if ((fstype <= 0) || (fstype >= nfstype))
return (EINVAL);
WLOCK_VFSSW();
if ((vfssw[fstype].vsw_flag & VSW_INSTALLED) == 0) {
WUNLOCK_VFSSW();
return (EINVAL);
}
vfssw[fstype].vsw_flag &= ~VSW_INSTALLED;
WUNLOCK_VFSSW();
return (0);
}
void
vfs_setops(vfs_t *vfsp, vfsops_t *vfsops)
{
ASSERT(vfsp != NULL);
ASSERT(vfsops != NULL);
vfsp->vfs_op = vfsops;
}
vfsops_t *
vfs_getops(vfs_t *vfsp)
{
ASSERT(vfsp != NULL);
return (vfsp->vfs_op);
}
int
vfs_matchops(vfs_t *vfsp, vfsops_t *vfsops)
{
return (vfs_getops(vfsp) == vfsops);
}
int
vfs_can_sync(vfs_t *vfsp)
{
return (vfs_getops(vfsp)->vfs_sync != fs_sync);
}
void
vfs_init(vfs_t *vfsp, vfsops_t *op, void *data)
{
bzero(vfsp, sizeof (vfs_t));
vfsp->vfs_count = 0;
vfsp->vfs_next = vfsp;
vfsp->vfs_prev = vfsp;
vfsp->vfs_zone_next = vfsp;
vfsp->vfs_zone_prev = vfsp;
vfsp->vfs_lofi_id = 0;
sema_init(&vfsp->vfs_reflock, 1, NULL, SEMA_DEFAULT, NULL);
vfsimpl_setup(vfsp);
vfsp->vfs_data = (data);
vfs_setops((vfsp), (op));
}
void
vfsimpl_setup(vfs_t *vfsp)
{
int i;
if (vfsp->vfs_implp != NULL) {
return;
}
vfsp->vfs_implp = kmem_alloc(sizeof (vfs_impl_t), KM_SLEEP);
vfsp->vfs_vskap = NULL;
vfsp->vfs_fstypevsp = NULL;
vfsp->vfs_featureset[0] = VFS_FEATURE_MAXSZ - 1;
for (i = 1; i < VFS_FEATURE_MAXSZ; i++) {
vfsp->vfs_featureset[i] = 0;
}
}
void
vfsimpl_teardown(vfs_t *vfsp)
{
vfs_impl_t *vip = vfsp->vfs_implp;
if (vip == NULL)
return;
kmem_free(vfsp->vfs_implp, sizeof (vfs_impl_t));
vfsp->vfs_implp = NULL;
}
krwlock_t vfssw_lock;
static krwlock_t vfslist;
int
fake_domount(char *fsname, struct mounta *uap, struct vfs **vfspp)
{
vnode_t *vp;
struct cred *credp;
struct vfssw *vswp;
vfsops_t *vfsops;
struct vfs *vfsp = NULL;
mntopts_t mnt_mntopts;
int error = 0;
int copyout_error = 0;
char *opts = uap->optptr;
char *inargs = opts;
int optlen = uap->optlen;
credp = CRED();
if (rootvfs != NULL)
return (EBUSY);
vp = rootdir;
mutex_enter(&vp->v_lock);
vp->v_flag |= VVFSLOCK;
mutex_exit(&vp->v_lock);
mnt_mntopts.mo_count = 0;
if ((vswp = vfs_getvfssw(fsname)) == NULL) {
return (EINVAL);
}
if (!VFS_INSTALLED(vswp))
return (EINVAL);
vfsops = &vswp->vsw_vfsops;
vfs_copyopttbl(&vswp->vsw_optproto, &mnt_mntopts);
if (uap->flags & MS_OPTIONSTR) {
if (optlen < 0 || optlen > MAX_MNTOPT_STR) {
error = EINVAL;
goto errout;
}
if ((uap->flags & MS_SYSSPACE) == 0) {
inargs = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP);
inargs[0] = '\0';
if (optlen) {
error = copyinstr(opts, inargs, (size_t)optlen,
NULL);
if (error) {
goto errout;
}
}
}
vfs_parsemntopts(&mnt_mntopts, inargs, 0);
}
if (uap->flags & MS_REMOUNT)
vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_REMOUNT, NULL, 0, 0);
if (uap->flags & MS_RDONLY)
vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_RO, NULL, 0, 0);
if (uap->flags & MS_NOSUID)
vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL, 0, 0);
if (vfs_optionisset_nolock(&mnt_mntopts,
MNTOPT_REMOUNT, NULL)) {
error = ENOTSUP;
goto errout;
}
if (vfs_optionisset_nolock(&mnt_mntopts, MNTOPT_RO, NULL)) {
uap->flags |= MS_RDONLY;
}
if (vfs_optionisset_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL)) {
uap->flags |= MS_NOSUID;
}
if ((uap->flags & (MS_DATA | MS_OPTIONSTR)) == 0) {
uap->dataptr = NULL;
uap->datalen = 0;
}
vfsp = vfs_alloc(KM_SLEEP);
VFS_INIT(vfsp, vfsops, NULL);
VFS_HOLD(vfsp);
uap->flags |= MS_NOSUID;
vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL, 0, 0);
if ((error = vfs_lock(vfsp)) != 0) {
vfs_free(vfsp);
vfsp = NULL;
goto errout;
}
vfs_swapopttbl(&mnt_mntopts, &vfsp->vfs_mntopts);
vfs_setresource(vfsp, uap->spec, 0);
vfs_setmntpoint(vfsp, uap->dir, 0);
error = VFS_MOUNT(vfsp, vp, uap, credp);
if (uap->flags & MS_RDONLY)
vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
if (uap->flags & MS_NOSUID)
vfs_setmntopt(vfsp, MNTOPT_NOSUID, NULL, 0);
if (uap->flags & MS_GLOBAL)
vfs_setmntopt(vfsp, MNTOPT_GLOBAL, NULL, 0);
if (error) {
vfs_unlock(vfsp);
vfs_free(vfsp);
vfsp = NULL;
} else {
vfsp->vfs_vnodecovered = vp;
vfsp->vfs_flag |= (VFS_NOSETUID|VFS_NODEVICES);
VFS_HOLD(vfsp);
rootvfs = vfsp;
if (vfs_optionisset(vfsp, MNTOPT_RO, NULL))
vfsp->vfs_flag |= VFS_RDONLY;
else
vfsp->vfs_flag &= ~VFS_RDONLY;
if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) {
vfsp->vfs_flag |= (VFS_NOSETUID|VFS_NODEVICES);
} else {
if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
vfsp->vfs_flag |= VFS_NODEVICES;
else
vfsp->vfs_flag &= ~VFS_NODEVICES;
if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL))
vfsp->vfs_flag |= VFS_NOSETUID;
else
vfsp->vfs_flag &= ~VFS_NOSETUID;
}
if (vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL))
vfsp->vfs_flag |= VFS_NBMAND;
else
vfsp->vfs_flag &= ~VFS_NBMAND;
if (vfs_optionisset(vfsp, MNTOPT_XATTR, NULL))
vfsp->vfs_flag |= VFS_XATTR;
else
vfsp->vfs_flag &= ~VFS_XATTR;
if (vfs_optionisset(vfsp, MNTOPT_NOEXEC, NULL))
vfsp->vfs_flag |= VFS_NOEXEC;
else
vfsp->vfs_flag &= ~VFS_NOEXEC;
if (uap->flags & MS_OPTIONSTR) {
vfs_list_read_lock();
copyout_error = vfs_buildoptionstr(
&vfsp->vfs_mntopts, inargs, optlen);
vfs_list_unlock();
if (copyout_error == 0 &&
(uap->flags & MS_SYSSPACE) == 0) {
copyout_error = copyout(inargs, opts, optlen);
}
}
if (vswp->vsw_flag & VSW_XID)
vfsp->vfs_flag |= VFS_XID;
vfs_unlock(vfsp);
error = VFS_ROOT(vfsp, &rootdir);
if (error != 0) {
panic("fake_domount, get root %d\n", error);
}
}
if ((error == 0) && (copyout_error == 0)) {
*vfspp = vfsp;
}
errout:
vfs_freeopttbl(&mnt_mntopts);
ASSERT(vswp != NULL);
vfs_unrefvfssw(vswp);
if (inargs != opts)
kmem_free(inargs, MAX_MNTOPT_STR);
if (copyout_error) {
if (vfsp != NULL) {
VFS_RELE(vfsp);
}
error = copyout_error;
}
return (error);
}
static void
vfs_setpath(
struct vfs *vfsp,
refstr_t **refp,
const char *newpath,
uint32_t flag)
{
refstr_t *ref;
int have_list_lock = 0;
ASSERT(!VFS_ON_LIST(vfsp) || vfs_lock_held(vfsp));
ASSERT(strlen(newpath) < MAXPATHLEN);
if (VFS_ON_LIST(vfsp)) {
have_list_lock = 1;
vfs_list_lock();
}
if (*refp != NULL)
refstr_rele(*refp);
ref = refstr_alloc(newpath);
*refp = ref;
if (have_list_lock) {
vfs_mnttab_modtimeupd();
vfs_list_unlock();
}
}
void
vfs_setresource(struct vfs *vfsp, const char *resource, uint32_t flag)
{
if (resource == NULL || resource[0] == '\0')
resource = VFS_NORESOURCE;
vfs_setpath(vfsp, &vfsp->vfs_resource, resource, flag);
}
void
vfs_setmntpoint(struct vfs *vfsp, const char *mntpt, uint32_t flag)
{
if (mntpt == NULL || mntpt[0] == '\0')
mntpt = VFS_NOMNTPT;
vfs_setpath(vfsp, &vfsp->vfs_mntpt, mntpt, flag);
}
refstr_t *
vfs_getresource(const struct vfs *vfsp)
{
refstr_t *resource;
vfs_list_read_lock();
resource = vfsp->vfs_resource;
refstr_hold(resource);
vfs_list_unlock();
return (resource);
}
refstr_t *
vfs_getmntpoint(const struct vfs *vfsp)
{
refstr_t *mntpt;
vfs_list_read_lock();
mntpt = vfsp->vfs_mntpt;
refstr_hold(mntpt);
vfs_list_unlock();
return (mntpt);
}
static void
vfs_swapopttbl_nolock(mntopts_t *optbl1, mntopts_t *optbl2)
{
uint_t tmpcnt;
mntopt_t *tmplist;
tmpcnt = optbl2->mo_count;
tmplist = optbl2->mo_list;
optbl2->mo_count = optbl1->mo_count;
optbl2->mo_list = optbl1->mo_list;
optbl1->mo_count = tmpcnt;
optbl1->mo_list = tmplist;
}
static void
vfs_swapopttbl(mntopts_t *optbl1, mntopts_t *optbl2)
{
vfs_list_lock();
vfs_swapopttbl_nolock(optbl1, optbl2);
vfs_mnttab_modtimeupd();
vfs_list_unlock();
}
static char **
vfs_copycancelopt_extend(char **const moc, int extend)
{
int i = 0;
int j;
char **result;
if (moc != NULL) {
for (; moc[i] != NULL; i++)
;
}
if (i + extend == 0)
return (NULL);
result = kmem_alloc((i + extend + 1) * sizeof (char *), KM_SLEEP);
for (j = 0; j < i; j++) {
result[j] = kmem_alloc(strlen(moc[j]) + 1, KM_SLEEP);
(void) strcpy(result[j], moc[j]);
}
for (; j <= i + extend; j++)
result[j] = NULL;
return (result);
}
static void
vfs_copyopt(const mntopt_t *s, mntopt_t *d)
{
char *sp, *dp;
d->mo_flags = s->mo_flags;
d->mo_data = s->mo_data;
sp = s->mo_name;
if (sp != NULL) {
dp = kmem_alloc(strlen(sp) + 1, KM_SLEEP);
(void) strcpy(dp, sp);
d->mo_name = dp;
} else {
d->mo_name = NULL;
}
d->mo_cancel = vfs_copycancelopt_extend(s->mo_cancel, 0);
sp = s->mo_arg;
if (sp != NULL) {
dp = kmem_alloc(strlen(sp) + 1, KM_SLEEP);
(void) strcpy(dp, sp);
d->mo_arg = dp;
} else {
d->mo_arg = NULL;
}
}
static void
vfs_copyopttbl_extend(const mntopts_t *smo, mntopts_t *dmo, int extra)
{
uint_t i, count;
mntopt_t *motbl;
vfs_freeopttbl(dmo);
count = (smo == NULL) ? 0 : smo->mo_count;
if ((count + extra) == 0)
return;
dmo->mo_count = count + extra;
motbl = kmem_zalloc((count + extra) * sizeof (mntopt_t), KM_SLEEP);
dmo->mo_list = motbl;
for (i = 0; i < count; i++) {
vfs_copyopt(&smo->mo_list[i], &motbl[i]);
}
for (i = count; i < count + extra; i++) {
motbl[i].mo_flags = MO_EMPTY;
}
}
void
vfs_copyopttbl(const mntopts_t *smo, mntopts_t *dmo)
{
vfs_copyopttbl_extend(smo, dmo, 0);
}
static char **
vfs_mergecancelopts(const mntopt_t *mop1, const mntopt_t *mop2)
{
int c1 = 0;
int c2 = 0;
char **result;
char **sp1, **sp2, **dp;
if (mop1->mo_cancel != NULL) {
for (; mop1->mo_cancel[c1] != NULL; c1++)
;
}
if (c1 == 0)
return (vfs_copycancelopt_extend(mop2->mo_cancel, 0));
if (mop2->mo_cancel != NULL) {
for (; mop2->mo_cancel[c2] != NULL; c2++)
;
}
result = vfs_copycancelopt_extend(mop1->mo_cancel, c2);
if (c2 == 0)
return (result);
dp = &result[c1];
for (sp2 = mop2->mo_cancel; *sp2 != NULL; sp2++) {
for (sp1 = mop1->mo_cancel; *sp1 != NULL; sp1++) {
if (strcmp(*sp1, *sp2) == 0)
break;
}
if (*sp1 == NULL) {
*dp = kmem_alloc(strlen(*sp2) + 1, KM_SLEEP);
(void) strcpy(*dp++, *sp2);
}
}
if (dp != &result[c1+c2]) {
size_t bytes = (dp - result + 1) * sizeof (char *);
char **nres = kmem_alloc(bytes, KM_SLEEP);
bcopy(result, nres, bytes);
kmem_free(result, (c1 + c2 + 1) * sizeof (char *));
result = nres;
}
return (result);
}
void
vfs_mergeopttbl(const mntopts_t *omo, const mntopts_t *imo, mntopts_t *dmo)
{
uint_t i, count;
mntopt_t *mop, *motbl;
uint_t freeidx;
count = omo->mo_count;
for (i = 0; i < imo->mo_count; i++) {
if (imo->mo_list[i].mo_flags & MO_EMPTY)
continue;
if (vfs_hasopt(omo, imo->mo_list[i].mo_name) == NULL)
count++;
}
ASSERT(count >= omo->mo_count &&
count <= omo->mo_count + imo->mo_count);
motbl = kmem_alloc(count * sizeof (mntopt_t), KM_SLEEP);
for (i = 0; i < omo->mo_count; i++)
vfs_copyopt(&omo->mo_list[i], &motbl[i]);
freeidx = omo->mo_count;
for (i = 0; i < imo->mo_count; i++) {
if (imo->mo_list[i].mo_flags & MO_EMPTY)
continue;
if ((mop = vfs_hasopt(omo, imo->mo_list[i].mo_name)) != NULL) {
char **newcanp;
uint_t index = mop - omo->mo_list;
newcanp = vfs_mergecancelopts(mop, &motbl[index]);
vfs_freeopt(&motbl[index]);
vfs_copyopt(&imo->mo_list[i], &motbl[index]);
vfs_freecancelopt(motbl[index].mo_cancel);
motbl[index].mo_cancel = newcanp;
} else {
vfs_copyopt(&imo->mo_list[i], &motbl[freeidx++]);
}
}
dmo->mo_count = count;
dmo->mo_list = motbl;
}
static void
vfs_clearmntopt_nolock(mntopts_t *mops, const char *opt, int update_mnttab)
{
struct mntopt *mop;
uint_t i, count;
ASSERT(!update_mnttab || RW_WRITE_HELD(&vfslist));
count = mops->mo_count;
for (i = 0; i < count; i++) {
mop = &mops->mo_list[i];
if (mop->mo_flags & MO_EMPTY)
continue;
if (strcmp(opt, mop->mo_name))
continue;
mop->mo_flags &= ~MO_SET;
if (mop->mo_arg != NULL) {
kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
}
mop->mo_arg = NULL;
if (update_mnttab)
vfs_mnttab_modtimeupd();
break;
}
}
void
vfs_clearmntopt(struct vfs *vfsp, const char *opt)
{
int gotlock = 0;
if (VFS_ON_LIST(vfsp)) {
gotlock = 1;
vfs_list_lock();
}
vfs_clearmntopt_nolock(&vfsp->vfs_mntopts, opt, gotlock);
if (gotlock)
vfs_list_unlock();
}
static void
vfs_setmntopt_nolock(mntopts_t *mops, const char *opt,
const char *arg, int flags, int update_mnttab)
{
mntopt_t *mop;
uint_t i, count;
char *sp;
ASSERT(!update_mnttab || RW_WRITE_HELD(&vfslist));
if (flags & VFS_CREATEOPT) {
if (vfs_hasopt(mops, opt) != NULL) {
flags &= ~VFS_CREATEOPT;
}
}
count = mops->mo_count;
for (i = 0; i < count; i++) {
mop = &mops->mo_list[i];
if (mop->mo_flags & MO_EMPTY) {
if ((flags & VFS_CREATEOPT) == 0)
continue;
sp = kmem_alloc(strlen(opt) + 1, KM_SLEEP);
(void) strcpy(sp, opt);
mop->mo_name = sp;
if (arg != NULL)
mop->mo_flags = MO_HASVALUE;
else
mop->mo_flags = 0;
} else if (strcmp(opt, mop->mo_name)) {
continue;
}
if ((mop->mo_flags & MO_IGNORE) && (flags & VFS_NOFORCEOPT))
break;
if (arg != NULL && (mop->mo_flags & MO_HASVALUE) != 0) {
sp = kmem_alloc(strlen(arg) + 1, KM_SLEEP);
(void) strcpy(sp, arg);
} else {
sp = NULL;
}
if (mop->mo_arg != NULL)
kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
mop->mo_arg = sp;
if (flags & VFS_DISPLAY)
mop->mo_flags &= ~MO_NODISPLAY;
if (flags & VFS_NODISPLAY)
mop->mo_flags |= MO_NODISPLAY;
mop->mo_flags |= MO_SET;
if (mop->mo_cancel != NULL) {
char **cp;
for (cp = mop->mo_cancel; *cp != NULL; cp++)
vfs_clearmntopt_nolock(mops, *cp, 0);
}
if (update_mnttab)
vfs_mnttab_modtimeupd();
break;
}
}
void
vfs_setmntopt(struct vfs *vfsp, const char *opt, const char *arg, int flags)
{
int gotlock = 0;
if (VFS_ON_LIST(vfsp)) {
gotlock = 1;
vfs_list_lock();
}
vfs_setmntopt_nolock(&vfsp->vfs_mntopts, opt, arg, flags, gotlock);
if (gotlock)
vfs_list_unlock();
}
void
vfs_parsemntopts(mntopts_t *mops, char *osp, int create)
{
char *s = osp, *p, *nextop, *valp, *cp, *ep = NULL;
int setflg = VFS_NOFORCEOPT;
if (osp == NULL)
return;
while (*s != '\0') {
p = strchr(s, ',');
if (p == NULL) {
cp = NULL;
p = s + strlen(s);
} else {
cp = p;
*p++ = '\0';
}
nextop = p;
p = strchr(s, '=');
if (p == NULL) {
valp = NULL;
ep = NULL;
} else {
ep = p;
*p++ = '\0';
valp = p;
}
if (create)
setflg |= VFS_CREATEOPT;
vfs_setmntopt_nolock(mops, s, valp, setflg, 0);
if (cp != NULL)
*cp = ',';
if (valp != NULL)
*ep = '=';
s = nextop;
}
}
struct mntopt *
vfs_hasopt(const mntopts_t *mops, const char *opt)
{
struct mntopt *mop;
uint_t i, count;
count = mops->mo_count;
for (i = 0; i < count; i++) {
mop = &mops->mo_list[i];
if (mop->mo_flags & MO_EMPTY)
continue;
if (strcmp(opt, mop->mo_name) == 0)
return (mop);
}
return (NULL);
}
static int
vfs_optionisset_nolock(const mntopts_t *mops, const char *opt, char **argp)
{
struct mntopt *mop;
uint_t i, count;
count = mops->mo_count;
for (i = 0; i < count; i++) {
mop = &mops->mo_list[i];
if (mop->mo_flags & MO_EMPTY)
continue;
if (strcmp(opt, mop->mo_name))
continue;
if ((mop->mo_flags & MO_SET) == 0)
return (0);
if (argp != NULL && (mop->mo_flags & MO_HASVALUE) != 0)
*argp = mop->mo_arg;
return (1);
}
return (0);
}
int
vfs_optionisset(const struct vfs *vfsp, const char *opt, char **argp)
{
int ret;
vfs_list_read_lock();
ret = vfs_optionisset_nolock(&vfsp->vfs_mntopts, opt, argp);
vfs_list_unlock();
return (ret);
}
int
vfs_buildoptionstr(const mntopts_t *mp, char *buf, int len)
{
char *cp;
uint_t i;
buf[0] = '\0';
cp = buf;
for (i = 0; i < mp->mo_count; i++) {
struct mntopt *mop;
mop = &mp->mo_list[i];
if (mop->mo_flags & MO_SET) {
int optlen, comma = 0;
if (buf[0] != '\0')
comma = 1;
optlen = strlen(mop->mo_name);
if (strlen(buf) + comma + optlen + 1 > len)
goto err;
if (comma)
*cp++ = ',';
(void) strcpy(cp, mop->mo_name);
cp += optlen;
if (mop->mo_arg != NULL) {
int arglen;
arglen = strlen(mop->mo_arg);
if (strlen(buf) + arglen + 2 > len)
goto err;
*cp++ = '=';
(void) strcpy(cp, mop->mo_arg);
cp += arglen;
}
}
}
return (0);
err:
return (EOVERFLOW);
}
static void
vfs_freecancelopt(char **moc)
{
if (moc != NULL) {
int ccnt = 0;
char **cp;
for (cp = moc; *cp != NULL; cp++) {
kmem_free(*cp, strlen(*cp) + 1);
ccnt++;
}
kmem_free(moc, (ccnt + 1) * sizeof (char *));
}
}
static void
vfs_freeopt(mntopt_t *mop)
{
if (mop->mo_name != NULL)
kmem_free(mop->mo_name, strlen(mop->mo_name) + 1);
vfs_freecancelopt(mop->mo_cancel);
if (mop->mo_arg != NULL)
kmem_free(mop->mo_arg, strlen(mop->mo_arg) + 1);
}
void
vfs_freeopttbl(mntopts_t *mp)
{
uint_t i, count;
count = mp->mo_count;
for (i = 0; i < count; i++) {
vfs_freeopt(&mp->mo_list[i]);
}
if (count) {
kmem_free(mp->mo_list, sizeof (mntopt_t) * count);
mp->mo_count = 0;
mp->mo_list = NULL;
}
}
void
vfs_mnttab_modtimeupd()
{
}
int
fake_dounmount(struct vfs *vfsp, int flag)
{
cred_t *cr = CRED();
vnode_t *coveredvp;
int error;
coveredvp = vfsp->vfs_vnodecovered;
if ((flag & MS_FORCE) == 0)
(void) VFS_SYNC(vfsp, 0, cr);
if (rootdir != NULL) {
ASSERT(rootdir != coveredvp);
VN_RELE(rootdir);
rootdir = NULL;
}
vfs_lock_wait(vfsp);
if ((error = VFS_UNMOUNT(vfsp, flag, cr)) != 0) {
int err2;
vfs_unlock(vfsp);
err2 = VFS_ROOT(vfsp, &rootdir);
if (err2 != 0) {
panic("fake_dounmount, get root %d\n", err2);
}
} else {
if (coveredvp != NULL) {
vfsp->vfs_vnodecovered = NULL;
rootdir = coveredvp;
}
if (rootvfs == vfsp) {
VFS_RELE(vfsp);
rootvfs = NULL;
}
vfs_unlock(vfsp);
VFS_RELE(vfsp);
}
return (error);
}
static krwlock_t vpvfsentry_ve_lock;
int
vfs_lock(vfs_t *vfsp)
{
if (rw_tryenter(&vpvfsentry_ve_lock, RW_WRITER))
return (0);
return (EBUSY);
}
int
vfs_rlock(vfs_t *vfsp)
{
if (rw_tryenter(&vpvfsentry_ve_lock, RW_READER))
return (0);
return (EBUSY);
}
void
vfs_lock_wait(vfs_t *vfsp)
{
rw_enter(&vpvfsentry_ve_lock, RW_WRITER);
}
void
vfs_rlock_wait(vfs_t *vfsp)
{
rw_enter(&vpvfsentry_ve_lock, RW_READER);
}
void
vfs_unlock(vfs_t *vfsp)
{
rw_exit(&vpvfsentry_ve_lock);
}
void
vfs_make_fsid(fsid_t *fsi, dev_t dev, int val)
{
if (!cmpldev((dev32_t *)&fsi->val[0], dev))
panic("device number too big for fsid!");
fsi->val[1] = val;
}
int
vfs_lock_held(vfs_t *vfsp)
{
int held;
held = rw_write_held(&vpvfsentry_ve_lock);
return (held);
}
void
vfs_list_lock()
{
rw_enter(&vfslist, RW_WRITER);
}
void
vfs_list_read_lock()
{
rw_enter(&vfslist, RW_READER);
}
void
vfs_list_unlock()
{
rw_exit(&vfslist);
}
int
vfs_devismounted(dev_t dev)
{
return (0);
}
int
vfs_opsinuse(vfsops_t *ops)
{
return (0);
}
struct vfssw *
allocate_vfssw(const char *type)
{
struct vfssw *vswp;
if (type[0] == '\0' || strlen(type) + 1 > _ST_FSTYPSZ) {
return (NULL);
}
ASSERT(VFSSW_WRITE_LOCKED());
for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++)
if (!ALLOCATED_VFSSW(vswp)) {
vswp->vsw_name = kmem_alloc(strlen(type) + 1, KM_SLEEP);
(void) strcpy(vswp->vsw_name, type);
ASSERT(vswp->vsw_count == 0);
vswp->vsw_count = 1;
mutex_init(&vswp->vsw_lock, NULL, MUTEX_DEFAULT, NULL);
return (vswp);
}
return (NULL);
}
struct vfssw *
vfs_getvfssw(const char *type)
{
struct vfssw *vswp;
if (type == NULL || *type == '\0')
return (NULL);
for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
if (strcmp(type, vswp->vsw_name) == 0) {
return (vswp);
}
}
return (NULL);
}
struct vfssw *
vfs_getvfsswbyname(const char *type)
{
struct vfssw *vswp;
ASSERT(VFSSW_LOCKED());
if (type == NULL || *type == '\0')
return (NULL);
for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
if (strcmp(type, vswp->vsw_name) == 0) {
vfs_refvfssw(vswp);
return (vswp);
}
}
return (NULL);
}
void
vfs_refvfssw(struct vfssw *vswp)
{
mutex_enter(&vswp->vsw_lock);
vswp->vsw_count++;
mutex_exit(&vswp->vsw_lock);
}
void
vfs_unrefvfssw(struct vfssw *vswp)
{
mutex_enter(&vswp->vsw_lock);
vswp->vsw_count--;
mutex_exit(&vswp->vsw_lock);
}
uint_t
vf_to_stf(uint_t vf)
{
uint_t stf = 0;
if (vf & VFS_RDONLY)
stf |= ST_RDONLY;
if (vf & VFS_NOSETUID)
stf |= ST_NOSUID;
if (vf & VFS_NOTRUNC)
stf |= ST_NOTRUNC;
return (stf);
}
#pragma init(vfsinit)
void
vfsinit(void)
{
vn_create_cache();
rootdir = vn_alloc(KM_SLEEP);
rootdir->v_type = VDIR;
}
vfs_t *
vfs_alloc(int kmflag)
{
vfs_t *vfsp;
vfsp = kmem_alloc(sizeof (struct vfs), kmflag);
bzero(vfsp, sizeof (vfs_t));
return (vfsp);
}
void
vfs_free(vfs_t *vfsp)
{
if (vfsp->vfs_implp)
vfsimpl_teardown(vfsp);
sema_destroy(&vfsp->vfs_reflock);
kmem_free(vfsp, sizeof (struct vfs));
}
void
vfs_hold(vfs_t *vfsp)
{
atomic_inc_32(&vfsp->vfs_count);
ASSERT(vfsp->vfs_count != 0);
}
void
vfs_rele(vfs_t *vfsp)
{
ASSERT(vfsp->vfs_count != 0);
if (atomic_dec_32_nv(&vfsp->vfs_count) == 0) {
VFS_FREEVFS(vfsp);
vfs_free(vfsp);
}
}
int
fs_build_vector(void *vector, int *unused_ops,
const fs_operation_trans_def_t *translation,
const fs_operation_def_t *operations)
{
int i, num_trans, num_ops, used;
{
const fs_operation_trans_def_t *p;
for (num_trans = 0, p = translation;
p->name != NULL;
num_trans++, p++)
;
}
{
const fs_operation_def_t *p;
for (num_ops = 0, p = operations;
p->name != NULL;
num_ops++, p++)
;
}
used = 0;
for (i = 0; i < num_trans; i++) {
int j, found;
char *curname;
fs_generic_func_p result;
fs_generic_func_p *location;
curname = translation[i].name;
found = 0;
for (j = 0; j < num_ops; j++) {
if (strcmp(operations[j].name, curname) == 0) {
used++;
found = 1;
break;
}
}
if (found) {
result = operations[j].func.fs_generic;
if (result == fs_default) {
result = translation[i].defaultFunc;
} else if (result == fs_error) {
result = translation[i].errorFunc;
} else if (result == NULL) {
return (EINVAL);
}
} else {
result = translation[i].defaultFunc;
}
location = (fs_generic_func_p *)
(((char *)vector) + translation[i].offset);
*location = result;
}
*unused_ops = num_ops - used;
return (0);
}
int
fs_error(void)
{
cmn_err(CE_PANIC, "fs_error called");
return (0);
}
int
fs_default(void)
{
cmn_err(CE_PANIC, "fs_default called");
return (0);
}
#define VFTINDEX(feature) (((feature) >> 32) & 0xFFFFFFFF)
#define VFTBITS(feature) ((feature) & 0xFFFFFFFFLL)
void
vfs_set_feature(vfs_t *vfsp, vfs_feature_t feature)
{
if (vfsp->vfs_implp == NULL)
return;
vfsp->vfs_featureset[VFTINDEX(feature)] |= VFTBITS(feature);
}
void
vfs_clear_feature(vfs_t *vfsp, vfs_feature_t feature)
{
if (vfsp->vfs_implp == NULL)
return;
vfsp->vfs_featureset[VFTINDEX(feature)] &= VFTBITS(~feature);
}
int
vfs_has_feature(vfs_t *vfsp, vfs_feature_t feature)
{
int ret = 0;
if (vfsp->vfs_implp == NULL)
return (ret);
if (vfsp->vfs_featureset[VFTINDEX(feature)] & VFTBITS(feature))
ret = 1;
return (ret);
}