#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/swap.h>
#include <sys/debug.h>
#include <sys/vnode.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/pathname.h>
#include <sys/bootconf.h>
#include <sys/dumphdr.h>
#include <sys/dc_ki.h>
#include <sys/poll.h>
#include <sys/sunddi.h>
#include <sys/sysmacros.h>
#include <sys/zone.h>
#include <sys/policy.h>
#include <sys/ctfs.h>
#include <sys/objfs.h>
#include <sys/console.h>
#include <sys/reboot.h>
#include <sys/attr.h>
#include <sys/zio.h>
#include <sys/spa.h>
#include <sys/lofi.h>
#include <sys/bootprops.h>
#include <vm/page.h>
#include <fs/fs_subr.h>
extern void initialize_vopstats(vopstats_t *);
extern vopstats_t *get_fstype_vopstats(struct vfs *, struct vfssw *);
extern vsk_anchor_t *get_vskstat_anchor(struct vfs *);
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_freemnttab(struct vfs *);
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_createopttbl_extend(mntopts_t *, const char *,
const mntopts_t *);
static char **vfs_copycancelopt_extend(char **const, int);
static void vfs_freecancelopt(char **);
static void getrootfs(char **, char **);
static int getmacpath(dev_info_t *, void *);
static void vfs_mnttabvp_setup(void);
struct ipmnt {
struct ipmnt *mip_next;
dev_t mip_dev;
struct vfs *mip_vfsp;
};
static kmutex_t vfs_miplist_mutex;
static struct ipmnt *vfs_miplist = NULL;
static struct ipmnt *vfs_miplist_end = NULL;
static kmem_cache_t *vfs_cache;
vnode_t *rootdir;
vnode_t *devicesdir;
vnode_t *devdir;
char *server_rootpath;
char *server_hostname;
static struct vfs root;
static struct vfs devices;
static struct vfs dev;
struct vfs *rootvfs = &root;
rvfs_t *rvfs_list;
int vfshsz = 512;
timespec_t vfs_mnttab_ctime;
timespec_t vfs_mnttab_mtime;
char *vfs_dummyfstype = "\0";
struct pollhead vfs_pollhd;
struct vnode *vfs_mntdummyvp;
int mntfstype;
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)
{
refstr_t *mntpt;
int ret = (*(vfsp)->vfs_op->vfs_root)(vfsp, vpp);
if (ret == 0 && vfsp->vfs_mntpt != NULL &&
(*vpp)->v_path == vn_vpath_empty) {
const char *path;
mntpt = vfs_getmntpoint(vfsp);
path = refstr_value(mntpt);
vn_setpath_str(*vpp, path, strlen(path));
refstr_rele(mntpt);
}
return (ret);
}
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)
{
if (vfs_has_feature(vfsp, VFSFT_SYSATTR_VIEWS) &&
fidp->fid_len == XATTR_FIDSZ)
return (xattr_dir_vget(vfsp, vpp, 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);
}
int
fsop_syncfs(vfs_t *vfsp, uint64_t flags, cred_t *cr)
{
return (*(vfsp)->vfs_op->vfs_syncfs)(vfsp, flags, cr);
}
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 },
{ VFSNAME_SYNCFS, offsetof(vfsops_t, vfs_syncfs),
(fs_generic_func_p)fs_nosys_syncfs,
(fs_generic_func_p)fs_nosys_syncfs },
{ NULL, 0, NULL, NULL }
};
return (fs_build_vector(actual, unused_ops, vfs_ops_table, template));
}
void
zfs_boot_init(void)
{
if (strcmp(rootfs.bo_fstype, MNTTYPE_ZFS) == 0)
spa_boot_init();
}
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)
{
vfsops_t *op;
ASSERT(vfsp != NULL);
ASSERT(vfsops != NULL);
op = vfsp->vfs_op;
membar_consumer();
if (vfsp->vfs_femhead == NULL &&
atomic_cas_ptr(&vfsp->vfs_op, op, vfsops) == op) {
return;
}
fsem_setvfsops(vfsp, vfsops);
}
vfsops_t *
vfs_getops(vfs_t *vfsp)
{
vfsops_t *op;
ASSERT(vfsp != NULL);
op = vfsp->vfs_op;
membar_consumer();
if (vfsp->vfs_femhead == NULL && op == vfsp->vfs_op) {
return (op);
} else {
return (fsem_getvfsops(vfsp));
}
}
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)
{
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;
}
void
vfs_sync(int flag)
{
struct vfssw *vswp;
RLOCK_VFSSW();
for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
if (ALLOCATED_VFSSW(vswp) && VFS_INSTALLED(vswp)) {
vfs_refvfssw(vswp);
RUNLOCK_VFSSW();
(void) (*vswp->vsw_vfsops.vfs_sync)(NULL, flag,
CRED());
vfs_unrefvfssw(vswp);
RLOCK_VFSSW();
}
}
RUNLOCK_VFSSW();
}
void
sync(void)
{
vfs_sync(0);
}
krwlock_t vfssw_lock;
static krwlock_t vfslist;
static void
vfs_mountdevices(void)
{
struct vfssw *vsw;
struct vnode *mvp;
struct mounta mounta = {
NULL,
NULL,
MS_SYSSPACE,
NULL,
NULL,
0,
NULL,
0
};
if (modload("fs", "devfs") == -1)
panic("Cannot _init devfs module");
RLOCK_VFSSW();
vsw = vfs_getvfsswbyname("devfs");
VFS_INIT(&devices, &vsw->vsw_vfsops, NULL);
VFS_HOLD(&devices);
if (lookupname("/devices", UIO_SYSSPACE, FOLLOW, NULLVPP, &mvp))
panic("Cannot find /devices");
if (VFS_MOUNT(&devices, mvp, &mounta, CRED()))
panic("Cannot mount /devices");
RUNLOCK_VFSSW();
vfs_setresource(&devices, "/devices", 0);
vfs_setmntpoint(&devices, "/devices", 0);
if (VFS_ROOT(&devices, &devicesdir))
panic("vfs_mountdevices: not devices root");
if (vfs_lock(&devices) != 0) {
VN_RELE(devicesdir);
cmn_err(CE_NOTE, "Cannot acquire vfs_lock of /devices");
return;
}
if (vn_vfswlock(mvp) != 0) {
vfs_unlock(&devices);
VN_RELE(devicesdir);
cmn_err(CE_NOTE, "Cannot acquire vfswlock of /devices");
return;
}
vfs_add(mvp, &devices, 0);
vn_vfsunlock(mvp);
vfs_unlock(&devices);
VN_RELE(devicesdir);
}
static void
vfs_mountdev1(void)
{
struct vfssw *vsw;
struct vnode *mvp;
struct mounta mounta = {
NULL,
NULL,
MS_SYSSPACE | MS_OVERLAY,
NULL,
NULL,
0,
NULL,
0
};
if (modload("fs", "dev") == -1)
cmn_err(CE_PANIC, "Cannot _init dev module\n");
RLOCK_VFSSW();
vsw = vfs_getvfsswbyname("dev");
VFS_INIT(&dev, &vsw->vsw_vfsops, NULL);
VFS_HOLD(&dev);
if (lookupname("/dev", UIO_SYSSPACE, FOLLOW, NULLVPP, &mvp))
cmn_err(CE_PANIC, "Cannot find /dev\n");
if (VFS_MOUNT(&dev, mvp, &mounta, CRED()))
cmn_err(CE_PANIC, "Cannot mount /dev 1\n");
RUNLOCK_VFSSW();
vfs_setresource(&dev, "/dev", 0);
vfs_setmntpoint(&dev, "/dev", 0);
if (VFS_ROOT(&dev, &devdir))
cmn_err(CE_PANIC, "vfs_mountdev1: not dev root");
if (vfs_lock(&dev) != 0) {
VN_RELE(devdir);
cmn_err(CE_NOTE, "Cannot acquire vfs_lock of /dev");
return;
}
if (vn_vfswlock(mvp) != 0) {
vfs_unlock(&dev);
VN_RELE(devdir);
cmn_err(CE_NOTE, "Cannot acquire vfswlock of /dev");
return;
}
vfs_add(mvp, &dev, 0);
vn_vfsunlock(mvp);
vfs_unlock(&dev);
VN_RELE(devdir);
}
static void
vfs_mountfs(char *module, char *spec, char *path)
{
struct vnode *mvp;
struct mounta mounta;
vfs_t *vfsp;
bzero(&mounta, sizeof (mounta));
mounta.flags = MS_SYSSPACE | MS_DATA;
mounta.fstype = module;
mounta.spec = spec;
mounta.dir = path;
if (lookupname(path, UIO_SYSSPACE, FOLLOW, NULLVPP, &mvp)) {
cmn_err(CE_WARN, "Cannot find %s", path);
return;
}
if (domount(NULL, &mounta, mvp, CRED(), &vfsp))
cmn_err(CE_WARN, "Cannot mount %s", path);
else
VFS_RELE(vfsp);
VN_RELE(mvp);
}
void
vfs_mountroot(void)
{
struct vnode *rvp = NULL;
char *path;
size_t plen;
struct vfssw *vswp;
proc_t *p;
rw_init(&vfssw_lock, NULL, RW_DEFAULT, NULL);
rw_init(&vfslist, NULL, RW_DEFAULT, NULL);
rvfs_list = kmem_zalloc(vfshsz * sizeof (rvfs_t), KM_SLEEP);
if (rootconf())
panic("vfs_mountroot: cannot mount root");
vfs_setmntpoint(rootvfs, "/", 0);
if (VFS_ROOT(rootvfs, &rootdir))
panic("vfs_mountroot: no root vnode");
mutex_enter(&pidlock);
for (p = practive; p != NULL; p = p->p_next) {
ASSERT(p == &p0 || p->p_parent == &p0);
PTOU(p)->u_cdir = rootdir;
VN_HOLD(PTOU(p)->u_cdir);
PTOU(p)->u_rdir = NULL;
}
mutex_exit(&pidlock);
global_zone->zone_rootvp = rootdir;
VN_HOLD(global_zone->zone_rootvp);
modrootloaded = 1;
zfs_boot_init();
vfs_setresource(rootvfs, rootfs.bo_name, 0);
clboot_mountroot();
if ((vswp = vfs_getvfsswbyvfsops(vfs_getops(rootvfs))) != NULL) {
if (vswp->vsw_flag & VSW_STATS) {
initialize_vopstats(&rootvfs->vfs_vopstats);
rootvfs->vfs_flag |= VFS_STATS;
rootvfs->vfs_fstypevsp =
get_fstype_vopstats(rootvfs, vswp);
rootvfs->vfs_vskap = get_vskstat_anchor(rootvfs);
}
vfs_unrefvfssw(vswp);
}
vfs_mountdevices();
vfs_mountdev1();
vfs_mountfs("ctfs", "ctfs", CTFS_ROOT);
vfs_mountfs("proc", "/proc", "/proc");
vfs_mountfs("mntfs", "/etc/mnttab", "/etc/mnttab");
vfs_mountfs("tmpfs", "/etc/svc/volatile", "/etc/svc/volatile");
vfs_mountfs("objfs", "objfs", OBJFS_ROOT);
vfs_mountfs("bootfs", "bootfs", "/system/boot");
if (getzoneid() == GLOBAL_ZONEID) {
vfs_mountfs("sharefs", "sharefs", "/etc/dfs/sharetab");
}
if (strcmp(rootfs.bo_fstype, "zfs") != 0) {
plen = strlen("/devices");
path = kmem_alloc(plen + MAXPATHLEN, KM_SLEEP);
(void) strcpy(path, "/devices");
if (i_ddi_prompath_to_devfspath(rootfs.bo_name, path + plen)
!= DDI_SUCCESS ||
lookupname(path, UIO_SYSSPACE, FOLLOW, NULLVPP, &rvp)) {
path[plen + MAXPATHLEN - 1] = '\0';
#ifdef DEBUG
cmn_err(CE_WARN, "!Cannot lookup root device: %s",
path);
#endif
}
kmem_free(path, plen + MAXPATHLEN);
}
vfs_mnttabvp_setup();
}
static int
lofi_add(const char *fsname, struct vfs *vfsp,
mntopts_t *mntopts, struct mounta *uap)
{
int fromspace = (uap->flags & MS_SYSSPACE) ?
UIO_SYSSPACE : UIO_USERSPACE;
struct lofi_ioctl *li = NULL;
struct vnode *vp = NULL;
struct pathname pn = { NULL };
ldi_ident_t ldi_id;
ldi_handle_t ldi_hdl;
vfssw_t *vfssw;
int id;
int err = 0;
if ((vfssw = vfs_getvfssw(fsname)) == NULL)
return (0);
if (!(vfssw->vsw_flag & VSW_CANLOFI)) {
vfs_unrefvfssw(vfssw);
return (0);
}
vfs_unrefvfssw(vfssw);
vfssw = NULL;
if (pn_get(uap->spec, fromspace, &pn) != 0)
return (0);
if (lookupname(uap->spec, fromspace, FOLLOW, NULL, &vp) != 0)
goto out;
if (vp->v_type != VREG)
goto out;
if ((uap->flags & (MS_REMOUNT|MS_GLOBAL)) ||
vfs_optionisset_nolock(mntopts, MNTOPT_SUID, NULL) ||
vfs_optionisset_nolock(mntopts, MNTOPT_SETUID, NULL) ||
vfs_optionisset_nolock(mntopts, MNTOPT_DEVICES, NULL)) {
err = EINVAL;
goto out;
}
ldi_id = ldi_ident_from_anon();
li = kmem_zalloc(sizeof (*li), KM_SLEEP);
(void) strlcpy(li->li_filename, pn.pn_path, MAXPATHLEN);
err = ldi_open_by_name("/dev/lofictl", FREAD | FWRITE, kcred,
&ldi_hdl, ldi_id);
if (err)
goto out2;
err = ldi_ioctl(ldi_hdl, LOFI_MAP_FILE, (intptr_t)li,
FREAD | FWRITE | FKIOCTL, kcred, &id);
(void) ldi_close(ldi_hdl, FREAD | FWRITE, kcred);
if (!err)
vfsp->vfs_lofi_id = id;
out2:
ldi_ident_release(ldi_id);
out:
if (li != NULL)
kmem_free(li, sizeof (*li));
if (vp != NULL)
VN_RELE(vp);
pn_free(&pn);
return (err);
}
static void
lofi_remove(struct vfs *vfsp)
{
struct lofi_ioctl *li;
ldi_ident_t ldi_id;
ldi_handle_t ldi_hdl;
int err;
if (vfsp->vfs_lofi_id == 0)
return;
ldi_id = ldi_ident_from_anon();
li = kmem_zalloc(sizeof (*li), KM_SLEEP);
li->li_id = vfsp->vfs_lofi_id;
li->li_cleanup = B_TRUE;
err = ldi_open_by_name("/dev/lofictl", FREAD | FWRITE, kcred,
&ldi_hdl, ldi_id);
if (err)
goto out;
err = ldi_ioctl(ldi_hdl, LOFI_UNMAP_FILE_MINOR, (intptr_t)li,
FREAD | FWRITE | FKIOCTL, kcred, NULL);
(void) ldi_close(ldi_hdl, FREAD | FWRITE, kcred);
if (!err)
vfsp->vfs_lofi_id = 0;
out:
ldi_ident_release(ldi_id);
kmem_free(li, sizeof (*li));
}
int
domount(char *fsname, struct mounta *uap, vnode_t *vp, struct cred *credp,
struct vfs **vfspp)
{
struct vfssw *vswp;
vfsops_t *vfsops;
struct vfs *vfsp;
struct vnode *bvp;
dev_t bdev = 0;
mntopts_t mnt_mntopts;
int error = 0;
int copyout_error = 0;
int ovflags = 0;
char *opts = uap->optptr;
char *inargs = opts;
int optlen = uap->optlen;
int remount;
int rdonly;
int nbmand = 0;
int delmip = 0;
int addmip = 0;
int splice = ((uap->flags & MS_NOSPLICE) == 0);
int fromspace = (uap->flags & MS_SYSSPACE) ?
UIO_SYSSPACE : UIO_USERSPACE;
char *resource = NULL, *mountpt = NULL;
refstr_t *oldresource, *oldmntpt;
struct pathname pn, rpn;
vsk_anchor_t *vskap;
char fstname[FSTYPSZ];
zone_t *zone;
mutex_enter(&vp->v_lock);
vp->v_flag |= VVFSLOCK;
mutex_exit(&vp->v_lock);
mnt_mntopts.mo_count = 0;
if (fsname != NULL) {
if ((vswp = vfs_getvfssw(fsname)) == NULL) {
return (EINVAL);
}
} else if (uap->flags & (MS_OPTIONSTR | MS_DATA | MS_FSS)) {
size_t n;
uint_t fstype;
fsname = fstname;
if ((fstype = (uintptr_t)uap->fstype) < 256) {
RLOCK_VFSSW();
if (fstype == 0 || fstype >= nfstype ||
!ALLOCATED_VFSSW(&vfssw[fstype])) {
RUNLOCK_VFSSW();
return (EINVAL);
}
(void) strcpy(fsname, vfssw[fstype].vsw_name);
RUNLOCK_VFSSW();
if ((vswp = vfs_getvfssw(fsname)) == NULL)
return (EINVAL);
} else {
if (uap->flags & MS_SYSSPACE) {
error = copystr(uap->fstype, fsname,
FSTYPSZ, &n);
} else {
error = copyinstr(uap->fstype, fsname,
FSTYPSZ, &n);
}
if (error) {
if (error == ENAMETOOLONG)
return (EINVAL);
return (error);
}
if ((vswp = vfs_getvfssw(fsname)) == NULL)
return (EINVAL);
}
} else {
if ((vswp = vfs_getvfsswbyvfsops(vfs_getops(rootvfs))) == NULL)
return (EINVAL);
fsname = vswp->vsw_name;
}
if (!VFS_INSTALLED(vswp))
return (EINVAL);
if ((error = secpolicy_fs_allowed_mount(fsname)) != 0) {
vfs_unrefvfssw(vswp);
return (error);
}
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 (remount = vfs_optionisset_nolock(&mnt_mntopts,
MNTOPT_REMOUNT, NULL)) {
if (!(vswp->vsw_flag & VSW_CANREMOUNT)) {
error = ENOTSUP;
goto errout;
}
uap->flags |= MS_REMOUNT;
}
if (rdonly = 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;
}
nbmand = vfs_optionisset_nolock(&mnt_mntopts, MNTOPT_NBMAND, NULL);
ASSERT(splice || !remount);
if (splice) {
ASSERT(vp->v_count > 0);
if (pn_get(uap->spec, fromspace, &pn) == 0) {
resource = kmem_alloc(pn.pn_pathlen + 1,
KM_SLEEP);
(void) strcpy(resource, pn.pn_path);
pn_free(&pn);
}
if ((vswp->vsw_flag & VSW_MOUNTDEV) &&
(uap->flags & MS_GLOBAL) == 0 &&
lookupname(uap->spec, fromspace,
FOLLOW, NULL, &bvp) == 0) {
addmip = 1;
}
if ((error = pn_get(uap->dir, fromspace, &pn)) == 0) {
pathname_t *pnp;
if (*pn.pn_path != '/') {
error = EINVAL;
pn_free(&pn);
goto errout;
}
pn_alloc(&rpn);
if (fromspace == UIO_USERSPACE) {
if ((error = lookuppn(&pn, &rpn, FOLLOW, NULL,
NULL)) == 0) {
pnp = &rpn;
} else {
pn_free(&rpn);
pn_free(&pn);
goto errout;
}
} else {
pnp = &pn;
}
mountpt = kmem_alloc(pnp->pn_pathlen + 1, KM_SLEEP);
(void) strcpy(mountpt, pnp->pn_path);
if ((curproc->p_zone->zone_rootpathlen - 1 +
strlen(mountpt)) > MAXPATHLEN ||
(resource != NULL &&
(curproc->p_zone->zone_rootpathlen - 1 +
strlen(resource)) > MAXPATHLEN)) {
error = ENAMETOOLONG;
}
pn_free(&rpn);
pn_free(&pn);
}
if (error)
goto errout;
if (vn_vfswlock(vp) != 0) {
error = EBUSY;
goto errout;
}
if (vn_mountedvfs(vp) != NULL) {
vn_vfsunlock(vp);
error = EBUSY;
goto errout;
}
if (vp->v_flag & VNOMOUNT) {
vn_vfsunlock(vp);
error = EINVAL;
goto errout;
}
}
if ((uap->flags & (MS_DATA | MS_OPTIONSTR)) == 0) {
uap->dataptr = NULL;
uap->datalen = 0;
}
if (remount) {
if ((vp->v_flag & VROOT) == 0) {
vn_vfsunlock(vp);
error = ENOENT;
goto errout;
}
if (rdonly && vn_is_readonly(vp) == 0 &&
(vswp->vsw_flag & VSW_CANRWRO) == 0) {
vn_vfsunlock(vp);
error = EINVAL;
goto errout;
}
if ((nbmand && ((vp->v_vfsp->vfs_flag & VFS_NBMAND) == 0)) ||
(!nbmand && (vp->v_vfsp->vfs_flag & VFS_NBMAND))) {
vn_vfsunlock(vp);
error = EINVAL;
goto errout;
}
vfsp = vp->v_vfsp;
ovflags = vfsp->vfs_flag;
vfsp->vfs_flag |= VFS_REMOUNT;
vfsp->vfs_flag &= ~VFS_RDONLY;
} else {
vfsp = vfs_alloc(KM_SLEEP);
VFS_INIT(vfsp, vfsops, NULL);
}
VFS_HOLD(vfsp);
if ((error = lofi_add(fsname, vfsp, &mnt_mntopts, uap)) != 0) {
if (!remount) {
if (splice)
vn_vfsunlock(vp);
vfs_free(vfsp);
} else {
vn_vfsunlock(vp);
VFS_RELE(vfsp);
}
goto errout;
}
if (vfsp->vfs_lofi_id != 0) {
uap->flags |= MS_NOSUID;
vfs_setmntopt_nolock(&mnt_mntopts, MNTOPT_NOSUID, NULL, 0, 0);
}
if ((sema_tryp(&vfsp->vfs_reflock) == 0) &&
!(vfsp->vfs_flag & VFS_REMOUNT))
cmn_err(CE_WARN,
"mount type %s couldn't get vfs_reflock", vswp->vsw_name);
if (!remount) {
if (error = vfs_lock(vfsp)) {
lofi_remove(vfsp);
if (splice)
vn_vfsunlock(vp);
vfs_free(vfsp);
goto errout;
}
} else {
vfs_lock_wait(vfsp);
}
if (!addmip) {
if ((vswp->vsw_flag & VSW_MOUNTDEV) &&
(uap->flags & MS_GLOBAL) == 0 &&
lookupname(uap->spec, fromspace, FOLLOW, NULL, &bvp) == 0) {
addmip = 1;
}
}
if (addmip) {
vnode_t *lvp = NULL;
error = vfs_get_lofi(vfsp, &lvp);
if (error > 0) {
lofi_remove(vfsp);
if (splice)
vn_vfsunlock(vp);
vfs_unlock(vfsp);
if (remount) {
VFS_RELE(vfsp);
} else {
vfs_free(vfsp);
}
goto errout;
} else if (error == -1) {
bdev = bvp->v_rdev;
VN_RELE(bvp);
} else {
bdev = lvp->v_rdev;
VN_RELE(lvp);
VN_RELE(bvp);
}
vfs_addmip(bdev, vfsp);
addmip = 0;
delmip = 1;
}
if (splice)
dnlc_purge_vp(vp);
if (uap->flags & MS_OPTIONSTR) {
if (!(vswp->vsw_flag & VSW_HASPROTO)) {
mntopts_t tmp_mntopts;
tmp_mntopts.mo_count = 0;
vfs_createopttbl_extend(&tmp_mntopts, inargs,
&mnt_mntopts);
vfs_parsemntopts(&tmp_mntopts, inargs, 1);
vfs_swapopttbl_nolock(&mnt_mntopts, &tmp_mntopts);
vfs_freeopttbl(&tmp_mntopts);
}
}
if (INGLOBALZONE(curproc)) {
zone = zone_find_by_path(mountpt);
ASSERT(zone != NULL);
} else {
zone = curproc->p_zone;
zone_hold(zone);
}
mount_in_progress(zone);
vfs_swapopttbl(&mnt_mntopts, &vfsp->vfs_mntopts);
if (remount) {
if ((oldresource = vfsp->vfs_resource) != NULL)
refstr_hold(oldresource);
if ((oldmntpt = vfsp->vfs_mntpt) != NULL)
refstr_hold(oldmntpt);
}
vfs_setresource(vfsp, resource, 0);
vfs_setmntpoint(vfsp, mountpt, 0);
vnevent_mountedover(vp, NULL);
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) {
lofi_remove(vfsp);
if (remount) {
vfs_swapopttbl(&mnt_mntopts, &vfsp->vfs_mntopts);
vfs_setmntpoint(vfsp, refstr_value(oldmntpt),
VFSSP_VERBATIM);
if (oldmntpt)
refstr_rele(oldmntpt);
vfs_setresource(vfsp, refstr_value(oldresource),
VFSSP_VERBATIM);
if (oldresource)
refstr_rele(oldresource);
vfsp->vfs_flag = ovflags;
vfs_unlock(vfsp);
VFS_RELE(vfsp);
} else {
vfs_unlock(vfsp);
vfs_freemnttab(vfsp);
vfs_free(vfsp);
}
} else {
vfsp->vfs_mtime = ddi_get_time();
if (remount) {
vfsp->vfs_flag &= ~VFS_REMOUNT;
if (oldresource)
refstr_rele(oldresource);
if (oldmntpt)
refstr_rele(oldmntpt);
} else if (splice) {
vfs_add(vp, vfsp, uap->flags);
} else {
vfsp->vfs_zone = NULL;
VFS_HOLD(vfsp);
vfsp->vfs_vnodecovered = NULL;
}
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 = copyoutstr(inargs, opts,
optlen, NULL);
}
}
if (!remount && (vswp->vsw_flag & VSW_STATS) && splice) {
initialize_vopstats(&vfsp->vfs_vopstats);
vfsp->vfs_vskap = NULL;
vfsp->vfs_flag |= VFS_STATS;
vfsp->vfs_fstypevsp = get_fstype_vopstats(vfsp, vswp);
}
if (vswp->vsw_flag & VSW_XID)
vfsp->vfs_flag |= VFS_XID;
vfs_unlock(vfsp);
}
mount_completed(zone);
zone_rele(zone);
if (splice)
vn_vfsunlock(vp);
if ((error == 0) && (copyout_error == 0)) {
if (!remount) {
vskap = get_vskstat_anchor(vfsp);
if (vskap != NULL) {
vfs_lock_wait(vfsp);
if (vfsp->vfs_flag & VFS_STATS) {
vfsp->vfs_vskap = vskap;
}
vfs_unlock(vfsp);
}
}
*vfspp = vfsp;
}
errout:
vfs_freeopttbl(&mnt_mntopts);
if (resource != NULL)
kmem_free(resource, strlen(resource) + 1);
if (mountpt != NULL)
kmem_free(mountpt, strlen(mountpt) + 1);
if (addmip)
VN_RELE(bvp);
if (delmip)
vfs_delmip(vfsp);
ASSERT(vswp != NULL);
vfs_unrefvfssw(vswp);
if (inargs != opts)
kmem_free(inargs, MAX_MNTOPT_STR);
if (copyout_error) {
lofi_remove(vfsp);
VFS_RELE(vfsp);
error = copyout_error;
}
return (error);
}
static void
vfs_setpath(
struct vfs *vfsp,
refstr_t **refp,
const char *newpath,
uint32_t flag)
{
size_t len;
refstr_t *ref;
zone_t *zone = curproc->p_zone;
char *sp;
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);
if (zone == global_zone || (flag & VFSSP_VERBATIM) || *newpath != '/') {
ref = refstr_alloc(newpath);
goto out;
}
len = strlen(newpath) + zone->zone_rootpathlen - 1;
sp = kmem_alloc(len, KM_SLEEP);
(void) strcpy(sp, zone->zone_rootpath);
sp[zone->zone_rootpathlen - 2] = '\0';
(void) strcat(sp, newpath);
ref = refstr_alloc(sp);
kmem_free(sp, len);
out:
*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_createopttbl_extend(mntopts_t *mops, const char *opts,
const mntopts_t *mtmpl)
{
const char *s = opts;
uint_t count;
if (opts == NULL || *opts == '\0') {
count = 0;
} else {
count = 1;
for (s = strchr(s, ','); s != NULL; s = strchr(s, ',')) {
count++;
s++;
}
}
vfs_copyopttbl_extend(mtmpl, mops, count);
}
void
vfs_createopttbl(mntopts_t *mops, const char *opts)
{
vfs_createopttbl_extend(mops, opts, NULL);
}
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();
}
static mntopt_t *
vfs_addtag(mntopts_t *mops, const char *tag)
{
uint_t count;
mntopt_t *mop, *motbl;
count = mops->mo_count + 1;
motbl = kmem_zalloc(count * sizeof (mntopt_t), KM_SLEEP);
if (mops->mo_count) {
size_t len = (count - 1) * sizeof (mntopt_t);
bcopy(mops->mo_list, motbl, len);
kmem_free(mops->mo_list, len);
}
mops->mo_count = count;
mops->mo_list = motbl;
mop = &motbl[count - 1];
mop->mo_flags = MO_TAG;
mop->mo_name = kmem_alloc(strlen(tag) + 1, KM_SLEEP);
(void) strcpy(mop->mo_name, tag);
return (mop);
}
int
vfs_settag(uint_t major, uint_t minor, const char *mntpt, const char *tag,
cred_t *cr)
{
vfs_t *vfsp;
mntopts_t *mops;
mntopt_t *mop;
int found = 0;
dev_t dev = makedevice(major, minor);
int err = 0;
char *buf = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP);
vfs_list_lock();
vfsp = rootvfs;
do {
if (vfsp->vfs_dev == dev &&
strcmp(mntpt, refstr_value(vfsp->vfs_mntpt)) == 0) {
found = 1;
break;
}
vfsp = vfsp->vfs_next;
} while (vfsp != rootvfs);
if (!found) {
err = EINVAL;
goto out;
}
err = secpolicy_fs_config(cr, vfsp);
if (err != 0)
goto out;
mops = &vfsp->vfs_mntopts;
if ((mop = vfs_hasopt(mops, tag)) == NULL) {
int len;
(void) vfs_buildoptionstr(mops, buf, MAX_MNTOPT_STR);
len = strlen(buf);
if (len + strlen(tag) + 2 > MAX_MNTOPT_STR) {
err = ENAMETOOLONG;
goto out;
}
mop = vfs_addtag(mops, tag);
}
if ((mop->mo_flags & MO_TAG) == 0) {
err = EINVAL;
goto out;
}
vfs_setmntopt_nolock(mops, tag, NULL, 0, 1);
out:
vfs_list_unlock();
kmem_free(buf, MAX_MNTOPT_STR);
return (err);
}
int
vfs_clrtag(uint_t major, uint_t minor, const char *mntpt, const char *tag,
cred_t *cr)
{
vfs_t *vfsp;
mntopt_t *mop;
int found = 0;
dev_t dev = makedevice(major, minor);
int err = 0;
vfs_list_lock();
vfsp = rootvfs;
do {
if (vfsp->vfs_dev == dev &&
strcmp(mntpt, refstr_value(vfsp->vfs_mntpt)) == 0) {
found = 1;
break;
}
vfsp = vfsp->vfs_next;
} while (vfsp != rootvfs);
if (!found) {
err = EINVAL;
goto out;
}
err = secpolicy_fs_config(cr, vfsp);
if (err != 0)
goto out;
if ((mop = vfs_hasopt(&vfsp->vfs_mntopts, tag)) == NULL) {
err = EINVAL;
goto out;
}
if ((mop->mo_flags & MO_TAG) == 0) {
err = EINVAL;
goto out;
}
vfs_clearmntopt_nolock(&vfsp->vfs_mntopts, tag, 1);
out:
vfs_list_unlock();
return (err);
}
void
vfs_parsemntopts(mntopts_t *mops, char *osp, int create)
{
char *s = osp, *p, *nextop, *valp, *cp, *ep;
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;
} 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;
}
}
static int
vfs_mntdummyread(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cred,
caller_context_t *ct)
{
return (0);
}
static int
vfs_mntdummywrite(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cred,
caller_context_t *ct)
{
return (0);
}
static int
vfs_mntdummygetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
bzero(vap, sizeof (vattr_t));
vap->va_type = VREG;
vap->va_nlink = 1;
vap->va_ctime = vfs_mnttab_ctime;
vap->va_mtime = vfs_mnttab_mtime;
vap->va_atime = vap->va_mtime;
return (0);
}
static void
vfs_mnttabvp_setup(void)
{
vnode_t *tvp;
vnodeops_t *vfs_mntdummyvnops;
const fs_operation_def_t mnt_dummyvnodeops_template[] = {
VOPNAME_READ, { .vop_read = vfs_mntdummyread },
VOPNAME_WRITE, { .vop_write = vfs_mntdummywrite },
VOPNAME_GETATTR, { .vop_getattr = vfs_mntdummygetattr },
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
NULL, NULL
};
if (vn_make_ops("mnttab", mnt_dummyvnodeops_template,
&vfs_mntdummyvnops) != 0) {
cmn_err(CE_WARN, "vfs_mnttabvp_setup: vn_make_ops failed");
return;
}
tvp = vn_alloc(KM_SLEEP);
tvp->v_flag = VNOMOUNT|VNOMAP|VNOSWAP|VNOCACHE;
vn_setops(tvp, vfs_mntdummyvnops);
tvp->v_type = VREG;
tvp->v_data = (caddr_t)tvp;
tvp->v_vfsp = rootvfs;
vfs_mntdummyvp = tvp;
}
static void
vfs_mnttab_rwop(int rw)
{
struct uio uio;
struct iovec iov;
char buf[1];
if (vfs_mntdummyvp == NULL)
return;
bzero(&uio, sizeof (uio));
bzero(&iov, sizeof (iov));
iov.iov_base = buf;
iov.iov_len = 0;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_loffset = 0;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_resid = 0;
if (rw) {
(void) VOP_WRITE(vfs_mntdummyvp, &uio, 0, kcred, NULL);
} else {
(void) VOP_READ(vfs_mntdummyvp, &uio, 0, kcred, NULL);
}
}
void
vfs_mnttab_writeop(void)
{
vfs_mnttab_rwop(1);
}
void
vfs_mnttab_readop(void)
{
vfs_mnttab_rwop(0);
}
static void
vfs_freemnttab(struct vfs *vfsp)
{
ASSERT(!VFS_ON_LIST(vfsp));
if (vfsp->vfs_mntpt != NULL) {
refstr_rele(vfsp->vfs_mntpt);
vfsp->vfs_mntpt = NULL;
}
if (vfsp->vfs_resource != NULL) {
refstr_rele(vfsp->vfs_resource);
vfsp->vfs_resource = NULL;
}
vfs_freeopttbl(&vfsp->vfs_mntopts);
}
void
vfs_mnttab_modtime(timespec_t *ts)
{
ASSERT(RW_LOCK_HELD(&vfslist));
*ts = vfs_mnttab_mtime;
}
void
vfs_mnttab_poll(timespec_t *old, struct pollhead **phpp)
{
int changed;
*phpp = (struct pollhead *)NULL;
changed = (old->tv_nsec != vfs_mnttab_mtime.tv_nsec) ||
(old->tv_sec != vfs_mnttab_mtime.tv_sec);
if (!changed) {
*phpp = &vfs_pollhd;
}
}
void
vfs_mono_time(timespec_t *ts)
{
static volatile hrtime_t hrt;
hrtime_t newhrt, oldhrt;
timespec_t newts;
gethrestime(&newts);
newhrt = ts2hrt(&newts);
for (;;) {
oldhrt = hrt;
if (newhrt <= hrt)
newhrt = hrt + 1;
if (atomic_cas_64((uint64_t *)&hrt, oldhrt, newhrt) == oldhrt)
break;
}
hrt2ts(newhrt, ts);
}
void
vfs_mnttab_modtimeupd()
{
hrtime_t oldhrt, newhrt;
ASSERT(RW_WRITE_HELD(&vfslist));
oldhrt = ts2hrt(&vfs_mnttab_mtime);
gethrestime(&vfs_mnttab_mtime);
newhrt = ts2hrt(&vfs_mnttab_mtime);
if (oldhrt == (hrtime_t)0)
vfs_mnttab_ctime = vfs_mnttab_mtime;
if (newhrt == oldhrt) {
newhrt++;
hrt2ts(newhrt, &vfs_mnttab_mtime);
}
pollwakeup(&vfs_pollhd, (short)POLLRDBAND);
vfs_mnttab_writeop();
}
int
dounmount(struct vfs *vfsp, int flag, cred_t *cr)
{
vnode_t *coveredvp;
int error;
extern void teardown_vopstats(vfs_t *);
coveredvp = vfsp->vfs_vnodecovered;
ASSERT(coveredvp == NULL || vn_vfswlock_held(coveredvp));
(void) dnlc_purge_vfsp(vfsp, 0);
if ((flag & MS_FORCE) == 0)
(void) VFS_SYNC(vfsp, 0, cr);
vfs_lock_wait(vfsp);
if (error = VFS_UNMOUNT(vfsp, flag, cr)) {
vfs_unlock(vfsp);
if (coveredvp != NULL)
vn_vfsunlock(coveredvp);
} else if (coveredvp != NULL) {
teardown_vopstats(vfsp);
VN_HOLD(coveredvp);
vfs_remove(vfsp);
vn_vfsunlock(coveredvp);
VN_RELE(coveredvp);
} else {
teardown_vopstats(vfsp);
vfs_unlock(vfsp);
VFS_RELE(vfsp);
}
return (error);
}
void
vfs_unmountall(void)
{
struct vfs *vfsp;
struct vfs *prev_vfsp = NULL;
int error;
dnlc_purge();
vfs_list_lock();
for (vfsp = rootvfs->vfs_prev; vfsp != rootvfs; vfsp = prev_vfsp) {
prev_vfsp = vfsp->vfs_prev;
if (vfs_lock(vfsp) != 0)
continue;
error = vn_vfswlock(vfsp->vfs_vnodecovered);
vfs_unlock(vfsp);
if (error)
continue;
vfs_list_unlock();
(void) VFS_SYNC(vfsp, SYNC_CLOSE, CRED());
(void) dounmount(vfsp, 0, CRED());
vfs_list_lock();
for (vfsp = rootvfs->vfs_prev;
vfsp != rootvfs; vfsp = vfsp->vfs_prev)
if (vfsp == prev_vfsp)
break;
if (vfsp == rootvfs && prev_vfsp != rootvfs)
prev_vfsp = rootvfs->vfs_prev;
}
vfs_list_unlock();
}
void
vfs_addmip(dev_t dev, struct vfs *vfsp)
{
struct ipmnt *mipp;
mipp = (struct ipmnt *)kmem_alloc(sizeof (struct ipmnt), KM_SLEEP);
mipp->mip_next = NULL;
mipp->mip_dev = dev;
mipp->mip_vfsp = vfsp;
mutex_enter(&vfs_miplist_mutex);
if (vfs_miplist_end != NULL)
vfs_miplist_end->mip_next = mipp;
else
vfs_miplist = mipp;
vfs_miplist_end = mipp;
mutex_exit(&vfs_miplist_mutex);
}
void
vfs_delmip(struct vfs *vfsp)
{
struct ipmnt *mipp, *mipprev;
mutex_enter(&vfs_miplist_mutex);
mipprev = NULL;
for (mipp = vfs_miplist;
mipp && mipp->mip_vfsp != vfsp; mipp = mipp->mip_next) {
mipprev = mipp;
}
if (mipp == NULL)
return;
if (mipp == vfs_miplist_end)
vfs_miplist_end = mipprev;
if (mipprev == NULL)
vfs_miplist = mipp->mip_next;
else
mipprev->mip_next = mipp->mip_next;
mutex_exit(&vfs_miplist_mutex);
kmem_free(mipp, sizeof (struct ipmnt));
}
void
vfs_add(vnode_t *coveredvp, struct vfs *vfsp, int mflag)
{
int newflag;
ASSERT(vfs_lock_held(vfsp));
VFS_HOLD(vfsp);
newflag = vfsp->vfs_flag;
if (mflag & MS_RDONLY)
newflag |= VFS_RDONLY;
else
newflag &= ~VFS_RDONLY;
if (mflag & MS_NOSUID)
newflag |= (VFS_NOSETUID|VFS_NODEVICES);
else
newflag &= ~(VFS_NOSETUID|VFS_NODEVICES);
if (mflag & MS_NOMNTTAB)
newflag |= VFS_NOMNTTAB;
else
newflag &= ~VFS_NOMNTTAB;
if (coveredvp != NULL) {
ASSERT(vn_vfswlock_held(coveredvp));
coveredvp->v_vfsmountedhere = vfsp;
VN_HOLD(coveredvp);
}
vfsp->vfs_vnodecovered = coveredvp;
vfsp->vfs_flag = newflag;
vfs_list_add(vfsp);
}
void
vfs_remove(struct vfs *vfsp)
{
vnode_t *vp;
ASSERT(vfs_lock_held(vfsp));
if (vfsp == rootvfs)
panic("vfs_remove: unmounting root");
vfs_list_remove(vfsp);
vp = vfsp->vfs_vnodecovered;
ASSERT(vn_vfswlock_held(vp));
vp->v_vfsmountedhere = NULL;
vfsp->vfs_vnodecovered = NULL;
VN_RELE(vp);
vfs_unlock(vfsp);
VFS_RELE(vfsp);
}
int
vfs_lock(vfs_t *vfsp)
{
vn_vfslocks_entry_t *vpvfsentry;
vpvfsentry = vn_vfslocks_getlock(vfsp);
if (rwst_tryenter(&vpvfsentry->ve_lock, RW_WRITER))
return (0);
vn_vfslocks_rele(vpvfsentry);
return (EBUSY);
}
int
vfs_rlock(vfs_t *vfsp)
{
vn_vfslocks_entry_t *vpvfsentry;
vpvfsentry = vn_vfslocks_getlock(vfsp);
if (rwst_tryenter(&vpvfsentry->ve_lock, RW_READER))
return (0);
vn_vfslocks_rele(vpvfsentry);
return (EBUSY);
}
void
vfs_lock_wait(vfs_t *vfsp)
{
vn_vfslocks_entry_t *vpvfsentry;
vpvfsentry = vn_vfslocks_getlock(vfsp);
rwst_enter(&vpvfsentry->ve_lock, RW_WRITER);
}
void
vfs_rlock_wait(vfs_t *vfsp)
{
vn_vfslocks_entry_t *vpvfsentry;
vpvfsentry = vn_vfslocks_getlock(vfsp);
rwst_enter(&vpvfsentry->ve_lock, RW_READER);
}
void
vfs_unlock(vfs_t *vfsp)
{
vn_vfslocks_entry_t *vpvfsentry;
if (panicstr)
return;
vpvfsentry = vn_vfslocks_getlock(vfsp);
vn_vfslocks_rele(vpvfsentry);
rwst_exit(&vpvfsentry->ve_lock);
vn_vfslocks_rele(vpvfsentry);
}
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;
vn_vfslocks_entry_t *vpvfsentry;
if (panicstr)
return (1);
vpvfsentry = vn_vfslocks_getlock(vfsp);
held = rwst_lock_held(&vpvfsentry->ve_lock, RW_WRITER);
vn_vfslocks_rele(vpvfsentry);
return (held);
}
struct _kthread *
vfs_lock_owner(vfs_t *vfsp)
{
struct _kthread *owner;
vn_vfslocks_entry_t *vpvfsentry;
if (panicstr)
return (NULL);
vpvfsentry = vn_vfslocks_getlock(vfsp);
owner = rwst_owner(&vpvfsentry->ve_lock);
vn_vfslocks_rele(vpvfsentry);
return (owner);
}
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);
}
static void
vfs_hash_add(struct vfs *vfsp, int insert_at_head)
{
int vhno;
struct vfs **hp;
dev_t dev;
ASSERT(RW_WRITE_HELD(&vfslist));
dev = expldev(vfsp->vfs_fsid.val[0]);
vhno = VFSHASH(getmajor(dev), getminor(dev));
mutex_enter(&rvfs_list[vhno].rvfs_lock);
if (insert_at_head) {
vfsp->vfs_hash = rvfs_list[vhno].rvfs_head;
rvfs_list[vhno].rvfs_head = vfsp;
} else {
for (hp = &rvfs_list[vhno].rvfs_head; *hp != NULL;
hp = &(*hp)->vfs_hash)
continue;
vfsp->vfs_hash = NULL;
*hp = vfsp;
}
rvfs_list[vhno].rvfs_len++;
mutex_exit(&rvfs_list[vhno].rvfs_lock);
}
static void
vfs_hash_remove(struct vfs *vfsp)
{
int vhno;
struct vfs *tvfsp;
dev_t dev;
ASSERT(RW_WRITE_HELD(&vfslist));
dev = expldev(vfsp->vfs_fsid.val[0]);
vhno = VFSHASH(getmajor(dev), getminor(dev));
mutex_enter(&rvfs_list[vhno].rvfs_lock);
if (rvfs_list[vhno].rvfs_head == vfsp) {
rvfs_list[vhno].rvfs_head = vfsp->vfs_hash;
rvfs_list[vhno].rvfs_len--;
goto foundit;
}
for (tvfsp = rvfs_list[vhno].rvfs_head; tvfsp != NULL;
tvfsp = tvfsp->vfs_hash) {
if (tvfsp->vfs_hash == vfsp) {
tvfsp->vfs_hash = vfsp->vfs_hash;
rvfs_list[vhno].rvfs_len--;
goto foundit;
}
}
cmn_err(CE_WARN, "vfs_list_remove: vfs not found in hash");
foundit:
mutex_exit(&rvfs_list[vhno].rvfs_lock);
}
void
vfs_list_add(struct vfs *vfsp)
{
zone_t *zone;
if (vfsp->vfs_implp == NULL)
vfsimpl_setup(vfsp);
vfs_mono_time(&vfsp->vfs_hrctime);
vfsp->vfs_zone = curproc->p_zone;
zone_init_ref(&vfsp->vfs_implp->vi_zone_ref);
zone_hold_ref(vfsp->vfs_zone, &vfsp->vfs_implp->vi_zone_ref,
ZONE_REF_VFS);
zone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
ASSERT(zone != NULL);
vfs_list_lock();
if (vfsp == &root) {
ASSERT(rootvfs == vfsp);
ASSERT(zone == global_zone);
ASSERT(zone->zone_vfslist == NULL);
zone->zone_vfslist = vfsp;
} else {
rootvfs->vfs_prev->vfs_next = vfsp;
vfsp->vfs_prev = rootvfs->vfs_prev;
rootvfs->vfs_prev = vfsp;
vfsp->vfs_next = rootvfs;
if (zone->zone_vfslist == NULL) {
ASSERT(zone != global_zone);
zone->zone_vfslist = vfsp;
} else {
zone->zone_vfslist->vfs_zone_prev->vfs_zone_next = vfsp;
vfsp->vfs_zone_prev = zone->zone_vfslist->vfs_zone_prev;
zone->zone_vfslist->vfs_zone_prev = vfsp;
vfsp->vfs_zone_next = zone->zone_vfslist;
}
}
vfs_hash_add(vfsp, 0);
vfs_mnttab_modtimeupd();
vfs_list_unlock();
zone_rele(zone);
}
void
vfs_list_remove(struct vfs *vfsp)
{
zone_t *zone;
zone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
ASSERT(zone != NULL);
ASSERT(vfsp != rootvfs);
vfs_list_lock();
vfs_hash_remove(vfsp);
vfsp->vfs_prev->vfs_next = vfsp->vfs_next;
vfsp->vfs_next->vfs_prev = vfsp->vfs_prev;
vfsp->vfs_next = vfsp->vfs_prev = NULL;
if (zone->zone_vfslist == vfsp)
zone->zone_vfslist = vfsp->vfs_zone_next;
if (vfsp->vfs_zone_next == vfsp) {
ASSERT(vfsp->vfs_zone_prev == vfsp);
ASSERT(zone->zone_vfslist == vfsp);
zone->zone_vfslist = NULL;
}
vfsp->vfs_zone_prev->vfs_zone_next = vfsp->vfs_zone_next;
vfsp->vfs_zone_next->vfs_zone_prev = vfsp->vfs_zone_prev;
vfsp->vfs_zone_next = vfsp->vfs_zone_prev = NULL;
vfs_mnttab_modtimeupd();
vfs_list_unlock();
zone_rele(zone);
}
struct vfs *
getvfs(fsid_t *fsid)
{
struct vfs *vfsp;
int val0 = fsid->val[0];
int val1 = fsid->val[1];
dev_t dev = expldev(val0);
int vhno = VFSHASH(getmajor(dev), getminor(dev));
kmutex_t *hmp = &rvfs_list[vhno].rvfs_lock;
mutex_enter(hmp);
for (vfsp = rvfs_list[vhno].rvfs_head; vfsp; vfsp = vfsp->vfs_hash) {
if (vfsp->vfs_fsid.val[0] == val0 &&
vfsp->vfs_fsid.val[1] == val1) {
VFS_HOLD(vfsp);
mutex_exit(hmp);
return (vfsp);
}
}
mutex_exit(hmp);
return (NULL);
}
int
vfs_devmounting(dev_t dev, struct vfs *vfsp)
{
int retval = 0;
struct ipmnt *mipp;
mutex_enter(&vfs_miplist_mutex);
for (mipp = vfs_miplist; mipp != NULL; mipp = mipp->mip_next) {
if (mipp->mip_dev == dev) {
if (mipp->mip_vfsp != vfsp)
retval = 1;
break;
}
}
mutex_exit(&vfs_miplist_mutex);
return (retval);
}
int
vfs_devismounted(dev_t dev)
{
struct vfs *vfsp;
int found;
vfs_list_read_lock();
vfsp = rootvfs;
found = 0;
do {
if (vfsp->vfs_dev == dev) {
found = 1;
break;
}
vfsp = vfsp->vfs_next;
} while (vfsp != rootvfs);
vfs_list_unlock();
return (found);
}
struct vfs *
vfs_dev2vfsp(dev_t dev)
{
struct vfs *vfsp;
int found;
vfs_list_read_lock();
vfsp = rootvfs;
found = 0;
do {
if (vfsp->vfs_dev == dev &&
ZONE_PATH_VISIBLE(refstr_value(vfsp->vfs_mntpt),
curproc->p_zone)) {
VFS_HOLD(vfsp);
found = 1;
break;
}
vfsp = vfsp->vfs_next;
} while (vfsp != rootvfs);
vfs_list_unlock();
return (found ? vfsp: NULL);
}
struct vfs *
vfs_mntpoint2vfsp(const char *mp)
{
struct vfs *vfsp;
struct vfs *retvfsp = NULL;
zone_t *zone = curproc->p_zone;
struct vfs *list;
vfs_list_read_lock();
if (getzoneid() == GLOBAL_ZONEID) {
vfsp = rootvfs->vfs_prev;
do {
if (strcmp(refstr_value(vfsp->vfs_mntpt), mp) == 0) {
retvfsp = vfsp;
break;
}
vfsp = vfsp->vfs_prev;
} while (vfsp != rootvfs->vfs_prev);
} else if ((list = zone->zone_vfslist) != NULL) {
const char *mntpt;
vfsp = list->vfs_zone_prev;
do {
mntpt = refstr_value(vfsp->vfs_mntpt);
mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
if (strcmp(mntpt, mp) == 0) {
retvfsp = vfsp;
break;
}
vfsp = vfsp->vfs_zone_prev;
} while (vfsp != list->vfs_zone_prev);
}
if (retvfsp)
VFS_HOLD(retvfsp);
vfs_list_unlock();
return (retvfsp);
}
int
vfs_opsinuse(vfsops_t *ops)
{
struct vfs *vfsp;
int found;
vfs_list_read_lock();
vfsp = rootvfs;
found = 0;
do {
if (vfs_getops(vfsp) == ops) {
found = 1;
break;
}
vfsp = vfsp->vfs_next;
} while (vfsp != rootvfs);
vfs_list_unlock();
return (found);
}
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);
}
static const char *
vfs_to_modname(const char *vfstype)
{
if (strcmp(vfstype, "proc") == 0) {
vfstype = "procfs";
} else if (strcmp(vfstype, "fd") == 0) {
vfstype = "fdfs";
} else if (strncmp(vfstype, "nfs", 3) == 0) {
vfstype = "nfs";
}
return (vfstype);
}
struct vfssw *
vfs_getvfssw(const char *type)
{
struct vfssw *vswp;
const char *modname;
RLOCK_VFSSW();
vswp = vfs_getvfsswbyname(type);
modname = vfs_to_modname(type);
if (rootdir == NULL) {
if (vswp == NULL) {
RUNLOCK_VFSSW();
WLOCK_VFSSW();
if ((vswp = vfs_getvfsswbyname(type)) == NULL) {
if ((vswp = allocate_vfssw(type)) == NULL) {
WUNLOCK_VFSSW();
return (NULL);
}
}
WUNLOCK_VFSSW();
RLOCK_VFSSW();
}
if (!VFS_INSTALLED(vswp)) {
RUNLOCK_VFSSW();
(void) modloadonly("fs", modname);
} else
RUNLOCK_VFSSW();
return (vswp);
}
while (vswp == NULL || !VFS_INSTALLED(vswp)) {
RUNLOCK_VFSSW();
if (modload("fs", modname) == -1)
return (NULL);
RLOCK_VFSSW();
if (vswp == NULL)
if ((vswp = vfs_getvfsswbyname(type)) == NULL)
break;
}
RUNLOCK_VFSSW();
return (vswp);
}
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);
}
struct vfssw *
vfs_getvfsswbyvfsops(vfsops_t *vfsops)
{
struct vfssw *vswp;
RLOCK_VFSSW();
for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
if (ALLOCATED_VFSSW(vswp) && &vswp->vsw_vfsops == vfsops) {
vfs_refvfssw(vswp);
RUNLOCK_VFSSW();
return (vswp);
}
}
RUNLOCK_VFSSW();
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);
}
static int sync_retries = 20;
static int sync_triesleft;
static pgcnt_t old_pgcnt, new_pgcnt;
static int new_bufcnt, old_bufcnt;
void
vfs_syncall(void)
{
if (rootdir == NULL && !modrootloaded)
return;
printf("syncing file systems...");
sync();
sync_triesleft = sync_retries;
old_bufcnt = new_bufcnt = INT_MAX;
old_pgcnt = new_pgcnt = ULONG_MAX;
while (sync_triesleft > 0) {
old_bufcnt = MIN(old_bufcnt, new_bufcnt);
old_pgcnt = MIN(old_pgcnt, new_pgcnt);
new_bufcnt = bio_busy(B_TRUE);
new_pgcnt = page_busy(B_TRUE);
if (new_bufcnt == 0 && new_pgcnt == 0)
break;
if (new_bufcnt < old_bufcnt || new_pgcnt < old_pgcnt)
sync_triesleft = sync_retries;
else
sync_triesleft--;
if (new_bufcnt)
printf(" [%d]", new_bufcnt);
if (new_pgcnt)
printf(" %lu", new_pgcnt);
delay(hz);
}
if (new_bufcnt != 0 || new_pgcnt != 0)
printf(" done (not all i/o completed)\n");
else
printf(" done\n");
delay(hz);
}
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);
}
int
vfsstray_sync(struct vfs *vfsp, short arg, struct cred *cr)
{
cmn_err(CE_PANIC, "stray vfs operation");
return (0);
}
int
vfsstray(void)
{
cmn_err(CE_PANIC, "stray vfs operation");
return (0);
}
int
vfs_EIO(void)
{
return (EIO);
}
int
vfs_EIO_sync(struct vfs *vfsp, short arg, struct cred *cr)
{
return (EIO);
}
vfs_t EIO_vfs;
vfsops_t *EIO_vfsops;
void
vfsinit(void)
{
struct vfssw *vswp;
int error;
extern int vopstats_enabled;
extern void vopstats_startup();
static const fs_operation_def_t EIO_vfsops_template[] = {
VFSNAME_MOUNT, { .error = vfs_EIO },
VFSNAME_UNMOUNT, { .error = vfs_EIO },
VFSNAME_ROOT, { .error = vfs_EIO },
VFSNAME_STATVFS, { .error = vfs_EIO },
VFSNAME_SYNC, { .vfs_sync = vfs_EIO_sync },
VFSNAME_VGET, { .error = vfs_EIO },
VFSNAME_MOUNTROOT, { .error = vfs_EIO },
VFSNAME_FREEVFS, { .error = vfs_EIO },
VFSNAME_VNSTATE, { .error = vfs_EIO },
VFSNAME_SYNCFS, { .error = vfs_EIO },
NULL, NULL
};
static const fs_operation_def_t stray_vfsops_template[] = {
VFSNAME_MOUNT, { .error = vfsstray },
VFSNAME_UNMOUNT, { .error = vfsstray },
VFSNAME_ROOT, { .error = vfsstray },
VFSNAME_STATVFS, { .error = vfsstray },
VFSNAME_SYNC, { .vfs_sync = vfsstray_sync },
VFSNAME_VGET, { .error = vfsstray },
VFSNAME_MOUNTROOT, { .error = vfsstray },
VFSNAME_FREEVFS, { .error = vfsstray },
VFSNAME_VNSTATE, { .error = vfsstray },
VFSNAME_SYNCFS, { .error = vfsstray },
NULL, NULL
};
vfs_cache = kmem_cache_create("vfs_cache", sizeof (struct vfs),
sizeof (uintptr_t), NULL, NULL, NULL, NULL, NULL, 0);
vn_create_cache();
fem_init();
error = vfs_setfsops(0, stray_vfsops_template, NULL);
error = vfs_makefsops(EIO_vfsops_template, &EIO_vfsops);
if (error != 0) {
cmn_err(CE_WARN, "vfsinit: bad EIO vfs ops template");
}
VFS_INIT(&EIO_vfs, EIO_vfsops, (caddr_t)NULL);
EIO_vfs.vfs_flag |= VFS_UNMOUNTED;
for (vswp = &vfssw[1]; vswp < &vfssw[nfstype]; vswp++) {
RLOCK_VFSSW();
if (vswp->vsw_init != NULL)
(void) (*vswp->vsw_init)(vswp - vfssw, vswp->vsw_name);
RUNLOCK_VFSSW();
}
vopstats_startup();
if (vopstats_enabled) {
initialize_vopstats(&EIO_vfs.vfs_vopstats);
EIO_vfs.vfs_fstypevsp = NULL;
EIO_vfs.vfs_vskap = NULL;
EIO_vfs.vfs_flag |= VFS_STATS;
}
xattr_init();
reparse_point_init();
}
vfs_t *
vfs_alloc(int kmflag)
{
vfs_t *vfsp;
vfsp = kmem_cache_alloc(vfs_cache, kmflag);
bzero(vfsp, sizeof (vfs_t));
return (vfsp);
}
void
vfs_free(vfs_t *vfsp)
{
if (vfsp->vfs_femhead) {
ASSERT(vfsp->vfs_femhead->femh_list == NULL);
mutex_destroy(&vfsp->vfs_femhead->femh_lock);
kmem_free(vfsp->vfs_femhead, sizeof (*(vfsp->vfs_femhead)));
vfsp->vfs_femhead = NULL;
}
if (vfsp->vfs_implp)
vfsimpl_teardown(vfsp);
sema_destroy(&vfsp->vfs_reflock);
kmem_cache_free(vfs_cache, vfsp);
}
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);
lofi_remove(vfsp);
if (vfsp->vfs_zone)
zone_rele_ref(&vfsp->vfs_implp->vi_zone_ref,
ZONE_REF_VFS);
vfs_freemnttab(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);
}
#ifdef __sparc
void
vfs_root_redev(vfs_t *vfsp, dev_t ndev, int fstype)
{
vfs_list_lock();
vfs_hash_remove(vfsp);
vfsp->vfs_dev = ndev;
vfs_make_fsid(&vfsp->vfs_fsid, ndev, fstype);
vfs_hash_add(vfsp, 1);
vfs_list_unlock();
}
#else
#if defined(__x86)
extern int hvmboot_rootconf();
#endif
extern ib_boot_prop_t *iscsiboot_prop;
int
rootconf()
{
int error;
struct vfssw *vsw;
extern void pm_init();
char *fstyp, *fsmod;
int ret = -1;
getrootfs(&fstyp, &fsmod);
#if defined(__x86)
if (error = hvmboot_rootconf())
return (error);
#endif
if (error = clboot_rootconf())
return (error);
if (modload("fs", fsmod) == -1)
panic("Cannot _init %s module", fsmod);
RLOCK_VFSSW();
vsw = vfs_getvfsswbyname(fstyp);
RUNLOCK_VFSSW();
if (vsw == NULL) {
cmn_err(CE_CONT, "Cannot find %s filesystem\n", fstyp);
return (ENXIO);
}
VFS_INIT(rootvfs, &vsw->vsw_vfsops, 0);
VFS_HOLD(rootvfs);
rootvfs->vfs_flag |= VFS_RDONLY;
pm_init();
if (netboot && iscsiboot_prop) {
cmn_err(CE_WARN, "NFS boot and iSCSI boot"
" shouldn't happen in the same time");
return (EINVAL);
}
if (netboot || iscsiboot_prop) {
ret = strplumb();
if (ret != 0) {
cmn_err(CE_WARN, "Cannot plumb network device %d", ret);
return (EFAULT);
}
}
if ((ret == 0) && iscsiboot_prop) {
ret = modload("drv", "iscsi");
if (ret == -1) {
cmn_err(CE_WARN, "Failed to load iscsi module");
iscsi_boot_prop_free();
return (EINVAL);
} else {
if (!i_ddi_attach_pseudo_node("iscsi")) {
cmn_err(CE_WARN,
"Failed to attach iscsi driver");
iscsi_boot_prop_free();
return (ENODEV);
}
}
}
error = VFS_MOUNTROOT(rootvfs, ROOT_INIT);
vfs_unrefvfssw(vsw);
rootdev = rootvfs->vfs_dev;
if (error)
cmn_err(CE_CONT, "Cannot mount root on %s fstype %s\n",
rootfs.bo_name, fstyp);
else
cmn_err(CE_CONT, "?root on %s fstype %s\n",
rootfs.bo_name, fstyp);
return (error);
}
void
getfsname(char *askfor, char *name, size_t namelen)
{
if (boothowto & RB_ASKNAME) {
printf("%s name: ", askfor);
console_gets(name, namelen);
}
}
static void
getrootfs(char **fstypp, char **fsmodp)
{
char *propstr = NULL;
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
DDI_PROP_DONTPASS, "fstype", &propstr)
== DDI_SUCCESS) {
(void) strncpy(rootfs.bo_fstype, propstr, BO_MAXFSNAME);
ddi_prop_free(propstr);
} else if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
DDI_PROP_DONTPASS, "zfs-bootfs", &propstr)
== DDI_SUCCESS) {
(void) strncpy(rootfs.bo_fstype, "zfs", BO_MAXFSNAME);
ddi_prop_free(propstr);
}
if (strncmp(rootfs.bo_fstype, "nfs", 3) != 0) {
*fstypp = *fsmodp = rootfs.bo_fstype;
return;
}
++netboot;
if (strcmp(rootfs.bo_fstype, "nfs2") == 0)
(void) strcpy(rootfs.bo_fstype, "nfs");
else if (strcmp(rootfs.bo_fstype, "nfs") == 0)
(void) strcpy(rootfs.bo_fstype, "nfsdyn");
if (ddi_prop_exists(DDI_DEV_T_ANY, ddi_root_node(), DDI_PROP_DONTPASS,
"xpv-nfsroot")) {
(void) strcpy(rootfs.bo_name, "/xpvd/xnf@0");
} else if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
DDI_PROP_DONTPASS, "bootpath", &propstr)
== DDI_SUCCESS) {
(void) strncpy(rootfs.bo_name, propstr, BO_MAXOBJNAME);
ddi_prop_free(propstr);
} else {
rootfs.bo_name[0] = '\0';
}
*fstypp = rootfs.bo_fstype;
*fsmodp = "nfs";
}
#endif
#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);
}
void
vfs_propagate_features(vfs_t *from, vfs_t *to)
{
int i;
if (to->vfs_implp == NULL || from->vfs_implp == NULL)
return;
for (i = 1; i <= to->vfs_featureset[0]; i++) {
to->vfs_featureset[i] = from->vfs_featureset[i];
}
}
#define LOFINODE_PATH "/dev/lofi/%d"
int
vfs_get_lofi(vfs_t *vfsp, vnode_t **vpp)
{
char *path = NULL;
int strsize;
int err;
if (vfsp->vfs_lofi_id == 0) {
*vpp = NULL;
return (-1);
}
strsize = snprintf(NULL, 0, LOFINODE_PATH, vfsp->vfs_lofi_id);
path = kmem_alloc(strsize + 1, KM_SLEEP);
(void) snprintf(path, strsize + 1, LOFINODE_PATH, vfsp->vfs_lofi_id);
for (;;) {
err = lookupname(path, UIO_SYSSPACE, FOLLOW, NULLVPP, vpp);
if (err != ENOENT)
break;
if ((err = delay_sig(hz / 8)) == EINTR)
break;
}
if (err)
*vpp = NULL;
kmem_free(path, strsize + 1);
return (err);
}