From 2277c5d7bbe1f9595fad512d8f790708473a9bf1 Mon Sep 17 00:00:00 2001 From: rsc Date: Sun, 21 Mar 2004 04:33:13 +0000 Subject: Small tweaks Lots of new code imported. --- src/lib9p/file.c | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 src/lib9p/file.c (limited to 'src/lib9p/file.c') diff --git a/src/lib9p/file.c b/src/lib9p/file.c new file mode 100644 index 00000000..79a8a11d --- /dev/null +++ b/src/lib9p/file.c @@ -0,0 +1,372 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +/* + * To avoid deadlock, the following rules must be followed. + * Always lock child then parent, never parent then child. + * If holding the free file lock, do not lock any Files. + */ +struct Filelist { + File *f; + Filelist *link; +}; + +static QLock filelk; +static File *freefilelist; + +static File* +allocfile(void) +{ + int i, a; + File *f; + enum { N = 16 }; + + qlock(&filelk); + if(freefilelist == nil){ + f = emalloc9p(N*sizeof(*f)); + for(i=0; iaux; + qunlock(&filelk); + + a = f->allocd; + memset(f, 0, sizeof *f); + f->allocd = a; + return f; +} + +static void +freefile(File *f) +{ + Filelist *fl, *flnext; + + for(fl=f->filelist; fl; fl=flnext){ + flnext = fl->link; + assert(fl->f == nil); + free(fl); + } + + free(f->dir.name); + free(f->dir.uid); + free(f->dir.gid); + free(f->dir.muid); + qlock(&filelk); + assert(f->ref.ref == 0); + f->aux = freefilelist; + freefilelist = f; + qunlock(&filelk); +} + +void +closefile(File *f) +{ + if(decref(&f->ref) == 0){ + f->tree->destroy(f); + freefile(f); + } +} + +static void +nop(File *f) +{ + USED(f); +} + +int +removefile(File *f) +{ + File *fp; + Filelist *fl; + + fp = f->parent; + if(fp == nil){ + werrstr("no parent"); + closefile(f); + return -1; + } + + if(fp == f){ + werrstr("cannot remove root"); + closefile(f); + return -1; + } + + wlock(&fp->rwlock); + wlock(&f->rwlock); + if(f->nchild != 0){ + werrstr("has children"); + wunlock(&f->rwlock); + wunlock(&fp->rwlock); + closefile(f); + return -1; + } + + if(f->parent != fp){ + werrstr("parent changed underfoot"); + wunlock(&f->rwlock); + wunlock(&fp->rwlock); + closefile(f); + return -1; + } + + for(fl=fp->filelist; fl; fl=fl->link) + if(fl->f == f) + break; + assert(fl != nil && fl->f == f); + + fl->f = nil; + fp->nchild--; + f->parent = nil; + wunlock(&fp->rwlock); + wunlock(&f->rwlock); + + closefile(fp); /* reference from child */ + closefile(f); /* reference from tree */ + closefile(f); + return 0; +} + +File* +createfile(File *fp, char *name, char *uid, ulong perm, void *aux) +{ + File *f; + Filelist *fl, *freel; + Tree *t; + + if((fp->dir.qid.type&QTDIR) == 0){ + werrstr("create in non-directory"); + return nil; + } + + freel = nil; + wlock(&fp->rwlock); + for(fl=fp->filelist; fl; fl=fl->link){ + if(fl->f == nil) + freel = fl; + else if(strcmp(fl->f->dir.name, name) == 0){ + wunlock(&fp->rwlock); + werrstr("file already exists"); + return nil; + } + } + + if(freel == nil){ + freel = emalloc9p(sizeof *freel); + freel->link = fp->filelist; + fp->filelist = freel; + } + + f = allocfile(); + f->dir.name = estrdup9p(name); + f->dir.uid = estrdup9p(uid ? uid : fp->dir.uid); + f->dir.gid = estrdup9p(fp->dir.gid); + f->dir.muid = estrdup9p(uid ? uid : "unknown"); + f->aux = aux; + f->dir.mode = perm; + + t = fp->tree; + lock(&t->genlock); + f->dir.qid.path = t->qidgen++; + unlock(&t->genlock); + if(perm & DMDIR) + f->dir.qid.type |= QTDIR; + if(perm & DMAPPEND) + f->dir.qid.type |= QTAPPEND; + if(perm & DMEXCL) + f->dir.qid.type |= QTEXCL; + + f->dir.mode = perm; + f->dir.atime = f->dir.mtime = time(0); + f->dir.length = 0; + f->parent = fp; + incref(&fp->ref); + f->tree = fp->tree; + + incref(&f->ref); /* being returned */ + incref(&f->ref); /* for the tree */ + freel->f = f; + fp->nchild++; + wunlock(&fp->rwlock); + + return f; +} + +static File* +walkfile1(File *dir, char *elem) +{ + File *fp; + Filelist *fl; + + rlock(&dir->rwlock); + if(strcmp(elem, "..") == 0){ + fp = dir->parent; + incref(&fp->ref); + runlock(&dir->rwlock); + closefile(dir); + return fp; + } + + fp = nil; + for(fl=dir->filelist; fl; fl=fl->link) + if(fl->f && strcmp(fl->f->dir.name, elem)==0){ + fp = fl->f; + incref(&fp->ref); + break; + } + + runlock(&dir->rwlock); + closefile(dir); + return fp; +} + +File* +walkfile(File *f, char *path) +{ + char *os, *s, *nexts; + File *nf; + + if(strchr(path, '/') == nil) + return walkfile1(f, path); /* avoid malloc */ + + os = s = estrdup9p(path); + incref(&f->ref); + for(; *s; s=nexts){ + if(nexts = strchr(s, '/')) + *nexts++ = '\0'; + else + nexts = s+strlen(s); + nf = walkfile1(f, s); + decref(&f->ref); + f = nf; + if(f == nil) + break; + } + free(os); + return f; +} + +Tree* +alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*)) +{ + char *muid; + Tree *t; + File *f; + + t = emalloc9p(sizeof *t); + f = allocfile(); + f->dir.name = estrdup9p("/"); + if(uid == nil){ + if(uid = getuser()) + uid = estrdup9p(uid); + } + if(uid == nil) + uid = estrdup9p("none"); + else + uid = estrdup9p(uid); + + if(gid == nil) + gid = estrdup9p(uid); + else + gid = estrdup9p(gid); + + muid = estrdup9p(uid); + + f->dir.qid = (Qid){0, 0, QTDIR}; + f->dir.length = 0; + f->dir.atime = f->dir.mtime = time(0); + f->dir.mode = DMDIR | mode; + f->tree = t; + f->parent = f; + f->dir.uid = uid; + f->dir.gid = gid; + f->dir.muid = muid; + + incref(&f->ref); + t->root = f; + t->qidgen = 0; + t->dirqidgen = 1; + if(destroy == nil) + destroy = nop; + t->destroy = destroy; + + return t; +} + +static void +_freefiles(File *f) +{ + Filelist *fl, *flnext; + + for(fl=f->filelist; fl; fl=flnext){ + flnext = fl->link; + _freefiles(fl->f); + free(fl); + } + + f->tree->destroy(f); + freefile(f); +} + +void +freetree(Tree *t) +{ + _freefiles(t->root); + free(t); +} + +struct Readdir { + Filelist *fl; +}; + +Readdir* +opendirfile(File *dir) +{ + Readdir *r; + + rlock(&dir->rwlock); + if((dir->dir.mode & DMDIR)==0){ + runlock(&dir->rwlock); + return nil; + } + r = emalloc9p(sizeof(*r)); + + /* + * This reference won't go away while we're using it + * since we are dir->rdir. + */ + r->fl = dir->filelist; + runlock(&dir->rwlock); + return r; +} + +long +readdirfile(Readdir *r, uchar *buf, long n) +{ + long x, m; + Filelist *fl; + + for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){ + if(fl->f == nil) + x = 0; + else if((x=convD2M(&fl->f->dir, buf+m, n-m)) <= BIT16SZ) + break; + } + r->fl = fl; + return m; +} + +void +closedirfile(Readdir *r) +{ + free(r); +} -- cgit v1.2.3