Browse Source

Rework movefile and cow break to use copyfile (#1488)

pull/1489/head
trapexit 3 months ago
committed by GitHub
parent
commit
e216a81a27
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      src/fs_attr.hpp
  2. 185
      src/fs_attr_linux.icpp
  3. 86
      src/fs_clonefile.cpp
  4. 25
      src/fs_clonefile.hpp
  5. 13
      src/fs_copydata.cpp
  6. 70
      src/fs_copydata_copy_file_range.cpp
  7. 7
      src/fs_copydata_copy_file_range.hpp
  8. 148
      src/fs_copydata_readwrite.cpp
  9. 6
      src/fs_copydata_readwrite.hpp
  10. 134
      src/fs_copyfile.cpp
  11. 19
      src/fs_copyfile.hpp
  12. 149
      src/fs_cow.cpp
  13. 58
      src/fs_fcntl.hpp
  14. 36
      src/fs_file_unchanged.hpp
  15. 180
      src/fs_movefile.cpp
  16. 147
      src/fs_movefile_and_open.cpp
  17. 18
      src/fs_movefile_and_open.hpp
  18. 2
      src/fsck_mergerfs.cpp
  19. 20
      src/fuse_write.cpp

9
src/fs_attr.hpp

@ -18,13 +18,18 @@
#include <string>
#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);
}

185
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);
}

86
src/fs_clonefile.cpp

@ -1,86 +0,0 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
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;
}
}

25
src/fs_clonefile.hpp

@ -1,25 +0,0 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
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);
}

13
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 <stddef.h>
@ -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_);
}
}

70
src/fs_copydata_copy_file_range.cpp

@ -18,57 +18,49 @@
#include "fs_copy_file_range.hpp"
#include "fs_fstat.hpp"
#include <cstdint>
#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_);
}
}

7
src/fs_copydata_copy_file_range.hpp

@ -16,12 +16,13 @@
#pragma once
#include <cstdint>
#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);
}

148
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 <vector>
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<char> 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<char> 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_);
}
}

6
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);
}

134
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 <fcntl.h>
#include <signal.h>
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;
}

19
src/fs_copyfile.hpp

@ -1,9 +1,24 @@
#pragma once
#include "int_types.h"
#include <filesystem>
#include <sys/stat.h>
#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);
}

149
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 <string>
#include "fs_copyfile.hpp"
#include <fcntl.h>
#include <sys/stat.h>
@ -34,102 +28,51 @@
#include <unistd.h>
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);
}

58
src/fs_fcntl.hpp

@ -0,0 +1,58 @@
#pragma once
#include <fcntl.h>
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
}
}

36
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;
}
}

180
src/fs_movefile.cpp

@ -1,180 +0,0 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
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 <string>
#include <vector>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
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<Branch*> 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_);
}
}

147
src/fs_movefile_and_open.cpp

@ -0,0 +1,147 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
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 <string>
#include <vector>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
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<Branch*> 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_);
}

18
src/fs_movefile.hpp → 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);
}

2
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 {} - {}",

20
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_;

Loading…
Cancel
Save