diff --git a/src/fs_attr.hpp b/src/fs_attr.hpp index 96d9944b..aae829ae 100644 --- a/src/fs_attr.hpp +++ b/src/fs_attr.hpp @@ -18,13 +18,18 @@ #include +#include "int_types.h" + +#define FS_ATTR_NONE (0) +#define FS_ATTR_CLEAR_IMMUTABLE (1 << 0) namespace fs { namespace attr { - int copy(const int fdin, - const int fdout); + int copy(const int fdin, + const int fdout, + const u32 flags); int copy(const std::string &from, const std::string &to); } diff --git a/src/fs_attr_linux.icpp b/src/fs_attr_linux.icpp index abae1e5e..43bdecba 100644 --- a/src/fs_attr_linux.icpp +++ b/src/fs_attr_linux.icpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "fs_attr.hpp" + #include "errno.hpp" #include "fs_close.hpp" #include "fs_open.hpp" @@ -26,115 +28,112 @@ using std::string; - -namespace fs +static +int +_get_fs_ioc_flags(const int fd, + int &flags) { - namespace attr - { - static - int - get_fs_ioc_flags(const int fd, - int &flags) - { - int rv; + int rv; - rv = fs::ioctl(fd,FS_IOC_GETFLAGS,(void*)&flags); - if((rv == -1) && (errno == EINVAL)) - errno = ENOTSUP; + rv = fs::ioctl(fd,FS_IOC_GETFLAGS,(void*)&flags); + if((rv == -1) && (errno == EINVAL)) + errno = ENOTSUP; - return rv; - } + return rv; +} + +static +int +_get_fs_ioc_flags(const string &file, + int &flags) +{ + int fd; + int rv; + const int openflags = O_RDONLY|O_NONBLOCK; - static - int - get_fs_ioc_flags(const string &file, - int &flags) + fd = fs::open(file,openflags); + if(fd == -1) + return -1; + + rv = ::_get_fs_ioc_flags(fd,flags); + if(rv == -1) { - int fd; - int rv; - const int openflags = O_RDONLY|O_NONBLOCK; - - fd = fs::open(file,openflags); - if(fd == -1) - return -1; - - rv = get_fs_ioc_flags(fd,flags); - if(rv == -1) - { - int error = errno; - fs::close(fd); - errno = error; - return -1; - } - - return fs::close(fd); + int error = errno; + fs::close(fd); + errno = error; + return -1; } - static - int - set_fs_ioc_flags(const int fd, - const int flags) - { - int rv; + return fs::close(fd); +} - rv = fs::ioctl(fd,FS_IOC_SETFLAGS,(void*)&flags); - if((rv == -1) && (errno == EINVAL)) - errno = ENOTSUP; +static +int +_set_fs_ioc_flags(const int fd, + const int flags) +{ + int rv; - return rv; - } + rv = fs::ioctl(fd,FS_IOC_SETFLAGS,(void*)&flags); + if((rv == -1) && (errno == EINVAL)) + errno = ENOTSUP; + + return rv; +} - static - int - set_fs_ioc_flags(const string &file, - const int flags) +static +int +_set_fs_ioc_flags(const string &file, + const int flags) +{ + int fd; + int rv; + const int openflags = O_RDONLY|O_NONBLOCK; + + fd = fs::open(file,openflags); + if(fd == -1) + return -1; + + rv = ::_set_fs_ioc_flags(fd,flags); + if(rv == -1) { - int fd; - int rv; - const int openflags = O_RDONLY|O_NONBLOCK; - - fd = fs::open(file,openflags); - if(fd == -1) - return -1; - - rv = set_fs_ioc_flags(fd,flags); - if(rv == -1) - { - int error = errno; - fs::close(fd); - errno = error; - return -1; - } - - return fs::close(fd); + int error = errno; + fs::close(fd); + errno = error; + return -1; } - int - copy(const int fdin, - const int fdout) - { - int rv; - int flags; + return fs::close(fd); +} - rv = get_fs_ioc_flags(fdin,flags); - if(rv == -1) - return -1; +int +fs::attr::copy(const int fdin, + const int fdout, + const u32 flags_) +{ + int rv; + int flags; - return set_fs_ioc_flags(fdout,flags); - } + rv = ::_get_fs_ioc_flags(fdin,flags); + if(rv == -1) + return -1; - int - copy(const string &from, - const string &to) - { - int rv; - int flags; + if(flags_ & FS_ATTR_CLEAR_IMMUTABLE) + flags = (flags & ~FS_IMMUTABLE_FL); - rv = get_fs_ioc_flags(from,flags); - if(rv == -1) - return -1; + return ::_set_fs_ioc_flags(fdout,flags); +} - return set_fs_ioc_flags(to,flags); - } - } +int +fs::attr::copy(const string &from, + const string &to) +{ + int rv; + int flags; + + rv = ::_get_fs_ioc_flags(from,flags); + if(rv == -1) + return -1; + + return ::_set_fs_ioc_flags(to,flags); } diff --git a/src/fs_clonefile.cpp b/src/fs_clonefile.cpp deleted file mode 100644 index a4e038e5..00000000 --- a/src/fs_clonefile.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - 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. -*/ - -#include "fs_fstat.hpp" -#include "fs_copydata.hpp" -#include "fs_attr.hpp" -#include "fs_xattr.hpp" -#include "fs_fchown.hpp" -#include "fs_fchmod.hpp" -#include "fs_futimens.hpp" - - -namespace l -{ - static - bool - ignorable_error(const int err_) - { - switch(err_) - { - case ENOTTY: - case ENOTSUP: -#if ENOTSUP != EOPNOTSUPP - case EOPNOTSUPP: -#endif - return true; - } - - return false; - } -} - -namespace fs -{ - int - clonefile(const int src_fd_, - const int dst_fd_) - { - int rv; - struct stat src_st; - - rv = fs::fstat(src_fd_,&src_st); - if(rv < 0) - return -1; - - rv = fs::copydata(src_fd_,dst_fd_,src_st.st_size); - if(rv == -1) - return -1; - - // TODO: should not copy "immutable" flag till last - rv = fs::attr::copy(src_fd_,dst_fd_); - if((rv == -1) && !l::ignorable_error(errno)) - return -1; - - rv = fs::xattr::copy(src_fd_,dst_fd_); - if((rv == -1) && !l::ignorable_error(errno)) - return -1; - - rv = fs::fchown_check_on_error(dst_fd_,src_st); - if(rv == -1) - return -1; - - rv = fs::fchmod_check_on_error(dst_fd_,src_st); - if(rv == -1) - return -1; - - rv = fs::futimens(dst_fd_,src_st); - if(rv == -1) - return -1; - - return 0; - } -} diff --git a/src/fs_clonefile.hpp b/src/fs_clonefile.hpp deleted file mode 100644 index 67eeca33..00000000 --- a/src/fs_clonefile.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - 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 - clonefile(const int src_fd, - const int dst_fd); -} diff --git a/src/fs_copydata.cpp b/src/fs_copydata.cpp index fee8821a..b49c9fe3 100644 --- a/src/fs_copydata.cpp +++ b/src/fs_copydata.cpp @@ -16,11 +16,12 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "errno.hpp" #include "fs_copydata_copy_file_range.hpp" #include "fs_copydata_readwrite.hpp" #include "fs_fadvise.hpp" +#include "fs_fallocate.hpp" #include "fs_ficlone.hpp" -#include "fs_ftruncate.hpp" #include @@ -34,9 +35,9 @@ namespace fs { int rv; - rv = fs::ftruncate(dst_fd_,count_); - if(rv == -1) - return -1; + rv = fs::fallocate(dst_fd_,0,0,count_); + if((rv == -1) && (errno == ENOSPC)) + return rv; rv = fs::ficlone(src_fd_,dst_fd_); if(rv != -1) @@ -45,10 +46,10 @@ namespace fs fs::fadvise_willneed(src_fd_,0,count_); fs::fadvise_sequential(src_fd_,0,count_); - rv = fs::copydata_copy_file_range(src_fd_,dst_fd_); + rv = fs::copydata_copy_file_range(src_fd_,dst_fd_,count_); if(rv != -1) return rv; - return fs::copydata_readwrite(src_fd_,dst_fd_); + return fs::copydata_readwrite(src_fd_,dst_fd_,count_); } } diff --git a/src/fs_copydata_copy_file_range.cpp b/src/fs_copydata_copy_file_range.cpp index a1be62b8..d066ddc6 100644 --- a/src/fs_copydata_copy_file_range.cpp +++ b/src/fs_copydata_copy_file_range.cpp @@ -18,57 +18,49 @@ #include "fs_copy_file_range.hpp" #include "fs_fstat.hpp" -#include +#include "int_types.h" -namespace l +static +s64 +_copydata_copy_file_range(const int src_fd_, + const int dst_fd_, + const u64 size_) { - int64_t - copydata_copy_file_range(const int src_fd_, - const int dst_fd_, - uint64_t size_) - { - int64_t rv; - uint64_t nleft; - int64_t src_off; - int64_t dst_off; + s64 rv; + u64 nleft; + s64 src_off; + s64 dst_off; - src_off = 0; - dst_off = 0; - nleft = size_; - do - { - rv = fs::copy_file_range(src_fd_,&src_off,dst_fd_,&dst_off,nleft,0); - if((rv == -1) && (errno == EINTR)) - continue; - if((rv == -1) && (errno == EAGAIN)) - continue; - if(rv == -1) - return -1; + src_off = 0; + dst_off = 0; + nleft = size_; + do + { + rv = fs::copy_file_range(src_fd_,&src_off,dst_fd_,&dst_off,nleft,0); + if((rv == -1) && (errno == EINTR)) + continue; + if((rv == -1) && (errno == EAGAIN)) + continue; + if(rv == -1) + return -1; - nleft -= rv; - } - while(nleft > 0); + nleft -= rv; + } + while(nleft > 0); - return size_; - } + return size_; } namespace fs { - int64_t + s64 copydata_copy_file_range(const int src_fd_, - const int dst_fd_) + const int dst_fd_, + const u64 count_) { - int rv; - struct stat st; - - rv = fs::fstat(src_fd_,&st); - if(rv < 0) - return -1; - - return l::copydata_copy_file_range(src_fd_, + return ::_copydata_copy_file_range(src_fd_, dst_fd_, - st.st_size); + count_); } } diff --git a/src/fs_copydata_copy_file_range.hpp b/src/fs_copydata_copy_file_range.hpp index b22f53ec..70d12eda 100644 --- a/src/fs_copydata_copy_file_range.hpp +++ b/src/fs_copydata_copy_file_range.hpp @@ -16,12 +16,13 @@ #pragma once -#include +#include "int_types.h" namespace fs { - int64_t + s64 copydata_copy_file_range(const int src_fd, - const int dst_fd); + const int dst_fd, + const u64 count); } diff --git a/src/fs_copydata_readwrite.cpp b/src/fs_copydata_readwrite.cpp index 0e318370..e59e450f 100644 --- a/src/fs_copydata_readwrite.cpp +++ b/src/fs_copydata_readwrite.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "fs_copydata_readwrite.hpp" + #include "errno.hpp" #include "fs_fstat.hpp" #include "fs_lseek.hpp" @@ -22,93 +24,83 @@ #include -using std::vector; - -namespace l +static +s64 +_writen(const int fd_, + const char *buf_, + const u64 size_) { - static - int - writen(const int fd_, - const char *buf_, - const size_t size_) - { - ssize_t rv; - size_t nleft; - - nleft = size_; - do - { - rv = fs::write(fd_,buf_,nleft); - if((rv == -1) && (errno == EINTR)) - continue; - if((rv == -1) && (errno == EAGAIN)) - continue; - if(rv == -1) - return -1; - - nleft -= rv; - buf_ += rv; - } - while(nleft > 0); - - return size_; - } + s64 rv; + u64 nleft; + + nleft = size_; + do + { + rv = fs::write(fd_,buf_,nleft); + if((rv == -1) && (errno == EINTR)) + continue; + if((rv == -1) && (errno == EAGAIN)) + continue; + if(rv == -1) + return -1; + + nleft -= rv; + buf_ += rv; + } + while(nleft > 0); + + return size_; +} - static - int - copydata_readwrite(const int src_fd_, - const int dst_fd_, - const size_t count_) - { - ssize_t nr; - ssize_t nw; - ssize_t bufsize; - size_t totalwritten; - vector buf; - - bufsize = (1024 * 1024); - buf.resize(bufsize); - - 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) && (errno == EAGAIN)) - continue; - if(nr == -1) - return -1; - - nw = l::writen(dst_fd_,&buf[0],nr); - if(nw == -1) - return -1; - - totalwritten += nw; - } - - return totalwritten; - } +static +s64 +_copydata_readwrite(const int src_fd_, + const int dst_fd_, + const u64 count_) +{ + s64 nr; + s64 nw; + s64 bufsize; + u64 totalwritten; + std::vector buf; + + bufsize = (1024 * 1024); + buf.resize(bufsize); + + 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) && (errno == EAGAIN)) + 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 + s64 copydata_readwrite(const int src_fd_, - const int dst_fd_) + const int dst_fd_, + const u64 count_) { - int rv; - struct stat st; - - rv = fs::fstat(src_fd_,&st); - if(rv < 0) - return -1; - - return l::copydata_readwrite(src_fd_, + return ::_copydata_readwrite(src_fd_, dst_fd_, - st.st_size); + count_); } } diff --git a/src/fs_copydata_readwrite.hpp b/src/fs_copydata_readwrite.hpp index 43271165..8f329067 100644 --- a/src/fs_copydata_readwrite.hpp +++ b/src/fs_copydata_readwrite.hpp @@ -16,10 +16,12 @@ #pragma once +#include "int_types.h" namespace fs { - int + s64 copydata_readwrite(const int src_fd, - const int dst_fd); + const int dst_fd, + const u64 count); } diff --git a/src/fs_copyfile.cpp b/src/fs_copyfile.cpp index de09b7db..4d8caf29 100644 --- a/src/fs_copyfile.cpp +++ b/src/fs_copyfile.cpp @@ -1,39 +1,153 @@ #include "fs_copyfile.hpp" -#include "fs_clonefile.hpp" +#include "fs_attr.hpp" #include "fs_close.hpp" +#include "fs_copydata.hpp" +#include "fs_fchmod.hpp" +#include "fs_fchown.hpp" +#include "fs_fcntl.hpp" +#include "fs_file_unchanged.hpp" +#include "fs_fstat.hpp" +#include "fs_futimens.hpp" #include "fs_mktemp.hpp" #include "fs_open.hpp" #include "fs_rename.hpp" +#include "fs_unlink.hpp" +#include "fs_xattr.hpp" + #include "scope_guard.hpp" #include +#include + +static +bool +_ignorable_error(const int err_) +{ + switch(err_) + { + case ENOTTY: + case ENOTSUP: +#if ENOTSUP != EOPNOTSUPP + case EOPNOTSUPP: +#endif + return true; + } + + return false; +} + +int +fs::copyfile(const int src_fd_, + const struct stat &src_st_, + const int dst_fd_) +{ + int rv; + + rv = fs::copydata(src_fd_,dst_fd_,src_st_.st_size); + if(rv == -1) + return -1; + + rv = fs::xattr::copy(src_fd_,dst_fd_); + if((rv == -1) && !::_ignorable_error(errno)) + return -1; + + rv = fs::attr::copy(src_fd_,dst_fd_,FS_ATTR_CLEAR_IMMUTABLE); + if((rv == -1) && !::_ignorable_error(errno)) + return -1; + + rv = fs::fchown_check_on_error(dst_fd_,src_st_); + if(rv == -1) + return -1; + + rv = fs::fchmod_check_on_error(dst_fd_,src_st_); + if(rv == -1) + return -1; + + rv = fs::futimens(dst_fd_,src_st_); + if(rv == -1) + return -1; + + return 0; +} + + + + +// Limitations: +// * Doesn't handle immutable files well. Will ignore the flag on attr +// copy. +// * Does not handle non-regular files. int fs::copyfile(const std::filesystem::path &src_, - const std::filesystem::path &dst_) + const std::filesystem::path &dst_, + const u32 flags_) { int rv; int src_fd; int dst_fd; + struct stat src_st = {0}; std::string dst_tmppath; + struct sigaction old_act; + struct sigaction new_act; + + // Done in case fcntl(F_SETLEASE) works. + new_act.sa_handler = SIG_IGN; + new_act.sa_flags = 0; + sigemptyset(&new_act.sa_mask); + sigaction(SIGIO,&new_act,&old_act); + DEFER { sigaction(SIGIO,&old_act,NULL); }; src_fd = fs::open(src_,O_RDONLY|O_NOFOLLOW); if(src_fd < 0) return src_fd; DEFER { fs::close(src_fd); }; - std::tie(dst_fd,dst_tmppath) = fs::mktemp(dst_,O_RDWR); - if(dst_fd < 0) - return dst_fd; - DEFER { fs::close(dst_fd); }; + while(true) + { + std::tie(dst_fd,dst_tmppath) = fs::mktemp(dst_,O_RDWR); + if(dst_fd < 0) + return dst_fd; + DEFER { fs::close(dst_fd); }; + + // If it fails or is unsupported... so be it. This will help + // limit the possibility of the file being modified while the + // copy happens. Opening read-only is fine but open for write or + // truncate will block for others till this finishes or the + // kernel wide timeout (/proc/sys/fs/lease-break-time). + fs::fcntl_setlease_rdlck(src_fd); + DEFER { fs::fcntl_setlease_unlck(src_fd); }; + + // For comparison after the copy to see if the file was + // modified. This could be made more thorough by adding some + // hashing but probably overkill. Also used to provide size and + // other details for data copying. + rv = fs::fstat(src_fd,&src_st); + if(rv < 0) + return rv; + + rv = fs::copyfile(src_fd,src_st,dst_fd); + if(rv < 0) + { + if(flags_ & FS_COPYFILE_CLEANUP_FAILURE) + fs::unlink(dst_tmppath); + return rv; + } - rv = fs::clonefile(src_fd,dst_fd); - if(rv < 0) - return rv; + rv = fs::file_changed(src_fd,src_st); + if(rv == FS_FILE_CHANGED) + { + fs::unlink(dst_tmppath); + continue; + } - rv = fs::rename(dst_tmppath,dst_); + rv = fs::rename(dst_tmppath,dst_); + if((rv < 0) && (flags_ & FS_COPYFILE_CLEANUP_FAILURE)) + fs::unlink(dst_tmppath); + break; + } return rv; } diff --git a/src/fs_copyfile.hpp b/src/fs_copyfile.hpp index e9ed3207..5f7be3a3 100644 --- a/src/fs_copyfile.hpp +++ b/src/fs_copyfile.hpp @@ -1,9 +1,24 @@ #pragma once +#include "int_types.h" + #include +#include + +#define FS_COPYFILE_NONE (0) +#define FS_COPYFILE_CLEANUP_FAILURE (1 << 0) + + namespace fs { - int copyfile(const std::filesystem::path &src, - const std::filesystem::path &dst); + int + copyfile(const int src_fd, + const struct stat &src_st, + const int dst_fd); + + int + copyfile(const std::filesystem::path &src, + const std::filesystem::path &dst, + const u32 flags); } diff --git a/src/fs_cow.cpp b/src/fs_cow.cpp index 01fbf7f2..fef70a55 100644 --- a/src/fs_cow.cpp +++ b/src/fs_cow.cpp @@ -16,17 +16,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "fs_cow.hpp" + #include "errno.hpp" -#include "fs_clonefile.hpp" -#include "fs_close.hpp" #include "fs_lstat.hpp" -#include "fs_mktemp.hpp" -#include "fs_open.hpp" -#include "fs_path.hpp" -#include "fs_rename.hpp" -#include "fs_unlink.hpp" - -#include +#include "fs_copyfile.hpp" #include #include @@ -34,102 +28,51 @@ #include -namespace l +bool +fs::cow::is_eligible(const int flags_) +{ + int accmode; + + accmode = (flags_ & O_ACCMODE); + + return ((accmode == O_RDWR) || + (accmode == O_WRONLY)); +} + +bool +fs::cow::is_eligible(const struct stat &st_) +{ + return ((S_ISREG(st_.st_mode)) && (st_.st_nlink > 1)); +} + +bool +fs::cow::is_eligible(const int flags_, + const struct stat &st_) +{ + return (is_eligible(flags_) && is_eligible(st_)); +} + +bool +fs::cow::is_eligible(const char *fullpath_, + const int flags_) { - static - int - cleanup_on_error(const int src_fd_, - const int dst_fd_ = -1, - const std::string &dst_fullpath_ = {}) - { - int error = errno; - - if(src_fd_ >= 0) - fs::close(src_fd_); - if(dst_fd_ >= 0) - fs::close(dst_fd_); - if(!dst_fullpath_.empty()) - fs::unlink(dst_fullpath_); - - errno = error; - - return -1; - } + int rv; + struct stat st; + + if(!fs::cow::is_eligible(flags_)) + return false; + + rv = fs::lstat(fullpath_,&st); + if(rv == -1) + return false; + + return fs::cow::is_eligible(st); } -namespace fs +int +fs::cow::break_link(const char *src_filepath_) { - namespace cow - { - bool - is_eligible(const int flags_) - { - int accmode; - - accmode = (flags_ & O_ACCMODE); - - return ((accmode == O_RDWR) || - (accmode == O_WRONLY)); - } - - bool - is_eligible(const struct stat &st_) - { - return ((S_ISREG(st_.st_mode)) && (st_.st_nlink > 1)); - } - - bool - is_eligible(const int flags_, - const struct stat &st_) - { - return (is_eligible(flags_) && is_eligible(st_)); - } - - bool - is_eligible(const char *fullpath_, - const int flags_) - { - int rv; - struct stat st; - - if(!fs::cow::is_eligible(flags_)) - return false; - - rv = fs::lstat(fullpath_,&st); - if(rv == -1) - return false; - - return fs::cow::is_eligible(st); - } - - int - break_link(const char *src_fullpath_) - { - int rv; - int src_fd; - int dst_fd; - std::string dst_fullpath; - - src_fd = fs::open(src_fullpath_,O_RDONLY|O_NOFOLLOW); - if(src_fd == -1) - return -1; - - std::tie(dst_fd,dst_fullpath) = fs::mktemp(src_fullpath_,O_WRONLY); - if(dst_fd < 0) - return l::cleanup_on_error(src_fd); - - rv = fs::clonefile(src_fd,dst_fd); - if(rv == -1) - return l::cleanup_on_error(src_fd,dst_fd,dst_fullpath); - - rv = fs::rename(dst_fullpath,src_fullpath_); - if(rv == -1) - return l::cleanup_on_error(src_fd,dst_fd,dst_fullpath); - - fs::close(src_fd); - fs::close(dst_fd); - - return 0; - } - } + return fs::copyfile(src_filepath_, + src_filepath_, + FS_COPYFILE_CLEANUP_FAILURE); } diff --git a/src/fs_fcntl.hpp b/src/fs_fcntl.hpp new file mode 100644 index 00000000..59d6b231 --- /dev/null +++ b/src/fs_fcntl.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace fs +{ + static + inline + int + fcntl(const int fd_, + const int op_) + { + int rv; + + rv = ::fcntl(fd_,op_); + + return ((rv == -1) ? -errno : rv); + } + + static + inline + int + fcntl(const int fd_, + const int op_, + const int arg_) + { + int rv; + + rv = ::fcntl(fd_,op_,arg_); + + return ((rv == -1) ? -errno : rv); + } + + static + inline + int + fcntl_setlease_rdlck(const int fd_) + { +#if defined(F_SETLEASE) && defined(F_RDLCK) + return fs::fcntl(fd_,F_SETLEASE,F_RDLCK); +#else + return -ENOTSUP; +#endif + } + + static + inline + int + fcntl_setlease_unlck(const int fd_) + { +#if defined(F_SETLEASE) && defined(F_UNLCK) + return fs::fcntl(fd_,F_SETLEASE,F_UNLCK); +#else + return -ENOTSUP; +#endif + + } +} diff --git a/src/fs_file_unchanged.hpp b/src/fs_file_unchanged.hpp new file mode 100644 index 00000000..17d7b57c --- /dev/null +++ b/src/fs_file_unchanged.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "fs_fstat.hpp" + +#define FS_FILE_CHANGED 0 +#define FS_FILE_UNCHANGED 1 + +namespace fs +{ + static + inline + int + file_changed(const int fd_, + const struct stat &orig_st_) + { + int rv; + struct stat new_st; + + rv = fs::fstat(fd_,&new_st); + if(rv < 0) + return rv; + + if(orig_st_.st_ino != new_st.st_ino) + return FS_FILE_CHANGED; + if(orig_st_.st_dev != new_st.st_dev) + return FS_FILE_CHANGED; + if(orig_st_.st_size != new_st.st_size) + return FS_FILE_CHANGED; + if(orig_st_.st_mtim.tv_sec != new_st.st_mtim.tv_sec) + return FS_FILE_CHANGED; + if(orig_st_.st_mtim.tv_nsec != new_st.st_mtim.tv_nsec) + return FS_FILE_CHANGED; + + return FS_FILE_UNCHANGED; + } +} diff --git a/src/fs_movefile.cpp b/src/fs_movefile.cpp deleted file mode 100644 index 74d8ec97..00000000 --- a/src/fs_movefile.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - 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. -*/ - -#include "errno.hpp" -#include "fs_clonefile.hpp" -#include "fs_clonepath.hpp" -#include "fs_close.hpp" -#include "fs_file_size.hpp" -#include "fs_findonfs.hpp" -#include "fs_getfl.hpp" -#include "fs_has_space.hpp" -#include "fs_mktemp.hpp" -#include "fs_open.hpp" -#include "fs_path.hpp" -#include "fs_rename.hpp" -#include "fs_stat.hpp" -#include "fs_unlink.hpp" -#include "policy.hpp" -#include "ugid.hpp" - -#include -#include - -#include -#include -#include -#include -#include - - -static -int -cleanup_flags(const int flags_) -{ - int rv; - - rv = flags_; - rv = (rv & ~O_TRUNC); - rv = (rv & ~O_CREAT); - rv = (rv & ~O_EXCL); - - return rv; -} - - -namespace l -{ - static - int - movefile(const Policy::Create &createFunc_, - const Branches::Ptr &branches_, - const std::string &fusepath_, - int origfd_) - { - int rv; - int srcfd; - int dstfd; - int dstfd_flags; - int origfd_flags; - int64_t srcfd_size; - std::string fusedir; - std::string srcfd_branch; - std::string srcfd_filepath; - std::string dstfd_filepath; - std::string dstfd_tmp_filepath; - std::vector dstfd_branch; - - srcfd = -1; - dstfd = -1; - - rv = fs::findonfs(branches_,fusepath_,origfd_,&srcfd_branch); - if(rv == -1) - return -errno; - - rv = createFunc_(branches_,fusepath_.c_str(),dstfd_branch); - if(rv == -1) - return -errno; - - origfd_flags = fs::getfl(origfd_); - if(origfd_flags == -1) - return -errno; - - srcfd_size = fs::file_size(origfd_); - if(srcfd_size == -1) - return -errno; - - if(fs::has_space(dstfd_branch[0]->path,srcfd_size) == false) - return -ENOSPC; - - fusedir = fs::path::dirname(fusepath_); - - rv = fs::clonepath(srcfd_branch,dstfd_branch[0]->path,fusedir); - if(rv == -1) - return -ENOSPC; - - srcfd_filepath = srcfd_branch; - fs::path::append(srcfd_filepath,fusepath_); - srcfd = fs::open(srcfd_filepath,O_RDONLY); - if(srcfd == -1) - return -ENOSPC; - - dstfd_filepath = dstfd_branch[0]->path; - fs::path::append(dstfd_filepath,fusepath_); - std::tie(dstfd,dstfd_tmp_filepath) = fs::mktemp(dstfd_filepath,O_WRONLY); - if(dstfd < 0) - { - fs::close(srcfd); - return -ENOSPC; - } - - rv = fs::clonefile(srcfd,dstfd); - if(rv == -1) - { - fs::close(srcfd); - fs::close(dstfd); - fs::unlink(dstfd_tmp_filepath); - return -ENOSPC; - } - - rv = fs::rename(dstfd_tmp_filepath,dstfd_filepath); - if(rv == -1) - { - fs::close(srcfd); - fs::close(dstfd); - fs::unlink(dstfd_tmp_filepath); - return -ENOSPC; - } - - fs::close(srcfd); - fs::close(dstfd); - - dstfd_flags = ::cleanup_flags(origfd_flags); - rv = fs::open(dstfd_filepath,dstfd_flags); - if(rv == -1) - { - fs::unlink(dstfd_tmp_filepath); - return -ENOSPC; - } - - fs::unlink(srcfd_filepath); - - return rv; - } -} - -namespace fs -{ - int - movefile(const Policy::Create &policy_, - const Branches::Ptr &basepaths_, - const std::string &fusepath_, - int origfd_) - { - return l::movefile(policy_,basepaths_,fusepath_,origfd_); - } - - int - movefile_as_root(const Policy::Create &policy_, - const Branches::Ptr &basepaths_, - const std::string &fusepath_, - int origfd_) - { - const ugid::Set ugid(0,0); - - return fs::movefile(policy_,basepaths_,fusepath_,origfd_); - } -} diff --git a/src/fs_movefile_and_open.cpp b/src/fs_movefile_and_open.cpp new file mode 100644 index 00000000..f9ca12f1 --- /dev/null +++ b/src/fs_movefile_and_open.cpp @@ -0,0 +1,147 @@ +/* + 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. +*/ + +#include "fs_movefile_and_open.hpp" + +#include "errno.hpp" +#include "fs_clonepath.hpp" +#include "fs_close.hpp" +#include "fs_copyfile.hpp" +#include "fs_file_size.hpp" +#include "fs_findonfs.hpp" +#include "fs_getfl.hpp" +#include "fs_has_space.hpp" +#include "fs_mktemp.hpp" +#include "fs_open.hpp" +#include "fs_path.hpp" +#include "fs_rename.hpp" +#include "fs_stat.hpp" +#include "fs_unlink.hpp" +#include "int_types.h" +#include "policy.hpp" +#include "ugid.hpp" + +#include +#include + +#include +#include +#include +#include +#include + + +static +int +_cleanup_flags(const int flags_) +{ + int rv; + + rv = flags_; + rv = (rv & ~O_TRUNC); + rv = (rv & ~O_CREAT); + rv = (rv & ~O_EXCL); + + return rv; +} + +static +int +_movefile_and_open(const Policy::Create &createFunc_, + const Branches::Ptr &branches_, + const std::string &branchpath_, + const std::string &fusepath_, + int origfd_) +{ + int rv; + int dstfd_flags; + int origfd_flags; + s64 src_size; + std::string fusedir; + std::string src_branch; + std::string src_filepath; + std::string dst_filepath; + std::vector dst_branch; + + src_branch = branchpath_; + + rv = createFunc_(branches_,fusepath_.c_str(),dst_branch); + if(rv == -1) + return -errno; + + origfd_flags = fs::getfl(origfd_); + if(origfd_flags == -1) + return -errno; + + src_size = fs::file_size(origfd_); + if(src_size == -1) + return -errno; + + if(fs::has_space(dst_branch[0]->path,src_size) == false) + return -ENOSPC; + + fusedir = fs::path::dirname(fusepath_); + + rv = fs::clonepath(src_branch,dst_branch[0]->path,fusedir); + if(rv == -1) + return -ENOSPC; + + src_filepath = fs::path::make(src_branch,fusepath_); + dst_filepath = fs::path::make(dst_branch[0]->path,fusepath_); + + rv = fs::copyfile(src_filepath,dst_filepath,FS_COPYFILE_CLEANUP_FAILURE); + if(rv < 0) + return -ENOSPC; + + dstfd_flags = ::_cleanup_flags(origfd_flags); + rv = fs::open(dst_filepath,dstfd_flags); + if(rv == -1) + return -ENOSPC; + + fs::unlink(src_filepath); + + return rv; +} + +int +fs::movefile_and_open(const Policy::Create &policy_, + const Branches::Ptr &branches_, + const std::string &branchpath_, + const std::string &fusepath_, + const int origfd_) +{ + return ::_movefile_and_open(policy_, + branches_, + branchpath_, + fusepath_, + origfd_); +} + +int +fs::movefile_and_open_as_root(const Policy::Create &policy_, + const Branches::Ptr &branches_, + const std::string &branchpath_, + const std::string &fusepath_, + const int origfd_) +{ + const ugid::Set ugid(0,0); + + return fs::movefile_and_open(policy_, + branches_, + branchpath_, + fusepath_, + origfd_); +} diff --git a/src/fs_movefile.hpp b/src/fs_movefile_and_open.hpp similarity index 61% rename from src/fs_movefile.hpp rename to src/fs_movefile_and_open.hpp index a6db4e21..effc25bd 100644 --- a/src/fs_movefile.hpp +++ b/src/fs_movefile_and_open.hpp @@ -25,14 +25,16 @@ namespace fs { int - movefile(const Policy::Create &policy, - const Branches::Ptr &branches, - const std::string &fusepath, - int origfd); + movefile_and_open(const Policy::Create &policy, + const Branches::Ptr &branches_, + const std::string &branchpath, + const std::string &fusepath, + const int origfd); int - movefile_as_root(const Policy::Create &policy, - const Branches::Ptr &branches, - const std::string &fusepath, - int origfd); + movefile_and_open_as_root(const Policy::Create &policy, + const Branches::Ptr &branches_, + const std::string &branchpath, + const std::string &fusepath, + int origfd); } diff --git a/src/fsck_mergerfs.cpp b/src/fsck_mergerfs.cpp index 82c3d8ca..9931a246 100644 --- a/src/fsck_mergerfs.cpp +++ b/src/fsck_mergerfs.cpp @@ -239,7 +239,7 @@ _copy_files(const std::string &src_, if(src_ == dst.path) continue; - rv = fs::copyfile(src_,dst.path); + rv = fs::copyfile(src_,dst.path,FS_COPYFILE_NONE); if(rv < 0) fmt::print(stderr, "ERROR: failed copying to {} - {}", diff --git a/src/fuse_write.cpp b/src/fuse_write.cpp index 8127d87a..70b04cca 100644 --- a/src/fuse_write.cpp +++ b/src/fuse_write.cpp @@ -19,7 +19,7 @@ #include "fileinfo.hpp" #include "fs_close.hpp" #include "fs_dup2.hpp" -#include "fs_movefile.hpp" +#include "fs_movefile_and_open.hpp" #include "fs_pwrite.hpp" #include "fs_pwriten.hpp" @@ -51,10 +51,11 @@ namespace l if(cfg->moveonenospc.enabled == false) return err_; - rv = fs::movefile_as_root(cfg->moveonenospc.policy, - cfg->branches, - fi_->fusepath, - fi_->fd); + rv = fs::movefile_and_open_as_root(cfg->moveonenospc.policy, + cfg->branches, + fi_->branch.path, + fi_->fusepath, + fi_->fd); if(rv < 0) return err_; @@ -82,10 +83,11 @@ namespace l if(cfg->moveonenospc.enabled == false) return err_; - rv = fs::movefile_as_root(cfg->moveonenospc.policy, - cfg->branches, - fi_->fusepath, - fi_->fd); + rv = fs::movefile_and_open_as_root(cfg->moveonenospc.policy, + cfg->branches, + fi_->branch.path, + fi_->fusepath, + fi_->fd); if(rv < 0) return err_;