xref: /freebsd/sys/fs/devfs/devfs_vnops.c (revision 055aefb1bcd6c54859c45274c8e03f03b3f5e681)
1 #define DEBUG 1
2 /*
3  * Copyright (c) 1992, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  * Copyright (c) 2000
6  *	Poul-Henning Kamp.  All rights reserved.
7  *
8  * This code is derived from software donated to Berkeley by
9  * Jan-Simon Pendry.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *	@(#)kernfs_vnops.c	8.15 (Berkeley) 5/21/95
33  * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43
34  *
35  * $FreeBSD$
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/vmmeter.h>
42 #include <sys/time.h>
43 #include <sys/conf.h>
44 #include <sys/vnode.h>
45 #include <sys/malloc.h>
46 #include <sys/proc.h>
47 #include <sys/stat.h>
48 #include <sys/mount.h>
49 #include <sys/namei.h>
50 #include <sys/dirent.h>
51 #include <sys/resource.h>
52 #include <sys/eventhandler.h>
53 
54 #define DEVFS_INTERN
55 #include <fs/devfs/devfs.h>
56 
57 #define KSTRING	256		/* Largest I/O available via this filesystem */
58 #define	UIO_MX 32
59 
60 static int	devfs_access __P((struct vop_access_args *ap));
61 static int	devfs_badop __P((void));
62 static int	devfs_getattr __P((struct vop_getattr_args *ap));
63 static int	devfs_lookup __P((struct vop_lookup_args *ap));
64 static int	devfs_print __P((struct vop_print_args *ap));
65 static int	devfs_readdir __P((struct vop_readdir_args *ap));
66 static int	devfs_readlink __P((struct vop_readlink_args *ap));
67 static int	devfs_reclaim __P((struct vop_reclaim_args *ap));
68 static int	devfs_remove __P((struct vop_remove_args *ap));
69 static int	devfs_revoke __P((struct vop_revoke_args *ap));
70 static int	devfs_setattr __P((struct vop_setattr_args *ap));
71 static int	devfs_symlink __P((struct vop_symlink_args *ap));
72 
73 
74 static int
75 devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct proc *p)
76 {
77 	int error;
78 	struct vnode *vp;
79 
80 loop:
81 	vp = de->de_vnode;
82 	if (vp != NULL) {
83 		if (vget(vp, 0, p ? p : curproc))
84 			goto loop;
85 		*vpp = vp;
86 		return (0);
87 	}
88 	error = getnewvnode(VT_DEVFS, mp, devfs_vnodeop_p, &vp);
89 	if (error != 0) {
90 		printf("devfs_allocv: failed to allocate new vnode\n");
91 		return (error);
92 	}
93 
94 	if (de->de_dirent->d_type == DT_CHR) {
95 		vp->v_type = VCHR;
96 		vp = addaliasu(vp, devfs_inot[de->de_inode]->si_udev);
97 		vp->v_op = devfs_specop_p;
98 		vp->v_data = de;
99 	} else if (de->de_dirent->d_type == DT_DIR) {
100 		vp->v_type = VDIR;
101 		vp->v_data = de->de_dir;
102 		TAILQ_FIRST(&de->de_dir->dd_list)->de_vnode = vp;
103 	} else if (de->de_dirent->d_type == DT_LNK) {
104 		vp->v_type = VLNK;
105 		vp->v_data = de;
106 	} else {
107 		vp->v_type = VBAD;
108 		vp->v_data = de;
109 	}
110 	de->de_vnode = vp;
111 	vhold(vp);
112 	*vpp = vp;
113 	return (0);
114 }
115 /*
116  * vp is the current namei directory
117  * ndp is the name to locate in that directory...
118  */
119 static int
120 devfs_lookup(ap)
121 	struct vop_lookup_args /* {
122 		struct vnode * a_dvp;
123 		struct vnode ** a_vpp;
124 		struct componentname * a_cnp;
125 	} */ *ap;
126 {
127 	struct componentname *cnp = ap->a_cnp;
128 	struct vnode **vpp = ap->a_vpp;
129 	struct vnode *dvp = ap->a_dvp;
130 	char *pname = cnp->cn_nameptr;
131 	struct proc *p = cnp->cn_proc;
132 	struct devfs_dir *dd;
133 	struct devfs_dirent *de;
134 	struct devfs_mount *fmp;
135 	dev_t cdev;
136 	int error, cloned;
137 
138 	*vpp = NULLVP;
139 
140 	VOP_UNLOCK(dvp, 0, p);
141 	if (cnp->cn_namelen == 1 && *pname == '.') {
142 		*vpp = dvp;
143 		VREF(dvp);
144 		vn_lock(dvp, LK_SHARED | LK_RETRY, p);
145 		return (0);
146 	}
147 
148 	cloned = 0;
149 
150 	fmp = (struct devfs_mount *)dvp->v_mount->mnt_data;
151 again:
152 
153 	devfs_populate(fmp);
154 	dd = dvp->v_data;
155 	TAILQ_FOREACH(de, &dd->dd_list, de_list) {
156 		if (cnp->cn_namelen != de->de_dirent->d_namlen)
157 			continue;
158 		if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name, de->de_dirent->d_namlen) != 0)
159 			continue;
160 		goto found;
161 	}
162 
163 	if (!cloned) {
164 		/* OK, we didn't have that one, so lets try to create it on the fly... */
165 		cdev = NODEV;
166 		EVENTHANDLER_INVOKE(devfs_clone, cnp->cn_nameptr, cnp->cn_namelen, &cdev);
167 #if 0
168 		printf("cloned %s -> %p %s\n", cnp->cn_nameptr, cdev,
169 		    cdev == NODEV ? "NODEV" : cdev->si_name);
170 #endif
171 		if (cdev != NODEV) {
172 			cloned = 1;
173 			goto again;
174 		}
175 	}
176 
177 	/* No luck, too bad. */
178 
179 	if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
180 	    (cnp->cn_flags & ISLASTCN)) {
181 		cnp->cn_flags |= SAVENAME;
182 		if (!(cnp->cn_flags & LOCKPARENT))
183 			VOP_UNLOCK(dvp, 0, p);
184 		return (EJUSTRETURN);
185 	} else {
186 		vn_lock(dvp, LK_SHARED | LK_RETRY, p);
187 		return (ENOENT);
188 	}
189 
190 
191 found:
192 
193 	error = devfs_allocv(de, dvp->v_mount, vpp, p);
194 	if (error != 0) {
195 		vn_lock(dvp, LK_SHARED | LK_RETRY, p);
196 		return (error);
197 	}
198 	if ((cnp->cn_nameiop == DELETE) && (cnp->cn_flags & ISLASTCN)) {
199 		if (*vpp == dvp) {
200 			VREF(dvp);
201 			*vpp = dvp;
202 			return (0);
203 		}
204 		VREF(*vpp);
205 		if (!(cnp->cn_flags & LOCKPARENT))
206 			VOP_UNLOCK(dvp, 0, p);
207 		return (0);
208 	}
209 	vn_lock(*vpp, LK_SHARED | LK_RETRY, p);
210 	if (!(cnp->cn_flags & LOCKPARENT))
211 		VOP_UNLOCK(dvp, 0, p);
212 	return (0);
213 }
214 
215 static int
216 devfs_access(ap)
217 	struct vop_access_args /* {
218 		struct vnode *a_vp;
219 		int  a_mode;
220 		struct ucred *a_cred;
221 		struct proc *a_p;
222 	} */ *ap;
223 {
224 	struct vnode *vp = ap->a_vp;
225 	mode_t amode = ap->a_mode;
226 	struct devfs_dirent *de = vp->v_data;
227 	mode_t fmode = de->de_mode;
228 
229 	/* Some files are simply not modifiable. */
230 	if ((amode & VWRITE) && (fmode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0)
231 		return (EPERM);
232 
233 	return (vaccess(vp->v_type, de->de_mode, de->de_uid, de->de_gid,
234 	    ap->a_mode, ap->a_cred));
235 }
236 
237 static int
238 devfs_getattr(ap)
239 	struct vop_getattr_args /* {
240 		struct vnode *a_vp;
241 		struct vattr *a_vap;
242 		struct ucred *a_cred;
243 		struct proc *a_p;
244 	} */ *ap;
245 {
246 	struct vnode *vp = ap->a_vp;
247 	struct vattr *vap = ap->a_vap;
248 	int error = 0;
249 	struct devfs_dirent *de;
250 	struct devfs_dir *dd;
251 
252 	if (vp->v_type == VDIR) {
253 		dd = vp->v_data;
254 		de = TAILQ_FIRST(&dd->dd_list);
255 	} else {
256 		de = vp->v_data;
257 	}
258 	bzero((caddr_t) vap, sizeof(*vap));
259 	vattr_null(vap);
260 	vap->va_uid = de->de_uid;
261 	vap->va_gid = de->de_gid;
262 	vap->va_mode = de->de_mode;
263 	vap->va_size = 0;
264 	vap->va_blocksize = DEV_BSIZE;
265 	vap->va_atime = de->de_atime;
266 	vap->va_mtime = de->de_mtime;
267 	vap->va_ctime = de->de_ctime;
268 	vap->va_gen = 0;
269 	vap->va_flags = 0;
270 	vap->va_rdev = 0;
271 	vap->va_bytes = 0;
272 	vap->va_nlink = 1;
273 	vap->va_fileid = de->de_inode;
274 
275 #if 0
276 	if (vp->v_flag & VROOT) {
277 #ifdef DEBUG
278 		printf("devfs_getattr: stat rootdir\n");
279 #endif
280 		vap->va_type = VDIR;
281 		vap->va_nlink = 2;
282 		vap->va_fileid = 2;
283 		vap->va_size = DEV_BSIZE;
284 	} else
285 #endif
286 
287 	if (de->de_dirent->d_type == DT_DIR) {
288 		vap->va_type = VDIR;
289 	} else if (de->de_dirent->d_type == DT_LNK) {
290 		vap->va_type = VLNK;
291 	} else if (de->de_dirent->d_type == DT_CHR) {
292 		vap->va_type = VCHR;
293 		vap->va_rdev = devfs_inot[de->de_inode]->si_udev;
294 	}
295 
296 #ifdef DEBUG
297 	if (error)
298 		printf("devfs_getattr: return error %d\n", error);
299 #endif
300 	return (error);
301 }
302 
303 static int
304 devfs_setattr(ap)
305 	struct vop_setattr_args /* {
306 		struct vnode *a_vp;
307 		struct vattr *a_vap;
308 		struct ucred *a_cred;
309 		struct proc *a_p;
310 	} */ *ap;
311 {
312 	struct devfs_dir *dd;
313 	struct devfs_dirent *de;
314 
315 	if (ap->a_vp->v_type == VDIR) {
316 		dd = ap->a_vp->v_data;
317 		de = TAILQ_FIRST(&dd->dd_list);
318 	} else {
319 		de = ap->a_vp->v_data;
320 	}
321 
322 	if (ap->a_vap->va_flags != VNOVAL)
323 		return (EOPNOTSUPP);
324 	if (ap->a_vap->va_uid != (uid_t)VNOVAL)
325 		de->de_uid = ap->a_vap->va_uid;
326 	if (ap->a_vap->va_gid != (gid_t)VNOVAL)
327 		de->de_gid = ap->a_vap->va_gid;
328 	if (ap->a_vap->va_mode != (mode_t)VNOVAL)
329 		de->de_mode = ap->a_vap->va_mode;
330 	if (ap->a_vap->va_atime.tv_sec != VNOVAL)
331 		de->de_atime = ap->a_vap->va_atime;
332 	if (ap->a_vap->va_mtime.tv_sec != VNOVAL)
333 		de->de_mtime = ap->a_vap->va_mtime;
334 
335 	/*
336 	 * Silently ignore attribute changes.
337 	 * This allows for open with truncate to have no
338 	 * effect until some data is written.  I want to
339 	 * do it this way because all writes are atomic.
340 	 */
341 	return (0);
342 }
343 
344 static int
345 devfs_readdir(ap)
346 	struct vop_readdir_args /* {
347 		struct vnode *a_vp;
348 		struct uio *a_uio;
349 		struct ucred *a_cred;
350 		int *a_eofflag;
351 		int *a_ncookies;
352 		u_long **a_cookies;
353 	} */ *ap;
354 {
355 	int error, i;
356 	struct uio *uio = ap->a_uio;
357 	struct dirent *dp;
358 	struct devfs_dir *dd;
359 	struct devfs_dirent *de;
360 	off_t off;
361 
362 	if (ap->a_vp->v_type != VDIR)
363 		return (ENOTDIR);
364 
365 	i = (u_int)off / UIO_MX;
366 	error = 0;
367 	dd = ap->a_vp->v_data;
368 	de = TAILQ_FIRST(&dd->dd_list);
369 	off = 0;
370 	while (uio->uio_resid >= UIO_MX && de != NULL) {
371 		dp = de->de_dirent;
372 		dp->d_fileno = de->de_inode;
373 		if (off >= uio->uio_offset)
374 			if ((error = uiomove((caddr_t)dp, dp->d_reclen, uio)) != 0)
375 				break;
376 		off += dp->d_reclen;
377 		de = TAILQ_NEXT(de, de_list);
378 	}
379 
380 	uio->uio_offset = off;
381 
382 	return (error);
383 }
384 
385 static int
386 devfs_readlink(ap)
387 	struct vop_readlink_args /* {
388 		struct vnode *a_vp;
389 		struct uio *a_uio;
390 		struct ucred *a_cead;
391 	} */ *ap;
392 {
393 	int error;
394 	struct devfs_dirent *de;
395 
396 	de = ap->a_vp->v_data;
397 	error = uiomove(de->de_symlink, strlen(de->de_symlink) + 1, ap->a_uio);
398 	return (error);
399 }
400 
401 static int
402 devfs_reclaim(ap)
403 	struct vop_reclaim_args /* {
404 		struct vnode *a_vp;
405 	} */ *ap;
406 {
407 	struct vnode *vp = ap->a_vp;
408 
409 	vp->v_data = NULL;
410 	return (0);
411 }
412 
413 static int
414 devfs_remove(ap)
415 	struct vop_remove_args /* {
416 		struct vnode *a_dvp;
417 		struct vnode *a_vp;
418 		struct componentname *a_cnp;
419 	} */ *ap;
420 {
421 	struct vnode *vp = ap->a_vp;
422 	struct devfs_dir *dd;
423 	struct devfs_dirent *de;
424 
425 	dd = ap->a_dvp->v_data;
426 	de = vp->v_data;
427 	devfs_delete(dd, de);
428 	return (0);
429 }
430 
431 /*
432  * Revoke is called on a tty when a terminal session ends.  The vnode
433  * is orphaned by setting v_op to deadfs so we need to let go of it
434  * as well so that we create a new one next time around.
435  */
436 static int
437 devfs_revoke(ap)
438 	struct vop_revoke_args /* {
439 		struct vnode *a_vp;
440 		int a_flags;
441 	} */ *ap;
442 {
443 	struct vnode *vp = ap->a_vp;
444 	struct devfs_dirent *de;
445 
446 	de = vp->v_data;
447 	vdrop(de->de_vnode);
448 	de->de_vnode = NULL;
449 	vop_revoke(ap);
450 	return (0);
451 }
452 
453 static int
454 devfs_symlink(ap)
455 	struct vop_symlink_args /* {
456 		struct vnode *a_dvp;
457 		struct vnode **a_vpp;
458 		struct componentname *a_cnp;
459 		struct vattr *a_vap;
460 		char *a_target;
461 	} */ *ap;
462 {
463 	int i;
464 	struct devfs_dir *dd;
465 	struct devfs_dirent *de;
466 	struct devfs_mount *fmp;
467 
468 	fmp = (struct devfs_mount *)ap->a_dvp->v_mount->mnt_data;
469 	dd = ap->a_dvp->v_data;
470 	de = devfs_newdirent(ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen);
471 	de->de_uid = 0;
472 	de->de_gid = 0;
473 	de->de_mode = 0642;
474 	de->de_inode = fmp->dm_inode++;
475 	de->de_dirent->d_type = DT_LNK;
476 	i = strlen(ap->a_target) + 1;
477 	MALLOC(de->de_symlink, char *, i, M_DEVFS, M_WAITOK);
478 	bcopy(ap->a_target, de->de_symlink, i);
479 	TAILQ_INSERT_TAIL(&dd->dd_list, de, de_list);
480 	devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, 0);
481 	VREF(*(ap->a_vpp));
482 	return (0);
483 }
484 
485 /*
486  * Print out the contents of a devfs vnode.
487  */
488 /* ARGSUSED */
489 static int
490 devfs_print(ap)
491 	struct vop_print_args /* {
492 		struct vnode *a_vp;
493 	} */ *ap;
494 {
495 
496 	printf("tag VT_DEVFS, devfs vnode\n");
497 	return (0);
498 }
499 
500 /*
501  * Kernfs "should never get here" operation
502  */
503 static int
504 devfs_badop()
505 {
506 	return (EIO);
507 }
508 
509 vop_t	**devfs_vnodeop_p;
510 static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
511 	{ &vop_default_desc,		(vop_t *) vop_defaultop },
512 	{ &vop_access_desc,		(vop_t *) devfs_access },
513 	{ &vop_bmap_desc,		(vop_t *) devfs_badop },
514 	{ &vop_getattr_desc,		(vop_t *) devfs_getattr },
515 	{ &vop_lookup_desc,		(vop_t *) devfs_lookup },
516 	{ &vop_pathconf_desc,		(vop_t *) vop_stdpathconf },
517 	{ &vop_print_desc,		(vop_t *) devfs_print },
518 	{ &vop_readdir_desc,		(vop_t *) devfs_readdir },
519 	{ &vop_readlink_desc,		(vop_t *) devfs_readlink },
520 	{ &vop_reclaim_desc,		(vop_t *) devfs_reclaim },
521 	{ &vop_remove_desc,		(vop_t *) devfs_remove },
522 	{ &vop_revoke_desc,		(vop_t *) devfs_revoke },
523 	{ &vop_setattr_desc,		(vop_t *) devfs_setattr },
524 	{ &vop_symlink_desc,		(vop_t *) devfs_symlink },
525 	{ NULL, NULL }
526 };
527 static struct vnodeopv_desc devfs_vnodeop_opv_desc =
528 	{ &devfs_vnodeop_p, devfs_vnodeop_entries };
529 
530 VNODEOP_SET(devfs_vnodeop_opv_desc);
531 
532 #if 0
533 int
534 foo(ap)
535 	struct vop_generic_args *ap;
536 {
537 	int i;
538 
539 	i = spec_vnoperate(ap);
540 	printf("foo(%s) = %d\n", ap->a_desc->vdesc_name, i);
541 	return (i);
542 }
543 #endif
544 
545 vop_t	**devfs_specop_p;
546 static struct vnodeopv_entry_desc devfs_specop_entries[] = {
547 #if 1
548 	{ &vop_default_desc,		(vop_t *) spec_vnoperate },
549 #else
550 	{ &vop_default_desc,		(vop_t *) foo },
551 	{ &vop_lock_desc,		(vop_t *) spec_vnoperate },
552 	{ &vop_unlock_desc,		(vop_t *) spec_vnoperate },
553 	{ &vop_lease_desc,		(vop_t *) spec_vnoperate },
554 	{ &vop_strategy_desc,		(vop_t *) spec_vnoperate },
555 	{ &vop_bmap_desc,		(vop_t *) spec_vnoperate },
556 #endif
557 	{ &vop_access_desc,		(vop_t *) devfs_access },
558 	{ &vop_getattr_desc,		(vop_t *) devfs_getattr },
559 	{ &vop_print_desc,		(vop_t *) devfs_print },
560 	{ &vop_reclaim_desc,		(vop_t *) devfs_reclaim },
561 	{ &vop_remove_desc,		(vop_t *) devfs_remove },
562 	{ &vop_revoke_desc,		(vop_t *) devfs_revoke },
563 	{ &vop_setattr_desc,		(vop_t *) devfs_setattr },
564 	{ NULL, NULL }
565 };
566 static struct vnodeopv_desc devfs_specop_opv_desc =
567 	{ &devfs_specop_p, devfs_specop_entries };
568 
569 VNODEOP_SET(devfs_specop_opv_desc);
570