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