diff options
Diffstat (limited to 'emu/Plan9/devfs.c')
| -rw-r--r-- | emu/Plan9/devfs.c | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/emu/Plan9/devfs.c b/emu/Plan9/devfs.c new file mode 100644 index 00000000..d0db522d --- /dev/null +++ b/emu/Plan9/devfs.c @@ -0,0 +1,365 @@ +/* + * Plan 9 file system interface + */ +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct Fsinfo Fsinfo; +struct Fsinfo +{ + int fd; + QLock; /* serialise access to offset */ + ulong offset; /* offset used only for directory reads */ + Cname* name; /* Plan 9's name for file */ + Qid rootqid; /* Plan 9's qid for Inferno's root */ + char* root; /* prefix to strip from all names in diagnostics */ +}; +#define FS(c) ((Fsinfo*)((c)->aux)) + +char rootdir[MAXROOT] = ROOT; + +static void +fserr(Fsinfo *f) +{ + int n; + char *p; + + oserrstr(up->env->errstr, ERRMAX); + if(f != nil && *up->env->errstr == '\'' && (n = strlen(f->root)) > 1){ + /* don't reveal full names */ + if(strncmp(up->env->errstr+1, f->root, n-1) == 0){ + p = up->env->errstr+1+n; + memmove(up->env->errstr+1, p, strlen(p)+1); + } + } + error(up->env->errstr); +} + +static void +fsfree(Chan *c) +{ + cnameclose(FS(c)->name); + free(c->aux); +} + +Chan* +fsattach(char *spec) +{ + Chan *c; + Dir *d; + char *root; + Qid rootqid; + static int devno; + static Lock l; + + if(!emptystr(spec)){ + if(strcmp(spec, "*") != 0) + error(Ebadspec); + root = "/"; + }else + root = rootdir; + + d = dirstat(root); + if(d == nil) + fserr(nil); + rootqid = d->qid; + free(d); + + c = devattach('U', spec); + lock(&l); + c->dev = devno++; + c->qid = rootqid; + unlock(&l); + c->aux = smalloc(sizeof(Fsinfo)); + FS(c)->name = newcname(root); + FS(c)->rootqid = rootqid; + FS(c)->fd = -1; + FS(c)->root = root; + + return c; +} + +Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int j, alloc; + Walkqid *wq; + Dir *dir; + char *n; + Cname *current, *next; + Qid rootqid; + + if(nname > 0) + isdir(c); /* do we need this? */ + + alloc = 0; + current = nil; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + cnameclose(current); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + + rootqid = FS(c)->rootqid; + current = FS(c)->name; + if(current != nil) + incref(¤t->r); + for(j=0; j<nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + break; + } + n = name[j]; + if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){ /* TO DO: underlying qids aliased */ + //print("** ufs walk '%s' -> %s\n", current->s, n); + next = current; + incref(&next->r); + next = addelem(current, n); + dir = dirstat(next->s); + if(dir == nil){ + cnameclose(next); + if(j == 0) + error(Enonexist); + strcpy(up->env->errstr, Enonexist); + break; + } + nc->qid = dir->qid; + free(dir); + cnameclose(current); + current = next; + } + wq->qid[wq->nqid++] = nc->qid; + } +// print("** ufs walk '%s'\n", current->s); + + poperror(); + if(wq->nqid < nname){ + cnameclose(current); + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + /* now attach to our device */ + nc->aux = smalloc(sizeof(Fsinfo)); + nc->type = c->type; + FS(nc)->rootqid = FS(c)->rootqid; + FS(nc)->name = current; + FS(nc)->fd = -1; + FS(nc)->root = FS(c)->root; + }else + panic("fswalk: can't happen"); + return wq; +} + +int +fsstat(Chan *c, uchar *dp, int n) +{ + if(FS(c)->fd >= 0) + n = fstat(FS(c)->fd, dp, n); + else + n = stat(FS(c)->name->s, dp, n); + if(n < 0) + fserr(FS(c)); + /* TO DO: change name to / if rootqid */ + return n; +} + +Chan* +fsopen(Chan *c, int mode) +{ + osenter(); + FS(c)->fd = open(FS(c)->name->s, mode); + osleave(); + if(FS(c)->fd < 0) + fserr(FS(c)); + c->mode = openmode(mode); + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; + return c; +} + +void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + Dir *d; + Cname *n; + + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error(Efilename); + n = addelem(newcname(FS(c)->name->s), name); + osenter(); + FS(c)->fd = create(n->s, mode, perm); + osleave(); + if(FS(c)->fd < 0) { + cnameclose(n); + fserr(FS(c)); + } + d = dirfstat(FS(c)->fd); + if(d == nil) { + cnameclose(n); + close(FS(c)->fd); + FS(c)->fd = -1; + fserr(FS(c)); + } + c->qid = d->qid; + free(d); + + cnameclose(FS(c)->name); + FS(c)->name = n; + + c->mode = openmode(mode); + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; +} + +void +fsclose(Chan *c) +{ + if(c->flag & COPEN){ + osenter(); + close(FS(c)->fd); + osleave(); + } + /* don't need to check for CRCLOSE, because Plan 9 itself implements ORCLOSE */ + fsfree(c); +} + +static long +fsdirread(Chan *c, void *va, long count, vlong offset) +{ + long n, r; + static char slop[16384]; + + if(FS(c)->offset != offset){ + seek(FS(c)->fd, 0, 0); + for(n=0; n<offset;) { + r = offset - n; + if(r > sizeof(slop)) + r = sizeof(slop); + osenter(); + r = read(FS(c)->fd, slop, r); + osleave(); + if(r <= 0){ + FS(c)->offset = n; + return 0; + } + n += r; + } + FS(c)->offset = offset; + } + osenter(); + r = read(FS(c)->fd, va, count); + osleave(); + if(r < 0) + return r; + FS(c)->offset = offset+r; + return r; +} + +long +fsread(Chan *c, void *va, long n, vlong offset) +{ + int r; + + if(c->qid.type & QTDIR){ /* need to maintain offset only for directories */ + qlock(FS(c)); + if(waserror()){ + qunlock(FS(c)); + nexterror(); + } + r = fsdirread(c, va, n, offset); + poperror(); + qunlock(FS(c)); + }else{ + osenter(); + r = pread(FS(c)->fd, va, n, offset); + osleave(); + } + if(r < 0) + fserr(FS(c)); + return r; +} + +long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + int r; + + osenter(); + r = pwrite(FS(c)->fd, va, n, offset); + osleave(); + if(r < 0) + fserr(FS(c)); + return r; +} + +void +fsremove(Chan *c) +{ + int r; + + if(waserror()){ + fsfree(c); + nexterror(); + } + osenter(); + r = remove(FS(c)->name->s); + osleave(); + if(r < 0) + fserr(FS(c)); + poperror(); + fsfree(c); +} + +int +fswstat(Chan *c, uchar *dp, int n) +{ + osenter(); + if(FS(c)->fd >= 0) + n = fwstat(FS(c)->fd, dp, n); + else + n = wstat(FS(c)->name->s, dp, n); + osleave(); + if(n < 0) + fserr(FS(c)); + return n; +} + +void +setid(char *name, int owner) +{ + if(!owner || iseve()) + kstrdup(&up->env->user, name); +} + +Dev fsdevtab = { + 'U', + "fs", + + devinit, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat +}; |
