Merge "init: Fix ramdump when enabling shutdown animations."
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index bec4ef1..7994065 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);
}
@@ -2583,10 +2583,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/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..dc733b9 100644
--- a/fastboot/storage.cpp
+++ b/fastboot/storage.cpp
@@ -23,24 +23,19 @@
#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 +58,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..cba6844 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -179,6 +179,7 @@
"libsnapshot_cow/cow_writer.cpp",
"libsnapshot_cow/cow_format.cpp",
"libsnapshot_cow/cow_compress.cpp",
+ "libsnapshot_cow/parser_v2.cpp",
],
host_supported: true,
recovery_available: true,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index c3ca00a..b228dff 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -28,6 +28,9 @@
static constexpr uint32_t kCowVersionManifest = 2;
+static constexpr uint32_t kMinCowVersion = 1;
+static constexpr uint32_t kMaxCowVersion = 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 +55,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 +93,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 +103,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 +164,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..7881f35 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -93,9 +93,6 @@
// 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:
@@ -171,8 +168,6 @@
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;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
index 29828bc..d798e25 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -31,9 +31,6 @@
// 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),
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..8f6344c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -89,38 +89,5 @@
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;
-};
-
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
index edc9d65..8f80bb3 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
@@ -65,9 +65,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;
@@ -114,9 +114,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;
@@ -193,9 +193,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;
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/cow_writer.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
index 0e18979..1eaa038 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
@@ -186,10 +186,10 @@
void CowWriter::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;
@@ -614,17 +614,6 @@
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() {
if (!FlushCluster()) {
LOG(ERROR) << "Finalize: FlushCluster() failed";
@@ -665,10 +654,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_))) {
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/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 2661482..e114d25 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3202,39 +3202,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 +3227,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 +3269,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()) {
@@ -3651,12 +3647,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 +3701,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..757f6f1 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));
@@ -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..6a3906e 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -149,95 +149,5 @@
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) {
- 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;
- }
- }
-
- return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitLabel(uint64_t) {
- // Not Needed
- return true;
-}
-
-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));
-}
-
} // namespace snapshot
} // namespace android
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..c3343b8 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;
}
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/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/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) {