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_DIRENT_H 27# include <dirent.h> 28# define NAMLEN(dirent) strlen((dirent)->d_name) 29#else 30# define dirent direct 31# define NAMLEN(dirent) (dirent)->d_namlen 32# if HAVE_SYS_NDIR_H 33# include <sys/ndir.h> 34# endif 35# if HAVE_SYS_DIR_H 36# include <sys/dir.h> 37# endif 38# if HAVE_NDIR_H 39# include <ndir.h> 40# endif 41#endif 42#if HAVE_STDIO_H 43#include <stdio.h> 44#endif 45 46extern "C" int truncate (const char*, off_t); 47 48using namespace llvm; 49 50namespace { 51 /// This class automatically closes the given file descriptor when it goes out 52 /// of scope. You can take back explicit ownership of the file descriptor by 53 /// calling take(). The destructor does not verify that close was successful. 54 /// Therefore, never allow this class to call close on a file descriptor that 55 /// has been read from or written to. 56 struct AutoFD { 57 int FileDescriptor; 58 59 AutoFD(int fd) : FileDescriptor(fd) {} 60 ~AutoFD() { 61 if (FileDescriptor >= 0) 62 ::close(FileDescriptor); 63 } 64 65 int take() { 66 int ret = FileDescriptor; 67 FileDescriptor = -1; 68 return ret; 69 } 70 71 operator int() const {return FileDescriptor;} 72 }; 73 74 error_code TempDir(SmallVectorImpl<char> &result) { 75 // FIXME: Don't use TMPDIR if program is SUID or SGID enabled. 76 const char *dir = 0; 77 (dir = std::getenv("TMPDIR" )) || 78 (dir = std::getenv("TMP" )) || 79 (dir = std::getenv("TEMP" )) || 80 (dir = std::getenv("TEMPDIR")) || 81#ifdef P_tmpdir 82 (dir = P_tmpdir) || 83#endif 84 (dir = "/tmp"); 85 86 result.clear(); 87 StringRef d(dir); 88 result.append(d.begin(), d.end()); 89 return success; 90 } 91} 92 93namespace llvm { 94namespace sys { 95namespace fs { 96 97error_code current_path(SmallVectorImpl<char> &result) { 98 result.reserve(MAXPATHLEN); 99 100 while (true) { 101 if (::getcwd(result.data(), result.capacity()) == 0) { 102 // See if there was a real error. 103 if (errno != errc::not_enough_memory) 104 return error_code(errno, system_category()); 105 // Otherwise there just wasn't enough space. 106 result.reserve(result.capacity() * 2); 107 } else 108 break; 109 } 110 111 result.set_size(strlen(result.data())); 112 return success; 113} 114 115error_code copy_file(const Twine &from, const Twine &to, copy_option copt) { 116 // Get arguments. 117 SmallString<128> from_storage; 118 SmallString<128> to_storage; 119 StringRef f = from.toNullTerminatedStringRef(from_storage); 120 StringRef t = to.toNullTerminatedStringRef(to_storage); 121 122 const size_t buf_sz = 32768; 123 char buffer[buf_sz]; 124 int from_file = -1, to_file = -1; 125 126 // Open from. 127 if ((from_file = ::open(f.begin(), O_RDONLY)) < 0) 128 return error_code(errno, system_category()); 129 AutoFD from_fd(from_file); 130 131 // Stat from. 132 struct stat from_stat; 133 if (::stat(f.begin(), &from_stat) != 0) 134 return error_code(errno, system_category()); 135 136 // Setup to flags. 137 int to_flags = O_CREAT | O_WRONLY; 138 if (copt == copy_option::fail_if_exists) 139 to_flags |= O_EXCL; 140 141 // Open to. 142 if ((to_file = ::open(t.begin(), to_flags, from_stat.st_mode)) < 0) 143 return error_code(errno, system_category()); 144 AutoFD to_fd(to_file); 145 146 // Copy! 147 ssize_t sz, sz_read = 1, sz_write; 148 while (sz_read > 0 && 149 (sz_read = ::read(from_fd, buffer, buf_sz)) > 0) { 150 // Allow for partial writes - see Advanced Unix Programming (2nd Ed.), 151 // Marc Rochkind, Addison-Wesley, 2004, page 94 152 sz_write = 0; 153 do { 154 if ((sz = ::write(to_fd, buffer + sz_write, sz_read - sz_write)) < 0) { 155 sz_read = sz; // cause read loop termination. 156 break; // error. 157 } 158 sz_write += sz; 159 } while (sz_write < sz_read); 160 } 161 162 // After all the file operations above the return value of close actually 163 // matters. 164 if (::close(from_fd.take()) < 0) sz_read = -1; 165 if (::close(to_fd.take()) < 0) sz_read = -1; 166 167 // Check for errors. 168 if (sz_read < 0) 169 return error_code(errno, system_category()); 170 171 return success; 172} 173 174error_code create_directory(const Twine &path, bool &existed) { 175 SmallString<128> path_storage; 176 StringRef p = path.toNullTerminatedStringRef(path_storage); 177 178 if (::mkdir(p.begin(), S_IRWXU | S_IRWXG) == -1) { 179 if (errno != errc::file_exists) 180 return error_code(errno, system_category()); 181 existed = true; 182 } else 183 existed = false; 184 185 return success; 186} 187 188error_code create_hard_link(const Twine &to, const Twine &from) { 189 // Get arguments. 190 SmallString<128> from_storage; 191 SmallString<128> to_storage; 192 StringRef f = from.toNullTerminatedStringRef(from_storage); 193 StringRef t = to.toNullTerminatedStringRef(to_storage); 194 195 if (::link(t.begin(), f.begin()) == -1) 196 return error_code(errno, system_category()); 197 198 return success; 199} 200 201error_code create_symlink(const Twine &to, const Twine &from) { 202 // Get arguments. 203 SmallString<128> from_storage; 204 SmallString<128> to_storage; 205 StringRef f = from.toNullTerminatedStringRef(from_storage); 206 StringRef t = to.toNullTerminatedStringRef(to_storage); 207 208 if (::symlink(t.begin(), f.begin()) == -1) 209 return error_code(errno, system_category()); 210 211 return success; 212} 213 214error_code remove(const Twine &path, bool &existed) { 215 SmallString<128> path_storage; 216 StringRef p = path.toNullTerminatedStringRef(path_storage); 217 218 if (::remove(p.begin()) == -1) { 219 if (errno != errc::no_such_file_or_directory) 220 return error_code(errno, system_category()); 221 existed = false; 222 } else 223 existed = true; 224 225 return success; 226} 227 228error_code rename(const Twine &from, const Twine &to) { 229 // Get arguments. 230 SmallString<128> from_storage; 231 SmallString<128> to_storage; 232 StringRef f = from.toNullTerminatedStringRef(from_storage); 233 StringRef t = to.toNullTerminatedStringRef(to_storage); 234 235 if (::rename(f.begin(), t.begin()) == -1) { 236 // If it's a cross device link, copy then delete, otherwise return the error 237 if (errno == EXDEV) { 238 if (error_code ec = copy_file(from, to, copy_option::overwrite_if_exists)) 239 return ec; 240 bool Existed; 241 if (error_code ec = remove(from, Existed)) 242 return ec; 243 } else 244 return error_code(errno, system_category()); 245 } 246 247 return success; 248} 249 250error_code resize_file(const Twine &path, uint64_t size) { 251 SmallString<128> path_storage; 252 StringRef p = path.toNullTerminatedStringRef(path_storage); 253 254 if (::truncate(p.begin(), size) == -1) 255 return error_code(errno, system_category()); 256 257 return success; 258} 259 260error_code exists(const Twine &path, bool &result) { 261 SmallString<128> path_storage; 262 StringRef p = path.toNullTerminatedStringRef(path_storage); 263 264 struct stat status; 265 if (::stat(p.begin(), &status) == -1) { 266 if (errno != errc::no_such_file_or_directory) 267 return error_code(errno, system_category()); 268 result = false; 269 } else 270 result = true; 271 272 return success; 273} 274 275error_code equivalent(const Twine &A, const Twine &B, bool &result) { 276 // Get arguments. 277 SmallString<128> a_storage; 278 SmallString<128> b_storage; 279 StringRef a = A.toNullTerminatedStringRef(a_storage); 280 StringRef b = B.toNullTerminatedStringRef(b_storage); 281 282 struct stat stat_a, stat_b; 283 int error_b = ::stat(b.begin(), &stat_b); 284 int error_a = ::stat(a.begin(), &stat_a); 285 286 // If both are invalid, it's an error. If only one is, the result is false. 287 if (error_a != 0 || error_b != 0) { 288 if (error_a == error_b) 289 return error_code(errno, system_category()); 290 result = false; 291 } else { 292 result = 293 stat_a.st_dev == stat_b.st_dev && 294 stat_a.st_ino == stat_b.st_ino; 295 } 296 297 return success; 298} 299 300error_code file_size(const Twine &path, uint64_t &result) { 301 SmallString<128> path_storage; 302 StringRef p = path.toNullTerminatedStringRef(path_storage); 303 304 struct stat status; 305 if (::stat(p.begin(), &status) == -1) 306 return error_code(errno, system_category()); 307 if (!S_ISREG(status.st_mode)) 308 return make_error_code(errc::operation_not_permitted); 309 310 result = status.st_size; 311 return success; 312} 313 314error_code status(const Twine &path, file_status &result) { 315 SmallString<128> path_storage; 316 StringRef p = path.toNullTerminatedStringRef(path_storage); 317 318 struct stat status; 319 if (::stat(p.begin(), &status) != 0) { 320 error_code ec(errno, system_category()); 321 if (ec == errc::no_such_file_or_directory) 322 result = file_status(file_type::file_not_found); 323 else 324 result = file_status(file_type::status_error); 325 return ec; 326 } 327 328 if (S_ISDIR(status.st_mode)) 329 result = file_status(file_type::directory_file); 330 else if (S_ISREG(status.st_mode)) 331 result = file_status(file_type::regular_file); 332 else if (S_ISBLK(status.st_mode)) 333 result = file_status(file_type::block_file); 334 else if (S_ISCHR(status.st_mode)) 335 result = file_status(file_type::character_file); 336 else if (S_ISFIFO(status.st_mode)) 337 result = file_status(file_type::fifo_file); 338 else if (S_ISSOCK(status.st_mode)) 339 result = file_status(file_type::socket_file); 340 else 341 result = file_status(file_type::type_unknown); 342 343 return success; 344} 345 346error_code unique_file(const Twine &model, int &result_fd, 347 SmallVectorImpl<char> &result_path) { 348 SmallString<128> Model; 349 model.toVector(Model); 350 // Null terminate. 351 Model.c_str(); 352 353 // Make model absolute by prepending a temp directory if it's not already. 354 bool absolute = path::is_absolute(Twine(Model)); 355 if (!absolute) { 356 SmallString<128> TDir; 357 if (error_code ec = TempDir(TDir)) return ec; 358 path::append(TDir, Twine(Model)); 359 Model.swap(TDir); 360 } 361 362 // Replace '%' with random chars. From here on, DO NOT modify model. It may be 363 // needed if the randomly chosen path already exists. 364 SmallString<128> RandomPath; 365 RandomPath.reserve(Model.size() + 1); 366 ::srand(::time(NULL)); 367 368retry_random_path: 369 // This is opened here instead of above to make it easier to track when to 370 // close it. Collisions should be rare enough for the possible extra syscalls 371 // not to matter. 372 FILE *RandomSource = ::fopen("/dev/urandom", "r"); 373 RandomPath.set_size(0); 374 for (SmallVectorImpl<char>::const_iterator i = Model.begin(), 375 e = Model.end(); i != e; ++i) { 376 if (*i == '%') { 377 char val = 0; 378 if (RandomSource) 379 val = fgetc(RandomSource); 380 else 381 val = ::rand(); 382 RandomPath.push_back("0123456789abcdef"[val & 15]); 383 } else 384 RandomPath.push_back(*i); 385 } 386 387 if (RandomSource) 388 ::fclose(RandomSource); 389 390 // Try to open + create the file. 391rety_open_create: 392 int RandomFD = ::open(RandomPath.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); 393 if (RandomFD == -1) { 394 // If the file existed, try again, otherwise, error. 395 if (errno == errc::file_exists) 396 goto retry_random_path; 397 // The path prefix doesn't exist. 398 if (errno == errc::no_such_file_or_directory) { 399 StringRef p(RandomPath.begin(), RandomPath.size()); 400 SmallString<64> dir_to_create; 401 for (path::const_iterator i = path::begin(p), 402 e = --path::end(p); i != e; ++i) { 403 path::append(dir_to_create, *i); 404 bool Exists; 405 if (error_code ec = exists(Twine(dir_to_create), Exists)) return ec; 406 if (!Exists) { 407 // Don't try to create network paths. 408 if (i->size() > 2 && (*i)[0] == '/' && 409 (*i)[1] == '/' && 410 (*i)[2] != '/') 411 return make_error_code(errc::no_such_file_or_directory); 412 if (::mkdir(dir_to_create.c_str(), 0700) == -1) 413 return error_code(errno, system_category()); 414 } 415 } 416 goto rety_open_create; 417 } 418 return error_code(errno, system_category()); 419 } 420 421 // Make the path absolute. 422 char real_path_buff[PATH_MAX + 1]; 423 if (realpath(RandomPath.c_str(), real_path_buff) == NULL) { 424 int error = errno; 425 ::close(RandomFD); 426 ::unlink(RandomPath.c_str()); 427 return error_code(error, system_category()); 428 } 429 430 result_path.clear(); 431 StringRef d(real_path_buff); 432 result_path.append(d.begin(), d.end()); 433 434 result_fd = RandomFD; 435 return success; 436} 437 438error_code directory_iterator_construct(directory_iterator &it, StringRef path){ 439 SmallString<128> path_null(path); 440 DIR *directory = ::opendir(path_null.c_str()); 441 if (directory == 0) 442 return error_code(errno, system_category()); 443 444 it.IterationHandle = reinterpret_cast<intptr_t>(directory); 445 // Add something for replace_filename to replace. 446 path::append(path_null, "."); 447 it.CurrentEntry = directory_entry(path_null.str()); 448 return directory_iterator_increment(it); 449} 450 451error_code directory_iterator_destruct(directory_iterator& it) { 452 if (it.IterationHandle) 453 ::closedir(reinterpret_cast<DIR *>(it.IterationHandle)); 454 it.IterationHandle = 0; 455 it.CurrentEntry = directory_entry(); 456 return success; 457} 458 459error_code directory_iterator_increment(directory_iterator& it) { 460 errno = 0; 461 dirent *cur_dir = ::readdir(reinterpret_cast<DIR *>(it.IterationHandle)); 462 if (cur_dir == 0 && errno != 0) { 463 return error_code(errno, system_category()); 464 } else if (cur_dir != 0) { 465 StringRef name(cur_dir->d_name, NAMLEN(cur_dir)); 466 if ((name.size() == 1 && name[0] == '.') || 467 (name.size() == 2 && name[0] == '.' && name[1] == '.')) 468 return directory_iterator_increment(it); 469 it.CurrentEntry.replace_filename(name); 470 } else 471 return directory_iterator_destruct(it); 472 473 return success; 474} 475 476error_code get_magic(const Twine &path, uint32_t len, 477 SmallVectorImpl<char> &result) { 478 SmallString<128> PathStorage; 479 StringRef Path = path.toNullTerminatedStringRef(PathStorage); 480 result.set_size(0); 481 482 // Open path. 483 std::FILE *file = std::fopen(Path.data(), "rb"); 484 if (file == 0) 485 return error_code(errno, system_category()); 486 487 // Reserve storage. 488 result.reserve(len); 489 490 // Read magic! 491 size_t size = std::fread(result.data(), 1, len, file); 492 if (std::ferror(file) != 0) { 493 std::fclose(file); 494 return error_code(errno, system_category()); 495 } else if (size != result.size()) { 496 if (std::feof(file) != 0) { 497 std::fclose(file); 498 result.set_size(size); 499 return make_error_code(errc::value_too_large); 500 } 501 } 502 std::fclose(file); 503 result.set_size(len); 504 return success; 505} 506 507} // end namespace fs 508} // end namespace sys 509} // end namespace llvm 510