]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
coredump,basic: generalize O_TMPFILE handling a bit
authorLennart Poettering <lennart@poettering.net>
Wed, 20 Apr 2016 17:27:32 +0000 (19:27 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 22 Apr 2016 14:16:53 +0000 (16:16 +0200)
This moves the O_TMPFILE handling from the coredumping code into common library
code, and generalizes it as open_tmpfile_linkable() + link_tmpfile(). The
existing open_tmpfile() function (which creates an unlinked temporary file that
cannot be linked into the fs) is renamed to open_tmpfile_unlinkable(), to make
the distinction clear. Thus, code may now choose between:

 a) open_tmpfile_linkable() + link_tmpfile()
 b) open_tmpfile_unlinkable()

Depending on whether they want a file that may be linked back into the fs later
on or not.

In a later commit we should probably convert fopen_temporary() to make use of
open_tmpfile_linkable().

Followup for: #3065

src/basic/fileio.c
src/basic/fileio.h
src/core/manager.c
src/coredump/coredump.c
src/journal-remote/journal-gatewayd.c
src/journal/journal-send.c
src/journal/journal-verify.c
src/test/test-tmpfiles.c

index 69590941e55e5b02f85bd29b6710f6a6ca4ee1b6..2a9b6e46ad84098306ea33e344385e8d2b820c6e 100644 (file)
@@ -1083,30 +1083,6 @@ int mkostemp_safe(char *pattern, int flags) {
         return fd;
 }
 
-int open_tmpfile(const char *path, int flags) {
-        char *p;
-        int fd;
-
-        assert(path);
-
-#ifdef O_TMPFILE
-        /* Try O_TMPFILE first, if it is supported */
-        fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
-        if (fd >= 0)
-                return fd;
-#endif
-
-        /* Fall back to unguessable name + unlinking */
-        p = strjoina(path, "/systemd-tmp-XXXXXX");
-
-        fd = mkostemp_safe(p, flags);
-        if (fd < 0)
-                return fd;
-
-        unlink(p);
-        return fd;
-}
-
 int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
         const char *fn;
         char *t;
@@ -1278,3 +1254,103 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
 
         return fputs(s, f);
 }
+
+int open_tmpfile_unlinkable(const char *directory, int flags) {
+        char *p;
+        int fd;
+
+        assert(directory);
+
+        /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
+
+#ifdef O_TMPFILE
+        /* Try O_TMPFILE first, if it is supported */
+        fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
+        if (fd >= 0)
+                return fd;
+#endif
+
+        /* Fall back to unguessable name + unlinking */
+        p = strjoina(directory, "/systemd-tmp-XXXXXX");
+
+        fd = mkostemp_safe(p, flags);
+        if (fd < 0)
+                return fd;
+
+        (void) unlink(p);
+
+        return fd;
+}
+
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
+        _cleanup_free_ char *tmp = NULL;
+        int r, fd;
+
+        assert(target);
+        assert(ret_path);
+
+        /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
+        assert((flags & O_EXCL) == 0);
+
+        /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
+         * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
+         * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
+
+#ifdef O_TMPFILE
+        {
+                _cleanup_free_ char *dn = NULL;
+
+                dn = dirname_malloc(target);
+                if (!dn)
+                        return -ENOMEM;
+
+                fd = open(dn, O_TMPFILE|flags, 0640);
+                if (fd >= 0) {
+                        *ret_path = NULL;
+                        return fd;
+                }
+
+                log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
+        }
+#endif
+
+        r = tempfn_random(target, NULL, &tmp);
+        if (r < 0)
+                return r;
+
+        fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
+        if (fd < 0)
+                return -errno;
+
+        *ret_path = tmp;
+        tmp = NULL;
+
+        return fd;
+}
+
+int link_tmpfile(int fd, const char *path, const char *target) {
+
+        assert(fd >= 0);
+        assert(target);
+
+        /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL 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.
+         *
+         * Note that in both cases we will not replace existing files. This is because linkat() dos not support this
+         * operation currently (renameat2() does), and there is no nice way to emulate this. */
+
+        if (path) {
+                if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0)
+                        return -errno;
+        } else {
+                char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
+
+                xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
+
+                if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
+                        return -errno;
+        }
+
+        return 0;
+}
index 8084895ff32a1ee22205e7356a37edbe1265594a..58dbc80c24718f9756e8cc38f43d83b399419d1b 100644 (file)
@@ -72,7 +72,6 @@ int fflush_and_check(FILE *f);
 
 int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
 int mkostemp_safe(char *pattern, int flags);
