add link_cow feature

When enabled if a regular file is opened which has a link count > 1 it will
copy the file to a temporary file and rename over the original. Effectively
breaking the link. This behavior is similar to cow-shell and other LD_PRELOAD
based "CoW" solutions.
This commit is contained in:
Antonio SJ Musumeci 2018-10-02 14:22:37 -04:00
parent 19a7d43b31
commit 93f7d7d927
18 changed files with 424 additions and 127 deletions

View File

@ -1,6 +1,6 @@
% mergerfs(1) mergerfs user manual
% Antonio SJ Musumeci <trapexit@spawn.link>
% 2018-09-30
% 2018-10-05
# NAME
@ -72,6 +72,7 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
* **nullrw=true|false**: turns reads and writes into no-ops. The request will succeed but do nothing. Useful for benchmarking mergerfs. (default: false)
* **ignorepponrename=true|false**: ignore path preserving on rename. Typically rename and link act differently depending on the policy of `create` (read below). Enabling this will cause rename and link to always use the non-path preserving behavior. This means files, when renamed or linked, will stay on the same drive. (default: false)
* **security_capability=true|false**: If false return ENOATTR when xattr security.capability is queried. (default: true)
* **link_cow=true|false**: When enabled if a regular file is opened which has a link count > 1 it will copy the file to a temporary file and rename over the original. Breaking the link and providing a basic copy-on-write function similar to cow-shell. (default: false)
* **threads=num**: number of threads to use in multithreaded mode. When set to zero (the default) it will attempt to discover and use the number of logical cores. If the lookup fails it will fall back to using 4. If the thread count is set negative it will look up the number of cores then divide by the absolute value. ie. threads=-2 on an 8 core machine will result in 8 / 2 = 4 threads. There will always be at least 1 thread. NOTE: higher number of threads increases parallelism but usually decreases throughput. (default: number of cores) *NOTE2:* the option is unavailable when built with system libfuse.
* **fsname=name**: sets the name of the filesystem as seen in **mount**, **df**, etc. Defaults to a list of the source paths concatenated together with the longest common prefix removed.
* **func.&lt;func&gt;=&lt;policy&gt;**: sets the specific FUSE function's policy. See below for the list of value types. Example: **func.getattr=newest**
@ -646,6 +647,10 @@ See the previous question's answer.
Yes. You need to use `use_ino` to support proper reporting of inodes. Read the section "rename & link" for caveats.
#### Does mergerfs support CoW / copy-on-write?
Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs or aufs sense. It does offer a [cow-shell](http://manpages.ubuntu.com/manpages/bionic/man1/cow-shell.1.html) like hardlink breaking (copy to temp file then rename over original) which can be useful when wanting to save space by hardlinking duplicate files but wish to treat each name as if it were a unique and separate file.
#### Why can't I see my files / directories?
It's almost always a permissions issue. Unlike mhddfs, which runs as root and attempts to access content as such, mergerfs always changes it's credentials to that of the caller. This means that if the user does not have access to a file or directory than neither will mergerfs. However, because mergerfs is creating a union of paths it may be able to read some files and directories on one drive but not another resulting in an incomplete set.

View File

@ -1,7 +1,7 @@
.\"t
.\" Automatically generated by Pandoc 1.19.2.4
.\"
.TH "mergerfs" "1" "2018\-09\-30" "mergerfs user manual" ""
.TH "mergerfs" "1" "2018\-10\-05" "mergerfs user manual" ""
.hy
.SH NAME
.PP
@ -156,6 +156,13 @@ This means files, when renamed or linked, will stay on the same drive.
xattr security.capability is queried.
(default: true)
.IP \[bu] 2
\f[B]link_cow=true|false\f[]: When enabled if a regular file is opened
which has a link count > 1 it will copy the file to a temporary file and
rename over the original.
Breaking the link and providing a basic copy\-on\-write function similar
to cow\-shell.
(default: false)
.IP \[bu] 2
\f[B]threads=num\f[]: number of threads to use in multithreaded mode.
When set to zero (the default) it will attempt to discover and use the
number of logical cores.
@ -1361,6 +1368,16 @@ See the previous question\[aq]s answer.
Yes.
You need to use \f[C]use_ino\f[] to support proper reporting of inodes.
Read the section "rename & link" for caveats.
.SS Does mergerfs support CoW / copy\-on\-write?
.PP
Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs
or aufs sense.
It does offer a
cow\-shell (http://manpages.ubuntu.com/manpages/bionic/man1/cow-shell.1.html)
like hardlink breaking (copy to temp file then rename over original)
which can be useful when wanting to save space by hardlinking duplicate
files but wish to treat each name as if it were a unique and separate
file.
.SS Why can\[aq]t I see my files / directories?
.PP
It\[aq]s almost always a permissions issue.

View File

@ -46,6 +46,7 @@ namespace mergerfs
nullrw(false),
ignorepponrename(false),
security_capability(true),
link_cow(false),
POLICYINIT(access),
POLICYINIT(chmod),
POLICYINIT(chown),

View File

@ -16,17 +16,17 @@
#pragma once
#include "fusefunc.hpp"
#include "policy.hpp"
#include <fuse.h>
#include <sys/stat.h>
#include <stdint.h>
#include <sys/stat.h>
#include <string>
#include <vector>
#include "policy.hpp"
#include "fusefunc.hpp"
namespace mergerfs
{
class Config
@ -37,8 +37,8 @@ namespace mergerfs
public:
int set_func_policy(const std::string &fusefunc_,
const std::string &policy_);
int set_category_policy(const std::string &category,
const std::string &policy);
int set_category_policy(const std::string &category_,
const std::string &policy_);
public:
std::string destmount;
@ -53,6 +53,7 @@ namespace mergerfs
bool nullrw;
bool ignorepponrename;
bool security_capability;
bool link_cow;
public:
const Policy *policies[FuseFunc::Enum::END];

View File

@ -18,30 +18,49 @@
#pragma once
#include <string>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
namespace fs
{
static
inline
int
open(const std::string &path,
const int flags)
open(const char *path_,
const int flags_)
{
return ::open(path.c_str(),flags);
return ::open(path_,flags_);
}
static
inline
int
open(const std::string &path,
const int flags,
const mode_t mode)
open(const char *path_,
const int flags_,
const mode_t mode_)
{
return ::open(path.c_str(),flags,mode);
return ::open(path_,flags_,mode_);
}
static
inline
int
open(const std::string &path_,
const int flags_)
{
return fs::open(path_.c_str(),flags_);
}
static
inline
int
open(const std::string &path_,
const int flags_,
const mode_t mode_)
{
return fs::open(path_.c_str(),flags_,mode_);
}
}

View File

@ -25,9 +25,18 @@ namespace fs
static
inline
int
rename(const std::string &oldpath,
const std::string &newpath)
rename(const char *oldpath_,
const char *newpath_)
{
return ::rename(oldpath.c_str(),newpath.c_str());
return ::rename(oldpath_,newpath_);
}
static
inline
int
rename(const std::string &oldpath_,
const std::string &newpath_)
{
return fs::rename(oldpath_.c_str(),newpath_.c_str());
}
}

View File

@ -27,8 +27,16 @@ namespace fs
static
inline
int
unlink(const std::string &path)
unlink(const char *path_)
{
return ::unlink(path.c_str());
return ::unlink(path_);
}
static
inline
int
unlink(const std::string &path_)
{
return fs::unlink(path_.c_str());
}
}

View File

@ -14,12 +14,6 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <fcntl.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include "errno.hpp"
#include "fs_attr.hpp"
#include "fs_base_chmod.hpp"
@ -37,6 +31,12 @@
#include "fs_sendfile.hpp"
#include "fs_xattr.hpp"
#include <fcntl.h>
#include <stdlib.h>
#include <string>
#include <vector>
#ifndef O_LARGEFILE
# define O_LARGEFILE 0
#endif
@ -49,36 +49,36 @@ using std::string;
using std::vector;
int
writen(const int fd,
const char *buf,
const size_t count)
writen(const int fd_,
const char *buf_,
const size_t count_)
{
size_t nleft;
ssize_t nwritten;
nleft = count;
nleft = count_;
do
{
nwritten = fs::write(fd,buf,nleft);
nwritten = fs::write(fd_,buf_,nleft);
if((nwritten == -1) && (errno == EINTR))
continue;
if(nwritten == -1)
return -1;
nleft -= nwritten;
buf += nwritten;
buf_ += nwritten;
}
while(nleft > 0);
return count;
return count_;
}
static
int
copyfile_rw(const int fdin,
const int fdout,
const size_t count,
const size_t blocksize)
copyfile_rw(const int src_fd_,
const int dst_fd_,
const size_t count_,
const size_t blocksize_)
{
ssize_t nr;
ssize_t nw;
@ -86,15 +86,15 @@ copyfile_rw(const int fdin,
size_t totalwritten;
vector<char> buf;
bufsize = (blocksize * 16);
bufsize = (blocksize_ * 16);
buf.resize(bufsize);
fs::lseek(fdin,0,SEEK_SET);
fs::lseek(src_fd_,0,SEEK_SET);
totalwritten = 0;
while(totalwritten < count)
while(totalwritten < count_)
{
nr = fs::read(fdin,&buf[0],bufsize);
nr = fs::read(src_fd_,&buf[0],bufsize);
if(nr == 0)
return totalwritten;
if((nr == -1) && (errno == EINTR))
@ -102,7 +102,7 @@ copyfile_rw(const int fdin,
if(nr == -1)
return -1;
nw = writen(fdout,&buf[0],nr);
nw = writen(dst_fd_,&buf[0],nr);
if(nw == -1)
return -1;
@ -114,30 +114,30 @@ copyfile_rw(const int fdin,
static
int
copydata(const int fdin,
const int fdout,
const size_t count,
const size_t blocksize)
copydata(const int src_fd_,
const int dst_fd_,
const size_t count_,
const size_t blocksize_)
{
int rv;
fs::fadvise_willneed(fdin,0,count);
fs::fadvise_sequential(fdin,0,count);
fs::fadvise_willneed(src_fd_,0,count_);
fs::fadvise_sequential(src_fd_,0,count_);
fs::fallocate(fdout,0,0,count);
fs::fallocate(dst_fd_,0,0,count_);
rv = fs::sendfile(fdin,fdout,count);
rv = fs::sendfile(src_fd_,dst_fd_,count_);
if((rv == -1) && ((errno == EINVAL) || (errno == ENOSYS)))
return ::copyfile_rw(fdin,fdout,count,blocksize);
return ::copyfile_rw(src_fd_,dst_fd_,count_,blocksize_);
return rv;
}
static
bool
ignorable_error(const int err)
ignorable_error(const int err_)
{
switch(err)
switch(err_)
{
case ENOTTY:
case ENOTSUP:
@ -153,69 +153,40 @@ ignorable_error(const int err)
namespace fs
{
int
clonefile(const int fdin,
const int fdout)
clonefile(const int src_fd_,
const int dst_fd_)
{
int rv;
struct stat stin;
struct stat src_st;
rv = fs::fstat(fdin,stin);
rv = fs::fstat(src_fd_,src_st);
if(rv == -1)
return -1;
rv = ::copydata(fdin,fdout,stin.st_size,stin.st_blksize);
rv = ::copydata(src_fd_,dst_fd_,src_st.st_size,src_st.st_blksize);
if(rv == -1)
return -1;
rv = fs::attr::copy(fdin,fdout);
rv = fs::attr::copy(src_fd_,dst_fd_);
if((rv == -1) && !ignorable_error(errno))
return -1;
rv = fs::xattr::copy(fdin,fdout);
rv = fs::xattr::copy(src_fd_,dst_fd_);
if((rv == -1) && !ignorable_error(errno))
return -1;
rv = fs::fchown_check_on_error(fdout,stin);
rv = fs::fchown_check_on_error(dst_fd_,src_st);
if(rv == -1)
return -1;
rv = fs::fchmod_check_on_error(fdout,stin);
rv = fs::fchmod_check_on_error(dst_fd_,src_st);
if(rv == -1)
return -1;
rv = fs::utime(fdout,stin);
rv = fs::utime(dst_fd_,src_st);
if(rv == -1)
return -1;
return 0;
}
int
clonefile(const string &in,
const string &out)
{
int rv;
int fdin;
int fdout;
int error;
fdin = fs::open(in,O_RDONLY|O_NOFOLLOW);
if(fdin == -1)
return -1;
const int flags = O_CREAT|O_LARGEFILE|O_NOATIME|O_NOFOLLOW|O_TRUNC|O_WRONLY;
const mode_t mode = S_IWUSR;
fdout = fs::open(out,flags,mode);
if(fdout == -1)
return -1;
rv = fs::clonefile(fdin,fdout);
error = errno;
fs::close(fdin);
fs::close(fdout);
errno = error;
return rv;
}
}

View File

@ -16,12 +16,9 @@
#pragma once
#include <string>
namespace fs
{
int clonefile(const int fdin,
const int fdout);
int clonefile(const std::string &from,
const std::string &to);
int
clonefile(const int src_fd_,
const int dst_fd_);
}

118
src/fs_cow.cpp Normal file
View File

@ -0,0 +1,118 @@
/*
ISC License
Copyright (c) 2018, 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_clonefile.hpp"
#include "fs_mktemp.hpp"
#include "fs_base_close.hpp"
#include "fs_base_open.hpp"
#include "fs_base_rename.hpp"
#include "fs_base_stat.hpp"
#include "fs_base_unlink.hpp"
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
using std::string;
static
int
cleanup_on_error(const int src_fd_,
const int dst_fd_ = -1,
const string &dst_fullpath_ = string())
{
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;
}
namespace fs
{
namespace cow
{
bool
is_eligible(const int flags_,
const struct stat &st_)
{
return (((flags_ & O_RDWR) || (flags_ & O_WRONLY)) &&
(S_ISREG(st_.st_mode)) &&
(st_.st_nlink > 1));
}
bool
is_eligible(const char *fullpath_,
const int flags_)
{
int rv;
struct stat st;
rv = fs::stat(fullpath_,st);
if(rv == -1)
return false;
return fs::cow::is_eligible(flags_,st);
}
int
break_link(const char *src_fullpath_)
{
int rv;
int src_fd;
int dst_fd;
string dst_fullpath;
src_fd = fs::open(src_fullpath_,O_RDONLY|O_NOFOLLOW|O_LARGEFILE);
if(src_fd == -1)
return -1;
dst_fullpath = src_fullpath_;
dst_fd = fs::mktemp(dst_fullpath,O_WRONLY|O_LARGEFILE);
if(dst_fd == -1)
return cleanup_on_error(src_fd);
rv = fs::clonefile(src_fd,dst_fd);
if(rv == -1)
return cleanup_on_error(src_fd,dst_fd,dst_fullpath);
rv = fs::rename(dst_fullpath,src_fullpath_);
if(rv == -1)
return cleanup_on_error(src_fd,dst_fd,dst_fullpath);
fs::close(src_fd);
fs::close(dst_fd);
return 0;
}
}
}

33
src/fs_cow.hpp Normal file
View File

@ -0,0 +1,33 @@
/*
ISC License
Copyright (c) 2018, 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
#include <sys/stat.h>
#include <sys/types.h>
namespace fs
{
namespace cow
{
bool is_eligible(const int flags_, const struct stat &st_);
bool is_eligible(const char *fullpath_, const int flags_);
int break_link(const char *fullpath_);
}
}

71
src/fs_mktemp.cpp Normal file
View File

@ -0,0 +1,71 @@
/*
ISC License
Copyright (c) 2018, 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_base_open.hpp"
#include <fcntl.h>
#include <string.h>
#include <cstdlib>
using std::string;
static
string
generate_tmp_path(const string &base_)
{
string tmp;
tmp = base_;
tmp += '_';
for(int i = 0; i < 6; i++)
tmp += ('A' + (std::rand() % 26));
return tmp;
}
namespace fs
{
int
mktemp(string &base_,
const int flags_)
{
int fd;
int count;
int flags;
string tmppath;
fd = -1;
count = 10;
flags = (flags_ | O_EXCL | O_CREAT);
while(count-- > 0)
{
tmppath = generate_tmp_path(base_);
fd = fs::open(tmppath,flags,S_IWUSR);
if((fd == -1) && (errno == EEXIST))
continue;
else if(fd != -1)
base_ = tmppath;
return fd;
}
return (errno=EEXIST,-1);
}
}

28
src/fs_mktemp.hpp Normal file
View File

@ -0,0 +1,28 @@
/*
ISC License
Copyright (c) 2018, 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
#include <string>
namespace fs
{
int
mktemp(std::string &base_,
const int flags_);
}

View File

@ -210,6 +210,8 @@ _getxattr_controlfile(const Config &config,
_getxattr_controlfile_bool(config.ignorepponrename,attrvalue);
else if(attr[2] == "security_capability")
_getxattr_controlfile_bool(config.security_capability,attrvalue);
else if(attr[2] == "link_cow")
_getxattr_controlfile_bool(config.link_cow,attrvalue);
else if(attr[2] == "policies")
_getxattr_controlfile_policies(config,attrvalue);
else if(attr[2] == "version")

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2018, 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
@ -52,6 +52,7 @@ _listxattr_controlfile(char *list,
("user.mergerfs.nullrw")
("user.mergerfs.ignorepponrename")
("user.mergerfs.security_capability")
("user.mergerfs.link_cow")
("user.mergerfs.policies")
("user.mergerfs.version")
("user.mergerfs.pid");

View File

@ -16,61 +16,67 @@
#include <fuse.h>
#include <fcntl.h>
#include <string>
#include <vector>
#include "config.hpp"
#include "errno.hpp"
#include "fileinfo.hpp"
#include "fs_base_open.hpp"
#include "fs_cow.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fcntl.h>
#include <string>
#include <vector>
using std::string;
using std::vector;
using mergerfs::Policy;
static
int
_open_core(const string *basepath,
const char *fusepath,
const int flags,
uint64_t &fh)
_open_core(const string *basepath_,
const char *fusepath_,
const int flags_,
const bool link_cow_,
uint64_t &fh_)
{
int fd;
string fullpath;
fs::path::make(basepath,fusepath,fullpath);
fs::path::make(basepath_,fusepath_,fullpath);
fd = fs::open(fullpath,flags);
if(link_cow_ && fs::cow::is_eligible(fullpath.c_str(),flags_))
fs::cow::break_link(fullpath.c_str());
fd = fs::open(fullpath,flags_);
if(fd == -1)
return -errno;
fh = reinterpret_cast<uint64_t>(new FileInfo(fd,fusepath));
fh_ = reinterpret_cast<uint64_t>(new FileInfo(fd,fusepath_));
return 0;
}
static
int
_open(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const uint64_t minfreespace,
const char *fusepath,
const int flags,
uint64_t &fh)
_open(Policy::Func::Search searchFunc_,
const vector<string> &srcmounts_,
const uint64_t minfreespace_,
const char *fusepath_,
const int flags_,
const bool link_cow_,
uint64_t &fh_)
{
int rv;
vector<const string*> basepaths;
rv = searchFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = searchFunc_(srcmounts_,fusepath_,minfreespace_,basepaths);
if(rv == -1)
return -errno;
return _open_core(basepaths[0],fusepath,flags,fh);
return _open_core(basepaths[0],fusepath_,flags_,link_cow_,fh_);
}
namespace mergerfs
@ -78,8 +84,8 @@ namespace mergerfs
namespace fuse
{
int
open(const char *fusepath,
fuse_file_info *ffi)
open(const char *fusepath_,
fuse_file_info *ffi_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
@ -89,9 +95,10 @@ namespace mergerfs
return _open(config.open,
config.srcmounts,
config.minfreespace,
fusepath,
ffi->flags,
ffi->fh);
fusepath_,
ffi_->flags,
config.link_cow,
ffi_->fh);
}
}
}

View File

@ -197,6 +197,8 @@ parse_and_process_kv_arg(Config &config,
rv = parse_and_process(value,config.ignorepponrename);
else if(key == "security_capability")
rv = parse_and_process(value,config.security_capability);
else if(key == "link_cow")
rv = parse_and_process(value,config.link_cow);
}
if(rv == -1)
@ -299,12 +301,15 @@ usage(void)
" timeout in seconds before will turn to symlinks.\n"
" default = 3600\n"
" -o nullrw=<bool> Disables reads and writes. For benchmarking.\n"
" default = false\n"
" -o ignorepponrename=<bool>\n"
" Ignore path preserving when performing renames\n"
" and links. default = false\n"
" -o security_capability=<bool>\n"
" When disabled return ENOATTR when the xattr\n"
" security.capability is queried. default = true\n"
" -o link_cow=<bool> delink/clone file on open to simulate CoW.\n"
" default = false\n"
<< std::endl;
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2018, 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
@ -302,6 +302,10 @@ _setxattr_controlfile(Config &config,
return _setxattr_bool(attrval,
flags,
config.security_capability);
else if(attr[2] == "link_cow")
return _setxattr_bool(attrval,
flags,
config.link_cow);
break;
case 4: