1 /* $NetBSD: ptyfs_vnops.c,v 1.5 2004/11/29 13:55:59 atatat Exp $ */ 2 3 /* 4 * Copyright (c) 1993, 1995 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 35 */ 36 37 /* 38 * Copyright (c) 1993 Jan-Simon Pendry 39 * 40 * This code is derived from software contributed to Berkeley by 41 * Jan-Simon Pendry. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed by the University of 54 * California, Berkeley and its contributors. 55 * 4. Neither the name of the University nor the names of its contributors 56 * may be used to endorse or promote products derived from this software 57 * without specific prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 * SUCH DAMAGE. 70 * 71 * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 72 */ 73 74 /* 75 * ptyfs vnode interface 76 */ 77 78 #include <sys/cdefs.h> 79 __KERNEL_RCSID(0, "$NetBSD: ptyfs_vnops.c,v 1.5 2004/11/29 13:55:59 atatat Exp $"); 80 81 #include <sys/param.h> 82 #include <sys/systm.h> 83 #include <sys/time.h> 84 #include <sys/kernel.h> 85 #include <sys/file.h> 86 #include <sys/filedesc.h> 87 #include <sys/proc.h> 88 #include <sys/vnode.h> 89 #include <sys/namei.h> 90 #include <sys/malloc.h> 91 #include <sys/mount.h> 92 #include <sys/select.h> 93 #include <sys/dirent.h> 94 #include <sys/resourcevar.h> 95 #include <sys/stat.h> 96 #include <sys/conf.h> 97 #include <sys/tty.h> 98 #include <sys/pty.h> 99 100 #include <uvm/uvm_extern.h> /* for PAGE_SIZE */ 101 102 #include <machine/reg.h> 103 104 #include <fs/ptyfs/ptyfs.h> 105 #include <miscfs/genfs/genfs.h> 106 #include <miscfs/specfs/specdev.h> 107 108 /* 109 * Vnode Operations. 110 * 111 */ 112 113 int ptyfs_lookup (void *); 114 #define ptyfs_create genfs_eopnotsupp 115 #define ptyfs_mknod genfs_eopnotsupp 116 int ptyfs_open (void *); 117 int ptyfs_close (void *); 118 int ptyfs_access (void *); 119 int ptyfs_getattr (void *); 120 int ptyfs_setattr (void *); 121 int ptyfs_read (void *); 122 int ptyfs_write (void *); 123 #define ptyfs_fcntl genfs_fcntl 124 int ptyfs_ioctl (void *); 125 int ptyfs_poll (void *); 126 int ptyfs_kqfilter (void *); 127 #define ptyfs_revoke genfs_revoke 128 #define ptyfs_mmap genfs_eopnotsupp 129 #define ptyfs_fsync genfs_nullop 130 #define ptyfs_seek genfs_nullop 131 #define ptyfs_remove genfs_eopnotsupp 132 #define ptyfs_link genfs_abortop 133 #define ptyfs_rename genfs_eopnotsupp 134 #define ptyfs_mkdir genfs_eopnotsupp 135 #define ptyfs_rmdir genfs_eopnotsupp 136 #define ptyfs_symlink genfs_abortop 137 int ptyfs_readdir (void *); 138 #define ptyfs_readlink genfs_eopnotsupp 139 #define ptyfs_abortop genfs_abortop 140 int ptyfs_inactive (void *); 141 int ptyfs_reclaim (void *); 142 #define ptyfs_lock genfs_lock 143 #define ptyfs_unlock genfs_unlock 144 #define ptyfs_bmap genfs_badop 145 #define ptyfs_strategy genfs_badop 146 int ptyfs_print (void *); 147 int ptyfs_pathconf (void *); 148 #define ptyfs_islocked genfs_islocked 149 #define ptyfs_advlock genfs_einval 150 #define ptyfs_blkatoff genfs_eopnotsupp 151 #define ptyfs_valloc genfs_eopnotsupp 152 #define ptyfs_vfree genfs_nullop 153 #define ptyfs_truncate genfs_eopnotsupp 154 int ptyfs_update (void *); 155 #define ptyfs_bwrite genfs_eopnotsupp 156 #define ptyfs_putpages genfs_null_putpages 157 158 static int ptyfs_chown(struct vnode *, uid_t, gid_t, struct ucred *, 159 struct proc *); 160 static int ptyfs_chmod(struct vnode *, mode_t, struct ucred *, struct proc *); 161 static void ptyfs_time(struct ptyfsnode *, struct timespec *, 162 struct timespec *); 163 static int atoi(const char *, size_t); 164 165 extern const struct cdevsw pts_cdevsw, ptc_cdevsw; 166 167 /* 168 * ptyfs vnode operations. 169 */ 170 int (**ptyfs_vnodeop_p)(void *); 171 const struct vnodeopv_entry_desc ptyfs_vnodeop_entries[] = { 172 { &vop_default_desc, vn_default_error }, 173 { &vop_lookup_desc, ptyfs_lookup }, /* lookup */ 174 { &vop_create_desc, ptyfs_create }, /* create */ 175 { &vop_mknod_desc, ptyfs_mknod }, /* mknod */ 176 { &vop_open_desc, ptyfs_open }, /* open */ 177 { &vop_close_desc, ptyfs_close }, /* close */ 178 { &vop_access_desc, ptyfs_access }, /* access */ 179 { &vop_getattr_desc, ptyfs_getattr }, /* getattr */ 180 { &vop_setattr_desc, ptyfs_setattr }, /* setattr */ 181 { &vop_read_desc, ptyfs_read }, /* read */ 182 { &vop_write_desc, ptyfs_write }, /* write */ 183 { &vop_ioctl_desc, ptyfs_ioctl }, /* ioctl */ 184 { &vop_fcntl_desc, ptyfs_fcntl }, /* fcntl */ 185 { &vop_poll_desc, ptyfs_poll }, /* poll */ 186 { &vop_kqfilter_desc, ptyfs_kqfilter }, /* kqfilter */ 187 { &vop_revoke_desc, ptyfs_revoke }, /* revoke */ 188 { &vop_mmap_desc, ptyfs_mmap }, /* mmap */ 189 { &vop_fsync_desc, ptyfs_fsync }, /* fsync */ 190 { &vop_seek_desc, ptyfs_seek }, /* seek */ 191 { &vop_remove_desc, ptyfs_remove }, /* remove */ 192 { &vop_link_desc, ptyfs_link }, /* link */ 193 { &vop_rename_desc, ptyfs_rename }, /* rename */ 194 { &vop_mkdir_desc, ptyfs_mkdir }, /* mkdir */ 195 { &vop_rmdir_desc, ptyfs_rmdir }, /* rmdir */ 196 { &vop_symlink_desc, ptyfs_symlink }, /* symlink */ 197 { &vop_readdir_desc, ptyfs_readdir }, /* readdir */ 198 { &vop_readlink_desc, ptyfs_readlink }, /* readlink */ 199 { &vop_abortop_desc, ptyfs_abortop }, /* abortop */ 200 { &vop_inactive_desc, ptyfs_inactive }, /* inactive */ 201 { &vop_reclaim_desc, ptyfs_reclaim }, /* reclaim */ 202 { &vop_lock_desc, ptyfs_lock }, /* lock */ 203 { &vop_unlock_desc, ptyfs_unlock }, /* unlock */ 204 { &vop_bmap_desc, ptyfs_bmap }, /* bmap */ 205 { &vop_strategy_desc, ptyfs_strategy }, /* strategy */ 206 { &vop_print_desc, ptyfs_print }, /* print */ 207 { &vop_islocked_desc, ptyfs_islocked }, /* islocked */ 208 { &vop_pathconf_desc, ptyfs_pathconf }, /* pathconf */ 209 { &vop_advlock_desc, ptyfs_advlock }, /* advlock */ 210 { &vop_blkatoff_desc, ptyfs_blkatoff }, /* blkatoff */ 211 { &vop_valloc_desc, ptyfs_valloc }, /* valloc */ 212 { &vop_vfree_desc, ptyfs_vfree }, /* vfree */ 213 { &vop_truncate_desc, ptyfs_truncate }, /* truncate */ 214 { &vop_update_desc, ptyfs_update }, /* update */ 215 { &vop_bwrite_desc, ptyfs_bwrite }, /* bwrite */ 216 { &vop_putpages_desc, ptyfs_putpages }, /* putpages */ 217 { NULL, NULL } 218 }; 219 const struct vnodeopv_desc ptyfs_vnodeop_opv_desc = 220 { &ptyfs_vnodeop_p, ptyfs_vnodeop_entries }; 221 222 /* 223 * _inactive is called when the ptyfsnode 224 * is vrele'd and the reference count goes 225 * to zero. (vp) will be on the vnode free 226 * list, so to get it back vget() must be 227 * used. 228 * 229 * for ptyfs, check if the pty is still 230 * in use and if it isn't then just throw away 231 * the vnode by calling vgone(). 232 * 233 * (vp) is locked on entry, but must be unlocked on exit. 234 */ 235 int 236 ptyfs_inactive(void *v) 237 { 238 struct vop_inactive_args /* { 239 struct vnode *a_vp; 240 struct proc *a_p; 241 } */ *ap = v; 242 struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp); 243 244 VOP_UNLOCK(ap->a_vp, 0); 245 if (pty_isfree(ptyfs->ptyfs_pty, 1)) 246 vgone(ap->a_vp); 247 248 return 0; 249 } 250 251 /* 252 * _reclaim is called when getnewvnode() 253 * wants to make use of an entry on the vnode 254 * free list. at this time the filesystem needs 255 * to free any private data and remove the node 256 * from any private lists. 257 */ 258 int 259 ptyfs_reclaim(void *v) 260 { 261 struct vop_reclaim_args /* { 262 struct vnode *a_vp; 263 } */ *ap = v; 264 return ptyfs_freevp(ap->a_vp); 265 } 266 267 /* 268 * Return POSIX pathconf information applicable to special devices. 269 */ 270 int 271 ptyfs_pathconf(void *v) 272 { 273 struct vop_pathconf_args /* { 274 struct vnode *a_vp; 275 int a_name; 276 register_t *a_retval; 277 } */ *ap = v; 278 279 switch (ap->a_name) { 280 case _PC_LINK_MAX: 281 *ap->a_retval = LINK_MAX; 282 return 0; 283 case _PC_MAX_CANON: 284 *ap->a_retval = MAX_CANON; 285 return 0; 286 case _PC_MAX_INPUT: 287 *ap->a_retval = MAX_INPUT; 288 return 0; 289 case _PC_PIPE_BUF: 290 *ap->a_retval = PIPE_BUF; 291 return 0; 292 case _PC_CHOWN_RESTRICTED: 293 *ap->a_retval = 1; 294 return 0; 295 case _PC_VDISABLE: 296 *ap->a_retval = _POSIX_VDISABLE; 297 return 0; 298 case _PC_SYNC_IO: 299 *ap->a_retval = 1; 300 return 0; 301 default: 302 return EINVAL; 303 } 304 } 305 306 /* 307 * _print is used for debugging. 308 * just print a readable description 309 * of (vp). 310 */ 311 int 312 ptyfs_print(void *v) 313 { 314 struct vop_print_args /* { 315 struct vnode *a_vp; 316 } */ *ap = v; 317 struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp); 318 319 printf("tag VT_PTYFS, type %d, pty %d\n", 320 ptyfs->ptyfs_type, ptyfs->ptyfs_pty); 321 return 0; 322 } 323 324 /* 325 * Invent attributes for ptyfsnode (vp) and store 326 * them in (vap). 327 * Directories lengths are returned as zero since 328 * any real length would require the genuine size 329 * to be computed, and nothing cares anyway. 330 * 331 * this is relatively minimal for ptyfs. 332 */ 333 int 334 ptyfs_getattr(void *v) 335 { 336 struct vop_getattr_args /* { 337 struct vnode *a_vp; 338 struct vattr *a_vap; 339 struct ucred *a_cred; 340 struct proc *a_p; 341 } */ *ap = v; 342 struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp); 343 struct vattr *vap = ap->a_vap; 344 struct timespec ts; 345 346 TIMEVAL_TO_TIMESPEC(&time, &ts); 347 ptyfs_time(ptyfs, &ts, &ts); 348 349 /* start by zeroing out the attributes */ 350 VATTR_NULL(vap); 351 352 /* next do all the common fields */ 353 vap->va_type = ap->a_vp->v_type; 354 vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0]; 355 vap->va_fileid = ptyfs->ptyfs_fileno; 356 vap->va_gen = 0; 357 vap->va_flags = 0; 358 vap->va_nlink = 1; 359 vap->va_blocksize = PAGE_SIZE; 360 361 vap->va_atime = ptyfs->ptyfs_atime; 362 vap->va_mtime = ptyfs->ptyfs_mtime; 363 vap->va_ctime = ptyfs->ptyfs_ctime; 364 vap->va_birthtime = ptyfs->ptyfs_birthtime; 365 vap->va_mode = ptyfs->ptyfs_mode; 366 vap->va_flags = ptyfs->ptyfs_flags; 367 vap->va_uid = ptyfs->ptyfs_uid; 368 vap->va_gid = ptyfs->ptyfs_gid; 369 370 switch (ptyfs->ptyfs_type) { 371 case PTYFSpts: 372 case PTYFSptc: 373 if (pty_isfree(ptyfs->ptyfs_pty, 1)) 374 return ENOENT; 375 vap->va_bytes = vap->va_size = 0; 376 vap->va_rdev = ap->a_vp->v_rdev; 377 break; 378 case PTYFSroot: 379 vap->va_rdev = 0; 380 vap->va_bytes = vap->va_size = DEV_BSIZE; 381 break; 382 383 default: 384 return EOPNOTSUPP; 385 } 386 387 return 0; 388 } 389 390 /*ARGSUSED*/ 391 int 392 ptyfs_setattr(void *v) 393 { 394 struct vop_setattr_args /* { 395 struct vnodeop_desc *a_desc; 396 struct vnode *a_vp; 397 struct vattr *a_vap; 398 struct ucred *a_cred; 399 struct proc *a_p; 400 } */ *ap = v; 401 struct vnode *vp = ap->a_vp; 402 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 403 struct vattr *vap = ap->a_vap; 404 struct ucred *cred = ap->a_cred; 405 struct proc *p = ap->a_p; 406 int error; 407 408 if (vap->va_size != VNOVAL) { 409 switch (ptyfs->ptyfs_type) { 410 case PTYFSroot: 411 return EISDIR; 412 case PTYFSpts: 413 case PTYFSptc: 414 break; 415 default: 416 return EINVAL; 417 } 418 } 419 420 if (vap->va_flags != VNOVAL) { 421 if (vp->v_mount->mnt_flag & MNT_RDONLY) 422 return EROFS; 423 if (cred->cr_uid != ptyfs->ptyfs_uid && 424 (error = suser(cred, &p->p_acflag)) != 0) 425 return error; 426 if (cred->cr_uid == 0) { 427 if ((ptyfs->ptyfs_flags & (SF_IMMUTABLE | SF_APPEND)) && 428 securelevel > 0) 429 return EPERM; 430 /* Snapshot flag cannot be set or cleared */ 431 if ((vap->va_flags & SF_SNAPSHOT) != 432 (ptyfs->ptyfs_flags & SF_SNAPSHOT)) 433 return EPERM; 434 ptyfs->ptyfs_flags = vap->va_flags; 435 } else { 436 if ((ptyfs->ptyfs_flags & (SF_IMMUTABLE | SF_APPEND)) || 437 (vap->va_flags & UF_SETTABLE) != vap->va_flags) 438 return EPERM; 439 if ((ptyfs->ptyfs_flags & SF_SETTABLE) != 440 (vap->va_flags & SF_SETTABLE)) 441 return EPERM; 442 ptyfs->ptyfs_flags &= SF_SETTABLE; 443 ptyfs->ptyfs_flags |= (vap->va_flags & UF_SETTABLE); 444 } 445 ptyfs->ptyfs_flag |= PTYFS_CHANGE; 446 if (vap->va_flags & (IMMUTABLE | APPEND)) 447 return 0; 448 } 449 if (ptyfs->ptyfs_flags & (IMMUTABLE | APPEND)) 450 return EPERM; 451 /* 452 * Go through the fields and update iff not VNOVAL. 453 */ 454 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { 455 if (vp->v_mount->mnt_flag & MNT_RDONLY) 456 return EROFS; 457 if (ptyfs->ptyfs_type == PTYFSroot) 458 return EPERM; 459 error = ptyfs_chown(vp, vap->va_uid, vap->va_gid, cred, p); 460 if (error) 461 return error; 462 } 463 464 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || 465 vap->va_birthtime.tv_sec != VNOVAL) { 466 if (vp->v_mount->mnt_flag & MNT_RDONLY) 467 return EROFS; 468 if ((ptyfs->ptyfs_flags & SF_SNAPSHOT) != 0) 469 return EPERM; 470 if (cred->cr_uid != ptyfs->ptyfs_uid && 471 (error = suser(cred, &p->p_acflag)) && 472 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || 473 (error = VOP_ACCESS(vp, VWRITE, cred, p)) != 0)) 474 return (error); 475 if (vap->va_atime.tv_sec != VNOVAL) 476 if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) 477 ptyfs->ptyfs_flag |= PTYFS_ACCESS; 478 if (vap->va_mtime.tv_sec != VNOVAL) 479 ptyfs->ptyfs_flag |= PTYFS_CHANGE | PTYFS_MODIFY; 480 if (vap->va_birthtime.tv_sec != VNOVAL) 481 ptyfs->ptyfs_birthtime = vap->va_birthtime; 482 ptyfs->ptyfs_flag |= PTYFS_CHANGE; 483 error = VOP_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 0); 484 if (error) 485 return error; 486 } 487 if (vap->va_mode != (mode_t)VNOVAL) { 488 if (vp->v_mount->mnt_flag & MNT_RDONLY) 489 return EROFS; 490 if (ptyfs->ptyfs_type == PTYFSroot) 491 return EPERM; 492 if ((ptyfs->ptyfs_flags & SF_SNAPSHOT) != 0 && 493 (vap->va_mode & 494 (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IXOTH|S_IWOTH))) 495 return EPERM; 496 error = ptyfs_chmod(vp, vap->va_mode, cred, p); 497 if (error) 498 return error; 499 } 500 VN_KNOTE(vp, NOTE_ATTRIB); 501 return 0; 502 } 503 504 /* 505 * Change the mode on a file. 506 * Inode must be locked before calling. 507 */ 508 static int 509 ptyfs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct proc *p) 510 { 511 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 512 int error; 513 514 if (cred->cr_uid != ptyfs->ptyfs_uid && 515 (error = suser(cred, &p->p_acflag)) != 0) 516 return error; 517 ptyfs->ptyfs_mode &= ~ALLPERMS; 518 ptyfs->ptyfs_mode |= (mode & ALLPERMS); 519 return 0; 520 } 521 522 /* 523 * Perform chown operation on inode ip; 524 * inode must be locked prior to call. 525 */ 526 static int 527 ptyfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, 528 struct proc *p) 529 { 530 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 531 int error; 532 533 if (uid == (uid_t)VNOVAL) 534 uid = ptyfs->ptyfs_uid; 535 if (gid == (gid_t)VNOVAL) 536 gid = ptyfs->ptyfs_gid; 537 /* 538 * If we don't own the file, are trying to change the owner 539 * of the file, or are not a member of the target group, 540 * the caller's credentials must imply super-user privilege 541 * or the call fails. 542 */ 543 if ((cred->cr_uid != ptyfs->ptyfs_uid || uid != ptyfs->ptyfs_uid || 544 (gid != ptyfs->ptyfs_gid && 545 !(cred->cr_gid == gid || groupmember((gid_t)gid, cred)))) && 546 ((error = suser(cred, &p->p_acflag)) != 0)) 547 return error; 548 549 ptyfs->ptyfs_gid = gid; 550 ptyfs->ptyfs_uid = uid; 551 return 0; 552 } 553 554 /* 555 * implement access checking. 556 * 557 * actually, the check for super-user is slightly 558 * broken since it will allow read access to write-only 559 * objects. this doesn't cause any particular trouble 560 * but does mean that the i/o entry points need to check 561 * that the operation really does make sense. 562 */ 563 int 564 ptyfs_access(void *v) 565 { 566 struct vop_access_args /* { 567 struct vnode *a_vp; 568 int a_mode; 569 struct ucred *a_cred; 570 struct proc *a_p; 571 } */ *ap = v; 572 struct vattr va; 573 int error; 574 575 if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred, ap->a_p)) != 0) 576 return error; 577 578 return vaccess(va.va_type, va.va_mode, 579 va.va_uid, va.va_gid, ap->a_mode, ap->a_cred); 580 } 581 582 /* 583 * lookup. this is incredibly complicated in the 584 * general case, however for most pseudo-filesystems 585 * very little needs to be done. 586 * 587 * Locking isn't hard here, just poorly documented. 588 * 589 * If we're looking up ".", just vref the parent & return it. 590 * 591 * If we're looking up "..", unlock the parent, and lock "..". If everything 592 * went ok, and we're on the last component and the caller requested the 593 * parent locked, try to re-lock the parent. We do this to prevent lock 594 * races. 595 * 596 * For anything else, get the needed node. Then unlock the parent if not 597 * the last component or not LOCKPARENT (i.e. if we wouldn't re-lock the 598 * parent in the .. case). 599 * 600 * We try to exit with the parent locked in error cases. 601 */ 602 int 603 ptyfs_lookup(void *v) 604 { 605 struct vop_lookup_args /* { 606 struct vnode * a_dvp; 607 struct vnode ** a_vpp; 608 struct componentname * a_cnp; 609 } */ *ap = v; 610 struct componentname *cnp = ap->a_cnp; 611 struct vnode **vpp = ap->a_vpp; 612 struct vnode *dvp = ap->a_dvp; 613 const char *pname = cnp->cn_nameptr; 614 struct ptyfsnode *ptyfs; 615 int pty, error, wantpunlock; 616 617 *vpp = NULL; 618 cnp->cn_flags &= ~PDIRUNLOCK; 619 620 if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) 621 return EROFS; 622 623 if (cnp->cn_namelen == 1 && *pname == '.') { 624 *vpp = dvp; 625 VREF(dvp); 626 return 0; 627 } 628 629 wantpunlock = ~cnp->cn_flags & (LOCKPARENT | ISLASTCN); 630 ptyfs = VTOPTYFS(dvp); 631 switch (ptyfs->ptyfs_type) { 632 case PTYFSroot: 633 /* 634 * Shouldn't get here with .. in the root node. 635 */ 636 if (cnp->cn_flags & ISDOTDOT) 637 return EIO; 638 639 pty = atoi(pname, cnp->cn_namelen); 640 641 if (pty < 0 || pty >= npty || pty_isfree(pty, 1)) 642 break; 643 644 error = ptyfs_allocvp(dvp->v_mount, vpp, PTYFSpts, pty, 645 curproc); 646 if (error == 0 && wantpunlock) { 647 VOP_UNLOCK(dvp, 0); 648 cnp->cn_flags |= PDIRUNLOCK; 649 } 650 return error; 651 652 default: 653 return ENOTDIR; 654 } 655 656 return cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS; 657 } 658 659 /* 660 * readdir returns directory entries from ptyfsnode (vp). 661 * 662 * the strategy here with ptyfs is to generate a single 663 * directory entry at a time (struct dirent) and then 664 * copy that out to userland using uiomove. a more efficent 665 * though more complex implementation, would try to minimize 666 * the number of calls to uiomove(). for ptyfs, this is 667 * hardly worth the added code complexity. 668 * 669 * this should just be done through read() 670 */ 671 int 672 ptyfs_readdir(void *v) 673 { 674 struct vop_readdir_args /* { 675 struct vnode *a_vp; 676 struct uio *a_uio; 677 struct ucred *a_cred; 678 int *a_eofflag; 679 off_t **a_cookies; 680 int *a_ncookies; 681 } */ *ap = v; 682 struct uio *uio = ap->a_uio; 683 struct dirent d; 684 struct ptyfsnode *ptyfs; 685 off_t i; 686 int error; 687 off_t *cookies = NULL; 688 int ncookies; 689 struct vnode *vp; 690 int nc = 0; 691 692 vp = ap->a_vp; 693 ptyfs = VTOPTYFS(vp); 694 695 if (uio->uio_resid < UIO_MX) 696 return EINVAL; 697 if (uio->uio_offset < 0) 698 return EINVAL; 699 700 error = 0; 701 i = uio->uio_offset; 702 (void)memset(&d, 0, sizeof(d)); 703 d.d_reclen = UIO_MX; 704 ncookies = uio->uio_resid / UIO_MX; 705 706 switch (ptyfs->ptyfs_type) { 707 case PTYFSroot: /* root */ 708 709 if (i >= npty) 710 return 0; 711 712 if (ap->a_ncookies) { 713 ncookies = min(ncookies, (npty + 2 - i)); 714 cookies = malloc(ncookies * sizeof (off_t), 715 M_TEMP, M_WAITOK); 716 *ap->a_cookies = cookies; 717 } 718 719 for (; i < 2; i++) { 720 switch (i) { 721 case 0: /* `.' */ 722 case 1: /* `..' */ 723 d.d_fileno = PTYFS_FILENO(0, PTYFSroot); 724 d.d_namlen = i + 1; 725 (void)memcpy(d.d_name, "..", d.d_namlen); 726 d.d_name[i + 1] = '\0'; 727 d.d_type = DT_DIR; 728 break; 729 } 730 if ((error = uiomove(&d, UIO_MX, uio)) != 0) 731 break; 732 if (cookies) 733 *cookies++ = i + 1; 734 nc++; 735 } 736 if (error) { 737 ncookies = nc; 738 break; 739 } 740 for (; uio->uio_resid >= UIO_MX && i < npty; i++) { 741 /* check for used ptys */ 742 if (pty_isfree(i - 2, 1)) 743 continue; 744 745 d.d_fileno = PTYFS_FILENO(i - 2, PTYFSpts); 746 d.d_namlen = snprintf(d.d_name, sizeof(d.d_name), 747 "%lld", (long long)(i - 2)); 748 d.d_type = DT_CHR; 749 if ((error = uiomove(&d, UIO_MX, uio)) != 0) 750 break; 751 if (cookies) 752 *cookies++ = i + 1; 753 nc++; 754 } 755 ncookies = nc; 756 break; 757 758 default: 759 error = ENOTDIR; 760 break; 761 } 762 763 if (ap->a_ncookies) { 764 if (error) { 765 if (cookies) 766 free(*ap->a_cookies, M_TEMP); 767 *ap->a_ncookies = 0; 768 *ap->a_cookies = NULL; 769 } else 770 *ap->a_ncookies = ncookies; 771 } 772 uio->uio_offset = i; 773 return error; 774 } 775 776 int 777 ptyfs_open(void *v) 778 { 779 struct vop_open_args /* { 780 struct vnode *a_vp; 781 int a_mode; 782 struct ucred *a_cred; 783 struct proc *a_p; 784 } */ *ap = v; 785 struct vnode *vp = ap->a_vp; 786 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 787 788 ptyfs->ptyfs_flag |= PTYFS_CHANGE|PTYFS_ACCESS; 789 switch (ptyfs->ptyfs_type) { 790 case PTYFSpts: 791 case PTYFSptc: 792 return spec_open(v); 793 case PTYFSroot: 794 return 0; 795 default: 796 return EINVAL; 797 } 798 } 799 800 int 801 ptyfs_close(void *v) 802 { 803 struct vop_close_args /* { 804 struct vnode *a_vp; 805 int a_fflag; 806 struct ucred *a_cred; 807 struct proc *a_p; 808 } */ *ap = v; 809 struct vnode *vp = ap->a_vp; 810 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 811 struct timespec ts; 812 813 simple_lock(&vp->v_interlock); 814 if (vp->v_usecount > 1) { 815 TIMEVAL_TO_TIMESPEC(&time, &ts); 816 ptyfs_time(ptyfs, &ts, &ts); 817 } 818 simple_unlock(&vp->v_interlock); 819 820 switch (ptyfs->ptyfs_type) { 821 case PTYFSpts: 822 case PTYFSptc: 823 return spec_close(v); 824 case PTYFSroot: 825 return 0; 826 default: 827 return EINVAL; 828 } 829 } 830 831 int 832 ptyfs_read(void *v) 833 { 834 struct vop_read_args /* { 835 struct vnode *a_vp; 836 struct uio *a_uio; 837 int a_ioflag; 838 struct ucred *a_cred; 839 } */ *ap = v; 840 struct vnode *vp = ap->a_vp; 841 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 842 int error; 843 844 ptyfs->ptyfs_flag |= PTYFS_ACCESS; 845 switch (ptyfs->ptyfs_type) { 846 case PTYFSpts: 847 VOP_UNLOCK(vp, 0); 848 error = (*pts_cdevsw.d_read)(vp->v_rdev, ap->a_uio, 849 ap->a_ioflag); 850 vn_lock(vp, LK_RETRY|LK_EXCLUSIVE); 851 return error; 852 case PTYFSptc: 853 VOP_UNLOCK(vp, 0); 854 error = (*ptc_cdevsw.d_read)(vp->v_rdev, ap->a_uio, 855 ap->a_ioflag); 856 vn_lock(vp, LK_RETRY|LK_EXCLUSIVE); 857 return error; 858 default: 859 return EOPNOTSUPP; 860 } 861 } 862 863 int 864 ptyfs_write(void *v) 865 { 866 struct vop_write_args /* { 867 struct vnode *a_vp; 868 struct uio *a_uio; 869 int a_ioflag; 870 struct ucred *a_cred; 871 } */ *ap = v; 872 int error; 873 struct vnode *vp = ap->a_vp; 874 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 875 876 ptyfs->ptyfs_flag |= PTYFS_MODIFY; 877 switch (ptyfs->ptyfs_type) { 878 case PTYFSpts: 879 VOP_UNLOCK(vp, 0); 880 error = (*pts_cdevsw.d_write)(vp->v_rdev, ap->a_uio, 881 ap->a_ioflag); 882 vn_lock(vp, LK_RETRY|LK_EXCLUSIVE); 883 return error; 884 case PTYFSptc: 885 VOP_UNLOCK(vp, 0); 886 error = (*ptc_cdevsw.d_write)(vp->v_rdev, ap->a_uio, 887 ap->a_ioflag); 888 vn_lock(vp, LK_RETRY|LK_EXCLUSIVE); 889 return error; 890 default: 891 return EOPNOTSUPP; 892 } 893 } 894 895 int 896 ptyfs_ioctl(void *v) 897 { 898 struct vop_ioctl_args /* { 899 struct vnode *a_vp; 900 u_long a_command; 901 void *a_data; 902 int a_fflag; 903 struct ucred *a_cred; 904 struct proc *a_p; 905 } */ *ap = v; 906 struct vnode *vp = ap->a_vp; 907 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 908 909 switch (ptyfs->ptyfs_type) { 910 case PTYFSpts: 911 return (*pts_cdevsw.d_ioctl)(vp->v_rdev, ap->a_command, 912 ap->a_data, ap->a_fflag, ap->a_p); 913 case PTYFSptc: 914 return (*ptc_cdevsw.d_ioctl)(vp->v_rdev, ap->a_command, 915 ap->a_data, ap->a_fflag, ap->a_p); 916 default: 917 return EOPNOTSUPP; 918 } 919 } 920 921 int 922 ptyfs_poll(void *v) 923 { 924 struct vop_poll_args /* { 925 struct vnode *a_vp; 926 int a_events; 927 struct proc *a_p; 928 } */ *ap = v; 929 struct vnode *vp = ap->a_vp; 930 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 931 932 switch (ptyfs->ptyfs_type) { 933 case PTYFSpts: 934 return (*pts_cdevsw.d_poll)(vp->v_rdev, ap->a_events, ap->a_p); 935 case PTYFSptc: 936 return (*ptc_cdevsw.d_poll)(vp->v_rdev, ap->a_events, ap->a_p); 937 default: 938 return genfs_poll(v); 939 } 940 } 941 942 int 943 ptyfs_kqfilter(void *v) 944 { 945 struct vop_kqfilter_args /* { 946 struct vnode *a_vp; 947 struct knote *a_kn; 948 } */ *ap = v; 949 struct vnode *vp = ap->a_vp; 950 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 951 952 switch (ptyfs->ptyfs_type) { 953 case PTYFSpts: 954 return (*pts_cdevsw.d_kqfilter)(vp->v_rdev, ap->a_kn); 955 case PTYFSptc: 956 return (*ptc_cdevsw.d_kqfilter)(vp->v_rdev, ap->a_kn); 957 default: 958 return genfs_kqfilter(v); 959 } 960 } 961 962 int 963 ptyfs_update(v) 964 void *v; 965 { 966 struct vop_update_args /* { 967 struct vnode *a_vp; 968 struct timespec *a_access; 969 struct timespec *a_modify; 970 int a_flags; 971 } */ *ap = v; 972 struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp); 973 struct timespec ts; 974 975 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 976 return 0; 977 978 TIMEVAL_TO_TIMESPEC(&time, &ts); 979 if (ap->a_access == NULL) 980 ap->a_access = &ts; 981 if (ap->a_modify == NULL) 982 ap->a_modify = &ts; 983 ptyfs_time(ptyfs, ap->a_access, ap->a_modify); 984 return 0; 985 } 986 987 static void 988 ptyfs_time(struct ptyfsnode *ptyfs, struct timespec *atime, 989 struct timespec *mtime) 990 { 991 if (ptyfs->ptyfs_flag & PTYFS_MODIFY) { 992 ptyfs->ptyfs_mtime = *mtime; 993 ptyfs->ptyfs_atime = *atime; 994 } else if (ptyfs->ptyfs_flag & PTYFS_ACCESS) 995 ptyfs->ptyfs_atime = *atime; 996 if (ptyfs->ptyfs_flag & PTYFS_CHANGE) 997 ptyfs->ptyfs_ctime = *atime; 998 ptyfs->ptyfs_flag = 0; 999 } 1000 1001 /* 1002 * convert decimal ascii to int 1003 */ 1004 static int 1005 atoi(const char *b, size_t len) 1006 { 1007 int p = 0; 1008 1009 while (len--) { 1010 char c = *b++; 1011 if (c < '0' || c > '9') 1012 return -1; 1013 p = 10 * p + (c - '0'); 1014 } 1015 1016 return p; 1017 } 1018