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