initial import of uboox utilities
authorJohn Crispin <blogic@openwrt.org>
Tue, 23 Apr 2013 18:52:03 +0000 (20:52 +0200)
committerJohn Crispin <blogic@openwrt.org>
Thu, 25 Apr 2013 09:11:26 +0000 (11:11 +0200)
Signed-off-by: John Crispin <blogic@openwrt.org>
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
Makefile [new file with mode: 0644]
board.c [new file with mode: 0644]
kmodloader.c [new file with mode: 0644]
lsbloader.c [new file with mode: 0644]
mount_root.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..c76b578
--- /dev/null
@@ -0,0 +1,10 @@
+mount_root
+board
+kmodloader
+lsbloader
+.*
+Makefile
+CMakeCache.txt
+CMakeFiles
+*.cmake
+install_manifest.txt
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..847ce08
--- /dev/null
@@ -0,0 +1,46 @@
+cmake_minimum_required(VERSION 2.6)
+
+PROJECT(procd C)
+ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
+
+SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+
+IF(APPLE)
+  INCLUDE_DIRECTORIES(/opt/local/include)
+  LINK_DIRECTORIES(/opt/local/lib)
+ENDIF()
+
+find_library(json NAMES json-c json)
+SET(LIBS ubox ubus ${json} blobmsg_json)
+
+IF(DEBUG)
+  ADD_DEFINITIONS(-DDEBUG -g3)
+ENDIF()
+
+ADD_EXECUTABLE(mount_root mount_root.c)
+TARGET_LINK_LIBRARIES(mount_root ${LIBS})
+
+INSTALL(TARGETS mount_root
+       RUNTIME DESTINATION sbin
+)
+
+ADD_EXECUTABLE(kmodloader kmodloader.c)
+TARGET_LINK_LIBRARIES(kmodloader ${LIBS})
+
+INSTALL(TARGETS kmodloader
+       RUNTIME DESTINATION sbin
+)
+
+ADD_EXECUTABLE(board board.c)
+TARGET_LINK_LIBRARIES(board ${LIBS})
+
+INSTALL(TARGETS board
+       RUNTIME DESTINATION sbin
+)
+
+ADD_EXECUTABLE(lsbloader lsbloader.c)
+TARGET_LINK_LIBRARIES(lsbloader ${LIBS})
+
+INSTALL(TARGETS lsbloader
+       RUNTIME DESTINATION sbin
+)
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..8357c9f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,331 @@
+# CMAKE generated file: DO NOT EDIT!
+# Generated by "Unix Makefiles" Generator, CMake Version 2.8
+
+# Default target executed when no arguments are given to make.
+default_target: all
+.PHONY : default_target
+
+#=============================================================================
+# Special targets provided by cmake.
+
+# Disable implicit rules so canonical targets will work.
+.SUFFIXES:
+
+# Remove some rules from gmake that .SUFFIXES does not remove.
+SUFFIXES =
+
+.SUFFIXES: .hpux_make_needs_suffix_list
+
+# Suppress display of executed commands.
+$(VERBOSE).SILENT:
+
+# A target that is always out of date.
+cmake_force:
+.PHONY : cmake_force
+
+#=============================================================================
+# Set environment variables for the build.
+
+# The shell in which to execute make rules.
+SHELL = /bin/sh
+
+# The CMake executable.
+CMAKE_COMMAND = /usr/bin/cmake
+
+# The command to remove a file.
+RM = /usr/bin/cmake -E remove -f
+
+# Escaping for special characters.
+EQUALS = =
+
+# The top-level source directory on which CMake was run.
+CMAKE_SOURCE_DIR = /openwrt/ubox
+
+# The top-level build directory on which CMake was run.
+CMAKE_BINARY_DIR = /openwrt/ubox
+
+#=============================================================================
+# Targets provided globally by CMake.
+
+# Special rule for the target edit_cache
+edit_cache:
+       @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running interactive CMake command-line interface..."
+       /usr/bin/cmake -i .
+.PHONY : edit_cache
+
+# Special rule for the target edit_cache
+edit_cache/fast: edit_cache
+.PHONY : edit_cache/fast
+
+# Special rule for the target install
+install: preinstall
+       @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
+       /usr/bin/cmake -P cmake_install.cmake
+.PHONY : install
+
+# Special rule for the target install
+install/fast: preinstall/fast
+       @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
+       /usr/bin/cmake -P cmake_install.cmake
+.PHONY : install/fast
+
+# Special rule for the target install/local
+install/local: preinstall
+       @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
+       /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
+.PHONY : install/local
+
+# Special rule for the target install/local
+install/local/fast: install/local
+.PHONY : install/local/fast
+
+# Special rule for the target install/strip
+install/strip: preinstall
+       @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
+       /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
+.PHONY : install/strip
+
+# Special rule for the target install/strip
+install/strip/fast: install/strip
+.PHONY : install/strip/fast
+
+# Special rule for the target list_install_components
+list_install_components:
+       @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\""
+.PHONY : list_install_components
+
+# Special rule for the target list_install_components
+list_install_components/fast: list_install_components
+.PHONY : list_install_components/fast
+
+# Special rule for the target rebuild_cache
+rebuild_cache:
+       @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
+       /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
+.PHONY : rebuild_cache
+
+# Special rule for the target rebuild_cache
+rebuild_cache/fast: rebuild_cache
+.PHONY : rebuild_cache/fast
+
+# The main all target
+all: cmake_check_build_system
+       $(CMAKE_COMMAND) -E cmake_progress_start /openwrt/ubox/CMakeFiles /openwrt/ubox/CMakeFiles/progress.marks
+       $(MAKE) -f CMakeFiles/Makefile2 all
+       $(CMAKE_COMMAND) -E cmake_progress_start /openwrt/ubox/CMakeFiles 0
+.PHONY : all
+
+# The main clean target
+clean:
+       $(MAKE) -f CMakeFiles/Makefile2 clean
+.PHONY : clean
+
+# The main clean target
+clean/fast: clean
+.PHONY : clean/fast
+
+# Prepare targets for installation.
+preinstall: all
+       $(MAKE) -f CMakeFiles/Makefile2 preinstall
+.PHONY : preinstall
+
+# Prepare targets for installation.
+preinstall/fast:
+       $(MAKE) -f CMakeFiles/Makefile2 preinstall
+.PHONY : preinstall/fast
+
+# clear depends
+depend:
+       $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
+.PHONY : depend
+
+#=============================================================================
+# Target rules for targets named board
+
+# Build rule for target.
+board: cmake_check_build_system
+       $(MAKE) -f CMakeFiles/Makefile2 board
+.PHONY : board
+
+# fast build rule for target.
+board/fast:
+       $(MAKE) -f CMakeFiles/board.dir/build.make CMakeFiles/board.dir/build
+.PHONY : board/fast
+
+#=============================================================================
+# Target rules for targets named kmodloader
+
+# Build rule for target.
+kmodloader: cmake_check_build_system
+       $(MAKE) -f CMakeFiles/Makefile2 kmodloader
+.PHONY : kmodloader
+
+# fast build rule for target.
+kmodloader/fast:
+       $(MAKE) -f CMakeFiles/kmodloader.dir/build.make CMakeFiles/kmodloader.dir/build
+.PHONY : kmodloader/fast
+
+#=============================================================================
+# Target rules for targets named lsbloader
+
+# Build rule for target.
+lsbloader: cmake_check_build_system
+       $(MAKE) -f CMakeFiles/Makefile2 lsbloader
+.PHONY : lsbloader
+
+# fast build rule for target.
+lsbloader/fast:
+       $(MAKE) -f CMakeFiles/lsbloader.dir/build.make CMakeFiles/lsbloader.dir/build
+.PHONY : lsbloader/fast
+
+#=============================================================================
+# Target rules for targets named mount_root
+
+# Build rule for target.
+mount_root: cmake_check_build_system
+       $(MAKE) -f CMakeFiles/Makefile2 mount_root
+.PHONY : mount_root
+
+# fast build rule for target.
+mount_root/fast:
+       $(MAKE) -f CMakeFiles/mount_root.dir/build.make CMakeFiles/mount_root.dir/build
+.PHONY : mount_root/fast
+
+board.o: board.c.o
+.PHONY : board.o
+
+# target to build an object file
+board.c.o:
+       $(MAKE) -f CMakeFiles/board.dir/build.make CMakeFiles/board.dir/board.c.o
+.PHONY : board.c.o
+
+board.i: board.c.i
+.PHONY : board.i
+
+# target to preprocess a source file
+board.c.i:
+       $(MAKE) -f CMakeFiles/board.dir/build.make CMakeFiles/board.dir/board.c.i
+.PHONY : board.c.i
+
+board.s: board.c.s
+.PHONY : board.s
+
+# target to generate assembly for a file
+board.c.s:
+       $(MAKE) -f CMakeFiles/board.dir/build.make CMakeFiles/board.dir/board.c.s
+.PHONY : board.c.s
+
+kmodloader.o: kmodloader.c.o
+.PHONY : kmodloader.o
+
+# target to build an object file
+kmodloader.c.o:
+       $(MAKE) -f CMakeFiles/kmodloader.dir/build.make CMakeFiles/kmodloader.dir/kmodloader.c.o
+.PHONY : kmodloader.c.o
+
+kmodloader.i: kmodloader.c.i
+.PHONY : kmodloader.i
+
+# target to preprocess a source file
+kmodloader.c.i:
+       $(MAKE) -f CMakeFiles/kmodloader.dir/build.make CMakeFiles/kmodloader.dir/kmodloader.c.i
+.PHONY : kmodloader.c.i
+
+kmodloader.s: kmodloader.c.s
+.PHONY : kmodloader.s
+
+# target to generate assembly for a file
+kmodloader.c.s:
+       $(MAKE) -f CMakeFiles/kmodloader.dir/build.make CMakeFiles/kmodloader.dir/kmodloader.c.s
+.PHONY : kmodloader.c.s
+
+lsbloader.o: lsbloader.c.o
+.PHONY : lsbloader.o
+
+# target to build an object file
+lsbloader.c.o:
+       $(MAKE) -f CMakeFiles/lsbloader.dir/build.make CMakeFiles/lsbloader.dir/lsbloader.c.o
+.PHONY : lsbloader.c.o
+
+lsbloader.i: lsbloader.c.i
+.PHONY : lsbloader.i
+
+# target to preprocess a source file
+lsbloader.c.i:
+       $(MAKE) -f CMakeFiles/lsbloader.dir/build.make CMakeFiles/lsbloader.dir/lsbloader.c.i
+.PHONY : lsbloader.c.i
+
+lsbloader.s: lsbloader.c.s
+.PHONY : lsbloader.s
+
+# target to generate assembly for a file
+lsbloader.c.s:
+       $(MAKE) -f CMakeFiles/lsbloader.dir/build.make CMakeFiles/lsbloader.dir/lsbloader.c.s
+.PHONY : lsbloader.c.s
+
+mount_root.o: mount_root.c.o
+.PHONY : mount_root.o
+
+# target to build an object file
+mount_root.c.o:
+       $(MAKE) -f CMakeFiles/mount_root.dir/build.make CMakeFiles/mount_root.dir/mount_root.c.o
+.PHONY : mount_root.c.o
+
+mount_root.i: mount_root.c.i
+.PHONY : mount_root.i
+
+# target to preprocess a source file
+mount_root.c.i:
+       $(MAKE) -f CMakeFiles/mount_root.dir/build.make CMakeFiles/mount_root.dir/mount_root.c.i
+.PHONY : mount_root.c.i
+
+mount_root.s: mount_root.c.s
+.PHONY : mount_root.s
+
+# target to generate assembly for a file
+mount_root.c.s:
+       $(MAKE) -f CMakeFiles/mount_root.dir/build.make CMakeFiles/mount_root.dir/mount_root.c.s
+.PHONY : mount_root.c.s
+
+# Help Target
+help:
+       @echo "The following are some of the valid targets for this Makefile:"
+       @echo "... all (the default if no target is provided)"
+       @echo "... clean"
+       @echo "... depend"
+       @echo "... board"
+       @echo "... edit_cache"
+       @echo "... install"
+       @echo "... install/local"
+       @echo "... install/strip"
+       @echo "... kmodloader"
+       @echo "... list_install_components"
+       @echo "... lsbloader"
+       @echo "... mount_root"
+       @echo "... rebuild_cache"
+       @echo "... board.o"
+       @echo "... board.i"
+       @echo "... board.s"
+       @echo "... kmodloader.o"
+       @echo "... kmodloader.i"
+       @echo "... kmodloader.s"
+       @echo "... lsbloader.o"
+       @echo "... lsbloader.i"
+       @echo "... lsbloader.s"
+       @echo "... mount_root.o"
+       @echo "... mount_root.i"
+       @echo "... mount_root.s"
+.PHONY : help
+
+
+
+#=============================================================================
+# Special targets to cleanup operation of make.
+
+# Special rule to run CMake to check the build system integrity.
+# No rule that depends on this can have commands that come from listfiles
+# because they might be regenerated.
+cmake_check_build_system:
+       $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
+.PHONY : cmake_check_build_system
+
diff --git a/board.c b/board.c
new file mode 100644 (file)
index 0000000..2dabc49
--- /dev/null
+++ b/board.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <libubox/blobmsg_json.h>
+
+#define CONFIG_FILE    "/etc/boards.json"
+#define MODEL_FILE     "/proc/devicetree/model"
+#define CPU_INFO       "/proc/cpuinfo"
+
+enum {
+       BOARD_ID,
+       __BOARD_MAX
+};
+
+static struct blob_buf b;
+
+static const struct blobmsg_policy board_policy[__BOARD_MAX] = {
+       [BOARD_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING },
+};
+
+static char* detect_mips_machine(void)
+{
+       FILE *fp;
+       static char line[64];
+       char *ret = NULL;
+
+       fp = fopen(CPU_INFO, "r");
+       if (!fp) {
+               perror("fopen");
+               return NULL;
+       }
+
+       while (fgets(line, sizeof(line), fp)) {
+               if (!strncmp(line, "machine", 7)) {
+                       char *machine = strstr(line, ": ");
+
+                       if (!machine)
+                               continue;
+
+                       machine[strlen(machine) - 1] = '\0';
+
+                       ret = &machine[2];
+                       break;
+               }
+       }
+
+       fclose(fp);
+
+       return ret;
+}
+
+static char* detect_devicetree_model(void)
+{
+       FILE *fp;
+       static char line[64];
+       char *ret = NULL;
+       struct stat s;
+
+       if (stat(MODEL_FILE, &s) || !S_ISREG(s.st_mode))
+               return NULL;
+
+       fp = fopen(MODEL_FILE, "r");
+       if (!fp) {
+               perror("fopen");
+               return NULL;
+       }
+
+       if (!fgets(line, sizeof(line), fp))
+               ret = line;
+
+       fclose(fp);
+
+       return ret;
+}
+
+static char* board_id(struct blob_attr *msg)
+{
+       struct blob_attr *tb[__BOARD_MAX];
+
+       blobmsg_parse(board_policy, __BOARD_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
+       if (tb[BOARD_ID])
+               return blobmsg_data(tb[BOARD_ID]);
+
+       return NULL;
+}
+
+static void write_file(char *file, char *val)
+{
+       FILE *fp;
+
+       fp = fopen(file, "w");
+       if (fp) {
+               fprintf(fp, val);
+               fclose(fp);
+       } else {
+               perror("fopen");
+       }
+
+}
+
+static int set_board(char *board, char *boardid)
+{
+       if (!boardid) {
+               fprintf(stderr, "failed to detect board %s\n", board);
+               return -1;
+       }
+       printf("detected %s - %s\n", board, boardid);
+
+       mkdir("/tmp/sysinfo", 0755);
+
+       write_file("/tmp/sysinfo/model", board);
+       write_file("/tmp/sysinfo/board_name", boardid);
+
+       return 0;
+}
+
+static int load_json(char *file)
+{
+       struct stat s;
+       int fd;
+       char *buf;
+
+       if (stat(file, &s) || !S_ISREG(s.st_mode)) {
+               fprintf(stderr, "failed to open %s\n", file);
+               return -1;
+       }
+
+       buf = malloc(s.st_size + 1);
+       if (!buf) {
+               perror("malloc");
+               return -1;
+       }
+
+       fd = open(file, O_RDONLY);
+       if (!fd) {
+               perror("open");
+               return -1;
+       }
+
+       if (read(fd, buf, s.st_size) != s.st_size) {
+               fprintf(stderr, "failed to read %s - %d bytes\n", file, (int)s.st_size);
+               close(fd);
+               return -1;
+       }
+       close(fd);
+       buf[s.st_size] = '\0';
+
+       blob_buf_init(&b, 0);
+       if (!blobmsg_add_json_from_string(&b, buf)) {
+               fprintf(stderr, "Failed to read json\n");
+               return - 1;
+       }
+       free(buf);
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       struct blob_attr *cur;
+       char *board;
+       int rem;
+
+       board = detect_mips_machine();
+       if (!board)
+               detect_devicetree_model();
+       if (!board) {
+               fprintf(stderr, "failed to detect board\n");
+               return -1;
+       }
+
+       if (load_json(CONFIG_FILE))
+               return -1;
+
+       blob_for_each_attr(cur, b.head, rem) {
+               if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
+                       continue;
+               if (!strcmp(blobmsg_name(cur), board)) {
+                       char *id = board_id(cur);
+
+                       if (id)
+                               return set_board(board, id);
+                       }
+       }
+
+       fprintf(stderr, "failed to identify %s\n", board);
+
+       return -1;
+}
diff --git a/kmodloader.c b/kmodloader.c
new file mode 100644 (file)
index 0000000..62b2d5b
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 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.
+ */
+
+#define _GNU_SOURCE
+#include <sys/syscall.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <values.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <glob.h>
+#include <sys/utsname.h>
+
+#define DEF_MOD_PATH "/lib/modules/%s/%s.ko"
+static int insmod(char *module, const char *options)
+{
+       struct utsname ver;
+       char path[256];
+       void *data = 0;
+       struct stat s;
+       int fd, ret = -1;
+
+       uname(&ver);
+       snprintf(path, 256, DEF_MOD_PATH, ver.release, module);
+
+       if (stat(path, &s)) {
+               fprintf(stderr, "missing module %s\n", path);
+               return ret;
+       }
+
+       fd = open(path, O_RDONLY);
+       if (!fd) {
+               fprintf(stderr, "cannot open %s\n", path);
+               return ret;
+       }
+
+       data = malloc(s.st_size);
+       if (read(fd, data, s.st_size) == s.st_size) {
+               ret = syscall(__NR_init_module, data, s.st_size, options);
+               if (ret)
+                       fprintf(stderr, "failed insert %s\n", module);
+       } else {
+               fprintf(stderr, "failed to read full module %s\n", path);
+       }
+
+       close(fd);
+       free(data);
+
+       return ret;
+}
+
+/*static void rmmod(char *module)
+{
+       syscall(__NR_delete_module, module, 0);
+}*/
+
+int main(int argc, char **argv)
+{
+       glob_t gl;
+       int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
+       char *tmp = malloc(256);
+       char *dir = "/etc/modules.d/*";
+
+       if (argc > 1)
+               dir = argv[1];
+
+       syslog(0, "kmodloader: loading kernel modules from %s\n", dir);
+
+       if (glob(dir, gl_flags, NULL, &gl) >= 0) {
+               int j;
+
+               for (j = 0; j < gl.gl_pathc; j++) {
+                       FILE *fp = fopen(gl.gl_pathv[j], "r");
+
+                       if (!fp) {
+                               fprintf(stderr, "failed to open %s\n", gl.gl_pathv[j]);
+                       } else {
+                               char mod[64];
+
+                               while (fgets(mod, 64, fp)) {
+                                       mod[strlen(mod) - 1] = '\0';
+                                       insmod(mod, "");
+                               }
+                               fclose(fp);
+                       }
+               }
+       }
+
+       globfree(&gl);
+       free(tmp);
+
+       return 0;
+}
diff --git a/lsbloader.c b/lsbloader.c
new file mode 100644 (file)
index 0000000..b40a505
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; version 2 of the License
+ *
+ *   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.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *   Copyright (C) 2012 John Crispin <blogic@openwrt.org> 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#define __USE_GNU
+#include <string.h>
+#include <regex.h>
+#include <glob.h>
+
+#include <libubox/list.h>
+#include <libubox/blobmsg_json.h>
+#include "libubus.h"
+
+struct initd {
+       struct list_head list;
+
+       char *name;
+       char *exec;
+       char *desc;
+       char *tpl;
+       char **deps;
+       int start;
+       int stop;
+};
+
+static LIST_HEAD(initds);
+static regex_t pat_provides, pat_require, pat_start, pat_stop, pat_desc, pat_exec, pat_tpl;
+static struct ubus_context *ctx;
+static struct blob_buf b;
+static uint32_t service;
+
+static void initd_free(struct initd *i)
+{
+       if (i->name)
+               free(i->name);
+       if (i->exec)
+               free(i->exec);
+       if (i->desc)
+               free(i->desc);
+       if (i->tpl)
+               free(i->tpl);
+}
+
+static int initd_parse(const char *file)
+{
+       FILE *fp;
+       struct initd *i;
+       regmatch_t matches[2];
+       char buffer[1024];
+       ssize_t len;
+
+       fp = fopen(file, "r");
+       if (!fp) {
+               fprintf(stderr, "failed to open %s\n", file);
+               return -1;
+       }
+       len = fread(buffer, 1, sizeof(buffer) - 1, fp);
+       fclose(fp);
+
+       if (len < 1) {
+               fprintf(stderr, "failed to read from %s\n", file);
+               return -1;
+       }
+       buffer[len] = '\0';
+
+       i = malloc(sizeof(struct initd));
+       if (!i) {
+               fprintf(stderr, "failed to alloc initd struct\n");
+               return -1;
+       }
+       memset(i, 0, sizeof(*i));
+
+       if (!regexec(&pat_provides, buffer, 2, matches, 0))
+               i->name = strndup(buffer + matches[1].rm_so, (size_t)matches[1].rm_eo - matches[1].rm_so);
+       if (!regexec(&pat_exec, buffer, 2, matches, 0))
+               i->exec = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
+       if (!regexec(&pat_desc, buffer, 2, matches, 0))
+               i->desc = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
+       if (!regexec(&pat_tpl, buffer, 2, matches, 0))
+               i->tpl = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
+       if (!regexec(&pat_start, buffer, 2, matches, 0))
+               i->start += atoi(buffer + matches[1].rm_so);
+       if (!regexec(&pat_stop, buffer, 2, matches, 0))
+               i->stop += atoi(buffer + matches[1].rm_so);
+
+       if (i->name && i->exec)
+               list_add(&i->list, &initds);
+       else
+               initd_free(i);
+
+       return 0;
+}
+
+static void initd_init(void)
+{
+       int gl_flags = GLOB_NOESCAPE | GLOB_MARK;
+       glob_t gl;
+
+       regcomp(&pat_provides, "# Provides:[ \t]*([a-zA-Z0-9]+)", REG_EXTENDED);
+       regcomp(&pat_require, "# Required-Start:[ \t]*([a-zA-Z0-9 ]+)", REG_EXTENDED);
+       regcomp(&pat_start, "# Default-Start:[ \t]*([0-9])", REG_EXTENDED);
+       regcomp(&pat_stop, "# Default-Stop:[ \t]*([0-9])", REG_EXTENDED);
+       regcomp(&pat_desc, "# Description:[ \t]*([a-zA-Z0-9 ]+)", REG_EXTENDED);
+       regcomp(&pat_exec, "# X-Exec:[ \t]*([a-zA-Z0-9/ ]+)", REG_EXTENDED);
+       regcomp(&pat_tpl, "# X-Template:[ \t]*([a-zA-Z0-9/.]+)", REG_EXTENDED);
+
+       if (glob("/etc/rc.d/P*", gl_flags, NULL, &gl) >= 0) {
+               int j;
+               for (j = 0; j < gl.gl_pathc; j++)
+                       initd_parse(gl.gl_pathv[j]);
+       }
+       globfree(&gl);
+
+       regfree(&pat_provides);
+       regfree(&pat_require);
+       regfree(&pat_start);
+       regfree(&pat_stop);
+       regfree(&pat_desc);
+       regfree(&pat_exec);
+       regfree(&pat_tpl);
+}
+
+static int init_services(void)
+{
+       struct initd *i;
+       void *instances, *instance, *command;
+
+       list_for_each_entry(i, &initds, list) {
+               char *t;
+
+               blob_buf_init(&b, 0);
+               blobmsg_add_string(&b, "name", i->name);
+               instances = blobmsg_open_table(&b, "instances");
+               instance = blobmsg_open_table(&b, "instance");
+               command = blobmsg_open_array(&b, "command");
+               t = strtok(i->exec, " ");
+               while (t) {
+                       blobmsg_add_string(&b, NULL, t);
+                       t = strtok(NULL, " ");
+               }
+               blobmsg_close_array(&b, command);
+               blobmsg_close_table(&b, instance);
+               blobmsg_close_table(&b, instances);
+               ubus_invoke(ctx, service, "add", b.head, NULL, 0, 1000);
+       }
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       int ret;
+
+       initd_init();
+
+       if (list_empty(&initds))
+               return 0;
+
+       ctx = ubus_connect(NULL);
+       if (!ctx) {
+               fprintf(stderr, "Failed to connect to ubus\n");
+               return -1;
+       }
+
+       ret = ubus_lookup_id(ctx, "service", &service);
+       if (ret) {
+               fprintf(stderr, "Failed to find service object: %s\n", ubus_strerror(ret));
+               return -1;
+       }
+
+       return init_services();
+}
diff --git a/mount_root.c b/mount_root.c
new file mode 100644 (file)
index 0000000..dc05368
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <glob.h>
+#include <dirent.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <asm/byteorder.h>
+
+#include <mtd/mtd-user.h>
+
+#define DEBUG(level, fmt, ...) do { \
+       if (debug >= level) \
+               fprintf(stderr, "%s %s(%d): " fmt, argv0, __func__, __LINE__, ## __VA_ARGS__); \
+       } while (0)
+
+#define LOG(fmt, ...) do { \
+               syslog(LOG_INFO, fmt, ## __VA_ARGS__); \
+               fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \
+       } while (0)
+
+#define ERROR(fmt, ...) do { \
+               syslog(LOG_ERR, fmt, ## __VA_ARGS__); \
+               fprintf(stderr, "%s: "fmt, argv0, ## __VA_ARGS__); \
+       } while (0)
+
+enum {
+       FS_NONE,
+       FS_JFFS2,
+       FS_DEADCODE,
+};
+
+static const char *argv0;
+
+/* this is a raw syscall - man 2 pivot_root */
+extern int pivot_root(const char *new_root, const char *put_old);
+
+static int debug = 0;
+
+static 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 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)
+                               return NULL;
+
+                       *t = '\0';
+                       t++;
+
+                       if (fs && strncmp(t, fs, strlen(fs))) {
+                               ERROR("block is mounted with wrong fs\n");
+                               return NULL;
+                       }
+                       point = p;
+                       break;
+               }
+       }
+
+       fclose(fp);
+
+       return point;
+}
+
+static char* find_mtd_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];
+                       DEBUG(1, "found %s -> index:%s\n", name, index);
+               }
+       }
+
+       fclose(fp);
+
+       return index;
+}
+
+static int find_mtd_block(char *name, char *part, int plen)
+{
+       char *index = find_mtd_index(name);
+
+       if (!index)
+               return -1;
+
+       snprintf(part, plen, "/dev/mtdblock%s", index);
+       DEBUG(1, "found %s -> %s\n", name, part);
+
+       return 0;
+}
+
+static int find_mtd_char(char *name, char *part, int plen)
+{
+       char *index = find_mtd_index(name);
+
+       if (!index)
+               return -1;
+
+       snprintf(part, plen, "/dev/mtd%s", index);
+       DEBUG(1, "found %s -> %s\n", name, part);
+
+       return 0;
+}
+
+static int mtd_unlock(char *mtd)
+{
+       struct erase_info_user mtdlock;
+       struct mtd_info_user mtdinfo;
+       int fd = open(mtd, O_RDWR | O_SYNC);
+       int ret = -1;
+
+       DEBUG(1, "%s\n", mtd);
+
+       if (!fd) {
+               ERROR("failed to open %s: %s\n", mtd, strerror(errno));
+               return -1;
+       }
+
+       ret = ioctl(fd, MEMGETINFO, &mtdinfo);
+       if (ret) {
+               ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno));
+               goto err_out;
+       }
+
+       mtdlock.start = 0;
+       mtdlock.length = mtdinfo.size;
+       ioctl(fd, MEMUNLOCK, &mtdlock);
+
+err_out:
+       close(fd);
+
+       return ret;
+}
+
+static int mtd_erase(const char *mtd)
+{
+       int fd = open(mtd, O_RDWR | O_SYNC);
+       struct mtd_info_user i;
+       struct erase_info_user e;
+       int ret;
+
+       if (!fd) {
+               ERROR("failed to open %s: %s\n", mtd, strerror(errno));
+               return -1;
+       }
+
+       ret = ioctl(fd, MEMGETINFO, &i);
+       if (ret) {
+               ERROR("ioctl(%s, MEMGETINFO) failed: %s\n", mtd, strerror(errno));
+               return -1;
+       }
+
+       e.length = i.erasesize;
+       for (e.start = 0; e.start < i.size; e.start += i.erasesize) {
+               ioctl(fd, MEMUNLOCK, &e);
+               if(ioctl(fd, MEMERASE, &e))
+                       ERROR("Failed to erase block on %s at 0x%x\n", mtd, e.start);
+       }
+
+       close(fd);
+       return 0;
+}
+
+static int do_mount_jffs2(void)
+{
+       char rootfs_data[32];
+
+
+       if (mkdir("/tmp/overlay", 0755)) {
+               ERROR("failed to mkdir /tmp/overlay: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (find_mtd_block("rootfs_data", rootfs_data, sizeof(rootfs_data))) {
+               ERROR("rootfs_data does not exist\n");
+               return -1;
+       }
+
+       if (mount(rootfs_data, "/tmp/overlay", "jffs2", MS_NOATIME, NULL)) {
+               ERROR("failed to mount -t jffs2 %s /tmp/overlay: %s\n", rootfs_data, strerror(errno));
+               return -1;
+       }
+
+       find_mtd_char("rootfs_data", rootfs_data, sizeof(rootfs_data));
+
+       return mtd_unlock(rootfs_data);
+}
+
+static int jffs2_ready(char *mtd)
+{
+       FILE *fp = fopen(mtd, "r");
+       __u32 deadc0de;
+       __u16 jffs2;
+       size_t sz;
+
+       if (!fp) {
+               ERROR("reading %s failed\n", mtd);
+               exit(-1);
+       }
+
+       sz = fread(&deadc0de, sizeof(deadc0de), 1, fp);
+       fclose(fp);
+
+       if (sz != 1) {
+               ERROR("reading %s failed: %s\n", mtd, strerror(errno));
+               exit(-1);
+       }
+
+       deadc0de = __be32_to_cpu(deadc0de);
+       jffs2 = __be16_to_cpu(deadc0de >> 16);
+
+       if (jffs2 == 0x1985) {
+               LOG("jffs2 is ready\n");
+               return FS_JFFS2;
+       }
+
+       if (deadc0de == 0xdeadc0de) {
+               LOG("jffs2 is not ready - marker found\n");
+               return FS_DEADCODE;
+       }
+
+       ERROR("No jffs2 marker was found\n");
+
+       return FS_NONE;
+}
+
+static int check_fs_exists(char *fs)
+{
+       FILE *fp = fopen("/proc/filesystems", "r");
+       static char line[256];
+       int ret = -1;
+
+       DEBUG(2, "%s\n", fs);
+
+       if (!fp) {
+               ERROR("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;
+}
+
+static 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;
+
+       DEBUG(2, "%s %s\n", oldroot, dir);
+
+       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)
+               DEBUG(1, "failed %s %s: %s\n", olddir, newdir, strerror(errno));
+
+       return ret;
+}
+
+static int pivot(char *new, char *old)
+{
+       char pivotdir[64];
+       int ret;
+
+       DEBUG(2, "%s %s\n", new, old);
+
+       if (mount_move("", new, "/proc"))
+               return -1;
+
+       snprintf(pivotdir, sizeof(pivotdir), "%s%s", new, old);
+
+       ret = pivot_root(new, pivotdir);
+
+       if (ret < 0) {
+               ERROR("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;
+}
+
+
+static int fopivot(char *rw_root, char *ro_root)
+{
+       char overlay[64], lowerdir[64];
+
+       DEBUG(2, "%s %s\n", rw_root, ro_root);
+
+       if (check_fs_exists("overlay")) {
+               ERROR("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)) {
+               ERROR("mount failed: %s\n", strerror(errno));
+               return -1;
+       }
+
+       return pivot("/mnt", ro_root);
+}
+
+static int ramoverlay(void)
+{
+       DEBUG(2, "\n");
+
+       mkdir("/tmp/root", 0755);
+       mount("tmpfs", "/tmp/root", "tmpfs", MS_NOATIME, "mode=0755");
+
+       return fopivot("/tmp/root", "/rom");
+}
+
+static int switch2jffs(void)
+{
+       char mtd[32];
+
+       if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
+               ERROR("no rootfs_data was found\n");
+               return -1;
+       }
+
+       if (mount(mtd, "/rom/overlay", "jffs2", MS_NOATIME, NULL)) {
+               ERROR("failed - mount -t jffs2 %s /rom/overlay: %s\n", mtd, strerror(errno));
+               return -1;
+       }
+
+       if (mount("none", "/", NULL, MS_NOATIME | MS_REMOUNT, 0)) {
+               ERROR("failed - mount -o remount,ro none: %s\n", strerror(errno));
+               return -1;
+       }
+
+       system("cp -a /tmp/root/* /rom/overlay");
+
+       if (pivot("/rom", "/mnt")) {
+               ERROR("failed - pivot /rom /mnt: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (mount_move("/mnt", "/tmp/root", "")) {
+               ERROR("failed - mount -o move /mnt /tmp/root %s\n", strerror(errno));
+               return -1;
+       }
+
+       return fopivot("/overlay", "/rom");
+}
+
+static 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)")) {
+                                       DEBUG(1, "unlinking %s\n", orig);
+                                       unlink(orig);
+                               }
+                       }
+               }
+               free(namelist[n]);
+       }
+       free(namelist);
+
+       return 0;
+}
+
+static int main_switch2jffs(int argc, char **argv)
+{
+       char mtd[32];
+       char *mp;
+       int ret = -1;
+
+       if (check_fs_exists("overlay")) {
+               ERROR("overlayfs not found\n");
+               return ret;
+       }
+
+       find_mtd_block("rootfs_data", mtd, sizeof(mtd));
+       mp = find_mount_point(mtd, NULL);
+       if (mp) {
+               LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
+               return -1;
+       }
+
+       if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
+               ERROR("no rootfs_data was found\n");
+               return ret;
+       }
+
+       switch (jffs2_ready(mtd)) {
+       case FS_NONE:
+               ERROR("no jffs2 marker found\n");
+               /* fall through */
+
+       case FS_DEADCODE:
+               ret = switch2jffs();
+               if (!ret) {
+                       DEBUG(1, "doing fo cleanup\n");
+                       umount2("/tmp/root", MNT_DETACH);
+                       foreachdir("/overlay/", handle_whiteout);
+               }
+               break;
+
+       case FS_JFFS2:
+               ret = do_mount_jffs2();
+               if (ret)
+                       break;
+               if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
+                       ERROR("switching to jffs2 failed\n");
+                       ret = -1;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+static int ask_user(int argc, char **argv)
+{
+       if ((argc < 2) || strcmp(argv[1], "-y")) {
+               LOG("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)) {
+                       DEBUG(1, "unlinking %s\n", file);
+                       unlink(file);
+               }
+               free(namelist[n]);
+       }
+       free(namelist);
+
+       DEBUG(1, "rmdir %s\n", dir);
+       rmdir(dir);
+
+       return 0;
+}
+
+static int main_jffs2reset(int argc, char **argv)
+{
+       char mtd[32];
+       char *mp;
+
+       if (ask_user(argc, argv))
+               return -1;
+
+       if (check_fs_exists("overlay")) {
+               ERROR("overlayfs not found\n");
+               return -1;
+       }
+
+       if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
+               ERROR("no rootfs_data was found\n");
+               return -1;
+       }
+
+       mp = find_mount_point(mtd, "jffs2");
+       if (mp) {
+               LOG("%s is mounted as %s, only ereasing files\n", mtd, mp);
+               foreachdir(mp, handle_rmdir);
+               mount(mp, "/", NULL, MS_REMOUNT, 0);
+       } else {
+               LOG("%s is not mounted, erasing it\n", mtd);
+               find_mtd_char("rootfs_data", mtd, sizeof(mtd));
+               mtd_erase(mtd);
+       }
+
+       return 0;
+}
+
+static int main_jffs2mark(int argc, char **argv)
+{
+       FILE *fp;
+       __u32 deadc0de = __cpu_to_be32(0xdeadc0de);
+       char mtd[32];
+       size_t sz;
+
+       if (ask_user(argc, argv))
+               return -1;
+
+       if (find_mtd_block("rootfs_data", mtd, sizeof(mtd))) {
+               ERROR("no rootfs_data was found\n");
+               return -1;
+       }
+
+       fp = fopen(mtd, "w");
+       LOG("%s - marking with deadc0de\n", mtd);
+       if (!fp) {
+               ERROR("opening %s failed\n", mtd);
+               return -1;
+       }
+
+       sz = fwrite(&deadc0de, sizeof(deadc0de), 1, fp);
+       fclose(fp);
+
+       if (sz != 1) {
+               ERROR("writing %s failed: %s\n", mtd, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       char *mp;
+       char mtd[32];
+
+       argv0 = basename(*argv);
+
+       if (!strcmp(basename(*argv), "switch2jffs"))
+               return main_switch2jffs(argc, argv);
+
+       if (!strcmp(basename(*argv), "jffs2mark"))
+               return main_jffs2mark(argc, argv);
+
+       if (!strcmp(basename(*argv), "jffs2reset"))
+               return main_jffs2reset(argc, argv);
+
+       if (find_mtd_char("rootfs_data", mtd, sizeof(mtd))) {
+               if (!find_mtd_char("rootfs", mtd, sizeof(mtd)))
+                       mtd_unlock(mtd);
+               LOG("mounting /dev/root\n");
+               mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
+       } else {
+               switch (jffs2_ready(mtd)) {
+               case FS_NONE:
+               case FS_DEADCODE:
+                       return ramoverlay();
+
+               case FS_JFFS2:
+                       find_mtd_block("rootfs_data", mtd, sizeof(mtd));
+                       mp = find_mount_point(mtd, NULL);
+                       if (mp) {
+                               LOG("rootfs_data:%s is already mounted as %s\n", mtd, mp);
+                               return -1;
+                       }
+
+                       do_mount_jffs2();
+                       DEBUG(1, "switching to jffs2\n");
+                       if (mount_move("/tmp", "", "/overlay") || fopivot("/overlay", "/rom")) {
+                               ERROR("switching to jffs2 failed - fallback to ramoverlay\n");
+                               return ramoverlay();
+                       }
+               }
+       }
+
+       return 0;
+}