1 // Copyright (C) 2019 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #pragma once 16 17 #include <stdint.h> 18 19 #include <condition_variable> 20 #include <cstdint> 21 #include <future> 22 #include <memory> 23 #include <mutex> 24 #include <optional> 25 #include <queue> 26 #include <string> 27 #include <thread> 28 #include <utility> 29 #include <vector> 30 31 #include <android-base/unique_fd.h> 32 #include <libsnapshot/cow_format.h> 33 #include <libsnapshot/cow_reader.h> 34 35 namespace android { 36 namespace snapshot { 37 38 struct CowOptions { 39 uint32_t block_size = 4096; 40 std::string compression; 41 42 // Maximum number of blocks that can be written. 43 std::optional<uint64_t> max_blocks; 44 45 // Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1. 46 uint32_t cluster_ops = 200; 47 48 bool scratch_space = true; 49 50 // Preset the number of merged ops. Only useful for testing. 51 uint64_t num_merge_ops = 0; 52 53 // Number of threads for compression 54 int num_compress_threads = 0; 55 56 // Batch write cluster ops 57 bool batch_write = false; 58 }; 59 60 // Interface for writing to a snapuserd COW. All operations are ordered; merges 61 // will occur in the sequence they were added to the COW. 62 class ICowWriter { 63 public: ICowWriter(const CowOptions & options)64 explicit ICowWriter(const CowOptions& options) : options_(options) {} 65 ~ICowWriter()66 virtual ~ICowWriter() {} 67 68 // Encode an operation that copies the contents of |old_block| to the 69 // location of |new_block|. 'num_blocks' is the number of contiguous 70 // COPY operations from |old_block| to |new_block|. 71 bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1); 72 73 // Encode a sequence of raw blocks. |size| must be a multiple of the block size. 74 bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size); 75 76 // Add a sequence of xor'd blocks. |size| must be a multiple of the block size. 77 bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block, 78 uint16_t offset); 79 80 // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size. 81 bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks); 82 83 // Add a label to the op sequence. 84 bool AddLabel(uint64_t label); 85 86 // Add sequence data for op merging. Data is a list of the destination block numbers. 87 bool AddSequenceData(size_t num_ops, const uint32_t* data); 88 89 // Flush all pending writes. This must be called before closing the writer 90 // to ensure that the correct headers and footers are written. 91 virtual bool Finalize() = 0; 92 93 // Return number of bytes the cow image occupies on disk. 94 virtual uint64_t GetCowSize() = 0; 95 96 // Returns true if AddCopy() operations are supported. SupportsCopyOperation()97 virtual bool SupportsCopyOperation() const { return true; } 98 options()99 const CowOptions& options() { return options_; } 100 101 protected: 102 virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0; 103 virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0; 104 virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, 105 uint32_t old_block, uint16_t offset) = 0; 106 virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0; 107 virtual bool EmitLabel(uint64_t label) = 0; 108 virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0; 109 110 bool ValidateNewBlock(uint64_t new_block); 111 112 protected: 113 CowOptions options_; 114 }; 115 116 class CompressWorker { 117 public: 118 CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size); 119 bool RunThread(); 120 void EnqueueCompressBlocks(const void* buffer, size_t num_blocks); 121 bool GetCompressedBuffers(std::vector<std::basic_string<uint8_t>>* compressed_buf); 122 void Finalize(); 123 static std::basic_string<uint8_t> Compress(CowCompressionAlgorithm compression, 124 const void* data, size_t length); 125 126 static bool CompressBlocks(CowCompressionAlgorithm compression, size_t block_size, 127 const void* buffer, size_t num_blocks, 128 std::vector<std::basic_string<uint8_t>>* compressed_data); 129 130 private: 131 struct CompressWork { 132 const void* buffer; 133 size_t num_blocks; 134 bool compression_status = false; 135 std::vector<std::basic_string<uint8_t>> compressed_data; 136 }; 137 138 CowCompressionAlgorithm compression_; 139 uint32_t block_size_; 140 141 std::queue<CompressWork> work_queue_; 142 std::queue<CompressWork> compressed_queue_; 143 std::mutex lock_; 144 std::condition_variable cv_; 145 bool stopped_ = false; 146 147 std::basic_string<uint8_t> Compress(const void* data, size_t length); 148 bool CompressBlocks(const void* buffer, size_t num_blocks, 149 std::vector<std::basic_string<uint8_t>>* compressed_data); 150 }; 151 152 class CowWriter : public ICowWriter { 153 public: 154 explicit CowWriter(const CowOptions& options); 155 ~CowWriter(); 156 157 // Set up the writer. 158 // The file starts from the beginning. 159 // 160 // If fd is < 0, the CowWriter will be opened against /dev/null. This is for 161 // computing COW sizes without using storage space. 162 bool Initialize(android::base::unique_fd&& fd); 163 bool Initialize(android::base::borrowed_fd fd); 164 // Set up a writer, assuming that the given label is the last valid label. 165 // This will result in dropping any labels that occur after the given on, and will fail 166 // if the given label does not appear. 167 bool InitializeAppend(android::base::unique_fd&&, uint64_t label); 168 bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label); 169 170 bool Finalize() override; 171 172 uint64_t GetCowSize() override; 173 GetCowVersion()174 uint32_t GetCowVersion() { return header_.major_version; } 175 176 protected: 177 virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override; 178 virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override; 179 virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, 180 uint32_t old_block, uint16_t offset) override; 181 virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override; 182 virtual bool EmitLabel(uint64_t label) override; 183 virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override; 184 185 private: 186 bool EmitCluster(); 187 bool EmitClusterIfNeeded(); 188 bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block, 189 uint16_t offset, uint8_t type); 190 void SetupHeaders(); 191 void SetupWriteOptions(); 192 bool ParseOptions(); 193 bool OpenForWrite(); 194 bool OpenForAppend(uint64_t label); 195 bool GetDataPos(uint64_t* pos); 196 bool WriteRawData(const void* data, size_t size); 197 bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0); 198 void AddOperation(const CowOperation& op); 199 void InitPos(); 200 void InitBatchWrites(); 201 void InitWorkers(); 202 bool FlushCluster(); 203 204 bool CompressBlocks(size_t num_blocks, const void* data); 205 bool SetFd(android::base::borrowed_fd fd); 206 bool Sync(); 207 bool Truncate(off_t length); 208 bool EnsureSpaceAvailable(const uint64_t bytes_needed) const; 209 210 private: 211 android::base::unique_fd owned_fd_; 212 android::base::borrowed_fd fd_; 213 CowHeader header_{}; 214 CowFooter footer_{}; 215 CowCompressionAlgorithm compression_ = kCowCompressNone; 216 uint64_t current_op_pos_ = 0; 217 uint64_t next_op_pos_ = 0; 218 uint64_t next_data_pos_ = 0; 219 uint64_t current_data_pos_ = 0; 220 ssize_t total_data_written_ = 0; 221 uint32_t cluster_size_ = 0; 222 uint32_t current_cluster_size_ = 0; 223 uint64_t current_data_size_ = 0; 224 bool is_dev_null_ = false; 225 bool merge_in_progress_ = false; 226 bool is_block_device_ = false; 227 uint64_t cow_image_size_ = INT64_MAX; 228 229 int num_compress_threads_ = 1; 230 std::vector<std::unique_ptr<CompressWorker>> compress_threads_; 231 std::vector<std::future<bool>> threads_; 232 std::vector<std::basic_string<uint8_t>> compressed_buf_; 233 std::vector<std::basic_string<uint8_t>>::iterator buf_iter_; 234 235 std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_; 236 std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_; 237 std::unique_ptr<struct iovec[]> cowop_vec_; 238 int op_vec_index_ = 0; 239 240 std::unique_ptr<struct iovec[]> data_vec_; 241 int data_vec_index_ = 0; 242 bool batch_write_ = false; 243 }; 244 245 } // namespace snapshot 246 } // namespace android 247