1 //
2 // Copyright (C) 2023 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 "super_flash_helper.h"
18 
19 #include <android-base/logging.h>
20 
21 #include "util.h"
22 
23 using android::base::borrowed_fd;
24 using android::base::unique_fd;
25 using android::fs_mgr::SuperImageExtent;
26 
SuperFlashHelper(const ImageSource & source)27 SuperFlashHelper::SuperFlashHelper(const ImageSource& source) : source_(source) {}
28 
Open(borrowed_fd fd)29 bool SuperFlashHelper::Open(borrowed_fd fd) {
30     if (!builder_.Open(fd)) {
31         LOG(VERBOSE) << "device does not support optimized super flashing";
32         return false;
33     }
34 
35     base_metadata_ = builder_.Export();
36     return !!base_metadata_;
37 }
38 
IncludeInSuper(const std::string & partition)39 bool SuperFlashHelper::IncludeInSuper(const std::string& partition) {
40     return should_flash_in_userspace(*base_metadata_.get(), partition);
41 }
42 
AddPartition(const std::string & partition,const std::string & image_name,bool optional)43 bool SuperFlashHelper::AddPartition(const std::string& partition, const std::string& image_name,
44                                     bool optional) {
45     if (!IncludeInSuper(partition)) {
46         return true;
47     }
48     auto iter = image_fds_.find(image_name);
49     if (iter == image_fds_.end()) {
50         unique_fd fd = source_.OpenFile(image_name);
51         if (fd < 0) {
52             if (!optional) {
53                 LOG(VERBOSE) << "could not find partition image: " << image_name;
54                 return false;
55             }
56             return true;
57         }
58         if (is_sparse_file(fd)) {
59             LOG(VERBOSE) << "cannot optimize dynamic partitions with sparse images";
60             return false;
61         }
62         iter = image_fds_.emplace(image_name, std::move(fd)).first;
63     }
64 
65     if (!builder_.AddPartition(partition, image_name, get_file_size(iter->second))) {
66         return false;
67     }
68 
69     will_flash_.emplace(partition);
70     return true;
71 }
72 
GetSparseLayout()73 SparsePtr SuperFlashHelper::GetSparseLayout() {
74     // Cache extents since the sparse ptr depends on data pointers.
75     if (extents_.empty()) {
76         extents_ = builder_.GetImageLayout();
77         if (extents_.empty()) {
78             LOG(VERBOSE) << "device does not support optimized super flashing";
79             return {nullptr, nullptr};
80         }
81     }
82 
83     unsigned int block_size = base_metadata_->geometry.logical_block_size;
84     int64_t flashed_size = extents_.back().offset + extents_.back().size;
85     SparsePtr s(sparse_file_new(block_size, flashed_size), sparse_file_destroy);
86 
87     for (const auto& extent : extents_) {
88         if (extent.offset / block_size > UINT_MAX) {
89             // Super image is too big to send via sparse files (>8TB).
90             LOG(VERBOSE) << "super image is too big to flash";
91             return {nullptr, nullptr};
92         }
93         unsigned int block = extent.offset / block_size;
94 
95         int rv = 0;
96         switch (extent.type) {
97             case SuperImageExtent::Type::DONTCARE:
98                 break;
99             case SuperImageExtent::Type::ZERO:
100                 rv = sparse_file_add_fill(s.get(), 0, extent.size, block);
101                 break;
102             case SuperImageExtent::Type::DATA:
103                 rv = sparse_file_add_data(s.get(), extent.blob->data(), extent.size, block);
104                 break;
105             case SuperImageExtent::Type::PARTITION: {
106                 auto iter = image_fds_.find(extent.image_name);
107                 if (iter == image_fds_.end()) {
108                     LOG(FATAL) << "image added but not found: " << extent.image_name;
109                     return {nullptr, nullptr};
110                 }
111                 rv = sparse_file_add_fd(s.get(), iter->second.get(), extent.image_offset,
112                                         extent.size, block);
113                 break;
114             }
115             default:
116                 LOG(VERBOSE) << "unrecognized extent type in super image layout";
117                 return {nullptr, nullptr};
118         }
119         if (rv) {
120             LOG(VERBOSE) << "sparse failure building super image layout";
121             return {nullptr, nullptr};
122         }
123     }
124     return s;
125 }
126