Files
OpenNetworkLinux/tools/mkshar
Andreas Rammhold a4cefaf8f5 tools: use /usr/bin/python2 instead of /usr/bin/python
python3 is starting to become the default on all major distributions.
Using `/usr/bin/python2` should be safe for the near future.
2017-06-30 11:40:29 +02:00

252 lines
6.2 KiB
Python
Executable File

#!/usr/bin/python2
"""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('--inplace',
action='store_true',
help="Perform fixups in-place")
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:]
def _spliceMaybe(tag, val):
val = "${%s-\"%s\"}" % (tag, val,)
_splice(tag, val)
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:
_spliceMaybe('SFX_UNZIP', '1')
else:
_spliceMaybe('SFX_UNZIP', '')
if opts.unzip_pipe:
_spliceMaybe('SFX_PIPE', '1')
else:
_spliceMaybe('SFX_PIPE', '')
if opts.unzip_loop:
_spliceMaybe('SFX_LOOP', '1')
else:
_spliceMaybe('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)
if opts.inplace:
_spliceMaybe('SFX_INPLACE', '1')
else:
_spliceMaybe('SFX_INPLACE', '')
# 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)