xref: /titanic_51/usr/src/uts/common/fs/fd/fdops.c (revision d7de0cea9111a93d26efcfa259585dabbde02eea)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5aa59c4cbSrsb  * Common Development and Distribution License (the "License").
6aa59c4cbSrsb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
220fbb751dSJohn Levon  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
267c478bd9Sstevel@tonic-gate /*	  All rights reserved.  	*/
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/param.h>
317c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
327c478bd9Sstevel@tonic-gate #include <sys/debug.h>
337c478bd9Sstevel@tonic-gate #include <sys/dirent.h>
347c478bd9Sstevel@tonic-gate #include <sys/errno.h>
357c478bd9Sstevel@tonic-gate #include <sys/file.h>
367c478bd9Sstevel@tonic-gate #include <sys/inline.h>
377c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
387c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
397c478bd9Sstevel@tonic-gate #include <sys/resource.h>
407c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
417c478bd9Sstevel@tonic-gate #include <sys/mount.h>
427c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
437c478bd9Sstevel@tonic-gate #include <sys/systm.h>
447c478bd9Sstevel@tonic-gate #include <sys/uio.h>
457c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
46aa59c4cbSrsb #include <sys/vfs_opreg.h>
477c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
487c478bd9Sstevel@tonic-gate #include <sys/cred.h>
497c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
507c478bd9Sstevel@tonic-gate #include <sys/mount.h>
517c478bd9Sstevel@tonic-gate #include <sys/user.h>
527c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
537c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
547c478bd9Sstevel@tonic-gate #include <sys/policy.h>
557c478bd9Sstevel@tonic-gate #include <fs/fs_subr.h>
567c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
577c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #define	round(r)	(((r)+sizeof (int)-1)&(~(sizeof (int)-1)))
607c478bd9Sstevel@tonic-gate #define	fdtoi(n)	((n)+100)
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate #define	FDDIRSIZE 14
637c478bd9Sstevel@tonic-gate struct fddirect {
647c478bd9Sstevel@tonic-gate 	short	d_ino;
657c478bd9Sstevel@tonic-gate 	char	d_name[FDDIRSIZE];
667c478bd9Sstevel@tonic-gate };
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate #define	FDROOTINO	2
697c478bd9Sstevel@tonic-gate #define	FDSDSIZE	sizeof (struct fddirect)
707c478bd9Sstevel@tonic-gate #define	FDNSIZE		10
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate static int		fdfstype = 0;
737c478bd9Sstevel@tonic-gate static major_t		fdfsmaj;
747c478bd9Sstevel@tonic-gate static minor_t		fdfsmin;
757c478bd9Sstevel@tonic-gate static major_t		fdrmaj;
767c478bd9Sstevel@tonic-gate static kmutex_t		fd_minor_lock;
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate static int fdget(vnode_t *, char *, vnode_t **);
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate /* ARGSUSED */
817c478bd9Sstevel@tonic-gate static int
82da6c28aaSamw fdopen(vnode_t **vpp, int mode, cred_t *cr, caller_context_t *ct)
837c478bd9Sstevel@tonic-gate {
847c478bd9Sstevel@tonic-gate 	if ((*vpp)->v_type != VDIR) {
857c478bd9Sstevel@tonic-gate 		mutex_enter(&(*vpp)->v_lock);
867c478bd9Sstevel@tonic-gate 		(*vpp)->v_flag |= VDUP;
877c478bd9Sstevel@tonic-gate 		mutex_exit(&(*vpp)->v_lock);
887c478bd9Sstevel@tonic-gate 	}
897c478bd9Sstevel@tonic-gate 	return (0);
907c478bd9Sstevel@tonic-gate }
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate /* ARGSUSED */
937c478bd9Sstevel@tonic-gate static int
94da6c28aaSamw fdclose(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
95da6c28aaSamw 	caller_context_t *ct)
967c478bd9Sstevel@tonic-gate {
977c478bd9Sstevel@tonic-gate 	return (0);
987c478bd9Sstevel@tonic-gate }
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate /* ARGSUSED */
1017c478bd9Sstevel@tonic-gate static int
1027c478bd9Sstevel@tonic-gate fdread(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct)
1037c478bd9Sstevel@tonic-gate {
1047c478bd9Sstevel@tonic-gate 	static struct fddirect dotbuf[] = {
1057c478bd9Sstevel@tonic-gate 		{ FDROOTINO, "."  },
1067c478bd9Sstevel@tonic-gate 		{ FDROOTINO, ".." }
1077c478bd9Sstevel@tonic-gate 	};
1087c478bd9Sstevel@tonic-gate 	struct fddirect dirbuf;
1097c478bd9Sstevel@tonic-gate 	int i, n;
1107c478bd9Sstevel@tonic-gate 	int minfd, maxfd, modoff, error = 0;
1117c478bd9Sstevel@tonic-gate 	int nentries;
1127c478bd9Sstevel@tonic-gate 	rctl_qty_t fdno_ctl;
1137c478bd9Sstevel@tonic-gate 	int endoff;
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 	if (vp->v_type != VDIR)
1167c478bd9Sstevel@tonic-gate 		return (ENOSYS);
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	mutex_enter(&curproc->p_lock);
1197c478bd9Sstevel@tonic-gate 	fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE],
1207c478bd9Sstevel@tonic-gate 	    curproc->p_rctls, curproc);
1217c478bd9Sstevel@tonic-gate 	nentries = MIN(P_FINFO(curproc)->fi_nfiles, (int)fdno_ctl);
1227c478bd9Sstevel@tonic-gate 	mutex_exit(&curproc->p_lock);
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	endoff = (nentries + 2) * FDSDSIZE;
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	/*
1277c478bd9Sstevel@tonic-gate 	 * Fake up ".", "..", and the /dev/fd directory entries.
1287c478bd9Sstevel@tonic-gate 	 */
1297c478bd9Sstevel@tonic-gate 	if (uiop->uio_loffset < (offset_t)0 ||
1307c478bd9Sstevel@tonic-gate 	    uiop->uio_loffset >= (offset_t)endoff ||
1317c478bd9Sstevel@tonic-gate 	    uiop->uio_resid <= 0)
1327c478bd9Sstevel@tonic-gate 		return (0);
1337c478bd9Sstevel@tonic-gate 	ASSERT(uiop->uio_loffset <= MAXOFF_T);
1347c478bd9Sstevel@tonic-gate 	if (uiop->uio_offset < 2*FDSDSIZE) {
1357c478bd9Sstevel@tonic-gate 		error = uiomove((caddr_t)dotbuf + uiop->uio_offset,
1367c478bd9Sstevel@tonic-gate 		    MIN(uiop->uio_resid, 2*FDSDSIZE - uiop->uio_offset),
1377c478bd9Sstevel@tonic-gate 		    UIO_READ, uiop);
1387c478bd9Sstevel@tonic-gate 		if (uiop->uio_resid <= 0 || error)
1397c478bd9Sstevel@tonic-gate 			return (error);
1407c478bd9Sstevel@tonic-gate 	}
1417c478bd9Sstevel@tonic-gate 	minfd = (uiop->uio_offset - 2*FDSDSIZE)/FDSDSIZE;
1427c478bd9Sstevel@tonic-gate 	maxfd = (uiop->uio_offset + uiop->uio_resid - 1)/FDSDSIZE;
1437c478bd9Sstevel@tonic-gate 	modoff = uiop->uio_offset % FDSDSIZE;
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	for (i = 0; i < FDDIRSIZE; i++)
1467c478bd9Sstevel@tonic-gate 		dirbuf.d_name[i] = '\0';
1477c478bd9Sstevel@tonic-gate 	for (i = minfd; i < MIN(maxfd, nentries); i++) {
1487c478bd9Sstevel@tonic-gate 		n = i;
1497c478bd9Sstevel@tonic-gate 		dirbuf.d_ino = fdtoi(n);
1507c478bd9Sstevel@tonic-gate 		numtos((ulong_t)n, dirbuf.d_name);
1517c478bd9Sstevel@tonic-gate 		error = uiomove((caddr_t)&dirbuf + modoff,
1527c478bd9Sstevel@tonic-gate 		    MIN(uiop->uio_resid, FDSDSIZE - modoff),
1537c478bd9Sstevel@tonic-gate 		    UIO_READ, uiop);
1547c478bd9Sstevel@tonic-gate 		if (uiop->uio_resid <= 0 || error)
1557c478bd9Sstevel@tonic-gate 			return (error);
1567c478bd9Sstevel@tonic-gate 		modoff = 0;
1577c478bd9Sstevel@tonic-gate 	}
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	return (error);
1607c478bd9Sstevel@tonic-gate }
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate /* ARGSUSED */
1637c478bd9Sstevel@tonic-gate static int
164da6c28aaSamw fdgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
165da6c28aaSamw 	caller_context_t *ct)
1667c478bd9Sstevel@tonic-gate {
1677c478bd9Sstevel@tonic-gate 	vfs_t *vfsp = vp->v_vfsp;
1687c478bd9Sstevel@tonic-gate 	timestruc_t now;
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	if (vp->v_type == VDIR) {
1717c478bd9Sstevel@tonic-gate 		vap->va_nlink = 2;
1727c478bd9Sstevel@tonic-gate 		vap->va_size = (u_offset_t)
1737c478bd9Sstevel@tonic-gate 		    ((P_FINFO(curproc)->fi_nfiles + 2) * FDSDSIZE);
1747c478bd9Sstevel@tonic-gate 		vap->va_mode = 0555;
1757c478bd9Sstevel@tonic-gate 		vap->va_nodeid = (ino64_t)FDROOTINO;
1767c478bd9Sstevel@tonic-gate 	} else {
1777c478bd9Sstevel@tonic-gate 		vap->va_nlink = 1;
1787c478bd9Sstevel@tonic-gate 		vap->va_size = (u_offset_t)0;
1797c478bd9Sstevel@tonic-gate 		vap->va_mode = 0666;
1807c478bd9Sstevel@tonic-gate 		vap->va_nodeid = (ino64_t)fdtoi(getminor(vp->v_rdev));
1817c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 	vap->va_type = vp->v_type;
1837c478bd9Sstevel@tonic-gate 	vap->va_rdev = vp->v_rdev;
1847c478bd9Sstevel@tonic-gate 	vap->va_blksize = vfsp->vfs_bsize;
1857c478bd9Sstevel@tonic-gate 	vap->va_nblocks = (fsblkcnt64_t)0;
1867c478bd9Sstevel@tonic-gate 	gethrestime(&now);
1877c478bd9Sstevel@tonic-gate 	vap->va_atime = vap->va_mtime = vap->va_ctime = now;
1887c478bd9Sstevel@tonic-gate 	vap->va_uid = 0;
1897c478bd9Sstevel@tonic-gate 	vap->va_gid = 0;
1907c478bd9Sstevel@tonic-gate 	vap->va_fsid = vfsp->vfs_dev;
1917c478bd9Sstevel@tonic-gate 	vap->va_seq = 0;
1927c478bd9Sstevel@tonic-gate 	return (0);
1937c478bd9Sstevel@tonic-gate }
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate /* ARGSUSED */
1967c478bd9Sstevel@tonic-gate static int
197da6c28aaSamw fdaccess(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
1987c478bd9Sstevel@tonic-gate {
1997c478bd9Sstevel@tonic-gate 	return (0);
2007c478bd9Sstevel@tonic-gate }
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate /* ARGSUSED */
2037c478bd9Sstevel@tonic-gate static int
2047c478bd9Sstevel@tonic-gate fdlookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pnp,
205da6c28aaSamw 	int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
206da6c28aaSamw 	int *direntflags, pathname_t *realpnp)
2077c478bd9Sstevel@tonic-gate {
2087c478bd9Sstevel@tonic-gate 	if (comp[0] == 0 || strcmp(comp, ".") == 0 || strcmp(comp, "..") == 0) {
2097c478bd9Sstevel@tonic-gate 		VN_HOLD(dp);
2107c478bd9Sstevel@tonic-gate 		*vpp = dp;
2117c478bd9Sstevel@tonic-gate 		return (0);
2127c478bd9Sstevel@tonic-gate 	}
2137c478bd9Sstevel@tonic-gate 	return (fdget(dp, comp, vpp));
2147c478bd9Sstevel@tonic-gate }
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate /* ARGSUSED */
2177c478bd9Sstevel@tonic-gate static int
2187c478bd9Sstevel@tonic-gate fdcreate(vnode_t *dvp, char *comp, vattr_t *vap, enum vcexcl excl,
219da6c28aaSamw 	int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
220da6c28aaSamw 	vsecattr_t *vsecp)
2217c478bd9Sstevel@tonic-gate {
2227c478bd9Sstevel@tonic-gate 	return (fdget(dvp, comp, vpp));
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate /* ARGSUSED */
2267c478bd9Sstevel@tonic-gate static int
227da6c28aaSamw fdreaddir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp, caller_context_t *ct,
228da6c28aaSamw 	int flags)
2297c478bd9Sstevel@tonic-gate {
2307c478bd9Sstevel@tonic-gate 	/* bp holds one dirent structure */
2317c478bd9Sstevel@tonic-gate 	u_offset_t bp[DIRENT64_RECLEN(FDNSIZE) / sizeof (u_offset_t)];
2327c478bd9Sstevel@tonic-gate 	struct dirent64 *dirent = (struct dirent64 *)bp;
2337c478bd9Sstevel@tonic-gate 	int reclen, nentries;
2347c478bd9Sstevel@tonic-gate 	rctl_qty_t fdno_ctl;
2357c478bd9Sstevel@tonic-gate 	int  n;
2367c478bd9Sstevel@tonic-gate 	int oresid;
2377c478bd9Sstevel@tonic-gate 	off_t off;
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	if (uiop->uio_offset < 0 || uiop->uio_resid <= 0 ||
2407c478bd9Sstevel@tonic-gate 	    (uiop->uio_offset % FDSDSIZE) != 0)
2417c478bd9Sstevel@tonic-gate 		return (ENOENT);
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	ASSERT(uiop->uio_loffset <= MAXOFF_T);
2447c478bd9Sstevel@tonic-gate 	oresid = uiop->uio_resid;
2457c478bd9Sstevel@tonic-gate 	bzero(bp, sizeof (bp));
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	mutex_enter(&curproc->p_lock);
2487c478bd9Sstevel@tonic-gate 	fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE],
2497c478bd9Sstevel@tonic-gate 	    curproc->p_rctls, curproc);
2507c478bd9Sstevel@tonic-gate 	nentries = MIN(P_FINFO(curproc)->fi_nfiles, (int)fdno_ctl);
2517c478bd9Sstevel@tonic-gate 	mutex_exit(&curproc->p_lock);
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 	while (uiop->uio_resid > 0) {
2547c478bd9Sstevel@tonic-gate 		if ((off = uiop->uio_offset) == 0) {	/* "." */
2557c478bd9Sstevel@tonic-gate 			dirent->d_ino = (ino64_t)FDROOTINO;
2567c478bd9Sstevel@tonic-gate 			dirent->d_name[0] = '.';
2577c478bd9Sstevel@tonic-gate 			dirent->d_name[1] = '\0';
2587c478bd9Sstevel@tonic-gate 			reclen = DIRENT64_RECLEN(1);
2597c478bd9Sstevel@tonic-gate 		} else if (off == FDSDSIZE) {		/* ".." */
2607c478bd9Sstevel@tonic-gate 			dirent->d_ino = (ino64_t)FDROOTINO;
2617c478bd9Sstevel@tonic-gate 			dirent->d_name[0] = '.';
2627c478bd9Sstevel@tonic-gate 			dirent->d_name[1] = '.';
2637c478bd9Sstevel@tonic-gate 			dirent->d_name[2] = '\0';
2647c478bd9Sstevel@tonic-gate 			reclen = DIRENT64_RECLEN(2);
2657c478bd9Sstevel@tonic-gate 		} else {
2667c478bd9Sstevel@tonic-gate 			/*
2677c478bd9Sstevel@tonic-gate 			 * Return entries corresponding to the allowable
2687c478bd9Sstevel@tonic-gate 			 * number of file descriptors for this process.
2697c478bd9Sstevel@tonic-gate 			 */
2707c478bd9Sstevel@tonic-gate 			if ((n = (off-2*FDSDSIZE)/FDSDSIZE) >= nentries)
2717c478bd9Sstevel@tonic-gate 				break;
2727c478bd9Sstevel@tonic-gate 			dirent->d_ino = (ino64_t)fdtoi(n);
2737c478bd9Sstevel@tonic-gate 			numtos((ulong_t)n, dirent->d_name);
2747c478bd9Sstevel@tonic-gate 			reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
2757c478bd9Sstevel@tonic-gate 		}
2767c478bd9Sstevel@tonic-gate 		dirent->d_off = (offset_t)(uiop->uio_offset + FDSDSIZE);
2777c478bd9Sstevel@tonic-gate 		dirent->d_reclen = (ushort_t)reclen;
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 		if (reclen > uiop->uio_resid) {
2807c478bd9Sstevel@tonic-gate 			/*
2817c478bd9Sstevel@tonic-gate 			 * Error if no entries have been returned yet.
2827c478bd9Sstevel@tonic-gate 			 */
2837c478bd9Sstevel@tonic-gate 			if (uiop->uio_resid == oresid)
2847c478bd9Sstevel@tonic-gate 				return (EINVAL);
2857c478bd9Sstevel@tonic-gate 			break;
2867c478bd9Sstevel@tonic-gate 		}
2877c478bd9Sstevel@tonic-gate 		/*
2887c478bd9Sstevel@tonic-gate 		 * uiomove() updates both resid and offset by the same
2897c478bd9Sstevel@tonic-gate 		 * amount.  But we want offset to change in increments
2907c478bd9Sstevel@tonic-gate 		 * of FDSDSIZE, which is different from the number of bytes
2917c478bd9Sstevel@tonic-gate 		 * being returned to the user.  So we set uio_offset
2927c478bd9Sstevel@tonic-gate 		 * separately, ignoring what uiomove() does.
2937c478bd9Sstevel@tonic-gate 		 */
2947c478bd9Sstevel@tonic-gate 		if (uiomove((caddr_t)dirent, reclen, UIO_READ, uiop))
2957c478bd9Sstevel@tonic-gate 			return (EFAULT);
2967c478bd9Sstevel@tonic-gate 		uiop->uio_offset = off + FDSDSIZE;
2977c478bd9Sstevel@tonic-gate 	}
2987c478bd9Sstevel@tonic-gate 	if (eofp)
2997c478bd9Sstevel@tonic-gate 		*eofp = ((uiop->uio_offset-2*FDSDSIZE)/FDSDSIZE >= nentries);
3007c478bd9Sstevel@tonic-gate 	return (0);
3017c478bd9Sstevel@tonic-gate }
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate /* ARGSUSED */
3047c478bd9Sstevel@tonic-gate static void
305da6c28aaSamw fdinactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
3067c478bd9Sstevel@tonic-gate {
3077c478bd9Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
3087c478bd9Sstevel@tonic-gate 	ASSERT(vp->v_count >= 1);
3097c478bd9Sstevel@tonic-gate 	if (--vp->v_count != 0) {
3107c478bd9Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
3117c478bd9Sstevel@tonic-gate 		return;
3127c478bd9Sstevel@tonic-gate 	}
3137c478bd9Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
3147c478bd9Sstevel@tonic-gate 	vn_invalid(vp);
3157c478bd9Sstevel@tonic-gate 	vn_free(vp);
3167c478bd9Sstevel@tonic-gate }
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate static struct vnodeops *fd_vnodeops;
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate static const fs_operation_def_t fd_vnodeops_template[] = {
321aa59c4cbSrsb 	VOPNAME_OPEN,		{ .vop_open = fdopen },
322aa59c4cbSrsb 	VOPNAME_CLOSE,		{ .vop_close = fdclose },
323aa59c4cbSrsb 	VOPNAME_READ,		{ .vop_read = fdread },
324aa59c4cbSrsb 	VOPNAME_GETATTR,	{ .vop_getattr = fdgetattr },
325aa59c4cbSrsb 	VOPNAME_ACCESS,		{ .vop_access = fdaccess },
326aa59c4cbSrsb 	VOPNAME_LOOKUP,		{ .vop_lookup = fdlookup },
327aa59c4cbSrsb 	VOPNAME_CREATE,		{ .vop_create = fdcreate },
328aa59c4cbSrsb 	VOPNAME_READDIR,	{ .vop_readdir = fdreaddir },
329aa59c4cbSrsb 	VOPNAME_INACTIVE,	{ .vop_inactive = fdinactive },
330aa59c4cbSrsb 	VOPNAME_FRLOCK,		{ .error = fs_error },
331aa59c4cbSrsb 	VOPNAME_POLL,		{ .error = fs_error },
332aa59c4cbSrsb 	VOPNAME_DISPOSE,	{ .error = fs_error },
3337c478bd9Sstevel@tonic-gate 	NULL,			NULL
3347c478bd9Sstevel@tonic-gate };
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate static int
3377c478bd9Sstevel@tonic-gate fdget(struct vnode *dvp, char *comp, struct vnode **vpp)
3387c478bd9Sstevel@tonic-gate {
3397c478bd9Sstevel@tonic-gate 	int n = 0;
3407c478bd9Sstevel@tonic-gate 	struct vnode *vp;
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	while (*comp) {
3437c478bd9Sstevel@tonic-gate 		if (*comp < '0' || *comp > '9')
3447c478bd9Sstevel@tonic-gate 			return (ENOENT);
3457c478bd9Sstevel@tonic-gate 		n = 10 * n + *comp++ - '0';
3467c478bd9Sstevel@tonic-gate 	}
3477c478bd9Sstevel@tonic-gate 	vp = vn_alloc(KM_SLEEP);
3487c478bd9Sstevel@tonic-gate 	vp->v_type = VCHR;
3497c478bd9Sstevel@tonic-gate 	vp->v_vfsp = dvp->v_vfsp;
3507c478bd9Sstevel@tonic-gate 	vn_setops(vp, fd_vnodeops);
3517c478bd9Sstevel@tonic-gate 	vp->v_data = NULL;
3527c478bd9Sstevel@tonic-gate 	vp->v_flag = VNOMAP;
3537c478bd9Sstevel@tonic-gate 	vp->v_rdev = makedevice(fdrmaj, n);
3547c478bd9Sstevel@tonic-gate 	vn_exists(vp);
3557c478bd9Sstevel@tonic-gate 	*vpp = vp;
3567c478bd9Sstevel@tonic-gate 	return (0);
3577c478bd9Sstevel@tonic-gate }
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate /*
3607c478bd9Sstevel@tonic-gate  * fdfs is mounted on /dev/fd, however, there are two interesting
3617c478bd9Sstevel@tonic-gate  * possibilities - two threads racing to do the same mount (protected
3627c478bd9Sstevel@tonic-gate  * by vfs locking), and two threads mounting fdfs in different places.
3637c478bd9Sstevel@tonic-gate  */
3647c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3657c478bd9Sstevel@tonic-gate static int
3667c478bd9Sstevel@tonic-gate fdmount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
3677c478bd9Sstevel@tonic-gate {
3687c478bd9Sstevel@tonic-gate 	struct vnode *vp;
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
3717c478bd9Sstevel@tonic-gate 		return (EPERM);
3727c478bd9Sstevel@tonic-gate 	if (mvp->v_type != VDIR)
3737c478bd9Sstevel@tonic-gate 		return (ENOTDIR);
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	mutex_enter(&mvp->v_lock);
3767c478bd9Sstevel@tonic-gate 	if ((uap->flags & MS_OVERLAY) == 0 &&
3777c478bd9Sstevel@tonic-gate 	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
3787c478bd9Sstevel@tonic-gate 		mutex_exit(&mvp->v_lock);
3797c478bd9Sstevel@tonic-gate 		return (EBUSY);
3807c478bd9Sstevel@tonic-gate 	}
3817c478bd9Sstevel@tonic-gate 	mutex_exit(&mvp->v_lock);
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	/*
3847c478bd9Sstevel@tonic-gate 	 * Having the resource be anything but "fd" doesn't make sense
3857c478bd9Sstevel@tonic-gate 	 */
386*d7de0ceaSRobert Harris 	vfs_setresource(vfsp, "fd", 0);
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	vp = vn_alloc(KM_SLEEP);
3897c478bd9Sstevel@tonic-gate 	vp->v_vfsp = vfsp;
3907c478bd9Sstevel@tonic-gate 	vn_setops(vp, fd_vnodeops);
3917c478bd9Sstevel@tonic-gate 	vp->v_type = VDIR;
3927c478bd9Sstevel@tonic-gate 	vp->v_data = NULL;
3937c478bd9Sstevel@tonic-gate 	vp->v_flag |= VROOT;
3947c478bd9Sstevel@tonic-gate 	vfsp->vfs_fstype = fdfstype;
3957c478bd9Sstevel@tonic-gate 	vfsp->vfs_data = (char *)vp;
3967c478bd9Sstevel@tonic-gate 	mutex_enter(&fd_minor_lock);
3977c478bd9Sstevel@tonic-gate 	do {
3987c478bd9Sstevel@tonic-gate 		fdfsmin = (fdfsmin + 1) & L_MAXMIN32;
3997c478bd9Sstevel@tonic-gate 		vfsp->vfs_dev = makedevice(fdfsmaj, fdfsmin);
4007c478bd9Sstevel@tonic-gate 	} while (vfs_devismounted(vfsp->vfs_dev));
4017c478bd9Sstevel@tonic-gate 	mutex_exit(&fd_minor_lock);
4027c478bd9Sstevel@tonic-gate 	vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, fdfstype);
4037c478bd9Sstevel@tonic-gate 	vfsp->vfs_bsize = 1024;
4047c478bd9Sstevel@tonic-gate 	return (0);
4057c478bd9Sstevel@tonic-gate }
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate /* ARGSUSED */
4087c478bd9Sstevel@tonic-gate static int
4097c478bd9Sstevel@tonic-gate fdunmount(vfs_t *vfsp, int flag, cred_t *cr)
4107c478bd9Sstevel@tonic-gate {
4117c478bd9Sstevel@tonic-gate 	vnode_t *rvp;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
4147c478bd9Sstevel@tonic-gate 		return (EPERM);
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	/*
4177c478bd9Sstevel@tonic-gate 	 * forced unmount is not supported by this file system
4187c478bd9Sstevel@tonic-gate 	 * and thus, ENOTSUP, is being returned.
4197c478bd9Sstevel@tonic-gate 	 */
4207c478bd9Sstevel@tonic-gate 	if (flag & MS_FORCE)
4217c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	rvp = (vnode_t *)vfsp->vfs_data;
4247c478bd9Sstevel@tonic-gate 	if (rvp->v_count > 1)
4257c478bd9Sstevel@tonic-gate 		return (EBUSY);
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 	VN_RELE(rvp);
4287c478bd9Sstevel@tonic-gate 	return (0);
4297c478bd9Sstevel@tonic-gate }
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate /* ARGSUSED */
4327c478bd9Sstevel@tonic-gate static int
4337c478bd9Sstevel@tonic-gate fdroot(vfs_t *vfsp, vnode_t **vpp)
4347c478bd9Sstevel@tonic-gate {
4357c478bd9Sstevel@tonic-gate 	vnode_t *vp = (vnode_t *)vfsp->vfs_data;
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate 	VN_HOLD(vp);
4387c478bd9Sstevel@tonic-gate 	*vpp = vp;
4397c478bd9Sstevel@tonic-gate 	return (0);
4407c478bd9Sstevel@tonic-gate }
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate /*
4437c478bd9Sstevel@tonic-gate  * No locking required because I held the root vnode before calling this
4447c478bd9Sstevel@tonic-gate  * function so the vfs won't disappear on me.  To be more explicit:
4457c478bd9Sstevel@tonic-gate  * fdvrootp->v_count will be greater than 1 so fdunmount will just return.
4467c478bd9Sstevel@tonic-gate  */
4477c478bd9Sstevel@tonic-gate static int
4487c478bd9Sstevel@tonic-gate fdstatvfs(struct vfs *vfsp, struct statvfs64 *sp)
4497c478bd9Sstevel@tonic-gate {
4507c478bd9Sstevel@tonic-gate 	dev32_t d32;
4517c478bd9Sstevel@tonic-gate 	rctl_qty_t fdno_ctl;
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	mutex_enter(&curproc->p_lock);
4547c478bd9Sstevel@tonic-gate 	fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE],
4557c478bd9Sstevel@tonic-gate 	    curproc->p_rctls, curproc);
4567c478bd9Sstevel@tonic-gate 	mutex_exit(&curproc->p_lock);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	bzero(sp, sizeof (*sp));
4597c478bd9Sstevel@tonic-gate 	sp->f_bsize = 1024;
4607c478bd9Sstevel@tonic-gate 	sp->f_frsize = 1024;
4617c478bd9Sstevel@tonic-gate 	sp->f_blocks = (fsblkcnt64_t)0;
4627c478bd9Sstevel@tonic-gate 	sp->f_bfree = (fsblkcnt64_t)0;
4637c478bd9Sstevel@tonic-gate 	sp->f_bavail = (fsblkcnt64_t)0;
4647c478bd9Sstevel@tonic-gate 	sp->f_files = (fsfilcnt64_t)
4657c478bd9Sstevel@tonic-gate 	    (MIN(P_FINFO(curproc)->fi_nfiles, fdno_ctl + 2));
4667c478bd9Sstevel@tonic-gate 	sp->f_ffree = (fsfilcnt64_t)0;
4677c478bd9Sstevel@tonic-gate 	sp->f_favail = (fsfilcnt64_t)0;
4687c478bd9Sstevel@tonic-gate 	(void) cmpldev(&d32, vfsp->vfs_dev);
4697c478bd9Sstevel@tonic-gate 	sp->f_fsid = d32;
4707c478bd9Sstevel@tonic-gate 	(void) strcpy(sp->f_basetype, vfssw[fdfstype].vsw_name);
4717c478bd9Sstevel@tonic-gate 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
4727c478bd9Sstevel@tonic-gate 	sp->f_namemax = FDNSIZE;
4737c478bd9Sstevel@tonic-gate 	(void) strcpy(sp->f_fstr, "/dev/fd");
4747c478bd9Sstevel@tonic-gate 	(void) strcpy(&sp->f_fstr[8], "/dev/fd");
4757c478bd9Sstevel@tonic-gate 	return (0);
4767c478bd9Sstevel@tonic-gate }
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate int
4797c478bd9Sstevel@tonic-gate fdinit(int fstype, char *name)
4807c478bd9Sstevel@tonic-gate {
4817c478bd9Sstevel@tonic-gate 	static const fs_operation_def_t fd_vfsops_template[] = {
482aa59c4cbSrsb 		VFSNAME_MOUNT,		{ .vfs_mount = fdmount },
483aa59c4cbSrsb 		VFSNAME_UNMOUNT,	{ .vfs_unmount = fdunmount },
484aa59c4cbSrsb 		VFSNAME_ROOT, 		{ .vfs_root = fdroot },
485aa59c4cbSrsb 		VFSNAME_STATVFS,	{ .vfs_statvfs = fdstatvfs },
4867c478bd9Sstevel@tonic-gate 		NULL,			NULL
4877c478bd9Sstevel@tonic-gate 	};
4887c478bd9Sstevel@tonic-gate 	int error;
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	fdfstype = fstype;
4917c478bd9Sstevel@tonic-gate 	ASSERT(fdfstype != 0);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	/*
4947c478bd9Sstevel@tonic-gate 	 * Associate VFS ops vector with this fstype.
4957c478bd9Sstevel@tonic-gate 	 */
4967c478bd9Sstevel@tonic-gate 	error = vfs_setfsops(fstype, fd_vfsops_template, NULL);
4977c478bd9Sstevel@tonic-gate 	if (error != 0) {
4987c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdinit: bad vnode ops template");
4997c478bd9Sstevel@tonic-gate 		return (error);
5007c478bd9Sstevel@tonic-gate 	}
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	error = vn_make_ops(name, fd_vnodeops_template, &fd_vnodeops);
5037c478bd9Sstevel@tonic-gate 	if (error != 0) {
5047c478bd9Sstevel@tonic-gate 		(void) vfs_freevfsops_by_type(fstype);
5057c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdinit: bad vnode ops template");
5067c478bd9Sstevel@tonic-gate 		return (error);
5077c478bd9Sstevel@tonic-gate 	}
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	/*
5107c478bd9Sstevel@tonic-gate 	 * Assign unique "device" numbers (reported by stat(2)).
5117c478bd9Sstevel@tonic-gate 	 */
5127c478bd9Sstevel@tonic-gate 	fdfsmaj = getudev();
5137c478bd9Sstevel@tonic-gate 	fdrmaj = getudev();
5147c478bd9Sstevel@tonic-gate 	if (fdfsmaj == (major_t)-1 || fdrmaj == (major_t)-1) {
5157c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdinit: can't get unique device numbers");
5167c478bd9Sstevel@tonic-gate 		if (fdfsmaj == (major_t)-1)
5177c478bd9Sstevel@tonic-gate 			fdfsmaj = 0;
5187c478bd9Sstevel@tonic-gate 		if (fdrmaj == (major_t)-1)
5197c478bd9Sstevel@tonic-gate 			fdrmaj = 0;
5207c478bd9Sstevel@tonic-gate 	}
5217c478bd9Sstevel@tonic-gate 	mutex_init(&fd_minor_lock, NULL, MUTEX_DEFAULT, NULL);
5227c478bd9Sstevel@tonic-gate 	return (0);
5237c478bd9Sstevel@tonic-gate }
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate /*
5267c478bd9Sstevel@tonic-gate  * FDFS Mount options table
5277c478bd9Sstevel@tonic-gate  */
5287c478bd9Sstevel@tonic-gate static char *rw_cancel[] = { MNTOPT_RO, NULL };
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate static mntopt_t mntopts[] = {
5317c478bd9Sstevel@tonic-gate /*
5327c478bd9Sstevel@tonic-gate  *	option name		cancel option	default arg	flags
5337c478bd9Sstevel@tonic-gate  */
5347c478bd9Sstevel@tonic-gate 	{ MNTOPT_RW,		rw_cancel,	NULL,		MO_DEFAULT,
5357c478bd9Sstevel@tonic-gate 		(void *)MNTOPT_NOINTR },
5367c478bd9Sstevel@tonic-gate 	{ MNTOPT_IGNORE,	NULL,		NULL,		0,
5377c478bd9Sstevel@tonic-gate 		(void *)0 },
5387c478bd9Sstevel@tonic-gate };
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate static mntopts_t fdfs_mntopts = {
5417c478bd9Sstevel@tonic-gate 	sizeof (mntopts) / sizeof (mntopt_t),
5427c478bd9Sstevel@tonic-gate 	mntopts
5437c478bd9Sstevel@tonic-gate };
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate static vfsdef_t vfw = {
5467c478bd9Sstevel@tonic-gate 	VFSDEF_VERSION,
5477c478bd9Sstevel@tonic-gate 	"fd",
5487c478bd9Sstevel@tonic-gate 	fdinit,
5490fbb751dSJohn Levon 	VSW_HASPROTO | VSW_ZMOUNT,
5507c478bd9Sstevel@tonic-gate 	&fdfs_mntopts
5517c478bd9Sstevel@tonic-gate };
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate static struct modlfs modlfs = {
5547c478bd9Sstevel@tonic-gate 	&mod_fsops,
5557c478bd9Sstevel@tonic-gate 	"filesystem for fd",
5567c478bd9Sstevel@tonic-gate 	&vfw
5577c478bd9Sstevel@tonic-gate };
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
5607c478bd9Sstevel@tonic-gate 	MODREV_1,
5617c478bd9Sstevel@tonic-gate 	&modlfs,
5627c478bd9Sstevel@tonic-gate 	NULL
5637c478bd9Sstevel@tonic-gate };
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate int
5667c478bd9Sstevel@tonic-gate _init(void)
5677c478bd9Sstevel@tonic-gate {
5687c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
5697c478bd9Sstevel@tonic-gate }
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate int
5727c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
5737c478bd9Sstevel@tonic-gate {
5747c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
5757c478bd9Sstevel@tonic-gate }
576