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 /// 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. 219 detail::directory_iterator::directory_iterator(std::shared_ptr< impl > pimpl) : 220 _pimpl(pimpl) 221 { 222 } 223 224 225 /// Destructor. 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 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 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 264 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 277 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& 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& 300 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* 311 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. 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. 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 347 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 357 fs::directory::end(void) const 358 { 359 return const_iterator::new_end(); 360 } 361