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
diff --git a/fs_mgr/libfiemap/metadata.cpp b/fs_mgr/libfiemap/metadata.cpp
index 22b8afb..0a56f6a 100644
--- a/fs_mgr/libfiemap/metadata.cpp
+++ b/fs_mgr/libfiemap/metadata.cpp
@@ -111,13 +111,7 @@
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;
}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 02b64ac..a2dbb10 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -123,13 +123,46 @@
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 @@
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;