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 File *f; 15 Filelist *link; 16 }; 17 18 static QLock filelk; 19 static File *freefilelist; 20 21 static File* 22 allocfile(void) 23 { 24 int i, a; 25 File *f; 26 enum { N = 16 }; 27 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 } 37 38 f = freefilelist; 39 freefilelist = f->aux; 40 qunlock(&filelk); 41 42 a = f->allocd; 43 memset(f, 0, sizeof *f); 44 f->allocd = a; 45 return f; 46 } 47 48 static void 49 freefile(File *f) 50 { 51 Filelist *fl, *flnext; 52 53 for(fl=f->filelist; fl; fl=flnext){ 54 flnext = fl->link; 55 assert(fl->f == nil); 56 free(fl); 57 } 58 59 free(f->name); 60 free(f->uid); 61 free(f->gid); 62 free(f->muid); 63 qlock(&filelk); 64 assert(f->ref == 0); 65 f->aux = freefilelist; 66 freefilelist = f; 67 qunlock(&filelk); 68 } 69 70 void 71 closefile(File *f) 72 { 73 if(decref(f) == 0){ 74 f->tree->destroy(f); 75 freefile(f); 76 } 77 } 78 79 static void 80 nop(File*) 81 { 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); 104 wlock(f); 105 if(f->nchild != 0){ 106 werrstr("has children"); 107 wunlock(f); 108 wunlock(fp); 109 closefile(f); 110 return -1; 111 } 112 113 if(f->parent != fp){ 114 werrstr("parent changed underfoot"); 115 wunlock(f); 116 wunlock(fp); 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); 130 wunlock(f); 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->qid.type&QTDIR) == 0){ 146 werrstr("create in non-directory"); 147 return nil; 148 } 149 150 freel = nil; 151 wlock(fp); 152 for(fl=fp->filelist; fl; fl=fl->link){ 153 if(fl->f == nil) 154 freel = fl; 155 else if(strcmp(fl->f->name, name) == 0){ 156 wunlock(fp); 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->name = estrdup9p(name); 170 f->uid = estrdup9p(uid ? uid : fp->uid); 171 f->gid = estrdup9p(fp->gid); 172 f->muid = estrdup9p(uid ? uid : "unknown"); 173 f->aux = aux; 174 f->mode = perm; 175 176 t = fp->tree; 177 lock(&t->genlock); 178 f->qid.path = t->qidgen++; 179 unlock(&t->genlock); 180 if(perm & DMDIR) 181 f->qid.type |= QTDIR; 182 if(perm & DMAPPEND) 183 f->qid.type |= QTAPPEND; 184 if(perm & DMEXCL) 185 f->qid.type |= QTEXCL; 186 187 f->mode = perm; 188 f->atime = f->mtime = time(0); 189 f->length = 0; 190 f->parent = fp; 191 incref(fp); 192 f->tree = fp->tree; 193 194 incref(f); /* being returned */ 195 incref(f); /* for the tree */ 196 freel->f = f; 197 fp->nchild++; 198 wunlock(fp); 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); 210 if(strcmp(elem, "..") == 0){ 211 fp = dir->parent; 212 incref(fp); 213 runlock(dir); 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->name, elem)==0){ 221 fp = fl->f; 222 incref(fp); 223 break; 224 } 225 226 runlock(dir); 227 closefile(dir); 228 return fp; 229 } 230 231 File* 232 walkfile(File *f, char *path) 233 { 234 char *os, *s, *nexts; 235 File *nf; 236 237 if(strchr(path, '/') == nil) 238 return walkfile1(f, path); /* avoid malloc */ 239 240 os = s = estrdup9p(path); 241 incref(f); 242 for(; *s; s=nexts){ 243 if(nexts = strchr(s, '/')) 244 *nexts++ = '\0'; 245 else 246 nexts = s+strlen(s); 247 nf = walkfile1(f, s); 248 decref(f); 249 f = nf; 250 if(f == nil) 251 break; 252 } 253 free(os); 254 return f; 255 } 256 257 Tree* 258 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*)) 259 { 260 char *muid; 261 Tree *t; 262 File *f; 263 264 t = emalloc9p(sizeof *t); 265 f = allocfile(); 266 f->name = estrdup9p("/"); 267 if(uid == nil){ 268 if(uid = getuser()) 269 uid = estrdup9p(uid); 270 } 271 if(uid == nil) 272 uid = estrdup9p("none"); 273 else 274 uid = estrdup9p(uid); 275 276 if(gid == nil) 277 gid = estrdup9p(uid); 278 else 279 gid = estrdup9p(gid); 280 281 muid = estrdup9p(uid); 282 283 f->qid = (Qid){0, 0, QTDIR}; 284 f->length = 0; 285 f->atime = f->mtime = time(0); 286 f->mode = DMDIR | mode; 287 f->tree = t; 288 f->parent = f; 289 f->uid = uid; 290 f->gid = gid; 291 f->muid = muid; 292 293 incref(f); 294 t->root = f; 295 t->qidgen = 0; 296 t->dirqidgen = 1; 297 if(destroy == nil) 298 destroy = nop; 299 t->destroy = destroy; 300 301 return t; 302 } 303 304 static void 305 _freefiles(File *f) 306 { 307 Filelist *fl, *flnext; 308 309 for(fl=f->filelist; fl; fl=flnext){ 310 flnext = fl->link; 311 _freefiles(fl->f); 312 free(fl); 313 } 314 315 f->tree->destroy(f); 316 freefile(f); 317 } 318 319 void 320 freetree(Tree *t) 321 { 322 _freefiles(t->root); 323 free(t); 324 } 325 326 struct Readdir { 327 Filelist *fl; 328 }; 329 330 Readdir* 331 opendirfile(File *dir) 332 { 333 Readdir *r; 334 335 rlock(dir); 336 if((dir->mode & DMDIR)==0){ 337 runlock(dir); 338 return nil; 339 } 340 r = emalloc9p(sizeof(*r)); 341 342 /* 343 * This reference won't go away while we're using it 344 * since we are dir->rdir. 345 */ 346 r->fl = dir->filelist; 347 runlock(dir); 348 return r; 349 } 350 351 long 352 readdirfile(Readdir *r, uchar *buf, long n) 353 { 354 long x, m; 355 Filelist *fl; 356 357 for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){ 358 if(fl->f == nil) 359 x = 0; 360 else if((x=convD2M(fl->f, buf+m, n-m)) <= BIT16SZ) 361 break; 362 } 363 r->fl = fl; 364 return m; 365 } 366 367 void 368 closedirfile(Readdir *r) 369 { 370 free(r); 371 } 372