]> git.lizzy.rs Git - plan9front.git/blob - sys/src/lib9p/file.c
pc, pc64: disable all pci devices for /dev/reboot
[plan9front.git] / sys / src / lib9p / file.c
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         if(fp->parent == nil){
195                 wunlock(fp);
196                 werrstr("create in deleted directory");
197                 return nil;
198         }
199
200         /*
201          * We might encounter blank spots along the
202          * way due to deleted files that have not yet
203          * been flushed from the file list.  Don't reuse
204          * those - some apps (e.g., omero) depend on
205          * the file order reflecting creation order. 
206          * Always create at the end of the list.
207          */
208         for(l=&fp->filelist; fl=*l; l=&fl->link){
209                 if(fl->f && strcmp(fl->f->name, name) == 0){
210                         wunlock(fp);
211                         werrstr("file already exists");
212                         return nil;
213                 }
214         }
215         
216         fl = emalloc9p(sizeof *fl);
217         *l = fl;
218
219         f = allocfile();
220         f->name = estrdup9p(name);
221         f->uid = estrdup9p(uid ? uid : fp->uid);
222         f->gid = estrdup9p(fp->gid);
223         f->muid = estrdup9p(uid ? uid : "unknown");
224         f->aux = aux;
225         f->mode = perm;
226
227         t = fp->tree;
228         lock(&t->genlock);
229         f->qid.path = t->qidgen++;
230         unlock(&t->genlock);
231         if(perm & DMDIR)
232                 f->qid.type |= QTDIR;
233         if(perm & DMAPPEND)
234                 f->qid.type |= QTAPPEND;
235         if(perm & DMEXCL)
236                 f->qid.type |= QTEXCL;
237
238         f->atime = f->mtime = time(0);
239         f->length = 0;
240         f->parent = fp;
241         incref(fp);
242         f->tree = fp->tree;
243
244         incref(f);      /* being returned */
245         incref(f);      /* for the tree */
246         fl->f = f;
247         fp->nchild++;
248         wunlock(fp);
249
250         return f;
251 }
252
253 static File*
254 walkfile1(File *dir, char *elem)
255 {
256         File *fp;
257         Filelist *fl;
258
259         rlock(dir);
260         if(strcmp(elem, "..") == 0){
261                 fp = dir->parent;
262                 if(fp != nil)
263                         incref(fp);
264                 runlock(dir);
265                 closefile(dir);
266                 return fp;
267         }
268
269         fp = nil;
270         for(fl=dir->filelist; fl; fl=fl->link)
271                 if(fl->f && strcmp(fl->f->name, elem)==0){
272                         fp = fl->f;
273                         incref(fp);
274                         break;
275                 }
276
277         runlock(dir);
278         closefile(dir);
279         return fp;
280 }
281
282 File*
283 walkfile(File *f, char *path)
284 {
285         char *os, *s, *nexts;
286
287         if(strchr(path, '/') == nil)
288                 return walkfile1(f, path);      /* avoid malloc */
289
290         os = s = estrdup9p(path);
291         for(; *s; s=nexts){
292                 if(nexts = strchr(s, '/'))
293                         *nexts++ = '\0';
294                 else
295                         nexts = s+strlen(s);
296                 f = walkfile1(f, s);
297                 if(f == nil)
298                         break;
299         }
300         free(os);
301         return f;
302 }
303                         
304 Tree*
305 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
306 {
307         char *muid;
308         Tree *t;
309         File *f;
310
311         t = emalloc9p(sizeof *t);
312         f = allocfile();
313         f->name = estrdup9p("/");
314         if(uid == nil){
315                 uid = getuser();
316                 if(uid == nil)
317                         uid = "none";
318         }
319         uid = estrdup9p(uid);
320
321         if(gid == nil)
322                 gid = estrdup9p(uid);
323         else
324                 gid = estrdup9p(gid);
325
326         muid = estrdup9p(uid);
327
328         f->qid = (Qid){0, 0, QTDIR};
329         f->length = 0;
330         f->atime = f->mtime = time(0);
331         f->mode = DMDIR | mode;
332         f->tree = t;
333         f->parent = f;
334         f->uid = uid;
335         f->gid = gid;
336         f->muid = muid;
337
338         incref(f);
339         t->root = f;
340
341         /* t->qidgen starts at 1 because root Qid.path is 0 */
342         t->qidgen = 1;
343         if(destroy == nil)
344                 destroy = nop;
345         t->destroy = destroy;
346
347         return t;
348 }
349
350 static void
351 _freefiles(File *f)
352 {
353         Filelist *fl, *flnext;
354
355         for(fl=f->filelist; fl; fl=flnext){
356                 flnext = fl->link;
357                 _freefiles(fl->f);
358                 free(fl);
359         }
360
361         f->tree->destroy(f);
362         freefile(f);
363 }
364
365 void
366 freetree(Tree *t)
367 {
368         _freefiles(t->root);
369         free(t);
370 }
371
372 Readdir*
373 opendirfile(File *dir)
374 {
375         Readdir *r;
376
377         rlock(dir);
378         if((dir->mode & DMDIR)==0){
379                 runlock(dir);
380                 return nil;
381         }
382         r = emalloc9p(sizeof(*r));
383
384         /*
385          * This reference won't go away while we're 
386          * using it because file list entries are not freed
387          * until the directory is removed and all refs to
388          * it (our fid is one!) have gone away.
389          */
390         r->fl = dir->filelist;
391         r->dir = dir;
392         incref(&dir->readers);
393         runlock(dir);
394         return r;
395 }
396
397 long
398 readdirfile(Readdir *r, uchar *buf, long n, long o)
399 {
400         long x, m;
401         Filelist *fl;
402
403         if(o == 0)
404                 fl = r->dir->filelist;
405         else
406                 fl = r->fl;
407         for(m=0; fl && m+2<=n; fl=fl->link, m+=x){
408                 if(fl->f == nil)
409                         x = 0;
410                 else if((x=convD2M(fl->f, buf+m, n-m)) <= BIT16SZ)
411                         break;
412         }
413         r->fl = fl;
414         return m;
415 }
416
417 void
418 closedirfile(Readdir *r)
419 {
420         if(decref(&r->dir->readers) == 0){
421                 wlock(r->dir);
422                 cleanfilelist(r->dir);
423                 wunlock(r->dir);
424         }
425         free(r);
426 }