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*
allocfile(void)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
freefile(File * f)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
cleanfilelist(File * f)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
closefile(File * f)111 closefile(File *f)
112 {
113 if(decref(f) == 0){
114 f->tree->destroy(f);
115 freefile(f);
116 }
117 }
118
119 static void
nop(File *)120 nop(File*)
121 {
122 }
123
124 int
removefile(File * f)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*
createfile(File * fp,char * name,char * uid,ulong perm,void * aux)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*
walkfile1(File * dir,char * elem)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*
walkfile(File * f,char * path)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*
alloctree(char * uid,char * gid,ulong mode,void (* destroy)(File *))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
_freefiles(File * f)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
freetree(Tree * t)359 freetree(Tree *t)
360 {
361 _freefiles(t->root);
362 free(t);
363 }
364
365 Readdir*
opendirfile(File * dir)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
readdirfile(Readdir * r,uchar * buf,long n)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
closedirfile(Readdir * r)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