-int open_tmpfile(const char *path, int flags);
 
 int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
 int tempfn_random(const char *p, const char *extra, char **ret);
@@ -82,3 +81,8 @@ int write_timestamp_file_atomic(const char *fn, usec_t n);
 int read_timestamp_file(const char *fn, usec_t *ret);
 
 int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
+
+int open_tmpfile_unlinkable(const char *directory, int flags);
+int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+
+int link_tmpfile(int fd, const char *path, const char *target);
index 5601770670bbec9644f8cfe28e573571472a8058..bd00c224f4a49af956353fc1bbd7d1c461200f20 100644 (file)
@@ -2191,7 +2191,7 @@ int manager_open_serialization(Manager *m, FILE **_f) {
         assert(_f);
 
         path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp";
-        fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
+        fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
index 2bbb95886191f2083853de842786aeba1471948c..41fc1993d5ef36f243a81373ae7e00ee3c16590e 100644 (file)
@@ -224,6 +224,8 @@ static int fix_permissions(
                 const char *context[_CONTEXT_MAX],
                 uid_t uid) {
 
+        int r;
+
         assert(fd >= 0);
         assert(target);
         assert(context);
@@ -236,18 +238,9 @@ static int fix_permissions(
         if (fsync(fd) < 0)
                 return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename));
 
-        if (filename) {
-                if (rename(filename, target) < 0)
-                        return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target);
-        } else {
-                _cleanup_free_ char *proc_fd_path = NULL;
-
-                if (asprintf(&proc_fd_path, "/proc/self/fd/%d", fd) < 0)
-                        return log_oom();
-
-                if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
-                        return log_error_errno(errno, "Failed to create coredump %s: %m", target);
-        }
+        r = link_tmpfile(fd, filename, target);
+        if (r < 0)
+                return log_error_errno(r, "Failed to move coredump %s into place: %m", target);
 
         return 0;
 }
@@ -308,33 +301,6 @@ static int make_filename(const char *context[_CONTEXT_MAX], char **ret) {
         return 0;
 }
 
