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. 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 66 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 78 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 90 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& 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. 137 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. 146 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. 166 ~impl(void) 167 { 168 if (_dirp != NULL) 169 close(); 170 } 171 172 /// Closes the directory and invalidates the iterator. 173 void 174 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 190 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. 213 detail::directory_iterator::directory_iterator(std::shared_ptr< impl > pimpl) : 214 _pimpl(pimpl) 215 { 216 } 217 218 219 /// Destructor. 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 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 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 258 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 271 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& 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& 294 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* 305 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. 320 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. 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 341 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 351 fs::directory::end(void) const 352 { 353 return const_iterator::new_end(); 354 } 355