17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
539d3e169Sevanl * Common Development and Distribution License (the "License").
639d3e169Sevanl * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
21a574db85Sraf
227c478bd9Sstevel@tonic-gate /*
23f798ee53SJan Kryl * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
267c478bd9Sstevel@tonic-gate #include <sys/param.h>
277c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
287c478bd9Sstevel@tonic-gate #include <sys/errno.h>
297c478bd9Sstevel@tonic-gate #include <sys/proc.h>
307c478bd9Sstevel@tonic-gate #include <sys/disp.h>
317c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
327c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
337c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
347c478bd9Sstevel@tonic-gate #include <sys/cred.h>
357c478bd9Sstevel@tonic-gate #include <sys/mount.h>
367c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
377c478bd9Sstevel@tonic-gate #include <sys/debug.h>
387c478bd9Sstevel@tonic-gate #include <sys/systm.h>
397c478bd9Sstevel@tonic-gate #include <sys/dirent.h>
407c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
417c478bd9Sstevel@tonic-gate #include <sys/fs/autofs.h>
427c478bd9Sstevel@tonic-gate #include <sys/callb.h>
437c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
447c478bd9Sstevel@tonic-gate #include <sys/zone.h>
4539d3e169Sevanl #include <sys/door.h>
467c478bd9Sstevel@tonic-gate #include <sys/fs/mntdata.h>
4739d3e169Sevanl #include <nfs/mount.h>
4839d3e169Sevanl #include <rpc/clnt.h>
4939d3e169Sevanl #include <rpcsvc/autofs_prot.h>
5039d3e169Sevanl #include <nfs/rnode.h>
5139d3e169Sevanl #include <sys/utsname.h>
52a574db85Sraf #include <sys/schedctl.h>
537c478bd9Sstevel@tonic-gate
547c478bd9Sstevel@tonic-gate /*
557c478bd9Sstevel@tonic-gate * Autofs and Zones:
567c478bd9Sstevel@tonic-gate *
577c478bd9Sstevel@tonic-gate * Zones are delegated the responsibility of managing their own autofs mounts
587c478bd9Sstevel@tonic-gate * and maps. Each zone runs its own copy of automountd, with its own timeouts,
597c478bd9Sstevel@tonic-gate * and other logically "global" parameters. kRPC and virtualization in the
607c478bd9Sstevel@tonic-gate * loopback transport (tl) will prevent a zone from communicating with another
617c478bd9Sstevel@tonic-gate * zone's automountd.
627c478bd9Sstevel@tonic-gate *
637c478bd9Sstevel@tonic-gate * Each zone has its own "rootfnnode" and associated tree of auto nodes.
647c478bd9Sstevel@tonic-gate *
657c478bd9Sstevel@tonic-gate * Each zone also has its own set of "unmounter" kernel threads; these are
667c478bd9Sstevel@tonic-gate * created and run within the zone's context (ie, they are created via
677c478bd9Sstevel@tonic-gate * zthread_create()).
687c478bd9Sstevel@tonic-gate *
697c478bd9Sstevel@tonic-gate * Cross-zone mount triggers are disallowed. There is a check in
707c478bd9Sstevel@tonic-gate * auto_trigger_mount() to this effect; EPERM is returned to indicate that the
717c478bd9Sstevel@tonic-gate * mount is not owned by the caller.
727c478bd9Sstevel@tonic-gate *
737c478bd9Sstevel@tonic-gate * autofssys() enables a caller in the global zone to clean up in-kernel (as
747c478bd9Sstevel@tonic-gate * well as regular) autofs mounts via the unmount_tree() mechanism. This is
757c478bd9Sstevel@tonic-gate * routinely done when all mounts are removed as part of zone shutdown.
767c478bd9Sstevel@tonic-gate */
777c478bd9Sstevel@tonic-gate #define TYPICALMAXPATHLEN 64
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate static kmutex_t autofs_nodeid_lock;
807c478bd9Sstevel@tonic-gate
81f798ee53SJan Kryl /* max number of unmount threads running */
82f798ee53SJan Kryl static int autofs_unmount_threads = 5;
83f798ee53SJan Kryl static int autofs_unmount_thread_timer = 120; /* in seconds */
84f798ee53SJan Kryl
857c478bd9Sstevel@tonic-gate static int auto_perform_link(fnnode_t *, struct linka *, cred_t *);
867c478bd9Sstevel@tonic-gate static int auto_perform_actions(fninfo_t *, fnnode_t *,
877c478bd9Sstevel@tonic-gate action_list *, cred_t *);
887c478bd9Sstevel@tonic-gate static int auto_getmntpnt(vnode_t *, char *, vnode_t **, cred_t *);
897c478bd9Sstevel@tonic-gate static int auto_lookup_request(fninfo_t *, char *, struct linka *,
903bfb48feSsemery bool_t, bool_t *, cred_t *);
913bfb48feSsemery static int auto_mount_request(fninfo_t *, char *, action_list **, cred_t *,
9239d3e169Sevanl bool_t);
9339d3e169Sevanl
947c478bd9Sstevel@tonic-gate /*
957c478bd9Sstevel@tonic-gate * Clears the MF_INPROG flag, and wakes up those threads sleeping on
967c478bd9Sstevel@tonic-gate * fn_cv_mount if MF_WAITING is set.
977c478bd9Sstevel@tonic-gate */
987c478bd9Sstevel@tonic-gate void
auto_unblock_others(fnnode_t * fnp,uint_t operation)997c478bd9Sstevel@tonic-gate auto_unblock_others(
1007c478bd9Sstevel@tonic-gate fnnode_t *fnp,
1017c478bd9Sstevel@tonic-gate uint_t operation) /* either MF_INPROG or MF_LOOKUP */
1027c478bd9Sstevel@tonic-gate {
1037c478bd9Sstevel@tonic-gate ASSERT(operation & (MF_INPROG | MF_LOOKUP));
1047c478bd9Sstevel@tonic-gate fnp->fn_flags &= ~operation;
1057c478bd9Sstevel@tonic-gate if (fnp->fn_flags & MF_WAITING) {
1067c478bd9Sstevel@tonic-gate fnp->fn_flags &= ~MF_WAITING;
1077c478bd9Sstevel@tonic-gate cv_broadcast(&fnp->fn_cv_mount);
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate
1117c478bd9Sstevel@tonic-gate int
auto_wait4mount(fnnode_t * fnp)1127c478bd9Sstevel@tonic-gate auto_wait4mount(fnnode_t *fnp)
1137c478bd9Sstevel@tonic-gate {
1147c478bd9Sstevel@tonic-gate int error;
1157c478bd9Sstevel@tonic-gate k_sigset_t smask;
1167c478bd9Sstevel@tonic-gate
1177c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_wait4mount: fnp=%p\n", (void *)fnp));
1187c478bd9Sstevel@tonic-gate
1197c478bd9Sstevel@tonic-gate mutex_enter(&fnp->fn_lock);
1207c478bd9Sstevel@tonic-gate while (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) {
1217c478bd9Sstevel@tonic-gate /*
1227c478bd9Sstevel@tonic-gate * There is a mount or a lookup in progress.
1237c478bd9Sstevel@tonic-gate */
1247c478bd9Sstevel@tonic-gate fnp->fn_flags |= MF_WAITING;
1257c478bd9Sstevel@tonic-gate sigintr(&smask, 1);
1267c478bd9Sstevel@tonic-gate if (!cv_wait_sig(&fnp->fn_cv_mount, &fnp->fn_lock)) {
1277c478bd9Sstevel@tonic-gate /*
1287c478bd9Sstevel@tonic-gate * Decided not to wait for operation to
1297c478bd9Sstevel@tonic-gate * finish after all.
1307c478bd9Sstevel@tonic-gate */
1317c478bd9Sstevel@tonic-gate sigunintr(&smask);
1327c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
1337c478bd9Sstevel@tonic-gate return (EINTR);
1347c478bd9Sstevel@tonic-gate }
1357c478bd9Sstevel@tonic-gate sigunintr(&smask);
1367c478bd9Sstevel@tonic-gate }
1377c478bd9Sstevel@tonic-gate error = fnp->fn_error;
1387c478bd9Sstevel@tonic-gate
1397c478bd9Sstevel@tonic-gate if (error == EINTR) {
1407c478bd9Sstevel@tonic-gate /*
1417c478bd9Sstevel@tonic-gate * The thread doing the mount got interrupted, we need to
1427c478bd9Sstevel@tonic-gate * try again, by returning EAGAIN.
1437c478bd9Sstevel@tonic-gate */
1447c478bd9Sstevel@tonic-gate error = EAGAIN;
1457c478bd9Sstevel@tonic-gate }
1467c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
1477c478bd9Sstevel@tonic-gate
1487c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_wait4mount: fnp=%p error=%d\n", (void *)fnp,
1497c478bd9Sstevel@tonic-gate error));
1507c478bd9Sstevel@tonic-gate return (error);
1517c478bd9Sstevel@tonic-gate }
1527c478bd9Sstevel@tonic-gate
1537c478bd9Sstevel@tonic-gate int
auto_lookup_aux(fnnode_t * fnp,char * name,cred_t * cred)1547c478bd9Sstevel@tonic-gate auto_lookup_aux(fnnode_t *fnp, char *name, cred_t *cred)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate struct fninfo *fnip;
1577c478bd9Sstevel@tonic-gate struct linka link;
1587c478bd9Sstevel@tonic-gate bool_t mountreq = FALSE;
1597c478bd9Sstevel@tonic-gate int error = 0;
1607c478bd9Sstevel@tonic-gate
1617c478bd9Sstevel@tonic-gate fnip = vfstofni(fntovn(fnp)->v_vfsp);
1627c478bd9Sstevel@tonic-gate bzero(&link, sizeof (link));
1633bfb48feSsemery error = auto_lookup_request(fnip, name, &link, TRUE, &mountreq, cred);
1647c478bd9Sstevel@tonic-gate if (!error) {
165*ed1b18c7SToomas Soome if (link.link != NULL) {
166*ed1b18c7SToomas Soome error = ENOENT;
1677c478bd9Sstevel@tonic-gate /*
1687c478bd9Sstevel@tonic-gate * This node should be a symlink
1697c478bd9Sstevel@tonic-gate */
170*ed1b18c7SToomas Soome if (*link.link != '\0')
171*ed1b18c7SToomas Soome error = auto_perform_link(fnp, &link, cred);
1727c478bd9Sstevel@tonic-gate } else if (mountreq) {
1737c478bd9Sstevel@tonic-gate /*
1747c478bd9Sstevel@tonic-gate * The automount daemon is requesting a mount,
1757c478bd9Sstevel@tonic-gate * implying this entry must be a wildcard match and
1767c478bd9Sstevel@tonic-gate * therefore in need of verification that the entry
1777c478bd9Sstevel@tonic-gate * exists on the server.
1787c478bd9Sstevel@tonic-gate */
1797c478bd9Sstevel@tonic-gate mutex_enter(&fnp->fn_lock);
1807c478bd9Sstevel@tonic-gate AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
1817c478bd9Sstevel@tonic-gate fnp->fn_error = 0;
1827c478bd9Sstevel@tonic-gate
1837c478bd9Sstevel@tonic-gate /*
1847c478bd9Sstevel@tonic-gate * Unblock other lookup requests on this node,
1857c478bd9Sstevel@tonic-gate * this is needed to let the lookup generated by
1867c478bd9Sstevel@tonic-gate * the mount call to complete. The caveat is
1877c478bd9Sstevel@tonic-gate * other lookups on this node can also get by,
1887c478bd9Sstevel@tonic-gate * i.e., another lookup on this node that occurs
1897c478bd9Sstevel@tonic-gate * while this lookup is attempting the mount
1907c478bd9Sstevel@tonic-gate * would return a positive result no matter what.
1917c478bd9Sstevel@tonic-gate * Therefore two lookups on the this node could
1927c478bd9Sstevel@tonic-gate * potentially get disparate results.
1937c478bd9Sstevel@tonic-gate */
1947c478bd9Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP);
1957c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
1967c478bd9Sstevel@tonic-gate /*
1977c478bd9Sstevel@tonic-gate * auto_new_mount_thread fires up a new thread which
1987c478bd9Sstevel@tonic-gate * calls automountd finishing up the work
1997c478bd9Sstevel@tonic-gate */
2007c478bd9Sstevel@tonic-gate auto_new_mount_thread(fnp, name, cred);
2017c478bd9Sstevel@tonic-gate
2027c478bd9Sstevel@tonic-gate /*
2037c478bd9Sstevel@tonic-gate * At this point, we are simply another thread
2047c478bd9Sstevel@tonic-gate * waiting for the mount to complete
2057c478bd9Sstevel@tonic-gate */
2067c478bd9Sstevel@tonic-gate error = auto_wait4mount(fnp);
2077c478bd9Sstevel@tonic-gate if (error == AUTOFS_SHUTDOWN)
2087c478bd9Sstevel@tonic-gate error = ENOENT;
2097c478bd9Sstevel@tonic-gate }
2107c478bd9Sstevel@tonic-gate }
2117c478bd9Sstevel@tonic-gate
21239d3e169Sevanl if (link.link)
21339d3e169Sevanl kmem_free(link.link, strlen(link.link) + 1);
21439d3e169Sevanl if (link.dir)
21539d3e169Sevanl kmem_free(link.dir, strlen(link.dir) + 1);
2167c478bd9Sstevel@tonic-gate mutex_enter(&fnp->fn_lock);
2177c478bd9Sstevel@tonic-gate fnp->fn_error = error;
2187c478bd9Sstevel@tonic-gate
2197c478bd9Sstevel@tonic-gate /*
2207c478bd9Sstevel@tonic-gate * Notify threads waiting for lookup/mount that
2217c478bd9Sstevel@tonic-gate * it's done.
2227c478bd9Sstevel@tonic-gate */
2237c478bd9Sstevel@tonic-gate if (mountreq) {
2247c478bd9Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2257c478bd9Sstevel@tonic-gate } else {
2267c478bd9Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP);
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
2297c478bd9Sstevel@tonic-gate return (error);
2307c478bd9Sstevel@tonic-gate }
2317c478bd9Sstevel@tonic-gate
2327c478bd9Sstevel@tonic-gate /*
2337c478bd9Sstevel@tonic-gate * Starting point for thread to handle mount requests with automountd.
2347c478bd9Sstevel@tonic-gate * XXX auto_mount_thread() is not suspend-safe within the scope of
2357c478bd9Sstevel@tonic-gate * the present model defined for cpr to suspend the system. Calls
2367c478bd9Sstevel@tonic-gate * made by the auto_mount_thread() that have been identified to be unsafe
2377c478bd9Sstevel@tonic-gate * are (1) RPC client handle setup and client calls to automountd which
2387c478bd9Sstevel@tonic-gate * can block deep down in the RPC library, (2) kmem_alloc() calls with the
2397c478bd9Sstevel@tonic-gate * KM_SLEEP flag which can block if memory is low, and (3) VFS_*(), and
2407c478bd9Sstevel@tonic-gate * lookuppnvp() calls which can result in over the wire calls to servers.
2417c478bd9Sstevel@tonic-gate * The thread should be completely reevaluated to make it suspend-safe in
2427c478bd9Sstevel@tonic-gate * case of future updates to the cpr model.
2437c478bd9Sstevel@tonic-gate */
2447c478bd9Sstevel@tonic-gate static void
auto_mount_thread(struct autofs_callargs * argsp)2457c478bd9Sstevel@tonic-gate auto_mount_thread(struct autofs_callargs *argsp)
2467c478bd9Sstevel@tonic-gate {
24739d3e169Sevanl struct fninfo *fnip;
24839d3e169Sevanl fnnode_t *fnp;
24939d3e169Sevanl vnode_t *vp;
25039d3e169Sevanl char *name;
25139d3e169Sevanl size_t namelen;
25239d3e169Sevanl cred_t *cred;
25339d3e169Sevanl action_list *alp = NULL;
25439d3e169Sevanl int error;
25539d3e169Sevanl callb_cpr_t cprinfo;
25639d3e169Sevanl kmutex_t auto_mount_thread_cpr_lock;
2577c478bd9Sstevel@tonic-gate
2587c478bd9Sstevel@tonic-gate mutex_init(&auto_mount_thread_cpr_lock, NULL, MUTEX_DEFAULT, NULL);
25939d3e169Sevanl CALLB_CPR_INIT(&cprinfo, &auto_mount_thread_cpr_lock,
260d6c8399bSrmesta callb_generic_cpr, "auto_mount_thread");
2617c478bd9Sstevel@tonic-gate
2627c478bd9Sstevel@tonic-gate fnp = argsp->fnc_fnp;
2637c478bd9Sstevel@tonic-gate vp = fntovn(fnp);
2647c478bd9Sstevel@tonic-gate fnip = vfstofni(vp->v_vfsp);
2657c478bd9Sstevel@tonic-gate name = argsp->fnc_name;
2667c478bd9Sstevel@tonic-gate cred = argsp->fnc_cred;
2677c478bd9Sstevel@tonic-gate ASSERT(crgetzoneid(argsp->fnc_cred) == fnip->fi_zoneid);
2687c478bd9Sstevel@tonic-gate
2693bfb48feSsemery error = auto_mount_request(fnip, name, &alp, cred, TRUE);
2707c478bd9Sstevel@tonic-gate if (!error)
2717c478bd9Sstevel@tonic-gate error = auto_perform_actions(fnip, fnp, alp, cred);
2727c478bd9Sstevel@tonic-gate mutex_enter(&fnp->fn_lock);
2737c478bd9Sstevel@tonic-gate fnp->fn_error = error;
2747c478bd9Sstevel@tonic-gate
2757c478bd9Sstevel@tonic-gate /*
2767c478bd9Sstevel@tonic-gate * Notify threads waiting for mount that
2777c478bd9Sstevel@tonic-gate * it's done.
2787c478bd9Sstevel@tonic-gate */
2797c478bd9Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2807c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
2817c478bd9Sstevel@tonic-gate
2827c478bd9Sstevel@tonic-gate VN_RELE(vp);
2837c478bd9Sstevel@tonic-gate crfree(argsp->fnc_cred);
2847c478bd9Sstevel@tonic-gate namelen = strlen(argsp->fnc_name) + 1;
2857c478bd9Sstevel@tonic-gate kmem_free(argsp->fnc_name, namelen);
2867c478bd9Sstevel@tonic-gate kmem_free(argsp, sizeof (*argsp));
2877c478bd9Sstevel@tonic-gate
2887c478bd9Sstevel@tonic-gate mutex_enter(&auto_mount_thread_cpr_lock);
2897c478bd9Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo);
2907c478bd9Sstevel@tonic-gate mutex_destroy(&auto_mount_thread_cpr_lock);
2917c478bd9Sstevel@tonic-gate zthread_exit();
2927c478bd9Sstevel@tonic-gate /* NOTREACHED */
2937c478bd9Sstevel@tonic-gate }
2947c478bd9Sstevel@tonic-gate
2957c478bd9Sstevel@tonic-gate static int autofs_thr_success = 0;
2967c478bd9Sstevel@tonic-gate
2977c478bd9Sstevel@tonic-gate /*
2987c478bd9Sstevel@tonic-gate * Creates new thread which calls auto_mount_thread which does
2997c478bd9Sstevel@tonic-gate * the bulk of the work calling automountd, via 'auto_perform_actions'.
3007c478bd9Sstevel@tonic-gate */
3017c478bd9Sstevel@tonic-gate void
auto_new_mount_thread(fnnode_t * fnp,char * name,cred_t * cred)3027c478bd9Sstevel@tonic-gate auto_new_mount_thread(fnnode_t *fnp, char *name, cred_t *cred)
3037c478bd9Sstevel@tonic-gate {
3047c478bd9Sstevel@tonic-gate struct autofs_callargs *argsp;
3057c478bd9Sstevel@tonic-gate
3067c478bd9Sstevel@tonic-gate argsp = kmem_alloc(sizeof (*argsp), KM_SLEEP);
3077c478bd9Sstevel@tonic-gate VN_HOLD(fntovn(fnp));
3087c478bd9Sstevel@tonic-gate argsp->fnc_fnp = fnp;
3097c478bd9Sstevel@tonic-gate argsp->fnc_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
3107c478bd9Sstevel@tonic-gate (void) strcpy(argsp->fnc_name, name);
3117c478bd9Sstevel@tonic-gate argsp->fnc_origin = curthread;
3127c478bd9Sstevel@tonic-gate crhold(cred);
3137c478bd9Sstevel@tonic-gate argsp->fnc_cred = cred;
3147c478bd9Sstevel@tonic-gate
3157c478bd9Sstevel@tonic-gate (void) zthread_create(NULL, 0, auto_mount_thread, argsp, 0,
3167c478bd9Sstevel@tonic-gate minclsyspri);
3177c478bd9Sstevel@tonic-gate autofs_thr_success++;
3187c478bd9Sstevel@tonic-gate }
3197c478bd9Sstevel@tonic-gate
320d6c8399bSrmesta #define DOOR_BUF_ALIGN (1024*1024)
321d6c8399bSrmesta #define DOOR_BUF_MULTIPLIER 3
322d6c8399bSrmesta #define DOOR_BUF_DEFAULT_SZ (DOOR_BUF_MULTIPLIER * DOOR_BUF_ALIGN)
323d6c8399bSrmesta int doorbuf_defsz = DOOR_BUF_DEFAULT_SZ;
324d6c8399bSrmesta
325d6c8399bSrmesta /*ARGSUSED*/
3267c478bd9Sstevel@tonic-gate int
auto_calldaemon(zoneid_t zoneid,int which,xdrproc_t xarg_func,void * argsp,xdrproc_t xresp_func,void * resp,int reslen,bool_t hard)3277c478bd9Sstevel@tonic-gate auto_calldaemon(
32839d3e169Sevanl zoneid_t zoneid,
32939d3e169Sevanl int which,
33039d3e169Sevanl xdrproc_t xarg_func,
33139d3e169Sevanl void *argsp,
33239d3e169Sevanl xdrproc_t xresp_func,
33339d3e169Sevanl void *resp,
33439d3e169Sevanl int reslen,
33539d3e169Sevanl bool_t hard) /* retry forever? */
3367c478bd9Sstevel@tonic-gate {
337d6c8399bSrmesta int retry;
338d6c8399bSrmesta int error = 0;
339d6c8399bSrmesta k_sigset_t smask;
340d6c8399bSrmesta door_arg_t door_args;
341d6c8399bSrmesta door_handle_t dh;
342d6c8399bSrmesta XDR xdrarg;
343d6c8399bSrmesta XDR xdrres;
34439d3e169Sevanl struct autofs_globals *fngp = NULL;
345d6c8399bSrmesta void *orp = NULL;
346d6c8399bSrmesta int orl;
3479d40d374Srmesta int rlen = 0; /* MUST be initialized */
34839d3e169Sevanl autofs_door_args_t *xdr_argsp;
349d6c8399bSrmesta int xdr_len = 0;
350d6c8399bSrmesta int printed_not_running_msg = 0;
351780213fbSevanl klwp_t *lwp = ttolwp(curthread);
3527c478bd9Sstevel@tonic-gate
3537c478bd9Sstevel@tonic-gate /*
35439d3e169Sevanl * We know that the current thread is doing work on
35539d3e169Sevanl * behalf of its own zone, so it's ok to use
35639d3e169Sevanl * curproc->p_zone.
3577c478bd9Sstevel@tonic-gate */
35839d3e169Sevanl ASSERT(zoneid == getzoneid());
359d6c8399bSrmesta if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) {
36039d3e169Sevanl /*
36139d3e169Sevanl * There's no point in trying to talk to
36239d3e169Sevanl * automountd. Plus, zone_shutdown() is
36339d3e169Sevanl * waiting for us.
36439d3e169Sevanl */
36539d3e169Sevanl return (ECONNREFUSED);
36639d3e169Sevanl }
3677c478bd9Sstevel@tonic-gate
368780213fbSevanl do {
369780213fbSevanl retry = 0;
370780213fbSevanl mutex_enter(&autofs_minor_lock);
371780213fbSevanl fngp = zone_getspecific(autofs_key, curproc->p_zone);
372780213fbSevanl mutex_exit(&autofs_minor_lock);
373780213fbSevanl if (fngp == NULL) {
374780213fbSevanl if (hard) {
375780213fbSevanl AUTOFS_DPRINT((5,
376780213fbSevanl "auto_calldaemon: "\
377780213fbSevanl "failed to get door handle\n"));
378780213fbSevanl if (!printed_not_running_msg) {
379780213fbSevanl printed_not_running_msg = 1;
380780213fbSevanl zprintf(zoneid, "automountd not "\
381780213fbSevanl "running, retrying\n");
382780213fbSevanl }
383780213fbSevanl delay(hz);
384780213fbSevanl retry = 1;
385780213fbSevanl } else {
386780213fbSevanl /*
387780213fbSevanl * There is no global data so no door.
388780213fbSevanl * There's no point in attempting to talk
389780213fbSevanl * to automountd if we can't get the door
390780213fbSevanl * handle.
391780213fbSevanl */
392780213fbSevanl return (ECONNREFUSED);
393780213fbSevanl }
394780213fbSevanl }
395780213fbSevanl } while (retry);
396780213fbSevanl
397780213fbSevanl if (printed_not_running_msg) {
398780213fbSevanl fngp->fng_printed_not_running_msg = printed_not_running_msg;
3997c478bd9Sstevel@tonic-gate }
4007c478bd9Sstevel@tonic-gate
40139d3e169Sevanl ASSERT(fngp != NULL);
4027c478bd9Sstevel@tonic-gate
40339d3e169Sevanl if (argsp != NULL && (xdr_len = xdr_sizeof(xarg_func, argsp)) == 0)
40439d3e169Sevanl return (EINVAL);
40539d3e169Sevanl xdr_argsp = kmem_zalloc(xdr_len + sizeof (*xdr_argsp), KM_SLEEP);
40639d3e169Sevanl xdr_argsp->xdr_len = xdr_len;
40739d3e169Sevanl xdr_argsp->cmd = which;
4087c478bd9Sstevel@tonic-gate
40939d3e169Sevanl if (argsp) {
41039d3e169Sevanl xdrmem_create(&xdrarg, (char *)&xdr_argsp->xdr_arg,
411d6c8399bSrmesta xdr_argsp->xdr_len, XDR_ENCODE);
4127c478bd9Sstevel@tonic-gate
41339d3e169Sevanl if (!(*xarg_func)(&xdrarg, argsp)) {
41439d3e169Sevanl kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
41539d3e169Sevanl return (EINVAL);
41639d3e169Sevanl }
41739d3e169Sevanl }
4187c478bd9Sstevel@tonic-gate
41939d3e169Sevanl /*
42039d3e169Sevanl * We're saving off the original pointer and length due to the
42139d3e169Sevanl * possibility that the results buffer returned by the door
42239d3e169Sevanl * upcall can be different then what we passed in. This is because
42339d3e169Sevanl * the door will allocate new memory if the results buffer passed
42439d3e169Sevanl * in isn't large enough to hold what we need to send back.
42539d3e169Sevanl * In this case we need to free the memory originally allocated
42639d3e169Sevanl * for that buffer.
42739d3e169Sevanl */
428d6c8399bSrmesta if (resp)
429d6c8399bSrmesta rlen = xdr_sizeof(xresp_func, resp);
430d6c8399bSrmesta orl = (rlen == 0) ? doorbuf_defsz : MAX(rlen, doorbuf_defsz);
431d6c8399bSrmesta orp = kmem_zalloc(orl, KM_SLEEP);
4327c478bd9Sstevel@tonic-gate
43339d3e169Sevanl do {
43439d3e169Sevanl retry = 0;
43539d3e169Sevanl mutex_enter(&fngp->fng_autofs_daemon_lock);
43639d3e169Sevanl dh = fngp->fng_autofs_daemon_dh;
43739d3e169Sevanl if (dh)
43839d3e169Sevanl door_ki_hold(dh);
43939d3e169Sevanl mutex_exit(&fngp->fng_autofs_daemon_lock);
44039d3e169Sevanl
44139d3e169Sevanl if (dh == NULL) {
442d6c8399bSrmesta if (orp)
443d6c8399bSrmesta kmem_free(orp, orl);
44439d3e169Sevanl kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
44539d3e169Sevanl return (ENOENT);
44639d3e169Sevanl }
44739d3e169Sevanl door_args.data_ptr = (char *)xdr_argsp;
44839d3e169Sevanl door_args.data_size = sizeof (*xdr_argsp) + xdr_argsp->xdr_len;
44939d3e169Sevanl door_args.desc_ptr = NULL;
45039d3e169Sevanl door_args.desc_num = 0;
451d6c8399bSrmesta door_args.rbuf = orp ? (char *)orp : NULL;
452d6c8399bSrmesta door_args.rsize = orl;
4537c478bd9Sstevel@tonic-gate
45439d3e169Sevanl sigintr(&smask, 1);
455323a81d9Sjwadams error =
456323a81d9Sjwadams door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
45739d3e169Sevanl sigunintr(&smask);
4587c478bd9Sstevel@tonic-gate
45939d3e169Sevanl door_ki_rele(dh);
46039d3e169Sevanl
461d6c8399bSrmesta /*
462d6c8399bSrmesta * Handle daemon errors
463d6c8399bSrmesta */
46439d3e169Sevanl if (!error) {
465d6c8399bSrmesta /*
466d6c8399bSrmesta * Upcall successful. Let's check for soft errors
467d6c8399bSrmesta * from the daemon. We only recover from overflow
468d6c8399bSrmesta * type scenarios. Any other errors, we return to
469d6c8399bSrmesta * the caller.
470d6c8399bSrmesta */
47139d3e169Sevanl autofs_door_res_t *adr =
472d6c8399bSrmesta (autofs_door_res_t *)door_args.rbuf;
473d6c8399bSrmesta
474d6c8399bSrmesta if (door_args.rbuf != NULL) {
475d6c8399bSrmesta int nl;
476d6c8399bSrmesta
477d6c8399bSrmesta switch (error = adr->res_status) {
478d6c8399bSrmesta case 0: /* no error; continue */
479d6c8399bSrmesta break;
480d6c8399bSrmesta
481d6c8399bSrmesta case EOVERFLOW:
482d6c8399bSrmesta /*
483d6c8399bSrmesta * orig landing buf not big enough.
484d6c8399bSrmesta * xdr_len in XDR_BYTES_PER_UNIT
485d6c8399bSrmesta */
486d6c8399bSrmesta if ((nl = adr->xdr_len) > 0 &&
487d6c8399bSrmesta (btopr(nl) < freemem/64)) {
488d6c8399bSrmesta if (orp)
489d6c8399bSrmesta kmem_free(orp, orl);
490d6c8399bSrmesta orp = kmem_zalloc(nl, KM_SLEEP);
491d6c8399bSrmesta orl = nl;
492d6c8399bSrmesta retry = 1;
493d6c8399bSrmesta break;
494d6c8399bSrmesta }
495d6c8399bSrmesta /*FALLTHROUGH*/
496d6c8399bSrmesta
497d6c8399bSrmesta default:
498d6c8399bSrmesta kmem_free(xdr_argsp,
499d6c8399bSrmesta xdr_len + sizeof (*xdr_argsp));
500d6c8399bSrmesta if (orp)
501d6c8399bSrmesta kmem_free(orp, orl);
502d6c8399bSrmesta return (error);
503d6c8399bSrmesta }
50439d3e169Sevanl }
50539d3e169Sevanl continue;
50639d3e169Sevanl }
507d6c8399bSrmesta
508d6c8399bSrmesta /*
509d6c8399bSrmesta * no daemon errors; now process door/comm errors (if any)
510d6c8399bSrmesta */
51139d3e169Sevanl switch (error) {
51239d3e169Sevanl case EINTR:
5137c478bd9Sstevel@tonic-gate /*
51439d3e169Sevanl * interrupts should be handled properly by the
515780213fbSevanl * door upcall. If the door doesn't handle the
516780213fbSevanl * interupt completely then we need to bail out.
517780213fbSevanl */
518780213fbSevanl if (lwp && (ISSIG(curthread,
519780213fbSevanl JUSTLOOKING) || MUSTRETURN(curproc, curthread))) {
520780213fbSevanl if (ISSIG(curthread, FORREAL) ||
521780213fbSevanl lwp->lwp_sysabort ||
522780213fbSevanl MUSTRETURN(curproc, curthread)) {
523780213fbSevanl lwp->lwp_sysabort = 0;
524780213fbSevanl return (EINTR);
525780213fbSevanl }
526780213fbSevanl }
527780213fbSevanl /*
52839d3e169Sevanl * We may have gotten EINTR for other reasons
52939d3e169Sevanl * like the door being revoked on us. Instead
53039d3e169Sevanl * of trying to extract this out of the door
53139d3e169Sevanl * handle, sleep and try again, if still
53239d3e169Sevanl * revoked we will get EBADF next time
53339d3e169Sevanl * through.
534a574db85Sraf *
535a574db85Sraf * If we have a pending cancellation and we don't
536a574db85Sraf * have cancellation disabled, we will get EINTR
537a574db85Sraf * forever, no matter how many times we retry,
538a574db85Sraf * so just get out now if this is the case.
5397c478bd9Sstevel@tonic-gate */
540a574db85Sraf if (schedctl_cancel_pending())
541a574db85Sraf break;
542780213fbSevanl /* FALLTHROUGH */
54339d3e169Sevanl case EAGAIN: /* process may be forking */
5447c478bd9Sstevel@tonic-gate /*
54539d3e169Sevanl * Back off for a bit
5467c478bd9Sstevel@tonic-gate */
54739d3e169Sevanl delay(hz);
54839d3e169Sevanl retry = 1;
54939d3e169Sevanl break;
55039d3e169Sevanl case EBADF: /* Invalid door */
55139d3e169Sevanl case EINVAL: /* Not a door, wrong target */
5527c478bd9Sstevel@tonic-gate /*
55339d3e169Sevanl * A fatal door error, if our failing door
55439d3e169Sevanl * handle is the current door handle, clean
55539d3e169Sevanl * up our state.
5567c478bd9Sstevel@tonic-gate */
55739d3e169Sevanl mutex_enter(&fngp->fng_autofs_daemon_lock);
55839d3e169Sevanl if (dh == fngp->fng_autofs_daemon_dh) {
55939d3e169Sevanl door_ki_rele(fngp->fng_autofs_daemon_dh);
56039d3e169Sevanl fngp->fng_autofs_daemon_dh = NULL;
5617c478bd9Sstevel@tonic-gate }
56239d3e169Sevanl mutex_exit(&fngp->fng_autofs_daemon_lock);
563d6c8399bSrmesta AUTOFS_DPRINT((5, "auto_calldaemon error=%d\n", error));
56439d3e169Sevanl if (hard) {
56539d3e169Sevanl if (!fngp->fng_printed_not_running_msg) {
566d6c8399bSrmesta fngp->fng_printed_not_running_msg = 1;
567d6c8399bSrmesta zprintf(zoneid, "automountd not "
568d6c8399bSrmesta "running, retrying\n");
5697c478bd9Sstevel@tonic-gate }
57039d3e169Sevanl delay(hz);
57139d3e169Sevanl retry = 1;
57239d3e169Sevanl break;
57339d3e169Sevanl } else {
57439d3e169Sevanl error = ECONNREFUSED;
57539d3e169Sevanl kmem_free(xdr_argsp,
576d6c8399bSrmesta xdr_len + sizeof (*xdr_argsp));
577d6c8399bSrmesta if (orp)
578d6c8399bSrmesta kmem_free(orp, orl);
57939d3e169Sevanl return (error);
5807c478bd9Sstevel@tonic-gate }
58139d3e169Sevanl default: /* Unknown must be fatal */
5827c478bd9Sstevel@tonic-gate error = ENOENT;
58339d3e169Sevanl kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
584d6c8399bSrmesta if (orp)
585d6c8399bSrmesta kmem_free(orp, orl);
58639d3e169Sevanl return (error);
5877c478bd9Sstevel@tonic-gate }
58839d3e169Sevanl } while (retry);
5897c478bd9Sstevel@tonic-gate
59039d3e169Sevanl if (fngp->fng_printed_not_running_msg == 1) {
59139d3e169Sevanl fngp->fng_printed_not_running_msg = 0;
59239d3e169Sevanl zprintf(zoneid, "automountd OK\n");
5937c478bd9Sstevel@tonic-gate }
5947c478bd9Sstevel@tonic-gate
595d6c8399bSrmesta if (orp && orl) {
59639d3e169Sevanl autofs_door_res_t *door_resp;
597d6c8399bSrmesta door_resp = (autofs_door_res_t *)door_args.rbuf;
598d6c8399bSrmesta
599d6c8399bSrmesta if ((void *)door_args.rbuf != orp)
600d6c8399bSrmesta kmem_free(orp, orl);
601d6c8399bSrmesta
60239d3e169Sevanl xdrmem_create(&xdrres, (char *)&door_resp->xdr_res,
603d6c8399bSrmesta door_resp->xdr_len, XDR_DECODE);
604d6c8399bSrmesta
60539d3e169Sevanl if (!((*xresp_func)(&xdrres, resp)))
60639d3e169Sevanl error = EINVAL;
60739d3e169Sevanl kmem_free(door_args.rbuf, door_args.rsize);
60839d3e169Sevanl }
60939d3e169Sevanl kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
6107c478bd9Sstevel@tonic-gate return (error);
6117c478bd9Sstevel@tonic-gate }
6127c478bd9Sstevel@tonic-gate
6137c478bd9Sstevel@tonic-gate static int
auto_null_request(zoneid_t zoneid,bool_t hard)614f798ee53SJan Kryl auto_null_request(zoneid_t zoneid, bool_t hard)
6157c478bd9Sstevel@tonic-gate {
6167c478bd9Sstevel@tonic-gate int error;
6177c478bd9Sstevel@tonic-gate
6187c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tauto_null_request\n"));
6197c478bd9Sstevel@tonic-gate
620f798ee53SJan Kryl error = auto_calldaemon(zoneid, NULLPROC,
621d6c8399bSrmesta xdr_void, NULL, xdr_void, NULL, 0, hard);
6227c478bd9Sstevel@tonic-gate
6237c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tauto_null_request: error=%d\n", error));
6247c478bd9Sstevel@tonic-gate return (error);
6257c478bd9Sstevel@tonic-gate }
6267c478bd9Sstevel@tonic-gate
6277c478bd9Sstevel@tonic-gate static int
auto_lookup_request(fninfo_t * fnip,char * key,struct linka * lnp,bool_t hard,bool_t * mountreq,cred_t * cred)6287c478bd9Sstevel@tonic-gate auto_lookup_request(
6297c478bd9Sstevel@tonic-gate fninfo_t *fnip,
6307c478bd9Sstevel@tonic-gate char *key,
6317c478bd9Sstevel@tonic-gate struct linka *lnp,
6327c478bd9Sstevel@tonic-gate bool_t hard,
6333bfb48feSsemery bool_t *mountreq,
6343bfb48feSsemery cred_t *cred)
6357c478bd9Sstevel@tonic-gate {
63639d3e169Sevanl int error;
63739d3e169Sevanl struct autofs_globals *fngp;
638d6c8399bSrmesta struct autofs_lookupargs reqst;
63939d3e169Sevanl autofs_lookupres *resp;
64039d3e169Sevanl struct linka *p;
64139d3e169Sevanl
6427c478bd9Sstevel@tonic-gate
64339d3e169Sevanl AUTOFS_DPRINT((4, "auto_lookup_equest: path=%s name=%s\n",
6447c478bd9Sstevel@tonic-gate fnip->fi_path, key));
6457c478bd9Sstevel@tonic-gate
6467c478bd9Sstevel@tonic-gate fngp = vntofn(fnip->fi_rootvp)->fn_globals;
64739d3e169Sevanl
64839d3e169Sevanl reqst.map = fnip->fi_map;
64939d3e169Sevanl reqst.path = fnip->fi_path;
6507c478bd9Sstevel@tonic-gate
6517c478bd9Sstevel@tonic-gate if (fnip->fi_flags & MF_DIRECT)
65239d3e169Sevanl reqst.name = fnip->fi_key;
6537c478bd9Sstevel@tonic-gate else
65439d3e169Sevanl reqst.name = key;
65539d3e169Sevanl AUTOFS_DPRINT((4, "auto_lookup_request: using key=%s\n", reqst.name));
65639d3e169Sevanl
65739d3e169Sevanl reqst.subdir = fnip->fi_subdir;
65839d3e169Sevanl reqst.opts = fnip->fi_opts;
65939d3e169Sevanl reqst.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
6603bfb48feSsemery reqst.uid = crgetuid(cred);
66139d3e169Sevanl
66239d3e169Sevanl resp = kmem_zalloc(sizeof (*resp), KM_SLEEP);
66339d3e169Sevanl
664d6c8399bSrmesta error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_LOOKUP,
665d6c8399bSrmesta xdr_autofs_lookupargs, &reqst, xdr_autofs_lookupres,
666d6c8399bSrmesta (void *)resp, sizeof (autofs_lookupres), hard);
66739d3e169Sevanl
66839d3e169Sevanl if (error) {
66939d3e169Sevanl xdr_free(xdr_autofs_lookupres, (char *)resp);
67039d3e169Sevanl kmem_free(resp, sizeof (*resp));
67139d3e169Sevanl return (error);
67239d3e169Sevanl }
67339d3e169Sevanl
6747c478bd9Sstevel@tonic-gate if (!error) {
67539d3e169Sevanl fngp->fng_verbose = resp->lu_verbose;
67639d3e169Sevanl switch (resp->lu_res) {
6777c478bd9Sstevel@tonic-gate case AUTOFS_OK:
67839d3e169Sevanl switch (resp->lu_type.action) {
6797c478bd9Sstevel@tonic-gate case AUTOFS_MOUNT_RQ:
6807c478bd9Sstevel@tonic-gate lnp->link = NULL;
6817c478bd9Sstevel@tonic-gate lnp->dir = NULL;
6827c478bd9Sstevel@tonic-gate *mountreq = TRUE;
6837c478bd9Sstevel@tonic-gate break;
684d6c8399bSrmesta
6857c478bd9Sstevel@tonic-gate case AUTOFS_LINK_RQ:
686d6c8399bSrmesta p = &resp->lu_type.lookup_result_type_u.lt_linka;
6877c478bd9Sstevel@tonic-gate lnp->dir = kmem_alloc(strlen(p->dir) + 1,
688d6c8399bSrmesta KM_SLEEP);
6897c478bd9Sstevel@tonic-gate (void) strcpy(lnp->dir, p->dir);
6907c478bd9Sstevel@tonic-gate lnp->link = kmem_alloc(strlen(p->link) + 1,
691d6c8399bSrmesta KM_SLEEP);
6927c478bd9Sstevel@tonic-gate (void) strcpy(lnp->link, p->link);
6937c478bd9Sstevel@tonic-gate break;
694d6c8399bSrmesta
6957c478bd9Sstevel@tonic-gate case AUTOFS_NONE:
6967c478bd9Sstevel@tonic-gate lnp->link = NULL;
6977c478bd9Sstevel@tonic-gate lnp->dir = NULL;
6987c478bd9Sstevel@tonic-gate break;
699d6c8399bSrmesta
7007c478bd9Sstevel@tonic-gate default:
701d6c8399bSrmesta auto_log(fngp->fng_verbose, fngp->fng_zoneid,
702d6c8399bSrmesta CE_WARN, "auto_lookup_request: bad action "
703d6c8399bSrmesta "type %d", resp->lu_res);
7047c478bd9Sstevel@tonic-gate error = ENOENT;
7057c478bd9Sstevel@tonic-gate }
7067c478bd9Sstevel@tonic-gate break;
707d6c8399bSrmesta
7087c478bd9Sstevel@tonic-gate case AUTOFS_NOENT:
7097c478bd9Sstevel@tonic-gate error = ENOENT;
7107c478bd9Sstevel@tonic-gate break;
711d6c8399bSrmesta
7127c478bd9Sstevel@tonic-gate default:
7137c478bd9Sstevel@tonic-gate error = ENOENT;
71439d3e169Sevanl auto_log(fngp->fng_verbose, fngp->fng_zoneid, CE_WARN,
7157c478bd9Sstevel@tonic-gate "auto_lookup_request: unknown result: %d",
71639d3e169Sevanl resp->lu_res);
7177c478bd9Sstevel@tonic-gate break;
7187c478bd9Sstevel@tonic-gate }
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate done:
72139d3e169Sevanl xdr_free(xdr_autofs_lookupres, (char *)resp);
72239d3e169Sevanl kmem_free(resp, sizeof (*resp));
7237c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_lookup_request: path=%s name=%s error=%d\n",
7247c478bd9Sstevel@tonic-gate fnip->fi_path, key, error));
7257c478bd9Sstevel@tonic-gate return (error);
7267c478bd9Sstevel@tonic-gate }
7277c478bd9Sstevel@tonic-gate
7287c478bd9Sstevel@tonic-gate static int
auto_mount_request(fninfo_t * fnip,char * key,action_list ** alpp,cred_t * cred,bool_t hard)7297c478bd9Sstevel@tonic-gate auto_mount_request(
7307c478bd9Sstevel@tonic-gate fninfo_t *fnip,
7317c478bd9Sstevel@tonic-gate char *key,
7327c478bd9Sstevel@tonic-gate action_list **alpp,
7333bfb48feSsemery cred_t *cred,
7347c478bd9Sstevel@tonic-gate bool_t hard)
7357c478bd9Sstevel@tonic-gate {
73639d3e169Sevanl int error;
73739d3e169Sevanl struct autofs_globals *fngp;
73839d3e169Sevanl autofs_lookupargs reqst;
73939d3e169Sevanl autofs_mountres *xdrres = NULL;
7407c478bd9Sstevel@tonic-gate
7417c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_mount_request: path=%s name=%s\n",
7427c478bd9Sstevel@tonic-gate fnip->fi_path, key));
7437c478bd9Sstevel@tonic-gate
7447c478bd9Sstevel@tonic-gate fngp = vntofn(fnip->fi_rootvp)->fn_globals;
74539d3e169Sevanl reqst.map = fnip->fi_map;
74639d3e169Sevanl reqst.path = fnip->fi_path;
7477c478bd9Sstevel@tonic-gate
7487c478bd9Sstevel@tonic-gate if (fnip->fi_flags & MF_DIRECT)
74939d3e169Sevanl reqst.name = fnip->fi_key;
7507c478bd9Sstevel@tonic-gate else
75139d3e169Sevanl reqst.name = key;
75239d3e169Sevanl
75339d3e169Sevanl AUTOFS_DPRINT((4, "auto_mount_request: using key=%s\n", reqst.name));
75439d3e169Sevanl
75539d3e169Sevanl reqst.subdir = fnip->fi_subdir;
75639d3e169Sevanl reqst.opts = fnip->fi_opts;
75739d3e169Sevanl reqst.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
7583bfb48feSsemery reqst.uid = crgetuid(cred);
75939d3e169Sevanl
76039d3e169Sevanl xdrres = kmem_zalloc(sizeof (*xdrres), KM_SLEEP);
76139d3e169Sevanl
762d6c8399bSrmesta error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_MNTINFO,
763d6c8399bSrmesta xdr_autofs_lookupargs, &reqst, xdr_autofs_mountres,
764d6c8399bSrmesta (void *)xdrres, sizeof (autofs_mountres), hard);
76539d3e169Sevanl
7667c478bd9Sstevel@tonic-gate if (!error) {
76739d3e169Sevanl fngp->fng_verbose = xdrres->mr_verbose;
76839d3e169Sevanl switch (xdrres->mr_type.status) {
7697c478bd9Sstevel@tonic-gate case AUTOFS_ACTION:
7707c478bd9Sstevel@tonic-gate error = 0;
7717c478bd9Sstevel@tonic-gate /*
7727c478bd9Sstevel@tonic-gate * Save the action list since it is used by
7737c478bd9Sstevel@tonic-gate * the caller. We NULL the action list pointer
7747c478bd9Sstevel@tonic-gate * in 'result' so that xdr_free() will not free
7757c478bd9Sstevel@tonic-gate * the list.
7767c478bd9Sstevel@tonic-gate */
77739d3e169Sevanl *alpp = xdrres->mr_type.mount_result_type_u.list;
77839d3e169Sevanl xdrres->mr_type.mount_result_type_u.list = NULL;
7797c478bd9Sstevel@tonic-gate break;
7807c478bd9Sstevel@tonic-gate case AUTOFS_DONE:
78139d3e169Sevanl error = xdrres->mr_type.mount_result_type_u.error;
7827c478bd9Sstevel@tonic-gate break;
7837c478bd9Sstevel@tonic-gate default:
7847c478bd9Sstevel@tonic-gate error = ENOENT;
78539d3e169Sevanl auto_log(fngp->fng_verbose, fngp->fng_zoneid, CE_WARN,
7867c478bd9Sstevel@tonic-gate "auto_mount_request: unknown status %d",
78739d3e169Sevanl xdrres->mr_type.status);
7887c478bd9Sstevel@tonic-gate break;
7897c478bd9Sstevel@tonic-gate }
7907c478bd9Sstevel@tonic-gate }
7917c478bd9Sstevel@tonic-gate
79239d3e169Sevanl xdr_free(xdr_autofs_mountres, (char *)xdrres);
79339d3e169Sevanl kmem_free(xdrres, sizeof (*xdrres));
79439d3e169Sevanl
7957c478bd9Sstevel@tonic-gate
7967c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_mount_request: path=%s name=%s error=%d\n",
7977c478bd9Sstevel@tonic-gate fnip->fi_path, key, error));
7987c478bd9Sstevel@tonic-gate return (error);
7997c478bd9Sstevel@tonic-gate }
8007c478bd9Sstevel@tonic-gate
8017c478bd9Sstevel@tonic-gate
8027c478bd9Sstevel@tonic-gate static int
auto_send_unmount_request(fninfo_t * fnip,umntrequest * ul,bool_t hard)8037c478bd9Sstevel@tonic-gate auto_send_unmount_request(
8047c478bd9Sstevel@tonic-gate fninfo_t *fnip,
8057c478bd9Sstevel@tonic-gate umntrequest *ul,
8067c478bd9Sstevel@tonic-gate bool_t hard)
8077c478bd9Sstevel@tonic-gate {
80839d3e169Sevanl int error;
80939d3e169Sevanl umntres xdrres;
81039d3e169Sevanl
81139d3e169Sevanl struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals;
8127c478bd9Sstevel@tonic-gate
8137c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tauto_send_unmount_request: fstype=%s "
814d6c8399bSrmesta " mntpnt=%s\n", ul->fstype, ul->mntpnt));
8157c478bd9Sstevel@tonic-gate
81639d3e169Sevanl bzero(&xdrres, sizeof (umntres));
817d6c8399bSrmesta error = auto_calldaemon(fngp->fng_zoneid, AUTOFS_UNMOUNT,
818d6c8399bSrmesta xdr_umntrequest, (void *)ul, xdr_umntres, (void *)&xdrres,
819d6c8399bSrmesta sizeof (umntres), hard);
8207c478bd9Sstevel@tonic-gate
82146a207baSevanl if (!error)
82246a207baSevanl error = xdrres.status;
82346a207baSevanl
8247c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tauto_send_unmount_request: error=%d\n", error));
8257c478bd9Sstevel@tonic-gate
8267c478bd9Sstevel@tonic-gate return (error);
8277c478bd9Sstevel@tonic-gate }
8287c478bd9Sstevel@tonic-gate
8297c478bd9Sstevel@tonic-gate static int
auto_perform_link(fnnode_t * fnp,struct linka * linkp,cred_t * cred)8307c478bd9Sstevel@tonic-gate auto_perform_link(fnnode_t *fnp, struct linka *linkp, cred_t *cred)
8317c478bd9Sstevel@tonic-gate {
8327c478bd9Sstevel@tonic-gate vnode_t *vp;
8337c478bd9Sstevel@tonic-gate size_t len;
8347c478bd9Sstevel@tonic-gate char *tmp;
8357c478bd9Sstevel@tonic-gate
8367c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((3, "auto_perform_link: fnp=%p dir=%s link=%s\n",
8377c478bd9Sstevel@tonic-gate (void *)fnp, linkp->dir, linkp->link));
8387c478bd9Sstevel@tonic-gate
8397c478bd9Sstevel@tonic-gate len = strlen(linkp->link) + 1; /* include '\0' */
8407c478bd9Sstevel@tonic-gate tmp = kmem_zalloc(len, KM_SLEEP);
8417c478bd9Sstevel@tonic-gate (void) kcopy(linkp->link, tmp, len);
8427c478bd9Sstevel@tonic-gate mutex_enter(&fnp->fn_lock);
8437c478bd9Sstevel@tonic-gate fnp->fn_symlink = tmp;
8447c478bd9Sstevel@tonic-gate fnp->fn_symlinklen = (uint_t)len;
8457c478bd9Sstevel@tonic-gate fnp->fn_flags |= MF_THISUID_MATCH_RQD;
8467c478bd9Sstevel@tonic-gate crhold(cred);
8477c478bd9Sstevel@tonic-gate fnp->fn_cred = cred;
8487c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
8497c478bd9Sstevel@tonic-gate
8507c478bd9Sstevel@tonic-gate vp = fntovn(fnp);
8517c478bd9Sstevel@tonic-gate vp->v_type = VLNK;
8527c478bd9Sstevel@tonic-gate
8537c478bd9Sstevel@tonic-gate return (0);
8547c478bd9Sstevel@tonic-gate }
8557c478bd9Sstevel@tonic-gate
8565e1e04ceSdm static void
auto_free_autofs_args(struct mounta * m)8575e1e04ceSdm auto_free_autofs_args(struct mounta *m)
8585e1e04ceSdm {
8595e1e04ceSdm autofs_args *aargs = (autofs_args *)m->dataptr;
8605e1e04ceSdm
8615e1e04ceSdm if (aargs->addr.buf)
8625e1e04ceSdm kmem_free(aargs->addr.buf, aargs->addr.len);
8635e1e04ceSdm if (aargs->path)
8645e1e04ceSdm kmem_free(aargs->path, strlen(aargs->path) + 1);
8655e1e04ceSdm if (aargs->opts)
8665e1e04ceSdm kmem_free(aargs->opts, strlen(aargs->opts) + 1);
8675e1e04ceSdm if (aargs->map)
8685e1e04ceSdm kmem_free(aargs->map, strlen(aargs->map) + 1);
8695e1e04ceSdm if (aargs->subdir)
8705e1e04ceSdm kmem_free(aargs->subdir, strlen(aargs->subdir) + 1);
8715e1e04ceSdm if (aargs->key)
8725e1e04ceSdm kmem_free(aargs->key, strlen(aargs->key) + 1);
8735e1e04ceSdm kmem_free(aargs, sizeof (*aargs));
8745e1e04ceSdm }
8755e1e04ceSdm
8765e1e04ceSdm static void
auto_free_action_list(action_list * alp)8775e1e04ceSdm auto_free_action_list(action_list *alp)
8785e1e04ceSdm {
8795e1e04ceSdm struct mounta *m;
8805e1e04ceSdm action_list *lastalp;
8815e1e04ceSdm char *fstype;
8825e1e04ceSdm
8835e1e04ceSdm m = &alp->action.action_list_entry_u.mounta;
8845e1e04ceSdm while (alp != NULL) {
8855e1e04ceSdm fstype = alp->action.action_list_entry_u.mounta.fstype;
8865e1e04ceSdm m = &alp->action.action_list_entry_u.mounta;
8875e1e04ceSdm if (m->dataptr) {
8885e1e04ceSdm if (strcmp(fstype, "autofs") == 0) {
8895e1e04ceSdm auto_free_autofs_args(m);
8905e1e04ceSdm }
8915e1e04ceSdm }
8925e1e04ceSdm if (m->spec)
8935e1e04ceSdm kmem_free(m->spec, strlen(m->spec) + 1);
8945e1e04ceSdm if (m->dir)
8955e1e04ceSdm kmem_free(m->dir, strlen(m->dir) + 1);
8965e1e04ceSdm if (m->fstype)
8975e1e04ceSdm kmem_free(m->fstype, strlen(m->fstype) + 1);
8985e1e04ceSdm if (m->optptr)
8995e1e04ceSdm kmem_free(m->optptr, m->optlen);
9005e1e04ceSdm lastalp = alp;
9015e1e04ceSdm alp = alp->next;
9025e1e04ceSdm kmem_free(lastalp, sizeof (*lastalp));
9035e1e04ceSdm }
9045e1e04ceSdm }
9055e1e04ceSdm
9067c478bd9Sstevel@tonic-gate static boolean_t
auto_invalid_autofs(fninfo_t * dfnip,fnnode_t * dfnp,action_list * p)90739d3e169Sevanl auto_invalid_autofs(fninfo_t *dfnip, fnnode_t *dfnp, action_list *p)
9087c478bd9Sstevel@tonic-gate {
9097c478bd9Sstevel@tonic-gate struct mounta *m;
9107c478bd9Sstevel@tonic-gate struct autofs_args *argsp;
9117c478bd9Sstevel@tonic-gate vnode_t *dvp;
9127c478bd9Sstevel@tonic-gate char buff[AUTOFS_MAXPATHLEN];
9137c478bd9Sstevel@tonic-gate size_t len;
9147c478bd9Sstevel@tonic-gate struct autofs_globals *fngp;
9155e1e04ceSdm
9167c478bd9Sstevel@tonic-gate fngp = dfnp->fn_globals;
9177c478bd9Sstevel@tonic-gate dvp = fntovn(dfnp);
91839d3e169Sevanl
9197c478bd9Sstevel@tonic-gate m = &p->action.action_list_entry_u.mounta;
9207c478bd9Sstevel@tonic-gate /*
9217c478bd9Sstevel@tonic-gate * Make sure we aren't geting passed NULL values or a "dir" that
9227c478bd9Sstevel@tonic-gate * isn't "." and doesn't begin with "./".
9237c478bd9Sstevel@tonic-gate *
9247c478bd9Sstevel@tonic-gate * We also only want to perform autofs mounts, so make sure
9257c478bd9Sstevel@tonic-gate * no-one is trying to trick us into doing anything else.
9267c478bd9Sstevel@tonic-gate */
9275e1e04ceSdm if (m->spec == NULL || m->dir == NULL || m->dir[0] != '.' ||
9287c478bd9Sstevel@tonic-gate (m->dir[1] != '/' && m->dir[1] != '\0') ||
9295e1e04ceSdm m->fstype == NULL || strcmp(m->fstype, "autofs") != 0 ||
9305e1e04ceSdm m->dataptr == NULL || m->datalen != sizeof (struct autofs_args) ||
9317c478bd9Sstevel@tonic-gate m->optptr == NULL)
9327c478bd9Sstevel@tonic-gate return (B_TRUE);
9337c478bd9Sstevel@tonic-gate /*
9347c478bd9Sstevel@tonic-gate * We also don't like ".."s in the pathname. Symlinks are
9357c478bd9Sstevel@tonic-gate * handled by the fact that we'll use NOFOLLOW when we do
9367c478bd9Sstevel@tonic-gate * lookup()s.
9377c478bd9Sstevel@tonic-gate */
9387c478bd9Sstevel@tonic-gate if (strstr(m->dir, "/../") != NULL ||
9397c478bd9Sstevel@tonic-gate (len = strlen(m->dir)) > sizeof ("/..") - 1 &&
9407c478bd9Sstevel@tonic-gate m->dir[len] == '.' && m->dir[len - 1] == '.' &&
9417c478bd9Sstevel@tonic-gate m->dir[len - 2] == '/')
9427c478bd9Sstevel@tonic-gate return (B_TRUE);
9437c478bd9Sstevel@tonic-gate argsp = (struct autofs_args *)m->dataptr;
9447c478bd9Sstevel@tonic-gate /*
9457c478bd9Sstevel@tonic-gate * We don't want NULL values here either.
9467c478bd9Sstevel@tonic-gate */
9477c478bd9Sstevel@tonic-gate if (argsp->addr.buf == NULL || argsp->path == NULL ||
9487c478bd9Sstevel@tonic-gate argsp->opts == NULL || argsp->map == NULL || argsp->subdir == NULL)
9497c478bd9Sstevel@tonic-gate return (B_TRUE);
9507c478bd9Sstevel@tonic-gate /*
9517c478bd9Sstevel@tonic-gate * We know what the claimed pathname *should* look like:
9527c478bd9Sstevel@tonic-gate *
9537c478bd9Sstevel@tonic-gate * If the parent (dfnp) is a mount point (VROOT), then
9547c478bd9Sstevel@tonic-gate * the path should be (dfnip->fi_path + m->dir).
9557c478bd9Sstevel@tonic-gate *
9567c478bd9Sstevel@tonic-gate * Else, we know we're only two levels deep, so we use
9577c478bd9Sstevel@tonic-gate * (dfnip->fi_path + dfnp->fn_name + m->dir).
9587c478bd9Sstevel@tonic-gate *
9597c478bd9Sstevel@tonic-gate * Furthermore, "." only makes sense if dfnp is a
9607c478bd9Sstevel@tonic-gate * trigger node.
9617c478bd9Sstevel@tonic-gate *
9627c478bd9Sstevel@tonic-gate * At this point it seems like the passed-in path is
9637c478bd9Sstevel@tonic-gate * redundant.
9647c478bd9Sstevel@tonic-gate */
9657c478bd9Sstevel@tonic-gate if (dvp->v_flag & VROOT) {
9667c478bd9Sstevel@tonic-gate if (m->dir[1] == '\0' && !(dfnp->fn_flags & MF_TRIGGER))
9677c478bd9Sstevel@tonic-gate return (B_TRUE);
9687c478bd9Sstevel@tonic-gate (void) snprintf(buff, sizeof (buff), "%s%s",
9697c478bd9Sstevel@tonic-gate dfnip->fi_path, m->dir + 1);
9707c478bd9Sstevel@tonic-gate } else {
9717c478bd9Sstevel@tonic-gate (void) snprintf(buff, sizeof (buff), "%s/%s%s",
9727c478bd9Sstevel@tonic-gate dfnip->fi_path, dfnp->fn_name, m->dir + 1);
9737c478bd9Sstevel@tonic-gate }
9747c478bd9Sstevel@tonic-gate if (strcmp(argsp->path, buff) != 0) {
97539d3e169Sevanl auto_log(fngp->fng_verbose, fngp->fng_zoneid,
97639d3e169Sevanl CE_WARN, "autofs: expected path of '%s', "
9777c478bd9Sstevel@tonic-gate "got '%s' instead.", buff, argsp->path);
9787c478bd9Sstevel@tonic-gate return (B_TRUE);
9797c478bd9Sstevel@tonic-gate }
9807c478bd9Sstevel@tonic-gate return (B_FALSE); /* looks OK */
9817c478bd9Sstevel@tonic-gate }
9827c478bd9Sstevel@tonic-gate
98339d3e169Sevanl /*
98439d3e169Sevanl * auto_invalid_action will validate the action_list received. If all is good
9855e1e04ceSdm * this function returns FALSE, if there is a problem it returns TRUE.
98639d3e169Sevanl */
98739d3e169Sevanl static boolean_t
auto_invalid_action(fninfo_t * dfnip,fnnode_t * dfnp,action_list * alistpp)98839d3e169Sevanl auto_invalid_action(fninfo_t *dfnip, fnnode_t *dfnp, action_list *alistpp)
98939d3e169Sevanl {
99039d3e169Sevanl
99139d3e169Sevanl /*
99239d3e169Sevanl * Before we go any further, this better be a mount request.
99339d3e169Sevanl */
99439d3e169Sevanl if (alistpp->action.action != AUTOFS_MOUNT_RQ)
99539d3e169Sevanl return (B_TRUE);
9965e1e04ceSdm return (auto_invalid_autofs(dfnip, dfnp, alistpp));
99739d3e169Sevanl
99839d3e169Sevanl }
99939d3e169Sevanl
10007c478bd9Sstevel@tonic-gate static int
auto_perform_actions(fninfo_t * dfnip,fnnode_t * dfnp,action_list * alp,cred_t * cred)10017c478bd9Sstevel@tonic-gate auto_perform_actions(
10027c478bd9Sstevel@tonic-gate fninfo_t *dfnip,
10037c478bd9Sstevel@tonic-gate fnnode_t *dfnp,
10047c478bd9Sstevel@tonic-gate action_list *alp,
10057c478bd9Sstevel@tonic-gate cred_t *cred) /* Credentials of the caller */
10067c478bd9Sstevel@tonic-gate {
10077c478bd9Sstevel@tonic-gate
100839d3e169Sevanl action_list *p;
100939d3e169Sevanl struct mounta *m, margs;
101039d3e169Sevanl struct autofs_args *argsp;
101139d3e169Sevanl int error, success = 0;
101239d3e169Sevanl vnode_t *mvp, *dvp, *newvp;
101339d3e169Sevanl fnnode_t *newfnp, *mfnp;
101439d3e169Sevanl int auto_mount = 0;
101539d3e169Sevanl int save_triggers = 0;
101639d3e169Sevanl int update_times = 0;
101739d3e169Sevanl char *mntpnt;
101839d3e169Sevanl char buff[AUTOFS_MAXPATHLEN];
101939d3e169Sevanl timestruc_t now;
102039d3e169Sevanl struct autofs_globals *fngp;
102139d3e169Sevanl cred_t *zcred;
102239d3e169Sevanl
1023d6c8399bSrmesta AUTOFS_DPRINT((4, "auto_perform_actions: alp=%p\n", (void *)alp));
10247c478bd9Sstevel@tonic-gate
10257c478bd9Sstevel@tonic-gate fngp = dfnp->fn_globals;
10267c478bd9Sstevel@tonic-gate dvp = fntovn(dfnp);
10277c478bd9Sstevel@tonic-gate
10287c478bd9Sstevel@tonic-gate /*
10297c478bd9Sstevel@tonic-gate * As automountd running in a zone may be compromised, and this may be
10307c478bd9Sstevel@tonic-gate * an attack, we can't trust everything passed in by automountd, and we
10317c478bd9Sstevel@tonic-gate * need to do argument verification. We'll issue a warning and drop
10327c478bd9Sstevel@tonic-gate * the request if it doesn't seem right.
10337c478bd9Sstevel@tonic-gate */
103439d3e169Sevanl
10357c478bd9Sstevel@tonic-gate for (p = alp; p != NULL; p = p->next) {
10367c478bd9Sstevel@tonic-gate if (auto_invalid_action(dfnip, dfnp, p)) {
10377c478bd9Sstevel@tonic-gate /*
10387c478bd9Sstevel@tonic-gate * This warning should be sent to the global zone,
10397c478bd9Sstevel@tonic-gate * since presumably the zone administrator is the same
10407c478bd9Sstevel@tonic-gate * as the attacker.
10417c478bd9Sstevel@tonic-gate */
10427c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "autofs: invalid action list received "
10437c478bd9Sstevel@tonic-gate "by automountd in zone %s.",
10447c478bd9Sstevel@tonic-gate curproc->p_zone->zone_name);
10457c478bd9Sstevel@tonic-gate /*
10467c478bd9Sstevel@tonic-gate * This conversation is over.
10477c478bd9Sstevel@tonic-gate */
10487c478bd9Sstevel@tonic-gate xdr_free(xdr_action_list, (char *)alp);
10497c478bd9Sstevel@tonic-gate return (EINVAL);
10507c478bd9Sstevel@tonic-gate }
10517c478bd9Sstevel@tonic-gate }
10527c478bd9Sstevel@tonic-gate
10537c478bd9Sstevel@tonic-gate zcred = zone_get_kcred(getzoneid());
10547c478bd9Sstevel@tonic-gate ASSERT(zcred != NULL);
10557c478bd9Sstevel@tonic-gate
10567c478bd9Sstevel@tonic-gate if (vn_mountedvfs(dvp) != NULL) {
10577c478bd9Sstevel@tonic-gate /*
10587c478bd9Sstevel@tonic-gate * The daemon successfully mounted a filesystem
10597c478bd9Sstevel@tonic-gate * on the AUTOFS root node.
10607c478bd9Sstevel@tonic-gate */
10617c478bd9Sstevel@tonic-gate mutex_enter(&dfnp->fn_lock);
10627c478bd9Sstevel@tonic-gate dfnp->fn_flags |= MF_MOUNTPOINT;
10637c478bd9Sstevel@tonic-gate ASSERT(dfnp->fn_dirents == NULL);
10647c478bd9Sstevel@tonic-gate mutex_exit(&dfnp->fn_lock);
10657c478bd9Sstevel@tonic-gate success++;
10667c478bd9Sstevel@tonic-gate } else {
10677c478bd9Sstevel@tonic-gate /*
10687c478bd9Sstevel@tonic-gate * Clear MF_MOUNTPOINT.
10697c478bd9Sstevel@tonic-gate */
10707c478bd9Sstevel@tonic-gate mutex_enter(&dfnp->fn_lock);
10717c478bd9Sstevel@tonic-gate if (dfnp->fn_flags & MF_MOUNTPOINT) {
10727c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((10, "autofs: clearing mountpoint "
1073d6c8399bSrmesta "flag on %s.", dfnp->fn_name));
10747c478bd9Sstevel@tonic-gate ASSERT(dfnp->fn_dirents == NULL);
10757c478bd9Sstevel@tonic-gate ASSERT(dfnp->fn_trigger == NULL);
10767c478bd9Sstevel@tonic-gate }
10777c478bd9Sstevel@tonic-gate dfnp->fn_flags &= ~MF_MOUNTPOINT;
10787c478bd9Sstevel@tonic-gate mutex_exit(&dfnp->fn_lock);
10797c478bd9Sstevel@tonic-gate }
10807c478bd9Sstevel@tonic-gate
10817c478bd9Sstevel@tonic-gate for (p = alp; p != NULL; p = p->next) {
108239d3e169Sevanl
10837c478bd9Sstevel@tonic-gate vfs_t *vfsp; /* dummy argument */
10847c478bd9Sstevel@tonic-gate vfs_t *mvfsp;
10857c478bd9Sstevel@tonic-gate
10867c478bd9Sstevel@tonic-gate auto_mount = 0;
10877c478bd9Sstevel@tonic-gate
10887c478bd9Sstevel@tonic-gate m = &p->action.action_list_entry_u.mounta;
10897c478bd9Sstevel@tonic-gate argsp = (struct autofs_args *)m->dataptr;
10905e1e04ceSdm ASSERT(strcmp(m->fstype, "autofs") == 0);
10915e1e04ceSdm /*
10925e1e04ceSdm * use the parent directory's timeout since it's the
10935e1e04ceSdm * one specified/inherited by automount.
10945e1e04ceSdm */
10955e1e04ceSdm argsp->mount_to = dfnip->fi_mount_to;
10965e1e04ceSdm /*
10975e1e04ceSdm * The mountpoint is relative, and it is guaranteed to
10985e1e04ceSdm * begin with "."
10995e1e04ceSdm *
11005e1e04ceSdm */
11015e1e04ceSdm ASSERT(m->dir[0] == '.');
11025e1e04ceSdm if (m->dir[0] == '.' && m->dir[1] == '\0') {
11037c478bd9Sstevel@tonic-gate /*
11045e1e04ceSdm * mounting on the trigger node
11057c478bd9Sstevel@tonic-gate */
11065e1e04ceSdm mvp = dvp;
11075e1e04ceSdm VN_HOLD(mvp);
11085e1e04ceSdm goto mount;
11095e1e04ceSdm }
11105e1e04ceSdm /*
11115e1e04ceSdm * ignore "./" in front of mountpoint
11125e1e04ceSdm */
11135e1e04ceSdm ASSERT(m->dir[1] == '/');
11145e1e04ceSdm mntpnt = m->dir + 2;
11155e1e04ceSdm
11165e1e04ceSdm AUTOFS_DPRINT((10, "\tdfnip->fi_path=%s\n", dfnip->fi_path));
11175e1e04ceSdm AUTOFS_DPRINT((10, "\tdfnip->fi_flags=%x\n", dfnip->fi_flags));
11185e1e04ceSdm AUTOFS_DPRINT((10, "\tmntpnt=%s\n", mntpnt));
11195e1e04ceSdm
11205e1e04ceSdm if (dfnip->fi_flags & MF_DIRECT) {
11215e1e04ceSdm AUTOFS_DPRINT((10, "\tDIRECT\n"));
1122d6c8399bSrmesta (void) sprintf(buff, "%s/%s", dfnip->fi_path, mntpnt);
11235e1e04ceSdm } else {
11245e1e04ceSdm AUTOFS_DPRINT((10, "\tINDIRECT\n"));
11255e1e04ceSdm (void) sprintf(buff, "%s/%s/%s",
1126d6c8399bSrmesta dfnip->fi_path, dfnp->fn_name, mntpnt);
11275e1e04ceSdm }
11285e1e04ceSdm
11295e1e04ceSdm if (vn_mountedvfs(dvp) == NULL) {
11307c478bd9Sstevel@tonic-gate /*
11315e1e04ceSdm * Daemon didn't mount anything on the root
11325e1e04ceSdm * We have to create the mountpoint if it
11335e1e04ceSdm * doesn't exist already
11347c478bd9Sstevel@tonic-gate *
11355e1e04ceSdm * We use the caller's credentials in case a
11365e1e04ceSdm * UID-match is required
11375e1e04ceSdm * (MF_THISUID_MATCH_RQD).
11387c478bd9Sstevel@tonic-gate */
11395e1e04ceSdm rw_enter(&dfnp->fn_rwlock, RW_WRITER);
11405e1e04ceSdm error = auto_search(dfnp, mntpnt, &mfnp, cred);
11415e1e04ceSdm if (error == 0) {
11427c478bd9Sstevel@tonic-gate /*
11435e1e04ceSdm * AUTOFS mountpoint exists
11447c478bd9Sstevel@tonic-gate */
11455e1e04ceSdm if (vn_mountedvfs(fntovn(mfnp)) != NULL) {
11465e1e04ceSdm cmn_err(CE_PANIC,
1147d6c8399bSrmesta "auto_perform_actions:"
1148d6c8399bSrmesta " mfnp=%p covered", (void *)mfnp);
11495e1e04ceSdm }
11507c478bd9Sstevel@tonic-gate } else {
11517c478bd9Sstevel@tonic-gate /*
11525e1e04ceSdm * Create AUTOFS mountpoint
11537c478bd9Sstevel@tonic-gate */
11545e1e04ceSdm ASSERT((dfnp->fn_flags & MF_MOUNTPOINT) == 0);
11555e1e04ceSdm error = auto_enter(dfnp, mntpnt, &mfnp, cred);
11565e1e04ceSdm ASSERT(mfnp->fn_linkcnt == 1);
11575e1e04ceSdm mfnp->fn_linkcnt++;
11585e1e04ceSdm }
11595e1e04ceSdm if (!error)
11605e1e04ceSdm update_times = 1;
11615e1e04ceSdm rw_exit(&dfnp->fn_rwlock);
11625e1e04ceSdm ASSERT(error != EEXIST);
11635e1e04ceSdm if (!error) {
11649670a2d7Skr /*
11655e1e04ceSdm * mfnp is already held.
11669670a2d7Skr */
11675e1e04ceSdm mvp = fntovn(mfnp);
11685e1e04ceSdm } else {
11695e1e04ceSdm auto_log(fngp->fng_verbose, fngp->fng_zoneid,
1170d6c8399bSrmesta CE_WARN, "autofs: mount of %s "
1171d6c8399bSrmesta "failed - can't create"
1172d6c8399bSrmesta " mountpoint.", buff);
1173d6c8399bSrmesta continue;
11747c478bd9Sstevel@tonic-gate }
11755e1e04ceSdm } else {
11769670a2d7Skr /*
11775e1e04ceSdm * Find mountpoint in VFS mounted here. If not
11785e1e04ceSdm * found, fail the submount, though the overall
11795e1e04ceSdm * mount has succeeded since the root is
11805e1e04ceSdm * mounted.
11819670a2d7Skr */
1182d6c8399bSrmesta if (error = auto_getmntpnt(dvp, mntpnt, &mvp, kcred)) {
1183d6c8399bSrmesta auto_log(fngp->fng_verbose, fngp->fng_zoneid,
1184d6c8399bSrmesta CE_WARN, "autofs: mount of %s "
1185d6c8399bSrmesta "failed - mountpoint doesn't"
1186d6c8399bSrmesta " exist.", buff);
11875e1e04ceSdm continue;
11885e1e04ceSdm }
11895e1e04ceSdm if (mvp->v_type == VLNK) {
1190d6c8399bSrmesta auto_log(fngp->fng_verbose, fngp->fng_zoneid,
1191d6c8399bSrmesta CE_WARN, "autofs: %s symbolic "
1192d6c8399bSrmesta "link: not a valid mountpoint "
1193d6c8399bSrmesta "- mount failed", buff);
11947c478bd9Sstevel@tonic-gate VN_RELE(mvp);
11955e1e04ceSdm error = ENOENT;
11967c478bd9Sstevel@tonic-gate continue;
11977c478bd9Sstevel@tonic-gate }
11985e1e04ceSdm }
11995e1e04ceSdm mount:
12005e1e04ceSdm m->flags |= MS_SYSSPACE | MS_OPTIONSTR;
120139d3e169Sevanl
12025e1e04ceSdm /*
12035e1e04ceSdm * Copy mounta struct here so we can substitute a
12045e1e04ceSdm * buffer that is large enough to hold the returned
12055e1e04ceSdm * option string, if that string is longer than the
12065e1e04ceSdm * input option string.
12075e1e04ceSdm * This can happen if there are default options enabled
12085e1e04ceSdm * that were not in the input option string.
12095e1e04ceSdm */
12105e1e04ceSdm bcopy(m, &margs, sizeof (*m));
12115e1e04ceSdm margs.optptr = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP);
12125e1e04ceSdm margs.optlen = MAX_MNTOPT_STR;
12135e1e04ceSdm (void) strcpy(margs.optptr, m->optptr);
12145e1e04ceSdm margs.dir = argsp->path;
12157c478bd9Sstevel@tonic-gate
12165e1e04ceSdm /*
12175e1e04ceSdm * We use the zone's kcred because we don't want the
12185e1e04ceSdm * zone to be able to thus do something it wouldn't
12195e1e04ceSdm * normally be able to.
12205e1e04ceSdm */
12215e1e04ceSdm error = domount(NULL, &margs, mvp, zcred, &vfsp);
12225e1e04ceSdm kmem_free(margs.optptr, MAX_MNTOPT_STR);
12235e1e04ceSdm if (error != 0) {
12245e1e04ceSdm auto_log(fngp->fng_verbose, fngp->fng_zoneid,
1225d6c8399bSrmesta CE_WARN, "autofs: domount of %s failed "
1226d6c8399bSrmesta "error=%d", buff, error);
12275e1e04ceSdm VN_RELE(mvp);
12285e1e04ceSdm continue;
12295e1e04ceSdm }
12305e1e04ceSdm VFS_RELE(vfsp);
12315e1e04ceSdm
12325e1e04ceSdm /*
12335e1e04ceSdm * If mountpoint is an AUTOFS node, then I'm going to
12345e1e04ceSdm * flag it that the Filesystem mounted on top was
12355e1e04ceSdm * mounted in the kernel so that the unmount can be
12365e1e04ceSdm * done inside the kernel as well.
12375e1e04ceSdm * I don't care to flag non-AUTOFS mountpoints when an
12385e1e04ceSdm * AUTOFS in-kernel mount was done on top, because the
12395e1e04ceSdm * unmount routine already knows that such case was
12405e1e04ceSdm * done in the kernel.
12415e1e04ceSdm */
1242d6c8399bSrmesta if (vfs_matchops(dvp->v_vfsp, vfs_getops(mvp->v_vfsp))) {
12435e1e04ceSdm mfnp = vntofn(mvp);
12445e1e04ceSdm mutex_enter(&mfnp->fn_lock);
12455e1e04ceSdm mfnp->fn_flags |= MF_IK_MOUNT;
12465e1e04ceSdm mutex_exit(&mfnp->fn_lock);
12475e1e04ceSdm }
12485e1e04ceSdm
12495e1e04ceSdm (void) vn_vfswlock_wait(mvp);
12505e1e04ceSdm mvfsp = vn_mountedvfs(mvp);
12515e1e04ceSdm if (mvfsp != NULL) {
12525e1e04ceSdm vfs_lock_wait(mvfsp);
12535e1e04ceSdm vn_vfsunlock(mvp);
12545e1e04ceSdm error = VFS_ROOT(mvfsp, &newvp);
12555e1e04ceSdm vfs_unlock(mvfsp);
12565e1e04ceSdm if (error) {
12575e1e04ceSdm /*
12585e1e04ceSdm * We've dropped the locks, so let's
12595e1e04ceSdm * get the mounted vfs again in case
12605e1e04ceSdm * it changed.
12615e1e04ceSdm */
12625e1e04ceSdm (void) vn_vfswlock_wait(mvp);
12635e1e04ceSdm mvfsp = vn_mountedvfs(mvp);
12645e1e04ceSdm if (mvfsp != NULL) {
12655e1e04ceSdm error = dounmount(mvfsp, 0, CRED());
12665e1e04ceSdm if (error) {
12675e1e04ceSdm cmn_err(CE_WARN,
1268d6c8399bSrmesta "autofs: could not unmount"
1269d6c8399bSrmesta " vfs=%p", (void *)mvfsp);
12705e1e04ceSdm }
12715e1e04ceSdm } else
12725e1e04ceSdm vn_vfsunlock(mvp);
12737c478bd9Sstevel@tonic-gate VN_RELE(mvp);
12747c478bd9Sstevel@tonic-gate continue;
12757c478bd9Sstevel@tonic-gate }
12765e1e04ceSdm } else {
12775e1e04ceSdm vn_vfsunlock(mvp);
12785e1e04ceSdm VN_RELE(mvp);
12795e1e04ceSdm continue;
12805e1e04ceSdm }
12817c478bd9Sstevel@tonic-gate
12825e1e04ceSdm auto_mount = vfs_matchops(dvp->v_vfsp,
1283d6c8399bSrmesta vfs_getops(newvp->v_vfsp));
12845e1e04ceSdm newfnp = vntofn(newvp);
12855e1e04ceSdm newfnp->fn_parent = dfnp;
12867c478bd9Sstevel@tonic-gate
12875e1e04ceSdm /*
12885e1e04ceSdm * At this time we want to save the AUTOFS filesystem
12895e1e04ceSdm * as a trigger node. (We only do this if the mount
12905e1e04ceSdm * occurred on a node different from the root.
12915e1e04ceSdm * We look at the trigger nodes during
12925e1e04ceSdm * the automatic unmounting to make sure we remove them
12935e1e04ceSdm * as a unit and remount them as a unit if the
12945e1e04ceSdm * filesystem mounted at the root could not be
12955e1e04ceSdm * unmounted.
12965e1e04ceSdm */
12975e1e04ceSdm if (auto_mount && (error == 0) && (mvp != dvp)) {
12985e1e04ceSdm save_triggers++;
12997c478bd9Sstevel@tonic-gate /*
13005e1e04ceSdm * Add AUTOFS mount to hierarchy
13017c478bd9Sstevel@tonic-gate */
13025e1e04ceSdm newfnp->fn_flags |= MF_TRIGGER;
13035e1e04ceSdm rw_enter(&newfnp->fn_rwlock, RW_WRITER);
13045e1e04ceSdm newfnp->fn_next = dfnp->fn_trigger;
13055e1e04ceSdm rw_exit(&newfnp->fn_rwlock);
13065e1e04ceSdm rw_enter(&dfnp->fn_rwlock, RW_WRITER);
13075e1e04ceSdm dfnp->fn_trigger = newfnp;
13085e1e04ceSdm rw_exit(&dfnp->fn_rwlock);
13095e1e04ceSdm /*
13105e1e04ceSdm * Don't VN_RELE(newvp) here since dfnp now
13115e1e04ceSdm * holds reference to it as its trigger node.
13125e1e04ceSdm */
13135e1e04ceSdm AUTOFS_DPRINT((10, "\tadding trigger %s to %s\n",
1314d6c8399bSrmesta newfnp->fn_name, dfnp->fn_name));
13155e1e04ceSdm AUTOFS_DPRINT((10, "\tfirst trigger is %s\n",
1316d6c8399bSrmesta dfnp->fn_trigger->fn_name));
13175e1e04ceSdm if (newfnp->fn_next != NULL)
1318d6c8399bSrmesta AUTOFS_DPRINT((10, "\tnext trigger is %s\n",
1319d6c8399bSrmesta newfnp->fn_next->fn_name));
13205e1e04ceSdm else
1321d6c8399bSrmesta AUTOFS_DPRINT((10, "\tno next trigger\n"));
13225e1e04ceSdm } else
13235e1e04ceSdm VN_RELE(newvp);
13247c478bd9Sstevel@tonic-gate
13255e1e04ceSdm if (!error)
13265e1e04ceSdm success++;
13277c478bd9Sstevel@tonic-gate
13285e1e04ceSdm if (update_times) {
13295e1e04ceSdm gethrestime(&now);
13305e1e04ceSdm dfnp->fn_atime = dfnp->fn_mtime = now;
13319670a2d7Skr }
13325e1e04ceSdm
13335e1e04ceSdm VN_RELE(mvp);
13347c478bd9Sstevel@tonic-gate }
13357c478bd9Sstevel@tonic-gate
13367c478bd9Sstevel@tonic-gate if (save_triggers) {
13377c478bd9Sstevel@tonic-gate /*
13387c478bd9Sstevel@tonic-gate * Make sure the parent can't be freed while it has triggers.
13397c478bd9Sstevel@tonic-gate */
13407c478bd9Sstevel@tonic-gate VN_HOLD(dvp);
13417c478bd9Sstevel@tonic-gate }
13427c478bd9Sstevel@tonic-gate
13437c478bd9Sstevel@tonic-gate crfree(zcred);
13447c478bd9Sstevel@tonic-gate
13457c478bd9Sstevel@tonic-gate done:
13467c478bd9Sstevel@tonic-gate /*
13477c478bd9Sstevel@tonic-gate * Return failure if daemon didn't mount anything, and all
13487c478bd9Sstevel@tonic-gate * kernel mounts attempted failed.
13497c478bd9Sstevel@tonic-gate */
13507c478bd9Sstevel@tonic-gate error = success ? 0 : ENOENT;
13517c478bd9Sstevel@tonic-gate
13527c478bd9Sstevel@tonic-gate if (alp != NULL) {
13537c478bd9Sstevel@tonic-gate if ((error == 0) && save_triggers) {
13547c478bd9Sstevel@tonic-gate /*
13557c478bd9Sstevel@tonic-gate * Save action_list information, so that we can use it
13567c478bd9Sstevel@tonic-gate * when it comes time to remount the trigger nodes
13577c478bd9Sstevel@tonic-gate * The action list is freed when the directory node
13587c478bd9Sstevel@tonic-gate * containing the reference to it is unmounted in
13597c478bd9Sstevel@tonic-gate * unmount_tree().
13607c478bd9Sstevel@tonic-gate */
13617c478bd9Sstevel@tonic-gate mutex_enter(&dfnp->fn_lock);
13627c478bd9Sstevel@tonic-gate ASSERT(dfnp->fn_alp == NULL);
13637c478bd9Sstevel@tonic-gate dfnp->fn_alp = alp;
13647c478bd9Sstevel@tonic-gate mutex_exit(&dfnp->fn_lock);
13657c478bd9Sstevel@tonic-gate } else {
13667c478bd9Sstevel@tonic-gate /*
13677c478bd9Sstevel@tonic-gate * free the action list now,
13687c478bd9Sstevel@tonic-gate */
13697c478bd9Sstevel@tonic-gate xdr_free(xdr_action_list, (char *)alp);
13707c478bd9Sstevel@tonic-gate }
13717c478bd9Sstevel@tonic-gate }
13727c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_perform_actions: error=%d\n", error));
13737c478bd9Sstevel@tonic-gate return (error);
13747c478bd9Sstevel@tonic-gate }
13757c478bd9Sstevel@tonic-gate
13767c478bd9Sstevel@tonic-gate fnnode_t *
auto_makefnnode(vtype_t type,vfs_t * vfsp,char * name,cred_t * cred,struct autofs_globals * fngp)13777c478bd9Sstevel@tonic-gate auto_makefnnode(
13787c478bd9Sstevel@tonic-gate vtype_t type,
13797c478bd9Sstevel@tonic-gate vfs_t *vfsp,
13807c478bd9Sstevel@tonic-gate char *name,
13817c478bd9Sstevel@tonic-gate cred_t *cred,
13827c478bd9Sstevel@tonic-gate struct autofs_globals *fngp)
13837c478bd9Sstevel@tonic-gate {
13847c478bd9Sstevel@tonic-gate fnnode_t *fnp;
13857c478bd9Sstevel@tonic-gate vnode_t *vp;
13867c478bd9Sstevel@tonic-gate char *tmpname;
13877c478bd9Sstevel@tonic-gate timestruc_t now;
13887c478bd9Sstevel@tonic-gate /*
13897c478bd9Sstevel@tonic-gate * autofs uses odd inode numbers
13907c478bd9Sstevel@tonic-gate * automountd uses even inode numbers
13917c478bd9Sstevel@tonic-gate *
13927c478bd9Sstevel@tonic-gate * To preserve the age-old semantics that inum+devid is unique across
13937c478bd9Sstevel@tonic-gate * the system, this variable must be global across zones.
13947c478bd9Sstevel@tonic-gate */
13957c478bd9Sstevel@tonic-gate static ino_t nodeid = 3;
13967c478bd9Sstevel@tonic-gate
13977c478bd9Sstevel@tonic-gate fnp = kmem_zalloc(sizeof (*fnp), KM_SLEEP);
13987c478bd9Sstevel@tonic-gate fnp->fn_vnode = vn_alloc(KM_SLEEP);
13997c478bd9Sstevel@tonic-gate
14007c478bd9Sstevel@tonic-gate vp = fntovn(fnp);
14017c478bd9Sstevel@tonic-gate tmpname = kmem_alloc(strlen(name) + 1, KM_SLEEP);
14027c478bd9Sstevel@tonic-gate (void) strcpy(tmpname, name);
14037c478bd9Sstevel@tonic-gate fnp->fn_name = &tmpname[0];
14047c478bd9Sstevel@tonic-gate fnp->fn_namelen = (int)strlen(tmpname) + 1; /* include '\0' */
14057c478bd9Sstevel@tonic-gate fnp->fn_uid = crgetuid(cred);
14067c478bd9Sstevel@tonic-gate fnp->fn_gid = crgetgid(cred);
14077c478bd9Sstevel@tonic-gate /*
14087c478bd9Sstevel@tonic-gate * ".." is added in auto_enter and auto_mount.
14097c478bd9Sstevel@tonic-gate * "." is added in auto_mkdir and auto_mount.
14107c478bd9Sstevel@tonic-gate */
14117c478bd9Sstevel@tonic-gate /*
14127c478bd9Sstevel@tonic-gate * Note that fn_size and fn_linkcnt are already 0 since
14137c478bd9Sstevel@tonic-gate * we used kmem_zalloc to allocated fnp
14147c478bd9Sstevel@tonic-gate */
14157c478bd9Sstevel@tonic-gate fnp->fn_mode = AUTOFS_MODE;
14167c478bd9Sstevel@tonic-gate gethrestime(&now);
14177c478bd9Sstevel@tonic-gate fnp->fn_atime = fnp->fn_mtime = fnp->fn_ctime = now;
14187c478bd9Sstevel@tonic-gate fnp->fn_ref_time = now.tv_sec;
14197c478bd9Sstevel@tonic-gate mutex_enter(&autofs_nodeid_lock);
14207c478bd9Sstevel@tonic-gate fnp->fn_nodeid = nodeid;
14217c478bd9Sstevel@tonic-gate nodeid += 2;
14227c478bd9Sstevel@tonic-gate fnp->fn_globals = fngp;
14237c478bd9Sstevel@tonic-gate fngp->fng_fnnode_count++;
14247c478bd9Sstevel@tonic-gate mutex_exit(&autofs_nodeid_lock);
14257c478bd9Sstevel@tonic-gate vn_setops(vp, auto_vnodeops);
14267c478bd9Sstevel@tonic-gate vp->v_type = type;
14277c478bd9Sstevel@tonic-gate vp->v_data = (void *)fnp;
14287c478bd9Sstevel@tonic-gate vp->v_vfsp = vfsp;
14297c478bd9Sstevel@tonic-gate mutex_init(&fnp->fn_lock, NULL, MUTEX_DEFAULT, NULL);
14307c478bd9Sstevel@tonic-gate rw_init(&fnp->fn_rwlock, NULL, RW_DEFAULT, NULL);
14317c478bd9Sstevel@tonic-gate cv_init(&fnp->fn_cv_mount, NULL, CV_DEFAULT, NULL);
14327c478bd9Sstevel@tonic-gate vn_exists(vp);
14337c478bd9Sstevel@tonic-gate return (fnp);
14347c478bd9Sstevel@tonic-gate }
14357c478bd9Sstevel@tonic-gate
14367c478bd9Sstevel@tonic-gate
14377c478bd9Sstevel@tonic-gate void
auto_freefnnode(fnnode_t * fnp)14387c478bd9Sstevel@tonic-gate auto_freefnnode(fnnode_t *fnp)
14397c478bd9Sstevel@tonic-gate {
14407c478bd9Sstevel@tonic-gate vnode_t *vp = fntovn(fnp);
14417c478bd9Sstevel@tonic-gate
14427c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_freefnnode: fnp=%p\n", (void *)fnp));
14437c478bd9Sstevel@tonic-gate
14447c478bd9Sstevel@tonic-gate ASSERT(fnp->fn_linkcnt == 0);
14457c478bd9Sstevel@tonic-gate ASSERT(vp->v_count == 0);
14467c478bd9Sstevel@tonic-gate ASSERT(fnp->fn_dirents == NULL);
14477c478bd9Sstevel@tonic-gate ASSERT(fnp->fn_parent == NULL);
14487c478bd9Sstevel@tonic-gate
14497c478bd9Sstevel@tonic-gate vn_invalid(vp);
14507c478bd9Sstevel@tonic-gate kmem_free(fnp->fn_name, fnp->fn_namelen);
14517c478bd9Sstevel@tonic-gate if (fnp->fn_symlink) {
14527c478bd9Sstevel@tonic-gate ASSERT(fnp->fn_flags & MF_THISUID_MATCH_RQD);
14537c478bd9Sstevel@tonic-gate kmem_free(fnp->fn_symlink, fnp->fn_symlinklen);
14547c478bd9Sstevel@tonic-gate }
14557c478bd9Sstevel@tonic-gate if (fnp->fn_cred)
14567c478bd9Sstevel@tonic-gate crfree(fnp->fn_cred);
14577c478bd9Sstevel@tonic-gate mutex_destroy(&fnp->fn_lock);
14587c478bd9Sstevel@tonic-gate rw_destroy(&fnp->fn_rwlock);
14597c478bd9Sstevel@tonic-gate cv_destroy(&fnp->fn_cv_mount);
14607c478bd9Sstevel@tonic-gate vn_free(vp);
14617c478bd9Sstevel@tonic-gate
14627c478bd9Sstevel@tonic-gate mutex_enter(&autofs_nodeid_lock);
14637c478bd9Sstevel@tonic-gate fnp->fn_globals->fng_fnnode_count--;
14647c478bd9Sstevel@tonic-gate mutex_exit(&autofs_nodeid_lock);
14657c478bd9Sstevel@tonic-gate kmem_free(fnp, sizeof (*fnp));
14667c478bd9Sstevel@tonic-gate }
14677c478bd9Sstevel@tonic-gate
14687c478bd9Sstevel@tonic-gate void
auto_disconnect(fnnode_t * dfnp,fnnode_t * fnp)14697c478bd9Sstevel@tonic-gate auto_disconnect(
14707c478bd9Sstevel@tonic-gate fnnode_t *dfnp,
14717c478bd9Sstevel@tonic-gate fnnode_t *fnp)
14727c478bd9Sstevel@tonic-gate {
14737c478bd9Sstevel@tonic-gate fnnode_t *tmp, **fnpp;
14747c478bd9Sstevel@tonic-gate vnode_t *vp = fntovn(fnp);
14757c478bd9Sstevel@tonic-gate timestruc_t now;
14767c478bd9Sstevel@tonic-gate
14777c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4,
14787c478bd9Sstevel@tonic-gate "auto_disconnect: dfnp=%p fnp=%p linkcnt=%d\n v_count=%d",
14797c478bd9Sstevel@tonic-gate (void *)dfnp, (void *)fnp, fnp->fn_linkcnt, vp->v_count));
14807c478bd9Sstevel@tonic-gate
14817c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
14827c478bd9Sstevel@tonic-gate ASSERT(fnp->fn_linkcnt == 1);
14837c478bd9Sstevel@tonic-gate
14847c478bd9Sstevel@tonic-gate if (vn_mountedvfs(vp) != NULL) {
14857c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "auto_disconnect: vp %p mounted on",
14867c478bd9Sstevel@tonic-gate (void *)vp);
14877c478bd9Sstevel@tonic-gate }
14887c478bd9Sstevel@tonic-gate
14897c478bd9Sstevel@tonic-gate /*
14907c478bd9Sstevel@tonic-gate * Decrement by 1 because we're removing the entry in dfnp.
14917c478bd9Sstevel@tonic-gate */
14927c478bd9Sstevel@tonic-gate fnp->fn_linkcnt--;
14937c478bd9Sstevel@tonic-gate fnp->fn_size--;
14947c478bd9Sstevel@tonic-gate
14957c478bd9Sstevel@tonic-gate /*
14967c478bd9Sstevel@tonic-gate * only changed while holding parent's (dfnp) rw_lock
14977c478bd9Sstevel@tonic-gate */
14987c478bd9Sstevel@tonic-gate fnp->fn_parent = NULL;
14997c478bd9Sstevel@tonic-gate
15007c478bd9Sstevel@tonic-gate fnpp = &dfnp->fn_dirents;
15017c478bd9Sstevel@tonic-gate for (;;) {
15027c478bd9Sstevel@tonic-gate tmp = *fnpp;
15037c478bd9Sstevel@tonic-gate if (tmp == NULL) {
15047c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC,
15057c478bd9Sstevel@tonic-gate "auto_disconnect: %p not in %p dirent list",
15067c478bd9Sstevel@tonic-gate (void *)fnp, (void *)dfnp);
15077c478bd9Sstevel@tonic-gate }
15087c478bd9Sstevel@tonic-gate if (tmp == fnp) {
15097c478bd9Sstevel@tonic-gate *fnpp = tmp->fn_next; /* remove it from the list */
15107c478bd9Sstevel@tonic-gate ASSERT(vp->v_count == 0);
15117c478bd9Sstevel@tonic-gate /* child had a pointer to parent ".." */
15127c478bd9Sstevel@tonic-gate dfnp->fn_linkcnt--;
15137c478bd9Sstevel@tonic-gate dfnp->fn_size--;
15147c478bd9Sstevel@tonic-gate break;
15157c478bd9Sstevel@tonic-gate }
15167c478bd9Sstevel@tonic-gate fnpp = &tmp->fn_next;
15177c478bd9Sstevel@tonic-gate }
15187c478bd9Sstevel@tonic-gate
15197c478bd9Sstevel@tonic-gate mutex_enter(&fnp->fn_lock);
15207c478bd9Sstevel@tonic-gate gethrestime(&now);
15217c478bd9Sstevel@tonic-gate fnp->fn_atime = fnp->fn_mtime = now;
15227c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
15237c478bd9Sstevel@tonic-gate
15247c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_disconnect: done\n"));
15257c478bd9Sstevel@tonic-gate }
15267c478bd9Sstevel@tonic-gate
15277c478bd9Sstevel@tonic-gate int
auto_enter(fnnode_t * dfnp,char * name,fnnode_t ** fnpp,cred_t * cred)15287c478bd9Sstevel@tonic-gate auto_enter(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred)
15297c478bd9Sstevel@tonic-gate {
15307c478bd9Sstevel@tonic-gate struct fnnode *cfnp, **spp;
15317c478bd9Sstevel@tonic-gate vnode_t *dvp = fntovn(dfnp);
15327c478bd9Sstevel@tonic-gate ushort_t offset = 0;
15337c478bd9Sstevel@tonic-gate ushort_t diff;
15347c478bd9Sstevel@tonic-gate
15357c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_enter: dfnp=%p, name=%s ", (void *)dfnp, name));
15367c478bd9Sstevel@tonic-gate
15377c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
15387c478bd9Sstevel@tonic-gate
15397c478bd9Sstevel@tonic-gate cfnp = dfnp->fn_dirents;
15407c478bd9Sstevel@tonic-gate if (cfnp == NULL) {
15417c478bd9Sstevel@tonic-gate /*
15427c478bd9Sstevel@tonic-gate * offset = 0 for '.' and offset = 1 for '..'
15437c478bd9Sstevel@tonic-gate */
15447c478bd9Sstevel@tonic-gate spp = &dfnp->fn_dirents;
15457c478bd9Sstevel@tonic-gate offset = 2;
15467c478bd9Sstevel@tonic-gate }
15477c478bd9Sstevel@tonic-gate
15487c478bd9Sstevel@tonic-gate for (; cfnp; cfnp = cfnp->fn_next) {
15497c478bd9Sstevel@tonic-gate if (strcmp(cfnp->fn_name, name) == 0) {
15507c478bd9Sstevel@tonic-gate mutex_enter(&cfnp->fn_lock);
15517c478bd9Sstevel@tonic-gate if (cfnp->fn_flags & MF_THISUID_MATCH_RQD) {
15527c478bd9Sstevel@tonic-gate /*
15537c478bd9Sstevel@tonic-gate * "thisuser" kind of node, need to
15547c478bd9Sstevel@tonic-gate * match CREDs as well
15557c478bd9Sstevel@tonic-gate */
15567c478bd9Sstevel@tonic-gate mutex_exit(&cfnp->fn_lock);
15577c478bd9Sstevel@tonic-gate if (crcmp(cfnp->fn_cred, cred) == 0)
15587c478bd9Sstevel@tonic-gate return (EEXIST);
15597c478bd9Sstevel@tonic-gate } else {
15607c478bd9Sstevel@tonic-gate mutex_exit(&cfnp->fn_lock);
15617c478bd9Sstevel@tonic-gate return (EEXIST);
15627c478bd9Sstevel@tonic-gate }
15637c478bd9Sstevel@tonic-gate }
15647c478bd9Sstevel@tonic-gate
15657c478bd9Sstevel@tonic-gate if (cfnp->fn_next != NULL) {
15667c478bd9Sstevel@tonic-gate diff = (ushort_t)
15677c478bd9Sstevel@tonic-gate (cfnp->fn_next->fn_offset - cfnp->fn_offset);
15687c478bd9Sstevel@tonic-gate ASSERT(diff != 0);
15697c478bd9Sstevel@tonic-gate if (diff > 1 && offset == 0) {
15707c478bd9Sstevel@tonic-gate offset = (ushort_t)cfnp->fn_offset + 1;
15717c478bd9Sstevel@tonic-gate spp = &cfnp->fn_next;
15727c478bd9Sstevel@tonic-gate }
15737c478bd9Sstevel@tonic-gate } else if (offset == 0) {
15747c478bd9Sstevel@tonic-gate offset = (ushort_t)cfnp->fn_offset + 1;
15757c478bd9Sstevel@tonic-gate spp = &cfnp->fn_next;
15767c478bd9Sstevel@tonic-gate }
15777c478bd9Sstevel@tonic-gate }
15787c478bd9Sstevel@tonic-gate
15797c478bd9Sstevel@tonic-gate *fnpp = auto_makefnnode(VDIR, dvp->v_vfsp, name, cred,
15807c478bd9Sstevel@tonic-gate dfnp->fn_globals);
15817c478bd9Sstevel@tonic-gate if (*fnpp == NULL)
15827c478bd9Sstevel@tonic-gate return (ENOMEM);
15837c478bd9Sstevel@tonic-gate
15847c478bd9Sstevel@tonic-gate /*
15857c478bd9Sstevel@tonic-gate * I don't hold the mutex on fnpp because I created it, and
15867c478bd9Sstevel@tonic-gate * I'm already holding the writers lock for it's parent
15877c478bd9Sstevel@tonic-gate * directory, therefore nobody can reference it without me first
15887c478bd9Sstevel@tonic-gate * releasing the writers lock.
15897c478bd9Sstevel@tonic-gate */
15907c478bd9Sstevel@tonic-gate (*fnpp)->fn_offset = offset;
15917c478bd9Sstevel@tonic-gate (*fnpp)->fn_next = *spp;
15927c478bd9Sstevel@tonic-gate *spp = *fnpp;
15937c478bd9Sstevel@tonic-gate (*fnpp)->fn_parent = dfnp;
15947c478bd9Sstevel@tonic-gate (*fnpp)->fn_linkcnt++; /* parent now holds reference to entry */
15957c478bd9Sstevel@tonic-gate (*fnpp)->fn_size++;
15967c478bd9Sstevel@tonic-gate
15977c478bd9Sstevel@tonic-gate /*
15987c478bd9Sstevel@tonic-gate * dfnp->fn_linkcnt and dfnp->fn_size protected by dfnp->rw_lock
15997c478bd9Sstevel@tonic-gate */
16007c478bd9Sstevel@tonic-gate dfnp->fn_linkcnt++; /* child now holds reference to parent '..' */
16017c478bd9Sstevel@tonic-gate dfnp->fn_size++;
16027c478bd9Sstevel@tonic-gate
16037c478bd9Sstevel@tonic-gate dfnp->fn_ref_time = gethrestime_sec();
16047c478bd9Sstevel@tonic-gate
16057c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "*fnpp=%p\n", (void *)*fnpp));
16067c478bd9Sstevel@tonic-gate return (0);
16077c478bd9Sstevel@tonic-gate }
16087c478bd9Sstevel@tonic-gate
16097c478bd9Sstevel@tonic-gate int
auto_search(fnnode_t * dfnp,char * name,fnnode_t ** fnpp,cred_t * cred)16107c478bd9Sstevel@tonic-gate auto_search(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred)
16117c478bd9Sstevel@tonic-gate {
16127c478bd9Sstevel@tonic-gate vnode_t *dvp;
16137c478bd9Sstevel@tonic-gate fnnode_t *p;
16147c478bd9Sstevel@tonic-gate int error = ENOENT, match = 0;
16157c478bd9Sstevel@tonic-gate
16167c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_search: dfnp=%p, name=%s...\n",
16177c478bd9Sstevel@tonic-gate (void *)dfnp, name));
16187c478bd9Sstevel@tonic-gate
16197c478bd9Sstevel@tonic-gate dvp = fntovn(dfnp);
16207c478bd9Sstevel@tonic-gate if (dvp->v_type != VDIR) {
16217c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "auto_search: dvp=%p not a directory",
16227c478bd9Sstevel@tonic-gate (void *)dvp);
16237c478bd9Sstevel@tonic-gate }
16247c478bd9Sstevel@tonic-gate
16257c478bd9Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&dfnp->fn_rwlock));
16267c478bd9Sstevel@tonic-gate for (p = dfnp->fn_dirents; p != NULL; p = p->fn_next) {
16277c478bd9Sstevel@tonic-gate if (strcmp(p->fn_name, name) == 0) {
16287c478bd9Sstevel@tonic-gate mutex_enter(&p->fn_lock);
16297c478bd9Sstevel@tonic-gate if (p->fn_flags & MF_THISUID_MATCH_RQD) {
16307c478bd9Sstevel@tonic-gate /*
16317c478bd9Sstevel@tonic-gate * "thisuser" kind of node
16327c478bd9Sstevel@tonic-gate * Need to match CREDs as well
16337c478bd9Sstevel@tonic-gate */
16347c478bd9Sstevel@tonic-gate mutex_exit(&p->fn_lock);
16357c478bd9Sstevel@tonic-gate match = crcmp(p->fn_cred, cred) == 0;
16367c478bd9Sstevel@tonic-gate } else {
16377c478bd9Sstevel@tonic-gate /*
16387c478bd9Sstevel@tonic-gate * No need to check CRED
16397c478bd9Sstevel@tonic-gate */
16407c478bd9Sstevel@tonic-gate mutex_exit(&p->fn_lock);
16417c478bd9Sstevel@tonic-gate match = 1;
16427c478bd9Sstevel@tonic-gate }
16437c478bd9Sstevel@tonic-gate }
16447c478bd9Sstevel@tonic-gate if (match) {
16457c478bd9Sstevel@tonic-gate error = 0;
16467c478bd9Sstevel@tonic-gate if (fnpp) {
16477c478bd9Sstevel@tonic-gate *fnpp = p;
16487c478bd9Sstevel@tonic-gate VN_HOLD(fntovn(*fnpp));
16497c478bd9Sstevel@tonic-gate }
16507c478bd9Sstevel@tonic-gate break;
16517c478bd9Sstevel@tonic-gate }
16527c478bd9Sstevel@tonic-gate }
16537c478bd9Sstevel@tonic-gate
16547c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_search: error=%d\n", error));
16557c478bd9Sstevel@tonic-gate return (error);
16567c478bd9Sstevel@tonic-gate }
16577c478bd9Sstevel@tonic-gate
16587c478bd9Sstevel@tonic-gate /*
16597c478bd9Sstevel@tonic-gate * If dvp is mounted on, get path's vnode in the mounted on
16607c478bd9Sstevel@tonic-gate * filesystem. Path is relative to dvp, ie "./path".
16617c478bd9Sstevel@tonic-gate * If successful, *mvp points to a the held mountpoint vnode.
16627c478bd9Sstevel@tonic-gate */
16637c478bd9Sstevel@tonic-gate /* ARGSUSED */
16647c478bd9Sstevel@tonic-gate static int
auto_getmntpnt(vnode_t * dvp,char * path,vnode_t ** mvpp,cred_t * cred)16657c478bd9Sstevel@tonic-gate auto_getmntpnt(
16667c478bd9Sstevel@tonic-gate vnode_t *dvp,
16677c478bd9Sstevel@tonic-gate char *path,
16687c478bd9Sstevel@tonic-gate vnode_t **mvpp, /* vnode for mountpoint */
16697c478bd9Sstevel@tonic-gate cred_t *cred)
16707c478bd9Sstevel@tonic-gate {
16717c478bd9Sstevel@tonic-gate int error = 0;
16727c478bd9Sstevel@tonic-gate vnode_t *newvp;
16737c478bd9Sstevel@tonic-gate char namebuf[TYPICALMAXPATHLEN];
16747c478bd9Sstevel@tonic-gate struct pathname lookpn;
16757c478bd9Sstevel@tonic-gate vfs_t *vfsp;
16767c478bd9Sstevel@tonic-gate
16777c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_getmntpnt: path=%s\n", path));
16787c478bd9Sstevel@tonic-gate
167995b97885Snr if (error = vn_vfsrlock_wait(dvp))
16807c478bd9Sstevel@tonic-gate return (error);
16817c478bd9Sstevel@tonic-gate
16827c478bd9Sstevel@tonic-gate /*
16837c478bd9Sstevel@tonic-gate * Now that we have the vfswlock, check to see if dvp
16847c478bd9Sstevel@tonic-gate * is still mounted on. If not, then just bail out as
16857c478bd9Sstevel@tonic-gate * there is no need to remount the triggers since the
16867c478bd9Sstevel@tonic-gate * higher level mount point has gotten unmounted.
16877c478bd9Sstevel@tonic-gate */
16887c478bd9Sstevel@tonic-gate vfsp = vn_mountedvfs(dvp);
16897c478bd9Sstevel@tonic-gate if (vfsp == NULL) {
16907c478bd9Sstevel@tonic-gate vn_vfsunlock(dvp);
16917c478bd9Sstevel@tonic-gate error = EBUSY;
16927c478bd9Sstevel@tonic-gate goto done;
16937c478bd9Sstevel@tonic-gate }
16947c478bd9Sstevel@tonic-gate /*
16957c478bd9Sstevel@tonic-gate * Since mounted on, lookup "path" in the new filesystem,
16967c478bd9Sstevel@tonic-gate * it is important that we do the filesystem jump here to
16977c478bd9Sstevel@tonic-gate * avoid lookuppn() calling auto_lookup on dvp and deadlock.
16987c478bd9Sstevel@tonic-gate */
16997c478bd9Sstevel@tonic-gate error = VFS_ROOT(vfsp, &newvp);
170095b97885Snr vn_vfsunlock(dvp);
17017c478bd9Sstevel@tonic-gate if (error)
17027c478bd9Sstevel@tonic-gate goto done;
17037c478bd9Sstevel@tonic-gate
17047c478bd9Sstevel@tonic-gate /*
17057c478bd9Sstevel@tonic-gate * We do a VN_HOLD on newvp just in case the first call to
17067c478bd9Sstevel@tonic-gate * lookuppnvp() fails with ENAMETOOLONG. We should still have a
17077c478bd9Sstevel@tonic-gate * reference to this vnode for the second call to lookuppnvp().
17087c478bd9Sstevel@tonic-gate */
17097c478bd9Sstevel@tonic-gate VN_HOLD(newvp);
17107c478bd9Sstevel@tonic-gate
17117c478bd9Sstevel@tonic-gate /*
17127c478bd9Sstevel@tonic-gate * Now create the pathname struct so we can make use of lookuppnvp,
17137c478bd9Sstevel@tonic-gate * and pn_getcomponent.
17147c478bd9Sstevel@tonic-gate * This code is similar to lookupname() in fs/lookup.c.
17157c478bd9Sstevel@tonic-gate */
17167c478bd9Sstevel@tonic-gate error = pn_get_buf(path, UIO_SYSSPACE, &lookpn,
1717d6c8399bSrmesta namebuf, sizeof (namebuf));
17187c478bd9Sstevel@tonic-gate if (error == 0) {
17197c478bd9Sstevel@tonic-gate error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP,
17207c478bd9Sstevel@tonic-gate mvpp, rootdir, newvp, cred);
17217c478bd9Sstevel@tonic-gate } else
17227c478bd9Sstevel@tonic-gate VN_RELE(newvp);
17237c478bd9Sstevel@tonic-gate if (error == ENAMETOOLONG) {
17247c478bd9Sstevel@tonic-gate /*
17257c478bd9Sstevel@tonic-gate * This thread used a pathname > TYPICALMAXPATHLEN bytes long.
17267c478bd9Sstevel@tonic-gate * newvp is VN_RELE'd by this call to lookuppnvp.
17277c478bd9Sstevel@tonic-gate *
17287c478bd9Sstevel@tonic-gate * Using 'rootdir' in a zone's context is OK here: we already
17297c478bd9Sstevel@tonic-gate * ascertained that there are no '..'s in the path, and we're
17307c478bd9Sstevel@tonic-gate * not following symlinks.
17317c478bd9Sstevel@tonic-gate */
17327c478bd9Sstevel@tonic-gate if ((error = pn_get(path, UIO_SYSSPACE, &lookpn)) == 0) {
17337c478bd9Sstevel@tonic-gate error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP,
17347c478bd9Sstevel@tonic-gate mvpp, rootdir, newvp, cred);
17357c478bd9Sstevel@tonic-gate pn_free(&lookpn);
17367c478bd9Sstevel@tonic-gate } else
17377c478bd9Sstevel@tonic-gate VN_RELE(newvp);
17387c478bd9Sstevel@tonic-gate } else {
17397c478bd9Sstevel@tonic-gate /*
17407c478bd9Sstevel@tonic-gate * Need to release newvp here since we held it.
17417c478bd9Sstevel@tonic-gate */
17427c478bd9Sstevel@tonic-gate VN_RELE(newvp);
17437c478bd9Sstevel@tonic-gate }
17447c478bd9Sstevel@tonic-gate
17457c478bd9Sstevel@tonic-gate done:
17467c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_getmntpnt: path=%s *mvpp=%p error=%d\n",
17477c478bd9Sstevel@tonic-gate path, (void *)*mvpp, error));
17487c478bd9Sstevel@tonic-gate return (error);
17497c478bd9Sstevel@tonic-gate }
17507c478bd9Sstevel@tonic-gate
17517c478bd9Sstevel@tonic-gate #define DEEPER(x) (((x)->fn_dirents != NULL) || \
17527c478bd9Sstevel@tonic-gate (vn_mountedvfs(fntovn((x)))) != NULL)
17537c478bd9Sstevel@tonic-gate
17547c478bd9Sstevel@tonic-gate /*
17557c478bd9Sstevel@tonic-gate * The caller, should have already VN_RELE'd its reference to the
17567c478bd9Sstevel@tonic-gate * root vnode of this filesystem.
17577c478bd9Sstevel@tonic-gate */
17587c478bd9Sstevel@tonic-gate static int
auto_inkernel_unmount(vfs_t * vfsp)17597c478bd9Sstevel@tonic-gate auto_inkernel_unmount(vfs_t *vfsp)
17607c478bd9Sstevel@tonic-gate {
17617c478bd9Sstevel@tonic-gate vnode_t *cvp = vfsp->vfs_vnodecovered;
17627c478bd9Sstevel@tonic-gate int error;
17637c478bd9Sstevel@tonic-gate
17647c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4,
17657c478bd9Sstevel@tonic-gate "auto_inkernel_unmount: devid=%lx mntpnt(%p) count %u\n",
17667c478bd9Sstevel@tonic-gate vfsp->vfs_dev, (void *)cvp, cvp->v_count));
17677c478bd9Sstevel@tonic-gate
17687c478bd9Sstevel@tonic-gate ASSERT(vn_vfswlock_held(cvp));
17697c478bd9Sstevel@tonic-gate
17707c478bd9Sstevel@tonic-gate /*
17717c478bd9Sstevel@tonic-gate * Perform the unmount
17727c478bd9Sstevel@tonic-gate * The mountpoint has already been locked by the caller.
17737c478bd9Sstevel@tonic-gate */
17747c478bd9Sstevel@tonic-gate error = dounmount(vfsp, 0, kcred);
17757c478bd9Sstevel@tonic-gate
17767c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_inkernel_unmount: exit count %u\n",
17777c478bd9Sstevel@tonic-gate cvp->v_count));
17787c478bd9Sstevel@tonic-gate return (error);
17797c478bd9Sstevel@tonic-gate }
17807c478bd9Sstevel@tonic-gate
17817c478bd9Sstevel@tonic-gate /*
17827c478bd9Sstevel@tonic-gate * unmounts trigger nodes in the kernel.
17837c478bd9Sstevel@tonic-gate */
17847c478bd9Sstevel@tonic-gate static void
unmount_triggers(fnnode_t * fnp,action_list ** alp)17857c478bd9Sstevel@tonic-gate unmount_triggers(fnnode_t *fnp, action_list **alp)
17867c478bd9Sstevel@tonic-gate {
17877c478bd9Sstevel@tonic-gate fnnode_t *tp, *next;
17887c478bd9Sstevel@tonic-gate int error = 0;
17897c478bd9Sstevel@tonic-gate vfs_t *vfsp;
17907c478bd9Sstevel@tonic-gate vnode_t *tvp;
17917c478bd9Sstevel@tonic-gate
17927c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "unmount_triggers: fnp=%p\n", (void *)fnp));
17937c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
17947c478bd9Sstevel@tonic-gate
17957c478bd9Sstevel@tonic-gate *alp = fnp->fn_alp;
17967c478bd9Sstevel@tonic-gate next = fnp->fn_trigger;
17977c478bd9Sstevel@tonic-gate while ((tp = next) != NULL) {
17987c478bd9Sstevel@tonic-gate tvp = fntovn(tp);
17997c478bd9Sstevel@tonic-gate ASSERT(tvp->v_count >= 2);
18007c478bd9Sstevel@tonic-gate next = tp->fn_next;
18017c478bd9Sstevel@tonic-gate /*
18027c478bd9Sstevel@tonic-gate * drop writer's lock since the unmount will end up
18037c478bd9Sstevel@tonic-gate * disconnecting this node from fnp and needs to acquire
18047c478bd9Sstevel@tonic-gate * the writer's lock again.
18057c478bd9Sstevel@tonic-gate * next has at least a reference count >= 2 since it's
18067c478bd9Sstevel@tonic-gate * a trigger node, therefore can not be accidentally freed
18077c478bd9Sstevel@tonic-gate * by a VN_RELE
18087c478bd9Sstevel@tonic-gate */
18097c478bd9Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock);
18107c478bd9Sstevel@tonic-gate
18117c478bd9Sstevel@tonic-gate vfsp = tvp->v_vfsp;
18127c478bd9Sstevel@tonic-gate
18137c478bd9Sstevel@tonic-gate /*
18147c478bd9Sstevel@tonic-gate * Its parent was holding a reference to it, since this
18157c478bd9Sstevel@tonic-gate * is a trigger vnode.
18167c478bd9Sstevel@tonic-gate */
18177c478bd9Sstevel@tonic-gate VN_RELE(tvp);
18187c478bd9Sstevel@tonic-gate if (error = auto_inkernel_unmount(vfsp)) {
18197c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "unmount_triggers: "
18207c478bd9Sstevel@tonic-gate "unmount of vp=%p failed error=%d",
18217c478bd9Sstevel@tonic-gate (void *)tvp, error);
18227c478bd9Sstevel@tonic-gate }
18237c478bd9Sstevel@tonic-gate /*
18247c478bd9Sstevel@tonic-gate * reacquire writer's lock
18257c478bd9Sstevel@tonic-gate */
18267c478bd9Sstevel@tonic-gate rw_enter(&fnp->fn_rwlock, RW_WRITER);
18277c478bd9Sstevel@tonic-gate }
18287c478bd9Sstevel@tonic-gate
18297c478bd9Sstevel@tonic-gate /*
18307c478bd9Sstevel@tonic-gate * We were holding a reference to our parent. Drop that.
18317c478bd9Sstevel@tonic-gate */
18327c478bd9Sstevel@tonic-gate VN_RELE(fntovn(fnp));
18337c478bd9Sstevel@tonic-gate fnp->fn_trigger = NULL;
18347c478bd9Sstevel@tonic-gate fnp->fn_alp = NULL;
18357c478bd9Sstevel@tonic-gate
18367c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "unmount_triggers: finished\n"));
18377c478bd9Sstevel@tonic-gate }
18387c478bd9Sstevel@tonic-gate
18397c478bd9Sstevel@tonic-gate /*
18407c478bd9Sstevel@tonic-gate * This routine locks the mountpoint of every trigger node if they're
1841f798ee53SJan Kryl * not busy, or returns EBUSY if any node is busy.
18427c478bd9Sstevel@tonic-gate */
1843f798ee53SJan Kryl static boolean_t
triggers_busy(fnnode_t * fnp)1844f798ee53SJan Kryl triggers_busy(fnnode_t *fnp)
18457c478bd9Sstevel@tonic-gate {
1846f798ee53SJan Kryl int done;
18477c478bd9Sstevel@tonic-gate int lck_error = 0;
18487c478bd9Sstevel@tonic-gate fnnode_t *tp, *t1p;
18497c478bd9Sstevel@tonic-gate vfs_t *vfsp;
18507c478bd9Sstevel@tonic-gate
18517c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock));
18527c478bd9Sstevel@tonic-gate
18537c478bd9Sstevel@tonic-gate for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) {
18547c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((10, "\ttrigger: %s\n", tp->fn_name));
1855f798ee53SJan Kryl /* MF_LOOKUP should never be set on trigger nodes */
1856f798ee53SJan Kryl ASSERT((tp->fn_flags & MF_LOOKUP) == 0);
18577c478bd9Sstevel@tonic-gate vfsp = fntovn(tp)->v_vfsp;
1858f798ee53SJan Kryl
18597c478bd9Sstevel@tonic-gate /*
18607c478bd9Sstevel@tonic-gate * The vn_vfsunlock will be done in auto_inkernel_unmount.
18617c478bd9Sstevel@tonic-gate */
18627c478bd9Sstevel@tonic-gate lck_error = vn_vfswlock(vfsp->vfs_vnodecovered);
1863f798ee53SJan Kryl
1864f798ee53SJan Kryl if (lck_error != 0 || (tp->fn_flags & MF_INPROG) ||
1865f798ee53SJan Kryl DEEPER(tp) || ((fntovn(tp))->v_count) > 2) {
18667c478bd9Sstevel@tonic-gate /*
18677c478bd9Sstevel@tonic-gate * couldn't lock it because it's busy,
18687c478bd9Sstevel@tonic-gate * It is mounted on or has dirents?
18697c478bd9Sstevel@tonic-gate * If reference count is greater than two, then
18707c478bd9Sstevel@tonic-gate * somebody else is holding a reference to this vnode.
18717c478bd9Sstevel@tonic-gate * One reference is for the mountpoint, and the second
18727c478bd9Sstevel@tonic-gate * is for the trigger node.
18737c478bd9Sstevel@tonic-gate */
18747c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((10, "\ttrigger busy\n"));
1875f798ee53SJan Kryl
18767c478bd9Sstevel@tonic-gate /*
18777c478bd9Sstevel@tonic-gate * Unlock previously locked mountpoints
18787c478bd9Sstevel@tonic-gate */
18797c478bd9Sstevel@tonic-gate for (done = 0, t1p = fnp->fn_trigger; !done;
18807c478bd9Sstevel@tonic-gate t1p = t1p->fn_next) {
18817c478bd9Sstevel@tonic-gate /*
18827c478bd9Sstevel@tonic-gate * Unlock all nodes previously
18837c478bd9Sstevel@tonic-gate * locked. All nodes up to 'tp'
18847c478bd9Sstevel@tonic-gate * were successfully locked. If 'lck_err' is
18857c478bd9Sstevel@tonic-gate * set, then 'tp' was not locked, and thus
18867c478bd9Sstevel@tonic-gate * should not be unlocked. If
18877c478bd9Sstevel@tonic-gate * 'lck_err' is not set, then 'tp' was
18887c478bd9Sstevel@tonic-gate * successfully locked, and it should
18897c478bd9Sstevel@tonic-gate * be unlocked.
18907c478bd9Sstevel@tonic-gate */
18917c478bd9Sstevel@tonic-gate if (t1p != tp || !lck_error) {
18927c478bd9Sstevel@tonic-gate vfsp = fntovn(t1p)->v_vfsp;
18937c478bd9Sstevel@tonic-gate vn_vfsunlock(vfsp->vfs_vnodecovered);
18947c478bd9Sstevel@tonic-gate }
18957c478bd9Sstevel@tonic-gate done = (t1p == tp);
18967c478bd9Sstevel@tonic-gate }
1897f798ee53SJan Kryl return (B_TRUE);
18987c478bd9Sstevel@tonic-gate }
18997c478bd9Sstevel@tonic-gate }
19007c478bd9Sstevel@tonic-gate
1901f798ee53SJan Kryl return (B_FALSE);
19027c478bd9Sstevel@tonic-gate }
19037c478bd9Sstevel@tonic-gate
19047c478bd9Sstevel@tonic-gate /*
19057c478bd9Sstevel@tonic-gate * It is the caller's responsibility to grab the VVFSLOCK.
19067c478bd9Sstevel@tonic-gate * Releases the VVFSLOCK upon return.
19077c478bd9Sstevel@tonic-gate */
19087c478bd9Sstevel@tonic-gate static int
unmount_node(vnode_t * cvp,int force)19097c478bd9Sstevel@tonic-gate unmount_node(vnode_t *cvp, int force)
19107c478bd9Sstevel@tonic-gate {
19117c478bd9Sstevel@tonic-gate int error = 0;
19127c478bd9Sstevel@tonic-gate fnnode_t *cfnp;
19137c478bd9Sstevel@tonic-gate vfs_t *vfsp;
19147c478bd9Sstevel@tonic-gate umntrequest ul;
19157c478bd9Sstevel@tonic-gate fninfo_t *fnip;
19167c478bd9Sstevel@tonic-gate
19177c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tunmount_node cvp=%p\n", (void *)cvp));
19187c478bd9Sstevel@tonic-gate
19197c478bd9Sstevel@tonic-gate ASSERT(vn_vfswlock_held(cvp));
19207c478bd9Sstevel@tonic-gate cfnp = vntofn(cvp);
19217c478bd9Sstevel@tonic-gate vfsp = vn_mountedvfs(cvp);
19227c478bd9Sstevel@tonic-gate
19237c478bd9Sstevel@tonic-gate if (force || cfnp->fn_flags & MF_IK_MOUNT) {
19247c478bd9Sstevel@tonic-gate /*
19257c478bd9Sstevel@tonic-gate * Mount was performed in the kernel, so
19267c478bd9Sstevel@tonic-gate * do an in-kernel unmount. auto_inkernel_unmount()
19277c478bd9Sstevel@tonic-gate * will vn_vfsunlock(cvp).
19287c478bd9Sstevel@tonic-gate */
19297c478bd9Sstevel@tonic-gate error = auto_inkernel_unmount(vfsp);
19307c478bd9Sstevel@tonic-gate } else {
19317c478bd9Sstevel@tonic-gate zone_t *zone = NULL;
19327c478bd9Sstevel@tonic-gate refstr_t *mntpt, *resource;
19337c478bd9Sstevel@tonic-gate size_t mntoptslen;
19347c478bd9Sstevel@tonic-gate
19357c478bd9Sstevel@tonic-gate /*
19367c478bd9Sstevel@tonic-gate * Get the mnttab information of the node
19377c478bd9Sstevel@tonic-gate * and ask the daemon to unmount it.
19387c478bd9Sstevel@tonic-gate */
19397c478bd9Sstevel@tonic-gate bzero(&ul, sizeof (ul));
19407c478bd9Sstevel@tonic-gate mntfs_getmntopts(vfsp, &ul.mntopts, &mntoptslen);
19417c478bd9Sstevel@tonic-gate if (ul.mntopts == NULL) {
194239d3e169Sevanl auto_log(cfnp->fn_globals->fng_verbose,
1943d6c8399bSrmesta cfnp->fn_globals->fng_zoneid, CE_WARN,
1944d6c8399bSrmesta "unmount_node: no memory");
19457c478bd9Sstevel@tonic-gate vn_vfsunlock(cvp);
19467c478bd9Sstevel@tonic-gate error = ENOMEM;
19477c478bd9Sstevel@tonic-gate goto done;
19487c478bd9Sstevel@tonic-gate }
19497c478bd9Sstevel@tonic-gate if (mntoptslen > AUTOFS_MAXOPTSLEN)
19507c478bd9Sstevel@tonic-gate ul.mntopts[AUTOFS_MAXOPTSLEN - 1] = '\0';
19517c478bd9Sstevel@tonic-gate
19527c478bd9Sstevel@tonic-gate mntpt = vfs_getmntpoint(vfsp);
19537c478bd9Sstevel@tonic-gate ul.mntpnt = (char *)refstr_value(mntpt);
19547c478bd9Sstevel@tonic-gate resource = vfs_getresource(vfsp);
19557c478bd9Sstevel@tonic-gate ul.mntresource = (char *)refstr_value(resource);
19567c478bd9Sstevel@tonic-gate
19577c478bd9Sstevel@tonic-gate fnip = vfstofni(cvp->v_vfsp);
19587c478bd9Sstevel@tonic-gate ul.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE;
19597c478bd9Sstevel@tonic-gate
19607c478bd9Sstevel@tonic-gate /*
19617c478bd9Sstevel@tonic-gate * Since a zone'd automountd's view of the autofs mount points
19627c478bd9Sstevel@tonic-gate * differs from those in the kernel, we need to make sure we
19637c478bd9Sstevel@tonic-gate * give it consistent mount points.
19647c478bd9Sstevel@tonic-gate */
19657c478bd9Sstevel@tonic-gate ASSERT(fnip->fi_zoneid == getzoneid());
19667c478bd9Sstevel@tonic-gate zone = curproc->p_zone;
19677c478bd9Sstevel@tonic-gate
19687c478bd9Sstevel@tonic-gate if (fnip->fi_zoneid != GLOBAL_ZONEID) {
19697c478bd9Sstevel@tonic-gate if (ZONE_PATH_VISIBLE(ul.mntpnt, zone)) {
19707c478bd9Sstevel@tonic-gate ul.mntpnt =
19717c478bd9Sstevel@tonic-gate ZONE_PATH_TRANSLATE(ul.mntpnt, zone);
19727c478bd9Sstevel@tonic-gate }
19737c478bd9Sstevel@tonic-gate if (ZONE_PATH_VISIBLE(ul.mntresource, zone)) {
19747c478bd9Sstevel@tonic-gate ul.mntresource =
19757c478bd9Sstevel@tonic-gate ZONE_PATH_TRANSLATE(ul.mntresource, zone);
19767c478bd9Sstevel@tonic-gate }
19777c478bd9Sstevel@tonic-gate }
197839d3e169Sevanl
19797c478bd9Sstevel@tonic-gate ul.fstype = vfssw[vfsp->vfs_fstype].vsw_name;
19807c478bd9Sstevel@tonic-gate vn_vfsunlock(cvp);
19817c478bd9Sstevel@tonic-gate
198239d3e169Sevanl error = auto_send_unmount_request(fnip, &ul, FALSE);
19837c478bd9Sstevel@tonic-gate kmem_free(ul.mntopts, mntoptslen);
19847c478bd9Sstevel@tonic-gate refstr_rele(mntpt);
19857c478bd9Sstevel@tonic-gate refstr_rele(resource);
19867c478bd9Sstevel@tonic-gate }
19877c478bd9Sstevel@tonic-gate
19887c478bd9Sstevel@tonic-gate done:
19897c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tunmount_node cvp=%p error=%d\n", (void *)cvp,
19907c478bd9Sstevel@tonic-gate error));
19917c478bd9Sstevel@tonic-gate return (error);
19927c478bd9Sstevel@tonic-gate }
19937c478bd9Sstevel@tonic-gate
19947c478bd9Sstevel@tonic-gate /*
19957c478bd9Sstevel@tonic-gate * return EBUSY if any thread is holding a reference to this vnode
1996f798ee53SJan Kryl * other than us. Result of this function cannot be relied on, since
1997f798ee53SJan Kryl * it doesn't follow proper locking rules (i.e. vp->v_vfsmountedhere
1998f798ee53SJan Kryl * and fnp->fn_trigger can change throughout this function). However
1999f798ee53SJan Kryl * it's good enough for rough estimation.
20007c478bd9Sstevel@tonic-gate */
20017c478bd9Sstevel@tonic-gate static int
check_auto_node(vnode_t * vp)20027c478bd9Sstevel@tonic-gate check_auto_node(vnode_t *vp)
20037c478bd9Sstevel@tonic-gate {
20047c478bd9Sstevel@tonic-gate fnnode_t *fnp;
20057c478bd9Sstevel@tonic-gate int error = 0;
20067c478bd9Sstevel@tonic-gate /*
20077c478bd9Sstevel@tonic-gate * number of references to expect for
20087c478bd9Sstevel@tonic-gate * a non-busy vnode.
20097c478bd9Sstevel@tonic-gate */
20107c478bd9Sstevel@tonic-gate uint_t count;
20117c478bd9Sstevel@tonic-gate
20127c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tcheck_auto_node vp=%p ", (void *)vp));
20137c478bd9Sstevel@tonic-gate fnp = vntofn(vp);
20147c478bd9Sstevel@tonic-gate
20157c478bd9Sstevel@tonic-gate count = 1; /* we are holding a reference to vp */
20167c478bd9Sstevel@tonic-gate if (fnp->fn_flags & MF_TRIGGER) {
20177c478bd9Sstevel@tonic-gate /*
20187c478bd9Sstevel@tonic-gate * parent holds a pointer to us (trigger)
20197c478bd9Sstevel@tonic-gate */
20207c478bd9Sstevel@tonic-gate count++;
20217c478bd9Sstevel@tonic-gate }
20227c478bd9Sstevel@tonic-gate if (fnp->fn_trigger != NULL) {
20237c478bd9Sstevel@tonic-gate /*
20247c478bd9Sstevel@tonic-gate * The trigger nodes have a hold on us.
20257c478bd9Sstevel@tonic-gate */
20267c478bd9Sstevel@tonic-gate count++;
20277c478bd9Sstevel@tonic-gate }
2028f798ee53SJan Kryl if (vn_ismntpt(vp)) {
2029f798ee53SJan Kryl /*
2030f798ee53SJan Kryl * File system is mounted on us.
2031f798ee53SJan Kryl */
2032f798ee53SJan Kryl count++;
2033f798ee53SJan Kryl }
20347c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock);
2035f798ee53SJan Kryl ASSERT(vp->v_count > 0);
20367c478bd9Sstevel@tonic-gate if (vp->v_flag & VROOT)
20377c478bd9Sstevel@tonic-gate count++;
20387c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tcount=%u ", vp->v_count));
20397c478bd9Sstevel@tonic-gate if (vp->v_count > count)
20407c478bd9Sstevel@tonic-gate error = EBUSY;
20417c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock);
20427c478bd9Sstevel@tonic-gate
20437c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tcheck_auto_node error=%d ", error));
20447c478bd9Sstevel@tonic-gate return (error);
20457c478bd9Sstevel@tonic-gate }
20467c478bd9Sstevel@tonic-gate
20477c478bd9Sstevel@tonic-gate /*
20487c478bd9Sstevel@tonic-gate * rootvp is the root of the AUTOFS filesystem.
20497c478bd9Sstevel@tonic-gate * If rootvp is busy (v_count > 1) returns EBUSY.
20507c478bd9Sstevel@tonic-gate * else removes every vnode under this tree.
20517c478bd9Sstevel@tonic-gate * ASSUMPTION: Assumes that the only node which can be busy is
20527c478bd9Sstevel@tonic-gate * the root vnode. This filesystem better be two levels deep only,
20537c478bd9Sstevel@tonic-gate * the root and its immediate subdirs.
20547c478bd9Sstevel@tonic-gate * The daemon will "AUTOFS direct-mount" only one level below the root.
20557c478bd9Sstevel@tonic-gate */
2056f798ee53SJan Kryl static void
unmount_autofs(vnode_t * rootvp)20577c478bd9Sstevel@tonic-gate unmount_autofs(vnode_t *rootvp)
20587c478bd9Sstevel@tonic-gate {
20597c478bd9Sstevel@tonic-gate fnnode_t *fnp, *rootfnp, *nfnp;
20607c478bd9Sstevel@tonic-gate
20617c478bd9Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tunmount_autofs rootvp=%p ", (void *)rootvp));
20627c478bd9Sstevel@tonic-gate
2063f798ee53SJan Kryl /*
2064f798ee53SJan Kryl * Remove all its immediate subdirectories.
2065f798ee53SJan Kryl */
2066f798ee53SJan Kryl rootfnp = vntofn(rootvp);
2067f798ee53SJan Kryl rw_enter(&rootfnp->fn_rwlock, RW_WRITER);
2068f798ee53SJan Kryl for (fnp = rootfnp->fn_dirents; fnp != NULL; fnp = nfnp) {
2069f798ee53SJan Kryl ASSERT(fntovn(fnp)->v_count == 0);
2070f798ee53SJan Kryl ASSERT(fnp->fn_dirents == NULL);
2071f798ee53SJan Kryl ASSERT(fnp->fn_linkcnt == 2);
2072f798ee53SJan Kryl fnp->fn_linkcnt--;
2073f798ee53SJan Kryl auto_disconnect(rootfnp, fnp);
2074f798ee53SJan Kryl nfnp = fnp->fn_next;
2075f798ee53SJan Kryl auto_freefnnode(fnp);
20767c478bd9Sstevel@tonic-gate }
2077f798ee53SJan Kryl rw_exit(&rootfnp->fn_rwlock);
20787c478bd9Sstevel@tonic-gate }
20797c478bd9Sstevel@tonic-gate
20807c478bd9Sstevel@tonic-gate /*
2081f798ee53SJan Kryl * If a node matches all unmount criteria, do:
2082f798ee53SJan Kryl * destroy subordinate trigger node(s) if there is any
2083f798ee53SJan Kryl * unmount filesystem mounted on top of the node if there is any
2084f798ee53SJan Kryl *
2085f798ee53SJan Kryl * Function should be called with locked fnp's mutex. The mutex is
2086f798ee53SJan Kryl * unlocked before return from function.
20877c478bd9Sstevel@tonic-gate */
2088f798ee53SJan Kryl static int
try_unmount_node(fnnode_t * fnp,boolean_t force)2089f798ee53SJan Kryl try_unmount_node(fnnode_t *fnp, boolean_t force)
20907c478bd9Sstevel@tonic-gate {
2091f798ee53SJan Kryl boolean_t trigger_unmount = B_FALSE;
2092f798ee53SJan Kryl action_list *alp = NULL;
2093f798ee53SJan Kryl vnode_t *vp;
2094f798ee53SJan Kryl int error = 0;
2095f798ee53SJan Kryl fninfo_t *fnip;
2096f798ee53SJan Kryl vfs_t *vfsp;
2097f798ee53SJan Kryl struct autofs_globals *fngp;
20987c478bd9Sstevel@tonic-gate
2099f798ee53SJan Kryl AUTOFS_DPRINT((10, "\ttry_unmount_node: processing node %p\n",
2100f798ee53SJan Kryl (void *)fnp));
21017c478bd9Sstevel@tonic-gate
2102f798ee53SJan Kryl ASSERT(MUTEX_HELD(&fnp->fn_lock));
21037c478bd9Sstevel@tonic-gate
2104f798ee53SJan Kryl fngp = fnp->fn_globals;
21057c478bd9Sstevel@tonic-gate vp = fntovn(fnp);
21067c478bd9Sstevel@tonic-gate fnip = vfstofni(vp->v_vfsp);
2107f798ee53SJan Kryl
21087c478bd9Sstevel@tonic-gate /*
2109f798ee53SJan Kryl * If either a mount, lookup or another unmount of this subtree is in
2110f798ee53SJan Kryl * progress, don't attempt to unmount at this time.
21117c478bd9Sstevel@tonic-gate */
21127c478bd9Sstevel@tonic-gate if (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) {
21137c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
2114f798ee53SJan Kryl return (EBUSY);
21157c478bd9Sstevel@tonic-gate }
2116f798ee53SJan Kryl
2117f798ee53SJan Kryl /*
2118f798ee53SJan Kryl * Bail out if someone else is holding reference to this vnode.
2119f798ee53SJan Kryl * This check isn't just an optimization (someone is probably
2120f798ee53SJan Kryl * just about to trigger mount). It is necessary to prevent a deadlock
2121f798ee53SJan Kryl * in domount() called from auto_perform_actions() if unmount of
2122f798ee53SJan Kryl * trigger parent fails. domount() calls lookupname() to resolve
2123f798ee53SJan Kryl * special in mount arguments. Special is set to a map name in case
2124f798ee53SJan Kryl * of autofs triggers (i.e. auto_ws.sun.com). Thus if current
2125f798ee53SJan Kryl * working directory is set to currently processed node, lookupname()
2126f798ee53SJan Kryl * calls into autofs vnops in order to resolve special, which deadlocks
2127f798ee53SJan Kryl * the process.
2128f798ee53SJan Kryl *
2129f798ee53SJan Kryl * Note: This should be fixed. Autofs shouldn't pass the map name
2130f798ee53SJan Kryl * in special and avoid useless lookup with potentially disasterous
2131f798ee53SJan Kryl * consequence.
2132f798ee53SJan Kryl */
2133f798ee53SJan Kryl if (check_auto_node(vp) == EBUSY) {
21347c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
2135f798ee53SJan Kryl return (EBUSY);
21367c478bd9Sstevel@tonic-gate }
21377c478bd9Sstevel@tonic-gate
21387c478bd9Sstevel@tonic-gate /*
2139f798ee53SJan Kryl * If not forced operation, back out if node has been referenced
2140f798ee53SJan Kryl * recently.
21417c478bd9Sstevel@tonic-gate */
2142f798ee53SJan Kryl if (!force &&
2143f798ee53SJan Kryl fnp->fn_ref_time + fnip->fi_mount_to > gethrestime_sec()) {
21447c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
2145f798ee53SJan Kryl return (EBUSY);
21467c478bd9Sstevel@tonic-gate }
21477c478bd9Sstevel@tonic-gate
2148f798ee53SJan Kryl /* block mounts/unmounts on the node */
21497c478bd9Sstevel@tonic-gate AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
21507c478bd9Sstevel@tonic-gate fnp->fn_error = 0;
21517c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
21527c478bd9Sstevel@tonic-gate
2153f798ee53SJan Kryl /* unmount next level triggers if there are any */
21547c478bd9Sstevel@tonic-gate rw_enter(&fnp->fn_rwlock, RW_WRITER);
21557c478bd9Sstevel@tonic-gate if (fnp->fn_trigger != NULL) {
2156f798ee53SJan Kryl trigger_unmount = B_TRUE;
21577c478bd9Sstevel@tonic-gate
2158f798ee53SJan Kryl if (triggers_busy(fnp)) {
21597c478bd9Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock);
21607c478bd9Sstevel@tonic-gate mutex_enter(&fnp->fn_lock);
21617c478bd9Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
21627c478bd9Sstevel@tonic-gate mutex_exit(&fnp->fn_lock);
2163f798ee53SJan Kryl return (EBUSY);
21647c478bd9Sstevel@tonic-gate }
21657c478bd9Sstevel@tonic-gate
21667c478bd9Sstevel@tonic-gate /*
21677c478bd9Sstevel@tonic-gate * At this point, we know all trigger nodes are locked,
21687c478bd9Sstevel@tonic-gate * and they're not busy or mounted on.
2169f798ee53SJan Kryl *
2170f798ee53SJan Kryl * Attempt to unmount all trigger nodes, save the
2171f798ee53SJan Kryl * action_list in case we need to remount them later.
2172f798ee53SJan Kryl * The action_list will be freed later if there was no
2173f798ee53SJan Kryl * need to remount the trigger nodes.
21747c478bd9Sstevel@tonic-gate */
2175f798ee53SJan Kryl unmount_triggers(fnp, &alp);
21767c478bd9Sstevel@tonic-gate }
21777c478bd9Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock);
21787c478bd9Sstevel@tonic-gate
21797c478bd9Sstevel@tonic-gate (void) vn_vfswlock_wait(vp);
21807c478bd9Sstevel@tonic-gate
21817c478bd9Sstevel@tonic-gate vfsp = vn_mountedvfs(vp);
21827c478bd9Sstevel@tonic-gate if (vfsp != NULL) {
2183f798ee53SJan Kryl /* vn_vfsunlock(vp) is done inside unmount_node() */
21847c478bd9Sstevel@tonic-gate error = unmount_node(vp, force);
21857c478bd9Sstevel@tonic-gate if (error == ECONNRESET) {
21867c478bd9Sstevel@tonic-gate if (vn_mountedvfs(vp) == NULL) {
21877c478bd9Sstevel@tonic-gate /*
21887c478bd9Sstevel@tonic-gate * The filesystem was unmounted before the
21897c478bd9Sstevel@tonic-gate * daemon died. Unfortunately we can not
21907c478bd9Sstevel@tonic-gate * determine whether all the cleanup work was
21917c478bd9Sstevel@tonic-gate * successfully finished (i.e. update mnttab,
21927c478bd9Sstevel@tonic-gate * or notify NFS server of the unmount).
21937c478bd9Sstevel@tonic-gate * We should not retry the operation since the
21947c478bd9Sstevel@tonic-gate * filesystem has already been unmounted, and
21957c478bd9Sstevel@tonic-gate * may have already been removed from mnttab,
21967c478bd9Sstevel@tonic-gate * in such case the devid/rdevid we send to
21977c478bd9Sstevel@tonic-gate * the daemon will not be matched. So we have
219839d3e169Sevanl * to be content with the partial unmount.
21997c478bd9Sstevel@tonic-gate * Since the mountpoint is no longer covered, we
22007c478bd9Sstevel@tonic-gate * clear the error condition.
22017c478bd9Sstevel@tonic-gate */
22027c478bd9Sstevel@tonic-gate error = 0;
220339d3e169Sevanl auto_log(fngp->fng_verbose, fngp->fng_zoneid,
2204f798ee53SJan Kryl CE_WARN, "autofs: automountd "
2205f798ee53SJan Kryl "connection dropped when unmounting %s/%s",
2206f798ee53SJan Kryl fnip->fi_path, (fnip->fi_flags & MF_DIRECT)
2207f798ee53SJan Kryl ? "" : fnp->fn_name);
22087c478bd9Sstevel@tonic-gate }
22097c478bd9Sstevel@tonic-gate }
22107c478bd9Sstevel@tonic-gate } else {
22117c478bd9Sstevel@tonic-gate vn_vfsunlock(vp);
2212f798ee53SJan Kryl /* Destroy all dirents of fnp if we unmounted its triggers */
2213f798ee53SJan Kryl if (trigger_unmount)
2214f798ee53SJan Kryl unmount_autofs(vp);
22157c478bd9Sstevel@tonic-gate }
22167c478bd9Sstevel@tonic-gate
2217f798ee53SJan Kryl /* If unmount failed, we got to remount triggers */
2218f798ee53SJan Kryl if (error != 0) {
2219f798ee53SJan Kryl if (trigger_unmount) {
2220f798ee53SJan Kryl int ret;
2221f798ee53SJan Kryl
2222f798ee53SJan Kryl ASSERT((fnp->fn_flags & MF_THISUID_MATCH_RQD) == 0);
2223f798ee53SJan Kryl
22247c478bd9Sstevel@tonic-gate /*
2225f798ee53SJan Kryl * The action list was free'd by auto_perform_actions
22267c478bd9Sstevel@tonic-gate */
2227f798ee53SJan Kryl ret = auto_perform_actions(fnip, fnp, alp, CRED());
2228f798ee53SJan Kryl if (ret != 0) {
2229d6c8399bSrmesta auto_log(fngp->fng_verbose, fngp->fng_zoneid,
2230d6c8399bSrmesta CE_WARN, "autofs: can't remount triggers "
2231f798ee53SJan Kryl "fnp=%p error=%d", (void *)fnp, ret);
22327c478bd9Sstevel@tonic-gate }
22337c478bd9Sstevel@tonic-gate }
2234f798ee53SJan Kryl mutex_enter(&fnp->fn_lock);
2235f798ee53SJan Kryl AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2236f798ee53SJan Kryl mutex_exit(&fnp->fn_lock);
22377c478bd9Sstevel@tonic-gate } else {
2238f798ee53SJan Kryl /* Free the action list here */
2239f798ee53SJan Kryl if (trigger_unmount)
2240f798ee53SJan Kryl xdr_free(xdr_action_list, (char *)alp);
22417c478bd9Sstevel@tonic-gate
22427c478bd9Sstevel@tonic-gate /*
2243f798ee53SJan Kryl * Other threads may be waiting for this unmount to
2244f798ee53SJan Kryl * finish. We must let it know that in order to
2245f798ee53SJan Kryl * proceed, it must trigger the mount itself.
22467c478bd9Sstevel@tonic-gate */
2247f798ee53SJan Kryl mutex_enter(&fnp->fn_lock);
2248f798ee53SJan Kryl fnp->fn_flags &= ~MF_IK_MOUNT;
2249f798ee53SJan Kryl if (fnp->fn_flags & MF_WAITING)
2250f798ee53SJan Kryl fnp->fn_error = EAGAIN;
2251f798ee53SJan Kryl AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG);
2252f798ee53SJan Kryl mutex_exit(&fnp->fn_lock);
22537c478bd9Sstevel@tonic-gate }
22547c478bd9Sstevel@tonic-gate
2255f798ee53SJan Kryl return (error);
2256f798ee53SJan Kryl }
2257f798ee53SJan Kryl
2258f798ee53SJan Kryl /*
2259f798ee53SJan Kryl * This is an implementation of depth-first search in a tree rooted by
2260f798ee53SJan Kryl * start_fnp and composed from fnnodes. Links between tree levels are
2261f798ee53SJan Kryl * fn_dirents, fn_trigger in fnnode_t and v_mountedvfs in vnode_t (if
2262f798ee53SJan Kryl * mounted vfs is autofs). The algorithm keeps track of visited nodes
2263f798ee53SJan Kryl * by means of a timestamp (fn_unmount_ref_time).
2264f798ee53SJan Kryl *
2265f798ee53SJan Kryl * Upon top-down traversal of the tree we apply following locking scheme:
2266f798ee53SJan Kryl * lock fn_rwlock of current node
2267f798ee53SJan Kryl * grab reference to child's vnode (VN_HOLD)
2268f798ee53SJan Kryl * unlock fn_rwlock
2269f798ee53SJan Kryl * free reference to current vnode (VN_RELE)
2270f798ee53SJan Kryl * Similar locking scheme is used for down-top and left-right traversal.
2271f798ee53SJan Kryl *
2272f798ee53SJan Kryl * Algorithm examines the most down-left node in tree, which hasn't been
2273f798ee53SJan Kryl * visited yet. From this follows that nodes are processed in bottom-up
2274f798ee53SJan Kryl * fashion.
2275f798ee53SJan Kryl *
2276f798ee53SJan Kryl * Function returns either zero if unmount of root node was successful
2277f798ee53SJan Kryl * or error code (mostly EBUSY).
2278f798ee53SJan Kryl */
2279f798ee53SJan Kryl int
unmount_subtree(fnnode_t * rootfnp,boolean_t force)2280f798ee53SJan Kryl unmount_subtree(fnnode_t *rootfnp, boolean_t force)
2281f798ee53SJan Kryl {
2282f798ee53SJan Kryl fnnode_t *currfnp; /* currently examined node in the tree */
2283f798ee53SJan Kryl fnnode_t *lastfnp; /* previously processed node */
2284f798ee53SJan Kryl fnnode_t *nextfnp; /* next examined node in the tree */
2285f798ee53SJan Kryl vnode_t *curvp;
2286f798ee53SJan Kryl vnode_t *newvp;
2287f798ee53SJan Kryl vfs_t *vfsp;
2288f798ee53SJan Kryl time_t timestamp;
2289f798ee53SJan Kryl
2290f798ee53SJan Kryl ASSERT(fntovn(rootfnp)->v_type != VLNK);
2291f798ee53SJan Kryl AUTOFS_DPRINT((10, "unmount_subtree: root=%p (%s)\n", (void *)rootfnp,
2292f798ee53SJan Kryl rootfnp->fn_name));
22937c478bd9Sstevel@tonic-gate
22947c478bd9Sstevel@tonic-gate /*
2295f798ee53SJan Kryl * Timestamp, which visited nodes are marked with, to distinguish them
2296f798ee53SJan Kryl * from unvisited nodes.
22977c478bd9Sstevel@tonic-gate */
2298f798ee53SJan Kryl timestamp = gethrestime_sec();
2299f798ee53SJan Kryl currfnp = lastfnp = rootfnp;
2300f798ee53SJan Kryl
2301f798ee53SJan Kryl /* Loop until we examine all nodes in the tree */
2302f798ee53SJan Kryl mutex_enter(&currfnp->fn_lock);
2303f798ee53SJan Kryl while (currfnp != rootfnp || rootfnp->fn_unmount_ref_time < timestamp) {
2304f798ee53SJan Kryl curvp = fntovn(currfnp);
2305f798ee53SJan Kryl AUTOFS_DPRINT((10, "\tunmount_subtree: entering node %p (%s)\n",
2306f798ee53SJan Kryl (void *)currfnp, currfnp->fn_name));
2307f798ee53SJan Kryl
2308f798ee53SJan Kryl /*
2309f798ee53SJan Kryl * New candidate for processing must have been already visited,
2310f798ee53SJan Kryl * by us because we want to process tree nodes in bottom-up
2311f798ee53SJan Kryl * order.
2312f798ee53SJan Kryl */
2313f798ee53SJan Kryl if (currfnp->fn_unmount_ref_time == timestamp &&
2314f798ee53SJan Kryl currfnp != lastfnp) {
2315f798ee53SJan Kryl (void) try_unmount_node(currfnp, force);
2316f798ee53SJan Kryl lastfnp = currfnp;
2317f798ee53SJan Kryl mutex_enter(&currfnp->fn_lock);
23187c478bd9Sstevel@tonic-gate /*
2319f798ee53SJan Kryl * Fall through to next if-branch to pick
2320f798ee53SJan Kryl * sibling or parent of this node.
23217c478bd9Sstevel@tonic-gate */
23227c478bd9Sstevel@tonic-gate }
23237c478bd9Sstevel@tonic-gate
23247c478bd9Sstevel@tonic-gate /*
2325f798ee53SJan Kryl * If this node has been already visited, it means that it's
2326f798ee53SJan Kryl * dead end and we need to pick sibling or parent as next node.
23277c478bd9Sstevel@tonic-gate */
2328f798ee53SJan Kryl if (currfnp->fn_unmount_ref_time >= timestamp ||
2329f798ee53SJan Kryl curvp->v_type == VLNK) {
2330f798ee53SJan Kryl mutex_exit(&currfnp->fn_lock);
2331f798ee53SJan Kryl /*
2332f798ee53SJan Kryl * Obtain parent's readers lock before grabbing
2333f798ee53SJan Kryl * reference to sibling.
2334f798ee53SJan Kryl */
2335f798ee53SJan Kryl rw_enter(&currfnp->fn_parent->fn_rwlock, RW_READER);
2336f798ee53SJan Kryl if ((nextfnp = currfnp->fn_next) != NULL) {
2337f798ee53SJan Kryl VN_HOLD(fntovn(nextfnp));
2338f798ee53SJan Kryl rw_exit(&currfnp->fn_parent->fn_rwlock);
2339f798ee53SJan Kryl VN_RELE(curvp);
2340f798ee53SJan Kryl currfnp = nextfnp;
2341f798ee53SJan Kryl mutex_enter(&currfnp->fn_lock);
2342f798ee53SJan Kryl continue;
2343f798ee53SJan Kryl }
2344f798ee53SJan Kryl rw_exit(&currfnp->fn_parent->fn_rwlock);
2345f798ee53SJan Kryl
2346f798ee53SJan Kryl /*
2347f798ee53SJan Kryl * All descendants and siblings were visited. Perform
2348f798ee53SJan Kryl * bottom-up move.
2349f798ee53SJan Kryl */
2350f798ee53SJan Kryl nextfnp = currfnp->fn_parent;
2351f798ee53SJan Kryl VN_HOLD(fntovn(nextfnp));
2352f798ee53SJan Kryl VN_RELE(curvp);
2353f798ee53SJan Kryl currfnp = nextfnp;
2354f798ee53SJan Kryl mutex_enter(&currfnp->fn_lock);
2355f798ee53SJan Kryl continue;
2356f798ee53SJan Kryl }
23577c478bd9Sstevel@tonic-gate
23587c478bd9Sstevel@tonic-gate /*
2359f798ee53SJan Kryl * Mark node as visited. Note that the timestamp could have
2360f798ee53SJan Kryl * been updated by somebody else in the meantime.
23617c478bd9Sstevel@tonic-gate */
2362f798ee53SJan Kryl if (currfnp->fn_unmount_ref_time < timestamp)
2363f798ee53SJan Kryl currfnp->fn_unmount_ref_time = timestamp;
23646bf33d39SJan Kryl
23656bf33d39SJan Kryl /*
23666bf33d39SJan Kryl * Don't descent below nodes, which are being unmounted/mounted.
23676bf33d39SJan Kryl *
23686bf33d39SJan Kryl * We need to hold both locks at once: fn_lock because we need
23696bf33d39SJan Kryl * to read MF_INPROG and fn_rwlock to prevent anybody from
23706bf33d39SJan Kryl * modifying fn_trigger until its used to traverse triggers
23716bf33d39SJan Kryl * below.
23726bf33d39SJan Kryl *
23736bf33d39SJan Kryl * Acquire fn_rwlock in non-blocking mode to avoid deadlock.
23746bf33d39SJan Kryl * If it can't be acquired, then acquire locks in correct
23756bf33d39SJan Kryl * order.
23766bf33d39SJan Kryl */
23776bf33d39SJan Kryl if (!rw_tryenter(&currfnp->fn_rwlock, RW_READER)) {
23786bf33d39SJan Kryl mutex_exit(&currfnp->fn_lock);
23796bf33d39SJan Kryl rw_enter(&currfnp->fn_rwlock, RW_READER);
23806bf33d39SJan Kryl mutex_enter(&currfnp->fn_lock);
23816bf33d39SJan Kryl }
23826bf33d39SJan Kryl if (currfnp->fn_flags & MF_INPROG) {
23836bf33d39SJan Kryl rw_exit(&currfnp->fn_rwlock);
23846bf33d39SJan Kryl continue;
23856bf33d39SJan Kryl }
2386f798ee53SJan Kryl mutex_exit(&currfnp->fn_lock);
2387f798ee53SJan Kryl
2388f798ee53SJan Kryl /*
2389f798ee53SJan Kryl * Examine descendants in this order: triggers, dirents, autofs
2390f798ee53SJan Kryl * mounts.
2391f798ee53SJan Kryl */
2392f798ee53SJan Kryl
2393f798ee53SJan Kryl if ((nextfnp = currfnp->fn_trigger) != NULL) {
2394f798ee53SJan Kryl VN_HOLD(fntovn(nextfnp));
2395f798ee53SJan Kryl rw_exit(&currfnp->fn_rwlock);
2396f798ee53SJan Kryl VN_RELE(curvp);
2397f798ee53SJan Kryl currfnp = nextfnp;
2398f798ee53SJan Kryl mutex_enter(&currfnp->fn_lock);
2399f798ee53SJan Kryl continue;
2400f798ee53SJan Kryl }
2401f798ee53SJan Kryl
2402f798ee53SJan Kryl if ((nextfnp = currfnp->fn_dirents) != NULL) {
2403f798ee53SJan Kryl VN_HOLD(fntovn(nextfnp));
2404f798ee53SJan Kryl rw_exit(&currfnp->fn_rwlock);
2405f798ee53SJan Kryl VN_RELE(curvp);
2406f798ee53SJan Kryl currfnp = nextfnp;
2407f798ee53SJan Kryl mutex_enter(&currfnp->fn_lock);
2408f798ee53SJan Kryl continue;
2409f798ee53SJan Kryl }
2410f798ee53SJan Kryl rw_exit(&currfnp->fn_rwlock);
24117c478bd9Sstevel@tonic-gate
2412f798ee53SJan Kryl (void) vn_vfswlock_wait(curvp);
2413f798ee53SJan Kryl vfsp = vn_mountedvfs(curvp);
2414f798ee53SJan Kryl if (vfsp != NULL &&
2415f798ee53SJan Kryl vfs_matchops(vfsp, vfs_getops(curvp->v_vfsp))) {
2416f798ee53SJan Kryl /*
2417f798ee53SJan Kryl * Deal with /xfn/host/jurassic alikes here...
2418f798ee53SJan Kryl *
2419f798ee53SJan Kryl * We know this call to VFS_ROOT is safe to call while
2420f798ee53SJan Kryl * holding VVFSLOCK, since it resolves to a call to
2421f798ee53SJan Kryl * auto_root().
2422f798ee53SJan Kryl */
2423f798ee53SJan Kryl if (VFS_ROOT(vfsp, &newvp)) {
2424f798ee53SJan Kryl cmn_err(CE_PANIC,
2425f798ee53SJan Kryl "autofs: VFS_ROOT(vfs=%p) failed",
2426f798ee53SJan Kryl (void *)vfsp);
2427f798ee53SJan Kryl }
2428f798ee53SJan Kryl vn_vfsunlock(curvp);
2429f798ee53SJan Kryl VN_RELE(curvp);
2430f798ee53SJan Kryl currfnp = vntofn(newvp);
2431f798ee53SJan Kryl mutex_enter(&currfnp->fn_lock);
2432f798ee53SJan Kryl continue;
2433f798ee53SJan Kryl }
2434f798ee53SJan Kryl vn_vfsunlock(curvp);
2435f798ee53SJan Kryl mutex_enter(&currfnp->fn_lock);
24367c478bd9Sstevel@tonic-gate }
24377c478bd9Sstevel@tonic-gate
2438f798ee53SJan Kryl /*
2439f798ee53SJan Kryl * Now we deal with the root node (currfnp's mutex is unlocked
2440f798ee53SJan Kryl * in try_unmount_node()).
2441f798ee53SJan Kryl */
2442f798ee53SJan Kryl return (try_unmount_node(currfnp, force));
2443f798ee53SJan Kryl }
2444f798ee53SJan Kryl
2445f798ee53SJan Kryl /*
2446f798ee53SJan Kryl * XXX unmount_tree() is not suspend-safe within the scope of
2447f798ee53SJan Kryl * the present model defined for cpr to suspend the system. Calls made
2448f798ee53SJan Kryl * by the unmount_tree() that have been identified to be unsafe are
2449f798ee53SJan Kryl * (1) RPC client handle setup and client calls to automountd which can
2450f798ee53SJan Kryl * block deep down in the RPC library, (2) kmem_alloc() calls with the
2451f798ee53SJan Kryl * KM_SLEEP flag which can block if memory is low, and (3) VFS_*() and
2452f798ee53SJan Kryl * VOP_*() calls which can result in over the wire calls to servers.
2453f798ee53SJan Kryl * The thread should be completely reevaluated to make it suspend-safe in
2454f798ee53SJan Kryl * case of future updates to the cpr model.
2455f798ee53SJan Kryl */
2456f798ee53SJan Kryl void
unmount_tree(struct autofs_globals * fngp,boolean_t force)2457f798ee53SJan Kryl unmount_tree(struct autofs_globals *fngp, boolean_t force)
2458f798ee53SJan Kryl {
2459f798ee53SJan Kryl callb_cpr_t cprinfo;
2460f798ee53SJan Kryl kmutex_t unmount_tree_cpr_lock;
2461f798ee53SJan Kryl fnnode_t *root, *fnp, *next;
2462f798ee53SJan Kryl
2463f798ee53SJan Kryl mutex_init(&unmount_tree_cpr_lock, NULL, MUTEX_DEFAULT, NULL);
2464f798ee53SJan Kryl CALLB_CPR_INIT(&cprinfo, &unmount_tree_cpr_lock, callb_generic_cpr,
2465f798ee53SJan Kryl "unmount_tree");
2466f798ee53SJan Kryl
2467f798ee53SJan Kryl /*
2468f798ee53SJan Kryl * autofssys() will be calling in from the global zone and doing
2469f798ee53SJan Kryl * work on the behalf of the given zone, hence we can't always
2470f798ee53SJan Kryl * assert that we have the right credentials, nor that the
2471f798ee53SJan Kryl * caller is always in the correct zone.
2472f798ee53SJan Kryl *
2473f798ee53SJan Kryl * We do, however, know that if this is a "forced unmount"
2474f798ee53SJan Kryl * operation (which autofssys() does), then we won't go down to
2475f798ee53SJan Kryl * the krpc layers, so we don't need to fudge with the
2476f798ee53SJan Kryl * credentials.
2477f798ee53SJan Kryl */
2478f798ee53SJan Kryl ASSERT(force || fngp->fng_zoneid == getzoneid());
24797c478bd9Sstevel@tonic-gate
24807c478bd9Sstevel@tonic-gate /*
2481f798ee53SJan Kryl * If automountd is not running in this zone,
2482f798ee53SJan Kryl * don't attempt unmounting this round.
24837c478bd9Sstevel@tonic-gate */
2484f798ee53SJan Kryl if (force || auto_null_request(fngp->fng_zoneid, FALSE) == 0) {
2485f798ee53SJan Kryl /*
2486f798ee53SJan Kryl * Iterate over top level autofs filesystems and call
2487f798ee53SJan Kryl * unmount_subtree() for each of them.
2488f798ee53SJan Kryl */
2489f798ee53SJan Kryl root = fngp->fng_rootfnnodep;
2490f798ee53SJan Kryl rw_enter(&root->fn_rwlock, RW_READER);
2491f798ee53SJan Kryl for (fnp = root->fn_dirents; fnp != NULL; fnp = next) {
2492f798ee53SJan Kryl VN_HOLD(fntovn(fnp));
2493f798ee53SJan Kryl rw_exit(&root->fn_rwlock);
2494f798ee53SJan Kryl (void) unmount_subtree(fnp, force);
2495f798ee53SJan Kryl rw_enter(&root->fn_rwlock, RW_READER);
2496f798ee53SJan Kryl next = fnp->fn_next;
2497f798ee53SJan Kryl VN_RELE(fntovn(fnp));
2498f798ee53SJan Kryl }
2499f798ee53SJan Kryl rw_exit(&root->fn_rwlock);
2500f798ee53SJan Kryl }
2501f798ee53SJan Kryl
25027c478bd9Sstevel@tonic-gate mutex_enter(&unmount_tree_cpr_lock);
25037c478bd9Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo);
25047c478bd9Sstevel@tonic-gate mutex_destroy(&unmount_tree_cpr_lock);
25057c478bd9Sstevel@tonic-gate }
25067c478bd9Sstevel@tonic-gate
25077c478bd9Sstevel@tonic-gate static void
unmount_zone_tree(struct autofs_globals * fngp)25087c478bd9Sstevel@tonic-gate unmount_zone_tree(struct autofs_globals *fngp)
25097c478bd9Sstevel@tonic-gate {
2510f798ee53SJan Kryl AUTOFS_DPRINT((5, "unmount_zone_tree started. Thread created.\n"));
2511f798ee53SJan Kryl
2512f798ee53SJan Kryl unmount_tree(fngp, B_FALSE);
25137c478bd9Sstevel@tonic-gate mutex_enter(&fngp->fng_unmount_threads_lock);
25147c478bd9Sstevel@tonic-gate fngp->fng_unmount_threads--;
25157c478bd9Sstevel@tonic-gate mutex_exit(&fngp->fng_unmount_threads_lock);
25167c478bd9Sstevel@tonic-gate
2517f798ee53SJan Kryl AUTOFS_DPRINT((5, "unmount_zone_tree done. Thread exiting.\n"));
25187c478bd9Sstevel@tonic-gate
25197c478bd9Sstevel@tonic-gate zthread_exit();
25207c478bd9Sstevel@tonic-gate /* NOTREACHED */
25217c478bd9Sstevel@tonic-gate }
25227c478bd9Sstevel@tonic-gate
25237c478bd9Sstevel@tonic-gate void
auto_do_unmount(struct autofs_globals * fngp)25247c478bd9Sstevel@tonic-gate auto_do_unmount(struct autofs_globals *fngp)
25257c478bd9Sstevel@tonic-gate {
25267c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo;
25277c478bd9Sstevel@tonic-gate clock_t timeleft;
25287c478bd9Sstevel@tonic-gate zone_t *zone = curproc->p_zone;
25297c478bd9Sstevel@tonic-gate
25307c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &fngp->fng_unmount_threads_lock,
2531d6c8399bSrmesta callb_generic_cpr, "auto_do_unmount");
25327c478bd9Sstevel@tonic-gate
25337c478bd9Sstevel@tonic-gate for (;;) { /* forever */
25347c478bd9Sstevel@tonic-gate mutex_enter(&fngp->fng_unmount_threads_lock);
25357c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo);
25367c478bd9Sstevel@tonic-gate newthread:
25377c478bd9Sstevel@tonic-gate mutex_exit(&fngp->fng_unmount_threads_lock);
2538d3d50737SRafael Vanoni timeleft = zone_status_timedwait(zone, ddi_get_lbolt() +
25397c478bd9Sstevel@tonic-gate autofs_unmount_thread_timer * hz, ZONE_IS_SHUTTING_DOWN);
25407c478bd9Sstevel@tonic-gate mutex_enter(&fngp->fng_unmount_threads_lock);
25417c478bd9Sstevel@tonic-gate
25427c478bd9Sstevel@tonic-gate if (timeleft != -1) { /* didn't time out */
25437c478bd9Sstevel@tonic-gate ASSERT(zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN);
25447c478bd9Sstevel@tonic-gate /*
25457c478bd9Sstevel@tonic-gate * zone is exiting... don't create any new threads.
25467c478bd9Sstevel@tonic-gate * fng_unmount_threads_lock is released implicitly by
25477c478bd9Sstevel@tonic-gate * the below.
25487c478bd9Sstevel@tonic-gate */
25497c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo,
2550d6c8399bSrmesta &fngp->fng_unmount_threads_lock);
25517c478bd9Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo);
25527c478bd9Sstevel@tonic-gate zthread_exit();
25537c478bd9Sstevel@tonic-gate /* NOTREACHED */
25547c478bd9Sstevel@tonic-gate }
25557c478bd9Sstevel@tonic-gate if (fngp->fng_unmount_threads < autofs_unmount_threads) {
25567c478bd9Sstevel@tonic-gate fngp->fng_unmount_threads++;
25577c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo,
2558d6c8399bSrmesta &fngp->fng_unmount_threads_lock);
25597c478bd9Sstevel@tonic-gate mutex_exit(&fngp->fng_unmount_threads_lock);
25607c478bd9Sstevel@tonic-gate
25617c478bd9Sstevel@tonic-gate (void) zthread_create(NULL, 0, unmount_zone_tree, fngp,
25627c478bd9Sstevel@tonic-gate 0, minclsyspri);
25637c478bd9Sstevel@tonic-gate } else
25647c478bd9Sstevel@tonic-gate goto newthread;
25657c478bd9Sstevel@tonic-gate }
25667c478bd9Sstevel@tonic-gate /* NOTREACHED */
25677c478bd9Sstevel@tonic-gate }
25687c478bd9Sstevel@tonic-gate
25697c478bd9Sstevel@tonic-gate /*
25707c478bd9Sstevel@tonic-gate * Is nobrowse specified in option string?
25717c478bd9Sstevel@tonic-gate * opts should be a null ('\0') terminated string.
25727c478bd9Sstevel@tonic-gate * Returns non-zero if nobrowse has been specified.
25737c478bd9Sstevel@tonic-gate */
25747c478bd9Sstevel@tonic-gate int
auto_nobrowse_option(char * opts)25757c478bd9Sstevel@tonic-gate auto_nobrowse_option(char *opts)
25767c478bd9Sstevel@tonic-gate {
25777c478bd9Sstevel@tonic-gate char *buf;
25787c478bd9Sstevel@tonic-gate char *p;
25797c478bd9Sstevel@tonic-gate char *t;
25807c478bd9Sstevel@tonic-gate int nobrowse = 0;
25817c478bd9Sstevel@tonic-gate int last_opt = 0;
25827c478bd9Sstevel@tonic-gate size_t len;
25837c478bd9Sstevel@tonic-gate
25847c478bd9Sstevel@tonic-gate len = strlen(opts) + 1;
25857c478bd9Sstevel@tonic-gate p = buf = kmem_alloc(len, KM_SLEEP);
25867c478bd9Sstevel@tonic-gate (void) strcpy(buf, opts);
25877c478bd9Sstevel@tonic-gate do {
25887c478bd9Sstevel@tonic-gate if (t = strchr(p, ','))
25897c478bd9Sstevel@tonic-gate *t++ = '\0';
25907c478bd9Sstevel@tonic-gate else
25917c478bd9Sstevel@tonic-gate last_opt++;
25927c478bd9Sstevel@tonic-gate if (strcmp(p, MNTOPT_NOBROWSE) == 0)
25937c478bd9Sstevel@tonic-gate nobrowse = 1;
25947c478bd9Sstevel@tonic-gate else if (strcmp(p, MNTOPT_BROWSE) == 0)
25957c478bd9Sstevel@tonic-gate nobrowse = 0;
25967c478bd9Sstevel@tonic-gate p = t;
25977c478bd9Sstevel@tonic-gate } while (!last_opt);
25987c478bd9Sstevel@tonic-gate kmem_free(buf, len);
25997c478bd9Sstevel@tonic-gate
26007c478bd9Sstevel@tonic-gate return (nobrowse);
26017c478bd9Sstevel@tonic-gate }
26027c478bd9Sstevel@tonic-gate
26037c478bd9Sstevel@tonic-gate /*
26047c478bd9Sstevel@tonic-gate * used to log warnings only if automountd is running
26057c478bd9Sstevel@tonic-gate * with verbose mode set
26067c478bd9Sstevel@tonic-gate */
260739d3e169Sevanl
26087c478bd9Sstevel@tonic-gate void
auto_log(int verbose,zoneid_t zoneid,int level,const char * fmt,...)260939d3e169Sevanl auto_log(int verbose, zoneid_t zoneid, int level, const char *fmt, ...)
26107c478bd9Sstevel@tonic-gate {
261139d3e169Sevanl va_list args;
26127c478bd9Sstevel@tonic-gate
261339d3e169Sevanl if (verbose) {
26147c478bd9Sstevel@tonic-gate va_start(args, fmt);
261539d3e169Sevanl vzcmn_err(zoneid, level, fmt, args);
26167c478bd9Sstevel@tonic-gate va_end(args);
26177c478bd9Sstevel@tonic-gate }
26187c478bd9Sstevel@tonic-gate }
26197c478bd9Sstevel@tonic-gate
26207c478bd9Sstevel@tonic-gate #ifdef DEBUG
26217c478bd9Sstevel@tonic-gate static int autofs_debug = 0;
26227c478bd9Sstevel@tonic-gate
26237c478bd9Sstevel@tonic-gate /*
26247c478bd9Sstevel@tonic-gate * Utilities used by both client and server
26257c478bd9Sstevel@tonic-gate * Standard levels:
26267c478bd9Sstevel@tonic-gate * 0) no debugging
26277c478bd9Sstevel@tonic-gate * 1) hard failures
26287c478bd9Sstevel@tonic-gate * 2) soft failures
26297c478bd9Sstevel@tonic-gate * 3) current test software
26307c478bd9Sstevel@tonic-gate * 4) main procedure entry points
26317c478bd9Sstevel@tonic-gate * 5) main procedure exit points
26327c478bd9Sstevel@tonic-gate * 6) utility procedure entry points
26337c478bd9Sstevel@tonic-gate * 7) utility procedure exit points
26347c478bd9Sstevel@tonic-gate * 8) obscure procedure entry points
26357c478bd9Sstevel@tonic-gate * 9) obscure procedure exit points
26367c478bd9Sstevel@tonic-gate * 10) random stuff
26377c478bd9Sstevel@tonic-gate * 11) all <= 1
26387c478bd9Sstevel@tonic-gate * 12) all <= 2
26397c478bd9Sstevel@tonic-gate * 13) all <= 3
26407c478bd9Sstevel@tonic-gate * ...
26417c478bd9Sstevel@tonic-gate */
26427c478bd9Sstevel@tonic-gate /* PRINTFLIKE2 */
26437c478bd9Sstevel@tonic-gate void
auto_dprint(int level,const char * fmt,...)26447c478bd9Sstevel@tonic-gate auto_dprint(int level, const char *fmt, ...)
26457c478bd9Sstevel@tonic-gate {
26467c478bd9Sstevel@tonic-gate va_list args;
26477c478bd9Sstevel@tonic-gate
26487c478bd9Sstevel@tonic-gate if (autofs_debug == level ||
26497c478bd9Sstevel@tonic-gate (autofs_debug > 10 && (autofs_debug - 10) >= level)) {
26507c478bd9Sstevel@tonic-gate va_start(args, fmt);
26517c478bd9Sstevel@tonic-gate (void) vprintf(fmt, args);
26527c478bd9Sstevel@tonic-gate va_end(args);
26537c478bd9Sstevel@tonic-gate }
26547c478bd9Sstevel@tonic-gate }
26557c478bd9Sstevel@tonic-gate #endif /* DEBUG */
2656