123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- #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;
- File *nf;
- if(strchr(path, '/') == nil)
- return walkfile1(f, path); /* avoid malloc */
- os = s = estrdup9p(path);
- incref(f);
- for(; *s; s=nexts){
- if(nexts = strchr(s, '/'))
- *nexts++ = '\0';
- else
- nexts = s+strlen(s);
- nf = walkfile1(f, s);
- closefile(f);
- 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->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);
- }
|