diff --git a/Makefile b/Makefile index 3686eb6f..a600e27a 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,6 @@ OPT_FLAGS := -O0 \ $(SANITIZE) \ -fstack-protector-strong \ -Wextra \ - -Werror \ -Wno-unused-parameter \ -DDEBUG endif diff --git a/libfuse/lib/fuse_lowlevel.cpp b/libfuse/lib/fuse_lowlevel.cpp index bf580195..ad0df588 100644 --- a/libfuse/lib/fuse_lowlevel.cpp +++ b/libfuse/lib/fuse_lowlevel.cpp @@ -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); diff --git a/mkdocs/docs/config/options.md b/mkdocs/docs/config/options.md index be18708e..326a162b 100644 --- a/mkdocs/docs/config/options.md +++ b/mkdocs/docs/config/options.md @@ -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 diff --git a/mkdocs/docs/faq/compatibility_and_integration.md b/mkdocs/docs/faq/compatibility_and_integration.md index 1ea27ad5..4e4485f8 100644 --- a/mkdocs/docs/faq/compatibility_and_integration.md +++ b/mkdocs/docs/faq/compatibility_and_integration.md @@ -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? diff --git a/mkdocs/docs/faq/recommendations_and_warnings.md b/mkdocs/docs/faq/recommendations_and_warnings.md deleted file mode 100644 index 12924f68..00000000 --- a/mkdocs/docs/faq/recommendations_and_warnings.md +++ /dev/null @@ -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. diff --git a/mkdocs/docs/faq/technical_behavior_and_limitations.md b/mkdocs/docs/faq/technical_behavior_and_limitations.md index d6459f42..f8f3e79e 100644 --- a/mkdocs/docs/faq/technical_behavior_and_limitations.md +++ b/mkdocs/docs/faq/technical_behavior_and_limitations.md @@ -146,7 +146,7 @@ You can remove the reserve by running: `tune2fs -m 0 ` ## 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. - -Additionally, mergerfs [utilizes a cache for supplemental -groups](../known_issues_bugs.md#supplemental-user-groups) due the the -high cost of querying that information. +group id (gid). + +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? diff --git a/mkdocs/docs/faq/usage_and_functionality.md b/mkdocs/docs/faq/usage_and_functionality.md index 44203e95..aa762221 100644 --- a/mkdocs/docs/faq/usage_and_functionality.md +++ b/mkdocs/docs/faq/usage_and_functionality.md @@ -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 diff --git a/mkdocs/docs/known_issues_bugs.md b/mkdocs/docs/known_issues_bugs.md index 4d5ba87b..2539affc 100644 --- a/mkdocs/docs/known_issues_bugs.md +++ b/mkdocs/docs/known_issues_bugs.md @@ -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. diff --git a/mkdocs/docs/resource_usage.md b/mkdocs/docs/resource_usage.md index c4f185b9..b0eb1d85 100644 --- a/mkdocs/docs/resource_usage.md +++ b/mkdocs/docs/resource_usage.md @@ -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 diff --git a/mkdocs/docs/runtime_interface.md b/mkdocs/docs/runtime_interface.md index d0bfc97f..42e19bf4 100644 --- a/mkdocs/docs/runtime_interface.md +++ b/mkdocs/docs/runtime_interface.md @@ -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 diff --git a/mkdocs/mkdocs.yml b/mkdocs/mkdocs.yml index 15859133..d65d22f2 100644 --- a/mkdocs/mkdocs.yml +++ b/mkdocs/mkdocs.yml @@ -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 diff --git a/src/caps.cpp b/src/caps.cpp new file mode 100644 index 00000000..01156cdc --- /dev/null +++ b/src/caps.cpp @@ -0,0 +1,104 @@ +#include "caps.hpp" + +#if defined __linux__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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 + +int +caps::setup() +{ + return -ENOSYS; +} +#endif diff --git a/src/caps.hpp b/src/caps.hpp new file mode 100644 index 00000000..7ca22636 --- /dev/null +++ b/src/caps.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace caps +{ + int setup(); +} diff --git a/src/config.cpp b/src/config.cpp index 46a2e894..691dd2bc 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -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; diff --git a/src/config.hpp b/src/config.hpp index a6335c97..c16e46d3 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -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; diff --git a/src/config_gidcache.cpp b/src/config_gidcache.cpp deleted file mode 100644 index f5267039..00000000 --- a/src/config_gidcache.cpp +++ /dev/null @@ -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; -} diff --git a/src/config_gidcache.hpp b/src/config_gidcache.hpp deleted file mode 100644 index 06a67161..00000000 --- a/src/config_gidcache.hpp +++ /dev/null @@ -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; -}; diff --git a/src/fs_attr_linux.icpp b/src/fs_attr_linux.icpp index 6133d6ed..47fd077b 100644 --- a/src/fs_attr_linux.icpp +++ b/src/fs_attr_linux.icpp @@ -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; diff --git a/src/fs_clonepath.cpp b/src/fs_clonepath.cpp index 423b9d46..088b737a 100644 --- a/src/fs_clonepath.cpp +++ b/src/fs_clonepath.cpp @@ -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); + + 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; - return fs::clonepath(srcpath_, - dstpath_, - relpath_, - return_metadata_errors_); + rv = fs::futimens(dstdirfd,st); + if(rv < 0) + return rv; + + return 0; } diff --git a/src/fs_clonepath.hpp b/src/fs_clonepath.hpp index 3bd1a8c5..f5821f9d 100644 --- a/src/fs_clonepath.hpp +++ b/src/fs_clonepath.hpp @@ -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); } diff --git a/src/fs_is_rofs.hpp b/src/fs_is_rofs.hpp index 22c556da..17ef7bf0 100644 --- a/src/fs_is_rofs.hpp +++ b/src/fs_is_rofs.hpp @@ -49,8 +49,6 @@ namespace fs bool is_rofs(const fs::path &path_) { - ugid::SetRootGuard const ugid; - int fd; std::string tmp_filepath; diff --git a/src/fs_mkdir_as.hpp b/src/fs_mkdir_as.hpp new file mode 100644 index 00000000..f922dd3a --- /dev/null +++ b/src/fs_mkdir_as.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "fs_mkdir.hpp" +#include "ugid.hpp" + + +#if defined __linux__ +namespace fs +{ + template + 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 + 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 diff --git a/src/fs_mkdir_as_root.hpp b/src/fs_mkdir_as_root.hpp deleted file mode 100644 index 3299ffa3..00000000 --- a/src/fs_mkdir_as_root.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - ISC License - - Copyright (c) 2021, Antonio SJ Musumeci - - 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 - static - inline - int - mkdir_as_root(const T &path_, - const mode_t mode_) - { - const ugid::SetRootGuard guard; - - return fs::mkdir(path_,mode_); - } -} diff --git a/src/fs_mkdirat.hpp b/src/fs_mkdirat.hpp new file mode 100644 index 00000000..f87f9150 --- /dev/null +++ b/src/fs_mkdirat.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "to_neg_errno.hpp" +#include "fs_path.hpp" + +#include +#include + +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_); + } +} diff --git a/src/fs_mknod_as.hpp b/src/fs_mknod_as.hpp new file mode 100644 index 00000000..712e5220 --- /dev/null +++ b/src/fs_mknod_as.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "fs_mknod.hpp" +#include "ugid.hpp" + + +#if defined __linux__ +namespace fs +{ + template + 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 + 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 diff --git a/src/fs_movefile_and_open.cpp b/src/fs_movefile_and_open.cpp index 9a9704e8..f59103cd 100644 --- a/src/fs_movefile_and_open.cpp +++ b/src/fs_movefile_and_open.cpp @@ -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_, diff --git a/src/fs_open_as.hpp b/src/fs_open_as.hpp new file mode 100644 index 00000000..bc54f3b8 --- /dev/null +++ b/src/fs_open_as.hpp @@ -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 diff --git a/src/fs_symlink_as.hpp b/src/fs_symlink_as.hpp new file mode 100644 index 00000000..7f71bcca --- /dev/null +++ b/src/fs_symlink_as.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "fs_symlink.hpp" +#include "ugid.hpp" + + +#if defined __linux__ +namespace fs +{ + template + 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 + 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 diff --git a/src/fuse_access.cpp b/src/fuse_access.cpp index ae3419c8..521b75ab 100644 --- a/src/fuse_access.cpp +++ b/src/fuse_access.cpp @@ -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, diff --git a/src/fuse_chmod.cpp b/src/fuse_chmod.cpp index aa3e1d59..79a8b1be 100644 --- a/src/fuse_chmod.cpp +++ b/src/fuse_chmod.cpp @@ -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_); } diff --git a/src/fuse_chown.cpp b/src/fuse_chown.cpp index 2653a44e..2abb37b6 100644 --- a/src/fuse_chown.cpp +++ b/src/fuse_chown.cpp @@ -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, diff --git a/src/fuse_create.cpp b/src/fuse_create.cpp index 253c3dc6..081628fa 100644 --- a/src/fuse_create.cpp +++ b/src/fuse_create.cpp @@ -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_, diff --git a/src/fuse_getattr.cpp b/src/fuse_getattr.cpp index 2dfe0096..6c441004 100644 --- a/src/fuse_getattr.cpp +++ b/src/fuse_getattr.cpp @@ -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_); } diff --git a/src/fuse_getxattr.cpp b/src/fuse_getxattr.cpp index 5da8b12f..7e09ef4f 100644 --- a/src/fuse_getxattr.cpp +++ b/src/fuse_getxattr.cpp @@ -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, diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index 1313e0f6..4f3f28c8 100644 --- a/src/fuse_init.cpp +++ b/src/fuse_init.cpp @@ -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); diff --git a/src/fuse_ioctl.cpp b/src/fuse_ioctl.cpp index 28e5a086..969bf2c1 100644 --- a/src/fuse_ioctl.cpp +++ b/src/fuse_ioctl.cpp @@ -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, diff --git a/src/fuse_link.cpp b/src/fuse_link.cpp index 9bbc4f7f..64be0135 100644 --- a/src/fuse_link.cpp +++ b/src/fuse_link.cpp @@ -55,7 +55,9 @@ _link_create_path_loop(const std::vector &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) diff --git a/src/fuse_listxattr.cpp b/src/fuse_listxattr.cpp index e7cadca8..7ae092ff 100644 --- a/src/fuse_listxattr.cpp +++ b/src/fuse_listxattr.cpp @@ -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, diff --git a/src/fuse_mkdir.cpp b/src/fuse_mkdir.cpp index 883f4ac2..854ec2ce 100644 --- a/src/fuse_mkdir.cpp +++ b/src/fuse_mkdir.cpp @@ -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 &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, diff --git a/src/fuse_mknod.cpp b/src/fuse_mknod.cpp index 80d4f853..a1df63f1 100644 --- a/src/fuse_mknod.cpp +++ b/src/fuse_mknod.cpp @@ -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 &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, diff --git a/src/fuse_open.cpp b/src/fuse_open.cpp index 6723380a..62de6d9a 100644 --- a/src/fuse_open.cpp +++ b/src/fuse_open.cpp @@ -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_); diff --git a/src/fuse_passthrough.hpp b/src/fuse_passthrough.hpp index fcccf817..99f8a7ea 100644 --- a/src/fuse_passthrough.hpp +++ b/src/fuse_passthrough.hpp @@ -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_); } } diff --git a/src/fuse_readdir_cor.cpp b/src/fuse_readdir_cor.cpp index f61a0e61..9c614e6b 100644 --- a/src/fuse_readdir_cor.cpp +++ b/src/fuse_readdir_cor.cpp @@ -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_); } diff --git a/src/fuse_readdir_cosr.cpp b/src/fuse_readdir_cosr.cpp index c7e39a1a..0add595d 100644 --- a/src/fuse_readdir_cosr.cpp +++ b/src/fuse_readdir_cosr.cpp @@ -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> 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_); } diff --git a/src/fuse_readdir_cosr_getdents.icpp b/src/fuse_readdir_cosr_getdents.icpp index 40e3992d..e4dcdb30 100644 --- a/src/fuse_readdir_cosr_getdents.icpp +++ b/src/fuse_readdir_cosr_getdents.icpp @@ -28,9 +28,7 @@ inline std::vector> _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> 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_; diff --git a/src/fuse_readdir_cosr_readdir.icpp b/src/fuse_readdir_cosr_readdir.icpp index ec76060e..3ff12bfc 100644 --- a/src/fuse_readdir_cosr_readdir.icpp +++ b/src/fuse_readdir_cosr_readdir.icpp @@ -37,9 +37,7 @@ inline std::vector> _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> 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_; diff --git a/src/fuse_readdir_seq.cpp b/src/fuse_readdir_seq.cpp index ee27021c..370a0985 100644 --- a/src/fuse_readdir_seq.cpp +++ b/src/fuse_readdir_seq.cpp @@ -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, diff --git a/src/fuse_readlink.cpp b/src/fuse_readlink.cpp index 989db75c..3a0123eb 100644 --- a/src/fuse_readlink.cpp +++ b/src/fuse_readlink.cpp @@ -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, diff --git a/src/fuse_removexattr.cpp b/src/fuse_removexattr.cpp index 87a2dcaf..c952d98a 100644 --- a/src/fuse_removexattr.cpp +++ b/src/fuse_removexattr.cpp @@ -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, diff --git a/src/fuse_rename.cpp b/src/fuse_rename.cpp index 224da590..f775c2fa 100644 --- a/src/fuse_rename.cpp +++ b/src/fuse_rename.cpp @@ -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) diff --git a/src/fuse_rmdir.cpp b/src/fuse_rmdir.cpp index 29b29d6f..5a7cff2e 100644 --- a/src/fuse_rmdir.cpp +++ b/src/fuse_rmdir.cpp @@ -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, diff --git a/src/fuse_setxattr.cpp b/src/fuse_setxattr.cpp index 6bbd0319..23dacb07 100644 --- a/src/fuse_setxattr.cpp +++ b/src/fuse_setxattr.cpp @@ -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, diff --git a/src/fuse_statfs.cpp b/src/fuse_statfs.cpp index 2ddd343a..c230881c 100644 --- a/src/fuse_statfs.cpp +++ b/src/fuse_statfs.cpp @@ -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, diff --git a/src/fuse_statx_supported.icpp b/src/fuse_statx_supported.icpp index 32089583..6e99e9ef 100644 --- a/src/fuse_statx_supported.icpp +++ b/src/fuse_statx_supported.icpp @@ -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_, diff --git a/src/fuse_symlink.cpp b/src/fuse_symlink.cpp index ae5803ce..7a18a08f 100644 --- a/src/fuse_symlink.cpp +++ b/src/fuse_symlink.cpp @@ -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 &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_); +} diff --git a/src/fuse_truncate.cpp b/src/fuse_truncate.cpp index ff0ad2f3..f3008268 100644 --- a/src/fuse_truncate.cpp +++ b/src/fuse_truncate.cpp @@ -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, diff --git a/src/fuse_unlink.cpp b/src/fuse_unlink.cpp index 144c9264..a870c930 100644 --- a/src/fuse_unlink.cpp +++ b/src/fuse_unlink.cpp @@ -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, diff --git a/src/fuse_utimens.cpp b/src/fuse_utimens.cpp index b383b466..485a14f2 100644 --- a/src/fuse_utimens.cpp +++ b/src/fuse_utimens.cpp @@ -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, diff --git a/src/gidcache.cpp b/src/gidcache.cpp deleted file mode 100644 index d84dff21..00000000 --- a/src/gidcache.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - Copyright (c) 2016, Antonio SJ Musumeci - - 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 -#include -#include -#include -#include - -#if defined __linux__ and UGID_USE_RWLOCK == 0 -# include -#elif __APPLE__ -# include -#endif - -int GIDCache::expire_timeout = (60 * 60); -int GIDCache::remove_timeout = (60 * 60 * 12); -boost::concurrent_flat_map 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 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 &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); -} diff --git a/src/gidcache.hpp b/src/gidcache.hpp deleted file mode 100644 index 957422cc..00000000 --- a/src/gidcache.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright (c) 2016, Antonio SJ Musumeci - - 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 -#include - -#include - -struct GIDRecord -{ - std::vector 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 _records; -}; diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index 207a3bc1..f7167fb6 100644 --- a/src/mergerfs.cpp +++ b/src/mergerfs.cpp @@ -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); diff --git a/src/policy_ff.cpp b/src/policy_ff.cpp index c80d1840..4d0daf54 100644 --- a/src/policy_ff.cpp +++ b/src/policy_ff.cpp @@ -16,6 +16,8 @@ #include "policy_ff.hpp" +#include "ugid.hpp" + #include "errno.hpp" #include "fs_exists.hpp" #include "fs_info.hpp" diff --git a/src/predictability.h b/src/predictability.h new file mode 100644 index 00000000..d41ced57 --- /dev/null +++ b/src/predictability.h @@ -0,0 +1,4 @@ +#pragma once + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) diff --git a/src/ugid.cpp b/src/ugid.cpp index 81a8ff58..cdf8b33d 100644 --- a/src/ugid.cpp +++ b/src/ugid.cpp @@ -1,33 +1,8 @@ -/* - Copyright (c) 2016, Antonio SJ Musumeci - - 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 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; } diff --git a/src/ugid.hpp b/src/ugid.hpp index 8adf47bb..29e9c02b 100644 --- a/src/ugid.hpp +++ b/src/ugid.hpp @@ -16,21 +16,112 @@ #pragma once +#include "fuse_req_ctx.h" +#include "predictability.h" + +#include "fuse_kernel.h" + +#include +#include +#include #include #include -#include -namespace ugid +struct ugid_t { - void init(); - void initgroups(const uid_t uid, const gid_t gid); -} + 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 __linux__ and UGID_USE_RWLOCK == 0 -#pragma message "using ugid_linux.hpp" -#include "ugid_linux.hpp" +#if defined SYS_getegid32 +#define GETEGID() (::syscall(SYS_getegid32)) #else -#pragma message "using ugid_rwlock.hpp" -#include "ugid_rwlock.hpp" +#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; + + 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; + }; +} + +#undef SETREUID +#undef SETREGID +#undef GETEUID +#undef GETEGID diff --git a/src/ugid_linux.hpp b/src/ugid_linux.hpp deleted file mode 100644 index 422904bd..00000000 --- a/src/ugid_linux.hpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - Copyright (c) 2016, Antonio SJ Musumeci - - 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 -#include -#include -#include -#include - -#include -#include - -#include -#include - -#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 diff --git a/src/ugid_linux.icpp b/src/ugid_linux.icpp deleted file mode 100644 index bd768fe5..00000000 --- a/src/ugid_linux.icpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright (c) 2016, Antonio SJ Musumeci - - 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 -#include -#include -#include - -#include - -namespace ugid -{ - thread_local uid_t currentuid = 0; - thread_local gid_t currentgid = 0; - thread_local bool initialized = false; - - void - init() - { - } -} diff --git a/src/ugid_rwlock.hpp b/src/ugid_rwlock.hpp deleted file mode 100644 index d0bf4fc5..00000000 --- a/src/ugid_rwlock.hpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright (c) 2016, Antonio SJ Musumeci - - 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 -#include -#include -#include - -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; - }; -} diff --git a/src/ugid_rwlock.icpp b/src/ugid_rwlock.icpp deleted file mode 100644 index 6642102d..00000000 --- a/src/ugid_rwlock.icpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright (c) 2016, Antonio SJ Musumeci - - 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 -#include -#include -#include - -#include - -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(); - } -}