Coverage Report

Created: 2022-05-17 06:19

/Users/buildslave/jenkins/workspace/coverage/llvm-project/libcxx/src/filesystem/directory_iterator.cpp
Line
Count
Source (jump to first uncovered line)
1
//===----------------------------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include <__assert>
10
#include <__config>
11
#include <errno.h>
12
#include <filesystem>
13
#include <stack>
14
15
#include "filesystem_common.h"
16
17
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
18
19
using detail::ErrorHandler;
20
21
#if defined(_LIBCPP_WIN32API)
22
class __dir_stream {
23
public:
24
  __dir_stream() = delete;
25
  __dir_stream& operator=(const __dir_stream&) = delete;
26
27
  __dir_stream(__dir_stream&& __ds) noexcept : __stream_(__ds.__stream_),
28
                                               __root_(std::move(__ds.__root_)),
29
                                               __entry_(std::move(__ds.__entry_)) {
30
    __ds.__stream_ = INVALID_HANDLE_VALUE;
31
  }
32
33
  __dir_stream(const path& root, directory_options opts, error_code& ec)
34
      : __stream_(INVALID_HANDLE_VALUE), __root_(root) {
35
    if (root.native().empty()) {
36
      ec = make_error_code(errc::no_such_file_or_directory);
37
      return;
38
    }
39
    __stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);
40
    if (__stream_ == INVALID_HANDLE_VALUE) {
41
      ec = detail::make_windows_error(GetLastError());
42
      const bool ignore_permission_denied =
43
          bool(opts & directory_options::skip_permission_denied);
44
      if (ignore_permission_denied &&
45
          ec.value() == static_cast<int>(errc::permission_denied))
46
        ec.clear();
47
      return;
48
    }
49
    if (!assign())
50
      advance(ec);
51
  }
52
53
  ~__dir_stream() noexcept {
54
    if (__stream_ == INVALID_HANDLE_VALUE)
55
      return;
56
    close();
57
  }
58
59
  bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
60
61
  bool advance(error_code& ec) {
62
    while (::FindNextFileW(__stream_, &__data_)) {
63
      if (assign())
64
        return true;
65
    }
66
    close();
67
    return false;
68
  }
69
70
  bool assign() {
71
    if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))
72
      return false;
73
    // FIXME: Cache more of this
74
    //directory_entry::__cached_data cdata;
75
    //cdata.__type_ = get_file_type(__data_);
76
    //cdata.__size_ = get_file_size(__data_);
77
    //cdata.__write_time_ = get_write_time(__data_);
78
    __entry_.__assign_iter_entry(
79
        __root_ / __data_.cFileName,
80
        directory_entry::__create_iter_result(detail::get_file_type(__data_)));
81
    return true;
82
  }
83
84
private:
85
  error_code close() noexcept {
86
    error_code ec;
87
    if (!::FindClose(__stream_))
88
      ec = detail::make_windows_error(GetLastError());
89
    __stream_ = INVALID_HANDLE_VALUE;
90
    return ec;
91
  }
92
93
  HANDLE __stream_{INVALID_HANDLE_VALUE};
94
  WIN32_FIND_DATAW __data_;
95
96
public:
97
  path __root_;
98
  directory_entry __entry_;
99
};
100
#else
101
class __dir_stream {
102
public:
103
  __dir_stream() = delete;
104
  __dir_stream& operator=(const __dir_stream&) = delete;
105
106
  __dir_stream(__dir_stream&& other) noexcept : __stream_(other.__stream_),
107
                                                __root_(std::move(other.__root_)),
108
0
                                                __entry_(std::move(other.__entry_)) {
109
0
    other.__stream_ = nullptr;
110
0
  }
111
112
  __dir_stream(const path& root, directory_options opts, error_code& ec)
113
0
      : __stream_(nullptr), __root_(root) {
114
0
    if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
115
0
      ec = detail::capture_errno();
116
0
      const bool allow_eacces =
117
0
          bool(opts & directory_options::skip_permission_denied);
118
0
      if (allow_eacces && ec.value() == EACCES)
119
0
        ec.clear();
120
0
      return;
121
0
    }
122
0
    advance(ec);
123
0
  }
124
125
0
  ~__dir_stream() noexcept {
126
0
    if (__stream_)
127
0
      close();
128
0
  }
129
130
0
  bool good() const noexcept { return __stream_ != nullptr; }
131
132
0
  bool advance(error_code& ec) {
133
0
    while (true) {
134
0
      auto str_type_pair = detail::posix_readdir(__stream_, ec);
135
0
      auto& str = str_type_pair.first;
136
0
      if (str == "." || str == "..") {
137
0
        continue;
138
0
      } else if (ec || str.empty()) {
139
0
        close();
140
0
        return false;
141
0
      } else {
142
0
        __entry_.__assign_iter_entry(
143
0
            __root_ / str,
144
0
            directory_entry::__create_iter_result(str_type_pair.second));
145
0
        return true;
146
0
      }
147
0
    }
148
0
  }
149
150
private:
151
0
  error_code close() noexcept {
152
0
    error_code m_ec;
153
0
    if (::closedir(__stream_) == -1)
154
0
      m_ec = detail::capture_errno();
155
0
    __stream_ = nullptr;
156
0
    return m_ec;
157
0
  }
158
159
  DIR* __stream_{nullptr};
160
161
public:
162
  path __root_;
163
  directory_entry __entry_;
164
};
165
#endif
166
167
// directory_iterator
168
169
directory_iterator::directory_iterator(const path& p, error_code* ec,
170
0
                                       directory_options opts) {
171
0
  ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);
172
173
0
  error_code m_ec;
