Compare commits

...
29 Commits
Author SHA1 Message Date
Antonio SJ Musumeci 5850fbe3f1 bump README date 2015-09-14 22:45:45 -04:00
Antonio SJ Musumeci 1cc1b1f746 Merge pull request #139 from trapexit/setxattr-cleanup
cleanup controlfile manipulation
2015-09-14 22:42:06 -04:00
Antonio SJ Musumeci a960a7ef46 cleanup controlfile manipulation 2015-09-14 22:40:49 -04:00
Antonio SJ Musumeci 9afd0654b7 Merge pull request #138 from trapexit/defaults
include default_permissions in default arguments
2015-09-14 22:13:37 -04:00
Antonio SJ Musumeci e0cf97261c include default_permissions in default arguments
closes #130
2015-09-14 22:12:24 -04:00
Antonio SJ Musumeci 1782910437 Merge pull request #137 from trapexit/setgroups-docs
change README regarding setgroups cache and new rwlock ugid fallback
2015-09-14 19:34:22 -04:00
Antonio SJ Musumeci f4e3f28153 change README regarding setgroups cache and new rwlock ugid fallback 2015-09-14 19:33:30 -04:00
Antonio SJ Musumeci 6a96d3b69b Merge pull request #136 from trapexit/typo
fix typo in README
2015-09-14 19:06:37 -04:00
Antonio SJ Musumeci 08c0c2dfae fix typo in README. closes #134 2015-09-14 19:05:42 -04:00
Antonio SJ Musumeci ad5380d69e Merge pull request #135 from trapexit/op-ugid
make changing credentials opportunistic + per thread setgroups cache
2015-09-14 19:01:23 -04:00
Antonio SJ Musumeci 3163258a33 make changing credentials opportunistic + per thread setgroups cache
closes #129,#131
2015-09-14 19:00:07 -04:00
Antonio SJ Musumeci b194272e39 add notes on permissions and errors to readme 2015-09-08 18:06:32 -04:00
Antonio SJ Musumeci c131310c55 include known issues section to readme 2015-09-07 19:58:10 -04:00
Antonio SJ Musumeci 8323e16ad2 Merge pull request #126 from trapexit/realpathize
realpath'ize all source mount points. closes #117
2015-09-06 18:25:21 -04:00
Antonio SJ Musumeci ce9352987c realpath'ize all source mount points. closes #117 2015-09-06 18:24:36 -04:00
Antonio SJ Musumeci accd2d4ae0 Merge pull request #125 from trapexit/xattr-minfreespace
fix non-suffixed setxattr of user.mergerfs.minfreespace
2015-09-06 12:07:43 -04:00
Antonio SJ Musumeci 2d899479c3 fix non-suffixed setxattr of user.mergerfs.minfreespace 2015-09-05 23:08:11 -04:00
Antonio SJ Musumeci 803b2ae999 Merge pull request #122 from trapexit/clean-version
remove version.hpp on clean
2015-09-05 22:30:49 -04:00
Antonio SJ Musumeci e98b801c9d remove version.hpp on clean 2015-09-05 22:29:28 -04:00
Antonio SJ Musumeci 27cf7f8148 Merge pull request #121 from trapexit/xattr-version
add user.mergerfs.version xattr
2015-09-05 21:55:43 -04:00
Antonio SJ Musumeci b22528bac4 add user.mergerfs.version xattr 2015-09-05 21:48:15 -04:00
Antonio SJ Musumeci adf983c8c3 Merge pull request #120 from trapexit/policies-xattr
add user.mergerfs.policies xattr
2015-09-05 20:04:12 -04:00
Antonio SJ Musumeci e377d54c93 add user.mergerfs.policies xattr 2015-09-05 19:49:08 -04:00
Antonio SJ Musumeci 7bee6230ba Merge pull request #119 from trapexit/rpm
add building of rpm
2015-09-05 18:44:14 -04:00
Antonio SJ Musumeci 08d07b71e1 add building of rpm 2015-09-05 18:41:18 -04:00
Antonio SJ Musumeci ce2462042f Merge pull request #118 from trapexit/basic-fedora-build
add basic instructions for building on Fedora
2015-09-05 16:35:40 -04:00
Antonio SJ Musumeci 305f190f09 add basic instructions for building on Fedora 2015-09-05 16:34:59 -04:00
Antonio SJ Musumeci d89735abf6 Merge pull request #114 from trapexit/xattr
refactor and simplify getxattr for user.mergerfs.\*
2015-09-03 17:24:57 -04:00
Antonio SJ Musumeci 8178bf518c refactor and simplify getxattr for user.mergerfs.\* 2015-09-03 16:56:54 -04:00
40 changed files with 671 additions and 285 deletions
+35 -6
View File
@@ -33,11 +33,14 @@ INSTALL = $(shell which install)
MKTEMP = $(shell which mktemp)
STRIP = $(shell which strip)
PANDOC = $(shell which pandoc)
SED = $(shell which sed)
GZIP = $(shell which gzip)
RPMBUILD = $(shell which rpmbuild)
GIT2DEBCL = ./tools/git2debcl
CPPFIND = ./tools/cppfind
ifeq ($(PKGCONFIG),"")
$(error "pkg-config not installed"
$(error "pkg-config not installed")
endif
ifeq ($(PANDOC),"")
@@ -62,6 +65,8 @@ FLOCK = $(shell $(CPPFIND) "fuse_fs_flock")
READ_BUF = $(shell $(CPPFIND) "fuse_fs_read_buf")
WRITE_BUF = $(shell $(CPPFIND) "fuse_fs_write_buf")
UGID_USE_RWLOCK = 0
OPTS = -O2
SRC = $(wildcard src/*.cpp)
OBJ = $(SRC:src/%.cpp=obj/%.o)
@@ -71,6 +76,7 @@ MANPAGE = $(TARGET).1
FUSE_CFLAGS = $(shell $(PKGCONFIG) --cflags fuse)
CFLAGS = -g -Wall \
$(OPTS) \
-Wno-unused-result \
$(FUSE_CFLAGS) \
-DFUSE_USE_VERSION=26 \
-MMD \
@@ -78,7 +84,8 @@ CFLAGS = -g -Wall \
-DFALLOCATE=$(FALLOCATE) \
-DFLOCK=$(FLOCK) \
-DREAD_BUF=$(READ_BUF) \
-DWRITE_BUF=$(WRITE_BUF)
-DWRITE_BUF=$(WRITE_BUF) \
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
LDFLAGS = $(shell $(PKGCONFIG) fuse --libs)
PREFIX = /usr/local
@@ -103,7 +110,7 @@ help:
@echo "usage: make"
@echo "make XATTR_AVAILABLE=0 - to build program without xattrs functionality (auto discovered otherwise)"
$(TARGET): obj/obj-stamp $(OBJ)
$(TARGET): src/version.hpp obj/obj-stamp $(OBJ)
$(CXX) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
clonepath: $(TARGET)
@@ -115,6 +122,13 @@ changelog:
authors:
$(GIT) log --format='%aN <%aE>' | sort -f | uniq > AUTHORS
src/version.hpp:
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
@echo "#ifndef _VERSION_HPP" > src/version.hpp
@echo "#define _VERSION_HPP" >> src/version.hpp
@echo "static const char MERGERFS_VERSION[] = \"$(VERSION)\";" >> src/version.hpp
@echo "#endif" >> src/version.hpp
obj/obj-stamp:
$(MKDIR) -p obj
$(TOUCH) $@
@@ -122,8 +136,9 @@ obj/obj-stamp:
obj/%.o: src/%.cpp
$(CXX) $(CFLAGS) -c $< -o $@
clean:
clean: rpm-clean
$(RM) -rf obj
$(RM) -f src/version.hpp
$(RM) -f "$(TARGET)" "$(MANPAGE)" clonepath
$(FIND) . -name "*~" -delete
@@ -160,13 +175,14 @@ $(MANPAGE): README.md
man: $(MANPAGE)
tarball: clean man changelog authors
tarball: clean man changelog authors src/version.hpp
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
$(eval VERSION := $(subst -,_,$(VERSION)))
$(eval FILENAME := $(TARGET)-$(VERSION))
$(eval TMPDIR := $(shell $(MKTEMP) --tmpdir -d .$(FILENAME).XXXXXXXX))
$(MKDIR) $(TMPDIR)/$(FILENAME)
$(CP) -ar . $(TMPDIR)/$(FILENAME)
$(TAR) --exclude=.git -cz -C $(TMPDIR) -f ../$(FILENAME).tar.gz $(FILENAME)
$(TAR) --exclude=.git -cz -C $(TMPDIR) -f $(FILENAME).tar.gz $(FILENAME)
$(RM) -rf $(TMPDIR)
debian-changelog:
@@ -178,6 +194,19 @@ deb: debian-changelog
unsigned-deb: debian-changelog
dpkg-buildpackage -uc -us
rpm-clean:
$(RM) -rf rpmbuild
rpm: tarball
$(eval VERSION := $(shell $(GIT) describe --always --tags --dirty))
$(eval VERSION := $(subst -,_,$(VERSION)))
$(MKDIR) -p rpmbuild/{BUILD,RPMS,SOURCES}
$(SED) 's/__VERSION__/$(VERSION)/g' $(TARGET).spec > \
rpmbuild/SOURCES/$(TARGET).spec
cp -ar $(TARGET)-$(VERSION).tar.gz rpmbuild/SOURCES
$(RPMBUILD) -ba rpmbuild/SOURCES/$(TARGET).spec \
--define "_topdir $(CURDIR)/rpmbuild"
.PHONY: all clean install help
include $(wildcard obj/*.d)
+27 -8
View File
@@ -1,6 +1,6 @@
% mergerfs(1) mergerfs user manual
% Antonio SJ Musumeci <trapexit@spawn.link>
% 2015-07-03
% 2015-09-14
# NAME
@@ -20,10 +20,10 @@ Why create **mergerfs** when those exist? **mhddfs** has not been updated in som
###options###
* **defaults** is a shortcut for **auto_cache**. **big_writes**, **atomic_o_trunc**, **splice_read**, **splice_write**, and **splice_move** are in effect also enabled (by asking **FUSE** internally for such features) but if unavailable will be ignored. These options seem to provide the best performance.
* **defaults** is 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.
* **minfreespace** (defaults to **4G**) is the minimum space value used for the **lfs**, **fwfs**, and **epmfs** policies. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively.
* All FUSE functions which have a category (see below) are option keys. The syntax being **func.&lt;func&gt;=&lt;policy&gt;**. Example: **func.getattr=newest**.
* To set all function policies in a category use **categor.&lt;category&gt;=&lt;policy&gt;**. Example: **category.create=mfs**.
* To set all function policies in a category use **category.&lt;category&gt;=&lt;policy&gt;**. Example: **category.create=mfs**.
* Options are evaluated in the order listed so if the options are **func.rmdir=rand,category.action=ff** the **action** category setting will override the **rmdir** setting.
###srcpoints###
@@ -31,10 +31,10 @@ Why create **mergerfs** when those exist? **mhddfs** has not been updated in som
The source points argument is a colon (':') delimited list of paths. To make it simpler to include multiple source points without having to modify your [fstab](http://linux.die.net/man/5/fstab) we also support [globbing](http://linux.die.net/man/7/glob).
```
$ mergerfs /mnt/disk*:/mnt/cdrom /media/drives
$ mergerfs /mnt/disk\*:/mnt/cdrom /media/drives
```
The above line will use all points in /mnt prefixed with *disk* and the directory *cdrom*.
The above line will use all points in /mnt prefixed with *disk* and the directory *cdrom*. The `*` (or any glob tokens) must be escaped on the command line else the shell resolve them.
In /etc/fstab it'd look like the following:
@@ -117,6 +117,15 @@ $ make deb
$ sudo dpkg -i ../mergerfs_version_arch.deb
```
#### Fedora
```
$ su -
# dnf install fuse-devel libattr-devel pandoc gcc-c++
# cd mergerfs
# make rpm
# rpm -i rpmbuild/RPMS/<arch>/mergerfs-<verion>.<arch>.rpm
```
#### Generically
Have pkg-config, pandoc, libfuse, libattr1 installed.
@@ -149,6 +158,8 @@ 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.policies: all,einval,enosys,enotsup,epmfs,erofs,exdev,ff,ffwp,fwfs,lfs,mfs,newest,rand
user.mergerfs.version: 2.5.0
user.mergerfs.category.action: all
user.mergerfs.category.create: epmfs
user.mergerfs.category.search: ff
@@ -236,11 +247,17 @@ A B C
# TIPS / NOTES
* If you don't see some directories / files you expect in a merged point be sure the user has permission to all the underlying directories. If `/drive0/a` has is owned by `root:root` with ACLs set to `0700` and `/drive1/a` is `root:root` and `0755` you'll see only `/drive1/a`.
* Since POSIX gives you only error or success on calls its difficult to determine the proper behavior when applying the behavior to multiple targets. Generally if something succeeds when reading it returns the data it can. If something fails when making an action we continue on and return the last error.
* The recommended options are **defaults,allow_other**. The **allow_other** is to allow users who are not the one which executed mergerfs access to the mountpoint. **defaults** is described above and should offer the best performance. It's possible that if you're running on an older platform the **splice** features aren't available and could error. In that case simply use the other options manually.
* Remember that some policies mixed with some functions may result in strange behaviors. Not that some of these behaviors and race conditions couldn't happen outside **mergerfs** but that they are far more likely to occur on account of attempt to merge together multiple sources of data which could be out of sync due to the different policies.
* An example: [Kodi](http://kodi.tv) can apparently use directory [mtime](http://linux.die.net/man/2/stat) to more efficiently determine whether or not to scan for new content rather than simply performing a full scan. If using the current default **getattr** policy of **ff** its possible **Kodi** will miss an update on account of it returning the first directory found's **stat** info and its a later directory on another mount which had the **mtime** recently updated. To fix this you will want to set **func.getattr=newest**. Remember though that this is just **stat**. If the file is later **open**'ed or **unlink**'ed and the policy is different for those then a completely different file or directory could be acted on.
* An example: [Kodi](http://kodi.tv) and [Plex](http://plex.tv) can apparently use directory [mtime](http://linux.die.net/man/2/stat) to more efficiently determine whether or not to scan for new content rather than simply performing a full scan. If using the current default **getattr** policy of **ff** its possible **Kodi** will miss an update on account of it returning the first directory found's **stat** info and its a later directory on another mount which had the **mtime** recently updated. To fix this you will want to set **func.getattr=newest**. Remember though that this is just **stat**. If the file is later **open**'ed or **unlink**'ed and the policy is different for those then a completely different file or directory could be acted on.
* Due to previously mentioned issues its generally best to set **category** wide policies rather than individual **func**'s. This will help limit the confusion of tools such as [rsync](http://linux.die.net/man/1/rsync).
# Known Issues / Bugs
* Due to the overhead of [getgroups/setgroups](http://linux.die.net/man/2/setgroups) mergerfs utilizes a cache. This cache is opportunistic and per thread. This means each thread will query the supplemental groups for a user when that particular thread needs to change credentials and currently it will keep that data for the lifetime of the mount or thread. This means that if a user is added to a group it may not be picked up without the restart of mergerfs. However, since the high level FUSE API's (at least the standard version) thread pool dynamically grows and shrinks it's possible that over time a thread will be killed and later a new thread with no cache will start and query the new data.
# FAQ
*It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?*
@@ -249,10 +266,12 @@ A B C
If you are familiar with POSIX standards you'll know that this behavior poses a problem. **seteuid** and **setegid** affect the whole process and **libfuse** is multithreaded by default. We'd need to lock access to **seteuid** and **setegid** with a mutex so that the several threads aren't stepping on one another and files end up with weird permissions and ownership. This however wouldn't scale well. With lots of calls the contention on that mutex would be extremely high. Thankfully on Linux and OSX we have a better solution.
OSX has a [non-portable pthread extension](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/pthread_setugid_np.2.html) for per-thread user and group impersonation. When building on OSX mergerfs will use this without any mutexes.
OSX has a [non-portable pthread extension](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/pthread_setugid_np.2.html) for per-thread user and group impersonation.
Linux does not support [pthread_setugid_np](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/pthread_setugid_np.2.html) but user and group IDs are a per-thread attribute though documentation on that fact or how to manipulate them is not well distributed. From the **4.00** release of the Linux man-pages project for [setuid](http://man7.org/linux/man-pages/man2/setuid.2.html)
> At the kernel level, user IDs and group IDs are a per-thread attribute. However, POSIX requires that all threads in a process share the same credentials. The NPTL threading implementation handles the POSIX requirements by providing wrapper functions for the various system calls that change process UIDs and GIDs. These wrapper functions (including the one for setuid()) employ a signal-based technique to ensure that when one thread changes credentials, all of the other threads in the process also change their credentials. For details, see nptl(7).
Turns out the setreuid syscalls apply only to the thread. GLIBC hides this away using RT signals and other tricks. Taking after **Samba** mergerfs uses **syscall(SYS_setreuid,...)** to set the callers credentials for that thread only. Jumping back to **root** as necessary should escalated privileges be needed (for instance: to clone paths).
Turns out the setreuid syscalls apply only to the thread. GLIBC hides this away using RT signals to inform all threads to change credentials. Taking after **Samba** mergerfs uses **syscall(SYS_setreuid,...)** to set the callers credentials for that thread only. Jumping back to **root** as necessary should escalated privileges be needed (for instance: to clone paths).
For non-Linux systems mergerfs uses a read-write lock and changes credentials only when necessary. If multiple threads are to be user X then only the first one will need to change the processes credentials. So long as the other threads need to be user X they will take a readlock allow multiple threads to share the credentials. Once a request comes in to run as user Y that thread will attempt a write lock and change to Y's credentials when it can. If the ability to give writers priority is supported then that flag will be used so threads trying to change credentials don't starve. This isn't the best solution but should work reasonably well. As new platforms are supported if they offer per thread credentials those APIs will be adopted.
+50
View File
@@ -0,0 +1,50 @@
Name: mergerfs
Version: __VERSION__
Release: 1%{?dist}
Summary: A FUSE union filesystem
Group: Applications/System
License: MIT
URL: https://github.com/trapexit/mergerfs
Source: mergerfs-%{version}.tar.gz
BuildRequires: gcc-c++
BuildRequires: libattr-devel
BuildRequires: fuse-devel
# rpmbuild driven by the Makefile uses git to generate a version number
BuildRequires: git
# pandoc pulls in ~60 packages (87M installed) from EPEL :(
BuildRequires: pandoc
Requires: fuse-libs
%prep
%setup -q
%description
mergerfs is similar to mhddfs, unionfs, and aufs. Like mhddfs in that it too
uses FUSE. Like aufs in that it provides multiple policies for how to handle
behavior.
%build
make %{?_smp_mflags}
%install
make install PREFIX=%{_prefix} DESTDIR=%{buildroot}
%files
%{_bindir}/*
%doc %{_mandir}/*
%changelog
* Sat Sep 05 2015 Antonio SJ Musumeci <trapexit@spawn.link>
- Include PREFIX to install
* Mon Dec 29 2014 Joe Lawrence <joe.lawrence@stratus.com>
- Tweak rpmbuild to archive current git HEAD into a tarball, then (re)build in
the rpmbuild directory -- more complicated but seemingly better suited to
generate source and debug rpms.
* Fri Jun 20 2014 Joe Lawrence <joe.lawrence@stratus.com>
- Initial rpm spec file.
+4 -4
View File
@@ -74,10 +74,10 @@ namespace mergerfs
access(const char *fusepath,
int mask)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _access(config.access,
config.srcmounts,
+4 -4
View File
@@ -74,10 +74,10 @@ namespace mergerfs
chmod(const char *fusepath,
mode_t mode)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _chmod(config.chmod,
config.srcmounts,
+4 -4
View File
@@ -78,10 +78,10 @@ namespace mergerfs
uid_t uid,
gid_t gid)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _chown(config.chown,
config.srcmounts,
+6 -5
View File
@@ -40,6 +40,7 @@
using std::string;
using std::vector;
using mergerfs::Policy;
using namespace mergerfs;
static
int
@@ -69,7 +70,7 @@ _create(Policy::Func::Search searchFunc,
if(createpath[0] != existingpath[0])
{
const mergerfs::ugid::SetResetGuard ugid(0,0);
const ugid::SetRootGuard ugidGuard;
fs::clonepath(existingpath[0],createpath[0],dirname);
}
@@ -93,10 +94,10 @@ namespace mergerfs
mode_t mode,
fuse_file_info *fileinfo)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _create(config.getattr,
config.create,
+18
View File
@@ -29,6 +29,8 @@
#include <cstdlib>
#include <iterator>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -510,4 +512,20 @@ namespace fs
globfree(&gbuf);
}
void
realpathize(vector<string> &strs)
{
char *rv;
char buf[PATH_MAX];
for(size_t i = 0; i < strs.size(); i++)
{
rv = ::realpath(strs[i].c_str(),buf);
if(rv == NULL)
continue;
strs[i] = buf;
}
}
};
+2
View File
@@ -122,6 +122,8 @@ namespace fs
void glob(const vector<string> &patterns,
vector<string> &strs);
void realpathize(vector<string> &strs);
};
#endif // __FS_HPP__
+7 -5
View File
@@ -45,14 +45,16 @@ static
int
_getattr_controlfile(struct stat &buf)
{
time_t now = time(NULL);
static const uid_t uid = ::getuid();
static const gid_t gid = ::getgid();
static const time_t now = ::time(NULL);
buf.st_dev = 0;
buf.st_ino = 0;
buf.st_mode = (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
buf.st_nlink = 1;
buf.st_uid = ::getuid();
buf.st_gid = ::getgid();
buf.st_uid = uid;
buf.st_gid = gid;
buf.st_rdev = 0;
buf.st_size = 0;
buf.st_blksize = 1024;
@@ -100,8 +102,8 @@ namespace mergerfs
if(fusepath == config.controlfile)
return _getattr_controlfile(*st);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _getattr(config.getattr,
config.srcmounts,
+89 -40
View File
@@ -40,21 +40,40 @@
#include "rwlock.hpp"
#include "xattr.hpp"
#include "str.hpp"
#include "version.hpp"
using std::string;
using std::vector;
using std::set;
using namespace mergerfs;
static
int
_lgetxattr(const string &path,
const string &name,
void *value,
const size_t size)
{
#ifndef WITHOUT_XATTR
int rv;
rv = ::lgetxattr(path.c_str(),name.c_str(),value,size);
return ((rv == -1) ? -errno : rv);
#else
return -ENOSUP;
#endif
}
static
void
_getxattr_controlfile_fusefunc_policy(const Config &config,
const char *attrbasename,
const string &attr,
string &attrvalue)
{
FuseFunc fusefunc;
fusefunc = FuseFunc::find(attrbasename);
fusefunc = FuseFunc::find(attr);
if(fusefunc != FuseFunc::invalid)
attrvalue = (std::string)*config.policies[(FuseFunc::Enum::Type)*fusefunc];
}
@@ -62,12 +81,12 @@ _getxattr_controlfile_fusefunc_policy(const Config &config,
static
void
_getxattr_controlfile_category_policy(const Config &config,
const char *attrbasename,
const string &attr,
string &attrvalue)
{
Category cat;
cat = Category::find(attrbasename);
cat = Category::find(attr);
if(cat != Category::invalid)
{
vector<string> policies;
@@ -104,28 +123,60 @@ _getxattr_controlfile_minfreespace(const Config &config,
attrvalue = buf;
}
static
void
_getxattr_controlfile_policies(const Config &config,
string &attrvalue)
{
size_t i = Policy::Enum::begin();
attrvalue = (string)Policy::policies[i];
for(i++; i < Policy::Enum::end(); i++)
attrvalue += ',' + (string)Policy::policies[i];
}
static
void
_getxattr_controlfile_version(string &attrvalue)
{
attrvalue = MERGERFS_VERSION;
}
static
int
_getxattr_controlfile(const Config &config,
const char *attrname,
const string &attrname,
char *buf,
const size_t count)
{
size_t len;
string attrvalue;
const char *attrbasename = &attrname[sizeof("user.mergerfs.")-1];
vector<string> attr;
if(strncmp("user.mergerfs.",attrname,sizeof("user.mergerfs.")-1))
str::split(attr,attrname,'.');
if((attr[0] != "user") || (attr[1] != "mergerfs"))
return -ENOATTR;
if(!strcmp("srcmounts",attrbasename))
_getxattr_controlfile_srcmounts(config,attrvalue);
else if(!strcmp("minfreespace",attrbasename))
_getxattr_controlfile_minfreespace(config,attrvalue);
else if(!strncmp("category.",attrbasename,sizeof("category.")-1))
_getxattr_controlfile_category_policy(config,&attrbasename[sizeof("category.")-1],attrvalue);
else if(!strncmp("func.",attrbasename,sizeof("func.")-1))
_getxattr_controlfile_fusefunc_policy(config,&attrbasename[sizeof("func.")-1],attrvalue);
switch(attr.size())
{
case 3:
if(attr[2] == "srcmounts")
_getxattr_controlfile_srcmounts(config,attrvalue);
else if(attr[2] == "minfreespace")
_getxattr_controlfile_minfreespace(config,attrvalue);
else if(attr[2] == "policies")
_getxattr_controlfile_policies(config,attrvalue);
else if(attr[2] == "version")
_getxattr_controlfile_version(attrvalue);
break;
case 4:
if(attr[2] == "category")
_getxattr_controlfile_category_policy(config,attr[3],attrvalue);
else if(attr[2] == "func")
_getxattr_controlfile_fusefunc_policy(config,attr[3],attrvalue);
break;
}
if(attrvalue.empty())
return -ENOATTR;
@@ -155,7 +206,7 @@ _getxattr_from_string(char *destbuf,
return srcbufsize;
if(srcbufsize > destbufsize)
return (errno = ERANGE, -1);
return -ERANGE;
memcpy(destbuf,src.data(),srcbufsize);
@@ -176,7 +227,7 @@ _getxattr_user_mergerfs_allpaths(const vector<string> &srcmounts,
concated = str::join(paths,'\0');
return ::_getxattr_from_string(buf,count,concated);
return _getxattr_from_string(buf,count,concated);
}
static
@@ -185,22 +236,24 @@ _getxattr_user_mergerfs(const string &basepath,
const string &fusepath,
const string &fullpath,
const vector<string> &srcmounts,
const char *attrname,
const string &attrname,
char *buf,
const size_t count)
{
const char *attrbasename = &attrname[sizeof("user.mergerfs")];
vector<string> attr;
if(!strcmp(attrbasename,"basepath"))
return ::_getxattr_from_string(buf,count,basepath);
else if(!strcmp(attrbasename,"fullpath"))
return ::_getxattr_from_string(buf,count,fullpath);
else if(!strcmp(attrbasename,"relpath"))
return ::_getxattr_from_string(buf,count,fusepath);
else if(!strcmp(attrbasename,"allpaths"))
return ::_getxattr_user_mergerfs_allpaths(srcmounts,fusepath,buf,count);
str::split(attr,attrname,'.');
return ::lgetxattr(fullpath.c_str(),attrname,buf,count);
if(attr[2] == "basepath")
return _getxattr_from_string(buf,count,basepath);
else if(attr[2] == "relpath")
return _getxattr_from_string(buf,count,fusepath);
else if(attr[2] == "fullpath")
return _getxattr_from_string(buf,count,fullpath);
else if(attr[2] == "allpaths")
return _getxattr_user_mergerfs_allpaths(srcmounts,fusepath,buf,count);
return -ENOATTR;
}
static
@@ -209,30 +262,26 @@ _getxattr(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const size_t minfreespace,
const string &fusepath,
const char *attrname,
const string &attrname,
char *buf,
const size_t count)
{
#ifndef WITHOUT_XATTR
int rv;
vector<string> basepath;
string fullpath;
vector<string> basepath;
rv = searchFunc(srcmounts,fusepath,minfreespace,basepath);
if(rv == -1)
return -errno;
fullpath = fs::path::make(basepath[0],fusepath);
fs::path::make(basepath[0],fusepath,fullpath);
if(!strncmp("user.mergerfs.",attrname,sizeof("user.mergerfs.")-1))
if(str::isprefix(attrname,"user.mergerfs."))
rv = _getxattr_user_mergerfs(basepath[0],fusepath,fullpath,srcmounts,attrname,buf,count);
else
rv = ::lgetxattr(fullpath.c_str(),attrname,buf,count);
rv = _lgetxattr(fullpath,attrname,buf,count);
return ((rv == -1) ? -errno : rv);
#else
return -ENOTSUP;
#endif
return rv;
}
namespace mergerfs
@@ -254,8 +303,8 @@ namespace mergerfs
buf,
count);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _getxattr(config.getxattr,
config.srcmounts,
+3 -18
View File
@@ -25,6 +25,7 @@
#include <fuse.h>
#include "config.hpp"
#include "ugid.hpp"
namespace mergerfs
{
@@ -33,27 +34,11 @@ namespace mergerfs
void *
init(fuse_conn_info *conn)
{
#ifdef FUSE_CAP_ASYNC_READ
conn->want |= FUSE_CAP_ASYNC_READ;
#endif
ugid::init();
#ifdef FUSE_CAP_IOCTL_DIR
conn->want |= FUSE_CAP_IOCTL_DIR;
#endif
#ifdef FUSE_CAP_ATOMIC_O_TRUNC
conn->want |= FUSE_CAP_ATOMIC_O_TRUNC;
#endif
#ifdef FUSE_CAP_BIG_WRITES
conn->want |= FUSE_CAP_BIG_WRITES;
#endif
#ifdef FUSE_CAP_SPLICE_WRITE
conn->want |= FUSE_CAP_SPLICE_WRITE;
#endif
#ifdef FUSE_CAP_SPLICE_READ
conn->want |= FUSE_CAP_SPLICE_READ;
#endif
#ifdef FUSE_CAP_SPLICE_MOVE
conn->want |= FUSE_CAP_SPLICE_MOVE;
#endif
return &Config::get_writable();
}
+7 -7
View File
@@ -41,9 +41,9 @@ using namespace mergerfs;
static
int
_ioctl(const int fd,
const int cmd,
void *data)
_ioctl(const int fd,
const int cmd,
void *data)
{
int rv;
@@ -98,10 +98,10 @@ _ioctl_dir(const string &fusepath,
const int cmd,
void *data)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _ioctl_dir_base(config.getattr,
config.srcmounts,
+6 -5
View File
@@ -38,6 +38,7 @@
using std::string;
using std::vector;
using mergerfs::Policy;
using namespace mergerfs;
static
int
@@ -64,7 +65,7 @@ _single_link(Policy::Func::Search searchFunc,
return -1;
{
const mergerfs::ugid::SetResetGuard ugid(0,0);
const ugid::SetRootGuard ugidGuard;
fs::clonepath(foundpath[0],base,newpathdir);
}
@@ -110,10 +111,10 @@ namespace mergerfs
link(const char *from,
const char *to)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _link(config.getattr,
config.link,
+6 -4
View File
@@ -52,9 +52,11 @@ _listxattr_controlfile(char *list,
const vector<string> strs =
buildvector<string>
("user.mergerfs.srcmounts")
("user.mergerfs.minfreespace");
("user.mergerfs.minfreespace")
("user.mergerfs.policies")
("user.mergerfs.version");
xattrs.reserve(512);
xattrs.reserve(1024);
for(size_t i = 0; i < strs.size(); i++)
xattrs += (strs[i] + '\0');
for(size_t i = Category::Enum::BEGIN; i < Category::Enum::END; i++)
@@ -115,8 +117,8 @@ namespace mergerfs
if(fusepath == config.controlfile)
return _listxattr_controlfile(list,size);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _listxattr(config.listxattr,
config.srcmounts,
+6 -5
View File
@@ -39,6 +39,7 @@
using std::string;
using std::vector;
using mergerfs::Policy;
using namespace mergerfs;
static
int
@@ -72,7 +73,7 @@ _mkdir(Policy::Func::Search searchFunc,
if(createpath != existingpath[0])
{
const mergerfs::ugid::SetResetGuard ugid(0,0);
const ugid::SetRootGuard ugidGuard;
fs::clonepath(existingpath[0],createpath,dirname);
}
@@ -94,10 +95,10 @@ namespace mergerfs
mkdir(const char *fusepath,
mode_t mode)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _mkdir(config.getattr,
config.mkdir,
+6 -6
View File
@@ -40,7 +40,7 @@
using std::string;
using std::vector;
using mergerfs::Policy;
using namespace mergerfs;
static
int
@@ -74,7 +74,7 @@ _mknod(Policy::Func::Search searchFunc,
if(createpath != existingpath[0])
{
const mergerfs::ugid::SetResetGuard ugid(0,0);
const ugid::SetRootGuard ugidGuard;
fs::clonepath(existingpath[0],createpath,dirname);
}
@@ -97,10 +97,10 @@ namespace mergerfs
mode_t mode,
dev_t rdev)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _mknod(config.getattr,
config.mknod,
+3
View File
@@ -53,6 +53,9 @@ namespace num
tmp *= (1024 * 1024 * 1024);
break;
case '\0':
break;
default:
return -1;
}
+4 -4
View File
@@ -76,10 +76,10 @@ namespace mergerfs
open(const char *fusepath,
fuse_file_info *fileinfo)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _open(config.open,
config.srcmounts,
+8 -5
View File
@@ -94,12 +94,13 @@ static
void
set_default_options(fuse_args &args)
{
// set_option(args,"big_writes");
// set_option(args,"splice_read");
// set_option(args,"splice_write");
// set_option(args,"splice_move");
set_option(args,"atomic_o_trunc");
set_option(args,"auto_cache");
// set_option(args,"atomic_o_trunc");
set_option(args,"big_writes");
set_option(args,"default_permissions");
set_option(args,"splice_move");
set_option(args,"splice_read");
set_option(args,"splice_write");
}
static
@@ -200,6 +201,8 @@ process_srcmounts(const char *arg,
fs::glob(paths,config.srcmounts);
fs::realpathize(config.srcmounts);
return 0;
}
+3
View File
@@ -60,6 +60,9 @@ namespace mergerfs
rand,
END
};
static size_t begin() { return BEGIN; }
static size_t end() { return END; }
};
struct Func
+4 -4
View File
@@ -96,10 +96,10 @@ namespace mergerfs
off_t offset,
fuse_file_info *fi)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _readdir(config.srcmounts,
fusepath,
+4 -4
View File
@@ -75,10 +75,10 @@ namespace mergerfs
char *buf,
size_t size)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _readlink(config.readlink,
config.srcmounts,
+2 -2
View File
@@ -87,8 +87,8 @@ namespace mergerfs
if(fusepath == config.controlfile)
return -ENOTSUP;
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _removexattr(config.removexattr,
config.srcmounts,
+4 -4
View File
@@ -130,10 +130,10 @@ namespace mergerfs
rename(const char *oldpath,
const char *newpath)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _rename(config.getattr,
config.rename,
+4 -4
View File
@@ -73,10 +73,10 @@ namespace mergerfs
int
rmdir(const char *fusepath)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readguard(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readguard(&config.srcmountslock);
return _rmdir(config.rmdir,
config.srcmounts,
+41 -37
View File
@@ -59,6 +59,7 @@ _add_srcmounts(vector<string> &srcmounts,
str::split(patterns,values,':');
fs::glob(patterns,additions);
fs::realpathize(additions);
if(!additions.empty())
{
@@ -106,6 +107,7 @@ _replace_srcmounts(vector<string> &srcmounts,
str::split(patterns,values,':');
fs::glob(patterns,newmounts);
fs::realpathize(newmounts);
{
const rwlock::WriteGuard wrg(&srcmountslock);
@@ -187,7 +189,7 @@ _setxattr_minfreespace(Config &config,
static
int
_setxattr_controlfile_func_policy(Config &config,
const char *funcname,
const string &funcname,
const string &attrval,
const int flags)
{
@@ -206,7 +208,7 @@ _setxattr_controlfile_func_policy(Config &config,
static
int
_setxattr_controlfile_category_policy(Config &config,
const char *categoryname,
const string &categoryname,
const string &attrval,
const int flags)
{
@@ -225,38 +227,45 @@ _setxattr_controlfile_category_policy(Config &config,
static
int
_setxattr_controlfile(Config &config,
const char *attrname,
const string &attrname,
const string &attrval,
const int flags)
{
const char *attrbasename = &attrname[sizeof("user.mergerfs.")-1];
vector<string> attr;
if(strncmp("user.mergerfs.",attrname,sizeof("user.mergerfs.")-1))
return -ENOATTR;
str::split(attr,attrname,'.');
if(attrval.empty())
return -EINVAL;
switch(attr.size())
{
case 3:
if(attr[2] == "srcmounts")
return _setxattr_srcmounts(config.srcmounts,
config.srcmountslock,
config.destmount,
attrval,
flags);
else if(attr[2] == "minfreespace")
return _setxattr_minfreespace(config,
attrval,
flags);
break;
if(!strcmp("srcmounts",attrbasename))
return _setxattr_srcmounts(config.srcmounts,
config.srcmountslock,
config.destmount,
attrval,
flags);
else if(!strcmp("minfreespace",attrbasename))
return _setxattr_minfreespace(config,
attrval,
flags);
else if(!strncmp("category.",attrbasename,sizeof("category.")-1))
return _setxattr_controlfile_category_policy(config,
&attrbasename[sizeof("category.")-1],
case 4:
if(attr[2] == "category")
return _setxattr_controlfile_category_policy(config,
attr[3],
attrval,
flags);
else if(attr[2] == "func")
return _setxattr_controlfile_func_policy(config,
attr[3],
attrval,
flags);
else if(!strncmp("func.",attrbasename,sizeof("func.")-1))
return _setxattr_controlfile_func_policy(config,
&attrbasename[sizeof("func.")-1],
attrval,
flags);
break;
default:
break;
}
return -EINVAL;
}
@@ -311,18 +320,13 @@ namespace mergerfs
const Config &config = Config::get(fc);
if(fusepath == config.controlfile)
{
if((fc->uid != ::getuid()) && (fc->gid != ::getgid()))
return -EPERM;
return _setxattr_controlfile(Config::get_writable(),
attrname,
string(attrval,attrvalsize),
flags);
return _setxattr_controlfile(Config::get_writable(),
attrname,
string(attrval,attrvalsize),
flags);
}
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _setxattr(config.setxattr,
config.srcmounts,
+4 -4
View File
@@ -124,10 +124,10 @@ namespace mergerfs
statfs(const char *fusepath,
struct statvfs *stat)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _statfs(config.srcmounts,
*stat);
+8
View File
@@ -139,4 +139,12 @@ namespace str
++si;
}
}
bool
isprefix(const string &s0,
const string &s1)
{
return ((s0.size() >= s1.size()) &&
(s0.compare(0,s1.size(),s1) == 0));
}
}
+4
View File
@@ -54,4 +54,8 @@ namespace str
void
erase_fnmatches(const std::vector<std::string> &pattern,
std::vector<std::string> &strs);
bool
isprefix(const std::string &s0,
const std::string &s1);
}
+4 -4
View File
@@ -77,10 +77,10 @@ namespace mergerfs
symlink(const char *oldpath,
const char *newpath)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _symlink(config.symlink,
config.srcmounts,
+4 -4
View File
@@ -77,10 +77,10 @@ namespace mergerfs
truncate(const char *fusepath,
off_t size)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _truncate(config.truncate,
config.srcmounts,
+59 -4
View File
@@ -22,10 +22,65 @@
THE SOFTWARE.
*/
#include "ugid.hpp"
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#if defined __linux__
#elif defined __APPLE__
#include <vector>
#include <map>
typedef std::vector<gid_t> gid_t_vector;
typedef std::map<uid_t,gid_t_vector> gid_t_cache;
#if defined __linux__ and UGID_USE_RWLOCK == 0
#include "ugid_linux.ipp"
#else
pthread_mutex_t mergerfs::ugid::SetResetGuard::lock = PTHREAD_MUTEX_INITIALIZER;
#include "ugid_rwlock.ipp"
#endif
namespace mergerfs
{
namespace ugid
{
static
inline
void
prime_cache(const uid_t uid,
const gid_t gid,
gid_t_vector &gidlist)
{
int rv;
char buf[4096];
struct passwd pwd;
struct passwd *pwdrv;
rv = getpwuid_r(uid,&pwd,buf,sizeof(buf),&pwdrv);
if(pwdrv != NULL && rv == 0)
{
int count;
count = 0;
rv = ::getgrouplist(pwd.pw_name,gid,NULL,&count);
gidlist.resize(count);
rv = ::getgrouplist(pwd.pw_name,gid,&gidlist[0],&count);
if(rv == -1)
gidlist.resize(1,gid);
}
}
void
initgroups(const uid_t uid,
const gid_t gid)
{
static __thread gid_t_cache cache;
gid_t_vector &gidlist = cache[uid];
if(gidlist.empty())
prime_cache(uid,gid,gidlist);
setgroups(gidlist);
}
}
}
+19 -4
View File
@@ -25,12 +25,27 @@
#ifndef __UGID_HPP__
#define __UGID_HPP__
#if defined __linux__
#include <sys/types.h>
#include <unistd.h>
#include <vector>
typedef std::vector<gid_t> gid_t_vector;
namespace mergerfs
{
namespace ugid
{
void init();
void initgroups(const uid_t uid, const gid_t gid);
void setgroups(const gid_t_vector &gidlist);
}
}
#if defined __linux__ and UGID_USE_RWLOCK == 0
#include "ugid_linux.hpp"
#elif defined __APPLE__
#include "ugid_osx.hpp"
#else
#include "ugid_mutex.hpp"
#include "ugid_rwlock.hpp"
#endif
#endif /* __UGID_HPP__ */
+53 -20
View File
@@ -27,38 +27,71 @@
#include <unistd.h>
#include <sys/syscall.h>
#include <grp.h>
#include <pwd.h>
#include <map>
#include <vector>
namespace mergerfs
{
namespace ugid
{
struct SetResetGuard
extern __thread uid_t currentuid;
extern __thread gid_t currentgid;
extern __thread bool initialized;
struct Set
{
SetResetGuard(const uid_t _newuid,
const gid_t _newgid)
Set(const uid_t newuid,
const gid_t newgid)
{
olduid = ::syscall(SYS_geteuid);
oldgid = ::syscall(SYS_getegid);
newuid = _newuid;
newgid = _newgid;
if(!initialized)
{
currentuid = ::syscall(SYS_geteuid);
currentgid = ::syscall(SYS_getegid);
initialized = true;
}
if(newgid != oldgid)
::syscall(SYS_setregid,-1,newgid);
if(newuid != olduid)
if(newuid == currentuid && newgid == currentgid)
return;
if(currentuid != 0)
{
::syscall(SYS_setreuid,-1,0);
::syscall(SYS_setregid,-1,0);
}
if(newgid)
{
::syscall(SYS_setregid,-1,newgid);
initgroups(newuid,newgid);
}
if(newuid)
::syscall(SYS_setreuid,-1,newuid);
}
~SetResetGuard()
currentuid = newuid;
currentgid = newgid;
}
};
struct SetRootGuard
{
SetRootGuard() :
prevuid(currentuid),
prevgid(currentgid)
{
if(olduid != newuid)
::syscall(SYS_setreuid,-1,olduid);
if(oldgid != newgid)
::syscall(SYS_setregid,-1,oldgid);
Set(0,0);
}
uid_t olduid;
gid_t oldgid;
uid_t newuid;
gid_t newgid;
~SetRootGuard()
{
Set(prevuid,prevgid);
}
const uid_t prevuid;
const gid_t prevgid;
};
}
}
+19 -23
View File
@@ -22,36 +22,32 @@
THE SOFTWARE.
*/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/unistd.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <vector>
typedef std::vector<gid_t> gid_t_vector;
namespace mergerfs
{
namespace ugid
{
struct SetResetGuard
__thread uid_t currentuid = 0;
__thread gid_t currentgid = 0;
__thread bool initialized = false;
void
init()
{
SetResetGuard(const uid_t _newuid,
const gid_t _newgid)
{
pthread_getugid_np(&olduid,&oldgid);
newuid = _newuid;
newgid = _newgid;
}
if(newgid != oldgid || newuid != olduid)
pthread_setugid_np(newuid,newgid);
}
~SetResetGuard()
{
if(newgid != oldgid || newuid != olduid)
pthread_setugid_np(newuid,newgid);
}
uid_t olduid;
gid_t oldgid;
uid_t newuid;
gid_t newgid;
};
int
setgroups(const gid_t_vector &gidlist)
{
return ::syscall(SYS_setgroups,gidlist.size(),&gidlist[0]);
}
}
}
+107
View File
@@ -0,0 +1,107 @@
/*
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 <pthread.h>
namespace mergerfs
{
namespace ugid
{
extern uid_t currentuid;
extern gid_t currentgid;
extern pthread_rwlock_t rwlock;
static
void
ugid_set(const uid_t newuid,
const gid_t newgid)
{
pthread_rwlock_rdlock(&rwlock);
if(newuid == currentuid && newgid == currentgid)
return;
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_wrlock(&rwlock);
if(newuid == currentuid && newgid == currentgid)
return;
if(currentuid != 0)
{
::seteuid(0);
::setegid(0);
}
if(newgid)
{
::setegid(newgid);
initgroups(newuid,newgid);
}
if(newuid)
::seteuid(newuid);
currentuid = newuid;
currentgid = newgid;
}
struct Set
{
Set(const uid_t newuid,
const gid_t newgid)
{
ugid_set(newuid,newgid);
}
~Set()
{
pthread_rwlock_unlock(&rwlock);
}
};
struct SetRootGuard
{
SetRootGuard() :
prevuid(currentuid),
prevgid(currentgid)
{
pthread_rwlock_unlock(&rwlock);
ugid_set(0,0);
}
~SetRootGuard()
{
pthread_rwlock_unlock(&rwlock);
ugid_set(prevuid,prevgid);
}
const uid_t prevuid;
const gid_t prevgid;
};
}
}
+25 -29
View File
@@ -22,47 +22,43 @@
THE SOFTWARE.
*/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
typedef std::vector<gid_t> gid_t_vector;
namespace mergerfs
{
namespace ugid
{
struct SetResetGuard
uid_t currentuid;
gid_t currentgid;
pthread_rwlock_t rwlock;
void
init()
{
SetResetGuard(const uid_t _newuid,
const gid_t _newgid)
{
pthread_mutex_lock(&lock);
pthread_rwlockattr_t attr;
olduid = ::geteuid();
oldgid = ::getegid();
newuid = _newuid;
newgid = _newgid;
pthread_rwlockattr_init(&attr);
# if defined PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
# endif
if(newgid != oldgid)
setegid(newgid);
if(newuid != olduid)
seteuid(newuid);
}
pthread_rwlock_init(&rwlock,&attr);
~SetResetGuard()
{
if(olduid != newuid)
seteuid(newuid);
if(oldgid != newgid)
setegid(newgid);
}
currentuid = ::geteuid();
currentgid = ::getegid();
}
uid_t olduid;
gid_t oldgid;
uid_t newuid;
gid_t newgid;
static pthread_mutex_t lock;
};
int
setgroups(const gid_t_vector &gidlist)
{
return ::setgroups(gidlist.size(),&gidlist[0]);
}
}
}
+4 -4
View File
@@ -74,10 +74,10 @@ namespace mergerfs
int
unlink(const char *fusepath)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _unlink(config.unlink,
config.srcmounts,
+4 -4
View File
@@ -77,10 +77,10 @@ namespace mergerfs
utimens(const char *fusepath,
const timespec ts[2])
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _utimens(config.utimens,
config.srcmounts,