From: Lennart Poettering Date: Tue, 13 Jun 2023 07:45:39 +0000 (+0200) Subject: tmpfile-util: add new LINK_TMPFILE_SYNC flag for syncing properly before/after linkin... X-Git-Tag: v254-rc1~227^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ce67bf366fd29008370ebf4d95e7ec6d8f47e353;p=thirdparty%2Fsystemd.git tmpfile-util: add new LINK_TMPFILE_SYNC flag for syncing properly before/after linking in the file This syncs the data before linking it in, and both data + dir once done. This should give us proper semantics for installing files safely into the fs. --- diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c index fdc66c5b5e2..e77ca942489 100644 --- a/src/basic/tmpfile-util.c +++ b/src/basic/tmpfile-util.c @@ -17,6 +17,7 @@ #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" +#include "sync-util.h" #include "tmpfile-util.h" #include "umask-util.h" @@ -361,34 +362,46 @@ int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, Li * an fd created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE * is not supported on the directory, and renameat2() is used instead. */ + if (FLAGS_SET(flags, LINK_TMPFILE_SYNC) && fsync(fd) < 0) + return -errno; + if (path) { if (FLAGS_SET(flags, LINK_TMPFILE_REPLACE)) - return RET_NERRNO(renameat(dir_fd, path, dir_fd, target)); + r = RET_NERRNO(renameat(dir_fd, path, dir_fd, target)); + else + r = rename_noreplace(dir_fd, path, dir_fd, target); + if (r < 0) + return r; + } else { - return rename_noreplace(dir_fd, path, dir_fd, target); - } + r = link_fd(fd, dir_fd, target); + if (r != -EEXIST || !FLAGS_SET(flags, LINK_TMPFILE_REPLACE)) + return r; - r = link_fd(fd, dir_fd, target); - if (r != -EEXIST || !FLAGS_SET(flags, LINK_TMPFILE_REPLACE)) - return r; + /* So the target already exists and we were asked to replace it. That sucks a bit, since the kernel's + * linkat() logic does not allow that. We work-around this by linking the file to a random name + * first, and then renaming that to the final name. This reintroduces the race O_TMPFILE kinda is + * trying to fix, but at least the vulnerability window (i.e. where the file is linked into the file + * system under a temporary name) is very short. */ - /* So the target already exists and we were asked to replace it. That sucks a bit, since the kernel's - * linkat() logic does not allow that. We work-around this by linking the file to a random name - * first, and then renaming that to the final name. This reintroduces the race O_TMPFILE kinda is - * trying to fix, but at least the vulnerability window (i.e. where the file is linked into the file - * system under a temporary name) is very short. */ + r = tempfn_random(target, NULL, &tmp); + if (r < 0) + return r; - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return r; + if (link_fd(fd, dir_fd, tmp) < 0) + return -EEXIST; /* propagate original error */ - if (link_fd(fd, dir_fd, tmp) < 0) - return -EEXIST; /* propagate original error */ + r = RET_NERRNO(renameat(dir_fd, tmp, dir_fd, target)); + if (r < 0) { + (void) unlinkat(dir_fd, tmp, 0); + return r; + } + } - r = RET_NERRNO(renameat(dir_fd, tmp, dir_fd, target)); - if (r < 0) { - (void) unlinkat(dir_fd, tmp, 0); - return r; + if (FLAGS_SET(flags, LINK_TMPFILE_SYNC)) { + r = fsync_full(fd); + if (r < 0) + return r; } return 0; @@ -404,7 +417,7 @@ int flink_tmpfile(FILE *f, const char *path, const char *target, LinkTmpfileFlag if (fd < 0) /* Not all FILE* objects encapsulate fds */ return -EBADF; - r = fflush_sync_and_check(f); + r = fflush_and_check(f); if (r < 0) return r; diff --git a/src/basic/tmpfile-util.h b/src/basic/tmpfile-util.h index 44e9b75f221..50904ecac10 100644 --- a/src/basic/tmpfile-util.h +++ b/src/basic/tmpfile-util.h @@ -32,6 +32,7 @@ int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE typedef enum LinkTmpfileFlags { LINK_TMPFILE_REPLACE = 1 << 0, + LINK_TMPFILE_SYNC = 1 << 1, } LinkTmpfileFlags; int link_tmpfile_at(int fd, int dir_fd, const char *path, const char *target, LinkTmpfileFlags flags);