summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-04-20 19:27:32 +0200
committerLennart Poettering <lennart@poettering.net>2016-04-22 16:16:53 +0200
commit03532f0ae0fae40f9f04091340e2bf156d0ec21a (patch)
tree52c0ad443ed1cbec01971d9284ca47ac276f00e9
parentf8591ee1b6c152055feed7a872159e67859fd83d (diff)
coredump,basic: generalize O_TMPFILE handling a bit
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
-rw-r--r--src/basic/fileio.c124
-rw-r--r--src/basic/fileio.h6
-rw-r--r--src/core/manager.c2
-rw-r--r--src/coredump/coredump.c54
-rw-r--r--src/journal-remote/journal-gatewayd.c4
-rw-r--r--src/journal/journal-send.c2
-rw-r--r--src/journal/journal-verify.c6
-rw-r--r--src/test/test-tmpfiles.c26
8 files changed, 146 insertions, 78 deletions
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 69590941e5..2a9b6e46ad 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -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;
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 8084895ff3..58dbc80c24 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -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);
diff --git a/src/core/manager.c b/src/core/manager.c
index 5601770670..bd00c224f4 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -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;
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 2bbb958861..41fc1993d5 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -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) {
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 60d897758b..4ad9184993 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -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;
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index a79846146a..f0959b6237 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -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;
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index a1241c9bcf..26572ddd76 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -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");
diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c
index d7223dd2bf..8fda0904f3 100644
--- a/src/test/test-tmpfiles.c
+++ b/src/test/test-tmpfiles.c
@@ -32,15 +32,17 @@
#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;
}