aboutsummaryrefslogtreecommitdiff
path: root/package/base-files/files/lib/upgrade/tar.sh
blob: a9d1d559e6f7dde342368b1db6625e5bf7e10680 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# SPDX-License-Identifier: GPL-2.0-or-later OR MIT

# Example usage:
#
# {
#         tar_print_member "date.txt" "It's $(date +"%Y")"
#         tar_print_trailer
# } > test.tar

__tar_print_padding() {
	dd if=/dev/zero bs=1 count=$1 2>/dev/null
}

tar_print_member() {
	local name="$1"
	local content="$2"
	local mtime="${3:-$(date +%s)}"
	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_print_trailer() {
	__tar_print_padding 1024
}