From 85026d5780b3c3ec1488af79b52ef2dd1c3a92fe Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Wed, 10 Oct 2018 10:43:35 -0400 Subject: [PATCH] add FICLONE and copy_file_range to clonefile If available FICLONE and copy_file_range will be tried in addition to sendfile when copying data between two files. The fallback is a tradition read/write loop. On systems that support these it should improve performance. --- src/fs_base_ioctl.hpp | 18 +++- src/fs_clonefile.cpp | 111 ++++-------------------- src/fs_copy_file_range.cpp | 23 +++++ src/fs_copy_file_range.hpp | 40 +++++++++ src/fs_copy_file_range_linux.icpp | 87 +++++++++++++++++++ src/fs_copy_file_range_unsupported.icpp | 42 +++++++++ src/fs_copyfile.cpp | 110 +++++++++++++++++++++++ src/fs_copyfile.hpp | 24 +++++ src/fs_ficlone.cpp | 23 +++++ src/fs_ficlone.hpp | 26 ++++++ src/fs_ficlone_linux.icpp | 36 ++++++++ src/fs_ficlone_unsupported.icpp | 29 +++++++ 12 files changed, 472 insertions(+), 97 deletions(-) create mode 100644 src/fs_copy_file_range.cpp create mode 100644 src/fs_copy_file_range.hpp create mode 100644 src/fs_copy_file_range_linux.icpp create mode 100644 src/fs_copy_file_range_unsupported.icpp create mode 100644 src/fs_copyfile.cpp create mode 100644 src/fs_copyfile.hpp create mode 100644 src/fs_ficlone.cpp create mode 100644 src/fs_ficlone.hpp create mode 100644 src/fs_ficlone_linux.icpp create mode 100644 src/fs_ficlone_unsupported.icpp diff --git a/src/fs_base_ioctl.hpp b/src/fs_base_ioctl.hpp index 0fe1e188..ba322fec 100644 --- a/src/fs_base_ioctl.hpp +++ b/src/fs_base_ioctl.hpp @@ -25,10 +25,20 @@ namespace fs static inline int - ioctl(const int fd, - const unsigned long request, - void *data) + ioctl(const int fd_, + const unsigned long request_, + void *data_) { - return ::ioctl(fd,request,data); + return ::ioctl(fd_,request_,data_); + } + + static + inline + int + ioctl(const int fd_, + const unsigned long request_, + const int int_) + { + return ::ioctl(fd_,request_,int_); } } diff --git a/src/fs_clonefile.cpp b/src/fs_clonefile.cpp index 5e034c9a..24bf6b3b 100644 --- a/src/fs_clonefile.cpp +++ b/src/fs_clonefile.cpp @@ -18,106 +18,22 @@ #include "fs_attr.hpp" #include "fs_base_chmod.hpp" #include "fs_base_chown.hpp" -#include "fs_base_close.hpp" #include "fs_base_fadvise.hpp" #include "fs_base_fallocate.hpp" -#include "fs_base_lseek.hpp" -#include "fs_base_mkdir.hpp" -#include "fs_base_open.hpp" -#include "fs_base_read.hpp" +#include "fs_base_ftruncate.hpp" #include "fs_base_stat.hpp" #include "fs_base_utime.hpp" -#include "fs_base_write.hpp" +#include "fs_copy_file_range.hpp" +#include "fs_copyfile.hpp" +#include "fs_ficlone.hpp" #include "fs_sendfile.hpp" #include "fs_xattr.hpp" -#include -#include - -#include -#include - -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif - -#ifndef O_NOATIME -# define O_NOATIME 0 -#endif - -using std::string; -using std::vector; - -int -writen(const int fd_, - const char *buf_, - const size_t count_) -{ - size_t nleft; - ssize_t nwritten; - - nleft = count_; - do - { - nwritten = fs::write(fd_,buf_,nleft); - if((nwritten == -1) && (errno == EINTR)) - continue; - if(nwritten == -1) - return -1; - - nleft -= nwritten; - buf_ += nwritten; - } - while(nleft > 0); - - return count_; -} - -static -int -copyfile_rw(const int src_fd_, - const int dst_fd_, - const size_t count_, - const size_t blocksize_) -{ - ssize_t nr; - ssize_t nw; - ssize_t bufsize; - size_t totalwritten; - vector buf; - - bufsize = (blocksize_ * 16); - buf.resize(bufsize); - - fs::lseek(src_fd_,0,SEEK_SET); - - totalwritten = 0; - while(totalwritten < count_) - { - nr = fs::read(src_fd_,&buf[0],bufsize); - if(nr == 0) - return totalwritten; - if((nr == -1) && (errno == EINTR)) - continue; - if(nr == -1) - return -1; - - nw = writen(dst_fd_,&buf[0],nr); - if(nw == -1) - return -1; - - totalwritten += nw; - } - - return totalwritten; -} - static int copydata(const int src_fd_, const int dst_fd_, - const size_t count_, - const size_t blocksize_) + const size_t count_) { int rv; @@ -125,12 +41,21 @@ copydata(const int src_fd_, fs::fadvise_sequential(src_fd_,0,count_); fs::fallocate(dst_fd_,0,0,count_); + fs::ftruncate(dst_fd_,count_); + + rv = fs::ficlone(src_fd_,dst_fd_); + if((rv != -1) || ((rv == -1) && (errno != EOPNOTSUPP))) + return rv; + + rv = fs::copy_file_range(src_fd_,dst_fd_,count_); + if((rv != -1) || ((rv == -1) && (errno != EOPNOTSUPP))) + return rv; rv = fs::sendfile(src_fd_,dst_fd_,count_); - if((rv == -1) && ((errno == EINVAL) || (errno == ENOSYS))) - return ::copyfile_rw(src_fd_,dst_fd_,count_,blocksize_); + if((rv != -1) || ((rv == -1) && (errno != EINVAL) && (errno != ENOSYS))) + return rv; - return rv; + return fs::copyfile(src_fd_,dst_fd_); } static @@ -163,7 +88,7 @@ namespace fs if(rv == -1) return -1; - rv = ::copydata(src_fd_,dst_fd_,src_st.st_size,src_st.st_blksize); + rv = ::copydata(src_fd_,dst_fd_,src_st.st_size); if(rv == -1) return -1; diff --git a/src/fs_copy_file_range.cpp b/src/fs_copy_file_range.cpp new file mode 100644 index 00000000..41300db5 --- /dev/null +++ b/src/fs_copy_file_range.cpp @@ -0,0 +1,23 @@ +/* + ISC License + + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifdef __linux__ +# include "fs_copy_file_range_linux.icpp" +#else +# include "fs_copy_file_range_unsupported.icpp" +#endif diff --git a/src/fs_copy_file_range.hpp b/src/fs_copy_file_range.hpp new file mode 100644 index 00000000..0b8984ec --- /dev/null +++ b/src/fs_copy_file_range.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +#include + +#include + +namespace fs +{ + ssize_t + copy_file_range(const int fd_in_, + loff_t *off_in_, + const int fd_out_, + loff_t *off_out_, + const size_t len_, + const unsigned int flags_); + + ssize_t + copy_file_range(const int fd_in_, + const int fd_out_, + const size_t len_, + const unsigned int flags_ = 0); +} diff --git a/src/fs_copy_file_range_linux.icpp b/src/fs_copy_file_range_linux.icpp new file mode 100644 index 00000000..f6016c10 --- /dev/null +++ b/src/fs_copy_file_range_linux.icpp @@ -0,0 +1,87 @@ +/* + ISC License + + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#define _GNU_SOURCE + +#include "errno.hpp" + +#include +#include +#include +#include + +static +loff_t +copy_file_range_(int fd_in_, + loff_t *off_in_, + int fd_out_, + loff_t *off_out_, + size_t len_, + unsigned int flags_) +{ +#ifdef SYS_copy_file_range + return ::syscall(SYS_copy_file_range, + fd_in_, + off_in_, + fd_out_, + off_out_, + len_, + flags_); +#else + return (errno=EOPNOTSUPP,-1); +#endif +} + +namespace fs +{ + ssize_t + copy_file_range(const int fd_in_, + loff_t *off_in_, + const int fd_out_, + loff_t *off_out_, + const size_t len_, + const unsigned int flags_) + { + return ::copy_file_range_(fd_in_, + off_in_, + fd_out_, + off_out_, + len_, + flags_); + } + + ssize_t + copy_file_range(const int fd_in_, + const int fd_out_, + const size_t len_, + const unsigned int flags_) + { + loff_t off_in; + loff_t off_out; + + off_in = 0; + off_out = 0; + + return fs::copy_file_range(fd_in_, + &off_in, + fd_out_, + &off_out, + len_, + flags_); + } +} diff --git a/src/fs_copy_file_range_unsupported.icpp b/src/fs_copy_file_range_unsupported.icpp new file mode 100644 index 00000000..3b460d44 --- /dev/null +++ b/src/fs_copy_file_range_unsupported.icpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include + +namespace fs +{ + ssize_t + copy_file_range(const int fd_in_, + loff_t *off_in_, + const int fd_out_, + loff_t *off_out_, + const size_t len_, + const unsigned int flags_) + { + return (errno=EOPNOTSUPP,-1); + } + + ssize_t + copy_file_range(const int fd_in_, + const int fd_out_, + const size_t len_, + const unsigned int flags_) + { + return (errno=EOPNOTSUPP,-1); + } +} diff --git a/src/fs_copyfile.cpp b/src/fs_copyfile.cpp new file mode 100644 index 00000000..e90b1114 --- /dev/null +++ b/src/fs_copyfile.cpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "errno.hpp" +#include "fs_base_stat.hpp" +#include "fs_base_lseek.hpp" +#include "fs_base_read.hpp" +#include "fs_base_write.hpp" + +#include + +using std::vector; + +static +int +writen(const int fd_, + const char *buf_, + const size_t count_) +{ + size_t nleft; + ssize_t nwritten; + + nleft = count_; + do + { + nwritten = fs::write(fd_,buf_,nleft); + if((nwritten == -1) && (errno == EINTR)) + continue; + if(nwritten == -1) + return -1; + + nleft -= nwritten; + buf_ += nwritten; + } + while(nleft > 0); + + return count_; +} + +static +int +copyfile(const int src_fd_, + const int dst_fd_, + const size_t count_, + const size_t blocksize_) +{ + ssize_t nr; + ssize_t nw; + ssize_t bufsize; + size_t totalwritten; + vector buf; + + bufsize = (blocksize_ * 16); + buf.resize(bufsize); + + fs::lseek(src_fd_,0,SEEK_SET); + + totalwritten = 0; + while(totalwritten < count_) + { + nr = fs::read(src_fd_,&buf[0],bufsize); + if(nr == 0) + return totalwritten; + if((nr == -1) && (errno == EINTR)) + continue; + if(nr == -1) + return -1; + + nw = writen(dst_fd_,&buf[0],nr); + if(nw == -1) + return -1; + + totalwritten += nw; + } + + return totalwritten; +} + +namespace fs +{ + int + copyfile(const int src_fd_, + const int dst_fd_) + { + int rv; + struct stat st; + + rv = fs::fstat(src_fd_,st); + if(rv == -1) + return rv; + + return ::copyfile(src_fd_, + dst_fd_, + st.st_size, + st.st_blksize); + } +} diff --git a/src/fs_copyfile.hpp b/src/fs_copyfile.hpp new file mode 100644 index 00000000..86118fd7 --- /dev/null +++ b/src/fs_copyfile.hpp @@ -0,0 +1,24 @@ +/* + Copyright (c) 2016, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +namespace fs +{ + int + copyfile(const int src_fd_, + const int dst_fd_); +} diff --git a/src/fs_ficlone.cpp b/src/fs_ficlone.cpp new file mode 100644 index 00000000..023345d2 --- /dev/null +++ b/src/fs_ficlone.cpp @@ -0,0 +1,23 @@ +/* + ISC License + + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifdef __linux__ +# include "fs_ficlone_linux.icpp" +#else +# include "fs_ficlone_unsupported.icpp" +#endif diff --git a/src/fs_ficlone.hpp b/src/fs_ficlone.hpp new file mode 100644 index 00000000..879ca3ad --- /dev/null +++ b/src/fs_ficlone.hpp @@ -0,0 +1,26 @@ +/* + ISC License + + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#pragma once + +namespace fs +{ + int + ficlone(const int src_fd_, + const int dst_fd_); +} diff --git a/src/fs_ficlone_linux.icpp b/src/fs_ficlone_linux.icpp new file mode 100644 index 00000000..dfd8f58b --- /dev/null +++ b/src/fs_ficlone_linux.icpp @@ -0,0 +1,36 @@ +/* + ISC License + + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "errno.hpp" +#include "fs_base_ioctl.hpp" + +#include + +namespace fs +{ + int + ficlone(const int src_fd_, + const int dst_fd_) + { +#ifdef FICLONE + return fs::ioctl(dst_fd_,FICLONE,src_fd_); +#else + return (errno=EOPNOTSUPP,-1); +#endif + } +} diff --git a/src/fs_ficlone_unsupported.icpp b/src/fs_ficlone_unsupported.icpp new file mode 100644 index 00000000..aa6c4f0f --- /dev/null +++ b/src/fs_ficlone_unsupported.icpp @@ -0,0 +1,29 @@ +/* + ISC License + + Copyright (c) 2018, Antonio SJ Musumeci + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "errno.hpp" + +namespace fs +{ + int + ficlone(const int src_fd_, + const int dst_fd_) + { + return (errno=EOPNOTSUPP,-1); + } +}