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