Blob
1 #include <u.h>2 #include <libc.h>3 #include <fcall.h>4 #include <thread.h>5 #include <9p.h>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 };17 static QLock filelk;18 static File *freefilelist;20 static File*21 allocfile(void)22 {23 int i, a;24 File *f;25 enum { N = 16 };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 }37 f = freefilelist;38 freefilelist = f->aux;39 qunlock(&filelk);41 a = f->allocd;42 memset(f, 0, sizeof *f);43 f->allocd = a;44 return f;45 }47 static void48 freefile(File *f)49 {50 Filelist *fl, *flnext;52 for(fl=f->filelist; fl; fl=flnext){53 flnext = fl->link;54 assert(fl->f == nil);55 free(fl);56 }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 }69 void70 closefile(File *f)71 {72 if(decref(&f->ref) == 0){73 f->tree->destroy(f);74 freefile(f);75 }76 }78 static void79 nop(File *f)80 {81 USED(f);82 }84 int85 removefile(File *f)86 {87 File *fp;88 Filelist *fl;90 fp = f->parent;91 if(fp == nil){92 werrstr("no parent");93 closefile(f);94 return -1;95 }97 if(fp == f){98 werrstr("cannot remove root");99 closefile(f);100 return -1;101 }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 }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 }121 for(fl=fp->filelist; fl; fl=fl->link)122 if(fl->f == f)123 break;124 assert(fl != nil && fl->f == f);126 fl->f = nil;127 fp->nchild--;128 f->parent = nil;129 wunlock(&fp->rwlock);130 wunlock(&f->rwlock);132 closefile(fp); /* reference from child */133 closefile(f); /* reference from tree */134 closefile(f);135 return 0;136 }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;145 if((fp->dir.qid.type&QTDIR) == 0){146 werrstr("create in non-directory");147 return nil;148 }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 }162 if(freel == nil){163 freel = emalloc9p(sizeof *freel);164 freel->link = fp->filelist;165 fp->filelist = freel;166 }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;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;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;194 incref(&f->ref); /* being returned */195 incref(&f->ref); /* for the tree */196 freel->f = f;197 fp->nchild++;198 wunlock(&fp->rwlock);200 return f;201 }203 static File*204 walkfile1(File *dir, char *elem)205 {206 File *fp;207 Filelist *fl;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 }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 }226 runlock(&dir->rwlock);227 closefile(dir);228 return fp;229 }231 File*232 walkfile(File *f, char *path)233 {234 char *os, *s, *nexts;236 if(strchr(path, '/') == nil)237 return walkfile1(f, path); /* avoid malloc */239 os = s = estrdup9p(path);240 for(; *s; s=nexts){241 if(nexts = strchr(s, '/'))242 *nexts++ = '\0';243 else244 nexts = s+strlen(s);245 f = walkfile1(f, s);246 if(f == nil)247 break;248 }249 free(os);250 return f;251 }253 static Qid254 mkqid(vlong path, long vers, int type)255 {256 Qid q;258 q.path = path;259 q.vers = vers;260 q.type = type;261 return q;262 }265 Tree*266 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))267 {268 char *muid;269 Tree *t;270 File *f;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 else282 uid = estrdup9p(uid);284 if(gid == nil)285 gid = estrdup9p(uid);286 else287 gid = estrdup9p(gid);289 muid = estrdup9p(uid);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;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;309 return t;310 }312 static void313 _freefiles(File *f)314 {315 Filelist *fl, *flnext;317 for(fl=f->filelist; fl; fl=flnext){318 flnext = fl->link;319 _freefiles(fl->f);320 free(fl);321 }323 f->tree->destroy(f);324 freefile(f);325 }327 void328 freetree(Tree *t)329 {330 _freefiles(t->root);331 free(t);332 }334 struct Readdir {335 Filelist *fl;336 };338 Readdir*339 opendirfile(File *dir)340 {341 Readdir *r;343 rlock(&dir->rwlock);344 if((dir->dir.mode & DMDIR)==0){345 runlock(&dir->rwlock);346 return nil;347 }348 r = emalloc9p(sizeof(*r));350 /*351 * This reference won't go away while we're using it352 * since we are dir->rdir.353 */354 r->fl = dir->filelist;355 runlock(&dir->rwlock);356 return r;357 }359 long360 readdirfile(Readdir *r, uchar *buf, long n)361 {362 long x, m;363 Filelist *fl;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 }375 void376 closedirfile(Readdir *r)377 {378 free(r);379 }