linux_getcwd.c revision 83366
169783Smsmith/* $FreeBSD: head/sys/compat/linux/linux_getcwd.c 83366 2001-09-12 08:38:13Z julian $ */ 2223520Sjhb/* $OpenBSD: linux_getcwd.c,v 1.2 2001/05/16 12:50:21 ho Exp $ */ 3223520Sjhb/* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */ 469783Smsmith 569783Smsmith/*- 669783Smsmith * Copyright (c) 1999 The NetBSD Foundation, Inc. 769783Smsmith * All rights reserved. 869783Smsmith * 969783Smsmith * This code is derived from software contributed to The NetBSD Foundation 1069783Smsmith * by Bill Sommerfeld. 1169783Smsmith * 1269783Smsmith * Redistribution and use in source and binary forms, with or without 1369783Smsmith * modification, are permitted provided that the following conditions 1469783Smsmith * are met: 1569783Smsmith * 1. Redistributions of source code must retain the above copyright 1669783Smsmith * notice, this list of conditions and the following disclaimer. 1769783Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1869783Smsmith * notice, this list of conditions and the following disclaimer in the 1969783Smsmith * documentation and/or other materials provided with the distribution. 2069783Smsmith * 3. All advertising materials mentioning features or use of this software 2169783Smsmith * must display the following acknowledgement: 2269783Smsmith * This product includes software developed by the NetBSD 2369783Smsmith * Foundation, Inc. and its contributors. 2469783Smsmith * 4. Neither the name of The NetBSD Foundation nor the names of its 2569783Smsmith * contributors may be used to endorse or promote products derived 2669783Smsmith * from this software without specific prior written permission. 2769783Smsmith * 28119418Sobrien * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 29119418Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 30119418Sobrien * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 3169783Smsmith * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 32223520Sjhb * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33223520Sjhb * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3469783Smsmith * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3569783Smsmith * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36224069Sjhb * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37221393Sjhb * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38107546Simp * POSSIBILITY OF SUCH DAMAGE. 39224069Sjhb */ 4069783Smsmith#include "opt_compat.h" 41223520Sjhb 42119285Simp#include <sys/param.h> 43119285Simp#include <sys/systm.h> 4469783Smsmith#include <sys/sysproto.h> 4569783Smsmith#include <sys/namei.h> 46107172Sjhb#include <sys/filedesc.h> 47107172Sjhb#include <sys/kernel.h> 48107172Sjhb#include <sys/file.h> 49107172Sjhb#include <sys/stat.h> 50107172Sjhb#include <sys/vnode.h> 51119266Simp#include <sys/mount.h> 52107172Sjhb#include <sys/proc.h> 53119266Simp#include <sys/uio.h> 54107172Sjhb#include <sys/malloc.h> 55107172Sjhb#include <sys/dirent.h> 56107248Sjhb#include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 57107172Sjhb 58107172Sjhb#include <machine/../linux/linux.h> 59107172Sjhb#include <machine/../linux/linux_proto.h> 60107172Sjhb#include <compat/linux/linux_util.h> 61107172Sjhb 62107172Sjhbstatic int 63107172Sjhblinux_getcwd_scandir __P((struct vnode **, struct vnode **, 64107172Sjhb char **, char *, struct thread *)); 65107172Sjhbstatic int 66107172Sjhblinux_getcwd_common __P((struct vnode *, struct vnode *, 67107172Sjhb char **, char *, int, int, struct thread *)); 68107172Sjhb 69107172Sjhb#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4) 70107172Sjhb 71107172Sjhb/* 72107172Sjhb * Vnode variable naming conventions in this file: 73107172Sjhb * 74107172Sjhb * rvp: the current root we're aiming towards. 75107172Sjhb * lvp, *lvpp: the "lower" vnode 76107172Sjhb * uvp, *uvpp: the "upper" vnode. 77107172Sjhb * 78107172Sjhb * Since all the vnodes we're dealing with are directories, and the 79107172Sjhb * lookups are going *up* in the filesystem rather than *down*, the 80107172Sjhb * usual "pvp" (parent) or "dvp" (directory) naming conventions are 81107172Sjhb * too confusing. 82107172Sjhb */ 83107172Sjhb 84107172Sjhb/* 85107172Sjhb * XXX Will infinite loop in certain cases if a directory read reliably 86107172Sjhb * returns EINVAL on last block. 87107248Sjhb * XXX is EINVAL the right thing to return if a directory is malformed? 88107172Sjhb */ 89107172Sjhb 90107172Sjhb/* 91107248Sjhb * XXX Untested vs. mount -o union; probably does the wrong thing. 92107172Sjhb */ 93107172Sjhb 94107172Sjhb/* 95107248Sjhb * Find parent vnode of *lvpp, return in *uvpp 96107172Sjhb * 97107172Sjhb * If we care about the name, scan it looking for name of directory 98107172Sjhb * entry pointing at lvp. 99107248Sjhb * 100107172Sjhb * Place the name in the buffer which starts at bufp, immediately 101107172Sjhb * before *bpp, and move bpp backwards to point at the start of it. 102107172Sjhb * 103107172Sjhb * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 104107172Sjhb * On exit, *uvpp is either NULL or is a locked vnode reference. 105107172Sjhb */ 106107172Sjhbstatic int 107107172Sjhblinux_getcwd_scandir(lvpp, uvpp, bpp, bufp, td) 108107172Sjhb struct vnode **lvpp; 109107172Sjhb struct vnode **uvpp; 110107172Sjhb char **bpp; 111107172Sjhb char *bufp; 112107172Sjhb struct thread *td; 113107172Sjhb{ 114215820Sjhb int error = 0; 115107172Sjhb int eofflag; 116215820Sjhb off_t off; 117107172Sjhb int tries; 118107172Sjhb struct uio uio; 119107172Sjhb struct iovec iov; 120144110Sjhb char *dirbuf = NULL; 121144110Sjhb int dirbuflen; 122144110Sjhb ino_t fileno; 123144110Sjhb struct vattr va; 124144110Sjhb struct vnode *uvp = NULL; 125107172Sjhb struct vnode *lvp = *lvpp; 126107172Sjhb struct componentname cn; 127107172Sjhb int len, reclen; 128107172Sjhb tries = 0; 129107172Sjhb 130107172Sjhb /* 131107172Sjhb * If we want the filename, get some info we need while the 132224069Sjhb * current directory is still locked. 133224069Sjhb */ 134224069Sjhb if (bufp != NULL) { 135224069Sjhb error = VOP_GETATTR(lvp, &va, td->td_proc->p_ucred, td); 136224069Sjhb if (error) { 137224069Sjhb vput(lvp); 138224069Sjhb *lvpp = NULL; 139224069Sjhb *uvpp = NULL; 140224069Sjhb return error; 141224069Sjhb } 142224069Sjhb } 143224069Sjhb 144224069Sjhb /* 145224069Sjhb * Ok, we have to do it the hard way.. 146224069Sjhb * Next, get parent vnode using lookup of .. 147224069Sjhb */ 148224069Sjhb cn.cn_nameiop = LOOKUP; 149224069Sjhb cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 150224069Sjhb cn.cn_thread = td; 151224069Sjhb cn.cn_cred = td->td_proc->p_ucred; 152224069Sjhb cn.cn_pnbuf = NULL; 153224069Sjhb cn.cn_nameptr = ".."; 154224069Sjhb cn.cn_namelen = 2; 155224069Sjhb cn.cn_consume = 0; 156224069Sjhb 157224069Sjhb /* 158224069Sjhb * At this point, lvp is locked and will be unlocked by the lookup. 159224069Sjhb * On successful return, *uvpp will be locked 160224069Sjhb */ 161224069Sjhb error = VOP_LOOKUP(lvp, uvpp, &cn); 162224069Sjhb if (error) { 163224069Sjhb vput(lvp); 164224069Sjhb *lvpp = NULL; 165224069Sjhb *uvpp = NULL; 166224069Sjhb return error; 167224069Sjhb } 168224069Sjhb uvp = *uvpp; 169224069Sjhb 170224069Sjhb /* If we don't care about the pathname, we're done */ 171224069Sjhb if (bufp == NULL) { 172224069Sjhb vrele(lvp); 173224069Sjhb *lvpp = NULL; 174224069Sjhb return 0; 175224069Sjhb } 176224069Sjhb 177224069Sjhb fileno = va.va_fileid; 178224069Sjhb 179224069Sjhb dirbuflen = DIRBLKSIZ; 180224069Sjhb if (dirbuflen < va.va_blocksize) 181224069Sjhb dirbuflen = va.va_blocksize; 182224069Sjhb dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); 183224069Sjhb 184224069Sjhb#if 0 185224069Sjhbunionread: 186224069Sjhb#endif 187224069Sjhb off = 0; 188224069Sjhb do { 189224069Sjhb /* call VOP_READDIR of parent */ 190224069Sjhb iov.iov_base = dirbuf; 191224069Sjhb iov.iov_len = dirbuflen; 192224069Sjhb 193224069Sjhb uio.uio_iov = &iov; 194224069Sjhb uio.uio_iovcnt = 1; 195224069Sjhb uio.uio_offset = off; 196224069Sjhb uio.uio_resid = dirbuflen; 197224069Sjhb uio.uio_segflg = UIO_SYSSPACE; 198224069Sjhb uio.uio_rw = UIO_READ; 199224069Sjhb uio.uio_td = td; 200224069Sjhb 201224069Sjhb eofflag = 0; 202224069Sjhb 203224069Sjhb error = VOP_READDIR(uvp, &uio, td->td_proc->p_ucred, &eofflag, 0, 0); 204224069Sjhb 205224069Sjhb off = uio.uio_offset; 206224069Sjhb 207224069Sjhb /* 208224069Sjhb * Try again if NFS tosses its cookies. 209224069Sjhb * XXX this can still loop forever if the directory is busted 210224069Sjhb * such that the second or subsequent page of it always 211224069Sjhb * returns EINVAL 212224069Sjhb */ 213224069Sjhb if ((error == EINVAL) && (tries < 3)) { 214224069Sjhb off = 0; 215224069Sjhb tries++; 216224069Sjhb continue; /* once more, with feeling */ 217224069Sjhb } 218224069Sjhb 219224069Sjhb if (!error) { 220224069Sjhb char *cpos; 221224069Sjhb struct dirent *dp; 222224069Sjhb 223224069Sjhb cpos = dirbuf; 224224069Sjhb tries = 0; 225224069Sjhb 226224069Sjhb /* scan directory page looking for matching vnode */ 227224069Sjhb for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) { 228224069Sjhb dp = (struct dirent *) cpos; 229224069Sjhb reclen = dp->d_reclen; 230224069Sjhb 231224069Sjhb /* check for malformed directory.. */ 232224069Sjhb if (reclen < DIRENT_MINSIZE) { 233224069Sjhb error = EINVAL; 234224069Sjhb goto out; 235224069Sjhb } 236224069Sjhb /* 237224069Sjhb * XXX should perhaps do VOP_LOOKUP to 238224069Sjhb * check that we got back to the right place, 239224069Sjhb * but getting the locking games for that 240224069Sjhb * right would be heinous. 241224069Sjhb */ 242224069Sjhb if ((dp->d_type != DT_WHT) && 243224069Sjhb (dp->d_fileno == fileno)) { 244224069Sjhb char *bp = *bpp; 245224069Sjhb bp -= dp->d_namlen; 246224069Sjhb 247224069Sjhb if (bp <= bufp) { 248224069Sjhb error = ERANGE; 249224069Sjhb goto out; 250224069Sjhb } 251224069Sjhb bcopy(dp->d_name, bp, dp->d_namlen); 252224069Sjhb error = 0; 253224069Sjhb *bpp = bp; 254224069Sjhb goto out; 255224069Sjhb } 256224069Sjhb cpos += reclen; 257224069Sjhb } 258224069Sjhb } 259224069Sjhb } while (!eofflag); 260224069Sjhb error = ENOENT; 261224069Sjhb 262224069Sjhbout: 263224069Sjhb vrele(lvp); 264224069Sjhb *lvpp = NULL; 265224069Sjhb free(dirbuf, M_TEMP); 266224069Sjhb return error; 267224069Sjhb} 268224069Sjhb 269224069Sjhb 270224069Sjhb/* 271224069Sjhb * common routine shared by sys___getcwd() and linux_vn_isunder() 272224069Sjhb */ 273224069Sjhb 274224069Sjhb#define GETCWD_CHECK_ACCESS 0x0001 275224069Sjhb 276224069Sjhbstatic int 277224069Sjhblinux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, td) 278224069Sjhb struct vnode *lvp; 279224069Sjhb struct vnode *rvp; 280224069Sjhb char **bpp; 281224069Sjhb char *bufp; 282224069Sjhb int limit; 283224069Sjhb int flags; 284224069Sjhb struct thread *td; 285224069Sjhb{ 286 struct filedesc *fdp = td->td_proc->p_fd; 287 struct vnode *uvp = NULL; 288 char *bp = NULL; 289 int error; 290 int perms = VEXEC; 291 292 if (rvp == NULL) { 293 rvp = fdp->fd_rdir; 294 if (rvp == NULL) 295 rvp = rootvnode; 296 } 297 298 VREF(rvp); 299 VREF(lvp); 300 301 /* 302 * Error handling invariant: 303 * Before a `goto out': 304 * lvp is either NULL, or locked and held. 305 * uvp is either NULL, or locked and held. 306 */ 307 308 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td); 309 if (error) { 310 vrele(lvp); 311 lvp = NULL; 312 goto out; 313 } 314 if (bufp) 315 bp = *bpp; 316 /* 317 * this loop will terminate when one of the following happens: 318 * - we hit the root 319 * - getdirentries or lookup fails 320 * - we run out of space in the buffer. 321 */ 322 if (lvp == rvp) { 323 if (bp) 324 *(--bp) = '/'; 325 goto out; 326 } 327 do { 328 if (lvp->v_type != VDIR) { 329 error = ENOTDIR; 330 goto out; 331 } 332 333 /* 334 * access check here is optional, depending on 335 * whether or not caller cares. 336 */ 337 if (flags & GETCWD_CHECK_ACCESS) { 338 error = VOP_ACCESS(lvp, perms, td->td_proc->p_ucred, td); 339 if (error) 340 goto out; 341 perms = VEXEC|VREAD; 342 } 343 344 /* 345 * step up if we're a covered vnode.. 346 */ 347 while (lvp->v_flag & VROOT) { 348 struct vnode *tvp; 349 350 if (lvp == rvp) 351 goto out; 352 353 tvp = lvp; 354 lvp = lvp->v_mount->mnt_vnodecovered; 355 vput(tvp); 356 /* 357 * hodie natus est radici frater 358 */ 359 if (lvp == NULL) { 360 error = ENOENT; 361 goto out; 362 } 363 VREF(lvp); 364 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td); 365 if (error != 0) { 366 vrele(lvp); 367 lvp = NULL; 368 goto out; 369 } 370 } 371 error = linux_getcwd_scandir(&lvp, &uvp, &bp, bufp, td); 372 if (error) 373 goto out; 374#if DIAGNOSTIC 375 if (lvp != NULL) 376 panic("getcwd: oops, forgot to null lvp"); 377 if (bufp && (bp <= bufp)) { 378 panic("getcwd: oops, went back too far"); 379 } 380#endif 381 if (bp) 382 *(--bp) = '/'; 383 lvp = uvp; 384 uvp = NULL; 385 limit--; 386 } while ((lvp != rvp) && (limit > 0)); 387 388out: 389 if (bpp) 390 *bpp = bp; 391 if (uvp) 392 vput(uvp); 393 if (lvp) 394 vput(lvp); 395 vrele(rvp); 396 return error; 397} 398 399 400/* 401 * Find pathname of process's current directory. 402 * 403 * Use vfs vnode-to-name reverse cache; if that fails, fall back 404 * to reading directory contents. 405 */ 406 407int 408linux_getcwd(struct thread *td, struct linux_getcwd_args *args) 409{ 410 struct __getcwd_args bsd; 411 caddr_t sg, bp, bend, path; 412 int error, len, lenused; 413 414#ifdef DEBUG 415 printf("Linux-emul(%ld): getcwd(%p, %ld)\n", (long)td->td_proc->p_pid, 416 args->buf, args->bufsize); 417#endif 418 419 sg = stackgap_init(); 420 bsd.buf = stackgap_alloc(&sg, SPARE_USRSPACE); 421 bsd.buflen = SPARE_USRSPACE; 422 error = __getcwd(td, &bsd); 423 if (!error) { 424 lenused = strlen(bsd.buf) + 1; 425 if (lenused <= args->bufsize) { 426 td->td_retval[0] = lenused; 427 error = copyout(bsd.buf, args->buf, lenused); 428 } 429 else 430 error = ERANGE; 431 } else { 432 len = args->bufsize; 433 434 if (len > MAXPATHLEN*4) 435 len = MAXPATHLEN*4; 436 else if (len < 2) 437 return ERANGE; 438 439 path = (char *)malloc(len, M_TEMP, M_WAITOK); 440 441 bp = &path[len]; 442 bend = bp; 443 *(--bp) = '\0'; 444 445 /* 446 * 5th argument here is "max number of vnodes to traverse". 447 * Since each entry takes up at least 2 bytes in the output buffer, 448 * limit it to N/2 vnodes for an N byte buffer. 449 */ 450 451 error = linux_getcwd_common (td->td_proc->p_fd->fd_cdir, NULL, 452 &bp, path, len/2, GETCWD_CHECK_ACCESS, td); 453 454 if (error) 455 goto out; 456 lenused = bend - bp; 457 td->td_retval[0] = lenused; 458 /* put the result into user buffer */ 459 error = copyout(bp, args->buf, lenused); 460 461out: 462 free(path, M_TEMP); 463 } 464 return (error); 465} 466 467