validate: support uci lists in cli client and use dt_parse() return value to decide...
[project/ubox.git] / mount_root.c
index dc05368..8868e40 100644 (file)
@@ -27,6 +27,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/mount.h>
+#include <sys/wait.h>
 
 #include <asm/byteorder.h>
 
@@ -78,6 +79,59 @@ static void foreachdir(const char *dir, int (*cb)(const char*))
        cb(dir);
 }
 
+static int find_overlay_mount(char *overlay)
+{
+       FILE *fp = fopen("/proc/mounts", "r");
+       static char line[256];
+       int ret = -1;
+
+       if(!fp)
+               return ret;
+
+       while (ret && fgets(line, sizeof(line), fp))
+               if (!strncmp(line, overlay, strlen(overlay)))
+                       ret = 0;
+
+       fclose(fp);
+
+       return ret;
+}
+
+static char* find_mount(char *mp)
+{
+       FILE *fp = fopen("/proc/mounts", "r");
+       static char line[256];
+       char *point = NULL;
+
+       if(!fp)
+               return NULL;
+
+       while (fgets(line, sizeof(line), fp)) {
+               char *s, *t = strstr(line, " ");
+
+               if (!t) {
+                       fclose(fp);
+                       return NULL;
+               }
+               t++;
+               s = strstr(t, " ");
+               if (!s) {
+                       fclose(fp);
+                       return NULL;
+               }
+               *s = '\0';
+
+               if (!strcmp(t, mp)) {
+                       fclose(fp);
+                       return t;
+               }
+       }
+
+       fclose(fp);
+
+       return point;
+}
+
 static char* find_mount_point(char *block, char *fs)
 {
        FILE *fp = fopen("/proc/mounts", "r");
@@ -93,13 +147,16 @@ static char* find_mount_point(char *block, char *fs)
                        char *p = &line[len + 1];
                        char *t = strstr(p, " ");
 
-                       if (!t)
+                       if (!t) {
+                               fclose(fp);
                                return NULL;
+                       }
 
                        *t = '\0';
                        t++;
 
                        if (fs && strncmp(t, fs, strlen(fs))) {
+                               fclose(fp);
                                ERROR("block is mounted with wrong fs\n");
                                return NULL;
                        }
@@ -196,40 +253,10 @@ err_out:
        return ret;
 }
 
-static int mtd_erase(const char *mtd)
-{
-       int fd = open(mtd, O_RDWR | O_SYNC);
-       struct mtd_info_user i;
-       struct erase_info_user e;
-       int ret;
-
-       if (!fd) {
-               ERROR("failed to open %s: %s\n", mtd, strerror(errno));
-               return -1;
-       }
-
-       ret = ioctl(fd, MEMGETINFO, &i);
-       if (ret) {
-               ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno));
-               return -1;
-       }
-
-       e.length = i.erasesize;
-       for (e.start = 0; e.start < i.size; e.start += i.erasesize) {
-               ioctl(fd, MEMUNLOCK, &e);
-               if(ioctl(fd, MEMERASE, &e))
-                       ERROR("Failed to erase block on %s at 0x%x\n", mtd, e.start);
-       }
-
-       close(fd);
-       return 0;
-}
-
-static int do_mount_jffs2(void)
+static int mtd_mount_jffs2(void)
 {
        char rootfs_data[32];
 
-
        if (mkdir("/tmp/overlay", 0755)) {
                ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno));
                return -1;
@@ -321,7 +348,7 @@ static int mount_move(char *oldroot, char *newroot, char *dir)
        char newdir[64];
        int ret;
 
-       DEBUG(2, "%s %s\n", oldroot, dir);
+       DEBUG(2, "%s %s %s\n", oldroot, newroot, dir);
 
        snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir);
        snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir);
@@ -367,7 +394,6 @@ static int pivot(char *new, char *old)
        return 0;
 }
 
