Merge "Check AServiceManager_isDeclared before AServiceManager_getService"
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3a135bc..287285b 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -396,7 +396,7 @@
ConnectedDevicesStorage storage;
std::set<std::string> devices;
- {
+ if (storage.Exists()) {
FileLock lock = storage.Lock();
devices = storage.ReadDevices(lock);
}
@@ -1780,32 +1780,11 @@
}
DetermineSlot();
- CollectImages();
CancelSnapshotIfNeeded();
- std::vector<char> contents;
- if (!fp_->source->ReadFile("fastboot-info.txt", &contents)) {
- LOG(VERBOSE) << "Flashing from hardcoded images. fastboot-info.txt is empty or does not "
- "exist";
- HardcodedFlash();
- return;
- }
-
- std::vector<std::unique_ptr<Task>> tasks =
- ParseFastbootInfo(fp_, Split({contents.data(), contents.size()}, "\n"));
-
- if (tasks.empty()) {
- LOG(FATAL) << "Invalid fastboot-info.txt file.";
- }
- LOG(VERBOSE) << "Flashing from fastboot-info.txt";
- for (auto& task : tasks) {
- task->Run();
- }
- if (fp_->wants_wipe) {
- // avoid adding duplicate wipe tasks in fastboot main code.
- fp_->wants_wipe = false;
- }
+ HardcodedFlash();
+ return;
}
void FlashAllTool::CheckRequirements() {
@@ -2251,7 +2230,9 @@
{"version", no_argument, 0, 0},
{0, 0, 0, 0}};
- serial = getenv("ANDROID_SERIAL");
+ serial = getenv("FASTBOOT_DEVICE");
+ if (!serial)
+ serial = getenv("ANDROID_SERIAL");
int c;
while ((c = getopt_long(argc, argv, "a::hls:S:vw", longopts, &longindex)) != -1) {
@@ -2603,10 +2584,13 @@
if (fp->force_flash) {
CancelSnapshotIfNeeded();
}
+ std::vector<std::unique_ptr<Task>> wipe_tasks;
std::vector<std::string> partitions = {"userdata", "cache", "metadata"};
for (const auto& partition : partitions) {
- tasks.emplace_back(std::make_unique<WipeTask>(fp.get(), partition));
+ wipe_tasks.emplace_back(std::make_unique<WipeTask>(fp.get(), partition));
}
+ tasks.insert(tasks.begin(), std::make_move_iterator(wipe_tasks.begin()),
+ std::make_move_iterator(wipe_tasks.end()));
}
if (fp->wants_set_active) {
fb->SetActive(next_active);
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 3d6c7b0..6ac26ce 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -28,6 +28,7 @@
#pragma once
#include <cstdlib>
#include <deque>
+#include <functional>
#include <limits>
#include <string>
#include <vector>
diff --git a/fastboot/filesystem.h b/fastboot/filesystem.h
index 5f41fbc..c5f9c94 100644
--- a/fastboot/filesystem.h
+++ b/fastboot/filesystem.h
@@ -31,6 +31,7 @@
#endif
std::string GetHomeDirPath();
+bool FileExists(const std::string& path);
bool EnsureDirectoryExists(const std::string& directory_path);
class FileLock {
diff --git a/fastboot/storage.cpp b/fastboot/storage.cpp
index d6e00cf..629ebc8 100644
--- a/fastboot/storage.cpp
+++ b/fastboot/storage.cpp
@@ -18,29 +18,25 @@
#include <android-base/logging.h>
#include <fstream>
+#include <iterator>
#include "storage.h"
#include "util.h"
ConnectedDevicesStorage::ConnectedDevicesStorage() {
- const std::string home_path = GetHomeDirPath();
- if (home_path.empty()) {
- return;
- }
-
- const std::string home_fastboot_path = home_path + kPathSeparator + ".fastboot";
-
- if (!EnsureDirectoryExists(home_fastboot_path)) {
- LOG(FATAL) << "Cannot create directory: " << home_fastboot_path;
- }
+ home_fastboot_path_ = GetHomeDirPath() + kPathSeparator + ".fastboot";
+ devices_path_ = home_fastboot_path_ + kPathSeparator + "devices";
// We're using a separate file for locking because the Windows LockFileEx does not
// permit opening a file stream for the locked file, even within the same process. So,
// we have to use fd or handle API to manipulate the storage files, which makes it
// nearly impossible to fully rewrite a file content without having to recreate it.
// Unfortunately, this is not an option during holding a lock.
- devices_path_ = home_fastboot_path + kPathSeparator + "devices";
- devices_lock_path_ = home_fastboot_path + kPathSeparator + "devices.lock";
+ devices_lock_path_ = home_fastboot_path_ + kPathSeparator + "devices.lock";
+}
+
+bool ConnectedDevicesStorage::Exists() const {
+ return FileExists(devices_path_);
}
void ConnectedDevicesStorage::WriteDevices(const FileLock&, const std::set<std::string>& devices) {
@@ -63,5 +59,8 @@
}
FileLock ConnectedDevicesStorage::Lock() const {
+ if (!EnsureDirectoryExists(home_fastboot_path_)) {
+ LOG(FATAL) << "Cannot create directory: " << home_fastboot_path_;
+ }
return FileLock(devices_lock_path_);
-}
\ No newline at end of file
+}
diff --git a/fastboot/storage.h b/fastboot/storage.h
index 0cc3d86..ae9d846 100644
--- a/fastboot/storage.h
+++ b/fastboot/storage.h
@@ -24,13 +24,15 @@
class ConnectedDevicesStorage {
public:
ConnectedDevicesStorage();
+
+ bool Exists() const;
void WriteDevices(const FileLock&, const std::set<std::string>& devices);
std::set<std::string> ReadDevices(const FileLock&);
void Clear(const FileLock&);
-
FileLock Lock() const;
private:
+ std::string home_fastboot_path_;
std::string devices_path_;
std::string devices_lock_path_;
};
\ No newline at end of file
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 9ce2cfd..03f9b89 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -46,6 +46,14 @@
do_for_partitions(pname_, slot_, flash, true);
}
+std::string FlashTask::ToString() {
+ std::string apply_vbmeta_string = "";
+ if (apply_vbmeta_) {
+ apply_vbmeta_string = " --apply_vbmeta";
+ }
+ return "flash" + apply_vbmeta_string + " " + pname_ + " " + fname_;
+}
+
std::string FlashTask::GetPartitionAndSlot() {
auto slot = slot_;
if (slot.empty()) {
@@ -84,6 +92,10 @@
}
}
+std::string RebootTask::ToString() {
+ return "reboot " + reboot_target_;
+}
+
FlashSuperLayoutTask::FlashSuperLayoutTask(const std::string& super_name,
std::unique_ptr<SuperFlashHelper> helper,
SparsePtr sparse_layout, uint64_t super_size)
@@ -106,6 +118,9 @@
// Send the data to the device.
flash_partition_files(super_name_, files);
}
+std::string FlashSuperLayoutTask::ToString() {
+ return "optimized-flash-super";
+}
std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::Initialize(
const FlashingPlan* fp, std::vector<ImageEntry>& os_images) {
@@ -263,6 +278,9 @@
}
fp_->fb->RawCommand(command, "Updating super partition");
}
+std::string UpdateSuperTask::ToString() {
+ return "update-super";
+}
ResizeTask::ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
const std::string& slot)
@@ -277,12 +295,20 @@
do_for_partitions(pname_, slot_, resize_partition, false);
}
+std::string ResizeTask::ToString() {
+ return "resize " + pname_;
+}
+
DeleteTask::DeleteTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
void DeleteTask::Run() {
fp_->fb->DeletePartition(pname_);
}
+std::string DeleteTask::ToString() {
+ return "delete " + pname_;
+}
+
WipeTask::WipeTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
void WipeTask::Run() {
@@ -298,3 +324,7 @@
}
fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options);
}
+
+std::string WipeTask::ToString() {
+ return "erase " + pname_;
+}
diff --git a/fastboot/task.h b/fastboot/task.h
index 82e8ebf..500655d 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -35,6 +35,8 @@
public:
Task() = default;
virtual void Run() = 0;
+ virtual std::string ToString() = 0;
+
virtual FlashTask* AsFlashTask() { return nullptr; }
virtual RebootTask* AsRebootTask() { return nullptr; }
virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
@@ -49,11 +51,12 @@
const bool apply_vbmeta, const FlashingPlan* fp);
virtual FlashTask* AsFlashTask() override { return this; }
+ void Run() override;
+ std::string ToString() override;
std::string GetPartition() { return pname_; }
std::string GetImageName() { return fname_; }
- std::string GetPartitionAndSlot();
std::string GetSlot() { return slot_; }
- void Run() override;
+ std::string GetPartitionAndSlot();
private:
const std::string pname_;
@@ -69,6 +72,7 @@
RebootTask(const FlashingPlan* fp, const std::string& reboot_target);
virtual RebootTask* AsRebootTask() override { return this; }
void Run() override;
+ std::string ToString() override;
private:
const std::string reboot_target_ = "";
@@ -85,6 +89,7 @@
const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
using ImageEntry = std::pair<const Image*, std::string>;
void Run() override;
+ std::string ToString() override;
private:
const std::string super_name_;
@@ -99,6 +104,7 @@
virtual UpdateSuperTask* AsUpdateSuperTask() override { return this; }
void Run() override;
+ std::string ToString() override;
private:
const FlashingPlan* fp_;
@@ -109,6 +115,7 @@
ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
const std::string& slot);
void Run() override;
+ std::string ToString() override;
private:
const FlashingPlan* fp_;
@@ -121,6 +128,7 @@
public:
DeleteTask(const FlashingPlan* fp, const std::string& pname);
void Run() override;
+ std::string ToString() override;
private:
const FlashingPlan* fp_;
@@ -131,8 +139,8 @@
public:
WipeTask(const FlashingPlan* fp, const std::string& pname);
virtual WipeTask* AsWipeTask() override { return this; }
-
void Run() override;
+ std::string ToString() override;
private:
const FlashingPlan* fp_;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 598a3d2..c3c10ba 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -242,7 +242,9 @@
LWARNING << "Warning: zramsize= flag malformed: " << arg;
}
}
- } else if (StartsWith(flag, "fileencryption=")) {
+ } else if (StartsWith(flag, "fileencryption=") || flag == "fileencryption") {
+ // "fileencryption" enables file-based encryption. It's normally followed by an = and
+ // then the encryption options. But that can be omitted to use the default options.
ParseFileEncryption(arg, entry);
} else if (StartsWith(flag, "max_comp_streams=")) {
if (!ParseInt(arg, &entry->max_comp_streams)) {
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 5deba65..ddda648 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -93,6 +93,9 @@
test_options: {
min_shipping_api_level: 29,
},
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
require_root: true,
}
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index c65481b..bd97a78 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -22,21 +22,25 @@
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <string>
+#include <utility>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include <fstab/fstab.h>
#include <gtest/gtest.h>
#include <libdm/loop_control.h>
#include <libfiemap/fiemap_writer.h>
#include <libfiemap/split_fiemap_writer.h>
#include <libgsi/libgsi.h>
+#include <storage_literals/storage_literals.h>
#include "utility.h"
@@ -46,6 +50,7 @@
using namespace std;
using namespace std::string_literals;
using namespace android::fiemap;
+using namespace android::storage_literals;
using unique_fd = android::base::unique_fd;
using LoopDevice = android::dm::LoopDevice;
@@ -427,90 +432,123 @@
ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
}
-class VerifyBlockWritesExt4 : public ::testing::Test {
+// Get max file size and free space.
+std::pair<uint64_t, uint64_t> GetBigFileLimit(const std::string& mount_point) {
+ struct statvfs fs;
+ if (statvfs(mount_point.c_str(), &fs) < 0) {
+ PLOG(ERROR) << "statfs failed";
+ return {0, 0};
+ }
+
+ auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);
+ auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;
+
+ LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free;
+
+ return {fs_limit, fs_free};
+}
+
+class FsTest : public ::testing::Test {
+ protected:
// 2GB Filesystem and 4k block size by default
static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
+ static constexpr uint64_t fs_size = 64 * 1024 * 1024;
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
+ void SetUp() {
+ android::fs_mgr::Fstab fstab;
+ ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
+
+ ASSERT_EQ(access(tmpdir_.path, F_OK), 0);
+ fs_path_ = tmpdir_.path + "/fs_image"s;
+ mntpoint_ = tmpdir_.path + "/mnt_point"s;
+
+ auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
+ ASSERT_NE(entry, nullptr);
+ if (entry->fs_type == "ext4") {
+ SetUpExt4();
+ } else if (entry->fs_type == "f2fs") {
+ SetUpF2fs();
+ } else {
+ FAIL() << "Unrecognized fs_type: " << entry->fs_type;
+ }
+ }
+
+ void SetUpExt4() {
uint64_t count = fs_size / block_size;
std::string dd_cmd =
::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
" count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
+ fs_path_.c_str(), block_size, count);
std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
+ ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path_.c_str());
// create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+ ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
// create file for the file system
int ret = system(dd_cmd.c_str());
ASSERT_EQ(ret, 0);
// Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path, 10s);
+ LoopDevice loop_dev(fs_path_, 10s);
ASSERT_TRUE(loop_dev.valid());
// create file system
ret = system(mkfs_cmd.c_str());
ASSERT_EQ(ret, 0);
// mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "ext4", 0, nullptr), 0);
}
- void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
- }
-
- std::string mntpoint;
- std::string fs_path;
-};
-
-class VerifyBlockWritesF2fs : public ::testing::Test {
- // 2GB Filesystem and 4k block size by default
- static constexpr uint64_t block_size = 4096;
- static constexpr uint64_t fs_size = 2147483648;
-
- protected:
- void SetUp() override {
- fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
+ void SetUpF2fs() {
uint64_t count = fs_size / block_size;
std::string dd_cmd =
::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
" count=%" PRIu64 " > /dev/null 2>&1",
- fs_path.c_str(), block_size, count);
+ fs_path_.c_str(), block_size, count);
std::string mkfs_cmd =
- ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
+ ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path_.c_str());
// create mount point
- mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
- ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+ ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
// create file for the file system
int ret = system(dd_cmd.c_str());
ASSERT_EQ(ret, 0);
// Get and attach a loop device to the filesystem we created
- LoopDevice loop_dev(fs_path, 10s);
+ LoopDevice loop_dev(fs_path_, 10s);
ASSERT_TRUE(loop_dev.valid());
// create file system
ret = system(mkfs_cmd.c_str());
ASSERT_EQ(ret, 0);
// mount the file system
- ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0);
}
void TearDown() override {
- umount(mntpoint.c_str());
- rmdir(mntpoint.c_str());
- unlink(fs_path.c_str());
+ umount(mntpoint_.c_str());
+ rmdir(mntpoint_.c_str());
+ unlink(fs_path_.c_str());
}
- std::string mntpoint;
- std::string fs_path;
+ TemporaryDir tmpdir_;
+ std::string mntpoint_;
+ std::string fs_path_;
};
+TEST_F(FsTest, LowSpaceError) {
+ auto limits = GetBigFileLimit(mntpoint_);
+ ASSERT_GE(limits.first, 0);
+
+ FiemapUniquePtr ptr;
+
+ auto test_file = mntpoint_ + "/big_file";
+ auto status = FiemapWriter::Open(test_file, limits.first, &ptr);
+ ASSERT_FALSE(status.is_ok());
+ ASSERT_EQ(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
+
+ // Also test for EFBIG.
+ status = FiemapWriter::Open(test_file, 16_TiB, &ptr);
+ ASSERT_FALSE(status.is_ok());
+ ASSERT_NE(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
+}
+
bool DetermineBlockSize() {
struct statfs s;
if (statfs(gTestDir.c_str(), &s)) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index d3bd904..e931bec 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -174,11 +174,13 @@
"libsnapshot_cow_defaults",
],
srcs: [
- "libsnapshot_cow/cow_decompress.cpp",
- "libsnapshot_cow/cow_reader.cpp",
- "libsnapshot_cow/cow_writer.cpp",
- "libsnapshot_cow/cow_format.cpp",
"libsnapshot_cow/cow_compress.cpp",
+ "libsnapshot_cow/cow_decompress.cpp",
+ "libsnapshot_cow/cow_format.cpp",
+ "libsnapshot_cow/cow_reader.cpp",
+ "libsnapshot_cow/parser_v2.cpp",
+ "libsnapshot_cow/writer_base.cpp",
+ "libsnapshot_cow/writer_v2.cpp",
],
host_supported: true,
recovery_available: true,
@@ -370,7 +372,7 @@
"libsnapshot_cow_defaults",
],
srcs: [
- "libsnapshot_cow/cow_api_test.cpp",
+ "libsnapshot_cow/test_v2.cpp",
],
cflags: [
"-D_FILE_OFFSET_BITS=64",
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index c3ca00a..dd626bc 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -28,6 +28,13 @@
static constexpr uint32_t kCowVersionManifest = 2;
+static constexpr uint32_t kMinCowVersion = 1;
+static constexpr uint32_t kMaxCowVersion = 2;
+
+// Normally, this should be kMaxCowVersion. When a new version is under testing
+// it may be the previous value of kMaxCowVersion.
+static constexpr uint32_t kDefaultCowVersion = 2;
+
// This header appears as the first sequence of bytes in the COW. All fields
// in the layout are little-endian encoded. The on-disk layout is:
//
@@ -52,13 +59,15 @@
// between writing the last operation/data pair, or the footer itself. In this
// case, the safest way to proceed is to assume the last operation is faulty.
-struct CowHeader {
+struct CowHeaderPrefix {
uint64_t magic;
uint16_t major_version;
uint16_t minor_version;
+ uint16_t header_size; // size of CowHeader.
+} __attribute__((packed));
- // Size of this struct.
- uint16_t header_size;
+struct CowHeader {
+ CowHeaderPrefix prefix;
// Size of footer struct
uint16_t footer_size;
@@ -88,7 +97,7 @@
// the compression type of that data (see constants below).
uint8_t compression;
- // Length of Footer Data. Currently 64 for both checksums
+ // Length of Footer Data. Currently 64.
uint16_t data_length;
// The amount of file space used by Cow operations
@@ -98,14 +107,6 @@
uint64_t num_ops;
} __attribute__((packed));
-struct CowFooterData {
- // SHA256 checksums of Footer op
- uint8_t footer_checksum[32];
-
- // SHA256 of the operation sequence.
- uint8_t ops_checksum[32];
-} __attribute__((packed));
-
// Cow operations are currently fixed-size entries, but this may change if
// needed.
struct CowOperation {
@@ -167,7 +168,7 @@
struct CowFooter {
CowFooterOperation op;
- CowFooterData data;
+ uint8_t unused[64];
} __attribute__((packed));
struct ScratchMetadata {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index c7b83a8..af2d3ef 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -1,16 +1,16 @@
-// Copyright (C) 2019 The Android Open Source Project
+// copyright (c) 2019 the android open source project
//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
+// licensed under the apache license, version 2.0 (the "license");
+// you may not use this file except in compliance with the license.
+// you may obtain a copy of the license at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/license-2.0
//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// unless required by applicable law or agreed to in writing, software
+// distributed under the license is distributed on an "as is" basis,
+// without warranties or conditions of any kind, either express or implied.
+// see the license for the specific language governing permissions and
+// limitations under the license.
#pragma once
@@ -61,30 +61,28 @@
// will occur in the sequence they were added to the COW.
class ICowWriter {
public:
- explicit ICowWriter(const CowOptions& options) : options_(options) {}
-
virtual ~ICowWriter() {}
// Encode an operation that copies the contents of |old_block| to the
// location of |new_block|. 'num_blocks' is the number of contiguous
// COPY operations from |old_block| to |new_block|.
- bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1);
+ virtual bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
// Encode a sequence of raw blocks. |size| must be a multiple of the block size.
- bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
+ virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
// Add a sequence of xor'd blocks. |size| must be a multiple of the block size.
- bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
- uint16_t offset);
+ virtual bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) = 0;
// Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
- bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
+ virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
// Add a label to the op sequence.
- bool AddLabel(uint64_t label);
+ virtual bool AddLabel(uint64_t label) = 0;
// Add sequence data for op merging. Data is a list of the destination block numbers.
- bool AddSequenceData(size_t num_ops, const uint32_t* data);
+ virtual bool AddSequenceData(size_t num_ops, const uint32_t* data) = 0;
// Flush all pending writes. This must be called before closing the writer
// to ensure that the correct headers and footers are written.
@@ -93,24 +91,8 @@
// Return number of bytes the cow image occupies on disk.
virtual uint64_t GetCowSize() = 0;
- // Returns true if AddCopy() operations are supported.
- virtual bool SupportsCopyOperation() const { return true; }
-
- const CowOptions& options() { return options_; }
-
- protected:
- virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
- virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
- virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
- uint32_t old_block, uint16_t offset) = 0;
- virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
- virtual bool EmitLabel(uint64_t label) = 0;
- virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
-
- bool ValidateNewBlock(uint64_t new_block);
-
- protected:
- CowOptions options_;
+ virtual uint32_t GetBlockSize() const = 0;
+ virtual std::optional<uint32_t> GetMaxBlocks() const = 0;
};
class CompressWorker {
@@ -149,98 +131,15 @@
std::vector<std::basic_string<uint8_t>>* compressed_data);
};
-class CowWriter : public ICowWriter {
- public:
- explicit CowWriter(const CowOptions& options);
- ~CowWriter();
+// Create an ICowWriter not backed by any file. This is useful for estimating
+// the final size of a cow file.
+std::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options);
- // Set up the writer.
- // The file starts from the beginning.
- //
- // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
- // computing COW sizes without using storage space.
- bool Initialize(android::base::unique_fd&& fd);
- bool Initialize(android::base::borrowed_fd fd);
- // Set up a writer, assuming that the given label is the last valid label.
- // This will result in dropping any labels that occur after the given on, and will fail
- // if the given label does not appear.
- bool InitializeAppend(android::base::unique_fd&&, uint64_t label);
- bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label);
-
- bool Finalize() override;
-
- uint64_t GetCowSize() override;
-
- uint32_t GetCowVersion() { return header_.major_version; }
-
- protected:
- virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
- virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
- uint32_t old_block, uint16_t offset) override;
- virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- virtual bool EmitLabel(uint64_t label) override;
- virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
-
- private:
- bool EmitCluster();
- bool EmitClusterIfNeeded();
- bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
- uint16_t offset, uint8_t type);
- void SetupHeaders();
- void SetupWriteOptions();
- bool ParseOptions();
- bool OpenForWrite();
- bool OpenForAppend(uint64_t label);
- bool GetDataPos(uint64_t* pos);
- bool WriteRawData(const void* data, size_t size);
- bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
- void AddOperation(const CowOperation& op);
- void InitPos();
- void InitBatchWrites();
- void InitWorkers();
- bool FlushCluster();
-
- bool CompressBlocks(size_t num_blocks, const void* data);
- bool SetFd(android::base::borrowed_fd fd);
- bool Sync();
- bool Truncate(off_t length);
- bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;
-
- private:
- android::base::unique_fd owned_fd_;
- android::base::borrowed_fd fd_;
- CowHeader header_{};
- CowFooter footer_{};
- CowCompressionAlgorithm compression_ = kCowCompressNone;
- uint64_t current_op_pos_ = 0;
- uint64_t next_op_pos_ = 0;
- uint64_t next_data_pos_ = 0;
- uint64_t current_data_pos_ = 0;
- ssize_t total_data_written_ = 0;
- uint32_t cluster_size_ = 0;
- uint32_t current_cluster_size_ = 0;
- uint64_t current_data_size_ = 0;
- bool is_dev_null_ = false;
- bool merge_in_progress_ = false;
- bool is_block_device_ = false;
- uint64_t cow_image_size_ = INT64_MAX;
-
- int num_compress_threads_ = 1;
- std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
- std::vector<std::future<bool>> threads_;
- std::vector<std::basic_string<uint8_t>> compressed_buf_;
- std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
-
- std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_;
- std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
- std::unique_ptr<struct iovec[]> cowop_vec_;
- int op_vec_index_ = 0;
-
- std::unique_ptr<struct iovec[]> data_vec_;
- int data_vec_index_ = 0;
- bool batch_write_ = false;
-};
+// Create an ICowWriter of the given version and options. If a label is given,
+// the writer is opened in append mode.
+std::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,
+ android::base::unique_fd&& fd,
+ std::optional<uint64_t> label = {});
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
index 29828bc..52e3a9c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -23,33 +23,23 @@
public:
using FileDescriptor = ISnapshotWriter::FileDescriptor;
- explicit MockSnapshotWriter(const CowOptions& options) : ISnapshotWriter(options) {}
- MockSnapshotWriter() : ISnapshotWriter({}) {}
-
MOCK_METHOD(bool, Finalize, (), (override));
// Return number of bytes the cow image occupies on disk.
MOCK_METHOD(uint64_t, GetCowSize, (), (override));
- // Returns true if AddCopy() operations are supported.
- MOCK_METHOD(bool, SupportsCopyOperation, (), (const override));
-
- MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t, uint64_t), (override));
- MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
- MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
+ MOCK_METHOD(bool, AddCopy, (uint64_t, uint64_t, uint64_t), (override));
+ MOCK_METHOD(bool, AddRawBlocks, (uint64_t, const void*, size_t), (override));
+ MOCK_METHOD(bool, AddXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
(override));
- MOCK_METHOD(bool, EmitZeroBlocks, (uint64_t, uint64_t), (override));
- MOCK_METHOD(bool, EmitLabel, (uint64_t), (override));
- MOCK_METHOD(bool, EmitSequenceData, (size_t, const uint32_t*), (override));
-
- // Open the writer in write mode (no append).
+ MOCK_METHOD(bool, AddZeroBlocks, (uint64_t, uint64_t), (override));
+ MOCK_METHOD(bool, AddLabel, (uint64_t), (override));
+ MOCK_METHOD(bool, AddSequenceData, (size_t, const uint32_t*), (override));
MOCK_METHOD(bool, Initialize, (), (override));
+ MOCK_METHOD(bool, InitializeAppend, (uint64_t), (override));
MOCK_METHOD(bool, VerifyMergeOps, (), (override, const, noexcept));
-
- // Open the writer in append mode, with the last label to resume
- // from. See CowWriter::InitializeAppend.
- MOCK_METHOD(bool, InitializeAppend, (uint64_t label), (override));
-
MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenReader, (), (override));
+ MOCK_METHOD(uint32_t, GetBlockSize, (), (override, const));
+ MOCK_METHOD(std::optional<uint32_t>, GetMaxBlocks, (), (override, const));
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 9eb89b6..ecf1d15 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -697,10 +697,6 @@
LockedFile* lock, const std::optional<std::string>& source_device,
const std::string& partition_name, const SnapshotStatus& status,
const SnapshotPaths& paths);
- std::unique_ptr<ISnapshotWriter> OpenKernelSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths);
// Map the base device, COW devices, and snapshot device.
bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index 0e3b1db..2653a60 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -31,13 +31,7 @@
public:
using FileDescriptor = chromeos_update_engine::FileDescriptor;
- explicit ISnapshotWriter(const CowOptions& options);
-
- // Set the source device. This is used for AddCopy() operations, if the
- // underlying writer needs the original bytes (for example if backed by
- // dm-snapshot or if writing directly to an unsnapshotted region). The
- // device is only opened on the first operation that requires it.
- void SetSourceDevice(const std::string& source_device);
+ virtual ~ISnapshotWriter() {}
// Open the writer in write mode (no append).
virtual bool Initialize() = 0;
@@ -47,15 +41,8 @@
virtual bool InitializeAppend(uint64_t label) = 0;
virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
+
virtual bool VerifyMergeOps() const noexcept = 0;
-
- protected:
- android::base::borrowed_fd GetSourceFd();
-
- std::optional<std::string> source_device_;
-
- private:
- android::base::unique_fd source_fd_;
};
// Send writes to a COW or a raw device directly, based on a threshold.
@@ -63,6 +50,8 @@
public:
CompressedSnapshotWriter(const CowOptions& options);
+ void SetSourceDevice(const std::string& source_device);
+
// Sets the COW device; this is required.
bool SetCowDevice(android::base::unique_fd&& cow_device);
@@ -70,56 +59,34 @@
bool InitializeAppend(uint64_t label) override;
bool Finalize() override;
uint64_t GetCowSize() override;
+ uint32_t GetBlockSize() const override;
+ std::optional<uint32_t> GetMaxBlocks() const override;
std::unique_ptr<FileDescriptor> OpenReader() override;
bool VerifyMergeOps() const noexcept;
- protected:
- bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
- bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
- uint16_t offset) override;
- bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- bool EmitLabel(uint64_t label) override;
- bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+ bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
+ bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+ uint16_t offset) override;
+ bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ bool AddLabel(uint64_t label) override;
+ bool AddSequenceData(size_t num_ops, const uint32_t* data) override;
private:
std::unique_ptr<CowReader> OpenCowReader() const;
+ android::base::borrowed_fd GetSourceFd();
+
+ CowOptions options_;
+
+ // Set the source device. This is used for AddCopy() operations, if the
+ // underlying writer needs the original bytes (for example if backed by
+ // dm-snapshot or if writing directly to an unsnapshotted region). The
+ // device is only opened on the first operation that requires it.
+ std::optional<std::string> source_device_;
+ android::base::unique_fd source_fd_;
+
android::base::unique_fd cow_device_;
-
- std::unique_ptr<CowWriter> cow_;
-};
-
-// Write directly to a dm-snapshot device.
-class OnlineKernelSnapshotWriter final : public ISnapshotWriter {
- public:
- OnlineKernelSnapshotWriter(const CowOptions& options);
-
- // Set the device used for all writes.
- void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
-
- bool Initialize() override { return true; }
- bool InitializeAppend(uint64_t) override { return true; }
-
- bool Finalize() override;
- uint64_t GetCowSize() override { return cow_size_; }
- std::unique_ptr<FileDescriptor> OpenReader() override;
-
- // Online kernel snapshot writer doesn't care about merge sequences.
- // So ignore.
- bool VerifyMergeOps() const noexcept override { return true; }
-
- protected:
- bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
- bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
- bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
- uint16_t offset) override;
- bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
- bool EmitLabel(uint64_t label) override;
- bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
-
- private:
- android::base::unique_fd snapshot_fd_;
- uint64_t cow_size_ = 0;
+ std::unique_ptr<ICowWriter> cow_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index 2157d0f..ff3ccec 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -19,10 +19,13 @@
#include <unistd.h>
#include <android-base/logging.h>
+#include "writer_v2.h"
namespace android {
namespace snapshot {
+using android::base::unique_fd;
+
std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
os << "CowOperation(type:";
if (op.type == kCowCopyOp)
@@ -103,5 +106,27 @@
}
}
+std::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,
+ unique_fd&& fd, std::optional<uint64_t> label) {
+ std::unique_ptr<CowWriterBase> base;
+ switch (version) {
+ case 1:
+ case 2:
+ base = std::make_unique<CowWriterV2>(options, std::move(fd));
+ break;
+ default:
+ LOG(ERROR) << "Cannot create unknown cow version: " << version;
+ return nullptr;
+ }
+ if (!base->Initialize(label)) {
+ return nullptr;
+ }
+ return base;
+}
+
+std::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options) {
+ return CreateCowWriter(version, options, unique_fd{-1}, std::nullopt);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 6749cf1..c2a7fdb 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -30,7 +30,7 @@
#include <zlib.h>
#include "cow_decompress.h"
-#include "libsnapshot/cow_format.h"
+#include "parser_v2.h"
namespace android {
namespace snapshot {
@@ -43,15 +43,6 @@
reader_flag_(reader_flag),
is_merge_(is_merge) {}
-static void SHA256(const void*, size_t, uint8_t[]) {
-#if 0
- SHA256_CTX c;
- SHA256_Init(&c);
- SHA256_Update(&c, data, length);
- SHA256_Final(out, &c);
-#endif
-}
-
std::unique_ptr<CowReader> CowReader::CloneCowReader() {
auto cow = std::make_unique<CowReader>();
cow->owned_fd_.reset();
@@ -63,7 +54,6 @@
cow->merge_op_start_ = merge_op_start_;
cow->num_total_data_ops_ = num_total_data_ops_;
cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
- cow->has_seq_ops_ = has_seq_ops_;
cow->data_loc_ = data_loc_;
cow->block_pos_index_ = block_pos_index_;
cow->is_merge_ = is_merge_;
@@ -101,217 +91,26 @@
bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label) {
fd_ = fd;
- auto pos = lseek(fd_.get(), 0, SEEK_END);
- if (pos < 0) {
- PLOG(ERROR) << "lseek end failed";
- return false;
- }
- fd_size_ = pos;
-
- if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek header failed";
- return false;
- }
- if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
- PLOG(ERROR) << "read header failed";
+ if (!ReadCowHeader(fd, &header_)) {
return false;
}
- if (header_.magic != kCowMagicNumber) {
- LOG(ERROR) << "Header Magic corrupted. Magic: " << header_.magic
- << "Expected: " << kCowMagicNumber;
- return false;
- }
- if (header_.footer_size != sizeof(CowFooter)) {
- LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
- << sizeof(CowFooter);
- return false;
- }
- if (header_.op_size != sizeof(CowOperation)) {
- LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
- return false;
- }
- if (header_.cluster_ops == 1) {
- LOG(ERROR) << "Clusters must contain at least two operations to function.";
- return false;
- }
- if (header_.op_size != sizeof(CowOperation)) {
- LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
- << sizeof(CowOperation);
- return false;
- }
- if (header_.cluster_ops == 1) {
- LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ CowParserV2 parser;
+ if (!parser.Parse(fd, header_, label)) {
return false;
}
- if ((header_.major_version > kCowVersionMajor) || (header_.minor_version != kCowVersionMinor)) {
- LOG(ERROR) << "Header version mismatch";
- LOG(ERROR) << "Major version: " << header_.major_version
- << "Expected: " << kCowVersionMajor;
- LOG(ERROR) << "Minor version: " << header_.minor_version
- << "Expected: " << kCowVersionMinor;
- return false;
- }
+ footer_ = parser.footer();
+ fd_size_ = parser.fd_size();
+ last_label_ = parser.last_label();
+ ops_ = std::move(parser.ops());
+ data_loc_ = parser.data_loc();
- if (!ParseOps(label)) {
- return false;
- }
// If we're resuming a write, we're not ready to merge
if (label.has_value()) return true;
return PrepMergeOps();
}
-bool CowReader::ParseOps(std::optional<uint64_t> label) {
- uint64_t pos;
- auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
-
- // Skip the scratch space
- if (header_.major_version >= 2 && (header_.buffer_size > 0)) {
- LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
- size_t init_offset = header_.header_size + header_.buffer_size;
- pos = lseek(fd_.get(), init_offset, SEEK_SET);
- if (pos != init_offset) {
- PLOG(ERROR) << "lseek ops failed";
- return false;
- }
- } else {
- pos = lseek(fd_.get(), header_.header_size, SEEK_SET);
- if (pos != header_.header_size) {
- PLOG(ERROR) << "lseek ops failed";
- return false;
- }
- // Reading a v1 version of COW which doesn't have buffer_size.
- header_.buffer_size = 0;
- }
- uint64_t data_pos = 0;
-
- if (header_.cluster_ops) {
- data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
- } else {
- data_pos = pos + sizeof(CowOperation);
- }
-
- auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
- uint64_t current_op_num = 0;
- uint64_t cluster_ops = header_.cluster_ops ?: 1;
- bool done = false;
-
- // Alternating op clusters and data
- while (!done) {
- uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
- if (to_add == 0) break;
- ops_buffer->resize(current_op_num + to_add);
- if (!android::base::ReadFully(fd_, &ops_buffer->data()[current_op_num],
- to_add * sizeof(CowOperation))) {
- PLOG(ERROR) << "read op failed";
- return false;
- }
- // Parse current cluster to find start of next cluster
- while (current_op_num < ops_buffer->size()) {
- auto& current_op = ops_buffer->data()[current_op_num];
- current_op_num++;
- if (current_op.type == kCowXorOp) {
- data_loc->insert({current_op.new_block, data_pos});
- }
- pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
- data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
-
- if (current_op.type == kCowClusterOp) {
- break;
- } else if (current_op.type == kCowLabelOp) {
- last_label_ = {current_op.source};
-
- // If we reach the requested label, stop reading.
- if (label && label.value() == current_op.source) {
- done = true;
- break;
- }
- } else if (current_op.type == kCowFooterOp) {
- footer_.emplace();
- CowFooter* footer = &footer_.value();
- memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
- off_t offs = lseek(fd_.get(), pos, SEEK_SET);
- if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
- PLOG(ERROR) << "lseek next op failed " << offs;
- return false;
- }
- if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
- LOG(ERROR) << "Could not read COW footer";
- return false;
- }
-
- // Drop the footer from the op stream.
- current_op_num--;
- done = true;
- break;
- } else if (current_op.type == kCowSequenceOp) {
- has_seq_ops_ = true;
- }
- }
-
- // Position for next cluster read
- off_t offs = lseek(fd_.get(), pos, SEEK_SET);
- if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
- PLOG(ERROR) << "lseek next op failed " << offs;
- return false;
- }
- ops_buffer->resize(current_op_num);
- }
-
- LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
- // To successfully parse a COW file, we need either:
- // (1) a label to read up to, and for that label to be found, or
- // (2) a valid footer.
- if (label) {
- if (!last_label_) {
- LOG(ERROR) << "Did not find label " << label.value()
- << " while reading COW (no labels found)";
- return false;
- }
- if (last_label_.value() != label.value()) {
- LOG(ERROR) << "Did not find label " << label.value()
- << ", last label=" << last_label_.value();
- return false;
- }
- } else if (!footer_) {
- LOG(ERROR) << "No COW footer found";
- return false;
- }
-
- uint8_t csum[32];
- memset(csum, 0, sizeof(uint8_t) * 32);
-
- if (footer_) {
- if (ops_buffer->size() != footer_->op.num_ops) {
- LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
- << ops_buffer->size();
- return false;
- }
- if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
- LOG(ERROR) << "ops size does not match ";
- return false;
- }
- SHA256(&footer_->op, sizeof(footer_->op), footer_->data.footer_checksum);
- if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
- LOG(ERROR) << "ops checksum does not match";
- return false;
- }
- SHA256(ops_buffer->data(), footer_->op.ops_size, csum);
- if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
- LOG(ERROR) << "ops checksum does not match";
- return false;
- }
- }
-
- ops_ = ops_buffer;
- ops_->shrink_to_fit();
- data_loc_ = data_loc;
-
- return true;
-}
-
//
// This sets up the data needed for MergeOpIter. MergeOpIter presents
// data in the order we intend to merge in.
@@ -446,7 +245,8 @@
continue;
}
- if (!has_seq_ops_ && IsOrderedOp(current_op)) {
+ // Sequence ops must be the first ops in the stream.
+ if (seq_ops_set.empty() && IsOrderedOp(current_op)) {
merge_op_blocks->emplace_back(current_op.new_block);
} else if (seq_ops_set.count(current_op.new_block) == 0) {
other_ops.push_back(current_op.new_block);
@@ -718,8 +518,8 @@
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
// Validate the offset, taking care to acknowledge possible overflow of offset+len.
- if (offset < header_.header_size || offset >= fd_size_ - sizeof(CowFooter) || len >= fd_size_ ||
- offset + len > fd_size_ - sizeof(CowFooter)) {
+ if (offset < header_.prefix.header_size || offset >= fd_size_ - sizeof(CowFooter) ||
+ len >= fd_size_ || offset + len > fd_size_ - sizeof(CowFooter)) {
LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
return false;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index 917cec4..c2c86ee 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -104,8 +104,9 @@
if (reader.GetFooter(&footer)) has_footer = true;
if (!opt.silent) {
- std::cout << "Version: " << header.major_version << "." << header.minor_version << "\n";
- std::cout << "Header size: " << header.header_size << "\n";
+ std::cout << "Version: " << header.prefix.major_version << "."
+ << header.prefix.minor_version << "\n";
+ std::cout << "Header size: " << header.prefix.header_size << "\n";
std::cout << "Footer size: " << header.footer_size << "\n";
std::cout << "Block size: " << header.block_size << "\n";
std::cout << "Merge ops: " << header.num_merge_ops << "\n";
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
new file mode 100644
index 0000000..fdb5c59
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -0,0 +1,238 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "parser_v2.h"
+
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header) {
+ if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+
+ memset(header, 0, sizeof(*header));
+
+ if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {
+ return false;
+ }
+ if (header->prefix.magic != kCowMagicNumber) {
+ LOG(ERROR) << "Header Magic corrupted. Magic: " << header->prefix.magic
+ << "Expected: " << kCowMagicNumber;
+ return false;
+ }
+ if (header->prefix.header_size > sizeof(CowHeader)) {
+ LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
+ << " bytes, expected at most " << sizeof(CowHeader) << " bytes)";
+ return false;
+ }
+
+ if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+ return android::base::ReadFully(fd, header, header->prefix.header_size);
+}
+
+bool CowParserV2::Parse(borrowed_fd fd, const CowHeader& header, std::optional<uint64_t> label) {
+ auto pos = lseek(fd.get(), 0, SEEK_END);
+ if (pos < 0) {
+ PLOG(ERROR) << "lseek end failed";
+ return false;
+ }
+ fd_size_ = pos;
+ header_ = header;
+
+ if (header_.footer_size != sizeof(CowFooter)) {
+ LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
+ << sizeof(CowFooter);
+ return false;
+ }
+ if (header_.op_size != sizeof(CowOperation)) {
+ LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+ << sizeof(CowOperation);
+ return false;
+ }
+ if (header_.cluster_ops == 1) {
+ LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ return false;
+ }
+ if (header_.op_size != sizeof(CowOperation)) {
+ LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+ << sizeof(CowOperation);
+ return false;
+ }
+ if (header_.cluster_ops == 1) {
+ LOG(ERROR) << "Clusters must contain at least two operations to function.";
+ return false;
+ }
+
+ if ((header_.prefix.major_version > kCowVersionMajor) ||
+ (header_.prefix.minor_version != kCowVersionMinor)) {
+ LOG(ERROR) << "Header version mismatch, "
+ << "major version: " << header_.prefix.major_version
+ << ", expected: " << kCowVersionMajor
+ << ", minor version: " << header_.prefix.minor_version
+ << ", expected: " << kCowVersionMinor;
+ return false;
+ }
+
+ return ParseOps(fd, label);
+}
+
+bool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
+ uint64_t pos;
+ auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+
+ // Skip the scratch space
+ if (header_.prefix.major_version >= 2 && (header_.buffer_size > 0)) {
+ LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
+ size_t init_offset = header_.prefix.header_size + header_.buffer_size;
+ pos = lseek(fd.get(), init_offset, SEEK_SET);
+ if (pos != init_offset) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
+ } else {
+ pos = lseek(fd.get(), header_.prefix.header_size, SEEK_SET);
+ if (pos != header_.prefix.header_size) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
+ // Reading a v1 version of COW which doesn't have buffer_size.
+ header_.buffer_size = 0;
+ }
+ uint64_t data_pos = 0;
+
+ if (header_.cluster_ops) {
+ data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
+ } else {
+ data_pos = pos + sizeof(CowOperation);
+ }
+
+ auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
+ uint64_t current_op_num = 0;
+ uint64_t cluster_ops = header_.cluster_ops ?: 1;
+ bool done = false;
+
+ // Alternating op clusters and data
+ while (!done) {
+ uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
+ if (to_add == 0) break;
+ ops_buffer->resize(current_op_num + to_add);
+ if (!android::base::ReadFully(fd, &ops_buffer->data()[current_op_num],
+ to_add * sizeof(CowOperation))) {
+ PLOG(ERROR) << "read op failed";
+ return false;
+ }
+ // Parse current cluster to find start of next cluster
+ while (current_op_num < ops_buffer->size()) {
+ auto& current_op = ops_buffer->data()[current_op_num];
+ current_op_num++;
+ if (current_op.type == kCowXorOp) {
+ data_loc->insert({current_op.new_block, data_pos});
+ }
+ pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
+ data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
+
+ if (current_op.type == kCowClusterOp) {
+ break;
+ } else if (current_op.type == kCowLabelOp) {
+ last_label_ = {current_op.source};
+
+ // If we reach the requested label, stop reading.
+ if (label && label.value() == current_op.source) {
+ done = true;
+ break;
+ }
+ } else if (current_op.type == kCowFooterOp) {
+ footer_.emplace();
+ CowFooter* footer = &footer_.value();
+ memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
+ off_t offs = lseek(fd.get(), pos, SEEK_SET);
+ if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
+ PLOG(ERROR) << "lseek next op failed " << offs;
+ return false;
+ }
+ if (!android::base::ReadFully(fd, &footer->unused, sizeof(footer->unused))) {
+ LOG(ERROR) << "Could not read COW footer";
+ return false;
+ }
+
+ // Drop the footer from the op stream.
+ current_op_num--;
+ done = true;
+ break;
+ }
+ }
+
+ // Position for next cluster read
+ off_t offs = lseek(fd.get(), pos, SEEK_SET);
+ if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
+ PLOG(ERROR) << "lseek next op failed " << offs;
+ return false;
+ }
+ ops_buffer->resize(current_op_num);
+ }
+
+ LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
+ // To successfully parse a COW file, we need either:
+ // (1) a label to read up to, and for that label to be found, or
+ // (2) a valid footer.
+ if (label) {
+ if (!last_label_) {
+ LOG(ERROR) << "Did not find label " << label.value()
+ << " while reading COW (no labels found)";
+ return false;
+ }
+ if (last_label_.value() != label.value()) {
+ LOG(ERROR) << "Did not find label " << label.value()
+ << ", last label=" << last_label_.value();
+ return false;
+ }
+ } else if (!footer_) {
+ LOG(ERROR) << "No COW footer found";
+ return false;
+ }
+
+ uint8_t csum[32];
+ memset(csum, 0, sizeof(uint8_t) * 32);
+
+ if (footer_) {
+ if (ops_buffer->size() != footer_->op.num_ops) {
+ LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
+ << ops_buffer->size();
+ return false;
+ }
+ if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
+ LOG(ERROR) << "ops size does not match ";
+ return false;
+ }
+ }
+
+ ops_ = ops_buffer;
+ ops_->shrink_to_fit();
+ data_loc_ = data_loc;
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
new file mode 100644
index 0000000..afcf687
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+#include <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+class CowParserV2 {
+ public:
+ bool Parse(android::base::borrowed_fd fd, const CowHeader& header,
+ std::optional<uint64_t> label = {});
+
+ const CowHeader& header() const { return header_; }
+ const std::optional<CowFooter>& footer() const { return footer_; }
+ std::shared_ptr<std::vector<CowOperation>> ops() { return ops_; }
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc() const { return data_loc_; }
+ uint64_t fd_size() const { return fd_size_; }
+ const std::optional<uint64_t>& last_label() const { return last_label_; }
+
+ private:
+ bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
+
+ CowHeader header_ = {};
+ std::optional<CowFooter> footer_;
+ std::shared_ptr<std::vector<CowOperation>> ops_;
+ std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
+ uint64_t fd_size_;
+ std::optional<uint64_t> last_label_;
+};
+
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeader* header);
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
similarity index 89%
rename from fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index edc9d65..120b2c0 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -25,7 +25,9 @@
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
#include "cow_decompress.h"
+#include "writer_v2.h"
+using android::base::unique_fd;
using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
@@ -42,6 +44,8 @@
virtual void TearDown() override { cow_ = nullptr; }
+ unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }
+
std::unique_ptr<TemporaryFile> cow_;
};
@@ -53,9 +57,9 @@
TEST_F(CowTest, CopyContiguous) {
CowOptions options;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddCopy(10, 1000, 100));
ASSERT_TRUE(writer.Finalize());
@@ -65,9 +69,9 @@
ASSERT_TRUE(reader.Parse(cow_->fd));
const auto& header = reader.GetHeader();
- ASSERT_EQ(header.magic, kCowMagicNumber);
- ASSERT_EQ(header.major_version, kCowVersionMajor);
- ASSERT_EQ(header.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
CowFooter footer;
@@ -96,9 +100,9 @@
TEST_F(CowTest, ReadWrite) {
CowOptions options;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -114,9 +118,9 @@
ASSERT_TRUE(reader.Parse(cow_->fd));
const auto& header = reader.GetHeader();
- ASSERT_EQ(header.magic, kCowMagicNumber);
- ASSERT_EQ(header.major_version, kCowVersionMajor);
- ASSERT_EQ(header.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
CowFooter footer;
@@ -175,9 +179,9 @@
TEST_F(CowTest, ReadWriteXor) {
CowOptions options;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -193,9 +197,9 @@
ASSERT_TRUE(reader.Parse(cow_->fd));
const auto& header = reader.GetHeader();
- ASSERT_EQ(header.magic, kCowMagicNumber);
- ASSERT_EQ(header.major_version, kCowVersionMajor);
- ASSERT_EQ(header.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+ ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
ASSERT_EQ(header.block_size, options.block_size);
CowFooter footer;
@@ -256,9 +260,9 @@
CowOptions options;
options.cluster_ops = 0;
options.compression = "gz";
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -296,9 +300,9 @@
options.compression = GetParam();
options.num_compress_threads = 2;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string xor_data = "This is test data-1. Testing xor";
xor_data.resize(options.block_size, '\0');
@@ -374,9 +378,9 @@
options.num_compress_threads = 1;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "Testing replace ops without batch writes";
data.resize(options.block_size * 1024, '\0');
@@ -497,9 +501,9 @@
CowOptions options;
options.compression = "gz";
options.cluster_ops = 2;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -562,9 +566,9 @@
CowOptions options;
options.compression = "gz";
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size * 2, '\0');
@@ -595,12 +599,12 @@
TEST_F(CowTest, GetSize) {
CowOptions options;
options.cluster_ops = 0;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
if (ftruncate(cow_->fd, 0) < 0) {
perror("Fails to set temp file size");
FAIL();
}
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -621,8 +625,8 @@
TEST_F(CowTest, AppendLabelSmall) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -632,8 +636,8 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 3));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({3}));
std::string data2 = "More data!";
data2.resize(options.block_size, '\0');
@@ -688,8 +692,8 @@
TEST_F(CowTest, AppendLabelMissing) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddLabel(0));
std::string data = "This is some data, believe it";
@@ -701,9 +705,9 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 1));
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 0));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_FALSE(writer->Initialize({1}));
+ ASSERT_TRUE(writer->Initialize({0}));
ASSERT_TRUE(writer->AddZeroBlocks(51, 1));
ASSERT_TRUE(writer->Finalize());
@@ -740,8 +744,8 @@
TEST_F(CowTest, AppendExtendedCorrupted) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddLabel(5));
@@ -763,8 +767,8 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({5}));
ASSERT_TRUE(writer->Finalize());
@@ -791,8 +795,8 @@
TEST_F(CowTest, AppendbyLabel) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size * 2, '\0');
@@ -810,9 +814,9 @@
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 12));
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_FALSE(writer->Initialize({12}));
+ ASSERT_TRUE(writer->Initialize({5}));
// This should drop label 6
ASSERT_TRUE(writer->Finalize());
@@ -879,8 +883,8 @@
TEST_F(CowTest, ClusterTest) {
CowOptions options;
options.cluster_ops = 4;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -976,16 +980,16 @@
TEST_F(CowTest, ClusterAppendTest) {
CowOptions options;
options.cluster_ops = 3;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddLabel(50));
ASSERT_TRUE(writer->Finalize()); // Adds a cluster op, should be dropped on append
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 50));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({50}));
std::string data2 = "More data!";
data2.resize(options.block_size, '\0');
@@ -1037,8 +1041,8 @@
TEST_F(CowTest, AppendAfterFinalize) {
CowOptions options;
options.cluster_ops = 0;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
@@ -1058,8 +1062,8 @@
ASSERT_TRUE(reader.Parse(cow_->fd));
}
-AssertionResult WriteDataBlock(CowWriter* writer, uint64_t new_block, std::string data) {
- data.resize(writer->options().block_size, '\0');
+AssertionResult WriteDataBlock(ICowWriter* writer, uint64_t new_block, std::string data) {
+ data.resize(writer->GetBlockSize(), '\0');
if (!writer->AddRawBlocks(new_block, data.data(), data.size())) {
return AssertionFailure() << "Failed to add raw block";
}
@@ -1088,8 +1092,8 @@
TEST_F(CowTest, ResumeMidCluster) {
CowOptions options;
options.cluster_ops = 7;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
@@ -1099,8 +1103,8 @@
ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
@@ -1145,8 +1149,8 @@
CowOptions options;
int cluster_ops = 5;
options.cluster_ops = cluster_ops;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
@@ -1160,8 +1164,8 @@
ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
@@ -1205,8 +1209,8 @@
TEST_F(CowTest, DeleteMidCluster) {
CowOptions options;
options.cluster_ops = 7;
- auto writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
@@ -1218,8 +1222,8 @@
ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(writer->Finalize());
ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
@@ -1255,14 +1259,14 @@
TEST_F(CowTest, BigSeqOp) {
CowOptions options;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
uint32_t sequence[seq_len];
for (int i = 0; i < seq_len; i++) {
sequence[i] = i + 1;
}
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len));
@@ -1287,14 +1291,14 @@
TEST_F(CowTest, MissingSeqOp) {
CowOptions options;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
const int seq_len = 10;
uint32_t sequence[seq_len];
for (int i = 0; i < seq_len; i++) {
sequence[i] = i + 1;
}
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));
@@ -1308,14 +1312,14 @@
TEST_F(CowTest, ResumeSeqOp) {
CowOptions options;
- auto writer = std::make_unique<CowWriter>(options);
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
const int seq_len = 10;
uint32_t sequence[seq_len];
for (int i = 0; i < seq_len; i++) {
sequence[i] = i + 1;
}
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
@@ -1328,8 +1332,8 @@
auto itr = reader->GetRevMergeOpIter();
ASSERT_TRUE(itr->AtEnd());
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
ASSERT_TRUE(writer->Finalize());
@@ -1358,10 +1362,10 @@
CowOptions options;
options.cluster_ops = 5;
options.num_merge_ops = 1;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
uint32_t sequence[] = {2, 10, 6, 7, 3, 5};
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddSequenceData(6, sequence));
ASSERT_TRUE(writer.AddCopy(6, 13));
@@ -1408,9 +1412,9 @@
CowOptions options;
options.cluster_ops = 5;
options.num_merge_ops = 1;
- CowWriter writer(options);
+ CowWriterV2 writer(options, GetCowFd());
- ASSERT_TRUE(writer.Initialize(cow_->fd));
+ ASSERT_TRUE(writer.Initialize());
ASSERT_TRUE(writer.AddCopy(2, 11));
ASSERT_TRUE(writer.AddCopy(10, 12));
@@ -1459,10 +1463,10 @@
options.num_merge_ops = 1;
std::string data = "This is some data, believe it";
data.resize(options.block_size, '\0');
- auto writer = std::make_unique<CowWriter>(options);
+ auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
CowReader reader;
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddCopy(3, 2));
ASSERT_TRUE(writer->AddCopy(2, 1));
@@ -1471,14 +1475,14 @@
ASSERT_TRUE(reader.Parse(cow_->fd));
ASSERT_TRUE(reader.VerifyMergeOps());
- ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
+ ASSERT_TRUE(writer->Initialize({1}));
ASSERT_TRUE(writer->AddCopy(4, 2));
ASSERT_TRUE(writer->Finalize());
ASSERT_TRUE(reader.Parse(cow_->fd));
ASSERT_FALSE(reader.VerifyMergeOps());
- writer = std::make_unique<CowWriter>(options);
- ASSERT_TRUE(writer->Initialize(cow_->fd));
+ writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+ ASSERT_TRUE(writer->Initialize());
ASSERT_TRUE(writer->AddCopy(2, 1));
ASSERT_TRUE(writer->AddXorBlocks(3, &data, data.size(), 1, 1));
ASSERT_TRUE(writer->Finalize());
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
new file mode 100644
index 0000000..22e63d0
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
@@ -0,0 +1,163 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "writer_base.h"
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+// The info messages here are spammy, but as useful for update_engine. Disable
+// them when running on the host.
+#ifdef __ANDROID__
+#define LOG_INFO LOG(INFO)
+#else
+#define LOG_INFO LOG(VERBOSE)
+#endif
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+namespace {
+std::string GetFdPath(borrowed_fd fd) {
+ const auto fd_path = "/proc/self/fd/" + std::to_string(fd.get());
+ std::string file_path(512, '\0');
+ const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());
+ if (err <= 0) {
+ PLOG(ERROR) << "Failed to determine path for fd " << fd.get();
+ file_path.clear();
+ } else {
+ file_path.resize(err);
+ }
+ return file_path;
+}
+} // namespace
+
+CowWriterBase::CowWriterBase(const CowOptions& options, unique_fd&& fd)
+ : options_(options), fd_(std::move(fd)) {}
+
+bool CowWriterBase::InitFd() {
+ if (fd_.get() < 0) {
+ fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "open /dev/null failed";
+ return false;
+ }
+ is_dev_null_ = true;
+ return true;
+ }
+
+ struct stat stat {};
+ if (fstat(fd_.get(), &stat) < 0) {
+ PLOG(ERROR) << "fstat failed";
+ return false;
+ }
+ const auto file_path = GetFdPath(fd_);
+ is_block_device_ = S_ISBLK(stat.st_mode);
+ if (is_block_device_) {
+ uint64_t size_in_bytes = 0;
+ if (ioctl(fd_.get(), BLKGETSIZE64, &size_in_bytes)) {
+ PLOG(ERROR) << "Failed to get total size for: " << fd_.get();
+ return false;
+ }
+ cow_image_size_ = size_in_bytes;
+ LOG_INFO << "COW image " << file_path << " has size " << size_in_bytes;
+ } else {
+ LOG_INFO << "COW image " << file_path
+ << " is not a block device, assuming unlimited space.";
+ }
+ return true;
+}
+
+bool CowWriterBase::AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+ CHECK(num_blocks != 0);
+
+ for (size_t i = 0; i < num_blocks; i++) {
+ if (!ValidateNewBlock(new_block + i)) {
+ return false;
+ }
+ }
+
+ return EmitCopy(new_block, old_block, num_blocks);
+}
+
+bool CowWriterBase::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ if (size % options_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << options_.block_size;
+ return false;
+ }
+
+ uint64_t num_blocks = size / options_.block_size;
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ return EmitRawBlocks(new_block_start, data, size);
+}
+
+bool CowWriterBase::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
+ if (size % options_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << options_.block_size;
+ return false;
+ }
+
+ uint64_t num_blocks = size / options_.block_size;
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ if (offset >= options_.block_size) {
+ LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
+ << options_.block_size;
+ }
+ return EmitXorBlocks(new_block_start, data, size, old_block, offset);
+}
+
+bool CowWriterBase::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ uint64_t last_block = new_block_start + num_blocks - 1;
+ if (!ValidateNewBlock(last_block)) {
+ return false;
+ }
+ return EmitZeroBlocks(new_block_start, num_blocks);
+}
+
+bool CowWriterBase::AddLabel(uint64_t label) {
+ return EmitLabel(label);
+}
+
+bool CowWriterBase::AddSequenceData(size_t num_ops, const uint32_t* data) {
+ return EmitSequenceData(num_ops, data);
+}
+
+bool CowWriterBase::ValidateNewBlock(uint64_t new_block) {
+ if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
+ LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
+ << options_.max_blocks.value();
+ return false;
+ }
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
new file mode 100644
index 0000000..8fa9065
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <libsnapshot/cow_writer.h>
+
+namespace android {
+namespace snapshot {
+
+class CowWriterBase : public ICowWriter {
+ public:
+ CowWriterBase(const CowOptions& options, android::base::unique_fd&& fd);
+ virtual ~CowWriterBase() {}
+
+ // Set up the writer.
+ // The file starts from the beginning.
+ //
+ // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
+ // computing COW sizes without using storage space.
+ //
+ // If a label is given, any operations after the given label will be dropped.
+ // If the given label is not found, Initialize will fail.
+ virtual bool Initialize(std::optional<uint64_t> label = {}) = 0;
+
+ bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
+ bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+ uint16_t offset) override;
+ bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ bool AddLabel(uint64_t label) override;
+ bool AddSequenceData(size_t num_ops, const uint32_t* data) override;
+ uint32_t GetBlockSize() const override { return options_.block_size; }
+ std::optional<uint32_t> GetMaxBlocks() const override { return options_.max_blocks; }
+
+ const CowOptions& options() const { return options_; }
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) = 0;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+ virtual bool EmitLabel(uint64_t label) = 0;
+ virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
+
+ bool InitFd();
+ bool ValidateNewBlock(uint64_t new_block);
+
+ CowOptions options_;
+ CowHeader header_{};
+
+ android::base::unique_fd fd_;
+ bool is_dev_null_ = false;
+ bool is_block_device_ = false;
+ uint64_t cow_image_size_ = INT64_MAX;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
similarity index 73%
rename from fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index 0e18979..b6603da 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -37,6 +37,8 @@
#include <sys/ioctl.h>
#include <unistd.h>
+#include "writer_v2.h"
+
// The info messages here are spammy, but as useful for update_engine. Disable
// them when running on the host.
#ifdef __ANDROID__
@@ -48,104 +50,17 @@
namespace android {
namespace snapshot {
-namespace {
-std::string GetFdPath(int fd) {
- const auto fd_path = "/proc/self/fd/" + std::to_string(fd);
- std::string file_path(512, '\0');
- const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());
- if (err <= 0) {
- PLOG(ERROR) << "Failed to determine path for fd " << fd;
- file_path.clear();
- } else {
- file_path.resize(err);
- }
- return file_path;
-}
-} // namespace
-
static_assert(sizeof(off_t) == sizeof(uint64_t));
-using android::base::borrowed_fd;
using android::base::unique_fd;
-bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
- CHECK(num_blocks != 0);
-
- for (size_t i = 0; i < num_blocks; i++) {
- if (!ValidateNewBlock(new_block + i)) {
- return false;
- }
- }
-
- return EmitCopy(new_block, old_block, num_blocks);
-}
-
-bool ICowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
- if (size % options_.block_size != 0) {
- LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
- << options_.block_size;
- return false;
- }
-
- uint64_t num_blocks = size / options_.block_size;
- uint64_t last_block = new_block_start + num_blocks - 1;
- if (!ValidateNewBlock(last_block)) {
- return false;
- }
- return EmitRawBlocks(new_block_start, data, size);
-}
-
-bool ICowWriter::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
- uint32_t old_block, uint16_t offset) {
- if (size % options_.block_size != 0) {
- LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
- << options_.block_size;
- return false;
- }
-
- uint64_t num_blocks = size / options_.block_size;
- uint64_t last_block = new_block_start + num_blocks - 1;
- if (!ValidateNewBlock(last_block)) {
- return false;
- }
- if (offset >= options_.block_size) {
- LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
- << options_.block_size;
- }
- return EmitXorBlocks(new_block_start, data, size, old_block, offset);
-}
-
-bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- uint64_t last_block = new_block_start + num_blocks - 1;
- if (!ValidateNewBlock(last_block)) {
- return false;
- }
- return EmitZeroBlocks(new_block_start, num_blocks);
-}
-
-bool ICowWriter::AddLabel(uint64_t label) {
- return EmitLabel(label);
-}
-
-bool ICowWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
- return EmitSequenceData(num_ops, data);
-}
-
-bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
- if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
- LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
- << options_.max_blocks.value();
- return false;
- }
- return true;
-}
-
-CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
+CowWriterV2::CowWriterV2(const CowOptions& options, unique_fd&& fd)
+ : CowWriterBase(options, std::move(fd)) {
SetupHeaders();
SetupWriteOptions();
}
-CowWriter::~CowWriter() {
+CowWriterV2::~CowWriterV2() {
for (size_t i = 0; i < compress_threads_.size(); i++) {
CompressWorker* worker = compress_threads_[i].get();
if (worker) {
@@ -164,7 +79,7 @@
compress_threads_.clear();
}
-void CowWriter::SetupWriteOptions() {
+void CowWriterV2::SetupWriteOptions() {
num_compress_threads_ = options_.num_compress_threads;
if (!num_compress_threads_) {
@@ -184,12 +99,12 @@
}
}
-void CowWriter::SetupHeaders() {
+void CowWriterV2::SetupHeaders() {
header_ = {};
- header_.magic = kCowMagicNumber;
- header_.major_version = kCowVersionMajor;
- header_.minor_version = kCowVersionMinor;
- header_.header_size = sizeof(CowHeader);
+ header_.prefix.magic = kCowMagicNumber;
+ header_.prefix.major_version = kCowVersionMajor;
+ header_.prefix.minor_version = kCowVersionMinor;
+ header_.prefix.header_size = sizeof(CowHeader);
header_.footer_size = sizeof(CowFooter);
header_.op_size = sizeof(CowOperation);
header_.block_size = options_.block_size;
@@ -201,7 +116,7 @@
footer_.op.type = kCowFooterOp;
}
-bool CowWriter::ParseOptions() {
+bool CowWriterV2::ParseOptions() {
auto algorithm = CompressionAlgorithmFromString(options_.compression);
if (!algorithm) {
LOG(ERROR) << "unrecognized compression: " << options_.compression;
@@ -216,42 +131,7 @@
return true;
}
-bool CowWriter::SetFd(android::base::borrowed_fd fd) {
- if (fd.get() < 0) {
- owned_fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
- if (owned_fd_ < 0) {
- PLOG(ERROR) << "open /dev/null failed";
- return false;
- }
- fd_ = owned_fd_;
- is_dev_null_ = true;
- } else {
- fd_ = fd;
-
- struct stat stat {};
- if (fstat(fd.get(), &stat) < 0) {
- PLOG(ERROR) << "fstat failed";
- return false;
- }
- const auto file_path = GetFdPath(fd.get());
- is_block_device_ = S_ISBLK(stat.st_mode);
- if (is_block_device_) {
- uint64_t size_in_bytes = 0;
- if (ioctl(fd.get(), BLKGETSIZE64, &size_in_bytes)) {
- PLOG(ERROR) << "Failed to get total size for: " << fd.get();
- return false;
- }
- cow_image_size_ = size_in_bytes;
- LOG_INFO << "COW image " << file_path << " has size " << size_in_bytes;
- } else {
- LOG_INFO << "COW image " << file_path
- << " is not a block device, assuming unlimited space.";
- }
- }
- return true;
-}
-
-void CowWriter::InitBatchWrites() {
+void CowWriterV2::InitBatchWrites() {
if (batch_write_) {
cowop_vec_ = std::make_unique<struct iovec[]>(header_.cluster_ops);
data_vec_ = std::make_unique<struct iovec[]>(header_.cluster_ops);
@@ -277,7 +157,7 @@
LOG_INFO << "Batch writes: " << batch_write;
}
-void CowWriter::InitWorkers() {
+void CowWriterV2::InitWorkers() {
if (num_compress_threads_ <= 1) {
LOG_INFO << "Not creating new threads for compression.";
return;
@@ -291,44 +171,27 @@
LOG_INFO << num_compress_threads_ << " thread used for compression";
}
-bool CowWriter::Initialize(unique_fd&& fd) {
- owned_fd_ = std::move(fd);
- return Initialize(borrowed_fd{owned_fd_});
-}
-
-bool CowWriter::Initialize(borrowed_fd fd) {
- if (!SetFd(fd) || !ParseOptions()) {
+bool CowWriterV2::Initialize(std::optional<uint64_t> label) {
+ if (!InitFd() || !ParseOptions()) {
return false;
}
-
- if (!OpenForWrite()) {
- return false;
+ if (!label) {
+ if (!OpenForWrite()) {
+ return false;
+ }
+ } else {
+ if (!OpenForAppend(*label)) {
+ return false;
+ }
}
- InitWorkers();
+ if (!compress_threads_.size()) {
+ InitWorkers();
+ }
return true;
}
-bool CowWriter::InitializeAppend(android::base::unique_fd&& fd, uint64_t label) {
- owned_fd_ = std::move(fd);
- return InitializeAppend(android::base::borrowed_fd{owned_fd_}, label);
-}
-
-bool CowWriter::InitializeAppend(android::base::borrowed_fd fd, uint64_t label) {
- if (!SetFd(fd) || !ParseOptions()) {
- return false;
- }
-
- bool ret = OpenForAppend(label);
-
- if (ret && !compress_threads_.size()) {
- InitWorkers();
- }
-
- return ret;
-}
-
-void CowWriter::InitPos() {
+void CowWriterV2::InitPos() {
next_op_pos_ = sizeof(header_) + header_.buffer_size;
cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
if (header_.cluster_ops) {
@@ -340,7 +203,7 @@
current_data_size_ = 0;
}
-bool CowWriter::OpenForWrite() {
+bool CowWriterV2::OpenForWrite() {
// This limitation is tied to the data field size in CowOperation.
if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Block size is too large";
@@ -388,7 +251,7 @@
return true;
}
-bool CowWriter::OpenForAppend(uint64_t label) {
+bool CowWriterV2::OpenForAppend(uint64_t label) {
auto reader = std::make_unique<CowReader>();
std::queue<CowOperation> toAdd;
@@ -424,7 +287,7 @@
return EmitClusterIfNeeded();
}
-bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+bool CowWriterV2::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
CHECK(!merge_in_progress_);
for (size_t i = 0; i < num_blocks; i++) {
@@ -440,16 +303,16 @@
return true;
}
-bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+bool CowWriterV2::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
}
-bool CowWriter::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
- uint32_t old_block, uint16_t offset) {
+bool CowWriterV2::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
}
-bool CowWriter::CompressBlocks(size_t num_blocks, const void* data) {
+bool CowWriterV2::CompressBlocks(size_t num_blocks, const void* data) {
size_t num_threads = (num_blocks == 1) ? 1 : num_compress_threads_;
size_t num_blocks_per_thread = num_blocks / num_threads;
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
@@ -483,8 +346,8 @@
return true;
}
-bool CowWriter::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
- uint64_t old_block, uint16_t offset, uint8_t type) {
+bool CowWriterV2::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
+ uint64_t old_block, uint16_t offset, uint8_t type) {
CHECK(!merge_in_progress_);
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
@@ -558,7 +421,7 @@
return true;
}
-bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+bool CowWriterV2::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
CHECK(!merge_in_progress_);
for (uint64_t i = 0; i < num_blocks; i++) {
CowOperation op = {};
@@ -570,7 +433,7 @@
return true;
}
-bool CowWriter::EmitLabel(uint64_t label) {
+bool CowWriterV2::EmitLabel(uint64_t label) {
CHECK(!merge_in_progress_);
CowOperation op = {};
op.type = kCowLabelOp;
@@ -578,7 +441,7 @@
return WriteOperation(op) && Sync();
}
-bool CowWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+bool CowWriterV2::EmitSequenceData(size_t num_ops, const uint32_t* data) {
CHECK(!merge_in_progress_);
size_t to_add = 0;
size_t max_ops = (header_.block_size * 2) / sizeof(uint32_t);
@@ -598,7 +461,7 @@
return true;
}
-bool CowWriter::EmitCluster() {
+bool CowWriterV2::EmitCluster() {
CowOperation op = {};
op.type = kCowClusterOp;
// Next cluster starts after remainder of current cluster and the next data block.
@@ -606,7 +469,7 @@
return WriteOperation(op);
}
-bool CowWriter::EmitClusterIfNeeded() {
+bool CowWriterV2::EmitClusterIfNeeded() {
// If there isn't room for another op and the cluster end op, end the current cluster
if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperation)) {
if (!EmitCluster()) return false;
@@ -614,18 +477,7 @@
return true;
}
-// TODO: Fix compilation issues when linking libcrypto library
-// when snapuserd is compiled as part of ramdisk.
-static void SHA256(const void*, size_t, uint8_t[]) {
-#if 0
- SHA256_CTX c;
- SHA256_Init(&c);
- SHA256_Update(&c, data, length);
- SHA256_Final(out, &c);
-#endif
-}
-
-bool CowWriter::Finalize() {
+bool CowWriterV2::Finalize() {
if (!FlushCluster()) {
LOG(ERROR) << "Finalize: FlushCluster() failed";
return false;
@@ -665,10 +517,8 @@
PLOG(ERROR) << "Failed to seek to footer position.";
return false;
}
- memset(&footer_.data.ops_checksum, 0, sizeof(uint8_t) * 32);
- memset(&footer_.data.footer_checksum, 0, sizeof(uint8_t) * 32);
+ memset(&footer_.unused, 0, sizeof(footer_.unused));
- SHA256(&footer_.op, sizeof(footer_.op), footer_.data.footer_checksum);
// Write out footer at end of file
if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&footer_),
sizeof(footer_))) {
@@ -701,7 +551,7 @@
return Sync();
}
-uint64_t CowWriter::GetCowSize() {
+uint64_t CowWriterV2::GetCowSize() {
if (current_data_size_ > 0) {
return next_data_pos_ + sizeof(footer_);
} else {
@@ -709,7 +559,7 @@
}
}
-bool CowWriter::GetDataPos(uint64_t* pos) {
+bool CowWriterV2::GetDataPos(uint64_t* pos) {
off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
if (offs < 0) {
PLOG(ERROR) << "lseek failed";
@@ -719,7 +569,7 @@
return true;
}
-bool CowWriter::EnsureSpaceAvailable(const uint64_t bytes_needed) const {
+bool CowWriterV2::EnsureSpaceAvailable(const uint64_t bytes_needed) const {
if (bytes_needed > cow_image_size_) {
LOG(ERROR) << "No space left on COW device. Required: " << bytes_needed
<< ", available: " << cow_image_size_;
@@ -729,7 +579,7 @@
return true;
}
-bool CowWriter::FlushCluster() {
+bool CowWriterV2::FlushCluster() {
ssize_t ret;
if (op_vec_index_) {
@@ -758,7 +608,7 @@
return true;
}
-bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
+bool CowWriterV2::WriteOperation(const CowOperation& op, const void* data, size_t size) {
if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op))) {
return false;
}
@@ -806,7 +656,7 @@
return EmitClusterIfNeeded();
}
-void CowWriter::AddOperation(const CowOperation& op) {
+void CowWriterV2::AddOperation(const CowOperation& op) {
footer_.op.num_ops++;
if (op.type == kCowClusterOp) {
@@ -821,14 +671,14 @@
next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
}
-bool CowWriter::WriteRawData(const void* data, const size_t size) {
+bool CowWriterV2::WriteRawData(const void* data, const size_t size) {
if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) {
return false;
}
return true;
}
-bool CowWriter::Sync() {
+bool CowWriterV2::Sync() {
if (is_dev_null_) {
return true;
}
@@ -839,7 +689,7 @@
return true;
}
-bool CowWriter::Truncate(off_t length) {
+bool CowWriterV2::Truncate(off_t length) {
if (is_dev_null_ || is_block_device_) {
return true;
}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
new file mode 100644
index 0000000..809ae57
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "writer_base.h"
+
+namespace android {
+namespace snapshot {
+
+class CowWriterV2 : public CowWriterBase {
+ public:
+ explicit CowWriterV2(const CowOptions& options, android::base::unique_fd&& fd);
+ ~CowWriterV2() override;
+
+ bool Initialize(std::optional<uint64_t> label = {}) override;
+ bool Finalize() override;
+ uint64_t GetCowSize() override;
+
+ protected:
+ virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
+ virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) override;
+ virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+ virtual bool EmitLabel(uint64_t label) override;
+ virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+
+ private:
+ bool EmitCluster();
+ bool EmitClusterIfNeeded();
+ bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
+ uint16_t offset, uint8_t type);
+ void SetupHeaders();
+ void SetupWriteOptions();
+ bool ParseOptions();
+ bool OpenForWrite();
+ bool OpenForAppend(uint64_t label);
+ bool GetDataPos(uint64_t* pos);
+ bool WriteRawData(const void* data, size_t size);
+ bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
+ void AddOperation(const CowOperation& op);
+ void InitPos();
+ void InitBatchWrites();
+ void InitWorkers();
+ bool FlushCluster();
+
+ bool CompressBlocks(size_t num_blocks, const void* data);
+ bool Sync();
+ bool Truncate(off_t length);
+ bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;
+
+ private:
+ CowFooter footer_{};
+ CowCompressionAlgorithm compression_ = kCowCompressNone;
+ uint64_t current_op_pos_ = 0;
+ uint64_t next_op_pos_ = 0;
+ uint64_t next_data_pos_ = 0;
+ uint64_t current_data_pos_ = 0;
+ ssize_t total_data_written_ = 0;
+ uint32_t cluster_size_ = 0;
+ uint32_t current_cluster_size_ = 0;
+ uint64_t current_data_size_ = 0;
+ bool merge_in_progress_ = false;
+
+ int num_compress_threads_ = 1;
+ std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
+ std::vector<std::future<bool>> threads_;
+ std::vector<std::basic_string<uint8_t>> compressed_buf_;
+ std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
+
+ std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_;
+ std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
+ std::unique_ptr<struct iovec[]> cowop_vec_;
+ int op_vec_index_ = 0;
+
+ std::unique_ptr<struct iovec[]> data_vec_;
+ int data_vec_index_ = 0;
+ bool batch_write_ = false;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 2661482..5920bc2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -45,6 +45,7 @@
#include <android/snapshot/snapshot.pb.h>
#include <libsnapshot/snapshot_stats.h>
#include "device_info.h"
+#include "libsnapshot_cow/writer_v2.h"
#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
#include "snapshot_reader.h"
@@ -3202,39 +3203,20 @@
CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
- std::map<std::string, SnapshotStatus> all_snapshot_status;
-
- // In case of error, automatically delete devices that are created along the way.
- // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
- // these devices.
- AutoDeviceList created_devices;
-
const auto& dap_metadata = manifest.dynamic_partition_metadata();
- CowOptions options;
- CowWriter writer(options);
- bool cow_format_support = true;
- if (dap_metadata.cow_version() < writer.GetCowVersion()) {
- cow_format_support = false;
- }
-
- LOG(INFO) << " dap_metadata.cow_version(): " << dap_metadata.cow_version()
- << " writer.GetCowVersion(): " << writer.GetCowVersion();
-
- // Deduce supported features.
- bool userspace_snapshots = CanUseUserspaceSnapshots();
- bool legacy_compression = GetLegacyCompressionEnabledProperty();
std::string vabc_disable_reason;
if (!dap_metadata.vabc_enabled()) {
vabc_disable_reason = "not enabled metadata";
} else if (device_->IsRecovery()) {
vabc_disable_reason = "recovery";
- } else if (!cow_format_support) {
- vabc_disable_reason = "cow format not supported";
} else if (!KernelSupportsCompressedSnapshots()) {
vabc_disable_reason = "kernel missing userspace block device support";
}
+ // Deduce supported features.
+ bool userspace_snapshots = CanUseUserspaceSnapshots();
+ bool legacy_compression = GetLegacyCompressionEnabledProperty();
if (!vabc_disable_reason.empty()) {
if (userspace_snapshots) {
LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
@@ -3246,6 +3228,16 @@
legacy_compression = false;
}
+ if (legacy_compression || userspace_snapshots) {
+ if (dap_metadata.cow_version() < kMinCowVersion ||
+ dap_metadata.cow_version() > kMaxCowVersion) {
+ LOG(ERROR) << "Manifest cow version is out of bounds (got: "
+ << dap_metadata.cow_version() << ", min: " << kMinCowVersion
+ << ", max: " << kMaxCowVersion << ")";
+ return Return::Error();
+ }
+ }
+
const bool using_snapuserd = userspace_snapshots || legacy_compression;
if (!using_snapuserd) {
LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
@@ -3278,6 +3270,11 @@
cow_creator.batched_writes = dap_metadata.vabc_feature_set().batch_writes();
}
+ // In case of error, automatically delete devices that are created along the way.
+ // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
+ // these devices.
+ AutoDeviceList created_devices;
+ std::map<std::string, SnapshotStatus> all_snapshot_status;
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
&all_snapshot_status);
if (!ret.is_ok()) {
@@ -3561,8 +3558,8 @@
}
options.compression = it->second.compression_algorithm();
- CowWriter writer(options);
- if (!writer.Initialize(fd) || !writer.Finalize()) {
+ CowWriterV2 writer(options, std::move(fd));
+ if (!writer.Initialize(std::nullopt) || !writer.Finalize()) {
LOG(ERROR) << "Could not initialize COW device for " << target_partition->name();
return Return::Error();
}
@@ -3651,12 +3648,13 @@
return nullptr;
}
- if (status.using_snapuserd()) {
- return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
- status, paths);
+ if (!status.using_snapuserd()) {
+ LOG(ERROR) << "Can only create snapshot writers with userspace or compressed snapshots";
+ return nullptr;
}
- return OpenKernelSnapshotWriter(lock.get(), source_device, params.GetPartitionName(), status,
- paths);
+
+ return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
+ status, paths);
#endif
}
@@ -3704,34 +3702,6 @@
return writer;
}
-
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenKernelSnapshotWriter(
- LockedFile* lock, const std::optional<std::string>& source_device,
- [[maybe_unused]] const std::string& partition_name, const SnapshotStatus& status,
- const SnapshotPaths& paths) {
- CHECK(lock);
-
- CowOptions cow_options;
- cow_options.max_blocks = {status.device_size() / cow_options.block_size};
-
- auto writer = std::make_unique<OnlineKernelSnapshotWriter>(cow_options);
-
- std::string path = paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
- unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << path;
- return nullptr;
- }
-
- if (source_device) {
- writer->SetSourceDevice(*source_device);
- }
-
- uint64_t cow_size = status.cow_partition_size() + status.cow_file_size();
- writer->SetSnapshotDevice(std::move(fd), cow_size);
-
- return writer;
-}
#endif // !defined(LIBSNAPSHOT_NO_COW_WRITE)
bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 22731e7..dac1b77 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -643,21 +643,38 @@
TEST_F(SnapshotTest, Merge) {
ASSERT_TRUE(AcquireLock());
- static const uint64_t kDeviceSize = 1024 * 1024;
-
- std::unique_ptr<ISnapshotWriter> writer;
- ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
-
- bool userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
-
- // Release the lock.
- lock_ = nullptr;
+ static constexpr uint64_t kDeviceSize = 1024 * 1024;
+ static constexpr uint32_t kBlockSize = 4096;
std::string test_string = "This is a test string.";
- test_string.resize(writer->options().block_size);
- ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
- ASSERT_TRUE(writer->Finalize());
- writer = nullptr;
+ test_string.resize(kBlockSize);
+
+ bool userspace_snapshots = false;
+ if (snapuserd_required_) {
+ std::unique_ptr<ISnapshotWriter> writer;
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
+
+ userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
+
+ // Release the lock.
+ lock_ = nullptr;
+
+ ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
+ ASSERT_TRUE(writer->Finalize());
+ writer = nullptr;
+ } else {
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+
+ // Release the lock.
+ lock_ = nullptr;
+
+ std::string path;
+ ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b", &path));
+
+ unique_fd fd(open(path.c_str(), O_WRONLY));
+ ASSERT_GE(fd, 0);
+ ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
+ }
// Done updating.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -1203,13 +1220,13 @@
SHA256_CTX ctx;
SHA256_Init(&ctx);
- if (!writer->options().max_blocks) {
+ if (!writer->GetMaxBlocks()) {
LOG(ERROR) << "CowWriter must specify maximum number of blocks";
return false;
}
- const auto num_blocks = writer->options().max_blocks.value();
+ const auto num_blocks = writer->GetMaxBlocks().value();
- const auto block_size = writer->options().block_size;
+ const auto block_size = writer->GetBlockSize();
std::string block(block_size, '\0');
for (uint64_t i = 0; i < num_blocks; i++) {
if (!ReadFully(rand, block.data(), block.size())) {
@@ -1237,13 +1254,13 @@
if (auto res = MapUpdateSnapshot(name, &writer); !res) {
return res;
}
- if (!writer->options().max_blocks || !*writer->options().max_blocks) {
+ if (!writer->GetMaxBlocks() || !*writer->GetMaxBlocks()) {
return AssertionFailure() << "No max blocks set for " << name << " writer";
}
- uint64_t src_block = (old_size / writer->options().block_size) - 1;
+ uint64_t src_block = (old_size / writer->GetBlockSize()) - 1;
uint64_t dst_block = 0;
- uint64_t max_blocks = *writer->options().max_blocks;
+ uint64_t max_blocks = *writer->GetMaxBlocks();
while (dst_block < max_blocks && dst_block < src_block) {
if (!writer->AddCopy(dst_block, src_block)) {
return AssertionFailure() << "Unable to add copy for " << name << " for blocks "
@@ -2372,60 +2389,6 @@
<< "FinishedSnapshotWrites should detect overflow of CoW device.";
}
-// Get max file size and free space.
-std::pair<uint64_t, uint64_t> GetBigFileLimit() {
- struct statvfs fs;
- if (statvfs("/data", &fs) < 0) {
- PLOG(ERROR) << "statfs failed";
- return {0, 0};
- }
-
- auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);
- auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;
-
- LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free;
-
- return {fs_limit, fs_free};
-}
-
-TEST_F(SnapshotUpdateTest, LowSpace) {
- // To make the low space test more reliable, we force a large cow estimate.
- // However legacy VAB ignores the COW estimate and uses InstallOperations
- // to compute the exact size required for dm-snapshot. It's difficult to
- // make this work reliably (we'd need to somehow fake an extremely large
- // super partition, and we don't have that level of dependency injection).
- //
- // For now, just skip this test on legacy VAB.
- if (!snapuserd_required_) {
- GTEST_SKIP() << "Skipping test on legacy VAB";
- }
-
- auto fs = GetBigFileLimit();
- ASSERT_NE(fs.first, 0);
-
- constexpr uint64_t partition_size = 10_MiB;
- SetSize(sys_, partition_size);
- SetSize(vnd_, partition_size);
- SetSize(prd_, partition_size);
- sys_->set_estimate_cow_size(fs.first);
- vnd_->set_estimate_cow_size(fs.first);
- prd_->set_estimate_cow_size(fs.first);
-
- AddOperationForPartitions();
-
- // Execute the update.
- ASSERT_TRUE(sm->BeginUpdate());
- auto res = sm->CreateUpdateSnapshots(manifest_);
- ASSERT_FALSE(res);
- ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
-
- // It's hard to predict exactly how much free space is needed, since /data
- // is writable and the test is not the only process running. Divide by two
- // as a rough lower bound, and adjust this in the future as necessary.
- auto expected_delta = fs.first - fs.second;
- ASSERT_GE(res.required_size(), expected_delta / 2);
-}
-
TEST_F(SnapshotUpdateTest, AddPartition) {
group_->add_partition_names("dlkm");
@@ -2688,6 +2651,24 @@
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
}
+TEST_F(SnapshotUpdateTest, BadCowVersion) {
+ if (!snapuserd_required_) {
+ GTEST_SKIP() << "VABC only";
+ }
+
+ ASSERT_TRUE(sm->BeginUpdate());
+
+ auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
+ dynamic_partition_metadata->set_cow_version(kMinCowVersion - 1);
+ ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));
+
+ dynamic_partition_metadata->set_cow_version(kMaxCowVersion + 1);
+ ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));
+
+ dynamic_partition_metadata->set_cow_version(kMaxCowVersion);
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+}
+
class FlashAfterUpdateTest : public SnapshotUpdateTest,
public WithParamInterface<std::tuple<uint32_t, bool>> {
public:
@@ -2796,38 +2777,6 @@
"Merge"s;
});
-class ImageManagerTest : public SnapshotTest {
- protected:
- void SetUp() override {
- SKIP_IF_NON_VIRTUAL_AB();
- SnapshotTest::SetUp();
- }
- void TearDown() override {
- RETURN_IF_NON_VIRTUAL_AB();
- CleanUp();
- SnapshotTest::TearDown();
- }
- void CleanUp() {
- if (!image_manager_) {
- return;
- }
- EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
- image_manager_->DeleteBackingImage(kImageName));
- }
-
- static constexpr const char* kImageName = "my_image";
-};
-
-TEST_F(ImageManagerTest, CreateImageNoSpace) {
- auto fs = GetBigFileLimit();
- ASSERT_NE(fs.first, 0);
-
- auto res = image_manager_->CreateBackingImage(kImageName, fs.first,
- IImageManager::CREATE_IMAGE_DEFAULT);
- ASSERT_FALSE(res);
- ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
-}
-
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 82a7fd7..0ea424c 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -19,6 +19,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <payload_consumer/file_descriptor.h>
+#include "libsnapshot_cow/writer_v2.h"
#include "snapshot_reader.h"
namespace android {
@@ -28,13 +29,11 @@
using android::base::unique_fd;
using chromeos_update_engine::FileDescriptor;
-ISnapshotWriter::ISnapshotWriter(const CowOptions& options) : ICowWriter(options) {}
-
-void ISnapshotWriter::SetSourceDevice(const std::string& source_device) {
+void CompressedSnapshotWriter::SetSourceDevice(const std::string& source_device) {
source_device_ = {source_device};
}
-borrowed_fd ISnapshotWriter::GetSourceFd() {
+borrowed_fd CompressedSnapshotWriter::GetSourceFd() {
if (!source_device_) {
LOG(ERROR) << "Attempted to read from source device but none was set";
return borrowed_fd{-1};
@@ -50,12 +49,10 @@
return source_fd_;
}
-CompressedSnapshotWriter::CompressedSnapshotWriter(const CowOptions& options)
- : ISnapshotWriter(options) {}
+CompressedSnapshotWriter::CompressedSnapshotWriter(const CowOptions& options) : options_(options) {}
bool CompressedSnapshotWriter::SetCowDevice(android::base::unique_fd&& cow_device) {
cow_device_ = std::move(cow_device);
- cow_ = std::make_unique<CowWriter>(options_);
return true;
}
@@ -106,137 +103,76 @@
reader->SetSourceDevice(*source_device_);
}
- const auto& cow_options = options();
- if (cow_options.max_blocks) {
- reader->SetBlockDeviceSize(*cow_options.max_blocks * cow_options.block_size);
+ if (options_.max_blocks) {
+ reader->SetBlockDeviceSize(*options_.max_blocks * options_.block_size);
}
return reader;
}
-bool CompressedSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block,
- uint64_t num_blocks) {
+bool CompressedSnapshotWriter::AddCopy(uint64_t new_block, uint64_t old_block,
+ uint64_t num_blocks) {
return cow_->AddCopy(new_block, old_block, num_blocks);
}
-bool CompressedSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
- size_t size) {
+bool CompressedSnapshotWriter::AddRawBlocks(uint64_t new_block_start, const void* data,
+ size_t size) {
return cow_->AddRawBlocks(new_block_start, data, size);
}
-bool CompressedSnapshotWriter::EmitXorBlocks(uint32_t new_block_start, const void* data,
- size_t size, uint32_t old_block, uint16_t offset) {
+bool CompressedSnapshotWriter::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+ uint32_t old_block, uint16_t offset) {
return cow_->AddXorBlocks(new_block_start, data, size, old_block, offset);
}
-bool CompressedSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+bool CompressedSnapshotWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
return cow_->AddZeroBlocks(new_block_start, num_blocks);
}
-bool CompressedSnapshotWriter::EmitLabel(uint64_t label) {
+bool CompressedSnapshotWriter::AddLabel(uint64_t label) {
return cow_->AddLabel(label);
}
-bool CompressedSnapshotWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+bool CompressedSnapshotWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
return cow_->AddSequenceData(num_ops, data);
}
bool CompressedSnapshotWriter::Initialize() {
- return cow_->Initialize(cow_device_);
+ unique_fd cow_fd(dup(cow_device_.get()));
+ if (cow_fd < 0) {
+ PLOG(ERROR) << "dup COW device";
+ return false;
+ }
+
+ auto cow = std::make_unique<CowWriterV2>(options_, std::move(cow_fd));
+ if (!cow->Initialize(std::nullopt)) {
+ return false;
+ }
+ cow_ = std::move(cow);
+ return true;
}
bool CompressedSnapshotWriter::InitializeAppend(uint64_t label) {
- return cow_->InitializeAppend(cow_device_, label);
-}
-
-OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
- : ISnapshotWriter(options) {}
-
-void OnlineKernelSnapshotWriter::SetSnapshotDevice(android::base::unique_fd&& snapshot_fd,
- uint64_t cow_size) {
- snapshot_fd_ = std::move(snapshot_fd);
- cow_size_ = cow_size;
-}
-
-bool OnlineKernelSnapshotWriter::Finalize() {
- if (fsync(snapshot_fd_.get()) < 0) {
- PLOG(ERROR) << "fsync";
- return false;
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
- size_t size) {
- uint64_t offset = new_block_start * options_.block_size;
- if (lseek(snapshot_fd_.get(), offset, SEEK_SET) < 0) {
- PLOG(ERROR) << "EmitRawBlocks lseek to offset " << offset;
- return false;
- }
- if (!android::base::WriteFully(snapshot_fd_, data, size)) {
- PLOG(ERROR) << "EmitRawBlocks write";
- return false;
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitXorBlocks(uint32_t, const void*, size_t, uint32_t, uint16_t) {
- LOG(ERROR) << "EmitXorBlocks not implemented.";
- return false;
-}
-
-bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
- std::string zeroes(options_.block_size, 0);
- for (uint64_t i = 0; i < num_blocks; i++) {
- if (!EmitRawBlocks(new_block_start + i, zeroes.data(), zeroes.size())) {
- return false;
- }
- }
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block,
- uint64_t num_blocks) {
- auto source_fd = GetSourceFd();
- if (source_fd < 0) {
+ unique_fd cow_fd(dup(cow_device_.get()));
+ if (cow_fd < 0) {
+ PLOG(ERROR) << "dup COW device";
return false;
}
- CHECK(num_blocks != 0);
-
- for (size_t i = 0; i < num_blocks; i++) {
- std::string buffer(options_.block_size, 0);
- uint64_t offset = (old_block + i) * options_.block_size;
- if (!android::base::ReadFullyAtOffset(source_fd, buffer.data(), buffer.size(), offset)) {
- PLOG(ERROR) << "EmitCopy read";
- return false;
- }
- if (!EmitRawBlocks(new_block + i, buffer.data(), buffer.size())) {
- PLOG(ERROR) << "EmitRawBlocks failed";
- return false;
- }
+ auto cow = std::make_unique<CowWriterV2>(options_, std::move(cow_fd));
+ if (!cow->Initialize(label)) {
+ return false;
}
-
+ cow_ = std::move(cow);
return true;
}
-bool OnlineKernelSnapshotWriter::EmitLabel(uint64_t) {
- // Not Needed
- return true;
+uint32_t CompressedSnapshotWriter::GetBlockSize() const {
+ return cow_->GetBlockSize();
}
-bool OnlineKernelSnapshotWriter::EmitSequenceData(size_t, const uint32_t*) {
- // Not Needed
- return true;
-}
-
-std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
- unique_fd fd(dup(snapshot_fd_.get()));
- if (fd < 0) {
- PLOG(ERROR) << "dup2 failed in OpenReader";
- return nullptr;
- }
- return std::make_unique<ReadFdFileDescriptor>(std::move(fd));
+std::optional<uint32_t> CompressedSnapshotWriter::GetMaxBlocks() const {
+ return cow_->GetMaxBlocks();
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
index 3c4ab2e..737c480 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
@@ -122,6 +122,7 @@
void SimulateDaemonRestart();
void StartMerge();
+ std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
void CreateCowDevice();
void CreateCowDeviceOrderedOps();
void CreateCowDeviceOrderedOpsInverted();
@@ -164,6 +165,7 @@
private:
void InitMetadata();
+ std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
void CreateCowDevice();
void CreateCowPartialFilledArea();
@@ -258,6 +260,19 @@
}
}
+std::unique_ptr<ICowWriter> CowSnapuserdTest::CreateCowDeviceInternal() {
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ CowOptions options;
+ options.compression = "gz";
+
+ unique_fd fd(cow_system_->fd);
+ cow_system_->fd = -1;
+
+ return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
void CowSnapuserdTest::ReadLastBlock() {
unique_fd rnd_fd;
total_base_size_ = BLOCK_SZ * 2;
@@ -280,9 +295,6 @@
base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
ASSERT_TRUE(base_loop_->valid());
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
-
std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_);
loff_t offset = 0;
@@ -294,16 +306,13 @@
offset += BLOCK_SZ;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+ ASSERT_TRUE(writer->AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
+ ASSERT_TRUE(writer->AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
- ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
- ASSERT_TRUE(writer.AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
-
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
SetDeviceControlName();
@@ -381,22 +390,16 @@
}
void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = 0;
// Create overlapping copy operations
while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
+ ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));
x -= 1;
if (x == 1) {
break;
@@ -405,7 +408,7 @@
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -433,22 +436,16 @@
}
void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = num_blocks - 1;
// Create overlapping copy operations
while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy + 1, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));
x -= 1;
if (x == 0) {
ASSERT_EQ(blk_src_copy, 0);
@@ -458,7 +455,7 @@
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -468,10 +465,11 @@
true);
// Merged operations
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), options.block_size, 0),
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),
+ 0),
true);
ASSERT_EQ(android::base::ReadFullyAtOffset(
- base_fd_, (char*)orig_buffer_.get() + options.block_size, size_, 0),
+ base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),
true);
}
@@ -479,8 +477,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -495,13 +493,7 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 3;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
@@ -509,7 +501,7 @@
size_t x = num_blocks;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
@@ -519,12 +511,12 @@
}
for (size_t i = num_blocks; i > 0; i--) {
- ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
- &random_buffer_1_.get()[options.block_size * (i - 1)],
- options.block_size, 2 * num_blocks + i - 1, xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(
+ num_blocks + i - 1, &random_buffer_1_.get()[writer->GetBlockSize() * (i - 1)],
+ writer->GetBlockSize(), 2 * num_blocks + i - 1, xor_offset));
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
@@ -542,8 +534,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -559,20 +551,14 @@
}
memset(random_buffer_1_.get(), 0, size_);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t source_blk = 0;
size_t blk_src_copy = 2 * num_blocks;
uint16_t xor_offset = 5;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
@@ -582,10 +568,10 @@
blk_src_copy += 1;
}
- ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
- xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
+ xor_offset));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
@@ -603,8 +589,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -619,13 +605,7 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 2;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
@@ -639,11 +619,11 @@
for (int i = 0; i < num_blocks; i++) {
sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
}
- ASSERT_TRUE(writer.AddSequenceData(2 * num_blocks, sequence));
+ ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
size_t x = num_blocks;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
@@ -655,24 +635,24 @@
source_blk = num_blocks;
blk_src_copy = blk_end_copy;
- ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
size_t blk_zero_copy_start = source_blk + num_blocks;
size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
- ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+ ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
size_t blk_random2_replace_start = blk_zero_copy_end;
- ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
size_t blk_xor_start = blk_random2_replace_start + num_blocks;
size_t xor_offset = BLOCK_SZ / 2;
- ASSERT_TRUE(writer.AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
- xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+ xor_offset));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
std::string zero_buffer(size_, 0);
@@ -902,29 +882,36 @@
ASSERT_TRUE(Merge());
}
-void CowSnapuserdMetadataTest::CreateCowPartialFilledArea() {
+std::unique_ptr<ICowWriter> CowSnapuserdMetadataTest::CreateCowDeviceInternal() {
std::string path = android::base::GetExecutableDirectory();
cow_system_ = std::make_unique<TemporaryFile>(path);
CowOptions options;
options.compression = "gz";
- CowWriter writer(options);
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+ unique_fd fd(cow_system_->fd);
+ cow_system_->fd = -1;
+
+ return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
+void CowSnapuserdMetadataTest::CreateCowPartialFilledArea() {
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
// Area 0 is completely filled with 256 exceptions
for (int i = 0; i < 256; i++) {
- ASSERT_TRUE(writer.AddCopy(i, 256 + i));
+ ASSERT_TRUE(writer->AddCopy(i, 256 + i));
}
// Area 1 is partially filled with 2 copy ops and 10 zero ops
- ASSERT_TRUE(writer.AddCopy(500, 1000));
- ASSERT_TRUE(writer.AddCopy(501, 1001));
+ ASSERT_TRUE(writer->AddCopy(500, 1000));
+ ASSERT_TRUE(writer->AddCopy(501, 1001));
- ASSERT_TRUE(writer.AddZeroBlocks(300, 10));
+ ASSERT_TRUE(writer->AddZeroBlocks(300, 10));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
}
void CowSnapuserdMetadataTest::ValidatePartialFilledArea() {
@@ -956,8 +943,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -972,50 +959,44 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
// Overlapping region. This has to be split
// into two batch operations
- ASSERT_TRUE(writer.AddCopy(23, 20));
- ASSERT_TRUE(writer.AddCopy(22, 19));
- ASSERT_TRUE(writer.AddCopy(21, 18));
- ASSERT_TRUE(writer.AddCopy(20, 17));
- ASSERT_TRUE(writer.AddCopy(19, 16));
- ASSERT_TRUE(writer.AddCopy(18, 15));
+ ASSERT_TRUE(writer->AddCopy(23, 20));
+ ASSERT_TRUE(writer->AddCopy(22, 19));
+ ASSERT_TRUE(writer->AddCopy(21, 18));
+ ASSERT_TRUE(writer->AddCopy(20, 17));
+ ASSERT_TRUE(writer->AddCopy(19, 16));
+ ASSERT_TRUE(writer->AddCopy(18, 15));
// Contiguous region but blocks in ascending order
// Daemon has to ensure that these blocks are merged
// in a batch
- ASSERT_TRUE(writer.AddCopy(50, 75));
- ASSERT_TRUE(writer.AddCopy(51, 76));
- ASSERT_TRUE(writer.AddCopy(52, 77));
- ASSERT_TRUE(writer.AddCopy(53, 78));
+ ASSERT_TRUE(writer->AddCopy(50, 75));
+ ASSERT_TRUE(writer->AddCopy(51, 76));
+ ASSERT_TRUE(writer->AddCopy(52, 77));
+ ASSERT_TRUE(writer->AddCopy(53, 78));
// Dis-contiguous region
- ASSERT_TRUE(writer.AddCopy(110, 130));
- ASSERT_TRUE(writer.AddCopy(105, 125));
- ASSERT_TRUE(writer.AddCopy(100, 120));
+ ASSERT_TRUE(writer->AddCopy(110, 130));
+ ASSERT_TRUE(writer->AddCopy(105, 125));
+ ASSERT_TRUE(writer->AddCopy(100, 120));
// Overlap
- ASSERT_TRUE(writer.AddCopy(25, 30));
- ASSERT_TRUE(writer.AddCopy(30, 31));
+ ASSERT_TRUE(writer->AddCopy(25, 30));
+ ASSERT_TRUE(writer->AddCopy(30, 31));
size_t source_blk = num_blocks;
- ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
size_t blk_zero_copy_start = source_blk + num_blocks;
- ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+ ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
}
void CowSnapuserdMetadataTest::InitMetadata() {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index efa43b7..da9bd11 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -628,8 +628,8 @@
bool Snapuserd::MmapMetadata() {
const auto& header = reader_->GetHeader();
- if (header.major_version >= 2 && header.buffer_size > 0) {
- total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
+ if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
+ total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
read_ahead_feature_ = true;
} else {
// mmap the first 4k page - older COW format
@@ -823,7 +823,7 @@
uint64_t Snapuserd::GetBufferMetadataOffset() {
const auto& header = reader_->GetHeader();
- size_t size = header.header_size + sizeof(BufferState);
+ size_t size = header.prefix.header_size + sizeof(BufferState);
return size;
}
@@ -845,7 +845,7 @@
size_t Snapuserd::GetBufferDataOffset() {
const auto& header = reader_->GetHeader();
- return (header.header_size + GetBufferMetadataSize());
+ return (header.prefix.header_size + GetBufferMetadataSize());
}
/*
@@ -862,7 +862,7 @@
const auto& header = reader_->GetHeader();
struct BufferState* ra_state =
- reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
+ reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
return ra_state;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index a519639..8e1212b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -240,9 +240,9 @@
bool SnapshotHandler::MmapMetadata() {
const auto& header = reader_->GetHeader();
- total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
+ total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
- if (header.major_version >= 2 && header.buffer_size > 0) {
+ if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
scratch_space_ = true;
}
@@ -362,7 +362,7 @@
uint64_t SnapshotHandler::GetBufferMetadataOffset() {
const auto& header = reader_->GetHeader();
- return (header.header_size + sizeof(BufferState));
+ return (header.prefix.header_size + sizeof(BufferState));
}
/*
@@ -390,7 +390,7 @@
size_t SnapshotHandler::GetBufferDataOffset() {
const auto& header = reader_->GetHeader();
- return (header.header_size + GetBufferMetadataSize());
+ return (header.prefix.header_size + GetBufferMetadataSize());
}
/*
@@ -413,7 +413,7 @@
const auto& header = reader_->GetHeader();
struct BufferState* ra_state =
- reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
+ reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
return ra_state;
}
@@ -448,5 +448,9 @@
return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
}
+bool SnapshotHandler::CheckPartitionVerification() {
+ return update_verify_->CheckPartitionVerification();
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 777aa07..c16ad24 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -46,6 +46,8 @@
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
#include <storage_literals/storage_literals.h>
+#include "snapuserd_readahead.h"
+#include "snapuserd_verify.h"
namespace android {
namespace snapshot {
@@ -96,112 +98,6 @@
: merge_state_(state), num_ios_in_progress(n_ios) {}
};
-class ReadAhead {
- public:
- ReadAhead(const std::string& cow_device, const std::string& backing_device,
- const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
- bool RunThread();
-
- private:
- void InitializeRAIter();
- bool RAIterDone();
- void RAIterNext();
- void RAResetIter(uint64_t num_blocks);
- const CowOperation* GetRAOpIter();
-
- void InitializeBuffer();
- bool InitReader();
- bool InitializeFds();
-
- void CloseFds() { backing_store_fd_ = {}; }
-
- bool ReadAheadIOStart();
- int PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
- std::vector<uint64_t>& blocks,
- std::vector<const CowOperation*>& xor_op_vec);
- bool ReconstructDataFromCow();
- void CheckOverlap(const CowOperation* cow_op);
-
- bool ReadAheadAsyncIO();
- bool ReapIoCompletions(int pending_ios_to_complete);
- bool ReadXorData(size_t block_index, size_t xor_op_index,
- std::vector<const CowOperation*>& xor_op_vec);
- void ProcessXorData(size_t& block_xor_index, size_t& xor_index,
- std::vector<const CowOperation*>& xor_op_vec, void* buffer,
- loff_t& buffer_offset);
- void UpdateScratchMetadata();
-
- bool ReadAheadSyncIO();
- bool InitializeIouring();
- void FinalizeIouring();
-
- void* read_ahead_buffer_;
- void* metadata_buffer_;
-
- std::unique_ptr<ICowOpIter> cowop_iter_;
-
- std::string cow_device_;
- std::string backing_store_device_;
- std::string misc_name_;
-
- unique_fd cow_fd_;
- unique_fd backing_store_fd_;
-
- std::shared_ptr<SnapshotHandler> snapuserd_;
- std::unique_ptr<CowReader> reader_;
-
- std::unordered_set<uint64_t> dest_blocks_;
- std::unordered_set<uint64_t> source_blocks_;
- bool overlap_;
- std::vector<uint64_t> blocks_;
- int total_blocks_merged_ = 0;
- std::unique_ptr<uint8_t[]> ra_temp_buffer_;
- std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
- BufferSink bufsink_;
-
- uint64_t total_ra_blocks_completed_ = 0;
- bool read_ahead_async_ = false;
- // Queue depth of 8 seems optimal. We don't want
- // to have a huge depth as it may put more memory pressure
- // on the kernel worker threads given that we use
- // IOSQE_ASYNC flag - ASYNC flags can potentially
- // result in EINTR; Since we don't restart
- // syscalls and fallback to synchronous I/O, we
- // don't want huge queue depth
- int queue_depth_ = 8;
- std::unique_ptr<struct io_uring> ring_;
-};
-
-class UpdateVerify {
- public:
- UpdateVerify(const std::string& misc_name);
- void VerifyUpdatePartition();
- bool CheckPartitionVerification();
-
- private:
- enum class UpdateVerifyState {
- VERIFY_UNKNOWN,
- VERIFY_FAILED,
- VERIFY_SUCCESS,
- };
-
- std::string misc_name_;
- UpdateVerifyState state_;
- std::mutex m_lock_;
- std::condition_variable m_cv_;
-
- int kMinThreadsToVerify = 1;
- int kMaxThreadsToVerify = 4;
- uint64_t kThresholdSize = 512_MiB;
- uint64_t kBlockSizeVerify = 1_MiB;
-
- bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
- void UpdatePartitionVerificationState(UpdateVerifyState state);
- bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
- bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
- off_t offset, int skip_blocks, uint64_t dev_sz);
-};
-
class Worker {
public:
Worker(const std::string& cow_device, const std::string& backing_device,
@@ -383,7 +279,7 @@
MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
bool IsIouringSupported();
- bool CheckPartitionVerification() { return update_verify_->CheckPartitionVerification(); }
+ bool CheckPartitionVerification();
private:
bool ReadMetadata();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 17f1f0e..af24286 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "snapuserd_readahead.h"
+
#include "snapuserd_core.h"
namespace android {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
new file mode 100644
index 0000000..5e94de0
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
@@ -0,0 +1,112 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_reader.h>
+#include <liburing.h>
+#include <snapuserd/snapuserd_buffer.h>
+
+namespace android {
+namespace snapshot {
+
+class SnapshotHandler;
+
+class ReadAhead {
+ public:
+ ReadAhead(const std::string& cow_device, const std::string& backing_device,
+ const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
+ bool RunThread();
+
+ private:
+ void InitializeRAIter();
+ bool RAIterDone();
+ void RAIterNext();
+ void RAResetIter(uint64_t num_blocks);
+ const CowOperation* GetRAOpIter();
+
+ void InitializeBuffer();
+ bool InitReader();
+ bool InitializeFds();
+
+ void CloseFds() { backing_store_fd_ = {}; }
+
+ bool ReadAheadIOStart();
+ int PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
+ std::vector<uint64_t>& blocks,
+ std::vector<const CowOperation*>& xor_op_vec);
+ bool ReconstructDataFromCow();
+ void CheckOverlap(const CowOperation* cow_op);
+
+ bool ReadAheadAsyncIO();
+ bool ReapIoCompletions(int pending_ios_to_complete);
+ bool ReadXorData(size_t block_index, size_t xor_op_index,
+ std::vector<const CowOperation*>& xor_op_vec);
+ void ProcessXorData(size_t& block_xor_index, size_t& xor_index,
+ std::vector<const CowOperation*>& xor_op_vec, void* buffer,
+ loff_t& buffer_offset);
+ void UpdateScratchMetadata();
+
+ bool ReadAheadSyncIO();
+ bool InitializeIouring();
+ void FinalizeIouring();
+
+ void* read_ahead_buffer_;
+ void* metadata_buffer_;
+
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+
+ std::string cow_device_;
+ std::string backing_store_device_;
+ std::string misc_name_;
+
+ android::base::unique_fd cow_fd_;
+ android::base::unique_fd backing_store_fd_;
+
+ std::shared_ptr<SnapshotHandler> snapuserd_;
+ std::unique_ptr<CowReader> reader_;
+
+ std::unordered_set<uint64_t> dest_blocks_;
+ std::unordered_set<uint64_t> source_blocks_;
+ bool overlap_;
+ std::vector<uint64_t> blocks_;
+ int total_blocks_merged_ = 0;
+ std::unique_ptr<uint8_t[]> ra_temp_buffer_;
+ std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
+ BufferSink bufsink_;
+
+ uint64_t total_ra_blocks_completed_ = 0;
+ bool read_ahead_async_ = false;
+ // Queue depth of 8 seems optimal. We don't want
+ // to have a huge depth as it may put more memory pressure
+ // on the kernel worker threads given that we use
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
+ std::unique_ptr<struct io_uring> ring_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 57f9e7a..efe0c14 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -125,6 +125,7 @@
void SimulateDaemonRestart();
+ std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
void CreateCowDevice();
void CreateCowDeviceOrderedOps();
void CreateCowDeviceOrderedOpsInverted();
@@ -277,23 +278,30 @@
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
-void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
+std::unique_ptr<ICowWriter> SnapuserdTest::CreateCowDeviceInternal() {
std::string path = android::base::GetExecutableDirectory();
cow_system_ = std::make_unique<TemporaryFile>(path);
CowOptions options;
options.compression = "gz";
- CowWriter writer(options);
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+ unique_fd fd(cow_system_->fd);
+ cow_system_->fd = -1;
- size_t num_blocks = size_ / options.block_size;
+ return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
+void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
+
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = 0;
// Create overlapping copy operations
while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
+ ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));
x -= 1;
if (x == 1) {
break;
@@ -302,7 +310,7 @@
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -330,22 +338,16 @@
}
void SnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = num_blocks - 1;
// Create overlapping copy operations
while (1) {
- ASSERT_TRUE(writer.AddCopy(blk_src_copy + 1, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));
x -= 1;
if (x == 0) {
ASSERT_EQ(blk_src_copy, 0);
@@ -355,7 +357,7 @@
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -365,10 +367,11 @@
true);
// Merged operations
- ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), options.block_size, 0),
+ ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),
+ 0),
true);
ASSERT_EQ(android::base::ReadFullyAtOffset(
- base_fd_, (char*)orig_buffer_.get() + options.block_size, size_, 0),
+ base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),
true);
}
@@ -376,8 +379,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -392,13 +395,7 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 3;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
@@ -406,7 +403,7 @@
size_t x = num_blocks;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
@@ -416,12 +413,12 @@
}
for (size_t i = num_blocks; i > 0; i--) {
- ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
- &random_buffer_1_.get()[options.block_size * (i - 1)],
- options.block_size, 2 * num_blocks + i - 1, xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(
+ num_blocks + i - 1, &random_buffer_1_.get()[writer->GetBlockSize() * (i - 1)],
+ writer->GetBlockSize(), 2 * num_blocks + i - 1, xor_offset));
}
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
@@ -439,8 +436,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -456,20 +453,14 @@
}
memset(random_buffer_1_.get(), 0, size_);
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t source_blk = 0;
size_t blk_src_copy = 2 * num_blocks;
uint16_t xor_offset = 5;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
@@ -479,10 +470,10 @@
blk_src_copy += 1;
}
- ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
- xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
+ xor_offset));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
@@ -500,8 +491,8 @@
unique_fd rnd_fd;
loff_t offset = 0;
- std::string path = android::base::GetExecutableDirectory();
- cow_system_ = std::make_unique<TemporaryFile>(path);
+ auto writer = CreateCowDeviceInternal();
+ ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
@@ -516,13 +507,7 @@
offset += 1_MiB;
}
- CowOptions options;
- options.compression = "gz";
- CowWriter writer(options);
-
- ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
- size_t num_blocks = size_ / options.block_size;
+ size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 2;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
@@ -536,11 +521,11 @@
for (int i = 0; i < num_blocks; i++) {
sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
}
- ASSERT_TRUE(writer.AddSequenceData(2 * num_blocks, sequence));
+ ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
size_t x = num_blocks;
while (1) {
- ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+ ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
@@ -552,24 +537,24 @@
source_blk = num_blocks;
blk_src_copy = blk_end_copy;
- ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
size_t blk_zero_copy_start = source_blk + num_blocks;
size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
- ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+ ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
size_t blk_random2_replace_start = blk_zero_copy_end;
- ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
+ ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
size_t blk_xor_start = blk_random2_replace_start + num_blocks;
size_t xor_offset = BLOCK_SZ / 2;
- ASSERT_TRUE(writer.AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
- xor_offset));
+ ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+ xor_offset));
// Flush operations
- ASSERT_TRUE(writer.Finalize());
+ ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
std::string zero_buffer(size_, 0);
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
index 18c1dfc..6817340 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-#include "snapuserd_core.h"
+#include "snapuserd_verify.h"
#include <android-base/chrono_utils.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
+#include "snapuserd_core.h"
+
namespace android {
namespace snapshot {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
new file mode 100644
index 0000000..d07d2f8
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <string>
+
+#include <snapuserd/snapuserd_kernel.h>
+#include <storage_literals/storage_literals.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android::storage_literals;
+
+class UpdateVerify {
+ public:
+ UpdateVerify(const std::string& misc_name);
+ void VerifyUpdatePartition();
+ bool CheckPartitionVerification();
+
+ private:
+ enum class UpdateVerifyState {
+ VERIFY_UNKNOWN,
+ VERIFY_FAILED,
+ VERIFY_SUCCESS,
+ };
+
+ std::string misc_name_;
+ UpdateVerifyState state_;
+ std::mutex m_lock_;
+ std::condition_variable m_cv_;
+
+ int kMinThreadsToVerify = 1;
+ int kMaxThreadsToVerify = 4;
+ uint64_t kThresholdSize = 512_MiB;
+ uint64_t kBlockSizeVerify = 1_MiB;
+
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ void UpdatePartitionVerificationState(UpdateVerifyState state);
+ bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
+ bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
+ off_t offset, int skip_blocks, uint64_t dev_sz);
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
index ac0dfbd..bbeabd5 100644
--- a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
+++ b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
@@ -37,6 +37,7 @@
using KiB = Size<10>;
using MiB = Size<20>;
using GiB = Size<30>;
+using TiB = Size<40>;
constexpr B operator""_B(unsigned long long v) { // NOLINT
return B{v};
@@ -54,6 +55,10 @@
return GiB{v};
}
+constexpr TiB operator""_TiB(unsigned long long v) { // NOLINT
+ return TiB{v};
+}
+
template <typename Dest, typename Src>
constexpr Dest size_cast(Src src) {
if (Src::power < Dest::power) {
@@ -69,6 +74,7 @@
static_assert(1_KiB == 1 << 10);
static_assert(1_MiB == 1 << 20);
static_assert(1_GiB == 1 << 30);
+static_assert(1_TiB == 1ULL << 40);
static_assert(size_cast<KiB>(1_B).count() == 0);
static_assert(size_cast<KiB>(1024_B).count() == 1);
static_assert(size_cast<KiB>(1_MiB).count() == 1024);
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index e33681c..5f889ca 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -497,6 +497,7 @@
EXPECT_EQ("none0", entry->mount_point);
{
FstabEntry::FsMgrFlags flags = {};
+ flags.file_encryption = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
EXPECT_EQ("", entry->metadata_key_dir);
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index bb2ceb9..4d771fa 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -55,6 +55,21 @@
}
TEST(fs, PartitionTypes) {
+ // Requirements only apply to Android 13+, 5.10+ devices.
+ int vsr_level = GetVsrLevel();
+ if (vsr_level < __ANDROID_API_T__) {
+ GTEST_SKIP();
+ }
+
+ struct utsname uts;
+ ASSERT_EQ(uname(&uts), 0);
+
+ unsigned int major, minor;
+ ASSERT_EQ(sscanf(uts.release, "%u.%u", &major, &minor), 2);
+ if (major < 5 || (major == 5 && minor < 10)) {
+ GTEST_SKIP();
+ }
+
android::fs_mgr::Fstab fstab;
ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
@@ -64,12 +79,7 @@
ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/super", &super_bdev));
ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
- int vsr_level = GetVsrLevel();
-
- std::vector<std::string> must_be_f2fs;
- if (vsr_level >= __ANDROID_API_T__) {
- must_be_f2fs.emplace_back("/data");
- }
+ std::vector<std::string> must_be_f2fs = {"/data"};
if (vsr_level >= __ANDROID_API_U__) {
must_be_f2fs.emplace_back("/metadata");
}
@@ -98,17 +108,13 @@
continue;
}
- if (vsr_level < __ANDROID_API_T__) {
- continue;
- }
- if (vsr_level == __ANDROID_API_T__ && parent_bdev != super_bdev) {
- // Only check for dynamic partitions at this VSR level.
- continue;
- }
-
if (entry.flags & MS_RDONLY) {
- std::vector<std::string> allowed = {"erofs", "ext4", "f2fs"};
+ if (parent_bdev != super_bdev) {
+ // Ignore non-AOSP partitions (eg anything outside of super).
+ continue;
+ }
+ std::vector<std::string> allowed = {"erofs", "ext4", "f2fs"};
EXPECT_NE(std::find(allowed.begin(), allowed.end(), entry.fs_type), allowed.end())
<< entry.mount_point;
} else {
diff --git a/init/README.md b/init/README.md
index 6bdff4a..5fced19 100644
--- a/init/README.md
+++ b/init/README.md
@@ -344,11 +344,14 @@
intended to be used with the `exec_start` builtin for any must-have checks during boot.
`restart_period <seconds>`
-> If a non-oneshot service exits, it will be restarted at its start time plus
- this period. It defaults to 5s to rate limit crashing services.
- This can be increased for services that are meant to run periodically. For
- example, it may be set to 3600 to indicate that the service should run every hour
- or 86400 to indicate that the service should run every day.
+> If a non-oneshot service exits, it will be restarted at its previous start time plus this period.
+ The default value is 5s. This can be used to implement periodic services together with the
+ `timeout_period` command below. For example, it may be set to 3600 to indicate that the service
+ should run every hour or 86400 to indicate that the service should run every day. This can be set
+ to a value shorter than 5s for example 0, but the minimum 5s delay is enforced if the restart was
+ due to a crash. This is to rate limit persistentally crashing services. In other words,
+ `<seconds>` smaller than 5 is respected only when the service exits deliverately and successfully
+ (i.e. by calling exit(0)).
`rlimit <resource> <cur> <max>`
> This applies the given rlimit to the service. rlimits are inherited by child
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 27a7876..3351c4c 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -680,8 +680,8 @@
<< "': " << result.error();
}
s->SetShutdownCritical();
- } else if (do_shutdown_animation) {
- continue;
+ } else if (do_shutdown_animation && s->classnames().count("animation") > 0) {
+ // Need these for shutdown animations.
} else if (s->IsShutdownCritical()) {
// Start shutdown critical service if not started.
if (auto result = s->Start(); !result.ok()) {
diff --git a/init/service.cpp b/init/service.cpp
index c152081..2945708 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -308,6 +308,7 @@
pid_ = 0;
flags_ &= (~SVC_RUNNING);
start_order_ = 0;
+ was_last_exit_ok_ = siginfo.si_code == CLD_EXITED && siginfo.si_status == 0;
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
@@ -361,7 +362,8 @@
// If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
// reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
- if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
+ if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART) &&
+ !was_last_exit_ok_) {
bool boot_completed = GetBoolProperty("sys.boot_completed", false);
if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {
if (++crash_count_ > 4) {
diff --git a/init/service.h b/init/service.h
index ce7c0da..b858eef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -19,6 +19,7 @@
#include <signal.h>
#include <sys/types.h>
+#include <algorithm>
#include <chrono>
#include <memory>
#include <optional>
@@ -115,6 +116,7 @@
pid_t pid() const { return pid_; }
android::base::boot_clock::time_point time_started() const { return time_started_; }
int crash_count() const { return crash_count_; }
+ int was_last_exit_ok() const { return was_last_exit_ok_; }
uid_t uid() const { return proc_attr_.uid(); }
gid_t gid() const { return proc_attr_.gid; }
int namespace_flags() const { return namespaces_.flags; }
@@ -130,7 +132,15 @@
bool process_cgroup_empty() const { return process_cgroup_empty_; }
unsigned long start_order() const { return start_order_; }
void set_sigstop(bool value) { sigstop_ = value; }
- std::chrono::seconds restart_period() const { return restart_period_; }
+ std::chrono::seconds restart_period() const {
+ // If the service exited abnormally or due to timeout, late limit the restart even if
+ // restart_period is set to a very short value.
+ // If not, i.e. restart after a deliberate and successful exit, respect the period.
+ if (!was_last_exit_ok_) {
+ return std::max(restart_period_, default_restart_period_);
+ }
+ return restart_period_;
+ }
std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
bool is_updatable() const { return updatable_; }
@@ -172,6 +182,8 @@
bool upgraded_mte_ = false; // whether we upgraded async MTE -> sync MTE before
std::chrono::minutes fatal_crash_window_ = 4min; // fatal() when more than 4 crashes in it
std::optional<std::string> fatal_reboot_target_; // reboot target of fatal handler
+ bool was_last_exit_ok_ =
+ true; // true if the service never exited, or exited with status code 0
std::optional<CapSet> capabilities_;
ProcessAttributes proc_attr_;
@@ -214,7 +226,8 @@
bool sigstop_ = false;
- std::chrono::seconds restart_period_ = 5s;
+ const std::chrono::seconds default_restart_period_ = 5s;
+ std::chrono::seconds restart_period_ = default_restart_period_;
std::optional<std::chrono::seconds> timeout_period_;
bool updatable_ = false;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index d46e1f7..a1b2cc5 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -370,8 +370,8 @@
Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) {
int period;
- if (!ParseInt(args[1], &period, 5)) {
- return Error() << "restart_period value must be an integer >= 5";
+ if (!ParseInt(args[1], &period, 0)) {
+ return Error() << "restart_period value must be an integer >= 0";
}
service_->restart_period_ = std::chrono::seconds(period);
return {};
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index d9fcd9d..dd46064 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -27,10 +27,13 @@
using std::literals::chrono_literals::operator""s;
void ExpectKillingServiceRecovers(const std::string& service_name) {
+ LOG(INFO) << "before we say hi to " << service_name << ", I can't have apexd around!";
+
// b/280514080 - servicemanager will restart apexd, and apexd will restart the
// system when crashed. This is fine as the device recovers, but it causes
// flakes in this test.
- ASSERT_TRUE(WaitForProperty("init.svc.apexd", "stopped", 60s)) << "apexd won't stop";
+ ASSERT_TRUE(WaitForProperty("init.svc.apexd", "stopped", 60s))
+ << (system("cat /dev/binderfs/binder_logs/state"), "apexd won't stop");
LOG(INFO) << "hello " << service_name << "!";
const std::string status_prop = "init.svc." + service_name;
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index b135e57..0b5c125 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -286,11 +286,14 @@
],
}
+always_static_test_libraries = [
+ "libjsoncpp",
+]
+
test_libraries = [
"libcutils",
"liblog",
"libbase",
- "libjsoncpp",
"libprocessgroup",
"libcgrouprc",
]
@@ -301,6 +304,7 @@
defaults: ["libcutils_test_default"],
host_supported: true,
shared_libs: test_libraries,
+ static_libs: always_static_test_libraries,
require_root: true,
}
@@ -310,7 +314,7 @@
static_libs: [
"libc",
"libcgrouprc_format",
- ] + test_libraries,
+ ] + test_libraries + always_static_test_libraries,
stl: "libc++_static",
require_root: true,
diff --git a/libcutils/KernelLibcutilsTest.xml b/libcutils/KernelLibcutilsTest.xml
index 40e4ef4..9750cbf 100644
--- a/libcutils/KernelLibcutilsTest.xml
+++ b/libcutils/KernelLibcutilsTest.xml
@@ -22,11 +22,11 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="KernelLibcutilsTest->/data/local/tmp/KernelLibcutilsTest" />
+ <option name="push" value="KernelLibcutilsTest->/data/local/tests/unrestricted/KernelLibcutilsTest" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="native-test-device-path" value="/data/local/tests/unrestricted" />
<option name="module-name" value="KernelLibcutilsTest" />
<option name="include-filter" value="*AshmemTest*" />
</test>
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index eb63aa5..78b3e44 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -12,6 +12,9 @@
"kernel-presubmit": [
{
"name": "libcutils_test"
+ },
+ {
+ "name": "KernelLibcutilsTest"
}
]
}
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 1971f01..858b955 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -439,54 +439,58 @@
return module_blocklist_.count(canonical_name) > 0;
}
-// Another option to load kernel modules. load in independent modules in parallel
-// and then update dependency list of other remaining modules, repeat these steps
-// until all modules are loaded.
+// Another option to load kernel modules. load independent modules dependencies
+// in parallel and then update dependency list of other remaining modules,
+// repeat these steps until all modules are loaded.
+// Discard all blocklist.
+// Softdeps are taken care in InsmodWithDeps().
bool Modprobe::LoadModulesParallel(int num_threads) {
bool ret = true;
- int count = -1;
- std::map<std::string, std::set<std::string>> mod_with_deps;
+ std::unordered_map<std::string, std::vector<std::string>> mod_with_deps;
// Get dependencies
for (const auto& module : module_load_) {
+ // Skip blocklist modules
+ if (IsBlocklisted(module)) {
+ LOG(VERBOSE) << "LMP: Blocklist: Module " << module << " skipping...";
+ continue;
+ }
auto dependencies = GetDependencies(MakeCanonical(module));
-
- for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) {
- mod_with_deps[module].emplace(*dep);
+ if (dependencies.empty()) {
+ LOG(ERROR) << "LMP: Hard-dep: Module " << module
+ << " not in .dep file";
+ return false;
}
+ mod_with_deps[MakeCanonical(module)] = dependencies;
}
- // Get soft dependencies
- for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
- if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
- mod_with_deps[MakeCanonical(it_mod)].emplace(
- GetDependencies(MakeCanonical(it_softdep))[0]);
- }
- }
-
- // Get soft post dependencies
- for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
- if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
- mod_with_deps[MakeCanonical(it_softdep)].emplace(
- GetDependencies(MakeCanonical(it_mod))[0]);
- }
- }
-
- while (!mod_with_deps.empty() && count != module_loaded_.size()) {
+ while (!mod_with_deps.empty()) {
std::vector<std::thread> threads;
std::vector<std::string> mods_path_to_load;
std::mutex vector_lock;
- count = module_loaded_.size();
// Find independent modules
for (const auto& [it_mod, it_dep] : mod_with_deps) {
- if (it_dep.size() == 1) {
- if (module_options_[it_mod].find("load_sequential=1") != std::string::npos) {
- if (!LoadWithAliases(it_mod, true) && !IsBlocklisted(it_mod)) {
- return false;
- }
- } else {
- mods_path_to_load.emplace_back(it_mod);
+ auto itd_last = it_dep.rbegin();
+ if (itd_last == it_dep.rend())
+ continue;
+
+ auto cnd_last = MakeCanonical(*itd_last);
+ // Hard-dependencies cannot be blocklisted
+ if (IsBlocklisted(cnd_last)) {
+ LOG(ERROR) << "LMP: Blocklist: Module-dep " << cnd_last
+ << " : failed to load module " << it_mod;
+ return false;
+ }
+
+ if (module_options_[cnd_last].find("load_sequential=1") != std::string::npos) {
+ if (!LoadWithAliases(cnd_last, true)) {
+ return false;
+ }
+ } else {
+ if (std::find(mods_path_to_load.begin(), mods_path_to_load.end(),
+ cnd_last) == mods_path_to_load.end()) {
+ mods_path_to_load.emplace_back(cnd_last);
}
}
}
@@ -502,7 +506,7 @@
lk.unlock();
ret_load &= LoadWithAliases(mod_to_load, true);
lk.lock();
- if (!ret_load && !IsBlocklisted(mod_to_load)) {
+ if (!ret_load) {
ret &= ret_load;
}
}
@@ -521,13 +525,18 @@
std::lock_guard guard(module_loaded_lock_);
// Remove loaded module form mod_with_deps and soft dependencies of other modules
for (const auto& module_loaded : module_loaded_) {
- mod_with_deps.erase(module_loaded);
+ if (mod_with_deps.find(module_loaded) != mod_with_deps.end()) {
+ mod_with_deps.erase(module_loaded);
+ }
}
// Remove loaded module form dependencies of other modules which are not loaded yet
for (const auto& module_loaded_path : module_loaded_paths_) {
for (auto& [mod, deps] : mod_with_deps) {
- deps.erase(module_loaded_path);
+ auto it = std::find(deps.begin(), deps.end(), module_loaded_path);
+ if (it != deps.end()) {
+ deps.erase(it);
+ }
}
}
}
diff --git a/libutils/Errors.cpp b/libutils/Errors.cpp
index 74f3bef..dfb4d9b 100644
--- a/libutils/Errors.cpp
+++ b/libutils/Errors.cpp
@@ -15,6 +15,8 @@
*/
#include <utils/Errors.h>
+#include <string.h>
+
namespace android {
std::string statusToString(status_t s) {
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index aed3b9b..d675598 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -28,7 +28,7 @@
using namespace android;
-static constexpr int NITERS = 1000000;
+static constexpr int NITERS = 500000;
static constexpr int INITIAL_STRONG_VALUE = 1 << 28; // Mirroring RefBase definition.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d755b50..0ee85c7 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -295,7 +295,7 @@
write /proc/sys/kernel/randomize_va_space 2
write /proc/sys/vm/mmap_min_addr 32768
write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
- write /proc/sys/net/unix/max_dgram_qlen 600
+ write /proc/sys/net/unix/max_dgram_qlen 2400
# Assign reasonable ceiling values for socket rcv/snd buffers.
# These should almost always be overridden by the target per the
@@ -1004,6 +1004,11 @@
exec_start derive_classpath
load_exports /data/system/environ/classpath
+ # Start ART's oneshot boot service to propagate boot experiment flags to
+ # dalvik.vm.*. This needs to be done before odsign since odrefresh uses and
+ # validates those properties against the signed cache-info.xml.
+ exec_start art_boot
+
# Start the on-device signing daemon, and wait for it to finish, to ensure
# ART artifacts are generated if needed.
# Must start after 'derive_classpath' to have *CLASSPATH variables set.