Browse Source

move on enospc when writing feature. closes #141

This feature mimics the standard mhddfs behavior but is more thorough.
If a write fails and the errno is set to ENOSPC then mergerfs will (if
the feature is enabled) attempt to move the file to the drive with
the most free space but only if it has enough room for the file plus
the amount to be written. If that transfer is successful it will then
unlink the original file and attempt the previously failed write again.

The copy includes copying the path and file including the acls, owners,
attributes, extended attributes, and timestamps.
pull/144/head
Antonio SJ Musumeci 9 years ago
parent
commit
5808ab7795
  1. 16
      Makefile
  2. 20
      README.md
  3. 4
      src/access.cpp
  4. 4
      src/chmod.cpp
  5. 4
      src/chown.cpp
  6. 69
      src/clone.cpp
  7. 2
      src/clone.hpp
  8. 1
      src/config.cpp
  9. 1
      src/config.hpp
  10. 14
      src/create.cpp
  11. 2
      src/create.hpp
  12. 6
      src/fallocate.cpp
  13. 6
      src/fgetattr.cpp
  14. 34
      src/fileinfo.hpp
  15. 6
      src/flush.cpp
  16. 486
      src/fs.cpp
  17. 91
      src/fs.hpp
  18. 135
      src/fs_attr.cpp
  19. 43
      src/fs_attr.hpp
  20. 231
      src/fs_clonefile.cpp
  21. 33
      src/fs_clonefile.hpp
  22. 117
      src/fs_clonepath.cpp
  23. 32
      src/fs_clonepath.hpp
  24. 117
      src/fs_movefile.cpp
  25. 37
      src/fs_movefile.hpp
  26. 118
      src/fs_path.cpp
  27. 76
      src/fs_path.hpp
  28. 399
      src/fs_xattr.cpp
  29. 78
      src/fs_xattr.hpp
  30. 6
      src/fsync.cpp
  31. 6
      src/ftruncate.cpp
  32. 4
      src/getattr.cpp
  33. 16
      src/getxattr.cpp
  34. 7
      src/ioctl.cpp
  35. 5
      src/link.cpp
  36. 9
      src/listxattr.cpp
  37. 8
      src/mergerfs.cpp
  38. 5
      src/mkdir.cpp
  39. 5
      src/mknod.cpp
  40. 13
      src/open.cpp
  41. 2
      src/open.hpp
  42. 17
      src/option_parser.cpp
  43. 1
      src/policy.hpp
  44. 3
      src/policy_all.cpp
  45. 1
      src/policy_epmfs.cpp
  46. 2
      src/policy_ff.cpp
  47. 3
      src/policy_ffwp.cpp
  48. 3
      src/policy_fwfs.cpp
  49. 3
      src/policy_lfs.cpp
  50. 3
      src/policy_mfs.cpp
  51. 3
      src/policy_newest.cpp
  52. 6
      src/read.cpp
  53. 6
      src/read_buf.cpp
  54. 8
      src/readdir.cpp
  55. 4
      src/readlink.cpp
  56. 12
      src/release.cpp
  57. 4
      src/removexattr.cpp
  58. 4
      src/rename.cpp
  59. 4
      src/rmdir.cpp
  60. 32
      src/setxattr.cpp
  61. 4
      src/symlink.cpp
  62. 4
      src/truncate.cpp
  63. 4
      src/unlink.cpp
  64. 4
      src/utimens.cpp
  65. 33
      src/write.cpp
  66. 44
      src/write_buf.cpp

16
Makefile

@ -104,7 +104,7 @@ $(warning "xattr not available: disabling")
CFLAGS += -DWITHOUT_XATTR
endif
all: $(TARGET) clonepath
all: $(TARGET) clone
help:
@echo "usage: make"
@ -113,7 +113,7 @@ help:
$(TARGET): src/version.hpp obj/obj-stamp $(OBJ)
$(CXX) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
clonepath: $(TARGET)
clone: $(TARGET)
$(LN) -s $< $@
changelog:
@ -139,18 +139,18 @@ obj/%.o: src/%.cpp
clean: rpm-clean
$(RM) -rf obj
$(RM) -f src/version.hpp
$(RM) -f "$(TARGET)" "$(MANPAGE)" clonepath
$(RM) -f "$(TARGET)" "$(MANPAGE)" clone
$(FIND) . -name "*~" -delete
distclean: clean
$(GIT) clean -fd
install: install-base install-clonepath install-man
install: install-base install-clone install-man
install-base: $(TARGET)
$(INSTALL) -v -m 0755 -D "$(TARGET)" "$(INSTALLBINDIR)/$(TARGET)"
install-clonepath: clonepath
install-clone: clone
$(CP) -a $< "$(INSTALLBINDIR)/$<"
install-man: $(MANPAGE)
@ -159,13 +159,13 @@ install-man: $(MANPAGE)
install-strip: install-base
$(STRIP) "$(INSTALLBINDIR)/$(TARGET)"
uninstall: uninstall-base uninstall-clonepath uninstall-man
uninstall: uninstall-base uninstall-clone uninstall-man
uninstall-base:
$(RM) -f "$(INSTALLBINDIR)/$(TARGET)"
uninstall-clonepath:
$(RM) -f "$(INSTALLBINDIR)/clonepath"
uninstall-clone:
$(RM) -f "$(INSTALLBINDIR)/clone"
uninstall-man:
$(RM) -f "$(INSTALLMAN1DIR)/$(MANPAGE)"

20
README.md

@ -33,7 +33,8 @@ Why **mergerfs** when those exist? **mhddfs** has not been updated in some time
* **defaults**: a shortcut for FUSE's **atomic_o_trunc**, **auto_cache**, **big_writes**, **default_permissions**, **splice_move**, **splice_read**, and **splice_write**. These options seem to provide the best performance.
* **direct_io**: causes FUSE to bypass an addition caching step which can increase write speeds at the detriment of read speed.
* **minfreespace**: (defaults to **4G**) the minimum space value used for the **lfs**, **fwfs**, and **epmfs** policies. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively.
* **minfreespace**: the minimum space value used for the **lfs**, **fwfs**, and **epmfs** policies. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
* **moveonenospc**: when enabled (set to **true**) if a **write** fails with **ENOSPC** a scan of all drives will be done looking for the drive with most free space which is at least the size of the file plus the amount which failed to write. An attempt to move the file to that drive will occur (keeping all metadata possible) and if successful the original is unlinked and the write retried. (default: false)
* **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**
* **category.&lt;category&gt;=&lt;policy&gt;**: Sets policy of all FUSE functions in the provided category. Example: **category.create=mfs**
@ -173,8 +174,9 @@ Use `xattr -l /mount/point/.mergerfs` to see all supported keys.
[trapexit:/tmp/mount] $ xattr -l .mergerfs
user.mergerfs.srcmounts: /tmp/a:/tmp/b
user.mergerfs.minfreespace: 4294967295
user.mergerfs.moveonenospc: false
user.mergerfs.policies: all,einval,enosys,enotsup,epmfs,erofs,exdev,ff,ffwp,fwfs,lfs,mfs,newest,rand
user.mergerfs.version: 2.5.0
user.mergerfs.version: x.y.z
user.mergerfs.category.action: all
user.mergerfs.category.create: epmfs
user.mergerfs.category.search: ff
@ -232,10 +234,20 @@ For **user.mergerfs.srcmounts** there are several instructions available for man
| -< | remove first in list |
| -> | remove last in list |
##### minfreespace #####
##### misc #####
Input: interger with an optional suffix. **K**, **M**, or **G**.
Output: value in bytes
Categories and funcs take a policy as described in the previous section. When reading funcs you'll get the policy string. However, with categories you'll get a comma separated list of policies for each type found. For example: if all search functions are **ff** except for **access** which is **ffwp** the value for **user.mergerfs.category.search** will be **ff,ffwp**.
##### moveonenospc #####
Input: **true** and **false**
Ouput: **true** or **false**
##### categories / funcs #####
Input: short policy string as described elsewhere in this document
Output: the policy string except for categories where its funcs have multiple types. In that case it will be a comma separated list.
#### mergerfs file xattrs ####

