1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <thread.h> 6 #include <9p.h> 7 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 { 15 File *f; 16 Filelist *link; 17 }; 18 19 struct Readdir 20 { 21 File *dir; 22 Filelist *fl; 23 }; 24 25 static QLock filelk; 26 static File *freefilelist; 27 28 static File* 29 allocfile(void) 30 { 31 int i, a; 32 File *f; 33 enum { N = 16 }; 34 35 qlock(&filelk); 36 if(freefilelist == nil){ 37 f = emalloc9p(N*sizeof(*f)); 38 for(i=0; i<N-1; i++) 39 f[i].aux = &f[i+1]; 40 f[N-1].aux = nil; 41 f[0].allocd = 1; 42 freefilelist = f; 43 } 44 45 f = freefilelist; 46 freefilelist = f->aux; 47 qunlock(&filelk); 48 49 a = f->allocd; 50 memset(f, 0, sizeof *f); 51 f->allocd = a; 52 return f; 53 } 54 55 static void 56 freefile(File *f) 57 { 58 Filelist *fl, *flnext; 59 60 for(fl=f->filelist; fl; fl=flnext){ 61 flnext = fl->link; 62 assert(fl->f == nil); 63 free(fl); 64 } 65 66 free(f->name); 67 free(f->uid); 68 free(f->gid); 69 free(f->muid); 70 qlock(&filelk); 71 assert(f->ref == 0); 72 f->aux = freefilelist; 73 freefilelist = f; 74 qunlock(&filelk); 75 } 76 77 static void 78 cleanfilelist(File *f) 79 { 80 Filelist **l; 81 Filelist *fl; 82 83 /* 84 * can't delete filelist structures while there 85 * are open readers of this directory, because 86 * they might have references to the structures. 87 * instead, just leave the empty refs in the list 88 * until there is no activity and then clean up. 89 */ 90 if(f->readers.ref != 0) 91 return; 92 if(f->nxchild == 0) 93 return; 94 95 /* 96 * no dir readers, file is locked, and 97 * there are empty entries in the file list. 98 * clean them out. 99 */ 100 for(l=&f->filelist; fl=*l; ){ 101 if(fl->f == nil){ 102 *l = (*l)->link; 103 free(fl); 104 }else 105 l = &(*l)->link; 106 } 107 f->nxchild = 0; 108 } 109 110 void 111 closefile(File *f) 112 { 113 if(decref(f) == 0){ 114 f->tree->destroy(f); 115 freefile(f); 116 } 117 } 118 119 static void 120 nop(File*) 121 { 122 } 123 124 int 125 removefile(File *f) 126 { 127 File *fp; 128 Filelist *fl; 129 130 fp = f->parent; 131 if(fp == nil){ 132 werrstr("no parent"); 133 closefile(f); 134 return -1; 135 } 136 137 if(fp == f){ 138 werrstr("cannot remove root"); 139 closefile(f); 140 return -1; 141 } 142 143 wlock(f); 144 wlock(fp); 145 if(f->nchild != 0){ 146 werrstr("has children"); 147 wunlock(fp); 148 wunlock(f); 149 closefile(f); 150 return -1; 151 } 152 153 if(f->parent != fp){ 154 werrstr("parent changed underfoot"); 155 wunlock(fp); 156 wunlock(f); 157 closefile(f); 158 return -1; 159 } 160 161 for(fl=fp->filelist; fl; fl=fl->link) 162 if(fl->f == f) 163 break; 164 assert(fl != nil && fl->f == f); 165 166 fl->f = nil; 167 fp->nchild--; 168 fp->nxchild++; 169 f->parent = nil; 170 wunlock(f); 171 172 cleanfilelist(fp); 173 wunlock(fp); 174 175 closefile(fp); /* reference from child */ 176 closefile(f); /* reference from tree */ 177 closefile(f); 178 return 0; 179 } 180 181 File* 182 createfile(File *fp, char *name, char *uid, ulong perm, void *aux) 183 { 184 File *f; 185 Filelist **l, *fl; 186 Tree *t; 187 188 if((fp->qid.type&QTDIR) == 0){ 189 werrstr("create in non-directory"); 190 return nil; 191 } 192 193 wlock(fp); 194 /* 195 * We might encounter blank spots along the 196 * way due to deleted files that have not yet 197 * been flushed from the file list. Don't reuse 198 * those - some apps (e.g., omero) depend on 199 * the file order reflecting creation order. 200 * Always create at the end of the list. 201 */ 202 for(l=&fp->filelist; fl=*l; l=&fl->link){ 203 if(fl->f && strcmp(fl->f->name, name) == 0){ 204 wunlock(fp); 205 werrstr("file already exists"); 206 return nil; 207 } 208 } 209 210 fl = emalloc9p(sizeof *fl); 211 *l = fl; 212 213 f = allocfile(); 214 f->name = estrdup9p(name); 215 f->uid = estrdup9p(uid ? uid : fp->uid); 216 f->gid = estrdup9p(fp->gid); 217 f->muid = estrdup9p(uid ? uid : "unknown"); 218 f->aux = aux; 219 f->mode = perm; 220 221 t = fp->tree; 222 lock(&t->genlock); 223 f->qid.path = t->qidgen++; 224 unlock(&t->genlock); 225 if(perm & DMDIR) 226 f->qid.type |= QTDIR; 227 if(perm & DMAPPEND) 228 f->qid.type |= QTAPPEND; 229 if(perm & DMEXCL) 230 f->qid.type |= QTEXCL; 231 232 f->mode = perm; 233 f->atime = f->mtime = time(0); 234 f->length = 0; 235 f->parent = fp; 236 incref(fp); 237 f->tree = fp->tree; 238 239 incref(f); /* being returned */ 240 incref(f); /* for the tree */ 241 fl->f = f; 242 fp->nchild++; 243 wunlock(fp); 244 245 return f; 246 } 247 248 static File* 249 walkfile1(File *dir, char *elem) 250 { 251 File *fp; 252 Filelist *fl; 253 254 rlock(dir); 255 if(strcmp(elem, "..") == 0){ 256 fp = dir->parent; 257 incref(fp); 258 runlock(dir); 259 closefile(dir); 260 return fp; 261 } 262 263 fp = nil; 264 for(fl=dir->filelist; fl; fl=fl->link) 265 if(fl->f && strcmp(fl->f->name, elem)==0){ 266 fp = fl->f; 267 incref(fp); 268 break; 269 } 270 271 runlock(dir); 272 closefile(dir); 273 return fp; 274 } 275 276 File* 277 walkfile(File *f, char *path) 278 { 279 char *os, *s, *nexts; 280 281 if(strchr(path, '/') == nil) 282 return walkfile1(f, path); /* avoid malloc */ 283 284 os = s = estrdup9p(path); 285 for(; *s; s=nexts){ 286 if(nexts = strchr(s, '/')) 287 *nexts++ = '\0'; 288 else 289 nexts = s+strlen(s); 290 f = walkfile1(f, s); 291 if(f == nil) 292 break; 293 } 294 free(os); 295 return f; 296 } 297 298 Tree* 299 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*)) 300 { 301 char *muid; 302 Tree *t; 303 File *f; 304 305 t = emalloc9p(sizeof *t); 306 f = allocfile(); 307 f->name = estrdup9p("/"); 308 if(uid == nil){ 309 uid = getuser(); 310 if(uid == nil) 311 uid = "none"; 312 } 313 uid = estrdup9p(uid); 314 315 if(gid == nil) 316 gid = estrdup9p(uid); 317 else 318 gid = estrdup9p(gid); 319 320 muid = estrdup9p(uid); 321 322 f->qid = (Qid){0, 0, QTDIR}; 323 f->length = 0; 324 f->atime = f->mtime = time(0); 325 f->mode = DMDIR | mode; 326 f->tree = t; 327 f->parent = f; 328 f->uid = uid; 329 f->gid = gid; 330 f->muid = muid; 331 332 incref(f); 333 t->root = f; 334 t->qidgen = 0; 335 t->dirqidgen = 1; 336 if(destroy == nil) 337 destroy = nop; 338 t->destroy = destroy; 339 340 return t; 341 } 342 343 static void 344 _freefiles(File *f) 345 { 346 Filelist *fl, *flnext; 347 348 for(fl=f->filelist; fl; fl=flnext){ 349 flnext = fl->link; 350 _freefiles(fl->f); 351 free(fl); 352 } 353 354 f->tree->destroy(f); 355 freefile(f); 356 } 357 358 void 359 freetree(Tree *t) 360 { 361 _freefiles(t->root); 362 free(t); 363 } 364 365 Readdir* 366 opendirfile(File *dir) 367 { 368 Readdir *r; 369 370 rlock(dir); 371 if((dir->mode & DMDIR)==0){ 372 runlock(dir); 373 return nil; 374 } 375 r = emalloc9p(sizeof(*r)); 376 377 /* 378 * This reference won't go away while we're 379 * using it because file list entries are not freed 380 * until the directory is removed and all refs to 381 * it (our fid is one!) have gone away. 382 */ 383 r->fl = dir->filelist; 384 r->dir = dir; 385 incref(&dir->readers); 386 runlock(dir); 387 return r; 388 } 389 390 long 391 readdirfile(Readdir *r, uchar *buf, long n) 392 { 393 long x, m; 394 Filelist *fl; 395 396 for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){ 397 if(fl->f == nil) 398 x = 0; 399 else if((x=convD2M(fl->f, buf+m, n-m)) <= BIT16SZ) 400 break; 401 } 402 r->fl = fl; 403 return m; 404 } 405 406 void 407 closedirfile(Readdir *r) 408 { 409 if(decref(&r->dir->readers) == 0){ 410 wlock(r->dir); 411 cleanfilelist(r->dir); 412 wunlock(r->dir); 413 } 414 free(r); 415 } 416