aboutsummaryrefslogtreecommitdiff
path: root/src/lib9p/file.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-03-21 04:33:13 +0000
committerrsc <devnull@localhost>2004-03-21 04:33:13 +0000
commit2277c5d7bbe1f9595fad512d8f790708473a9bf1 (patch)
tree4d653e13906f1971d3170dba6dbe0fbf92eb48d6 /src/lib9p/file.c
parenta770daa795754cb600ad3fab2fdd2961147006c4 (diff)
downloadplan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.tar.gz
plan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.tar.bz2
plan9port-2277c5d7bbe1f9595fad512d8f790708473a9bf1.zip
Small tweaks
Lots of new code imported.
Diffstat (limited to 'src/lib9p/file.c')
-rw-r--r--src/lib9p/file.c372
1 files changed, 372 insertions, 0 deletions
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 <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;
+};
+
+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->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);
+}