-
 static int fopivot(char *rw_root, char *ro_root)
 {
        char overlay[64], lowerdir[64];
@@ -404,6 +430,9 @@ static int switch2jffs(void)
 {
        char mtd[32];
 
+       if (!find_mtd_block("rootfs_patches", mtd, sizeof(mtd)))
+               return 0;
+
        if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
                ERROR("no rootfs_data was found\n");
                return -1;
@@ -471,55 +500,33 @@ static int handle_whiteout(const char *dir)
        return 0;
 }
 
-static int main_switch2jffs(int argc, char **argv)
+static int mtd_erase(const char *mtd)
 {
-       char mtd[32];
-       char *mp;
-       int ret = -1;
-
-       if (check_fs_exists("overlay")) {
-               ERROR("overlayfs not found\n");
-               return ret;
-       }
+       int fd = open(mtd, O_RDWR | O_SYNC);
+       struct mtd_info_user i;
+       struct erase_info_user e;
+       int ret;
 
-       find_mtd_block("rootfs_data", mtd, sizeof(mtd));
-       mp = find_mount_point(mtd, NULL);
-       if (mp) {
-               LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
+       if (!fd) {
+               ERROR("failed to open %s: %s\n", mtd, strerror(errno));
                return -1;
        }
 
-       if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
-               ERROR("no rootfs_data was found\n");
-               return ret;
+       ret = ioctl(fd, MEMGETINFO, &i);
+       if (ret) {
+               ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno));
+               return -1;
        }
 
-       switch (jffs2_ready(mtd)) {
-       case FS_NONE:
-               ERROR("no jffs2 marker found\n");
-               /* fall through */
-
-       case FS_DEADCODE:
-               ret = switch2jffs();
-               if (!ret) {
-                       DEBUG(1, "doing fo cleanup\n");
-                       umount2("/tmp/root", MNT_DETACH);
-                       foreachdir("/overlay/", handle_whiteout);
-               }
-               break;
-
-       case FS_JFFS2:
-               ret = do_mount_jffs2();
-               if (ret)
-                       break;
-               if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
-                       ERROR("switching to jffs2 failed\n");
-                       ret = -1;
-               }
-               break;
+       e.length = i.erasesize;
+       for (e.start = 0; e.start < i.size; e.start += i.erasesize) {
+               ioctl(fd, MEMUNLOCK, &e);
+               if(ioctl(fd, MEMERASE, &e))
+                       ERROR("Failed to erase block on %s at 0x%x\n", mtd, e.start);
        }
 
-       return ret;
+       close(fd);
+       return 0;
 }
 
 static int ask_user(int argc, char **argv)
@@ -582,7 +589,7 @@ static int main_jffs2reset(int argc, char **argv)
 
        mp = find_mount_point(mtd, "jffs2");
        if (mp) {
-               LOG("%s is mounted as %s, only ereasing files\n", mtd, mp);
+               LOG("%s is mounted as %s, only erasing files\n", mtd, mp);
                foreachdir(mp, handle_rmdir);
                mount(mp, "/", NULL, MS_REMOUNT, 0);
        } else {
@@ -625,6 +632,126 @@ static int main_jffs2mark(int argc, char **argv)
        }
 
        return 0;
