xref: /illumos-gate/usr/src/uts/common/fs/dev/sdev_vnops.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * vnode ops for the /dev filesystem
28  *
29  * - VDIR, VCHR, CBLK, and VLNK are considered must supported files
30  * - VREG and VDOOR are used for some internal implementations in
31  *    the global zone, e.g. devname and devfsadm communication
32  * - other file types are unusual in this namespace and
33  *    not supported for now
34  */
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/t_lock.h>
39 #include <sys/systm.h>
40 #include <sys/sysmacros.h>
41 #include <sys/user.h>
42 #include <sys/time.h>
43 #include <sys/vfs.h>
44 #include <sys/vnode.h>
45 #include <sys/vfs_opreg.h>
46 #include <sys/file.h>
47 #include <sys/fcntl.h>
48 #include <sys/flock.h>
49 #include <sys/kmem.h>
50 #include <sys/uio.h>
51 #include <sys/errno.h>
52 #include <sys/stat.h>
53 #include <sys/cred.h>
54 #include <sys/cred_impl.h>
55 #include <sys/dirent.h>
56 #include <sys/pathname.h>
57 #include <sys/cmn_err.h>
58 #include <sys/debug.h>
59 #include <sys/policy.h>
60 #include <vm/hat.h>
61 #include <vm/seg_vn.h>
62 #include <vm/seg_map.h>
63 #include <vm/seg.h>
64 #include <vm/as.h>
65 #include <vm/page.h>
66 #include <sys/proc.h>
67 #include <sys/mode.h>
68 #include <sys/sunndi.h>
69 #include <sys/ptms.h>
70 #include <fs/fs_subr.h>
71 #include <sys/fs/dv_node.h>
72 #include <sys/fs/sdev_impl.h>
73 
74 /*ARGSUSED*/
75 static int
76 sdev_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct)
77 {
78 	struct sdev_node *dv = VTOSDEV(*vpp);
79 	struct sdev_node *ddv = dv->sdev_dotdot;
80 	int error = 0;
81 
82 	if ((*vpp)->v_type == VDIR)
83 		return (0);
84 
85 	if (!SDEV_IS_GLOBAL(dv))
86 		return (ENOTSUP);
87 
88 	ASSERT((*vpp)->v_type == VREG);
89 	if ((*vpp)->v_type != VREG)
90 		return (ENOTSUP);
91 
92 	ASSERT(ddv);
93 	rw_enter(&ddv->sdev_contents, RW_READER);
94 	if (dv->sdev_attrvp == NULL) {
95 		rw_exit(&ddv->sdev_contents);
96 		return (ENOENT);
97 	}
98 	error = VOP_OPEN(&(dv->sdev_attrvp), flag, cred, ct);
99 	rw_exit(&ddv->sdev_contents);
100 	return (error);
101 }
102 
103 /*ARGSUSED1*/
104 static int
105 sdev_close(struct vnode *vp, int flag, int count,
106     offset_t offset, struct cred *cred, caller_context_t *ct)
107 {
108 	struct sdev_node *dv = VTOSDEV(vp);
109 
110 	if (vp->v_type == VDIR) {
111 		cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
112 		cleanshares(vp, ttoproc(curthread)->p_pid);
113 		return (0);
114 	}
115 
116 	if (!SDEV_IS_GLOBAL(dv))
117 		return (ENOTSUP);
118 
119 	ASSERT(vp->v_type == VREG);
120 	if (vp->v_type != VREG)
121 		return (ENOTSUP);
122 
123 	ASSERT(dv->sdev_attrvp);
124 	return (VOP_CLOSE(dv->sdev_attrvp, flag, count, offset, cred, ct));
125 }
126 
127 /*ARGSUSED*/
128 static int
129 sdev_read(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred,
130 	struct caller_context *ct)
131 {
132 	struct sdev_node *dv = (struct sdev_node *)VTOSDEV(vp);
133 	int	error;
134 
135 	if (!SDEV_IS_GLOBAL(dv))
136 		return (EINVAL);
137 
138 	if (vp->v_type == VDIR)
139 		return (EISDIR);
140 
141 	/* only supporting regular files in /dev */
142 	ASSERT(vp->v_type == VREG);
143 	if (vp->v_type != VREG)
144 		return (EINVAL);
145 
146 	ASSERT(RW_READ_HELD(&VTOSDEV(vp)->sdev_contents));
147 	ASSERT(dv->sdev_attrvp);
148 	(void) VOP_RWLOCK(dv->sdev_attrvp, 0, ct);
149 	error = VOP_READ(dv->sdev_attrvp, uio, ioflag, cred, ct);
150 	VOP_RWUNLOCK(dv->sdev_attrvp, 0, ct);
151 	return (error);
152 }
153 
154 /*ARGSUSED*/
155 static int
156 sdev_write(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred,
157 	struct caller_context *ct)
158 {
159 	struct sdev_node *dv = VTOSDEV(vp);
160 	int	error = 0;
161 
162 	if (!SDEV_IS_GLOBAL(dv))
163 		return (EINVAL);
164 
165 	if (vp->v_type == VDIR)
166 		return (EISDIR);
167 
168 	/* only supporting regular files in /dev */
169 	ASSERT(vp->v_type == VREG);
170 	if (vp->v_type != VREG)
171 		return (EINVAL);
172 
173 	ASSERT(dv->sdev_attrvp);
174 
175 	(void) VOP_RWLOCK(dv->sdev_attrvp, 1, ct);
176 	error = VOP_WRITE(dv->sdev_attrvp, uio, ioflag, cred, ct);
177 	VOP_RWUNLOCK(dv->sdev_attrvp, 1, ct);
178 	if (error == 0) {
179 		sdev_update_timestamps(dv->sdev_attrvp, kcred,
180 		    AT_MTIME);
181 	}
182 	return (error);
183 }
184 
185 /*ARGSUSED*/
186 static int
187 sdev_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag,
188     struct cred *cred, int *rvalp,  caller_context_t *ct)
189 {
190 	struct sdev_node *dv = VTOSDEV(vp);
191 
192 	if (!SDEV_IS_GLOBAL(dv) || (vp->v_type == VDIR))
193 		return (ENOTTY);
194 
195 	ASSERT(vp->v_type == VREG);
196 	if (vp->v_type != VREG)
197 		return (EINVAL);
198 
199 	ASSERT(dv->sdev_attrvp);
200 	return (VOP_IOCTL(dv->sdev_attrvp, cmd, arg, flag, cred, rvalp, ct));
201 }
202 
203 static int
204 sdev_getattr(struct vnode *vp, struct vattr *vap, int flags,
205     struct cred *cr, caller_context_t *ct)
206 {
207 	int			error = 0;
208 	struct sdev_node	*dv = VTOSDEV(vp);
209 	struct sdev_node	*parent = dv->sdev_dotdot;
210 
211 	ASSERT(parent);
212 
213 	rw_enter(&parent->sdev_contents, RW_READER);
214 	ASSERT(dv->sdev_attr || dv->sdev_attrvp);
215 
216 	/*
217 	 * search order:
218 	 * 	- for persistent nodes (SDEV_PERSIST): backstore
219 	 *	- for non-persistent nodes: module ops if global, then memory
220 	 */
221 	if (dv->sdev_attrvp) {
222 		rw_exit(&parent->sdev_contents);
223 		error = VOP_GETATTR(dv->sdev_attrvp, vap, flags, cr, ct);
224 		sdev_vattr_merge(dv, vap);
225 	} else {
226 		ASSERT(dv->sdev_attr);
227 		*vap = *dv->sdev_attr;
228 		sdev_vattr_merge(dv, vap);
229 		rw_exit(&parent->sdev_contents);
230 	}
231 
232 	return (error);
233 }
234 
235 /*ARGSUSED4*/
236 static int
237 sdev_setattr(struct vnode *vp, struct vattr *vap, int flags,
238     struct cred *cred, caller_context_t *ctp)
239 {
240 	return (devname_setattr_func(vp, vap, flags, cred, NULL, 0));
241 }
242 
243 static int
244 sdev_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags,
245     struct cred *cr, caller_context_t *ct)
246 {
247 	int	error;
248 	struct sdev_node *dv = VTOSDEV(vp);
249 	struct vnode *avp = dv->sdev_attrvp;
250 
251 	if (avp == NULL) {
252 		/* return fs_fab_acl() if flavor matches, else do nothing */
253 		if ((SDEV_ACL_FLAVOR(vp) == _ACL_ACLENT_ENABLED &&
254 		    (vsap->vsa_mask & (VSA_ACLCNT | VSA_DFACLCNT))) ||
255 		    (SDEV_ACL_FLAVOR(vp) == _ACL_ACE_ENABLED &&
256 		    (vsap->vsa_mask & (VSA_ACECNT | VSA_ACE))))
257 			return (fs_fab_acl(vp, vsap, flags, cr, ct));
258 
259 		return (ENOSYS);
260 	}
261 
262 	(void) VOP_RWLOCK(avp, 1, ct);
263 	error = VOP_GETSECATTR(avp, vsap, flags, cr, ct);
264 	VOP_RWUNLOCK(avp, 1, ct);
265 	return (error);
266 }
267 
268 static int
269 sdev_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags,
270     struct cred *cr, caller_context_t *ct)
271 {
272 	int	error;
273 	struct sdev_node *dv = VTOSDEV(vp);
274 	struct vnode *avp = dv->sdev_attrvp;
275 
276 	if (dv->sdev_state == SDEV_ZOMBIE)
277 		return (0);
278 
279 	if (avp == NULL) {
280 		if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_PERSIST(dv))
281 			return (fs_nosys());
282 		ASSERT(dv->sdev_attr);
283 		/*
284 		 * if coming in directly, the acl system call will
285 		 * have held the read-write lock via VOP_RWLOCK()
286 		 * If coming in via specfs, specfs will have
287 		 * held the rw lock on the realvp i.e. us.
288 		 */
289 		ASSERT(RW_WRITE_HELD(&dv->sdev_contents));
290 		sdev_vattr_merge(dv, dv->sdev_attr);
291 		error = sdev_shadow_node(dv, cr);
292 		if (error) {
293 			return (fs_nosys());
294 		}
295 
296 		ASSERT(dv->sdev_attrvp);
297 		/* clean out the memory copy if any */
298 		if (dv->sdev_attr) {
299 			kmem_free(dv->sdev_attr, sizeof (struct vattr));
300 			dv->sdev_attr = NULL;
301 		}
302 		avp = dv->sdev_attrvp;
303 	}
304 	ASSERT(avp);
305 
306 	(void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, ct);
307 	error = VOP_SETSECATTR(avp, vsap, flags, cr, ct);
308 	VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, ct);
309 	return (error);
310 }
311 
312 int
313 sdev_unlocked_access(void *vdv, int mode, struct cred *cr)
314 {
315 	struct sdev_node	*dv = vdv;
316 	int			shift = 0;
317 	uid_t			owner = dv->sdev_attr->va_uid;
318 
319 	if (crgetuid(cr) != owner) {
320 		shift += 3;
321 		if (groupmember(dv->sdev_attr->va_gid, cr) == 0)
322 			shift += 3;
323 	}
324 
325 	mode &= ~(dv->sdev_attr->va_mode << shift);
326 	if (mode == 0)
327 		return (0);
328 
329 	return (secpolicy_vnode_access(cr, SDEVTOV(dv), owner, mode));
330 }
331 
332 static int
333 sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr,
334     caller_context_t *ct)
335 {
336 	struct sdev_node	*dv = VTOSDEV(vp);
337 	int ret = 0;
338 
339 	ASSERT(dv->sdev_attr || dv->sdev_attrvp);
340 
341 	if (dv->sdev_attrvp) {
342 		ret = VOP_ACCESS(dv->sdev_attrvp, mode, flags, cr, ct);
343 	} else if (dv->sdev_attr) {
344 		rw_enter(&dv->sdev_contents, RW_READER);
345 		ret = sdev_unlocked_access(dv, mode, cr);
346 		if (ret)
347 			ret = EACCES;
348 		rw_exit(&dv->sdev_contents);
349 	}
350 
351 	return (ret);
352 }
353 
354 /*
355  * Lookup
356  */
357 /*ARGSUSED3*/
358 static int
359 sdev_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
360     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
361     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
362 {
363 	struct sdev_node *parent;
364 	int error;
365 
366 	parent = VTOSDEV(dvp);
367 	ASSERT(parent);
368 
369 	/* execute access is required to search the directory */
370 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
371 		return (error);
372 
373 	if (!SDEV_IS_GLOBAL(parent))
374 		return (prof_lookup(dvp, nm, vpp, cred));
375 	return (devname_lookup_func(parent, nm, vpp, cred, NULL, 0));
376 }
377 
378 /*ARGSUSED2*/
379 static int
380 sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
381     int mode, struct vnode **vpp, struct cred *cred, int flag,
382     caller_context_t *ct, vsecattr_t *vsecp)
383 {
384 	struct vnode		*vp = NULL;
385 	struct vnode		*avp;
386 	struct sdev_node	*parent;
387 	struct sdev_node	*self = NULL;
388 	int			error = 0;
389 	vtype_t			type = vap->va_type;
390 
391 	ASSERT(type != VNON && type != VBAD);
392 
393 	if ((type == VFIFO) || (type == VSOCK) ||
394 	    (type == VPROC) || (type == VPORT))
395 		return (ENOTSUP);
396 
397 	parent = VTOSDEV(dvp);
398 	ASSERT(parent);
399 
400 	rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER);
401 	if (parent->sdev_state == SDEV_ZOMBIE) {
402 		rw_exit(&parent->sdev_dotdot->sdev_contents);
403 		return (ENOENT);
404 	}
405 
406 	/* non-global do not allow pure node creation */
407 	if (!SDEV_IS_GLOBAL(parent)) {
408 		rw_exit(&parent->sdev_dotdot->sdev_contents);
409 		return (prof_lookup(dvp, nm, vpp, cred));
410 	}
411 	rw_exit(&parent->sdev_dotdot->sdev_contents);
412 
413 	/* execute access is required to search the directory */
414 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
415 		return (error);
416 
417 	/* check existing name */
418 /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */
419 	error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
420 
421 	/* name found */
422 	if (error == 0) {
423 		ASSERT(vp);
424 		if (excl == EXCL) {
425 			error = EEXIST;
426 		} else if ((vp->v_type == VDIR) && (mode & VWRITE)) {
427 			/* allowing create/read-only an existing directory */
428 			error = EISDIR;
429 		} else {
430 			error = VOP_ACCESS(vp, mode, 0, cred, ct);
431 		}
432 
433 		if (error) {
434 			VN_RELE(vp);
435 			return (error);
436 		}
437 
438 		/* truncation first */
439 		if ((vp->v_type == VREG) && (vap->va_mask & AT_SIZE) &&
440 		    (vap->va_size == 0)) {
441 			ASSERT(parent->sdev_attrvp);
442 			error = VOP_CREATE(parent->sdev_attrvp,
443 			    nm, vap, excl, mode, &avp, cred, flag, ct, vsecp);
444 
445 			if (error) {
446 				VN_RELE(vp);
447 				return (error);
448 			}
449 		}
450 
451 		sdev_update_timestamps(vp, kcred,
452 		    AT_CTIME|AT_MTIME|AT_ATIME);
453 		*vpp = vp;
454 		return (0);
455 	}
456 
457 	/* bail out early */
458 	if (error != ENOENT)
459 		return (error);
460 
461 	/* verify write access - compliance specifies ENXIO */
462 	if ((error = VOP_ACCESS(dvp, VEXEC|VWRITE, 0, cred, ct)) != 0) {
463 		if (error == EACCES)
464 			error = ENXIO;
465 		return (error);
466 	}
467 
468 	/*
469 	 * For memory-based (ROFS) directory:
470 	 * 	- either disallow node creation;
471 	 *	- or implement VOP_CREATE of its own
472 	 */
473 	rw_enter(&parent->sdev_contents, RW_WRITER);
474 	if (!SDEV_IS_PERSIST(parent)) {
475 		rw_exit(&parent->sdev_contents);
476 		return (ENOTSUP);
477 	}
478 	ASSERT(parent->sdev_attrvp);
479 	error = sdev_mknode(parent, nm, &self, vap, NULL, NULL,
480 	    cred, SDEV_READY);
481 	if (error) {
482 		rw_exit(&parent->sdev_contents);
483 		if (self)
484 			SDEV_RELE(self);
485 		return (error);
486 	}
487 	rw_exit(&parent->sdev_contents);
488 
489 	ASSERT(self);
490 	/* take care the timestamps for the node and its parent */
491 	sdev_update_timestamps(SDEVTOV(self), kcred,
492 	    AT_CTIME|AT_MTIME|AT_ATIME);
493 	sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
494 	if (SDEV_IS_GLOBAL(parent))
495 		atomic_inc_ulong(&parent->sdev_gdir_gen);
496 
497 	/* wake up other threads blocked on looking up this node */
498 	mutex_enter(&self->sdev_lookup_lock);
499 	SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP);
500 	mutex_exit(&self->sdev_lookup_lock);
501 	error = sdev_to_vp(self, vpp);
502 	return (error);
503 }
504 
505 static int
506 sdev_remove(struct vnode *dvp, char *nm, struct cred *cred,
507     caller_context_t *ct, int flags)
508 {
509 	int	error;
510 	struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp);
511 	struct vnode *vp = NULL;
512 	struct sdev_node *dv = NULL;
513 	int len;
514 	int bkstore = 0;
515 
516 	/* bail out early */
517 	len = strlen(nm);
518 	if (nm[0] == '.') {
519 		if (len == 1) {
520 			return (EINVAL);
521 		} else if (len == 2 && nm[1] == '.') {
522 			return (EEXIST);
523 		}
524 	}
525 
526 	ASSERT(parent);
527 	rw_enter(&parent->sdev_contents, RW_READER);
528 	if (!SDEV_IS_GLOBAL(parent)) {
529 		rw_exit(&parent->sdev_contents);
530 		return (ENOTSUP);
531 	}
532 
533 	/* execute access is required to search the directory */
534 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) {
535 		rw_exit(&parent->sdev_contents);
536 		return (error);
537 	}
538 
539 	/* check existence first */
540 	dv = sdev_cache_lookup(parent, nm);
541 	if (dv == NULL) {
542 		rw_exit(&parent->sdev_contents);
543 		return (ENOENT);
544 	}
545 
546 	vp = SDEVTOV(dv);
547 	if ((dv->sdev_state == SDEV_INIT) ||
548 	    (dv->sdev_state == SDEV_ZOMBIE)) {
549 		rw_exit(&parent->sdev_contents);
550 		VN_RELE(vp);
551 		return (ENOENT);
552 	}
553 
554 	/* write access is required to remove an entry */
555 	if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) {
556 		rw_exit(&parent->sdev_contents);
557 		VN_RELE(vp);
558 		return (error);
559 	}
560 
561 	/*
562 	 * sdev_dirdelete does the real job of:
563 	 *  - make sure no open ref count
564 	 *  - destroying the sdev_node
565 	 *  - releasing the hold on attrvp
566 	 */
567 	bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0;
568 	if (!rw_tryupgrade(&parent->sdev_contents)) {
569 		rw_exit(&parent->sdev_contents);
570 		rw_enter(&parent->sdev_contents, RW_WRITER);
571 	}
572 	error = sdev_cache_update(parent, &dv, nm, SDEV_CACHE_DELETE);
573 	rw_exit(&parent->sdev_contents);
574 
575 	sdcmn_err2(("sdev_remove: cache_update error %d\n", error));
576 	if (error && (error != EBUSY)) {
577 		/* report errors other than EBUSY */
578 		VN_RELE(vp);
579 	} else {
580 		sdcmn_err2(("sdev_remove: cleaning node %s from cache "
581 		    " with error %d\n", nm, error));
582 
583 		/*
584 		 * best efforts clean up the backing store
585 		 */
586 		if (bkstore) {
587 			ASSERT(parent->sdev_attrvp);
588 			error = VOP_REMOVE(parent->sdev_attrvp, nm, cred,
589 			    ct, flags);
590 			/*
591 			 * do not report BUSY error
592 			 * because the backing store ref count is released
593 			 * when the last ref count on the sdev_node is
594 			 * released.
595 			 */
596 			if (error == EBUSY) {
597 				sdcmn_err2(("sdev_remove: device %s is still on"
598 				    "disk %s\n", nm, parent->sdev_path));
599 				error = 0;
600 			}
601 		}
602 
603 		if (error == EBUSY)
604 			error = 0;
605 	}
606 
607 	return (error);
608 }
609 
610 /*
611  * Some restrictions for this file system:
612  *  - both oldnm and newnm are in the scope of /dev file system,
613  *    to simply the namespace management model.
614  */
615 /*ARGSUSED6*/
616 static int
617 sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm,
618     struct cred *cred, caller_context_t *ct, int flags)
619 {
620 	struct sdev_node	*fromparent = NULL;
621 	struct vattr		vattr;
622 	struct sdev_node	*toparent;
623 	struct sdev_node	*fromdv = NULL;	/* source node */
624 	struct vnode 		*ovp = NULL;	/* source vnode */
625 	struct sdev_node	*todv = NULL;	/* destination node */
626 	struct vnode 		*nvp = NULL;	/* destination vnode */
627 	int			samedir = 0;	/* set if odvp == ndvp */
628 	struct vnode		*realvp;
629 	int error = 0;
630 	dev_t fsid;
631 	int bkstore = 0;
632 	vtype_t type;
633 
634 	/* prevent modifying "." and ".." */
635 	if ((onm[0] == '.' &&
636 	    (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) ||
637 	    (nnm[0] == '.' &&
638 	    (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0')))) {
639 		return (EINVAL);
640 	}
641 
642 	fromparent = VTOSDEV(odvp);
643 	toparent = VTOSDEV(ndvp);
644 
645 	/* ZOMBIE parent doesn't allow new node creation */
646 	rw_enter(&fromparent->sdev_dotdot->sdev_contents, RW_READER);
647 	if (fromparent->sdev_state == SDEV_ZOMBIE) {
648 		rw_exit(&fromparent->sdev_dotdot->sdev_contents);
649 		return (ENOENT);
650 	}
651 
652 	/* renaming only supported for global device nodes */
653 	if (!SDEV_IS_GLOBAL(fromparent)) {
654 		rw_exit(&fromparent->sdev_dotdot->sdev_contents);
655 		return (ENOTSUP);
656 	}
657 	rw_exit(&fromparent->sdev_dotdot->sdev_contents);
658 
659 	rw_enter(&toparent->sdev_dotdot->sdev_contents, RW_READER);
660 	if (toparent->sdev_state == SDEV_ZOMBIE) {
661 		rw_exit(&toparent->sdev_dotdot->sdev_contents);
662 		return (ENOENT);
663 	}
664 	rw_exit(&toparent->sdev_dotdot->sdev_contents);
665 
666 	/*
667 	 * acquire the global lock to prevent
668 	 * mount/unmount/other rename activities.
669 	 */
670 	mutex_enter(&sdev_lock);
671 
672 	/* check existence of the source node */
673 /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */
674 	error = VOP_LOOKUP(odvp, onm, &ovp, NULL, 0, NULL, cred, ct,
675 	    NULL, NULL);
676 	if (error) {
677 		sdcmn_err2(("sdev_rename: the source node %s exists\n",
678 		    onm));
679 		mutex_exit(&sdev_lock);
680 		return (error);
681 	}
682 
683 	if (VOP_REALVP(ovp, &realvp, ct) == 0) {
684 		VN_HOLD(realvp);
685 		VN_RELE(ovp);
686 		ovp = realvp;
687 	}
688 
689 	/* check existence of destination */
690 /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */
691 	error = VOP_LOOKUP(ndvp, nnm, &nvp, NULL, 0, NULL, cred, ct,
692 	    NULL, NULL);
693 	if (error && (error != ENOENT)) {
694 		mutex_exit(&sdev_lock);
695 		VN_RELE(ovp);
696 		return (error);
697 	}
698 
699 	if (nvp && (VOP_REALVP(nvp, &realvp, ct) == 0)) {
700 		VN_HOLD(realvp);
701 		VN_RELE(nvp);
702 		nvp = realvp;
703 	}
704 
705 	/*
706 	 * make sure the source and the destination are
707 	 * in the same dev filesystem
708 	 */
709 	if (odvp != ndvp) {
710 		vattr.va_mask = AT_FSID;
711 		if (error = VOP_GETATTR(odvp, &vattr, 0, cred, ct)) {
712 			mutex_exit(&sdev_lock);
713 			VN_RELE(ovp);
714 			return (error);
715 		}
716 		fsid = vattr.va_fsid;
717 		vattr.va_mask = AT_FSID;
718 		if (error = VOP_GETATTR(ndvp, &vattr, 0, cred, ct)) {
719 			mutex_exit(&sdev_lock);
720 			VN_RELE(ovp);
721 			return (error);
722 		}
723 		if (fsid != vattr.va_fsid) {
724 			mutex_exit(&sdev_lock);
725 			VN_RELE(ovp);
726 			return (EXDEV);
727 		}
728 	}
729 
730 	/* make sure the old entry can be deleted */
731 	error = VOP_ACCESS(odvp, VWRITE, 0, cred, ct);
732 	if (error) {
733 		mutex_exit(&sdev_lock);
734 		VN_RELE(ovp);
735 		return (error);
736 	}
737 
738 	/* make sure the destination allows creation */
739 	samedir = (fromparent == toparent);
740 	if (!samedir) {
741 		error = VOP_ACCESS(ndvp, VEXEC|VWRITE, 0, cred, ct);
742 		if (error) {
743 			mutex_exit(&sdev_lock);
744 			VN_RELE(ovp);
745 			return (error);
746 		}
747 	}
748 
749 	fromdv = VTOSDEV(ovp);
750 	ASSERT(fromdv);
751 
752 	/* destination file exists */
753 	if (nvp) {
754 		todv = VTOSDEV(nvp);
755 		ASSERT(todv);
756 	}
757 
758 	/*
759 	 * link source to new target in the memory
760 	 */
761 	error = sdev_rnmnode(fromparent, fromdv, toparent, &todv, nnm, cred);
762 	if (error) {
763 		sdcmn_err2(("sdev_rename: renaming %s to %s failed "
764 		    " with error %d\n", onm, nnm, error));
765 		mutex_exit(&sdev_lock);
766 		if (nvp)
767 			VN_RELE(nvp);
768 		VN_RELE(ovp);
769 		return (error);
770 	}
771 
772 	/*
773 	 * unlink from source
774 	 */
775 	rw_enter(&fromparent->sdev_contents, RW_READER);
776 	fromdv = sdev_cache_lookup(fromparent, onm);
777 	if (fromdv == NULL) {
778 		rw_exit(&fromparent->sdev_contents);
779 		mutex_exit(&sdev_lock);
780 		sdcmn_err2(("sdev_rename: the source is deleted already\n"));
781 		return (0);
782 	}
783 
784 	if (fromdv->sdev_state == SDEV_ZOMBIE) {
785 		rw_exit(&fromparent->sdev_contents);
786 		mutex_exit(&sdev_lock);
787 		VN_RELE(SDEVTOV(fromdv));
788 		sdcmn_err2(("sdev_rename: the source is being deleted\n"));
789 		return (0);
790 	}
791 	rw_exit(&fromparent->sdev_contents);
792 	ASSERT(SDEVTOV(fromdv) == ovp);
793 	VN_RELE(ovp);
794 
795 	/* clean out the directory contents before it can be removed */
796 	type = SDEVTOV(fromdv)->v_type;
797 	if (type == VDIR) {
798 		error = sdev_cleandir(fromdv, NULL, 0);
799 		sdcmn_err2(("sdev_rename: cleandir finished with %d\n",
800 		    error));
801 		if (error == EBUSY)
802 			error = 0;
803 	}
804 
805 	rw_enter(&fromparent->sdev_contents, RW_WRITER);
806 	bkstore = SDEV_IS_PERSIST(fromdv) ? 1 : 0;
807 	error = sdev_cache_update(fromparent, &fromdv, onm,
808 	    SDEV_CACHE_DELETE);
809 
810 	/* best effforts clean up the backing store */
811 	if (bkstore) {
812 		ASSERT(fromparent->sdev_attrvp);
813 		if (type != VDIR) {
814 /* XXXci - We may need to translate the C-I flags on VOP_REMOVE */
815 			error = VOP_REMOVE(fromparent->sdev_attrvp,
816 			    onm, kcred, ct, 0);
817 		} else {
818 /* XXXci - We may need to translate the C-I flags on VOP_RMDIR */
819 			error = VOP_RMDIR(fromparent->sdev_attrvp,
820 			    onm, fromparent->sdev_attrvp, kcred, ct, 0);
821 		}
822 
823 		if (error) {
824 			sdcmn_err2(("sdev_rename: device %s is "
825 			    "still on disk %s\n", onm,
826 			    fromparent->sdev_path));
827 			error = 0;
828 		}
829 	}
830 	rw_exit(&fromparent->sdev_contents);
831 	mutex_exit(&sdev_lock);
832 
833 	/* once reached to this point, the rename is regarded successful */
834 	return (0);
835 }
836 
837 /*
838  * dev-fs version of "ln -s path dev-name"
839  *	tnm - path, e.g. /devices/... or /dev/...
840  *	lnm - dev_name
841  */
842 /*ARGSUSED6*/
843 static int
844 sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva,
845     char *tnm, struct cred *cred, caller_context_t *ct, int flags)
846 {
847 	int error;
848 	struct vnode *vp = NULL;
849 	struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp);
850 	struct sdev_node *self = (struct sdev_node *)NULL;
851 
852 	ASSERT(parent);
853 	rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER);
854 	if (parent->sdev_state == SDEV_ZOMBIE) {
855 		rw_exit(&parent->sdev_dotdot->sdev_contents);
856 		sdcmn_err2(("sdev_symlink: parent %s is ZOMBIED \n",
857 		    parent->sdev_name));
858 		return (ENOENT);
859 	}
860 
861 	if (!SDEV_IS_GLOBAL(parent)) {
862 		rw_exit(&parent->sdev_dotdot->sdev_contents);
863 		return (ENOTSUP);
864 	}
865 	rw_exit(&parent->sdev_dotdot->sdev_contents);
866 
867 	/* execute access is required to search a directory */
868 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
869 		return (error);
870 
871 	/* find existing name */
872 /* XXXci - We may need to translate the C-I flags here */
873 	error = VOP_LOOKUP(dvp, lnm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
874 	if (error == 0) {
875 		ASSERT(vp);
876 		VN_RELE(vp);
877 		sdcmn_err2(("sdev_symlink: node %s already exists\n", lnm));
878 		return (EEXIST);
879 	}
880 	if (error != ENOENT)
881 		return (error);
882 
883 	/* write access is required to create a symlink */
884 	if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0)
885 		return (error);
886 
887 	/* put it into memory cache */
888 	rw_enter(&parent->sdev_contents, RW_WRITER);
889 	error = sdev_mknode(parent, lnm, &self, tva, NULL, (void *)tnm,
890 	    cred, SDEV_READY);
891 	if (error) {
892 		rw_exit(&parent->sdev_contents);
893 		sdcmn_err2(("sdev_symlink: node %s creation failed\n", lnm));
894 		if (self)
895 			SDEV_RELE(self);
896 
897 		return (error);
898 	}
899 	ASSERT(self && (self->sdev_state == SDEV_READY));
900 	rw_exit(&parent->sdev_contents);
901 
902 	/* take care the timestamps for the node and its parent */
903 	sdev_update_timestamps(SDEVTOV(self), kcred,
904 	    AT_CTIME|AT_MTIME|AT_ATIME);
905 	sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
906 	if (SDEV_IS_GLOBAL(parent))
907 		atomic_inc_ulong(&parent->sdev_gdir_gen);
908 
909 	/* wake up other threads blocked on looking up this node */
910 	mutex_enter(&self->sdev_lookup_lock);
911 	SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP);
912 	mutex_exit(&self->sdev_lookup_lock);
913 	SDEV_RELE(self);	/* don't return with vnode held */
914 	return (0);
915 }
916 
917 /*ARGSUSED6*/
918 static int
919 sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp,
920     struct cred *cred, caller_context_t *ct, int flags, vsecattr_t *vsecp)
921 {
922 	int error;
923 	struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp);
924 	struct sdev_node *self = NULL;
925 	struct vnode	*vp = NULL;
926 
927 	ASSERT(parent && parent->sdev_dotdot);
928 	rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER);
929 	if (parent->sdev_state == SDEV_ZOMBIE) {
930 		rw_exit(&parent->sdev_dotdot->sdev_contents);
931 		return (ENOENT);
932 	}
933 
934 	/* non-global do not allow pure directory creation */
935 	if (!SDEV_IS_GLOBAL(parent)) {
936 		rw_exit(&parent->sdev_dotdot->sdev_contents);
937 		return (prof_lookup(dvp, nm, vpp, cred));
938 	}
939 	rw_exit(&parent->sdev_dotdot->sdev_contents);
940 
941 	/* execute access is required to search the directory */
942 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) {
943 		return (error);
944 	}
945 
946 	/* find existing name */
947 /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */
948 	error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL);
949 	if (error == 0) {
950 		VN_RELE(vp);
951 		return (EEXIST);
952 	}
953 	if (error != ENOENT)
954 		return (error);
955 
956 	/* require write access to create a directory */
957 	if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) {
958 		return (error);
959 	}
960 
961 	/* put it into memory */
962 	rw_enter(&parent->sdev_contents, RW_WRITER);
963 	error = sdev_mknode(parent, nm, &self,
964 	    va, NULL, NULL, cred, SDEV_READY);
965 	if (error) {
966 		rw_exit(&parent->sdev_contents);
967 		if (self)
968 			SDEV_RELE(self);
969 		return (error);
970 	}
971 	ASSERT(self && (self->sdev_state == SDEV_READY));
972 	rw_exit(&parent->sdev_contents);
973 
974 	/* take care the timestamps for the node and its parent */
975 	sdev_update_timestamps(SDEVTOV(self), kcred,
976 	    AT_CTIME|AT_MTIME|AT_ATIME);
977 	sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
978 	if (SDEV_IS_GLOBAL(parent))
979 		atomic_inc_ulong(&parent->sdev_gdir_gen);
980 
981 	/* wake up other threads blocked on looking up this node */
982 	mutex_enter(&self->sdev_lookup_lock);
983 	SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP);
984 	mutex_exit(&self->sdev_lookup_lock);
985 	*vpp = SDEVTOV(self);
986 	return (0);
987 }
988 
989 /*
990  * allowing removing an empty directory under /dev
991  */
992 /*ARGSUSED*/
993 static int
994 sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred,
995     caller_context_t *ct, int flags)
996 {
997 	int error = 0;
998 	struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp);
999 	struct sdev_node *self = NULL;
1000 	struct vnode *vp = NULL;
1001 
1002 	/* bail out early */
1003 	if (strcmp(nm, ".") == 0)
1004 		return (EINVAL);
1005 	if (strcmp(nm, "..") == 0)
1006 		return (EEXIST); /* should be ENOTEMPTY */
1007 
1008 	/* no destruction of non-global node */
1009 	ASSERT(parent && parent->sdev_dotdot);
1010 	rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER);
1011 	if (!SDEV_IS_GLOBAL(parent)) {
1012 		rw_exit(&parent->sdev_dotdot->sdev_contents);
1013 		return (ENOTSUP);
1014 	}
1015 	rw_exit(&parent->sdev_dotdot->sdev_contents);
1016 
1017 	/* execute access is required to search the directory */
1018 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
1019 		return (error);
1020 
1021 	/* check existing name */
1022 	rw_enter(&parent->sdev_contents, RW_WRITER);
1023 	self = sdev_cache_lookup(parent, nm);
1024 	if (self == NULL) {
1025 		rw_exit(&parent->sdev_contents);
1026 		return (ENOENT);
1027 	}
1028 
1029 	vp = SDEVTOV(self);
1030 	if ((self->sdev_state == SDEV_INIT) ||
1031 	    (self->sdev_state == SDEV_ZOMBIE)) {
1032 		rw_exit(&parent->sdev_contents);
1033 		VN_RELE(vp);
1034 		return (ENOENT);
1035 	}
1036 
1037 	/* write access is required to remove a directory */
1038 	if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) {
1039 		rw_exit(&parent->sdev_contents);
1040 		VN_RELE(vp);
1041 		return (error);
1042 	}
1043 
1044 	/* some sanity checks */
1045 	if (vp == dvp || vp == cdir) {
1046 		rw_exit(&parent->sdev_contents);
1047 		VN_RELE(vp);
1048 		return (EINVAL);
1049 	}
1050 
1051 	if (vp->v_type != VDIR) {
1052 		rw_exit(&parent->sdev_contents);
1053 		VN_RELE(vp);
1054 		return (ENOTDIR);
1055 	}
1056 
1057 	if (vn_vfswlock(vp)) {
1058 		rw_exit(&parent->sdev_contents);
1059 		VN_RELE(vp);
1060 		return (EBUSY);
1061 	}
1062 
1063 	if (vn_mountedvfs(vp) != NULL) {
1064 		rw_exit(&parent->sdev_contents);
1065 		vn_vfsunlock(vp);
1066 		VN_RELE(vp);
1067 		return (EBUSY);
1068 	}
1069 
1070 	self = VTOSDEV(vp);
1071 	/* bail out on a non-empty directory */
1072 	rw_enter(&self->sdev_contents, RW_READER);
1073 	if (self->sdev_nlink > 2) {
1074 		rw_exit(&self->sdev_contents);
1075 		rw_exit(&parent->sdev_contents);
1076 		vn_vfsunlock(vp);
1077 		VN_RELE(vp);
1078 		return (ENOTEMPTY);
1079 	}
1080 	rw_exit(&self->sdev_contents);
1081 
1082 	/* unlink it from the directory cache */
1083 	error = sdev_cache_update(parent, &self, nm, SDEV_CACHE_DELETE);
1084 	rw_exit(&parent->sdev_contents);
1085 	vn_vfsunlock(vp);
1086 
1087 	if (error && (error != EBUSY)) {
1088 		VN_RELE(vp);
1089 	} else {
1090 		sdcmn_err2(("sdev_rmdir: cleaning node %s from directory "
1091 		    " cache with error %d\n", nm, error));
1092 
1093 		/* best effort to clean up the backing store */
1094 		if (SDEV_IS_PERSIST(parent)) {
1095 			ASSERT(parent->sdev_attrvp);
1096 			error = VOP_RMDIR(parent->sdev_attrvp, nm,
1097 			    parent->sdev_attrvp, kcred, ct, flags);
1098 			sdcmn_err2(("sdev_rmdir: cleaning device %s is on"
1099 			    " disk error %d\n", parent->sdev_path, error));
1100 		}
1101 
1102 		if (error == EBUSY)
1103 			error = 0;
1104 	}
1105 
1106 	return (error);
1107 }
1108 
1109 /*
1110  * read the contents of a symbolic link
1111  */
1112 static int
1113 sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred,
1114     caller_context_t *ct)
1115 {
1116 	struct sdev_node *dv;
1117 	int	error = 0;
1118 
1119 	ASSERT(vp->v_type == VLNK);
1120 
1121 	dv = VTOSDEV(vp);
1122 
1123 	if (dv->sdev_attrvp) {
1124 		/* non-NULL attrvp implys a persisted node at READY state */
1125 		return (VOP_READLINK(dv->sdev_attrvp, uiop, cred, ct));
1126 	} else if (dv->sdev_symlink != NULL) {
1127 		/* memory nodes, e.g. local nodes */
1128 		rw_enter(&dv->sdev_contents, RW_READER);
1129 		sdcmn_err2(("sdev_readlink link is %s\n", dv->sdev_symlink));
1130 		error = uiomove(dv->sdev_symlink, strlen(dv->sdev_symlink),
1131 		    UIO_READ, uiop);
1132 		rw_exit(&dv->sdev_contents);
1133 		return (error);
1134 	}
1135 
1136 	return (ENOENT);
1137 }
1138 
1139 /*ARGSUSED4*/
1140 static int
1141 sdev_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp,
1142     caller_context_t *ct, int flags)
1143 {
1144 	struct sdev_node *parent = VTOSDEV(dvp);
1145 	int error;
1146 
1147 	/* execute access is required to search the directory */
1148 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
1149 		return (error);
1150 
1151 	ASSERT(parent);
1152 	if (!SDEV_IS_GLOBAL(parent))
1153 		prof_filldir(parent);
1154 	return (devname_readdir_func(dvp, uiop, cred, eofp, SDEV_BROWSE));
1155 }
1156 
1157 /*ARGSUSED1*/
1158 static void
1159 sdev_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct)
1160 {
1161 	devname_inactive_func(vp, cred, NULL);
1162 }
1163 
1164 /*ARGSUSED2*/
1165 static int
1166 sdev_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct)
1167 {
1168 	struct sdev_node	*dv = VTOSDEV(vp);
1169 	struct sdev_fid	*sdev_fid;
1170 
1171 	if (fidp->fid_len < (sizeof (struct sdev_fid) - sizeof (ushort_t))) {
1172 		fidp->fid_len = sizeof (struct sdev_fid) - sizeof (ushort_t);
1173 		return (ENOSPC);
1174 	}
1175 
1176 	sdev_fid = (struct sdev_fid *)fidp;
1177 	bzero(sdev_fid, sizeof (struct sdev_fid));
1178 	sdev_fid->sdevfid_len =
1179 	    (int)sizeof (struct sdev_fid) - sizeof (ushort_t);
1180 	sdev_fid->sdevfid_ino = dv->sdev_ino;
1181 
1182 	return (0);
1183 }
1184 
1185 /*
1186  * This pair of routines bracket all VOP_READ, VOP_WRITE
1187  * and VOP_READDIR requests.  The contents lock stops things
1188  * moving around while we're looking at them.
1189  */
1190 /*ARGSUSED2*/
1191 static int
1192 sdev_rwlock(struct vnode *vp, int write_flag, caller_context_t *ctp)
1193 {
1194 	rw_enter(&VTOSDEV(vp)->sdev_contents,
1195 	    write_flag ? RW_WRITER : RW_READER);
1196 	return (write_flag ? V_WRITELOCK_TRUE : V_WRITELOCK_FALSE);
1197 }
1198 
1199 /*ARGSUSED1*/
1200 static void
1201 sdev_rwunlock(struct vnode *vp, int write_flag, caller_context_t *ctp)
1202 {
1203 	rw_exit(&VTOSDEV(vp)->sdev_contents);
1204 }
1205 
1206 /*ARGSUSED1*/
1207 static int
1208 sdev_seek(struct vnode *vp, offset_t ooff, offset_t *noffp,
1209     caller_context_t *ct)
1210 {
1211 	struct vnode *attrvp = VTOSDEV(vp)->sdev_attrvp;
1212 
1213 	ASSERT(vp->v_type != VCHR &&
1214 	    vp->v_type != VBLK && vp->v_type != VLNK);
1215 
1216 	if (vp->v_type == VDIR)
1217 		return (fs_seek(vp, ooff, noffp, ct));
1218 
1219 	ASSERT(attrvp);
1220 	return (VOP_SEEK(attrvp, ooff, noffp, ct));
1221 }
1222 
1223 /*ARGSUSED1*/
1224 static int
1225 sdev_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
1226     offset_t offset, struct flk_callback *flk_cbp, struct cred *cr,
1227     caller_context_t *ct)
1228 {
1229 	int error;
1230 	struct sdev_node *dv = VTOSDEV(vp);
1231 
1232 	ASSERT(dv);
1233 	ASSERT(dv->sdev_attrvp);
1234 	error = VOP_FRLOCK(dv->sdev_attrvp, cmd, bfp, flag, offset,
1235 	    flk_cbp, cr, ct);
1236 
1237 	return (error);
1238 }
1239 
1240 static int
1241 sdev_setfl(struct vnode *vp, int oflags, int nflags, cred_t *cr,
1242     caller_context_t *ct)
1243 {
1244 	struct sdev_node *dv = VTOSDEV(vp);
1245 	ASSERT(dv);
1246 	ASSERT(dv->sdev_attrvp);
1247 
1248 	return (VOP_SETFL(dv->sdev_attrvp, oflags, nflags, cr, ct));
1249 }
1250 
1251 static int
1252 sdev_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
1253     caller_context_t *ct)
1254 {
1255 	switch (cmd) {
1256 	case _PC_ACL_ENABLED:
1257 		*valp = SDEV_ACL_FLAVOR(vp);
1258 		return (0);
1259 	}
1260 
1261 	return (fs_pathconf(vp, cmd, valp, cr, ct));
1262 }
1263 
1264 vnodeops_t *sdev_vnodeops;
1265 
1266 const fs_operation_def_t sdev_vnodeops_tbl[] = {
1267 	VOPNAME_OPEN,		{ .vop_open = sdev_open },
1268 	VOPNAME_CLOSE,		{ .vop_close = sdev_close },
1269 	VOPNAME_READ,		{ .vop_read = sdev_read },
1270 	VOPNAME_WRITE,		{ .vop_write = sdev_write },
1271 	VOPNAME_IOCTL,		{ .vop_ioctl = sdev_ioctl },
1272 	VOPNAME_GETATTR,	{ .vop_getattr = sdev_getattr },
1273 	VOPNAME_SETATTR,	{ .vop_setattr = sdev_setattr },
1274 	VOPNAME_ACCESS,		{ .vop_access = sdev_access },
1275 	VOPNAME_LOOKUP,		{ .vop_lookup = sdev_lookup },
1276 	VOPNAME_CREATE,		{ .vop_create = sdev_create },
1277 	VOPNAME_RENAME,		{ .vop_rename = sdev_rename },
1278 	VOPNAME_REMOVE,		{ .vop_remove = sdev_remove },
1279 	VOPNAME_MKDIR,		{ .vop_mkdir = sdev_mkdir },
1280 	VOPNAME_RMDIR,		{ .vop_rmdir = sdev_rmdir },
1281 	VOPNAME_READDIR,	{ .vop_readdir = sdev_readdir },
1282 	VOPNAME_SYMLINK,	{ .vop_symlink = sdev_symlink },
1283 	VOPNAME_READLINK,	{ .vop_readlink = sdev_readlink },
1284 	VOPNAME_INACTIVE,	{ .vop_inactive = sdev_inactive },
1285 	VOPNAME_FID,		{ .vop_fid = sdev_fid },
1286 	VOPNAME_RWLOCK,		{ .vop_rwlock = sdev_rwlock },
1287 	VOPNAME_RWUNLOCK,	{ .vop_rwunlock = sdev_rwunlock },
1288 	VOPNAME_SEEK,		{ .vop_seek = sdev_seek },
1289 	VOPNAME_FRLOCK,		{ .vop_frlock = sdev_frlock },
1290 	VOPNAME_PATHCONF,	{ .vop_pathconf = sdev_pathconf },
1291 	VOPNAME_SETFL,		{ .vop_setfl = sdev_setfl },
1292 	VOPNAME_SETSECATTR,	{ .vop_setsecattr = sdev_setsecattr },
1293 	VOPNAME_GETSECATTR,	{ .vop_getsecattr = sdev_getsecattr },
1294 	NULL,			NULL
1295 };
1296 
1297 int sdev_vnodeops_tbl_size = sizeof (sdev_vnodeops_tbl);
1298