From 4aeeefd261ad372448dfb9584da31f596b2538b4 Mon Sep 17 00:00:00 2001 From: Jeffrey Townsend Date: Fri, 11 Dec 2015 21:34:35 +0000 Subject: [PATCH] Installer SHAR support. --- tools/mkshar | 240 ++++++++++++++++++++++++++++++++++ tools/scripts/sfx.sh.in | 283 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100755 tools/mkshar create mode 100644 tools/scripts/sfx.sh.in diff --git a/tools/mkshar b/tools/mkshar new file mode 100755 index 00000000..138f2755 --- /dev/null +++ b/tools/mkshar @@ -0,0 +1,240 @@ +#!/usr/bin/python + +"""mkshar + +Generate a shar (with a zip file payload) for a Switch Light installer. +""" + +import sys, os, shutil +import subprocess +import hashlib +import optparse + +HELP = """\ +%prog [OPTIONS] SHAR-OUTPUT SFX-FRAGMENT INSTALL-SCRIPT [ARG ...] +""" + +parser = optparse.OptionParser(usage=HELP) +parser.add_option('--blocksize', + type=int, default=512, + help="Set block size for e.g. 'dd'") +parser.add_option('--bzip', + action='store_true', + help="Enable bzip2") +parser.add_option('--lazy', + action='store_true', + help="Enable lazy unpacking") +parser.add_option('--unzip-sfx', + action='store_true', + help="Use 'unzip' to natively handle SFX preamble") +parser.add_option('--unzip-pipe', + action='store_true', + help="Enable 'dd' to handle SFX preamble") +parser.add_option('--unzip-loop', + action='store_true', + help="Enable 'losetup' to handle SFX preamble") +parser.add_option('--unzip-pad', + action='store_true', + help="Special pad options for deficient unzip") +parser.add_option('--fixup-perms', + type=str, + help="Post-unpack shell script to fix permissions") + +opts, args = parser.parse_args() + +ZIP = "/usr/bin/zip" +ZIP_OPTS = ['-v', + '-y', + '-n', '.zip:.loader:.swi:.jar:.gz:.bz:.bz2:.xz',] + +# enable (experimental) bzip2, make sure that +# busybox unzip supports it +# Ha ha, using '-9' not only cranks up the compression, +# but *ignores* the suffix list for 'store-only'. +if opts.bzip: + ZIP_COMPRESS_OPTS = ['-Z', 'bzip2', '-8',] +else: + ZIP_COMPRESS_OPTS = ['-8',] + +shar = args.pop(0) +sfx = args.pop(0) +install = args.pop(0) + +import logging +logging.basicConfig() +logger = logging.getLogger("mkshar") +logger.setLevel(logging.DEBUG) + +logger.info("initializing ZIP") +zipFile = os.path.abspath(shar + ".zip.in") +if os.path.exists(zipFile): + logger.debug("+ /bin/rm %s", zipFile) + os.unlink(zipFile) + +def _addfile(path, *opts): + logger.info("adding file %s with opts %s", + path, " ".join(opts)) + cmd = [ZIP,] + ZIP_OPTS + list(opts) + ['-u', zipFile, path,] + subprocess.check_call(cmd) + +def _addfiles(paths, *opts): + logger.info("adding %d files with opts %s", + len(paths), " ".join(opts)) + cmd = [ZIP,] + ZIP_OPTS + list(opts) + [zipFile,] + paths + subprocess.check_call(cmd) + +def _zipopts(path): + ext = os.path.splitext(path)[1].lower() + if ext in ('.loader', '.zip', '.swi',): + return ['-0',] + else: + return ZIP_COMPRESS_OPTS + +# block to 512 bytes +buf = open(sfx).read() +sz = len(buf) +logger.info("script size is %d bytes", sz) +rem = sz % opts.blocksize +pad = opts.blocksize - rem + +# extra block for house keeping +if opts.unzip_pad: + pad = pad + opts.blocksize + +if pad == 1: + buf += "\n" +elif pad > 1: + pad -= 1 + buf = buf + ('#' * pad) + "\n" + +logger.info("padded script size is %d bytes", len(buf)) + +if opts.unzip_pad: + logger.info("adding SFX pad to beginning as archive item") + fd = open("pad.bin", "w") + fd.write(('#' * (len(buf)-1))) + fd.write('\n') + fd.close() + _addfile("pad.bin", '-0', '-j') + os.unlink("pad.bin") + +_addfile(install, '-0') + +if opts.fixup_perms: + _addfile(opts.fixup_perms, '-0') + +logger.info("adding other payload items") +o = ['-r',] + ZIP_COMPRESS_OPTS +_addfiles(args, *o) + +def _splice(tag, val): + global buf + pat = tag + '=' + p = buf.find(pat) + q = buf.find("\n", p+1) + if p < 0: + raise ValueError("cannot find tag %s" % repr(tag)) + llen = q - p + line = "%s=%s #" % (tag, str(val),) + if len(line) > llen: + raise SystemExit("cannot insert byte count marker") + llen -= len(line) + line = line + ('#' * llen) + buf = buf[:p] + line + buf[q:] + +logger.info("prepping SFX") + +_splice('SFX_BYTES', len(buf)) +_splice('SFX_BLOCKSIZE', opts.blocksize) +_splice('SFX_INSTALL', os.path.split(install)[1]) +_splice('SFX_CHECKSUM', '') +if opts.lazy: + _splice('SFX_LAZY', '1') +else: + _splice('SFX_LAZY', '') +if opts.unzip_sfx: + _splice('SFX_UNZIP', '1') +else: + _splice('SFX_UNZIP', '') +if opts.unzip_pipe: + _splice('SFX_PIPE', '1') +else: + _splice('SFX_PIPE', '') +if opts.unzip_loop: + _splice('SFX_LOOP', '1') +else: + _splice('SFX_LOOP', '') +if opts.unzip_pad: + _splice('SFX_PAD', 'pad.bin') +else: + _splice('SFX_PAD', '') +if opts.fixup_perms: + _splice('SFX_PERMS', opts.fixup_perms) + +# remember the checksum offset +ckStart = buf.find("SFX_CHECKSUM=") +ckEnd = buf.find("\n", ckStart+1) + +logger.info("generating shar") + +if opts.unzip_pad: + + wfd = open(shar, "w+") + rfd = open(zipFile, "r") + shutil.copyfileobj(rfd, wfd) + rfd.close() + + logger.info("capturing first block") + wfd.seek(0, 0) + magic = wfd.read(opts.blocksize) + + logger.info("splicing in SFX") + wfd.seek(0, 0) + wfd.write(buf) + + logger.info("saving first block") + wfd.seek(len(buf)-opts.blocksize, 0) + wfd.write(magic) + + wfd.close() + + # adjust buf to include magic + buf = buf[:-opts.blocksize] + magic + +else: + + wfd = open(shar, "w") + wfd.write(buf) + rfd = open(zipFile, "r") + shutil.copyfileobj(rfd, wfd) + rfd.close() + wfd.close() + + logger.info("adjusting SFX") + cmd = [ZIP,] + ZIP_OPTS + ['-A', shar,] + subprocess.check_call(cmd) + +# compute the actual checksum +ckf = hashlib.md5() +ckf.update(buf[:ckStart]) +ckf.update(buf[ckEnd+1:]) + +rfd = open(shar, "r") +rfd.seek(len(buf), 0) +while True: + buf = rfd.read(10*1024) + if not buf: break + ckf.update(buf) +rfd.close() + +logger.info("adjusting checksum") +wfd = open(shar, "r+") +wfd.seek(ckStart, 0) +wfd.write("SFX_CHECKSUM=%s #" % ckf.hexdigest()) +wfd.close() + +logger.info("+ /bin/chmod +x %s", shar) +os.chmod(shar, 0755) + +logger.debug("+ /bin/rm %s", zipFile) +os.unlink(zipFile) diff --git a/tools/scripts/sfx.sh.in b/tools/scripts/sfx.sh.in new file mode 100644 index 00000000..bd1598bc --- /dev/null +++ b/tools/scripts/sfx.sh.in @@ -0,0 +1,283 @@ +#!/bin/sh + +set -e + +CMD=${0##*/} + +UNZIP=/usr/bin/unzip + +UNZIPOPTS= +UNZIPARGS= +UNZIPDIR= +UNZIPQ="-q" +UNZIPV= + +UNZIPLOOP= + +mode=install + +SHAR=$0 + +shardir=`dirname $SHAR` +shardir=`cd $shardir && pwd` +SHARABS="$shardir"/${SHAR##*/} + +SFX_BYTES=0 ## fill in the size (padded) of this SFX header ################## +SFX_BLOCKSIZE=1 ## use a larger block size, 'dd bs=1' is sometimes slow ###### +SFX_CHECKSUM= ## compute checksum over this SFX and the payload ############## +SFX_INSTALL=install ## internal script in the payload to run ################# +SFX_PERMS= ## internal script to correct file permissions #################### +SFX_PAD= ## pad file (this payload) to skip during unpack #################### +SFX_LAZY= ## set to '1' to defer extraction to SFX_INSTALL ################## +SFX_UNZIP=1 ## set to '' if this unzip cannot parse SFX headers ############# +SFX_LOOP=1 ## set to '' if this unzip cannot read from a loopback/block #### +SFX_PIPE=1 ## set to '' if this unzip cannot read from a pipe ############## + +if test "$SFX_PAD"; then + UNZIPARGS=$UNZIPARGS${UNZIPARGS:+" "}"-x $SFX_PAD" +fi + +while test $# -gt 0; do + case "$1" in + -x) + # N.B. that busybox unzip has trouble with multiple files + mode=unzip + shift + UNZIPARGS=$UNZIPARGS${UNZIPARGS:+" "}"$1" + shift + UNZIPARGS=$UNZIPARGS${UNZIPARGS:+" "}"$1" + continue + ;; + -d) + shift + UNZIPDIR=$1 + shift + continue + ;; + -v) + UNZIPQ= + UNZIPV="$1" + shift + ;; + -q) + UNZIPQ="$1" + UNZIPV= + shift + ;; + -*) + mode=unzip + UNZIPOPTS=$UNZIPOPTS${UNZIPOPTS:+" "}"$1" + shift + ;; + *) + mode=unzip + UNZIPARGS=$UNZIPARGS${UNZIPARGS:+" "}"$1" + shift + continue + ;; + esac +done + +if test "$SFX_BLOCKSIZE" -gt 1; then + SFX_BLOCKS=$(($SFX_BYTES / $SFX_BLOCKSIZE)) + SFX_TRAILER=$(($SFX_BYTES % $SFX_BLOCKSIZE)) + if test $SFX_TRAILER -ne 0; then + echo "$CMD: *** SFX is not block-aligned" 1>&2 + exit 1 + fi +else + echo "$CMD: *** SFX is blocked, dd may be slow" 1>&2 +fi + +workdir=`mktemp -t -d sfx-XXXXXX` || exit 1 +do_cleanup() +{ + if test -b "$UNZIPLOOP"; then + losetup -d "$UNZIPLOOP" || : + fi + cd / + /bin/rm -fr $workdir 2>/dev/null +} +trap "do_cleanup" 0 1 + +_t() +{ + local c z + c="$1"; shift + z="$1"; shift + $c | $UNZIP $UNZIPQ $UNZIPV -l "$z" 1>/dev/null 2>&1 + return $? +} + +# be wary of busybox unzip +# http://lists.busybox.net/pipermail/busybox/2010-August/073131.html +case "$SFX_PAD:$SFX_UNZIP:$SFX_LOOP:$SFX_PIPE" in + :1:*:*) + echo "$CMD: testing for SFX support" + if ! _t ":" "$SHARABS"; then + echo "$CMD: *** SFX failed" 1>&2 + SFX_UNZIP= + fi + ;; +esac + +case "$SFX_PAD:$SFX_UNZIP:$SFX_LOOP:$SFX_PIPE" in + ::1:*) + echo "$CMD: testing for loopback support" + UNZIPLOOP=`losetup -f` + losetup -r -o $SFX_BYTES "$UNZIPLOOP" "$SHARABS" + if ! _t ":" "$UNZIPLOOP"; then + echo "$CMD: *** loopback failed" 1>&2 + SFX_LOOP= + fi + ;; +esac + +case "$SFX_PAD:$SFX_UNZIP:$SFX_LOOP:$SFX_PIPE" in + :::1) + echo "$CMD: testing for pipe support" + if ! _t "dd if=$SHARABS bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS" "-"; then + echo "$CMD: *** pipe failed" 1>&2 + SFX_PIPE= + fi + ;; +esac + +if test "$SFX_PAD"; then + echo "$CMD: copying file and resetting pad" + cp "$SHARABS" $workdir/onie-installer.zip + dd if="$SHARABS" of=$workdir/onie-installer.zip bs=512 skip=$(($SFX_BLOCKS-1)) count=1 conv=notrunc + _CAT=":" + _ZIP="$workdir/onie-installer.zip" +elif test "$SFX_UNZIP"; then + echo "$CMD: processing SFX with unzip" + _CAT=":" + _ZIP="$SHARABS" +elif test "$SFX_LOOP"; then + echo "$CMD: processing SFX with losetup" + _CAT=":" + _ZIP="$UNZIPLOOP" +elif test "$SFX_PIPE"; then + echo "$CMD: processing SFX with dd" + _CAT="dd if=$SHARABS bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS" + _ZIP="-" +else + echo "$CMD: *** copying file to find zip offset" + dd if=$SHARABS of=$workdir/onie-installer.zip bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS + _CAT=":" + _ZIP="$workdir/onie-installer.zip" +fi + +if test "$mode" = "unzip"; then + echo "$CMD: processing with zip" + + if test "$UNZIPDIR"; then + cd "$UNZIPDIR" + fi + + $_CAT | $UNZIP $UNZIPQ $UNZIPV $UNZIPOPTS "$_ZIP" $UNZIPARGS + sts=$? + test $sts -eq 0 || exit $sts + + if test -f "$SFX_PERMS"; then + echo "$CMD: correcting permissions with $SFX_PERMS" + chmod +x "$SFX_PERMS" + ./"$SFX_PERMS" + fi + + exit $sts +fi + +if test "$mode" != "install"; then + echo "$CMD: *** invalid mode: $mode" 1>&2 + exit 1 +fi + +if test "$UNZIPDIR"; then + : +else + UNZIPDIR=$workdir +fi + +banner=`unzip 2>&1` || : +case "$banner" in + *"-t"*) + echo "$CMD: testing shar" + $_CAT | $UNZIP $UNZIPQ $UNZIPV $UNZIPOPTS -t "$_ZIP" $UNZIPARGS + sts=$? + test $sts -eq 0 || exit $sts + ;; +esac + +echo "$CMD: computing checksum" +{ + dd if="$SHARABS" bs=$SFX_BLOCKSIZE count=$SFX_BLOCKS 2>/dev/null | sed -e "/^SFX_CHECKSUM=/d"; + dd if="$SHARABS" bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS 2>/dev/null +} | md5sum > "$UNZIPDIR/ck" + +set dummy `cat "$UNZIPDIR/ck"` +newck=$2 + +rm -f "$UNZIPDIR/ck" + +if test "$SFX_CHECKSUM" = "$newck"; then + echo "$CMD: checksum is OK" +else + echo "$CMD: *** checksum mismatch" 1>&2 + exit 1 +fi + +shardir=`dirname $0` +shardir=`cd $shardir && pwd` + +echo "$CMD: extracting shar into $UNZIPDIR" +cd $UNZIPDIR + +if test "$SFX_LAZY"; then + $_CAT | $UNZIP $UNZIPQ $UNZIPOPTS "$_ZIP" "$SFX_INSTALL" +else + $_CAT | $UNZIP $UNZIPQ $UNZIPOPTS "$_ZIP" $UNZIPARGS +fi + +if test -f "$SFX_PERMS"; then + echo "$CMD: correcting permissions with $SFX_PERMS" + chmod +x "$SFX_PERMS" + ./"$SFX_PERMS" +fi + +if test -f "$SFX_INSTALL"; then + echo "$CMD: invoking installer $SFX_INSTALL" + + tmp_install=`mktemp $UNZIPDIR/install-XXXXXX` + mv "$SFX_INSTALL" $tmp_install + chmod +x "$tmp_install" + + export SFX_BLOCKSIZE SFX_BLOCKS SFX_PAD SFX_UNZIP SFX_LOOP SFX_PIPE SFX_LAZY SFX_PERMS + + case "$-" in + *x*) + dashx="-x" + ;; + esac + + if test -e "$_ZIP"; then + eval "$tmp_install" $dashx "$_ZIP" + else + eval "$tmp_install" $dashx "$SHARABS" + fi + + exit $? +else + echo "$CMD: *** missing installer: $SFX_INSTALL" 1>&2 + exit 1 +fi + +############################## +# +# END OF SHAR HEADER +# +# BINARY ZIP DATA FOLLOWS +# +# DO NOT EDIT! +# +##############################