summaryrefslogtreecommitdiff
path: root/llvm/lib/Support/Unix/Path.inc
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Support/Unix/Path.inc')
-rw-r--r--llvm/lib/Support/Unix/Path.inc61
1 files changed, 47 insertions, 14 deletions
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index c37b3a54644a..19d89db55627 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -39,6 +39,9 @@
#include <mach-o/dyld.h>
#include <sys/attr.h>
#include <copyfile.h>
+#if __has_include(<sys/clonefile.h>)
+#include <sys/clonefile.h>
+#endif
#elif defined(__FreeBSD__)
#include <osreldate.h>
#if __FreeBSD_version >= 1300057
@@ -125,7 +128,8 @@ const file_t kInvalidFile = -1;
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
defined(__minix) || defined(__FreeBSD_kernel__) || defined(__linux__) || \
- defined(__CYGWIN__) || defined(__DragonFly__) || defined(_AIX) || defined(__GNU__)
+ defined(__CYGWIN__) || defined(__DragonFly__) || defined(_AIX) || defined(__GNU__) || \
+ (defined(__sun__) && defined(__svr4__))
static int
test_dir(char ret[PATH_MAX], const char *dir, const char *bin)
{
@@ -283,6 +287,20 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) {
// Fall back to the classical detection.
if (getprogpath(exe_path, argv0))
return exe_path;
+#elif defined(__sun__) && defined(__svr4__)
+ char exe_path[PATH_MAX];
+ const char *aPath = "/proc/self/execname";
+ if (sys::fs::exists(aPath)) {
+ int fd = open(aPath, O_RDONLY);
+ if (fd == -1)
+ return "";
+ if (read(fd, exe_path, sizeof(exe_path)) < 0)
+ return "";
+ return exe_path;
+ }
+ // Fall back to the classical detection.
+ if (getprogpath(exe_path, argv0) != NULL)
+ return exe_path;
#elif defined(__MVS__)
int token = 0;
W_PSPROC buf;
@@ -1442,22 +1460,37 @@ namespace fs {
/// file descriptor variant of this function still uses the default
/// implementation.
std::error_code copy_file(const Twine &From, const Twine &To) {
- uint32_t Flag = COPYFILE_DATA;
-#if __has_builtin(__builtin_available) && defined(COPYFILE_CLONE)
+ std::string FromS = From.str();
+ std::string ToS = To.str();
+#if __has_builtin(__builtin_available)
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;
+ // Optimistically try to use clonefile() and handle errors, rather than
+ // calling stat() to see if it'll work.
+ //
+ // Note: It's okay if From is a symlink. In contrast to the behaviour of
+ // copyfile() with COPYFILE_CLONE, clonefile() clones targets (not the
+ // symlink itself) unless the flag CLONE_NOFOLLOW is passed.
+ if (!clonefile(FromS.c_str(), ToS.c_str(), 0))
+ return std::error_code();
+
+ auto Errno = errno;
+ switch (Errno) {
+ case EEXIST: // To already exists.
+ case ENOTSUP: // Device does not support cloning.
+ case EXDEV: // From and To are on different devices.
+ break;
+ default:
+ // Anything else will also break copyfile().
+ return std::error_code(Errno, std::generic_category());
+ }
+
+ // TODO: For EEXIST, profile calling fs::generateUniqueName() and
+ // clonefile() in a retry loop (then rename() on success) before falling
+ // back to copyfile(). Depending on the size of the file this could be
+ // cheaper.
}
#endif
- int Status =
- copyfile(From.str().c_str(), To.str().c_str(), /* State */ NULL, Flag);
-
- if (Status == 0)
+ if (!copyfile(FromS.c_str(), ToS.c_str(), /*State=*/NULL, COPYFILE_DATA))
return std::error_code();
return std::error_code(errno, std::generic_category());
}