• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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