• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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