Browse Source

Backport staged sysupgrade patches

The staged sysupgrade allows to properly unmount the rootfs before writing
the new partitions. This will fix upgrades losing configuration when
parition sizes change on x86 and similar image types.
Matthias Schiffer 7 years ago
parent
commit
d4a69c0004

+ 28 - 0
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

+ 32 - 0
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
+ }

+ 1116 - 0
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
++

+ 897 - 0
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
+-}

+ 468 - 0
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\"
++}"

+ 19 - 0
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
+ 

+ 36 - 0
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)" ] && {

+ 255 - 0
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
+ 

+ 71 - 0
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

+ 62 - 0
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

+ 90 - 0
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
+ }

+ 26 - 0
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
+ 

+ 85 - 0
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' ) && {