2013-06-26 19:33:03 +00:00
|
|
|
//===- llvm/Support/Unix/Path.inc - Unix Path Implementation ----*- C++ -*-===//
|
2010-11-29 22:28:51 +00:00
|
|
|
//
|
2019-01-19 08:50:56 +00:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2010-11-29 22:28:51 +00:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2013-06-26 19:33:03 +00:00
|
|
|
// This file implements the Unix specific implementation of the Path API.
|
2010-11-29 22:28:51 +00:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//=== WARNING: Implementation here must contain only generic UNIX code that
|
|
|
|
//=== is guaranteed to work on *all* UNIX variants.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "Unix.h"
|
2016-04-05 20:45:04 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include <stdio.h>
|
2010-12-01 19:32:01 +00:00
|
|
|
#if HAVE_SYS_STAT_H
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#endif
|
|
|
|
#if HAVE_FCNTL_H
|
|
|
|
#include <fcntl.h>
|
|
|
|
#endif
|
2016-06-13 15:54:56 +00:00
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
2012-06-20 00:28:54 +00:00
|
|
|
#ifdef HAVE_SYS_MMAN_H
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#endif
|
2010-12-01 19:32:01 +00:00
|
|
|
|
2018-04-02 17:17:29 +00:00
|
|
|
#include <dirent.h>
|
2017-03-10 17:39:21 +00:00
|
|
|
#include <pwd.h>
|
|
|
|
|
2013-06-26 05:25:44 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <mach-o/dyld.h>
|
2016-06-13 15:54:56 +00:00
|
|
|
#include <sys/attr.h>
|
2019-04-18 21:22:50 +00:00
|
|
|
#include <copyfile.h>
|
2018-11-10 01:01:03 +00:00
|
|
|
#elif defined(__DragonFly__)
|
|
|
|
#include <sys/mount.h>
|
2013-06-26 05:25:44 +00:00
|
|
|
#endif
|
|
|
|
|
2016-11-20 13:47:59 +00:00
|
|
|
// Both stdio.h and cstdio are included via different paths and
|
2012-08-10 10:56:09 +00:00
|
|
|
// stdcxx's cstdio doesn't include stdio.h, so it doesn't #undef the macros
|
|
|
|
// either.
|
|
|
|
#undef ferror
|
|
|
|
#undef feof
|
|
|
|
|
2012-04-11 15:35:36 +00:00
|
|
|
// For GNU Hurd
|
|
|
|
#if defined(__GNU__) && !defined(PATH_MAX)
|
|
|
|
# define PATH_MAX 4096
|
2018-10-23 07:13:47 +00:00
|
|
|
# define MAXPATHLEN 4096
|
2012-04-11 15:35:36 +00:00
|
|
|
#endif
|
|
|
|
|
2016-04-01 00:18:08 +00:00
|
|
|
#include <sys/types.h>
|
2017-02-21 20:55:47 +00:00
|
|
|
#if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && \
|
2019-03-29 23:32:47 +00:00
|
|
|
!defined(__linux__) && !defined(__FreeBSD_kernel__) && !defined(_AIX)
|
2016-04-01 00:18:08 +00:00
|
|
|
#include <sys/statvfs.h>
|
|
|
|
#define STATVFS statvfs
|
2017-02-21 20:55:47 +00:00
|
|
|
#define FSTATVFS fstatvfs
|
2016-04-01 00:18:08 +00:00
|
|
|
#define STATVFS_F_FRSIZE(vfs) vfs.f_frsize
|
|
|
|
#else
|
2017-02-21 20:55:47 +00:00
|
|
|
#if defined(__OpenBSD__) || defined(__FreeBSD__)
|
2016-04-08 16:45:05 +00:00
|
|
|
#include <sys/mount.h>
|
2017-06-06 11:49:48 +00:00
|
|
|
#include <sys/param.h>
|
2017-02-21 20:55:47 +00:00
|
|
|
#elif defined(__linux__)
|
2017-02-22 18:09:15 +00:00
|
|
|
#if defined(HAVE_LINUX_MAGIC_H)
|
2017-02-21 20:55:47 +00:00
|
|
|
#include <linux/magic.h>
|
2017-02-22 18:09:15 +00:00
|
|
|
#else
|
|
|
|
#if defined(HAVE_LINUX_NFS_FS_H)
|
|
|
|
#include <linux/nfs_fs.h>
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_LINUX_SMB_H)
|
|
|
|
#include <linux/smb.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
2016-04-01 00:18:08 +00:00
|
|
|
#include <sys/vfs.h>
|
2019-03-29 23:32:47 +00:00
|
|
|
#elif defined(_AIX)
|
|
|
|
#include <sys/statfs.h>
|
|
|
|
|
|
|
|
// <sys/vmount.h> depends on `uint` to be a typedef from <sys/types.h> to
|
|
|
|
// `uint_t`; however, <sys/types.h> does not always declare `uint`. We provide
|
|
|
|
// the typedef prior to including <sys/vmount.h> to work around this issue.
|
|
|
|
typedef uint_t uint;
|
|
|
|
#include <sys/vmount.h>
|
2016-04-01 00:18:08 +00:00
|
|
|
#else
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#endif
|
|
|
|
#define STATVFS statfs
|
2017-02-21 20:55:47 +00:00
|
|
|
#define FSTATVFS fstatfs
|
2016-04-01 00:18:08 +00:00
|
|
|
#define STATVFS_F_FRSIZE(vfs) static_cast<uint64_t>(vfs.f_bsize)
|
|
|
|
#endif
|
|
|
|
|
2018-11-10 01:01:03 +00:00
|
|
|
#if defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__)
|
2017-02-21 20:55:47 +00:00
|
|
|
#define STATVFS_F_FLAG(vfs) (vfs).f_flag
|
|
|
|
#else
|
|
|
|
#define STATVFS_F_FLAG(vfs) (vfs).f_flags
|
|
|
|
#endif
|
2016-04-01 00:18:08 +00:00
|
|
|
|
2010-12-03 01:21:28 +00:00
|
|
|
using namespace llvm;
|
|
|
|
|
2010-11-29 22:28:51 +00:00
|
|
|
namespace llvm {
|
|
|
|
namespace sys {
|
2010-12-07 01:22:31 +00:00
|
|
|
namespace fs {
|
2018-06-04 19:38:11 +00:00
|
|
|
|
|
|
|
const file_t kInvalidFile = -1;
|
|
|
|
|
2019-03-04 04:53:50 +00:00
|
|
|
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
|
2017-07-21 22:48:47 +00:00
|
|
|
defined(__minix) || defined(__FreeBSD_kernel__) || defined(__linux__) || \
|
2018-10-23 07:13:47 +00:00
|
|
|
defined(__CYGWIN__) || defined(__DragonFly__) || defined(_AIX) || defined(__GNU__)
|
2016-04-05 20:45:04 +00:00
|
|
|
static int
|
2013-12-09 16:27:00 +00:00
|
|
|
test_dir(char ret[PATH_MAX], const char *dir, const char *bin)
|
2016-04-01 00:18:08 +00:00
|
|
|
{
|
2013-06-26 05:01:35 +00:00
|
|
|
struct stat sb;
|
2013-12-09 16:27:00 +00:00
|
|
|
char fullpath[PATH_MAX];
|
2013-06-26 05:01:35 +00:00
|
|
|
|
2019-03-13 10:37:56 +00:00
|
|
|
int chars = snprintf(fullpath, PATH_MAX, "%s/%s", dir, bin);
|
|
|
|
// We cannot write PATH_MAX characters because the string will be terminated
|
|
|
|
// with a null character. Fail if truncation happened.
|
|
|
|
if (chars >= PATH_MAX)
|
|
|
|
return 1;
|
2015-10-06 23:24:35 +00:00
|
|
|
if (!realpath(fullpath, ret))
|
|
|
|
return 1;
|
2013-12-09 16:27:00 +00:00
|
|
|
if (stat(fullpath, &sb) != 0)
|
2015-10-06 23:24:35 +00:00
|
|
|
return 1;
|
2013-06-26 05:01:35 +00:00
|
|
|
|
2015-10-06 23:24:35 +00:00
|
|
|
return 0;
|
2013-06-26 05:01:35 +00:00
|
|
|
}
|
|
|
|
|
2016-04-05 20:45:04 +00:00
|
|
|
static char *
|
2013-06-26 05:01:35 +00:00
|
|
|
getprogpath(char ret[PATH_MAX], const char *bin)
|
|
|
|
{
|
|
|
|
/* First approach: absolute path. */
|
|
|
|
if (bin[0] == '/') {
|
2013-12-09 16:27:00 +00:00
|
|
|
if (test_dir(ret, "/", bin) == 0)
|
2015-10-06 23:24:35 +00:00
|
|
|
return ret;
|
|
|
|
return nullptr;
|
2013-06-26 05:01:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Second approach: relative path. */
|
2015-10-06 23:24:35 +00:00
|
|
|
if (strchr(bin, '/')) {
|
2013-12-09 16:27:00 +00:00
|
|
|
char cwd[PATH_MAX];
|
2015-10-06 23:24:35 +00:00
|
|
|
if (!getcwd(cwd, PATH_MAX))
|
|
|
|
return nullptr;
|
2013-12-09 16:27:00 +00:00
|
|
|
if (test_dir(ret, cwd, bin) == 0)
|
2015-10-06 23:24:35 +00:00
|
|
|
return ret;
|
|
|
|
return nullptr;
|
2013-06-26 05:01:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Third approach: $PATH */
|
2019-05-16 14:02:13 +00:00
|
|
|
char *pv;
|
2015-10-06 23:24:35 +00:00
|
|
|
if ((pv = getenv("PATH")) == nullptr)
|
|
|
|
return nullptr;
|
2019-05-16 14:02:13 +00:00
|
|
|
char *s = strdup(pv);
|
|
|
|
if (!s)
|
2015-10-06 23:24:35 +00:00
|
|
|
return nullptr;
|
2019-05-16 14:02:13 +00:00
|
|
|
char *state;
|
|
|
|
for (char *t = strtok_r(s, ":", &state); t != nullptr;
|
|
|
|
t = strtok_r(nullptr, ":", &state)) {
|
2013-12-09 16:27:00 +00:00
|
|
|
if (test_dir(ret, t, bin) == 0) {
|
2019-05-16 14:02:13 +00:00
|
|
|
free(s);
|
2015-10-06 23:24:35 +00:00
|
|
|
return ret;
|
2013-06-26 05:01:35 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-16 14:02:13 +00:00
|
|
|
free(s);
|
2015-10-06 23:24:35 +00:00
|
|
|
return nullptr;
|
2013-06-26 05:01:35 +00:00
|
|
|
}
|
Revert r303015, because it has the unintended side effect of breaking
driver-mode recognition in clang (this is because the sysctl method
always returns one and only one executable path, even for an executable
with multiple links):
Fix DynamicLibraryTest.cpp on FreeBSD and NetBSD
Summary:
After rL301562, on FreeBSD the DynamicLibrary unittests fail, because
the test uses getMainExecutable("DynamicLibraryTests", Ptr), and since
the path does not contain any slashes, retrieving the main executable
will not work.
Reimplement getMainExecutable() for FreeBSD and NetBSD using sysctl(3),
which is more reliable than fiddling with relative or absolute paths.
Also add retrieval of the original argv[] from the GoogleTest framework,
to use as a fallback for other OSes.
Reviewers: emaste, marsupial, hans, krytarowski
Reviewed By: krytarowski
Subscribers: krytarowski, llvm-commits
Differential Revision: https://reviews.llvm.org/D33171
llvm-svn: 303285
2017-05-17 19:33:10 +00:00
|
|
|
#endif // __FreeBSD__ || __NetBSD__ || __FreeBSD_kernel__
|
2013-06-26 05:01:35 +00:00
|
|
|
|
|
|
|
/// GetMainExecutable - Return the path to the main executable, given the
|
|
|
|
/// value of argv[0] from program startup.
|
|
|
|
std::string getMainExecutable(const char *argv0, void *MainAddr) {
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
// On OS X the executable path is saved to the stack by dyld. Reading it
|
|
|
|
// from there is much faster than calling dladdr, especially for large
|
|
|
|
// binaries with symbols.
|
|
|
|
char exe_path[MAXPATHLEN];
|
|
|
|
uint32_t size = sizeof(exe_path);
|
|
|
|
if (_NSGetExecutablePath(exe_path, &size) == 0) {
|
|
|
|
char link_path[MAXPATHLEN];
|
|
|
|
if (realpath(exe_path, link_path))
|
2013-06-26 05:25:44 +00:00
|
|
|
return link_path;
|
2013-06-26 05:01:35 +00:00
|
|
|
}
|
2019-03-04 04:53:50 +00:00
|
|
|
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
|
2017-07-21 22:48:47 +00:00
|
|
|
defined(__minix) || defined(__DragonFly__) || \
|
|
|
|
defined(__FreeBSD_kernel__) || defined(_AIX)
|
2019-04-29 09:24:51 +00:00
|
|
|
StringRef curproc("/proc/curproc/file");
|
2013-06-26 05:01:35 +00:00
|
|
|
char exe_path[PATH_MAX];
|
2019-04-29 09:24:51 +00:00
|
|
|
// /proc is not mounted by default under FreeBSD, but gives more accurate
|
|
|
|
// information than argv[0] when it is.
|
|
|
|
if (sys::fs::exists(curproc)) {
|
|
|
|
ssize_t len = readlink(curproc.str().c_str(), exe_path, sizeof(exe_path));
|
|
|
|
if (len > 0) {
|
|
|
|
// Null terminate the string for realpath. readlink never null
|
|
|
|
// terminates its output.
|
|
|
|
len = std::min(len, ssize_t(sizeof(exe_path) - 1));
|
|
|
|
exe_path[len] = '\0';
|
|
|
|
return exe_path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we don't have procfs mounted, fall back to argv[0]
|
2013-06-26 05:01:35 +00:00
|
|
|
if (getprogpath(exe_path, argv0) != NULL)
|
2013-06-26 06:10:32 +00:00
|
|
|
return exe_path;
|
2013-06-26 05:01:35 +00:00
|
|
|
#elif defined(__linux__) || defined(__CYGWIN__)
|
|
|
|
char exe_path[MAXPATHLEN];
|
|
|
|
StringRef aPath("/proc/self/exe");
|
|
|
|
if (sys::fs::exists(aPath)) {
|
2018-10-23 23:35:43 +00:00
|
|
|
// /proc is not always mounted under Linux (chroot for example).
|
|
|
|
ssize_t len = readlink(aPath.str().c_str(), exe_path, sizeof(exe_path));
|
|
|
|
if (len < 0)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
// Null terminate the string for realpath. readlink never null
|
|
|
|
// terminates its output.
|
2018-10-23 23:44:44 +00:00
|
|
|
len = std::min(len, ssize_t(sizeof(exe_path) - 1));
|
2018-10-23 23:35:43 +00:00
|
|
|
exe_path[len] = '\0';
|
|
|
|
|
2018-10-23 23:44:44 +00:00
|
|
|
// On Linux, /proc/self/exe always looks through symlinks. However, on
|
|
|
|
// GNU/Hurd, /proc/self/exe is a symlink to the path that was used to start
|
|
|
|
// the program, and not the eventual binary file. Therefore, call realpath
|
|
|
|
// so this behaves the same on all platforms.
|
2018-10-23 23:35:43 +00:00
|
|
|
#if _POSIX_VERSION >= 200112 || defined(__GLIBC__)
|
|
|
|
char *real_path = realpath(exe_path, NULL);
|
|
|
|
std::string ret = std::string(real_path);
|
|
|
|
free(real_path);
|
|
|
|
return ret;
|
|
|
|
#else
|
|
|
|
char real_path[MAXPATHLEN];
|
|
|
|
realpath(exe_path, real_path);
|
|
|
|
return std::string(real_path);
|
|
|
|
#endif
|
2013-06-26 05:01:35 +00:00
|
|
|
} else {
|
2018-10-23 23:35:43 +00:00
|
|
|
// Fall back to the classical detection.
|
|
|
|
if (getprogpath(exe_path, argv0))
|
|
|
|
return exe_path;
|
2013-06-26 05:01:35 +00:00
|
|
|
}
|
2017-02-02 01:17:49 +00:00
|
|
|
#elif defined(HAVE_DLFCN_H) && defined(HAVE_DLADDR)
|
2013-06-26 05:01:35 +00:00
|
|
|
// Use dladdr to get executable path if available.
|
|
|
|
Dl_info DLInfo;
|
|
|
|
int err = dladdr(MainAddr, &DLInfo);
|
|
|
|
if (err == 0)
|
2013-06-26 06:10:32 +00:00
|
|
|
return "";
|
2013-06-26 05:01:35 +00:00
|
|
|
|
|
|
|
// If the filename is a symlink, we need to resolve and return the location of
|
|
|
|
// the actual executable.
|
|
|
|
char link_path[MAXPATHLEN];
|
|
|
|
if (realpath(DLInfo.dli_fname, link_path))
|
2013-06-26 06:10:32 +00:00
|
|
|
return link_path;
|
2013-06-26 05:01:35 +00:00
|
|
|
#else
|
|
|
|
#error GetMainExecutable is not implemented on this host yet.
|
|
|
|
#endif
|
|
|
|
return "";
|
|
|
|
}
|
2010-11-29 22:28:51 +00:00
|
|
|
|
2017-10-10 22:19:46 +00:00
|
|
|
TimePoint<> basic_file_status::getLastAccessedTime() const {
|
2018-11-26 00:03:39 +00:00
|
|
|
return toTimePoint(fs_st_atime, fs_st_atime_nsec);
|
2016-03-25 07:30:21 +00:00
|
|
|
}
|
|
|
|
|
2017-10-10 22:19:46 +00:00
|
|
|
TimePoint<> basic_file_status::getLastModificationTime() const {
|
2018-11-26 00:03:39 +00:00
|
|
|
return toTimePoint(fs_st_mtime, fs_st_mtime_nsec);
|
2013-06-20 18:42:04 +00:00
|
|
|
}
|
|
|
|
|
2013-07-29 21:55:38 +00:00
|
|
|
UniqueID file_status::getUniqueID() const {
|
2013-07-29 21:26:49 +00:00
|
|
|
return UniqueID(fs_st_dev, fs_st_ino);
|
|
|
|
}
|
|
|
|
|
2017-03-20 23:55:20 +00:00
|
|
|
uint32_t file_status::getLinkCount() const {
|
|
|
|
return fs_st_nlinks;
|
|
|
|
}
|
|
|
|
|
2016-04-01 00:18:08 +00:00
|
|
|
ErrorOr<space_info> disk_space(const Twine &Path) {
|
|
|
|
struct STATVFS Vfs;
|
2019-03-29 23:32:47 +00:00
|
|
|
if (::STATVFS(const_cast<char *>(Path.str().c_str()), &Vfs))
|
2016-04-01 00:18:08 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
auto FrSize = STATVFS_F_FRSIZE(Vfs);
|
|
|
|
space_info SpaceInfo;
|
|
|
|
SpaceInfo.capacity = static_cast<uint64_t>(Vfs.f_blocks) * FrSize;
|
|
|
|
SpaceInfo.free = static_cast<uint64_t>(Vfs.f_bfree) * FrSize;
|
|
|
|
SpaceInfo.available = static_cast<uint64_t>(Vfs.f_bavail) * FrSize;
|
|
|
|
return SpaceInfo;
|
|
|
|
}
|
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
std::error_code current_path(SmallVectorImpl<char> &result) {
|
2013-08-10 00:50:57 +00:00
|
|
|
result.clear();
|
|
|
|
|
|
|
|
const char *pwd = ::getenv("PWD");
|
|
|
|
llvm::sys::fs::file_status PWDStatus, DotStatus;
|
|
|
|
if (pwd && llvm::sys::path::is_absolute(pwd) &&
|
|
|
|
!llvm::sys::fs::status(pwd, PWDStatus) &&
|
|
|
|
!llvm::sys::fs::status(".", DotStatus) &&
|
|
|
|
PWDStatus.getUniqueID() == DotStatus.getUniqueID()) {
|
|
|
|
result.append(pwd, pwd + strlen(pwd));
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2013-08-10 00:50:57 +00:00
|
|
|
}
|
|
|
|
|
2012-04-11 15:35:36 +00:00
|
|
|
#ifdef MAXPATHLEN
|
2011-03-24 16:43:37 +00:00
|
|
|
result.reserve(MAXPATHLEN);
|
2012-04-11 15:35:36 +00:00
|
|
|
#else
|
|
|
|
// For GNU Hurd
|
|
|
|
result.reserve(1024);
|
|
|
|
#endif
|
2010-12-07 01:22:31 +00:00
|
|
|
|
|
|
|
while (true) {
|
2014-04-28 04:05:08 +00:00
|
|
|
if (::getcwd(result.data(), result.capacity()) == nullptr) {
|
2010-12-07 01:22:31 +00:00
|
|
|
// See if there was a real error.
|
2014-05-31 02:29:28 +00:00
|
|
|
if (errno != ENOMEM)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2010-12-07 01:22:31 +00:00
|
|
|
// Otherwise there just wasn't enough space.
|
|
|
|
result.reserve(result.capacity() * 2);
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
2010-11-29 22:28:51 +00:00
|
|
|
|
|
|
|
result.set_size(strlen(result.data()));
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2010-11-29 22:28:51 +00:00
|
|
|
}
|
|
|
|
|
2017-01-24 10:32:03 +00:00
|
|
|
std::error_code set_current_path(const Twine &path) {
|
|
|
|
SmallString<128> path_storage;
|
|
|
|
StringRef p = path.toNullTerminatedStringRef(path_storage);
|
|
|
|
|
|
|
|
if (::chdir(p.begin()) == -1)
|
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-08-06 21:04:55 +00:00
|
|
|
std::error_code create_directory(const Twine &path, bool IgnoreExisting,
|
|
|
|
perms Perms) {
|
2010-12-03 05:42:11 +00:00
|
|
|
SmallString<128> path_storage;
|
|
|
|
StringRef p = path.toNullTerminatedStringRef(path_storage);
|
|
|
|
|
2015-08-06 21:04:55 +00:00
|
|
|
if (::mkdir(p.begin(), Perms) == -1) {
|
2014-05-31 02:29:28 +00:00
|
|
|
if (errno != EEXIST || !IgnoreExisting)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2014-02-23 13:56:14 +00:00
|
|
|
}
|
2010-12-03 05:42:11 +00:00
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2010-12-03 05:42:11 +00:00
|
|
|
}
|
|
|
|
|
2014-03-11 18:40:24 +00:00
|
|
|
// Note that we are using symbolic link because hard links are not supported by
|
|
|
|
// all filesystems (SMB doesn't).
|
2014-06-13 02:24:39 +00:00
|
|
|
std::error_code create_link(const Twine &to, const Twine &from) {
|
2010-12-03 05:58:41 +00:00
|
|
|
// Get arguments.
|
|
|
|
SmallString<128> from_storage;
|
|
|
|
SmallString<128> to_storage;
|
|
|
|
StringRef f = from.toNullTerminatedStringRef(from_storage);
|
|
|
|
StringRef t = to.toNullTerminatedStringRef(to_storage);
|
|
|
|
|
2014-03-11 18:40:24 +00:00
|
|
|
if (::symlink(t.begin(), f.begin()) == -1)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2010-12-03 07:41:25 +00:00
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2010-12-03 07:41:25 +00:00
|
|
|
}
|
|
|
|
|
2016-12-14 04:56:42 +00:00
|
|
|
std::error_code create_hard_link(const Twine &to, const Twine &from) {
|
|
|
|
// Get arguments.
|
|
|
|
SmallString<128> from_storage;
|
|
|
|
SmallString<128> to_storage;
|
|
|
|
StringRef f = from.toNullTerminatedStringRef(from_storage);
|
|
|
|
StringRef t = to.toNullTerminatedStringRef(to_storage);
|
|
|
|
|
|
|
|
if (::link(t.begin(), f.begin()) == -1)
|
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
std::error_code remove(const Twine &path, bool IgnoreNonExisting) {
|
2010-12-03 17:53:43 +00:00
|
|
|
SmallString<128> path_storage;
|
|
|
|
StringRef p = path.toNullTerminatedStringRef(path_storage);
|
|
|
|
|
2013-06-17 20:35:51 +00:00
|
|
|
struct stat buf;
|
2014-03-21 01:25:37 +00:00
|
|
|
if (lstat(p.begin(), &buf) != 0) {
|
2014-05-31 02:29:28 +00:00
|
|
|
if (errno != ENOENT || !IgnoreNonExisting)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
return std::error_code();
|
2013-06-17 20:35:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Note: this check catches strange situations. In all cases, LLVM should
|
|
|
|
// only be involved in the creation and deletion of regular files. This
|
|
|
|
// check ensures that what we're trying to erase is a regular file. It
|
|
|
|
// effectively prevents LLVM from erasing things like /dev/null, any block
|
|
|
|
// special file, or other things that aren't "regular" files.
|
2014-03-21 01:25:37 +00:00
|
|
|
if (!S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode) && !S_ISLNK(buf.st_mode))
|
2014-06-13 17:20:48 +00:00
|
|
|
return make_error_code(errc::operation_not_permitted);
|
2013-06-17 20:35:51 +00:00
|
|
|
|
2010-12-03 17:53:43 +00:00
|
|
|
if (::remove(p.begin()) == -1) {
|
2014-05-31 02:29:28 +00:00
|
|
|
if (errno != ENOENT || !IgnoreNonExisting)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2014-02-23 13:56:14 +00:00
|
|
|
}
|
2010-12-03 17:53:43 +00:00
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2010-12-03 17:53:43 +00:00
|
|
|
}
|
|
|
|
|
2017-02-21 20:55:47 +00:00
|
|
|
static bool is_local_impl(struct STATVFS &Vfs) {
|
2018-10-23 07:13:47 +00:00
|
|
|
#if defined(__linux__) || defined(__GNU__)
|
2017-02-22 18:09:15 +00:00
|
|
|
#ifndef NFS_SUPER_MAGIC
|
|
|
|
#define NFS_SUPER_MAGIC 0x6969
|
|
|
|
#endif
|
|
|
|
#ifndef SMB_SUPER_MAGIC
|
|
|
|
#define SMB_SUPER_MAGIC 0x517B
|
|
|
|
#endif
|
2017-02-21 21:13:10 +00:00
|
|
|
#ifndef CIFS_MAGIC_NUMBER
|
|
|
|
#define CIFS_MAGIC_NUMBER 0xFF534D42
|
|
|
|
#endif
|
2018-10-23 07:13:47 +00:00
|
|
|
#ifdef __GNU__
|
|
|
|
switch ((uint32_t)Vfs.__f_type) {
|
|
|
|
#else
|
2017-02-21 20:55:47 +00:00
|
|
|
switch ((uint32_t)Vfs.f_type) {
|
2018-10-23 07:13:47 +00:00
|
|
|
#endif
|
2017-02-21 20:55:47 +00:00
|
|
|
case NFS_SUPER_MAGIC:
|
|
|
|
case SMB_SUPER_MAGIC:
|
|
|
|
case CIFS_MAGIC_NUMBER:
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
2017-03-09 13:43:31 +00:00
|
|
|
#elif defined(__CYGWIN__)
|
|
|
|
// Cygwin doesn't expose this information; would need to use Win32 API.
|
|
|
|
return false;
|
2018-05-03 01:38:49 +00:00
|
|
|
#elif defined(__Fuchsia__)
|
|
|
|
// Fuchsia doesn't yet support remote filesystem mounts.
|
|
|
|
return true;
|
2019-06-23 00:29:59 +00:00
|
|
|
#elif defined(__EMSCRIPTEN__)
|
|
|
|
// Emscripten doesn't currently support remote filesystem mounts.
|
|
|
|
return true;
|
2018-07-18 13:42:18 +00:00
|
|
|
#elif defined(__HAIKU__)
|
|
|
|
// Haiku doesn't expose this information.
|
|
|
|
return false;
|
2017-06-01 12:57:00 +00:00
|
|
|
#elif defined(__sun)
|
|
|
|
// statvfs::f_basetype contains a null-terminated FSType name of the mounted target
|
|
|
|
StringRef fstype(Vfs.f_basetype);
|
|
|
|
// NFS is the only non-local fstype??
|
|
|
|
return !fstype.equals("nfs");
|
2019-03-29 23:32:47 +00:00
|
|
|
#elif defined(_AIX)
|
|
|
|
// Call mntctl; try more than twice in case of timing issues with a concurrent
|
|
|
|
// mount.
|
|
|
|
int Ret;
|
|
|
|
size_t BufSize = 2048u;
|
|
|
|
std::unique_ptr<char[]> Buf;
|
|
|
|
int Tries = 3;
|
|
|
|
while (Tries--) {
|
|
|
|
Buf = llvm::make_unique<char[]>(BufSize);
|
|
|
|
Ret = mntctl(MCTL_QUERY, BufSize, Buf.get());
|
|
|
|
if (Ret != 0)
|
|
|
|
break;
|
|
|
|
BufSize = *reinterpret_cast<unsigned int *>(Buf.get());
|
|
|
|
Buf.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Ret == -1)
|
|
|
|
// There was an error; "remote" is the conservative answer.
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Look for the correct vmount entry.
|
|
|
|
char *CurObjPtr = Buf.get();
|
|
|
|
while (Ret--) {
|
|
|
|
struct vmount *Vp = reinterpret_cast<struct vmount *>(CurObjPtr);
|
|
|
|
static_assert(sizeof(Vfs.f_fsid) == sizeof(Vp->vmt_fsid),
|
|
|
|
"fsid length mismatch");
|
|
|
|
if (memcmp(&Vfs.f_fsid, &Vp->vmt_fsid, sizeof Vfs.f_fsid) == 0)
|
|
|
|
return (Vp->vmt_flags & MNT_REMOTE) == 0;
|
|
|
|
|
|
|
|
CurObjPtr += Vp->vmt_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
// vmount entry not found; "remote" is the conservative answer.
|
|
|
|
return false;
|
2017-02-21 20:55:47 +00:00
|
|
|
#else
|
|
|
|
return !!(STATVFS_F_FLAG(Vfs) & MNT_LOCAL);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code is_local(const Twine &Path, bool &Result) {
|
|
|
|
struct STATVFS Vfs;
|
2019-03-29 23:32:47 +00:00
|
|
|
if (::STATVFS(const_cast<char *>(Path.str().c_str()), &Vfs))
|
2017-02-21 20:55:47 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
|
|
|
|
Result = is_local_impl(Vfs);
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code is_local(int FD, bool &Result) {
|
|
|
|
struct STATVFS Vfs;
|
|
|
|
if (::FSTATVFS(FD, &Vfs))
|
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
|
|
|
|
Result = is_local_impl(Vfs);
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
std::error_code rename(const Twine &from, const Twine &to) {
|
2010-12-03 17:53:55 +00:00
|
|
|
// Get arguments.
|
|
|
|
SmallString<128> from_storage;
|
|
|
|
SmallString<128> to_storage;
|
|
|
|
StringRef f = from.toNullTerminatedStringRef(from_storage);
|
|
|
|
StringRef t = to.toNullTerminatedStringRef(to_storage);
|
|
|
|
|
2013-07-17 03:33:41 +00:00
|
|
|
if (::rename(f.begin(), t.begin()) == -1)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2010-12-03 17:53:55 +00:00
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2010-12-03 17:53:55 +00:00
|
|
|
}
|
|
|
|
|
2014-12-12 17:55:12 +00:00
|
|
|
std::error_code resize_file(int FD, uint64_t Size) {
|
2016-07-19 20:19:56 +00:00
|
|
|
#if defined(HAVE_POSIX_FALLOCATE)
|
|
|
|
// If we have posix_fallocate use it. Unlike ftruncate it always allocates
|
|
|
|
// space, so we get an error if the disk is full.
|
2017-05-05 17:55:58 +00:00
|
|
|
if (int Err = ::posix_fallocate(FD, 0, Size)) {
|
2019-04-04 00:40:34 +00:00
|
|
|
#ifdef _AIX
|
|
|
|
constexpr int NotSupportedError = ENOTSUP;
|
|
|
|
#else
|
|
|
|
constexpr int NotSupportedError = EOPNOTSUPP;
|
|
|
|
#endif
|
|
|
|
if (Err != EINVAL && Err != NotSupportedError)
|
2017-05-05 17:55:58 +00:00
|
|
|
return std::error_code(Err, std::generic_category());
|
|
|
|
}
|
|
|
|
#endif
|
2016-07-19 20:19:56 +00:00
|
|
|
// Use ftruncate as a fallback. It may or may not allocate space. At least on
|
|
|
|
// OS X with HFS+ it does.
|
2014-12-12 17:55:12 +00:00
|
|
|
if (::ftruncate(FD, Size) == -1)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2010-12-03 17:54:07 +00:00
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2010-12-03 17:54:07 +00:00
|
|
|
}
|
|
|
|
|
2016-04-05 20:45:04 +00:00
|
|
|
static int convertAccessMode(AccessMode Mode) {
|
2014-09-11 20:30:02 +00:00
|
|
|
switch (Mode) {
|
|
|
|
case AccessMode::Exist:
|
|
|
|
return F_OK;
|
|
|
|
case AccessMode::Write:
|
|
|
|
return W_OK;
|
|
|
|
case AccessMode::Execute:
|
|
|
|
return R_OK | X_OK; // scripts also need R_OK.
|
|
|
|
}
|
|
|
|
llvm_unreachable("invalid enum");
|
2010-12-03 01:21:28 +00:00
|
|
|
}
|
|
|
|
|
2014-09-11 20:30:02 +00:00
|
|
|
std::error_code access(const Twine &Path, AccessMode Mode) {
|
2013-06-18 20:56:38 +00:00
|
|
|
SmallString<128> PathStorage;
|
|
|
|
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
|
|
|
|
|
2014-09-11 20:30:02 +00:00
|
|
|
if (::access(P.begin(), convertAccessMode(Mode)) == -1)
|
|
|
|
return std::error_code(errno, std::generic_category());
|
2013-06-14 19:38:45 +00:00
|
|
|
|
2014-09-11 20:30:02 +00:00
|
|
|
if (Mode == AccessMode::Execute) {
|
|
|
|
// Don't say that directories are executable.
|
|
|
|
struct stat buf;
|
|
|
|
if (0 != stat(P.begin(), &buf))
|
|
|
|
return errc::permission_denied;
|
|
|
|
if (!S_ISREG(buf.st_mode))
|
|
|
|
return errc::permission_denied;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
2013-06-14 19:38:45 +00:00
|
|
|
}
|
|
|
|
|
2015-09-10 23:28:06 +00:00
|
|
|
bool can_execute(const Twine &Path) {
|
|
|
|
return !access(Path, AccessMode::Execute);
|
|
|
|
}
|
|
|
|
|
2011-12-12 06:04:28 +00:00
|
|
|
bool equivalent(file_status A, file_status B) {
|
|
|
|
assert(status_known(A) && status_known(B));
|
2012-04-23 16:37:23 +00:00
|
|
|
return A.fs_st_dev == B.fs_st_dev &&
|
|
|
|
A.fs_st_ino == B.fs_st_ino;
|
2011-12-12 06:04:28 +00:00
|
|
|
}
|
2010-12-03 18:49:13 +00:00
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
|
2011-12-12 06:04:28 +00:00
|
|
|
file_status fsA, fsB;
|
2014-06-13 02:24:39 +00:00
|
|
|
if (std::error_code ec = status(A, fsA))
|
|
|
|
return ec;
|
|
|
|
if (std::error_code ec = status(B, fsB))
|
|
|
|
return ec;
|
2011-12-12 06:04:28 +00:00
|
|
|
result = equivalent(fsA, fsB);
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2010-12-03 18:49:13 +00:00
|
|
|
}
|
|
|
|
|
2017-03-10 17:39:21 +00:00
|
|
|
static void expandTildeExpr(SmallVectorImpl<char> &Path) {
|
|
|
|
StringRef PathStr(Path.begin(), Path.size());
|
|
|
|
if (PathStr.empty() || !PathStr.startswith("~"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
PathStr = PathStr.drop_front();
|
2017-03-16 22:34:18 +00:00
|
|
|
StringRef Expr =
|
|
|
|
PathStr.take_until([](char c) { return path::is_separator(c); });
|
2017-03-10 17:39:21 +00:00
|
|
|
StringRef Remainder = PathStr.substr(Expr.size() + 1);
|
|
|
|
SmallString<128> Storage;
|
|
|
|
if (Expr.empty()) {
|
|
|
|
// This is just ~/..., resolve it to the current user's home dir.
|
|
|
|
if (!path::home_directory(Storage)) {
|
|
|
|
// For some reason we couldn't get the home directory. Just exit.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overwrite the first character and insert the rest.
|
|
|
|
Path[0] = Storage[0];
|
|
|
|
Path.insert(Path.begin() + 1, Storage.begin() + 1, Storage.end());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a string of the form ~username/, look up this user's entry in the
|
|
|
|
// password database.
|
|
|
|
struct passwd *Entry = nullptr;
|
|
|
|
std::string User = Expr.str();
|
|
|
|
Entry = ::getpwnam(User.c_str());
|
|
|
|
|
|
|
|
if (!Entry) {
|
|
|
|
// Unable to look up the entry, just return back the original path.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Storage = Remainder;
|
|
|
|
Path.clear();
|
|
|
|
Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir));
|
|
|
|
llvm::sys::path::append(Path, Storage);
|
|
|
|
}
|
|
|
|
|
2018-11-13 18:23:32 +00:00
|
|
|
|
|
|
|
void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) {
|
|
|
|
dest.clear();
|
|
|
|
if (path.isTriviallyEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
path.toVector(dest);
|
|
|
|
expandTildeExpr(dest);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-09-12 22:08:10 +00:00
|
|
|
static file_type typeForMode(mode_t Mode) {
|
|
|
|
if (S_ISDIR(Mode))
|
|
|
|
return file_type::directory_file;
|
|
|
|
else if (S_ISREG(Mode))
|
|
|
|
return file_type::regular_file;
|
|
|
|
else if (S_ISBLK(Mode))
|
|
|
|
return file_type::block_file;
|
|
|
|
else if (S_ISCHR(Mode))
|
|
|
|
return file_type::character_file;
|
|
|
|
else if (S_ISFIFO(Mode))
|
|
|
|
return file_type::fifo_file;
|
|
|
|
else if (S_ISSOCK(Mode))
|
|
|
|
return file_type::socket_file;
|
|
|
|
else if (S_ISLNK(Mode))
|
|
|
|
return file_type::symlink_file;
|
|
|
|
return file_type::type_unknown;
|
|
|
|
}
|
|
|
|
|
2016-04-05 20:45:04 +00:00
|
|
|
static std::error_code fillStatus(int StatRet, const struct stat &Status,
|
2018-06-10 04:53:14 +00:00
|
|
|
file_status &Result) {
|
2013-07-16 03:20:13 +00:00
|
|
|
if (StatRet != 0) {
|
2018-09-12 22:08:10 +00:00
|
|
|
std::error_code EC(errno, std::generic_category());
|
|
|
|
if (EC == errc::no_such_file_or_directory)
|
2013-07-16 03:20:13 +00:00
|
|
|
Result = file_status(file_type::file_not_found);
|
2010-12-04 00:32:40 +00:00
|
|
|
else
|
2013-07-16 03:20:13 +00:00
|
|
|
Result = file_status(file_type::status_error);
|
2018-09-12 22:08:10 +00:00
|
|
|
return EC;
|
2010-12-04 00:32:40 +00:00
|
|
|
}
|
|
|
|
|
2018-11-26 00:03:39 +00:00
|
|
|
uint32_t atime_nsec, mtime_nsec;
|
|
|
|
#if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
|
|
|
|
atime_nsec = Status.st_atimespec.tv_nsec;
|
|
|
|
mtime_nsec = Status.st_mtimespec.tv_nsec;
|
|
|
|
#elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
|
|
|
|
atime_nsec = Status.st_atim.tv_nsec;
|
|
|
|
mtime_nsec = Status.st_mtim.tv_nsec;
|
|
|
|
#else
|
|
|
|
atime_nsec = mtime_nsec = 0;
|
|
|
|
#endif
|
|
|
|
|
[Support] Add support for getting file system permissions on Windows and implement sys::fs::set/getPermissions to work with them
This change adds support for functions to set and get file permissions, in a similar manner to the C++17 permissions() function in <filesystem>. The setter uses chmod on Unix systems and SetFileAttributes on Windows, setting the permissions as passed in. The getter simply uses the existing status() function.
Prior to this change, status() would always return an unknown value for the permissions on a Windows file, making it impossible to test the new function on Windows. I have therefore added support for this as well. On Linux, prior to this change, the permissions included the file type, which should actually be accessed via a different member of the file_status class.
Note that on Windows, only the *_write permission bits have any affect - if any are set, the file is writable, and if not, the file is read-only. This is in common with what MSDN describes for their behaviour of std::filesystem::permissions(), and also what boost::filesystem does.
The motivation behind this change is so that we can easily test behaviour on read-only files in LLVM unit tests, but I am sure that others may find it useful in some situations.
Reviewers: zturner, amccarth, aaron.ballman
Differential Revision: https://reviews.llvm.org/D30736
llvm-svn: 297945
2017-03-16 11:22:09 +00:00
|
|
|
perms Perms = static_cast<perms>(Status.st_mode) & all_perms;
|
2018-09-12 22:08:10 +00:00
|
|
|
Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev,
|
2018-11-26 00:03:39 +00:00
|
|
|
Status.st_nlink, Status.st_ino,
|
|
|
|
Status.st_atime, atime_nsec, Status.st_mtime, mtime_nsec,
|
|
|
|
Status.st_uid, Status.st_gid, Status.st_size);
|
2011-12-12 06:04:28 +00:00
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2012-06-20 00:28:54 +00:00
|
|
|
}
|
|
|
|
|
2017-03-07 16:10:10 +00:00
|
|
|
std::error_code status(const Twine &Path, file_status &Result, bool Follow) {
|
2013-07-16 03:20:13 +00:00
|
|
|
SmallString<128> PathStorage;
|
|
|
|
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
|
|
|
|
|
|
|
|
struct stat Status;
|
2017-03-07 16:10:10 +00:00
|
|
|
int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status);
|
2013-07-16 03:20:13 +00:00
|
|
|
return fillStatus(StatRet, Status, Result);
|
|
|
|
}
|
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
std::error_code status(int FD, file_status &Result) {
|
2013-07-16 03:20:13 +00:00
|
|
|
struct stat Status;
|
|
|
|
int StatRet = ::fstat(FD, &Status);
|
2017-03-13 12:24:51 +00:00
|
|
|
return fillStatus(StatRet, Status, Result);
|
|
|
|
}
|
|
|
|
|
[Support] Add support for getting file system permissions on Windows and implement sys::fs::set/getPermissions to work with them
This change adds support for functions to set and get file permissions, in a similar manner to the C++17 permissions() function in <filesystem>. The setter uses chmod on Unix systems and SetFileAttributes on Windows, setting the permissions as passed in. The getter simply uses the existing status() function.
Prior to this change, status() would always return an unknown value for the permissions on a Windows file, making it impossible to test the new function on Windows. I have therefore added support for this as well. On Linux, prior to this change, the permissions included the file type, which should actually be accessed via a different member of the file_status class.
Note that on Windows, only the *_write permission bits have any affect - if any are set, the file is writable, and if not, the file is read-only. This is in common with what MSDN describes for their behaviour of std::filesystem::permissions(), and also what boost::filesystem does.
The motivation behind this change is so that we can easily test behaviour on read-only files in LLVM unit tests, but I am sure that others may find it useful in some situations.
Reviewers: zturner, amccarth, aaron.ballman
Differential Revision: https://reviews.llvm.org/D30736
llvm-svn: 297945
2017-03-16 11:22:09 +00:00
|
|
|
std::error_code setPermissions(const Twine &Path, perms Permissions) {
|
|
|
|
SmallString<128> PathStorage;
|
|
|
|
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
|
|
|
|
|
|
|
|
if (::chmod(P.begin(), Permissions))
|
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2018-08-13 23:03:45 +00:00
|
|
|
std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime,
|
|
|
|
TimePoint<> ModificationTime) {
|
2017-03-13 12:24:51 +00:00
|
|
|
#if defined(HAVE_FUTIMENS)
|
|
|
|
timespec Times[2];
|
2018-08-13 23:03:45 +00:00
|
|
|
Times[0] = sys::toTimeSpec(AccessTime);
|
|
|
|
Times[1] = sys::toTimeSpec(ModificationTime);
|
2013-07-04 01:10:38 +00:00
|
|
|
if (::futimens(FD, Times))
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
return std::error_code();
|
2013-07-04 01:10:38 +00:00
|
|
|
#elif defined(HAVE_FUTIMES)
|
2013-06-20 20:56:14 +00:00
|
|
|
timeval Times[2];
|
2018-08-13 23:03:45 +00:00
|
|
|
Times[0] = sys::toTimeVal(
|
|
|
|
std::chrono::time_point_cast<std::chrono::microseconds>(AccessTime));
|
|
|
|
Times[1] =
|
|
|
|
sys::toTimeVal(std::chrono::time_point_cast<std::chrono::microseconds>(
|
|
|
|
ModificationTime));
|
2013-06-20 20:56:14 +00:00
|
|
|
if (::futimes(FD, Times))
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
return std::error_code();
|
2013-12-11 15:42:33 +00:00
|
|
|
#else
|
|
|
|
#warning Missing futimes() and futimens()
|
2014-06-30 18:57:04 +00:00
|
|
|
return make_error_code(errc::function_not_supported);
|
2013-12-11 15:42:33 +00:00
|
|
|
#endif
|
2013-06-20 20:56:14 +00:00
|
|
|
}
|
|
|
|
|
2014-12-11 20:12:55 +00:00
|
|
|
std::error_code mapped_file_region::init(int FD, uint64_t Offset,
|
|
|
|
mapmode Mode) {
|
2014-12-12 18:13:23 +00:00
|
|
|
assert(Size != 0);
|
2012-08-15 19:05:47 +00:00
|
|
|
|
|
|
|
int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
|
|
|
|
int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE);
|
2017-02-22 21:24:06 +00:00
|
|
|
#if defined(__APPLE__)
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Newer versions of MacOSX have a flag that will allow us to read from
|
|
|
|
// binaries whose code signature is invalid without crashing by using
|
|
|
|
// the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media
|
|
|
|
// is mapped we can avoid crashing and return zeroes to any pages we try
|
|
|
|
// to read if the media becomes unavailable by using the
|
|
|
|
// MAP_RESILIENT_MEDIA flag. These flags are only usable when mapping
|
|
|
|
// with PROT_READ, so take care not to specify them otherwise.
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
if (Mode == readonly) {
|
|
|
|
#if defined(MAP_RESILIENT_CODESIGN)
|
|
|
|
flags |= MAP_RESILIENT_CODESIGN;
|
|
|
|
#endif
|
|
|
|
#if defined(MAP_RESILIENT_MEDIA)
|
|
|
|
flags |= MAP_RESILIENT_MEDIA;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif // #if defined (__APPLE__)
|
2017-02-21 20:55:47 +00:00
|
|
|
|
2014-04-28 04:05:08 +00:00
|
|
|
Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset);
|
2012-08-15 19:05:47 +00:00
|
|
|
if (Mapping == MAP_FAILED)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
return std::error_code();
|
2012-08-15 19:05:47 +00:00
|
|
|
}
|
|
|
|
|
2017-09-27 15:59:16 +00:00
|
|
|
mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
|
2014-12-11 20:12:55 +00:00
|
|
|
uint64_t offset, std::error_code &ec)
|
LTO: Keep file handles open for memory mapped files.
On Windows we've observed that if you open a file, write to it, map it into
memory and close the file handle, the contents of the memory mapping can
sometimes be incorrect. That was what we did when adding an entry to the
ThinLTO cache using the TempFile and MemoryBuffer classes, and it was causing
intermittent build failures on Chromium's ThinLTO bots on Windows. More
details are in the associated Chromium bug (crbug.com/786127).
We can prevent this from happening by keeping a handle to the file open while
the mapping is active. So this patch changes the mapped_file_region class to
duplicate the file handle when mapping the file and close it upon unmapping it.
One gotcha is that the file handle that we keep open must not have been
created with FILE_FLAG_DELETE_ON_CLOSE, as otherwise the operating system
will prevent other processes from opening the file. We can achieve this
by avoiding the use of FILE_FLAG_DELETE_ON_CLOSE altogether. Instead,
we use SetFileInformationByHandle with FileDispositionInfo to manage the
delete-on-close bit. This lets us remove the hack that we used to use to
clear the delete-on-close bit on a file opened with FILE_FLAG_DELETE_ON_CLOSE.
A downside of using SetFileInformationByHandle/FileDispositionInfo as
opposed to FILE_FLAG_DELETE_ON_CLOSE is that it prevents us from using
CreateFile to open the file while the flag is set, even within the same
process. This doesn't seem to matter for almost every client of TempFile,
except for LockFileManager, which calls sys::fs::create_link to create a
hard link from the lock file, and in the process of doing so tries to open
the file. To prevent this change from breaking LockFileManager I changed it
to stop using TempFile by effectively reverting r318550.
Differential Revision: https://reviews.llvm.org/D48051
llvm-svn: 334630
2018-06-13 18:03:14 +00:00
|
|
|
: Size(length), Mapping(), Mode(mode) {
|
2018-02-15 18:46:59 +00:00
|
|
|
(void)Mode;
|
2014-12-11 20:12:55 +00:00
|
|
|
ec = init(fd, offset, mode);
|
2012-08-15 19:05:47 +00:00
|
|
|
if (ec)
|
2014-04-28 04:05:08 +00:00
|
|
|
Mapping = nullptr;
|
2012-08-15 19:05:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mapped_file_region::~mapped_file_region() {
|
|
|
|
if (Mapping)
|
|
|
|
::munmap(Mapping, Size);
|
|
|
|
}
|
|
|
|
|
2017-09-27 16:08:33 +00:00
|
|
|
size_t mapped_file_region::size() const {
|
2012-08-15 19:05:47 +00:00
|
|
|
assert(Mapping && "Mapping failed but used anyway!");
|
|
|
|
return Size;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *mapped_file_region::data() const {
|
|
|
|
assert(Mapping && "Mapping failed but used anyway!");
|
|
|
|
return reinterpret_cast<char*>(Mapping);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *mapped_file_region::const_data() const {
|
|
|
|
assert(Mapping && "Mapping failed but used anyway!");
|
|
|
|
return reinterpret_cast<const char*>(Mapping);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mapped_file_region::alignment() {
|
2019-05-08 02:11:07 +00:00
|
|
|
return Process::getPageSizeEstimate();
|
2012-08-15 19:05:47 +00:00
|
|
|
}
|
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
2017-03-08 22:49:32 +00:00
|
|
|
StringRef path,
|
|
|
|
bool follow_symlinks) {
|
2011-01-05 16:38:57 +00:00
|
|
|
SmallString<128> path_null(path);
|
|
|
|
DIR *directory = ::opendir(path_null.c_str());
|
2014-04-28 04:05:08 +00:00
|
|
|
if (!directory)
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2011-01-05 16:38:57 +00:00
|
|
|
|
|
|
|
it.IterationHandle = reinterpret_cast<intptr_t>(directory);
|
|
|
|
// Add something for replace_filename to replace.
|
|
|
|
path::append(path_null, ".");
|
2017-03-08 22:49:32 +00:00
|
|
|
it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks);
|
2011-01-05 16:38:57 +00:00
|
|
|
return directory_iterator_increment(it);
|
|
|
|
}
|
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
|
2011-01-05 16:38:57 +00:00
|
|
|
if (it.IterationHandle)
|
|
|
|
::closedir(reinterpret_cast<DIR *>(it.IterationHandle));
|
|
|
|
it.IterationHandle = 0;
|
|
|
|
it.CurrentEntry = directory_entry();
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2011-01-05 16:38:57 +00:00
|
|
|
}
|
|
|
|
|
2018-09-12 22:08:10 +00:00
|
|
|
static file_type direntType(dirent* Entry) {
|
|
|
|
// Most platforms provide the file type in the dirent: Linux/BSD/Mac.
|
|
|
|
// The DTTOIF macro lets us reuse our status -> type conversion.
|
|
|
|
#if defined(_DIRENT_HAVE_D_TYPE) && defined(DTTOIF)
|
|
|
|
return typeForMode(DTTOIF(Entry->d_type));
|
|
|
|
#else
|
|
|
|
// Other platforms such as Solaris require a stat() to get the type.
|
|
|
|
return file_type::type_unknown;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code detail::directory_iterator_increment(detail::DirIterState &It) {
|
2011-01-05 16:38:57 +00:00
|
|
|
errno = 0;
|
2018-09-12 22:08:10 +00:00
|
|
|
dirent *CurDir = ::readdir(reinterpret_cast<DIR *>(It.IterationHandle));
|
|
|
|
if (CurDir == nullptr && errno != 0) {
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2018-09-12 22:08:10 +00:00
|
|
|
} else if (CurDir != nullptr) {
|
|
|
|
StringRef Name(CurDir->d_name);
|
|
|
|
if ((Name.size() == 1 && Name[0] == '.') ||
|
|
|
|
(Name.size() == 2 && Name[0] == '.' && Name[1] == '.'))
|
|
|
|
return directory_iterator_increment(It);
|
|
|
|
It.CurrentEntry.replace_filename(Name, direntType(CurDir));
|
2011-01-05 16:38:57 +00:00
|
|
|
} else
|
2018-09-12 22:08:10 +00:00
|
|
|
return directory_iterator_destruct(It);
|
2011-01-05 16:38:57 +00:00
|
|
|
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2011-01-05 16:38:57 +00:00
|
|
|
}
|
|
|
|
|
2017-10-10 22:19:46 +00:00
|
|
|
ErrorOr<basic_file_status> directory_entry::status() const {
|
|
|
|
file_status s;
|
|
|
|
if (auto EC = fs::status(Path, s, FollowSymlinks))
|
|
|
|
return EC;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2016-06-13 15:54:56 +00:00
|
|
|
#if !defined(F_GETPATH)
|
|
|
|
static bool hasProcSelfFD() {
|
|
|
|
// If we have a /proc filesystem mounted, we can quickly establish the
|
|
|
|
// real name of the file with readlink
|
|
|
|
static const bool Result = (::access("/proc/self/fd", R_OK) == 0);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-06-07 19:58:58 +00:00
|
|
|
static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
|
|
|
|
FileAccess Access) {
|
|
|
|
int Result = 0;
|
|
|
|
if (Access == FA_Read)
|
|
|
|
Result |= O_RDONLY;
|
|
|
|
else if (Access == FA_Write)
|
|
|
|
Result |= O_WRONLY;
|
|
|
|
else if (Access == (FA_Read | FA_Write))
|
|
|
|
Result |= O_RDWR;
|
|
|
|
|
|
|
|
// This is for compatibility with old code that assumed F_Append implied
|
|
|
|
// would open an existing file. See Windows/Path.inc for a longer comment.
|
|
|
|
if (Flags & F_Append)
|
|
|
|
Disp = CD_OpenAlways;
|
|
|
|
|
|
|
|
if (Disp == CD_CreateNew) {
|
|
|
|
Result |= O_CREAT; // Create if it doesn't exist.
|
|
|
|
Result |= O_EXCL; // Fail if it does.
|
|
|
|
} else if (Disp == CD_CreateAlways) {
|
|
|
|
Result |= O_CREAT; // Create if it doesn't exist.
|
|
|
|
Result |= O_TRUNC; // Truncate if it does.
|
|
|
|
} else if (Disp == CD_OpenAlways) {
|
|
|
|
Result |= O_CREAT; // Create if it doesn't exist.
|
|
|
|
} else if (Disp == CD_OpenExisting) {
|
|
|
|
// Nothing special, just don't add O_CREAT and we get these semantics.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Flags & F_Append)
|
|
|
|
Result |= O_APPEND;
|
|
|
|
|
2017-01-24 10:57:01 +00:00
|
|
|
#ifdef O_CLOEXEC
|
2018-06-08 15:15:56 +00:00
|
|
|
if (!(Flags & OF_ChildInherit))
|
|
|
|
Result |= O_CLOEXEC;
|
2017-01-24 10:57:01 +00:00
|
|
|
#endif
|
2018-06-07 19:58:58 +00:00
|
|
|
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2018-06-07 23:25:13 +00:00
|
|
|
std::error_code openFile(const Twine &Name, int &ResultFD,
|
|
|
|
CreationDisposition Disp, FileAccess Access,
|
|
|
|
OpenFlags Flags, unsigned Mode) {
|
2018-06-07 19:58:58 +00:00
|
|
|
int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
|
|
|
|
|
|
|
|
SmallString<128> Storage;
|
|
|
|
StringRef P = Name.toNullTerminatedStringRef(Storage);
|
2018-08-27 15:55:39 +00:00
|
|
|
// Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
|
|
|
|
// when open is overloaded, such as in Bionic.
|
|
|
|
auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); };
|
|
|
|
if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0)
|
2017-06-29 13:15:31 +00:00
|
|
|
return std::error_code(errno, std::generic_category());
|
2017-01-24 10:57:01 +00:00
|
|
|
#ifndef O_CLOEXEC
|
2018-06-08 15:15:56 +00:00
|
|
|
if (!(Flags & OF_ChildInherit)) {
|
|
|
|
int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
|
|
|
|
(void)r;
|
|
|
|
assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
|
|
|
|
}
|
2017-01-24 10:57:01 +00:00
|
|
|
#endif
|
2018-06-07 19:58:58 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2018-06-07 23:25:13 +00:00
|
|
|
Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp,
|
|
|
|
FileAccess Access, OpenFlags Flags,
|
|
|
|
unsigned Mode) {
|
|
|
|
|
|
|
|
int FD;
|
|
|
|
std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode);
|
|
|
|
if (EC)
|
|
|
|
return errorCodeToError(EC);
|
|
|
|
return FD;
|
|
|
|
}
|
|
|
|
|
2018-06-07 19:58:58 +00:00
|
|
|
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
|
|
|
|
OpenFlags Flags,
|
|
|
|
SmallVectorImpl<char> *RealPath) {
|
|
|
|
std::error_code EC =
|
|
|
|
openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666);
|
|
|
|
if (EC)
|
|
|
|
return EC;
|
|
|
|
|
2016-06-13 15:54:56 +00:00
|
|
|
// Attempt to get the real name of the file, if the user asked
|
|
|
|
if(!RealPath)
|
|
|
|
return std::error_code();
|
|
|
|
RealPath->clear();
|
|
|
|
#if defined(F_GETPATH)
|
|
|
|
// When F_GETPATH is availble, it is the quickest way to get
|
|
|
|
// the real path name.
|
|
|
|
char Buffer[MAXPATHLEN];
|
|
|
|
if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1)
|
|
|
|
RealPath->append(Buffer, Buffer + strlen(Buffer));
|
|
|
|
#else
|
|
|
|
char Buffer[PATH_MAX];
|
|
|
|
if (hasProcSelfFD()) {
|
|
|
|
char ProcPath[64];
|
|
|
|
snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD);
|
|
|
|
ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer));
|
|
|
|
if (CharCount > 0)
|
|
|
|
RealPath->append(Buffer, Buffer + CharCount);
|
|
|
|
} else {
|
2018-06-07 19:58:58 +00:00
|
|
|
SmallString<128> Storage;
|
|
|
|
StringRef P = Name.toNullTerminatedStringRef(Storage);
|
|
|
|
|
2016-06-13 15:54:56 +00:00
|
|
|
// Use ::realpath to get the real path name
|
|
|
|
if (::realpath(P.begin(), Buffer) != nullptr)
|
|
|
|
RealPath->append(Buffer, Buffer + strlen(Buffer));
|
|
|
|
}
|
|
|
|
#endif
|
2014-06-13 02:24:39 +00:00
|
|
|
return std::error_code();
|
2013-07-17 14:58:25 +00:00
|
|
|
}
|
2012-06-20 00:28:54 +00:00
|
|
|
|
2018-06-07 19:58:58 +00:00
|
|
|
Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
|
2018-06-04 19:38:11 +00:00
|
|
|
SmallVectorImpl<char> *RealPath) {
|
|
|
|
file_t ResultFD;
|
2018-06-07 19:58:58 +00:00
|
|
|
std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath);
|
2018-06-04 19:38:11 +00:00
|
|
|
if (EC)
|
|
|
|
return errorCodeToError(EC);
|
|
|
|
return ResultFD;
|
|
|
|
}
|
|
|
|
|
|
|
|
void closeFile(file_t &F) {
|
|
|
|
::close(F);
|
|
|
|
F = kInvalidFile;
|
|
|
|
}
|
|
|
|
|
2017-03-08 22:49:32 +00:00
|
|
|
template <typename T>
|
|
|
|
static std::error_code remove_directories_impl(const T &Entry,
|
|
|
|
bool IgnoreErrors) {
|
|
|
|
std::error_code EC;
|
|
|
|
directory_iterator Begin(Entry, EC, false);
|
|
|
|
directory_iterator End;
|
|
|
|
while (Begin != End) {
|
|
|
|
auto &Item = *Begin;
|
2017-10-10 22:19:46 +00:00
|
|
|
ErrorOr<basic_file_status> st = Item.status();
|
|
|
|
if (!st && !IgnoreErrors)
|
|
|
|
return st.getError();
|
2017-03-08 22:49:32 +00:00
|
|
|
|
2017-10-10 22:19:46 +00:00
|
|
|
if (is_directory(*st)) {
|
2017-03-08 22:49:32 +00:00
|
|
|
EC = remove_directories_impl(Item, IgnoreErrors);
|
|
|
|
if (EC && !IgnoreErrors)
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
|
|
|
EC = fs::remove(Item.path(), true);
|
|
|
|
if (EC && !IgnoreErrors)
|
|
|
|
return EC;
|
|
|
|
|
|
|
|
Begin.increment(EC);
|
|
|
|
if (EC && !IgnoreErrors)
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code remove_directories(const Twine &path, bool IgnoreErrors) {
|
|
|
|
auto EC = remove_directories_impl(path, IgnoreErrors);
|
|
|
|
if (EC && !IgnoreErrors)
|
|
|
|
return EC;
|
|
|
|
EC = fs::remove(path, true);
|
|
|
|
if (EC && !IgnoreErrors)
|
|
|
|
return EC;
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2017-03-10 17:39:21 +00:00
|
|
|
std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest,
|
|
|
|
bool expand_tilde) {
|
|
|
|
dest.clear();
|
|
|
|
if (path.isTriviallyEmpty())
|
|
|
|
return std::error_code();
|
|
|
|
|
|
|
|
if (expand_tilde) {
|
|
|
|
SmallString<128> Storage;
|
|
|
|
path.toVector(Storage);
|
|
|
|
expandTildeExpr(Storage);
|
|
|
|
return real_path(Storage, dest, false);
|
|
|
|
}
|
|
|
|
|
2018-01-09 17:27:45 +00:00
|
|
|
SmallString<128> Storage;
|
|
|
|
StringRef P = path.toNullTerminatedStringRef(Storage);
|
|
|
|
char Buffer[PATH_MAX];
|
|
|
|
if (::realpath(P.begin(), Buffer) == nullptr)
|
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
dest.append(Buffer, Buffer + strlen(Buffer));
|
2017-03-10 17:39:21 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2010-12-01 19:32:01 +00:00
|
|
|
} // end namespace fs
|
2014-01-31 23:46:06 +00:00
|
|
|
|
|
|
|
namespace path {
|
|
|
|
|
|
|
|
bool home_directory(SmallVectorImpl<char> &result) {
|
2017-03-22 15:24:59 +00:00
|
|
|
char *RequestedDir = getenv("HOME");
|
|
|
|
if (!RequestedDir) {
|
|
|
|
struct passwd *pw = getpwuid(getuid());
|
|
|
|
if (pw && pw->pw_dir)
|
|
|
|
RequestedDir = pw->pw_dir;
|
2014-01-31 23:46:06 +00:00
|
|
|
}
|
2017-03-22 15:24:59 +00:00
|
|
|
if (!RequestedDir)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
result.clear();
|
|
|
|
result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
|
|
|
|
return true;
|
2014-01-31 23:46:06 +00:00
|
|
|
}
|
|
|
|
|
2016-04-05 20:45:04 +00:00
|
|
|
static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) {
|
2015-11-02 09:49:17 +00:00
|
|
|
#if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR)
|
|
|
|
// On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR.
|
|
|
|
// macros defined in <unistd.h> on darwin >= 9
|
|
|
|
int ConfName = TempDir ? _CS_DARWIN_USER_TEMP_DIR
|
|
|
|
: _CS_DARWIN_USER_CACHE_DIR;
|
|
|
|
size_t ConfLen = confstr(ConfName, nullptr, 0);
|
|
|
|
if (ConfLen > 0) {
|
|
|
|
do {
|
|
|
|
Result.resize(ConfLen);
|
|
|
|
ConfLen = confstr(ConfName, Result.data(), Result.size());
|
|
|
|
} while (ConfLen > 0 && ConfLen != Result.size());
|
|
|
|
|
|
|
|
if (ConfLen > 0) {
|
|
|
|
assert(Result.back() == 0);
|
|
|
|
Result.pop_back();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result.clear();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-04-05 20:45:04 +00:00
|
|
|
static const char *getEnvTempDir() {
|
2014-08-26 14:47:52 +00:00
|
|
|
// Check whether the temporary directory is specified by an environment
|
|
|
|
// variable.
|
|
|
|
const char *EnvironmentVariables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
|
|
|
|
for (const char *Env : EnvironmentVariables) {
|
|
|
|
if (const char *Dir = std::getenv(Env))
|
|
|
|
return Dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-04-05 20:45:04 +00:00
|
|
|
static const char *getDefaultTempDir(bool ErasedOnReboot) {
|
2014-08-26 14:47:52 +00:00
|
|
|
#ifdef P_tmpdir
|
2014-08-27 11:47:52 +00:00
|
|
|
if ((bool)P_tmpdir)
|
2014-08-26 14:47:52 +00:00
|
|
|
return P_tmpdir;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ErasedOnReboot)
|
|
|
|
return "/tmp";
|
|
|
|
return "/var/tmp";
|
|
|
|
}
|
|
|
|
|
|
|
|
void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
|
|
|
|
Result.clear();
|
|
|
|
|
|
|
|
if (ErasedOnReboot) {
|
|
|
|
// There is no env variable for the cache directory.
|
|
|
|
if (const char *RequestedDir = getEnvTempDir()) {
|
|
|
|
Result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-02 09:49:17 +00:00
|
|
|
if (getDarwinConfDir(ErasedOnReboot, Result))
|
|
|
|
return;
|
2014-08-26 14:47:52 +00:00
|
|
|
|
|
|
|
const char *RequestedDir = getDefaultTempDir(ErasedOnReboot);
|
|
|
|
Result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
|
|
|
|
}
|
|
|
|
|
2014-01-31 23:46:06 +00:00
|
|
|
} // end namespace path
|
|
|
|
|
2019-04-18 21:22:50 +00:00
|
|
|
namespace fs {
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
/// This implementation tries to perform an APFS CoW clone of the file,
|
|
|
|
/// which can be much faster and uses less space.
|
2019-04-24 19:08:43 +00:00
|
|
|
/// Unfortunately fcopyfile(3) does not support COPYFILE_CLONE, so the
|
|
|
|
/// file descriptor variant of this function still uses the default
|
|
|
|
/// implementation.
|
2019-04-18 21:22:50 +00:00
|
|
|
std::error_code copy_file(const Twine &From, const Twine &To) {
|
|
|
|
uint32_t Flag = COPYFILE_DATA;
|
2019-05-07 17:10:27 +00:00
|
|
|
#if __has_builtin(__builtin_available)
|
2019-04-18 21:22:50 +00:00
|
|
|
if (__builtin_available(macos 10.12, *)) {
|
|
|
|
bool IsSymlink;
|
|
|
|
if (std::error_code Error = is_symlink_file(From, IsSymlink))
|
|
|
|
return Error;
|
|
|
|
// COPYFILE_CLONE clones the symlink instead of following it
|
|
|
|
// and returns EEXISTS if the target file already exists.
|
|
|
|
if (!IsSymlink && !exists(To))
|
|
|
|
Flag = COPYFILE_CLONE;
|
|
|
|
}
|
2019-05-07 17:10:27 +00:00
|
|
|
#endif
|
2019-04-18 21:22:50 +00:00
|
|
|
int Status =
|
|
|
|
copyfile(From.str().c_str(), To.str().c_str(), /* State */ NULL, Flag);
|
|
|
|
|
|
|
|
if (Status == 0)
|
|
|
|
return std::error_code();
|
|
|
|
return std::error_code(errno, std::generic_category());
|
|
|
|
}
|
2019-04-24 19:08:43 +00:00
|
|
|
#endif // __APPLE__
|
2019-04-18 21:22:50 +00:00
|
|
|
|
|
|
|
} // end namespace fs
|
|
|
|
|
2010-11-29 22:28:51 +00:00
|
|
|
} // end namespace sys
|
|
|
|
} // end namespace llvm
|