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/mutex.h> 53 #include <sys/malloc.h> 54 #include <sys/file.h> /* Must come after sys/malloc.h */ 55 #include <sys/mount.h> 56 #include <sys/namei.h> 57 #include <sys/proc.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(struct vop_getattr_args *ap); 76 static int fdesc_inactive(struct vop_inactive_args *ap); 77 static int fdesc_lookup(struct vop_lookup_args *ap); 78 static int fdesc_open(struct vop_open_args *ap); 79 static int fdesc_readdir(struct vop_readdir_args *ap); 80 static int fdesc_reclaim(struct vop_reclaim_args *ap); 81 static int fdesc_poll(struct vop_poll_args *ap); 82 static int fdesc_setattr(struct vop_setattr_args *ap); 83 84 /* 85 * Initialise cache headers 86 */ 87 int 88 fdesc_init(vfsp) 89 struct vfsconf *vfsp; 90 { 91 92 fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash); 93 return (0); 94 } 95 96 int 97 fdesc_allocvp(ftype, ix, mp, vpp, td) 98 fdntype ftype; 99 int ix; 100 struct mount *mp; 101 struct vnode **vpp; 102 struct thread *td; 103 { 104 struct fdhashhead *fc; 105 struct fdescnode *fd; 106 int error = 0; 107 108 fc = FD_NHASH(ix); 109 loop: 110 LIST_FOREACH(fd, fc, fd_hash) { 111 if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) { 112 if (vget(fd->fd_vnode, 0, td)) 113 goto loop; 114 *vpp = fd->fd_vnode; 115 return (error); 116 } 117 } 118 119 /* 120 * otherwise lock the array while we call getnewvnode 121 * since that can block. 122 */ 123 if (fdcache_lock & FDL_LOCKED) { 124 fdcache_lock |= FDL_WANT; 125 (void) tsleep((caddr_t) &fdcache_lock, PINOD, "fdalvp", 0); 126 goto loop; 127 } 128 fdcache_lock |= FDL_LOCKED; 129 130 /* 131 * Do the MALLOC before the getnewvnode since doing so afterward 132 * might cause a bogus v_data pointer to get dereferenced 133 * elsewhere if MALLOC should block. 134 */ 135 MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); 136 137 error = getnewvnode("fdesc", mp, fdesc_vnodeop_p, vpp); 138 if (error) { 139 FREE(fd, M_TEMP); 140 goto out; 141 } 142 (*vpp)->v_data = fd; 143 fd->fd_vnode = *vpp; 144 fd->fd_type = ftype; 145 fd->fd_fd = -1; 146 fd->fd_ix = ix; 147 LIST_INSERT_HEAD(fc, fd, fd_hash); 148 149 out: 150 fdcache_lock &= ~FDL_LOCKED; 151 152 if (fdcache_lock & FDL_WANT) { 153 fdcache_lock &= ~FDL_WANT; 154 wakeup((caddr_t) &fdcache_lock); 155 } 156 157 return (error); 158 } 159 160 /* 161 * vp is the current namei directory 162 * ndp is the name to locate in that directory... 163 */ 164 static int 165 fdesc_lookup(ap) 166 struct vop_lookup_args /* { 167 struct vnode * a_dvp; 168 struct vnode ** a_vpp; 169 struct componentname * a_cnp; 170 } */ *ap; 171 { 172 struct vnode **vpp = ap->a_vpp; 173 struct vnode *dvp = ap->a_dvp; 174 struct componentname *cnp = ap->a_cnp; 175 char *pname = cnp->cn_nameptr; 176 struct thread *td = cnp->cn_thread; 177 struct file *fp; 178 int nlen = cnp->cn_namelen; 179 u_int fd; 180 int error; 181 struct vnode *fvp; 182 183 if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) { 184 error = EROFS; 185 goto bad; 186 } 187 188 VOP_UNLOCK(dvp, 0, td); 189 if (cnp->cn_namelen == 1 && *pname == '.') { 190 *vpp = dvp; 191 VREF(dvp); 192 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 193 return (0); 194 } 195 196 if (VTOFDESC(dvp)->fd_type != Froot) { 197 error = ENOTDIR; 198 goto bad; 199 } 200 201 fd = 0; 202 /* the only time a leading 0 is acceptable is if it's "0" */ 203 if (*pname == '0' && nlen != 1) { 204 error = ENOENT; 205 goto bad; 206 } 207 while (nlen--) { 208 if (*pname < '0' || *pname > '9') { 209 error = ENOENT; 210 goto bad; 211 } 212 fd = 10 * fd + *pname++ - '0'; 213 } 214 215 if ((error = fget(td, fd, &fp)) != 0) 216 goto bad; 217 218 error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp, td); 219 fdrop(fp, td); 220 if (error) 221 goto bad; 222 VTOFDESC(fvp)->fd_fd = fd; 223 vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, td); 224 *vpp = fvp; 225 return (0); 226 227 bad: 228 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 229 *vpp = NULL; 230 return (error); 231 } 232 233 static int 234 fdesc_open(ap) 235 struct vop_open_args /* { 236 struct vnode *a_vp; 237 int a_mode; 238 struct ucred *a_cred; 239 struct thread *a_td; 240 } */ *ap; 241 { 242 struct vnode *vp = ap->a_vp; 243 244 if (VTOFDESC(vp)->fd_type == Froot) 245 return (0); 246 247 /* 248 * XXX Kludge: set td->td_proc->p_dupfd to contain the value of the the file 249 * descriptor being sought for duplication. The error return ensures 250 * that the vnode for this device will be released by vn_open. Open 251 * will detect this special error and take the actions in dupfdopen. 252 * Other callers of vn_open or VOP_OPEN will simply report the 253 * error. 254 */ 255 ap->a_td->td_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */ 256 return (ENODEV); 257 } 258 259 static int 260 fdesc_getattr(ap) 261 struct vop_getattr_args /* { 262 struct vnode *a_vp; 263 struct vattr *a_vap; 264 struct ucred *a_cred; 265 struct thread *a_td; 266 } */ *ap; 267 { 268 struct vnode *vp = ap->a_vp; 269 struct vattr *vap = ap->a_vap; 270 struct file *fp; 271 struct stat stb; 272 u_int fd; 273 int error = 0; 274 275 switch (VTOFDESC(vp)->fd_type) { 276 case Froot: 277 VATTR_NULL(vap); 278 279 vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 280 vap->va_type = VDIR; 281 vap->va_nlink = 2; 282 vap->va_size = DEV_BSIZE; 283 vap->va_fileid = VTOFDESC(vp)->fd_ix; 284 vap->va_uid = 0; 285 vap->va_gid = 0; 286 vap->va_blocksize = DEV_BSIZE; 287 vap->va_atime.tv_sec = boottime.tv_sec; 288 vap->va_atime.tv_nsec = 0; 289 vap->va_mtime = vap->va_atime; 290 vap->va_ctime = vap->va_mtime; 291 vap->va_gen = 0; 292 vap->va_flags = 0; 293 vap->va_rdev = 0; 294 vap->va_bytes = 0; 295 break; 296 297 case Fdesc: 298 fd = VTOFDESC(vp)->fd_fd; 299 300 if ((error = fget(ap->a_td, fd, &fp)) != 0) 301 return (error); 302 303 bzero(&stb, sizeof(stb)); 304 error = fo_stat(fp, &stb, ap->a_td->td_ucred, ap->a_td); 305 fdrop(fp, ap->a_td); 306 if (error == 0) { 307 VATTR_NULL(vap); 308 vap->va_type = IFTOVT(stb.st_mode); 309 vap->va_mode = stb.st_mode; 310 #define FDRX (VREAD|VEXEC) 311 if (vap->va_type == VDIR) 312 vap->va_mode &= ~((FDRX)|(FDRX>>3)|(FDRX>>6)); 313 #undef FDRX 314 vap->va_nlink = 1; 315 vap->va_flags = 0; 316 vap->va_bytes = stb.st_blocks * stb.st_blksize; 317 vap->va_fileid = VTOFDESC(vp)->fd_ix; 318 vap->va_size = stb.st_size; 319 vap->va_blocksize = stb.st_blksize; 320 vap->va_rdev = stb.st_rdev; 321 322 /* 323 * If no time data is provided, use the current time. 324 */ 325 if (stb.st_atimespec.tv_sec == 0 && 326 stb.st_atimespec.tv_nsec == 0) 327 nanotime(&stb.st_atimespec); 328 329 if (stb.st_ctimespec.tv_sec == 0 && 330 stb.st_ctimespec.tv_nsec == 0) 331 nanotime(&stb.st_ctimespec); 332 333 if (stb.st_mtimespec.tv_sec == 0 && 334 stb.st_mtimespec.tv_nsec == 0) 335 nanotime(&stb.st_mtimespec); 336 337 vap->va_atime = stb.st_atimespec; 338 vap->va_mtime = stb.st_mtimespec; 339 vap->va_ctime = stb.st_ctimespec; 340 vap->va_uid = stb.st_uid; 341 vap->va_gid = stb.st_gid; 342 } 343 break; 344 345 default: 346 panic("fdesc_getattr"); 347 break; 348 } 349 350 if (error == 0) 351 vp->v_type = vap->va_type; 352 return (error); 353 } 354 355 static int 356 fdesc_setattr(ap) 357 struct vop_setattr_args /* { 358 struct vnode *a_vp; 359 struct vattr *a_vap; 360 struct ucred *a_cred; 361 struct thread *a_td; 362 } */ *ap; 363 { 364 struct vattr *vap = ap->a_vap; 365 struct vnode *vp; 366 struct mount *mp; 367 struct file *fp; 368 unsigned fd; 369 int error; 370 371 /* 372 * Can't mess with the root vnode 373 */ 374 if (VTOFDESC(ap->a_vp)->fd_type == Froot) 375 return (EACCES); 376 377 fd = VTOFDESC(ap->a_vp)->fd_fd; 378 379 /* 380 * Allow setattr where there is an underlying vnode. 381 */ 382 error = getvnode(ap->a_td->td_proc->p_fd, fd, &fp); 383 if (error) { 384 /* 385 * getvnode() returns EINVAL if the file descriptor is not 386 * backed by a vnode. Silently drop all changes except 387 * chflags(2) in this case. 388 */ 389 if (error == EINVAL) { 390 if (vap->va_flags != VNOVAL) 391 error = EOPNOTSUPP; 392 else 393 error = 0; 394 } 395 return (error); 396 } 397 vp = (struct vnode *)fp->f_data; 398 if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { 399 fdrop(fp, ap->a_td); 400 return (error); 401 } 402 error = VOP_SETATTR(vp, ap->a_vap, ap->a_cred, ap->a_td); 403 vn_finished_write(mp); 404 fdrop(fp, ap->a_td); 405 return (error); 406 } 407 408 #define UIO_MX 16 409 410 static int 411 fdesc_readdir(ap) 412 struct vop_readdir_args /* { 413 struct vnode *a_vp; 414 struct uio *a_uio; 415 struct ucred *a_cred; 416 int *a_eofflag; 417 u_long *a_cookies; 418 int a_ncookies; 419 } */ *ap; 420 { 421 struct uio *uio = ap->a_uio; 422 struct filedesc *fdp; 423 struct dirent d; 424 struct dirent *dp = &d; 425 int error, i, off, fcnt; 426 427 /* 428 * We don't allow exporting fdesc mounts, and currently local 429 * requests do not need cookies. 430 */ 431 if (ap->a_ncookies) 432 panic("fdesc_readdir: not hungry"); 433 434 if (VTOFDESC(ap->a_vp)->fd_type != Froot) 435 panic("fdesc_readdir: not dir"); 436 437 off = (int)uio->uio_offset; 438 if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 || 439 uio->uio_resid < UIO_MX) 440 return (EINVAL); 441 i = (u_int)off / UIO_MX; 442 fdp = uio->uio_td->td_proc->p_fd; 443 error = 0; 444 445 fcnt = i - 2; /* The first two nodes are `.' and `..' */ 446 447 FILEDESC_LOCK(fdp); 448 while (i < fdp->fd_nfiles + 2 && uio->uio_resid >= UIO_MX) { 449 switch (i) { 450 case 0: /* `.' */ 451 case 1: /* `..' */ 452 bzero((caddr_t)dp, UIO_MX); 453 454 dp->d_fileno = i + FD_ROOT; 455 dp->d_namlen = i + 1; 456 dp->d_reclen = UIO_MX; 457 bcopy("..", dp->d_name, dp->d_namlen); 458 dp->d_name[i + 1] = '\0'; 459 dp->d_type = DT_DIR; 460 break; 461 default: 462 if (fdp->fd_ofiles[fcnt] == NULL) { 463 FILEDESC_UNLOCK(fdp); 464 goto done; 465 } 466 467 bzero((caddr_t) dp, UIO_MX); 468 dp->d_namlen = sprintf(dp->d_name, "%d", fcnt); 469 dp->d_reclen = UIO_MX; 470 dp->d_type = DT_UNKNOWN; 471 dp->d_fileno = i + FD_DESC; 472 break; 473 } 474 /* 475 * And ship to userland 476 */ 477 FILEDESC_UNLOCK(fdp); 478 error = uiomove((caddr_t) dp, UIO_MX, uio); 479 if (error) 480 goto done; 481 FILEDESC_LOCK(fdp); 482 i++; 483 fcnt++; 484 } 485 FILEDESC_UNLOCK(fdp); 486 487 done: 488 uio->uio_offset = i * UIO_MX; 489 return (error); 490 } 491 492 static int 493 fdesc_poll(ap) 494 struct vop_poll_args /* { 495 struct vnode *a_vp; 496 int a_events; 497 struct ucred *a_cred; 498 struct thread *a_td; 499 } */ *ap; 500 { 501 return seltrue(0, ap->a_events, ap->a_td); 502 } 503 504 static int 505 fdesc_inactive(ap) 506 struct vop_inactive_args /* { 507 struct vnode *a_vp; 508 struct thread *a_td; 509 } */ *ap; 510 { 511 struct vnode *vp = ap->a_vp; 512 513 /* 514 * Clear out the v_type field to avoid 515 * nasty things happening in vgone(). 516 */ 517 VOP_UNLOCK(vp, 0, ap->a_td); 518 vp->v_type = VNON; 519 return (0); 520 } 521 522 static int 523 fdesc_reclaim(ap) 524 struct vop_reclaim_args /* { 525 struct vnode *a_vp; 526 } */ *ap; 527 { 528 struct vnode *vp = ap->a_vp; 529 struct fdescnode *fd = VTOFDESC(vp); 530 531 LIST_REMOVE(fd, fd_hash); 532 FREE(vp->v_data, M_TEMP); 533 vp->v_data = 0; 534 535 return (0); 536 } 537 538 static struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = { 539 { &vop_default_desc, (vop_t *) vop_defaultop }, 540 { &vop_access_desc, (vop_t *) vop_null }, 541 { &vop_getattr_desc, (vop_t *) fdesc_getattr }, 542 { &vop_inactive_desc, (vop_t *) fdesc_inactive }, 543 { &vop_lookup_desc, (vop_t *) fdesc_lookup }, 544 { &vop_open_desc, (vop_t *) fdesc_open }, 545 { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, 546 { &vop_poll_desc, (vop_t *) fdesc_poll }, 547 { &vop_print_desc, (vop_t *) vop_null }, 548 { &vop_readdir_desc, (vop_t *) fdesc_readdir }, 549 { &vop_reclaim_desc, (vop_t *) fdesc_reclaim }, 550 { &vop_setattr_desc, (vop_t *) fdesc_setattr }, 551 { &vop_lock_desc, (vop_t *) vop_stdlock }, 552 { &vop_unlock_desc, (vop_t *) vop_stdunlock }, 553 { &vop_islocked_desc, (vop_t *) vop_stdislocked }, 554 { NULL, NULL } 555 }; 556 static struct vnodeopv_desc fdesc_vnodeop_opv_desc = 557 { &fdesc_vnodeop_p, fdesc_vnodeop_entries }; 558 559 VNODEOP_SET(fdesc_vnodeop_opv_desc); 560