4
src/access.cpp

@ -34,10 +34,10 @@
#include <unistd.h>
#include <errno.h>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

4
src/chmod.cpp

@ -28,10 +28,10 @@
#include <string>
#include <vector>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

4
src/chown.cpp

@ -29,10 +29,10 @@
#include <string>
#include <vector>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

69
src/clone.cpp

@ -0,0 +1,69 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#include "fs.hpp"
#include "fs_clonefile.hpp"
#include "fs_clonepath.hpp"
namespace clonetool
{
static
void
print_usage_and__exit(void)
{
std::cerr << "usage: clone "
<< "[path <sourcedir> <destdir> <relativepath>]"
<< " | "
<< "[file <source> <dest>]"
<< std::endl;
_exit(1);
}
int
main(const int argc,
char * const argv[])
{
int rv = 0;
if(argc == 4 && !strcmp(argv[1],"file"))
rv = fs::clonefile(argv[2],argv[3]);
else if(argc == 5 && !strcmp(argv[1],"path"))
rv = fs::clonepath(argv[2],argv[3],argv[4]);
else
print_usage_and__exit();
if(rv == -1)
std::cerr << "error: "
<< strerror(errno)
<< std::endl;
return 0;
}
}

2
src/clonepath.hpp → src/clone.hpp

@ -22,7 +22,7 @@
THE SOFTWARE.
*/
namespace clonepath
namespace clonetool
{
int
main(const int argc,

1
src/config.cpp

@ -45,6 +45,7 @@ namespace mergerfs
srcmounts(),
srcmountslock(),
minfreespace(UINT32_MAX),
moveonenospc(false),
POLICYINIT(access),
POLICYINIT(chmod),
POLICYINIT(chown),

1
src/config.hpp

@ -54,6 +54,7 @@ namespace mergerfs
std::vector<std::string> srcmounts;
mutable pthread_rwlock_t srcmountslock;
size_t minfreespace;
bool moveonenospc;
public:
const Policy *policies[FuseFunc::Enum::END];

14
src/create.cpp

@ -33,9 +33,11 @@
#include <vector>
#include "config.hpp"
#include "ugid.hpp"
#include "fs.hpp"
#include "fileinfo.hpp"
#include "fs_path.hpp"
#include "fs_clonepath.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;
@ -80,7 +82,7 @@ _create(Policy::Func::Search searchFunc,
if(fd == -1)
return -errno;
fh = fd;
fh = reinterpret_cast<uint64_t>(new FileInfo(fd));
return 0;
}
@ -92,7 +94,7 @@ namespace mergerfs
int
create(const char *fusepath,
mode_t mode,
fuse_file_info *fileinfo)
fuse_file_info *ffi)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
@ -105,8 +107,8 @@ namespace mergerfs
config.minfreespace,
fusepath,
(mode & ~fc->umask),
fileinfo->flags,
fileinfo->fh);
ffi->flags,
ffi->fh);
}
}
}

2
src/create.hpp

@ -32,6 +32,6 @@ namespace mergerfs
int
create(const char *fusepath,
mode_t mode,
fuse_file_info *fileinfo);
fuse_file_info *ffi);
}
}

6
src/fallocate.cpp

@ -33,6 +33,8 @@
#include <errno.h>
#include <fcntl.h>
#include "fileinfo.hpp"
static
int
_fallocate(const int fd,
@ -73,7 +75,9 @@ namespace mergerfs
off_t len,
fuse_file_info *ffi)
{
return _fallocate(ffi->fh,
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _fallocate(fi->fd,
mode,
offset,
len);

6
src/fgetattr.cpp

@ -29,6 +29,8 @@
#include <unistd.h>
#include <errno.h>
#include "fileinfo.hpp"
static
int
_fgetattr(const int fd,
@ -50,7 +52,9 @@ namespace mergerfs
struct stat *st,
fuse_file_info *ffi)
{
return _fgetattr(ffi->fh,
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _fgetattr(fi->fd,
*st);
}
}

34
src/clonepath.cpp → src/fileinfo.hpp

@ -22,33 +22,19 @@
THE SOFTWARE.
*/
#include <unistd.h>
#ifndef __FILEINFO_HPP__
#define __FILEINFO_HPP__
#include <iostream>
#include "fs.hpp"
namespace clonepath
class FileInfo
{
static
void
print_usage_and__exit(void)
public:
FileInfo(int _fd) :
fd(_fd)
{
std::cerr << "usage: clonepath "
<< "<sourcedir> <destdir> <relativepath>"
<< std::endl;
_exit(1);
}
int
main(const int argc,
char * const argv[])
{
if(argc != 4)
print_usage_and__exit();
public:
int fd;
};
return fs::clonepath(argv[1],
argv[2],
argv[3]);
}
}
#endif

6
src/flush.cpp

@ -27,6 +27,8 @@
#include <unistd.h>
#include <errno.h>
#include "fileinfo.hpp"
static
int
_flush(const int fd)
@ -50,7 +52,9 @@ namespace mergerfs
flush(const char *fusepath,
fuse_file_info *ffi)
{
return _flush(ffi->fh);
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _flush(fi->fd);
}
}
}

486
src/fs.cpp

