1 /* $NetBSD: vfs_getcwd.c,v 1.63 2024/12/07 02:27:38 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 1999, 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Bill Sommerfeld. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: vfs_getcwd.c,v 1.63 2024/12/07 02:27:38 riastradh Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/types.h> 37 38 #include <sys/dirent.h> 39 #include <sys/file.h> 40 #include <sys/filedesc.h> 41 #include <sys/kauth.h> 42 #include <sys/kernel.h> 43 #include <sys/kmem.h> 44 #include <sys/mount.h> 45 #include <sys/namei.h> 46 #include <sys/proc.h> 47 #include <sys/sdt.h> 48 #include <sys/stat.h> 49 #include <sys/syscallargs.h> 50 #include <sys/systm.h> 51 #include <sys/uio.h> 52 #include <sys/vnode.h> 53 54 #include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 55 56 57 /* 58 * Vnode variable naming conventions in this file: 59 * 60 * rvp: the current root we're aiming towards. 61 * lvp, *lvpp: the "lower" vnode 62 * uvp, *uvpp: the "upper" vnode. 63 * 64 * Since all the vnodes we're dealing with are directories, and the 65 * lookups are going *up* in the filesystem rather than *down*, the 66 * usual "pvp" (parent) or "dvp" (directory) naming conventions are 67 * too confusing. 68 */ 69 70 /* 71 * XXX Will infinite loop in certain cases if a directory read reliably 72 * returns EINVAL on last block. 73 * XXX is EINVAL the right thing to return if a directory is malformed? 74 */ 75 76 /* 77 * XXX Untested vs. mount -o union; probably does the wrong thing. 78 */ 79 80 /* 81 * Find parent vnode of *lvpp, return in *uvpp 82 * 83 * If we care about the name, scan it looking for name of directory 84 * entry pointing at lvp. 85 * 86 * Place the name in the buffer which starts at bufp, immediately 87 * before *bpp, and move bpp backwards to point at the start of it. 88 * 89 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 90 * On exit, *uvpp is either NULL or is a locked vnode reference. 91 */ 92 static int 93 getcwd_scandir(struct vnode *lvp, struct vnode **uvpp, char **bpp, 94 char *bufp, struct lwp *l) 95 { 96 int error = 0; 97 int eofflag; 98 off_t off; 99 int tries; 100 struct uio uio; 101 struct iovec iov; 102 char *dirbuf = NULL; 103 int dirbuflen; 104 ino_t fileno; 105 struct vattr va; 106 struct vnode *uvp = NULL; 107 kauth_cred_t cred = l->l_cred; 108 struct componentname cn; 109 int len, reclen; 110 tries = 0; 111 112 /* Need exclusive for UFS VOP_GETATTR (itimes) & VOP_LOOKUP. */ 113 KASSERT(VOP_ISLOCKED(lvp) == LK_EXCLUSIVE); 114 115 /* 116 * If we want the filename, get some info we need while the 117 * current directory is still locked. 118 */ 119 if (bufp != NULL) { 120 error = VOP_GETATTR(lvp, &va, cred); 121 if (error) { 122 VOP_UNLOCK(lvp); 123 *uvpp = NULL; 124 return error; 125 } 126 } 127 128 /* 129 * Ok, we have to do it the hard way.. 130 * Next, get parent vnode using lookup of .. 131 */ 132 cn.cn_nameiop = LOOKUP; 133 cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 134 cn.cn_cred = cred; 135 cn.cn_nameptr = ".."; 136 cn.cn_namelen = 2; 137 138 /* At this point, lvp is locked */ 139 error = VOP_LOOKUP(lvp, uvpp, &cn); 140 VOP_UNLOCK(lvp); 141 if (error) { 142 *uvpp = NULL; 143 return error; 144 } 145 uvp = *uvpp; 146 /* If we don't care about the pathname, we're done */ 147 if (bufp == NULL) { 148 return 0; 149 } 150 151 fileno = va.va_fileid; 152 153 /* I guess UFS_DIRBLKSIZ is a good guess at a good size to use? */ 154 dirbuflen = UFS_DIRBLKSIZ; 155 if (dirbuflen < va.va_blocksize) 156 dirbuflen = va.va_blocksize; 157 dirbuf = kmem_alloc(dirbuflen, KM_SLEEP); 158 159 /* Now lvp is unlocked, try to lock uvp */ 160 error = vn_lock(uvp, LK_SHARED); 161 if (error) { 162 vrele(uvp); 163 *uvpp = NULL; 164 return error; 165 } 166 167 #if 0 168 unionread: 169 #endif 170 off = 0; 171 do { 172 /* call VOP_READDIR of parent */ 173 iov.iov_base = dirbuf; 174 iov.iov_len = dirbuflen; 175 176 uio.uio_iov = &iov; 177 uio.uio_iovcnt = 1; 178 uio.uio_offset = off; 179 uio.uio_resid = dirbuflen; 180 uio.uio_rw = UIO_READ; 181 UIO_SETUP_SYSSPACE(&uio); 182 183 eofflag = 0; 184 185 error = VOP_READDIR(uvp, &uio, cred, &eofflag, 0, 0); 186 187 off = uio.uio_offset; 188 189 /* 190 * Try again if NFS tosses its cookies. 191 * XXX this can still loop forever if the directory is busted 192 * such that the second or subsequent page of it always 193 * returns EINVAL 194 */ 195 if ((error == EINVAL) && (tries < 3)) { 196 off = 0; 197 tries++; 198 continue; /* once more, with feeling */ 199 } 200 201 if (!error) { 202 char *cpos; 203 struct dirent *dp; 204 205 cpos = dirbuf; 206 tries = 0; 207 208 /* scan directory page looking for matching vnode */ 209 for (len = (dirbuflen - uio.uio_resid); len > 0; 210 len -= reclen) { 211 dp = (struct dirent *) cpos; 212 reclen = dp->d_reclen; 213 214 /* check for malformed directory.. */ 215 if (reclen < _DIRENT_MINSIZE(dp) || 216 reclen > len) { 217 error = SET_ERROR(EINVAL); 218 goto out; 219 } 220 /* 221 * XXX should perhaps do VOP_LOOKUP to 222 * check that we got back to the right place, 223 * but getting the locking games for that 224 * right would be heinous. 225 */ 226 if ((dp->d_type != DT_WHT) && 227 (dp->d_fileno == fileno)) { 228 char *bp = *bpp; 229 230 bp -= dp->d_namlen; 231 if (bp <= bufp) { 232 error = SET_ERROR(ERANGE); 233 goto out; 234 } 235 memcpy(bp, dp->d_name, dp->d_namlen); 236 error = 0; 237 *bpp = bp; 238 goto out; 239 } 240 cpos += reclen; 241 } 242 } else 243 goto out; 244 } while (!eofflag); 245 #if 0 246 /* 247 * Deal with mount -o union, which unions only the 248 * root directory of the mount. 249 */ 250 if ((uvp->v_vflag & VV_ROOT) && 251 (uvp->v_mount->mnt_flag & MNT_UNION)) { 252 struct vnode *tvp = uvp; 253 254 uvp = uvp->v_mount->mnt_vnodecovered; 255 vput(tvp); 256 vref(uvp); 257 *uvpp = uvp; 258 vn_lock(uvp, LK_SHARED | LK_RETRY); 259 goto unionread; 260 } 261 #endif 262 error = SET_ERROR(ENOENT); 263 264 out: 265 VOP_UNLOCK(uvp); 266 kmem_free(dirbuf, dirbuflen); 267 return error; 268 } 269 270 /* 271 * common routine shared by sys___getcwd() and vn_isunder() 272 */ 273 int 274 getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp, 275 int limit, int flags, struct lwp *l) 276 { 277 struct cwdinfo *cwdi = l->l_proc->p_cwdi; 278 kauth_cred_t cred = l->l_cred; 279 struct vnode *uvp = NULL; 280 char *bp = NULL; 281 int error; 282 accmode_t accmode = VEXEC; 283 284 error = 0; 285 if (rvp == NULL) { 286 rvp = cwdi->cwdi_rdir; 287 if (rvp == NULL) 288 rvp = rootvnode; 289 } 290 291 vref(rvp); 292 vref(lvp); 293 294 /* 295 * Error handling invariant: 296 * Before a `goto out': 297 * lvp is either NULL, or held. 298 * uvp is either NULL, or held. 299 */ 300 301 if (bufp) 302 bp = *bpp; 303 304 /* 305 * this loop will terminate when one of the following happens: 306 * - we hit the root 307 * - getdirentries or lookup fails 308 * - we run out of space in the buffer. 309 */ 310 if (lvp == rvp) { 311 if (bp) 312 *(--bp) = '/'; 313 goto out; 314 } 315 do { 316 /* 317 * access check here is optional, depending on 318 * whether or not caller cares. 319 */ 320 int chkaccess = (flags & GETCWD_CHECK_ACCESS); 321 bool locked = false; 322 323 /* 324 * step up if we're a covered vnode.. 325 * check access on the first vnode only. 326 */ 327 if (lvp->v_vflag & VV_ROOT) { 328 vn_lock(lvp, LK_SHARED | LK_RETRY); 329 if (chkaccess) { 330 error = VOP_ACCESS(lvp, accmode, cred); 331 if (error) { 332 VOP_UNLOCK(lvp); 333 goto out; 334 } 335 chkaccess = 0; 336 } 337 while (lvp->v_vflag & VV_ROOT) { 338 struct vnode *tvp; 339 340 if (lvp == rvp) { 341 VOP_UNLOCK(lvp); 342 goto out; 343 } 344 345 tvp = lvp->v_mount->mnt_vnodecovered; 346 /* 347 * hodie natus est radici frater 348 */ 349 if (tvp == NULL) { 350 VOP_UNLOCK(lvp); 351 error = SET_ERROR(ENOENT); 352 goto out; 353 } 354 vref(tvp); 355 vput(lvp); 356 lvp = tvp; 357 if (lvp->v_vflag & VV_ROOT) 358 vn_lock(lvp, LK_SHARED | LK_RETRY); 359 } 360 } 361 362 /* Do we need to check access to the directory? */ 363 if (chkaccess && !cache_have_id(lvp)) { 364 /* 365 * Need exclusive for UFS VOP_GETATTR (itimes) 366 * & VOP_LOOKUP. 367 */ 368 vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 369 error = VOP_ACCESS(lvp, accmode, cred); 370 if (error) { 371 VOP_UNLOCK(lvp); 372 goto out; 373 } 374 chkaccess = 0; 375 locked = true; 376 } 377 378 /* 379 * Look in the name cache; if that fails, look in the 380 * directory.. 381 */ 382 error = cache_revlookup(lvp, &uvp, &bp, bufp, chkaccess, 383 accmode); 384 if (error == -1) { 385 if (!locked) { 386 locked = true; 387 vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 388 } 389 if (lvp->v_type != VDIR) { 390 VOP_UNLOCK(lvp); 391 error = SET_ERROR(ENOTDIR); 392 goto out; 393 } 394 error = getcwd_scandir(lvp, &uvp, &bp, bufp, l); 395 /* lvp now unlocked */ 396 } else if (locked) { 397 VOP_UNLOCK(lvp); 398 } 399 if (error) 400 goto out; 401 #if DIAGNOSTIC 402 if (bufp && (bp <= bufp)) { 403 panic("getcwd: oops, went back too far"); 404 } 405 #endif 406 accmode = VEXEC | VREAD; 407 if (bp) 408 *(--bp) = '/'; 409 vrele(lvp); 410 lvp = uvp; 411 uvp = NULL; 412 limit--; 413 } while ((lvp != rvp) && (limit > 0)); 414 415 out: 416 if (bpp) 417 *bpp = bp; 418 if (uvp) 419 vrele(uvp); 420 if (lvp) 421 vrele(lvp); 422 vrele(rvp); 423 return error; 424 } 425 426 /* 427 * Check if one directory can be found inside another in the directory 428 * hierarchy. 429 * 430 * Intended to be used in chroot, chdir, fchdir, etc., to ensure that 431 * chroot() actually means something. 432 */ 433 int 434 vn_isunder(struct vnode *lvp, struct vnode *rvp, struct lwp *l) 435 { 436 int error; 437 438 error = getcwd_common(lvp, rvp, NULL, NULL, MAXPATHLEN / 2, 0, l); 439 440 if (!error) 441 return 1; 442 else 443 return 0; 444 } 445 446 /* 447 * Returns true if proc p1's root directory equal to or under p2's 448 * root directory. 449 * 450 * Intended to be used from ptrace/procfs sorts of things. 451 */ 452 453 int 454 proc_isunder(struct proc *p1, struct lwp *l2) 455 { 456 struct vnode *r1 = p1->p_cwdi->cwdi_rdir; 457 struct vnode *r2 = l2->l_proc->p_cwdi->cwdi_rdir; 458 459 if (r1 == NULL) 460 return (r2 == NULL); 461 else if (r2 == NULL) 462 return 1; 463 else 464 return vn_isunder(r1, r2, l2); 465 } 466 467 /* 468 * Find pathname of process's current directory. 469 * 470 * Use vfs vnode-to-name reverse cache; if that fails, fall back 471 * to reading directory contents. 472 */ 473 474 int 475 sys___getcwd(struct lwp *l, const struct sys___getcwd_args *uap, 476 register_t *retval) 477 { 478 /* { 479 syscallarg(char *) bufp; 480 syscallarg(size_t) length; 481 } */ 482 483 int error; 484 char *path; 485 char *bp, *bend; 486 int len = SCARG(uap, length); 487 int lenused; 488 struct cwdinfo *cwdi; 489 490 if (len > MAXPATHLEN * 4) 491 len = MAXPATHLEN * 4; 492 else if (len < 2) 493 return SET_ERROR(ERANGE); 494 495 path = kmem_alloc(len, KM_SLEEP); 496 bp = &path[len]; 497 bend = bp; 498 *(--bp) = '\0'; 499 500 /* 501 * 5th argument here is "max number of vnodes to traverse". 502 * Since each entry takes up at least 2 bytes in the output buffer, 503 * limit it to N/2 vnodes for an N byte buffer. 504 */ 505 cwdi = l->l_proc->p_cwdi; 506 rw_enter(&cwdi->cwdi_lock, RW_READER); 507 error = getcwd_common(cwdi->cwdi_cdir, NULL, &bp, path, 508 len/2, GETCWD_CHECK_ACCESS, l); 509 rw_exit(&cwdi->cwdi_lock); 510 511 if (error) 512 goto out; 513 lenused = bend - bp; 514 *retval = lenused; 515 /* put the result into user buffer */ 516 error = copyout(bp, SCARG(uap, bufp), lenused); 517 518 out: 519 kmem_free(path, len); 520 return error; 521 } 522 523 /* 524 * Try to find a pathname for a vnode. Since there is no mapping vnode -> 525 * parent directory, this needs the namecache to succeed. Caller holds a 526 * reference to the vnode. 527 */ 528 int 529 vnode_to_path(char *path, size_t len, struct vnode *vp, struct lwp *curl, 530 struct proc *p) 531 { 532 struct proc *curp = curl->l_proc; 533 int error, lenused, elen; 534 char *bp, *bend; 535 struct vnode *dvp; 536 537 KASSERT(vrefcnt(vp) > 0); 538 539 bp = bend = &path[len]; 540 *(--bp) = '\0'; 541 542 error = cache_revlookup(vp, &dvp, &bp, path, false, 0); 543 if (error != 0) 544 return (error == -1 ? SET_ERROR(ENOENT) : error); 545 546 *(--bp) = '/'; 547 error = getcwd_common(dvp, NULL, &bp, path, len / 2, 548 GETCWD_CHECK_ACCESS, curl); 549 vrele(dvp); 550 if (error != 0) 551 return error; 552 553 /* 554 * Strip off emulation path for emulated processes looking at 555 * the maps file of a process of the same emulation. (Won't 556 * work if /emul/xxx is a symlink..) 557 */ 558 if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) { 559 elen = strlen(curp->p_emul->e_path); 560 if (!strncmp(bp, curp->p_emul->e_path, elen)) 561 bp = &bp[elen]; 562 } 563 564 lenused = bend - bp; 565 566 memcpy(path, bp, lenused); 567 path[lenused] = '\0'; 568 569 return 0; 570 } 571