diff options
author | Lennart Poettering <lennart@poettering.net> | 2022-09-13 11:34:18 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2022-09-23 09:27:53 +0200 |
commit | 9e430ce3d4335541ac54658625483d282d0a1998 (patch) | |
tree | 14774655faaba40d8aa355fd8672c35c9308134c | |
parent | 8f6fb95cd069884f4ce0a24eb20efc821ae3bc5e (diff) | |
download | systemd-9e430ce3d4335541ac54658625483d282d0a1998.tar.gz |
tmpfiles: move symlink creation into its own function, and modernize
Let's ensure it also operates based on O_PATH, like fifo/device node/…
creation.
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 139 |
1 files changed, 94 insertions, 45 deletions
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index a354f3289c..8442cce5bc 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -2005,6 +2005,95 @@ static int create_fifo(Item *i) { return fd_set_perms(i, fd, i->path, &st, creation); } +static int create_symlink(Item *i) { + _cleanup_close_ int pfd = -1, fd = -1; + _cleanup_free_ char *bn = NULL; + CreationMode creation; + struct stat st; + bool good = false; + int r; + + assert(i); + + r = path_extract_filename(i->path, &bn); + if (r < 0) + return log_error_errno(r, "Failed to extract filename from path '%s': %m", i->path); + if (r == O_DIRECTORY) + return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Cannot open path '%s' for creating FIFO, is a directory.", i->path); + + pfd = path_open_parent_safe(i->path); + if (pfd < 0) + return pfd; + + mac_selinux_create_file_prepare(i->path, S_IFLNK); + r = RET_NERRNO(symlinkat(i->argument, pfd, bn)); + mac_selinux_create_file_clear(); + + creation = r >= 0 ? CREATION_NORMAL : CREATION_EXISTING; + + fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (fd < 0) { + if (r < 0) + return log_error_errno(r, "Failed to create symlink '%s': %m", i->path); /* original error! */ + + return log_error_errno(errno, "Failed to open symlink we just created '%s': %m", i->path); + } + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to fstat(%s): %m", i->path); + + if (S_ISLNK(st.st_mode)) { + _cleanup_free_ char *x = NULL; + + r = readlinkat_malloc(fd, "", &x); + if (r < 0) + return log_error_errno(r, "readlinkat(%s) failed: %m", i->path); + + good = streq(x, i->argument); + } else + good = false; + + if (!good) { + if (!i->append_or_force) { + log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path); + return 0; + } + + fd = safe_close(fd); + + mac_selinux_create_file_prepare(i->path, S_IFLNK); + r = symlinkat_atomic_full(i->argument, pfd, bn, /* make_relative= */ false); + mac_selinux_create_file_clear(); + if (IN_SET(r, -EISDIR, -EEXIST, -ENOTEMPTY)) { + r = rm_rf_child(pfd, bn, REMOVE_PHYSICAL); + if (r < 0) + return log_error_errno(r, "rm -rf %s failed: %m", i->path); + + mac_selinux_create_file_prepare(i->path, S_IFLNK); + r = RET_NERRNO(symlinkat(i->argument, pfd, i->path)); + mac_selinux_create_file_clear(); + } + if (r < 0) + return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path); + + fd = openat(pfd, bn, O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (fd < 0) + return log_error_errno(errno, "Failed to open symlink we just created '%s': %m", i->path); + + /* Validate type before change ownership below */ + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to fstat(%s): %m", i->path); + + if (!S_ISLNK(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Symlink we just created is not a symlink, refusing."); + + creation = CREATION_FORCE; + } + + log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path); + return fd_set_perms(i, fd, i->path, &st, creation); +} + typedef int (*action_t)(Item *i, const char *path, CreationMode creation); typedef int (*fdaction_t)(Item *i, int fd, const char *path, const struct stat *st, CreationMode creation); @@ -2303,8 +2392,7 @@ static int mkdir_parents_item(Item *i, mode_t child_mode) { } static int create_item(Item *i) { - CreationMode creation; - int r = 0; + int r; assert(i); @@ -2328,7 +2416,6 @@ static int create_item(Item *i) { r = truncate_file(i, i->path); else r = create_file(i, i->path); - if (r < 0) return r; break; @@ -2389,54 +2476,16 @@ static int create_item(Item *i) { return r; break; - case CREATE_SYMLINK: { + case CREATE_SYMLINK: r = mkdir_parents_item(i, S_IFLNK); if (r < 0) return r; - mac_selinux_create_file_prepare(i->path, S_IFLNK); - r = RET_NERRNO(symlink(i->argument, i->path)); - mac_selinux_create_file_clear(); - - if (r < 0) { - _cleanup_free_ char *x = NULL; - - if (r != -EEXIST) - return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path); - - r = readlink_malloc(i->path, &x); - if (r < 0 || !streq(i->argument, x)) { - - if (i->append_or_force) { - mac_selinux_create_file_prepare(i->path, S_IFLNK); - r = symlink_atomic(i->argument, i->path); - mac_selinux_create_file_clear(); - - if (IN_SET(r, -EISDIR, -EEXIST, -ENOTEMPTY)) { - r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL); - if (r < 0) - return log_error_errno(r, "rm -fr %s failed: %m", i->path); - - mac_selinux_create_file_prepare(i->path, S_IFLNK); - r = RET_NERRNO(symlink(i->argument, i->path)); - mac_selinux_create_file_clear(); - } - if (r < 0) - return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path); - - creation = CREATION_FORCE; - } else { - log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path); - return 0; - } - } else - creation = CREATION_EXISTING; - } else + r = create_symlink(i); + if (r < 0) + return r; - creation = CREATION_NORMAL; - log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path); break; - } case CREATE_BLOCK_DEVICE: case CREATE_CHAR_DEVICE: |