summaryrefslogtreecommitdiff
path: root/sys/src/lib9p/file.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/lib9p/file.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/lib9p/file.c')
-rwxr-xr-xsys/src/lib9p/file.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/sys/src/lib9p/file.c b/sys/src/lib9p/file.c
new file mode 100755
index 000000000..1dc324f63
--- /dev/null
+++ b/sys/src/lib9p/file.c
@@ -0,0 +1,415 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#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;
+};
+
+struct Readdir
+{
+ File *dir;
+ Filelist *fl;
+};
+
+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; i<N-1; i++)
+ f[i].aux = &f[i+1];
+ f[N-1].aux = nil;
+ f[0].allocd = 1;
+ freefilelist = f;
+ }
+
+ f = freefilelist;
+ freefilelist = f->aux;
+ 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->name);
+ free(f->uid);
+ free(f->gid);
+ free(f->muid);
+ qlock(&filelk);
+ assert(f->ref == 0);
+ f->aux = freefilelist;
+ freefilelist = f;
+ qunlock(&filelk);
+}
+
+static void
+cleanfilelist(File *f)
+{
+ Filelist **l;
+ Filelist *fl;
+
+ /*
+ * can't delete filelist structures while there
+ * are open readers of this directory, because
+ * they might have references to the structures.
+ * instead, just leave the empty refs in the list
+ * until there is no activity and then clean up.
+ */
+ if(f->readers.ref != 0)
+ return;
+ if(f->nxchild == 0)
+ return;
+
+ /*
+ * no dir readers, file is locked, and
+ * there are empty entries in the file list.
+ * clean them out.
+ */
+ for(l=&f->filelist; fl=*l; ){
+ if(fl->f == nil){
+ *l = (*l)->link;
+ free(fl);
+ }else
+ l = &(*l)->link;
+ }
+ f->nxchild = 0;
+}
+
+void
+closefile(File *f)
+{
+ if(decref(f) == 0){
+ f->tree->destroy(f);
+ freefile(f);
+ }
+}
+
+static void
+nop(File*)
+{
+}
+
+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(f);
+ wlock(fp);
+ if(f->nchild != 0){
+ werrstr("has children");
+ wunlock(fp);
+ wunlock(f);
+ closefile(f);
+ return -1;
+ }
+
+ if(f->parent != fp){
+ werrstr("parent changed underfoot");
+ wunlock(fp);
+ wunlock(f);
+ 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--;
+ fp->nxchild++;
+ f->parent = nil;
+ wunlock(f);
+
+ cleanfilelist(fp);
+ wunlock(fp);
+
+ 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 **l, *fl;
+ Tree *t;
+
+ if((fp->qid.type&QTDIR) == 0){
+ werrstr("create in non-directory");
+ return nil;
+ }
+
+ wlock(fp);
+ /*
+ * We might encounter blank spots along the
+ * way due to deleted files that have not yet
+ * been flushed from the file list. Don't reuse
+ * those - some apps (e.g., omero) depend on
+ * the file order reflecting creation order.
+ * Always create at the end of the list.
+ */
+ for(l=&fp->filelist; fl=*l; l=&fl->link){
+ if(fl->f && strcmp(fl->f->name, name) == 0){
+ wunlock(fp);
+ werrstr("file already exists");
+ return nil;
+ }
+ }
+
+ fl = emalloc9p(sizeof *fl);
+ *l = fl;
+
+ f = allocfile();
+ f->name = estrdup9p(name);
+ f->uid = estrdup9p(uid ? uid : fp->uid);
+ f->gid = estrdup9p(fp->gid);
+ f->muid = estrdup9p(uid ? uid : "unknown");
+ f->aux = aux;
+ f->mode = perm;
+
+ t = fp->tree;
+ lock(&t->genlock);
+ f->qid.path = t->qidgen++;
+ unlock(&t->genlock);
+ if(perm & DMDIR)
+ f->qid.type |= QTDIR;
+ if(perm & DMAPPEND)
+ f->qid.type |= QTAPPEND;
+ if(perm & DMEXCL)
+ f->qid.type |= QTEXCL;
+
+ f->mode = perm;
+ f->atime = f->mtime = time(0);
+ f->length = 0;
+ f->parent = fp;
+ incref(fp);
+ f->tree = fp->tree;
+
+ incref(f); /* being returned */
+ incref(f); /* for the tree */
+ fl->f = f;
+ fp->nchild++;
+ wunlock(fp);
+
+ return f;
+}
+
+static File*
+walkfile1(File *dir, char *elem)
+{
+ File *fp;
+ Filelist *fl;
+
+ rlock(dir);
+ if(strcmp(elem, "..") == 0){
+ fp = dir->parent;
+ incref(fp);
+ runlock(dir);
+ closefile(dir);
+ return fp;
+ }
+
+ fp = nil;
+ for(fl=dir->filelist; fl; fl=fl->link)
+ if(fl->f && strcmp(fl->f->name, elem)==0){
+ fp = fl->f;
+ incref(fp);
+ break;
+ }
+
+ runlock(dir);
+ closefile(dir);
+ return fp;
+}
+
+File*
+walkfile(File *f, char *path)
+{
+ char *os, *s, *nexts;
+
+ if(strchr(path, '/') == nil)
+ return walkfile1(f, path); /* avoid malloc */
+
+ os = s = estrdup9p(path);
+ for(; *s; s=nexts){
+ if(nexts = strchr(s, '/'))
+ *nexts++ = '\0';
+ else
+ nexts = s+strlen(s);
+ f = walkfile1(f, s);
+ 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->name = estrdup9p("/");
+ if(uid == nil){
+ uid = getuser();
+ if(uid == nil)
+ uid = "none";
+ }
+ uid = estrdup9p(uid);
+
+ if(gid == nil)
+ gid = estrdup9p(uid);
+ else
+ gid = estrdup9p(gid);
+
+ muid = estrdup9p(uid);
+
+ f->qid = (Qid){0, 0, QTDIR};
+ f->length = 0;
+ f->atime = f->mtime = time(0);
+ f->mode = DMDIR | mode;
+ f->tree = t;
+ f->parent = f;
+ f->uid = uid;
+ f->gid = gid;
+ f->muid = muid;
+
+ incref(f);
+ 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);
+}
+
+Readdir*
+opendirfile(File *dir)
+{
+ Readdir *r;
+
+ rlock(dir);
+ if((dir->mode & DMDIR)==0){
+ runlock(dir);
+ return nil;
+ }
+ r = emalloc9p(sizeof(*r));
+
+ /*
+ * This reference won't go away while we're
+ * using it because file list entries are not freed
+ * until the directory is removed and all refs to
+ * it (our fid is one!) have gone away.
+ */
+ r->fl = dir->filelist;
+ r->dir = dir;
+ incref(&dir->readers);
+ runlock(dir);
+ 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, buf+m, n-m)) <= BIT16SZ)
+ break;
+ }
+ r->fl = fl;
+ return m;
+}
+
+void
+closedirfile(Readdir *r)
+{
+ if(decref(&r->dir->readers) == 0){
+ wlock(r->dir);
+ cleanfilelist(r->dir);
+ wunlock(r->dir);
+ }
+ free(r);
+}