1 /* $NetBSD: ptyfs_subr.c,v 1.2 2004/11/11 19:19:59 jdolecek Exp $ */ 2 3 /* 4 * Copyright (c) 1993 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 * @(#)ptyfs_subr.c 8.6 (Berkeley) 5/14/95 35 */ 36 37 /* 38 * Copyright (c) 1994 Christopher G. Demetriou. All rights reserved. 39 * Copyright (c) 1993 Jan-Simon Pendry 40 * 41 * This code is derived from software contributed to Berkeley by 42 * Jan-Simon Pendry. 43 * 44 * Redistribution and use in source and binary forms, with or without 45 * modification, are permitted provided that the following conditions 46 * are met: 47 * 1. Redistributions of source code must retain the above copyright 48 * notice, this list of conditions and the following disclaimer. 49 * 2. Redistributions in binary form must reproduce the above copyright 50 * notice, this list of conditions and the following disclaimer in the 51 * documentation and/or other materials provided with the distribution. 52 * 3. All advertising materials mentioning features or use of this software 53 * must display the following acknowledgement: 54 * This product includes software developed by the University of 55 * California, Berkeley and its contributors. 56 * 4. Neither the name of the University nor the names of its contributors 57 * may be used to endorse or promote products derived from this software 58 * without specific prior written permission. 59 * 60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 70 * SUCH DAMAGE. 71 * 72 * @(#)procfs_subr.c 8.6 (Berkeley) 5/14/95 73 */ 74 75 #include <sys/cdefs.h> 76 __KERNEL_RCSID(0, "$NetBSD: ptyfs_subr.c,v 1.2 2004/11/11 19:19:59 jdolecek Exp $"); 77 78 #include <sys/param.h> 79 #include <sys/systm.h> 80 #include <sys/time.h> 81 #include <sys/kernel.h> 82 #include <sys/vnode.h> 83 #include <sys/malloc.h> 84 #include <sys/stat.h> 85 #include <sys/file.h> 86 #include <sys/namei.h> 87 #include <sys/filedesc.h> 88 #include <sys/select.h> 89 #include <sys/tty.h> 90 #include <sys/pty.h> 91 92 #include <fs/ptyfs/ptyfs.h> 93 #include <miscfs/specfs/specdev.h> 94 95 static struct lock ptyfs_hashlock; 96 97 static LIST_HEAD(ptyfs_hashhead, ptyfsnode) *ptyfs_used_tbl, *ptyfs_free_tbl; 98 static u_long ptyfs_used_mask, ptyfs_free_mask; /* size of hash table - 1 */ 99 static struct simplelock ptyfs_used_slock, ptyfs_free_slock; 100 101 static void ptyfs_getinfo(struct ptyfsnode *, struct proc *); 102 103 static void ptyfs_hashins(struct ptyfsnode *); 104 static void ptyfs_hashrem(struct ptyfsnode *); 105 106 static struct vnode *ptyfs_used_get(ptyfstype, int, struct mount *); 107 static struct ptyfsnode *ptyfs_free_get(ptyfstype, int, struct proc *); 108 109 static void ptyfs_rehash(struct simplelock *, struct ptyfs_hashhead **, 110 u_long *); 111 112 #define PTYHASH(type, pty, mask) (PTYFS_FILENO(type, pty) % (mask + 1)) 113 114 115 static void 116 ptyfs_getinfo(struct ptyfsnode *ptyfs, struct proc *p) 117 { 118 extern struct ptm_pty *ptyfs_save_ptm, ptm_ptyfspty; 119 120 if (ptyfs->ptyfs_type == PTYFSroot) { 121 ptyfs->ptyfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP| 122 S_IROTH|S_IXOTH; 123 goto out; 124 } else 125 ptyfs->ptyfs_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP| 126 S_IROTH|S_IWOTH; 127 128 if (ptyfs_save_ptm != NULL && ptyfs_save_ptm != &ptm_ptyfspty) { 129 int error; 130 struct nameidata nd; 131 char ttyname[64]; 132 struct ucred *cred; 133 struct vattr va; 134 /* 135 * We support traditional ptys, so we copy the info 136 * from the inode 137 */ 138 if ((error = (*ptyfs_save_ptm->makename)( 139 ptyfs_save_ptm, ttyname, sizeof(ttyname), 140 ptyfs->ptyfs_pty, ptyfs->ptyfs_type == PTYFSpts ? 't' 141 : 'p')) != 0) 142 goto out; 143 NDINIT(&nd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE, ttyname, 144 p); 145 if ((error = namei(&nd)) != 0) 146 goto out; 147 cred = crget(); 148 error = VOP_GETATTR(nd.ni_vp, &va, cred, p); 149 crfree(cred); 150 VOP_UNLOCK(nd.ni_vp, 0); 151 vrele(nd.ni_vp); 152 if (error) 153 goto out; 154 ptyfs->ptyfs_uid = va.va_uid; 155 ptyfs->ptyfs_gid = va.va_gid; 156 ptyfs->ptyfs_mode = va.va_mode; 157 ptyfs->ptyfs_flags = va.va_flags; 158 ptyfs->ptyfs_birthtime = va.va_birthtime; 159 ptyfs->ptyfs_ctime = va.va_ctime; 160 ptyfs->ptyfs_mtime = va.va_mtime; 161 ptyfs->ptyfs_atime = va.va_atime; 162 return; 163 } 164 out: 165 ptyfs->ptyfs_uid = ptyfs->ptyfs_gid = 0; 166 TIMEVAL_TO_TIMESPEC(&time, &ptyfs->ptyfs_ctime); 167 ptyfs->ptyfs_birthtime = ptyfs->ptyfs_mtime = 168 ptyfs->ptyfs_atime = ptyfs->ptyfs_ctime; 169 ptyfs->ptyfs_flags = 0; 170 } 171 172 173 /* 174 * allocate a ptyfsnode/vnode pair. the vnode is 175 * referenced, and locked. 176 * 177 * the pid, ptyfs_type, and mount point uniquely 178 * identify a ptyfsnode. the mount point is needed 179 * because someone might mount this filesystem 180 * twice. 181 * 182 * all ptyfsnodes are maintained on a singly-linked 183 * list. new nodes are only allocated when they cannot 184 * be found on this list. entries on the list are 185 * removed when the vfs reclaim entry is called. 186 * 187 * a single lock is kept for the entire list. this is 188 * needed because the getnewvnode() function can block 189 * waiting for a vnode to become free, in which case there 190 * may be more than one ptyess trying to get the same 191 * vnode. this lock is only taken if we are going to 192 * call getnewvnode, since the kernel itself is single-threaded. 193 * 194 * if an entry is found on the list, then call vget() to 195 * take a reference. this is done because there may be 196 * zero references to it and so it needs to removed from 197 * the vnode free list. 198 */ 199 int 200 ptyfs_allocvp(struct mount *mp, struct vnode **vpp, ptyfstype type, int pty, 201 struct proc *p) 202 { 203 struct ptyfsnode *ptyfs; 204 struct vnode *vp, *nvp; 205 int error; 206 207 do { 208 if ((*vpp = ptyfs_used_get(type, pty, mp)) != NULL) 209 return 0; 210 } while (lockmgr(&ptyfs_hashlock, LK_EXCLUSIVE|LK_SLEEPFAIL, 0)); 211 212 if ((error = getnewvnode(VT_PTYFS, mp, ptyfs_vnodeop_p, &vp)) != 0) { 213 *vpp = NULL; 214 lockmgr(&ptyfs_hashlock, LK_RELEASE, NULL); 215 return error; 216 } 217 218 vp->v_data = ptyfs = ptyfs_free_get(type, pty, p); 219 ptyfs->ptyfs_vnode = vp; 220 221 switch (type) { 222 case PTYFSroot: /* /pts = dr-xr-xr-x */ 223 vp->v_type = VDIR; 224 vp->v_flag = VROOT; 225 break; 226 227 case PTYFSpts: /* /pts/N = cxxxxxxxxx */ 228 case PTYFSptc: /* controlling side = cxxxxxxxxx */ 229 vp->v_type = VCHR; 230 if ((nvp = checkalias(vp, PTYFS_MAKEDEV(ptyfs), mp)) != NULL) { 231 /* 232 * Discard unneeded vnode, but save its inode. 233 */ 234 nvp->v_data = vp->v_data; 235 vp->v_data = NULL; 236 /* XXX spec_vnodeops has no locking, do it explicitly */ 237 VOP_UNLOCK(vp, 0); 238 vp->v_op = spec_vnodeop_p; 239 vp->v_flag &= ~VLOCKSWORK; 240 vrele(vp); 241 vgone(vp); 242 lockmgr(&nvp->v_lock, LK_EXCLUSIVE, &nvp->v_interlock); 243 /* 244 * Reinitialize aliased inode. 245 */ 246 vp = nvp; 247 ptyfs->ptyfs_vnode = vp; 248 } 249 break; 250 default: 251 panic("ptyfs_allocvp"); 252 } 253 254 ptyfs_hashins(ptyfs); 255 uvm_vnp_setsize(vp, 0); 256 lockmgr(&ptyfs_hashlock, LK_RELEASE, NULL); 257 258 *vpp = vp; 259 return 0; 260 } 261 262 int 263 ptyfs_freevp(struct vnode *vp) 264 { 265 struct ptyfsnode *ptyfs = VTOPTYFS(vp); 266 267 ptyfs_hashrem(ptyfs); 268 vp->v_data = NULL; 269 return 0; 270 } 271 272 /* 273 * Initialize ptyfsnode hash table. 274 */ 275 void 276 ptyfs_hashinit(void) 277 { 278 lockinit(&ptyfs_hashlock, PINOD, "ptyfs_hashlock", 0, 0); 279 ptyfs_used_tbl = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, 280 M_WAITOK, &ptyfs_used_mask); 281 ptyfs_free_tbl = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, 282 M_WAITOK, &ptyfs_free_mask); 283 simple_lock_init(&ptyfs_used_slock); 284 simple_lock_init(&ptyfs_free_slock); 285 } 286 287 void 288 ptyfs_hashreinit(void) 289 { 290 ptyfs_rehash(&ptyfs_used_slock, &ptyfs_used_tbl, &ptyfs_used_mask); 291 ptyfs_rehash(&ptyfs_free_slock, &ptyfs_free_tbl, &ptyfs_free_mask); 292 } 293 294 static void 295 ptyfs_rehash(struct simplelock *hlock, struct ptyfs_hashhead **hhead, 296 u_long *hmask) 297 { 298 struct ptyfsnode *pp; 299 struct ptyfs_hashhead *oldhash, *hash; 300 u_long i, oldmask, mask, val; 301 302 hash = hashinit(desiredvnodes / 4, HASH_LIST, M_UFSMNT, M_WAITOK, 303 &mask); 304 305 simple_lock(hlock); 306 oldhash = *hhead; 307 oldmask = *hmask; 308 *hhead = hash; 309 *hmask = mask; 310 for (i = 0; i <= oldmask; i++) { 311 while ((pp = LIST_FIRST(&oldhash[i])) != NULL) { 312 LIST_REMOVE(pp, ptyfs_hash); 313 val = PTYHASH(pp->ptyfs_type, pp->ptyfs_pty, 314 ptyfs_used_mask); 315 LIST_INSERT_HEAD(&hash[val], pp, ptyfs_hash); 316 } 317 } 318 simple_unlock(hlock); 319 hashdone(oldhash, M_UFSMNT); 320 } 321 322 /* 323 * Free ptyfsnode hash table. 324 */ 325 void 326 ptyfs_hashdone(void) 327 { 328 hashdone(ptyfs_used_tbl, M_UFSMNT); 329 hashdone(ptyfs_free_tbl, M_UFSMNT); 330 } 331 332 /* 333 * Get a ptyfsnode from the free table, or allocate one. 334 * Removes the node from the free table. 335 */ 336 struct ptyfsnode * 337 ptyfs_free_get(ptyfstype type, int pty, struct proc *p) 338 { 339 struct ptyfs_hashhead *ppp; 340 struct ptyfsnode *pp; 341 342 simple_lock(&ptyfs_free_slock); 343 ppp = &ptyfs_free_tbl[PTYHASH(type, pty, ptyfs_free_mask)]; 344 LIST_FOREACH(pp, ppp, ptyfs_hash) { 345 if (pty == pp->ptyfs_pty && pp->ptyfs_type == type) { 346 LIST_REMOVE(pp, ptyfs_hash); 347 simple_unlock(&ptyfs_free_slock); 348 return pp; 349 } 350 } 351 simple_unlock(&ptyfs_free_slock); 352 353 MALLOC(pp, void *, sizeof(struct ptyfsnode), M_TEMP, M_WAITOK); 354 pp->ptyfs_pty = pty; 355 pp->ptyfs_type = type; 356 pp->ptyfs_fileno = PTYFS_FILENO(pty, type); 357 ptyfs_getinfo(pp, p); 358 return pp; 359 } 360 361 struct vnode * 362 ptyfs_used_get(ptyfstype type, int pty, struct mount *mp) 363 { 364 struct ptyfs_hashhead *ppp; 365 struct ptyfsnode *pp; 366 struct vnode *vp; 367 368 loop: 369 simple_lock(&ptyfs_used_slock); 370 ppp = &ptyfs_used_tbl[PTYHASH(type, pty, ptyfs_used_mask)]; 371 LIST_FOREACH(pp, ppp, ptyfs_hash) { 372 vp = PTYFSTOV(pp); 373 if (pty == pp->ptyfs_pty && pp->ptyfs_type == type && 374 vp->v_mount == mp) { 375 simple_lock(&vp->v_interlock); 376 simple_unlock(&ptyfs_used_slock); 377 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) 378 goto loop; 379 return vp; 380 } 381 } 382 simple_unlock(&ptyfs_used_slock); 383 return NULL; 384 } 385 386 /* 387 * Insert the ptyfsnode into the used table and lock it. 388 */ 389 static void 390 ptyfs_hashins(struct ptyfsnode *pp) 391 { 392 struct ptyfs_hashhead *ppp; 393 394 /* lock the ptyfsnode, then put it on the appropriate hash list */ 395 lockmgr(&pp->ptyfs_vnode->v_lock, LK_EXCLUSIVE, NULL); 396 397 simple_lock(&ptyfs_used_slock); 398 ppp = &ptyfs_used_tbl[PTYHASH(pp->ptyfs_type, pp->ptyfs_pty, 399 ptyfs_used_mask)]; 400 LIST_INSERT_HEAD(ppp, pp, ptyfs_hash); 401 simple_unlock(&ptyfs_used_slock); 402 } 403 404 /* 405 * Remove the ptyfsnode from the used table, and add it to the free table 406 */ 407 static void 408 ptyfs_hashrem(struct ptyfsnode *pp) 409 { 410 struct ptyfs_hashhead *ppp; 411 412 simple_lock(&ptyfs_used_slock); 413 LIST_REMOVE(pp, ptyfs_hash); 414 simple_unlock(&ptyfs_used_slock); 415 416 simple_lock(&ptyfs_free_slock); 417 ppp = &ptyfs_free_tbl[PTYHASH(pp->ptyfs_type, pp->ptyfs_pty, 418 ptyfs_free_mask)]; 419 LIST_INSERT_HEAD(ppp, pp, ptyfs_hash); 420 simple_unlock(&ptyfs_free_slock); 421 } 422