diff options
author | Nikita Ioffe <ioffe@google.com> | 2020-01-02 02:44:44 +0000 |
---|---|---|
committer | Nikita Ioffe <ioffe@google.com> | 2020-01-02 03:16:54 +0000 |
commit | db8fdbb02b7aa45dc05261a57a05c3e9432017fb (patch) | |
tree | 1f209b260bf2f7c635feda1a2db058f829b4820a | |
parent | 2b1c19a07463a65ea49f3b0bdf514f195e62e194 (diff) |
Add hashtree_loop_name to MountedApexData
And also fill in hashtree_loop_name inside
MountedApexDatabase::PopulateFromMounts.
This is a prerequisite for freeing a loop device when an apex is
deactivated (e.g. during userspace reboot). Actual logic to free a loop
device will be implemented in a follow up CL.
Since I was there, added tests for
MountedApexDatabase::PopulateFromMounts function.
Test: atest apex_database_test
Test: atest apexservice_test
Test: atest --test-mapping system/apex:presubmit
Bug: 135984674
Bug: 145670581
Change-Id: Ib2a4ef30073f26931d7e2e326b589b929166e223
-rw-r--r-- | apexd/apex_database.cpp | 65 | ||||
-rw-r--r-- | apexd/apex_database.h | 21 | ||||
-rw-r--r-- | apexd/apex_database_test.cpp | 137 | ||||
-rw-r--r-- | apexd/apexd.cpp | 8 | ||||
-rw-r--r-- | apexd/apexservice_test.cpp | 115 |
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")); } |