Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <fcall.h>
4 #include <thread.h>
5 #include <9p.h>
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 };
17 static QLock filelk;
18 static File *freefilelist;
20 static File*
21 allocfile(void)
22 {
23 int i, a;
24 File *f;
25 enum { N = 16 };
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 }
37 f = freefilelist;
38 freefilelist = f->aux;
39 qunlock(&filelk);
41 a = f->allocd;
42 memset(f, 0, sizeof *f);
43 f->allocd = a;
44 return f;
45 }
47 static void
48 freefile(File *f)
49 {
50 Filelist *fl, *flnext;
52 for(fl=f->filelist; fl; fl=flnext){
53 flnext = fl->link;
54 assert(fl->f == nil);
55 free(fl);
56 }
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 }
69 void
70 closefile(File *f)
71 {
72 if(decref(&f->ref) == 0){
73 f->tree->destroy(f);
74 freefile(f);
75 }
76 }
78 static void
79 nop(File *f)
80 {
81 USED(f);
82 }
84 int
85 removefile(File *f)
86 {
87 File *fp;
88 Filelist *fl;
90 fp = f->parent;
91 if(fp == nil){
92 werrstr("no parent");
93 closefile(f);
94 return -1;
95 }
97 if(fp == f){
98 werrstr("cannot remove root");
99 closefile(f);
100 return -1;
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;
113 if(f->parent != fp){
114 werrstr("parent changed underfoot");
115 wunlock(&f->rwlock);
116 wunlock(&fp->rwlock);
117 closefile(f);
118 return -1;
121 for(fl=fp->filelist; fl; fl=fl->link)
122 if(fl->f == f)
123 break;
124 assert(fl != nil && fl->f == f);
126 fl->f = nil;
127 fp->nchild--;
128 f->parent = nil;
129 wunlock(&fp->rwlock);
130 wunlock(&f->rwlock);
132 closefile(fp); /* reference from child */
133 closefile(f); /* reference from tree */
134 closefile(f);
135 return 0;
138 File*
139 createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
141 File *f;
142 Filelist *fl, *freel;
143 Tree *t;
145 if((fp->dir.qid.type&QTDIR) == 0){
146 werrstr("create in non-directory");
147 return nil;
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;
162 if(freel == nil){
163 freel = emalloc9p(sizeof *freel);
164 freel->link = fp->filelist;
165 fp->filelist = freel;
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;
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;
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;
194 incref(&f->ref); /* being returned */
195 incref(&f->ref); /* for the tree */
196 freel->f = f;
197 fp->nchild++;
198 wunlock(&fp->rwlock);
200 return f;
203 static File*
204 walkfile1(File *dir, char *elem)
206 File *fp;
207 Filelist *fl;
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;
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;
226 runlock(&dir->rwlock);
227 closefile(dir);
228 return fp;
231 File*
232 walkfile(File *f, char *path)
234 char *os, *s, *nexts;
236 if(strchr(path, '/') == nil)
237 return walkfile1(f, path); /* avoid malloc */
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;
249 free(os);
250 return f;
253 static Qid
254 mkqid(vlong path, long vers, int type)
256 Qid q;
258 q.path = path;
259 q.vers = vers;
260 q.type = type;
261 return q;
265 Tree*
266 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
268 char *muid;
269 Tree *t;
270 File *f;
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);
279 if(uid == nil)
280 uid = estrdup9p("none");
281 else
282 uid = estrdup9p(uid);
284 if(gid == nil)
285 gid = estrdup9p(uid);
286 else
287 gid = estrdup9p(gid);
289 muid = estrdup9p(uid);
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;
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;
309 return t;
312 static void
313 _freefiles(File *f)
315 Filelist *fl, *flnext;
317 for(fl=f->filelist; fl; fl=flnext){
318 flnext = fl->link;
319 _freefiles(fl->f);
320 free(fl);
323 f->tree->destroy(f);
324 freefile(f);
327 void
328 freetree(Tree *t)
330 _freefiles(t->root);
331 free(t);
334 struct Readdir {
335 Filelist *fl;
336 };
338 Readdir*
339 opendirfile(File *dir)
341 Readdir *r;
343 rlock(&dir->rwlock);
344 if((dir->dir.mode & DMDIR)==0){
345 runlock(&dir->rwlock);
346 return nil;
348 r = emalloc9p(sizeof(*r));
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;
359 long
360 readdirfile(Readdir *r, uchar *buf, long n)
362 long x, m;
363 Filelist *fl;
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;
371 r->fl = fl;
372 return m;
375 void
376 closedirfile(Readdir *r)
378 free(r);