From 583bae5a0ad1c636841bd36ac9c1658198f948b1 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 15 Aug 2018 05:52:47 +0200 Subject: [PATCH] Use clonefile instead of copyfile on macOS. (#10020) * Use clonefile instead of copyfile on macOS. * Address PR review comments. * Address PR review comments (if -> assert). * RTLD_DEFAULT - > libc_handle --- mono/metadata/w32file-unix.c | 147 ++++++++++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/mono/metadata/w32file-unix.c b/mono/metadata/w32file-unix.c index 8225075ae1b..cc860e5bd9e 100644 --- a/mono/metadata/w32file-unix.c +++ b/mono/metadata/w32file-unix.c @@ -14,12 +14,6 @@ #ifdef HAVE_SYS_STATVFS_H #include #endif -#ifdef HAVE_COPYFILE_H -#include -# if !defined(COPYFILE_CLONE) -# define COPYFILE_CLONE (1 << 24) -# endif -#endif #if defined(HAVE_SYS_STATFS_H) #include #endif @@ -43,6 +37,9 @@ #ifdef HAVE_DIRENT_H # include #endif +#if HOST_DARWIN +#include +#endif #include "w32file.h" #include "w32file-internals.h" @@ -112,6 +109,12 @@ static MonoCoopMutex file_share_mutex; static GHashTable *finds; static MonoCoopMutex finds_mutex; +#if HOST_DARWIN +typedef int (*clonefile_fn) (const char *from, const char *to, int flags); +static void *libc_handle; +static clonefile_fn clonefile_ptr; +#endif + static void time_t_to_filetime (time_t timeval, FILETIME *filetime) { @@ -2331,21 +2334,23 @@ write_file (gint src_fd, gint dest_fd, struct stat *st_src, gboolean report_erro return TRUE ; } -#if HAVE_COPYFILE_H +#if HOST_DARWIN static int -_wapi_copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags) +_wapi_clonefile(const char *from, const char *to, int flags) { gchar *located_from, *located_to; int ret; + g_assert (clonefile_ptr != NULL); + located_from = mono_portability_find_file (from, FALSE); located_to = mono_portability_find_file (to, FALSE); MONO_ENTER_GC_SAFE; - ret = copyfile ( + ret = clonefile_ptr ( located_from == NULL ? from : located_from, located_to == NULL ? to : located_to, - state, flags); + flags); MONO_EXIT_GC_SAFE; g_free (located_from); @@ -2398,39 +2403,6 @@ CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_ex return(FALSE); } -#if HAVE_COPYFILE_H - if (!_wapi_stat (utf8_dest, &dest_st)) { - /* Before trying to open/create the dest, we need to report a 'file busy' - * error if src and dest are actually the same file. We do the check here to take - * advantage of the IOMAP capability */ - if (!_wapi_stat (utf8_src, &st) && st.st_dev == dest_st.st_dev && st.st_ino == dest_st.st_ino) { - g_free (utf8_src); - g_free (utf8_dest); - - mono_w32error_set_last (ERROR_SHARING_VIOLATION); - return (FALSE); - } - - /* Also bail out if the destination is read-only (FIXME: path is not translated by mono_portability_find_file!) */ - if (!is_file_writable (&dest_st, utf8_dest)) { - g_free (utf8_src); - g_free (utf8_dest); - - mono_w32error_set_last (ERROR_ACCESS_DENIED); - return (FALSE); - } - } - - ret = _wapi_copyfile (utf8_src, utf8_dest, NULL, COPYFILE_ALL | COPYFILE_CLONE | (fail_if_exists ? COPYFILE_EXCL : COPYFILE_UNLINK)); - g_free (utf8_src); - g_free (utf8_dest); - if (ret != 0) { - _wapi_set_last_error_from_errno (); - return FALSE; - } - - return TRUE; -#else gint src_fd, dest_fd; gint ret_utime; gint syscall_res; @@ -2460,22 +2432,76 @@ CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_ex return(FALSE); } - /* Before trying to open/create the dest, we need to report a 'file busy' - * error if src and dest are actually the same file. We do the check here to take - * advantage of the IOMAP capability */ - if (!_wapi_stat (utf8_dest, &dest_st) && st.st_dev == dest_st.st_dev && - st.st_ino == dest_st.st_ino) { + if (!_wapi_stat (utf8_dest, &dest_st)) { + /* Before trying to open/create the dest, we need to report a 'file busy' + * error if src and dest are actually the same file. We do the check here to take + * advantage of the IOMAP capability */ + if (st.st_dev == dest_st.st_dev && st.st_ino == dest_st.st_ino) { + g_free (utf8_src); + g_free (utf8_dest); + MONO_ENTER_GC_SAFE; + close (src_fd); + MONO_EXIT_GC_SAFE; - g_free (utf8_src); - g_free (utf8_dest); - MONO_ENTER_GC_SAFE; - close (src_fd); - MONO_EXIT_GC_SAFE; + mono_w32error_set_last (ERROR_SHARING_VIOLATION); + return (FALSE); + } - mono_w32error_set_last (ERROR_SHARING_VIOLATION); - return (FALSE); + /* Take advantage of the fact that we already know the file exists and bail out + * early */ + if (fail_if_exists) { + g_free (utf8_src); + g_free (utf8_dest); + MONO_ENTER_GC_SAFE; + close (src_fd); + MONO_EXIT_GC_SAFE; + + mono_w32error_set_last (ERROR_ALREADY_EXISTS); + return (FALSE); + } + +#if HOST_DARWIN + /* If we attempt to use clonefile API we need to unlink the destination file + * first */ + if (clonefile_ptr != NULL) { + + /* Bail out if the destination is read-only */ + if (!is_file_writable (&dest_st, utf8_dest)) { + g_free (utf8_src); + g_free (utf8_dest); + MONO_ENTER_GC_SAFE; + close (src_fd); + MONO_EXIT_GC_SAFE; + + mono_w32error_set_last (ERROR_ACCESS_DENIED); + return (FALSE); + } + + _wapi_unlink (utf8_dest); + } +#endif } +#if HOST_DARWIN + if (clonefile_ptr != NULL) { + ret = _wapi_clonefile (utf8_src, utf8_dest, 0); + if (ret == 0 || errno != ENOTSUP) { + g_free (utf8_src); + g_free (utf8_dest); + MONO_ENTER_GC_SAFE; + close (src_fd); + MONO_EXIT_GC_SAFE; + + if (ret == 0) { + return (TRUE); + } else { + _wapi_set_last_error_from_errno (); + return (FALSE); + } + } + } +#endif + if (fail_if_exists) { dest_fd = _wapi_open (utf8_dest, O_WRONLY | O_CREAT | O_EXCL, st.st_mode); } else { @@ -2533,7 +2559,6 @@ CopyFile (const gunichar2 *name, const gunichar2 *dest_name, gboolean fail_if_ex g_free (utf8_dest); return ret; -#endif } static gchar* @@ -4887,6 +4912,12 @@ mono_w32file_init (void) finds = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, finds_remove); mono_coop_mutex_init (&finds_mutex); +#if HOST_DARWIN + libc_handle = dlopen ("/usr/lib/libc.dylib", 0); + g_assert (libc_handle); + clonefile_ptr = (clonefile_fn)dlsym (libc_handle, "clonefile"); +#endif + if (g_hasenv ("MONO_STRICT_IO_EMULATION")) lock_while_writing = TRUE; } @@ -4901,6 +4932,10 @@ mono_w32file_cleanup (void) g_hash_table_destroy (finds); mono_coop_mutex_destroy (&finds_mutex); + +#if HOST_DARWIN + dlclose (libc_handle); +#endif } gboolean -- 2.11.4.GIT