From 39c38d773fbe2b4f76ff29ecd3344640efb9a86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 11 May 2017 10:23:36 -0400 Subject: [PATCH 1/2] basic/fileio: extend atomic file writing with timestamp setting There should be no functional change. --- src/basic/fileio-label.c | 4 ++-- src/basic/fileio-label.h | 5 ++++- src/basic/fileio.c | 16 ++++++++++++---- src/basic/fileio.h | 10 ++++++++-- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c index 66dbc0fe1e6..ef51c493955 100644 --- a/src/basic/fileio-label.c +++ b/src/basic/fileio-label.c @@ -24,14 +24,14 @@ #include "fileio.h" #include "selinux-util.h" -int write_string_file_atomic_label(const char *fn, const char *line) { +int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts) { int r; r = mac_selinux_create_file_prepare(fn, S_IFREG); if (r < 0) return r; - r = write_string_file(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + r = write_string_file_ts(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC, ts); mac_selinux_create_file_clear(); diff --git a/src/basic/fileio-label.h b/src/basic/fileio-label.h index fe7543013d1..9854ea50b9d 100644 --- a/src/basic/fileio-label.h +++ b/src/basic/fileio-label.h @@ -24,7 +24,10 @@ #include "fileio.h" -int write_string_file_atomic_label(const char *fn, const char *line); +int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts); +static inline int write_string_file_atomic_label(const char *fn, const char *line) { + return write_string_file_atomic_label_ts(fn, line, NULL); +} int write_env_file_label(const char *fname, char **l); int fopen_temporary_label(const char *target, const char *path, FILE **f, char **temp_path); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 7c2c2b38f51..9a185e3e609 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -51,7 +51,7 @@ #define READ_FULL_BYTES_MAX (4U*1024U*1024U) -int write_string_stream(FILE *f, const char *line, bool enforce_newline) { +int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts) { assert(f); assert(line); @@ -60,6 +60,13 @@ int write_string_stream(FILE *f, const char *line, bool enforce_newline) { if (enforce_newline && !endswith(line, "\n")) fputc('\n', f); + if (ts) { + struct timespec twice[2] = {*ts, *ts}; + + if (futimens(fileno(f), twice) < 0) + return -errno; + } + return fflush_and_check(f); } @@ -89,7 +96,7 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor return r; } -int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { +int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts) { _cleanup_fclose_ FILE *f = NULL; int q, r; @@ -104,7 +111,8 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla goto fail; return r; - } + } else + assert(ts == NULL); if (flags & WRITE_STRING_FILE_CREATE) { f = fopen(fn, "we"); @@ -131,7 +139,7 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla } } - r = write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_stream_ts(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE), ts); if (r < 0) goto fail; diff --git a/src/basic/fileio.h b/src/basic/fileio.h index e547614cc48..60985622650 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -35,8 +35,14 @@ typedef enum { WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8, } WriteStringFileFlags; -int write_string_stream(FILE *f, const char *line, bool enforce_newline); -int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags); +int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts); +static inline int write_string_stream(FILE *f, const char *line, bool enforce_newline) { + return write_string_stream_ts(f, line, enforce_newline, NULL); +} +int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts); +static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { + return write_string_file_ts(fn, line, flags, NULL); +} int read_one_line_file(const char *fn, char **line); int read_full_file(const char *fn, char **contents, size_t *size); From 872c40396384f9fa89b01baf4e739d28ed590299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 11 May 2017 10:29:34 -0400 Subject: [PATCH 2/2] update-done: use newly added library function to write the file Fixes #5861. --- src/update-done/update-done.c | 66 +++++++---------------------------- 1 file changed, 12 insertions(+), 54 deletions(-) diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index 06e2d7b71b3..ec467f1953a 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -18,10 +18,7 @@ ***/ #include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" +#include "fileio-label.h" #include "selinux-util.h" #include "util.h" @@ -31,64 +28,25 @@ "# was updated. See man:systemd-update-done.service(8).\n" static int apply_timestamp(const char *path, struct timespec *ts) { - struct timespec twice[2] = { - *ts, - *ts - }; - _cleanup_fclose_ FILE *f = NULL; - int fd = -1; + _cleanup_free_ char *message = NULL; int r; - _cleanup_(unlink_and_freep) char *tmp = NULL; - - assert(path); - assert(ts); /* * We store the timestamp both as mtime of the file and in the file itself, * to support filesystems which cannot store nanosecond-precision timestamps. - * Hence, don't bother updating the file, let's just rewrite it. */ - r = mac_selinux_create_file_prepare(path, S_IFREG); + if (asprintf(&message, + MESSAGE + "TIMESTAMP_NSEC=" NSEC_FMT "\n", + timespec_load_nsec(ts)) < 0) + return log_oom(); + + r = write_string_file_atomic_label_ts(path, message, ts); + if (r == -EROFS) + return log_debug("Cannot create \"%s\", file system is read-only.", path); if (r < 0) - return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); - - 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 temporary timestamp file %s, file system is read-only.", tmp); - - 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", tmp); - } - - (void) fprintf(f, - MESSAGE - "TIMESTAMP_NSEC=" NSEC_FMT "\n", - timespec_load_nsec(ts)); - - r = fflush_and_check(f); - if (r < 0) - 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", 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 log_error_errno(r, "Failed to write \"%s\": %m", path); return 0; }