diff --git a/src/linkapp.cpp b/src/linkapp.cpp index ad0b56e..000971a 100644 --- a/src/linkapp.cpp +++ b/src/linkapp.cpp @@ -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 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 diff --git a/src/utilities.cpp b/src/utilities.cpp index 0bbf636..e66ca15 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -28,6 +28,7 @@ //for browsing the filesystem #include #include +#include #include #include #include @@ -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. diff --git a/src/utilities.h b/src/utilities.h index cd442ac..a4873d2 100644 --- a/src/utilities.h +++ b/src/utilities.h @@ -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);