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