linux_getcwd.c revision 82518
182518Sgallatin/* $FreeBSD: head/sys/compat/linux/linux_getcwd.c 82518 2001-08-29 19:05:27Z gallatin $ */
282518Sgallatin/* $OpenBSD: linux_getcwd.c,v 1.2 2001/05/16 12:50:21 ho Exp $ */
382518Sgallatin/* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
482518Sgallatin
582518Sgallatin/*-
682518Sgallatin * Copyright (c) 1999 The NetBSD Foundation, Inc.
782518Sgallatin * All rights reserved.
882518Sgallatin *
982518Sgallatin * This code is derived from software contributed to The NetBSD Foundation
1082518Sgallatin * by Bill Sommerfeld.
1182518Sgallatin *
1282518Sgallatin * Redistribution and use in source and binary forms, with or without
1382518Sgallatin * modification, are permitted provided that the following conditions
1482518Sgallatin * are met:
1582518Sgallatin * 1. Redistributions of source code must retain the above copyright
1682518Sgallatin *    notice, this list of conditions and the following disclaimer.
1782518Sgallatin * 2. Redistributions in binary form must reproduce the above copyright
1882518Sgallatin *    notice, this list of conditions and the following disclaimer in the
1982518Sgallatin *    documentation and/or other materials provided with the distribution.
2082518Sgallatin * 3. All advertising materials mentioning features or use of this software
2182518Sgallatin *    must display the following acknowledgement:
2282518Sgallatin *        This product includes software developed by the NetBSD
2382518Sgallatin *        Foundation, Inc. and its contributors.
2482518Sgallatin * 4. Neither the name of The NetBSD Foundation nor the names of its
2582518Sgallatin *    contributors may be used to endorse or promote products derived
2682518Sgallatin *    from this software without specific prior written permission.
2782518Sgallatin *
2882518Sgallatin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2982518Sgallatin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
3082518Sgallatin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
3182518Sgallatin * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3282518Sgallatin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3382518Sgallatin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3482518Sgallatin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3582518Sgallatin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3682518Sgallatin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3782518Sgallatin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3882518Sgallatin * POSSIBILITY OF SUCH DAMAGE.
3982518Sgallatin */
4082518Sgallatin#include "opt_compat.h"
4182518Sgallatin
4282518Sgallatin#include <sys/param.h>
4382518Sgallatin#include <sys/systm.h>
4482518Sgallatin#include <sys/sysproto.h>
4582518Sgallatin#include <sys/namei.h>
4682518Sgallatin#include <sys/filedesc.h>
4782518Sgallatin#include <sys/kernel.h>
4882518Sgallatin#include <sys/file.h>
4982518Sgallatin#include <sys/stat.h>
5082518Sgallatin#include <sys/vnode.h>
5182518Sgallatin#include <sys/mount.h>
5282518Sgallatin#include <sys/proc.h>
5382518Sgallatin#include <sys/uio.h>
5482518Sgallatin#include <sys/malloc.h>
5582518Sgallatin#include <sys/dirent.h>
5682518Sgallatin#include <ufs/ufs/dir.h>	/* XXX only for DIRBLKSIZ */
5782518Sgallatin
5882518Sgallatin#include <machine/../linux/linux.h>
5982518Sgallatin#include <machine/../linux/linux_proto.h>
6082518Sgallatin#include <compat/linux/linux_util.h>
6182518Sgallatin
6282518Sgallatinstatic int
6382518Sgallatinlinux_getcwd_scandir __P((struct vnode **, struct vnode **,
6482518Sgallatin    char **, char *, struct proc *));
6582518Sgallatinstatic int
6682518Sgallatinlinux_getcwd_common __P((struct vnode *, struct vnode *,
6782518Sgallatin		   char **, char *, int, int, struct proc *));
6882518Sgallatin
6982518Sgallatin#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4)
7082518Sgallatin
7182518Sgallatin/*
7282518Sgallatin * Vnode variable naming conventions in this file:
7382518Sgallatin *
7482518Sgallatin * rvp: the current root we're aiming towards.
7582518Sgallatin * lvp, *lvpp: the "lower" vnode
7682518Sgallatin * uvp, *uvpp: the "upper" vnode.
7782518Sgallatin *
7882518Sgallatin * Since all the vnodes we're dealing with are directories, and the
7982518Sgallatin * lookups are going *up* in the filesystem rather than *down*, the
8082518Sgallatin * usual "pvp" (parent) or "dvp" (directory) naming conventions are
8182518Sgallatin * too confusing.
8282518Sgallatin */
8382518Sgallatin
8482518Sgallatin/*
8582518Sgallatin * XXX Will infinite loop in certain cases if a directory read reliably
8682518Sgallatin *	returns EINVAL on last block.
8782518Sgallatin * XXX is EINVAL the right thing to return if a directory is malformed?
8882518Sgallatin */
8982518Sgallatin
9082518Sgallatin/*
9182518Sgallatin * XXX Untested vs. mount -o union; probably does the wrong thing.
9282518Sgallatin */
9382518Sgallatin
9482518Sgallatin/*
9582518Sgallatin * Find parent vnode of *lvpp, return in *uvpp
9682518Sgallatin *
9782518Sgallatin * If we care about the name, scan it looking for name of directory
9882518Sgallatin * entry pointing at lvp.
9982518Sgallatin *
10082518Sgallatin * Place the name in the buffer which starts at bufp, immediately
10182518Sgallatin * before *bpp, and move bpp backwards to point at the start of it.
10282518Sgallatin *
10382518Sgallatin * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed
10482518Sgallatin * On exit, *uvpp is either NULL or is a locked vnode reference.
10582518Sgallatin */
10682518Sgallatinstatic int
10782518Sgallatinlinux_getcwd_scandir(lvpp, uvpp, bpp, bufp, p)
10882518Sgallatin	struct vnode **lvpp;
10982518Sgallatin	struct vnode **uvpp;
11082518Sgallatin	char **bpp;
11182518Sgallatin	char *bufp;
11282518Sgallatin	struct proc *p;
11382518Sgallatin{
11482518Sgallatin	int     error = 0;
11582518Sgallatin	int     eofflag;
11682518Sgallatin	off_t   off;
11782518Sgallatin	int     tries;
11882518Sgallatin	struct uio uio;
11982518Sgallatin	struct iovec iov;
12082518Sgallatin	char   *dirbuf = NULL;
12182518Sgallatin	int	dirbuflen;
12282518Sgallatin	ino_t   fileno;
12382518Sgallatin	struct vattr va;
12482518Sgallatin	struct vnode *uvp = NULL;
12582518Sgallatin	struct vnode *lvp = *lvpp;
12682518Sgallatin	struct componentname cn;
12782518Sgallatin	int len, reclen;
12882518Sgallatin	tries = 0;
12982518Sgallatin
13082518Sgallatin	/*
13182518Sgallatin	 * If we want the filename, get some info we need while the
13282518Sgallatin	 * current directory is still locked.
13382518Sgallatin	 */
13482518Sgallatin	if (bufp != NULL) {
13582518Sgallatin		error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
13682518Sgallatin		if (error) {
13782518Sgallatin			vput(lvp);
13882518Sgallatin			*lvpp = NULL;
13982518Sgallatin			*uvpp = NULL;
14082518Sgallatin			return error;
14182518Sgallatin		}
14282518Sgallatin	}
14382518Sgallatin
14482518Sgallatin	/*
14582518Sgallatin	 * Ok, we have to do it the hard way..
14682518Sgallatin	 * Next, get parent vnode using lookup of ..
14782518Sgallatin	 */
14882518Sgallatin	cn.cn_nameiop = LOOKUP;
14982518Sgallatin	cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
15082518Sgallatin	cn.cn_proc = p;
15182518Sgallatin	cn.cn_cred = p->p_ucred;
15282518Sgallatin	cn.cn_pnbuf = NULL;
15382518Sgallatin	cn.cn_nameptr = "..";
15482518Sgallatin	cn.cn_namelen = 2;
15582518Sgallatin	cn.cn_consume = 0;
15682518Sgallatin
15782518Sgallatin	/*
15882518Sgallatin	 * At this point, lvp is locked and will be unlocked by the lookup.
15982518Sgallatin	 * On successful return, *uvpp will be locked
16082518Sgallatin	 */
16182518Sgallatin	error = VOP_LOOKUP(lvp, uvpp, &cn);
16282518Sgallatin	if (error) {
16382518Sgallatin		vput(lvp);
16482518Sgallatin		*lvpp = NULL;
16582518Sgallatin		*uvpp = NULL;
16682518Sgallatin		return error;
16782518Sgallatin	}
16882518Sgallatin	uvp = *uvpp;
16982518Sgallatin
17082518Sgallatin	/* If we don't care about the pathname, we're done */
17182518Sgallatin	if (bufp == NULL) {
17282518Sgallatin		vrele(lvp);
17382518Sgallatin		*lvpp = NULL;
17482518Sgallatin		return 0;
17582518Sgallatin	}
17682518Sgallatin
17782518Sgallatin	fileno = va.va_fileid;
17882518Sgallatin
17982518Sgallatin	dirbuflen = DIRBLKSIZ;
18082518Sgallatin	if (dirbuflen < va.va_blocksize)
18182518Sgallatin		dirbuflen = va.va_blocksize;
18282518Sgallatin	dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK);
18382518Sgallatin
18482518Sgallatin#if 0
18582518Sgallatinunionread:
18682518Sgallatin#endif
18782518Sgallatin	off = 0;
18882518Sgallatin	do {
18982518Sgallatin		/* call VOP_READDIR of parent */
19082518Sgallatin		iov.iov_base = dirbuf;
19182518Sgallatin		iov.iov_len = dirbuflen;
19282518Sgallatin
19382518Sgallatin		uio.uio_iov = &iov;
19482518Sgallatin		uio.uio_iovcnt = 1;
19582518Sgallatin		uio.uio_offset = off;
19682518Sgallatin		uio.uio_resid = dirbuflen;
19782518Sgallatin		uio.uio_segflg = UIO_SYSSPACE;
19882518Sgallatin		uio.uio_rw = UIO_READ;
19982518Sgallatin		uio.uio_procp = p;
20082518Sgallatin
20182518Sgallatin		eofflag = 0;
20282518Sgallatin
20382518Sgallatin		error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag, 0, 0);
20482518Sgallatin
20582518Sgallatin		off = uio.uio_offset;
20682518Sgallatin
20782518Sgallatin		/*
20882518Sgallatin		 * Try again if NFS tosses its cookies.
20982518Sgallatin		 * XXX this can still loop forever if the directory is busted
21082518Sgallatin		 * such that the second or subsequent page of it always
21182518Sgallatin		 * returns EINVAL
21282518Sgallatin		 */
21382518Sgallatin		if ((error == EINVAL) && (tries < 3)) {
21482518Sgallatin			off = 0;
21582518Sgallatin			tries++;
21682518Sgallatin			continue;	/* once more, with feeling */
21782518Sgallatin		}
21882518Sgallatin
21982518Sgallatin		if (!error) {
22082518Sgallatin			char   *cpos;
22182518Sgallatin			struct dirent *dp;
22282518Sgallatin
22382518Sgallatin			cpos = dirbuf;
22482518Sgallatin			tries = 0;
22582518Sgallatin
22682518Sgallatin			/* scan directory page looking for matching vnode */
22782518Sgallatin			for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) {
22882518Sgallatin				dp = (struct dirent *) cpos;
22982518Sgallatin				reclen = dp->d_reclen;
23082518Sgallatin
23182518Sgallatin				/* check for malformed directory.. */
23282518Sgallatin				if (reclen < DIRENT_MINSIZE) {
23382518Sgallatin					error = EINVAL;
23482518Sgallatin					goto out;
23582518Sgallatin				}
23682518Sgallatin				/*
23782518Sgallatin				 * XXX should perhaps do VOP_LOOKUP to
23882518Sgallatin				 * check that we got back to the right place,
23982518Sgallatin				 * but getting the locking games for that
24082518Sgallatin				 * right would be heinous.
24182518Sgallatin				 */
24282518Sgallatin				if ((dp->d_type != DT_WHT) &&
24382518Sgallatin				    (dp->d_fileno == fileno)) {
24482518Sgallatin					char *bp = *bpp;
24582518Sgallatin					bp -= dp->d_namlen;
24682518Sgallatin
24782518Sgallatin					if (bp <= bufp) {
24882518Sgallatin						error = ERANGE;
24982518Sgallatin						goto out;
25082518Sgallatin					}
25182518Sgallatin					bcopy(dp->d_name, bp, dp->d_namlen);
25282518Sgallatin					error = 0;
25382518Sgallatin					*bpp = bp;
25482518Sgallatin					goto out;
25582518Sgallatin				}
25682518Sgallatin				cpos += reclen;
25782518Sgallatin			}
25882518Sgallatin		}
25982518Sgallatin	} while (!eofflag);
26082518Sgallatin	error = ENOENT;
26182518Sgallatin
26282518Sgallatinout:
26382518Sgallatin	vrele(lvp);
26482518Sgallatin	*lvpp = NULL;
26582518Sgallatin	free(dirbuf, M_TEMP);
26682518Sgallatin	return error;
26782518Sgallatin}
26882518Sgallatin
26982518Sgallatin
27082518Sgallatin/*
27182518Sgallatin * common routine shared by sys___getcwd() and linux_vn_isunder()
27282518Sgallatin */
27382518Sgallatin
27482518Sgallatin#define GETCWD_CHECK_ACCESS 0x0001
27582518Sgallatin
27682518Sgallatinstatic int
27782518Sgallatinlinux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, p)
27882518Sgallatin	struct vnode *lvp;
27982518Sgallatin	struct vnode *rvp;
28082518Sgallatin	char **bpp;
28182518Sgallatin	char *bufp;
28282518Sgallatin	int limit;
28382518Sgallatin	int flags;
28482518Sgallatin	struct proc *p;
28582518Sgallatin{
28682518Sgallatin	struct filedesc *fdp = p->p_fd;
28782518Sgallatin	struct vnode *uvp = NULL;
28882518Sgallatin	char *bp = NULL;
28982518Sgallatin	int error;
29082518Sgallatin	int perms = VEXEC;
29182518Sgallatin
29282518Sgallatin	if (rvp == NULL) {
29382518Sgallatin		rvp = fdp->fd_rdir;
29482518Sgallatin		if (rvp == NULL)
29582518Sgallatin			rvp = rootvnode;
29682518Sgallatin	}
29782518Sgallatin
29882518Sgallatin	VREF(rvp);
29982518Sgallatin	VREF(lvp);
30082518Sgallatin
30182518Sgallatin	/*
30282518Sgallatin	 * Error handling invariant:
30382518Sgallatin	 * Before a `goto out':
30482518Sgallatin	 *	lvp is either NULL, or locked and held.
30582518Sgallatin	 *	uvp is either NULL, or locked and held.
30682518Sgallatin	 */
30782518Sgallatin
30882518Sgallatin	error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
30982518Sgallatin	if (error) {
31082518Sgallatin		vrele(lvp);
31182518Sgallatin		lvp = NULL;
31282518Sgallatin		goto out;
31382518Sgallatin	}
31482518Sgallatin	if (bufp)
31582518Sgallatin		bp = *bpp;
31682518Sgallatin	/*
31782518Sgallatin	 * this loop will terminate when one of the following happens:
31882518Sgallatin	 *	- we hit the root
31982518Sgallatin	 *	- getdirentries or lookup fails
32082518Sgallatin	 *	- we run out of space in the buffer.
32182518Sgallatin	 */
32282518Sgallatin	if (lvp == rvp) {
32382518Sgallatin		if (bp)
32482518Sgallatin			*(--bp) = '/';
32582518Sgallatin		goto out;
32682518Sgallatin	}
32782518Sgallatin	do {
32882518Sgallatin		if (lvp->v_type != VDIR) {
32982518Sgallatin			error = ENOTDIR;
33082518Sgallatin			goto out;
33182518Sgallatin		}
33282518Sgallatin
33382518Sgallatin		/*
33482518Sgallatin		 * access check here is optional, depending on
33582518Sgallatin		 * whether or not caller cares.
33682518Sgallatin		 */
33782518Sgallatin		if (flags & GETCWD_CHECK_ACCESS) {
33882518Sgallatin			error = VOP_ACCESS(lvp, perms, p->p_ucred, p);
33982518Sgallatin			if (error)
34082518Sgallatin				goto out;
34182518Sgallatin			perms = VEXEC|VREAD;
34282518Sgallatin		}
34382518Sgallatin
34482518Sgallatin		/*
34582518Sgallatin		 * step up if we're a covered vnode..
34682518Sgallatin		 */
34782518Sgallatin		while (lvp->v_flag & VROOT) {
34882518Sgallatin			struct vnode *tvp;
34982518Sgallatin
35082518Sgallatin			if (lvp == rvp)
35182518Sgallatin				goto out;
35282518Sgallatin
35382518Sgallatin			tvp = lvp;
35482518Sgallatin			lvp = lvp->v_mount->mnt_vnodecovered;
35582518Sgallatin			vput(tvp);
35682518Sgallatin			/*
35782518Sgallatin			 * hodie natus est radici frater
35882518Sgallatin			 */
35982518Sgallatin			if (lvp == NULL) {
36082518Sgallatin				error = ENOENT;
36182518Sgallatin				goto out;
36282518Sgallatin			}
36382518Sgallatin			VREF(lvp);
36482518Sgallatin			error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
36582518Sgallatin			if (error != 0) {
36682518Sgallatin				vrele(lvp);
36782518Sgallatin				lvp = NULL;
36882518Sgallatin				goto out;
36982518Sgallatin			}
37082518Sgallatin		}
37182518Sgallatin		error = linux_getcwd_scandir(&lvp, &uvp, &bp, bufp, p);
37282518Sgallatin		if (error)
37382518Sgallatin			goto out;
37482518Sgallatin#if DIAGNOSTIC
37582518Sgallatin		if (lvp != NULL)
37682518Sgallatin			panic("getcwd: oops, forgot to null lvp");
37782518Sgallatin		if (bufp && (bp <= bufp)) {
37882518Sgallatin			panic("getcwd: oops, went back too far");
37982518Sgallatin		}
38082518Sgallatin#endif
38182518Sgallatin		if (bp)
38282518Sgallatin			*(--bp) = '/';
38382518Sgallatin		lvp = uvp;
38482518Sgallatin		uvp = NULL;
38582518Sgallatin		limit--;
38682518Sgallatin	} while ((lvp != rvp) && (limit > 0));
38782518Sgallatin
38882518Sgallatinout:
38982518Sgallatin	if (bpp)
39082518Sgallatin		*bpp = bp;
39182518Sgallatin	if (uvp)
39282518Sgallatin		vput(uvp);
39382518Sgallatin	if (lvp)
39482518Sgallatin		vput(lvp);
39582518Sgallatin	vrele(rvp);
39682518Sgallatin	return error;
39782518Sgallatin}
39882518Sgallatin
39982518Sgallatin
40082518Sgallatin/*
40182518Sgallatin * Find pathname of process's current directory.
40282518Sgallatin *
40382518Sgallatin * Use vfs vnode-to-name reverse cache; if that fails, fall back
40482518Sgallatin * to reading directory contents.
40582518Sgallatin */
40682518Sgallatin
40782518Sgallatinint
40882518Sgallatinlinux_getcwd(struct proc *p, struct linux_getcwd_args *args)
40982518Sgallatin{
41082518Sgallatin	struct __getcwd_args bsd;
41182518Sgallatin	caddr_t sg, bp, bend, path;
41282518Sgallatin	int error, len, lenused;
41382518Sgallatin
41482518Sgallatin#ifdef DEBUG
41582518Sgallatin	printf("Linux-emul(%ld): getcwd(%p, %ld)\n", (long)p->p_pid,
41682518Sgallatin	       args->buf, args->bufsize);
41782518Sgallatin#endif
41882518Sgallatin
41982518Sgallatin	sg = stackgap_init();
42082518Sgallatin	bsd.buf = stackgap_alloc(&sg, SPARE_USRSPACE);
42182518Sgallatin	bsd.buflen = SPARE_USRSPACE;
42282518Sgallatin	error = __getcwd(p, &bsd);
42382518Sgallatin	if (!error) {
42482518Sgallatin		lenused = strlen(bsd.buf) + 1;
42582518Sgallatin		if (lenused <= args->bufsize) {
42682518Sgallatin			p->p_retval[0] = lenused;
42782518Sgallatin			error = copyout(bsd.buf, args->buf, lenused);
42882518Sgallatin		}
42982518Sgallatin		else
43082518Sgallatin			error = ERANGE;
43182518Sgallatin	} else {
43282518Sgallatin		len = args->bufsize;
43382518Sgallatin
43482518Sgallatin		if (len > MAXPATHLEN*4)
43582518Sgallatin			len = MAXPATHLEN*4;
43682518Sgallatin		else if (len < 2)
43782518Sgallatin			return ERANGE;
43882518Sgallatin
43982518Sgallatin		path = (char *)malloc(len, M_TEMP, M_WAITOK);
44082518Sgallatin
44182518Sgallatin		bp = &path[len];
44282518Sgallatin		bend = bp;
44382518Sgallatin		*(--bp) = '\0';
44482518Sgallatin
44582518Sgallatin		/*
44682518Sgallatin		 * 5th argument here is "max number of vnodes to traverse".
44782518Sgallatin		 * Since each entry takes up at least 2 bytes in the output buffer,
44882518Sgallatin		 * limit it to N/2 vnodes for an N byte buffer.
44982518Sgallatin		 */
45082518Sgallatin
45182518Sgallatin		error = linux_getcwd_common (p->p_fd->fd_cdir, NULL,
45282518Sgallatin		    &bp, path, len/2, GETCWD_CHECK_ACCESS, p);
45382518Sgallatin
45482518Sgallatin		if (error)
45582518Sgallatin			goto out;
45682518Sgallatin		lenused = bend - bp;
45782518Sgallatin		p->p_retval[0] = lenused;
45882518Sgallatin		/* put the result into user buffer */
45982518Sgallatin		error = copyout(bp, args->buf, lenused);
46082518Sgallatin
46182518Sgallatinout:
46282518Sgallatin		free(path, M_TEMP);
46382518Sgallatin	}
46482518Sgallatin	return (error);
46582518Sgallatin}
46682518Sgallatin
467