| // 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 |
| // |
| // 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 <condition_variable> |
| #include <cstdint> |
| #include <future> |
| #include <memory> |
| #include <mutex> |
| #include <optional> |
| #include <queue> |
| #include <string> |
| #include <thread> |
| #include <utility> |
| #include <vector> |
| |
| #include <android-base/unique_fd.h> |
| #include <libsnapshot/cow_format.h> |
| #include <libsnapshot/cow_reader.h> |
| |
| namespace android { |
| namespace snapshot { |
| |
| struct CowOptions { |
| uint32_t block_size = 4096; |
| std::string compression; |
| |
| // Maximum number of blocks that can be written. |
| std::optional<uint64_t> max_blocks; |
| |
| // Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1. |
| uint32_t cluster_ops = 200; |
| |
| bool scratch_space = true; |
| |
| // Preset the number of merged ops. Only useful for testing. |
| uint64_t num_merge_ops = 0; |
| |
| // Number of threads for compression |
| int num_compress_threads = 0; |
| |
| // Batch write cluster ops |
| bool batch_write = false; |
| }; |
| |
| // Interface for writing to a snapuserd COW. All operations are ordered; merges |
| // will occur in the sequence they were added to the COW. |
| class ICowWriter { |
| public: |
| using FileDescriptor = chromeos_update_engine::FileDescriptor; |
| |
| 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|. |
| 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. |
| 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. |
| 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. |
| virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0; |
| |
| // Add a label to the op sequence. |
| virtual bool AddLabel(uint64_t label) = 0; |
| |
| // Add sequence data for op merging. Data is a list of the destination block numbers. |
| 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. |
| virtual bool Finalize() = 0; |
| |
| // Return number of bytes the cow image occupies on disk. |
| virtual uint64_t GetCowSize() = 0; |
| |
| virtual uint32_t GetBlockSize() const = 0; |
| virtual std::optional<uint32_t> GetMaxBlocks() const = 0; |
| |
| // Open an ICowReader for this writer. The reader will be a snapshot of the |
| // current operations in the writer; new writes after OpenReader() will not |
| // be reflected. |
| virtual std::unique_ptr<ICowReader> OpenReader() = 0; |
| |
| // Open a file descriptor. This allows reading and seeing through the cow |
| // as if it were a normal file. The optional source_device must be a valid |
| // path if the CowReader contains any copy or xor operations. |
| virtual std::unique_ptr<FileDescriptor> OpenFileDescriptor( |
| const std::optional<std::string>& source_device) = 0; |
| }; |
| |
| class CompressWorker { |
| public: |
| CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size); |
| bool RunThread(); |
| void EnqueueCompressBlocks(const void* buffer, size_t num_blocks); |
| bool GetCompressedBuffers(std::vector<std::basic_string<uint8_t>>* compressed_buf); |
| void Finalize(); |
| static std::basic_string<uint8_t> Compress(CowCompressionAlgorithm compression, |
| const void* data, size_t length); |
| |
| static bool CompressBlocks(CowCompressionAlgorithm compression, size_t block_size, |
| const void* buffer, size_t num_blocks, |
| std::vector<std::basic_string<uint8_t>>* compressed_data); |
| |
| private: |
| struct CompressWork { |
| const void* buffer; |
| size_t num_blocks; |
| bool compression_status = false; |
| std::vector<std::basic_string<uint8_t>> compressed_data; |
| }; |
| |
| CowCompressionAlgorithm compression_; |
| uint32_t block_size_; |
| |
| std::queue<CompressWork> work_queue_; |
| std::queue<CompressWork> compressed_queue_; |
| std::mutex lock_; |
| std::condition_variable cv_; |
| bool stopped_ = false; |
| |
| std::basic_string<uint8_t> Compress(const void* data, size_t length); |
| bool CompressBlocks(const void* buffer, size_t num_blocks, |
| std::vector<std::basic_string<uint8_t>>* compressed_data); |
| }; |
| |
| // 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); |
| |
| // 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 |