summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2016-02-04 18:08:21 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2016-02-04 18:08:21 +0000
commitf13e7d60575604d9d02fd12ce871729dc2e73b7d (patch)
tree491a8b819b85c49cd27f1915beb529512be02862 /usr/src
parent0e08080c57a8dbbbf4eeb790edc912a174042c58 (diff)
downloadillumos-joyent-f13e7d60575604d9d02fd12ce871729dc2e73b7d.tar.gz
OS-5070 missing autofs dev ioctls - automounter restart recovery doesn't work
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/devfsadm/i386/lx_link_i386.c18
-rw-r--r--usr/src/lib/brand/lx/zone/platform.xml3
-rw-r--r--usr/src/uts/common/brand/lx/autofs/lx_autofs.c974
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_autofs.h145
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h10
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_ioctl.c39
-rw-r--r--usr/src/uts/intel/lxautofs/Makefile9
7 files changed, 1069 insertions, 129 deletions
diff --git a/usr/src/cmd/devfsadm/i386/lx_link_i386.c b/usr/src/cmd/devfsadm/i386/lx_link_i386.c
index 855f4f7383..4f8a17b655 100644
--- a/usr/src/cmd/devfsadm/i386/lx_link_i386.c
+++ b/usr/src/cmd/devfsadm/i386/lx_link_i386.c
@@ -22,18 +22,19 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <devfsadm.h>
#include <strings.h>
#include <stdio.h>
#include <sys/lx_ptm.h>
#include <sys/lx_audio.h>
+#include <sys/lx_autofs.h>
static int lx_ptm(di_minor_t minor, di_node_t node);
static int lx_audio(di_minor_t minor, di_node_t node);
+static int lx_autofs(di_minor_t minor, di_node_t node);
static int lx_systrace(di_minor_t minor, di_node_t node);
static devfsadm_create_t lx_create_cbt[] = {
@@ -41,6 +42,8 @@ static devfsadm_create_t lx_create_cbt[] = {
TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_ptm },
{ "pseudo", "ddi_pseudo", LX_AUDIO_DRV,
TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_audio },
+ { "pseudo", "ddi_pseudo", LX_AUTOFS_NAME,
+ TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_autofs },
{ "pseudo", "ddi_pseudo", "lx_systrace",
TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_systrace },
};
@@ -74,6 +77,17 @@ lx_audio(di_minor_t minor, di_node_t node)
}
static int
+lx_autofs(di_minor_t minor, di_node_t node)
+{
+ char *mname = di_minor_name(minor);
+
+ if (strcmp(LX_AUTOFS_MINORNAME, mname) == 0)
+ (void) devfsadm_mklink("brand/lx/autofs", node, minor, 0);
+
+ return (DEVFSADM_CONTINUE);
+}
+
+static int
lx_systrace(di_minor_t minor, di_node_t node)
{
char *mname = di_minor_name(minor);
diff --git a/usr/src/lib/brand/lx/zone/platform.xml b/usr/src/lib/brand/lx/zone/platform.xml
index e0aeef073c..ccb6ab3ba1 100644
--- a/usr/src/lib/brand/lx/zone/platform.xml
+++ b/usr/src/lib/brand/lx/zone/platform.xml
@@ -22,7 +22,7 @@
Copyright 2007 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
- Copyright 2015 Joyent, Inc.
+ Copyright 2016 Joyent, Inc.
DO NOT EDIT THIS FILE.
-->
@@ -126,6 +126,7 @@
<device match="vni" ip-type="exclusive" />
<!-- Renamed devices to create under /dev -->
+ <device match="brand/lx/autofs" name="autofs" />
<device match="brand/lx/ptmx" name="ptmx" />
<device match="zcons/%z/zoneconsole" name="console" />
<device match="zfd/%z/slave/0" name="zfd/0" />
diff --git a/usr/src/uts/common/brand/lx/autofs/lx_autofs.c b/usr/src/uts/common/brand/lx/autofs/lx_autofs.c
index f93b4fc14c..c55fc6d95f 100644
--- a/usr/src/uts/common/brand/lx/autofs/lx_autofs.c
+++ b/usr/src/uts/common/brand/lx/autofs/lx_autofs.c
@@ -30,6 +30,7 @@
*/
#include <fs/fs_subr.h>
+#include <sys/stat.h>
#include <sys/atomic.h>
#include <sys/cmn_err.h>
#include <sys/dirent.h>
@@ -38,6 +39,7 @@
#include <sys/mount.h>
#include <sys/policy.h>
#include <sys/sunddi.h>
+#include <sys/conf.h>
#include <sys/sdt.h>
#include <sys/sysmacros.h>
@@ -65,6 +67,29 @@ static vnodeops_t *lx_autofs_vn_ops = NULL;
static int lx_autofs_fstype;
static major_t lx_autofs_major;
static minor_t lx_autofs_minor = 0;
+static dev_info_t *lx_autofs_dip = NULL;
+
+#define LX_AUTOFS_DEV_VERSION_MAJOR 1
+#define LX_AUTOFS_DEV_VERSION_MINOR 0
+
+/* The Linux autofs superblock magic number */
+#define LX_AUTOFS_SB_MAGIC 0x0187
+
+/* Linux autofs mount types */
+#define LX_AUTOFS_TYPE_INDIRECT 1
+#define LX_AUTOFS_TYPE_DIRECT 2
+#define LX_AUTOFS_TYPE_OFFSET 4
+
+/* Structure passed for autofs dev ioctls */
+typedef struct lx_autofs_dv_ioctl {
+ uint32_t lad_ver_major;
+ uint32_t lad_ver_minor;
+ uint32_t lad_size;
+ uint32_t lad_ioctlfd;
+ uint32_t lad_arg1;
+ uint32_t lad_arg2;
+ char lad_path[0];
+} lx_autofs_dv_ioctl_t;
/*
* Support functions
@@ -155,7 +180,7 @@ lx_autofs_fifo_peer_vp(vnode_t *vp)
static vnode_t *
lx_autofs_vn_alloc(vfs_t *vfsp, vnode_t *uvp)
{
- lx_autofs_vfs_t *data = vfsp->vfs_data;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
vnode_t *vp, *vp_old;
/* Allocate a new vnode structure in case we need it. */
@@ -207,7 +232,7 @@ static void
lx_autofs_vn_free(vnode_t *vp)
{
vfs_t *vfsp = vp->v_vfsp;
- lx_autofs_vfs_t *data = vfsp->vfs_data;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
vnode_t *uvp = vp->v_data;
vnode_t *vp_tmp;
@@ -969,7 +994,7 @@ lx_autofs_automounter_call(vnode_t *dvp, char *nm)
boolean_t is_dup;
/* Get a pointer to the vfs mount data. */
- data = dvp->v_vfsp->vfs_data;
+ data = (lx_autofs_vfs_t *)dvp->v_vfsp->vfs_data;
/* The automounter only supports queries in the root directory. */
if (dvp != data->lav_root)
@@ -1078,9 +1103,10 @@ lx_autofs_may_unmount(vfs_t *vfsp, struct cred *cr)
if (vfsp->vfs_count > 2)
return (B_FALSE);
- /* Check for any remaining holds on the root vnode. */
- data = vfsp->vfs_data;
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
ASSERT(data->lav_root->v_vfsp == vfsp);
+
+ /* Check for any remaining holds on the root vnode. */
if (data->lav_root->v_count > 1)
return (B_FALSE);
@@ -1226,7 +1252,7 @@ errexit:
* Note that lx_autofs_automounter_call() only supports queries in the root
* directory, so all mntent names are relative to that.
*/
-static void
+static int
lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
{
lx_autofs_vfs_t *data;
@@ -1238,7 +1264,7 @@ lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
boolean_t busy = B_FALSE;
char exp_path[MAXPATHLEN];
- data = vfsp->vfs_data;
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
/*
* We process only the first element (i.e. do not do multi). This
@@ -1247,8 +1273,41 @@ lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
mutex_enter(&data->lav_lock);
mp = (lx_autofs_mntent_t *)list_remove_head(&data->lav_mnt_list);
mutex_exit(&data->lav_lock);
- if (mp == NULL)
- return;
+ if (mp == NULL) {
+ if (data->lav_mnttype == LXAMT_OFFSET) {
+ /*
+ * During restart the automounter will openmount each
+ * offset mount for management. It won't closemount the
+ * offset mount until we expire it, even though nothing
+ * is mounted over that offset. We handle this as a
+ * special expiration case.
+ */
+ int cnt;
+
+ mutex_enter(&data->lav_lock);
+ cnt = data->lav_openmnt_cnt;
+ mutex_exit(&data->lav_lock);
+
+ if (cnt == 1 && vn_ismntpt(data->lav_root) == 0) {
+ char *mntpt = (char *)
+ refstr_value(vfsp->vfs_mntpt);
+ char *nm = ZONE_PATH_TRANSLATE(mntpt, curzone);
+
+ mp = kmem_zalloc(sizeof (lx_autofs_mntent_t),
+ KM_SLEEP);
+ mp->lxafme_len = strlen(nm) + 1;
+ mp->lxafme_path = kmem_zalloc(mp->lxafme_len,
+ KM_SLEEP);
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ (void) strlcpy(mp->lxafme_path, nm,
+ mp->lxafme_len);
+
+ goto exp_offset;
+ }
+ }
+
+ return (EAGAIN);
+ }
/*
* We only return an expired mount if it is inactive for the full
@@ -1262,7 +1321,7 @@ lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
mutex_enter(&data->lav_lock);
list_insert_tail(&data->lav_mnt_list, mp);
mutex_exit(&data->lav_lock);
- return;
+ return (EAGAIN);
}
}
@@ -1278,16 +1337,6 @@ lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
if (fnd_vfs != NULL) {
boolean_t skip = B_FALSE;
vfssw_t *vfssw;
- char *vfsmp = (char *)refstr_value(fnd_vfs->vfs_mntpt);
-
- /*
- * Check if we found a sub-mount. This happens with an offset
- * mount below an indirect mount subdirectory.
- */
- if (strcmp(exp_path, vfsmp) != 0) {
- busy = B_TRUE;
- skip = B_TRUE;
- }
/*
* If it's an NFS file system (typical) then we check in
@@ -1353,7 +1402,7 @@ lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
mutex_enter(&data->lav_lock);
list_insert_tail(&data->lav_mnt_list, mp);
mutex_exit(&data->lav_lock);
- return;
+ return (EAGAIN);
}
/*
@@ -1361,6 +1410,7 @@ lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
* to the automounter in a similar way.
*/
+exp_offset:
/* Verify that the automount process pipe still exists. */
mutex_enter(&data->lav_lock);
if (data->lav_fifo_wr == NULL) {
@@ -1424,20 +1474,38 @@ lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
}
lx_autofs_la_release(data, laar);
- return;
+ return (0);
err_free:
kmem_free(mp->lxafme_path, mp->lxafme_len);
kmem_free(mp, sizeof (lx_autofs_mntent_t));
+ return (EAGAIN);
+}
+
+static int
+lx_autofs_ack(int reqid, vfs_t *vfsp, enum lx_autofs_callres result)
+{
+ lx_autofs_vfs_t *data;
+ lx_autofs_automnt_req_t *laar;
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ if ((laar = lx_autofs_la_find(data, reqid)) == NULL)
+ return (ENXIO);
+
+ /* Mark the request as complete and release it. */
+ laar->laar_result = result;
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ return (0);
}
static int
lx_autofs_automounter_ioctl(vnode_t *vp, int cmd, intptr_t arg, cred_t *cr)
{
lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
- lx_autofs_automnt_req_t *laar;
int id = arg;
int v;
+ int err;
/*
* Be strict.
@@ -1452,20 +1520,13 @@ lx_autofs_automounter_ioctl(vnode_t *vp, int cmd, intptr_t arg, cred_t *cr)
switch (cmd) {
case LX_AUTOFS_IOC_READY:
- case LX_AUTOFS_IOC_FAIL:
- /*
- * We don't actually care if the request failed or succeeded.
- * We do the same thing either way.
- */
- if ((laar = lx_autofs_la_find(data, id)) == NULL)
- return (ENXIO);
-
- laar->laar_result = ((cmd == LX_AUTOFS_IOC_READY) ?
- LXACR_READY : LXACR_FAIL);
+ if ((err = lx_autofs_ack(id, vp->v_vfsp, LXACR_READY)) != 0)
+ return (err);
+ return (0);
- /* Mark the request as complete and release it. */
- lx_autofs_la_complete(data, laar);
- lx_autofs_la_release(data, laar);
+ case LX_AUTOFS_IOC_FAIL:
+ if ((err = lx_autofs_ack(id, vp->v_vfsp, LXACR_FAIL)) != 0)
+ return (err);
return (0);
case LX_AUTOFS_IOC_CATATONIC:
@@ -1475,14 +1536,14 @@ lx_autofs_automounter_ioctl(vnode_t *vp, int cmd, intptr_t arg, cred_t *cr)
case LX_AUTOFS_IOC_PROTOVER:
v = LX_AUTOFS_PROTO_VERS5;
- if (copyout(&v, (caddr_t)arg, sizeof (int)))
- return (set_errno(EFAULT));
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
return (0);
case LX_AUTOFS_IOC_PROTOSUBVER:
v = LX_AUTOFS_PROTO_SUBVERSION;
- if (copyout(&v, (caddr_t)arg, sizeof (int)))
- return (set_errno(EFAULT));
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
return (0);
case LX_AUTOFS_IOC_ASKUMOUNT:
@@ -1495,18 +1556,14 @@ lx_autofs_automounter_ioctl(vnode_t *vp, int cmd, intptr_t arg, cred_t *cr)
if (lx_autofs_may_unmount(vp->v_vfsp, cr))
v = 0;
- if (copyout(&v, (caddr_t)arg, sizeof (int)))
- return (set_errno(EFAULT));
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
return (0);
case LX_AUTOFS_IOC_SETTIMEOUT:
- /*
- * We currently don't use the timeout, but we keep track of
- * it in case we want to use it as an expiration hint in the
- * future.
- */
- if (copyin((caddr_t)arg, &data->lav_timeout, sizeof (ulong_t)))
- return (set_errno(EFAULT));
+ if (copyin((caddr_t)arg, &data->lav_timeout, sizeof (ulong_t))
+ != 0)
+ return (EFAULT);
return (0);
case LX_AUTOFS_IOC_EXPIRE:
@@ -1743,7 +1800,7 @@ lx_autofs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
return (EBUSY);
/* Check for any remaining holds on the root vnode. */
- data = vfsp->vfs_data;
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
ASSERT(data->lav_root->v_vfsp == vfsp);
if (data->lav_root->v_count > 1)
return (EBUSY);
@@ -1792,7 +1849,7 @@ lx_autofs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
static int
lx_autofs_root(vfs_t *vfsp, vnode_t **vpp)
{
- lx_autofs_vfs_t *data = vfsp->vfs_data;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
*vpp = data->lav_root;
VN_HOLD(*vpp);
@@ -1803,7 +1860,7 @@ lx_autofs_root(vfs_t *vfsp, vnode_t **vpp)
static int
lx_autofs_statvfs(vfs_t *vfsp, statvfs64_t *sp)
{
- lx_autofs_vfs_t *data = vfsp->vfs_data;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
vnode_t *urvp = data->lav_root->v_data;
dev32_t d32;
int error;
@@ -1883,7 +1940,7 @@ static vnode_t *
lx_autofs_do_direct(vnode_t *vp)
{
vfs_t *vfsp = vp->v_vfsp;
- lx_autofs_vfs_t *data = vfsp->vfs_data;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
vnode_t *nvp;
boolean_t skip_am_call = B_FALSE;
@@ -2015,6 +2072,7 @@ lx_autofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
vnode_t *uvp = vp->v_data;
vnode_t *dvp;
int error;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
if ((dvp = lx_autofs_do_direct(vp)) != NULL) {
uvp = dvp;
@@ -2025,9 +2083,20 @@ lx_autofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
if (dvp != NULL) {
/* we operated on the direct mounted fs */
VN_RELE(dvp);
+ if (error == 0) {
+ /*
+ * During automounter restart recovery the automounter
+ * will fstat the fd provided in the setpipe ioctl. It
+ * uses the resulting inode & dev to correlate future
+ * autofs fifo requests to the correct entry. Thus, we
+ * have to update the attributes with our own id's.
+ */
+ vap->va_fsid = data->lav_dev;
+ vap->va_nodeid = data->lav_ino;
+ }
} else if (error == 0) {
/* Update the attributes with our filesystem id. */
- vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_fsid = data->lav_dev;
}
return (error);
@@ -2071,7 +2140,7 @@ lx_autofs_mkdir(vnode_t *dvp, char *nm, struct vattr *vap, vnode_t **vpp,
static void
lx_autofs_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ctp)
{
- lx_autofs_vfs_t *data = vp->v_vfsp->vfs_data;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
/*
* We need to hold the vfs lock because if we're going to free
@@ -2107,9 +2176,11 @@ lx_autofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
{
vnode_t *udvp = dvp->v_data;
vnode_t *uvp = NULL;
- lx_autofs_vfs_t *data = dvp->v_vfsp->vfs_data;
+ lx_autofs_vfs_t *data;
int error = ENOENT;
+ data = (lx_autofs_vfs_t *)dvp->v_vfsp->vfs_data;
+
/*
* For an indirect mount first try to lookup if this path component
* already exists.
@@ -2126,6 +2197,13 @@ lx_autofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
if (error != ENOENT)
return (error);
+ if (data->lav_catatonic)
+ return (ENOENT);
+
+ /* Save the uid/gid for the requestor ioctl. */
+ data->lav_uid = crgetuid(cr);
+ data->lav_gid = crgetgid(cr);
+
/* Refer the lookup to the automounter. */
if ((error = lx_autofs_automounter_call(dvp, nm)) != 0)
return (error);
@@ -2210,8 +2288,669 @@ static const fs_operation_def_t lx_autofs_tops_root[] = {
};
/*
+ * DEV-specific entry points
+ */
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ return (0);
+}
+
+static int
+lx_autofs_dev_validate_cmd(intptr_t arg, lx_autofs_dv_ioctl_t *dcmd)
+{
+ if (copyin((caddr_t)arg, dcmd, sizeof (lx_autofs_dv_ioctl_t)) != 0)
+ return (EFAULT);
+
+ if (dcmd->lad_ver_major != LX_AUTOFS_DEV_VERSION_MAJOR ||
+ dcmd->lad_ver_minor > LX_AUTOFS_DEV_VERSION_MINOR)
+ return (EINVAL);
+
+ DTRACE_PROBE1(lx__dev__cmd, void *, dcmd);
+
+ /* Fill in the version for return */
+ dcmd->lad_ver_major = LX_AUTOFS_DEV_VERSION_MAJOR;
+ dcmd->lad_ver_minor = LX_AUTOFS_DEV_VERSION_MINOR;
+ return (0);
+}
+
+static vfs_t *
+lx_autofs_dev_getvfs_bypath(char *fs_mntpt)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ vfs_t *fnd_vfs = NULL;
+ zone_t *zone = curzone;
+
+ vfs_list_read_lock();
+
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ return (NULL);
+ }
+
+ do {
+ if (vfsp->vfs_op == lx_autofs_vfsops) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(fs_mntpt, ZONE_PATH_TRANSLATE(mntpt, zone))
+ == 0) {
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ return (fnd_vfs);
+}
+
+static int
+lx_autofs_dev_fd_preamble(intptr_t arg, lx_autofs_dv_ioctl_t *dc, vfs_t **vfspp)
+{
+ int err;
+ lx_autofs_vfs_t *data;
+ file_t *fp;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, dc)) != 0)
+ return (err);
+
+ if ((fp = getf(dc->lad_ioctlfd)) == NULL)
+ return (EBADF);
+
+ vfsp = fp->f_vnode->v_vfsp;
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ releasef(dc->lad_ioctlfd);
+ return (EBADF);
+ }
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ if (data->lav_root->v_count <= 1) {
+ releasef(dc->lad_ioctlfd);
+ return (EBADF);
+ }
+
+ VFS_HOLD(vfsp);
+ *vfspp = vfsp;
+
+ releasef(dc->lad_ioctlfd);
+ return (0);
+}
+
+static int
+lx_autofs_dev_vers(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_protver(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ dcmd.lad_arg1 = LX_AUTOFS_PROTO_VERS5;
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_protosubver(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ dcmd.lad_arg1 = LX_AUTOFS_PROTO_SUBVERSION;
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_get_path_cmd(intptr_t arg, lx_autofs_dv_ioctl_t **dcp)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd, *dc;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ if (dcmd.lad_size <= sizeof (dcmd) ||
+ dcmd.lad_size > (sizeof (dcmd) + MAXPATHLEN))
+ return (EINVAL);
+
+ dc = kmem_alloc(dcmd.lad_size, KM_SLEEP);
+
+ /* re-copyin the full struct with the path */
+ if (copyin((caddr_t)arg, dc, dcmd.lad_size) != 0) {
+ kmem_free(dc, dcmd.lad_size);
+ return (EFAULT);
+ }
+ dc->lad_size = dcmd.lad_size;
+
+ if (dc->lad_path[0] != '/' ||
+ dc->lad_path[dcmd.lad_size - sizeof (dcmd) - 1] != '\0') {
+ kmem_free(dc, dcmd.lad_size);
+ return (EINVAL);
+ }
+
+ *dcp = dc;
+ return (0);
+}
+
+static int
+lx_autofs_dev_openmount(intptr_t arg)
+{
+ int err;
+ int fd;
+ lx_autofs_dv_ioctl_t *dc;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ if ((vfsp = lx_autofs_dev_getvfs_bypath(dc->lad_path)) == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ /* lad_arg1 is the dev number of the mnt but we don't check that */
+
+ /*
+ * Do an "open" on the root vnode. To fully simulate "open" we also add
+ * a hold on the root vnode itself since lx_autofs_open will only open
+ * (and hold) the underlying vnode.
+ */
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ VN_HOLD(data->lav_root);
+ if ((err = fassign(&data->lav_root, FWRITE|FREAD, &fd)) != 0) {
+ VN_RELE(data->lav_root);
+ VFS_RELE(vfsp);
+ kmem_free(dc, dc->lad_size);
+ return (err);
+ }
+
+ mutex_enter(&data->lav_lock);
+ data->lav_openmnt_cnt++;
+ mutex_exit(&data->lav_lock);
+
+ dc->lad_ioctlfd = fd;
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ mutex_enter(&data->lav_lock);
+ data->lav_openmnt_cnt--;
+ mutex_exit(&data->lav_lock);
+ (void) closeandsetf(fd, NULL);
+ VFS_RELE(vfsp);
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+ VFS_RELE(vfsp);
+
+ kmem_free(dc, dc->lad_size);
+ return (0);
+}
+
+static int
+lx_autofs_dev_closemount(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ /* "close" the vnode */
+ if ((err = closeandsetf(dcmd.lad_ioctlfd, NULL)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ mutex_enter(&data->lav_lock);
+ ASSERT(data->lav_openmnt_cnt > 0);
+ data->lav_openmnt_cnt--;
+ mutex_exit(&data->lav_lock);
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+static int
+lx_autofs_dev_ready(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if ((err = lx_autofs_ack(dcmd.lad_arg1, vfsp, LXACR_READY)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+static int
+lx_autofs_dev_fail(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if ((err = lx_autofs_ack(dcmd.lad_arg1, vfsp, LXACR_FAIL)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+/*
+ * Update the fifo pipe information we use to talk to the automounter. The
+ * ioctl is used when the automounter restarts. This logic is similar to the
+ * handling done in lx_autofs_parse_mntopt() when the filesytem is first
+ * mounted.
+ */
+static int
+lx_autofs_dev_setpipefd(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+ int fd, pgrp;
+ file_t *fp_wr, *fp_rd;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ mutex_enter(&pidlock);
+ pgrp = curproc->p_pgrp;
+ mutex_exit(&pidlock);
+ fd = dcmd.lad_arg1;
+
+ /* Lookup the new fifos. See comment in lx_autofs_parse_mntopt. */
+ if (lx_autofs_fifo_lookup(pgrp, fd, &fp_wr, &fp_rd) != 0) {
+ int pid = (int)curproc->p_pid;
+
+ if (lx_autofs_fifo_lookup(pid, fd, &fp_wr, &fp_rd) != 0) {
+ VFS_RELE(vfsp);
+ return (EINVAL);
+ }
+ }
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ /* Close the old fifos. */
+ if (data->lav_fifo_wr != NULL)
+ (void) closef(data->lav_fifo_wr);
+ if (data->lav_fifo_rd != NULL)
+ (void) closef(data->lav_fifo_rd);
+
+ data->lav_fd = fd;
+ data->lav_pgrp = pgrp;
+ data->lav_fifo_rd = fp_rd;
+ data->lav_fifo_wr = fp_wr;
+ /*
+ * Not explicitly in the ioctl spec. but necessary for correct recovery
+ */
+ data->lav_catatonic = B_FALSE;
+
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_catatonic(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ data->lav_catatonic = B_TRUE;
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_expire(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ /* If it succeeds in expiring then we don't want to return EAGAIN */
+ if ((err = lx_autofs_expire(vfsp, kcred)) == 0) {
+ VFS_RELE(vfsp);
+ return (0);
+ }
+
+ VFS_RELE(vfsp);
+ return (EAGAIN);
+}
+
+static int
+lx_autofs_dev_timeout(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ data->lav_timeout = dcmd.lad_arg1;
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_requestor(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t *dc;
+ vfs_t *vfsp;
+ vfs_t *fnd_vfs = NULL;
+ struct vfs *vfslist;
+ zone_t *zone = curzone;
+ lx_autofs_vfs_t *data;
+ uid_t uid;
+ gid_t gid;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ vfs_list_read_lock();
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ do {
+ /* Skip mounts we shouldn't show. */
+ if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(dc->lad_path,
+ ZONE_PATH_TRANSLATE(mntpt, zone)) == 0) {
+
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ /*
+ * Found an indirect mount (probably
+ * NFS) so we need to get the vfs it's
+ * mounted onto.
+ */
+ vnode_t *vn = vfsp->vfs_vnodecovered;
+ vfsp = vn->v_vfsp;
+
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ /*
+ * autofs doesn't manage this
+ * path.
+ */
+ break;
+ }
+ }
+
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+ vfs_list_unlock();
+
+ if (fnd_vfs == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ data = (lx_autofs_vfs_t *)fnd_vfs->vfs_data;
+ uid = data->lav_uid;
+ gid = data->lav_gid;
+ VFS_RELE(fnd_vfs);
+
+ dc->lad_arg1 = uid;
+ dc->lad_arg2 = gid;
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+
+ kmem_free(dc, dc->lad_size);
+ return (0);
+}
+
+static int
+lx_autofs_dev_ismntpt(intptr_t arg)
+{
+ int err = 0;
+ lx_autofs_dv_ioctl_t *dc;
+ struct vfs *vfslist;
+ vfs_t *vfsp;
+ vfs_t *fnd_vfs = NULL;
+ zone_t *zone = curzone;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ /*
+ * The automounter will always pass a path. It can also either pass an
+ * ioctlfd or, if it's -1, arg1 can be an LX_AUTOFS_TYPE_* value. We
+ * currently don't need those for our algorithm.
+ */
+
+ vfs_list_read_lock();
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ kmem_free(dc, dc->lad_size);
+ return (0); /* return 0 if not a mount point */
+ }
+
+ do {
+ if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(dc->lad_path,
+ ZONE_PATH_TRANSLATE(mntpt, zone)) == 0) {
+
+ /*
+ * To handle direct mounts (on top of an autofs
+ * mount), we must prefer non-autofs vfs for
+ * this request.
+ */
+ if (fnd_vfs != NULL)
+ VFS_RELE(fnd_vfs);
+
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+
+ if (fnd_vfs->vfs_op != lx_autofs_vfsops)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+ vfs_list_unlock();
+
+ if (fnd_vfs == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (0); /* return 0 if not a mount point */
+ }
+
+ /*
+ * arg1 is device number, arg2 is superblock magic number
+ * The superblock value only matters if autofs or not.
+ */
+ dc->lad_arg1 = fnd_vfs->vfs_dev;
+ if (fnd_vfs->vfs_op == lx_autofs_vfsops) {
+ dc->lad_arg2 = LX_AUTOFS_SB_MAGIC;
+ } else {
+ dc->lad_arg2 = ~LX_AUTOFS_SB_MAGIC;
+ }
+
+ VFS_RELE(fnd_vfs);
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+
+ kmem_free(dc, dc->lad_size);
+
+ /*
+ * We have to return 1 if it is a mount point. The lx ioctl autofs
+ * translator will convert a negative value back to a positive,
+ * non-error return value.
+ */
+ return (-1);
+}
+
+static int
+lx_autofs_dev_askumount(intptr_t arg)
+{
+ int err;
+ int v;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if (lx_autofs_may_unmount(vfsp, kcred)) {
+ v = 0;
+ } else {
+ v = 1;
+ }
+ VFS_RELE(vfsp);
+
+ dcmd.lad_arg1 = v;
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ switch (cmd) {
+ case LX_AUTOFS_DEV_IOC_VERSION_CMD:
+ return (lx_autofs_dev_vers(arg));
+
+ case LX_AUTOFS_DEV_IOC_PROTOVER_CMD:
+ return (lx_autofs_dev_protver(arg));
+
+ case LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD:
+ return (lx_autofs_dev_protosubver(arg));
+
+ case LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD:
+ return (lx_autofs_dev_openmount(arg));
+
+ case LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD:
+ return (lx_autofs_dev_closemount(arg));
+
+ case LX_AUTOFS_DEV_IOC_READY_CMD:
+ return (lx_autofs_dev_ready(arg));
+
+ case LX_AUTOFS_DEV_IOC_FAIL_CMD:
+ return (lx_autofs_dev_fail(arg));
+
+ case LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD:
+ return (lx_autofs_dev_setpipefd(arg));
+
+ case LX_AUTOFS_DEV_IOC_CATATONIC_CMD:
+ return (lx_autofs_dev_catatonic(arg));
+
+ case LX_AUTOFS_DEV_IOC_TIMEOUT_CMD:
+ return (lx_autofs_dev_timeout(arg));
+
+ case LX_AUTOFS_DEV_IOC_REQUESTER_CMD:
+ return (lx_autofs_dev_requestor(arg));
+
+ case LX_AUTOFS_DEV_IOC_EXPIRE_CMD:
+ return (lx_autofs_dev_expire(arg));
+
+ case LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD:
+ return (lx_autofs_dev_askumount(arg));
+
+ case LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD:
+ return (lx_autofs_dev_ismntpt(arg));
+ }
+
+ return (EINVAL);
+}
+
+/*
* lx_autofs_init() gets invoked via the mod_install() call in
- * this modules _init() routine. Therefor, the code that cleans
+ * this module's _init() routine. Therefore, the code that cleans
* up the structures we allocate below is actually found in
* our _fini() routine.
*/
@@ -2221,22 +2960,7 @@ lx_autofs_init(int fstype, char *name)
{
int error;
- if ((lx_autofs_major =
- (major_t)space_fetch(LX_AUTOFS_SPACE_KEY_UDEV)) == 0) {
-
- if ((lx_autofs_major = getudev()) == (major_t)-1) {
- cmn_err(CE_WARN, "lx_autofs_init: "
- "can't get unique device number");
- return (EAGAIN);
- }
-
- if (space_store(LX_AUTOFS_SPACE_KEY_UDEV,
- (uintptr_t)lx_autofs_major) != 0) {
- cmn_err(CE_WARN, "lx_autofs_init: "
- "can't save unique device number");
- return (EAGAIN);
- }
- }
+ lx_autofs_major = ddi_name_to_major(LX_AUTOFS_NAME);
lx_autofs_fstype = fstype;
if ((error = vfs_setfsops(fstype, lx_autofs_vfstops,
@@ -2255,6 +2979,78 @@ lx_autofs_init(int fstype, char *name)
return (0);
}
+/*ARGSUSED*/
+static int
+lx_autofs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int instance = ddi_get_instance(dip);
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ ASSERT(instance == 0);
+ if (instance != 0)
+ return (DDI_FAILURE);
+
+ /* create our minor node */
+ if (ddi_create_minor_node(dip, LX_AUTOFS_MINORNAME, S_IFCHR, 0,
+ DDI_PSEUDO, 0) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ lx_autofs_dip = dip;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ lx_autofs_dip = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **resultp)
+{
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *resultp = lx_autofs_dip;
+ return (DDI_SUCCESS);
+
+ case DDI_INFO_DEVT2INSTANCE:
+ *resultp = (void *)0;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+/*
+ * Driver flags
+ */
+static struct cb_ops lx_autofs_cb_ops = {
+ lx_autofs_dev_open, /* open */
+ lx_autofs_dev_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ lx_autofs_dev_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* vb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
/*
* Module linkage
@@ -2282,20 +3078,48 @@ static vfsdef_t vfw = {
&lx_autofs_mntopts
};
+static struct dev_ops lx_autofs_dev_ops = {
+ DEVO_REV, /* version */
+ 0, /* refcnt */
+ lx_autofs_info, /* info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ lx_autofs_attach, /* attach */
+ lx_autofs_detach, /* detach */
+ nodev, /* reset */
+ &lx_autofs_cb_ops, /* driver operations */
+ NULL, /* no bus operations */
+ NULL, /* power */
+ ddi_quiesce_not_needed /* quiesce */
+};
+
extern struct mod_ops mod_fsops;
static struct modlfs modlfs = {
&mod_fsops, "lx autofs filesystem", &vfw
};
+static struct modldrv modldrv = {
+ &mod_driverops, "lx autofs driver", &lx_autofs_dev_ops
+};
+
static struct modlinkage modlinkage = {
- MODREV_1, (void *)&modlfs, NULL
+ MODREV_1,
+ (void *)&modlfs,
+ (void *)&modldrv,
+ NULL
};
int
_init(void)
{
- return (mod_install(&modlinkage));
+ int error;
+
+ if ((error = mod_install(&modlinkage)) != 0) {
+ return (error);
+ }
+
+ return (0);
}
int
diff --git a/usr/src/uts/common/brand/lx/sys/lx_autofs.h b/usr/src/uts/common/brand/lx/sys/lx_autofs.h
index ae3b6a7315..17b19895f4 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_autofs.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_autofs.h
@@ -31,46 +31,46 @@
#define _LX_AUTOFS_H
/*
- * The lxautofs filesystem exists to emulate the Linux autofs filesystem
- * and provide support for the Linux "automount" automounter.
+ * The lxautofs filesystem and driver exist to emulate the Linux autofs
+ * filesystem and /dev/autofs device (this code emulates both). The
+ * purpose is to provide support for the Linux "automount" automounter.
*
- * We emulate parts of the Linux autofs v4 file system (which confusingly uses
- * the v5 autofs protocol to user-land).
+ * The device ioctls map fairly closely to the filesystem ioctls. The device
+ * ioctls have superseded the filesystem ioctls and the automounter will
+ * use the device ioctls if the device exists.
+ *
+ * The device ioctls are used by the automounter to perform recovery
+ * in cases where the automounter is restarted while mounts are present. It
+ * also allows for better management operations when a filesystem is mounted
+ * on top of an autofs mountpoint, as in the case of an NFS direct mount on
+ * top of an autofs mount.
*
*
* +++ Linux automounter background.
*
- * Linux has two automounters: "amd" and "automount"
- *
- * 1) "amd" is a userland NFS server. It basically mounts an NFS filesystem
- * at an automount point, and it acts as the NFS server for the mount. When
- * an access is done to that NFS filesystem, the access is redirected by the
- * kernel to the "amd" process via rpc. "amd" then looks up any information
- * required to resolve the requests, mounts real NFS filesystems if
- * necessary, and returns. "amd" has it's own strange configuration
- * mechanism that doesn't seem to be very compatabile with illumos's network
- * based automounter map support.
- *
- * 2) "automount" is the other Linux automounter. It utilizes a kernel
- * filesystem (autofs) to provide it's functionality. Basically, it mounts
- * the autofs filesystem at any automounter controlled mountpoint. This
- * filesystem then intercepts and redirects lookup operations (or expire ops)
+ * Linux has two automounters: "amd" (not used in any popular, modern distro)
+ * and "automount".
+ *
+ * "automount" is the normal Linux automounter. It utilizes a kernel
+ * filesystem (autofs) and device (/dev/autofs) to provide its functionality.
+ * Basically, it mounts the autofs filesystem at any automounter controlled
+ * mountpoint. This filesystem then intercepts and redirects lookup operations
* to the userland automounter process via a pipe. The pipe to the automounter
- * is established via mount options when the autofs filesystem is mounted. When
- * the automounter recieves a request via this pipe, it does lookups (or
- * unmounts) to whatever backing store it's configured to use, does mkdir
- * operations on the autofs filesystem, mounts remote NFS filesystems on any
- * leaf directories it just created, and signals the autofs filesystem via an
- * ioctl to let it know that the lookup (or expire) can continue.
+ * is established via a mount option when the autofs filesystem is mounted or
+ * via the setpipefd ioctl if the automounter restarts. When the automounter
+ * receives a request via this pipe, it does lookups (or unmounts) to whatever
+ * backing store it's configured to use, does mkdir operations on the autofs
+ * filesystem, mounts remote NFS filesystems on any directories it manages or
+ * just created, and signals the autofs device via an ioctl to let it know
+ * that the lookup (or expire) can continue. Other management operations (such
+ * as querying expiration for unmounting) are performed using the autofs device.
*
*
* +++ Linux autofs documentation.
*
* Within the Linux src tree, see the file:
- * Documentation/filesystems/autofs4-mount-control.txt. This documents some
- * of the autofs behavior and the dev ioctls (which we currently do not
- * support). The dev ioctls are used for recovery if the automounter dies, or
- * is killed, and restarted.
+ * Documentation/filesystems/autofs4-mount-control.txt
+ * This documents some of the autofs behavior and the device driver ioctls.
*
* The following URL (https://lwn.net/Articles/606960/) documents autofs in
* general. This patch was targeted for Documentation/filesystems/autofs4.txt,
@@ -79,14 +79,23 @@
*
* +++ Linux autofs (and automount daemon) notes
*
- * Since we're mimicking the behavior of the Linux autofs filesystem it's
- * important to document some of it's observed behavior here. There are
- * multiple versions of the autofs kernel API protocol and modern
- * implementations of the user-land automount daemon depend on v5.
+ * Since we're mimicking the behavior of the Linux autofs filesystem and
+ * device, we document some of the observed behavior here.
+ *
+ * There are multiple versions of the autofs filesystem kernel API protocol
+ * and modern implementations of the user-land automount daemon would depend
+ * on v5, although the filesystem API has been superseded by the driver ioctl
+ * API, which is roughly similar.
*
- * Our original autofs implementation was developed in the mid-2000s around
- * the v2 protocol, but that is currently obsolete. Our current implementation
- * is based around the v5 protocol API.
+ * We'll describe the filesystem ioctls first, since support for those was
+ * implemented first. The device ioctls roughly correspond to the filesystem
+ * ioctls and were implemented last, but the automounter will use those
+ * ioctls, instead of the filesystem ioctls, when the device is present.
+ *
+ * Our original autofs implementation was developed in the mid-2000s around the
+ * v2 protocol, but that is currently obsolete. Our current implementation is
+ * based around the v5 protocol API. There was no autofs device support at that
+ * time.
*
* The autoumounter supports 3 different, mutually exclusive, mount options for
* each mountpoint:
@@ -203,6 +212,11 @@
* obtain the name of a mountpoint which the automounter can unmount.
* Unmounting is dicussed in more detail below.
*
+ * H) The device ioctls roughly correspond to the filesystem ioctls, but
+ * instead of being tied to an auotfs mountpoint vnode, they can be called any
+ * time. The argument structure uses either a path or an autofs pipe file
+ * descriptor to indicate what is being operated on.
+ *
* +++ lxautofs notes
*
* 1) In general, the lxautofs filesystem tries to mimic the behavior of the
@@ -264,14 +278,14 @@
* 4) Unmounting
*
* The automounter has a timeout associated with each mount. It informs autofs
- * of this timeout using the LX_AUTOFS_IOC_SETTIMEOUT ioctl after autofs has
- * been mounted on the mountpoint.
+ * of this timeout using the LX_AUTOFS_DEV_IOC_TIMEOUT_CMD ioctl after autofs
+ * has been mounted on the mountpoint.
*
* After the automounter has mounted something associated with the mountpoint
* then periodically (<timeout>/4 seconds) the automounter will issue the
- * LX_AUTOFS_IOC_EXPIRE_MULTI ioctl on the autofs mount. autofs is expected to
- * respond with one or more underlying mountpoint entries which are candidates
- * for unmounting. The automounter will attempt to unmount the filesystem
+ * LX_AUTOFS_DEV_IOC_EXPIRE_CMD ioctl on the autofs mount. autofs is expected
+ * to respond with an underlying mountpoint entry which is a candidate for
+ * unmounting. The automounter will attempt to unmount the filesystem
* (which may fail if it is busy, since this is obviously racy) and then
* acknowledge the expire ioctl. The successful acknowledgement is independent
* of the success of unmounting the underlying filesystem.
@@ -282,13 +296,13 @@
* To support 'indirect' mount expiration, the autofs vfs keeps track of the
* filesystems mounted immediately under the autofs mountpoint (in
* lav_mnt_list) after a lookup has completed successfully. Upon receipt of the
- * LX_AUTOFS_IOC_EXPIRE_MULTI ioctl, autofs removes the first element from the
- * list, attempts to check if it is busy and if not, returns that mountpoint
- * as the ioctl response (if busy the entry is added to the end of the list).
- * When the ioctl is acknowledged, if the mountpoint still exists, that
- * means the unmount failed and the entry is added at the back of the list. If
- * there are no elements or the first one is busy, EAGAIN is returned for the
- * 'expire' ioctl and the autoumounter will check again in <timeout>/4 seconds.
+ * LX_AUTOFS_IOC_DEV_EXPIRE_CMD ioctl, autofs removes the first element from
+ * the list, attempts to check if it is busy and if not, returns that mountpoint
+ * over the fifo (if busy the entry is added to the end of the list). When the
+ * ioctl is acknowledged, if the mountpoint still exists, that means the unmount
+ * failed and the entry is added at the back of the list. If there are no
+ * elements or the first one is busy, EAGAIN is returned for the 'expire' ioctl
+ * and the autoumounter will check again in <timeout>/4 seconds.
*
* For example, if /home is an autofs indirect mount, then there are typically
* many different {username}-specific NFS mounts under that /home autofs mount.
@@ -333,6 +347,21 @@
* mounts under /net/jurassic. We umount those using the lx_autofs_umount_offset
* function and respond to the automounter expire ioctl with "jurassic", in the
* same way as we would for any other indirect mount.
+ *
+ * 5) Recovery
+ *
+ * If the automounter is restarted for any reason, it needs to cope with
+ * pre-existing autofs mounts, as well as other automount-initiated mounts (e.g.
+ * a direct mount on top of an autofs mountpoint). The automounter uses the
+ * /proc/mounts file to correlate mounts to the managed mountpoints. It then
+ * uses the /dev/autofs device to openmount each of the autofs devices and
+ * reinitialize them using the various dev ioctls (timeout, requester, etc.).
+ *
+ * In general, the autoumounter will closemount the mountpoint once it's done,
+ * but it doesn't in the case of an offset mountpoint with nothing mounted
+ * on top. In this case the automounter expects autofs to expire that mountpoint
+ * before it will closemount (so things can subsequently cleanup). We handle
+ * this special case in the expire code path.
*/
#ifdef __cplusplus
@@ -346,6 +375,8 @@ extern "C" {
*/
#define LX_AUTOFS_NAME "lxautofs"
+#define LX_AUTOFS_MINORNAME "autofs"
+
/*
* Mount options supported.
*/
@@ -455,6 +486,24 @@ union lx_autofs_pkt {
#define LX_AUTOFS_IOC_EXPIRE_INDIRECT LX_AUTOFS_IOC_EXPIRE_MULTI
#define LX_AUTOFS_IOC_EXPIRE_DIRECT LX_AUTOFS_IOC_EXPIRE_MULTI
+/*
+ * autofs device ioctls
+ */
+#define LX_AUTOFS_DEV_IOC_VERSION_CMD 0xc0189371
+#define LX_AUTOFS_DEV_IOC_PROTOVER_CMD 0xc0189372
+#define LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD 0xc0189373
+#define LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD 0xc0189374
+#define LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD 0xc0189375
+#define LX_AUTOFS_DEV_IOC_READY_CMD 0xc0189376
+#define LX_AUTOFS_DEV_IOC_FAIL_CMD 0xc0189377
+#define LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD 0xc0189378
+#define LX_AUTOFS_DEV_IOC_CATATONIC_CMD 0xc0189379
+#define LX_AUTOFS_DEV_IOC_TIMEOUT_CMD 0xc018937a
+#define LX_AUTOFS_DEV_IOC_REQUESTER_CMD 0xc018937b
+#define LX_AUTOFS_DEV_IOC_EXPIRE_CMD 0xc018937c
+#define LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD 0xc018937d
+#define LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD 0xc018937e
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h b/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h
index 508b800a3c..39ea96d1fe 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h
@@ -93,11 +93,21 @@ typedef struct lx_autofs_vfs {
*/
ulong_t lav_timeout;
+ /* ioctl-set catatonic value (prevents future mounts). */
+ boolean_t lav_catatonic;
+
+ /* Mount initiator's uid/gid for recovery handling. */
+ uid_t lav_uid;
+ gid_t lav_gid;
+
/* Each automount requests needs a unique id. */
id_space_t *lav_ids;
/* All remaining structure members are protected by lav_lock. */
kmutex_t lav_lock;
+ /* openmount counter */
+ int lav_openmnt_cnt;
+
/* Hashes to keep track of outstanding automounter requests. */
mod_hash_t *lav_path_hash;
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c b/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c
index f7a18ac481..5fb58e26ea 100644
--- a/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c
+++ b/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2015 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/errno.h>
@@ -1194,6 +1194,27 @@ ict_siocgifconf(file_t *fp, int cmd, intptr_t arg, int lxcmd)
return (ict_siocgifconf32(fp, cmd, arg, lxcmd));
}
+/*
+ * Unfortunately some of the autofs ioctls want to return a positive integer
+ * result which does not indicate an error. To minimize disruption in the
+ * rest of the code, we'll treat a positive return as an errno and a negative
+ * return as the non-error return (which we then negate).
+ */
+static int
+ict_autofs(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int res = 0;
+ int rv;
+
+ res = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+ if (res > 0)
+ return (set_errno(res));
+ if (res == 0)
+ return (0);
+ return (-res);
+}
+
/* Structure used to define an ioctl translator. */
typedef struct lx_ioc_cmd_translator {
int lict_lxcmd;
@@ -1309,6 +1330,22 @@ static lx_ioc_cmd_translator_t lx_ioc_xlate_autofs[] = {
LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_PROTOSUBVER)
LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_ASKUMOUNT)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_VERSION_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_PROTOVER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_READY_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_FAIL_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_CATATONIC_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_TIMEOUT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_REQUESTER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_EXPIRE_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD,
+ ict_autofs)
+
LX_IOC_CMD_TRANSLATOR_END
};
diff --git a/usr/src/uts/intel/lxautofs/Makefile b/usr/src/uts/intel/lxautofs/Makefile
index 606da5e632..5de66af48f 100644
--- a/usr/src/uts/intel/lxautofs/Makefile
+++ b/usr/src/uts/intel/lxautofs/Makefile
@@ -47,7 +47,9 @@ UTSBASE = ../..
MODULE = lxautofs
OBJECTS = $(LX_AUTOFS_OBJS:%=$(OBJS_DIR)/%)
LINTS = $(LX_AUTOFS_OBJS:%.o=$(LINTS_DIR)/%.ln)
-ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+ROOTLINK = $(USR_FS_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/brand/lx/autofs
INC_PATH += -I$(UTSBASE)/common/brand/lx
@@ -61,7 +63,7 @@ include $(UTSBASE)/intel/Makefile.intel
#
ALL_TARGET = $(BINARY)
LINT_TARGET = $(MODULE).lint
-INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
#
# Overrides.
@@ -97,6 +99,9 @@ clean.lint: $(CLEAN_LINT_DEPS)
install: $(INSTALL_DEPS)
+$(ROOTLINK): $(ROOT_FS_DIR) $(ROOTMODULE)
+ -$(RM) $@; ln $(ROOTMODULE) $@
+
#
# Include common targets.
#