move files around
authorJohn Crispin <blogic@openwrt.org>
Thu, 3 Apr 2014 17:38:18 +0000 (18:38 +0100)
committerJohn Crispin <blogic@openwrt.org>
Thu, 3 Apr 2014 22:03:46 +0000 (23:03 +0100)
Signed-off-by: John Crispin <blogic@openwrt.org>
21 files changed:
CMakeLists.txt
backend/base.c [deleted file]
backend/extroot.c [deleted file]
backend/jffs2.c [deleted file]
backend/overlay.c [deleted file]
backend/snapshot.c [deleted file]
driver/mtd.c [deleted file]
driver/volume.c [deleted file]
driver/volume.h [deleted file]
lib/find.c [deleted file]
lib/mount.c [deleted file]
libfstools/extroot.c [new file with mode: 0644]
libfstools/find.c [new file with mode: 0644]
libfstools/jffs2.c [new file with mode: 0644]
libfstools/mount.c [new file with mode: 0644]
libfstools/mount_root.c [new file with mode: 0644]
libfstools/mtd.c [new file with mode: 0644]
libfstools/overlay.c [new file with mode: 0644]
libfstools/snapshot.c [new file with mode: 0644]
libfstools/volume.c [new file with mode: 0644]
libfstools/volume.h [new file with mode: 0644]

index 5266785102e9a5cb5b0a33f6e08a9ccd4f03b998..b81d66c12251cbeb913d0e645e1e73e8fcafd42d 100644 (file)
@@ -6,15 +6,14 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
 SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
 
 ADD_EXECUTABLE(fs-state fs-state.c
-               backend/base.c
-               backend/snapshot.c
-               backend/extroot.c
-               backend/jffs2.c
-               driver/volume.c
-               driver/mtd.c
-               lib/mount.c
-               lib/find.c)
-
+               libfstools/mount_root.c
+               libfstools/snapshot.c
+               libfstools/extroot.c
+               libfstools/jffs2.c
+               libfstools/volume.c
+               libfstools/mtd.c
+               libfstools/mount.c
+               libfstools/find.c)
 TARGET_LINK_LIBRARIES(fs-state ubox)
 INSTALL(TARGETS fs-state RUNTIME DESTINATION sbin)
 
