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