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:
parent
f8591ee1b6
commit
03532f0ae0
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue