-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunpack
executable file
·150 lines (140 loc) · 4.81 KB
/
unpack
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/bin/bash
# decrypts specified file 'infile' with matching keys from `keysfile`,
# restores decrypted and decompressed directory to its original path,
# now relative to the current dir.
PROGN=${0##*/}
# function to import a whole directory
# takes one argument: outputdir
# reads from file descriptor 3
function importdir {
cd "$1" || {
printf "$PROGN importdir: failed to change to directory $1!\n" >&2
exit 2
}
# create subdirectories
while true; do
read -r -d ',' NAME
[ -z "$NAME" ] && break; # no more subdirs
mkdir "$NAME" || {
printf "$PROGN importdir: failed to create output directory $1/$NAME!\n" >&2
exit 2
}
importdir "$NAME" # recursive descent into subdir
done
# create the files
while true; do
read -r -N 1 CH
[ "$CH" = ',' ] && break; # no more files
read -r -d ',' NAME
read -r -d ',' FSIZE
# u (uncomressed) could be for a listed extension,
# or because all compression attempts have failed to gain
# read from infile (fd 3) and write the file data as is
[ "$CH" = 'u' ] && {
head -c "$FSIZE" <&3 > "$NAME"
continue
}
# apply code specified decompression to a compressed file
head -c $FSIZE <&3 | case "$CH" in # read from piped in stdin
h ) hexify;;
b ) base64 -w 0;;
i ) lzma -d;;
j ) lzma -d | hexify;;
k ) lzma -d | base64 -w 0;;
l ) zstd -d -c;;
m ) zstd -d -c | hexify;;
n ) zstd -d -c | base64 -w 0;;
* ) printf "$PROGN importfile: unrecognised ch\n"; exit 2;;
esac > "$NAME"
done
cd ..
} # end of importdir
function usage { printf "Usage: $PROGN -[h][q][v] indexfile infile keyfile\n" >&2; }
function helpmsg {
usage
printf "\t%s\n" \
'-h | --help : print this text' \
'-q | --quiet : turn off the final summary' \
'-v | --verbose : detailed reporting' >&2
exit 1
}
# Main starts here ###########################################################
# get options, including long options, extracted as arg to the last option '-'
while getopts hqv-: OPT; do
if [ "$OPT" = "-" ]; then # long option: reset OPT and OPTARG
OPT="${OPTARG%%=*}" # extract long option name
OPTARG="${OPTARG#$OPT}" # extract long option argument (here empty)
OPTARG="${OPTARG#=}" # remove any assignments `=` from long options
fi
case "$OPT" in
h | help ) helpmsg;;
q | quiet ) QUIET=0;;
v | verbose ) VERBOSE=0;;
??* ) printf "$PROGN quitting, invalid long option: $OPT\n" >&2; usage; exit 2;;
*) exit 2;; # invalid short option (error will be reported via getopts)
esac
done
shift $((OPTIND-1)) # remove already parsed options and args from $@ list
# Validate number of program arguments
[ "$#" -ne 3 ] && {
printf "$PROGN expected three arguments, "$#" given!\n" >&2
exit 2
}
# Globally validate the index file
[ -e "$1" ] || {
printf "$PROGN: indexfile '$1' not found\n" >&2
exit 2
}
# Globally validate the input file
[ -e "$2" ] || {
printf "$PROGN: inputfile '$2' not found\n" >&2
exit 2
}
# Globally validate the keyfile
[ -e "$3" ] || {
printf "$PROGN: keyfile '$3' not found\n" >&2
exit 2
}
# Warn about missing decompressors
if [ -z $( which zstd ) ]; then
printf "$PROGN warning, zstd compressor is not installed, will not decompress .zst files\n" >&2
fi
if [ -z $( which lzma ) ]; then
printf "$PROGN warning, lzma compressor is not installed, will not decompress .lz files\n" >&2
fi
if [ -z $( which base64 ) ]; then
printf "$PROGN warning, base64 decoder is not installed, will not unpack .b64 files\n" >&2
fi
if [ -z $( which hexify ) ]; then
printf "$PROGN warning, hexify is not installed, will not unpack .hex files\n" >&2
fi
INDEXSIZE=`stat -c%s "$1"` # get size of indexfile
INFILE=_infile.tmp # temporary file for decrypted files data input
# decrypt infile with tail of keyfile
tail -c +$(( "$INDEXSIZE" + 1 )) "$3" | symcrypt "$2" > "$INFILE"
# Open the infile as file descriptor 3
exec 3<"$INFILE" || {
printf "$PROGN: infile file '$INFILE' failed to open for reading\n" >&2
exit 2
}
# decrypt indexfile with head of keyfile, lzma uncompress, and pipe to importdir
head -c $INDEXSIZE "$3" | symcrypt "$1" | lzma -d | {
read -r -d ',' DIRNAME
[ -e "$DIRNAME" ] && {
printf "$PROGN: output directory '$DIRNAME' already exists!\n" >&2
exit 2
}
mkdir "$DIRNAME" || {
printf "$PROGN: failed to create output directory '$DIRNAME'!\n" >&2
exit 2
}
printf "$PROGN: restoring directory '$DIRNAME'\n"
importdir "$DIRNAME"
rm "$INFILE" # clean up
# optional final report
if ! [ $QUIET ]; then
SIZES=( `du -hbs "$DIRNAME" "$3"` ) # array of size,dir,size,dir
printf "$PROGN: ${SIZES[3]} ${SIZES[2]} (%s%%) => ${SIZES[1]} ${SIZES[0]} (100%%)\n" \
$(( 100*${SIZES[2]}/${SIZES[0]} )) # calculate percentage
fi
}