Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 55974f14 authored by Yifan Hong's avatar Yifan Hong
Browse files

libsnapshot: write files atomically

... by writing to a temporary file then rename()'ing it
back.

Test: libsnapshot_test
Bug: 144549076
Change-Id: Ide400aff8d67d56d422d0adea3a4f1673ebc9994
parent b6663cfb
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -217,7 +217,7 @@ bool SnapshotManager::FinishedSnapshotWrites() {
    // 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 @@ bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state) {
    }
#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 @@ bool SnapshotManager::WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus
    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;
    }
+12 −0
Original line number Diff line number Diff line
@@ -140,5 +140,17 @@ AutoUnmountDevice::~AutoUnmountDevice() {
    }
}

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
+7 −0
Original line number Diff line number Diff line
@@ -112,5 +112,12 @@ std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
// 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