diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2022-06-27 14:43:54 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2022-06-28 12:09:20 +0100 |
commit | 198781144f33b0ef17dd2094580b5c77ad97d6e8 (patch) | |
tree | ea71d88cfa12271bebe5437f606e0981dc0cdaa3 /libstdc++-v3/src/filesystem/dir-common.h | |
parent | 835b19936bf30d693783bfa39145a4ce243bbd7c (diff) | |
download | gcc-198781144f33b0ef17dd2094580b5c77ad97d6e8.zip gcc-198781144f33b0ef17dd2094580b5c77ad97d6e8.tar.gz gcc-198781144f33b0ef17dd2094580b5c77ad97d6e8.tar.bz2 |
libstdc++: Improve directory iterator abstractions for openat
Currently the _Dir::open_subdir function decides whether to construct a
_Dir_base with just a pathname, or a file descriptor and pathname. But
that means it is tiughtly coupled to the implementation of
_Dir_base::openat, which is what actually decides whether to use a file
descriptor or not. If the derived class passes a file descriptor and
filename, but the base class expects a full path and ignores the file
descriptor, then recursive_directory_iterator cannot recurse.
This change introduces a new type that provides the union of all the
information available to the derived class (the full pathname, as well
as a file descriptor for a directory and another pathname relative to
that directory). This allows the derived class to be agnostic to how the
base class will use that information.
libstdc++-v3/ChangeLog:
* src/c++17/fs_dir.cc (_Dir::dir_and_pathname):: Replace with
current() returning _At_path.
(_Dir::_Dir, _Dir::open_subdir, _Dir::do_unlink): Adjust.
* src/filesystem/dir-common.h (_Dir_base::_At_path): New class.
(_Dir_base::_Dir_Base, _Dir_base::openat): Use _At_path.
* src/filesystem/dir.cc (_Dir::dir_and_pathname): Replace with
current() returning _At_path.
(_Dir::_Dir, _Dir::open_subdir): Adjust.
Diffstat (limited to 'libstdc++-v3/src/filesystem/dir-common.h')
-rw-r--r-- | libstdc++-v3/src/filesystem/dir-common.h | 66 |
1 files changed, 48 insertions, 18 deletions
diff --git a/libstdc++-v3/src/filesystem/dir-common.h b/libstdc++-v3/src/filesystem/dir-common.h index 669780e..4844b1a 100644 --- a/libstdc++-v3/src/filesystem/dir-common.h +++ b/libstdc++-v3/src/filesystem/dir-common.h @@ -25,6 +25,7 @@ #ifndef _GLIBCXX_DIR_COMMON_H #define _GLIBCXX_DIR_COMMON_H 1 +#include <stdint.h> // uint32_t #include <string.h> // strcmp #include <errno.h> #if _GLIBCXX_FILESYSTEM_IS_WINDOWS @@ -91,12 +92,50 @@ is_permission_denied_error(int e) struct _Dir_base { + // As well as the full pathname (including the directory iterator's path) + // this type contains a file descriptor for a directory and a second pathname + // relative to that directory. The file descriptor and relative pathname + // can be used with POSIX openat and unlinkat. + struct _At_path + { + // No file descriptor given, so interpret the pathname relative to the CWD. + _At_path(const char* p) noexcept + : pathname(p), dir_fd(fdcwd()), offset(0) + { } + + _At_path(int fd, const char* p, size_t offset) noexcept + : pathname(p), dir_fd(fd), offset(offset) + { } + + const char* path() const noexcept { return pathname; } + + int dir() const noexcept { return dir_fd; } + const char* path_at_dir() const noexcept { return pathname + offset; } + + private: + const posix::char_type* pathname; // Full path relative to CWD. + int dir_fd; // A directory descriptor (either the parent dir, or AT_FDCWD). + uint32_t offset; // Offset into pathname for the part relative to dir_fd. + + // Special value representing the current working directory. + // Not a valid file descriptor for an open directory stream. + static constexpr int + fdcwd() noexcept + { +#ifdef AT_FDCWD + return AT_FDCWD; +#else + return -1; // Use invalid fd if AT_FDCWD isn't supported. +#endif + } + }; + // If no error occurs then dirp is non-null, // otherwise null (even if a permission denied error is ignored). - _Dir_base(int fd, const posix::char_type* pathname, + _Dir_base(const _At_path& atp, bool skip_permission_denied, bool nofollow, error_code& ec) noexcept - : dirp(_Dir_base::openat(fd, pathname, nofollow)) + : dirp(_Dir_base::openat(atp, nofollow)) { if (dirp) ec.clear(); @@ -143,16 +182,6 @@ struct _Dir_base } } - static constexpr int - fdcwd() noexcept - { -#ifdef AT_FDCWD - return AT_FDCWD; -#else - return -1; // Use invalid fd if AT_FDCWD isn't supported. -#endif - } - static bool is_dot_or_dotdot(const char* s) noexcept { return !strcmp(s, ".") || !strcmp(s, ".."); } @@ -174,7 +203,7 @@ struct _Dir_base } static posix::DIR* - openat(int fd, const posix::char_type* pathname, bool nofollow) + openat(const _At_path& atp, bool nofollow) { #if _GLIBCXX_HAVE_FDOPENDIR && defined O_RDONLY && defined O_DIRECTORY \ && ! _GLIBCXX_FILESYSTEM_IS_WINDOWS @@ -198,16 +227,17 @@ struct _Dir_base nofollow = false; #endif + int fd; -#if _GLIBCXX_HAVE_OPENAT && defined AT_FDCWD - fd = ::openat(fd, pathname, flags); +#if _GLIBCXX_HAVE_OPENAT + fd = ::openat(atp.dir(), atp.path_at_dir(), flags); #else // If we cannot use openat, there's no benefit to using posix::open unless // we will use O_NOFOLLOW, so just use the simpler posix::opendir. if (!nofollow) - return posix::opendir(pathname); + return posix::opendir(atp.path()); - fd = ::open(pathname, flags); + fd = ::open(atp.path(), flags); #endif if (fd == -1) @@ -220,7 +250,7 @@ struct _Dir_base errno = err; return nullptr; #else - return posix::opendir(pathname); + return posix::opendir(atp.path()); #endif } |