split fs-state back into seperate tools
authorJohn Crispin <blogic@openwrt.org>
Sun, 6 Apr 2014 02:16:05 +0000 (03:16 +0100)
committerJohn Crispin <blogic@openwrt.org>
Mon, 7 Apr 2014 12:27:38 +0000 (13:27 +0100)
Signed-off-by: John Crispin <blogic@openwrt.org>
19 files changed:
.gitignore
CMakeLists.txt
fs-state.c [deleted file]
fs-state.h [deleted file]
jffs2reset.c [new file with mode: 0644]
libfstools/extroot.c
libfstools/find.c
libfstools/jffs2.c
libfstools/libfstools.h [new file with mode: 0644]
libfstools/mount.c
libfstools/mount_root.c [deleted file]
libfstools/mtd.c
libfstools/overlay.c
libfstools/snapshot.c
libfstools/volume.c
libubi/libubi.c
libubi/ubi-user.h
mount_root.c [new file with mode: 0644]
snapshot.c [new file with mode: 0644]

index 5631098..d00efbf 100644 (file)
@@ -1,6 +1,11 @@
+jffs2reset
+mount_root
+snapshot_tool
 ubi
 block
 fs-state
+*.so
+*.a
 .*
 Makefile
 CMakeCache.txt
index 4845954..8234b44 100644 (file)
@@ -5,19 +5,18 @@ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
 
 SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
 
