chdir.c revision 5753:d64b1f799526
1314564Sdim/*
2314564Sdim * CDDL HEADER START
3254721Semaste *
4353358Sdim * The contents of this file are subject to the terms of the
5353358Sdim * Common Development and Distribution License (the "License").
6353358Sdim * You may not use this file except in compliance with the License.
7254721Semaste *
8254721Semaste * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9254721Semaste * or http://www.opensolaris.org/os/licensing.
10254721Semaste * See the License for the specific language governing permissions
11254721Semaste * and limitations under the License.
12254721Semaste *
13254721Semaste * When distributing Covered Code, include this CDDL HEADER in each
14254721Semaste * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15344779Sdim * If applicable, add the following below this CDDL HEADER, with the
16314564Sdim * fields enclosed by brackets "[]" replaced with your own identifying
17254721Semaste * information: Portions Copyright [yyyy] [name of copyright owner]
18254721Semaste *
19254721Semaste * CDDL HEADER END
20314564Sdim */
21314564Sdim/*
22254721Semaste * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23254721Semaste * Use is subject to license terms.
24353358Sdim */
25314564Sdim
26314564Sdim/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27314564Sdim/*	  All Rights Reserved  	*/
28314564Sdim
29353358Sdim/*
30296417Sdim * Portions of this source code were derived from Berkeley 4.3 BSD
31314564Sdim * under license from the Regents of the University of California.
32314564Sdim */
33254721Semaste
34314564Sdim#pragma ident	"%Z%%M%	%I%	%E% SMI"
35314564Sdim
36314564Sdim#include <sys/param.h>
37314564Sdim#include <sys/isa_defs.h>
38314564Sdim#include <sys/types.h>
39314564Sdim#include <sys/sysmacros.h>
40314564Sdim#include <sys/cred.h>
41314564Sdim#include <sys/user.h>
42314564Sdim#include <sys/systm.h>
43314564Sdim#include <sys/errno.h>
44314564Sdim#include <sys/fcntl.h>
45314564Sdim#include <sys/pathname.h>
46314564Sdim#include <sys/var.h>
47314564Sdim#include <sys/vfs.h>
48314564Sdim#include <sys/vnode.h>
49314564Sdim#include <sys/file.h>
50314564Sdim#include <sys/mode.h>
51314564Sdim#include <sys/proc.h>
52314564Sdim#include <sys/uio.h>
53314564Sdim#include <sys/poll.h>
54314564Sdim#include <sys/kmem.h>
55314564Sdim#include <sys/filio.h>
56314564Sdim#include <sys/cmn_err.h>
57314564Sdim#include <sys/policy.h>
58314564Sdim#include <sys/zone.h>
59314564Sdim
60314564Sdim#include <sys/debug.h>
61254721Semaste#include <c2/audit.h>
62314564Sdim#include <fs/fs_subr.h>
63254721Semaste
64314564Sdim/*
65314564Sdim * Change current working directory (".").
66314564Sdim */
67314564Sdimstatic int	chdirec(vnode_t *, int ischroot, int do_traverse);
68254721Semaste
69314564Sdimint
70314564Sdimchdir(char *fname)
71314564Sdim{
72314564Sdim	vnode_t *vp;
73314564Sdim	int error;
74254721Semaste	int estale_retry = 0;
75314564Sdim
76314564Sdimlookup:
77254721Semaste	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
78314564Sdim		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
79314564Sdim			goto lookup;
80314564Sdim		return (set_errno(error));
81296417Sdim	}
82314564Sdim
83314564Sdim	error = chdirec(vp, 0, 1);
84314564Sdim	if (error) {
85296417Sdim		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
86254721Semaste			goto lookup;
87314564Sdim		return (set_errno(error));
88254721Semaste	}
89314564Sdim	return (0);
90254721Semaste}
91314564Sdim
92314564Sdim/*
93314564Sdim * File-descriptor based version of 'chdir'.
94314564Sdim */
95254721Semasteint
96254721Semastefchdir(int fd)
97254721Semaste{
98254721Semaste	vnode_t *vp;
99296417Sdim	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(), 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
243bad:
244	VN_RELE(vp);
245	return (error);
246}
247