Home | History | Annotate | 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 2007 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     35 
     36 #include <sys/param.h>
     37 #include <sys/isa_defs.h>
     38 #include <sys/types.h>
     39 #include <sys/sysmacros.h>
     40 #include <sys/cred.h>
     41 #include <sys/user.h>
     42 #include <sys/systm.h>
     43 #include <sys/errno.h>
     44 #include <sys/fcntl.h>
     45 #include <sys/pathname.h>
     46 #include <sys/var.h>
     47 #include <sys/vfs.h>
     48 #include <sys/vnode.h>
     49 #include <sys/file.h>
     50 #include <sys/mode.h>
     51 #include <sys/proc.h>
     52 #include <sys/uio.h>
     53 #include <sys/poll.h>
     54 #include <sys/kmem.h>
     55 #include <sys/filio.h>
     56 #include <sys/cmn_err.h>
     57 #include <sys/policy.h>
     58 #include <sys/zone.h>
     59 
     60 #include <sys/debug.h>
     61 #include <c2/audit.h>
     62 #include <fs/fs_subr.h>
     63 
     64 /*
     65  * Change current working directory (".").
     66  */
     67 static int	chdirec(vnode_t *, int ischroot, int do_traverse);
     68 
     69 int
     70 chdir(char *fname)
     71 {
     72 	vnode_t *vp;
     73 	int error;
     74 	int estale_retry = 0;
     75 
     76 lookup:
     77 	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
     78 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
     79 			goto lookup;
     80 		return (set_errno(error));
     81 	}
     82 
     83 	error = chdirec(vp, 0, 1);
     84 	if (error) {
     85 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
     86 			goto lookup;
     87 		return (set_errno(error));
     88 	}
     89 	return (0);
     90 }
     91 
     92 /*
     93  * File-descriptor based version of 'chdir'.
     94  */
     95 int
     96 fchdir(int fd)
     97 {
     98 	vnode_t *vp;
     99 	file_t *fp;
    100 	int error;
    101 
    102 	if ((fp = getf(fd)) == NULL)
    103 		return (set_errno(EBADF));
    104 	vp = fp->f_vnode;
    105 	VN_HOLD(vp);
    106 	releasef(fd);
    107 	error = chdirec(vp, 0, 0);
    108 	if (error)
    109 		return (set_errno(error));
    110 	return (0);
    111 }
    112 
    113 /*
    114  * Change notion of root ("/") directory.
    115  */
    116 int
    117 chroot(char *fname)
    118 {
    119 	vnode_t *vp;
    120 	int error;
    121 	int estale_retry = 0;
    122 
    123 lookup:
    124 	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
    125 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
    126 			goto lookup;
    127 		return (set_errno(error));
    128 	}
    129 
    130 	error = chdirec(vp, 1, 1);
    131 	if (error) {
    132 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
    133 			goto lookup;
    134 		return (set_errno(error));
    135 	}
    136 	return (0);
    137 }
    138 
    139 /*
    140  *	++++++++++++++++++++++++
    141  *	++  SunOS4.1 Buyback  ++
    142  *	++++++++++++++++++++++++
    143  * Change root directory with a user given fd
    144  */
    145 int
    146 fchroot(int fd)
    147 {
    148 	vnode_t *vp;
    149 	file_t *fp;
    150 	int error;
    151 
    152 	if ((fp = getf(fd)) == NULL)
    153 		return (set_errno(EBADF));
    154 	vp = fp->f_vnode;
    155 	VN_HOLD(vp);
    156 	releasef(fd);
    157 	error = chdirec(vp, 1, 0);
    158 	if (error)
    159 		return (set_errno(error));
    160 	return (0);
    161 }
    162 
    163 static int
    164 chdirec(vnode_t *vp, int ischroot, int do_traverse)
    165 {
    166 	int error;
    167 	vnode_t *oldvp;
    168 	proc_t *pp = curproc;
    169 	vnode_t **vpp;
    170 	refstr_t *cwd;
    171 	int newcwd = 1;
    172 
    173 	if (vp->v_type != VDIR) {
    174 		error = ENOTDIR;
    175 		goto bad;
    176 	}
    177 	if (error = VOP_ACCESS(vp, VEXEC, 0, CRED(), NULL))
    178 		goto bad;
    179 
    180 	/*
    181 	 * The VOP_ACCESS() may have covered 'vp' with a new filesystem,
    182 	 * if 'vp' is an autoFS vnode. Traverse the mountpoint so
    183 	 * that we don't end up with a covered current directory.
    184 	 */
    185 	if (vn_mountedvfs(vp) != NULL && do_traverse) {
    186 		if (error = traverse(&vp))
    187 			goto bad;
    188 	}
    189 
    190 	/*
    191 	 * Special chroot semantics: chroot is allowed if privileged
    192 	 * or if the target is really a loopback mount of the root (or
    193 	 * root of the zone) as determined by comparing dev and inode
    194 	 * numbers
    195 	 */
    196 	if (ischroot) {
    197 		struct vattr tattr;
    198 		struct vattr rattr;
    199 		vnode_t *zonevp = curproc->p_zone->zone_rootvp;
    200 
    201 		tattr.va_mask = AT_FSID|AT_NODEID;
    202 		if (error = VOP_GETATTR(vp, &tattr, 0, CRED(), NULL))
    203 			goto bad;
    204 
    205 		rattr.va_mask = AT_FSID|AT_NODEID;
    206 		if (error = VOP_GETATTR(zonevp, &rattr, 0, CRED(), NULL))
    207 			goto bad;
    208 
    209 		if ((tattr.va_fsid != rattr.va_fsid ||
    210 		    tattr.va_nodeid != rattr.va_nodeid) &&
    211 		    (error = secpolicy_chroot(CRED())) != 0)
    212 			goto bad;
    213 
    214 		vpp = &PTOU(pp)->u_rdir;
    215 	} else {
    216 		vpp = &PTOU(pp)->u_cdir;
    217 	}
    218 
    219 	if (audit_active)	/* update abs cwd/root path see c2audit.c */
    220 		audit_chdirec(vp, vpp);
    221 
    222 	mutex_enter(&pp->p_lock);
    223 	/*
    224 	 * This bit of logic prevents us from overwriting u_cwd if we are
    225 	 * changing to the same directory.  We set the cwd to NULL so that we
    226 	 * don't try to do the lookup on the next call to getcwd().
    227 	 */
    228 	if (!ischroot && *vpp != NULL && vp != NULL && VN_CMP(*vpp, vp))
    229 		newcwd = 0;
    230 
    231 	oldvp = *vpp;
    232 	*vpp = vp;
    233 	if ((cwd = PTOU(pp)->u_cwd) != NULL && newcwd)
    234 		PTOU(pp)->u_cwd = NULL;
    235 	mutex_exit(&pp->p_lock);
    236 
    237 	if (cwd && newcwd)
    238 		refstr_rele(cwd);
    239 	if (oldvp)
    240 		VN_RELE(oldvp);
    241 	return (0);
    242 
    243 bad:
    244 	VN_RELE(vp);
    245 	return (error);
    246 }
    247