-ADD_EXECUTABLE(fs-state fs-state.c
-               libfstools/mount_root.c
+ADD_LIBRARY(fstools SHARED
                libfstools/snapshot.c
                libfstools/extroot.c
-               libfstools/jffs2.c
+               libfstools/overlay.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)
+TARGET_LINK_LIBRARIES(fstools ubox)
+INSTALL(TARGETS fstools LIBRARY DESTINATION lib)
 
-ADD_EXECUTABLE(block block.c
+ADD_LIBRARY(blkid-tiny SHARED
                libblkid-tiny/libblkid-tiny.c
                libblkid-tiny/mkdev.c
                libblkid-tiny/ext.c
@@ -27,12 +26,29 @@ ADD_EXECUTABLE(block block.c
                libblkid-tiny/swap.c
                libblkid-tiny/ubifs.c
                libblkid-tiny/squashfs.c)
-TARGET_LINK_LIBRARIES(block uci ubox blobmsg_json)
-INSTALL(TARGETS block RUNTIME DESTINATION sbin)
+INSTALL(TARGETS blkid-tiny LIBRARY DESTINATION lib)
 
-ADD_EXECUTABLE(ubi ubi.c
+ADD_LIBRARY(ubi-utils STATIC
                libubi/libubi.c
                libubi/libubi-tiny.c
                libubi/ubiutils-common.c)
 
+ADD_EXECUTABLE(mount_root mount_root.c)
+TARGET_LINK_LIBRARIES(mount_root fstools)
+INSTALL(TARGETS mount_root RUNTIME DESTINATION sbin)
+
+ADD_EXECUTABLE(block block.c)
+TARGET_LINK_LIBRARIES(block blkid-tiny uci ubox blobmsg_json)
+INSTALL(TARGETS block RUNTIME DESTINATION sbin)
+
+ADD_EXECUTABLE(jffs2reset jffs2reset.c)
+TARGET_LINK_LIBRARIES(jffs2reset fstools)
+INSTALL(TARGETS jffs2reset RUNTIME DESTINATION sbin)
+
+ADD_EXECUTABLE(snapshot_tool snapshot.c)
+TARGET_LINK_LIBRARIES(snapshot_tool fstools)
+INSTALL(TARGETS snapshot_tool RUNTIME DESTINATION sbin)
+
+ADD_EXECUTABLE(ubi ubi.c)
+TARGET_LINK_LIBRARIES(ubi ubi-utils)
 INSTALL(TARGETS ubi RUNTIME DESTINATION sbin)
diff --git a/fs-state.c b/fs-state.c
deleted file mode 100644 (file)
index b99a2ff..0000000
+++ /dev/null
@@ -1,80 +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 <stdio.h>
-#include <string.h>
-
-#include "fs-state.h"
-
-static LIST_HEAD(backends);
-
-void
-register_backend(struct backend *b)
-{
-       list_add(&b->list, &backends);
-}
-
-struct backend*
-find_backend(char *name)
-{
-       struct backend *b;
-
-       list_for_each_entry(b, &backends, list)
-               if (!strcmp(name, b->name))
-                       return b;
-       return NULL;
-}
-
-static void
-help(void)
-{
-       struct backend *b;
-
-       list_for_each_entry(b, &backends, list) {
-               int i;
-
-               if (b->desc)
-                       fprintf(stderr, "-> %s\n", b->name);
-               for (i = 0; i < b->num_handlers; i++)
-                       if (b->handlers[i].desc)
-                               fprintf(stderr, "--> %s\n", b->handlers[i].name);
-       }
-}
-
-int
-main(int argc, char **argv)
-{
-       struct backend *b;
-
-       if (argc > 1) list_for_each_entry(b, &backends, list) {
-               int i;
-
-               srand(time(NULL));
-
-               if (strcmp(argv[1], b->name))
-                       continue;
-
-               for (i = 0; i < b->num_handlers; i++)
-                       if (!strcmp(argv[2], b->handlers[i].name))
-                               return b->handlers[i].cli(argc - 2, &argv[2]);
-
-               if (b->cli)
-                       return b->cli(argc - 1, &argv[1]);
-
-               break;
-       }
-
-       help();
-
-       return 0;
-}
diff --git a/fs-state.h b/fs-state.h
deleted file mode 100644 (file)
index d168b76..0000000
+++ /dev/null
@@ -1,80 +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 _FS_STATE_H__
-#define _FS_STATE_H__
-
-#include <libubox/list.h>
-#include <libubox/blob.h>
-
-enum {
-       FS_NONE,
-       FS_SNAPSHOT,
-       FS_JFFS2,
-       FS_DEADCODE,
-};
-
-typedef int (*backend_cli_t)(int argc, char **argv);
-typedef int (*backend_mount_t)(void);
-typedef int (*backend_info_t)(void);
-
-extern char const *extroot_prefix;
-
-struct backend_handler
-{
-       char            *name;
-       char            *desc;
-       backend_cli_t   cli;
-};
-
-struct backend
-{
-       struct list_head        list;
-       char                    *name;
-       char                    *desc;
-       int                     num_handlers;
-       backend_cli_t           cli;
-       backend_mount_t         mount;
-       backend_info_t          info;
-       struct backend_handler  *handlers;
-};
-
-void register_backend(struct backend *);
-struct backend* find_backend(char *);
-int backend_mount(char *name);
-
-#define BACKEND(x)                                     \
-       static void __attribute__((constructor))        \
-       register_##x(void) {                            \
-               register_backend(&x);                   \
-       }
-
-int mount_move(char *oldroot, char *newroot, char *dir);
-int pivot(char *new, char *old);
-int fopivot(char *rw_root, char *ro_root);
-int ramoverlay(void);
-
-int find_overlay_mount(char *overlay);
-char* find_mount(char *mp);
-char* find_mount_point(char *block, char *fs);
-int find_filesystem(char *fs);
-int find_mtd_block(char *name, char *part, int plen);
-int find_mtd_char(char *name, char *part, int plen);
-
-int jffs2_ready(char *mtd);
-int jffs2_switch(int argc, char **argv);
-
-int handle_whiteout(const char *dir);
-void foreachdir(const char *dir, int (*cb)(const char*));
-
-#endif
diff --git a/jffs2reset.c b/jffs2reset.c
new file mode 100644 (file)
index 0000000..0673982
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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 <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libfstools/libfstools.h"
+#include "libfstools/volume.h"
+
+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
+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
+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 main(int argc, char **argv)
+{
+       if (!strcmp(*argv, "jffs2mark"))
+               return jffs2_mark(argc, argv);
+       return jffs2_reset(argc, argv);
+}
index 2ed9b37..a8c1028 100644 (file)
 #include <sys/stat.h>
 #include <sys/wait.h>
 
-#include "../fs-state.h"
+#include "libfstools.h"
 
 char const *extroot_prefix = NULL;
 
-static int mount_extroot(void)
+int mount_extroot(void)
 {
        char block_path[32];
        char kmod_loader[64];
@@ -93,9 +93,3 @@ static int mount_extroot(void)
        }
        return -1;
 }
-
-static struct backend extroot_backend = {
-       .name = "extroot",
-       .mount = mount_extroot,
-};
-BACKEND(extroot_backend);
index 35e37e7..71548f5 100644 (file)
@@ -15,7 +15,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "../fs-state.h"
+#include "libfstools.h"
 
 int
 find_overlay_mount(char *overlay)
index 9a06ff8..0da9f2f 100644 (file)
@@ -27,7 +27,7 @@
 #include <dirent.h>
 #include <fcntl.h>
 
-#include "../fs-state.h"
+#include "libfstools.h"
 #include "volume.h"
 
 #define SWITCH_JFFS2 "/tmp/.switch_jffs2"
@@ -156,113 +156,6 @@ handle_whiteout(const char *dir)
        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)
 {
@@ -270,7 +163,7 @@ jffs2_switch(int argc, char **argv)
        char *mp;
        int ret = -1;
 
-       if (find_overlay_mount("overlayfs:/tmp/root"))
+       if (find_mount_overlay("overlayfs:/tmp/root"))
                return -1;
 
        if (find_filesystem("overlay")) {
@@ -313,7 +206,7 @@ jffs2_switch(int argc, char **argv)
        return ret;
 }
 
-static int overlay_mount_fs(void)
+static int mount_overlay_fs(void)
 {
        struct volume *v;
 
@@ -339,7 +232,7 @@ static int overlay_mount_fs(void)
        return -1;
 }
 
-static int overlay_mount(void)
+int mount_overlay(void)
 {
        struct volume *v = volume_find("rootfs_data");;
        char *mp;
@@ -353,10 +246,10 @@ static int overlay_mount(void)
                return -1;
        }
 
-       overlay_mount_fs();
+       mount_overlay_fs();
 
        extroot_prefix = "/tmp/overlay";
-       if (!backend_mount("extroot")) {
+       if (!mount_extroot()) {
                fprintf(stderr, "fs-state: switched to extroot\n");
                return 0;
        }
@@ -369,20 +262,3 @@ static int overlay_mount(void)
 
        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/libfstools.h b/libfstools/libfstools.h
new file mode 100644 (file)
index 0000000..598196e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 _FS_STATE_H__
+#define _FS_STATE_H__
+
+#include <libubox/list.h>
+#include <libubox/blob.h>
+
+enum {
+       FS_NONE,
+       FS_SNAPSHOT,
+       FS_JFFS2,
+       FS_DEADCODE,
+};
+
+extern char const *extroot_prefix;
+extern int mount_extroot(void);
+extern int mount_snapshot(void);
+extern int mount_overlay(void);
+
+extern int mount_move(char *oldroot, char *newroot, char *dir);
+extern int pivot(char *new, char *old);
+extern int fopivot(char *rw_root, char *ro_root);
+extern int ramoverlay(void);
+
+extern int find_overlay_mount(char *overlay);
+extern char* find_mount(char *mp);
+extern char* find_mount_point(char *block, char *fs);
+extern int find_filesystem(char *fs);
+extern int find_mtd_block(char *name, char *part, int plen);
+extern int find_mtd_char(char *name, char *part, int plen);
+
+extern int jffs2_ready(char *mtd);
+extern int jffs2_switch(int argc, char **argv);
+
+extern int handle_whiteout(const char *dir);
+extern void foreachdir(const char *dir, int (*cb)(const char*));
+
+#endif
index e7b57f0..efcfcd8 100644 (file)
@@ -20,7 +20,7 @@
 #include <unistd.h>
 #include <string.h>
 
-#include "../fs-state.h"
+#include "libfstools.h"
 
 /* this is a raw syscall - man 2 pivot_root */
 extern int pivot_root(const char *new_root, const char *put_old);
diff --git a/libfstools/mount_root.c b/libfstools/mount_root.c
deleted file mode 100644 (file)
index ce20604..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 "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);
index a0005d7..60326fe 100644 (file)
@@ -21,7 +21,7 @@
 #include <stdlib.h>
 #include <mtd/mtd-user.h>
 
-#include "../fs-state.h"
+#include "libfstools.h"
 
 #include "volume.h"
 
index 85237e4..06d0bfd 100644 (file)
@@ -27,7 +27,7 @@
 #include <dirent.h>
 #include <fcntl.h>
 
-#include "../fs-state.h"
+#include "libfstools.h"
 #include "volume.h"
 
 #define SWITCH_JFFS2 "/tmp/.switch_jffs2"
@@ -142,18 +142,6 @@ handle_whiteout(const char *dir)
        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)
 {
@@ -182,7 +170,7 @@ jffs2_switch(int argc, char **argv)
                /* fall through */
 
        case FS_DEADCODE:
-               ret = switch2jffs();
+               ret = switch2jffs(v);
                if (!ret) {
                        fprintf(stderr, "doing fo cleanup\n");
                        umount2("/tmp/root", MNT_DETACH);
@@ -230,7 +218,7 @@ static int overlay_mount_fs(void)
        return -1;
 }
 
-static int overlay_mount(void)
+int mount_overlay(void)
 {
        struct volume *v = volume_find("rootfs_data");;
        char *mp;
@@ -247,8 +235,8 @@ static int overlay_mount(void)
        overlay_mount_fs();
 
        extroot_prefix = "/tmp/overlay";
-       if (!backend_mount("extroot")) {
-               fprintf(stderr, "fs-state: switched to extroot\n");
+       if (!mount_extroot()) {
+               fprintf(stderr, "switched to extroot\n");
                return 0;
        }
 
@@ -260,9 +248,3 @@ static int overlay_mount(void)
 
        return -1;
 }
-
-static struct backend overlay_backend = {
-       .name = "overlay",
-       .mount = overlay_mount,
-};
-BACKEND(overlay_backend);
index bcbce94..6fe9c94 100644 (file)
@@ -30,7 +30,7 @@
 #include <libubox/blob.h>
 #include <libubox/md5.h>
 
-#include "../fs-state.h"
+#include "libfstools.h"
 #include "volume.h"
 
 #define PATH_MAX       256
@@ -172,43 +172,6 @@ config_find(struct volume *v, struct file_header *conf, struct file_header *sent
        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)
 {
@@ -368,139 +331,6 @@ volatile_write(struct volume *v, uint32_t _seq)
        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)
 {
@@ -556,8 +386,8 @@ _ramoverlay(char *rom, char *overlay)
        return fopivot(overlay, rom);
 }
 
-static int
-snapshot_mount(void)
+int
+mount_snapshot(void)
 {
        snapshot_sync();
        setenv("SNAPSHOT", "magic", 1);
@@ -573,30 +403,3 @@ snapshot_mount(void)
        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);
index 4dc0a8e..e81491e 100644 (file)
@@ -15,7 +15,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "../fs-state.h"
+#include "libfstools.h"
 #include "volume.h"
 
 enum {
index 3494f9d..d1f2a8b 100644 (file)
@@ -861,7 +861,7 @@ int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev)
                sys_errmsg("cannot open \"%s\"", node);
                return -1;
        }
-       ret = ioctl(fd, UBI_IOCDET, &ubi_dev);
+       ret = ioctl(fd, UBI_IOCFDET, &ubi_dev);
        if (ret == -1)
                goto out_close;
 
index 1c06d88..a379353 100644 (file)
 #define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req)
 /* Detach an MTD device */
 #define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t)
+#define UBI_IOCFDET _IOW(UBI_CTRL_IOC_MAGIC, 99, int32_t)
 
 /* ioctl commands of UBI volume character devices */
 
diff --git a/mount_root.c b/mount_root.c
new file mode 100644 (file)
index 0000000..e281c0b
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 "libfstools/libfstools.h"
+#include "libfstools/volume.h"
+
+static int
+start(int argc, char *argv[1])
+{
+       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 (!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:
+               mount_overlay();
+               break;
+
+       case FS_SNAPSHOT:
+               mount_snapshot();
+               break;
+       }
+
+       return 0;
+}
+
+static int
+stop(int argc, char *argv[1])
+{
+       if (!getenv("SHUTDOWN"))
+               return -1;
+
+       return 0;
+}
+
+static int
+done(int argc, char *argv[1])
+{
+       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;
+}
+
+int main(int argc, char **argv)
+{
+       if (argc < 2)
+               return start(argc, argv);
+       if (!strcmp(argv[1], "stop"))
+               return stop(argc, argv);
+       if (!strcmp(argv[1], "done"))
+               return done(argc, argv);
+       return -1;
+}
diff --git a/snapshot.c b/snapshot.c
new file mode 100644 (file)
index 0000000..05a6dee
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * 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 "libfstools/libfstools.h"
+#include "libfstools/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_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[1])
+{
+       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[1])
+{
+       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[1])
+{
+       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[1])
+{
+       __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[1])
+{
+       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;
+}
+
+int main(int argc, char **argv)
+{
+       if (argc < 2)
+               return -1;
+
+       if (!strcmp(argv[1], "config_read"))
+               return config_read(argc, argv);
+       if (!strcmp(argv[1], "config_write"))
+               return config_write(argc, argv);
+       if (!strcmp(argv[1], "read"))
+               return snapshot_read(argc, argv);
+       if (!strcmp(argv[1], "write"))
+               return snapshot_write(argc, argv);
+       if (!strcmp(argv[1], "mark"))
+               return snapshot_mark(argc, argv);
+       return -1;
+}