Skip to content

Commit

Permalink
unix: add UV_FS_COPYFILE_FICLONE support
Browse files Browse the repository at this point in the history
UV_FS_COPYFILE_FICLONE attemps to use copy-on-write
semantics in uv_fs_copyfile(). If CoW is not available,
it falls back to a normal copy operation.

Refs: libuv#1465
PR-URL: libuv#1491
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Santiago Gimeno <[email protected]>
  • Loading branch information
cjihrig committed Mar 5, 2018
1 parent cb1acaa commit db91836
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 2 deletions.
5 changes: 5 additions & 0 deletions docs/src/fs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ API
- `UV_FS_COPYFILE_EXCL`: If present, `uv_fs_copyfile()` will fail with
`UV_EEXIST` if the destination path already exists. The default behavior
is to overwrite the destination if it exists.
- `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.
.. warning::
If the destination path is created, but an error occurs while copying
Expand All @@ -258,6 +261,8 @@ API
.. versionadded:: 1.14.0
.. versionchanged:: 1.20.0 `UV_FS_COPYFILE_FICLONE` is 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)
Limited equivalent to :man:`sendfile(2)`.
Expand Down
6 changes: 6 additions & 0 deletions include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,12 @@ UV_EXTERN int uv_fs_write(uv_loop_t* loop,
*/
#define UV_FS_COPYFILE_EXCL 0x0001

/*
* This flag can be used with uv_fs_copyfile() to attempt to create a reflink.
* If copy-on-write is not supported, a fallback copy mechanism is used.
*/
#define UV_FS_COPYFILE_FICLONE 0x0002

UV_EXTERN int uv_fs_copyfile(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
Expand Down
24 changes: 23 additions & 1 deletion src/unix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@

#if defined(__APPLE__)
# include <copyfile.h>
#elif defined(__linux__) && !defined(FICLONE)
# include <sys/ioctl.h>
# define FICLONE _IOW(0x94, 9, int)
#endif

#define INIT(subtype) \
Expand Down Expand Up @@ -790,6 +793,10 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
if (req->flags & UV_FS_COPYFILE_EXCL)
flags |= COPYFILE_EXCL;

#ifdef COPYFILE_CLONE
if (req->flags & UV_FS_COPYFILE_FICLONE)
flags |= COPYFILE_CLONE;
#endif
return copyfile(req->path, req->new_path, NULL, flags);
#else
uv_fs_t fs_req;
Expand Down Expand Up @@ -842,6 +849,21 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
goto out;
}

#ifdef FICLONE
if (req->flags & UV_FS_COPYFILE_FICLONE) {
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) {
err = -errno;
goto out;
}
} else {
goto out;
}
}
#endif

bytes_to_send = statsbuf.st_size;
in_offset = 0;
while (bytes_to_send != 0) {
Expand Down Expand Up @@ -1504,7 +1526,7 @@ int uv_fs_copyfile(uv_loop_t* loop,
uv_fs_cb cb) {
INIT(COPYFILE);

if (flags & ~UV_FS_COPYFILE_EXCL)
if (flags & ~(UV_FS_COPYFILE_EXCL | UV_FS_COPYFILE_FICLONE))
return UV_EINVAL;

PATH2;
Expand Down
2 changes: 1 addition & 1 deletion src/win/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -2334,7 +2334,7 @@ int uv_fs_copyfile(uv_loop_t* loop,

INIT(UV_FS_COPYFILE);

if (flags & ~UV_FS_COPYFILE_EXCL)
if (flags & ~(UV_FS_COPYFILE_EXCL | UV_FS_COPYFILE_FICLONE))
return UV_EINVAL;

err = fs__capture_path(req, path, new_path, cb != NULL);
Expand Down
7 changes: 7 additions & 0 deletions test/test-fs-copyfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ TEST_IMPL(fs_copyfile) {
r = uv_fs_copyfile(loop, &req, fixture, dst, -1, fail_cb);
ASSERT(r == UV_EINVAL);
uv_run(loop, UV_RUN_DEFAULT);

/* Copies file using UV_FS_COPYFILE_FICLONE. */
unlink(dst);
r = uv_fs_copyfile(NULL, &req, fixture, dst, UV_FS_COPYFILE_FICLONE, NULL);
ASSERT(r == 0);
handle_result(&req);

unlink(dst); /* Cleanup */
return 0;
}

0 comments on commit db91836

Please sign in to comment.