174
0
  __imp_ = make_shared<__dir_stream>(p, opts, m_ec);
175
0
  if (ec)
176
0
    *ec = m_ec;
177
0
  if (!__imp_->good()) {
178
0
    __imp_.reset();
179
0
    if (m_ec)
180
0
      err.report(m_ec);
181
0
  }
182
0
}
183
184
0
directory_iterator& directory_iterator::__increment(error_code* ec) {
185
0
  _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
186
0
  ErrorHandler<void> err("directory_iterator::operator++()", ec);
187
188
0
  error_code m_ec;
189
0
  if (!__imp_->advance(m_ec)) {
190
0
    path root = std::move(__imp_->__root_);
191
0
    __imp_.reset();
192
0
    if (m_ec)
193
0
      err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());
194
0
  }
195
0
  return *this;
196
0
}
197
198
0
directory_entry const& directory_iterator::__dereference() const {
199
0
  _LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
200
0
  return __imp_->__entry_;
201
0
}
202
203
// recursive_directory_iterator
204
205
struct recursive_directory_iterator::__shared_imp {
206
  stack<__dir_stream> __stack_;
207
  directory_options __options_;
208
};
209
210
recursive_directory_iterator::recursive_directory_iterator(
211
    const path& p, directory_options opt, error_code* ec)
212
0
    : __imp_(nullptr), __rec_(true) {
213
0
  ErrorHandler<void> err("recursive_directory_iterator", ec, &p);
214
215
0
  error_code m_ec;
216
0
  __dir_stream new_s(p, opt, m_ec);
217
0
  if (m_ec)
218
0
    err.report(m_ec);
219
0
  if (m_ec || !new_s.good())
220
0
    return;
221
222
0
  __imp_ = make_shared<__shared_imp>();
223
0
  __imp_->__options_ = opt;
224
0
  __imp_->__stack_.push(std::move(new_s));
225
0
}
226
227
0
void recursive_directory_iterator::__pop(error_code* ec) {
228
0
  _LIBCPP_ASSERT(__imp_, "Popping the end iterator");
229
0
  if (ec)
230
0
    ec->clear();
231
0
  __imp_->__stack_.pop();
232
0
  if (__imp_->__stack_.size() == 0)
233
0
    __imp_.reset();
234
0
  else
235
0
    __advance(ec);
236
0
}
237
238
0
directory_options recursive_directory_iterator::options() const {
239
0
  return __imp_->__options_;
240
0
}
241
242
0
int recursive_directory_iterator::depth() const {
243
0
  return __imp_->__stack_.size() - 1;
244
0
}
245
246
0
const directory_entry& recursive_directory_iterator::__dereference() const {
247
0
  return __imp_->__stack_.top().__entry_;
248
0
}
249
250
recursive_directory_iterator&
251
0
recursive_directory_iterator::__increment(error_code* ec) {
252
0
  if (ec)
253
0
    ec->clear();
254
0
  if (recursion_pending()) {
255
0
    if (__try_recursion(ec) || (ec && *ec))
256
0
      return *this;
257
0
  }
258
0
  __rec_ = true;
259
0
  __advance(ec);
260
0
  return *this;
261
0
}
262
263
0
void recursive_directory_iterator::__advance(error_code* ec) {
264
0
  ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
265
266
0
  const directory_iterator end_it;
267
0
  auto& stack = __imp_->__stack_;
268
0
  error_code m_ec;
269
0
  while (stack.size() > 0) {
270
0
    if (stack.top().advance(m_ec))
271
0
      return;
272
0
    if (m_ec)
273
0
      break;
274
0
    stack.pop();
275
0
  }
276
277
0
  if (m_ec) {
278
0
    path root = std::move(stack.top().__root_);
279
0
    __imp_.reset();
280
0
    err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());
281
0
  } else {
282
0
    __imp_.reset();
283
0
  }
284
0
}
285
286
0
bool recursive_directory_iterator::__try_recursion(error_code* ec) {
287
0
  ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
288
289
0
  bool rec_sym = bool(options() & directory_options::follow_directory_symlink);
290
291
0
  auto& curr_it = __imp_->__stack_.top();
292
293
0
  bool skip_rec = false;
294
0
  error_code m_ec;
295
0
  if (!rec_sym) {
296
0
    file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));
297
0
    if (m_ec && status_known(st))
298
0
      m_ec.clear();
299
0
    if (m_ec || is_symlink(st) || !is_directory(st))
300
0
      skip_rec = true;
301
0
  } else {
302
0
    file_status st(curr_it.__entry_.__get_ft(&m_ec));
303
0
    if (m_ec && status_known(st))
304
0
      m_ec.clear();
305
0
    if (m_ec || !is_directory(st))
306
0
      skip_rec = true;
307
0
  }
308
309
0
  if (!skip_rec) {
310
0
    __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
311
0
    if (new_it.good()) {
312
0
      __imp_->__stack_.push(std::move(new_it));
313
0
      return true;
314
0
    }
315
0
  }
316
0
  if (m_ec) {
317
0
    const bool allow_eacess =
318
0
        bool(__imp_->__options_ & directory_options::skip_permission_denied);
319
0
    if (m_ec.value() == EACCES && allow_eacess) {
320
0
      if (ec)
321
0
        ec->clear();
322
0
    } else {
323
0
      path at_ent = std::move(curr_it.__entry_.__p_);
324
0
      __imp_.reset();
325
0
      err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT,
326
0
                 at_ent.c_str());
327
0
    }
328
0
  }
329
0
  return false;
330
0
}
331
332
_LIBCPP_END_NAMESPACE_FILESYSTEM