summaryrefslogtreecommitdiffstats
path: root/sys/miscfs/devfs/devfs_front.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/miscfs/devfs/devfs_front.c')
-rw-r--r--sys/miscfs/devfs/devfs_front.c481
1 files changed, 481 insertions, 0 deletions
diff --git a/sys/miscfs/devfs/devfs_front.c b/sys/miscfs/devfs/devfs_front.c
new file mode 100644
index 0000000..ce2f7d7
--- /dev/null
+++ b/sys/miscfs/devfs/devfs_front.c
@@ -0,0 +1,481 @@
+/*
+ * Written by Julian Elischer (julian@DIALix.oz.au)
+ *
+ * $Header: /sys/miscfs/devfs/RCS/devfs_front.c,v 1.3 1995/01/07 04:20:25 root Exp root $
+ *
+ */
+
+#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(devb_p parent,devb_p child) /*proto*/
+{
+ devf_p newfp;
+ devf_p falias;
+
+ DBPRINT((" devfs_add_fronts\n"));
+ /***********************************************\
+ * Find the frontnodes of the parent node *
+ \***********************************************/
+ for (falias = parent->fronts; 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*/
+{
+ devf_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,devb_p back,devf_p *devf_pp , struct devfsmount *dvm) /*proto*/
+{
+ devf_p newfp;
+ struct devfsmount *dmt;
+ devb_p newback;
+ devf_p newfront;
+ int error;
+
+ DBPRINT((" dev_mk_front\n"));
+ if(parent && (parent->type != DEV_DIR)) return EINVAL;
+ /*XXX*/ /* printf?*/
+ if(!(newfp = malloc(sizeof(*newfp),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 *
+ * Check the removal code for this! XXX *
+ \*******************************************************/
+ 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:
+ newfp->dnp = back->dnp;
+ newfp->dnp->links++; /* wherever it is.....*/
+ break;
+ case DEV_DIR:
+ newfp->dnp = malloc(sizeof(devnode_t),
+ M_DEVFSNODE,M_NOWAIT);
+ if(!(newfp->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));
+ newfp->dnp->links = 1; /* EXTRA from '.' */
+ newfp->dnp->links++; /* wherever it is.....*/
+ newfp->dnp->by.Dir.dirlast =
+ &newfp->dnp->by.Dir.dirlist;
+ newfp->dnp->by.Dir.dirlist = NULL;
+ newfp->dnp->by.Dir.entrycount = 0;
+ newfp->dnp->vn = NULL;
+ newfp->dnp->vn_id = 0;
+ break;
+ case DEV_SLNK: /* should never happen */
+ 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...
+ */
+ newfp->dnp->by.Dir.parent = newfp->dnp;
+ newfp->dnp->links++; /* extra for '..'*/
+ newfp->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->lastfront;
+ newfp->prev_frontp = back->lastfront;
+ *back->lastfront = newfp;
+ back->lastfront = &(newfp->next_front);
+ back->frontcount++;
+ newfp->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.BackDir.dirlist;
+ newback; newback = newback->next)
+ {
+ if(error = dev_mk_front(newfp->dnp,
+ newback, &newfront, NULL))
+ {
+ return error;
+ }
+ }
+ }
+ *devf_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*/
+{
+ devf_p parent;
+ devf_p new;
+ devb_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*/
+{
+ devf_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(devb_p devbp) /*proto*/
+{
+ while(devbp->fronts)
+ {
+ dev_free_front(devbp->fronts);
+ }
+}
+/***************************************************************\
+* Free a front node (and any below it of it's a directory node) *
+\***************************************************************/
+void dev_free_front(devf_p devfp) /*proto*/
+{
+ dn_p parent = devfp->parent;
+ devb_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->realthing) /* yes an assign */
+ {
+ if( *devfp->prev_frontp = devfp->next_front)/* yes, assign */
+ {
+ devfp->next_front->prev_frontp = devfp->prev_frontp;
+ }
+ else
+ {
+ back->lastfront = 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);
+}
+
+/***************************************************************\
+* Think about this: *
+* Though this routine uses a front node, it also uses a backing *
+* node indirectly, via the 'realthing' link. This may prove bad *
+* in the case of a user-added slink, where there migh not be a *
+* backing node. (e.g. if a slink points out of the fs it CAN'T *
+* have a backing node, unlike a hardlink which does..) *
+* we are going to have to think very carefully about slinks.. *
+\***************************************************************/
+int devfs_dntovn(dn_p front, struct vnode **vn_pp) /*proto*/
+{
+ struct vnode *vn_p, *nvp;
+ int error = 0;
+
+ vn_p = front->vn;
+DBPRINT(("dntovn "));
+ if( vn_p)
+ {
+ if(vn_p->v_id != front->vn_id)
+ {
+ printf("bad-id ");
+ goto skip;
+ }
+ if(vn_p->v_tag != VT_DEVFS)
+ {
+ printf("bad-tag ");
+ goto skip;
+ }
+ if(vn_p->v_op != *(front->ops))
+ {
+ printf("bad-ops ");
+ goto skip;
+ }
+ if((dn_p)(vn_p->v_data) != front)
+ {
+ 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,
+ front->dvm->mount,
+ *(front->ops),
+ &vn_p)))
+ {
+ front->vn = vn_p;
+ front->vn_id = vn_p->v_id;
+ *vn_pp = vn_p;
+DBPRINT(("(New vnode)"));
+ switch(front->type)
+ {
+ case DEV_SLNK:
+ break;
+ case DEV_DIR:
+ if(front->by.Dir.parent == front)
+ {
+ vn_p->v_flag |= VROOT;
+ }
+ vn_p->v_type = VDIR;
+ break;
+ case DEV_BDEV:
+ vn_p->v_type = VBLK;
+ if (nvp = checkalias(vn_p,
+ front->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,
+ front->by.Cdev.dev,
+ (struct mount *)0))
+ {
+ vput(vn_p);
+ vn_p = nvp;
+ }
+ break;
+ case DEV_DDEV:
+ break;
+ }
+ if ( vn_p)
+ {
+ vn_p->v_mount = front->dvm->mount;/* Duplicated */
+ *vn_pp = vn_p;
+ vn_p->v_data = (void *)front;
+/*XXX*/ /* maybe not.. I mean what if it's a dev... (vnode at back)*/
+ }
+ else
+ {
+ error = EINVAL;
+ }
+ }
+ return(error);
+}
+
OpenPOWER on IntegriCloud