[PATCH V2 1/3] base-files: sysupgrade: add tar.sh with helpers for building archives

Rafał Miłecki zajec5 at gmail.com
Wed Feb 28 00:03:02 PST 2024


From: Jo-Philipp Wich <jo at mein.io>

This allows building uncompressed tar archives from shell scripts (and
compressing them later if needed)

Signed-off-by: Rafał Miłecki <rafal at milecki.pl>
---
V2: Simplify dd in __tar_print_padding (I still think helper is useful)
    Hardcode 0/0/ root/root for now as most likely it'll be enough
    Simplify name validation (leasing slash)
    Reorder some variables

 package/base-files/files/lib/upgrade/tar.sh | 78 +++++++++++++++++++++
 1 file changed, 78 insertions(+)
 create mode 100644 package/base-files/files/lib/upgrade/tar.sh

diff --git a/package/base-files/files/lib/upgrade/tar.sh b/package/base-files/files/lib/upgrade/tar.sh
new file mode 100644
index 0000000000..406d5fd71b
--- /dev/null
+++ b/package/base-files/files/lib/upgrade/tar.sh
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+
+__tar_print_padding() {
+	dd if=/dev/zero bs=$1 count=1 2>/dev/null
+}
+
+__tar_make_member() {
+	local name="$1"
+	local content="$2"
+	local mtime="$3"
+	local mode=644
+	local uid=0
+	local gid=0
+	local size=${#content}
+	local type=0
+	local link=""
+	local username="root"
+	local groupname="root"
+
+	# 100 byte of padding bytes, using 0x01 since the shell does not tolerate null bytes in strings
+	local pad=$'\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1'
+
+	# validate name (strip leading slash if present)
+	name=${name#/}
+
+	# truncate string header values to their maximum length
+	name=${name:0:100}
+	link=${link:0:100}
+	username=${username:0:32}
+	groupname=${groupname:0:32}
+
+	# construct header part before checksum field
+	local header1="${name}${pad:0:$((100 - ${#name}))}"
+	header1="${header1}$(printf '%07d\1' $mode)"
+	header1="${header1}$(printf '%07o\1' $uid)"
+	header1="${header1}$(printf '%07o\1' $gid)"
+	header1="${header1}$(printf '%011o\1' $size)"
+	header1="${header1}$(printf '%011o\1' $mtime)"
+
+	# construct header part after checksum field
+	local header2="$(printf '%d' $type)"
+	header2="${header2}${link}${pad:0:$((100 - ${#link}))}"
+	header2="${header2}ustar  ${pad:0:1}"
+	header2="${header2}${username}${pad:0:$((32 - ${#username}))}"
+	header2="${header2}${groupname}${pad:0:$((32 - ${#groupname}))}"
+
+	# calculate checksum over header fields
+	local checksum=0
+	for byte in $(printf '%s%8s%s' "$header1" "" "$header2" | tr '\1' '\0' | hexdump -ve '1/1 "%u "'); do
+		checksum=$((checksum + byte))
+	done
+
+	# print member header, padded to 512 byte
+	printf '%s%06o\0 %s' "$header1" $checksum "$header2" | tr '\1' '\0'
+	__tar_print_padding 183
+
+	# print content data, padded to multiple of 512 byte
+	printf "%s" "$content"
+	__tar_print_padding $((512 - (size % 512)))
+}
+
+tar_make_member_from_file() {
+	local name="$1"
+
+	__tar_make_member "$name" "$(cat $name)" "$(date +%s -r "$1")"
+}
+
+tar_make_member_inline() {
+	local name="$1"
+	local content="$2"
+	local mtime="${3:-$(date +%s)}"
+
+	__tar_make_member "$name" "$content" "$mtime"
+}
+
+tar_close() {
+	__tar_print_padding 1024
+}
-- 
2.35.3




More information about the openwrt-devel mailing list