base-files: sysupgrade: add tar.sh with helpers for building archives
[openwrt/staging/mans0n.git] / package / base-files / files / lib / upgrade / tar.sh
1 # SPDX-License-Identifier: GPL-2.0-or-later OR MIT
2
3 # Example usage:
4 #
5 # {
6 # tar_print_member "date.txt" "It's $(date +"%Y")"
7 # tar_print_trailer
8 # } > test.tar
9
10 __tar_print_padding() {
11 dd if=/dev/zero bs=1 count=$1 2>/dev/null
12 }
13
14 tar_print_member() {
15 local name="$1"
16 local content="$2"
17 local mtime="${3:-$(date +%s)}"
18 local mode=644
19 local uid=0
20 local gid=0
21 local size=${#content}
22 local type=0
23 local link=""
24 local username="root"
25 local groupname="root"
26
27 # 100 byte of padding bytes, using 0x01 since the shell does not tolerate null bytes in strings
28 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'
29
30 # validate name (strip leading slash if present)
31 name=${name#/}
32
33 # truncate string header values to their maximum length
34 name=${name:0:100}
35 link=${link:0:100}
36 username=${username:0:32}
37 groupname=${groupname:0:32}
38
39 # construct header part before checksum field
40 local header1="${name}${pad:0:$((100 - ${#name}))}"
41 header1="${header1}$(printf '%07d\1' $mode)"
42 header1="${header1}$(printf '%07o\1' $uid)"
43 header1="${header1}$(printf '%07o\1' $gid)"
44 header1="${header1}$(printf '%011o\1' $size)"
45 header1="${header1}$(printf '%011o\1' $mtime)"
46
47 # construct header part after checksum field
48 local header2="$(printf '%d' $type)"
49 header2="${header2}${link}${pad:0:$((100 - ${#link}))}"
50 header2="${header2}ustar ${pad:0:1}"
51 header2="${header2}${username}${pad:0:$((32 - ${#username}))}"
52 header2="${header2}${groupname}${pad:0:$((32 - ${#groupname}))}"
53
54 # calculate checksum over header fields
55 local checksum=0
56 for byte in $(printf '%s%8s%s' "$header1" "" "$header2" | tr '\1' '\0' | hexdump -ve '1/1 "%u "'); do
57 checksum=$((checksum + byte))
58 done
59
60 # print member header, padded to 512 byte
61 printf '%s%06o\0 %s' "$header1" $checksum "$header2" | tr '\1' '\0'
62 __tar_print_padding 183
63
64 # print content data, padded to multiple of 512 byte
65 printf "%s" "$content"
66 __tar_print_padding $((512 - (size % 512)))
67 }
68
69 tar_print_trailer() {
70 __tar_print_padding 1024
71 }