diff options
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/fd-util.c')
-rw-r--r-- | src/libnm-systemd-shared/src/basic/fd-util.c | 118 |
1 files changed, 86 insertions, 32 deletions
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index cee20a9a81..4d6d01cd99 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -29,7 +29,6 @@ #include "stat-util.h" #include "stdio-util.h" #include "tmpfile-util.h" -#include "util.h" /* The maximum number of iterations in the loop to close descriptors in the fallback case * when /proc/self/fd/ is inaccessible. */ @@ -57,11 +56,9 @@ int close_nointr(int fd) { } int safe_close(int fd) { - /* - * Like close_nointr() but cannot fail. Guarantees errno is - * unchanged. Is a NOP with negative fds passed, and returns - * -1, so that it can be used in this syntax: + * Like close_nointr() but cannot fail. Guarantees errno is unchanged. Is a noop for negative fds, + * and returns -EBADF, so that it can be used in this syntax: * * fd = safe_close(fd); */ @@ -77,7 +74,7 @@ int safe_close(int fd) { assert_se(close_nointr(fd) != -EBADF); } - return -1; + return -EBADF; } void safe_close_pair(int p[static 2]) { @@ -174,12 +171,35 @@ int fd_cloexec(int fd, bool cloexec) { return RET_NERRNO(fcntl(fd, F_SETFD, nflags)); } +int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) { + int ret = 0, r; + + assert(n_fds == 0 || fds); + + for (size_t i = 0; i < n_fds; i++) { + if (fds[i] < 0) /* Skip gracefully over already invalidated fds */ + continue; + + r = fd_cloexec(fds[i], cloexec); + if (r < 0 && ret >= 0) /* Continue going, but return first error */ + ret = r; + else + ret = 1; /* report if we did anything */ + } + + return ret; +} + _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { assert(n_fdset == 0 || fdset); - for (size_t i = 0; i < n_fdset; i++) + for (size_t i = 0; i < n_fdset; i++) { + if (fdset[i] < 0) + continue; + if (fdset[i] == fd) return true; + } return false; } @@ -226,7 +246,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) { "Refusing to loop over %d potential fds.", max_fd); - for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { + for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) { int q; if (fd_in_set(fd, except, n_except)) @@ -252,6 +272,10 @@ static int close_all_fds_special_case(const int except[], size_t n_except) { if (!have_close_range) return 0; + if (n_except == 1 && except[0] < 0) /* Minor optimization: if we only got one fd, and it's invalid, + * we got none */ + n_except = 0; + switch (n_except) { case 0: @@ -386,7 +410,7 @@ int close_all_fds(const int except[], size_t n_except) { return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */ FOREACH_DIRENT(de, d, return -errno) { - int fd = -1, q; + int fd = -EBADF, q; if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) continue; @@ -475,7 +499,8 @@ void cmsg_close_all(struct msghdr *mh) { CMSG_FOREACH(cmsg, mh) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); + close_many(CMSG_TYPED_DATA(cmsg, int), + (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); } bool fdname_is_valid(const char *s) { @@ -604,25 +629,23 @@ int fd_move_above_stdio(int fd) { } int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) { - - int fd[3] = { /* Put together an array of fds we work on */ - original_input_fd, - original_output_fd, - original_error_fd - }; - - int r, i, - null_fd = -1, /* if we open /dev/null, we store the fd to it here */ - copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */ + int fd[3] = { original_input_fd, /* Put together an array of fds we work on */ + original_output_fd, + original_error_fd }, + null_fd = -EBADF, /* If we open /dev/null, we store the fd to it here */ + copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here + * temporarily, and hence need to close at the end. */ + r; bool null_readable, null_writable; - /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is - * specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as - * itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be - * on. + /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors + * is specified as -EBADF it will be connected with /dev/null instead. If any of the file descriptors + * is passed as itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is + * turned off should it be on. * - * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on - * failure! Thus, callers should assume that when this function returns the input fds are invalidated. + * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and + * on failure! Thus, callers should assume that when this function returns the input fds are + * invalidated. * * Note that when this function fails stdin/stdout/stderr might remain half set up! * @@ -658,7 +681,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_ } /* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */ - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { if (fd[i] < 0) fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */ @@ -674,10 +697,10 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_ } } - /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we - * have freedom to move them around. If the fds already were at the right places then the specific fds are - * -1. Let's now move them to the right places. This is the point of no return. */ - for (i = 0; i < 3; i++) { + /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that + * we have freedom to move them around. If the fds already were at the right places then the specific + * fds are -EBADF. Let's now move them to the right places. This is the point of no return. */ + for (int i = 0; i < 3; i++) { if (fd[i] == i) { @@ -708,7 +731,7 @@ finish: safe_close_above_stdio(original_error_fd); /* Close the copies we moved > 2 */ - for (i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) safe_close(copy_fd[i]); /* Close our null fd, if it's > 2 */ @@ -754,6 +777,37 @@ int fd_reopen(int fd, int flags) { return new_fd; } +int fd_reopen_condition( + int fd, + int flags, + int mask, + int *ret_new_fd) { + + int r, new_fd; + + assert(fd >= 0); + + /* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified + * flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if + * needed, but only then. */ + + r = fcntl(fd, F_GETFL); + if (r < 0) + return -errno; + + if ((r & mask) == (flags & mask)) { + *ret_new_fd = -EBADF; + return fd; + } + + new_fd = fd_reopen(fd, flags); + if (new_fd < 0) + return new_fd; + + *ret_new_fd = new_fd; + return new_fd; +} + int read_nr_open(void) { _cleanup_free_ char *nr_open = NULL; int r; |