1 // Copyright 2010 Google Inc. 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/operations.hpp" 30 31 #if defined(HAVE_CONFIG_H) 32 # include "config.h" 33 #endif 34 35 extern "C" { 36 #include <sys/stat.h> 37 38 #include <dirent.h> 39 #include <unistd.h> 40 } 41 42 #include <cerrno> 43 #include <cstdlib> 44 #include <cstring> 45 #include <sstream> 46 #include <string> 47 48 #include "utils/auto_array.ipp" 49 #include "utils/defs.hpp" 50 #include "utils/env.hpp" 51 #include "utils/format/macros.hpp" 52 #include "utils/fs/exceptions.hpp" 53 #include "utils/fs/path.hpp" 54 #include "utils/logging/macros.hpp" 55 #include "utils/optional.ipp" 56 #include "utils/sanity.hpp" 57 58 namespace fs = utils::fs; 59 60 using utils::optional; 61 62 63 namespace { 64 65 66 /// Stats a file, without following links. 67 /// 68 /// \param path The file to stat. 69 /// 70 /// \return The stat structure on success. 71 /// 72 /// \throw system_error An error on failure. 73 static struct ::stat 74 safe_stat(const fs::path& path) 75 { 76 struct ::stat sb; 77 if (::lstat(path.c_str(), &sb) == -1) { 78 const int original_errno = errno; 79 throw fs::system_error(F("Cannot get information about %s") % path, 80 original_errno); 81 } 82 return sb; 83 } 84 85 86 } // anonymous namespace 87 88 89 /// Queries the path to the current directory. 90 /// 91 /// \return The path to the current directory. 92 /// 93 /// \throw fs::error If there is a problem querying the current directory. 94 fs::path 95 fs::current_path(void) 96 { 97 char* cwd; 98 #if defined(HAVE_GETCWD_DYN) 99 cwd = ::getcwd(NULL, 0); 100 #else 101 cwd = ::getcwd(NULL, MAXPATHLEN); 102 #endif 103 if (cwd == NULL) { 104 const int original_errno = errno; 105 throw fs::system_error(F("Failed to get current working directory"), 106 original_errno); 107 } 108 109 try { 110 const fs::path result(cwd); 111 std::free(cwd); 112 return result; 113 } catch (...) { 114 std::free(cwd); 115 throw; 116 } 117 } 118 119 120 /// Checks if a file exists. 121 /// 122 /// Be aware that this is racy in the same way as access(2) is. 123 /// 124 /// \param path The file to check the existance of. 125 /// 126 /// \return True if the file exists; false otherwise. 127 bool 128 fs::exists(const fs::path& path) 129 { 130 return ::access(path.c_str(), F_OK) == 0; 131 } 132 133 134 /// Locates a file in the PATH. 135 /// 136 /// \param name The file to locate. 137 /// 138 /// \return The path to the located file or none if it was not found. The 139 /// returned path is always absolute. 140 optional< fs::path > 141 fs::find_in_path(const char* name) 142 { 143 const optional< std::string > current_path = utils::getenv("PATH"); 144 if (!current_path || current_path.get().empty()) 145 return none; 146 147 std::istringstream path_input(current_path.get() + ":"); 148 std::string path_component; 149 while (std::getline(path_input, path_component, ':').good()) { 150 const fs::path candidate = path_component.empty() ? 151 fs::path(name) : (fs::path(path_component) / name); 152 if (exists(candidate)) { 153 if (candidate.is_absolute()) 154 return utils::make_optional(candidate); 155 else 156 return utils::make_optional(candidate.to_absolute()); 157 } 158 } 159 return none; 160 } 161 162 163 /// Creates a directory. 164 /// 165 /// \param dir The path to the directory to create. 166 /// \param mode The permissions for the new directory. 167 /// 168 /// \throw system_error If the call to mkdir(2) fails. 169 void 170 fs::mkdir(const fs::path& dir, const int mode) 171 { 172 if (::mkdir(dir.c_str(), static_cast< mode_t >(mode)) == -1) { 173 const int original_errno = errno; 174 throw fs::system_error(F("Failed to create directory %s") % dir, 175 original_errno); 176 } 177 } 178 179 180 /// Creates a directory and any missing parents. 181 /// 182 /// This is separate from the fs::mkdir function to clearly differentiate the 183 /// libc wrapper from the more complex algorithm implemented here. 184 /// 185 /// \param dir The path to the directory to create. 186 /// \param mode The permissions for the new directories. 187 /// 188 /// \throw system_error If any call to mkdir(2) fails. 189 void 190 fs::mkdir_p(const fs::path& dir, const int mode) 191 { 192 try { 193 fs::mkdir(dir, mode); 194 } catch (const fs::system_error& e) { 195 if (e.original_errno() == ENOENT) { 196 fs::mkdir_p(dir.branch_path(), mode); 197 fs::mkdir(dir, mode); 198 } else if (e.original_errno() != EEXIST) 199 throw e; 200 } 201 } 202 203 204 /// Creates a temporary directory. 205 /// 206 /// The temporary directory is created using mkdtemp(3) using the provided 207 /// template. This should be most likely used in conjunction with 208 /// fs::auto_directory. 209 /// 210 /// \param path_template The template for the temporary path, which is a 211 /// basename that is created within the TMPDIR. Must contain the XXXXXX 212 /// pattern, which is atomically replaced by a random unique string. 213 /// 214 /// \return The generated path for the temporary directory. 215 /// 216 /// \throw fs::system_error If the call to mkdtemp(3) fails. 217 fs::path 218 fs::mkdtemp(const std::string& path_template) 219 { 220 PRE(path_template.find("XXXXXX") != std::string::npos); 221 222 const fs::path tmpdir(utils::getenv_with_default("TMPDIR", "/tmp")); 223 const fs::path full_template = tmpdir / path_template; 224 225 utils::auto_array< char > buf(new char[full_template.str().length() + 1]); 226 std::strcpy(buf.get(), full_template.c_str()); 227 if (::mkdtemp(buf.get()) == NULL) { 228 const int original_errno = errno; 229 throw fs::system_error(F("Cannot create temporary directory using " 230 "template %s") % full_template, 231 original_errno); 232 } 233 return fs::path(buf.get()); 234 } 235 236 237 /// Creates a temporary file. 238 /// 239 /// The temporary file is created using mkstemp(3) using the provided template. 240 /// This should be most likely used in conjunction with fs::auto_file. 241 /// 242 /// \param path_template The template for the temporary path, which is a 243 /// basename that is created within the TMPDIR. Must contain the XXXXXX 244 /// pattern, which is atomically replaced by a random unique string. 245 /// 246 /// \return The generated path for the temporary directory. 247 /// 248 /// \throw fs::system_error If the call to mkstemp(3) fails. 249 fs::path 250 fs::mkstemp(const std::string& path_template) 251 { 252 PRE(path_template.find("XXXXXX") != std::string::npos); 253 254 const fs::path tmpdir(utils::getenv_with_default("TMPDIR", "/tmp")); 255 const fs::path full_template = tmpdir / path_template; 256 257 utils::auto_array< char > buf(new char[full_template.str().length() + 1]); 258 std::strcpy(buf.get(), full_template.c_str()); 259 if (::mkstemp(buf.get()) == -1) { 260 const int original_errno = errno; 261 throw fs::system_error(F("Cannot create temporary file using template " 262 "%s") % full_template, original_errno); 263 } 264 return fs::path(buf.get()); 265 } 266 267 268 /// Recursively removes a directory. 269 /// 270 /// This operation simulates a "rm -r". No effort is made to forcibly delete 271 /// files and no attention is paid to mount points. 272 /// 273 /// \param directory The directory to remove. 274 /// 275 /// \throw fs::error If there is a problem removing any directory or file. 276 void 277 fs::rm_r(const fs::path& directory) 278 { 279 DIR* dirp = ::opendir(directory.c_str()); 280 if (dirp == NULL) { 281 const int original_errno = errno; 282 throw fs::system_error(F("Failed to open directory %s") % 283 directory.str(), original_errno); 284 } 285 try { 286 ::dirent* dp; 287 while ((dp = ::readdir(dirp)) != NULL) { 288 const std::string name = dp->d_name; 289 if (name == "." || name == "..") 290 continue; 291 292 const fs::path entry = directory / dp->d_name; 293 294 const struct ::stat sb = safe_stat(entry); 295 if (S_ISDIR(sb.st_mode)) { 296 LD(F("Descending into %s") % entry); 297 fs::rm_r(entry); 298 } else { 299 LD(F("Removing file %s") % entry); 300 fs::unlink(entry); 301 } 302 } 303 } catch (...) { 304 ::closedir(dirp); 305 throw; 306 } 307 ::closedir(dirp); 308 309 LD(F("Removing empty directory %s") % directory); 310 fs::rmdir(directory); 311 } 312 313 314 /// Removes an empty directory. 315 /// 316 /// \param file The directory to remove. 317 /// 318 /// \throw fs::system_error If the call to rmdir(2) fails. 319 void 320 fs::rmdir(const path& file) 321 { 322 if (::rmdir(file.c_str()) == -1) { 323 const int original_errno = errno; 324 throw fs::system_error(F("Removal of %s failed") % file, 325 original_errno); 326 } 327 } 328 329 330 /// Removes a file. 331 /// 332 /// \param file The file to remove. 333 /// 334 /// \throw fs::system_error If the call to unlink(2) fails. 335 void 336 fs::unlink(const path& file) 337 { 338 if (::unlink(file.c_str()) == -1) { 339 const int original_errno = errno; 340 throw fs::system_error(F("Removal of %s failed") % file, 341 original_errno); 342 } 343 } 344