URI:
       tfile.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tfile.c (5985B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <fcall.h>
            4 #include <thread.h>
            5 #include <9p.h>
            6 
            7 /*
            8  * To avoid deadlock, the following rules must be followed.
            9  * Always lock child then parent, never parent then child.
           10  * If holding the free file lock, do not lock any Files.
           11  */
           12 struct Filelist {
           13         File *f;
           14         Filelist *link;
           15 };
           16 
           17 static QLock filelk;
           18 static File *freefilelist;
           19 
           20 static File*
           21 allocfile(void)
           22 {
           23         int i, a;
           24         File *f;
           25         enum { N = 16 };
           26 
           27         qlock(&filelk);
           28         if(freefilelist == nil){
           29                 f = emalloc9p(N*sizeof(*f));
           30                 for(i=0; i<N-1; i++)
           31                         f[i].aux = &f[i+1];
           32                 f[N-1].aux = nil;
           33                 f[0].allocd = 1;
           34                 freefilelist = f;
           35         }
           36 
           37         f = freefilelist;
           38         freefilelist = f->aux;
           39         qunlock(&filelk);
           40 
           41         a = f->allocd;
           42         memset(f, 0, sizeof *f);
           43         f->allocd = a;
           44         return f;
           45 }
           46 
           47 static void
           48 freefile(File *f)
           49 {
           50         Filelist *fl, *flnext;
           51 
           52         for(fl=f->filelist; fl; fl=flnext){
           53                 flnext = fl->link;
           54                 assert(fl->f == nil);
           55                 free(fl);
           56         }
           57 
           58         free(f->dir.name);
           59         free(f->dir.uid);
           60         free(f->dir.gid);
           61         free(f->dir.muid);
           62         qlock(&filelk);
           63         assert(f->ref.ref == 0);
           64         f->aux = freefilelist;
           65         freefilelist = f;
           66         qunlock(&filelk);
           67 }
           68 
           69 void
           70 closefile(File *f)
           71 {
           72         if(decref(&f->ref) == 0){
           73                 f->tree->destroy(f);
           74                 freefile(f);
           75         }
           76 }
           77 
           78 static void
           79 nop(File *f)
           80 {
           81         USED(f);
           82 }
           83 
           84 int
           85 removefile(File *f)
           86 {
           87         File *fp;
           88         Filelist *fl;
           89 
           90         fp = f->parent;
           91         if(fp == nil){
           92                 werrstr("no parent");
           93                 closefile(f);
           94                 return -1;
           95         }
           96 
           97         if(fp == f){
           98                 werrstr("cannot remove root");
           99                 closefile(f);
          100                 return -1;
          101         }
          102 
          103         wlock(&fp->rwlock);
          104         wlock(&f->rwlock);
          105         if(f->nchild != 0){
          106                 werrstr("has children");
          107                 wunlock(&f->rwlock);
          108                 wunlock(&fp->rwlock);
          109                 closefile(f);
          110                 return -1;
          111         }
          112 
          113         if(f->parent != fp){
          114                 werrstr("parent changed underfoot");
          115                 wunlock(&f->rwlock);
          116                 wunlock(&fp->rwlock);
          117                 closefile(f);
          118                 return -1;
          119         }
          120 
          121         for(fl=fp->filelist; fl; fl=fl->link)
          122                 if(fl->f == f)
          123                         break;
          124         assert(fl != nil && fl->f == f);
          125 
          126         fl->f = nil;
          127         fp->nchild--;
          128         f->parent = nil;
          129         wunlock(&fp->rwlock);
          130         wunlock(&f->rwlock);
          131 
          132         closefile(fp);        /* reference from child */
          133         closefile(f);        /* reference from tree */
          134         closefile(f);
          135         return 0;
          136 }
          137 
          138 File*
          139 createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
          140 {
          141         File *f;
          142         Filelist *fl, *freel;
          143         Tree *t;
          144 
          145         if((fp->dir.qid.type&QTDIR) == 0){
          146                 werrstr("create in non-directory");
          147                 return nil;
          148         }
          149 
          150         freel = nil;
          151         wlock(&fp->rwlock);
          152         for(fl=fp->filelist; fl; fl=fl->link){
          153                 if(fl->f == nil)
          154                         freel = fl;
          155                 else if(strcmp(fl->f->dir.name, name) == 0){
          156                         wunlock(&fp->rwlock);
          157                         werrstr("file already exists");
          158                         return nil;
          159                 }
          160         }
          161 
          162         if(freel == nil){
          163                 freel = emalloc9p(sizeof *freel);
          164                 freel->link = fp->filelist;
          165                 fp->filelist = freel;
          166         }
          167 
          168         f = allocfile();
          169         f->dir.name = estrdup9p(name);
          170         f->dir.uid = estrdup9p(uid ? uid : fp->dir.uid);
          171         f->dir.gid = estrdup9p(fp->dir.gid);
          172         f->dir.muid = estrdup9p(uid ? uid : "unknown");
          173         f->aux = aux;
          174         f->dir.mode = perm;
          175 
          176         t = fp->tree;
          177         lock(&t->genlock);
          178         f->dir.qid.path = t->qidgen++;
          179         unlock(&t->genlock);
          180         if(perm & DMDIR)
          181                 f->dir.qid.type |= QTDIR;
          182         if(perm & DMAPPEND)
          183                 f->dir.qid.type |= QTAPPEND;
          184         if(perm & DMEXCL)
          185                 f->dir.qid.type |= QTEXCL;
          186 
          187         f->dir.mode = perm;
          188         f->dir.atime = f->dir.mtime = time(0);
          189         f->dir.length = 0;
          190         f->parent = fp;
          191         incref(&fp->ref);
          192         f->tree = fp->tree;
          193 
          194         incref(&f->ref);        /* being returned */
          195         incref(&f->ref);        /* for the tree */
          196         freel->f = f;
          197         fp->nchild++;
          198         wunlock(&fp->rwlock);
          199 
          200         return f;
          201 }
          202 
          203 static File*
          204 walkfile1(File *dir, char *elem)
          205 {
          206         File *fp;
          207         Filelist *fl;
          208 
          209         rlock(&dir->rwlock);
          210         if(strcmp(elem, "..") == 0){
          211                 fp = dir->parent;
          212                 incref(&fp->ref);
          213                 runlock(&dir->rwlock);
          214                 closefile(dir);
          215                 return fp;
          216         }
          217 
          218         fp = nil;
          219         for(fl=dir->filelist; fl; fl=fl->link)
          220                 if(fl->f && strcmp(fl->f->dir.name, elem)==0){
          221                         fp = fl->f;
          222                         incref(&fp->ref);
          223                         break;
          224                 }
          225 
          226         runlock(&dir->rwlock);
          227         closefile(dir);
          228         return fp;
          229 }
          230 
          231 File*
          232 walkfile(File *f, char *path)
          233 {
          234         char *os, *s, *nexts;
          235 
          236         if(strchr(path, '/') == nil)
          237                 return walkfile1(f, path);        /* avoid malloc */
          238 
          239         os = s = estrdup9p(path);
          240         for(; *s; s=nexts){
          241                 if(nexts = strchr(s, '/'))
          242                         *nexts++ = '\0';
          243                 else
          244                         nexts = s+strlen(s);
          245                 f = walkfile1(f, s);
          246                 if(f == nil)
          247                         break;
          248         }
          249         free(os);
          250         return f;
          251 }
          252 
          253 static Qid
          254 mkqid(vlong path, long vers, int type)
          255 {
          256         Qid q;
          257 
          258         q.path = path;
          259         q.vers = vers;
          260         q.type = type;
          261         return q;
          262 }
          263 
          264 
          265 Tree*
          266 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
          267 {
          268         char *muid;
          269         Tree *t;
          270         File *f;
          271 
          272         t = emalloc9p(sizeof *t);
          273         f = allocfile();
          274         f->dir.name = estrdup9p("/");
          275         if(uid == nil){
          276                 if(uid = getuser())
          277                         uid = estrdup9p(uid);
          278         }
          279         if(uid == nil)
          280                 uid = estrdup9p("none");
          281         else
          282                 uid = estrdup9p(uid);
          283 
          284         if(gid == nil)
          285                 gid = estrdup9p(uid);
          286         else
          287                 gid = estrdup9p(gid);
          288 
          289         muid = estrdup9p(uid);
          290 
          291         f->dir.qid = mkqid(0, 0, QTDIR);
          292         f->dir.length = 0;
          293         f->dir.atime = f->dir.mtime = time(0);
          294         f->dir.mode = DMDIR | mode;
          295         f->tree = t;
          296         f->parent = f;
          297         f->dir.uid = uid;
          298         f->dir.gid = gid;
          299         f->dir.muid = muid;
          300 
          301         incref(&f->ref);
          302         t->root = f;
          303         t->qidgen = 0;
          304         t->dirqidgen = 1;
          305         if(destroy == nil)
          306                 destroy = nop;
          307         t->destroy = destroy;
          308 
          309         return t;
          310 }
          311 
          312 static void
          313 _freefiles(File *f)
          314 {
          315         Filelist *fl, *flnext;
          316 
          317         for(fl=f->filelist; fl; fl=flnext){
          318                 flnext = fl->link;
          319                 _freefiles(fl->f);
          320                 free(fl);
          321         }
          322 
          323         f->tree->destroy(f);
          324         freefile(f);
          325 }
          326 
          327 void
          328 freetree(Tree *t)
          329 {
          330         _freefiles(t->root);
          331         free(t);
          332 }
          333 
          334 struct Readdir {
          335         Filelist *fl;
          336 };
          337 
          338 Readdir*
          339 opendirfile(File *dir)
          340 {
          341         Readdir *r;
          342 
          343         rlock(&dir->rwlock);
          344         if((dir->dir.mode & DMDIR)==0){
          345                 runlock(&dir->rwlock);
          346                 return nil;
          347         }
          348         r = emalloc9p(sizeof(*r));
          349 
          350         /*
          351          * This reference won't go away while we're using it
          352          * since we are dir->rdir.
          353          */
          354         r->fl = dir->filelist;
          355         runlock(&dir->rwlock);
          356         return r;
          357 }
          358 
          359 long
          360 readdirfile(Readdir *r, uchar *buf, long n)
          361 {
          362         long x, m;
          363         Filelist *fl;
          364 
          365         for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){
          366                 if(fl->f == nil)
          367                         x = 0;
          368                 else if((x=convD2M(&fl->f->dir, buf+m, n-m)) <= BIT16SZ)
          369                         break;
          370         }
          371         r->fl = fl;
          372         return m;
          373 }
          374 
          375 void
          376 closedirfile(Readdir *r)
          377 {
          378         free(r);
          379 }