Merge changes from topic "apexdenrollback"
* changes:
Snapshot and restore DE(user) apex data.
Snapshot and restore CE apex data directories.
diff --git a/apexd/aidl/android/apex/IApexService.aidl b/apexd/aidl/android/apex/IApexService.aidl
index 6eeb2b7..0b87473 100644
--- a/apexd/aidl/android/apex/IApexService.aidl
+++ b/apexd/aidl/android/apex/IApexService.aidl
@@ -33,6 +33,18 @@
void abortActiveSession();
+ /**
+ * Copies the CE apex data directory for the given user to the backup
+ * location, and returns the inode of the snapshot directory.
+ */
+ long snapshotCeData(int user_id, int rollback_id, in @utf8InCpp String apex_name);
+
+ /**
+ * Restores the snapshot of the CE apex data directory for the given user and
+ * apex.
+ */
+ void restoreCeData(int user_id, int rollback_id, in @utf8InCpp String apex_name);
+
void unstagePackages(in @utf8InCpp List<String> active_package_paths);
/**
diff --git a/apexd/apex_constants.h b/apexd/apex_constants.h
index 9f1ce25..e5c305f 100644
--- a/apexd/apex_constants.h
+++ b/apexd/apex_constants.h
@@ -41,6 +41,8 @@
static constexpr const char* kApexSnapshotSubDir = "apexrollback";
static constexpr const char* kDeSysDataDir = "/data/misc";
+static constexpr const char* kDeNDataDir = "/data/misc_de";
+static constexpr const char* kCeDataDir = "/data/misc_ce";
static constexpr const char* kApexPackageSuffix = ".apex";
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index 8d4c508..8ad9ff4 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -1311,41 +1311,106 @@
return ReplaceFiles(from_path, to_path);
}
-void snapshotOrRestoreIfNeeded(const ApexSession& session,
- const std::vector<std::string>& apexes) {
+void snapshotOrRestoreIfNeeded(const std::string& base_dir,
+ const ApexSession& session) {
if (session.HasRollbackEnabled()) {
- for (const auto& apex : apexes) {
- Result<ApexFile> apex_file = ApexFile::Open(apex);
- if (!apex_file) {
- LOG(ERROR) << "Cannot open apex for snapshot: " << apex;
- continue;
- }
- auto apex_name = apex_file->GetManifest().name();
- Result<void> result = snapshotDataDirectory(
- kDeSysDataDir, session.GetRollbackId(), apex_name);
+ for (const auto& apex_name : session.GetApexNames()) {
+ Result<void> result =
+ snapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name);
if (!result) {
- LOG(ERROR) << "Snapshot failed for " << apex << ": " << result.error();
+ LOG(ERROR) << "Snapshot failed for " << apex_name << ": "
+ << result.error();
}
}
} else if (session.IsRollback()) {
- for (const auto& apex : apexes) {
- Result<ApexFile> apex_file = ApexFile::Open(apex);
- if (!apex_file) {
- LOG(ERROR) << "Cannot open apex for restore of data: " << apex;
- continue;
- }
- auto apex_name = apex_file->GetManifest().name();
+ for (const auto& apex_name : session.GetApexNames()) {
// TODO: Back up existing files in case rollback is reverted.
- Result<void> result = restoreDataDirectory(
- kDeSysDataDir, session.GetRollbackId(), apex_name);
+ Result<void> result =
+ restoreDataDirectory(base_dir, session.GetRollbackId(), apex_name);
if (!result) {
- LOG(ERROR) << "Restore of data failed for " << apex << ": "
+ LOG(ERROR) << "Restore of data failed for " << apex_name << ": "
<< result.error();
}
}
}
}
+int snapshotOrRestoreDeUserData() {
+ auto filter_fn = [](const std::filesystem::directory_entry& entry) {
+ std::error_code ec;
+ bool result = entry.is_directory(ec);
+ if (ec) {
+ LOG(ERROR) << "Failed to check is_directory : " << ec.message();
+ return false;
+ }
+ return result;
+ };
+ auto user_dirs = ReadDir(kDeNDataDir, filter_fn);
+
+ if (!user_dirs) {
+ LOG(ERROR) << "Error reading dirs " << user_dirs.error();
+ return 1;
+ }
+
+ auto sessions = ApexSession::GetSessionsInState(SessionState::ACTIVATED);
+
+ for (const ApexSession& session : sessions) {
+ for (const auto& user_dir : *user_dirs) {
+ snapshotOrRestoreIfNeeded(user_dir, session);
+ }
+ }
+
+ return 0;
+}
+
+Result<ino_t> snapshotCeData(const int user_id, const int rollback_id,
+ const std::string& apex_name) {
+ auto base_dir = StringPrintf("%s/%d", kCeDataDir, user_id);
+ Result<void> result = snapshotDataDirectory(base_dir, rollback_id, apex_name);
+ if (!result) {
+ return result.error();
+ }
+ auto ce_snapshot_path =
+ StringPrintf("%s/%s/%d/%s", base_dir.c_str(), kApexSnapshotSubDir,
+ rollback_id, apex_name.c_str());
+ return get_path_inode(ce_snapshot_path);
+}
+
+Result<void> restoreCeData(const int user_id, const int rollback_id,
+ const std::string& apex_name) {
+ auto base_dir = StringPrintf("%s/%d", kCeDataDir, user_id);
+ return restoreDataDirectory(base_dir, rollback_id, apex_name);
+}
+
+// Migrates sessions directory from /data/apex/sessions to
+// /metadata/apex/sessions, if necessary.
+Result<void> migrateSessionsDirIfNeeded() {
+ namespace fs = std::filesystem;
+ auto from_path = std::string(kApexDataDir) + "/sessions";
+ auto exists = PathExists(from_path);
+ if (!exists) {
+ return Error() << "Failed to access " << from_path << ": "
+ << exists.error();
+ }
+ if (!*exists) {
+ LOG(DEBUG) << from_path << " does not exist. Nothing to migrate.";
+ return {};
+ }
+ auto to_path = kApexSessionsDir;
+ std::error_code error_code;
+ fs::copy(from_path, to_path, fs::copy_options::recursive, error_code);
+ if (error_code) {
+ return Error() << "Failed to copy old sessions directory"
+ << error_code.message();
+ }
+ fs::remove_all(from_path, error_code);
+ if (error_code) {
+ return Error() << "Failed to delete old sessions directory "
+ << error_code.message();
+ }
+ return {};
+}
+
void scanStagedSessionsDirAndStage() {
LOG(INFO) << "Scanning " << kApexSessionsDir
<< " looking for sessions to be activated.";
@@ -1420,7 +1485,17 @@
continue;
}
- snapshotOrRestoreIfNeeded(session, apexes);
+ for (const auto& apex : apexes) {
+ // TODO: Avoid opening ApexFile repeatedly.
+ Result<ApexFile> apex_file = ApexFile::Open(apex);
+ if (!apex_file) {
+ LOG(ERROR) << "Cannot open apex file during staging: " << apex;
+ continue;
+ }
+ session.AddApexName(apex_file->GetManifest().name());
+ }
+
+ snapshotOrRestoreIfNeeded(kDeSysDataDir, session);
const Result<void> result = stagePackages(apexes);
if (!result) {
diff --git a/apexd/apexd.h b/apexd/apexd.h
index c38e858..0549ce2 100644
--- a/apexd/apexd.h
+++ b/apexd/apexd.h
@@ -73,10 +73,18 @@
android::base::Result<void> abortActiveSession();
+android::base::Result<ino_t> snapshotCeData(const int user_id,
+ const int rollback_id,
+ const std::string& apex_name);
+android::base::Result<void> restoreCeData(const int user_id,
+ const int rollback_id,
+ const std::string& apex_name);
+
int onBootstrap();
void onStart(CheckpointInterface* checkpoint_service);
void onAllPackagesReady();
void unmountDanglingMounts();
+int snapshotOrRestoreDeUserData();
int unmountAll();
diff --git a/apexd/apexd.rc b/apexd/apexd.rc
index ea70ddf..8f9f87f 100644
--- a/apexd/apexd.rc
+++ b/apexd/apexd.rc
@@ -11,3 +11,9 @@
group system
oneshot
disabled
+
+service apexd-snapshotde /system/bin/apexd --snapshotde
+ user root
+ group system
+ oneshot
+ disabled
diff --git a/apexd/apexd_main.cpp b/apexd/apexd_main.cpp
index 7b42925..c0e0936 100644
--- a/apexd/apexd_main.cpp
+++ b/apexd/apexd_main.cpp
@@ -52,6 +52,11 @@
return android::apex::unmountAll();
}
+ if (strcmp("--snapshotde", argv[1]) == 0) {
+ LOG(INFO) << "Snapshot DE subcommand detected";
+ return android::apex::snapshotOrRestoreDeUserData();
+ }
+
LOG(ERROR) << "Unknown subcommand: " << argv[1];
return 1;
}
diff --git a/apexd/apexd_session.cpp b/apexd/apexd_session.cpp
index c8fe36d..27963a8 100644
--- a/apexd/apexd_session.cpp
+++ b/apexd/apexd_session.cpp
@@ -210,6 +210,11 @@
child_session_ids.end()};
}
+const google::protobuf::RepeatedPtrField<std::string>
+ApexSession::GetApexNames() const {
+ return state_.apex_names();
+}
+
void ApexSession::SetBuildFingerprint(const std::string& fingerprint) {
*(state_.mutable_expected_build_fingerprint()) = fingerprint;
}
@@ -231,6 +236,10 @@
state_.set_crashing_native_process(crashing_process);
}
+void ApexSession::AddApexName(const std::string& apex_name) {
+ state_.add_apex_names(apex_name);
+}
+
Result<void> ApexSession::UpdateStateAndCommit(
const SessionState::State& session_state) {
state_.set_state(session_state);
diff --git a/apexd/apexd_session.h b/apexd/apexd_session.h
index d6fa6f5..2ec823c 100644
--- a/apexd/apexd_session.h
+++ b/apexd/apexd_session.h
@@ -50,6 +50,7 @@
bool HasRollbackEnabled() const;
bool IsRollback() const;
int GetRollbackId() const;
+ const google::protobuf::RepeatedPtrField<std::string> GetApexNames() const;
void SetChildSessionIds(const std::vector<int>& child_session_ids);
void SetBuildFingerprint(const std::string& fingerprint);
@@ -57,6 +58,7 @@
void SetIsRollback(const bool is_rollback);
void SetRollbackId(const int rollback_id);
void SetCrashingNativeProcess(const std::string& crashing_process);
+ void AddApexName(const std::string& apex_name);
android::base::Result<void> UpdateStateAndCommit(
const ::apex::proto::SessionState::State& state);
diff --git a/apexd/apexd_utils.h b/apexd/apexd_utils.h
index 436c704..5d096f1 100644
--- a/apexd/apexd_utils.h
+++ b/apexd/apexd_utils.h
@@ -199,6 +199,16 @@
return {};
}
+inline Result<ino_t> get_path_inode(const std::string& path) {
+ struct stat buf;
+ memset(&buf, 0, sizeof(buf));
+ if (stat(path.c_str(), &buf) != 0) {
+ return ErrnoError() << "Failed to stat " << path;
+ } else {
+ return buf.st_ino;
+ }
+}
+
inline Result<bool> PathExists(const std::string& path) {
namespace fs = std::filesystem;
diff --git a/apexd/apexservice.cpp b/apexd/apexservice.cpp
index 013e13b..816af10 100644
--- a/apexd/apexservice.cpp
+++ b/apexd/apexservice.cpp
@@ -79,6 +79,11 @@
BinderStatus abortActiveSession() override;
BinderStatus rollbackActiveSession() override;
BinderStatus resumeRollbackIfNeeded() override;
+ BinderStatus snapshotCeData(int user_id, int rollback_id,
+ const std::string& apex_name,
+ int64_t* _aidl_return) override;
+ BinderStatus restoreCeData(int user_id, int rollback_id,
+ const std::string& apex_name) override;
status_t dump(int fd, const Vector<String16>& args) override;
@@ -468,6 +473,34 @@
return BinderStatus::ok();
}
+BinderStatus ApexService::snapshotCeData(int user_id, int rollback_id,
+ const std::string& apex_name,
+ int64_t* _aidl_return) {
+ LOG(DEBUG) << "snapshotCeData() received by ApexService.";
+ Result<ino_t> res =
+ ::android::apex::snapshotCeData(user_id, rollback_id, apex_name);
+ if (!res) {
+ return BinderStatus::fromExceptionCode(
+ BinderStatus::EX_SERVICE_SPECIFIC,
+ String8(res.error().message().c_str()));
+ }
+ *_aidl_return = static_cast<uint64_t>(*res);
+ return BinderStatus::ok();
+}
+
+BinderStatus ApexService::restoreCeData(int user_id, int rollback_id,
+ const std::string& apex_name) {
+ LOG(DEBUG) << "restoreCeData() received by ApexService.";
+ Result<void> res =
+ ::android::apex::restoreCeData(user_id, rollback_id, apex_name);
+ if (!res) {
+ return BinderStatus::fromExceptionCode(
+ BinderStatus::EX_SERVICE_SPECIFIC,
+ String8(res.error().message().c_str()));
+ }
+ return BinderStatus::ok();
+}
+
status_t ApexService::onTransact(uint32_t _aidl_code, const Parcel& _aidl_data,
Parcel* _aidl_reply, uint32_t _aidl_flags) {
switch (_aidl_code) {
diff --git a/apexd/apexservice_test.cpp b/apexd/apexservice_test.cpp
index 701b08a..2178ed9 100644
--- a/apexd/apexservice_test.cpp
+++ b/apexd/apexservice_test.cpp
@@ -290,6 +290,15 @@
return data;
}
+ static void DeleteIfExists(const std::string& path) {
+ if (fs::exists(path)) {
+ std::error_code ec;
+ fs::remove_all(path, ec);
+ ASSERT_FALSE(ec) << "Failed to delete dir " << path << " : "
+ << ec.message();
+ }
+ }
+
struct PrepareTestApexForInstall {
static constexpr const char* kTestDir = "/data/app-staging/apexservice_tmp";
@@ -434,6 +443,9 @@
<< ec.message();
});
ASSERT_TRUE(IsOk(status));
+
+ DeleteIfExists("/data/misc_ce/0/apexdata/apex.apexd_test");
+ DeleteIfExists("/data/misc_ce/0/apexrollback/123456");
}
};
@@ -786,6 +798,68 @@
ASSERT_EQ(0, session->GetRollbackId());
}
+TEST_F(ApexServiceTest, SnapshotCeData) {
+ std::error_code ec;
+ fs::create_directory("/data/misc_ce/0/apexdata/apex.apexd_test");
+ ASSERT_FALSE(ec) << "Failed to create data dir "
+ << " : " << ec.message();
+
+ std::ofstream ofs("/data/misc_ce/0/apexdata/apex.apexd_test/hello.txt");
+ ASSERT_TRUE(ofs.good());
+ ofs.close();
+
+ ASSERT_TRUE(
+ RegularFileExists("/data/misc_ce/0/apexdata/apex.apexd_test/hello.txt"));
+
+ int64_t result;
+ service_->snapshotCeData(0, 123456, "apex.apexd_test", &result);
+
+ ASSERT_TRUE(RegularFileExists(
+ "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/hello.txt"));
+
+ // Check that the return value is the inode of the snapshot directory.
+ struct stat buf;
+ memset(&buf, 0, sizeof(buf));
+ ASSERT_EQ(0,
+ stat("/data/misc_ce/0/apexrollback/123456/apex.apexd_test", &buf));
+ ASSERT_EQ(int64_t(buf.st_ino), result);
+}
+
+TEST_F(ApexServiceTest, RestoreCeData) {
+ std::error_code ec;
+ fs::create_directory("/data/misc_ce/0/apexdata/apex.apexd_test", ec);
+ ASSERT_FALSE(ec) << "Failed to create data dir "
+ << " : " << ec.message();
+ fs::create_directory("/data/misc_ce/0/apexrollback/123456", ec);
+ ASSERT_FALSE(ec) << "Failed to create snapshot root dir "
+ << " : " << ec.message();
+ fs::create_directory("/data/misc_ce/0/apexrollback/123456/apex.apexd_test",
+ ec);
+ ASSERT_FALSE(ec) << "Failed to create snapshot dir "
+ << " : " << ec.message();
+
+ std::ofstream newfs("/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt");
+ ASSERT_TRUE(newfs.good());
+ newfs.close();
+
+ std::ofstream oldfs(
+ "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/oldfile.txt");
+ ASSERT_TRUE(oldfs.good());
+ oldfs.close();
+
+ ASSERT_TRUE(RegularFileExists(
+ "/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt"));
+ ASSERT_TRUE(RegularFileExists(
+ "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/oldfile.txt"));
+
+ service_->restoreCeData(0, 123456, "apex.apexd_test");
+
+ ASSERT_TRUE(RegularFileExists(
+ "/data/misc_ce/0/apexdata/apex.apexd_test/oldfile.txt"));
+ ASSERT_FALSE(RegularFileExists(
+ "/data/misc_ce/0/apexdata/apex.apexd_test/newfile.txt"));
+}
+
template <typename NameProvider>
class ApexServiceActivationTest : public ApexServiceTest {
public:
diff --git a/proto/session_state.proto b/proto/session_state.proto
index 63c2f52..ecf00a5 100644
--- a/proto/session_state.proto
+++ b/proto/session_state.proto
@@ -54,4 +54,8 @@
// The crashing native process that has caused this session to be reverted
string crashing_native_process = 8;
+
+ // The names of the apexes within this session. Only populated for sessions
+ // that have been activated.
+ repeated string apex_names = 9;
}