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 if (!WriteToImageFile(metadata_file, *exported.get())) {
115 LOG(ERROR) << "Unable to save new metadata";
116 return false;
117 }
118
119 return true;
120 }
121
RemoveAllMetadata(const std::string & dir)122 bool RemoveAllMetadata(const std::string& dir) {
123 auto metadata_file = GetMetadataFile(dir);
124 std::string err;
125 if (!android::base::RemoveFileIfExists(metadata_file, &err)) {
126 LOG(ERROR) << "Could not remove metadata file: " << err;
127 return false;
128 }
129 return true;
130 }
131
FillPartitionExtents(MetadataBuilder * builder,Partition * partition,SplitFiemap * file,uint64_t partition_size)132 bool FillPartitionExtents(MetadataBuilder* builder, Partition* partition, SplitFiemap* file,
133 uint64_t partition_size) {
134 auto block_device = android::base::Basename(GetDevicePathForFile(file));
135
136 uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;
137 for (const auto& extent : file->extents()) {
138 if (extent.fe_length % LP_SECTOR_SIZE != 0) {
139 LOG(ERROR) << "Extent is not sector-aligned: " << extent.fe_length;
140 return false;
141 }
142 if (extent.fe_physical % LP_SECTOR_SIZE != 0) {
143 LOG(ERROR) << "Extent physical sector is not sector-aligned: " << extent.fe_physical;
144 return false;
145 }
146
147 uint64_t num_sectors =
148 std::min(static_cast<uint64_t>(extent.fe_length / LP_SECTOR_SIZE), sectors_needed);
149 if (!num_sectors || !sectors_needed) {
150 // This should never happen, but we include it just in case. It would
151 // indicate that the last filesystem block had multiple extents.
152 LOG(WARNING) << "FiemapWriter allocated extra blocks";
153 break;
154 }
155
156 uint64_t physical_sector = extent.fe_physical / LP_SECTOR_SIZE;
157 if (!builder->AddLinearExtent(partition, block_device, num_sectors, physical_sector)) {
158 LOG(ERROR) << "Could not add extent to lp metadata";
159 return false;
160 }
161
162 sectors_needed -= num_sectors;
163 }
164 return true;
165 }
166
RemoveImageMetadata(const std::string & metadata_dir,const std::string & partition_name)167 bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name) {
168 if (!MetadataExists(metadata_dir)) {
169 return true;
170 }
171 auto metadata = OpenMetadata(metadata_dir);
172 if (!metadata) {
173 return false;
174 }
175
176 PartitionOpener opener;
177 auto builder = MetadataBuilder::New(*metadata.get(), &opener);
178 if (!builder) {
179 return false;
180 }
181 builder->RemovePartition(partition_name);
182 return SaveMetadata(builder.get(), metadata_dir);
183 }
184
UpdateMetadata(const std::string & metadata_dir,const std::string & partition_name,SplitFiemap * file,uint64_t partition_size,bool readonly)185 bool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,
186 SplitFiemap* file, uint64_t partition_size, bool readonly) {
187 auto builder = OpenOrCreateMetadata(metadata_dir, file);
188 if (!builder) {
189 return false;
190 }
191 auto partition = builder->FindPartition(partition_name);
192 if (!partition) {
193 int attrs = 0;
194 if (readonly) attrs |= LP_PARTITION_ATTR_READONLY;
195
196 if ((partition = builder->AddPartition(partition_name, attrs)) == nullptr) {
197 LOG(ERROR) << "Could not add partition " << partition_name << " to metadata";
198 return false;
199 }
200 }
201 partition->RemoveExtents();
202
203 if (!FillPartitionExtents(builder.get(), partition, file, partition_size)) {
204 return false;
205 }
206 return SaveMetadata(builder.get(), metadata_dir);
207 }
208
AddAttributes(const std::string & metadata_dir,const std::string & partition_name,uint32_t attributes)209 bool AddAttributes(const std::string& metadata_dir, const std::string& partition_name,
210 uint32_t attributes) {
211 auto metadata = OpenMetadata(metadata_dir);
212 if (!metadata) {
213 return false;
214 }
215 auto builder = MetadataBuilder::New(*metadata.get());
216 if (!builder) {
217 return false;
218 }
219 auto partition = builder->FindPartition(partition_name);
220 if (!partition) {
221 return false;
222 }
223 partition->set_attributes(partition->attributes() | attributes);
224 return SaveMetadata(builder.get(), metadata_dir);
225 }
226
227 } // namespace fiemap
228 } // namespace android
229