@ -24,128 +24,28 @@
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <cstdlib>
#include <iterator>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <glob.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <unistd.h>
#include <glob.h>
#include "fs.hpp"
#include "xattr.hpp"
#include "fs_attr.hpp"
#include "fs_path.hpp"
#include "fs_xattr.hpp"
#include "str.hpp"
using std::string;
using std::vector;
using std::map;
using std::istringstream;
template <typename Iter>
Iter
random_element(Iter begin,
Iter end)
{
const unsigned long n = std::distance(begin, end);
std::advance(begin, (std::rand() % n));
return begin;
}
namespace fs
{
namespace path
{
string
dirname(const string &path)
{
string parent = path;
string::reverse_iterator i;
string::reverse_iterator bi;
bi = parent.rend();
i = parent.rbegin();
while(*i == '/' && i != bi)
i++;
while(*i != '/' && i != bi)
i++;
while(*i == '/' && i != bi)
i++;
parent.erase(i.base(),parent.end());
return parent;
}
string
basename(const string &path)
{
return path.substr(path.find_last_of('/')+1);
}
bool
is_empty(const string &path)
{
DIR *dir;
struct dirent *de;
dir = ::opendir(path.c_str());
if(!dir)
return false;
while((de = ::readdir(dir)))
{
const char *d_name = de->d_name;
if(d_name[0] == '.' &&
((d_name[1] == '\0') ||
(d_name[1] == '.' && d_name[2] == '\0')))
continue;
::closedir(dir);
return false;
}
::closedir(dir);
return true;
}
bool
exists(const vector<string> &paths,
const string &fusepath)
{
for(size_t i = 0, ei = paths.size(); i != ei; i++)
{
int rv;
string path;
struct stat st;
fs::path::make(paths[i],fusepath,path);
rv = ::lstat(path.c_str(),&st);
if(rv == 0)
return true;
}
return false;
}
}
void
findallfiles(const vector<string> &srcmounts,
const string &fusepath,
@ -166,331 +66,37 @@ namespace fs
}
int
listxattr(const string &path,
vector<char> &attrs)
{
#ifndef WITHOUT_XATTR
int rv;
int size;
rv = -1;
errno = ERANGE;
while(rv == -1 && errno == ERANGE)
{
size = ::listxattr(path.c_str(),NULL,0);
attrs.resize(size);
rv = ::listxattr(path.c_str(),&attrs[0],size);
}
return rv;
#else
errno = ENOTSUP;
return -1;
#endif
}
int
listxattr(const string &path,
vector<string> &attrvector)
{
int rv;
vector<char> attrs;
rv = listxattr(path,attrs);
if(rv != -1)
{
string tmp(attrs.begin(),attrs.end());
str::split(attrvector,tmp,'\0');
}
return rv;
}
int
listxattr(const string &path,
string &attrstr)
{
int rv;
vector<char> attrs;
rv = listxattr(path,attrs);
if(rv != -1)
attrstr = string(attrs.begin(),attrs.end());
return rv;
}
int
getxattr(const string &path,
const string &attr,
vector<char> &value)
{
#ifndef WITHOUT_XATTR
int rv;
int size;
rv = -1;
errno = ERANGE;
while(rv == -1 && errno == ERANGE)
{
size = ::getxattr(path.c_str(),attr.c_str(),NULL,0);
value.resize(size);
rv = ::getxattr(path.c_str(),attr.c_str(),&value[0],size);
}
return rv;
#else
errno = ENOTSUP;
return -1;
#endif
}
int
getxattr(const string &path,
const string &attr,
string &value)
{
int rv;
vector<char> tmpvalue;
rv = getxattr(path,attr,tmpvalue);
if(rv != -1)
value = string(tmpvalue.begin(),tmpvalue.end());
return rv;
}
int
getxattrs(const string &path,
map<string,string> &attrs)
{
int rv;
string attrstr;
rv = listxattr(path,attrstr);
if(rv == -1)
return -1;
{
string key;
istringstream ss(attrstr);
while(getline(ss,key,'\0'))
{
string value;
rv = getxattr(path,key,value);
if(rv != -1)
attrs[key] = value;
}
}
return 0;
}
int
setxattr(const string &path,
const string &key,
const string &value,
const int flags)
{
#ifndef WITHOUT_XATTR
return ::setxattr(path.c_str(),
key.c_str(),
value.data(),
value.size(),
flags);
#else
errno = ENOTSUP;
return -1;
#endif
}
int
setxattr(const int fd,
const string &key,
const string &value,
const int flags)
{
#ifndef WITHOUT_XATTR
return ::fsetxattr(fd,
key.c_str(),
value.data(),
value.size(),
flags);
#else
errno = ENOTSUP;
return -1;
#endif
}
int
setxattrs(const string &path,
const map<string,string> &attrs)
{
int fd;
fd = ::open(path.c_str(),O_RDONLY|O_NONBLOCK);
if(fd == -1)
return -1;
for(map<string,string>::const_iterator
i = attrs.begin(), ei = attrs.end(); i != ei; ++i)
{
setxattr(fd,i->first,i->second,0);
}
return ::close(fd);
}
int
copyxattrs(const string &from,
const string &to)
{
int rv;
map<string,string> attrs;
rv = getxattrs(from,attrs);
if(rv == -1)
return -1;
return setxattrs(to,attrs);
}
static
int
get_fs_ioc_flags(const string &file,
int &flags)
findonfs(const vector<string> &srcmounts,
const string &fusepath,
const int fd,
string &basepath)
{
int fd;
int rv;
const int openflags = O_RDONLY|O_NONBLOCK;
fd = ::open(file.c_str(),openflags);
if(fd == -1)
return -1;
string tmppath;
unsigned long fsid;
struct statvfs buf;
rv = ::ioctl(fd,FS_IOC_GETFLAGS,&flags);
rv = ::fstatvfs(fd,&buf);
if(rv == -1)
{
int error = errno;
::close(fd);
errno = error;
return -1;
}
return ::close(fd);
}
static
int
set_fs_ioc_flags(const string &file,
const int flags)
fsid = buf.f_fsid;
for(int i = 0, ei = srcmounts.size(); i != ei; i++)
{
int fd;
int rv;
const int openflags = O_RDONLY|O_NONBLOCK;
fd = ::open(file.c_str(),openflags);
if(fd == -1)
return -1;
fs::path::make(srcmounts[i],fusepath,tmppath);
rv = ::ioctl(fd,FS_IOC_SETFLAGS,&flags);
rv = ::statvfs(tmppath.c_str(),&buf);
if(rv == -1)
{
int error = errno;
::close(fd);
errno = error;
return -1;
}
return ::close(fd);
}
int
copyattr(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);
}
static
bool
ignorable_error(const int err)
{
switch(err)
{
case ENOTTY:
case ENOTSUP:
#if ENOTSUP != EOPNOTSUPP
case EOPNOTSUPP:
#endif
return true;
}
return false;
}
int
clonepath(const string &fromsrc,
const string &tosrc,
const string &relative)
{
int rv;
struct stat st;
string topath;
string frompath;
string dirname;
continue;
dirname = fs::path::dirname(relative);
if(!dirname.empty())
if(buf.f_fsid == fsid)
{
rv = clonepath(fromsrc,tosrc,dirname);
if(rv == -1)
return -1;
basepath = srcmounts[i];
return 0;
}
fs::path::make(fromsrc,relative,frompath);
rv = ::stat(frompath.c_str(),&st);
if(rv == -1)
return -1;
else if(!S_ISDIR(st.st_mode))
return (errno = ENOTDIR,-1);
fs::path::make(tosrc,relative,topath);
rv = ::mkdir(topath.c_str(),st.st_mode);
if(rv == -1)
{
if(errno != EEXIST)
return -1;
rv = ::chmod(topath.c_str(),st.st_mode);
if(rv == -1)
return -1;
}
rv = ::chown(topath.c_str(),st.st_uid,st.st_gid);
if(rv == -1)
return -1;
// It may not support it... it's fine...
rv = copyattr(frompath,topath);
if(rv == -1 && !ignorable_error(errno))
return -1;
rv = copyxattrs(frompath,topath);
if(rv == -1 && !ignorable_error(errno))
return -1;
return 0;
return (errno=ENOENT,-1);
}
void
@ -528,4 +134,48 @@ namespace fs
strs[i] = buf;
}
}
int
getfl(const int fd)
{
return ::fcntl(fd,F_GETFL,0);
}
int
mfs(const vector<string> &basepaths,
const size_t minfreespace,
string &path)
{
fsblkcnt_t mfs;
ssize_t mfsidx;
mfs = 0;
mfsidx = -1;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
int rv;
struct statvfs fsstats;
const string &basepath = basepaths[i];
rv = ::statvfs(basepath.c_str(),&fsstats);
if(rv == 0)
{
fsblkcnt_t spaceavail;
spaceavail = (fsstats.f_frsize * fsstats.f_bavail);
if((spaceavail > mfs) && (spaceavail >= minfreespace))
{
mfs = spaceavail;
mfsidx = i;
}
}
}
if(mfsidx == -1)
return (errno=ENOENT,-1);
path = basepaths[mfsidx];
return 0;
}
};

