xref: /freebsd/sys/fs/pseudofs/pseudofs_vnops.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
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$
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/fcntl.h>
37 #include <sys/lock.h>
38 #include <sys/mount.h>
39 #include <sys/mutex.h>
40 #include <sys/namei.h>
41 #include <sys/proc.h>
42 #include <sys/sbuf.h>
43 #include <sys/sx.h>
44 #include <sys/sysctl.h>
45 #include <sys/vnode.h>
46 
47 #include <fs/pseudofs/pseudofs.h>
48 #include <fs/pseudofs/pseudofs_internal.h>
49 
50 #if 0
51 #define PFS_TRACE(foo) \
52 	do { \
53 		printf("pseudofs: %s(): line %d: ", __func__, __LINE__); \
54 		printf foo ; \
55 		printf("\n"); \
56 	} while (0)
57 #define PFS_RETURN(err) \
58 	do { \
59 		printf("pseudofs: %s(): line %d: returning %d\n", \
60 		    __func__, __LINE__, err); \
61 		return (err); \
62 	} while (0)
63 #else
64 #define PFS_TRACE(foo) \
65 	do { /* nothing */ } while (0)
66 #define PFS_RETURN(err) \
67 	return (err)
68 #endif
69 
70 /*
71  * Returns non-zero if given file is visible to given process
72  */
73 static int
74 pfs_visible(struct thread *td, struct pfs_node *pn, pid_t pid)
75 {
76 	struct proc *proc;
77 	int r;
78 
79 	PFS_TRACE(("%s (pid: %d, req: %d)",
80 	    pn->pn_name, pid, td->td_proc->p_pid));
81 
82 	if (pn->pn_flags & PFS_DISABLED)
83 		PFS_RETURN (0);
84 
85 	r = 1;
86 	if (pid != NO_PID) {
87 		if ((proc = pfind(pid)) == NULL)
88 			PFS_RETURN (0);
89 		/* XXX should lock td->td_proc? */
90 		if (p_cansee(td->td_proc, proc) != 0 ||
91 		    (pn->pn_vis != NULL && !(pn->pn_vis)(td, proc, pn)))
92 			r = 0;
93 		PROC_UNLOCK(proc);
94 	}
95 	PFS_RETURN (r);
96 }
97 
98 /*
99  * Verify permissions
100  */
101 static int
102 pfs_access(struct vop_access_args *va)
103 {
104 	struct vnode *vn = va->a_vp;
105 	struct vattr vattr;
106 	int error;
107 
108 	PFS_TRACE((((struct pfs_vdata *)vn->v_data)->pvd_pn->pn_name));
109 
110 	error = VOP_GETATTR(vn, &vattr, va->a_cred, va->a_td);
111 	if (error)
112 		PFS_RETURN (error);
113 	error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid,
114 	    vattr.va_gid, va->a_mode, va->a_cred, NULL);
115 	PFS_RETURN (error);
116 }
117 
118 /*
119  * Close a file or directory
120  */
121 static int
122 pfs_close(struct vop_close_args *va)
123 {
124 	struct vnode *vn = va->a_vp;
125 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
126 	struct pfs_node *pn = pvd->pvd_pn;
127 	struct proc *proc;
128 	int error;
129 
130 	PFS_TRACE((pn->pn_name));
131 
132 	/*
133 	 * Do nothing unless this is the last close and the node has a
134 	 * last-close handler.
135 	 */
136 	if (vn->v_usecount > 1 || pn->pn_close == NULL)
137 		PFS_RETURN (0);
138 
139 	if (pvd->pvd_pid != NO_PID)
140 		proc = pfind(pvd->pvd_pid);
141 	else
142 		proc = NULL;
143 
144 	error = (pn->pn_close)(va->a_td, proc, pn);
145 
146 	if (proc != NULL)
147 		PROC_UNLOCK(proc);
148 
149 	PFS_RETURN (error);
150 }
151 
152 /*
153  * Get file attributes
154  */
155 static int
156 pfs_getattr(struct vop_getattr_args *va)
157 {
158 	struct vnode *vn = va->a_vp;
159 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
160 	struct pfs_node *pn = pvd->pvd_pn;
161 	struct vattr *vap = va->a_vap;
162 	struct proc *proc;
163 	int error = 0;
164 
165 	PFS_TRACE((pn->pn_name));
166 
167 	VATTR_NULL(vap);
168 	vap->va_type = vn->v_type;
169 	vap->va_fileid = pn->pn_fileno;
170 	vap->va_flags = 0;
171 	vap->va_blocksize = PAGE_SIZE;
172 	vap->va_bytes = vap->va_size = 0;
173 	vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0];
174 	vap->va_nlink = 1;
175 	nanotime(&vap->va_ctime);
176 	vap->va_atime = vap->va_mtime = vap->va_ctime;
177 
178 	switch (pn->pn_type) {
179 	case pfstype_procdir:
180 	case pfstype_root:
181 	case pfstype_dir:
182 		vap->va_mode = 0555;
183 		break;
184 	case pfstype_file:
185 	case pfstype_symlink:
186 		vap->va_mode = 0444;
187 		break;
188 	default:
189 		printf("shouldn't be here!\n");
190 		vap->va_mode = 0;
191 		break;
192 	}
193 
194 	if (pvd->pvd_pid != NO_PID) {
195 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
196 			PFS_RETURN (ENOENT);
197 		vap->va_uid = proc->p_ucred->cr_ruid;
198 		vap->va_gid = proc->p_ucred->cr_rgid;
199 		if (pn->pn_attr != NULL)
200 			error = (pn->pn_attr)(va->a_td, proc, pn, vap);
201 		PROC_UNLOCK(proc);
202 	} else {
203 		vap->va_uid = 0;
204 		vap->va_gid = 0;
205 	}
206 
207 	PFS_RETURN (error);
208 }
209 
210 /*
211  * Perform an ioctl
212  */
213 static int
214 pfs_ioctl(struct vop_ioctl_args *va)
215 {
216 	struct vnode *vn = va->a_vp;
217 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
218 	struct pfs_node *pn = pvd->pvd_pn;
219 	struct proc *proc = NULL;
220 	int error;
221 
222 	PFS_TRACE(("%s: %lx", pn->pn_name, va->a_command));
223 
224 	if (vn->v_type != VREG)
225 		PFS_RETURN (EINVAL);
226 
227 	if (pn->pn_ioctl == NULL)
228 		PFS_RETURN (ENOTTY);
229 
230 	/*
231 	 * This is necessary because either process' privileges may
232 	 * have changed since the open() call.
233 	 */
234 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
235 		PFS_RETURN (EIO);
236 
237 	/* XXX duplicates bits of pfs_visible() */
238 	if (pvd->pvd_pid != NO_PID) {
239 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
240 			PFS_RETURN (EIO);
241 		_PHOLD(proc);
242 		PROC_UNLOCK(proc);
243 	}
244 
245 	error = (pn->pn_ioctl)(curthread, proc, pn, va->a_command, va->a_data);
246 
247 	if (proc != NULL)
248 		PRELE(proc);
249 
250 	PFS_RETURN (error);
251 }
252 
253 /*
254  * Perform getextattr
255  */
256 static int
257 pfs_getextattr(struct vop_getextattr_args *va)
258 {
259 	struct vnode *vn = va->a_vp;
260 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
261 	struct pfs_node *pn = pvd->pvd_pn;
262 	struct proc *proc = NULL;
263 	int error;
264 
265 	PFS_TRACE((pd->pn_name));
266 
267 	if (pn->pn_getextattr == NULL)
268 		PFS_RETURN (EOPNOTSUPP);
269 
270 	/*
271 	 * This is necessary because either process' privileges may
272 	 * have changed since the open() call.
273 	 */
274 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
275 		PFS_RETURN (EIO);
276 
277 	/* XXX duplicates bits of pfs_visible() */
278 	if (pvd->pvd_pid != NO_PID) {
279 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
280 			PFS_RETURN (EIO);
281 		_PHOLD(proc);
282 		PROC_UNLOCK(proc);
283 	}
284 
285 	error = (pn->pn_getextattr)(curthread, proc, pn, va->a_attrnamespace,
286 	    va->a_name, va->a_uio, va->a_size, va->a_cred);
287 
288 	if (proc != NULL)
289 		PRELE(proc);
290 
291 	PFS_RETURN (error);
292 }
293 
294 /*
295  * Look up a file or directory
296  *
297  * XXX NOTE!  pfs_lookup() has been hooked into vop_lookup_desc!  This
298  * will result in a lookup operation for a vnode which may already be
299  * cached, therefore we have to be careful to purge the VFS cache when
300  * reusing a vnode.
301  *
302  * This code will work, but is not really correct.  Normally we would hook
303  * vfs_cache_lookup() into vop_lookup_desc and hook pfs_lookup() into
304  * vop_cachedlookup_desc.
305  */
306 static int
307 pfs_lookup(struct vop_lookup_args *va)
308 {
309 	struct vnode *vn = va->a_dvp;
310 	struct vnode **vpp = va->a_vpp;
311 	struct componentname *cnp = va->a_cnp;
312 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
313 	struct pfs_node *pd = pvd->pvd_pn;
314 	struct pfs_node *pn, *pdn = NULL;
315 	pid_t pid = pvd->pvd_pid;
316 	char *pname;
317 	int error, i, namelen;
318 
319 	PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr));
320 
321 	if (vn->v_type != VDIR)
322 		PFS_RETURN (ENOTDIR);
323 
324 	/*
325 	 * Don't support DELETE or RENAME.  CREATE is supported so
326 	 * that O_CREAT will work, but the lookup will still fail if
327 	 * the file does not exist.
328 	 */
329 	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
330 		PFS_RETURN (EOPNOTSUPP);
331 
332 	/* shortcut: check if the name is too long */
333 	if (cnp->cn_namelen >= PFS_NAMELEN)
334 		PFS_RETURN (ENOENT);
335 
336 	/* check that parent directory is visisble... */
337 	if (!pfs_visible(curthread, pd, pvd->pvd_pid))
338 		PFS_RETURN (ENOENT);
339 
340 	/* self */
341 	namelen = cnp->cn_namelen;
342 	pname = cnp->cn_nameptr;
343 	if (namelen == 1 && *pname == '.') {
344 		pn = pd;
345 		*vpp = vn;
346 		VREF(vn);
347 		PFS_RETURN (0);
348 	}
349 
350 	/* parent */
351 	if (cnp->cn_flags & ISDOTDOT) {
352 		if (pd->pn_type == pfstype_root)
353 			PFS_RETURN (EIO);
354 		KASSERT(pd->pn_parent, ("non-root directory has no parent"));
355 		/*
356 		 * This one is tricky.  Descendents of procdir nodes
357 		 * inherit their parent's process affinity, but
358 		 * there's no easy reverse mapping.  For simplicity,
359 		 * we assume that if this node is a procdir, its
360 		 * parent isn't (which is correct as long as
361 		 * descendents of procdir nodes are never procdir
362 		 * nodes themselves)
363 		 */
364 		if (pd->pn_type == pfstype_procdir)
365 			pid = NO_PID;
366 		pn = pd->pn_parent;
367 		goto got_pnode;
368 	}
369 
370 	/* named node */
371 	for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next)
372 		if (pn->pn_type == pfstype_procdir)
373 			pdn = pn;
374 		else if (pn->pn_name[namelen] == '\0'
375 		    && bcmp(pname, pn->pn_name, namelen) == 0)
376 			goto got_pnode;
377 
378 	/* process dependent node */
379 	if ((pn = pdn) != NULL) {
380 		pid = 0;
381 		for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i)
382 			if ((pid = pid * 10 + pname[i] - '0') > PID_MAX)
383 				break;
384 		if (i == cnp->cn_namelen)
385 			goto got_pnode;
386 	}
387 
388 	PFS_RETURN (ENOENT);
389  got_pnode:
390 	if (pn != pd->pn_parent && !pn->pn_parent)
391 		pn->pn_parent = pd;
392 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
393 		PFS_RETURN (ENOENT);
394 	error = pfs_vncache_alloc(vn->v_mount, vpp, pn, pid);
395 	if (error)
396 		PFS_RETURN (error);
397 	/*
398 	 * XXX See comment at top of the routine.
399 	 */
400 	if (cnp->cn_flags & MAKEENTRY)
401 		cache_enter(vn, *vpp, cnp);
402 	PFS_RETURN (0);
403 }
404 
405 /*
406  * Open a file or directory.
407  */
408 static int
409 pfs_open(struct vop_open_args *va)
410 {
411 	struct vnode *vn = va->a_vp;
412 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
413 	struct pfs_node *pn = pvd->pvd_pn;
414 	int mode = va->a_mode;
415 
416 	PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode));
417 
418 	/*
419 	 * check if the file is visible to the caller
420 	 *
421 	 * XXX Not sure if this is necessary, as the VFS system calls
422          * XXX pfs_lookup() and pfs_access() first, and pfs_lookup()
423          * XXX calls pfs_visible().  There's a race condition here, but
424 	 * XXX calling pfs_visible() from here doesn't really close it,
425 	 * XXX and the only consequence of that race is an EIO further
426 	 * XXX down the line.
427 	 */
428 	if (!pfs_visible(va->a_td, pn, pvd->pvd_pid))
429 		PFS_RETURN (ENOENT);
430 
431 	/* check if the requested mode is permitted */
432 	if (((mode & FREAD) && !(mode & PFS_RD)) ||
433 	    ((mode & FWRITE) && !(mode & PFS_WR)))
434 		PFS_RETURN (EPERM);
435 
436 	/* we don't support locking */
437 	if ((mode & O_SHLOCK) || (mode & O_EXLOCK))
438 		PFS_RETURN (EOPNOTSUPP);
439 
440 	PFS_RETURN (0);
441 }
442 
443 /*
444  * Read from a file
445  */
446 static int
447 pfs_read(struct vop_read_args *va)
448 {
449 	struct vnode *vn = va->a_vp;
450 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
451 	struct pfs_node *pn = pvd->pvd_pn;
452 	struct uio *uio = va->a_uio;
453 	struct proc *proc = NULL;
454 	struct sbuf *sb = NULL;
455 	char *ps;
456 	int error, xlen;
457 
458 	PFS_TRACE((pn->pn_name));
459 
460 	if (vn->v_type != VREG)
461 		PFS_RETURN (EINVAL);
462 
463 	if (!(pn->pn_flags & PFS_RD))
464 		PFS_RETURN (EBADF);
465 
466 	if (pn->pn_func == NULL)
467 		PFS_RETURN (EIO);
468 
469 	/*
470 	 * This is necessary because either process' privileges may
471 	 * have changed since the open() call.
472 	 */
473 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
474 		PFS_RETURN (EIO);
475 
476 	/* XXX duplicates bits of pfs_visible() */
477 	if (pvd->pvd_pid != NO_PID) {
478 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
479 			PFS_RETURN (EIO);
480 		_PHOLD(proc);
481 		PROC_UNLOCK(proc);
482 	}
483 
484 	if (pn->pn_flags & PFS_RAWRD) {
485 		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
486 		if (proc != NULL)
487 			PRELE(proc);
488 		PFS_RETURN (error);
489 	}
490 
491 	sb = sbuf_new(sb, NULL, uio->uio_offset + uio->uio_resid, 0);
492 	if (sb == NULL) {
493 		if (proc != NULL)
494 			PRELE(proc);
495 		PFS_RETURN (EIO);
496 	}
497 
498 	error = (pn->pn_func)(curthread, proc, pn, sb, uio);
499 
500 	if (proc != NULL)
501 		PRELE(proc);
502 
503 	if (error) {
504 		sbuf_delete(sb);
505 		PFS_RETURN (error);
506 	}
507 
508 	/* XXX we should possibly detect and handle overflows */
509 	sbuf_finish(sb);
510 	ps = sbuf_data(sb) + uio->uio_offset;
511 	xlen = sbuf_len(sb) - uio->uio_offset;
512 	xlen = imin(xlen, uio->uio_resid);
513 	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
514 	sbuf_delete(sb);
515 	PFS_RETURN (error);
516 }
517 
518 /*
519  * Iterate through directory entries
520  */
521 static int
522 pfs_iterate(struct thread *td, pid_t pid, struct pfs_node *pd,
523             struct pfs_node **pn, struct proc **p)
524 {
525 	if ((*pn) == NULL)
526 		*pn = pd->pn_nodes;
527 	else
528  again:
529 	if ((*pn)->pn_type != pfstype_procdir)
530 		*pn = (*pn)->pn_next;
531 
532 	while (*pn != NULL && (*pn)->pn_type == pfstype_procdir) {
533 		if (*p == NULL)
534 			*p = LIST_FIRST(&allproc);
535 		else
536 			*p = LIST_NEXT(*p, p_list);
537 		if (*p != NULL)
538 			break;
539 		*pn = (*pn)->pn_next;
540 	}
541 
542 	if ((*pn) == NULL)
543 		return (-1);
544 
545 	if (!pfs_visible(td, *pn, *p ? (*p)->p_pid : pid))
546 		goto again;
547 
548 	return (0);
549 }
550 
551 /*
552  * Return directory entries.
553  */
554 static int
555 pfs_readdir(struct vop_readdir_args *va)
556 {
557 	struct vnode *vn = va->a_vp;
558 	struct pfs_info *pi = (struct pfs_info *)vn->v_mount->mnt_data;
559 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
560 	struct pfs_node *pd = pvd->pvd_pn;
561 	pid_t pid = pvd->pvd_pid;
562 	struct pfs_node *pn;
563 	struct dirent entry;
564 	struct uio *uio;
565 	struct proc *p;
566 	off_t offset;
567 	int error, i, resid;
568 
569 	PFS_TRACE((pd->pn_name));
570 
571 	if (vn->v_type != VDIR)
572 		PFS_RETURN (ENOTDIR);
573 	uio = va->a_uio;
574 
575 	/* check if the directory is visible to the caller */
576 	if (!pfs_visible(curthread, pd, pid))
577 		PFS_RETURN (ENOENT);
578 
579 	/* only allow reading entire entries */
580 	offset = uio->uio_offset;
581 	resid = uio->uio_resid;
582 	if (offset < 0 || offset % PFS_DELEN != 0 || resid < PFS_DELEN)
583 		PFS_RETURN (EINVAL);
584 
585 	/* skip unwanted entries */
586 	sx_slock(&allproc_lock);
587 	for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN)
588 		if (pfs_iterate(curthread, pid, pd, &pn, &p) == -1) {
589 			/* nothing left... */
590 			sx_sunlock(&allproc_lock);
591 			PFS_RETURN (0);
592 		}
593 
594 	/* fill in entries */
595 	entry.d_reclen = PFS_DELEN;
596 	while (pfs_iterate(curthread, pid, pd, &pn, &p) != -1 && resid > 0) {
597 		if (!pn->pn_parent)
598 			pn->pn_parent = pd;
599 		if (!pn->pn_fileno)
600 			pfs_fileno_alloc(pi, pn);
601 		if (pid != NO_PID)
602 			entry.d_fileno = pn->pn_fileno * NO_PID + pid;
603 		else
604 			entry.d_fileno = pn->pn_fileno;
605 		/* PFS_DELEN was picked to fit PFS_NAMLEN */
606 		for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i)
607 			entry.d_name[i] = pn->pn_name[i];
608 		entry.d_name[i] = 0;
609 		entry.d_namlen = i;
610 		switch (pn->pn_type) {
611 		case pfstype_procdir:
612 			KASSERT(p != NULL,
613 			    ("reached procdir node with p == NULL"));
614 			entry.d_fileno = pn->pn_fileno * NO_PID + p->p_pid;
615 			entry.d_namlen = snprintf(entry.d_name,
616 			    PFS_NAMELEN, "%d", p->p_pid);
617 			/* fall through */
618 		case pfstype_root:
619 		case pfstype_dir:
620 		case pfstype_this:
621 		case pfstype_parent:
622 			entry.d_type = DT_DIR;
623 			break;
624 		case pfstype_file:
625 			entry.d_type = DT_REG;
626 			break;
627 		case pfstype_symlink:
628 			entry.d_type = DT_LNK;
629 			break;
630 		default:
631 			sx_sunlock(&allproc_lock);
632 			panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
633 		}
634 		PFS_TRACE((entry.d_name));
635 		if ((error = uiomove((caddr_t)&entry, PFS_DELEN, uio))) {
636 			sx_sunlock(&allproc_lock);
637 			PFS_RETURN (error);
638 		}
639 		offset += PFS_DELEN;
640 		resid -= PFS_DELEN;
641 	}
642 
643 	sx_sunlock(&allproc_lock);
644 	uio->uio_offset += offset;
645 	PFS_RETURN (0);
646 }
647 
648 /*
649  * Read a symbolic link
650  */
651 static int
652 pfs_readlink(struct vop_readlink_args *va)
653 {
654 	struct vnode *vn = va->a_vp;
655 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
656 	struct pfs_node *pn = pvd->pvd_pn;
657 	struct uio *uio = va->a_uio;
658 	struct proc *proc = NULL;
659 	char buf[MAXPATHLEN], *ps;
660 	struct sbuf sb;
661 	int error, xlen;
662 
663 	PFS_TRACE((pn->pn_name));
664 
665 	if (vn->v_type != VLNK)
666 		PFS_RETURN (EINVAL);
667 
668 	if (pn->pn_func == NULL)
669 		PFS_RETURN (EIO);
670 
671 	if (pvd->pvd_pid != NO_PID) {
672 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
673 			PFS_RETURN (EIO);
674 		_PHOLD(proc);
675 		PROC_UNLOCK(proc);
676 	}
677 
678 	/* sbuf_new() can't fail with a static buffer */
679 	sbuf_new(&sb, buf, sizeof buf, 0);
680 
681 	error = (pn->pn_func)(curthread, proc, pn, &sb, NULL);
682 
683 	if (proc != NULL)
684 		PRELE(proc);
685 
686 	if (error) {
687 		sbuf_delete(&sb);
688 		PFS_RETURN (error);
689 	}
690 
691 	/* XXX we should detect and handle overflows */
692 	sbuf_finish(&sb);
693 	ps = sbuf_data(&sb) + uio->uio_offset;
694 	xlen = sbuf_len(&sb) - uio->uio_offset;
695 	xlen = imin(xlen, uio->uio_resid);
696 	error = (xlen <= 0 ? 0 : uiomove(ps, xlen, uio));
697 	sbuf_delete(&sb);
698 	PFS_RETURN (error);
699 }
700 
701 /*
702  * Reclaim a vnode
703  */
704 static int
705 pfs_reclaim(struct vop_reclaim_args *va)
706 {
707 	PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
708 
709 	return (pfs_vncache_free(va->a_vp));
710 }
711 
712 /*
713  * Set attributes
714  */
715 static int
716 pfs_setattr(struct vop_setattr_args *va)
717 {
718 	PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
719 
720 	PFS_RETURN (EOPNOTSUPP);
721 }
722 
723 /*
724  * Read from a file
725  */
726 static int
727 pfs_write(struct vop_read_args *va)
728 {
729 	struct vnode *vn = va->a_vp;
730 	struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
731 	struct pfs_node *pn = pvd->pvd_pn;
732 	struct uio *uio = va->a_uio;
733 	struct proc *proc = NULL;
734 	struct sbuf sb;
735 	int error;
736 
737 	PFS_TRACE((pn->pn_name));
738 
739 	if (vn->v_type != VREG)
740 		PFS_RETURN (EINVAL);
741 
742 	if (!(pn->pn_flags & PFS_WR))
743 		PFS_RETURN (EBADF);
744 
745 	if (pn->pn_func == NULL)
746 		PFS_RETURN (EIO);
747 
748 	/*
749 	 * This is necessary because either process' privileges may
750 	 * have changed since the open() call.
751 	 */
752 	if (!pfs_visible(curthread, pn, pvd->pvd_pid))
753 		PFS_RETURN (EIO);
754 
755 	/* XXX duplicates bits of pfs_visible() */
756 	if (pvd->pvd_pid != NO_PID) {
757 		if ((proc = pfind(pvd->pvd_pid)) == NULL)
758 			PFS_RETURN (EIO);
759 		_PHOLD(proc);
760 		PROC_UNLOCK(proc);
761 	}
762 
763 	if (pn->pn_flags & PFS_RAWWR) {
764 		error = (pn->pn_func)(curthread, proc, pn, NULL, uio);
765 		if (proc != NULL)
766 			PRELE(proc);
767 		PFS_RETURN (error);
768 	}
769 
770 	sbuf_uionew(&sb, uio, &error);
771 	if (error)
772 		PFS_RETURN (error);
773 
774 	error = (pn->pn_func)(curthread, proc, pn, &sb, uio);
775 
776 	if (proc != NULL)
777 		PRELE(proc);
778 
779 	sbuf_delete(&sb);
780 	PFS_RETURN (error);
781 }
782 
783 /*
784  * Dummy operations */
785 static int pfs_badop(void *va)		{ return (EOPNOTSUPP); }
786 #if 0
787 static int pfs_erofs(void *va)		{ return (EROFS); }
788 static int pfs_null(void *va)		{ return (0); }
789 #endif
790 
791 /*
792  * Vnode operations
793  */
794 vop_t **pfs_vnodeop_p;
795 static struct vnodeopv_entry_desc pfs_vnodeop_entries[] = {
796 	{ &vop_default_desc,		(vop_t *)vop_defaultop	},
797 	{ &vop_access_desc,		(vop_t *)pfs_access	},
798 	{ &vop_close_desc,		(vop_t *)pfs_close	},
799 	{ &vop_create_desc,		(vop_t *)pfs_badop	},
800 	{ &vop_getattr_desc,		(vop_t *)pfs_getattr	},
801 	{ &vop_getextattr_desc,		(vop_t *)pfs_getextattr	},
802 	{ &vop_ioctl_desc,		(vop_t *)pfs_ioctl	},
803 	{ &vop_link_desc,		(vop_t *)pfs_badop	},
804 	{ &vop_lookup_desc,		(vop_t *)pfs_lookup	},
805 	{ &vop_mkdir_desc,		(vop_t *)pfs_badop	},
806 	{ &vop_mknod_desc,		(vop_t *)pfs_badop	},
807 	{ &vop_open_desc,		(vop_t *)pfs_open	},
808 	{ &vop_read_desc,		(vop_t *)pfs_read	},
809 	{ &vop_readdir_desc,		(vop_t *)pfs_readdir	},
810 	{ &vop_readlink_desc,		(vop_t *)pfs_readlink	},
811 	{ &vop_reclaim_desc,		(vop_t *)pfs_reclaim	},
812 	{ &vop_remove_desc,		(vop_t *)pfs_badop	},
813 	{ &vop_rename_desc,		(vop_t *)pfs_badop	},
814 	{ &vop_rmdir_desc,		(vop_t *)pfs_badop	},
815 	{ &vop_setattr_desc,		(vop_t *)pfs_setattr	},
816 	{ &vop_symlink_desc,		(vop_t *)pfs_badop	},
817 	{ &vop_write_desc,		(vop_t *)pfs_write	},
818 	/* XXX I've probably forgotten a few that need pfs_badop */
819 	{ NULL,				(vop_t *)NULL		}
820 };
821 
822 static struct vnodeopv_desc pfs_vnodeop_opv_desc =
823 	{ &pfs_vnodeop_p, pfs_vnodeop_entries };
824 
825 VNODEOP_SET(pfs_vnodeop_opv_desc);
826