d3a30778bb68927616afac096659c60922a75915
[akaros.git] / kern / drivers / dev / root.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 #include <umem.h>
16
17 /* make it a power of 2 and nobody gets hurt */
18 #define MAXFILE 1024
19 int rootmaxq = MAXFILE;
20 int inumber = 13;
21
22 /* TODO:
23  *  - fix DOTDOT in gen.
24  *  - synchronization!  what needs protection from concurrent use, etc.
25  *      - clean up documentation and whatnot
26  *      - does remove, mkdir, rmdir work?
27  *      - fill this with cpio stuff
28  *      - figure out how to use the page cache
29  */
30
31 /* this gives you some idea of how much I like linked lists. Just make
32  * a big old table. Later on we can put next and prev indices into the
33  * data if we want but, our current kfs is 1-3 levels deep and very small
34  * (< 200 entries) so I doubt we'll need to do that. It just makes debugging
35  * memory a tad easier.
36  */
37 /* Da Rules.
38  * The roottab contains [name, qid, length, perm]. Length means length for files.
39  * Qid is [path, vers, type]. Path is me. vers is next. Type is QTDIR for dir
40  * and QTFILE for file and 0 for empty.
41  * Data is [dotdot, ptr, size, *sizep, next]
42  * dotdot is .., ptr is data (for files)
43  * size is # elements (for dirs)
44  * *sizep is a pointer for reasons not understood.
45  * next is the sibling. For a dir, it's the first element after '.'.
46  *      int     dotdot;
47  *      int     child; 
48  *      void    *ptr;
49  *      int     size;
50  *      int     *sizep;
51  * 
52  * entry is empty if type is 0. We look in roottab to determine that. 
53 */
54 /* we pack the qid as follows: path is the index, vers is ., and type is type */
55
56 /* Inferno seems to want to: perm |= DMDIR.  It gets checked in other places.
57  * NxM didn't want this, IIRC.
58  *
59  * Also note that "" (/, #r, whatever) has no vers/next/sibling. */
60 struct dirtab roottab[MAXFILE] = {
61         {"", {0, 0, QTDIR}, 0, DMDIR | 0777},
62         {"chan", {1, 2, QTDIR}, 0, DMDIR | 0777},
63         {"dev", {2, 3, QTDIR}, 0, DMDIR | 0777},
64         {"fd", {3, 4, QTDIR}, 0, DMDIR | 0777},
65         {"prog", {4, 5, QTDIR}, 0, DMDIR | 0777},
66         {"prof", {5, 6, QTDIR}, 0, DMDIR | 0777},
67         {"net", {6, 7, QTDIR}, 0, DMDIR | 0777},
68         {"net.alt", {7, 8, QTDIR}, 0, DMDIR | 0777},
69         {"nvfs", {8, 9, QTDIR}, 0, DMDIR | 0777},
70         {"env", {9, 10, QTDIR}, 0, DMDIR | 0777},
71         {"root", {10, 11, QTDIR}, 0, DMDIR | 0777},
72         {"srv", {11, 12, QTDIR}, 0, DMDIR | 0777},
73         {"mnt", {12, 13, QTDIR}, 0, DMDIR | 0777},
74         {"proc", {13, 0, QTDIR}, 0, DMDIR | 0777},
75 };
76
77 struct rootdata {
78         int dotdot;
79         int child;
80         void *ptr;
81         int size;
82         int *sizep;
83 };
84
85 struct rootdata rootdata[MAXFILE] = {
86         {0,     1,       &roottab[1],    12,    NULL},
87         {0,     0,       NULL,   0,      NULL},
88         {0,     0,       NULL,   0,      NULL},
89         {0,     0,       NULL,   0,      NULL},
90         {0,     0,       NULL,   0,      NULL},
91         {0,     0,       NULL,   0,      NULL},
92         {0,     0,       NULL,   0,      NULL},
93         {0,     0,       NULL,   0,      NULL},
94         {0,     0,       NULL,   0,      NULL},
95         {0,     0,       NULL,   0,      NULL},
96         {0,     0,       NULL,   0,      NULL},
97         {0,     0,       NULL,   0,      NULL},
98         {0,     0,       NULL,   0,      NULL},
99 };
100
101 /* this is super useful */
102 void dumprootdev(void)
103 {
104         struct dirtab *r = roottab;
105         struct rootdata *rd = rootdata;
106         int i;
107
108         printk("[       dirtab     ]      name: [pth, ver, typ],   len,        "
109                "perm,  .., chld,       data pointer,  size,       size pointer\n");
110         for (i = 0; i < rootmaxq; i++, r++, rd++) {
111                 if (i && (!r->name[0]))
112                         continue;
113                 printk("[%p]%10s: [%3d, %3d, %3d], %5d, %11o,",
114                            r,
115                            r->name, r->qid.path, r->qid.vers, r->qid.type,
116                            r->length, r->perm);
117                 printk(" %3d, %4d, %p, %5d, %p\n",
118                            rd->dotdot, rd->child, rd->ptr, rd->size, rd->sizep);
119         }
120 }
121
122 static int findempty(void)
123 {
124         int i;
125         for (i = 0; i < rootmaxq; i++) {
126                 if (!roottab[i].qid.type) {
127                         memset(&roottab[i], 0, sizeof(roottab[i]));
128                         return i;
129                 }
130         }
131         return -1;
132 }
133
134 static void freeempty(int i)
135 {
136         roottab[i].qid.type = 0;
137 }
138
139 static int newentry(int parent)
140 {
141         int n = findempty();
142         int sib;
143         if (n < 0)
144                 error("#r. No more");
145         printd("new entry is %d\n", n);
146         /* add the new one to the head of the linked list.  vers is 'next' */
147         roottab[n].qid.vers = rootdata[parent].child;
148         rootdata[parent].child = n;
149         return n;
150 }
151
152 static int createentry(int dir, char *name, int omode, int perm)
153 {
154         int n = newentry(dir);
155         strncpy(roottab[n].name, name, sizeof(roottab[n].name));
156         roottab[n].length = 0;
157         roottab[n].perm = perm;
158         /* vers is already properly set. */
159         mkqid(&roottab[n].qid, n, roottab[n].qid.vers,
160               perm & DMDIR ? QTDIR : QTFILE);
161         rootdata[n].dotdot = roottab[dir].qid.path;
162         rootdata[dir].ptr = &roottab[n];
163         rootdata[n].size = 0;
164         rootdata[n].sizep = &rootdata[n].size;
165         return n;
166 }
167
168 static void rootinit(void)
169 {
170         /* brho: pretty sure this should only be run once.  putting it in attach
171          * will run it multiple times. */
172         int i;
173         uint32_t len;
174         struct rootdata *r;
175         /* this begins with the root. */
176         for (i = 0;; i++) {
177                 r = &rootdata[i];
178                 if (r->sizep) {
179                         len = *r->sizep;
180                         r->size = len;
181                         roottab[i].length = len;
182                 }
183                 i = roottab[i].qid.vers;
184                 if (!i)
185                         break;
186         }
187 }
188
189 static struct chan *rootattach(char *spec)
190 {
191         struct chan *c;
192         if (*spec)
193                 error(Ebadspec);
194         c = devattach('r', spec);
195         mkqid(&c->qid, roottab[0].qid.path, roottab[0].qid.vers, QTDIR);
196         return c;
197 }
198
199 static int
200 rootgen(struct chan *c, char *name,
201                 struct dirtab *tab, int nd, int s, struct dir *dp)
202 {
203         int p, i;
204         struct rootdata *r;
205         int iter;
206         printd("rootgen, path is %d, tap %p, nd %d s %d name %s\n", c->qid.path,
207                tab, nd, s, name);
208
209         if (s == DEVDOTDOT) {
210                 panic("this is busted");
211                 p = rootdata[c->qid.path].dotdot;
212                 // XXX we need to set the vers too.  equiv to mkqid(&c->qid, ...)
213                 c->qid.path = p;
214                 c->qid.type = QTDIR;
215                 name = "#r";
216                 if (p != 0) {
217                         /* TODO: what is this doing?  do we want to walk the entire table,
218                          * or are we just walking the siblings of our parent? */
219                         for (i = p;;) {
220                                 if (roottab[i].qid.path == c->qid.path) {
221                                         name = roottab[i].name;
222                                         break;
223                                 }
224                                 i = roottab[i].qid.vers;
225                                 if (!i)
226                                         break;
227                         }
228                 }
229                 devdir(c, c->qid, name, 0, eve, 0777, dp);
230                 return 1;
231         }
232
233         if (c->qid.type != QTDIR) {
234                 /* return ourselved the first time; after that, -1 */
235                 if (s)
236                         return -1;
237                 tab = &roottab[c->qid.path];
238                 devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
239                 return 1;
240         }
241
242         if (name != NULL) {
243                 int path = c->qid.path;
244                 isdir(c);
245                 tab = &roottab[rootdata[path].child];
246                 /* we're starting at a directory. It might be '.' */
247                 for (iter = 0, i = rootdata[path].child; /* break */; iter++) {
248                         if(strcmp(tab->name, name) == 0){
249                                 printd("Rootgen returns 1 for %s\n", name);
250                                 devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
251                                 printd("return 1 with [%d, %d, %d]\n", dp->qid.path,
252                                        dp->qid.vers, dp->qid.type);
253                                 return 1;
254                         }
255                         if (iter > rootmaxq) {
256                                 printk("BUG:");
257                                 dumprootdev();
258                                 printk("name %s\n", name);
259                                 return -1;
260                         }
261                         i = roottab[i].qid.vers;
262                         if (!i)
263                                 break;
264                         tab = &roottab[i];
265                 }
266                 printd("rootgen: :%s: failed at path %d\n", name, path);
267                 return -1;
268         }
269         /* need to gen the file or the contents of the directory we are currently
270          * at.  but i think the tab entries are all over the place.  nd is how
271          * many entries the directory has. */
272         if (s >= nd) {
273                 printd("S OVERFLOW\n");
274                 return -1;
275         }
276         //tab += s;     /* this would only work if our entries were contig in the tab */
277         for (i = rootdata[c->qid.path].child; i; i = roottab[i].qid.vers) {
278                 tab = &roottab[i];
279                 if (s-- == 0)
280                         break;
281         }
282         if (!i) {
283                 printd("I OVERFLOW\n");
284                 return -1;
285         }
286         printd("root scan find returns path %p name %s\n", tab->qid.path, tab->name);
287         devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
288         return 1;
289 }
290
291 static struct walkqid *rootwalk(struct chan *c, struct chan *nc, char **name,
292                                                                 int nname)
293 {
294         uint32_t p;
295         if (0){
296                 printk("rootwalk: c %p. :", c);
297                 if (nname){
298                         int i;
299                         for (i = 0; i < nname - 1; i++)
300                                 printk("%s/", name[i]);
301                         printk("%s:\n", name[i]);
302                 }
303         }
304         p = c->qid.path;
305         printd("Start from #%d at %p\n", p, &roottab[p]);
306         return devwalk(c, nc, name, nname, &roottab[p], rootdata[p].size, rootgen);
307 }
308
309 static int rootstat(struct chan *c, uint8_t * dp, int n)
310 {
311         int p = c->qid.path;
312         return devstat(c, dp, n, rootdata[p].ptr, rootdata[p].size, rootgen);
313 }
314
315 static struct chan *rootopen(struct chan *c, int omode)
316 {
317         int p;
318         printd("rootopen: omode %o\n", omode);
319         p = c->qid.path;
320         return devopen(c, omode, rootdata[p].ptr, rootdata[p].size, rootgen);
321 }
322
323 static void rootcreate(struct chan *c, char *name, int omode, uint32_t perm)
324 {
325         struct dirtab *r = &roottab[c->qid.path], *newr;
326         struct rootdata *rd = &rootdata[c->qid.path];
327         /* need to filter openmode so that it gets only the access-type bits */
328         omode = openmode(omode);
329         c->mode = openmode(omode);
330         printd("rootcreate: c %p, name %s, omode %o, perm %x\n", 
331                c, name, omode, perm);
332         /* find an empty slot */
333         int path = c->qid.path;
334         int newfile;
335         newfile = createentry(path, name, omode, perm);
336         c->qid = roottab[newfile].qid;  /* need to update c */
337         rd->size++;
338         if (newfile > rootmaxq)
339                 rootmaxq = newfile;
340         printd("create: %s, newfile %d, dotdot %d, rootmaxq %d\n", name, newfile,
341                rootdata[newfile].dotdot, rootmaxq);
342 }
343
344 /*
345  * sysremove() knows this is a nop
346  *              fyi, this isn't true anymore!  they need to set c->type = -1;
347  */
348 static void rootclose(struct chan *c)
349 {
350 }
351
352 static long rootread(struct chan *c, void *buf, long n, int64_t offset)
353 {
354         uint32_t p, len;
355         uint8_t *data;
356
357         p = c->qid.path;
358         if (c->qid.type & QTDIR) {
359                 return devdirread(c, buf, n, rootdata[p].ptr, rootdata[p].size,
360                                                   rootgen);
361         }
362         len = rootdata[p].size;
363         if (offset < 0 || offset >= len) {
364                 return 0;
365         }
366         if (offset + n > len)
367                 n = len - offset;
368         data = rootdata[p].ptr;
369         /* we can't really claim it has to be a user address. Lots of
370          * kernel things read directly, e.g. /dev/reboot, #V, etc.
371          * Address validation should be done in the syscall layer.
372          */
373         memcpy(buf, data + offset, n);
374         return n;
375 }
376
377 /* For now, just kzmalloc the right amount. Later, we should use
378  * pages so mmap will go smoothly. Would be really nice to have a
379  * kpagemalloc ... barret?
380  *              we have kpage_alloc (gives a page) and kpage_alloc_addr (void*)
381  */
382 static long rootwrite(struct chan *c, void *a, long n, int64_t off)
383 {
384         struct rootdata *rd = &rootdata[c->qid.path];
385         struct dirtab *r = &roottab[c->qid.path];
386
387         if (off < 0)
388                 error("rootwrite: offset < 0!");
389
390         if (off + n > rd->size){
391                 void *p;
392                 p = krealloc(rd->ptr, off + n, KMALLOC_WAIT);
393                 if (! p)
394                         error("rootwrite: could not grow the file to %d bytes", off + n);
395                 rd->ptr = p;
396                 rd->size = off + n;
397         }
398         assert(current);
399         if (memcpy_from_user_errno(current, rd->ptr + off, a, n) < 0)
400                 error("%s: bad user addr %p", __FUNCTION__, a);
401
402         return n;
403 }
404
405 static int rootwstat(struct chan *c, uint8_t *m_buf, int m_buf_sz)
406 {
407         struct dirtab *file = &roottab[c->qid.path];
408         struct dir *dir;
409         int m_sz;
410
411         /* TODO: some security check, Eperm on error */
412
413         /* common trick in wstats.  we want the dir and any strings in the M.  the
414          * strings are smaller than entire M (strings plus other M).  the strings
415          * will be placed right after the dir (dir[1]) */
416         dir = kzmalloc(sizeof(struct dir) + m_buf_sz, KMALLOC_WAIT);
417         m_sz = convM2D(m_buf, m_buf_sz, &dir[0], (char*)&dir[1]);
418         if (!m_sz) {
419                 kfree(dir);
420                 error(Eshortstat);
421         }
422         /* TODO: handle more things than just the mode */
423         if (!emptystr(dir->name))
424                 printk("[%s] attempted rename of %s to %s\n", __FUNCTION__,
425                        file->name, dir->name);  /* strncpy for this btw */
426         if (dir->mode != ~0UL)
427                 file->perm = dir->mode | (file->qid.type == QTDIR ? DMDIR : 0);
428         kfree(dir);
429         return m_sz;
430 }
431
432 struct dev rootdevtab __devtab = {
433         'r',
434         "root",
435         devreset,
436         rootinit,
437         devshutdown,
438         rootattach,
439         rootwalk,
440         rootstat,
441         rootopen,
442         rootcreate,
443         rootclose,
444         rootread,
445         devbread,
446         rootwrite,
447         devbwrite,
448         devremove,
449         rootwstat,
450         devpower,
451         devchaninfo,
452 };