diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index da4ee63add..24f06b1ebc 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -617,16 +617,8 @@ int fd_get_path(int fd, char **ret) { return safe_getcwd(ret); r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret); - if (r == -ENOENT) { - /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make - * things debuggable and distinguish the two. */ - - if (proc_mounted() == 0) - return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot - * environment. */ - return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ - } - + if (r == -ENOENT) + return proc_fd_enoent_errno(); return r; } @@ -823,8 +815,6 @@ finish: } int fd_reopen(int fd, int flags) { - int r; - assert(fd >= 0 || fd == AT_FDCWD); assert(!FLAGS_SET(flags, O_CREAT)); @@ -860,13 +850,7 @@ int fd_reopen(int fd, int flags) { if (errno != ENOENT) return -errno; - r = proc_mounted(); - if (r == 0) - return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ - - return r > 0 ? -EBADF : -ENOENT; /* If /proc/ is definitely around then this means the fd is - * not valid, otherwise let's propagate the original - * error */ + return proc_fd_enoent_errno(); } return new_fd; @@ -1136,3 +1120,18 @@ char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, assert_se(snprintf_ok(buf, PROC_PID_FD_PATH_MAX, "/proc/" PID_FMT "/fd/%i", pid == 0 ? getpid_cached() : pid, fd)); return buf; } + +int proc_fd_enoent_errno(void) { + int r; + + /* When ENOENT is returned during the use of FORMAT_PROC_FD_PATH, it can mean two things: + * that the fd does not exist or that /proc/ is not mounted. + * Let's make things debuggable and figure out the most appropriate errno. */ + + r = proc_mounted(); + if (r == 0) + return -ENOSYS; /* /proc/ is not available or not set up properly, we're most likely + in some chroot environment. */ + return r > 0 ? -EBADF : -ENOENT; /* If /proc/ is definitely around then this means the fd is + not valid, otherwise let's propagate the original ENOENT. */ +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index af17481dd8..a047c3a3f4 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -161,6 +161,8 @@ char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, #define FORMAT_PROC_PID_FD_PATH(pid, fd) \ format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd)) +int proc_fd_enoent_errno(void); + const char *accmode_to_string(int flags); /* Like ASSERT_PTR, but for fds */ diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 64d309317d..92ceb3b352 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -313,10 +313,7 @@ int fchmod_opath(int fd, mode_t m) { if (errno != ENOENT) return -errno; - if (proc_mounted() == 0) - return -ENOSYS; /* if we have no /proc/, the concept is not implementable */ - - return -ENOENT; + return proc_fd_enoent_errno(); } return 0; @@ -339,10 +336,7 @@ int futimens_opath(int fd, const struct timespec ts[2]) { if (errno != ENOENT) return -errno; - if (proc_mounted() == 0) - return -ENOSYS; - - return -ENOENT; + return proc_fd_enoent_errno(); } return 0; @@ -633,17 +627,18 @@ static int tmp_dir_internal(const char *def, const char **ret) { return 0; } - k = is_dir(def, true); + k = is_dir(def, /* follow = */ true); if (k == 0) k = -ENOTDIR; if (k < 0) - return r < 0 ? r : k; + return RET_GATHER(r, k); *ret = def; return 0; } int var_tmp_dir(const char **ret) { + assert(ret); /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is @@ -654,6 +649,7 @@ int var_tmp_dir(const char **ret) { } int tmp_dir(const char **ret) { + assert(ret); /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually * backed by an in-memory file system: /tmp. */ @@ -662,6 +658,8 @@ int tmp_dir(const char **ret) { } int unlink_or_warn(const char *filename) { + assert(filename); + if (unlink(filename) < 0 && errno != ENOENT) /* If the file doesn't exist and the fs simply was read-only (in which * case unlink() returns EROFS even if the file doesn't exist), don't @@ -673,26 +671,31 @@ int unlink_or_warn(const char *filename) { } int access_fd(int fd, int mode) { + assert(fd >= 0); + /* Like access() but operates on an already open fd */ + if (faccessat(fd, "", mode, AT_EMPTY_PATH) >= 0) + return 0; + if (errno != EINVAL) + return -errno; + + /* Support for AT_EMPTY_PATH is added rather late (kernel 5.8), so fall back to going through /proc/ + * if unavailable. */ + if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) { if (errno != ENOENT) return -errno; - /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's - * make things debuggable and distinguish the two. */ - - if (proc_mounted() == 0) - return -ENOSYS; /* /proc is not available or not set up properly, we're most likely in some chroot - * environment. */ - - return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ + return proc_fd_enoent_errno(); } return 0; } void unlink_tempfilep(char (*p)[]) { + assert(p); + /* If the file is created with mkstemp(), it will (almost always) * change the suffix. Treat this as a sign that the file was * successfully created. We ignore both the rare case where the @@ -706,6 +709,8 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { struct stat st; off_t l, bs; + assert(fd >= 0 || fd == AT_FDCWD); + assert(name); assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0); /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other @@ -1245,7 +1250,7 @@ int xopenat_lock_full( } int link_fd(int fd, int newdirfd, const char *newpath) { - int r; + int r, k; assert(fd >= 0); assert(newdirfd >= 0 || newdirfd == AT_FDCWD); @@ -1258,8 +1263,11 @@ int link_fd(int fd, int newdirfd, const char *newpath) { /* Fall back to symlinking via AT_EMPTY_PATH as fallback (this requires CAP_DAC_READ_SEARCH and a * more recent kernel, but does not require /proc/ mounted) */ - if (proc_mounted() != 0) + k = proc_mounted(); + if (k < 0) return r; + if (k > 0) + return -EBADF; return RET_NERRNO(linkat(fd, "", newdirfd, newpath, AT_EMPTY_PATH)); } diff --git a/src/basic/inotify-util.c b/src/basic/inotify-util.c index ee9b416c87..d8cbb04509 100644 --- a/src/basic/inotify-util.c +++ b/src/basic/inotify-util.c @@ -42,7 +42,10 @@ bool inotify_event_next( } int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - int wd, r; + int wd; + + assert(fd >= 0); + assert(what >= 0); /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask); @@ -50,14 +53,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { if (errno != ENOENT) return -errno; - /* Didn't work with ENOENT? If so, then either /proc/ isn't mounted, or the fd is bad */ - r = proc_mounted(); - if (r == 0) - return -ENOSYS; - if (r > 0) - return -EBADF; - - return -ENOENT; /* OK, no clue, let's propagate the original error */ + return proc_fd_enoent_errno(); } return wd; diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 66fa35b630..03e56f6cab 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -596,11 +596,14 @@ int dev_is_devtmpfs(void) { return false; } -int mount_fd(const char *source, - int target_fd, - const char *filesystemtype, - unsigned long mountflags, - const void *data) { +static int mount_fd( + const char *source, + int target_fd, + const char *filesystemtype, + unsigned long mountflags, + const void *data) { + + assert(target_fd >= 0); if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) { if (errno != ENOENT) @@ -626,6 +629,8 @@ int mount_nofollow( _cleanup_close_ int fd = -EBADF; + assert(target); + /* In almost all cases we want to manipulate the mount table without following symlinks, hence * mount_nofollow() is usually the way to go. The only exceptions are environments where /proc/ is * not available yet, since we need /proc/self/fd/ for this logic to work. i.e. during the early diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h index d7c6251aff..e5f21780f8 100644 --- a/src/basic/mountpoint-util.h +++ b/src/basic/mountpoint-util.h @@ -63,8 +63,12 @@ const char* fstype_norecovery_option(const char *fstype); int dev_is_devtmpfs(void); -int mount_fd(const char *source, int target_fd, const char *filesystemtype, unsigned long mountflags, const void *data); -int mount_nofollow(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); +int mount_nofollow( + const char *source, + const char *target, + const char *filesystemtype, + unsigned long mountflags, + const void *data); const char *mount_propagation_flag_to_string(unsigned long flags); int mount_propagation_flag_from_string(const char *name, unsigned long *ret);