1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/param.h> 30*0Sstevel@tonic-gate #include <sys/kmem.h> 31*0Sstevel@tonic-gate #include <sys/errno.h> 32*0Sstevel@tonic-gate #include <sys/proc.h> 33*0Sstevel@tonic-gate #include <sys/disp.h> 34*0Sstevel@tonic-gate #include <sys/vfs.h> 35*0Sstevel@tonic-gate #include <sys/vnode.h> 36*0Sstevel@tonic-gate #include <sys/pathname.h> 37*0Sstevel@tonic-gate #include <sys/cred.h> 38*0Sstevel@tonic-gate #include <sys/mount.h> 39*0Sstevel@tonic-gate #include <sys/cmn_err.h> 40*0Sstevel@tonic-gate #include <sys/debug.h> 41*0Sstevel@tonic-gate #include <sys/systm.h> 42*0Sstevel@tonic-gate #include <rpc/types.h> 43*0Sstevel@tonic-gate #include <rpc/xdr.h> 44*0Sstevel@tonic-gate #include <rpc/auth.h> 45*0Sstevel@tonic-gate #include <rpc/clnt.h> 46*0Sstevel@tonic-gate #include <sys/ticotsord.h> 47*0Sstevel@tonic-gate #include <sys/dirent.h> 48*0Sstevel@tonic-gate #include <fs/fs_subr.h> 49*0Sstevel@tonic-gate #include <rpcsvc/autofs_prot.h> 50*0Sstevel@tonic-gate #include <sys/fs/autofs.h> 51*0Sstevel@tonic-gate #include <sys/callb.h> 52*0Sstevel@tonic-gate #include <sys/sysmacros.h> 53*0Sstevel@tonic-gate #include <sys/zone.h> 54*0Sstevel@tonic-gate #include <sys/fs/mntdata.h> 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate /* 57*0Sstevel@tonic-gate * Autofs and Zones: 58*0Sstevel@tonic-gate * 59*0Sstevel@tonic-gate * Zones are delegated the responsibility of managing their own autofs mounts 60*0Sstevel@tonic-gate * and maps. Each zone runs its own copy of automountd, with its own timeouts, 61*0Sstevel@tonic-gate * and other logically "global" parameters. kRPC and virtualization in the 62*0Sstevel@tonic-gate * loopback transport (tl) will prevent a zone from communicating with another 63*0Sstevel@tonic-gate * zone's automountd. 64*0Sstevel@tonic-gate * 65*0Sstevel@tonic-gate * Each zone has its own "rootfnnode" and associated tree of auto nodes. 66*0Sstevel@tonic-gate * 67*0Sstevel@tonic-gate * Each zone also has its own set of "unmounter" kernel threads; these are 68*0Sstevel@tonic-gate * created and run within the zone's context (ie, they are created via 69*0Sstevel@tonic-gate * zthread_create()). 70*0Sstevel@tonic-gate * 71*0Sstevel@tonic-gate * Cross-zone mount triggers are disallowed. There is a check in 72*0Sstevel@tonic-gate * auto_trigger_mount() to this effect; EPERM is returned to indicate that the 73*0Sstevel@tonic-gate * mount is not owned by the caller. 74*0Sstevel@tonic-gate * 75*0Sstevel@tonic-gate * autofssys() enables a caller in the global zone to clean up in-kernel (as 76*0Sstevel@tonic-gate * well as regular) autofs mounts via the unmount_tree() mechanism. This is 77*0Sstevel@tonic-gate * routinely done when all mounts are removed as part of zone shutdown. 78*0Sstevel@tonic-gate */ 79*0Sstevel@tonic-gate #define TYPICALMAXPATHLEN 64 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gate static kmutex_t autofs_nodeid_lock; 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate static int auto_perform_link(fnnode_t *, struct linka *, cred_t *); 84*0Sstevel@tonic-gate static int auto_perform_actions(fninfo_t *, fnnode_t *, 85*0Sstevel@tonic-gate action_list *, cred_t *); 86*0Sstevel@tonic-gate static int auto_getmntpnt(vnode_t *, char *, vnode_t **, cred_t *); 87*0Sstevel@tonic-gate static int auto_lookup_request(fninfo_t *, char *, struct linka *, 88*0Sstevel@tonic-gate cred_t *, bool_t, bool_t *); 89*0Sstevel@tonic-gate static int auto_mount_request(fninfo_t *, char *, action_list **, 90*0Sstevel@tonic-gate cred_t *, bool_t); 91*0Sstevel@tonic-gate 92*0Sstevel@tonic-gate /* 93*0Sstevel@tonic-gate * Clears the MF_INPROG flag, and wakes up those threads sleeping on 94*0Sstevel@tonic-gate * fn_cv_mount if MF_WAITING is set. 95*0Sstevel@tonic-gate */ 96*0Sstevel@tonic-gate void 97*0Sstevel@tonic-gate auto_unblock_others( 98*0Sstevel@tonic-gate fnnode_t *fnp, 99*0Sstevel@tonic-gate uint_t operation) /* either MF_INPROG or MF_LOOKUP */ 100*0Sstevel@tonic-gate { 101*0Sstevel@tonic-gate ASSERT(operation & (MF_INPROG | MF_LOOKUP)); 102*0Sstevel@tonic-gate fnp->fn_flags &= ~operation; 103*0Sstevel@tonic-gate if (fnp->fn_flags & MF_WAITING) { 104*0Sstevel@tonic-gate fnp->fn_flags &= ~MF_WAITING; 105*0Sstevel@tonic-gate cv_broadcast(&fnp->fn_cv_mount); 106*0Sstevel@tonic-gate } 107*0Sstevel@tonic-gate } 108*0Sstevel@tonic-gate 109*0Sstevel@tonic-gate int 110*0Sstevel@tonic-gate auto_wait4mount(fnnode_t *fnp) 111*0Sstevel@tonic-gate { 112*0Sstevel@tonic-gate int error; 113*0Sstevel@tonic-gate k_sigset_t smask; 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_wait4mount: fnp=%p\n", (void *)fnp)); 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 118*0Sstevel@tonic-gate while (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) { 119*0Sstevel@tonic-gate /* 120*0Sstevel@tonic-gate * There is a mount or a lookup in progress. 121*0Sstevel@tonic-gate */ 122*0Sstevel@tonic-gate fnp->fn_flags |= MF_WAITING; 123*0Sstevel@tonic-gate sigintr(&smask, 1); 124*0Sstevel@tonic-gate if (!cv_wait_sig(&fnp->fn_cv_mount, &fnp->fn_lock)) { 125*0Sstevel@tonic-gate /* 126*0Sstevel@tonic-gate * Decided not to wait for operation to 127*0Sstevel@tonic-gate * finish after all. 128*0Sstevel@tonic-gate */ 129*0Sstevel@tonic-gate sigunintr(&smask); 130*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 131*0Sstevel@tonic-gate return (EINTR); 132*0Sstevel@tonic-gate } 133*0Sstevel@tonic-gate sigunintr(&smask); 134*0Sstevel@tonic-gate } 135*0Sstevel@tonic-gate error = fnp->fn_error; 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate if (error == EINTR) { 138*0Sstevel@tonic-gate /* 139*0Sstevel@tonic-gate * The thread doing the mount got interrupted, we need to 140*0Sstevel@tonic-gate * try again, by returning EAGAIN. 141*0Sstevel@tonic-gate */ 142*0Sstevel@tonic-gate error = EAGAIN; 143*0Sstevel@tonic-gate } 144*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_wait4mount: fnp=%p error=%d\n", (void *)fnp, 147*0Sstevel@tonic-gate error)); 148*0Sstevel@tonic-gate return (error); 149*0Sstevel@tonic-gate } 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate int 152*0Sstevel@tonic-gate auto_lookup_aux(fnnode_t *fnp, char *name, cred_t *cred) 153*0Sstevel@tonic-gate { 154*0Sstevel@tonic-gate struct fninfo *fnip; 155*0Sstevel@tonic-gate struct linka link; 156*0Sstevel@tonic-gate bool_t mountreq = FALSE; 157*0Sstevel@tonic-gate int error = 0; 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate fnip = vfstofni(fntovn(fnp)->v_vfsp); 160*0Sstevel@tonic-gate bzero(&link, sizeof (link)); 161*0Sstevel@tonic-gate error = auto_lookup_request(fnip, name, &link, cred, TRUE, &mountreq); 162*0Sstevel@tonic-gate if (!error) { 163*0Sstevel@tonic-gate if (link.link != NULL) { 164*0Sstevel@tonic-gate /* 165*0Sstevel@tonic-gate * This node should be a symlink 166*0Sstevel@tonic-gate */ 167*0Sstevel@tonic-gate error = auto_perform_link(fnp, &link, cred); 168*0Sstevel@tonic-gate kmem_free(link.dir, strlen(link.dir) + 1); 169*0Sstevel@tonic-gate kmem_free(link.link, strlen(link.link) + 1); 170*0Sstevel@tonic-gate } else if (mountreq) { 171*0Sstevel@tonic-gate /* 172*0Sstevel@tonic-gate * The automount daemon is requesting a mount, 173*0Sstevel@tonic-gate * implying this entry must be a wildcard match and 174*0Sstevel@tonic-gate * therefore in need of verification that the entry 175*0Sstevel@tonic-gate * exists on the server. 176*0Sstevel@tonic-gate */ 177*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 178*0Sstevel@tonic-gate AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG); 179*0Sstevel@tonic-gate fnp->fn_error = 0; 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate /* 182*0Sstevel@tonic-gate * Unblock other lookup requests on this node, 183*0Sstevel@tonic-gate * this is needed to let the lookup generated by 184*0Sstevel@tonic-gate * the mount call to complete. The caveat is 185*0Sstevel@tonic-gate * other lookups on this node can also get by, 186*0Sstevel@tonic-gate * i.e., another lookup on this node that occurs 187*0Sstevel@tonic-gate * while this lookup is attempting the mount 188*0Sstevel@tonic-gate * would return a positive result no matter what. 189*0Sstevel@tonic-gate * Therefore two lookups on the this node could 190*0Sstevel@tonic-gate * potentially get disparate results. 191*0Sstevel@tonic-gate */ 192*0Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP); 193*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 194*0Sstevel@tonic-gate /* 195*0Sstevel@tonic-gate * auto_new_mount_thread fires up a new thread which 196*0Sstevel@tonic-gate * calls automountd finishing up the work 197*0Sstevel@tonic-gate */ 198*0Sstevel@tonic-gate auto_new_mount_thread(fnp, name, cred); 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate /* 201*0Sstevel@tonic-gate * At this point, we are simply another thread 202*0Sstevel@tonic-gate * waiting for the mount to complete 203*0Sstevel@tonic-gate */ 204*0Sstevel@tonic-gate error = auto_wait4mount(fnp); 205*0Sstevel@tonic-gate if (error == AUTOFS_SHUTDOWN) 206*0Sstevel@tonic-gate error = ENOENT; 207*0Sstevel@tonic-gate } 208*0Sstevel@tonic-gate } 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 211*0Sstevel@tonic-gate fnp->fn_error = error; 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gate /* 214*0Sstevel@tonic-gate * Notify threads waiting for lookup/mount that 215*0Sstevel@tonic-gate * it's done. 216*0Sstevel@tonic-gate */ 217*0Sstevel@tonic-gate if (mountreq) { 218*0Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 219*0Sstevel@tonic-gate } else { 220*0Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_LOOKUP); 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 223*0Sstevel@tonic-gate return (error); 224*0Sstevel@tonic-gate } 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate /* 227*0Sstevel@tonic-gate * Starting point for thread to handle mount requests with automountd. 228*0Sstevel@tonic-gate * XXX auto_mount_thread() is not suspend-safe within the scope of 229*0Sstevel@tonic-gate * the present model defined for cpr to suspend the system. Calls 230*0Sstevel@tonic-gate * made by the auto_mount_thread() that have been identified to be unsafe 231*0Sstevel@tonic-gate * are (1) RPC client handle setup and client calls to automountd which 232*0Sstevel@tonic-gate * can block deep down in the RPC library, (2) kmem_alloc() calls with the 233*0Sstevel@tonic-gate * KM_SLEEP flag which can block if memory is low, and (3) VFS_*(), and 234*0Sstevel@tonic-gate * lookuppnvp() calls which can result in over the wire calls to servers. 235*0Sstevel@tonic-gate * The thread should be completely reevaluated to make it suspend-safe in 236*0Sstevel@tonic-gate * case of future updates to the cpr model. 237*0Sstevel@tonic-gate */ 238*0Sstevel@tonic-gate static void 239*0Sstevel@tonic-gate auto_mount_thread(struct autofs_callargs *argsp) 240*0Sstevel@tonic-gate { 241*0Sstevel@tonic-gate struct fninfo *fnip; 242*0Sstevel@tonic-gate fnnode_t *fnp; 243*0Sstevel@tonic-gate vnode_t *vp; 244*0Sstevel@tonic-gate char *name; 245*0Sstevel@tonic-gate size_t namelen; 246*0Sstevel@tonic-gate cred_t *cred; 247*0Sstevel@tonic-gate action_list *alp = NULL; 248*0Sstevel@tonic-gate int error; 249*0Sstevel@tonic-gate callb_cpr_t cprinfo; 250*0Sstevel@tonic-gate kmutex_t auto_mount_thread_cpr_lock; 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate mutex_init(&auto_mount_thread_cpr_lock, NULL, MUTEX_DEFAULT, NULL); 253*0Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &auto_mount_thread_cpr_lock, callb_generic_cpr, 254*0Sstevel@tonic-gate "auto_mount_thread"); 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate fnp = argsp->fnc_fnp; 257*0Sstevel@tonic-gate vp = fntovn(fnp); 258*0Sstevel@tonic-gate fnip = vfstofni(vp->v_vfsp); 259*0Sstevel@tonic-gate name = argsp->fnc_name; 260*0Sstevel@tonic-gate cred = argsp->fnc_cred; 261*0Sstevel@tonic-gate ASSERT(crgetzoneid(argsp->fnc_cred) == fnip->fi_zoneid); 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate error = auto_mount_request(fnip, name, &alp, cred, TRUE); 264*0Sstevel@tonic-gate if (!error) 265*0Sstevel@tonic-gate error = auto_perform_actions(fnip, fnp, alp, cred); 266*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 267*0Sstevel@tonic-gate fnp->fn_error = error; 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gate /* 270*0Sstevel@tonic-gate * Notify threads waiting for mount that 271*0Sstevel@tonic-gate * it's done. 272*0Sstevel@tonic-gate */ 273*0Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 274*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 275*0Sstevel@tonic-gate 276*0Sstevel@tonic-gate VN_RELE(vp); 277*0Sstevel@tonic-gate crfree(argsp->fnc_cred); 278*0Sstevel@tonic-gate namelen = strlen(argsp->fnc_name) + 1; 279*0Sstevel@tonic-gate kmem_free(argsp->fnc_name, namelen); 280*0Sstevel@tonic-gate kmem_free(argsp, sizeof (*argsp)); 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate mutex_enter(&auto_mount_thread_cpr_lock); 283*0Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); 284*0Sstevel@tonic-gate mutex_destroy(&auto_mount_thread_cpr_lock); 285*0Sstevel@tonic-gate zthread_exit(); 286*0Sstevel@tonic-gate /* NOTREACHED */ 287*0Sstevel@tonic-gate } 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate static int autofs_thr_success = 0; 290*0Sstevel@tonic-gate 291*0Sstevel@tonic-gate /* 292*0Sstevel@tonic-gate * Creates new thread which calls auto_mount_thread which does 293*0Sstevel@tonic-gate * the bulk of the work calling automountd, via 'auto_perform_actions'. 294*0Sstevel@tonic-gate */ 295*0Sstevel@tonic-gate void 296*0Sstevel@tonic-gate auto_new_mount_thread(fnnode_t *fnp, char *name, cred_t *cred) 297*0Sstevel@tonic-gate { 298*0Sstevel@tonic-gate struct autofs_callargs *argsp; 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate argsp = kmem_alloc(sizeof (*argsp), KM_SLEEP); 301*0Sstevel@tonic-gate VN_HOLD(fntovn(fnp)); 302*0Sstevel@tonic-gate argsp->fnc_fnp = fnp; 303*0Sstevel@tonic-gate argsp->fnc_name = kmem_alloc(strlen(name) + 1, KM_SLEEP); 304*0Sstevel@tonic-gate (void) strcpy(argsp->fnc_name, name); 305*0Sstevel@tonic-gate argsp->fnc_origin = curthread; 306*0Sstevel@tonic-gate crhold(cred); 307*0Sstevel@tonic-gate argsp->fnc_cred = cred; 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate (void) zthread_create(NULL, 0, auto_mount_thread, argsp, 0, 310*0Sstevel@tonic-gate minclsyspri); 311*0Sstevel@tonic-gate autofs_thr_success++; 312*0Sstevel@tonic-gate } 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate int 315*0Sstevel@tonic-gate auto_calldaemon( 316*0Sstevel@tonic-gate fninfo_t *fnip, 317*0Sstevel@tonic-gate rpcproc_t which, 318*0Sstevel@tonic-gate xdrproc_t xdrargs, 319*0Sstevel@tonic-gate void *argsp, 320*0Sstevel@tonic-gate xdrproc_t xdrres, 321*0Sstevel@tonic-gate void *resp, 322*0Sstevel@tonic-gate cred_t *cred, 323*0Sstevel@tonic-gate bool_t hard) /* retry forever? */ 324*0Sstevel@tonic-gate { 325*0Sstevel@tonic-gate CLIENT *client; 326*0Sstevel@tonic-gate enum clnt_stat status; 327*0Sstevel@tonic-gate struct rpc_err rpcerr; 328*0Sstevel@tonic-gate struct timeval wait; 329*0Sstevel@tonic-gate bool_t tryagain; 330*0Sstevel@tonic-gate int error = 0; 331*0Sstevel@tonic-gate k_sigset_t smask; 332*0Sstevel@tonic-gate struct autofs_globals *fngp = vntofn(fnip->fi_rootvp)->fn_globals; 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_calldaemon\n")); 335*0Sstevel@tonic-gate 336*0Sstevel@tonic-gate error = clnt_tli_kcreate(&fnip->fi_knconf, &fnip->fi_addr, 337*0Sstevel@tonic-gate AUTOFS_PROG, AUTOFS_VERS, 0, INT_MAX, cred, &client); 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate if (error) { 340*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "autofs: clnt_tli_kcreate: error %d", 341*0Sstevel@tonic-gate error); 342*0Sstevel@tonic-gate goto done; 343*0Sstevel@tonic-gate } 344*0Sstevel@tonic-gate 345*0Sstevel@tonic-gate /* 346*0Sstevel@tonic-gate * Release the old authentication handle. It was probably 347*0Sstevel@tonic-gate * AUTH_UNIX. 348*0Sstevel@tonic-gate */ 349*0Sstevel@tonic-gate auth_destroy(client->cl_auth); 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gate /* 352*0Sstevel@tonic-gate * Create a new authentication handle for AUTH_LOOPBACK. This 353*0Sstevel@tonic-gate * will allow us to correctly handle the entire groups list. 354*0Sstevel@tonic-gate */ 355*0Sstevel@tonic-gate client->cl_auth = authloopback_create(); 356*0Sstevel@tonic-gate if (client->cl_auth == NULL) { 357*0Sstevel@tonic-gate clnt_destroy(client); 358*0Sstevel@tonic-gate error = EINTR; 359*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, 360*0Sstevel@tonic-gate "autofs: authloopback_create: error %d", error); 361*0Sstevel@tonic-gate goto done; 362*0Sstevel@tonic-gate } 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate wait.tv_sec = fnip->fi_rpc_to; 365*0Sstevel@tonic-gate wait.tv_usec = 0; 366*0Sstevel@tonic-gate do { 367*0Sstevel@tonic-gate tryagain = FALSE; 368*0Sstevel@tonic-gate error = 0; 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate /* 371*0Sstevel@tonic-gate * Mask out all signals except SIGHUP, SIGINT, SIGQUIT 372*0Sstevel@tonic-gate * and SIGTERM. (Preserving the existing masks) 373*0Sstevel@tonic-gate */ 374*0Sstevel@tonic-gate sigintr(&smask, 1); 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate status = CLNT_CALL(client, which, xdrargs, argsp, 377*0Sstevel@tonic-gate xdrres, resp, wait); 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate /* 380*0Sstevel@tonic-gate * Restore original signal mask 381*0Sstevel@tonic-gate */ 382*0Sstevel@tonic-gate sigunintr(&smask); 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate switch (status) { 385*0Sstevel@tonic-gate case RPC_SUCCESS: 386*0Sstevel@tonic-gate break; 387*0Sstevel@tonic-gate 388*0Sstevel@tonic-gate case RPC_INTR: 389*0Sstevel@tonic-gate error = EINTR; 390*0Sstevel@tonic-gate break; 391*0Sstevel@tonic-gate 392*0Sstevel@tonic-gate case RPC_TIMEDOUT: 393*0Sstevel@tonic-gate tryagain = TRUE; 394*0Sstevel@tonic-gate error = ETIMEDOUT; 395*0Sstevel@tonic-gate break; 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate case RPC_CANTCONNECT: 398*0Sstevel@tonic-gate case RPC_CANTCREATESTREAM: 399*0Sstevel@tonic-gate /* 400*0Sstevel@tonic-gate * The connection could not be established 401*0Sstevel@tonic-gate */ 402*0Sstevel@tonic-gate /* fall thru */ 403*0Sstevel@tonic-gate case RPC_XPRTFAILED: 404*0Sstevel@tonic-gate /* 405*0Sstevel@tonic-gate * The connection could not be established or 406*0Sstevel@tonic-gate * was dropped, we differentiate between the two 407*0Sstevel@tonic-gate * conditions by calling CLNT_GETERR and look at 408*0Sstevel@tonic-gate * rpcerror.re_errno. 409*0Sstevel@tonic-gate * If rpcerr.re_errno == ECONNREFUSED, then the 410*0Sstevel@tonic-gate * connection could not be established at all. 411*0Sstevel@tonic-gate */ 412*0Sstevel@tonic-gate error = ECONNREFUSED; 413*0Sstevel@tonic-gate if (status == RPC_XPRTFAILED) { 414*0Sstevel@tonic-gate CLNT_GETERR(client, &rpcerr); 415*0Sstevel@tonic-gate if (rpcerr.re_errno != ECONNREFUSED) { 416*0Sstevel@tonic-gate /* 417*0Sstevel@tonic-gate * The connection was dropped, return 418*0Sstevel@tonic-gate * to the caller if hard is not set. 419*0Sstevel@tonic-gate * It is the responsability of the 420*0Sstevel@tonic-gate * caller to retry the call if 421*0Sstevel@tonic-gate * appropriate. 422*0Sstevel@tonic-gate */ 423*0Sstevel@tonic-gate error = ECONNRESET; 424*0Sstevel@tonic-gate } 425*0Sstevel@tonic-gate } 426*0Sstevel@tonic-gate /* 427*0Sstevel@tonic-gate * We know that the current thread is doing work on 428*0Sstevel@tonic-gate * behalf of its own zone, so it's ok to use 429*0Sstevel@tonic-gate * curproc->p_zone. 430*0Sstevel@tonic-gate */ 431*0Sstevel@tonic-gate ASSERT(fngp->fng_zoneid == getzoneid()); 432*0Sstevel@tonic-gate if (zone_status_get(curproc->p_zone) >= 433*0Sstevel@tonic-gate ZONE_IS_SHUTTING_DOWN) { 434*0Sstevel@tonic-gate /* 435*0Sstevel@tonic-gate * There's no point in trying to talk to 436*0Sstevel@tonic-gate * automountd. Plus, zone_shutdown() is 437*0Sstevel@tonic-gate * waiting for us. 438*0Sstevel@tonic-gate */ 439*0Sstevel@tonic-gate tryagain = FALSE; 440*0Sstevel@tonic-gate break; 441*0Sstevel@tonic-gate } 442*0Sstevel@tonic-gate tryagain = hard; 443*0Sstevel@tonic-gate if (!fngp->fng_printed_not_running_msg) { 444*0Sstevel@tonic-gate if (tryagain) { 445*0Sstevel@tonic-gate fngp->fng_printed_not_running_msg = 1; 446*0Sstevel@tonic-gate zprintf(fngp->fng_zoneid, 447*0Sstevel@tonic-gate "automountd not running, retrying\n"); 448*0Sstevel@tonic-gate } 449*0Sstevel@tonic-gate } 450*0Sstevel@tonic-gate break; 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gate default: 453*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "autofs: %s", 454*0Sstevel@tonic-gate clnt_sperrno(status)); 455*0Sstevel@tonic-gate error = ENOENT; 456*0Sstevel@tonic-gate break; 457*0Sstevel@tonic-gate } 458*0Sstevel@tonic-gate } while (tryagain); 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate if (status == RPC_SUCCESS) { 461*0Sstevel@tonic-gate if (fngp->fng_printed_not_running_msg == 1) { 462*0Sstevel@tonic-gate fngp->fng_printed_not_running_msg = 0; 463*0Sstevel@tonic-gate zprintf(fngp->fng_zoneid, "automountd OK\n"); 464*0Sstevel@tonic-gate } 465*0Sstevel@tonic-gate } 466*0Sstevel@tonic-gate auth_destroy(client->cl_auth); 467*0Sstevel@tonic-gate clnt_destroy(client); 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate done: 470*0Sstevel@tonic-gate ASSERT(status == RPC_SUCCESS || error != 0); 471*0Sstevel@tonic-gate 472*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_calldaemon error=%d\n", error)); 473*0Sstevel@tonic-gate return (error); 474*0Sstevel@tonic-gate } 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate static int 477*0Sstevel@tonic-gate auto_null_request(fninfo_t *fnip, cred_t *cred, bool_t hard) 478*0Sstevel@tonic-gate { 479*0Sstevel@tonic-gate int error; 480*0Sstevel@tonic-gate 481*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tauto_null_request\n")); 482*0Sstevel@tonic-gate 483*0Sstevel@tonic-gate error = auto_calldaemon(fnip, NULLPROC, xdr_void, NULL, xdr_void, NULL, 484*0Sstevel@tonic-gate cred, hard); 485*0Sstevel@tonic-gate 486*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tauto_null_request: error=%d\n", error)); 487*0Sstevel@tonic-gate return (error); 488*0Sstevel@tonic-gate } 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate static int 491*0Sstevel@tonic-gate auto_lookup_request( 492*0Sstevel@tonic-gate fninfo_t *fnip, 493*0Sstevel@tonic-gate char *key, 494*0Sstevel@tonic-gate struct linka *lnp, 495*0Sstevel@tonic-gate cred_t *cred, 496*0Sstevel@tonic-gate bool_t hard, 497*0Sstevel@tonic-gate bool_t *mountreq) 498*0Sstevel@tonic-gate { 499*0Sstevel@tonic-gate int error; 500*0Sstevel@tonic-gate struct autofs_globals *fngp; 501*0Sstevel@tonic-gate struct autofs_lookupargs request; 502*0Sstevel@tonic-gate struct autofs_lookupres result; 503*0Sstevel@tonic-gate struct linka *p; 504*0Sstevel@tonic-gate 505*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_lookup_request: path=%s name=%s\n", 506*0Sstevel@tonic-gate fnip->fi_path, key)); 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate fngp = vntofn(fnip->fi_rootvp)->fn_globals; 509*0Sstevel@tonic-gate request.map = fnip->fi_map; 510*0Sstevel@tonic-gate request.path = fnip->fi_path; 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate if (fnip->fi_flags & MF_DIRECT) 513*0Sstevel@tonic-gate request.name = fnip->fi_key; 514*0Sstevel@tonic-gate else 515*0Sstevel@tonic-gate request.name = key; 516*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_lookup_request: using key=%s\n", request.name)); 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate request.subdir = fnip->fi_subdir; 519*0Sstevel@tonic-gate request.opts = fnip->fi_opts; 520*0Sstevel@tonic-gate request.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE; 521*0Sstevel@tonic-gate 522*0Sstevel@tonic-gate bzero(&result, sizeof (result)); 523*0Sstevel@tonic-gate error = auto_calldaemon(fnip, AUTOFS_LOOKUP, 524*0Sstevel@tonic-gate xdr_autofs_lookupargs, &request, 525*0Sstevel@tonic-gate xdr_autofs_lookupres, &result, 526*0Sstevel@tonic-gate cred, hard); 527*0Sstevel@tonic-gate if (!error) { 528*0Sstevel@tonic-gate fngp->fng_verbose = result.lu_verbose; 529*0Sstevel@tonic-gate switch (result.lu_res) { 530*0Sstevel@tonic-gate case AUTOFS_OK: 531*0Sstevel@tonic-gate switch (result.lu_type.action) { 532*0Sstevel@tonic-gate case AUTOFS_MOUNT_RQ: 533*0Sstevel@tonic-gate lnp->link = NULL; 534*0Sstevel@tonic-gate lnp->dir = NULL; 535*0Sstevel@tonic-gate *mountreq = TRUE; 536*0Sstevel@tonic-gate break; 537*0Sstevel@tonic-gate case AUTOFS_LINK_RQ: 538*0Sstevel@tonic-gate p = 539*0Sstevel@tonic-gate &result.lu_type.lookup_result_type_u.lt_linka; 540*0Sstevel@tonic-gate lnp->dir = kmem_alloc(strlen(p->dir) + 1, 541*0Sstevel@tonic-gate KM_SLEEP); 542*0Sstevel@tonic-gate (void) strcpy(lnp->dir, p->dir); 543*0Sstevel@tonic-gate lnp->link = kmem_alloc(strlen(p->link) + 1, 544*0Sstevel@tonic-gate KM_SLEEP); 545*0Sstevel@tonic-gate (void) strcpy(lnp->link, p->link); 546*0Sstevel@tonic-gate break; 547*0Sstevel@tonic-gate case AUTOFS_NONE: 548*0Sstevel@tonic-gate lnp->link = NULL; 549*0Sstevel@tonic-gate lnp->dir = NULL; 550*0Sstevel@tonic-gate break; 551*0Sstevel@tonic-gate default: 552*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, 553*0Sstevel@tonic-gate "auto_lookup_request: bad action type %d", 554*0Sstevel@tonic-gate result.lu_res); 555*0Sstevel@tonic-gate error = ENOENT; 556*0Sstevel@tonic-gate } 557*0Sstevel@tonic-gate break; 558*0Sstevel@tonic-gate case AUTOFS_NOENT: 559*0Sstevel@tonic-gate error = ENOENT; 560*0Sstevel@tonic-gate break; 561*0Sstevel@tonic-gate default: 562*0Sstevel@tonic-gate error = ENOENT; 563*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, 564*0Sstevel@tonic-gate "auto_lookup_request: unknown result: %d", 565*0Sstevel@tonic-gate result.lu_res); 566*0Sstevel@tonic-gate break; 567*0Sstevel@tonic-gate } 568*0Sstevel@tonic-gate } 569*0Sstevel@tonic-gate 570*0Sstevel@tonic-gate done: 571*0Sstevel@tonic-gate xdr_free(xdr_autofs_lookupres, (char *)&result); 572*0Sstevel@tonic-gate 573*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_lookup_request: path=%s name=%s error=%d\n", 574*0Sstevel@tonic-gate fnip->fi_path, key, error)); 575*0Sstevel@tonic-gate return (error); 576*0Sstevel@tonic-gate } 577*0Sstevel@tonic-gate 578*0Sstevel@tonic-gate static int 579*0Sstevel@tonic-gate auto_mount_request( 580*0Sstevel@tonic-gate fninfo_t *fnip, 581*0Sstevel@tonic-gate char *key, 582*0Sstevel@tonic-gate action_list **alpp, 583*0Sstevel@tonic-gate cred_t *cred, 584*0Sstevel@tonic-gate bool_t hard) 585*0Sstevel@tonic-gate { 586*0Sstevel@tonic-gate int error; 587*0Sstevel@tonic-gate struct autofs_globals *fngp; 588*0Sstevel@tonic-gate struct autofs_lookupargs request; 589*0Sstevel@tonic-gate struct autofs_mountres *result; 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_mount_request: path=%s name=%s\n", 592*0Sstevel@tonic-gate fnip->fi_path, key)); 593*0Sstevel@tonic-gate 594*0Sstevel@tonic-gate fngp = vntofn(fnip->fi_rootvp)->fn_globals; 595*0Sstevel@tonic-gate request.map = fnip->fi_map; 596*0Sstevel@tonic-gate request.path = fnip->fi_path; 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate if (fnip->fi_flags & MF_DIRECT) 599*0Sstevel@tonic-gate request.name = fnip->fi_key; 600*0Sstevel@tonic-gate else 601*0Sstevel@tonic-gate request.name = key; 602*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_mount_request: using key=%s\n", request.name)); 603*0Sstevel@tonic-gate 604*0Sstevel@tonic-gate request.subdir = fnip->fi_subdir; 605*0Sstevel@tonic-gate request.opts = fnip->fi_opts; 606*0Sstevel@tonic-gate request.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE; 607*0Sstevel@tonic-gate 608*0Sstevel@tonic-gate *alpp = NULL; 609*0Sstevel@tonic-gate result = kmem_zalloc(sizeof (*result), KM_SLEEP); 610*0Sstevel@tonic-gate error = auto_calldaemon(fnip, AUTOFS_MOUNT, 611*0Sstevel@tonic-gate xdr_autofs_lookupargs, &request, 612*0Sstevel@tonic-gate xdr_autofs_mountres, result, 613*0Sstevel@tonic-gate cred, hard); 614*0Sstevel@tonic-gate if (!error) { 615*0Sstevel@tonic-gate fngp->fng_verbose = result->mr_verbose; 616*0Sstevel@tonic-gate switch (result->mr_type.status) { 617*0Sstevel@tonic-gate case AUTOFS_ACTION: 618*0Sstevel@tonic-gate error = 0; 619*0Sstevel@tonic-gate /* 620*0Sstevel@tonic-gate * Save the action list since it is used by 621*0Sstevel@tonic-gate * the caller. We NULL the action list pointer 622*0Sstevel@tonic-gate * in 'result' so that xdr_free() will not free 623*0Sstevel@tonic-gate * the list. 624*0Sstevel@tonic-gate */ 625*0Sstevel@tonic-gate *alpp = result->mr_type.mount_result_type_u.list; 626*0Sstevel@tonic-gate result->mr_type.mount_result_type_u.list = NULL; 627*0Sstevel@tonic-gate break; 628*0Sstevel@tonic-gate case AUTOFS_DONE: 629*0Sstevel@tonic-gate error = result->mr_type.mount_result_type_u.error; 630*0Sstevel@tonic-gate break; 631*0Sstevel@tonic-gate default: 632*0Sstevel@tonic-gate error = ENOENT; 633*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, 634*0Sstevel@tonic-gate "auto_mount_request: unknown status %d", 635*0Sstevel@tonic-gate result->mr_type.status); 636*0Sstevel@tonic-gate break; 637*0Sstevel@tonic-gate } 638*0Sstevel@tonic-gate } 639*0Sstevel@tonic-gate 640*0Sstevel@tonic-gate xdr_free(xdr_autofs_mountres, (char *)result); 641*0Sstevel@tonic-gate kmem_free(result, sizeof (*result)); 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_mount_request: path=%s name=%s error=%d\n", 644*0Sstevel@tonic-gate fnip->fi_path, key, error)); 645*0Sstevel@tonic-gate return (error); 646*0Sstevel@tonic-gate } 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate 649*0Sstevel@tonic-gate static int 650*0Sstevel@tonic-gate auto_send_unmount_request( 651*0Sstevel@tonic-gate fninfo_t *fnip, 652*0Sstevel@tonic-gate umntrequest *ul, 653*0Sstevel@tonic-gate cred_t *cred, 654*0Sstevel@tonic-gate bool_t hard) 655*0Sstevel@tonic-gate { 656*0Sstevel@tonic-gate int error; 657*0Sstevel@tonic-gate umntres result; 658*0Sstevel@tonic-gate 659*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tauto_send_unmount_request: fstype=%s " 660*0Sstevel@tonic-gate " mntpnt=%s\n", ul->fstype, ul->mntpnt)); 661*0Sstevel@tonic-gate 662*0Sstevel@tonic-gate error = auto_calldaemon(fnip, AUTOFS_UNMOUNT, 663*0Sstevel@tonic-gate xdr_umntrequest, ul, 664*0Sstevel@tonic-gate xdr_umntres, &result, 665*0Sstevel@tonic-gate cred, hard); 666*0Sstevel@tonic-gate if (!error) 667*0Sstevel@tonic-gate error = result.status; 668*0Sstevel@tonic-gate 669*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tauto_send_unmount_request: error=%d\n", error)); 670*0Sstevel@tonic-gate 671*0Sstevel@tonic-gate return (error); 672*0Sstevel@tonic-gate } 673*0Sstevel@tonic-gate 674*0Sstevel@tonic-gate static int 675*0Sstevel@tonic-gate auto_perform_link(fnnode_t *fnp, struct linka *linkp, cred_t *cred) 676*0Sstevel@tonic-gate { 677*0Sstevel@tonic-gate vnode_t *vp; 678*0Sstevel@tonic-gate size_t len; 679*0Sstevel@tonic-gate char *tmp; 680*0Sstevel@tonic-gate 681*0Sstevel@tonic-gate AUTOFS_DPRINT((3, "auto_perform_link: fnp=%p dir=%s link=%s\n", 682*0Sstevel@tonic-gate (void *)fnp, linkp->dir, linkp->link)); 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate len = strlen(linkp->link) + 1; /* include '\0' */ 685*0Sstevel@tonic-gate tmp = kmem_zalloc(len, KM_SLEEP); 686*0Sstevel@tonic-gate (void) kcopy(linkp->link, tmp, len); 687*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 688*0Sstevel@tonic-gate fnp->fn_symlink = tmp; 689*0Sstevel@tonic-gate fnp->fn_symlinklen = (uint_t)len; 690*0Sstevel@tonic-gate fnp->fn_flags |= MF_THISUID_MATCH_RQD; 691*0Sstevel@tonic-gate crhold(cred); 692*0Sstevel@tonic-gate fnp->fn_cred = cred; 693*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 694*0Sstevel@tonic-gate 695*0Sstevel@tonic-gate vp = fntovn(fnp); 696*0Sstevel@tonic-gate vp->v_type = VLNK; 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gate return (0); 699*0Sstevel@tonic-gate } 700*0Sstevel@tonic-gate 701*0Sstevel@tonic-gate static boolean_t 702*0Sstevel@tonic-gate auto_invalid_action(fninfo_t *dfnip, fnnode_t *dfnp, action_list *p) 703*0Sstevel@tonic-gate { 704*0Sstevel@tonic-gate struct mounta *m; 705*0Sstevel@tonic-gate struct autofs_args *argsp; 706*0Sstevel@tonic-gate vnode_t *dvp; 707*0Sstevel@tonic-gate char buff[AUTOFS_MAXPATHLEN]; 708*0Sstevel@tonic-gate size_t len; 709*0Sstevel@tonic-gate struct autofs_globals *fngp; 710*0Sstevel@tonic-gate 711*0Sstevel@tonic-gate fngp = dfnp->fn_globals; 712*0Sstevel@tonic-gate dvp = fntovn(dfnp); 713*0Sstevel@tonic-gate /* 714*0Sstevel@tonic-gate * Before we go any further, this better be a mount request. 715*0Sstevel@tonic-gate */ 716*0Sstevel@tonic-gate if (p->action.action != AUTOFS_MOUNT_RQ) 717*0Sstevel@tonic-gate return (B_TRUE); 718*0Sstevel@tonic-gate m = &p->action.action_list_entry_u.mounta; 719*0Sstevel@tonic-gate /* 720*0Sstevel@tonic-gate * Make sure we aren't geting passed NULL values or a "dir" that 721*0Sstevel@tonic-gate * isn't "." and doesn't begin with "./". 722*0Sstevel@tonic-gate * 723*0Sstevel@tonic-gate * We also only want to perform autofs mounts, so make sure 724*0Sstevel@tonic-gate * no-one is trying to trick us into doing anything else. 725*0Sstevel@tonic-gate */ 726*0Sstevel@tonic-gate if (m->spec == NULL || m->dir == NULL || m->dir[0] != '.' || 727*0Sstevel@tonic-gate (m->dir[1] != '/' && m->dir[1] != '\0') || 728*0Sstevel@tonic-gate m->fstype == NULL || strcmp(m->fstype, "autofs") != 0 || 729*0Sstevel@tonic-gate m->dataptr == NULL || m->datalen != sizeof (struct autofs_args) || 730*0Sstevel@tonic-gate m->optptr == NULL) 731*0Sstevel@tonic-gate return (B_TRUE); 732*0Sstevel@tonic-gate /* 733*0Sstevel@tonic-gate * We also don't like ".."s in the pathname. Symlinks are 734*0Sstevel@tonic-gate * handled by the fact that we'll use NOFOLLOW when we do 735*0Sstevel@tonic-gate * lookup()s. 736*0Sstevel@tonic-gate */ 737*0Sstevel@tonic-gate if (strstr(m->dir, "/../") != NULL || 738*0Sstevel@tonic-gate (len = strlen(m->dir)) > sizeof ("/..") - 1 && 739*0Sstevel@tonic-gate m->dir[len] == '.' && m->dir[len - 1] == '.' && 740*0Sstevel@tonic-gate m->dir[len - 2] == '/') 741*0Sstevel@tonic-gate return (B_TRUE); 742*0Sstevel@tonic-gate argsp = (struct autofs_args *)m->dataptr; 743*0Sstevel@tonic-gate /* 744*0Sstevel@tonic-gate * We don't want NULL values here either. 745*0Sstevel@tonic-gate */ 746*0Sstevel@tonic-gate if (argsp->addr.buf == NULL || argsp->path == NULL || 747*0Sstevel@tonic-gate argsp->opts == NULL || argsp->map == NULL || argsp->subdir == NULL) 748*0Sstevel@tonic-gate return (B_TRUE); 749*0Sstevel@tonic-gate /* 750*0Sstevel@tonic-gate * We know what the claimed pathname *should* look like: 751*0Sstevel@tonic-gate * 752*0Sstevel@tonic-gate * If the parent (dfnp) is a mount point (VROOT), then 753*0Sstevel@tonic-gate * the path should be (dfnip->fi_path + m->dir). 754*0Sstevel@tonic-gate * 755*0Sstevel@tonic-gate * Else, we know we're only two levels deep, so we use 756*0Sstevel@tonic-gate * (dfnip->fi_path + dfnp->fn_name + m->dir). 757*0Sstevel@tonic-gate * 758*0Sstevel@tonic-gate * Furthermore, "." only makes sense if dfnp is a 759*0Sstevel@tonic-gate * trigger node. 760*0Sstevel@tonic-gate * 761*0Sstevel@tonic-gate * At this point it seems like the passed-in path is 762*0Sstevel@tonic-gate * redundant. 763*0Sstevel@tonic-gate */ 764*0Sstevel@tonic-gate if (dvp->v_flag & VROOT) { 765*0Sstevel@tonic-gate if (m->dir[1] == '\0' && !(dfnp->fn_flags & MF_TRIGGER)) 766*0Sstevel@tonic-gate return (B_TRUE); 767*0Sstevel@tonic-gate (void) snprintf(buff, sizeof (buff), "%s%s", 768*0Sstevel@tonic-gate dfnip->fi_path, m->dir + 1); 769*0Sstevel@tonic-gate } else { 770*0Sstevel@tonic-gate (void) snprintf(buff, sizeof (buff), "%s/%s%s", 771*0Sstevel@tonic-gate dfnip->fi_path, dfnp->fn_name, m->dir + 1); 772*0Sstevel@tonic-gate } 773*0Sstevel@tonic-gate if (strcmp(argsp->path, buff) != 0) { 774*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "autofs: expected path of '%s', " 775*0Sstevel@tonic-gate "got '%s' instead.", buff, argsp->path); 776*0Sstevel@tonic-gate return (B_TRUE); 777*0Sstevel@tonic-gate } 778*0Sstevel@tonic-gate return (B_FALSE); /* looks OK */ 779*0Sstevel@tonic-gate } 780*0Sstevel@tonic-gate 781*0Sstevel@tonic-gate static int 782*0Sstevel@tonic-gate auto_perform_actions( 783*0Sstevel@tonic-gate fninfo_t *dfnip, 784*0Sstevel@tonic-gate fnnode_t *dfnp, 785*0Sstevel@tonic-gate action_list *alp, 786*0Sstevel@tonic-gate cred_t *cred) /* Credentials of the caller */ 787*0Sstevel@tonic-gate { 788*0Sstevel@tonic-gate action_list *p; 789*0Sstevel@tonic-gate struct mounta *m, margs; 790*0Sstevel@tonic-gate struct autofs_args *argsp; 791*0Sstevel@tonic-gate int error, success = 0; 792*0Sstevel@tonic-gate vnode_t *mvp, *dvp, *newvp; 793*0Sstevel@tonic-gate fnnode_t *newfnp, *mfnp; 794*0Sstevel@tonic-gate int auto_mount = 0; 795*0Sstevel@tonic-gate int save_triggers = 0; /* set when we need to save at least */ 796*0Sstevel@tonic-gate /* one trigger node */ 797*0Sstevel@tonic-gate int update_times = 0; 798*0Sstevel@tonic-gate char *mntpnt; 799*0Sstevel@tonic-gate char buff[AUTOFS_MAXPATHLEN]; 800*0Sstevel@tonic-gate timestruc_t now; 801*0Sstevel@tonic-gate struct autofs_globals *fngp; 802*0Sstevel@tonic-gate cred_t *zcred; /* kcred-like credentials limited by our zone */ 803*0Sstevel@tonic-gate 804*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_perform_actions: alp=%p\n", (void *)alp)); 805*0Sstevel@tonic-gate 806*0Sstevel@tonic-gate fngp = dfnp->fn_globals; 807*0Sstevel@tonic-gate dvp = fntovn(dfnp); 808*0Sstevel@tonic-gate 809*0Sstevel@tonic-gate /* 810*0Sstevel@tonic-gate * As automountd running in a zone may be compromised, and this may be 811*0Sstevel@tonic-gate * an attack, we can't trust everything passed in by automountd, and we 812*0Sstevel@tonic-gate * need to do argument verification. We'll issue a warning and drop 813*0Sstevel@tonic-gate * the request if it doesn't seem right. 814*0Sstevel@tonic-gate */ 815*0Sstevel@tonic-gate for (p = alp; p != NULL; p = p->next) { 816*0Sstevel@tonic-gate if (auto_invalid_action(dfnip, dfnp, p)) { 817*0Sstevel@tonic-gate /* 818*0Sstevel@tonic-gate * This warning should be sent to the global zone, 819*0Sstevel@tonic-gate * since presumably the zone administrator is the same 820*0Sstevel@tonic-gate * as the attacker. 821*0Sstevel@tonic-gate */ 822*0Sstevel@tonic-gate cmn_err(CE_WARN, "autofs: invalid action list received " 823*0Sstevel@tonic-gate "by automountd in zone %s.", 824*0Sstevel@tonic-gate curproc->p_zone->zone_name); 825*0Sstevel@tonic-gate /* 826*0Sstevel@tonic-gate * This conversation is over. 827*0Sstevel@tonic-gate */ 828*0Sstevel@tonic-gate xdr_free(xdr_action_list, (char *)alp); 829*0Sstevel@tonic-gate return (EINVAL); 830*0Sstevel@tonic-gate } 831*0Sstevel@tonic-gate } 832*0Sstevel@tonic-gate 833*0Sstevel@tonic-gate zcred = zone_get_kcred(getzoneid()); 834*0Sstevel@tonic-gate ASSERT(zcred != NULL); 835*0Sstevel@tonic-gate 836*0Sstevel@tonic-gate if (vn_mountedvfs(dvp) != NULL) { 837*0Sstevel@tonic-gate /* 838*0Sstevel@tonic-gate * The daemon successfully mounted a filesystem 839*0Sstevel@tonic-gate * on the AUTOFS root node. 840*0Sstevel@tonic-gate */ 841*0Sstevel@tonic-gate mutex_enter(&dfnp->fn_lock); 842*0Sstevel@tonic-gate dfnp->fn_flags |= MF_MOUNTPOINT; 843*0Sstevel@tonic-gate ASSERT(dfnp->fn_dirents == NULL); 844*0Sstevel@tonic-gate mutex_exit(&dfnp->fn_lock); 845*0Sstevel@tonic-gate success++; 846*0Sstevel@tonic-gate } else { 847*0Sstevel@tonic-gate /* 848*0Sstevel@tonic-gate * Clear MF_MOUNTPOINT. 849*0Sstevel@tonic-gate */ 850*0Sstevel@tonic-gate mutex_enter(&dfnp->fn_lock); 851*0Sstevel@tonic-gate if (dfnp->fn_flags & MF_MOUNTPOINT) { 852*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "autofs: clearing mountpoint " 853*0Sstevel@tonic-gate "flag on %s.", dfnp->fn_name)); 854*0Sstevel@tonic-gate ASSERT(dfnp->fn_dirents == NULL); 855*0Sstevel@tonic-gate ASSERT(dfnp->fn_trigger == NULL); 856*0Sstevel@tonic-gate } 857*0Sstevel@tonic-gate dfnp->fn_flags &= ~MF_MOUNTPOINT; 858*0Sstevel@tonic-gate mutex_exit(&dfnp->fn_lock); 859*0Sstevel@tonic-gate } 860*0Sstevel@tonic-gate 861*0Sstevel@tonic-gate for (p = alp; p != NULL; p = p->next) { 862*0Sstevel@tonic-gate vfs_t *vfsp; /* dummy argument */ 863*0Sstevel@tonic-gate vfs_t *mvfsp; 864*0Sstevel@tonic-gate 865*0Sstevel@tonic-gate auto_mount = 0; 866*0Sstevel@tonic-gate 867*0Sstevel@tonic-gate m = &p->action.action_list_entry_u.mounta; 868*0Sstevel@tonic-gate argsp = (struct autofs_args *)m->dataptr; 869*0Sstevel@tonic-gate /* 870*0Sstevel@tonic-gate * use the parent directory's timeout since it's the 871*0Sstevel@tonic-gate * one specified/inherited by automount. 872*0Sstevel@tonic-gate */ 873*0Sstevel@tonic-gate argsp->mount_to = dfnip->fi_mount_to; 874*0Sstevel@tonic-gate /* 875*0Sstevel@tonic-gate * The mountpoint is relative, and it is guaranteed to 876*0Sstevel@tonic-gate * begin with "." 877*0Sstevel@tonic-gate * 878*0Sstevel@tonic-gate */ 879*0Sstevel@tonic-gate ASSERT(m->dir[0] == '.'); 880*0Sstevel@tonic-gate if (m->dir[0] == '.' && m->dir[1] == '\0') { 881*0Sstevel@tonic-gate /* 882*0Sstevel@tonic-gate * mounting on the trigger node 883*0Sstevel@tonic-gate */ 884*0Sstevel@tonic-gate mvp = dvp; 885*0Sstevel@tonic-gate VN_HOLD(mvp); 886*0Sstevel@tonic-gate goto mount; 887*0Sstevel@tonic-gate } 888*0Sstevel@tonic-gate /* 889*0Sstevel@tonic-gate * ignore "./" in front of mountpoint 890*0Sstevel@tonic-gate */ 891*0Sstevel@tonic-gate ASSERT(m->dir[1] == '/'); 892*0Sstevel@tonic-gate mntpnt = m->dir + 2; 893*0Sstevel@tonic-gate 894*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tdfnip->fi_path=%s\n", dfnip->fi_path)); 895*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tdfnip->fi_flags=%x\n", dfnip->fi_flags)); 896*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tmntpnt=%s\n", mntpnt)); 897*0Sstevel@tonic-gate 898*0Sstevel@tonic-gate if (dfnip->fi_flags & MF_DIRECT) { 899*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tDIRECT\n")); 900*0Sstevel@tonic-gate (void) sprintf(buff, "%s/%s", dfnip->fi_path, mntpnt); 901*0Sstevel@tonic-gate } else { 902*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tINDIRECT\n")); 903*0Sstevel@tonic-gate (void) sprintf(buff, "%s/%s/%s", dfnip->fi_path, 904*0Sstevel@tonic-gate dfnp->fn_name, mntpnt); 905*0Sstevel@tonic-gate } 906*0Sstevel@tonic-gate 907*0Sstevel@tonic-gate if (vn_mountedvfs(dvp) == NULL) { 908*0Sstevel@tonic-gate /* 909*0Sstevel@tonic-gate * Daemon didn't mount anything on the root 910*0Sstevel@tonic-gate * We have to create the mountpoint if it doesn't 911*0Sstevel@tonic-gate * exist already 912*0Sstevel@tonic-gate * 913*0Sstevel@tonic-gate * We use the caller's credentials in case a UID-match 914*0Sstevel@tonic-gate * is required (MF_THISUID_MATCH_RQD). 915*0Sstevel@tonic-gate */ 916*0Sstevel@tonic-gate rw_enter(&dfnp->fn_rwlock, RW_WRITER); 917*0Sstevel@tonic-gate error = auto_search(dfnp, mntpnt, &mfnp, cred); 918*0Sstevel@tonic-gate if (error == 0) { 919*0Sstevel@tonic-gate /* 920*0Sstevel@tonic-gate * AUTOFS mountpoint exists 921*0Sstevel@tonic-gate */ 922*0Sstevel@tonic-gate if (vn_mountedvfs(fntovn(mfnp)) != NULL) { 923*0Sstevel@tonic-gate cmn_err(CE_PANIC, 924*0Sstevel@tonic-gate "auto_perform_actions: " 925*0Sstevel@tonic-gate "mfnp=%p covered", (void *)mfnp); 926*0Sstevel@tonic-gate } 927*0Sstevel@tonic-gate } else { 928*0Sstevel@tonic-gate /* 929*0Sstevel@tonic-gate * Create AUTOFS mountpoint 930*0Sstevel@tonic-gate */ 931*0Sstevel@tonic-gate ASSERT((dfnp->fn_flags & MF_MOUNTPOINT) == 0); 932*0Sstevel@tonic-gate error = auto_enter(dfnp, mntpnt, &mfnp, cred); 933*0Sstevel@tonic-gate ASSERT(mfnp->fn_linkcnt == 1); 934*0Sstevel@tonic-gate mfnp->fn_linkcnt++; 935*0Sstevel@tonic-gate } 936*0Sstevel@tonic-gate if (!error) 937*0Sstevel@tonic-gate update_times = 1; 938*0Sstevel@tonic-gate rw_exit(&dfnp->fn_rwlock); 939*0Sstevel@tonic-gate ASSERT(error != EEXIST); 940*0Sstevel@tonic-gate if (!error) { 941*0Sstevel@tonic-gate /* 942*0Sstevel@tonic-gate * mfnp is already held. 943*0Sstevel@tonic-gate */ 944*0Sstevel@tonic-gate mvp = fntovn(mfnp); 945*0Sstevel@tonic-gate } else { 946*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "autofs: mount of %s " 947*0Sstevel@tonic-gate "failed - can't create mountpoint.", buff); 948*0Sstevel@tonic-gate continue; 949*0Sstevel@tonic-gate } 950*0Sstevel@tonic-gate } else { 951*0Sstevel@tonic-gate /* 952*0Sstevel@tonic-gate * Find mountpoint in VFS mounted here. If not found, 953*0Sstevel@tonic-gate * fail the submount, though the overall mount has 954*0Sstevel@tonic-gate * succeeded since the root is mounted. 955*0Sstevel@tonic-gate */ 956*0Sstevel@tonic-gate if (error = auto_getmntpnt(dvp, mntpnt, &mvp, kcred)) { 957*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "autofs: mount of %s " 958*0Sstevel@tonic-gate "failed - mountpoint doesn't exist.", buff); 959*0Sstevel@tonic-gate continue; 960*0Sstevel@tonic-gate } 961*0Sstevel@tonic-gate if (mvp->v_type == VLNK) { 962*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "autofs: %s symbolic " 963*0Sstevel@tonic-gate "link: not a valid mountpoint " 964*0Sstevel@tonic-gate "- mount failed", buff); 965*0Sstevel@tonic-gate VN_RELE(mvp); 966*0Sstevel@tonic-gate error = ENOENT; 967*0Sstevel@tonic-gate continue; 968*0Sstevel@tonic-gate } 969*0Sstevel@tonic-gate } 970*0Sstevel@tonic-gate mount: 971*0Sstevel@tonic-gate m->flags |= MS_SYSSPACE | MS_OPTIONSTR; 972*0Sstevel@tonic-gate /* 973*0Sstevel@tonic-gate * Copy mounta struct here so we can substitute a buffer 974*0Sstevel@tonic-gate * that is large enough to hold the returned option string, 975*0Sstevel@tonic-gate * if that string is longer that the input option string. 976*0Sstevel@tonic-gate * This can happen if there are default options enabled 977*0Sstevel@tonic-gate * that were not in the input option string. 978*0Sstevel@tonic-gate */ 979*0Sstevel@tonic-gate bcopy(m, &margs, sizeof (*m)); 980*0Sstevel@tonic-gate margs.optptr = kmem_alloc(MAX_MNTOPT_STR, KM_SLEEP); 981*0Sstevel@tonic-gate margs.optlen = MAX_MNTOPT_STR; 982*0Sstevel@tonic-gate (void) strcpy(margs.optptr, m->optptr); 983*0Sstevel@tonic-gate margs.dir = argsp->path; 984*0Sstevel@tonic-gate /* 985*0Sstevel@tonic-gate * We use the zone's kcred because we don't want the zone to be 986*0Sstevel@tonic-gate * able to thus do something it wouldn't normally be able to. 987*0Sstevel@tonic-gate */ 988*0Sstevel@tonic-gate error = domount(NULL, &margs, mvp, zcred, &vfsp); 989*0Sstevel@tonic-gate kmem_free(margs.optptr, MAX_MNTOPT_STR); 990*0Sstevel@tonic-gate if (error != 0) { 991*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, 992*0Sstevel@tonic-gate "autofs: domount of %s failed error=%d", 993*0Sstevel@tonic-gate buff, error); 994*0Sstevel@tonic-gate VN_RELE(mvp); 995*0Sstevel@tonic-gate continue; 996*0Sstevel@tonic-gate } 997*0Sstevel@tonic-gate VFS_RELE(vfsp); 998*0Sstevel@tonic-gate 999*0Sstevel@tonic-gate /* 1000*0Sstevel@tonic-gate * If mountpoint is an AUTOFS node, then I'm going to 1001*0Sstevel@tonic-gate * flag it that the Filesystem mounted on top was mounted 1002*0Sstevel@tonic-gate * in the kernel so that the unmount can be done inside the 1003*0Sstevel@tonic-gate * kernel as well. 1004*0Sstevel@tonic-gate * I don't care to flag non-AUTOFS mountpoints when an AUTOFS 1005*0Sstevel@tonic-gate * in-kernel mount was done on top, because the unmount 1006*0Sstevel@tonic-gate * routine already knows that such case was done in the kernel. 1007*0Sstevel@tonic-gate */ 1008*0Sstevel@tonic-gate if (vfs_matchops(dvp->v_vfsp, vfs_getops(mvp->v_vfsp))) { 1009*0Sstevel@tonic-gate mfnp = vntofn(mvp); 1010*0Sstevel@tonic-gate mutex_enter(&mfnp->fn_lock); 1011*0Sstevel@tonic-gate mfnp->fn_flags |= MF_IK_MOUNT; 1012*0Sstevel@tonic-gate mutex_exit(&mfnp->fn_lock); 1013*0Sstevel@tonic-gate } 1014*0Sstevel@tonic-gate 1015*0Sstevel@tonic-gate (void) vn_vfswlock_wait(mvp); 1016*0Sstevel@tonic-gate mvfsp = vn_mountedvfs(mvp); 1017*0Sstevel@tonic-gate if (mvfsp != NULL) { 1018*0Sstevel@tonic-gate vfs_lock_wait(mvfsp); 1019*0Sstevel@tonic-gate vn_vfsunlock(mvp); 1020*0Sstevel@tonic-gate error = VFS_ROOT(mvfsp, &newvp); 1021*0Sstevel@tonic-gate vfs_unlock(mvfsp); 1022*0Sstevel@tonic-gate if (error) { 1023*0Sstevel@tonic-gate /* 1024*0Sstevel@tonic-gate * We've dropped the locks, so let's get 1025*0Sstevel@tonic-gate * the mounted vfs again in case it changed. 1026*0Sstevel@tonic-gate */ 1027*0Sstevel@tonic-gate (void) vn_vfswlock_wait(mvp); 1028*0Sstevel@tonic-gate mvfsp = vn_mountedvfs(mvp); 1029*0Sstevel@tonic-gate if (mvfsp != NULL) { 1030*0Sstevel@tonic-gate error = dounmount(mvfsp, 0, CRED()); 1031*0Sstevel@tonic-gate if (error) { 1032*0Sstevel@tonic-gate cmn_err(CE_WARN, 1033*0Sstevel@tonic-gate "autofs: could not " 1034*0Sstevel@tonic-gate "unmount vfs=%p", 1035*0Sstevel@tonic-gate (void *)mvfsp); 1036*0Sstevel@tonic-gate } 1037*0Sstevel@tonic-gate } else 1038*0Sstevel@tonic-gate vn_vfsunlock(mvp); 1039*0Sstevel@tonic-gate VN_RELE(mvp); 1040*0Sstevel@tonic-gate continue; 1041*0Sstevel@tonic-gate } 1042*0Sstevel@tonic-gate } else { 1043*0Sstevel@tonic-gate vn_vfsunlock(mvp); 1044*0Sstevel@tonic-gate VN_RELE(mvp); 1045*0Sstevel@tonic-gate continue; 1046*0Sstevel@tonic-gate } 1047*0Sstevel@tonic-gate 1048*0Sstevel@tonic-gate auto_mount = vfs_matchops(dvp->v_vfsp, 1049*0Sstevel@tonic-gate vfs_getops(newvp->v_vfsp)); 1050*0Sstevel@tonic-gate newfnp = vntofn(newvp); 1051*0Sstevel@tonic-gate newfnp->fn_parent = dfnp; 1052*0Sstevel@tonic-gate 1053*0Sstevel@tonic-gate /* 1054*0Sstevel@tonic-gate * At this time we want to save the AUTOFS filesystem as 1055*0Sstevel@tonic-gate * a trigger node. (We only do this if the mount occured 1056*0Sstevel@tonic-gate * on a node different from the root. 1057*0Sstevel@tonic-gate * We look at the trigger nodes during 1058*0Sstevel@tonic-gate * the automatic unmounting to make sure we remove them 1059*0Sstevel@tonic-gate * as a unit and remount them as a unit if the filesystem 1060*0Sstevel@tonic-gate * mounted at the root could not be unmounted. 1061*0Sstevel@tonic-gate */ 1062*0Sstevel@tonic-gate if (auto_mount && (error == 0) && (mvp != dvp)) { 1063*0Sstevel@tonic-gate save_triggers++; 1064*0Sstevel@tonic-gate /* 1065*0Sstevel@tonic-gate * Add AUTOFS mount to hierarchy 1066*0Sstevel@tonic-gate */ 1067*0Sstevel@tonic-gate newfnp->fn_flags |= MF_TRIGGER; 1068*0Sstevel@tonic-gate rw_enter(&newfnp->fn_rwlock, RW_WRITER); 1069*0Sstevel@tonic-gate newfnp->fn_next = dfnp->fn_trigger; 1070*0Sstevel@tonic-gate rw_exit(&newfnp->fn_rwlock); 1071*0Sstevel@tonic-gate rw_enter(&dfnp->fn_rwlock, RW_WRITER); 1072*0Sstevel@tonic-gate dfnp->fn_trigger = newfnp; 1073*0Sstevel@tonic-gate rw_exit(&dfnp->fn_rwlock); 1074*0Sstevel@tonic-gate /* 1075*0Sstevel@tonic-gate * Don't VN_RELE(newvp) here since dfnp now holds 1076*0Sstevel@tonic-gate * reference to it as its trigger node. 1077*0Sstevel@tonic-gate */ 1078*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tadding trigger %s to %s\n", 1079*0Sstevel@tonic-gate newfnp->fn_name, dfnp->fn_name)); 1080*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tfirst trigger is %s\n", 1081*0Sstevel@tonic-gate dfnp->fn_trigger->fn_name)); 1082*0Sstevel@tonic-gate if (newfnp->fn_next != NULL) 1083*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tnext trigger is %s\n", 1084*0Sstevel@tonic-gate newfnp->fn_next->fn_name)); 1085*0Sstevel@tonic-gate else 1086*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tno next trigger\n")); 1087*0Sstevel@tonic-gate } else 1088*0Sstevel@tonic-gate VN_RELE(newvp); 1089*0Sstevel@tonic-gate 1090*0Sstevel@tonic-gate if (!error) 1091*0Sstevel@tonic-gate success++; 1092*0Sstevel@tonic-gate 1093*0Sstevel@tonic-gate if (update_times) { 1094*0Sstevel@tonic-gate gethrestime(&now); 1095*0Sstevel@tonic-gate dfnp->fn_atime = dfnp->fn_mtime = now; 1096*0Sstevel@tonic-gate } 1097*0Sstevel@tonic-gate 1098*0Sstevel@tonic-gate VN_RELE(mvp); 1099*0Sstevel@tonic-gate } 1100*0Sstevel@tonic-gate 1101*0Sstevel@tonic-gate if (save_triggers) { 1102*0Sstevel@tonic-gate /* 1103*0Sstevel@tonic-gate * Make sure the parent can't be freed while it has triggers. 1104*0Sstevel@tonic-gate */ 1105*0Sstevel@tonic-gate VN_HOLD(dvp); 1106*0Sstevel@tonic-gate } 1107*0Sstevel@tonic-gate 1108*0Sstevel@tonic-gate crfree(zcred); 1109*0Sstevel@tonic-gate 1110*0Sstevel@tonic-gate done: 1111*0Sstevel@tonic-gate /* 1112*0Sstevel@tonic-gate * Return failure if daemon didn't mount anything, and all 1113*0Sstevel@tonic-gate * kernel mounts attempted failed. 1114*0Sstevel@tonic-gate */ 1115*0Sstevel@tonic-gate error = success ? 0 : ENOENT; 1116*0Sstevel@tonic-gate 1117*0Sstevel@tonic-gate if (alp != NULL) { 1118*0Sstevel@tonic-gate if ((error == 0) && save_triggers) { 1119*0Sstevel@tonic-gate /* 1120*0Sstevel@tonic-gate * Save action_list information, so that we can use it 1121*0Sstevel@tonic-gate * when it comes time to remount the trigger nodes 1122*0Sstevel@tonic-gate * The action list is freed when the directory node 1123*0Sstevel@tonic-gate * containing the reference to it is unmounted in 1124*0Sstevel@tonic-gate * unmount_tree(). 1125*0Sstevel@tonic-gate */ 1126*0Sstevel@tonic-gate mutex_enter(&dfnp->fn_lock); 1127*0Sstevel@tonic-gate ASSERT(dfnp->fn_alp == NULL); 1128*0Sstevel@tonic-gate dfnp->fn_alp = alp; 1129*0Sstevel@tonic-gate mutex_exit(&dfnp->fn_lock); 1130*0Sstevel@tonic-gate } else { 1131*0Sstevel@tonic-gate /* 1132*0Sstevel@tonic-gate * free the action list now, 1133*0Sstevel@tonic-gate */ 1134*0Sstevel@tonic-gate xdr_free(xdr_action_list, (char *)alp); 1135*0Sstevel@tonic-gate } 1136*0Sstevel@tonic-gate } 1137*0Sstevel@tonic-gate 1138*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_perform_actions: error=%d\n", error)); 1139*0Sstevel@tonic-gate return (error); 1140*0Sstevel@tonic-gate } 1141*0Sstevel@tonic-gate 1142*0Sstevel@tonic-gate fnnode_t * 1143*0Sstevel@tonic-gate auto_makefnnode( 1144*0Sstevel@tonic-gate vtype_t type, 1145*0Sstevel@tonic-gate vfs_t *vfsp, 1146*0Sstevel@tonic-gate char *name, 1147*0Sstevel@tonic-gate cred_t *cred, 1148*0Sstevel@tonic-gate struct autofs_globals *fngp) 1149*0Sstevel@tonic-gate { 1150*0Sstevel@tonic-gate fnnode_t *fnp; 1151*0Sstevel@tonic-gate vnode_t *vp; 1152*0Sstevel@tonic-gate char *tmpname; 1153*0Sstevel@tonic-gate timestruc_t now; 1154*0Sstevel@tonic-gate /* 1155*0Sstevel@tonic-gate * autofs uses odd inode numbers 1156*0Sstevel@tonic-gate * automountd uses even inode numbers 1157*0Sstevel@tonic-gate * 1158*0Sstevel@tonic-gate * To preserve the age-old semantics that inum+devid is unique across 1159*0Sstevel@tonic-gate * the system, this variable must be global across zones. 1160*0Sstevel@tonic-gate */ 1161*0Sstevel@tonic-gate static ino_t nodeid = 3; 1162*0Sstevel@tonic-gate 1163*0Sstevel@tonic-gate fnp = kmem_zalloc(sizeof (*fnp), KM_SLEEP); 1164*0Sstevel@tonic-gate fnp->fn_vnode = vn_alloc(KM_SLEEP); 1165*0Sstevel@tonic-gate 1166*0Sstevel@tonic-gate vp = fntovn(fnp); 1167*0Sstevel@tonic-gate tmpname = kmem_alloc(strlen(name) + 1, KM_SLEEP); 1168*0Sstevel@tonic-gate (void) strcpy(tmpname, name); 1169*0Sstevel@tonic-gate fnp->fn_name = &tmpname[0]; 1170*0Sstevel@tonic-gate fnp->fn_namelen = (int)strlen(tmpname) + 1; /* include '\0' */ 1171*0Sstevel@tonic-gate fnp->fn_uid = crgetuid(cred); 1172*0Sstevel@tonic-gate fnp->fn_gid = crgetgid(cred); 1173*0Sstevel@tonic-gate /* 1174*0Sstevel@tonic-gate * ".." is added in auto_enter and auto_mount. 1175*0Sstevel@tonic-gate * "." is added in auto_mkdir and auto_mount. 1176*0Sstevel@tonic-gate */ 1177*0Sstevel@tonic-gate /* 1178*0Sstevel@tonic-gate * Note that fn_size and fn_linkcnt are already 0 since 1179*0Sstevel@tonic-gate * we used kmem_zalloc to allocated fnp 1180*0Sstevel@tonic-gate */ 1181*0Sstevel@tonic-gate fnp->fn_mode = AUTOFS_MODE; 1182*0Sstevel@tonic-gate gethrestime(&now); 1183*0Sstevel@tonic-gate fnp->fn_atime = fnp->fn_mtime = fnp->fn_ctime = now; 1184*0Sstevel@tonic-gate fnp->fn_ref_time = now.tv_sec; 1185*0Sstevel@tonic-gate mutex_enter(&autofs_nodeid_lock); 1186*0Sstevel@tonic-gate fnp->fn_nodeid = nodeid; 1187*0Sstevel@tonic-gate nodeid += 2; 1188*0Sstevel@tonic-gate fnp->fn_globals = fngp; 1189*0Sstevel@tonic-gate fngp->fng_fnnode_count++; 1190*0Sstevel@tonic-gate mutex_exit(&autofs_nodeid_lock); 1191*0Sstevel@tonic-gate vn_setops(vp, auto_vnodeops); 1192*0Sstevel@tonic-gate vp->v_type = type; 1193*0Sstevel@tonic-gate vp->v_data = (void *)fnp; 1194*0Sstevel@tonic-gate vp->v_vfsp = vfsp; 1195*0Sstevel@tonic-gate mutex_init(&fnp->fn_lock, NULL, MUTEX_DEFAULT, NULL); 1196*0Sstevel@tonic-gate rw_init(&fnp->fn_rwlock, NULL, RW_DEFAULT, NULL); 1197*0Sstevel@tonic-gate cv_init(&fnp->fn_cv_mount, NULL, CV_DEFAULT, NULL); 1198*0Sstevel@tonic-gate vn_exists(vp); 1199*0Sstevel@tonic-gate return (fnp); 1200*0Sstevel@tonic-gate } 1201*0Sstevel@tonic-gate 1202*0Sstevel@tonic-gate 1203*0Sstevel@tonic-gate void 1204*0Sstevel@tonic-gate auto_freefnnode(fnnode_t *fnp) 1205*0Sstevel@tonic-gate { 1206*0Sstevel@tonic-gate vnode_t *vp = fntovn(fnp); 1207*0Sstevel@tonic-gate 1208*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_freefnnode: fnp=%p\n", (void *)fnp)); 1209*0Sstevel@tonic-gate 1210*0Sstevel@tonic-gate ASSERT(fnp->fn_linkcnt == 0); 1211*0Sstevel@tonic-gate ASSERT(vp->v_count == 0); 1212*0Sstevel@tonic-gate ASSERT(fnp->fn_dirents == NULL); 1213*0Sstevel@tonic-gate ASSERT(fnp->fn_parent == NULL); 1214*0Sstevel@tonic-gate 1215*0Sstevel@tonic-gate vn_invalid(vp); 1216*0Sstevel@tonic-gate kmem_free(fnp->fn_name, fnp->fn_namelen); 1217*0Sstevel@tonic-gate if (fnp->fn_symlink) { 1218*0Sstevel@tonic-gate ASSERT(fnp->fn_flags & MF_THISUID_MATCH_RQD); 1219*0Sstevel@tonic-gate kmem_free(fnp->fn_symlink, fnp->fn_symlinklen); 1220*0Sstevel@tonic-gate } 1221*0Sstevel@tonic-gate if (fnp->fn_cred) 1222*0Sstevel@tonic-gate crfree(fnp->fn_cred); 1223*0Sstevel@tonic-gate mutex_destroy(&fnp->fn_lock); 1224*0Sstevel@tonic-gate rw_destroy(&fnp->fn_rwlock); 1225*0Sstevel@tonic-gate cv_destroy(&fnp->fn_cv_mount); 1226*0Sstevel@tonic-gate vn_free(vp); 1227*0Sstevel@tonic-gate 1228*0Sstevel@tonic-gate mutex_enter(&autofs_nodeid_lock); 1229*0Sstevel@tonic-gate fnp->fn_globals->fng_fnnode_count--; 1230*0Sstevel@tonic-gate mutex_exit(&autofs_nodeid_lock); 1231*0Sstevel@tonic-gate kmem_free(fnp, sizeof (*fnp)); 1232*0Sstevel@tonic-gate } 1233*0Sstevel@tonic-gate 1234*0Sstevel@tonic-gate void 1235*0Sstevel@tonic-gate auto_disconnect( 1236*0Sstevel@tonic-gate fnnode_t *dfnp, 1237*0Sstevel@tonic-gate fnnode_t *fnp) 1238*0Sstevel@tonic-gate { 1239*0Sstevel@tonic-gate fnnode_t *tmp, **fnpp; 1240*0Sstevel@tonic-gate vnode_t *vp = fntovn(fnp); 1241*0Sstevel@tonic-gate timestruc_t now; 1242*0Sstevel@tonic-gate 1243*0Sstevel@tonic-gate AUTOFS_DPRINT((4, 1244*0Sstevel@tonic-gate "auto_disconnect: dfnp=%p fnp=%p linkcnt=%d\n v_count=%d", 1245*0Sstevel@tonic-gate (void *)dfnp, (void *)fnp, fnp->fn_linkcnt, vp->v_count)); 1246*0Sstevel@tonic-gate 1247*0Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock)); 1248*0Sstevel@tonic-gate ASSERT(fnp->fn_linkcnt == 1); 1249*0Sstevel@tonic-gate 1250*0Sstevel@tonic-gate if (vn_mountedvfs(vp) != NULL) { 1251*0Sstevel@tonic-gate cmn_err(CE_PANIC, "auto_disconnect: vp %p mounted on", 1252*0Sstevel@tonic-gate (void *)vp); 1253*0Sstevel@tonic-gate } 1254*0Sstevel@tonic-gate 1255*0Sstevel@tonic-gate /* 1256*0Sstevel@tonic-gate * Decrement by 1 because we're removing the entry in dfnp. 1257*0Sstevel@tonic-gate */ 1258*0Sstevel@tonic-gate fnp->fn_linkcnt--; 1259*0Sstevel@tonic-gate fnp->fn_size--; 1260*0Sstevel@tonic-gate 1261*0Sstevel@tonic-gate /* 1262*0Sstevel@tonic-gate * only changed while holding parent's (dfnp) rw_lock 1263*0Sstevel@tonic-gate */ 1264*0Sstevel@tonic-gate fnp->fn_parent = NULL; 1265*0Sstevel@tonic-gate 1266*0Sstevel@tonic-gate fnpp = &dfnp->fn_dirents; 1267*0Sstevel@tonic-gate for (;;) { 1268*0Sstevel@tonic-gate tmp = *fnpp; 1269*0Sstevel@tonic-gate if (tmp == NULL) { 1270*0Sstevel@tonic-gate cmn_err(CE_PANIC, 1271*0Sstevel@tonic-gate "auto_disconnect: %p not in %p dirent list", 1272*0Sstevel@tonic-gate (void *)fnp, (void *)dfnp); 1273*0Sstevel@tonic-gate } 1274*0Sstevel@tonic-gate if (tmp == fnp) { 1275*0Sstevel@tonic-gate *fnpp = tmp->fn_next; /* remove it from the list */ 1276*0Sstevel@tonic-gate ASSERT(vp->v_count == 0); 1277*0Sstevel@tonic-gate /* child had a pointer to parent ".." */ 1278*0Sstevel@tonic-gate dfnp->fn_linkcnt--; 1279*0Sstevel@tonic-gate dfnp->fn_size--; 1280*0Sstevel@tonic-gate break; 1281*0Sstevel@tonic-gate } 1282*0Sstevel@tonic-gate fnpp = &tmp->fn_next; 1283*0Sstevel@tonic-gate } 1284*0Sstevel@tonic-gate 1285*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 1286*0Sstevel@tonic-gate gethrestime(&now); 1287*0Sstevel@tonic-gate fnp->fn_atime = fnp->fn_mtime = now; 1288*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 1289*0Sstevel@tonic-gate 1290*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_disconnect: done\n")); 1291*0Sstevel@tonic-gate } 1292*0Sstevel@tonic-gate 1293*0Sstevel@tonic-gate int 1294*0Sstevel@tonic-gate auto_enter(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred) 1295*0Sstevel@tonic-gate { 1296*0Sstevel@tonic-gate struct fnnode *cfnp, **spp; 1297*0Sstevel@tonic-gate vnode_t *dvp = fntovn(dfnp); 1298*0Sstevel@tonic-gate ushort_t offset = 0; 1299*0Sstevel@tonic-gate ushort_t diff; 1300*0Sstevel@tonic-gate 1301*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_enter: dfnp=%p, name=%s ", (void *)dfnp, name)); 1302*0Sstevel@tonic-gate 1303*0Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock)); 1304*0Sstevel@tonic-gate 1305*0Sstevel@tonic-gate cfnp = dfnp->fn_dirents; 1306*0Sstevel@tonic-gate if (cfnp == NULL) { 1307*0Sstevel@tonic-gate /* 1308*0Sstevel@tonic-gate * offset = 0 for '.' and offset = 1 for '..' 1309*0Sstevel@tonic-gate */ 1310*0Sstevel@tonic-gate spp = &dfnp->fn_dirents; 1311*0Sstevel@tonic-gate offset = 2; 1312*0Sstevel@tonic-gate } 1313*0Sstevel@tonic-gate 1314*0Sstevel@tonic-gate for (; cfnp; cfnp = cfnp->fn_next) { 1315*0Sstevel@tonic-gate if (strcmp(cfnp->fn_name, name) == 0) { 1316*0Sstevel@tonic-gate mutex_enter(&cfnp->fn_lock); 1317*0Sstevel@tonic-gate if (cfnp->fn_flags & MF_THISUID_MATCH_RQD) { 1318*0Sstevel@tonic-gate /* 1319*0Sstevel@tonic-gate * "thisuser" kind of node, need to 1320*0Sstevel@tonic-gate * match CREDs as well 1321*0Sstevel@tonic-gate */ 1322*0Sstevel@tonic-gate mutex_exit(&cfnp->fn_lock); 1323*0Sstevel@tonic-gate if (crcmp(cfnp->fn_cred, cred) == 0) 1324*0Sstevel@tonic-gate return (EEXIST); 1325*0Sstevel@tonic-gate } else { 1326*0Sstevel@tonic-gate mutex_exit(&cfnp->fn_lock); 1327*0Sstevel@tonic-gate return (EEXIST); 1328*0Sstevel@tonic-gate } 1329*0Sstevel@tonic-gate } 1330*0Sstevel@tonic-gate 1331*0Sstevel@tonic-gate if (cfnp->fn_next != NULL) { 1332*0Sstevel@tonic-gate diff = (ushort_t) 1333*0Sstevel@tonic-gate (cfnp->fn_next->fn_offset - cfnp->fn_offset); 1334*0Sstevel@tonic-gate ASSERT(diff != 0); 1335*0Sstevel@tonic-gate if (diff > 1 && offset == 0) { 1336*0Sstevel@tonic-gate offset = (ushort_t)cfnp->fn_offset + 1; 1337*0Sstevel@tonic-gate spp = &cfnp->fn_next; 1338*0Sstevel@tonic-gate } 1339*0Sstevel@tonic-gate } else if (offset == 0) { 1340*0Sstevel@tonic-gate offset = (ushort_t)cfnp->fn_offset + 1; 1341*0Sstevel@tonic-gate spp = &cfnp->fn_next; 1342*0Sstevel@tonic-gate } 1343*0Sstevel@tonic-gate } 1344*0Sstevel@tonic-gate 1345*0Sstevel@tonic-gate *fnpp = auto_makefnnode(VDIR, dvp->v_vfsp, name, cred, 1346*0Sstevel@tonic-gate dfnp->fn_globals); 1347*0Sstevel@tonic-gate if (*fnpp == NULL) 1348*0Sstevel@tonic-gate return (ENOMEM); 1349*0Sstevel@tonic-gate 1350*0Sstevel@tonic-gate /* 1351*0Sstevel@tonic-gate * I don't hold the mutex on fnpp because I created it, and 1352*0Sstevel@tonic-gate * I'm already holding the writers lock for it's parent 1353*0Sstevel@tonic-gate * directory, therefore nobody can reference it without me first 1354*0Sstevel@tonic-gate * releasing the writers lock. 1355*0Sstevel@tonic-gate */ 1356*0Sstevel@tonic-gate (*fnpp)->fn_offset = offset; 1357*0Sstevel@tonic-gate (*fnpp)->fn_next = *spp; 1358*0Sstevel@tonic-gate *spp = *fnpp; 1359*0Sstevel@tonic-gate (*fnpp)->fn_parent = dfnp; 1360*0Sstevel@tonic-gate (*fnpp)->fn_linkcnt++; /* parent now holds reference to entry */ 1361*0Sstevel@tonic-gate (*fnpp)->fn_size++; 1362*0Sstevel@tonic-gate 1363*0Sstevel@tonic-gate /* 1364*0Sstevel@tonic-gate * dfnp->fn_linkcnt and dfnp->fn_size protected by dfnp->rw_lock 1365*0Sstevel@tonic-gate */ 1366*0Sstevel@tonic-gate dfnp->fn_linkcnt++; /* child now holds reference to parent '..' */ 1367*0Sstevel@tonic-gate dfnp->fn_size++; 1368*0Sstevel@tonic-gate 1369*0Sstevel@tonic-gate dfnp->fn_ref_time = gethrestime_sec(); 1370*0Sstevel@tonic-gate 1371*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "*fnpp=%p\n", (void *)*fnpp)); 1372*0Sstevel@tonic-gate return (0); 1373*0Sstevel@tonic-gate } 1374*0Sstevel@tonic-gate 1375*0Sstevel@tonic-gate int 1376*0Sstevel@tonic-gate auto_search(fnnode_t *dfnp, char *name, fnnode_t **fnpp, cred_t *cred) 1377*0Sstevel@tonic-gate { 1378*0Sstevel@tonic-gate vnode_t *dvp; 1379*0Sstevel@tonic-gate fnnode_t *p; 1380*0Sstevel@tonic-gate int error = ENOENT, match = 0; 1381*0Sstevel@tonic-gate 1382*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_search: dfnp=%p, name=%s...\n", 1383*0Sstevel@tonic-gate (void *)dfnp, name)); 1384*0Sstevel@tonic-gate 1385*0Sstevel@tonic-gate dvp = fntovn(dfnp); 1386*0Sstevel@tonic-gate if (dvp->v_type != VDIR) { 1387*0Sstevel@tonic-gate cmn_err(CE_PANIC, "auto_search: dvp=%p not a directory", 1388*0Sstevel@tonic-gate (void *)dvp); 1389*0Sstevel@tonic-gate } 1390*0Sstevel@tonic-gate 1391*0Sstevel@tonic-gate ASSERT(RW_LOCK_HELD(&dfnp->fn_rwlock)); 1392*0Sstevel@tonic-gate for (p = dfnp->fn_dirents; p != NULL; p = p->fn_next) { 1393*0Sstevel@tonic-gate if (strcmp(p->fn_name, name) == 0) { 1394*0Sstevel@tonic-gate mutex_enter(&p->fn_lock); 1395*0Sstevel@tonic-gate if (p->fn_flags & MF_THISUID_MATCH_RQD) { 1396*0Sstevel@tonic-gate /* 1397*0Sstevel@tonic-gate * "thisuser" kind of node 1398*0Sstevel@tonic-gate * Need to match CREDs as well 1399*0Sstevel@tonic-gate */ 1400*0Sstevel@tonic-gate mutex_exit(&p->fn_lock); 1401*0Sstevel@tonic-gate match = crcmp(p->fn_cred, cred) == 0; 1402*0Sstevel@tonic-gate } else { 1403*0Sstevel@tonic-gate /* 1404*0Sstevel@tonic-gate * No need to check CRED 1405*0Sstevel@tonic-gate */ 1406*0Sstevel@tonic-gate mutex_exit(&p->fn_lock); 1407*0Sstevel@tonic-gate match = 1; 1408*0Sstevel@tonic-gate } 1409*0Sstevel@tonic-gate } 1410*0Sstevel@tonic-gate if (match) { 1411*0Sstevel@tonic-gate error = 0; 1412*0Sstevel@tonic-gate if (fnpp) { 1413*0Sstevel@tonic-gate *fnpp = p; 1414*0Sstevel@tonic-gate VN_HOLD(fntovn(*fnpp)); 1415*0Sstevel@tonic-gate } 1416*0Sstevel@tonic-gate break; 1417*0Sstevel@tonic-gate } 1418*0Sstevel@tonic-gate } 1419*0Sstevel@tonic-gate 1420*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_search: error=%d\n", error)); 1421*0Sstevel@tonic-gate return (error); 1422*0Sstevel@tonic-gate } 1423*0Sstevel@tonic-gate 1424*0Sstevel@tonic-gate /* 1425*0Sstevel@tonic-gate * If dvp is mounted on, get path's vnode in the mounted on 1426*0Sstevel@tonic-gate * filesystem. Path is relative to dvp, ie "./path". 1427*0Sstevel@tonic-gate * If successful, *mvp points to a the held mountpoint vnode. 1428*0Sstevel@tonic-gate */ 1429*0Sstevel@tonic-gate /* ARGSUSED */ 1430*0Sstevel@tonic-gate static int 1431*0Sstevel@tonic-gate auto_getmntpnt( 1432*0Sstevel@tonic-gate vnode_t *dvp, 1433*0Sstevel@tonic-gate char *path, 1434*0Sstevel@tonic-gate vnode_t **mvpp, /* vnode for mountpoint */ 1435*0Sstevel@tonic-gate cred_t *cred) 1436*0Sstevel@tonic-gate { 1437*0Sstevel@tonic-gate int error = 0; 1438*0Sstevel@tonic-gate vnode_t *newvp; 1439*0Sstevel@tonic-gate char namebuf[TYPICALMAXPATHLEN]; 1440*0Sstevel@tonic-gate struct pathname lookpn; 1441*0Sstevel@tonic-gate vfs_t *vfsp; 1442*0Sstevel@tonic-gate 1443*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "auto_getmntpnt: path=%s\n", path)); 1444*0Sstevel@tonic-gate 1445*0Sstevel@tonic-gate if (error = vn_vfswlock_wait(dvp)) 1446*0Sstevel@tonic-gate return (error); 1447*0Sstevel@tonic-gate 1448*0Sstevel@tonic-gate /* 1449*0Sstevel@tonic-gate * Now that we have the vfswlock, check to see if dvp 1450*0Sstevel@tonic-gate * is still mounted on. If not, then just bail out as 1451*0Sstevel@tonic-gate * there is no need to remount the triggers since the 1452*0Sstevel@tonic-gate * higher level mount point has gotten unmounted. 1453*0Sstevel@tonic-gate */ 1454*0Sstevel@tonic-gate vfsp = vn_mountedvfs(dvp); 1455*0Sstevel@tonic-gate if (vfsp == NULL) { 1456*0Sstevel@tonic-gate vn_vfsunlock(dvp); 1457*0Sstevel@tonic-gate error = EBUSY; 1458*0Sstevel@tonic-gate goto done; 1459*0Sstevel@tonic-gate } 1460*0Sstevel@tonic-gate /* 1461*0Sstevel@tonic-gate * Since mounted on, lookup "path" in the new filesystem, 1462*0Sstevel@tonic-gate * it is important that we do the filesystem jump here to 1463*0Sstevel@tonic-gate * avoid lookuppn() calling auto_lookup on dvp and deadlock. 1464*0Sstevel@tonic-gate */ 1465*0Sstevel@tonic-gate vfs_lock_wait(vfsp); 1466*0Sstevel@tonic-gate vn_vfsunlock(dvp); 1467*0Sstevel@tonic-gate error = VFS_ROOT(vfsp, &newvp); 1468*0Sstevel@tonic-gate vfs_unlock(vfsp); 1469*0Sstevel@tonic-gate if (error) 1470*0Sstevel@tonic-gate goto done; 1471*0Sstevel@tonic-gate 1472*0Sstevel@tonic-gate /* 1473*0Sstevel@tonic-gate * We do a VN_HOLD on newvp just in case the first call to 1474*0Sstevel@tonic-gate * lookuppnvp() fails with ENAMETOOLONG. We should still have a 1475*0Sstevel@tonic-gate * reference to this vnode for the second call to lookuppnvp(). 1476*0Sstevel@tonic-gate */ 1477*0Sstevel@tonic-gate VN_HOLD(newvp); 1478*0Sstevel@tonic-gate 1479*0Sstevel@tonic-gate /* 1480*0Sstevel@tonic-gate * Now create the pathname struct so we can make use of lookuppnvp, 1481*0Sstevel@tonic-gate * and pn_getcomponent. 1482*0Sstevel@tonic-gate * This code is similar to lookupname() in fs/lookup.c. 1483*0Sstevel@tonic-gate */ 1484*0Sstevel@tonic-gate error = pn_get_buf(path, UIO_SYSSPACE, &lookpn, 1485*0Sstevel@tonic-gate namebuf, sizeof (namebuf)); 1486*0Sstevel@tonic-gate if (error == 0) { 1487*0Sstevel@tonic-gate error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP, 1488*0Sstevel@tonic-gate mvpp, rootdir, newvp, cred); 1489*0Sstevel@tonic-gate } else 1490*0Sstevel@tonic-gate VN_RELE(newvp); 1491*0Sstevel@tonic-gate if (error == ENAMETOOLONG) { 1492*0Sstevel@tonic-gate /* 1493*0Sstevel@tonic-gate * This thread used a pathname > TYPICALMAXPATHLEN bytes long. 1494*0Sstevel@tonic-gate * newvp is VN_RELE'd by this call to lookuppnvp. 1495*0Sstevel@tonic-gate * 1496*0Sstevel@tonic-gate * Using 'rootdir' in a zone's context is OK here: we already 1497*0Sstevel@tonic-gate * ascertained that there are no '..'s in the path, and we're 1498*0Sstevel@tonic-gate * not following symlinks. 1499*0Sstevel@tonic-gate */ 1500*0Sstevel@tonic-gate if ((error = pn_get(path, UIO_SYSSPACE, &lookpn)) == 0) { 1501*0Sstevel@tonic-gate error = lookuppnvp(&lookpn, NULL, NO_FOLLOW, NULLVPP, 1502*0Sstevel@tonic-gate mvpp, rootdir, newvp, cred); 1503*0Sstevel@tonic-gate pn_free(&lookpn); 1504*0Sstevel@tonic-gate } else 1505*0Sstevel@tonic-gate VN_RELE(newvp); 1506*0Sstevel@tonic-gate } else { 1507*0Sstevel@tonic-gate /* 1508*0Sstevel@tonic-gate * Need to release newvp here since we held it. 1509*0Sstevel@tonic-gate */ 1510*0Sstevel@tonic-gate VN_RELE(newvp); 1511*0Sstevel@tonic-gate } 1512*0Sstevel@tonic-gate 1513*0Sstevel@tonic-gate done: 1514*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_getmntpnt: path=%s *mvpp=%p error=%d\n", 1515*0Sstevel@tonic-gate path, (void *)*mvpp, error)); 1516*0Sstevel@tonic-gate return (error); 1517*0Sstevel@tonic-gate } 1518*0Sstevel@tonic-gate 1519*0Sstevel@tonic-gate #define DEEPER(x) (((x)->fn_dirents != NULL) || \ 1520*0Sstevel@tonic-gate (vn_mountedvfs(fntovn((x)))) != NULL) 1521*0Sstevel@tonic-gate 1522*0Sstevel@tonic-gate /* 1523*0Sstevel@tonic-gate * The caller, should have already VN_RELE'd its reference to the 1524*0Sstevel@tonic-gate * root vnode of this filesystem. 1525*0Sstevel@tonic-gate */ 1526*0Sstevel@tonic-gate static int 1527*0Sstevel@tonic-gate auto_inkernel_unmount(vfs_t *vfsp) 1528*0Sstevel@tonic-gate { 1529*0Sstevel@tonic-gate vnode_t *cvp = vfsp->vfs_vnodecovered; 1530*0Sstevel@tonic-gate int error; 1531*0Sstevel@tonic-gate 1532*0Sstevel@tonic-gate AUTOFS_DPRINT((4, 1533*0Sstevel@tonic-gate "auto_inkernel_unmount: devid=%lx mntpnt(%p) count %u\n", 1534*0Sstevel@tonic-gate vfsp->vfs_dev, (void *)cvp, cvp->v_count)); 1535*0Sstevel@tonic-gate 1536*0Sstevel@tonic-gate ASSERT(vn_vfswlock_held(cvp)); 1537*0Sstevel@tonic-gate 1538*0Sstevel@tonic-gate /* 1539*0Sstevel@tonic-gate * Perform the unmount 1540*0Sstevel@tonic-gate * The mountpoint has already been locked by the caller. 1541*0Sstevel@tonic-gate */ 1542*0Sstevel@tonic-gate error = dounmount(vfsp, 0, kcred); 1543*0Sstevel@tonic-gate 1544*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "auto_inkernel_unmount: exit count %u\n", 1545*0Sstevel@tonic-gate cvp->v_count)); 1546*0Sstevel@tonic-gate return (error); 1547*0Sstevel@tonic-gate } 1548*0Sstevel@tonic-gate 1549*0Sstevel@tonic-gate /* 1550*0Sstevel@tonic-gate * unmounts trigger nodes in the kernel. 1551*0Sstevel@tonic-gate */ 1552*0Sstevel@tonic-gate static void 1553*0Sstevel@tonic-gate unmount_triggers(fnnode_t *fnp, action_list **alp) 1554*0Sstevel@tonic-gate { 1555*0Sstevel@tonic-gate fnnode_t *tp, *next; 1556*0Sstevel@tonic-gate int error = 0; 1557*0Sstevel@tonic-gate vfs_t *vfsp; 1558*0Sstevel@tonic-gate vnode_t *tvp; 1559*0Sstevel@tonic-gate 1560*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "unmount_triggers: fnp=%p\n", (void *)fnp)); 1561*0Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock)); 1562*0Sstevel@tonic-gate 1563*0Sstevel@tonic-gate *alp = fnp->fn_alp; 1564*0Sstevel@tonic-gate next = fnp->fn_trigger; 1565*0Sstevel@tonic-gate while ((tp = next) != NULL) { 1566*0Sstevel@tonic-gate tvp = fntovn(tp); 1567*0Sstevel@tonic-gate ASSERT(tvp->v_count >= 2); 1568*0Sstevel@tonic-gate next = tp->fn_next; 1569*0Sstevel@tonic-gate /* 1570*0Sstevel@tonic-gate * drop writer's lock since the unmount will end up 1571*0Sstevel@tonic-gate * disconnecting this node from fnp and needs to acquire 1572*0Sstevel@tonic-gate * the writer's lock again. 1573*0Sstevel@tonic-gate * next has at least a reference count >= 2 since it's 1574*0Sstevel@tonic-gate * a trigger node, therefore can not be accidentally freed 1575*0Sstevel@tonic-gate * by a VN_RELE 1576*0Sstevel@tonic-gate */ 1577*0Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock); 1578*0Sstevel@tonic-gate 1579*0Sstevel@tonic-gate vfsp = tvp->v_vfsp; 1580*0Sstevel@tonic-gate 1581*0Sstevel@tonic-gate /* 1582*0Sstevel@tonic-gate * Its parent was holding a reference to it, since this 1583*0Sstevel@tonic-gate * is a trigger vnode. 1584*0Sstevel@tonic-gate */ 1585*0Sstevel@tonic-gate VN_RELE(tvp); 1586*0Sstevel@tonic-gate if (error = auto_inkernel_unmount(vfsp)) { 1587*0Sstevel@tonic-gate cmn_err(CE_PANIC, "unmount_triggers: " 1588*0Sstevel@tonic-gate "unmount of vp=%p failed error=%d", 1589*0Sstevel@tonic-gate (void *)tvp, error); 1590*0Sstevel@tonic-gate } 1591*0Sstevel@tonic-gate /* 1592*0Sstevel@tonic-gate * reacquire writer's lock 1593*0Sstevel@tonic-gate */ 1594*0Sstevel@tonic-gate rw_enter(&fnp->fn_rwlock, RW_WRITER); 1595*0Sstevel@tonic-gate } 1596*0Sstevel@tonic-gate 1597*0Sstevel@tonic-gate /* 1598*0Sstevel@tonic-gate * We were holding a reference to our parent. Drop that. 1599*0Sstevel@tonic-gate */ 1600*0Sstevel@tonic-gate VN_RELE(fntovn(fnp)); 1601*0Sstevel@tonic-gate fnp->fn_trigger = NULL; 1602*0Sstevel@tonic-gate fnp->fn_alp = NULL; 1603*0Sstevel@tonic-gate 1604*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "unmount_triggers: finished\n")); 1605*0Sstevel@tonic-gate } 1606*0Sstevel@tonic-gate 1607*0Sstevel@tonic-gate /* 1608*0Sstevel@tonic-gate * This routine locks the mountpoint of every trigger node if they're 1609*0Sstevel@tonic-gate * not busy, or returns EBUSY if any node is busy. If a trigger node should 1610*0Sstevel@tonic-gate * be unmounted first, then it sets nfnp to point to it, otherwise nfnp 1611*0Sstevel@tonic-gate * points to NULL. 1612*0Sstevel@tonic-gate */ 1613*0Sstevel@tonic-gate static int 1614*0Sstevel@tonic-gate triggers_busy(fnnode_t *fnp, fnnode_t **nfnp) 1615*0Sstevel@tonic-gate { 1616*0Sstevel@tonic-gate int error = 0, done; 1617*0Sstevel@tonic-gate int lck_error = 0; 1618*0Sstevel@tonic-gate fnnode_t *tp, *t1p; 1619*0Sstevel@tonic-gate vfs_t *vfsp; 1620*0Sstevel@tonic-gate 1621*0Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock)); 1622*0Sstevel@tonic-gate 1623*0Sstevel@tonic-gate *nfnp = NULL; 1624*0Sstevel@tonic-gate for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) { 1625*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\ttrigger: %s\n", tp->fn_name)); 1626*0Sstevel@tonic-gate vfsp = fntovn(tp)->v_vfsp; 1627*0Sstevel@tonic-gate error = 0; 1628*0Sstevel@tonic-gate /* 1629*0Sstevel@tonic-gate * The vn_vfsunlock will be done in auto_inkernel_unmount. 1630*0Sstevel@tonic-gate */ 1631*0Sstevel@tonic-gate lck_error = vn_vfswlock(vfsp->vfs_vnodecovered); 1632*0Sstevel@tonic-gate if (lck_error == 0) { 1633*0Sstevel@tonic-gate mutex_enter(&tp->fn_lock); 1634*0Sstevel@tonic-gate ASSERT((tp->fn_flags & MF_LOOKUP) == 0); 1635*0Sstevel@tonic-gate if (tp->fn_flags & MF_INPROG) { 1636*0Sstevel@tonic-gate /* 1637*0Sstevel@tonic-gate * a mount is in progress 1638*0Sstevel@tonic-gate */ 1639*0Sstevel@tonic-gate error = EBUSY; 1640*0Sstevel@tonic-gate } 1641*0Sstevel@tonic-gate mutex_exit(&tp->fn_lock); 1642*0Sstevel@tonic-gate } 1643*0Sstevel@tonic-gate if (lck_error || error || DEEPER(tp) || 1644*0Sstevel@tonic-gate ((fntovn(tp))->v_count) > 2) { 1645*0Sstevel@tonic-gate /* 1646*0Sstevel@tonic-gate * couldn't lock it because it's busy, 1647*0Sstevel@tonic-gate * It is mounted on or has dirents? 1648*0Sstevel@tonic-gate * If reference count is greater than two, then 1649*0Sstevel@tonic-gate * somebody else is holding a reference to this vnode. 1650*0Sstevel@tonic-gate * One reference is for the mountpoint, and the second 1651*0Sstevel@tonic-gate * is for the trigger node. 1652*0Sstevel@tonic-gate */ 1653*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\ttrigger busy\n")); 1654*0Sstevel@tonic-gate if ((lck_error == 0) && (error == 0)) { 1655*0Sstevel@tonic-gate *nfnp = tp; 1656*0Sstevel@tonic-gate /* 1657*0Sstevel@tonic-gate * The matching VN_RELE is done in 1658*0Sstevel@tonic-gate * unmount_tree(). 1659*0Sstevel@tonic-gate */ 1660*0Sstevel@tonic-gate VN_HOLD(fntovn(*nfnp)); 1661*0Sstevel@tonic-gate } 1662*0Sstevel@tonic-gate /* 1663*0Sstevel@tonic-gate * Unlock previously locked mountpoints 1664*0Sstevel@tonic-gate */ 1665*0Sstevel@tonic-gate for (done = 0, t1p = fnp->fn_trigger; !done; 1666*0Sstevel@tonic-gate t1p = t1p->fn_next) { 1667*0Sstevel@tonic-gate /* 1668*0Sstevel@tonic-gate * Unlock all nodes previously 1669*0Sstevel@tonic-gate * locked. All nodes up to 'tp' 1670*0Sstevel@tonic-gate * were successfully locked. If 'lck_err' is 1671*0Sstevel@tonic-gate * set, then 'tp' was not locked, and thus 1672*0Sstevel@tonic-gate * should not be unlocked. If 1673*0Sstevel@tonic-gate * 'lck_err' is not set, then 'tp' was 1674*0Sstevel@tonic-gate * successfully locked, and it should 1675*0Sstevel@tonic-gate * be unlocked. 1676*0Sstevel@tonic-gate */ 1677*0Sstevel@tonic-gate if (t1p != tp || !lck_error) { 1678*0Sstevel@tonic-gate vfsp = fntovn(t1p)->v_vfsp; 1679*0Sstevel@tonic-gate vn_vfsunlock(vfsp->vfs_vnodecovered); 1680*0Sstevel@tonic-gate } 1681*0Sstevel@tonic-gate done = (t1p == tp); 1682*0Sstevel@tonic-gate } 1683*0Sstevel@tonic-gate error = EBUSY; 1684*0Sstevel@tonic-gate break; 1685*0Sstevel@tonic-gate } 1686*0Sstevel@tonic-gate } 1687*0Sstevel@tonic-gate 1688*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "triggers_busy: error=%d\n", error)); 1689*0Sstevel@tonic-gate return (error); 1690*0Sstevel@tonic-gate } 1691*0Sstevel@tonic-gate 1692*0Sstevel@tonic-gate /* 1693*0Sstevel@tonic-gate * Unlock previously locked trigger nodes. 1694*0Sstevel@tonic-gate */ 1695*0Sstevel@tonic-gate static int 1696*0Sstevel@tonic-gate triggers_unlock(fnnode_t *fnp) 1697*0Sstevel@tonic-gate { 1698*0Sstevel@tonic-gate fnnode_t *tp; 1699*0Sstevel@tonic-gate vfs_t *vfsp; 1700*0Sstevel@tonic-gate 1701*0Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&fnp->fn_rwlock)); 1702*0Sstevel@tonic-gate 1703*0Sstevel@tonic-gate for (tp = fnp->fn_trigger; tp != NULL; tp = tp->fn_next) { 1704*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tunlock trigger: %s\n", tp->fn_name)); 1705*0Sstevel@tonic-gate vfsp = fntovn(tp)->v_vfsp; 1706*0Sstevel@tonic-gate vn_vfsunlock(vfsp->vfs_vnodecovered); 1707*0Sstevel@tonic-gate } 1708*0Sstevel@tonic-gate 1709*0Sstevel@tonic-gate return (0); 1710*0Sstevel@tonic-gate } 1711*0Sstevel@tonic-gate 1712*0Sstevel@tonic-gate /* 1713*0Sstevel@tonic-gate * It is the caller's responsibility to grab the VVFSLOCK. 1714*0Sstevel@tonic-gate * Releases the VVFSLOCK upon return. 1715*0Sstevel@tonic-gate */ 1716*0Sstevel@tonic-gate static int 1717*0Sstevel@tonic-gate unmount_node(vnode_t *cvp, int force) 1718*0Sstevel@tonic-gate { 1719*0Sstevel@tonic-gate int error = 0; 1720*0Sstevel@tonic-gate fnnode_t *cfnp; 1721*0Sstevel@tonic-gate vfs_t *vfsp; 1722*0Sstevel@tonic-gate umntrequest ul; 1723*0Sstevel@tonic-gate fninfo_t *fnip; 1724*0Sstevel@tonic-gate 1725*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tunmount_node cvp=%p\n", (void *)cvp)); 1726*0Sstevel@tonic-gate 1727*0Sstevel@tonic-gate ASSERT(vn_vfswlock_held(cvp)); 1728*0Sstevel@tonic-gate cfnp = vntofn(cvp); 1729*0Sstevel@tonic-gate vfsp = vn_mountedvfs(cvp); 1730*0Sstevel@tonic-gate 1731*0Sstevel@tonic-gate if (force || cfnp->fn_flags & MF_IK_MOUNT) { 1732*0Sstevel@tonic-gate /* 1733*0Sstevel@tonic-gate * Mount was performed in the kernel, so 1734*0Sstevel@tonic-gate * do an in-kernel unmount. auto_inkernel_unmount() 1735*0Sstevel@tonic-gate * will vn_vfsunlock(cvp). 1736*0Sstevel@tonic-gate */ 1737*0Sstevel@tonic-gate error = auto_inkernel_unmount(vfsp); 1738*0Sstevel@tonic-gate } else { 1739*0Sstevel@tonic-gate zone_t *zone = NULL; 1740*0Sstevel@tonic-gate refstr_t *mntpt, *resource; 1741*0Sstevel@tonic-gate size_t mntoptslen; 1742*0Sstevel@tonic-gate 1743*0Sstevel@tonic-gate /* 1744*0Sstevel@tonic-gate * Get the mnttab information of the node 1745*0Sstevel@tonic-gate * and ask the daemon to unmount it. 1746*0Sstevel@tonic-gate */ 1747*0Sstevel@tonic-gate bzero(&ul, sizeof (ul)); 1748*0Sstevel@tonic-gate mntfs_getmntopts(vfsp, &ul.mntopts, &mntoptslen); 1749*0Sstevel@tonic-gate if (ul.mntopts == NULL) { 1750*0Sstevel@tonic-gate auto_log(cfnp->fn_globals, CE_WARN, "unmount_node: " 1751*0Sstevel@tonic-gate "no memory"); 1752*0Sstevel@tonic-gate vn_vfsunlock(cvp); 1753*0Sstevel@tonic-gate error = ENOMEM; 1754*0Sstevel@tonic-gate goto done; 1755*0Sstevel@tonic-gate } 1756*0Sstevel@tonic-gate if (mntoptslen > AUTOFS_MAXOPTSLEN) 1757*0Sstevel@tonic-gate ul.mntopts[AUTOFS_MAXOPTSLEN - 1] = '\0'; 1758*0Sstevel@tonic-gate 1759*0Sstevel@tonic-gate mntpt = vfs_getmntpoint(vfsp); 1760*0Sstevel@tonic-gate ul.mntpnt = (char *)refstr_value(mntpt); 1761*0Sstevel@tonic-gate resource = vfs_getresource(vfsp); 1762*0Sstevel@tonic-gate ul.mntresource = (char *)refstr_value(resource); 1763*0Sstevel@tonic-gate 1764*0Sstevel@tonic-gate fnip = vfstofni(cvp->v_vfsp); 1765*0Sstevel@tonic-gate ul.isdirect = fnip->fi_flags & MF_DIRECT ? TRUE : FALSE; 1766*0Sstevel@tonic-gate 1767*0Sstevel@tonic-gate /* 1768*0Sstevel@tonic-gate * Since a zone'd automountd's view of the autofs mount points 1769*0Sstevel@tonic-gate * differs from those in the kernel, we need to make sure we 1770*0Sstevel@tonic-gate * give it consistent mount points. 1771*0Sstevel@tonic-gate */ 1772*0Sstevel@tonic-gate ASSERT(fnip->fi_zoneid == getzoneid()); 1773*0Sstevel@tonic-gate zone = curproc->p_zone; 1774*0Sstevel@tonic-gate 1775*0Sstevel@tonic-gate if (fnip->fi_zoneid != GLOBAL_ZONEID) { 1776*0Sstevel@tonic-gate if (ZONE_PATH_VISIBLE(ul.mntpnt, zone)) { 1777*0Sstevel@tonic-gate ul.mntpnt = 1778*0Sstevel@tonic-gate ZONE_PATH_TRANSLATE(ul.mntpnt, zone); 1779*0Sstevel@tonic-gate } 1780*0Sstevel@tonic-gate if (ZONE_PATH_VISIBLE(ul.mntresource, zone)) { 1781*0Sstevel@tonic-gate ul.mntresource = 1782*0Sstevel@tonic-gate ZONE_PATH_TRANSLATE(ul.mntresource, zone); 1783*0Sstevel@tonic-gate } 1784*0Sstevel@tonic-gate } 1785*0Sstevel@tonic-gate ul.fstype = vfssw[vfsp->vfs_fstype].vsw_name; 1786*0Sstevel@tonic-gate vn_vfsunlock(cvp); 1787*0Sstevel@tonic-gate 1788*0Sstevel@tonic-gate error = auto_send_unmount_request(fnip, &ul, CRED(), FALSE); 1789*0Sstevel@tonic-gate kmem_free(ul.mntopts, mntoptslen); 1790*0Sstevel@tonic-gate refstr_rele(mntpt); 1791*0Sstevel@tonic-gate refstr_rele(resource); 1792*0Sstevel@tonic-gate } 1793*0Sstevel@tonic-gate 1794*0Sstevel@tonic-gate done: 1795*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tunmount_node cvp=%p error=%d\n", (void *)cvp, 1796*0Sstevel@tonic-gate error)); 1797*0Sstevel@tonic-gate return (error); 1798*0Sstevel@tonic-gate } 1799*0Sstevel@tonic-gate 1800*0Sstevel@tonic-gate /* 1801*0Sstevel@tonic-gate * vp is the "root" of the AUTOFS filesystem. 1802*0Sstevel@tonic-gate * return EBUSY if any thread is holding a reference to this vnode 1803*0Sstevel@tonic-gate * other than us. 1804*0Sstevel@tonic-gate */ 1805*0Sstevel@tonic-gate static int 1806*0Sstevel@tonic-gate check_auto_node(vnode_t *vp) 1807*0Sstevel@tonic-gate { 1808*0Sstevel@tonic-gate fnnode_t *fnp; 1809*0Sstevel@tonic-gate int error = 0; 1810*0Sstevel@tonic-gate /* 1811*0Sstevel@tonic-gate * number of references to expect for 1812*0Sstevel@tonic-gate * a non-busy vnode. 1813*0Sstevel@tonic-gate */ 1814*0Sstevel@tonic-gate uint_t count; 1815*0Sstevel@tonic-gate 1816*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tcheck_auto_node vp=%p ", (void *)vp)); 1817*0Sstevel@tonic-gate fnp = vntofn(vp); 1818*0Sstevel@tonic-gate ASSERT(fnp->fn_flags & MF_INPROG); 1819*0Sstevel@tonic-gate ASSERT((fnp->fn_flags & MF_LOOKUP) == 0); 1820*0Sstevel@tonic-gate 1821*0Sstevel@tonic-gate count = 1; /* we are holding a reference to vp */ 1822*0Sstevel@tonic-gate if (fnp->fn_flags & MF_TRIGGER) { 1823*0Sstevel@tonic-gate /* 1824*0Sstevel@tonic-gate * parent holds a pointer to us (trigger) 1825*0Sstevel@tonic-gate */ 1826*0Sstevel@tonic-gate count++; 1827*0Sstevel@tonic-gate } 1828*0Sstevel@tonic-gate if (fnp->fn_trigger != NULL) { 1829*0Sstevel@tonic-gate /* 1830*0Sstevel@tonic-gate * The trigger nodes have a hold on us. 1831*0Sstevel@tonic-gate */ 1832*0Sstevel@tonic-gate count++; 1833*0Sstevel@tonic-gate } 1834*0Sstevel@tonic-gate mutex_enter(&vp->v_lock); 1835*0Sstevel@tonic-gate if (vp->v_flag & VROOT) 1836*0Sstevel@tonic-gate count++; 1837*0Sstevel@tonic-gate ASSERT(vp->v_count > 0); 1838*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tcount=%u ", vp->v_count)); 1839*0Sstevel@tonic-gate if (vp->v_count > count) 1840*0Sstevel@tonic-gate error = EBUSY; 1841*0Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1842*0Sstevel@tonic-gate 1843*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tcheck_auto_node error=%d ", error)); 1844*0Sstevel@tonic-gate return (error); 1845*0Sstevel@tonic-gate } 1846*0Sstevel@tonic-gate 1847*0Sstevel@tonic-gate /* 1848*0Sstevel@tonic-gate * rootvp is the root of the AUTOFS filesystem. 1849*0Sstevel@tonic-gate * If rootvp is busy (v_count > 1) returns EBUSY. 1850*0Sstevel@tonic-gate * else removes every vnode under this tree. 1851*0Sstevel@tonic-gate * ASSUMPTION: Assumes that the only node which can be busy is 1852*0Sstevel@tonic-gate * the root vnode. This filesystem better be two levels deep only, 1853*0Sstevel@tonic-gate * the root and its immediate subdirs. 1854*0Sstevel@tonic-gate * The daemon will "AUTOFS direct-mount" only one level below the root. 1855*0Sstevel@tonic-gate */ 1856*0Sstevel@tonic-gate static int 1857*0Sstevel@tonic-gate unmount_autofs(vnode_t *rootvp) 1858*0Sstevel@tonic-gate { 1859*0Sstevel@tonic-gate fnnode_t *fnp, *rootfnp, *nfnp; 1860*0Sstevel@tonic-gate int error; 1861*0Sstevel@tonic-gate 1862*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "\tunmount_autofs rootvp=%p ", (void *)rootvp)); 1863*0Sstevel@tonic-gate 1864*0Sstevel@tonic-gate error = check_auto_node(rootvp); 1865*0Sstevel@tonic-gate if (error == 0) { 1866*0Sstevel@tonic-gate /* 1867*0Sstevel@tonic-gate * Remove all its immediate subdirectories. 1868*0Sstevel@tonic-gate */ 1869*0Sstevel@tonic-gate rootfnp = vntofn(rootvp); 1870*0Sstevel@tonic-gate rw_enter(&rootfnp->fn_rwlock, RW_WRITER); 1871*0Sstevel@tonic-gate nfnp = NULL; /* lint clean */ 1872*0Sstevel@tonic-gate for (fnp = rootfnp->fn_dirents; fnp != NULL; fnp = nfnp) { 1873*0Sstevel@tonic-gate ASSERT(fntovn(fnp)->v_count == 0); 1874*0Sstevel@tonic-gate ASSERT(fnp->fn_dirents == NULL); 1875*0Sstevel@tonic-gate ASSERT(fnp->fn_linkcnt == 2); 1876*0Sstevel@tonic-gate fnp->fn_linkcnt--; 1877*0Sstevel@tonic-gate auto_disconnect(rootfnp, fnp); 1878*0Sstevel@tonic-gate nfnp = fnp->fn_next; 1879*0Sstevel@tonic-gate auto_freefnnode(fnp); 1880*0Sstevel@tonic-gate } 1881*0Sstevel@tonic-gate rw_exit(&rootfnp->fn_rwlock); 1882*0Sstevel@tonic-gate } 1883*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "\tunmount_autofs error=%d ", error)); 1884*0Sstevel@tonic-gate return (error); 1885*0Sstevel@tonic-gate } 1886*0Sstevel@tonic-gate 1887*0Sstevel@tonic-gate /* 1888*0Sstevel@tonic-gate * max number of unmount threads running 1889*0Sstevel@tonic-gate */ 1890*0Sstevel@tonic-gate static int autofs_unmount_threads = 5; 1891*0Sstevel@tonic-gate 1892*0Sstevel@tonic-gate /* 1893*0Sstevel@tonic-gate * XXX unmount_tree() is not suspend-safe within the scope of 1894*0Sstevel@tonic-gate * the present model defined for cpr to suspend the system. Calls made 1895*0Sstevel@tonic-gate * by the unmount_tree() that have been identified to be unsafe are 1896*0Sstevel@tonic-gate * (1) RPC client handle setup and client calls to automountd which can 1897*0Sstevel@tonic-gate * block deep down in the RPC library, (2) kmem_alloc() calls with the 1898*0Sstevel@tonic-gate * KM_SLEEP flag which can block if memory is low, and (3) VFS_*() and 1899*0Sstevel@tonic-gate * VOP_*() calls which can result in over the wire calls to servers. 1900*0Sstevel@tonic-gate * The thread should be completely reevaluated to make it suspend-safe in 1901*0Sstevel@tonic-gate * case of future updates to the cpr model. 1902*0Sstevel@tonic-gate */ 1903*0Sstevel@tonic-gate void 1904*0Sstevel@tonic-gate unmount_tree(struct autofs_globals *fngp, int force) 1905*0Sstevel@tonic-gate { 1906*0Sstevel@tonic-gate vnode_t *vp, *newvp; 1907*0Sstevel@tonic-gate vfs_t *vfsp; 1908*0Sstevel@tonic-gate fnnode_t *fnp, *nfnp, *pfnp; 1909*0Sstevel@tonic-gate action_list *alp; 1910*0Sstevel@tonic-gate int error, ilocked_it = 0; 1911*0Sstevel@tonic-gate fninfo_t *fnip; 1912*0Sstevel@tonic-gate time_t ref_time; 1913*0Sstevel@tonic-gate int autofs_busy_root, unmount_as_unit, unmount_done = 0; 1914*0Sstevel@tonic-gate timestruc_t now; 1915*0Sstevel@tonic-gate 1916*0Sstevel@tonic-gate callb_cpr_t cprinfo; 1917*0Sstevel@tonic-gate kmutex_t unmount_tree_cpr_lock; 1918*0Sstevel@tonic-gate 1919*0Sstevel@tonic-gate mutex_init(&unmount_tree_cpr_lock, NULL, MUTEX_DEFAULT, NULL); 1920*0Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &unmount_tree_cpr_lock, callb_generic_cpr, 1921*0Sstevel@tonic-gate "unmount_tree"); 1922*0Sstevel@tonic-gate 1923*0Sstevel@tonic-gate /* 1924*0Sstevel@tonic-gate * Got to release lock before attempting unmount in case 1925*0Sstevel@tonic-gate * it hangs. 1926*0Sstevel@tonic-gate */ 1927*0Sstevel@tonic-gate rw_enter(&fngp->fng_rootfnnodep->fn_rwlock, RW_READER); 1928*0Sstevel@tonic-gate if ((fnp = fngp->fng_rootfnnodep->fn_dirents) == NULL) { 1929*0Sstevel@tonic-gate ASSERT(fngp->fng_fnnode_count == 1); 1930*0Sstevel@tonic-gate /* 1931*0Sstevel@tonic-gate * no autofs mounted, done. 1932*0Sstevel@tonic-gate */ 1933*0Sstevel@tonic-gate rw_exit(&fngp->fng_rootfnnodep->fn_rwlock); 1934*0Sstevel@tonic-gate goto done; 1935*0Sstevel@tonic-gate } 1936*0Sstevel@tonic-gate VN_HOLD(fntovn(fnp)); 1937*0Sstevel@tonic-gate rw_exit(&fngp->fng_rootfnnodep->fn_rwlock); 1938*0Sstevel@tonic-gate 1939*0Sstevel@tonic-gate vp = fntovn(fnp); 1940*0Sstevel@tonic-gate fnip = vfstofni(vp->v_vfsp); 1941*0Sstevel@tonic-gate /* 1942*0Sstevel@tonic-gate * autofssys() will be calling in from the global zone and doing 1943*0Sstevel@tonic-gate * work on the behalf of the given zone, hence we can't always assert 1944*0Sstevel@tonic-gate * that we have the right credentials, nor that the caller is always in 1945*0Sstevel@tonic-gate * the correct zone. 1946*0Sstevel@tonic-gate * 1947*0Sstevel@tonic-gate * We do, however, know that if this is a "forced unmount" operation 1948*0Sstevel@tonic-gate * (which autofssys() does), then we won't go down to the krpc layers, 1949*0Sstevel@tonic-gate * so we don't need to fudge with the credentials. 1950*0Sstevel@tonic-gate */ 1951*0Sstevel@tonic-gate ASSERT(force || fnip->fi_zoneid == getzoneid()); 1952*0Sstevel@tonic-gate if (!force && auto_null_request(fnip, kcred, FALSE) != 0) { 1953*0Sstevel@tonic-gate /* 1954*0Sstevel@tonic-gate * automountd not running in this zone, 1955*0Sstevel@tonic-gate * don't attempt unmounting this round. 1956*0Sstevel@tonic-gate */ 1957*0Sstevel@tonic-gate VN_RELE(vp); 1958*0Sstevel@tonic-gate goto done; 1959*0Sstevel@tonic-gate } 1960*0Sstevel@tonic-gate /* reference time for this unmount round */ 1961*0Sstevel@tonic-gate ref_time = gethrestime_sec(); 1962*0Sstevel@tonic-gate /* 1963*0Sstevel@tonic-gate * If this an autofssys() call, we need to make sure we don't skip 1964*0Sstevel@tonic-gate * nodes because we think we saw them recently. 1965*0Sstevel@tonic-gate */ 1966*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 1967*0Sstevel@tonic-gate if (force && fnp->fn_unmount_ref_time >= ref_time) 1968*0Sstevel@tonic-gate ref_time = fnp->fn_unmount_ref_time + 1; 1969*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 1970*0Sstevel@tonic-gate 1971*0Sstevel@tonic-gate AUTOFS_DPRINT((4, "unmount_tree (ID=%ld)\n", ref_time)); 1972*0Sstevel@tonic-gate top: 1973*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "unmount_tree: %s\n", fnp->fn_name)); 1974*0Sstevel@tonic-gate ASSERT(fnp); 1975*0Sstevel@tonic-gate vp = fntovn(fnp); 1976*0Sstevel@tonic-gate if (vp->v_type == VLNK) { 1977*0Sstevel@tonic-gate /* 1978*0Sstevel@tonic-gate * can't unmount symbolic links 1979*0Sstevel@tonic-gate */ 1980*0Sstevel@tonic-gate goto next; 1981*0Sstevel@tonic-gate } 1982*0Sstevel@tonic-gate fnip = vfstofni(vp->v_vfsp); 1983*0Sstevel@tonic-gate ASSERT(vp->v_count > 0); 1984*0Sstevel@tonic-gate error = 0; 1985*0Sstevel@tonic-gate autofs_busy_root = unmount_as_unit = 0; 1986*0Sstevel@tonic-gate alp = NULL; 1987*0Sstevel@tonic-gate 1988*0Sstevel@tonic-gate ilocked_it = 0; 1989*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 1990*0Sstevel@tonic-gate if (fnp->fn_flags & (MF_INPROG | MF_LOOKUP)) { 1991*0Sstevel@tonic-gate /* 1992*0Sstevel@tonic-gate * Either a mount, lookup or another unmount of this 1993*0Sstevel@tonic-gate * subtree is in progress, don't attempt to unmount at 1994*0Sstevel@tonic-gate * this time. 1995*0Sstevel@tonic-gate */ 1996*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 1997*0Sstevel@tonic-gate error = EBUSY; 1998*0Sstevel@tonic-gate goto next; 1999*0Sstevel@tonic-gate } 2000*0Sstevel@tonic-gate if (fnp->fn_unmount_ref_time >= ref_time) { 2001*0Sstevel@tonic-gate /* 2002*0Sstevel@tonic-gate * Already been here, try next node. 2003*0Sstevel@tonic-gate */ 2004*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 2005*0Sstevel@tonic-gate error = EBUSY; 2006*0Sstevel@tonic-gate goto next; 2007*0Sstevel@tonic-gate } 2008*0Sstevel@tonic-gate fnp->fn_unmount_ref_time = ref_time; 2009*0Sstevel@tonic-gate 2010*0Sstevel@tonic-gate /* 2011*0Sstevel@tonic-gate * If forced operation ignore timeout values 2012*0Sstevel@tonic-gate */ 2013*0Sstevel@tonic-gate if (!force && fnp->fn_ref_time + fnip->fi_mount_to > 2014*0Sstevel@tonic-gate gethrestime_sec()) { 2015*0Sstevel@tonic-gate /* 2016*0Sstevel@tonic-gate * Node has been referenced recently, try the 2017*0Sstevel@tonic-gate * unmount of its children if any. 2018*0Sstevel@tonic-gate */ 2019*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 2020*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "fn_ref_time within range\n")); 2021*0Sstevel@tonic-gate rw_enter(&fnp->fn_rwlock, RW_READER); 2022*0Sstevel@tonic-gate if (fnp->fn_dirents) { 2023*0Sstevel@tonic-gate /* 2024*0Sstevel@tonic-gate * Has subdirectory, attempt their 2025*0Sstevel@tonic-gate * unmount first 2026*0Sstevel@tonic-gate */ 2027*0Sstevel@tonic-gate nfnp = fnp->fn_dirents; 2028*0Sstevel@tonic-gate VN_HOLD(fntovn(nfnp)); 2029*0Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock); 2030*0Sstevel@tonic-gate 2031*0Sstevel@tonic-gate VN_RELE(vp); 2032*0Sstevel@tonic-gate fnp = nfnp; 2033*0Sstevel@tonic-gate goto top; 2034*0Sstevel@tonic-gate } 2035*0Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock); 2036*0Sstevel@tonic-gate /* 2037*0Sstevel@tonic-gate * No children, try next node. 2038*0Sstevel@tonic-gate */ 2039*0Sstevel@tonic-gate error = EBUSY; 2040*0Sstevel@tonic-gate goto next; 2041*0Sstevel@tonic-gate } 2042*0Sstevel@tonic-gate 2043*0Sstevel@tonic-gate AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG); 2044*0Sstevel@tonic-gate fnp->fn_error = 0; 2045*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 2046*0Sstevel@tonic-gate ilocked_it = 1; 2047*0Sstevel@tonic-gate 2048*0Sstevel@tonic-gate rw_enter(&fnp->fn_rwlock, RW_WRITER); 2049*0Sstevel@tonic-gate if (fnp->fn_trigger != NULL) { 2050*0Sstevel@tonic-gate unmount_as_unit = 1; 2051*0Sstevel@tonic-gate if ((vn_mountedvfs(vp) == NULL) && (check_auto_node(vp))) { 2052*0Sstevel@tonic-gate /* 2053*0Sstevel@tonic-gate * AUTOFS mountpoint is busy, there's 2054*0Sstevel@tonic-gate * no point trying to unmount. Fall through 2055*0Sstevel@tonic-gate * to attempt to unmount subtrees rooted 2056*0Sstevel@tonic-gate * at a possible trigger node, but remember 2057*0Sstevel@tonic-gate * not to unmount this tree. 2058*0Sstevel@tonic-gate */ 2059*0Sstevel@tonic-gate autofs_busy_root = 1; 2060*0Sstevel@tonic-gate } 2061*0Sstevel@tonic-gate 2062*0Sstevel@tonic-gate if (triggers_busy(fnp, &nfnp)) { 2063*0Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock); 2064*0Sstevel@tonic-gate if (nfnp == NULL) { 2065*0Sstevel@tonic-gate error = EBUSY; 2066*0Sstevel@tonic-gate goto next; 2067*0Sstevel@tonic-gate } 2068*0Sstevel@tonic-gate /* 2069*0Sstevel@tonic-gate * nfnp is busy, try to unmount it first 2070*0Sstevel@tonic-gate */ 2071*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 2072*0Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 2073*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 2074*0Sstevel@tonic-gate VN_RELE(vp); 2075*0Sstevel@tonic-gate ASSERT(fntovn(nfnp)->v_count > 1); 2076*0Sstevel@tonic-gate fnp = nfnp; 2077*0Sstevel@tonic-gate goto top; 2078*0Sstevel@tonic-gate } 2079*0Sstevel@tonic-gate 2080*0Sstevel@tonic-gate /* 2081*0Sstevel@tonic-gate * At this point, we know all trigger nodes are locked, 2082*0Sstevel@tonic-gate * and they're not busy or mounted on. 2083*0Sstevel@tonic-gate */ 2084*0Sstevel@tonic-gate 2085*0Sstevel@tonic-gate if (autofs_busy_root) { 2086*0Sstevel@tonic-gate /* 2087*0Sstevel@tonic-gate * Got to unlock the the trigger nodes since 2088*0Sstevel@tonic-gate * I'm not really going to unmount the filesystem. 2089*0Sstevel@tonic-gate */ 2090*0Sstevel@tonic-gate (void) triggers_unlock(fnp); 2091*0Sstevel@tonic-gate } else { 2092*0Sstevel@tonic-gate /* 2093*0Sstevel@tonic-gate * Attempt to unmount all the trigger nodes, 2094*0Sstevel@tonic-gate * save the action_list in case we need to 2095*0Sstevel@tonic-gate * remount them later. The action_list will be XDR 2096*0Sstevel@tonic-gate * freed later if there was no need to remount the 2097*0Sstevel@tonic-gate * trigger nodes. 2098*0Sstevel@tonic-gate */ 2099*0Sstevel@tonic-gate unmount_triggers(fnp, &alp); 2100*0Sstevel@tonic-gate } 2101*0Sstevel@tonic-gate } 2102*0Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock); 2103*0Sstevel@tonic-gate 2104*0Sstevel@tonic-gate if (autofs_busy_root) 2105*0Sstevel@tonic-gate goto next; 2106*0Sstevel@tonic-gate 2107*0Sstevel@tonic-gate (void) vn_vfswlock_wait(vp); 2108*0Sstevel@tonic-gate 2109*0Sstevel@tonic-gate vfsp = vn_mountedvfs(vp); 2110*0Sstevel@tonic-gate if (vfsp != NULL) { 2111*0Sstevel@tonic-gate /* 2112*0Sstevel@tonic-gate * Node is mounted on. 2113*0Sstevel@tonic-gate */ 2114*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tNode is mounted on\n")); 2115*0Sstevel@tonic-gate 2116*0Sstevel@tonic-gate /* 2117*0Sstevel@tonic-gate * Deal with /xfn/host/jurassic alikes here... 2118*0Sstevel@tonic-gate */ 2119*0Sstevel@tonic-gate if (vfs_matchops(vfsp, vfs_getops(vp->v_vfsp))) { 2120*0Sstevel@tonic-gate /* 2121*0Sstevel@tonic-gate * If the filesystem mounted here is AUTOFS, and it 2122*0Sstevel@tonic-gate * is busy, try to unmount the tree rooted on it 2123*0Sstevel@tonic-gate * first. We know this call to VFS_ROOT is safe to 2124*0Sstevel@tonic-gate * call while holding VVFSLOCK, since it resolves 2125*0Sstevel@tonic-gate * to a call to auto_root(). 2126*0Sstevel@tonic-gate */ 2127*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\t\tAUTOFS mounted here\n")); 2128*0Sstevel@tonic-gate vfs_lock_wait(vfsp); 2129*0Sstevel@tonic-gate if (VFS_ROOT(vfsp, &newvp)) { 2130*0Sstevel@tonic-gate cmn_err(CE_PANIC, 2131*0Sstevel@tonic-gate "unmount_tree: VFS_ROOT(vfs=%p) failed", 2132*0Sstevel@tonic-gate (void *)vfsp); 2133*0Sstevel@tonic-gate } 2134*0Sstevel@tonic-gate vfs_unlock(vfsp); 2135*0Sstevel@tonic-gate nfnp = vntofn(newvp); 2136*0Sstevel@tonic-gate if (DEEPER(nfnp)) { 2137*0Sstevel@tonic-gate vn_vfsunlock(vp); 2138*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 2139*0Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 2140*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 2141*0Sstevel@tonic-gate VN_RELE(vp); 2142*0Sstevel@tonic-gate fnp = nfnp; 2143*0Sstevel@tonic-gate goto top; 2144*0Sstevel@tonic-gate } 2145*0Sstevel@tonic-gate /* 2146*0Sstevel@tonic-gate * Fall through to unmount this filesystem 2147*0Sstevel@tonic-gate */ 2148*0Sstevel@tonic-gate VN_RELE(newvp); 2149*0Sstevel@tonic-gate } 2150*0Sstevel@tonic-gate 2151*0Sstevel@tonic-gate /* 2152*0Sstevel@tonic-gate * vn_vfsunlock(vp) is done inside unmount_node() 2153*0Sstevel@tonic-gate */ 2154*0Sstevel@tonic-gate error = unmount_node(vp, force); 2155*0Sstevel@tonic-gate if (error == ECONNRESET) { 2156*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tConnection dropped\n")); 2157*0Sstevel@tonic-gate if (vn_mountedvfs(vp) == NULL) { 2158*0Sstevel@tonic-gate /* 2159*0Sstevel@tonic-gate * The filesystem was unmounted before the 2160*0Sstevel@tonic-gate * daemon died. Unfortunately we can not 2161*0Sstevel@tonic-gate * determine whether all the cleanup work was 2162*0Sstevel@tonic-gate * successfully finished (i.e. update mnttab, 2163*0Sstevel@tonic-gate * or notify NFS server of the unmount). 2164*0Sstevel@tonic-gate * We should not retry the operation since the 2165*0Sstevel@tonic-gate * filesystem has already been unmounted, and 2166*0Sstevel@tonic-gate * may have already been removed from mnttab, 2167*0Sstevel@tonic-gate * in such case the devid/rdevid we send to 2168*0Sstevel@tonic-gate * the daemon will not be matched. So we have 2169*0Sstevel@tonic-gate * to be contempt with the partial unmount. 2170*0Sstevel@tonic-gate * Since the mountpoint is no longer covered, we 2171*0Sstevel@tonic-gate * clear the error condition. 2172*0Sstevel@tonic-gate */ 2173*0Sstevel@tonic-gate error = 0; 2174*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, 2175*0Sstevel@tonic-gate "unmount_tree: automountd connection " 2176*0Sstevel@tonic-gate "dropped"); 2177*0Sstevel@tonic-gate if (fnip->fi_flags & MF_DIRECT) { 2178*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "unmount_tree: " 2179*0Sstevel@tonic-gate "%s successfully unmounted - " 2180*0Sstevel@tonic-gate "do not remount triggers", 2181*0Sstevel@tonic-gate fnip->fi_path); 2182*0Sstevel@tonic-gate } else { 2183*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "unmount_tree: " 2184*0Sstevel@tonic-gate "%s/%s successfully unmounted - " 2185*0Sstevel@tonic-gate "do not remount triggers", 2186*0Sstevel@tonic-gate fnip->fi_path, fnp->fn_name); 2187*0Sstevel@tonic-gate } 2188*0Sstevel@tonic-gate } 2189*0Sstevel@tonic-gate } 2190*0Sstevel@tonic-gate } else { 2191*0Sstevel@tonic-gate vn_vfsunlock(vp); 2192*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tNode is AUTOFS\n")); 2193*0Sstevel@tonic-gate if (unmount_as_unit) { 2194*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tunmount as unit\n")); 2195*0Sstevel@tonic-gate error = unmount_autofs(vp); 2196*0Sstevel@tonic-gate } else { 2197*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tunmount one at a time\n")); 2198*0Sstevel@tonic-gate rw_enter(&fnp->fn_rwlock, RW_READER); 2199*0Sstevel@tonic-gate if (fnp->fn_dirents != NULL) { 2200*0Sstevel@tonic-gate /* 2201*0Sstevel@tonic-gate * Has subdirectory, attempt their 2202*0Sstevel@tonic-gate * unmount first 2203*0Sstevel@tonic-gate */ 2204*0Sstevel@tonic-gate nfnp = fnp->fn_dirents; 2205*0Sstevel@tonic-gate VN_HOLD(fntovn(nfnp)); 2206*0Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock); 2207*0Sstevel@tonic-gate 2208*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 2209*0Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 2210*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 2211*0Sstevel@tonic-gate VN_RELE(vp); 2212*0Sstevel@tonic-gate fnp = nfnp; 2213*0Sstevel@tonic-gate goto top; 2214*0Sstevel@tonic-gate } 2215*0Sstevel@tonic-gate rw_exit(&fnp->fn_rwlock); 2216*0Sstevel@tonic-gate goto next; 2217*0Sstevel@tonic-gate } 2218*0Sstevel@tonic-gate } 2219*0Sstevel@tonic-gate 2220*0Sstevel@tonic-gate if (error) { 2221*0Sstevel@tonic-gate AUTOFS_DPRINT((10, "\tUnmount failed\n")); 2222*0Sstevel@tonic-gate if (alp != NULL) { 2223*0Sstevel@tonic-gate /* 2224*0Sstevel@tonic-gate * Unmount failed, got to remount triggers. 2225*0Sstevel@tonic-gate */ 2226*0Sstevel@tonic-gate ASSERT((fnp->fn_flags & MF_THISUID_MATCH_RQD) == 0); 2227*0Sstevel@tonic-gate error = auto_perform_actions(fnip, fnp, alp, CRED()); 2228*0Sstevel@tonic-gate if (error) { 2229*0Sstevel@tonic-gate auto_log(fngp, CE_WARN, "autofs: can't remount " 2230*0Sstevel@tonic-gate "triggers fnp=%p error=%d", (void *)fnp, 2231*0Sstevel@tonic-gate error); 2232*0Sstevel@tonic-gate error = 0; 2233*0Sstevel@tonic-gate /* 2234*0Sstevel@tonic-gate * The action list should have been 2235*0Sstevel@tonic-gate * xdr_free'd by auto_perform_actions 2236*0Sstevel@tonic-gate * since an error occured 2237*0Sstevel@tonic-gate */ 2238*0Sstevel@tonic-gate alp = NULL; 2239*0Sstevel@tonic-gate } 2240*0Sstevel@tonic-gate } 2241*0Sstevel@tonic-gate } else { 2242*0Sstevel@tonic-gate /* 2243*0Sstevel@tonic-gate * The unmount succeeded, which will cause this node to 2244*0Sstevel@tonic-gate * be removed from its parent if its an indirect mount, 2245*0Sstevel@tonic-gate * therefore update the parent's atime and mtime now. 2246*0Sstevel@tonic-gate * I don't update them in auto_disconnect() because I 2247*0Sstevel@tonic-gate * don't want atime and mtime changing every time a 2248*0Sstevel@tonic-gate * lookup goes to the daemon and creates a new node. 2249*0Sstevel@tonic-gate */ 2250*0Sstevel@tonic-gate unmount_done = 1; 2251*0Sstevel@tonic-gate if ((fnip->fi_flags & MF_DIRECT) == 0) { 2252*0Sstevel@tonic-gate gethrestime(&now); 2253*0Sstevel@tonic-gate if (fnp->fn_parent == fngp->fng_rootfnnodep) 2254*0Sstevel@tonic-gate fnp->fn_atime = fnp->fn_mtime = now; 2255*0Sstevel@tonic-gate else 2256*0Sstevel@tonic-gate fnp->fn_parent->fn_atime = 2257*0Sstevel@tonic-gate fnp->fn_parent->fn_mtime = now; 2258*0Sstevel@tonic-gate } 2259*0Sstevel@tonic-gate 2260*0Sstevel@tonic-gate /* 2261*0Sstevel@tonic-gate * Free the action list here 2262*0Sstevel@tonic-gate */ 2263*0Sstevel@tonic-gate if (alp != NULL) { 2264*0Sstevel@tonic-gate xdr_free(xdr_action_list, (char *)alp); 2265*0Sstevel@tonic-gate alp = NULL; 2266*0Sstevel@tonic-gate } 2267*0Sstevel@tonic-gate } 2268*0Sstevel@tonic-gate 2269*0Sstevel@tonic-gate fnp->fn_ref_time = gethrestime_sec(); 2270*0Sstevel@tonic-gate 2271*0Sstevel@tonic-gate next: 2272*0Sstevel@tonic-gate /* 2273*0Sstevel@tonic-gate * Obtain parent's readers lock before grabbing 2274*0Sstevel@tonic-gate * reference to next sibling. 2275*0Sstevel@tonic-gate * XXX Note that nodes in the top level list (mounted 2276*0Sstevel@tonic-gate * in user space not by the daemon in the kernel) parent is itself, 2277*0Sstevel@tonic-gate * therefore grabbing the lock makes no sense, but doesn't 2278*0Sstevel@tonic-gate * hurt either. 2279*0Sstevel@tonic-gate */ 2280*0Sstevel@tonic-gate pfnp = fnp->fn_parent; 2281*0Sstevel@tonic-gate ASSERT(pfnp != NULL); 2282*0Sstevel@tonic-gate rw_enter(&pfnp->fn_rwlock, RW_READER); 2283*0Sstevel@tonic-gate if ((nfnp = fnp->fn_next) != NULL) 2284*0Sstevel@tonic-gate VN_HOLD(fntovn(nfnp)); 2285*0Sstevel@tonic-gate rw_exit(&pfnp->fn_rwlock); 2286*0Sstevel@tonic-gate 2287*0Sstevel@tonic-gate if (ilocked_it) { 2288*0Sstevel@tonic-gate mutex_enter(&fnp->fn_lock); 2289*0Sstevel@tonic-gate if (unmount_done) { 2290*0Sstevel@tonic-gate /* 2291*0Sstevel@tonic-gate * Other threads may be waiting for this unmount to 2292*0Sstevel@tonic-gate * finish. We must let it know that in order to 2293*0Sstevel@tonic-gate * proceed, it must trigger the mount itself. 2294*0Sstevel@tonic-gate */ 2295*0Sstevel@tonic-gate fnp->fn_flags &= ~MF_IK_MOUNT; 2296*0Sstevel@tonic-gate if (fnp->fn_flags & MF_WAITING) 2297*0Sstevel@tonic-gate fnp->fn_error = EAGAIN; 2298*0Sstevel@tonic-gate unmount_done = 0; 2299*0Sstevel@tonic-gate } 2300*0Sstevel@tonic-gate AUTOFS_UNBLOCK_OTHERS(fnp, MF_INPROG); 2301*0Sstevel@tonic-gate mutex_exit(&fnp->fn_lock); 2302*0Sstevel@tonic-gate ilocked_it = 0; 2303*0Sstevel@tonic-gate } 2304*0Sstevel@tonic-gate 2305*0Sstevel@tonic-gate if (nfnp != NULL) { 2306*0Sstevel@tonic-gate VN_RELE(vp); 2307*0Sstevel@tonic-gate fnp = nfnp; 2308*0Sstevel@tonic-gate /* 2309*0Sstevel@tonic-gate * Unmount next element 2310*0Sstevel@tonic-gate */ 2311*0Sstevel@tonic-gate goto top; 2312*0Sstevel@tonic-gate } 2313*0Sstevel@tonic-gate 2314*0Sstevel@tonic-gate /* 2315*0Sstevel@tonic-gate * We don't want to unmount rootfnnodep, so the check is made here 2316*0Sstevel@tonic-gate */ 2317*0Sstevel@tonic-gate ASSERT(pfnp != fnp); 2318*0Sstevel@tonic-gate if (pfnp != fngp->fng_rootfnnodep) { 2319*0Sstevel@tonic-gate /* 2320*0Sstevel@tonic-gate * Now attempt to unmount my parent 2321*0Sstevel@tonic-gate */ 2322*0Sstevel@tonic-gate VN_HOLD(fntovn(pfnp)); 2323*0Sstevel@tonic-gate VN_RELE(vp); 2324*0Sstevel@tonic-gate fnp = pfnp; 2325*0Sstevel@tonic-gate 2326*0Sstevel@tonic-gate goto top; 2327*0Sstevel@tonic-gate } 2328*0Sstevel@tonic-gate 2329*0Sstevel@tonic-gate VN_RELE(vp); 2330*0Sstevel@tonic-gate 2331*0Sstevel@tonic-gate /* 2332*0Sstevel@tonic-gate * At this point we've walked the entire tree and attempted to unmount 2333*0Sstevel@tonic-gate * as much as we can one level at a time. 2334*0Sstevel@tonic-gate */ 2335*0Sstevel@tonic-gate done: 2336*0Sstevel@tonic-gate mutex_enter(&unmount_tree_cpr_lock); 2337*0Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); 2338*0Sstevel@tonic-gate mutex_destroy(&unmount_tree_cpr_lock); 2339*0Sstevel@tonic-gate } 2340*0Sstevel@tonic-gate 2341*0Sstevel@tonic-gate static void 2342*0Sstevel@tonic-gate unmount_zone_tree(struct autofs_globals *fngp) 2343*0Sstevel@tonic-gate { 2344*0Sstevel@tonic-gate unmount_tree(fngp, 0); 2345*0Sstevel@tonic-gate mutex_enter(&fngp->fng_unmount_threads_lock); 2346*0Sstevel@tonic-gate fngp->fng_unmount_threads--; 2347*0Sstevel@tonic-gate mutex_exit(&fngp->fng_unmount_threads_lock); 2348*0Sstevel@tonic-gate 2349*0Sstevel@tonic-gate AUTOFS_DPRINT((5, "unmount_tree done. Thread exiting.\n")); 2350*0Sstevel@tonic-gate 2351*0Sstevel@tonic-gate zthread_exit(); 2352*0Sstevel@tonic-gate /* NOTREACHED */ 2353*0Sstevel@tonic-gate } 2354*0Sstevel@tonic-gate 2355*0Sstevel@tonic-gate static int autofs_unmount_thread_timer = 120; /* in seconds */ 2356*0Sstevel@tonic-gate 2357*0Sstevel@tonic-gate void 2358*0Sstevel@tonic-gate auto_do_unmount(struct autofs_globals *fngp) 2359*0Sstevel@tonic-gate { 2360*0Sstevel@tonic-gate callb_cpr_t cprinfo; 2361*0Sstevel@tonic-gate clock_t timeleft; 2362*0Sstevel@tonic-gate zone_t *zone = curproc->p_zone; 2363*0Sstevel@tonic-gate 2364*0Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &fngp->fng_unmount_threads_lock, 2365*0Sstevel@tonic-gate callb_generic_cpr, "auto_do_unmount"); 2366*0Sstevel@tonic-gate 2367*0Sstevel@tonic-gate for (;;) { /* forever */ 2368*0Sstevel@tonic-gate mutex_enter(&fngp->fng_unmount_threads_lock); 2369*0Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 2370*0Sstevel@tonic-gate newthread: 2371*0Sstevel@tonic-gate mutex_exit(&fngp->fng_unmount_threads_lock); 2372*0Sstevel@tonic-gate timeleft = zone_status_timedwait(zone, lbolt + 2373*0Sstevel@tonic-gate autofs_unmount_thread_timer * hz, ZONE_IS_SHUTTING_DOWN); 2374*0Sstevel@tonic-gate mutex_enter(&fngp->fng_unmount_threads_lock); 2375*0Sstevel@tonic-gate 2376*0Sstevel@tonic-gate if (timeleft != -1) { /* didn't time out */ 2377*0Sstevel@tonic-gate ASSERT(zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN); 2378*0Sstevel@tonic-gate /* 2379*0Sstevel@tonic-gate * zone is exiting... don't create any new threads. 2380*0Sstevel@tonic-gate * fng_unmount_threads_lock is released implicitly by 2381*0Sstevel@tonic-gate * the below. 2382*0Sstevel@tonic-gate */ 2383*0Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, 2384*0Sstevel@tonic-gate &fngp->fng_unmount_threads_lock); 2385*0Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); 2386*0Sstevel@tonic-gate zthread_exit(); 2387*0Sstevel@tonic-gate /* NOTREACHED */ 2388*0Sstevel@tonic-gate } 2389*0Sstevel@tonic-gate if (fngp->fng_unmount_threads < autofs_unmount_threads) { 2390*0Sstevel@tonic-gate fngp->fng_unmount_threads++; 2391*0Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, 2392*0Sstevel@tonic-gate &fngp->fng_unmount_threads_lock); 2393*0Sstevel@tonic-gate mutex_exit(&fngp->fng_unmount_threads_lock); 2394*0Sstevel@tonic-gate 2395*0Sstevel@tonic-gate (void) zthread_create(NULL, 0, unmount_zone_tree, fngp, 2396*0Sstevel@tonic-gate 0, minclsyspri); 2397*0Sstevel@tonic-gate } else 2398*0Sstevel@tonic-gate goto newthread; 2399*0Sstevel@tonic-gate } 2400*0Sstevel@tonic-gate /* NOTREACHED */ 2401*0Sstevel@tonic-gate } 2402*0Sstevel@tonic-gate 2403*0Sstevel@tonic-gate /* 2404*0Sstevel@tonic-gate * Is nobrowse specified in option string? 2405*0Sstevel@tonic-gate * opts should be a null ('\0') terminated string. 2406*0Sstevel@tonic-gate * Returns non-zero if nobrowse has been specified. 2407*0Sstevel@tonic-gate */ 2408*0Sstevel@tonic-gate int 2409*0Sstevel@tonic-gate auto_nobrowse_option(char *opts) 2410*0Sstevel@tonic-gate { 2411*0Sstevel@tonic-gate char *buf; 2412*0Sstevel@tonic-gate char *p; 2413*0Sstevel@tonic-gate char *t; 2414*0Sstevel@tonic-gate int nobrowse = 0; 2415*0Sstevel@tonic-gate int last_opt = 0; 2416*0Sstevel@tonic-gate size_t len; 2417*0Sstevel@tonic-gate 2418*0Sstevel@tonic-gate len = strlen(opts) + 1; 2419*0Sstevel@tonic-gate p = buf = kmem_alloc(len, KM_SLEEP); 2420*0Sstevel@tonic-gate (void) strcpy(buf, opts); 2421*0Sstevel@tonic-gate do { 2422*0Sstevel@tonic-gate if (t = strchr(p, ',')) 2423*0Sstevel@tonic-gate *t++ = '\0'; 2424*0Sstevel@tonic-gate else 2425*0Sstevel@tonic-gate last_opt++; 2426*0Sstevel@tonic-gate if (strcmp(p, MNTOPT_NOBROWSE) == 0) 2427*0Sstevel@tonic-gate nobrowse = 1; 2428*0Sstevel@tonic-gate else if (strcmp(p, MNTOPT_BROWSE) == 0) 2429*0Sstevel@tonic-gate nobrowse = 0; 2430*0Sstevel@tonic-gate p = t; 2431*0Sstevel@tonic-gate } while (!last_opt); 2432*0Sstevel@tonic-gate kmem_free(buf, len); 2433*0Sstevel@tonic-gate 2434*0Sstevel@tonic-gate return (nobrowse); 2435*0Sstevel@tonic-gate } 2436*0Sstevel@tonic-gate 2437*0Sstevel@tonic-gate /* 2438*0Sstevel@tonic-gate * used to log warnings only if automountd is running 2439*0Sstevel@tonic-gate * with verbose mode set 2440*0Sstevel@tonic-gate */ 2441*0Sstevel@tonic-gate void 2442*0Sstevel@tonic-gate auto_log(struct autofs_globals *fngp, int level, const char *fmt, ...) 2443*0Sstevel@tonic-gate { 2444*0Sstevel@tonic-gate va_list args; 2445*0Sstevel@tonic-gate 2446*0Sstevel@tonic-gate if (fngp->fng_verbose > 0) { 2447*0Sstevel@tonic-gate va_start(args, fmt); 2448*0Sstevel@tonic-gate vzcmn_err(fngp->fng_zoneid, level, fmt, args); 2449*0Sstevel@tonic-gate va_end(args); 2450*0Sstevel@tonic-gate } 2451*0Sstevel@tonic-gate } 2452*0Sstevel@tonic-gate 2453*0Sstevel@tonic-gate #ifdef DEBUG 2454*0Sstevel@tonic-gate static int autofs_debug = 0; 2455*0Sstevel@tonic-gate 2456*0Sstevel@tonic-gate /* 2457*0Sstevel@tonic-gate * Utilities used by both client and server 2458*0Sstevel@tonic-gate * Standard levels: 2459*0Sstevel@tonic-gate * 0) no debugging 2460*0Sstevel@tonic-gate * 1) hard failures 2461*0Sstevel@tonic-gate * 2) soft failures 2462*0Sstevel@tonic-gate * 3) current test software 2463*0Sstevel@tonic-gate * 4) main procedure entry points 2464*0Sstevel@tonic-gate * 5) main procedure exit points 2465*0Sstevel@tonic-gate * 6) utility procedure entry points 2466*0Sstevel@tonic-gate * 7) utility procedure exit points 2467*0Sstevel@tonic-gate * 8) obscure procedure entry points 2468*0Sstevel@tonic-gate * 9) obscure procedure exit points 2469*0Sstevel@tonic-gate * 10) random stuff 2470*0Sstevel@tonic-gate * 11) all <= 1 2471*0Sstevel@tonic-gate * 12) all <= 2 2472*0Sstevel@tonic-gate * 13) all <= 3 2473*0Sstevel@tonic-gate * ... 2474*0Sstevel@tonic-gate */ 2475*0Sstevel@tonic-gate /* PRINTFLIKE2 */ 2476*0Sstevel@tonic-gate void 2477*0Sstevel@tonic-gate auto_dprint(int level, const char *fmt, ...) 2478*0Sstevel@tonic-gate { 2479*0Sstevel@tonic-gate va_list args; 2480*0Sstevel@tonic-gate 2481*0Sstevel@tonic-gate if (autofs_debug == level || 2482*0Sstevel@tonic-gate (autofs_debug > 10 && (autofs_debug - 10) >= level)) { 2483*0Sstevel@tonic-gate va_start(args, fmt); 2484*0Sstevel@tonic-gate (void) vprintf(fmt, args); 2485*0Sstevel@tonic-gate va_end(args); 2486*0Sstevel@tonic-gate } 2487*0Sstevel@tonic-gate } 2488*0Sstevel@tonic-gate #endif /* DEBUG */ 2489