2001-04-07 19:51:12 +00:00
|
|
|
/*-
|
2012-01-15 13:23:18 +00:00
|
|
|
* Copyright (c) 2001 Dag-Erling Coïdan Smørgrav
|
2001-04-07 19:51:12 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer
|
|
|
|
* in this position and unchanged.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2005-03-14 15:54:11 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
#include "opt_pseudofs.h"
|
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/ctype.h>
|
|
|
|
#include <sys/dirent.h>
|
2001-09-30 19:41:29 +00:00
|
|
|
#include <sys/fcntl.h>
|
2003-10-02 15:00:55 +00:00
|
|
|
#include <sys/limits.h>
|
2001-06-11 15:04:48 +00:00
|
|
|
#include <sys/lock.h>
|
2005-03-14 16:21:32 +00:00
|
|
|
#include <sys/malloc.h>
|
2001-04-07 19:51:12 +00:00
|
|
|
#include <sys/mount.h>
|
2001-06-10 10:42:55 +00:00
|
|
|
#include <sys/mutex.h>
|
2001-04-07 19:51:12 +00:00
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/sbuf.h>
|
|
|
|
#include <sys/sx.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
|
|
|
|
#include <fs/pseudofs/pseudofs.h>
|
|
|
|
#include <fs/pseudofs/pseudofs_internal.h>
|
|
|
|
|
2009-04-07 16:13:10 +00:00
|
|
|
#define KASSERT_PN_IS_DIR(pn) \
|
|
|
|
KASSERT((pn)->pn_type == pfstype_root || \
|
|
|
|
(pn)->pn_type == pfstype_dir || \
|
|
|
|
(pn)->pn_type == pfstype_procdir, \
|
|
|
|
("%s(): VDIR vnode refers to non-directory pfs_node", __func__))
|
|
|
|
|
|
|
|
#define KASSERT_PN_IS_FILE(pn) \
|
|
|
|
KASSERT((pn)->pn_type == pfstype_file, \
|
|
|
|
("%s(): VREG vnode refers to non-file pfs_node", __func__))
|
|
|
|
|
|
|
|
#define KASSERT_PN_IS_LINK(pn) \
|
|
|
|
KASSERT((pn)->pn_type == pfstype_symlink, \
|
|
|
|
("%s(): VLNK vnode refers to non-link pfs_node", __func__))
|
|
|
|
|
2007-04-14 14:08:30 +00:00
|
|
|
/*
|
2007-04-15 17:10:01 +00:00
|
|
|
* Returns the fileno, adjusted for target pid
|
2007-04-14 14:08:30 +00:00
|
|
|
*/
|
|
|
|
static uint32_t
|
2007-04-15 17:10:01 +00:00
|
|
|
pn_fileno(struct pfs_node *pn, pid_t pid)
|
2007-04-14 14:08:30 +00:00
|
|
|
{
|
2007-04-15 17:10:01 +00:00
|
|
|
|
|
|
|
KASSERT(pn->pn_fileno > 0,
|
|
|
|
("%s(): no fileno allocated", __func__));
|
2007-04-14 14:08:30 +00:00
|
|
|
if (pid != NO_PID)
|
|
|
|
return (pn->pn_fileno * NO_PID + pid);
|
|
|
|
return (pn->pn_fileno);
|
|
|
|
}
|
|
|
|
|
2001-10-01 04:22:20 +00:00
|
|
|
/*
|
2007-04-15 17:10:01 +00:00
|
|
|
* Returns non-zero if given file is visible to given thread.
|
2001-10-01 04:22:20 +00:00
|
|
|
*/
|
2007-04-15 17:10:01 +00:00
|
|
|
static int
|
|
|
|
pfs_visible_proc(struct thread *td, struct pfs_node *pn, struct proc *proc)
|
|
|
|
{
|
|
|
|
int visible;
|
|
|
|
|
|
|
|
if (proc == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
PROC_LOCK_ASSERT(proc, MA_OWNED);
|
|
|
|
|
|
|
|
visible = ((proc->p_flag & P_WEXIT) == 0);
|
|
|
|
if (visible)
|
|
|
|
visible = (p_cansee(td, proc) == 0);
|
|
|
|
if (visible && pn->pn_vis != NULL)
|
|
|
|
visible = pn_vis(td, proc, pn);
|
|
|
|
if (!visible)
|
|
|
|
return (0);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2001-10-01 04:22:20 +00:00
|
|
|
static int
|
2006-02-22 17:24:54 +00:00
|
|
|
pfs_visible(struct thread *td, struct pfs_node *pn, pid_t pid, struct proc **p)
|
2001-10-01 04:22:20 +00:00
|
|
|
{
|
|
|
|
struct proc *proc;
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-10-01 04:22:20 +00:00
|
|
|
PFS_TRACE(("%s (pid: %d, req: %d)",
|
|
|
|
pn->pn_name, pid, td->td_proc->p_pid));
|
2001-10-02 22:22:42 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
if (p)
|
|
|
|
*p = NULL;
|
|
|
|
if (pid == NO_PID)
|
|
|
|
PFS_RETURN (1);
|
|
|
|
if ((proc = pfind(pid)) == NULL)
|
2001-10-02 22:22:42 +00:00
|
|
|
PFS_RETURN (0);
|
2007-04-15 17:10:01 +00:00
|
|
|
if (pfs_visible_proc(td, pn, proc)) {
|
|
|
|
if (p)
|
2006-02-22 17:24:54 +00:00
|
|
|
*p = proc;
|
2007-04-15 17:10:01 +00:00
|
|
|
else
|
2006-02-22 17:24:54 +00:00
|
|
|
PROC_UNLOCK(proc);
|
2007-04-15 17:10:01 +00:00
|
|
|
PFS_RETURN (1);
|
|
|
|
}
|
|
|
|
PROC_UNLOCK(proc);
|
|
|
|
PFS_RETURN (0);
|
2001-10-01 04:22:20 +00:00
|
|
|
}
|
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
/*
|
|
|
|
* Verify permissions
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_access(struct vop_access_args *va)
|
|
|
|
{
|
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-04-07 19:51:12 +00:00
|
|
|
struct vattr vattr;
|
|
|
|
int error;
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
PFS_TRACE(("%s", pvd->pvd_pn->pn_name));
|
2007-04-15 20:35:18 +00:00
|
|
|
(void)pvd;
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2008-08-28 15:23:18 +00:00
|
|
|
error = VOP_GETATTR(vn, &vattr, va->a_cred);
|
2001-04-07 19:51:12 +00:00
|
|
|
if (error)
|
2001-10-01 04:22:20 +00:00
|
|
|
PFS_RETURN (error);
|
2001-04-07 19:51:12 +00:00
|
|
|
error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid,
|
2008-10-28 13:44:11 +00:00
|
|
|
vattr.va_gid, va->a_accmode, va->a_cred, NULL);
|
2001-10-01 04:22:20 +00:00
|
|
|
PFS_RETURN (error);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close a file or directory
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_close(struct vop_close_args *va)
|
|
|
|
{
|
2001-11-27 13:26:27 +00:00
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-11-27 13:26:27 +00:00
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
|
|
|
struct proc *proc;
|
|
|
|
int error;
|
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
PFS_TRACE(("%s", pn->pn_name));
|
|
|
|
pfs_assert_not_owned(pn);
|
2001-11-27 13:26:27 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do nothing unless this is the last close and the node has a
|
|
|
|
* last-close handler.
|
|
|
|
*/
|
2002-09-25 02:32:42 +00:00
|
|
|
if (vrefcnt(vn) > 1 || pn->pn_close == NULL)
|
2001-11-27 13:26:27 +00:00
|
|
|
PFS_RETURN (0);
|
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
if (pvd->pvd_pid != NO_PID) {
|
2001-11-27 13:26:27 +00:00
|
|
|
proc = pfind(pvd->pvd_pid);
|
2007-04-15 17:10:01 +00:00
|
|
|
} else {
|
2001-11-27 13:26:27 +00:00
|
|
|
proc = NULL;
|
2007-04-15 17:10:01 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
error = pn_close(va->a_td, proc, pn);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-11-27 13:26:27 +00:00
|
|
|
if (proc != NULL)
|
|
|
|
PROC_UNLOCK(proc);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-11-27 13:26:27 +00:00
|
|
|
PFS_RETURN (error);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get file attributes
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_getattr(struct vop_getattr_args *va)
|
|
|
|
{
|
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-06-10 18:39:21 +00:00
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
2001-04-07 19:51:12 +00:00
|
|
|
struct vattr *vap = va->a_vap;
|
2001-09-29 00:49:29 +00:00
|
|
|
struct proc *proc;
|
|
|
|
int error = 0;
|
2001-04-07 19:51:12 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
PFS_TRACE(("%s", pn->pn_name));
|
|
|
|
pfs_assert_not_owned(pn);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2006-02-22 17:24:54 +00:00
|
|
|
if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc))
|
2003-08-19 10:26:41 +00:00
|
|
|
PFS_RETURN (ENOENT);
|
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
vap->va_type = vn->v_type;
|
2007-04-15 17:10:01 +00:00
|
|
|
vap->va_fileid = pn_fileno(pn, pvd->pvd_pid);
|
2001-04-07 19:51:12 +00:00
|
|
|
vap->va_flags = 0;
|
|
|
|
vap->va_blocksize = PAGE_SIZE;
|
|
|
|
vap->va_bytes = vap->va_size = 0;
|
2008-09-20 19:50:52 +00:00
|
|
|
vap->va_filerev = 0;
|
2001-04-07 19:51:12 +00:00
|
|
|
vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0];
|
|
|
|
vap->va_nlink = 1;
|
|
|
|
nanotime(&vap->va_ctime);
|
|
|
|
vap->va_atime = vap->va_mtime = vap->va_ctime;
|
|
|
|
|
2001-09-29 00:49:29 +00:00
|
|
|
switch (pn->pn_type) {
|
|
|
|
case pfstype_procdir:
|
|
|
|
case pfstype_root:
|
|
|
|
case pfstype_dir:
|
2007-04-15 17:10:01 +00:00
|
|
|
#if 0
|
|
|
|
pfs_lock(pn);
|
|
|
|
/* compute link count */
|
|
|
|
pfs_unlock(pn);
|
|
|
|
#endif
|
2001-09-29 00:49:29 +00:00
|
|
|
vap->va_mode = 0555;
|
|
|
|
break;
|
|
|
|
case pfstype_file:
|
|
|
|
case pfstype_symlink:
|
2001-09-30 19:41:29 +00:00
|
|
|
vap->va_mode = 0444;
|
2001-09-29 00:49:29 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("shouldn't be here!\n");
|
|
|
|
vap->va_mode = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-02-22 17:24:54 +00:00
|
|
|
if (proc != NULL) {
|
2001-09-29 00:49:29 +00:00
|
|
|
vap->va_uid = proc->p_ucred->cr_ruid;
|
|
|
|
vap->va_gid = proc->p_ucred->cr_rgid;
|
|
|
|
} else {
|
|
|
|
vap->va_uid = 0;
|
|
|
|
vap->va_gid = 0;
|
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2009-02-16 15:17:26 +00:00
|
|
|
if (pn->pn_attr != NULL)
|
|
|
|
error = pn_attr(curthread, proc, pn, vap);
|
|
|
|
|
|
|
|
if(proc != NULL)
|
|
|
|
PROC_UNLOCK(proc);
|
|
|
|
|
2001-09-29 00:49:29 +00:00
|
|
|
PFS_RETURN (error);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
2001-10-26 18:52:47 +00:00
|
|
|
/*
|
|
|
|
* Perform an ioctl
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_ioctl(struct vop_ioctl_args *va)
|
|
|
|
{
|
2009-06-10 13:57:36 +00:00
|
|
|
struct vnode *vn;
|
|
|
|
struct pfs_vdata *pvd;
|
|
|
|
struct pfs_node *pn;
|
2006-02-22 17:24:54 +00:00
|
|
|
struct proc *proc;
|
2001-10-26 18:52:47 +00:00
|
|
|
int error;
|
|
|
|
|
2009-06-10 13:57:36 +00:00
|
|
|
vn = va->a_vp;
|
|
|
|
vn_lock(vn, LK_SHARED | LK_RETRY);
|
|
|
|
if (vn->v_iflag & VI_DOOMED) {
|
|
|
|
VOP_UNLOCK(vn, 0);
|
|
|
|
return (EBADF);
|
|
|
|
}
|
|
|
|
pvd = vn->v_data;
|
|
|
|
pn = pvd->pvd_pn;
|
|
|
|
|
2001-12-09 00:28:12 +00:00
|
|
|
PFS_TRACE(("%s: %lx", pn->pn_name, va->a_command));
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_assert_not_owned(pn);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2009-06-10 13:57:36 +00:00
|
|
|
if (vn->v_type != VREG) {
|
|
|
|
VOP_UNLOCK(vn, 0);
|
2001-10-26 18:52:47 +00:00
|
|
|
PFS_RETURN (EINVAL);
|
2009-06-10 13:57:36 +00:00
|
|
|
}
|
2009-04-07 16:13:10 +00:00
|
|
|
KASSERT_PN_IS_FILE(pn);
|
2001-10-26 18:52:47 +00:00
|
|
|
|
2009-06-10 13:57:36 +00:00
|
|
|
if (pn->pn_ioctl == NULL) {
|
|
|
|
VOP_UNLOCK(vn, 0);
|
2001-10-26 18:52:47 +00:00
|
|
|
PFS_RETURN (ENOTTY);
|
2009-06-10 13:57:36 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-10-26 18:52:47 +00:00
|
|
|
/*
|
2003-06-20 23:29:04 +00:00
|
|
|
* This is necessary because process' privileges may
|
2001-10-26 18:52:47 +00:00
|
|
|
* have changed since the open() call.
|
|
|
|
*/
|
2009-06-10 13:57:36 +00:00
|
|
|
if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) {
|
|
|
|
VOP_UNLOCK(vn, 0);
|
2001-10-26 18:52:47 +00:00
|
|
|
PFS_RETURN (EIO);
|
2009-06-10 13:57:36 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
error = pn_ioctl(curthread, proc, pn, va->a_command, va->a_data);
|
2001-10-26 18:52:47 +00:00
|
|
|
|
|
|
|
if (proc != NULL)
|
2007-04-15 17:10:01 +00:00
|
|
|
PROC_UNLOCK(proc);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2009-06-10 13:57:36 +00:00
|
|
|
VOP_UNLOCK(vn, 0);
|
2001-10-26 18:52:47 +00:00
|
|
|
PFS_RETURN (error);
|
|
|
|
}
|
|
|
|
|
2001-12-11 20:48:20 +00:00
|
|
|
/*
|
|
|
|
* Perform getextattr
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_getextattr(struct vop_getextattr_args *va)
|
|
|
|
{
|
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-12-11 20:48:20 +00:00
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
2006-02-22 17:24:54 +00:00
|
|
|
struct proc *proc;
|
2001-12-11 20:48:20 +00:00
|
|
|
int error;
|
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
PFS_TRACE(("%s", pn->pn_name));
|
|
|
|
pfs_assert_not_owned(pn);
|
2001-12-11 20:48:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is necessary because either process' privileges may
|
|
|
|
* have changed since the open() call.
|
|
|
|
*/
|
2006-02-22 17:24:54 +00:00
|
|
|
if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc))
|
2001-12-11 20:48:20 +00:00
|
|
|
PFS_RETURN (EIO);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
if (pn->pn_getextattr == NULL)
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
else
|
|
|
|
error = pn_getextattr(curthread, proc, pn,
|
|
|
|
va->a_attrnamespace, va->a_name, va->a_uio,
|
|
|
|
va->a_size, va->a_cred);
|
2001-12-11 20:48:20 +00:00
|
|
|
|
|
|
|
if (proc != NULL)
|
2007-04-15 17:10:01 +00:00
|
|
|
PROC_UNLOCK(proc);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-12-11 20:48:20 +00:00
|
|
|
PFS_RETURN (error);
|
|
|
|
}
|
|
|
|
|
2008-12-30 21:49:39 +00:00
|
|
|
/*
|
|
|
|
* Convert a vnode to its component name
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_vptocnp(struct vop_vptocnp_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct vnode **dvp = ap->a_vpp;
|
|
|
|
struct pfs_vdata *pvd = vp->v_data;
|
|
|
|
struct pfs_node *pd = pvd->pvd_pn;
|
|
|
|
struct pfs_node *pn;
|
|
|
|
struct mount *mp;
|
|
|
|
char *buf = ap->a_buf;
|
|
|
|
int *buflen = ap->a_buflen;
|
|
|
|
char pidbuf[PFS_NAMELEN];
|
|
|
|
pid_t pid = pvd->pvd_pid;
|
|
|
|
int len, i, error, locked;
|
|
|
|
|
|
|
|
i = *buflen;
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
pfs_lock(pd);
|
|
|
|
|
|
|
|
if (vp->v_type == VDIR && pd->pn_type == pfstype_root) {
|
|
|
|
*dvp = vp;
|
|
|
|
vhold(*dvp);
|
|
|
|
pfs_unlock(pd);
|
|
|
|
PFS_RETURN (0);
|
|
|
|
} else if (vp->v_type == VDIR && pd->pn_type == pfstype_procdir) {
|
|
|
|
len = snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
|
|
|
|
i -= len;
|
|
|
|
if (i < 0) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
bcopy(pidbuf, buf + i, len);
|
|
|
|
} else {
|
2009-05-28 10:24:26 +00:00
|
|
|
len = strlen(pd->pn_name);
|
|
|
|
i -= len;
|
2008-12-30 21:49:39 +00:00
|
|
|
if (i < 0) {
|
|
|
|
error = ENOMEM;
|
|
|
|
goto failed;
|
|
|
|
}
|
2009-05-28 10:24:26 +00:00
|
|
|
bcopy(pd->pn_name, buf + i, len);
|
2008-12-30 21:49:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pn = pd->pn_parent;
|
|
|
|
pfs_unlock(pd);
|
|
|
|
|
|
|
|
mp = vp->v_mount;
|
|
|
|
error = vfs_busy(mp, 0);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vp is held by caller.
|
|
|
|
*/
|
|
|
|
locked = VOP_ISLOCKED(vp);
|
|
|
|
VOP_UNLOCK(vp, 0);
|
|
|
|
|
|
|
|
error = pfs_vncache_alloc(mp, dvp, pn, pid);
|
|
|
|
if (error) {
|
|
|
|
vn_lock(vp, locked | LK_RETRY);
|
|
|
|
vfs_unbusy(mp);
|
|
|
|
PFS_RETURN(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
*buflen = i;
|
2011-11-19 07:50:49 +00:00
|
|
|
VOP_UNLOCK(*dvp, 0);
|
2008-12-30 21:49:39 +00:00
|
|
|
vn_lock(vp, locked | LK_RETRY);
|
|
|
|
vfs_unbusy(mp);
|
|
|
|
|
|
|
|
PFS_RETURN (0);
|
|
|
|
failed:
|
|
|
|
pfs_unlock(pd);
|
|
|
|
PFS_RETURN(error);
|
|
|
|
}
|
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
/*
|
|
|
|
* Look up a file or directory
|
|
|
|
*/
|
|
|
|
static int
|
2005-03-14 16:24:50 +00:00
|
|
|
pfs_lookup(struct vop_cachedlookup_args *va)
|
2001-04-07 19:51:12 +00:00
|
|
|
{
|
2001-06-10 18:39:21 +00:00
|
|
|
struct vnode *vn = va->a_dvp;
|
2001-04-07 19:51:12 +00:00
|
|
|
struct vnode **vpp = va->a_vpp;
|
|
|
|
struct componentname *cnp = va->a_cnp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-06-10 18:39:21 +00:00
|
|
|
struct pfs_node *pd = pvd->pvd_pn;
|
|
|
|
struct pfs_node *pn, *pdn = NULL;
|
2012-03-05 11:38:02 +00:00
|
|
|
struct mount *mp;
|
2001-06-10 18:39:21 +00:00
|
|
|
pid_t pid = pvd->pvd_pid;
|
2001-04-07 19:51:12 +00:00
|
|
|
char *pname;
|
2007-04-15 17:10:01 +00:00
|
|
|
int error, i, namelen, visible;
|
2001-04-07 19:51:12 +00:00
|
|
|
|
2001-09-29 00:49:29 +00:00
|
|
|
PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr));
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_assert_not_owned(pd);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-06-10 18:39:21 +00:00
|
|
|
if (vn->v_type != VDIR)
|
2001-09-29 00:49:29 +00:00
|
|
|
PFS_RETURN (ENOTDIR);
|
2009-04-07 16:13:10 +00:00
|
|
|
KASSERT_PN_IS_DIR(pd);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2005-03-14 16:24:50 +00:00
|
|
|
error = VOP_ACCESS(vn, VEXEC, cnp->cn_cred, cnp->cn_thread);
|
|
|
|
if (error)
|
|
|
|
PFS_RETURN (error);
|
|
|
|
|
2001-09-29 00:49:29 +00:00
|
|
|
/*
|
|
|
|
* Don't support DELETE or RENAME. CREATE is supported so
|
|
|
|
* that O_CREAT will work, but the lookup will still fail if
|
|
|
|
* the file does not exist.
|
|
|
|
*/
|
2005-03-14 16:24:50 +00:00
|
|
|
if ((cnp->cn_flags & ISLASTCN) &&
|
|
|
|
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (EOPNOTSUPP);
|
2001-04-07 19:51:12 +00:00
|
|
|
|
2001-09-29 00:49:29 +00:00
|
|
|
/* shortcut: check if the name is too long */
|
2001-04-07 19:51:12 +00:00
|
|
|
if (cnp->cn_namelen >= PFS_NAMELEN)
|
2001-09-29 00:49:29 +00:00
|
|
|
PFS_RETURN (ENOENT);
|
|
|
|
|
2006-02-22 17:24:54 +00:00
|
|
|
/* check that parent directory is visible... */
|
|
|
|
if (!pfs_visible(curthread, pd, pvd->pvd_pid, NULL))
|
2001-10-01 04:22:20 +00:00
|
|
|
PFS_RETURN (ENOENT);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
/* self */
|
2001-06-10 18:39:21 +00:00
|
|
|
namelen = cnp->cn_namelen;
|
2001-04-07 19:51:12 +00:00
|
|
|
pname = cnp->cn_nameptr;
|
2005-03-14 16:24:50 +00:00
|
|
|
if (namelen == 1 && pname[0] == '.') {
|
2001-04-07 19:51:12 +00:00
|
|
|
pn = pd;
|
2001-06-10 18:39:21 +00:00
|
|
|
*vpp = vn;
|
|
|
|
VREF(vn);
|
2001-10-01 04:22:20 +00:00
|
|
|
PFS_RETURN (0);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
2012-03-05 11:38:02 +00:00
|
|
|
mp = vn->v_mount;
|
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
/* parent */
|
|
|
|
if (cnp->cn_flags & ISDOTDOT) {
|
|
|
|
if (pd->pn_type == pfstype_root)
|
2001-09-29 00:49:29 +00:00
|
|
|
PFS_RETURN (EIO);
|
2012-03-05 11:38:02 +00:00
|
|
|
error = vfs_busy(mp, MBF_NOWAIT);
|
|
|
|
if (error != 0) {
|
|
|
|
vfs_ref(mp);
|
|
|
|
VOP_UNLOCK(vn, 0);
|
|
|
|
error = vfs_busy(mp, 0);
|
|
|
|
vn_lock(vn, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
vfs_rel(mp);
|
|
|
|
if (error != 0)
|
|
|
|
PFS_RETURN(ENOENT);
|
|
|
|
if (vn->v_iflag & VI_DOOMED) {
|
|
|
|
vfs_unbusy(mp);
|
|
|
|
PFS_RETURN(ENOENT);
|
|
|
|
}
|
|
|
|
}
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(vn, 0);
|
2007-04-15 17:10:01 +00:00
|
|
|
KASSERT(pd->pn_parent != NULL,
|
|
|
|
("%s(): non-root directory has no parent", __func__));
|
2001-06-10 18:39:21 +00:00
|
|
|
/*
|
|
|
|
* This one is tricky. Descendents of procdir nodes
|
|
|
|
* inherit their parent's process affinity, but
|
|
|
|
* there's no easy reverse mapping. For simplicity,
|
|
|
|
* we assume that if this node is a procdir, its
|
|
|
|
* parent isn't (which is correct as long as
|
|
|
|
* descendents of procdir nodes are never procdir
|
|
|
|
* nodes themselves)
|
|
|
|
*/
|
|
|
|
if (pd->pn_type == pfstype_procdir)
|
|
|
|
pid = NO_PID;
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_lock(pd);
|
2001-10-01 04:22:20 +00:00
|
|
|
pn = pd->pn_parent;
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_unlock(pd);
|
2001-10-01 04:22:20 +00:00
|
|
|
goto got_pnode;
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_lock(pd);
|
|
|
|
|
2001-06-10 18:39:21 +00:00
|
|
|
/* named node */
|
2001-10-19 01:43:06 +00:00
|
|
|
for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next)
|
2001-06-10 18:39:21 +00:00
|
|
|
if (pn->pn_type == pfstype_procdir)
|
|
|
|
pdn = pn;
|
2005-03-14 16:24:50 +00:00
|
|
|
else if (pn->pn_name[namelen] == '\0' &&
|
2007-04-15 17:10:01 +00:00
|
|
|
bcmp(pname, pn->pn_name, namelen) == 0) {
|
|
|
|
pfs_unlock(pd);
|
2001-04-07 19:51:12 +00:00
|
|
|
goto got_pnode;
|
2007-04-15 17:10:01 +00:00
|
|
|
}
|
2001-06-10 18:39:21 +00:00
|
|
|
|
|
|
|
/* process dependent node */
|
|
|
|
if ((pn = pdn) != NULL) {
|
|
|
|
pid = 0;
|
|
|
|
for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i)
|
|
|
|
if ((pid = pid * 10 + pname[i] - '0') > PID_MAX)
|
2001-04-07 19:51:12 +00:00
|
|
|
break;
|
2007-04-15 17:10:01 +00:00
|
|
|
if (i == cnp->cn_namelen) {
|
|
|
|
pfs_unlock(pd);
|
2001-04-07 19:51:12 +00:00
|
|
|
goto got_pnode;
|
2007-04-15 17:10:01 +00:00
|
|
|
}
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_unlock(pd);
|
|
|
|
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (ENOENT);
|
2007-04-15 17:10:01 +00:00
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
got_pnode:
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_assert_not_owned(pd);
|
|
|
|
pfs_assert_not_owned(pn);
|
2007-10-05 17:37:25 +00:00
|
|
|
visible = pfs_visible(curthread, pn, pid, NULL);
|
2007-04-15 17:10:01 +00:00
|
|
|
if (!visible) {
|
2005-03-28 09:34:36 +00:00
|
|
|
error = ENOENT;
|
|
|
|
goto failed;
|
|
|
|
}
|
2002-07-08 01:50:14 +00:00
|
|
|
|
2012-03-05 11:38:02 +00:00
|
|
|
error = pfs_vncache_alloc(mp, vpp, pn, pid);
|
2001-04-07 19:51:12 +00:00
|
|
|
if (error)
|
2005-03-28 09:34:36 +00:00
|
|
|
goto failed;
|
2002-07-08 01:50:14 +00:00
|
|
|
|
2012-03-05 11:38:02 +00:00
|
|
|
if (cnp->cn_flags & ISDOTDOT) {
|
|
|
|
vfs_unbusy(mp);
|
|
|
|
vn_lock(vn, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
if (vn->v_iflag & VI_DOOMED) {
|
|
|
|
vput(*vpp);
|
|
|
|
*vpp = NULL;
|
|
|
|
PFS_RETURN(ENOENT);
|
|
|
|
}
|
|
|
|
}
|
2010-04-20 10:19:27 +00:00
|
|
|
if (cnp->cn_flags & MAKEENTRY && !(vn->v_iflag & VI_DOOMED))
|
2001-06-10 18:39:21 +00:00
|
|
|
cache_enter(vn, *vpp, cnp);
|
2001-09-29 00:49:29 +00:00
|
|
|
PFS_RETURN (0);
|
2005-03-28 09:34:36 +00:00
|
|
|
failed:
|
2012-03-05 11:38:02 +00:00
|
|
|
if (cnp->cn_flags & ISDOTDOT) {
|
|
|
|
vfs_unbusy(mp);
|
|
|
|
vn_lock(vn, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
*vpp = NULL;
|
|
|
|
}
|
2005-03-28 09:34:36 +00:00
|
|
|
PFS_RETURN(error);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open a file or directory.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_open(struct vop_open_args *va)
|
|
|
|
{
|
2001-09-29 00:49:29 +00:00
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-09-29 00:49:29 +00:00
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
2001-09-30 19:41:29 +00:00
|
|
|
int mode = va->a_mode;
|
2001-09-29 00:49:29 +00:00
|
|
|
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode));
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_assert_not_owned(pn);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-09-30 19:41:29 +00:00
|
|
|
/* check if the requested mode is permitted */
|
|
|
|
if (((mode & FREAD) && !(mode & PFS_RD)) ||
|
|
|
|
((mode & FWRITE) && !(mode & PFS_WR)))
|
|
|
|
PFS_RETURN (EPERM);
|
|
|
|
|
|
|
|
/* we don't support locking */
|
|
|
|
if ((mode & O_SHLOCK) || (mode & O_EXLOCK))
|
|
|
|
PFS_RETURN (EOPNOTSUPP);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-10-01 04:22:20 +00:00
|
|
|
PFS_RETURN (0);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read from a file
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_read(struct vop_read_args *va)
|
|
|
|
{
|
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-06-10 18:39:21 +00:00
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
2001-04-07 19:51:12 +00:00
|
|
|
struct uio *uio = va->a_uio;
|
2006-02-22 17:24:54 +00:00
|
|
|
struct proc *proc;
|
2001-06-10 10:34:21 +00:00
|
|
|
struct sbuf *sb = NULL;
|
2008-12-29 12:12:23 +00:00
|
|
|
int error, locked;
|
2013-11-20 19:41:00 +00:00
|
|
|
off_t buflen;
|
2001-04-07 19:51:12 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
PFS_TRACE(("%s", pn->pn_name));
|
|
|
|
pfs_assert_not_owned(pn);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
if (vn->v_type != VREG)
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (EINVAL);
|
2009-04-07 16:13:10 +00:00
|
|
|
KASSERT_PN_IS_FILE(pn);
|
2001-04-07 19:51:12 +00:00
|
|
|
|
2001-09-30 19:41:29 +00:00
|
|
|
if (!(pn->pn_flags & PFS_RD))
|
|
|
|
PFS_RETURN (EBADF);
|
2001-09-25 13:25:30 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
if (pn->pn_fill == NULL)
|
2001-10-26 18:52:47 +00:00
|
|
|
PFS_RETURN (EIO);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-10-01 04:22:20 +00:00
|
|
|
/*
|
|
|
|
* This is necessary because either process' privileges may
|
|
|
|
* have changed since the open() call.
|
|
|
|
*/
|
2006-02-22 17:24:54 +00:00
|
|
|
if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc))
|
2001-10-01 04:22:20 +00:00
|
|
|
PFS_RETURN (EIO);
|
2006-02-22 17:24:54 +00:00
|
|
|
if (proc != NULL) {
|
2001-06-10 18:39:21 +00:00
|
|
|
_PHOLD(proc);
|
|
|
|
PROC_UNLOCK(proc);
|
|
|
|
}
|
2001-09-25 13:25:30 +00:00
|
|
|
|
2008-12-29 12:12:23 +00:00
|
|
|
vhold(vn);
|
|
|
|
locked = VOP_ISLOCKED(vn);
|
|
|
|
VOP_UNLOCK(vn, 0);
|
|
|
|
|
2001-09-25 13:25:30 +00:00
|
|
|
if (pn->pn_flags & PFS_RAWRD) {
|
2009-06-25 18:46:30 +00:00
|
|
|
PFS_TRACE(("%zd resid", uio->uio_resid));
|
2007-04-15 17:10:01 +00:00
|
|
|
error = pn_fill(curthread, proc, pn, NULL, uio);
|
2009-06-25 18:46:30 +00:00
|
|
|
PFS_TRACE(("%zd resid", uio->uio_resid));
|
2008-12-29 12:12:23 +00:00
|
|
|
goto ret;
|
2001-09-25 13:25:30 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2013-11-20 19:41:00 +00:00
|
|
|
if (uio->uio_resid < 0 || uio->uio_offset < 0 ||
|
|
|
|
uio->uio_resid > OFF_MAX - uio->uio_offset) {
|
2008-12-29 12:12:23 +00:00
|
|
|
error = EINVAL;
|
|
|
|
goto ret;
|
2003-10-02 15:00:55 +00:00
|
|
|
}
|
2013-11-20 19:41:00 +00:00
|
|
|
buflen = uio->uio_offset + uio->uio_resid;
|
2012-01-06 10:12:59 +00:00
|
|
|
if (buflen > MAXPHYS)
|
|
|
|
buflen = MAXPHYS;
|
2007-04-15 17:10:01 +00:00
|
|
|
|
2012-01-06 10:12:59 +00:00
|
|
|
sb = sbuf_new(sb, NULL, buflen + 1, 0);
|
2001-06-10 18:39:21 +00:00
|
|
|
if (sb == NULL) {
|
2008-12-29 12:12:23 +00:00
|
|
|
error = EIO;
|
|
|
|
goto ret;
|
2001-06-10 18:39:21 +00:00
|
|
|
}
|
2001-04-07 19:51:12 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
error = pn_fill(curthread, proc, pn, sb, uio);
|
2001-06-10 18:39:21 +00:00
|
|
|
|
2001-06-10 21:39:01 +00:00
|
|
|
if (error) {
|
|
|
|
sbuf_delete(sb);
|
2008-12-29 12:12:23 +00:00
|
|
|
goto ret;
|
2001-06-10 21:39:01 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2012-01-06 10:12:59 +00:00
|
|
|
/*
|
|
|
|
* XXX: If the buffer overflowed, sbuf_len() will not return
|
|
|
|
* the data length. Then just use the full length because an
|
|
|
|
* overflowed sbuf must be full.
|
|
|
|
*/
|
|
|
|
if (sbuf_finish(sb) == 0)
|
|
|
|
buflen = sbuf_len(sb);
|
|
|
|
error = uiomove_frombuf(sbuf_data(sb), buflen, uio);
|
2001-06-10 10:34:21 +00:00
|
|
|
sbuf_delete(sb);
|
2008-12-29 12:12:23 +00:00
|
|
|
ret:
|
|
|
|
vn_lock(vn, locked | LK_RETRY);
|
|
|
|
vdrop(vn);
|
|
|
|
if (proc != NULL)
|
|
|
|
PRELE(proc);
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (error);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
2001-06-10 18:39:21 +00:00
|
|
|
/*
|
|
|
|
* Iterate through directory entries
|
|
|
|
*/
|
|
|
|
static int
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_iterate(struct thread *td, struct proc *proc, struct pfs_node *pd,
|
2002-06-06 16:59:24 +00:00
|
|
|
struct pfs_node **pn, struct proc **p)
|
2001-06-10 18:39:21 +00:00
|
|
|
{
|
2007-04-15 17:10:01 +00:00
|
|
|
int visible;
|
|
|
|
|
2006-02-22 17:24:54 +00:00
|
|
|
sx_assert(&allproc_lock, SX_SLOCKED);
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_assert_owned(pd);
|
2001-10-01 04:22:20 +00:00
|
|
|
again:
|
2003-08-18 13:36:09 +00:00
|
|
|
if (*pn == NULL) {
|
|
|
|
/* first node */
|
|
|
|
*pn = pd->pn_nodes;
|
|
|
|
} else if ((*pn)->pn_type != pfstype_procdir) {
|
|
|
|
/* next node */
|
2001-10-19 01:43:06 +00:00
|
|
|
*pn = (*pn)->pn_next;
|
2003-08-18 13:36:09 +00:00
|
|
|
}
|
|
|
|
if (*pn != NULL && (*pn)->pn_type == pfstype_procdir) {
|
|
|
|
/* next process */
|
2001-06-10 18:39:21 +00:00
|
|
|
if (*p == NULL)
|
|
|
|
*p = LIST_FIRST(&allproc);
|
|
|
|
else
|
|
|
|
*p = LIST_NEXT(*p, p_list);
|
2003-08-18 13:36:09 +00:00
|
|
|
/* out of processes: next node */
|
|
|
|
if (*p == NULL)
|
|
|
|
*pn = (*pn)->pn_next;
|
2007-04-15 17:10:01 +00:00
|
|
|
else
|
|
|
|
PROC_LOCK(*p);
|
2001-06-10 18:39:21 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-10-19 01:43:06 +00:00
|
|
|
if ((*pn) == NULL)
|
2001-06-10 18:39:21 +00:00
|
|
|
return (-1);
|
2001-10-01 04:22:20 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
if (*p != NULL) {
|
|
|
|
visible = pfs_visible_proc(td, *pn, *p);
|
|
|
|
PROC_UNLOCK(*p);
|
|
|
|
} else if (proc != NULL) {
|
|
|
|
visible = pfs_visible_proc(td, *pn, proc);
|
|
|
|
} else {
|
|
|
|
visible = 1;
|
|
|
|
}
|
|
|
|
if (!visible)
|
2001-10-01 04:22:20 +00:00
|
|
|
goto again;
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-06-10 18:39:21 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2011-11-16 10:11:55 +00:00
|
|
|
/* Directory entry list */
|
|
|
|
struct pfsentry {
|
|
|
|
STAILQ_ENTRY(pfsentry) link;
|
|
|
|
struct dirent entry;
|
|
|
|
};
|
|
|
|
STAILQ_HEAD(pfsdirentlist, pfsentry);
|
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
/*
|
|
|
|
* Return directory entries.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_readdir(struct vop_readdir_args *va)
|
|
|
|
{
|
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-06-10 18:39:21 +00:00
|
|
|
struct pfs_node *pd = pvd->pvd_pn;
|
2001-10-01 04:22:20 +00:00
|
|
|
pid_t pid = pvd->pvd_pid;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct proc *p, *proc;
|
2001-06-10 18:39:21 +00:00
|
|
|
struct pfs_node *pn;
|
2001-04-07 19:51:12 +00:00
|
|
|
struct uio *uio;
|
2011-11-16 10:11:55 +00:00
|
|
|
struct pfsentry *pfsent, *pfsent2;
|
|
|
|
struct pfsdirentlist lst;
|
2001-04-07 19:51:12 +00:00
|
|
|
off_t offset;
|
|
|
|
int error, i, resid;
|
|
|
|
|
2011-11-16 10:11:55 +00:00
|
|
|
STAILQ_INIT(&lst);
|
|
|
|
error = 0;
|
2007-04-14 14:08:30 +00:00
|
|
|
KASSERT(pd->pn_info == vn->v_mount->mnt_data,
|
2007-04-15 17:10:01 +00:00
|
|
|
("%s(): pn_info does not match mountpoint", __func__));
|
|
|
|
PFS_TRACE(("%s pid %lu", pd->pn_name, (unsigned long)pid));
|
|
|
|
pfs_assert_not_owned(pd);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
if (vn->v_type != VDIR)
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (ENOTDIR);
|
2009-04-07 16:13:10 +00:00
|
|
|
KASSERT_PN_IS_DIR(pd);
|
2001-04-07 19:51:12 +00:00
|
|
|
uio = va->a_uio;
|
|
|
|
|
|
|
|
/* only allow reading entire entries */
|
|
|
|
offset = uio->uio_offset;
|
|
|
|
resid = uio->uio_resid;
|
2005-03-14 16:21:32 +00:00
|
|
|
if (offset < 0 || offset % PFS_DELEN != 0 ||
|
|
|
|
(resid && resid < PFS_DELEN))
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (EINVAL);
|
2005-03-14 16:21:32 +00:00
|
|
|
if (resid == 0)
|
|
|
|
PFS_RETURN (0);
|
2004-07-09 11:43:37 +00:00
|
|
|
|
2001-06-10 18:39:21 +00:00
|
|
|
sx_slock(&allproc_lock);
|
2007-04-15 17:10:01 +00:00
|
|
|
pfs_lock(pd);
|
|
|
|
|
|
|
|
/* check if the directory is visible to the caller */
|
|
|
|
if (!pfs_visible(curthread, pd, pid, &proc)) {
|
|
|
|
sx_sunlock(&allproc_lock);
|
|
|
|
pfs_unlock(pd);
|
|
|
|
PFS_RETURN (ENOENT);
|
|
|
|
}
|
|
|
|
KASSERT(pid == NO_PID || proc != NULL,
|
|
|
|
("%s(): no process for pid %lu", __func__, (unsigned long)pid));
|
|
|
|
|
|
|
|
/* skip unwanted entries */
|
|
|
|
for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN) {
|
|
|
|
if (pfs_iterate(curthread, proc, pd, &pn, &p) == -1) {
|
2001-10-19 01:43:06 +00:00
|
|
|
/* nothing left... */
|
2007-04-15 17:10:01 +00:00
|
|
|
if (proc != NULL)
|
|
|
|
PROC_UNLOCK(proc);
|
|
|
|
pfs_unlock(pd);
|
2001-10-19 01:43:06 +00:00
|
|
|
sx_sunlock(&allproc_lock);
|
|
|
|
PFS_RETURN (0);
|
|
|
|
}
|
2007-04-15 17:10:01 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
/* fill in entries */
|
2007-04-15 17:10:01 +00:00
|
|
|
while (pfs_iterate(curthread, proc, pd, &pn, &p) != -1 &&
|
2005-03-14 16:21:32 +00:00
|
|
|
resid >= PFS_DELEN) {
|
2011-11-16 10:11:55 +00:00
|
|
|
if ((pfsent = malloc(sizeof(struct pfsentry), M_IOV,
|
|
|
|
M_NOWAIT | M_ZERO)) == NULL) {
|
|
|
|
error = ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pfsent->entry.d_reclen = PFS_DELEN;
|
|
|
|
pfsent->entry.d_fileno = pn_fileno(pn, pid);
|
2001-04-07 19:51:12 +00:00
|
|
|
/* PFS_DELEN was picked to fit PFS_NAMLEN */
|
|
|
|
for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i)
|
2011-11-16 10:11:55 +00:00
|
|
|
pfsent->entry.d_name[i] = pn->pn_name[i];
|
|
|
|
pfsent->entry.d_name[i] = 0;
|
|
|
|
pfsent->entry.d_namlen = i;
|
2001-04-07 19:51:12 +00:00
|
|
|
switch (pn->pn_type) {
|
2001-06-10 18:39:21 +00:00
|
|
|
case pfstype_procdir:
|
|
|
|
KASSERT(p != NULL,
|
|
|
|
("reached procdir node with p == NULL"));
|
2011-11-16 10:11:55 +00:00
|
|
|
pfsent->entry.d_namlen = snprintf(pfsent->entry.d_name,
|
2001-06-10 18:39:21 +00:00
|
|
|
PFS_NAMELEN, "%d", p->p_pid);
|
|
|
|
/* fall through */
|
2001-04-07 19:51:12 +00:00
|
|
|
case pfstype_root:
|
|
|
|
case pfstype_dir:
|
|
|
|
case pfstype_this:
|
|
|
|
case pfstype_parent:
|
2011-11-16 10:11:55 +00:00
|
|
|
pfsent->entry.d_type = DT_DIR;
|
2001-04-07 19:51:12 +00:00
|
|
|
break;
|
|
|
|
case pfstype_file:
|
2011-11-16 10:11:55 +00:00
|
|
|
pfsent->entry.d_type = DT_REG;
|
2001-04-07 19:51:12 +00:00
|
|
|
break;
|
|
|
|
case pfstype_symlink:
|
2011-11-16 10:11:55 +00:00
|
|
|
pfsent->entry.d_type = DT_LNK;
|
2001-04-07 19:51:12 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
|
|
|
|
}
|
2011-11-16 10:11:55 +00:00
|
|
|
PFS_TRACE(("%s", pfsent->entry.d_name));
|
|
|
|
STAILQ_INSERT_TAIL(&lst, pfsent, link);
|
2001-04-07 19:51:12 +00:00
|
|
|
offset += PFS_DELEN;
|
|
|
|
resid -= PFS_DELEN;
|
|
|
|
}
|
2007-04-15 17:10:01 +00:00
|
|
|
if (proc != NULL)
|
|
|
|
PROC_UNLOCK(proc);
|
|
|
|
pfs_unlock(pd);
|
2001-06-10 18:39:21 +00:00
|
|
|
sx_sunlock(&allproc_lock);
|
2011-11-16 10:11:55 +00:00
|
|
|
i = 0;
|
|
|
|
STAILQ_FOREACH_SAFE(pfsent, &lst, link, pfsent2) {
|
|
|
|
if (error == 0)
|
|
|
|
error = uiomove(&pfsent->entry, PFS_DELEN, uio);
|
|
|
|
free(pfsent, M_IOV);
|
|
|
|
i++;
|
|
|
|
}
|
2011-11-16 18:41:59 +00:00
|
|
|
PFS_TRACE(("%d bytes", i * PFS_DELEN));
|
2004-07-09 11:43:37 +00:00
|
|
|
PFS_RETURN (error);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a symbolic link
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_readlink(struct vop_readlink_args *va)
|
|
|
|
{
|
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-06-10 18:39:21 +00:00
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
2001-04-07 19:51:12 +00:00
|
|
|
struct uio *uio = va->a_uio;
|
2001-06-10 18:39:21 +00:00
|
|
|
struct proc *proc = NULL;
|
2007-04-15 17:10:01 +00:00
|
|
|
char buf[PATH_MAX];
|
2001-04-07 19:51:12 +00:00
|
|
|
struct sbuf sb;
|
2009-05-31 15:01:50 +00:00
|
|
|
int error, locked;
|
2001-04-07 19:51:12 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
PFS_TRACE(("%s", pn->pn_name));
|
|
|
|
pfs_assert_not_owned(pn);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
if (vn->v_type != VLNK)
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (EINVAL);
|
2009-04-07 16:13:10 +00:00
|
|
|
KASSERT_PN_IS_LINK(pn);
|
2001-04-07 19:51:12 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
if (pn->pn_fill == NULL)
|
2001-10-26 18:52:47 +00:00
|
|
|
PFS_RETURN (EIO);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-06-10 18:39:21 +00:00
|
|
|
if (pvd->pvd_pid != NO_PID) {
|
|
|
|
if ((proc = pfind(pvd->pvd_pid)) == NULL)
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (EIO);
|
Close some races between procfs/ptrace and exit(2):
- Reorder the events in exit(2) slightly so that we trigger the S_EXIT
stop event earlier. After we have signalled that, we set P_WEXIT and
then wait for any processes with a hold on the vmspace via PHOLD to
release it. PHOLD now KASSERT()'s that P_WEXIT is clear when it is
invoked, and PRELE now does a wakeup if P_WEXIT is set and p_lock drops
to zero.
- Change proc_rwmem() to require that the processing read from has its
vmspace held via PHOLD by the caller and get rid of all the junk to
screw around with the vmspace reference count as we no longer need it.
- In ptrace() and pseudofs(), treat a process with P_WEXIT set as if it
doesn't exist.
- Only do one PHOLD in kern_ptrace() now, and do it earlier so it covers
FIX_SSTEP() (since on alpha at least this can end up calling proc_rwmem()
to clear an earlier single-step simualted via a breakpoint). We only
do one to avoid races. Also, by making the EINVAL error for unknown
requests be part of the default: case in the switch, the various
switch cases can now just break out to return which removes a _lot_ of
duplicated PRELE and proc unlocks, etc. Also, it fixes at least one bug
where a LWP ptrace command could return EINVAL with the proc lock still
held.
- Changed the locking for ptrace_single_step(), ptrace_set_pc(), and
ptrace_clear_single_step() to always be called with the proc lock
held (it was a mixed bag previously). Alpha and arm have to drop
the lock while the mess around with breakpoints, but other archs
avoid extra lock release/acquires in ptrace(). I did have to fix a
couple of other consumers in kern_kse and a few other places to
hold the proc lock and PHOLD.
Tested by: ps (1 mostly, but some bits of 2-4 as well)
MFC after: 1 week
2006-02-22 18:57:50 +00:00
|
|
|
if (proc->p_flag & P_WEXIT) {
|
|
|
|
PROC_UNLOCK(proc);
|
|
|
|
PFS_RETURN (EIO);
|
|
|
|
}
|
2001-06-10 18:39:21 +00:00
|
|
|
_PHOLD(proc);
|
|
|
|
PROC_UNLOCK(proc);
|
|
|
|
}
|
2009-05-31 15:01:50 +00:00
|
|
|
vhold(vn);
|
|
|
|
locked = VOP_ISLOCKED(vn);
|
|
|
|
VOP_UNLOCK(vn, 0);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
/* sbuf_new() can't fail with a static buffer */
|
|
|
|
sbuf_new(&sb, buf, sizeof buf, 0);
|
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
error = pn_fill(curthread, proc, pn, &sb, NULL);
|
2001-06-10 18:39:21 +00:00
|
|
|
|
|
|
|
if (proc != NULL)
|
|
|
|
PRELE(proc);
|
2009-05-31 15:01:50 +00:00
|
|
|
vn_lock(vn, locked | LK_RETRY);
|
|
|
|
vdrop(vn);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-06-10 21:39:01 +00:00
|
|
|
if (error) {
|
|
|
|
sbuf_delete(&sb);
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (error);
|
2001-06-10 21:39:01 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2012-01-06 09:17:34 +00:00
|
|
|
if (sbuf_finish(&sb) != 0) {
|
|
|
|
sbuf_delete(&sb);
|
|
|
|
PFS_RETURN (ENAMETOOLONG);
|
|
|
|
}
|
|
|
|
|
2003-10-02 15:00:55 +00:00
|
|
|
error = uiomove_frombuf(sbuf_data(&sb), sbuf_len(&sb), uio);
|
2001-04-07 19:51:12 +00:00
|
|
|
sbuf_delete(&sb);
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (error);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reclaim a vnode
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_reclaim(struct vop_reclaim_args *va)
|
|
|
|
{
|
2007-04-15 17:10:01 +00:00
|
|
|
struct vnode *vn = va->a_vp;
|
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
|
|
|
|
|
|
|
PFS_TRACE(("%s", pn->pn_name));
|
|
|
|
pfs_assert_not_owned(pn);
|
2001-12-19 23:58:09 +00:00
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
return (pfs_vncache_free(va->a_vp));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set attributes
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
pfs_setattr(struct vop_setattr_args *va)
|
|
|
|
{
|
2007-04-15 17:10:01 +00:00
|
|
|
struct vnode *vn = va->a_vp;
|
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
|
|
|
|
|
|
|
PFS_TRACE(("%s", pn->pn_name));
|
|
|
|
pfs_assert_not_owned(pn);
|
2002-02-04 18:21:59 +00:00
|
|
|
|
|
|
|
PFS_RETURN (EOPNOTSUPP);
|
2001-04-07 19:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-04-30 16:27:20 +00:00
|
|
|
* Write to a file
|
2001-04-07 19:51:12 +00:00
|
|
|
*/
|
2001-09-25 13:25:30 +00:00
|
|
|
static int
|
2004-12-01 23:16:38 +00:00
|
|
|
pfs_write(struct vop_write_args *va)
|
2001-09-25 13:25:30 +00:00
|
|
|
{
|
|
|
|
struct vnode *vn = va->a_vp;
|
2007-04-15 17:10:01 +00:00
|
|
|
struct pfs_vdata *pvd = vn->v_data;
|
2001-09-25 13:25:30 +00:00
|
|
|
struct pfs_node *pn = pvd->pvd_pn;
|
|
|
|
struct uio *uio = va->a_uio;
|
2006-02-22 17:24:54 +00:00
|
|
|
struct proc *proc;
|
2001-09-29 00:49:29 +00:00
|
|
|
struct sbuf sb;
|
2001-09-25 13:25:30 +00:00
|
|
|
int error;
|
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
PFS_TRACE(("%s", pn->pn_name));
|
|
|
|
pfs_assert_not_owned(pn);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-09-25 13:25:30 +00:00
|
|
|
if (vn->v_type != VREG)
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (EINVAL);
|
2009-04-07 16:13:10 +00:00
|
|
|
KASSERT_PN_IS_FILE(pn);
|
2001-09-25 13:25:30 +00:00
|
|
|
|
2001-09-30 19:41:29 +00:00
|
|
|
if (!(pn->pn_flags & PFS_WR))
|
|
|
|
PFS_RETURN (EBADF);
|
2001-09-25 13:25:30 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
if (pn->pn_fill == NULL)
|
2001-10-26 18:52:47 +00:00
|
|
|
PFS_RETURN (EIO);
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2001-10-01 04:22:20 +00:00
|
|
|
/*
|
|
|
|
* This is necessary because either process' privileges may
|
|
|
|
* have changed since the open() call.
|
|
|
|
*/
|
2006-02-22 17:24:54 +00:00
|
|
|
if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc))
|
2001-10-01 04:22:20 +00:00
|
|
|
PFS_RETURN (EIO);
|
2006-02-22 17:24:54 +00:00
|
|
|
if (proc != NULL) {
|
2001-09-25 13:25:30 +00:00
|
|
|
_PHOLD(proc);
|
|
|
|
PROC_UNLOCK(proc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pn->pn_flags & PFS_RAWWR) {
|
2007-04-15 17:10:01 +00:00
|
|
|
error = pn_fill(curthread, proc, pn, NULL, uio);
|
2001-09-25 13:25:30 +00:00
|
|
|
if (proc != NULL)
|
|
|
|
PRELE(proc);
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (error);
|
2001-09-25 13:25:30 +00:00
|
|
|
}
|
2001-09-29 00:49:29 +00:00
|
|
|
|
|
|
|
sbuf_uionew(&sb, uio, &error);
|
2007-04-15 17:10:01 +00:00
|
|
|
if (error) {
|
|
|
|
if (proc != NULL)
|
|
|
|
PRELE(proc);
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (error);
|
2007-04-15 17:10:01 +00:00
|
|
|
}
|
2002-06-06 16:59:24 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
error = pn_fill(curthread, proc, pn, &sb, uio);
|
2001-09-29 00:49:29 +00:00
|
|
|
|
2007-04-15 17:10:01 +00:00
|
|
|
sbuf_delete(&sb);
|
2001-09-29 00:49:29 +00:00
|
|
|
if (proc != NULL)
|
|
|
|
PRELE(proc);
|
2001-09-30 19:41:29 +00:00
|
|
|
PFS_RETURN (error);
|
2001-09-25 13:25:30 +00:00
|
|
|
}
|
|
|
|
|
2001-04-07 19:51:12 +00:00
|
|
|
/*
|
|
|
|
* Vnode operations
|
|
|
|
*/
|
2004-12-01 23:16:38 +00:00
|
|
|
struct vop_vector pfs_vnodeops = {
|
|
|
|
.vop_default = &default_vnodeops,
|
2005-01-13 18:59:48 +00:00
|
|
|
|
2004-12-01 23:16:38 +00:00
|
|
|
.vop_access = pfs_access,
|
2005-03-14 16:24:50 +00:00
|
|
|
.vop_cachedlookup = pfs_lookup,
|
2004-12-01 23:16:38 +00:00
|
|
|
.vop_close = pfs_close,
|
|
|
|
.vop_create = VOP_EOPNOTSUPP,
|
|
|
|
.vop_getattr = pfs_getattr,
|
|
|
|
.vop_getextattr = pfs_getextattr,
|
|
|
|
.vop_ioctl = pfs_ioctl,
|
|
|
|
.vop_link = VOP_EOPNOTSUPP,
|
2005-03-14 16:24:50 +00:00
|
|
|
.vop_lookup = vfs_cache_lookup,
|
2004-12-01 23:16:38 +00:00
|
|
|
.vop_mkdir = VOP_EOPNOTSUPP,
|
|
|
|
.vop_mknod = VOP_EOPNOTSUPP,
|
|
|
|
.vop_open = pfs_open,
|
|
|
|
.vop_read = pfs_read,
|
|
|
|
.vop_readdir = pfs_readdir,
|
|
|
|
.vop_readlink = pfs_readlink,
|
|
|
|
.vop_reclaim = pfs_reclaim,
|
|
|
|
.vop_remove = VOP_EOPNOTSUPP,
|
|
|
|
.vop_rename = VOP_EOPNOTSUPP,
|
|
|
|
.vop_rmdir = VOP_EOPNOTSUPP,
|
|
|
|
.vop_setattr = pfs_setattr,
|
|
|
|
.vop_symlink = VOP_EOPNOTSUPP,
|
2008-12-30 21:49:39 +00:00
|
|
|
.vop_vptocnp = pfs_vptocnp,
|
2004-12-01 23:16:38 +00:00
|
|
|
.vop_write = pfs_write,
|
|
|
|
/* XXX I've probably forgotten a few that need VOP_EOPNOTSUPP */
|
2001-04-07 19:51:12 +00:00
|
|
|
};
|