a3aba089d48bcf27a921ce4e18e96201c16ef225
[platform/core/system/upgrade.git] / scripts / upgrade-support / upgrade-common.inc
1 IMG_VERIFIER="/usr/sbin/img-verifier"
2 STATUS_DIR="/opt/data/update"
3 HAL_UPGRADE_CFG_DIR="/hal/etc/upgrade/"
4 HAL_PART_MAP_FILE="label_map.list"
5 HAL_PART_LIST_FILE="background_copy.list"
6 CONFIG_FILE="update.cfg"
7 SET_UPGRADE_STATUS="/usr/bin/device_board_set_upgrade_status"
8 DO_RW_UPDATE_FILE="$FOTA_UPDATE_PREFIX/opt/.do_rw_update"
9 TRUE=0
10 FALSE=1
11
12 #------------------------------------------------
13 #       critical_log msg [file]
14 #------------------------------------------------
15 critical_log() {
16         # log format: [script_name][tag]actual_log
17         LOG="[${SCRIPT_NAME}]$1"
18         dlogsend -k "$LOG"
19         if [ "$2" != "" ]; then
20                 echo "$LOG" >> "$2"
21         fi
22         echo "$LOG"
23
24         return 0
25 }
26
27 critical_flog() {
28         critical_log "$1" "$LOG_FILE"
29 }
30
31 #------------------------------------------------
32 #       log msg [file]
33 #------------------------------------------------
34 log() {
35         # log format: [script_name][tag]actual_log
36         LOG="[${SCRIPT_NAME}]$1"
37         if [ "$2" != "" ]; then
38                 echo "$LOG" >> "$2"
39         fi
40         echo "$LOG"
41
42         return 0
43 }
44
45 flog() {
46         log "$1" "$LOG_FILE"
47 }
48
49 untrap() {
50         trap '' ERR
51 }
52
53 retrap() {
54         trap 'echo "Aborting due to errexit on ${0##*/}:$LINENO. Exit code: $?" >&2' ERR
55 }
56
57 verify_file() {
58         FILE_NAME="$1"
59         FILE_PATH="$FOTA_DIR/$FILE_NAME"
60         VALID_CHECKSUM=$(awk "\$2 ~ /^$FILE_NAME\$/ {print \$1}" "$FOTA_DIR/checksum.SHA1")
61         if [ "$VALID_CHECKSUM" == "" ]; then
62                 echo "[Error] No $FILE_NAME in checksum.SHA1"
63                 return $FALSE
64         fi
65
66         if ! echo "$VALID_CHECKSUM  $FILE_PATH" | sha1sum --check --status; then
67                 echo "[Error] Checksum for file $FILE_NAME is invalid"
68                 return $FALSE
69         fi
70
71         echo "[Info] Checksum of $FILE_NAME is OK"
72         return $TRUE
73 }
74
75 unpack_file() {
76         ARCHIVE_NAME="$1"
77         FILE_NAME="$2"
78         tar xpf "${ARCHIVE_NAME}" -C "${FOTA_DIR}" "${FILE_NAME}" 2> /dev/null
79         if [ ! -e "${FOTA_DIR}/${FILE_NAME}" ]; then
80                 flog "[Error] There is no ${FILE_NAME}"
81         fi
82
83         if ! verify_file "$FILE_NAME"; then
84                 exit_error
85         fi
86 }
87
88 verify_img() {
89         DELTA_FILE="$1"
90         if [ -e "$IMG_VERIFIER" ]; then
91                 log "[Info] Package verifier is found. Verify $DELTA_FILE" "$LOG_FILE"
92                 if ! "$IMG_VERIFIER" -i "$DELTA_FILE" -l "/opt/var/log/last_iv.log"; then
93                         log "[Error] Update package verification FAILED..." "$LOG_FILE"
94                         echo 5 > "$STATUS_DIR"/result
95                         exit_error
96                 else
97                         log "[Info] Update package verification PASSED!" "$LOG_FILE"
98                 fi
99         fi
100 }
101
102 check_ab_partition_scheme() {
103         CURRENT_AB="$(/bin/sed -E 's|.*(partition_ab=)([a-b]).*|\2|' /proc/cmdline)"
104         if [ "$CURRENT_AB" != "a" ] && [ "$CURRENT_AB" != "b" ]; then
105                 flog "[Info] There is no A/B partition scheme"
106                 exit_error
107         fi
108         NEXT_AB="b"
109         if [ "$CURRENT_AB" == "b" ]; then
110                 NEXT_AB="a"
111         fi
112 }
113
114 check_used_block_device() {
115         local MAPPER_MATCH_REGEX='.*/dev/mapper.*'
116         ROOTFS_DEVICE="$(findmnt / -no SOURCE)"
117         MAPPER_DEVICE=""
118         if [[ "${ROOTFS_DEVICE}" =~ ${MAPPER_MATCH_REGEX} ]]; then
119                 MAPPER_DEVICE="${ROOTFS_DEVICE}"
120                 DM_NAME=$(basename "${MAPPER_DEVICE}")
121                 DM_N=$(dmsetup ls -o blkdevname | grep "${DM_NAME}" | sed -e 's/^.*(\([^)]\+\))/\1/')
122                 ROOTFS_DEVICE="/dev/$(ls /sys/class/block/${DM_N}/slaves/)"
123         fi
124
125         if ! EMMC_DEVICE="/dev/$(/bin/lsblk -ndo pkname "${ROOTFS_DEVICE}")" ||\
126                 [ "$EMMC_DEVICE" == "/dev/" ]; then
127                 flog "[Error] Unable to find used block device: $EMMC_DEVICE, for /"
128                 exit_error
129         fi
130 }
131
132 background_copy() {
133         flog "[Info] Background copying A|B partitions for update..."
134         for partition_name in ${PARTITION_LIST}; do
135                 # echo is there to suspend abort when partition will not be found e.g. hal
136                 if ! CURRENT_PARTITION="$(/usr/bin/blkid-print "$EMMC_DEVICE" "$partition_name" "$CURRENT_AB" |\
137                         sed -E 's|(part_nr: [0-9]+ )\((.*)\): (.*)|\3|' || echo "__FALSE__")" || \
138                         [ "$CURRENT_PARTITION" == "__FALSE__" ]; then
139                         flog "[Error] Unable to find: $partition_name current partition on $EMMC_DEVICE device on $CURRENT_AB slot"
140                         check_optional_partition "$partition_name" 1
141                         continue
142                 fi
143                 if ! NEXT_PARTITION="$(/usr/bin/blkid-print "$EMMC_DEVICE" "$partition_name" "$NEXT_AB" |\
144                         sed -E 's|(part_nr: [0-9]+ )\((.*)\): (.*)|\3|')"; then
145                         flog "[Error] Unable to find: $partition_name next partition on $EMMC_DEVICE device on $CURRENT_AB slot"
146                         check_optional_partition "$partition_name" 1
147                         continue
148                 fi
149                 if [ "$CURRENT_PARTITION" == "" ] || [ "$NEXT_PARTITION" = "" ]; then
150                         flog "[Error] current: $CURRENT_PARTITION or next: $NEXT_PARTITION partition is empty on $EMMC_DEVICE device"
151                         return $FALSE
152                 fi
153                 if [ "$CURRENT_PARTITION" == "$NEXT_PARTITION" ]; then
154                         flog "[Info] $partition_name partition current and next are the same: $CURRENT_PARTITION on $EMMC_DEVICE device"
155                         continue
156                 fi
157                 flog "[Info] Background copy $partition_name, from: $CURRENT_PARTITION to $NEXT_PARTITION"
158                 /bin/dd if="$CURRENT_PARTITION" of="$NEXT_PARTITION" bs=4096
159                 flog "[Info] Finished background copy $partition_name from $CURRENT_PARTITION to $NEXT_PARTITION"
160
161                 if [ "$partition_name" == "rootfs" ]; then
162                         flog "[Info] Checksum verification for $partition_name"
163                         CUR_SHA1=$(sha1sum "$CURRENT_PARTITION" | awk '{print $1}')
164                         NEXT_SHA1=$(sha1sum "$NEXT_PARTITION" | awk '{print $1}')
165                         if [ "$CUR_SHA1" == "$NEXT_SHA1" ]; then
166                                 flog "[Info] Partition $partition_name was cloned correctly"
167                         else
168                                 flog "[Error] Checksums are different: $CUR_SHA1 != $NEXT_SHA1"
169                                 exit_error
170                         fi
171                 fi
172         done
173         return $TRUE
174 }
175
176 load_background_copy_list() {
177         PARTITION_LIST=$(sed -e '/^#/d' -e '/^$/d' "${HAL_UPGRADE_CFG_DIR}/${HAL_PART_LIST_FILE}")
178 }
179
180 upgrade_images() {
181         DELTA_TAR="$1"
182         flog "[Info] Flash images for update..."
183
184         LABEL_MAP_PATH=${HAL_UPGRADE_CFG_DIR}/${HAL_}
185         while read LABEL PARTLABEL; do
186                 declare "LABEL_MAP_${LABEL}=${PARTLABEL}"
187         done < <(sed -e '/^#/d' -e '/^$/d' "${LABEL_MAP_PATH}/${HAL_PART_MAP_FILE}")
188
189         # _OFFSET _SIZE _HASH1 _HASH2
190         while read -r LABEL_NAME DELTA_NAME TYPE DEV OFFSET OLD_SIZE NEW_SIZE OLD_SHA NEW_SHA
191         do
192                 local LABEL_NAME="$(echo "$LABEL_NAME" | /bin/awk '{print tolower($0)}')"
193
194                 # Translate LABEL to PARTLABEL using label_map.list
195                 local TMP="LABEL_MAP_${LABEL_NAME}"
196                 local PART_NAME=${!TMP}
197                 if [ -z "${PART_NAME}" ]; then
198                         PART_NAME=${LABEL_NAME}
199                 fi
200
201                 TYPE_S=(${TYPE//:/ })
202                 # We support only FULL_IMAGE, DELTA_IMAGE and DELTA_FS
203                 if [[ ! "${TYPE_S[0]}" =~ ^(FULL_IMAGE|DELTA_IMAGE|DELTA_FS)$ ]]; then
204                         PARTS_NAME_TO_UPDATE+=( "$PART_NAME:$TYPE" )
205                         continue
206                 fi
207
208                 if ! /bin/tar tf "$DELTA_TAR" "$DELTA_NAME"; then
209                         flog "[Error] There is no delta $DELTA_NAME for label $LABEL_NAME from part $PART_NAME"
210                         exit_error
211                 fi
212
213                 local NEXT_PARTITION="$(/usr/bin/blkid-print "$EMMC_DEVICE" "$PART_NAME" "$NEXT_AB" |\
214                         /bin/sed -E 's|(part_nr: [0-9]+ )\((.*)\): (.*)|\3|')"
215                 local CURR_PARTITION="$(/usr/bin/blkid-print "$EMMC_DEVICE" "$PART_NAME" "$CURRENT_AB" |\
216                         /bin/sed -E 's|(part_nr: [0-9]+ )\((.*)\): (.*)|\3|')"
217
218                 flog "[Info] Flashing $DELTA_NAME... to $NEXT_PARTITION"
219
220                 local UP_RES
221                 untrap
222                 case "${TYPE_S[0]}" in
223                         FULL_IMAGE)
224                                 "$FOTA_DIR/upgrade-apply" --archive "$DELTA_TAR" \
225                                                           --dest "$NEXT_PARTITION" \
226                                                           --dest-size "$NEW_SIZE" \
227                                                           --archive-file "$DELTA_NAME" \
228                                                           --kind raw \
229                                                           --dest-sha1 "$NEW_SHA" && UP_RES=$? || UP_RES=$?
230                                 ;;
231                         DELTA_IMAGE)
232                                 "$FOTA_DIR/upgrade-apply" --archive "$DELTA_TAR" \
233                                                           --dest "$NEXT_PARTITION" \
234                                                           --dest-size "$NEW_SIZE" \
235                                                           --archive-file "$DELTA_NAME" \
236                                                           --kind ss_brotli_patch \
237                                                           --patch-orig "$CURR_PARTITION" \
238                                                           --no-write-all \
239                                                           --dest-sha1 "$NEW_SHA" && UP_RES=$? || UP_RES=$?
240                                 ;;
241                         DELTA_FS)
242                                 mkdir "$FOTA_DIR/partition_mnt"
243                                 mount "$NEXT_PARTITION" "$FOTA_DIR/partition_mnt"
244                                 "$FOTA_DIR/upgrade-apply-deltafs"   --archive "$DELTA_TAR" \
245                                                                     --dest "$FOTA_DIR/partition_mnt" \
246                                                                     --archive-file "${DELTA_NAME}/" && UP_RES=$? || UP_RES=$?
247                                 umount "$NEXT_PARTITION"
248                                 rm -rf "$FOTA_DIR/partition_mnt"
249                                 ;;
250                 esac
251                 retrap
252                 case $UP_RES in
253                         0)
254                                 flog "[Info] Finished flashing $DELTA_NAME... to $NEXT_PARTITION"
255                                 ;;
256                         *)
257                                 flog "[Info] Flashing error $DELTA_NAME... to $NEXT_PARTITION"
258                                 return $FALSE
259                                 ;;
260                 esac
261         done < "$FOTA_DIR/$CONFIG_FILE"
262 }
263
264 run_setup_script() {
265         DELTA_TAR="$1"
266         SETUP_SCRIPT_NAME=setup.sh
267         SETUP_SCRIPT_PATH=$FOTA_DIR/$SETUP_SCRIPT_NAME
268
269         /bin/tar xvfp "$DELTA_TAR" -C "$FOTA_DIR" "$SETUP_SCRIPT_NAME" 2>/dev/null ||\
270                 (log "[Info] setup.sh does not exist, skipping." "$LOG_FILE" && return)
271
272         if [ -e "$SETUP_SCRIPT_PATH" ]; then
273                 /bin/sh "$SETUP_SCRIPT_PATH"
274                 rm "$SETUP_SCRIPT_PATH"
275         fi
276 }
277
278 write_version_info() {
279         VERSION_FILE="$1"
280         if [ -f "$VERSION_FILE" ]; then
281                 return
282         fi
283         OLD_VER=$(/bin/grep platform.version\" /etc/config/model-config.xml \
284                         | sed -e 's/.*>\(.*\)<.*/\1/' | head -1)
285         i=0
286         VER=(0 0 0 0)
287         for ENT in $(echo "$OLD_VER" | tr "." "\n"); do
288                 VER[$i]=$ENT
289                 ((i++))
290         done
291         CVT_VER=${VER[0]}.${VER[1]}.${VER[2]}.${VER[3]}
292
293         echo "OLD_VER=$CVT_VER" > "$VERSION_FILE"
294 }
295
296 set_upgrade_status() {
297         VALUE="$1"
298         ${SET_UPGRADE_STATUS} ${VALUE}
299         if [ $? -eq 0 ]; then
300                 critical_log "set_upgrade_status success: ${VALUE}" "$LOG_FILE"
301         else
302                 critical_log "set_upgrade_status failed: ${VALUE}" "$LOG_FILE"
303         fi
304 }
305
306 exit_error() {
307         set_upgrade_status -1
308         LOG_DIR="/var/log/fota"
309
310         if [ ! -d "$LOG_DIR" ]; then
311                 /usr/bin/mkdir -p "$LOG_DIR"
312         fi
313
314         if [ -e /tmp/upgrade-prepare-partitions.log ]; then
315                 /usr/bin/mv /tmp/upgrade-prepare-partitions.log $LOG_DIR/
316         fi
317
318         if [ -e /tmp/upgrade-trigger.log ]; then
319                 /usr/bin/mv /tmp/upgrade-trigger.log $LOG_DIR/
320         fi
321
322         if [ -e /tmp/upgrade-partial.log ]; then
323                 /usr/bin/mv /tmp/upgrade-partial.log $LOG_DIR/
324         fi
325
326         if [ -e /tmp/update-fota.log ]; then
327                 /usr/bin/mv /tmp/update-fota.log $LOG_DIR/
328         fi
329
330         exit 1
331 }
332
333 mount_partition() {
334         local GPT_LABEL="$1"
335         local DST="$2"
336         local MOUNT_OPTIONS="$3"
337         local SRC
338         if ! SRC="$(/usr/bin/blkid-print "$EMMC_DEVICE" "$GPT_LABEL" "$NEXT_AB" |\
339                 /bin/sed -E 's|(part_nr: [0-9]+ )\((.*)\): (.*)|\3|')"; then
340                 flog "[Error] Unable to find $GPT_LABEL partition on $EMMC_DEVICE device for $NEXT_AB slot"
341                 return 1
342         fi
343         local MOUNTED="$(/bin/findmnt -n -o TARGET "$SRC" | /bin/head -n 1 || echo "")"
344         if [ "$MOUNTED" != "" ]; then
345                 MOUNT_OPTIONS="bind,${MOUNT_OPTIONS}"
346                 SRC="$MOUNTED"
347         fi
348         if ! /bin/mkdir -p "$DST"; then
349                 flog "[Error] Unable to mkdir for mount destination: $DST"
350                 return 1
351         fi
352         if ! /bin/mount -o "$MOUNT_OPTIONS" "$SRC" "$DST"; then
353                 flog "[Error] Unable to mount $SRC to $DST, options: $MOUNT_OPTIONS"
354                 return 1
355         fi
356         CLEANUP_PARTITION+=("$DST")
357         return 0
358 }
359
360 cleanup_partitions() {
361         # umount in reverse order
362         for (( idx=${#CLEANUP_PARTITION[@]}-1 ; idx>=0 ; idx-- )) ; do
363                 local DST="${CLEANUP_PARTITION[idx]}"
364                 if [ "$DST" == "" ]; then
365                         flog "[Error] Partition for cleanup is empty, idx: $idx, partition count: ${#CLEANUP_PARTITION[@]}"
366                         continue
367                 fi
368                 if ! /bin/umount "$DST"; then
369                         flog "[Error] Unable to umount $DST"
370                 fi
371
372         done
373         CLEANUP_PARTITION=()
374 }