91
src/fs.hpp

@ -27,103 +27,32 @@
#include <string>
#include <vector>
#include <map>
#include "path.hpp"
namespace fs
{
using std::size_t;
using std::string;
using std::vector;
using std::map;
namespace path
{
string dirname(const string &path);
string basename(const string &path);
bool is_empty(const string &path);
bool exists(vector<string>::const_iterator begin,
vector<string>::const_iterator end,
const string &fusepath);
bool exists(const vector<string> &srcmounts,
const string &fusepath);
inline
string
make(const string &base,
const string &suffix)
{
return base + suffix;
}
inline
void
make(const string &base,
const string &suffix,
string &output)
{
output = base + suffix;
}
inline
void
append(string &base,
const string &suffix)
{
base += suffix;
}
}
void findallfiles(const vector<string> &srcmounts,
const string &fusepath,
vector<string> &paths);
int clonepath(const string &srcfrom,
const string &srcto,
const string &relative);
int listxattr(const string &path,
vector<char> &attrs);
int listxattr(const string &path,
string &attrs);
int listxattr(const string &path,
vector<string> &attrs);
int getxattr(const string &path,
const string &attr,
vector<char> &value);
int getxattr(const string &path,
const string &attr,
string &value);
int getxattrs(const string &path,
map<string,string> &attrs);
int setxattr(const string &path,
const string &key,
const string &value,
const int flags);
int setxattr(const int fd,
const string &key,
const string &value,
const int flags);
int setxattrs(const string &path,
const map<string,string> &attrs);
int copyxattrs(const string &from,
const string &to);
int copyattr(const string &from,
const string &to);
int findonfs(const vector<string> &srcmounts,
const string &fusepath,
const int fd,
string &basepath);
void glob(const vector<string> &patterns,
vector<string> &strs);
void realpathize(vector<string> &strs);
int getfl(const int fd);
int mfs(const vector<string> &srcs,
const size_t minfreespace,
string &path);
};
#endif // __FS_HPP__

135
src/fs_attr.cpp

@ -0,0 +1,135 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
using std::string;
namespace fs
{
namespace attr
{
static
int
get_fs_ioc_flags(const int fd,
int &flags)
{
return ::ioctl(fd,FS_IOC_GETFLAGS,&flags);
}
static
int
get_fs_ioc_flags(const string &file,
int &flags)
{
int fd;
int rv;
const int openflags = O_RDONLY|O_NONBLOCK;
fd = ::open(file.c_str(),openflags);
if(fd == -1)
return -1;
rv = get_fs_ioc_flags(fd,flags);
if(rv == -1)
{
int error = errno;
::close(fd);
errno = error;
return -1;
}
return ::close(fd);
}
static
int
set_fs_ioc_flags(const int fd,
const int flags)
{
return ::ioctl(fd,FS_IOC_SETFLAGS,&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 = ::open(file.c_str(),openflags);
if(fd == -1)
return -1;
rv = set_fs_ioc_flags(fd,flags);
if(rv == -1)
{
int error = errno;
::close(fd);
errno = error;
return -1;
}
return ::close(fd);
}
int
copy(const int fdin,
const int fdout)
{
int rv;
int flags;
rv = get_fs_ioc_flags(fdin,flags);
if(rv == -1)
return -1;
return set_fs_ioc_flags(fdout,flags);
}
int
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);
}
}
}

43
src/fs_attr.hpp

@ -0,0 +1,43 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __FS_ATTR_HPP__
#define __FS_ATTR_HPP__
#include <string>
namespace fs
{
namespace attr
{
using std::string;
int copy(const int fdin,
const int fdout);
int copy(const string &from,
const string &to);
}
}
#endif // __FS_ATTR_HPP__

231
src/fs_clonefile.cpp

