1// Filesystem directory iterator utilities -*- C++ -*-
2
3// Copyright (C) 2014-2020 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library.  This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23// <http://www.gnu.org/licenses/>.
24
25#ifndef _GLIBCXX_DIR_COMMON_H
26#define _GLIBCXX_DIR_COMMON_H 1
27
28#include <string.h>  // strcmp
29#include <errno.h>
30#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
31#include <wchar.h>  // wcscmp
32#endif
33#ifdef _GLIBCXX_HAVE_DIRENT_H
34# ifdef _GLIBCXX_HAVE_SYS_TYPES_H
35#  include <sys/types.h>
36# endif
37# include <dirent.h>
38#endif
39
40namespace std _GLIBCXX_VISIBILITY(default)
41{
42_GLIBCXX_BEGIN_NAMESPACE_VERSION
43namespace filesystem
44{
45namespace __gnu_posix
46{
47#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
48// Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*.
49using char_type = wchar_t;
50using DIR = ::_WDIR;
51using dirent = _wdirent;
52inline DIR* opendir(const wchar_t* path) { return ::_wopendir(path); }
53inline dirent* readdir(DIR* dir) { return ::_wreaddir(dir); }
54inline int closedir(DIR* dir) { return ::_wclosedir(dir); }
55#elif defined _GLIBCXX_HAVE_DIRENT_H
56using char_type = char;
57using DIR = ::DIR;
58typedef struct ::dirent dirent;
59using ::opendir;
60using ::readdir;
61using ::closedir;
62#else
63using char_type = char;
64struct dirent { const char* d_name; };
65struct DIR { };
66inline DIR* opendir(const char*) { return nullptr; }
67inline dirent* readdir(DIR*) { return nullptr; }
68inline int closedir(DIR*) { return -1; }
69#endif
70} // namespace __gnu_posix
71
72namespace posix = __gnu_posix;
73
74struct _Dir_base
75{
76  _Dir_base(posix::DIR* dirp = nullptr) : dirp(dirp) { }
77
78  // If no error occurs then dirp is non-null,
79  // otherwise null (whether error ignored or not).
80  _Dir_base(const posix::char_type* pathname, bool skip_permission_denied,
81	    error_code& ec) noexcept
82  : dirp(posix::opendir(pathname))
83  {
84    if (dirp)
85      ec.clear();
86    else
87    {
88      const int err = errno;
89      if (err == EACCES && skip_permission_denied)
90	ec.clear();
91      else
92	ec.assign(err, std::generic_category());
93    }
94  }
95
96  _Dir_base(_Dir_base&& d) : dirp(std::exchange(d.dirp, nullptr)) { }
97
98  _Dir_base& operator=(_Dir_base&&) = delete;
99
100  ~_Dir_base() { if (dirp) posix::closedir(dirp); }
101
102  const posix::dirent*
103  advance(bool skip_permission_denied, error_code& ec) noexcept
104  {
105    ec.clear();
106
107    int err = std::exchange(errno, 0);
108    const posix::dirent* entp = posix::readdir(dirp);
109    // std::swap cannot be used with Bionic's errno
110    err = std::exchange(errno, err);
111
112    if (entp)
113      {
114	// skip past dot and dot-dot
115	if (is_dot_or_dotdot(entp->d_name))
116	  return advance(skip_permission_denied, ec);
117	return entp;
118      }
119    else if (err)
120      {
121	if (err == EACCES && skip_permission_denied)
122	  return nullptr;
123	ec.assign(err, std::generic_category());
124	return nullptr;
125      }
126    else
127      {
128	// reached the end
129	return nullptr;
130      }
131  }
132
133  static bool is_dot_or_dotdot(const char* s) noexcept
134  { return !strcmp(s, ".") || !strcmp(s, ".."); }
135
136#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
137  static bool is_dot_or_dotdot(const wchar_t* s) noexcept
138  { return !wcscmp(s, L".") || !wcscmp(s, L".."); }
139#endif
140
141  posix::DIR*	dirp;
142};
143
144inline bool
145is_permission_denied_error(int e)
146{
147  if (e == EACCES)
148    return true;
149#ifdef __APPLE__
150  if (e == EPERM) // See PR 99533
151    return true;
152#endif
153  return false;
154}
155
156} // namespace filesystem
157
158// BEGIN/END macros must be defined before including this file.
159_GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
160
161inline file_type
162get_file_type(const std::filesystem::__gnu_posix::dirent& d [[gnu::unused]])
163{
164#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
165  switch (d.d_type)
166  {
167  case DT_BLK:
168    return file_type::block;
169  case DT_CHR:
170    return file_type::character;
171  case DT_DIR:
172    return file_type::directory;
173  case DT_FIFO:
174    return file_type::fifo;
175  case DT_LNK:
176    return file_type::symlink;
177  case DT_REG:
178    return file_type::regular;
179  case DT_SOCK:
180    return file_type::socket;
181  case DT_UNKNOWN:
182    return file_type::unknown;
183  default:
184    return file_type::none;
185  }
186#else
187  return file_type::none;
188#endif
189}
190_GLIBCXX_END_NAMESPACE_FILESYSTEM
191
192_GLIBCXX_END_NAMESPACE_VERSION
193} // namespace std
194
195#endif // _GLIBCXX_DIR_COMMON_H
196