xref: /plan9/sys/src/lib9p/file.c (revision 2cba34c722cc5343f414835ed47b457dd3b026ca)
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