1
0
mirror of git://projects.qi-hardware.com/gmenu2x.git synced 2024-09-28 19:30:23 +03: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() {
if (!edited) return false;
if (!edited) return true;
std::ostringstream out;
if (!isOpk()) {
@ -348,16 +348,7 @@ bool LinkApp::save() {
if (out.tellp() > 0) {
DEBUG("Saving app settings: %s\n", file.c_str());
ofstream f(file.c_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;
}
return writeStringToFile(file, out.str());
} else {
DEBUG("Empty app settings: %s\n", file.c_str());
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) {
save();
if (!save()) {
ERROR("Error saving app settings to '%s'.\n", file.c_str());
}
if (!isOpk()) {
//Set correct working directory

View File

@ -28,6 +28,7 @@
//for browsing the filesystem
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <fstream>
#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) {
// 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.

View File

@ -49,6 +49,14 @@ std::string rtrim(const std::string& s);
/** Returns the contents of the given file as a string. */
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 cmdclean(std::string cmdline);