vfs_getcwd.c revision 1.47
1/* $NetBSD: vfs_getcwd.c,v 1.47 2010/11/30 10:30:02 dholland Exp $ */
2
3/*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Bill Sommerfeld.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: vfs_getcwd.c,v 1.47 2010/11/30 10:30:02 dholland Exp $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/namei.h>
38#include <sys/filedesc.h>
39#include <sys/kernel.h>
40#include <sys/file.h>
41#include <sys/stat.h>
42#include <sys/vnode.h>
43#include <sys/mount.h>
44#include <sys/proc.h>
45#include <sys/uio.h>
46#include <sys/kmem.h>
47#include <sys/dirent.h>
48#include <sys/kauth.h>
49
50#include <ufs/ufs/dir.h>	/* XXX only for DIRBLKSIZ */
51
52#include <sys/syscallargs.h>
53
54/*
55 * Vnode variable naming conventions in this file:
56 *
57 * rvp: the current root we're aiming towards.
58 * lvp, *lvpp: the "lower" vnode
59 * uvp, *uvpp: the "upper" vnode.
60 *
61 * Since all the vnodes we're dealing with are directories, and the
62 * lookups are going *up* in the filesystem rather than *down*, the
63 * usual "pvp" (parent) or "dvp" (directory) naming conventions are
64 * too confusing.
65 */
66
67/*
68 * XXX Will infinite loop in certain cases if a directory read reliably
69 *	returns EINVAL on last block.
70 * XXX is EINVAL the right thing to return if a directory is malformed?
71 */
72
73/*
74 * XXX Untested vs. mount -o union; probably does the wrong thing.
75 */
76
77/*
78 * Find parent vnode of *lvpp, return in *uvpp
79 *
80 * If we care about the name, scan it looking for name of directory
81 * entry pointing at lvp.
82 *
83 * Place the name in the buffer which starts at bufp, immediately
84 * before *bpp, and move bpp backwards to point at the start of it.
85 *
86 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed
87 * On exit, *uvpp is either NULL or is a locked vnode reference.
88 */
89static int
90getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
91    char *bufp, struct lwp *l)
92{
93	int     error = 0;
94	int     eofflag;
95	off_t   off;
96	int     tries;
97	struct uio uio;
98	struct iovec iov;
99	char   *dirbuf = NULL;
100	int	dirbuflen;
101	ino_t   fileno;
102	struct vattr va;
103	struct vnode *uvp = NULL;
104	struct vnode *lvp = *lvpp;
105	kauth_cred_t cred = l->l_cred;
106	struct componentname cn;
107	int len, reclen;
108	tries = 0;
109
110	/*
111	 * If we want the filename, get some info we need while the
112	 * current directory is still locked.
113	 */
114	if (bufp != NULL) {
115		error = VOP_GETATTR(lvp, &va, cred);
116		if (error) {
117			vput(lvp);
118			*lvpp = NULL;
119			*uvpp = NULL;
120			return error;
121		}
122	}
123
124	/*
125	 * Ok, we have to do it the hard way..
126	 * Next, get parent vnode using lookup of ..
127	 */
128	cn.cn_nameiop = LOOKUP;
129	cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
130	cn.cn_cred = cred;
131	cn.cn_nameptr = "..";
132	cn.cn_namelen = 2;
133	cn.cn_hash = 0;
134	cn.cn_consume = 0;
135
136	/*
137	 * At this point, lvp is locked.
138	 * On successful return, *uvpp will be locked
139	 */
140	error = VOP_LOOKUP(lvp, uvpp, &cn);
141	vput(lvp);
142	if (error) {
143		*lvpp = NULL;
144		*uvpp = NULL;
145		return error;
146	}
147	uvp = *uvpp;
148
149	/* If we don't care about the pathname, we're done */
150	if (bufp == NULL) {
151		*lvpp = NULL;
152		return 0;
153	}
154
155	fileno = va.va_fileid;
156
157	dirbuflen = DIRBLKSIZ;
158	if (dirbuflen < va.va_blocksize)
159		dirbuflen = va.va_blocksize;
160	dirbuf = kmem_alloc(dirbuflen, KM_SLEEP);
161
162#if 0
163unionread:
164#endif
165	off = 0;
166	do {
167		/* call VOP_READDIR of parent */
168		iov.iov_base = dirbuf;
169		iov.iov_len = dirbuflen;
170
171		uio.uio_iov = &iov;
172		uio.uio_iovcnt = 1;
173		uio.uio_offset = off;
174		uio.uio_resid = dirbuflen;
175		uio.uio_rw = UIO_READ;
176		UIO_SETUP_SYSSPACE(&uio);
177
178		eofflag = 0;
179
180		error = VOP_READDIR(uvp, &uio, cred, &eofflag, 0, 0);
181
182		off = uio.uio_offset;
183
184		/*
185		 * Try again if NFS tosses its cookies.
186		 * XXX this can still loop forever if the directory is busted
187		 * such that the second or subsequent page of it always
188		 * returns EINVAL
189		 */
190		if ((error == EINVAL) && (tries < 3)) {
191			off = 0;
192			tries++;
193			continue;	/* once more, with feeling */
194		}
195
196		if (!error) {
197			char   *cpos;
198			struct dirent *dp;
199
200			cpos = dirbuf;
201			tries = 0;
202
203			/* scan directory page looking for matching vnode */
204			for (len = (dirbuflen - uio.uio_resid); len > 0;
205			    len -= reclen) {
206				dp = (struct dirent *) cpos;
207				reclen = dp->d_reclen;
208
209				/* check for malformed directory.. */
210				if (reclen < _DIRENT_MINSIZE(dp)) {
211					error = EINVAL;
212					goto out;
213				}
214				/*
215				 * XXX should perhaps do VOP_LOOKUP to
216				 * check that we got back to the right place,
217				 * but getting the locking games for that
218				 * right would be heinous.
219				 */
220				if ((dp->d_type != DT_WHT) &&
221				    (dp->d_fileno == fileno)) {
222					char *bp = *bpp;
223
224					bp -= dp->d_namlen;
225					if (bp <= bufp) {
226						error = ERANGE;
227						goto out;
228					}
229					memcpy(bp, dp->d_name, dp->d_namlen);
230					error = 0;
231					*bpp = bp;
232					goto out;
233				}
234				cpos += reclen;
235			}
236		} else
237			goto out;
238	} while (!eofflag);
239#if 0
240	/*
241	 * Deal with mount -o union, which unions only the
242	 * root directory of the mount.
243	 */
244	if ((uvp->v_vflag & VV_ROOT) &&
245	    (uvp->v_mount->mnt_flag & MNT_UNION)) {
246		struct vnode *tvp = uvp;
247
248		uvp = uvp->v_mount->mnt_vnodecovered;
249		vput(tvp);
250		vref(uvp);
251		*uvpp = uvp;
252		vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY);
253		goto unionread;
254	}
255#endif
256	error = ENOENT;
257
258out:
259	*lvpp = NULL;
260	kmem_free(dirbuf, dirbuflen);
261	return error;
262}
263
264/*
265 * Look in the vnode-to-name reverse cache to see if
266 * we can find things the easy way.
267 *
268 * XXX vget failure path is untested.
269 *
270 * On entry, *lvpp is a locked vnode reference.
271 * On exit, one of the following is the case:
272 *	0) Both *lvpp and *uvpp are NULL and failure is returned.
273 * 	1) *uvpp is NULL, *lvpp remains locked and -1 is returned (cache miss)
274 *	2) *uvpp is a locked vnode reference, *lvpp is vput and NULL'ed
275 *	   and 0 is returned (cache hit)
276 */
277
278static int
279getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
280    char *bufp)
281{
282	struct vnode *lvp, *uvp = NULL;
283	int error;
284
285	lvp = *lvpp;
286
287	/*
288	 * This returns 0 on a cache hit, -1 on a clean cache miss,
289	 * or an errno on other failure.
290	 */
291	error = cache_revlookup(lvp, uvpp, bpp, bufp);
292	if (error) {
293		if (error != -1) {
294			vput(lvp);
295			*lvpp = NULL;
296			*uvpp = NULL;
297		}
298		return error;
299	}
300	uvp = *uvpp;
301
302	/*
303	 * Since we're going up, we have to release the current lock
304	 * before we take the parent lock.
305	 */
306
307	VOP_UNLOCK(lvp);
308	vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY);
309	vrele(lvp);
310	*lvpp = NULL;
311
312	return error;
313}
314
315/*
316 * common routine shared by sys___getcwd() and vn_isunder()
317 */
318
319int
320getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
321    int limit, int flags, struct lwp *l)
322{
323	struct cwdinfo *cwdi = l->l_proc->p_cwdi;
324	kauth_cred_t cred = l->l_cred;
325	struct vnode *uvp = NULL;
326	char *bp = NULL;
327	int error;
328	int perms = VEXEC;
329
330	error = 0;
331	if (rvp == NULL) {
332		rvp = cwdi->cwdi_rdir;
333		if (rvp == NULL)
334			rvp = rootvnode;
335	}
336
337	vref(rvp);
338	vref(lvp);
339
340	/*
341	 * Error handling invariant:
342	 * Before a `goto out':
343	 *	lvp is either NULL, or locked and held.
344	 *	uvp is either NULL, or locked and held.
345	 */
346
347	vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
348	if (bufp)
349		bp = *bpp;
350
351	/*
352	 * this loop will terminate when one of the following happens:
353	 *	- we hit the root
354	 *	- getdirentries or lookup fails
355	 *	- we run out of space in the buffer.
356	 */
357	if (lvp == rvp) {
358		if (bp)
359			*(--bp) = '/';
360		goto out;
361	}
362	do {
363		/*
364		 * access check here is optional, depending on
365		 * whether or not caller cares.
366		 */
367		if (flags & GETCWD_CHECK_ACCESS) {
368			error = VOP_ACCESS(lvp, perms, cred);
369			if (error)
370				goto out;
371			perms = VEXEC|VREAD;
372		}
373
374		/*
375		 * step up if we're a covered vnode..
376		 */
377		while (lvp->v_vflag & VV_ROOT) {
378			struct vnode *tvp;
379
380			if (lvp == rvp)
381				goto out;
382
383			tvp = lvp;
384			lvp = lvp->v_mount->mnt_vnodecovered;
385			vput(tvp);
386			/*
387			 * hodie natus est radici frater
388			 */
389			if (lvp == NULL) {
390				error = ENOENT;
391				goto out;
392			}
393			vref(lvp);
394			error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
395			if (error != 0) {
396				vrele(lvp);
397				lvp = NULL;
398				goto out;
399			}
400		}
401		/*
402		 * Look in the name cache; if that fails, look in the
403		 * directory..
404		 */
405		error = getcwd_getcache(&lvp, &uvp, &bp, bufp);
406		if (error == -1) {
407			if (lvp->v_type != VDIR) {
408				error = ENOTDIR;
409				goto out;
410			}
411			error = getcwd_scandir(&lvp, &uvp, &bp, bufp, l);
412		}
413		if (error)
414			goto out;
415#if DIAGNOSTIC
416		if (lvp != NULL)
417			panic("getcwd: oops, forgot to null lvp");
418		if (bufp && (bp <= bufp)) {
419			panic("getcwd: oops, went back too far");
420		}
421#endif
422		if (bp)
423			*(--bp) = '/';
424		lvp = uvp;
425		uvp = NULL;
426		limit--;
427	} while ((lvp != rvp) && (limit > 0));
428
429out:
430	if (bpp)
431		*bpp = bp;
432	if (uvp)
433		vput(uvp);
434	if (lvp)
435		vput(lvp);
436	vrele(rvp);
437	return error;
438}
439
440/*
441 * Check if one directory can be found inside another in the directory
442 * hierarchy.
443 *
444 * Intended to be used in chroot, chdir, fchdir, etc., to ensure that
445 * chroot() actually means something.
446 */
447int
448vn_isunder(struct vnode *lvp, struct vnode *rvp, struct lwp *l)
449{
450	int error;
451
452	error = getcwd_common(lvp, rvp, NULL, NULL, MAXPATHLEN / 2, 0, l);
453
454	if (!error)
455		return 1;
456	else
457		return 0;
458}
459
460/*
461 * Returns true if proc p1's root directory equal to or under p2's
462 * root directory.
463 *
464 * Intended to be used from ptrace/procfs sorts of things.
465 */
466
467int
468proc_isunder(struct proc *p1, struct lwp *l2)
469{
470	struct vnode *r1 = p1->p_cwdi->cwdi_rdir;
471	struct vnode *r2 = l2->l_proc->p_cwdi->cwdi_rdir;
472
473	if (r1 == NULL)
474		return (r2 == NULL);
475	else if (r2 == NULL)
476		return 1;
477	else
478		return vn_isunder(r1, r2, l2);
479}
480
481/*
482 * Find pathname of process's current directory.
483 *
484 * Use vfs vnode-to-name reverse cache; if that fails, fall back
485 * to reading directory contents.
486 */
487
488int
489sys___getcwd(struct lwp *l, const struct sys___getcwd_args *uap, register_t *retval)
490{
491	/* {
492		syscallarg(char *) bufp;
493		syscallarg(size_t) length;
494	} */
495
496	int     error;
497	char   *path;
498	char   *bp, *bend;
499	int     len = SCARG(uap, length);
500	int	lenused;
501	struct	cwdinfo *cwdi;
502
503	if (len > MAXPATHLEN * 4)
504		len = MAXPATHLEN * 4;
505	else if (len < 2)
506		return ERANGE;
507
508	path = kmem_alloc(len, KM_SLEEP);
509	if (!path)
510		return ENOMEM;
511
512	bp = &path[len];
513	bend = bp;
514	*(--bp) = '\0';
515
516	/*
517	 * 5th argument here is "max number of vnodes to traverse".
518	 * Since each entry takes up at least 2 bytes in the output buffer,
519	 * limit it to N/2 vnodes for an N byte buffer.
520	 */
521	cwdi = l->l_proc->p_cwdi;
522	rw_enter(&cwdi->cwdi_lock, RW_READER);
523	error = getcwd_common(cwdi->cwdi_cdir, NULL, &bp, path,
524	    len/2, GETCWD_CHECK_ACCESS, l);
525	rw_exit(&cwdi->cwdi_lock);
526
527	if (error)
528		goto out;
529	lenused = bend - bp;
530	*retval = lenused;
531	/* put the result into user buffer */
532	error = copyout(bp, SCARG(uap, bufp), lenused);
533
534out:
535	kmem_free(path, len);
536	return error;
537}
538
539/*
540 * Try to find a pathname for a vnode. Since there is no mapping
541 * vnode -> parent directory, this needs the NAMECACHE_ENTER_REVERSE
542 * option to work (to make cache_revlookup succeed). Caller holds a
543 * reference to the vnode.
544 */
545int
546vnode_to_path(char *path, size_t len, struct vnode *vp, struct lwp *curl,
547    struct proc *p)
548{
549	struct proc *curp = curl->l_proc;
550	int error, lenused, elen;
551	char *bp, *bend;
552	struct vnode *dvp;
553
554	KASSERT(vp->v_usecount > 0);
555
556	bp = bend = &path[len];
557	*(--bp) = '\0';
558
559	error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
560	if (error != 0)
561		return error;
562	error = cache_revlookup(vp, &dvp, &bp, path);
563	VOP_UNLOCK(vp);
564	if (error != 0)
565		return (error == -1 ? ENOENT : error);
566
567	*(--bp) = '/';
568	error = getcwd_common(dvp, NULL, &bp, path, len / 2,
569	    GETCWD_CHECK_ACCESS, curl);
570	vrele(dvp);
571
572	/*
573	 * Strip off emulation path for emulated processes looking at
574	 * the maps file of a process of the same emulation. (Won't
575	 * work if /emul/xxx is a symlink..)
576	 */
577	if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) {
578		elen = strlen(curp->p_emul->e_path);
579		if (!strncmp(bp, curp->p_emul->e_path, elen))
580			bp = &bp[elen];
581	}
582
583	lenused = bend - bp;
584
585	memcpy(path, bp, lenused);
586	path[lenused] = 0;
587
588	return 0;
589}
590