@ -0,0 +1,231 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#ifdef __linux__
#include <sys/sendfile.h>
#endif
#include <string>
#include <vector>
#include "fs_attr.hpp"
#include "fs_xattr.hpp"
using std::string;
using std::vector;
namespace fs
{
static
ssize_t
sendfile(const int fdin,
const int fdout,
const size_t count)
{
#if defined __linux__
off_t offset = 0;
return ::sendfile(fdout,fdin,&offset,count);
#else
return (errno=EINVAL,-1);
#endif
}
int
writen(const int fd,
const char *buf,
const size_t count)
{
size_t nleft;
ssize_t nwritten;
nleft = count;
while(nleft > 0)
{
nwritten = ::write(fd,buf,nleft);
if(nwritten == -1)
{
if(errno == EINTR)
continue;
return -1;
}
nleft -= nwritten;
buf += nwritten;
}
return count;
}
static
int
copyfile_rw(const int fdin,
const int fdout,
const size_t count,
const size_t blocksize)
{
ssize_t nr;
ssize_t nw;
ssize_t bufsize;
size_t totalwritten;
vector<char> buf;
bufsize = (blocksize * 16);
buf.resize(bufsize);
totalwritten = 0;
while(totalwritten < count)
{
nr = ::read(fdin,&buf[0],bufsize);
if(nr == -1)
{
if(errno == EINTR)
continue;
else
return -1;
}
nw = writen(fdout,&buf[0],nr);
if(nw == -1)
return -1;
totalwritten += nw;
}
return count;
}
static
int
copydata(const int fdin,
const int fdout,
const size_t count,
const size_t blocksize)
{
int rv;
::posix_fadvise(fdin,0,count,POSIX_FADV_WILLNEED);
::posix_fadvise(fdin,0,count,POSIX_FADV_SEQUENTIAL);
::posix_fallocate(fdout,0,count);
rv = fs::sendfile(fdin,fdout,count);
if((rv == -1) && ((errno == EINVAL) || (errno == ENOSYS)))
return fs::copyfile_rw(fdin,fdout,count,blocksize);
return rv;
}
static
bool
ignorable_error(const int err)
{
switch(err)
{
case ENOTTY:
case ENOTSUP:
#if ENOTSUP != EOPNOTSUPP
case EOPNOTSUPP:
#endif
return true;
}
return false;
}
int
clonefile(const int fdin,
const int fdout)
{
int rv;
struct stat stin;
rv = ::fstat(fdin,&stin);
if(rv == -1)
return -1;
rv = copydata(fdin,fdout,stin.st_size,stin.st_blksize);
if(rv == -1)
return -1;
rv = fs::attr::copy(fdin,fdout);
if(rv == -1 && !ignorable_error(errno))
return -1;
rv = fs::xattr::copy(fdin,fdout);
if(rv == -1 && !ignorable_error(errno))
return -1;
rv = ::fchown(fdout,stin.st_uid,stin.st_gid);
if(rv == -1)
return -1;
rv = ::fchmod(fdout,stin.st_mode);
if(rv == -1)
return -1;
struct timespec times[2];
times[0] = stin.st_atim;
times[1] = stin.st_mtim;
rv = ::futimens(fdout,times);
if(rv == -1)
return -1;
return 0;
}
int
clonefile(const string &in,
const string &out)
{
int rv;
int fdin;
int fdout;
int error;
fdin = ::open(in.c_str(),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 int mode = S_IWUSR;
fdout = ::open(out.c_str(),flags,mode);
if(fdout == -1)
return -1;
rv = fs::clonefile(fdin,fdout);
error = errno;
::close(fdin);
::close(fdout);
errno = error;
return rv;
}
}

33
src/fs_clonefile.hpp

@ -0,0 +1,33 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <string>
namespace fs
{
int clonefile(const int fdin,
const int fdout);
int clonefile(const std::string &from,
const std::string &to);
}

117
src/fs_clonepath.cpp

@ -0,0 +1,117 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string>
#include "fs_path.hpp"
#include "fs_attr.hpp"
#include "fs_xattr.hpp"
using std::string;
namespace fs
{
static
bool
ignorable_error(const int err)
{
switch(err)
{
case ENOTTY:
case ENOTSUP:
#if ENOTSUP != EOPNOTSUPP
case EOPNOTSUPP:
#endif
return true;
}
return false;
}
int
clonepath(const string &fromsrc,
const string &tosrc,
const string &relative)
{
int rv;
struct stat st;
string topath;
string frompath;
string dirname;
dirname = fs::path::dirname(relative);
if(!dirname.empty())
{
rv = clonepath(fromsrc,tosrc,dirname);
if(rv == -1)
return -1;
}
fs::path::make(fromsrc,relative,frompath);
rv = ::stat(frompath.c_str(),&st);
if(rv == -1)
return -1;
else if(!S_ISDIR(st.st_mode))
return (errno = ENOTDIR,-1);
fs::path::make(tosrc,relative,topath);
rv = ::mkdir(topath.c_str(),st.st_mode);
if(rv == -1)
{
if(errno != EEXIST)
return -1;
rv = ::chmod(topath.c_str(),st.st_mode);
if(rv == -1)
return -1;
}
// It may not support it... it's fine...
rv = fs::attr::copy(frompath,topath);
if(rv == -1 && !ignorable_error(errno))
return -1;
rv = fs::xattr::copy(frompath,topath);
if(rv == -1 && !ignorable_error(errno))
return -1;
rv = ::chown(topath.c_str(),st.st_uid,st.st_gid);
if(rv == -1)
return -1;
struct timespec times[2];
times[0] = st.st_atim;
times[1] = st.st_mtim;
rv = ::utimensat(-1,topath.c_str(),times,0);
if(rv == -1)
return -1;
return 0;
}
}

32
src/fs_clonepath.hpp

@ -0,0 +1,32 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <string>
namespace fs
{
int clonepath(const std::string &from,
const std::string &to,
const std::string &relative);
}

117
src/fs_movefile.cpp

@ -0,0 +1,117 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <vector>
#include "fs.hpp"
#include "fs_path.hpp"
#include "fs_clonepath.hpp"
#include "fs_clonefile.hpp"
using std::string;
using std::vector;
namespace fs
{
int
movefile(const vector<string> &basepaths,
const char *fusepath,
const size_t additional_size,
int &origfd)
{
int rv;
int fdin;
int fdout;
int fdin_flags;
string fusedir;
string fdin_path;
string fdout_path;
struct stat fdin_st;
fdin = origfd;
rv = fstat(fdin,&fdin_st);
if(rv == -1)
return -1;
fdin_flags = fs::getfl(fdin);
if(rv == -1)
return -1;
rv = fs::findonfs(basepaths,fusepath,fdin,fdin_path);
if(rv == -1)
return -1;
fdin_st.st_size += additional_size;
rv = fs::mfs(basepaths,fdin_st.st_size,fdout_path);
if(rv == -1)
return -1;
fusedir = fs::path::dirname(fusepath);
rv = fs::clonepath(fdin_path,fdout_path,fusedir);
if(rv == -1)
return -1;
fs::path::append(fdin_path,fusepath);
fdin = ::open(fdin_path.c_str(),O_RDONLY);
if(fdin == -1)
return -1;
fs::path::append(fdout_path,fusepath);
fdout = ::open(fdout_path.c_str(),fdin_flags|O_CREAT,fdin_st.st_mode);
if(fdout == -1)
return -1;
rv = fs::clonefile(fdin,fdout);
if(rv == -1)
{
::close(fdin);
::close(fdout);
::unlink(fdout_path.c_str());
return -1;
}
rv = ::unlink(fdin_path.c_str());
if(rv == -1)
{
::close(fdin);
::close(fdout);
::unlink(fdout_path.c_str());
return -1;
}
::close(fdin);
::close(origfd);
origfd = fdout;
return 0;
}
}

37
src/path.hpp → src/fs_movefile.hpp

@ -22,34 +22,19 @@
THE SOFTWARE.
*/
#ifndef __PATH_HPP__
#define __PATH_HPP__
#ifndef __FS_MOVEFILE_HPP__
#define __FS_MOVEFILE_HPP__
#include <string>
#include <vector>
struct Path
namespace fs
{
Path() {}
explicit
Path(const std::string &b,
const std::string &f)
: base(b),
full(f)
{}
explicit
Path(const char *b,
const std::string &f)
: base(b),
full(f)
{}
std::string base;
std::string full;
};
typedef std::vector<Path> Paths;
#endif /* __PATH_HPP__ */
int
movefile(const vector<string> &basepaths,
const char *fusepath,
const size_t additional_size,
int &origfd);
}
#endif

118
src/fs_path.cpp

