1 /*
2 * Copyright (C) 2018 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 "libdm/dm_target.h"
18
19 #include <inttypes.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22
23 #include <android-base/logging.h>
24 #include <android-base/macros.h>
25 #include <android-base/parseint.h>
26 #include <android-base/strings.h>
27
28 #include <libdm/dm.h>
29
30 namespace android {
31 namespace dm {
32
Serialize() const33 std::string DmTarget::Serialize() const {
34 // Create a string containing a dm_target_spec, parameter data, and an
35 // explicit null terminator.
36 std::string data(sizeof(dm_target_spec), '\0');
37 data += GetParameterString();
38 data.push_back('\0');
39
40 // The kernel expects each target to be 8-byte aligned.
41 size_t padding = DM_ALIGN(data.size()) - data.size();
42 for (size_t i = 0; i < padding; i++) {
43 data.push_back('\0');
44 }
45
46 // Finally fill in the dm_target_spec.
47 struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
48 spec->sector_start = start();
49 spec->length = size();
50 snprintf(spec->target_type, sizeof(spec->target_type), "%s", name().c_str());
51 spec->next = (uint32_t)data.size();
52 return data;
53 }
54
GetParameterString() const55 std::string DmTargetZero::GetParameterString() const {
56 // The zero target type has no additional parameters.
57 return "";
58 }
59
GetParameterString() const60 std::string DmTargetLinear::GetParameterString() const {
61 return block_device_ + " " + std::to_string(physical_sector_);
62 }
63
DmTargetVerity(uint64_t start,uint64_t length,uint32_t version,const std::string & block_device,const std::string & hash_device,uint32_t data_block_size,uint32_t hash_block_size,uint32_t num_data_blocks,uint32_t hash_start_block,const std::string & hash_algorithm,const std::string & root_digest,const std::string & salt)64 DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
65 const std::string& block_device, const std::string& hash_device,
66 uint32_t data_block_size, uint32_t hash_block_size,
67 uint32_t num_data_blocks, uint32_t hash_start_block,
68 const std::string& hash_algorithm, const std::string& root_digest,
69 const std::string& salt)
70 : DmTarget(start, length), valid_(true) {
71 base_args_ = {
72 std::to_string(version),
73 block_device,
74 hash_device,
75 std::to_string(data_block_size),
76 std::to_string(hash_block_size),
77 std::to_string(num_data_blocks),
78 std::to_string(hash_start_block),
79 hash_algorithm,
80 root_digest,
81 salt,
82 };
83 }
84
UseFec(const std::string & device,uint32_t num_roots,uint32_t num_blocks,uint32_t start)85 void DmTargetVerity::UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks,
86 uint32_t start) {
87 optional_args_.emplace_back("use_fec_from_device");
88 optional_args_.emplace_back(device);
89 optional_args_.emplace_back("fec_roots");
90 optional_args_.emplace_back(std::to_string(num_roots));
91 optional_args_.emplace_back("fec_blocks");
92 optional_args_.emplace_back(std::to_string(num_blocks));
93 optional_args_.emplace_back("fec_start");
94 optional_args_.emplace_back(std::to_string(start));
95 }
96
SetVerityMode(const std::string & mode)97 void DmTargetVerity::SetVerityMode(const std::string& mode) {
98 if (mode != "panic_on_corruption" &&
99 mode != "restart_on_corruption" &&
100 mode != "ignore_corruption") {
101 LOG(ERROR) << "Unknown verity mode: " << mode;
102 valid_ = false;
103 return;
104 }
105 optional_args_.emplace_back(mode);
106 }
107
IgnoreZeroBlocks()108 void DmTargetVerity::IgnoreZeroBlocks() {
109 optional_args_.emplace_back("ignore_zero_blocks");
110 }
111
CheckAtMostOnce()112 void DmTargetVerity::CheckAtMostOnce() {
113 optional_args_.emplace_back("check_at_most_once");
114 }
115
GetParameterString() const116 std::string DmTargetVerity::GetParameterString() const {
117 std::string base = android::base::Join(base_args_, " ");
118 if (optional_args_.empty()) {
119 return base;
120 }
121 std::string optional = android::base::Join(optional_args_, " ");
122 return base + " " + std::to_string(optional_args_.size()) + " " + optional;
123 }
124
GetParameterString() const125 std::string DmTargetAndroidVerity::GetParameterString() const {
126 return keyid_ + " " + block_device_;
127 }
128
GetParameterString() const129 std::string DmTargetBow::GetParameterString() const {
130 if (!block_size_) return target_string_;
131 return target_string_ + " 1 block_size:" + std::to_string(block_size_);
132 }
133
name() const134 std::string DmTargetSnapshot::name() const {
135 if (mode_ == SnapshotStorageMode::Merge) {
136 return "snapshot-merge";
137 }
138 return "snapshot";
139 }
140
GetParameterString() const141 std::string DmTargetSnapshot::GetParameterString() const {
142 std::string mode;
143 switch (mode_) {
144 case SnapshotStorageMode::Persistent:
145 case SnapshotStorageMode::Merge:
146 // Note: "O" lets us query for overflow in the status message. This
147 // is only supported on kernels 4.4+. On earlier kernels, an overflow
148 // will be reported as "Invalid" in the status string.
149 mode = "P";
150 if (ReportsOverflow(name())) {
151 mode += "O";
152 }
153 break;
154 case SnapshotStorageMode::Transient:
155 mode = "N";
156 break;
157 default:
158 LOG(ERROR) << "DmTargetSnapshot unknown mode";
159 break;
160 }
161 return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
162 }
163
164 // Computes the percentage of complition for snapshot status.
165 // @sectors_initial is the number of sectors_allocated stored right before
166 // starting the merge.
MergePercent(const DmTargetSnapshot::Status & status,uint64_t sectors_initial)167 double DmTargetSnapshot::MergePercent(const DmTargetSnapshot::Status& status,
168 uint64_t sectors_initial) {
169 uint64_t s = status.sectors_allocated;
170 uint64_t t = status.total_sectors;
171 uint64_t m = status.metadata_sectors;
172 uint64_t i = sectors_initial == 0 ? t : sectors_initial;
173
174 if (t <= s || i <= s) {
175 return 0.0;
176 }
177 if (s == 0 || t == 0 || s <= m) {
178 return 100.0;
179 }
180 return 100.0 / (i - m) * (i - s);
181 }
182
ReportsOverflow(const std::string & target_type)183 bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
184 DeviceMapper& dm = DeviceMapper::Instance();
185 DmTargetTypeInfo info;
186 if (!dm.GetTargetByName(target_type, &info)) {
187 return false;
188 }
189 if (target_type == "snapshot") {
190 return info.IsAtLeast(1, 15, 0);
191 }
192 if (target_type == "snapshot-merge") {
193 return info.IsAtLeast(1, 4, 0);
194 }
195 return false;
196 }
197
ParseStatusText(const std::string & text,Status * status)198 bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
199 // Try to parse the line as it should be
200 int args = sscanf(text.c_str(), "%" PRIu64 "/%" PRIu64 " %" PRIu64, &status->sectors_allocated,
201 &status->total_sectors, &status->metadata_sectors);
202 if (args == 3) {
203 return true;
204 }
205 auto sections = android::base::Split(text, " ");
206 if (sections.size() == 0) {
207 LOG(ERROR) << "could not parse empty status";
208 return false;
209 }
210 // Error codes are: "Invalid", "Overflow" and "Merge failed"
211 if (sections.size() == 1) {
212 if (text == "Invalid" || text == "Overflow") {
213 status->error = text;
214 return true;
215 }
216 } else if (sections.size() == 2 && text == "Merge failed") {
217 status->error = text;
218 return true;
219 }
220 LOG(ERROR) << "could not parse snapshot status: wrong format";
221 return false;
222 }
223
GetDevicesFromParams(const std::string & params,std::string * base_device,std::string * cow_device)224 bool DmTargetSnapshot::GetDevicesFromParams(const std::string& params, std::string* base_device,
225 std::string* cow_device) {
226 auto pieces = android::base::Split(params, " ");
227 if (pieces.size() < 2) {
228 LOG(ERROR) << "Parameter string is invalid: " << params;
229 return false;
230 }
231 *base_device = pieces[0];
232 *cow_device = pieces[1];
233 return true;
234 }
235
GetParameterString() const236 std::string DmTargetCrypt::GetParameterString() const {
237 std::vector<std::string> argv = {
238 cipher_,
239 key_,
240 std::to_string(iv_sector_offset_),
241 device_,
242 std::to_string(device_sector_),
243 };
244
245 std::vector<std::string> extra_argv;
246 if (allow_discards_) extra_argv.emplace_back("allow_discards");
247 if (allow_encrypt_override_) extra_argv.emplace_back("allow_encrypt_override");
248 if (iv_large_sectors_) extra_argv.emplace_back("iv_large_sectors");
249 if (sector_size_) extra_argv.emplace_back("sector_size:" + std::to_string(sector_size_));
250
251 if (!extra_argv.empty()) argv.emplace_back(std::to_string(extra_argv.size()));
252
253 argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());
254 return android::base::Join(argv, " ");
255 }
256
Valid() const257 bool DmTargetDefaultKey::Valid() const {
258 if (!use_legacy_options_format_ && !set_dun_) return false;
259 return true;
260 }
261
GetParameterString() const262 std::string DmTargetDefaultKey::GetParameterString() const {
263 std::vector<std::string> argv;
264 argv.emplace_back(cipher_);
265 argv.emplace_back(key_);
266 if (!use_legacy_options_format_) {
267 argv.emplace_back("0"); // iv_offset
268 }
269 argv.emplace_back(blockdev_);
270 argv.push_back(std::to_string(start_sector_));
271 std::vector<std::string> extra_argv;
272 if (use_legacy_options_format_) {
273 if (set_dun_) { // v2 always sets the DUN.
274 extra_argv.emplace_back("set_dun");
275 }
276 } else {
277 extra_argv.emplace_back("allow_discards");
278 extra_argv.emplace_back("sector_size:4096");
279 extra_argv.emplace_back("iv_large_sectors");
280 if (is_hw_wrapped_) extra_argv.emplace_back("wrappedkey_v0");
281 }
282 if (!extra_argv.empty()) {
283 argv.emplace_back(std::to_string(extra_argv.size()));
284 argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());
285 }
286 return android::base::Join(argv, " ");
287 }
288
GetParameterString() const289 std::string DmTargetUser::GetParameterString() const {
290 std::vector<std::string> argv;
291 argv.push_back(std::to_string(start()));
292 argv.push_back(std::to_string(size()));
293 argv.push_back(control_device());
294 return android::base::Join(argv, " ");
295 }
296
297 } // namespace dm
298 } // namespace android
299