Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
	/*
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*
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*
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*
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
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
359
freetree(Tree *t)
360
{
361
	_freefiles(t->root);
362
	free(t);
363
}
364
 
365
Readdir*
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
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
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
}