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