ddconvnotrunc: Implement conv=notrunc support for busybox dd

ddconvnotrunc is a wrapper for dd that implements support for
conv=notrunc if the system dd doesn't support it (e.g. busybox dd).
conv=notrunc is useful for assembling binary images or patching binary
files based on other files.

Install it as /usr/local/bin/dd.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: I651b92956226d0917248f8ecd6e4af758747743d
diff --git a/amboar/obmc-scripts/ddconvnotrunc/ddconvnotrunc b/amboar/obmc-scripts/ddconvnotrunc/ddconvnotrunc
new file mode 100755
index 0000000..12bc4cb
--- /dev/null
+++ b/amboar/obmc-scripts/ddconvnotrunc/ddconvnotrunc
@@ -0,0 +1,130 @@
+#!/bin/sh -x
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright 2020 IBM Corp.
+
+set -eu
+
+ARG_IF=
+ARG_OF=
+ARG_BS=
+ARG_COUNT=
+ARG_SKIP=
+ARG_SEEK=
+ARG_APPEND=
+ARG_CONV_NOTRUNC=
+DD=/bin/dd
+
+for arg in "$@"
+do
+	case "$arg" in
+		if=*)
+			ARG_IF="${arg#*=}"
+			;;
+		of=*)
+			ARG_OF="${arg#*=}"
+			;;
+		bs=*)
+			ARG_BS="${arg#*=}"
+			;;
+		count=*)
+			ARG_COUNT="${arg#*=}"
+			;;
+		skip=*)
+			ARG_SKIP="${arg#*=}"
+			;;
+		seek=*)
+			ARG_SEEK="${arg#*=}"
+			;;
+		conv=notrunc)
+			ARG_CONV_NOTRUNC=1
+			;;
+		*)
+			ARG_APPEND+="${arg#*=}"
+			;;
+	esac
+done
+
+# If conv=notrunc isn't supplied, invoke system dd
+if [ -z "$ARG_CONV_NOTRUNC" ]
+then
+	exec $DD "$@"
+fi
+
+# If conv=notrunc is supplied and system dd supports it, invoke system dd
+if $DD conv=notrunc < /dev/null > /dev/null 2> /dev/null
+then
+	exec $DD "$@"
+fi
+
+# Otherwise, emulate conv=notrunc support
+
+# Examples:
+#
+# Inject foo into bar at offset 100:
+#
+# $ dd if=foo of=bar bs=1 seek=100 conv=notrunc
+if [ "$ARG_IF" = "$ARG_OF" ]
+then
+	>&2 echo "Add support for self-modification"
+	exit 1
+fi
+
+cleanup()
+{
+	rm -f $OF_FILE
+}
+
+filesize()
+{
+	# busybox stat(1) doesn't support --format=%s
+	wc -c "$1" | awk '{print $1}'
+}
+
+trap cleanup EXIT
+
+if [ -z "$ARG_OF" ]
+then
+	DATA_SOURCE=
+else
+	DATA_SOURCE='if='$ARG_OF
+fi
+
+# Intermediate output file
+OF_FILE=$(mktemp)
+
+# Generate the prefix
+if [ -z "$ARG_SEEK" ]
+then
+	PREFIX_COUNT=
+else
+	PREFIX_COUNT='count='$ARG_SEEK
+fi
+
+if [ -z "$ARG_BS" ]
+then
+	PREFIX_BS=
+else
+	PREFIX_BS='bs='$ARG_BS
+fi
+$DD $DATA_SOURCE of="$OF_FILE" $PREFIX_BS $PREFIX_COUNT $ARG_APPEND
+OF_UPDATE_OFF=$(filesize "$OF_FILE")
+
+# Append the source data
+if [ -z "$ARG_IF" ]
+then
+	cat >> "$OF_FILE"
+else
+	cat < "$IF_FILE" >> "$OF_FILE"
+fi
+OF_SUFFIX_OFF=$(filesize "$OF_FILE")
+
+if [ -z "$ARG_OF" ]
+then
+	OF_SUFFIX_OFF=$(($OF_SUFFIX_OFF - $OF_UPDATE_OFF))
+fi
+
+# Append the suffix
+$DD $DATA_SOURCE of="$OF_FILE" bs=1 skip=$OF_SUFFIX_OFF seek=$OF_SUFFIX_OFF $ARG_APPEND
+
+mv "$OF_FILE" "$ARG_OF"