1// Copyright 2015 The Kyua Authors.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9//   notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright
11//   notice, this list of conditions and the following disclaimer in the
12//   documentation and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors
14//   may be used to endorse or promote products derived from this software
15//   without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include "utils/fs/directory.hpp"
30
31extern "C" {
32#include <sys/types.h>
33
34#include <dirent.h>
35}
36
37#include <cerrno>
38#include <memory>
39
40#include "utils/format/macros.hpp"
41#include "utils/fs/exceptions.hpp"
42#include "utils/fs/path.hpp"
43#include "utils/noncopyable.hpp"
44#include "utils/sanity.hpp"
45#include "utils/text/operations.ipp"
46
47namespace detail = utils::fs::detail;
48namespace fs = utils::fs;
49namespace text = utils::text;
50
51
52/// Constructs a new directory entry.
53///
54/// \param name_ Name of the directory entry.
55fs::directory_entry::directory_entry(const std::string& name_) : name(name_)
56{
57}
58
59
60/// Checks if two directory entries are equal.
61///
62/// \param other The entry to compare to.
63///
64/// \return True if the two entries are equal; false otherwise.
65bool
66fs::directory_entry::operator==(const directory_entry& other) const
67{
68    return name == other.name;
69}
70
71
72/// Checks if two directory entries are different.
73///
74/// \param other The entry to compare to.
75///
76/// \return True if the two entries are different; false otherwise.
77bool
78fs::directory_entry::operator!=(const directory_entry& other) const
79{
80    return !(*this == other);
81}
82
83
84/// Checks if this entry sorts before another entry.
85///
86/// \param other The entry to compare to.
87///
88/// \return True if this entry sorts before the other entry; false otherwise.
89bool
90fs::directory_entry::operator<(const directory_entry& other) const
91{
92    return name < other.name;
93}
94
95
96/// Formats a directory entry.
97///
98/// \param output Stream into which to inject the formatted entry.
99/// \param entry The entry to format.
100///
101/// \return A reference to output.
102std::ostream&
103fs::operator<<(std::ostream& output, const directory_entry& entry)
104{
105    output << F("directory_entry{name=%s}") % text::quote(entry.name, '\'');
106    return output;
107}
108
109
110/// Internal implementation details for the directory_iterator.
111///
112/// In order to support multiple concurrent iterators over the same directory
113/// object, this class is the one that performs all directory-level accesses.
114/// In particular, even if it may seem surprising, this is the class that
115/// handles the DIR object for the directory.
116///
117/// Note that iterators implemented by this class do not rely on the container
118/// directory class at all.  This should not be relied on for object lifecycle
119/// purposes.
120struct utils::fs::detail::directory_iterator::impl : utils::noncopyable {
121    /// Path of the directory accessed by this iterator.
122    const fs::path _path;
123
124    /// Raw pointer to the system representation of the directory.
125    ///
126    /// We also use this to determine if the iterator is valid (at the end) or
127    /// not.  A null pointer means an invalid iterator.
128    ::DIR* _dirp;
129
130    /// Raw representation of the system directory entry.
131    ///
132    /// We need to keep this at the class level so that we can use the
133    /// readdir_r(3) function.
134    ::dirent _dirent;
135
136    /// Custom representation of the directory entry.
137    ///
138    /// This is separate from _dirent because this is the type we return to the
139    /// user.  We must keep this as a pointer so that we can support the common
140    /// operators (* and ->) over iterators.
141    std::auto_ptr< directory_entry > _entry;
142
143    /// Constructs an iterator pointing to the "end" of the directory.
144    impl(void) : _path("invalid-directory-entry"), _dirp(NULL)
145    {
146    }
147
148    /// Constructs a new iterator to start scanning a directory.
149    ///
150    /// \param path The directory that will be scanned.
151    ///
152    /// \throw system_error If there is a problem opening the directory.
153    explicit impl(const path& path) : _path(path)
154    {
155        DIR* dirp = ::opendir(_path.c_str());
156        if (dirp == NULL) {
157            const int original_errno = errno;
158            throw fs::system_error(F("opendir(%s) failed") % _path,
159                                   original_errno);
160        }
161        _dirp = dirp;
162
163        // Initialize our first directory entry.  Note that this may actually
164        // close the directory we just opened if the directory happens to be
165        // empty -- but directories are never empty because they at least have
166        // '.' and '..' entries.
167        next();
168    }
169
170    /// Destructor.
171    ///
172    /// This closes the directory if still open.
173    ~impl(void)
174    {
175        if (_dirp != NULL)
176            close();
177    }
178
179    /// Closes the directory and invalidates the iterator.
180    void
181    close(void)
182    {
183        PRE(_dirp != NULL);
184        if (::closedir(_dirp) == -1) {
185            UNREACHABLE_MSG("Invalid dirp provided to closedir(3)");
186        }
187        _dirp = NULL;
188    }
189
190    /// Advances the directory entry to the next one.
191    ///
192    /// It is possible to use this function on a new directory_entry object to
193    /// initialize the first entry.
194    ///
195    /// \throw system_error If the call to readdir_r fails.
196    void
197    next(void)
198    {
199        ::dirent* result;
200
201        if (::readdir_r(_dirp, &_dirent, &result) == -1) {
202            const int original_errno = errno;
203            throw fs::system_error(F("readdir_r(%s) failed") % _path,
204                                   original_errno);
205        }
206        if (result == NULL) {
207            _entry.reset(NULL);
208            close();
209        } else {
210            _entry.reset(new directory_entry(_dirent.d_name));
211        }
212    }
213};
214
215
216/// Constructs a new directory iterator.
217///
218/// \param pimpl The constructed internal implementation structure to use.
219detail::directory_iterator::directory_iterator(std::shared_ptr< impl > pimpl) :
220    _pimpl(pimpl)
221{
222}
223
224
225/// Destructor.
226detail::directory_iterator::~directory_iterator(void)
227{
228}
229
230
231/// Creates a new directory iterator for a directory.
232///
233/// \return The directory iterator.  Note that the result may be invalid.
234///
235/// \throw system_error If opening the directory or reading its first entry
236///     fails.
237detail::directory_iterator
238detail::directory_iterator::new_begin(const path& path)
239{
240    return directory_iterator(std::shared_ptr< impl >(new impl(path)));
241}
242
243
244/// Creates a new invalid directory iterator.
245///
246/// \return The invalid directory iterator.
247detail::directory_iterator
248detail::directory_iterator::new_end(void)
249{
250    return directory_iterator(std::shared_ptr< impl >(new impl()));
251}
252
253
254/// Checks if two iterators are equal.
255///
256/// We consider two iterators to be equal if both of them are invalid or,
257/// otherwise, if they have the exact same internal representation (as given by
258/// equality of the pimpl pointers).
259///
260/// \param other The object to compare to.
261///
262/// \return True if the two iterators are equal; false otherwise.
263bool
264detail::directory_iterator::operator==(const directory_iterator& other) const
265{
266    return (_pimpl->_dirp == NULL && other._pimpl->_dirp == NULL) ||
267        _pimpl == other._pimpl;
268}
269
270
271/// Checks if two iterators are different.
272///
273/// \param other The object to compare to.
274///
275/// \return True if the two iterators are different; false otherwise.
276bool
277detail::directory_iterator::operator!=(const directory_iterator& other) const
278{
279    return !(*this == other);
280}
281
282
283/// Moves the iterator one element forward.
284///
285/// \return A reference to the iterator.
286///
287/// \throw system_error If advancing the iterator fails.
288detail::directory_iterator&
289detail::directory_iterator::operator++(void)
290{
291    _pimpl->next();
292    return *this;
293}
294
295
296/// Dereferences the iterator to its contents.
297///
298/// \return A reference to the directory entry pointed to by the iterator.
299const fs::directory_entry&
300detail::directory_iterator::operator*(void) const
301{
302    PRE(_pimpl->_entry.get() != NULL);
303    return *_pimpl->_entry;
304}
305
306
307/// Dereferences the iterator to its contents.
308///
309/// \return A pointer to the directory entry pointed to by the iterator.
310const fs::directory_entry*
311detail::directory_iterator::operator->(void) const
312{
313    PRE(_pimpl->_entry.get() != NULL);
314    return _pimpl->_entry.get();
315}
316
317
318/// Internal implementation details for the directory.
319struct utils::fs::directory::impl : utils::noncopyable {
320    /// Path to the directory to scan.
321    fs::path _path;
322
323    /// Constructs a new directory.
324    ///
325    /// \param path_ Path to the directory to scan.
326    impl(const fs::path& path_) : _path(path_)
327    {
328    }
329};
330
331
332/// Constructs a new directory.
333///
334/// \param path_ Path to the directory to scan.
335fs::directory::directory(const path& path_) : _pimpl(new impl(path_))
336{
337}
338
339
340/// Returns an iterator to start scanning the directory.
341///
342/// \return An iterator on the directory.
343///
344/// \throw system_error If the directory cannot be opened to obtain its first
345///     entry.
346fs::directory::const_iterator
347fs::directory::begin(void) const
348{
349    return const_iterator::new_begin(_pimpl->_path);
350}
351
352
353/// Returns an invalid iterator to check for the end of an scan.
354///
355/// \return An invalid iterator.
356fs::directory::const_iterator
357fs::directory::end(void) const
358{
359    return const_iterator::new_end();
360}
361