| #!/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 |