macos: fix source not being followed when cloning (#3941)

Fixes: https://github.com/libuv/libuv/issues/3940
This commit is contained in:
Lewis Russell 2023-04-23 10:54:25 +01:00 committed by GitHub
parent 1c935a3445
commit 507f2f950d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -67,7 +67,8 @@
#if defined(__APPLE__)
# include <copyfile.h>
# include <sys/sysctl.h>
# include <sys/clonefile.h>
# define UV__FS_CLONE_ACL 0x0004
#elif defined(__linux__) && !defined(FICLONE)
# include <sys/ioctl.h>
# define FICLONE _IOW(0x94, 9, int)
@ -1242,9 +1243,34 @@ done:
return r;
}
static ssize_t uv__fs_copyfile(uv_fs_t* req) {
#if defined(__APPLE__) && !TARGET_OS_IPHONE
/* On macOS, use the native copyfile(3). */
static int uv__fs_clonefile_mac(uv_fs_t* req) {
static _Atomic int no_clone_acl;
int flags;
flags = UV__FS_CLONE_ACL;
if (atomic_load_explicit(&no_clone_acl, memory_order_relaxed))
flags = 0;
/* Note: clonefile() does not set the group ID on the destination
* file correctly. */
if (!clonefile(req->path, req->new_path, flags))
return 0; /* success */
if (errno == EINVAL) {
atomic_store_explicit(&no_clone_acl, 1, memory_order_relaxed);
errno = 0;
/* UV__FS_CLONE_ACL flag not supported (macOS < 13); try without. */
if (!clonefile(req->path, req->new_path, 0))
return 0; /* success */
}
return UV__ERR(errno);
}
static int uv__fs_fcopyfile_mac(uv_fs_t* req) {
int rc;
copyfile_flags_t flags;
/* Don't overwrite the destination if its permissions disallow it. */
@ -1253,22 +1279,38 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
return UV__ERR(errno);
}
flags = COPYFILE_ALL;
if ((req->flags & UV_FS_COPYFILE_FICLONE) ||
(req->flags & UV_FS_COPYFILE_FICLONE_FORCE)) {
rc = uv__fs_clonefile_mac(req);
if (req->flags & UV_FS_COPYFILE_FICLONE)
flags |= COPYFILE_CLONE;
/* Return on success.
* If an error occurred and force was set, return the error to the caller;
* fall back to copyfile() when force was not set. */
if (rc == 0 || (req->flags & UV_FS_COPYFILE_FICLONE_FORCE))
return rc;
if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE)
flags |= COPYFILE_CLONE_FORCE;
if (req->flags & UV_FS_COPYFILE_EXCL)
flags |= COPYFILE_EXCL;
/* cloning failed. Inherit clonefile flags required for
falling back to copyfile. */
flags = COPYFILE_ALL | COPYFILE_NOFOLLOW_SRC | COPYFILE_EXCL;
} else {
flags = COPYFILE_ALL;
if (req->flags & UV_FS_COPYFILE_EXCL)
flags |= COPYFILE_EXCL;
}
if (copyfile(req->path, req->new_path, NULL, flags))
return UV__ERR(errno);
return 0;
#else /* defined(__APPLE__) && !TARGET_OS_IPHONE */
}
#endif /* defined(__APPLE__) && !TARGET_OS_IPHONE */
static int uv__fs_copyfile(uv_fs_t* req) {
#if defined(__APPLE__) && !TARGET_OS_IPHONE
/* On macOS, use the native clonefile(2)/copyfile(3). */
return uv__fs_fcopyfile_mac(req);
#else
uv_fs_t fs_req;
uv_file srcfd;
uv_file dstfd;