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