1 /* $FreeBSD$ */ 2 /* $OpenBSD: linux_getcwd.c,v 1.2 2001/05/16 12:50:21 ho Exp $ */ 3 /* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */ 4 5 /*- 6 * Copyright (c) 1999 The NetBSD Foundation, Inc. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to The NetBSD Foundation 10 * by Bill Sommerfeld. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the NetBSD 23 * Foundation, Inc. and its contributors. 24 * 4. Neither the name of The NetBSD Foundation nor the names of its 25 * contributors may be used to endorse or promote products derived 26 * from this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 */ 40 #include "opt_compat.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/sysproto.h> 45 #include <sys/namei.h> 46 #include <sys/filedesc.h> 47 #include <sys/kernel.h> 48 #include <sys/file.h> 49 #include <sys/stat.h> 50 #include <sys/vnode.h> 51 #include <sys/mount.h> 52 #include <sys/proc.h> 53 #include <sys/uio.h> 54 #include <sys/malloc.h> 55 #include <sys/dirent.h> 56 #include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 57 58 #include <machine/../linux/linux.h> 59 #include <machine/../linux/linux_proto.h> 60 #include <compat/linux/linux_util.h> 61 62 static int 63 linux_getcwd_scandir(struct vnode **, struct vnode **, 64 char **, char *, struct thread *); 65 static int 66 linux_getcwd_common(struct vnode *, struct vnode *, 67 char **, char *, int, int, struct thread *); 68 69 #define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4) 70 71 /* 72 * Vnode variable naming conventions in this file: 73 * 74 * rvp: the current root we're aiming towards. 75 * lvp, *lvpp: the "lower" vnode 76 * uvp, *uvpp: the "upper" vnode. 77 * 78 * Since all the vnodes we're dealing with are directories, and the 79 * lookups are going *up* in the filesystem rather than *down*, the 80 * usual "pvp" (parent) or "dvp" (directory) naming conventions are 81 * too confusing. 82 */ 83 84 /* 85 * XXX Will infinite loop in certain cases if a directory read reliably 86 * returns EINVAL on last block. 87 * XXX is EINVAL the right thing to return if a directory is malformed? 88 */ 89 90 /* 91 * XXX Untested vs. mount -o union; probably does the wrong thing. 92 */ 93 94 /* 95 * Find parent vnode of *lvpp, return in *uvpp 96 * 97 * If we care about the name, scan it looking for name of directory 98 * entry pointing at lvp. 99 * 100 * Place the name in the buffer which starts at bufp, immediately 101 * before *bpp, and move bpp backwards to point at the start of it. 102 * 103 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 104 * On exit, *uvpp is either NULL or is a locked vnode reference. 105 */ 106 static int 107 linux_getcwd_scandir(lvpp, uvpp, bpp, bufp, td) 108 struct vnode **lvpp; 109 struct vnode **uvpp; 110 char **bpp; 111 char *bufp; 112 struct thread *td; 113 { 114 int error = 0; 115 int eofflag; 116 off_t off; 117 int tries; 118 struct uio uio; 119 struct iovec iov; 120 char *dirbuf = NULL; 121 int dirbuflen; 122 ino_t fileno; 123 struct vattr va; 124 struct vnode *uvp = NULL; 125 struct vnode *lvp = *lvpp; 126 struct componentname cn; 127 int len, reclen; 128 tries = 0; 129 130 /* 131 * If we want the filename, get some info we need while the 132 * current directory is still locked. 133 */ 134 if (bufp != NULL) { 135 error = VOP_GETATTR(lvp, &va, td->td_ucred, td); 136 if (error) { 137 vput(lvp); 138 *lvpp = NULL; 139 *uvpp = NULL; 140 return error; 141 } 142 } 143 144 /* 145 * Ok, we have to do it the hard way.. 146 * Next, get parent vnode using lookup of .. 147 */ 148 cn.cn_nameiop = LOOKUP; 149 cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 150 cn.cn_thread = td; 151 cn.cn_cred = td->td_ucred; 152 cn.cn_pnbuf = NULL; 153 cn.cn_nameptr = ".."; 154 cn.cn_namelen = 2; 155 cn.cn_consume = 0; 156 157 /* 158 * At this point, lvp is locked and will be unlocked by the lookup. 159 * On successful return, *uvpp will be locked 160 */ 161 error = VOP_LOOKUP(lvp, uvpp, &cn); 162 if (error) { 163 vput(lvp); 164 *lvpp = NULL; 165 *uvpp = NULL; 166 return error; 167 } 168 uvp = *uvpp; 169 170 /* If we don't care about the pathname, we're done */ 171 if (bufp == NULL) { 172 vrele(lvp); 173 *lvpp = NULL; 174 return 0; 175 } 176 177 fileno = va.va_fileid; 178 179 dirbuflen = DIRBLKSIZ; 180 if (dirbuflen < va.va_blocksize) 181 dirbuflen = va.va_blocksize; 182 dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); 183 184 #if 0 185 unionread: 186 #endif 187 off = 0; 188 do { 189 /* call VOP_READDIR of parent */ 190 iov.iov_base = dirbuf; 191 iov.iov_len = dirbuflen; 192 193 uio.uio_iov = &iov; 194 uio.uio_iovcnt = 1; 195 uio.uio_offset = off; 196 uio.uio_resid = dirbuflen; 197 uio.uio_segflg = UIO_SYSSPACE; 198 uio.uio_rw = UIO_READ; 199 uio.uio_td = td; 200 201 eofflag = 0; 202 203 error = VOP_READDIR(uvp, &uio, td->td_ucred, &eofflag, 0, 0); 204 205 off = uio.uio_offset; 206 207 /* 208 * Try again if NFS tosses its cookies. 209 * XXX this can still loop forever if the directory is busted 210 * such that the second or subsequent page of it always 211 * returns EINVAL 212 */ 213 if ((error == EINVAL) && (tries < 3)) { 214 off = 0; 215 tries++; 216 continue; /* once more, with feeling */ 217 } 218 219 if (!error) { 220 char *cpos; 221 struct dirent *dp; 222 223 cpos = dirbuf; 224 tries = 0; 225 226 /* scan directory page looking for matching vnode */ 227 for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) { 228 dp = (struct dirent *) cpos; 229 reclen = dp->d_reclen; 230 231 /* check for malformed directory.. */ 232 if (reclen < DIRENT_MINSIZE) { 233 error = EINVAL; 234 goto out; 235 } 236 /* 237 * XXX should perhaps do VOP_LOOKUP to 238 * check that we got back to the right place, 239 * but getting the locking games for that 240 * right would be heinous. 241 */ 242 if ((dp->d_type != DT_WHT) && 243 (dp->d_fileno == fileno)) { 244 char *bp = *bpp; 245 bp -= dp->d_namlen; 246 247 if (bp <= bufp) { 248 error = ERANGE; 249 goto out; 250 } 251 bcopy(dp->d_name, bp, dp->d_namlen); 252 error = 0; 253 *bpp = bp; 254 goto out; 255 } 256 cpos += reclen; 257 } 258 } 259 } while (!eofflag); 260 error = ENOENT; 261 262 out: 263 vrele(lvp); 264 *lvpp = NULL; 265 free(dirbuf, M_TEMP); 266 return error; 267 } 268 269 270 /* 271 * common routine shared by sys___getcwd() and linux_vn_isunder() 272 */ 273 274 #define GETCWD_CHECK_ACCESS 0x0001 275 276 static int 277 linux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, td) 278 struct vnode *lvp; 279 struct vnode *rvp; 280 char **bpp; 281 char *bufp; 282 int limit; 283 int flags; 284 struct thread *td; 285 { 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_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 388 out: 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 407 int 408 linux_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, (long)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 461 out: 462 free(path, M_TEMP); 463 } 464 return (error); 465 } 466 467