chdir.c revision 2051:50a87bae0c3a
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 2006 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 */
67static int	chdirec(vnode_t *, int ischroot, int do_traverse);
68
69int
70chdir(char *fname)
71{
72	vnode_t *vp;
73	int error;
74	int estale_retry = 0;
75
76lookup:
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 */
95int
96fchdir(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 */
116int
117chroot(char *fname)
118{
119	vnode_t *vp;
120	int error;
121	int estale_retry = 0;
122
123lookup:
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 */
145int
146fchroot(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
163static int
164chdirec(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()))
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()))
203			goto bad;
204
205		rattr.va_mask = AT_FSID|AT_NODEID;
206		if (error = VOP_GETATTR(zonevp, &rattr, 0, CRED()))
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#ifdef C2_AUDIT
220	if (audit_active)	/* update abs cwd/root path see c2audit.c */
221		audit_chdirec(vp, vpp);
222#endif
223
224	mutex_enter(&pp->p_lock);
225	/*
226	 * This bit of logic prevents us from overwriting u_cwd if we are
227	 * changing to the same directory.  We set the cwd to NULL so that we
228	 * don't try to do the lookup on the next call to getcwd().
229	 */
230	if (!ischroot && *vpp != NULL && vp != NULL && VN_CMP(*vpp, vp))
231		newcwd = 0;
232
233	oldvp = *vpp;
234	*vpp = vp;
235	if ((cwd = PTOU(pp)->u_cwd) != NULL && newcwd)
236		PTOU(pp)->u_cwd = NULL;
237	mutex_exit(&pp->p_lock);
238
239	if (cwd && newcwd)
240		refstr_rele(cwd);
241	if (oldvp)
242		VN_RELE(oldvp);
243	return (0);
244
245bad:
246	VN_RELE(vp);
247	return (error);
248}
249