#pragma ident "@(#)auto_vfsops.c 1.58 06/03/24 SMI"
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <mach/machine/vm_types.h>
#include <sys/vnode.h>
#include <sys/vfs_context.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/attr.h>
#include <sys/sysctl.h>
#include <sys/conf.h>
#include <miscfs/devfs/devfs.h>
#include <kern/locks.h>
#include <kern/assert.h>
#include <IOKit/IOLib.h>
#include "autofs.h"
#include "triggers.h"
#include "triggers_priv.h"
#include "autofs_kern.h"
lck_grp_t *autofs_lck_grp;
static lck_mtx_t *autofs_global_lock;
lck_mtx_t *autofs_nodeid_lock;
static u_int autofs_mounts;
__private_extern__ int auto_module_start(kmod_info_t *, void *);
__private_extern__ int auto_module_stop(kmod_info_t *, void *);
static int auto_mount(mount_t, vnode_t, user_addr_t, vfs_context_t);
static int auto_start(mount_t, int, vfs_context_t);
static int auto_unmount(mount_t, int, vfs_context_t);
static int auto_vfs_getattr(mount_t, struct vfs_attr *, vfs_context_t);
static int auto_sync(mount_t, int, vfs_context_t);
static int auto_vget(mount_t, ino64_t, vnode_t *, vfs_context_t);
static int autofs_sysctl(int *, u_int, user_addr_t, size_t *, user_addr_t, size_t, vfs_context_t);
static struct vfsops autofs_vfsops = {
auto_mount,
auto_start,
auto_unmount,
auto_root,
NULL,
auto_vfs_getattr,
auto_sync,
auto_vget,
NULL,
NULL,
NULL,
autofs_sysctl,
NULL,
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
extern struct vnodeopv_desc autofsfs_vnodeop_opv_desc;
struct vnodeopv_desc * autofs_vnodeop_opv_descs[] =
{&autofsfs_vnodeop_opv_desc, NULL};
struct vfs_fsentry autofs_fsentry = {
&autofs_vfsops,
1,
autofs_vnodeop_opv_descs,
0,
MNTTYPE_AUTOFS,
VFS_TBLTHREADSAFE | VFS_TBLNOTYPENUM | VFS_TBL64BITREADY | VFS_TBLNATIVEXATTR,
{ NULL, NULL }
};
static vfstable_t auto_vfsconf;
static struct autofs_globals *global_fngp;
static struct autofs_globals *
autofs_zone_get_globals(void)
{
return (global_fngp);
}
static void
autofs_zone_set_globals(struct autofs_globals *fngp)
{
global_fngp = fngp;
}
static struct autofs_globals *
autofs_zone_init(void)
{
struct autofs_globals *fngp = NULL;
fnnode_t *fnp = NULL;
static const char fnnode_name[] = "root_fnnode";
struct timeval now;
MALLOC(fngp, struct autofs_globals *, sizeof (*fngp), M_AUTOFS,
M_WAITOK);
if (fngp == NULL) {
IOLog("autofs_zone_init: Couldn't create autofs globals structure\n");
goto fail;
}
bzero(fngp, sizeof (*fngp));
MALLOC(fnp, fnnode_t *, sizeof(fnnode_t), M_AUTOFS, M_WAITOK);
if (fnp == NULL) {
IOLog("autofs_zone_init: Couldn't create autofs global fnnode\n");
goto fail;
}
bzero(fnp, sizeof(*fnp));
fnp->fn_namelen = sizeof fnnode_name;
MALLOC(fnp->fn_name, char *, fnp->fn_namelen + 1, M_AUTOFS, M_WAITOK);
if (fnp->fn_name == NULL) {
IOLog("autofs_zone_init: Couldn't create autofs global fnnode name\n");
goto fail;
}
bcopy(fnnode_name, fnp->fn_name, sizeof fnnode_name);
fnp->fn_name[sizeof fnnode_name] = '\0';
fnp->fn_mode = AUTOFS_MODE;
microtime(&now);
fnp->fn_crtime = fnp->fn_atime = fnp->fn_mtime = fnp->fn_ctime = now;
fnp->fn_nodeid = 1;
fnp->fn_globals = fngp;
fnp->fn_lock = lck_mtx_alloc_init(autofs_lck_grp, NULL);
if (fnp->fn_lock == NULL) {
IOLog("autofs_zone_init: Couldn't create autofs global fnnode mutex\n");
goto fail;
}
fnp->fn_rwlock = lck_rw_alloc_init(autofs_lck_grp, NULL);
if (fnp->fn_rwlock == NULL) {
IOLog("autofs_zone_init: Couldn't create autofs global fnnode rwlock\n");
goto fail;
}
fngp->fng_rootfnnodep = fnp;
fngp->fng_fnnode_count = 1;
fngp->fng_printed_not_running_msg = 0;
fngp->fng_flush_notification_lock = lck_mtx_alloc_init(autofs_lck_grp,
LCK_ATTR_NULL);
if (fngp->fng_flush_notification_lock == NULL) {
IOLog("autofs_zone_init: Couldn't create autofs global flush notification lock\n");
goto fail;
}
return (fngp);
fail:
if (fnp != NULL) {
if (fnp->fn_rwlock != NULL)
lck_rw_free(fnp->fn_rwlock, autofs_lck_grp);
if (fnp->fn_lock != NULL)
lck_mtx_free(fnp->fn_lock, autofs_lck_grp);
if (fnp->fn_name != NULL)
FREE(fnp->fn_name, M_AUTOFS);
FREE(fnp, M_AUTOFS);
}
if (fngp != NULL) {
if (fngp->fng_flush_notification_lock != NULL)
lck_mtx_free(fngp->fng_flush_notification_lock,
autofs_lck_grp);
FREE(fngp, M_AUTOFS);
}
return (NULL);
}
static char *
strstr(const char *s, const char *find)
{
char c, sc;
size_t len;
if ((c = *find++) != 0) {
len = strlen(find);
do {
do {
if ((sc = *s++) == 0)
return (NULL);
} while (sc != c);
} while (strncmp(s, find, len) != 0);
s--;
}
return ((char *)s);
}
static const struct mntopt restropts[] = {
RESTRICTED_MNTOPTS
};
#define NROPTS ((sizeof (restropts)/sizeof (restropts[0])) - 1)
static uint64_t
optionisset(mount_t mp, const struct mntopt *opt)
{
uint64_t flags;
flags = vfs_flags(mp) & opt->m_flag;
if (opt->m_inverse)
return !flags;
else
return flags;
}
static int
autofs_restrict_opts(mount_t mp, char *buf, size_t maxlen, size_t *curlen)
{
fninfo_t *fnip = vfstofni(mp);
u_int i;
char *p;
size_t len = *curlen - 1;
if (!(fnip->fi_mntflags & AUTOFS_MNT_RESTRICT))
return (0);
for (i = 0; i < NROPTS; i++) {
size_t olen = strlen(restropts[i].m_option);
if ((i == 0 || optionisset(mp, &restropts[i])) &&
((p = strstr(buf, restropts[i].m_option)) == NULL ||
!((p == buf || p[-1] == ',') &&
(p[olen] == '\0' || p[olen] == ',')))) {
if (len + olen + 1 > maxlen)
return (-1);
if (*buf != '\0')
buf[len++] = ',';
bcopy(restropts[i].m_option, &buf[len], olen);
len += olen;
}
}
if (len + 1 > maxlen)
return (-1);
buf[len++] = '\0';
*curlen = len;
return (0);
}
static int
auto_mount(mount_t mp, __unused vnode_t devvp, user_addr_t data,
vfs_context_t context)
{
int error;
size_t len;
uint32_t argsvers;
struct autofs_args_64 args;
fninfo_t *fnip = NULL;
vnode_t myrootvp = NULL;
fnnode_t *rootfnp = NULL;
char strbuff[PATH_MAX+1];
uint64_t flags;
#ifdef DEBUG
lck_attr_t *lckattr;
#endif
struct autofs_globals *fngp;
int node_type;
boolean_t lu_verbose;
AUTOFS_DPRINT((4, "auto_mount: mp %p devvp %p\n", mp, devvp));
lck_mtx_lock(autofs_global_lock);
if ((fngp = autofs_zone_get_globals()) == NULL) {
fngp = autofs_zone_init();
autofs_zone_set_globals(fngp);
}
lck_mtx_unlock(autofs_global_lock);
if (fngp == NULL)
return (ENOMEM);
error = copyin(data, (caddr_t)&argsvers, sizeof (argsvers));
if (error)
return (error);
switch (argsvers) {
case 2:
if (vfs_context_is64bit(context))
error = copyin(data, &args, sizeof (args));
else {
struct autofs_args_32 args32;
error = copyin(data, &args32, sizeof (args32));
if (error == 0) {
args.path = CAST_USER_ADDR_T(args32.path);
args.opts = CAST_USER_ADDR_T(args32.opts);
args.map = CAST_USER_ADDR_T(args32.map);
args.subdir = CAST_USER_ADDR_T(args32.subdir);
args.key = CAST_USER_ADDR_T(args32.key);
args.mntflags = args32.mntflags;
args.direct = args32.direct;
args.mount_type = args32.mount_type;
args.node_type = args32.node_type;
}
}
if (error)
return (error);
break;
default:
return (EINVAL);
}
flags = vfs_flags(mp) & MNT_CMDFLAGS;
if (flags & MNT_UPDATE)
return (EINVAL);
MALLOC(fnip, fninfo_t *, sizeof(*fnip), M_AUTOFS, M_WAITOK);
if (fnip == NULL)
return (ENOMEM);
bzero(fnip, sizeof(*fnip));
vfs_getnewfsid(mp);
vfs_setfsprivate(mp, fnip);
error = copyinstr(args.path, strbuff, sizeof (strbuff), &len);
if (error) {
error = EFAULT;
goto errout;
}
MALLOC(fnip->fi_path, char *, len, M_AUTOFS, M_WAITOK);
if (fnip->fi_path == NULL) {
error = ENOMEM;
goto errout;
}
fnip->fi_pathlen = (int)len;
bcopy(strbuff, fnip->fi_path, len);
error = copyinstr(args.opts, strbuff, sizeof (strbuff), &len);
if (error != 0)
goto errout;
if (autofs_restrict_opts(mp, strbuff, sizeof (strbuff), &len) != 0) {
error = EFAULT;
goto errout;
}
MALLOC(fnip->fi_opts, char *, len, M_AUTOFS, M_WAITOK);
if (fnip->fi_opts == NULL) {
error = ENOMEM;
goto errout;
}
fnip->fi_optslen = (int)len;
bcopy(strbuff, fnip->fi_opts, len);
error = copyinstr(args.map, strbuff, sizeof (strbuff), &len);
if (error)
goto errout;
MALLOC(fnip->fi_map, char *, len, M_AUTOFS, M_WAITOK);
if (fnip->fi_map == NULL) {
error = ENOMEM;
goto errout;
}
fnip->fi_maplen = (int)len;
bcopy(strbuff, fnip->fi_map, len);
switch (args.mount_type) {
case MOUNT_TYPE_MAP:
snprintf(vfs_statfs(mp)->f_mntfromname, sizeof(vfs_statfs(mp)->f_mntfromname),
"map %.*s", fnip->fi_maplen, fnip->fi_map);
break;
case MOUNT_TYPE_TRIGGERED_MAP:
snprintf(vfs_statfs(mp)->f_mntfromname, sizeof(vfs_statfs(mp)->f_mntfromname),
"triggered map %.*s", fnip->fi_maplen, fnip->fi_map);
break;
case MOUNT_TYPE_SUBTRIGGER:
strncpy(vfs_statfs(mp)->f_mntfromname, "subtrigger",
sizeof(vfs_statfs(mp)->f_mntfromname)-1);
vfs_statfs(mp)->f_mntfromname[sizeof(vfs_statfs(mp)->f_mntfromname)-1] = (char)0;
fnip->fi_flags |= MF_SUBTRIGGER;
break;
}
error = copyinstr(args.subdir, strbuff, sizeof (strbuff), &len);
if (error)
goto errout;
MALLOC(fnip->fi_subdir, char *, len, M_AUTOFS, M_WAITOK);
if (fnip->fi_subdir == NULL) {
error = ENOMEM;
goto errout;
}
fnip->fi_subdirlen = (int)len;
bcopy(strbuff, fnip->fi_subdir, len);
error = copyinstr(args.key, strbuff, sizeof (strbuff), &len);
if (error)
goto errout;
MALLOC(fnip->fi_key, char *, len, M_AUTOFS, M_WAITOK);
if (fnip->fi_key == NULL) {
error = ENOMEM;
goto errout;
}
fnip->fi_keylen = (int)len;
bcopy(strbuff, fnip->fi_key, len);
fnip->fi_mntflags = args.mntflags;
if (args.direct == 1)
fnip->fi_flags |= MF_DIRECT;
#ifdef DEBUG
lckattr = lck_attr_alloc_init();
lck_attr_setdebug(lckattr);
fnip->fi_rwlock = lck_rw_alloc_init(autofs_lck_grp, lckattr);
lck_attr_free(lckattr);
#else
fnip->fi_rwlock = lck_rw_alloc_init(autofs_lck_grp, NULL);
#endif
if (args.direct == 1) {
if (args.mount_type == MOUNT_TYPE_SUBTRIGGER)
node_type = NT_TRIGGER;
else {
error = auto_lookup_request(fnip, fnip->fi_key,
fnip->fi_keylen, fnip->fi_subdir,
context, &node_type, &lu_verbose);
if (error) {
node_type = NT_TRIGGER;
}
}
} else {
node_type = 0;
}
error = auto_makefnnode(&rootfnp, node_type, mp, NULL, "", NULL,
1, fngp);
if (error)
goto errout;
myrootvp = fntovn(rootfnp);
rootfnp->fn_mode = AUTOFS_MODE;
rootfnp->fn_parent = rootfnp;
rootfnp->fn_linkcnt = 1;
error = vnode_ref(myrootvp);
if (error) {
vnode_put(myrootvp);
vnode_recycle(myrootvp);
goto errout;
}
fnip->fi_rootvp = myrootvp;
if (args.mount_type == MOUNT_TYPE_MAP) {
lck_rw_lock_exclusive(fngp->fng_rootfnnodep->fn_rwlock);
rootfnp->fn_parent = fngp->fng_rootfnnodep;
rootfnp->fn_next = fngp->fng_rootfnnodep->fn_dirents;
fngp->fng_rootfnnodep->fn_dirents = rootfnp;
lck_rw_unlock_exclusive(fngp->fng_rootfnnodep->fn_rwlock);
}
lck_mtx_lock(autofs_global_lock);
autofs_mounts++;
lck_mtx_unlock(autofs_global_lock);
vnode_put(myrootvp);
AUTOFS_DPRINT((5, "auto_mount: mp %p root %p fnip %p return %d\n",
mp, myrootvp, fnip, error));
return (0);
errout:
assert(fnip != NULL);
if (fnip->fi_rwlock != NULL)
lck_rw_free(fnip->fi_rwlock, autofs_lck_grp);
if (fnip->fi_path != NULL)
FREE(fnip->fi_path, M_AUTOFS);
if (fnip->fi_opts != NULL)
FREE(fnip->fi_opts, M_AUTOFS);
if (fnip->fi_map != NULL)
FREE(fnip->fi_map, M_AUTOFS);
if (fnip->fi_subdir != NULL)
FREE(fnip->fi_subdir, M_AUTOFS);
if (fnip->fi_key != NULL)
FREE(fnip->fi_key, M_AUTOFS);
FREE(fnip, sizeof M_AUTOFS);
AUTOFS_DPRINT((5, "auto_mount: vfs %p root %p fnip %p return %d\n",
(void *)mp, (void *)myrootvp, (void *)fnip, error));
return (error);
}
static int
auto_update_options(struct autofs_update_args_64 *update_argsp)
{
mount_t mp;
fninfo_t *fnip;
fnnode_t *fnp;
char strbuff[PATH_MAX+1];
int error;
char *opts, *map;
size_t optslen, maplen;
int was_nobrowse;
mp = vfs_getvfs(&update_argsp->fsid);
if (mp == NULL)
return (ENOENT);
if (!auto_is_autofs(mp))
return (EINVAL);
fnip = vfstofni(mp);
if (fnip == NULL)
return (EINVAL);
if ((update_argsp->direct == 1 && !(fnip->fi_flags & MF_DIRECT)) ||
(update_argsp->direct != 1 && (fnip->fi_flags & MF_DIRECT))) {
fnp = vntofn(fnip->fi_rootvp);
if (fnp->fn_dirents != NULL)
return (EINVAL);
}
error = copyinstr(update_argsp->opts, strbuff, sizeof (strbuff),
&optslen);
if (error)
return (error);
if (autofs_restrict_opts(mp, strbuff, sizeof (strbuff), &optslen) != 0)
return (EFAULT);
MALLOC(opts, char *, optslen, M_AUTOFS, M_WAITOK);
bcopy(strbuff, opts, optslen);
error = copyinstr(update_argsp->map, strbuff, sizeof (strbuff),
&maplen);
if (error) {
FREE(opts, M_AUTOFS);
return (error);
}
MALLOC(map, char *, maplen, M_AUTOFS, M_WAITOK);
bcopy(strbuff, map, maplen);
lck_rw_lock_exclusive(fnip->fi_rwlock);
was_nobrowse = auto_nobrowse(fnip->fi_rootvp);
FREE(fnip->fi_opts, M_AUTOFS);
fnip->fi_opts = opts;
fnip->fi_optslen = (int)optslen;
FREE(fnip->fi_map, M_AUTOFS);
fnip->fi_map = map;
fnip->fi_maplen = (int)maplen;
if (update_argsp->direct == 1)
fnip->fi_flags |= MF_DIRECT;
else
fnip->fi_flags &= ~MF_DIRECT;
fnip->fi_flags &= ~MF_UNMOUNTING;
fnip->fi_mntflags = update_argsp->mntflags;
snprintf(vfs_statfs(mp)->f_mntfromname, sizeof(vfs_statfs(mp)->f_mntfromname),
"map %.*s", fnip->fi_maplen, fnip->fi_map);
lck_rw_unlock_exclusive(fnip->fi_rwlock);
if (vnode_ismonitored(fnip->fi_rootvp) &&
(!was_nobrowse || !auto_nobrowse(fnip->fi_rootvp))) {
struct vnode_attr vattr;
vfs_get_notify_attributes(&vattr);
auto_get_attributes(fnip->fi_rootvp, &vattr);
vnode_notify(fnip->fi_rootvp, VNODE_EVENT_WRITE, &vattr);
}
return (0);
}
int
auto_start(__unused mount_t mp, __unused int flags,
__unused vfs_context_t context)
{
return (0);
}
static int
auto_unmount(mount_t mp, int mntflags, __unused vfs_context_t context)
{
fninfo_t *fnip;
vnode_t rvp;
fnnode_t *rfnp, *fnp, *pfnp;
fnnode_t *myrootfnnodep;
int error;
fnip = vfstofni(mp);
AUTOFS_DPRINT((4, "auto_unmount mp %p fnip %p\n", (void *)mp,
(void *)fnip));
if (mntflags & MNT_FORCE)
return (ENOTSUP);
rvp = fnip->fi_rootvp;
rfnp = vntofn(rvp);
lck_rw_lock_exclusive(fnip->fi_rwlock);
error = vflush(mp, rvp, 0);
if (error) {
lck_rw_unlock_exclusive(fnip->fi_rwlock);
return (error);
}
if (vnode_isinuse(rvp, 1) || rfnp->fn_dirents != NULL) {
lck_rw_unlock_exclusive(fnip->fi_rwlock);
return (EBUSY);
}
myrootfnnodep = rfnp->fn_globals->fng_rootfnnodep;
pfnp = NULL;
lck_rw_lock_exclusive(myrootfnnodep->fn_rwlock);
fnp = myrootfnnodep->fn_dirents;
while (fnp != NULL) {
if (fnp == rfnp) {
if (vnode_isinuse(rvp, 1) || rfnp->fn_dirents != NULL) {
lck_rw_unlock_exclusive(myrootfnnodep->fn_rwlock);
lck_rw_unlock_exclusive(fnip->fi_rwlock);
return (EBUSY);
}
if (pfnp)
pfnp->fn_next = fnp->fn_next;
else
myrootfnnodep->fn_dirents = fnp->fn_next;
fnp->fn_next = NULL;
break;
}
pfnp = fnp;
fnp = fnp->fn_next;
}
lck_rw_unlock_exclusive(myrootfnnodep->fn_rwlock);
fnip->fi_flags |= MF_UNMOUNTING;
lck_rw_unlock_exclusive(fnip->fi_rwlock);
assert(vnode_isinuse(rvp, 0) && !vnode_isinuse(rvp, 1));
assert(rfnp->fn_direntcnt == 0);
assert(rfnp->fn_linkcnt == 1);
rfnp->fn_linkcnt--;
vnode_rele(rvp);
vflush(mp, NULLVP, 0);
lck_rw_free(fnip->fi_rwlock, autofs_lck_grp);
FREE(fnip->fi_path, M_AUTOFS);
FREE(fnip->fi_map, M_AUTOFS);
FREE(fnip->fi_subdir, M_AUTOFS);
FREE(fnip->fi_key, M_AUTOFS);
FREE(fnip->fi_opts, M_AUTOFS);
FREE(fnip, M_AUTOFS);
lck_mtx_lock(autofs_global_lock);
autofs_mounts--;
lck_mtx_unlock(autofs_global_lock);
AUTOFS_DPRINT((5, "auto_unmount: return=0\n"));
return (0);
}
int
auto_root(mount_t mp, vnode_t *vpp, __unused vfs_context_t context)
{
int error;
fninfo_t *fnip;
fnip = vfstofni(mp);
*vpp = fnip->fi_rootvp;
error = vnode_get(*vpp);
AUTOFS_DPRINT((5, "auto_root: mp %p, *vpp %p, error %d\n",
mp, *vpp, error));
return (error);
}
static int
auto_vfs_getattr(__unused mount_t mp, struct vfs_attr *vfap,
__unused vfs_context_t context)
{
AUTOFS_DPRINT((4, "auto_vfs_getattr %p\n", (void *)mp));
VFSATTR_RETURN(vfap, f_bsize, AUTOFS_BLOCKSIZE);
VFSATTR_RETURN(vfap, f_iosize, 512);
VFSATTR_RETURN(vfap, f_blocks, 0);
VFSATTR_RETURN(vfap, f_bfree, 0);
VFSATTR_RETURN(vfap, f_bavail, 0);
VFSATTR_RETURN(vfap, f_files, 0);
VFSATTR_RETURN(vfap, f_ffree, 0);
if (VFSATTR_IS_ACTIVE(vfap, f_capabilities)) {
vfap->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] =
VOL_CAP_FMT_SYMBOLICLINKS |
VOL_CAP_FMT_HARDLINKS |
VOL_CAP_FMT_NO_ROOT_TIMES |
VOL_CAP_FMT_CASE_SENSITIVE |
VOL_CAP_FMT_CASE_PRESERVING |
VOL_CAP_FMT_FAST_STATFS |
VOL_CAP_FMT_2TB_FILESIZE |
VOL_CAP_FMT_HIDDEN_FILES;
vfap->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] =
VOL_CAP_INT_ATTRLIST;
vfap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
vfap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
vfap->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] =
VOL_CAP_FMT_PERSISTENTOBJECTIDS |
VOL_CAP_FMT_SYMBOLICLINKS |
VOL_CAP_FMT_HARDLINKS |
VOL_CAP_FMT_JOURNAL |
VOL_CAP_FMT_JOURNAL_ACTIVE |
VOL_CAP_FMT_NO_ROOT_TIMES |
VOL_CAP_FMT_SPARSE_FILES |
VOL_CAP_FMT_ZERO_RUNS |
VOL_CAP_FMT_CASE_SENSITIVE |
VOL_CAP_FMT_CASE_PRESERVING |
VOL_CAP_FMT_FAST_STATFS |
VOL_CAP_FMT_2TB_FILESIZE |
VOL_CAP_FMT_OPENDENYMODES |
VOL_CAP_FMT_HIDDEN_FILES |
VOL_CAP_FMT_PATH_FROM_ID |
VOL_CAP_FMT_NO_VOLUME_SIZES;
vfap->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] =
VOL_CAP_INT_SEARCHFS |
VOL_CAP_INT_ATTRLIST |
VOL_CAP_INT_NFSEXPORT |
VOL_CAP_INT_READDIRATTR |
VOL_CAP_INT_EXCHANGEDATA |
VOL_CAP_INT_COPYFILE |
VOL_CAP_INT_ALLOCATE |
VOL_CAP_INT_VOL_RENAME |
VOL_CAP_INT_ADVLOCK |
VOL_CAP_INT_FLOCK |
VOL_CAP_INT_EXTENDED_SECURITY |
VOL_CAP_INT_USERACCESS |
VOL_CAP_INT_MANLOCK |
VOL_CAP_INT_EXTENDED_ATTR |
VOL_CAP_INT_NAMEDSTREAMS;
vfap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
vfap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
VFSATTR_SET_SUPPORTED(vfap, f_capabilities);
}
if (VFSATTR_IS_ACTIVE(vfap, f_attributes)) {
vfap->f_attributes.validattr.commonattr =
ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID |
ATTR_CMN_OBJTYPE | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |
ATTR_CMN_PAROBJID |
ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME |
ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK |
ATTR_CMN_FLAGS | ATTR_CMN_USERACCESS | ATTR_CMN_FILEID;
vfap->f_attributes.validattr.volattr =
ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |
ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |
ATTR_VOL_ATTRIBUTES;
vfap->f_attributes.validattr.dirattr =
ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS;
vfap->f_attributes.validattr.fileattr =
ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |
ATTR_FILE_IOBLOCKSIZE | ATTR_FILE_DEVTYPE |
ATTR_FILE_DATALENGTH;
vfap->f_attributes.validattr.forkattr = 0;
vfap->f_attributes.nativeattr.commonattr =
ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID |
ATTR_CMN_OBJTYPE | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |
ATTR_CMN_PAROBJID |
ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME |
ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK |
ATTR_CMN_FLAGS | ATTR_CMN_USERACCESS | ATTR_CMN_FILEID;
vfap->f_attributes.nativeattr.volattr =
ATTR_VOL_OBJCOUNT |
ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |
ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |
ATTR_VOL_ATTRIBUTES;
vfap->f_attributes.nativeattr.dirattr =
ATTR_DIR_MOUNTSTATUS;
vfap->f_attributes.nativeattr.fileattr =
ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |
ATTR_FILE_IOBLOCKSIZE | ATTR_FILE_DEVTYPE |
ATTR_FILE_DATALENGTH;
vfap->f_attributes.nativeattr.forkattr = 0;
VFSATTR_SET_SUPPORTED(vfap, f_attributes);
}
return (0);
}
static int
auto_sync(__unused mount_t mp, __unused int waitfor,
__unused vfs_context_t context)
{
return (0);
}
static int
auto_vget(__unused mount_t mp, __unused ino64_t ino, __unused vnode_t *vpp,
__unused vfs_context_t context)
{
return (ENOTSUP);
}
static int
autofs_sysctl(int *name, u_int namelen, __unused user_addr_t oldp,
__unused size_t *oldlenp, __unused user_addr_t newp,
__unused size_t newlen, __unused vfs_context_t context)
{
int error;
#ifdef DEBUG
struct sysctl_req *req = NULL;
uint32_t debug;
#endif
if (namelen > 1)
return ENOTDIR;
error = 0;
#ifdef DEBUG
req = CAST_DOWN(struct sysctl_req *, oldp);
#endif
AUTOFS_DPRINT((4, "autofs_sysctl called.\n"));
switch (name[0]) {
#ifdef DEBUG
case AUTOFS_CTL_DEBUG:
error = SYSCTL_IN(req, &debug, sizeof(debug));
if (error)
break;
auto_debug_set(debug);
break;
#endif
default:
error = ENOTSUP;
break;
}
return (error);
}
static d_open_t autofs_dev_open;
static d_close_t autofs_dev_close;
static d_ioctl_t autofs_ioctl;
static struct cdevsw autofs_cdevsw = {
autofs_dev_open,
autofs_dev_close,
eno_rdwrt,
eno_rdwrt,
autofs_ioctl,
eno_stop,
eno_reset,
0,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
0
};
static int autofs_major = -1;
static void *autofs_devfs;
static int automounter_pid;
static lck_rw_t *autofs_automounter_pid_rwlock;
static int
autofs_dev_open(__unused dev_t dev, __unused int oflags, __unused int devtype,
struct proc *p)
{
lck_rw_lock_exclusive(autofs_automounter_pid_rwlock);
if (automounter_pid != 0) {
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
return (EBUSY);
}
automounter_pid = proc_pid(p);
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
return (0);
}
static int
autofs_dev_close(__unused dev_t dev, __unused int flag, __unused int fmt,
__unused struct proc *p)
{
lck_rw_lock_exclusive(autofs_automounter_pid_rwlock);
automounter_pid = 0;
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
return (0);
}
static int
autofs_ioctl(__unused dev_t dev, u_long cmd, __unused caddr_t data,
__unused int flag, __unused struct proc *p)
{
struct autofs_globals *fngp;
fnnode_t *fnp;
vnode_t vp;
fninfo_t *fnip;
struct timeval now;
struct vnode_attr vattr;
int error;
lck_mtx_lock(autofs_global_lock);
if ((fngp = autofs_zone_get_globals()) == NULL) {
fngp = autofs_zone_init();
autofs_zone_set_globals(fngp);
}
lck_mtx_unlock(autofs_global_lock);
if (fngp == NULL)
return (ENOMEM);
switch (cmd) {
case AUTOFS_NOTIFYCHANGE:
if (fngp != NULL) {
lck_mtx_lock(fngp->fng_flush_notification_lock);
fngp->fng_flush_notification_pending = 1;
wakeup(&fngp->fng_flush_notification_pending);
lck_mtx_unlock(fngp->fng_flush_notification_lock);
microtime(&now);
lck_rw_lock_shared(fngp->fng_rootfnnodep->fn_rwlock);
for (fnp = fngp->fng_rootfnnodep->fn_dirents;
fnp != NULL; fnp = fnp->fn_next) {
vp = fntovn(fnp);
fnip = vfstofni(vnode_mount(vp));
if (fnip->fi_flags & MF_DIRECT)
continue;
fnp->fn_mtime = now;
if (vnode_ismonitored(vp) &&
!auto_nobrowse(vp)) {
vfs_get_notify_attributes(&vattr);
auto_get_attributes(vp, &vattr);
vnode_notify(vp, VNODE_EVENT_WRITE,
&vattr);
}
}
lck_rw_unlock_shared(fngp->fng_rootfnnodep->fn_rwlock);
}
error = 0;
break;
case AUTOFS_WAITFORFLUSH:
lck_mtx_lock(autofs_global_lock);
if (fngp == NULL)
fngp = autofs_zone_init();
lck_mtx_unlock(autofs_global_lock);
if (fngp == NULL)
return (ENOMEM);
error = 0;
lck_mtx_lock(fngp->fng_flush_notification_lock);
while (error == 0 && !fngp->fng_flush_notification_pending) {
error = msleep(&fngp->fng_flush_notification_pending,
fngp->fng_flush_notification_lock, PWAIT | PCATCH,
"flush notification", NULL);
}
fngp->fng_flush_notification_pending = 0;
lck_mtx_unlock(fngp->fng_flush_notification_lock);
break;
default:
error = EINVAL;
break;
}
return (error);
}
int
auto_is_automounter(int pid)
{
int is_automounter;
lck_rw_lock_shared(autofs_automounter_pid_rwlock);
is_automounter = (automounter_pid != 0 &&
(pid == automounter_pid || proc_isinferior(pid, automounter_pid)));
lck_rw_unlock_shared(autofs_automounter_pid_rwlock);
return (is_automounter);
}
static d_open_t autofs_nowait_dev_open;
static d_close_t autofs_nowait_dev_close;
static struct cdevsw autofs_nowait_cdevsw = {
autofs_nowait_dev_open,
autofs_nowait_dev_close,
eno_rdwrt,
eno_rdwrt,
eno_ioctl,
eno_stop,
eno_reset,
0,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
0
};
static int autofs_nowait_major = -1;
static void *autofs_nowait_devfs;
struct nowait_process {
LIST_ENTRY(nowait_process) entries;
int pid;
int minor;
};
static LIST_HEAD(nowaitproclist, nowait_process) nowait_processes;
static lck_rw_t *autofs_nowait_processes_rwlock;
static int
autofs_nowait_dev_clone(__unused dev_t dev, int action)
{
int minor;
struct nowait_process *nowait_process;
if (action == DEVFS_CLONE_ALLOC) {
minor = 0;
lck_rw_lock_exclusive(autofs_nowait_processes_rwlock);
LIST_FOREACH(nowait_process, &nowait_processes, entries) {
if (minor < nowait_process->minor) {
break;
}
minor = nowait_process->minor + 1;
}
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
return (minor);
}
return (-1);
}
static int
autofs_nowait_dev_open(dev_t dev, __unused int oflags, __unused int devtype,
struct proc *p)
{
struct nowait_process *newnowait_process, *nowait_process, *lastnowait_process;
MALLOC(newnowait_process, struct nowait_process *, sizeof(*newnowait_process),
M_AUTOFS, M_WAITOK);
if (newnowait_process == NULL)
return (ENOMEM);
newnowait_process->pid = proc_pid(p);
newnowait_process->minor = minor(dev);
lck_rw_lock_exclusive(autofs_nowait_processes_rwlock);
if (LIST_EMPTY(&nowait_processes)) {
LIST_INSERT_HEAD(&nowait_processes, newnowait_process, entries);
} else {
LIST_FOREACH(nowait_process, &nowait_processes, entries) {
if (newnowait_process->minor < nowait_process->minor) {
LIST_INSERT_BEFORE(nowait_process,
newnowait_process, entries);
goto done;
}
lastnowait_process = nowait_process;
}
LIST_INSERT_AFTER(lastnowait_process, newnowait_process,
entries);
}
done:
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
return (0);
}
static int
autofs_nowait_dev_close(dev_t dev, __unused int flag, __unused int fmt,
__unused struct proc *p)
{
struct nowait_process *nowait_process;
lck_rw_lock_exclusive(autofs_nowait_processes_rwlock);
LIST_FOREACH(nowait_process, &nowait_processes, entries) {
if (minor(dev) == nowait_process->minor) {
LIST_REMOVE(nowait_process, entries);
FREE(nowait_process, M_AUTOFS);
break;
}
}
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
return (0);
}
int
auto_is_nowait_process(int pid)
{
struct nowait_process *nowait_process;
lck_rw_lock_shared(autofs_nowait_processes_rwlock);
LIST_FOREACH(nowait_process, &nowait_processes, entries) {
if (pid == nowait_process->pid) {
lck_rw_unlock_shared(autofs_nowait_processes_rwlock);
return (1);
}
}
lck_rw_unlock_shared(autofs_nowait_processes_rwlock);
return (0);
}
static d_open_t autofs_notrigger_dev_open;
static d_close_t autofs_notrigger_dev_close;
static struct cdevsw autofs_notrigger_cdevsw = {
autofs_notrigger_dev_open,
autofs_notrigger_dev_close,
eno_rdwrt,
eno_rdwrt,
eno_ioctl,
eno_stop,
eno_reset,
0,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
0
};
static int autofs_notrigger_major = -1;
static void *autofs_notrigger_devfs;
struct notrigger_process {
LIST_ENTRY(notrigger_process) entries;
int pid;
int minor;
};
static LIST_HEAD(notriggerproclist, notrigger_process) notrigger_processes;
static lck_rw_t *autofs_notrigger_processes_rwlock;
static int
autofs_notrigger_dev_clone(__unused dev_t dev, int action)
{
int minor;
struct notrigger_process *notrigger_process;
if (action == DEVFS_CLONE_ALLOC) {
minor = 0;
lck_rw_lock_exclusive(autofs_notrigger_processes_rwlock);
LIST_FOREACH(notrigger_process, ¬rigger_processes, entries) {
if (minor < notrigger_process->minor) {
break;
}
minor = notrigger_process->minor + 1;
}
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
return (minor);
}
return (-1);
}
static int
autofs_notrigger_dev_open(dev_t dev, __unused int oflags, __unused int devtype,
struct proc *p)
{
struct notrigger_process *newnotrigger_process, *notrigger_process, *lastnotrigger_process;
MALLOC(newnotrigger_process, struct notrigger_process *, sizeof(*newnotrigger_process),
M_AUTOFS, M_WAITOK);
if (newnotrigger_process == NULL)
return (ENOMEM);
newnotrigger_process->pid = proc_pid(p);
newnotrigger_process->minor = minor(dev);
lck_rw_lock_exclusive(autofs_notrigger_processes_rwlock);
if (LIST_EMPTY(¬rigger_processes)) {
LIST_INSERT_HEAD(¬rigger_processes, newnotrigger_process, entries);
} else {
LIST_FOREACH(notrigger_process, ¬rigger_processes, entries) {
if (newnotrigger_process->minor < notrigger_process->minor) {
LIST_INSERT_BEFORE(notrigger_process,
newnotrigger_process, entries);
goto done;
}
lastnotrigger_process = notrigger_process;
}
LIST_INSERT_AFTER(lastnotrigger_process, newnotrigger_process,
entries);
}
done:
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
return (0);
}
static int
autofs_notrigger_dev_close(dev_t dev, __unused int flag, __unused int fmt,
__unused struct proc *p)
{
struct notrigger_process *notrigger_process;
lck_rw_lock_exclusive(autofs_notrigger_processes_rwlock);
LIST_FOREACH(notrigger_process, ¬rigger_processes, entries) {
if (minor(dev) == notrigger_process->minor) {
LIST_REMOVE(notrigger_process, entries);
FREE(notrigger_process, M_AUTOFS);
break;
}
}
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
return (0);
}
int
auto_is_notrigger_process(int pid)
{
struct notrigger_process *notrigger_process;
if (auto_is_automounter(pid))
return (1);
lck_rw_lock_shared(autofs_notrigger_processes_rwlock);
LIST_FOREACH(notrigger_process, ¬rigger_processes, entries) {
if (pid == notrigger_process->pid) {
lck_rw_unlock_shared(autofs_notrigger_processes_rwlock);
return (1);
}
}
lck_rw_unlock_shared(autofs_notrigger_processes_rwlock);
return (0);
}
static d_open_t autofs_homedirmounter_dev_open;
static d_close_t autofs_homedirmounter_dev_close;
static struct cdevsw autofs_homedirmounter_cdevsw = {
autofs_homedirmounter_dev_open,
autofs_homedirmounter_dev_close,
eno_rdwrt,
eno_rdwrt,
eno_ioctl,
eno_stop,
eno_reset,
0,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
0
};
static int autofs_homedirmounter_major = -1;
static void *autofs_homedirmounter_devfs;
struct homedirmounter_process {
LIST_ENTRY(homedirmounter_process) entries;
int pid;
int minor;
vnode_t mount_point;
};
static LIST_HEAD(homedirmounterproclist, homedirmounter_process) homedirmounter_processes;
static lck_rw_t *autofs_homedirmounter_processes_rwlock;
static int
autofs_homedirmounter_dev_clone(__unused dev_t dev, int action)
{
int minor;
struct homedirmounter_process *homedirmounter_process;
if (action == DEVFS_CLONE_ALLOC) {
minor = 0;
lck_rw_lock_exclusive(autofs_homedirmounter_processes_rwlock);
LIST_FOREACH(homedirmounter_process, &homedirmounter_processes,
entries) {
if (minor < homedirmounter_process->minor) {
break;
}
minor = homedirmounter_process->minor + 1;
}
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
return (minor);
}
return (-1);
}
static int
autofs_homedirmounter_dev_open(dev_t dev, __unused int oflags, __unused int devtype,
struct proc *p)
{
struct homedirmounter_process *newhomedirmounter_process, *homedirmounter_process, *lasthomedirmounter_process;
MALLOC(newhomedirmounter_process, struct homedirmounter_process *,
sizeof(*newhomedirmounter_process), M_AUTOFS, M_WAITOK);
if (newhomedirmounter_process == NULL)
return (ENOMEM);
newhomedirmounter_process->pid = proc_pid(p);
newhomedirmounter_process->minor = minor(dev);
newhomedirmounter_process->mount_point = NULL;
lck_rw_lock_exclusive(autofs_homedirmounter_processes_rwlock);
if (LIST_EMPTY(&homedirmounter_processes)) {
LIST_INSERT_HEAD(&homedirmounter_processes,
newhomedirmounter_process, entries);
} else {
LIST_FOREACH(homedirmounter_process, &homedirmounter_processes,
entries) {
if (newhomedirmounter_process->minor < homedirmounter_process->minor) {
LIST_INSERT_BEFORE(homedirmounter_process,
newhomedirmounter_process, entries);
goto done;
}
lasthomedirmounter_process = homedirmounter_process;
}
LIST_INSERT_AFTER(lasthomedirmounter_process,
newhomedirmounter_process, entries);
}
done:
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
return (0);
}
static int
autofs_homedirmounter_dev_close(dev_t dev, __unused int flag, __unused int fmt,
__unused struct proc *p)
{
struct homedirmounter_process *homedirmounter_process;
vnode_t vp;
fnnode_t *fnp;
lck_rw_lock_exclusive(autofs_homedirmounter_processes_rwlock);
LIST_FOREACH(homedirmounter_process, &homedirmounter_processes,
entries) {
if (minor(dev) == homedirmounter_process->minor) {
LIST_REMOVE(homedirmounter_process, entries);
vp = homedirmounter_process->mount_point;
if (vp != NULL) {
fnp = vntofn(vp);
lck_mtx_lock(fnp->fn_lock);
fnp->fn_flags &= ~MF_HOMEDIRMOUNT;
lck_mtx_unlock(fnp->fn_lock);
vnode_rele(vp);
}
FREE(homedirmounter_process, M_AUTOFS);
break;
}
}
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
return (0);
}
int
auto_is_homedirmounter_process(vnode_t vp, int pid)
{
struct homedirmounter_process *homedirmounter_process;
int ret;
lck_rw_lock_shared(autofs_homedirmounter_processes_rwlock);
LIST_FOREACH(homedirmounter_process, &homedirmounter_processes,
entries) {
if (pid == homedirmounter_process->pid) {
ret = (vp == NULL) || (vp == homedirmounter_process->mount_point);
lck_rw_unlock_shared(autofs_homedirmounter_processes_rwlock);
return (ret);
}
}
lck_rw_unlock_shared(autofs_homedirmounter_processes_rwlock);
return (0);
}
int
auto_mark_vnode_homedirmount(vnode_t vp, int pid)
{
struct homedirmounter_process *homedirmounter_process;
int error;
fnnode_t *fnp;
lck_rw_lock_shared(autofs_homedirmounter_processes_rwlock);
LIST_FOREACH(homedirmounter_process, &homedirmounter_processes,
entries) {
if (pid == homedirmounter_process->pid) {
if (homedirmounter_process->mount_point != NULL) {
if (homedirmounter_process->mount_point == vp) {
error = 0;
} else {
error = EBUSY;
}
} else {
error = vnode_ref(vp);
if (error == 0) {
fnp = vntofn(vp);
lck_mtx_lock(fnp->fn_lock);
fnp->fn_flags |= MF_HOMEDIRMOUNT;
lck_mtx_unlock(fnp->fn_lock);
homedirmounter_process->mount_point = vp;
}
}
lck_rw_unlock_shared(autofs_homedirmounter_processes_rwlock);
return (error);
}
}
lck_rw_unlock_shared(autofs_homedirmounter_processes_rwlock);
return (EINVAL);
}
static d_open_t auto_control_dev_open;
static d_close_t auto_control_dev_close;
static d_ioctl_t auto_control_ioctl;
static struct cdevsw autofs_control_cdevsw = {
auto_control_dev_open,
auto_control_dev_close,
eno_rdwrt,
eno_rdwrt,
auto_control_ioctl,
eno_stop,
eno_reset,
0,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
0
};
static int autofs_control_major = -1;
static void *autofs_control_devfs;
static int autofs_control_isopen;
static lck_mtx_t *autofs_control_isopen_lock;
static int
auto_control_dev_open(__unused dev_t dev, __unused int oflags,
__unused int devtype, __unused struct proc *p)
{
lck_mtx_lock(autofs_control_isopen_lock);
if (autofs_control_isopen) {
lck_mtx_unlock(autofs_control_isopen_lock);
return (EBUSY);
}
autofs_control_isopen = 1;
lck_mtx_unlock(autofs_control_isopen_lock);
return (0);
}
static int
auto_control_dev_close(__unused dev_t dev, __unused int flag,
__unused int fmt, __unused struct proc *p)
{
lck_mtx_lock(autofs_control_isopen_lock);
autofs_control_isopen = 0;
lck_mtx_unlock(autofs_control_isopen_lock);
return (0);
}
static int
auto_control_ioctl(__unused dev_t dev, u_long cmd, caddr_t data,
__unused int flag, __unused proc_t p)
{
struct autofs_globals *fngp;
int error;
struct autofs_update_args_32 *update_argsp_32;
struct autofs_update_args_64 update_args;
mount_t mp;
fninfo_t *fnip;
lck_mtx_lock(autofs_global_lock);
if ((fngp = autofs_zone_get_globals()) == NULL) {
fngp = autofs_zone_init();
autofs_zone_set_globals(fngp);
}
lck_mtx_unlock(autofs_global_lock);
if (fngp == NULL)
return (ENOMEM);
switch (cmd) {
case AUTOFS_SET_MOUNT_TO:
trigger_set_mount_to(*(int *)data);
error = 0;
break;
case AUTOFS_UPDATE_OPTIONS_32:
update_argsp_32 = (struct autofs_update_args_32 *)data;
update_args.fsid = update_argsp_32->fsid;
update_args.opts = CAST_USER_ADDR_T(update_argsp_32->opts);
update_args.map = CAST_USER_ADDR_T(update_argsp_32->map);
update_args.mntflags = update_argsp_32->mntflags;
update_args.direct = update_argsp_32->direct;
update_args.node_type = update_argsp_32->node_type;
error = auto_update_options(&update_args);
break;
case AUTOFS_UPDATE_OPTIONS_64:
error = auto_update_options((struct autofs_update_args_64 *)data);
break;
case AUTOFS_NOTIFYCHANGE:
if (fngp != NULL) {
lck_mtx_lock(fngp->fng_flush_notification_lock);
fngp->fng_flush_notification_pending = 1;
wakeup(&fngp->fng_flush_notification_pending);
lck_mtx_unlock(fngp->fng_flush_notification_lock);
}
error = 0;
break;
case AUTOFS_UNMOUNT:
error = 0;
if (fngp != NULL) {
mp = vfs_getvfs((fsid_t *)data);
if (mp == NULL) {
error = ENOENT;
break;
}
if (!auto_is_autofs(mp)) {
error = EINVAL;
break;
}
fnip = vfstofni(mp);
lck_rw_lock_exclusive(fnip->fi_rwlock);
fnip->fi_flags |= MF_UNMOUNTING;
lck_rw_unlock_exclusive(fnip->fi_rwlock);
error = vfs_unmountbyfsid((fsid_t *)data, 0,
vfs_context_current());
if (error != 0) {
lck_rw_lock_exclusive(fnip->fi_rwlock);
fnip->fi_flags &= ~MF_UNMOUNTING;
lck_rw_unlock_exclusive(fnip->fi_rwlock);
}
}
break;
case AUTOFS_UNMOUNT_TRIGGERED:
unmount_triggered_mounts(1);
error = 0;
break;
default:
error = EINVAL;
break;
}
return (error);
}
__private_extern__ int
auto_module_start(__unused kmod_info_t *ki, __unused void *data)
{
errno_t error;
autofs_lck_grp = lck_grp_alloc_init("autofs", NULL);
if (autofs_lck_grp == NULL) {
IOLog("auto_module_start: Couldn't create autofs lock group\n");
goto fail;
}
autofs_global_lock = lck_mtx_alloc_init(autofs_lck_grp, LCK_ATTR_NULL);
if (autofs_global_lock == NULL) {
IOLog("auto_module_start: Couldn't create autofs global lock\n");
goto fail;
}
autofs_nodeid_lock = lck_mtx_alloc_init(autofs_lck_grp, LCK_ATTR_NULL);
if (autofs_nodeid_lock == NULL) {
IOLog("auto_module_start: Couldn't create autofs node ID lock\n");
goto fail;
}
autofs_automounter_pid_rwlock = lck_rw_alloc_init(autofs_lck_grp, NULL);
if (autofs_automounter_pid_rwlock == NULL) {
IOLog("auto_module_start: Couldn't create autofs automounter pid lock\n");
goto fail;
}
autofs_nowait_processes_rwlock = lck_rw_alloc_init(autofs_lck_grp, NULL);
if (autofs_nowait_processes_rwlock == NULL) {
IOLog("auto_module_start: Couldn't create autofs nowait_processes list lock\n");
goto fail;
}
autofs_notrigger_processes_rwlock = lck_rw_alloc_init(autofs_lck_grp, NULL);
if (autofs_notrigger_processes_rwlock == NULL) {
IOLog("auto_module_start: Couldn't create autofs notrigger_processes list lock\n");
goto fail;
}
autofs_homedirmounter_processes_rwlock = lck_rw_alloc_init(autofs_lck_grp, NULL);
if (autofs_homedirmounter_processes_rwlock == NULL) {
IOLog("auto_module_start: Couldn't create autofs homedirmounter_processes list lock\n");
goto fail;
}
autofs_control_isopen_lock = lck_mtx_alloc_init(autofs_lck_grp, LCK_ATTR_NULL);
if (autofs_control_isopen_lock == NULL) {
IOLog("auto_module_start: Couldn't create autofs control device lock\n");
goto fail;
}
autofs_major = cdevsw_add(-1, &autofs_cdevsw);
if (autofs_major == -1) {
IOLog("auto_module_start: cdevsw_add failed on autofs device\n");
goto fail;
}
autofs_devfs = devfs_make_node(makedev(autofs_major, 0),
DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0600, AUTOFS_DEVICE);
if (autofs_devfs == NULL) {
IOLog("auto_module_start: devfs_make_node failed on autofs device\n");
goto fail;
}
autofs_nowait_major = cdevsw_add(-1, &autofs_nowait_cdevsw);
if (autofs_nowait_major == -1) {
IOLog("auto_module_start: cdevsw_add failed on autofs_nowait device\n");
goto fail;
}
autofs_nowait_devfs = devfs_make_node_clone(makedev(autofs_nowait_major, 0),
DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0666, autofs_nowait_dev_clone,
AUTOFS_NOWAIT_DEVICE);
if (autofs_nowait_devfs == NULL) {
IOLog("auto_module_start: devfs_make_node failed on autofs nowait device\n");
goto fail;
}
autofs_notrigger_major = cdevsw_add(-1, &autofs_notrigger_cdevsw);
if (autofs_notrigger_major == -1) {
IOLog("auto_module_start: cdevsw_add failed on autofs_notrigger device\n");
goto fail;
}
autofs_notrigger_devfs = devfs_make_node_clone(makedev(autofs_notrigger_major, 0),
DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0666, autofs_notrigger_dev_clone,
AUTOFS_NOTRIGGER_DEVICE);
if (autofs_notrigger_devfs == NULL) {
IOLog("auto_module_start: devfs_make_node failed on autofs notrigger device\n");
goto fail;
}
autofs_homedirmounter_major = cdevsw_add(-1, &autofs_homedirmounter_cdevsw);
if (autofs_homedirmounter_major == -1) {
IOLog("auto_module_start: cdevsw_add failed on autofs_homedirmounter device\n");
goto fail;
}
autofs_homedirmounter_devfs = devfs_make_node_clone(makedev(autofs_homedirmounter_major, 0),
DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0666, autofs_homedirmounter_dev_clone,
AUTOFS_HOMEDIRMOUNTER_DEVICE);
if (autofs_homedirmounter_devfs == NULL) {
IOLog("auto_module_start: devfs_make_node failed on autofs homedirmounter device\n");
goto fail;
}
autofs_control_major = cdevsw_add(-1, &autofs_control_cdevsw);
if (autofs_control_major == -1) {
IOLog("auto_module_start: cdevsw_add failed on autofs control device\n");
goto fail;
}
autofs_control_devfs = devfs_make_node(makedev(autofs_control_major, 0),
DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0600, AUTOFS_CONTROL_DEVICE);
if (autofs_control_devfs == NULL) {
IOLog("auto_module_start: devfs_make_node failed on autofs control device\n");
goto fail;
}
error = vfs_fsadd(&autofs_fsentry, &auto_vfsconf);
if (error != 0) {
IOLog("auto_module_start: Error %d from vfs_fsadd\n",
error);
goto fail;
}
return (KERN_SUCCESS);
fail:
if (autofs_control_devfs != NULL)
devfs_remove(autofs_control_devfs);
if (autofs_control_major != -1) {
if (cdevsw_remove(autofs_control_major, &autofs_control_cdevsw) == -1)
panic("auto_module_start: can't remove autofs control device from cdevsw");
}
if (autofs_nowait_devfs != NULL)
devfs_remove(autofs_nowait_devfs);
if (autofs_nowait_major != -1) {
if (cdevsw_remove(autofs_nowait_major, &autofs_nowait_cdevsw) == -1)
panic("auto_module_start: can't remove autofs nowait device from cdevsw");
}
if (autofs_notrigger_devfs != NULL)
devfs_remove(autofs_notrigger_devfs);
if (autofs_notrigger_major != -1) {
if (cdevsw_remove(autofs_notrigger_major, &autofs_notrigger_cdevsw) == -1)
panic("auto_module_start: can't remove autofs notrigger device from cdevsw");
}
if (autofs_homedirmounter_devfs != NULL)
devfs_remove(autofs_homedirmounter_devfs);
if (autofs_homedirmounter_major != -1) {
if (cdevsw_remove(autofs_homedirmounter_major, &autofs_homedirmounter_cdevsw) == -1)
panic("auto_module_start: can't remove autofs homedirmounter device from cdevsw");
}
if (autofs_devfs != NULL)
devfs_remove(autofs_devfs);
if (autofs_major != -1) {
if (cdevsw_remove(autofs_major, &autofs_cdevsw) == -1)
panic("auto_module_start: can't remove autofs device from cdevsw");
}
if (autofs_control_isopen_lock != NULL)
lck_mtx_free(autofs_control_isopen_lock, autofs_lck_grp);
if (autofs_nowait_processes_rwlock != NULL)
lck_rw_free(autofs_nowait_processes_rwlock, autofs_lck_grp);
if (autofs_notrigger_processes_rwlock != NULL)
lck_rw_free(autofs_notrigger_processes_rwlock, autofs_lck_grp);
if (autofs_homedirmounter_processes_rwlock != NULL)
lck_rw_free(autofs_homedirmounter_processes_rwlock, autofs_lck_grp);
if (autofs_automounter_pid_rwlock != NULL)
lck_rw_free(autofs_automounter_pid_rwlock, autofs_lck_grp);
if (autofs_nodeid_lock != NULL)
lck_mtx_free(autofs_nodeid_lock, autofs_lck_grp);
if (autofs_global_lock != NULL)
lck_mtx_free(autofs_global_lock, autofs_lck_grp);
if (autofs_lck_grp != NULL)
lck_grp_free(autofs_lck_grp);
return (KERN_FAILURE);
}
__private_extern__ int
auto_module_stop(__unused kmod_info_t *ki, __unused void *data)
{
struct autofs_globals *fngp;
int error;
lck_mtx_lock(autofs_global_lock);
lck_mtx_lock(autofs_nodeid_lock);
lck_rw_lock_exclusive(autofs_automounter_pid_rwlock);
lck_rw_lock_exclusive(autofs_nowait_processes_rwlock);
lck_rw_lock_exclusive(autofs_notrigger_processes_rwlock);
lck_rw_lock_exclusive(autofs_homedirmounter_processes_rwlock);
lck_mtx_lock(autofs_control_isopen_lock);
if (autofs_mounts != 0) {
AUTOFS_DPRINT((2, "auto_module_stop: Can't remove, still %u mounts active\n", autofs_mounts));
lck_mtx_unlock(autofs_control_isopen_lock);
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
lck_mtx_unlock(autofs_nodeid_lock);
lck_mtx_unlock(autofs_global_lock);
return (KERN_NO_ACCESS);
}
if (automounter_pid != 0) {
AUTOFS_DPRINT((2, "auto_module_stop: Can't remove, automounter still running\n"));
lck_mtx_unlock(autofs_control_isopen_lock);
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
lck_mtx_unlock(autofs_nodeid_lock);
lck_mtx_unlock(autofs_global_lock);
return (KERN_NO_ACCESS);
}
if (!LIST_EMPTY(&nowait_processes)) {
AUTOFS_DPRINT((2, "auto_module_stop: Can't remove, still nowait processes running\n"));
lck_mtx_unlock(autofs_control_isopen_lock);
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
lck_mtx_unlock(autofs_nodeid_lock);
lck_mtx_unlock(autofs_global_lock);
return (KERN_NO_ACCESS);
}
if (!LIST_EMPTY(¬rigger_processes)) {
AUTOFS_DPRINT((2, "auto_module_stop: Can't remove, still notrigger processes running\n"));
lck_mtx_unlock(autofs_control_isopen_lock);
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
lck_mtx_unlock(autofs_nodeid_lock);
lck_mtx_unlock(autofs_global_lock);
return (KERN_NO_ACCESS);
}
if (!LIST_EMPTY(&homedirmounter_processes)) {
AUTOFS_DPRINT((2, "auto_module_stop: Can't remove, still homedirmounter processes running\n"));
lck_mtx_unlock(autofs_control_isopen_lock);
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
lck_mtx_unlock(autofs_nodeid_lock);
lck_mtx_unlock(autofs_global_lock);
return (KERN_NO_ACCESS);
}
if (autofs_control_isopen) {
AUTOFS_DPRINT((2, "auto_module_stop: Can't remove, automount command is running\n"));
lck_mtx_unlock(autofs_control_isopen_lock);
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
lck_mtx_unlock(autofs_nodeid_lock);
lck_mtx_unlock(autofs_global_lock);
return (KERN_NO_ACCESS);
}
AUTOFS_DPRINT((10, "auto_module_stop: removing autofs from vfs conf. list...\n"));
fngp = autofs_zone_get_globals();
if (fngp) {
assert(fngp->fng_fnnode_count == 1);
}
error = vfs_fsremove(auto_vfsconf);
if (error) {
IOLog("auto_module_stop: Error %d from vfs_remove\n",
error);
return (KERN_FAILURE);
}
devfs_remove(autofs_devfs);
autofs_devfs = NULL;
if (cdevsw_remove(autofs_major, &autofs_cdevsw) == -1)
panic("auto_module_stop: can't remove autofs device from cdevsw");
autofs_major = -1;
devfs_remove(autofs_nowait_devfs);
autofs_nowait_devfs = NULL;
if (cdevsw_remove(autofs_nowait_major, &autofs_nowait_cdevsw) == -1)
panic("auto_module_stop: can't remove autofs nowait device from cdevsw");
autofs_nowait_major = -1;
devfs_remove(autofs_notrigger_devfs);
autofs_notrigger_devfs = NULL;
if (cdevsw_remove(autofs_notrigger_major, &autofs_notrigger_cdevsw) == -1)
panic("auto_module_stop: can't remove autofs notrigger device from cdevsw");
autofs_notrigger_major = -1;
devfs_remove(autofs_homedirmounter_devfs);
autofs_homedirmounter_devfs = NULL;
if (cdevsw_remove(autofs_homedirmounter_major, &autofs_homedirmounter_cdevsw) == -1)
panic("auto_module_stop: can't remove autofs homedirmounter device from cdevsw");
autofs_homedirmounter_major = -1;
devfs_remove(autofs_control_devfs);
autofs_control_devfs = NULL;
if (cdevsw_remove(autofs_control_major, &autofs_control_cdevsw) == -1)
panic("auto_module_start: can't remove autofs control device from cdevsw");
if (fngp) {
FREE(fngp->fng_rootfnnodep->fn_name, M_AUTOFS);
FREE(fngp->fng_rootfnnodep, M_AUTOFS);
lck_mtx_free(fngp->fng_flush_notification_lock, autofs_lck_grp);
FREE(fngp, M_AUTOFS);
}
lck_mtx_unlock(autofs_nodeid_lock);
lck_mtx_free(autofs_nodeid_lock, autofs_lck_grp);
lck_mtx_unlock(autofs_global_lock);
lck_mtx_free(autofs_global_lock, autofs_lck_grp);
lck_rw_unlock_exclusive(autofs_automounter_pid_rwlock);
lck_rw_free(autofs_automounter_pid_rwlock, autofs_lck_grp);
lck_rw_unlock_exclusive(autofs_nowait_processes_rwlock);
lck_rw_free(autofs_nowait_processes_rwlock, autofs_lck_grp);
lck_rw_unlock_exclusive(autofs_notrigger_processes_rwlock);
lck_rw_free(autofs_notrigger_processes_rwlock, autofs_lck_grp);
lck_rw_unlock_exclusive(autofs_homedirmounter_processes_rwlock);
lck_rw_free(autofs_homedirmounter_processes_rwlock, autofs_lck_grp);
lck_mtx_unlock(autofs_control_isopen_lock);
lck_mtx_free(autofs_control_isopen_lock, autofs_lck_grp);
lck_grp_free(autofs_lck_grp);
return (KERN_SUCCESS);
}