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)
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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");
-}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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