Installer SHAR support.

This commit is contained in:
Jeffrey Townsend
2015-12-11 21:34:35 +00:00
parent 7655700810
commit 4aeeefd261
2 changed files with 523 additions and 0 deletions

240
tools/mkshar Executable file
View File

@@ -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)

283
tools/scripts/sfx.sh.in Normal file
View File

@@ -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!
#
##############################