477 lines
11 KiB
C
477 lines
11 KiB
C
/*
|
|
* Written by Julian Elischer (julian@DIALix.oz.au)
|
|
*
|
|
* $Header: /home/ncvs/src/sys/miscfs/devfs/devfs_front.c,v 1.3 1995/04/20 07:42:41 julian Exp $
|
|
*
|
|
*/
|
|
|
|
#include "param.h"
|
|
#include "systm.h"
|
|
#include "kernel.h"
|
|
#include "file.h" /* define FWRITE ... */
|
|
#include "conf.h"
|
|
#include "stat.h"
|
|
#include "mount.h"
|
|
#include "vnode.h"
|
|
#include "malloc.h"
|
|
#include "dir.h" /* defines dirent structure */
|
|
#include "devfsdefs.h"
|
|
|
|
|
|
|
|
/***********************************************************************\
|
|
* Given a directory backing node, and a child backing node, add the *
|
|
* appropriate front nodes to the front nodes of the directory to *
|
|
* represent the child node to the user *
|
|
* *
|
|
* on failure, front nodes will either be correct or not exist for each *
|
|
* front dir, however dirs completed will not be stripped of completed *
|
|
* frontnodes on failure of a later parent frontnode *
|
|
* *
|
|
\***********************************************************************/
|
|
int devfs_add_fronts(devnm_p parent,devnm_p child) /*proto*/
|
|
{
|
|
devnm_p newfp;
|
|
devnm_p falias;
|
|
|
|
DBPRINT((" devfs_add_fronts\n"));
|
|
/***********************************************\
|
|
* Find the frontnodes of the parent node *
|
|
\***********************************************/
|
|
for (falias = parent->next_front; falias; falias = falias->next_front)
|
|
{
|
|
if(dev_findfront(falias->dnp,child->name))
|
|
{
|
|
printf("Device %s not created, already exists\n",
|
|
child->name);
|
|
continue;
|
|
}
|
|
if( dev_mk_front(falias->dnp,child,&newfp,NULL))
|
|
{
|
|
printf("Device %s: allocation failed\n",
|
|
child->name);
|
|
continue;
|
|
}
|
|
|
|
}
|
|
return(0); /* for now always succeed */
|
|
}
|
|
|
|
/***************************************************************\
|
|
* Search down the linked list off a front dir to find "name" *
|
|
* return the dn_p for that node.
|
|
\***************************************************************/
|
|
dn_p dev_findfront(dn_p dir,char *name) /*proto*/
|
|
{
|
|
devnm_p newfp;
|
|
DBPRINT((" dev_findfront(%s)\n",name));
|
|
if(dir->type != DEV_DIR) return 0;/*XXX*/ /* printf?*/
|
|
|
|
if(name[0] == '.')
|
|
{
|
|
if(name[1] == 0)
|
|
{
|
|
return dir;
|
|
}
|
|
if((name[1] == '.') && (name[2] == 0))
|
|
{
|
|
if(dir->by.Dir.parent == dir) /* root? */
|
|
return dir;
|
|
else
|
|
return dir->by.Dir.parent;
|
|
}
|
|
}
|
|
newfp = dir->by.Dir.dirlist;
|
|
while(newfp)
|
|
{
|
|
if(!(strcmp(name,newfp->name)))
|
|
break;
|
|
newfp = newfp->next;
|
|
}
|
|
if(newfp)
|
|
return newfp->dnp;
|
|
else
|
|
return (dn_p)0;
|
|
}
|
|
|
|
/***************************************************************\
|
|
* Create and link in a new front element.. *
|
|
* Parent can be 0 for a root node *
|
|
* Not presently usable to make a symlink XXX *
|
|
* Must teach this to handle where there is no back node *
|
|
* maybe split into two bits? *
|
|
\***************************************************************/
|
|
int dev_mk_front(dn_p parent,devnm_p back,devnm_p *devnm_pp , struct devfsmount *dvm) /*proto*/
|
|
{
|
|
devnm_p newfp;
|
|
struct devfsmount *dmt;
|
|
devnm_p newback;
|
|
devnm_p newfront;
|
|
int error;
|
|
dn_p dnp;
|
|
|
|
DBPRINT((" dev_mk_front\n"));
|
|
if(parent && (parent->type != DEV_DIR)) return EINVAL;
|
|
/*XXX*/ /* printf?*/
|
|
if(!(newfp = malloc(sizeof(devnm_t),M_DEVFSFRONT,M_NOWAIT)))
|
|
{
|
|
return(ENOMEM);
|
|
}
|
|
bzero(newfp,sizeof(*newfp));
|
|
strcpy(newfp->name,back->name);
|
|
|
|
/*******************************************************\
|
|
* If we are creating an alias, Then we need to find the *
|
|
* real object's file_node. (It must pre-exist) *
|
|
* this means that aliases have no front nodes... *
|
|
* In effect ALIAS back nodes are just place markers *
|
|
\*******************************************************/
|
|
if(back->dnp->type == DEV_ALIAS)
|
|
{
|
|
back = back->dnp->by.Alias.realthing;
|
|
}
|
|
|
|
/*
|
|
* either use the existing devnode or make our own,
|
|
* depending on if we are a dev or a dir.
|
|
*/
|
|
switch(back->dnp->type) {
|
|
case DEV_BDEV:
|
|
case DEV_CDEV:
|
|
case DEV_DDEV:
|
|
dnp = newfp->dnp = back->dnp;
|
|
newfp->dnp->links++; /* wherever it is.....*/
|
|
break;
|
|
case DEV_DIR:
|
|
dnp = newfp->dnp = malloc(sizeof(devnode_t),
|
|
M_DEVFSNODE,M_NOWAIT);
|
|
if(!(dnp))
|
|
{
|
|
free(newfp,M_DEVFSFRONT);
|
|
return ENOMEM;
|
|
}
|
|
/*
|
|
* we have two options.. bcopy and reset some items,
|
|
* or bzero and reset or copy some items...
|
|
*/
|
|
bcopy(back->dnp,newfp->dnp,sizeof(devnode_t));
|
|
dnp->links = 1; /* EXTRA from '.' */
|
|
dnp->links++; /* wherever it is.....*/
|
|
dnp->by.Dir.dirlast =
|
|
&dnp->by.Dir.dirlist;
|
|
dnp->by.Dir.dirlist = NULL;
|
|
dnp->by.Dir.entrycount = 0;
|
|
dnp->vn = NULL;
|
|
dnp->vn_id = 0;
|
|
break;
|
|
case DEV_SLNK: /* should never happen XXX (hmm might)*/
|
|
default:
|
|
printf("unknown DEV type\n");
|
|
return EINVAL;
|
|
}
|
|
/*******************************************************\
|
|
* Put it in the parent's directory list (at the end). *
|
|
\*******************************************************/
|
|
if(parent)
|
|
{
|
|
newfp->next = *parent->by.Dir.dirlast;
|
|
newfp->prevp = parent->by.Dir.dirlast;
|
|
*parent->by.Dir.dirlast = newfp;
|
|
parent->by.Dir.dirlast = &newfp->next;
|
|
parent->by.Dir.entrycount++;
|
|
newfp->dnp->dvm = parent->dvm; /* XXX bad for devs */
|
|
if(back->dnp->type == DEV_DIR)
|
|
{
|
|
newfp->dnp->by.Dir.parent
|
|
= parent;
|
|
parent->links++; /* only dirs have '..'*/
|
|
}
|
|
parent->len += strlen(newfp->name) + 8;/*ok, ok?*/
|
|
} else {
|
|
/*
|
|
* it's the root node, put in the dvm
|
|
* and link it to itself...
|
|
* we know it's a DIR
|
|
*/
|
|
dnp->by.Dir.parent = newfp->dnp;
|
|
dnp->links++; /* extra for '..'*/
|
|
dnp->dvm = dvm;
|
|
}
|
|
|
|
/*
|
|
* not accounted for in the link counts..
|
|
* only used to get from the front name entries
|
|
* to the total length of the names
|
|
* which is stored in the parent's devnode
|
|
*/
|
|
newfp->parent = parent; /* is NULL for root */
|
|
/*******************************************************\
|
|
* Put it in the appropriate back/front list too. *
|
|
\*******************************************************/
|
|
newfp->next_front = *back->prev_frontp;
|
|
newfp->prev_frontp = back->prev_frontp;
|
|
*back->prev_frontp = newfp;
|
|
back->prev_frontp = &(newfp->next_front);
|
|
back->frontcount++;
|
|
newfp->as.front.realthing = back;
|
|
|
|
/*
|
|
* If it is a directory, then recurse down all the other
|
|
* subnodes in it....
|
|
*/
|
|
if ( newfp->dnp->type == DEV_DIR)
|
|
{
|
|
for(newback = back->dnp->by.Dir.dirlist;
|
|
newback; newback = newback->next)
|
|
{
|
|
if(error = dev_mk_front(newfp->dnp,
|
|
newback, &newfront, NULL))
|
|
{
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
*devnm_pp = newfp;
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* duplicate the backing tree into a tree of nodes hung off the
|
|
* mount point given as the argument. Do this by
|
|
* calling dev_mk_front() which recurses all the way
|
|
* up the tree..
|
|
*/
|
|
int devfs_make_plane(struct devfsmount *devfs_mp_p) /*proto*/
|
|
{
|
|
devnm_p parent;
|
|
devnm_p new;
|
|
devnm_p realthing;
|
|
int error;
|
|
|
|
DBPRINT((" devfs_make_plane\n"));
|
|
realthing = dev_root;
|
|
if(error = dev_mk_front(0, realthing,&new, devfs_mp_p))
|
|
{
|
|
return error;
|
|
}
|
|
devfs_mp_p->plane_root = new;
|
|
|
|
return error;
|
|
}
|
|
|
|
void devfs_free_plane(struct devfsmount *devfs_mp_p) /*proto*/
|
|
{
|
|
devnm_p devfp;
|
|
|
|
DBPRINT((" devfs_free_plane\n"));
|
|
devfp = devfs_mp_p->plane_root;
|
|
if(devfp) dev_free_front(devfp);
|
|
}
|
|
|
|
/*
|
|
* Remove all the front nodes associated with a backing node
|
|
*/
|
|
void devfs_remove_fronts(devnm_p devbp) /*proto*/
|
|
{
|
|
while(devbp->next_front)
|
|
{
|
|
dev_free_front(devbp->next_front);
|
|
}
|
|
}
|
|
/***************************************************************\
|
|
* Free a front node (and any below it of it's a directory node) *
|
|
\***************************************************************/
|
|
void dev_free_front(devnm_p devfp) /*proto*/
|
|
{
|
|
dn_p parent = devfp->parent;
|
|
devnm_p back;
|
|
|
|
DBPRINT((" dev_free_front\n"));
|
|
if(devfp->dnp->type == DEV_DIR)
|
|
{
|
|
while(devfp->dnp->by.Dir.dirlist)
|
|
{
|
|
dev_free_front(devfp->dnp->by.Dir.dirlist);
|
|
}
|
|
/*
|
|
* drop the reference counts on our and our parent's
|
|
* nodes for "." and ".." (root has ".." -> "." )
|
|
*/
|
|
devfs_dn_free(devfp->dnp); /* account for '.' */
|
|
devfs_dn_free(devfp->dnp->by.Dir.parent); /* and '..' */
|
|
}
|
|
/*
|
|
* unlink ourselves from the directory on this plane
|
|
*/
|
|
if(parent) /* if not fs root */
|
|
{
|
|
if( *devfp->prevp = devfp->next)/* yes, assign */
|
|
{
|
|
devfp->next->prevp = devfp->prevp;
|
|
}
|
|
else
|
|
{
|
|
parent->by.Dir.dirlast
|
|
= devfp->prevp;
|
|
}
|
|
parent->by.Dir.entrycount--;
|
|
parent->len -= strlen(devfp->name);
|
|
}
|
|
/*
|
|
* If the node has a backing pointer we need to free ourselves
|
|
* from that..
|
|
* Remember that we may not HAVE a backing node.
|
|
*/
|
|
if (back = devfp->as.front.realthing) /* yes an assign */
|
|
{
|
|
if( *devfp->prev_frontp = devfp->next_front)/* yes, assign */
|
|
{
|
|
devfp->next_front->prev_frontp = devfp->prev_frontp;
|
|
}
|
|
else
|
|
{
|
|
back->prev_frontp = devfp->prev_frontp;
|
|
}
|
|
back->frontcount--;
|
|
}
|
|
/***************************************************************\
|
|
* If the front node has it's own devnode structure, *
|
|
* then free it. *
|
|
\***************************************************************/
|
|
devfs_dn_free(devfp->dnp);
|
|
free(devfp,M_DEVFSFRONT);
|
|
return;
|
|
}
|
|
|
|
/*******************************************************\
|
|
* Theoretically this could be called for any kind of *
|
|
* vnode, however in practice it must be a DEVFS vnode *
|
|
\*******************************************************/
|
|
int devfs_vntodn(struct vnode *vn_p, dn_p *dn_pp) /*proto*/
|
|
{
|
|
|
|
DBPRINT((" vntodn "));
|
|
if(vn_p->v_tag != VT_DEVFS)
|
|
{
|
|
printf("bad-tag ");
|
|
Debugger("bad-tag ");
|
|
return(EINVAL);
|
|
}
|
|
if(vn_p->v_usecount == 0)
|
|
{
|
|
printf("not locked! ");
|
|
}
|
|
if((vn_p->v_type == VBAD) || (vn_p->v_type == VNON))
|
|
{
|
|
printf("bad-type ");
|
|
return(EINVAL);
|
|
}
|
|
*dn_pp = (dn_p)vn_p->v_data;
|
|
|
|
return(0);
|
|
}
|
|
|
|
/***************************************************************\
|
|
* given a dev_node, find the appropriate vnode if one is already*
|
|
* associated, or get a new one an associate it with the dev_node*
|
|
* need to check about vnode references.. should we increment it?*
|
|
\***************************************************************/
|
|
int devfs_dntovn(dn_p dnp, struct vnode **vn_pp) /*proto*/
|
|
{
|
|
struct vnode *vn_p, *nvp;
|
|
int error = 0;
|
|
|
|
vn_p = dnp->vn;
|
|
DBPRINT(("dntovn "));
|
|
if( vn_p)
|
|
{
|
|
if(vn_p->v_id != dnp->vn_id)
|
|
{
|
|
printf("bad-id ");
|
|
goto skip;
|
|
}
|
|
if(vn_p->v_tag != VT_DEVFS)
|
|
{
|
|
printf("bad-tag ");
|
|
goto skip;
|
|
}
|
|
if(vn_p->v_op != *(dnp->ops))
|
|
{
|
|
printf("bad-ops ");
|
|
goto skip;
|
|
}
|
|
if((dn_p)(vn_p->v_data) != dnp)
|
|
{
|
|
printf("bad-rev_link ");
|
|
goto skip;
|
|
}
|
|
if(vn_p->v_type != VNON)
|
|
{
|
|
vget(vn_p,0/*lockflag ?*/); /*XXX*/
|
|
*vn_pp = vn_p;
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
printf("bad-type");
|
|
}
|
|
skip:
|
|
vn_p = (struct vnode *) 0;
|
|
}
|
|
if(!(error = getnewvnode(VT_DEVFS,
|
|
dnp->dvm->mount,
|
|
*(dnp->ops),
|
|
&vn_p)))
|
|
{
|
|
dnp->vn = vn_p;
|
|
dnp->vn_id = vn_p->v_id;
|
|
*vn_pp = vn_p;
|
|
DBPRINT(("(New vnode)"));
|
|
switch(dnp->type)
|
|
{
|
|
case DEV_SLNK:
|
|
break;
|
|
case DEV_DIR:
|
|
if(dnp->by.Dir.parent == dnp)
|
|
{
|
|
vn_p->v_flag |= VROOT;
|
|
}
|
|
vn_p->v_type = VDIR;
|
|
break;
|
|
case DEV_BDEV:
|
|
vn_p->v_type = VBLK;
|
|
if (nvp = checkalias(vn_p,
|
|
dnp->by.Bdev.dev,
|
|
(struct mount *)0))
|
|
{
|
|
vput(vn_p);
|
|
vn_p = nvp;
|
|
}
|
|
break;
|
|
case DEV_CDEV:
|
|
vn_p->v_type = VCHR;
|
|
if (nvp = checkalias(vn_p,
|
|
dnp->by.Cdev.dev,
|
|
(struct mount *)0))
|
|
{
|
|
vput(vn_p);
|
|
vn_p = nvp;
|
|
}
|
|
break;
|
|
case DEV_DDEV:
|
|
break;
|
|
}
|
|
if ( vn_p)
|
|
{
|
|
vn_p->v_mount = dnp->dvm->mount;/* XXX Duplicated */
|
|
*vn_pp = vn_p;
|
|
vn_p->v_data = (void *)dnp;
|
|
}
|
|
else
|
|
{
|
|
error = EINVAL;
|
|
}
|
|
}
|
|
return error;
|
|
}
|