linux_getcwd.c revision 144290
182518Sgallatin/* $OpenBSD: linux_getcwd.c,v 1.2 2001/05/16 12:50:21 ho Exp $ */ 282518Sgallatin/* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */ 382518Sgallatin/*- 482518Sgallatin * Copyright (c) 1999 The NetBSD Foundation, Inc. 582518Sgallatin * All rights reserved. 682518Sgallatin * 782518Sgallatin * This code is derived from software contributed to The NetBSD Foundation 882518Sgallatin * by Bill Sommerfeld. 982518Sgallatin * 1082518Sgallatin * Redistribution and use in source and binary forms, with or without 1182518Sgallatin * modification, are permitted provided that the following conditions 1282518Sgallatin * are met: 1382518Sgallatin * 1. Redistributions of source code must retain the above copyright 1482518Sgallatin * notice, this list of conditions and the following disclaimer. 1582518Sgallatin * 2. Redistributions in binary form must reproduce the above copyright 1682518Sgallatin * notice, this list of conditions and the following disclaimer in the 1782518Sgallatin * documentation and/or other materials provided with the distribution. 1882518Sgallatin * 3. All advertising materials mentioning features or use of this software 1982518Sgallatin * must display the following acknowledgement: 2082518Sgallatin * This product includes software developed by the NetBSD 2182518Sgallatin * Foundation, Inc. and its contributors. 2282518Sgallatin * 4. Neither the name of The NetBSD Foundation nor the names of its 2382518Sgallatin * contributors may be used to endorse or promote products derived 2482518Sgallatin * from this software without specific prior written permission. 2582518Sgallatin * 2682518Sgallatin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2782518Sgallatin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2882518Sgallatin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2982518Sgallatin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 3082518Sgallatin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3182518Sgallatin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3282518Sgallatin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3382518Sgallatin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3482518Sgallatin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3582518Sgallatin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3682518Sgallatin * POSSIBILITY OF SUCH DAMAGE. 3782518Sgallatin */ 38116173Sobrien 39116173Sobrien#include <sys/cdefs.h> 40116173Sobrien__FBSDID("$FreeBSD: head/sys/compat/linux/linux_getcwd.c 144290 2005-03-29 10:16:12Z jeff $"); 41116173Sobrien 42112430Sphk#include "opt_compat.h" 43112430Sphk#include "opt_mac.h" 4482518Sgallatin 4582518Sgallatin#include <sys/param.h> 4682518Sgallatin#include <sys/systm.h> 47112430Sphk#include <sys/namei.h> 48112430Sphk#include <sys/filedesc.h> 49112430Sphk#include <sys/kernel.h> 50112430Sphk#include <sys/file.h> 51112430Sphk#include <sys/stat.h> 52102872Siedowse#include <sys/syscallsubr.h> 53112430Sphk#include <sys/vnode.h> 54112430Sphk#include <sys/mount.h> 5582518Sgallatin#include <sys/proc.h> 56112430Sphk#include <sys/uio.h> 57112430Sphk#include <sys/mac.h> 58112430Sphk#include <sys/malloc.h> 59112430Sphk#include <sys/dirent.h> 60112430Sphk#include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 6182518Sgallatin 62133816Stjr#include "opt_compat.h" 63133816Stjr 64140214Sobrien#ifdef COMPAT_LINUX32 65140214Sobrien#include <machine/../linux32/linux.h> 66140214Sobrien#include <machine/../linux32/linux32_proto.h> 67140214Sobrien#else 6882518Sgallatin#include <machine/../linux/linux.h> 6982518Sgallatin#include <machine/../linux/linux_proto.h> 70133816Stjr#endif 71112430Sphk#include <compat/linux/linux_util.h> 7282518Sgallatin 73112430Sphkstatic int 74112430Sphklinux_getcwd_scandir(struct vnode **, struct vnode **, 75112430Sphk char **, char *, struct thread *); 76112430Sphkstatic int 77112430Sphklinux_getcwd_common(struct vnode *, struct vnode *, 78112430Sphk char **, char *, int, int, struct thread *); 79112430Sphk 80112430Sphk#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4) 81112430Sphk 8282518Sgallatin/* 83112430Sphk * Vnode variable naming conventions in this file: 84112430Sphk * 85112430Sphk * rvp: the current root we're aiming towards. 86112430Sphk * lvp, *lvpp: the "lower" vnode 87112430Sphk * uvp, *uvpp: the "upper" vnode. 88112430Sphk * 89112430Sphk * Since all the vnodes we're dealing with are directories, and the 90112430Sphk * lookups are going *up* in the filesystem rather than *down*, the 91112430Sphk * usual "pvp" (parent) or "dvp" (directory) naming conventions are 92112430Sphk * too confusing. 93112430Sphk */ 94112430Sphk 95112430Sphk/* 96112430Sphk * XXX Will infinite loop in certain cases if a directory read reliably 97112430Sphk * returns EINVAL on last block. 98112430Sphk * XXX is EINVAL the right thing to return if a directory is malformed? 99112430Sphk */ 100112430Sphk 101112430Sphk/* 102112430Sphk * XXX Untested vs. mount -o union; probably does the wrong thing. 103112430Sphk */ 104112430Sphk 105112430Sphk/* 106112430Sphk * Find parent vnode of *lvpp, return in *uvpp 107112430Sphk * 108112430Sphk * If we care about the name, scan it looking for name of directory 109112430Sphk * entry pointing at lvp. 110112430Sphk * 111112430Sphk * Place the name in the buffer which starts at bufp, immediately 112112430Sphk * before *bpp, and move bpp backwards to point at the start of it. 113112430Sphk * 114112430Sphk * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 115112430Sphk * On exit, *uvpp is either NULL or is a locked vnode reference. 116112430Sphk */ 117112430Sphkstatic int 118112430Sphklinux_getcwd_scandir(lvpp, uvpp, bpp, bufp, td) 119112430Sphk struct vnode **lvpp; 120112430Sphk struct vnode **uvpp; 121112430Sphk char **bpp; 122112430Sphk char *bufp; 123112430Sphk struct thread *td; 124112430Sphk{ 125112430Sphk int error = 0; 126112430Sphk int eofflag; 127112430Sphk off_t off; 128112430Sphk int tries; 129112430Sphk struct uio uio; 130112430Sphk struct iovec iov; 131112430Sphk char *dirbuf = NULL; 132112430Sphk int dirbuflen; 133112430Sphk ino_t fileno; 134112430Sphk struct vattr va; 135112430Sphk struct vnode *uvp = NULL; 136112430Sphk struct vnode *lvp = *lvpp; 137112430Sphk struct componentname cn; 138112430Sphk int len, reclen; 139112430Sphk tries = 0; 140112430Sphk 141112430Sphk /* 142112430Sphk * If we want the filename, get some info we need while the 143112430Sphk * current directory is still locked. 144112430Sphk */ 145112430Sphk if (bufp != NULL) { 146112430Sphk error = VOP_GETATTR(lvp, &va, td->td_ucred, td); 147112430Sphk if (error) { 148112430Sphk vput(lvp); 149112430Sphk *lvpp = NULL; 150112430Sphk *uvpp = NULL; 151112430Sphk return error; 152112430Sphk } 153112430Sphk } 154112430Sphk 155112430Sphk /* 156112430Sphk * Ok, we have to do it the hard way.. 157112430Sphk * Next, get parent vnode using lookup of .. 158112430Sphk */ 159112430Sphk cn.cn_nameiop = LOOKUP; 160112430Sphk cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 161112430Sphk cn.cn_thread = td; 162112430Sphk cn.cn_cred = td->td_ucred; 163112430Sphk cn.cn_pnbuf = NULL; 164112430Sphk cn.cn_nameptr = ".."; 165112430Sphk cn.cn_namelen = 2; 166112430Sphk cn.cn_consume = 0; 167144290Sjeff cn.cn_lkflags = LK_EXCLUSIVE; 168112430Sphk 169112430Sphk /* 170112430Sphk * At this point, lvp is locked and will be unlocked by the lookup. 171112430Sphk * On successful return, *uvpp will be locked 172112430Sphk */ 173122861Srwatson#ifdef MAC 174122861Srwatson error = mac_check_vnode_lookup(td->td_ucred, lvp, &cn); 175122861Srwatson if (error == 0) 176122861Srwatson#endif 177122861Srwatson error = VOP_LOOKUP(lvp, uvpp, &cn); 178112430Sphk if (error) { 179112430Sphk vput(lvp); 180112430Sphk *lvpp = NULL; 181112430Sphk *uvpp = NULL; 182112430Sphk return error; 183112430Sphk } 184112430Sphk uvp = *uvpp; 185112430Sphk 186112430Sphk /* If we don't care about the pathname, we're done */ 187112430Sphk if (bufp == NULL) { 188112430Sphk vrele(lvp); 189112430Sphk *lvpp = NULL; 190112430Sphk return 0; 191112430Sphk } 192112430Sphk 193112430Sphk fileno = va.va_fileid; 194112430Sphk 195112430Sphk dirbuflen = DIRBLKSIZ; 196112430Sphk if (dirbuflen < va.va_blocksize) 197112430Sphk dirbuflen = va.va_blocksize; 198112430Sphk dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); 199112430Sphk 200112430Sphk#if 0 201112430Sphkunionread: 202112430Sphk#endif 203112430Sphk off = 0; 204112430Sphk do { 205112430Sphk /* call VOP_READDIR of parent */ 206112430Sphk iov.iov_base = dirbuf; 207112430Sphk iov.iov_len = dirbuflen; 208112430Sphk 209112430Sphk uio.uio_iov = &iov; 210112430Sphk uio.uio_iovcnt = 1; 211112430Sphk uio.uio_offset = off; 212112430Sphk uio.uio_resid = dirbuflen; 213112430Sphk uio.uio_segflg = UIO_SYSSPACE; 214112430Sphk uio.uio_rw = UIO_READ; 215112430Sphk uio.uio_td = td; 216112430Sphk 217112430Sphk eofflag = 0; 218112430Sphk 219112430Sphk#ifdef MAC 220112430Sphk error = mac_check_vnode_readdir(td->td_ucred, uvp); 221112430Sphk if (error == 0) 222112430Sphk#endif /* MAC */ 223112430Sphk error = VOP_READDIR(uvp, &uio, td->td_ucred, &eofflag, 224112430Sphk 0, 0); 225112430Sphk 226112430Sphk off = uio.uio_offset; 227112430Sphk 228112430Sphk /* 229112430Sphk * Try again if NFS tosses its cookies. 230112430Sphk * XXX this can still loop forever if the directory is busted 231112430Sphk * such that the second or subsequent page of it always 232112430Sphk * returns EINVAL 233112430Sphk */ 234112430Sphk if ((error == EINVAL) && (tries < 3)) { 235112430Sphk off = 0; 236112430Sphk tries++; 237112430Sphk continue; /* once more, with feeling */ 238112430Sphk } 239112430Sphk 240112430Sphk if (!error) { 241112430Sphk char *cpos; 242112430Sphk struct dirent *dp; 243112430Sphk 244112430Sphk cpos = dirbuf; 245112430Sphk tries = 0; 246112430Sphk 247112430Sphk /* scan directory page looking for matching vnode */ 248112430Sphk for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) { 249112430Sphk dp = (struct dirent *) cpos; 250112430Sphk reclen = dp->d_reclen; 251112430Sphk 252112430Sphk /* check for malformed directory.. */ 253112430Sphk if (reclen < DIRENT_MINSIZE) { 254112430Sphk error = EINVAL; 255112430Sphk goto out; 256112430Sphk } 257112430Sphk /* 258112430Sphk * XXX should perhaps do VOP_LOOKUP to 259112430Sphk * check that we got back to the right place, 260112430Sphk * but getting the locking games for that 261112430Sphk * right would be heinous. 262112430Sphk */ 263112430Sphk if ((dp->d_type != DT_WHT) && 264112430Sphk (dp->d_fileno == fileno)) { 265112430Sphk char *bp = *bpp; 266112430Sphk bp -= dp->d_namlen; 267112430Sphk 268112430Sphk if (bp <= bufp) { 269112430Sphk error = ERANGE; 270112430Sphk goto out; 271112430Sphk } 272112430Sphk bcopy(dp->d_name, bp, dp->d_namlen); 273112430Sphk error = 0; 274112430Sphk *bpp = bp; 275112430Sphk goto out; 276112430Sphk } 277112430Sphk cpos += reclen; 278112430Sphk } 279112430Sphk } 280112430Sphk } while (!eofflag); 281112430Sphk error = ENOENT; 282112430Sphk 283112430Sphkout: 284112430Sphk vrele(lvp); 285112430Sphk *lvpp = NULL; 286112430Sphk free(dirbuf, M_TEMP); 287112430Sphk return error; 288112430Sphk} 289112430Sphk 290112430Sphk 291112430Sphk/* 292112430Sphk * common routine shared by sys___getcwd() and linux_vn_isunder() 293112430Sphk */ 294112430Sphk 295112430Sphk#define GETCWD_CHECK_ACCESS 0x0001 296112430Sphk 297112430Sphkstatic int 298112430Sphklinux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, td) 299112430Sphk struct vnode *lvp; 300112430Sphk struct vnode *rvp; 301112430Sphk char **bpp; 302112430Sphk char *bufp; 303112430Sphk int limit; 304112430Sphk int flags; 305112430Sphk struct thread *td; 306112430Sphk{ 307112430Sphk struct filedesc *fdp = td->td_proc->p_fd; 308112430Sphk struct vnode *uvp = NULL; 309112430Sphk char *bp = NULL; 310112430Sphk int error; 311112430Sphk int perms = VEXEC; 312112430Sphk 313112430Sphk if (rvp == NULL) { 314112430Sphk rvp = fdp->fd_rdir; 315112430Sphk if (rvp == NULL) 316112430Sphk rvp = rootvnode; 317112430Sphk } 318112430Sphk 319112430Sphk VREF(rvp); 320112430Sphk VREF(lvp); 321112430Sphk 322112430Sphk /* 323112430Sphk * Error handling invariant: 324112430Sphk * Before a `goto out': 325112430Sphk * lvp is either NULL, or locked and held. 326112430Sphk * uvp is either NULL, or locked and held. 327112430Sphk */ 328112430Sphk 329112430Sphk error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td); 330112430Sphk if (error) { 331112430Sphk vrele(lvp); 332112430Sphk lvp = NULL; 333112430Sphk goto out; 334112430Sphk } 335112430Sphk if (bufp) 336112430Sphk bp = *bpp; 337112430Sphk /* 338112430Sphk * this loop will terminate when one of the following happens: 339112430Sphk * - we hit the root 340112430Sphk * - getdirentries or lookup fails 341112430Sphk * - we run out of space in the buffer. 342112430Sphk */ 343112430Sphk if (lvp == rvp) { 344112430Sphk if (bp) 345112430Sphk *(--bp) = '/'; 346112430Sphk goto out; 347112430Sphk } 348112430Sphk do { 349112430Sphk if (lvp->v_type != VDIR) { 350112430Sphk error = ENOTDIR; 351112430Sphk goto out; 352112430Sphk } 353112430Sphk 354112430Sphk /* 355112430Sphk * access check here is optional, depending on 356112430Sphk * whether or not caller cares. 357112430Sphk */ 358112430Sphk if (flags & GETCWD_CHECK_ACCESS) { 359112430Sphk error = VOP_ACCESS(lvp, perms, td->td_ucred, td); 360112430Sphk if (error) 361112430Sphk goto out; 362112430Sphk perms = VEXEC|VREAD; 363112430Sphk } 364112430Sphk 365112430Sphk /* 366112430Sphk * step up if we're a covered vnode.. 367112430Sphk */ 368112430Sphk while (lvp->v_vflag & VV_ROOT) { 369112430Sphk struct vnode *tvp; 370112430Sphk 371112430Sphk if (lvp == rvp) 372112430Sphk goto out; 373112430Sphk 374112430Sphk tvp = lvp; 375112430Sphk lvp = lvp->v_mount->mnt_vnodecovered; 376112430Sphk vput(tvp); 377112430Sphk /* 378112430Sphk * hodie natus est radici frater 379112430Sphk */ 380112430Sphk if (lvp == NULL) { 381112430Sphk error = ENOENT; 382112430Sphk goto out; 383112430Sphk } 384112430Sphk VREF(lvp); 385112430Sphk error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td); 386112430Sphk if (error != 0) { 387112430Sphk vrele(lvp); 388112430Sphk lvp = NULL; 389112430Sphk goto out; 390112430Sphk } 391112430Sphk } 392112430Sphk error = linux_getcwd_scandir(&lvp, &uvp, &bp, bufp, td); 393112430Sphk if (error) 394112430Sphk goto out; 395112430Sphk#if DIAGNOSTIC 396112430Sphk if (lvp != NULL) 397112430Sphk panic("getcwd: oops, forgot to null lvp"); 398112430Sphk if (bufp && (bp <= bufp)) { 399112430Sphk panic("getcwd: oops, went back too far"); 400112430Sphk } 401112430Sphk#endif 402112430Sphk if (bp) 403112430Sphk *(--bp) = '/'; 404112430Sphk lvp = uvp; 405112430Sphk uvp = NULL; 406112430Sphk limit--; 407112430Sphk } while ((lvp != rvp) && (limit > 0)); 408112430Sphk 409112430Sphkout: 410112430Sphk if (bpp) 411112430Sphk *bpp = bp; 412112430Sphk if (uvp) 413112430Sphk vput(uvp); 414112430Sphk if (lvp) 415112430Sphk vput(lvp); 416112430Sphk vrele(rvp); 417112430Sphk return error; 418112430Sphk} 419112430Sphk 420112430Sphk 421112430Sphk/* 42282518Sgallatin * Find pathname of process's current directory. 42382518Sgallatin * 42482518Sgallatin * Use vfs vnode-to-name reverse cache; if that fails, fall back 425112430Sphk * to reading directory contents. 42682518Sgallatin */ 42782518Sgallatin 42882518Sgallatinint 42983366Sjulianlinux_getcwd(struct thread *td, struct linux_getcwd_args *args) 43082518Sgallatin{ 431112430Sphk caddr_t bp, bend, path; 432112430Sphk int error, len, lenused; 433112430Sphk 434112430Sphk#ifdef DEBUG 435112430Sphk printf("Linux-emul(%ld): getcwd(%p, %ld)\n", (long)td->td_proc->p_pid, 436112430Sphk args->buf, (long)args->bufsize); 437112430Sphk#endif 438112430Sphk 439112430Sphk len = args->bufsize; 440112430Sphk 441112430Sphk if (len > MAXPATHLEN*4) 442112430Sphk len = MAXPATHLEN*4; 443112430Sphk else if (len < 2) 444112430Sphk return ERANGE; 445112430Sphk 446112430Sphk path = (char *)malloc(len, M_TEMP, M_WAITOK); 447112430Sphk 448112430Sphk error = kern___getcwd(td, path, UIO_SYSSPACE, len); 449112430Sphk if (!error) { 450112430Sphk lenused = strlen(path) + 1; 451112430Sphk if (lenused <= args->bufsize) { 452112430Sphk td->td_retval[0] = lenused; 453112430Sphk error = copyout(path, args->buf, lenused); 454112430Sphk } 455112430Sphk else 456112430Sphk error = ERANGE; 457112430Sphk } else { 458112430Sphk bp = &path[len]; 459112430Sphk bend = bp; 460112430Sphk *(--bp) = '\0'; 461112430Sphk 462112430Sphk /* 463112430Sphk * 5th argument here is "max number of vnodes to traverse". 464112430Sphk * Since each entry takes up at least 2 bytes in the output buffer, 465112430Sphk * limit it to N/2 vnodes for an N byte buffer. 466112430Sphk */ 467112430Sphk 468112430Sphk error = linux_getcwd_common (td->td_proc->p_fd->fd_cdir, NULL, 469112430Sphk &bp, path, len/2, GETCWD_CHECK_ACCESS, td); 470112430Sphk 471112430Sphk if (error) 472112430Sphk goto out; 473112430Sphk lenused = bend - bp; 474112430Sphk td->td_retval[0] = lenused; 475112430Sphk /* put the result into user buffer */ 476112430Sphk error = copyout(bp, args->buf, lenused); 477112430Sphk } 478112430Sphkout: 479112430Sphk free(path, M_TEMP); 480112430Sphk return (error); 48182518Sgallatin} 48282518Sgallatin 483