xref: /aosp_15_r20/system/extras/partition_tools/lpunpack.cc (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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