Browse Source

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.
pull/509/head
Antonio SJ Musumeci 6 years ago
parent
commit
93f7d7d927
  1. 7
      README.md
  2. 19
      man/mergerfs.1
  3. 1
      src/config.cpp
  4. 13
      src/config.hpp
  5. 37
      src/fs_base_open.hpp
  6. 15
      src/fs_base_rename.hpp
  7. 12
      src/fs_base_unlink.hpp
  8. 115
      src/fs_clonefile.cpp
  9. 9
      src/fs_clonefile.hpp
  10. 118
      src/fs_cow.cpp
  11. 33
      src/fs_cow.hpp
  12. 71
      src/fs_mktemp.cpp
  13. 28
      src/fs_mktemp.hpp
  14. 2
      src/getxattr.cpp
  15. 3
      src/listxattr.cpp
  16. 57
      src/open.cpp
  17. 5
      src/option_parser.cpp
  18. 6
      src/setxattr.cpp

7
README.md

@ -1,6 +1,6 @@
% mergerfs(1) mergerfs user manual % mergerfs(1) mergerfs user manual
% Antonio SJ Musumeci <trapexit@spawn.link> % Antonio SJ Musumeci <trapexit@spawn.link>
% 2018-09-30
% 2018-10-05
# NAME # 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) * **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) * **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) * **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. * **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. * **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** * **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. 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? #### 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. 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.

19
man/mergerfs.1

@ -1,7 +1,7 @@
.\"t .\"t
.\" Automatically generated by Pandoc 1.19.2.4 .\" 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 .hy
.SH NAME .SH NAME
.PP .PP
@ -156,6 +156,13 @@ This means files, when renamed or linked, will stay on the same drive.
xattr security.capability is queried. xattr security.capability is queried.
(default: true) (default: true)
.IP \[bu] 2 .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. \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 When set to zero (the default) it will attempt to discover and use the
number of logical cores. number of logical cores.
@ -1361,6 +1368,16 @@ See the previous question\[aq]s answer.
Yes. Yes.
You need to use \f[C]use_ino\f[] to support proper reporting of inodes. You need to use \f[C]use_ino\f[] to support proper reporting of inodes.
Read the section "rename & link" for caveats. 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? .SS Why can\[aq]t I see my files / directories?
.PP .PP
It\[aq]s almost always a permissions issue. It\[aq]s almost always a permissions issue.

1
src/config.cpp

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

13
src/config.hpp

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

37
src/fs_base_open.hpp

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

15
src/fs_base_rename.hpp

@ -25,9 +25,18 @@ namespace fs
static static
inline inline
int 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());
} }
} }

12
src/fs_base_unlink.hpp

@ -27,8 +27,16 @@ namespace fs
static static
inline inline
int 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());
} }
} }

115
src/fs_clonefile.cpp

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

9
src/fs_clonefile.hpp

@ -16,12 +16,9 @@
#pragma once #pragma once
#include <string>
namespace fs 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

@ -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

@ -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

@ -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

@ -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_);
}

2
src/getxattr.cpp

@ -210,6 +210,8 @@ _getxattr_controlfile(const Config &config,
_getxattr_controlfile_bool(config.ignorepponrename,attrvalue); _getxattr_controlfile_bool(config.ignorepponrename,attrvalue);
else if(attr[2] == "security_capability") else if(attr[2] == "security_capability")
_getxattr_controlfile_bool(config.security_capability,attrvalue); _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") else if(attr[2] == "policies")
_getxattr_controlfile_policies(config,attrvalue); _getxattr_controlfile_policies(config,attrvalue);
else if(attr[2] == "version") else if(attr[2] == "version")

3
src/listxattr.cpp

@ -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 Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above 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.nullrw")
("user.mergerfs.ignorepponrename") ("user.mergerfs.ignorepponrename")
("user.mergerfs.security_capability") ("user.mergerfs.security_capability")
("user.mergerfs.link_cow")
("user.mergerfs.policies") ("user.mergerfs.policies")
("user.mergerfs.version") ("user.mergerfs.version")
("user.mergerfs.pid"); ("user.mergerfs.pid");

57
src/open.cpp

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

5
src/option_parser.cpp

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

6
src/setxattr.cpp

@ -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 Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -302,6 +302,10 @@ _setxattr_controlfile(Config &config,
return _setxattr_bool(attrval, return _setxattr_bool(attrval,
flags, flags,
config.security_capability); config.security_capability);
else if(attr[2] == "link_cow")
return _setxattr_bool(attrval,
flags,
config.link_cow);
break; break;
case 4: case 4:

Loading…
Cancel
Save