1 /*
2  * Copyright (C) 2018 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 #ifndef ANDROID_APEXD_APEX_DATABASE_H_
18 #define ANDROID_APEXD_APEX_DATABASE_H_
19 
20 #include <map>
21 #include <string>
22 #include <unordered_set>
23 
24 #include <android-base/logging.h>
25 
26 namespace android {
27 namespace apex {
28 
29 class MountedApexDatabase {
30  public:
31   // Stores associated low-level data for a mounted APEX. To conserve memory,
32   // the APEX file isn't stored, but must be opened to retrieve specific data.
33   struct MountedApexData {
34     std::string loop_name;  // Loop device used (fs path).
35     std::string full_path;  // Full path to the apex file.
36     std::string mount_point;  // Path this apex is mounted on.
37     std::string device_name;  // Name of the dm verity device.
38     // Name of the loop device backing up hashtree or empty string in case
39     // hashtree is embedded inside an APEX.
40     std::string hashtree_loop_name;
41     // Whenever apex file specified in full_path was deleted.
42     bool deleted;
43 
MountedApexDataMountedApexData44     MountedApexData() {}
MountedApexDataMountedApexData45     MountedApexData(const std::string& loop_name, const std::string& full_path,
46                     const std::string& mount_point,
47                     const std::string& device_name,
48                     const std::string& hashtree_loop_name)
49         : loop_name(loop_name),
50           full_path(full_path),
51           mount_point(mount_point),
52           device_name(device_name),
53           hashtree_loop_name(hashtree_loop_name) {}
54 
55     inline bool operator<(const MountedApexData& rhs) const {
56       int compare_val = loop_name.compare(rhs.loop_name);
57       if (compare_val < 0) {
58         return true;
59       } else if (compare_val > 0) {
60         return false;
61       }
62       compare_val = full_path.compare(rhs.full_path);
63       if (compare_val < 0) {
64         return true;
65       } else if (compare_val > 0) {
66         return false;
67       }
68       compare_val = mount_point.compare(rhs.mount_point);
69       if (compare_val < 0) {
70         return true;
71       } else if (compare_val > 0) {
72         return false;
73       }
74       compare_val = device_name.compare(rhs.device_name);
75       if (compare_val < 0) {
76         return true;
77       } else if (compare_val > 0) {
78         return false;
79       }
80       return hashtree_loop_name < rhs.hashtree_loop_name;
81     }
82   };
83 
CheckAtMostOneLatest()84   inline void CheckAtMostOneLatest() {
85     for (const auto& apex_set : mounted_apexes_) {
86       size_t count = 0;
87       for (const auto& pair : apex_set.second) {
88         if (pair.second) {
89           count++;
90         }
91       }
92       CHECK_LE(count, 1u) << apex_set.first;
93     }
94   }
95 
CheckUniqueLoopDm()96   inline void CheckUniqueLoopDm() {
97     std::unordered_set<std::string> loop_devices;
98     std::unordered_set<std::string> dm_devices;
99     for (const auto& apex_set : mounted_apexes_) {
100       for (const auto& pair : apex_set.second) {
101         if (pair.first.loop_name != "") {
102           CHECK(loop_devices.insert(pair.first.loop_name).second)
103               << "Duplicate loop device: " << pair.first.loop_name;
104         }
105         if (pair.first.device_name != "") {
106           CHECK(dm_devices.insert(pair.first.device_name).second)
107               << "Duplicate dm device: " << pair.first.device_name;
108         }
109         if (pair.first.hashtree_loop_name != "") {
110           CHECK(loop_devices.insert(pair.first.hashtree_loop_name).second)
111               << "Duplicate loop device: " << pair.first.hashtree_loop_name;
112         }
113       }
114     }
115   }
116 
117   template <typename... Args>
AddMountedApex(const std::string & package,bool latest,Args &&...args)118   inline void AddMountedApex(const std::string& package, bool latest,
119                              Args&&... args) {
120     auto it = mounted_apexes_.find(package);
121     if (it == mounted_apexes_.end()) {
122       auto insert_it =
123           mounted_apexes_.emplace(package, std::map<MountedApexData, bool>());
124       CHECK(insert_it.second);
125       it = insert_it.first;
126     }
127 
128     auto check_it = it->second.emplace(
129         MountedApexData(std::forward<Args>(args)...), latest);
130     CHECK(check_it.second);
131 
132     CheckAtMostOneLatest();
133     CheckUniqueLoopDm();
134   }
135 
RemoveMountedApex(const std::string & package,const std::string & full_path)136   inline void RemoveMountedApex(const std::string& package,
137                                 const std::string& full_path) {
138     auto it = mounted_apexes_.find(package);
139     if (it == mounted_apexes_.end()) {
140       return;
141     }
142 
143     auto& pkg_map = it->second;
144 
145     for (auto pkg_it = pkg_map.begin(); pkg_it != pkg_map.end(); ++pkg_it) {
146       if (pkg_it->first.full_path == full_path) {
147         pkg_map.erase(pkg_it);
148         return;
149       }
150     }
151   }
152 
SetLatest(const std::string & package,const std::string & full_path)153   inline void SetLatest(const std::string& package,
154                         const std::string& full_path) {
155     auto it = mounted_apexes_.find(package);
156     CHECK(it != mounted_apexes_.end());
157 
158     auto& pkg_map = it->second;
159 
160     for (auto pkg_it = pkg_map.begin(); pkg_it != pkg_map.end(); ++pkg_it) {
161       if (pkg_it->first.full_path == full_path) {
162         pkg_it->second = true;
163         for (auto reset_it = pkg_map.begin(); reset_it != pkg_map.end();
164              ++reset_it) {
165           if (reset_it != pkg_it) {
166             reset_it->second = false;
167           }
168         }
169         return;
170       }
171     }
172 
173     LOG(FATAL) << "Did not find " << package << " " << full_path;
174   }
175 
UnsetLatestForall(const std::string & package)176   inline void UnsetLatestForall(const std::string& package) {
177     auto it = mounted_apexes_.find(package);
178     if (it == mounted_apexes_.end()) {
179       return;
180     }
181     for (auto& data : it->second) {
182       data.second = false;
183     }
184   }
185 
186   template <typename T>
ForallMountedApexes(const std::string & package,const T & handler)187   inline void ForallMountedApexes(const std::string& package,
188                                   const T& handler) const {
189     auto it = mounted_apexes_.find(package);
190     if (it == mounted_apexes_.end()) {
191       return;
192     }
193     for (auto& pair : it->second) {
194       handler(pair.first, pair.second);
195     }
196   }
197 
198   template <typename T>
ForallMountedApexes(const T & handler)199   inline void ForallMountedApexes(const T& handler) const {
200     for (const auto& pkg : mounted_apexes_) {
201       for (const auto& pair : pkg.second) {
202         handler(pkg.first, pair.first, pair.second);
203       }
204     }
205   }
206 
207   void PopulateFromMounts();
208 
209  private:
210   // A map from package name to mounted apexes.
211   // Note: using std::maps to
212   //         a) so we do not have to worry about iterator invalidation.
213   //         b) do not have to const_cast (over std::set)
214   // TODO(b/158467745): This structure (and functions) need to be guarded by
215   //   locks.
216   std::map<std::string, std::map<MountedApexData, bool>> mounted_apexes_;
217 };
218 
219 }  // namespace apex
220 }  // namespace android
221 
222 #endif  // ANDROID_APEXD_APEX_DATABASE_H_
223