mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
Switch to dynamic rather than static initialization.
This makes it possible (in theory) for nodes to be added and / or removed from pseudofs filesystems at runtime.
This commit is contained in:
parent
b51cc76c45
commit
33802b9eff
3 changed files with 288 additions and 62 deletions
|
|
@ -44,9 +44,194 @@
|
|||
#include <fs/pseudofs/pseudofs.h>
|
||||
#include <fs/pseudofs/pseudofs_internal.h>
|
||||
|
||||
static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes");
|
||||
|
||||
SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW, 0,
|
||||
"pseudofs");
|
||||
|
||||
/*
|
||||
* Add a node to a directory
|
||||
*/
|
||||
static int
|
||||
_pfs_add_node(struct pfs_node *parent, struct pfs_node *node)
|
||||
{
|
||||
KASSERT(parent != NULL,
|
||||
(__FUNCTION__ "(): parent is NULL"));
|
||||
KASSERT(parent->pn_info != NULL,
|
||||
(__FUNCTION__ "(): parent has no pn_info"));
|
||||
KASSERT(parent->pn_type == pfstype_dir ||
|
||||
parent->pn_type == pfstype_procdir ||
|
||||
parent->pn_type == pfstype_root,
|
||||
(__FUNCTION__ "(): parent is not a directory"));
|
||||
|
||||
/* XXX should check for duplicate names etc. */
|
||||
|
||||
mtx_lock(&parent->pn_info->pi_mutex);
|
||||
node->pn_info = parent->pn_info;
|
||||
node->pn_parent = parent;
|
||||
node->pn_next = parent->pn_nodes;
|
||||
parent->pn_nodes = node;
|
||||
mtx_unlock(&parent->pn_info->pi_mutex);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add . and .. to a directory
|
||||
*/
|
||||
static int
|
||||
_pfs_fixup_dir(struct pfs_node *parent)
|
||||
{
|
||||
struct pfs_node *dir;
|
||||
|
||||
MALLOC(dir, struct pfs_node *, sizeof *dir,
|
||||
M_PFSNODES, M_WAITOK|M_ZERO);
|
||||
dir->pn_name[0] = '.';
|
||||
dir->pn_type = pfstype_this;
|
||||
|
||||
if (_pfs_add_node(parent, dir) != 0) {
|
||||
FREE(dir, M_PFSNODES);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
MALLOC(dir, struct pfs_node *, sizeof *dir,
|
||||
M_PFSNODES, M_WAITOK|M_ZERO);
|
||||
dir->pn_name[0] = dir->pn_name[1] = '.';
|
||||
dir->pn_type = pfstype_parent;
|
||||
|
||||
if (_pfs_add_node(parent, dir) != 0) {
|
||||
FREE(dir, M_PFSNODES);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a directory
|
||||
*/
|
||||
struct pfs_node *
|
||||
pfs_create_dir(struct pfs_node *parent, char *name,
|
||||
pfs_attr_t attr, pfs_vis_t vis, int flags)
|
||||
{
|
||||
struct pfs_node *dir;
|
||||
|
||||
KASSERT(strlen(name) < PFS_NAMELEN,
|
||||
(__FUNCTION__ "(): node name is too long"));
|
||||
|
||||
MALLOC(dir, struct pfs_node *, sizeof *dir,
|
||||
M_PFSNODES, M_WAITOK|M_ZERO);
|
||||
strcpy(dir->pn_name, name);
|
||||
dir->pn_type = (flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir;
|
||||
dir->pn_attr = attr;
|
||||
dir->pn_vis = vis;
|
||||
dir->pn_flags = flags & ~PFS_PROCDEP;
|
||||
|
||||
if (_pfs_add_node(parent, dir) != 0) {
|
||||
FREE(dir, M_PFSNODES);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (_pfs_fixup_dir(dir) != 0) {
|
||||
pfs_destroy(dir);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a file
|
||||
*/
|
||||
struct pfs_node *
|
||||
pfs_create_file(struct pfs_node *parent, char *name, pfs_fill_t fill,
|
||||
pfs_attr_t attr, pfs_vis_t vis, int flags)
|
||||
{
|
||||
struct pfs_node *node;
|
||||
|
||||
KASSERT(strlen(name) < PFS_NAMELEN,
|
||||
(__FUNCTION__ "(): node name is too long"));
|
||||
|
||||
MALLOC(node, struct pfs_node *, sizeof *node,
|
||||
M_PFSNODES, M_WAITOK|M_ZERO);
|
||||
strcpy(node->pn_name, name);
|
||||
node->pn_type = pfstype_file;
|
||||
node->pn_func = fill;
|
||||
node->pn_attr = attr;
|
||||
node->pn_vis = vis;
|
||||
node->pn_flags = flags;
|
||||
|
||||
if (_pfs_add_node(parent, node) != 0) {
|
||||
FREE(node, M_PFSNODES);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a symlink
|
||||
*/
|
||||
struct pfs_node *
|
||||
pfs_create_link(struct pfs_node *parent, char *name, pfs_fill_t fill,
|
||||
pfs_attr_t attr, pfs_vis_t vis, int flags)
|
||||
{
|
||||
struct pfs_node *node;
|
||||
|
||||
node = pfs_create_file(parent, name, fill, attr, vis, flags);
|
||||
if (node == NULL)
|
||||
return (NULL);
|
||||
node->pn_type = pfstype_symlink;
|
||||
return (node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a node or a tree of nodes
|
||||
*/
|
||||
int
|
||||
pfs_destroy(struct pfs_node *node)
|
||||
{
|
||||
struct pfs_node *parent, *rover;
|
||||
|
||||
KASSERT(node != NULL,
|
||||
(__FUNCTION__ "(): node is NULL"));
|
||||
KASSERT(node->pn_info != NULL,
|
||||
(__FUNCTION__ "(): node has no pn_info"));
|
||||
|
||||
/* destroy children */
|
||||
if (node->pn_type == pfstype_dir ||
|
||||
node->pn_type == pfstype_procdir ||
|
||||
node->pn_type == pfstype_root)
|
||||
while (node->pn_nodes != NULL)
|
||||
pfs_destroy(node->pn_nodes);
|
||||
|
||||
/* unlink from parent */
|
||||
if ((parent = node->pn_parent) != NULL) {
|
||||
KASSERT(parent->pn_info == node->pn_info,
|
||||
(__FUNCTION__ "(): parent has different pn_info"));
|
||||
mtx_lock(&node->pn_info->pi_mutex);
|
||||
if (parent->pn_nodes == node) {
|
||||
parent->pn_nodes = node->pn_next;
|
||||
} else {
|
||||
rover = parent->pn_nodes;
|
||||
while (rover->pn_next != NULL) {
|
||||
if (rover->pn_next == node) {
|
||||
rover->pn_next = node->pn_next;
|
||||
break;
|
||||
}
|
||||
rover = rover->pn_next;
|
||||
}
|
||||
}
|
||||
mtx_unlock(&node->pn_info->pi_mutex);
|
||||
}
|
||||
|
||||
/* revoke vnodes and release memory */
|
||||
pfs_disable(node);
|
||||
FREE(node, M_PFSNODES);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mount a pseudofs instance
|
||||
*/
|
||||
|
|
@ -121,7 +306,32 @@ pfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
|
|||
int
|
||||
pfs_init(struct pfs_info *pi, struct vfsconf *vfc)
|
||||
{
|
||||
struct pfs_node *root;
|
||||
int error;
|
||||
|
||||
mtx_init(&pi->pi_mutex, "pseudofs", MTX_DEF);
|
||||
|
||||
/* set up the root diretory */
|
||||
MALLOC(root, struct pfs_node *, sizeof *root,
|
||||
M_PFSNODES, M_WAITOK|M_ZERO);
|
||||
root->pn_type = pfstype_root;
|
||||
root->pn_name[0] = '/';
|
||||
root->pn_info = pi;
|
||||
if (_pfs_fixup_dir(root) != 0) {
|
||||
FREE(root, M_PFSNODES);
|
||||
return (ENODEV); /* XXX not really the right errno */
|
||||
}
|
||||
pi->pi_root = root;
|
||||
|
||||
/* construct file hierarchy */
|
||||
error = (pi->pi_init)(pi, vfc);
|
||||
if (error) {
|
||||
pfs_destroy(root);
|
||||
pi->pi_root = NULL;
|
||||
mtx_destroy(&pi->pi_mutex);
|
||||
return (error);
|
||||
}
|
||||
|
||||
pfs_fileno_init(pi);
|
||||
if (bootverbose)
|
||||
printf("%s registered\n", pi->pi_name);
|
||||
|
|
@ -134,11 +344,16 @@ pfs_init(struct pfs_info *pi, struct vfsconf *vfc)
|
|||
int
|
||||
pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc)
|
||||
{
|
||||
int error;
|
||||
|
||||
pfs_fileno_uninit(pi);
|
||||
pfs_destroy(pi->pi_root);
|
||||
pi->pi_root = NULL;
|
||||
mtx_destroy(&pi->pi_mutex);
|
||||
if (bootverbose)
|
||||
printf("%s unregistered\n", pi->pi_name);
|
||||
return (0);
|
||||
error = (pi->pi_uninit)(pi, vfc);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ typedef enum {
|
|||
#define PFS_RAWRD 0x0004 /* raw reader */
|
||||
#define PFS_RAWWR 0x0008 /* raw writer */
|
||||
#define PFS_RAW (PFS_RAWRD|PFS_RAWWR)
|
||||
#define PFS_PROCDEP 0x0010 /* process-dependent */
|
||||
#define PFS_DISABLED 0x8000 /* node is disabled */
|
||||
|
||||
/*
|
||||
|
|
@ -66,6 +67,15 @@ struct pfs_info;
|
|||
struct pfs_node;
|
||||
struct pfs_bitmap;
|
||||
|
||||
/*
|
||||
* Init / uninit callback
|
||||
*/
|
||||
#define PFS_INIT_ARGS \
|
||||
struct pfs_info *pi, struct vfsconf *vfc
|
||||
#define PFS_INIT_PROTO(name) \
|
||||
int name(PFS_INIT_ARGS);
|
||||
typedef int (*pfs_init_t)(PFS_INIT_ARGS);
|
||||
|
||||
/*
|
||||
* Filler callback
|
||||
*/
|
||||
|
|
@ -103,8 +113,10 @@ typedef int (*pfs_vis_t)(PFS_VIS_ARGS);
|
|||
*/
|
||||
struct pfs_info {
|
||||
char pi_name[MFSNAMELEN];
|
||||
struct pfs_node *pi_root;
|
||||
pfs_init_t pi_init;
|
||||
pfs_init_t pi_uninit;
|
||||
/* members below this line aren't initialized */
|
||||
struct pfs_node *pi_root;
|
||||
/* currently, the mutex is only used to protect the bitmap */
|
||||
struct mtx pi_mutex;
|
||||
struct pfs_bitmap *pi_bitmap;
|
||||
|
|
@ -123,62 +135,56 @@ struct pfs_node {
|
|||
} u1;
|
||||
#define pn_func u1._pn_func
|
||||
#define pn_nodes u1._pn_nodes
|
||||
/*pfs_ioctl_t pn_ioctl;*/
|
||||
pfs_attr_t pn_attr;
|
||||
pfs_vis_t pn_vis;
|
||||
void *pn_data;
|
||||
int pn_flags;
|
||||
/* members below this line aren't initialized */
|
||||
|
||||
struct pfs_info *pn_info;
|
||||
struct pfs_node *pn_parent;
|
||||
struct pfs_node *pn_next;
|
||||
u_int32_t pn_fileno;
|
||||
};
|
||||
|
||||
#define PFS_NODE(name, type, fill, attr, vis, data, flags) \
|
||||
{ (name), (type), { (fill) }, (attr), (vis), (data), (flags) }
|
||||
#define PFS_DIR(name, nodes, attr, vis, data, flags) \
|
||||
PFS_NODE(name, pfstype_dir, nodes, attr, vis, data, flags)
|
||||
#define PFS_ROOT(nodes) \
|
||||
PFS_NODE("/", pfstype_root, nodes, NULL, NULL, NULL, 0)
|
||||
#define PFS_THIS \
|
||||
PFS_NODE(".", pfstype_this, NULL, NULL, NULL, NULL, 0)
|
||||
#define PFS_PARENT \
|
||||
PFS_NODE("..", pfstype_parent, NULL, NULL, NULL, NULL, 0)
|
||||
#define PFS_FILE(name, func, attr, vis, data, flags) \
|
||||
PFS_NODE(name, pfstype_file, func, attr, vis, data, flags)
|
||||
#define PFS_SYMLINK(name, func, attr, vis, data, flags) \
|
||||
PFS_NODE(name, pfstype_symlink, func, attr, vis, data, flags)
|
||||
#define PFS_PROCDIR(nodes, attr, vis, data, flags) \
|
||||
PFS_NODE("", pfstype_procdir, nodes, attr, vis, data, flags)
|
||||
#define PFS_LASTNODE \
|
||||
PFS_NODE("", pfstype_none, NULL, NULL, NULL, NULL, 0)
|
||||
|
||||
/*
|
||||
* VFS interface
|
||||
*/
|
||||
int pfs_mount (struct pfs_info *pi,
|
||||
int pfs_mount (struct pfs_info *pi,
|
||||
struct mount *mp, char *path, caddr_t data,
|
||||
struct nameidata *ndp, struct thread *td);
|
||||
int pfs_unmount (struct mount *mp, int mntflags,
|
||||
int pfs_unmount (struct mount *mp, int mntflags,
|
||||
struct thread *td);
|
||||
int pfs_root (struct mount *mp, struct vnode **vpp);
|
||||
int pfs_statfs (struct mount *mp, struct statfs *sbp,
|
||||
int pfs_root (struct mount *mp, struct vnode **vpp);
|
||||
int pfs_statfs (struct mount *mp, struct statfs *sbp,
|
||||
struct thread *td);
|
||||
int pfs_init (struct pfs_info *pi, struct vfsconf *vfc);
|
||||
int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc);
|
||||
int pfs_init (struct pfs_info *pi, struct vfsconf *vfc);
|
||||
int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc);
|
||||
|
||||
/*
|
||||
* Other utility functions
|
||||
* Directory structure construction and manipulation
|
||||
*/
|
||||
int pfs_disable (struct pfs_node *pn);
|
||||
int pfs_enable (struct pfs_node *pn);
|
||||
struct pfs_node *pfs_create_dir (struct pfs_node *parent, char *name,
|
||||
pfs_attr_t attr, pfs_vis_t vis, int flags);
|
||||
struct pfs_node *pfs_create_file(struct pfs_node *parent, char *name,
|
||||
pfs_fill_t fill, pfs_attr_t attr,
|
||||
pfs_vis_t vis, int flags);
|
||||
struct pfs_node *pfs_create_link(struct pfs_node *parent, char *name,
|
||||
pfs_fill_t fill, pfs_attr_t attr,
|
||||
pfs_vis_t vis, int flags);
|
||||
int pfs_disable (struct pfs_node *pn);
|
||||
int pfs_enable (struct pfs_node *pn);
|
||||
int pfs_destroy (struct pfs_node *pn);
|
||||
|
||||
/*
|
||||
* Now for some initialization magic...
|
||||
*/
|
||||
#define PSEUDOFS(name, root, version) \
|
||||
#define PSEUDOFS(name, version) \
|
||||
\
|
||||
static struct pfs_info name##_info = { \
|
||||
#name, \
|
||||
&(root) \
|
||||
&name##_init, \
|
||||
&name##_uninit, \
|
||||
}; \
|
||||
\
|
||||
static int \
|
||||
|
|
@ -215,6 +221,6 @@ static struct vfsops name##_vfsops = { \
|
|||
}; \
|
||||
VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC); \
|
||||
MODULE_VERSION(name, version); \
|
||||
MODULE_DEPEND(name, pseudofs, 2, 2, 2);
|
||||
MODULE_DEPEND(name, pseudofs, 3, 3, 3);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -50,13 +50,14 @@
|
|||
#if 0
|
||||
#define PFS_TRACE(foo) \
|
||||
do { \
|
||||
printf("pseudofs: %s(): ", __FUNCTION__); \
|
||||
printf("pseudofs: %s(): line %d: ", __FUNCTION__, __LINE__); \
|
||||
printf foo ; \
|
||||
printf("\n"); \
|
||||
} while (0)
|
||||
#define PFS_RETURN(err) \
|
||||
do { \
|
||||
printf("pseudofs: %s(): returning %d\n", __FUNCTION__, err); \
|
||||
printf("pseudofs: %s(): line %d: returning %d\n", \
|
||||
__FUNCTION__, __LINE__, err); \
|
||||
return (err); \
|
||||
} while (0)
|
||||
#else
|
||||
|
|
@ -101,12 +102,10 @@ static int
|
|||
pfs_access(struct vop_access_args *va)
|
||||
{
|
||||
struct vnode *vn = va->a_vp;
|
||||
struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
|
||||
struct pfs_node *pn = pvd->pvd_pn;
|
||||
struct vattr vattr;
|
||||
int error;
|
||||
|
||||
PFS_TRACE((pn->pn_name));
|
||||
PFS_TRACE((((struct pfs_vdata *)vn->v_data)->pvd_pn->pn_name));
|
||||
|
||||
error = VOP_GETATTR(vn, &vattr, va->a_cred, va->a_td);
|
||||
if (error)
|
||||
|
|
@ -251,7 +250,7 @@ pfs_lookup(struct vop_lookup_args *va)
|
|||
}
|
||||
|
||||
/* named node */
|
||||
for (pn = pd->pn_nodes; pn->pn_type; ++pn)
|
||||
for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next)
|
||||
if (pn->pn_type == pfstype_procdir)
|
||||
pdn = pn;
|
||||
else if (pn->pn_name[namelen] == '\0'
|
||||
|
|
@ -343,6 +342,9 @@ pfs_read(struct vop_read_args *va)
|
|||
if (!(pn->pn_flags & PFS_RD))
|
||||
PFS_RETURN (EBADF);
|
||||
|
||||
if (pn->pn_func == NULL)
|
||||
error = EIO;
|
||||
|
||||
/*
|
||||
* This is necessary because either process' privileges may
|
||||
* have changed since the open() call.
|
||||
|
|
@ -396,26 +398,27 @@ pfs_read(struct vop_read_args *va)
|
|||
* Iterate through directory entries
|
||||
*/
|
||||
static int
|
||||
pfs_iterate(struct thread *td, pid_t pid, struct pfs_node **pn, struct proc **p)
|
||||
pfs_iterate(struct thread *td, pid_t pid, struct pfs_node *pd,
|
||||
struct pfs_node **pn, struct proc **p)
|
||||
{
|
||||
if ((*pn)->pn_type == pfstype_none)
|
||||
return (-1);
|
||||
|
||||
if ((*pn) == NULL)
|
||||
*pn = pd->pn_nodes;
|
||||
else
|
||||
again:
|
||||
if ((*pn)->pn_type != pfstype_procdir)
|
||||
++*pn;
|
||||
*pn = (*pn)->pn_next;
|
||||
|
||||
while ((*pn)->pn_type == pfstype_procdir) {
|
||||
while (*pn != NULL && (*pn)->pn_type == pfstype_procdir) {
|
||||
if (*p == NULL)
|
||||
*p = LIST_FIRST(&allproc);
|
||||
else
|
||||
*p = LIST_NEXT(*p, p_list);
|
||||
if (*p != NULL)
|
||||
break;
|
||||
++*pn;
|
||||
*pn = (*pn)->pn_next;
|
||||
}
|
||||
|
||||
if ((*pn)->pn_type == pfstype_none)
|
||||
if ((*pn) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (!pfs_visible(td, *pn, *p ? (*p)->p_pid : pid))
|
||||
|
|
@ -460,13 +463,16 @@ pfs_readdir(struct vop_readdir_args *va)
|
|||
|
||||
/* skip unwanted entries */
|
||||
sx_slock(&allproc_lock);
|
||||
for (pn = pd->pn_nodes, p = NULL; offset > 0; offset -= PFS_DELEN)
|
||||
if (pfs_iterate(curthread, pid, &pn, &p) == -1)
|
||||
break;
|
||||
for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN)
|
||||
if (pfs_iterate(curthread, pid, pd, &pn, &p) == -1) {
|
||||
/* nothing left... */
|
||||
sx_sunlock(&allproc_lock);
|
||||
PFS_RETURN (0);
|
||||
}
|
||||
|
||||
/* fill in entries */
|
||||
entry.d_reclen = PFS_DELEN;
|
||||
while (pfs_iterate(curthread, pid, &pn, &p) != -1 && resid > 0) {
|
||||
while (pfs_iterate(curthread, pid, pd, &pn, &p) != -1 && resid > 0) {
|
||||
if (!pn->pn_parent)
|
||||
pn->pn_parent = pd;
|
||||
if (!pn->pn_fileno)
|
||||
|
|
@ -504,6 +510,7 @@ pfs_readdir(struct vop_readdir_args *va)
|
|||
sx_sunlock(&allproc_lock);
|
||||
panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type);
|
||||
}
|
||||
PFS_TRACE((entry.d_name));
|
||||
if ((error = uiomove((caddr_t)&entry, PFS_DELEN, uio))) {
|
||||
sx_sunlock(&allproc_lock);
|
||||
PFS_RETURN (error);
|
||||
|
|
@ -537,6 +544,9 @@ pfs_readlink(struct vop_readlink_args *va)
|
|||
if (vn->v_type != VLNK)
|
||||
PFS_RETURN (EINVAL);
|
||||
|
||||
if (pn->pn_func == NULL)
|
||||
error = EIO;
|
||||
|
||||
if (pvd->pvd_pid != NO_PID) {
|
||||
if ((proc = pfind(pvd->pvd_pid)) == NULL)
|
||||
PFS_RETURN (EIO);
|
||||
|
|
@ -573,11 +583,7 @@ pfs_readlink(struct vop_readlink_args *va)
|
|||
static int
|
||||
pfs_reclaim(struct vop_reclaim_args *va)
|
||||
{
|
||||
struct vnode *vn = va->a_vp;
|
||||
struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
|
||||
struct pfs_node *pn = pvd->pvd_pn;
|
||||
|
||||
PFS_TRACE((pn->pn_name));
|
||||
PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
|
||||
|
||||
return (pfs_vncache_free(va->a_vp));
|
||||
}
|
||||
|
|
@ -588,11 +594,7 @@ pfs_reclaim(struct vop_reclaim_args *va)
|
|||
static int
|
||||
pfs_setattr(struct vop_setattr_args *va)
|
||||
{
|
||||
struct vnode *vn = va->a_vp;
|
||||
struct pfs_vdata *pvd = (struct pfs_vdata *)vn->v_data;
|
||||
struct pfs_node *pn = pvd->pvd_pn;
|
||||
|
||||
PFS_TRACE((pn->pn_name));
|
||||
PFS_TRACE((((struct pfs_vdata *)va->a_vp->v_data)->pvd_pn->pn_name));
|
||||
|
||||
if (va->a_vap->va_flags != (u_long)VNOVAL)
|
||||
PFS_RETURN (EOPNOTSUPP);
|
||||
|
|
@ -622,6 +624,9 @@ pfs_write(struct vop_read_args *va)
|
|||
if (!(pn->pn_flags & PFS_WR))
|
||||
PFS_RETURN (EBADF);
|
||||
|
||||
if (pn->pn_func == NULL)
|
||||
error = EIO;
|
||||
|
||||
/*
|
||||
* This is necessary because either process' privileges may
|
||||
* have changed since the open() call.
|
||||
|
|
|
|||
Loading…
Reference in a new issue