linux_getcwd.c revision 102872
182518Sgallatin/* $FreeBSD: head/sys/compat/linux/linux_getcwd.c 102872 2002-09-02 22:46:05Z iedowse $ */ 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" 41101189Srwatson#include "opt_mac.h" 4282518Sgallatin 4382518Sgallatin#include <sys/param.h> 4482518Sgallatin#include <sys/systm.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> 50102872Siedowse#include <sys/syscallsubr.h> 5182518Sgallatin#include <sys/vnode.h> 5282518Sgallatin#include <sys/mount.h> 5382518Sgallatin#include <sys/proc.h> 5482518Sgallatin#include <sys/uio.h> 55101189Srwatson#include <sys/mac.h> 5682518Sgallatin#include <sys/malloc.h> 5782518Sgallatin#include <sys/dirent.h> 5882518Sgallatin#include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 5982518Sgallatin 6082518Sgallatin#include <machine/../linux/linux.h> 6182518Sgallatin#include <machine/../linux/linux_proto.h> 6282518Sgallatin#include <compat/linux/linux_util.h> 6382518Sgallatin 6482518Sgallatinstatic int 6592761Salfredlinux_getcwd_scandir(struct vnode **, struct vnode **, 6692761Salfred char **, char *, struct thread *); 6782518Sgallatinstatic int 6892761Salfredlinux_getcwd_common(struct vnode *, struct vnode *, 6992761Salfred char **, char *, int, int, struct thread *); 7082518Sgallatin 7182518Sgallatin#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4) 7282518Sgallatin 7382518Sgallatin/* 7482518Sgallatin * Vnode variable naming conventions in this file: 7582518Sgallatin * 7682518Sgallatin * rvp: the current root we're aiming towards. 7782518Sgallatin * lvp, *lvpp: the "lower" vnode 7882518Sgallatin * uvp, *uvpp: the "upper" vnode. 7982518Sgallatin * 8082518Sgallatin * Since all the vnodes we're dealing with are directories, and the 8182518Sgallatin * lookups are going *up* in the filesystem rather than *down*, the 8282518Sgallatin * usual "pvp" (parent) or "dvp" (directory) naming conventions are 8382518Sgallatin * too confusing. 8482518Sgallatin */ 8582518Sgallatin 8682518Sgallatin/* 8782518Sgallatin * XXX Will infinite loop in certain cases if a directory read reliably 8882518Sgallatin * returns EINVAL on last block. 8982518Sgallatin * XXX is EINVAL the right thing to return if a directory is malformed? 9082518Sgallatin */ 9182518Sgallatin 9282518Sgallatin/* 9382518Sgallatin * XXX Untested vs. mount -o union; probably does the wrong thing. 9482518Sgallatin */ 9582518Sgallatin 9682518Sgallatin/* 9782518Sgallatin * Find parent vnode of *lvpp, return in *uvpp 9882518Sgallatin * 9982518Sgallatin * If we care about the name, scan it looking for name of directory 10082518Sgallatin * entry pointing at lvp. 10182518Sgallatin * 10282518Sgallatin * Place the name in the buffer which starts at bufp, immediately 10382518Sgallatin * before *bpp, and move bpp backwards to point at the start of it. 10482518Sgallatin * 10582518Sgallatin * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 10682518Sgallatin * On exit, *uvpp is either NULL or is a locked vnode reference. 10782518Sgallatin */ 10882518Sgallatinstatic int 10983366Sjulianlinux_getcwd_scandir(lvpp, uvpp, bpp, bufp, td) 11082518Sgallatin struct vnode **lvpp; 11182518Sgallatin struct vnode **uvpp; 11282518Sgallatin char **bpp; 11382518Sgallatin char *bufp; 11483366Sjulian struct thread *td; 11582518Sgallatin{ 11682518Sgallatin int error = 0; 11782518Sgallatin int eofflag; 11882518Sgallatin off_t off; 11982518Sgallatin int tries; 12082518Sgallatin struct uio uio; 12182518Sgallatin struct iovec iov; 12282518Sgallatin char *dirbuf = NULL; 12382518Sgallatin int dirbuflen; 12482518Sgallatin ino_t fileno; 12582518Sgallatin struct vattr va; 12682518Sgallatin struct vnode *uvp = NULL; 12782518Sgallatin struct vnode *lvp = *lvpp; 12882518Sgallatin struct componentname cn; 12982518Sgallatin int len, reclen; 13082518Sgallatin tries = 0; 13182518Sgallatin 13282518Sgallatin /* 13382518Sgallatin * If we want the filename, get some info we need while the 13482518Sgallatin * current directory is still locked. 13582518Sgallatin */ 13682518Sgallatin if (bufp != NULL) { 13791406Sjhb error = VOP_GETATTR(lvp, &va, td->td_ucred, td); 13882518Sgallatin if (error) { 13982518Sgallatin vput(lvp); 14082518Sgallatin *lvpp = NULL; 14182518Sgallatin *uvpp = NULL; 14282518Sgallatin return error; 14382518Sgallatin } 14482518Sgallatin } 14582518Sgallatin 14682518Sgallatin /* 14782518Sgallatin * Ok, we have to do it the hard way.. 14882518Sgallatin * Next, get parent vnode using lookup of .. 14982518Sgallatin */ 15082518Sgallatin cn.cn_nameiop = LOOKUP; 15182518Sgallatin cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 15283366Sjulian cn.cn_thread = td; 15391406Sjhb cn.cn_cred = td->td_ucred; 15482518Sgallatin cn.cn_pnbuf = NULL; 15582518Sgallatin cn.cn_nameptr = ".."; 15682518Sgallatin cn.cn_namelen = 2; 15782518Sgallatin cn.cn_consume = 0; 15882518Sgallatin 15982518Sgallatin /* 16082518Sgallatin * At this point, lvp is locked and will be unlocked by the lookup. 16182518Sgallatin * On successful return, *uvpp will be locked 16282518Sgallatin */ 16382518Sgallatin error = VOP_LOOKUP(lvp, uvpp, &cn); 16482518Sgallatin if (error) { 16582518Sgallatin vput(lvp); 16682518Sgallatin *lvpp = NULL; 16782518Sgallatin *uvpp = NULL; 16882518Sgallatin return error; 16982518Sgallatin } 17082518Sgallatin uvp = *uvpp; 17182518Sgallatin 17282518Sgallatin /* If we don't care about the pathname, we're done */ 17382518Sgallatin if (bufp == NULL) { 17482518Sgallatin vrele(lvp); 17582518Sgallatin *lvpp = NULL; 17682518Sgallatin return 0; 17782518Sgallatin } 17882518Sgallatin 17982518Sgallatin fileno = va.va_fileid; 18082518Sgallatin 18182518Sgallatin dirbuflen = DIRBLKSIZ; 18282518Sgallatin if (dirbuflen < va.va_blocksize) 18382518Sgallatin dirbuflen = va.va_blocksize; 18482518Sgallatin dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); 18582518Sgallatin 18682518Sgallatin#if 0 18782518Sgallatinunionread: 18882518Sgallatin#endif 18982518Sgallatin off = 0; 19082518Sgallatin do { 19182518Sgallatin /* call VOP_READDIR of parent */ 19282518Sgallatin iov.iov_base = dirbuf; 19382518Sgallatin iov.iov_len = dirbuflen; 19482518Sgallatin 19582518Sgallatin uio.uio_iov = &iov; 19682518Sgallatin uio.uio_iovcnt = 1; 19782518Sgallatin uio.uio_offset = off; 19882518Sgallatin uio.uio_resid = dirbuflen; 19982518Sgallatin uio.uio_segflg = UIO_SYSSPACE; 20082518Sgallatin uio.uio_rw = UIO_READ; 20183366Sjulian uio.uio_td = td; 20282518Sgallatin 20382518Sgallatin eofflag = 0; 20482518Sgallatin 205101189Srwatson#ifdef MAC 206101189Srwatson error = mac_check_vnode_readdir(td->td_ucred, uvp); 207101189Srwatson if (error == 0) 208101189Srwatson#endif /* MAC */ 209101189Srwatson error = VOP_READDIR(uvp, &uio, td->td_ucred, &eofflag, 210101189Srwatson 0, 0); 21182518Sgallatin 21282518Sgallatin off = uio.uio_offset; 21382518Sgallatin 21482518Sgallatin /* 21582518Sgallatin * Try again if NFS tosses its cookies. 21682518Sgallatin * XXX this can still loop forever if the directory is busted 21782518Sgallatin * such that the second or subsequent page of it always 21882518Sgallatin * returns EINVAL 21982518Sgallatin */ 22082518Sgallatin if ((error == EINVAL) && (tries < 3)) { 22182518Sgallatin off = 0; 22282518Sgallatin tries++; 22382518Sgallatin continue; /* once more, with feeling */ 22482518Sgallatin } 22582518Sgallatin 22682518Sgallatin if (!error) { 22782518Sgallatin char *cpos; 22882518Sgallatin struct dirent *dp; 22982518Sgallatin 23082518Sgallatin cpos = dirbuf; 23182518Sgallatin tries = 0; 23282518Sgallatin 23382518Sgallatin /* scan directory page looking for matching vnode */ 23482518Sgallatin for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) { 23582518Sgallatin dp = (struct dirent *) cpos; 23682518Sgallatin reclen = dp->d_reclen; 23782518Sgallatin 23882518Sgallatin /* check for malformed directory.. */ 23982518Sgallatin if (reclen < DIRENT_MINSIZE) { 24082518Sgallatin error = EINVAL; 24182518Sgallatin goto out; 24282518Sgallatin } 24382518Sgallatin /* 24482518Sgallatin * XXX should perhaps do VOP_LOOKUP to 24582518Sgallatin * check that we got back to the right place, 24682518Sgallatin * but getting the locking games for that 24782518Sgallatin * right would be heinous. 24882518Sgallatin */ 24982518Sgallatin if ((dp->d_type != DT_WHT) && 25082518Sgallatin (dp->d_fileno == fileno)) { 25182518Sgallatin char *bp = *bpp; 25282518Sgallatin bp -= dp->d_namlen; 25382518Sgallatin 25482518Sgallatin if (bp <= bufp) { 25582518Sgallatin error = ERANGE; 25682518Sgallatin goto out; 25782518Sgallatin } 25882518Sgallatin bcopy(dp->d_name, bp, dp->d_namlen); 25982518Sgallatin error = 0; 26082518Sgallatin *bpp = bp; 26182518Sgallatin goto out; 26282518Sgallatin } 26382518Sgallatin cpos += reclen; 26482518Sgallatin } 26582518Sgallatin } 26682518Sgallatin } while (!eofflag); 26782518Sgallatin error = ENOENT; 26882518Sgallatin 26982518Sgallatinout: 27082518Sgallatin vrele(lvp); 27182518Sgallatin *lvpp = NULL; 27282518Sgallatin free(dirbuf, M_TEMP); 27382518Sgallatin return error; 27482518Sgallatin} 27582518Sgallatin 27682518Sgallatin 27782518Sgallatin/* 27882518Sgallatin * common routine shared by sys___getcwd() and linux_vn_isunder() 27982518Sgallatin */ 28082518Sgallatin 28182518Sgallatin#define GETCWD_CHECK_ACCESS 0x0001 28282518Sgallatin 28382518Sgallatinstatic int 28483366Sjulianlinux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, td) 28582518Sgallatin struct vnode *lvp; 28682518Sgallatin struct vnode *rvp; 28782518Sgallatin char **bpp; 28882518Sgallatin char *bufp; 28982518Sgallatin int limit; 29082518Sgallatin int flags; 29183366Sjulian struct thread *td; 29282518Sgallatin{ 29383366Sjulian struct filedesc *fdp = td->td_proc->p_fd; 29482518Sgallatin struct vnode *uvp = NULL; 29582518Sgallatin char *bp = NULL; 29682518Sgallatin int error; 29782518Sgallatin int perms = VEXEC; 29882518Sgallatin 29982518Sgallatin if (rvp == NULL) { 30082518Sgallatin rvp = fdp->fd_rdir; 30182518Sgallatin if (rvp == NULL) 30282518Sgallatin rvp = rootvnode; 30382518Sgallatin } 30482518Sgallatin 30582518Sgallatin VREF(rvp); 30682518Sgallatin VREF(lvp); 30782518Sgallatin 30882518Sgallatin /* 30982518Sgallatin * Error handling invariant: 31082518Sgallatin * Before a `goto out': 31182518Sgallatin * lvp is either NULL, or locked and held. 31282518Sgallatin * uvp is either NULL, or locked and held. 31382518Sgallatin */ 31482518Sgallatin 31583366Sjulian error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td); 31682518Sgallatin if (error) { 31782518Sgallatin vrele(lvp); 31882518Sgallatin lvp = NULL; 31982518Sgallatin goto out; 32082518Sgallatin } 32182518Sgallatin if (bufp) 32282518Sgallatin bp = *bpp; 32382518Sgallatin /* 32482518Sgallatin * this loop will terminate when one of the following happens: 32582518Sgallatin * - we hit the root 32682518Sgallatin * - getdirentries or lookup fails 32782518Sgallatin * - we run out of space in the buffer. 32882518Sgallatin */ 32982518Sgallatin if (lvp == rvp) { 33082518Sgallatin if (bp) 33182518Sgallatin *(--bp) = '/'; 33282518Sgallatin goto out; 33382518Sgallatin } 33482518Sgallatin do { 33582518Sgallatin if (lvp->v_type != VDIR) { 33682518Sgallatin error = ENOTDIR; 33782518Sgallatin goto out; 33882518Sgallatin } 33982518Sgallatin 34082518Sgallatin /* 34182518Sgallatin * access check here is optional, depending on 34282518Sgallatin * whether or not caller cares. 34382518Sgallatin */ 34482518Sgallatin if (flags & GETCWD_CHECK_ACCESS) { 34591406Sjhb error = VOP_ACCESS(lvp, perms, td->td_ucred, td); 34682518Sgallatin if (error) 34782518Sgallatin goto out; 34882518Sgallatin perms = VEXEC|VREAD; 34982518Sgallatin } 35082518Sgallatin 35182518Sgallatin /* 35282518Sgallatin * step up if we're a covered vnode.. 35382518Sgallatin */ 354101308Sjeff while (lvp->v_vflag & VV_ROOT) { 35582518Sgallatin struct vnode *tvp; 35682518Sgallatin 35782518Sgallatin if (lvp == rvp) 35882518Sgallatin goto out; 35982518Sgallatin 36082518Sgallatin tvp = lvp; 36182518Sgallatin lvp = lvp->v_mount->mnt_vnodecovered; 36282518Sgallatin vput(tvp); 36382518Sgallatin /* 36482518Sgallatin * hodie natus est radici frater 36582518Sgallatin */ 36682518Sgallatin if (lvp == NULL) { 36782518Sgallatin error = ENOENT; 36882518Sgallatin goto out; 36982518Sgallatin } 37082518Sgallatin VREF(lvp); 37183366Sjulian error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td); 37282518Sgallatin if (error != 0) { 37382518Sgallatin vrele(lvp); 37482518Sgallatin lvp = NULL; 37582518Sgallatin goto out; 37682518Sgallatin } 37782518Sgallatin } 37883366Sjulian error = linux_getcwd_scandir(&lvp, &uvp, &bp, bufp, td); 37982518Sgallatin if (error) 38082518Sgallatin goto out; 38182518Sgallatin#if DIAGNOSTIC 38282518Sgallatin if (lvp != NULL) 38382518Sgallatin panic("getcwd: oops, forgot to null lvp"); 38482518Sgallatin if (bufp && (bp <= bufp)) { 38582518Sgallatin panic("getcwd: oops, went back too far"); 38682518Sgallatin } 38782518Sgallatin#endif 38882518Sgallatin if (bp) 38982518Sgallatin *(--bp) = '/'; 39082518Sgallatin lvp = uvp; 39182518Sgallatin uvp = NULL; 39282518Sgallatin limit--; 39382518Sgallatin } while ((lvp != rvp) && (limit > 0)); 39482518Sgallatin 39582518Sgallatinout: 39682518Sgallatin if (bpp) 39782518Sgallatin *bpp = bp; 39882518Sgallatin if (uvp) 39982518Sgallatin vput(uvp); 40082518Sgallatin if (lvp) 40182518Sgallatin vput(lvp); 40282518Sgallatin vrele(rvp); 40382518Sgallatin return error; 40482518Sgallatin} 40582518Sgallatin 40682518Sgallatin 40782518Sgallatin/* 40882518Sgallatin * Find pathname of process's current directory. 40982518Sgallatin * 41082518Sgallatin * Use vfs vnode-to-name reverse cache; if that fails, fall back 41182518Sgallatin * to reading directory contents. 41282518Sgallatin */ 41382518Sgallatin 41482518Sgallatinint 41583366Sjulianlinux_getcwd(struct thread *td, struct linux_getcwd_args *args) 41682518Sgallatin{ 417102872Siedowse caddr_t bp, bend, path; 41882518Sgallatin int error, len, lenused; 41982518Sgallatin 42082518Sgallatin#ifdef DEBUG 42183366Sjulian printf("Linux-emul(%ld): getcwd(%p, %ld)\n", (long)td->td_proc->p_pid, 42286482Speter args->buf, (long)args->bufsize); 42382518Sgallatin#endif 42482518Sgallatin 425102872Siedowse len = args->bufsize; 426102872Siedowse 427102872Siedowse if (len > MAXPATHLEN*4) 428102872Siedowse len = MAXPATHLEN*4; 429102872Siedowse else if (len < 2) 430102872Siedowse return ERANGE; 431102872Siedowse 432102872Siedowse path = (char *)malloc(len, M_TEMP, M_WAITOK); 433102872Siedowse 434102872Siedowse error = kern___getcwd(td, path, UIO_SYSSPACE, len); 43582518Sgallatin if (!error) { 436102872Siedowse lenused = strlen(path) + 1; 43782518Sgallatin if (lenused <= args->bufsize) { 43883366Sjulian td->td_retval[0] = lenused; 439102872Siedowse error = copyout(path, args->buf, lenused); 44082518Sgallatin } 44182518Sgallatin else 44282518Sgallatin error = ERANGE; 44382518Sgallatin } else { 44482518Sgallatin bp = &path[len]; 44582518Sgallatin bend = bp; 44682518Sgallatin *(--bp) = '\0'; 44782518Sgallatin 44882518Sgallatin /* 44982518Sgallatin * 5th argument here is "max number of vnodes to traverse". 45082518Sgallatin * Since each entry takes up at least 2 bytes in the output buffer, 45182518Sgallatin * limit it to N/2 vnodes for an N byte buffer. 45282518Sgallatin */ 45382518Sgallatin 45483366Sjulian error = linux_getcwd_common (td->td_proc->p_fd->fd_cdir, NULL, 45583366Sjulian &bp, path, len/2, GETCWD_CHECK_ACCESS, td); 45682518Sgallatin 45782518Sgallatin if (error) 45882518Sgallatin goto out; 45982518Sgallatin lenused = bend - bp; 46083366Sjulian td->td_retval[0] = lenused; 46182518Sgallatin /* put the result into user buffer */ 46282518Sgallatin error = copyout(bp, args->buf, lenused); 463102872Siedowse } 46482518Sgallatinout: 465102872Siedowse free(path, M_TEMP); 46682518Sgallatin return (error); 46782518Sgallatin} 46882518Sgallatin 469