1 /*- 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software donated to Berkeley by 6 * Jan-Simon Pendry. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)fdesc_vnops.c 8.9 (Berkeley) 1/21/94 33 * 34 * $FreeBSD$ 35 */ 36 37 /* 38 * /dev/fd Filesystem 39 */ 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/conf.h> 44 #include <sys/dirent.h> 45 #include <sys/filedesc.h> 46 #include <sys/kernel.h> /* boottime */ 47 #include <sys/lock.h> 48 #include <sys/mutex.h> 49 #include <sys/malloc.h> 50 #include <sys/file.h> /* Must come after sys/malloc.h */ 51 #include <sys/mount.h> 52 #include <sys/namei.h> 53 #include <sys/proc.h> 54 #include <sys/stat.h> 55 #include <sys/vnode.h> 56 57 #include <fs/fdescfs/fdesc.h> 58 59 #define FDL_WANT 0x01 60 #define FDL_LOCKED 0x02 61 static int fdcache_lock; 62 63 #define NFDCACHE 4 64 #define FD_NHASH(ix) \ 65 (&fdhashtbl[(ix) & fdhash]) 66 static LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl; 67 static u_long fdhash; 68 69 static vop_getattr_t fdesc_getattr; 70 static vop_inactive_t fdesc_inactive; 71 static vop_lookup_t fdesc_lookup; 72 static vop_open_t fdesc_open; 73 static vop_readdir_t fdesc_readdir; 74 static vop_reclaim_t fdesc_reclaim; 75 static vop_setattr_t fdesc_setattr; 76 77 extern struct vop_vector fdesc_vnodeops; 78 79 /* 80 * Initialise cache headers 81 */ 82 int 83 fdesc_init(vfsp) 84 struct vfsconf *vfsp; 85 { 86 87 fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash); 88 return (0); 89 } 90 91 int 92 fdesc_allocvp(ftype, ix, mp, vpp, td) 93 fdntype ftype; 94 int ix; 95 struct mount *mp; 96 struct vnode **vpp; 97 struct thread *td; 98 { 99 struct fdhashhead *fc; 100 struct fdescnode *fd; 101 int error = 0; 102 103 fc = FD_NHASH(ix); 104 loop: 105 LIST_FOREACH(fd, fc, fd_hash) { 106 if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) { 107 if (vget(fd->fd_vnode, 0, td)) 108 goto loop; 109 *vpp = fd->fd_vnode; 110 return (error); 111 } 112 } 113 114 /* 115 * otherwise lock the array while we call getnewvnode 116 * since that can block. 117 */ 118 if (fdcache_lock & FDL_LOCKED) { 119 fdcache_lock |= FDL_WANT; 120 (void) tsleep( &fdcache_lock, PINOD, "fdalvp", 0); 121 goto loop; 122 } 123 fdcache_lock |= FDL_LOCKED; 124 125 /* 126 * Do the MALLOC before the getnewvnode since doing so afterward 127 * might cause a bogus v_data pointer to get dereferenced 128 * elsewhere if MALLOC should block. 129 */ 130 MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); 131 132 error = getnewvnode("fdesc", mp, &fdesc_vnodeops, vpp); 133 if (error) { 134 FREE(fd, M_TEMP); 135 goto out; 136 } 137 (*vpp)->v_data = fd; 138 fd->fd_vnode = *vpp; 139 fd->fd_type = ftype; 140 fd->fd_fd = -1; 141 fd->fd_ix = ix; 142 LIST_INSERT_HEAD(fc, fd, fd_hash); 143 144 out: 145 fdcache_lock &= ~FDL_LOCKED; 146 147 if (fdcache_lock & FDL_WANT) { 148 fdcache_lock &= ~FDL_WANT; 149 wakeup( &fdcache_lock); 150 } 151 152 return (error); 153 } 154 155 /* 156 * vp is the current namei directory 157 * ndp is the name to locate in that directory... 158 */ 159 static int 160 fdesc_lookup(ap) 161 struct vop_lookup_args /* { 162 struct vnode * a_dvp; 163 struct vnode ** a_vpp; 164 struct componentname * a_cnp; 165 } */ *ap; 166 { 167 struct vnode **vpp = ap->a_vpp; 168 struct vnode *dvp = ap->a_dvp; 169 struct componentname *cnp = ap->a_cnp; 170 char *pname = cnp->cn_nameptr; 171 struct thread *td = cnp->cn_thread; 172 struct file *fp; 173 int nlen = cnp->cn_namelen; 174 u_int fd; 175 int error; 176 struct vnode *fvp; 177 178 if ((cnp->cn_flags & ISLASTCN) && 179 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { 180 error = EROFS; 181 goto bad; 182 } 183 184 VOP_UNLOCK(dvp, 0, td); 185 if (cnp->cn_namelen == 1 && *pname == '.') { 186 *vpp = dvp; 187 VREF(dvp); 188 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 189 return (0); 190 } 191 192 if (VTOFDESC(dvp)->fd_type != Froot) { 193 error = ENOTDIR; 194 goto bad; 195 } 196 197 fd = 0; 198 /* the only time a leading 0 is acceptable is if it's "0" */ 199 if (*pname == '0' && nlen != 1) { 200 error = ENOENT; 201 goto bad; 202 } 203 while (nlen--) { 204 if (*pname < '0' || *pname > '9') { 205 error = ENOENT; 206 goto bad; 207 } 208 fd = 10 * fd + *pname++ - '0'; 209 } 210 211 if ((error = fget(td, fd, &fp)) != 0) 212 goto bad; 213 214 error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp, td); 215 fdrop(fp, td); 216 if (error) 217 goto bad; 218 VTOFDESC(fvp)->fd_fd = fd; 219 vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, td); 220 *vpp = fvp; 221 return (0); 222 223 bad: 224 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 225 *vpp = NULL; 226 return (error); 227 } 228 229 static int 230 fdesc_open(ap) 231 struct vop_open_args /* { 232 struct vnode *a_vp; 233 int a_mode; 234 struct ucred *a_cred; 235 struct thread *a_td; 236 } */ *ap; 237 { 238 struct vnode *vp = ap->a_vp; 239 240 if (VTOFDESC(vp)->fd_type == Froot) 241 return (0); 242 243 /* 244 * XXX Kludge: set td->td_proc->p_dupfd to contain the value of the the file 245 * descriptor being sought for duplication. The error return ensures 246 * that the vnode for this device will be released by vn_open. Open 247 * will detect this special error and take the actions in dupfdopen. 248 * Other callers of vn_open or VOP_OPEN will simply report the 249 * error. 250 */ 251 ap->a_td->td_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */ 252 return (ENODEV); 253 } 254 255 static int 256 fdesc_getattr(ap) 257 struct vop_getattr_args /* { 258 struct vnode *a_vp; 259 struct vattr *a_vap; 260 struct ucred *a_cred; 261 struct thread *a_td; 262 } */ *ap; 263 { 264 struct vnode *vp = ap->a_vp; 265 struct vattr *vap = ap->a_vap; 266 struct file *fp; 267 struct stat stb; 268 u_int fd; 269 int error = 0; 270 271 switch (VTOFDESC(vp)->fd_type) { 272 case Froot: 273 VATTR_NULL(vap); 274 275 vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 276 vap->va_type = VDIR; 277 vap->va_nlink = 2; 278 vap->va_size = DEV_BSIZE; 279 vap->va_fileid = VTOFDESC(vp)->fd_ix; 280 vap->va_uid = 0; 281 vap->va_gid = 0; 282 vap->va_blocksize = DEV_BSIZE; 283 vap->va_atime.tv_sec = boottime.tv_sec; 284 vap->va_atime.tv_nsec = 0; 285 vap->va_mtime = vap->va_atime; 286 vap->va_ctime = vap->va_mtime; 287 vap->va_gen = 0; 288 vap->va_flags = 0; 289 vap->va_rdev = 0; 290 vap->va_bytes = 0; 291 break; 292 293 case Fdesc: 294 fd = VTOFDESC(vp)->fd_fd; 295 296 if ((error = fget(ap->a_td, fd, &fp)) != 0) 297 return (error); 298 299 bzero(&stb, sizeof(stb)); 300 error = fo_stat(fp, &stb, ap->a_td->td_ucred, ap->a_td); 301 fdrop(fp, ap->a_td); 302 if (error == 0) { 303 VATTR_NULL(vap); 304 vap->va_type = IFTOVT(stb.st_mode); 305 vap->va_mode = stb.st_mode; 306 #define FDRX (VREAD|VEXEC) 307 if (vap->va_type == VDIR) 308 vap->va_mode &= ~((FDRX)|(FDRX>>3)|(FDRX>>6)); 309 #undef FDRX 310 vap->va_nlink = 1; 311 vap->va_flags = 0; 312 vap->va_bytes = stb.st_blocks * stb.st_blksize; 313 vap->va_fileid = VTOFDESC(vp)->fd_ix; 314 vap->va_size = stb.st_size; 315 vap->va_blocksize = stb.st_blksize; 316 vap->va_rdev = stb.st_rdev; 317 318 /* 319 * If no time data is provided, use the current time. 320 */ 321 if (stb.st_atimespec.tv_sec == 0 && 322 stb.st_atimespec.tv_nsec == 0) 323 nanotime(&stb.st_atimespec); 324 325 if (stb.st_ctimespec.tv_sec == 0 && 326 stb.st_ctimespec.tv_nsec == 0) 327 nanotime(&stb.st_ctimespec); 328 329 if (stb.st_mtimespec.tv_sec == 0 && 330 stb.st_mtimespec.tv_nsec == 0) 331 nanotime(&stb.st_mtimespec); 332 333 vap->va_atime = stb.st_atimespec; 334 vap->va_mtime = stb.st_mtimespec; 335 vap->va_ctime = stb.st_ctimespec; 336 vap->va_uid = stb.st_uid; 337 vap->va_gid = stb.st_gid; 338 } 339 break; 340 341 default: 342 panic("fdesc_getattr"); 343 break; 344 } 345 346 if (error == 0) 347 vp->v_type = vap->va_type; 348 return (error); 349 } 350 351 static int 352 fdesc_setattr(ap) 353 struct vop_setattr_args /* { 354 struct vnode *a_vp; 355 struct vattr *a_vap; 356 struct ucred *a_cred; 357 struct thread *a_td; 358 } */ *ap; 359 { 360 struct vattr *vap = ap->a_vap; 361 struct vnode *vp; 362 struct mount *mp; 363 struct file *fp; 364 unsigned fd; 365 int error; 366 367 /* 368 * Can't mess with the root vnode 369 */ 370 if (VTOFDESC(ap->a_vp)->fd_type == Froot) 371 return (EACCES); 372 373 fd = VTOFDESC(ap->a_vp)->fd_fd; 374 375 /* 376 * Allow setattr where there is an underlying vnode. 377 */ 378 error = getvnode(ap->a_td->td_proc->p_fd, fd, &fp); 379 if (error) { 380 /* 381 * getvnode() returns EINVAL if the file descriptor is not 382 * backed by a vnode. Silently drop all changes except 383 * chflags(2) in this case. 384 */ 385 if (error == EINVAL) { 386 if (vap->va_flags != VNOVAL) 387 error = EOPNOTSUPP; 388 else 389 error = 0; 390 } 391 return (error); 392 } 393 vp = fp->f_vnode; 394 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, ap->a_td); 395 if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) == 0) { 396 error = VOP_SETATTR(vp, ap->a_vap, ap->a_cred, ap->a_td); 397 vn_finished_write(mp); 398 } 399 VOP_UNLOCK(vp, 0, ap->a_td); 400 fdrop(fp, ap->a_td); 401 return (error); 402 } 403 404 #define UIO_MX 16 405 406 static int 407 fdesc_readdir(ap) 408 struct vop_readdir_args /* { 409 struct vnode *a_vp; 410 struct uio *a_uio; 411 struct ucred *a_cred; 412 int *a_eofflag; 413 u_long *a_cookies; 414 int a_ncookies; 415 } */ *ap; 416 { 417 struct uio *uio = ap->a_uio; 418 struct filedesc *fdp; 419 struct dirent d; 420 struct dirent *dp = &d; 421 int error, i, off, fcnt; 422 423 /* 424 * We don't allow exporting fdesc mounts, and currently local 425 * requests do not need cookies. 426 */ 427 if (ap->a_ncookies) 428 panic("fdesc_readdir: not hungry"); 429 430 if (VTOFDESC(ap->a_vp)->fd_type != Froot) 431 panic("fdesc_readdir: not dir"); 432 433 off = (int)uio->uio_offset; 434 if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 || 435 uio->uio_resid < UIO_MX) 436 return (EINVAL); 437 i = (u_int)off / UIO_MX; 438 fdp = uio->uio_td->td_proc->p_fd; 439 error = 0; 440 441 fcnt = i - 2; /* The first two nodes are `.' and `..' */ 442 443 FILEDESC_LOCK_FAST(fdp); 444 while (i < fdp->fd_nfiles + 2 && uio->uio_resid >= UIO_MX) { 445 switch (i) { 446 case 0: /* `.' */ 447 case 1: /* `..' */ 448 bzero((caddr_t)dp, UIO_MX); 449 450 dp->d_fileno = i + FD_ROOT; 451 dp->d_namlen = i + 1; 452 dp->d_reclen = UIO_MX; 453 bcopy("..", dp->d_name, dp->d_namlen); 454 dp->d_name[i + 1] = '\0'; 455 dp->d_type = DT_DIR; 456 break; 457 default: 458 if (fdp->fd_ofiles[fcnt] == NULL) { 459 FILEDESC_UNLOCK_FAST(fdp); 460 goto done; 461 } 462 463 bzero((caddr_t) dp, UIO_MX); 464 dp->d_namlen = sprintf(dp->d_name, "%d", fcnt); 465 dp->d_reclen = UIO_MX; 466 dp->d_type = DT_UNKNOWN; 467 dp->d_fileno = i + FD_DESC; 468 break; 469 } 470 /* 471 * And ship to userland 472 */ 473 FILEDESC_UNLOCK_FAST(fdp); 474 error = uiomove(dp, UIO_MX, uio); 475 if (error) 476 goto done; 477 FILEDESC_LOCK_FAST(fdp); 478 i++; 479 fcnt++; 480 } 481 FILEDESC_UNLOCK_FAST(fdp); 482 483 done: 484 uio->uio_offset = i * UIO_MX; 485 return (error); 486 } 487 488 static int 489 fdesc_inactive(ap) 490 struct vop_inactive_args /* { 491 struct vnode *a_vp; 492 struct thread *a_td; 493 } */ *ap; 494 { 495 struct vnode *vp = ap->a_vp; 496 497 /* 498 * Clear out the v_type field to avoid 499 * nasty things happening in vgone(). 500 */ 501 VOP_UNLOCK(vp, 0, ap->a_td); 502 vp->v_type = VNON; 503 return (0); 504 } 505 506 static int 507 fdesc_reclaim(ap) 508 struct vop_reclaim_args /* { 509 struct vnode *a_vp; 510 } */ *ap; 511 { 512 struct vnode *vp = ap->a_vp; 513 struct fdescnode *fd = VTOFDESC(vp); 514 515 LIST_REMOVE(fd, fd_hash); 516 FREE(vp->v_data, M_TEMP); 517 vp->v_data = 0; 518 519 return (0); 520 } 521 522 static struct vop_vector fdesc_vnodeops = { 523 .vop_default = &default_vnodeops, 524 525 .vop_access = VOP_NULL, 526 .vop_getattr = fdesc_getattr, 527 .vop_inactive = fdesc_inactive, 528 .vop_lookup = fdesc_lookup, 529 .vop_open = fdesc_open, 530 .vop_pathconf = vop_stdpathconf, 531 .vop_readdir = fdesc_readdir, 532 .vop_reclaim = fdesc_reclaim, 533 .vop_setattr = fdesc_setattr, 534 }; 535