1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <libsnapshot/cow_format.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20
21 #include <android-base/logging.h>
22 #include <android-base/stringprintf.h>
23 #include <libsnapshot/cow_format.h>
24 #include <storage_literals/storage_literals.h>
25 #include "writer_v2.h"
26 #include "writer_v3.h"
27
28 namespace android {
29 namespace snapshot {
30
31 using android::base::unique_fd;
32 using namespace android::storage_literals;
33
EmitCowTypeString(std::ostream & os,CowOperationType cow_type)34 std::ostream& EmitCowTypeString(std::ostream& os, CowOperationType cow_type) {
35 switch (cow_type) {
36 case kCowCopyOp:
37 return os << "kCowCopyOp";
38 case kCowReplaceOp:
39 return os << "kCowReplaceOp";
40 case kCowZeroOp:
41 return os << "kZeroOp";
42 case kCowFooterOp:
43 return os << "kCowFooterOp";
44 case kCowLabelOp:
45 return os << "kCowLabelOp";
46 case kCowClusterOp:
47 return os << "kCowClusterOp";
48 case kCowXorOp:
49 return os << "kCowXorOp";
50 case kCowSequenceOp:
51 return os << "kCowSequenceOp";
52 default:
53 return os << (int)cow_type << "unknown";
54 }
55 }
56
operator <<(std::ostream & os,CowOperationType cow_type)57 std::ostream& operator<<(std::ostream& os, CowOperationType cow_type) {
58 return EmitCowTypeString(os, cow_type);
59 }
60
operator <<(std::ostream & os,CowOperationV2 const & op)61 std::ostream& operator<<(std::ostream& os, CowOperationV2 const& op) {
62 os << "CowOperationV2(";
63 EmitCowTypeString(os, op.type) << ", ";
64 switch (op.compression) {
65 case kCowCompressNone:
66 os << "uncompressed, ";
67 break;
68 case kCowCompressGz:
69 os << "gz, ";
70 break;
71 case kCowCompressBrotli:
72 os << "brotli, ";
73 break;
74 case kCowCompressLz4:
75 os << "lz4, ";
76 break;
77 case kCowCompressZstd:
78 os << "zstd, ";
79 break;
80 }
81 os << "data_length:" << op.data_length << ", ";
82 os << "new_block:" << op.new_block << ", ";
83 os << "source:" << op.source;
84 os << ")";
85 return os;
86 }
87
operator <<(std::ostream & os,CowOperationV3 const & op)88 std::ostream& operator<<(std::ostream& os, CowOperationV3 const& op) {
89 os << "CowOperation(";
90 EmitCowTypeString(os, op.type());
91 if (op.type() == kCowReplaceOp || op.type() == kCowXorOp || op.type() == kCowSequenceOp) {
92 os << ", data_length:" << op.data_length;
93 }
94 if (op.type() != kCowClusterOp && op.type() != kCowSequenceOp && op.type() != kCowLabelOp) {
95 os << ", new_block:" << op.new_block;
96 }
97 if (op.type() == kCowXorOp || op.type() == kCowReplaceOp || op.type() == kCowCopyOp) {
98 os << ", source:" << op.source();
99 } else if (op.type() == kCowClusterOp) {
100 os << ", cluster_data:" << op.source();
101 }
102 // V3 op stores resume points in header, so CowOp can never be Label.
103 os << ")";
104 return os;
105 }
106
operator <<(std::ostream & os,ResumePoint const & resume_point)107 std::ostream& operator<<(std::ostream& os, ResumePoint const& resume_point) {
108 os << "ResumePoint(" << resume_point.label << " , " << resume_point.op_index << ")";
109 return os;
110 }
111
GetNextOpOffset(const CowOperationV2 & op,uint32_t cluster_ops)112 int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {
113 if (op.type == kCowClusterOp) {
114 return op.source;
115 } else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
116 return op.data_length;
117 } else {
118 return 0;
119 }
120 }
121
GetNextDataOffset(const CowOperationV2 & op,uint32_t cluster_ops)122 int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_ops) {
123 if (op.type == kCowClusterOp) {
124 return cluster_ops * sizeof(CowOperationV2);
125 } else if (cluster_ops == 0) {
126 return sizeof(CowOperationV2);
127 } else {
128 return 0;
129 }
130 }
131
IsMetadataOp(const CowOperation & op)132 bool IsMetadataOp(const CowOperation& op) {
133 switch (op.type()) {
134 case kCowLabelOp:
135 case kCowClusterOp:
136 case kCowFooterOp:
137 case kCowSequenceOp:
138 return true;
139 default:
140 return false;
141 }
142 }
143
IsOrderedOp(const CowOperation & op)144 bool IsOrderedOp(const CowOperation& op) {
145 switch (op.type()) {
146 case kCowCopyOp:
147 case kCowXorOp:
148 return true;
149 default:
150 return false;
151 }
152 }
153
CreateCowWriter(uint32_t version,const CowOptions & options,unique_fd && fd,std::optional<uint64_t> label)154 std::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,
155 unique_fd&& fd, std::optional<uint64_t> label) {
156 std::unique_ptr<CowWriterBase> base;
157 switch (version) {
158 case 1:
159 case 2:
160 base = std::make_unique<CowWriterV2>(options, std::move(fd));
161 break;
162 case 3:
163 base = std::make_unique<CowWriterV3>(options, std::move(fd));
164 break;
165 default:
166 LOG(ERROR) << "Cannot create unknown cow version: " << version;
167 return nullptr;
168 }
169 if (!base->Initialize(label)) {
170 return nullptr;
171 }
172 return base;
173 }
174
CreateCowEstimator(uint32_t version,const CowOptions & options)175 std::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options) {
176 return CreateCowWriter(version, options, unique_fd{-1}, std::nullopt);
177 }
178
CowOpCompressionSize(const CowOperation * op,size_t block_size)179 size_t CowOpCompressionSize(const CowOperation* op, size_t block_size) {
180 uint8_t compression_bits = op->compression_bits();
181 return (block_size << compression_bits);
182 }
183
GetBlockOffset(const CowOperation * op,uint64_t io_block,size_t block_size,off_t * offset)184 bool GetBlockOffset(const CowOperation* op, uint64_t io_block, size_t block_size, off_t* offset) {
185 const uint64_t new_block = op->new_block;
186
187 if (op->type() != kCowReplaceOp || io_block < new_block) {
188 LOG(VERBOSE) << "Invalid IO request for block: " << io_block
189 << " CowOperation: new_block: " << new_block;
190 return false;
191 }
192
193 // Get the actual compression size
194 const size_t compression_size = CowOpCompressionSize(op, block_size);
195 // Find the number of blocks spanned
196 const size_t num_blocks = compression_size / block_size;
197 // Find the distance of the I/O block which this
198 // CowOperation encompasses
199 const size_t block_distance = io_block - new_block;
200 // Check if this block is within this range;
201 // if so, return the relative offset
202 if (block_distance < num_blocks) {
203 *offset = block_distance * block_size;
204 return true;
205 }
206
207 return false;
208 }
209
210 } // namespace snapshot
211 } // namespace android
212