mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-26 02:48:59 -04:00
Rework credential handling to support chroot & idmap (#1595)
Some checks are pending
mkdocs / deploy (push) Waiting to run
Some checks are pending
mkdocs / deploy (push) Waiting to run
Run in an elevated credential mode (root) and let the kernel manage entitlements. Will result in slightly different behavior but should not be noticed by most.
This commit is contained in:
parent
d8918458bf
commit
9b046d9a34
69 changed files with 788 additions and 1066 deletions
1
Makefile
1
Makefile
|
|
@ -65,7 +65,6 @@ OPT_FLAGS := -O0 \
|
|||
$(SANITIZE) \
|
||||
-fstack-protector-strong \
|
||||
-Wextra \
|
||||
-Werror \
|
||||
-Wno-unused-parameter \
|
||||
-DDEBUG
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -1737,14 +1737,14 @@ fuse_send_enomem(struct fuse_ll *f_,
|
|||
fuse_send_errno(f_,ch_,ENOMEM,unique_id_);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_send_einval(struct fuse_ll *f_,
|
||||
struct fuse_chan *ch_,
|
||||
const uint64_t unique_id_)
|
||||
{
|
||||
fuse_send_errno(f_,ch_,EINVAL,unique_id_);
|
||||
}
|
||||
// static
|
||||
// void
|
||||
// fuse_send_einval(struct fuse_ll *f_,
|
||||
// struct fuse_chan *ch_,
|
||||
// const uint64_t unique_id_)
|
||||
// {
|
||||
// fuse_send_errno(f_,ch_,EINVAL,unique_id_);
|
||||
// }
|
||||
|
||||
static
|
||||
int
|
||||
|
|
@ -1777,9 +1777,6 @@ fuse_ll_buf_process_read(struct fuse_session *se_,
|
|||
|
||||
in = (struct fuse_in_header*)msgbuf_->mem;
|
||||
|
||||
if((in->uid == FUSE_INVALID_UIDGID) || (in->gid == FUSE_INVALID_UIDGID))
|
||||
return fuse_send_einval(se_->f,se_->ch,in->unique);
|
||||
|
||||
req = fuse_ll_alloc_req(se_->f);
|
||||
if(req == NULL)
|
||||
return fuse_send_enomem(se_->f,se_->ch,in->unique);
|
||||
|
|
|
|||
|
|
@ -213,17 +213,14 @@ config file.
|
|||
* **passthrough.max-stack-depth=INT**: Set to `1` another filesystem
|
||||
can be stacked on mergerfs. Set to `2` to have mergerfs stacked over
|
||||
another filesystem. (default: 1)
|
||||
* **gid-cache.expire-timeout=INT**: Number of seconds till
|
||||
supplemental group data is refreshed in the [GID
|
||||
cache](../known_issues_bugs.md#supplemental-user-groups). (default:
|
||||
3600)
|
||||
* **gid-cache.remove-timeout=INT**: Number of seconds to wait till
|
||||
cached data is removed due to lack of usage. (default: 43200)
|
||||
* **remember-nodes=INT**: The number of seconds to keep the internal
|
||||
representation of a file once the OS tells mergerfs it is no longer
|
||||
needed. Really only needed for [exporting mergerfs via
|
||||
NFS](../remote_filesystems.md) (default: 0)
|
||||
* **noforget**: Effectively sets `remember-nodes` to infinity.
|
||||
* **allow-idmap=BOOL**: Enables idmap (identity mapping). (default:
|
||||
true)
|
||||
|
||||
|
||||
**NOTE:** Options are evaluated in the order listed so if the options
|
||||
are **func.rmdir=rand,category.action=ff** the **action** category
|
||||
|
|
|
|||
|
|
@ -5,16 +5,10 @@
|
|||
[Primarily Linux.](../setup/installation.md) FreeBSD is casually
|
||||
supported but not well tested.
|
||||
|
||||
With FreeBSD certain Linux functions and FUSE features are not
|
||||
supported. In many cases the absense will not be noticed however
|
||||
performance may be impacted due to a core mergerfs design decision
|
||||
relying on a Linux feature.
|
||||
|
||||
Linux allows individual threads to change credentials whereas [FreeBSD
|
||||
does not](https://wiki.freebsd.org/Per-Thread%20Credentials). As a
|
||||
result mergerfs must use a lock to ensure critical sections which need
|
||||
to change credentials are safeguarded. This will limit throughput on
|
||||
systems where requests to mergerfs come from multiple user identities.
|
||||
With FreeBSD certain Linux functions and FUSE features [are not
|
||||
supported.](../known_issues_bugs.md#freebsd-version) In many cases the
|
||||
absence will not be noticed however more advanced features will not be
|
||||
available.
|
||||
|
||||
|
||||
### Why not support MacOS?
|
||||
|
|
@ -36,7 +30,7 @@ level API.
|
|||
|
||||
Windows, while used for NAS systems more often than MacOS, is still
|
||||
relatively uncommon when compared to Linux. [Drive
|
||||
Pool](../project_comparisons.md#stablebits-drivepool) is a reasonable
|
||||
Pool](../project_comparisons.md#stablebits-drivepool) is a good
|
||||
alternative.
|
||||
|
||||
|
||||
|
|
@ -44,21 +38,31 @@ alternative.
|
|||
|
||||
ext4, btrfs, xfs, f2fs, zfs, nfs, etc.
|
||||
|
||||
On the surface any filesystem should work but there could be issues
|
||||
with non-POSIX compliant filesystems such as vfat, ntfs, cifs, exfat,
|
||||
Most any filesystem should work but there could be issues with
|
||||
non-POSIX compliant filesystems such as vfat, ntfs, cifs, exfat,
|
||||
etc. When directories need to be created or files moved by mergerfs if
|
||||
the filesystem returns errors due to not supporting certain POSIX
|
||||
filesystem features it could result in the core functions failing.
|
||||
|
||||
Since mergerfs is not generally used with non-POSIX filesystems this
|
||||
has not been a problem for users and there are some checks for known
|
||||
edgecases but it is possible some are not accounted for. If use with a
|
||||
edge cases but it is possible some are not accounted for. If use with a
|
||||
filesystem results in issues please [file a
|
||||
ticket](https://github.com/trapexit/mergerfs/issues) with the details.
|
||||
|
||||
|
||||
## Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
|
||||
|
||||
[https://www.snapraid.it](https://www.snapraid.it)
|
||||
|
||||
Yes. They are completely unrelated pieces of software that just happen
|
||||
to work well together.
|
||||
|
||||
|
||||
## Can I use mergerfs without nonraid? nonraid without mergerfs?
|
||||
|
||||
[https://github.com/qvr/nonraid](https://github.com/qvr/nonraid)
|
||||
|
||||
Yes. They are completely unrelated pieces of software that just happen
|
||||
to work well together.
|
||||
|
||||
|
|
@ -80,16 +84,8 @@ pool.
|
|||
|
||||
## Can mergerfs run via Docker, Podman, Kubernetes, etc.
|
||||
|
||||
Yes. With Docker you'll need to include `--cap-add=SYS_ADMIN
|
||||
--device=/dev/fuse --security-opt=apparmor:unconfined` or similar with
|
||||
other container runtimes. You should also be running it as root or
|
||||
given sufficient caps to allow mergerfs to change user and group
|
||||
identity as well as have root like filesystem permissions. This
|
||||
ability is critical to how mergerfs works.
|
||||
|
||||
Also, as mentioned by [hotio](https://hotio.dev/containers/mergerfs),
|
||||
with Docker you should probably be mounting with `bind-propagation`
|
||||
set to `slave`.
|
||||
Yes. [See installation
|
||||
page.](../setup/installation.md##podman-docker-oci-containers)
|
||||
|
||||
|
||||
## How does mergerfs interact with user namespaces?
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
# Recommendations and Warnings
|
||||
|
||||
## What should mergerfs NOT be used for?
|
||||
|
||||
* Situations where you need large amounts of contiguous space beyond
|
||||
that available on any singular device. Such as putting 10GiB file on
|
||||
2 6GiB filesystems.
|
||||
* databases: Even if the database stored data in separate files
|
||||
(mergerfs wouldn't offer much otherwise) the higher latency of the
|
||||
indirection will really harm performance. If it is a lightly used
|
||||
sqlite3 database then it should be fine.
|
||||
* VM images: For the same reasons as databases. VM images are accessed
|
||||
very aggressively and mergerfs will introduce a lot of extra latency.
|
||||
* As replacement for RAID: mergerfs is just for pooling branches. If
|
||||
you need device performance aggregation or high availability you
|
||||
should stick with RAID. However, it is fine to put a filesystem
|
||||
which is on a RAID setup in mergerfs.
|
||||
|
||||
**However, if using [passthrough](../config/passthrough.md) the
|
||||
performance related issues above are less likely to be a concern. Best
|
||||
to do testing for your specific use case.**
|
||||
|
||||
|
||||
## It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?
|
||||
|
||||
[mhddfs](https://github.com/trapexit/mhddfs) manages running as
|
||||
`root` by calling
|
||||
[getuid()](https://github.com/trapexit/mhddfs/blob/cae96e6251dd91e2bdc24800b4a18a74044f6672/src/main.c#L319)
|
||||
and if it returns `0` then it will
|
||||
[chown](http://linux.die.net/man/1/chown) the file. Not only is that a
|
||||
race condition but it doesn't handle other situations. Rather than
|
||||
attempting to simulate POSIX ACL behavior the proper way to manage
|
||||
this is to use [seteuid](http://linux.die.net/man/2/seteuid) and
|
||||
[setegid](http://linux.die.net/man/2/setegid), in effect, becoming the
|
||||
user making the original call, and perform the action as them. This is
|
||||
what mergerfs does and why mergerfs should always run as root.
|
||||
|
||||
In Linux setreuid syscalls apply only to the thread. glibc hides this
|
||||
away by using realtime 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 between
|
||||
filesystems).
|
||||
|
||||
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 allowing 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 assuming there are
|
||||
few users.
|
||||
|
|
@ -146,7 +146,7 @@ You can remove the reserve by running: `tune2fs -m 0 <device>`
|
|||
|
||||
## I notice massive slowdowns of writes when enabling cache.files.
|
||||
|
||||
When file caching is enabled in any form (`cache.files!=off`) it will
|
||||
When file caching is enabled in any form (`cache.files!=off`) it may
|
||||
issue `getxattr` requests for `security.capability` prior to _every
|
||||
single write_. This will usually result in performance degradation,
|
||||
especially when using a network filesystem (such as NFS or SMB.)
|
||||
|
|
@ -178,20 +178,21 @@ disadvantages to each one.
|
|||
|
||||
A FUSE based solution has all the downsides of FUSE:
|
||||
|
||||
- Higher IO latency due to the trips in and out of kernel space
|
||||
- Higher general overhead due to trips in and out of kernel space
|
||||
- Double caching when using page caching
|
||||
- Misc limitations due to FUSE's design
|
||||
* Higher IO latency due to the trips in and out of kernel space
|
||||
(though now minimized with passthrough IO)
|
||||
* Higher general overhead due to trips in and out of kernel space
|
||||
* Double caching when using page caching
|
||||
* Misc limitations due to FUSE's design
|
||||
|
||||
But FUSE also has a lot of upsides:
|
||||
|
||||
- Easier to offer a cross platform solution
|
||||
- Easier forward and backward compatibility
|
||||
- Easier updates for users
|
||||
- Easier and faster release cadence
|
||||
- Allows more flexibility in design and features
|
||||
- Overall easier to write, secure, and maintain
|
||||
- Much lower barrier to entry (getting code into the kernel takes a
|
||||
* Easier to offer a cross platform solution
|
||||
* Easier forward and backward compatibility
|
||||
* Easier updates for users
|
||||
* Easier and faster release cadence
|
||||
* Allows more flexibility in design and features
|
||||
* Overall easier to write, secure, and maintain
|
||||
* Much lower barrier to entry (getting code into the kernel takes a
|
||||
lot of time and effort initially)
|
||||
|
||||
|
||||
|
|
@ -220,45 +221,56 @@ removed to simplify the codebase.
|
|||
|
||||
## How does mergerfs handle credentials?
|
||||
|
||||
|
||||
mergerfs is a multithreaded application in order to handle requests
|
||||
from the kernel concurrently. Each FUSE message has a header with
|
||||
certain details about the request including the process ID (pid) of
|
||||
the requesting application, the process' effective user id (uid), and
|
||||
group id (gid). To ensure proper POSIX filesystem behavior and
|
||||
security mergerfs must change its identity to match that of the
|
||||
requester when performing the certain functions on the underlying
|
||||
filesystem. As required by standards most Unix/POSIX based systems a
|
||||
process and all its threads are under the same uid and gid. However,
|
||||
on Linux each thread **may** have its own credentials. This allows
|
||||
mergerfs to be multithreaded and for each thread to change to the
|
||||
credentials as required by the incoming message it is
|
||||
handling. However, currently on FreeBSD this is not possible (though
|
||||
there has been
|
||||
[discussions](https://wiki.freebsd.org/Per-Thread%20Credentials)) and
|
||||
as such must change the credentials of the whole application when
|
||||
actioning messages. mergerfs does optimize this behavior by only
|
||||
changing credentials and locking the thread to do so if the process is
|
||||
currently not the same as what is necessary by the incoming
|
||||
request. As a result of this design FreeBSD may experience more
|
||||
contention and therefore lower performance than Linux.
|
||||
group id (gid).
|
||||
|
||||
Additionally, mergerfs [utilizes a cache for supplemental
|
||||
groups](../known_issues_bugs.md#supplemental-user-groups) due the the
|
||||
high cost of querying that information.
|
||||
FUSE and the kernel have two ways of managing permissions. A kernel
|
||||
side `default_permissions` option and leaving it to the FUSE
|
||||
server. When default permissions is enabled the kernel will do the
|
||||
entitlement checks and only allow requests through which should be
|
||||
allowed according to normal POSIX permissions. When not enabled it is
|
||||
the responsibility of the FUSE server, in this case mergerfs, to do
|
||||
whatever is necessary to manage entitlements.
|
||||
|
||||
Prior to mergerfs v2.42.0 it would enable `default_permissions` but
|
||||
also leveraged the uid and gid available in each FUSE request. The
|
||||
thread actioning the request would change its credentials to match
|
||||
those of the requesting application to ensure permissions were
|
||||
properly handled as well as dealing with some quirks of non-POSIX
|
||||
compatible filesystems. However, this strategy had two major
|
||||
issues. First, it was not compatible with the new `allow-idmap`
|
||||
feature of FUSE which allows a filesystem to advertise it can be used
|
||||
with id mapping which is often used with containers. Secondly, it
|
||||
caused permission issues when the kernel would say something was
|
||||
allowed but due to the way mergerfs was changing creds would
|
||||
fail. This mostly came in the form of `chroot`ed setups like used in
|
||||
containers. Neither of these tended to impact casual users but did
|
||||
break some niche or power user use cases. Since the casual user won't
|
||||
notice the subtle changes and it would enable new use cases it was
|
||||
decided that the credential handling would change.
|
||||
|
||||
As of v2.42.0 mergerfs now runs as root more generally. Either
|
||||
changing credentials when creating files (Linux) or chown'ing them
|
||||
after creation (FreeBSD).
|
||||
|
||||
As a result of this change it is now necessary for the FUSE
|
||||
`default_permissions` feature be used for proper entitlements
|
||||
management. mergerfs does allow it to be disabled but it should only
|
||||
be done so for debugging purposes.
|
||||
|
||||
|
||||
## Does mergerfs support idmap?
|
||||
|
||||
Yes. At least in so far as it's been enabled now the FUSE itself
|
||||
allows a filesystem to indicate it is allowed.
|
||||
Yes, by setting [allow-idmap=true](../config/options.md) (which is the
|
||||
default.)
|
||||
|
||||
Requires that
|
||||
[kernel-permissions-check](../config/kernel-permissions-check.md) be
|
||||
enabled (the default.)
|
||||
|
||||
If there are any usage issues contact the [author](../support.md).
|
||||
|
||||
|
||||
## What happens if a branch filesystem blocks?
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,26 @@
|
|||
# Usage and Functionality
|
||||
|
||||
## What should mergerfs NOT be used for?
|
||||
|
||||
* Situations where you need large amounts of contiguous space beyond
|
||||
that available on any singular device. Such as putting 10GiB a file
|
||||
on 2 6GiB filesystems.
|
||||
* databases: Even if the database stored data in separate files
|
||||
(mergerfs wouldn't offer much otherwise) the higher latency of the
|
||||
indirection will harm performance. Though if it is a sqlite3
|
||||
database then its likely fine.
|
||||
* VM images: For the same reasons as databases. VM images are accessed
|
||||
very aggressively and mergerfs will introduce a lot of extra latency.
|
||||
* As replacement for RAID: mergerfs is just for pooling branches. If
|
||||
you need device performance aggregation or high availability you
|
||||
should stick with RAID. However, it is fine to put a filesystem
|
||||
which is on a RAID setup in mergerfs.
|
||||
|
||||
**However, if using [passthrough](../config/passthrough.md) the
|
||||
performance related issues above are less likely to be a concern. Best
|
||||
to do testing for your specific use case.**
|
||||
|
||||
|
||||
## What happens when file paths overlap?
|
||||
|
||||
It depends on the situation and the configuration of mergerfs. The
|
||||
|
|
|
|||
|
|
@ -4,10 +4,17 @@
|
|||
|
||||
### FreeBSD version
|
||||
|
||||
* FreeBSD doesn't have per thread credentials meaning threads must
|
||||
block to change credentials as required by numerous filesystem
|
||||
functions. This impacts performance.
|
||||
* FreeBSD's FUSE implementation is lacking many features of Linux.
|
||||
* [https://wiki.freebsd.org/FUSEFS](https://wiki.freebsd.org/FUSEFS)
|
||||
* FreeBSD does not have per thread credentials nor Linux capabilities
|
||||
like abilities meaning it runs as root and has to create files as
|
||||
root then chown them rather than changing credentials and creating
|
||||
the file as the uid:gid.
|
||||
* FreeBSD does not have `getdents` like APIs for reading
|
||||
directories. As a result it uses traditional `readdir`.
|
||||
* FreeBSD's FUSE implementation is lacking many features of the Linux
|
||||
version.
|
||||
* runtime interface
|
||||
* ioctl support
|
||||
* IO passthrough
|
||||
* statx
|
||||
* lazy umount
|
||||
|
|
@ -16,26 +23,10 @@
|
|||
* kernel symlink caching
|
||||
* kernel readdir caching
|
||||
* writeback caching
|
||||
* idmap
|
||||
* ...
|
||||
|
||||
|
||||
### Supplemental user groups
|
||||
|
||||
#### Supplemental group caching
|
||||
|
||||
Due to the high cost of querying supplemental groups it is necessary
|
||||
for mergerfs to cache the list. As a result if supplemental groups are
|
||||
changed while mergerfs is running the cache may become stale. This
|
||||
generally isn't a problem as these groups tend not to be changed
|
||||
often. However, the cache has a default expiry of 1 hour and after 12
|
||||
hours of no usage the cache entry will be removed altogether.
|
||||
|
||||
See [gidcache.expire-timeout and
|
||||
gid-cache.remove-timeout](config/options.md) and the [runtime
|
||||
interface](runtime_interface.md#commands) for forcing expiry and clearing of
|
||||
the cache.
|
||||
|
||||
|
||||
#### Host vs Container identity
|
||||
|
||||
While not a bug some users have found when using containers that
|
||||
|
|
@ -130,7 +121,7 @@ Details on enabling `mmap` can be found at:
|
|||
That said it is recommended that config and runtime files be stored on
|
||||
SSDs on a regular filesystem for performance reasons. See [What should
|
||||
mergerfs NOT be used
|
||||
for?](faq/recommendations_and_warnings.md#what-should-mergerfs-not-be-used-for). Though
|
||||
for?](faq/usage_and_functionality.md#what-should-mergerfs-not-be-used-for). Though
|
||||
with [passthrough.io](config/passthrough.md) enabled that is less
|
||||
of a concern.
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
* 1MB+ pre reader thread + inflight processing for messages
|
||||
depending on [fuse-msg-size](config/fuse-msg-size.md)
|
||||
* buffers allocated temporarily for reading directories
|
||||
* [gidcache](faq/technical_behavior_and_limitations.md#how-does-mergerfs-handle-credentials)
|
||||
* FUSE nodes
|
||||
* noforget forgotten nodes
|
||||
|
||||
|
|
|
|||
|
|
@ -143,8 +143,6 @@ allows a value.
|
|||
| user.mergerfs.cmd.gc | (empty) |Trigger a thorough garbage collection of certain pools of resources. The xattr value is not used. |
|
||||
| user.mergerfs.cmd.gc1 | (empty) | Trigger a simple garbage collection of certain pools of resources. This is also done on a timer. |
|
||||
| user.mergerfs.cmd.invalidate-all-nodes | (empty) | Attempts to invalidate FUSE file nodes. Primarily used for debugging. |
|
||||
| user.mergerfs.cmd.invalidate-gid-cache | (empty) | Invalidates all entries in gid cache. Primarily used for debugging but can also be useful in cases where gid supplemental groups change. |
|
||||
| user.mergerfs.cmd.clear-gid-cache | (empty) | Clears all entries in the gid cache. Primarily used for debugging but can also be useful in cases where gid supplemental groups change. |
|
||||
|
||||
```
|
||||
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.cmd.gc /mnt/mergerfs/.mergerfs
|
||||
|
|
|
|||
|
|
@ -113,7 +113,6 @@ nav:
|
|||
- faq/usage_and_functionality.md
|
||||
- faq/reliability_and_scalability.md
|
||||
- faq/compatibility_and_integration.md
|
||||
- faq/recommendations_and_warnings.md
|
||||
- faq/technical_behavior_and_limitations.md
|
||||
- faq/have_you_considered.md
|
||||
- faq/limit_drive_spinup.md
|
||||
|
|
|
|||
104
src/caps.cpp
Normal file
104
src/caps.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#include "caps.hpp"
|
||||
|
||||
#if defined __linux__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <grp.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/securebits.h>
|
||||
|
||||
|
||||
static
|
||||
int
|
||||
capset(cap_user_header_t header_,
|
||||
const cap_user_data_t data_)
|
||||
{
|
||||
return ::syscall(SYS_capset,header_,data_);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
capget(cap_user_header_t header_,
|
||||
cap_user_data_t data_)
|
||||
{
|
||||
return ::syscall(SYS_capget,header_,data_);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
capset(int cap_bit_)
|
||||
{
|
||||
int rv;
|
||||
struct __user_cap_header_struct header;
|
||||
struct __user_cap_data_struct data[2];
|
||||
|
||||
header.version = _LINUX_CAPABILITY_VERSION_3;
|
||||
header.pid = 0;
|
||||
|
||||
rv = capget(&header,data);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
int word = cap_bit_ / 32;
|
||||
int bit = cap_bit_ % 32;
|
||||
|
||||
data[word].permitted |= (1 << bit);
|
||||
data[word].effective |= (1 << bit);
|
||||
data[word].inheritable |= (1 << bit);
|
||||
|
||||
rv = capset(&header,data);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
caps::setup()
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = capset(CAP_DAC_OVERRIDE);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
rv = capset(CAP_DAC_READ_SEARCH);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
rv = capset(CAP_FOWNER);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
rv = capset(CAP_CHOWN);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
rv = capset(CAP_SETUID);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
rv = capset(CAP_SETGID);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
rv = prctl(PR_SET_SECUREBITS,
|
||||
SECBIT_KEEP_CAPS | SECBIT_NO_SETUID_FIXUP);
|
||||
if(rv < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#include <errno.h>
|
||||
|
||||
int
|
||||
caps::setup()
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
6
src/caps.hpp
Normal file
6
src/caps.hpp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace caps
|
||||
{
|
||||
int setup();
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ Config::CfgConfigFile::to_string() const
|
|||
|
||||
Config::Config()
|
||||
:
|
||||
allow_idmap(false),
|
||||
allow_idmap(true),
|
||||
async_read(true),
|
||||
branches(),
|
||||
branches_mount_timeout(0),
|
||||
|
|
@ -102,8 +102,6 @@ Config::Config()
|
|||
fsname(),
|
||||
func(),
|
||||
fuse_msg_size("1M"),
|
||||
gid_cache_expire_timeout(60 * 60),
|
||||
gid_cache_remove_timeout(60 * 60 * 12),
|
||||
handle_killpriv(true),
|
||||
handle_killpriv_v2(true),
|
||||
ignorepponrename(false),
|
||||
|
|
@ -250,8 +248,8 @@ Config::Config()
|
|||
_map["func.utimens"] = &func.utimens;
|
||||
_map["fuse-msg-size"] = &fuse_msg_size;
|
||||
_map["gid"] = &_gid;
|
||||
_map["gid-cache.expire-timeout"] = &gid_cache_expire_timeout;
|
||||
_map["gid-cache.remove-timeout"] = &gid_cache_remove_timeout;
|
||||
_map["gid-cache.expire-timeout"] = &_dummy;
|
||||
_map["gid-cache.remove-timeout"] = &_dummy;
|
||||
_map["handle-killpriv"] = &handle_killpriv;
|
||||
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
|
||||
_map["hard-remove"] = &_dummy;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include "config_dummy.hpp"
|
||||
#include "config_flushonclose.hpp"
|
||||
#include "config_follow_symlinks.hpp"
|
||||
#include "config_gidcache.hpp"
|
||||
#include "config_inodecalc.hpp"
|
||||
#include "config_link_exdev.hpp"
|
||||
#include "config_log_metrics.hpp"
|
||||
|
|
@ -133,8 +132,6 @@ public:
|
|||
ConfigSTR fsname;
|
||||
Funcs func;
|
||||
ConfigPageSize fuse_msg_size;
|
||||
GIDCacheExpireTimeout gid_cache_expire_timeout;
|
||||
GIDCacheRemoveTimeout gid_cache_remove_timeout;
|
||||
ConfigBOOL handle_killpriv;
|
||||
ConfigBOOL handle_killpriv_v2;
|
||||
ConfigBOOL ignorepponrename;
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
#include "config_gidcache.hpp"
|
||||
|
||||
#include "gidcache.hpp"
|
||||
|
||||
#include "to_string.hpp"
|
||||
#include "from_string.hpp"
|
||||
|
||||
|
||||
GIDCacheExpireTimeout::GIDCacheExpireTimeout(const int i_)
|
||||
{
|
||||
GIDCache::expire_timeout = i_;
|
||||
}
|
||||
|
||||
GIDCacheExpireTimeout::GIDCacheExpireTimeout(const std::string &s_)
|
||||
{
|
||||
from_string(s_);
|
||||
}
|
||||
|
||||
std::string
|
||||
GIDCacheExpireTimeout::to_string(void) const
|
||||
{
|
||||
return str::to(GIDCache::expire_timeout);
|
||||
}
|
||||
|
||||
int
|
||||
GIDCacheExpireTimeout::from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = str::from(s_,&GIDCache::expire_timeout);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
GIDCacheRemoveTimeout::GIDCacheRemoveTimeout(const int i_)
|
||||
{
|
||||
GIDCache::remove_timeout = i_;
|
||||
}
|
||||
|
||||
|
||||
GIDCacheRemoveTimeout::GIDCacheRemoveTimeout(const std::string &s_)
|
||||
{
|
||||
from_string(s_);
|
||||
}
|
||||
|
||||
std::string
|
||||
GIDCacheRemoveTimeout::to_string(void) const
|
||||
{
|
||||
return str::to(GIDCache::remove_timeout);
|
||||
}
|
||||
|
||||
int
|
||||
GIDCacheRemoveTimeout::from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = str::from(s_,&GIDCache::remove_timeout);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
class GIDCacheExpireTimeout : public ToFromString
|
||||
{
|
||||
public:
|
||||
GIDCacheExpireTimeout(const int i = 60 * 60);
|
||||
GIDCacheExpireTimeout(const std::string &);
|
||||
|
||||
public:
|
||||
std::string to_string(void) const final;
|
||||
int from_string(const std::string_view) final;
|
||||
};
|
||||
|
||||
class GIDCacheRemoveTimeout : public ToFromString
|
||||
{
|
||||
public:
|
||||
GIDCacheRemoveTimeout(const int = 60 * 60 * 12);
|
||||
GIDCacheRemoveTimeout(const std::string &);
|
||||
|
||||
public:
|
||||
std::string to_string(void) const final;
|
||||
int from_string(const std::string_view) final;
|
||||
};
|
||||
|
|
@ -31,7 +31,7 @@ using std::string;
|
|||
static
|
||||
int
|
||||
_get_fs_ioc_flags(const int fd,
|
||||
int &flags)
|
||||
int &flags)
|
||||
{
|
||||
int rv;
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ _get_fs_ioc_flags(const int fd,
|
|||
static
|
||||
int
|
||||
_get_fs_ioc_flags(const string &file,
|
||||
int &flags)
|
||||
int &flags)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,16 @@
|
|||
#include "fs_xattr.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include "fs_close.hpp"
|
||||
#include "fs_fstat.hpp"
|
||||
#include "fs_mkdirat.hpp"
|
||||
#include "fs_openat.hpp"
|
||||
#include "fs_fchown.hpp"
|
||||
#include "fs_fchmod.hpp"
|
||||
#include "fs_futimens.hpp"
|
||||
|
||||
#include "scope_guard.hpp"
|
||||
|
||||
|
||||
static
|
||||
bool
|
||||
|
|
@ -107,21 +117,62 @@ fs::clonepath(const fs::path &srcpath_,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// WORK IN PROGRESS
|
||||
static
|
||||
int
|
||||
fs::clonepath_as_root(const fs::path &srcpath_,
|
||||
const fs::path &dstpath_,
|
||||
const fs::path &relpath_,
|
||||
const bool return_metadata_errors_)
|
||||
_clonepath2(const int srcfd_,
|
||||
const int dstfd_,
|
||||
const fs::path &dirname_,
|
||||
const bool return_metadata_errors_)
|
||||
{
|
||||
if(relpath_.empty())
|
||||
return 0;
|
||||
if(srcpath_ == dstpath_)
|
||||
int rv;
|
||||
int srcdirfd;
|
||||
int dstdirfd;
|
||||
struct stat st;
|
||||
|
||||
if(dirname_.empty())
|
||||
return 0;
|
||||
|
||||
const ugid::SetRootGuard ugid_guard;
|
||||
rv = fs::mkdirat(dstfd_,dirname_,0);
|
||||
if(rv < 0)
|
||||
return ((rv == -EEXIST) ? 0 : rv);
|
||||
|
||||
return fs::clonepath(srcpath_,
|
||||
dstpath_,
|
||||
relpath_,
|
||||
return_metadata_errors_);
|
||||
srcdirfd = fs::openat(srcfd_,dirname_,O_DIRECTORY);
|
||||
if(srcdirfd < 0)
|
||||
return srcdirfd;
|
||||
DEFER { fs::close(srcdirfd); };
|
||||
|
||||
dstdirfd = fs::openat(dstfd_,dirname_,O_DIRECTORY);
|
||||
if(dstdirfd < 0)
|
||||
return dstdirfd;
|
||||
DEFER { fs::close(dstdirfd); };
|
||||
|
||||
rv = fs::attr::copy(srcdirfd,dstdirfd,FS_ATTR_CLEAR_IMMUTABLE);
|
||||
if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv))
|
||||
return rv;
|
||||
|
||||
rv = fs::xattr::copy(srcdirfd,dstdirfd);
|
||||
if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv))
|
||||
return rv;
|
||||
|
||||
rv = fs::fstat(srcdirfd,&st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
if(!S_ISDIR(st.st_mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
rv = fs::fchown_check_on_error(dstdirfd,st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
rv = fs::fchmod_check_on_error(dstdirfd,st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
rv = fs::futimens(dstdirfd,st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,4 @@ namespace fs
|
|||
const fs::path &dstpath,
|
||||
const fs::path &relpath,
|
||||
const bool return_metadata_errors = false);
|
||||
|
||||
int clonepath_as_root(const fs::path &srcpath,
|
||||
const fs::path &dstpath,
|
||||
const fs::path &relpath,
|
||||
const bool return_metadata_errors = false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,8 +49,6 @@ namespace fs
|
|||
bool
|
||||
is_rofs(const fs::path &path_)
|
||||
{
|
||||
ugid::SetRootGuard const ugid;
|
||||
|
||||
int fd;
|
||||
std::string tmp_filepath;
|
||||
|
||||
|
|
|
|||
49
src/fs_mkdir_as.hpp
Normal file
49
src/fs_mkdir_as.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "fs_mkdir.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
|
||||
#if defined __linux__
|
||||
namespace fs
|
||||
{
|
||||
template<typename T>
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdir_as(const ugid_t ugid_,
|
||||
const T &path_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
const ugid::SetGuard _(ugid_);
|
||||
|
||||
return fs::mkdir(path_,mode_);
|
||||
}
|
||||
}
|
||||
#elif defined __FreeBSD__
|
||||
#include "fs_lchown.hpp"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
template<typename T>
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdir_as(const ugid_t ugid_,
|
||||
const T &path_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = fs::mkdir(path_,mode_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
fs::lchown(path_,ugid_.uid,ugid_.gid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error "Not Supported"
|
||||
#endif
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fs_mkdir.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
template<typename T>
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdir_as_root(const T &path_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
const ugid::SetRootGuard guard;
|
||||
|
||||
return fs::mkdir(path_,mode_);
|
||||
}
|
||||
}
|
||||
48
src/fs_mkdirat.hpp
Normal file
48
src/fs_mkdirat.hpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "to_neg_errno.hpp"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdirat(const int dirfd_,
|
||||
const char *pathname_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ::mkdirat(dirfd_,pathname_,mode_);
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdirat(const int dirfd_,
|
||||
const std::string &pathname_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
return fs::mkdirat(dirfd_,
|
||||
pathname_.c_str(),
|
||||
mode_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdirat(const int dirfd_,
|
||||
const fs::path &pathname_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
return fs::mkdirat(dirfd_,
|
||||
pathname_.c_str(),
|
||||
mode_);
|
||||
}
|
||||
}
|
||||
49
src/fs_mknod_as.hpp
Normal file
49
src/fs_mknod_as.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "fs_mknod.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
|
||||
#if defined __linux__
|
||||
namespace fs
|
||||
{
|
||||
template<typename T>
|
||||
static
|
||||
inline
|
||||
int
|
||||
mknod_as(const ugid_t ugid_,
|
||||
const T &path_,
|
||||
const mode_t mode_,
|
||||
const dev_t dev_)
|
||||
{
|
||||
const ugid::SetGuard _(ugid_);
|
||||
|
||||
return fs::mknod(path_,mode_,dev_);
|
||||
}
|
||||
}
|
||||
#elif defined __FreeBSD__
|
||||
#include "fs_lchown.hpp"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
template<typename T>
|
||||
static
|
||||
inline
|
||||
int
|
||||
mknod_as(const ugid_t ugid_,
|
||||
const T &path_,
|
||||
const mode_t mode_,
|
||||
const dev_t dev_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = fs::mknod(path_,mode_,dev_);
|
||||
|
||||
fs::lchown(path_,ugid_.uid,ugid_.gid);
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error "Not Supported!"
|
||||
#endif
|
||||
|
|
@ -137,8 +137,6 @@ fs::movefile_and_open_as_root(const Policy::Create &policy_,
|
|||
const fs::path &fusepath_,
|
||||
const int origfd_)
|
||||
{
|
||||
const ugid::Set ugid(0,0);
|
||||
|
||||
return fs::movefile_and_open(policy_,
|
||||
branches_,
|
||||
branchpath_,
|
||||
|
|
|
|||
56
src/fs_open_as.hpp
Normal file
56
src/fs_open_as.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "fs_open.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
// Linux can set ugid because it allows for credentials per
|
||||
// thread. FreeBSD however does not and must create as root then
|
||||
// chown. Another option for both platforms would be to create a temp
|
||||
// file, chown, then rename to target. Unfortunately, depending on if
|
||||
// the target filesystem is POSIX compliant or uses POSIX / extended
|
||||
// ACLs it is not possible to know what is best.
|
||||
|
||||
|
||||
#if defined __linux__
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
int
|
||||
open_as(const ugid_t ugid_,
|
||||
const fs::path &path_,
|
||||
const int flags_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
const ugid::SetGuard _(ugid_);
|
||||
|
||||
return fs::open(path_,flags_,mode_);
|
||||
}
|
||||
}
|
||||
#elif defined __FreeBSD__
|
||||
#include "fs_fchown.hpp"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
int
|
||||
open_as(const ugid_t ugid_,
|
||||
const fs::path &path_,
|
||||
const int flags_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = fs::open(path_,flags_,mode_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
fs::fchown(rv,ugid_.uid,ugid_.gid);
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error "Not Supported!"
|
||||
#endif
|
||||
49
src/fs_symlink_as.hpp
Normal file
49
src/fs_symlink_as.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include "fs_symlink.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
|
||||
#if defined __linux__
|
||||
namespace fs
|
||||
{
|
||||
template<typename T>
|
||||
static
|
||||
inline
|
||||
int
|
||||
symlink_as(const ugid_t ugid_,
|
||||
const char *target_,
|
||||
const T &linkpath_)
|
||||
{
|
||||
const ugid::SetGuard _(ugid_);
|
||||
|
||||
return fs::symlink(target_,linkpath_);
|
||||
}
|
||||
}
|
||||
#elif defined __FreeBSD__
|
||||
#include "fs_lchown.hpp"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
template<typename T>
|
||||
static
|
||||
inline
|
||||
int
|
||||
symlink_as(const ugid_t ugid_,
|
||||
const char *target_,
|
||||
const T &linkpath_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = fs::symlink(target_,linkpath_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
fs::lchown(linkpath_,ugid_.uid,ugid_.gid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error "Not Supported!"
|
||||
#endif
|
||||
|
|
@ -54,8 +54,7 @@ FUSE::access(const fuse_req_ctx_t *ctx_,
|
|||
const char *fusepath_,
|
||||
int mask_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_->uid,ctx_->gid);
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_access(cfg.func.access.policy,
|
||||
cfg.branches,
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ FUSE::chmod(const fuse_req_ctx_t *ctx_,
|
|||
mode_t mode_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_->uid,ctx_->gid);
|
||||
|
||||
return ::_chmod(fusepath,mode_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,8 +99,7 @@ FUSE::chown(const fuse_req_ctx_t *ctx_,
|
|||
uid_t uid_,
|
||||
gid_t gid_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_->uid,ctx_->gid);
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_chown(cfg.func.chown.policy,
|
||||
cfg.func.getattr.policy,
|
||||
|
|
|
|||
|
|
@ -19,11 +19,15 @@
|
|||
#include "state.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
#include "fs_readlink.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fileinfo.hpp"
|
||||
#include "fs_acl.hpp"
|
||||
#include "fs_close.hpp"
|
||||
#include "fs_clonepath.hpp"
|
||||
#include "fs_open.hpp"
|
||||
#include "fs_open_as.hpp"
|
||||
#include "fs_openat.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fuse_passthrough.hpp"
|
||||
#include "procfs.hpp"
|
||||
|
|
@ -132,7 +136,8 @@ _config_to_ffi_flags(Config &cfg_,
|
|||
|
||||
static
|
||||
int
|
||||
_create_core(const fs::path &fullpath_,
|
||||
_create_core(const ugid_t ugid_,
|
||||
const fs::path &fullpath_,
|
||||
mode_t mode_,
|
||||
const mode_t umask_,
|
||||
const int flags_)
|
||||
|
|
@ -140,12 +145,13 @@ _create_core(const fs::path &fullpath_,
|
|||
if(!fs::acl::dir_has_defaults(fullpath_))
|
||||
mode_ &= ~umask_;
|
||||
|
||||
return fs::open(fullpath_,flags_,mode_);
|
||||
return fs::open_as(ugid_,fullpath_,flags_,mode_);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_create_core(const Branch *branch_,
|
||||
_create_core(const ugid_t ugid_,
|
||||
const Branch *branch_,
|
||||
const fs::path &fusepath_,
|
||||
fuse_file_info_t *ffi_,
|
||||
const mode_t mode_,
|
||||
|
|
@ -157,7 +163,7 @@ _create_core(const Branch *branch_,
|
|||
|
||||
fullpath = branch_->path / fusepath_;
|
||||
|
||||
rv = ::_create_core(fullpath,mode_,umask_,ffi_->flags);
|
||||
rv = ::_create_core(ugid_,fullpath,mode_,umask_,ffi_->flags);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
|
|
@ -170,7 +176,8 @@ _create_core(const Branch *branch_,
|
|||
|
||||
static
|
||||
int
|
||||
_create(const Policy::Search &searchFunc_,
|
||||
_create(const ugid_t ugid_,
|
||||
const Policy::Search &searchFunc_,
|
||||
const Policy::Create &createFunc_,
|
||||
const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
|
|
@ -194,13 +201,14 @@ _create(const Policy::Search &searchFunc_,
|
|||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
rv = fs::clonepath_as_root(existingpaths[0]->path,
|
||||
createpaths[0]->path,
|
||||
fusedirpath);
|
||||
rv = fs::clonepath(existingpaths[0]->path,
|
||||
createpaths[0]->path,
|
||||
fusedirpath);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return ::_create_core(createpaths[0],
|
||||
return ::_create_core(ugid_,
|
||||
createpaths[0],
|
||||
fusepath_,
|
||||
ffi_,
|
||||
mode_,
|
||||
|
|
@ -225,7 +233,6 @@ _create_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
|||
{
|
||||
int rv;
|
||||
FileInfo *fi;
|
||||
const ugid::Set ugid(ctx_->uid,ctx_->gid);
|
||||
|
||||
::_config_to_ffi_flags(cfg,ctx_->pid,ffi_);
|
||||
if(cfg.cache_writeback)
|
||||
|
|
@ -233,7 +240,8 @@ _create_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
|||
ffi_->noflush = !::_calculate_flush(cfg.flushonclose,
|
||||
ffi_->flags);
|
||||
|
||||
rv = ::_create(cfg.func.getattr.policy,
|
||||
rv = ::_create(ctx_,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.func.create.policy,
|
||||
cfg.branches,
|
||||
fusepath_,
|
||||
|
|
@ -243,7 +251,8 @@ _create_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
|||
if(rv == -EROFS)
|
||||
{
|
||||
cfg.branches.find_and_set_mode_ro();
|
||||
rv = ::_create(cfg.func.getattr.policy,
|
||||
rv = ::_create(ctx_,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.func.create.policy,
|
||||
cfg.branches,
|
||||
fusepath_,
|
||||
|
|
|
|||
|
|
@ -213,8 +213,6 @@ FUSE::getattr(const fuse_req_ctx_t *ctx_,
|
|||
struct stat *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
return FUSE::getattr(fusepath_,st_,timeout_);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -193,8 +193,6 @@ FUSE::getxattr(const fuse_req_ctx_t *ctx_,
|
|||
if(cfg.xattr.to_int())
|
||||
return -cfg.xattr.to_int();
|
||||
|
||||
const ugid::Set ugid(ctx_->uid,ctx_->gid);
|
||||
|
||||
return ::_getxattr(cfg.func.getxattr.policy,
|
||||
cfg.branches,
|
||||
fusepath,
|
||||
|
|
|
|||
|
|
@ -187,7 +187,6 @@ void *
|
|||
FUSE::init(fuse_conn_info *conn_)
|
||||
{
|
||||
procfs::init();
|
||||
ugid::init();
|
||||
cfg.readdir.initialize();
|
||||
|
||||
::_want_if_capable(conn_,FUSE_CAP_ASYNC_DIO);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
#include "fs_ioctl.hpp"
|
||||
#include "fs_open.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "gidcache.hpp"
|
||||
#include "mergerfs_ioctl.hpp"
|
||||
#include "str.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
|
@ -113,8 +112,7 @@ _ioctl_file(const fuse_req_ctx_t *ctx_,
|
|||
void *data_,
|
||||
uint32_t *out_bufsz_)
|
||||
{
|
||||
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
|
||||
const ugid::Set ugid(ctx_);
|
||||
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
|
||||
|
||||
return ::_ioctl(fi->fd,cmd_,data_,out_bufsz_);
|
||||
}
|
||||
|
|
@ -162,8 +160,7 @@ _ioctl_dir(const fuse_req_ctx_t *ctx_,
|
|||
void *data_,
|
||||
uint32_t *out_bufsz_)
|
||||
{
|
||||
DirInfo *di = DirInfo::from_fh(ffi_->fh);
|
||||
const ugid::Set ugid(ctx_);
|
||||
DirInfo *di = DirInfo::from_fh(ffi_->fh);
|
||||
|
||||
return ::_ioctl_dir_base(cfg.func.open.policy,
|
||||
cfg.branches,
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ _link_create_path_loop(const std::vector<Branch*> &oldbranches_,
|
|||
rv = fs::link(oldfullpath,newfullpath);
|
||||
if(rv == -ENOENT)
|
||||
{
|
||||
rv = fs::clonepath_as_root(newbranch_->path,oldbranch->path,newfusedirpath_);
|
||||
rv = fs::clonepath(newbranch_->path,
|
||||
oldbranch->path,
|
||||
newfusedirpath_);
|
||||
if(rv == 0)
|
||||
rv = fs::link(oldfullpath,newfullpath);
|
||||
}
|
||||
|
|
@ -328,9 +330,8 @@ FUSE::link(const fuse_req_ctx_t *ctx_,
|
|||
fuse_timeouts_t *timeouts_)
|
||||
{
|
||||
int rv;
|
||||
const fs::path oldpath{oldpath_};
|
||||
const fs::path newpath{newpath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
const fs::path oldpath{oldpath_};
|
||||
const fs::path newpath{newpath_};
|
||||
|
||||
rv = ::_link(ctx_,oldpath,newpath,st_,timeouts_);
|
||||
if(rv == -EXDEV)
|
||||
|
|
|
|||
|
|
@ -143,8 +143,6 @@ FUSE::listxattr(const fuse_req_ctx_t *ctx_,
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
return ::_listxattr(cfg.func.listxattr.policy,
|
||||
cfg.branches,
|
||||
fusepath,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include "error.hpp"
|
||||
#include "fs_acl.hpp"
|
||||
#include "fs_clonepath.hpp"
|
||||
#include "fs_mkdir.hpp"
|
||||
#include "fs_mkdir_as.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "policy.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
|
@ -33,19 +33,21 @@
|
|||
|
||||
static
|
||||
int
|
||||
_mkdir_core(const fs::path &fullpath_,
|
||||
_mkdir_core(const ugid_t ugid_,
|
||||
const fs::path &fullpath_,
|
||||
mode_t mode_,
|
||||
const mode_t umask_)
|
||||
{
|
||||
if(!fs::acl::dir_has_defaults(fullpath_))
|
||||
mode_ &= ~umask_;
|
||||
|
||||
return fs::mkdir(fullpath_,mode_);
|
||||
return fs::mkdir_as(ugid_,fullpath_,mode_);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_mkdir_loop_core(const fs::path &createpath_,
|
||||
_mkdir_loop_core(const ugid_t ugid_,
|
||||
const fs::path &createpath_,
|
||||
const fs::path &fusepath_,
|
||||
const mode_t mode_,
|
||||
const mode_t umask_)
|
||||
|
|
@ -55,14 +57,15 @@ _mkdir_loop_core(const fs::path &createpath_,
|
|||
|
||||
fullpath = createpath_ / fusepath_;
|
||||
|
||||
rv = ::_mkdir_core(fullpath,mode_,umask_);
|
||||
rv = ::_mkdir_core(ugid_,fullpath,mode_,umask_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_mkdir_loop(const Branch *existingbranch_,
|
||||
_mkdir_loop(const ugid_t ugid_,
|
||||
const Branch *existingbranch_,
|
||||
const std::vector<Branch*> &createbranches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::path &fusedirpath_,
|
||||
|
|
@ -74,16 +77,17 @@ _mkdir_loop(const Branch *existingbranch_,
|
|||
|
||||
for(const auto &createbranch : createbranches_)
|
||||
{
|
||||
rv = fs::clonepath_as_root(existingbranch_->path,
|
||||
createbranch->path,
|
||||
fusedirpath_);
|
||||
rv = fs::clonepath(existingbranch_->path,
|
||||
createbranch->path,
|
||||
fusedirpath_);
|
||||
if(rv < 0)
|
||||
{
|
||||
err = rv;
|
||||
continue;
|
||||
}
|
||||
|
||||
err = ::_mkdir_loop_core(createbranch->path,
|
||||
err = ::_mkdir_loop_core(ugid_,
|
||||
createbranch->path,
|
||||
fusepath_,
|
||||
mode_,
|
||||
umask_);
|
||||
|
|
@ -94,7 +98,8 @@ _mkdir_loop(const Branch *existingbranch_,
|
|||
|
||||
static
|
||||
int
|
||||
_mkdir(const Policy::Search &getattrPolicy_,
|
||||
_mkdir(const ugid_t ugid_,
|
||||
const Policy::Search &getattrPolicy_,
|
||||
const Policy::Create &mkdirPolicy_,
|
||||
const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
|
|
@ -116,7 +121,8 @@ _mkdir(const Policy::Search &getattrPolicy_,
|
|||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return ::_mkdir_loop(existingbranches[0],
|
||||
return ::_mkdir_loop(ugid_,
|
||||
existingbranches[0],
|
||||
createbranches,
|
||||
fusepath_,
|
||||
fusedirpath,
|
||||
|
|
@ -130,10 +136,10 @@ FUSE::mkdir(const fuse_req_ctx_t *ctx_,
|
|||
mode_t mode_)
|
||||
{
|
||||
int rv;
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
rv = ::_mkdir(cfg.func.getattr.policy,
|
||||
rv = ::_mkdir(ctx_,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.func.mkdir.policy,
|
||||
cfg.branches,
|
||||
fusepath,
|
||||
|
|
@ -142,7 +148,8 @@ FUSE::mkdir(const fuse_req_ctx_t *ctx_,
|
|||
if(rv == -EROFS)
|
||||
{
|
||||
cfg.branches.find_and_set_mode_ro();
|
||||
rv = ::_mkdir(cfg.func.getattr.policy,
|
||||
rv = ::_mkdir(ctx_,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.func.mkdir.policy,
|
||||
cfg.branches,
|
||||
fusepath,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include "errno.hpp"
|
||||
#include "error.hpp"
|
||||
#include "fs_acl.hpp"
|
||||
#include "fs_mknod.hpp"
|
||||
#include "fs_mknod_as.hpp"
|
||||
#include "fs_clonepath.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
|
@ -34,7 +34,8 @@
|
|||
static
|
||||
inline
|
||||
int
|
||||
_mknod_core(const fs::path &fullpath_,
|
||||
_mknod_core(const ugid_t ugid_,
|
||||
const fs::path &fullpath_,
|
||||
mode_t mode_,
|
||||
const mode_t umask_,
|
||||
const dev_t dev_)
|
||||
|
|
@ -42,12 +43,13 @@ _mknod_core(const fs::path &fullpath_,
|
|||
if(!fs::acl::dir_has_defaults(fullpath_))
|
||||
mode_ &= ~umask_;
|
||||
|
||||
return fs::mknod(fullpath_,mode_,dev_);
|
||||
return fs::mknod_as(ugid_,fullpath_,mode_,dev_);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_mknod_loop_core(const fs::path &createbranch_,
|
||||
_mknod_loop_core(const ugid_t ugid_,
|
||||
const fs::path &createbranch_,
|
||||
const fs::path &fusepath_,
|
||||
const mode_t mode_,
|
||||
const mode_t umask_,
|
||||
|
|
@ -58,14 +60,15 @@ _mknod_loop_core(const fs::path &createbranch_,
|
|||
|
||||
fullpath = createbranch_ / fusepath_;
|
||||
|
||||
rv = ::_mknod_core(fullpath,mode_,umask_,dev_);
|
||||
rv = ::_mknod_core(ugid_,fullpath,mode_,umask_,dev_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_mknod_loop(const fs::path &existingbranch_,
|
||||
_mknod_loop(const ugid_t ugid_,
|
||||
const fs::path &existingbranch_,
|
||||
const std::vector<Branch*> &createbranches_,
|
||||
const fs::path &fusepath_,
|
||||
const fs::path &fusedirpath_,
|
||||
|
|
@ -78,16 +81,17 @@ _mknod_loop(const fs::path &existingbranch_,
|
|||
|
||||
for(const auto &createbranch : createbranches_)
|
||||
{
|
||||
rv = fs::clonepath_as_root(existingbranch_,
|
||||
createbranch->path,
|
||||
fusedirpath_);
|
||||
rv = fs::clonepath(existingbranch_,
|
||||
createbranch->path,
|
||||
fusedirpath_);
|
||||
if(rv < 0)
|
||||
{
|
||||
err = rv;
|
||||
continue;
|
||||
}
|
||||
|
||||
err = ::_mknod_loop_core(createbranch->path,
|
||||
err = ::_mknod_loop_core(ugid_,
|
||||
createbranch->path,
|
||||
fusepath_,
|
||||
mode_,
|
||||
umask_,
|
||||
|
|
@ -99,7 +103,8 @@ _mknod_loop(const fs::path &existingbranch_,
|
|||
|
||||
static
|
||||
int
|
||||
_mknod(const Policy::Search &searchFunc_,
|
||||
_mknod(const ugid_t ugid_,
|
||||
const Policy::Search &searchFunc_,
|
||||
const Policy::Create &createFunc_,
|
||||
const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
|
|
@ -122,7 +127,8 @@ _mknod(const Policy::Search &searchFunc_,
|
|||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return ::_mknod_loop(existingbranches[0]->path,
|
||||
return ::_mknod_loop(ugid_,
|
||||
existingbranches[0]->path,
|
||||
createbranches,
|
||||
fusepath_,
|
||||
fusedirpath,
|
||||
|
|
@ -138,10 +144,10 @@ FUSE::mknod(const fuse_req_ctx_t *ctx_,
|
|||
dev_t rdev_)
|
||||
{
|
||||
int rv;
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
rv = ::_mknod(cfg.func.getattr.policy,
|
||||
rv = ::_mknod(ctx_,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.func.mknod.policy,
|
||||
cfg.branches,
|
||||
fusepath,
|
||||
|
|
@ -151,7 +157,8 @@ FUSE::mknod(const fuse_req_ctx_t *ctx_,
|
|||
if(rv == -EROFS)
|
||||
{
|
||||
cfg.branches.find_and_set_mode_ro();
|
||||
rv = ::_mknod(cfg.func.getattr.policy,
|
||||
rv = ::_mknod(ctx_,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.func.mknod.policy,
|
||||
cfg.branches,
|
||||
fusepath,
|
||||
|
|
|
|||
|
|
@ -281,7 +281,6 @@ _open_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
|||
{
|
||||
int rv;
|
||||
FileInfo *fi;
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
::_config_to_ffi_flags(cfg,ctx_->pid,ffi_);
|
||||
|
||||
|
|
@ -337,7 +336,6 @@ _open_for_update_lambda(const fuse_req_ctx_t *ctx_,
|
|||
State::OpenFile *of_)
|
||||
{
|
||||
int rv;
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
::_config_to_ffi_flags(cfg,ctx_->pid,ffi_);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ namespace FUSE
|
|||
int
|
||||
passthrough_open(const int fd_)
|
||||
{
|
||||
const ugid::SetRootGuard _;
|
||||
|
||||
return fuse_passthrough_open(fd_);
|
||||
}
|
||||
|
||||
|
|
@ -24,8 +22,6 @@ namespace FUSE
|
|||
int
|
||||
passthrough_close(const int backing_id_)
|
||||
{
|
||||
const ugid::SetRootGuard _;
|
||||
|
||||
return fuse_passthrough_close(backing_id_);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,9 +48,7 @@ int
|
|||
_concurrent_readdir(ThreadPool &tp_,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &rel_dirpath_,
|
||||
fuse_dirents_t *dirents_,
|
||||
const uid_t uid_,
|
||||
const gid_t gid_)
|
||||
fuse_dirents_t *dirents_)
|
||||
{
|
||||
HashSet names;
|
||||
std::mutex mutex;
|
||||
|
|
@ -62,10 +60,8 @@ _concurrent_readdir(ThreadPool &tp_,
|
|||
for(const auto &branch : *branches_)
|
||||
{
|
||||
auto func =
|
||||
[&,dirents_,uid_,gid_]()
|
||||
[&,dirents_]()
|
||||
{
|
||||
const ugid::Set ugid(uid_,gid_);
|
||||
|
||||
return ::_readdir(branch.path,
|
||||
rel_dirpath_,
|
||||
names,
|
||||
|
|
@ -95,7 +91,5 @@ FUSE::ReadDirCOR::operator()(const fuse_req_ctx_t *ctx_,
|
|||
return ::_concurrent_readdir(_tp,
|
||||
cfg.branches,
|
||||
di->fusepath,
|
||||
dirents_,
|
||||
ctx_->uid,
|
||||
ctx_->gid);
|
||||
dirents_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,16 +45,14 @@ int
|
|||
_readdir(ThreadPool &tp_,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &rel_dirpath_,
|
||||
fuse_dirents_t *dirents_,
|
||||
uid_t const uid_,
|
||||
gid_t const gid_)
|
||||
fuse_dirents_t *dirents_)
|
||||
{
|
||||
int rv;
|
||||
std::vector<std::future<DirRV>> futures;
|
||||
|
||||
fuse_dirents_reset(dirents_);
|
||||
|
||||
futures = ::_opendir(tp_,branches_,rel_dirpath_,uid_,gid_);
|
||||
futures = ::_opendir(tp_,branches_,rel_dirpath_);
|
||||
rv = ::_readdir(futures,rel_dirpath_,dirents_);
|
||||
|
||||
return rv;
|
||||
|
|
@ -70,7 +68,5 @@ FUSE::ReadDirCOSR::operator()(const fuse_req_ctx_t *ctx_,
|
|||
return ::_readdir(_tp,
|
||||
cfg.branches,
|
||||
di->fusepath,
|
||||
dirents_,
|
||||
ctx_->uid,
|
||||
ctx_->gid);
|
||||
dirents_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,7 @@ inline
|
|||
std::vector<std::future<DirRV>>
|
||||
_opendir(ThreadPool &tp_,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &rel_dirpath_,
|
||||
uid_t const uid_,
|
||||
gid_t const gid_)
|
||||
const fs::path &rel_dirpath_)
|
||||
{
|
||||
std::vector<std::future<DirRV>> futures;
|
||||
|
||||
|
|
@ -39,11 +37,10 @@ _opendir(ThreadPool &tp_,
|
|||
for(const auto &branch : *branches_)
|
||||
{
|
||||
auto func =
|
||||
[&branch,&rel_dirpath_,uid_,gid_]()
|
||||
[&branch,&rel_dirpath_]()
|
||||
{
|
||||
int fd;
|
||||
fs::path abs_dirpath;
|
||||
const ugid::Set ugid(uid_,gid_);
|
||||
|
||||
abs_dirpath = branch.path / rel_dirpath_;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,9 +37,7 @@ inline
|
|||
std::vector<std::future<DirRV>>
|
||||
_opendir(ThreadPool &tp_,
|
||||
const Branches::Ptr &branches_,
|
||||
const fs::path &rel_dirpath_,
|
||||
uid_t const uid_,
|
||||
gid_t const gid_)
|
||||
const fs::path &rel_dirpath_)
|
||||
{
|
||||
std::vector<std::future<DirRV>> futures;
|
||||
|
||||
|
|
@ -48,11 +46,10 @@ _opendir(ThreadPool &tp_,
|
|||
for(const auto &branch : *branches_)
|
||||
{
|
||||
auto func =
|
||||
[&branch,&rel_dirpath_,uid_,gid_]()
|
||||
[&branch,&rel_dirpath_]()
|
||||
{
|
||||
DIR *dir;
|
||||
fs::path abs_dirpath;
|
||||
const ugid::Set ugid(uid_,gid_);
|
||||
|
||||
abs_dirpath = branch.path / rel_dirpath_;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ FUSE::ReadDirSeq::operator()(const fuse_req_ctx_t *ctx_,
|
|||
const fuse_file_info_t *ffi_,
|
||||
fuse_dirents_t *dirents_)
|
||||
{
|
||||
DirInfo *di = DirInfo::from_fh(ffi_->fh);
|
||||
const ugid::Set ugid(ctx_);
|
||||
DirInfo *di = DirInfo::from_fh(ffi_->fh);
|
||||
|
||||
return ::_readdir(cfg.branches,
|
||||
di->fusepath,
|
||||
|
|
|
|||
|
|
@ -120,8 +120,7 @@ FUSE::readlink(const fuse_req_ctx_t *ctx_,
|
|||
char *buf_,
|
||||
size_t size_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_readlink(cfg.func.readlink.policy,
|
||||
cfg.branches,
|
||||
|
|
|
|||
|
|
@ -101,8 +101,6 @@ FUSE::removexattr(const fuse_req_ctx_t *ctx_,
|
|||
if(cfg.xattr.to_int())
|
||||
return -cfg.xattr.to_int();
|
||||
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
return ::_removexattr(cfg.func.removexattr.policy,
|
||||
cfg.func.getxattr.policy,
|
||||
cfg.branches,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include "errno.hpp"
|
||||
#include "fs_clonepath.hpp"
|
||||
#include "fs_link.hpp"
|
||||
#include "fs_mkdir_as_root.hpp"
|
||||
#include "fs_mkdir_as.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_remove.hpp"
|
||||
#include "fs_rename.hpp"
|
||||
|
|
@ -105,9 +105,9 @@ _rename_create_path(const Policy::Search &searchPolicy_,
|
|||
rv = fs::rename(oldfullpath,newfullpath);
|
||||
if(rv < 0)
|
||||
{
|
||||
rv = fs::clonepath_as_root(newbranches[0]->path,
|
||||
branch.path,
|
||||
newfusepath_.parent_path());
|
||||
rv = fs::clonepath(newbranches[0]->path,
|
||||
branch.path,
|
||||
newfusepath_.parent_path());
|
||||
if(rv >= 0)
|
||||
rv = fs::rename(oldfullpath,newfullpath);
|
||||
}
|
||||
|
|
@ -209,7 +209,6 @@ _rename_exdev_rename_target(const Policy::Action &actionPolicy_,
|
|||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
ugid::SetRootGuard ugidGuard;
|
||||
for(auto &branch : obranches_)
|
||||
{
|
||||
clonesrc = branch->path;
|
||||
|
|
@ -219,7 +218,7 @@ _rename_exdev_rename_target(const Policy::Action &actionPolicy_,
|
|||
rv = fs::clonepath(clonesrc,clonedst,oldfusepath_.parent_path());
|
||||
if(rv == -ENOENT)
|
||||
{
|
||||
fs::mkdir(clonedst,01777);
|
||||
fs::mkdir_as({0,0},clonedst,01777);
|
||||
rv = fs::clonepath(clonesrc,clonedst,oldfusepath_.parent_path());
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +354,6 @@ FUSE::rename(const fuse_req_ctx_t *ctx_,
|
|||
int rv;
|
||||
const fs::path oldfusepath{oldfusepath_};
|
||||
const fs::path newfusepath{newfusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
rv = ::_rename(oldfusepath,newfusepath);
|
||||
if(rv == -EXDEV)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ FUSE::rmdir(const fuse_req_ctx_t *ctx_,
|
|||
const char *fusepath_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
return ::_rmdir(cfg.func.rmdir.policy,
|
||||
cfg.branches,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include "fs_lsetxattr.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_statvfs_cache.hpp"
|
||||
#include "gidcache.hpp"
|
||||
#include "num.hpp"
|
||||
#include "policy_rv.hpp"
|
||||
#include "str.hpp"
|
||||
|
|
@ -55,10 +54,6 @@ _setxattr_cmd_xattr(const std::string_view &attrname_,
|
|||
return (fuse_gc1(),0);
|
||||
if(cmd == "invalidate-all-nodes")
|
||||
return (fuse_invalidate_all_nodes(),0);
|
||||
if(cmd == "invalidate-gid-cache")
|
||||
return (GIDCache::invalidate_all(),0);
|
||||
if(cmd == "clear-gid-cache")
|
||||
return (GIDCache::clear_all(),0);
|
||||
|
||||
return -ENOATTR;
|
||||
}
|
||||
|
|
@ -194,8 +189,6 @@ _setxattr(const fuse_req_ctx_t *ctx_,
|
|||
if(cfg.xattr.to_int())
|
||||
return -cfg.xattr.to_int();
|
||||
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
return ::_setxattr(cfg.func.setxattr.policy,
|
||||
cfg.func.getxattr.policy,
|
||||
cfg.branches,
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ FUSE::statfs(const fuse_req_ctx_t *ctx_,
|
|||
struct statvfs *st_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
return ::_statfs(cfg.branches,
|
||||
fusepath,
|
||||
|
|
|
|||
|
|
@ -213,8 +213,6 @@ FUSE::statx(const fuse_req_ctx_t *ctx_,
|
|||
if(Config::is_ctrl_file(fusepath))
|
||||
return ::_statx_controlfile(st_);
|
||||
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
return ::_statx(fusepath,
|
||||
flags_|AT_STATX_DONT_SYNC,
|
||||
mask_,
|
||||
|
|
@ -247,8 +245,6 @@ FUSE::statx_fh(const fuse_req_ctx_t *ctx_,
|
|||
|
||||
FileInfo *fi = FileInfo::from_fh(fh);
|
||||
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
return ::_statx(fi->fusepath,
|
||||
flags_|AT_STATX_DONT_SYNC,
|
||||
mask_,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include "fs_lstat.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "fs_symlink.hpp"
|
||||
#include "fs_symlink_as.hpp"
|
||||
#include "fuse_getattr.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
|
||||
static
|
||||
int
|
||||
_symlink_loop_core(const fs::path &newbranch_,
|
||||
_symlink_loop_core(const ugid_t ugid_,
|
||||
const fs::path &newbranch_,
|
||||
const char *target_,
|
||||
const fs::path &linkpath_,
|
||||
struct stat *st_)
|
||||
|
|
@ -45,7 +46,7 @@ _symlink_loop_core(const fs::path &newbranch_,
|
|||
|
||||
fullnewpath = newbranch_ / linkpath_;
|
||||
|
||||
rv = fs::symlink(target_,fullnewpath);
|
||||
rv = fs::symlink_as(ugid_,target_,fullnewpath);
|
||||
if((rv >= 0) && (st_ != NULL) && (st_->st_ino == 0))
|
||||
{
|
||||
fs::lstat(fullnewpath,st_);
|
||||
|
|
@ -60,7 +61,8 @@ _symlink_loop_core(const fs::path &newbranch_,
|
|||
|
||||
static
|
||||
int
|
||||
_symlink_loop(const fs::path &existingbranch_,
|
||||
_symlink_loop(const ugid_t ugid_,
|
||||
const fs::path &existingbranch_,
|
||||
const std::vector<Branch*> &newbranches_,
|
||||
const char *target_,
|
||||
const fs::path &linkpath_,
|
||||
|
|
@ -72,13 +74,14 @@ _symlink_loop(const fs::path &existingbranch_,
|
|||
|
||||
for(auto &newbranch :newbranches_)
|
||||
{
|
||||
rv = fs::clonepath_as_root(existingbranch_,
|
||||
newbranch->path,
|
||||
newdirpath_);
|
||||
rv = fs::clonepath(existingbranch_,
|
||||
newbranch->path,
|
||||
newdirpath_);
|
||||
if(rv < 0)
|
||||
err = rv;
|
||||
else
|
||||
err = ::_symlink_loop_core(newbranch->path,
|
||||
err = ::_symlink_loop_core(ugid_,
|
||||
newbranch->path,
|
||||
target_,
|
||||
linkpath_,
|
||||
st_);
|
||||
|
|
@ -89,7 +92,8 @@ _symlink_loop(const fs::path &existingbranch_,
|
|||
|
||||
static
|
||||
int
|
||||
_symlink(const Policy::Search &searchFunc_,
|
||||
_symlink(const ugid_t ugid_,
|
||||
const Policy::Search &searchFunc_,
|
||||
const Policy::Create &createFunc_,
|
||||
const Branches &branches_,
|
||||
const char *target_,
|
||||
|
|
@ -111,7 +115,8 @@ _symlink(const Policy::Search &searchFunc_,
|
|||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
return ::_symlink_loop(existingbranches[0]->path,
|
||||
return ::_symlink_loop(ugid_,
|
||||
existingbranches[0]->path,
|
||||
newbranches,
|
||||
target_,
|
||||
linkpath_,
|
||||
|
|
@ -119,22 +124,6 @@ _symlink(const Policy::Search &searchFunc_,
|
|||
st_);
|
||||
}
|
||||
|
||||
int
|
||||
FUSE::symlink(const fuse_req_ctx_t *ctx_,
|
||||
const char *target_,
|
||||
const char *linkpath_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeouts_)
|
||||
{
|
||||
const fs::path linkpath{linkpath_};
|
||||
|
||||
return FUSE::symlink(ctx_,
|
||||
target_,
|
||||
linkpath,
|
||||
st_,
|
||||
timeouts_);
|
||||
}
|
||||
|
||||
int
|
||||
FUSE::symlink(const fuse_req_ctx_t *ctx_,
|
||||
const char *target_,
|
||||
|
|
@ -143,9 +132,9 @@ FUSE::symlink(const fuse_req_ctx_t *ctx_,
|
|||
fuse_timeouts_t *timeouts_)
|
||||
{
|
||||
int rv;
|
||||
const ugid::Set ugid(ctx_);
|
||||
|
||||
rv = ::_symlink(cfg.func.getattr.policy,
|
||||
rv = ::_symlink(ctx_,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.func.symlink.policy,
|
||||
cfg.branches,
|
||||
target_,
|
||||
|
|
@ -154,7 +143,8 @@ FUSE::symlink(const fuse_req_ctx_t *ctx_,
|
|||
if(rv == -EROFS)
|
||||
{
|
||||
cfg.branches.find_and_set_mode_ro();
|
||||
rv = ::_symlink(cfg.func.getattr.policy,
|
||||
rv = ::_symlink(ctx_,
|
||||
cfg.func.getattr.policy,
|
||||
cfg.func.symlink.policy,
|
||||
cfg.branches,
|
||||
target_,
|
||||
|
|
@ -181,3 +171,19 @@ FUSE::symlink(const fuse_req_ctx_t *ctx_,
|
|||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
FUSE::symlink(const fuse_req_ctx_t *ctx_,
|
||||
const char *target_,
|
||||
const char *linkpath_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeouts_)
|
||||
{
|
||||
const fs::path linkpath{linkpath_};
|
||||
|
||||
return FUSE::symlink(ctx_,
|
||||
target_,
|
||||
linkpath,
|
||||
st_,
|
||||
timeouts_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,8 +94,7 @@ FUSE::truncate(const fuse_req_ctx_t *ctx_,
|
|||
const char *fusepath_,
|
||||
off_t size_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_truncate(cfg.func.truncate.policy,
|
||||
cfg.func.getattr.policy,
|
||||
|
|
|
|||
|
|
@ -68,8 +68,7 @@ int
|
|||
FUSE::unlink(const fuse_req_ctx_t *ctx_,
|
||||
const char *fusepath_)
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_unlink(cfg.func.unlink.policy,
|
||||
cfg.branches,
|
||||
|
|
|
|||
|
|
@ -93,8 +93,7 @@ FUSE::utimens(const fuse_req_ctx_t *ctx_,
|
|||
const char *fusepath_,
|
||||
const timespec ts_[2])
|
||||
{
|
||||
const fs::path fusepath{fusepath_};
|
||||
const ugid::Set ugid(ctx_);
|
||||
const fs::path fusepath{fusepath_};
|
||||
|
||||
return ::_utimens(cfg.func.utimens.policy,
|
||||
cfg.func.getattr.policy,
|
||||
|
|
|
|||
181
src/gidcache.cpp
181
src/gidcache.cpp
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "gidcache.hpp"
|
||||
|
||||
#include "syslog.hpp"
|
||||
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined __linux__ and UGID_USE_RWLOCK == 0
|
||||
# include <sys/syscall.h>
|
||||
#elif __APPLE__
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
int GIDCache::expire_timeout = (60 * 60);
|
||||
int GIDCache::remove_timeout = (60 * 60 * 12);
|
||||
boost::concurrent_flat_map<uid_t,GIDRecord> GIDCache::_records;
|
||||
|
||||
static
|
||||
int
|
||||
_getgrouplist(const char *user,
|
||||
const gid_t group,
|
||||
gid_t *groups,
|
||||
int *ngroups)
|
||||
{
|
||||
#if __APPLE__
|
||||
return ::getgrouplist(user,group,(int*)groups,ngroups);
|
||||
#else
|
||||
return ::getgrouplist(user,group,groups,ngroups);
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
_setgroups(const std::vector<gid_t> gids_)
|
||||
{
|
||||
if(gids_.empty())
|
||||
return 0;
|
||||
|
||||
#if defined __linux__ and UGID_USE_RWLOCK == 0
|
||||
# if defined SYS_setgroups32
|
||||
return ::syscall(SYS_setgroups32,gids_.size(),gids_.data());
|
||||
# else
|
||||
return ::syscall(SYS_setgroups,gids_.size(),gids_.data());
|
||||
# endif
|
||||
#else
|
||||
return ::setgroups(gids_.size(),gids_.data());
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_getgroups(const uid_t uid_,
|
||||
const gid_t gid_,
|
||||
std::vector<gid_t> &gids_)
|
||||
{
|
||||
int rv;
|
||||
int ngroups;
|
||||
char buf[4096];
|
||||
struct passwd pwd;
|
||||
struct passwd *pwdrv;
|
||||
|
||||
gids_.clear();
|
||||
rv = ::getpwuid_r(uid_,&pwd,buf,sizeof(buf),&pwdrv);
|
||||
if((rv == -1) || (pwdrv == NULL))
|
||||
goto error;
|
||||
|
||||
ngroups = 0;
|
||||
rv = ::_getgrouplist(pwd.pw_name,gid_,NULL,&ngroups);
|
||||
gids_.resize(ngroups);
|
||||
|
||||
rv = ::_getgrouplist(pwd.pw_name,gid_,gids_.data(),&ngroups);
|
||||
if((size_t)ngroups < gids_.size())
|
||||
gids_.resize(ngroups);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
gids_.clear();
|
||||
//gids_.push_back(gid_);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
GIDCache::initgroups(const uid_t uid_,
|
||||
const gid_t gid_)
|
||||
{
|
||||
auto first_func =
|
||||
[=](auto &x)
|
||||
{
|
||||
x.second.last_update = ::time(NULL);
|
||||
::_getgroups(uid_,gid_,x.second.gids);
|
||||
::_setgroups(x.second.gids);
|
||||
};
|
||||
auto exists_func =
|
||||
[=](auto &x)
|
||||
{
|
||||
time_t now;
|
||||
|
||||
now = ::time(NULL);
|
||||
if((now - x.second.last_update) > GIDCache::expire_timeout)
|
||||
{
|
||||
::_getgroups(uid_,gid_,x.second.gids);
|
||||
x.second.last_update = now;
|
||||
}
|
||||
::_setgroups(x.second.gids);
|
||||
};
|
||||
|
||||
_records.try_emplace_and_visit(uid_,
|
||||
first_func,
|
||||
exists_func);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
GIDCache::invalidate_all()
|
||||
{
|
||||
size_t size;
|
||||
|
||||
size = _records.size();
|
||||
_records.visit_all([](auto &x)
|
||||
{
|
||||
x.second.last_update = 0;
|
||||
});
|
||||
|
||||
SysLog::info("gid cache invalidated, {} entries",size);
|
||||
}
|
||||
|
||||
void
|
||||
GIDCache::clear_all()
|
||||
{
|
||||
size_t size;
|
||||
|
||||
size = _records.size();
|
||||
_records.clear();
|
||||
|
||||
SysLog::info("gid cache cleared, {} entries",size);
|
||||
}
|
||||
|
||||
void
|
||||
GIDCache::clear_unused()
|
||||
{
|
||||
int erased = 0;
|
||||
time_t now = ::time(NULL);
|
||||
auto erase_func =
|
||||
[now,&erased](auto &x)
|
||||
{
|
||||
bool should_erase;
|
||||
time_t time_delta;
|
||||
|
||||
time_delta = (now - x.second.last_update);
|
||||
should_erase = (time_delta > GIDCache::remove_timeout);
|
||||
erased += should_erase;
|
||||
|
||||
return should_erase;
|
||||
};
|
||||
|
||||
_records.erase_if(erase_func);
|
||||
|
||||
SysLog::info("cleared {} unused gid cache entries",erased);
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/unordered/concurrent_flat_map.hpp"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct GIDRecord
|
||||
{
|
||||
std::vector<gid_t> gids;
|
||||
time_t last_update;
|
||||
};
|
||||
|
||||
struct GIDCache
|
||||
{
|
||||
public:
|
||||
static
|
||||
int
|
||||
initgroups(const uid_t uid,
|
||||
const gid_t gid);
|
||||
|
||||
static void invalidate_all();
|
||||
static void clear_all();
|
||||
static void clear_unused();
|
||||
|
||||
public:
|
||||
static int expire_timeout;
|
||||
static int remove_timeout;
|
||||
|
||||
private:
|
||||
static boost::concurrent_flat_map<uid_t,GIDRecord> _records;
|
||||
};
|
||||
|
|
@ -18,12 +18,12 @@
|
|||
#include "mergerfs_fsck.hpp"
|
||||
#include "mergerfs_collect_info.hpp"
|
||||
|
||||
#include "caps.hpp"
|
||||
#include "config.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_readahead.hpp"
|
||||
#include "fs_umount2.hpp"
|
||||
#include "fs_wait_for_mount.hpp"
|
||||
#include "gidcache.hpp"
|
||||
#include "maintenance_thread.hpp"
|
||||
#include "oom.hpp"
|
||||
#include "option_parser.hpp"
|
||||
|
|
@ -269,7 +269,6 @@ _usr2_signal_handler(int signal_)
|
|||
{
|
||||
// SysLog::info("Received SIGUSR2 - triggering thorough gc");
|
||||
// fuse_gc();
|
||||
// GIDCache::clear_all();
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -340,12 +339,6 @@ _main(int argc_,
|
|||
}
|
||||
|
||||
::_warn_if_not_root();
|
||||
|
||||
MaintenanceThread::push_job([](int count_)
|
||||
{
|
||||
if((count_ % 60) == 0)
|
||||
GIDCache::clear_unused();
|
||||
});
|
||||
::_setup_resources(cfg.scheduling_priority);
|
||||
::_setup_signal_handlers();
|
||||
::_set_oom_score_adj();
|
||||
|
|
@ -354,6 +347,8 @@ _main(int argc_,
|
|||
if(cfg.lazy_umount_mountpoint)
|
||||
::_lazy_umount(cfg.mountpoint);
|
||||
|
||||
caps::setup();
|
||||
|
||||
rv = fuse_main(args.argc,
|
||||
args.argv,
|
||||
&ops);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "policy_ff.hpp"
|
||||
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include "errno.hpp"
|
||||
#include "fs_exists.hpp"
|
||||
#include "fs_info.hpp"
|
||||
|
|
|
|||
4
src/predictability.h
Normal file
4
src/predictability.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
33
src/ugid.cpp
33
src/ugid.cpp
|
|
@ -1,33 +1,8 @@
|
|||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "gidcache.hpp"
|
||||
|
||||
#if defined __linux__ and UGID_USE_RWLOCK == 0
|
||||
#include "ugid_linux.icpp"
|
||||
#else
|
||||
#include "ugid_rwlock.icpp"
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
namespace ugid
|
||||
{
|
||||
void
|
||||
initgroups(const uid_t uid_,
|
||||
const gid_t gid_)
|
||||
{
|
||||
GIDCache::initgroups(uid_,gid_);
|
||||
}
|
||||
thread_local uid_t currentuid = 0;
|
||||
thread_local gid_t currentgid = 0;
|
||||
thread_local bool initialized = false;
|
||||
}
|
||||
|
|
|
|||
111
src/ugid.hpp
111
src/ugid.hpp
|
|
@ -16,21 +16,112 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_req_ctx.h"
|
||||
#include "predictability.h"
|
||||
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct ugid_t
|
||||
{
|
||||
ugid_t(const uid_t uid_,
|
||||
const gid_t gid_)
|
||||
: uid(uid_),
|
||||
gid(gid_)
|
||||
{
|
||||
}
|
||||
|
||||
ugid_t(const fuse_req_ctx_t *ctx_)
|
||||
: ugid_t(ctx_->uid,ctx_->gid)
|
||||
{
|
||||
}
|
||||
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
};
|
||||
|
||||
#if defined SYS_setreuid32
|
||||
#define SETREUID(R,E) (::syscall(SYS_setreuid32,(R),(E)))
|
||||
#else
|
||||
#define SETREUID(R,E) (::syscall(SYS_setreuid,(R),(E)))
|
||||
#endif
|
||||
|
||||
#if defined SYS_setregid32
|
||||
#define SETREGID(R,E) (::syscall(SYS_setregid32,(R),(E)))
|
||||
#else
|
||||
#define SETREGID(R,E) (::syscall(SYS_setregid,(R),(E)))
|
||||
#endif
|
||||
|
||||
#if defined SYS_geteuid32
|
||||
#define GETEUID() (::syscall(SYS_geteuid32))
|
||||
#else
|
||||
#define GETEUID() (::syscall(SYS_geteuid))
|
||||
#endif
|
||||
|
||||
#if defined SYS_getegid32
|
||||
#define GETEGID() (::syscall(SYS_getegid32))
|
||||
#else
|
||||
#define GETEGID() (::syscall(SYS_getegid))
|
||||
#endif
|
||||
|
||||
namespace ugid
|
||||
{
|
||||
void init();
|
||||
void initgroups(const uid_t uid, const gid_t gid);
|
||||
extern thread_local uid_t currentuid;
|
||||
extern thread_local gid_t currentgid;
|
||||
extern thread_local bool initialized;
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
set(const uid_t newuid_,
|
||||
const gid_t newgid_)
|
||||
{
|
||||
assert(newuid_ != FUSE_INVALID_UIDGID);
|
||||
assert(newgid_ != FUSE_INVALID_UIDGID);
|
||||
|
||||
if(unlikely(!initialized))
|
||||
{
|
||||
currentuid = GETEUID();
|
||||
currentgid = GETEGID();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
if((newuid_ == currentuid) && (newgid_ == currentgid))
|
||||
return;
|
||||
|
||||
SETREGID(-1,newgid_);
|
||||
SETREUID(-1,newuid_);
|
||||
|
||||
currentuid = newuid_;
|
||||
currentgid = newgid_;
|
||||
}
|
||||
|
||||
struct SetGuard
|
||||
{
|
||||
SetGuard(const ugid_t ugid_)
|
||||
: prev(currentuid,currentgid)
|
||||
{
|
||||
assert(currentuid == 0);
|
||||
assert(currentgid == 0);
|
||||
ugid::set(ugid_.uid,ugid_.gid);
|
||||
}
|
||||
|
||||
~SetGuard()
|
||||
{
|
||||
ugid::set(prev.uid,prev.gid);
|
||||
}
|
||||
|
||||
const ugid_t prev;
|
||||
};
|
||||
}
|
||||
|
||||
#if defined __linux__ and UGID_USE_RWLOCK == 0
|
||||
#pragma message "using ugid_linux.hpp"
|
||||
#include "ugid_linux.hpp"
|
||||
#else
|
||||
#pragma message "using ugid_rwlock.hpp"
|
||||
#include "ugid_rwlock.hpp"
|
||||
#endif
|
||||
#undef SETREUID
|
||||
#undef SETREGID
|
||||
#undef GETEUID
|
||||
#undef GETEGID
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_req_ctx.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#if defined SYS_setreuid32
|
||||
#define SETREUID(R,E) (::syscall(SYS_setreuid32,(R),(E)))
|
||||
#else
|
||||
#define SETREUID(R,E) (::syscall(SYS_setreuid,(R),(E)))
|
||||
#endif
|
||||
|
||||
#if defined SYS_setregid32
|
||||
#define SETREGID(R,E) (::syscall(SYS_setregid32,(R),(E)))
|
||||
#else
|
||||
#define SETREGID(R,E) (::syscall(SYS_setregid,(R),(E)))
|
||||
#endif
|
||||
|
||||
#if defined SYS_geteuid32
|
||||
#define GETEUID() (::syscall(SYS_geteuid32))
|
||||
#else
|
||||
#define GETEUID() (::syscall(SYS_geteuid))
|
||||
#endif
|
||||
|
||||
#if defined SYS_getegid32
|
||||
#define GETEGID() (::syscall(SYS_getegid32))
|
||||
#else
|
||||
#define GETEGID() (::syscall(SYS_getegid))
|
||||
#endif
|
||||
|
||||
namespace ugid
|
||||
{
|
||||
extern thread_local uid_t currentuid;
|
||||
extern thread_local gid_t currentgid;
|
||||
extern thread_local bool initialized;
|
||||
|
||||
struct Set
|
||||
{
|
||||
Set(const uid_t newuid_,
|
||||
const gid_t newgid_)
|
||||
{
|
||||
assert((int)newuid_ != -1);
|
||||
assert((int)newgid_ != -1);
|
||||
|
||||
if(!initialized)
|
||||
{
|
||||
currentuid = GETEUID();
|
||||
currentgid = GETEGID();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
if((newuid_ == currentuid) && (newgid_ == currentgid))
|
||||
return;
|
||||
|
||||
if(currentuid != 0)
|
||||
{
|
||||
SETREUID(-1,0);
|
||||
SETREGID(-1,0);
|
||||
}
|
||||
|
||||
if(newgid_)
|
||||
{
|
||||
SETREGID(-1,newgid_);
|
||||
ugid::initgroups(newuid_,newgid_);
|
||||
}
|
||||
|
||||
if(newuid_)
|
||||
SETREUID(-1,newuid_);
|
||||
|
||||
currentuid = newuid_;
|
||||
currentgid = newgid_;
|
||||
}
|
||||
|
||||
Set(const fuse_req_ctx_t *ctx_)
|
||||
: Set(ctx_->uid,ctx_->gid)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct SetRootGuard
|
||||
{
|
||||
SetRootGuard() :
|
||||
prevuid(currentuid),
|
||||
prevgid(currentgid)
|
||||
{
|
||||
Set(0,0);
|
||||
}
|
||||
|
||||
~SetRootGuard()
|
||||
{
|
||||
Set(prevuid,prevgid);
|
||||
}
|
||||
|
||||
const uid_t prevuid;
|
||||
const gid_t prevgid;
|
||||
};
|
||||
}
|
||||
|
||||
#undef SETREUID
|
||||
#undef SETREGID
|
||||
#undef GETEUID
|
||||
#undef GETEGID
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ugid
|
||||
{
|
||||
thread_local uid_t currentuid = 0;
|
||||
thread_local gid_t currentgid = 0;
|
||||
thread_local bool initialized = false;
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_req_ctx.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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(const fuse_req_ctx_t *ctx_)
|
||||
: Set(ctx_->uid,ctx_->gid)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~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;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ugid
|
||||
{
|
||||
uid_t currentuid;
|
||||
gid_t currentgid;
|
||||
pthread_rwlock_t rwlock;
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
pthread_rwlockattr_t attr;
|
||||
|
||||
pthread_rwlockattr_init(&attr);
|
||||
# if defined PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
|
||||
pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
|
||||
# endif
|
||||
|
||||
pthread_rwlock_init(&rwlock,&attr);
|
||||
|
||||
currentuid = ::geteuid();
|
||||
currentgid = ::getegid();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue