1 #define DEBUG 1 2 /* 3 * Copyright (c) 1992, 1993 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 2000 6 * Poul-Henning Kamp. All rights reserved. 7 * 8 * This code is derived from software donated to Berkeley by 9 * Jan-Simon Pendry. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. 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 * @(#)kernfs_vnops.c 8.15 (Berkeley) 5/21/95 33 * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43 34 * 35 * $FreeBSD$ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/vmmeter.h> 42 #include <sys/time.h> 43 #include <sys/conf.h> 44 #include <sys/vnode.h> 45 #include <sys/malloc.h> 46 #include <sys/proc.h> 47 #include <sys/stat.h> 48 #include <sys/mount.h> 49 #include <sys/namei.h> 50 #include <sys/dirent.h> 51 #include <sys/resource.h> 52 #include <sys/eventhandler.h> 53 54 #define DEVFS_INTERN 55 #include <fs/devfs/devfs.h> 56 57 #define KSTRING 256 /* Largest I/O available via this filesystem */ 58 #define UIO_MX 32 59 60 static int devfs_access __P((struct vop_access_args *ap)); 61 static int devfs_badop __P((void)); 62 static int devfs_getattr __P((struct vop_getattr_args *ap)); 63 static int devfs_lookup __P((struct vop_lookup_args *ap)); 64 static int devfs_print __P((struct vop_print_args *ap)); 65 static int devfs_readdir __P((struct vop_readdir_args *ap)); 66 static int devfs_readlink __P((struct vop_readlink_args *ap)); 67 static int devfs_reclaim __P((struct vop_reclaim_args *ap)); 68 static int devfs_remove __P((struct vop_remove_args *ap)); 69 static int devfs_revoke __P((struct vop_revoke_args *ap)); 70 static int devfs_setattr __P((struct vop_setattr_args *ap)); 71 static int devfs_symlink __P((struct vop_symlink_args *ap)); 72 73 74 static int 75 devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct proc *p) 76 { 77 int error; 78 struct vnode *vp; 79 80 loop: 81 vp = de->de_vnode; 82 if (vp != NULL) { 83 if (vget(vp, 0, p ? p : curproc)) 84 goto loop; 85 *vpp = vp; 86 return (0); 87 } 88 error = getnewvnode(VT_DEVFS, mp, devfs_vnodeop_p, &vp); 89 if (error != 0) { 90 printf("devfs_allocv: failed to allocate new vnode\n"); 91 return (error); 92 } 93 94 if (de->de_dirent->d_type == DT_CHR) { 95 vp->v_type = VCHR; 96 vp = addaliasu(vp, devfs_inot[de->de_inode]->si_udev); 97 vp->v_op = devfs_specop_p; 98 vp->v_data = de; 99 } else if (de->de_dirent->d_type == DT_DIR) { 100 vp->v_type = VDIR; 101 vp->v_data = de->de_dir; 102 TAILQ_FIRST(&de->de_dir->dd_list)->de_vnode = vp; 103 } else if (de->de_dirent->d_type == DT_LNK) { 104 vp->v_type = VLNK; 105 vp->v_data = de; 106 } else { 107 vp->v_type = VBAD; 108 vp->v_data = de; 109 } 110 de->de_vnode = vp; 111 vhold(vp); 112 *vpp = vp; 113 return (0); 114 } 115 /* 116 * vp is the current namei directory 117 * ndp is the name to locate in that directory... 118 */ 119 static int 120 devfs_lookup(ap) 121 struct vop_lookup_args /* { 122 struct vnode * a_dvp; 123 struct vnode ** a_vpp; 124 struct componentname * a_cnp; 125 } */ *ap; 126 { 127 struct componentname *cnp = ap->a_cnp; 128 struct vnode **vpp = ap->a_vpp; 129 struct vnode *dvp = ap->a_dvp; 130 char *pname = cnp->cn_nameptr; 131 struct proc *p = cnp->cn_proc; 132 struct devfs_dir *dd; 133 struct devfs_dirent *de; 134 struct devfs_mount *fmp; 135 dev_t cdev; 136 int error, cloned; 137 138 *vpp = NULLVP; 139 140 VOP_UNLOCK(dvp, 0, p); 141 if (cnp->cn_namelen == 1 && *pname == '.') { 142 *vpp = dvp; 143 VREF(dvp); 144 vn_lock(dvp, LK_SHARED | LK_RETRY, p); 145 return (0); 146 } 147 148 cloned = 0; 149 150 fmp = (struct devfs_mount *)dvp->v_mount->mnt_data; 151 again: 152 153 devfs_populate(fmp); 154 dd = dvp->v_data; 155 TAILQ_FOREACH(de, &dd->dd_list, de_list) { 156 if (cnp->cn_namelen != de->de_dirent->d_namlen) 157 continue; 158 if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name, de->de_dirent->d_namlen) != 0) 159 continue; 160 goto found; 161 } 162 163 if (!cloned) { 164 /* OK, we didn't have that one, so lets try to create it on the fly... */ 165 cdev = NODEV; 166 EVENTHANDLER_INVOKE(devfs_clone, cnp->cn_nameptr, cnp->cn_namelen, &cdev); 167 #if 0 168 printf("cloned %s -> %p %s\n", cnp->cn_nameptr, cdev, 169 cdev == NODEV ? "NODEV" : cdev->si_name); 170 #endif 171 if (cdev != NODEV) { 172 cloned = 1; 173 goto again; 174 } 175 } 176 177 /* No luck, too bad. */ 178 179 if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && 180 (cnp->cn_flags & ISLASTCN)) { 181 cnp->cn_flags |= SAVENAME; 182 if (!(cnp->cn_flags & LOCKPARENT)) 183 VOP_UNLOCK(dvp, 0, p); 184 return (EJUSTRETURN); 185 } else { 186 vn_lock(dvp, LK_SHARED | LK_RETRY, p); 187 return (ENOENT); 188 } 189 190 191 found: 192 193 error = devfs_allocv(de, dvp->v_mount, vpp, p); 194 if (error != 0) { 195 vn_lock(dvp, LK_SHARED | LK_RETRY, p); 196 return (error); 197 } 198 if ((cnp->cn_nameiop == DELETE) && (cnp->cn_flags & ISLASTCN)) { 199 if (*vpp == dvp) { 200 VREF(dvp); 201 *vpp = dvp; 202 return (0); 203 } 204 VREF(*vpp); 205 if (!(cnp->cn_flags & LOCKPARENT)) 206 VOP_UNLOCK(dvp, 0, p); 207 return (0); 208 } 209 vn_lock(*vpp, LK_SHARED | LK_RETRY, p); 210 if (!(cnp->cn_flags & LOCKPARENT)) 211 VOP_UNLOCK(dvp, 0, p); 212 return (0); 213 } 214 215 static int 216 devfs_access(ap) 217 struct vop_access_args /* { 218 struct vnode *a_vp; 219 int a_mode; 220 struct ucred *a_cred; 221 struct proc *a_p; 222 } */ *ap; 223 { 224 struct vnode *vp = ap->a_vp; 225 mode_t amode = ap->a_mode; 226 struct devfs_dirent *de = vp->v_data; 227 mode_t fmode = de->de_mode; 228 229 /* Some files are simply not modifiable. */ 230 if ((amode & VWRITE) && (fmode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0) 231 return (EPERM); 232 233 return (vaccess(vp->v_type, de->de_mode, de->de_uid, de->de_gid, 234 ap->a_mode, ap->a_cred)); 235 } 236 237 static int 238 devfs_getattr(ap) 239 struct vop_getattr_args /* { 240 struct vnode *a_vp; 241 struct vattr *a_vap; 242 struct ucred *a_cred; 243 struct proc *a_p; 244 } */ *ap; 245 { 246 struct vnode *vp = ap->a_vp; 247 struct vattr *vap = ap->a_vap; 248 int error = 0; 249 struct devfs_dirent *de; 250 struct devfs_dir *dd; 251 252 if (vp->v_type == VDIR) { 253 dd = vp->v_data; 254 de = TAILQ_FIRST(&dd->dd_list); 255 } else { 256 de = vp->v_data; 257 } 258 bzero((caddr_t) vap, sizeof(*vap)); 259 vattr_null(vap); 260 vap->va_uid = de->de_uid; 261 vap->va_gid = de->de_gid; 262 vap->va_mode = de->de_mode; 263 vap->va_size = 0; 264 vap->va_blocksize = DEV_BSIZE; 265 vap->va_atime = de->de_atime; 266 vap->va_mtime = de->de_mtime; 267 vap->va_ctime = de->de_ctime; 268 vap->va_gen = 0; 269 vap->va_flags = 0; 270 vap->va_rdev = 0; 271 vap->va_bytes = 0; 272 vap->va_nlink = 1; 273 vap->va_fileid = de->de_inode; 274 275 #if 0 276 if (vp->v_flag & VROOT) { 277 #ifdef DEBUG 278 printf("devfs_getattr: stat rootdir\n"); 279 #endif 280 vap->va_type = VDIR; 281 vap->va_nlink = 2; 282 vap->va_fileid = 2; 283 vap->va_size = DEV_BSIZE; 284 } else 285 #endif 286 287 if (de->de_dirent->d_type == DT_DIR) { 288 vap->va_type = VDIR; 289 } else if (de->de_dirent->d_type == DT_LNK) { 290 vap->va_type = VLNK; 291 } else if (de->de_dirent->d_type == DT_CHR) { 292 vap->va_type = VCHR; 293 vap->va_rdev = devfs_inot[de->de_inode]->si_udev; 294 } 295 296 #ifdef DEBUG 297 if (error) 298 printf("devfs_getattr: return error %d\n", error); 299 #endif 300 return (error); 301 } 302 303 static int 304 devfs_setattr(ap) 305 struct vop_setattr_args /* { 306 struct vnode *a_vp; 307 struct vattr *a_vap; 308 struct ucred *a_cred; 309 struct proc *a_p; 310 } */ *ap; 311 { 312 struct devfs_dir *dd; 313 struct devfs_dirent *de; 314 315 if (ap->a_vp->v_type == VDIR) { 316 dd = ap->a_vp->v_data; 317 de = TAILQ_FIRST(&dd->dd_list); 318 } else { 319 de = ap->a_vp->v_data; 320 } 321 322 if (ap->a_vap->va_flags != VNOVAL) 323 return (EOPNOTSUPP); 324 if (ap->a_vap->va_uid != (uid_t)VNOVAL) 325 de->de_uid = ap->a_vap->va_uid; 326 if (ap->a_vap->va_gid != (gid_t)VNOVAL) 327 de->de_gid = ap->a_vap->va_gid; 328 if (ap->a_vap->va_mode != (mode_t)VNOVAL) 329 de->de_mode = ap->a_vap->va_mode; 330 if (ap->a_vap->va_atime.tv_sec != VNOVAL) 331 de->de_atime = ap->a_vap->va_atime; 332 if (ap->a_vap->va_mtime.tv_sec != VNOVAL) 333 de->de_mtime = ap->a_vap->va_mtime; 334 335 /* 336 * Silently ignore attribute changes. 337 * This allows for open with truncate to have no 338 * effect until some data is written. I want to 339 * do it this way because all writes are atomic. 340 */ 341 return (0); 342 } 343 344 static int 345 devfs_readdir(ap) 346 struct vop_readdir_args /* { 347 struct vnode *a_vp; 348 struct uio *a_uio; 349 struct ucred *a_cred; 350 int *a_eofflag; 351 int *a_ncookies; 352 u_long **a_cookies; 353 } */ *ap; 354 { 355 int error, i; 356 struct uio *uio = ap->a_uio; 357 struct dirent *dp; 358 struct devfs_dir *dd; 359 struct devfs_dirent *de; 360 off_t off; 361 362 if (ap->a_vp->v_type != VDIR) 363 return (ENOTDIR); 364 365 i = (u_int)off / UIO_MX; 366 error = 0; 367 dd = ap->a_vp->v_data; 368 de = TAILQ_FIRST(&dd->dd_list); 369 off = 0; 370 while (uio->uio_resid >= UIO_MX && de != NULL) { 371 dp = de->de_dirent; 372 dp->d_fileno = de->de_inode; 373 if (off >= uio->uio_offset) 374 if ((error = uiomove((caddr_t)dp, dp->d_reclen, uio)) != 0) 375 break; 376 off += dp->d_reclen; 377 de = TAILQ_NEXT(de, de_list); 378 } 379 380 uio->uio_offset = off; 381 382 return (error); 383 } 384 385 static int 386 devfs_readlink(ap) 387 struct vop_readlink_args /* { 388 struct vnode *a_vp; 389 struct uio *a_uio; 390 struct ucred *a_cead; 391 } */ *ap; 392 { 393 int error; 394 struct devfs_dirent *de; 395 396 de = ap->a_vp->v_data; 397 error = uiomove(de->de_symlink, strlen(de->de_symlink) + 1, ap->a_uio); 398 return (error); 399 } 400 401 static int 402 devfs_reclaim(ap) 403 struct vop_reclaim_args /* { 404 struct vnode *a_vp; 405 } */ *ap; 406 { 407 struct vnode *vp = ap->a_vp; 408 409 vp->v_data = NULL; 410 return (0); 411 } 412 413 static int 414 devfs_remove(ap) 415 struct vop_remove_args /* { 416 struct vnode *a_dvp; 417 struct vnode *a_vp; 418 struct componentname *a_cnp; 419 } */ *ap; 420 { 421 struct vnode *vp = ap->a_vp; 422 struct devfs_dir *dd; 423 struct devfs_dirent *de; 424 425 dd = ap->a_dvp->v_data; 426 de = vp->v_data; 427 devfs_delete(dd, de); 428 return (0); 429 } 430 431 /* 432 * Revoke is called on a tty when a terminal session ends. The vnode 433 * is orphaned by setting v_op to deadfs so we need to let go of it 434 * as well so that we create a new one next time around. 435 */ 436 static int 437 devfs_revoke(ap) 438 struct vop_revoke_args /* { 439 struct vnode *a_vp; 440 int a_flags; 441 } */ *ap; 442 { 443 struct vnode *vp = ap->a_vp; 444 struct devfs_dirent *de; 445 446 de = vp->v_data; 447 vdrop(de->de_vnode); 448 de->de_vnode = NULL; 449 vop_revoke(ap); 450 return (0); 451 } 452 453 static int 454 devfs_symlink(ap) 455 struct vop_symlink_args /* { 456 struct vnode *a_dvp; 457 struct vnode **a_vpp; 458 struct componentname *a_cnp; 459 struct vattr *a_vap; 460 char *a_target; 461 } */ *ap; 462 { 463 int i; 464 struct devfs_dir *dd; 465 struct devfs_dirent *de; 466 struct devfs_mount *fmp; 467 468 fmp = (struct devfs_mount *)ap->a_dvp->v_mount->mnt_data; 469 dd = ap->a_dvp->v_data; 470 de = devfs_newdirent(ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen); 471 de->de_uid = 0; 472 de->de_gid = 0; 473 de->de_mode = 0642; 474 de->de_inode = fmp->dm_inode++; 475 de->de_dirent->d_type = DT_LNK; 476 i = strlen(ap->a_target) + 1; 477 MALLOC(de->de_symlink, char *, i, M_DEVFS, M_WAITOK); 478 bcopy(ap->a_target, de->de_symlink, i); 479 TAILQ_INSERT_TAIL(&dd->dd_list, de, de_list); 480 devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, 0); 481 VREF(*(ap->a_vpp)); 482 return (0); 483 } 484 485 /* 486 * Print out the contents of a devfs vnode. 487 */ 488 /* ARGSUSED */ 489 static int 490 devfs_print(ap) 491 struct vop_print_args /* { 492 struct vnode *a_vp; 493 } */ *ap; 494 { 495 496 printf("tag VT_DEVFS, devfs vnode\n"); 497 return (0); 498 } 499 500 /* 501 * Kernfs "should never get here" operation 502 */ 503 static int 504 devfs_badop() 505 { 506 return (EIO); 507 } 508 509 vop_t **devfs_vnodeop_p; 510 static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = { 511 { &vop_default_desc, (vop_t *) vop_defaultop }, 512 { &vop_access_desc, (vop_t *) devfs_access }, 513 { &vop_bmap_desc, (vop_t *) devfs_badop }, 514 { &vop_getattr_desc, (vop_t *) devfs_getattr }, 515 { &vop_lookup_desc, (vop_t *) devfs_lookup }, 516 { &vop_pathconf_desc, (vop_t *) vop_stdpathconf }, 517 { &vop_print_desc, (vop_t *) devfs_print }, 518 { &vop_readdir_desc, (vop_t *) devfs_readdir }, 519 { &vop_readlink_desc, (vop_t *) devfs_readlink }, 520 { &vop_reclaim_desc, (vop_t *) devfs_reclaim }, 521 { &vop_remove_desc, (vop_t *) devfs_remove }, 522 { &vop_revoke_desc, (vop_t *) devfs_revoke }, 523 { &vop_setattr_desc, (vop_t *) devfs_setattr }, 524 { &vop_symlink_desc, (vop_t *) devfs_symlink }, 525 { NULL, NULL } 526 }; 527 static struct vnodeopv_desc devfs_vnodeop_opv_desc = 528 { &devfs_vnodeop_p, devfs_vnodeop_entries }; 529 530 VNODEOP_SET(devfs_vnodeop_opv_desc); 531 532 #if 0 533 int 534 foo(ap) 535 struct vop_generic_args *ap; 536 { 537 int i; 538 539 i = spec_vnoperate(ap); 540 printf("foo(%s) = %d\n", ap->a_desc->vdesc_name, i); 541 return (i); 542 } 543 #endif 544 545 vop_t **devfs_specop_p; 546 static struct vnodeopv_entry_desc devfs_specop_entries[] = { 547 #if 1 548 { &vop_default_desc, (vop_t *) spec_vnoperate }, 549 #else 550 { &vop_default_desc, (vop_t *) foo }, 551 { &vop_lock_desc, (vop_t *) spec_vnoperate }, 552 { &vop_unlock_desc, (vop_t *) spec_vnoperate }, 553 { &vop_lease_desc, (vop_t *) spec_vnoperate }, 554 { &vop_strategy_desc, (vop_t *) spec_vnoperate }, 555 { &vop_bmap_desc, (vop_t *) spec_vnoperate }, 556 #endif 557 { &vop_access_desc, (vop_t *) devfs_access }, 558 { &vop_getattr_desc, (vop_t *) devfs_getattr }, 559 { &vop_print_desc, (vop_t *) devfs_print }, 560 { &vop_reclaim_desc, (vop_t *) devfs_reclaim }, 561 { &vop_remove_desc, (vop_t *) devfs_remove }, 562 { &vop_revoke_desc, (vop_t *) devfs_revoke }, 563 { &vop_setattr_desc, (vop_t *) devfs_setattr }, 564 { NULL, NULL } 565 }; 566 static struct vnodeopv_desc devfs_specop_opv_desc = 567 { &devfs_specop_p, devfs_specop_entries }; 568 569 VNODEOP_SET(devfs_specop_opv_desc); 570