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* 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 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 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 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 1209a747e4fSDavid du Colombier nop(File*) 1217dd7cddfSDavid du Colombier { 1227dd7cddfSDavid du Colombier } 1237dd7cddfSDavid du Colombier 1249a747e4fSDavid du Colombier int 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* 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* 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* 2779a747e4fSDavid du Colombier walkfile(File *f, char *path) 2787dd7cddfSDavid du Colombier { 2799a747e4fSDavid du Colombier char *os, *s, *nexts; 2809a747e4fSDavid du Colombier File *nf; 2817dd7cddfSDavid du Colombier 2829a747e4fSDavid du Colombier if(strchr(path, '/') == nil) 2839a747e4fSDavid du Colombier return walkfile1(f, path); /* avoid malloc */ 2849a747e4fSDavid du Colombier 2859a747e4fSDavid du Colombier os = s = estrdup9p(path); 2869a747e4fSDavid du Colombier incref(f); 2879a747e4fSDavid du Colombier for(; *s; s=nexts){ 2889a747e4fSDavid du Colombier if(nexts = strchr(s, '/')) 2899a747e4fSDavid du Colombier *nexts++ = '\0'; 2909a747e4fSDavid du Colombier else 2919a747e4fSDavid du Colombier nexts = s+strlen(s); 2929a747e4fSDavid du Colombier nf = walkfile1(f, s); 29388110b43SDavid du Colombier closefile(f); 2949a747e4fSDavid du Colombier f = nf; 2959a747e4fSDavid du Colombier if(f == nil) 2969a747e4fSDavid du Colombier break; 2979a747e4fSDavid du Colombier } 2989a747e4fSDavid du Colombier free(os); 2997dd7cddfSDavid du Colombier return f; 3007dd7cddfSDavid du Colombier } 3017dd7cddfSDavid du Colombier 3029a747e4fSDavid du Colombier Tree* 3039a747e4fSDavid du Colombier alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*)) 3047dd7cddfSDavid du Colombier { 3059a747e4fSDavid du Colombier char *muid; 3069a747e4fSDavid du Colombier Tree *t; 3079a747e4fSDavid du Colombier File *f; 3087dd7cddfSDavid du Colombier 3099a747e4fSDavid du Colombier t = emalloc9p(sizeof *t); 3109a747e4fSDavid du Colombier f = allocfile(); 3119a747e4fSDavid du Colombier f->name = estrdup9p("/"); 3129a747e4fSDavid du Colombier if(uid == nil){ 313*73e742d7SDavid du Colombier uid = getuser(); 3149a747e4fSDavid du Colombier if(uid == nil) 315*73e742d7SDavid du Colombier uid = "none"; 316*73e742d7SDavid du Colombier } 3179a747e4fSDavid du Colombier uid = estrdup9p(uid); 3189a747e4fSDavid du Colombier 3199a747e4fSDavid du Colombier if(gid == nil) 3209a747e4fSDavid du Colombier gid = estrdup9p(uid); 3219a747e4fSDavid du Colombier else 3229a747e4fSDavid du Colombier gid = estrdup9p(gid); 3239a747e4fSDavid du Colombier 3249a747e4fSDavid du Colombier muid = estrdup9p(uid); 3259a747e4fSDavid du Colombier 3269a747e4fSDavid du Colombier f->qid = (Qid){0, 0, QTDIR}; 3279a747e4fSDavid du Colombier f->length = 0; 3289a747e4fSDavid du Colombier f->atime = f->mtime = time(0); 3299a747e4fSDavid du Colombier f->mode = DMDIR | mode; 3309a747e4fSDavid du Colombier f->tree = t; 3319a747e4fSDavid du Colombier f->parent = f; 3329a747e4fSDavid du Colombier f->uid = uid; 3339a747e4fSDavid du Colombier f->gid = gid; 3349a747e4fSDavid du Colombier f->muid = muid; 3359a747e4fSDavid du Colombier 3369a747e4fSDavid du Colombier incref(f); 3379a747e4fSDavid du Colombier t->root = f; 3389a747e4fSDavid du Colombier t->qidgen = 0; 3399a747e4fSDavid du Colombier t->dirqidgen = 1; 3409a747e4fSDavid du Colombier if(destroy == nil) 3419a747e4fSDavid du Colombier destroy = nop; 3429a747e4fSDavid du Colombier t->destroy = destroy; 3439a747e4fSDavid du Colombier 3449a747e4fSDavid du Colombier return t; 3457dd7cddfSDavid du Colombier } 3469a747e4fSDavid du Colombier 3479a747e4fSDavid du Colombier static void 3489a747e4fSDavid du Colombier _freefiles(File *f) 3499a747e4fSDavid du Colombier { 3509a747e4fSDavid du Colombier Filelist *fl, *flnext; 3519a747e4fSDavid du Colombier 3529a747e4fSDavid du Colombier for(fl=f->filelist; fl; fl=flnext){ 3539a747e4fSDavid du Colombier flnext = fl->link; 3549a747e4fSDavid du Colombier _freefiles(fl->f); 3559a747e4fSDavid du Colombier free(fl); 3567dd7cddfSDavid du Colombier } 3579a747e4fSDavid du Colombier 3589a747e4fSDavid du Colombier f->tree->destroy(f); 3599a747e4fSDavid du Colombier freefile(f); 3609a747e4fSDavid du Colombier } 3619a747e4fSDavid du Colombier 3629a747e4fSDavid du Colombier void 3639a747e4fSDavid du Colombier freetree(Tree *t) 3649a747e4fSDavid du Colombier { 3659a747e4fSDavid du Colombier _freefiles(t->root); 3669a747e4fSDavid du Colombier free(t); 3679a747e4fSDavid du Colombier } 3689a747e4fSDavid du Colombier 3699a747e4fSDavid du Colombier Readdir* 3709a747e4fSDavid du Colombier opendirfile(File *dir) 3719a747e4fSDavid du Colombier { 3729a747e4fSDavid du Colombier Readdir *r; 3739a747e4fSDavid du Colombier 3749a747e4fSDavid du Colombier rlock(dir); 3759a747e4fSDavid du Colombier if((dir->mode & DMDIR)==0){ 3769a747e4fSDavid du Colombier runlock(dir); 3777dd7cddfSDavid du Colombier return nil; 3787dd7cddfSDavid du Colombier } 3799a747e4fSDavid du Colombier r = emalloc9p(sizeof(*r)); 3809a747e4fSDavid du Colombier 3819a747e4fSDavid du Colombier /* 38288110b43SDavid du Colombier * This reference won't go away while we're 38388110b43SDavid du Colombier * using it because file list entries are not freed 38488110b43SDavid du Colombier * until the directory is removed and all refs to 38588110b43SDavid du Colombier * it (our fid is one!) have gone away. 3869a747e4fSDavid du Colombier */ 3879a747e4fSDavid du Colombier r->fl = dir->filelist; 38888110b43SDavid du Colombier r->dir = dir; 38988110b43SDavid du Colombier incref(&dir->readers); 3909a747e4fSDavid du Colombier runlock(dir); 3919a747e4fSDavid du Colombier return r; 3929a747e4fSDavid du Colombier } 3939a747e4fSDavid du Colombier 3949a747e4fSDavid du Colombier long 3959a747e4fSDavid du Colombier readdirfile(Readdir *r, uchar *buf, long n) 3969a747e4fSDavid du Colombier { 3979a747e4fSDavid du Colombier long x, m; 3989a747e4fSDavid du Colombier Filelist *fl; 3999a747e4fSDavid du Colombier 4009a747e4fSDavid du Colombier for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){ 4019a747e4fSDavid du Colombier if(fl->f == nil) 4029a747e4fSDavid du Colombier x = 0; 4039a747e4fSDavid du Colombier else if((x=convD2M(fl->f, buf+m, n-m)) <= BIT16SZ) 4049a747e4fSDavid du Colombier break; 4059a747e4fSDavid du Colombier } 4069a747e4fSDavid du Colombier r->fl = fl; 4079a747e4fSDavid du Colombier return m; 4089a747e4fSDavid du Colombier } 4099a747e4fSDavid du Colombier 4109a747e4fSDavid du Colombier void 4119a747e4fSDavid du Colombier closedirfile(Readdir *r) 4129a747e4fSDavid du Colombier { 41388110b43SDavid du Colombier if(decref(&r->dir->readers) == 0){ 41488110b43SDavid du Colombier wlock(r->dir); 41588110b43SDavid du Colombier cleanfilelist(r->dir); 41688110b43SDavid du Colombier wunlock(r->dir); 41788110b43SDavid du Colombier } 4189a747e4fSDavid du Colombier free(r); 4199a747e4fSDavid du Colombier } 420