summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apexd/apex_database.cpp65
-rw-r--r--apexd/apex_database.h21
-rw-r--r--apexd/apex_database_test.cpp137
-rw-r--r--apexd/apexd.cpp8
-rw-r--r--apexd/apexservice_test.cpp115
5 files changed, 272 insertions, 74 deletions
diff --git a/apexd/apex_database.cpp b/apexd/apex_database.cpp
index 096c56f..99fdbe8 100644
--- a/apexd/apex_database.cpp
+++ b/apexd/apex_database.cpp
@@ -125,6 +125,50 @@ bool isActiveMountPoint(const std::string& mountPoint) {
return (mountPoint.find('@') == std::string::npos);
}
+Result<void> PopulateLoopInfo(const BlockDevice& top_device,
+ MountedApexData* apex_data) {
+ std::vector<BlockDevice> slaves = top_device.GetSlaves();
+ if (slaves.size() != 1 && slaves.size() != 2) {
+ return Error() << "dm device " << top_device.DevPath()
+ << " has unexpected number of slaves : " << slaves.size();
+ }
+ std::vector<std::string> backing_files;
+ backing_files.reserve(slaves.size());
+ for (const auto& dev : slaves) {
+ if (dev.GetType() != LoopDevice) {
+ return Error() << dev.DevPath() << " is not a loop device";
+ }
+ auto backing_file = dev.GetProperty("loop/backing_file");
+ if (!backing_file) {
+ return backing_file.error();
+ }
+ backing_files.push_back(std::move(*backing_file));
+ }
+ // Enforce following invariant:
+ // * slaves[0] always represents a data loop device
+ // * if size = 2 then slaves[1] represents an external hashtree loop device
+ if (slaves.size() == 2) {
+ if (!StartsWith(backing_files[0], kActiveApexPackagesDataDir)) {
+ std::swap(slaves[0], slaves[1]);
+ std::swap(backing_files[0], backing_files[1]);
+ }
+ }
+ if (!StartsWith(backing_files[0], kActiveApexPackagesDataDir)) {
+ return Error() << "Data loop device " << slaves[0].DevPath()
+ << " has unexpected backing file " << backing_files[0];
+ }
+ if (slaves.size() == 2) {
+ if (!StartsWith(backing_files[1], kApexHashTreeDir)) {
+ return Error() << "Hashtree loop device " << slaves[1].DevPath()
+ << " has unexpected backing file " << backing_files[1];
+ }
+ apex_data->hashtree_loop_name = slaves[1].DevPath();
+ }
+ apex_data->loop_name = slaves[0].DevPath();
+ apex_data->full_path = backing_files[0];
+ return {};
+}
+
Result<MountedApexData> resolveMountInfo(const BlockDevice& block,
const std::string& mountPoint) {
// Now, see if it is dm-verity or loop mounted
@@ -134,25 +178,22 @@ Result<MountedApexData> resolveMountInfo(const BlockDevice& block,
if (!backingFile) {
return backingFile.error();
}
- return MountedApexData(block.DevPath(), *backingFile, mountPoint, "");
+ return MountedApexData(block.DevPath(), *backingFile, mountPoint,
+ /* device_name= */ "",
+ /* hashtree_loop_name= */ "");
}
case DeviceMapperDevice: {
auto name = block.GetProperty("dm/name");
if (!name) {
return name.error();
}
- auto slaves = block.GetSlaves();
- if (slaves.empty() || slaves[0].GetType() != LoopDevice) {
- return Errorf("DeviceMapper device with no loop devices");
- }
- // TODO(jooyung): handle multiple loop devices when hash tree is
- // externalized
- auto slave = slaves[0];
- auto backingFile = slave.GetProperty("loop/backing_file");
- if (!backingFile) {
- return backingFile.error();
+ MountedApexData result;
+ result.mount_point = mountPoint;
+ result.device_name = *name;
+ if (auto status = PopulateLoopInfo(block, &result); !status) {
+ return status.error();
}
- return MountedApexData(slave.DevPath(), *backingFile, mountPoint, *name);
+ return result;
}
case UnknownDevice: {
return Errorf("Can't resolve {}", block.DevPath().string());
diff --git a/apexd/apex_database.h b/apexd/apex_database.h
index e9e1c32..5b738b7 100644
--- a/apexd/apex_database.h
+++ b/apexd/apex_database.h
@@ -35,15 +35,20 @@ class MountedApexDatabase {
std::string full_path; // Full path to the apex file.
std::string mount_point; // Path this apex is mounted on.
std::string device_name; // Name of the dm verity device.
+ // Name of the loop device backing up hashtree or empty string in case
+ // hashtree is embedded inside an APEX.
+ std::string hashtree_loop_name;
MountedApexData() {}
MountedApexData(const std::string& loop_name, const std::string& full_path,
const std::string& mount_point,
- const std::string& device_name)
+ const std::string& device_name,
+ const std::string& hashtree_loop_name)
: loop_name(loop_name),
full_path(full_path),
mount_point(mount_point),
- device_name(device_name) {}
+ device_name(device_name),
+ hashtree_loop_name(hashtree_loop_name) {}
inline bool operator<(const MountedApexData& rhs) const {
int compare_val = loop_name.compare(rhs.loop_name);
@@ -64,7 +69,13 @@ class MountedApexDatabase {
} else if (compare_val > 0) {
return false;
}
- return device_name < rhs.device_name;
+ compare_val = device_name.compare(rhs.device_name);
+ if (compare_val < 0) {
+ return true;
+ } else if (compare_val > 0) {
+ return false;
+ }
+ return hashtree_loop_name < rhs.hashtree_loop_name;
}
};
@@ -93,6 +104,10 @@ class MountedApexDatabase {
CHECK(dm_devices.insert(pair.first.device_name).second)
<< "Duplicate dm device: " << pair.first.device_name;
}
+ if (pair.first.hashtree_loop_name != "") {
+ CHECK(loop_devices.insert(pair.first.hashtree_loop_name).second)
+ << "Duplicate loop device: " << pair.first.hashtree_loop_name;
+ }
}
}
}
diff --git a/apexd/apex_database_test.cpp b/apexd/apex_database_test.cpp
index ccfdb10..fce3c20 100644
--- a/apexd/apex_database_test.cpp
+++ b/apexd/apex_database_test.cpp
@@ -33,6 +33,8 @@ TEST(MountedApexDataTest, LinearOrder) {
constexpr const char* kPath[] = {"path1", "path2", "path3"};
constexpr const char* kMount[] = {"mount1", "mount2", "mount3"};
constexpr const char* kDm[] = {"dm1", "dm2", "dm3"};
+ constexpr const char* kHashtreeLoopName[] = {"hash-loop1", "hash-loop2",
+ "hash-loop3"};
constexpr size_t kCount = arraysize(kLoopName) * arraysize(kPath) *
arraysize(kMount) * arraysize(kDm);
@@ -43,26 +45,32 @@ TEST(MountedApexDataTest, LinearOrder) {
const size_t path_rest = loop_rest / arraysize(kPath);
const size_t mount_index = path_rest % arraysize(kMount);
const size_t mount_rest = path_rest / arraysize(kMount);
- ;
const size_t dm_index = mount_rest % arraysize(kDm);
- CHECK_EQ(mount_rest / arraysize(kDm), 0u);
- return std::make_tuple(loop_index, path_index, mount_index, dm_index);
+ const size_t dm_rest = mount_rest / arraysize(kHashtreeLoopName);
+ const size_t hashtree_loop_index = dm_rest % arraysize(kHashtreeLoopName);
+ CHECK_EQ(dm_rest / arraysize(kHashtreeLoopName), 0u);
+ return std::make_tuple(loop_index, path_index, mount_index, dm_index,
+ hashtree_loop_index);
};
MountedApexData data[kCount];
for (size_t i = 0; i < kCount; ++i) {
- size_t loop_idx, path_idx, mount_idx, dm_idx;
- std::tie(loop_idx, path_idx, mount_idx, dm_idx) = index_fn(i);
- data[i] = MountedApexData(kLoopName[loop_idx], kPath[path_idx],
- kMount[mount_idx], kDm[dm_idx]);
+ size_t loop_idx, path_idx, mount_idx, dm_idx, hash_loop_idx;
+ std::tie(loop_idx, path_idx, mount_idx, dm_idx, hash_loop_idx) =
+ index_fn(i);
+ data[i] =
+ MountedApexData(kLoopName[loop_idx], kPath[path_idx], kMount[mount_idx],
+ kDm[dm_idx], kHashtreeLoopName[hash_loop_idx]);
}
for (size_t i = 0; i < kCount; ++i) {
- size_t loop_idx_i, path_idx_i, mount_idx_i, dm_idx_i;
- std::tie(loop_idx_i, path_idx_i, mount_idx_i, dm_idx_i) = index_fn(i);
+ size_t loop_idx_i, path_idx_i, mount_idx_i, dm_idx_i, hash_loop_idx_i;
+ std::tie(loop_idx_i, path_idx_i, mount_idx_i, dm_idx_i, hash_loop_idx_i) =
+ index_fn(i);
for (size_t j = i; j < kCount; ++j) {
- size_t loop_idx_j, path_idx_j, mount_idx_j, dm_idx_j;
- std::tie(loop_idx_j, path_idx_j, mount_idx_j, dm_idx_j) = index_fn(j);
+ size_t loop_idx_j, path_idx_j, mount_idx_j, dm_idx_j, hash_loop_idx_j;
+ std::tie(loop_idx_j, path_idx_j, mount_idx_j, dm_idx_j, hash_loop_idx_j) =
+ index_fn(j);
if (loop_idx_i != loop_idx_j) {
EXPECT_EQ(loop_idx_i < loop_idx_j, data[i] < data[j]);
continue;
@@ -75,7 +83,11 @@ TEST(MountedApexDataTest, LinearOrder) {
EXPECT_EQ(mount_idx_i < mount_idx_j, data[i] < data[j]);
continue;
}
- EXPECT_EQ(dm_idx_i < dm_idx_j, data[i] < data[j]);
+ if (dm_idx_i != dm_idx_j) {
+ EXPECT_EQ(dm_idx_i < dm_idx_j, data[i] < data[j]);
+ continue;
+ }
+ EXPECT_EQ(hash_loop_idx_i < hash_loop_idx_j, data[i] < data[j]);
}
}
}
@@ -90,12 +102,14 @@ size_t CountPackages(const MountedApexDatabase& db) {
bool Contains(const MountedApexDatabase& db, const std::string& package,
const std::string& loop_name, const std::string& full_path,
- const std::string& mount_point, const std::string& device_name) {
+ const std::string& mount_point, const std::string& device_name,
+ const std::string& hashtree_loop_name) {
bool found = false;
db.ForallMountedApexes([&](const std::string& p, const MountedApexData& d,
bool b ATTRIBUTE_UNUSED) {
if (package == p && loop_name == d.loop_name && full_path == d.full_path &&
- mount_point == d.mount_point && device_name == d.device_name) {
+ mount_point == d.mount_point && device_name == d.device_name &&
+ hashtree_loop_name == d.hashtree_loop_name) {
found = true;
}
});
@@ -104,12 +118,13 @@ bool Contains(const MountedApexDatabase& db, const std::string& package,
bool ContainsPackage(const MountedApexDatabase& db, const std::string& package,
const std::string& loop_name, const std::string& full_path,
- const std::string& dm) {
+ const std::string& dm,
+ const std::string& hashtree_loop_name) {
bool found = false;
db.ForallMountedApexes(
package, [&](const MountedApexData& d, bool b ATTRIBUTE_UNUSED) {
if (loop_name == d.loop_name && full_path == d.full_path &&
- dm == d.device_name) {
+ dm == d.device_name && hashtree_loop_name == d.hashtree_loop_name) {
found = true;
}
});
@@ -122,20 +137,23 @@ TEST(ApexDatabaseTest, AddRemovedMountedApex) {
constexpr const char* kPath = "path";
constexpr const char* kMountPoint = "mount";
constexpr const char* kDeviceName = "dev";
+ constexpr const char* kHashtreeLoopName = "hash-loop";
MountedApexDatabase db;
ASSERT_EQ(CountPackages(db), 0u);
- db.AddMountedApex(kPackage, false, kLoopName, kPath, kMountPoint,
- kDeviceName);
- ASSERT_TRUE(
- Contains(db, kPackage, kLoopName, kPath, kMountPoint, kDeviceName));
- ASSERT_TRUE(ContainsPackage(db, kPackage, kLoopName, kPath, kDeviceName));
+ db.AddMountedApex(kPackage, false, kLoopName, kPath, kMountPoint, kDeviceName,
+ kHashtreeLoopName);
+ ASSERT_TRUE(Contains(db, kPackage, kLoopName, kPath, kMountPoint, kDeviceName,
+ kHashtreeLoopName));
+ ASSERT_TRUE(ContainsPackage(db, kPackage, kLoopName, kPath, kDeviceName,
+ kHashtreeLoopName));
db.RemoveMountedApex(kPackage, kPath);
- EXPECT_FALSE(
- Contains(db, kPackage, kLoopName, kPath, kMountPoint, kDeviceName));
- EXPECT_FALSE(ContainsPackage(db, kPackage, kLoopName, kPath, kDeviceName));
+ EXPECT_FALSE(Contains(db, kPackage, kLoopName, kPath, kMountPoint,
+ kDeviceName, kHashtreeLoopName));
+ EXPECT_FALSE(ContainsPackage(db, kPackage, kLoopName, kPath, kDeviceName,
+ kHashtreeLoopName));
}
TEST(ApexDatabaseTest, MountMultiple) {
@@ -145,40 +163,41 @@ TEST(ApexDatabaseTest, MountMultiple) {
constexpr const char* kPath[] = {"path", "path2", "path", "path4"};
constexpr const char* kMountPoint[] = {"mount", "mount2", "mount", "mount4"};
constexpr const char* kDeviceName[] = {"dev", "dev2", "dev3", "dev4"};
-
+ constexpr const char* kHashtreeLoopName[] = {"hash-loop", "hash-loop2",
+ "hash-loop3", "hash-loop4"};
MountedApexDatabase db;
ASSERT_EQ(CountPackages(db), 0u);
for (size_t i = 0; i < arraysize(kPackage); ++i) {
db.AddMountedApex(kPackage[i], false, kLoopName[i], kPath[i],
- kMountPoint[i], kDeviceName[i]);
+ kMountPoint[i], kDeviceName[i], kHashtreeLoopName[i]);
}
ASSERT_EQ(CountPackages(db), 4u);
for (size_t i = 0; i < arraysize(kPackage); ++i) {
ASSERT_TRUE(Contains(db, kPackage[i], kLoopName[i], kPath[i],
- kMountPoint[i], kDeviceName[i]));
+ kMountPoint[i], kDeviceName[i], kHashtreeLoopName[i]));
ASSERT_TRUE(ContainsPackage(db, kPackage[i], kLoopName[i], kPath[i],
- kDeviceName[i]));
+ kDeviceName[i], kHashtreeLoopName[i]));
}
db.RemoveMountedApex(kPackage[0], kPath[0]);
EXPECT_FALSE(Contains(db, kPackage[0], kLoopName[0], kPath[0], kMountPoint[0],
- kDeviceName[0]));
- EXPECT_FALSE(
- ContainsPackage(db, kPackage[0], kLoopName[0], kPath[0], kDeviceName[0]));
+ kDeviceName[0], kHashtreeLoopName[0]));
+ EXPECT_FALSE(ContainsPackage(db, kPackage[0], kLoopName[0], kPath[0],
+ kDeviceName[0], kHashtreeLoopName[0]));
EXPECT_TRUE(Contains(db, kPackage[1], kLoopName[1], kPath[1], kMountPoint[1],
- kDeviceName[1]));
- EXPECT_TRUE(
- ContainsPackage(db, kPackage[1], kLoopName[1], kPath[1], kDeviceName[1]));
+ kDeviceName[1], kHashtreeLoopName[1]));
+ EXPECT_TRUE(ContainsPackage(db, kPackage[1], kLoopName[1], kPath[1],
+ kDeviceName[1], kHashtreeLoopName[1]));
EXPECT_TRUE(Contains(db, kPackage[2], kLoopName[2], kPath[2], kMountPoint[2],
- kDeviceName[2]));
- EXPECT_TRUE(
- ContainsPackage(db, kPackage[2], kLoopName[2], kPath[2], kDeviceName[2]));
+ kDeviceName[2], kHashtreeLoopName[2]));
+ EXPECT_TRUE(ContainsPackage(db, kPackage[2], kLoopName[2], kPath[2],
+ kDeviceName[2], kHashtreeLoopName[2]));
EXPECT_TRUE(Contains(db, kPackage[3], kLoopName[3], kPath[3], kMountPoint[3],
- kDeviceName[3]));
- EXPECT_TRUE(
- ContainsPackage(db, kPackage[3], kLoopName[3], kPath[3], kDeviceName[3]));
+ kDeviceName[3], kHashtreeLoopName[3]));
+ EXPECT_TRUE(ContainsPackage(db, kPackage[3], kLoopName[3], kPath[3],
+ kDeviceName[3], kHashtreeLoopName[3]));
}
#pragma clang diagnostic push
@@ -186,12 +205,38 @@ TEST(ApexDatabaseTest, MountMultiple) {
// [-Werror,-Wused-but-marked-unused]
#pragma clang diagnostic ignored "-Wused-but-marked-unused"
-TEST(MountedApexDataTest, NoDuplicateLoop) {
+TEST(MountedApexDataTest, NoDuplicateLoopDataLoopDevices) {
+ ASSERT_DEATH(
+ {
+ MountedApexDatabase db;
+ db.AddMountedApex("package", false, "loop", "path", "mount", "dm",
+ "hashtree-loop1");
+ db.AddMountedApex("package2", false, "loop", "path2", "mount2", "dm2",
+ "hashtree-loop2");
+ },
+ "Duplicate loop device: loop");
+}
+
+TEST(MountedApexDataTest, NoDuplicateLoopHashtreeLoopDevices) {
+ ASSERT_DEATH(
+ {
+ MountedApexDatabase db;
+ db.AddMountedApex("package", false, "loop1", "path", "mount", "dm",
+ "hashtree-loop");
+ db.AddMountedApex("package2", false, "loop2", "path2", "mount2", "dm2",
+ "hashtree-loop");
+ },
+ "Duplicate loop device: hashtree-loop");
+}
+
+TEST(MountedApexDataTest, NoDuplicateLoopHashtreeAndDataLoopDevices) {
ASSERT_DEATH(
{
MountedApexDatabase db;
- db.AddMountedApex("package", false, "loop", "path", "mount", "dm");
- db.AddMountedApex("package2", false, "loop", "path2", "mount2", "dm2");
+ db.AddMountedApex("package", false, "loop", "path", "mount", "dm",
+ "hashtree-loop1");
+ db.AddMountedApex("package2", false, "loop2", "path2", "mount2", "dm2",
+ "loop");
},
"Duplicate loop device: loop");
}
@@ -200,8 +245,10 @@ TEST(MountedApexDataTest, NoDuplicateDm) {
ASSERT_DEATH(
{
MountedApexDatabase db;
- db.AddMountedApex("package", false, "loop", "path", "mount", "dm");
- db.AddMountedApex("package2", false, "loop2", "path2", "mount2", "dm");
+ db.AddMountedApex("package", false, "loop", "path", "mount", "dm",
+ /* hashtree_loop_name= */ "");
+ db.AddMountedApex("package2", false, "loop2", "path2", "mount2", "dm",
+ /* hashtree_loop_name= */ "");
},
"Duplicate dm device: dm");
}
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index b698c3a..fc1ef8e 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -405,7 +405,8 @@ Result<MountedApexData> MountPackageImpl(const ApexFile& apex,
}
std::string blockDevice = loopbackDevice.name;
MountedApexData apex_data(loopbackDevice.name, apex.GetPath(), mountPoint,
- /* device_name = */ "");
+ /* device_name = */ "",
+ /* hashtree_loop_name = */ "");
// for APEXes in immutable partitions, we don't need to mount them on
// dm-verity because they are already in the dm-verity protected partition;
@@ -424,6 +425,7 @@ Result<MountedApexData> MountPackageImpl(const ApexFile& apex,
}
loop_for_hash = std::move(*hash_tree);
hash_device = loop_for_hash.name;
+ apex_data.hashtree_loop_name = hash_device;
}
auto verityTable =
createVerityTable(*verityData, loopbackDevice.name, hash_device,
@@ -530,13 +532,11 @@ Result<void> Unmount(const MountedApexData& data) {
// Try to free up the loop device.
if (!data.loop_name.empty()) {
- auto log_fn = [](const std::string& path,
- const std::string& id ATTRIBUTE_UNUSED) {
+ auto log_fn = [](const std::string& path, const std::string& /*id*/) {
LOG(VERBOSE) << "Freeing loop device " << path << " for unmount.";
};
loop::DestroyLoopDevice(data.loop_name, log_fn);
}
-
return {};
}
diff --git a/apexd/apexservice_test.cpp b/apexd/apexservice_test.cpp
index 769a137..23f6877 100644
--- a/apexd/apexservice_test.cpp
+++ b/apexd/apexservice_test.cpp
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-#include <stdio.h>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <functional>
#include <memory>
+#include <optional>
#include <string>
#include <unordered_set>
#include <vector>
#include <grp.h>
+#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -47,6 +48,7 @@
#include <android/apex/IApexService.h>
#include "apex_constants.h"
+#include "apex_database.h"
#include "apex_file.h"
#include "apex_manifest.h"
#include "apexd_private.h"
@@ -70,8 +72,10 @@ using android::apex::testing::SessionInfoEq;
using android::base::Errorf;
using android::base::Join;
using android::base::ReadFully;
+using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::unique_fd;
+using android::dm::DeviceMapper;
using android::fs_mgr::Fstab;
using android::fs_mgr::GetEntryForMountPoint;
using android::fs_mgr::ReadFstabFromFile;
@@ -82,6 +86,8 @@ using ::testing::Not;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
+using MountedApexData = MountedApexDatabase::MountedApexData;
+
namespace fs = std::filesystem;
class ApexServiceTest : public ::testing::Test {
@@ -577,8 +583,6 @@ TEST_F(ApexServiceTest, StageSuccess) {
TEST_F(ApexServiceTest,
SubmitStagegSessionSuccessDoesNotLeakTempVerityDevices) {
- using android::dm::DeviceMapper;
-
PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
"/data/app-staging/session_1543",
"staging_data_file");
@@ -617,8 +621,6 @@ TEST_F(ApexServiceTest, SubmitStagedSessionStoresBuildFingerprint) {
}
TEST_F(ApexServiceTest, SubmitStagedSessionFailDoesNotLeakTempVerityDevices) {
- using android::dm::DeviceMapper;
-
PrepareTestApexForInstall installer(
GetTestFile("apex.apexd_test_manifest_mismatch.apex"),
"/data/app-staging/session_239", "staging_data_file");
@@ -721,8 +723,6 @@ TEST_F(ApexServiceTest, MultiStageSuccess) {
}
TEST_F(ApexServiceTest, CannotBeRollbackAndHaveRollbackEnabled) {
- using android::dm::DeviceMapper;
-
PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
"/data/app-staging/session_1543",
"staging_data_file");
@@ -934,6 +934,44 @@ TEST_F(ApexServiceActivationSuccessTest, GetActivePackage) {
ASSERT_EQ(installer_->test_installed_file, active->modulePath);
}
+TEST_F(ApexServiceActivationSuccessTest, ShowsUpInMountedApexDatabase) {
+ ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
+ << GetDebugStr(installer_.get());
+
+ MountedApexDatabase db;
+ db.PopulateFromMounts();
+
+ std::optional<MountedApexData> mounted_apex;
+ db.ForallMountedApexes(installer_->package,
+ [&](const MountedApexData& d, bool active) {
+ if (active) {
+ mounted_apex.emplace(d);
+ }
+ });
+ ASSERT_TRUE(mounted_apex)
+ << "Haven't found " << installer_->test_installed_file
+ << " in the database of mounted apexes";
+
+ // Get all necessary data for assertions on mounted_apex.
+ std::string package_id =
+ installer_->package + "@" + std::to_string(installer_->version);
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string dm_path;
+ ASSERT_TRUE(dm.GetDmDevicePathByName(package_id, &dm_path))
+ << "Failed to get path of dm device " << package_id;
+ auto loop_device = dm.GetParentBlockDeviceByPath(dm_path);
+ ASSERT_TRUE(loop_device) << "Failed to find parent block device of "
+ << dm_path;
+
+ // Now we are ready to assert on mounted_apex.
+ ASSERT_EQ(*loop_device, mounted_apex->loop_name);
+ ASSERT_EQ(installer_->test_installed_file, mounted_apex->full_path);
+ std::string expected_mount = std::string(kApexRoot) + "/" + package_id;
+ ASSERT_EQ(expected_mount, mounted_apex->mount_point);
+ ASSERT_EQ(package_id, mounted_apex->device_name);
+ ASSERT_EQ("", mounted_apex->hashtree_loop_name);
+}
+
struct NoHashtreeApexNameProvider {
static std::string GetTestName() {
return "apex.apexd_test_no_hashtree.apex";
@@ -1020,6 +1058,64 @@ TEST_F(ApexServiceNoHashtreeApexActivationTest,
ASSERT_TRUE(IsOk(block_device));
}
+TEST_F(ApexServiceNoHashtreeApexActivationTest, ShowsUpInMountedApexDatabase) {
+ ASSERT_TRUE(IsOk(service_->activatePackage(installer_->test_installed_file)))
+ << GetDebugStr(installer_.get());
+
+ MountedApexDatabase db;
+ db.PopulateFromMounts();
+
+ std::optional<MountedApexData> mounted_apex;
+ db.ForallMountedApexes(installer_->package,
+ [&](const MountedApexData& d, bool active) {
+ if (active) {
+ mounted_apex.emplace(d);
+ }
+ });
+ ASSERT_TRUE(mounted_apex)
+ << "Haven't found " << installer_->test_installed_file
+ << " in the database of mounted apexes";
+
+ // Get all necessary data for assertions on mounted_apex.
+ std::string package_id =
+ installer_->package + "@" + std::to_string(installer_->version);
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string dm_path;
+ ASSERT_TRUE(dm.GetDmDevicePathByName(package_id, &dm_path))
+ << "Failed to get path of dm device " << package_id;
+ // It's a little bit sad we can't use ConsumePrefix here :(
+ constexpr std::string_view kDevPrefix = "/dev/";
+ ASSERT_TRUE(StartsWith(dm_path, kDevPrefix)) << "Illegal path " << dm_path;
+ dm_path = dm_path.substr(kDevPrefix.length());
+ std::vector<std::string> slaves;
+ {
+ std::string slaves_dir = "/sys/" + dm_path + "/slaves";
+ auto st = WalkDir(slaves_dir, [&](const auto& entry) {
+ std::error_code ec;
+ if (entry.is_symlink(ec)) {
+ slaves.push_back("/dev/block/" + entry.path().filename().string());
+ }
+ if (ec) {
+ ADD_FAILURE() << "Failed to scan " << slaves_dir << " : " << ec;
+ }
+ });
+ ASSERT_TRUE(IsOk(st));
+ }
+ ASSERT_EQ(2u, slaves.size())
+ << "Unexpected number of slaves: " << Join(slaves, ",");
+
+ // Now we are ready to assert on mounted_apex.
+ ASSERT_EQ(installer_->test_installed_file, mounted_apex->full_path);
+ std::string expected_mount = std::string(kApexRoot) + "/" + package_id;
+ ASSERT_EQ(expected_mount, mounted_apex->mount_point);
+ ASSERT_EQ(package_id, mounted_apex->device_name);
+ // For loops we only check that both loop_name and hashtree_loop_name are
+ // slaves of the top device mapper device.
+ ASSERT_THAT(slaves, Contains(mounted_apex->loop_name));
+ ASSERT_THAT(slaves, Contains(mounted_apex->hashtree_loop_name));
+ ASSERT_NE(mounted_apex->loop_name, mounted_apex->hashtree_loop_name);
+}
+
TEST_F(ApexServiceTest, NoHashtreeApexStagePackagesMovesHashtree) {
PrepareTestApexForInstall installer(
GetTestFile("apex.apexd_test_no_hashtree.apex"),
@@ -1083,7 +1179,6 @@ TEST_F(ApexServiceTest, NoHashtreeApexStagePackagesMovesHashtree) {
}
TEST_F(ApexServiceTest, GetFactoryPackages) {
- using ::android::base::StartsWith;
Result<std::vector<ApexInfo>> factoryPackages = GetFactoryPackages();
ASSERT_TRUE(IsOk(factoryPackages));
ASSERT_TRUE(factoryPackages->size() > 0);
@@ -1225,8 +1320,8 @@ TEST_F(ApexServiceActivationSuccessTest, DmDeviceTearDown) {
installer_->package + "@" + std::to_string(installer_->version);
auto find_fn = [](const std::string& name) {
- auto& dm = dm::DeviceMapper::Instance();
- std::vector<dm::DeviceMapper::DmBlockDevice> devices;
+ auto& dm = DeviceMapper::Instance();
+ std::vector<DeviceMapper::DmBlockDevice> devices;
if (!dm.GetAvailableDevices(&devices)) {
return Result<bool>(Errorf("GetAvailableDevices failed"));
}