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