summaryrefslogtreecommitdiff
path: root/src/libnm-systemd-shared/src/basic/fd-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/fd-util.c')
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c183
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 */