xref: /inferno-os/emu/Plan9/devfs.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
1 /*
2  * Plan 9 file system interface
3  */
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"error.h"
7 
8 typedef struct Fsinfo Fsinfo;
9 struct Fsinfo
10 {
11 	int	fd;
12 	QLock;		/* serialise access to offset */
13 	ulong	offset;	/* offset used only for directory reads */
14 	Cname*	name;	/* Plan 9's name for file */
15 	Qid	rootqid;		/* Plan 9's qid for Inferno's root */
16 	char*	root;		/* prefix to strip from all names in diagnostics */
17 };
18 #define	FS(c)	((Fsinfo*)((c)->aux))
19 
20 char	rootdir[MAXROOT] = ROOT;
21 
22 static void
23 fserr(Fsinfo *f)
24 {
25 	int n;
26 	char *p;
27 
28 	oserrstr(up->env->errstr, ERRMAX);
29 	if(f != nil && *up->env->errstr == '\'' && (n = strlen(f->root)) > 1){
30 		/* don't reveal full names */
31 		if(strncmp(up->env->errstr+1, f->root, n-1) == 0){
32 			p = up->env->errstr+1+n;
33 			memmove(up->env->errstr+1, p, strlen(p)+1);
34 		}
35 	}
36 	error(up->env->errstr);
37 }
38 
39 static void
40 fsfree(Chan *c)
41 {
42 	cnameclose(FS(c)->name);
43 	free(c->aux);
44 }
45 
46 Chan*
47 fsattach(char *spec)
48 {
49 	Chan *c;
50 	Dir *d;
51 	char *root;
52 	Qid rootqid;
53 	static int devno;
54 	static Lock l;
55 
56 	if(!emptystr(spec)){
57 		if(strcmp(spec, "*") != 0)
58 			error(Ebadspec);
59 		root = "/";
60 	}else
61 		root = rootdir;
62 
63 	d = dirstat(root);
64 	if(d == nil)
65 		fserr(nil);
66 	rootqid = d->qid;
67 	free(d);
68 
69 	c = devattach('U', spec);
70 	lock(&l);
71 	c->dev = devno++;
72 	c->qid = rootqid;
73 	unlock(&l);
74 	c->aux = smalloc(sizeof(Fsinfo));
75 	FS(c)->name = newcname(root);
76 	FS(c)->rootqid = rootqid;
77 	FS(c)->fd = -1;
78 	FS(c)->root = root;
79 
80 	return c;
81 }
82 
83 Walkqid*
84 fswalk(Chan *c, Chan *nc, char **name, int nname)
85 {
86 	int j, alloc;
87 	Walkqid *wq;
88 	Dir *dir;
89 	char *n;
90 	Cname *current, *next;
91 	Qid rootqid;
92 
93 	if(nname > 0)
94 		isdir(c);	/* do we need this? */
95 
96 	alloc = 0;
97 	current = nil;
98 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
99 	if(waserror()){
100 		if(alloc && wq->clone!=nil)
101 			cclose(wq->clone);
102 		cnameclose(current);
103 		free(wq);
104 		return nil;
105 	}
106 	if(nc == nil){
107 		nc = devclone(c);
108 		nc->type = 0;
109 		alloc = 1;
110 	}
111 	wq->clone = nc;
112 
113 	rootqid = FS(c)->rootqid;
114 	current = FS(c)->name;
115 	if(current != nil)
116 		incref(&current->r);
117 	for(j=0; j<nname; j++){
118 		if(!(nc->qid.type&QTDIR)){
119 			if(j==0)
120 				error(Enotdir);
121 			break;
122 		}
123 		n = name[j];
124 		if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){	/* TO DO: underlying qids aliased */
125 			//print("** ufs walk '%s' -> %s\n", current->s, n);
126 			next = current;
127 			incref(&next->r);
128 			next = addelem(current, n);
129 			dir = dirstat(next->s);
130 			if(dir == nil){
131 				cnameclose(next);
132 				if(j == 0)
133 					error(Enonexist);
134 				strcpy(up->env->errstr, Enonexist);
135 				break;
136 			}
137 			nc->qid = dir->qid;
138 			free(dir);
139 			cnameclose(current);
140 			current = next;
141 		}
142 		wq->qid[wq->nqid++] = nc->qid;
143 	}
144 //	print("** ufs walk '%s'\n", current->s);
145 
146 	poperror();
147 	if(wq->nqid < nname){
148 		cnameclose(current);
149 		if(alloc)
150 			cclose(wq->clone);
151 		wq->clone = nil;
152 	}else if(wq->clone){
153 		/* now attach to our device */
154 		nc->aux = smalloc(sizeof(Fsinfo));
155 		nc->type = c->type;
156 		FS(nc)->rootqid = FS(c)->rootqid;
157 		FS(nc)->name = current;
158 		FS(nc)->fd = -1;
159 		FS(nc)->root = FS(c)->root;
160 	}else
161 		panic("fswalk: can't happen");
162 	return wq;
163 }
164 
165 int
166 fsstat(Chan *c, uchar *dp, int n)
167 {
168 	if(FS(c)->fd >= 0)
169 		n = fstat(FS(c)->fd, dp, n);
170 	else
171 		n = stat(FS(c)->name->s, dp, n);
172 	if(n < 0)
173 		fserr(FS(c));
174 	/* TO DO: change name to / if rootqid */
175 	return n;
176 }
177 
178 Chan*
179 fsopen(Chan *c, int mode)
180 {
181 	osenter();
182 	FS(c)->fd = open(FS(c)->name->s, mode);
183 	osleave();
184 	if(FS(c)->fd < 0)
185 		fserr(FS(c));
186 	c->mode = openmode(mode);
187 	c->offset = 0;
188 	FS(c)->offset = 0;
189 	c->flag |= COPEN;
190 	return c;
191 }
192 
193 void
194 fscreate(Chan *c, char *name, int mode, ulong perm)
195 {
196 	Dir *d;
197 	Cname *n;
198 
199 	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
200 		error(Efilename);
201 	n = addelem(newcname(FS(c)->name->s), name);
202 	osenter();
203 	FS(c)->fd = create(n->s, mode, perm);
204 	osleave();
205 	if(FS(c)->fd < 0) {
206 		cnameclose(n);
207 		fserr(FS(c));
208 	}
209 	d = dirfstat(FS(c)->fd);
210 	if(d == nil) {
211 		cnameclose(n);
212 		close(FS(c)->fd);
213 		FS(c)->fd = -1;
214 		fserr(FS(c));
215 	}
216 	c->qid = d->qid;
217 	free(d);
218 
219 	cnameclose(FS(c)->name);
220 	FS(c)->name = n;
221 
222 	c->mode = openmode(mode);
223 	c->offset = 0;
224 	FS(c)->offset = 0;
225 	c->flag |= COPEN;
226 }
227 
228 void
229 fsclose(Chan *c)
230 {
231 	if(c->flag & COPEN){
232 		osenter();
233 		close(FS(c)->fd);
234 		osleave();
235 	}
236 	/* don't need to check for CRCLOSE, because Plan 9 itself implements ORCLOSE */
237 	fsfree(c);
238 }
239 
240 static long
241 fsdirread(Chan *c, void *va, long count, vlong offset)
242 {
243 	long n, r;
244 	static char slop[16384];
245 
246 	if(FS(c)->offset != offset){
247 		seek(FS(c)->fd, 0, 0);
248 		for(n=0; n<offset;) {
249 			r = offset - n;
250 			if(r > sizeof(slop))
251 				r = sizeof(slop);
252 			osenter();
253 			r = read(FS(c)->fd, slop, r);
254 			osleave();
255 			if(r <= 0){
256 				FS(c)->offset = n;
257 				return 0;
258 			}
259 			n += r;
260 		}
261 		FS(c)->offset = offset;
262 	}
263 	osenter();
264 	r = read(FS(c)->fd, va, count);
265 	osleave();
266 	if(r < 0)
267 		return r;
268 	FS(c)->offset = offset+r;
269 	return r;
270 }
271 
272 long
273 fsread(Chan *c, void *va, long n, vlong offset)
274 {
275 	int r;
276 
277 	if(c->qid.type & QTDIR){	/* need to maintain offset only for directories */
278 		qlock(FS(c));
279 		if(waserror()){
280 			qunlock(FS(c));
281 			nexterror();
282 		}
283 		r = fsdirread(c, va, n, offset);
284 		poperror();
285 		qunlock(FS(c));
286 	}else{
287 		osenter();
288 		r = pread(FS(c)->fd, va, n, offset);
289 		osleave();
290 	}
291 	if(r < 0)
292 		fserr(FS(c));
293 	return r;
294 }
295 
296 long
297 fswrite(Chan *c, void *va, long n, vlong offset)
298 {
299 	int r;
300 
301 	osenter();
302 	r = pwrite(FS(c)->fd, va, n, offset);
303 	osleave();
304 	if(r < 0)
305 		fserr(FS(c));
306 	return r;
307 }
308 
309 void
310 fsremove(Chan *c)
311 {
312 	int r;
313 
314 	if(waserror()){
315 		fsfree(c);
316 		nexterror();
317 	}
318 	osenter();
319 	r = remove(FS(c)->name->s);
320 	osleave();
321 	if(r < 0)
322 		fserr(FS(c));
323 	poperror();
324 	fsfree(c);
325 }
326 
327 int
328 fswstat(Chan *c, uchar *dp, int n)
329 {
330 	osenter();
331 	if(FS(c)->fd >= 0)
332 		n = fwstat(FS(c)->fd, dp, n);
333 	else
334 		n = wstat(FS(c)->name->s, dp, n);
335 	osleave();
336 	if(n < 0)
337 		fserr(FS(c));
338 	return n;
339 }
340 
341 void
342 setid(char *name, int owner)
343 {
344 	if(!owner || iseve())
345 		kstrdup(&up->env->user, name);
346 }
347 
348 Dev fsdevtab = {
349 	'U',
350 	"fs",
351 
352 	devinit,
353 	fsattach,
354 	fswalk,
355 	fsstat,
356 	fsopen,
357 	fscreate,
358 	fsclose,
359 	fsread,
360 	devbread,
361 	fswrite,
362 	devbwrite,
363 	fsremove,
364 	fswstat
365 };
366