+ }
+static int main_switch2jffs(int argc, char **argv)
+{
+       char mtd[32];
+       char *mp;
+       int ret = -1;
+
+       if (find_overlay_mount("overlayfs:/tmp/root"))
+               return -1;
+
+       if (check_fs_exists("overlay")) {
+               ERROR("overlayfs not found\n");
+               return ret;
+       }
+
+       find_mtd_block("rootfs_data", mtd, sizeof(mtd));
+       mp = find_mount_point(mtd, NULL);
+       if (mp) {
+               LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
+               return -1;
+       }
+
+       if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
+               ERROR("no rootfs_data was found\n");
+               return ret;
+       }
+
+       switch (jffs2_ready(mtd)) {
+       case FS_NONE:
+               ERROR("no jffs2 marker found\n");
+               /* fall through */
+
+       case FS_DEADCODE:
+               ret = switch2jffs();
+               if (!ret) {
+                       DEBUG(1, "doing fo cleanup\n");
+                       umount2("/tmp/root", MNT_DETACH);
+                       foreachdir("/overlay/", handle_whiteout);
+               }
+               break;
+
+       case FS_JFFS2:
+               ret = mtd_mount_jffs2();
+               if (ret)
+                       break;
+               if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
+                       ERROR("switching to jffs2 failed\n");
+                       ret = -1;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+static int extroot(const char *prefix)
+{
+       char block_path[32];
+       char kmod_loader[64];
+       struct stat s;
+       pid_t pid;
+
+       sprintf(block_path, "%s/sbin/block", prefix);
+
+       if (stat(block_path, &s))
+               return -1;
+
+       sprintf(kmod_loader, "/sbin/kmodloader %s/etc/modules-boot.d/ %s", prefix, prefix);
+       system(kmod_loader);
+
+       pid = fork();
+       if (!pid) {
+               mkdir("/tmp/extroot", 0755);
+               execl(block_path, block_path, "extroot", NULL);
+               exit(-1);
+       } else if (pid > 0) {
+               int status;
+
+               waitpid(pid, &status, 0);
+               if (!WEXITSTATUS(status)) {
+                       if (find_mount("/tmp/extroot/mnt")) {
+                               mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT | MS_RDONLY, 0);
+
+                               mkdir("/tmp/extroot/mnt/proc", 0755);
+                               mkdir("/tmp/extroot/mnt/dev", 0755);
+                               mkdir("/tmp/extroot/mnt/sys", 0755);
+                               mkdir("/tmp/extroot/mnt/tmp", 0755);
+                               mkdir("/tmp/extroot/mnt/rom", 0755);
+
+                               if (mount_move("/tmp/extroot", "", "/mnt")) {
+                                       ERROR("moving pivotroot failed - continue normal boot\n");
+                                       umount("/tmp/extroot/mnt");
+                               } else if (pivot("/mnt", "/rom")) {
+                                       ERROR("switching to pivotroot failed - continue normal boot\n");
+                                       umount("/mnt");
+                               } else {
+                                       umount("/tmp/overlay");
+                                       rmdir("/tmp/overlay");
+                                       rmdir("/tmp/extroot/mnt");
+                                       rmdir("/tmp/extroot");
+                                       return 0;
+                               }
+                       } else if (find_mount("/tmp/extroot/overlay")) {
+                               if (mount_move("/tmp/extroot", "", "/overlay")) {
+                                       ERROR("moving extroot failed - continue normal boot\n");
+                                       umount("/tmp/extroot/overlay");
+                               } else if (fopivot("/overlay", "/rom")) {
+                                       ERROR("switching to extroot failed - continue normal boot\n");
+                                       umount("/overlay");
+                               } else {
+                                       umount("/tmp/overlay");
+                                       rmdir("/tmp/overlay");
+                                       rmdir("/tmp/extroot/overlay");
+                                       rmdir("/tmp/extroot");
+                                       return 0;
+                               }
+                       }
+               }
+       }
+       return -1;
 }
 
 int main(int argc, char **argv)
@@ -634,21 +761,31 @@ int main(int argc, char **argv)
 
        argv0 = basename(*argv);
 
-       if (!strcmp(basename(*argv), "switch2jffs"))
-               return main_switch2jffs(argc, argv);
-
        if (!strcmp(basename(*argv), "jffs2mark"))
                return main_jffs2mark(argc, argv);
 
        if (!strcmp(basename(*argv), "jffs2reset"))
                return main_jffs2reset(argc, argv);
 
-       if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
+       if (!strcmp(basename(*argv), "switch2jffs"))
+               return main_switch2jffs(argc, argv);
+
+       if (!getenv("PREINIT"))
+               return -1;
+
+       if (!find_mtd_block("rootfs_patches", mtd, sizeof(mtd))) {
+               ramoverlay();
+       } else if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
                if (!find_mtd_char("rootfs", mtd, sizeof(mtd)))
                        mtd_unlock(mtd);
                LOG("mounting /dev/root\n");
                mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
        } else {
+               if (!extroot("")) {
+                       fprintf(stderr, "mount_root: switched to extroot\n");
+                       return 0;
+               }
+
                switch (jffs2_ready(mtd)) {
                case FS_NONE:
                case FS_DEADCODE:
@@ -662,7 +799,13 @@ int main(int argc, char **argv)
                                return -1;
                        }
 
-                       do_mount_jffs2();
+                       mtd_mount_jffs2();
+
+                       if (!extroot("/tmp/overlay")) {
+                               fprintf(stderr, "mount_root: switched to extroot\n");
+                               return 0;
+                       }
+
                        DEBUG(1, "switching to jffs2\n");
                        if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
                                ERROR("switching to jffs2 failed - fallback to ramoverlay\n");