Skip to content

Commit c80d39b

Browse files
committed
Improve atomiticy of update checkpointing
Current check point works by writing different prefs to different files under a pending directory, and rename the pending directory to actual pref directory afterwards to achieve atomicity. It has two pitfalls: 1. Before the rename() call, existing prefs dir must be rm -rf'ed , this deletion process isn't atomic. If device rebooted during rm -rf, we will end up with a partially deleted old pref. 2. fsync() on the parent directory is needed after rename() This CL addresses both issues. For #1, we rename() the old pref dir to a tmp dir first, and then rm -rf the tmp dir. Upon device restart, if the current prefs dir is empty, we can simply rename() the pending directory to actual pref directory. Test: th Bug: 295252766 Change-Id: Ic671a18245986c579b51d7443c3e8c10e206c448
1 parent e53a318 commit c80d39b

File tree

3 files changed

+37
-4
lines changed

3 files changed

+37
-4
lines changed

common/prefs.cc

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
#include <filesystem>
2121
#include <unistd.h>
2222

23+
#include <android-base/file.h>
2324
#include <base/files/file_enumerator.h>
2425
#include <base/files/file_util.h>
2526
#include <base/logging.h>
2627
#include <base/strings/string_number_conversions.h>
2728
#include <base/strings/string_split.h>
2829
#include <base/strings/string_util.h>
2930

30-
#include "update_engine/common/platform_constants.h"
3131
#include "update_engine/common/utils.h"
3232

3333
using std::string;
@@ -74,7 +74,16 @@ bool PrefsBase::GetInt64(const std::string_view key, int64_t* value) const {
7474
if (!GetString(key, &str_value))
7575
return false;
7676
base::TrimWhitespaceASCII(str_value, base::TRIM_ALL, &str_value);
77-
TEST_AND_RETURN_FALSE(base::StringToInt64(str_value, value));
77+
if (str_value.empty()) {
78+
LOG(ERROR) << "When reading pref " << key
79+
<< ", got an empty value after trim";
80+
return false;
81+
}
82+
if (!base::StringToInt64(str_value, value)) {
83+
LOG(ERROR) << "When reading pref " << key << ", failed to convert value "
84+
<< str_value << " to integer";
85+
return false;
86+
}
7887
return true;
7988
}
8089

@@ -206,18 +215,30 @@ bool Prefs::FileStorage::DeleteTemporaryPrefs() {
206215
}
207216

208217
bool Prefs::FileStorage::SwapPrefs() {
209-
if (std::filesystem::exists(prefs_dir_.value())) {
210-
std::filesystem::remove_all(prefs_dir_.value());
218+
if (!utils::DeleteDirectory(prefs_dir_.value().c_str())) {
219+
LOG(ERROR) << "Failed to remove prefs dir " << prefs_dir_;
220+
return false;
211221
}
212222
if (rename(GetTemporaryDir().c_str(), prefs_dir_.value().c_str()) != 0) {
213223
LOG(ERROR) << "Error replacing prefs with prefs_tmp" << strerror(errno);
214224
return false;
215225
}
226+
if (!utils::FsyncDirectory(
227+
android::base::Dirname(prefs_dir_.value()).c_str())) {
228+
PLOG(ERROR) << "Failed to fsync prefs parent dir after swapping prefs";
229+
}
216230
return true;
217231
}
218232

219233
bool Prefs::FileStorage::Init(const base::FilePath& prefs_dir) {
220234
prefs_dir_ = prefs_dir;
235+
if (!std::filesystem::exists(prefs_dir_.value())) {
236+
LOG(INFO) << "Prefs dir does not exist, possibly due to an interrupted "
237+
"transaction.";
238+
if (std::filesystem::exists(GetTemporaryDir())) {
239+
SwapPrefs();
240+
}
241+
}
221242

222243
// Delete empty directories. Ignore errors when deleting empty directories.
223244
DeleteEmptyDirectories(prefs_dir_);

common/utils.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,17 @@ bool SendFile(int out_fd, int in_fd, size_t count) {
412412
return true;
413413
}
414414

415+
bool DeleteDirectory(const char* dirname) {
416+
const std::string tmpdir = std::string(dirname) + "_deleted";
417+
std::filesystem::remove_all(tmpdir);
418+
if (rename(dirname, tmpdir.c_str()) != 0) {
419+
PLOG(ERROR) << "Failed to rename " << dirname << " to " << tmpdir;
420+
return false;
421+
}
422+
std::filesystem::remove_all(tmpdir);
423+
return true;
424+
}
425+
415426
bool FsyncDirectory(const char* dirname) {
416427
android::base::unique_fd fd(
417428
TEMP_FAILURE_RETRY(open(dirname, O_RDONLY | O_CLOEXEC)));

common/utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ off_t FileSize(int fd);
162162
bool SendFile(int out_fd, int in_fd, size_t count);
163163

164164
bool FsyncDirectory(const char* dirname);
165+
bool DeleteDirectory(const char* dirname);
165166
bool WriteStringToFileAtomic(const std::string& path, std::string_view content);
166167

167168
// Returns true if the file exists for sure. Returns false if it doesn't exist,

0 commit comments

Comments
 (0)