Dev zeros the dir path before genning
[akaros.git] / kern / drivers / dev / dev.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15
16 extern uint32_t kerndate;
17 extern char *eve;
18
19 void
20 mkqid(struct qid *q, int64_t path, uint32_t vers, int type)
21 {
22         q->type = type;
23         q->vers = vers;
24         q->path = path;
25 }
26
27 int
28 devno(int c, int user)
29 {
30         int i;
31
32         for(i = 0; &devtab[i] < __devtabend; i++) {
33                 if(devtab[i].dc == c)
34                         return i;
35         }
36         if (user == 0)
37                 panic("devno %c 0x%u", c, c);
38
39         return -1;
40 }
41
42 void
43 devdir(struct chan *c, struct qid qid, char *n,
44        int64_t length, char *user, long perm, struct dir *db)
45 {
46         db->name = n;
47         if(c->flag&CMSG)
48                 qid.type |= QTMOUNT;
49         db->qid = qid;
50         db->type = devtab[c->type].dc;
51         db->dev = c->dev;
52         db->mode = perm;
53         db->mode |= qid.type << 24;
54         db->atime = seconds();
55         db->mtime = kerndate;
56         db->length = length;
57         db->uid = user;
58         db->gid = eve;
59         db->muid = user;
60 }
61
62 /*
63  * the zeroth element of the table MUST be the directory itself for ..
64 */
65 int
66 devgen(struct chan *c, char *unused_char_p_t, struct dirtab *tab, int ntab, int i,
67        struct dir *dp)
68 {
69         if(tab == 0)
70                 return -1;
71         if(i != DEVDOTDOT){
72                 /* skip over the first element, that for . itself */
73                 i++;
74                 if(i >= ntab)
75                         return -1;
76                 tab += i;
77         }
78         devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
79         return 1;
80 }
81
82 void
83 devreset(void)
84 {
85 }
86
87 void
88 devinit(void)
89 {
90 }
91
92 void
93 devshutdown(void)
94 {
95 }
96
97 struct chan*
98 devattach(int tc, char *spec)
99 {
100         struct chan *c;
101         char *buf;
102
103         c = newchan();
104         mkqid(&c->qid, 0, 0, QTDIR);
105         c->type = devno(tc, 0);
106         if(spec == NULL)
107                 spec = "";
108         /* 2 for #c, 1 for \0 */
109         buf = kzmalloc(2 + strlen(spec) + 1, KMALLOC_WAIT);
110         snprintf(buf, sizeof(buf), "#%c%s", tc, spec);
111         c->name = newcname(buf);
112         kfree(buf);
113         return c;
114 }
115
116
117 struct chan*
118 devclone(struct chan *c)
119 {
120         struct chan *nc;
121
122         if(c->flag & COPEN)
123                 panic("clone of open file type %c\n", devtab[c->type].dc);
124
125         nc = newchan();
126
127         nc->type = c->type;
128         nc->dev = c->dev;
129         nc->mode = c->mode;
130         nc->qid = c->qid;
131         nc->offset = c->offset;
132         nc->umh = NULL;
133         nc->mountid = c->mountid;
134         nc->aux = c->aux;
135         nc->mqid = c->mqid;
136         nc->mcp = c->mcp;
137         return nc;
138 }
139
140 struct walkqid*
141 devwalk(struct chan *c,
142         struct chan *nc, char **name, int nname,
143         struct dirtab *tab, int ntab, Devgen *gen)
144 {
145         ERRSTACK(1);
146         int i, j;
147         volatile int alloc; /* to keep waserror from optimizing this out */
148         struct walkqid *wq;
149         char *n;
150         struct dir dir;
151
152         if(nname > 0)
153                 isdir(c);
154
155         alloc = 0;
156         wq = kzmalloc(sizeof(struct walkqid) + nname * sizeof(struct qid),
157                       KMALLOC_WAIT);
158         if(waserror()){
159                 if(alloc && wq->clone!=NULL)
160                         cclose(wq->clone);
161                 kfree(wq);
162                 poperror();
163                 return NULL;
164         }
165         if(nc == NULL){
166                 nc = devclone(c);
167                 /* inferno was setting this to 0, assuming it was devroot.  lining up
168                  * with chanrelease and newchan */
169                 nc->type = -1;  /* device doesn't know about this channel yet */
170                 alloc = 1;
171         }
172         wq->clone = nc;
173
174         dir.qid.path = 0;
175         for(j=0; j<nname; j++){
176                 if(!(nc->qid.type&QTDIR)){
177                         if(j==0)
178                                 error(Enotdir);
179                         goto Done;
180                 }
181                 n = name[j];
182                 if(strcmp(n, ".") == 0){
183     Accept:
184                         wq->qid[wq->nqid++] = nc->qid;
185                         continue;
186                 }
187                 if(strcmp(n, "..") == 0){
188                         (*gen)(nc, NULL, tab, ntab, DEVDOTDOT, &dir);
189                         nc->qid = dir.qid;
190                         goto Accept;
191                 }
192                 /*
193                  * Ugly problem: If we're using devgen, make sure we're
194                  * walking the directory itself, represented by the first
195                  * entry in the table, and not trying to step into a sub-
196                  * directory of the table, e.g. /net/net. Devgen itself
197                  * should take care of the problem, but it doesn't have
198                  * the necessary information (that we're doing a walk).
199                  */
200                 if(gen==devgen && nc->qid.path!=tab[0].qid.path)
201                         goto Notfound;
202                 dir.qid.path = 0;
203                 for(i=0;; i++) {
204                         switch((*gen)(nc, n, tab, ntab, i, &dir)){
205                         case -1:
206                                 printd("DEVWALK -1, i was %d, want path %p\n", i, c->qid.path);
207                         Notfound:
208                                 set_errno(ENOENT);
209                                 if(j == 0)
210                                         error(Enonexist);
211                                 set_errstr(Enonexist);
212                                 goto Done;
213                         case 0:
214                                 printd("DEVWALK continue, i was %d\n", i);
215                                 continue;
216                         case 1:
217                                 printd("DEVWALK gen returns path %p name %s, want path %p\n",
218                                        dir.qid.path, dir.name, c->qid.path);
219                                 if(strcmp(n, dir.name) == 0){
220                                         nc->qid = dir.qid;
221                                         goto Accept;
222                                 }
223                                 continue;
224                         }
225                 }
226         }
227         /*
228          * We processed at least one name, so will return some data.
229          * If we didn't process all nname entries succesfully, we drop
230          * the cloned channel and return just the Qids of the walks.
231          */
232 Done:
233         poperror();
234         if(wq->nqid < nname){
235                 if(alloc)
236                         cclose(wq->clone);
237                 wq->clone = NULL;
238         }else if(wq->clone){
239                 /* attach cloned channel to same device */
240                 wq->clone->type = c->type;
241         }
242         return wq;
243 }
244
245 int
246 devstat(struct chan *c, uint8_t *db, int n,
247         struct dirtab *tab, int ntab, Devgen *gen)
248 {
249         int i;
250         struct dir dir;
251         char *p, *elem;
252
253         dir.qid.path = 0;
254         for(i=0;; i++)
255                 switch((*gen)(c, NULL, tab, ntab, i, &dir)){
256                 case -1:
257                         if(c->qid.type & QTDIR){
258                                 printd("DEVSTAT got a dir: %llu\n", c->qid.path);
259                                 if(c->name == NULL)
260                                         elem = "???";
261                                 else if(strcmp(c->name->s, "/") == 0)
262                                         elem = "/";
263                                 else
264                                         for(elem=p=c->name->s; *p; p++)
265                                                 if(*p == '/')
266                                                         elem = p+1;
267                                 devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
268                                 n = convD2M(&dir, db, n);
269                                 if(n == 0)
270                                         error(Ebadarg);
271                                 return n;
272                         }
273                         printd("DEVSTAT fails:%c %llu\n", devtab[c->type].dc, c->qid.path);
274                         set_errno(ENOENT);
275                         error(Enonexist);
276                 case 0:
277                         printd("DEVSTAT got 0\n");
278                         break;
279                 case 1:
280                         printd("DEVSTAT gen returns path %p name %s, want path %p\n",
281                                dir.qid.path, dir.name, c->qid.path);
282                         if(c->qid.path == dir.qid.path) {
283                                 if(c->flag&CMSG)
284                                         dir.mode |= DMMOUNT;
285                                 n = convD2M(&dir, db, n);
286                                 if(n == 0)
287                                         error(Ebadarg);
288                                 return n;
289                         }
290                         break;
291                 }
292 }
293
294 long
295 devdirread(struct chan *c, char *d, long n,
296            struct dirtab *tab, int ntab, Devgen *gen)
297 {
298         long m, dsz;
299         /* this is gross. Make it 2 so we have room at the end for
300          * bad things.
301          */
302         struct dir dir[4];
303
304         dir[0].qid.path = 0;
305         for(m=0; m<n; c->dri++) {
306                 switch((*gen)(c, NULL, tab, ntab, c->dri, &dir[0])){
307                 case -1:
308                         printd("DEVDIRREAD got -1, asked for s = %d\n", c->dri);
309                         return m;
310
311                 case 0:
312                         printd("DEVDIRREAD got 0, asked for s = %d\n", c->dri);
313                         break;
314
315                 case 1:
316                         printd("DEVDIRREAD got 1, asked for s = %d\n", c->dri);
317                         dsz = convD2M(&dir[0], ( uint8_t *)d, n-m);
318                         if(dsz <= BIT16SZ){     /* <= not < because this isn't stat; read is stuck */
319                                 if(m == 0)
320                                         error(Eshort);
321                                 return m;
322                         }
323                         m += dsz;
324                         d += dsz;
325                         break;
326                 }
327         }
328
329         return m;
330 }
331
332 /*
333  * error(Eperm) if open permission not granted for up->env->user.
334  */
335 void
336 devpermcheck(char *fileuid, uint32_t perm, int omode)
337 {
338         uint32_t t;
339         static int access[] = { 0400, 0200, 0600, 0100 };
340
341         if(strcmp(current->user, fileuid) == 0)
342                 perm <<= 0;
343         else
344         if(strcmp(current->user, eve) == 0)
345                 perm <<= 3;
346         else
347                 perm <<= 6;
348
349         t = access[omode&3];
350         if((t&perm) != t)
351                 error("%s: devpermcheck(%s,0x%x,0x%x) failed", Eperm, fileuid, perm, omode);
352 }
353
354 struct chan*
355 devopen(struct chan *c, int omode, struct dirtab *tab, int ntab, Devgen *gen)
356 {
357         int i;
358         struct dir dir;
359
360         dir.qid.path = 0;
361         for(i=0;; i++) {
362                 switch((*gen)(c, NULL, 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) && !IS_RDONLY(omode))
378                 error("Tried opening dir with non-read-only mode %o", omode);
379         c->mode = openmode(omode);
380         c->flag |= COPEN;
381         return c;
382 }
383
384 void
385 devcreate(struct chan*c, char *unused_char_p_t, int unused_int, uint32_t u)
386 {
387         error(Eperm);
388 }
389
390 struct block*
391 devbread(struct chan *c, long n, uint32_t offset)
392 {
393         ERRSTACK(1);
394         struct block *bp;
395
396         bp = allocb(n);
397         if(bp == 0)
398                 error(Enomem);
399         if(waserror()) {
400                 freeb(bp);
401                 nexterror();
402         }
403         bp->wp += devtab[c->type].read(c, bp->wp, n, offset);
404         poperror();
405         return bp;
406 }
407
408 long
409 devbwrite(struct chan *c, struct block *bp, uint32_t offset)
410 {
411         ERRSTACK(1);
412         long n;
413
414         if(waserror()) {
415                 freeb(bp);
416                 nexterror();
417         }
418         n = devtab[c->type].write(c, bp->rp, BLEN(bp), offset);
419         poperror();
420         freeb(bp);
421
422         return n;
423 }
424
425 void
426 devremove(struct chan*c)
427 {
428         error(Eperm);
429 }
430
431 int
432 devwstat(struct chan*c, uint8_t *unused_uint8_p_t, int i)
433 {
434         error(Eperm);
435         return 0;
436 }
437
438 void
439 devpower(int i)
440 {
441         error(Eperm);
442 }
443
444 #if 0
445 int
446 devconfig( int unused_int, char *c, DevConf *)
447 {
448         error(Eperm);
449         return 0;
450 }
451 #endif
452 /*
453  * check that the name in a wstat is plausible
454  */
455 void
456 validwstatname(char *name)
457 {
458         validname(name, 0);
459         if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
460                 error(Efilename);
461 }
462
463 struct dev*
464 devbyname(char *name)
465 {
466         int i;
467
468         for(i = 0; &devtab[i] < __devtabend; i++)
469                 if(strcmp(devtab[i].name, name) == 0)
470                         return &devtab[i];
471         return NULL;
472 }
473
474