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 "metadata.h"
18 
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 
23 #include <android-base/file.h>
24 #include <android-base/logging.h>
25 #include <liblp/builder.h>
26 
27 #include "utility.h"
28 
29 namespace android {
30 namespace fiemap {
31 
32 using namespace android::fs_mgr;
33 using android::base::unique_fd;
34 
35 static constexpr uint32_t kMaxMetadataSize = 256 * 1024;
36 
GetMetadataFile(const std::string & metadata_dir)37 std::string GetMetadataFile(const std::string& metadata_dir) {
38     return JoinPaths(metadata_dir, "lp_metadata");
39 }
40 
MetadataExists(const std::string & metadata_dir)41 bool MetadataExists(const std::string& metadata_dir) {
42     auto metadata_file = GetMetadataFile(metadata_dir);
43     if (access(metadata_file.c_str(), F_OK)) {
44         if (errno != ENOENT) {
45             PLOG(ERROR) << "Access " << metadata_file << " failed:";
46         }
47         return false;
48     }
49     return true;
50 }
51 
OpenMetadata(const std::string & metadata_dir)52 std::unique_ptr<LpMetadata> OpenMetadata(const std::string& metadata_dir) {
53     auto metadata_file = GetMetadataFile(metadata_dir);
54     auto metadata = ReadFromImageFile(metadata_file);
55     if (!metadata) {
56         LOG(ERROR) << "Could not read metadata file " << metadata_file;
57         return nullptr;
58     }
59     return metadata;
60 }
61 
62 // :TODO: overwrite on create if open fails
OpenOrCreateMetadata(const std::string & metadata_dir,SplitFiemap * file)63 std::unique_ptr<MetadataBuilder> OpenOrCreateMetadata(const std::string& metadata_dir,
64                                                       SplitFiemap* file) {
65     auto metadata_file = GetMetadataFile(metadata_dir);
66 
67     PartitionOpener opener;
68     std::unique_ptr<MetadataBuilder> builder;
69     if (access(metadata_file.c_str(), R_OK)) {
70         if (errno != ENOENT) {
71             PLOG(ERROR) << "Access " << metadata_file << " failed:";
72             return nullptr;
73         }
74 
75         auto data_device = GetDevicePathForFile(file);
76 
77         BlockDeviceInfo device_info;
78         if (!opener.GetInfo(data_device, &device_info)) {
79             LOG(ERROR) << "Could not read partition: " << data_device;
80             return nullptr;
81         }
82 
83         std::vector<BlockDeviceInfo> block_devices = {device_info};
84         auto super_name = android::base::Basename(data_device);
85         builder = MetadataBuilder::New(block_devices, super_name, kMaxMetadataSize, 1);
86     } else {
87         auto metadata = OpenMetadata(metadata_dir);
88         if (!metadata) {
89             return nullptr;
90         }
91         builder = MetadataBuilder::New(*metadata.get(), &opener);
92     }
93 
94     if (!builder) {
95         LOG(ERROR) << "Could not create metadata builder";
96         return nullptr;
97     }
98     return builder;
99 }
100 
SaveMetadata(MetadataBuilder * builder,const std::string & metadata_dir)101 bool SaveMetadata(MetadataBuilder* builder, const std::string& metadata_dir) {
102     auto exported = builder->Export();
103     if (!exported) {
104         LOG(ERROR) << "Unable to export new metadata";
105         return false;
106     }
107 
108     // If there are no more partitions in the metadata, just delete the file.
109     auto metadata_file = GetMetadataFile(metadata_dir);
110     if (exported->partitions.empty() && android::base::RemoveFileIfExists(metadata_file)) {
111         return true;
112     }
113 
114     unique_fd fd(open(metadata_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY | O_SYNC, 0644));
115     if (fd < 0) {
116         LOG(ERROR) << "open failed: " << metadata_file;
117         return false;
118     }
119 
120     if (!WriteToImageFile(fd, *exported.get())) {
121         LOG(ERROR) << "Unable to save new metadata";
122         return false;
123     }
124 
125     return true;
126 }
127 
RemoveAllMetadata(const std::string & dir)128 bool RemoveAllMetadata(const std::string& dir) {
129     auto metadata_file = GetMetadataFile(dir);
130     std::string err;
131     if (!android::base::RemoveFileIfExists(metadata_file, &err)) {
132         LOG(ERROR) << "Could not remove metadata file: " << err;
133         return false;
134     }
135     return true;
136 }
137 
FillPartitionExtents(MetadataBuilder * builder,Partition * partition,SplitFiemap * file,uint64_t partition_size)138 bool FillPartitionExtents(MetadataBuilder* builder, Partition* partition, SplitFiemap* file,
139                           uint64_t partition_size) {
140     auto block_device = android::base::Basename(GetDevicePathForFile(file));
141 
142     uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;
143     for (const auto& extent : file->extents()) {
144         if (extent.fe_length % LP_SECTOR_SIZE != 0) {
145             LOG(ERROR) << "Extent is not sector-aligned: " << extent.fe_length;
146             return false;
147         }
148         if (extent.fe_physical % LP_SECTOR_SIZE != 0) {
149             LOG(ERROR) << "Extent physical sector is not sector-aligned: " << extent.fe_physical;
150             return false;
151         }
152 
153         uint64_t num_sectors =
154                 std::min(static_cast<uint64_t>(extent.fe_length / LP_SECTOR_SIZE), sectors_needed);
155         if (!num_sectors || !sectors_needed) {
156             // This should never happen, but we include it just in case. It would
157             // indicate that the last filesystem block had multiple extents.
158             LOG(WARNING) << "FiemapWriter allocated extra blocks";
159             break;
160         }
161 
162         uint64_t physical_sector = extent.fe_physical / LP_SECTOR_SIZE;
163         if (!builder->AddLinearExtent(partition, block_device, num_sectors, physical_sector)) {
164             LOG(ERROR) << "Could not add extent to lp metadata";
165             return false;
166         }
167 
168         sectors_needed -= num_sectors;
169     }
170     return true;
171 }
172 
RemoveImageMetadata(const std::string & metadata_dir,const std::string & partition_name)173 bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name) {
174     if (!MetadataExists(metadata_dir)) {
175         return true;
176     }
177     auto metadata = OpenMetadata(metadata_dir);
178     if (!metadata) {
179         return false;
180     }
181 
182     PartitionOpener opener;
183     auto builder = MetadataBuilder::New(*metadata.get(), &opener);
184     if (!builder) {
185         return false;
186     }
187     builder->RemovePartition(partition_name);
188     return SaveMetadata(builder.get(), metadata_dir);
189 }
190 
UpdateMetadata(const std::string & metadata_dir,const std::string & partition_name,SplitFiemap * file,uint64_t partition_size,bool readonly)191 bool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,
192                     SplitFiemap* file, uint64_t partition_size, bool readonly) {
193     auto builder = OpenOrCreateMetadata(metadata_dir, file);
194     if (!builder) {
195         return false;
196     }
197     auto partition = builder->FindPartition(partition_name);
198     if (!partition) {
199         int attrs = 0;
200         if (readonly) attrs |= LP_PARTITION_ATTR_READONLY;
201 
202         if ((partition = builder->AddPartition(partition_name, attrs)) == nullptr) {
203             LOG(ERROR) << "Could not add partition " << partition_name << " to metadata";
204             return false;
205         }
206     }
207     partition->RemoveExtents();
208 
209     if (!FillPartitionExtents(builder.get(), partition, file, partition_size)) {
210         return false;
211     }
212     return SaveMetadata(builder.get(), metadata_dir);
213 }
214 
AddAttributes(const std::string & metadata_dir,const std::string & partition_name,uint32_t attributes)215 bool AddAttributes(const std::string& metadata_dir, const std::string& partition_name,
216                    uint32_t attributes) {
217     auto metadata = OpenMetadata(metadata_dir);
218     if (!metadata) {
219         return false;
220     }
221     auto builder = MetadataBuilder::New(*metadata.get());
222     if (!builder) {
223         return false;
224     }
225     auto partition = builder->FindPartition(partition_name);
226     if (!partition) {
227         return false;
228     }
229     partition->set_attributes(partition->attributes() | attributes);
230     return SaveMetadata(builder.get(), metadata_dir);
231 }
232 
233 }  // namespace fiemap
234 }  // namespace android
235