• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <mutex>
22 #include <optional>
23 #include <string>
24 #include <unordered_set>
25 
26 #include <android-base/logging.h>
27 #include <android-base/result.h>
28 #include <android-base/thread_annotations.h>
29 
30 namespace android {
31 namespace apex {
32 
33 class MountedApexDatabase {
34  public:
35   // Stores associated low-level data for a mounted APEX. To conserve memory,
36   // the APEX file isn't stored, but must be opened to retrieve specific data.
37   struct MountedApexData {
38     std::string loop_name;  // Loop device used (fs path).
39     std::string full_path;  // Full path to the apex file.
40     std::string mount_point;  // Path this apex is mounted on.
41     std::string device_name;  // Name of the dm verity device.
42     // Name of the loop device backing up hashtree or empty string in case
43     // hashtree is embedded inside an APEX.
44     std::string hashtree_loop_name;
45     // Whenever apex file specified in full_path was deleted.
46     bool deleted;
47     // Whether the mount is a temp mount or not.
48     bool is_temp_mount;
49 
MountedApexDataMountedApexData50     MountedApexData() {}
51     MountedApexData(const std::string& loop_name, const std::string& full_path,
52                     const std::string& mount_point,
53                     const std::string& device_name,
54                     const std::string& hashtree_loop_name,
55                     bool is_temp_mount = false)
loop_nameMountedApexData56         : loop_name(loop_name),
57           full_path(full_path),
58           mount_point(mount_point),
59           device_name(device_name),
60           hashtree_loop_name(hashtree_loop_name),
61           is_temp_mount(is_temp_mount) {}
62 
63     inline bool operator<(const MountedApexData& rhs) const {
64       int compare_val = loop_name.compare(rhs.loop_name);
65       if (compare_val < 0) {
66         return true;
67       } else if (compare_val > 0) {
68         return false;
69       }
70       compare_val = full_path.compare(rhs.full_path);
71       if (compare_val < 0) {
72         return true;
73       } else if (compare_val > 0) {
74         return false;
75       }
76       compare_val = mount_point.compare(rhs.mount_point);
77       if (compare_val < 0) {
78         return true;
79       } else if (compare_val > 0) {
80         return false;
81       }
82       compare_val = device_name.compare(rhs.device_name);
83       if (compare_val < 0) {
84         return true;
85       } else if (compare_val > 0) {
86         return false;
87       }
88       return hashtree_loop_name < rhs.hashtree_loop_name;
89     }
90   };
91 
92   template <typename... Args>
AddMountedApexLocked(const std::string & package,bool latest,Args &&...args)93   inline void AddMountedApexLocked(const std::string& package, bool latest,
94                                    Args&&... args)
95       REQUIRES(mounted_apexes_mutex_) {
96     auto it = mounted_apexes_.find(package);
97     if (it == mounted_apexes_.end()) {
98       auto insert_it =
99           mounted_apexes_.emplace(package, std::map<MountedApexData, bool>());
100       CHECK(insert_it.second);
101       it = insert_it.first;
102     }
103 
104     auto check_it = it->second.emplace(
105         MountedApexData(std::forward<Args>(args)...), latest);
106     CHECK(check_it.second);
107 
108     CheckAtMostOneLatest();
109     CheckUniqueLoopDm();
110   }
111 
112   template <typename... Args>
AddMountedApex(const std::string & package,bool latest,Args &&...args)113   inline void AddMountedApex(const std::string& package, bool latest,
114                              Args&&... args) REQUIRES(!mounted_apexes_mutex_) {
115     std::lock_guard lock(mounted_apexes_mutex_);
116     AddMountedApexLocked(package, latest, args...);
117   }
118 
119   inline void RemoveMountedApex(const std::string& package,
120                                 const std::string& full_path,
121                                 bool match_temp_mounts = false)
122       REQUIRES(!mounted_apexes_mutex_) {
123     std::lock_guard lock(mounted_apexes_mutex_);
124     auto it = mounted_apexes_.find(package);
125     if (it == mounted_apexes_.end()) {
126       return;
127     }
128 
129     auto& pkg_map = it->second;
130 
131     for (auto pkg_it = pkg_map.begin(); pkg_it != pkg_map.end(); ++pkg_it) {
132       if (pkg_it->first.full_path == full_path &&
133           pkg_it->first.is_temp_mount == match_temp_mounts) {
134         pkg_map.erase(pkg_it);
135         return;
136       }
137     }
138   }
139 
SetLatest(const std::string & package,const std::string & full_path)140   inline void SetLatest(const std::string& package,
141                         const std::string& full_path)
142       REQUIRES(!mounted_apexes_mutex_) {
143     std::lock_guard lock(mounted_apexes_mutex_);
144     SetLatestLocked(package, full_path);
145   }
146 
SetLatestLocked(const std::string & package,const std::string & full_path)147   inline void SetLatestLocked(const std::string& package,
148                               const std::string& full_path)
149       REQUIRES(mounted_apexes_mutex_) {
150     auto it = mounted_apexes_.find(package);
151     CHECK(it != mounted_apexes_.end());
152 
153     auto& pkg_map = it->second;
154 
155     for (auto pkg_it = pkg_map.begin(); pkg_it != pkg_map.end(); ++pkg_it) {
156       if (pkg_it->first.full_path == full_path) {
157         pkg_it->second = true;
158         for (auto reset_it = pkg_map.begin(); reset_it != pkg_map.end();
159              ++reset_it) {
160           if (reset_it != pkg_it) {
161             reset_it->second = false;
162           }
163         }
164         return;
165       }
166     }
167 
168     LOG(FATAL) << "Did not find " << package << " " << full_path;
169   }
170 
171   template <typename T>
172   inline void ForallMountedApexes(const std::string& package, const T& handler,
173                                   bool match_temp_mounts = false) const
174       REQUIRES(!mounted_apexes_mutex_) {
175     std::lock_guard lock(mounted_apexes_mutex_);
176     auto it = mounted_apexes_.find(package);
177     if (it == mounted_apexes_.end()) {
178       return;
179     }
180     for (auto& pair : it->second) {
181       if (pair.first.is_temp_mount == match_temp_mounts) {
182         handler(pair.first, pair.second);
183       }
184     }
185   }
186 
187   template <typename T>
188   inline void ForallMountedApexes(const T& handler,
189                                   bool match_temp_mounts = false) const
190       REQUIRES(!mounted_apexes_mutex_) {
191     std::lock_guard lock(mounted_apexes_mutex_);
192     for (const auto& pkg : mounted_apexes_) {
193       for (const auto& pair : pkg.second) {
194         if (pair.first.is_temp_mount == match_temp_mounts) {
195           handler(pkg.first, pair.first, pair.second);
196         }
197       }
198     }
199   }
200 
GetLatestMountedApex(const std::string & package)201   inline std::optional<MountedApexData> GetLatestMountedApex(
202       const std::string& package) REQUIRES(!mounted_apexes_mutex_) {
203     std::optional<MountedApexData> ret;
204     ForallMountedApexes(package,
205                         [&ret](const MountedApexData& data, bool latest) {
206                           if (latest) {
207                             ret.emplace(data);
208                           }
209                         });
210     return ret;
211   }
212 
213   void PopulateFromMounts(const std::string& active_apex_dir,
214                           const std::string& decompression_dir,
215                           const std::string& apex_hash_tree_dir);
216 
217   // Resets state of the database. Should only be used in testing.
Reset()218   inline void Reset() REQUIRES(!mounted_apexes_mutex_) {
219     std::lock_guard lock(mounted_apexes_mutex_);
220     mounted_apexes_.clear();
221   }
222 
223  private:
224   // A map from package name to mounted apexes.
225   // Note: using std::maps to
226   //         a) so we do not have to worry about iterator invalidation.
227   //         b) do not have to const_cast (over std::set)
228   // TODO(b/158467745): This structure (and functions) need to be guarded by
229   //   locks.
230   std::map<std::string, std::map<MountedApexData, bool>> mounted_apexes_
231       GUARDED_BY(mounted_apexes_mutex_);
232 
233   // To fix thread safety negative capability warning
234   class Mutex : public std::mutex {
235    public:
236     // for negative capabilities
237     const Mutex& operator!() const { return *this; }
238   };
239   mutable Mutex mounted_apexes_mutex_;
240 
CheckAtMostOneLatest()241   inline void CheckAtMostOneLatest() REQUIRES(mounted_apexes_mutex_) {
242     for (const auto& apex_set : mounted_apexes_) {
243       size_t count = 0;
244       for (const auto& pair : apex_set.second) {
245         if (pair.second) {
246           count++;
247         }
248       }
249       CHECK_LE(count, 1u) << apex_set.first;
250     }
251   }
252 
CheckUniqueLoopDm()253   inline void CheckUniqueLoopDm() REQUIRES(mounted_apexes_mutex_) {
254     std::unordered_set<std::string> loop_devices;
255     std::unordered_set<std::string> dm_devices;
256     for (const auto& apex_set : mounted_apexes_) {
257       for (const auto& pair : apex_set.second) {
258         if (pair.first.loop_name != "") {
259           CHECK(loop_devices.insert(pair.first.loop_name).second)
260               << "Duplicate loop device: " << pair.first.loop_name;
261         }
262         if (pair.first.device_name != "") {
263           CHECK(dm_devices.insert(pair.first.device_name).second)
264               << "Duplicate dm device: " << pair.first.device_name;
265         }
266         if (pair.first.hashtree_loop_name != "") {
267           CHECK(loop_devices.insert(pair.first.hashtree_loop_name).second)
268               << "Duplicate loop device: " << pair.first.hashtree_loop_name;
269         }
270       }
271     }
272   }
273 };
274 
275 }  // namespace apex
276 }  // namespace android
277 
278 #endif  // ANDROID_APEXD_APEX_DATABASE_H_
279