~ltrager/maas-images/add_ib

« back to all changes in this revision

Viewing changes to bin/img2squashfs

  • Committer: Lee Trager
  • Date: 2017-07-14 18:11:02 UTC
  • mfrom: (372.1.4 v3_squashfs_only)
  • Revision ID: lee.trager@canonical.com-20170714181102-uba69ty0lnvsqepd
If the SquashFS config option is set convert root-image.gz to a SquashFS image.

When the SquashFS config option is set and upstream is only providing a
root-image.gz convert the root-image.gz into a SquashFS image. Both images
are included in the stream however MAAS will only use the SquashFS image.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/bin/bash
 
2
 
 
3
VERBOSITY=1
 
4
TEMP_D=""
 
5
VALID_FORMATS=( auto img-tar root-image root-image-gz root-tar squashfs-image
 
6
  dir )
 
7
 
 
8
error() { echo "$@" 1>&2; }
 
9
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
 
10
 
 
11
Usage() {
 
12
    cat <<EOF
 
13
Usage: ${0##*/} [ options ] source-image output-squash-image
 
14
 
 
15
   convert 'source-image' into a squashfs filesystem image.
 
16
 
 
17
   source-image is a file in one of:
 
18
     ${VALID_FORMATS[*]}
 
19
 
 
20
   output-squash-image will be a squashfs image.
 
21
 
 
22
   options:
 
23
    -v | --verbose     increase verbosity
 
24
    -f | --format   F  source-image is of format F. default: auto.
 
25
                       must be one of:
 
26
                         auto: determine based on file and name heuristics
 
27
                         img-tar: tarball of root image (image file named .img)
 
28
                         root-image: filesytem (ext[234] in a file)
 
29
                         root-image-gz: root-image that is compressed with gzip
 
30
                         root-tar: tarball of / (supports .tar.xz .tar.gz)
 
31
                         squashfs-image: a squahsfs image (.squashfs)
 
32
    -O | --owner    O   change ownership of output to O (user:group)
 
33
 
 
34
   Example:
 
35
     ${0##*/} --format=my.tar.gz my.squashfs
 
36
EOF
 
37
}
 
38
 
 
39
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; }
 
40
cleanup() {
 
41
    [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
 
42
}
 
43
 
 
44
ddebug() {
 
45
    local level="$1"; shift;
 
46
    [ "${level}" -gt "${VERBOSITY}" ] && return
 
47
    error "$(date -R):" "$@"
 
48
}
 
49
 
 
50
debug() {
 
51
    local level=${1}; shift;
 
52
    [ "${level}" -gt "${VERBOSITY}" ] && return
 
53
    error "${@}"
 
54
}
 
55
 
 
56
inargs() {
 
57
    #inargs(needle, [haystack])
 
58
    # return true if needle is in haystack
 
59
    local needle="$1" i=""
 
60
    shift
 
61
    for i in "$@"; do
 
62
        [ "$i" = "$needle" ] && return 0
 
63
    done
 
64
    return 1
 
65
}
 
66
 
 
67
get_img_from_tar() {
 
68
    # given 'img' (image.tar.gz), extract it and put the .img file in 'out'
 
69
    local img="$1" out="$2" tempd="$3"
 
70
    local mtmp=$(mktemp -d "${tempd}/img_from_tar.XXXXXX")
 
71
    tar -C "$mtmp" -Sxf "$img" ||
 
72
        { error "failed to extract $img"; return 1; }
 
73
    local f="" found=""
 
74
    for f in "${mtmp}/"*; do
 
75
        [ ! -f "$f" -o "${f%.img}" = "$f" ] && continue
 
76
        [ -z "$found" ] ||
 
77
            { error "multiple .img found in $img"; return 1; }
 
78
        found="$f"
 
79
    done
 
80
    [ -n "$found" ] || { error "no .img in $img"; return 1; }
 
81
    mv "$found" "$out" && rm -Rf "$mtmp"
 
82
}
 
83
 
 
84
determine_format() {
 
85
    local input="$1" fmt="$2" fout=""
 
86
    inargs "$fmt" "${VALID_FORMATS[@]}" || {
 
87
        error "provided '--format=$fmt' not a valid format:" \
 
88
            "${VALID_FORMATS[*]}"
 
89
        return 1;
 
90
    }
 
91
    [ "$fmt" != "auto" ] && _RET="$fmt" && return 0
 
92
    if [ -d "$input" ]; then
 
93
        debug 1 "${input} is a directory";
 
94
        _RET="$dir"
 
95
        return
 
96
    fi
 
97
    fout=$(LANG=C file "$input") ||
 
98
        { error "failed: file $input"; return 1; }
 
99
    case "${fout#$input: }" in
 
100
        "gzip compressed"*)
 
101
            fout=$(zcat "$input" | file -)
 
102
            case "${fout#*: }" in
 
103
                POSIX\ tar*) fmt="img-tar";;
 
104
                *) fmt="root-image-gz";;
 