diff --git a/backend/base.c b/backend/base.c
deleted file mode 100644 (file)
index 6bb503e..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <sys/mount.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "../fs-state.h"
-
-#include "../driver/volume.h"
-
-int
-backend_mount(char *name)
-{
-       struct backend *b = find_backend(name);
-
-       if (!b || !b->mount)
-               return -1;
-
-       return b->mount();
-}
-
-static int
-backend_info(char *name)
-{
-       struct backend *b = find_backend(name);
-
-       if (!b || !b->info)
-               return -1;
-
-       return b->info();
-}
-
-static int
-start(int argc, char **argv)
-{
-       struct volume *v = volume_find("rootfs_data");
-
-       if (!getenv("PREINIT"))
-               return -1;
-
-       if (!v) {
-               v = volume_find("rootfs");
-               volume_init(v);
-               fprintf(stderr, "mounting /dev/root\n");
-               mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
-               return 0;
-       }
-
-       extroot_prefix = "";
-       if (!backend_mount("extroot")) {
-               fprintf(stderr, "fs-state: switched to extroot\n");
-               return 0;
-       }
-
-       switch (volume_identify(v)) {
-       case FS_NONE:
-       case FS_DEADCODE:
-               return ramoverlay();
-
-       case FS_JFFS2:
-               backend_mount("overlay");
-               break;
-
-       case FS_SNAPSHOT:
-               backend_mount("snapshot");
-               break;
-       }
-
-       return 0;
-}
-
-static int
-stop(int argc, char **argv)
-{
-       if (!getenv("SHUTDOWN"))
-               return -1;
-
-       return 0;
-}
-
-static int
-done(int argc, char **argv)
-{
-       struct volume *v = volume_find("rootfs_data");
-
-       if (!v)
-               return -1;
-
-       switch (volume_identify(v)) {
-       case FS_NONE:
-       case FS_DEADCODE:
-               return jffs2_switch(argc, argv);
-       }
-
-       return 0;
-}
-
-static int
-info(int argc, char **argv)
-{
-       struct volume *v = volume_find("rootfs_data");
-
-       if (!v)
-               return -1;
-
-       switch (volume_identify(v)) {
-       case FS_SNAPSHOT:
-               backend_info("snapshot");
-               return 0;
-       }
-
-       return 0;
-}
-
-static struct backend start_backend = {
-       .name = "start",
-       .cli = start,
-};
-BACKEND(start_backend);
-
-static struct backend stop_backend = {
-       .name = "stop",
-       .cli = stop,
-};
-BACKEND(stop_backend);
-
-static struct backend done_backend = {
-       .name = "done",
-       .cli = done,
-};
-BACKEND(done_backend);
-
-static struct backend info_backend = {
-       .name = "info",
-       .cli = info,
-};
-BACKEND(info_backend);
diff --git a/backend/extroot.c b/backend/extroot.c
deleted file mode 100644 (file)
index 2ed9b37..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include "../fs-state.h"
-
-char const *extroot_prefix = NULL;
-
-static int mount_extroot(void)
-{
-       char block_path[32];
-       char kmod_loader[64];
-       struct stat s;
-       pid_t pid;
-
-       if (!extroot_prefix)
-               return -1;
-
-       sprintf(block_path, "%s/sbin/block", extroot_prefix);
-
-       if (stat(block_path, &s))
-               return -1;
-
-       sprintf(kmod_loader, "/sbin/kmodloader %s/etc/modules-boot.d/ %s", extroot_prefix, extroot_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")) {
-                                       fprintf(stderr, "moving pivotroot failed - continue normal boot\n");
-                                       umount("/tmp/extroot/mnt");
-                               } else if (pivot("/mnt", "/rom")) {
-                                       fprintf(stderr, "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")) {
-                                       fprintf(stderr, "moving extroot failed - continue normal boot\n");
-                                       umount("/tmp/extroot/overlay");
-                               } else if (fopivot("/overlay", "/rom")) {
-                                       fprintf(stderr, "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;
-}
-
-static struct backend extroot_backend = {
-       .name = "extroot",
-       .mount = mount_extroot,
-};
-BACKEND(extroot_backend);
diff --git a/backend/jffs2.c b/backend/jffs2.c
deleted file mode 100644 (file)
index 09c66cf..0000000
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-
-#include <asm/byteorder.h>
-
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <glob.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-
-#include "../fs-state.h"
-#include "../driver/volume.h"
-
-#define SWITCH_JFFS2 "/tmp/.switch_jffs2"
-
-void
-foreachdir(const char *dir, int (*cb)(const char*))
-{
-       char globdir[256];
-       glob_t gl;
-       int j;
-
-       if (dir[strlen(dir) - 1] == '/')
-               snprintf(globdir, 256, "%s*", dir);
-       else
-               snprintf(globdir, 256, "%s/*", dir);
-
-       if (!glob(globdir, GLOB_NOESCAPE | GLOB_MARK | GLOB_ONLYDIR, NULL, &gl))
-               for (j = 0; j < gl.gl_pathc; j++)
-                       foreachdir(gl.gl_pathv[j], cb);
-
-       cb(dir);
-}
-
-static int
-jffs2_mount(void)
-{
-       struct volume *v;
-
-       if (mkdir("/tmp/overlay", 0755)) {
-               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
-               return -1;
-       }
-
-       v = volume_find("rootfs_data");
-       if (!v) {
-               fprintf(stderr, "rootfs_data does not exist\n");
-               return -1;
-       }
-
-       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
-               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n", v->blk, strerror(errno));
-               return -1;
-       }
-
-       return volume_init(v);
-}
-
-static int
-switch2jffs(void)
-{
-       struct volume *v = volume_find("rootfs_data");
-       struct stat s;
-       int ret;
-
-       if (!stat(SWITCH_JFFS2, &s)) {
-               fprintf(stderr, "jffs2 switch already running\n");
-               return -1;
-       }
-
-       if (!v) {
-               fprintf(stderr, "no rootfs_data was found\n");
-               return -1;
-       }
-
-       creat("/tmp/.switch_jffs2", 0600);
-       ret = mount(v->blk, "/rom/overlay", "jffs2", MS_NOATIME, NULL);
-       unlink("/tmp/.switch_jffs2");
-       if (ret) {
-               fprintf(stderr, "failed - mount -t jffs2 %s /rom/overlay: %s\n", v->blk, strerror(errno));
-               return -1;
-       }
-
-       if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) {
-               fprintf(stderr, "failed - mount -o remount,ro none: %s\n", strerror(errno));
-               return -1;
-       }
-
-       system("cp -a /tmp/root/* /rom/overlay");
-
-       if (pivot("/rom", "/mnt")) {
-               fprintf(stderr, "failed - pivot /rom /mnt: %s\n", strerror(errno));
-               return -1;
-       }
-
-       if (mount_move("/mnt", "/tmp/root", "")) {
-               fprintf(stderr, "failed - mount -o move /mnt /tmp/root %s\n", strerror(errno));
-               return -1;
-       }
-
-       return fopivot("/overlay", "/rom");
-}
-
-int
-handle_whiteout(const char *dir)
-{
-       struct stat s;
-       char link[256];
-       ssize_t sz;
-       struct dirent **namelist;
-       int n;
-
-       n = scandir(dir, &namelist, NULL, NULL);
-
-       if (n < 1)
-               return -1;
-
-       while (n--) {
-               char file[256];
-
-               snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name);
-               if (!lstat(file, &s) && S_ISLNK(s.st_mode)) {
-                       sz = readlink(file, link, sizeof(link) - 1);
-                       if (sz > 0) {
-                               char *orig;
-
-                               link[sz] = '\0';
-                               orig = strstr(&file[1], "/");
-                               if (orig && !strcmp(link, "(overlay-whiteout)"))
-                                       unlink(orig);
-                       }
-               }
-               free(namelist[n]);
-       }
-       free(namelist);
-
-       return 0;
-}
-
-static int
-ask_user(int argc, char **argv)
-{
-       if ((argc < 2) || strcmp(argv[1], "-y")) {
-               fprintf(stderr, "This will erase all settings and remove any installed packages. Are you sure? [N/y]\n");
-               if (getchar() != 'y')
-                       return -1;
-       }
-       return 0;
-
-}
-
-static int
-handle_rmdir(const char *dir)
-{
-       struct stat s;
-       struct dirent **namelist;
-       int n;
-
-       n = scandir(dir, &namelist, NULL, NULL);
-
-       if (n < 1)
-               return -1;
-
-       while (n--) {
-               char file[256];
-
-               snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name);
-               if (!lstat(file, &s) && !S_ISDIR(s.st_mode))
-                       unlink(file);
-               free(namelist[n]);
-       }
-       free(namelist);
-
-       rmdir(dir);
-
-       return 0;
-}
-
-static int
-jffs2_reset(int argc, char **argv)
-{
-       struct volume *v;
-       char *mp;
-
-       if (ask_user(argc, argv))
-               return -1;
-
-       if (find_filesystem("overlay")) {
-               fprintf(stderr, "overlayfs not found\n");
-               return -1;
-       }
-
-       v = volume_find("rootfs_data");
-       if (!v) {
-               fprintf(stderr, "no rootfs_data was found\n");
-               return -1;
-       }
-
-       mp = find_mount_point(v->blk, "jffs2");
-       if (mp) {
-               fprintf(stderr, "%s is mounted as %s, only erasing files\n", v->blk, mp);
-               foreachdir(mp, handle_rmdir);
-               mount(mp, "/", NULL, MS_REMOUNT, 0);
-       } else {
-               fprintf(stderr, "%s is not mounted, erasing it\n", v->blk);
-               volume_erase_all(v);
-       }
-
-       return 0;
-}
-
-static int
-jffs2_mark(int argc, char **argv)
-{
-       __u32 deadc0de = __cpu_to_be32(0xdeadc0de);
-       struct volume *v;
-       size_t sz;
-       int fd;
-
-       if (ask_user(argc, argv))
-               return -1;
-
-       v = volume_find("rootfs_data");
-       if (!v) {
-               fprintf(stderr, "no rootfs_data was found\n");
-               return -1;
-       }
-
-       fd = open(v->blk, O_WRONLY);
-       fprintf(stderr, "%s - marking with deadc0de\n", v->blk);
-       if (!fd) {
-               fprintf(stderr, "opening %s failed\n", v->blk);
-               return -1;
-       }
-
-       sz = write(fd, &deadc0de, sizeof(deadc0de));
-       close(fd);
-
-       if (sz != 1) {
-               fprintf(stderr, "writing %s failed: %s\n", v->blk, strerror(errno));
-               return -1;
-       }
-
-       return 0;
-}
-
-int
-jffs2_switch(int argc, char **argv)
-{
-       struct volume *v;
-       char *mp;
-       int ret = -1;
-
-       if (find_overlay_mount("overlayfs:/tmp/root"))
-               return -1;
-
-       if (find_filesystem("overlay")) {
-               fprintf(stderr, "overlayfs not found\n");
-               return ret;
-       }
-
-       v = volume_find("rootfs_data");
-       mp = find_mount_point(v->blk, NULL);
-       if (mp) {
-               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
-               return -1;
-       }
-
-       switch (volume_identify(v)) {
-       case FS_NONE:
-               fprintf(stderr, "no jffs2 marker found\n");
-               /* fall through */
-
-       case FS_DEADCODE:
-               ret = switch2jffs();
-               if (!ret) {
-                       fprintf(stderr, "doing fo cleanup\n");
-                       umount2("/tmp/root", MNT_DETACH);
-                       foreachdir("/overlay/", handle_whiteout);
-               }
-               break;
-
-       case FS_JFFS2:
-               ret = jffs2_mount();
-               if (ret)
-                       break;
-               if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
-                       fprintf(stderr, "switching to jffs2 failed\n");
-                       ret = -1;
-               }
-               break;
-       }
-
-       return ret;
-}
-
-static int overlay_mount_fs(void)
-{
-       struct volume *v;
-
-       if (mkdir("/tmp/overlay", 0755)) {
-               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
-               return -1;
-       }
-
-       v = volume_find("rootfs_data");
-       if (!v) {
-               fprintf(stderr, "rootfs_data does not exist\n");
-               return -1;
-       }
-
-       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
-               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n",
-                               v->blk, strerror(errno));
-               return -1;
-       }
-
-       volume_init(v);
-
-       return -1;
-}
-
-static int overlay_mount(void)
-{
-       struct volume *v = volume_find("rootfs_data");;
-       char *mp;
-
-       if (!v)
-               return -1;
-
-       mp = find_mount_point(v->blk, NULL);
-       if (mp) {
-               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
-               return -1;
-       }
-
-       overlay_mount_fs();
-
-       extroot_prefix = "/tmp/overlay";
-       if (!backend_mount("extroot")) {
-               fprintf(stderr, "fs-state: switched to extroot\n");
-               return 0;
-       }
-
-       fprintf(stderr, "switching to jffs2\n");
-       if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
-               fprintf(stderr, "switching to jffs2 failed - fallback to ramoverlay\n");
-               return ramoverlay();
-       }
-
-       return -1;
-}
-
-static struct backend_handler jffs2_handlers[] = {
-{
-       .name = "jffs2reset",
-       .cli = jffs2_reset,
-}, {
-       .name = "jffs2mark",
-       .cli = jffs2_mark,
-}};
-
-static struct backend overlay_backend = {
-       .name = "overlay",
-       .num_handlers = ARRAY_SIZE(jffs2_handlers),
-       .handlers = jffs2_handlers,
-       .mount = overlay_mount,
-};
-BACKEND(overlay_backend);
diff --git a/backend/overlay.c b/backend/overlay.c
deleted file mode 100644 (file)
index 7cd308f..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-
-#include <asm/byteorder.h>
-
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <glob.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-
-#include "../fs-state.h"
-#include "../driver/volume.h"
-
-#define SWITCH_JFFS2 "/tmp/.switch_jffs2"
-
-void
-foreachdir(const char *dir, int (*cb)(const char*))
-{
-       char globdir[256];
-       glob_t gl;
-       int j;
-
-       if (dir[strlen(dir) - 1] == '/')
-               snprintf(globdir, 256, "%s*", dir);
-       else
-               snprintf(globdir, 256, "%s/*", dir);
-
-       if (!glob(globdir, GLOB_NOESCAPE | GLOB_MARK | GLOB_ONLYDIR, NULL, &gl))
-               for (j = 0; j < gl.gl_pathc; j++)
-                       foreachdir(gl.gl_pathv[j], cb);
-
-       cb(dir);
-}
-
-static int
-overlay_mount(struct volume *v, char *fs)
-{
-       if (mkdir("/tmp/overlay", 0755)) {
-               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
-               return -1;
-       }
-
-       if (mount(v->blk, "/tmp/overlay", fs, MS_NOATIME, NULL)) {
-               fprintf(stderr, "failed to mount -t %s %s /tmp/overlay: %s\n", fs, v->blk, strerror(errno));
-               return -1;
-       }
-
-       return volume_init(v);
-}
-
-static int
-switch2jffs(struct volume *v)
-{
-       struct stat s;
-       int ret;
-
-       if (!stat(SWITCH_JFFS2, &s)) {
-               fprintf(stderr, "jffs2 switch already running\n");
-               return -1;
-       }
-
-       creat("/tmp/.switch_jffs2", 0600);
-       ret = mount(v->blk, "/rom/overlay", "jffs2", MS_NOATIME, NULL);
-       unlink("/tmp/.switch_jffs2");
-       if (ret) {
-               fprintf(stderr, "failed - mount -t jffs2 %s /rom/overlay: %s\n", v->blk, strerror(errno));
-               return -1;
-       }
-
-       if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) {
-               fprintf(stderr, "failed - mount -o remount,ro none: %s\n", strerror(errno));
-               return -1;
-       }
-
-       system("cp -a /tmp/root/* /rom/overlay");
-
-       if (pivot("/rom", "/mnt")) {
-               fprintf(stderr, "failed - pivot /rom /mnt: %s\n", strerror(errno));
-               return -1;
-       }
-
-       if (mount_move("/mnt", "/tmp/root", "")) {
-               fprintf(stderr, "failed - mount -o move /mnt /tmp/root %s\n", strerror(errno));
-               return -1;
-       }
-
-       return fopivot("/overlay", "/rom");
-}
-
-int
-handle_whiteout(const char *dir)
-{
-       struct stat s;
-       char link[256];
-       ssize_t sz;
-       struct dirent **namelist;
-       int n;
-
-       n = scandir(dir, &namelist, NULL, NULL);
-
-       if (n < 1)
-               return -1;
-
-       while (n--) {
-               char file[256];
-
-               snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name);
-               if (!lstat(file, &s) && S_ISLNK(s.st_mode)) {
-                       sz = readlink(file, link, sizeof(link) - 1);
-                       if (sz > 0) {
-                               char *orig;
-
-                               link[sz] = '\0';
-                               orig = strstr(&file[1], "/");
-                               if (orig && !strcmp(link, "(overlay-whiteout)"))
-                                       unlink(orig);
-                       }
-               }
-               free(namelist[n]);
-       }
-       free(namelist);
-
-       return 0;
-}
-
-static int
-ask_user(int argc, char **argv)
-{
-       if ((argc < 2) || strcmp(argv[1], "-y")) {
-               fprintf(stderr, "This will erase all settings and remove any installed packages. Are you sure? [N/y]\n");
-               if (getchar() != 'y')
-                       return -1;
-       }
-       return 0;
-
-}
-
-int
-jffs2_switch(int argc, char **argv)
-{
-       struct volume *v;
-       char *mp;
-       int ret = -1;
-
-       if (find_overlay_mount("overlayfs:/tmp/root"))
-               return -1;
-
-       if (find_filesystem("overlay")) {
-               fprintf(stderr, "overlayfs not found\n");
-               return ret;
-       }
-
-       v = volume_find("rootfs_data");
-       mp = find_mount_point(v->blk, NULL);
-       if (mp) {
-               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
-               return -1;
-       }
-
-       switch (volume_identify(v)) {
-       case FS_NONE:
-               fprintf(stderr, "no jffs2 marker found\n");
-               /* fall through */
-
-       case FS_DEADCODE:
-               ret = switch2jffs();
-               if (!ret) {
-                       fprintf(stderr, "doing fo cleanup\n");
-                       umount2("/tmp/root", MNT_DETACH);
-                       foreachdir("/overlay/", handle_whiteout);
-               }
-               break;
-
-       case FS_JFFS2:
-               ret = overlay_mount(v, "jffs2");
-               if (ret)
-                       break;
-               if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
-                       fprintf(stderr, "switching to jffs2 failed\n");
-                       ret = -1;
-               }
-               break;
-       }
-
-       return ret;
-}
-
-static int overlay_mount_fs(void)
-{
-       struct volume *v;
-
-       if (mkdir("/tmp/overlay", 0755)) {
-               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
-               return -1;
-       }
-
-       v = volume_find("rootfs_data");
-       if (!v) {
-               fprintf(stderr, "rootfs_data does not exist\n");
-               return -1;
-       }
-
-       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
-               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n",
-                               v->blk, strerror(errno));
-               return -1;
-       }
-
-       volume_init(v);
-
-       return -1;
-}
-
-static int overlay_mount(void)
-{
-       struct volume *v = volume_find("rootfs_data");;
-       char *mp;
-
-       if (!v)
-               return -1;
-
-       mp = find_mount_point(v->blk, NULL);
-       if (mp) {
-               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
-               return -1;
-       }
-
-       overlay_mount_fs();
-
-       extroot_prefix = "/tmp/overlay";
-       if (!backend_mount("extroot")) {
-               fprintf(stderr, "fs-state: switched to extroot\n");
-               return 0;
-       }
-
-       fprintf(stderr, "switching to jffs2\n");
-       if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
-               fprintf(stderr, "switching to jffs2 failed - fallback to ramoverlay\n");
-               return ramoverlay();
-       }
-
-       return -1;
-}
-
-static struct backend overlay_backend = {
-       .name = "overlay",
-       .mount = overlay_mount,
-};
-BACKEND(overlay_backend);
diff --git a/backend/snapshot.c b/backend/snapshot.c
deleted file mode 100644 (file)
index 5873f5c..0000000
+++ /dev/null
@@ -1,602 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <sys/stat.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <mtd/mtd-user.h>
-
-#include <glob.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <libubox/list.h>
-#include <libubox/blob.h>
-#include <libubox/md5.h>
-
-#include "../fs-state.h"
-#include "../driver/volume.h"
-
-#define PATH_MAX       256
-#define OWRT           0x4f575254
-#define DATA           0x44415441
-#define CONF           0x434f4e46
-
-struct file_header {
-       uint32_t magic;
-       uint32_t type;
-       uint32_t seq;
-       uint32_t length;
-       uint32_t md5[4];
-};
-
-static inline int
-is_config(struct file_header *h)
-{
-       return ((h->magic == OWRT) && (h->type == CONF));
-}
-
-static inline int
-valid_file_size(int fs)
-{
-       if ((fs > 8 * 1024 * 1204) || (fs <= 0))
-               return -1;
-
-       return 0;
-}
-
-static void
-hdr_to_be32(struct file_header *hdr)
-{
-       uint32_t *h = (uint32_t *) hdr;
-       int i;
-
-       for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++)
-               h[i] = cpu_to_be32(h[i]);
-}
-
-static void
-be32_to_hdr(struct file_header *hdr)
-{
-       uint32_t *h = (uint32_t *) hdr;
-       int i;
-
-       for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++)
-               h[i] = be32_to_cpu(h[i]);
-}
-
-static int
-pad_file_size(struct volume *v, int size)
-{
-       int mod;
-
-       size += sizeof(struct file_header);
-       mod = size % v->block_size;
-       if (mod) {
-               size -= mod;
-               size += v->block_size;
-       }
-
-       return size;
-}
-
-static int
-verify_file_hash(char *file, uint32_t *hash)
-{
-       uint32_t md5[4];
-
-       if (md5sum(file, md5)) {
-               fprintf(stderr, "failed to generate md5 sum\n");
-               return -1;
-       }
-
-       if (memcmp(md5, hash, sizeof(md5))) {
-               fprintf(stderr, "failed to verify hash of %s.\n", file);
-               return -1;
-       }
-
-       return 0;
-}
-
-static int
-snapshot_next_free(struct volume *v, uint32_t *seq)
-{
-       struct file_header hdr = { 0 };
-       int block = 0;
-
-       *seq = rand();
-
-       do {
-               if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
-                       fprintf(stderr, "scanning for next free block failed\n");
-                       return 0;
-               }
-
-               be32_to_hdr(&hdr);
-
-               if (hdr.magic != OWRT)
-                       break;
-
-               if (hdr.type == DATA && !valid_file_size(hdr.length)) {
-                       if (*seq + 1 != hdr.seq && block)
-                               return block;
-                       *seq = hdr.seq;
-                       block += pad_file_size(v, hdr.length) / v->block_size;
-               }
-       } while (hdr.type == DATA);
-
-       return block;
-}
-
-static int
-config_find(struct volume *v, struct file_header *conf, struct file_header *sentinel)
-{
-       uint32_t seq;
-       int i, next = snapshot_next_free(v, &seq);
-
-       conf->magic = sentinel->magic = 0;
-
-       if (!volume_read(v, conf, next, sizeof(*conf)))
-               be32_to_hdr(conf);
-
-       for (i = (v->size / v->block_size) - 1; i > 0; i--) {
-               if (volume_read(v, sentinel,  i * v->block_size, sizeof(*sentinel))) {
-                       fprintf(stderr, "failed to read header\n");
-                       return -1;
-               }
-               be32_to_hdr(sentinel);
-
-               if (sentinel->magic == OWRT && sentinel->type == CONF && !valid_file_size(sentinel->length)) {
-                       if (next == i)
-                               return -1;
-                       return i;
-               }
-       }
-
-       return -1;
-}
-
-static int
-snapshot_info(void)
-{
-       struct volume *v = volume_find("rootfs_data");
-       struct file_header hdr = { 0 }, conf;
-       int block = 0;
-
-       if (!v)
-               return -1;
-
-       fprintf(stderr, "sectors:\t%llu, block_size:\t%dK\n", v->size / v->block_size, v->block_size / 1024);
-       do {
-               if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
-                       fprintf(stderr, "scanning for next free block failed\n");
-                       return 0;
-               }
-
-               be32_to_hdr(&hdr);
-
-               if (hdr.magic != OWRT)
-                       break;
-
-               if (hdr.type == DATA)
-                       fprintf(stderr, "block %d:\tsnapshot entry, size: %d, sectors: %d, sequence: %d\n", block,  hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
-               else if (hdr.type == CONF)
-                       fprintf(stderr, "block %d:\tvolatile entry, size: %d, sectors: %d, sequence: %d\n", block,  hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
-
-               if (hdr.type == DATA && !valid_file_size(hdr.length))
-                       block += pad_file_size(v, hdr.length) / v->block_size;
-       } while (hdr.type == DATA);
-       block = config_find(v, &conf, &hdr);
-       if (block > 0)
-               fprintf(stderr, "block %d:\tsentinel entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
-
-       return 0;
-}
-
-static int
-snapshot_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type)
-{
-       uint32_t md5[4] = { 0 };
-       struct file_header hdr;
-       struct stat s;
-        char buffer[256];
-       int in = 0, len, offset;
-       int ret = -1;
-
-       if (stat(file, &s) || md5sum(file, md5)) {
-               fprintf(stderr, "stat failed on %s\n", file);
-               goto out;
-       }
-
-       if ((block * v->block_size) + pad_file_size(v, s.st_size) > v->size) {
-               fprintf(stderr, "upgrade is too big for the flash\n");
-               goto out;
-       }
-       volume_erase(v, block * v->block_size, pad_file_size(v, s.st_size));
-       volume_erase(v, block * v->block_size + pad_file_size(v, s.st_size), v->block_size);
-
-       hdr.length = s.st_size;
-       hdr.magic = OWRT;
-       hdr.type = type;
-       hdr.seq = seq;
-       memcpy(hdr.md5, md5, sizeof(md5));
-       hdr_to_be32(&hdr);
-
-       if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
-               fprintf(stderr, "failed to write header\n");
-               goto out;
-       }
-
-       in = open(file, O_RDONLY);
-       if (in < 1) {
-               fprintf(stderr, "failed to open %s\n", file);
-               goto out;
-       }
-
-       offset = (block * v->block_size) + sizeof(struct file_header);
-
-       while ((len = read(in, buffer, sizeof(buffer))) > 0) {
-               if (volume_write(v, buffer, offset, len) < 0)
-                       goto out;
-               offset += len;
-       }
-
-       ret = 0;
-
-out:
-       if (in > 0)
-               close(in);
-
-       return ret;
-}
-
-static int
-snapshot_read_file(struct volume *v, int block, char *file, uint32_t type)
-{
-       struct file_header hdr;
-       char buffer[256];
-       int out, offset = 0;
-
-       if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
-               fprintf(stderr, "failed to read header\n");
-               return -1;
-       }
-       be32_to_hdr(&hdr);
-
-       if (hdr.magic != OWRT)
-               return -1;
-
-       if (hdr.type != type)
-               return -1;
-
-       if (valid_file_size(hdr.length))
-               return -1;
-
-       out = open(file, O_WRONLY | O_CREAT, 0700);
-       if (!out) {
-               fprintf(stderr, "failed to open %s\n", file);
-               return -1;
-       }
-
-       while (hdr.length > 0) {
-               int len = sizeof(buffer);
-
-               if (hdr.length < len)
-                       len = hdr.length;
-
-               if ((volume_read(v, buffer, offset, len) != len) || (write(out, buffer, len) != len))
-                       return -1;
-
-               offset += len;
-               hdr.length -= len;
-       }
-
-       close(out);
-
-       if (verify_file_hash(file, hdr.md5)) {
-               fprintf(stderr, "md5 verification failed\n");
-               unlink(file);
-               return 0;
-       }
-
-        block += pad_file_size(v, hdr.length) / v->block_size;
-
-       return block;
-}
-
-static int
-sentinel_write(struct volume *v, uint32_t _seq)
-{
-       int ret, block;
-       struct stat s;
-       uint32_t seq;
-
-       if (stat("/tmp/config.tar.gz", &s)) {
-               fprintf(stderr, "failed to stat /tmp/config.tar.gz\n");
-               return -1;
-       }
-
-       snapshot_next_free(v, &seq);
-       if (_seq)
-               seq = _seq;
-       block = v->size / v->block_size;
-       block -= pad_file_size(v, s.st_size) / v->block_size;
-       if (block < 0)
-               block = 0;
-
-       ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
-       if (ret)
-               fprintf(stderr, "failed to write sentinel\n");
-       else
-               fprintf(stderr, "wrote /tmp/config.tar.gz sentinel\n");
-       return ret;
-}
-
-static int
-volatile_write(struct volume *v, uint32_t _seq)
-{
-       int block, ret;
-       uint32_t seq;
-
-       block = snapshot_next_free(v, &seq);
-       if (_seq)
-               seq = _seq;
-       if (block < 0)
-               block = 0;
-
-       ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
-       if (ret)
-               fprintf(stderr, "failed to write /tmp/config.tar.gz\n");
-       else
-               fprintf(stderr, "wrote /tmp/config.tar.gz\n");
-       return ret;
-}
-
-static int
-config_write(int argc, char **argv)
-{
-       struct volume *v = volume_find("rootfs_data");
-       int ret;
-
-       if (!v)
-               return -1;
-
-       ret = volatile_write(v, 0);
-       if (!ret)
-               ret = sentinel_write(v, 0);
-
-       return ret;
-}
-
-static int
-config_read(int argc, char **argv)
-{
-       struct volume *v = volume_find("rootfs_data");
-       struct file_header conf, sentinel;
-       int next, block, ret = 0;
-       uint32_t seq;
-
-       if (!v)
-               return -1;
-
-       block = config_find(v, &conf, &sentinel);
-       next = snapshot_next_free(v, &seq);
-       if (is_config(&conf) && conf.seq == seq)
-               block = next;
-       else if (!is_config(&sentinel) || sentinel.seq != seq)
-               return -1;
-
-       unlink("/tmp/config.tar.gz");
-       ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
-
-       if (ret < 1)
-               fprintf(stderr, "failed to read /tmp/config.tar.gz\n");
-
-       return ret;
-}
-
-static int
-snapshot_write(int argc, char **argv)
-{
-       struct volume *v = volume_find("rootfs_data");
-       int block, ret;
-       uint32_t seq;
-
-       if (!v)
-               return -1;
-
-       block = snapshot_next_free(v, &seq);
-       if (block < 0)
-               block = 0;
-
-       ret = snapshot_write_file(v, block, "/tmp/snapshot.tar.gz", seq + 1, DATA);
-       if (ret)
-               fprintf(stderr, "failed to write /tmp/snapshot.tar.gz\n");
-       else
-               fprintf(stderr, "wrote /tmp/snapshot.tar.gz\n");
-
-       return ret;
-}
-
-static int
-snapshot_mark(int argc, char **argv)
-{
-       __be32 owrt = cpu_to_be32(OWRT);
-       struct volume *v;
-       size_t sz;
-       int fd;
-
-       fprintf(stderr, "This will remove all snapshot data stored on the system. Are you sure? [N/y]\n");
-       if (getchar() != 'y')
-               return -1;
-
-       v = volume_find("rootfs_data");
-       if (!v) {
-               fprintf(stderr, "no rootfs_data was found\n");
-               return -1;
-       }
-
-       fd = open(v->blk, O_WRONLY);
-       fprintf(stderr, "%s - marking with 0x%08x\n", v->blk, owrt);
-       if (fd < 0) {
-               fprintf(stderr, "opening %s failed\n", v->blk);
-               return -1;
-       }
-
-       sz = write(fd, &owrt, sizeof(owrt));
-       close(fd);
-
-       if (sz != 1) {
-               fprintf(stderr, "writing %s failed: %s\n", v->blk, strerror(errno));
-               return -1;
-       }
-
-       return 0;
-}
-
-static int
-snapshot_read(int argc, char **argv)
-{
-       struct volume *v = volume_find("rootfs_data");;
-       int block = 0, ret = 0;
-       char file[64];
-
-       if (!v)
-               return -1;
-
-       if (argc > 1) {
-               block = atoi(argv[1]);
-               if (block >= (v->size / v->block_size)) {
-                       fprintf(stderr, "invalid block %d > %llu\n", block, v->size / v->block_size);
-                       goto out;
-               }
-               snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
-
-               ret = snapshot_read_file(v, block, file, DATA);
-               goto out;
-       }
-
-       do {
-               snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
-               block = snapshot_read_file(v, block, file, DATA);
-       } while (block > 0);
-
-out:
-       return ret;
-}
-
-static int
-snapshot_sync(void)
-{
-       struct volume *v = volume_find("rootfs_data");
-       struct file_header sentinel, conf;
-       int next, block = 0;
-       uint32_t seq;
-
-       if (!v)
-               return -1;
-
-       next = snapshot_next_free(v, &seq);
-       block = config_find(v, &conf, &sentinel);
-       if (is_config(&conf) && conf.seq != seq) {
-               conf.magic = 0;
-               volume_erase(v, next * v->block_size, 2 * v->block_size);
-       }
-
-       if (is_config(&sentinel) && (sentinel.seq != seq)) {
-               sentinel.magic = 0;
-               volume_erase(v, block * v->block_size, v->block_size);
-       }
-
-       if (!is_config(&conf) && !is_config(&sentinel)) {
-       //      fprintf(stderr, "no config found\n");
-       } else if (((is_config(&conf) && is_config(&sentinel)) &&
-                               (memcmp(conf.md5, sentinel.md5, sizeof(conf.md5)) || (conf.seq != sentinel.seq))) ||
-                       (is_config(&conf) && !is_config(&sentinel))) {
-               uint32_t seq;
-               int next = snapshot_next_free(v, &seq);
-               int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF);
-               if (ret > 0) {
-                       if (sentinel_write(v, conf.seq))
-                               fprintf(stderr, "failed to write sentinel data");
-               }
-       } else if (!is_config(&conf) && is_config(&sentinel) && next) {
-               int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
-               if (ret > 0)
-                       if (volatile_write(v, sentinel.seq))
-                               fprintf(stderr, "failed to write sentinel data");
-       } else
-               fprintf(stderr, "config in sync\n");
-
-       unlink("/tmp/config.tar.gz");
-
-       return 0;
-}
-
-static int
-_ramoverlay(char *rom, char *overlay)
-{
-       mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755");
-       return fopivot(overlay, rom);
-}
-
-static int
-snapshot_mount(void)
-{
-       snapshot_sync();
-       setenv("SNAPSHOT", "magic", 1);
-       _ramoverlay("/rom", "/overlay");
-       system("/sbin/snapshot unpack");
-       foreachdir("/overlay/", handle_whiteout);
-       mkdir("/volatile", 0700);
-       _ramoverlay("/rom", "/volatile");
-       mount_move("/rom/volatile", "/volatile", "");
-       mount_move("/rom/rom", "/rom", "");
-       system("/sbin/snapshot config_unpack");
-       foreachdir("/volatile/", handle_whiteout);
-       unsetenv("SNAPSHOT");
-       return -1;
-}
-
-static struct backend_handler snapshot_handlers[] = {
-{
-       .name = "config_read",
-       .cli = config_read,
-}, {
-       .name = "config_write",
-       .cli = config_write,
-}, {
-       .name = "read",
-       .cli = snapshot_read,
-}, {
-       .name = "write",
-       .cli = snapshot_write,
-}, {
-       .name = "mark",
-       .cli = snapshot_mark,
-}};
-
-static struct backend snapshot_backend = {
-       .name = "snapshot",
-       .num_handlers = ARRAY_SIZE(snapshot_handlers),
-       .handlers = snapshot_handlers,
-       .mount = snapshot_mount,
-       .info = snapshot_info,
-};
-BACKEND(snapshot_backend);
diff --git a/driver/mtd.c b/driver/mtd.c
deleted file mode 100644 (file)
index a0005d7..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <sys/mount.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <asm/byteorder.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <mtd/mtd-user.h>
-
-#include "../fs-state.h"
-
-#include "volume.h"
-
-#define PATH_MAX               256
-
-struct mtd_priv {
-       int     fd;
-       int     idx;
-       char    *chr;
-};
-
-static struct driver mtd_driver;
-
-static int mtd_open(const char *mtd, int block)
-{
-       FILE *fp;
-       char dev[PATH_MAX];
-       int i, ret, flags = O_RDWR | O_SYNC;
-
-       if ((fp = fopen("/proc/mtd", "r"))) {
-               while (fgets(dev, sizeof(dev), fp)) {
-                       if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
-                               snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
-                               ret = open(dev, flags);
-                               if (ret < 0) {
-                                       snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
-                                       ret = open(dev, flags);
-                               }
-                               fclose(fp);
-                               return ret;
-                       }
-               }
-               fclose(fp);
-       }
-
-       return open(mtd, flags);
-}
-
-static void mtd_volume_close(struct volume *v)
-{
-       struct mtd_priv *p = (struct mtd_priv*) v->priv;
-
-       if (!p->fd)
-               return;
-
-       close(p->fd);
-       p->fd = 0;
-}
-
-static int mtd_volume_load(struct volume *v)
-{
-       struct mtd_priv *p = (struct mtd_priv*) v->priv;
-       struct mtd_info_user mtdInfo;
-       struct erase_info_user mtdLockInfo;
-
-       if (p->fd)
-               return 0;
-
-       if (!p->chr)
-               return -1;
-
-       p->fd = mtd_open(p->chr, 0);
-       if (p->fd < 0) {
-               p->fd = 0;
-               fprintf(stderr, "Could not open mtd device: %s\n", p->chr);
-               return -1;
-       }
-
-       if (ioctl(p->fd, MEMGETINFO, &mtdInfo)) {
-               mtd_volume_close(v);
-               fprintf(stderr, "Could not get MTD device info from %s\n", p->chr);
-               return -1;
-       }
-
-       v->size = mtdInfo.size;
-       v->block_size = mtdInfo.erasesize;
-       switch (mtdInfo.type) {
-       case MTD_NORFLASH:
-               v->type = NORFLASH;
-               break;
-       case MTD_NANDFLASH:
-               v->type = NANDFLASH;
-               break;
-       case MTD_UBIVOLUME:
-               v->type = UBIVOLUME;
-               break;
-       default:
-               v->type = UNKNOWN_TYPE;
-               break;
-       }
-
-       mtdLockInfo.start = 0;
-       mtdLockInfo.length = v->size;
-       ioctl(p->fd, MEMUNLOCK, &mtdLockInfo);
-
-       return 0;
-}
-
-static char* mtd_find_index(char *name)
-{
-       FILE *fp = fopen("/proc/mtd", "r");
-       static char line[256];
-       char *index = NULL;
-
-       if(!fp)
-               return index;
-
-       while (!index && fgets(line, sizeof(line), fp)) {
-               if (strstr(line, name)) {
-                       char *eol = strstr(line, ":");
-
-                       if (!eol)
-                               continue;
-
-                       *eol = '\0';
-                       index = &line[3];
-               }
-       }
-
-       fclose(fp);
-
-       return index;
-}
-
-static int mtd_volume_find(struct volume *v, char *name)
-{
-       char *idx = mtd_find_index(name);
-       struct mtd_priv *p;
-       char buffer[32];
-
-       if (!idx)
-               return -1;
-
-       p = calloc(1, sizeof(struct mtd_priv));
-       if (!p)
-               return -1;
-
-       v->priv = p;
-       v->name = strdup(name);
-       v->drv = &mtd_driver;
-       p->idx = atoi(idx);
-
-       snprintf(buffer, sizeof(buffer), "/dev/mtdblock%s", idx);
-       v->blk = strdup(buffer);
-
-       snprintf(buffer, sizeof(buffer), "/dev/mtd%s", idx);
-       p->chr = strdup(buffer);
-
-       return 0;
-}
-
-static int mtd_volume_identify(struct volume *v)
-{
-       struct mtd_priv *p = (struct mtd_priv*) v->priv;
-       __u32 deadc0de;
-       __u16 jffs2;
-       size_t sz;
-
-       if (mtd_volume_load(v)) {
-               fprintf(stderr, "reading %s failed\n", v->name);
-               return -1;
-       }
-
-       sz = read(p->fd, &deadc0de, sizeof(deadc0de));
-
-       if (sz != sizeof(deadc0de)) {
-               fprintf(stderr, "reading %s failed: %s\n", v->name, strerror(errno));
-               return -1;
-       }
-
-       if (deadc0de == 0x4f575254)
-               return FS_SNAPSHOT;
-
-       deadc0de = __be32_to_cpu(deadc0de);
-       if (deadc0de == 0xdeadc0de) {
-               fprintf(stderr, "jffs2 is not ready - marker found\n");
-               return FS_DEADCODE;
-       }
-
-       jffs2 = __be16_to_cpu(deadc0de >> 16);
-       if (jffs2 == 0x1985) {
-               fprintf(stderr, "jffs2 is ready\n");
-               return FS_JFFS2;
-       }
-
-       if (v->type == UBIVOLUME && deadc0de == 0xffffffff) {
-               fprintf(stderr, "jffs2 is ready\n");
-               return FS_JFFS2;
-       }
-
-       fprintf(stderr, "No jffs2 marker was found\n");
-
-       return FS_NONE;
-}
-
-static int mtd_volume_erase(struct volume *v, int offset, int len)
-{
-       struct mtd_priv *p = (struct mtd_priv*) v->priv;
-       struct erase_info_user eiu;
-       int first_block, num_blocks;
-
-       if (mtd_volume_load(v))
-               return -1;
-
-       if (offset % v->block_size || len % v->block_size) {
-               fprintf(stderr, "mtd erase needs to be block aligned\n");
-               return -1;
-       }
-
-       first_block = offset / v->block_size;
-       num_blocks = len / v->block_size;
-       eiu.length = v->block_size;
-
-       for (eiu.start = first_block * v->block_size;
-                       eiu.start < v->size && eiu.start < (first_block + num_blocks) * v->block_size;
-                       eiu.start += v->block_size) {
-               fprintf(stderr, "erasing %x %x\n", eiu.start, v->block_size);
-               ioctl(p->fd, MEMUNLOCK, &eiu);
-               if (ioctl(p->fd, MEMERASE, &eiu))
-                       fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start);
-       }
-
-       mtd_volume_close(v);
-
-       return 0;
-}
-
-static int mtd_volume_erase_all(struct volume *v)
-{
-       mtd_volume_erase(v, 0, v->size);
-       mtd_volume_close(v);
-
-       return 0;
-}
-
-static int mtd_volume_init(struct volume *v)
-{
-       struct mtd_priv *p = (struct mtd_priv*) v->priv;
-       struct mtd_info_user mtdinfo;
-       int ret;
-
-       if (mtd_volume_load(v))
-               return -1;
-
-       ret = ioctl(p->fd, MEMGETINFO, &mtdinfo);
-       if (ret) {
-               fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", p->fd, strerror(errno));
-       } else {
-               struct erase_info_user mtdlock;
-
-               mtdlock.start = 0;
-               mtdlock.length = mtdinfo.size;
-               ioctl(p->fd, MEMUNLOCK, &mtdlock);
-       }
-
-       return ret;
-}
-
-static int mtd_volume_read(struct volume *v, void *buf, int offset, int length)
-{
-       struct mtd_priv *p = (struct mtd_priv*) v->priv;
-
-       if (mtd_volume_load(v))
-               return -1;
-
-       if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
-               fprintf(stderr, "lseek/read failed\n");
-               return -1;
-       }
-
-       if (read(p->fd, buf, length) == -1) {
-               fprintf(stderr, "read failed\n");
-               return -1;
-       }
-
-       return 0;
-}
-
-static int mtd_volume_write(struct volume *v, void *buf, int offset, int length)
-{
-       struct mtd_priv *p = (struct mtd_priv*) v->priv;
-
-       if (mtd_volume_load(v))
-               return -1;
-
-       if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
-               fprintf(stderr, "lseek/write failed at offset %d\n", offset);
-               perror("lseek");
-               return -1;
-       }
-
-       if (write(p->fd, buf, length) == -1) {
-               fprintf(stderr, "write failed\n");
-               return -1;
-       }
-
-       return 0;
-}
-
-static struct driver mtd_driver = {
-       .name = "mtd",
-       .find = mtd_volume_find,
-       .init = mtd_volume_init,
-       .erase = mtd_volume_erase,
-       .erase_all = mtd_volume_erase_all,
-       .read = mtd_volume_read,
-       .write = mtd_volume_write,
-       .identify = mtd_volume_identify,
-};
-DRIVER(mtd_driver);
diff --git a/driver/volume.c b/driver/volume.c
deleted file mode 100644 (file)
index 4dc0a8e..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <sys/mount.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "../fs-state.h"
-#include "volume.h"
-
-enum {
-       FLASH_NOR,
-       FLASH_NAND,
-};
-
-static LIST_HEAD(drivers);
-
-void
-volume_register_driver(struct driver *d)
-{
-       list_add(&d->list, &drivers);
-}
-
-struct volume* volume_find(char *name)
-{
-       struct volume *v = malloc(sizeof(struct volume));
-       struct driver *d;
-
-       if (!v)
-               return NULL;
-
-       list_for_each_entry(d, &drivers, list) {
-               memset(v, 0, sizeof(struct volume));
-
-               if (d->find && !d->find(v, name))
-                       return v;
-       }
-
-       free(v);
-
-       return NULL;
-}
diff --git a/driver/volume.h b/driver/volume.h
deleted file mode 100644 (file)
index 4fa5641..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _VOLUME_H__
-#define _VOLUME_H__
-
-#include <asm/byteorder.h>
-
-struct volume;
-
-typedef int (*volume_probe_t)(void);
-typedef int (*volume_init_t)(struct volume *v);
-typedef void (*volume_stop_t)(struct volume *v);
-typedef int (*volume_find_t)(struct volume *v, char *name);
-typedef int (*volume_identify_t)(struct volume *v);
-typedef int (*volume_read_t)(struct volume *v, void *buf, int offset, int length);
-typedef int (*volume_write_t)(struct volume *v, void *buf, int offset, int length);
-typedef int (*volume_erase_t)(struct volume *v, int start, int len);
-typedef int (*volume_erase_all_t)(struct volume *v);
-
-struct driver {
-        struct list_head        list;
-
-       char                    *name;
-       volume_probe_t          probe;
-       volume_init_t           init;
-       volume_stop_t           stop;
-       volume_find_t           find;
-       volume_identify_t       identify;
-       volume_read_t           read;
-       volume_write_t          write;
-       volume_erase_t          erase;
-       volume_erase_all_t      erase_all;
-};
-
-enum {
-       UNKNOWN_TYPE,
-       NANDFLASH,
-       NORFLASH,
-       UBIVOLUME,
-};
-
-struct volume {
-       struct driver   *drv;
-       void            *priv;
-       char            *name;
-       char            *blk;
-
-       __u64           size;
-       __u32           block_size;
-       int             type;
-};
-
-extern struct volume* volume_find(char *name);
-extern void volume_register_driver(struct driver *drv);
-
-static inline int volume_init(struct volume *v)
-{
-       if (v && v->drv->init)
-               return v->drv->init(v);
-       return -1;
-}
-
-static inline int volume_identify(struct volume *v)
-{
-       if (v && v->drv->identify)
-               return v->drv->identify(v);
-       return -1;
-}
-
-static inline int volume_erase(struct volume *v, int offset, int len)
-{
-       if (v && v->drv->erase)
-               return v->drv->erase(v, offset, len);
-       return -1;
-}
-
-static inline int volume_erase_all(struct volume *v)
-{
-       if (v && v->drv->erase_all)
-               return v->drv->erase_all(v);
-       return -1;
-}
-
-static inline int volume_read(struct volume *v, void *buf, int offset, int length)
-{
-       if (v && v->drv->read)
-               return v->drv->read(v, buf, offset, length);
-       return -1;
-}
-
-static inline int volume_write(struct volume *v, void *buf, int offset, int length)
-{
-       if (v && v->drv->write)
-               return v->drv->write(v, buf, offset, length);
-       return -1;
-}
-
-#define DRIVER(x)                                      \
-       static void __attribute__((constructor))        \
-       drv_register_##x(void) {                        \
-               volume_register_driver(&x);             \
-       }
-
-#endif
diff --git a/lib/find.c b/lib/find.c
deleted file mode 100644 (file)
index 35e37e7..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "../fs-state.h"
-
-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;
-}
-
-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;
-}
-
-char*
-find_mount_point(char *block, char *fs)
-{
-       FILE *fp = fopen("/proc/mounts", "r");
-       static char line[256];
-       int len = strlen(block);
-       char *point = NULL;
-
-       if(!fp)
-               return NULL;
-
-       while (fgets(line, sizeof(line), fp)) {
-               if (!strncmp(line, block, len)) {
-                       char *p = &line[len + 1];
-                       char *t = strstr(p, " ");
-
-                       if (!t) {
-                               fclose(fp);
-                               return NULL;
-                       }
-
-                       *t = '\0';
-                       t++;
-
-                       if (fs && strncmp(t, fs, strlen(fs))) {
-                               fclose(fp);
-                               fprintf(stderr, "block is mounted with wrong fs\n");
-                               return NULL;
-                       }
-                       point = p;
-
-                       break;
-               }
-       }
-
-       fclose(fp);
-
-       return point;
-}
-
-int
-find_filesystem(char *fs)
-{
-       FILE *fp = fopen("/proc/filesystems", "r");
-       static char line[256];
-       int ret = -1;
-
-       if (!fp) {
-               fprintf(stderr, "opening /proc/filesystems failed: %s\n", strerror(errno));
-               goto out;
-       }
-
-       while (ret && fgets(line, sizeof(line), fp))
-               if (strstr(line, fs))
-                       ret = 0;
-
-       fclose(fp);
-
-out:
-       return ret;
-}
diff --git a/lib/mount.c b/lib/mount.c
deleted file mode 100644 (file)
index e7b57f0..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "../fs-state.h"
-
-/* this is a raw syscall - man 2 pivot_root */
-extern int pivot_root(const char *new_root, const char *put_old);
-
-int
-mount_move(char *oldroot, char *newroot, char *dir)
-{
-#ifndef MS_MOVE
-#define MS_MOVE        (1 << 13)
-#endif
-       struct stat s;
-       char olddir[64];
-       char newdir[64];
-       int ret;
-
-       snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir);
-       snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir);
-
-       if (stat(olddir, &s) || !S_ISDIR(s.st_mode))
-               return -1;
-
-       if (stat(newdir, &s) || !S_ISDIR(s.st_mode))
-               return -1;
-
-       ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL);
-
-/*     if (ret)
-               fprintf(stderr, "failed %s %s: %s\n", olddir, newdir, strerror(errno));*/
-
-       return ret;
-}
-
-int
-pivot(char *new, char *old)
-{
-       char pivotdir[64];
-       int ret;
-
-       if (mount_move("", new, "/proc"))
-               return -1;
-
-       snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old);
-
-       ret = pivot_root(new, pivotdir);
-
-       if (ret < 0) {
-               fprintf(stderr, "pivot_root failed %s %s: %s\n", new, pivotdir, strerror(errno));
-               return -1;
-       }
-
-       mount_move(old, "", "/dev");
-       mount_move(old, "", "/tmp");
-       mount_move(old, "", "/sys");
-       mount_move(old, "", "/overlay");
-
-       return 0;
-}
-
-int
-fopivot(char *rw_root, char *ro_root)
-{
-       char overlay[64], lowerdir[64];
-
-       if (find_filesystem("overlay")) {
-               fprintf(stderr, "BUG: no suitable fs found\n");
-               return -1;
-       }
-
-       snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root);
-       snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s", rw_root);
-
-       if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) {
-               fprintf(stderr, "mount failed: %s\n", strerror(errno));
-               return -1;
-       }
-
-       return pivot("/mnt", ro_root);
-}
-
-int
-ramoverlay(void)
-{
-       mkdir("/tmp/root", 0755);
-       mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755");
-
-       return fopivot("/tmp/root", "/rom");
-}
diff --git a/libfstools/extroot.c b/libfstools/extroot.c
new file mode 100644 (file)
index 0000000..2ed9b37
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "../fs-state.h"
+
+char const *extroot_prefix = NULL;
+
+static int mount_extroot(void)
+{
+       char block_path[32];
+       char kmod_loader[64];
+       struct stat s;
+       pid_t pid;
+
+       if (!extroot_prefix)
+               return -1;
+
+       sprintf(block_path, "%s/sbin/block", extroot_prefix);
+
+       if (stat(block_path, &s))
+               return -1;
+
+       sprintf(kmod_loader, "/sbin/kmodloader %s/etc/modules-boot.d/ %s", extroot_prefix, extroot_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")) {
+                                       fprintf(stderr, "moving pivotroot failed - continue normal boot\n");
+                                       umount("/tmp/extroot/mnt");
+                               } else if (pivot("/mnt", "/rom")) {
+                                       fprintf(stderr, "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")) {
+                                       fprintf(stderr, "moving extroot failed - continue normal boot\n");
+                                       umount("/tmp/extroot/overlay");
+                               } else if (fopivot("/overlay", "/rom")) {
+                                       fprintf(stderr, "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;
+}
+
+static struct backend extroot_backend = {
+       .name = "extroot",
+       .mount = mount_extroot,
+};
+BACKEND(extroot_backend);
diff --git a/libfstools/find.c b/libfstools/find.c
new file mode 100644 (file)
index 0000000..35e37e7
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../fs-state.h"
+
+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;
+}
+
+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;
+}
+
+char*
+find_mount_point(char *block, char *fs)
+{
+       FILE *fp = fopen("/proc/mounts", "r");
+       static char line[256];
+       int len = strlen(block);
+       char *point = NULL;
+
+       if(!fp)
+               return NULL;
+
+       while (fgets(line, sizeof(line), fp)) {
+               if (!strncmp(line, block, len)) {
+                       char *p = &line[len + 1];
+                       char *t = strstr(p, " ");
+
+                       if (!t) {
+                               fclose(fp);
+                               return NULL;
+                       }
+
+                       *t = '\0';
+                       t++;
+
+                       if (fs && strncmp(t, fs, strlen(fs))) {
+                               fclose(fp);
+                               fprintf(stderr, "block is mounted with wrong fs\n");
+                               return NULL;
+                       }
+                       point = p;
+
+                       break;
+               }
+       }
+
+       fclose(fp);
+
+       return point;
+}
+
+int
+find_filesystem(char *fs)
+{
+       FILE *fp = fopen("/proc/filesystems", "r");
+       static char line[256];
+       int ret = -1;
+
+       if (!fp) {
+               fprintf(stderr, "opening /proc/filesystems failed: %s\n", strerror(errno));
+               goto out;
+       }
+
+       while (ret && fgets(line, sizeof(line), fp))
+               if (strstr(line, fs))
+                       ret = 0;
+
+       fclose(fp);
+
+out:
+       return ret;
+}
diff --git a/libfstools/jffs2.c b/libfstools/jffs2.c
new file mode 100644 (file)
index 0000000..9a06ff8
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <asm/byteorder.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glob.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "../fs-state.h"
+#include "volume.h"
+
+#define SWITCH_JFFS2 "/tmp/.switch_jffs2"
+
+void
+foreachdir(const char *dir, int (*cb)(const char*))
+{
+       char globdir[256];
+       glob_t gl;
+       int j;
+
+       if (dir[strlen(dir) - 1] == '/')
+               snprintf(globdir, 256, "%s*", dir);
+       else
+               snprintf(globdir, 256, "%s/*", dir);
+
+       if (!glob(globdir, GLOB_NOESCAPE | GLOB_MARK | GLOB_ONLYDIR, NULL, &gl))
+               for (j = 0; j < gl.gl_pathc; j++)
+                       foreachdir(gl.gl_pathv[j], cb);
+
+       cb(dir);
+}
+
+static int
+jffs2_mount(void)
+{
+       struct volume *v;
+
+       if (mkdir("/tmp/overlay", 0755)) {
+               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
+               return -1;
+       }
+
+       v = volume_find("rootfs_data");
+       if (!v) {
+               fprintf(stderr, "rootfs_data does not exist\n");
+               return -1;
+       }
+
+       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
+               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n", v->blk, strerror(errno));
+               return -1;
+       }
+
+       return volume_init(v);
+}
+
+static int
+switch2jffs(void)
+{
+       struct volume *v = volume_find("rootfs_data");
+       struct stat s;
+       int ret;
+
+       if (!stat(SWITCH_JFFS2, &s)) {
+               fprintf(stderr, "jffs2 switch already running\n");
+               return -1;
+       }
+
+       if (!v) {
+               fprintf(stderr, "no rootfs_data was found\n");
+               return -1;
+       }
+
+       creat("/tmp/.switch_jffs2", 0600);
+       ret = mount(v->blk, "/rom/overlay", "jffs2", MS_NOATIME, NULL);
+       unlink("/tmp/.switch_jffs2");
+       if (ret) {
+               fprintf(stderr, "failed - mount -t jffs2 %s /rom/overlay: %s\n", v->blk, strerror(errno));
+               return -1;
+       }
+
+       if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) {
+               fprintf(stderr, "failed - mount -o remount,ro none: %s\n", strerror(errno));
+               return -1;
+       }
+
+       system("cp -a /tmp/root/* /rom/overlay");
+
+       if (pivot("/rom", "/mnt")) {
+               fprintf(stderr, "failed - pivot /rom /mnt: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (mount_move("/mnt", "/tmp/root", "")) {
+               fprintf(stderr, "failed - mount -o move /mnt /tmp/root %s\n", strerror(errno));
+               return -1;
+       }
+
+       return fopivot("/overlay", "/rom");
+}
+
+int
+handle_whiteout(const char *dir)
+{
+       struct stat s;
+       char link[256];
+       ssize_t sz;
+       struct dirent **namelist;
+       int n;
+
+       n = scandir(dir, &namelist, NULL, NULL);
+
+       if (n < 1)
+               return -1;
+
+       while (n--) {
+               char file[256];
+
+               snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name);
+               if (!lstat(file, &s) && S_ISLNK(s.st_mode)) {
+                       sz = readlink(file, link, sizeof(link) - 1);
+                       if (sz > 0) {
+                               char *orig;
+
+                               link[sz] = '\0';
+                               orig = strstr(&file[1], "/");
+                               if (orig && !strcmp(link, "(overlay-whiteout)"))
+                                       unlink(orig);
+                       }
+               }
+               free(namelist[n]);
+       }
+       free(namelist);
+
+       return 0;
+}
+
+static int
+ask_user(int argc, char **argv)
+{
+       if ((argc < 2) || strcmp(argv[1], "-y")) {
+               fprintf(stderr, "This will erase all settings and remove any installed packages. Are you sure? [N/y]\n");
+               if (getchar() != 'y')
+                       return -1;
+       }
+       return 0;
+
+}
+
+static int
+handle_rmdir(const char *dir)
+{
+       struct stat s;
+       struct dirent **namelist;
+       int n;
+
+       n = scandir(dir, &namelist, NULL, NULL);
+
+       if (n < 1)
+               return -1;
+
+       while (n--) {
+               char file[256];
+
+               snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name);
+               if (!lstat(file, &s) && !S_ISDIR(s.st_mode))
+                       unlink(file);
+               free(namelist[n]);
+       }
+       free(namelist);
+
+       rmdir(dir);
+
+       return 0;
+}
+
+static int
+jffs2_reset(int argc, char **argv)
+{
+       struct volume *v;
+       char *mp;
+
+       if (ask_user(argc, argv))
+               return -1;
+
+       if (find_filesystem("overlay")) {
+               fprintf(stderr, "overlayfs not found\n");
+               return -1;
+       }
+
+       v = volume_find("rootfs_data");
+       if (!v) {
+               fprintf(stderr, "no rootfs_data was found\n");
+               return -1;
+       }
+
+       mp = find_mount_point(v->blk, "jffs2");
+       if (mp) {
+               fprintf(stderr, "%s is mounted as %s, only erasing files\n", v->blk, mp);
+               foreachdir(mp, handle_rmdir);
+               mount(mp, "/", NULL, MS_REMOUNT, 0);
+       } else {
+               fprintf(stderr, "%s is not mounted, erasing it\n", v->blk);
+               volume_erase_all(v);
+       }
+
+       return 0;
+}
+
+static int
+jffs2_mark(int argc, char **argv)
+{
+       __u32 deadc0de = __cpu_to_be32(0xdeadc0de);
+       struct volume *v;
+       size_t sz;
+       int fd;
+
+       if (ask_user(argc, argv))
+               return -1;
+
+       v = volume_find("rootfs_data");
+       if (!v) {
+               fprintf(stderr, "no rootfs_data was found\n");
+               return -1;
+       }
+
+       fd = open(v->blk, O_WRONLY);
+       fprintf(stderr, "%s - marking with deadc0de\n", v->blk);
+       if (!fd) {
+               fprintf(stderr, "opening %s failed\n", v->blk);
+               return -1;
+       }
+
+       sz = write(fd, &deadc0de, sizeof(deadc0de));
+       close(fd);
+
+       if (sz != 1) {
+               fprintf(stderr, "writing %s failed: %s\n", v->blk, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+jffs2_switch(int argc, char **argv)
+{
+       struct volume *v;
+       char *mp;
+       int ret = -1;
+
+       if (find_overlay_mount("overlayfs:/tmp/root"))
+               return -1;
+
+       if (find_filesystem("overlay")) {
+               fprintf(stderr, "overlayfs not found\n");
+               return ret;
+       }
+
+       v = volume_find("rootfs_data");
+       mp = find_mount_point(v->blk, NULL);
+       if (mp) {
+               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
+               return -1;
+       }
+
+       switch (volume_identify(v)) {
+       case FS_NONE:
+               fprintf(stderr, "no jffs2 marker found\n");
+               /* fall through */
+
+       case FS_DEADCODE:
+               ret = switch2jffs();
+               if (!ret) {
+                       fprintf(stderr, "doing fo cleanup\n");
+                       umount2("/tmp/root", MNT_DETACH);
+                       foreachdir("/overlay/", handle_whiteout);
+               }
+               break;
+
+       case FS_JFFS2:
+               ret = jffs2_mount();
+               if (ret)
+                       break;
+               if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
+                       fprintf(stderr, "switching to jffs2 failed\n");
+                       ret = -1;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+static int overlay_mount_fs(void)
+{
+       struct volume *v;
+
+       if (mkdir("/tmp/overlay", 0755)) {
+               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
+               return -1;
+       }
+
+       v = volume_find("rootfs_data");
+       if (!v) {
+               fprintf(stderr, "rootfs_data does not exist\n");
+               return -1;
+       }
+
+       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
+               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n",
+                               v->blk, strerror(errno));
+               return -1;
+       }
+
+       volume_init(v);
+
+       return -1;
+}
+
+static int overlay_mount(void)
+{
+       struct volume *v = volume_find("rootfs_data");;
+       char *mp;
+
+       if (!v)
+               return -1;
+
+       mp = find_mount_point(v->blk, NULL);
+       if (mp) {
+               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
+               return -1;
+       }
+
+       overlay_mount_fs();
+
+       extroot_prefix = "/tmp/overlay";
+       if (!backend_mount("extroot")) {
+               fprintf(stderr, "fs-state: switched to extroot\n");
+               return 0;
+       }
+
+       fprintf(stderr, "switching to jffs2\n");
+       if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
+               fprintf(stderr, "switching to jffs2 failed - fallback to ramoverlay\n");
+               return ramoverlay();
+       }
+
+       return -1;
+}
+
+static struct backend_handler jffs2_handlers[] = {
+{
+       .name = "jffs2reset",
+       .cli = jffs2_reset,
+}, {
+       .name = "jffs2mark",
+       .cli = jffs2_mark,
+}};
+
+static struct backend overlay_backend = {
+       .name = "overlay",
+       .num_handlers = ARRAY_SIZE(jffs2_handlers),
+       .handlers = jffs2_handlers,
+       .mount = overlay_mount,
+};
+BACKEND(overlay_backend);
diff --git a/libfstools/mount.c b/libfstools/mount.c
new file mode 100644 (file)
index 0000000..e7b57f0
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "../fs-state.h"
+
+/* this is a raw syscall - man 2 pivot_root */
+extern int pivot_root(const char *new_root, const char *put_old);
+
+int
+mount_move(char *oldroot, char *newroot, char *dir)
+{
+#ifndef MS_MOVE
+#define MS_MOVE        (1 << 13)
+#endif
+       struct stat s;
+       char olddir[64];
+       char newdir[64];
+       int ret;
+
+       snprintf(olddir, sizeof(olddir), "%s%s", oldroot, dir);
+       snprintf(newdir, sizeof(newdir), "%s%s", newroot, dir);
+
+       if (stat(olddir, &s) || !S_ISDIR(s.st_mode))
+               return -1;
+
+       if (stat(newdir, &s) || !S_ISDIR(s.st_mode))
+               return -1;
+
+       ret = mount(olddir, newdir, NULL, MS_NOATIME | MS_MOVE, NULL);
+
+/*     if (ret)
+               fprintf(stderr, "failed %s %s: %s\n", olddir, newdir, strerror(errno));*/
+
+       return ret;
+}
+
+int
+pivot(char *new, char *old)
+{
+       char pivotdir[64];
+       int ret;
+
+       if (mount_move("", new, "/proc"))
+               return -1;
+
+       snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old);
+
+       ret = pivot_root(new, pivotdir);
+
+       if (ret < 0) {
+               fprintf(stderr, "pivot_root failed %s %s: %s\n", new, pivotdir, strerror(errno));
+               return -1;
+       }
+
+       mount_move(old, "", "/dev");
+       mount_move(old, "", "/tmp");
+       mount_move(old, "", "/sys");
+       mount_move(old, "", "/overlay");
+
+       return 0;
+}
+
+int
+fopivot(char *rw_root, char *ro_root)
+{
+       char overlay[64], lowerdir[64];
+
+       if (find_filesystem("overlay")) {
+               fprintf(stderr, "BUG: no suitable fs found\n");
+               return -1;
+       }
+
+       snprintf(overlay, sizeof(overlay), "overlayfs:%s", rw_root);
+       snprintf(lowerdir, sizeof(lowerdir), "lowerdir=/,upperdir=%s", rw_root);
+
+       if (mount(overlay, "/mnt", "overlayfs", MS_NOATIME, lowerdir)) {
+               fprintf(stderr, "mount failed: %s\n", strerror(errno));
+               return -1;
+       }
+
+       return pivot("/mnt", ro_root);
+}
+
+int
+ramoverlay(void)
+{
+       mkdir("/tmp/root", 0755);
+       mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755");
+
+       return fopivot("/tmp/root", "/rom");
+}
diff --git a/libfstools/mount_root.c b/libfstools/mount_root.c
new file mode 100644 (file)
index 0000000..ce20604
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../fs-state.h"
+
+#include "volume.h"
+
+int
+backend_mount(char *name)
+{
+       struct backend *b = find_backend(name);
+
+       if (!b || !b->mount)
+               return -1;
+
+       return b->mount();
+}
+
+static int
+backend_info(char *name)
+{
+       struct backend *b = find_backend(name);
+
+       if (!b || !b->info)
+               return -1;
+
+       return b->info();
+}
+
+static int
+start(int argc, char **argv)
+{
+       struct volume *v = volume_find("rootfs_data");
+
+       if (!getenv("PREINIT"))
+               return -1;
+
+       if (!v) {
+               v = volume_find("rootfs");
+               volume_init(v);
+               fprintf(stderr, "mounting /dev/root\n");
+               mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
+               return 0;
+       }
+
+       extroot_prefix = "";
+       if (!backend_mount("extroot")) {
+               fprintf(stderr, "fs-state: switched to extroot\n");
+               return 0;
+       }
+
+       switch (volume_identify(v)) {
+       case FS_NONE:
+       case FS_DEADCODE:
+               return ramoverlay();
+
+       case FS_JFFS2:
+               backend_mount("overlay");
+               break;
+
+       case FS_SNAPSHOT:
+               backend_mount("snapshot");
+               break;
+       }
+
+       return 0;
+}
+
+static int
+stop(int argc, char **argv)
+{
+       if (!getenv("SHUTDOWN"))
+               return -1;
+
+       return 0;
+}
+
+static int
+done(int argc, char **argv)
+{
+       struct volume *v = volume_find("rootfs_data");
+
+       if (!v)
+               return -1;
+
+       switch (volume_identify(v)) {
+       case FS_NONE:
+       case FS_DEADCODE:
+               return jffs2_switch(argc, argv);
+       }
+
+       return 0;
+}
+
+static int
+info(int argc, char **argv)
+{
+       struct volume *v = volume_find("rootfs_data");
+
+       if (!v)
+               return -1;
+
+       switch (volume_identify(v)) {
+       case FS_SNAPSHOT:
+               backend_info("snapshot");
+               return 0;
+       }
+
+       return 0;
+}
+
+static struct backend start_backend = {
+       .name = "start",
+       .cli = start,
+};
+BACKEND(start_backend);
+
+static struct backend stop_backend = {
+       .name = "stop",
+       .cli = stop,
+};
+BACKEND(stop_backend);
+
+static struct backend done_backend = {
+       .name = "done",
+       .cli = done,
+};
+BACKEND(done_backend);
+
+static struct backend info_backend = {
+       .name = "info",
+       .cli = info,
+};
+BACKEND(info_backend);
diff --git a/libfstools/mtd.c b/libfstools/mtd.c
new file mode 100644 (file)
index 0000000..a0005d7
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <asm/byteorder.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <mtd/mtd-user.h>
+
+#include "../fs-state.h"
+
+#include "volume.h"
+
+#define PATH_MAX               256
+
+struct mtd_priv {
+       int     fd;
+       int     idx;
+       char    *chr;
+};
+
+static struct driver mtd_driver;
+
+static int mtd_open(const char *mtd, int block)
+{
+       FILE *fp;
+       char dev[PATH_MAX];
+       int i, ret, flags = O_RDWR | O_SYNC;
+
+       if ((fp = fopen("/proc/mtd", "r"))) {
+               while (fgets(dev, sizeof(dev), fp)) {
+                       if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
+                               snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
+                               ret = open(dev, flags);
+                               if (ret < 0) {
+                                       snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
+                                       ret = open(dev, flags);
+                               }
+                               fclose(fp);
+                               return ret;
+                       }
+               }
+               fclose(fp);
+       }
+
+       return open(mtd, flags);
+}
+
+static void mtd_volume_close(struct volume *v)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+
+       if (!p->fd)
+               return;
+
+       close(p->fd);
+       p->fd = 0;
+}
+
+static int mtd_volume_load(struct volume *v)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+       struct mtd_info_user mtdInfo;
+       struct erase_info_user mtdLockInfo;
+
+       if (p->fd)
+               return 0;
+
+       if (!p->chr)
+               return -1;
+
+       p->fd = mtd_open(p->chr, 0);
+       if (p->fd < 0) {
+               p->fd = 0;
+               fprintf(stderr, "Could not open mtd device: %s\n", p->chr);
+               return -1;
+       }
+
+       if (ioctl(p->fd, MEMGETINFO, &mtdInfo)) {
+               mtd_volume_close(v);
+               fprintf(stderr, "Could not get MTD device info from %s\n", p->chr);
+               return -1;
+       }
+
+       v->size = mtdInfo.size;
+       v->block_size = mtdInfo.erasesize;
+       switch (mtdInfo.type) {
+       case MTD_NORFLASH:
+               v->type = NORFLASH;
+               break;
+       case MTD_NANDFLASH:
+               v->type = NANDFLASH;
+               break;
+       case MTD_UBIVOLUME:
+               v->type = UBIVOLUME;
+               break;
+       default:
+               v->type = UNKNOWN_TYPE;
+               break;
+       }
+
+       mtdLockInfo.start = 0;
+       mtdLockInfo.length = v->size;
+       ioctl(p->fd, MEMUNLOCK, &mtdLockInfo);
+
+       return 0;
+}
+
+static char* mtd_find_index(char *name)
+{
+       FILE *fp = fopen("/proc/mtd", "r");
+       static char line[256];
+       char *index = NULL;
+
+       if(!fp)
+               return index;
+
+       while (!index && fgets(line, sizeof(line), fp)) {
+               if (strstr(line, name)) {
+                       char *eol = strstr(line, ":");
+
+                       if (!eol)
+                               continue;
+
+                       *eol = '\0';
+                       index = &line[3];
+               }
+       }
+
+       fclose(fp);
+
+       return index;
+}
+
+static int mtd_volume_find(struct volume *v, char *name)
+{
+       char *idx = mtd_find_index(name);
+       struct mtd_priv *p;
+       char buffer[32];
+
+       if (!idx)
+               return -1;
+
+       p = calloc(1, sizeof(struct mtd_priv));
+       if (!p)
+               return -1;
+
+       v->priv = p;
+       v->name = strdup(name);
+       v->drv = &mtd_driver;
+       p->idx = atoi(idx);
+
+       snprintf(buffer, sizeof(buffer), "/dev/mtdblock%s", idx);
+       v->blk = strdup(buffer);
+
+       snprintf(buffer, sizeof(buffer), "/dev/mtd%s", idx);
+       p->chr = strdup(buffer);
+
+       return 0;
+}
+
+static int mtd_volume_identify(struct volume *v)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+       __u32 deadc0de;
+       __u16 jffs2;
+       size_t sz;
+
+       if (mtd_volume_load(v)) {
+               fprintf(stderr, "reading %s failed\n", v->name);
+               return -1;
+       }
+
+       sz = read(p->fd, &deadc0de, sizeof(deadc0de));
+
+       if (sz != sizeof(deadc0de)) {
+               fprintf(stderr, "reading %s failed: %s\n", v->name, strerror(errno));
+               return -1;
+       }
+
+       if (deadc0de == 0x4f575254)
+               return FS_SNAPSHOT;
+
+       deadc0de = __be32_to_cpu(deadc0de);
+       if (deadc0de == 0xdeadc0de) {
+               fprintf(stderr, "jffs2 is not ready - marker found\n");
+               return FS_DEADCODE;
+       }
+
+       jffs2 = __be16_to_cpu(deadc0de >> 16);
+       if (jffs2 == 0x1985) {
+               fprintf(stderr, "jffs2 is ready\n");
+               return FS_JFFS2;
+       }
+
+       if (v->type == UBIVOLUME && deadc0de == 0xffffffff) {
+               fprintf(stderr, "jffs2 is ready\n");
+               return FS_JFFS2;
+       }
+
+       fprintf(stderr, "No jffs2 marker was found\n");
+
+       return FS_NONE;
+}
+
+static int mtd_volume_erase(struct volume *v, int offset, int len)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+       struct erase_info_user eiu;
+       int first_block, num_blocks;
+
+       if (mtd_volume_load(v))
+               return -1;
+
+       if (offset % v->block_size || len % v->block_size) {
+               fprintf(stderr, "mtd erase needs to be block aligned\n");
+               return -1;
+       }
+
+       first_block = offset / v->block_size;
+       num_blocks = len / v->block_size;
+       eiu.length = v->block_size;
+
+       for (eiu.start = first_block * v->block_size;
+                       eiu.start < v->size && eiu.start < (first_block + num_blocks) * v->block_size;
+                       eiu.start += v->block_size) {
+               fprintf(stderr, "erasing %x %x\n", eiu.start, v->block_size);
+               ioctl(p->fd, MEMUNLOCK, &eiu);
+               if (ioctl(p->fd, MEMERASE, &eiu))
+                       fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start);
+       }
+
+       mtd_volume_close(v);
+
+       return 0;
+}
+
+static int mtd_volume_erase_all(struct volume *v)
+{
+       mtd_volume_erase(v, 0, v->size);
+       mtd_volume_close(v);
+
+       return 0;
+}
+
+static int mtd_volume_init(struct volume *v)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+       struct mtd_info_user mtdinfo;
+       int ret;
+
+       if (mtd_volume_load(v))
+               return -1;
+
+       ret = ioctl(p->fd, MEMGETINFO, &mtdinfo);
+       if (ret) {
+               fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", p->fd, strerror(errno));
+       } else {
+               struct erase_info_user mtdlock;
+
+               mtdlock.start = 0;
+               mtdlock.length = mtdinfo.size;
+               ioctl(p->fd, MEMUNLOCK, &mtdlock);
+       }
+
+       return ret;
+}
+
+static int mtd_volume_read(struct volume *v, void *buf, int offset, int length)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+
+       if (mtd_volume_load(v))
+               return -1;
+
+       if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
+               fprintf(stderr, "lseek/read failed\n");
+               return -1;
+       }
+
+       if (read(p->fd, buf, length) == -1) {
+               fprintf(stderr, "read failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int mtd_volume_write(struct volume *v, void *buf, int offset, int length)
+{
+       struct mtd_priv *p = (struct mtd_priv*) v->priv;
+
+       if (mtd_volume_load(v))
+               return -1;
+
+       if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
+               fprintf(stderr, "lseek/write failed at offset %d\n", offset);
+               perror("lseek");
+               return -1;
+       }
+
+       if (write(p->fd, buf, length) == -1) {
+               fprintf(stderr, "write failed\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static struct driver mtd_driver = {
+       .name = "mtd",
+       .find = mtd_volume_find,
+       .init = mtd_volume_init,
+       .erase = mtd_volume_erase,
+       .erase_all = mtd_volume_erase_all,
+       .read = mtd_volume_read,
+       .write = mtd_volume_write,
+       .identify = mtd_volume_identify,
+};
+DRIVER(mtd_driver);
diff --git a/libfstools/overlay.c b/libfstools/overlay.c
new file mode 100644 (file)
index 0000000..85237e4
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <asm/byteorder.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glob.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "../fs-state.h"
+#include "volume.h"
+
+#define SWITCH_JFFS2 "/tmp/.switch_jffs2"
+
+void
+foreachdir(const char *dir, int (*cb)(const char*))
+{
+       char globdir[256];
+       glob_t gl;
+       int j;
+
+       if (dir[strlen(dir) - 1] == '/')
+               snprintf(globdir, 256, "%s*", dir);
+       else
+               snprintf(globdir, 256, "%s/*", dir);
+
+       if (!glob(globdir, GLOB_NOESCAPE | GLOB_MARK | GLOB_ONLYDIR, NULL, &gl))
+               for (j = 0; j < gl.gl_pathc; j++)
+                       foreachdir(gl.gl_pathv[j], cb);
+
+       cb(dir);
+}
+
+static int
+overlay_mount(struct volume *v, char *fs)
+{
+       if (mkdir("/tmp/overlay", 0755)) {
+               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (mount(v->blk, "/tmp/overlay", fs, MS_NOATIME, NULL)) {
+               fprintf(stderr, "failed to mount -t %s %s /tmp/overlay: %s\n", fs, v->blk, strerror(errno));
+               return -1;
+       }
+
+       return volume_init(v);
+}
+
+static int
+switch2jffs(struct volume *v)
+{
+       struct stat s;
+       int ret;
+
+       if (!stat(SWITCH_JFFS2, &s)) {
+               fprintf(stderr, "jffs2 switch already running\n");
+               return -1;
+       }
+
+       creat("/tmp/.switch_jffs2", 0600);
+       ret = mount(v->blk, "/rom/overlay", "jffs2", MS_NOATIME, NULL);
+       unlink("/tmp/.switch_jffs2");
+       if (ret) {
+               fprintf(stderr, "failed - mount -t jffs2 %s /rom/overlay: %s\n", v->blk, strerror(errno));
+               return -1;
+       }
+
+       if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) {
+               fprintf(stderr, "failed - mount -o remount,ro none: %s\n", strerror(errno));
+               return -1;
+       }
+
+       system("cp -a /tmp/root/* /rom/overlay");
+
+       if (pivot("/rom", "/mnt")) {
+               fprintf(stderr, "failed - pivot /rom /mnt: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (mount_move("/mnt", "/tmp/root", "")) {
+               fprintf(stderr, "failed - mount -o move /mnt /tmp/root %s\n", strerror(errno));
+               return -1;
+       }
+
+       return fopivot("/overlay", "/rom");
+}
+
+int
+handle_whiteout(const char *dir)
+{
+       struct stat s;
+       char link[256];
+       ssize_t sz;
+       struct dirent **namelist;
+       int n;
+
+       n = scandir(dir, &namelist, NULL, NULL);
+
+       if (n < 1)
+               return -1;
+
+       while (n--) {
+               char file[256];
+
+               snprintf(file, sizeof(file), "%s%s", dir, namelist[n]->d_name);
+               if (!lstat(file, &s) && S_ISLNK(s.st_mode)) {
+                       sz = readlink(file, link, sizeof(link) - 1);
+                       if (sz > 0) {
+                               char *orig;
+
+                               link[sz] = '\0';
+                               orig = strstr(&file[1], "/");
+                               if (orig && !strcmp(link, "(overlay-whiteout)"))
+                                       unlink(orig);
+                       }
+               }
+               free(namelist[n]);
+       }
+       free(namelist);
+
+       return 0;
+}
+
+static int
+ask_user(int argc, char **argv)
+{
+       if ((argc < 2) || strcmp(argv[1], "-y")) {
+               fprintf(stderr, "This will erase all settings and remove any installed packages. Are you sure? [N/y]\n");
+               if (getchar() != 'y')
+                       return -1;
+       }
+       return 0;
+
+}
+
+int
+jffs2_switch(int argc, char **argv)
+{
+       struct volume *v;
+       char *mp;
+       int ret = -1;
+
+       if (find_overlay_mount("overlayfs:/tmp/root"))
+               return -1;
+
+       if (find_filesystem("overlay")) {
+               fprintf(stderr, "overlayfs not found\n");
+               return ret;
+       }
+
+       v = volume_find("rootfs_data");
+       mp = find_mount_point(v->blk, NULL);
+       if (mp) {
+               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
+               return -1;
+       }
+
+       switch (volume_identify(v)) {
+       case FS_NONE:
+               fprintf(stderr, "no jffs2 marker found\n");
+               /* fall through */
+
+       case FS_DEADCODE:
+               ret = switch2jffs();
+               if (!ret) {
+                       fprintf(stderr, "doing fo cleanup\n");
+                       umount2("/tmp/root", MNT_DETACH);
+                       foreachdir("/overlay/", handle_whiteout);
+               }
+               break;
+
+       case FS_JFFS2:
+               ret = overlay_mount(v, "jffs2");
+               if (ret)
+                       break;
+               if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
+                       fprintf(stderr, "switching to jffs2 failed\n");
+                       ret = -1;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+static int overlay_mount_fs(void)
+{
+       struct volume *v;
+
+       if (mkdir("/tmp/overlay", 0755)) {
+               fprintf(stderr, "failed to mkdir /tmp/overlay: %s\n", strerror(errno));
+               return -1;
+       }
+
+       v = volume_find("rootfs_data");
+       if (!v) {
+               fprintf(stderr, "rootfs_data does not exist\n");
+               return -1;
+       }
+
+       if (mount(v->blk, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
+               fprintf(stderr, "failed to mount -t jffs2 %s /tmp/overlay: %s\n",
+                               v->blk, strerror(errno));
+               return -1;
+       }
+
+       volume_init(v);
+
+       return -1;
+}
+
+static int overlay_mount(void)
+{
+       struct volume *v = volume_find("rootfs_data");;
+       char *mp;
+
+       if (!v)
+               return -1;
+
+       mp = find_mount_point(v->blk, NULL);
+       if (mp) {
+               fprintf(stderr, "rootfs_data:%s is already mounted as %s\n", v->blk, mp);
+               return -1;
+       }
+
+       overlay_mount_fs();
+
+       extroot_prefix = "/tmp/overlay";
+       if (!backend_mount("extroot")) {
+               fprintf(stderr, "fs-state: switched to extroot\n");
+               return 0;
+       }
+
+       fprintf(stderr, "switching to jffs2\n");
+       if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
+               fprintf(stderr, "switching to jffs2 failed - fallback to ramoverlay\n");
+               return ramoverlay();
+       }
+
+       return -1;
+}
+
+static struct backend overlay_backend = {
+       .name = "overlay",
+       .mount = overlay_mount,
+};
+BACKEND(overlay_backend);
diff --git a/libfstools/snapshot.c b/libfstools/snapshot.c
new file mode 100644 (file)
index 0000000..bcbce94
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <mtd/mtd-user.h>
+
+#include <glob.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <libubox/list.h>
+#include <libubox/blob.h>
+#include <libubox/md5.h>
+
+#include "../fs-state.h"
+#include "volume.h"
+
+#define PATH_MAX       256
+#define OWRT           0x4f575254
+#define DATA           0x44415441
+#define CONF           0x434f4e46
+
+struct file_header {
+       uint32_t magic;
+       uint32_t type;
+       uint32_t seq;
+       uint32_t length;
+       uint32_t md5[4];
+};
+
+static inline int
+is_config(struct file_header *h)
+{
+       return ((h->magic == OWRT) && (h->type == CONF));
+}
+
+static inline int
+valid_file_size(int fs)
+{
+       if ((fs > 8 * 1024 * 1204) || (fs <= 0))
+               return -1;
+
+       return 0;
+}
+
+static void
+hdr_to_be32(struct file_header *hdr)
+{
+       uint32_t *h = (uint32_t *) hdr;
+       int i;
+
+       for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++)
+               h[i] = cpu_to_be32(h[i]);
+}
+
+static void
+be32_to_hdr(struct file_header *hdr)
+{
+       uint32_t *h = (uint32_t *) hdr;
+       int i;
+
+       for (i = 0; i < sizeof(struct file_header) / sizeof(uint32_t); i++)
+               h[i] = be32_to_cpu(h[i]);
+}
+
+static int
+pad_file_size(struct volume *v, int size)
+{
+       int mod;
+
+       size += sizeof(struct file_header);
+       mod = size % v->block_size;
+       if (mod) {
+               size -= mod;
+               size += v->block_size;
+       }
+
+       return size;
+}
+
+static int
+verify_file_hash(char *file, uint32_t *hash)
+{
+       uint32_t md5[4];
+
+       if (md5sum(file, md5)) {
+               fprintf(stderr, "failed to generate md5 sum\n");
+               return -1;
+       }
+
+       if (memcmp(md5, hash, sizeof(md5))) {
+               fprintf(stderr, "failed to verify hash of %s.\n", file);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+snapshot_next_free(struct volume *v, uint32_t *seq)
+{
+       struct file_header hdr = { 0 };
+       int block = 0;
+
+       *seq = rand();
+
+       do {
+               if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
+                       fprintf(stderr, "scanning for next free block failed\n");
+                       return 0;
+               }
+
+               be32_to_hdr(&hdr);
+
+               if (hdr.magic != OWRT)
+                       break;
+
+               if (hdr.type == DATA && !valid_file_size(hdr.length)) {
+                       if (*seq + 1 != hdr.seq && block)
+                               return block;
+                       *seq = hdr.seq;
+                       block += pad_file_size(v, hdr.length) / v->block_size;
+               }
+       } while (hdr.type == DATA);
+
+       return block;
+}
+
+static int
+config_find(struct volume *v, struct file_header *conf, struct file_header *sentinel)
+{
+       uint32_t seq;
+       int i, next = snapshot_next_free(v, &seq);
+
+       conf->magic = sentinel->magic = 0;
+
+       if (!volume_read(v, conf, next, sizeof(*conf)))
+               be32_to_hdr(conf);
+
+       for (i = (v->size / v->block_size) - 1; i > 0; i--) {
+               if (volume_read(v, sentinel,  i * v->block_size, sizeof(*sentinel))) {
+                       fprintf(stderr, "failed to read header\n");
+                       return -1;
+               }
+               be32_to_hdr(sentinel);
+
+               if (sentinel->magic == OWRT && sentinel->type == CONF && !valid_file_size(sentinel->length)) {
+                       if (next == i)
+                               return -1;
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+static int
+snapshot_info(void)
+{
+       struct volume *v = volume_find("rootfs_data");
+       struct file_header hdr = { 0 }, conf;
+       int block = 0;
+
+       if (!v)
+               return -1;
+
+       fprintf(stderr, "sectors:\t%llu, block_size:\t%dK\n", v->size / v->block_size, v->block_size / 1024);
+       do {
+               if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
+                       fprintf(stderr, "scanning for next free block failed\n");
+                       return 0;
+               }
+
+               be32_to_hdr(&hdr);
+
+               if (hdr.magic != OWRT)
+                       break;
+
+               if (hdr.type == DATA)
+                       fprintf(stderr, "block %d:\tsnapshot entry, size: %d, sectors: %d, sequence: %d\n", block,  hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
+               else if (hdr.type == CONF)
+                       fprintf(stderr, "block %d:\tvolatile entry, size: %d, sectors: %d, sequence: %d\n", block,  hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
+
+               if (hdr.type == DATA && !valid_file_size(hdr.length))
+                       block += pad_file_size(v, hdr.length) / v->block_size;
+       } while (hdr.type == DATA);
+       block = config_find(v, &conf, &hdr);
+       if (block > 0)
+               fprintf(stderr, "block %d:\tsentinel entry, size: %d, sectors: %d, sequence: %d\n", block, hdr.length, pad_file_size(v, hdr.length) / v->block_size, hdr.seq);
+
+       return 0;
+}
+
+static int
+snapshot_write_file(struct volume *v, int block, char *file, uint32_t seq, uint32_t type)
+{
+       uint32_t md5[4] = { 0 };
+       struct file_header hdr;
+       struct stat s;
+        char buffer[256];
+       int in = 0, len, offset;
+       int ret = -1;
+
+       if (stat(file, &s) || md5sum(file, md5)) {
+               fprintf(stderr, "stat failed on %s\n", file);
+               goto out;
+       }
+
+       if ((block * v->block_size) + pad_file_size(v, s.st_size) > v->size) {
+               fprintf(stderr, "upgrade is too big for the flash\n");
+               goto out;
+       }
+       volume_erase(v, block * v->block_size, pad_file_size(v, s.st_size));
+       volume_erase(v, block * v->block_size + pad_file_size(v, s.st_size), v->block_size);
+
+       hdr.length = s.st_size;
+       hdr.magic = OWRT;
+       hdr.type = type;
+       hdr.seq = seq;
+       memcpy(hdr.md5, md5, sizeof(md5));
+       hdr_to_be32(&hdr);
+
+       if (volume_write(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
+               fprintf(stderr, "failed to write header\n");
+               goto out;
+       }
+
+       in = open(file, O_RDONLY);
+       if (in < 1) {
+               fprintf(stderr, "failed to open %s\n", file);
+               goto out;
+       }
+
+       offset = (block * v->block_size) + sizeof(struct file_header);
+
+       while ((len = read(in, buffer, sizeof(buffer))) > 0) {
+               if (volume_write(v, buffer, offset, len) < 0)
+                       goto out;
+               offset += len;
+       }
+
+       ret = 0;
+
+out:
+       if (in > 0)
+               close(in);
+
+       return ret;
+}
+
+static int
+snapshot_read_file(struct volume *v, int block, char *file, uint32_t type)
+{
+       struct file_header hdr;
+       char buffer[256];
+       int out, offset = 0;
+
+       if (volume_read(v, &hdr, block * v->block_size, sizeof(struct file_header))) {
+               fprintf(stderr, "failed to read header\n");
+               return -1;
+       }
+       be32_to_hdr(&hdr);
+
+       if (hdr.magic != OWRT)
+               return -1;
+
+       if (hdr.type != type)
+               return -1;
+
+       if (valid_file_size(hdr.length))
+               return -1;
+
+       out = open(file, O_WRONLY | O_CREAT, 0700);
+       if (!out) {
+               fprintf(stderr, "failed to open %s\n", file);
+               return -1;
+       }
+
+       while (hdr.length > 0) {
+               int len = sizeof(buffer);
+
+               if (hdr.length < len)
+                       len = hdr.length;
+
+               if ((volume_read(v, buffer, offset, len) != len) || (write(out, buffer, len) != len))
+                       return -1;
+
+               offset += len;
+               hdr.length -= len;
+       }
+
+       close(out);
+
+       if (verify_file_hash(file, hdr.md5)) {
+               fprintf(stderr, "md5 verification failed\n");
+               unlink(file);
+               return 0;
+       }
+
+        block += pad_file_size(v, hdr.length) / v->block_size;
+
+       return block;
+}
+
+static int
+sentinel_write(struct volume *v, uint32_t _seq)
+{
+       int ret, block;
+       struct stat s;
+       uint32_t seq;
+
+       if (stat("/tmp/config.tar.gz", &s)) {
+               fprintf(stderr, "failed to stat /tmp/config.tar.gz\n");
+               return -1;
+       }
+
+       snapshot_next_free(v, &seq);
+       if (_seq)
+               seq = _seq;
+       block = v->size / v->block_size;
+       block -= pad_file_size(v, s.st_size) / v->block_size;
+       if (block < 0)
+               block = 0;
+
+       ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
+       if (ret)
+               fprintf(stderr, "failed to write sentinel\n");
+       else
+               fprintf(stderr, "wrote /tmp/config.tar.gz sentinel\n");
+       return ret;
+}
+
+static int
+volatile_write(struct volume *v, uint32_t _seq)
+{
+       int block, ret;
+       uint32_t seq;
+
+       block = snapshot_next_free(v, &seq);
+       if (_seq)
+               seq = _seq;
+       if (block < 0)
+               block = 0;
+
+       ret = snapshot_write_file(v, block, "/tmp/config.tar.gz", seq, CONF);
+       if (ret)
+               fprintf(stderr, "failed to write /tmp/config.tar.gz\n");
+       else
+               fprintf(stderr, "wrote /tmp/config.tar.gz\n");
+       return ret;
+}
+
+static int
+config_write(int argc, char **argv)
+{
+       struct volume *v = volume_find("rootfs_data");
+       int ret;
+
+       if (!v)
+               return -1;
+
+       ret = volatile_write(v, 0);
+       if (!ret)
+               ret = sentinel_write(v, 0);
+
+       return ret;
+}
+
+static int
+config_read(int argc, char **argv)
+{
+       struct volume *v = volume_find("rootfs_data");
+       struct file_header conf, sentinel;
+       int next, block, ret = 0;
+       uint32_t seq;
+
+       if (!v)
+               return -1;
+
+       block = config_find(v, &conf, &sentinel);
+       next = snapshot_next_free(v, &seq);
+       if (is_config(&conf) && conf.seq == seq)
+               block = next;
+       else if (!is_config(&sentinel) || sentinel.seq != seq)
+               return -1;
+
+       unlink("/tmp/config.tar.gz");
+       ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
+
+       if (ret < 1)
+               fprintf(stderr, "failed to read /tmp/config.tar.gz\n");
+
+       return ret;
+}
+
+static int
+snapshot_write(int argc, char **argv)
+{
+       struct volume *v = volume_find("rootfs_data");
+       int block, ret;
+       uint32_t seq;
+
+       if (!v)
+               return -1;
+
+       block = snapshot_next_free(v, &seq);
+       if (block < 0)
+               block = 0;
+
+       ret = snapshot_write_file(v, block, "/tmp/snapshot.tar.gz", seq + 1, DATA);
+       if (ret)
+               fprintf(stderr, "failed to write /tmp/snapshot.tar.gz\n");
+       else
+               fprintf(stderr, "wrote /tmp/snapshot.tar.gz\n");
+
+       return ret;
+}
+
+static int
+snapshot_mark(int argc, char **argv)
+{
+       __be32 owrt = cpu_to_be32(OWRT);
+       struct volume *v;
+       size_t sz;
+       int fd;
+
+       fprintf(stderr, "This will remove all snapshot data stored on the system. Are you sure? [N/y]\n");
+       if (getchar() != 'y')
+               return -1;
+
+       v = volume_find("rootfs_data");
+       if (!v) {
+               fprintf(stderr, "no rootfs_data was found\n");
+               return -1;
+       }
+
+       fd = open(v->blk, O_WRONLY);
+       fprintf(stderr, "%s - marking with 0x%08x\n", v->blk, owrt);
+       if (fd < 0) {
+               fprintf(stderr, "opening %s failed\n", v->blk);
+               return -1;
+       }
+
+       sz = write(fd, &owrt, sizeof(owrt));
+       close(fd);
+
+       if (sz != 1) {
+               fprintf(stderr, "writing %s failed: %s\n", v->blk, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+snapshot_read(int argc, char **argv)
+{
+       struct volume *v = volume_find("rootfs_data");;
+       int block = 0, ret = 0;
+       char file[64];
+
+       if (!v)
+               return -1;
+
+       if (argc > 1) {
+               block = atoi(argv[1]);
+               if (block >= (v->size / v->block_size)) {
+                       fprintf(stderr, "invalid block %d > %llu\n", block, v->size / v->block_size);
+                       goto out;
+               }
+               snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
+
+               ret = snapshot_read_file(v, block, file, DATA);
+               goto out;
+       }
+
+       do {
+               snprintf(file, sizeof(file), "/tmp/snapshot/block%d.tar.gz", block);
+               block = snapshot_read_file(v, block, file, DATA);
+       } while (block > 0);
+
+out:
+       return ret;
+}
+
+static int
+snapshot_sync(void)
+{
+       struct volume *v = volume_find("rootfs_data");
+       struct file_header sentinel, conf;
+       int next, block = 0;
+       uint32_t seq;
+
+       if (!v)
+               return -1;
+
+       next = snapshot_next_free(v, &seq);
+       block = config_find(v, &conf, &sentinel);
+       if (is_config(&conf) && conf.seq != seq) {
+               conf.magic = 0;
+               volume_erase(v, next * v->block_size, 2 * v->block_size);
+       }
+
+       if (is_config(&sentinel) && (sentinel.seq != seq)) {
+               sentinel.magic = 0;
+               volume_erase(v, block * v->block_size, v->block_size);
+       }
+
+       if (!is_config(&conf) && !is_config(&sentinel)) {
+       //      fprintf(stderr, "no config found\n");
+       } else if (((is_config(&conf) && is_config(&sentinel)) &&
+                               (memcmp(conf.md5, sentinel.md5, sizeof(conf.md5)) || (conf.seq != sentinel.seq))) ||
+                       (is_config(&conf) && !is_config(&sentinel))) {
+               uint32_t seq;
+               int next = snapshot_next_free(v, &seq);
+               int ret = snapshot_read_file(v, next, "/tmp/config.tar.gz", CONF);
+               if (ret > 0) {
+                       if (sentinel_write(v, conf.seq))
+                               fprintf(stderr, "failed to write sentinel data");
+               }
+       } else if (!is_config(&conf) && is_config(&sentinel) && next) {
+               int ret = snapshot_read_file(v, block, "/tmp/config.tar.gz", CONF);
+               if (ret > 0)
+                       if (volatile_write(v, sentinel.seq))
+                               fprintf(stderr, "failed to write sentinel data");
+       } else
+               fprintf(stderr, "config in sync\n");
+
+       unlink("/tmp/config.tar.gz");
+
+       return 0;
+}
+
+static int
+_ramoverlay(char *rom, char *overlay)
+{
+       mount("tmpfs", overlay, "tmpfs", MS_NOATIME, "mode=0755");
+       return fopivot(overlay, rom);
+}
+
+static int
+snapshot_mount(void)
+{
+       snapshot_sync();
+       setenv("SNAPSHOT", "magic", 1);
+       _ramoverlay("/rom", "/overlay");
+       system("/sbin/snapshot unpack");
+       foreachdir("/overlay/", handle_whiteout);
+       mkdir("/volatile", 0700);
+       _ramoverlay("/rom", "/volatile");
+       mount_move("/rom/volatile", "/volatile", "");
+       mount_move("/rom/rom", "/rom", "");
+       system("/sbin/snapshot config_unpack");
+       foreachdir("/volatile/", handle_whiteout);
+       unsetenv("SNAPSHOT");
+       return -1;
+}
+
+static struct backend_handler snapshot_handlers[] = {
+{
+       .name = "config_read",
+       .cli = config_read,
+}, {
+       .name = "config_write",
+       .cli = config_write,
+}, {
+       .name = "read",
+       .cli = snapshot_read,
+}, {
+       .name = "write",
+       .cli = snapshot_write,
+}, {
+       .name = "mark",
+       .cli = snapshot_mark,
+}};
+
+static struct backend snapshot_backend = {
+       .name = "snapshot",
+       .num_handlers = ARRAY_SIZE(snapshot_handlers),
+       .handlers = snapshot_handlers,
+       .mount = snapshot_mount,
+       .info = snapshot_info,
+};
+BACKEND(snapshot_backend);
diff --git a/libfstools/volume.c b/libfstools/volume.c
new file mode 100644 (file)
index 0000000..4dc0a8e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/mount.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../fs-state.h"
+#include "volume.h"
+
+enum {
+       FLASH_NOR,
+       FLASH_NAND,
+};
+
+static LIST_HEAD(drivers);
+
+void
+volume_register_driver(struct driver *d)
+{
+       list_add(&d->list, &drivers);
+}
+
+struct volume* volume_find(char *name)
+{
+       struct volume *v = malloc(sizeof(struct volume));
+       struct driver *d;
+
+       if (!v)
+               return NULL;
+
+       list_for_each_entry(d, &drivers, list) {
+               memset(v, 0, sizeof(struct volume));
+
+               if (d->find && !d->find(v, name))
+                       return v;
+       }
+
+       free(v);
+
+       return NULL;
+}
diff --git a/libfstools/volume.h b/libfstools/volume.h
new file mode 100644 (file)
index 0000000..4fa5641
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _VOLUME_H__
+#define _VOLUME_H__
+
+#include <asm/byteorder.h>
+
+struct volume;
+
+typedef int (*volume_probe_t)(void);
+typedef int (*volume_init_t)(struct volume *v);
+typedef void (*volume_stop_t)(struct volume *v);
+typedef int (*volume_find_t)(struct volume *v, char *name);
+typedef int (*volume_identify_t)(struct volume *v);
+typedef int (*volume_read_t)(struct volume *v, void *buf, int offset, int length);
+typedef int (*volume_write_t)(struct volume *v, void *buf, int offset, int length);
+typedef int (*volume_erase_t)(struct volume *v, int start, int len);
+typedef int (*volume_erase_all_t)(struct volume *v);
+
+struct driver {
+        struct list_head        list;
+
+       char                    *name;
+       volume_probe_t          probe;
+       volume_init_t           init;
+       volume_stop_t           stop;
+       volume_find_t           find;
+       volume_identify_t       identify;
+       volume_read_t           read;
+       volume_write_t          write;
+       volume_erase_t          erase;
+       volume_erase_all_t      erase_all;
+};
+
+enum {
+       UNKNOWN_TYPE,
+       NANDFLASH,
+       NORFLASH,
+       UBIVOLUME,
+};
+
+struct volume {
+       struct driver   *drv;
+       void            *priv;
+       char            *name;
+       char            *blk;
+
+       __u64           size;
+       __u32           block_size;
+       int             type;
+};
+
+extern struct volume* volume_find(char *name);
+extern void volume_register_driver(struct driver *drv);
+
+static inline int volume_init(struct volume *v)
+{
+       if (v && v->drv->init)
+               return v->drv->init(v);
+       return -1;
+}
+
+static inline int volume_identify(struct volume *v)
+{
+       if (v && v->drv->identify)
+               return v->drv->identify(v);
+       return -1;
+}
+
+static inline int volume_erase(struct volume *v, int offset, int len)
+{
+       if (v && v->drv->erase)
+               return v->drv->erase(v, offset, len);
+       return -1;
+}
+
+static inline int volume_erase_all(struct volume *v)
+{
+       if (v && v->drv->erase_all)
+               return v->drv->erase_all(v);
+       return -1;
+}
+
+static inline int volume_read(struct volume *v, void *buf, int offset, int length)
+{
+       if (v && v->drv->read)
+               return v->drv->read(v, buf, offset, length);
+       return -1;
+}
+
+static inline int volume_write(struct volume *v, void *buf, int offset, int length)
+{
+       if (v && v->drv->write)
+               return v->drv->write(v, buf, offset, length);
+       return -1;
+}
+
+#define DRIVER(x)                                      \
+       static void __attribute__((constructor))        \
+       drv_register_##x(void) {                        \
+               volume_register_driver(&x);             \
+       }
+
+#endif