devfs_vnops.c revision 103314
1/*
2 * Copyright (c) 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 2000
5 *	Poul-Henning Kamp.  All rights reserved.
6 *
7 * This code is derived from software donated to Berkeley by
8 * Jan-Simon Pendry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	@(#)kernfs_vnops.c	8.15 (Berkeley) 5/21/95
32 * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43
33 *
34 * $FreeBSD: head/sys/fs/devfs/devfs_vnops.c 103314 2002-09-14 09:02:28Z njl $
35 */
36
37/*
38 * TODO:
39 *	remove empty directories
40 *	mknod: hunt down DE_DELETED, compare name, reinstantiate.
41 *	mkdir: want it ?
42 */
43
44#include <opt_devfs.h>
45#include <opt_mac.h>
46#ifndef NODEVFS
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/conf.h>
51#include <sys/dirent.h>
52#include <sys/kernel.h>
53#include <sys/lock.h>
54#include <sys/mac.h>
55#include <sys/malloc.h>
56#include <sys/mount.h>
57#include <sys/namei.h>
58#include <sys/proc.h>
59#include <sys/time.h>
60#include <sys/unistd.h>
61#include <sys/vnode.h>
62
63#include <fs/devfs/devfs.h>
64
65static int	devfs_access(struct vop_access_args *ap);
66static int	devfs_getattr(struct vop_getattr_args *ap);
67static int	devfs_ioctl(struct vop_ioctl_args *ap);
68static int	devfs_lookupx(struct vop_lookup_args *ap);
69static int	devfs_mknod(struct vop_mknod_args *ap);
70static int	devfs_pathconf(struct vop_pathconf_args *ap);
71static int	devfs_print(struct vop_print_args *ap);
72static int	devfs_read(struct vop_read_args *ap);
73static int	devfs_readdir(struct vop_readdir_args *ap);
74static int	devfs_readlink(struct vop_readlink_args *ap);
75static int	devfs_reclaim(struct vop_reclaim_args *ap);
76#ifdef MAC
77static int	devfs_refreshlabel(struct vop_refreshlabel_args *ap);
78#endif
79static int	devfs_remove(struct vop_remove_args *ap);
80static int	devfs_revoke(struct vop_revoke_args *ap);
81static int	devfs_setattr(struct vop_setattr_args *ap);
82#ifdef MAC
83static int	devfs_setlabel(struct vop_setlabel_args *ap);
84#endif
85static int	devfs_symlink(struct vop_symlink_args *ap);
86
87/*
88 * Construct the fully qualified path name relative to the mountpoint
89 */
90static char *
91devfs_fqpn(char *buf, struct vnode *dvp, struct componentname *cnp)
92{
93	int i;
94	struct devfs_dirent *de, *dd;
95	struct devfs_mount *dmp;
96
97	dmp = VFSTODEVFS(dvp->v_mount);
98	dd = dvp->v_data;
99	i = SPECNAMELEN;
100	buf[i] = '\0';
101	i -= cnp->cn_namelen;
102	if (i < 0)
103		 return (NULL);
104	bcopy(cnp->cn_nameptr, buf + i, cnp->cn_namelen);
105	de = dd;
106	while (de != dmp->dm_basedir) {
107		i--;
108		if (i < 0)
109			 return (NULL);
110		buf[i] = '/';
111		i -= de->de_dirent->d_namlen;
112		if (i < 0)
113			 return (NULL);
114		bcopy(de->de_dirent->d_name, buf + i,
115		    de->de_dirent->d_namlen);
116		de = TAILQ_FIRST(&de->de_dlist);	/* "." */
117		de = TAILQ_NEXT(de, de_list);		/* ".." */
118		de = de->de_dir;
119	}
120	return (buf + i);
121}
122
123int
124devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct thread *td)
125{
126	int error;
127	struct vnode *vp;
128	dev_t dev;
129
130	if (td == NULL)
131		td = curthread; /* XXX */
132loop:
133	vp = de->de_vnode;
134	if (vp != NULL) {
135		if (vget(vp, LK_EXCLUSIVE, td ? td : curthread))
136			goto loop;
137		*vpp = vp;
138		return (0);
139	}
140	if (de->de_dirent->d_type == DT_CHR) {
141		dev = *devfs_itod(de->de_inode);
142		if (dev == NULL)
143			return (ENOENT);
144	} else {
145		dev = NODEV;
146	}
147	error = getnewvnode("devfs", mp, devfs_vnodeop_p, &vp);
148	if (error != 0) {
149		printf("devfs_allocv: failed to allocate new vnode\n");
150		return (error);
151	}
152
153	if (de->de_dirent->d_type == DT_CHR) {
154		vp->v_type = VCHR;
155		vp = addaliasu(vp, dev->si_udev);
156		vp->v_op = devfs_specop_p;
157	} else if (de->de_dirent->d_type == DT_DIR) {
158		vp->v_type = VDIR;
159	} else if (de->de_dirent->d_type == DT_LNK) {
160		vp->v_type = VLNK;
161	} else {
162		vp->v_type = VBAD;
163	}
164	vp->v_data = de;
165	de->de_vnode = vp;
166	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
167#ifdef MAC
168	mac_create_devfs_vnode(de, vp);
169#endif
170	*vpp = vp;
171	return (0);
172}
173
174static int
175devfs_access(ap)
176	struct vop_access_args /* {
177		struct vnode *a_vp;
178		int  a_mode;
179		struct ucred *a_cred;
180		struct thread *a_td;
181	} */ *ap;
182{
183	struct vnode *vp = ap->a_vp;
184	struct devfs_dirent *de;
185
186	de = vp->v_data;
187	if (vp->v_type == VDIR)
188		de = de->de_dir;
189
190	return (vaccess(vp->v_type, de->de_mode, de->de_uid, de->de_gid,
191	    ap->a_mode, ap->a_cred, NULL));
192}
193
194static int
195devfs_getattr(ap)
196	struct vop_getattr_args /* {
197		struct vnode *a_vp;
198		struct vattr *a_vap;
199		struct ucred *a_cred;
200		struct thread *a_td;
201	} */ *ap;
202{
203	struct vnode *vp = ap->a_vp;
204	struct vattr *vap = ap->a_vap;
205	int error = 0;
206	struct devfs_dirent *de;
207	dev_t dev;
208
209	de = vp->v_data;
210	if (vp->v_type == VDIR)
211		de = de->de_dir;
212	bzero((caddr_t) vap, sizeof(*vap));
213	vattr_null(vap);
214	vap->va_uid = de->de_uid;
215	vap->va_gid = de->de_gid;
216	vap->va_mode = de->de_mode;
217	if (vp->v_type == VLNK)
218		vap->va_size = de->de_dirent->d_namlen;
219	else if (vp->v_type == VDIR)
220		vap->va_size = vap->va_bytes = DEV_BSIZE;
221	else
222		vap->va_size = 0;
223	if (vp->v_type != VDIR)
224		vap->va_bytes = 0;
225	vap->va_blocksize = DEV_BSIZE;
226	vap->va_type = vp->v_type;
227
228#define fix(aa)							\
229	do {							\
230		if ((aa).tv_sec == 0) {				\
231			(aa).tv_sec = boottime.tv_sec;		\
232			(aa).tv_nsec = boottime.tv_usec * 1000; \
233		}						\
234	} while (0)
235
236	if (vp->v_type != VCHR)  {
237		fix(de->de_atime);
238		vap->va_atime = de->de_atime;
239		fix(de->de_mtime);
240		vap->va_mtime = de->de_mtime;
241		fix(de->de_ctime);
242		vap->va_ctime = de->de_ctime;
243	} else {
244		dev = vp->v_rdev;
245		fix(dev->si_atime);
246		vap->va_atime = dev->si_atime;
247		fix(dev->si_mtime);
248		vap->va_mtime = dev->si_mtime;
249		fix(dev->si_ctime);
250		vap->va_ctime = dev->si_ctime;
251		vap->va_rdev = dev->si_udev;
252	}
253	vap->va_gen = 0;
254	vap->va_flags = 0;
255	vap->va_nlink = de->de_links;
256	vap->va_fileid = de->de_inode;
257
258	return (error);
259}
260
261static int
262devfs_ioctl(ap)
263	struct vop_ioctl_args /* {
264		struct vnode *a_vp;
265		int  a_command;
266		caddr_t  a_data;
267		int  a_fflag;
268		struct ucred *a_cred;
269		struct thread *a_td;
270	} */ *ap;
271{
272	int error;
273
274	error = devfs_rules_ioctl(ap->a_vp->v_mount, ap->a_command, ap->a_data,
275	    ap->a_td);
276	return (error);
277}
278
279static int
280devfs_lookupx(ap)
281	struct vop_lookup_args /* {
282		struct vnode * a_dvp;
283		struct vnode ** a_vpp;
284		struct componentname * a_cnp;
285	} */ *ap;
286{
287	struct componentname *cnp;
288	struct vnode *dvp, **vpp;
289	struct thread *td;
290	struct devfs_dirent *de, *dd;
291	struct devfs_mount *dmp;
292	dev_t cdev, *cpdev;
293	int error, cloned, flags, nameiop;
294	char specname[SPECNAMELEN + 1], *pname;
295
296	cnp = ap->a_cnp;
297	vpp = ap->a_vpp;
298	dvp = ap->a_dvp;
299	pname = cnp->cn_nameptr;
300	td = cnp->cn_thread;
301	flags = cnp->cn_flags;
302	nameiop = cnp->cn_nameiop;
303	dmp = VFSTODEVFS(dvp->v_mount);
304	cloned = 0;
305	dd = dvp->v_data;
306
307	*vpp = NULLVP;
308	cnp->cn_flags &= ~PDIRUNLOCK;
309
310	if ((flags & ISLASTCN) && nameiop == RENAME)
311		return (EOPNOTSUPP);
312
313	if (dvp->v_type != VDIR)
314		return (ENOTDIR);
315
316	if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT))
317		return (EIO);
318
319	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td);
320	if (error)
321		return (error);
322
323	if (cnp->cn_namelen == 1 && *pname == '.') {
324		if ((flags & ISLASTCN) && nameiop != LOOKUP)
325			return (EINVAL);
326		*vpp = dvp;
327		VREF(dvp);
328		return (0);
329	}
330
331	if (flags & ISDOTDOT) {
332		if ((flags & ISLASTCN) && nameiop != LOOKUP)
333			return (EINVAL);
334		VOP_UNLOCK(dvp, 0, td);
335		cnp->cn_flags |= PDIRUNLOCK;
336		de = TAILQ_FIRST(&dd->de_dlist);	/* "." */
337		de = TAILQ_NEXT(de, de_list);		/* ".." */
338		de = de->de_dir;
339		error = devfs_allocv(de, dvp->v_mount, vpp, td);
340		if (error || ((flags & LOCKPARENT) && (flags & ISLASTCN))) {
341			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
342			cnp->cn_flags &= ~PDIRUNLOCK;
343		}
344		return (error);
345	}
346
347	devfs_populate(dmp);
348	dd = dvp->v_data;
349	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
350		if (cnp->cn_namelen != de->de_dirent->d_namlen)
351			continue;
352		if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name,
353		    de->de_dirent->d_namlen) != 0)
354			continue;
355		if (de->de_flags & DE_WHITEOUT)
356			goto notfound;
357		goto found;
358	}
359
360	if (nameiop == DELETE)
361		goto notfound;
362
363	/*
364	 * OK, we didn't have an entry for the name we were asked for
365	 * so we try to see if anybody can create it on demand.
366	 */
367	pname = devfs_fqpn(specname, dvp, cnp);
368	if (pname == NULL)
369		goto notfound;
370
371	cdev = NODEV;
372	EVENTHANDLER_INVOKE(dev_clone, pname, strlen(pname), &cdev);
373	if (cdev == NODEV)
374		goto notfound;
375
376	devfs_populate(dmp);
377	dd = dvp->v_data;
378
379	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
380		cpdev = devfs_itod(de->de_inode);
381		if (cpdev != NULL && cdev == *cpdev)
382			goto found;
383		continue;
384	}
385
386notfound:
387
388	if ((nameiop == CREATE || nameiop == RENAME) &&
389	    (flags & (LOCKPARENT | WANTPARENT)) && (flags & ISLASTCN)) {
390		cnp->cn_flags |= SAVENAME;
391		if (!(flags & LOCKPARENT)) {
392			VOP_UNLOCK(dvp, 0, td);
393			cnp->cn_flags |= PDIRUNLOCK;
394		}
395		return (EJUSTRETURN);
396	}
397	return (ENOENT);
398
399
400found:
401
402	if ((cnp->cn_nameiop == DELETE) && (flags & ISLASTCN)) {
403		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
404		if (error)
405			return (error);
406		if (*vpp == dvp) {
407			VREF(dvp);
408			*vpp = dvp;
409			return (0);
410		}
411		error = devfs_allocv(de, dvp->v_mount, vpp, td);
412		if (error)
413			return (error);
414		if (!(flags & LOCKPARENT)) {
415			VOP_UNLOCK(dvp, 0, td);
416			cnp->cn_flags |= PDIRUNLOCK;
417		}
418		return (0);
419	}
420	error = devfs_allocv(de, dvp->v_mount, vpp, td);
421	if (error)
422		return (error);
423	if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) {
424		VOP_UNLOCK(dvp, 0, td);
425		cnp->cn_flags |= PDIRUNLOCK;
426	}
427	return (0);
428}
429
430static int
431devfs_lookup(struct vop_lookup_args *ap)
432{
433	int j;
434	struct devfs_mount *dmp;
435
436	dmp = VFSTODEVFS(ap->a_dvp->v_mount);
437	lockmgr(&dmp->dm_lock, LK_SHARED, 0, curthread);
438	j = devfs_lookupx(ap);
439	lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curthread);
440	return (j);
441}
442
443static int
444devfs_mknod(struct vop_mknod_args *ap)
445/*
446struct vop_mknod_args {
447        struct vnodeop_desc *a_desc;
448        struct vnode *a_dvp;
449        struct vnode **a_vpp;
450        struct componentname *a_cnp;
451        struct vattr *a_vap;
452};
453*/
454{
455	struct componentname *cnp;
456	struct vnode *dvp, **vpp;
457	struct thread *td;
458	struct devfs_dirent *dd, *de;
459	struct devfs_mount *dmp;
460	int cloned, flags, nameiop;
461	int error;
462
463	dvp = ap->a_dvp;
464	dmp = VFSTODEVFS(dvp->v_mount);
465	lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curthread);
466
467	cnp = ap->a_cnp;
468	vpp = ap->a_vpp;
469	td = cnp->cn_thread;
470	flags = cnp->cn_flags;
471	nameiop = cnp->cn_nameiop;
472	cloned = 0;
473	dd = dvp->v_data;
474
475	error = ENOENT;
476	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
477		if (cnp->cn_namelen != de->de_dirent->d_namlen)
478			continue;
479		if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name,
480		    de->de_dirent->d_namlen) != 0)
481			continue;
482		if (de->de_flags & DE_WHITEOUT)
483			break;
484		goto notfound;
485	}
486	if (de == NULL)
487		goto notfound;
488	de->de_flags &= ~DE_WHITEOUT;
489	error = devfs_allocv(de, dvp->v_mount, vpp, td);
490notfound:
491	lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curthread);
492	return (error);
493}
494
495
496static int
497devfs_pathconf(ap)
498	struct vop_pathconf_args /* {
499		struct vnode *a_vp;
500		int a_name;
501		int *a_retval;
502	} */ *ap;
503{
504
505	switch (ap->a_name) {
506	case _PC_NAME_MAX:
507		*ap->a_retval = NAME_MAX;
508		return (0);
509	case _PC_PATH_MAX:
510		*ap->a_retval = PATH_MAX;
511		return (0);
512	case _POSIX_MAC_PRESENT:
513#ifdef MAC
514		/*
515		 * If MAC is enabled, devfs automatically supports
516		 * trivial non-persistant label storage.
517		 */
518		*ap->a_retval = 1;
519#else
520		*ap->a_retval = 0;
521#endif /* MAC */
522		return (0);
523	default:
524		return (vop_stdpathconf(ap));
525	}
526	/* NOTREACHED */
527}
528
529/* ARGSUSED */
530static int
531devfs_print(ap)
532	struct vop_print_args /* {
533		struct vnode *a_vp;
534	} */ *ap;
535{
536
537	printf("tag %s, devfs vnode\n", ap->a_vp->v_tag);
538	return (0);
539}
540
541static int
542devfs_read(ap)
543	struct vop_read_args /* {
544		struct vnode *a_vp;
545		struct uio *a_uio;
546		int a_ioflag;
547		struct ucred *a_cred;
548	} */ *ap;
549{
550
551	if (ap->a_vp->v_type != VDIR)
552		return (EINVAL);
553	return (VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred, NULL, NULL, NULL));
554}
555
556static int
557devfs_readdir(ap)
558	struct vop_readdir_args /* {
559		struct vnode *a_vp;
560		struct uio *a_uio;
561		struct ucred *a_cred;
562		int *a_eofflag;
563		int *a_ncookies;
564		u_long **a_cookies;
565	} */ *ap;
566{
567	int error;
568	struct uio *uio;
569	struct dirent *dp;
570	struct devfs_dirent *dd;
571	struct devfs_dirent *de;
572	struct devfs_mount *dmp;
573	off_t off, oldoff;
574	int ncookies = 0;
575	u_long *cookiebuf, *cookiep;
576	struct dirent *dps, *dpe;
577
578	if (ap->a_vp->v_type != VDIR)
579		return (ENOTDIR);
580
581	uio = ap->a_uio;
582	if (uio->uio_offset < 0)
583		return (EINVAL);
584
585	dmp = VFSTODEVFS(ap->a_vp->v_mount);
586	lockmgr(&dmp->dm_lock, LK_SHARED, 0, curthread);
587	devfs_populate(dmp);
588	error = 0;
589	de = ap->a_vp->v_data;
590	off = 0;
591	oldoff = uio->uio_offset;
592	TAILQ_FOREACH(dd, &de->de_dlist, de_list) {
593		if (dd->de_flags & DE_WHITEOUT)
594			continue;
595		if (dd->de_dirent->d_type == DT_DIR)
596			de = dd->de_dir;
597		else
598			de = dd;
599		dp = dd->de_dirent;
600		if (dp->d_reclen > uio->uio_resid)
601			break;
602		dp->d_fileno = de->de_inode;
603		if (off >= uio->uio_offset) {
604			ncookies++;
605			error = uiomove((caddr_t)dp, dp->d_reclen, uio);
606			if (error)
607				break;
608		}
609		off += dp->d_reclen;
610	}
611	if( !error && ap->a_ncookies != NULL && ap->a_cookies != NULL ) {
612		MALLOC(cookiebuf, u_long *, ncookies * sizeof(u_long),
613                       M_TEMP, M_WAITOK);
614		cookiep = cookiebuf;
615		dps = (struct dirent *)
616			(uio->uio_iov->iov_base - (uio->uio_offset - oldoff));
617		dpe = (struct dirent *) uio->uio_iov->iov_base;
618		for( dp = dps;
619			dp < dpe;
620			dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
621				oldoff += dp->d_reclen;
622				*cookiep++ = (u_long) oldoff;
623		}
624		*ap->a_ncookies = ncookies;
625		*ap->a_cookies = cookiebuf;
626    }
627	lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curthread);
628	uio->uio_offset = off;
629	return (error);
630}
631
632static int
633devfs_readlink(ap)
634	struct vop_readlink_args /* {
635		struct vnode *a_vp;
636		struct uio *a_uio;
637		struct ucred *a_cead;
638	} */ *ap;
639{
640	int error;
641	struct devfs_dirent *de;
642
643	de = ap->a_vp->v_data;
644	error = uiomove(de->de_symlink, strlen(de->de_symlink), ap->a_uio);
645	return (error);
646}
647
648static int
649devfs_reclaim(ap)
650	struct vop_reclaim_args /* {
651		struct vnode *a_vp;
652	} */ *ap;
653{
654	struct vnode *vp = ap->a_vp;
655	struct devfs_dirent *de;
656	int i;
657
658	de = vp->v_data;
659	if (de != NULL)
660		de->de_vnode = NULL;
661	vp->v_data = NULL;
662	if (vp->v_rdev != NODEV && vp->v_rdev != NULL) {
663		i = vcount(vp);
664		if ((vp->v_rdev->si_flags & SI_CHEAPCLONE) && i == 0 &&
665		    (vp->v_rdev->si_flags & SI_NAMED))
666			destroy_dev(vp->v_rdev);
667	}
668	return (0);
669}
670
671#ifdef MAC
672static int
673devfs_refreshlabel(ap)
674	struct vop_refreshlabel_args /* {
675		struct vnode *a_vp;
676		struct ucred *a_cred;
677	} */ *ap;
678{
679
680	/* Labels are always in sync. */
681	return (0);
682}
683#endif
684
685static int
686devfs_remove(ap)
687	struct vop_remove_args /* {
688		struct vnode *a_dvp;
689		struct vnode *a_vp;
690		struct componentname *a_cnp;
691	} */ *ap;
692{
693	struct vnode *vp = ap->a_vp;
694	struct devfs_dirent *dd;
695	struct devfs_dirent *de;
696	struct devfs_mount *dmp = VFSTODEVFS(vp->v_mount);
697
698	lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curthread);
699	dd = ap->a_dvp->v_data;
700	de = vp->v_data;
701	if (de->de_dirent->d_type == DT_LNK) {
702		TAILQ_REMOVE(&dd->de_dlist, de, de_list);
703		if (de->de_vnode)
704			de->de_vnode->v_data = NULL;
705#ifdef MAC
706		mac_destroy_devfsdirent(de);
707#endif
708		FREE(de, M_DEVFS);
709	} else {
710		de->de_flags |= DE_WHITEOUT;
711	}
712	lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curthread);
713	return (0);
714}
715
716/*
717 * Revoke is called on a tty when a terminal session ends.  The vnode
718 * is orphaned by setting v_op to deadfs so we need to let go of it
719 * as well so that we create a new one next time around.
720 */
721static int
722devfs_revoke(ap)
723	struct vop_revoke_args /* {
724		struct vnode *a_vp;
725		int a_flags;
726	} */ *ap;
727{
728	struct vnode *vp = ap->a_vp;
729	struct devfs_dirent *de;
730
731	de = vp->v_data;
732	de->de_vnode = NULL;
733	vop_revoke(ap);
734	return (0);
735}
736
737static int
738devfs_setattr(ap)
739	struct vop_setattr_args /* {
740		struct vnode *a_vp;
741		struct vattr *a_vap;
742		struct ucred *a_cred;
743		struct proc *a_p;
744	} */ *ap;
745{
746	struct devfs_dirent *de;
747	struct vattr *vap;
748	struct vnode *vp;
749	int c, error;
750	uid_t uid;
751	gid_t gid;
752
753	vap = ap->a_vap;
754	vp = ap->a_vp;
755	if ((vap->va_type != VNON) ||
756	    (vap->va_nlink != VNOVAL) ||
757	    (vap->va_fsid != VNOVAL) ||
758	    (vap->va_fileid != VNOVAL) ||
759	    (vap->va_blocksize != VNOVAL) ||
760	    (vap->va_flags != VNOVAL && vap->va_flags != 0) ||
761	    (vap->va_rdev != VNOVAL) ||
762	    ((int)vap->va_bytes != VNOVAL) ||
763	    (vap->va_gen != VNOVAL)) {
764		return (EINVAL);
765	}
766
767	de = vp->v_data;
768	if (vp->v_type == VDIR)
769		de = de->de_dir;
770
771	error = c = 0;
772	if (vap->va_uid == (uid_t)VNOVAL)
773		uid = de->de_uid;
774	else
775		uid = vap->va_uid;
776	if (vap->va_gid == (gid_t)VNOVAL)
777		gid = de->de_gid;
778	else
779		gid = vap->va_gid;
780	if (uid != de->de_uid || gid != de->de_gid) {
781		if (((ap->a_cred->cr_uid != de->de_uid) || uid != de->de_uid ||
782		    (gid != de->de_gid && !groupmember(gid, ap->a_cred))) &&
783		    (error = suser_cred(ap->a_td->td_ucred, PRISON_ROOT)) != 0)
784			return (error);
785		de->de_uid = uid;
786		de->de_gid = gid;
787		c = 1;
788	}
789
790	if (vap->va_mode != (mode_t)VNOVAL) {
791		if ((ap->a_cred->cr_uid != de->de_uid) &&
792		    (error = suser_cred(ap->a_td->td_ucred, PRISON_ROOT)))
793			return (error);
794		de->de_mode = vap->va_mode;
795		c = 1;
796	}
797
798	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
799		/* See the comment in ufs_vnops::ufs_setattr(). */
800		if ((error = VOP_ACCESS(vp, VADMIN, ap->a_cred, ap->a_td)) &&
801		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
802		    (error = VOP_ACCESS(vp, VWRITE, ap->a_cred, ap->a_td))))
803			return (error);
804		if (vap->va_atime.tv_sec != VNOVAL) {
805			if (vp->v_type == VCHR)
806				vp->v_rdev->si_atime = vap->va_atime;
807			else
808				de->de_atime = vap->va_atime;
809		}
810		if (vap->va_mtime.tv_sec != VNOVAL) {
811			if (vp->v_type == VCHR)
812				vp->v_rdev->si_mtime = vap->va_mtime;
813			else
814				de->de_mtime = vap->va_mtime;
815		}
816		c = 1;
817	}
818
819	if (c) {
820		if (vp->v_type == VCHR)
821			vfs_timestamp(&vp->v_rdev->si_ctime);
822		else
823			vfs_timestamp(&de->de_mtime);
824	}
825	return (0);
826}
827
828#ifdef MAC
829static int
830devfs_setlabel(ap)
831	struct vop_setlabel_args /* {
832		struct vnode *a_vp;
833		struct mac *a_label;
834		struct ucred *a_cred;
835		struct thread *a_td;
836	} */ *ap;
837{
838	struct vnode *vp;
839	struct devfs_dirent *de;
840
841	vp = ap->a_vp;
842	de = vp->v_data;
843
844	mac_relabel_vnode(ap->a_cred, vp, ap->a_label);
845	mac_update_devfsdirent(de, vp);
846
847	return (0);
848}
849#endif
850
851static int
852devfs_symlink(ap)
853	struct vop_symlink_args /* {
854		struct vnode *a_dvp;
855		struct vnode **a_vpp;
856		struct componentname *a_cnp;
857		struct vattr *a_vap;
858		char *a_target;
859	} */ *ap;
860{
861	int i, error;
862	struct devfs_dirent *dd;
863	struct devfs_dirent *de;
864	struct devfs_mount *dmp;
865
866	error = suser(ap->a_cnp->cn_thread);
867	if (error)
868		return(error);
869	dmp = VFSTODEVFS(ap->a_dvp->v_mount);
870	dd = ap->a_dvp->v_data;
871	de = devfs_newdirent(ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen);
872	de->de_uid = 0;
873	de->de_gid = 0;
874	de->de_mode = 0755;
875	de->de_inode = dmp->dm_inode++;
876	de->de_dirent->d_type = DT_LNK;
877	i = strlen(ap->a_target) + 1;
878	MALLOC(de->de_symlink, char *, i, M_DEVFS, M_WAITOK);
879	bcopy(ap->a_target, de->de_symlink, i);
880	lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curthread);
881	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
882	devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, 0);
883#ifdef MAC
884	mac_create_vnode(ap->a_cnp->cn_cred, ap->a_dvp, *ap->a_vpp);
885	mac_update_devfsdirent(de, *ap->a_vpp);
886#endif /* MAC */
887	lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curthread);
888	return (0);
889}
890
891static vop_t **devfs_vnodeop_p;
892static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
893	{ &vop_default_desc,		(vop_t *) vop_defaultop },
894	{ &vop_access_desc,		(vop_t *) devfs_access },
895	{ &vop_getattr_desc,		(vop_t *) devfs_getattr },
896	{ &vop_ioctl_desc,		(vop_t *) devfs_ioctl },
897	{ &vop_islocked_desc,		(vop_t *) vop_stdislocked },
898	{ &vop_lock_desc,		(vop_t *) vop_stdlock },
899	{ &vop_lookup_desc,		(vop_t *) devfs_lookup },
900	{ &vop_mknod_desc,		(vop_t *) devfs_mknod },
901	{ &vop_pathconf_desc,		(vop_t *) devfs_pathconf },
902	{ &vop_print_desc,		(vop_t *) devfs_print },
903	{ &vop_read_desc,		(vop_t *) devfs_read },
904	{ &vop_readdir_desc,		(vop_t *) devfs_readdir },
905	{ &vop_readlink_desc,		(vop_t *) devfs_readlink },
906	{ &vop_reclaim_desc,		(vop_t *) devfs_reclaim },
907	{ &vop_remove_desc,		(vop_t *) devfs_remove },
908#ifdef MAC
909	{ &vop_refreshlabel_desc,	(vop_t *) devfs_refreshlabel },
910#endif
911	{ &vop_revoke_desc,		(vop_t *) devfs_revoke },
912	{ &vop_setattr_desc,		(vop_t *) devfs_setattr },
913#ifdef MAC
914	{ &vop_setlabel_desc,		(vop_t *) devfs_setlabel },
915#endif
916	{ &vop_symlink_desc,		(vop_t *) devfs_symlink },
917	{ &vop_unlock_desc,		(vop_t *) vop_stdunlock },
918	{ NULL, NULL }
919};
920static struct vnodeopv_desc devfs_vnodeop_opv_desc =
921	{ &devfs_vnodeop_p, devfs_vnodeop_entries };
922
923VNODEOP_SET(devfs_vnodeop_opv_desc);
924
925static vop_t **devfs_specop_p;
926static struct vnodeopv_entry_desc devfs_specop_entries[] = {
927	{ &vop_default_desc,		(vop_t *) spec_vnoperate },
928	{ &vop_access_desc,		(vop_t *) devfs_access },
929	{ &vop_getattr_desc,		(vop_t *) devfs_getattr },
930	{ &vop_islocked_desc,		(vop_t *) vop_stdislocked },
931	{ &vop_lock_desc,		(vop_t *) vop_stdlock },
932	{ &vop_pathconf_desc,		(vop_t *) devfs_pathconf },
933	{ &vop_print_desc,		(vop_t *) devfs_print },
934	{ &vop_reclaim_desc,		(vop_t *) devfs_reclaim },
935#ifdef MAC
936	{ &vop_refreshlabel_desc,	(vop_t *) devfs_refreshlabel },
937#endif
938	{ &vop_remove_desc,		(vop_t *) devfs_remove },
939	{ &vop_revoke_desc,		(vop_t *) devfs_revoke },
940	{ &vop_setattr_desc,		(vop_t *) devfs_setattr },
941#ifdef MAC
942	{ &vop_setlabel_desc,		(vop_t *) devfs_setlabel },
943#endif
944	{ &vop_unlock_desc,		(vop_t *) vop_stdunlock },
945	{ NULL, NULL }
946};
947static struct vnodeopv_desc devfs_specop_opv_desc =
948	{ &devfs_specop_p, devfs_specop_entries };
949
950VNODEOP_SET(devfs_specop_opv_desc);
951#endif
952