| # 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 |
| } |