HardenedBSD/sys/miscfs/devfs/devfs_back.c
Julian Elischer e475eb56ca remove about 50 lines of duplicate code..
the code to create the root directory now calls the generic
make directory routine..
1995-09-03 08:39:26 +00:00

468 lines
12 KiB
C

/*
* Written by Julian Elischer (julian@DIALix.oz.au)
*
* $Header: /home/ncvs/src/sys/miscfs/devfs/devfs_back.c,v 1.4 1995/09/03 05:43:38 julian Exp $
*/
#include "param.h"
#include "systm.h"
#include "types.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"
#include "sys/devfsext.h"
SYSINIT(devfs, SI_SUB_DEVFS, SI_ORDER_FIRST, devfs_sinit, NULL)
devnm_p dev_root; /* root of the backing tree */
/*
* Set up the root directory node in the backing plane
* This is happenning before the vfs system has been
* set up yet, so be careful about what we reference..
* Notice that the ops are by indirection.. as they haven't
* been set up yet!
*/
void devfs_sinit() /*proto*/
{
int retval; /* we will discard this */
/*
* call the right routine at the right time with the right args....
*/
retval = dev_add_node("root",NULL,DEV_DIR,NULL,&dev_root);
printf("DEVFS: ready for devices\n");
}
/***********************************************************************\
* Given a starting node (0 for root) and a pathname, return the node *
* for the end item on the path. It MUST BE A DIRECTORY. If the 'CREATE' *
* option is true, then create any missing nodes in the path and create *
* and return the final node as well. *
* Generally, this MUST be the first function called by any module *
* as it also calls the initial setup code, in case it has never been *
* done yet. *
* This is used to set up a directory, before making nodes in it.. *
* *
* Warning: This function is RECURSIVE. *
* char *path, find this dir (err if not dir) *
* dn_p dirnode, starting point (0 = root) *
* int create, create path if not found *
* dn_p *dn_pp) where to return the node of the dir *
\***********************************************************************/
int dev_finddir(char *orig_path, dn_p dirnode, int create, dn_p *dn_pp) /*proto*/
{
devnm_p devnmp;
char pathbuf[DEVMAXPATHSIZE];
char *path;
char *name;
register char *cp;
int retval;
DBPRINT(("dev_finddir\n"));
if(!dirnode) dirnode = dev_root->dnp;
if(dirnode->type != DEV_DIR) return ENOTDIR;
if(strlen(orig_path) > (DEVMAXPATHSIZE - 1)) return ENAMETOOLONG;
path = pathbuf;
strcpy(path,orig_path);
while(*path == '/') path++; /* always absolute, skip leading / */
/***************************************\
* find the next segment of the name *
\***************************************/
cp = name = path;
while((*cp != '/') && (*cp != 0))
{
cp++;
}
/***********************************************\
* Check to see if it's the last component *
\***********************************************/
if(*cp)
{
path = cp + 1; /* path refers to the rest */
*cp = 0; /* name is now a separate string */
if(!(*path))
{
path = (char *)0; /* was trailing slash */
}
}
else
{
path = (char *)0; /* no more to do */
}
/***************************************\
* Start scanning along the linked list *
\***************************************/
devnmp = dirnode->by.Dir.dirlist;
while(devnmp && strcmp(devnmp->name,name))
{
devnmp = devnmp->next;
}
if(devnmp)
{ /* check it's a directory */
if(devnmp->dnp->type != DEV_DIR) return ENOTDIR;
}
else
{
/***************************************\
* The required element does not exist *
* So we will add it if asked to. *
\***************************************/
if(!create) return ENOENT;
if(retval = dev_add_node(name, dirnode ,DEV_DIR,
NULL, &devnmp))
{
return retval;
}
}
if(path) /* decide whether to recurse more or return */
{
return (dev_finddir(path,devnmp->dnp,create,dn_pp));
}
else
{
*dn_pp = devnmp->dnp;
return 0;
}
}
/***********************************************************************\
* Add a new element to the devfs backing structure. *
* If we're creating a root node, then dirname is NULL *
\***********************************************************************/
int dev_add_node(char *name, dn_p dirnode, int entrytype, union typeinfo *by, devnm_p *devnm_pp) /*proto*/
{
devnm_p devnmp;
devnm_p realthing; /* needed to create an alias */
dn_p dnp;
int retval;
DBPRINT(("dev_add_node\n"));
if(dirnode ) {
if(dirnode->type != DEV_DIR) return(ENOTDIR);
retval = dev_finddir(name,dirnode,0,&dnp); /*don't create!*/
dnp = NULL; /*just want the return code..*/
if(retval != ENOENT) /* only acceptable answer */
return(EEXIST);
}
/*
* make sure the name is legal
*/
if(strlen(name) > (DEVMAXNAMESIZE - 1)) return (ENAMETOOLONG);
/*
* Allocate and fill out a new backing node
*/
if(!(devnmp = (devnm_p)malloc(sizeof(devnm_t),
M_DEVFSBACK, M_NOWAIT)))
{
return ENOMEM;
}
bzero(devnmp,sizeof(devnm_t));
if(!(dnp = (dn_p)malloc(sizeof(devnode_t),
M_DEVFSNODE, M_NOWAIT)))
{
free(devnmp,M_DEVFSBACK);
return ENOMEM;
}
bzero(dnp,sizeof(devnode_t));
/*
* Link hte two together
* include the implicit link in the count of links to the devnode..
* this stops it from being accidentally freed later.
*/
devnmp->dnp = dnp;
dnp->links = 1; /* implicit from our own name-node */
/*
* note the node type we are adding
* and set the creation times to NOW
* put in it's name
*/
strcpy(devnmp->name,name);
dnp->type = entrytype;
TIMEVAL_TO_TIMESPEC(&time,&(dnp->ctime))
dnp->mtime = dnp->ctime;
dnp->atime = dnp->ctime;
/*
* And set up a new 'clones' list (empty)
*/
devnmp->prev_frontp = &(devnmp->next_front);
/*
* Check if we are making a root node..
* (with no parent)
*/
if(dirnode) {
/*
* Put it on the END of the linked list of directory entries
*/
devnmp->parent = dirnode;
devnmp->prevp = dirnode->by.Dir.dirlast;
devnmp->next = *(devnmp->prevp); /* should be NULL */ /*right?*/
*(devnmp->prevp) = devnmp;
dirnode->by.Dir.dirlast = &(devnmp->next);
dirnode->by.Dir.entrycount++;
}
/*
* return the answer
*/
switch(entrytype) {
case DEV_DIR:
/*
* As it's a directory, make sure it has a null entries list
*/
dnp->by.Dir.dirlast =
&(dnp->by.Dir.dirlist);
dnp->by.Dir.dirlist = (devnm_p)0;
if ( dirnode ) {
dnp->by.Dir.parent = (dn_p)dirnode;
} else {
/* root loops to self */
dnp->by.Dir.parent = dnp;
}
dnp->by.Dir.parent->links++; /* account for .. */
dnp->links++; /* for .*/
dnp->by.Dir.myname = devnmp;
/*
* make sure that the ops associated with it are the ops
* that we use (by default) for directories
*/
dnp->ops = &devfs_vnodeop_p;
dnp->mode |= 0555; /* default perms */
break;
case DEV_BDEV:
/*
* Make sure it has DEVICE type ops
* and device specific fields are correct
*/
dnp->ops = &dev_spec_vnodeop_p;
dnp->by.Bdev.bdevsw = by->Bdev.bdevsw;
dnp->by.Bdev.dev = by->Bdev.dev;
break;
case DEV_CDEV:
/*
* Make sure it has DEVICE type ops
* and device specific fields are correct
*/
dnp->ops = &dev_spec_vnodeop_p;
dnp->by.Cdev.cdevsw = by->Cdev.cdevsw;
dnp->by.Cdev.dev = by->Cdev.dev;
break;
case DEV_DDEV:
/*
* store the address of (the address of) the ops
* and the magic cookie to use with them
*/
dnp->by.Ddev.arg = by->Ddev.arg;
dnp->ops = by->Ddev.ops;
break;
case DEV_ALIAS:
/*
* point to the node we want to shadow
* Also store the fact we exist so that aliases
* can be deleted accuratly when the original node
* is deleted.. (i.e. when device is removed)
*/
realthing = by->Alias.realthing;
dnp->by.Alias.realthing = realthing;
dnp->by.Alias.next = realthing->as.back.aliases;
realthing->as.back.aliases = devnmp;
realthing->as.back.alias_count++;
break;
}
/*
* If we have a parent, then maybe we should duplicate
* ourselves onto any plane that the parent is on...
* Though this may be better handled elsewhere as
* it stops this routine from being used for front nodes
*/
if(dirnode) {
if(retval = devfs_add_fronts(dirnode->by.Dir.myname,devnmp))
{
/*XXX*//* no idea what to do if it fails... */
return retval;
}
}
*devnm_pp = devnmp;
return 0 ;
}
/***********************************************************************
* remove all fronts to this dev and also it's aliases,
* Then remove this node.
* For now only allow DEVICE nodes to go.. XXX
* directory nodes are more complicated and may need more work..
*/
int dev_remove(devnm_p devnmp) /*proto*/
{
devnm_p alias;
DBPRINT(("dev_remove\n"));
/*
* Check the type of the node.. for now don't allow dirs
*/
switch(devnmp->dnp->type)
{
case DEV_BDEV:
case DEV_CDEV:
case DEV_DDEV:
case DEV_ALIAS:
case DEV_SLNK:
break;
case DEV_DIR:
default:
return(EINVAL);
}
/*
* Free each alias
*/
while ( devnmp->as.back.alias_count)
{
alias = devnmp->as.back.aliases;
devnmp->as.back.aliases = alias->dnp->by.Alias.next;
devnmp->as.back.alias_count--;
devfs_dn_free(alias->dnp);
free (alias, M_DEVFSBACK);
}
/*
* Now remove front items of the Main node itself
*/
devfs_remove_fronts(devnmp);
/*
* now we should free the main node
*/
devfs_dn_free(devnmp->dnp);
free (devnmp, M_DEVFSBACK);
return 0;
}
int dev_touch(devnm_p key) /* update the node for this dev */ /*proto*/
{
DBPRINT(("dev_touch\n"));
TIMEVAL_TO_TIMESPEC(&time,&(key->dnp->mtime))
return 0; /*XXX*/
}
void devfs_dn_free(dn_p dnp) /*proto*/
{
if(dnp->links <= 0)
{
printf("devfs node reference count bogus\n");
Debugger("devfs_dn_free");
return;
}
if(--dnp->links == 0 )
{
devfs_dropvnode(dnp);
free (dnp, M_DEVFSNODE);
}
}
/***********************************************************************\
* UTILITY routine: *
* Return the major number for the cdevsw entry containing the given *
* address. *
\***********************************************************************/
int get_cdev_major_num(caddr_t addr) /*proto*/
{
int index = 0;
DBPRINT(("get_cdev_major_num\n"));
while (index < nchrdev)
{
if(((caddr_t)(cdevsw[index].d_open) == addr)
||((caddr_t)(cdevsw[index].d_read) == addr)
||((caddr_t)(cdevsw[index].d_ioctl) == addr))
{
return index;
}
index++;
}
return -1;
}
int get_bdev_major_num(caddr_t addr) /*proto*/
{
int index = 0;
DBPRINT(("get_bdev_major_num\n"));
while (index < nblkdev)
{
if(((caddr_t)(bdevsw[index].d_open) == addr)
||((caddr_t)(bdevsw[index].d_strategy) == addr)
||((caddr_t)(bdevsw[index].d_ioctl) == addr))
{
return index;
}
index++;
}
return -1;
}
/***********************************************************************\
* Add the named device entry into the given directory, and make it *
* The appropriate type... (called (sometimes indirectly) by drivers..) *
\***********************************************************************/
void *dev_add(char *path,
char *name,
void *funct,
int minor,
int chrblk,
uid_t uid,
gid_t gid,
int perms)
{
devnm_p new_dev;
dn_p dnp; /* devnode for parent directory */
int retval;
int major ;
union typeinfo by;
DBPRINT(("dev_add\n"));
retval = dev_finddir(path,NULL,1,&dnp);
if (retval) return 0;
switch(chrblk)
{
case DV_CHR:
major = get_cdev_major_num(funct);
by.Cdev.cdevsw = cdevsw + major;
by.Cdev.dev = makedev(major, minor);
if( dev_add_node(name, dnp, DEV_CDEV,
&by,&new_dev))
return 0;
break;
case DV_BLK:
major = get_bdev_major_num(funct);
by.Bdev.bdevsw = bdevsw + major;
by.Bdev.dev = makedev(major, minor);
if( dev_add_node(name, dnp, DEV_BDEV,
&by, &new_dev))
return 0;
break;
default:
return(0);
}
new_dev->dnp->gid = gid;
new_dev->dnp->uid = uid;
new_dev->dnp->mode |= perms;
return new_dev;
}