diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp index 4cf2119a7..70fdac197 100644 --- a/fs_mgr/libsnapshot/cow_writer.cpp +++ b/fs_mgr/libsnapshot/cow_writer.cpp @@ -32,6 +32,45 @@ namespace snapshot { static_assert(sizeof(off_t) == sizeof(uint64_t)); +bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block) { + if (!ValidateNewBlock(new_block)) { + return false; + } + return EmitCopy(new_block, old_block); +} + +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::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::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) { SetupHeaders(); } @@ -134,7 +173,7 @@ bool CowWriter::OpenForAppend() { return true; } -bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) { +bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) { CowOperation op = {}; op.type = kCowCopyOp; op.new_block = new_block; @@ -143,13 +182,7 @@ bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) { return true; } -bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) { - if (size % header_.block_size != 0) { - LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of " - << header_.block_size; - return false; - } - +bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) { uint64_t pos; if (!GetDataPos(&pos)) { return false; @@ -195,7 +228,7 @@ bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t return true; } -bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) { +bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) { for (uint64_t i = 0; i < num_blocks; i++) { CowOperation op = {}; op.type = kCowZeroOp; @@ -291,7 +324,7 @@ bool CowWriter::Flush() { return true; } -size_t CowWriter::GetCowSize() { +uint64_t CowWriter::GetCowSize() { return header_.ops_offset + header_.num_ops * sizeof(CowOperation); } diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h index 8569161fb..245da0c7d 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h @@ -16,6 +16,7 @@ #include +#include #include #include @@ -27,6 +28,9 @@ namespace snapshot { struct CowOptions { uint32_t block_size = 4096; std::string compression; + + // Maximum number of blocks that can be written. + std::optional max_blocks; }; // Interface for writing to a snapuserd COW. All operations are ordered; merges @@ -39,20 +43,29 @@ class ICowWriter { // Encode an operation that copies the contents of |old_block| to the // location of |new_block|. - virtual bool AddCopy(uint64_t new_block, uint64_t old_block) = 0; + bool AddCopy(uint64_t new_block, uint64_t old_block); // Encode a sequence of raw blocks. |size| must be a multiple of the block size. - virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0; + bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size); // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size. - virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0; + bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks); // Flush all pending writes. This must be called before closing the writer // to ensure that the correct headers and footers are written. virtual bool Flush() = 0; // Return number of bytes the cow image occupies on disk. - virtual size_t GetCowSize() = 0; + virtual uint64_t GetCowSize() = 0; + + const CowOptions& options() { return options_; } + + protected: + virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0; + virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0; + virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0; + + bool ValidateNewBlock(uint64_t new_block); protected: CowOptions options_; @@ -68,13 +81,14 @@ class CowWriter : public ICowWriter { bool Initialize(android::base::unique_fd&& fd, OpenMode mode = OpenMode::WRITE); bool Initialize(android::base::borrowed_fd fd, OpenMode mode = OpenMode::WRITE); - bool AddCopy(uint64_t new_block, uint64_t old_block) override; - bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override; - bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override; - bool Flush() override; - size_t GetCowSize() override; + uint64_t GetCowSize() override; + + protected: + virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override; + virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override; + virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override; private: void SetupHeaders();