xref: /plan9-contrib/sys/src/lib9p/file.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
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 	File *f;
15 	Filelist *link;
16 };
17 
18 static QLock filelk;
19 static File *freefilelist;
20 
21 static File*
22 allocfile(void)
23 {
24 	int i, a;
25 	File *f;
26 	enum { N = 16 };
27 
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 	}
37 
38 	f = freefilelist;
39 	freefilelist = f->aux;
40 	qunlock(&filelk);
41 
42 	a = f->allocd;
43 	memset(f, 0, sizeof *f);
44 	f->allocd = a;
45 	return f;
46 }
47 
48 static void
49 freefile(File *f)
50 {
51 	Filelist *fl, *flnext;
52 
53 	for(fl=f->filelist; fl; fl=flnext){
54 		flnext = fl->link;
55 		assert(fl->f == nil);
56 		free(fl);
57 	}
58 
59 	free(f->name);
60 	free(f->uid);
61 	free(f->gid);
62 	free(f->muid);
63 	qlock(&filelk);
64 	assert(f->ref == 0);
65 	f->aux = freefilelist;
66 	freefilelist = f;
67 	qunlock(&filelk);
68 }
69 
70 void
71 closefile(File *f)
72 {
73 	if(decref(f) == 0){
74 		f->tree->destroy(f);
75 		freefile(f);
76 	}
77 }
78 
79 static void
80 nop(File*)
81 {
82 }
83 
84 int
85 removefile(File *f)
86 {
87 	File *fp;
88 	Filelist *fl;
89 
90 	fp = f->parent;
91 	if(fp == nil){
92 		werrstr("no parent");
93 		closefile(f);
94 		return -1;
95 	}
96 
97 	if(fp == f){
98 		werrstr("cannot remove root");
99 		closefile(f);
100 		return -1;
101 	}
102 
103 	wlock(fp);
104 	wlock(f);
105 	if(f->nchild != 0){
106 		werrstr("has children");
107 		wunlock(f);
108 		wunlock(fp);
109 		closefile(f);
110 		return -1;
111 	}
112 
113 	if(f->parent != fp){
114 		werrstr("parent changed underfoot");
115 		wunlock(f);
116 		wunlock(fp);
117 		closefile(f);
118 		return -1;
119 	}
120 
121 	for(fl=fp->filelist; fl; fl=fl->link)
122 		if(fl->f == f)
123 			break;
124 	assert(fl != nil && fl->f == f);
125 
126 	fl->f = nil;
127 	fp->nchild--;
128 	f->parent = nil;
129 	wunlock(fp);
130 	wunlock(f);
131 
132 	closefile(fp);	/* reference from child */
133 	closefile(f);	/* reference from tree */
134 	closefile(f);
135 	return 0;
136 }
137 
138 File*
139 createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
140 {
141 	File *f;
142 	Filelist *fl, *freel;
143 	Tree *t;
144 
145 	if((fp->qid.type&QTDIR) == 0){
146 		werrstr("create in non-directory");
147 		return nil;
148 	}
149 
150 	freel = nil;
151 	wlock(fp);
152 	for(fl=fp->filelist; fl; fl=fl->link){
153 		if(fl->f == nil)
154 			freel = fl;
155 		else if(strcmp(fl->f->name, name) == 0){
156 			wunlock(fp);
157 			werrstr("file already exists");
158 			return nil;
159 		}
160 	}
161 
162 	if(freel == nil){
163 		freel = emalloc9p(sizeof *freel);
164 		freel->link = fp->filelist;
165 		fp->filelist = freel;
166 	}
167 
168 	f = allocfile();
169 	f->name = estrdup9p(name);
170 	f->uid = estrdup9p(uid ? uid : fp->uid);
171 	f->gid = estrdup9p(fp->gid);
172 	f->muid = estrdup9p(uid ? uid : "unknown");
173 	f->aux = aux;
174 	f->mode = perm;
175 
176 	t = fp->tree;
177 	lock(&t->genlock);
178 	f->qid.path = t->qidgen++;
179 	unlock(&t->genlock);
180 	if(perm & DMDIR)
181 		f->qid.type |= QTDIR;
182 	if(perm & DMAPPEND)
183 		f->qid.type |= QTAPPEND;
184 	if(perm & DMEXCL)
185 		f->qid.type |= QTEXCL;
186 
187 	f->mode = perm;
188 	f->atime = f->mtime = time(0);
189 	f->length = 0;
190 	f->parent = fp;
191 	incref(fp);
192 	f->tree = fp->tree;
193 
194 	incref(f);	/* being returned */
195 	incref(f);	/* for the tree */
196 	freel->f = f;
197 	fp->nchild++;
198 	wunlock(fp);
199 
200 	return f;
201 }
202 
203 static File*
204 walkfile1(File *dir, char *elem)
205 {
206 	File *fp;
207 	Filelist *fl;
208 
209 	rlock(dir);
210 	if(strcmp(elem, "..") == 0){
211 		fp = dir->parent;
212 		incref(fp);
213 		runlock(dir);
214 		closefile(dir);
215 		return fp;
216 	}
217 
218 	fp = nil;
219 	for(fl=dir->filelist; fl; fl=fl->link)
220 		if(fl->f && strcmp(fl->f->name, elem)==0){
221 			fp = fl->f;
222 			incref(fp);
223 			break;
224 		}
225 
226 	runlock(dir);
227 	closefile(dir);
228 	return fp;
229 }
230 
231 File*
232 walkfile(File *f, char *path)
233 {
234 	char *os, *s, *nexts;
235 	File *nf;
236 
237 	if(strchr(path, '/') == nil)
238 		return walkfile1(f, path);	/* avoid malloc */
239 
240 	os = s = estrdup9p(path);
241 	incref(f);
242 	for(; *s; s=nexts){
243 		if(nexts = strchr(s, '/'))
244 			*nexts++ = '\0';
245 		else
246 			nexts = s+strlen(s);
247 		nf = walkfile1(f, s);
248 		decref(f);
249 		f = nf;
250 		if(f == nil)
251 			break;
252 	}
253 	free(os);
254 	return f;
255 }
256 
257 Tree*
258 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
259 {
260 	char *muid;
261 	Tree *t;
262 	File *f;
263 
264 	t = emalloc9p(sizeof *t);
265 	f = allocfile();
266 	f->name = estrdup9p("/");
267 	if(uid == nil){
268 		if(uid = getuser())
269 			uid = estrdup9p(uid);
270 	}
271 	if(uid == nil)
272 		uid = estrdup9p("none");
273 	else
274 		uid = estrdup9p(uid);
275 
276 	if(gid == nil)
277 		gid = estrdup9p(uid);
278 	else
279 		gid = estrdup9p(gid);
280 
281 	muid = estrdup9p(uid);
282 
283 	f->qid = (Qid){0, 0, QTDIR};
284 	f->length = 0;
285 	f->atime = f->mtime = time(0);
286 	f->mode = DMDIR | mode;
287 	f->tree = t;
288 	f->parent = f;
289 	f->uid = uid;
290 	f->gid = gid;
291 	f->muid = muid;
292 
293 	incref(f);
294 	t->root = f;
295 	t->qidgen = 0;
296 	t->dirqidgen = 1;
297 	if(destroy == nil)
298 		destroy = nop;
299 	t->destroy = destroy;
300 
301 	return t;
302 }
303 
304 static void
305 _freefiles(File *f)
306 {
307 	Filelist *fl, *flnext;
308 
309 	for(fl=f->filelist; fl; fl=flnext){
310 		flnext = fl->link;
311 		_freefiles(fl->f);
312 		free(fl);
313 	}
314 
315 	f->tree->destroy(f);
316 	freefile(f);
317 }
318 
319 void
320 freetree(Tree *t)
321 {
322 	_freefiles(t->root);
323 	free(t);
324 }
325 
326 struct Readdir {
327 	Filelist *fl;
328 };
329 
330 Readdir*
331 opendirfile(File *dir)
332 {
333 	Readdir *r;
334 
335 	rlock(dir);
336 	if((dir->mode & DMDIR)==0){
337 		runlock(dir);
338 		return nil;
339 	}
340 	r = emalloc9p(sizeof(*r));
341 
342 	/*
343 	 * This reference won't go away while we're using it
344 	 * since we are dir->rdir.
345 	 */
346 	r->fl = dir->filelist;
347 	runlock(dir);
348 	return r;
349 }
350 
351 long
352 readdirfile(Readdir *r, uchar *buf, long n)
353 {
354 	long x, m;
355 	Filelist *fl;
356 
357 	for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){
358 		if(fl->f == nil)
359 			x = 0;
360 		else if((x=convD2M(fl->f, buf+m, n-m)) <= BIT16SZ)
361 			break;
362 	}
363 	r->fl = fl;
364 	return m;
365 }
366 
367 void
368 closedirfile(Readdir *r)
369 {
370 	free(r);
371 }
372