#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/namei.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
#include <sys/disklabel.h>
#include <sys/lock.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <miscfs/specfs/specdev.h>
#include <sys/dirent.h>
#include <sys/vmmeter.h>
#include <sys/vm.h>
#include "devfsdefs.h"
static int
devfs_lookup(struct vop_lookup_args *ap)
{
struct componentname *cnp = ap->a_cnp;
struct vnode *dir_vnode = ap->a_dvp;
struct vnode **result_vnode = ap->a_vpp;
devnode_t * dir_node;
devnode_t * node = NULL;
devdirent_t * nodename;
int flags = cnp->cn_flags;
int op = cnp->cn_nameiop;
int lockparent = flags & LOCKPARENT;
int wantparent = flags & (LOCKPARENT|WANTPARENT);
int error = 0;
struct proc *p = cnp->cn_proc;
char heldchar;
*result_vnode = NULL;
if (dir_vnode->v_usecount == 0)
printf("devfs_lookup: dir had no refs ");
dir_node = VTODN(dir_vnode);
if (dir_node->dn_type != DEV_DIR) {
return (ENOTDIR);
}
if ((error = VOP_ACCESS(dir_vnode, VEXEC, cnp->cn_cred, p)) != 0) {
return (error);
}
heldchar = cnp->cn_nameptr[cnp->cn_namelen];
cnp->cn_nameptr[cnp->cn_namelen] = '\0';
DEVFS_LOCK(p);
nodename = dev_findname(dir_node,cnp->cn_nameptr);
if (nodename) {
node = nodename->de_dnp;
node->dn_last_lookup = nodename;
error = devfs_dntovn(node, result_vnode, p);
}
DEVFS_UNLOCK(p);
cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
if (error)
return (error);
if (!nodename) {
if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
return ENOENT;
}
if ((error = VOP_ACCESS(dir_vnode, VWRITE,
cnp->cn_cred, p)) != 0)
{
return (error);
}
cnp->cn_flags |= SAVENAME;
if (!lockparent)
VOP_UNLOCK(dir_vnode, 0, p);
return (EJUSTRETURN);
}
if (op == DELETE && (flags & ISLASTCN)) {
if ((error = VOP_ACCESS(dir_vnode, VWRITE,
cnp->cn_cred, p)) != 0)
return (error);
if (dir_node == node) {
VREF(dir_vnode);
*result_vnode = dir_vnode;
return (0);
}
#ifdef NOTYET
if ((dir_node->mode & ISVTX) &&
cnp->cn_cred->cr_uid != 0 &&
cnp->cn_cred->cr_uid != dir_node->uid &&
cnp->cn_cred->cr_uid != node->uid) {
VOP_UNLOCK(*result_vnode, 0, p);
return (EPERM);
}
#endif
if (!lockparent)
VOP_UNLOCK(dir_vnode, 0, p);
return (0);
}
if (op == RENAME && wantparent && (flags & ISLASTCN)) {
if ((error = VOP_ACCESS(dir_vnode, VWRITE,
cnp->cn_cred, p)) != 0)
return (error);
if (dir_node == node)
return (EISDIR);
cnp->cn_flags |= SAVENAME;
if (!lockparent)
VOP_UNLOCK(dir_vnode, 0, p);
return (0);
}
if (flags & ISDOTDOT) {
VOP_UNLOCK(dir_vnode, 0, p);
if (lockparent && (flags & ISLASTCN))
vn_lock(dir_vnode, LK_EXCLUSIVE | LK_RETRY, p);
} else if (dir_node == node) {
#if 0
VREF(dir_vnode);
#endif
*result_vnode = dir_vnode;
} else {
if (!lockparent || (flags & ISLASTCN))
VOP_UNLOCK(dir_vnode, 0, p);
}
return (0);
}
static int
devfs_access(struct vop_access_args *ap)
{
struct vnode *vp = ap->a_vp;
int mode = ap->a_mode;
struct ucred *cred = ap->a_cred;
devnode_t * file_node;
gid_t *gp;
int i;
struct proc *p = ap->a_p;
file_node = VTODN(vp);
if (p == NULL)
return 0;
if (cred->cr_uid != file_node->dn_uid)
{
mode >>= 3;
gp = cred->cr_groups;
for (i = 0; i < cred->cr_ngroups; i++, gp++)
{
if (file_node->dn_gid == *gp)
{
goto found;
}
}
mode >>= 3;
found:
;
}
if ((file_node->dn_mode & mode) == mode)
return (0);
if( suser(cred, &ap->a_p->p_acflag) == 0)
return 0;
return (EACCES);
}
static int
devfs_getattr(struct vop_getattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
devnode_t * file_node;
struct timeval tv;
file_node = VTODN(vp);
tv = time;
dn_times(file_node, tv, tv);
vap->va_rdev = 0;
vap->va_mode = file_node->dn_mode;
switch (file_node->dn_type)
{
case DEV_DIR:
vap->va_rdev = (dev_t)file_node->dn_dvm;
vap->va_mode |= (S_IFDIR);
break;
case DEV_CDEV:
vap->va_rdev = file_node->dn_typeinfo.dev;
vap->va_mode |= (S_IFCHR);
break;
case DEV_BDEV:
vap->va_rdev = file_node->dn_typeinfo.dev;
vap->va_mode |= (S_IFBLK);
break;
case DEV_SLNK:
vap->va_mode |= (S_IFLNK);
break;
}
vap->va_type = vp->v_type;
vap->va_nlink = file_node->dn_links;
vap->va_uid = file_node->dn_uid;
vap->va_gid = file_node->dn_gid;
vap->va_fsid = (int32_t)(void *)file_node->dn_dvm;
vap->va_fileid = (int32_t)(void *)file_node;
vap->va_size = file_node->dn_len;
if (vp->v_type == VBLK)
vap->va_blocksize = BLKDEV_IOSIZE;
else if (vp->v_type == VCHR)
vap->va_blocksize = MAXPHYSIO;
else
vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
if (file_node->dn_ctime.tv_sec == 0)
file_node->dn_ctime.tv_sec = boottime.tv_sec;
if (file_node->dn_mtime.tv_sec == 0)
file_node->dn_mtime.tv_sec = boottime.tv_sec;
if (file_node->dn_atime.tv_sec == 0)
file_node->dn_atime.tv_sec = boottime.tv_sec;
vap->va_ctime = file_node->dn_ctime;
vap->va_mtime = file_node->dn_mtime;
vap->va_atime = file_node->dn_atime;
vap->va_gen = 0;
vap->va_flags = 0;
vap->va_bytes = file_node->dn_len;
vap->va_filerev = 0;
vap->va_vaflags = 0;
return 0;
}
static int
devfs_setattr(struct vop_setattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
struct ucred *cred = ap->a_cred;
struct proc *p = ap->a_p;
int error = 0;
gid_t *gp;
int i;
devnode_t * file_node;
struct timeval atimeval, mtimeval;
if (vap->va_flags != VNOVAL)
return (EOPNOTSUPP);
file_node = VTODN(vp);
if ((vap->va_type != VNON) ||
(vap->va_nlink != VNOVAL) ||
(vap->va_fsid != VNOVAL) ||
(vap->va_fileid != VNOVAL) ||
(vap->va_blocksize != VNOVAL) ||
(vap->va_rdev != VNOVAL) ||
(vap->va_bytes != VNOVAL) ||
(vap->va_gen != VNOVAL ))
{
return EINVAL;
}
if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
if (cred->cr_uid != file_node->dn_uid &&
(error = suser(cred, &p->p_acflag)) &&
((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
(error = VOP_ACCESS(vp, VWRITE, cred, p))))
return (error);
if (vap->va_atime.tv_sec != VNOVAL)
file_node->dn_flags |= DN_ACCESS;
if (vap->va_mtime.tv_sec != VNOVAL)
file_node->dn_flags |= DN_CHANGE | DN_UPDATE;
atimeval.tv_sec = vap->va_atime.tv_sec;
atimeval.tv_usec = vap->va_atime.tv_nsec / 1000;
mtimeval.tv_sec = vap->va_mtime.tv_sec;
mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000;
if (error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))
return (error);
}
if (vap->va_mode != (u_short)VNOVAL) {
if ((cred->cr_uid != file_node->dn_uid)
&& (error = suser(cred, &p->p_acflag)))
return (error);
file_node->dn_mode &= ~07777;
file_node->dn_mode |= vap->va_mode & 07777;
}
if (vap->va_uid != (uid_t)VNOVAL) {
if (error = suser(cred, &p->p_acflag))
return (error);
file_node->dn_uid = vap->va_uid;
}
if (vap->va_gid != (gid_t)VNOVAL) {
if (cred->cr_uid == file_node->dn_uid){
gp = cred->cr_groups;
for (i = 0; i < cred->cr_ngroups; i++, gp++) {
if (vap->va_gid == *gp)
goto cando;
}
}
if (error = suser(cred, &p->p_acflag))
return (error);
cando:
file_node->dn_gid = vap->va_gid;
}
#if 0
if (vap->va_flags != VNOVAL) {
if (error = suser(cred, &p->p_acflag))
return error;
if (cred->cr_uid == 0)
;
else {
}
}
#endif
return error;
}
static int
devfs_read(struct vop_read_args *ap)
{
devnode_t * dn_p = VTODN(ap->a_vp);
switch (ap->a_vp->v_type) {
case VDIR: {
dn_p->dn_flags |= DN_ACCESS;
return VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred,
NULL, NULL, NULL);
}
default: {
printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
return(EINVAL);
break;
}
}
return (0);
}
static int
devfs_close(ap)
struct vop_close_args *ap;
{
struct vnode * vp = ap->a_vp;
register devnode_t * dnp = VTODN(vp);
simple_lock(&vp->v_interlock);
if (vp->v_usecount > 1)
dn_times(dnp, time, time);
simple_unlock(&vp->v_interlock);
return (0);
}
static int
devfsspec_close(ap)
struct vop_close_args *ap;
{
struct vnode * vp = ap->a_vp;
register devnode_t * dnp = VTODN(vp);
simple_lock(&vp->v_interlock);
if (vp->v_usecount > 1)
dn_times(dnp, time, time);
simple_unlock(&vp->v_interlock);
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
}
static int
devfsspec_read(struct vop_read_args *ap)
{
VTODN(ap->a_vp)->dn_flags |= DN_ACCESS;
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
}
static int
devfsspec_write(struct vop_write_args *ap)
{
VTODN(ap->a_vp)->dn_flags |= DN_CHANGE | DN_UPDATE;
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
}
static int
devfs_write(struct vop_write_args *ap)
{
switch (ap->a_vp->v_type) {
case VDIR:
return(EISDIR);
default:
printf("devfs_write(): bad file type %d", ap->a_vp->v_type);
return (EINVAL);
}
return 0;
}
static int
devfs_remove(struct vop_remove_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
devnode_t * tp;
devnode_t * tdp;
devdirent_t * tnp;
int doingdirectory = 0;
int error = 0;
uid_t ouruid = cnp->cn_cred->cr_uid;
struct proc *p = cnp->cn_proc;
tp = VTODN(vp);
tdp = VTODN(dvp);
tnp = tp->dn_last_lookup;
if ((tp->dn_flags & (IMMUTABLE | APPEND))
|| (tdp->dn_flags & APPEND) ) {
error = EPERM;
goto abort;
}
if ((tp->dn_type) == DEV_DIR) {
if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
|| (cnp->cn_flags&ISDOTDOT) ) {
error = EINVAL;
goto abort;
}
doingdirectory++;
}
tdp->dn_flags |= DN_CHANGE | DN_UPDATE;
if ((tdp->dn_mode & S_ISTXT)
&& ouruid != 0
&& ouruid != tdp->dn_uid
&& ouruid != tp->dn_uid ) {
error = EPERM;
goto abort;
}
if (( doingdirectory) && (tp->dn_links > 2)) {
error = ENOTEMPTY;
goto abort;
}
DEVFS_LOCK(p);
dev_free_name(tnp);
DEVFS_UNLOCK(p);
abort:
if (dvp == vp)
vrele(vp);
else
vput(vp);
vput(dvp);
return (error);
}
static int
devfs_link(struct vop_link_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode *tdvp = ap->a_tdvp;
struct componentname *cnp = ap->a_cnp;
struct proc *p = cnp->cn_proc;
devnode_t * fp;
devnode_t * tdp;
devdirent_t * tnp;
int error = 0;
struct timeval tv;
if (cnp->cn_namelen > DEVMAXNAMESIZE) {
error = ENAMETOOLONG;
goto out1;
}
tdp = VTODN(tdvp);
fp = VTODN(vp);
if (tdvp->v_mount != vp->v_mount) {
error = EXDEV;
VOP_ABORTOP(tdvp, cnp);
goto out2;
}
if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
VOP_ABORTOP(tdvp, cnp);
goto out2;
}
if (fp->dn_flags & (IMMUTABLE | APPEND)) {
VOP_ABORTOP(tdvp, cnp);
error = EPERM;
goto out1;
}
fp->dn_flags |= DN_CHANGE;
tv = time;
error = VOP_UPDATE(vp, &tv, &tv, 1);
if (!error) {
DEVFS_LOCK(p);
error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
DEVFS_UNLOCK(p);
}
out1:
if (tdvp != vp)
VOP_UNLOCK(vp, 0, p);
out2:
vput(tdvp);
return (error);
}
int
devfs_checkpath(source, target)
devnode_t *source, *target;
{
int error = 0;
devnode_t * ntmp;
devnode_t * tmp;
struct vnode *vp;
vp = target->dn_vn;
tmp = target;
do {
if (tmp == source) {
error = EINVAL;
break;
}
ntmp = tmp;
} while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
if (vp != NULL)
vput(vp);
return (error);
}
static int
devfs_rename(struct vop_rename_args *ap)
{
struct vnode *tvp = ap->a_tvp;
struct vnode *tdvp = ap->a_tdvp;
struct vnode *fvp = ap->a_fvp;
struct vnode *fdvp = ap->a_fdvp;
struct componentname *tcnp = ap->a_tcnp;
struct componentname *fcnp = ap->a_fcnp;
struct proc *p = fcnp->cn_proc;
devnode_t *fp, *fdp, *tp, *tdp;
devdirent_t *fnp,*tnp;
int doingdirectory = 0;
int error = 0;
struct timeval tv;
if(tcnp->cn_namelen > DEVMAXNAMESIZE) {
error = ENAMETOOLONG;
goto abortit;
}
tdp = VTODN(tdvp);
fdp = VTODN(fdvp);
fp = VTODN(fvp);
fnp = fp->dn_last_lookup;
tp = NULL;
tnp = NULL;
if (tvp) {
tp = VTODN(tvp);
tnp = tp->dn_last_lookup;
}
if ((fvp->v_mount != tdvp->v_mount) ||
(tvp && (fvp->v_mount != tvp->v_mount))) {
error = EXDEV;
abortit:
VOP_ABORTOP(tdvp, tcnp);
if (tdvp == tvp)
vrele(tdvp);
else
vput(tdvp);
if (tvp)
vput(tvp);
VOP_ABORTOP(fdvp, fcnp);
vrele(fdvp);
vrele(fvp);
return (error);
}
if ((tp && (tp->dn_flags & (IMMUTABLE | APPEND)))
|| (fp->dn_flags & (IMMUTABLE | APPEND))
|| (fdp->dn_flags & APPEND)) {
error = EPERM;
goto abortit;
}
if ((fp->dn_type) == DEV_DIR) {
if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')
|| (fcnp->cn_flags&ISDOTDOT)
|| (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.')
|| (tcnp->cn_flags&ISDOTDOT)
|| (tdp == fp )) {
error = EINVAL;
goto abortit;
}
doingdirectory++;
}
if (doingdirectory && (tdp != fdp)) {
devnode_t * tmp, *ntmp;
error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
tmp = tdp;
do {
if(tmp == fp) {
error = EINVAL;
goto out;
}
ntmp = tmp;
} while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
}
fp->dn_flags |= DN_CHANGE;
tv = time;
if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) {
VOP_UNLOCK(fvp, 0, p);
goto bad;
}
if (fvp == tvp) {
if (fvp->v_type == VDIR) {
error = EINVAL;
goto abortit;
}
VOP_ABORTOP(tdvp, tcnp);
vput(tdvp);
vput(tvp);
VOP_ABORTOP(fdvp, fcnp);
vrele(fdvp);
vrele(fvp);
dev_free_name(fnp);
return 0;
}
vrele(fdvp);
fp->dn_links++;
if (tp) {
int ouruid = tcnp->cn_cred->cr_uid;
if ((tdp->dn_mode & S_ISTXT)
&& ouruid != 0
&& ouruid != tdp->dn_uid
&& ouruid != tp->dn_uid ) {
error = EPERM;
goto bad;
}
if (( doingdirectory) && (tp->dn_links > 2)) {
error = ENOTEMPTY;
goto bad;
}
dev_free_name(tnp);
tp = NULL;
}
dev_add_name(tcnp->cn_nameptr,tdp,NULL,fp,&tnp);
fnp->de_dnp = NULL;
fp->dn_links--;
dev_free_name(fnp);
fp->dn_links--;
if (tdp)
vput(tdvp);
if (tp)
vput(fvp);
vrele(fvp);
return (error);
bad:
if (tp)
vput(tvp);
vput(tdvp);
out:
if (vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
fp->dn_links--;
vput(fvp);
} else
vrele(fvp);
return (error);
}
static int
devfs_symlink(struct vop_symlink_args *ap)
{
struct componentname * cnp = ap->a_cnp;
struct vnode *vp = NULL;
int error = 0;
devnode_t * dir_p;
devnode_type_t typeinfo;
devdirent_t * nm_p;
devnode_t * dev_p;
struct vattr * vap = ap->a_vap;
struct vnode * * vpp = ap->a_vpp;
struct proc *p = cnp->cn_proc;
struct timeval tv;
dir_p = VTODN(ap->a_dvp);
typeinfo.Slnk.name = ap->a_target;
typeinfo.Slnk.namelen = strlen(ap->a_target);
DEVFS_LOCK(p);
error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK,
&typeinfo, NULL, NULL, &nm_p);
DEVFS_UNLOCK(p);
if (error) {
goto failure;
}
dev_p = nm_p->de_dnp;
dev_p->dn_uid = dir_p->dn_uid;
dev_p->dn_gid = dir_p->dn_gid;
dev_p->dn_mode = vap->va_mode;
dn_copy_times(dev_p, dir_p);
error = devfs_dntovn(dev_p, vpp, p);
if (error)
goto failure;
vp = *vpp;
vput(vp);
failure:
if ((cnp->cn_flags & SAVESTART) == 0)
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
vput(ap->a_dvp);
return error;
}
int
devfs_mknod(ap)
struct vop_mknod_args *ap;
{
struct componentname * cnp = ap->a_cnp;
devnode_t * dev_p;
devdirent_t * devent;
devnode_t * dir_p;
struct vnode * dvp = ap->a_dvp;
int error = 0;
devnode_type_t typeinfo;
struct vattr * vap = ap->a_vap;
struct vnode ** vpp = ap->a_vpp;
struct proc * p = cnp->cn_proc;
*vpp = NULL;
if (!vap->va_type == VBLK && !vap->va_type == VCHR) {
error = EINVAL;
goto failure;
}
dir_p = VTODN(dvp);
typeinfo.dev = vap->va_rdev;
DEVFS_LOCK(p);
error = dev_add_entry(cnp->cn_nameptr, dir_p,
(vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
&typeinfo, NULL, NULL, &devent);
DEVFS_UNLOCK(p);
if (error) {
goto failure;
}
dev_p = devent->de_dnp;
error = devfs_dntovn(dev_p, vpp, p);
if (error)
goto failure;
dev_p->dn_uid = cnp->cn_cred->cr_uid;
dev_p->dn_gid = dir_p->dn_gid;
dev_p->dn_mode = vap->va_mode;
failure:
if (*vpp) {
vput(*vpp);
*vpp = 0;
}
if ((cnp->cn_flags & SAVESTART) == 0)
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
vput(dvp);
return (error);
}
static int
devfs_readdir(struct vop_readdir_args *ap)
{
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct dirent dirent;
devnode_t * dir_node;
devdirent_t * name_node;
char *name;
int error = 0;
int reclen;
int nodenumber;
int startpos,pos;
struct proc * p = uio->uio_procp;
dir_node = VTODN(vp);
if(dir_node->dn_type != DEV_DIR)
return(ENOTDIR);
pos = 0;
startpos = uio->uio_offset;
DEVFS_LOCK(p);
name_node = dir_node->dn_typeinfo.Dir.dirlist;
nodenumber = 0;
dir_node->dn_flags |= DN_ACCESS;
while ((name_node || (nodenumber < 2)) && (uio->uio_resid > 0))
{
switch(nodenumber)
{
case 0:
dirent.d_fileno = (int32_t)(void *)dir_node;
name = ".";
dirent.d_namlen = 1;
dirent.d_type = DT_DIR;
break;
case 1:
if(dir_node->dn_typeinfo.Dir.parent)
dirent.d_fileno
= (int32_t)dir_node->dn_typeinfo.Dir.parent;
else
dirent.d_fileno = (u_int32_t)dir_node;
name = "..";
dirent.d_namlen = 2;
dirent.d_type = DT_DIR;
break;
default:
dirent.d_fileno = (int32_t)(void *)name_node->de_dnp;
dirent.d_namlen = strlen(name_node->de_name);
name = name_node->de_name;
switch(name_node->de_dnp->dn_type) {
case DEV_BDEV:
dirent.d_type = DT_BLK;
break;
case DEV_CDEV:
dirent.d_type = DT_CHR;
break;
case DEV_DIR:
dirent.d_type = DT_DIR;
break;
case DEV_SLNK:
dirent.d_type = DT_LNK;
break;
default:
dirent.d_type = DT_UNKNOWN;
}
}
#define GENERIC_DIRSIZ(dp) \
((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
if(pos >= startpos)
{
if (uio->uio_resid < reclen)
break;
strcpy( dirent.d_name,name);
if ((error = uiomove ((caddr_t)&dirent,
dirent.d_reclen, uio)) != 0)
break;
}
pos += reclen;
if((nodenumber >1) && name_node)
name_node = name_node->de_next;
nodenumber++;
}
DEVFS_UNLOCK(p);
uio->uio_offset = pos;
return (error);
}
static int
devfs_readlink(struct vop_readlink_args *ap)
{
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
devnode_t * lnk_node;
int error = 0;
lnk_node = VTODN(vp);
if(lnk_node->dn_type != DEV_SLNK)
return(EINVAL);
if ((error = VOP_ACCESS(vp, VREAD, ap->a_cred, NULL)) != 0) {
return error;
}
error = uiomove(lnk_node->dn_typeinfo.Slnk.name,
lnk_node->dn_typeinfo.Slnk.namelen, uio);
return error;
}
static int
devfs_abortop(struct vop_abortop_args *ap)
{
if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
}
return 0;
}
static int
devfs_reclaim(struct vop_reclaim_args *ap)
{
struct vnode * vp = ap->a_vp;
devnode_t * dnp = VTODN(vp);
if (dnp) {
dnp->dn_vn = NULL;
vp->v_data = NULL;
if (dnp->dn_delete) {
devnode_free(dnp);
}
}
return(0);
}
static int
devfs_print(struct vop_print_args *ap)
{
return (0);
}
static int
devfs_inactive(struct vop_inactive_args *ap)
{
struct vnode * vp = ap->a_vp;
devnode_t * dnp = VTODN(vp);
if (dnp) {
dnp->dn_vn = NULL;
vp->v_data = NULL;
if (dnp->dn_delete) {
devnode_free(dnp);
}
}
VOP_UNLOCK(vp, 0, ap->a_p);
return (0);
}
int
devfs_update(ap)
struct vop_update_args *ap;
{
register struct fs *fs;
int error;
devnode_t * ip;
ip = VTODN(ap->a_vp);
if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) {
ip->dn_flags &=
~(DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE);
return (0);
}
if ((ip->dn_flags &
(DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE)) == 0)
return (0);
dn_times(ip, time, time);
return (0);
}
#define VOPFUNC int (*)(void *)
int (**devfs_vnodeop_p)(void *);
static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
{ &vop_default_desc, (VOPFUNC)vn_default_error },
{ &vop_lookup_desc, (VOPFUNC)devfs_lookup },
{ &vop_create_desc, (VOPFUNC)err_create },
{ &vop_whiteout_desc, (VOPFUNC)err_whiteout },
{ &vop_mknod_desc, (VOPFUNC)devfs_mknod },
{ &vop_open_desc, (VOPFUNC)nop_open },
{ &vop_close_desc, (VOPFUNC)devfs_close },
{ &vop_access_desc, (VOPFUNC)devfs_access },
{ &vop_getattr_desc, (VOPFUNC)devfs_getattr },
{ &vop_setattr_desc, (VOPFUNC)devfs_setattr },
{ &vop_read_desc, (VOPFUNC)devfs_read },
{ &vop_write_desc, (VOPFUNC)devfs_write },
{ &vop_lease_desc, (VOPFUNC)nop_lease },
{ &vop_ioctl_desc, (VOPFUNC)err_ioctl },
{ &vop_select_desc, (VOPFUNC)err_select },
{ &vop_revoke_desc, (VOPFUNC)err_revoke },
{ &vop_mmap_desc, (VOPFUNC)err_mmap },
{ &vop_fsync_desc, (VOPFUNC)nop_fsync },
{ &vop_seek_desc, (VOPFUNC)err_seek },
{ &vop_remove_desc, (VOPFUNC)devfs_remove },
{ &vop_link_desc, (VOPFUNC)devfs_link },
{ &vop_rename_desc, (VOPFUNC)devfs_rename },
{ &vop_mkdir_desc, (VOPFUNC)err_mkdir },
{ &vop_rmdir_desc, (VOPFUNC)err_rmdir },
{ &vop_symlink_desc, (VOPFUNC)devfs_symlink },
{ &vop_readdir_desc, (VOPFUNC)devfs_readdir },
{ &vop_readlink_desc, (VOPFUNC)devfs_readlink },
{ &vop_abortop_desc, (VOPFUNC)devfs_abortop },
{ &vop_inactive_desc, (VOPFUNC)devfs_inactive },
{ &vop_reclaim_desc, (VOPFUNC)devfs_reclaim },
{ &vop_lock_desc, (VOPFUNC)nop_lock },
{ &vop_unlock_desc, (VOPFUNC)nop_unlock },
{ &vop_bmap_desc, (VOPFUNC)err_bmap },
{ &vop_strategy_desc, (VOPFUNC)err_strategy },
{ &vop_print_desc, (VOPFUNC)err_print },
{ &vop_islocked_desc, (VOPFUNC)nop_islocked },
{ &vop_pathconf_desc, (VOPFUNC)err_pathconf },
{ &vop_advlock_desc, (VOPFUNC)err_advlock },
{ &vop_blkatoff_desc, (VOPFUNC)err_blkatoff },
{ &vop_valloc_desc, (VOPFUNC)err_valloc },
{ &vop_reallocblks_desc, (VOPFUNC)err_reallocblks },
{ &vop_vfree_desc, (VOPFUNC)err_vfree },
{ &vop_truncate_desc, (VOPFUNC)err_truncate },
{ &vop_update_desc, (VOPFUNC)devfs_update },
{ &vop_bwrite_desc, (VOPFUNC)err_bwrite },
{ &vop_pagein_desc, (VOPFUNC)err_pagein },
{ &vop_pageout_desc, (VOPFUNC)err_pageout },
{ &vop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vop_blktooff_desc, (VOPFUNC)err_blktooff },
{ &vop_offtoblk_desc, (VOPFUNC)err_offtoblk },
{ &vop_cmap_desc, (VOPFUNC)err_cmap },
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc devfs_vnodeop_opv_desc =
{ &devfs_vnodeop_p, devfs_vnodeop_entries };
int (**devfs_spec_vnodeop_p)(void *);
static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
{ &vop_default_desc, (VOPFUNC)vn_default_error },
{ &vop_lookup_desc, (VOPFUNC)spec_lookup },
{ &vop_create_desc, (VOPFUNC)spec_create },
{ &vop_mknod_desc, (VOPFUNC)spec_mknod },
{ &vop_open_desc, (VOPFUNC)spec_open },
{ &vop_close_desc, (VOPFUNC)devfsspec_close },
{ &vop_access_desc, (VOPFUNC)devfs_access },
{ &vop_getattr_desc, (VOPFUNC)devfs_getattr },
{ &vop_setattr_desc, (VOPFUNC)devfs_setattr },
{ &vop_read_desc, (VOPFUNC)devfsspec_read },
{ &vop_write_desc, (VOPFUNC)devfsspec_write },
{ &vop_lease_desc, (VOPFUNC)spec_lease_check },
{ &vop_ioctl_desc, (VOPFUNC)spec_ioctl },
{ &vop_select_desc, (VOPFUNC)spec_select },
{ &vop_revoke_desc, (VOPFUNC)spec_revoke },
{ &vop_mmap_desc, (VOPFUNC)spec_mmap },
{ &vop_fsync_desc, (VOPFUNC)spec_fsync },
{ &vop_seek_desc, (VOPFUNC)spec_seek },
{ &vop_remove_desc, (VOPFUNC)devfs_remove },
{ &vop_link_desc, (VOPFUNC)devfs_link },
{ &vop_rename_desc, (VOPFUNC)spec_rename },
{ &vop_mkdir_desc, (VOPFUNC)spec_mkdir },
{ &vop_rmdir_desc, (VOPFUNC)spec_rmdir },
{ &vop_symlink_desc, (VOPFUNC)spec_symlink },
{ &vop_readdir_desc, (VOPFUNC)spec_readdir },
{ &vop_readlink_desc, (VOPFUNC)spec_readlink },
{ &vop_abortop_desc, (VOPFUNC)spec_abortop },
{ &vop_inactive_desc, (VOPFUNC)devfs_inactive },
{ &vop_reclaim_desc, (VOPFUNC)devfs_reclaim },
{ &vop_lock_desc, (VOPFUNC)nop_lock },
{ &vop_unlock_desc, (VOPFUNC)nop_unlock },
{ &vop_bmap_desc, (VOPFUNC)spec_bmap },
{ &vop_strategy_desc, (VOPFUNC)spec_strategy },
{ &vop_print_desc, (VOPFUNC)devfs_print },
{ &vop_islocked_desc, (VOPFUNC)nop_islocked },
{ &vop_pathconf_desc, (VOPFUNC)spec_pathconf },
{ &vop_advlock_desc, (VOPFUNC)spec_advlock },
{ &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff },
{ &vop_valloc_desc, (VOPFUNC)spec_valloc },
{ &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks },
{ &vop_vfree_desc, (VOPFUNC)nop_vfree },
{ &vop_truncate_desc, (VOPFUNC)spec_truncate },
{ &vop_update_desc, (VOPFUNC)devfs_update },
{ &vop_bwrite_desc, (VOPFUNC)vn_bwrite },
{ &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize },
{ &vop_pagein_desc, (VOPFUNC)err_pagein },
{ &vop_pageout_desc, (VOPFUNC)err_pageout },
{ &vop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vop_blktooff_desc, (VOPFUNC)spec_blktooff },
{ &vop_blktooff_desc, (VOPFUNC)spec_offtoblk },
{ &vop_cmap_desc, (VOPFUNC)spec_cmap },
{ (struct vnodeop_desc*)NULL, (int(*)())NULL }
};
struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
{ &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries };