/* * 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; }