• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 <fcntl.h>
18 #include <getopt.h>
19 #include <stdio.h>
20 #include <sysexits.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
24 #include <iostream>
25 #include <limits>
26 #include <string>
27 #include <unordered_map>
28 #include <unordered_set>
29 
30 #include <android-base/file.h>
31 #include <android-base/parseint.h>
32 #include <liblp/liblp.h>
33 #include <sparse/sparse.h>
34 
35 using namespace android::fs_mgr;
36 using android::base::unique_fd;
37 using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
38 
39 class ImageExtractor final {
40   public:
41     ImageExtractor(unique_fd&& image_fd, std::unique_ptr<LpMetadata>&& metadata,
42                    std::unordered_set<std::string>&& partitions, const std::string& output_dir);
43 
44     bool Extract();
45 
46   private:
47     bool BuildPartitionList();
48     bool ExtractPartition(const LpMetadataPartition* partition);
49     bool ExtractExtent(const LpMetadataExtent& extent, int output_fd);
50 
51     unique_fd image_fd_;
52     std::unique_ptr<LpMetadata> metadata_;
53     std::unordered_set<std::string> partitions_;
54     std::string output_dir_;
55     std::unordered_map<std::string, const LpMetadataPartition*> partition_map_;
56 };
57 
58 // Note that "sparse" here refers to filesystem sparse, not the Android sparse
59 // file format.
60 class SparseWriter final {
61   public:
62     SparseWriter(int output_fd, int image_fd, uint32_t block_size);
63 
64     bool WriteExtent(const LpMetadataExtent& extent);
65     bool Finish();
66 
67   private:
68     bool WriteBlock(const uint8_t* data);
69 
70     int output_fd_;
71     int image_fd_;
72     uint32_t block_size_;
73     off_t hole_size_ = 0;
74 };
75 
76 /* Prints program usage to |where|. */
usage(int,char * argv[])77 static int usage(int /* argc */, char* argv[]) {
78     fprintf(stderr,
79             "%s - command-line tool for extracting partition images from super\n"
80             "\n"
81             "Usage:\n"
82             "  %s [options...] SUPER_IMAGE [OUTPUT_DIR]\n"
83             "\n"
84             "Options:\n"
85             "  -p, --partition=NAME     Extract the named partition. This can\n"
86             "                           be specified multiple times.\n"
87             "  -S, --slot=NUM           Slot number (default is 0).\n",
88             argv[0], argv[0]);
89     return EX_USAGE;
90 }
91 
main(int argc,char * argv[])92 int main(int argc, char* argv[]) {
93     // clang-format off
94     struct option options[] = {
95         { "partition",  required_argument,  nullptr, 'p' },
96         { "slot",       required_argument,  nullptr, 'S' },
97         { nullptr,      0,                  nullptr, 0 },
98     };
99     // clang-format on
100 
101     uint32_t slot_num = 0;
102     std::unordered_set<std::string> partitions;
103 
104     int rv, index;
105     while ((rv = getopt_long_only(argc, argv, "+p:sh", options, &index)) != -1) {
106         switch (rv) {
107             case 'h':
108                 usage(argc, argv);
109                 return EX_OK;
110             case '?':
111                 std::cerr << "Unrecognized argument.\n";
112                 return usage(argc, argv);
113             case 'S':
114                 if (!android::base::ParseUint(optarg, &slot_num)) {
115                     std::cerr << "Slot must be a valid unsigned number.\n";
116                     return usage(argc, argv);
117                 }
118                 break;
119             case 'p':
120                 partitions.emplace(optarg);
121                 break;
122         }
123     }
124 
125     if (optind + 1 > argc) {
126         std::cerr << "Missing super image argument.\n";
127         return usage(argc, argv);
128     }
129     std::string super_path = argv[optind++];
130 
131     std::string output_dir = ".";
132     if (optind + 1 <= argc) {
133         output_dir = argv[optind++];
134     }
135 
136     if (optind < argc) {
137         std::cerr << "Unrecognized command-line arguments.\n";
138         return usage(argc, argv);
139     }
140 
141     // Done reading arguments; open super.img. PartitionOpener will decorate
142     // relative paths with /dev/block/by-name, so get an absolute path here.
143     std::string abs_super_path;
144     if (!android::base::Realpath(super_path, &abs_super_path)) {
145         std::cerr << "realpath failed: " << super_path << ": " << strerror(errno) << "\n";
146         return EX_OSERR;
147     }
148 
149     unique_fd fd(open(super_path.c_str(), O_RDONLY | O_CLOEXEC));
150     if (fd < 0) {
151         std::cerr << "open failed: " << abs_super_path << ": " << strerror(errno) << "\n";
152         return EX_OSERR;
153     }
154 
155     auto metadata = ReadMetadata(abs_super_path, slot_num);
156     if (!metadata) {
157         SparsePtr ptr(sparse_file_import(fd, false, false), sparse_file_destroy);
158         if (ptr) {
159             std::cerr << "This image appears to be a sparse image. It must be "
160                          "unsparsed to be"
161                       << " unpacked.\n";
162             return EX_USAGE;
163         }
164         std::cerr << "Image does not appear to be in super-partition format.\n";
165         return EX_USAGE;
166     }
167 
168     ImageExtractor extractor(std::move(fd), std::move(metadata), std::move(partitions), output_dir);
169     if (!extractor.Extract()) {
170         return EX_SOFTWARE;
171     }
172     return EX_OK;
173 }
174 
ImageExtractor(unique_fd && image_fd,std::unique_ptr<LpMetadata> && metadata,std::unordered_set<std::string> && partitions,const std::string & output_dir)175 ImageExtractor::ImageExtractor(unique_fd&& image_fd, std::unique_ptr<LpMetadata>&& metadata,
176                                std::unordered_set<std::string>&& partitions,
177                                const std::string& output_dir)
178     : image_fd_(std::move(image_fd)),
179       metadata_(std::move(metadata)),
180       partitions_(std::move(partitions)),
181       output_dir_(output_dir) {}
182 
Extract()183 bool ImageExtractor::Extract() {
184     if (!BuildPartitionList()) {
185         return false;
186     }
187 
188     for (const auto& [name, info] : partition_map_) {
189         if (!ExtractPartition(info)) {
190             return false;
191         }
192     }
193     return true;
194 }
195 
BuildPartitionList()196 bool ImageExtractor::BuildPartitionList() {
197     bool extract_all = partitions_.empty();
198 
199     for (const auto& partition : metadata_->partitions) {
200         auto name = GetPartitionName(partition);
201         if (extract_all || partitions_.count(name)) {
202             partition_map_[name] = &partition;
203             partitions_.erase(name);
204         }
205     }
206 
207     if (!extract_all && !partitions_.empty()) {
208         std::cerr << "Could not find partition: " << *partitions_.begin() << "\n";
209         return false;
210     }
211     return true;
212 }
213 
ExtractPartition(const LpMetadataPartition * partition)214 bool ImageExtractor::ExtractPartition(const LpMetadataPartition* partition) {
215     // Validate the extents and find the total image size.
216     uint64_t total_size = 0;
217     for (uint32_t i = 0; i < partition->num_extents; i++) {
218         uint32_t index = partition->first_extent_index + i;
219         const LpMetadataExtent& extent = metadata_->extents[index];
220 
221         if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
222             std::cerr << "Unsupported target type in extent: " << extent.target_type << "\n";
223             return false;
224         }
225         if (extent.target_source != 0) {
226             std::cerr << "Split super devices are not supported.\n";
227             return false;
228         }
229         total_size += extent.num_sectors * LP_SECTOR_SIZE;
230     }
231 
232     // Make a temporary file so we can import it with sparse_file_read.
233     std::string output_path = output_dir_ + "/" + GetPartitionName(*partition) + ".img";
234     unique_fd output_fd(open(output_path.c_str(), O_RDWR | O_CLOEXEC | O_CREAT | O_TRUNC, 0644));
235     if (output_fd < 0) {
236         std::cerr << "open failed: " << output_path << ": " << strerror(errno) << "\n";
237         return false;
238     }
239 
240     SparseWriter writer(output_fd, image_fd_, metadata_->geometry.logical_block_size);
241 
242     // Extract each extent into output_fd.
243     for (uint32_t i = 0; i < partition->num_extents; i++) {
244         uint32_t index = partition->first_extent_index + i;
245         const LpMetadataExtent& extent = metadata_->extents[index];
246 
247         if (!writer.WriteExtent(extent)) {
248             return false;
249         }
250     }
251     return writer.Finish();
252 }
253 
SparseWriter(int output_fd,int image_fd,uint32_t block_size)254 SparseWriter::SparseWriter(int output_fd, int image_fd, uint32_t block_size)
255     : output_fd_(output_fd), image_fd_(image_fd), block_size_(block_size) {}
256 
WriteExtent(const LpMetadataExtent & extent)257 bool SparseWriter::WriteExtent(const LpMetadataExtent& extent) {
258     auto buffer = std::make_unique<uint8_t[]>(block_size_);
259 
260     off_t super_offset = extent.target_data * LP_SECTOR_SIZE;
261     if (lseek(image_fd_, super_offset, SEEK_SET) < 0) {
262         std::cerr << "image lseek failed: " << strerror(errno) << "\n";
263         return false;
264     }
265 
266     uint64_t remaining_bytes = extent.num_sectors * LP_SECTOR_SIZE;
267     while (remaining_bytes) {
268         if (remaining_bytes < block_size_) {
269             std::cerr << "extent is not block-aligned\n";
270             return false;
271         }
272         if (!android::base::ReadFully(image_fd_, buffer.get(), block_size_)) {
273             std::cerr << "read failed: " << strerror(errno) << "\n";
274             return false;
275         }
276         if (!WriteBlock(buffer.get())) {
277             return false;
278         }
279         remaining_bytes -= block_size_;
280     }
281     return true;
282 }
283 
ShouldSkipChunk(const uint8_t * data,size_t len)284 static bool ShouldSkipChunk(const uint8_t* data, size_t len) {
285     for (size_t i = 0; i < len; i++) {
286         if (data[i] != 0) {
287             return false;
288         }
289     }
290     return true;
291 }
292 
WriteBlock(const uint8_t * data)293 bool SparseWriter::WriteBlock(const uint8_t* data) {
294     if (ShouldSkipChunk(data, block_size_)) {
295         hole_size_ += block_size_;
296         return true;
297     }
298 
299     if (hole_size_) {
300         if (lseek(output_fd_, hole_size_, SEEK_CUR) < 0) {
301             std::cerr << "lseek failed: " << strerror(errno) << "\n";
302             return false;
303         }
304         hole_size_ = 0;
305     }
306     if (!android::base::WriteFully(output_fd_, data, block_size_)) {
307         std::cerr << "write failed: " << strerror(errno) << "\n";
308         return false;
309     }
310     return true;
311 }
312 
Finish()313 bool SparseWriter::Finish() {
314     if (hole_size_) {
315         off_t offset = lseek(output_fd_, 0, SEEK_CUR);
316         if (offset < 0) {
317             std::cerr << "lseek failed: " << strerror(errno) << "\n";
318             return false;
319         }
320         if (ftruncate(output_fd_, offset + hole_size_) < 0) {
321             std::cerr << "ftruncate failed: " << strerror(errno) << "\n";
322             return false;
323         }
324     }
325     return true;
326 }
327