file.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 }