tar.sh 2.1 KB

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