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