diff options
Diffstat (limited to 'sys/miscfs/devfs/devfs_back.c')
-rw-r--r-- | sys/miscfs/devfs/devfs_back.c | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/sys/miscfs/devfs/devfs_back.c b/sys/miscfs/devfs/devfs_back.c new file mode 100644 index 0000000..718f740 --- /dev/null +++ b/sys/miscfs/devfs/devfs_back.c @@ -0,0 +1,500 @@ + +/* + * Written by Julian Elischer (julian@DIALix.oz.au) + * + * $Header: /sys/miscfs/devfs/RCS/devfs_back.c,v 1.3 1995/01/07 04:20:25 root Exp root $ + */ + +#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" + + + +devb_p dev_root; /* root of the backing tree */ +int devfs_set_up = 0; /* note tha we HAVE set up 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_back_init() /*proto*/ +{ + + devb_p devbp; + dn_p dnp; + /* + * This may be called several times.. only do it if it needs + * to be done. + */ + if(!devfs_set_up) + { + /* + * Allocate and fill out a new backing node + */ + if(!(devbp = (devb_p)malloc(sizeof(devb_t), + M_DEVFSBACK, M_NOWAIT))) + { + return ; + } + bzero(devbp,sizeof(devb_t)); + /* + * And the devnode associated with it + */ + if(!(dnp = (dn_p)malloc(sizeof(devnode_t), + M_DEVFSNODE, M_NOWAIT))) + { + free(devbp,M_DEVFSBACK); + return ; + } + bzero(dnp,sizeof(devnode_t)); + /* + * Link the two together + */ + devbp->dnp = dnp; + dnp->links = 1; + /* + * set up the directory node for the root + * and put in all the usual entries for a directory node + */ + dnp->type = DEV_DIR; + dnp->links++; /* for .*/ + /* root loops to self */ + dnp->by.BackDir.parent = dnp; + dnp->links++; /* for ..*/ + /* + * set up the list of children (none so far) + */ + dnp->by.BackDir.dirlist = (devb_p)0; + dnp->by.BackDir.dirlast = + &dnp->by.BackDir.dirlist; + dnp->by.BackDir.myname = devbp; + /* + * set up a pointer to directory type ops + */ + dnp->ops = &devfs_vnodeop_p; + dnp->mode |= 0555; /* default perms */ + /* + * note creation times etc, as now (boot time) + */ + TIMEVAL_TO_TIMESPEC(&time,&(dnp->ctime)) + dnp->mtime = dnp->ctime; + dnp->atime = dnp->ctime; + + /* + * and the list of layers + */ + devbp->fronts = NULL; + devbp->lastfront = &(devbp->fronts); + + + /* + * next time, we don't need to do all this + */ + dev_root = devbp; + devfs_set_up = 1; + } +} + +/***********************************************************************\ +* 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*/ +{ + devb_p devbp; + char pathbuf[DEVMAXPATHSIZE]; + char *path; + char *name; + register char *cp; + int retval; + + + DBPRINT(("dev_finddir\n")); + devfs_back_init(); /* in case we are the first */ + 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 * + \***************************************/ + devbp = dirnode->by.BackDir.dirlist; + while(devbp && strcmp(devbp->name,name)) + { + devbp = devbp->next; + } + if(devbp) + { /* check it's a directory */ + if(devbp->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, &devbp)) + { + return retval; + } + } + if(path) /* decide whether to recurse more or return */ + { + return (dev_finddir(path,devbp->dnp,create,dn_pp)); + } + else + { + *dn_pp = devbp->dnp; + return 0; + } +} + +/***********************************************************************\ +* Add a new element to the devfs backing structure. * +\***********************************************************************/ +int dev_add_node(char *name, dn_p dirnode, int entrytype, union typeinfo *by, devb_p *devb_pp) /*proto*/ +{ + devb_p devbp; + devb_p realthing; /* needed to create an alias */ + dn_p dnp; + int retval; + + DBPRINT(("dev_add_node\n")); + if(dirnode->type != DEV_DIR) return(ENOTDIR); + if(strlen(name) > (DEVMAXNAMESIZE - 1)) return (ENAMETOOLONG); + + 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); + /* + * Allocate and fill out a new backing node + */ + if(!(devbp = (devb_p)malloc(sizeof(devb_t), + M_DEVFSBACK, M_NOWAIT))) + { + return ENOMEM; + } + bzero(devbp,sizeof(devb_t)); + if(!(dnp = (dn_p)malloc(sizeof(devnode_t), + M_DEVFSNODE, M_NOWAIT))) + { + free(devbp,M_DEVFSBACK); + return ENOMEM; + } + bzero(dnp,sizeof(devnode_t)); + devbp->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 + * include the implicit link in the count of links to the devnode.. + * this stops it from being accidentally freed later. + */ + strcpy(devbp->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) + */ + devbp->lastfront = &(devbp->fronts); + + /* + * Put it on the END of the linked list of directory entries + */ + devbp->parent = dirnode; + devbp->prevp = dirnode->by.BackDir.dirlast; + devbp->next = *(devbp->prevp); /* should be NULL */ /*right?*/ + *(devbp->prevp) = devbp; + dirnode->by.BackDir.dirlast = &(devbp->next); + dirnode->by.BackDir.entrycount++; + + /* + * return the answer + */ + switch(entrytype) { + case DEV_DIR: + /* + * As it's a directory, make sure it has a null entries list + */ + dnp->by.BackDir.dirlast = + &(dnp->by.BackDir.dirlist); + dnp->by.BackDir.dirlist = (devb_p)0; + dnp->by.BackDir.parent = (dn_p)dirnode; + dnp->by.BackDir.myname = devbp; + /* + * 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->aliases; + realthing->aliases = devbp; + realthing->alias_count++; + break; + } + + if(retval = devfs_add_fronts(dirnode->by.BackDir.myname/*XXX*/,devbp)) + { + /*XXX*//* no idea what to do if it fails... */ + return retval; + } + + *devb_pp = devbp; + 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(devb_p devbp) /*proto*/ +{ + devb_p alias; + + DBPRINT(("dev_remove\n")); + /* + * Check the type of the node.. for now don't allow dirs + */ + switch(devbp->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 ( devbp->alias_count) + { + alias = devbp->aliases; + devbp->aliases = alias->dnp->by.Alias.next; + devbp->alias_count--; + devfs_dn_free(alias->dnp); + free (alias, M_DEVFSBACK); + } + /* + * Now remove front items of the Main node itself + */ + devfs_remove_fronts(devbp); + + /* + * now we should free the main node + */ + devfs_dn_free(devbp->dnp); + free (devbp, M_DEVFSBACK); + return 0; +} + +int dev_touch(devb_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..) * +\***********************************************************************/ +devb_p dev_add(char *path,char *name,caddr_t funct,int minor,int chrblk,uid_t uid,gid_t gid, int perms) /*proto*/ +{ + devb_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 0: + 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 1: + 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; +} + + + |