Date: | 2014-08-18 15:35:56 (9 years 7 months ago) |
---|---|
Author: | Maarten ter Huurne |
Commit: | fc4582a61fce2b51625a2dfac6d97ca9549e948d |
Message: | 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. |
Files: |
src/linkapp.cpp (3 diffs) src/utilities.cpp (2 diffs) src/utilities.h (1 diff) |
Change Details
src/linkapp.cpp | ||
---|---|---|
328 | 328 | } |
329 | 329 | |
330 | 330 | bool LinkApp::save() { |
331 | if (!edited) return false; | |
331 | if (!edited) return true; | |
332 | 332 | |
333 | 333 | std::ostringstream out; |
334 | 334 | if (!isOpk()) { |
... | ... | |
348 | 348 | |
349 | 349 | if (out.tellp() > 0) { |
350 | 350 | DEBUG("Saving app settings: %s\n", file.c_str()); |
351 | ofstream f(file.c_str()); | |
352 | if (f.is_open()) { | |
353 | f << out.str(); | |
354 | f.close(); | |
355 | sync(); | |
356 | return true; | |
357 | } else { | |
358 | ERROR("Error while opening the file '%s' for write.\n", file.c_str()); | |
359 | return false; | |
360 | } | |
351 | return writeStringToFile(file, out.str()); | |
361 | 352 | } else { |
362 | 353 | DEBUG("Empty app settings: %s\n", file.c_str()); |
363 | 354 | return unlink(file.c_str()) == 0 || errno == ENOENT; |
... | ... | |
560 | 551 | } |
561 | 552 | |
562 | 553 | unique_ptr<Launcher> LinkApp::prepareLaunch(const string &selectedFile) { |
563 | save(); | |
554 | if (!save()) { | |
555 | ERROR("Error saving app settings to '%s'.\n", file.c_str()); | |
556 | } | |
564 | 557 | |
565 | 558 | if (!isOpk()) { |
566 | 559 | //Set correct working directory |
src/utilities.cpp | ||
---|---|---|
28 | 28 | //for browsing the filesystem |
29 | 29 | #include <sys/stat.h> |
30 | 30 | #include <sys/types.h> |
31 | #include <fcntl.h> | |
31 | 32 | #include <dirent.h> |
32 | 33 | #include <fstream> |
33 | 34 | #include <iostream> |
... | ... | |
81 | 82 | } |
82 | 83 | } |
83 | 84 | |
85 | // Use C functions since STL doesn't seem to have any way of applying fsync(). | |
86 | bool writeStringToFile(string const& filename, string const& data) { | |
87 | // Open temporary file. | |
88 | string tempname = filename + '~'; | |
89 | int fd = open(tempname.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC); | |
90 | if (fd < 0) { | |
91 | return false; | |
92 | } | |
93 | ||
94 | // Write temporary file. | |
95 | bool ok = write(fd, data.c_str(), data.size()) >= 0; | |
96 | if (ok) { | |
97 | ok = fsync(fd) == 0; | |
98 | } | |
99 | ||
100 | // Close temporary file. | |
101 | while (close(fd)) { | |
102 | if (errno != EINTR) { | |
103 | return false; | |
104 | } | |
105 | } | |
106 | ||
107 | // Replace actual output file with temporary file. | |
108 | if (ok) { | |
109 | ok = rename(tempname.c_str(), filename.c_str()) == 0; | |
110 | } | |
111 | ||
112 | return ok; | |
113 | } | |
114 | ||
84 | 115 | string parentDir(string const& dir) { |
85 | 116 | // Note that size() is unsigned, so for short strings the '- 2' wraps |
86 | 117 | // around and as a result the entire string is searched, which is fine. |
src/utilities.h | ||
---|---|---|
49 | 49 | /** Returns the contents of the given file as a string. */ |
50 | 50 | std::string readFileAsString(std::string const& filename); |
51 | 51 | |
52 | /** | |
53 | * Writes the given string to a file. | |
54 | * The update is done atomically but not durably; if you need durability | |
55 | * when fsync() the parent directory afterwards. | |
56 | * @return True iff the file was written successfully. | |
57 | */ | |
58 | bool writeStringToFile(std::string const& filename, std::string const& data); | |
59 | ||
52 | 60 | std::string strreplace(std::string orig, const std::string &search, const std::string &replace); |
53 | 61 | std::string cmdclean(std::string cmdline); |
54 | 62 |
Branches:
install_locations
master
opkrun
packages