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 #include "opt_mac.h" 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/sysproto.h> 46 #include <sys/namei.h> 47 #include <sys/filedesc.h> 48 #include <sys/kernel.h> 49 #include <sys/file.h> 50 #include <sys/stat.h> 51 #include <sys/vnode.h> 52 #include <sys/mount.h> 53 #include <sys/proc.h> 54 #include <sys/uio.h> 55 #include <sys/mac.h> 56 #include <sys/malloc.h> 57 #include <sys/dirent.h> 58 #include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 59 60 #include <machine/../linux/linux.h> 61 #include <machine/../linux/linux_proto.h> 62 #include <compat/linux/linux_util.h> 63 64 static int 65 linux_getcwd_scandir(struct vnode **, struct vnode **, 66 char **, char *, struct thread *); 67 static int 68 linux_getcwd_common(struct vnode *, struct vnode *, 69 char **, char *, int, int, struct thread *); 70 71 #define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4) 72 73 /* 74 * Vnode variable naming conventions in this file: 75 * 76 * rvp: the current root we're aiming towards. 77 * lvp, *lvpp: the "lower" vnode 78 * uvp, *uvpp: the "upper" vnode. 79 * 80 * Since all the vnodes we're dealing with are directories, and the 81 * lookups are going *up* in the filesystem rather than *down*, the 82 * usual "pvp" (parent) or "dvp" (directory) naming conventions are 83 * too confusing. 84 */ 85 86 /* 87 * XXX Will infinite loop in certain cases if a directory read reliably 88 * returns EINVAL on last block. 89 * XXX is EINVAL the right thing to return if a directory is malformed? 90 */ 91 92 /* 93 * XXX Untested vs. mount -o union; probably does the wrong thing. 94 */ 95 96 /* 97 * Find parent vnode of *lvpp, return in *uvpp 98 * 99 * If we care about the name, scan it looking for name of directory 100 * entry pointing at lvp. 101 * 102 * Place the name in the buffer which starts at bufp, immediately 103 * before *bpp, and move bpp backwards to point at the start of it. 104 * 105 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 106 * On exit, *uvpp is either NULL or is a locked vnode reference. 107 */ 108 static int 109 linux_getcwd_scandir(lvpp, uvpp, bpp, bufp, td) 110 struct vnode **lvpp; 111 struct vnode **uvpp; 112 char **bpp; 113 char *bufp; 114 struct thread *td; 115 { 116 int error = 0; 117 int eofflag; 118 off_t off; 119 int tries; 120 struct uio uio; 121 struct iovec iov; 122 char *dirbuf = NULL; 123 int dirbuflen; 124 ino_t fileno; 125 struct vattr va; 126 struct vnode *uvp = NULL; 127 struct vnode *lvp = *lvpp; 128 struct componentname cn; 129 int len, reclen; 130 tries = 0; 131 132 /* 133 * If we want the filename, get some info we need while the 134 * current directory is still locked. 135 */ 136 if (bufp != NULL) { 137 error = VOP_GETATTR(lvp, &va, td->td_ucred, td); 138 if (error) { 139 vput(lvp); 140 *lvpp = NULL; 141 *uvpp = NULL; 142 return error; 143 } 144 } 145 146 /* 147 * Ok, we have to do it the hard way.. 148 * Next, get parent vnode using lookup of .. 149 */ 150 cn.cn_nameiop = LOOKUP; 151 cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 152 cn.cn_thread = td; 153 cn.cn_cred = td->td_ucred; 154 cn.cn_pnbuf = NULL; 155 cn.cn_nameptr = ".."; 156 cn.cn_namelen = 2; 157 cn.cn_consume = 0; 158 159 /* 160 * At this point, lvp is locked and will be unlocked by the lookup. 161 * On successful return, *uvpp will be locked 162 */ 163 error = VOP_LOOKUP(lvp, uvpp, &cn); 164 if (error) { 165 vput(lvp); 166 *lvpp = NULL; 167 *uvpp = NULL; 168 return error; 169 } 170 uvp = *uvpp; 171 172 /* If we don't care about the pathname, we're done */ 173 if (bufp == NULL) { 174 vrele(lvp); 175 *lvpp = NULL; 176 return 0; 177 } 178 179 fileno = va.va_fileid; 180 181 dirbuflen = DIRBLKSIZ; 182 if (dirbuflen < va.va_blocksize) 183 dirbuflen = va.va_blocksize; 184 dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK); 185 186 #if 0 187 unionread: 188 #endif 189 off = 0; 190 do { 191 /* call VOP_READDIR of parent */ 192 iov.iov_base = dirbuf; 193 iov.iov_len = dirbuflen; 194 195 uio.uio_iov = &iov; 196 uio.uio_iovcnt = 1; 197 uio.uio_offset = off; 198 uio.uio_resid = dirbuflen; 199 uio.uio_segflg = UIO_SYSSPACE; 200 uio.uio_rw = UIO_READ; 201 uio.uio_td = td; 202 203 eofflag = 0; 204 205 #ifdef MAC 206 error = mac_check_vnode_readdir(td->td_ucred, uvp); 207 if (error == 0) 208 #endif /* MAC */ 209 error = VOP_READDIR(uvp, &uio, td->td_ucred, &eofflag, 210 0, 0); 211 212 off = uio.uio_offset; 213 214 /* 215 * Try again if NFS tosses its cookies. 216 * XXX this can still loop forever if the directory is busted 217 * such that the second or subsequent page of it always 218 * returns EINVAL 219 */ 220 if ((error == EINVAL) && (tries < 3)) { 221 off = 0; 222 tries++; 223 continue; /* once more, with feeling */ 224 } 225 226 if (!error) { 227 char *cpos; 228 struct dirent *dp; 229 230 cpos = dirbuf; 231 tries = 0; 232 233 /* scan directory page looking for matching vnode */ 234 for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) { 235 dp = (struct dirent *) cpos; 236 reclen = dp->d_reclen; 237 238 /* check for malformed directory.. */ 239 if (reclen < DIRENT_MINSIZE) { 240 error = EINVAL; 241 goto out; 242 } 243 /* 244 * XXX should perhaps do VOP_LOOKUP to 245 * check that we got back to the right place, 246 * but getting the locking games for that 247 * right would be heinous. 248 */ 249 if ((dp->d_type != DT_WHT) && 250 (dp->d_fileno == fileno)) { 251 char *bp = *bpp; 252 bp -= dp->d_namlen; 253 254 if (bp <= bufp) { 255 error = ERANGE; 256 goto out; 257 } 258 bcopy(dp->d_name, bp, dp->d_namlen); 259 error = 0; 260 *bpp = bp; 261 goto out; 262 } 263 cpos += reclen; 264 } 265 } 266 } while (!eofflag); 267 error = ENOENT; 268 269 out: 270 vrele(lvp); 271 *lvpp = NULL; 272 free(dirbuf, M_TEMP); 273 return error; 274 } 275 276 277 /* 278 * common routine shared by sys___getcwd() and linux_vn_isunder() 279 */ 280 281 #define GETCWD_CHECK_ACCESS 0x0001 282 283 static int 284 linux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, td) 285 struct vnode *lvp; 286 struct vnode *rvp; 287 char **bpp; 288 char *bufp; 289 int limit; 290 int flags; 291 struct thread *td; 292 { 293 struct filedesc *fdp = td->td_proc->p_fd; 294 struct vnode *uvp = NULL; 295 char *bp = NULL; 296 int error; 297 int perms = VEXEC; 298 299 if (rvp == NULL) { 300 rvp = fdp->fd_rdir; 301 if (rvp == NULL) 302 rvp = rootvnode; 303 } 304 305 VREF(rvp); 306 VREF(lvp); 307 308 /* 309 * Error handling invariant: 310 * Before a `goto out': 311 * lvp is either NULL, or locked and held. 312 * uvp is either NULL, or locked and held. 313 */ 314 315 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td); 316 if (error) { 317 vrele(lvp); 318 lvp = NULL; 319 goto out; 320 } 321 if (bufp) 322 bp = *bpp; 323 /* 324 * this loop will terminate when one of the following happens: 325 * - we hit the root 326 * - getdirentries or lookup fails 327 * - we run out of space in the buffer. 328 */ 329 if (lvp == rvp) { 330 if (bp) 331 *(--bp) = '/'; 332 goto out; 333 } 334 do { 335 if (lvp->v_type != VDIR) { 336 error = ENOTDIR; 337 goto out; 338 } 339 340 /* 341 * access check here is optional, depending on 342 * whether or not caller cares. 343 */ 344 if (flags & GETCWD_CHECK_ACCESS) { 345 error = VOP_ACCESS(lvp, perms, td->td_ucred, td); 346 if (error) 347 goto out; 348 perms = VEXEC|VREAD; 349 } 350 351 /* 352 * step up if we're a covered vnode.. 353 */ 354 while (lvp->v_vflag & VV_ROOT) { 355 struct vnode *tvp; 356 357 if (lvp == rvp) 358 goto out; 359 360 tvp = lvp; 361 lvp = lvp->v_mount->mnt_vnodecovered; 362 vput(tvp); 363 /* 364 * hodie natus est radici frater 365 */ 366 if (lvp == NULL) { 367 error = ENOENT; 368 goto out; 369 } 370 VREF(lvp); 371 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td); 372 if (error != 0) { 373 vrele(lvp); 374 lvp = NULL; 375 goto out; 376 } 377 } 378 error = linux_getcwd_scandir(&lvp, &uvp, &bp, bufp, td); 379 if (error) 380 goto out; 381 #if DIAGNOSTIC 382 if (lvp != NULL) 383 panic("getcwd: oops, forgot to null lvp"); 384 if (bufp && (bp <= bufp)) { 385 panic("getcwd: oops, went back too far"); 386 } 387 #endif 388 if (bp) 389 *(--bp) = '/'; 390 lvp = uvp; 391 uvp = NULL; 392 limit--; 393 } while ((lvp != rvp) && (limit > 0)); 394 395 out: 396 if (bpp) 397 *bpp = bp; 398 if (uvp) 399 vput(uvp); 400 if (lvp) 401 vput(lvp); 402 vrele(rvp); 403 return error; 404 } 405 406 407 /* 408 * Find pathname of process's current directory. 409 * 410 * Use vfs vnode-to-name reverse cache; if that fails, fall back 411 * to reading directory contents. 412 */ 413 414 int 415 linux_getcwd(struct thread *td, struct linux_getcwd_args *args) 416 { 417 struct __getcwd_args bsd; 418 caddr_t sg, bp, bend, path; 419 int error, len, lenused; 420 421 #ifdef DEBUG 422 printf("Linux-emul(%ld): getcwd(%p, %ld)\n", (long)td->td_proc->p_pid, 423 args->buf, (long)args->bufsize); 424 #endif 425 426 sg = stackgap_init(); 427 bsd.buf = stackgap_alloc(&sg, SPARE_USRSPACE); 428 bsd.buflen = SPARE_USRSPACE; 429 error = __getcwd(td, &bsd); 430 if (!error) { 431 lenused = strlen(bsd.buf) + 1; 432 if (lenused <= args->bufsize) { 433 td->td_retval[0] = lenused; 434 error = copyout(bsd.buf, args->buf, lenused); 435 } 436 else 437 error = ERANGE; 438 } else { 439 len = args->bufsize; 440 441 if (len > MAXPATHLEN*4) 442 len = MAXPATHLEN*4; 443 else if (len < 2) 444 return ERANGE; 445 446 path = (char *)malloc(len, M_TEMP, M_WAITOK); 447 448 bp = &path[len]; 449 bend = bp; 450 *(--bp) = '\0'; 451 452 /* 453 * 5th argument here is "max number of vnodes to traverse". 454 * Since each entry takes up at least 2 bytes in the output buffer, 455 * limit it to N/2 vnodes for an N byte buffer. 456 */ 457 458 error = linux_getcwd_common (td->td_proc->p_fd->fd_cdir, NULL, 459 &bp, path, len/2, GETCWD_CHECK_ACCESS, td); 460 461 if (error) 462 goto out; 463 lenused = bend - bp; 464 td->td_retval[0] = lenused; 465 /* put the result into user buffer */ 466 error = copyout(bp, args->buf, lenused); 467 468 out: 469 free(path, M_TEMP); 470 } 471 return (error); 472 } 473 474