1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * vnode ops for the devfs
31*0Sstevel@tonic-gate  *
32*0Sstevel@tonic-gate  * For leaf vnode special files (VCHR|VBLK) specfs will always see the VOP
33*0Sstevel@tonic-gate  * first because dv_find always performs leaf vnode substitution, returning
34*0Sstevel@tonic-gate  * a specfs vnode with an s_realvp pointing to the devfs leaf vnode. This
35*0Sstevel@tonic-gate  * means that the only leaf special file VOP operations that devfs will see
36*0Sstevel@tonic-gate  * after VOP_LOOKUP are the ones that specfs forwards.
37*0Sstevel@tonic-gate  */
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate #include <sys/types.h>
40*0Sstevel@tonic-gate #include <sys/param.h>
41*0Sstevel@tonic-gate #include <sys/t_lock.h>
42*0Sstevel@tonic-gate #include <sys/systm.h>
43*0Sstevel@tonic-gate #include <sys/sysmacros.h>
44*0Sstevel@tonic-gate #include <sys/user.h>
45*0Sstevel@tonic-gate #include <sys/time.h>
46*0Sstevel@tonic-gate #include <sys/vfs.h>
47*0Sstevel@tonic-gate #include <sys/vnode.h>
48*0Sstevel@tonic-gate #include <sys/file.h>
49*0Sstevel@tonic-gate #include <sys/fcntl.h>
50*0Sstevel@tonic-gate #include <sys/flock.h>
51*0Sstevel@tonic-gate #include <sys/kmem.h>
52*0Sstevel@tonic-gate #include <sys/uio.h>
53*0Sstevel@tonic-gate #include <sys/errno.h>
54*0Sstevel@tonic-gate #include <sys/stat.h>
55*0Sstevel@tonic-gate #include <sys/cred.h>
56*0Sstevel@tonic-gate #include <sys/dirent.h>
57*0Sstevel@tonic-gate #include <sys/pathname.h>
58*0Sstevel@tonic-gate #include <sys/cmn_err.h>
59*0Sstevel@tonic-gate #include <sys/debug.h>
60*0Sstevel@tonic-gate #include <sys/policy.h>
61*0Sstevel@tonic-gate #include <sys/modctl.h>
62*0Sstevel@tonic-gate 
63*0Sstevel@tonic-gate #include <fs/fs_subr.h>
64*0Sstevel@tonic-gate #include <sys/fs/dv_node.h>
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate extern struct vattr	dv_vattr_dir, dv_vattr_file;
67*0Sstevel@tonic-gate extern dev_t rconsdev;
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate /*
70*0Sstevel@tonic-gate  * Open of devices (leaf nodes) is handled by specfs.
71*0Sstevel@tonic-gate  * There is nothing to do to open a directory
72*0Sstevel@tonic-gate  */
73*0Sstevel@tonic-gate /*ARGSUSED*/
74*0Sstevel@tonic-gate static int
75*0Sstevel@tonic-gate devfs_open(struct vnode **vpp, int flag, struct cred *cred)
76*0Sstevel@tonic-gate {
77*0Sstevel@tonic-gate 	struct dv_node	*dv = VTODV(*vpp);
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate 	dcmn_err2(("devfs_open %s\n", dv->dv_name));
80*0Sstevel@tonic-gate 	ASSERT((*vpp)->v_type == VDIR);
81*0Sstevel@tonic-gate 	return (0);
82*0Sstevel@tonic-gate }
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate /*
85*0Sstevel@tonic-gate  * Close of devices (leaf nodes) is handled by specfs.
86*0Sstevel@tonic-gate  * There is nothing much to do inorder to close a directory.
87*0Sstevel@tonic-gate  */
88*0Sstevel@tonic-gate /*ARGSUSED1*/
89*0Sstevel@tonic-gate static int
90*0Sstevel@tonic-gate devfs_close(struct vnode *vp, int flag, int count,
91*0Sstevel@tonic-gate     offset_t offset, struct cred *cred)
92*0Sstevel@tonic-gate {
93*0Sstevel@tonic-gate 	struct dv_node	*dv = VTODV(vp);
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate 	dcmn_err2(("devfs_close %s\n", dv->dv_name));
96*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR);
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate 	cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
99*0Sstevel@tonic-gate 	cleanshares(vp, ttoproc(curthread)->p_pid);
100*0Sstevel@tonic-gate 	return (0);
101*0Sstevel@tonic-gate }
102*0Sstevel@tonic-gate 
103*0Sstevel@tonic-gate /*
104*0Sstevel@tonic-gate  * Read of devices (leaf nodes) is handled by specfs.
105*0Sstevel@tonic-gate  * Read of directories is not supported.
106*0Sstevel@tonic-gate  */
107*0Sstevel@tonic-gate /*ARGSUSED*/
108*0Sstevel@tonic-gate static int
109*0Sstevel@tonic-gate devfs_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred,
110*0Sstevel@tonic-gate 	struct caller_context *ct)
111*0Sstevel@tonic-gate {
112*0Sstevel@tonic-gate 	dcmn_err2(("devfs_read %s\n", VTODV(vp)->dv_name));
113*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR);
114*0Sstevel@tonic-gate 	ASSERT(RW_READ_HELD(&VTODV(vp)->dv_contents));
115*0Sstevel@tonic-gate 	return (EISDIR);
116*0Sstevel@tonic-gate }
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate /*
119*0Sstevel@tonic-gate  * Write of devices (leaf nodes) is handled by specfs.
120*0Sstevel@tonic-gate  * Write of directories is not supported.
121*0Sstevel@tonic-gate  */
122*0Sstevel@tonic-gate /*ARGSUSED*/
123*0Sstevel@tonic-gate static int
124*0Sstevel@tonic-gate devfs_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred,
125*0Sstevel@tonic-gate 	struct caller_context *ct)
126*0Sstevel@tonic-gate {
127*0Sstevel@tonic-gate 	dcmn_err2(("devfs_write %s\n", VTODV(vp)->dv_name));
128*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR);
129*0Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&VTODV(vp)->dv_contents));
130*0Sstevel@tonic-gate 	return (EISDIR);
131*0Sstevel@tonic-gate }
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate /*
134*0Sstevel@tonic-gate  * Ioctls to device (leaf nodes) is handled by specfs.
135*0Sstevel@tonic-gate  * Ioctl to directories is not supported.
136*0Sstevel@tonic-gate  */
137*0Sstevel@tonic-gate /*ARGSUSED*/
138*0Sstevel@tonic-gate static int
139*0Sstevel@tonic-gate devfs_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag,
140*0Sstevel@tonic-gate     struct cred *cred, int *rvalp)
141*0Sstevel@tonic-gate {
142*0Sstevel@tonic-gate 	dcmn_err2(("devfs_ioctl %s\n", VTODV(vp)->dv_name));
143*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR);
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate 	return (ENOTTY);	/* no ioctls supported */
146*0Sstevel@tonic-gate }
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate /*
149*0Sstevel@tonic-gate  * We can be asked directly about the attributes of directories, or
150*0Sstevel@tonic-gate  * (via sp->s_realvp) about the filesystem attributes of special files.
151*0Sstevel@tonic-gate  *
152*0Sstevel@tonic-gate  * For directories, we just believe the attribute store
153*0Sstevel@tonic-gate  * though we mangle the nodeid, fsid, and rdev to convince userland we
154*0Sstevel@tonic-gate  * really are a different filesystem.
155*0Sstevel@tonic-gate  *
156*0Sstevel@tonic-gate  * For special files, a little more fakery is required.
157*0Sstevel@tonic-gate  *
158*0Sstevel@tonic-gate  * If the attribute store is not there (read only root), we believe our
159*0Sstevel@tonic-gate  * memory based attributes.
160*0Sstevel@tonic-gate  */
161*0Sstevel@tonic-gate static int
162*0Sstevel@tonic-gate devfs_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cr)
163*0Sstevel@tonic-gate {
164*0Sstevel@tonic-gate 	struct dv_node	*dv = VTODV(vp);
165*0Sstevel@tonic-gate 	int		error = 0;
166*0Sstevel@tonic-gate 	uint_t		mask;
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 	/*
169*0Sstevel@tonic-gate 	 * Message goes to console only. Otherwise, the message
170*0Sstevel@tonic-gate 	 * causes devfs_getattr to be invoked again... infinite loop
171*0Sstevel@tonic-gate 	 */
172*0Sstevel@tonic-gate 	dcmn_err2(("?devfs_getattr %s\n", dv->dv_name));
173*0Sstevel@tonic-gate 	ASSERT(dv->dv_attr || dv->dv_attrvp);
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	if (!(vp->v_type == VDIR || vp->v_type == VCHR || vp->v_type == VBLK)) {
176*0Sstevel@tonic-gate 		cmn_err(CE_WARN,	/* panic ? */
177*0Sstevel@tonic-gate 		    "?%s: getattr on vnode type %d", dvnm, vp->v_type);
178*0Sstevel@tonic-gate 		return (ENOENT);
179*0Sstevel@tonic-gate 	}
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 	if (dv->dv_attr) {
182*0Sstevel@tonic-gate 		/*
183*0Sstevel@tonic-gate 		 * obtain from the memory version of attribute.
184*0Sstevel@tonic-gate 		 * preserve mask for those that optimize.
185*0Sstevel@tonic-gate 		 * devfs specific fields are already merged on creation.
186*0Sstevel@tonic-gate 		 */
187*0Sstevel@tonic-gate 		mask = vap->va_mask;
188*0Sstevel@tonic-gate 		*vap = *dv->dv_attr;
189*0Sstevel@tonic-gate 		vap->va_mask = mask;
190*0Sstevel@tonic-gate 	} else {
191*0Sstevel@tonic-gate 		/* obtain from attribute store and merge */
192*0Sstevel@tonic-gate 		error = VOP_GETATTR(dv->dv_attrvp, vap, flags, cr);
193*0Sstevel@tonic-gate 		dsysdebug(error, ("vop_getattr %s %d\n", dv->dv_name, error));
194*0Sstevel@tonic-gate 		dv_vattr_merge(dv, vap);
195*0Sstevel@tonic-gate 	}
196*0Sstevel@tonic-gate 
197*0Sstevel@tonic-gate 	/*
198*0Sstevel@tonic-gate 	 * Restrict the permissions of the node fronting the console
199*0Sstevel@tonic-gate 	 * to 0600 with root as the owner.  This prevents a non-root
200*0Sstevel@tonic-gate 	 * user from gaining access to a serial terminal (like /dev/term/a)
201*0Sstevel@tonic-gate 	 * which is in reality serving as the console device (/dev/console).
202*0Sstevel@tonic-gate 	 */
203*0Sstevel@tonic-gate 	if (vp->v_rdev == rconsdev) {
204*0Sstevel@tonic-gate 		mode_t	rconsmask = S_IXUSR|S_IRWXG|S_IRWXO;
205*0Sstevel@tonic-gate 		vap->va_mode &= (~rconsmask);
206*0Sstevel@tonic-gate 		vap->va_uid = 0;
207*0Sstevel@tonic-gate 	}
208*0Sstevel@tonic-gate 
209*0Sstevel@tonic-gate 	return (error);
210*0Sstevel@tonic-gate }
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate static int devfs_unlocked_access(void *, int, struct cred *);
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate /*ARGSUSED4*/
215*0Sstevel@tonic-gate static int
216*0Sstevel@tonic-gate devfs_setattr_dir(
217*0Sstevel@tonic-gate 	struct dv_node *dv,
218*0Sstevel@tonic-gate 	struct vnode *vp,
219*0Sstevel@tonic-gate 	struct vattr *vap,
220*0Sstevel@tonic-gate 	int flags,
221*0Sstevel@tonic-gate 	struct cred *cr)
222*0Sstevel@tonic-gate {
223*0Sstevel@tonic-gate 	struct vattr	*map;
224*0Sstevel@tonic-gate 	long int	mask;
225*0Sstevel@tonic-gate 	int		error = 0;
226*0Sstevel@tonic-gate 	struct vattr	vattr;
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate 	ASSERT(dv->dv_attr || dv->dv_attrvp);
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR);
231*0Sstevel@tonic-gate 	ASSERT((dv->dv_flags & DV_NO_FSPERM) == 0);
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 	if (vap->va_mask & AT_NOSET)
234*0Sstevel@tonic-gate 		return (EINVAL);
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	/* to ensure consistency, single thread setting of attributes */
237*0Sstevel@tonic-gate 	rw_enter(&dv->dv_contents, RW_WRITER);
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate again:	if (dv->dv_attr) {
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 		error = secpolicy_vnode_setattr(cr, vp, vap, dv->dv_attr,
242*0Sstevel@tonic-gate 					flags, devfs_unlocked_access, dv);
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 		if (error)
245*0Sstevel@tonic-gate 			goto out;
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 		/*
248*0Sstevel@tonic-gate 		 * Apply changes to the memory based attribute. This code
249*0Sstevel@tonic-gate 		 * is modeled after the tmpfs implementation of memory
250*0Sstevel@tonic-gate 		 * based vnodes
251*0Sstevel@tonic-gate 		 */
252*0Sstevel@tonic-gate 		map = dv->dv_attr;
253*0Sstevel@tonic-gate 		mask = vap->va_mask;
254*0Sstevel@tonic-gate 
255*0Sstevel@tonic-gate 		/* Change file access modes. */
256*0Sstevel@tonic-gate 		if (mask & AT_MODE) {
257*0Sstevel@tonic-gate 			map->va_mode &= S_IFMT;
258*0Sstevel@tonic-gate 			map->va_mode |= vap->va_mode & ~S_IFMT;
259*0Sstevel@tonic-gate 		}
260*0Sstevel@tonic-gate 		if (mask & AT_UID)
261*0Sstevel@tonic-gate 			map->va_uid = vap->va_uid;
262*0Sstevel@tonic-gate 		if (mask & AT_GID)
263*0Sstevel@tonic-gate 			map->va_gid = vap->va_gid;
264*0Sstevel@tonic-gate 		if (mask & AT_ATIME)
265*0Sstevel@tonic-gate 			map->va_atime = vap->va_atime;
266*0Sstevel@tonic-gate 		if (mask & AT_MTIME)
267*0Sstevel@tonic-gate 			map->va_mtime = vap->va_mtime;
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 		if (mask & (AT_MODE | AT_UID | AT_GID | AT_MTIME))
270*0Sstevel@tonic-gate 			gethrestime(&map->va_ctime);
271*0Sstevel@tonic-gate 	} else {
272*0Sstevel@tonic-gate 		/* use the backing attribute store */
273*0Sstevel@tonic-gate 		ASSERT(dv->dv_attrvp);
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate 		/*
276*0Sstevel@tonic-gate 		 * See if we are changing something we care about
277*0Sstevel@tonic-gate 		 * the persistence of - return success if we don't care.
278*0Sstevel@tonic-gate 		 */
279*0Sstevel@tonic-gate 		if (vap->va_mask & (AT_MODE|AT_UID|AT_GID|AT_ATIME|AT_MTIME)) {
280*0Sstevel@tonic-gate 			/* Set the attributes */
281*0Sstevel@tonic-gate 			error = VOP_SETATTR(dv->dv_attrvp,
282*0Sstevel@tonic-gate 				vap, flags, cr, NULL);
283*0Sstevel@tonic-gate 			dsysdebug(error,
284*0Sstevel@tonic-gate 				("vop_setattr %s %d\n", dv->dv_name, error));
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 			/*
287*0Sstevel@tonic-gate 			 * Some file systems may return EROFS for a setattr
288*0Sstevel@tonic-gate 			 * on a readonly file system.  In this case we create
289*0Sstevel@tonic-gate 			 * our own memory based attribute.
290*0Sstevel@tonic-gate 			 */
291*0Sstevel@tonic-gate 			if (error == EROFS) {
292*0Sstevel@tonic-gate 				/*
293*0Sstevel@tonic-gate 				 * obtain attributes from existing file
294*0Sstevel@tonic-gate 				 * that we will modify and switch to memory
295*0Sstevel@tonic-gate 				 * based attribute until attribute store is
296*0Sstevel@tonic-gate 				 * read/write.
297*0Sstevel@tonic-gate 				 */
298*0Sstevel@tonic-gate 				vattr = dv_vattr_dir;
299*0Sstevel@tonic-gate 				if (VOP_GETATTR(dv->dv_attrvp, &vattr,
300*0Sstevel@tonic-gate 				    flags, cr) == 0) {
301*0Sstevel@tonic-gate 					dv->dv_attr = kmem_alloc(
302*0Sstevel@tonic-gate 					    sizeof (struct vattr), KM_SLEEP);
303*0Sstevel@tonic-gate 					*dv->dv_attr = vattr;
304*0Sstevel@tonic-gate 					dv_vattr_merge(dv, dv->dv_attr);
305*0Sstevel@tonic-gate 					goto again;
306*0Sstevel@tonic-gate 				}
307*0Sstevel@tonic-gate 			}
308*0Sstevel@tonic-gate 		}
309*0Sstevel@tonic-gate 	}
310*0Sstevel@tonic-gate out:
311*0Sstevel@tonic-gate 	rw_exit(&dv->dv_contents);
312*0Sstevel@tonic-gate 	return (error);
313*0Sstevel@tonic-gate }
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate 
316*0Sstevel@tonic-gate /*
317*0Sstevel@tonic-gate  * Compare the uid/gid/mode changes requested for a setattr
318*0Sstevel@tonic-gate  * operation with the same details of a node's default minor
319*0Sstevel@tonic-gate  * perm information.  Return 0 if identical.
320*0Sstevel@tonic-gate  */
321*0Sstevel@tonic-gate static int
322*0Sstevel@tonic-gate dv_setattr_cmp(struct vattr *map, mperm_t *mp)
323*0Sstevel@tonic-gate {
324*0Sstevel@tonic-gate 	if ((map->va_mode & S_IAMB) != (mp->mp_mode & S_IAMB))
325*0Sstevel@tonic-gate 		return (1);
326*0Sstevel@tonic-gate 	if (map->va_uid != mp->mp_uid)
327*0Sstevel@tonic-gate 		return (1);
328*0Sstevel@tonic-gate 	if (map->va_gid != mp->mp_gid)
329*0Sstevel@tonic-gate 		return (1);
330*0Sstevel@tonic-gate 	return (0);
331*0Sstevel@tonic-gate }
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate /*ARGSUSED4*/
335*0Sstevel@tonic-gate static int
336*0Sstevel@tonic-gate devfs_setattr(
337*0Sstevel@tonic-gate 	struct vnode *vp,
338*0Sstevel@tonic-gate 	struct vattr *vap,
339*0Sstevel@tonic-gate 	int flags,
340*0Sstevel@tonic-gate 	struct cred *cr,
341*0Sstevel@tonic-gate 	caller_context_t *ct)
342*0Sstevel@tonic-gate {
343*0Sstevel@tonic-gate 	struct dv_node	*dv = VTODV(vp);
344*0Sstevel@tonic-gate 	struct dv_node	*ddv;
345*0Sstevel@tonic-gate 	struct vnode	*dvp;
346*0Sstevel@tonic-gate 	struct vattr	*map;
347*0Sstevel@tonic-gate 	long int	mask;
348*0Sstevel@tonic-gate 	int		error = 0;
349*0Sstevel@tonic-gate 	struct vattr	*free_vattr = NULL;
350*0Sstevel@tonic-gate 	struct vattr	*vattrp = NULL;
351*0Sstevel@tonic-gate 	mperm_t		mp;
352*0Sstevel@tonic-gate 	int		persist;
353*0Sstevel@tonic-gate 
354*0Sstevel@tonic-gate 	/*
355*0Sstevel@tonic-gate 	 * Message goes to console only. Otherwise, the message
356*0Sstevel@tonic-gate 	 * causes devfs_getattr to be invoked again... infinite loop
357*0Sstevel@tonic-gate 	 */
358*0Sstevel@tonic-gate 	dcmn_err2(("?devfs_setattr %s\n", dv->dv_name));
359*0Sstevel@tonic-gate 	ASSERT(dv->dv_attr || dv->dv_attrvp);
360*0Sstevel@tonic-gate 
361*0Sstevel@tonic-gate 	if (!(vp->v_type == VDIR || vp->v_type == VCHR || vp->v_type == VBLK)) {
362*0Sstevel@tonic-gate 		cmn_err(CE_WARN,	/* panic ? */
363*0Sstevel@tonic-gate 		    "?%s: getattr on vnode type %d", dvnm, vp->v_type);
364*0Sstevel@tonic-gate 		return (ENOENT);
365*0Sstevel@tonic-gate 	}
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	if (vap->va_mask & AT_NOSET)
368*0Sstevel@tonic-gate 		return (EINVAL);
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 	/*
371*0Sstevel@tonic-gate 	 * If we are changing something we don't care about
372*0Sstevel@tonic-gate 	 * the persistence of, return success.
373*0Sstevel@tonic-gate 	 */
374*0Sstevel@tonic-gate 	if ((vap->va_mask &
375*0Sstevel@tonic-gate 	    (AT_MODE|AT_UID|AT_GID|AT_ATIME|AT_MTIME)) == 0)
376*0Sstevel@tonic-gate 		return (0);
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate 	/*
379*0Sstevel@tonic-gate 	 * If driver overrides fs perm, disallow chmod
380*0Sstevel@tonic-gate 	 * and do not create attribute nodes.
381*0Sstevel@tonic-gate 	 */
382*0Sstevel@tonic-gate 	if (dv->dv_flags & DV_NO_FSPERM) {
383*0Sstevel@tonic-gate 		ASSERT(dv->dv_attr);
384*0Sstevel@tonic-gate 		if (vap->va_mask & (AT_MODE | AT_UID | AT_GID))
385*0Sstevel@tonic-gate 			return (EPERM);
386*0Sstevel@tonic-gate 		if ((vap->va_mask & (AT_ATIME|AT_MTIME)) == 0)
387*0Sstevel@tonic-gate 			return (0);
388*0Sstevel@tonic-gate 		rw_enter(&dv->dv_contents, RW_WRITER);
389*0Sstevel@tonic-gate 		if (vap->va_mask & AT_ATIME)
390*0Sstevel@tonic-gate 			dv->dv_attr->va_atime = vap->va_atime;
391*0Sstevel@tonic-gate 		if (vap->va_mask & AT_MTIME)
392*0Sstevel@tonic-gate 			dv->dv_attr->va_mtime = vap->va_mtime;
393*0Sstevel@tonic-gate 		rw_exit(&dv->dv_contents);
394*0Sstevel@tonic-gate 		return (0);
395*0Sstevel@tonic-gate 	}
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 	/*
398*0Sstevel@tonic-gate 	 * Directories are always created but device nodes are
399*0Sstevel@tonic-gate 	 * only used to persist non-default permissions.
400*0Sstevel@tonic-gate 	 */
401*0Sstevel@tonic-gate 	if (vp->v_type == VDIR) {
402*0Sstevel@tonic-gate 		ASSERT(dv->dv_attr || dv->dv_attrvp);
403*0Sstevel@tonic-gate 		return (devfs_setattr_dir(dv, vp, vap, flags, cr));
404*0Sstevel@tonic-gate 	}
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 	/*
407*0Sstevel@tonic-gate 	 * Allocate now before we take any locks
408*0Sstevel@tonic-gate 	 */
409*0Sstevel@tonic-gate 	vattrp = kmem_zalloc(sizeof (*vattrp), KM_SLEEP);
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate 	/* to ensure consistency, single thread setting of attributes */
412*0Sstevel@tonic-gate 	rw_enter(&dv->dv_contents, RW_WRITER);
413*0Sstevel@tonic-gate 
414*0Sstevel@tonic-gate 	/*
415*0Sstevel@tonic-gate 	 * We don't need to create an attribute node
416*0Sstevel@tonic-gate 	 * to persist access or modification times.
417*0Sstevel@tonic-gate 	 */
418*0Sstevel@tonic-gate 	persist = (vap->va_mask & (AT_MODE | AT_UID | AT_GID));
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate 	/*
421*0Sstevel@tonic-gate 	 * If persisting something, get the default permissions
422*0Sstevel@tonic-gate 	 * for this minor to compare against what the attributes
423*0Sstevel@tonic-gate 	 * are now being set to.  Default ordering is:
424*0Sstevel@tonic-gate 	 *	- minor_perm match for this minor
425*0Sstevel@tonic-gate 	 *	- mode supplied by ddi_create_priv_minor_node
426*0Sstevel@tonic-gate 	 *	- devfs defaults
427*0Sstevel@tonic-gate 	 */
428*0Sstevel@tonic-gate 	if (persist) {
429*0Sstevel@tonic-gate 		if (dev_minorperm(dv->dv_devi, dv->dv_name, &mp) != 0) {
430*0Sstevel@tonic-gate 			mp.mp_uid = dv_vattr_file.va_uid;
431*0Sstevel@tonic-gate 			mp.mp_gid = dv_vattr_file.va_gid;
432*0Sstevel@tonic-gate 			mp.mp_mode = dv_vattr_file.va_mode;
433*0Sstevel@tonic-gate 			if (dv->dv_flags & DV_DFLT_MODE) {
434*0Sstevel@tonic-gate 				ASSERT((dv->dv_dflt_mode & ~S_IAMB) == 0);
435*0Sstevel@tonic-gate 				mp.mp_mode &= ~S_IAMB;
436*0Sstevel@tonic-gate 				mp.mp_mode |= dv->dv_dflt_mode;
437*0Sstevel@tonic-gate 				dcmn_err5(("%s: setattr priv default 0%o\n",
438*0Sstevel@tonic-gate 				    dv->dv_name, mp.mp_mode));
439*0Sstevel@tonic-gate 			} else {
440*0Sstevel@tonic-gate 				dcmn_err5(("%s: setattr devfs default 0%o\n",
441*0Sstevel@tonic-gate 				    dv->dv_name, mp.mp_mode));
442*0Sstevel@tonic-gate 			}
443*0Sstevel@tonic-gate 		} else {
444*0Sstevel@tonic-gate 			dcmn_err5(("%s: setattr minor perm default 0%o\n",
445*0Sstevel@tonic-gate 			    dv->dv_name, mp.mp_mode));
446*0Sstevel@tonic-gate 		}
447*0Sstevel@tonic-gate 	}
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 	/*
450*0Sstevel@tonic-gate 	 * If we don't have a vattr for this node, construct one.
451*0Sstevel@tonic-gate 	 */
452*0Sstevel@tonic-gate 	if (dv->dv_attr) {
453*0Sstevel@tonic-gate 		free_vattr = vattrp;
454*0Sstevel@tonic-gate 		vattrp = NULL;
455*0Sstevel@tonic-gate 	} else {
456*0Sstevel@tonic-gate 		ASSERT(dv->dv_attrvp);
457*0Sstevel@tonic-gate 		ASSERT(vp->v_type != VDIR);
458*0Sstevel@tonic-gate 		*vattrp = dv_vattr_file;
459*0Sstevel@tonic-gate 		error = VOP_GETATTR(dv->dv_attrvp, vattrp, 0, cr);
460*0Sstevel@tonic-gate 		dsysdebug(error, ("vop_getattr %s %d\n",
461*0Sstevel@tonic-gate 			dv->dv_name, error));
462*0Sstevel@tonic-gate 		if (error)
463*0Sstevel@tonic-gate 			goto out;
464*0Sstevel@tonic-gate 		dv->dv_attr = vattrp;
465*0Sstevel@tonic-gate 		dv_vattr_merge(dv, dv->dv_attr);
466*0Sstevel@tonic-gate 		vattrp = NULL;
467*0Sstevel@tonic-gate 	}
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate 	error = secpolicy_vnode_setattr(cr, vp, vap, dv->dv_attr,
470*0Sstevel@tonic-gate 					flags, devfs_unlocked_access, dv);
471*0Sstevel@tonic-gate 	if (error) {
472*0Sstevel@tonic-gate 		dsysdebug(error, ("devfs_setattr %s secpolicy error %d\n",
473*0Sstevel@tonic-gate 			dv->dv_name, error));
474*0Sstevel@tonic-gate 		goto out;
475*0Sstevel@tonic-gate 	}
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 	/*
478*0Sstevel@tonic-gate 	 * Apply changes to the memory based attribute. This code
479*0Sstevel@tonic-gate 	 * is modeled after the tmpfs implementation of memory
480*0Sstevel@tonic-gate 	 * based vnodes
481*0Sstevel@tonic-gate 	 */
482*0Sstevel@tonic-gate 	map = dv->dv_attr;
483*0Sstevel@tonic-gate 	mask = vap->va_mask;
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 	/* Change file access modes. */
486*0Sstevel@tonic-gate 	if (mask & AT_MODE) {
487*0Sstevel@tonic-gate 		map->va_mode &= S_IFMT;
488*0Sstevel@tonic-gate 		map->va_mode |= vap->va_mode & ~S_IFMT;
489*0Sstevel@tonic-gate 	}
490*0Sstevel@tonic-gate 	if (mask & AT_UID)
491*0Sstevel@tonic-gate 		map->va_uid = vap->va_uid;
492*0Sstevel@tonic-gate 	if (mask & AT_GID)
493*0Sstevel@tonic-gate 		map->va_gid = vap->va_gid;
494*0Sstevel@tonic-gate 	if (mask & AT_ATIME)
495*0Sstevel@tonic-gate 		map->va_atime = vap->va_atime;
496*0Sstevel@tonic-gate 	if (mask & AT_MTIME)
497*0Sstevel@tonic-gate 		map->va_mtime = vap->va_mtime;
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 	if (mask & (AT_MODE | AT_UID | AT_GID | AT_MTIME)) {
500*0Sstevel@tonic-gate 		gethrestime(&map->va_ctime);
501*0Sstevel@tonic-gate 	}
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate 	/*
504*0Sstevel@tonic-gate 	 * A setattr to defaults means we no longer need the
505*0Sstevel@tonic-gate 	 * shadow node as a persistent store, unless there
506*0Sstevel@tonic-gate 	 * are ACLs.  Otherwise create a shadow node if one
507*0Sstevel@tonic-gate 	 * doesn't exist yet.
508*0Sstevel@tonic-gate 	 */
509*0Sstevel@tonic-gate 	if (persist) {
510*0Sstevel@tonic-gate 		if ((dv_setattr_cmp(map, &mp) == 0) &&
511*0Sstevel@tonic-gate 		    ((dv->dv_flags & DV_ACL) == 0)) {
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 			if (dv->dv_attrvp) {
514*0Sstevel@tonic-gate 				ddv = dv->dv_dotdot;
515*0Sstevel@tonic-gate 				ASSERT(ddv->dv_attrvp);
516*0Sstevel@tonic-gate 				error = VOP_REMOVE(ddv->dv_attrvp,
517*0Sstevel@tonic-gate 				    dv->dv_name, cr);
518*0Sstevel@tonic-gate 				dsysdebug(error,
519*0Sstevel@tonic-gate 				    ("vop_remove %s %s %d\n",
520*0Sstevel@tonic-gate 				    ddv->dv_name, dv->dv_name, error));
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate 				if (error == EROFS)
523*0Sstevel@tonic-gate 					error = 0;
524*0Sstevel@tonic-gate 				VN_RELE(dv->dv_attrvp);
525*0Sstevel@tonic-gate 				dv->dv_attrvp = NULL;
526*0Sstevel@tonic-gate 			}
527*0Sstevel@tonic-gate 			ASSERT(dv->dv_attr);
528*0Sstevel@tonic-gate 		} else {
529*0Sstevel@tonic-gate 			if (mask & AT_MODE)
530*0Sstevel@tonic-gate 				dcmn_err5(("%s persisting mode 0%o\n",
531*0Sstevel@tonic-gate 					dv->dv_name, vap->va_mode));
532*0Sstevel@tonic-gate 			if (mask & AT_UID)
533*0Sstevel@tonic-gate 				dcmn_err5(("%s persisting uid %d\n",
534*0Sstevel@tonic-gate 					dv->dv_name, vap->va_uid));
535*0Sstevel@tonic-gate 			if (mask & AT_GID)
536*0Sstevel@tonic-gate 				dcmn_err5(("%s persisting gid %d\n",
537*0Sstevel@tonic-gate 					dv->dv_name, vap->va_gid));
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate 			if (dv->dv_attrvp == NULL) {
540*0Sstevel@tonic-gate 				dvp = DVTOV(dv->dv_dotdot);
541*0Sstevel@tonic-gate 				dv_shadow_node(dvp, dv->dv_name, vp,
542*0Sstevel@tonic-gate 				    NULL, NULLVP, cr,
543*0Sstevel@tonic-gate 				    DV_SHADOW_CREATE | DV_SHADOW_WRITE_HELD);
544*0Sstevel@tonic-gate 			}
545*0Sstevel@tonic-gate 			if (dv->dv_attrvp) {
546*0Sstevel@tonic-gate 				error = VOP_SETATTR(dv->dv_attrvp,
547*0Sstevel@tonic-gate 				    vap, flags, cr, NULL);
548*0Sstevel@tonic-gate 				dsysdebug(error, ("vop_setattr %s %d\n",
549*0Sstevel@tonic-gate 				    dv->dv_name, error));
550*0Sstevel@tonic-gate 			}
551*0Sstevel@tonic-gate 			/*
552*0Sstevel@tonic-gate 			 * Some file systems may return EROFS for a setattr
553*0Sstevel@tonic-gate 			 * on a readonly file system.  In this case save
554*0Sstevel@tonic-gate 			 * as our own memory based attribute.
555*0Sstevel@tonic-gate 			 * NOTE: ufs is NOT one of these (see ufs_iupdat).
556*0Sstevel@tonic-gate 			 */
557*0Sstevel@tonic-gate 			if (dv->dv_attr && dv->dv_attrvp && error == 0) {
558*0Sstevel@tonic-gate 				vattrp = dv->dv_attr;
559*0Sstevel@tonic-gate 				dv->dv_attr = NULL;
560*0Sstevel@tonic-gate 			} else if (error == EROFS)
561*0Sstevel@tonic-gate 				error = 0;
562*0Sstevel@tonic-gate 		}
563*0Sstevel@tonic-gate 	}
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate out:
566*0Sstevel@tonic-gate 	rw_exit(&dv->dv_contents);
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 	if (vattrp)
569*0Sstevel@tonic-gate 		kmem_free(vattrp, sizeof (*vattrp));
570*0Sstevel@tonic-gate 	if (free_vattr)
571*0Sstevel@tonic-gate 		kmem_free(free_vattr, sizeof (*free_vattr));
572*0Sstevel@tonic-gate 	return (error);
573*0Sstevel@tonic-gate }
574*0Sstevel@tonic-gate 
575*0Sstevel@tonic-gate static int
576*0Sstevel@tonic-gate devfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr)
577*0Sstevel@tonic-gate {
578*0Sstevel@tonic-gate 	switch (cmd) {
579*0Sstevel@tonic-gate 	case _PC_ACL_ENABLED:
580*0Sstevel@tonic-gate 		/*
581*0Sstevel@tonic-gate 		 * We rely on the underlying filesystem for ACLs,
582*0Sstevel@tonic-gate 		 * so direct the query for ACL support there.
583*0Sstevel@tonic-gate 		 * ACL support isn't relative to the file
584*0Sstevel@tonic-gate 		 * and we can't guarantee that the dv node
585*0Sstevel@tonic-gate 		 * has an attribute node, so any valid
586*0Sstevel@tonic-gate 		 * attribute node will suffice.
587*0Sstevel@tonic-gate 		 */
588*0Sstevel@tonic-gate 		ASSERT(dvroot);
589*0Sstevel@tonic-gate 		ASSERT(dvroot->dv_attrvp);
590*0Sstevel@tonic-gate 		return (VOP_PATHCONF(dvroot->dv_attrvp, cmd, valp, cr));
591*0Sstevel@tonic-gate 		/*NOTREACHED*/
592*0Sstevel@tonic-gate 	}
593*0Sstevel@tonic-gate 
594*0Sstevel@tonic-gate 	return (fs_pathconf(vp, cmd, valp, cr));
595*0Sstevel@tonic-gate }
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate /*
598*0Sstevel@tonic-gate  * Let avp handle security attributes (acl's).
599*0Sstevel@tonic-gate  */
600*0Sstevel@tonic-gate static int
601*0Sstevel@tonic-gate devfs_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags,
602*0Sstevel@tonic-gate     struct cred *cr)
603*0Sstevel@tonic-gate {
604*0Sstevel@tonic-gate 	dvnode_t *dv = VTODV(vp);
605*0Sstevel@tonic-gate 	struct vnode *avp;
606*0Sstevel@tonic-gate 	int	error;
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 	dcmn_err2(("devfs_getsecattr %s\n", dv->dv_name));
609*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR || vp->v_type == VCHR || vp->v_type == VBLK);
610*0Sstevel@tonic-gate 
611*0Sstevel@tonic-gate 	rw_enter(&dv->dv_contents, RW_READER);
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 	avp = dv->dv_attrvp;
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	/* fabricate the acl */
616*0Sstevel@tonic-gate 	if (avp == NULL) {
617*0Sstevel@tonic-gate 		error = fs_fab_acl(vp, vsap, flags, cr);
618*0Sstevel@tonic-gate 		rw_exit(&dv->dv_contents);
619*0Sstevel@tonic-gate 		return (error);
620*0Sstevel@tonic-gate 	}
621*0Sstevel@tonic-gate 
622*0Sstevel@tonic-gate 	error = VOP_GETSECATTR(avp, vsap, flags, cr);
623*0Sstevel@tonic-gate 	dsysdebug(error, ("vop_getsecattr %s %d\n", VTODV(vp)->dv_name, error));
624*0Sstevel@tonic-gate 
625*0Sstevel@tonic-gate 	rw_exit(&dv->dv_contents);
626*0Sstevel@tonic-gate 	return (error);
627*0Sstevel@tonic-gate }
628*0Sstevel@tonic-gate 
629*0Sstevel@tonic-gate /*
630*0Sstevel@tonic-gate  * Set security attributes (acl's)
631*0Sstevel@tonic-gate  *
632*0Sstevel@tonic-gate  * Note that the dv_contents lock has already been acquired
633*0Sstevel@tonic-gate  * by the caller's VOP_RWLOCK.
634*0Sstevel@tonic-gate  */
635*0Sstevel@tonic-gate static int
636*0Sstevel@tonic-gate devfs_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags,
637*0Sstevel@tonic-gate     struct cred *cr)
638*0Sstevel@tonic-gate {
639*0Sstevel@tonic-gate 	dvnode_t *dv = VTODV(vp);
640*0Sstevel@tonic-gate 	struct vnode *avp;
641*0Sstevel@tonic-gate 	int	error;
642*0Sstevel@tonic-gate 
643*0Sstevel@tonic-gate 	dcmn_err2(("devfs_setsecattr %s\n", dv->dv_name));
644*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR || vp->v_type == VCHR || vp->v_type == VBLK);
645*0Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&dv->dv_contents));
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate 	/*
648*0Sstevel@tonic-gate 	 * Not a supported operation on drivers not providing
649*0Sstevel@tonic-gate 	 * file system based permissions.
650*0Sstevel@tonic-gate 	 */
651*0Sstevel@tonic-gate 	if (dv->dv_flags & DV_NO_FSPERM)
652*0Sstevel@tonic-gate 		return (ENOTSUP);
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 	/*
655*0Sstevel@tonic-gate 	 * To complete, the setsecattr requires an underlying attribute node.
656*0Sstevel@tonic-gate 	 */
657*0Sstevel@tonic-gate 	if (dv->dv_attrvp == NULL) {
658*0Sstevel@tonic-gate 		ASSERT(vp->v_type == VCHR || vp->v_type == VBLK);
659*0Sstevel@tonic-gate 		dv_shadow_node(DVTOV(dv->dv_dotdot), dv->dv_name, vp,
660*0Sstevel@tonic-gate 		    NULL, NULLVP, cr, DV_SHADOW_CREATE | DV_SHADOW_WRITE_HELD);
661*0Sstevel@tonic-gate 	}
662*0Sstevel@tonic-gate 
663*0Sstevel@tonic-gate 	if ((avp = dv->dv_attrvp) == NULL) {
664*0Sstevel@tonic-gate 		dcmn_err2(("devfs_setsecattr %s: "
665*0Sstevel@tonic-gate 		    "cannot construct attribute node\n", dv->dv_name));
666*0Sstevel@tonic-gate 		return (fs_nosys());
667*0Sstevel@tonic-gate 	}
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 	/*
670*0Sstevel@tonic-gate 	 * The acl(2) system call issues a VOP_RWLOCK before setting an ACL.
671*0Sstevel@tonic-gate 	 * Since backing file systems expect the lock to be held before seeing
672*0Sstevel@tonic-gate 	 * a VOP_SETSECATTR ACL, we need to issue the VOP_RWLOCK to the backing
673*0Sstevel@tonic-gate 	 * store before forwarding the ACL.
674*0Sstevel@tonic-gate 	 */
675*0Sstevel@tonic-gate 	(void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, NULL);
676*0Sstevel@tonic-gate 	error = VOP_SETSECATTR(avp, vsap, flags, cr);
677*0Sstevel@tonic-gate 	dsysdebug(error, ("vop_setsecattr %s %d\n", VTODV(vp)->dv_name, error));
678*0Sstevel@tonic-gate 	VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, NULL);
679*0Sstevel@tonic-gate 
680*0Sstevel@tonic-gate 	/*
681*0Sstevel@tonic-gate 	 * NB: This code should call fs_acl_nontrivial when available so that
682*0Sstevel@tonic-gate 	 * DV_ACL is only set on nontrivial ACLs.
683*0Sstevel@tonic-gate 	 */
684*0Sstevel@tonic-gate 	if (error == 0)
685*0Sstevel@tonic-gate 		dv->dv_flags |= DV_ACL;
686*0Sstevel@tonic-gate 	return (error);
687*0Sstevel@tonic-gate }
688*0Sstevel@tonic-gate 
689*0Sstevel@tonic-gate /*
690*0Sstevel@tonic-gate  * This function is used for secpolicy_setattr().  It must call an
691*0Sstevel@tonic-gate  * access() like function while it is already holding the
692*0Sstevel@tonic-gate  * dv_contents lock.  We only care about this when dv_attr != NULL;
693*0Sstevel@tonic-gate  * so the unlocked access call only concerns itself with that
694*0Sstevel@tonic-gate  * particular branch of devfs_access().
695*0Sstevel@tonic-gate  */
696*0Sstevel@tonic-gate static int
697*0Sstevel@tonic-gate devfs_unlocked_access(void *vdv, int mode, struct cred *cr)
698*0Sstevel@tonic-gate {
699*0Sstevel@tonic-gate 	struct dv_node *dv = vdv;
700*0Sstevel@tonic-gate 	int shift = 0;
701*0Sstevel@tonic-gate 	uid_t owner = dv->dv_attr->va_uid;
702*0Sstevel@tonic-gate 
703*0Sstevel@tonic-gate 	/* Check access based on owner, group and public permissions. */
704*0Sstevel@tonic-gate 	if (crgetuid(cr) != owner) {
705*0Sstevel@tonic-gate 		shift += 3;
706*0Sstevel@tonic-gate 		if (groupmember(dv->dv_attr->va_gid, cr) == 0)
707*0Sstevel@tonic-gate 			shift += 3;
708*0Sstevel@tonic-gate 	}
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 	/* compute missing mode bits */
711*0Sstevel@tonic-gate 	mode &= ~(dv->dv_attr->va_mode << shift);
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate 	if (mode == 0)
714*0Sstevel@tonic-gate 		return (0);
715*0Sstevel@tonic-gate 
716*0Sstevel@tonic-gate 	return (secpolicy_vnode_access(cr, DVTOV(dv), owner, mode));
717*0Sstevel@tonic-gate }
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate static int
720*0Sstevel@tonic-gate devfs_access(struct vnode *vp, int mode, int flags, struct cred *cr)
721*0Sstevel@tonic-gate {
722*0Sstevel@tonic-gate 	struct dv_node	*dv = VTODV(vp);
723*0Sstevel@tonic-gate 	int		res;
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate 	dcmn_err2(("devfs_access %s\n", dv->dv_name));
726*0Sstevel@tonic-gate 	ASSERT(dv->dv_attr || dv->dv_attrvp);
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 	/* restrict console access to privileged processes */
729*0Sstevel@tonic-gate 	if ((vp->v_rdev == rconsdev) && secpolicy_console(cr) != 0) {
730*0Sstevel@tonic-gate 		return (EACCES);
731*0Sstevel@tonic-gate 	}
732*0Sstevel@tonic-gate 
733*0Sstevel@tonic-gate 	if (dv->dv_attr && ((dv->dv_flags & DV_ACL) == 0)) {
734*0Sstevel@tonic-gate 		rw_enter(&dv->dv_contents, RW_READER);
735*0Sstevel@tonic-gate 		if (dv->dv_attr) {
736*0Sstevel@tonic-gate 			res = devfs_unlocked_access(dv, mode, cr);
737*0Sstevel@tonic-gate 			rw_exit(&dv->dv_contents);
738*0Sstevel@tonic-gate 			return (res);
739*0Sstevel@tonic-gate 		}
740*0Sstevel@tonic-gate 		rw_exit(&dv->dv_contents);
741*0Sstevel@tonic-gate 	}
742*0Sstevel@tonic-gate 	return (VOP_ACCESS(dv->dv_attrvp, mode, flags, cr));
743*0Sstevel@tonic-gate }
744*0Sstevel@tonic-gate 
745*0Sstevel@tonic-gate /*
746*0Sstevel@tonic-gate  * Lookup
747*0Sstevel@tonic-gate  *
748*0Sstevel@tonic-gate  * Given the directory vnode and the name of the component, return
749*0Sstevel@tonic-gate  * the corresponding held vnode for that component.
750*0Sstevel@tonic-gate  *
751*0Sstevel@tonic-gate  * Of course in these fictional filesystems, nothing's ever quite
752*0Sstevel@tonic-gate  * -that- simple.
753*0Sstevel@tonic-gate  *
754*0Sstevel@tonic-gate  * devfs name	type		shadow (fs attributes)	type	comments
755*0Sstevel@tonic-gate  * -------------------------------------------------------------------------
756*0Sstevel@tonic-gate  * drv[@addr]	VDIR		drv[@addr]		VDIR	nexus driver
757*0Sstevel@tonic-gate  * drv[@addr]:m	VCHR/VBLK	drv[@addr]:m		VREG	leaf driver
758*0Sstevel@tonic-gate  * drv[@addr]	VCHR/VBLK	drv[@addr]:.default	VREG	leaf driver
759*0Sstevel@tonic-gate  * -------------------------------------------------------------------------
760*0Sstevel@tonic-gate  *
761*0Sstevel@tonic-gate  * The following names are reserved for the attribute filesystem (which
762*0Sstevel@tonic-gate  * could easily be another layer on top of this one - we simply need to
763*0Sstevel@tonic-gate  * hold the vnode of the thing we're looking at)
764*0Sstevel@tonic-gate  *
765*0Sstevel@tonic-gate  * attr name	type		shadow (fs attributes)	type	comments
766*0Sstevel@tonic-gate  * -------------------------------------------------------------------------
767*0Sstevel@tonic-gate  * drv[@addr]	VDIR		-			-	attribute dir
768*0Sstevel@tonic-gate  * minorname	VDIR		-			-	minorname
769*0Sstevel@tonic-gate  * attribute	VREG		-			-	attribute
770*0Sstevel@tonic-gate  * -------------------------------------------------------------------------
771*0Sstevel@tonic-gate  *
772*0Sstevel@tonic-gate  * Examples:
773*0Sstevel@tonic-gate  *
774*0Sstevel@tonic-gate  *	devfs:/devices/.../mm@0:zero		VCHR
775*0Sstevel@tonic-gate  *	shadow:/.devices/.../mm@0:zero		VREG, fs attrs
776*0Sstevel@tonic-gate  *	devfs:/devices/.../mm@0:/zero/attr	VREG, driver attribute
777*0Sstevel@tonic-gate  *
778*0Sstevel@tonic-gate  *	devfs:/devices/.../sd@0,0:a		VBLK
779*0Sstevel@tonic-gate  *	shadow:/.devices/.../sd@0,0:a		VREG, fs attrs
780*0Sstevel@tonic-gate  *	devfs:/devices/.../sd@0,0:/a/.type	VREG, "ddi_block:chan"
781*0Sstevel@tonic-gate  *
782*0Sstevel@tonic-gate  *	devfs:/devices/.../mm@0			VCHR
783*0Sstevel@tonic-gate  *	shadow:/.devices/.../mm@0:.default	VREG, fs attrs
784*0Sstevel@tonic-gate  *	devfs:/devices/.../mm@0:/.default/attr	VREG, driver attribute
785*0Sstevel@tonic-gate  *	devfs:/devices/.../mm@0:/.default/.type	VREG, "ddi_pseudo"
786*0Sstevel@tonic-gate  *
787*0Sstevel@tonic-gate  *	devfs:/devices/.../obio			VDIR
788*0Sstevel@tonic-gate  *	shadow:/devices/.../obio		VDIR, needed for fs attrs.
789*0Sstevel@tonic-gate  *	devfs:/devices/.../obio:/.default/attr	VDIR, driver attribute
790*0Sstevel@tonic-gate  *
791*0Sstevel@tonic-gate  * We also need to be able deal with "old" devices that have gone away,
792*0Sstevel@tonic-gate  * though I think that provided we return them with readdir, they can
793*0Sstevel@tonic-gate  * be removed (i.e. they don't have to respond to lookup, though it might
794*0Sstevel@tonic-gate  * be weird if they didn't ;-)
795*0Sstevel@tonic-gate  *
796*0Sstevel@tonic-gate  * Lookup has side-effects.
797*0Sstevel@tonic-gate  *
798*0Sstevel@tonic-gate  * - It will create directories and fs attribute files in the shadow hierarchy.
799*0Sstevel@tonic-gate  * - It should cause non-SID devices to be probed (ask the parent nexi).
800*0Sstevel@tonic-gate  */
801*0Sstevel@tonic-gate /*ARGSUSED3*/
802*0Sstevel@tonic-gate static int
803*0Sstevel@tonic-gate devfs_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
804*0Sstevel@tonic-gate     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred)
805*0Sstevel@tonic-gate {
806*0Sstevel@tonic-gate 	ASSERT(dvp->v_type == VDIR);
807*0Sstevel@tonic-gate 	dcmn_err2(("devfs_lookup: %s\n", nm));
808*0Sstevel@tonic-gate 	return (dv_find(VTODV(dvp), nm, vpp, pnp, rdir, cred, 0));
809*0Sstevel@tonic-gate }
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate /*
812*0Sstevel@tonic-gate  * devfs nodes can't really be created directly by userland - however,
813*0Sstevel@tonic-gate  * we do allow creates to find existing nodes:
814*0Sstevel@tonic-gate  *
815*0Sstevel@tonic-gate  * - any create fails if the node doesn't exist - EROFS.
816*0Sstevel@tonic-gate  * - creating an existing directory read-only succeeds, otherwise EISDIR.
817*0Sstevel@tonic-gate  * - exclusive creates fail if the node already exists - EEXIST.
818*0Sstevel@tonic-gate  * - failure to create the snode for an existing device - ENOSYS.
819*0Sstevel@tonic-gate  */
820*0Sstevel@tonic-gate /*ARGSUSED2*/
821*0Sstevel@tonic-gate static int
822*0Sstevel@tonic-gate devfs_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
823*0Sstevel@tonic-gate     int mode, struct vnode **vpp, struct cred *cred, int flag)
824*0Sstevel@tonic-gate {
825*0Sstevel@tonic-gate 	int error;
826*0Sstevel@tonic-gate 	struct vnode *vp;
827*0Sstevel@tonic-gate 
828*0Sstevel@tonic-gate 	dcmn_err2(("devfs_create %s\n", nm));
829*0Sstevel@tonic-gate 	error = dv_find(VTODV(dvp), nm, &vp, NULL, NULLVP, cred, 0);
830*0Sstevel@tonic-gate 	if (error == 0) {
831*0Sstevel@tonic-gate 		if (excl == EXCL)
832*0Sstevel@tonic-gate 			error = EEXIST;
833*0Sstevel@tonic-gate 		else if (vp->v_type == VDIR && (mode & VWRITE))
834*0Sstevel@tonic-gate 			error = EISDIR;
835*0Sstevel@tonic-gate 		else
836*0Sstevel@tonic-gate 			error = VOP_ACCESS(vp, mode, 0, cred);
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 		if (error) {
839*0Sstevel@tonic-gate 			VN_RELE(vp);
840*0Sstevel@tonic-gate 		} else
841*0Sstevel@tonic-gate 			*vpp = vp;
842*0Sstevel@tonic-gate 	} else if (error == ENOENT)
843*0Sstevel@tonic-gate 		error = EROFS;
844*0Sstevel@tonic-gate 
845*0Sstevel@tonic-gate 	return (error);
846*0Sstevel@tonic-gate }
847*0Sstevel@tonic-gate 
848*0Sstevel@tonic-gate /*
849*0Sstevel@tonic-gate  * If DV_BUILD is set, we call into nexus driver to do a BUS_CONFIG_ALL.
850*0Sstevel@tonic-gate  * Otherwise, simply return cached dv_node's. Hotplug code always call
851*0Sstevel@tonic-gate  * devfs_clean() to invalid the dv_node cache.
852*0Sstevel@tonic-gate  */
853*0Sstevel@tonic-gate static int
854*0Sstevel@tonic-gate devfs_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp)
855*0Sstevel@tonic-gate {
856*0Sstevel@tonic-gate 	struct dv_node *ddv, *dv;
857*0Sstevel@tonic-gate 	struct dirent64 *de, *bufp;
858*0Sstevel@tonic-gate 	offset_t diroff;
859*0Sstevel@tonic-gate 	offset_t	soff;
860*0Sstevel@tonic-gate 	size_t reclen, movesz;
861*0Sstevel@tonic-gate 	int error;
862*0Sstevel@tonic-gate 	struct vattr va;
863*0Sstevel@tonic-gate 	size_t bufsz;
864*0Sstevel@tonic-gate 
865*0Sstevel@tonic-gate 	ddv = VTODV(dvp);
866*0Sstevel@tonic-gate 	dcmn_err2(("devfs_readdir %s: offset %lld len %ld\n",
867*0Sstevel@tonic-gate 	    ddv->dv_name, uiop->uio_loffset, uiop->uio_iov->iov_len));
868*0Sstevel@tonic-gate 	ASSERT(ddv->dv_attr || ddv->dv_attrvp);
869*0Sstevel@tonic-gate 	ASSERT(RW_READ_HELD(&ddv->dv_contents));
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate 	if (uiop->uio_loffset >= MAXOFF_T) {
872*0Sstevel@tonic-gate 		if (eofp)
873*0Sstevel@tonic-gate 			*eofp = 1;
874*0Sstevel@tonic-gate 		return (0);
875*0Sstevel@tonic-gate 	}
876*0Sstevel@tonic-gate 
877*0Sstevel@tonic-gate 	if (uiop->uio_iovcnt != 1)
878*0Sstevel@tonic-gate 		return (EINVAL);
879*0Sstevel@tonic-gate 
880*0Sstevel@tonic-gate 	if (dvp->v_type != VDIR)
881*0Sstevel@tonic-gate 		return (ENOTDIR);
882*0Sstevel@tonic-gate 
883*0Sstevel@tonic-gate 	/* Load the initial contents */
884*0Sstevel@tonic-gate 	if (ddv->dv_flags & DV_BUILD) {
885*0Sstevel@tonic-gate 		if (!rw_tryupgrade(&ddv->dv_contents)) {
886*0Sstevel@tonic-gate 			rw_exit(&ddv->dv_contents);
887*0Sstevel@tonic-gate 			rw_enter(&ddv->dv_contents, RW_WRITER);
888*0Sstevel@tonic-gate 		}
889*0Sstevel@tonic-gate 
890*0Sstevel@tonic-gate 		/* recheck and fill */
891*0Sstevel@tonic-gate 		if (ddv->dv_flags & DV_BUILD)
892*0Sstevel@tonic-gate 			dv_filldir(ddv);
893*0Sstevel@tonic-gate 
894*0Sstevel@tonic-gate 		rw_downgrade(&ddv->dv_contents);
895*0Sstevel@tonic-gate 	}
896*0Sstevel@tonic-gate 
897*0Sstevel@tonic-gate 	soff = uiop->uio_offset;
898*0Sstevel@tonic-gate 	bufsz = uiop->uio_iov->iov_len;
899*0Sstevel@tonic-gate 	de = bufp = kmem_alloc(bufsz, KM_SLEEP);
900*0Sstevel@tonic-gate 	movesz = 0;
901*0Sstevel@tonic-gate 	dv = (struct dv_node *)-1;
902*0Sstevel@tonic-gate 
903*0Sstevel@tonic-gate 	/*
904*0Sstevel@tonic-gate 	 * Move as many entries into the uio structure as it will take.
905*0Sstevel@tonic-gate 	 * Special case "." and "..".
906*0Sstevel@tonic-gate 	 */
907*0Sstevel@tonic-gate 	diroff = 0;
908*0Sstevel@tonic-gate 	if (soff == 0) {				/* . */
909*0Sstevel@tonic-gate 		reclen = DIRENT64_RECLEN(strlen("."));
910*0Sstevel@tonic-gate 		if ((movesz + reclen) > bufsz)
911*0Sstevel@tonic-gate 			goto full;
912*0Sstevel@tonic-gate 		de->d_ino = (ino64_t)ddv->dv_ino;
913*0Sstevel@tonic-gate 		de->d_off = (off64_t)diroff + 1;
914*0Sstevel@tonic-gate 		de->d_reclen = (ushort_t)reclen;
915*0Sstevel@tonic-gate 
916*0Sstevel@tonic-gate 		/* use strncpy(9f) to zero out uninitialized bytes */
917*0Sstevel@tonic-gate 
918*0Sstevel@tonic-gate 		(void) strncpy(de->d_name, ".", DIRENT64_NAMELEN(reclen));
919*0Sstevel@tonic-gate 		movesz += reclen;
920*0Sstevel@tonic-gate 		de = (dirent64_t *)((char *)de + reclen);
921*0Sstevel@tonic-gate 		dcmn_err3(("devfs_readdir: A: diroff %lld, soff %lld: '%s' "
922*0Sstevel@tonic-gate 		    "reclen %lu\n", diroff, soff, ".", reclen));
923*0Sstevel@tonic-gate 	}
924*0Sstevel@tonic-gate 
925*0Sstevel@tonic-gate 	diroff++;
926*0Sstevel@tonic-gate 	if (soff <= 1) {				/* .. */
927*0Sstevel@tonic-gate 		reclen = DIRENT64_RECLEN(strlen(".."));
928*0Sstevel@tonic-gate 		if ((movesz + reclen) > bufsz)
929*0Sstevel@tonic-gate 			goto full;
930*0Sstevel@tonic-gate 		de->d_ino = (ino64_t)ddv->dv_dotdot->dv_ino;
931*0Sstevel@tonic-gate 		de->d_off = (off64_t)diroff + 1;
932*0Sstevel@tonic-gate 		de->d_reclen = (ushort_t)reclen;
933*0Sstevel@tonic-gate 
934*0Sstevel@tonic-gate 		/* use strncpy(9f) to zero out uninitialized bytes */
935*0Sstevel@tonic-gate 
936*0Sstevel@tonic-gate 		(void) strncpy(de->d_name, "..", DIRENT64_NAMELEN(reclen));
937*0Sstevel@tonic-gate 		movesz += reclen;
938*0Sstevel@tonic-gate 		de = (dirent64_t *)((char *)de + reclen);
939*0Sstevel@tonic-gate 		dcmn_err3(("devfs_readdir: B: diroff %lld, soff %lld: '%s' "
940*0Sstevel@tonic-gate 		    "reclen %lu\n", diroff, soff, "..", reclen));
941*0Sstevel@tonic-gate 	}
942*0Sstevel@tonic-gate 
943*0Sstevel@tonic-gate 	diroff++;
944*0Sstevel@tonic-gate 	for (dv = ddv->dv_dot; dv; dv = dv->dv_next, diroff++) {
945*0Sstevel@tonic-gate 		/*
946*0Sstevel@tonic-gate 		 * although DDM_INTERNAL_PATH minor nodes are skipped for
947*0Sstevel@tonic-gate 		 * readdirs outside the kernel, they still occupy directory
948*0Sstevel@tonic-gate 		 * offsets
949*0Sstevel@tonic-gate 		 */
950*0Sstevel@tonic-gate 		if (diroff < soff ||
951*0Sstevel@tonic-gate 		    ((dv->dv_flags & DV_INTERNAL) && (cred != kcred)))
952*0Sstevel@tonic-gate 			continue;
953*0Sstevel@tonic-gate 
954*0Sstevel@tonic-gate 		reclen = DIRENT64_RECLEN(strlen(dv->dv_name));
955*0Sstevel@tonic-gate 		if ((movesz + reclen) > bufsz) {
956*0Sstevel@tonic-gate 			dcmn_err3(("devfs_readdir: C: diroff "
957*0Sstevel@tonic-gate 			    "%lld, soff %lld: '%s' reclen %lu\n",
958*0Sstevel@tonic-gate 			    diroff, soff, dv->dv_name, reclen));
959*0Sstevel@tonic-gate 			goto full;
960*0Sstevel@tonic-gate 		}
961*0Sstevel@tonic-gate 		de->d_ino = (ino64_t)dv->dv_ino;
962*0Sstevel@tonic-gate 		de->d_off = (off64_t)diroff + 1;
963*0Sstevel@tonic-gate 		de->d_reclen = (ushort_t)reclen;
964*0Sstevel@tonic-gate 
965*0Sstevel@tonic-gate 		/* use strncpy(9f) to zero out uninitialized bytes */
966*0Sstevel@tonic-gate 
967*0Sstevel@tonic-gate 		ASSERT(strlen(dv->dv_name) + 1 <=
968*0Sstevel@tonic-gate 		    DIRENT64_NAMELEN(reclen));
969*0Sstevel@tonic-gate 		(void) strncpy(de->d_name, dv->dv_name,
970*0Sstevel@tonic-gate 		    DIRENT64_NAMELEN(reclen));
971*0Sstevel@tonic-gate 
972*0Sstevel@tonic-gate 		movesz += reclen;
973*0Sstevel@tonic-gate 		de = (dirent64_t *)((char *)de + reclen);
974*0Sstevel@tonic-gate 		dcmn_err4(("devfs_readdir: D: diroff "
975*0Sstevel@tonic-gate 		    "%lld, soff %lld: '%s' reclen %lu\n", diroff, soff,
976*0Sstevel@tonic-gate 		    dv->dv_name, reclen));
977*0Sstevel@tonic-gate 	}
978*0Sstevel@tonic-gate 
979*0Sstevel@tonic-gate 	/* the buffer is full, or we exhausted everything */
980*0Sstevel@tonic-gate full:	dcmn_err3(("devfs_readdir: moving %lu bytes: "
981*0Sstevel@tonic-gate 	    "diroff %lld, soff %lld, dv %p\n",
982*0Sstevel@tonic-gate 	    movesz, diroff, soff, (void *)dv));
983*0Sstevel@tonic-gate 
984*0Sstevel@tonic-gate 	if ((movesz == 0) && dv)
985*0Sstevel@tonic-gate 		error = EINVAL;		/* cannot be represented */
986*0Sstevel@tonic-gate 	else {
987*0Sstevel@tonic-gate 		error = uiomove(bufp, movesz, UIO_READ, uiop);
988*0Sstevel@tonic-gate 		if (error == 0) {
989*0Sstevel@tonic-gate 			if (eofp)
990*0Sstevel@tonic-gate 				*eofp = dv ? 0 : 1;
991*0Sstevel@tonic-gate 			uiop->uio_offset = diroff;
992*0Sstevel@tonic-gate 		}
993*0Sstevel@tonic-gate 
994*0Sstevel@tonic-gate 		va.va_mask = AT_ATIME;
995*0Sstevel@tonic-gate 		gethrestime(&va.va_atime);
996*0Sstevel@tonic-gate 		rw_exit(&ddv->dv_contents);
997*0Sstevel@tonic-gate 		(void) devfs_setattr(dvp, &va, 0, cred, NULL);
998*0Sstevel@tonic-gate 		rw_enter(&ddv->dv_contents, RW_READER);
999*0Sstevel@tonic-gate 	}
1000*0Sstevel@tonic-gate 
1001*0Sstevel@tonic-gate 	kmem_free(bufp, bufsz);
1002*0Sstevel@tonic-gate 	return (error);
1003*0Sstevel@tonic-gate }
1004*0Sstevel@tonic-gate 
1005*0Sstevel@tonic-gate /*ARGSUSED*/
1006*0Sstevel@tonic-gate static int
1007*0Sstevel@tonic-gate devfs_fsync(struct vnode *vp, int syncflag, struct cred *cred)
1008*0Sstevel@tonic-gate {
1009*0Sstevel@tonic-gate 	/*
1010*0Sstevel@tonic-gate 	 * Message goes to console only. Otherwise, the message
1011*0Sstevel@tonic-gate 	 * causes devfs_fsync to be invoked again... infinite loop
1012*0Sstevel@tonic-gate 	 */
1013*0Sstevel@tonic-gate 	dcmn_err2(("devfs_fsync %s\n", VTODV(vp)->dv_name));
1014*0Sstevel@tonic-gate 	return (0);
1015*0Sstevel@tonic-gate }
1016*0Sstevel@tonic-gate 
1017*0Sstevel@tonic-gate /*
1018*0Sstevel@tonic-gate  * Normally, we leave the dv_node here at count of 0.
1019*0Sstevel@tonic-gate  * The node will be destroyed when dv_cleandir() is called.
1020*0Sstevel@tonic-gate  *
1021*0Sstevel@tonic-gate  * Stale dv_node's are already unlinked from the fs tree,
1022*0Sstevel@tonic-gate  * so dv_cleandir() won't find them. We destroy such nodes
1023*0Sstevel@tonic-gate  * immediately.
1024*0Sstevel@tonic-gate  */
1025*0Sstevel@tonic-gate /*ARGSUSED1*/
1026*0Sstevel@tonic-gate static void
1027*0Sstevel@tonic-gate devfs_inactive(struct vnode *vp, struct cred *cred)
1028*0Sstevel@tonic-gate {
1029*0Sstevel@tonic-gate 	int destroy;
1030*0Sstevel@tonic-gate 	struct dv_node *dv = VTODV(vp);
1031*0Sstevel@tonic-gate 
1032*0Sstevel@tonic-gate 	dcmn_err2(("devfs_inactive: %s\n", dv->dv_name));
1033*0Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
1034*0Sstevel@tonic-gate 	ASSERT(vp->v_count >= 1);
1035*0Sstevel@tonic-gate 	--vp->v_count;
1036*0Sstevel@tonic-gate 	destroy = (DV_STALE(dv) && vp->v_count == 0);
1037*0Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
1038*0Sstevel@tonic-gate 
1039*0Sstevel@tonic-gate 	/* stale nodes cannot be rediscovered, destroy it here */
1040*0Sstevel@tonic-gate 	if (destroy)
1041*0Sstevel@tonic-gate 		dv_destroy(dv, 0);
1042*0Sstevel@tonic-gate }
1043*0Sstevel@tonic-gate 
1044*0Sstevel@tonic-gate /*
1045*0Sstevel@tonic-gate  * XXX Why do we need this?  NFS mounted /dev directories?
1046*0Sstevel@tonic-gate  * XXX Talk to peter staubach about this.
1047*0Sstevel@tonic-gate  */
1048*0Sstevel@tonic-gate static int
1049*0Sstevel@tonic-gate devfs_fid(struct vnode *vp, struct fid *fidp)
1050*0Sstevel@tonic-gate {
1051*0Sstevel@tonic-gate 	struct dv_node	*dv = VTODV(vp);
1052*0Sstevel@tonic-gate 	struct dv_fid	*dv_fid;
1053*0Sstevel@tonic-gate 
1054*0Sstevel@tonic-gate 	if (fidp->fid_len < (sizeof (struct dv_fid) - sizeof (ushort_t))) {
1055*0Sstevel@tonic-gate 		fidp->fid_len = sizeof (struct dv_fid) - sizeof (ushort_t);
1056*0Sstevel@tonic-gate 		return (ENOSPC);
1057*0Sstevel@tonic-gate 	}
1058*0Sstevel@tonic-gate 
1059*0Sstevel@tonic-gate 	dv_fid = (struct dv_fid *)fidp;
1060*0Sstevel@tonic-gate 	bzero(dv_fid, sizeof (struct dv_fid));
1061*0Sstevel@tonic-gate 	dv_fid->dvfid_len = (int)sizeof (struct dv_fid) - sizeof (ushort_t);
1062*0Sstevel@tonic-gate 	dv_fid->dvfid_ino = dv->dv_ino;
1063*0Sstevel@tonic-gate 	/* dv_fid->dvfid_gen = dv->tn_gen; XXX ? */
1064*0Sstevel@tonic-gate 
1065*0Sstevel@tonic-gate 	return (0);
1066*0Sstevel@tonic-gate }
1067*0Sstevel@tonic-gate 
1068*0Sstevel@tonic-gate /*
1069*0Sstevel@tonic-gate  * This pair of routines bracket all VOP_READ, VOP_WRITE
1070*0Sstevel@tonic-gate  * and VOP_READDIR requests.  The contents lock stops things
1071*0Sstevel@tonic-gate  * moving around while we're looking at them.
1072*0Sstevel@tonic-gate  *
1073*0Sstevel@tonic-gate  * Also used by file and record locking.
1074*0Sstevel@tonic-gate  */
1075*0Sstevel@tonic-gate /*ARGSUSED2*/
1076*0Sstevel@tonic-gate static int
1077*0Sstevel@tonic-gate devfs_rwlock(struct vnode *vp, int write_flag, caller_context_t *ct)
1078*0Sstevel@tonic-gate {
1079*0Sstevel@tonic-gate 	dcmn_err2(("devfs_rwlock %s\n", VTODV(vp)->dv_name));
1080*0Sstevel@tonic-gate 	rw_enter(&VTODV(vp)->dv_contents, write_flag ? RW_WRITER : RW_READER);
1081*0Sstevel@tonic-gate 	return (write_flag);
1082*0Sstevel@tonic-gate }
1083*0Sstevel@tonic-gate 
1084*0Sstevel@tonic-gate /*ARGSUSED1*/
1085*0Sstevel@tonic-gate static void
1086*0Sstevel@tonic-gate devfs_rwunlock(struct vnode *vp, int write_flag, caller_context_t *ct)
1087*0Sstevel@tonic-gate {
1088*0Sstevel@tonic-gate 	dcmn_err2(("devfs_rwunlock %s\n", VTODV(vp)->dv_name));
1089*0Sstevel@tonic-gate 	rw_exit(&VTODV(vp)->dv_contents);
1090*0Sstevel@tonic-gate }
1091*0Sstevel@tonic-gate 
1092*0Sstevel@tonic-gate /*
1093*0Sstevel@tonic-gate  * XXX	Should probably do a better job of computing the maximum
1094*0Sstevel@tonic-gate  *	offset available in the directory.
1095*0Sstevel@tonic-gate  */
1096*0Sstevel@tonic-gate /*ARGSUSED1*/
1097*0Sstevel@tonic-gate static int
1098*0Sstevel@tonic-gate devfs_seek(struct vnode *vp, offset_t ooff, offset_t *noffp)
1099*0Sstevel@tonic-gate {
1100*0Sstevel@tonic-gate 	ASSERT(vp->v_type == VDIR);
1101*0Sstevel@tonic-gate 	dcmn_err2(("devfs_seek %s\n", VTODV(vp)->dv_name));
1102*0Sstevel@tonic-gate 	return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
1103*0Sstevel@tonic-gate }
1104*0Sstevel@tonic-gate 
1105*0Sstevel@tonic-gate vnodeops_t *dv_vnodeops;
1106*0Sstevel@tonic-gate 
1107*0Sstevel@tonic-gate const fs_operation_def_t dv_vnodeops_template[] = {
1108*0Sstevel@tonic-gate 	VOPNAME_OPEN, devfs_open,
1109*0Sstevel@tonic-gate 	VOPNAME_CLOSE, devfs_close,
1110*0Sstevel@tonic-gate 	VOPNAME_READ, devfs_read,
1111*0Sstevel@tonic-gate 	VOPNAME_WRITE, devfs_write,
1112*0Sstevel@tonic-gate 	VOPNAME_IOCTL, devfs_ioctl,
1113*0Sstevel@tonic-gate 	VOPNAME_GETATTR, devfs_getattr,
1114*0Sstevel@tonic-gate 	VOPNAME_SETATTR, devfs_setattr,
1115*0Sstevel@tonic-gate 	VOPNAME_ACCESS, devfs_access,
1116*0Sstevel@tonic-gate 	VOPNAME_LOOKUP, devfs_lookup,
1117*0Sstevel@tonic-gate 	VOPNAME_CREATE, devfs_create,
1118*0Sstevel@tonic-gate 	VOPNAME_READDIR, devfs_readdir,
1119*0Sstevel@tonic-gate 	VOPNAME_FSYNC, devfs_fsync,
1120*0Sstevel@tonic-gate 	VOPNAME_INACTIVE, (fs_generic_func_p) devfs_inactive,
1121*0Sstevel@tonic-gate 	VOPNAME_FID, devfs_fid,
1122*0Sstevel@tonic-gate 	VOPNAME_RWLOCK, devfs_rwlock,
1123*0Sstevel@tonic-gate 	VOPNAME_RWUNLOCK, (fs_generic_func_p) devfs_rwunlock,
1124*0Sstevel@tonic-gate 	VOPNAME_SEEK, devfs_seek,
1125*0Sstevel@tonic-gate 	VOPNAME_PATHCONF, devfs_pathconf,
1126*0Sstevel@tonic-gate 	VOPNAME_DISPOSE, fs_error,
1127*0Sstevel@tonic-gate 	VOPNAME_SETSECATTR, devfs_setsecattr,
1128*0Sstevel@tonic-gate 	VOPNAME_GETSECATTR, devfs_getsecattr,
1129*0Sstevel@tonic-gate 	NULL, NULL
1130*0Sstevel@tonic-gate };
1131