1*33f37583SAndroid Build Coastguard Worker /*
2*33f37583SAndroid Build Coastguard Worker * Copyright (C) 2019 The Android Open Source Project
3*33f37583SAndroid Build Coastguard Worker *
4*33f37583SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*33f37583SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*33f37583SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*33f37583SAndroid Build Coastguard Worker *
8*33f37583SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*33f37583SAndroid Build Coastguard Worker *
10*33f37583SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*33f37583SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*33f37583SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*33f37583SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*33f37583SAndroid Build Coastguard Worker * limitations under the License.
15*33f37583SAndroid Build Coastguard Worker */
16*33f37583SAndroid Build Coastguard Worker
17*33f37583SAndroid Build Coastguard Worker #include "apex_database.h"
18*33f37583SAndroid Build Coastguard Worker #include "apex_constants.h"
19*33f37583SAndroid Build Coastguard Worker #include "apex_file.h"
20*33f37583SAndroid Build Coastguard Worker #include "apexd_utils.h"
21*33f37583SAndroid Build Coastguard Worker #include "string_log.h"
22*33f37583SAndroid Build Coastguard Worker
23*33f37583SAndroid Build Coastguard Worker #include <android-base/file.h>
24*33f37583SAndroid Build Coastguard Worker #include <android-base/logging.h>
25*33f37583SAndroid Build Coastguard Worker #include <android-base/parseint.h>
26*33f37583SAndroid Build Coastguard Worker #include <android-base/result.h>
27*33f37583SAndroid Build Coastguard Worker #include <android-base/strings.h>
28*33f37583SAndroid Build Coastguard Worker
29*33f37583SAndroid Build Coastguard Worker #include <filesystem>
30*33f37583SAndroid Build Coastguard Worker #include <fstream>
31*33f37583SAndroid Build Coastguard Worker #include <string>
32*33f37583SAndroid Build Coastguard Worker #include <unordered_map>
33*33f37583SAndroid Build Coastguard Worker #include <utility>
34*33f37583SAndroid Build Coastguard Worker
35*33f37583SAndroid Build Coastguard Worker using android::base::ConsumeSuffix;
36*33f37583SAndroid Build Coastguard Worker using android::base::EndsWith;
37*33f37583SAndroid Build Coastguard Worker using android::base::ErrnoError;
38*33f37583SAndroid Build Coastguard Worker using android::base::Error;
39*33f37583SAndroid Build Coastguard Worker using android::base::ParseInt;
40*33f37583SAndroid Build Coastguard Worker using android::base::ReadFileToString;
41*33f37583SAndroid Build Coastguard Worker using android::base::Result;
42*33f37583SAndroid Build Coastguard Worker using android::base::Split;
43*33f37583SAndroid Build Coastguard Worker using android::base::StartsWith;
44*33f37583SAndroid Build Coastguard Worker using android::base::Trim;
45*33f37583SAndroid Build Coastguard Worker
46*33f37583SAndroid Build Coastguard Worker namespace fs = std::filesystem;
47*33f37583SAndroid Build Coastguard Worker
48*33f37583SAndroid Build Coastguard Worker namespace android {
49*33f37583SAndroid Build Coastguard Worker namespace apex {
50*33f37583SAndroid Build Coastguard Worker
51*33f37583SAndroid Build Coastguard Worker namespace {
52*33f37583SAndroid Build Coastguard Worker
53*33f37583SAndroid Build Coastguard Worker using MountedApexData = MountedApexDatabase::MountedApexData;
54*33f37583SAndroid Build Coastguard Worker
55*33f37583SAndroid Build Coastguard Worker enum BlockDeviceType {
56*33f37583SAndroid Build Coastguard Worker UnknownDevice,
57*33f37583SAndroid Build Coastguard Worker LoopDevice,
58*33f37583SAndroid Build Coastguard Worker DeviceMapperDevice,
59*33f37583SAndroid Build Coastguard Worker };
60*33f37583SAndroid Build Coastguard Worker
61*33f37583SAndroid Build Coastguard Worker const fs::path kDevBlock = "/dev/block";
62*33f37583SAndroid Build Coastguard Worker const fs::path kSysBlock = "/sys/block";
63*33f37583SAndroid Build Coastguard Worker
64*33f37583SAndroid Build Coastguard Worker class BlockDevice {
65*33f37583SAndroid Build Coastguard Worker std::string name; // loopN, dm-N, ...
66*33f37583SAndroid Build Coastguard Worker public:
BlockDevice(const fs::path & path)67*33f37583SAndroid Build Coastguard Worker explicit BlockDevice(const fs::path& path) { name = path.filename(); }
68*33f37583SAndroid Build Coastguard Worker
GetType() const69*33f37583SAndroid Build Coastguard Worker BlockDeviceType GetType() const {
70*33f37583SAndroid Build Coastguard Worker if (StartsWith(name, "loop")) return LoopDevice;
71*33f37583SAndroid Build Coastguard Worker if (StartsWith(name, "dm-")) return DeviceMapperDevice;
72*33f37583SAndroid Build Coastguard Worker return UnknownDevice;
73*33f37583SAndroid Build Coastguard Worker }
74*33f37583SAndroid Build Coastguard Worker
SysPath() const75*33f37583SAndroid Build Coastguard Worker fs::path SysPath() const { return kSysBlock / name; }
76*33f37583SAndroid Build Coastguard Worker
DevPath() const77*33f37583SAndroid Build Coastguard Worker fs::path DevPath() const { return kDevBlock / name; }
78*33f37583SAndroid Build Coastguard Worker
GetProperty(const std::string & property) const79*33f37583SAndroid Build Coastguard Worker Result<std::string> GetProperty(const std::string& property) const {
80*33f37583SAndroid Build Coastguard Worker auto property_file = SysPath() / property;
81*33f37583SAndroid Build Coastguard Worker std::string property_value;
82*33f37583SAndroid Build Coastguard Worker if (!ReadFileToString(property_file, &property_value)) {
83*33f37583SAndroid Build Coastguard Worker return ErrnoError() << "Fail to read";
84*33f37583SAndroid Build Coastguard Worker }
85*33f37583SAndroid Build Coastguard Worker return Trim(property_value);
86*33f37583SAndroid Build Coastguard Worker }
87*33f37583SAndroid Build Coastguard Worker
GetSlaves() const88*33f37583SAndroid Build Coastguard Worker std::vector<BlockDevice> GetSlaves() const {
89*33f37583SAndroid Build Coastguard Worker std::vector<BlockDevice> slaves;
90*33f37583SAndroid Build Coastguard Worker std::error_code ec;
91*33f37583SAndroid Build Coastguard Worker auto status = WalkDir(SysPath() / "slaves", [&](const auto& entry) {
92*33f37583SAndroid Build Coastguard Worker BlockDevice dev(entry);
93*33f37583SAndroid Build Coastguard Worker if (fs::is_block_file(dev.DevPath(), ec)) {
94*33f37583SAndroid Build Coastguard Worker slaves.push_back(dev);
95*33f37583SAndroid Build Coastguard Worker }
96*33f37583SAndroid Build Coastguard Worker });
97*33f37583SAndroid Build Coastguard Worker if (!status.ok()) {
98*33f37583SAndroid Build Coastguard Worker LOG(WARNING) << status.error();
99*33f37583SAndroid Build Coastguard Worker }
100*33f37583SAndroid Build Coastguard Worker return slaves;
101*33f37583SAndroid Build Coastguard Worker }
102*33f37583SAndroid Build Coastguard Worker };
103*33f37583SAndroid Build Coastguard Worker
ParseMountInfo(const std::string & mount_info)104*33f37583SAndroid Build Coastguard Worker std::pair<fs::path, fs::path> ParseMountInfo(const std::string& mount_info) {
105*33f37583SAndroid Build Coastguard Worker const auto& tokens = Split(mount_info, " ");
106*33f37583SAndroid Build Coastguard Worker if (tokens.size() < 2) {
107*33f37583SAndroid Build Coastguard Worker return std::make_pair("", "");
108*33f37583SAndroid Build Coastguard Worker }
109*33f37583SAndroid Build Coastguard Worker return std::make_pair(tokens[0], tokens[1]);
110*33f37583SAndroid Build Coastguard Worker }
111*33f37583SAndroid Build Coastguard Worker
ParseMountPoint(const std::string & mount_point)112*33f37583SAndroid Build Coastguard Worker std::pair<std::string, int> ParseMountPoint(const std::string& mount_point) {
113*33f37583SAndroid Build Coastguard Worker auto package_id = fs::path(mount_point).filename();
114*33f37583SAndroid Build Coastguard Worker auto split = Split(package_id, "@");
115*33f37583SAndroid Build Coastguard Worker if (split.size() == 2) {
116*33f37583SAndroid Build Coastguard Worker int version;
117*33f37583SAndroid Build Coastguard Worker if (!ParseInt(split[1], &version)) {
118*33f37583SAndroid Build Coastguard Worker version = -1;
119*33f37583SAndroid Build Coastguard Worker }
120*33f37583SAndroid Build Coastguard Worker return std::make_pair(split[0], version);
121*33f37583SAndroid Build Coastguard Worker }
122*33f37583SAndroid Build Coastguard Worker return std::make_pair(package_id, -1);
123*33f37583SAndroid Build Coastguard Worker }
124*33f37583SAndroid Build Coastguard Worker
IsActiveMountPoint(const std::string & mount_point)125*33f37583SAndroid Build Coastguard Worker bool IsActiveMountPoint(const std::string& mount_point) {
126*33f37583SAndroid Build Coastguard Worker return (mount_point.find('@') == std::string::npos);
127*33f37583SAndroid Build Coastguard Worker }
128*33f37583SAndroid Build Coastguard Worker
IsTempMountPoint(const std::string & mount_point)129*33f37583SAndroid Build Coastguard Worker bool IsTempMountPoint(const std::string& mount_point) {
130*33f37583SAndroid Build Coastguard Worker return EndsWith(mount_point, ".tmp");
131*33f37583SAndroid Build Coastguard Worker }
132*33f37583SAndroid Build Coastguard Worker
PopulateLoopInfo(const BlockDevice & top_device,const std::vector<std::string> & data_dirs,MountedApexData * apex_data)133*33f37583SAndroid Build Coastguard Worker Result<void> PopulateLoopInfo(const BlockDevice& top_device,
134*33f37583SAndroid Build Coastguard Worker const std::vector<std::string>& data_dirs,
135*33f37583SAndroid Build Coastguard Worker MountedApexData* apex_data) {
136*33f37583SAndroid Build Coastguard Worker std::vector<BlockDevice> slaves = top_device.GetSlaves();
137*33f37583SAndroid Build Coastguard Worker if (slaves.size() != 1) {
138*33f37583SAndroid Build Coastguard Worker return Error() << "dm device " << top_device.DevPath()
139*33f37583SAndroid Build Coastguard Worker << " has unexpected number of slaves (should be 1) : "
140*33f37583SAndroid Build Coastguard Worker << slaves.size();
141*33f37583SAndroid Build Coastguard Worker }
142*33f37583SAndroid Build Coastguard Worker if (slaves[0].GetType() != LoopDevice) {
143*33f37583SAndroid Build Coastguard Worker return Error() << slaves[0].DevPath() << " is not a loop device";
144*33f37583SAndroid Build Coastguard Worker }
145*33f37583SAndroid Build Coastguard Worker std::string backing_file =
146*33f37583SAndroid Build Coastguard Worker OR_RETURN(slaves[0].GetProperty("loop/backing_file"));
147*33f37583SAndroid Build Coastguard Worker bool is_data_loop_device = std::any_of(
148*33f37583SAndroid Build Coastguard Worker data_dirs.begin(), data_dirs.end(),
149*33f37583SAndroid Build Coastguard Worker [&](const std::string& dir) { return StartsWith(backing_file, dir); });
150*33f37583SAndroid Build Coastguard Worker if (!is_data_loop_device) {
151*33f37583SAndroid Build Coastguard Worker return Error() << "Data loop device " << slaves[0].DevPath()
152*33f37583SAndroid Build Coastguard Worker << " has unexpected backing file " << backing_file;
153*33f37583SAndroid Build Coastguard Worker }
154*33f37583SAndroid Build Coastguard Worker apex_data->loop_name = slaves[0].DevPath();
155*33f37583SAndroid Build Coastguard Worker apex_data->full_path = backing_file;
156*33f37583SAndroid Build Coastguard Worker return {};
157*33f37583SAndroid Build Coastguard Worker }
158*33f37583SAndroid Build Coastguard Worker
159*33f37583SAndroid Build Coastguard Worker // This is not the right place to do this normalization, but proper solution
160*33f37583SAndroid Build Coastguard Worker // will require some refactoring first. :(
161*33f37583SAndroid Build Coastguard Worker // TODO(b/158469911): introduce MountedApexDataBuilder and delegate all
162*33f37583SAndroid Build Coastguard Worker // building/normalization logic to it.
NormalizeIfDeleted(MountedApexData * apex_data)163*33f37583SAndroid Build Coastguard Worker void NormalizeIfDeleted(MountedApexData* apex_data) {
164*33f37583SAndroid Build Coastguard Worker std::string_view full_path = apex_data->full_path;
165*33f37583SAndroid Build Coastguard Worker if (ConsumeSuffix(&full_path, "(deleted)")) {
166*33f37583SAndroid Build Coastguard Worker apex_data->deleted = true;
167*33f37583SAndroid Build Coastguard Worker auto it = full_path.rbegin();
168*33f37583SAndroid Build Coastguard Worker while (it != full_path.rend() && isspace(*it)) {
169*33f37583SAndroid Build Coastguard Worker it++;
170*33f37583SAndroid Build Coastguard Worker }
171*33f37583SAndroid Build Coastguard Worker full_path.remove_suffix(it - full_path.rbegin());
172*33f37583SAndroid Build Coastguard Worker } else {
173*33f37583SAndroid Build Coastguard Worker apex_data->deleted = false;
174*33f37583SAndroid Build Coastguard Worker }
175*33f37583SAndroid Build Coastguard Worker apex_data->full_path = full_path;
176*33f37583SAndroid Build Coastguard Worker }
177*33f37583SAndroid Build Coastguard Worker
ResolveMountInfo(const BlockDevice & block,const std::string & mount_point,const std::vector<std::string> & data_dirs)178*33f37583SAndroid Build Coastguard Worker Result<MountedApexData> ResolveMountInfo(
179*33f37583SAndroid Build Coastguard Worker const BlockDevice& block, const std::string& mount_point,
180*33f37583SAndroid Build Coastguard Worker const std::vector<std::string>& data_dirs) {
181*33f37583SAndroid Build Coastguard Worker // Now, see if it is dm-verity or loop mounted
182*33f37583SAndroid Build Coastguard Worker switch (block.GetType()) {
183*33f37583SAndroid Build Coastguard Worker case LoopDevice: {
184*33f37583SAndroid Build Coastguard Worker auto backing_file = block.GetProperty("loop/backing_file");
185*33f37583SAndroid Build Coastguard Worker if (!backing_file.ok()) {
186*33f37583SAndroid Build Coastguard Worker return backing_file.error();
187*33f37583SAndroid Build Coastguard Worker }
188*33f37583SAndroid Build Coastguard Worker MountedApexData result;
189*33f37583SAndroid Build Coastguard Worker result.loop_name = block.DevPath();
190*33f37583SAndroid Build Coastguard Worker result.full_path = *backing_file;
191*33f37583SAndroid Build Coastguard Worker result.mount_point = mount_point;
192*33f37583SAndroid Build Coastguard Worker NormalizeIfDeleted(&result);
193*33f37583SAndroid Build Coastguard Worker return result;
194*33f37583SAndroid Build Coastguard Worker }
195*33f37583SAndroid Build Coastguard Worker case DeviceMapperDevice: {
196*33f37583SAndroid Build Coastguard Worker auto name = block.GetProperty("dm/name");
197*33f37583SAndroid Build Coastguard Worker if (!name.ok()) {
198*33f37583SAndroid Build Coastguard Worker return name.error();
199*33f37583SAndroid Build Coastguard Worker }
200*33f37583SAndroid Build Coastguard Worker MountedApexData result;
201*33f37583SAndroid Build Coastguard Worker result.mount_point = mount_point;
202*33f37583SAndroid Build Coastguard Worker result.device_name = *name;
203*33f37583SAndroid Build Coastguard Worker auto status = PopulateLoopInfo(block, data_dirs, &result);
204*33f37583SAndroid Build Coastguard Worker if (!status.ok()) {
205*33f37583SAndroid Build Coastguard Worker return status.error();
206*33f37583SAndroid Build Coastguard Worker }
207*33f37583SAndroid Build Coastguard Worker NormalizeIfDeleted(&result);
208*33f37583SAndroid Build Coastguard Worker return result;
209*33f37583SAndroid Build Coastguard Worker }
210*33f37583SAndroid Build Coastguard Worker case UnknownDevice: {
211*33f37583SAndroid Build Coastguard Worker return Errorf("Can't resolve {}", block.DevPath().string());
212*33f37583SAndroid Build Coastguard Worker }
213*33f37583SAndroid Build Coastguard Worker }
214*33f37583SAndroid Build Coastguard Worker }
215*33f37583SAndroid Build Coastguard Worker
216*33f37583SAndroid Build Coastguard Worker } // namespace
217*33f37583SAndroid Build Coastguard Worker
218*33f37583SAndroid Build Coastguard Worker // On startup, APEX database is populated from /proc/mounts.
219*33f37583SAndroid Build Coastguard Worker
220*33f37583SAndroid Build Coastguard Worker // /apex/<package-id> can be mounted from
221*33f37583SAndroid Build Coastguard Worker // - /dev/block/loopX : loop device
222*33f37583SAndroid Build Coastguard Worker // - /dev/block/dm-X : dm-verity
223*33f37583SAndroid Build Coastguard Worker
224*33f37583SAndroid Build Coastguard Worker // In case of loop device, the original APEX file can be tracked
225*33f37583SAndroid Build Coastguard Worker // by /sys/block/loopX/loop/backing_file.
226*33f37583SAndroid Build Coastguard Worker
227*33f37583SAndroid Build Coastguard Worker // In case of dm-verity, it is mapped to a loop device.
228*33f37583SAndroid Build Coastguard Worker // This mapped loop device can be traced by
229*33f37583SAndroid Build Coastguard Worker // /sys/block/dm-X/slaves/ directory which contains
230*33f37583SAndroid Build Coastguard Worker // a symlink to /sys/block/loopY, which leads to
231*33f37583SAndroid Build Coastguard Worker // the original APEX file.
232*33f37583SAndroid Build Coastguard Worker // Device name can be retrieved from
233*33f37583SAndroid Build Coastguard Worker // /sys/block/dm-Y/dm/name.
234*33f37583SAndroid Build Coastguard Worker
235*33f37583SAndroid Build Coastguard Worker // Need to read /proc/mounts on startup since apexd can start
236*33f37583SAndroid Build Coastguard Worker // at any time (It's a lazy service).
PopulateFromMounts(const std::vector<std::string> & data_dirs)237*33f37583SAndroid Build Coastguard Worker void MountedApexDatabase::PopulateFromMounts(
238*33f37583SAndroid Build Coastguard Worker const std::vector<std::string>& data_dirs)
239*33f37583SAndroid Build Coastguard Worker REQUIRES(!mounted_apexes_mutex_) {
240*33f37583SAndroid Build Coastguard Worker LOG(INFO) << "Populating APEX database from mounts...";
241*33f37583SAndroid Build Coastguard Worker
242*33f37583SAndroid Build Coastguard Worker std::ifstream mounts("/proc/mounts");
243*33f37583SAndroid Build Coastguard Worker std::string line;
244*33f37583SAndroid Build Coastguard Worker std::lock_guard lock(mounted_apexes_mutex_);
245*33f37583SAndroid Build Coastguard Worker while (std::getline(mounts, line)) {
246*33f37583SAndroid Build Coastguard Worker auto [block, mount_point] = ParseMountInfo(line);
247*33f37583SAndroid Build Coastguard Worker // TODO(b/158469914): distinguish between temp and non-temp mounts
248*33f37583SAndroid Build Coastguard Worker if (fs::path(mount_point).parent_path() != kApexRoot) {
249*33f37583SAndroid Build Coastguard Worker continue;
250*33f37583SAndroid Build Coastguard Worker }
251*33f37583SAndroid Build Coastguard Worker if (IsActiveMountPoint(mount_point)) {
252*33f37583SAndroid Build Coastguard Worker continue;
253*33f37583SAndroid Build Coastguard Worker }
254*33f37583SAndroid Build Coastguard Worker if (IsTempMountPoint(mount_point)) {
255*33f37583SAndroid Build Coastguard Worker continue;
256*33f37583SAndroid Build Coastguard Worker }
257*33f37583SAndroid Build Coastguard Worker auto mount_data =
258*33f37583SAndroid Build Coastguard Worker ResolveMountInfo(BlockDevice(block), mount_point, data_dirs);
259*33f37583SAndroid Build Coastguard Worker if (!mount_data.ok()) {
260*33f37583SAndroid Build Coastguard Worker LOG(WARNING) << "Can't resolve mount info " << mount_data.error();
261*33f37583SAndroid Build Coastguard Worker continue;
262*33f37583SAndroid Build Coastguard Worker }
263*33f37583SAndroid Build Coastguard Worker
264*33f37583SAndroid Build Coastguard Worker auto [package, version] = ParseMountPoint(mount_point);
265*33f37583SAndroid Build Coastguard Worker mount_data->version = version;
266*33f37583SAndroid Build Coastguard Worker AddMountedApexLocked(package, *mount_data);
267*33f37583SAndroid Build Coastguard Worker
268*33f37583SAndroid Build Coastguard Worker LOG(INFO) << "Found " << mount_point << " backed by"
269*33f37583SAndroid Build Coastguard Worker << (mount_data->deleted ? " deleted " : " ") << "file "
270*33f37583SAndroid Build Coastguard Worker << mount_data->full_path;
271*33f37583SAndroid Build Coastguard Worker }
272*33f37583SAndroid Build Coastguard Worker
273*33f37583SAndroid Build Coastguard Worker LOG(INFO) << mounted_apexes_.size() << " packages restored.";
274*33f37583SAndroid Build Coastguard Worker }
275*33f37583SAndroid Build Coastguard Worker
276*33f37583SAndroid Build Coastguard Worker } // namespace apex
277*33f37583SAndroid Build Coastguard Worker } // namespace android
278