new features: follow-symlinks, rename-exdev, link-exdev

* follow-symlinks: allows mergerfs to transparently follow symlinks
* link-exdev: in the event a link returns EXDEV create a symlink instead
* rename-exdev: in the event a rename returns EXDEV move the oldpath and
  create a symlink for the newpath
This commit is contained in:
Antonio SJ Musumeci 2020-06-21 14:45:14 -04:00
parent 50daf84ec0
commit 8adebc9489
26 changed files with 6980 additions and 450 deletions

View File

@ -40,7 +40,7 @@ USE_XATTR = 1
UGID_USE_RWLOCK = 0 UGID_USE_RWLOCK = 0
ifeq ($(DEBUG),1) ifeq ($(DEBUG),1)
OPT_FLAGS := -O0 -g OPT_FLAGS := -O0 -g -fsanitize=undefined
else else
OPT_FLAGS := -O2 OPT_FLAGS := -O2
endif endif

View File

@ -113,6 +113,9 @@ These options are the same regardless you use them with the `mergerfs` commandli
* **statfs=base|full**: Controls how statfs works. 'base' means it will always use all branches in statfs calculations. 'full' is in effect path preserving and only includes drives where the path exists. (default: base) * **statfs=base|full**: Controls how statfs works. 'base' means it will always use all branches in statfs calculations. 'full' is in effect path preserving and only includes drives where the path exists. (default: base)
* **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to ignore available space for branches mounted or tagged as 'read-only' or 'no create'. 'nc' will ignore available space for branches tagged as 'no create'. (default: none) * **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to ignore available space for branches mounted or tagged as 'read-only' or 'no create'. 'nc' will ignore available space for branches tagged as 'no create'. (default: none)
* **nfsopenhack=off|git|all**: A workaround for exporting mergerfs over NFS where there are issues with creating files for write while setting the mode to read-only. (default: off) * **nfsopenhack=off|git|all**: A workaround for exporting mergerfs over NFS where there are issues with creating files for write while setting the mode to read-only. (default: off)
* **follow-symlinks=never|directory|regular|all**: Turns symlinks into what they point to. (default: never)
* **link-exdev=passthrough|rel-symlink|abs-base-symlink|abs-pool-symlink**: When a link fails with EXDEV optionally create a symlink to the file instead.
* **rename-exdev=passthrough|rel-symlink|abs-symlink**: When a rename fails with EXDEV optionally move the file to a special directory and symlink to it.
* **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel and underlying filesystem). (default: false) * **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel and underlying filesystem). (default: false)
* **async_read=BOOL**: Perform reads asynchronously. If disabled or unavailable the kernel will ensure there is at most one pending read request per file handle and will attempt to order requests by offset. (default: true) * **async_read=BOOL**: Perform reads asynchronously. If disabled or unavailable the kernel will ensure there is at most one pending read request per file handle and will attempt to order requests by offset. (default: true)
* **fuse_msg_size=UINT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256) * **fuse_msg_size=UINT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256)
@ -240,6 +243,54 @@ In Linux 4.20 a new feature was added allowing the negotiation of the max messag
Since there should be no downsides to increasing `fuse_msg_size` / `max_pages`, outside a minor bump in RAM usage due to larger message buffers, mergerfs defaults the value to 256. On kernels before 4.20 the value has no effect. The reason the value is configurable is to enable experimentation and benchmarking. See the BENCHMARKING section for examples. Since there should be no downsides to increasing `fuse_msg_size` / `max_pages`, outside a minor bump in RAM usage due to larger message buffers, mergerfs defaults the value to 256. On kernels before 4.20 the value has no effect. The reason the value is configurable is to enable experimentation and benchmarking. See the BENCHMARKING section for examples.
### follow-symlinks
This feature, when enabled, will cause symlinks to be interpreted by mergerfs as their target (depending on the mode).
When there is a getattr/stat request for a file mergerfs will check if the file is a symlink and depending on the `follow-symlinks` setting will replace the information about the symlink with that of that which it points to.
When unlink'ing or rmdir'ing the followed symlink it will remove the symlink itself and not that which it points to.
* never: Behave as normal. Symlinks are treated as such.
* directory: Resolve symlinks only which point to directories.
* regular: Resolve symlinks only which point to regular files.
* all: Resolve all symlinks to that which they point to.
Symlinks which do not point to anything are left as is.
WARNING: This feature works but there might be edge cases yet found. If you find any odd behaviors please file a ticket on [github](https://github.com/trapexit/mergerfs/issues).
### link-exdev
If using path preservation and a `link` fails with EXDEV make a call to `symlink` where the `target` is the `oldlink` and the `linkpath` is the `newpath`. The `target` value is determined by the value of `link-exdev`.
* passthrough: Return EXDEV as normal.
* rel-symlink: A relative path from the `newpath`.
* abs-base-symlink: A absolute value using the underlying branch.
* abs-pool-symlink: A absolute value using the mergerfs mount point.
NOTE: It is possible that some applications check the file they link. In those cases it is possible it will error or complain.
### rename-exdev
If using path preservation and a `rename` fails with EXDEV:
1. Move file from **/branch/a/b/c** to **/branch/.mergerfs_rename_exdev/a/b/c**.
2. symlink the rename's `newpath` to the moved file.
The `target` value is determined by the value of `rename-exdev`.
* passthrough: Return EXDEV as normal.
* rel-symlink: A relative path from the `newpath`.
* abs-symlink: A absolute value using the mergerfs mount point.
NOTE: It is possible that some applications check the file they rename. In those cases it is possible it will error or complain.
NOTE: The reason `abs-symlink` is not split into two like `link-exdev` is due to the complexities in managing absolute base symlinks when multiple `oldpaths` exist.
### symlinkify ### symlinkify
Due to the levels of indirection introduced by mergerfs and the underlying technology FUSE there can be varying levels of performance degradation. This feature will turn non-directories which are not writable into symlinks to the original file found by the `readlink` policy after the mtime and ctime are older than the timeout. Due to the levels of indirection introduced by mergerfs and the underlying technology FUSE there can be varying levels of performance degradation. This feature will turn non-directories which are not writable into symlinks to the original file found by the `readlink` policy after the mtime and ctime are older than the timeout.

View File

@ -10,7 +10,7 @@ INSTALLUTILS :=
endif endif
ifeq ($(DEBUG),1) ifeq ($(DEBUG),1)
OPT_FLAGS := -O0 -g OPT_FLAGS := -O0 -g -fsanitize=undefined
else else
OPT_FLAGS := -O2 OPT_FLAGS := -O2
endif endif

View File

@ -121,13 +121,13 @@ struct fuse_operations
int (*rmdir) (const char *); int (*rmdir) (const char *);
/** Create a symbolic link */ /** Create a symbolic link */
int (*symlink) (const char *, const char *); int (*symlink) (const char *, const char *, struct stat *, fuse_timeouts_t *);
/** Rename a file */ /** Rename a file */
int (*rename) (const char *, const char *); int (*rename) (const char *, const char *);
/** Create a hard link to a file */ /** Create a hard link to a file */
int (*link) (const char *, const char *); int (*link) (const char *, const char *, struct stat *, fuse_timeouts_t *);
/** Change the permission bits of a file */ /** Change the permission bits of a file */
int (*chmod) (const char *, mode_t); int (*chmod) (const char *, mode_t);
@ -794,9 +794,16 @@ int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
const char *newpath); const char *newpath);
int fuse_fs_unlink(struct fuse_fs *fs, const char *path); int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, int fuse_fs_symlink(struct fuse_fs *fs,
const char *path); const char *linkname,
int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); const char *path,
struct stat *st,
fuse_timeouts_t *timeouts);
int fuse_fs_link(struct fuse_fs *fs,
const char *oldpath,
const char *newpath,
struct stat *st,
fuse_timeouts_t *timeouts);
int fuse_fs_release(struct fuse_fs *fs, int fuse_fs_release(struct fuse_fs *fs,
fuse_file_info_t *fi); fuse_file_info_t *fi);
int fuse_fs_open(struct fuse_fs *fs, const char *path, int fuse_fs_open(struct fuse_fs *fs, const char *path,

View File

@ -1650,25 +1650,30 @@ fuse_fs_rmdir(struct fuse_fs *fs,
} }
int int
fuse_fs_symlink(struct fuse_fs *fs, fuse_fs_symlink(struct fuse_fs *fs_,
const char *linkname, const char *linkname_,
const char *path) const char *path_,
struct stat *st_,
fuse_timeouts_t *timeouts_)
{ {
if(fs->op.symlink == NULL)
if(fs_->op.symlink == NULL)
return -ENOSYS; return -ENOSYS;
fuse_get_context()->private_data = fs->user_data; if(fs_->debug)
fprintf(stderr,"symlink %s %s\n",linkname_,path_);
if(fs->debug) fuse_get_context()->private_data = fs_->user_data;
fprintf(stderr,"symlink %s %s\n",linkname,path);
return fs->op.symlink(linkname,path); return fs_->op.symlink(linkname_,path_,st_,timeouts_);
} }
int int
fuse_fs_link(struct fuse_fs *fs, fuse_fs_link(struct fuse_fs *fs,
const char *oldpath, const char *oldpath,
const char *newpath) const char *newpath,
struct stat *st_,
fuse_timeouts_t *timeouts_)
{ {
if(fs->op.link == NULL) if(fs->op.link == NULL)
return -ENOSYS; return -ENOSYS;
@ -1678,7 +1683,7 @@ fuse_fs_link(struct fuse_fs *fs,
if(fs->debug) if(fs->debug)
fprintf(stderr,"link %s %s\n",oldpath,newpath); fprintf(stderr,"link %s %s\n",oldpath,newpath);
return fs->op.link(oldpath,newpath); return fs->op.link(oldpath,newpath,st_,timeouts_);
} }
int int
@ -2541,6 +2546,37 @@ update_stat(struct node *node_,
node_->mtim = stnew_->st_mtim; node_->mtim = stnew_->st_mtim;
} }
static
int
set_path_info(struct fuse *f,
fuse_ino_t nodeid,
const char *name,
struct fuse_entry_param *e)
{
struct node *node;
node = find_node(f,nodeid,name);
if(node == NULL)
return -ENOMEM;
e->ino = node->nodeid;
e->generation = node->generation;
pthread_mutex_lock(&f->lock);
update_stat(node,&e->attr);
pthread_mutex_unlock(&f->lock);
set_stat(f,e->ino,&e->attr);
if(f->conf.debug)
fprintf(stderr,
" NODEID: %llu\n"
" GEN: %llu\n",
(unsigned long long)e->ino,
(unsigned long long)e->generation);
return 0;
}
static static
int int
lookup_path(struct fuse *f, lookup_path(struct fuse *f,
@ -2550,43 +2586,18 @@ lookup_path(struct fuse *f,
struct fuse_entry_param *e, struct fuse_entry_param *e,
fuse_file_info_t *fi) fuse_file_info_t *fi)
{ {
int res; int rv;
memset(e,0,sizeof(struct fuse_entry_param)); memset(e,0,sizeof(struct fuse_entry_param));
res = ((fi == NULL) ? rv = ((fi == NULL) ?
fuse_fs_getattr(f->fs,path,&e->attr,&e->timeout) : fuse_fs_getattr(f->fs,path,&e->attr,&e->timeout) :
fuse_fs_fgetattr(f->fs,&e->attr,fi,&e->timeout)); fuse_fs_fgetattr(f->fs,&e->attr,fi,&e->timeout));
if(res == 0) if(rv)
{ return rv;
struct node *node;
node = find_node(f,nodeid,name); return set_path_info(f,nodeid,name,e);
if(node == NULL)
{
res = -ENOMEM;
}
else
{
e->ino = node->nodeid;
e->generation = node->generation;
pthread_mutex_lock(&f->lock);
update_stat(node,&e->attr);
pthread_mutex_unlock(&f->lock);
set_stat(f,e->ino,&e->attr);
if(f->conf.debug)
fprintf(stderr,
" NODEID: %llu\n"
" GEN: %llu\n",
(unsigned long long)e->ino,
(unsigned long long)e->generation);
}
}
return res;
} }
static static
@ -3236,26 +3247,28 @@ fuse_lib_rmdir(fuse_req_t req,
static static
void void
fuse_lib_symlink(fuse_req_t req, fuse_lib_symlink(fuse_req_t req_,
const char *linkname, const char *linkname_,
fuse_ino_t parent, fuse_ino_t parent_,
const char *name) const char *name_)
{ {
struct fuse *f = req_fuse_prepare(req); int rv;
struct fuse_entry_param e;
char *path; char *path;
int err; struct fuse *f;
struct fuse_entry_param e = {0};
err = get_path_name(f,parent,name,&path); f = req_fuse_prepare(req_);
if(!err)
rv = get_path_name(f,parent_,name_,&path);
if(rv == 0)
{ {
err = fuse_fs_symlink(f->fs,linkname,path); rv = fuse_fs_symlink(f->fs,linkname_,path,&e.attr,&e.timeout);
if(!err) if(rv == 0)
err = lookup_path(f,parent,name,path,&e,NULL); rv = set_path_info(f,parent_,name_,&e);
free_path(f,parent,path); free_path(f,parent_,path);
} }
reply_entry(req,&e,err); reply_entry(req_,&e,rv);
} }
static static
@ -3298,27 +3311,32 @@ fuse_lib_rename(fuse_req_t req,
reply_err(req,err); reply_err(req,err);
} }
static void fuse_lib_link(fuse_req_t req,fuse_ino_t ino,fuse_ino_t newparent, static
const char *newname) void
fuse_lib_link(fuse_req_t req,
fuse_ino_t ino,
fuse_ino_t newparent,
const char *newname)
{ {
struct fuse *f = req_fuse_prepare(req); int rv;
struct fuse_entry_param e;
char *oldpath; char *oldpath;
char *newpath; char *newpath;
int err; struct fuse *f;
struct fuse_entry_param e = {0};
err = get_path2(f,ino,NULL,newparent,newname, f = req_fuse_prepare(req);
rv = get_path2(f,ino,NULL,newparent,newname,
&oldpath,&newpath,NULL,NULL); &oldpath,&newpath,NULL,NULL);
if(!err) if(!rv)
{ {
err = fuse_fs_link(f->fs,oldpath,newpath); rv = fuse_fs_link(f->fs,oldpath,newpath,&e.attr,&e.timeout);
if(!err) if(rv == 0)
err = lookup_path(f,newparent,newname,newpath, rv = set_path_info(f,newparent,newname,&e);
&e,NULL);
free_path2(f,ino,newparent,NULL,NULL,oldpath,newpath); free_path2(f,ino,newparent,NULL,NULL,oldpath,newpath);
} }
reply_entry(req,&e,err); reply_entry(req,&e,rv);
} }
static static

View File

@ -223,6 +223,18 @@ over NFS where there are issues with creating files for write while
setting the mode to read\-only. setting the mode to read\-only.
(default: off) (default: off)
.IP \[bu] 2 .IP \[bu] 2
\f[B]follow\-symlinks=never|directory|regular|all\f[]: Turns symlinks
into what they point to.
(default: never)
.IP \[bu] 2
\f[B]link\-exdev=passthrough|rel\-symlink|abs\-base\-symlink|abs\-pool\-symlink\f[]:
When a link fails with EXDEV optionally create a symlink to the file
instead.
.IP \[bu] 2
\f[B]rename\-exdev=passthrough|rel\-symlink|abs\-symlink\f[]: When a
rename fails with EXDEV optionally move the file to a special directory
and symlink to it.
.IP \[bu] 2
\f[B]posix_acl=BOOL\f[]: Enable POSIX ACL support (if supported by \f[B]posix_acl=BOOL\f[]: Enable POSIX ACL support (if supported by
kernel and underlying filesystem). kernel and underlying filesystem).
(default: false) (default: false)
@ -542,6 +554,75 @@ On kernels before 4.20 the value has no effect.
The reason the value is configurable is to enable experimentation and The reason the value is configurable is to enable experimentation and
benchmarking. benchmarking.
See the BENCHMARKING section for examples. See the BENCHMARKING section for examples.
.SS follow\-symlinks
.PP
This feature, when enabled, will cause symlinks to be interpreted by
mergerfs as their target (depending on the mode).
.PP
When there is a getattr/stat request for a file mergerfs will check if
the file is a symlink and depending on the \f[C]follow\-symlinks\f[]
setting will replace the information about the symlink with that of that
which it points to.
.PP
When unlink\[aq]ing or rmdir\[aq]ing the followed symlink it will remove
the symlink itself and not that which it points to.
.IP \[bu] 2
never: Behave as normal.
Symlinks are treated as such.
.IP \[bu] 2
directory: Resolve symlinks only which point to directories.
.IP \[bu] 2
regular: Resolve symlinks only which point to regular files.
.IP \[bu] 2
all: Resolve all symlinks to that which they point to.
.PP
Symlinks which do not point to anything are left as is.
.PP
WARNING: This feature works but there might be edge cases yet found.
If you find any odd behaviors please file a ticket on
github (https://github.com/trapexit/mergerfs/issues).
.SS link\-exdev
.PP
If using path preservation and a \f[C]link\f[] fails with EXDEV make a
call to \f[C]symlink\f[] where the \f[C]target\f[] is the
\f[C]oldlink\f[] and the \f[C]linkpath\f[] is the \f[C]newpath\f[].
The \f[C]target\f[] value is determined by the value of
\f[C]link\-exdev\f[].
.IP \[bu] 2
passthrough: Return EXDEV as normal.
.IP \[bu] 2
rel\-symlink: A relative path from the \f[C]newpath\f[].
.IP \[bu] 2
abs\-base\-symlink: A absolute value using the underlying branch.
.IP \[bu] 2
abs\-pool\-symlink: A absolute value using the mergerfs mount point.
.PP
NOTE: It is possible that some applications check the file they link.
In those cases it is possible it will error or complain.
.SS rename\-exdev
.PP
If using path preservation and a \f[C]rename\f[] fails with EXDEV:
.IP "1." 3
Move file from \f[B]/branch/a/b/c\f[] to
\f[B]/branch/.mergerfs_rename_exdev/a/b/c\f[].
.IP "2." 3
symlink the rename\[aq]s \f[C]newpath\f[] to the moved file.
.PP
The \f[C]target\f[] value is determined by the value of
\f[C]rename\-exdev\f[].
.IP \[bu] 2
passthrough: Return EXDEV as normal.
.IP \[bu] 2
rel\-symlink: A relative path from the \f[C]newpath\f[].
.IP \[bu] 2
abs\-symlink: A absolute value using the mergerfs mount point.
.PP
NOTE: It is possible that some applications check the file they rename.
In those cases it is possible it will error or complain.
.PP
NOTE: The reason \f[C]abs\-symlink\f[] is not split into two like
\f[C]link\-exdev\f[] is due to the complexities in managing absolute
base symlinks when multiple \f[C]oldpaths\f[] exist.
.SS symlinkify .SS symlinkify
.PP .PP
Due to the levels of indirection introduced by mergerfs and the Due to the levels of indirection introduced by mergerfs and the

View File

@ -71,6 +71,7 @@ namespace l
Config::Config() Config::Config()
: async_read(true), : async_read(true),
auto_cache(false), auto_cache(false),
minfreespace(MINFREESPACE_DEFAULT),
branches(minfreespace), branches(minfreespace),
cache_attr(1), cache_attr(1),
cache_entry(1), cache_entry(1),
@ -83,12 +84,13 @@ Config::Config()
direct_io(false), direct_io(false),
dropcacheonclose(false), dropcacheonclose(false),
fsname(), fsname(),
follow_symlinks(FollowSymlinks::ENUM::NEVER),
func(), func(),
fuse_msg_size(FUSE_MAX_MAX_PAGES), fuse_msg_size(FUSE_MAX_MAX_PAGES),
ignorepponrename(false), ignorepponrename(false),
inodecalc("hybrid-hash"), inodecalc("hybrid-hash"),
link_cow(false), link_cow(false),
minfreespace(MINFREESPACE_DEFAULT), link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
mount(), mount(),
moveonenospc(false), moveonenospc(false),
nfsopenhack(NFSOpenHack::ENUM::OFF), nfsopenhack(NFSOpenHack::ENUM::OFF),
@ -97,6 +99,7 @@ Config::Config()
posix_acl(false), posix_acl(false),
readdir(ReadDir::ENUM::POSIX), readdir(ReadDir::ENUM::POSIX),
readdirplus(false), readdirplus(false),
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
security_capability(true), security_capability(true),
srcmounts(branches), srcmounts(branches),
statfs(StatFS::ENUM::BASE), statfs(StatFS::ENUM::BASE),
@ -124,6 +127,7 @@ Config::Config()
_map["category.search"] = &category.search; _map["category.search"] = &category.search;
_map["direct_io"] = &direct_io; _map["direct_io"] = &direct_io;
_map["dropcacheonclose"] = &dropcacheonclose; _map["dropcacheonclose"] = &dropcacheonclose;
_map["follow-symlinks"] = &follow_symlinks;
_map["fsname"] = &fsname; _map["fsname"] = &fsname;
_map["func.access"] = &func.access; _map["func.access"] = &func.access;
_map["func.chmod"] = &func.chmod; _map["func.chmod"] = &func.chmod;
@ -150,6 +154,7 @@ Config::Config()
_map["inodecalc"] = &inodecalc; _map["inodecalc"] = &inodecalc;
_map["kernel_cache"] = &kernel_cache; _map["kernel_cache"] = &kernel_cache;
_map["link_cow"] = &link_cow; _map["link_cow"] = &link_cow;
_map["link-exdev"] = &link_exdev;
_map["minfreespace"] = &minfreespace; _map["minfreespace"] = &minfreespace;
_map["mount"] = &mount; _map["mount"] = &mount;
_map["moveonenospc"] = &moveonenospc; _map["moveonenospc"] = &moveonenospc;
@ -159,6 +164,7 @@ Config::Config()
_map["posix_acl"] = &posix_acl; _map["posix_acl"] = &posix_acl;
// _map["readdir"] = &readdir; // _map["readdir"] = &readdir;
_map["readdirplus"] = &readdirplus; _map["readdirplus"] = &readdirplus;
_map["rename-exdev"] = &rename_exdev;
_map["security_capability"] = &security_capability; _map["security_capability"] = &security_capability;
_map["srcmounts"] = &srcmounts; _map["srcmounts"] = &srcmounts;
_map["statfs"] = &statfs; _map["statfs"] = &statfs;

View File

@ -19,10 +19,13 @@
#include "branches.hpp" #include "branches.hpp"
#include "category.hpp" #include "category.hpp"
#include "config_cachefiles.hpp" #include "config_cachefiles.hpp"
#include "config_follow_symlinks.hpp"
#include "config_inodecalc.hpp" #include "config_inodecalc.hpp"
#include "config_link_exdev.hpp"
#include "config_moveonenospc.hpp" #include "config_moveonenospc.hpp"
#include "config_nfsopenhack.hpp" #include "config_nfsopenhack.hpp"
#include "config_readdir.hpp" #include "config_readdir.hpp"
#include "config_rename_exdev.hpp"
#include "config_statfs.hpp" #include "config_statfs.hpp"
#include "config_statfsignore.hpp" #include "config_statfsignore.hpp"
#include "config_xattr.hpp" #include "config_xattr.hpp"
@ -38,7 +41,6 @@
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <memory> #include <memory>
#include <mutex>
#include <string> #include <string>
#include <vector> #include <vector>
@ -98,6 +100,7 @@ public:
public: public:
ConfigBOOL async_read; ConfigBOOL async_read;
ConfigBOOL auto_cache; ConfigBOOL auto_cache;
ConfigUINT64 minfreespace;
Branches branches; Branches branches;
ConfigUINT64 cache_attr; ConfigUINT64 cache_attr;
ConfigUINT64 cache_entry; ConfigUINT64 cache_entry;
@ -110,13 +113,14 @@ public:
ConfigBOOL direct_io; ConfigBOOL direct_io;
ConfigBOOL dropcacheonclose; ConfigBOOL dropcacheonclose;
ConfigSTR fsname; ConfigSTR fsname;
FollowSymlinks follow_symlinks;
Funcs func; Funcs func;
ConfigUINT64 fuse_msg_size; ConfigUINT64 fuse_msg_size;
ConfigBOOL ignorepponrename; ConfigBOOL ignorepponrename;
InodeCalc inodecalc; InodeCalc inodecalc;
ConfigBOOL kernel_cache; ConfigBOOL kernel_cache;
ConfigBOOL link_cow; ConfigBOOL link_cow;
ConfigUINT64 minfreespace; LinkEXDEV link_exdev;
ConfigSTR mount; ConfigSTR mount;
MoveOnENOSPC moveonenospc; MoveOnENOSPC moveonenospc;
NFSOpenHack nfsopenhack; NFSOpenHack nfsopenhack;
@ -125,6 +129,7 @@ public:
ConfigBOOL posix_acl; ConfigBOOL posix_acl;
ReadDir readdir; ReadDir readdir;
ConfigBOOL readdirplus; ConfigBOOL readdirplus;
RenameEXDEV rename_exdev;
ConfigBOOL security_capability; ConfigBOOL security_capability;
SrcMounts srcmounts; SrcMounts srcmounts;
StatFS statfs; StatFS statfs;

View File

@ -0,0 +1,58 @@
/*
ISC License
Copyright (c) 2020, 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 "config_follow_symlinks.hpp"
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
FollowSymlinks::to_string(void) const
{
switch(_data)
{
case FollowSymlinks::ENUM::NEVER:
return "never";
case FollowSymlinks::ENUM::DIRECTORY:
return "directory";
case FollowSymlinks::ENUM::REGULAR:
return "regular";
case FollowSymlinks::ENUM::ALL:
return "all";
}
return "invalid";
}
template<>
int
FollowSymlinks::from_string(const std::string &s_)
{
if(s_ == "never")
_data = FollowSymlinks::ENUM::NEVER;
ef(s_ == "directory")
_data = FollowSymlinks::ENUM::DIRECTORY;
ef(s_ == "regular")
_data = FollowSymlinks::ENUM::REGULAR;
ef(s_ == "all")
_data = FollowSymlinks::ENUM::ALL;
else
return -EINVAL;
return 0;
}

View File

@ -0,0 +1,30 @@
/*
ISC License
Copyright (c) 2020, 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 "enum.hpp"
enum class FollowSymlinksEnum
{
NEVER,
DIRECTORY,
REGULAR,
ALL
};
typedef Enum<FollowSymlinksEnum> FollowSymlinks;

58
src/config_link_exdev.cpp Normal file
View File

@ -0,0 +1,58 @@
/*
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 "config_link_exdev.hpp"
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
LinkEXDEV::to_string(void) const
{
switch(_data)
{
case LinkEXDEV::ENUM::PASSTHROUGH:
return "passthrough";
case LinkEXDEV::ENUM::REL_SYMLINK:
return "rel-symlink";
case LinkEXDEV::ENUM::ABS_BASE_SYMLINK:
return "abs-base-symlink";
case LinkEXDEV::ENUM::ABS_POOL_SYMLINK:
return "abs-pool-symlink";
}
return "invalid";
}
template<>
int
LinkEXDEV::from_string(const std::string &s_)
{
if(s_ == "passthrough")
_data = LinkEXDEV::ENUM::PASSTHROUGH;
ef(s_ == "rel-symlink")
_data = LinkEXDEV::ENUM::REL_SYMLINK;
ef(s_ == "abs-base-symlink")
_data = LinkEXDEV::ENUM::ABS_BASE_SYMLINK;
ef(s_ == "abs-pool-symlink")
_data = LinkEXDEV::ENUM::ABS_POOL_SYMLINK;
else
return -EINVAL;
return 0;
}

30
src/config_link_exdev.hpp Normal file
View File

@ -0,0 +1,30 @@
/*
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.
*/
#pragma once
#include "enum.hpp"
enum class LinkEXDEVEnum
{
PASSTHROUGH,
REL_SYMLINK,
ABS_BASE_SYMLINK,
ABS_POOL_SYMLINK
};
typedef Enum<LinkEXDEVEnum> LinkEXDEV;

View File

@ -0,0 +1,54 @@
/*
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 "config_rename_exdev.hpp"
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
RenameEXDEV::to_string(void) const
{
switch(_data)
{
case RenameEXDEV::ENUM::PASSTHROUGH:
return "passthrough";
case RenameEXDEV::ENUM::REL_SYMLINK:
return "rel-symlink";
case RenameEXDEV::ENUM::ABS_SYMLINK:
return "abs-symlink";
}
return "invalid";
}
template<>
int
RenameEXDEV::from_string(const std::string &s_)
{
if(s_ == "passthrough")
_data = RenameEXDEV::ENUM::PASSTHROUGH;
ef(s_ == "rel-symlink")
_data = RenameEXDEV::ENUM::REL_SYMLINK;
ef(s_ == "abs-symlink")
_data = RenameEXDEV::ENUM::ABS_SYMLINK;
else
return -EINVAL;
return 0;
}

View File

@ -0,0 +1,29 @@
/*
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.
*/
#pragma once
#include "enum.hpp"
enum class RenameEXDEVEnum
{
PASSTHROUGH,
REL_SYMLINK,
ABS_SYMLINK
};
typedef Enum<RenameEXDEVEnum> RenameEXDEV;

View File

@ -67,8 +67,8 @@ public:
} }
public: public:
std::string to_string() const; std::string to_string() const final;
int from_string(const std::string &); int from_string(const std::string &) final;
public: public:
int to_int() const int to_int() const

View File

@ -18,6 +18,8 @@
#pragma once #pragma once
#include "ghc/filesystem.hpp"
#include <string> #include <string>
#include <sys/stat.h> #include <sys/stat.h>
@ -26,12 +28,30 @@
namespace fs namespace fs
{ {
static
inline
int
mkdir(const char *path_,
const mode_t mode_)
{
return ::mkdir(path_,mode_);
}
static static
inline inline
int int
mkdir(const std::string &path_, mkdir(const std::string &path_,
const mode_t mode_) const mode_t mode_)
{ {
return ::mkdir(path_.c_str(),mode_); return fs::mkdir(path_.c_str(),mode_);
}
static
inline
int
mkdir(const ghc::filesystem::path &path_,
const mode_t mode_)
{
return fs::mkdir(path_.c_str(),mode_);
} }
} }

35
src/fs_mkdir_as_root.hpp Normal file
View File

@ -0,0 +1,35 @@
/*
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_);
}
}

View File

@ -28,30 +28,27 @@ namespace fs
static static
inline inline
int int
symlink(const char *oldpath_, symlink(const char *target_,
const char *newpath_) const char *linkpath_)
{ {
return ::symlink(oldpath_, return ::symlink(target_,linkpath_);
newpath_);
} }
static static
inline inline
int int
symlink(const std::string &oldpath_, symlink(const std::string &target_,
const std::string &newpath_) const std::string &linkpath_)
{ {
return fs::symlink(oldpath_.c_str(), return ::symlink(target_.c_str(),linkpath_.c_str());
newpath_.c_str());
} }
static static
inline inline
int int
symlink(const char *oldpath_, symlink(const char *target_,
const std::string &newpath_) const std::string &linkpath_)
{ {
return fs::symlink(oldpath_, return ::symlink(target_,linkpath_.c_str());
newpath_.c_str());
} }
} }

View File

@ -19,6 +19,7 @@
#include "fs_inode.hpp" #include "fs_inode.hpp"
#include "fs_lstat.hpp" #include "fs_lstat.hpp"
#include "fs_path.hpp" #include "fs_path.hpp"
#include "fs_stat.hpp"
#include "symlinkify.hpp" #include "symlinkify.hpp"
#include "ugid.hpp" #include "ugid.hpp"
@ -31,6 +32,42 @@ using std::string;
namespace l namespace l
{ {
static
void
set_stat_if_leads_to_dir(const std::string &path_,
struct stat *st_)
{
int rv;
struct stat st;
rv = fs::stat(path_,&st);
if(rv == -1)
return;
if(S_ISDIR(st.st_mode))
*st_ = st;
return;
}
static
void
set_stat_if_leads_to_reg(const std::string &path_,
struct stat *st_)
{
int rv;
struct stat st;
rv = fs::stat(path_,&st);
if(rv == -1)
return;
if(S_ISREG(st.st_mode))
*st_ = st;
return;
}
static static
int int
getattr_controlfile(struct stat *st_) getattr_controlfile(struct stat *st_)
@ -63,7 +100,8 @@ namespace l
const char *fusepath_, const char *fusepath_,
struct stat *st_, struct stat *st_,
const bool symlinkify_, const bool symlinkify_,
const time_t symlinkify_timeout_) const time_t symlinkify_timeout_,
FollowSymlinks followsymlinks_)
{ {
int rv; int rv;
string fullpath; string fullpath;
@ -75,7 +113,28 @@ namespace l
fullpath = fs::path::make(basepaths[0],fusepath_); fullpath = fs::path::make(basepaths[0],fusepath_);
rv = fs::lstat(fullpath,st_); switch(followsymlinks_)
{
case FollowSymlinks::ENUM::NEVER:
rv = fs::lstat(fullpath,st_);
break;
case FollowSymlinks::ENUM::DIRECTORY:
rv = fs::lstat(fullpath,st_);
if(S_ISLNK(st_->st_mode))
l::set_stat_if_leads_to_dir(fullpath,st_);
break;
case FollowSymlinks::ENUM::REGULAR:
rv = fs::lstat(fullpath,st_);
if(S_ISLNK(st_->st_mode))
l::set_stat_if_leads_to_reg(fullpath,st_);
break;
case FollowSymlinks::ENUM::ALL:
rv = fs::stat(fullpath,st_);
if(rv != 0)
rv = fs::lstat(fullpath,st_);
break;
}
if(rv == -1) if(rv == -1)
return -errno; return -errno;
@ -102,7 +161,8 @@ namespace l
fusepath_, fusepath_,
st_, st_,
cfg->symlinkify, cfg->symlinkify,
cfg->symlinkify_timeout); cfg->symlinkify_timeout,
cfg->follow_symlinks);
timeout_->entry = ((rv >= 0) ? timeout_->entry = ((rv >= 0) ?
cfg->cache_entry : cfg->cache_entry :

View File

@ -18,7 +18,11 @@
#include "errno.hpp" #include "errno.hpp"
#include "fs_clonepath.hpp" #include "fs_clonepath.hpp"
#include "fs_link.hpp" #include "fs_link.hpp"
#include "fs_lstat.hpp"
#include "fs_path.hpp" #include "fs_path.hpp"
#include "fuse_getattr.hpp"
#include "fuse_symlink.hpp"
#include "ghc/filesystem.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "fuse.h" #include "fuse.h"
@ -28,7 +32,7 @@
using std::string; using std::string;
using std::vector; using std::vector;
namespace gfs = ghc::filesystem;
namespace error namespace error
{ {
@ -52,47 +56,38 @@ namespace error
namespace l namespace l
{ {
static
int
link_create_path_core(const string &oldbasepath_,
const string &newbasepath_,
const char *oldfusepath_,
const char *newfusepath_,
const int error_)
{
int rv;
string oldfullpath;
string newfullpath;
oldfullpath = fs::path::make(oldbasepath_,oldfusepath_);
newfullpath = fs::path::make(oldbasepath_,newfusepath_);
rv = fs::link(oldfullpath,newfullpath);
return error::calc(rv,error_,errno);
}
static static
int int
link_create_path_loop(const StrVec &oldbasepaths_, link_create_path_loop(const StrVec &oldbasepaths_,
const string &newbasepath_, const string &newbasepath_,
const char *oldfusepath_, const char *oldfusepath_,
const char *newfusepath_, const char *newfusepath_,
const string &newfusedirpath_) const string &newfusedirpath_,
struct stat *st_)
{ {
int rv; int rv;
int error; int error;
string oldfullpath;
string newfullpath;
error = -1; error = -1;
for(size_t i = 0, ei = oldbasepaths_.size(); i != ei; i++) for(auto &oldbasepath : oldbasepaths_)
{ {
rv = fs::clonepath_as_root(newbasepath_,oldbasepaths_[i],newfusedirpath_); oldfullpath = fs::path::make(oldbasepath,oldfusepath_);
if(rv == -1) newfullpath = fs::path::make(oldbasepath,newfusepath_);
error = error::calc(rv,error,errno);
else rv = fs::link(oldfullpath,newfullpath);
error = l::link_create_path_core(oldbasepaths_[i],newbasepath_, if((rv == -1) && (errno == ENOENT))
oldfusepath_,newfusepath_, {
error); rv = fs::clonepath_as_root(newbasepath_,oldbasepath,newfusedirpath_);
if(rv == 0)
rv = fs::link(oldfullpath,newfullpath);
}
if((rv == 0) && (st_->st_ino == 0))
rv = fs::lstat(oldfullpath,st_);
error = error::calc(rv,error,errno);
} }
return -error; return -error;
@ -104,7 +99,8 @@ namespace l
const Policy::Action &actionFunc_, const Policy::Action &actionFunc_,
const Branches &branches_, const Branches &branches_,
const char *oldfusepath_, const char *oldfusepath_,
const char *newfusepath_) const char *newfusepath_,
struct stat *st_)
{ {
int rv; int rv;
string newfusedirpath; string newfusedirpath;
@ -123,47 +119,17 @@ namespace l
return l::link_create_path_loop(oldbasepaths,newbasepaths[0], return l::link_create_path_loop(oldbasepaths,newbasepaths[0],
oldfusepath_,newfusepath_, oldfusepath_,newfusepath_,
newfusedirpath); newfusedirpath,
st_);
} }
static static
int int
clonepath_if_would_create(const Policy::Search &searchFunc_, link_preserve_path_core(const string &oldbasepath_,
const Policy::Create &createFunc_, const char *oldfusepath_,
const Branches &branches_, const char *newfusepath_,
const string &oldbasepath_, struct stat *st_,
const char *oldfusepath_, const int error_)
const char *newfusepath_)
{
int rv;
string newfusedirpath;
StrVec newbasepath;
newfusedirpath = fs::path::dirname(newfusepath_);
rv = createFunc_(branches_,newfusedirpath,&newbasepath);
if(rv == -1)
return -1;
if(oldbasepath_ != newbasepath[0])
return (errno=EXDEV,-1);
rv = searchFunc_(branches_,newfusedirpath,&newbasepath);
if(rv == -1)
return -1;
return fs::clonepath_as_root(newbasepath[0],oldbasepath_,newfusedirpath);
}
static
int
link_preserve_path_core(const Policy::Search &searchFunc_,
const Policy::Create &createFunc_,
const Branches &branches_,
const string &oldbasepath_,
const char *oldfusepath_,
const char *newfusepath_,
const int error_)
{ {
int rv; int rv;
string oldfullpath; string oldfullpath;
@ -174,36 +140,29 @@ namespace l
rv = fs::link(oldfullpath,newfullpath); rv = fs::link(oldfullpath,newfullpath);
if((rv == -1) && (errno == ENOENT)) if((rv == -1) && (errno == ENOENT))
{ errno = EXDEV;
rv = l::clonepath_if_would_create(searchFunc_,createFunc_, if((rv == 0) && (st_->st_ino == 0))
branches_, rv = fs::lstat(oldfullpath,st_);
oldbasepath_,
oldfusepath_,newfusepath_);
if(rv != -1)
rv = fs::link(oldfullpath,newfullpath);
}
return error::calc(rv,error_,errno); return error::calc(rv,error_,errno);
} }
static static
int int
link_preserve_path_loop(const Policy::Search &searchFunc_, link_preserve_path_loop(const StrVec &oldbasepaths_,
const Policy::Create &createFunc_, const char *oldfusepath_,
const Branches &branches_, const char *newfusepath_,
const char *oldfusepath_, struct stat *st_)
const char *newfusepath_,
const StrVec &oldbasepaths_)
{ {
int error; int error;
error = -1; error = -1;
for(size_t i = 0, ei = oldbasepaths_.size(); i != ei; i++) for(auto &oldbasepath : oldbasepaths_)
{ {
error = l::link_preserve_path_core(searchFunc_,createFunc_, error = l::link_preserve_path_core(oldbasepath,
branches_, oldfusepath_,
oldbasepaths_[i], newfusepath_,
oldfusepath_,newfusepath_, st_,
error); error);
} }
@ -212,12 +171,11 @@ namespace l
static static
int int
link_preserve_path(const Policy::Search &searchFunc_, link_preserve_path(const Policy::Action &actionFunc_,
const Policy::Action &actionFunc_,
const Policy::Create &createFunc_,
const Branches &branches_, const Branches &branches_,
const char *oldfusepath_, const char *oldfusepath_,
const char *newfusepath_) const char *newfusepath_,
struct stat *st_)
{ {
int rv; int rv;
StrVec oldbasepaths; StrVec oldbasepaths;
@ -226,35 +184,183 @@ namespace l
if(rv == -1) if(rv == -1)
return -errno; return -errno;
return l::link_preserve_path_loop(searchFunc_,createFunc_, return l::link_preserve_path_loop(oldbasepaths,
branches_, oldfusepath_,
oldfusepath_,newfusepath_, newfusepath_,
oldbasepaths); st_);
}
static
int
link(Config::Read &cfg_,
const char *oldpath_,
const char *newpath_,
struct stat *st_)
{
if(cfg_->func.create.policy.path_preserving() && !cfg_->ignorepponrename)
return l::link_preserve_path(cfg_->func.link.policy,
cfg_->branches,
oldpath_,
newpath_,
st_);
return l::link_create_path(cfg_->func.getattr.policy,
cfg_->func.link.policy,
cfg_->branches,
oldpath_,
newpath_,
st_);
}
static
int
link(Config::Read &cfg_,
const char *oldpath_,
const char *newpath_,
struct stat *st_,
fuse_timeouts_t *timeouts_)
{
int rv;
rv = l::link(cfg_,oldpath_,newpath_,st_);
timeouts_->entry = ((rv >= 0) ?
cfg_->cache_entry :
cfg_->cache_negative_entry);
timeouts_->attr = cfg_->cache_attr;
return rv;
}
static
int
link_exdev_rel_symlink(const char *oldpath_,
const char *newpath_,
struct stat *st_,
fuse_timeouts_t *timeouts_)
{
int rv;
gfs::path target(oldpath_);
gfs::path linkpath(newpath_);
target = target.lexically_relative(linkpath.parent_path());
rv = FUSE::symlink(target.c_str(),linkpath.c_str());
if(rv == 0)
rv = FUSE::getattr(oldpath_,st_,timeouts_);
// Disable attr caching since we created a symlink but should be a regular.
timeouts_->attr = 0;
return rv;
}
static
int
link_exdev_abs_base_symlink(const Policy::Search &openPolicy_,
const Branches::CPtr &branches_,
const char *oldpath_,
const char *newpath_,
struct stat *st_,
fuse_timeouts_t *timeouts_)
{
int rv;
StrVec basepaths;
std::string target;
rv = openPolicy_(branches_,oldpath_,&basepaths);
if(rv == -1)
return -errno;
target = fs::path::make(basepaths[0],oldpath_);
rv = FUSE::symlink(target.c_str(),newpath_);
if(rv == 0)
rv = FUSE::getattr(oldpath_,st_,timeouts_);
// Disable attr caching since we created a symlink but should be a regular.
timeouts_->attr = 0;
return rv;
}
static
int
link_exdev_abs_pool_symlink(const std::string &mount_,
const char *oldpath_,
const char *newpath_,
struct stat *st_,
fuse_timeouts_t *timeouts_)
{
int rv;
StrVec basepaths;
std::string target;
target = fs::path::make(mount_,oldpath_);
rv = FUSE::symlink(target.c_str(),newpath_);
if(rv == 0)
rv = FUSE::getattr(oldpath_,st_,timeouts_);
// Disable attr caching since we created a symlink but should be a regular.
timeouts_->attr = 0;
return rv;
}
static
int
link_exdev(Config::Read &cfg_,
const char *oldpath_,
const char *newpath_,
struct stat *st_,
fuse_timeouts_t *timeouts_)
{
switch(cfg_->link_exdev)
{
case LinkEXDEV::ENUM::PASSTHROUGH:
return -EXDEV;
case LinkEXDEV::ENUM::REL_SYMLINK:
return l::link_exdev_rel_symlink(oldpath_,
newpath_,
st_,
timeouts_);
case LinkEXDEV::ENUM::ABS_BASE_SYMLINK:
return l::link_exdev_abs_base_symlink(cfg_->func.open.policy,
cfg_->branches,
oldpath_,
newpath_,
st_,
timeouts_);
case LinkEXDEV::ENUM::ABS_POOL_SYMLINK:
return l::link_exdev_abs_pool_symlink(cfg_->mount,
oldpath_,
newpath_,
st_,
timeouts_);
}
return -EXDEV;
} }
} }
namespace FUSE namespace FUSE
{ {
int int
link(const char *from_, link(const char *oldpath_,
const char *to_) const char *newpath_,
struct stat *st_,
fuse_timeouts_t *timeouts_)
{ {
int rv;
Config::Read cfg; Config::Read cfg;
const fuse_context *fc = fuse_get_context(); const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid); const ugid::Set ugid(fc->uid,fc->gid);
if(cfg->func.create.policy.path_preserving() && !cfg->ignorepponrename) rv = l::link(cfg,oldpath_,newpath_,st_,timeouts_);
return l::link_preserve_path(cfg->func.getattr.policy, if(rv == -EXDEV)
cfg->func.link.policy, return l::link_exdev(cfg,oldpath_,newpath_,st_,timeouts_);
cfg->func.create.policy,
cfg->branches,
from_,
to_);
return l::link_create_path(cfg->func.getattr.policy, return rv;
cfg->func.link.policy,
cfg->branches,
from_,
to_);
} }
} }

View File

@ -16,10 +16,14 @@
#pragma once #pragma once
#include "fuse.h"
namespace FUSE namespace FUSE
{ {
int int
link(const char *from, link(const char *oldpath,
const char *to); const char *newpath,
struct stat *st,
fuse_timeouts_t *timeouts);
} }

View File

@ -17,16 +17,26 @@
#include "config.hpp" #include "config.hpp"
#include "errno.hpp" #include "errno.hpp"
#include "fs_clonepath.hpp" #include "fs_clonepath.hpp"
#include "fs_link.hpp"
#include "fs_mkdir_as_root.hpp"
#include "fs_path.hpp" #include "fs_path.hpp"
#include "fs_remove.hpp" #include "fs_remove.hpp"
#include "fs_rename.hpp" #include "fs_rename.hpp"
#include "fs_symlink.hpp"
#include "fs_unlink.hpp"
#include "fuse_symlink.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "ghc/filesystem.hpp"
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <vector>
#include <iostream>
using std::string; using std::string;
namespace gfs = ghc::filesystem;
namespace error namespace error
{ {
@ -48,265 +58,351 @@ namespace error
} }
} }
static namespace l
bool
member(const StrVec &haystack,
const string &needle)
{ {
for(size_t i = 0, ei = haystack.size(); i != ei; i++) static
{ bool
if(haystack[i] == needle) contains(const StrVec &haystack_,
return true; const char *needle_)
} {
for(auto &hay : haystack_)
{
if(hay == needle_)
return true;
}
return false; return false;
} }
static static
void bool
_remove(const StrVec &toremove) contains(const StrVec &haystack_,
{ const string &needle_)
for(size_t i = 0, ei = toremove.size(); i != ei; i++) {
fs::remove(toremove[i]); return l::contains(haystack_,needle_.c_str());
} }
static static
void void
_rename_create_path_core(const StrVec &oldbasepaths, remove(const StrVec &toremove_)
const string &oldbasepath, {
const string &newbasepath, for(auto &path : toremove_)
const char *oldfusepath, fs::remove(path);
const char *newfusepath, }
const string &newfusedirpath,
int &error,
StrVec &tounlink)
{
int rv;
bool ismember;
string oldfullpath;
string newfullpath;
ismember = member(oldbasepaths,oldbasepath); static
if(ismember) void
{ remove(const Branches::CPtr &branches_,
rv = fs::clonepath_as_root(newbasepath,oldbasepath,newfusedirpath); const std::string &relpath_)
if(rv != -1) {
{ std::string fullpath;
oldfullpath = fs::path::make(oldbasepath,oldfusepath);
newfullpath = fs::path::make(oldbasepath,newfusepath);
rv = fs::rename(oldfullpath,newfullpath); for(auto &branch : *branches_)
} {
fullpath = fs::path::make(branch.path,relpath_);
fs::remove(fullpath);
}
}
error = error::calc(rv,error,errno); static
if(rv == -1) int
tounlink.push_back(oldfullpath); rename_create_path(const Policy::Search &searchPolicy_,
} const Policy::Action &actionPolicy_,
else const Branches::CPtr &branches_,
{ const gfs::path &oldfusepath_,
newfullpath = fs::path::make(oldbasepath,newfusepath); const gfs::path &newfusepath_)
{
int rv;
int error;
StrVec toremove;
StrVec newbasepath;
StrVec oldbasepaths;
gfs::path oldfullpath;
gfs::path newfullpath;
tounlink.push_back(newfullpath); rv = actionPolicy_(branches_,oldfusepath_,&oldbasepaths);
} if(rv == -1)
} return -errno;
static rv = searchPolicy_(branches_,newfusepath_.parent_path(),&newbasepath);
int if(rv == -1)
_rename_create_path(const Policy::Search &searchFunc, return -errno;
const Policy::Action &actionFunc,
const Branches::CPtr &branches_,
const char *oldfusepath,
const char *newfusepath)
{
int rv;
int error;
string newfusedirpath;
StrVec toremove;
StrVec newbasepath;
StrVec oldbasepaths;
StrVec branches;
rv = actionFunc(branches_,oldfusepath,&oldbasepaths); error = -1;
if(rv == -1) for(auto &branch : *branches_)
return -errno; {
newfullpath = branch.path;
newfullpath += newfusepath_;
newfusedirpath = fs::path::dirname(newfusepath); if(!l::contains(oldbasepaths,branch.path))
{
toremove.push_back(newfullpath);
continue;
}
rv = searchFunc(branches_,newfusedirpath.c_str(),&newbasepath); oldfullpath = branch.path;
if(rv == -1) oldfullpath += oldfusepath_;
return -errno;
branches_->to_paths(branches); rv = fs::rename(oldfullpath,newfullpath);
if(rv == -1)
{
rv = fs::clonepath_as_root(newbasepath[0],branch.path,newfusepath_.parent_path());
if(rv == 0)
rv = fs::rename(oldfullpath,newfullpath);
}
error = -1; error = error::calc(rv,error,errno);
for(size_t i = 0, ei = branches.size(); i != ei; i++) if(rv == -1)
{ toremove.push_back(oldfullpath);
const string &oldbasepath = branches[i]; }
_rename_create_path_core(oldbasepaths, if(error == 0)
oldbasepath,newbasepath[0], l::remove(toremove);
oldfusepath,newfusepath,
newfusedirpath,
error,toremove);
}
return -error;
}
if(error == 0) static
_remove(toremove); int
rename_preserve_path(const Policy::Action &actionPolicy_,
const Branches::CPtr &branches_,
const gfs::path &oldfusepath_,
const gfs::path &newfusepath_)
{
int rv;
bool success;
StrVec toremove;
StrVec oldbasepaths;
gfs::path oldfullpath;
gfs::path newfullpath;
return -error; rv = actionPolicy_(branches_,oldfusepath_,&oldbasepaths);
} if(rv == -1)
return -errno;
static success = false;
int for(auto &branch : *branches_)
_clonepath(const Policy::Search &searchFunc, {
const Branches::CPtr &branches_, newfullpath = branch.path;
const string &dstbasepath, newfullpath += newfusepath_;
const string &fusedirpath)
{
int rv;
StrVec srcbasepath;
rv = searchFunc(branches_,fusedirpath.c_str(),&srcbasepath); if(!l::contains(oldbasepaths,branch.path))
if(rv == -1) {
return -errno; toremove.push_back(newfullpath);
continue;
}
fs::clonepath_as_root(srcbasepath[0],dstbasepath,fusedirpath); oldfullpath = branch.path;
oldfullpath += oldfusepath_;
return 0; rv = fs::rename(oldfullpath,newfullpath);
} if(rv == -1)
{
toremove.push_back(oldfullpath);
continue;
}
static success = true;
int }
_clonepath_if_would_create(const Policy::Search &searchFunc,
const Policy::Create &createFunc, // TODO: probably should try to be nuanced here.
if(success == false)
return -EXDEV;
l::remove(toremove);
return 0;
}
static
void
rename_exdev_rename_back(const StrVec &basepaths_,
const gfs::path &oldfusepath_)
{
gfs::path oldpath;
gfs::path newpath;
for(auto &basepath : basepaths_)
{
oldpath = basepath;
oldpath /= ".mergerfs_rename_exdev";
oldpath += oldfusepath_;
newpath = basepath;
newpath += oldfusepath_;
fs::rename(oldpath,newpath);
}
}
static
int
rename_exdev_rename_target(const Policy::Action &actionPolicy_,
const Branches::CPtr &branches_,
const gfs::path &oldfusepath_,
StrVec *basepaths_)
{
int rv;
gfs::path clonesrc;
gfs::path clonetgt;
rv = actionPolicy_(branches_,oldfusepath_,basepaths_);
if(rv == -1)
return -errno;
ugid::SetRootGuard ugidGuard;
for(auto &basepath : *basepaths_)
{
clonesrc = basepath;
clonetgt = basepath;
clonetgt /= ".mergerfs_rename_exdev";
rv = fs::clonepath(clonesrc,clonetgt,oldfusepath_.parent_path());
if((rv == -1) && (errno == ENOENT))
{
fs::mkdir(clonetgt,01777);
rv = fs::clonepath(clonesrc,clonetgt,oldfusepath_.parent_path());
}
if(rv == -1)
goto error;
clonesrc += oldfusepath_;
clonetgt += oldfusepath_;
rv = fs::rename(clonesrc,clonetgt);
if(rv == -1)
goto error;
}
return 0;
error:
l::rename_exdev_rename_back(*basepaths_,oldfusepath_);
return -EXDEV;
}
static
int
rename_exdev_rel_symlink(const Policy::Action &actionPolicy_,
const Branches::CPtr &branches_, const Branches::CPtr &branches_,
const string &oldbasepath, const gfs::path &oldfusepath_,
const char *oldfusepath, const gfs::path &newfusepath_)
const char *newfusepath) {
{ int rv;
int rv; StrVec basepaths;
string newfusedirpath; gfs::path target;
StrVec newbasepath; gfs::path linkpath;
newfusedirpath = fs::path::dirname(newfusepath); rv = l::rename_exdev_rename_target(actionPolicy_,branches_,oldfusepath_,&basepaths);
if(rv < 0)
return rv;
linkpath = newfusepath_;
target = "/.mergerfs_rename_exdev";
target += oldfusepath_;
target = target.lexically_relative(linkpath.parent_path());
rv = FUSE::symlink(target.c_str(),linkpath.c_str());
if(rv < 0)
l::rename_exdev_rename_back(basepaths,oldfusepath_);
rv = createFunc(branches_,newfusedirpath.c_str(),&newbasepath);
if(rv == -1)
return rv; return rv;
}
if(oldbasepath == newbasepath[0]) static
return _clonepath(searchFunc,branches_,oldbasepath,newfusedirpath); int
rename_exdev_abs_symlink(const Policy::Action &actionPolicy_,
return (errno=EXDEV,-1);
}
static
void
_rename_preserve_path_core(const Policy::Search &searchFunc,
const Policy::Create &createFunc,
const Branches::CPtr &branches_, const Branches::CPtr &branches_,
const StrVec &oldbasepaths, const std::string &mount_,
const string &oldbasepath, const gfs::path &oldfusepath_,
const char *oldfusepath, const gfs::path &newfusepath_)
const char *newfusepath, {
int &error, int rv;
StrVec &toremove) StrVec basepaths;
{ gfs::path target;
int rv; gfs::path linkpath;
bool ismember;
string newfullpath;
newfullpath = fs::path::make(oldbasepath,newfusepath); rv = l::rename_exdev_rename_target(actionPolicy_,branches_,oldfusepath_,&basepaths);
if(rv < 0)
return rv;
ismember = member(oldbasepaths,oldbasepath); linkpath = newfusepath_;
if(ismember) target = mount_;
{ target /= ".mergerfs_rename_exdev";
string oldfullpath; target += oldfusepath_;
oldfullpath = fs::path::make(oldbasepath,oldfusepath); rv = FUSE::symlink(target.c_str(),linkpath.c_str());
if(rv < 0)
l::rename_exdev_rename_back(basepaths,oldfusepath_);
rv = fs::rename(oldfullpath,newfullpath); return rv;
if((rv == -1) && (errno == ENOENT)) }
{
rv = _clonepath_if_would_create(searchFunc,createFunc,
branches_,oldbasepath,
oldfusepath,newfusepath);
if(rv == 0)
rv = fs::rename(oldfullpath,newfullpath);
}
error = error::calc(rv,error,errno); static
if(rv == -1) int
toremove.push_back(oldfullpath); rename_exdev(Config::Read &cfg_,
} const gfs::path &oldfusepath_,
else const gfs::path &newfusepath_)
{ {
toremove.push_back(newfullpath); switch(cfg_->rename_exdev)
} {
} case RenameEXDEV::ENUM::PASSTHROUGH:
return -EXDEV;
case RenameEXDEV::ENUM::REL_SYMLINK:
return l::rename_exdev_rel_symlink(cfg_->func.rename.policy,
cfg_->branches,
oldfusepath_,
newfusepath_);
case RenameEXDEV::ENUM::ABS_SYMLINK:
return l::rename_exdev_abs_symlink(cfg_->func.rename.policy,
cfg_->branches,
cfg_->mount,
oldfusepath_,
newfusepath_);
}
static return -EXDEV;
int }
_rename_preserve_path(const Policy::Search &searchFunc,
const Policy::Action &actionFunc,
const Policy::Create &createFunc,
const Branches::CPtr &branches_,
const char *oldfusepath,
const char *newfusepath)
{
int rv;
int error;
StrVec toremove;
StrVec oldbasepaths;
StrVec branches;
rv = actionFunc(branches_,oldfusepath,&oldbasepaths); static
if(rv == -1) int
return -errno; rename(Config::Read &cfg_,
const gfs::path &oldpath_,
const gfs::path &newpath_)
{
if(cfg_->func.create.policy.path_preserving() && !cfg_->ignorepponrename)
return l::rename_preserve_path(cfg_->func.rename.policy,
cfg_->branches,
oldpath_,
newpath_);
branches_->to_paths(branches); return l::rename_create_path(cfg_->func.getattr.policy,
cfg_->func.rename.policy,
error = -1; cfg_->branches,
for(size_t i = 0, ei = branches.size(); i != ei; i++) oldpath_,
{ newpath_);
const string &oldbasepath = branches[i]; }
_rename_preserve_path_core(searchFunc,createFunc,
branches_,
oldbasepaths,oldbasepath,
oldfusepath,newfusepath,
error,toremove);
}
if(error == 0)
_remove(toremove);
return -error;
} }
namespace FUSE namespace FUSE
{ {
int int
rename(const char *oldpath, rename(const char *oldfusepath_,
const char *newpath) const char *newfusepath_)
{ {
int rv;
Config::Read cfg; Config::Read cfg;
gfs::path oldfusepath(oldfusepath_);
gfs::path newfusepath(newfusepath_);
const fuse_context *fc = fuse_get_context(); const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid); const ugid::Set ugid(fc->uid,fc->gid);
if(cfg->func.create.policy.path_preserving() && !cfg->ignorepponrename) rv = l::rename(cfg,oldfusepath,newfusepath);
return _rename_preserve_path(cfg->func.getattr.policy, if(rv == -EXDEV)
cfg->func.rename.policy, return l::rename_exdev(cfg,oldfusepath,newfusepath);
cfg->func.create.policy,
cfg->branches,
oldpath,
newpath);
return _rename_create_path(cfg->func.getattr.policy, return rv;
cfg->func.rename.policy,
cfg->branches,
oldpath,
newpath);
} }
} }

View File

@ -16,8 +16,9 @@
#include "config.hpp" #include "config.hpp"
#include "errno.hpp" #include "errno.hpp"
#include "fs_rmdir.hpp"
#include "fs_path.hpp" #include "fs_path.hpp"
#include "fs_rmdir.hpp"
#include "fs_unlink.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "fuse.h" #include "fuse.h"
@ -50,9 +51,21 @@ namespace l
{ {
static static
int int
rmdir_loop_core(const string &basepath_, should_unlink(int rv_,
const char *fusepath_, int errno_,
const int error_) FollowSymlinks followsymlinks_)
{
return ((rv_ == -1) &&
(errno_ == ENOTDIR) &&
(followsymlinks_ != FollowSymlinks::ENUM::NEVER));
}
static
int
rmdir_core(const string &basepath_,
const char *fusepath_,
const FollowSymlinks followsymlinks_,
const int error_)
{ {
int rv; int rv;
string fullpath; string fullpath;
@ -60,21 +73,24 @@ namespace l
fullpath = fs::path::make(basepath_,fusepath_); fullpath = fs::path::make(basepath_,fusepath_);
rv = fs::rmdir(fullpath); rv = fs::rmdir(fullpath);
if(l::should_unlink(rv,errno,followsymlinks_))
rv = fs::unlink(fullpath);
return error::calc(rv,error_,errno); return error::calc(rv,error_,errno);
} }
static static
int int
rmdir_loop(const StrVec &basepaths_, rmdir_loop(const StrVec &basepaths_,
const char *fusepath_) const char *fusepath_,
const FollowSymlinks followsymlinks_)
{ {
int error; int error;
error = 0; error = 0;
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++) for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{ {
error = l::rmdir_loop_core(basepaths_[i],fusepath_,error); error = l::rmdir_core(basepaths_[i],fusepath_,followsymlinks_,error);
} }
return -error; return -error;
@ -84,6 +100,7 @@ namespace l
int int
rmdir(const Policy::Action &actionFunc_, rmdir(const Policy::Action &actionFunc_,
const Branches &branches_, const Branches &branches_,
const FollowSymlinks followsymlinks_,
const char *fusepath_) const char *fusepath_)
{ {
int rv; int rv;
@ -93,7 +110,7 @@ namespace l
if(rv == -1) if(rv == -1)
return -errno; return -errno;
return l::rmdir_loop(basepaths,fusepath_); return l::rmdir_loop(basepaths,fusepath_,followsymlinks_);
} }
} }
@ -108,6 +125,7 @@ namespace FUSE
return l::rmdir(cfg->func.rmdir.policy, return l::rmdir(cfg->func.rmdir.policy,
cfg->branches, cfg->branches,
cfg->follow_symlinks,
fusepath_); fusepath_);
} }
} }

View File

@ -16,9 +16,10 @@
#include "config.hpp" #include "config.hpp"
#include "errno.hpp" #include "errno.hpp"
#include "fs_symlink.hpp"
#include "fs_clonepath.hpp" #include "fs_clonepath.hpp"
#include "fs_path.hpp" #include "fs_path.hpp"
#include "fs_symlink.hpp"
#include "fuse_getattr.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "fuse.h" #include "fuse.h"
@ -56,16 +57,16 @@ namespace l
static static
int int
symlink_loop_core(const string &newbasepath_, symlink_loop_core(const string &newbasepath_,
const char *oldpath_, const char *target_,
const char *newpath_, const char *linkpath_,
const int error_) const int error_)
{ {
int rv; int rv;
string fullnewpath; string fullnewpath;
fullnewpath = fs::path::make(newbasepath_,newpath_); fullnewpath = fs::path::make(newbasepath_,linkpath_);
rv = fs::symlink(oldpath_,fullnewpath); rv = fs::symlink(target_,fullnewpath);
return error::calc(rv,error_,errno); return error::calc(rv,error_,errno);
} }
@ -74,8 +75,8 @@ namespace l
int int
symlink_loop(const string &existingpath_, symlink_loop(const string &existingpath_,
const StrVec &newbasepaths_, const StrVec &newbasepaths_,
const char *oldpath_, const char *target_,
const char *newpath_, const char *linkpath_,
const string &newdirpath_) const string &newdirpath_)
{ {
int rv; int rv;
@ -89,8 +90,8 @@ namespace l
error = error::calc(rv,error,errno); error = error::calc(rv,error,errno);
else else
error = l::symlink_loop_core(newbasepaths_[i], error = l::symlink_loop_core(newbasepaths_[i],
oldpath_, target_,
newpath_, linkpath_,
error); error);
} }
@ -102,15 +103,15 @@ namespace l
symlink(const Policy::Search &searchFunc_, symlink(const Policy::Search &searchFunc_,
const Policy::Create &createFunc_, const Policy::Create &createFunc_,
const Branches &branches_, const Branches &branches_,
const char *oldpath_, const char *target_,
const char *newpath_) const char *linkpath_)
{ {
int rv; int rv;
string newdirpath; string newdirpath;
StrVec newbasepaths; StrVec newbasepaths;
StrVec existingpaths; StrVec existingpaths;
newdirpath = fs::path::dirname(newpath_); newdirpath = fs::path::dirname(linkpath_);
rv = searchFunc_(branches_,newdirpath,&existingpaths); rv = searchFunc_(branches_,newdirpath,&existingpaths);
if(rv == -1) if(rv == -1)
@ -121,15 +122,15 @@ namespace l
return -errno; return -errno;
return l::symlink_loop(existingpaths[0],newbasepaths, return l::symlink_loop(existingpaths[0],newbasepaths,
oldpath_,newpath_,newdirpath); target_,linkpath_,newdirpath);
} }
} }
namespace FUSE namespace FUSE
{ {
int int
symlink(const char *oldpath_, symlink(const char *target_,
const char *newpath_) const char *linkpath_)
{ {
Config::Read cfg; Config::Read cfg;
const fuse_context *fc = fuse_get_context(); const fuse_context *fc = fuse_get_context();
@ -138,7 +139,22 @@ namespace FUSE
return l::symlink(cfg->func.getattr.policy, return l::symlink(cfg->func.getattr.policy,
cfg->func.symlink.policy, cfg->func.symlink.policy,
cfg->branches, cfg->branches,
oldpath_, target_,
newpath_); linkpath_);
}
int
symlink(const char *target_,
const char *linkpath_,
struct stat *st_,
fuse_timeouts_t *timeout_)
{
int rv;
rv = FUSE::symlink(target_,linkpath_);
if(rv < 0)
return rv;
return FUSE::getattr(target_,st_,timeout_);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link> Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -16,10 +16,19 @@
#pragma once #pragma once
#include "fuse.h"
#include <sys/stat.h>
namespace FUSE namespace FUSE
{ {
int int
symlink(const char *oldpath, symlink(const char *target,
const char *newpath); const char *linkpath);
int
symlink(const char *target,
const char *linkpath,
struct stat *st,
fuse_timeouts_t *timeouts);
} }

5742
src/ghc/filesystem.hpp Normal file

File diff suppressed because it is too large Load Diff