@ -0,0 +1,118 @@
/*
The MIT License (MIT)
Copyright (c) 2015 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include "fs_path.hpp"
using std::string;
namespace fs
{
namespace path
{
string
dirname(const string &path)
{
string parent = path;
string::reverse_iterator i;
string::reverse_iterator bi;
bi = parent.rend();
i = parent.rbegin();
while(*i == '/' && i != bi)
i++;
while(*i != '/' && i != bi)
i++;
while(*i == '/' && i != bi)
i++;
parent.erase(i.base(),parent.end());
return parent;
}
string
basename(const string &path)
{
return path.substr(path.find_last_of('/')+1);
}
bool
is_empty(const string &path)
{
DIR *dir;
struct dirent *de;
dir = ::opendir(path.c_str());
if(!dir)
return false;
while((de = ::readdir(dir)))
{
const char *d_name = de->d_name;
if(d_name[0] == '.' &&
((d_name[1] == '\0') ||
(d_name[1] == '.' && d_name[2] == '\0')))
continue;
::closedir(dir);
return false;
}
::closedir(dir);
return true;
}
bool
exists(const vector<string> &paths,
const string &fusepath)
{
for(size_t i = 0, ei = paths.size(); i != ei; i++)
{
int rv;
string path;
struct stat st;
fs::path::make(paths[i],fusepath,path);
rv = ::lstat(path.c_str(),&st);
if(rv == 0)
return true;
}
return false;
}
}
}

76
src/fs_path.hpp

@ -0,0 +1,76 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __FS_PATH_HPP__
#define __FS_PATH_HPP__
#include <string>
#include <vector>
namespace fs
{
namespace path
{
using std::string;
using std::vector;
string dirname(const string &path);
string basename(const string &path);
bool is_empty(const string &path);
bool exists(vector<string>::const_iterator begin,
vector<string>::const_iterator end,
const string &fusepath);
bool exists(const vector<string> &srcmounts,
const string &fusepath);
inline
string
make(const string &base,
const string &suffix)
{
return base + suffix;
}
inline
void
make(const string &base,
const string &suffix,
string &output)
{
output = base + suffix;
}
inline
void
append(string &base,
const string &suffix)
{
base += suffix;
}
}
};
#endif // __FS_PATH_HPP__

399
src/fs_xattr.cpp

@ -0,0 +1,399 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <sys/types.h>
#include <attr/xattr.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include "str.hpp"
#include "xattr.hpp"
using std::string;
using std::vector;
using std::map;
using std::istringstream;
namespace fs
{
namespace xattr
{
int
list(const int fd,
vector<char> &attrs)
{
#ifndef WITHOUT_XATTR
ssize_t rv;
ssize_t size;
rv = -1;
errno = ERANGE;
while(rv == -1 && errno == ERANGE)
{
size = ::flistxattr(fd,NULL,0);
attrs.resize(size);
if(size == 0)
return 0;
rv = ::flistxattr(fd,&attrs[0],size);
}
return rv;
#else
errno = ENOTSUP;
return -1;
#endif
}
int
list(const string &path,
vector<char> &attrs)
{
#ifndef WITHOUT_XATTR
int rv;
int size;
rv = -1;
errno = ERANGE;
while(rv == -1 && errno == ERANGE)
{
size = ::listxattr(path.c_str(),NULL,0);
attrs.resize(size);
if(size == 0)
return 0;
rv = ::listxattr(path.c_str(),&attrs[0],size);
}
return rv;
#else
errno = ENOTSUP;
return -1;
#endif
}
int
list(const int fd,
vector<string> &attrvector)
{
int rv;
vector<char> attrs;
rv = list(fd,attrs);
if(rv != -1)
{
string tmp(attrs.begin(),attrs.end());
str::split(attrvector,tmp,'\0');
}
return rv;
}
int
list(const string &path,
vector<string> &attrvector)
{
int rv;
vector<char> attrs;
rv = list(path,attrs);
if(rv != -1)
{
string tmp(attrs.begin(),attrs.end());
str::split(attrvector,tmp,'\0');
}
return rv;
}
int
list(const int fd,
string &attrstr)
{
int rv;
vector<char> attrs;
rv = list(fd,attrs);
if(rv != -1)
attrstr = string(attrs.begin(),attrs.end());
return rv;
}
int
list(const string &path,
string &attrstr)
{
int rv;
vector<char> attrs;
rv = list(path,attrs);
if(rv != -1)
attrstr = string(attrs.begin(),attrs.end());
return rv;
}
int
get(const int fd,
const string &attr,
vector<char> &value)
{
#ifndef WITHOUT_XATTR
int rv;
int size;
rv = -1;
errno = ERANGE;
while(rv == -1 && errno == ERANGE)
{
size = ::fgetxattr(fd,attr.c_str(),NULL,0);
value.resize(size);
if(size == 0)
return 0;
rv = ::fgetxattr(fd,attr.c_str(),&value[0],size);
}
return rv;
#else
errno = ENOTSUP;
return -1;
#endif
}
int
get(const string &path,
const string &attr,
vector<char> &value)
{
#ifndef WITHOUT_XATTR
int rv;
int size;
rv = -1;
errno = ERANGE;
while(rv == -1 && errno == ERANGE)
{
size = ::getxattr(path.c_str(),attr.c_str(),NULL,0);
value.resize(size);
if(size == 0)
return 0;
rv = ::getxattr(path.c_str(),attr.c_str(),&value[0],size);
}
return rv;
#else
errno = ENOTSUP;
return -1;
#endif
}
int
get(const int fd,
const string &attr,
string &value)
{
int rv;
vector<char> tmpvalue;
rv = get(fd,attr,tmpvalue);
if(rv != -1)
value = string(tmpvalue.begin(),tmpvalue.end());
return rv;
}
int
get(const string &path,
const string &attr,
string &value)
{
int rv;
vector<char> tmpvalue;
rv = get(path,attr,tmpvalue);
if(rv != -1)
value = string(tmpvalue.begin(),tmpvalue.end());
return rv;
}
int
get(const int fd,
map<string,string> &attrs)
{
int rv;
string attrstr;
rv = list(fd,attrstr);
if(rv == -1)
return -1;
{
string key;
istringstream ss(attrstr);
while(getline(ss,key,'\0'))
{
string value;
rv = get(fd,key,value);
if(rv != -1)
attrs[key] = value;
}
}
return 0;
}
int
get(const string &path,
map<string,string> &attrs)
{
int rv;
string attrstr;
rv = list(path,attrstr);
if(rv == -1)
return -1;
{
string key;
istringstream ss(attrstr);
while(getline(ss,key,'\0'))
{
string value;
rv = get(path,key,value);
if(rv != -1)
attrs[key] = value;
}
}
return 0;
}
int
set(const int fd,
const string &key,
const string &value,
const int flags)
{
#ifndef WITHOUT_XATTR
return ::fsetxattr(fd,
key.c_str(),
value.data(),
value.size(),
flags);
#else
errno = ENOTSUP;
return -1;
#endif
}
int
set(const string &path,
const string &key,
const string &value,
const int flags)
{
#ifndef WITHOUT_XATTR
return ::setxattr(path.c_str(),
key.c_str(),
value.data(),
value.size(),
flags);
#else
errno = ENOTSUP;
return -1;
#endif
}
int
set(const int fd,
const map<string,string> &attrs)
{
int rv;
for(map<string,string>::const_iterator
i = attrs.begin(), ei = attrs.end(); i != ei; ++i)
{
rv = set(fd,i->first,i->second,0);
if(rv == -1)
return -1;
}
return 0;
}
int
set(const string &path,
const map<string,string> &attrs)
{
int fd;
fd = ::open(path.c_str(),O_RDONLY|O_NONBLOCK);
if(fd == -1)
return -1;
set(fd,attrs);
return ::close(fd);
}
int
copy(const int fdin,
const int fdout)
{
int rv;
map<string,string> attrs;
rv = get(fdin,attrs);
if(rv == -1)
return -1;
return set(fdout,attrs);
}
int
copy(const string &from,
const string &to)
{
int rv;
map<string,string> attrs;
rv = get(from,attrs);
if(rv == -1)
return -1;
return set(to,attrs);
}
}
}

78
src/fs_xattr.hpp

@ -0,0 +1,78 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __FS_XATTR_HPP__
#define __FS_XATTR_HPP__
#include <string>
#include <vector>
#include <map>
namespace fs
{
namespace xattr
{
using std::size_t;
using std::string;
using std::vector;
using std::map;
int list(const string &path,
vector<char> &attrs);
int list(const string &path,
string &attrs);
int list(const string &path,
vector<string> &attrs);
int get(const string &path,
const string &attr,
vector<char> &value);
int get(const string &path,
const string &attr,
string &value);
int get(const string &path,
map<string,string> &attrs);
int set(const string &path,
const string &key,
const string &value,
const int flags);
int set(const int fd,
const string &key,
const string &value,
const int flags);
int set(const string &path,
const map<string,string> &attrs);
int copy(const int fdin,
const int fdout);
int copy(const string &from,
const string &to);
}
}
#endif // __FS_HPP__

6
src/fsync.cpp

@ -34,6 +34,8 @@
#include <unistd.h>
#include <errno.h>
#include "fileinfo.hpp"
static
int
_fsync(const int fd,
@ -57,7 +59,9 @@ namespace mergerfs
int isdatasync,
fuse_file_info *ffi)
{
return _fsync(ffi->fh,
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _fsync(fi->fd,
isdatasync);
}
}

6
src/ftruncate.cpp

@ -28,6 +28,8 @@
#include <sys/types.h>
#include <errno.h>
#include "fileinfo.hpp"
static
int
_ftruncate(const int fd,
@ -49,7 +51,9 @@ namespace mergerfs
off_t size,
fuse_file_info *ffi)
{
return _ftruncate(ffi->fh,
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _ftruncate(fi->fd,
size);
}
}

4
src/getattr.cpp

@ -33,9 +33,9 @@
#include <errno.h>
#include "config.hpp"
#include "fs.hpp"
#include "ugid.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

16
src/getxattr.cpp

@ -35,12 +35,12 @@
#include <string.h>
#include "config.hpp"
#include "fs.hpp"
#include "ugid.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "xattr.hpp"
#include "str.hpp"
#include "ugid.hpp"
#include "version.hpp"
#include "xattr.hpp"
using std::string;
using std::vector;
@ -123,6 +123,14 @@ _getxattr_controlfile_minfreespace(const Config &config,
attrvalue = buf;
}
static
void
_getxattr_controlfile_moveonenospc(const Config &config,
string &attrvalue)
{
attrvalue = (config.moveonenospc ? "true" : "false");
}
static
void
_getxattr_controlfile_policies(const Config &config,
@ -164,6 +172,8 @@ _getxattr_controlfile(const Config &config,
_getxattr_controlfile_srcmounts(config,attrvalue);
else if(attr[2] == "minfreespace")
_getxattr_controlfile_minfreespace(config,attrvalue);
else if(attr[2] == "moveonenospc")
_getxattr_controlfile_moveonenospc(config,attrvalue);
else if(attr[2] == "policies")
_getxattr_controlfile_policies(config,attrvalue);
else if(attr[2] == "version")

7
src/ioctl.cpp

@ -32,8 +32,10 @@
#include <linux/fs.h>
#include "config.hpp"
#include "ugid.hpp"
#include "fileinfo.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;
@ -130,8 +132,9 @@ namespace mergerfs
cmd,
data);
#endif
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _ioctl(ffi->fh,
return _ioctl(fi->fd,
cmd,
data);
}

5
src/link.cpp

@ -31,9 +31,10 @@
#include <unistd.h>
#include "config.hpp"
#include "fs.hpp"
#include "ugid.hpp"
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

9
src/listxattr.cpp

@ -31,13 +31,13 @@
#include <errno.h>
#include <string.h>
#include "config.hpp"
#include "buildvector.hpp"
#include "category.hpp"
#include "fs.hpp"
#include "ugid.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include "xattr.hpp"
#include "buildvector.hpp"
using std::string;
using std::vector;
@ -53,6 +53,7 @@ _listxattr_controlfile(char *list,
buildvector<string>
("user.mergerfs.srcmounts")
("user.mergerfs.minfreespace")
("user.mergerfs.moveonenospc")
("user.mergerfs.policies")
("user.mergerfs.version");

8
src/mergerfs.cpp

@ -31,9 +31,9 @@
#include "mergerfs.hpp"
#include "option_parser.hpp"
#include "resources.hpp"
#include "fs.hpp"
#include "fs_path.hpp"
#include "clonepath.hpp"
#include "clone.hpp"
#include "access.hpp"
#include "chmod.hpp"
@ -195,8 +195,8 @@ main(int argc,
std::string appname;
appname = local::getappname(argc,argv);
if(appname == "clonepath")
return clonepath::main(argc,argv);
if(appname == "clone")
return clonetool::main(argc,argv);
return mergerfs::main(argc,argv);
}

5
src/mkdir.cpp

@ -31,10 +31,11 @@
#include <string>
#include <vector>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

5
src/mknod.cpp

@ -33,10 +33,11 @@
#include <fcntl.h>
#include <unistd.h>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

13
src/open.cpp

@ -31,10 +31,11 @@
#include <string>
#include <vector>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fileinfo.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;
@ -63,7 +64,7 @@ _open(Policy::Func::Search searchFunc,
if(fd == -1)
return -errno;
fh = fd;
fh = reinterpret_cast<uint64_t>(new FileInfo(fd));
return 0;
}
@ -74,7 +75,7 @@ namespace mergerfs
{
int
open(const char *fusepath,
fuse_file_info *fileinfo)
fuse_file_info *ffi)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
@ -85,8 +86,8 @@ namespace mergerfs
config.srcmounts,
config.minfreespace,
fusepath,
fileinfo->flags,
fileinfo->fh);
ffi->flags,
ffi->fh);
}
}
}

2
src/open.hpp

@ -30,6 +30,6 @@ namespace mergerfs
{
int
open(const char *fusepath,
fuse_file_info *fileinfo);
fuse_file_info *ffi);
}
}

17
src/option_parser.cpp

@ -117,6 +117,21 @@ parse_and_process_minfreespace(const std::string &value,
return 0;
}
static
int
parse_and_process_moveonenospc(const std::string &value,
bool &moveonenospc)
{
if(value == "false")
moveonenospc = false;
else if(value == "true")
moveonenospc = true;
else
return 1;
return 0;
}
static
int
parse_and_process_arg(Config &config,
@ -153,6 +168,8 @@ parse_and_process_kv_arg(Config &config,
{
if(key == "minfreespace")
rv = parse_and_process_minfreespace(value,config.minfreespace);
else if(key == "moveonenospc")
rv = parse_and_process_moveonenospc(value,config.moveonenospc);
}
if(rv == -1)

1
src/policy.hpp

@ -29,7 +29,6 @@
#include <vector>
#include <map>
#include "path.hpp"
#include "fs.hpp"
#include "category.hpp"

3
src/policy_all.cpp

@ -30,6 +30,7 @@
#include <string>
#include <vector>
#include "fs_path.hpp"
#include "policy.hpp"
using std::string;
@ -50,7 +51,7 @@ _all(const vector<string> &basepaths,
{
const string &basepath = basepaths[i];
fullpath = fs::path::make(basepath,fusepath);
fs::path::make(basepath,fusepath,fullpath);
rv = ::lstat(fullpath.c_str(),&st);
if(rv == 0)

1
src/policy_epmfs.cpp

@ -31,6 +31,7 @@
#include <string>
#include <vector>
#include "fs_path.hpp"
#include "policy.hpp"
using std::string;

2
src/policy_ff.cpp

@ -30,7 +30,7 @@
#include <string>
#include <vector>
#include "path.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
using std::string;

3
src/policy_ffwp.cpp

@ -30,6 +30,7 @@
#include <string>
#include <vector>
#include "fs_path.hpp"
#include "policy.hpp"
using std::string;
@ -51,7 +52,7 @@ _ffwp(const vector<string> &basepaths,
string fullpath;
const string &basepath = basepaths[i];
fullpath = fs::path::make(basepath,fusepath);
fs::path::make(basepath,fusepath,fullpath);
rv = ::lstat(fullpath.c_str(),&st);
if(rv == 0)

3
src/policy_fwfs.cpp

@ -28,6 +28,7 @@
#include <string>
#include <vector>
#include "fs_path.hpp"
#include "policy.hpp"
using std::string;
@ -83,7 +84,7 @@ _fwfs(const Category::Enum::Type type,
struct statvfs fsstats;
const string &basepath = basepaths[i];
fullpath = fs::path::make(basepath,fusepath);
fs::path::make(basepath,fusepath,fullpath);
rv = ::statvfs(fullpath.c_str(),&fsstats);
if(rv == 0)

3
src/policy_lfs.cpp

@ -31,6 +31,7 @@
#include <string>
#include <vector>
#include "fs_path.hpp"
#include "policy.hpp"
using std::string;
@ -99,7 +100,7 @@ _lfs(const Category::Enum::Type type,
struct statvfs fsstats;
const string &basepath = basepaths[i];
fullpath = fs::path::make(basepath,fusepath);
fs::path::make(basepath,fusepath,fullpath);
rv = ::statvfs(fullpath.c_str(),&fsstats);
if(rv == 0)

3
src/policy_mfs.cpp

@ -28,6 +28,7 @@
#include <string>
#include <vector>
#include "fs_path.hpp"
#include "policy.hpp"
using std::string;
@ -89,7 +90,7 @@ _mfs(const vector<string> &basepaths,
struct statvfs fsstats;
const string &basepath = basepaths[i];
fullpath = fs::path::make(basepath,fusepath);
fs::path::make(basepath,fusepath,fullpath);
rv = ::statvfs(fullpath.c_str(),&fsstats);
if(rv == 0)

3
src/policy_newest.cpp

@ -31,6 +31,7 @@
#include <vector>
#include <limits>
#include "fs_path.hpp"
#include "policy.hpp"
using std::string;
@ -53,7 +54,7 @@ _newest(const vector<string> &basepaths,
string fullpath;
const string &basepath = basepaths[i];
fullpath = fs::path::make(basepath,fusepath);
fs::path::make(basepath,fusepath,fullpath);
rv = ::lstat(fullpath.c_str(),&st);
if(rv == 0 && st.st_mtime >= newest)

6
src/read.cpp

@ -30,6 +30,8 @@
#include <string>
#include "fileinfo.hpp"
static
int
_read(const int fd,
@ -55,7 +57,9 @@ namespace mergerfs
off_t offset,
fuse_file_info *ffi)
{
return _read(ffi->fh,
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _read(fi->fd,
buf,
count,
offset);

6
src/read_buf.cpp

@ -30,6 +30,8 @@
#include <errno.h>
#include <string.h>
#include "fileinfo.hpp"
static
int
_read_buf(const int fd,
@ -65,7 +67,9 @@ namespace mergerfs
off_t offset,
fuse_file_info *ffi)
{
return _read_buf(ffi->fh,
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _read_buf(fi->fd,
bufp,
size,
offset);

8
src/readdir.cpp

@ -33,11 +33,11 @@
#include <errno.h>
#include <dirent.h>
#include "readdir.hpp"
#include "config.hpp"
#include "ugid.hpp"
#include "fs.hpp"
#include "fs_path.hpp"
#include "readdir.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;
@ -61,7 +61,7 @@ _readdir(const vector<string> &srcmounts,
DIR *dh;
string basepath;
basepath = fs::path::make(srcmounts[i],dirname);
fs::path::make(srcmounts[i],dirname,basepath);
dh = ::opendir(basepath.c_str());
if(!dh)
continue;

4
src/readlink.cpp

@ -31,9 +31,9 @@
#include <string>
#include "config.hpp"
#include "ugid.hpp"
#include "fs.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

12
src/release.cpp

@ -29,13 +29,15 @@
#include <string>
#include "fileinfo.hpp"
static
int
_release(uint64_t &fh)
_release(FileInfo *fi)
{
::close(fh);
::close(fi->fd);
fh = 0;
delete fi;
return 0;
}
@ -48,7 +50,9 @@ namespace mergerfs
release(const char *fusepath,
fuse_file_info *ffi)
{
return _release(ffi->fh);
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
return _release(fi);
}
}
}

4
src/removexattr.cpp

@ -31,9 +31,9 @@
#include <sys/types.h>
#include "config.hpp"
#include "ugid.hpp"
#include "fs.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include "xattr.hpp"
using std::string;

4
src/rename.cpp

@ -32,10 +32,10 @@
#include <vector>
#include <set>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

4
src/rmdir.cpp

@ -29,10 +29,10 @@
#include <string>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

32
src/setxattr.cpp

@ -33,12 +33,12 @@
#include <string.h>
#include "config.hpp"
#include "fs.hpp"
#include "ugid.hpp"
#include "fs_path.hpp"
#include "num.hpp"
#include "rwlock.hpp"
#include "xattr.hpp"
#include "str.hpp"
#include "num.hpp"
#include "ugid.hpp"
#include "xattr.hpp"
using std::string;
using std::vector;
@ -186,6 +186,26 @@ _setxattr_minfreespace(Config &config,
return 0;
}
static
int
_setxattr_moveonenospc(Config &config,
const string &attrval,
const int flags)
{
if((flags & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
if(attrval == "false")
config.moveonenospc = false;
else if(attrval == "true")
config.moveonenospc = true;
else
return -EINVAL;
return 0;
}
static
int
_setxattr_controlfile_func_policy(Config &config,
@ -248,6 +268,10 @@ _setxattr_controlfile(Config &config,
return _setxattr_minfreespace(config,
attrval,
flags);
else if(attr[2] == "moveonenospc")
return _setxattr_moveonenospc(config,
attrval,
flags);
break;
case 4:

4
src/symlink.cpp

@ -29,10 +29,10 @@
#include <unistd.h>
#include <string>
#include "fs.hpp"
#include "config.hpp"
#include "ugid.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

4
src/truncate.cpp

@ -31,10 +31,10 @@
#include <string>
#include <vector>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

4
src/unlink.cpp

@ -30,10 +30,10 @@
#include <string>
#include <vector>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

4
src/utimens.cpp

@ -31,10 +31,10 @@
#include <string>
#include <vector>
#include "ugid.hpp"
#include "fs.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
using std::string;
using std::vector;

33
src/write.cpp

@ -27,6 +27,12 @@
#include <errno.h>
#include <unistd.h>
#include "config.hpp"
#include "fileinfo.hpp"
#include "fs_movefile.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
static
int
_write(const int fd,
@ -52,10 +58,29 @@ namespace mergerfs
off_t offset,
fuse_file_info *ffi)
{
return _write(ffi->fh,
buf,
count,
offset);
int rv;
FileInfo* fi = reinterpret_cast<FileInfo*>(ffi->fh);
rv = _write(fi->fd,buf,count,offset);
if(rv == -ENOSPC)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
if(config.moveonenospc)
{
const ugid::Set ugid(0,0);
const rwlock::ReadGuard readlock(&config.srcmountslock);
rv = fs::movefile(config.srcmounts,fusepath,count,fi->fd);
if(rv == -1)
return -ENOSPC;
rv = _write(fi->fd,buf,count,offset);
}
}
return rv;
}
}
}

44
src/write_buf.cpp

@ -25,11 +25,25 @@
#if WRITE_BUF
#include <fuse.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "config.hpp"
#include "fileinfo.hpp"
#include "fs_movefile.hpp"
#include "policy.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include "write.hpp"
using std::string;
using std::vector;
using namespace mergerfs;
static
int
_write_buf(const int fd,
@ -58,9 +72,31 @@ namespace mergerfs
off_t offset,
fuse_file_info *ffi)
{
return _write_buf(ffi->fh,
*src,
offset);
int rv;
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi->fh);
rv = _write_buf(fi->fd,*src,offset);
if(rv == -ENOSPC)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
if(config.moveonenospc)
{
size_t extra;
const ugid::Set ugid(0,0);
const rwlock::ReadGuard readlock(&config.srcmountslock);
extra = fuse_buf_size(src);
rv = fs::movefile(config.srcmounts,fusepath,extra,fi->fd);
if(rv == -1)
return -ENOSPC;
rv = _write_buf(fi->fd,*src,offset);
}
}
return rv;
}
}
}

Loading…
Cancel
Save