105
            esac
 
106
            ;;
 
107
        "POSIX tar"*) fmt="img-tar";;
 
108
        *ext[234]\ filesystem*) fmt="root-image";;
 
109
        *[Ss]quashfs*) fmt="squashfs-image";;
 
110
        *)
 
111
            # if the above failed (on trusty a .tar.gz file was reported
 
112
            # as a Minux file system) then try filename based heuristics
 
113
            case "$input" in
 
114
                *-root.t??|*-root.tar|*-root.tar.??) fmt="root-tar";;
 
115
                *.tar.gz|*.tgz|*.tar) fmt="img-tar";;
 
116
                *.gz) fmt="root-image-gz";;
 
117
                *.squashfs) fmt="squashfs-image";;
 
118
                *) 
 
119
                    error "WARN: file '$input' did not match name hueristics"
 
120
                    fmt="root-image";;
 
121
            esac
 
122
            debug 1 "guessing $input is $fmt based on name and 'file' [$fout]";;
 
123
    esac
 
124
    debug 1 "determined format is $fmt"
 
125
    _RET="$fmt"
 
126
}
 
127
 
 
128
get_usable_input() {
 
129
    # given 'input' in format 'fmt' return:
 
130
    #  _RET_fmt: the format of _RET_PATH. either
 
131
    #     fs-image : a mountable filesystem image.
 
132
    #     dir : a directory
 
133
    #  _RET_path: full path to a file in tempd
 
134
    local input="$1" fmt="$2" tempd="$3" cmd=""
 
135
    case "$fmt" in
 
136
        img-tar)
 
137
            _RET_fmt="fs-image"
 
138
            _RET_path="fs-image"
 
139
            get_img_from_tar "$input" "${tempd}/fs-image" "${tempd}"
 
140
            return;;
 
141
        root-image-gz)
 
142
            _RET_fmt="fs-image"
 
143
            _RET_path="$tempd/fs-image"
 
144
            zcat -c "$input" > "$tempd/fs-image.tmp" &&
 
145
                mv "$tempd/fs-image.tmp" "$tempd/fs-image"
 
146
            return;;
 
147
        root-image)
 
148
            _RET_fmt="fs-image"
 
149
            _RET_path="$tempd/fs-image"
 
150
            ln -s "$(readlink -f "$input")" "${tempd}/fs-image"
 
151
            return;;
 
152
        squashfs-image)
 
153
            _RET_fmt="squashfs-image"
 
154
            _RET_PATH="${tempd}/fs-image"
 
155
            ln -s "$(readlink -f "$input")" "${tempd}/fs-image"
 
156
            return;;
 
157
        root-tar)
 
158
            _RET_fmt="dir"
 
159
            _RET_path="${tempd}/fs-dir"
 
160
            mkdir "${_RET_path}"
 
161
            cmd=( tar -C "${_RET_path}" -xpSf "$input" \
 
162
                --numeric-owner --xattrs "--xattrs-include=*" )
 
163
            debug 1 "extracting $fmt with ${cmd[*]}"
 
164
            "${cmd[@]}" || {
 
165
                error "failed: extracting $fmt with ${cmd[*]}"
 
166
                return 1;
 
167
            }
 
168
            return
 
169
            ;;
 
170
        dir)
 
171
            _RET_fmt="dir"
 
172
            _RET_PATH="${tempd}/fs-dir"
 
173
            ln -s "$(readlink -f "$input")" "${tempd}/fs-dir"
 
174
            return;;
 
175
        *)
 
176
            error "Unknown format '$fmt'";
 
177
            return 1;;
 
178
    esac
 
179
}
 
