update-done: Create using a temporary file (#5789)

'/etc/.updated' is created without using a temporary file, this can be
problematic with filesystems that cache writes. Modify so that the
timestamp is written to a temporary file and then use an atomic move
to move it to its correct place.
This commit is contained in:
codekipper 2017-04-27 01:49:06 +02:00 committed by Zbigniew Jędrzejewski-Szmek
parent 6385cb31ef
commit 5a1d67639d

View file

@ -17,8 +17,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
#include "selinux-util.h"
#include "util.h"
@ -36,6 +38,7 @@ static int apply_timestamp(const char *path, struct timespec *ts) {
_cleanup_fclose_ FILE *f = NULL;
int fd = -1;
int r;
_cleanup_(unlink_and_freep) char *tmp = NULL;
assert(path);
assert(ts);
@ -50,20 +53,20 @@ static int apply_timestamp(const char *path, struct timespec *ts) {
if (r < 0)
return log_error_errno(r, "Failed to set SELinux context for %s: %m", path);
fd = open(path, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
fd = open_tmpfile_linkable(path, O_WRONLY|O_CLOEXEC, &tmp);
mac_selinux_create_file_clear();
if (fd < 0) {
if (errno == EROFS)
return log_debug("Can't create timestamp file %s, file system is read-only.", path);
return log_debug("Can't create temporary timestamp file %s, file system is read-only.", tmp);
return log_error_errno(errno, "Failed to create/open timestamp file %s: %m", path);
return log_error_errno(errno, "Failed to create/open temporary timestamp file %s: %m", tmp);
}
f = fdopen(fd, "we");
if (!f) {
safe_close(fd);
return log_error_errno(errno, "Failed to fdopen() timestamp file %s: %m", path);
return log_error_errno(errno, "Failed to fdopen() timestamp file %s: %m", tmp);
}
(void) fprintf(f,
@ -76,7 +79,15 @@ static int apply_timestamp(const char *path, struct timespec *ts) {
return log_error_errno(r, "Failed to write timestamp file: %m");
if (futimens(fd, twice) < 0)
return log_error_errno(errno, "Failed to update timestamp on %s: %m", path);
return log_error_errno(errno, "Failed to update timestamp on %s: %m", tmp);
/* fix permissions */
(void) fchmod(fd, 0644);
r = link_tmpfile(fd, tmp, path);
if (r < 0)
return log_error_errno(r, "Failed to move \"%s\" to \"%s\": %m", tmp, path);
tmp = mfree(tmp);
return 0;
}