xref: /inferno-os/emu/port/dev.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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 	nc->type = c->type;
106 	nc->dev = c->dev;
107 	nc->mode = c->mode;
108 	nc->qid = c->qid;
109 	nc->offset = c->offset;
110 	nc->umh = nil;
111 	nc->mountid = c->mountid;
112 	nc->aux = c->aux;
113 	nc->mqid = c->mqid;
114 	nc->mcp = c->mcp;
115 	return nc;
116 }
117 
118 Walkqid*
119 devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen)
120 {
121 	int i, j;
122 	volatile int 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 }
253 
254 long
255 devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen)
256 {
257 	long m, dsz;
258 	struct{
259 		Dir d;
260 		char slop[100];	/* TO DO */
261 	}dir;
262 
263 	for(m=0; m<n; c->dri++) {
264 		switch((*gen)(c, nil, tab, ntab, c->dri, &dir.d)){
265 		case -1:
266 			return m;
267 
268 		case 0:
269 			break;
270 
271 		case 1:
272 			dsz = convD2M(&dir.d, (uchar*)d, n-m);
273 			if(dsz <= BIT16SZ){	/* <= not < because this isn't stat; read is stuck */
274 				if(m == 0)
275 					error(Eshort);
276 				return m;
277 			}
278 			m += dsz;
279 			d += dsz;
280 			break;
281 		}
282 	}
283 
284 	return m;
285 }
286 
287 /*
288  * error(Eperm) if open permission not granted for up->env->user.
289  */
290 void
291 devpermcheck(char *fileuid, ulong perm, int omode)
292 {
293 	ulong t;
294 	static int access[] = { 0400, 0200, 0600, 0100 };
295 
296 	if(strcmp(up->env->user, fileuid) == 0)
297 		perm <<= 0;
298 	else
299 	if(strcmp(up->env->user, eve) == 0)
300 		perm <<= 3;
301 	else
302 		perm <<= 6;
303 
304 	t = access[omode&3];
305 	if((t&perm) != t)
306 		error(Eperm);
307 }
308 
309 Chan*
310 devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen)
311 {
312 	int i;
313 	Dir dir;
314 
315 	for(i=0;; i++) {
316 		switch((*gen)(c, nil, tab, ntab, i, &dir)){
317 		case -1:
318 			goto Return;
319 		case 0:
320 			break;
321 		case 1:
322 			if(c->qid.path == dir.qid.path) {
323 				devpermcheck(dir.uid, dir.mode, omode);
324 				goto Return;
325 			}
326 			break;
327 		}
328 	}
329 Return:
330 	c->offset = 0;
331 	if((c->qid.type&QTDIR) && omode!=OREAD)
332 		error(Eperm);
333 	c->mode = openmode(omode);
334 	c->flag |= COPEN;
335 	return c;
336 }
337 
338 Block*
339 devbread(Chan *c, long n, ulong offset)
340 {
341 	Block *bp;
342 
343 	bp = allocb(n);
344 	if(waserror()) {
345 		freeb(bp);
346 		nexterror();
347 	}
348 	bp->wp += devtab[c->type]->read(c, bp->wp, n, offset);
349 	poperror();
350 	return bp;
351 }
352 
353 long
354 devbwrite(Chan *c, Block *bp, ulong offset)
355 {
356 	long n;
357 
358 	if(waserror()) {
359 		freeb(bp);
360 		nexterror();
361 	}
362 	n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset);
363 	poperror();
364 	freeb(bp);
365 
366 	return n;
367 }
368 
369 void
370 devcreate(Chan *c, char *name, int mode, ulong perm)
371 {
372 	USED(c);
373 	USED(name);
374 	USED(mode);
375 	USED(perm);
376 	error(Eperm);
377 }
378 
379 void
380 devremove(Chan *c)
381 {
382 	USED(c);
383 	error(Eperm);
384 }
385 
386 int
387 devwstat(Chan *c, uchar *dp, int n)
388 {
389 	USED(c);
390 	USED(dp);
391 	USED(n);
392 	error(Eperm);
393 	return 0;
394 }
395 
396 int
397 readstr(ulong off, char *buf, ulong n, char *str)
398 {
399 	int size;
400 
401 	size = strlen(str);
402 	if(off >= size)
403 		return 0;
404 	if(off+n > size)
405 		n = size-off;
406 	memmove(buf, str+off, n);
407 	return n;
408 }
409 
410 int
411 readnum(ulong off, char *buf, ulong n, ulong val, int size)
412 {
413 	char tmp[64];
414 
415 	if(size > 64) size = 64;
416 
417 	snprint(tmp, sizeof(tmp), "%*.0lud ", size, val);
418 	if(off >= size)
419 		return 0;
420 	if(off+n > size)
421 		n = size-off;
422 	memmove(buf, tmp+off, n);
423 	return n;
424 }
425 
426 /*
427  * check that the name in a wstat is plausible
428  */
429 void
430 validwstatname(char *name)
431 {
432 	validname(name, 0);
433 	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
434 		error(Efilename);
435 }
436 
437 Dev*
438 devbyname(char *name)
439 {
440 	int i;
441 
442 	for(i = 0; devtab[i] != nil; i++)
443 		if(strcmp(devtab[i]->name, name) == 0)
444 			return devtab[i];
445 	return nil;
446 }
447