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( &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( &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_flags & ISLASTCN) && 184 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) { 185 error = EROFS; 186 goto bad; 187 } 188 189 VOP_UNLOCK(dvp, 0, td); 190 if (cnp->cn_namelen == 1 && *pname == '.') { 191 *vpp = dvp; 192 VREF(dvp); 193 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 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 ((error = fget(td, fd, &fp)) != 0) 217 goto bad; 218 219 error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp, td); 220 fdrop(fp, td); 221 if (error) 222 goto bad; 223 VTOFDESC(fvp)->fd_fd = fd; 224 vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, td); 225 *vpp = fvp; 226 return (0); 227 228 bad: 229 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 230 *vpp = NULL; 231 return (error); 232 } 233 234 static int 235 fdesc_open(ap) 236 struct vop_open_args /* { 237 struct vnode *a_vp; 238 int a_mode; 239 struct ucred *a_cred; 240 struct thread *a_td; 241 } */ *ap; 242 { 243 struct vnode *vp = ap->a_vp; 244 245 if (VTOFDESC(vp)->fd_type == Froot) 246 return (0); 247 248 /* 249 * XXX Kludge: set td->td_proc->p_dupfd to contain the value of the the file 250 * descriptor being sought for duplication. The error return ensures 251 * that the vnode for this device will be released by vn_open. Open 252 * will detect this special error and take the actions in dupfdopen. 253 * Other callers of vn_open or VOP_OPEN will simply report the 254 * error. 255 */ 256 ap->a_td->td_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */ 257 return (ENODEV); 258 } 259 260 static int 261 fdesc_getattr(ap) 262 struct vop_getattr_args /* { 263 struct vnode *a_vp; 264 struct vattr *a_vap; 265 struct ucred *a_cred; 266 struct thread *a_td; 267 } */ *ap; 268 { 269 struct vnode *vp = ap->a_vp; 270 struct vattr *vap = ap->a_vap; 271 struct file *fp; 272 struct stat stb; 273 u_int fd; 274 int error = 0; 275 276 switch (VTOFDESC(vp)->fd_type) { 277 case Froot: 278 VATTR_NULL(vap); 279 280 vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 281 vap->va_type = VDIR; 282 vap->va_nlink = 2; 283 vap->va_size = DEV_BSIZE; 284 vap->va_fileid = VTOFDESC(vp)->fd_ix; 285 vap->va_uid = 0; 286 vap->va_gid = 0; 287 vap->va_blocksize = DEV_BSIZE; 288 vap->va_atime.tv_sec = boottime.tv_sec; 289 vap->va_atime.tv_nsec = 0; 290 vap->va_mtime = vap->va_atime; 291 vap->va_ctime = vap->va_mtime; 292 vap->va_gen = 0; 293 vap->va_flags = 0; 294 vap->va_rdev = 0; 295 vap->va_bytes = 0; 296 break; 297 298 case Fdesc: 299 fd = VTOFDESC(vp)->fd_fd; 300 301 if ((error = fget(ap->a_td, fd, &fp)) != 0) 302 return (error); 303 304 bzero(&stb, sizeof(stb)); 305 error = fo_stat(fp, &stb, ap->a_td->td_ucred, ap->a_td); 306 fdrop(fp, ap->a_td); 307 if (error == 0) { 308 VATTR_NULL(vap); 309 vap->va_type = IFTOVT(stb.st_mode); 310 vap->va_mode = stb.st_mode; 311 #define FDRX (VREAD|VEXEC) 312 if (vap->va_type == VDIR) 313 vap->va_mode &= ~((FDRX)|(FDRX>>3)|(FDRX>>6)); 314 #undef FDRX 315 vap->va_nlink = 1; 316 vap->va_flags = 0; 317 vap->va_bytes = stb.st_blocks * stb.st_blksize; 318 vap->va_fileid = VTOFDESC(vp)->fd_ix; 319 vap->va_size = stb.st_size; 320 vap->va_blocksize = stb.st_blksize; 321 vap->va_rdev = stb.st_rdev; 322 323 /* 324 * If no time data is provided, use the current time. 325 */ 326 if (stb.st_atimespec.tv_sec == 0 && 327 stb.st_atimespec.tv_nsec == 0) 328 nanotime(&stb.st_atimespec); 329 330 if (stb.st_ctimespec.tv_sec == 0 && 331 stb.st_ctimespec.tv_nsec == 0) 332 nanotime(&stb.st_ctimespec); 333 334 if (stb.st_mtimespec.tv_sec == 0 && 335 stb.st_mtimespec.tv_nsec == 0) 336 nanotime(&stb.st_mtimespec); 337 338 vap->va_atime = stb.st_atimespec; 339 vap->va_mtime = stb.st_mtimespec; 340 vap->va_ctime = stb.st_ctimespec; 341 vap->va_uid = stb.st_uid; 342 vap->va_gid = stb.st_gid; 343 } 344 break; 345 346 default: 347 panic("fdesc_getattr"); 348 break; 349 } 350 351 if (error == 0) 352 vp->v_type = vap->va_type; 353 return (error); 354 } 355 356 static int 357 fdesc_setattr(ap) 358 struct vop_setattr_args /* { 359 struct vnode *a_vp; 360 struct vattr *a_vap; 361 struct ucred *a_cred; 362 struct thread *a_td; 363 } */ *ap; 364 { 365 struct vattr *vap = ap->a_vap; 366 struct vnode *vp; 367 struct mount *mp; 368 struct file *fp; 369 unsigned fd; 370 int error; 371 372 /* 373 * Can't mess with the root vnode 374 */ 375 if (VTOFDESC(ap->a_vp)->fd_type == Froot) 376 return (EACCES); 377 378 fd = VTOFDESC(ap->a_vp)->fd_fd; 379 380 /* 381 * Allow setattr where there is an underlying vnode. 382 */ 383 error = getvnode(ap->a_td->td_proc->p_fd, fd, &fp); 384 if (error) { 385 /* 386 * getvnode() returns EINVAL if the file descriptor is not 387 * backed by a vnode. Silently drop all changes except 388 * chflags(2) in this case. 389 */ 390 if (error == EINVAL) { 391 if (vap->va_flags != VNOVAL) 392 error = EOPNOTSUPP; 393 else 394 error = 0; 395 } 396 return (error); 397 } 398 vp = fp->f_vnode; 399 if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { 400 fdrop(fp, ap->a_td); 401 return (error); 402 } 403 error = VOP_SETATTR(vp, ap->a_vap, ap->a_cred, ap->a_td); 404 vn_finished_write(mp); 405 fdrop(fp, ap->a_td); 406 return (error); 407 } 408 409 #define UIO_MX 16 410 411 static int 412 fdesc_readdir(ap) 413 struct vop_readdir_args /* { 414 struct vnode *a_vp; 415 struct uio *a_uio; 416 struct ucred *a_cred; 417 int *a_eofflag; 418 u_long *a_cookies; 419 int a_ncookies; 420 } */ *ap; 421 { 422 struct uio *uio = ap->a_uio; 423 struct filedesc *fdp; 424 struct dirent d; 425 struct dirent *dp = &d; 426 int error, i, off, fcnt; 427 428 /* 429 * We don't allow exporting fdesc mounts, and currently local 430 * requests do not need cookies. 431 */ 432 if (ap->a_ncookies) 433 panic("fdesc_readdir: not hungry"); 434 435 if (VTOFDESC(ap->a_vp)->fd_type != Froot) 436 panic("fdesc_readdir: not dir"); 437 438 off = (int)uio->uio_offset; 439 if (off != uio->uio_offset || off < 0 || (u_int)off % UIO_MX != 0 || 440 uio->uio_resid < UIO_MX) 441 return (EINVAL); 442 i = (u_int)off / UIO_MX; 443 fdp = uio->uio_td->td_proc->p_fd; 444 error = 0; 445 446 fcnt = i - 2; /* The first two nodes are `.' and `..' */ 447 448 FILEDESC_LOCK(fdp); 449 while (i < fdp->fd_nfiles + 2 && uio->uio_resid >= UIO_MX) { 450 switch (i) { 451 case 0: /* `.' */ 452 case 1: /* `..' */ 453 bzero((caddr_t)dp, UIO_MX); 454 455 dp->d_fileno = i + FD_ROOT; 456 dp->d_namlen = i + 1; 457 dp->d_reclen = UIO_MX; 458 bcopy("..", dp->d_name, dp->d_namlen); 459 dp->d_name[i + 1] = '\0'; 460 dp->d_type = DT_DIR; 461 break; 462 default: 463 if (fdp->fd_ofiles[fcnt] == NULL) { 464 FILEDESC_UNLOCK(fdp); 465 goto done; 466 } 467 468 bzero((caddr_t) dp, UIO_MX); 469 dp->d_namlen = sprintf(dp->d_name, "%d", fcnt); 470 dp->d_reclen = UIO_MX; 471 dp->d_type = DT_UNKNOWN; 472 dp->d_fileno = i + FD_DESC; 473 break; 474 } 475 /* 476 * And ship to userland 477 */ 478 FILEDESC_UNLOCK(fdp); 479 error = uiomove(dp, UIO_MX, uio); 480 if (error) 481 goto done; 482 FILEDESC_LOCK(fdp); 483 i++; 484 fcnt++; 485 } 486 FILEDESC_UNLOCK(fdp); 487 488 done: 489 uio->uio_offset = i * UIO_MX; 490 return (error); 491 } 492 493 static int 494 fdesc_poll(ap) 495 struct vop_poll_args /* { 496 struct vnode *a_vp; 497 int a_events; 498 struct ucred *a_cred; 499 struct thread *a_td; 500 } */ *ap; 501 { 502 return seltrue(0, ap->a_events, ap->a_td); 503 } 504 505 static int 506 fdesc_inactive(ap) 507 struct vop_inactive_args /* { 508 struct vnode *a_vp; 509 struct thread *a_td; 510 } */ *ap; 511 { 512 struct vnode *vp = ap->a_vp; 513 514 /* 515 * Clear out the v_type field to avoid 516 * nasty things happening in vgone(). 517 */ 518 VOP_UNLOCK(vp, 0, ap->a_td); 519 vp->v_type = VNON; 520 return (0); 521 } 522 523 static int 524 fdesc_reclaim(ap) 525 struct vop_reclaim_args /* { 526 struct vnode *a_vp; 527 } */ *ap; 528 { 529 struct vnode *vp = ap->a_vp; 530 struct fdescnode *fd = VTOFDESC(vp); 531 532 LIST_REMOVE(fd, fd_hash); 533 FREE(vp->v_data, M_TEMP); 534 vp->v_data = 0; 535 536 return (0); 537 } 538 539 static struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = { 540 { &vop_default_desc, (vop_t *) vop_defaultop }, 541 { &vop_access_desc, (vop_t *) vop_null }, 542 { &vop_getattr_desc, (vop_t *) fdesc_getattr }, 543 { &vop_inactive_desc, (vop_t *) fdesc_inactive }, 544 { &vop_lookup_desc, (vop_t *) fdesc_lookup }, 545 { &vop_open_desc, (vop_t *) fdesc_open }, 546 { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, 547 { &vop_poll_desc, (vop_t *) fdesc_poll }, 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 { NULL, NULL } 552 }; 553 static struct vnodeopv_desc fdesc_vnodeop_opv_desc = 554 { &fdesc_vnodeop_p, fdesc_vnodeop_entries }; 555 556 VNODEOP_SET(fdesc_vnodeop_opv_desc); 557