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 | 183 |
1 files changed, 111 insertions, 72 deletions
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index a0e2f4eb8c..afdc05f2ab 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -94,11 +94,25 @@ void safe_close_pair(int p[static 2]) { p[1] = safe_close(p[1]); } -void close_many(const int fds[], size_t n_fd) { - assert(fds || n_fd <= 0); +void close_many(const int fds[], size_t n_fds) { + assert(fds || n_fds == 0); - for (size_t i = 0; i < n_fd; i++) - safe_close(fds[i]); + FOREACH_ARRAY(fd, fds, n_fds) + safe_close(*fd); +} + +void close_many_unset(int fds[], size_t n_fds) { + assert(fds || n_fds == 0); + + FOREACH_ARRAY(fd, fds, n_fds) + *fd = safe_close(*fd); +} + +void close_many_and_free(int *fds, size_t n_fds) { + assert(fds || n_fds == 0); + + close_many(fds, n_fds); + free(fds); } int fclose_nointr(FILE *f) { @@ -158,6 +172,19 @@ int fd_nonblock(int fd, bool nonblock) { return RET_NERRNO(fcntl(fd, F_SETFL, nflags)); } +int stdio_disable_nonblock(void) { + int ret = 0; + + /* stdin/stdout/stderr really should have O_NONBLOCK, which would confuse apps if left on, as + * write()s might unexpectedly fail with EAGAIN. */ + + RET_GATHER(ret, fd_nonblock(STDIN_FILENO, false)); + RET_GATHER(ret, fd_nonblock(STDOUT_FILENO, false)); + RET_GATHER(ret, fd_nonblock(STDERR_FILENO, false)); + + return ret; +} + int fd_cloexec(int fd, bool cloexec) { int flags, nflags; @@ -176,32 +203,32 @@ int fd_cloexec(int fd, bool cloexec) { #if 0 /* NM_IGNORED */ int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) { - int ret = 0, r; + int r = 0; - assert(n_fds == 0 || fds); + assert(fds || n_fds == 0); - for (size_t i = 0; i < n_fds; i++) { - if (fds[i] < 0) /* Skip gracefully over already invalidated fds */ + FOREACH_ARRAY(fd, fds, n_fds) { + if (*fd < 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 */ + RET_GATHER(r, fd_cloexec(*fd, cloexec)); + + if (r >= 0) + r = 1; /* report if we did anything */ } - return ret; + return r; } -_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { - assert(n_fdset == 0 || fdset); +static bool fd_in_set(int fd, const int fds[], size_t n_fds) { + assert(fd >= 0); + assert(fds || n_fds == 0); - for (size_t i = 0; i < n_fdset; i++) { - if (fdset[i] < 0) + FOREACH_ARRAY(i, fds, n_fds) { + if (*i < 0) continue; - if (fdset[i] == fd) + if (*i == fd) return true; } @@ -232,7 +259,7 @@ int get_max_fd(void) { static int close_all_fds_frugal(const int except[], size_t n_except) { int max_fd, r = 0; - assert(n_except == 0 || except); + assert(except || n_except == 0); /* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so * and hence is safe to be called in signal handler context. Most users should call close_all_fds(), @@ -247,8 +274,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) { * spin the CPU for a long time. */ if (max_fd > MAX_FD_LOOP_LIMIT) return log_debug_errno(SYNTHETIC_ERRNO(EPERM), - "Refusing to loop over %d potential fds.", - max_fd); + "Refusing to loop over %d potential fds.", max_fd); for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) { int q; @@ -257,8 +283,8 @@ static int close_all_fds_frugal(const int except[], size_t n_except) { continue; q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) - r = q; + if (q != -EBADF) + RET_GATHER(r, q); } return r; @@ -589,7 +615,7 @@ int move_fd(int from, int to, int cloexec) { if (fl < 0) return -errno; - cloexec = !!(fl & FD_CLOEXEC); + cloexec = FLAGS_SET(fl, FD_CLOEXEC); } r = dup3(from, to, cloexec ? O_CLOEXEC : 0); @@ -647,7 +673,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_ 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 + copy_fd[3] = EBADF_TRIPLET, /* This contains all fds we duplicate here * temporarily, and hence need to close at the end. */ r; bool null_readable, null_writable; @@ -745,8 +771,7 @@ finish: safe_close_above_stdio(original_error_fd); /* Close the copies we moved > 2 */ - for (int i = 0; i < 3; i++) - safe_close(copy_fd[i]); + close_many(copy_fd, 3); /* Close our null fd, if it's > 2 */ safe_close_above_stdio(null_fd); @@ -756,9 +781,10 @@ finish: #endif /* NM_IGNORED */ int fd_reopen(int fd, int flags) { - int new_fd, r; + int r; assert(fd >= 0 || fd == AT_FDCWD); + assert(!FLAGS_SET(flags, O_CREAT)); /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to * turn O_RDWR fds into O_RDONLY fds. @@ -782,19 +808,12 @@ int fd_reopen(int fd, int flags) { * the same way as the non-O_DIRECTORY case. */ return -ELOOP; - if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD) { + if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD) /* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole * magic /proc/ directory, and make ourselves independent of that being mounted. */ - new_fd = openat(fd, ".", flags | O_DIRECTORY); - if (new_fd < 0) - return -errno; - - return new_fd; - } + return RET_NERRNO(openat(fd, ".", flags | O_DIRECTORY)); - assert(fd >= 0); - - new_fd = open(FORMAT_PROC_FD_PATH(fd), flags); + int new_fd = open(FORMAT_PROC_FD_PATH(fd), flags); if (new_fd < 0) { if (errno != ENOENT) return -errno; @@ -811,7 +830,6 @@ int fd_reopen(int fd, int flags) { return new_fd; } -#if 0 /* NM_IGNORED */ int fd_reopen_condition( int fd, int flags, @@ -821,6 +839,7 @@ int fd_reopen_condition( int r, new_fd; assert(fd >= 0); + assert(!FLAGS_SET(flags, O_CREAT)); /* 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 @@ -843,6 +862,7 @@ int fd_reopen_condition( return new_fd; } +#if 0 /* NM_IGNORED */ int fd_is_opath(int fd) { int r; @@ -901,75 +921,86 @@ int fd_get_diskseq(int fd, uint64_t *ret) { } int path_is_root_at(int dir_fd, const char *path) { - STRUCT_NEW_STATX_DEFINE(st); - STRUCT_NEW_STATX_DEFINE(pst); - _cleanup_close_ int fd = -EBADF; - int r; + _cleanup_close_ int fd = -EBADF, pfd = -EBADF; assert(dir_fd >= 0 || dir_fd == AT_FDCWD); if (!isempty(path)) { - fd = openat(dir_fd, path, O_PATH|O_CLOEXEC); + fd = openat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC); if (fd < 0) - return -errno; + return errno == ENOTDIR ? false : -errno; dir_fd = fd; } - r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx); - if (r == -ENOTDIR) - return false; + pfd = openat(dir_fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC); + if (pfd < 0) + return errno == ENOTDIR ? false : -errno; + + /* Even if the parent directory has the same inode, the fd may not point to the root directory "/", + * and we also need to check that the mount ids are the same. Otherwise, a construct like the + * following could be used to trick us: + * + * $ mkdir /tmp/x /tmp/x/y + * $ mount --bind /tmp/x /tmp/x/y + */ + + return fds_are_same_mount(dir_fd, pfd); +} + +int fds_are_same_mount(int fd1, int fd2) { + STRUCT_NEW_STATX_DEFINE(st1); + STRUCT_NEW_STATX_DEFINE(st2); + int r; + + assert(fd1 >= 0); + assert(fd2 >= 0); + + r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx); if (r < 0) return r; - r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx); + r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx); if (r < 0) return r; /* First, compare inode. If these are different, the fd does not point to the root directory "/". */ - if (!statx_inode_same(&st.sx, &pst.sx)) + if (!statx_inode_same(&st1.sx, &st2.sx)) return false; - /* Even if the parent directory has the same inode, the fd may not point to the root directory "/", - * and we also need to check that the mount ids are the same. Otherwise, a construct like the - * following could be used to trick us: - * - * $ mkdir /tmp/x /tmp/x/y - * $ mount --bind /tmp/x /tmp/x/y - * - * Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old - * kernel is used without /proc mounted. In that case, let's assume that we do not have such spurious - * mount points in an early boot stage, and silently skip the following check. */ + /* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old + * kernel is used. In that case, let's assume that we do not have such spurious mount points in an + * early boot stage, and silently skip the following check. */ - if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) { + if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) { int mntid; - r = path_get_mnt_id_at(dir_fd, "", &mntid); - if (r == -ENOSYS) + r = path_get_mnt_id_at_fallback(fd1, "", &mntid); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return true; /* skip the mount ID check */ if (r < 0) return r; assert(mntid >= 0); - st.nsx.stx_mnt_id = mntid; - st.nsx.stx_mask |= STATX_MNT_ID; + st1.nsx.stx_mnt_id = mntid; + st1.nsx.stx_mask |= STATX_MNT_ID; } - if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) { + if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) { int mntid; - r = path_get_mnt_id_at(dir_fd, "..", &mntid); - if (r == -ENOSYS) + r = path_get_mnt_id_at_fallback(fd2, "", &mntid); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) return true; /* skip the mount ID check */ if (r < 0) return r; assert(mntid >= 0); - pst.nsx.stx_mnt_id = mntid; - pst.nsx.stx_mask |= STATX_MNT_ID; + st2.nsx.stx_mnt_id = mntid; + st2.nsx.stx_mask |= STATX_MNT_ID; } - return statx_mount_same(&st.nsx, &pst.nsx); + return statx_mount_same(&st1.nsx, &st2.nsx); } const char *accmode_to_string(int flags) { @@ -984,4 +1015,12 @@ const char *accmode_to_string(int flags) { return NULL; } } + +char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd) { + assert(buf); + assert(fd >= 0); + assert(pid >= 0); + assert_se(snprintf_ok(buf, PROC_PID_FD_PATH_MAX, "/proc/" PID_FMT "/fd/%i", pid == 0 ? getpid_cached() : pid, fd)); + return buf; +} #endif /* NM_IGNORED */ |