libsnapshot: write files atomically
... by writing to a temporary file then rename()'ing it
back.
Test: libsnapshot_test
Bug: 144549076
Change-Id: Ide400aff8d67d56d422d0adea3a4f1673ebc9994
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 0ee982c..2d490af 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -217,7 +217,7 @@
// we can tell whether or not we performed a rollback.
auto contents = device_->GetSlotSuffix();
auto boot_file = GetSnapshotBootIndicatorPath();
- if (!android::base::WriteStringToFile(contents, boot_file)) {
+ if (!WriteStringToFileAtomic(contents, boot_file)) {
PLOG(ERROR) << "write failed: " << boot_file;
return false;
}
@@ -1731,7 +1731,7 @@
}
#endif
- if (!android::base::WriteStringToFile(contents, GetStateFilePath())) {
+ if (!WriteStringToFileAtomic(contents, GetStateFilePath())) {
PLOG(ERROR) << "Could not write to state file";
return false;
}
@@ -1780,14 +1780,14 @@
CHECK(!status.name().empty());
auto path = GetSnapshotStatusFilePath(status.name());
- unique_fd fd(
- open(path.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW | O_CREAT | O_SYNC | O_TRUNC, 0660));
- if (fd < 0) {
- PLOG(ERROR) << "Open failed: " << path;
+
+ std::string content;
+ if (!status.SerializeToString(&content)) {
+ LOG(ERROR) << "Unable to serialize SnapshotStatus for " << status.name();
return false;
}
- if (!status.SerializeToFileDescriptor(fd.get())) {
+ if (!WriteStringToFileAtomic(content, path)) {
PLOG(ERROR) << "Unable to write SnapshotStatus to " << path;
return false;
}
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 1b2f528..fa1d7f0 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -140,5 +140,17 @@
}
}
+bool WriteStringToFileAtomic(const std::string& content, const std::string& path) {
+ std::string tmp_path = path + ".tmp";
+ if (!android::base::WriteStringToFile(content, tmp_path)) {
+ return false;
+ }
+ if (rename(tmp_path.c_str(), path.c_str()) == -1) {
+ PLOG(ERROR) << "rename failed from " << tmp_path << " to " << path;
+ return false;
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 0c08ed2..5cc572e 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -112,5 +112,12 @@
// Initialize a device before using it as the COW device for a dm-snapshot device.
bool InitializeCow(const std::string& device);
+// "Atomically" write string to file. This is done by a series of actions:
+// 1. Write to path + ".tmp"
+// 2. Move temporary file to path using rename()
+// Note that rename() is an atomic operation. This function may not work properly if there
+// is an open fd to |path|, because that fd has an old view of the file.
+bool WriteStringToFileAtomic(const std::string& content, const std::string& path);
+
} // namespace snapshot
} // namespace android