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