10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 53898Srsb * Common Development and Distribution License (the "License"). 63898Srsb * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*12633Sjohn.levon@sun.com * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. 230Sstevel@tonic-gate */ 240Sstevel@tonic-gate 250Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 260Sstevel@tonic-gate /* All rights reserved. */ 270Sstevel@tonic-gate 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include <sys/types.h> 300Sstevel@tonic-gate #include <sys/param.h> 310Sstevel@tonic-gate #include <sys/cmn_err.h> 320Sstevel@tonic-gate #include <sys/debug.h> 330Sstevel@tonic-gate #include <sys/dirent.h> 340Sstevel@tonic-gate #include <sys/errno.h> 350Sstevel@tonic-gate #include <sys/file.h> 360Sstevel@tonic-gate #include <sys/inline.h> 370Sstevel@tonic-gate #include <sys/kmem.h> 380Sstevel@tonic-gate #include <sys/pathname.h> 390Sstevel@tonic-gate #include <sys/resource.h> 400Sstevel@tonic-gate #include <sys/statvfs.h> 410Sstevel@tonic-gate #include <sys/mount.h> 420Sstevel@tonic-gate #include <sys/sysmacros.h> 430Sstevel@tonic-gate #include <sys/systm.h> 440Sstevel@tonic-gate #include <sys/uio.h> 450Sstevel@tonic-gate #include <sys/vfs.h> 463898Srsb #include <sys/vfs_opreg.h> 470Sstevel@tonic-gate #include <sys/vnode.h> 480Sstevel@tonic-gate #include <sys/cred.h> 490Sstevel@tonic-gate #include <sys/mntent.h> 500Sstevel@tonic-gate #include <sys/mount.h> 510Sstevel@tonic-gate #include <sys/user.h> 520Sstevel@tonic-gate #include <sys/t_lock.h> 530Sstevel@tonic-gate #include <sys/modctl.h> 540Sstevel@tonic-gate #include <sys/policy.h> 550Sstevel@tonic-gate #include <fs/fs_subr.h> 560Sstevel@tonic-gate #include <sys/atomic.h> 570Sstevel@tonic-gate #include <sys/mkdev.h> 580Sstevel@tonic-gate 590Sstevel@tonic-gate #define round(r) (((r)+sizeof (int)-1)&(~(sizeof (int)-1))) 600Sstevel@tonic-gate #define fdtoi(n) ((n)+100) 610Sstevel@tonic-gate 620Sstevel@tonic-gate #define FDDIRSIZE 14 630Sstevel@tonic-gate struct fddirect { 640Sstevel@tonic-gate short d_ino; 650Sstevel@tonic-gate char d_name[FDDIRSIZE]; 660Sstevel@tonic-gate }; 670Sstevel@tonic-gate 680Sstevel@tonic-gate #define FDROOTINO 2 690Sstevel@tonic-gate #define FDSDSIZE sizeof (struct fddirect) 700Sstevel@tonic-gate #define FDNSIZE 10 710Sstevel@tonic-gate 720Sstevel@tonic-gate static int fdfstype = 0; 730Sstevel@tonic-gate static major_t fdfsmaj; 740Sstevel@tonic-gate static minor_t fdfsmin; 750Sstevel@tonic-gate static major_t fdrmaj; 760Sstevel@tonic-gate static kmutex_t fd_minor_lock; 770Sstevel@tonic-gate 780Sstevel@tonic-gate static int fdget(vnode_t *, char *, vnode_t **); 790Sstevel@tonic-gate 800Sstevel@tonic-gate /* ARGSUSED */ 810Sstevel@tonic-gate static int 825331Samw fdopen(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct) 830Sstevel@tonic-gate { 840Sstevel@tonic-gate if ((*vpp)->v_type != VDIR) { 850Sstevel@tonic-gate mutex_enter(&(*vpp)->v_lock); 860Sstevel@tonic-gate (*vpp)->v_flag |= VDUP; 870Sstevel@tonic-gate mutex_exit(&(*vpp)->v_lock); 880Sstevel@tonic-gate } 890Sstevel@tonic-gate return (0); 900Sstevel@tonic-gate } 910Sstevel@tonic-gate 920Sstevel@tonic-gate /* ARGSUSED */ 930Sstevel@tonic-gate static int 945331Samw fdclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, 955331Samw caller_context_t *ct) 960Sstevel@tonic-gate { 970Sstevel@tonic-gate return (0); 980Sstevel@tonic-gate } 990Sstevel@tonic-gate 1000Sstevel@tonic-gate /* ARGSUSED */ 1010Sstevel@tonic-gate static int 1020Sstevel@tonic-gate fdread(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct) 1030Sstevel@tonic-gate { 1040Sstevel@tonic-gate static struct fddirect dotbuf[] = { 1050Sstevel@tonic-gate { FDROOTINO, "." }, 1060Sstevel@tonic-gate { FDROOTINO, ".." } 1070Sstevel@tonic-gate }; 1080Sstevel@tonic-gate struct fddirect dirbuf; 1090Sstevel@tonic-gate int i, n; 1100Sstevel@tonic-gate int minfd, maxfd, modoff, error = 0; 1110Sstevel@tonic-gate int nentries; 1120Sstevel@tonic-gate rctl_qty_t fdno_ctl; 1130Sstevel@tonic-gate int endoff; 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate if (vp->v_type != VDIR) 1160Sstevel@tonic-gate return (ENOSYS); 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate mutex_enter(&curproc->p_lock); 1190Sstevel@tonic-gate fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE], 1200Sstevel@tonic-gate curproc->p_rctls, curproc); 1210Sstevel@tonic-gate nentries = MIN(P_FINFO(curproc)->fi_nfiles, (int)fdno_ctl); 1220Sstevel@tonic-gate mutex_exit(&curproc->p_lock); 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate endoff = (nentries + 2) * FDSDSIZE; 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate /* 1270Sstevel@tonic-gate * Fake up ".", "..", and the /dev/fd directory entries. 1280Sstevel@tonic-gate */ 1290Sstevel@tonic-gate if (uiop->uio_loffset < (offset_t)0 || 1300Sstevel@tonic-gate uiop->uio_loffset >= (offset_t)endoff || 1310Sstevel@tonic-gate uiop->uio_resid <= 0) 1320Sstevel@tonic-gate return (0); 1330Sstevel@tonic-gate ASSERT(uiop->uio_loffset <= MAXOFF_T); 1340Sstevel@tonic-gate if (uiop->uio_offset < 2*FDSDSIZE) { 1350Sstevel@tonic-gate error = uiomove((caddr_t)dotbuf + uiop->uio_offset, 1360Sstevel@tonic-gate MIN(uiop->uio_resid, 2*FDSDSIZE - uiop->uio_offset), 1370Sstevel@tonic-gate UIO_READ, uiop); 1380Sstevel@tonic-gate if (uiop->uio_resid <= 0 || error) 1390Sstevel@tonic-gate return (error); 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate minfd = (uiop->uio_offset - 2*FDSDSIZE)/FDSDSIZE; 1420Sstevel@tonic-gate maxfd = (uiop->uio_offset + uiop->uio_resid - 1)/FDSDSIZE; 1430Sstevel@tonic-gate modoff = uiop->uio_offset % FDSDSIZE; 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate for (i = 0; i < FDDIRSIZE; i++) 1460Sstevel@tonic-gate dirbuf.d_name[i] = '\0'; 1470Sstevel@tonic-gate for (i = minfd; i < MIN(maxfd, nentries); i++) { 1480Sstevel@tonic-gate n = i; 1490Sstevel@tonic-gate dirbuf.d_ino = fdtoi(n); 1500Sstevel@tonic-gate numtos((ulong_t)n, dirbuf.d_name); 1510Sstevel@tonic-gate error = uiomove((caddr_t)&dirbuf + modoff, 1520Sstevel@tonic-gate MIN(uiop->uio_resid, FDSDSIZE - modoff), 1530Sstevel@tonic-gate UIO_READ, uiop); 1540Sstevel@tonic-gate if (uiop->uio_resid <= 0 || error) 1550Sstevel@tonic-gate return (error); 1560Sstevel@tonic-gate modoff = 0; 1570Sstevel@tonic-gate } 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate return (error); 1600Sstevel@tonic-gate } 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate /* ARGSUSED */ 1630Sstevel@tonic-gate static int 1645331Samw fdgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, 1655331Samw caller_context_t *ct) 1660Sstevel@tonic-gate { 1670Sstevel@tonic-gate vfs_t *vfsp = vp->v_vfsp; 1680Sstevel@tonic-gate timestruc_t now; 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate if (vp->v_type == VDIR) { 1710Sstevel@tonic-gate vap->va_nlink = 2; 1720Sstevel@tonic-gate vap->va_size = (u_offset_t) 1730Sstevel@tonic-gate ((P_FINFO(curproc)->fi_nfiles + 2) * FDSDSIZE); 1740Sstevel@tonic-gate vap->va_mode = 0555; 1750Sstevel@tonic-gate vap->va_nodeid = (ino64_t)FDROOTINO; 1760Sstevel@tonic-gate } else { 1770Sstevel@tonic-gate vap->va_nlink = 1; 1780Sstevel@tonic-gate vap->va_size = (u_offset_t)0; 1790Sstevel@tonic-gate vap->va_mode = 0666; 1800Sstevel@tonic-gate vap->va_nodeid = (ino64_t)fdtoi(getminor(vp->v_rdev)); 1810Sstevel@tonic-gate } 1820Sstevel@tonic-gate vap->va_type = vp->v_type; 1830Sstevel@tonic-gate vap->va_rdev = vp->v_rdev; 1840Sstevel@tonic-gate vap->va_blksize = vfsp->vfs_bsize; 1850Sstevel@tonic-gate vap->va_nblocks = (fsblkcnt64_t)0; 1860Sstevel@tonic-gate gethrestime(&now); 1870Sstevel@tonic-gate vap->va_atime = vap->va_mtime = vap->va_ctime = now; 1880Sstevel@tonic-gate vap->va_uid = 0; 1890Sstevel@tonic-gate vap->va_gid = 0; 1900Sstevel@tonic-gate vap->va_fsid = vfsp->vfs_dev; 1910Sstevel@tonic-gate vap->va_seq = 0; 1920Sstevel@tonic-gate return (0); 1930Sstevel@tonic-gate } 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate /* ARGSUSED */ 1960Sstevel@tonic-gate static int 1975331Samw fdaccess(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) 1980Sstevel@tonic-gate { 1990Sstevel@tonic-gate return (0); 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate /* ARGSUSED */ 2030Sstevel@tonic-gate static int 2040Sstevel@tonic-gate fdlookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pnp, 2055331Samw int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, 2065331Samw int *direntflags, pathname_t *realpnp) 2070Sstevel@tonic-gate { 2080Sstevel@tonic-gate if (comp[0] == 0 || strcmp(comp, ".") == 0 || strcmp(comp, "..") == 0) { 2090Sstevel@tonic-gate VN_HOLD(dp); 2100Sstevel@tonic-gate *vpp = dp; 2110Sstevel@tonic-gate return (0); 2120Sstevel@tonic-gate } 2130Sstevel@tonic-gate return (fdget(dp, comp, vpp)); 2140Sstevel@tonic-gate } 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate /* ARGSUSED */ 2170Sstevel@tonic-gate static int 2180Sstevel@tonic-gate fdcreate(vnode_t *dvp, char *comp, vattr_t *vap, enum vcexcl excl, 2195331Samw int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct, 2205331Samw vsecattr_t *vsecp) 2210Sstevel@tonic-gate { 2220Sstevel@tonic-gate return (fdget(dvp, comp, vpp)); 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate /* ARGSUSED */ 2260Sstevel@tonic-gate static int 2275331Samw fdreaddir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, caller_context_t *ct, 2285331Samw int flags) 2290Sstevel@tonic-gate { 2300Sstevel@tonic-gate /* bp holds one dirent structure */ 2310Sstevel@tonic-gate u_offset_t bp[DIRENT64_RECLEN(FDNSIZE) / sizeof (u_offset_t)]; 2320Sstevel@tonic-gate struct dirent64 *dirent = (struct dirent64 *)bp; 2330Sstevel@tonic-gate int reclen, nentries; 2340Sstevel@tonic-gate rctl_qty_t fdno_ctl; 2350Sstevel@tonic-gate int n; 2360Sstevel@tonic-gate int oresid; 2370Sstevel@tonic-gate off_t off; 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate if (uiop->uio_offset < 0 || uiop->uio_resid <= 0 || 2400Sstevel@tonic-gate (uiop->uio_offset % FDSDSIZE) != 0) 2410Sstevel@tonic-gate return (ENOENT); 2420Sstevel@tonic-gate 2430Sstevel@tonic-gate ASSERT(uiop->uio_loffset <= MAXOFF_T); 2440Sstevel@tonic-gate oresid = uiop->uio_resid; 2450Sstevel@tonic-gate bzero(bp, sizeof (bp)); 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate mutex_enter(&curproc->p_lock); 2480Sstevel@tonic-gate fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE], 2490Sstevel@tonic-gate curproc->p_rctls, curproc); 2500Sstevel@tonic-gate nentries = MIN(P_FINFO(curproc)->fi_nfiles, (int)fdno_ctl); 2510Sstevel@tonic-gate mutex_exit(&curproc->p_lock); 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate while (uiop->uio_resid > 0) { 2540Sstevel@tonic-gate if ((off = uiop->uio_offset) == 0) { /* "." */ 2550Sstevel@tonic-gate dirent->d_ino = (ino64_t)FDROOTINO; 2560Sstevel@tonic-gate dirent->d_name[0] = '.'; 2570Sstevel@tonic-gate dirent->d_name[1] = '\0'; 2580Sstevel@tonic-gate reclen = DIRENT64_RECLEN(1); 2590Sstevel@tonic-gate } else if (off == FDSDSIZE) { /* ".." */ 2600Sstevel@tonic-gate dirent->d_ino = (ino64_t)FDROOTINO; 2610Sstevel@tonic-gate dirent->d_name[0] = '.'; 2620Sstevel@tonic-gate dirent->d_name[1] = '.'; 2630Sstevel@tonic-gate dirent->d_name[2] = '\0'; 2640Sstevel@tonic-gate reclen = DIRENT64_RECLEN(2); 2650Sstevel@tonic-gate } else { 2660Sstevel@tonic-gate /* 2670Sstevel@tonic-gate * Return entries corresponding to the allowable 2680Sstevel@tonic-gate * number of file descriptors for this process. 2690Sstevel@tonic-gate */ 2700Sstevel@tonic-gate if ((n = (off-2*FDSDSIZE)/FDSDSIZE) >= nentries) 2710Sstevel@tonic-gate break; 2720Sstevel@tonic-gate dirent->d_ino = (ino64_t)fdtoi(n); 2730Sstevel@tonic-gate numtos((ulong_t)n, dirent->d_name); 2740Sstevel@tonic-gate reclen = DIRENT64_RECLEN(strlen(dirent->d_name)); 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate dirent->d_off = (offset_t)(uiop->uio_offset + FDSDSIZE); 2770Sstevel@tonic-gate dirent->d_reclen = (ushort_t)reclen; 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate if (reclen > uiop->uio_resid) { 2800Sstevel@tonic-gate /* 2810Sstevel@tonic-gate * Error if no entries have been returned yet. 2820Sstevel@tonic-gate */ 2830Sstevel@tonic-gate if (uiop->uio_resid == oresid) 2840Sstevel@tonic-gate return (EINVAL); 2850Sstevel@tonic-gate break; 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate /* 2880Sstevel@tonic-gate * uiomove() updates both resid and offset by the same 2890Sstevel@tonic-gate * amount. But we want offset to change in increments 2900Sstevel@tonic-gate * of FDSDSIZE, which is different from the number of bytes 2910Sstevel@tonic-gate * being returned to the user. So we set uio_offset 2920Sstevel@tonic-gate * separately, ignoring what uiomove() does. 2930Sstevel@tonic-gate */ 2940Sstevel@tonic-gate if (uiomove((caddr_t)dirent, reclen, UIO_READ, uiop)) 2950Sstevel@tonic-gate return (EFAULT); 2960Sstevel@tonic-gate uiop->uio_offset = off + FDSDSIZE; 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate if (eofp) 2990Sstevel@tonic-gate *eofp = ((uiop->uio_offset-2*FDSDSIZE)/FDSDSIZE >= nentries); 3000Sstevel@tonic-gate return (0); 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate /* ARGSUSED */ 3040Sstevel@tonic-gate static void 3055331Samw fdinactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) 3060Sstevel@tonic-gate { 3070Sstevel@tonic-gate mutex_enter(&vp->v_lock); 3080Sstevel@tonic-gate ASSERT(vp->v_count >= 1); 3090Sstevel@tonic-gate if (--vp->v_count != 0) { 3100Sstevel@tonic-gate mutex_exit(&vp->v_lock); 3110Sstevel@tonic-gate return; 3120Sstevel@tonic-gate } 3130Sstevel@tonic-gate mutex_exit(&vp->v_lock); 3140Sstevel@tonic-gate vn_invalid(vp); 3150Sstevel@tonic-gate vn_free(vp); 3160Sstevel@tonic-gate } 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate static struct vnodeops *fd_vnodeops; 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate static const fs_operation_def_t fd_vnodeops_template[] = { 3213898Srsb VOPNAME_OPEN, { .vop_open = fdopen }, 3223898Srsb VOPNAME_CLOSE, { .vop_close = fdclose }, 3233898Srsb VOPNAME_READ, { .vop_read = fdread }, 3243898Srsb VOPNAME_GETATTR, { .vop_getattr = fdgetattr }, 3253898Srsb VOPNAME_ACCESS, { .vop_access = fdaccess }, 3263898Srsb VOPNAME_LOOKUP, { .vop_lookup = fdlookup }, 3273898Srsb VOPNAME_CREATE, { .vop_create = fdcreate }, 3283898Srsb VOPNAME_READDIR, { .vop_readdir = fdreaddir }, 3293898Srsb VOPNAME_INACTIVE, { .vop_inactive = fdinactive }, 3303898Srsb VOPNAME_FRLOCK, { .error = fs_error }, 3313898Srsb VOPNAME_POLL, { .error = fs_error }, 3323898Srsb VOPNAME_DISPOSE, { .error = fs_error }, 3333898Srsb NULL, NULL 3340Sstevel@tonic-gate }; 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate static int 3370Sstevel@tonic-gate fdget(struct vnode *dvp, char *comp, struct vnode **vpp) 3380Sstevel@tonic-gate { 3390Sstevel@tonic-gate int n = 0; 3400Sstevel@tonic-gate struct vnode *vp; 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate while (*comp) { 3430Sstevel@tonic-gate if (*comp < '0' || *comp > '9') 3440Sstevel@tonic-gate return (ENOENT); 3450Sstevel@tonic-gate n = 10 * n + *comp++ - '0'; 3460Sstevel@tonic-gate } 3470Sstevel@tonic-gate vp = vn_alloc(KM_SLEEP); 3480Sstevel@tonic-gate vp->v_type = VCHR; 3490Sstevel@tonic-gate vp->v_vfsp = dvp->v_vfsp; 3500Sstevel@tonic-gate vn_setops(vp, fd_vnodeops); 3510Sstevel@tonic-gate vp->v_data = NULL; 3520Sstevel@tonic-gate vp->v_flag = VNOMAP; 3530Sstevel@tonic-gate vp->v_rdev = makedevice(fdrmaj, n); 3540Sstevel@tonic-gate vn_exists(vp); 3550Sstevel@tonic-gate *vpp = vp; 3560Sstevel@tonic-gate return (0); 3570Sstevel@tonic-gate } 3580Sstevel@tonic-gate 3590Sstevel@tonic-gate /* 3600Sstevel@tonic-gate * fdfs is mounted on /dev/fd, however, there are two interesting 3610Sstevel@tonic-gate * possibilities - two threads racing to do the same mount (protected 3620Sstevel@tonic-gate * by vfs locking), and two threads mounting fdfs in different places. 3630Sstevel@tonic-gate */ 3640Sstevel@tonic-gate /*ARGSUSED*/ 3650Sstevel@tonic-gate static int 3660Sstevel@tonic-gate fdmount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) 3670Sstevel@tonic-gate { 3680Sstevel@tonic-gate struct vnode *vp; 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate if (secpolicy_fs_mount(cr, mvp, vfsp) != 0) 3710Sstevel@tonic-gate return (EPERM); 3720Sstevel@tonic-gate if (mvp->v_type != VDIR) 3730Sstevel@tonic-gate return (ENOTDIR); 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate mutex_enter(&mvp->v_lock); 3760Sstevel@tonic-gate if ((uap->flags & MS_OVERLAY) == 0 && 3770Sstevel@tonic-gate (mvp->v_count > 1 || (mvp->v_flag & VROOT))) { 3780Sstevel@tonic-gate mutex_exit(&mvp->v_lock); 3790Sstevel@tonic-gate return (EBUSY); 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate mutex_exit(&mvp->v_lock); 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate /* 3840Sstevel@tonic-gate * Having the resource be anything but "fd" doesn't make sense 3850Sstevel@tonic-gate */ 3860Sstevel@tonic-gate vfs_setresource(vfsp, "fd"); 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate vp = vn_alloc(KM_SLEEP); 3890Sstevel@tonic-gate vp->v_vfsp = vfsp; 3900Sstevel@tonic-gate vn_setops(vp, fd_vnodeops); 3910Sstevel@tonic-gate vp->v_type = VDIR; 3920Sstevel@tonic-gate vp->v_data = NULL; 3930Sstevel@tonic-gate vp->v_flag |= VROOT; 3940Sstevel@tonic-gate vfsp->vfs_fstype = fdfstype; 3950Sstevel@tonic-gate vfsp->vfs_data = (char *)vp; 3960Sstevel@tonic-gate mutex_enter(&fd_minor_lock); 3970Sstevel@tonic-gate do { 3980Sstevel@tonic-gate fdfsmin = (fdfsmin + 1) & L_MAXMIN32; 3990Sstevel@tonic-gate vfsp->vfs_dev = makedevice(fdfsmaj, fdfsmin); 4000Sstevel@tonic-gate } while (vfs_devismounted(vfsp->vfs_dev)); 4010Sstevel@tonic-gate mutex_exit(&fd_minor_lock); 4020Sstevel@tonic-gate vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, fdfstype); 4030Sstevel@tonic-gate vfsp->vfs_bsize = 1024; 4040Sstevel@tonic-gate return (0); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate /* ARGSUSED */ 4080Sstevel@tonic-gate static int 4090Sstevel@tonic-gate fdunmount(vfs_t *vfsp, int flag, cred_t *cr) 4100Sstevel@tonic-gate { 4110Sstevel@tonic-gate vnode_t *rvp; 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate if (secpolicy_fs_unmount(cr, vfsp) != 0) 4140Sstevel@tonic-gate return (EPERM); 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate /* 4170Sstevel@tonic-gate * forced unmount is not supported by this file system 4180Sstevel@tonic-gate * and thus, ENOTSUP, is being returned. 4190Sstevel@tonic-gate */ 4200Sstevel@tonic-gate if (flag & MS_FORCE) 4210Sstevel@tonic-gate return (ENOTSUP); 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate rvp = (vnode_t *)vfsp->vfs_data; 4240Sstevel@tonic-gate if (rvp->v_count > 1) 4250Sstevel@tonic-gate return (EBUSY); 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate VN_RELE(rvp); 4280Sstevel@tonic-gate return (0); 4290Sstevel@tonic-gate } 4300Sstevel@tonic-gate 4310Sstevel@tonic-gate /* ARGSUSED */ 4320Sstevel@tonic-gate static int 4330Sstevel@tonic-gate fdroot(vfs_t *vfsp, vnode_t **vpp) 4340Sstevel@tonic-gate { 4350Sstevel@tonic-gate vnode_t *vp = (vnode_t *)vfsp->vfs_data; 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate VN_HOLD(vp); 4380Sstevel@tonic-gate *vpp = vp; 4390Sstevel@tonic-gate return (0); 4400Sstevel@tonic-gate } 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate /* 4430Sstevel@tonic-gate * No locking required because I held the root vnode before calling this 4440Sstevel@tonic-gate * function so the vfs won't disappear on me. To be more explicit: 4450Sstevel@tonic-gate * fdvrootp->v_count will be greater than 1 so fdunmount will just return. 4460Sstevel@tonic-gate */ 4470Sstevel@tonic-gate static int 4480Sstevel@tonic-gate fdstatvfs(struct vfs *vfsp, struct statvfs64 *sp) 4490Sstevel@tonic-gate { 4500Sstevel@tonic-gate dev32_t d32; 4510Sstevel@tonic-gate rctl_qty_t fdno_ctl; 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate mutex_enter(&curproc->p_lock); 4540Sstevel@tonic-gate fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE], 4550Sstevel@tonic-gate curproc->p_rctls, curproc); 4560Sstevel@tonic-gate mutex_exit(&curproc->p_lock); 4570Sstevel@tonic-gate 4580Sstevel@tonic-gate bzero(sp, sizeof (*sp)); 4590Sstevel@tonic-gate sp->f_bsize = 1024; 4600Sstevel@tonic-gate sp->f_frsize = 1024; 4610Sstevel@tonic-gate sp->f_blocks = (fsblkcnt64_t)0; 4620Sstevel@tonic-gate sp->f_bfree = (fsblkcnt64_t)0; 4630Sstevel@tonic-gate sp->f_bavail = (fsblkcnt64_t)0; 4640Sstevel@tonic-gate sp->f_files = (fsfilcnt64_t) 4650Sstevel@tonic-gate (MIN(P_FINFO(curproc)->fi_nfiles, fdno_ctl + 2)); 4660Sstevel@tonic-gate sp->f_ffree = (fsfilcnt64_t)0; 4670Sstevel@tonic-gate sp->f_favail = (fsfilcnt64_t)0; 4680Sstevel@tonic-gate (void) cmpldev(&d32, vfsp->vfs_dev); 4690Sstevel@tonic-gate sp->f_fsid = d32; 4700Sstevel@tonic-gate (void) strcpy(sp->f_basetype, vfssw[fdfstype].vsw_name); 4710Sstevel@tonic-gate sp->f_flag = vf_to_stf(vfsp->vfs_flag); 4720Sstevel@tonic-gate sp->f_namemax = FDNSIZE; 4730Sstevel@tonic-gate (void) strcpy(sp->f_fstr, "/dev/fd"); 4740Sstevel@tonic-gate (void) strcpy(&sp->f_fstr[8], "/dev/fd"); 4750Sstevel@tonic-gate return (0); 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate int 4790Sstevel@tonic-gate fdinit(int fstype, char *name) 4800Sstevel@tonic-gate { 4810Sstevel@tonic-gate static const fs_operation_def_t fd_vfsops_template[] = { 4823898Srsb VFSNAME_MOUNT, { .vfs_mount = fdmount }, 4833898Srsb VFSNAME_UNMOUNT, { .vfs_unmount = fdunmount }, 4843898Srsb VFSNAME_ROOT, { .vfs_root = fdroot }, 4853898Srsb VFSNAME_STATVFS, { .vfs_statvfs = fdstatvfs }, 4863898Srsb NULL, NULL 4870Sstevel@tonic-gate }; 4880Sstevel@tonic-gate int error; 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate fdfstype = fstype; 4910Sstevel@tonic-gate ASSERT(fdfstype != 0); 4920Sstevel@tonic-gate 4930Sstevel@tonic-gate /* 4940Sstevel@tonic-gate * Associate VFS ops vector with this fstype. 4950Sstevel@tonic-gate */ 4960Sstevel@tonic-gate error = vfs_setfsops(fstype, fd_vfsops_template, NULL); 4970Sstevel@tonic-gate if (error != 0) { 4980Sstevel@tonic-gate cmn_err(CE_WARN, "fdinit: bad vnode ops template"); 4990Sstevel@tonic-gate return (error); 5000Sstevel@tonic-gate } 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate error = vn_make_ops(name, fd_vnodeops_template, &fd_vnodeops); 5030Sstevel@tonic-gate if (error != 0) { 5040Sstevel@tonic-gate (void) vfs_freevfsops_by_type(fstype); 5050Sstevel@tonic-gate cmn_err(CE_WARN, "fdinit: bad vnode ops template"); 5060Sstevel@tonic-gate return (error); 5070Sstevel@tonic-gate } 5080Sstevel@tonic-gate 5090Sstevel@tonic-gate /* 5100Sstevel@tonic-gate * Assign unique "device" numbers (reported by stat(2)). 5110Sstevel@tonic-gate */ 5120Sstevel@tonic-gate fdfsmaj = getudev(); 5130Sstevel@tonic-gate fdrmaj = getudev(); 5140Sstevel@tonic-gate if (fdfsmaj == (major_t)-1 || fdrmaj == (major_t)-1) { 5150Sstevel@tonic-gate cmn_err(CE_WARN, "fdinit: can't get unique device numbers"); 5160Sstevel@tonic-gate if (fdfsmaj == (major_t)-1) 5170Sstevel@tonic-gate fdfsmaj = 0; 5180Sstevel@tonic-gate if (fdrmaj == (major_t)-1) 5190Sstevel@tonic-gate fdrmaj = 0; 5200Sstevel@tonic-gate } 5210Sstevel@tonic-gate mutex_init(&fd_minor_lock, NULL, MUTEX_DEFAULT, NULL); 5220Sstevel@tonic-gate return (0); 5230Sstevel@tonic-gate } 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate /* 5260Sstevel@tonic-gate * FDFS Mount options table 5270Sstevel@tonic-gate */ 5280Sstevel@tonic-gate static char *rw_cancel[] = { MNTOPT_RO, NULL }; 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate static mntopt_t mntopts[] = { 5310Sstevel@tonic-gate /* 5320Sstevel@tonic-gate * option name cancel option default arg flags 5330Sstevel@tonic-gate */ 5340Sstevel@tonic-gate { MNTOPT_RW, rw_cancel, NULL, MO_DEFAULT, 5350Sstevel@tonic-gate (void *)MNTOPT_NOINTR }, 5360Sstevel@tonic-gate { MNTOPT_IGNORE, NULL, NULL, 0, 5370Sstevel@tonic-gate (void *)0 }, 5380Sstevel@tonic-gate }; 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate static mntopts_t fdfs_mntopts = { 5410Sstevel@tonic-gate sizeof (mntopts) / sizeof (mntopt_t), 5420Sstevel@tonic-gate mntopts 5430Sstevel@tonic-gate }; 5440Sstevel@tonic-gate 5450Sstevel@tonic-gate static vfsdef_t vfw = { 5460Sstevel@tonic-gate VFSDEF_VERSION, 5470Sstevel@tonic-gate "fd", 5480Sstevel@tonic-gate fdinit, 549*12633Sjohn.levon@sun.com VSW_HASPROTO | VSW_ZMOUNT, 5500Sstevel@tonic-gate &fdfs_mntopts 5510Sstevel@tonic-gate }; 5520Sstevel@tonic-gate 5530Sstevel@tonic-gate static struct modlfs modlfs = { 5540Sstevel@tonic-gate &mod_fsops, 5550Sstevel@tonic-gate "filesystem for fd", 5560Sstevel@tonic-gate &vfw 5570Sstevel@tonic-gate }; 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate static struct modlinkage modlinkage = { 5600Sstevel@tonic-gate MODREV_1, 5610Sstevel@tonic-gate &modlfs, 5620Sstevel@tonic-gate NULL 5630Sstevel@tonic-gate }; 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate int 5660Sstevel@tonic-gate _init(void) 5670Sstevel@tonic-gate { 5680Sstevel@tonic-gate return (mod_install(&modlinkage)); 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate int 5720Sstevel@tonic-gate _info(struct modinfo *modinfop) 5730Sstevel@tonic-gate { 5740Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 5750Sstevel@tonic-gate } 576