blob: c48fb85a1ebde26163040533c0db7518d2d2ad08 [file] [log] [blame]
#!/usr/bin/python
# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This module is responsible for generate a stateful update payload."""
import logging
import optparse
import os
import sys
import subprocess
import tempfile
STATEFUL_FILE = 'stateful.tgz'
def GenerateStatefulPayload(image_path, output_directory, logger):
"""Generates a stateful update payload given a full path to an image.
Args:
image_path: Full path to the image.
output_directory: Path to the directory to leave the resulting output.
logger: logging instance.
"""
logger.info('Generating stateful update file.')
from_dir = os.path.dirname(image_path)
image = os.path.basename(image_path)
output_gz = os.path.join(output_directory, STATEFUL_FILE)
# TODO(zbehan): This is only used for mount_gpt_image.sh. That script also
# needs to move away from src/scripts.
try:
crosutils_dir = '%s/src/scripts' % os.environ['CROS_WORKON_SRCROOT']
except KeyError:
# Wow, we're outside the chroot. There's no clean way to do this, but we
# need to find out where sourceroot is.
# The two places where we may be running is platform/dev/, or /usr/bin/.
# Coincidentally, both are just a ../../../ away from sourceroot. Other
# locations for this script will just not work, and long term, we want
# exactly one anyway. (/usr/bin/)
self_path = os.path.dirname(sys.argv[0])
crosutils_dir = os.path.realpath(
os.path.join(self_path, '../../..', 'src/scripts'))
# Temporary directories for this function.
rootfs_dir = tempfile.mkdtemp(suffix='rootfs', prefix='tmp')
stateful_dir = tempfile.mkdtemp(suffix='stateful', prefix='tmp')
# Mount the image to pull out the important directories.
try:
# Only need stateful partition, but this saves us having to manage our
# own loopback device.
subprocess.check_call(['%s/mount_gpt_image.sh' % crosutils_dir,
'--from=%s' % from_dir,
'--image=%s' % image,
'--read_only',
'--rootfs_mountpt=%s' % rootfs_dir,
'--stateful_mountpt=%s' % stateful_dir,
])
logger.info('Tarring up /usr/local and /var!')
subprocess.check_call(['sudo',
'tar',
'-czf',
output_gz,
'--directory=%s' % stateful_dir,
'--hard-dereference',
'--transform=s,^dev_image,dev_image_new,',
'--transform=s,^var_overlay,var_new,',
'dev_image',
'var_overlay',
])
except:
logger.error('Failed to create stateful update file')
raise
finally:
# Unmount best effort regardless.
subprocess.call(['%s/mount_gpt_image.sh' % crosutils_dir,
'--unmount',
'--rootfs_mountpt=%s' % rootfs_dir,
'--stateful_mountpt=%s' % stateful_dir,
])
# Clean up our directories.
os.rmdir(rootfs_dir)
os.rmdir(stateful_dir)
logger.info('Successfully generated %s' % output_gz)
def main():
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(os.path.basename(__file__))
parser = optparse.OptionParser()
parser.add_option('-i', '--image_path',
help='The image to generate the stateful update for.')
parser.add_option('-o', '--output_dir',
help='The path to the directory to output the update file.')
options, unused_args = parser.parse_args()
if not options.image_path:
parser.error('Missing image for stateful payload generator')
if not options.output_dir:
parser.error('Missing output directory for the payload generator')
GenerateStatefulPayload(os.path.abspath(options.image_path),
options.output_dir, logger)
if __name__ == '__main__':
main()