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 #include <limits.h> 36 } 37 38 #include <cerrno> 39 #include <memory> 40 41 #include "utils/format/macros.hpp" 42 #include "utils/fs/exceptions.hpp" 43 #include "utils/fs/path.hpp" 44 #include "utils/noncopyable.hpp" 45 #include "utils/sanity.hpp" 46 #include "utils/text/operations.ipp" 47 48 namespace detail = utils::fs::detail; 49 namespace fs = utils::fs; 50 namespace text = utils::text; 51 52 53 /// Constructs a new directory entry. 54 /// 55 /// \param name_ Name of the directory entry. 56 fs::directory_entry::directory_entry(const std::string& name_) : name(name_) 57 { 58 } 59 60 61 /// Checks if two directory entries are equal. 62 /// 63 /// \param other The entry to compare to. 64 /// 65 /// \return True if the two entries are equal; false otherwise. 66 bool 67 fs::directory_entry::operator==(const directory_entry& other) const 68 { 69 return name == other.name; 70 } 71 72 73 /// Checks if two directory entries are different. 74 /// 75 /// \param other The entry to compare to. 76 /// 77 /// \return True if the two entries are different; false otherwise. 78 bool 79 fs::directory_entry::operator!=(const directory_entry& other) const 80 { 81 return !(*this == other); 82 } 83 84 85 /// Checks if this entry sorts before another entry. 86 /// 87 /// \param other The entry to compare to. 88 /// 89 /// \return True if this entry sorts before the other entry; false otherwise. 90 bool 91 fs::directory_entry::operator<(const directory_entry& other) const 92 { 93 return name < other.name; 94 } 95 96 97 /// Formats a directory entry. 98 /// 99 /// \param output Stream into which to inject the formatted entry. 100 /// \param entry The entry to format. 101 /// 102 /// \return A reference to output. 103 std::ostream& 104 fs::operator<<(std::ostream& output, const directory_entry& entry) 105 { 106 output << F("directory_entry{name=%s}") % text::quote(entry.name, '\''); 107 return output; 108 } 109 110 111 /// Internal implementation details for the directory_iterator. 112 /// 113 /// In order to support multiple concurrent iterators over the same directory 114 /// object, this class is the one that performs all directory-level accesses. 115 /// In particular, even if it may seem surprising, this is the class that 116 /// handles the DIR object for the directory. 117 /// 118 /// Note that iterators implemented by this class do not rely on the container 119 /// directory class at all. This should not be relied on for object lifecycle 120 /// purposes. 121 struct utils::fs::detail::directory_iterator::impl : utils::noncopyable { 122 /// Path of the directory accessed by this iterator. 123 const fs::path _path; 124 125 /// Raw pointer to the system representation of the directory. 126 /// 127 /// We also use this to determine if the iterator is valid (at the end) or 128 /// not. A null pointer means an invalid iterator. 129 ::DIR* _dirp; 130 131 /// Custom representation of the directory entry. 132 std::unique_ptr< directory_entry > _entry; 133 134 /// Constructs an iterator pointing to the "end" of the directory. 135 impl(void) : _path("invalid-directory-entry"), _dirp(NULL) 136 { 137 } 138 139 /// Constructs a new iterator to start scanning a directory. 140 /// 141 /// \param path The directory that will be scanned. 142 /// 143 /// \throw system_error If there is a problem opening the directory. 144 explicit impl(const path& path) : _path(path) 145 { 146 DIR* dirp = ::opendir(_path.c_str()); 147 if (dirp == NULL) { 148 const int original_errno = errno; 149 throw fs::system_error(F("opendir(%s) failed") % _path, 150 original_errno); 151 } 152 _dirp = dirp; 153 154 // Initialize our first directory entry. Note that this may actually 155 // close the directory we just opened if the directory happens to be 156 // empty -- but directories are never empty because they at least have 157 // '.' and '..' entries. 158 next(); 159 } 160 161 /// Destructor. 162 /// 163 /// This closes the directory if still open. 164 ~impl(void) 165 { 166 if (_dirp != NULL) 167 close(); 168 } 169 170 /// Closes the directory and invalidates the iterator. 171 void 172 close(void) 173 { 174 PRE(_dirp != NULL); 175 if (::closedir(_dirp) == -1) { 176 UNREACHABLE_MSG("Invalid dirp provided to closedir(3)"); 177 } 178 _dirp = NULL; 179 } 180 181 /// Advances the directory entry to the next one. 182 /// 183 /// It is possible to use this function on a new directory_entry object to 184 /// initialize the first entry. 185 /// 186 /// \throw system_error If the call to readdir fails. 187 void 188 next(void) 189 { 190 ::dirent* result; 191 192 // TODO(ngie): use `std::filesystem::directory_iterator` once the 193 // minimum C++ standard is C++20. 194 result = ::readdir(_dirp); 195 if (result == NULL) { 196 _entry.reset(); 197 close(); 198 } else { 199 _entry.reset(new directory_entry(result->d_name)); 200 } 201 } 202 }; 203 204 205 /// Constructs a new directory iterator. 206 /// 207 /// \param pimpl The constructed internal implementation structure to use. 208 detail::directory_iterator::directory_iterator(std::shared_ptr< impl > pimpl) : 209 _pimpl(pimpl) 210 { 211 } 212 213 214 /// Destructor. 215 detail::directory_iterator::~directory_iterator(void) 216 { 217 } 218 219 220 /// Creates a new directory iterator for a directory. 221 /// 222 /// \return The directory iterator. Note that the result may be invalid. 223 /// 224 /// \throw system_error If opening the directory or reading its first entry 225 /// fails. 226 detail::directory_iterator 227 detail::directory_iterator::new_begin(const path& path) 228 { 229 return directory_iterator(std::shared_ptr< impl >(new impl(path))); 230 } 231 232 233 /// Creates a new invalid directory iterator. 234 /// 235 /// \return The invalid directory iterator. 236 detail::directory_iterator 237 detail::directory_iterator::new_end(void) 238 { 239 return directory_iterator(std::shared_ptr< impl >(new impl())); 240 } 241 242 243 /// Checks if two iterators are equal. 244 /// 245 /// We consider two iterators to be equal if both of them are invalid or, 246 /// otherwise, if they have the exact same internal representation (as given by 247 /// equality of the pimpl pointers). 248 /// 249 /// \param other The object to compare to. 250 /// 251 /// \return True if the two iterators are equal; false otherwise. 252 bool 253 detail::directory_iterator::operator==(const directory_iterator& other) const 254 { 255 return (_pimpl->_dirp == NULL && other._pimpl->_dirp == NULL) || 256 _pimpl == other._pimpl; 257 } 258 259 260 /// Checks if two iterators are different. 261 /// 262 /// \param other The object to compare to. 263 /// 264 /// \return True if the two iterators are different; false otherwise. 265 bool 266 detail::directory_iterator::operator!=(const directory_iterator& other) const 267 { 268 return !(*this == other); 269 } 270 271 272 /// Moves the iterator one element forward. 273 /// 274 /// \return A reference to the iterator. 275 /// 276 /// \throw system_error If advancing the iterator fails. 277 detail::directory_iterator& 278 detail::directory_iterator::operator++(void) 279 { 280 _pimpl->next(); 281 return *this; 282 } 283 284 285 /// Dereferences the iterator to its contents. 286 /// 287 /// \return A reference to the directory entry pointed to by the iterator. 288 const fs::directory_entry& 289 detail::directory_iterator::operator*(void) const 290 { 291 PRE(_pimpl->_entry.get() != NULL); 292 return *_pimpl->_entry; 293 } 294 295 296 /// Dereferences the iterator to its contents. 297 /// 298 /// \return A pointer to the directory entry pointed to by the iterator. 299 const fs::directory_entry* 300 detail::directory_iterator::operator->(void) const 301 { 302 PRE(_pimpl->_entry.get() != NULL); 303 return _pimpl->_entry.get(); 304 } 305 306 307 /// Internal implementation details for the directory. 308 struct utils::fs::directory::impl : utils::noncopyable { 309 /// Path to the directory to scan. 310 fs::path _path; 311 312 /// Constructs a new directory. 313 /// 314 /// \param path_ Path to the directory to scan. 315 impl(const fs::path& path_) : _path(path_) 316 { 317 } 318 }; 319 320 321 /// Constructs a new directory. 322 /// 323 /// \param path_ Path to the directory to scan. 324 fs::directory::directory(const path& path_) : _pimpl(new impl(path_)) 325 { 326 } 327 328 329 /// Returns an iterator to start scanning the directory. 330 /// 331 /// \return An iterator on the directory. 332 /// 333 /// \throw system_error If the directory cannot be opened to obtain its first 334 /// entry. 335 fs::directory::const_iterator 336 fs::directory::begin(void) const 337 { 338 return const_iterator::new_begin(_pimpl->_path); 339 } 340 341 342 /// Returns an invalid iterator to check for the end of an scan. 343 /// 344 /// \return An invalid iterator. 345 fs::directory::const_iterator 346 fs::directory::end(void) const 347 { 348 return const_iterator::new_end(); 349 } 350