-static int open_coredump_tmpfile(const char *target, char **ret_filename) {
-        _cleanup_free_ char *tmp = NULL;
-        int fd;
-        int r;
-
-        assert(target);
-        assert(ret_filename);
-
-        fd = open("/var/lib/systemd/coredump", O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0640);
-        if (fd < 0) {
-                log_debug_errno(errno, "Failed to use O_TMPFILE: %m");
-
-                r = tempfn_random(target, NULL, &tmp);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to determine temporary file name: %m");
-
-                fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640);
-                if (fd < 0)
-                        return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp);
-        }
-
-        *ret_filename = tmp;
-        tmp = NULL;
-
-        return fd;
-}
-
 static int save_external_coredump(
                 const char *context[_CONTEXT_MAX],
                 int input_fd,
@@ -378,9 +344,9 @@ static int save_external_coredump(
 
         mkdir_p_label("/var/lib/systemd/coredump", 0755);
 
-        fd = open_coredump_tmpfile(fn, &tmp);
+        fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp);
         if (fd < 0)
-                return fd;
+                return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
 
         r = copy_bytes(input_fd, fd, max_size, false);
         if (r == -EFBIG) {
@@ -418,9 +384,11 @@ static int save_external_coredump(
                         goto uncompressed;
                 }
 
-                fd_compressed = open_coredump_tmpfile(fn_compressed, &tmp_compressed);
-                if (fd_compressed < 0)
+                fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
+                if (fd_compressed < 0) {
+                        log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
                         goto uncompressed;
+                }
 
                 r = compress_stream(fd, fd_compressed, -1);
                 if (r < 0) {
index 60d897758be8bcefd085327de9e3cef2afdb47a9..4ad9184993c6352a6adb3e4895f4135309da0446 100644 (file)
@@ -122,12 +122,14 @@ static int open_journal(RequestMeta *m) {
 }
 
 static int request_meta_ensure_tmp(RequestMeta *m) {
+        assert(m);
+
         if (m->tmp)
                 rewind(m->tmp);
         else {
                 int fd;
 
-                fd = open_tmpfile("/tmp", O_RDWR|O_CLOEXEC);
+                fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC);
                 if (fd < 0)
                         return fd;
 
index a79846146a3c9175a972f7e94564e778dbad893d..f0959b623761e291dab3dcd4a063aa14d70ba390 100644 (file)
@@ -316,7 +316,7 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
         buffer_fd = memfd_new(NULL);
         if (buffer_fd < 0) {
                 if (buffer_fd == -ENOSYS) {
-                        buffer_fd = open_tmpfile("/dev/shm", O_RDWR | O_CLOEXEC);
+                        buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC);
                         if (buffer_fd < 0)
                                 return buffer_fd;
 
index a1241c9bcf0a2efdf6a1303a7f7762161838384f..26572ddd76931319c0fe561f9d370d118f54b06e 100644 (file)
@@ -838,19 +838,19 @@ int journal_file_verify(
         } else if (f->seal)
                 return -ENOKEY;
 
-        data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+        data_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
         if (data_fd < 0) {
                 r = log_error_errno(data_fd, "Failed to create data file: %m");
                 goto fail;
         }
 
-        entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+        entry_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
         if (entry_fd < 0) {
                 r = log_error_errno(entry_fd, "Failed to create entry file: %m");
                 goto fail;
         }
 
-        entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
+        entry_array_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC);
         if (entry_array_fd < 0) {
                 r = log_error_errno(entry_array_fd,
                                     "Failed to create entry array file: %m");
index d7223dd2bfa056dedab7b78bd2e0e5cb540257b5..8fda0904f305c98a4410d4208632e449116cf1fa 100644 (file)
 #include "util.h"
 
 int main(int argc, char** argv) {
+        _cleanup_free_ char *cmd = NULL, *cmd2 = NULL, *ans = NULL, *ans2 = NULL, *d = NULL, *tmp = NULL, *line = NULL;
+        _cleanup_close_ int fd = -1, fd2 = -1, fd3 = -1;
         const char *p = argv[1] ?: "/tmp";
-        char *pattern = strjoina(p, "/systemd-test-XXXXXX");
-        _cleanup_close_ int fd, fd2;
-        _cleanup_free_ char *cmd, *cmd2, *ans, *ans2;
+        char *pattern;
 
         log_set_max_level(LOG_DEBUG);
         log_parse_environment();
 
-        fd = open_tmpfile(p, O_RDWR|O_CLOEXEC);
+        pattern = strjoina(p, "/systemd-test-XXXXXX");
+
+        fd = open_tmpfile_unlinkable(p, O_RDWR|O_CLOEXEC);
         assert_se(fd >= 0);
 
         assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0);
@@ -59,5 +61,21 @@ int main(int argc, char** argv) {
         log_debug("link2: %s", ans2);
         assert_se(endswith(ans2, " (deleted)"));
 
+        pattern = strjoina(p, "/tmpfiles-test");
+        assert_se(tempfn_random(pattern, NULL, &d) >= 0);
+
+        fd = open_tmpfile_linkable(d, O_RDWR|O_CLOEXEC, &tmp);
+        assert_se(fd >= 0);
+        assert_se(write(fd, "foobar\n", 7) == 7);
+
+        assert_se(touch(d) >= 0);
+        assert_se(link_tmpfile(fd, tmp, d) == -EEXIST);
+        assert_se(unlink(d) >= 0);
+        assert_se(link_tmpfile(fd, tmp, d) >= 0);
+
+        assert_se(read_one_line_file(d, &line) >= 0);
+        assert_se(streq(line, "foobar"));
+        assert_se(unlink(d) >= 0);
+
         return 0;
 }