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
This commit is contained in:
Lennart Poettering 2016-04-20 19:27:32 +02:00
parent f8591ee1b6
commit 03532f0ae0
8 changed files with 146 additions and 78 deletions

View 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;
}

View 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);

View 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;

View 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) {

View 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;

View 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;

View 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");

View file

@ -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;
}