extract_files: Add extract2() function

New devices have more than one partition, and it's very
cumbersome to try to extract all the blobs for different
partitions from one directory. Adding multi-directory support
breaks the existing single directory method, so a new function
was born. Also, have native support for SAR system image mounts
so you can do some argument simplification in the device
extract-files.sh

Change-Id: I94925897690fc23816a28c9cdb34fb11e46635b8
diff --git a/build/tools/extract_utils.sh b/build/tools/extract_utils.sh
index 9402de6..7bcfc01 100755
--- a/build/tools/extract_utils.sh
+++ b/build/tools/extract_utils.sh
@@ -1380,6 +1380,240 @@
 }
 
 #
+# extract2:
+#
+# Positional parameters:
+# $1: file containing the list of items to extract (aka proprietary-files.txt)
+#
+# Non-positional parameters (coming after $2):
+# --section: selects the portion to parse and extracts from proprietary-files.txt
+# --kang: if present, this option will activate the printing of hashes for the
+#         extracted blobs. Useful with --section for subsequent pinning of
+#         blobs taken from other origins.
+#
+function extract2() {
+    # Consume positional parameters
+    local PROPRIETARY_FILES_TXT="$1"; shift
+    local SECTION=""
+    local KANG=false
+
+    # Consume optional, non-positional parameters
+    while [ "$#" -gt 0 ]; do
+        case "$1" in
+        --adb)
+            ADB=true
+            ;;
+        --system)
+            SYSTEM_SRC="$2"; shift
+            ;;
+        --vendor)
+            VENDOR_SRC="$2"; shift
+            ;;
+        --odm)
+            ODM_SRC="$2"; shift
+            ;;
+        --product)
+            PRODUCT_SRC="$2"; shift
+            ;;
+        -s|--section)
+            SECTION="$2"; shift
+            ;;
+        -k|--kang)
+            KANG=true
+            DISABLE_PINNING=1
+            ;;
+        esac
+        shift
+    done
+
+    if [ -z "$ADB" ] || [ -z "$SYSTEM_SRC" && -z "$VENDOR_SRC" && -z "$ODM_SRC" && -z "$PRODUCT_SRC" ]; then
+        echo "No sources set! You must select --adb or pass paths to partition dumps."
+        exit 1
+    fi
+
+    if [ -z "$OUTDIR" ]; then
+        echo "Output dir not set!"
+        exit 1
+    fi
+
+    parse_file_list "${PROPRIETARY_FILES_TXT}" "${SECTION}"
+
+    # Allow failing, so we can try $DEST and/or $FILE
+    set +e
+
+    local FILELIST=( ${PRODUCT_COPY_FILES_LIST[@]} ${PRODUCT_PACKAGES_LIST[@]} )
+    local HASHLIST=( ${PRODUCT_COPY_FILES_HASHES[@]} ${PRODUCT_PACKAGES_HASHES[@]} )
+    local FIXUP_HASHLIST=( ${PRODUCT_COPY_FILES_FIXUP_HASHES[@]} ${PRODUCT_PACKAGES_FIXUP_HASHES[@]} )
+    local PRODUCT_COPY_FILES_COUNT=${#PRODUCT_COPY_FILES_LIST[@]}
+    local COUNT=${#FILELIST[@]}
+    local OUTPUT_ROOT="$OMNI_ROOT"/"$OUTDIR"/proprietary
+    local OUTPUT_TMP="$TMPDIR"/"$OUTDIR"/proprietary
+
+    if [ "$ADB" = true ]; then
+        init_adb_connection
+    fi
+
+    if [ "$VENDOR_STATE" -eq "0" ]; then
+        echo "Cleaning output directory ($OUTPUT_ROOT).."
+        rm -rf "${OUTPUT_TMP:?}"
+        mkdir -p "${OUTPUT_TMP:?}"
+        if [ -d "$OUTPUT_ROOT" ]; then
+            mv "${OUTPUT_ROOT:?}/"* "${OUTPUT_TMP:?}/"
+        fi
+        VENDOR_STATE=1
+    fi
+
+    echo "Extracting ${COUNT} files in ${PROPRIETARY_FILES_TXT} from ${SRC}:"
+
+    for (( i=1; i<COUNT+1; i++ )); do
+
+        local SPEC_SRC_FILE=$(src_file "${FILELIST[$i-1]}")
+        local SPEC_DST_FILE=$(target_file "${FILELIST[$i-1]}")
+        local SPEC_ARGS=$(target_args "${FILELIST[$i-1]}")
+        local OUTPUT_DIR=
+        local TMP_DIR=
+        local SRC_FILE=
+        local DST_FILE=
+        local IS_PRODUCT_PACKAGE=false
+
+        # Note: this relies on the fact that the ${FILELIST[@]} array
+        # contains first ${PRODUCT_COPY_FILES_LIST[@]}, then ${PRODUCT_PACKAGES_LIST[@]}.
+        if [ "${i}" -gt "${PRODUCT_COPY_FILES_COUNT}" ]; then
+            IS_PRODUCT_PACKAGE=true
+        fi
+
+        if [ "${SPEC_ARGS}" = "rootfs" ]; then
+            OUTPUT_DIR="${OUTPUT_ROOT}/rootfs"
+            TMP_DIR="${OUTPUT_TMP}/rootfs"
+        else
+            OUTPUT_DIR="${OUTPUT_ROOT}"
+            TMP_DIR="${OUTPUT_TMP}"
+        fi
+        SRC_FILE="${SPEC_SRC_FILE}"
+        DST_FILE="${SPEC_DST_FILE}"
+
+        local VENDOR_REPO_FILE="$OUTPUT_DIR/${DST_FILE}"
+        local BLOB_DISPLAY_NAME="${DST_FILE}"
+        mkdir -p $(dirname "${VENDOR_REPO_FILE}")
+
+        # Check pinned files
+        local HASH="$(echo ${HASHLIST[$i-1]} | awk '{ print tolower($0); }')"
+        local FIXUP_HASH="$(echo ${FIXUP_HASHLIST[$i-1]} | awk '{ print tolower($0); }')"
+        local KEEP=""
+        if [ "$DISABLE_PINNING" != "1" ] && [ "$HASH" != "x" ]; then
+            if [ -f "${VENDOR_REPO_FILE}" ]; then
+                local PINNED="${VENDOR_REPO_FILE}"
+            else
+                local PINNED="${TMP_DIR}${DST_FILE}"
+            fi
+            if [ -f "$PINNED" ]; then
+                local TMP_HASH=$(get_hash "${PINNED}")
+                if [ "${TMP_HASH}" = "${HASH}" ] || [ "${TMP_HASH}" = "${FIXUP_HASH}" ]; then
+                    KEEP="1"
+                    if [ ! -f "${VENDOR_REPO_FILE}" ]; then
+                        cp -p "$PINNED" "${VENDOR_REPO_FILE}"
+                    fi
+                fi
+            fi
+        fi
+
+        if [ "${KANG}" = false ]; then
+            printf '  - %s\n' "${BLOB_DISPLAY_NAME}"
+        fi
+
+        if [ "$KEEP" = "1" ]; then
+            printf '    + keeping pinned file with hash %s\n' "${HASH}"
+        else
+            FOUND=false
+            PARTITION_SOURCE_DIR=
+            # Try Lineage target first.
+            for CANDIDATE in "${DST_FILE}" "${SRC_FILE}"; do
+                PARTITION=$(echo "$CANDIDATE" | cut -d/ -f1)
+                if [ "$PARTITION" = "system" ]; then
+                    PARTITION_SOURCE_DIR="$SYSTEM_SRC"
+                elif [ "$PARTITION" = "vendor" ]; then
+                    PARTITION_SOURCE_DIR="$VENDOR_SRC"
+                elif [ "$PARTITION" = "product" ]; then
+                    PARTITION_SOURCE_DIR="$PRODUCT_SRC"
+                elif [ "$PARTITION" = "odm" ]; then
+                    PARTITION_SOURCE_DIR="$ODM_SRC"
+                fi
+                CANDIDATE_RELATIVE_NAME=$(echo "$CANDIDATE" | cut -d/ -f2-)
+                get_file ${CANDIDATE_RELATIVE_NAME} ${VENDOR_REPO_FILE} ${PARTITION_SOURCE_DIR} && {
+                    FOUND=true
+                    break
+                }
+                # Search with the full system/ prefix if the file was not found on the system partition
+                # because we may be searching in a mounted system-as-root system.img
+                if [[ "${FOUND}" = false && "$PARTITION" = "system" ]]; then
+                    get_file ${CANDIDATE} ${VENDOR_REPO_FILE} ${PARTITION_SOURCE_DIR} && {
+                        FOUND=true
+                        break
+                    }
+                fi
+            done
+
+            if [ -z "${PARTITION_SOURCE_DIR}" ]; then
+                echo "$CANDIDATE has no preceeding partition path. Prepend system/, vendor/, product/, or odm/ to this entry."
+            fi
+
+            if [ "${FOUND}" = false ]; then
+                printf '    !! %s: file not found in source\n' "${BLOB_DISPLAY_NAME}"
+                continue
+            fi
+        fi
+
+        # Blob fixup pipeline has 2 parts: one that is fixed and
+        # one that is user-configurable
+        local PRE_FIXUP_HASH=$(get_hash ${VENDOR_REPO_FILE})
+        # Deodex apk|jar if that's the case
+        if [[ "$FULLY_DEODEXED" -ne "1" && "${VENDOR_REPO_FILE}" =~ .(apk|jar)$ ]]; then
+            oat2dex "${VENDOR_REPO_FILE}" "${SRC_FILE}" "${SYSTEM_SRC}"
+            if [ -f "$TMPDIR/classes.dex" ]; then
+                zip -gjq "${VENDOR_REPO_FILE}" "$TMPDIR/classes"*
+                rm "$TMPDIR/classes"*
+                printf '    (updated %s from odex files)\n' "${SRC_FILE}"
+            fi
+        elif [[ "${VENDOR_REPO_FILE}" =~ .xml$ ]]; then
+            fix_xml "${VENDOR_REPO_FILE}"
+        fi
+        # Now run user-supplied fixup function
+        blob_fixup "${BLOB_DISPLAY_NAME}" "${VENDOR_REPO_FILE}"
+        local POST_FIXUP_HASH=$(get_hash ${VENDOR_REPO_FILE})
+
+        if [ -f "${VENDOR_REPO_FILE}" ]; then
+            local DIR=$(dirname "${VENDOR_REPO_FILE}")
+            local TYPE="${DIR##*/}"
+            if [ "$TYPE" = "bin" -o "$TYPE" = "sbin" ]; then
+                chmod 755 "${VENDOR_REPO_FILE}"
+            else
+                chmod 644 "${VENDOR_REPO_FILE}"
+            fi
+        fi
+
+        if [ "${KANG}" =  true ]; then
+            print_spec "${IS_PRODUCT_PACKAGE}" "${SPEC_SRC_FILE}" "${SPEC_DST_FILE}" "${SPEC_ARGS}" "${PRE_FIXUP_HASH}" "${POST_FIXUP_HASH}"
+        fi
+
+        # Check and print whether the fixup pipeline actually did anything.
+        # This isn't done right after the fixup pipeline because we want this print
+        # to come after print_spec above, when in kang mode.
+        if [ "${PRE_FIXUP_HASH}" != "${POST_FIXUP_HASH}" ]; then
+            printf "    + Fixed up %s\n" "${BLOB_DISPLAY_NAME}"
+            # Now sanity-check the spec for this blob.
+            if [ "${KANG}" = false ] && [ "${FIXUP_HASH}" = "x" ] && [ "${HASH}" != "x" ]; then
+                printf "WARNING: The %s file was fixed up, but it is pinned.\n" ${BLOB_DISPLAY_NAME}
+                printf "This is a mistake and you want to either remove the hash completely, or add an extra one.\n"
+            fi
+        fi
+
+    done
+
+    # Don't allow failing
+    set -e
+}
+
+#
 # extract_firmware:
 #
 # $1: file containing the list of items to extract