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 /// 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.
implutils::fs::detail::directory_iterator::impl144 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.
implutils::fs::detail::directory_iterator::impl153 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.
~implutils::fs::detail::directory_iterator::impl173 ~impl(void)
174 {
175 if (_dirp != NULL)
176 close();
177 }
178
179 /// Closes the directory and invalidates the iterator.
180 void
closeutils::fs::detail::directory_iterator::impl181 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
nextutils::fs::detail::directory_iterator::impl197 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.
directory_iterator(std::shared_ptr<impl> pimpl)219 detail::directory_iterator::directory_iterator(std::shared_ptr< impl > pimpl) :
220 _pimpl(pimpl)
221 {
222 }
223
224
225 /// Destructor.
~directory_iterator(void)226 detail::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.
237 detail::directory_iterator
new_begin(const path & path)238 detail::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.
247 detail::directory_iterator
new_end(void)248 detail::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.
263 bool
operator ==(const directory_iterator & other) const264 detail::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.
276 bool
operator !=(const directory_iterator & other) const277 detail::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.
288 detail::directory_iterator&
operator ++(void)289 detail::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.
299 const fs::directory_entry&
operator *(void) const300 detail::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.
310 const fs::directory_entry*
operator ->(void) const311 detail::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.
319 struct 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.
implutils::fs::directory::impl326 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.
directory(const path & path_)335 fs::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.
346 fs::directory::const_iterator
begin(void) const347 fs::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.
356 fs::directory::const_iterator
end(void) const357 fs::directory::end(void) const
358 {
359 return const_iterator::new_end();
360 }
361