update_engine: Expose the update_engine.conf in the FilesystemInterface.
The old image contains a /etc/update_engine.conf file that states the
payload version supported by that image. Currently we read this file
directly from the mounted filesystem and parse its contents. To stop
mounting the filesystem we need to retrieve the information on this file
somehow.
This patch extends the FilesystemInterface with a method to load the
update_engine.conf settings from the filesystem and implemets it using
ext2fs on ext2 filesystems.
CQ-DEPED=CL:282380
BUG=chromium:305832
TEST=Added new unittests with and without this file.
Change-Id: I41b41e8aac58c645fb40aabfe340cde8821e405a
Reviewed-on: https://chromium-review.googlesource.com/282381
Reviewed-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
Trybot-Ready: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
diff --git a/payload_generator/ext2_filesystem.cc b/payload_generator/ext2_filesystem.cc
index 9670e39..b7276d5 100644
--- a/payload_generator/ext2_filesystem.cc
+++ b/payload_generator/ext2_filesystem.cc
@@ -111,6 +111,7 @@
if (filename.empty())
return nullptr;
unique_ptr<Ext2Filesystem> result(new Ext2Filesystem());
+ result->filename_ = filename;
errcode_t err = ext2fs_open(filename.c_str(),
0, // flags (read only)
@@ -304,4 +305,39 @@
return true;
}
+bool Ext2Filesystem::LoadSettings(chromeos::KeyValueStore* store) const {
+ // First search for the settings inode following symlinks if we find some.
+ ext2_ino_t ino_num = 0;
+ errcode_t err = ext2fs_namei_follow(
+ filsys_, EXT2_ROOT_INO /* root */, EXT2_ROOT_INO /* cwd */,
+ "/etc/update_engine.conf", &ino_num);
+ if (err != 0)
+ return false;
+
+ ext2_inode ino_data;
+ if (ext2fs_read_inode(filsys_, ino_num, &ino_data) != 0)
+ return false;
+
+ // Load the list of blocks and then the contents of the inodes.
+ vector<Extent> extents;
+ err = ext2fs_block_iterate2(filsys_, ino_num, BLOCK_FLAG_DATA_ONLY,
+ nullptr, // block_buf
+ ProcessInodeAllBlocks,
+ &extents);
+ if (err != 0)
+ return false;
+
+ chromeos::Blob blob;
+ uint64_t physical_size = BlocksInExtents(extents) * filsys_->blocksize;
+ // Sparse holes in the settings file are not supported.
+ if (EXT2_I_SIZE(&ino_data) > physical_size)
+ return false;
+ if (!utils::ReadExtents(filename_, extents, &blob, physical_size,
+ filsys_->blocksize))
+ return false;
+
+ string text(blob.begin(), blob.begin() + EXT2_I_SIZE(&ino_data));
+ return store->LoadFromString(text);
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/ext2_filesystem.h b/payload_generator/ext2_filesystem.h
index c87849c..66cb65f 100644
--- a/payload_generator/ext2_filesystem.h
+++ b/payload_generator/ext2_filesystem.h
@@ -40,12 +40,17 @@
// and bitmap tables.
bool GetFiles(std::vector<File>* files) const override;
+ bool LoadSettings(chromeos::KeyValueStore* store) const override;
+
private:
Ext2Filesystem() = default;
// The ext2 main data structure holding the filesystem.
ext2_filsys filsys_ = nullptr;
+ // The file where the filesystem is stored.
+ std::string filename_;
+
DISALLOW_COPY_AND_ASSIGN(Ext2Filesystem);
};
diff --git a/payload_generator/ext2_filesystem_unittest.cc b/payload_generator/ext2_filesystem_unittest.cc
index 122ba02..d2ea2b3 100644
--- a/payload_generator/ext2_filesystem_unittest.cc
+++ b/payload_generator/ext2_filesystem_unittest.cc
@@ -177,4 +177,26 @@
}
}
+TEST_F(Ext2FilesystemTest, LoadSettingsFailsTest) {
+ base::FilePath path = test_utils::GetBuildArtifactsPath().Append(
+ "gen/disk_ext2_1k.img");
+ unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(path.value());
+
+ chromeos::KeyValueStore store;
+ // disk_ext2_1k.img doesn't have the /etc/update_engine.conf file.
+ EXPECT_FALSE(fs->LoadSettings(&store));
+}
+
+TEST_F(Ext2FilesystemTest, LoadSettingsWorksTest) {
+ base::FilePath path = test_utils::GetBuildArtifactsPath().Append(
+ "gen/disk_ext2_ue_settings.img");
+ unique_ptr<Ext2Filesystem> fs = Ext2Filesystem::CreateFromFile(path.value());
+
+ chromeos::KeyValueStore store;
+ EXPECT_TRUE(fs->LoadSettings(&store));
+ string minor_version;
+ EXPECT_TRUE(store.GetString("PAYLOAD_MINOR_VERSION", &minor_version));
+ EXPECT_EQ("1234", minor_version);
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/fake_filesystem.cc b/payload_generator/fake_filesystem.cc
index b474b26..00911e1 100644
--- a/payload_generator/fake_filesystem.cc
+++ b/payload_generator/fake_filesystem.cc
@@ -38,4 +38,11 @@
files_.push_back(file);
}
+bool FakeFilesystem::LoadSettings(chromeos::KeyValueStore* store) const {
+ if (minor_version_ < 0)
+ return false;
+ store->SetString("PAYLOAD_MINOR_VERSION", std::to_string(minor_version_));
+ return true;
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/fake_filesystem.h b/payload_generator/fake_filesystem.h
index 10b6683..35b7756 100644
--- a/payload_generator/fake_filesystem.h
+++ b/payload_generator/fake_filesystem.h
@@ -26,17 +26,25 @@
size_t GetBlockSize() const override;
size_t GetBlockCount() const override;
bool GetFiles(std::vector<File>* files) const override;
+ bool LoadSettings(chromeos::KeyValueStore* store) const override;
// Fake methods.
// Add a file to the list of fake files.
void AddFile(const std::string& filename, const std::vector<Extent> extents);
+ // Sets the PAYLOAD_MINOR_VERSION key stored by LoadSettings(). Use a negative
+ // value to produce an error in LoadSettings().
+ void SetMinorVersion(int minor_version) {
+ minor_version_ = minor_version;
+ }
+
private:
FakeFilesystem() = default;
uint64_t block_size_;
uint64_t block_count_;
+ int minor_version_{-1};
std::vector<File> files_;
diff --git a/payload_generator/filesystem_interface.h b/payload_generator/filesystem_interface.h
index 501a6be..186eac4 100644
--- a/payload_generator/filesystem_interface.h
+++ b/payload_generator/filesystem_interface.h
@@ -20,6 +20,7 @@
#include <vector>
#include <base/macros.h>
+#include <chromeos/key_value_store.h>
#include "update_engine/update_metadata.pb.h"
@@ -66,6 +67,10 @@
// Returns whether the function succeeded.
virtual bool GetFiles(std::vector<File>* files) const = 0;
+ // Load the image settings stored in the filesystem in the
+ // /etc/update_engine.conf file. Returns whether the settings were found.
+ virtual bool LoadSettings(chromeos::KeyValueStore* store) const = 0;
+
protected:
FilesystemInterface() = default;
diff --git a/payload_generator/raw_filesystem.h b/payload_generator/raw_filesystem.h
index d8e1fe5..bd66487 100644
--- a/payload_generator/raw_filesystem.h
+++ b/payload_generator/raw_filesystem.h
@@ -29,6 +29,10 @@
// with the name passed during construction.
bool GetFiles(std::vector<File>* files) const override;
+ bool LoadSettings(chromeos::KeyValueStore* store) const override {
+ return false;
+ }
+
private:
RawFilesystem() = default;
diff --git a/sample_images/disk_ext2_1k.txt b/sample_images/disk_ext2_1k.txt
index f13c080..1494535 100644
--- a/sample_images/disk_ext2_1k.txt
+++ b/sample_images/disk_ext2_1k.txt
@@ -1 +1 @@
-16777216 1024
+default 16777216 1024
diff --git a/sample_images/disk_ext2_4k.txt b/sample_images/disk_ext2_4k.txt
index d01d105..cac718f 100644
--- a/sample_images/disk_ext2_4k.txt
+++ b/sample_images/disk_ext2_4k.txt
@@ -1 +1 @@
-16777216 4096
+default 16777216 4096
diff --git a/sample_images/disk_ext2_ue_settings.txt b/sample_images/disk_ext2_ue_settings.txt
new file mode 100644
index 0000000..554f7da
--- /dev/null
+++ b/sample_images/disk_ext2_ue_settings.txt
@@ -0,0 +1 @@
+ue_settings 16777216 4096
diff --git a/sample_images/generate_image.sh b/sample_images/generate_image.sh
index 0f0c384..c4f132a 100755
--- a/sample_images/generate_image.sh
+++ b/sample_images/generate_image.sh
@@ -17,29 +17,11 @@
rmdir "$1"
}
-# generate_fs <filename> <size> [block_size] [block_groups]
-generate_fs() {
- local filename="$1"
- local size="$2"
- local block_size="${3:-4096}"
- local block_groups="${4:-}"
-
- local mkfs_opts=( -q -F -b "${block_size}" -L "ROOT-TEST" -t ext2 )
- if [[ -n "${block_groups}" ]]; then
- mkfs_opts+=( -G "${block_groups}" )
- fi
-
- local mntdir=$(mktemp --tmpdir -d generate_ext2.XXXXXX)
- trap 'cleanup "${mntdir}"; rm -f "${filename}"' INT TERM EXIT
-
- # Cleanup old image.
- if [[ -e "${filename}" ]]; then
- rm -f "${filename}"
- fi
- truncate --size="${size}" "${filename}"
-
- mkfs.ext2 "${mkfs_opts[@]}" "${filename}"
- sudo mount "${filename}" "${mntdir}" -o loop
+# add_files_default <mntdir> <block_size>
+# Add several test files to the image mounted in <mntdir>.
+add_files_default() {
+ local mntdir="$1"
+ local block_size="$2"
### Generate the files used in unittest with descriptive names.
sudo touch "${mntdir}"/empty-file
@@ -93,6 +75,71 @@
"empty space data but it won't be all zeros." |
sudo dd of="${mntdir}"/removed conv=fsync status=none
sudo rm "${mntdir}"/removed
+}
+
+# add_files_ue_settings <mntdir> <block_size>
+# Add the update_engine.conf settings file. This file contains the
+add_files_ue_settings() {
+ local mntdir="$1"
+
+ sudo mkdir -p "${mntdir}"/etc >/dev/null
+ sudo tee "${mntdir}"/etc/update_engine.conf >/dev/null <<EOF
+PAYLOAD_MINOR_VERSION=1234
+EOF
+ # Example of a real lsb-release file released on link stable.
+ sudo tee "${mntdir}"/etc/lsb-release >/dev/null <<EOF
+CHROMEOS_AUSERVER=https://tools.google.com/service/update2
+CHROMEOS_BOARD_APPID={F26D159B-52A3-491A-AE25-B23670A66B32}
+CHROMEOS_CANARY_APPID={90F229CE-83E2-4FAF-8479-E368A34938B1}
+CHROMEOS_DEVSERVER=
+CHROMEOS_RELEASE_APPID={F26D159B-52A3-491A-AE25-B23670A66B32}
+CHROMEOS_RELEASE_BOARD=link-signed-mp-v4keys
+CHROMEOS_RELEASE_BRANCH_NUMBER=63
+CHROMEOS_RELEASE_BUILD_NUMBER=6946
+CHROMEOS_RELEASE_BUILD_TYPE=Official Build
+CHROMEOS_RELEASE_CHROME_MILESTONE=43
+CHROMEOS_RELEASE_DESCRIPTION=6946.63.0 (Official Build) stable-channel link
+CHROMEOS_RELEASE_NAME=Chrome OS
+CHROMEOS_RELEASE_PATCH_NUMBER=0
+CHROMEOS_RELEASE_TRACK=stable-channel
+CHROMEOS_RELEASE_VERSION=6946.63.0
+GOOGLE_RELEASE=6946.63.0
+EOF
+}
+
+# generate_fs <filename> <kind> <size> [block_size] [block_groups]
+generate_fs() {
+ local filename="$1"
+ local kind="$2"
+ local size="$3"
+ local block_size="${4:-4096}"
+ local block_groups="${5:-}"
+
+ local mkfs_opts=( -q -F -b "${block_size}" -L "ROOT-TEST" -t ext2 )
+ if [[ -n "${block_groups}" ]]; then
+ mkfs_opts+=( -G "${block_groups}" )
+ fi
+
+ local mntdir=$(mktemp --tmpdir -d generate_ext2.XXXXXX)
+ trap 'cleanup "${mntdir}"; rm -f "${filename}"' INT TERM EXIT
+
+ # Cleanup old image.
+ if [[ -e "${filename}" ]]; then
+ rm -f "${filename}"
+ fi
+ truncate --size="${size}" "${filename}"
+
+ mkfs.ext2 "${mkfs_opts[@]}" "${filename}"
+ sudo mount "${filename}" "${mntdir}" -o loop
+
+ case "${kind}" in
+ ue_settings)
+ add_files_ue_settings "${mntdir}" "${block_size}"
+ ;;
+ default)
+ add_files_default "${mntdir}" "${block_size}"
+ ;;
+ esac
cleanup "${mntdir}"
trap - INT TERM EXIT
diff --git a/update_engine.gyp b/update_engine.gyp
index 9294f66..6df8e7e 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -332,6 +332,7 @@
'sources': [
'sample_images/disk_ext2_1k.txt',
'sample_images/disk_ext2_4k.txt',
+ 'sample_images/disk_ext2_ue_settings.txt',
],
'includes': ['generate_image.gypi'],
},