1
0
mirror of git://projects.qi-hardware.com/gmenu2x.git synced 2024-11-22 18:23:08 +02:00

Write app settings files atomically

This is safer than the original code, which didn't use a temporary file
and could therefore leave partial files. Also it avoids a full sync(),
which can take a long time if for example a big file transfer is going
on or recently ended.
This commit is contained in:
Maarten ter Huurne 2014-08-18 15:35:56 +02:00
parent 956a9b6429
commit fc4582a61f
3 changed files with 44 additions and 12 deletions

View File

@ -328,7 +328,7 @@ bool LinkApp::targetExists()
} }
bool LinkApp::save() { bool LinkApp::save() {
if (!edited) return false; if (!edited) return true;
std::ostringstream out; std::ostringstream out;
if (!isOpk()) { if (!isOpk()) {
@ -348,16 +348,7 @@ bool LinkApp::save() {
if (out.tellp() > 0) { if (out.tellp() > 0) {
DEBUG("Saving app settings: %s\n", file.c_str()); DEBUG("Saving app settings: %s\n", file.c_str());
ofstream f(file.c_str()); return writeStringToFile(file, out.str());
if (f.is_open()) {
f << out.str();
f.close();
sync();
return true;
} else {
ERROR("Error while opening the file '%s' for write.\n", file.c_str());
return false;
}
} else { } else {
DEBUG("Empty app settings: %s\n", file.c_str()); DEBUG("Empty app settings: %s\n", file.c_str());
return unlink(file.c_str()) == 0 || errno == ENOENT; return unlink(file.c_str()) == 0 || errno == ENOENT;
@ -560,7 +551,9 @@ void LinkApp::selector(int startSelection, const string &selectorDir) {
} }
unique_ptr<Launcher> LinkApp::prepareLaunch(const string &selectedFile) { unique_ptr<Launcher> LinkApp::prepareLaunch(const string &selectedFile) {
save(); if (!save()) {
ERROR("Error saving app settings to '%s'.\n", file.c_str());
}
if (!isOpk()) { if (!isOpk()) {
//Set correct working directory //Set correct working directory

View File

@ -28,6 +28,7 @@
//for browsing the filesystem //for browsing the filesystem
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <fcntl.h>
#include <dirent.h> #include <dirent.h>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
@ -81,6 +82,36 @@ string readFileAsString(string const& filename) {
} }
} }
// Use C functions since STL doesn't seem to have any way of applying fsync().
bool writeStringToFile(string const& filename, string const& data) {
// Open temporary file.
string tempname = filename + '~';
int fd = open(tempname.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC);
if (fd < 0) {
return false;
}
// Write temporary file.
bool ok = write(fd, data.c_str(), data.size()) >= 0;
if (ok) {
ok = fsync(fd) == 0;
}
// Close temporary file.
while (close(fd)) {
if (errno != EINTR) {
return false;
}
}
// Replace actual output file with temporary file.
if (ok) {
ok = rename(tempname.c_str(), filename.c_str()) == 0;
}
return ok;
}
string parentDir(string const& dir) { string parentDir(string const& dir) {
// Note that size() is unsigned, so for short strings the '- 2' wraps // Note that size() is unsigned, so for short strings the '- 2' wraps
// around and as a result the entire string is searched, which is fine. // around and as a result the entire string is searched, which is fine.

View File

@ -49,6 +49,14 @@ std::string rtrim(const std::string& s);
/** Returns the contents of the given file as a string. */ /** Returns the contents of the given file as a string. */
std::string readFileAsString(std::string const& filename); std::string readFileAsString(std::string const& filename);
/**
* Writes the given string to a file.
* The update is done atomically but not durably; if you need durability
* when fsync() the parent directory afterwards.
* @return True iff the file was written successfully.
*/
bool writeStringToFile(std::string const& filename, std::string const& data);
std::string strreplace(std::string orig, const std::string &search, const std::string &replace); std::string strreplace(std::string orig, const std::string &search, const std::string &replace);
std::string cmdclean(std::string cmdline); std::string cmdclean(std::string cmdline);