xref: /inferno-os/emu/port/dev.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1 #include	"dat.h"
2 #include	"fns.h"
3 #include	"error.h"
4 
5 extern ulong	kerndate;
6 
7 void
8 mkqid(Qid *q, vlong path, ulong vers, int type)
9 {
10 	q->type = type;
11 	q->vers = vers;
12 	q->path = path;
13 }
14 
15 int
16 devno(int c, int user)
17 {
18 	int i;
19 
20 	for(i = 0; devtab[i] != nil; i++) {
21 		if(devtab[i]->dc == c)
22 			return i;
23 	}
24 	if(user == 0)
25 		panic("devno %C 0x%ux", c, c);
26 
27 	return -1;
28 }
29 
30 void
31 devdir(Chan *c, Qid qid, char *n, long length, char *user, long perm, Dir *db)
32 {
33 	db->name = n;
34 	if(c->flag&CMSG)
35 		qid.type |= QTMOUNT;
36 	db->qid = qid;
37 	db->type = devtab[c->type]->dc;
38 	db->dev = c->dev;
39 	db->mode = perm | (qid.type << 24);
40 	db->atime = time(0);
41 	db->mtime = kerndate;
42 	db->length = length;
43 	db->uid = user;
44 	db->gid = eve;
45 	db->muid = user;
46 }
47 
48 /*
49  * the zeroth element of the table MUST be the directory itself for ..
50  */
51 int
52 devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
53 {
54 	USED(name);
55 	if(tab == 0)
56 		return -1;
57 	if(i != DEVDOTDOT){
58 		/* skip over the first element, that for . itself */
59 		i++;
60 		if(i >= ntab)
61 			return -1;
62 		tab += i;
63 	}
64 	devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
65 	return 1;
66 }
67 
68 void
69 devinit(void)
70 {
71 }
72 
73 void
74 devshutdown(void)
75 {
76 }
77 
78 Chan*
79 devattach(int tc, char *spec)
80 {
81 	Chan *c;
82 	char *buf;
83 
84 	c = newchan();
85 	mkqid(&c->qid, 0, 0, QTDIR);
86 	c->type = devno(tc, 0);
87 	if(spec == nil)
88 		spec = "";
89 	buf = smalloc(4+strlen(spec)+1);
90 	sprint(buf, "#%C%s", tc, spec);
91 	c->name = newcname(buf);
92 	free(buf);
93 	return c;
94 }
95 
96 Chan*
97 devclone(Chan *c)
98 {
99 	Chan *nc;
100 
101 	if(c->flag & COPEN)
102 		panic("clone of open file type %C\n", devtab[c->type]->dc);
103 
104 	nc = newchan();
105 
106 	nc->type = c->type;
107 	nc->dev = c->dev;
108 	nc->mode = c->mode;
109 	nc->qid = c->qid;
110 	nc->offset = c->offset;
111 	nc->umh = nil;
112 	nc->mountid = c->mountid;
113 	nc->aux = c->aux;
114 	nc->mqid = c->mqid;
115 	nc->mcp = c->mcp;
116 	return nc;
117 }
118 
119 Walkqid*
120 devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen)
121 {
122 	int i, j, alloc;
123 	Walkqid *wq;
124 	char *n;
125 	Dir dir;
126 
127 	if(nname > 0)
128 		isdir(c);
129 
130 	alloc = 0;
131 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
132 	if(waserror()){
133 		if(alloc && wq->clone!=nil)
134 			cclose(wq->clone);
135 		free(wq);
136 		return nil;
137 	}
138 	if(nc == nil){
139 		nc = devclone(c);
140 		nc->type = 0;	/* device doesn't know about this channel yet */
141 		alloc = 1;
142 	}
143 	wq->clone = nc;
144 
145 	for(j=0; j<nname; j++){
146 		if(!(nc->qid.type&QTDIR)){
147 			if(j==0)
148 				error(Enotdir);
149 			goto Done;
150 		}
151 		n = name[j];
152 		if(strcmp(n, ".") == 0){
153     Accept:
154 			wq->qid[wq->nqid++] = nc->qid;
155 			continue;
156 		}
157 		if(strcmp(n, "..") == 0){
158 			(*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir);
159 			nc->qid = dir.qid;
160 			goto Accept;
161 		}
162 		/*
163 		 * Ugly problem: If we're using devgen, make sure we're
164 		 * walking the directory itself, represented by the first
165 		 * entry in the table, and not trying to step into a sub-
166 		 * directory of the table, e.g. /net/net. Devgen itself
167 		 * should take care of the problem, but it doesn't have
168 		 * the necessary information (that we're doing a walk).
169 		 */
170 		if(gen==devgen && nc->qid.path!=tab[0].qid.path)
171 			goto Notfound;
172 		for(i=0;; i++) {
173 			switch((*gen)(nc, n, tab, ntab, i, &dir)){
174 			case -1:
175 			Notfound:
176 				if(j == 0)
177 					error(Enonexist);
178 				kstrcpy(up->env->errstr, Enonexist, ERRMAX);
179 				goto Done;
180 			case 0:
181 				continue;
182 			case 1:
183 				if(strcmp(n, dir.name) == 0){
184 					nc->qid = dir.qid;
185 					goto Accept;
186 				}
187 				continue;
188 			}
189 		}
190 	}
191 	/*
192 	 * We processed at least one name, so will return some data.
193 	 * If we didn't process all nname entries succesfully, we drop
194 	 * the cloned channel and return just the Qids of the walks.
195 	 */
196 Done:
197 	poperror();
198 	if(wq->nqid < nname){
199 		if(alloc)
200 			cclose(wq->clone);
201 		wq->clone = nil;
202 	}else if(wq->clone){
203 		/* attach cloned channel to same device */
204 		wq->clone->type = c->type;
205 	}
206 	return wq;
207 }
208 
209 int
210 devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen)
211 {
212 	int i;
213 	Dir dir;
214 	char *p, *elem;
215 
216 	for(i=0;; i++)
217 		switch((*gen)(c, nil, tab, ntab, i, &dir)){
218 		case -1:
219 			if(c->qid.type & QTDIR){
220 				if(c->name == nil)
221 					elem = "???";
222 				else if(strcmp(c->name->s, "/") == 0)
223 					elem = "/";
224 				else
225 					for(elem=p=c->name->s; *p; p++)
226 						if(*p == '/')
227 							elem = p+1;
228 				devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
229 				n = convD2M(&dir, db, n);
230 				if(n == 0)
231 					error(Ebadarg);
232 				return n;
233 			}
234 			print("%s %s: devstat %C %llux\n",
235 				up->text, up->env->user,
236 				devtab[c->type]->dc, c->qid.path);
237 
238 			error(Enonexist);
239 		case 0:
240 			break;
241 		case 1:
242 			if(c->qid.path == dir.qid.path) {
243 				if(c->flag&CMSG)
244 					dir.mode |= DMMOUNT;
245 				n = convD2M(&dir, db, n);
246 				if(n == 0)
247 					error(Ebadarg);
248 				return n;
249 			}
250 			break;
251 		}
252 	error(Egreg);	/* not reached? */
253 	return -1;
254 }
255 
256 long
257 devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen)
258 {
259 	long m, dsz;
260 	struct{
261 		Dir d;
262 		char slop[100];	/* TO DO */
263 	}dir;
264 
265 	for(m=0; m<n; c->dri++) {
266 		switch((*gen)(c, nil, tab, ntab, c->dri, &dir.d)){
267 		case -1:
268 			return m;
269 
270 		case 0:
271 			break;
272 
273 		case 1:
274 			dsz = convD2M(&dir.d, (uchar*)d, n-m);
275 			if(dsz <= BIT16SZ){	/* <= not < because this isn't stat; read is stuck */
276 				if(m == 0)
277 					error(Eshort);
278 				return m;
279 			}
280 			m += dsz;
281 			d += dsz;
282 			break;
283 		}
284 	}
285 
286 	return m;
287 }
288 
289 /*
290  * error(Eperm) if open permission not granted for up->env->user.
291  */
292 void
293 devpermcheck(char *fileuid, ulong perm, int omode)
294 {
295 	ulong t;
296 	static int access[] = { 0400, 0200, 0600, 0100 };
297 
298 	if(strcmp(up->env->user, fileuid) == 0)
299 		perm <<= 0;
300 	else
301 	if(strcmp(up->env->user, eve) == 0)
302 		perm <<= 3;
303 	else
304 		perm <<= 6;
305 
306 	t = access[omode&3];
307 	if((t&perm) != t)
308 		error(Eperm);
309 }
310 
311 Chan*
312 devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen)
313 {
314 	int i;
315 	Dir dir;
316 
317 	for(i=0;; i++) {
318 		switch((*gen)(c, nil, tab, ntab, i, &dir)){
319 		case -1:
320 			goto Return;
321 		case 0:
322 			break;
323 		case 1:
324 			if(c->qid.path == dir.qid.path) {
325 				devpermcheck(dir.uid, dir.mode, omode);
326 				goto Return;
327 			}
328 			break;
329 		}
330 	}
331 Return:
332 	c->offset = 0;
333 	if((c->qid.type&QTDIR) && omode!=OREAD)
334 		error(Eperm);
335 	c->mode = openmode(omode);
336 	c->flag |= COPEN;
337 	return c;
338 }
339 
340 Block*
341 devbread(Chan *c, long n, ulong offset)
342 {
343 	Block *bp;
344 
345 	bp = allocb(n);
346 	if(waserror()) {
347 		freeb(bp);
348 		nexterror();
349 	}
350 	bp->wp += devtab[c->type]->read(c, bp->wp, n, offset);
351 	poperror();
352 	return bp;
353 }
354 
355 long
356 devbwrite(Chan *c, Block *bp, ulong offset)
357 {
358 	long n;
359 
360 	if(waserror()) {
361 		freeb(bp);
362 		nexterror();
363 	}
364 	n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset);
365 	poperror();
366 	freeb(bp);
367 
368 	return n;
369 }
370 
371 void
372 devcreate(Chan *c, char *name, int mode, ulong perm)
373 {
374 	USED(c);
375 	USED(name);
376 	USED(mode);
377 	USED(perm);
378 	error(Eperm);
379 }
380 
381 void
382 devremove(Chan *c)
383 {
384 	USED(c);
385 	error(Eperm);
386 }
387 
388 int
389 devwstat(Chan *c, uchar *dp, int n)
390 {
391 	USED(c);
392 	USED(dp);
393 	USED(n);
394 	error(Eperm);
395 	return 0;
396 }
397 
398 int
399 readstr(ulong off, char *buf, ulong n, char *str)
400 {
401 	int size;
402 
403 	size = strlen(str);
404 	if(off >= size)
405 		return 0;
406 	if(off+n > size)
407 		n = size-off;
408 	memmove(buf, str+off, n);
409 	return n;
410 }
411 
412 int
413 readnum(ulong off, char *buf, ulong n, ulong val, int size)
414 {
415 	char tmp[64];
416 
417 	if(size > 64) size = 64;
418 
419 	snprint(tmp, sizeof(tmp), "%*.0lud ", size, val);
420 	if(off >= size)
421 		return 0;
422 	if(off+n > size)
423 		n = size-off;
424 	memmove(buf, tmp+off, n);
425 	return n;
426 }
427 
428 /*
429  * check that the name in a wstat is plausible
430  */
431 void
432 validwstatname(char *name)
433 {
434 	validname(name, 0);
435 	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
436 		error(Efilename);
437 }
438 
439 Dev*
440 devbyname(char *name)
441 {
442 	int i;
443 
444 	for(i = 0; devtab[i] != nil; i++)
445 		if(strcmp(devtab[i]->name, name) == 0)
446 			return devtab[i];
447 	return nil;
448 }
449