OpenGrok

Cross Reference: chdir.c
xref: /onnv/onnv-gate/usr/src/uts/common/syscall/chdir.c
Home | History | Annotate | Line # | Download | only in syscall
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
     27 /*	  All Rights Reserved  	*/
     28 
     29 /*
     30  * Portions of this source code were derived from Berkeley 4.3 BSD
     31  * under license from the Regents of the University of California.
     32  */
     33 
     34 #include <sys/param.h>
     35 #include <sys/isa_defs.h>
     36 #include <sys/types.h>
     37 #include <sys/sysmacros.h>
     38 #include <sys/cred.h>
     39 #include <sys/user.h>
     40 #include <sys/systm.h>
     41 #include <sys/errno.h>
     42 #include <sys/fcntl.h>
     43 #include <sys/pathname.h>
     44 #include <sys/var.h>
     45 #include <sys/vfs.h>
     46 #include <sys/vnode.h>
     47 #include <sys/file.h>
     48 #include <sys/mode.h>
     49 #include <sys/proc.h>
     50 #include <sys/uio.h>
     51 #include <sys/poll.h>
     52 #include <sys/kmem.h>
     53 #include <sys/filio.h>
     54 #include <sys/cmn_err.h>
     55 #include <sys/policy.h>
     56 #include <sys/zone.h>
     57 
     58 #include <sys/debug.h>
     59 #include <c2/audit.h>
     60 #include <fs/fs_subr.h>
     61 
     62 /*
     63  * Change current working directory (".").
     64  */
     65 static int	chdirec(vnode_t *, int ischroot, int do_traverse);
     66 
     67 int
     68 chdir(char *fname)
     69 {
     70 	vnode_t *vp;
     71 	int error;
     72 	int estale_retry = 0;
     73 
     74 lookup:
     75 	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
     76 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
     77 			goto lookup;
     78 		return (set_errno(error));
     79 	}
     80 
     81 	error = chdirec(vp, 0, 1);
     82 	if (error) {
     83 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
     84 			goto lookup;
     85 		return (set_errno(error));
     86 	}
     87 	return (0);
     88 }
     89 
     90 /*
     91  * File-descriptor based version of 'chdir'.
     92  */
     93 int
     94 fchdir(int fd)
     95 {
     96 	vnode_t *vp;
     97 	file_t *fp;
     98 	int error;
     99 
    100 	if ((fp = getf(fd)) == NULL)
    101 		return (set_errno(EBADF));
    102 	vp = fp->f_vnode;
    103 	VN_HOLD(vp);
    104 	releasef(fd);
    105 	error = chdirec(vp, 0, 0);
    106 	if (error)
    107 		return (set_errno(error));
    108 	return (0);
    109 }
    110 
    111 /*
    112  * Change notion of root ("/") directory.
    113  */
    114 int
    115 chroot(char *fname)
    116 {
    117 	vnode_t *vp;
    118 	int error;
    119 	int estale_retry = 0;
    120 
    121 lookup:
    122 	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
    123 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
    124 			goto lookup;
    125 		return (set_errno(error));
    126 	}
    127 
    128 	error = chdirec(vp, 1, 1);
    129 	if (error) {
    130 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
    131 			goto lookup;
    132 		return (set_errno(error));
    133 	}
    134 	return (0);
    135 }
    136 
    137 /*
    138  *	++++++++++++++++++++++++
    139  *	++  SunOS4.1 Buyback  ++
    140  *	++++++++++++++++++++++++
    141  * Change root directory with a user given fd
    142  */
    143 int
    144 fchroot(int fd)
    145 {
    146 	vnode_t *vp;
    147 	file_t *fp;
    148 	int error;
    149 
    150 	if ((fp = getf(fd)) == NULL)
    151 		return (set_errno(EBADF));
    152 	vp = fp->f_vnode;
    153 	VN_HOLD(vp);
    154 	releasef(fd);
    155 	error = chdirec(vp, 1, 0);
    156 	if (error)
    157 		return (set_errno(error));
    158 	return (0);
    159 }
    160 
    161 static int
    162 chdirec(vnode_t *vp, int ischroot, int do_traverse)
    163 {
    164 	int error;
    165 	vnode_t *oldvp;
    166 	proc_t *pp = curproc;
    167 	vnode_t **vpp;
    168 	refstr_t *cwd;
    169 	int newcwd = 1;
    170 
    171 	if (vp->v_type != VDIR) {
    172 		error = ENOTDIR;
    173 		goto bad;
    174 	}
    175 	if (error = VOP_ACCESS(vp, VEXEC, 0, CRED(), NULL))
    176 		goto bad;
    177 
    178 	/*
    179 	 * The VOP_ACCESS() may have covered 'vp' with a new filesystem,
    180 	 * if 'vp' is an autoFS vnode. Traverse the mountpoint so
    181 	 * that we don't end up with a covered current directory.
    182 	 */
    183 	if (vn_mountedvfs(vp) != NULL && do_traverse) {
    184 		if (error = traverse(&vp))
    185 			goto bad;
    186 	}
    187 
    188 	/*
    189 	 * Special chroot semantics: chroot is allowed if privileged
    190 	 * or if the target is really a loopback mount of the root (or
    191 	 * root of the zone) as determined by comparing dev and inode
    192 	 * numbers
    193 	 */
    194 	if (ischroot) {
    195 		struct vattr tattr;
    196 		struct vattr rattr;
    197 		vnode_t *zonevp = curproc->p_zone->zone_rootvp;
    198 
    199 		tattr.va_mask = AT_FSID|AT_NODEID;
    200 		if (error = VOP_GETATTR(vp, &tattr, 0, CRED(), NULL))
    201 			goto bad;
    202 
    203 		rattr.va_mask = AT_FSID|AT_NODEID;
    204 		if (error = VOP_GETATTR(zonevp, &rattr, 0, CRED(), NULL))
    205 			goto bad;
    206 
    207 		if ((tattr.va_fsid != rattr.va_fsid ||
    208 		    tattr.va_nodeid != rattr.va_nodeid) &&
    209 		    (error = secpolicy_chroot(CRED())) != 0)
    210 			goto bad;
    211 
    212 		vpp = &PTOU(pp)->u_rdir;
    213 	} else {
    214 		vpp = &PTOU(pp)->u_cdir;
    215 	}
    216 
    217 	/* update abs cwd/root path see c2/audit.c */
    218 	if (AU_AUDITING())
    219 		audit_chdirec(vp, vpp);
    220 
    221 	mutex_enter(&pp->p_lock);
    222 	/*
    223 	 * This bit of logic prevents us from overwriting u_cwd if we are
    224 	 * changing to the same directory.  We set the cwd to NULL so that we
    225 	 * don't try to do the lookup on the next call to getcwd().
    226 	 */
    227 	if (!ischroot && *vpp != NULL && vp != NULL && VN_CMP(*vpp, vp))
    228 		newcwd = 0;
    229 
    230 	oldvp = *vpp;
    231 	*vpp = vp;
    232 	if ((cwd = PTOU(pp)->u_cwd) != NULL && newcwd)
    233 		PTOU(pp)->u_cwd = NULL;
    234 	mutex_exit(&pp->p_lock);
    235 
    236 	if (cwd && newcwd)
    237 		refstr_rele(cwd);
    238 	if (oldvp)
    239 		VN_RELE(oldvp);
    240 	return (0);
    241 
    242 bad:
    243 	VN_RELE(vp);
    244 	return (error);
    245 }
    246