#!/bin/bash # # Copyright (C) 2010 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # mkobb.sh - Creates OBB files on Linux machines # Directory where we should temporarily mount the OBB loopback to copy files MOUNTDIR=/tmp # Presets. Changing these will probably break your OBB on the device CRYPTO=twofish FS=vfat MKFS=mkfs.vfat LOSETUP=losetup BLOCK_SIZE=512 SLOP=512 # Amount of filesystem slop in ${BLOCK_SIZE} blocks find_binaries() { MKFSBIN=`which ${MKFS}` LOSETUPBIN=`which ${LOSETUP}` MOUNTBIN=`which mount` UMOUNTBIN=`which umount` DDBIN=`which dd` RSYNCBIN=`which rsync` PBKDF2GEN=`which pbkdf2gen` } check_prereqs() { if [ "`uname -s`x" != "Linuxx" ]; then \ echo "ERROR: This script only works on Linux!" exit 1 fi if ! egrep -q "^cryptoloop " /proc/modules; then \ echo "ERROR: Could not find cryptoloop in the kernel." echo "Perhaps you need to: modprobe cryptoloop" exit 1 fi if ! egrep -q "name\s*:\s*${CRYPTO}$" /proc/crypto; then \ echo "ERROR: Could not find crypto \`${CRYPTO}' in the kernel." echo "Perhaps you need to: modprobe ${CRYPTO}" exit 1 fi if ! egrep -q "^\s*${FS}$" /proc/filesystems; then \ echo "ERROR: Could not find filesystem \`${FS}' in the kernel." echo "Perhaps you need to: modprobe ${FS}" exit 1 fi if [ "${MKFSBIN}x" = "x" ]; then \ echo "ERROR: Could not find ${MKFS} in your path!" exit 1 elif [ ! -x "${MKFSBIN}" ]; then \ echo "ERROR: ${MKFSBIN} is not executable!" exit 1 fi if [ "${LOSETUPBIN}x" = "x" ]; then \ echo "ERROR: Could not find ${LOSETUP} in your path!" exit 1 elif [ ! -x "${LOSETUPBIN}" ]; then \ echo "ERROR: ${LOSETUPBIN} is not executable!" exit 1 fi if [ "${PBKDF2GEN}x" = "x" ]; then \ echo "ERROR: Could not find pbkdf2gen in your path!" exit 1 fi } cleanup() { if [ "${loopdev}x" != "x" ]; then \ ${LOSETUPBIN} -d ${loopdev} fi } hidden_prompt() { unset output prompt="$1" outvar="$2" while read -s -n 1 -p "$prompt" c; do \ if [ "x$c" = "x" ]; then \ break fi prompt='*' output="${output}${c}" done echo eval $outvar="$output" unset output } read_key() { hidden_prompt " Encryption key: " key if [ "${key}x" = "x" ]; then \ echo "ERROR: An empty key is not allowed!" exit 1 fi hidden_prompt "Encryption key (again): " key2 if [ "${key}x" != "${key2}x" ]; then \ echo "ERROR: Encryption keys do not match!" exit 1 fi } onexit() { if [ "x${temp_mount}" != "x" ]; then \ ${UMOUNTBIN} ${temp_mount} rmdir ${temp_mount} fi if [ "x${loop_dev}" != "x" ]; then \ if [ ${use_crypto} -eq 1 ]; then \ dmsetup remove -f ${loop_dev} ${LOSETUPBIN} -d ${old_loop_dev} else \ ${LOSETUPBIN} -d ${loop_dev} fi fi if [ "x${tempfile}" != "x" -a -f "${tempfile}" ]; then \ rm -f ${tempfile} fi if [ "x${keyfile}" != "x" -a -f "${keyfile}" ]; then \ rm -f ${keyfile} fi echo "Fatal error." exit 1 } usage() { echo "mkobb.sh -- Create OBB files for use on Android" echo "" echo " -d Use as input for OBB files" echo " -k Use to encrypt OBB file" echo " -K Prompt for key to encrypt OBB file" echo " -o Write OBB file out to " echo " -v Verbose mode" echo " -h Help; this usage screen" } find_binaries check_prereqs use_crypto=0 args=`getopt -o d:hk:Ko:v -- "$@"` eval set -- "$args" while true; do \ case "$1" in -d) directory=$2; shift 2;; -h) usage; exit 1;; -k) key=$2; use_crypto=1; shift 2;; -K) prompt_key=1; use_crypto=1; shift;; -v) verbose=1; shift;; -o) filename=$2; shift 2;; --) shift; break;; *) echo "ERROR: Invalid argument in option parsing! Cannot recover. Ever."; exit 1;; esac done if [ "${directory}x" = "x" -o ! -d "${directory}" ]; then \ echo "ERROR: Must specify valid input directory" echo "" usage exit 1; fi if [ "${filename}x" = "x" ]; then \ echo "ERROR: Must specify filename" echo "" usage exit 1; fi if [ ${use_crypto} -eq 1 -a "${key}x" = "x" -a 0${prompt_key} -eq 0 ]; then \ echo "ERROR: Crypto desired, but no key supplied or requested to prompt for." exit 1 fi if [ 0${prompt_key} -eq 1 ]; then \ read_key fi outdir=`dirname ${filename}` if [ ! -d "${outdir}" ]; then \ echo "ERROR: Output directory does not exist: ${outdir}" exit 1 fi # Make sure we clean up any stuff we create from here on during error conditions trap onexit ERR tempfile=$(tempfile -d ${outdir}) || ( echo "ERROR: couldn't create temporary file in ${outdir}"; exit 1 ) block_count=`du -s --apparent-size --block-size=512 ${directory} | awk '{ print $1; }'` if [ $? -ne 0 ]; then \ echo "ERROR: Couldn't read size of input directory ${directory}" exit 1 fi echo "Creating temporary file..." ${DDBIN} if=/dev/zero of=${tempfile} bs=${BLOCK_SIZE} count=$((${block_count} + ${SLOP})) > /dev/null 2>&1 if [ $? -ne 0 ]; then \ echo "ERROR: creating temporary file: $?" fi loop_dev=$(${LOSETUPBIN} -f) || ( echo "ERROR: losetup wouldn't tell us the next unused device"; exit 1 ) ${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 ) if [ ${use_crypto} -eq 1 ]; then \ eval `${PBKDF2GEN} ${key}` unique_dm_name=`basename ${tempfile}` echo "0 `blockdev --getsize ${loop_dev}` crypt ${CRYPTO} ${key} 0 ${loop_dev} 0" | dmsetup create ${unique_dm_name} old_loop_dev=${loop_dev} loop_dev=/dev/mapper/${unique_dm_name} fi # # Create the filesystem # echo "" ${MKFSBIN} -I ${loop_dev} echo "" # # Make the temporary mount point and mount it # temp_mount="${MOUNTDIR}/${RANDOM}" mkdir ${temp_mount} ${MOUNTBIN} -t ${FS} -o loop ${loop_dev} ${temp_mount} # # rsync the files! # echo "Copying files:" ${RSYNCBIN} -av --no-owner --no-group ${directory}/ ${temp_mount}/ echo "" echo "Successfully created \`${filename}'" if [ ${use_crypto} -eq 1 ]; then \ echo "salt for use with obbtool is:" echo "${salt}" fi # # Undo all the temporaries # umount ${temp_mount} rmdir ${temp_mount} if [ ${use_crypto} -eq 1 ]; then \ dmsetup remove -f ${loop_dev} ${LOSETUPBIN} -d ${old_loop_dev} else \ ${LOSETUPBIN} -d ${loop_dev} fi mv ${tempfile} ${filename} trap - ERR exit 0