update_engine: Generate valid delta files for squashfs.
This patch generates a valid (but inefficient) payload when a delta
payload is requested for a squashfs filesystem.
Since we are reusing the full payload generator for this purpose, this
patch relaxes the requirement of chunk_size to be positive in the
full_update_generator.cc code, replacing it by the default value of
1 MiB when nothing is passed.
BUG=chromium:430950
TEST=FEATURES=test emerge-link update_engine
TEST=cros_generate_update_payload for a delta payload works with
squashfs
TEST=`cros flash` a squashfs device with a delta payload works.
Change-Id: I88c7a571874c4c4697f528d44c52091aa1aed0c5
Reviewed-on: https://chromium-review.googlesource.com/260444
Reviewed-by: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index b366c7e..5109fa0 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -107,9 +107,6 @@
kValidOperationData,
};
-// Chuck size used for full payloads during test.
-size_t kDefaultFullChunkSize = 1024 * 1024;
-
} // namespace
static void CompareFilesByBlock(const string& a_file, const string& b_file) {
@@ -489,8 +486,6 @@
DeltaPerformer::kSupportedMinorPayloadVersion;
} else {
payload_config.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
- if (payload_config.chunk_size == -1)
- payload_config.chunk_size = kDefaultFullChunkSize;
}
payload_config.target.rootfs_part = state->b_img;
payload_config.target.rootfs_mountpt = b_mnt;
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 3fc298e..1813abe 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -1050,16 +1050,28 @@
// Select payload generation strategy based on the config.
OperationsGenerator* strategy = nullptr;
if (config.is_delta) {
- // Delta update (with possibly a full kernel update).
- if (config.minor_version == kInPlaceMinorPayloadVersion) {
- LOG(INFO) << "Using generator InplaceGenerator::GenerateInplaceDelta";
- strategy = &InplaceGenerator::GenerateInplaceDelta;
- } else if (config.minor_version == kSourceMinorPayloadVersion) {
- LOG(INFO) << "Using generator DeltaDiffGenerator::GenerateSourceDelta";
- strategy = &DeltaDiffGenerator::GenerateDeltaWithSourceOperations;
+ // We don't efficiently support deltas on squashfs. For now, we will
+ // produce full operations in that case.
+ if (utils::IsSquashfsFilesystem(config.target.rootfs_part)) {
+ LOG(INFO) << "Using generator FullUpdateGenerator::Run for squashfs "
+ "deltas";
+ strategy = &FullUpdateGenerator::Run;
+ } else if (utils::IsExtFilesystem(config.target.rootfs_part)) {
+ // Delta update (with possibly a full kernel update).
+ if (config.minor_version == kInPlaceMinorPayloadVersion) {
+ LOG(INFO) << "Using generator InplaceGenerator::GenerateInplaceDelta";
+ strategy = &InplaceGenerator::GenerateInplaceDelta;
+ } else if (config.minor_version == kSourceMinorPayloadVersion) {
+ LOG(INFO) << "Using generator DeltaDiffGenerator::GenerateSourceDelta";
+ strategy = &DeltaDiffGenerator::GenerateDeltaWithSourceOperations;
+ } else {
+ LOG(ERROR) << "Unsupported minor version given for delta payload: "
+ << config.minor_version;
+ return false;
+ }
} else {
- LOG(ERROR) << "Unsupported minor version given for delta payload: "
- << config.minor_version;
+ LOG(ERROR) << "Unsupported filesystem for delta payload in "
+ << config.target.rootfs_part;
return false;
}
} else {
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
index fef6f78..0259b7d 100644
--- a/payload_generator/full_update_generator.cc
+++ b/payload_generator/full_update_generator.cc
@@ -11,6 +11,7 @@
#include <deque>
#include <memory>
+#include <base/format_macros.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_util.h>
@@ -26,6 +27,8 @@
namespace {
+const size_t kDefaultFullChunkSize = 1024 * 1024; // 1 MiB
+
// This class encapsulates a full update chunk processing thread. The processor
// reads a chunk of data from the input file descriptor and compresses it. The
// processor needs to be started through Start() then waited on through Wait().
@@ -115,9 +118,18 @@
vector<DeltaArchiveManifest_InstallOperation>* kernel_ops,
vector<Vertex::Index>* final_order) {
TEST_AND_RETURN_FALSE(config.Validate());
+
// FullUpdateGenerator requires a positive chuck_size, otherwise there will
// be only one operation with the whole partition which should not be allowed.
- TEST_AND_RETURN_FALSE(config.chunk_size > 0);
+ size_t full_chunk_size = kDefaultFullChunkSize;
+ if (config.chunk_size >= 0) {
+ full_chunk_size = config.chunk_size;
+ } else {
+ LOG(INFO) << "No chunk_size provided, using the default chunk_size for the "
+ << "full operations: " << full_chunk_size << " bytes.";
+ }
+ TEST_AND_RETURN_FALSE(full_chunk_size > 0);
+ TEST_AND_RETURN_FALSE(full_chunk_size % config.block_size == 0);
const ImageConfig& target = config.target; // Shortcut.
size_t max_threads = std::max(sysconf(_SC_NPROCESSORS_ONLN), 4L);
@@ -134,17 +146,17 @@
ScopedFdCloser in_fd_closer(&in_fd);
deque<shared_ptr<ChunkProcessor>> threads;
int last_progress_update = INT_MIN;
- off_t bytes_left = part_sizes[partition], counter = 0, offset = 0;
+ size_t bytes_left = part_sizes[partition], counter = 0, offset = 0;
while (bytes_left > 0 || !threads.empty()) {
// Check and start new chunk processors if possible.
while (threads.size() < max_threads && bytes_left > 0) {
+ size_t this_chunk_bytes = std::min(bytes_left, full_chunk_size);
shared_ptr<ChunkProcessor> processor(
- new ChunkProcessor(in_fd, offset,
- std::min(bytes_left, config.chunk_size)));
+ new ChunkProcessor(in_fd, offset, this_chunk_bytes));
threads.push_back(processor);
TEST_AND_RETURN_FALSE(processor->Start());
- bytes_left -= config.chunk_size;
- offset += config.chunk_size;
+ bytes_left -= this_chunk_bytes;
+ offset += this_chunk_bytes;
}
// Need to wait for a chunk processor to complete and process its output
@@ -157,7 +169,7 @@
if (partition == 0) {
graph->emplace_back();
graph->back().file_name =
- base::StringPrintf("<rootfs-operation-%" PRIi64 ">", counter++);
+ base::StringPrintf("<rootfs-operation-%" PRIuS ">", counter++);
op = &graph->back().op;
final_order->push_back(graph->size() - 1);
} else {
@@ -178,7 +190,7 @@
op->set_data_length(use_buf.size());
Extent* dst_extent = op->add_dst_extents();
dst_extent->set_start_block(processor->offset() / config.block_size);
- dst_extent->set_num_blocks(config.chunk_size / config.block_size);
+ dst_extent->set_num_blocks(full_chunk_size / config.block_size);
int progress = static_cast<int>(
(processor->offset() + processor->buffer_in().size()) * 100.0 /
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 2cb8de2..1b558be 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -400,11 +400,6 @@
}
}
- // Full payloads use a hard-coded chunk_size of 1 MiB.
- if (!payload_config.is_delta) {
- payload_config.chunk_size = 1024 * 1024;
- }
-
// Load the rootfs size from verity's kernel command line if rootfs
// verification is enabled.
payload_config.source.LoadVerityRootfsSize();
diff --git a/utils.cc b/utils.cc
index 7b3c6d0..c076682 100644
--- a/utils.cc
+++ b/utils.cc
@@ -811,6 +811,24 @@
return true;
}
+bool IsExtFilesystem(const std::string& device) {
+ chromeos::Blob header;
+ // The first 2 KiB is enough to read the ext2 superblock (located at offset
+ // 1024).
+ if (!ReadFileChunk(device, 0, 2048, &header))
+ return false;
+ return GetExt3Size(header.data(), header.size(), nullptr, nullptr);
+}
+
+bool IsSquashfsFilesystem(const std::string& device) {
+ chromeos::Blob header;
+ // The first 96 is enough to read the squashfs superblock.
+ const ssize_t kSquashfsSuperBlockSize = 96;
+ if (!ReadFileChunk(device, 0, kSquashfsSuperBlockSize, &header))
+ return false;
+ return GetSquashfs4Size(header.data(), header.size(), nullptr, nullptr);
+}
+
string GetPathOnBoard(const string& command) {
int return_code = 0;
string command_path;
diff --git a/utils.h b/utils.h
index c820ab2..2374a94 100644
--- a/utils.h
+++ b/utils.h
@@ -237,6 +237,16 @@
int* out_block_count,
int* out_block_size);
+// Returns whether the filesystem is an ext[234] filesystem. In case of failure,
+// such as if the file |device| doesn't exists or can't be read, it returns
+// false.
+bool IsExtFilesystem(const std::string& device);
+
+// Returns whether the filesystem is a squashfs filesystem. In case of failure,
+// such as if the file |device| doesn't exists or can't be read, it returns
+// false.
+bool IsSquashfsFilesystem(const std::string& device);
+
// Returns the path of the passed |command| on the board. This uses the
// environment variable SYSROOT to determine the path to the command on the
// board instead of the path on the running environment.