mirror of
git://projects.qi-hardware.com/gmenu2x.git
synced 2024-11-16 17:08:25 +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() {
|
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
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user