From 3ae88200d6f52ea7b3d8ff9b8164adde65f6c43a Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 6 Mar 2018 21:23:35 -0500 Subject: [PATCH] unix,win: add UV_FS_COPYFILE_FICLONE_FORCE support UV_FS_COPYFILE_FICLONE_FORCE attempts to use copy-on-write semantics in uv_fs_copyfile(). If CoW is not available, an error is returned. Refs: https://github.com/libuv/libuv/pull/1465 Refs: https://github.com/libuv/libuv/pull/1491 PR-URL: https://github.com/libuv/libuv/pull/1768 Reviewed-By: Santiago Gimeno --- docs/src/fs.rst | 6 +++++- include/uv.h | 6 ++++++ src/unix/fs.c | 30 +++++++++++++++++++++++++----- src/win/fs.c | 11 ++++++++++- test/test-fs-copyfile.c | 9 +++++++++ 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/docs/src/fs.rst b/docs/src/fs.rst index 77096eff..29227be0 100644 --- a/docs/src/fs.rst +++ b/docs/src/fs.rst @@ -252,6 +252,9 @@ API - `UV_FS_COPYFILE_FICLONE`: If present, `uv_fs_copyfile()` will attempt to create a copy-on-write reflink. If the underlying platform does not support copy-on-write, then a fallback copy mechanism is used. + - `UV_FS_COPYFILE_FICLONE_FORCE`: If present, `uv_fs_copyfile()` will + attempt to create a copy-on-write reflink. If the underlying platform does + not support copy-on-write, then an error is returned. .. warning:: If the destination path is created, but an error occurs while copying @@ -261,7 +264,8 @@ API .. versionadded:: 1.14.0 - .. versionchanged:: 1.20.0 `UV_FS_COPYFILE_FICLONE` is supported. + .. versionchanged:: 1.20.0 `UV_FS_COPYFILE_FICLONE` and + `UV_FS_COPYFILE_FICLONE_FORCE` are supported. .. c:function:: int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, uv_fs_cb cb) diff --git a/include/uv.h b/include/uv.h index cdd251d8..41fad451 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1196,6 +1196,12 @@ UV_EXTERN int uv_fs_write(uv_loop_t* loop, */ #define UV_FS_COPYFILE_FICLONE 0x0002 +/* + * This flag can be used with uv_fs_copyfile() to attempt to create a reflink. + * If copy-on-write is not supported, an error is returned. + */ +#define UV_FS_COPYFILE_FICLONE_FORCE 0x0004 + UV_EXTERN int uv_fs_copyfile(uv_loop_t* loop, uv_fs_t* req, const char* path, diff --git a/src/unix/fs.c b/src/unix/fs.c index e0518d05..de678733 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -797,6 +797,15 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { if (req->flags & UV_FS_COPYFILE_FICLONE) flags |= COPYFILE_CLONE; #endif + + if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { +#ifdef COPYFILE_CLONE_FORCE + flags |= COPYFILE_CLONE_FORCE; +#else + return UV_ENOSYS; +#endif + } + return copyfile(req->path, req->new_path, NULL, flags); #else uv_fs_t fs_req; @@ -850,11 +859,14 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { } #ifdef FICLONE - if (req->flags & UV_FS_COPYFILE_FICLONE) { + if (req->flags & UV_FS_COPYFILE_FICLONE || + req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { if (ioctl(dstfd, FICLONE, srcfd) == -1) { - /* If an error occurred that the sendfile fallback also won't handle, - then exit. Otherwise, fall through to try using sendfile(). */ - if (errno != ENOTTY && errno != EOPNOTSUPP && errno != EXDEV) { + /* If an error occurred that the sendfile fallback also won't handle, or + this is a force clone then exit. Otherwise, fall through to try using + sendfile(). */ + if ((errno != ENOTTY && errno != EOPNOTSUPP && errno != EXDEV) || + req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { err = -errno; goto out; } @@ -862,6 +874,11 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { goto out; } } +#else + if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { + err = UV_ENOSYS; + goto out; + } #endif bytes_to_send = statsbuf.st_size; @@ -1526,8 +1543,11 @@ int uv_fs_copyfile(uv_loop_t* loop, uv_fs_cb cb) { INIT(COPYFILE); - if (flags & ~(UV_FS_COPYFILE_EXCL | UV_FS_COPYFILE_FICLONE)) + if (flags & ~(UV_FS_COPYFILE_EXCL | + UV_FS_COPYFILE_FICLONE | + UV_FS_COPYFILE_FICLONE_FORCE)) { return UV_EINVAL; + } PATH2; req->flags = flags; diff --git a/src/win/fs.c b/src/win/fs.c index 8143d171..e454edac 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -1390,6 +1390,12 @@ static void fs__copyfile(uv_fs_t* req) { int overwrite; flags = req->fs.info.file_flags; + + if (flags & UV_FS_COPYFILE_FICLONE_FORCE) { + SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); + return; + } + overwrite = flags & UV_FS_COPYFILE_EXCL; if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) == 0) { @@ -2334,8 +2340,11 @@ int uv_fs_copyfile(uv_loop_t* loop, INIT(UV_FS_COPYFILE); - if (flags & ~(UV_FS_COPYFILE_EXCL | UV_FS_COPYFILE_FICLONE)) + if (flags & ~(UV_FS_COPYFILE_EXCL | + UV_FS_COPYFILE_FICLONE | + UV_FS_COPYFILE_FICLONE_FORCE)) { return UV_EINVAL; + } err = fs__capture_path(req, path, new_path, cb != NULL); diff --git a/test/test-fs-copyfile.c b/test/test-fs-copyfile.c index 59f3a2c5..6cd43b45 100644 --- a/test/test-fs-copyfile.c +++ b/test/test-fs-copyfile.c @@ -175,6 +175,15 @@ TEST_IMPL(fs_copyfile) { ASSERT(r == 0); handle_result(&req); + /* Copies file using UV_FS_COPYFILE_FICLONE_FORCE. */ + unlink(dst); + r = uv_fs_copyfile(NULL, &req, fixture, dst, UV_FS_COPYFILE_FICLONE_FORCE, + NULL); + ASSERT(r == 0 || r == UV_ENOSYS || r == UV_ENOTSUP || r == UV_ENOTTY); + + if (r == 0) + handle_result(&req); + unlink(dst); /* Cleanup */ return 0; }