commit 16ff5d124b8e6c5d1f9dd4edb81b6ca5c9129134 Author: Jonathan Wakely Date: Tue Sep 29 11:58:19 2015 +0100 PR libstdc++/67747 Allocate space for dirent::d_name PR libstdc++/67747 * src/filesystem/dir.cc (_Dir::dirent_buf): New member. (get_name_max): New function. (native_readdir) [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Copy to supplied dirent object. Handle end of directory. (_Dir::advance): Allocate space for d_name. diff --git a/libstdc++-v3/src/filesystem/dir.cc b/libstdc++-v3/src/filesystem/dir.cc index bce751c..d29f8eb 100644 --- a/libstdc++-v3/src/filesystem/dir.cc +++ b/libstdc++-v3/src/filesystem/dir.cc @@ -25,8 +25,12 @@ #include #include #include +#include #include #include +#ifdef _GLIBCXX_HAVE_UNISTD_H +# include +#endif #ifdef _GLIBCXX_HAVE_DIRENT_H # ifdef _GLIBCXX_HAVE_SYS_TYPES_H # include @@ -64,20 +68,23 @@ struct fs::_Dir fs::path path; directory_entry entry; file_type type = file_type::none; + unique_ptr dirent_buf; }; namespace { template - inline bool is_set(Bitmask obj, Bitmask bits) + inline bool + is_set(Bitmask obj, Bitmask bits) { return (obj & bits) != Bitmask::none; } // Returns {dirp, p} on success, {nullptr, p} on error. // If an ignored EACCES error occurs returns {}. - fs::_Dir - open_dir(const fs::path& p, fs::directory_options options, std::error_code* ec) + inline fs::_Dir + open_dir(const fs::path& p, fs::directory_options options, + std::error_code* ec) { if (ec) ec->clear(); @@ -99,8 +106,22 @@ namespace return {nullptr, p}; } + inline long + get_name_max(const fs::path& path __attribute__((__unused__))) + { +#ifdef _GLIBCXX_HAVE_UNISTD_H + long name_max = pathconf(path.c_str(), _PC_NAME_MAX); + if (name_max != -1) + return name_max; +#endif + + // Maximum path component on Windows is 255 (UTF-16?) characters, + // which is a reasonable default for POSIX too. + return 255; + } + inline fs::file_type - get_file_type(const dirent& d __attribute__((__unused__))) + get_file_type(const ::dirent& d __attribute__((__unused__))) { #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE switch (d.d_type) @@ -129,12 +150,26 @@ namespace #endif } - int + inline int native_readdir(DIR* dirp, ::dirent*& entryp) { #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS - if ((entryp = ::readdir(dirp))) - return 0; + const int saved_errno = errno; + errno = 0; + if (auto entp = ::readdir(dirp)) + { + size_t name_len = strlen(entp->d_name); + if (name_len > 255) + return ENAMETOOLONG; + size_t len = offsetof(::dirent, d_name) + name_len + 1; + memcpy(entryp, entp, len); + return 0; + } + else if (errno == 0) // End of directory reached. + { + errno = saved_errno; + entryp = nullptr; + } return errno; #else return ::readdir_r(dirp, entryp, &entryp); @@ -142,6 +177,7 @@ namespace } } + // Returns false when the end of the directory entries is reached. // Reports errors by setting ec or throwing. bool @@ -150,9 +186,15 @@ fs::_Dir::advance(error_code* ec, directory_options options) if (ec) ec->clear(); - ::dirent ent; - ::dirent* result = &ent; - if (int err = native_readdir(dirp, result)) + if (!dirent_buf) + { + size_t len = offsetof(::dirent, d_name) + get_name_max(path) + 1; + dirent_buf.reset(new char[len]); + } + + ::dirent* entp = reinterpret_cast<::dirent*>(dirent_buf.get()); + + if (int err = native_readdir(dirp, entp)) { if (err == EACCES && is_set(options, directory_options::skip_permission_denied)) @@ -165,13 +207,13 @@ fs::_Dir::advance(error_code* ec, directory_options options) ec->assign(err, std::generic_category()); return true; } - else if (result != nullptr) + else if (entp != nullptr) { // skip past dot and dot-dot - if (!strcmp(ent.d_name, ".") || !strcmp(ent.d_name, "..")) + if (!strcmp(entp->d_name, ".") || !strcmp(entp->d_name, "..")) return advance(ec, options); - entry = fs::directory_entry{path / ent.d_name}; - type = get_file_type(ent); + entry = fs::directory_entry{path / entp->d_name}; + type = get_file_type(*entp); return true; } else