One more step toward a full-featured kinit; do lots of cleanups

diff --git a/kinit/Makefile b/kinit/Makefile
index 0771568..6ac41ea 100644
--- a/kinit/Makefile
+++ b/kinit/Makefile
@@ -13,7 +13,8 @@
 	$(SRCROOT)/ipconfig/libipconfig.a \
 	$(KLIBC) $(LIBGCC)
 PROGS	= kinit
-OBJS	= kinit.o do_mounts.o nfsroot.o
+OBJS	= kinit.o do_mounts.o nfsroot.o getintfile.o initrd.o \
+	  open.o readfile.o
 
 all: $(PROGS)
 
diff --git a/kinit/do_mounts.c b/kinit/do_mounts.c
index 5d5bf8e..150f0cd 100644
--- a/kinit/do_mounts.c
+++ b/kinit/do_mounts.c
@@ -14,7 +14,6 @@
 
 #define BUF_SZ		4096
 
-static const char *progname = "VFS";
 static const int do_devfs; // FIXME
 
 /* Find dev_t for e.g. "hda,NULL" or "hdb,2" */
@@ -195,7 +194,7 @@
 }
 
 /* Create the device node "name" */
-static int
+int
 create_dev(const char *name, dev_t dev, const char *devfs_name)
 {
 	char path[BUF_SZ];
@@ -221,70 +220,82 @@
 	return symlink(path + 5, name);
 }
 
