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