blob: 5bd5fd36d73785e4da8b987cdddc3e8b5b7f1599 [file] [log] [blame] [raw]
/*
* (c) 2002 Pawel Jakub Dawidek <nick@garage.freebsd.pl>
*
* $Id: sysctlfs_vnops.c,v 1.6 2002/12/24 07:20:16 jules Exp $
*
*/
/*
* sysctlfs vnode interface
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/fcntl.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#if __FreeBSD_version >= 500040
#include <sys/vnode.h>
#else
#include "vnode_4.h"
#endif
#include <sys/uio.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/dirent.h>
#include <sys/malloc.h>
#include <machine/reg.h>
#if __FreeBSD_version < 500033
#include <vm/vm_zone.h>
#endif
#include <sys/pioctl.h>
#include <sys/sysctl.h>
#if 0
#include <miscfs/sysctlfs/sysctlfs.h>
#else
#include "sysctlfs.h"
#endif
#ifndef SYSCTL_CHILDREN
#define SYSCTL_CHILDREN(OID_PTR) ((struct sysctl_oid_list *)(OID_PTR)->oid_arg1)
#endif
#if __FreeBSD_version >= 500033
#define a_p a_td
#endif
static int sysctlfs_access __P((struct vop_access_args *));
#if __FreeBSD_version < 600007
static int sysctlfs_badop __P((void));
#endif
static int sysctlfs_bmap __P((struct vop_bmap_args *));
static int sysctlfs_close __P((struct vop_close_args *));
static int sysctlfs_getattr __P((struct vop_getattr_args *));
static int sysctlfs_inactive __P((struct vop_inactive_args *));
static int sysctlfs_lookup __P((struct vop_lookup_args *));
static int sysctlfs_open __P((struct vop_open_args *));
static int sysctlfs_print __P((struct vop_print_args *));
static int sysctlfs_readdir __P((struct vop_readdir_args *));
static int sysctlfs_reclaim __P((struct vop_reclaim_args *));
static int sysctlfs_setattr __P((struct vop_setattr_args *));
static int
sysctlfs_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
switch (scfs->scfs_type) {
case Sctl:
#ifdef DEBUG
uprintf("sysctlfs: OPEN [file: %s][mode: %o]\n",
scfs->scfs_oidp->oid_name, ap->a_mode);
#endif
if (((scfs->scfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) ||
((scfs->scfs_flags & O_EXCL) && (ap->a_mode & FWRITE)))
return (EBUSY);
if (ap->a_mode & FWRITE)
scfs->scfs_flags = ap->a_mode & (FWRITE|O_EXCL);
return (0);
default:
break;
}
return (0);
}
static int
sysctlfs_close(ap)
struct vop_close_args /* {
struct vnode *a_vp;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
switch (scfs->scfs_type) {
case Sctl:
#ifdef DEBUG
uprintf("sysctlfs: CLOSE [file: %s]\n", scfs->scfs_oidp->oid_name);
#endif
if ((ap->a_fflag & FWRITE) && (scfs->scfs_flags & O_EXCL))
scfs->scfs_flags &= ~(FWRITE|O_EXCL);
break;
default:
break;
}
return (0);
}
static int
sysctlfs_bmap(ap)
struct vop_bmap_args /* {
struct vnode *a_vp;
daddr_t a_bn;
struct vnode **a_vpp;
daddr_t *a_bnp;
int *a_runp;
} */ *ap;
{
#ifdef DEBUG
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
uprintf("sysctlfs: BMAP [file: %s]\n", scfs->scfs_oidp->oid_name);
#endif
#if __FreeBSD_version < 600007
if(ap->a_vpp) *ap->a_vpp = ap->a_vp;
#else
if(ap->a_bop) *ap->a_bop = &ap->a_vp->v_bufobj;
#endif
if (ap->a_bnp != NULL)
*ap->a_bnp = ap->a_bn;
if (ap->a_runp != NULL)
*ap->a_runp = 0;
return (0);
}
static int
sysctlfs_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
} */ *ap;
{
#ifdef DEBUG
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
#endif
struct vnode *vp = ap->a_vp;
#ifdef DEBUG
uprintf("sysctlfs: INACTIVE [file: %s]\n", scfs->scfs_oidp->oid_name);
#endif
VOP_UNLOCK(vp, 0
#if __FreeBSD_version < 800011
, ap->a_p
#endif
);
return (0);
}
static int
sysctlfs_reclaim(ap)
struct vop_reclaim_args /* {
struct vnode *a_vp;
} */ *ap;
{
#ifdef DEBUG
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
uprintf("sysctlfs: RECLAIM [file: %s]\n", scfs->scfs_oidp->oid_name);
#endif
return (sysctlfs_freevp(ap->a_vp));
}
static int
sysctlfs_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
printf("tag VT_SYSCTLFS, type %d, oid_name %s, mode %o, flags %lx, "
"fileno %lu, oid_kind %x\n", scfs->scfs_type,
scfs->scfs_oidp->oid_name, scfs->scfs_mode, scfs->scfs_flags,
scfs->scfs_fileno, scfs->scfs_oidp->oid_kind);
return (0);
}
#if __FreeBSD_version < 600007
/*
* generic entry point for unsupported operations
*/
static int
sysctlfs_badop()
{
#ifdef DEBUG
uprintf("sysctlfs: BADOP\n");
#endif
return (EIO);
}
#endif
/*
* Invent attributes for scfsnode (vp) and store
* them in (vap).
*/
static int
sysctlfs_getattr(ap)
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
struct vattr *vap = ap->a_vap;
int error;
size_t scsize;
error = 0;
/* start by zeroing out the attributes */
VATTR_NULL(vap);
/* next do all the common fields */
vap->va_type = ap->a_vp->v_type;
vap->va_mode = scfs->scfs_mode;
vap->va_fileid = scfs->scfs_fileno;
vap->va_flags = 0;
vap->va_blocksize = PAGE_SIZE;
vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
/*
* Make all times be current TOD.
*/
nanotime(&vap->va_ctime);
vap->va_atime = vap->va_mtime = vap->va_ctime;
vap->va_nlink = 1;
vap->va_uid = 0;
vap->va_gid = 0;
if (scfs->scfs_type == Sctl) {
error = sysctlfs_sysctl(
#if __FreeBSD_version < 800046
ap->a_p,
#else
curthread,
#endif
scfs->scfs_oidp, NULL, 0, NULL, 0, &scsize);
if (error != 0)
return (error);
vap->va_bytes = vap->va_size = scsize;
} else if (scfs->scfs_type == Sroot || scfs->scfs_type == Snode)
vap->va_size = vap->va_bytes = DEV_BSIZE;
else
panic("sysctlfs_getattr");
#ifdef DEBUG
uprintf("sysctlfs: GETATTR [file: %s][uid: %u][gid: %u][mode: %o]"
"[size: %llu]\n", scfs->scfs_oidp->oid_name, vap->va_uid,
vap->va_gid, vap->va_mode, vap->va_size);
#endif
return (error);
}
static int
sysctlfs_setattr(ap)
struct vop_setattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
#ifdef DEBUG
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
#endif
if (ap->a_vap->va_flags != VNOVAL)
return (EOPNOTSUPP);
#ifdef DEBUG
uprintf("sysctlfs: SETATTR [file: %s]\n", scfs->scfs_oidp->oid_name);
#endif
/*
* just fake out attribute setting
* it's not good to generate an error
* return, otherwise things like creat()
* will fail when they try to set the
* file length to 0.
*/
return (0);
}
static int
sysctlfs_access(ap)
struct vop_access_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
#ifdef DEBUG
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
#endif
struct vattr *vap;
struct vattr vattr;
int error;
vap = &vattr;
error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred
#if __FreeBSD_version < 800046
, ap->a_p
#endif
);
if (error)
return (error);
if (ap->a_cred->cr_uid != vap->va_uid) {
gid_t *gp;
int i;
#if __FreeBSD_version < 800052
ap->a_mode >>= 3;
#else
ap->a_accmode >>= 3;
#endif
gp = ap->a_cred->cr_groups;
for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++)
if (vap->va_gid == *gp)
goto found;
#if __FreeBSD_version < 800052
ap->a_mode >>= 3;
#else
ap->a_accmode >>= 3;
#endif
found:
;
}
#ifdef DEBUG
uprintf("sysctlfs: ACCESS [file: %s][%o & %o = %o]\n",
scfs->scfs_oidp->oid_name, vap->va_mode,
#if __FreeBSD_version < 800052
ap->a_mode, vap->va_mode & ap->a_mode
#else
ap->a_accmode, vap->va_mode & ap->a_accmode
#endif
);
#endif
#if __FreeBSD_version < 800052
if ((vap->va_mode & ap->a_mode) == ap->a_mode) return 0;
#else
if ((vap->va_mode & ap->a_accmode) == ap->a_accmode) return 0;
#endif
return (EACCES);
}
static int
sysctlfs_lookup(ap)
struct vop_lookup_args /* {
struct vnode * a_dvp;
struct vnode ** a_vpp;
struct componentname * a_cnp;
} */ *ap;
{
struct componentname *cnp = ap->a_cnp;
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
char *pname = cnp->cn_nameptr;
struct scfsnode *scfs;
struct sysctl_oid *oidp;
scfstype type;
size_t len;
#ifdef DEBUG
uprintf("sysctlfs: LOOKUP [file: %s]\n", pname);
#endif
scfs = VTOSCFS(dvp);
*vpp = NULL;
if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
return (EROFS);
if (cnp->cn_namelen == 1 && *pname == '.') {
*vpp = dvp;
VREF(dvp);
#if 0
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, curp);
#endif
return (0);
}
scfs = VTOSCFS(dvp);
switch (scfs->scfs_type) {
case Sroot:
if (cnp->cn_flags & ISDOTDOT)
return (EIO);
oidp = SLIST_FIRST(&sysctl__children);
while (oidp != NULL) {
len = strlen(oidp->oid_name);
if (cnp->cn_namelen == len &&
bcmp(oidp->oid_name, pname, cnp->cn_namelen) == 0) {
type = (oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE ? Snode : Sctl;
return (sysctlfs_allocvp(dvp->v_mount, vpp,
oidp, type));
}
oidp = SLIST_NEXT(oidp, oid_link);
}
break;
case Snode:
oidp = SLIST_FIRST(SYSCTL_CHILDREN(scfs->scfs_oidp));
if (cnp->cn_flags & ISDOTDOT) {
oidp = SLIST_FIRST(scfs->scfs_oidp->oid_parent);
if (oidp->oid_parent == &sysctl__children)
return (sysctlfs_root(dvp->v_mount,
#if __FreeBSD_version >= 600023
0,
#endif
vpp
#if __FreeBSD_version < 800087 && __FreeBSD_version >= 502123
, curthread
#endif
));
else {
/* For now ivalue != 0 is never returned. */
if (sysctlfs_findparent(dvp->v_mount,
scfs->scfs_oidp, &oidp) != 0) {
return (sysctlfs_root(dvp->v_mount,
#if __FreeBSD_version >= 600023
0,
#endif
vpp
#if __FreeBSD_version < 800087 && __FreeBSD_version >= 502123
, curthread
#endif
));
}
return (sysctlfs_allocvp(dvp->v_mount, vpp,
oidp, Snode));
}
}
while (oidp != NULL) {
len = strlen(oidp->oid_name);
if (cnp->cn_namelen == len &&
bcmp(oidp->oid_name, pname, cnp->cn_namelen) == 0) {
type = (oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE ? Snode : Sctl;
return (sysctlfs_allocvp(dvp->v_mount, vpp,
oidp, type));
}
oidp = SLIST_NEXT(oidp, oid_link);
}
break;
default:
return (ENOTDIR);
}
return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS);
}
/*
* readdir() returns directory entries from scfsnode (vp).
*
* We generate just one directory entry at a time, as it would probably
* not pay off to buffer several entries locally to save uiomove calls.
*/
static int
sysctlfs_readdir(ap)
struct vop_readdir_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
int *a_eofflag;
int *a_ncookies;
u_long **a_cookies;
} */ *ap;
{
struct uio *uio = ap->a_uio;
struct dirent d;
struct dirent *dp = &d;
struct scfsnode *scfs = VTOSCFS(ap->a_vp);
int error, i, j, off;
static u_int delen;
static struct sysctl_oid *oidp;
#ifdef DEBUG
uprintf("sysctlfs: READDIR [dir: %s]\n", scfs->scfs_oidp->oid_name);
#endif
if (ap->a_ncookies != NULL) {
#ifdef DEBUG
printf("sysctlfs: not hungry\n");
#endif
return (EOPNOTSUPP);
}
if (!delen) {
d.d_namlen = MAXNAMLEN;
delen = GENERIC_DIRSIZ(&d);
}
d.d_namlen = MAXNAMLEN;
delen = GENERIC_DIRSIZ(&d);
off = (int)uio->uio_offset;
if (off != uio->uio_offset || off < 0 ||
off % delen != 0 || uio->uio_resid < delen)
return (EINVAL);
error = 0;
i = off / delen;
switch(scfs->scfs_type) {
case Sroot:
oidp = SLIST_FIRST(&sysctl__children);
/* 0 and 1 reserved for "." and ".." */
for (j = 2; oidp != NULL && j < i; ++j)
oidp = SLIST_NEXT(oidp, oid_link);
for (; oidp != NULL && uio->uio_resid >= delen; i++) {
bzero((char *) dp, delen);
dp->d_reclen = delen;
switch (i) {
case 0: /* `.' */
case 1: /* `..' */
dp->d_fileno = i + 1;
dp->d_namlen = i + 1;
bcopy("..", dp->d_name, dp->d_namlen);
dp->d_name[i + 1] = '\0';
dp->d_type = DT_DIR;
break;
default:
dp->d_namlen = snprintf(dp->d_name,
sizeof (dp->d_name), "%s", oidp->oid_name);
if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE &&
oidp->oid_handler == NULL)
dp->d_type = DT_DIR;
else
dp->d_type = DT_REG;
dp->d_fileno = i + 3;
oidp = SLIST_NEXT(oidp, oid_link);
break;
}
/*
* And ship to userland
*/
error = uiomove((caddr_t)dp, delen, uio);
if (error)
break;
}
break;
case Snode:
if (scfs->scfs_oidp == NULL)
return (ENOENT);
oidp = SLIST_FIRST(SYSCTL_CHILDREN(scfs->scfs_oidp));
/* 0 and 1 reserved for "." and ".." */
for (j = 2; oidp != NULL && j < i; ++j)
oidp = SLIST_NEXT(oidp, oid_link);
for (; oidp != NULL && uio->uio_resid >= delen; i++) {
bzero((char *) dp, delen);
dp->d_reclen = delen;
switch (i) {
case 0: /* `.' */
case 1: /* `..' */
dp->d_fileno = i + 1;
dp->d_namlen = i + 1;
bcopy("..", dp->d_name, dp->d_namlen);
dp->d_name[i + 1] = '\0';
dp->d_type = DT_DIR;
break;
default:
dp->d_namlen = snprintf(dp->d_name,
sizeof (dp->d_name), "%s", oidp->oid_name);
if ((oidp->oid_kind & CTLTYPE) == CTLTYPE_NODE &&
oidp->oid_handler == NULL)
dp->d_type = DT_DIR;
else
dp->d_type = DT_REG;
dp->d_fileno = i + 3;
oidp = SLIST_NEXT(oidp, oid_link);
break;
}
/*
* And ship to userland
*/
error = uiomove((caddr_t)dp, delen, uio);
if (error)
break;
}
break;
default:
error = ENOTDIR;
break;
}
uio->uio_offset = i * delen;
return (error);
}
/*
* sysctlfs vnode operations.
*/
#if __FreeBSD_version < 600007
vop_t **sysctlfs_vnodeop_p;
static struct vnodeopv_entry_desc sysctlfs_vnodeop_entries[] = {
{ &vop_default_desc, (vop_t *) vop_defaultop },
{ &vop_pathconf_desc, (vop_t *) vop_stdpathconf },
{ &vop_access_desc, (vop_t *) sysctlfs_access },
{ &vop_bmap_desc, (vop_t *) sysctlfs_bmap },
{ &vop_close_desc, (vop_t *) sysctlfs_close },
{ &vop_getattr_desc, (vop_t *) sysctlfs_getattr },
{ &vop_inactive_desc, (vop_t *) sysctlfs_inactive },
{ &vop_lookup_desc, (vop_t *) sysctlfs_lookup },
{ &vop_open_desc, (vop_t *) sysctlfs_open },
{ &vop_print_desc, (vop_t *) sysctlfs_print },
{ &vop_read_desc, (vop_t *) sysctlfs_read },
{ &vop_readdir_desc, (vop_t *) sysctlfs_readdir },
{ &vop_reclaim_desc, (vop_t *) sysctlfs_reclaim },
{ &vop_setattr_desc, (vop_t *) sysctlfs_setattr },
{ &vop_write_desc, (vop_t *) sysctlfs_write },
{ &vop_advlock_desc, (vop_t *) sysctlfs_badop },
{ &vop_create_desc, (vop_t *) sysctlfs_badop },
{ &vop_ioctl_desc, (vop_t *) sysctlfs_badop },
{ &vop_link_desc, (vop_t *) sysctlfs_badop },
{ &vop_mkdir_desc, (vop_t *) sysctlfs_badop },
{ &vop_mknod_desc, (vop_t *) sysctlfs_badop },
{ &vop_readlink_desc, (vop_t *) sysctlfs_badop },
{ &vop_remove_desc, (vop_t *) sysctlfs_badop },
{ &vop_rename_desc, (vop_t *) sysctlfs_badop },
{ &vop_rmdir_desc, (vop_t *) sysctlfs_badop },
{ &vop_symlink_desc, (vop_t *) sysctlfs_badop },
{ NULL, NULL }
};
static struct vnodeopv_desc sysctlfs_vnodeop_opv_desc =
{ &sysctlfs_vnodeop_p, sysctlfs_vnodeop_entries };
VNODEOP_SET(sysctlfs_vnodeop_opv_desc);
#else
struct vop_vector sysctlfs_vnodeops = {
.vop_default = &default_vnodeops,
.vop_pathconf = vop_stdpathconf,
.vop_access = sysctlfs_access,
.vop_bmap = sysctlfs_bmap,
.vop_close = sysctlfs_close,
.vop_getattr = sysctlfs_getattr,
.vop_inactive = sysctlfs_inactive,
.vop_lookup = sysctlfs_lookup,
.vop_open = sysctlfs_open,
.vop_print = sysctlfs_print,
.vop_read = sysctlfs_read,
.vop_readdir = sysctlfs_readdir,
.vop_reclaim = sysctlfs_reclaim,
.vop_setattr = sysctlfs_setattr,
.vop_write = sysctlfs_write,
.vop_advlock = VOP_EOPNOTSUPP,
.vop_create = VOP_EOPNOTSUPP,
.vop_ioctl = VOP_EOPNOTSUPP,
.vop_link = VOP_EOPNOTSUPP,
.vop_mkdir = VOP_EOPNOTSUPP,
.vop_mknod = VOP_EOPNOTSUPP,
.vop_readlink = VOP_EOPNOTSUPP,
.vop_remove = VOP_EOPNOTSUPP,
.vop_rename = VOP_EOPNOTSUPP,
.vop_rmdir = VOP_EOPNOTSUPP,
.vop_symlink = VOP_EOPNOTSUPP,
};
#endif