| #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; |
| } |
| |