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 "images.h"
18
19 #include <limits.h>
20 #include <sys/stat.h>
21
22 #include <android-base/file.h>
23
24 #include "reader.h"
25 #include "utility.h"
26 #include "writer.h"
27
28 namespace android {
29 namespace fs_mgr {
30
31 using android::base::borrowed_fd;
32 using android::base::unique_fd;
33
34 #if defined(_WIN32)
35 static const int O_NOFOLLOW = 0;
36 #endif
37
IsEmptySuperImage(borrowed_fd fd)38 static bool IsEmptySuperImage(borrowed_fd fd) {
39 struct stat s;
40 if (fstat(fd.get(), &s) < 0) {
41 PERROR << __PRETTY_FUNCTION__ << " fstat failed";
42 return false;
43 }
44 if (s.st_size < LP_METADATA_GEOMETRY_SIZE) {
45 return false;
46 }
47
48 // Rewind back to the start, read the geometry struct.
49 LpMetadataGeometry geometry = {};
50 if (SeekFile64(fd.get(), 0, SEEK_SET) < 0) {
51 PERROR << __PRETTY_FUNCTION__ << " lseek failed";
52 return false;
53 }
54 if (!android::base::ReadFully(fd, &geometry, sizeof(geometry))) {
55 PERROR << __PRETTY_FUNCTION__ << " read failed";
56 return false;
57 }
58 return geometry.magic == LP_METADATA_GEOMETRY_MAGIC;
59 }
60
IsEmptySuperImage(const std::string & file)61 bool IsEmptySuperImage(const std::string& file) {
62 unique_fd fd = GetControlFileOrOpen(file, O_RDONLY | O_CLOEXEC);
63 if (fd < 0) {
64 PERROR << __PRETTY_FUNCTION__ << " open failed";
65 return false;
66 }
67 return IsEmptySuperImage(fd);
68 }
69
ReadFromImageFile(int fd)70 std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
71 std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
72 if (SeekFile64(fd, 0, SEEK_SET) < 0) {
73 PERROR << __PRETTY_FUNCTION__ << " lseek failed";
74 return nullptr;
75 }
76 if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
77 PERROR << __PRETTY_FUNCTION__ << " read failed";
78 return nullptr;
79 }
80 LpMetadataGeometry geometry;
81 if (!ParseGeometry(buffer.get(), &geometry)) {
82 return nullptr;
83 }
84 return ParseMetadata(geometry, fd);
85 }
86
ReadFromImageBlob(const void * data,size_t bytes)87 std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes) {
88 if (bytes < LP_METADATA_GEOMETRY_SIZE) {
89 LERROR << __PRETTY_FUNCTION__ << ": " << bytes << " is smaller than geometry header";
90 return nullptr;
91 }
92
93 LpMetadataGeometry geometry;
94 if (!ParseGeometry(data, &geometry)) {
95 return nullptr;
96 }
97
98 const uint8_t* metadata_buffer =
99 reinterpret_cast<const uint8_t*>(data) + LP_METADATA_GEOMETRY_SIZE;
100 size_t metadata_buffer_size = bytes - LP_METADATA_GEOMETRY_SIZE;
101 return ParseMetadata(geometry, metadata_buffer, metadata_buffer_size);
102 }
103
ReadFromImageFile(const std::string & image_file)104 std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file) {
105 unique_fd fd = GetControlFileOrOpen(image_file.c_str(), O_RDONLY | O_CLOEXEC);
106 if (fd < 0) {
107 PERROR << __PRETTY_FUNCTION__ << " open failed: " << image_file;
108 return nullptr;
109 }
110 return ReadFromImageFile(fd);
111 }
112
WriteToImageFile(borrowed_fd fd,const LpMetadata & input)113 bool WriteToImageFile(borrowed_fd fd, const LpMetadata& input) {
114 std::string geometry = SerializeGeometry(input.geometry);
115 std::string metadata = SerializeMetadata(input);
116
117 std::string everything = geometry + metadata;
118
119 if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
120 PERROR << __PRETTY_FUNCTION__ << " write " << everything.size() << " bytes failed";
121 return false;
122 }
123 return true;
124 }
125
WriteToImageFile(const std::string & file,const LpMetadata & input)126 bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
127 unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));
128 if (fd < 0) {
129 PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
130 return false;
131 }
132 return WriteToImageFile(fd, input);
133 }
134
ImageBuilder(const LpMetadata & metadata,uint32_t block_size,const std::map<std::string,std::string> & images,bool sparsify)135 ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
136 const std::map<std::string, std::string>& images, bool sparsify)
137 : metadata_(metadata),
138 geometry_(metadata.geometry),
139 block_size_(block_size),
140 sparsify_(sparsify),
141 images_(images) {
142 uint64_t total_size = GetTotalSuperPartitionSize(metadata);
143 if (block_size % LP_SECTOR_SIZE != 0) {
144 LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
145 return;
146 }
147 if (total_size % block_size != 0) {
148 LERROR << "Device size must be a multiple of the block size, " << block_size;
149 return;
150 }
151 if (metadata.geometry.metadata_max_size % block_size != 0) {
152 LERROR << "Metadata max size must be a multiple of the block size, " << block_size;
153 return;
154 }
155 if (LP_METADATA_GEOMETRY_SIZE % block_size != 0) {
156 LERROR << "Geometry size is not a multiple of the block size, " << block_size;
157 return;
158 }
159 if (LP_PARTITION_RESERVED_BYTES % block_size != 0) {
160 LERROR << "Reserved size is not a multiple of the block size, " << block_size;
161 return;
162 }
163
164 uint64_t num_blocks = total_size / block_size;
165 if (num_blocks >= UINT_MAX) {
166 // libsparse counts blocks in unsigned 32-bit integers, so we check to
167 // make sure we're not going to overflow.
168 LERROR << "Block device is too large to encode with libsparse.";
169 return;
170 }
171
172 for (const auto& block_device : metadata.block_devices) {
173 SparsePtr file(sparse_file_new(block_size_, block_device.size), sparse_file_destroy);
174 if (!file) {
175 LERROR << "Could not allocate sparse file of size " << block_device.size;
176 return;
177 }
178 device_images_.emplace_back(std::move(file));
179 }
180 }
181
IsValid() const182 bool ImageBuilder::IsValid() const {
183 return device_images_.size() == metadata_.block_devices.size();
184 }
185
Export(const std::string & file)186 bool ImageBuilder::Export(const std::string& file) {
187 unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));
188 if (fd < 0) {
189 PERROR << "open failed: " << file;
190 return false;
191 }
192 if (device_images_.size() > 1) {
193 LERROR << "Cannot export to a single image on retrofit builds.";
194 return false;
195 }
196 // No gzip compression; no checksum.
197 int ret = sparse_file_write(device_images_[0].get(), fd, false, sparsify_, false);
198 if (ret != 0) {
199 LERROR << "sparse_file_write failed (error code " << ret << ")";
200 return false;
201 }
202 return true;
203 }
204
ExportFiles(const std::string & output_dir)205 bool ImageBuilder::ExportFiles(const std::string& output_dir) {
206 for (size_t i = 0; i < device_images_.size(); i++) {
207 std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
208 std::string file_name = "super_" + name + ".img";
209 std::string file_path = output_dir + "/" + file_name;
210
211 static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
212 unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));
213 if (fd < 0) {
214 PERROR << "open failed: " << file_path;
215 return false;
216 }
217 // No gzip compression; no checksum.
218 int ret = sparse_file_write(device_images_[i].get(), fd, false, sparsify_, false);
219 if (ret != 0) {
220 LERROR << "sparse_file_write failed (error code " << ret << ")";
221 return false;
222 }
223 }
224 return true;
225 }
226
AddData(sparse_file * file,const std::string & blob,uint64_t sector)227 bool ImageBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
228 uint32_t block;
229 if (!SectorToBlock(sector, &block)) {
230 return false;
231 }
232 void* data = const_cast<char*>(blob.data());
233 int ret = sparse_file_add_data(file, data, blob.size(), block);
234 if (ret != 0) {
235 LERROR << "sparse_file_add_data failed (error code " << ret << ")";
236 return false;
237 }
238 return true;
239 }
240
SectorToBlock(uint64_t sector,uint32_t * block)241 bool ImageBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
242 // The caller must ensure that the metadata has an alignment that is a
243 // multiple of the block size. liblp will take care of the rest, ensuring
244 // that all partitions are on an aligned boundary. Therefore all writes
245 // should be block-aligned, and if they are not, the table was misconfigured.
246 // Note that the default alignment is 1MiB, which is a multiple of the
247 // default block size (4096).
248 if ((sector * LP_SECTOR_SIZE) % block_size_ != 0) {
249 LERROR << "sector " << sector << " is not aligned to block size " << block_size_;
250 return false;
251 }
252 *block = (sector * LP_SECTOR_SIZE) / block_size_;
253 return true;
254 }
255
BlockToSector(uint64_t block) const256 uint64_t ImageBuilder::BlockToSector(uint64_t block) const {
257 return (block * block_size_) / LP_SECTOR_SIZE;
258 }
259
Build()260 bool ImageBuilder::Build() {
261 if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
262 LERROR << "Could not add initial sparse block for reserved zeroes";
263 return false;
264 }
265
266 std::string geometry_blob = SerializeGeometry(geometry_);
267 std::string metadata_blob = SerializeMetadata(metadata_);
268 metadata_blob.resize(geometry_.metadata_max_size);
269
270 // Two copies of geometry, then two copies of each metadata slot.
271 all_metadata_ += geometry_blob + geometry_blob;
272 for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
273 all_metadata_ += metadata_blob;
274 }
275
276 uint64_t first_sector = LP_PARTITION_RESERVED_BYTES / LP_SECTOR_SIZE;
277 if (!AddData(device_images_[0].get(), all_metadata_, first_sector)) {
278 return false;
279 }
280
281 if (!CheckExtentOrdering()) {
282 return false;
283 }
284
285 for (const auto& partition : metadata_.partitions) {
286 auto iter = images_.find(GetPartitionName(partition));
287 if (iter == images_.end()) {
288 continue;
289 }
290 if (!AddPartitionImage(partition, iter->second)) {
291 return false;
292 }
293 images_.erase(iter);
294 }
295
296 if (!images_.empty()) {
297 LERROR << "Partition image was specified but no partition was found.";
298 return false;
299 }
300 return true;
301 }
302
HasFillValue(uint32_t * buffer,size_t count)303 static inline bool HasFillValue(uint32_t* buffer, size_t count) {
304 uint32_t fill_value = buffer[0];
305 for (size_t i = 1; i < count; i++) {
306 if (fill_value != buffer[i]) {
307 return false;
308 }
309 }
310 return true;
311 }
312
AddPartitionImage(const LpMetadataPartition & partition,const std::string & file)313 bool ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,
314 const std::string& file) {
315 // Track which extent we're processing.
316 uint32_t extent_index = partition.first_extent_index;
317
318 const LpMetadataExtent& extent = metadata_.extents[extent_index];
319 if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
320 LERROR << "Partition should only have linear extents: " << GetPartitionName(partition);
321 return false;
322 }
323
324 int fd = OpenImageFile(file);
325 if (fd < 0) {
326 LERROR << "Could not open image for partition: " << GetPartitionName(partition);
327 return false;
328 }
329
330 // Make sure the image does not exceed the partition size.
331 uint64_t file_length;
332 if (!GetDescriptorSize(fd, &file_length)) {
333 LERROR << "Could not compute image size";
334 return false;
335 }
336 uint64_t partition_size = ComputePartitionSize(partition);
337 if (file_length > partition_size) {
338 LERROR << "Image for partition '" << GetPartitionName(partition)
339 << "' is greater than its size (" << file_length << ", expected " << partition_size
340 << ")";
341 return false;
342 }
343 if (SeekFile64(fd, 0, SEEK_SET)) {
344 PERROR << "lseek failed";
345 return false;
346 }
347
348 // We track the current logical sector and the position the current extent
349 // ends at.
350 uint64_t output_sector = 0;
351 uint64_t extent_last_sector = extent.num_sectors;
352
353 // We also track the output device and the current output block within that
354 // device.
355 uint32_t output_block;
356 if (!SectorToBlock(extent.target_data, &output_block)) {
357 return false;
358 }
359 sparse_file* output_device = device_images_[extent.target_source].get();
360
361 // Proceed to read the file and build sparse images.
362 uint64_t pos = 0;
363 uint64_t remaining = file_length;
364 while (remaining) {
365 // Check if we need to advance to the next extent.
366 if (output_sector == extent_last_sector) {
367 extent_index++;
368 if (extent_index >= partition.first_extent_index + partition.num_extents) {
369 LERROR << "image is larger than extent table";
370 return false;
371 }
372
373 const LpMetadataExtent& extent = metadata_.extents[extent_index];
374 extent_last_sector += extent.num_sectors;
375 output_device = device_images_[extent.target_source].get();
376 if (!SectorToBlock(extent.target_data, &output_block)) {
377 return false;
378 }
379 }
380
381 uint32_t buffer[block_size_ / sizeof(uint32_t)];
382 size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining);
383 if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) {
384 PERROR << "read failed";
385 return false;
386 }
387 if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) {
388 int rv = sparse_file_add_fd(output_device, fd, pos, read_size, output_block);
389 if (rv) {
390 LERROR << "sparse_file_add_fd failed with code: " << rv;
391 return false;
392 }
393 } else {
394 int rv = sparse_file_add_fill(output_device, buffer[0], read_size, output_block);
395 if (rv) {
396 LERROR << "sparse_file_add_fill failed with code: " << rv;
397 return false;
398 }
399 }
400 pos += read_size;
401 remaining -= read_size;
402 output_sector += block_size_ / LP_SECTOR_SIZE;
403 output_block++;
404 }
405
406 return true;
407 }
408
ComputePartitionSize(const LpMetadataPartition & partition) const409 uint64_t ImageBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
410 uint64_t sectors = 0;
411 for (size_t i = 0; i < partition.num_extents; i++) {
412 sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
413 }
414 return sectors * LP_SECTOR_SIZE;
415 }
416
417 // For simplicity, we don't allow serializing any configuration: extents must
418 // be ordered, such that any extent at position I in the table occurs *before*
419 // any extent after position I, for the same block device. We validate that
420 // here.
421 //
422 // Without this, it would be more difficult to find the appropriate extent for
423 // an output block. With this guarantee it is a linear walk.
CheckExtentOrdering()424 bool ImageBuilder::CheckExtentOrdering() {
425 std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
426
427 for (const auto& extent : metadata_.extents) {
428 if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
429 LERROR << "Extents must all be type linear.";
430 return false;
431 }
432 if (extent.target_data <= last_sectors[extent.target_source]) {
433 LERROR << "Extents must appear in increasing order.";
434 return false;
435 }
436 if ((extent.num_sectors * LP_SECTOR_SIZE) % block_size_ != 0) {
437 LERROR << "Extents must be aligned to the block size.";
438 return false;
439 }
440 last_sectors[extent.target_source] = extent.target_data;
441 }
442 return true;
443 }
444
OpenImageFile(const std::string & file)445 int ImageBuilder::OpenImageFile(const std::string& file) {
446 unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY);
447 if (source_fd < 0) {
448 PERROR << "open image file failed: " << file;
449 return -1;
450 }
451
452 SparsePtr source(sparse_file_import(source_fd, true, true), sparse_file_destroy);
453 if (!source) {
454 int fd = source_fd.get();
455 temp_fds_.push_back(std::move(source_fd));
456 return fd;
457 }
458
459 TemporaryFile tf;
460 if (tf.fd < 0) {
461 PERROR << "make temporary file failed";
462 return -1;
463 }
464
465 // We temporarily unsparse the file, rather than try to merge its chunks.
466 int rv = sparse_file_write(source.get(), tf.fd, false, false, false);
467 if (rv) {
468 LERROR << "sparse_file_write failed with code: " << rv;
469 return -1;
470 }
471 temp_fds_.push_back(android::base::unique_fd(tf.release()));
472 return temp_fds_.back().get();
473 }
474
WriteToImageFile(const std::string & file,const LpMetadata & metadata,uint32_t block_size,const std::map<std::string,std::string> & images,bool sparsify)475 bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
476 const std::map<std::string, std::string>& images, bool sparsify) {
477 ImageBuilder builder(metadata, block_size, images, sparsify);
478 return builder.IsValid() && builder.Build() && builder.Export(file);
479 }
480
WriteSplitImageFiles(const std::string & output_dir,const LpMetadata & metadata,uint32_t block_size,const std::map<std::string,std::string> & images,bool sparsify)481 bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
482 uint32_t block_size, const std::map<std::string, std::string>& images,
483 bool sparsify) {
484 ImageBuilder builder(metadata, block_size, images, sparsify);
485 return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
486 }
487
488 } // namespace fs_mgr
489 } // namespace android
490