17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <auth.h>
47dd7cddfSDavid du Colombier #include <fcall.h>
57dd7cddfSDavid du Colombier #include <thread.h>
69a747e4fSDavid du Colombier #include <9p.h>
77dd7cddfSDavid du Colombier
87dd7cddfSDavid du Colombier /*
97dd7cddfSDavid du Colombier * To avoid deadlock, the following rules must be followed.
107dd7cddfSDavid du Colombier * Always lock child then parent, never parent then child.
117dd7cddfSDavid du Colombier * If holding the free file lock, do not lock any Files.
127dd7cddfSDavid du Colombier */
1388110b43SDavid du Colombier struct Filelist
1488110b43SDavid du Colombier {
157dd7cddfSDavid du Colombier File *f;
167dd7cddfSDavid du Colombier Filelist *link;
177dd7cddfSDavid du Colombier };
187dd7cddfSDavid du Colombier
1988110b43SDavid du Colombier struct Readdir
2088110b43SDavid du Colombier {
2188110b43SDavid du Colombier File *dir;
2288110b43SDavid du Colombier Filelist *fl;
2388110b43SDavid du Colombier };
2488110b43SDavid du Colombier
257dd7cddfSDavid du Colombier static QLock filelk;
267dd7cddfSDavid du Colombier static File *freefilelist;
277dd7cddfSDavid du Colombier
287dd7cddfSDavid du Colombier static File*
allocfile(void)297dd7cddfSDavid du Colombier allocfile(void)
307dd7cddfSDavid du Colombier {
319a747e4fSDavid du Colombier int i, a;
327dd7cddfSDavid du Colombier File *f;
337dd7cddfSDavid du Colombier enum { N = 16 };
347dd7cddfSDavid du Colombier
357dd7cddfSDavid du Colombier qlock(&filelk);
367dd7cddfSDavid du Colombier if(freefilelist == nil){
379a747e4fSDavid du Colombier f = emalloc9p(N*sizeof(*f));
387dd7cddfSDavid du Colombier for(i=0; i<N-1; i++)
397dd7cddfSDavid du Colombier f[i].aux = &f[i+1];
407dd7cddfSDavid du Colombier f[N-1].aux = nil;
419a747e4fSDavid du Colombier f[0].allocd = 1;
427dd7cddfSDavid du Colombier freefilelist = f;
437dd7cddfSDavid du Colombier }
447dd7cddfSDavid du Colombier
457dd7cddfSDavid du Colombier f = freefilelist;
467dd7cddfSDavid du Colombier freefilelist = f->aux;
477dd7cddfSDavid du Colombier qunlock(&filelk);
487dd7cddfSDavid du Colombier
499a747e4fSDavid du Colombier a = f->allocd;
507dd7cddfSDavid du Colombier memset(f, 0, sizeof *f);
519a747e4fSDavid du Colombier f->allocd = a;
527dd7cddfSDavid du Colombier return f;
537dd7cddfSDavid du Colombier }
547dd7cddfSDavid du Colombier
557dd7cddfSDavid du Colombier static void
freefile(File * f)567dd7cddfSDavid du Colombier freefile(File *f)
577dd7cddfSDavid du Colombier {
589a747e4fSDavid du Colombier Filelist *fl, *flnext;
599a747e4fSDavid du Colombier
609a747e4fSDavid du Colombier for(fl=f->filelist; fl; fl=flnext){
619a747e4fSDavid du Colombier flnext = fl->link;
629a747e4fSDavid du Colombier assert(fl->f == nil);
639a747e4fSDavid du Colombier free(fl);
649a747e4fSDavid du Colombier }
659a747e4fSDavid du Colombier
669a747e4fSDavid du Colombier free(f->name);
679a747e4fSDavid du Colombier free(f->uid);
689a747e4fSDavid du Colombier free(f->gid);
699a747e4fSDavid du Colombier free(f->muid);
707dd7cddfSDavid du Colombier qlock(&filelk);
719a747e4fSDavid du Colombier assert(f->ref == 0);
727dd7cddfSDavid du Colombier f->aux = freefilelist;
737dd7cddfSDavid du Colombier freefilelist = f;
747dd7cddfSDavid du Colombier qunlock(&filelk);
757dd7cddfSDavid du Colombier }
767dd7cddfSDavid du Colombier
7788110b43SDavid du Colombier static void
cleanfilelist(File * f)7888110b43SDavid du Colombier cleanfilelist(File *f)
7988110b43SDavid du Colombier {
8088110b43SDavid du Colombier Filelist **l;
8188110b43SDavid du Colombier Filelist *fl;
8288110b43SDavid du Colombier
8388110b43SDavid du Colombier /*
8488110b43SDavid du Colombier * can't delete filelist structures while there
8588110b43SDavid du Colombier * are open readers of this directory, because
8688110b43SDavid du Colombier * they might have references to the structures.
8788110b43SDavid du Colombier * instead, just leave the empty refs in the list
8888110b43SDavid du Colombier * until there is no activity and then clean up.
8988110b43SDavid du Colombier */
9088110b43SDavid du Colombier if(f->readers.ref != 0)
9188110b43SDavid du Colombier return;
9288110b43SDavid du Colombier if(f->nxchild == 0)
9388110b43SDavid du Colombier return;
9488110b43SDavid du Colombier
9588110b43SDavid du Colombier /*
9688110b43SDavid du Colombier * no dir readers, file is locked, and
9788110b43SDavid du Colombier * there are empty entries in the file list.
9888110b43SDavid du Colombier * clean them out.
9988110b43SDavid du Colombier */
10088110b43SDavid du Colombier for(l=&f->filelist; fl=*l; ){
10188110b43SDavid du Colombier if(fl->f == nil){
10288110b43SDavid du Colombier *l = (*l)->link;
10388110b43SDavid du Colombier free(fl);
10488110b43SDavid du Colombier }else
10588110b43SDavid du Colombier l = &(*l)->link;
10688110b43SDavid du Colombier }
10788110b43SDavid du Colombier f->nxchild = 0;
10888110b43SDavid du Colombier }
10988110b43SDavid du Colombier
1109a747e4fSDavid du Colombier void
closefile(File * f)1119a747e4fSDavid du Colombier closefile(File *f)
1127dd7cddfSDavid du Colombier {
1139a747e4fSDavid du Colombier if(decref(f) == 0){
1149a747e4fSDavid du Colombier f->tree->destroy(f);
1157dd7cddfSDavid du Colombier freefile(f);
1167dd7cddfSDavid du Colombier }
1177dd7cddfSDavid du Colombier }
1187dd7cddfSDavid du Colombier
1197dd7cddfSDavid du Colombier static void
nop(File *)1209a747e4fSDavid du Colombier nop(File*)
1217dd7cddfSDavid du Colombier {
1227dd7cddfSDavid du Colombier }
1237dd7cddfSDavid du Colombier
1249a747e4fSDavid du Colombier int
removefile(File * f)1259a747e4fSDavid du Colombier removefile(File *f)
1267dd7cddfSDavid du Colombier {
1279a747e4fSDavid du Colombier File *fp;
1289a747e4fSDavid du Colombier Filelist *fl;
1297dd7cddfSDavid du Colombier
1309a747e4fSDavid du Colombier fp = f->parent;
1319a747e4fSDavid du Colombier if(fp == nil){
1329a747e4fSDavid du Colombier werrstr("no parent");
1339a747e4fSDavid du Colombier closefile(f);
1349a747e4fSDavid du Colombier return -1;
1359a747e4fSDavid du Colombier }
13659cc4ca5SDavid du Colombier
1379a747e4fSDavid du Colombier if(fp == f){
1389a747e4fSDavid du Colombier werrstr("cannot remove root");
1399a747e4fSDavid du Colombier closefile(f);
1409a747e4fSDavid du Colombier return -1;
1419a747e4fSDavid du Colombier }
1427dd7cddfSDavid du Colombier
1439a747e4fSDavid du Colombier wlock(f);
144eb752b91SDavid du Colombier wlock(fp);
1459a747e4fSDavid du Colombier if(f->nchild != 0){
1469a747e4fSDavid du Colombier werrstr("has children");
1479a747e4fSDavid du Colombier wunlock(fp);
148eb752b91SDavid du Colombier wunlock(f);
1499a747e4fSDavid du Colombier closefile(f);
1509a747e4fSDavid du Colombier return -1;
1519a747e4fSDavid du Colombier }
1527dd7cddfSDavid du Colombier
1539a747e4fSDavid du Colombier if(f->parent != fp){
1549a747e4fSDavid du Colombier werrstr("parent changed underfoot");
1559a747e4fSDavid du Colombier wunlock(fp);
156eb752b91SDavid du Colombier wunlock(f);
1579a747e4fSDavid du Colombier closefile(f);
1589a747e4fSDavid du Colombier return -1;
1599a747e4fSDavid du Colombier }
1607dd7cddfSDavid du Colombier
1619a747e4fSDavid du Colombier for(fl=fp->filelist; fl; fl=fl->link)
1629a747e4fSDavid du Colombier if(fl->f == f)
1639a747e4fSDavid du Colombier break;
1649a747e4fSDavid du Colombier assert(fl != nil && fl->f == f);
1659a747e4fSDavid du Colombier
1669a747e4fSDavid du Colombier fl->f = nil;
1679a747e4fSDavid du Colombier fp->nchild--;
16888110b43SDavid du Colombier fp->nxchild++;
1699a747e4fSDavid du Colombier f->parent = nil;
1709a747e4fSDavid du Colombier wunlock(f);
1719a747e4fSDavid du Colombier
17288110b43SDavid du Colombier cleanfilelist(fp);
17388110b43SDavid du Colombier wunlock(fp);
17488110b43SDavid du Colombier
1759a747e4fSDavid du Colombier closefile(fp); /* reference from child */
1769a747e4fSDavid du Colombier closefile(f); /* reference from tree */
1779a747e4fSDavid du Colombier closefile(f);
1789a747e4fSDavid du Colombier return 0;
1797dd7cddfSDavid du Colombier }
1807dd7cddfSDavid du Colombier
1817dd7cddfSDavid du Colombier File*
createfile(File * fp,char * name,char * uid,ulong perm,void * aux)1829a747e4fSDavid du Colombier createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
1837dd7cddfSDavid du Colombier {
1847dd7cddfSDavid du Colombier File *f;
18588110b43SDavid du Colombier Filelist **l, *fl;
1869a747e4fSDavid du Colombier Tree *t;
1877dd7cddfSDavid du Colombier
1889a747e4fSDavid du Colombier if((fp->qid.type&QTDIR) == 0){
1897dd7cddfSDavid du Colombier werrstr("create in non-directory");
1907dd7cddfSDavid du Colombier return nil;
1917dd7cddfSDavid du Colombier }
1927dd7cddfSDavid du Colombier
1939a747e4fSDavid du Colombier wlock(fp);
19488110b43SDavid du Colombier /*
19588110b43SDavid du Colombier * We might encounter blank spots along the
19688110b43SDavid du Colombier * way due to deleted files that have not yet
19788110b43SDavid du Colombier * been flushed from the file list. Don't reuse
19888110b43SDavid du Colombier * those - some apps (e.g., omero) depend on
19988110b43SDavid du Colombier * the file order reflecting creation order.
20088110b43SDavid du Colombier * Always create at the end of the list.
20188110b43SDavid du Colombier */
20288110b43SDavid du Colombier for(l=&fp->filelist; fl=*l; l=&fl->link){
20388110b43SDavid du Colombier if(fl->f && strcmp(fl->f->name, name) == 0){
2049a747e4fSDavid du Colombier wunlock(fp);
2057dd7cddfSDavid du Colombier werrstr("file already exists");
2067dd7cddfSDavid du Colombier return nil;
2077dd7cddfSDavid du Colombier }
2087dd7cddfSDavid du Colombier }
2097dd7cddfSDavid du Colombier
21088110b43SDavid du Colombier fl = emalloc9p(sizeof *fl);
21188110b43SDavid du Colombier *l = fl;
2127dd7cddfSDavid du Colombier
2137dd7cddfSDavid du Colombier f = allocfile();
2149a747e4fSDavid du Colombier f->name = estrdup9p(name);
2159a747e4fSDavid du Colombier f->uid = estrdup9p(uid ? uid : fp->uid);
2169a747e4fSDavid du Colombier f->gid = estrdup9p(fp->gid);
2179a747e4fSDavid du Colombier f->muid = estrdup9p(uid ? uid : "unknown");
2189a747e4fSDavid du Colombier f->aux = aux;
2199a747e4fSDavid du Colombier f->mode = perm;
2207dd7cddfSDavid du Colombier
2219a747e4fSDavid du Colombier t = fp->tree;
2229a747e4fSDavid du Colombier lock(&t->genlock);
2237dd7cddfSDavid du Colombier f->qid.path = t->qidgen++;
2249a747e4fSDavid du Colombier unlock(&t->genlock);
2259a747e4fSDavid du Colombier if(perm & DMDIR)
2269a747e4fSDavid du Colombier f->qid.type |= QTDIR;
2279a747e4fSDavid du Colombier if(perm & DMAPPEND)
2289a747e4fSDavid du Colombier f->qid.type |= QTAPPEND;
2299a747e4fSDavid du Colombier if(perm & DMEXCL)
2309a747e4fSDavid du Colombier f->qid.type |= QTEXCL;
2317dd7cddfSDavid du Colombier
2329a747e4fSDavid du Colombier f->mode = perm;
2337dd7cddfSDavid du Colombier f->atime = f->mtime = time(0);
2347dd7cddfSDavid du Colombier f->length = 0;
2359a747e4fSDavid du Colombier f->parent = fp;
2369a747e4fSDavid du Colombier incref(fp);
2379a747e4fSDavid du Colombier f->tree = fp->tree;
2387dd7cddfSDavid du Colombier
2399a747e4fSDavid du Colombier incref(f); /* being returned */
2409a747e4fSDavid du Colombier incref(f); /* for the tree */
24188110b43SDavid du Colombier fl->f = f;
2429a747e4fSDavid du Colombier fp->nchild++;
2439a747e4fSDavid du Colombier wunlock(fp);
2447dd7cddfSDavid du Colombier
2457dd7cddfSDavid du Colombier return f;
2467dd7cddfSDavid du Colombier }
2477dd7cddfSDavid du Colombier
2489a747e4fSDavid du Colombier static File*
walkfile1(File * dir,char * elem)2499a747e4fSDavid du Colombier walkfile1(File *dir, char *elem)
2507dd7cddfSDavid du Colombier {
2519a747e4fSDavid du Colombier File *fp;
2527dd7cddfSDavid du Colombier Filelist *fl;
2537dd7cddfSDavid du Colombier
2549a747e4fSDavid du Colombier rlock(dir);
2559a747e4fSDavid du Colombier if(strcmp(elem, "..") == 0){
2569a747e4fSDavid du Colombier fp = dir->parent;
2579a747e4fSDavid du Colombier incref(fp);
2589a747e4fSDavid du Colombier runlock(dir);
2599a747e4fSDavid du Colombier closefile(dir);
2609a747e4fSDavid du Colombier return fp;
2617dd7cddfSDavid du Colombier }
2627dd7cddfSDavid du Colombier
2639a747e4fSDavid du Colombier fp = nil;
2649a747e4fSDavid du Colombier for(fl=dir->filelist; fl; fl=fl->link)
2659a747e4fSDavid du Colombier if(fl->f && strcmp(fl->f->name, elem)==0){
2669a747e4fSDavid du Colombier fp = fl->f;
2679a747e4fSDavid du Colombier incref(fp);
2687dd7cddfSDavid du Colombier break;
2697dd7cddfSDavid du Colombier }
2707dd7cddfSDavid du Colombier
2719a747e4fSDavid du Colombier runlock(dir);
2729a747e4fSDavid du Colombier closefile(dir);
2739a747e4fSDavid du Colombier return fp;
2747dd7cddfSDavid du Colombier }
2757dd7cddfSDavid du Colombier
2767dd7cddfSDavid du Colombier File*
walkfile(File * f,char * path)2779a747e4fSDavid du Colombier walkfile(File *f, char *path)
2787dd7cddfSDavid du Colombier {
2799a747e4fSDavid du Colombier char *os, *s, *nexts;
2807dd7cddfSDavid du Colombier
2819a747e4fSDavid du Colombier if(strchr(path, '/') == nil)
2829a747e4fSDavid du Colombier return walkfile1(f, path); /* avoid malloc */
2839a747e4fSDavid du Colombier
2849a747e4fSDavid du Colombier os = s = estrdup9p(path);
2859a747e4fSDavid du Colombier for(; *s; s=nexts){
2869a747e4fSDavid du Colombier if(nexts = strchr(s, '/'))
2879a747e4fSDavid du Colombier *nexts++ = '\0';
2889a747e4fSDavid du Colombier else
2899a747e4fSDavid du Colombier nexts = s+strlen(s);
290*2cba34c7SDavid du Colombier f = walkfile1(f, s);
2919a747e4fSDavid du Colombier if(f == nil)
2929a747e4fSDavid du Colombier break;
2939a747e4fSDavid du Colombier }
2949a747e4fSDavid du Colombier free(os);
2957dd7cddfSDavid du Colombier return f;
2967dd7cddfSDavid du Colombier }
2977dd7cddfSDavid du Colombier
2989a747e4fSDavid du Colombier Tree*
alloctree(char * uid,char * gid,ulong mode,void (* destroy)(File *))2999a747e4fSDavid du Colombier alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
3007dd7cddfSDavid du Colombier {
3019a747e4fSDavid du Colombier char *muid;
3029a747e4fSDavid du Colombier Tree *t;
3039a747e4fSDavid du Colombier File *f;
3047dd7cddfSDavid du Colombier
3059a747e4fSDavid du Colombier t = emalloc9p(sizeof *t);
3069a747e4fSDavid du Colombier f = allocfile();
3079a747e4fSDavid du Colombier f->name = estrdup9p("/");
3089a747e4fSDavid du Colombier if(uid == nil){
30973e742d7SDavid du Colombier uid = getuser();
3109a747e4fSDavid du Colombier if(uid == nil)
31173e742d7SDavid du Colombier uid = "none";
31273e742d7SDavid du Colombier }
3139a747e4fSDavid du Colombier uid = estrdup9p(uid);
3149a747e4fSDavid du Colombier
3159a747e4fSDavid du Colombier if(gid == nil)
3169a747e4fSDavid du Colombier gid = estrdup9p(uid);
3179a747e4fSDavid du Colombier else
3189a747e4fSDavid du Colombier gid = estrdup9p(gid);
3199a747e4fSDavid du Colombier
3209a747e4fSDavid du Colombier muid = estrdup9p(uid);
3219a747e4fSDavid du Colombier
3229a747e4fSDavid du Colombier f->qid = (Qid){0, 0, QTDIR};
3239a747e4fSDavid du Colombier f->length = 0;
3249a747e4fSDavid du Colombier f->atime = f->mtime = time(0);
3259a747e4fSDavid du Colombier f->mode = DMDIR | mode;
3269a747e4fSDavid du Colombier f->tree = t;
3279a747e4fSDavid du Colombier f->parent = f;
3289a747e4fSDavid du Colombier f->uid = uid;
3299a747e4fSDavid du Colombier f->gid = gid;
3309a747e4fSDavid du Colombier f->muid = muid;
3319a747e4fSDavid du Colombier
3329a747e4fSDavid du Colombier incref(f);
3339a747e4fSDavid du Colombier t->root = f;
3349a747e4fSDavid du Colombier t->qidgen = 0;
3359a747e4fSDavid du Colombier t->dirqidgen = 1;
3369a747e4fSDavid du Colombier if(destroy == nil)
3379a747e4fSDavid du Colombier destroy = nop;
3389a747e4fSDavid du Colombier t->destroy = destroy;
3399a747e4fSDavid du Colombier
3409a747e4fSDavid du Colombier return t;
3417dd7cddfSDavid du Colombier }
3429a747e4fSDavid du Colombier
3439a747e4fSDavid du Colombier static void
_freefiles(File * f)3449a747e4fSDavid du Colombier _freefiles(File *f)
3459a747e4fSDavid du Colombier {
3469a747e4fSDavid du Colombier Filelist *fl, *flnext;
3479a747e4fSDavid du Colombier
3489a747e4fSDavid du Colombier for(fl=f->filelist; fl; fl=flnext){
3499a747e4fSDavid du Colombier flnext = fl->link;
3509a747e4fSDavid du Colombier _freefiles(fl->f);
3519a747e4fSDavid du Colombier free(fl);
3527dd7cddfSDavid du Colombier }
3539a747e4fSDavid du Colombier
3549a747e4fSDavid du Colombier f->tree->destroy(f);
3559a747e4fSDavid du Colombier freefile(f);
3569a747e4fSDavid du Colombier }
3579a747e4fSDavid du Colombier
3589a747e4fSDavid du Colombier void
freetree(Tree * t)3599a747e4fSDavid du Colombier freetree(Tree *t)
3609a747e4fSDavid du Colombier {
3619a747e4fSDavid du Colombier _freefiles(t->root);
3629a747e4fSDavid du Colombier free(t);
3639a747e4fSDavid du Colombier }
3649a747e4fSDavid du Colombier
3659a747e4fSDavid du Colombier Readdir*
opendirfile(File * dir)3669a747e4fSDavid du Colombier opendirfile(File *dir)
3679a747e4fSDavid du Colombier {
3689a747e4fSDavid du Colombier Readdir *r;
3699a747e4fSDavid du Colombier
3709a747e4fSDavid du Colombier rlock(dir);
3719a747e4fSDavid du Colombier if((dir->mode & DMDIR)==0){
3729a747e4fSDavid du Colombier runlock(dir);
3737dd7cddfSDavid du Colombier return nil;
3747dd7cddfSDavid du Colombier }
3759a747e4fSDavid du Colombier r = emalloc9p(sizeof(*r));
3769a747e4fSDavid du Colombier
3779a747e4fSDavid du Colombier /*
37888110b43SDavid du Colombier * This reference won't go away while we're
37988110b43SDavid du Colombier * using it because file list entries are not freed
38088110b43SDavid du Colombier * until the directory is removed and all refs to
38188110b43SDavid du Colombier * it (our fid is one!) have gone away.
3829a747e4fSDavid du Colombier */
3839a747e4fSDavid du Colombier r->fl = dir->filelist;
38488110b43SDavid du Colombier r->dir = dir;
38588110b43SDavid du Colombier incref(&dir->readers);
3869a747e4fSDavid du Colombier runlock(dir);
3879a747e4fSDavid du Colombier return r;
3889a747e4fSDavid du Colombier }
3899a747e4fSDavid du Colombier
3909a747e4fSDavid du Colombier long
readdirfile(Readdir * r,uchar * buf,long n)3919a747e4fSDavid du Colombier readdirfile(Readdir *r, uchar *buf, long n)
3929a747e4fSDavid du Colombier {
3939a747e4fSDavid du Colombier long x, m;
3949a747e4fSDavid du Colombier Filelist *fl;
3959a747e4fSDavid du Colombier
3969a747e4fSDavid du Colombier for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){
3979a747e4fSDavid du Colombier if(fl->f == nil)
3989a747e4fSDavid du Colombier x = 0;
3999a747e4fSDavid du Colombier else if((x=convD2M(fl->f, buf+m, n-m)) <= BIT16SZ)
4009a747e4fSDavid du Colombier break;
4019a747e4fSDavid du Colombier }
4029a747e4fSDavid du Colombier r->fl = fl;
4039a747e4fSDavid du Colombier return m;
4049a747e4fSDavid du Colombier }
4059a747e4fSDavid du Colombier
4069a747e4fSDavid du Colombier void
closedirfile(Readdir * r)4079a747e4fSDavid du Colombier closedirfile(Readdir *r)
4089a747e4fSDavid du Colombier {
40988110b43SDavid du Colombier if(decref(&r->dir->readers) == 0){
41088110b43SDavid du Colombier wlock(r->dir);
41188110b43SDavid du Colombier cleanfilelist(r->dir);
41288110b43SDavid du Colombier wunlock(r->dir);
41388110b43SDavid du Colombier }
4149a747e4fSDavid du Colombier free(r);
4159a747e4fSDavid du Colombier }
416