-/* get a list of all filesystems to try to mount root with */
-static void
-get_fs_names(int argc, char *argv[], char *buf)
+/* mount a filesystem, possibly trying a set of different types */
+const char *
+mount_block(const char *source, const char *target,
+	    const char *type, unsigned long flags,
+	    const void *data)
 {
-	char *s, *p;
-	char line[BUF_SZ];
-	const char *root_fs_names;
-	FILE *fp;
+	char *fslist, *p, *ep;
+	const char *rp;
+	ssize_t fsbytes;
 
-	root_fs_names = get_arg(argc, argv, "rootfstype=");
-	if (root_fs_names) {
-		strcpy(buf, root_fs_names);
-		for (s = buf; *s; s++) {
-			if (*s == ',')
-				*s = 0;
-		}
-	} else {
-		buf[0] = 0;
-		if ((fp = fopen("/proc/filesystems", "r")) == NULL)
-			goto done;
-
-		s = buf;
-		while (fgets(line, BUF_SZ, fp)) {
-			if (line[0] != '\t')
-				continue;
-			p = strchr(line, '\n');
-			if (!p)
-				continue;
-
-			*p = 0;
-			strcpy(s, line + 1);
-			s += strlen(line + 1) + 1;
-		}
+	if ( type ) {
+		int rv = mount(source, target, type, flags, data);
+		/* Mount readonly if necessary */
+		if ( rv == -1 && errno == EACCES && !(flags & MS_RDONLY) )
+			rv = mount(source, target, type, flags|MS_RDONLY, data);
+		return rv ? NULL : type;
 	}
- done:
-	return;
+
+	fsbytes = readfile("/proc/filesystems", &fslist);
+
+	errno = EINVAL;
+	if ( fsbytes < 0 )
+		return NULL;
+
+	p = fslist;
+	ep = fslist+fsbytes;
+
+	rp = NULL;
+
+	while ( p < ep ) {
+		type = p;
+		p = strchr(p, '\n');
+		if (!p)
+			break;
+		*p++ = '\0';
+		if (*type != '\t')/* We can't mount a block device as a "nodev" fs */
+			continue;
+		
+		type++;
+		rp = mount_block(source, target, type, flags, data);
+		if ( rp )
+			break;
+		if ( errno != EINVAL )
+			break;
+	}
+
+	free(fslist);
+	return rp;
 }
 
 /* mount the root filesystem from a block device */
 static int
 mount_block_root(int argc, char *argv[], dev_t root_dev,
-		 const char *root_dev_name, int flags)
+		 const char *root_dev_name, unsigned long flags)
 {
-	char fs_names[BUF_SZ];
-	const char *data;
-	char *p;
+	const char *data, *type;
+	const char *rp;
 
 	data = get_arg(argc, argv, "rootflags=");
-
 	create_dev("/dev/root", root_dev, root_dev_name);
 
-	get_fs_names(argc, argv, fs_names);
-retry:
-	for (p = fs_names; *p; p += strlen(p)+1) {
-		if (mount("/dev/root", "/root", p, flags, data) == 0)
-			goto out;
+	errno = 0;
 
-		switch (errno) {
-			case -EACCES:
-				flags |= MS_RDONLY;
-				goto retry;
-			case -EINVAL:
-				continue;
-		}
+	type = get_arg(argc, argv, "rootfstype=");
+	if ( type ) {
+		if ( (rp = mount_block("/dev/root", "/root", type, flags, data)) )
+			goto ok;
+		if ( errno != EINVAL )
+			goto bad;
+	}
+	
+	if ( !errno && (rp = mount_block("/dev/root", "/root", NULL, flags, data)) )
+		goto ok;
+
+ bad:
+	if ( errno != EINVAL ) {
 	        /*
 		 * Allow the user to distinguish between failed open
 		 * and bad superblock on root device.
@@ -292,32 +303,24 @@
 		fprintf(stderr, "%s: Cannot open root device \"%04x\"\n",
 			progname, root_dev);
 		return -errno;
+	} else {
+		fprintf(stderr, "%s: Unable to mount root fs on \"%04x\"\n",
+			progname, root_dev);
+		return -ESRCH;
 	}
-	fprintf(stderr, "%s: Unable to mount root fs on \"%04x\"\n",
-		progname, root_dev);
-	return -ESRCH;
 
-out:
+ok:
 	printf("%s: Mounted root (%s filesystem)%s.\n",
-	       progname, p, (flags & MS_RDONLY) ? " readonly" : "");
+	       progname, rp, (flags & MS_RDONLY) ? " readonly" : "");
 	return 0;
 }
 
 int
-do_mounts(int argc, char *argv[])
+mount_root(int argc, char *argv[], dev_t root_dev, const char *root_dev_name)
 {
-	const char *root_dev_name = get_arg(argc, argv, "root=");
-	int flags = MS_RDONLY | MS_VERBOSE;
-	dev_t root_dev = 0;
+	unsigned long flags = MS_RDONLY|MS_VERBOSE;
 	int ret;
 
-	if (root_dev_name) {
-		root_dev = name_to_dev_t(root_dev_name);
-		if (strncmp(root_dev_name, "/dev/", 5) == 0) {
-			root_dev_name += 5;
-		}
-	}
-
 	if (get_arg(argc, argv, "ro")) {
 		flags |= MS_RDONLY;
 	}
@@ -337,3 +340,39 @@
 	
 	return ret;
 }
+
+void
+md_run_setup(void)
+{
+	/* Muck around with md device(s) if necessary */
+}
+
+int do_mounts(int argc, char *argv[])
+{
+	const char *root_dev_name = get_arg(argc, argv, "root=");
+	const char *root_delay = get_arg(argc, argv, "rootdelay=");
+	dev_t root_dev = 0;
+
+	if (root_delay) {
+		int delay = atoi(root_delay);
+		fprintf(stderr, "Waiting %d s before mounting root device...\n", delay);
+		sleep(delay);
+	}
+
+	md_run_setup();
+
+	if (root_dev_name) {
+		root_dev = name_to_dev_t(root_dev_name);
+		if (strncmp(root_dev_name, "/dev/", 5) == 0) {
+			root_dev_name += 5;
+		}
+	}
+
+	if ( initrd_load(argc, argv, root_dev) )
+		return 0;
+
+	/* If it's a floppy(only?) and load_ramdisk is set, then load
+	   a non-initrd ramdisk */
+
+	return mount_root(argc, argv, root_dev, root_dev_name);
+}
diff --git a/kinit/do_mounts.h b/kinit/do_mounts.h
index fb65aae..5717afd 100644
--- a/kinit/do_mounts.h
+++ b/kinit/do_mounts.h
@@ -11,4 +11,19 @@
 #define	Root_RAM0	makedev(1,0)
 #define Root_NFS	makedev(0,255)
 
+int
+create_dev(const char *name, dev_t dev, const char *devfs_name);
+
+const char *
+mount_block(const char *source, const char *target,
+	    const char *type, unsigned long flags,
+	    const void *data);
+
+int
+mount_root(int argc, char *argv[], dev_t root_dev, const char *root_dev_name);
+
+int do_mounts(int argc, char *argv[]);
+
+int initrd_load(int argc, char *argv[], dev_t root_dev);
+
 #endif /* DO_MOUNTS_H */
diff --git a/kinit/getintfile.c b/kinit/getintfile.c
index 4a34a86..069870d 100644
--- a/kinit/getintfile.c
+++ b/kinit/getintfile.c
@@ -18,9 +18,9 @@
 	if ( !f )
 		return -1;
 
-	memset(buffer, 0, sizeof buffer);
-	fread(buffer, 1, sizeof buffer, f);
+	ep = buffer + fread(buffer, 1, sizeof buffer-1, f);
 	fclose(f);
+	*ep = '\0';
 
 	*val = strtol(buffer, &ep, 0);
 	if ( *ep && *ep != '\n' )
diff --git a/kinit/initrd.c b/kinit/initrd.c
index 31877cf..9aa5bd5 100644
--- a/kinit/initrd.c
+++ b/kinit/initrd.c
@@ -12,6 +12,7 @@
 #include <sys/stat.h>
 #include <sys/mount.h>
 #include <sys/ioctl.h>
+#include <sys/wait.h>
 #include "do_mounts.h"
 #include "kinit.h"
 
@@ -21,13 +22,13 @@
  * Copy the initrd to /dev/ram0, copy from the end to the beginning
  * to avoid taking 2x the memory.
  */
-int rd_copy_image(const char *path)
+int rd_copy_image(int ffd)
 {
-	int ffd = open(path, O_RDONLY);
 	int dfd = open("/dev/ram0", O_RDWR);
 	char buffer[BUF_SIZE];
 	struct stat st;
 	off_t bytes;
+	int rv = -1;
 
 	if ( ffd < 0 || dfd < 0 )
 		goto barf;
@@ -47,27 +48,33 @@
 		ftruncate(ffd, offset); /* Free up memory */
 		bytes = offset;
 	}
-
-	return 0;		/* Success! */
+	rv = 0;			/* Done! */
 
  barf:
 	close(ffd);
 	close(dfd);
-	return -1;
+	return rv;
 }
 
 /*
  * Run /linuxrc, for emulation of old-style initrd
  */
-int run_linuxrc(const char *ramdisk)
+static int
+run_linuxrc(int argc, char *argv[], dev_t root_dev)
 {
 	int root_fd, old_fd;
 	pid_t pid;
-	int err;
 	long realroot = Root_RAM0;
+	const char *ramdisk_name = "/dev/ram0";
+	FILE *fp;
 
-	if ( err = mount_block_root(ramdisk, root_mountflags & ~MS_RDONLY) )
-		return err;
+	if ( !mount_block(ramdisk_name, "/", NULL, MS_VERBOSE, NULL) )
+		return -errno;
+
+	/* Write the current "real root device" out to procfs */
+	fp = fopen("/proc/sys/kernel/real-root-dev", "w");
+	fprintf(fp, "%lu", (unsigned long)root_dev);
+	fclose(fp);
 
 	mkdir("/old", 0700);
 	root_fd = open_cloexec("/", O_RDONLY|O_DIRECTORY, 0);
@@ -104,37 +111,44 @@
 	close(old_fd);
 
 	getintfile("/proc/sys/kernel/real-root-dev", &realroot);
-	mount_root((dev_t)realroot);
+	mount_root(argc, argv, (dev_t)realroot, NULL);
 
 	/* If /root/initrd exists, move the initrd there, otherwise discard */
 	if ( !mount("/old", "/root/initrd", NULL, MS_MOVE, NULL) ) {
 		/* We're good */
 	} else {
-		int olddev = open(ramdisk, O_RDWR);
+		int olddev = open(ramdisk_name, O_RDWR);
 		umount2("/old", MNT_DETACH);
 		if ( olddev < 0 ||
 		     ioctl(olddev, BLKFLSBUF, (long)0) ||
 		     close(olddev) ) {
-			/* Print error message */
+			fprintf(stderr, "%s: Cannot flush initrd contents\n", progname);
 		}
 	}
+
+	return 0;
 }
 
 
-int initrd_load(dev_t *root_dev)
+int initrd_load(int argc, char *argv[], dev_t root_dev)
 {
-	int mount_initrd = 1;
+	int initrd_fd;
 
-	if ( mount_initrd ) {
-		create_dev("/dev/ram0", Root_RAM0, NULL);
-
-		if ( rd_copy_image("/initrd.image") == 0 && *root_dev != Root_RAM0 ) {
-			run_linuxrc(root_dev)
-				return 1;
-		}
+	initrd_fd = open("/initrd.image", O_RDWR);
+	if ( initrd_fd < 0 )
+		return 0;
+	
+	create_dev("/dev/ram0", Root_RAM0, NULL);
+	
+	if ( rd_copy_image(initrd_fd) ||
+	     unlink("/initrd.image") )
+		return 0;
+	
+	if ( root_dev != Root_RAM0 ) {
+		run_linuxrc(argc, argv, root_dev);
+		return 1;
 	}
 
-	unlink("/initrd.image");
 	return 0;
 }
 					
diff --git a/kinit/kinit.c b/kinit/kinit.c
index b3be538..738d40f 100644
--- a/kinit/kinit.c
+++ b/kinit/kinit.c
@@ -11,7 +11,7 @@
 #include "kinit.h"
 #include "ipconfig.h"
 
-static const char *progname = "kinit";
+const char *progname = "kinit";
 int mnt_procfs;
 int mnt_sysfs;
 
@@ -193,6 +193,8 @@
 int init_argc;
 char **init_argv;
 
+extern ssize_t readfile(const char *, char **);
+
 int main(int argc, char *argv[])
 {
 	char **cmdv;
@@ -202,6 +204,11 @@
 	int ret = 0;
 	int cmdc;
 	int fd;
+	
+	ret = readfile("/proc/cmdline", &cmdline);
+	printf("Got: len = %d data = \"%s\"\n", ret, cmdline);
+
+	exit(0);
 
 	/* Default parameters for anything init-like we execute */
 	init_argc = argc;
diff --git a/kinit/kinit.h b/kinit/kinit.h
index 5cead89..ba19af4 100644
--- a/kinit/kinit.h
+++ b/kinit/kinit.h
@@ -2,6 +2,8 @@
  * kinit/kinit.h
  */
 
+#include <stddef.h>
+
 extern void dump_args(int argc, char *argv[]);
 extern int do_mounts(int argc, char *argv[]);
 extern int mount_nfs_root(int argc, char *argv[], int flags);
@@ -12,6 +14,12 @@
 
 extern int init_argc;
 extern char **init_argv;
+extern const char *progname;
+
+int open_cloexec(const char *path, int flags, mode_t mode);
+int getintfile(const char *path, long *val);
+ssize_t readfile(const char *path, char **pptr);
+ssize_t freadfile(FILE *f, char **pptr);
 
 #undef INI_DEBUG
 
diff --git a/kinit/readfile.c b/kinit/readfile.c
new file mode 100644
index 0000000..98bb800
--- /dev/null
+++ b/kinit/readfile.c
@@ -0,0 +1,93 @@
+/*
+ * readfile.c
+ *
+ * Read the entire contents of a file into malloc'd storage.  This
+ * is mostly useful for things like /proc files where we can't just
+ * fstat() to get the length and then mmap().
+ *
+ * Returns the number of bytes read, or -1 on error.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+ssize_t freadfile(FILE *f, char **pp)
+{
+  size_t bs;			/* Decent starting point... */
+  size_t bf;			/* Bytes free */
+  size_t bu = 0;		/* Bytes used */
+  char *buffer, *nb;
+  size_t rv;
+  int old_errno = errno;
+#if 0
+  struct stat st;
+
+  if ( !fstat(fileno(f), &st) && S_ISREG(st.st_mode) && st.st_size )
+    bs = st.st_size+1;
+  else
+#endif
+    bs = BUFSIZ;		/* A guess as good as any */
+
+  bf = bs;
+  buffer = malloc(bs);
+
+  if ( !buffer )
+    return -1;
+  
+  for (;;) {
+    errno = 0;
+
+    while ( bf && (rv = _fread(buffer+bu, bf, f)) ) {
+      bu += rv;
+      bf -= rv;
+    }
+  
+    if ( errno && errno != EINTR && errno != EAGAIN ) {
+      /* error */
+      free(buffer);
+      return -1;
+    }
+
+    if ( bf ) {
+      /* Hit EOF, no error */
+
+      /* Try to free superfluous memory */
+      if ( (nb = realloc(buffer, bu+1)) )
+	buffer = nb;
+
+      /* Null-terminate result for good measure */
+      buffer[bu] = '\0';
+
+      *pp = buffer;
+      errno = old_errno;
+      return bu;
+    }
+
+    /* Double the size of the buffer */
+    bf += bs;
+    bs += bs;
+    if ( !(nb = realloc(buffer, bs)) ) {
+      /* out of memory error */
+      free(buffer);
+      return -1;
+    }
+    buffer = nb;
+  }
+}
+
+ssize_t readfile(const char *filename, char **pp)
+{
+  FILE *f = fopen(filename, "r");
+  ssize_t rv;
+
+  if ( !f )
+    return -1;
+
+  rv = freadfile(f, pp);
+
+  fclose(f);
+
+  return rv;
+}