Make partition metadata write atomic

We have seen multiple OTA failures with invalid geometry magic
signature. Make partition metadata write atomic by writing to a tmpfile
first and then do a rename.

Test: th
Bug: 303770065
Bug: 298149189
Change-Id: Id1d565de73439b95b665144c2f02fc97273d341c
This commit is contained in:
Kelvin Zhang 2023-10-12 10:16:44 -07:00
parent c519d1dde2
commit 78d5352289
2 changed files with 41 additions and 13 deletions

View file

@ -111,13 +111,7 @@ bool SaveMetadata(MetadataBuilder* builder, const std::string& metadata_dir) {
return true;
}
unique_fd fd(open(metadata_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY | O_SYNC, 0644));
if (fd < 0) {
LOG(ERROR) << "open failed: " << metadata_file;
return false;
}
if (!WriteToImageFile(fd, *exported.get())) {
if (!WriteToImageFile(metadata_file, *exported.get())) {
LOG(ERROR) << "Unable to save new metadata";
return false;
}

View file

@ -123,13 +123,46 @@ bool WriteToImageFile(borrowed_fd fd, const LpMetadata& input) {
return true;
}
bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
#if !defined(_WIN32)
bool FsyncDirectory(const char* dirname) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname, O_RDONLY | O_CLOEXEC)));
if (fd == -1) {
PLOG(ERROR) << "Failed to open " << dirname;
return false;
}
return WriteToImageFile(fd, input);
if (fsync(fd) == -1) {
if (errno == EROFS || errno == EINVAL) {
PLOG(WARNING) << "Skip fsync " << dirname
<< " on a file system does not support synchronization";
} else {
PLOG(ERROR) << "Failed to fsync " << dirname;
return false;
}
}
return true;
}
#endif
bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
const auto parent_dir = base::Dirname(file);
TemporaryFile tmpfile(parent_dir);
if (!WriteToImageFile(tmpfile.fd, input)) {
PLOG(ERROR) << "Failed to write geometry data to tmpfile " << tmpfile.path;
return false;
}
#if !defined(_WIN32)
fsync(tmpfile.fd);
#endif
const auto err = rename(tmpfile.path, file.c_str());
if (err != 0) {
PLOG(ERROR) << "Failed to rename tmp geometry file " << tmpfile.path << " to " << file;
return false;
}
#if !defined(_WIN32)
FsyncDirectory(parent_dir.c_str());
#endif
return true;
}
ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
@ -208,7 +241,8 @@ bool ImageBuilder::ExportFiles(const std::string& output_dir) {
std::string file_name = "super_" + name + ".img";
std::string file_path = output_dir + "/" + file_name;
static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
static const int kOpenFlags =
O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));
if (fd < 0) {
PERROR << "open failed: " << file_path;