Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
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 };
18 static QLock filelk;
19 static File *freefilelist;
21 static File*
22 allocfile(void)
23 {
24 int i, a;
25 File *f;
26 enum { N = 16 };
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 }
38 f = freefilelist;
39 freefilelist = f->aux;
40 qunlock(&filelk);
42 a = f->allocd;
43 memset(f, 0, sizeof *f);
44 f->allocd = a;
45 return f;
46 }
48 static void
49 freefile(File *f)
50 {
51 Filelist *fl, *flnext;
53 for(fl=f->filelist; fl; fl=flnext){
54 flnext = fl->link;
55 assert(fl->f == nil);
56 free(fl);
57 }
59 free(f->dir.name);
60 free(f->dir.uid);
61 free(f->dir.gid);
62 free(f->dir.muid);
63 qlock(&filelk);
64 assert(f->ref.ref == 0);
65 f->aux = freefilelist;
66 freefilelist = f;
67 qunlock(&filelk);
68 }
70 void
71 closefile(File *f)
72 {
73 if(decref(&f->ref) == 0){
74 f->tree->destroy(f);
75 freefile(f);
76 }
77 }
79 static void
80 nop(File *f)
81 {
82 USED(f);
83 }
85 int
86 removefile(File *f)
87 {
88 File *fp;
89 Filelist *fl;
91 fp = f->parent;
92 if(fp == nil){
93 werrstr("no parent");
94 closefile(f);
95 return -1;
96 }
98 if(fp == f){
99 werrstr("cannot remove root");
100 closefile(f);
101 return -1;
104 wlock(&fp->rwlock);
105 wlock(&f->rwlock);
106 if(f->nchild != 0){
107 werrstr("has children");
108 wunlock(&f->rwlock);
109 wunlock(&fp->rwlock);
110 closefile(f);
111 return -1;
114 if(f->parent != fp){
115 werrstr("parent changed underfoot");
116 wunlock(&f->rwlock);
117 wunlock(&fp->rwlock);
118 closefile(f);
119 return -1;
122 for(fl=fp->filelist; fl; fl=fl->link)
123 if(fl->f == f)
124 break;
125 assert(fl != nil && fl->f == f);
127 fl->f = nil;
128 fp->nchild--;
129 f->parent = nil;
130 wunlock(&fp->rwlock);
131 wunlock(&f->rwlock);
133 closefile(fp); /* reference from child */
134 closefile(f); /* reference from tree */
135 closefile(f);
136 return 0;
139 File*
140 createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
142 File *f;
143 Filelist *fl, *freel;
144 Tree *t;
146 if((fp->dir.qid.type&QTDIR) == 0){
147 werrstr("create in non-directory");
148 return nil;
151 freel = nil;
152 wlock(&fp->rwlock);
153 for(fl=fp->filelist; fl; fl=fl->link){
154 if(fl->f == nil)
155 freel = fl;
156 else if(strcmp(fl->f->dir.name, name) == 0){
157 wunlock(&fp->rwlock);
158 werrstr("file already exists");
159 return nil;
163 if(freel == nil){
164 freel = emalloc9p(sizeof *freel);
165 freel->link = fp->filelist;
166 fp->filelist = freel;
169 f = allocfile();
170 f->dir.name = estrdup9p(name);
171 f->dir.uid = estrdup9p(uid ? uid : fp->dir.uid);
172 f->dir.gid = estrdup9p(fp->dir.gid);
173 f->dir.muid = estrdup9p(uid ? uid : "unknown");
174 f->aux = aux;
175 f->dir.mode = perm;
177 t = fp->tree;
178 lock(&t->genlock);
179 f->dir.qid.path = t->qidgen++;
180 unlock(&t->genlock);
181 if(perm & DMDIR)
182 f->dir.qid.type |= QTDIR;
183 if(perm & DMAPPEND)
184 f->dir.qid.type |= QTAPPEND;
185 if(perm & DMEXCL)
186 f->dir.qid.type |= QTEXCL;
188 f->dir.mode = perm;
189 f->dir.atime = f->dir.mtime = time(0);
190 f->dir.length = 0;
191 f->parent = fp;
192 incref(&fp->ref);
193 f->tree = fp->tree;
195 incref(&f->ref); /* being returned */
196 incref(&f->ref); /* for the tree */
197 freel->f = f;
198 fp->nchild++;
199 wunlock(&fp->rwlock);
201 return f;
204 static File*
205 walkfile1(File *dir, char *elem)
207 File *fp;
208 Filelist *fl;
210 rlock(&dir->rwlock);
211 if(strcmp(elem, "..") == 0){
212 fp = dir->parent;
213 incref(&fp->ref);
214 runlock(&dir->rwlock);
215 closefile(dir);
216 return fp;
219 fp = nil;
220 for(fl=dir->filelist; fl; fl=fl->link)
221 if(fl->f && strcmp(fl->f->dir.name, elem)==0){
222 fp = fl->f;
223 incref(&fp->ref);
224 break;
227 runlock(&dir->rwlock);
228 closefile(dir);
229 return fp;
232 File*
233 walkfile(File *f, char *path)
235 char *os, *s, *nexts;
236 File *nf;
238 if(strchr(path, '/') == nil)
239 return walkfile1(f, path); /* avoid malloc */
241 os = s = estrdup9p(path);
242 incref(&f->ref);
243 for(; *s; s=nexts){
244 if(nexts = strchr(s, '/'))
245 *nexts++ = '\0';
246 else
247 nexts = s+strlen(s);
248 nf = walkfile1(f, s);
249 decref(&f->ref);
250 f = nf;
251 if(f == nil)
252 break;
254 free(os);
255 return f;
258 static Qid
259 mkqid(vlong path, long vers, int type)
261 Qid q;
263 q.path = path;
264 q.vers = vers;
265 q.type = type;
266 return q;
270 Tree*
271 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
273 char *muid;
274 Tree *t;
275 File *f;
277 t = emalloc9p(sizeof *t);
278 f = allocfile();
279 f->dir.name = estrdup9p("/");
280 if(uid == nil){
281 if(uid = getuser())
282 uid = estrdup9p(uid);
284 if(uid == nil)
285 uid = estrdup9p("none");
286 else
287 uid = estrdup9p(uid);
289 if(gid == nil)
290 gid = estrdup9p(uid);
291 else
292 gid = estrdup9p(gid);
294 muid = estrdup9p(uid);
296 f->dir.qid = mkqid(0, 0, QTDIR);
297 f->dir.length = 0;
298 f->dir.atime = f->dir.mtime = time(0);
299 f->dir.mode = DMDIR | mode;
300 f->tree = t;
301 f->parent = f;
302 f->dir.uid = uid;
303 f->dir.gid = gid;
304 f->dir.muid = muid;
306 incref(&f->ref);
307 t->root = f;
308 t->qidgen = 0;
309 t->dirqidgen = 1;
310 if(destroy == nil)
311 destroy = nop;
312 t->destroy = destroy;
314 return t;
317 static void
318 _freefiles(File *f)
320 Filelist *fl, *flnext;
322 for(fl=f->filelist; fl; fl=flnext){
323 flnext = fl->link;
324 _freefiles(fl->f);
325 free(fl);
328 f->tree->destroy(f);
329 freefile(f);
332 void
333 freetree(Tree *t)
335 _freefiles(t->root);
336 free(t);
339 struct Readdir {
340 Filelist *fl;
341 };
343 Readdir*
344 opendirfile(File *dir)
346 Readdir *r;
348 rlock(&dir->rwlock);
349 if((dir->dir.mode & DMDIR)==0){
350 runlock(&dir->rwlock);
351 return nil;
353 r = emalloc9p(sizeof(*r));
355 /*
356 * This reference won't go away while we're using it
357 * since we are dir->rdir.
358 */
359 r->fl = dir->filelist;
360 runlock(&dir->rwlock);
361 return r;
364 long
365 readdirfile(Readdir *r, uchar *buf, long n)
367 long x, m;
368 Filelist *fl;
370 for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){
371 if(fl->f == nil)
372 x = 0;
373 else if((x=convD2M(&fl->f->dir, buf+m, n-m)) <= BIT16SZ)
374 break;
376 r->fl = fl;
377 return m;
380 void
381 closedirfile(Readdir *r)
383 free(r);