1#!/bin/bash
2
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# This script modifies the original GSI to match the vendor version and
18# the security patch level.
19#
20# Usage: change_security_patch_ver.sh <system.img> [<output_system.img> \
21#                 [<new_security_patch_level> [<input_file_contexts.bin>]]] \
22#                 [-v <vendor_version>]
23#
24# Examples:
25# change_security_patch_ver.sh system.img
26#   - Shows current version information.
27# change_security_patch_ver.sh system.img new_system.img 2018-04-05
28#   - Make new_system.img that has replaced SPL with 2018-04-05.
29# change_security_patch_ver.sh system.img new_system.img -v 8.1.0
30#   - Make new_system.img that includes the patches for vendor version 8.1.0.
31# change_security_patch_ver.sh system.img new_system.img 2018-04-05 -v 8.1.0
32#   - Make new_system.img that has both new SPL and vendor version.
33
34function unmount() {
35  echo "Unmounting..."
36  sudo umount "${MOUNT_POINT}/"
37}
38
39SCRIPT_NAME=$(basename $0)
40
41declare -a SUPPORTED_VENDOR_VERSIONS=(
42  8.1.0
43  9
44)
45SUPPORTED_VENDOR_VERSIONS="${SUPPORTED_VENDOR_VERSIONS[@]}"
46
47param_count=0
48while [[ $# -gt 0 ]]
49do
50case $1 in
51-v|--vendor) # set vendor version
52  VENDOR_VERSION=$2
53  shift
54  shift
55  ;;
56*) # set the ordered parameters
57  ((param_count++))
58  case $param_count in
59  1) # The input file name for original GSI
60    SYSTEM_IMG=$1
61    shift
62    ;;
63  2) # The output file name for modified GSI
64    OUTPUT_SYSTEM_IMG=$1
65    shift
66    ;;
67  3) # New Security Patch Level to be written. It must be YYYY-MM-DD format.
68    NEW_SPL=$1
69    shift
70    ;;
71  4) # Selinux file context
72    FILE_CONTEXTS_BIN=$1
73    shift
74    ;;
75  *)
76    ERROR=true
77    break
78    ;;
79  esac
80  ;;
81esac
82done
83
84if ((param_count == 0)) || [ "$ERROR" == "true" ]; then
85  echo "Usage: $SCRIPT_NAME <system.img> [<output_system.img> [<new_security_patch_level> [<input_file_contexts.bin>]]] [-v <vendor_version>]"
86  exit 1
87fi
88
89# SPL must have YYYY-MM-DD format
90if ((param_count >= 3)) && [[ ! ${NEW_SPL} =~ ^[0-9]{4}-(0[0-9]|1[012])-([012][0-9]|3[01])$ ]]; then
91  echo "<new_security_patch_level> must have YYYY-MM-DD format"
92  exit 1
93fi
94
95if [ "$VENDOR_VERSION" != "" ] && [[ ! ${VENDOR_VERSION} =~ ^(${SUPPORTED_VENDOR_VERSIONS// /\|})$ ]]; then
96  echo "Available vendor_version: $SUPPORTED_VENDOR_VERSIONS"
97  exit 1
98fi
99
100if [ "$VENDOR_VERSION" != "" ] && [ "$OUTPUT_SYSTEM_IMG" == "" ]; then
101  echo "<output_system.img> must be provided to set vendor version"
102  exit 1
103fi
104
105REQUIRED_BINARIES_LIST=(
106  "img2simg"
107  "simg2img"
108)
109if [ ! -z "${FILE_CONTEXTS_BIN}" ]; then
110  REQUIRED_BINARIES_LIST+=("mkuserimg_mke2fs")
111fi
112
113# number of binaries to find.
114BIN_COUNT=${#REQUIRED_BINARIES_LIST[@]}
115
116# use an associative array to store binary path
117declare -A BIN_PATH
118for bin in ${REQUIRED_BINARIES_LIST[@]}; do
119  BIN_PATH[${bin}]=""
120done
121
122# check current PATH environment first
123for bin in ${REQUIRED_BINARIES_LIST[@]}; do
124  if command -v ${bin} >/dev/null 2>&1; then
125    echo "found ${bin} in PATH."
126    BIN_PATH[${bin}]=${bin}
127    ((BIN_COUNT--))
128  fi
129done
130
131if [ ${BIN_COUNT} -gt 0 ]; then
132  # listed in the recommended order.
133  PATH_LIST=("${PWD}")
134  if [ "${PWD##*/}" == "testcases" ] && [ -d "${PWD}/../bin" ]; then
135    PATH_LIST+=("${PWD}/../bin")
136  fi
137  if [ -d "${ANDROID_HOST_OUT}" ]; then
138    PATH_LIST+=("${ANDROID_HOST_OUT}/bin")
139  fi
140
141  for dir in ${PATH_LIST[@]}; do
142    for bin in ${REQUIRED_BINARIES_LIST[@]}; do
143      if [ -z "${BIN_PATH[${bin}]}" ] && [ -f "${dir}/${bin}" ]; then
144        echo "found ${bin} in ${dir}."
145        BIN_PATH[${bin}]=${dir}/${bin}
146        ((BIN_COUNT--))
147        if [ ${BIN_COUNT} -eq 0 ]; then break; fi
148      fi
149    done
150  done
151fi
152
153if [ ${BIN_COUNT} -gt 0 ]; then
154  echo "Cannot find the required binaries. Need lunch; or run in a correct path."
155  exit 1
156fi
157echo "Found all binaries."
158
159MOUNT_POINT="${PWD}/temp_mnt"
160SPL_PROPERTY_NAME="ro.build.version.security_patch"
161RELEASE_VERSION_PROPERTY_NAME="ro.build.version.release"
162VNDK_VERSION_PROPERTY="ro.vndk.version"
163VNDK_VERSION_PROPERTY_OMR1="${VNDK_VERSION_PROPERTY}=27"
164
165UNSPARSED_SYSTEM_IMG="${SYSTEM_IMG}.raw"
166SYSTEM_IMG_MAGIC="$(xxd -g 4 -l 4 "$SYSTEM_IMG" | head -n1 | awk '{print $2}')"
167if [ "$SYSTEM_IMG_MAGIC" = "3aff26ed" ]; then
168  echo "Unsparsing ${SYSTEM_IMG}..."
169  ${BIN_PATH["simg2img"]} "$SYSTEM_IMG" "$UNSPARSED_SYSTEM_IMG"
170else
171  echo "Copying unsparse input system image ${SYSTEM_IMG}..."
172  cp "$SYSTEM_IMG" "$UNSPARSED_SYSTEM_IMG"
173fi
174
175IMG_SIZE=$(stat -c%s "$UNSPARSED_SYSTEM_IMG")
176
177echo "Mounting..."
178mkdir -p "$MOUNT_POINT"
179sudo mount -t ext4 -o loop "$UNSPARSED_SYSTEM_IMG" "${MOUNT_POINT}/"
180
181# check the property file path
182BUILD_PROP_PATH_LIST=(
183  "/system/build.prop"  # layout of A/B support
184  "/build.prop"         # layout of non-A/B support
185)
186BUILD_PROP_MOUNT_PATH=""
187BUILD_PROP_PATH=""
188
189echo "Finding build.prop..."
190for path in ${BUILD_PROP_PATH_LIST[@]}; do
191  if [ -f "${MOUNT_POINT}${path}" ]; then
192    BUILD_PROP_MOUNT_PATH="${MOUNT_POINT}${path}"
193    BUILD_PROP_PATH=${path}
194    echo "  ${path}"
195    break
196  fi
197done
198
199PROP_DEFAULT_PATH_LIST=(
200  "/system/etc/prop.default"  # layout of A/B support
201  "/etc/prop.default"         # layout of non-A/B support
202)
203
204if [ "$BUILD_PROP_MOUNT_PATH" != "" ]; then
205  if [ "$OUTPUT_SYSTEM_IMG" != "" ]; then
206    echo "Replacing..."
207  fi
208  CURRENT_SPL=`sudo sed -n -r "s/^${SPL_PROPERTY_NAME}=(.*)$/\1/p" ${BUILD_PROP_MOUNT_PATH}`
209  CURRENT_VERSION=`sudo sed -n -r "s/^${RELEASE_VERSION_PROPERTY_NAME}=(.*)$/\1/p" ${BUILD_PROP_MOUNT_PATH}`
210  echo "  Current security patch level: ${CURRENT_SPL}"
211  echo "  Current release version: ${CURRENT_VERSION}"
212
213  # Update SPL to <new_security_patch_level>
214  if [[ "$OUTPUT_SYSTEM_IMG" != "" && "$NEW_SPL" != "" ]]; then
215    if [[ "$CURRENT_SPL" == "" ]]; then
216      echo "ERROR: Cannot find ${SPL_PROPERTY_NAME} in ${BUILD_PROP_PATH}"
217    else
218      echo "  New security patch level: ${NEW_SPL}"
219      seek=$(sudo grep --byte-offset "${SPL_PROPERTY_NAME}=" "${BUILD_PROP_MOUNT_PATH}" | cut -d':' -f 1)
220      seek=$(($seek + ${#SPL_PROPERTY_NAME} + 1))   # 1 is for '='
221      echo "${NEW_SPL}" | sudo dd of="${BUILD_PROP_MOUNT_PATH}" seek="$seek" bs=1 count=10 conv=notrunc
222    fi
223  fi
224
225  # Update release version to <vendor_version>
226  if [[ "$OUTPUT_SYSTEM_IMG" != "" && "$VENDOR_VERSION" != "" ]]; then
227    if [[ "$CURRENT_VERSION" == "" ]]; then
228      echo "ERROR: Cannot find ${RELEASE_VERSION_PROPERTY_NAME} in ${BUILD_PROP_PATH}"
229    else
230      echo "  New release version for vendor.img: ${VENDOR_VERSION}"
231      sudo sed -i -e "s/^${RELEASE_VERSION_PROPERTY_NAME}=.*$/${RELEASE_VERSION_PROPERTY_NAME}=${VENDOR_VERSION}/" ${BUILD_PROP_MOUNT_PATH}
232    fi
233
234    if [[ "$VENDOR_VERSION" == "8.1.0" ]]; then
235      # add ro.vndk.version for O-MR1
236      echo "Finding prop.default..."
237      for path in ${PROP_DEFAULT_PATH_LIST[@]}; do
238        if [ -f "${MOUNT_POINT}${path}" ]; then
239          PROP_DEFAULT_PATH=${path}
240          echo "  ${path}"
241          break
242        fi
243      done
244
245      if [[ "$PROP_DEFAULT_PATH" != "" ]]; then
246        CURRENT_VNDK_VERSION=`sudo sed -n -r "s/^${VNDK_VERSION_PROPERTY}=(.*)$/\1/p" ${MOUNT_POINT}${PROP_DEFAULT_PATH}`
247        if [[ "$CURRENT_VNDK_VERSION" != "" ]]; then
248          echo "WARNING: ${VNDK_VERSION_PROPERTY} is already set to ${CURRENT_VNDK_VERSION} in ${PROP_DEFAULT_PATH}"
249        else
250          echo "  Add \"${VNDK_VERSION_PROPERTY_OMR1}\" to ${PROP_DEFAULT_PATH} for O-MR1 vendor image."
251          sudo sed -i -e "\$a\#\n\# FOR O-MR1 DEVICES\n\#\n${VNDK_VERSION_PROPERTY_OMR1}" ${MOUNT_POINT}${PROP_DEFAULT_PATH}
252        fi
253      else
254        echo "ERROR: Cannot find prop.default."
255      fi
256    fi
257  fi
258else
259  echo "ERROR: Cannot find build.prop."
260fi
261
262if [ "$OUTPUT_SYSTEM_IMG" != "" ]; then
263  if [ "$FILE_CONTEXTS_BIN" != "" ]; then
264    echo "Writing ${OUTPUT_SYSTEM_IMG}..."
265
266    (cd $ANDROID_BUILD_TOP
267     if [[ "$(whereis mkuserimg_mke2fs | wc -w)" < 2 ]]; then
268       make mkuserimg_mke2fs -j
269     fi
270     NON_AB=$(expr "$BUILD_PROP_PATH" == "/build.prop")
271     if [ $NON_AB -eq 1 ]; then
272       sudo /bin/bash -c "PATH=out/host/linux-x86/bin/:\$PATH mkuserimg_mke2fs -s ${MOUNT_POINT} $OUTPUT_SYSTEM_IMG ext4 system $IMG_SIZE -D ${MOUNT_POINT} -L system $FILE_CONTEXTS_BIN"
273     else
274       sudo /bin/bash -c "PATH=out/host/linux-x86/bin/:\$PATH mkuserimg_mke2fs -s ${MOUNT_POINT} $OUTPUT_SYSTEM_IMG ext4 / $IMG_SIZE -D ${MOUNT_POINT}/system -L / $FILE_CONTEXTS_BIN"
275     fi)
276
277    unmount
278  else
279    unmount
280
281    echo "Writing ${OUTPUT_SYSTEM_IMG}..."
282    ${BIN_PATH["img2simg"]} "$UNSPARSED_SYSTEM_IMG" "$OUTPUT_SYSTEM_IMG"
283  fi
284else
285  unmount
286fi
287
288echo "Done."
289