PathV2.inc revision 936671b2eaa0a6b27903f8d85a8f4b28fcf8ee84
1//===- llvm/Support/Unix/PathV2.cpp - Unix Path Implementation --*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file implements the Unix specific implementation of the PathV2 API. 11// 12//===----------------------------------------------------------------------===// 13 14//===----------------------------------------------------------------------===// 15//=== WARNING: Implementation here must contain only generic UNIX code that 16//=== is guaranteed to work on *all* UNIX variants. 17//===----------------------------------------------------------------------===// 18 19#include "Unix.h" 20#if HAVE_SYS_STAT_H 21#include <sys/stat.h> 22#endif 23#if HAVE_FCNTL_H 24#include <fcntl.h> 25#endif 26#if HAVE_STDIO_H 27#include <stdio.h> 28#endif 29 30using namespace llvm; 31 32namespace { 33 /// This class automatically closes the given file descriptor when it goes out 34 /// of scope. You can take back explicit ownership of the file descriptor by 35 /// calling take(). The destructor does not verify that close was successful. 36 /// Therefore, never allow this class to call close on a file descriptor that 37 /// has been read from or written to. 38 struct AutoFD { 39 int FileDescriptor; 40 41 AutoFD(int fd) : FileDescriptor(fd) {} 42 ~AutoFD() { 43 if (FileDescriptor >= 0) 44 ::close(FileDescriptor); 45 } 46 47 int take() { 48 int ret = FileDescriptor; 49 FileDescriptor = -1; 50 return ret; 51 } 52 53 operator int() const {return FileDescriptor;} 54 }; 55 56 error_code TempDir(SmallVectorImpl<char> &result) { 57 // FIXME: Don't use TMPDIR if program is SUID or SGID enabled. 58 const char *dir = 0; 59 (dir = std::getenv("TMPDIR" )) || 60 (dir = std::getenv("TMP" )) || 61 (dir = std::getenv("TEMP" )) || 62 (dir = std::getenv("TEMPDIR")) || 63#ifdef P_tmpdir 64 (dir = P_tmpdir) || 65#endif 66 (dir = "/tmp"); 67 68 result.clear(); 69 StringRef d(dir); 70 result.append(d.begin(), d.end()); 71 return success; 72 } 73} 74 75namespace llvm { 76namespace sys { 77namespace fs { 78 79error_code current_path(SmallVectorImpl<char> &result) { 80 result.reserve(MAXPATHLEN); 81 82 while (true) { 83 if (::getcwd(result.data(), result.capacity()) == 0) { 84 // See if there was a real error. 85 if (errno != errc::not_enough_memory) 86 return error_code(errno, system_category()); 87 // Otherwise there just wasn't enough space. 88 result.reserve(result.capacity() * 2); 89 } else 90 break; 91 } 92 93 result.set_size(strlen(result.data())); 94 return success; 95} 96 97error_code copy_file(const Twine &from, const Twine &to, copy_option copt) { 98 // Get arguments. 99 SmallString<128> from_storage; 100 SmallString<128> to_storage; 101 StringRef f = from.toNullTerminatedStringRef(from_storage); 102 StringRef t = to.toNullTerminatedStringRef(to_storage); 103 104 const size_t buf_sz = 32768; 105 char buffer[buf_sz]; 106 int from_file = -1, to_file = -1; 107 108 // Open from. 109 if ((from_file = ::open(f.begin(), O_RDONLY)) < 0) 110 return error_code(errno, system_category()); 111 AutoFD from_fd(from_file); 112 113 // Stat from. 114 struct stat from_stat; 115 if (::stat(f.begin(), &from_stat) != 0) 116 return error_code(errno, system_category()); 117 118 // Setup to flags. 119 int to_flags = O_CREAT | O_WRONLY; 120 if (copt == copy_option::fail_if_exists) 121 to_flags |= O_EXCL; 122 123 // Open to. 124 if ((to_file = ::open(t.begin(), to_flags, from_stat.st_mode)) < 0) 125 return error_code(errno, system_category()); 126 AutoFD to_fd(to_file); 127 128 // Copy! 129 ssize_t sz, sz_read = 1, sz_write; 130 while (sz_read > 0 && 131 (sz_read = ::read(from_fd, buffer, buf_sz)) > 0) { 132 // Allow for partial writes - see Advanced Unix Programming (2nd Ed.), 133 // Marc Rochkind, Addison-Wesley, 2004, page 94 134 sz_write = 0; 135 do { 136 if ((sz = ::write(to_fd, buffer + sz_write, sz_read - sz_write)) < 0) { 137 sz_read = sz; // cause read loop termination. 138 break; // error. 139 } 140 sz_write += sz; 141 } while (sz_write < sz_read); 142 } 143 144 // After all the file operations above the return value of close actually 145 // matters. 146 if (::close(from_fd.take()) < 0) sz_read = -1; 147 if (::close(to_fd.take()) < 0) sz_read = -1; 148 149 // Check for errors. 150 if (sz_read < 0) 151 return error_code(errno, system_category()); 152 153 return success; 154} 155 156error_code create_directory(const Twine &path, bool &existed) { 157 SmallString<128> path_storage; 158 StringRef p = path.toNullTerminatedStringRef(path_storage); 159 160 if (::mkdir(p.begin(), S_IRWXU | S_IRWXG) == -1) { 161 if (errno != errc::file_exists) 162 return error_code(errno, system_category()); 163 existed = true; 164 } else 165 existed = false; 166 167 return success; 168} 169 170error_code create_hard_link(const Twine &to, const Twine &from) { 171 // Get arguments. 172 SmallString<128> from_storage; 173 SmallString<128> to_storage; 174 StringRef f = from.toNullTerminatedStringRef(from_storage); 175 StringRef t = to.toNullTerminatedStringRef(to_storage); 176 177 if (::link(t.begin(), f.begin()) == -1) 178 return error_code(errno, system_category()); 179 180 return success; 181} 182 183error_code create_symlink(const Twine &to, const Twine &from) { 184 // Get arguments. 185 SmallString<128> from_storage; 186 SmallString<128> to_storage; 187 StringRef f = from.toNullTerminatedStringRef(from_storage); 188 StringRef t = to.toNullTerminatedStringRef(to_storage); 189 190 if (::symlink(t.begin(), f.begin()) == -1) 191 return error_code(errno, system_category()); 192 193 return success; 194} 195 196error_code remove(const Twine &path, bool &existed) { 197 SmallString<128> path_storage; 198 StringRef p = path.toNullTerminatedStringRef(path_storage); 199 200 if (::remove(p.begin()) == -1) { 201 if (errno != errc::no_such_file_or_directory) 202 return error_code(errno, system_category()); 203 existed = false; 204 } else 205 existed = true; 206 207 return success; 208} 209 210error_code rename(const Twine &from, const Twine &to) { 211 // Get arguments. 212 SmallString<128> from_storage; 213 SmallString<128> to_storage; 214 StringRef f = from.toNullTerminatedStringRef(from_storage); 215 StringRef t = to.toNullTerminatedStringRef(to_storage); 216 217 if (::rename(f.begin(), t.begin()) == -1) 218 return error_code(errno, system_category()); 219 220 return success; 221} 222 223error_code resize_file(const Twine &path, uint64_t size) { 224 SmallString<128> path_storage; 225 StringRef p = path.toNullTerminatedStringRef(path_storage); 226 227 if (::truncate(p.begin(), size) == -1) 228 return error_code(errno, system_category()); 229 230 return success; 231} 232 233error_code exists(const Twine &path, bool &result) { 234 SmallString<128> path_storage; 235 StringRef p = path.toNullTerminatedStringRef(path_storage); 236 237 struct stat status; 238 if (::stat(p.begin(), &status) == -1) { 239 if (errno != errc::no_such_file_or_directory) 240 return error_code(errno, system_category()); 241 result = false; 242 } else 243 result = true; 244 245 return success; 246} 247 248error_code equivalent(const Twine &A, const Twine &B, bool &result) { 249 // Get arguments. 250 SmallString<128> a_storage; 251 SmallString<128> b_storage; 252 StringRef a = A.toNullTerminatedStringRef(a_storage); 253 StringRef b = B.toNullTerminatedStringRef(b_storage); 254 255 struct stat stat_a, stat_b; 256 int error_b = ::stat(b.begin(), &stat_b); 257 int error_a = ::stat(a.begin(), &stat_a); 258 259 // If both are invalid, it's an error. If only one is, the result is false. 260 if (error_a != 0 || error_b != 0) { 261 if (error_a == error_b) 262 return error_code(errno, system_category()); 263 result = false; 264 } else { 265 result = 266 stat_a.st_dev == stat_b.st_dev && 267 stat_a.st_ino == stat_b.st_ino; 268 } 269 270 return success; 271} 272 273error_code file_size(const Twine &path, uint64_t &result) { 274 SmallString<128> path_storage; 275 StringRef p = path.toNullTerminatedStringRef(path_storage); 276 277 struct stat status; 278 if (::stat(p.begin(), &status) == -1) 279 return error_code(errno, system_category()); 280 if (!S_ISREG(status.st_mode)) 281 return make_error_code(errc::operation_not_permitted); 282 283 result = status.st_size; 284 return success; 285} 286 287error_code status(const Twine &path, file_status &result) { 288 SmallString<128> path_storage; 289 StringRef p = path.toNullTerminatedStringRef(path_storage); 290 291 struct stat status; 292 if (::stat(p.begin(), &status) != 0) { 293 error_code ec(errno, system_category()); 294 if (ec == errc::no_such_file_or_directory) 295 result = file_status(file_type::file_not_found); 296 else 297 result = file_status(file_type::status_error); 298 return ec; 299 } 300 301 if (S_ISDIR(status.st_mode)) 302 result = file_status(file_type::directory_file); 303 else if (S_ISREG(status.st_mode)) 304 result = file_status(file_type::regular_file); 305 else if (S_ISBLK(status.st_mode)) 306 result = file_status(file_type::block_file); 307 else if (S_ISCHR(status.st_mode)) 308 result = file_status(file_type::character_file); 309 else if (S_ISFIFO(status.st_mode)) 310 result = file_status(file_type::fifo_file); 311 else if (S_ISSOCK(status.st_mode)) 312 result = file_status(file_type::socket_file); 313 else 314 result = file_status(file_type::type_unknown); 315 316 return success; 317} 318 319error_code unique_file(const Twine &model, int &result_fd, 320 SmallVectorImpl<char> &result_path) { 321 SmallString<128> Model; 322 model.toVector(Model); 323 // Null terminate. 324 Model.c_str(); 325 326 // Make model absolute by prepending a temp directory if it's not already. 327 bool absolute; 328 path::is_absolute(Twine(Model), absolute); 329 if (!absolute) { 330 SmallString<128> TDir; 331 if (error_code ec = TempDir(TDir)) return ec; 332 path::append(TDir, Twine(Model)); 333 Model.swap(TDir); 334 } 335 336 // Replace '%' with random chars. From here on, DO NOT modify model. It may be 337 // needed if the randomly chosen path already exists. 338 SmallString<128> RandomPath; 339 RandomPath.reserve(Model.size() + 1); 340 ::srand(::time(NULL)); 341 342retry_random_path: 343 // This is opened here instead of above to make it easier to track when to 344 // close it. Collisions should be rare enough for the possible extra syscalls 345 // not to matter. 346 FILE *RandomSource = ::fopen("/dev/urandom", "r"); 347 RandomPath.set_size(0); 348 for (SmallVectorImpl<char>::const_iterator i = Model.begin(), 349 e = Model.end(); i != e; ++i) { 350 if (*i == '%') { 351 char val = 0; 352 if (RandomSource) 353 val = fgetc(RandomSource); 354 else 355 val = ::rand(); 356 RandomPath.push_back("0123456789abcdef"[val & 15]); 357 } else 358 RandomPath.push_back(*i); 359 } 360 361 if (RandomSource) 362 ::fclose(RandomSource); 363 364 // Try to open + create the file. 365rety_open_create: 366 int RandomFD = ::open(RandomPath.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); 367 if (RandomFD == -1) { 368 // If the file existed, try again, otherwise, error. 369 if (errno == errc::file_exists) 370 goto retry_random_path; 371 // The path prefix doesn't exist. 372 if (errno == errc::no_such_file_or_directory) { 373 StringRef p(RandomPath.begin(), RandomPath.size()); 374 SmallString<64> dir_to_create; 375 for (path::const_iterator i = path::begin(p), 376 e = --path::end(p); i != e; ++i) { 377 path::append(dir_to_create, *i); 378 bool Exists; 379 if (error_code ec = exists(Twine(dir_to_create), Exists)) return ec; 380 if (!Exists) { 381 // Don't try to create network paths. 382 if (i->size() > 2 && (*i)[0] == '/' && 383 (*i)[1] == '/' && 384 (*i)[2] != '/') 385 return make_error_code(errc::no_such_file_or_directory); 386 if (::mkdir(dir_to_create.c_str(), 0700) == -1) 387 return error_code(errno, system_category()); 388 } 389 } 390 goto rety_open_create; 391 } 392 return error_code(errno, system_category()); 393 } 394 395 // Make the path absolute. 396 char real_path_buff[PATH_MAX + 1]; 397 if (realpath(RandomPath.c_str(), real_path_buff) == NULL) { 398 int error = errno; 399 ::close(RandomFD); 400 ::unlink(RandomPath.c_str()); 401 return error_code(error, system_category()); 402 } 403 404 result_path.clear(); 405 StringRef d(real_path_buff); 406 result_path.append(d.begin(), d.end()); 407 408 result_fd = RandomFD; 409 return success; 410} 411 412} // end namespace fs 413} // end namespace sys 414} // end namespace llvm 415