diff options
Diffstat (limited to 'src/cmd/9660srv/xfile.c')
-rw-r--r-- | src/cmd/9660srv/xfile.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/src/cmd/9660srv/xfile.c b/src/cmd/9660srv/xfile.c new file mode 100644 index 00000000..c46adf21 --- /dev/null +++ b/src/cmd/9660srv/xfile.c @@ -0,0 +1,170 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> +#include "dat.h" +#include "fns.h" + +static Xfile* clean(Xfile*); + +#define FIDMOD 127 /* prime */ + +static Xdata* xhead; +static Xfile* xfiles[FIDMOD]; +static Xfile* freelist; + +Xdata* +getxdata(char *name) +{ + int fd; + Dir *dir; + Xdata *xf, *fxf; + int flag; + + if(name[0] == 0) + name = deffile; + if(name == 0) + error(Enofile); + flag = (access(name, 6) == 0) ? ORDWR : OREAD; + fd = open(name, flag); + if(fd < 0) + error(Enonexist); + dir = nil; + if(waserror()){ + close(fd); + free(dir); + nexterror(); + } + if((dir = dirfstat(fd)) == nil) + error("I/O error"); + if((dir->qid.type & ~QTTMP) != QTFILE) + error("attach name not a plain file"); + for(fxf=0,xf=xhead; xf; xf=xf->next){ + if(xf->name == 0){ + if(fxf == 0) + fxf = xf; + continue; + } + if(xf->qid.path != dir->qid.path || xf->qid.vers != dir->qid.vers) + continue; + if(xf->type != dir->type || xf->fdev != dir->dev) + continue; + xf->ref++; + chat("incref=%d, \"%s\", dev=%d...", xf->ref, xf->name, xf->dev); + close(fd); + poperror(); + free(dir); + return xf; + } + if(fxf==0){ + fxf = ealloc(sizeof(Xfs)); + fxf->next = xhead; + xhead = fxf; + } + chat("alloc \"%s\", dev=%d...", name, fd); + fxf->ref = 1; + fxf->name = strcpy(ealloc(strlen(name)+1), name); + fxf->qid = dir->qid; + fxf->type = dir->type; + fxf->fdev = dir->dev; + fxf->dev = fd; + free(dir); + poperror(); + return fxf; +} + +static void +putxdata(Xdata *d) +{ + if(d->ref <= 0) + panic(0, "putxdata"); + d->ref--; + chat("decref=%d, \"%s\", dev=%d...", d->ref, d->name, d->dev); + if(d->ref == 0){ + chat("purgebuf..."); + purgebuf(d); + close(d->dev); + free(d->name); + d->name = 0; + } +} + +void +refxfs(Xfs *xf, int delta) +{ + xf->ref += delta; + if(xf->ref == 0){ + if(xf->d) + putxdata(xf->d); + if(xf->ptr) + free(xf->ptr); + free(xf); + } +} + +Xfile* +xfile(int fid, int flag) +{ + int k = fid%FIDMOD; + Xfile **hp=&xfiles[k], *f, *pf; + + for(f=*hp,pf=0; f; pf=f,f=f->next) + if(f->fid == fid) + break; + if(f && pf){ + pf->next = f->next; + f->next = *hp; + *hp = f; + } + switch(flag){ + default: + panic(0, "xfile"); + case Asis: + if(f == 0) + error("unassigned fid"); + return f; + case Clean: + break; + case Clunk: + if(f){ + *hp = f->next; + clean(f); + f->next = freelist; + freelist = f; + } + return 0; + } + if(f) + return clean(f); + if(f = freelist) /* assign = */ + freelist = f->next; + else + f = ealloc(sizeof(Xfile)); + f->next = *hp; + *hp = f; + f->xf = 0; + f->fid = fid; + f->flags = 0; + f->qid = (Qid){0,0,0}; + f->len = 0; + f->ptr = 0; + return f; +} + +static Xfile * +clean(Xfile *f) +{ + if(f->xf){ + refxfs(f->xf, -1); + f->xf = 0; + } + if(f->len){ + free(f->ptr); + f->len = 0; + } + f->ptr = 0; + f->flags = 0; + f->qid = (Qid){0,0,0}; + return f; +} + |