Allow additional entries (files, subdirs) in kernfs. Also allow

defining additional kfstypes and provide hooks to run arbitrary code
for any vnodeop on the additional types.
This commit is contained in:
cl 2004-05-07 15:33:17 +00:00
parent 52fc58caa3
commit 65569a4d16
2 changed files with 396 additions and 24 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: kernfs.h,v 1.21 2004/05/07 15:06:15 cl Exp $ */
/* $NetBSD: kernfs.h,v 1.22 2004/05/07 15:33:17 cl Exp $ */
/*
* Copyright (c) 1992, 1993
@ -57,10 +57,13 @@ typedef enum {
KFSipsecspdir, /* ipsec security policy (top dir) */
KFSipsecsa, /* ipsec security association entry */
KFSipsecsp, /* ipsec security policy entry */
KFSsubdir, /* directory */
KFSlasttype, /* last used type */
KFSmaxtype = (1<<6) - 1 /* last possible type */
} kfstype;
/*
* control data for the kern file system.
* Control data for the kern file system.
*/
struct kern_target {
u_char kt_type;
@ -72,6 +75,18 @@ struct kern_target {
mode_t kt_mode;
};
struct dyn_kern_target {
struct kern_target dkt_kt;
SIMPLEQ_ENTRY(dyn_kern_target) dkt_queue;
};
struct kernfs_subdir {
SIMPLEQ_HEAD(,dyn_kern_target) ks_entries;
unsigned int ks_nentries;
unsigned int ks_dirs;
const struct kern_target *ks_parent;
};
struct kernfs_node {
LIST_ENTRY(kernfs_node) kfs_hash; /* hash chain */
TAILQ_ENTRY(kernfs_node) kfs_list; /* flat list */
@ -93,9 +108,11 @@ struct kernfs_mount {
#define UIO_MX 32
#define KERNFS_FILENO(kt, typ, cookie) \
((kt >= &kern_targets[0] && kt < &kern_targets[nkern_targets]) ? \
2 + ((kt) - &kern_targets[0]) \
((kt >= &kern_targets[0] && kt < &kern_targets[static_nkern_targets]) \
? 2 + ((kt) - &kern_targets[0]) \
: (((cookie + 1) << 6) | (typ)))
#define KERNFS_TYPE_FILENO(typ, cookie) \
(((cookie + 1) << 6) | (typ))
#define VFSTOKERNFS(mp) ((struct kernfs_mount *)((mp)->mnt_data))
#define VTOKERN(vp) ((struct kernfs_node *)(vp)->v_data)
@ -103,6 +120,7 @@ struct kernfs_mount {
extern const struct kern_target kern_targets[];
extern int nkern_targets;
extern const int static_nkern_targets;
extern int (**kernfs_vnodeop_p) __P((void *));
extern struct vfsops kernfs_vfsops;
extern dev_t rrootdev;
@ -121,4 +139,57 @@ int kernfs_allocvp __P((struct mount *, struct vnode **, kfstype,
void kernfs_revoke_sa __P((struct secasvar *));
void kernfs_revoke_sp __P((struct secpolicy *));
/*
* Data types for the kernfs file operations.
*/
typedef enum {
KERNFS_XWRITE,
KERNFS_FILEOP_CLOSE,
KERNFS_FILEOP_GETATTR,
KERNFS_FILEOP_IOCTL,
KERNFS_FILEOP_MMAP,
KERNFS_FILEOP_OPEN,
KERNFS_FILEOP_WRITE,
} kfsfileop;
struct kernfs_fileop {
kfstype kf_type;
kfsfileop kf_fileop;
union {
void *_kf_genop;
int (*_kf_vop)(void *);
int (*_kf_xwrite)
(const struct kernfs_node *, char *, size_t);
} _kf_opfn;
SPLAY_ENTRY(kernfs_fileop) kf_node;
};
#define kf_genop _kf_opfn
#define kf_vop _kf_opfn._kf_vop
#define kf_xwrite _kf_opfn._kf_xwrite
typedef struct kern_target kernfs_parentdir_t;
typedef struct dyn_kern_target kernfs_entry_t;
/*
* Functions for adding kernfs datatypes and nodes.
*/
kfstype kernfs_alloctype(int, const struct kernfs_fileop *);
#define KERNFS_ALLOCTYPE(kf) kernfs_alloctype(sizeof((kf)) / \
sizeof((kf)[0]), (kf))
#define KERNFS_ALLOCENTRY(dkt, m_type, m_flags) \
dkt = (struct dyn_kern_target *)malloc( \
sizeof(struct dyn_kern_target), (m_type), (m_flags))
#define KERNFS_INITENTRY(dkt, type, name, data, tag, vtype, mode) do { \
(dkt)->dkt_kt.kt_type = (type); \
(dkt)->dkt_kt.kt_namlen = strlen((name)); \
(dkt)->dkt_kt.kt_name = (name); \
(dkt)->dkt_kt.kt_data = (data); \
(dkt)->dkt_kt.kt_tag = (tag); \
(dkt)->dkt_kt.kt_vtype = (vtype); \
(dkt)->dkt_kt.kt_mode = (mode); \
} while (/*CONSTCOND*/0)
#define KERNFS_ENTOPARENTDIR(dkt) &(dkt)->dkt_kt
int kernfs_addentry __P((kernfs_parentdir_t *, kernfs_entry_t *));
#endif /* _KERNEL */

View File

@ -1,4 +1,4 @@
/* $NetBSD: kernfs_vnops.c,v 1.101 2004/05/07 15:06:15 cl Exp $ */
/* $NetBSD: kernfs_vnops.c,v 1.102 2004/05/07 15:33:17 cl Exp $ */
/*
* Copyright (c) 1992, 1993
@ -39,7 +39,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kernfs_vnops.c,v 1.101 2004/05/07 15:06:15 cl Exp $");
__KERNEL_RCSID(0, "$NetBSD: kernfs_vnops.c,v 1.102 2004/05/07 15:33:17 cl Exp $");
#ifdef _KERNEL_OPT
#include "opt_ipsec.h"
@ -80,8 +80,8 @@ __KERNEL_RCSID(0, "$NetBSD: kernfs_vnops.c,v 1.101 2004/05/07 15:06:15 cl Exp $"
#define READ_MODE (S_IRUSR|S_IRGRP|S_IROTH)
#define WRITE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
#define UREAD_MODE (S_IRUSR)
#define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
#define UDIR_MODE (S_IRUSR|S_IXUSR)
#define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
#define UDIR_MODE (S_IRUSR|S_IXUSR)
#define N(s) sizeof(s)-1, s
const struct kern_target kern_targets[] = {
@ -113,6 +113,12 @@ const struct kern_target kern_targets[] = {
{ DT_REG, N("version"), (void *)version,
KFSstring, VREG, READ_MODE },
};
const struct kern_target subdir_targets[] = {
/* NOTE: The name must be less than UIO_MX-16 chars in length */
/* name data tag type ro/rw */
{ DT_DIR, N("."), 0, KFSsubdir, VDIR, DIR_MODE },
{ DT_DIR, N(".."), 0, KFSkern, VDIR, DIR_MODE },
};
#ifdef IPSEC
const struct kern_target ipsecsa_targets[] = {
/* NOTE: The name must be less than UIO_MX-16 chars in length */
@ -132,12 +138,36 @@ const struct kern_target ipsecsp_kt =
{ DT_DIR, N(""), 0, KFSipsecsp, VREG, UREAD_MODE };
#endif
#undef N
SIMPLEQ_HEAD(,dyn_kern_target) dyn_kern_targets =
SIMPLEQ_HEAD_INITIALIZER(dyn_kern_targets);
int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]);
const int static_nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]);
#ifdef IPSEC
int nipsecsa_targets = sizeof(ipsecsa_targets) / sizeof(ipsecsa_targets[0]);
int nipsecsp_targets = sizeof(ipsecsp_targets) / sizeof(ipsecsp_targets[0]);
int nkern_dirs = 4; /* 2 extra subdirs */
#else
int nkern_dirs = 2;
#endif
int kernfs_try_fileop(kfstype, kfsfileop, void *, int);
int kernfs_try_xwrite(kfstype, const struct kernfs_node *, char *,
size_t, int);
static int kernfs_default_xwrite(void *v);
static int kernfs_default_fileop_getattr(void *);
/* must include all fileop's */
const struct kernfs_fileop kernfs_default_fileops[] = {
{ .kf_fileop = KERNFS_XWRITE },
{ .kf_fileop = KERNFS_FILEOP_OPEN },
{ .kf_fileop = KERNFS_FILEOP_GETATTR,
.kf_vop = kernfs_default_fileop_getattr },
{ .kf_fileop = KERNFS_FILEOP_IOCTL },
{ .kf_fileop = KERNFS_FILEOP_MMAP },
{ .kf_fileop = KERNFS_FILEOP_CLOSE },
{ .kf_fileop = KERNFS_FILEOP_WRITE, .kf_vop = kernfs_default_xwrite },
};
int kernfs_lookup __P((void *));
#define kernfs_create genfs_eopnotsupp
@ -150,9 +180,10 @@ int kernfs_setattr __P((void *));
int kernfs_read __P((void *));
int kernfs_write __P((void *));
#define kernfs_fcntl genfs_fcntl
#define kernfs_ioctl genfs_enoioctl
int kernfs_ioctl __P((void *));
#define kernfs_poll genfs_poll
#define kernfs_revoke genfs_revoke
int kernfs_mmap __P((void *));
#define kernfs_fsync genfs_nullop
#define kernfs_seek genfs_nullop
#define kernfs_remove genfs_eopnotsupp
@ -202,6 +233,7 @@ const struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = {
{ &vop_ioctl_desc, kernfs_ioctl }, /* ioctl */
{ &vop_poll_desc, kernfs_poll }, /* poll */
{ &vop_revoke_desc, kernfs_revoke }, /* revoke */
{ &vop_mmap_desc, kernfs_mmap }, /* mmap */
{ &vop_fsync_desc, kernfs_fsync }, /* fsync */
{ &vop_seek_desc, kernfs_seek }, /* seek */
{ &vop_remove_desc, kernfs_remove }, /* remove */
@ -235,6 +267,109 @@ const struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = {
const struct vnodeopv_desc kernfs_vnodeop_opv_desc =
{ &kernfs_vnodeop_p, kernfs_vnodeop_entries };
static __inline int
kernfs_fileop_compare(struct kernfs_fileop *a, struct kernfs_fileop *b)
{
if (a->kf_type < b->kf_type)
return -1;
if (a->kf_type > b->kf_type)
return 1;
if (a->kf_fileop < b->kf_fileop)
return -1;
if (a->kf_fileop > b->kf_fileop)
return 1;
return (0);
}
SPLAY_HEAD(kfsfileoptree, kernfs_fileop) kfsfileoptree =
SPLAY_INITIALIZER(kfsfileoptree);
SPLAY_PROTOTYPE(kfsfileoptree, kernfs_fileop, kf_node, kernfs_fileop_compare);
SPLAY_GENERATE(kfsfileoptree, kernfs_fileop, kf_node, kernfs_fileop_compare);
kfstype
kernfs_alloctype(int nkf, const struct kernfs_fileop *kf)
{
static u_char nextfreetype = KFSlasttype;
struct kernfs_fileop *dkf, *fkf, skf;
int i;
/* XXX need to keep track of dkf's memory if we support
deallocating types */
dkf = malloc(sizeof(kernfs_default_fileops), M_TEMP, M_WAITOK);
memcpy(dkf, kernfs_default_fileops, sizeof(kernfs_default_fileops));
for (i = 0; i < sizeof(kernfs_default_fileops) /
sizeof(kernfs_default_fileops[0]); i++) {
dkf[i].kf_type = nextfreetype;
SPLAY_INSERT(kfsfileoptree, &kfsfileoptree, &dkf[i]);
}
for (i = 0; i < nkf; i++) {
skf.kf_type = nextfreetype;
skf.kf_fileop = kf[i].kf_fileop;
if ((fkf = SPLAY_FIND(kfsfileoptree, &kfsfileoptree, &skf)))
fkf->kf_genop = kf[i].kf_genop;
}
return nextfreetype++;
}
int
kernfs_try_fileop(kfstype type, kfsfileop fileop, void *v, int error)
{
struct kernfs_fileop *kf, skf;
skf.kf_type = type;
skf.kf_fileop = fileop;
if ((kf = SPLAY_FIND(kfsfileoptree, &kfsfileoptree, &skf)))
if (kf->kf_vop)
return kf->kf_vop(v);
return error;
}
int
kernfs_try_xwrite(kfstype type, const struct kernfs_node *kfs, char *buf,
size_t len, int error)
{
struct kernfs_fileop *kf, skf;
skf.kf_type = type;
skf.kf_fileop = KERNFS_XWRITE;
if ((kf = SPLAY_FIND(kfsfileoptree, &kfsfileoptree, &skf)))
if (kf->kf_xwrite)
return kf->kf_xwrite(kfs, buf, len);
return error;
}
int
kernfs_addentry(kernfs_parentdir_t *pkt, kernfs_entry_t *dkt)
{
struct kernfs_subdir *ks, *parent;
if (pkt == NULL) {
SIMPLEQ_INSERT_TAIL(&dyn_kern_targets, dkt, dkt_queue);
nkern_targets++;
if (dkt->dkt_kt.kt_vtype == VDIR)
nkern_dirs++;
} else {
parent = (struct kernfs_subdir *)pkt->kt_data;
SIMPLEQ_INSERT_TAIL(&parent->ks_entries, dkt, dkt_queue);
parent->ks_nentries++;
if (dkt->dkt_kt.kt_vtype == VDIR)
parent->ks_dirs++;
}
if (dkt->dkt_kt.kt_vtype == VDIR && dkt->dkt_kt.kt_data == NULL) {
ks = malloc(sizeof(struct kernfs_subdir),
M_TEMP, M_WAITOK);
SIMPLEQ_INIT(&ks->ks_entries);
ks->ks_nentries = 2; /* . and .. */
ks->ks_dirs = 2;
ks->ks_parent = pkt ? pkt : &kern_targets[0];
dkt->dkt_kt.kt_data = ks;
}
return 0;
}
static int
kernfs_xread(kfs, off, bufp, len, wrlen)
struct kernfs_node *kfs;
@ -412,7 +547,7 @@ kernfs_xwrite(kfs, buf, len)
return (0);
default:
return (EIO);
return kernfs_try_xwrite(kfs->kfs_type, kfs, buf, len, EIO);
}
}
@ -436,6 +571,8 @@ kernfs_lookup(v)
const char *pname = cnp->cn_nameptr;
const struct kernfs_node *kfs;
const struct kern_target *kt;
const struct dyn_kern_target *dkt;
const struct kernfs_subdir *ks;
int error, i, wantpunlock;
#ifdef IPSEC
char *ep;
@ -464,12 +601,19 @@ kernfs_lookup(v)
if (cnp->cn_flags & ISDOTDOT)
return (EIO);
for (i = 0; i < nkern_targets; i++) {
for (i = 0; i < static_nkern_targets; i++) {
kt = &kern_targets[i];
if (cnp->cn_namelen == kt->kt_namlen &&
memcmp(kt->kt_name, pname, cnp->cn_namelen) == 0)
goto found;
}
SIMPLEQ_FOREACH(dkt, &dyn_kern_targets, dkt_queue) {
if (cnp->cn_namelen == dkt->dkt_kt.kt_namlen &&
memcmp(dkt->dkt_kt.kt_name, pname, cnp->cn_namelen) == 0) {
kt = &dkt->dkt_kt;
goto found;
}
}
break;
found:
@ -480,6 +624,22 @@ kernfs_lookup(v)
}
return (error);
case KFSsubdir:
ks = (struct kernfs_subdir *)kfs->kfs_kt->kt_data;
if (cnp->cn_flags & ISDOTDOT) {
kt = ks->ks_parent;
goto found;
}
SIMPLEQ_FOREACH(dkt, &ks->ks_entries, dkt_queue) {
if (cnp->cn_namelen == dkt->dkt_kt.kt_namlen &&
memcmp(dkt->dkt_kt.kt_name, pname, cnp->cn_namelen) == 0) {
kt = &dkt->dkt_kt;
goto found;
}
}
break;
#ifdef IPSEC
case KFSipsecsadir:
if (cnp->cn_flags & ISDOTDOT) {
@ -575,7 +735,8 @@ kernfs_open(v)
#endif
default:
return (0);
return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_OPEN,
v, 0);
}
}
@ -599,7 +760,8 @@ kernfs_close(v)
#endif
default:
break;
return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_CLOSE,
v, 0);
}
return (0);
@ -625,6 +787,24 @@ kernfs_access(v)
ap->a_mode, ap->a_cred));
}
static int
kernfs_default_fileop_getattr(v)
void *v;
{
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap = v;
struct vattr *vap = ap->a_vap;
vap->va_nlink = 1;
vap->va_bytes = vap->va_size = 0;
return 0;
}
int
kernfs_getattr(v)
void *v;
@ -636,6 +816,7 @@ kernfs_getattr(v)
struct proc *a_p;
} */ *ap = v;
struct kernfs_node *kfs = VTOKERN(ap->a_vp);
struct kernfs_subdir *ks;
struct vattr *vap = ap->a_vap;
int error = 0;
char strbuf[KSTRING], *buf;
@ -670,11 +851,7 @@ kernfs_getattr(v)
switch (kfs->kfs_type) {
case KFSkern:
#ifdef IPSEC
vap->va_nlink = 4; /* 2 extra subdirs */
#else
vap->va_nlink = 2;
#endif
vap->va_nlink = nkern_dirs;
vap->va_bytes = vap->va_size = DEV_BSIZE;
break;
@ -683,6 +860,12 @@ kernfs_getattr(v)
vap->va_bytes = vap->va_size = DEV_BSIZE;
break;
case KFSsubdir:
ks = (struct kernfs_subdir *)kfs->kfs_kt->kt_data;
vap->va_nlink = ks->ks_dirs;
vap->va_bytes = vap->va_size = DEV_BSIZE;
break;
case KFSnull:
case KFStime:
case KFSint:
@ -715,7 +898,8 @@ kernfs_getattr(v)
#endif
default:
error = EINVAL;
error = kernfs_try_fileop(kfs->kfs_type,
KERNFS_FILEOP_GETATTR, v, EINVAL);
break;
}
@ -764,8 +948,8 @@ kernfs_read(v)
return (error);
}
int
kernfs_write(v)
static int
kernfs_default_xwrite(v)
void *v;
{
struct vop_write_args /* {
@ -794,6 +978,56 @@ kernfs_write(v)
return (kernfs_xwrite(kfs, strbuf, xlen));
}
int
kernfs_write(v)
void *v;
{
struct vop_write_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap = v;
struct kernfs_node *kfs = VTOKERN(ap->a_vp);
return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_WRITE, v, 0);
}
int
kernfs_ioctl(v)
void *v;
{
struct vop_ioctl_args /* {
const struct vnodeop_desc *a_desc;
struct vnode *a_vp;
u_long a_command;
void *a_data;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap = v;
struct kernfs_node *kfs = VTOKERN(ap->a_vp);
return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_IOCTL, v,
EPASSTHROUGH);
}
int
kernfs_mmap(v)
void *v;
{
struct vop_mmap_args /* {
const struct vnodeop_desc *a_desc;
struct vnode *a_vp;
int a_fflags;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap = v;
struct kernfs_node *kfs = VTOKERN(ap->a_vp);
return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_MMAP, v, 0);
}
static int
kernfs_setdirentfileno_kt(struct dirent *d, const struct kern_target *kt,
u_int32_t value, struct vop_readdir_args *ap)
@ -863,7 +1097,9 @@ kernfs_readdir(v)
struct dirent d;
struct kernfs_node *kfs = VTOKERN(ap->a_vp);
const struct kern_target *kt;
off_t i;
const struct dyn_kern_target *dkt = NULL;
const struct kernfs_subdir *ks;
off_t i, j;
int error;
off_t *cookies = NULL;
int ncookies = 0, n;
@ -897,7 +1133,23 @@ kernfs_readdir(v)
n = 0;
for (; i < nkern_targets && uio->uio_resid >= UIO_MX; i++) {
kt = &kern_targets[i];
if (i < static_nkern_targets)
kt = &kern_targets[i];
else {
if (dkt == NULL) {
dkt = SIMPLEQ_FIRST(&dyn_kern_targets);
for (j = static_nkern_targets; j < i &&
dkt != NULL; j++)
dkt = SIMPLEQ_NEXT(dkt, dkt_queue);
if (j != i)
break;
} else {
dkt = SIMPLEQ_NEXT(dkt, dkt_queue);
if (dkt == NULL)
break;
}
kt = &dkt->dkt_kt;
}
if (kt->kt_tag == KFSdevice) {
dev_t *dp = kt->kt_data;
struct vnode *fvp;
@ -948,6 +1200,55 @@ kernfs_readdir(v)
ncookies = n;
break;
case KFSsubdir:
ks = (struct kernfs_subdir *)kfs->kfs_kt->kt_data;
if (i >= ks->ks_nentries)
return (0);
if (ap->a_ncookies) {
ncookies = min(ncookies, (ks->ks_nentries - i));
cookies = malloc(ncookies * sizeof(off_t), M_TEMP,
M_WAITOK);
*ap->a_cookies = cookies;
}
dkt = SIMPLEQ_FIRST(&ks->ks_entries);
for (j = 0; j < i && dkt != NULL; j++)
dkt = SIMPLEQ_NEXT(dkt, dkt_queue);
n = 0;
for (; i < ks->ks_nentries && uio->uio_resid >= UIO_MX; i++) {
if (i < 2)
kt = &subdir_targets[i];
else {
/* check if ks_nentries lied to us */
if (dkt == NULL)
break;
kt = &dkt->dkt_kt;
dkt = SIMPLEQ_NEXT(dkt, dkt_queue);
}
if (kt->kt_tag == KFSdevice) {
dev_t *dp = kt->kt_data;
struct vnode *fvp;
if (*dp == NODEV ||
!vfinddev(*dp, kt->kt_vtype, &fvp))
continue;
}
d.d_namlen = kt->kt_namlen;
if ((error = kernfs_setdirentfileno(&d, i, kfs,
ks->ks_parent, kt, ap)) != 0)
break;
memcpy(d.d_name, kt->kt_name, kt->kt_namlen + 1);
d.d_type = kt->kt_type;
if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0)
break;
if (cookies)
*cookies++ = i + 1;
n++;
}
ncookies = n;
break;
#ifdef IPSEC
case KFSipsecsadir:
/* count SA in the system */
@ -1030,7 +1331,7 @@ kernfs_readdir(v)
TAILQ_FOREACH(sp, &sptailq, tailq)
n++;
if (i >= 2 + n)
if (i >= nipsecsp_targets + n)
return (0);
if (ap->a_ncookies) {