factory-utils: add factory scripts from crosutils (copy only)

BUG=n0ne
TEST=

Review URL: http://codereview.chromium.org/6368069
diff --git a/cros_sign_to_ssd b/cros_sign_to_ssd
new file mode 100755
index 0000000..64960ce
--- /dev/null
+++ b/cros_sign_to_ssd
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+# 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.
+
+# Script to resign the kernel partition generated in the output of build_image
+# with SSD keys.
+
+# --- BEGIN COMMON.SH BOILERPLATE ---
+# Load common CrOS utilities.  Inside the chroot this file is installed in
+# /usr/lib/crosutils.  Outside the chroot we find it relative to the script's
+# location.
+find_common_sh() {
+  local common_paths=(/usr/lib/crosutils "$(dirname "$(readlink -f "$0")")/..")
+  local path
+
+  SCRIPT_ROOT=
+  for path in "${common_paths[@]}"; do
+    if [ -r "${path}/common.sh" ]; then
+      SCRIPT_ROOT=${path}
+      break
+    fi
+  done
+}
+
+find_common_sh
+. "${SCRIPT_ROOT}/common.sh" || (echo "Unable to load common.sh" && exit 1)
+# --- END COMMON.SH BOILERPLATE ---
+
+# Need to be inside the chroot to load chromeos-common.sh
+assert_inside_chroot
+
+# Load functions and constants for chromeos-install
+. "/usr/lib/installer/chromeos-common.sh" || \
+  die "Unable to load /usr/lib/installer/chromeos-common.sh"
+
+locate_gpt
+
+DEFINE_string from "chromiumos_image.bin" \
+  "Input file name of Chrome OS image to re-sign."
+
+# Parse command line
+FLAGS "$@" || exit 1
+eval set -- "${FLAGS_ARGV}"
+
+failure() {
+  echo "SIGNING HAD FAILED"
+  exit 1
+}
+
+# Abort on error
+set -e
+
+trap "failure" EXIT
+
+if [ -z "${FLAGS_from}" ] || [ ! -f "${FLAGS_from}" ] ; then
+  echo "Error: invalid flag --from"
+  exit 1
+fi
+
+# Example commandline is as follows:
+# ./sign_official_build.sh \
+# ssd \
+# /.../build/images/x86-mario/0.8.68.2/chromiumos_test_image.bin \
+# ../../tests/devkeys/ \
+# /.../build/images/x86-mario/0.8.68.2/chromiumos_test_ssd_image.bin
+
+VBOOT_DIR="${SRC_ROOT}/platform/vboot_reference"
+if [ ! -d "${VBOOT_DIR}" ]; then
+  die "VBOOT DIR NOT FOUND at \'${VBOOT_DIR}\' .."
+fi
+
+TMP_IMAGE=$(mktemp)
+VBOOT_KEYS="${VBOOT_DIR}/tests/devkeys"
+if [ ! -d "${VBOOT_KEYS}" ]; then
+  die "VBOOT KEYS NOT FOUND at \'${VBOOT_KEYS}\' .."
+fi
+
+VBOOT_SIGN="${VBOOT_DIR}/scripts/image_signing/sign_official_build.sh"
+if [ ! -x "${VBOOT_SIGN}" ]; then
+  die "VBOOT TOOL sign_official_build.sh NOT FOUND at \'${VBOOT_SIGN}\' .."
+fi
+
+cp "${FLAGS_from}" "${TMP_IMAGE}"
+
+${VBOOT_SIGN} ssd "${TMP_IMAGE}" "${VBOOT_KEYS}" "${FLAGS_from}"
+
+rm "${TMP_IMAGE}"
+
+set +e
+trap - EXIT
diff --git a/make_factory_package.sh b/make_factory_package.sh
new file mode 100755
index 0000000..9d9b6a9
--- /dev/null
+++ b/make_factory_package.sh
@@ -0,0 +1,426 @@
+#!/bin/bash
+
+# Copyright (c) 2009 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.
+
+# Script to generate a factory install partition set and miniomaha.conf
+# file from a release image and a factory image. This creates a server
+# configuration that can be installed using a factory install shim.
+#
+# miniomaha lives in src/platform/dev/ and miniomaha partition sets live
+# in src/platform/dev/static.
+
+# --- BEGIN COMMON.SH BOILERPLATE ---
+# Load common CrOS utilities.  Inside the chroot this file is installed in
+# /usr/lib/crosutils.  Outside the chroot we find it relative to the script's
+# location.
+find_common_sh() {
+  local common_paths=(/usr/lib/crosutils $(dirname "$(readlink -f "$0")"))
+  local path
+
+  SCRIPT_ROOT=
+  for path in "${common_paths[@]}"; do
+    if [ -r "${path}/common.sh" ]; then
+      SCRIPT_ROOT=${path}
+      break
+    fi
+  done
+}
+
+find_common_sh
+. "${SCRIPT_ROOT}/common.sh" || (echo "Unable to load common.sh" && exit 1)
+# --- END COMMON.SH BOILERPLATE ---
+
+# Need to be inside the chroot to load chromeos-common.sh
+assert_inside_chroot
+
+# Load functions and constants for chromeos-install
+. "/usr/lib/installer/chromeos-common.sh" || \
+  die "Unable to load /usr/lib/installer/chromeos-common.sh"
+
+# Load functions designed for image processing
+. "${SCRIPT_ROOT}/lib/cros_image_common.sh" ||
+  die "Cannot load required library: lib/cros_image_common.sh; Abort."
+
+get_default_board
+
+# Flags
+DEFINE_string board "${DEFAULT_BOARD}" "Board for which the image was built"
+DEFINE_string factory "" \
+  "Directory and file containing factory image: /path/chromiumos_test_image.bin"
+DEFINE_string firmware_updater "" \
+  "If set, include the firmware shellball into the server configuration"
+DEFINE_string release "" \
+  "Directory and file containing release image: /path/chromiumos_image.bin"
+DEFINE_string subfolder "" \
+  "If set, the name of the subfolder to put the payload items inside"
+DEFINE_string diskimg "" \
+  "If set, the name of the diskimage file to output"
+DEFINE_boolean preserve ${FLAGS_FALSE} \
+  "If set, reuse the diskimage file, if available"
+DEFINE_integer sectors 31277232  "Size of image in sectors"
+
+# Parse command line
+FLAGS "$@" || exit 1
+eval set -- "${FLAGS_ARGV}"
+
+if [ ! -f "${FLAGS_release}" ]; then
+  echo "Cannot find image file ${FLAGS_release}"
+  exit 1
+fi
+
+if [ ! -f "${FLAGS_factory}" ]; then
+  echo "Cannot find image file ${FLAGS_factory}"
+  exit 1
+fi
+
+if [ -n "${FLAGS_firmware_updater}" ] &&
+   [ ! -f "${FLAGS_firmware_updater}" ]; then
+  echo "Cannot find firmware file ${FLAGS_firmware_updater}"
+  exit 1
+fi
+
+# Convert args to paths.  Need eval to un-quote the string so that shell
+# chars like ~ are processed; just doing FOO=`readlink -f ${FOO}` won't work.
+OMAHA_DIR="${SRC_ROOT}/platform/dev"
+OMAHA_CONF="${OMAHA_DIR}/miniomaha.conf"
+OMAHA_DATA_DIR="${OMAHA_DIR}/static/"
+
+# Note: The subfolder flag can only append configs.  That means you will need
+# to have unique board IDs for every time you run.  If you delete miniomaha.conf
+# you can still use this flag and it will start fresh.
+if [ -n "${FLAGS_subfolder}" ]; then
+  OMAHA_DATA_DIR="${OMAHA_DIR}/static/${FLAGS_subfolder}/"
+fi
+
+if [ ${INSIDE_CHROOT} -eq 0 ]; then
+  echo "Caching sudo authentication"
+  sudo -v
+  echo "Done"
+fi
+
+# Use this image as the source image to copy
+RELEASE_DIR="$(dirname "${FLAGS_release}")"
+FACTORY_DIR="$(dirname "${FLAGS_factory}")"
+RELEASE_IMAGE="$(basename "${FLAGS_release}")"
+FACTORY_IMAGE="$(basename "${FLAGS_factory}")"
+
+prepare_img() {
+  local outdev="$(readlink -f "$FLAGS_diskimg")"
+  local sectors="$FLAGS_sectors"
+  local force_full="true"
+
+  # We'll need some code to put in the PMBR, for booting on legacy BIOS.
+  echo "Fetch PMBR"
+  local pmbrcode="$(mktemp -d)/gptmbr.bin"
+  sudo dd bs=512 count=1 if="${FLAGS_release}" of="${pmbrcode}" status=noxfer
+
+  echo "Prepare base disk image"
+  # Create an output file if requested, or if none exists.
+  if [ -b "${outdev}" ] ; then
+    echo "Using block device ${outdev}"
+  elif [ ! -e "${outdev}" -o \
+        "$(stat -c %s ${outdev})" != "$(( ${sectors} * 512 ))"  -o \
+        "$FLAGS_preserve" = "$FLAGS_FALSE" ]; then
+    echo "Generating empty image file"
+    image_dump_partial_file /dev/zero 0 "${sectors}" |
+        dd of="${outdev}" bs=8M
+  else
+    echo "Reusing $outdev"
+  fi
+
+  # Create GPT partition table.
+  locate_gpt
+  install_gpt "${outdev}" 0 0 "${pmbrcode}" 0 "${force_full}"
+  # Activate the correct partition.
+  sudo "${GPT}" add -i 2 -S 1 -P 1 "${outdev}"
+}
+
+prepare_omaha() {
+  sudo rm -rf "${OMAHA_DATA_DIR}/rootfs-test.gz"
+  sudo rm -rf "${OMAHA_DATA_DIR}/rootfs-release.gz"
+  rm -rf "${OMAHA_DATA_DIR}/efi.gz"
+  rm -rf "${OMAHA_DATA_DIR}/oem.gz"
+  rm -rf "${OMAHA_DATA_DIR}/state.gz"
+  if [ ! -d "${OMAHA_DATA_DIR}" ]; then
+    mkdir -p "${OMAHA_DATA_DIR}"
+  fi
+}
+
+prepare_dir() {
+  sudo rm -rf rootfs-test.gz
+  sudo rm -rf rootfs-release.gz
+  rm -rf efi.gz
+  rm -rf oem.gz
+  rm -rf state.gz
+}
+
+compress_and_hash_memento_image() {
+  local input_file="$1"
+
+  if [ -n "${IMAGE_IS_UNPACKED}" ]; then
+    sudo "${SCRIPTS_DIR}/mk_memento_images_factory.sh" part_2 part_3 |
+      grep hash |
+      awk '{print $4}'
+  else
+    sudo "${SCRIPTS_DIR}/mk_memento_images_factory.sh" "$input_file" 2 3 |
+      grep hash |
+      awk '{print $4}'
+  fi
+}
+
+compress_and_hash_file() {
+  local input_file="$1"
+  local output_file="$2"
+
+  if [ -z "$input_file" ]; then
+    # Runs as a pipe processor
+    image_gzip_compress -c -9 |
+    tee "$output_file" |
+    openssl sha1 -binary |
+    openssl base64
+  else
+    image_gzip_compress -c -9 "$input_file" |
+    tee "$output_file" |
+    openssl sha1 -binary |
+    openssl base64
+  fi
+}
+
+compress_and_hash_partition() {
+  local input_file="$1"
+  local part_num="$2"
+  local output_file="$3"
+
+  if [ -n "${IMAGE_IS_UNPACKED}" ]; then
+    compress_and_hash_file "part_$part_num" "$output_file"
+  else
+    image_dump_partition "$input_file" "$part_num" |
+    compress_and_hash_file "" "$output_file"
+  fi
+}
+
+# Decide if we should unpack partition
+if image_has_part_tools; then
+  IMAGE_IS_UNPACKED=
+else
+  #TODO(hungte) Currently we run unpack_partitions.sh if part_tools are not
+  # found. If the format of unpack_partitions.sh is reliable, we can prevent
+  # creating temporary files. See image_part_offset for more information.
+  echo "WARNING: cannot find partition tools. Using unpack_partitions.sh." >&2
+  IMAGE_IS_UNPACKED=1
+fi
+
+mount_esp() {
+  local image="$1"
+  local esp_mountpoint="$2"
+  offset=$(partoffset "${image}" 12)
+  sudo mount -o loop,offset=$(( offset * 512 )) \
+      "${image}" "${esp_mountpoint}"
+  ESP_MOUNT="${esp_mountpoint}"
+}
+
+umount_esp() {
+  if [ -n "${ESP_MOUNT}" ]; then
+    sudo umount "${ESP_MOUNT}"
+  fi
+}
+
+generate_img() {
+  local outdev="$(readlink -f "$FLAGS_diskimg")"
+  local sectors="$FLAGS_sectors"
+
+  prepare_img
+
+  # Get the release image.
+  pushd "${RELEASE_DIR}" >/dev/null
+
+  echo "Release Kernel"
+  image_partition_copy "${RELEASE_IMAGE}" 2 "${outdev}" 4
+  echo "Release Rootfs"
+  image_partition_copy "${RELEASE_IMAGE}" 3 "${outdev}" 5
+  echo "OEM parition"
+  image_partition_copy "${RELEASE_IMAGE}" 8 "${outdev}" 8
+
+  popd >/dev/null
+
+  # Go to retrieve the factory test image.
+  pushd "${FACTORY_DIR}" >/dev/null
+
+  echo "Factory Kernel"
+  image_partition_copy "${FACTORY_IMAGE}" 2 "${outdev}" 2
+  echo "Factory Rootfs"
+  image_partition_copy "${FACTORY_IMAGE}" 3 "${outdev}" 3
+  echo "Factory Stateful"
+  image_partition_copy "${FACTORY_IMAGE}" 1 "${outdev}" 1
+  echo "EFI Partition"
+  image_partition_copy "${FACTORY_IMAGE}" 12 "${outdev}" 12
+
+  # TODO(nsanders, wad): consolidate this code into some common code
+  # when cleaning up kernel commandlines. There is code that touches
+  # this in postint/chromeos-setimage and build_image. However none
+  # of the preexisting code actually does what we want here.
+  local tmpesp="$(mktemp -d)"
+  mount_esp "${outdev}" "${tmpesp}"
+
+  trap "umount_esp" EXIT
+
+  # Edit boot device default for legacy.
+  # Support both vboot and regular boot.
+  sudo sed -i "s/chromeos-usb.A/chromeos-hd.A/" \
+      "${tmpesp}"/syslinux/default.cfg
+  sudo sed -i "s/chromeos-vusb.A/chromeos-vhd.A/" \
+      "${tmpesp}"/syslinux/default.cfg
+
+  # Edit root fs default for legacy
+  # Somewhat safe as ARM does not support syslinux, I believe.
+  sudo sed -i "s'HDROOTA'/dev/sda3'g" "${tmpesp}"/syslinux/root.A.cfg
+
+  trap - EXIT
+
+  umount_esp
+
+  echo "Generated Image at $outdev."
+  echo "Done"
+}
+
+generate_omaha() {
+  # Clean up stale config and data files.
+  prepare_omaha
+
+  # Get the release image.
+  pushd "${RELEASE_DIR}" >/dev/null
+  echo "Generating omaha release image from ${FLAGS_release}"
+  echo "Generating omaha factory image from ${FLAGS_factory}"
+  echo "Output omaha image to ${OMAHA_DATA_DIR}"
+  echo "Output omaha config to ${OMAHA_CONF}"
+
+  prepare_dir
+
+  if [ -n "${IMAGE_IS_UNPACKED}" ]; then
+    echo "Unpacking image ${RELEASE_IMAGE} ..." >&2
+    sudo ./unpack_partitions.sh "${RELEASE_IMAGE}" 2>/dev/null
+  fi
+
+  release_hash="$(compress_and_hash_memento_image "${RELEASE_IMAGE}")"
+  sudo chmod a+rw update.gz
+  mv update.gz rootfs-release.gz
+  mv rootfs-release.gz "${OMAHA_DATA_DIR}"
+  echo "release: ${release_hash}"
+
+  oem_hash="$(compress_and_hash_partition "${RELEASE_IMAGE}" 8 "oem.gz")"
+  mv oem.gz "${OMAHA_DATA_DIR}"
+  echo "oem: ${oem_hash}"
+
+  popd >/dev/null
+
+  # Go to retrieve the factory test image.
+  pushd "${FACTORY_DIR}" >/dev/null
+  prepare_dir
+
+  if [ -n "${IMAGE_IS_UNPACKED}" ]; then
+    echo "Unpacking image ${FACTORY_IMAGE} ..." >&2
+    sudo ./unpack_partitions.sh "${FACTORY_IMAGE}" 2>/dev/null
+  fi
+
+  test_hash="$(compress_and_hash_memento_image "${FACTORY_IMAGE}")"
+  sudo chmod a+rw update.gz
+  mv update.gz rootfs-test.gz
+  mv rootfs-test.gz "${OMAHA_DATA_DIR}"
+  echo "test: ${test_hash}"
+
+  state_hash="$(compress_and_hash_partition "${FACTORY_IMAGE}" 1 "state.gz")"
+  mv state.gz "${OMAHA_DATA_DIR}"
+  echo "state: ${state_hash}"
+
+  efi_hash="$(compress_and_hash_partition "${FACTORY_IMAGE}" 12 "efi.gz")"
+  mv efi.gz "${OMAHA_DATA_DIR}"
+  echo "efi: ${efi_hash}"
+
+  popd >/dev/null
+
+  if [ -n "${FLAGS_firmware_updater}" ]; then
+    SHELLBALL="${FLAGS_firmware_updater}"
+    if [ ! -f  "$SHELLBALL" ]; then
+      echo "Failed to find firmware updater: $SHELLBALL."
+      exit 1
+    fi
+
+    firmware_hash="$(compress_and_hash_file "$SHELLBALL" "firmware.gz")"
+    mv firmware.gz "${OMAHA_DATA_DIR}"
+    echo "firmware: ${firmware_hash}"
+  fi
+
+  # If the file does exist and we are using the subfolder flag we are going to
+  # append another config.
+  if [ -n "${FLAGS_subfolder}" ] &&
+     [ -f "${OMAHA_CONF}" ]; then
+    # Remove the ']' from the last line of the file
+    # so we can add another config.
+    while  [ -s "${OMAHA_CONF}" ]; do
+      # If the last line is null
+      if [ -z "$(tail -1 "${OMAHA_CONF}")" ]; then
+        sed -i '$d' "${OMAHA_CONF}"
+      elif [ "$(tail -1 "${OMAHA_CONF}")" != ']' ]; then
+        sed -i '$d' "${OMAHA_CONF}"
+      else
+        break
+      fi
+    done
+
+    # Remove the last ]
+    if [ "$(tail -1 "${OMAHA_CONF}")" = ']' ]; then
+      sed -i '$d' "${OMAHA_CONF}"
+    fi
+
+    # If the file is empty, create it from scratch
+    if [ ! -s "${OMAHA_CONF}" ]; then
+      echo "config = [" >"${OMAHA_CONF}"
+    fi
+  else
+    echo "config = [" >"${OMAHA_CONF}"
+  fi
+
+  if [ -n "${FLAGS_subfolder}" ]; then
+    subfolder="${FLAGS_subfolder}/"
+  fi
+
+  echo -n "{
+   'qual_ids': set([\"${FLAGS_board}\"]),
+   'factory_image': '${subfolder}rootfs-test.gz',
+   'factory_checksum': '${test_hash}',
+   'release_image': '${subfolder}rootfs-release.gz',
+   'release_checksum': '${release_hash}',
+   'oempartitionimg_image': '${subfolder}oem.gz',
+   'oempartitionimg_checksum': '${oem_hash}',
+   'efipartitionimg_image': '${subfolder}efi.gz',
+   'efipartitionimg_checksum': '${efi_hash}',
+   'stateimg_image': '${subfolder}state.gz',
+   'stateimg_checksum': '${state_hash}'," >>"${OMAHA_CONF}"
+
+  if [ -n "${FLAGS_firmware_updater}" ]  ; then
+    echo -n "
+   'firmware_image': '${subfolder}firmware.gz',
+   'firmware_checksum': '${firmware_hash}'," >>"${OMAHA_CONF}"
+  fi
+
+  echo -n "
+ },
+]
+" >>"${OMAHA_CONF}"
+
+  echo "The miniomaha server lives in src/platform/dev.
+To validate the configutarion, run:
+  python2.6 devserver.py --factory_config miniomaha.conf \
+  --validate_factory_config
+To run the server:
+  python2.6 devserver.py --factory_config miniomaha.conf"
+}
+
+# Main
+if [ -n "$FLAGS_diskimg" ]; then
+  generate_img
+else
+  generate_omaha
+fi
diff --git a/mk_memento_images_factory.sh b/mk_memento_images_factory.sh
new file mode 100755
index 0000000..c74cb50
--- /dev/null
+++ b/mk_memento_images_factory.sh
@@ -0,0 +1,135 @@
+#!/bin/bash
+
+# Copyright (c) 2009 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 script takes a path to a rootfs.ext2 which was generated by
+# build_image.sh and generates an image that can be used for auto
+# update.
+
+set -e
+
+# --- BEGIN COMMON.SH BOILERPLATE ---
+# Load common CrOS utilities.  Inside the chroot this file is installed in
+# /usr/lib/crosutils.  Outside the chroot we find it relative to the script's
+# location.
+find_common_sh() {
+  local common_paths=(/usr/lib/crosutils $(dirname "$(readlink -f "$0")"))
+  local path
+
+  SCRIPT_ROOT=
+  for path in "${common_paths[@]}"; do
+    if [ -r "${path}/common.sh" ]; then
+      SCRIPT_ROOT=${path}
+      break
+    fi
+  done
+}
+
+find_common_sh
+. "${SCRIPT_ROOT}/common.sh" || (echo "Unable to load common.sh" && exit 1)
+# --- END COMMON.SH BOILERPLATE ---
+
+# Load functions designed for image processing
+if ! . "${SCRIPT_ROOT}/lib/cros_image_common.sh"; then
+  echo "ERROR: Cannot load required library: lib/cros_image_common.sh; Abort."
+  exit 1
+fi
+
+if [ -z "$2" -o -z "$1" ] || [ "${#@}" -ne 2 -a "${#@}" -ne 3 ]; then
+  echo "usage: $0 path/to/kernel_partition_img path/to/rootfs_partition_img"
+  echo "    or $0 path/to/chromiumos_img kern_part_no rootfs_part_no"
+  exit 1
+fi
+
+if [ "$CROS_GENERATE_UPDATE_PAYLOAD_CALLED" != "1" ]; then
+  echo "WARNING:"
+  echo " This script should only be called from cros_generate_update_payload"
+  echo " Please run that script with --help to see how to use it."
+fi
+
+if ! image_has_command pigz; then
+  (echo "WARNING:"
+   echo " Your system does not have pigz (parallel gzip) installed."
+   echo " COMPRESSING WILL BE VERY SLOW. It is recommended to install pigz"
+   if image_has_command apt-get; then
+     echo " by 'sudo apt-get install pigz'."
+   elif image_has_command emerge; then
+     echo  " by 'sudo emerge pigz'."
+   fi) >&2
+fi
+
+if [ $(whoami) = "root" ]; then
+  echo "running $0 as root which is unneccessary"
+fi
+
+# Determine the offset size, and file name of parameters
+if [ -z "$3" ]; then
+  # kernnel_img rootfs_img
+  KPART="$1"
+  ROOT_PART="$2"
+  KPART_SIZE=$(stat -c%s "$KPART")
+  ROOT_PART_SIZE=$(stat -c%s "$ROOT_PART")
+  KPART_OFFSET=0
+  KPART_SECTORS=$((KPART_SIZE / 512))
+  ROOT_OFFSET=0
+  ROOT_SECTORS=$((ROOT_PART_SIZE / 512))
+else
+  # chromiumos_img kern_part_no rootfs_part_no
+  KPART="$1"
+  ROOT_PART="$1"
+  KPART_OFFSET="$(image_part_offset "$KPART" "$2")" ||
+    image_die "cannot retieve kernel partition offset"
+  KPART_SECTORS="$(image_part_size "$KPART" "$2")" ||
+    image_die "cannot retieve kernel partition size"
+  ROOT_OFFSET="$(image_part_offset "$ROOT_PART" "$3")" ||
+    image_die "cannot retieve root partition offset"
+  ROOT_SECTORS="$(image_part_size "$ROOT_PART" "$3")" ||
+    image_die "cannot retieve root partition size"
+  KPART_SIZE=$((KPART_SECTORS * 512))
+fi
+
+# Sanity check size.
+if [ "$KPART_SIZE" -gt $((16 * 1024 * 1024)) ]; then
+  echo "Kernel partition size ($KPART_SIZE bytes) greater than 16 MiB."
+  echo "That's too big."
+  exit 1
+fi
+
+FINAL_OUT_FILE=$(dirname "$1")/update.gz
+
+# Update payload format:
+#  [kernel_size: big-endian uint64][kernel_blob][rootfs_blob]
+
+# Prepare kernel_size by using printf as a number like 00000000003d0900, then
+# sed to convert as: \x00\x00\x00\x00\x00\x3d\x09\x00, finally echo -e to
+# convert into binary.
+KPART_SIZE_SIGNATURE="$(printf "%016x" "$KPART_SIZE" |
+                        sed 's/\([0-9a-f][0-9a-f]\)/\\x\1/g')"
+
+# Build the blob!
+CS_AND_RET_CODES="$(
+  (echo -en "$KPART_SIZE_SIGNATURE"
+    echo "Compressing kernel..." >&2
+    image_dump_partial_file "$KPART" "$KPART_OFFSET" "$KPART_SECTORS"
+    echo "Compressing rootfs..." >&2
+    image_dump_partial_file "$ROOT_PART" "$ROOT_OFFSET" "$ROOT_SECTORS") |
+  image_gzip_compress -c -9 |
+  tee "$FINAL_OUT_FILE" |
+  openssl sha1 -binary |
+  openssl base64 |
+  tr '\n' ' '
+  echo ${PIPESTATUS[*]})"
+
+EXPECTED_RET_CODES="0 0 0 0 0 0"
+set -- $CS_AND_RET_CODES
+CALC_CS="$1"
+shift
+RET_CODES="$@"
+if [ "$RET_CODES" != "$EXPECTED_RET_CODES" ]; then
+  echo compression/hash failed. $RET_CODES
+  exit 1
+fi
+
+echo Success. hash is "$CALC_CS"
diff --git a/serve_factory_packages.py b/serve_factory_packages.py
new file mode 100644
index 0000000..7233867
--- /dev/null
+++ b/serve_factory_packages.py
@@ -0,0 +1,182 @@
+#!/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 script runs inside chroot environment. It signs and build factory packages.
+Then serves them using devserver. All paths should be specified relative to the
+chroot environment.
+
+E.g.: ./enter_chroot.sh -- serve_factory_packages.py --board <board>
+
+Always precede the call to the script with './enter_chroot.sh -- ".
+"""
+
+import gflags
+import os
+import shlex
+import signal
+import subprocess
+import sys
+
+
+CWD = os.getcwd()
+USER = os.environ['USER']
+HOME_DIR = '/home/%s/trunk/src' % USER
+SCRIPTS_DIR = HOME_DIR + '/scripts'
+DEVSERVER_DIR = HOME_DIR + '/platform/dev'
+
+# Paths to image signing directory and dev key.
+VBOOT_REF_DIR = HOME_DIR + '/platform/vboot_reference'
+IMG_SIGN_DIR = VBOOT_REF_DIR + '/scripts/image_signing'
+DEVKEYS = VBOOT_REF_DIR + '/tests/devkeys'
+
+FLAGS = gflags.FLAGS
+
+gflags.DEFINE_string('board', None, 'Platform to build.')
+gflags.DEFINE_string('base_image', None, 'Path to base image.')
+gflags.DEFINE_string('firmware_updater', None, 'Path to firmware updater.')
+gflags.DEFINE_boolean('start_devserver', False, 'Start devserver.')
+
+class KillableProcess():
+  """A killable process.
+  """
+
+  running_process = None
+
+  def __init__(self, cmd, timeout=60, cwd=CWD):
+    """Initialize process
+
+    Args:
+      cmd: command to run.
+    """
+    self.cmd = shlex.split(cmd)
+    self.cmd_timeout = timeout
+    self.cmd_cwd = cwd
+
+  def start(self, wait=True):
+    """Start the process.
+
+    Args:
+      wait: wait for command to complete.
+    """
+    self.running_process = subprocess.Popen(self.cmd,
+                                            cwd=self.cmd_cwd)
+    if wait:
+      self.running_process.wait()
+
+  def stop(self):
+    """Stop the process.
+
+       This will only work for commands that do not exit.
+    """
+    self.running_process.send_signal(signal.SIGINT)
+    self.running_process.wait()
+
+
+def start_devserver():
+  """Starts devserver."""
+  cmd = 'python devserver.py --factory_config miniomaha.conf'
+  print 'Running command: %s' % cmd
+  devserver_process = KillableProcess(cmd, cwd=DEVSERVER_DIR)
+  devserver_process.start(wait=False)
+
+
+def assert_is_file(path, message):
+  """Assert file exists.
+
+  Args:
+    path: path to file.
+    message: message to print if file does not exist.
+  """
+  if not os.path.isfile(path):
+    error_message = '%s: %s is not a file!' % (message, path)
+    print error_message
+    sys.exit(1)
+
+
+def setup_board(board):
+  """Setup the board inside chroot.
+  """
+  cmd = './setup_board --board %s' % board
+  print 'Setting up board: %s' % board
+  setup_board_process = KillableProcess(cmd, cwd=SCRIPTS_DIR)
+  setup_board_process.start()
+
+
+def sign_build(image, output):
+  """Make an SSD signed build.
+
+  Args:
+    image: image to sign.
+    output: destination path for signed image.
+  """
+  assert_is_file(image, 'Asserting base image exists')
+  cmd = ('sudo ./sign_official_build.sh ssd %s %s %s'
+         % (image, DEVKEYS, output))
+  print 'IMG_SIGN_DIR: %s' % IMG_SIGN_DIR
+  print 'Signing image: %s' % cmd
+  sign_process = KillableProcess(cmd, cwd=IMG_SIGN_DIR)
+  sign_process.start()
+
+
+def build_factory_packages(signed_image, base_image, fw_updater, folder, board):
+  """Build factory packages and modify mini omaha config.
+
+  Args:
+    signed_image: signed image
+    base_image: base image
+    fw_updater: firmware updater
+    folder: destination folder to write packages
+    board: platform to build
+  """
+  cmd = ('./make_factory_package.sh --release %s --factory %s'
+         ' --subfolder %s --board %s'
+         % (signed_image, base_image, folder, board))
+  if fw_updater:
+    cmd = '%s --firmware_updater %s' % (cmd, fw_updater)
+  else:
+    print ('No --firmware_updater specified. Not including firmware shellball.')
+
+  print 'Building factory packages: %s' % cmd
+  build_packages_process = KillableProcess(cmd, cwd=SCRIPTS_DIR)
+  build_packages_process.start()
+
+
+def exit(message):
+  print message
+  sys.exit(1)
+
+
+def main(argv):
+  try:
+    argv = FLAGS(argv)
+  except gflags.FlagsError, e:
+    print '%s\nUsage: %s ARGS\n%s' % (e, sys.argv[0], FLAGS)
+    sys.exit(1)
+
+  if not FLAGS.base_image:
+    exit('No --base_image specified.')
+
+  if FLAGS.firmware_updater:
+    assert_is_file(FLAGS.firmware_updater,
+                   'Invalid or missing firmware updater.')
+
+  assert_is_file(FLAGS.base_image, 'Invalid or missing base image.')
+
+  signed_image = os.path.join(os.path.dirname(FLAGS.base_image),
+                              '%s_ssd_signed.bin' % FLAGS.board)
+
+  setup_board(FLAGS.board)
+  sign_build(FLAGS.base_image, signed_image)
+  build_factory_packages(signed_image, FLAGS.base_image,
+                         FLAGS.firmware_updater,
+                         folder=FLAGS.board, board=FLAGS.board)
+
+  if FLAGS.start_devserver:
+    start_devserver()
+
+
+if __name__ == '__main__':
+  main(sys.argv)