mirror of
git://projects.qi-hardware.com/gmenu2x.git
synced 2024-11-22 06:15:00 +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:
parent
956a9b6429
commit
fc4582a61f
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user