summaryrefslogtreecommitdiff
path: root/scripts/image_signing/align_rootfs.sh
blob: e8056a33ac412ef19fd300f8a3520d10b1c76616 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/bin/bash

# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Script to preserve the on-disk file layout of the specified image and
# the latest shipping image.  This is accomplished by copying the new rootfs
# over a template rootfs (aka the latest shipping image) to preserve as much
# of the metadata from the shipping rootfs as possible. This will ensure
# minimal disk shuffling when applying the auto-update.
#
# Note: This script does not recompute the rootfs hash.

# Load common library.  This should be the first executable line.
# The path to common.sh should be relative to your script's location.
. "$(dirname "$0")/common.sh"

load_shflags

# Flags.
DEFINE_string image "" \
  "The image that needs to be aligned to the latest shipping image."
DEFINE_string src_image "" \
  "The image to align against."

# Copies the rootfs from |SRC_IMAGE| to the |DST_ROOT_FS| and preserves as
# much of the file system metadata in |DST_ROOT_FS| as possible.
# Args: SRC_IMAGE DST_ROOT_FS
copy_root_fs() {
  local src_image=$1
  local dst_root_fs=$2

  # Mount the src and dst rootfs.
  local src_root_fs_dir=$(mktemp -d "/tmp/align_root_fs_src_mount_dir.XXXX")
  add_cleanup_action "sudo rm -rf \"${src_root_fs_dir}\""
  mount_image_partition_ro "${src_image}" 3 "${src_root_fs_dir}"
  add_cleanup_action "sudo umount \"${src_root_fs_dir}\""

  local dst_root_fs_dir=$(mktemp -d "/tmp/align_root_fs_dst_mount_dir.XXXX")
  add_cleanup_action "sudo rm -rf \"${dst_root_fs_dir}\""
  sudo mount -o loop "${dst_root_fs}" "${dst_root_fs_dir}" -o loop
  add_cleanup_action "sudo umount \"${dst_root_fs_dir}\""

  # Temporarily make immutable files on the dst rootfs mutable.
  # We'll need to track these files in ${immutable_files} so we can make them
  # mutable again.
  local immutable_files=()
  sudo find "${dst_root_fs_dir}" -xdev -type f |
    while read -r file; do
      immutable=$(sudo lsattr "${file}" | cut -d' ' -f1 | grep -q i ; echo $?)
      if [ $immutable -eq 0 ]; then
        immutable_files=("${immutable_files[@]}" "${file}")
        sudo chattr -i "${file}"
      fi
    done

  # Copy files from the src rootfs over top of dst rootfs.
  # Use the --inplace flag to preserve as much of the file system metadata
  # as possible.
  sudo rsync -v -a -H -A -x --force --inplace --numeric-ids --delete \
      "${src_root_fs_dir}"/ "${dst_root_fs_dir}"

  # Make immutable files immutable again.
  for file in ${immutable_files[*]} ; do
    sudo chattr +i "${file}"
  done

  # Unmount the src and dst root fs so that we can replace the rootfs later.
  perform_latest_cleanup_action
  perform_latest_cleanup_action
  perform_latest_cleanup_action
  perform_latest_cleanup_action
}

# Zeroes the rootfs free space in the specified image.
# Args: IMAGE
zero_root_fs_free_space() {
  local image=$1
  local root_fs_dir=$(mktemp -d "/tmp/align_rootfs_zero_free_space_dir.XXXX")
  add_cleanup_action "sudo rm -rf \"${root_fs_dir}\""
  mount_image_partition "${image}" 3 "${root_fs_dir}"
  add_cleanup_action "sudo umount \"${root_fs_dir}\""

  info "Zeroing free space in rootfs"
  sudo dd if=/dev/zero of="${root_fs_dir}/filler" oflag=sync bs=4096 || true
  sudo rm -f "${root_fs_dir}/filler"
  sudo sync

  perform_latest_cleanup_action
  perform_latest_cleanup_action
}

main() {
  # Parse command line.
  FLAGS "$@" || exit 1
  eval set -- "${FLAGS_ARGV}"

  # Only now can we die on error.  shflags functions leak non-zero error codes,
  # so will die prematurely if 'set -e' is specified before now.
  set -e

  # Make sure we have the required parameters.
  if [ -z "${FLAGS_image}" ];  then
    die "--image is required."
  fi

  if [ ! -f "${FLAGS_image}" ]; then
    die "Cannot find the specified image."
  fi

  if [ -z "${FLAGS_src_image}" ];  then
    die "--src_image is required."
  fi

  if [ ! -f "${FLAGS_src_image}" ]; then
    die "Cannot find the specified source image."
  fi

  # Make sure the two rootfs are the same size.
  # If they are not, then there is nothing for us to do.
  # Note: Exit with a zero code so we do not break the build workflow.
  local src_root_fs_size=$(partsize "${FLAGS_src_image}" 3)
  local new_root_fs_size=$(partsize "${FLAGS_image}" 3)
  if [ ${src_root_fs_size} -ne ${new_root_fs_size} ]; then
    warn "The source rootfs and the new rootfs are not the same size."
    exit 0
  fi

  # Extract the rootfs from the src image and use this as a template
  # for the new image.
  temp_root_fs=$(mktemp "/tmp/align_rootfs_temp_rootfs.XXXX")
  add_cleanup_action "sudo rm -f \"${temp_root_fs}\""
  info "Extracting rootfs from src image"
  extract_image_partition "${FLAGS_src_image}" 3 "${temp_root_fs}"
  enable_rw_mount "${temp_root_fs}"

  # Perform actual copy of the two root file systems.
  info "Copying rootfs"
  copy_root_fs "${FLAGS_image}" "${temp_root_fs}"

  # Replace the rootfs in the new image with the aligned version.
  info "Replacing rootfs"
  replace_image_partition "${FLAGS_image}" 3 "${temp_root_fs}"

  # Zero rootfs free space.
  zero_root_fs_free_space "${FLAGS_image}"
}

main "$@"