Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8755f79a authored by Nikita Ioffe's avatar Nikita Ioffe
Browse files

installd: Add destroyAppDataSnapshot and corresponding binder API

- destroyAppDataSnapshot: deletes both ce and de snapshots (controlled
  via storageFlags), for ce snapshot fallbacks to deleting it by it's
  inode in case snapshot is not accessible (user hasn't unlocked the
  phone yet).
- snapshotAppData: in case ce snapshot was requested, it will now return
  inode if a folder with the snapshot. Otherwise it will return 0.

Passing ceSnapshotInode to destroyAppDataSnapshot should make logic
inside RollbackManagerService a bit easier. Since it will be able to
delete ce snapshot regardless of user unlocking their phone.

Bug: 112431924
Test: installd_service_test installd_utils_test
Change-Id: I015de12593b87f3000f004cc0a901a9208d0b693
Merged-In: I015de12593b87f3000f004cc0a901a9208d0b693
parent a60ff681
Loading
Loading
Loading
Loading
+67 −11
Original line number Diff line number Diff line
@@ -167,6 +167,15 @@ binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) {
    }
}

binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) {
    if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) {
        return ok();
    } else {
        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
                StringPrintf("UUID must be null or \"%s\", got: %s", kTestUuid, uuid->c_str()));
    }
}

binder::Status checkArgumentPackageName(const std::string& packageName) {
    if (is_valid_package_name(packageName.c_str())) {
        return ok();
@@ -219,6 +228,13 @@ binder::Status checkArgumentPath(const std::unique_ptr<std::string>& path) {
    }                                                       \
}

#define CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(uuid) {         \
    auto status = checkArgumentUuidTestOrNull(uuid);        \
    if (!status.isOk()) {                                   \
        return status;                                      \
    }                                                       \
}                                                           \

#define CHECK_ARGUMENT_PACKAGE_NAME(packageName) {          \
    binder::Status status =                                 \
            checkArgumentPackageName((packageName));        \
@@ -768,20 +784,21 @@ static int32_t copy_directory_recursive(const char* from, const char* to) {

binder::Status InstalldNativeService::snapshotAppData(
        const std::unique_ptr<std::string>& volumeUuid,
        const std::string& packageName, int32_t user, int32_t storageFlags) {
        const std::string& packageName, int32_t user, int32_t storageFlags,
        int64_t* _aidl_return) {
    ENFORCE_UID(AID_SYSTEM);
    CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
    std::lock_guard<std::recursive_mutex> lock(mLock);

    const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
    const char* package_name = packageName.c_str();

    if (volume_uuid && strcmp(volume_uuid, kTestUuid)) {
        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
                StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid));
    }

    binder::Status res = ok();
    // Default result to 0, it will be populated with inode of ce data snapshot
    // if FLAG_STORAGE_CE has been passed.
    if (_aidl_return != nullptr) *_aidl_return = 0;

    bool clear_ce_on_exit = false;
    bool clear_de_on_exit = false;

@@ -862,6 +879,16 @@ binder::Status InstalldNativeService::snapshotAppData(
            clear_ce_on_exit = true;
            return res;
        }
        if (_aidl_return != nullptr) {
            auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
                    package_name);
            rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return));
            if (rc != 0) {
                res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path);
                clear_ce_on_exit = true;
                return res;
            }
        }
    }

    return res;
@@ -872,17 +899,13 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot(
        const int32_t appId, const int64_t ceDataInode, const std::string& seInfo,
        const int32_t user, int32_t storageFlags) {
    ENFORCE_UID(AID_SYSTEM);
    CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
    std::lock_guard<std::recursive_mutex> lock(mLock);

    const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
    const char* package_name = packageName.c_str();

    if (volume_uuid && strcmp(volume_uuid, kTestUuid)) {
        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
                StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid));
    }

    auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid,
            user, package_name);
    auto from_de = create_data_misc_de_rollback_package_path(volume_uuid,
@@ -933,6 +956,39 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot(
    return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo);
}

binder::Status InstalldNativeService::destroyAppDataSnapshot(
        const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName,
        const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags) {
    ENFORCE_UID(AID_SYSTEM);
    CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
    std::lock_guard<std::recursive_mutex> lock(mLock);

    const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
    const char* package_name = packageName.c_str();

    if (storageFlags & FLAG_STORAGE_DE) {
        auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid,
                user, package_name);

        int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */);
        if (res != 0) {
            return error(res, "Failed clearing snapshot " + de_snapshot_path);
        }
    }

    if (storageFlags & FLAG_STORAGE_CE) {
        auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid,
                user, package_name, ceSnapshotInode);
        int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */);
        if (res != 0) {
            return error(res, "Failed clearing snapshot " + ce_snapshot_path);
        }
    }
    return ok();
}


binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
        const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
        const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+5 −1
Original line number Diff line number Diff line
@@ -61,10 +61,14 @@ public:
    binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);

    binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid,
            const std::string& packageName, const int32_t user, int32_t storageFlags);
            const std::string& packageName, const int32_t user, int32_t storageFlags,
            int64_t* _aidl_return);
    binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid,
            const std::string& packageName, const int32_t appId, const int64_t ceDataInode,
            const std::string& seInfo, const int32_t user, int32_t storageFlags);
    binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid,
            const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode,
            int32_t storageFlags);

    binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
            const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+3 −5
Original line number Diff line number Diff line
@@ -105,14 +105,12 @@ interface IInstalld {
        int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath,
        @nullable @utf8InCpp String dexMetadata);

    void snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
    long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
            int userId, int storageFlags);
    void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
            int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags);

    // TODO(narayan) we need an API to delete the app data snapshot as well.
    // void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid,
    //        in @utf8InCpp String packageName, int userId, int storageFlags);
    void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
            int userId, long ceSnapshotInode, int storageFlags);

    const int FLAG_STORAGE_DE = 0x1;
    const int FLAG_STORAGE_CE = 0x2;
+145 −8
Original line number Diff line number Diff line
@@ -292,8 +292,13 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) {
          0700, 10000, 20000, false /* follow_symlinks */));

  // Request a snapshot of the CE content but not the DE content.
  int64_t ce_snapshot_inode;
  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
          "com.foo", 0, FLAG_STORAGE_CE));
          "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode));
  struct stat buf;
  memset(&buf, 0, sizeof(buf));
  ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf));
  ASSERT_EQ(ce_snapshot_inode, (int64_t) buf.st_ino);

  std::string ce_content, de_content;
  // At this point, we should have the CE content but not the DE content.
@@ -311,7 +316,9 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) {

  // Request a snapshot of the DE content but not the CE content.
  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
          "com.foo", 0, FLAG_STORAGE_DE));
          "com.foo", 0, FLAG_STORAGE_DE, &ce_snapshot_inode));
  // Only DE content snapshot was requested.
  ASSERT_EQ(ce_snapshot_inode, 0);

  // At this point, both the CE as well as the DE content should be fully
  // populated.
@@ -330,7 +337,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) {

  // Request a snapshot of both the CE as well as the DE content.
  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));

  ASSERT_TRUE(android::base::ReadFileToString(
      rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
@@ -356,10 +363,13 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) {

  auto scope_guard = android::base::make_scope_guard(deleter);

  int64_t ce_snapshot_inode;
  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
          "com.foo", 0, FLAG_STORAGE_CE));
          "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode));
  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
          "com.foo", 0, FLAG_STORAGE_DE));
          "com.foo", 0, FLAG_STORAGE_DE, nullptr));
  // No CE content snapshot was performed.
  ASSERT_EQ(ce_snapshot_inode, 0);

  // The snapshot calls must succeed but there should be no snapshot
  // created.
@@ -409,7 +419,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsExistingSnapshot) {
          0700, 10000, 20000, false /* follow_symlinks */));

  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));

  // Previous snapshot (with data for file1) must be cleared.
  struct stat sb;
@@ -435,7 +445,7 @@ TEST_F(ServiceTest, SnapshotAppData_WrongVolumeUuid) {
  auto scope_guard = android::base::make_scope_guard(deleter);

  EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"),
          "com.foo", 0, FLAG_STORAGE_DE));
          "com.foo", 0, FLAG_STORAGE_DE, nullptr));
}

TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) {
@@ -484,7 +494,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) {
          "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1",
          0700, 10000, 20000, false /* follow_symlinks */));
  ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
          "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE));
          "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr));
  // The snapshot call must clear cache.
  struct stat sb;
  ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb));
@@ -546,6 +556,133 @@ TEST_F(ServiceTest, RestoreAppDataSnapshot) {
  ASSERT_EQ("DE_RESTORE_CONTENT", de_content);
}

TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) {
  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);

  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));

  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");

  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));

  auto deleter = [&rollback_ce_dir, &rollback_de_dir,
          &fake_package_ce_path, &fake_package_de_path]() {
      delete_dir_contents(rollback_ce_dir, true);
      delete_dir_contents(rollback_de_dir, true);
      delete_dir_contents(fake_package_ce_path, true);
      delete_dir_contents(fake_package_de_path, true);
      rmdir(rollback_ce_dir.c_str());
      rmdir(rollback_de_dir.c_str());
  };
  auto scope_guard = android::base::make_scope_guard(deleter);

  // Prepare data for snapshot.
  ASSERT_TRUE(android::base::WriteStringToFile(
          "TEST_CONTENT_CE", fake_package_ce_path + "/file1",
          0700, 10000, 20000, false /* follow_symlinks */));
  ASSERT_TRUE(android::base::WriteStringToFile(
          "TEST_CONTENT_DE", fake_package_de_path + "/file1",
          0700, 10000, 20000, false /* follow_symlinks */));

  int64_t ce_snapshot_inode;
  // Request a snapshot of both the CE as well as the DE content.
  ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
          "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
  // Because CE data snapshot was requested, ce_snapshot_inode can't be null.
  ASSERT_NE(0, ce_snapshot_inode);
  // Check snapshot is there.
  struct stat sb;
  ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
  ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb));


  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
          "com.foo", 0, ce_snapshot_inode, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
  // Check snapshot is deleted.
  ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
  ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
}

TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) {
  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);

  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));

  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");

  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));

  auto deleter = [&rollback_ce_dir, &rollback_de_dir,
          &fake_package_ce_path, &fake_package_de_path]() {
      delete_dir_contents(rollback_ce_dir, true);
      delete_dir_contents(rollback_de_dir, true);
      delete_dir_contents(fake_package_ce_path, true);
      delete_dir_contents(fake_package_de_path, true);
      rmdir(rollback_ce_dir.c_str());
      rmdir(rollback_de_dir.c_str());
  };
  auto scope_guard = android::base::make_scope_guard(deleter);

  // Create a snapshot
  ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700));
  ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700));
  ASSERT_TRUE(android::base::WriteStringToFile(
          "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1",
          0700, 10000, 20000, false /* follow_symlinks */));
  ASSERT_TRUE(android::base::WriteStringToFile(
          "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1",
          0700, 10000, 20000, false /* follow_symlinks */));

  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
          "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());

  // Check snapshot is deleted.
  struct stat sb;
  ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
  ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));

  // Check that deleting already deleted snapshot is no-op.
  ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
          "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
}

TEST_F(ServiceTest, DestroyAppDataSnapshot_WrongVolumeUuid) {
  // Setup rollback data to make sure that test fails due to wrong volumeUuid
  // being passed, not because of some other reason.
  auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
  auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);

  ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
  ASSERT_TRUE(mkdirs(rollback_de_dir, 700));

  auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
  auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");

  ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
  ASSERT_TRUE(mkdirs(fake_package_de_path, 700));

  auto deleter = [&rollback_ce_dir, &rollback_de_dir,
          &fake_package_ce_path, &fake_package_de_path]() {
      delete_dir_contents(rollback_ce_dir, true);
      delete_dir_contents(rollback_de_dir, true);
      delete_dir_contents(fake_package_ce_path, true);
      delete_dir_contents(fake_package_de_path, true);
      rmdir(rollback_ce_dir.c_str());
      rmdir(rollback_de_dir.c_str());
  };
  auto scope_guard = android::base::make_scope_guard(deleter);

  ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"),
          "com.foo", 0, 0, FLAG_STORAGE_DE).isOk());
}

TEST_F(ServiceTest, RestoreAppDataSnapshot_WrongVolumeUuid) {
  // Setup rollback data to make sure that fails due to wrong volumeUuid being
+24 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <string.h>

#include <android-base/logging.h>
#include <android-base/scopeguard.h>
#include <gtest/gtest.h>

#include "InstalldNativeService.h"
@@ -565,6 +566,29 @@ TEST_F(UtilsTest, TestRollbackPaths) {
    EXPECT_EQ("/data/misc_de/10/rollback",
            create_data_misc_de_rollback_path(nullptr, 10));

    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 0));
    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 239));

    auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo");
    auto deleter = [&rollback_ce_package_path]() {
        delete_dir_contents_and_dir(rollback_ce_package_path, true /* ignore_if_missing */);
    };
    auto scope_guard = android::base::make_scope_guard(deleter);

    ASSERT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700));

    ino_t ce_data_inode;
    ASSERT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode));

    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", ce_data_inode));
    // Check that path defined by inode is picked even if it's not the same as
    // the fallback one.
    EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
            create_data_misc_ce_rollback_package_path(nullptr, 0, "com.bar", ce_data_inode));

    // These last couple of cases are never exercised in production because we
    // only snapshot apps in the primary data partition. Exercise them here for
    // the sake of completeness.
Loading