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