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
31 extern "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
47 namespace detail = utils::fs::detail;
48 namespace fs = utils::fs;
49 namespace text = utils::text;
50
51
52 /// Constructs a new directory entry.
53 ///
54 /// \param name_ Name of the directory entry.
directory_entry(const std::string & name_)55 fs::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.
65 bool
operator ==(const directory_entry & other) const66 fs::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.
77 bool
operator !=(const directory_entry & other) const78 fs::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.
89 bool
operator <(const directory_entry & other) const90 fs::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.
102 std::ostream&
operator <<(std::ostream & output,const directory_entry & entry)103 fs::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.
120 struct 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 /// Custom representation of the directory entry.
131 ///
132 /// We must keep this as a pointer so that we can support the common
133 /// operators (* and ->) over iterators.
134 std::unique_ptr< directory_entry > _entry;
135
136 /// Constructs an iterator pointing to the "end" of the directory.
implutils::fs::detail::directory_iterator::impl137 impl(void) : _path("invalid-directory-entry"), _dirp(NULL)
138 {
139 }
140
141 /// Constructs a new iterator to start scanning a directory.
142 ///
143 /// \param path The directory that will be scanned.
144 ///
145 /// \throw system_error If there is a problem opening the directory.
implutils::fs::detail::directory_iterator::impl146 explicit impl(const path& path) : _path(path)
147 {
148 DIR* dirp = ::opendir(_path.c_str());
149 if (dirp == NULL) {
150 const int original_errno = errno;
151 throw fs::system_error(F("opendir(%s) failed") % _path,
152 original_errno);
153 }
154 _dirp = dirp;
155
156 // Initialize our first directory entry. Note that this may actually
157 // close the directory we just opened if the directory happens to be
158 // empty -- but directories are never empty because they at least have
159 // '.' and '..' entries.
160 next();
161 }
162
163 /// Destructor.
164 ///
165 /// This closes the directory if still open.
~implutils::fs::detail::directory_iterator::impl166 ~impl(void)
167 {
168 if (_dirp != NULL)
169 close();
170 }
171
172 /// Closes the directory and invalidates the iterator.
173 void
closeutils::fs::detail::directory_iterator::impl174 close(void)
175 {
176 PRE(_dirp != NULL);
177 if (::closedir(_dirp) == -1) {
178 UNREACHABLE_MSG("Invalid dirp provided to closedir(3)");
179 }
180 _dirp = NULL;
181 }
182
183 /// Advances the directory entry to the next one.
184 ///
185 /// It is possible to use this function on a new directory_entry object to
186 /// initialize the first entry.
187 ///
188 /// \throw system_error If the call to readdir fails.
189 void
nextutils::fs::detail::directory_iterator::impl190 next(void)
191 {
192 ::dirent* result;
193
194 errno = 0;
195 if ((result = ::readdir(_dirp)) == NULL && errno != 0) {
196 const int original_errno = errno;
197 throw fs::system_error(F("readdir(%s) failed") % _path,
198 original_errno);
199 }
200 if (result == NULL) {
201 _entry.reset();
202 close();
203 } else {
204 _entry.reset(new directory_entry(result->d_name));
205 }
206 }
207 };
208
209
210 /// Constructs a new directory iterator.
211 ///
212 /// \param pimpl The constructed internal implementation structure to use.
directory_iterator(std::shared_ptr<impl> pimpl)213 detail::directory_iterator::directory_iterator(std::shared_ptr< impl > pimpl) :
214 _pimpl(pimpl)
215 {
216 }
217
218
219 /// Destructor.
~directory_iterator(void)220 detail::directory_iterator::~directory_iterator(void)
221 {
222 }
223
224
225 /// Creates a new directory iterator for a directory.
226 ///
227 /// \return The directory iterator. Note that the result may be invalid.
228 ///
229 /// \throw system_error If opening the directory or reading its first entry
230 /// fails.
231 detail::directory_iterator
new_begin(const path & path)232 detail::directory_iterator::new_begin(const path& path)
233 {
234 return directory_iterator(std::shared_ptr< impl >(new impl(path)));
235 }
236
237
238 /// Creates a new invalid directory iterator.
239 ///
240 /// \return The invalid directory iterator.
241 detail::directory_iterator
new_end(void)242 detail::directory_iterator::new_end(void)
243 {
244 return directory_iterator(std::shared_ptr< impl >(new impl()));
245 }
246
247
248 /// Checks if two iterators are equal.
249 ///
250 /// We consider two iterators to be equal if both of them are invalid or,
251 /// otherwise, if they have the exact same internal representation (as given by
252 /// equality of the pimpl pointers).
253 ///
254 /// \param other The object to compare to.
255 ///
256 /// \return True if the two iterators are equal; false otherwise.
257 bool
operator ==(const directory_iterator & other) const258 detail::directory_iterator::operator==(const directory_iterator& other) const
259 {
260 return (_pimpl->_dirp == NULL && other._pimpl->_dirp == NULL) ||
261 _pimpl == other._pimpl;
262 }
263
264
265 /// Checks if two iterators are different.
266 ///
267 /// \param other The object to compare to.
268 ///
269 /// \return True if the two iterators are different; false otherwise.
270 bool
operator !=(const directory_iterator & other) const271 detail::directory_iterator::operator!=(const directory_iterator& other) const
272 {
273 return !(*this == other);
274 }
275
276
277 /// Moves the iterator one element forward.
278 ///
279 /// \return A reference to the iterator.
280 ///
281 /// \throw system_error If advancing the iterator fails.
282 detail::directory_iterator&
operator ++(void)283 detail::directory_iterator::operator++(void)
284 {
285 _pimpl->next();
286 return *this;
287 }
288
289
290 /// Dereferences the iterator to its contents.
291 ///
292 /// \return A reference to the directory entry pointed to by the iterator.
293 const fs::directory_entry&
operator *(void) const294 detail::directory_iterator::operator*(void) const
295 {
296 PRE(_pimpl->_entry.get() != NULL);
297 return *_pimpl->_entry;
298 }
299
300
301 /// Dereferences the iterator to its contents.
302 ///
303 /// \return A pointer to the directory entry pointed to by the iterator.
304 const fs::directory_entry*
operator ->(void) const305 detail::directory_iterator::operator->(void) const
306 {
307 PRE(_pimpl->_entry.get() != NULL);
308 return _pimpl->_entry.get();
309 }
310
311
312 /// Internal implementation details for the directory.
313 struct utils::fs::directory::impl : utils::noncopyable {
314 /// Path to the directory to scan.
315 fs::path _path;
316
317 /// Constructs a new directory.
318 ///
319 /// \param path_ Path to the directory to scan.
implutils::fs::directory::impl320 impl(const fs::path& path_) : _path(path_)
321 {
322 }
323 };
324
325
326 /// Constructs a new directory.
327 ///
328 /// \param path_ Path to the directory to scan.
directory(const path & path_)329 fs::directory::directory(const path& path_) : _pimpl(new impl(path_))
330 {
331 }
332
333
334 /// Returns an iterator to start scanning the directory.
335 ///
336 /// \return An iterator on the directory.
337 ///
338 /// \throw system_error If the directory cannot be opened to obtain its first
339 /// entry.
340 fs::directory::const_iterator
begin(void) const341 fs::directory::begin(void) const
342 {
343 return const_iterator::new_begin(_pimpl->_path);
344 }
345
346
347 /// Returns an invalid iterator to check for the end of an scan.
348 ///
349 /// \return An invalid iterator.
350 fs::directory::const_iterator
end(void) const351 fs::directory::end(void) const
352 {
353 return const_iterator::new_end();
354 }
355