diff --git a/patches/openwrt/0104-procd-clean-up-trailing-whitespace-in-nand.sh.patch b/patches/openwrt/0104-procd-clean-up-trailing-whitespace-in-nand.sh.patch
new file mode 100644
index 0000000000000000000000000000000000000000..4b66f8b488353680e034a0fdf29c5438324c0ee6
--- /dev/null
+++ b/patches/openwrt/0104-procd-clean-up-trailing-whitespace-in-nand.sh.patch
@@ -0,0 +1,28 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 3 May 2017 08:27:40 +0200
+Subject: procd: clean up trailing whitespace in nand.sh
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/system/procd/files/nand.sh b/package/system/procd/files/nand.sh
+index 0ed1b632d5a3ecc0299d3f8da853191c453d3bb4..e0d3f2dfdb1e04e17ccf6fea60b0f2af617206f3 100644
+--- a/package/system/procd/files/nand.sh
++++ b/package/system/procd/files/nand.sh
+@@ -194,7 +194,7 @@ nand_upgrade_prepare_ubi() {
+ 
+ nand_do_upgrade_success() {
+ 	local conf_tar="/tmp/sysupgrade.tgz"
+-	
++
+ 	sync
+ 	[ -f "$conf_tar" ] && nand_restore_config "$conf_tar"
+ 	echo "sysupgrade successful"
+@@ -229,7 +229,7 @@ nand_upgrade_ubifs() {
+ 	local rootfs_length=`(cat $1 | wc -c) 2> /dev/null`
+ 
+ 	nand_upgrade_prepare_ubi "$rootfs_length" "ubifs" "0" "0"
+-	
++
+ 	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
+ 	local root_ubivol="$(nand_find_volume $ubidev rootfs)"
+ 	ubiupdatevol /dev/$root_ubivol -s $rootfs_length $1
diff --git a/patches/openwrt/0105-procd-prepare-NAND-sysupgrade-for-making-upgraded-dynamically-linked.patch b/patches/openwrt/0105-procd-prepare-NAND-sysupgrade-for-making-upgraded-dynamically-linked.patch
new file mode 100644
index 0000000000000000000000000000000000000000..eefd848aaee49fa1be46769d03e55617d3aacdcc
--- /dev/null
+++ b/patches/openwrt/0105-procd-prepare-NAND-sysupgrade-for-making-upgraded-dynamically-linked.patch
@@ -0,0 +1,32 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 3 May 2017 08:28:05 +0200
+Subject: procd: prepare NAND sysupgrade for making upgraded dynamically linked
+
+Use install_bin to copy upgraded with all dependencies. The old name
+/tmp/upgraded is temporarily retained as a symlink to avoid breaking
+things.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/system/procd/files/nand.sh b/package/system/procd/files/nand.sh
+index e0d3f2dfdb1e04e17ccf6fea60b0f2af617206f3..ec2a014a00461ca897d521e2d065f5399f1f8c48 100644
+--- a/package/system/procd/files/nand.sh
++++ b/package/system/procd/files/nand.sh
+@@ -318,7 +318,7 @@ nand_upgrade_stage1() {
+ 		[ "$SAVE_CONFIG" != 1 -a -f "$CONF_TAR" ] &&
+ 			rm $CONF_TAR
+ 
+-		ubus call system nandupgrade "{\"path\": \"$path\" }"
++		ubus call system nandupgrade "{\"prefix\": \"$RAM_ROOT\", \"path\": \"$path\" }"
+ 		exit 0
+ 	}
+ }
+@@ -359,6 +359,7 @@ nand_do_platform_check() {
+ # $(1): file to be used for upgrade
+ nand_do_upgrade() {
+ 	echo -n $1 > /tmp/sysupgrade-nand-path
+-	cp /sbin/upgraded /tmp/
++	install_bin /sbin/upgraded
++	ln -s "$RAM_ROOT"/sbin/upgraded /tmp/upgraded
+ 	nand_upgrade_stage1
+ }
diff --git a/patches/openwrt/0106-procd-add-universal-staged-sysupgrade-patches.patch b/patches/openwrt/0106-procd-add-universal-staged-sysupgrade-patches.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6e588eff545cf2d74b744a1017d6e31d1211a6e9
--- /dev/null
+++ b/patches/openwrt/0106-procd-add-universal-staged-sysupgrade-patches.patch
@@ -0,0 +1,1116 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Thu, 1 Jun 2017 22:42:04 +0200
+Subject: procd: add universal staged sysupgrade patches
+
+diff --git a/package/system/procd/patches/0001-Add-support-for-alternative-rc.d-directories.patch b/package/system/procd/patches/0001-Add-support-for-alternative-rc.d-directories.patch
+deleted file mode 100644
+index bc2434200364b46f1db4c2eec22c4e8b973844d5..0000000000000000000000000000000000000000
+--- a/package/system/procd/patches/0001-Add-support-for-alternative-rc.d-directories.patch
++++ /dev/null
+@@ -1,97 +0,0 @@
+-From 03a2bc70e4260ec9f669391c47b9a7a9ecd0b75d Mon Sep 17 00:00:00 2001
+-Message-Id: <03a2bc70e4260ec9f669391c47b9a7a9ecd0b75d.1407329621.git.mschiffer@universe-factory.net>
+-From: Matthias Schiffer <mschiffer@universe-factory.net>
+-Date: Wed, 6 Aug 2014 14:51:49 +0200
+-Subject: [PATCH] Add support for alternative rc.d directories
+-
+----
+- initd/preinit.c | 38 ++++++++++++++++++++++++++++++++++++++
+- rcS.c           |  2 +-
+- 2 files changed, 39 insertions(+), 1 deletion(-)
+-
+-diff --git a/initd/preinit.c b/initd/preinit.c
+-index fb94527..8b832a7 100644
+---- a/initd/preinit.c
+-+++ b/initd/preinit.c
+-@@ -12,6 +12,8 @@
+-  * GNU General Public License for more details.
+-  */
+- 
+-+#define _GNU_SOURCE
+-+
+- #include <sys/stat.h>
+- #include <sys/types.h>
+- #include <sys/mount.h>
+-@@ -46,6 +48,35 @@ check_dbglvl(void)
+- 		debug = lvl;
+- }
+- 
+-+static char*
+-+get_rc_d(void)
+-+{
+-+	size_t n = 0;
+-+	ssize_t len;
+-+	char *ret = NULL;
+-+
+-+	FILE *fp = fopen("/tmp/rc_d_path", "r");
+-+
+-+	if (!fp)
+-+		return NULL;
+-+
+-+	len = getline(&ret, &n, fp);
+-+
+-+	fclose(fp);
+-+
+-+	unlink("/tmp/rc_d_path");
+-+
+-+	if (len <= 0) {
+-+		free(ret);
+-+		return NULL;
+-+	}
+-+
+-+	if (ret[len-1] == '\n')
+-+		ret[len-1] = 0;
+-+
+-+	return ret;
+-+}
+-+
+- static void
+- spawn_procd(struct uloop_process *proc, int ret)
+- {
+-@@ -53,6 +84,7 @@ spawn_procd(struct uloop_process *proc, int ret)
+- 	char *argv[] = { "/sbin/procd", NULL};
+- 	struct stat s;
+- 	char dbg[2];
+-+	char *rc_d_path;
+- 
+- 	if (plugd_proc.pid > 0)
+- 		kill(plugd_proc.pid, SIGKILL);
+-@@ -72,6 +104,12 @@ spawn_procd(struct uloop_process *proc, int ret)
+- 		setenv("DBGLVL", dbg, 1);
+- 	}
+- 
+-+	rc_d_path = get_rc_d();
+-+	if (rc_d_path) {
+-+		setenv("RC_D_PATH", rc_d_path, 1);
+-+		free(rc_d_path);
+-+	}
+-+
+- 	execvp(argv[0], argv);
+- }
+- 
+-diff --git a/rcS.c b/rcS.c
+-index 0e1b0ba..1b00831 100644
+---- a/rcS.c
+-+++ b/rcS.c
+-@@ -150,7 +150,7 @@ int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *))
+- 	q.empty_cb = q_empty;
+- 	q.max_running_tasks = 1;
+- 
+--	return _rc(&q, "/etc/rc.d", pattern, "*", param);
+-+	return _rc(&q, getenv("RC_D_PATH") ?: "/etc/rc.d", pattern, "*", param);
+- }
+- 
+- int rc(const char *file, char *param)
+--- 
+-2.0.4
+-
+diff --git a/package/system/procd/patches/1001-Add-support-for-alternative-rc.d-directories.patch b/package/system/procd/patches/1001-Add-support-for-alternative-rc.d-directories.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..2d3c24181cef32ae9e15401ff2e4772805ea9551
+--- /dev/null
++++ b/package/system/procd/patches/1001-Add-support-for-alternative-rc.d-directories.patch
+@@ -0,0 +1,97 @@
++From 36673c2a0e409d9c8ea9e1c15363e73bb21ae65b Mon Sep 17 00:00:00 2001
++Message-Id: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Wed, 6 Aug 2014 14:51:49 +0200
++Subject: [PATCH 1001/1007] Add support for alternative rc.d directories
++
++---
++ initd/preinit.c | 38 ++++++++++++++++++++++++++++++++++++++
++ rcS.c           |  2 +-
++ 2 files changed, 39 insertions(+), 1 deletion(-)
++
++diff --git a/initd/preinit.c b/initd/preinit.c
++index f38d8ef..acc64e7 100644
++--- a/initd/preinit.c
+++++ b/initd/preinit.c
++@@ -12,6 +12,8 @@
++  * GNU General Public License for more details.
++  */
++ 
+++#define _GNU_SOURCE
+++
++ #include <sys/stat.h>
++ #include <sys/types.h>
++ #include <sys/mount.h>
++@@ -47,6 +49,35 @@ check_dbglvl(void)
++ 		debug = lvl;
++ }
++ 
+++static char*
+++get_rc_d(void)
+++{
+++	size_t n = 0;
+++	ssize_t len;
+++	char *ret = NULL;
+++
+++	FILE *fp = fopen("/tmp/rc_d_path", "r");
+++
+++	if (!fp)
+++		return NULL;
+++
+++	len = getline(&ret, &n, fp);
+++
+++	fclose(fp);
+++
+++	unlink("/tmp/rc_d_path");
+++
+++	if (len <= 0) {
+++		free(ret);
+++		return NULL;
+++	}
+++
+++	if (ret[len-1] == '\n')
+++		ret[len-1] = 0;
+++
+++	return ret;
+++}
+++
++ static void
++ spawn_procd(struct uloop_process *proc, int ret)
++ {
++@@ -54,6 +85,7 @@ spawn_procd(struct uloop_process *proc, int ret)
++ 	char *argv[] = { "/sbin/procd", NULL};
++ 	struct stat s;
++ 	char dbg[2];
+++	char *rc_d_path;
++ 
++ 	if (plugd_proc.pid > 0)
++ 		kill(plugd_proc.pid, SIGKILL);
++@@ -73,6 +105,12 @@ spawn_procd(struct uloop_process *proc, int ret)
++ 		setenv("DBGLVL", dbg, 1);
++ 	}
++ 
+++	rc_d_path = get_rc_d();
+++	if (rc_d_path) {
+++		setenv("RC_D_PATH", rc_d_path, 1);
+++		free(rc_d_path);
+++	}
+++
++ 	execvp(argv[0], argv);
++ }
++ 
++diff --git a/rcS.c b/rcS.c
++index b3e3c22..e231d71 100644
++--- a/rcS.c
+++++ b/rcS.c
++@@ -162,7 +162,7 @@ int rcS(char *pattern, char *param, void (*q_empty)(struct runqueue *))
++ 	q.empty_cb = q_empty;
++ 	q.max_running_tasks = 1;
++ 
++-	return _rc(&q, "/etc/rc.d", pattern, "*", param);
+++	return _rc(&q, getenv("RC_D_PATH") ?: "/etc/rc.d", pattern, "*", param);
++ }
++ 
++ int rc(const char *file, char *param)
++-- 
++2.13.0
++
+diff --git a/package/system/procd/patches/1002-system-always-support-staged-sysupgrade.patch b/package/system/procd/patches/1002-system-always-support-staged-sysupgrade.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..f19a21aed1a5931d609684ff70784fe40b0d1406
+--- /dev/null
++++ b/package/system/procd/patches/1002-system-always-support-staged-sysupgrade.patch
+@@ -0,0 +1,114 @@
++From 7d2c86de79b6bd976f0e37f7c3cbc61a3b3c3bb7 Mon Sep 17 00:00:00 2001
++Message-Id: <7d2c86de79b6bd976f0e37f7c3cbc61a3b3c3bb7.1496349467.git.mschiffer@universe-factory.net>
++In-Reply-To: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++References: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Fri, 21 Apr 2017 20:06:59 +0200
++Subject: [PATCH 1002/1007] system: always support staged sysupgrade
++
++In preparation for switching all targets to the staged sysupgrade
++mechanism, upgraded is always built, and the "nandupgrade" ubus method is
++renamed to "sysupgrade".
++
++To make the migration easier, support for the old name "nandupgrade" and
++the "upgrade" method that will become unused with the staged sysupgrade is
++retained for now.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ CMakeLists.txt |  4 +---
++ system.c       | 31 +++++++++++++------------------
++ 2 files changed, 14 insertions(+), 21 deletions(-)
++
++diff --git a/CMakeLists.txt b/CMakeLists.txt
++index cc1e4a5..9e378ae 100644
++--- a/CMakeLists.txt
+++++ b/CMakeLists.txt
++@@ -32,9 +32,7 @@ IF(ZRAM_TMPFS)
++   SET(SOURCES_ZRAM initd/zram.c)
++ ENDIF()
++ 
++-IF(BUILD_UPGRADED)
++-  add_subdirectory(upgraded)
++-ENDIF()
+++add_subdirectory(upgraded)
++ 
++ ADD_EXECUTABLE(procd ${SOURCES})
++ TARGET_LINK_LIBRARIES(procd ${LIBS})
++diff --git a/system.c b/system.c
++index fb7fbe4..701ff35 100644
++--- a/system.c
+++++ b/system.c
++@@ -328,12 +328,12 @@ static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
++ }
++ 
++ enum {
++-	NAND_PATH,
++-	__NAND_MAX
+++	SYSUPGRADE_PATH,
+++	__SYSUPGRADE_MAX
++ };
++ 
++-static const struct blobmsg_policy nand_policy[__NAND_MAX] = {
++-	[NAND_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
+++static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
+++	[SYSUPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
++ };
++ 
++ static void
++@@ -352,20 +352,20 @@ procd_spawn_upgraded(char *path)
++ 	execvp(argv[0], argv);
++ }
++ 
++-static int nand_set(struct ubus_context *ctx, struct ubus_object *obj,
++-			struct ubus_request_data *req, const char *method,
++-			struct blob_attr *msg)
+++static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
+++		      struct ubus_request_data *req, const char *method,
+++		      struct blob_attr *msg)
++ {
++-	struct blob_attr *tb[__NAND_MAX];
+++	struct blob_attr *tb[__SYSUPGRADE_MAX];
++ 
++ 	if (!msg)
++ 		return UBUS_STATUS_INVALID_ARGUMENT;
++ 
++-	blobmsg_parse(nand_policy, __NAND_MAX, tb, blob_data(msg), blob_len(msg));
++-	if (!tb[NAND_PATH])
+++	blobmsg_parse(sysupgrade_policy, __SYSUPGRADE_MAX, tb, blob_data(msg), blob_len(msg));
+++	if (!tb[SYSUPGRADE_PATH])
++ 		return UBUS_STATUS_INVALID_ARGUMENT;
++ 
++-	procd_spawn_upgraded(blobmsg_get_string(tb[NAND_PATH]));
+++	procd_spawn_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PATH]));
++ 	fprintf(stderr, "Yikees, something went wrong. no /sbin/upgraded ?\n");
++ 	return 0;
++ }
++@@ -383,9 +383,8 @@ static const struct ubus_method system_methods[] = {
++ 	UBUS_METHOD_NOARG("upgrade", system_upgrade),
++ 	UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
++ 	UBUS_METHOD("signal", proc_signal, signal_policy),
++-
++-	/* must remain at the end as it ia not always loaded */
++-	UBUS_METHOD("nandupgrade", nand_set, nand_policy),
+++	UBUS_METHOD("nandupgrade", sysupgrade, sysupgrade_policy),
+++	UBUS_METHOD("sysupgrade", sysupgrade, sysupgrade_policy),
++ };
++ 
++ static struct ubus_object_type system_object_type =
++@@ -414,12 +413,8 @@ procd_bcast_event(char *event, struct blob_attr *msg)
++ 
++ void ubus_init_system(struct ubus_context *ctx)
++ {
++-	struct stat s;
++ 	int ret;
++ 
++-	if (stat("/sbin/upgraded", &s))
++-		system_object.n_methods -= 1;
++-
++ 	_ctx = ctx;
++ 	ret = ubus_add_object(ctx, &system_object);
++ 	if (ret)
++-- 
++2.13.0
++
+diff --git a/package/system/procd/patches/1003-upgraded-link-dynamically-chroot-during-exec.patch b/package/system/procd/patches/1003-upgraded-link-dynamically-chroot-during-exec.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..d57cb02fba42894e5d279d4aa4ab00e0619f66f4
+--- /dev/null
++++ b/package/system/procd/patches/1003-upgraded-link-dynamically-chroot-during-exec.patch
+@@ -0,0 +1,222 @@
++From a6f07873f2189dd7b6742c04064c7bbee2c9d28b Mon Sep 17 00:00:00 2001
++Message-Id: <a6f07873f2189dd7b6742c04064c7bbee2c9d28b.1496349467.git.mschiffer@universe-factory.net>
++In-Reply-To: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++References: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sun, 23 Apr 2017 02:28:13 +0200
++Subject: [PATCH 1003/1007] upgraded: link dynamically, chroot during exec
++
++The chroot ensures we don't reference anything on the rootfs and is
++reverted after the upgraded exec. While we're at it, also improve error
++handling a bit.
++
++This change also required changes to sysupgrade, as the dynamically linked
++version is expected at a different location, and libraries need to be made
++available.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ system.c                | 25 +++++++++++++++++++------
++ upgraded/CMakeLists.txt | 10 +---------
++ upgraded/upgraded.c     | 26 +++++++++++++++++++++-----
++ watchdog.c              |  9 +++++++--
++ watchdog.h              |  2 +-
++ 5 files changed, 49 insertions(+), 23 deletions(-)
++
++diff --git a/system.c b/system.c
++index 701ff35..ad71956 100644
++--- a/system.c
+++++ b/system.c
++@@ -329,27 +329,40 @@ static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
++ 
++ enum {
++ 	SYSUPGRADE_PATH,
+++	SYSUPGRADE_PREFIX,
++ 	__SYSUPGRADE_MAX
++ };
++ 
++ static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
++ 	[SYSUPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
+++	[SYSUPGRADE_PREFIX] = { .name = "prefix", .type = BLOBMSG_TYPE_STRING },
++ };
++ 
++ static void
++-procd_spawn_upgraded(char *path)
+++procd_exec_upgraded(const char *prefix, char *path)
++ {
++ 	char *wdt_fd = watchdog_fd();
++-	char *argv[] = { "/tmp/upgraded", NULL, NULL};
+++	char *argv[] = { "/sbin/upgraded", NULL, NULL};
+++
+++	if (chroot(prefix)) {
+++		fprintf(stderr, "Failed to chroot for upgraded exec.\n");
+++		return;
+++	}
++ 
++ 	argv[1] = path;
++ 
++ 	DEBUG(2, "Exec to upgraded now\n");
++ 	if (wdt_fd) {
++-		watchdog_no_cloexec();
+++		watchdog_set_cloexec(false);
++ 		setenv("WDTFD", wdt_fd, 1);
++ 	}
++ 	execvp(argv[0], argv);
+++
+++	/* Cleanup on failure */
+++	fprintf(stderr, "Failed to exec upgraded.\n");
+++	unsetenv("WDTFD");
+++	watchdog_set_cloexec(true);
+++	chroot(".");
++ }
++ 
++ static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
++@@ -362,11 +375,11 @@ static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
++ 		return UBUS_STATUS_INVALID_ARGUMENT;
++ 
++ 	blobmsg_parse(sysupgrade_policy, __SYSUPGRADE_MAX, tb, blob_data(msg), blob_len(msg));
++-	if (!tb[SYSUPGRADE_PATH])
+++	if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
++ 		return UBUS_STATUS_INVALID_ARGUMENT;
++ 
++-	procd_spawn_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PATH]));
++-	fprintf(stderr, "Yikees, something went wrong. no /sbin/upgraded ?\n");
+++	procd_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
+++			    blobmsg_get_string(tb[SYSUPGRADE_PATH]));
++ 	return 0;
++ }
++ 
++diff --git a/upgraded/CMakeLists.txt b/upgraded/CMakeLists.txt
++index 093dba2..00d8ce5 100644
++--- a/upgraded/CMakeLists.txt
+++++ b/upgraded/CMakeLists.txt
++@@ -2,16 +2,8 @@ cmake_minimum_required(VERSION 2.6)
++ 
++ PROJECT(upgraded C)
++ ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations)
++-set(CMAKE_EXE_LINKER_FLAGS "-static -fPIC")
++-set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
++-set(CMAKE_EXE_LINK_DYNAMIC_C_FLAGS)
++-set(CMAKE_EXE_LINK_DYNAMIC_CXX_FLAGS)
++-set(CMAKE_SHARED_LIBRARY_C_FLAGS)
++-set(CMAKE_SHARED_LIBRARY_CXX_FLAGS)
++-set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
++-set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)
++ ADD_EXECUTABLE(upgraded upgraded.c ../watchdog.c)
++-TARGET_LINK_LIBRARIES(upgraded ubox rt -lc -lgcc_pic)
+++TARGET_LINK_LIBRARIES(upgraded ubox)
++ INSTALL(TARGETS upgraded
++ 	RUNTIME DESTINATION sbin
++ )
++diff --git a/upgraded/upgraded.c b/upgraded/upgraded.c
++index d7433e7..aa0b4ff 100644
++--- a/upgraded/upgraded.c
+++++ b/upgraded/upgraded.c
++@@ -14,6 +14,7 @@
++ 
++ #include <sys/reboot.h>
++ 
+++#include <fcntl.h>
++ #include <stdio.h>
++ #include <stdlib.h>
++ #include <string.h>
++@@ -24,6 +25,10 @@
++ 
++ #include "../watchdog.h"
++ 
+++#ifndef O_PATH
+++#define O_PATH		010000000
+++#endif
+++
++ static struct uloop_process upgrade_proc;
++ unsigned int debug = 2;
++ 
++@@ -34,7 +39,7 @@ static void upgrade_proc_cb(struct uloop_process *proc, int ret)
++ 	uloop_end();
++ }
++ 
++-static void sysupgarde(char *folder)
+++static void sysupgrade(char *folder)
++ {
++ 	char *args[] = { "/sbin/sysupgrade", "nand", NULL, NULL };
++ 
++@@ -47,7 +52,7 @@ static void sysupgarde(char *folder)
++ 		exit(-1);
++ 	}
++ 	if (upgrade_proc.pid <= 0) {
++-		fprintf(stderr, "Failed to start sysupgarde\n");
+++		fprintf(stderr, "Failed to start sysupgrade\n");
++ 		uloop_end();
++ 	}
++ }
++@@ -60,10 +65,21 @@ int main(int argc, char **argv)
++ 		fprintf(stderr, "this tool needs to run as pid 1\n");
++ 		return -1;
++ 	}
++-	if (chdir("/tmp") == -1) {
++-		fprintf(stderr, "failed to chdir to /tmp: %s\n", strerror(errno));
+++
+++	int fd = open("/", O_DIRECTORY|O_PATH);
+++	if (fd < 0) {
+++		fprintf(stderr, "unable to open prefix directory: %s\n", strerror(errno));
++ 		return -1;
++ 	}
+++
+++	chroot(".");
+++
+++	if (fchdir(fd) == -1) {
+++		fprintf(stderr, "failed to chdir to prefix directory: %s\n", strerror(errno));
+++		return -1;
+++	}
+++	close(fd);
+++
++ 	if (argc != 2) {
++ 		fprintf(stderr, "sysupgrade stage 2 failed, no folder specified\n");
++ 		return -1;
++@@ -71,7 +87,7 @@ int main(int argc, char **argv)
++ 
++ 	uloop_init();
++ 	watchdog_init(0);
++-	sysupgarde(argv[1]);
+++	sysupgrade(argv[1]);
++ 	uloop_run();
++ 
++ 	reboot(RB_AUTOBOOT);
++diff --git a/watchdog.c b/watchdog.c
++index 592ae7e..780b321 100644
++--- a/watchdog.c
+++++ b/watchdog.c
++@@ -126,10 +126,15 @@ void watchdog_init(int preinit)
++ }
++ 
++ 
++-void watchdog_no_cloexec(void)
+++void watchdog_set_cloexec(bool val)
++ {
++ 	if (wdt_fd < 0)
++ 		return;
++ 
++-	fcntl(wdt_fd, F_SETFD, fcntl(wdt_fd, F_GETFD) & ~FD_CLOEXEC);
+++	int flags = fcntl(wdt_fd, F_GETFD);
+++	if (val)
+++		flags |= FD_CLOEXEC;
+++	else
+++		flags &= ~FD_CLOEXEC;
+++	fcntl(wdt_fd, F_SETFD,  flags);
++ }
++diff --git a/watchdog.h b/watchdog.h
++index 015fa93..e857010 100644
++--- a/watchdog.h
+++++ b/watchdog.h
++@@ -21,7 +21,7 @@ int watchdog_timeout(int timeout);
++ int watchdog_frequency(int frequency);
++ void watchdog_set_stopped(bool val);
++ bool watchdog_get_stopped(void);
++-void watchdog_no_cloexec(void);
+++void watchdog_set_cloexec(bool val);
++ void watchdog_ping(void);
++ 
++ #endif
++-- 
++2.13.0
++
+diff --git a/package/system/procd/patches/1004-upgraded-add-support-for-passing-a-command-argument-.patch b/package/system/procd/patches/1004-upgraded-add-support-for-passing-a-command-argument-.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..ac8c3fe6ee15e773c973c9d340fbe52ce428d6ee
+--- /dev/null
++++ b/package/system/procd/patches/1004-upgraded-add-support-for-passing-a-command-argument-.patch
+@@ -0,0 +1,104 @@
++From af1e6d9839a9a8f2c579202597630df9b8f842e6 Mon Sep 17 00:00:00 2001
++Message-Id: <af1e6d9839a9a8f2c579202597630df9b8f842e6.1496349467.git.mschiffer@universe-factory.net>
++In-Reply-To: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++References: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sun, 23 Apr 2017 19:04:25 +0200
++Subject: [PATCH 1004/1007] upgraded: add support for passing a "command"
++ argument to stage2
++
++This allows us to make use of upgraded in "snapshot convert" as well.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ system.c            | 10 +++++++---
++ upgraded/upgraded.c | 13 +++++++------
++ 2 files changed, 14 insertions(+), 9 deletions(-)
++
++diff --git a/system.c b/system.c
++index ad71956..1e8a06d 100644
++--- a/system.c
+++++ b/system.c
++@@ -330,19 +330,21 @@ static int proc_signal(struct ubus_context *ctx, struct ubus_object *obj,
++ enum {
++ 	SYSUPGRADE_PATH,
++ 	SYSUPGRADE_PREFIX,
+++	SYSUPGRADE_COMMAND,
++ 	__SYSUPGRADE_MAX
++ };
++ 
++ static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
++ 	[SYSUPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
++ 	[SYSUPGRADE_PREFIX] = { .name = "prefix", .type = BLOBMSG_TYPE_STRING },
+++	[SYSUPGRADE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
++ };
++ 
++ static void
++-procd_exec_upgraded(const char *prefix, char *path)
+++procd_exec_upgraded(const char *prefix, char *path, char *command)
++ {
++ 	char *wdt_fd = watchdog_fd();
++-	char *argv[] = { "/sbin/upgraded", NULL, NULL};
+++	char *argv[] = { "/sbin/upgraded", NULL, NULL, NULL};
++ 
++ 	if (chroot(prefix)) {
++ 		fprintf(stderr, "Failed to chroot for upgraded exec.\n");
++@@ -350,6 +352,7 @@ procd_exec_upgraded(const char *prefix, char *path)
++ 	}
++ 
++ 	argv[1] = path;
+++	argv[2] = command;
++ 
++ 	DEBUG(2, "Exec to upgraded now\n");
++ 	if (wdt_fd) {
++@@ -379,7 +382,8 @@ static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
++ 		return UBUS_STATUS_INVALID_ARGUMENT;
++ 
++ 	procd_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
++-			    blobmsg_get_string(tb[SYSUPGRADE_PATH]));
+++			    blobmsg_get_string(tb[SYSUPGRADE_PATH]),
+++			    tb[SYSUPGRADE_COMMAND] ? blobmsg_get_string(tb[SYSUPGRADE_COMMAND]) : NULL);
++ 	return 0;
++ }
++ 
++diff --git a/upgraded/upgraded.c b/upgraded/upgraded.c
++index aa0b4ff..303edb7 100644
++--- a/upgraded/upgraded.c
+++++ b/upgraded/upgraded.c
++@@ -39,11 +39,12 @@ static void upgrade_proc_cb(struct uloop_process *proc, int ret)
++ 	uloop_end();
++ }
++ 
++-static void sysupgrade(char *folder)
+++static void sysupgrade(char *path, char *command)
++ {
++-	char *args[] = { "/sbin/sysupgrade", "nand", NULL, NULL };
+++	char *args[] = { "/sbin/sysupgrade", "nand", NULL, NULL, NULL };
++ 
++-	args[2] = folder;
+++	args[2] = path;
+++	args[3] = command;
++ 	upgrade_proc.cb = upgrade_proc_cb;
++ 	upgrade_proc.pid = fork();
++ 	if (!upgrade_proc.pid) {
++@@ -80,14 +81,14 @@ int main(int argc, char **argv)
++ 	}
++ 	close(fd);
++ 
++-	if (argc != 2) {
++-		fprintf(stderr, "sysupgrade stage 2 failed, no folder specified\n");
+++	if (argc != 2 && argc != 3) {
+++		fprintf(stderr, "sysupgrade stage 2 failed, invalid command line\n");
++ 		return -1;
++ 	}
++ 
++ 	uloop_init();
++ 	watchdog_init(0);
++-	sysupgrade(argv[1]);
+++	sysupgrade(argv[1], (argc == 3) ? argv[2] : NULL);
++ 	uloop_run();
++ 
++ 	reboot(RB_AUTOBOOT);
++-- 
++2.13.0
++
+diff --git a/package/system/procd/patches/1005-Remove-code-that-has-become-unnecessary-after-sysupg.patch b/package/system/procd/patches/1005-Remove-code-that-has-become-unnecessary-after-sysupg.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..0be544bce113b920556bc6a9889041227fc42e07
+--- /dev/null
++++ b/package/system/procd/patches/1005-Remove-code-that-has-become-unnecessary-after-sysupg.patch
+@@ -0,0 +1,119 @@
++From 45bd440ec30f777b3619dcd0e7db330cc29a7850 Mon Sep 17 00:00:00 2001
++Message-Id: <45bd440ec30f777b3619dcd0e7db330cc29a7850.1496349467.git.mschiffer@universe-factory.net>
++In-Reply-To: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++References: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Sun, 23 Apr 2017 19:06:12 +0200
++Subject: [PATCH 1005/1007] Remove code that has become unnecessary after
++ sysupgrade changes
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ procd.h             |  1 -
++ service/instance.c  |  2 --
++ system.c            | 12 ------------
++ upgraded/upgraded.c | 10 +++++-----
++ 4 files changed, 5 insertions(+), 20 deletions(-)
++
++diff --git a/procd.h b/procd.h
++index 66d183c..796e524 100644
++--- a/procd.h
+++++ b/procd.h
++@@ -27,7 +27,6 @@
++ #define __init __attribute__((constructor))
++ 
++ extern char *ubus_socket;
++-extern int upgrade_running;
++ 
++ void procd_connect_ubus(void);
++ void procd_reconnect_ubus(int reconnect);
++diff --git a/service/instance.c b/service/instance.c
++index 40ff021..f33eae9 100644
++--- a/service/instance.c
+++++ b/service/instance.c
++@@ -453,8 +453,6 @@ instance_exit(struct uloop_process *p, int ret)
++ 	runtime = tp.tv_sec - in->start.tv_sec;
++ 
++ 	DEBUG(2, "Instance %s::%s exit with error code %d after %ld seconds\n", in->srv->name, in->name, ret, runtime);
++-	if (upgrade_running)
++-		return;
++ 
++ 	uloop_timeout_cancel(&in->timeout);
++ 	if (in->halt) {
++diff --git a/system.c b/system.c
++index 1e8a06d..80205da 100644
++--- a/system.c
+++++ b/system.c
++@@ -31,8 +31,6 @@ static struct blob_buf b;
++ static int notify;
++ static struct ubus_context *_ctx;
++ 
++-int upgrade_running = 0;
++-
++ static int system_board(struct ubus_context *ctx, struct ubus_object *obj,
++                  struct ubus_request_data *req, const char *method,
++                  struct blob_attr *msg)
++@@ -227,14 +225,6 @@ static int system_info(struct ubus_context *ctx, struct ubus_object *obj,
++ 	return UBUS_STATUS_OK;
++ }
++ 
++-static int system_upgrade(struct ubus_context *ctx, struct ubus_object *obj,
++-			struct ubus_request_data *req, const char *method,
++-			struct blob_attr *msg)
++-{
++-	upgrade_running = 1;
++-	return 0;
++-}
++-
++ enum {
++ 	WDT_FREQUENCY,
++ 	WDT_TIMEOUT,
++@@ -397,10 +387,8 @@ procd_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
++ static const struct ubus_method system_methods[] = {
++ 	UBUS_METHOD_NOARG("board", system_board),
++ 	UBUS_METHOD_NOARG("info",  system_info),
++-	UBUS_METHOD_NOARG("upgrade", system_upgrade),
++ 	UBUS_METHOD("watchdog", watchdog_set, watchdog_policy),
++ 	UBUS_METHOD("signal", proc_signal, signal_policy),
++-	UBUS_METHOD("nandupgrade", sysupgrade, sysupgrade_policy),
++ 	UBUS_METHOD("sysupgrade", sysupgrade, sysupgrade_policy),
++ };
++ 
++diff --git a/upgraded/upgraded.c b/upgraded/upgraded.c
++index 303edb7..79ebd37 100644
++--- a/upgraded/upgraded.c
+++++ b/upgraded/upgraded.c
++@@ -41,10 +41,10 @@ static void upgrade_proc_cb(struct uloop_process *proc, int ret)
++ 
++ static void sysupgrade(char *path, char *command)
++ {
++-	char *args[] = { "/sbin/sysupgrade", "nand", NULL, NULL, NULL };
+++	char *args[] = { "/lib/upgrade/stage2", NULL, NULL, NULL };
++ 
++-	args[2] = path;
++-	args[3] = command;
+++	args[1] = path;
+++	args[2] = command;
++ 	upgrade_proc.cb = upgrade_proc_cb;
++ 	upgrade_proc.pid = fork();
++ 	if (!upgrade_proc.pid) {
++@@ -81,14 +81,14 @@ int main(int argc, char **argv)
++ 	}
++ 	close(fd);
++ 
++-	if (argc != 2 && argc != 3) {
+++	if (argc != 3) {
++ 		fprintf(stderr, "sysupgrade stage 2 failed, invalid command line\n");
++ 		return -1;
++ 	}
++ 
++ 	uloop_init();
++ 	watchdog_init(0);
++-	sysupgrade(argv[1], (argc == 3) ? argv[2] : NULL);
+++	sysupgrade(argv[1], argv[2]);
++ 	uloop_run();
++ 
++ 	reboot(RB_AUTOBOOT);
++-- 
++2.13.0
++
+diff --git a/package/system/procd/patches/1006-init-add-support-for-sysupgrades-triggered-from-prei.patch b/package/system/procd/patches/1006-init-add-support-for-sysupgrades-triggered-from-prei.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..872846be3e3984f6177512a7adbf0886b78d2677
+--- /dev/null
++++ b/package/system/procd/patches/1006-init-add-support-for-sysupgrades-triggered-from-prei.patch
+@@ -0,0 +1,280 @@
++From f4e6df8848e54d83bac0ab7451312a9db5b59143 Mon Sep 17 00:00:00 2001
++Message-Id: <f4e6df8848e54d83bac0ab7451312a9db5b59143.1496349467.git.mschiffer@universe-factory.net>
++In-Reply-To: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++References: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Mon, 24 Apr 2017 00:40:27 +0200
++Subject: [PATCH 1006/1007] init: add support for sysupgrades triggered from
++ preinit
++
++This will allow to add support for sysupgrades via upgraded from failsafe
++mode.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ CMakeLists.txt  |  4 ++--
++ initd/preinit.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
++ system.c        | 35 ++++-------------------------------
++ sysupgrade.c    | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
++ sysupgrade.h    | 21 +++++++++++++++++++++
++ watchdog.h      |  2 ++
++ 6 files changed, 119 insertions(+), 38 deletions(-)
++ create mode 100644 sysupgrade.c
++ create mode 100644 sysupgrade.h
++
++diff --git a/CMakeLists.txt b/CMakeLists.txt
++index 9e378ae..441216b 100644
++--- a/CMakeLists.txt
+++++ b/CMakeLists.txt
++@@ -17,7 +17,7 @@ INSTALL(TARGETS setlbf
++ )
++ 
++ 
++-SET(SOURCES procd.c signal.c watchdog.c state.c	inittab.c rcS.c	ubus.c system.c
+++SET(SOURCES procd.c signal.c watchdog.c state.c	inittab.c rcS.c	ubus.c system.c sysupgrade.c
++ 	service/service.c service/instance.c service/validate.c service/trigger.c service/watch.c
++ 	plug/coldplug.c plug/hotplug.c utils/utils.c)
++ 
++@@ -41,7 +41,7 @@ INSTALL(TARGETS procd
++ )
++ 
++ 
++-ADD_EXECUTABLE(init initd/init.c initd/early.c initd/preinit.c initd/mkdev.c watchdog.c
+++ADD_EXECUTABLE(init initd/init.c initd/early.c initd/preinit.c initd/mkdev.c sysupgrade.c watchdog.c
++ 	utils/utils.c ${SOURCES_ZRAM})
++ TARGET_LINK_LIBRARIES(init ${LIBS})
++ INSTALL(TARGETS init
++diff --git a/initd/preinit.c b/initd/preinit.c
++index acc64e7..3a606e4 100644
++--- a/initd/preinit.c
+++++ b/initd/preinit.c
++@@ -28,6 +28,7 @@
++ 
++ #include "init.h"
++ #include "../watchdog.h"
+++#include "../sysupgrade.h"
++ 
++ static struct uloop_process preinit_proc;
++ static struct uloop_process plugd_proc;
++@@ -79,23 +80,58 @@ get_rc_d(void)
++ }
++ 
++ static void
+++check_sysupgrade(void)
+++{
+++	char *prefix = NULL, *path = NULL, *command = NULL;
+++	size_t n;
+++
+++	if (chdir("/"))
+++		return;
+++
+++	FILE *sysupgrade = fopen("/tmp/sysupgrade", "r");
+++	if (!sysupgrade)
+++		return;
+++
+++	n = 0;
+++	if (getdelim(&prefix, &n, 0, sysupgrade) < 0)
+++		goto fail;
+++	n = 0;
+++	if (getdelim(&path, &n, 0, sysupgrade) < 0)
+++		goto fail;
+++	n = 0;
+++	if (getdelim(&command, &n, 0, sysupgrade) < 0)
+++		goto fail;
+++
+++	fclose(sysupgrade);
+++
+++	sysupgrade_exec_upgraded(prefix, path, command);
+++
+++	while (true)
+++		sleep(1);
+++
+++fail:
+++	fclose(sysupgrade);
+++	free(prefix);
+++	free(path);
+++	free(command);
+++}
+++
+++static void
++ spawn_procd(struct uloop_process *proc, int ret)
++ {
++ 	char *wdt_fd = watchdog_fd();
++ 	char *argv[] = { "/sbin/procd", NULL};
++-	struct stat s;
++ 	char dbg[2];
++ 	char *rc_d_path;
++ 
++ 	if (plugd_proc.pid > 0)
++ 		kill(plugd_proc.pid, SIGKILL);
++ 
++-	if (!stat("/tmp/sysupgrade", &s))
++-		while (true)
++-			sleep(1);
++-
++ 	unsetenv("INITRAMFS");
++ 	unsetenv("PREINIT");
+++
+++	check_sysupgrade();
+++
++ 	DEBUG(2, "Exec to real procd now\n");
++ 	if (wdt_fd)
++ 		setenv("WDTFD", wdt_fd, 1);
++diff --git a/system.c b/system.c
++index 80205da..700530d 100644
++--- a/system.c
+++++ b/system.c
++@@ -25,6 +25,7 @@
++ #include <libubox/uloop.h>
++ 
++ #include "procd.h"
+++#include "sysupgrade.h"
++ #include "watchdog.h"
++ 
++ static struct blob_buf b;
++@@ -330,34 +331,6 @@ static const struct blobmsg_policy sysupgrade_policy[__SYSUPGRADE_MAX] = {
++ 	[SYSUPGRADE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
++ };
++ 
++-static void
++-procd_exec_upgraded(const char *prefix, char *path, char *command)
++-{
++-	char *wdt_fd = watchdog_fd();
++-	char *argv[] = { "/sbin/upgraded", NULL, NULL, NULL};
++-
++-	if (chroot(prefix)) {
++-		fprintf(stderr, "Failed to chroot for upgraded exec.\n");
++-		return;
++-	}
++-
++-	argv[1] = path;
++-	argv[2] = command;
++-
++-	DEBUG(2, "Exec to upgraded now\n");
++-	if (wdt_fd) {
++-		watchdog_set_cloexec(false);
++-		setenv("WDTFD", wdt_fd, 1);
++-	}
++-	execvp(argv[0], argv);
++-
++-	/* Cleanup on failure */
++-	fprintf(stderr, "Failed to exec upgraded.\n");
++-	unsetenv("WDTFD");
++-	watchdog_set_cloexec(true);
++-	chroot(".");
++-}
++-
++ static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
++ 		      struct ubus_request_data *req, const char *method,
++ 		      struct blob_attr *msg)
++@@ -371,9 +344,9 @@ static int sysupgrade(struct ubus_context *ctx, struct ubus_object *obj,
++ 	if (!tb[SYSUPGRADE_PATH] || !tb[SYSUPGRADE_PREFIX])
++ 		return UBUS_STATUS_INVALID_ARGUMENT;
++ 
++-	procd_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
++-			    blobmsg_get_string(tb[SYSUPGRADE_PATH]),
++-			    tb[SYSUPGRADE_COMMAND] ? blobmsg_get_string(tb[SYSUPGRADE_COMMAND]) : NULL);
+++	sysupgrade_exec_upgraded(blobmsg_get_string(tb[SYSUPGRADE_PREFIX]),
+++				 blobmsg_get_string(tb[SYSUPGRADE_PATH]),
+++				 tb[SYSUPGRADE_COMMAND] ? blobmsg_get_string(tb[SYSUPGRADE_COMMAND]) : NULL);
++ 	return 0;
++ }
++ 
++diff --git a/sysupgrade.c b/sysupgrade.c
++new file mode 100644
++index 0000000..30f1836
++--- /dev/null
+++++ b/sysupgrade.c
++@@ -0,0 +1,49 @@
+++/*
+++ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+++ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+++ * Copyright (C) 2017 Matthias Schiffer <mschiffer@universe-factory.net>
+++ *
+++ * 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 "watchdog.h"
+++#include "sysupgrade.h"
+++
+++#include <stdio.h>
+++#include <stdlib.h>
+++#include <unistd.h>
+++
+++
+++void sysupgrade_exec_upgraded(const char *prefix, char *path, char *command)
+++{
+++	char *wdt_fd = watchdog_fd();
+++	char *argv[] = { "/sbin/upgraded", NULL, NULL, NULL};
+++
+++	if (chroot(prefix)) {
+++		fprintf(stderr, "Failed to chroot for upgraded exec.\n");
+++		return;
+++	}
+++
+++	argv[1] = path;
+++	argv[2] = command;
+++
+++	if (wdt_fd) {
+++		watchdog_set_cloexec(false);
+++		setenv("WDTFD", wdt_fd, 1);
+++	}
+++	execvp(argv[0], argv);
+++
+++	/* Cleanup on failure */
+++	fprintf(stderr, "Failed to exec upgraded.\n");
+++	unsetenv("WDTFD");
+++	watchdog_set_cloexec(true);
+++	chroot(".");
+++}
++diff --git a/sysupgrade.h b/sysupgrade.h
++new file mode 100644
++index 0000000..8c09fc9
++--- /dev/null
+++++ b/sysupgrade.h
++@@ -0,0 +1,21 @@
+++/*
+++ * Copyright (C) 2017 Matthias Schiffer <mschiffer@universe-factory.net>
+++ *
+++ * 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 __PROCD_SYSUPGRADE_H
+++#define __PROCD_SYSUPGRADE_H
+++
+++
+++void sysupgrade_exec_upgraded(const char *prefix, char *path, char *command);
+++
+++
+++#endif
++diff --git a/watchdog.h b/watchdog.h
++index e857010..06ce5e5 100644
++--- a/watchdog.h
+++++ b/watchdog.h
++@@ -15,6 +15,8 @@
++ #ifndef __PROCD_WATCHDOG_H
++ #define __PROCD_WATCHDOG_H
++ 
+++#include <stdbool.h>
+++
++ void watchdog_init(int preinit);
++ char* watchdog_fd(void);
++ int watchdog_timeout(int timeout);
++-- 
++2.13.0
++
+diff --git a/package/system/procd/patches/1007-upgraded-define-__GNU_SOURCE.patch b/package/system/procd/patches/1007-upgraded-define-__GNU_SOURCE.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..c98cb9baa4371b64d6b1297fc0a9921d580947be
+--- /dev/null
++++ b/package/system/procd/patches/1007-upgraded-define-__GNU_SOURCE.patch
+@@ -0,0 +1,31 @@
++From 8137d283c7f858ca658fa556b95eb62e35ae980d Mon Sep 17 00:00:00 2001
++Message-Id: <8137d283c7f858ca658fa556b95eb62e35ae980d.1496349467.git.mschiffer@universe-factory.net>
++In-Reply-To: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++References: <36673c2a0e409d9c8ea9e1c15363e73bb21ae65b.1496349467.git.mschiffer@universe-factory.net>
++From: Matthias Schiffer <mschiffer@universe-factory.net>
++Date: Tue, 30 May 2017 07:23:57 +0200
++Subject: [PATCH 1007/1007] upgraded: define __GNU_SOURCE
++
++It is required on non-musl libcs for O_DIRECTORY.
++
++Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
++---
++ upgraded/upgraded.c | 2 ++
++ 1 file changed, 2 insertions(+)
++
++diff --git a/upgraded/upgraded.c b/upgraded/upgraded.c
++index 79ebd37..e70f92d 100644
++--- a/upgraded/upgraded.c
+++++ b/upgraded/upgraded.c
++@@ -12,6 +12,8 @@
++  * GNU General Public License for more details.
++  */
++ 
+++#define _GNU_SOURCE
+++
++ #include <sys/reboot.h>
++ 
++ #include <fcntl.h>
++-- 
++2.13.0
++
diff --git a/patches/openwrt/0107-procd-remove-procd-nand-package.patch b/patches/openwrt/0107-procd-remove-procd-nand-package.patch
new file mode 100644
index 0000000000000000000000000000000000000000..32ce7069cb1105fc0c73fae373eea33623dd180e
--- /dev/null
+++ b/patches/openwrt/0107-procd-remove-procd-nand-package.patch
@@ -0,0 +1,897 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Fri, 21 Apr 2017 20:37:58 +0200
+Subject: procd: remove procd-nand package
+
+We always want to support staged upgrades now, so it's better to include
+upgraded into the main package. /lib/upgrade/nand.sh is moved to
+base-files.
+
+The procd-nand-firstboot package is removed for now, it may return later
+as a separate package.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/base-files/Makefile b/package/base-files/Makefile
+index 9f6810f4d8bad89eb11db1023693ad59f7369d57..3e27e43c92af01b6cac212e77cde3d1db916ba6f 100644
+--- a/package/base-files/Makefile
++++ b/package/base-files/Makefile
+@@ -31,7 +31,7 @@ endif
+ define Package/base-files
+   SECTION:=base
+   CATEGORY:=Base system
+-  DEPENDS:=+netifd +libc +procd +jsonfilter +SIGNED_PACKAGES:usign
++  DEPENDS:=+netifd +libc +procd +jsonfilter +SIGNED_PACKAGES:usign +NAND_SUPPORT:ubi-utils
+   TITLE:=Base filesystem for OpenWrt
+   URL:=http://openwrt.org/
+   VERSION:=$(PKG_RELEASE)-$(REVISION)
+@@ -103,9 +103,16 @@ ifdef CONFIG_SIGNED_PACKAGES
+   endef
+ endif
+ 
++ifeq ($(CONFIG_NAND_SUPPORT),)
++  define Package/base-files/nand-support
++	rm -f $(1)/lib/upgrade/nand.sh
++  endef
++endif
++
+ define Package/base-files/install
+ 	$(CP) ./files/* $(1)/
+ 	$(Package/base-files/install-key)
++	$(Package/base-files/nand-support)
+ 	if [ -d $(GENERIC_PLATFORM_DIR)/base-files/. ]; then \
+ 		$(CP) $(GENERIC_PLATFORM_DIR)/base-files/* $(1)/; \
+ 	fi
+diff --git a/package/base-files/files/lib/upgrade/nand.sh b/package/base-files/files/lib/upgrade/nand.sh
+new file mode 100644
+index 0000000000000000000000000000000000000000..ec2a014a00461ca897d521e2d065f5399f1f8c48
+--- /dev/null
++++ b/package/base-files/files/lib/upgrade/nand.sh
+@@ -0,0 +1,365 @@
++#!/bin/sh
++# Copyright (C) 2014 OpenWrt.org
++#
++
++. /lib/functions.sh
++
++# 'kernel' partition on NAND contains the kernel
++CI_KERNPART="kernel"
++
++# 'ubi' partition on NAND contains UBI
++CI_UBIPART="ubi"
++
++ubi_mknod() {
++	local dir="$1"
++	local dev="/dev/$(basename $dir)"
++
++	[ -e "$dev" ] && return 0
++
++	local devid="$(cat $dir/dev)"
++	local major="${devid%%:*}"
++	local minor="${devid##*:}"
++	mknod "$dev" c $major $minor
++}
++
++nand_find_volume() {
++	local ubidevdir ubivoldir
++	ubidevdir="/sys/devices/virtual/ubi/$1"
++	[ ! -d "$ubidevdir" ] && return 1
++	for ubivoldir in $ubidevdir/${1}_*; do
++		[ ! -d "$ubivoldir" ] && continue
++		if [ "$( cat $ubivoldir/name )" = "$2" ]; then
++			basename $ubivoldir
++			ubi_mknod "$ubivoldir"
++			return 0
++		fi
++	done
++}
++
++nand_find_ubi() {
++	local ubidevdir ubidev mtdnum
++	mtdnum="$( find_mtd_index $1 )"
++	[ ! "$mtdnum" ] && return 1
++	for ubidevdir in /sys/devices/virtual/ubi/ubi*; do
++		[ ! -d "$ubidevdir" ] && continue
++		cmtdnum="$( cat $ubidevdir/mtd_num )"
++		[ ! "$mtdnum" ] && continue
++		if [ "$mtdnum" = "$cmtdnum" ]; then
++			ubidev=$( basename $ubidevdir )
++			ubi_mknod "$ubidevdir"
++			echo $ubidev
++			return 0
++		fi
++	done
++}
++
++nand_get_magic_long() {
++	dd if="$1" skip=$2 bs=4 count=1 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'
++}
++
++get_magic_long_tar() {
++	( tar xf $1 $2 -O | dd bs=4 count=1 | hexdump -v -n 4 -e '1/1 "%02x"') 2> /dev/null
++}
++
++identify_magic() {
++	local magic=$1
++	case "$magic" in
++		"55424923")
++			echo "ubi"
++			;;
++		"31181006")
++			echo "ubifs"
++			;;
++		"68737173")
++			echo "squashfs"
++			;;
++		"d00dfeed")
++			echo "fit"
++			;;
++		"4349"*)
++			echo "combined"
++			;;
++		*)
++			echo "unknown $magic"
++			;;
++	esac
++}
++
++
++identify() {
++	identify_magic $(nand_get_magic_long "$1" "${2:-0}")
++}
++
++identify_tar() {
++	identify_magic $(get_magic_long_tar "$1" "$2")
++}
++
++nand_restore_config() {
++	sync
++	local ubidev=$( nand_find_ubi $CI_UBIPART )
++	local ubivol="$( nand_find_volume $ubidev rootfs_data )"
++	[ ! "$ubivol" ] &&
++		ubivol="$( nand_find_volume $ubidev rootfs )"
++	mkdir /tmp/new_root
++	if ! mount -t ubifs /dev/$ubivol /tmp/new_root; then
++		echo "mounting ubifs $ubivol failed"
++		rmdir /tmp/new_root
++		return 1
++	fi
++	mv "$1" "/tmp/new_root/sysupgrade.tgz"
++	umount /tmp/new_root
++	sync
++	rmdir /tmp/new_root
++}
++
++nand_upgrade_prepare_ubi() {
++	local rootfs_length="$1"
++	local rootfs_type="$2"
++	local has_kernel="${3:-0}"
++	local has_env="${4:-0}"
++
++	local mtdnum="$( find_mtd_index "$CI_UBIPART" )"
++	if [ ! "$mtdnum" ]; then
++		echo "cannot find ubi mtd partition $CI_UBIPART"
++		return 1
++	fi
++
++	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++	if [ ! "$ubidev" ]; then
++		ubiattach -m "$mtdnum"
++		sync
++		ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++	fi
++
++	if [ ! "$ubidev" ]; then
++		ubiformat /dev/mtd$mtdnum -y
++		ubiattach -m "$mtdnum"
++		sync
++		ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++		[ "$has_env" -gt 0 ] && {
++			ubimkvol /dev/$ubidev -n 0 -N ubootenv -s 1MiB
++			ubimkvol /dev/$ubidev -n 1 -N ubootenv2 -s 1MiB
++		}
++	fi
++
++	local kern_ubivol="$( nand_find_volume $ubidev kernel )"
++	local root_ubivol="$( nand_find_volume $ubidev rootfs )"
++	local data_ubivol="$( nand_find_volume $ubidev rootfs_data )"
++
++	# remove ubiblock device of rootfs
++	local root_ubiblk="ubiblock${root_ubivol:3}"
++	if [ "$root_ubivol" -a -e "/dev/$root_ubiblk" ]; then
++		echo "removing $root_ubiblk"
++		if ! ubiblock -r /dev/$root_ubivol; then
++			echo "cannot remove $root_ubiblk"
++			return 1;
++		fi
++	fi
++
++	# kill volumes
++	[ "$kern_ubivol" ] && ubirmvol /dev/$ubidev -N kernel || true
++	[ "$root_ubivol" ] && ubirmvol /dev/$ubidev -N rootfs || true
++	[ "$data_ubivol" ] && ubirmvol /dev/$ubidev -N rootfs_data || true
++
++	# update kernel
++	if [ "$has_kernel" = "1" ]; then
++		if ! ubimkvol /dev/$ubidev -N kernel -s $kernel_length; then
++			echo "cannot create kernel volume"
++			return 1;
++		fi
++	fi
++
++	# update rootfs
++	local root_size_param
++	if [ "$rootfs_type" = "ubifs" ]; then
++		root_size_param="-m"
++	else
++		root_size_param="-s $rootfs_length"
++	fi
++	if ! ubimkvol /dev/$ubidev -N rootfs $root_size_param; then
++		echo "cannot create rootfs volume"
++		return 1;
++	fi
++
++	# create rootfs_data for non-ubifs rootfs
++	if [ "$rootfs_type" != "ubifs" ]; then
++		if ! ubimkvol /dev/$ubidev -N rootfs_data -m; then
++			echo "cannot initialize rootfs_data volume"
++			return 1
++		fi
++	fi
++	sync
++	return 0
++}
++
++nand_do_upgrade_success() {
++	local conf_tar="/tmp/sysupgrade.tgz"
++
++	sync
++	[ -f "$conf_tar" ] && nand_restore_config "$conf_tar"
++	echo "sysupgrade successful"
++	reboot -f
++}
++
++# Flash the UBI image to MTD partition
++nand_upgrade_ubinized() {
++	local ubi_file="$1"
++	local mtdnum="$(find_mtd_index "$CI_UBIPART")"
++
++	[ ! "$mtdnum" ] && {
++		CI_UBIPART="rootfs"
++		mtdnum="$(find_mtd_index "$CI_UBIPART")"
++	}
++
++	if [ ! "$mtdnum" ]; then
++		echo "cannot find mtd device $CI_UBIPART"
++		reboot -f
++	fi
++
++	local mtddev="/dev/mtd${mtdnum}"
++	ubidetach -p "${mtddev}" || true
++	sync
++	ubiformat "${mtddev}" -y -f "${ubi_file}"
++	ubiattach -p "${mtddev}"
++	nand_do_upgrade_success
++}
++
++# Write the UBIFS image to UBI volume
++nand_upgrade_ubifs() {
++	local rootfs_length=`(cat $1 | wc -c) 2> /dev/null`
++
++	nand_upgrade_prepare_ubi "$rootfs_length" "ubifs" "0" "0"
++
++	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++	local root_ubivol="$(nand_find_volume $ubidev rootfs)"
++	ubiupdatevol /dev/$root_ubivol -s $rootfs_length $1
++
++	nand_do_upgrade_success
++}
++
++nand_upgrade_tar() {
++	local tar_file="$1"
++	local board_name="$(cat /tmp/sysinfo/board_name)"
++	local kernel_mtd="$(find_mtd_index $CI_KERNPART)"
++
++	local kernel_length=`(tar xf $tar_file sysupgrade-$board_name/kernel -O | wc -c) 2> /dev/null`
++	local rootfs_length=`(tar xf $tar_file sysupgrade-$board_name/root -O | wc -c) 2> /dev/null`
++
++	local rootfs_type="$(identify_tar "$tar_file" sysupgrade-$board_name/root)"
++
++	local has_kernel=1
++	local has_env=0
++
++	[ "$kernel_length" != 0 -a -n "$kernel_mtd" ] && {
++		tar xf $tar_file sysupgrade-$board_name/kernel -O | mtd write - $CI_KERNPART
++	}
++	[ "$kernel_length" = 0 -o ! -z "$kernel_mtd" ] && has_kernel=0
++
++	nand_upgrade_prepare_ubi "$rootfs_length" "$rootfs_type" "$has_kernel" "$has_env"
++
++	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
++	[ "$has_kernel" = "1" ] && {
++		local kern_ubivol="$(nand_find_volume $ubidev kernel)"
++	 	tar xf $tar_file sysupgrade-$board_name/kernel -O | \
++			ubiupdatevol /dev/$kern_ubivol -s $kernel_length -
++	}
++
++	local root_ubivol="$(nand_find_volume $ubidev rootfs)"
++	tar xf $tar_file sysupgrade-$board_name/root -O | \
++		ubiupdatevol /dev/$root_ubivol -s $rootfs_length -
++
++	nand_do_upgrade_success
++}
++
++# Recognize type of passed file and start the upgrade process
++nand_do_upgrade_stage2() {
++	local file_type=$(identify $1)
++
++	[ ! "$(find_mtd_index "$CI_UBIPART")" ] && CI_UBIPART="rootfs"
++
++	case "$file_type" in
++		"ubi")		nand_upgrade_ubinized $1;;
++		"ubifs")	nand_upgrade_ubifs $1;;
++		*)		nand_upgrade_tar $1;;
++	esac
++}
++
++nand_upgrade_stage2() {
++	[ $1 = "nand" ] && {
++		[ -f "$2" ] && {
++			touch /tmp/sysupgrade
++
++			killall -9 telnetd
++			killall -9 dropbear
++			killall -9 ash
++
++			kill_remaining TERM
++			sleep 3
++			kill_remaining KILL
++
++			sleep 1
++
++			if [ -n "$(rootfs_type)" ]; then
++				v "Switching to ramdisk..."
++				run_ramfs ". /lib/functions.sh; include /lib/upgrade; nand_do_upgrade_stage2 $2"
++			else
++				nand_do_upgrade_stage2 $2
++			fi
++			return 0
++		}
++		echo "Nand upgrade failed"
++		exit 1
++	}
++}
++
++nand_upgrade_stage1() {
++	[ -f /tmp/sysupgrade-nand-path ] && {
++		path="$(cat /tmp/sysupgrade-nand-path)"
++		[ "$SAVE_CONFIG" != 1 -a -f "$CONF_TAR" ] &&
++			rm $CONF_TAR
++
++		ubus call system nandupgrade "{\"prefix\": \"$RAM_ROOT\", \"path\": \"$path\" }"
++		exit 0
++	}
++}
++append sysupgrade_pre_upgrade nand_upgrade_stage1
++
++# Check if passed file is a valid one for NAND sysupgrade. Currently it accepts
++# 3 types of files:
++# 1) UBI - should contain an ubinized image, header is checked for the proper
++#    MAGIC
++# 2) UBIFS - should contain UBIFS partition that will replace "rootfs" volume,
++#    header is checked for the proper MAGIC
++# 3) TAR - archive has to include "sysupgrade-BOARD" directory with a non-empty
++#    "CONTROL" file (at this point its content isn't verified)
++#
++# You usually want to call this function in platform_check_image.
++#
++# $(1): board name, used in case of passing TAR file
++# $(2): file to be checked
++nand_do_platform_check() {
++	local board_name="$1"
++	local tar_file="$2"
++	local control_length=`(tar xf $tar_file sysupgrade-$board_name/CONTROL -O | wc -c) 2> /dev/null`
++	local file_type="$(identify $2)"
++
++	[ "$control_length" = 0 -a "$file_type" != "ubi" -a "$file_type" != "ubifs" ] && {
++		echo "Invalid sysupgrade file."
++		return 1
++	}
++
++	echo -n $2 > /tmp/sysupgrade-nand-path
++	cp /sbin/upgraded /tmp/
++
++	return 0
++}
++
++# Start NAND upgrade process
++#
++# $(1): file to be used for upgrade
++nand_do_upgrade() {
++	echo -n $1 > /tmp/sysupgrade-nand-path
++	install_bin /sbin/upgraded
++	ln -s "$RAM_ROOT"/sbin/upgraded /tmp/upgraded
++	nand_upgrade_stage1
++}
+diff --git a/package/system/procd/Makefile b/package/system/procd/Makefile
+index d343f7481f3bb627b3e7ad79f45864de82a42371..b95ef64b727f6f07c8f92c9caf027c21dbb30863 100644
+--- a/package/system/procd/Makefile
++++ b/package/system/procd/Makefile
+@@ -24,7 +24,7 @@ PKG_LICENSE_FILES:=
+ 
+ PKG_MAINTAINER:=John Crispin <blogic@openwrt.org>
+ 
+-PKG_CONFIG_DEPENDS:= CONFIG_KERNEL_SECCOMP CONFIG_NAND_SUPPORT CONFIG_PROCD_SHOW_BOOT CONFIG_PROCD_ZRAM_TMPFS \
++PKG_CONFIG_DEPENDS:= CONFIG_KERNEL_SECCOMP CONFIG_PROCD_SHOW_BOOT CONFIG_PROCD_ZRAM_TMPFS \
+ 	CONFIG_KERNEL_NAMESPACES CONFIG_PACKAGE_procd-ujail CONFIG_PACKAGE_procd-seccomp
+ 
+ include $(INCLUDE_DIR)/package.mk
+@@ -35,7 +35,7 @@ TARGET_LDFLAGS += $(if $(CONFIG_USE_GLIBC),-lrt)
+ define Package/procd
+   SECTION:=base
+   CATEGORY:=Base system
+-  DEPENDS:=+ubusd +ubus +libjson-script +ubox +USE_GLIBC:librt +libubox +libubus +NAND_SUPPORT:procd-nand
++  DEPENDS:=+ubusd +ubus +libjson-script +ubox +USE_GLIBC:librt +libubox +libubus
+   TITLE:=OpenWrt system process manager
+ endef
+ 
+@@ -53,20 +53,6 @@ define Package/procd-seccomp
+   TITLE:=OpenWrt process seccomp helper + utrace
+ endef
+ 
+-define Package/procd-nand
+-  SECTION:=utils
+-  CATEGORY:=Utilities
+-  DEPENDS:=@NAND_SUPPORT +ubi-utils
+-  TITLE:=OpenWrt sysupgrade nand helper
+-endef
+-
+-define Package/procd-nand-firstboot
+-  SECTION:=utils
+-  CATEGORY:=Utilities
+-  DEPENDS:=procd-nand
+-  TITLE:=OpenWrt firstboot nand helper
+-endef
+-
+ define Package/procd/config
+ menu "Configuration"
+ 	depends on PACKAGE_procd
+@@ -84,10 +70,6 @@ endmenu
+ endef
+ 
+ 
+-ifeq ($(CONFIG_NAND_SUPPORT),y)
+-  CMAKE_OPTIONS += -DBUILD_UPGRADED=1
+-endif
+-
+ ifeq ($(CONFIG_PROCD_SHOW_BOOT),y)
+   CMAKE_OPTIONS += -DSHOW_BOOT_ON_CONSOLE=1
+ endif
+@@ -107,7 +89,7 @@ endif
+ define Package/procd/install
+ 	$(INSTALL_DIR) $(1)/sbin $(1)/etc $(1)/lib/functions
+ 
+-	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/{init,procd,askfirst,udevtrigger} $(1)/sbin/
++	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/{init,procd,askfirst,udevtrigger,upgraded} $(1)/sbin/
+ 	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libsetlbf.so $(1)/lib
+ 	$(INSTALL_BIN) ./files/reload_config $(1)/sbin/
+ 	$(INSTALL_DATA) ./files/hotplug*.json $(1)/etc/
+@@ -126,21 +108,6 @@ define Package/procd-seccomp/install
+ 	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libpreload-trace.so $(1)/lib
+ endef
+ 
+-define Package/procd-nand/install
+-	$(INSTALL_DIR) $(1)/sbin $(1)/lib/upgrade
+-
+-	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/upgraded $(1)/sbin/
+-	$(INSTALL_DATA) ./files/nand.sh $(1)/lib/upgrade/
+-endef
+-
+-define Package/procd-nand-firstboot/install
+-	$(INSTALL_DIR) $(1)/lib/preinit
+-
+-	$(INSTALL_DATA) ./files/nand-preinit.sh $(1)/lib/preinit/60-nand-firstboot.sh
+-endef
+-
+ $(eval $(call BuildPackage,procd))
+ $(eval $(call BuildPackage,procd-ujail))
+ $(eval $(call BuildPackage,procd-seccomp))
+-$(eval $(call BuildPackage,procd-nand))
+-$(eval $(call BuildPackage,procd-nand-firstboot))
+diff --git a/package/system/procd/files/nand-preinit.sh b/package/system/procd/files/nand-preinit.sh
+deleted file mode 100644
+index cf596246d1f2891cbeb7b5c7cac4bb6e002b13fb..0000000000000000000000000000000000000000
+--- a/package/system/procd/files/nand-preinit.sh
++++ /dev/null
+@@ -1,21 +0,0 @@
+-#!/bin/sh
+-# Copyright (C) 2014 OpenWrt.org
+-
+-nand_takeover() {
+-	. /lib/upgrade/nand.sh
+-	mtd=$(find_mtd_index "$CI_UBIPART")
+-	esize=$(cat /proc/mtd | grep mtd$mtd |cut -d" " -f 3)
+-	[ -z "$esize" ] && return 1
+-	esize=$(printf "%d" 0x$esize)
+-	for a in `seq 0 64`; do
+-		mtd -o $((a * esize)) -l 400 dump /dev/mtd$mtd > /tmp/takeover.hdr
+-		MAGIC=$(dd if=/tmp/takeover.hdr bs=1 skip=261 count=5 2> /dev/null)
+-		SIZE=$(printf "%d" 0x$(dd if=/tmp/takeover.hdr bs=4 count=1 2> /dev/null | hexdump -v -n 4 -e '1/1 "%02x"'))
+-		[ "$MAGIC" = "ustar" ] && {
+-			mtd -o $((a * esize)) -l $((SIZE + 4)) dump /dev/mtd$mtd | dd bs=1 skip=4 of=/tmp/sysupgrade.tar
+-			nand_do_upgrade_stage2 /tmp/sysupgrade.tar
+-		}
+-	done
+-}
+-
+-boot_hook_add initramfs nand_takeover
+diff --git a/package/system/procd/files/nand.sh b/package/system/procd/files/nand.sh
+deleted file mode 100644
+index ec2a014a00461ca897d521e2d065f5399f1f8c48..0000000000000000000000000000000000000000
+--- a/package/system/procd/files/nand.sh
++++ /dev/null
+@@ -1,365 +0,0 @@
+-#!/bin/sh
+-# Copyright (C) 2014 OpenWrt.org
+-#
+-
+-. /lib/functions.sh
+-
+-# 'kernel' partition on NAND contains the kernel
+-CI_KERNPART="kernel"
+-
+-# 'ubi' partition on NAND contains UBI
+-CI_UBIPART="ubi"
+-
+-ubi_mknod() {
+-	local dir="$1"
+-	local dev="/dev/$(basename $dir)"
+-
+-	[ -e "$dev" ] && return 0
+-
+-	local devid="$(cat $dir/dev)"
+-	local major="${devid%%:*}"
+-	local minor="${devid##*:}"
+-	mknod "$dev" c $major $minor
+-}
+-
+-nand_find_volume() {
+-	local ubidevdir ubivoldir
+-	ubidevdir="/sys/devices/virtual/ubi/$1"
+-	[ ! -d "$ubidevdir" ] && return 1
+-	for ubivoldir in $ubidevdir/${1}_*; do
+-		[ ! -d "$ubivoldir" ] && continue
+-		if [ "$( cat $ubivoldir/name )" = "$2" ]; then
+-			basename $ubivoldir
+-			ubi_mknod "$ubivoldir"
+-			return 0
+-		fi
+-	done
+-}
+-
+-nand_find_ubi() {
+-	local ubidevdir ubidev mtdnum
+-	mtdnum="$( find_mtd_index $1 )"
+-	[ ! "$mtdnum" ] && return 1
+-	for ubidevdir in /sys/devices/virtual/ubi/ubi*; do
+-		[ ! -d "$ubidevdir" ] && continue
+-		cmtdnum="$( cat $ubidevdir/mtd_num )"
+-		[ ! "$mtdnum" ] && continue
+-		if [ "$mtdnum" = "$cmtdnum" ]; then
+-			ubidev=$( basename $ubidevdir )
+-			ubi_mknod "$ubidevdir"
+-			echo $ubidev
+-			return 0
+-		fi
+-	done
+-}
+-
+-nand_get_magic_long() {
+-	dd if="$1" skip=$2 bs=4 count=1 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'
+-}
+-
+-get_magic_long_tar() {
+-	( tar xf $1 $2 -O | dd bs=4 count=1 | hexdump -v -n 4 -e '1/1 "%02x"') 2> /dev/null
+-}
+-
+-identify_magic() {
+-	local magic=$1
+-	case "$magic" in
+-		"55424923")
+-			echo "ubi"
+-			;;
+-		"31181006")
+-			echo "ubifs"
+-			;;
+-		"68737173")
+-			echo "squashfs"
+-			;;
+-		"d00dfeed")
+-			echo "fit"
+-			;;
+-		"4349"*)
+-			echo "combined"
+-			;;
+-		*)
+-			echo "unknown $magic"
+-			;;
+-	esac
+-}
+-
+-
+-identify() {
+-	identify_magic $(nand_get_magic_long "$1" "${2:-0}")
+-}
+-
+-identify_tar() {
+-	identify_magic $(get_magic_long_tar "$1" "$2")
+-}
+-
+-nand_restore_config() {
+-	sync
+-	local ubidev=$( nand_find_ubi $CI_UBIPART )
+-	local ubivol="$( nand_find_volume $ubidev rootfs_data )"
+-	[ ! "$ubivol" ] &&
+-		ubivol="$( nand_find_volume $ubidev rootfs )"
+-	mkdir /tmp/new_root
+-	if ! mount -t ubifs /dev/$ubivol /tmp/new_root; then
+-		echo "mounting ubifs $ubivol failed"
+-		rmdir /tmp/new_root
+-		return 1
+-	fi
+-	mv "$1" "/tmp/new_root/sysupgrade.tgz"
+-	umount /tmp/new_root
+-	sync
+-	rmdir /tmp/new_root
+-}
+-
+-nand_upgrade_prepare_ubi() {
+-	local rootfs_length="$1"
+-	local rootfs_type="$2"
+-	local has_kernel="${3:-0}"
+-	local has_env="${4:-0}"
+-
+-	local mtdnum="$( find_mtd_index "$CI_UBIPART" )"
+-	if [ ! "$mtdnum" ]; then
+-		echo "cannot find ubi mtd partition $CI_UBIPART"
+-		return 1
+-	fi
+-
+-	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
+-	if [ ! "$ubidev" ]; then
+-		ubiattach -m "$mtdnum"
+-		sync
+-		ubidev="$( nand_find_ubi "$CI_UBIPART" )"
+-	fi
+-
+-	if [ ! "$ubidev" ]; then
+-		ubiformat /dev/mtd$mtdnum -y
+-		ubiattach -m "$mtdnum"
+-		sync
+-		ubidev="$( nand_find_ubi "$CI_UBIPART" )"
+-		[ "$has_env" -gt 0 ] && {
+-			ubimkvol /dev/$ubidev -n 0 -N ubootenv -s 1MiB
+-			ubimkvol /dev/$ubidev -n 1 -N ubootenv2 -s 1MiB
+-		}
+-	fi
+-
+-	local kern_ubivol="$( nand_find_volume $ubidev kernel )"
+-	local root_ubivol="$( nand_find_volume $ubidev rootfs )"
+-	local data_ubivol="$( nand_find_volume $ubidev rootfs_data )"
+-
+-	# remove ubiblock device of rootfs
+-	local root_ubiblk="ubiblock${root_ubivol:3}"
+-	if [ "$root_ubivol" -a -e "/dev/$root_ubiblk" ]; then
+-		echo "removing $root_ubiblk"
+-		if ! ubiblock -r /dev/$root_ubivol; then
+-			echo "cannot remove $root_ubiblk"
+-			return 1;
+-		fi
+-	fi
+-
+-	# kill volumes
+-	[ "$kern_ubivol" ] && ubirmvol /dev/$ubidev -N kernel || true
+-	[ "$root_ubivol" ] && ubirmvol /dev/$ubidev -N rootfs || true
+-	[ "$data_ubivol" ] && ubirmvol /dev/$ubidev -N rootfs_data || true
+-
+-	# update kernel
+-	if [ "$has_kernel" = "1" ]; then
+-		if ! ubimkvol /dev/$ubidev -N kernel -s $kernel_length; then
+-			echo "cannot create kernel volume"
+-			return 1;
+-		fi
+-	fi
+-
+-	# update rootfs
+-	local root_size_param
+-	if [ "$rootfs_type" = "ubifs" ]; then
+-		root_size_param="-m"
+-	else
+-		root_size_param="-s $rootfs_length"
+-	fi
+-	if ! ubimkvol /dev/$ubidev -N rootfs $root_size_param; then
+-		echo "cannot create rootfs volume"
+-		return 1;
+-	fi
+-
+-	# create rootfs_data for non-ubifs rootfs
+-	if [ "$rootfs_type" != "ubifs" ]; then
+-		if ! ubimkvol /dev/$ubidev -N rootfs_data -m; then
+-			echo "cannot initialize rootfs_data volume"
+-			return 1
+-		fi
+-	fi
+-	sync
+-	return 0
+-}
+-
+-nand_do_upgrade_success() {
+-	local conf_tar="/tmp/sysupgrade.tgz"
+-
+-	sync
+-	[ -f "$conf_tar" ] && nand_restore_config "$conf_tar"
+-	echo "sysupgrade successful"
+-	reboot -f
+-}
+-
+-# Flash the UBI image to MTD partition
+-nand_upgrade_ubinized() {
+-	local ubi_file="$1"
+-	local mtdnum="$(find_mtd_index "$CI_UBIPART")"
+-
+-	[ ! "$mtdnum" ] && {
+-		CI_UBIPART="rootfs"
+-		mtdnum="$(find_mtd_index "$CI_UBIPART")"
+-	}
+-
+-	if [ ! "$mtdnum" ]; then
+-		echo "cannot find mtd device $CI_UBIPART"
+-		reboot -f
+-	fi
+-
+-	local mtddev="/dev/mtd${mtdnum}"
+-	ubidetach -p "${mtddev}" || true
+-	sync
+-	ubiformat "${mtddev}" -y -f "${ubi_file}"
+-	ubiattach -p "${mtddev}"
+-	nand_do_upgrade_success
+-}
+-
+-# Write the UBIFS image to UBI volume
+-nand_upgrade_ubifs() {
+-	local rootfs_length=`(cat $1 | wc -c) 2> /dev/null`
+-
+-	nand_upgrade_prepare_ubi "$rootfs_length" "ubifs" "0" "0"
+-
+-	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
+-	local root_ubivol="$(nand_find_volume $ubidev rootfs)"
+-	ubiupdatevol /dev/$root_ubivol -s $rootfs_length $1
+-
+-	nand_do_upgrade_success
+-}
+-
+-nand_upgrade_tar() {
+-	local tar_file="$1"
+-	local board_name="$(cat /tmp/sysinfo/board_name)"
+-	local kernel_mtd="$(find_mtd_index $CI_KERNPART)"
+-
+-	local kernel_length=`(tar xf $tar_file sysupgrade-$board_name/kernel -O | wc -c) 2> /dev/null`
+-	local rootfs_length=`(tar xf $tar_file sysupgrade-$board_name/root -O | wc -c) 2> /dev/null`
+-
+-	local rootfs_type="$(identify_tar "$tar_file" sysupgrade-$board_name/root)"
+-
+-	local has_kernel=1
+-	local has_env=0
+-
+-	[ "$kernel_length" != 0 -a -n "$kernel_mtd" ] && {
+-		tar xf $tar_file sysupgrade-$board_name/kernel -O | mtd write - $CI_KERNPART
+-	}
+-	[ "$kernel_length" = 0 -o ! -z "$kernel_mtd" ] && has_kernel=0
+-
+-	nand_upgrade_prepare_ubi "$rootfs_length" "$rootfs_type" "$has_kernel" "$has_env"
+-
+-	local ubidev="$( nand_find_ubi "$CI_UBIPART" )"
+-	[ "$has_kernel" = "1" ] && {
+-		local kern_ubivol="$(nand_find_volume $ubidev kernel)"
+-	 	tar xf $tar_file sysupgrade-$board_name/kernel -O | \
+-			ubiupdatevol /dev/$kern_ubivol -s $kernel_length -
+-	}
+-
+-	local root_ubivol="$(nand_find_volume $ubidev rootfs)"
+-	tar xf $tar_file sysupgrade-$board_name/root -O | \
+-		ubiupdatevol /dev/$root_ubivol -s $rootfs_length -
+-
+-	nand_do_upgrade_success
+-}
+-
+-# Recognize type of passed file and start the upgrade process
+-nand_do_upgrade_stage2() {
+-	local file_type=$(identify $1)
+-
+-	[ ! "$(find_mtd_index "$CI_UBIPART")" ] && CI_UBIPART="rootfs"
+-
+-	case "$file_type" in
+-		"ubi")		nand_upgrade_ubinized $1;;
+-		"ubifs")	nand_upgrade_ubifs $1;;
+-		*)		nand_upgrade_tar $1;;
+-	esac
+-}
+-
+-nand_upgrade_stage2() {
+-	[ $1 = "nand" ] && {
+-		[ -f "$2" ] && {
+-			touch /tmp/sysupgrade
+-
+-			killall -9 telnetd
+-			killall -9 dropbear
+-			killall -9 ash
+-
+-			kill_remaining TERM
+-			sleep 3
+-			kill_remaining KILL
+-
+-			sleep 1
+-
+-			if [ -n "$(rootfs_type)" ]; then
+-				v "Switching to ramdisk..."
+-				run_ramfs ". /lib/functions.sh; include /lib/upgrade; nand_do_upgrade_stage2 $2"
+-			else
+-				nand_do_upgrade_stage2 $2
+-			fi
+-			return 0
+-		}
+-		echo "Nand upgrade failed"
+-		exit 1
+-	}
+-}
+-
+-nand_upgrade_stage1() {
+-	[ -f /tmp/sysupgrade-nand-path ] && {
+-		path="$(cat /tmp/sysupgrade-nand-path)"
+-		[ "$SAVE_CONFIG" != 1 -a -f "$CONF_TAR" ] &&
+-			rm $CONF_TAR
+-
+-		ubus call system nandupgrade "{\"prefix\": \"$RAM_ROOT\", \"path\": \"$path\" }"
+-		exit 0
+-	}
+-}
+-append sysupgrade_pre_upgrade nand_upgrade_stage1
+-
+-# Check if passed file is a valid one for NAND sysupgrade. Currently it accepts
+-# 3 types of files:
+-# 1) UBI - should contain an ubinized image, header is checked for the proper
+-#    MAGIC
+-# 2) UBIFS - should contain UBIFS partition that will replace "rootfs" volume,
+-#    header is checked for the proper MAGIC
+-# 3) TAR - archive has to include "sysupgrade-BOARD" directory with a non-empty
+-#    "CONTROL" file (at this point its content isn't verified)
+-#
+-# You usually want to call this function in platform_check_image.
+-#
+-# $(1): board name, used in case of passing TAR file
+-# $(2): file to be checked
+-nand_do_platform_check() {
+-	local board_name="$1"
+-	local tar_file="$2"
+-	local control_length=`(tar xf $tar_file sysupgrade-$board_name/CONTROL -O | wc -c) 2> /dev/null`
+-	local file_type="$(identify $2)"
+-
+-	[ "$control_length" = 0 -a "$file_type" != "ubi" -a "$file_type" != "ubifs" ] && {
+-		echo "Invalid sysupgrade file."
+-		return 1
+-	}
+-
+-	echo -n $2 > /tmp/sysupgrade-nand-path
+-	cp /sbin/upgraded /tmp/
+-
+-	return 0
+-}
+-
+-# Start NAND upgrade process
+-#
+-# $(1): file to be used for upgrade
+-nand_do_upgrade() {
+-	echo -n $1 > /tmp/sysupgrade-nand-path
+-	install_bin /sbin/upgraded
+-	ln -s "$RAM_ROOT"/sbin/upgraded /tmp/upgraded
+-	nand_upgrade_stage1
+-}
diff --git a/patches/openwrt/0108-base-files-always-use-staged-sysupgrade.patch b/patches/openwrt/0108-base-files-always-use-staged-sysupgrade.patch
new file mode 100644
index 0000000000000000000000000000000000000000..2e4efe787d17c1923783c92a77b971b96b8d8fe2
--- /dev/null
+++ b/patches/openwrt/0108-base-files-always-use-staged-sysupgrade.patch
@@ -0,0 +1,468 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Sat, 22 Apr 2017 00:54:50 +0200
+Subject: base-files: always use staged sysupgrade
+
+Support for the -d and -p options is dropped; it may be added again at some
+point by adding these flags to the ubus sysupgrade call.
+
+A downside of this is that we get a lot less information about the progress
+of the upgrade: as soon as the actual upgrade starts, all shell sessions
+are killed to allow unmounting the root filesystem.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/base-files/files/lib/upgrade/common.sh b/package/base-files/files/lib/upgrade/common.sh
+index 2064adf775c491725dbff5826b94bd6132d8d060..23f18ceb775caac057ee7a757922954f812f77ce 100644
+--- a/package/base-files/files/lib/upgrade/common.sh
++++ b/package/base-files/files/lib/upgrade/common.sh
+@@ -92,51 +92,37 @@ run_ramfs() { # <command> [...]
+ 	exec /bin/busybox ash -c "$*"
+ }
+ 
+-kill_remaining() { # [ <signal> ]
++kill_remaining() { # [ <signal> [ <loop> ] ]
+ 	local sig="${1:-TERM}"
++	local loop="${2:-0}"
++	local run=true
++	local stat
++
+ 	echo -n "Sending $sig to remaining processes ... "
+ 
+-	local my_pid=$$
+-	local my_ppid=$(cut -d' ' -f4  /proc/$my_pid/stat)
+-	local my_ppisupgraded=
+-	grep -q upgraded /proc/$my_ppid/cmdline >/dev/null && {
+-		local my_ppisupgraded=1
+-	}
+-	
+-	local stat
+-	for stat in /proc/[0-9]*/stat; do
+-		[ -f "$stat" ] || continue
+-
+-		local pid name state ppid rest
+-		read pid name state ppid rest < $stat
+-		name="${name#(}"; name="${name%)}"
+-
+-		local cmdline
+-		read cmdline < /proc/$pid/cmdline
+-
+-		# Skip kernel threads
+-		[ -n "$cmdline" ] || continue
+-
+-		if [ $$ -eq 1 ] || [ $my_ppid -eq 1 ] && [ -n "$my_ppisupgraded" ]; then
+-			# Running as init process, kill everything except me
+-			if [ $pid -ne $$ ] && [ $pid -ne $my_ppid ]; then
+-				echo -n "$name "
+-				kill -$sig $pid 2>/dev/null
+-			fi
+-		else 
+-			case "$name" in
+-				# Skip essential services
+-				*procd*|*ash*|*init*|*watchdog*|*ssh*|*dropbear*|*telnet*|*login*|*hostapd*|*wpa_supplicant*|*nas*) : ;;
+-
+-				# Killable process
+-				*)
+-					if [ $pid -ne $$ ] && [ $ppid -ne $$ ]; then
+-						echo -n "$name "
+-						kill -$sig $pid 2>/dev/null
+-					fi
+-				;;
+-			esac
+-		fi
++	while $run; do
++		run=false
++		for stat in /proc/[0-9]*/stat; do
++			[ -f "$stat" ] || continue
++
++			local pid name state ppid rest
++			read pid name state ppid rest < $stat
++			name="${name#(}"; name="${name%)}"
++
++			# Skip PID1, ourself and our children
++			[ $pid -ne 1 -a $pid -ne $$ -a $ppid -ne $$ ] || continue
++
++			local cmdline
++			read cmdline < /proc/$pid/cmdline
++
++			# Skip kernel threads
++			[ -n "$cmdline" ] || continue
++
++			echo -n "$name "
++			kill -$sig $pid 2>/dev/null
++
++			[ $loop -eq 1 ] && run=true
++		done
+ 	done
+ 	echo ""
+ }
+@@ -171,28 +157,31 @@ v() {
+ 	[ "$VERBOSE" -ge 1 ] && echo "$@"
+ }
+ 
++json_string() {
++	local v="$1"
++	v="${v//\\/\\\\}"
++	v="${v//\"/\\\"}"
++	echo "\"$v\""
++}
++
+ rootfs_type() {
+ 	/bin/mount | awk '($3 ~ /^\/$/) && ($5 !~ /rootfs/) { print $5 }'
+ }
+ 
+ get_image() { # <source> [ <command> ]
+ 	local from="$1"
+-	local conc="$2"
+-	local cmd
+-
+-	case "$from" in
+-		http://*|ftp://*) cmd="wget -O- -q";;
+-		*) cmd="cat";;
+-	esac
+-	if [ -z "$conc" ]; then
+-		local magic="$(eval $cmd \"$from\" 2>/dev/null | dd bs=2 count=1 2>/dev/null | hexdump -n 2 -e '1/1 "%02x"')"
++	local cat="$2"
++
++	if [ -z "$cat" ]; then
++		local magic="$(dd if="$from" bs=2 count=1 2>/dev/null | hexdump -n 2 -e '1/1 "%02x"')"
+ 		case "$magic" in
+-			1f8b) conc="zcat";;
+-			425a) conc="bzcat";;
++			1f8b) cat="zcat";;
++			425a) cat="bzcat";;
++			*) cat="cat";;
+ 		esac
+ 	fi
+ 
+-	eval "$cmd \"$from\" 2>/dev/null ${conc:+| $conc}"
++	$cat "$from" 2>/dev/null
+ }
+ 
+ get_magic_word() {
+@@ -316,12 +305,14 @@ default_do_upgrade() {
+ 	fi
+ }
+ 
+-do_upgrade() {
++do_upgrade_stage2() {
+ 	v "Performing system upgrade..."
+-	if type 'platform_do_upgrade' >/dev/null 2>/dev/null; then
+-		platform_do_upgrade "$ARGV"
++	if [ -n "$do_upgrade" ]; then
++		$do_upgrade "$IMAGE"
++	elif type 'platform_do_upgrade' >/dev/null 2>/dev/null; then
++		platform_do_upgrade "$IMAGE"
+ 	else
+-		default_do_upgrade "$ARGV"
++		default_do_upgrade "$IMAGE"
+ 	fi
+ 
+ 	if [ "$SAVE_CONFIG" -eq 1 ] && type 'platform_copy_config' >/dev/null 2>/dev/null; then
+@@ -329,11 +320,11 @@ do_upgrade() {
+ 	fi
+ 
+ 	v "Upgrade completed"
+-	[ -n "$DELAY" ] && sleep "$DELAY"
+-	ask_bool 1 "Reboot" && {
+-		v "Rebooting system..."
+-		reboot -f
+-		sleep 5
+-		echo b 2>/dev/null >/proc/sysrq-trigger
+-	}
++	sleep 1
++
++	v "Rebooting system..."
++	umount -a
++	reboot -f
++	sleep 5
++	echo b 2>/dev/null >/proc/sysrq-trigger
+ }
+diff --git a/package/base-files/files/lib/upgrade/nand.sh b/package/base-files/files/lib/upgrade/nand.sh
+index ec2a014a00461ca897d521e2d065f5399f1f8c48..05940e2567e22fe1936fb5afdc7c1df4826570ee 100644
+--- a/package/base-files/files/lib/upgrade/nand.sh
++++ b/package/base-files/files/lib/upgrade/nand.sh
+@@ -272,7 +272,16 @@ nand_upgrade_tar() {
+ }
+ 
+ # Recognize type of passed file and start the upgrade process
+-nand_do_upgrade_stage2() {
++nand_do_upgrade() {
++	if [ -n "$IS_PRE_UPGRADE" ]; then
++		# Previously, nand_do_upgrade was called from the platform_pre_upgrade
++		# hook; this piece of code handles scripts that haven't been
++		# updated. All scripts should gradually move to call nand_do_upgrade
++		# from platform_do_upgrade instead.
++		export do_upgrade=nand_do_upgrade
++		return
++	fi
++
+ 	local file_type=$(identify $1)
+ 
+ 	[ ! "$(find_mtd_index "$CI_UBIPART")" ] && CI_UBIPART="rootfs"
+@@ -284,46 +293,6 @@ nand_do_upgrade_stage2() {
+ 	esac
+ }
+ 
+-nand_upgrade_stage2() {
+-	[ $1 = "nand" ] && {
+-		[ -f "$2" ] && {
+-			touch /tmp/sysupgrade
+-
+-			killall -9 telnetd
+-			killall -9 dropbear
+-			killall -9 ash
+-
+-			kill_remaining TERM
+-			sleep 3
+-			kill_remaining KILL
+-
+-			sleep 1
+-
+-			if [ -n "$(rootfs_type)" ]; then
+-				v "Switching to ramdisk..."
+-				run_ramfs ". /lib/functions.sh; include /lib/upgrade; nand_do_upgrade_stage2 $2"
+-			else
+-				nand_do_upgrade_stage2 $2
+-			fi
+-			return 0
+-		}
+-		echo "Nand upgrade failed"
+-		exit 1
+-	}
+-}
+-
+-nand_upgrade_stage1() {
+-	[ -f /tmp/sysupgrade-nand-path ] && {
+-		path="$(cat /tmp/sysupgrade-nand-path)"
+-		[ "$SAVE_CONFIG" != 1 -a -f "$CONF_TAR" ] &&
+-			rm $CONF_TAR
+-
+-		ubus call system nandupgrade "{\"prefix\": \"$RAM_ROOT\", \"path\": \"$path\" }"
+-		exit 0
+-	}
+-}
+-append sysupgrade_pre_upgrade nand_upgrade_stage1
+-
+ # Check if passed file is a valid one for NAND sysupgrade. Currently it accepts
+ # 3 types of files:
+ # 1) UBI - should contain an ubinized image, header is checked for the proper
+@@ -353,13 +322,3 @@ nand_do_platform_check() {
+ 
+ 	return 0
+ }
+-
+-# Start NAND upgrade process
+-#
+-# $(1): file to be used for upgrade
+-nand_do_upgrade() {
+-	echo -n $1 > /tmp/sysupgrade-nand-path
+-	install_bin /sbin/upgraded
+-	ln -s "$RAM_ROOT"/sbin/upgraded /tmp/upgraded
+-	nand_upgrade_stage1
+-}
+diff --git a/package/base-files/files/lib/upgrade/stage2 b/package/base-files/files/lib/upgrade/stage2
+new file mode 100755
+index 0000000000000000000000000000000000000000..4e2aa3a23c3bab07a795762a30a4d4f701081934
+--- /dev/null
++++ b/package/base-files/files/lib/upgrade/stage2
+@@ -0,0 +1,50 @@
++#!/bin/sh
++
++. /lib/functions.sh
++. /lib/functions/system.sh
++
++export IMAGE="$1"
++COMMAND="$2"
++
++export ARGV="$IMAGE"
++export ARGC=1
++
++export SAVE_CONFIG=1
++export SAVE_PARTITIONS=1
++
++export INTERACTIVE=0
++export VERBOSE=1
++export CONFFILES=/tmp/sysupgrade.conffiles
++export CONF_TAR=/tmp/sysupgrade.tgz
++
++
++[ -f "$CONF_TAR" ] || export SAVE_CONFIG=0
++[ -f /tmp/sysupgrade.always.overwrite.bootdisk.partmap ] && export SAVE_PARTITIONS=0
++
++include /lib/upgrade
++
++
++killall -9 telnetd
++killall -9 dropbear
++killall -9 ash
++
++kill_remaining TERM
++sleep 3
++kill_remaining KILL 1
++
++sleep 1
++
++
++if [ -n "$IMAGE" ] && type 'platform_pre_upgrade' >/dev/null 2>/dev/null; then
++	IS_PRE_UPGRADE=1 platform_pre_upgrade "$IMAGE"
++
++	# Needs to be unset again because of busybox weirdness ...
++	IS_PRE_UPGRADE=
++fi
++
++if [ -n "$(rootfs_type)" ]; then
++	echo "Switching to ramdisk..."
++	run_ramfs "$COMMAND"
++else
++	exec /bin/busybox ash -c "$COMMAND"
++fi
+diff --git a/package/base-files/files/sbin/sysupgrade b/package/base-files/files/sbin/sysupgrade
+index 759c841e131a415c8009995c372cce1f55fb78a0..d48416f6de244b97f426ae0b83d425ac5c1bc5b3 100755
+--- a/package/base-files/files/sbin/sysupgrade
++++ b/package/base-files/files/sbin/sysupgrade
+@@ -1,4 +1,7 @@
+ #!/bin/sh
++
++[ "$1" = "nand" ] && exec /lib/upgrade/stage2 "$2" "$3"
++
+ . /lib/functions.sh
+ . /lib/functions/system.sh
+ 
+@@ -11,7 +14,6 @@ export VERBOSE=1
+ export SAVE_CONFIG=1
+ export SAVE_OVERLAY=0
+ export SAVE_PARTITIONS=1
+-export DELAY=
+ export CONF_IMAGE=
+ export CONF_BACKUP_LIST=0
+ export CONF_BACKUP=
+@@ -25,7 +27,6 @@ export TEST=0
+ while [ -n "$1" ]; do
+ 	case "$1" in
+ 		-i) export INTERACTIVE=1;;
+-		-d) export DELAY="$2"; shift;;
+ 		-v) export VERBOSE="$(($VERBOSE + 1))";;
+ 		-q) export VERBOSE="$(($VERBOSE - 1))";;
+ 		-n) export SAVE_CONFIG=0;;
+@@ -50,10 +51,9 @@ done
+ export CONFFILES=/tmp/sysupgrade.conffiles
+ export CONF_TAR=/tmp/sysupgrade.tgz
+ 
+-export ARGV="$*"
+-export ARGC="$#"
++IMAGE="$1"
+ 
+-[ -z "$ARGV" -a -z "$NEED_IMAGE" -o $HELP -gt 0 ] && {
++[ -z "$IMAGE" -a -z "$NEED_IMAGE" -o $HELP -gt 0 ] && {
+ 	cat <<EOF
+ Usage: $0 [<upgrade-option>...] <image file or URL>
+        $0 [-q] [-i] <backup-command> <file>
+@@ -90,7 +90,7 @@ EOF
+ 	exit 1
+ }
+ 
+-[ -n "$ARGV" -a -n "$NEED_IMAGE" ] && {
++[ -n "$IMAGE" -a -n "$NEED_IMAGE" ] && {
+ 	cat <<-EOF
+ 		-b|--create-backup and -r|--restore-backup do not perform a firmware upgrade.
+ 		Do not specify both -b|-r and a firmware image.
+@@ -134,14 +134,13 @@ sysupgrade_image_check="platform_check_image"
+ 
+ include /lib/upgrade
+ 
+-[ "$1" = "nand" ] && nand_upgrade_stage2 $@
+-
+ do_save_conffiles() {
+ 	local conf_tar="${1:-$CONF_TAR}"
+ 
+ 	[ -z "$(rootfs_type)" ] && {
+ 		echo "Cannot save config while running from ramdisk."
+ 		ask_bool 0 "Abort" && exit
++		rm -f "$conf_tar"
+ 		return 0
+ 	}
+ 	run_hooks "$CONFFILES" $sysupgrade_init_conffiles
+@@ -182,8 +181,33 @@ type platform_check_image >/dev/null 2>/dev/null || {
+ 	exit 1
+ }
+ 
++case "$IMAGE" in
++	http://*)
++		wget -O/tmp/sysupgrade.img "$IMAGE"
++		IMAGE=/tmp/sysupgrade.img
++		;;
++esac
++
++IMAGE="$(readlink -f "$IMAGE")"
++
++case "$IMAGE" in
++	'')
++		echo "Image file not found."
++		exit 1
++		;;
++	/tmp/*)	;;
++	*)
++		v "Image not in /tmp, copying..."
++		cp -f "$IMAGE" /tmp/sysupgrade.img
++		IMAGE=/tmp/sysupgrade.img
++		;;
++esac
++
++export ARGV="$IMAGE"
++export ARGC=1
++
+ for check in $sysupgrade_image_check; do
+-	( eval "$check \"\$ARGV\"" ) || {
++	( $check "$IMAGE" ) || {
+ 		if [ $FORCE -eq 1 ]; then
+ 			echo "Image check '$check' failed but --force given - will update anyway!"
+ 			break
+@@ -209,6 +233,7 @@ elif ask_bool $SAVE_CONFIG "Keep config files over reflash"; then
+ 	[ $TEST -eq 1 ] || do_save_conffiles
+ 	export SAVE_CONFIG=1
+ else
++	[ $TEST -eq 1 ] || rm -f "$CONF_TAR"
+ 	export SAVE_CONFIG=0
+ fi
+ 
+@@ -216,28 +241,18 @@ if [ $TEST -eq 1 ]; then
+ 	exit 0
+ fi
+ 
+-run_hooks "" $sysupgrade_pre_upgrade
+-
+-# Some platforms/devices may want different sysupgrade process, e.g. without
+-# killing processes yet or calling ubus system upgrade method.
+-# This is needed e.g. on NAND devices where we just want to trigger stage1 at
+-# this point.
+-if type 'platform_pre_upgrade' >/dev/null 2>/dev/null; then
+-	platform_pre_upgrade "$ARGV"
++if [ $SAVE_PARTITIONS -eq 0 ]; then
++	touch /tmp/sysupgrade.always.overwrite.bootdisk.partmap
++else
++	rm -f /tmp/sysupgrade.always.overwrite.bootdisk.partmap
+ fi
+ 
+-ubus call system upgrade
+-touch /tmp/sysupgrade
+-
+-if [ ! -f /tmp/failsafe ] ; then
+-	kill_remaining TERM
+-	sleep 3
+-	kill_remaining KILL
+-fi
++run_hooks "" $sysupgrade_pre_upgrade
+ 
+-if [ -n "$(rootfs_type)" ]; then
+-	v "Switching to ramdisk..."
+-	run_ramfs '. /lib/functions.sh; include /lib/upgrade; do_upgrade'
+-else
+-	do_upgrade
+-fi
++install_bin /sbin/upgraded
++v "Commencing upgrade. All shell sessions will be closed now."
++ubus call system sysupgrade "{
++	\"prefix\": \"$RAM_ROOT\",
++	\"path\": $(json_string "$IMAGE"),
++	\"command\": \". /lib/functions.sh; include /lib/upgrade; do_upgrade_stage2\"
++}"
diff --git a/patches/openwrt/0109-fstools-clean-up-trailing-whitespace-in-snapshot-script.patch b/patches/openwrt/0109-fstools-clean-up-trailing-whitespace-in-snapshot-script.patch
new file mode 100644
index 0000000000000000000000000000000000000000..2011b289cc7c0a9d3f4b8e177a851ca9c301553e
--- /dev/null
+++ b/patches/openwrt/0109-fstools-clean-up-trailing-whitespace-in-snapshot-script.patch
@@ -0,0 +1,19 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 3 May 2017 08:29:24 +0200
+Subject: fstools: clean up trailing whitespace in snapshot script
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/system/fstools/files/snapshot b/package/system/fstools/files/snapshot
+index a0e0ec0c334ca72086c3e30c62ae988c61ff1bde..eec7214032d1b147d5e989cd305c5c0cd1357a55 100644
+--- a/package/system/fstools/files/snapshot
++++ b/package/system/fstools/files/snapshot
+@@ -42,7 +42,7 @@ do_snapshot_upgrade() {
+ 
+ 	opkg list-upgradable
+ 	[ $? -eq 0 ] || exit 2
+-	
++
+ 	UPDATES=`opkg list-upgradable | cut -d" " -f1`
+ 	[ -z "${UPDATES}" ] && exit 0
+ 
diff --git a/patches/openwrt/0110-fstools-snapshot-handle-jffs2-conversion-using-upgraded.patch b/patches/openwrt/0110-fstools-snapshot-handle-jffs2-conversion-using-upgraded.patch
new file mode 100644
index 0000000000000000000000000000000000000000..812733ec061b46a9c733bed96d3592f82540c5f8
--- /dev/null
+++ b/patches/openwrt/0110-fstools-snapshot-handle-jffs2-conversion-using-upgraded.patch
@@ -0,0 +1,36 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 3 May 2017 08:29:55 +0200
+Subject: fstools: snapshot: handle jffs2 conversion using upgraded
+
+We can reuse the kill_remaining and run_ramfs facilities of the stage2 run
+by upgraded.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/system/fstools/files/snapshot b/package/system/fstools/files/snapshot
+index eec7214032d1b147d5e989cd305c5c0cd1357a55..79be0d24bb68cc9b4ea8ce5b5d6b7558b2f0b0c5 100644
+--- a/package/system/fstools/files/snapshot
++++ b/package/system/fstools/files/snapshot
+@@ -64,14 +64,16 @@ do_convert_jffs2() {
+ do_convert() {
+ 	. /lib/functions.sh
+ 	. /lib/upgrade/common.sh
+-	ubus call system upgrade
+-	touch /tmp/sysupgrade
++
+ 	cd /overlay
+ 	tar czf /tmp/snapshot.tar.gz *
+-	kill_remaining TERM
+-	sleep 3
+-	kill_remaining KILL
+-	run_ramfs '. /sbin/snapshot; do_convert_jffs2'
++
++	install_bin /sbin/upgraded
++	ubus call system sysupgrade "{
++		\"prefix\": \"$RAM_ROOT\",
++		\"path\": \"\",
++		\"command\": \". /sbin/snapshot; do_convert_jffs2\"
++	}"
+ }
+ 
+ [ -n "$(cat /proc/mounts|grep /overlay|grep jffs2)" ] && {
diff --git a/patches/openwrt/0111-base-files-sysupgrade-cleanup.patch b/patches/openwrt/0111-base-files-sysupgrade-cleanup.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5a7e5044c7aae1913c6872bcbc42093259bec073
--- /dev/null
+++ b/patches/openwrt/0111-base-files-sysupgrade-cleanup.patch
@@ -0,0 +1,255 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Sun, 23 Apr 2017 23:33:14 +0200
+Subject: base-files: sysupgrade cleanup
+
+Some functions only used by stage2 are moved there from common.sh.
+
+One piece that could still use more cleanup is platform_pre_upgrade: many
+targets reference files from there are aren't available in the ramfs, so
+we need to evaluate it before the switch; conversely, flash writes happen
+in that function on some targets. Targets that do the latter should be
+fixed eventually to use platform_do_upgrade for that purpose.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/base-files/files/lib/upgrade/common.sh b/package/base-files/files/lib/upgrade/common.sh
+index 23f18ceb775caac057ee7a757922954f812f77ce..fc59bf2323498d332159b00eb7ab443bfe6b147e 100644
+--- a/package/base-files/files/lib/upgrade/common.sh
++++ b/package/base-files/files/lib/upgrade/common.sh
+@@ -30,103 +30,6 @@ install_bin() { # <file> [ <symlink> ... ]
+ 	}; done
+ }
+ 
+-supivot() { # <new_root> <old_root>
+-	/bin/mount | grep "on $1 type" 2>&- 1>&- || /bin/mount -o bind $1 $1
+-	mkdir -p $1$2 $1/proc $1/sys $1/dev $1/tmp $1/overlay && \
+-	/bin/mount -o noatime,move /proc $1/proc && \
+-	pivot_root $1 $1$2 || {
+-		/bin/umount -l $1 $1
+-		return 1
+-	}
+-
+-	/bin/mount -o noatime,move $2/sys /sys
+-	/bin/mount -o noatime,move $2/dev /dev
+-	/bin/mount -o noatime,move $2/tmp /tmp
+-	/bin/mount -o noatime,move $2/overlay /overlay 2>&-
+-	return 0
+-}
+-
+-run_ramfs() { # <command> [...]
+-	install_bin /bin/busybox /bin/ash /bin/sh /bin/mount /bin/umount	\
+-		/sbin/pivot_root /usr/bin/wget /sbin/reboot /bin/sync /bin/dd	\
+-		/bin/grep /bin/cp /bin/mv /bin/tar /usr/bin/md5sum "/usr/bin/["	\
+-		/bin/dd /bin/vi /bin/ls /bin/cat /usr/bin/awk /usr/bin/hexdump	\
+-		/bin/sleep /bin/zcat /usr/bin/bzcat /usr/bin/printf /usr/bin/wc \
+-		/bin/cut /usr/bin/printf /bin/sync /bin/mkdir /bin/rmdir	\
+-		/bin/rm /usr/bin/basename /bin/kill /bin/chmod /usr/bin/find
+-
+-	install_bin /sbin/mtd
+-	install_bin /sbin/mount_root
+-	install_bin /sbin/snapshot
+-	install_bin /sbin/snapshot_tool
+-	install_bin /usr/sbin/ubiupdatevol
+-	install_bin /usr/sbin/ubiattach
+-	install_bin /usr/sbin/ubiblock
+-	install_bin /usr/sbin/ubiformat
+-	install_bin /usr/sbin/ubidetach
+-	install_bin /usr/sbin/ubirsvol
+-	install_bin /usr/sbin/ubirmvol
+-	install_bin /usr/sbin/ubimkvol
+-	install_bin /usr/sbin/partx
+-	for file in $RAMFS_COPY_BIN; do
+-		install_bin ${file//:/ }
+-	done
+-	install_file /etc/resolv.conf /lib/*.sh /lib/functions/*.sh /lib/upgrade/*.sh $RAMFS_COPY_DATA
+-
+-	[ -L "/lib64" ] && ln -s /lib $RAM_ROOT/lib64
+-
+-	supivot $RAM_ROOT /mnt || {
+-		echo "Failed to switch over to ramfs. Please reboot."
+-		exit 1
+-	}
+-
+-	/bin/mount -o remount,ro /mnt
+-	/bin/umount -l /mnt
+-
+-	grep /overlay /proc/mounts > /dev/null && {
+-		/bin/mount -o noatime,remount,ro /overlay
+-		/bin/umount -l /overlay
+-	}
+-
+-	# spawn a new shell from ramdisk to reduce the probability of cache issues
+-	exec /bin/busybox ash -c "$*"
+-}
+-
+-kill_remaining() { # [ <signal> [ <loop> ] ]
+-	local sig="${1:-TERM}"
+-	local loop="${2:-0}"
+-	local run=true
+-	local stat
+-
+-	echo -n "Sending $sig to remaining processes ... "
+-
+-	while $run; do
+-		run=false
+-		for stat in /proc/[0-9]*/stat; do
+-			[ -f "$stat" ] || continue
+-
+-			local pid name state ppid rest
+-			read pid name state ppid rest < $stat
+-			name="${name#(}"; name="${name%)}"
+-
+-			# Skip PID1, ourself and our children
+-			[ $pid -ne 1 -a $pid -ne $$ -a $ppid -ne $$ ] || continue
+-
+-			local cmdline
+-			read cmdline < /proc/$pid/cmdline
+-
+-			# Skip kernel threads
+-			[ -n "$cmdline" ] || continue
+-
+-			echo -n "$name "
+-			kill -$sig $pid 2>/dev/null
+-
+-			[ $loop -eq 1 ] && run=true
+-		done
+-	done
+-	echo ""
+-}
+-
+ run_hooks() {
+ 	local arg="$1"; shift
+ 	for func in "$@"; do
+diff --git a/package/base-files/files/lib/upgrade/stage2 b/package/base-files/files/lib/upgrade/stage2
+index 4e2aa3a23c3bab07a795762a30a4d4f701081934..cc8047d988e39ca9ba27d2588744aad469d1d978 100755
+--- a/package/base-files/files/lib/upgrade/stage2
++++ b/package/base-files/files/lib/upgrade/stage2
+@@ -24,6 +24,104 @@ export CONF_TAR=/tmp/sysupgrade.tgz
+ include /lib/upgrade
+ 
+ 
++supivot() { # <new_root> <old_root>
++	/bin/mount | grep "on $1 type" 2>&- 1>&- || /bin/mount -o bind $1 $1
++	mkdir -p $1$2 $1/proc $1/sys $1/dev $1/tmp $1/overlay && \
++	/bin/mount -o noatime,move /proc $1/proc && \
++	pivot_root $1 $1$2 || {
++		/bin/umount -l $1 $1
++		return 1
++	}
++
++	/bin/mount -o noatime,move $2/sys /sys
++	/bin/mount -o noatime,move $2/dev /dev
++	/bin/mount -o noatime,move $2/tmp /tmp
++	/bin/mount -o noatime,move $2/overlay /overlay 2>&-
++	return 0
++}
++
++switch_to_ramfs() {
++	install_bin /bin/busybox /bin/ash /bin/sh /bin/mount /bin/umount	\
++		/sbin/pivot_root /sbin/reboot /bin/sync /bin/dd	/bin/grep       \
++		/bin/cp /bin/mv /bin/tar /usr/bin/md5sum "/usr/bin/[" /bin/dd	\
++		/bin/vi /bin/ls /bin/cat /usr/bin/awk /usr/bin/hexdump		\
++		/bin/sleep /bin/zcat /usr/bin/bzcat /usr/bin/printf /usr/bin/wc \
++		/bin/cut /usr/bin/printf /bin/sync /bin/mkdir /bin/rmdir	\
++		/bin/rm /usr/bin/basename /bin/kill /bin/chmod /usr/bin/find \
++		/bin/mknod
++
++	install_bin /sbin/mtd
++	install_bin /sbin/mount_root
++	install_bin /sbin/snapshot
++	install_bin /sbin/snapshot_tool
++	install_bin /usr/sbin/ubiupdatevol
++	install_bin /usr/sbin/ubiattach
++	install_bin /usr/sbin/ubiblock
++	install_bin /usr/sbin/ubiformat
++	install_bin /usr/sbin/ubidetach
++	install_bin /usr/sbin/ubirsvol
++	install_bin /usr/sbin/ubirmvol
++	install_bin /usr/sbin/ubimkvol
++	install_bin /usr/sbin/partx
++	install_bin /usr/sbin/losetup
++	install_bin /usr/sbin/mkfs.ext4
++	for file in $RAMFS_COPY_BIN; do
++		install_bin ${file//:/ }
++	done
++	install_file /etc/resolv.conf /lib/*.sh /lib/functions/*.sh /lib/upgrade/*.sh $RAMFS_COPY_DATA
++
++	[ -L "/lib64" ] && ln -s /lib $RAM_ROOT/lib64
++
++	supivot $RAM_ROOT /mnt || {
++		echo "Failed to switch over to ramfs. Please reboot."
++		exit 1
++	}
++
++	/bin/mount -o remount,ro /mnt
++	/bin/umount -l /mnt
++
++	grep /overlay /proc/mounts > /dev/null && {
++		/bin/mount -o noatime,remount,ro /overlay
++		/bin/umount -l /overlay
++	}
++}
++
++kill_remaining() { # [ <signal> [ <loop> ] ]
++	local sig="${1:-TERM}"
++	local loop="${2:-0}"
++	local run=true
++	local stat
++
++	echo -n "Sending $sig to remaining processes ... "
++
++	while $run; do
++		run=false
++		for stat in /proc/[0-9]*/stat; do
++			[ -f "$stat" ] || continue
++
++			local pid name state ppid rest
++			read pid name state ppid rest < $stat
++			name="${name#(}"; name="${name%)}"
++
++			# Skip PID1, ourself and our children
++			[ $pid -ne 1 -a $pid -ne $$ -a $ppid -ne $$ ] || continue
++
++			local cmdline
++			read cmdline < /proc/$pid/cmdline
++
++			# Skip kernel threads
++			[ -n "$cmdline" ] || continue
++
++			echo -n "$name "
++			kill -$sig $pid 2>/dev/null
++
++			[ $loop -eq 1 ] && run=true
++		done
++	done
++	echo ""
++}
++
++
+ killall -9 telnetd
+ killall -9 dropbear
+ killall -9 ash
+@@ -44,7 +142,8 @@ fi
+ 
+ if [ -n "$(rootfs_type)" ]; then
+ 	echo "Switching to ramdisk..."
+-	run_ramfs "$COMMAND"
+-else
+-	exec /bin/busybox ash -c "$COMMAND"
++	switch_to_ramfs
+ fi
++
++# Exec new shell from ramfs
++exec /bin/busybox ash -c "$COMMAND"
+diff --git a/package/base-files/files/sbin/sysupgrade b/package/base-files/files/sbin/sysupgrade
+index d48416f6de244b97f426ae0b83d425ac5c1bc5b3..2060679e61948e4661a408df009ff3ef3c6faa91 100755
+--- a/package/base-files/files/sbin/sysupgrade
++++ b/package/base-files/files/sbin/sysupgrade
+@@ -1,7 +1,5 @@
+ #!/bin/sh
+ 
+-[ "$1" = "nand" ] && exec /lib/upgrade/stage2 "$2" "$3"
+-
+ . /lib/functions.sh
+ . /lib/functions/system.sh
+ 
diff --git a/patches/openwrt/0112-base-files-add-support-for-staged-sysupgrades-from-failsafe-mode.patch b/patches/openwrt/0112-base-files-add-support-for-staged-sysupgrades-from-failsafe-mode.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7654b066751c29325483100d1348fbb7c69fe1f4
--- /dev/null
+++ b/patches/openwrt/0112-base-files-add-support-for-staged-sysupgrades-from-failsafe-mode.patch
@@ -0,0 +1,71 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Mon, 24 Apr 2017 01:31:04 +0200
+Subject: base-files: add support for staged sysupgrades from failsafe mode
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/package/base-files/files/lib/preinit/40_run_failsafe_hook b/package/base-files/files/lib/preinit/40_run_failsafe_hook
+index cb43ad39cbae07c533af63d7ff445d41a5b988df..fee0c8f0e19f2bf46771cd23ceaaff2db7af7611 100644
+--- a/package/base-files/files/lib/preinit/40_run_failsafe_hook
++++ b/package/base-files/files/lib/preinit/40_run_failsafe_hook
+@@ -4,8 +4,12 @@
+ 
+ run_failsafe_hook() {
+     if [ "$FAILSAFE" = "true" ]; then
++	lock /tmp/.failsafe
+ 	boot_run_hook failsafe
+-	lock -w /tmp/.failsafe
++	while [ ! -e /tmp/sysupgrade ]; do
++	    lock -w /tmp/.failsafe
++	done
++	exit
+     fi
+ }
+ 
+diff --git a/package/base-files/files/lib/preinit/99_10_failsafe_login b/package/base-files/files/lib/preinit/99_10_failsafe_login
+index 15dcbd884f1f2ec4fd84c92482a9b656d1a26dd7..96e95b1d99c631536c0b30ee5c2e19b3c63bbc64 100644
+--- a/package/base-files/files/lib/preinit/99_10_failsafe_login
++++ b/package/base-files/files/lib/preinit/99_10_failsafe_login
+@@ -7,9 +7,13 @@ failsafe_netlogin () {
+ }
+ 
+ failsafe_shell() {
+-	lock /tmp/.failsafe
+-	ash --login
+-	echo "Please reboot system when done with failsafe network logins"
++	local console="$(sed -e 's/ /\n/g' /proc/cmdline | grep '^console=' | head -1 | sed -e 's/^console=//' -e 's/,.*//')"
++	[ -n "$console" ] || console=console
++	[ -c "/dev/$console" ] || return 0
++	while true; do
++		ash --login <"/dev/$console" >"/dev/$console" 2>"/dev/$console"
++		sleep 1
++	done &
+ }
+ 
+ boot_hook_add failsafe failsafe_netlogin
+diff --git a/package/base-files/files/sbin/sysupgrade b/package/base-files/files/sbin/sysupgrade
+index 2060679e61948e4661a408df009ff3ef3c6faa91..853134a2cb8a2ef1d56671bfeee46704892909d8 100755
+--- a/package/base-files/files/sbin/sysupgrade
++++ b/package/base-files/files/sbin/sysupgrade
+@@ -249,8 +249,16 @@ run_hooks "" $sysupgrade_pre_upgrade
+ 
+ install_bin /sbin/upgraded
+ v "Commencing upgrade. All shell sessions will be closed now."
+-ubus call system sysupgrade "{
+-	\"prefix\": \"$RAM_ROOT\",
+-	\"path\": $(json_string "$IMAGE"),
+-	\"command\": \". /lib/functions.sh; include /lib/upgrade; do_upgrade_stage2\"
+-}"
++
++COMMAND='. /lib/functions.sh; include /lib/upgrade; do_upgrade_stage2'
++
++if [ -n "$FAILSAFE" ]; then
++	printf '%s\x00%s\x00%s' "$RAM_ROOT" "$IMAGE" "$COMMAND" >/tmp/sysupgrade
++	lock -u /tmp/.failsafe
++else
++	ubus call system sysupgrade "{
++		\"prefix\": $(json_string "$RAM_ROOT"),
++		\"path\": $(json_string "$IMAGE"),
++		\"command\": $(json_string "$COMMAND")
++	}"
++fi
diff --git a/patches/openwrt/0113-x86-sysupgrade-move-partition-table-change-check-to-platform_check_image.patch b/patches/openwrt/0113-x86-sysupgrade-move-partition-table-change-check-to-platform_check_image.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8da5a5d2c2d2e9c393a931ebad851ca582e82bc1
--- /dev/null
+++ b/patches/openwrt/0113-x86-sysupgrade-move-partition-table-change-check-to-platform_check_image.patch
@@ -0,0 +1,62 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 3 May 2017 08:57:29 +0200
+Subject: x86: sysupgrade: move partition table change check to platform_check_image
+
+The staged sysupgrade will prevent us from using ask_bool in
+platform_do_upgrade; therefore, the check is moved to platform_check_image.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/target/linux/x86/base-files/lib/upgrade/platform.sh b/target/linux/x86/base-files/lib/upgrade/platform.sh
+index d3e9f360aadedad0995da55205364940c9884ba4..81b349a81816033eef9df464b2a70fdb998e5a1d 100644
+--- a/target/linux/x86/base-files/lib/upgrade/platform.sh
++++ b/target/linux/x86/base-files/lib/upgrade/platform.sh
+@@ -1,13 +1,37 @@
+ platform_check_image() {
++	local diskdev partdev diff
+ 	[ "$#" -gt 1 ] && return 1
+ 
+ 	case "$(get_magic_word "$1")" in
+-		eb48|eb63) return 0;;
++		eb48|eb63) ;;
+ 		*)
+ 			echo "Invalid image type"
+ 			return 1
+ 		;;
+ 	esac
++
++	export_bootdevice && export_partdevice diskdev 0 || {
++		echo "Unable to determine upgrade device"
++		return 1
++	}
++
++	get_partitions "/dev/$diskdev" bootdisk
++
++	#extract the boot sector from the image
++	get_image "$@" | dd of=/tmp/image.bs count=1 bs=512b 2>/dev/null
++
++	get_partitions /tmp/image.bs image
++
++	#compare tables
++	diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)"
++
++	rm -f /tmp/image.bs /tmp/partmap.bootdisk /tmp/partmap.image
++
++	if [ -n "$diff" ]; then
++		echo "Partition layout has changed. Full image will be written."
++		ask_bool 0 "Abort" && exit 1
++		return 0
++	fi
+ }
+ 
+ platform_copy_config() {
+@@ -36,9 +60,6 @@ platform_do_upgrade() {
+ 			#compare tables
+ 			diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)"
+ 			if [ -n "$diff" ]; then
+-				echo "Partition layout is changed.  Full image will be written."
+-				ask_bool 0 "Abort" && exit
+-
+ 				get_image "$@" | dd of="/dev/$diskdev" bs=4096 conv=fsync
+ 				return 0
+ 			fi
diff --git a/patches/openwrt/0114-x86-sysupgrade-refactor-platform_do_upgrade.patch b/patches/openwrt/0114-x86-sysupgrade-refactor-platform_do_upgrade.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a70f3ad481a9f44e65be8a72b9918949a5703281
--- /dev/null
+++ b/patches/openwrt/0114-x86-sysupgrade-refactor-platform_do_upgrade.patch
@@ -0,0 +1,90 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 3 May 2017 09:05:25 +0200
+Subject: x86: sysupgrade: refactor platform_do_upgrade
+
+By returning early when no upgrade device can be found and handling the
+SAVE_PARTITIONS=0 case differently, we can get rid of two levels of if.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/target/linux/x86/base-files/lib/upgrade/platform.sh b/target/linux/x86/base-files/lib/upgrade/platform.sh
+index 81b349a81816033eef9df464b2a70fdb998e5a1d..4fa71999be7be3972676a1019488972dccd57fa2 100644
+--- a/target/linux/x86/base-files/lib/upgrade/platform.sh
++++ b/target/linux/x86/base-files/lib/upgrade/platform.sh
+@@ -47,40 +47,43 @@ platform_copy_config() {
+ platform_do_upgrade() {
+ 	local diskdev partdev diff
+ 
+-	if export_bootdevice && export_partdevice diskdev 0; then
+-		sync
+-		if [ "$SAVE_PARTITIONS" = "1" ]; then
+-			get_partitions "/dev/$diskdev" bootdisk
+-
+-			#extract the boot sector from the image
+-			get_image "$@" | dd of=/tmp/image.bs count=1 bs=512b
+-
+-			get_partitions /tmp/image.bs image
+-
+-			#compare tables
+-			diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)"
+-			if [ -n "$diff" ]; then
+-				get_image "$@" | dd of="/dev/$diskdev" bs=4096 conv=fsync
+-				return 0
+-			fi
+-
+-			#iterate over each partition from the image and write it to the boot disk
+-			while read part start size; do
+-				if export_partdevice partdev $part; then
+-					echo "Writing image to /dev/$partdev..."
+-					get_image "$@" | dd of="/dev/$partdev" ibs="512" obs=1M skip="$start" count="$size" conv=fsync
+-				else
+-					echo "Unable to find partition $part device, skipped."
+-				fi
+-			done < /tmp/partmap.image
+-
+-			#copy partition uuid
+-			echo "Writing new UUID to /dev/$diskdev..."
+-			get_image "$@" | dd of="/dev/$diskdev" bs=1 skip=440 count=4 seek=440 conv=fsync
++	export_bootdevice && export_partdevice diskdev 0 || {
++		echo "Unable to determine upgrade device"
++		return 1
++	}
++
++	sync
++
++	if [ "$SAVE_PARTITIONS" = "1" ]; then
++		get_partitions "/dev/$diskdev" bootdisk
++
++		#extract the boot sector from the image
++		get_image "$@" | dd of=/tmp/image.bs count=1 bs=512b
++
++		get_partitions /tmp/image.bs image
++
++		#compare tables
++		diff="$(grep -F -x -v -f /tmp/partmap.bootdisk /tmp/partmap.image)"
++	else
++		diff=1
++	fi
++
++	if [ -n "$diff" ]; then
++		get_image "$@" | dd of="/dev/$diskdev" bs=4096 conv=fsync
++		return 0
++	fi
++
++	#iterate over each partition from the image and write it to the boot disk
++	while read part start size; do
++		if export_partdevice partdev $part; then
++			echo "Writing image to /dev/$partdev..."
++			get_image "$@" | dd of="/dev/$partdev" ibs="512" obs=1M skip="$start" count="$size" conv=fsync
+ 		else
+-			get_image "$@" | dd of="/dev/$diskdev" bs=4096 conv=fsync
++			echo "Unable to find partition $part device, skipped."
+ 		fi
++	done < /tmp/partmap.image
+ 
+-		sleep 1
+-	fi
++	#copy partition uuid
++	echo "Writing new UUID to /dev/$diskdev..."
++	get_image "$@" | dd of="/dev/$diskdev" bs=1 skip=440 count=4 seek=440 conv=fsync
+ }
diff --git a/patches/openwrt/0115-x86-sysupgrade-explicitly-rescan-disk-after-writing-partition-table.patch b/patches/openwrt/0115-x86-sysupgrade-explicitly-rescan-disk-after-writing-partition-table.patch
new file mode 100644
index 0000000000000000000000000000000000000000..4e46ce60e65bec91e9eb070f429129649b1abef5
--- /dev/null
+++ b/patches/openwrt/0115-x86-sysupgrade-explicitly-rescan-disk-after-writing-partition-table.patch
@@ -0,0 +1,26 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 3 May 2017 09:08:29 +0200
+Subject: x86: sysupgrade: explicitly rescan disk after writing partition table
+
+This should ensure that the kernel partition can be mounted in
+platform_copy_config when its size has changed.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/target/linux/x86/base-files/lib/upgrade/platform.sh b/target/linux/x86/base-files/lib/upgrade/platform.sh
+index 4fa71999be7be3972676a1019488972dccd57fa2..439ba8f5125d97932248ff966340165a84e1b24a 100644
+--- a/target/linux/x86/base-files/lib/upgrade/platform.sh
++++ b/target/linux/x86/base-files/lib/upgrade/platform.sh
+@@ -70,6 +70,12 @@ platform_do_upgrade() {
+ 
+ 	if [ -n "$diff" ]; then
+ 		get_image "$@" | dd of="/dev/$diskdev" bs=4096 conv=fsync
++
++		# Separate removal and addtion is necessary; otherwise, partition 1
++		# will be missing if it overlaps with the old partition 2
++		partx -d - "/dev/$diskdev"
++		partx -a - "/dev/$diskdev"
++
+ 		return 0
+ 	fi
+ 
diff --git a/patches/openwrt/0116-mvebu-fix-sysupgrade.patch b/patches/openwrt/0116-mvebu-fix-sysupgrade.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7a345beff112d56d4ba2ab68dfdba86924fe46b8
--- /dev/null
+++ b/patches/openwrt/0116-mvebu-fix-sysupgrade.patch
@@ -0,0 +1,85 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Thu, 1 Jun 2017 18:39:12 +0200
+Subject: mvebu: fix sysupgrade
+
+mvebu was modifying RAMFS_COPY_BIN and RAMFS_COPY_DATA from a
+sysupgrade_pre_upgrade hook. As the ramfs is created from stage2, this
+did not have an effect anymore after the staged sysupgrade changes.
+
+As it doesn't really hurt to copy fw_printenv and fw_setenv
+unconditionally, simply add them in /lib/upgrade/platform.sh, so stage2
+will see them.
+
+Config copying is moved to a function called by platform_copy_config, where
+it belongs.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+Fixes: FS#821
+Fixes: 30f61a34b4cf "base-files: always use staged sysupgrade"
+
+diff --git a/package/base-files/files/lib/upgrade/stage2 b/package/base-files/files/lib/upgrade/stage2
+index cc8047d988e39ca9ba27d2588744aad469d1d978..bdbb8926643287f48a4ae62c5d1d4b4a29130859 100755
+--- a/package/base-files/files/lib/upgrade/stage2
++++ b/package/base-files/files/lib/upgrade/stage2
+@@ -48,7 +48,7 @@ switch_to_ramfs() {
+ 		/bin/sleep /bin/zcat /usr/bin/bzcat /usr/bin/printf /usr/bin/wc \
+ 		/bin/cut /usr/bin/printf /bin/sync /bin/mkdir /bin/rmdir	\
+ 		/bin/rm /usr/bin/basename /bin/kill /bin/chmod /usr/bin/find \
+-		/bin/mknod
++		/bin/mknod /bin/touch
+ 
+ 	install_bin /sbin/mtd
+ 	install_bin /sbin/mount_root
+diff --git a/target/linux/mvebu/base-files/lib/upgrade/linksys.sh b/target/linux/mvebu/base-files/lib/upgrade/linksys.sh
+index fc403332bd38fc521a056ab85783abd90629a1cf..63d1cd14a4deed407b217a518ae25a752f62969e 100644
+--- a/target/linux/mvebu/base-files/lib/upgrade/linksys.sh
++++ b/target/linux/mvebu/base-files/lib/upgrade/linksys.sh
+@@ -73,14 +73,7 @@ platform_do_upgrade_linksys() {
+ 	}
+ }
+ 
+-linksys_preupgrade() {
+-	export RAMFS_COPY_BIN="${RAMFS_COPY_BIN} /usr/sbin/fw_printenv /usr/sbin/fw_setenv"
+-	export RAMFS_COPY_BIN="${RAMFS_COPY_BIN} /bin/mkdir /bin/touch"
+-	export RAMFS_COPY_DATA="${RAMFS_COPY_DATA} /etc/fw_env.config /var/lock/fw_printenv.lock"
+-
+-	[ -f /tmp/sysupgrade.tgz ] && {
+-		cp /tmp/sysupgrade.tgz /tmp/syscfg/sysupgrade.tgz
+-	}
++platform_copy_config_linksys() {
++	cp -f /tmp/sysupgrade.tgz /tmp/syscfg/sysupgrade.tgz
++	sync
+ }
+-
+-append sysupgrade_pre_upgrade linksys_preupgrade
+diff --git a/target/linux/mvebu/base-files/lib/upgrade/platform.sh b/target/linux/mvebu/base-files/lib/upgrade/platform.sh
+index 46b84435977b83d002ae766fe37c6459a55177ff..7465f7be7649f778ada62cf90aa94b16c11c7e34 100755
+--- a/target/linux/mvebu/base-files/lib/upgrade/platform.sh
++++ b/target/linux/mvebu/base-files/lib/upgrade/platform.sh
+@@ -4,7 +4,8 @@
+ 
+ . /lib/mvebu.sh
+ 
+-RAMFS_COPY_DATA=/lib/mvebu.sh
++RAMFS_COPY_BIN='/usr/sbin/fw_printenv /usr/sbin/fw_setenv'
++RAMFS_COPY_DATA='/lib/mvebu.sh /etc/fw_env.config /var/lock/fw_printenv.lock'
+ 
+ platform_check_image() {
+ 	local board=$(mvebu_board_name)
+@@ -39,6 +40,16 @@ platform_do_upgrade() {
+ 	esac
+ }
+ 
++platform_copy_config() {
++	local board=$(mvebu_board_name)
++
++	case "$board" in
++	armada-385-linksys-caiman|armada-385-linksys-cobra|armada-385-linksys-rango|armada-385-linksys-shelby|armada-xp-linksys-mamba)
++		platform_copy_config_linksys
++		;;
++	esac
++}
++
+ disable_watchdog() {
+ 	killall watchdog
+ 	( ps | grep -v 'grep' | grep '/dev/watchdog' ) && {