#!/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 <directory> Use <directory> as input for OBB files"
echo " -k <key> Use <key> to encrypt OBB file"
echo " -K Prompt for key to encrypt OBB file"
echo " -o <filename> Write OBB file out to <filename>"
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