summaryrefslogtreecommitdiff
path: root/sys/src/9/port/dev.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/dev.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/dev.c')
-rwxr-xr-xsys/src/9/port/dev.c449
1 files changed, 449 insertions, 0 deletions
diff --git a/sys/src/9/port/dev.c b/sys/src/9/port/dev.c
new file mode 100755
index 000000000..bbe1fcc12
--- /dev/null
+++ b/sys/src/9/port/dev.c
@@ -0,0 +1,449 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+extern ulong kerndate;
+
+void
+mkqid(Qid *q, vlong path, ulong vers, int type)
+{
+ q->type = type;
+ q->vers = vers;
+ q->path = path;
+}
+
+int
+devno(int c, int user)
+{
+ int i;
+
+ for(i = 0; devtab[i] != nil; i++) {
+ if(devtab[i]->dc == c)
+ return i;
+ }
+ if(user == 0)
+ panic("devno %C %#ux", c, c);
+
+ return -1;
+}
+
+void
+devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db)
+{
+ db->name = n;
+ if(c->flag&CMSG)
+ qid.type |= QTMOUNT;
+ db->qid = qid;
+ db->type = devtab[c->type]->dc;
+ db->dev = c->dev;
+ db->mode = perm;
+ db->mode |= qid.type << 24;
+ db->atime = seconds();
+ db->mtime = kerndate;
+ db->length = length;
+ db->uid = user;
+ db->gid = eve;
+ db->muid = user;
+}
+
+/*
+ * (here, Devgen is the prototype; devgen is the function in dev.c.)
+ *
+ * a Devgen is expected to return the directory entry for ".."
+ * if you pass it s==DEVDOTDOT (-1). otherwise...
+ *
+ * there are two contradictory rules.
+ *
+ * (i) if c is a directory, a Devgen is expected to list its children
+ * as you iterate s.
+ *
+ * (ii) whether or not c is a directory, a Devgen is expected to list
+ * its siblings as you iterate s.
+ *
+ * devgen always returns the list of children in the root
+ * directory. thus it follows (i) when c is the root and (ii) otherwise.
+ * many other Devgens follow (i) when c is a directory and (ii) otherwise.
+ *
+ * devwalk assumes (i). it knows that devgen breaks (i)
+ * for children that are themselves directories, and explicitly catches them.
+ *
+ * devstat assumes (ii). if the Devgen in question follows (i)
+ * for this particular c, devstat will not find the necessary info.
+ * with our particular Devgen functions, this happens only for
+ * directories, so devstat makes something up, assuming
+ * c->name, c->qid, eve, DMDIR|0555.
+ *
+ * devdirread assumes (i). the callers have to make sure
+ * that the Devgen satisfies (i) for the chan being read.
+ */
+/*
+ * the zeroth element of the table MUST be the directory itself for ..
+*/
+int
+devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+ if(tab == 0)
+ return -1;
+ if(i == DEVDOTDOT){
+ /* nothing */
+ }else if(name){
+ for(i=1; i<ntab; i++)
+ if(strcmp(tab[i].name, name) == 0)
+ break;
+ if(i==ntab)
+ return -1;
+ tab += i;
+ }else{
+ /* skip over the first element, that for . itself */
+ i++;
+ if(i >= ntab)
+ return -1;
+ tab += i;
+ }
+ devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
+ return 1;
+}
+
+void
+devreset(void)
+{
+}
+
+void
+devinit(void)
+{
+}
+
+void
+devshutdown(void)
+{
+}
+
+Chan*
+devattach(int tc, char *spec)
+{
+ int n;
+ Chan *c;
+ char *buf;
+
+ c = newchan();
+ mkqid(&c->qid, 0, 0, QTDIR);
+ c->type = devno(tc, 0);
+ if(spec == nil)
+ spec = "";
+ n = 1+UTFmax+strlen(spec)+1;
+ buf = smalloc(n);
+ snprint(buf, n, "#%C%s", tc, spec);
+ c->path = newpath(buf);
+ free(buf);
+ return c;
+}
+
+
+Chan*
+devclone(Chan *c)
+{
+ Chan *nc;
+
+ if(c->flag & COPEN)
+ panic("clone of open file type %C\n", devtab[c->type]->dc);
+
+ nc = newchan();
+
+ nc->type = c->type;
+ nc->dev = c->dev;
+ nc->mode = c->mode;
+ nc->qid = c->qid;
+ nc->offset = c->offset;
+ nc->umh = nil;
+ nc->aux = c->aux;
+ nc->mqid = c->mqid;
+ nc->mcp = c->mcp;
+ return nc;
+}
+
+Walkqid*
+devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen)
+{
+ int i, j, alloc;
+ Walkqid *wq;
+ char *n;
+ Dir dir;
+
+ if(nname > 0)
+ isdir(c);
+
+ alloc = 0;
+ wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
+ if(waserror()){
+ if(alloc && wq->clone!=nil)
+ cclose(wq->clone);
+ free(wq);
+ return nil;
+ }
+ if(nc == nil){
+ nc = devclone(c);
+ nc->type = 0; /* device doesn't know about this channel yet */
+ alloc = 1;
+ }
+ wq->clone = nc;
+
+ for(j=0; j<nname; j++){
+ if(!(nc->qid.type&QTDIR)){
+ if(j==0)
+ error(Enotdir);
+ goto Done;
+ }
+ n = name[j];
+ if(strcmp(n, ".") == 0){
+ Accept:
+ wq->qid[wq->nqid++] = nc->qid;
+ continue;
+ }
+ if(strcmp(n, "..") == 0){
+ if((*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir) != 1){
+ print("devgen walk .. in dev%s %llux broken\n",
+ devtab[nc->type]->name, nc->qid.path);
+ error("broken devgen");
+ }
+ nc->qid = dir.qid;
+ goto Accept;
+ }
+ /*
+ * Ugly problem: If we're using devgen, make sure we're
+ * walking the directory itself, represented by the first
+ * entry in the table, and not trying to step into a sub-
+ * directory of the table, e.g. /net/net. Devgen itself
+ * should take care of the problem, but it doesn't have
+ * the necessary information (that we're doing a walk).
+ */
+ if(gen==devgen && nc->qid.path!=tab[0].qid.path)
+ goto Notfound;
+ for(i=0;; i++) {
+ switch((*gen)(nc, n, tab, ntab, i, &dir)){
+ case -1:
+ Notfound:
+ if(j == 0)
+ error(Enonexist);
+ kstrcpy(up->errstr, Enonexist, ERRMAX);
+ goto Done;
+ case 0:
+ continue;
+ case 1:
+ if(strcmp(n, dir.name) == 0){
+ nc->qid = dir.qid;
+ goto Accept;
+ }
+ continue;
+ }
+ }
+ }
+ /*
+ * We processed at least one name, so will return some data.
+ * If we didn't process all nname entries succesfully, we drop
+ * the cloned channel and return just the Qids of the walks.
+ */
+Done:
+ poperror();
+ if(wq->nqid < nname){
+ if(alloc)
+ cclose(wq->clone);
+ wq->clone = nil;
+ }else if(wq->clone){
+ /* attach cloned channel to same device */
+ wq->clone->type = c->type;
+ }
+ return wq;
+}
+
+int
+devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen)
+{
+ int i;
+ Dir dir;
+ char *p, *elem;
+
+ for(i=0;; i++){
+ switch((*gen)(c, nil, tab, ntab, i, &dir)){
+ case -1:
+ if(c->qid.type & QTDIR){
+ if(c->path == nil)
+ elem = "???";
+ else if(strcmp(c->path->s, "/") == 0)
+ elem = "/";
+ else
+ for(elem=p=c->path->s; *p; p++)
+ if(*p == '/')
+ elem = p+1;
+ devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
+ n = convD2M(&dir, db, n);
+ if(n == 0)
+ error(Ebadarg);
+ return n;
+ }
+
+ error(Enonexist);
+ case 0:
+ break;
+ case 1:
+ if(c->qid.path == dir.qid.path) {
+ if(c->flag&CMSG)
+ dir.mode |= DMMOUNT;
+ n = convD2M(&dir, db, n);
+ if(n == 0)
+ error(Ebadarg);
+ return n;
+ }
+ break;
+ }
+ }
+}
+
+long
+devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen)
+{
+ long m, dsz;
+ Dir dir;
+
+ for(m=0; m<n; c->dri++) {
+ switch((*gen)(c, nil, tab, ntab, c->dri, &dir)){
+ case -1:
+ return m;
+
+ case 0:
+ break;
+
+ case 1:
+ dsz = convD2M(&dir, (uchar*)d, n-m);
+ if(dsz <= BIT16SZ){ /* <= not < because this isn't stat; read is stuck */
+ if(m == 0)
+ error(Eshort);
+ return m;
+ }
+ m += dsz;
+ d += dsz;
+ break;
+ }
+ }
+
+ return m;
+}
+
+/*
+ * error(Eperm) if open permission not granted for up->user.
+ */
+void
+devpermcheck(char *fileuid, ulong perm, int omode)
+{
+ ulong t;
+ static int access[] = { 0400, 0200, 0600, 0100 };
+
+ if(strcmp(up->user, fileuid) == 0)
+ perm <<= 0;
+ else
+ if(strcmp(up->user, eve) == 0)
+ perm <<= 3;
+ else
+ perm <<= 6;
+
+ t = access[omode&3];
+ if((t&perm) != t)
+ error(Eperm);
+}
+
+Chan*
+devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen)
+{
+ int i;
+ Dir dir;
+
+ for(i=0;; i++) {
+ switch((*gen)(c, nil, tab, ntab, i, &dir)){
+ case -1:
+ goto Return;
+ case 0:
+ break;
+ case 1:
+ if(c->qid.path == dir.qid.path) {
+ devpermcheck(dir.uid, dir.mode, omode);
+ goto Return;
+ }
+ break;
+ }
+ }
+Return:
+ c->offset = 0;
+ if((c->qid.type&QTDIR) && omode!=OREAD)
+ error(Eperm);
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ return c;
+}
+
+void
+devcreate(Chan*, char*, int, ulong)
+{
+ error(Eperm);
+}
+
+Block*
+devbread(Chan *c, long n, ulong offset)
+{
+ Block *bp;
+
+ bp = allocb(n);
+ if(bp == 0)
+ error(Enomem);
+ if(waserror()) {
+ freeb(bp);
+ nexterror();
+ }
+ bp->wp += devtab[c->type]->read(c, bp->wp, n, offset);
+ poperror();
+ return bp;
+}
+
+long
+devbwrite(Chan *c, Block *bp, ulong offset)
+{
+ long n;
+
+ if(waserror()) {
+ freeb(bp);
+ nexterror();
+ }
+ n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset);
+ poperror();
+ freeb(bp);
+
+ return n;
+}
+
+void
+devremove(Chan*)
+{
+ error(Eperm);
+}
+
+int
+devwstat(Chan*, uchar*, int)
+{
+ error(Eperm);
+ return 0;
+}
+
+void
+devpower(int)
+{
+ error(Eperm);
+}
+
+int
+devconfig(int, char *, DevConf *)
+{
+ error(Eperm);
+ return 0;
+}