diff --git a/src/fds.cpp b/src/fds.cpp index 4f3020976..610cfd84d 100644 --- a/src/fds.cpp +++ b/src/fds.cpp @@ -5,11 +5,17 @@ #include "fds.h" #include +#include #include +#include "common.h" #include "flog.h" #include "wutil.h" +#if defined(__linux__) +#include +#endif + void autoclose_fd_t::close() { if (fd_ < 0) return; exec_close(fd_); @@ -63,6 +69,85 @@ maybe_t make_autoclose_pipes(const fd_set_t &fdset) { return autoclose_pipes_t(std::move(read_end), std::move(write_end)); } +int set_cloexec(int fd, bool should_set) { + // Note we don't want to overwrite existing flags like O_NONBLOCK which may be set. So fetch the + // existing flags and modify them. + int flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) { + return -1; + } + int new_flags = flags; + if (should_set) { + new_flags |= FD_CLOEXEC; + } else { + new_flags &= ~FD_CLOEXEC; + } + if (flags == new_flags) { + return 0; + } else { + return fcntl(fd, F_SETFD, new_flags); + } +} + +int open_cloexec(const std::string &path, int flags, mode_t mode) { + return open_cloexec(path.c_str(), flags, mode); +} + +int open_cloexec(const char *path, int flags, mode_t mode) { + int fd; + + // Prefer to use O_CLOEXEC. +#ifdef O_CLOEXEC + fd = open(path, flags | O_CLOEXEC, mode); +#else + fd = open(path, flags, mode); + if (fd >= 0 && !set_cloexec(fd)) { + exec_close(fd); + fd = -1; + } +#endif + return fd; +} + +int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) { + return open_cloexec(wcs2string(pathname), flags, mode); +} + +int fd_check_is_remote(int fd) { + UNUSED(fd); +#if defined(__linux__) + struct statfs buf {}; + if (fstatfs(fd, &buf) < 0) { + return -1; + } + // Linux has constants for these like NFS_SUPER_MAGIC, SMB_SUPER_MAGIC, CIFS_MAGIC_NUMBER but + // these are in varying headers. Simply hard code them. + // NOTE: The cast is necessary for 32-bit systems because of the 4-byte CIFS_MAGIC_NUMBER + switch (static_cast(buf.f_type)) { + case 0x6969: // NFS_SUPER_MAGIC + case 0x517B: // SMB_SUPER_MAGIC + case 0xFE534D42U: // SMB2_MAGIC_NUMBER - not in the manpage + case 0xFF534D42U: // CIFS_MAGIC_NUMBER + return 1; + default: + // Other FSes are assumed local. + return 0; + } +#elif defined(ST_LOCAL) + // ST_LOCAL is a flag to statvfs, which is itself standardized. + // In practice the only system to use this path is NetBSD. + struct statvfs buf {}; + if (fstatvfs(fd, &buf) < 0) return -1; + return (buf.f_flag & ST_LOCAL) ? 0 : 1; +#elif defined(MNT_LOCAL) + struct statfs buf {}; + if (fstatfs(fd, &buf) < 0) return -1; + return (buf.f_flags & MNT_LOCAL) ? 0 : 1; +#else + return -1; +#endif +} + void exec_close(int fd) { assert(fd >= 0 && "Invalid fd"); while (close(fd) == -1) { diff --git a/src/fds.h b/src/fds.h index 288bf9f17..808a8cb75 100644 --- a/src/fds.h +++ b/src/fds.h @@ -4,10 +4,13 @@ #define FISH_FDS_H #include +#include #include #include "maybe.h" +using wcstring = std::wstring; + /// Pipe redirection error message. #define PIPE_ERROR _(L"An error occurred while setting up pipe") @@ -95,6 +98,27 @@ maybe_t make_autoclose_pipes(const fd_set_t &fdset); /// cloexec. \returns invalid fd on failure (in which case the given fd is still closed). autoclose_fd_t move_fd_to_unused(autoclose_fd_t fd, const fd_set_t &fdset); +/// Sets CLO_EXEC on a given fd according to the value of \p should_set. +int set_cloexec(int fd, bool should_set = true); + +/// Wide character version of open() that also sets the close-on-exec flag (atomically when +/// possible). +int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode = 0); + +/// Narrow versions of wopen_cloexec. +int open_cloexec(const std::string &path, int flags, mode_t mode = 0); +int open_cloexec(const char *path, int flags, mode_t mode = 0); + +/// Mark an fd as nonblocking; returns errno or 0 on success. +int make_fd_nonblocking(int fd); + +/// Mark an fd as blocking; returns errno or 0 on success. +int make_fd_blocking(int fd); + +/// Check if an fd is on a remote filesystem (NFS, SMB, CFS) +/// Return 1 if remote, 0 if local, -1 on error or if not implemented on this platform. +int fd_check_is_remote(int fd); + /// Close a file descriptor \p fd, retrying on EINTR. void exec_close(int fd); diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index b6dca2fa1..6451a8026 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -39,6 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "env.h" #include "expand.h" #include "fish_version.h" +#include "fds.h" #include "flog.h" #include "highlight.h" #include "operation_context.h" diff --git a/src/history_file.cpp b/src/history_file.cpp index 7598191c1..909e670f9 100644 --- a/src/history_file.cpp +++ b/src/history_file.cpp @@ -2,10 +2,11 @@ #include "history_file.h" -#include - +#include "fds.h" #include "history.h" +#include + // Some forward declarations. static history_item_t decode_item_fish_2_0(const char *base, size_t len); static history_item_t decode_item_fish_1_x(const char *begin, size_t length); diff --git a/src/iothread.cpp b/src/iothread.cpp index 57dda3caf..87c20b66b 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -19,6 +19,7 @@ #include #include "common.h" +#include "fds.h" #include "flog.h" #include "global_safety.h" #include "wutil.h" diff --git a/src/wutil.cpp b/src/wutil.cpp index e430b9e9d..dd515c3ca 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -12,9 +12,6 @@ #include #include -#if defined(__linux__) -#include -#endif #include #include #include @@ -142,50 +139,6 @@ wcstring wgetcwd() { return wcstring(); } -int set_cloexec(int fd, bool should_set) { - // Note we don't want to overwrite existing flags like O_NONBLOCK which may be set. So fetch the - // existing flags and modify them. - int flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) { - return -1; - } - int new_flags = flags; - if (should_set) { - new_flags |= FD_CLOEXEC; - } else { - new_flags &= ~FD_CLOEXEC; - } - if (flags == new_flags) { - return 0; - } else { - return fcntl(fd, F_SETFD, new_flags); - } -} - -int open_cloexec(const std::string &path, int flags, mode_t mode) { - return open_cloexec(path.c_str(), flags, mode); -} - -int open_cloexec(const char *path, int flags, mode_t mode) { - int fd; - -// Prefer to use O_CLOEXEC. -#ifdef O_CLOEXEC - fd = open(path, flags | O_CLOEXEC, mode); -#else - fd = open(path, flags, mode); - if (fd >= 0 && !set_cloexec(fd)) { - exec_close(fd); - fd = -1; - } -#endif - return fd; -} - -int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) { - cstring tmp = wcs2string(pathname); - return open_cloexec(tmp, flags, mode); -} DIR *wopendir(const wcstring &name) { const cstring tmp = wcs2string(name); @@ -256,40 +209,6 @@ int make_fd_blocking(int fd) { return err == -1 ? errno : 0; } -int fd_check_is_remote(int fd) { -#if defined(__linux__) - struct statfs buf {}; - if (fstatfs(fd, &buf) < 0) { - return -1; - } - // Linux has constants for these like NFS_SUPER_MAGIC, SMB_SUPER_MAGIC, CIFS_MAGIC_NUMBER but - // these are in varying headers. Simply hard code them. - // NOTE: The cast is necessary for 32-bit systems because of the 4-byte CIFS_MAGIC_NUMBER - switch (static_cast(buf.f_type)) { - case 0x6969: // NFS_SUPER_MAGIC - case 0x517B: // SMB_SUPER_MAGIC - case 0xFE534D42U: // SMB2_MAGIC_NUMBER - not in the manpage - case 0xFF534D42U: // CIFS_MAGIC_NUMBER - return 1; - default: - // Other FSes are assumed local. - return 0; - } -#elif defined(ST_LOCAL) - // ST_LOCAL is a flag to statvfs, which is itself standardized. - // In practice the only system to use this path is NetBSD. - struct statvfs buf {}; - if (fstatvfs(fd, &buf) < 0) return -1; - return (buf.f_flag & ST_LOCAL) ? 0 : 1; -#elif defined(MNT_LOCAL) - struct statfs buf {}; - if (fstatfs(fd, &buf) < 0) return -1; - return (buf.f_flags & MNT_LOCAL) ? 0 : 1; -#else - return -1; -#endif -} - static inline void safe_append(char *buffer, const char *s, size_t buffsize) { std::strncat(buffer, s, buffsize - std::strlen(buffer) - 1); } diff --git a/src/wutil.h b/src/wutil.h index 54f5e8055..6ab8fb31a 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -19,27 +19,6 @@ #include "common.h" #include "maybe.h" -/// Sets CLO_EXEC on a given fd according to the value of \p should_set. -int set_cloexec(int fd, bool should_set = true); - -/// Wide character version of open() that also sets the close-on-exec flag (atomically when -/// possible). -int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode = 0); - -/// Narrow versions of wopen_cloexec. -int open_cloexec(const std::string &path, int flags, mode_t mode = 0); -int open_cloexec(const char *path, int flags, mode_t mode = 0); - -/// Mark an fd as nonblocking; returns errno or 0 on success. -int make_fd_nonblocking(int fd); - -/// Mark an fd as blocking; returns errno or 0 on success. -int make_fd_blocking(int fd); - -/// Check if an fd is on a remote filesystem (NFS, SMB, CFS) -/// Return 1 if remote, 0 if local, -1 on error or if not implemented on this platform. -int fd_check_is_remote(int fd); - /// Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by /// POSIX (hooray). DIR *wopendir(const wcstring &name);