devroot: Fix stat and clean up rootgen.

Stat on directories didn't work.  It was doing the usual devstat() thing
where devstat() makes a fake entry.  But we can actually do better, by
implementing rootstat directly.  Since we're trying to be a filesystem, we
shouldn't be using the synthetic solutions (e.g. devstat), which get the
timestamps and usernames wrong.

Likewise, I cleaned up rootgen() a little.  We had been taking a dirtab
pointer, but not actually using it (except in one odd case), which was
confusing.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/drivers/dev/root.c b/kern/drivers/dev/root.c
index dc9e89e..08a16e4 100644
--- a/kern/drivers/dev/root.c
+++ b/kern/drivers/dev/root.c
@@ -51,7 +51,6 @@
 /* make it a power of 2 and nobody gets hurt */
 #define MAXFILE 1024
 int rootmaxq = MAXFILE;
-int inumber = 13;
 
 /* TODO:
  *  - synchronization!  what needs protection from concurrent use, etc.
@@ -59,6 +58,7 @@
  * 	- does remove, mkdir, rmdir work?
  * 	- fill this with cpio stuff
  * 	- figure out how to use the page cache
+ * 	- atime/ctime/mtime/etctime
  */
 
 /* this gives you some idea of how much I like linked lists. Just make
@@ -229,16 +229,21 @@
 	return c;
 }
 
-static int
-rootgen(struct chan *c, char *name,
-		struct dirtab *tab, int nd, int s, struct dir *dp)
+/* rootgen is unlike other gens when it comes to the dirtab (tab) and ntab (nd).
+ * We actually are a linked list of entries that happen to be in a table.  We
+ * can figure out where to start based on the chan's qid.path and start the gen
+ * from there.  So we don't need the 'tab' from dev.  Likewise, for directories,
+ * we can figure out nd - our length in the dirtab. */
+static int rootgen(struct chan *c, char *name, struct dirtab *tab_unused,
+                   int nd_unused, int s, struct dir *dp)
 {
 	int p, i;
 	struct rootdata *r;
 	int iter;
-	printd("rootgen, path is %d, tap %p, nd %d s %d name %s\n", c->qid.path,
-	       tab, nd, s, name);
+	struct dirtab *tab;
 
+	printd("rootgen from %s, for %s, s %d\n", roottab[c->qid.path].name, name,
+	       s);
 	if (s == DEVDOTDOT) {
 		p = rootdata[c->qid.path].dotdot;
 		c->qid = roottab[p].qid;
@@ -284,21 +289,19 @@
 		printd("rootgen: :%s: failed at path %d\n", name, path);
 		return -1;
 	}
-	/* need to gen the file or the contents of the directory we are currently
-	 * at.  but i think the tab entries are all over the place.  nd is how
-	 * many entries the directory has. */
-	if (s >= nd) {
-		printd("S OVERFLOW\n");
+	/* Note we do not provide a "direct gen" for directories, which normally I'd
+	 * do here.  If we do, that will confuse devdirread, which expects us to
+	 * list our children, not ourselves.  stat() wants a "direct gen", e.g. for
+	 * ls -l or stat of a directory.  We can handle that in rootstat(). */
+	if (s >= roottab[c->qid.path].length)
 		return -1;
-	}
-	//tab += s;	/* this would only work if our entries were contig in the tab */
 	for (i = rootdata[c->qid.path].child; i; i = roottab[i].qid.vers) {
 		tab = &roottab[i];
 		if (s-- == 0)
 			break;
 	}
 	if (!i) {
-		printd("I OVERFLOW\n");
+		warn("#root overflowed 'i', probably a bug");
 		return -1;
 	}
 	printd("root scan find returns path %p name %s\n", tab->qid.path, tab->name);
@@ -309,33 +312,32 @@
 static struct walkqid *rootwalk(struct chan *c, struct chan *nc, char **name,
 								int nname)
 {
-	uint32_t p;
-	if (0){
-		printk("rootwalk: c %p. :", c);
-		if (nname){
-			int i;
-			for (i = 0; i < nname - 1; i++)
-				printk("%s/", name[i]);
-			printk("%s:\n", name[i]);
-		}
-	}
-	p = c->qid.path;
-	printd("Start from #%d at %p\n", p, &roottab[p]);
-	return devwalk(c, nc, name, nname, &roottab[p], roottab[p].length, rootgen);
+	return devwalk(c, nc, name, nname, NULL, 0, rootgen);
 }
 
+/* Instead of using devstat, we use our own.  This allows us to have stats on
+ * directories.  devstat() just fakes it.  Note that gen cannot return a direct
+ * gen for a directory, since that would break devdirread(). */
 static int rootstat(struct chan *c, uint8_t * dp, int n)
 {
-	int p = c->qid.path;
-	return devstat(c, dp, n, rootdata[p].ptr, roottab[p].length, rootgen);
+	struct dir dir[1];
+	ssize_t ret;
+	struct dirtab *entry = &roottab[c->qid.path];
+
+	/* TODO: this assumes eve is the user, which is what synthetic devices do.
+	 * Likewise, it sets atime/mtime like a synth device.  There might be other
+	 * weird things, like qid.type and mode. */
+	devdir(c, entry->qid, entry->name, entry->length, eve.name, entry->perm,
+	       dir);
+	ret = convD2M(dir, dp, n);
+	if (!ret)
+		error(EFAIL, "#root failed to convert stat object to M");
+	return ret;
 }
 
 static struct chan *rootopen(struct chan *c, int omode)
 {
-	int p;
-	printd("rootopen: omode %o\n", omode);
-	p = c->qid.path;
-	return devopen(c, omode, rootdata[p].ptr, roottab[p].length, rootgen);
+	return devopen(c, omode, NULL, 0, rootgen);
 }
 
 static void rootcreate(struct chan *c, char *name, int omode, uint32_t perm)
@@ -373,10 +375,8 @@
 	uint8_t *data;
 
 	p = c->qid.path;
-	if (c->qid.type & QTDIR) {
-		return devdirread(c, buf, n, rootdata[p].ptr, roottab[p].length,
-						  rootgen);
-	}
+	if (c->qid.type & QTDIR)
+		return devdirread(c, buf, n, NULL, 0, rootgen);
 	len = roottab[p].length;
 	if (offset < 0 || offset >= len) {
 		return 0;
diff --git a/kern/src/ns/dev.c b/kern/src/ns/dev.c
index 22df3f8..ebafa05 100644
--- a/kern/src/ns/dev.c
+++ b/kern/src/ns/dev.c
@@ -96,7 +96,15 @@
  *
  * TODO(cross): Document devgen and clean this mess up. Devgen should probably
  * be removed and replaced with a smarter data structure.
- */
+ *
+ * Keep in mind that the expected behavior of gen functions that interoperate
+ * with dev functions (e.g. devdirread()) is that files are directly genned, but
+ * not directories.  Directories will fail to gen, and devstat() just makes
+ * something up.  See also:
+ * https://github.com/brho/plan9/blob/89d43d2262ad43eb4b26c2a8d6a27cfeddb33828/nix/sys/src/nix/port/dev.c#L74
+ *
+ * The comment about genning a file's siblings needs a grain of salt too.  Look
+ * through ipgen().  I think it's what I call "direct genning." */
 int
 devgen(struct chan *c, char *unused_name, struct dirtab *tab, int ntab,
        int i, struct dir *dp)