pseudofs_vnops.c revision 84082
1/*-
2 * Copyright (c) 2001 Dag-Erling Co�dan Sm�rgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 *	$FreeBSD: head/sys/fs/pseudofs/pseudofs_vnops.c 84082 2001-09-28 12:36:54Z des $
29 */
30
31#include <sys/param.h>
32#include <sys/kernel.h>
33#include <sys/systm.h>
34#include <sys/ctype.h>
35#include <sys/dirent.h>
36#include <sys/lock.h>
37#include <sys/mount.h>
38#include <sys/mutex.h>
39#include <sys/namei.h>
40#include <sys/proc.h>
41#include <sys/sbuf.h>
42#include <sys/sx.h>
43#include <sys/sysctl.h>
44#include <sys/vnode.h>
45
46#include <fs/pseudofs/pseudofs.h>
47#include <fs/pseudofs/pseudofs_internal.h>
48
49/*
50 * Verify permissions
51 */
52static int
53pfs_access(struct vop_access_args *va)
54{
55	struct vnode *vn = va->a_vp;
56	struct vattr vattr;
57	int error;
58
59	error = VOP_GETATTR(vn, &vattr, va->a_cred, va->a_td);
60	if (error)
61		return (error);
62	error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid,
63	    vattr.va_gid, va->a_mode, va->a_cred, NULL);
64	return (error);
65}
66
67/*
68 * Close a file or directory
69 */
70static int
71pfs_close(struct vop_close_args *va)
72{
73	return (0);
74}
75
76/*
77 * Get file attributes
78 */
79static int
80pfs_getattr(struct vop_getattr_args *va)
81{
82	struct vnode *vn = va->a_vp;
83	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
84	struct pfs_node *pn = pvd->pvd_pn;
85	struct vattr *vap = va->a_vap;
86
87	VATTR_NULL(vap);
88	vap->va_type = vn->v_type;
89	vap->va_mode = pn->pn_mode;
90	vap->va_fileid = pn->pn_fileno;
91	vap->va_flags = 0;
92	vap->va_blocksize = PAGE_SIZE;
93	vap->va_bytes = vap->va_size = 0;
94	vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0];
95	vap->va_nlink = 1;
96	nanotime(&vap->va_ctime);
97	vap->va_atime = vap->va_mtime = vap->va_ctime;
98	vap->va_uid = pn->pn_uid;
99	vap->va_gid = pn->pn_gid;
100
101	return (0);
102}
103
104/*
105 * Look up a file or directory
106 */
107static int
108pfs_lookup(struct vop_lookup_args *va)
109{
110	struct vnode *vn = va->a_dvp;
111	struct vnode **vpp = va->a_vpp;
112	struct componentname *cnp = va->a_cnp;
113	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
114	struct pfs_node *pd = pvd->pvd_pn;
115	struct pfs_node *pn, *pdn = NULL;
116	pid_t pid = pvd->pvd_pid;
117	char *pname;
118	int error, i, namelen;
119
120	if (vn->v_type != VDIR)
121		return (ENOTDIR);
122
123	/* don't support CREATE, RENAME or DELETE */
124	if (cnp->cn_nameiop != LOOKUP)
125		return (EROFS);
126
127	/* shortcut */
128	if (cnp->cn_namelen >= PFS_NAMELEN)
129		return (ENOENT);
130
131	/* self */
132	namelen = cnp->cn_namelen;
133	pname = cnp->cn_nameptr;
134	if (namelen == 1 && *pname == '.') {
135		pn = pd;
136		*vpp = vn;
137		VREF(vn);
138		goto got_vnode;
139	}
140
141	/* parent */
142	if (cnp->cn_flags & ISDOTDOT) {
143		if (pd->pn_type == pfstype_root)
144			return (EIO);
145		KASSERT(pd->pn_parent, ("non-root directory has no parent"));
146		/*
147		 * This one is tricky.  Descendents of procdir nodes
148		 * inherit their parent's process affinity, but
149		 * there's no easy reverse mapping.  For simplicity,
150		 * we assume that if this node is a procdir, its
151		 * parent isn't (which is correct as long as
152		 * descendents of procdir nodes are never procdir
153		 * nodes themselves)
154		 */
155		if (pd->pn_type == pfstype_procdir)
156			pid = NO_PID;
157		return pfs_vncache_alloc(vn->v_mount, vpp, pd->pn_parent, pid);
158	}
159
160	/* named node */
161	for (pn = pd->pn_nodes; pn->pn_type; ++pn)
162		if (pn->pn_type == pfstype_procdir)
163			pdn = pn;
164		else if (pn->pn_name[namelen] == '\0'
165		    && bcmp(pname, pn->pn_name, namelen) == 0)
166			goto got_pnode;
167
168	/* process dependent node */
169	if ((pn = pdn) != NULL) {
170		pid = 0;
171		for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i)
172			if ((pid = pid * 10 + pname[i] - '0') > PID_MAX)
173				break;
174		if (i == cnp->cn_namelen)
175			goto got_pnode;
176	}
177
178	return (ENOENT);
179 got_pnode:
180	if (!pn->pn_parent)
181		pn->pn_parent = pd;
182	error = pfs_vncache_alloc(vn->v_mount, vpp, pn, pid);
183	if (error)
184		return error;
185 got_vnode:
186	if (cnp->cn_flags & MAKEENTRY)
187		cache_enter(vn, *vpp, cnp);
188	return (0);
189}
190
191/*
192 * Open a file or directory.
193 */
194static int
195pfs_open(struct vop_open_args *va)
196{
197	/* XXX */
198	return (0);
199}
200
201/*
202 * Read from a file
203 */
204static int
205pfs_read(struct vop_read_args *va)
206{
207	struct vnode *vn = va->a_vp;
208	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
209	struct pfs_node *pn = pvd->pvd_pn;
210	struct uio *uio = va->a_uio;
211	struct proc *proc = NULL;
212	struct sbuf *sb = NULL;
213	char *ps;
214	int error, xlen;
215
216	if (vn->v_type != VREG)
217		return (EINVAL);
218
219	if (pn->pn_flags & PFS_WRONLY)
220		return (EBADF);
221
222	if (pvd->pvd_pid != NO_PID) {
223		if ((proc = pfind(pvd->pvd_pid)) == NULL)
224			return (EIO);
225		_PHOLD(proc);
226		PROC_UNLOCK(proc);
227	}
228
229	if (pn->pn_flags & PFS_RAWRD) {
230		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
231		if (proc != NULL)
232			PRELE(proc);
233		return (error);
234	}
235
236	sb = sbuf_new(sb, NULL, uio->uio_offset + uio->uio_resid, 0);
237	if (sb == NULL) {
238		if (proc != NULL)
239			PRELE(proc);
240		return (EIO);
241	}
242
243	error = (pn->pn_func)(curthread, proc, pn, sb, uio);
244
245	if (proc != NULL)
246		PRELE(proc);
247
248	if (error) {
249		sbuf_delete(sb);
250		return (error);
251	}
252
253	/* XXX we should possibly detect and handle overflows */
254	sbuf_finish(sb);
255	ps = sbuf_data(sb) + uio->uio_offset;
256	xlen = sbuf_len(sb) - uio->uio_offset;
257	xlen = imin(xlen, uio->uio_resid);
258	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
259	sbuf_delete(sb);
260	return (error);
261}
262
263/*
264 * Iterate through directory entries
265 */
266static int
267pfs_iterate(struct pfs_info *pi, struct pfs_node **pn, struct proc **p)
268{
269	if ((*pn)->pn_type == pfstype_none)
270		return (-1);
271
272	if ((*pn)->pn_type != pfstype_procdir)
273		++*pn;
274
275	while ((*pn)->pn_type == pfstype_procdir) {
276		if (*p == NULL)
277			*p = LIST_FIRST(&allproc);
278		else
279			*p = LIST_NEXT(*p, p_list);
280		if (*p != NULL)
281			return (0);
282		++*pn;
283	}
284
285	if ((*pn)->pn_type == pfstype_none)
286		return (-1);
287
288	return (0);
289}
290
291/*
292 * Return directory entries.
293 */
294static int
295pfs_readdir(struct vop_readdir_args *va)
296{
297	struct vnode *vn = va->a_vp;
298	struct pfs_info *pi = (struct pfs_info *)vn->v_mount->mnt_data;
299	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
300	struct pfs_node *pd = pvd->pvd_pn;
301	struct pfs_node *pn;
302	struct dirent entry;
303	struct uio *uio;
304	struct proc *p;
305	off_t offset;
306	int error, i, resid;
307
308	if (vn->v_type != VDIR)
309		return (ENOTDIR);
310	uio = va->a_uio;
311
312	/* only allow reading entire entries */
313	offset = uio->uio_offset;
314	resid = uio->uio_resid;
315	if (offset < 0 || offset % PFS_DELEN != 0 || resid < PFS_DELEN)
316		return (EINVAL);
317
318	/* skip unwanted entries */
319	sx_slock(&allproc_lock);
320	for (pn = pd->pn_nodes, p = NULL; offset > 0; offset -= PFS_DELEN)
321		if (pfs_iterate(pi, &pn, &p) == -1)
322			break;
323
324	/* fill in entries */
325	entry.d_reclen = PFS_DELEN;
326	while (pfs_iterate(pi, &pn, &p) != -1 && resid > 0) {
327		if (!pn->pn_parent)
328			pn->pn_parent = pd;
329		if (!pn->pn_fileno)
330			pfs_fileno_alloc(pi, pn);
331		if (pvd->pvd_pid != NO_PID)
332			entry.d_fileno = pn->pn_fileno * NO_PID + pvd->pvd_pid;
333		else
334			entry.d_fileno = pn->pn_fileno;
335		/* PFS_DELEN was picked to fit PFS_NAMLEN */
336		for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i)
337			entry.d_name[i] = pn->pn_name[i];
338		entry.d_name[i] = 0;
339		entry.d_namlen = i;
340		switch (pn->pn_type) {
341		case pfstype_procdir:
342			KASSERT(p != NULL,
343			    ("reached procdir node with p == NULL"));
344			entry.d_fileno = pn->pn_fileno * NO_PID + p->p_pid;
345			entry.d_namlen = snprintf(entry.d_name,
346			    PFS_NAMELEN, "%d", p->p_pid);
347			/* fall through */
348		case pfstype_root:
349		case pfstype_dir:
350		case pfstype_this:
351		case pfstype_parent:
352			entry.d_type = DT_DIR;
353			break;
354		case pfstype_file:
355			entry.d_type = DT_REG;
356			break;
357		case pfstype_symlink:
358			entry.d_type = DT_LNK;
359			break;
360		default:
361			sx_sunlock(&allproc_lock);
362			panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
363		}
364		if ((error = uiomove((caddr_t)&entry, PFS_DELEN, uio))) {
365			sx_sunlock(&allproc_lock);
366			return (error);
367		}
368		offset += PFS_DELEN;
369		resid -= PFS_DELEN;
370	}
371
372	sx_sunlock(&allproc_lock);
373	uio->uio_offset += offset;
374	return (0);
375}
376
377/*
378 * Read a symbolic link
379 */
380static int
381pfs_readlink(struct vop_readlink_args *va)
382{
383	struct vnode *vn = va->a_vp;
384	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
385	struct pfs_node *pn = pvd->pvd_pn;
386	struct uio *uio = va->a_uio;
387	struct proc *proc = NULL;
388	char buf[MAXPATHLEN], *ps;
389	struct sbuf sb;
390	int error, xlen;
391
392	if (vn->v_type != VLNK)
393		return (EINVAL);
394
395	if (pvd->pvd_pid != NO_PID) {
396		if ((proc = pfind(pvd->pvd_pid)) == NULL)
397			return (EIO);
398		_PHOLD(proc);
399		PROC_UNLOCK(proc);
400	}
401
402	/* sbuf_new() can't fail with a static buffer */
403	sbuf_new(&sb, buf, sizeof buf, 0);
404
405	error = (pn->pn_func)(curthread, proc, pn, &sb, NULL);
406
407	if (proc != NULL)
408		PRELE(proc);
409
410	if (error) {
411		sbuf_delete(&sb);
412		return (error);
413	}
414
415	/* XXX we should detect and handle overflows */
416	sbuf_finish(&sb);
417	ps = sbuf_data(&sb) + uio->uio_offset;
418	xlen = sbuf_len(&sb) - uio->uio_offset;
419	xlen = imin(xlen, uio->uio_resid);
420	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
421	sbuf_delete(&sb);
422	return (error);
423}
424
425/*
426 * Reclaim a vnode
427 */
428static int
429pfs_reclaim(struct vop_reclaim_args *va)
430{
431	return (pfs_vncache_free(va->a_vp));
432}
433
434/*
435 * Set attributes
436 */
437static int
438pfs_setattr(struct vop_setattr_args *va)
439{
440	if (va->a_vap->va_flags != (u_long)VNOVAL)
441		return (EOPNOTSUPP);
442	/* XXX it's a bit more complex than that, really... */
443	return (0);
444}
445
446/*
447 * Read from a file
448 */
449static int
450pfs_write(struct vop_read_args *va)
451{
452	struct vnode *vn = va->a_vp;
453	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
454	struct pfs_node *pn = pvd->pvd_pn;
455	struct uio *uio = va->a_uio;
456	struct proc *proc = NULL;
457	int error;
458
459	if (vn->v_type != VREG)
460		return (EINVAL);
461
462	if (pn->pn_flags & PFS_RDONLY)
463		return (EBADF);
464
465	if (pvd->pvd_pid != NO_PID) {
466		if ((proc = pfind(pvd->pvd_pid)) == NULL)
467			return (EIO);
468		_PHOLD(proc);
469		PROC_UNLOCK(proc);
470	}
471
472	if (pn->pn_flags & PFS_RAWWR) {
473		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
474		if (proc != NULL)
475			PRELE(proc);
476		return (error);
477	}
478
479	/*
480	 * We currently don't support non-raw writers.  It's not very
481	 * hard to do, but it probably requires further changes to the
482	 * sbuf API.
483	 */
484	return (EIO);
485}
486
487/*
488 * Dummy operations */
489static int pfs_erofs(void *va)		{ return (EROFS); }
490#if 0
491static int pfs_null(void *va)		{ return (0); }
492#endif
493
494/*
495 * Vnode operations
496 */
497vop_t **pfs_vnodeop_p;
498static struct vnodeopv_entry_desc pfs_vnodeop_entries[] = {
499	{ &vop_default_desc,		(vop_t *)vop_defaultop	},
500	{ &vop_access_desc,		(vop_t *)pfs_access	},
501	{ &vop_close_desc,		(vop_t *)pfs_close	},
502	{ &vop_create_desc,		(vop_t *)pfs_erofs	},
503	{ &vop_getattr_desc,		(vop_t *)pfs_getattr	},
504	{ &vop_link_desc,		(vop_t *)pfs_erofs	},
505	{ &vop_lookup_desc,		(vop_t *)pfs_lookup	},
506	{ &vop_mkdir_desc,		(vop_t *)pfs_erofs	},
507	{ &vop_open_desc,		(vop_t *)pfs_open	},
508	{ &vop_read_desc,		(vop_t *)pfs_read	},
509	{ &vop_readdir_desc,		(vop_t *)pfs_readdir	},
510	{ &vop_readlink_desc,		(vop_t *)pfs_readlink	},
511	{ &vop_reclaim_desc,		(vop_t *)pfs_reclaim	},
512	{ &vop_remove_desc,		(vop_t *)pfs_erofs	},
513	{ &vop_rename_desc,		(vop_t *)pfs_erofs	},
514	{ &vop_rmdir_desc,		(vop_t *)pfs_erofs	},
515	{ &vop_setattr_desc,		(vop_t *)pfs_setattr	},
516	{ &vop_symlink_desc,		(vop_t *)pfs_erofs	},
517	{ &vop_write_desc,		(vop_t *)pfs_write	},
518	/* XXX I've probably forgotten a few that need pfs_erofs */
519	{ NULL,				(vop_t *)NULL		}
520};
521
522static struct vnodeopv_desc pfs_vnodeop_opv_desc =
523	{ &pfs_vnodeop_p, pfs_vnodeop_entries };
524
525VNODEOP_SET(pfs_vnodeop_opv_desc);
526
527