From 9303a84fb4bcf84ebcf58f2afdd7cac7dde0161f Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 6 Apr 2014 03:16:05 +0100 Subject: [PATCH] split fs-state back into seperate tools Signed-off-by: John Crispin --- .gitignore | 5 + CMakeLists.txt | 34 +- fs-state.c | 80 ---- fs-state.h | 80 ---- jffs2reset.c | 140 +++++++ libfstools/extroot.c | 10 +- libfstools/find.c | 2 +- libfstools/jffs2.c | 136 +------ libfstools/libfstools.h | 50 +++ libfstools/mount.c | 2 +- libfstools/mtd.c | 2 +- libfstools/overlay.c | 28 +- libfstools/snapshot.c | 203 +--------- libfstools/volume.c | 2 +- libubi/libubi.c | 2 +- libubi/ubi-user.h | 1 + libfstools/mount_root.c => mount_root.c | 85 +---- snapshot.c | 483 ++++++++++++++++++++++++ 18 files changed, 741 insertions(+), 604 deletions(-) delete mode 100644 fs-state.c delete mode 100644 fs-state.h create mode 100644 jffs2reset.c create mode 100644 libfstools/libfstools.h rename libfstools/mount_root.c => mount_root.c (55%) create mode 100644 snapshot.c diff --git a/.gitignore b/.gitignore index 5631098..d00efbf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ +jffs2reset +mount_root +snapshot_tool ubi block fs-state +*.so +*.a .* Makefile CMakeCache.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 4845954..8234b44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index b99a2ff..0000000 --- a/fs-state.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2014 John Crispin - * - * 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 -#include - -#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 index d168b76..0000000 --- a/fs-state.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2014 John Crispin - * - * 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 -#include - -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 index 0000000..0673982 --- /dev/null +++ b/jffs2reset.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2014 John Crispin + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/libfstools/extroot.c b/libfstools/extroot.c index 2ed9b37..a8c1028 100644 --- a/libfstools/extroot.c +++ b/libfstools/extroot.c @@ -20,11 +20,11 @@ #include #include -#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); diff --git a/libfstools/find.c b/libfstools/find.c index 35e37e7..71548f5 100644 --- a/libfstools/find.c +++ b/libfstools/find.c @@ -15,7 +15,7 @@ #include #include -#include "../fs-state.h" +#include "libfstools.h" int find_overlay_mount(char *overlay) diff --git a/libfstools/jffs2.c b/libfstools/jffs2.c index 9a06ff8..0da9f2f 100644 --- a/libfstools/jffs2.c +++ b/libfstools/jffs2.c @@ -27,7 +27,7 @@ #include #include -#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 index 0000000..598196e --- /dev/null +++ b/libfstools/libfstools.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 John Crispin + * + * 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 +#include + +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 diff --git a/libfstools/mount.c b/libfstools/mount.c index e7b57f0..efcfcd8 100644 --- a/libfstools/mount.c +++ b/libfstools/mount.c @@ -20,7 +20,7 @@ #include #include -#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/mtd.c b/libfstools/mtd.c index a0005d7..60326fe 100644 --- a/libfstools/mtd.c +++ b/libfstools/mtd.c @@ -21,7 +21,7 @@ #include #include -#include "../fs-state.h" +#include "libfstools.h" #include "volume.h" diff --git a/libfstools/overlay.c b/libfstools/overlay.c index 85237e4..06d0bfd 100644 --- a/libfstools/overlay.c +++ b/libfstools/overlay.c @@ -27,7 +27,7 @@ #include #include -#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); diff --git a/libfstools/snapshot.c b/libfstools/snapshot.c index bcbce94..6fe9c94 100644 --- a/libfstools/snapshot.c +++ b/libfstools/snapshot.c @@ -30,7 +30,7 @@ #include #include -#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); diff --git a/libfstools/volume.c b/libfstools/volume.c index 4dc0a8e..e81491e 100644 --- a/libfstools/volume.c +++ b/libfstools/volume.c @@ -15,7 +15,7 @@ #include #include -#include "../fs-state.h" +#include "libfstools.h" #include "volume.h" enum { diff --git a/libubi/libubi.c b/libubi/libubi.c index 3494f9d..d1f2a8b 100644 --- a/libubi/libubi.c +++ b/libubi/libubi.c @@ -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; diff --git a/libubi/ubi-user.h b/libubi/ubi-user.h index 1c06d88..a379353 100644 --- a/libubi/ubi-user.h +++ b/libubi/ubi-user.h @@ -166,6 +166,7 @@ #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/libfstools/mount_root.c b/mount_root.c similarity index 55% rename from libfstools/mount_root.c rename to mount_root.c index ce20604..e281c0b 100644 --- a/libfstools/mount_root.c +++ b/mount_root.c @@ -15,34 +15,11 @@ #include #include -#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(); -} +#include "libfstools/libfstools.h" +#include "libfstools/volume.h" static int -start(int argc, char **argv) +start(int argc, char *argv[1]) { struct volume *v = volume_find("rootfs_data"); @@ -58,7 +35,7 @@ start(int argc, char **argv) } extroot_prefix = ""; - if (!backend_mount("extroot")) { + if (!mount_extroot()) { fprintf(stderr, "fs-state: switched to extroot\n"); return 0; } @@ -69,11 +46,11 @@ start(int argc, char **argv) return ramoverlay(); case FS_JFFS2: - backend_mount("overlay"); + mount_overlay(); break; case FS_SNAPSHOT: - backend_mount("snapshot"); + mount_snapshot(); break; } @@ -81,7 +58,7 @@ start(int argc, char **argv) } static int -stop(int argc, char **argv) +stop(int argc, char *argv[1]) { if (!getenv("SHUTDOWN")) return -1; @@ -90,7 +67,7 @@ stop(int argc, char **argv) } static int -done(int argc, char **argv) +done(int argc, char *argv[1]) { struct volume *v = volume_find("rootfs_data"); @@ -106,43 +83,13 @@ done(int argc, char **argv) return 0; } -static int -info(int argc, char **argv) +int main(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; + 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; } - -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/snapshot.c b/snapshot.c new file mode 100644 index 0000000..05a6dee --- /dev/null +++ b/snapshot.c @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2014 John Crispin + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; +} -- 2.30.2