180
 
 
181
 
 
182
dir2squashfs() {
 
183
    local src_d="$1" out="$2" owner="${3:-$(id -u):$(id -g)}"
 
184
    local tmpfile ret="" cmd=""
 
185
    tmpfile="${out}.${0##*/}.$$" || return
 
186
    cmd=( mksquashfs "$src_d" "$tmpfile" -xattrs -comp xz )
 
187
    ddebug 1 "starting: ${cmd[*]}"
 
188
    "${cmd[@]}"
 
189
    ret=$?
 
190
    ddebug 1 "finished: returned $ret"
 
191
    if [ $ret -eq 0 ]; then
 
192
        chown "$owner" "$tmpfile" ||
 
193
            { error "failed chown $owner $tmpfile"; return 1; }
 
194
        mv "$tmpfile" "$out" ||
 
195
            { error "failed to move file to $out"; return 1; }
 
196
    else
 
197
        rm -f "$tmpfile"
 
198
        error "mksquashfs failed [$ret]: ${cmd[*]}."
 
199
    fi
 
200
    return $ret
 
201
}
 
202
 
 
203
fsimage2squashfs() {
 
204
    local src="$1" out="$2" owner="$3"
 
205
    local sudo="sudo"
 
206
    [ "$(id -u )" = "0" ] && sudo=""
 
207
    local cmd=""
 
208
    cmd=( mount-image-callback "$src" --
 
209
        "$0" dir2squashfs _MOUNTPOINT_ "$out" $owner )
 
210
    debug 1 "calling ${cmd[*]}"
 
211
    "${cmd[@]}"
 
212
}
 
213
 
 
214
main() {
 
215
    local short_opts="hf:O:v"
 
216
    local long_opts="help,format:,owner:,verbose"
 
217
    local getopt_out=""
 
218
    getopt_out=$(getopt --name "${0##*/}" \
 
219
        --options "${short_opts}" --long "${long_opts}" -- "$@") &&
 
220
        eval set -- "${getopt_out}" ||
 
221
        { bad_Usage; return; }
 
222
 
 
223
    local cur="" next="" input="" output="" owner="" fmt_in="auto" fmt=""
 
224
    while [ $# -ne 0 ]; do
 
225
        cur="$1"; next="$2";
 
226
        case "$cur" in
 
227
            -h|--help) Usage ; exit 0;;
 
228
            -f|--format) fmt_in="$next"; shift;;
 
229
            -O|--owner) owner="$next"; shift;;
 
230
            -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
 
231
            --) shift; break;;
 
232
        esac
 
233
        shift;
 
234
    done
 
235
 
 
236
    [ $# -eq 2 ] || { bad_Usage "must provide input and output."; return; }
 
237
    src_in="$1"
 
238
    output="$2"
 
239
 
 
240
    TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
 
241
        { error "failed to make tempdir"; return 1; }
 
242
    trap cleanup EXIT
 
243
    local tempd="${TEMP_D}"
 
244
 
 
245
    determine_format "${src_in}" "${fmt_in}" || return
 
246
    local fmt="${_RET}"
 
247
    debug 1 "format of '${src_in}' is '$fmt'"
 
248
 
 
249
    case "$fmt" in
 
250
        dir|squashfs-image|root-tar) :;;
 
251
        *)
 
252
            [ "$(id -u)" = "0" ] || {
 
253
                error "Cannot convert $fmt to squashfs as non-root";
 
254
                return 1;
 
255
            }
 
256
    esac
 
257
    get_usable_input "${src_in}" "$fmt" "$tempd" || return
 
258
    local cfmt="${_RET_fmt}" tpath="${_RET_path}"
 
259
 
 
260
    debug 1 "got format '$cfmt' at temp path '$tpath'"
 
261
    if [ "$cfmt" = "squashfs-image" ]; then
 
262
        local tmpf="$output.$$"
 
263
        cp "$src_in" "$tmpf" &&
 
264
            { [ -z "$owner" ] || chown "$owner" "$tmpf" ; } &&
 
265
            mv "$tmpf" "$output"  || {
 
266
                rm -f "$tmpf"
 
267
                error "failed copying $src_in to $output"
 
268
                return 1
 
269
            }
 
270
    elif [ -f "$tpath" ]; then
 
271
        fsimage2squashfs "$tpath" "$output" "$owner" || return
 
272
    elif [ -d "$tpath" ]; then
 
273
        dir2squashfs "$tpath" "$output" || return
 
274
    else
 
275
        error "failed. path='$tpath' cfmt=${cfmt}"
 
276
        return 1
 
277
    fi
 
278
    error "output in $output. took ${SECONDS}s."
 
279
    return 0
 
280
}
 
281
 
 
282
if [ "$1" = "dir2squashfs" ]; then
 
283
    shift
 
284
    dir2squashfs "$@"
 
285
else
 
286
    main "$@"
 
287
fi
 
288
 
 
289
# vi: ts=4 expandtab