mirror of
https://github.com/trapexit/mergerfs.git
synced 2024-11-22 13:47:03 +08:00
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:
parent
50daf84ec0
commit
8adebc9489
2
Makefile
2
Makefile
|
@ -40,7 +40,7 @@ USE_XATTR = 1
|
|||
UGID_USE_RWLOCK = 0
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
|
|
51
README.md
51
README.md
|
@ -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_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)
|
||||
* **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)
|
||||
* **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)
|
||||
|
@ -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.
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
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.
|
||||
|
|
|
@ -10,7 +10,7 @@ INSTALLUTILS :=
|
|||
endif
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
|
|
|
@ -121,13 +121,13 @@ struct fuse_operations
|
|||
int (*rmdir) (const char *);
|
||||
|
||||
/** Create a symbolic link */
|
||||
int (*symlink) (const char *, const char *);
|
||||
int (*symlink) (const char *, const char *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/** Rename a file */
|
||||
int (*rename) (const char *, const char *);
|
||||
|
||||
/** 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 */
|
||||
int (*chmod) (const char *, mode_t);
|
||||
|
@ -794,9 +794,16 @@ int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
|
|||
const char *newpath);
|
||||
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_symlink(struct fuse_fs *fs, const char *linkname,
|
||||
const char *path);
|
||||
int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
|
||||
int fuse_fs_symlink(struct fuse_fs *fs,
|
||||
const char *linkname,
|
||||
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,
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_open(struct fuse_fs *fs, const char *path,
|
||||
|
|
|
@ -1650,25 +1650,30 @@ fuse_fs_rmdir(struct fuse_fs *fs,
|
|||
}
|
||||
|
||||
int
|
||||
fuse_fs_symlink(struct fuse_fs *fs,
|
||||
const char *linkname,
|
||||
const char *path)
|
||||
fuse_fs_symlink(struct fuse_fs *fs_,
|
||||
const char *linkname_,
|
||||
const char *path_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeouts_)
|
||||
{
|
||||
if(fs->op.symlink == NULL)
|
||||
|
||||
if(fs_->op.symlink == NULL)
|
||||
return -ENOSYS;
|
||||
|
||||
fuse_get_context()->private_data = fs->user_data;
|
||||
if(fs_->debug)
|
||||
fprintf(stderr,"symlink %s %s\n",linkname_,path_);
|
||||
|
||||
if(fs->debug)
|
||||
fprintf(stderr,"symlink %s %s\n",linkname,path);
|
||||
fuse_get_context()->private_data = fs_->user_data;
|
||||
|
||||
return fs->op.symlink(linkname,path);
|
||||
return fs_->op.symlink(linkname_,path_,st_,timeouts_);
|
||||
}
|
||||
|
||||
int
|
||||
fuse_fs_link(struct fuse_fs *fs,
|
||||
const char *oldpath,
|
||||
const char *newpath)
|
||||
const char *newpath,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeouts_)
|
||||
{
|
||||
if(fs->op.link == NULL)
|
||||
return -ENOSYS;
|
||||
|
@ -1678,7 +1683,7 @@ fuse_fs_link(struct fuse_fs *fs,
|
|||
if(fs->debug)
|
||||
fprintf(stderr,"link %s %s\n",oldpath,newpath);
|
||||
|
||||
return fs->op.link(oldpath,newpath);
|
||||
return fs->op.link(oldpath,newpath,st_,timeouts_);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -2543,32 +2548,17 @@ update_stat(struct node *node_,
|
|||
|
||||
static
|
||||
int
|
||||
lookup_path(struct fuse *f,
|
||||
set_path_info(struct fuse *f,
|
||||
fuse_ino_t nodeid,
|
||||
const char *name,
|
||||
const char *path,
|
||||
struct fuse_entry_param *e,
|
||||
fuse_file_info_t *fi)
|
||||
struct fuse_entry_param *e)
|
||||
{
|
||||
int res;
|
||||
|
||||
memset(e,0,sizeof(struct fuse_entry_param));
|
||||
|
||||
res = ((fi == NULL) ?
|
||||
fuse_fs_getattr(f->fs,path,&e->attr,&e->timeout) :
|
||||
fuse_fs_fgetattr(f->fs,&e->attr,fi,&e->timeout));
|
||||
|
||||
if(res == 0)
|
||||
{
|
||||
struct node *node;
|
||||
|
||||
node = find_node(f,nodeid,name);
|
||||
if(node == NULL)
|
||||
{
|
||||
res = -ENOMEM;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -ENOMEM;
|
||||
|
||||
e->ino = node->nodeid;
|
||||
e->generation = node->generation;
|
||||
|
||||
|
@ -2583,10 +2573,31 @@ lookup_path(struct fuse *f,
|
|||
" GEN: %llu\n",
|
||||
(unsigned long long)e->ino,
|
||||
(unsigned long long)e->generation);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
lookup_path(struct fuse *f,
|
||||
fuse_ino_t nodeid,
|
||||
const char *name,
|
||||
const char *path,
|
||||
struct fuse_entry_param *e,
|
||||
fuse_file_info_t *fi)
|
||||
{
|
||||
int rv;
|
||||
|
||||
memset(e,0,sizeof(struct fuse_entry_param));
|
||||
|
||||
rv = ((fi == NULL) ?
|
||||
fuse_fs_getattr(f->fs,path,&e->attr,&e->timeout) :
|
||||
fuse_fs_fgetattr(f->fs,&e->attr,fi,&e->timeout));
|
||||
|
||||
if(rv)
|
||||
return rv;
|
||||
|
||||
return set_path_info(f,nodeid,name,e);
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -3236,26 +3247,28 @@ fuse_lib_rmdir(fuse_req_t req,
|
|||
|
||||
static
|
||||
void
|
||||
fuse_lib_symlink(fuse_req_t req,
|
||||
const char *linkname,
|
||||
fuse_ino_t parent,
|
||||
const char *name)
|
||||
fuse_lib_symlink(fuse_req_t req_,
|
||||
const char *linkname_,
|
||||
fuse_ino_t parent_,
|
||||
const char *name_)
|
||||
{
|
||||
struct fuse *f = req_fuse_prepare(req);
|
||||
struct fuse_entry_param e;
|
||||
int rv;
|
||||
char *path;
|
||||
int err;
|
||||
struct fuse *f;
|
||||
struct fuse_entry_param e = {0};
|
||||
|
||||
err = get_path_name(f,parent,name,&path);
|
||||
if(!err)
|
||||
f = req_fuse_prepare(req_);
|
||||
|
||||
rv = get_path_name(f,parent_,name_,&path);
|
||||
if(rv == 0)
|
||||
{
|
||||
err = fuse_fs_symlink(f->fs,linkname,path);
|
||||
if(!err)
|
||||
err = lookup_path(f,parent,name,path,&e,NULL);
|
||||
free_path(f,parent,path);
|
||||
rv = fuse_fs_symlink(f->fs,linkname_,path,&e.attr,&e.timeout);
|
||||
if(rv == 0)
|
||||
rv = set_path_info(f,parent_,name_,&e);
|
||||
free_path(f,parent_,path);
|
||||
}
|
||||
|
||||
reply_entry(req,&e,err);
|
||||
reply_entry(req_,&e,rv);
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -3298,27 +3311,32 @@ fuse_lib_rename(fuse_req_t req,
|
|||
reply_err(req,err);
|
||||
}
|
||||
|
||||
static void fuse_lib_link(fuse_req_t req,fuse_ino_t ino,fuse_ino_t newparent,
|
||||
static
|
||||
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);
|
||||
struct fuse_entry_param e;
|
||||
int rv;
|
||||
char *oldpath;
|
||||
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);
|
||||
if(!err)
|
||||
if(!rv)
|
||||
{
|
||||
err = fuse_fs_link(f->fs,oldpath,newpath);
|
||||
if(!err)
|
||||
err = lookup_path(f,newparent,newname,newpath,
|
||||
&e,NULL);
|
||||
rv = fuse_fs_link(f->fs,oldpath,newpath,&e.attr,&e.timeout);
|
||||
if(rv == 0)
|
||||
rv = set_path_info(f,newparent,newname,&e);
|
||||
free_path2(f,ino,newparent,NULL,NULL,oldpath,newpath);
|
||||
}
|
||||
|
||||
reply_entry(req,&e,err);
|
||||
reply_entry(req,&e,rv);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
|
@ -223,6 +223,18 @@ over NFS where there are issues with creating files for write while
|
|||
setting the mode to read\-only.
|
||||
(default: off)
|
||||
.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
|
||||
kernel and underlying filesystem).
|
||||
(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
|
||||
benchmarking.
|
||||
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
|
||||
.PP
|
||||
Due to the levels of indirection introduced by mergerfs and the
|
||||
|
|
|
@ -71,6 +71,7 @@ namespace l
|
|||
Config::Config()
|
||||
: async_read(true),
|
||||
auto_cache(false),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
branches(minfreespace),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
|
@ -83,12 +84,13 @@ Config::Config()
|
|||
direct_io(false),
|
||||
dropcacheonclose(false),
|
||||
fsname(),
|
||||
follow_symlinks(FollowSymlinks::ENUM::NEVER),
|
||||
func(),
|
||||
fuse_msg_size(FUSE_MAX_MAX_PAGES),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
link_cow(false),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
|
||||
mount(),
|
||||
moveonenospc(false),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
|
@ -97,6 +99,7 @@ Config::Config()
|
|||
posix_acl(false),
|
||||
readdir(ReadDir::ENUM::POSIX),
|
||||
readdirplus(false),
|
||||
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
|
@ -124,6 +127,7 @@ Config::Config()
|
|||
_map["category.search"] = &category.search;
|
||||
_map["direct_io"] = &direct_io;
|
||||
_map["dropcacheonclose"] = &dropcacheonclose;
|
||||
_map["follow-symlinks"] = &follow_symlinks;
|
||||
_map["fsname"] = &fsname;
|
||||
_map["func.access"] = &func.access;
|
||||
_map["func.chmod"] = &func.chmod;
|
||||
|
@ -150,6 +154,7 @@ Config::Config()
|
|||
_map["inodecalc"] = &inodecalc;
|
||||
_map["kernel_cache"] = &kernel_cache;
|
||||
_map["link_cow"] = &link_cow;
|
||||
_map["link-exdev"] = &link_exdev;
|
||||
_map["minfreespace"] = &minfreespace;
|
||||
_map["mount"] = &mount;
|
||||
_map["moveonenospc"] = &moveonenospc;
|
||||
|
@ -159,6 +164,7 @@ Config::Config()
|
|||
_map["posix_acl"] = &posix_acl;
|
||||
// _map["readdir"] = &readdir;
|
||||
_map["readdirplus"] = &readdirplus;
|
||||
_map["rename-exdev"] = &rename_exdev;
|
||||
_map["security_capability"] = &security_capability;
|
||||
_map["srcmounts"] = &srcmounts;
|
||||
_map["statfs"] = &statfs;
|
||||
|
|
|
@ -19,10 +19,13 @@
|
|||
#include "branches.hpp"
|
||||
#include "category.hpp"
|
||||
#include "config_cachefiles.hpp"
|
||||
#include "config_follow_symlinks.hpp"
|
||||
#include "config_inodecalc.hpp"
|
||||
#include "config_link_exdev.hpp"
|
||||
#include "config_moveonenospc.hpp"
|
||||
#include "config_nfsopenhack.hpp"
|
||||
#include "config_readdir.hpp"
|
||||
#include "config_rename_exdev.hpp"
|
||||
#include "config_statfs.hpp"
|
||||
#include "config_statfsignore.hpp"
|
||||
#include "config_xattr.hpp"
|
||||
|
@ -38,7 +41,6 @@
|
|||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -98,6 +100,7 @@ public:
|
|||
public:
|
||||
ConfigBOOL async_read;
|
||||
ConfigBOOL auto_cache;
|
||||
ConfigUINT64 minfreespace;
|
||||
Branches branches;
|
||||
ConfigUINT64 cache_attr;
|
||||
ConfigUINT64 cache_entry;
|
||||
|
@ -110,13 +113,14 @@ public:
|
|||
ConfigBOOL direct_io;
|
||||
ConfigBOOL dropcacheonclose;
|
||||
ConfigSTR fsname;
|
||||
FollowSymlinks follow_symlinks;
|
||||
Funcs func;
|
||||
ConfigUINT64 fuse_msg_size;
|
||||
ConfigBOOL ignorepponrename;
|
||||
InodeCalc inodecalc;
|
||||
ConfigBOOL kernel_cache;
|
||||
ConfigBOOL link_cow;
|
||||
ConfigUINT64 minfreespace;
|
||||
LinkEXDEV link_exdev;
|
||||
ConfigSTR mount;
|
||||
MoveOnENOSPC moveonenospc;
|
||||
NFSOpenHack nfsopenhack;
|
||||
|
@ -125,6 +129,7 @@ public:
|
|||
ConfigBOOL posix_acl;
|
||||
ReadDir readdir;
|
||||
ConfigBOOL readdirplus;
|
||||
RenameEXDEV rename_exdev;
|
||||
ConfigBOOL security_capability;
|
||||
SrcMounts srcmounts;
|
||||
StatFS statfs;
|
||||
|
|
58
src/config_follow_symlinks.cpp
Normal file
58
src/config_follow_symlinks.cpp
Normal 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;
|
||||
}
|
30
src/config_follow_symlinks.hpp
Normal file
30
src/config_follow_symlinks.hpp
Normal 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
58
src/config_link_exdev.cpp
Normal 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
30
src/config_link_exdev.hpp
Normal 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;
|
54
src/config_rename_exdev.cpp
Normal file
54
src/config_rename_exdev.cpp
Normal 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;
|
||||
}
|
29
src/config_rename_exdev.hpp
Normal file
29
src/config_rename_exdev.hpp
Normal 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;
|
|
@ -67,8 +67,8 @@ public:
|
|||
}
|
||||
|
||||
public:
|
||||
std::string to_string() const;
|
||||
int from_string(const std::string &);
|
||||
std::string to_string() const final;
|
||||
int from_string(const std::string &) final;
|
||||
|
||||
public:
|
||||
int to_int() const
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "ghc/filesystem.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
@ -26,12 +28,30 @@
|
|||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdir(const char *path_,
|
||||
const mode_t mode_)
|
||||
{
|
||||
return ::mkdir(path_,mode_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
mkdir(const std::string &path_,
|
||||
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
35
src/fs_mkdir_as_root.hpp
Normal 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_);
|
||||
}
|
||||
}
|
|
@ -28,30 +28,27 @@ namespace fs
|
|||
static
|
||||
inline
|
||||
int
|
||||
symlink(const char *oldpath_,
|
||||
const char *newpath_)
|
||||
symlink(const char *target_,
|
||||
const char *linkpath_)
|
||||
{
|
||||
return ::symlink(oldpath_,
|
||||
newpath_);
|
||||
return ::symlink(target_,linkpath_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
symlink(const std::string &oldpath_,
|
||||
const std::string &newpath_)
|
||||
symlink(const std::string &target_,
|
||||
const std::string &linkpath_)
|
||||
{
|
||||
return fs::symlink(oldpath_.c_str(),
|
||||
newpath_.c_str());
|
||||
return ::symlink(target_.c_str(),linkpath_.c_str());
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
symlink(const char *oldpath_,
|
||||
const std::string &newpath_)
|
||||
symlink(const char *target_,
|
||||
const std::string &linkpath_)
|
||||
{
|
||||
return fs::symlink(oldpath_,
|
||||
newpath_.c_str());
|
||||
return ::symlink(target_,linkpath_.c_str());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "fs_inode.hpp"
|
||||
#include "fs_lstat.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_stat.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
|
@ -31,6 +32,42 @@ using std::string;
|
|||
|
||||
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
|
||||
int
|
||||
getattr_controlfile(struct stat *st_)
|
||||
|
@ -63,7 +100,8 @@ namespace l
|
|||
const char *fusepath_,
|
||||
struct stat *st_,
|
||||
const bool symlinkify_,
|
||||
const time_t symlinkify_timeout_)
|
||||
const time_t symlinkify_timeout_,
|
||||
FollowSymlinks followsymlinks_)
|
||||
{
|
||||
int rv;
|
||||
string fullpath;
|
||||
|
@ -75,7 +113,28 @@ namespace l
|
|||
|
||||
fullpath = fs::path::make(basepaths[0],fusepath_);
|
||||
|
||||
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)
|
||||
return -errno;
|
||||
|
||||
|
@ -102,7 +161,8 @@ namespace l
|
|||
fusepath_,
|
||||
st_,
|
||||
cfg->symlinkify,
|
||||
cfg->symlinkify_timeout);
|
||||
cfg->symlinkify_timeout,
|
||||
cfg->follow_symlinks);
|
||||
|
||||
timeout_->entry = ((rv >= 0) ?
|
||||
cfg->cache_entry :
|
||||
|
|
|
@ -18,7 +18,11 @@
|
|||
#include "errno.hpp"
|
||||
#include "fs_clonepath.hpp"
|
||||
#include "fs_link.hpp"
|
||||
#include "fs_lstat.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fuse_getattr.hpp"
|
||||
#include "fuse_symlink.hpp"
|
||||
#include "ghc/filesystem.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
@ -28,7 +32,7 @@
|
|||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace gfs = ghc::filesystem;
|
||||
|
||||
namespace error
|
||||
{
|
||||
|
@ -52,47 +56,38 @@ namespace error
|
|||
|
||||
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
|
||||
int
|
||||
link_create_path_loop(const StrVec &oldbasepaths_,
|
||||
const string &newbasepath_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_,
|
||||
const string &newfusedirpath_)
|
||||
const string &newfusedirpath_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
int error;
|
||||
string oldfullpath;
|
||||
string newfullpath;
|
||||
|
||||
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_);
|
||||
if(rv == -1)
|
||||
oldfullpath = fs::path::make(oldbasepath,oldfusepath_);
|
||||
newfullpath = fs::path::make(oldbasepath,newfusepath_);
|
||||
|
||||
rv = fs::link(oldfullpath,newfullpath);
|
||||
if((rv == -1) && (errno == ENOENT))
|
||||
{
|
||||
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);
|
||||
else
|
||||
error = l::link_create_path_core(oldbasepaths_[i],newbasepath_,
|
||||
oldfusepath_,newfusepath_,
|
||||
error);
|
||||
}
|
||||
|
||||
return -error;
|
||||
|
@ -104,7 +99,8 @@ namespace l
|
|||
const Policy::Action &actionFunc_,
|
||||
const Branches &branches_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_)
|
||||
const char *newfusepath_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
string newfusedirpath;
|
||||
|
@ -123,46 +119,16 @@ namespace l
|
|||
|
||||
return l::link_create_path_loop(oldbasepaths,newbasepaths[0],
|
||||
oldfusepath_,newfusepath_,
|
||||
newfusedirpath);
|
||||
newfusedirpath,
|
||||
st_);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
clonepath_if_would_create(const Policy::Search &searchFunc_,
|
||||
const Policy::Create &createFunc_,
|
||||
const Branches &branches_,
|
||||
const string &oldbasepath_,
|
||||
const char *oldfusepath_,
|
||||
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_,
|
||||
link_preserve_path_core(const string &oldbasepath_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_,
|
||||
struct stat *st_,
|
||||
const int error_)
|
||||
{
|
||||
int rv;
|
||||
|
@ -174,36 +140,29 @@ namespace l
|
|||
|
||||
rv = fs::link(oldfullpath,newfullpath);
|
||||
if((rv == -1) && (errno == ENOENT))
|
||||
{
|
||||
rv = l::clonepath_if_would_create(searchFunc_,createFunc_,
|
||||
branches_,
|
||||
oldbasepath_,
|
||||
oldfusepath_,newfusepath_);
|
||||
if(rv != -1)
|
||||
rv = fs::link(oldfullpath,newfullpath);
|
||||
}
|
||||
errno = EXDEV;
|
||||
if((rv == 0) && (st_->st_ino == 0))
|
||||
rv = fs::lstat(oldfullpath,st_);
|
||||
|
||||
return error::calc(rv,error_,errno);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
link_preserve_path_loop(const Policy::Search &searchFunc_,
|
||||
const Policy::Create &createFunc_,
|
||||
const Branches &branches_,
|
||||
link_preserve_path_loop(const StrVec &oldbasepaths_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_,
|
||||
const StrVec &oldbasepaths_)
|
||||
struct stat *st_)
|
||||
{
|
||||
int error;
|
||||
|
||||
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_,
|
||||
branches_,
|
||||
oldbasepaths_[i],
|
||||
oldfusepath_,newfusepath_,
|
||||
error = l::link_preserve_path_core(oldbasepath,
|
||||
oldfusepath_,
|
||||
newfusepath_,
|
||||
st_,
|
||||
error);
|
||||
}
|
||||
|
||||
|
@ -212,12 +171,11 @@ namespace l
|
|||
|
||||
static
|
||||
int
|
||||
link_preserve_path(const Policy::Search &searchFunc_,
|
||||
const Policy::Action &actionFunc_,
|
||||
const Policy::Create &createFunc_,
|
||||
link_preserve_path(const Policy::Action &actionFunc_,
|
||||
const Branches &branches_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_)
|
||||
const char *newfusepath_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
StrVec oldbasepaths;
|
||||
|
@ -226,35 +184,183 @@ namespace l
|
|||
if(rv == -1)
|
||||
return -errno;
|
||||
|
||||
return l::link_preserve_path_loop(searchFunc_,createFunc_,
|
||||
branches_,
|
||||
oldfusepath_,newfusepath_,
|
||||
oldbasepaths);
|
||||
return l::link_preserve_path_loop(oldbasepaths,
|
||||
oldfusepath_,
|
||||
newfusepath_,
|
||||
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
|
||||
{
|
||||
int
|
||||
link(const char *from_,
|
||||
const char *to_)
|
||||
link(const char *oldpath_,
|
||||
const char *newpath_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeouts_)
|
||||
{
|
||||
int rv;
|
||||
Config::Read cfg;
|
||||
const fuse_context *fc = fuse_get_context();
|
||||
const ugid::Set ugid(fc->uid,fc->gid);
|
||||
|
||||
if(cfg->func.create.policy.path_preserving() && !cfg->ignorepponrename)
|
||||
return l::link_preserve_path(cfg->func.getattr.policy,
|
||||
cfg->func.link.policy,
|
||||
cfg->func.create.policy,
|
||||
cfg->branches,
|
||||
from_,
|
||||
to_);
|
||||
rv = l::link(cfg,oldpath_,newpath_,st_,timeouts_);
|
||||
if(rv == -EXDEV)
|
||||
return l::link_exdev(cfg,oldpath_,newpath_,st_,timeouts_);
|
||||
|
||||
return l::link_create_path(cfg->func.getattr.policy,
|
||||
cfg->func.link.policy,
|
||||
cfg->branches,
|
||||
from_,
|
||||
to_);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
||||
namespace FUSE
|
||||
{
|
||||
int
|
||||
link(const char *from,
|
||||
const char *to);
|
||||
link(const char *oldpath,
|
||||
const char *newpath,
|
||||
struct stat *st,
|
||||
fuse_timeouts_t *timeouts);
|
||||
}
|
||||
|
|
|
@ -17,16 +17,26 @@
|
|||
#include "config.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fs_clonepath.hpp"
|
||||
#include "fs_link.hpp"
|
||||
#include "fs_mkdir_as_root.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_remove.hpp"
|
||||
#include "fs_rename.hpp"
|
||||
#include "fs_symlink.hpp"
|
||||
#include "fs_unlink.hpp"
|
||||
#include "fuse_symlink.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include "ghc/filesystem.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace gfs = ghc::filesystem;
|
||||
|
||||
namespace error
|
||||
{
|
||||
|
@ -48,190 +58,95 @@ namespace error
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
member(const StrVec &haystack,
|
||||
const string &needle)
|
||||
namespace l
|
||||
{
|
||||
for(size_t i = 0, ei = haystack.size(); i != ei; i++)
|
||||
static
|
||||
bool
|
||||
contains(const StrVec &haystack_,
|
||||
const char *needle_)
|
||||
{
|
||||
if(haystack[i] == needle)
|
||||
for(auto &hay : haystack_)
|
||||
{
|
||||
if(hay == needle_)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_remove(const StrVec &toremove)
|
||||
{
|
||||
for(size_t i = 0, ei = toremove.size(); i != ei; i++)
|
||||
fs::remove(toremove[i]);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_rename_create_path_core(const StrVec &oldbasepaths,
|
||||
const string &oldbasepath,
|
||||
const string &newbasepath,
|
||||
const char *oldfusepath,
|
||||
const char *newfusepath,
|
||||
const string &newfusedirpath,
|
||||
int &error,
|
||||
StrVec &tounlink)
|
||||
{
|
||||
int rv;
|
||||
bool ismember;
|
||||
string oldfullpath;
|
||||
string newfullpath;
|
||||
|
||||
ismember = member(oldbasepaths,oldbasepath);
|
||||
if(ismember)
|
||||
{
|
||||
rv = fs::clonepath_as_root(newbasepath,oldbasepath,newfusedirpath);
|
||||
if(rv != -1)
|
||||
{
|
||||
oldfullpath = fs::path::make(oldbasepath,oldfusepath);
|
||||
newfullpath = fs::path::make(oldbasepath,newfusepath);
|
||||
|
||||
rv = fs::rename(oldfullpath,newfullpath);
|
||||
}
|
||||
|
||||
error = error::calc(rv,error,errno);
|
||||
if(rv == -1)
|
||||
tounlink.push_back(oldfullpath);
|
||||
}
|
||||
else
|
||||
static
|
||||
bool
|
||||
contains(const StrVec &haystack_,
|
||||
const string &needle_)
|
||||
{
|
||||
newfullpath = fs::path::make(oldbasepath,newfusepath);
|
||||
|
||||
tounlink.push_back(newfullpath);
|
||||
return l::contains(haystack_,needle_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_rename_create_path(const Policy::Search &searchFunc,
|
||||
const Policy::Action &actionFunc,
|
||||
static
|
||||
void
|
||||
remove(const StrVec &toremove_)
|
||||
{
|
||||
for(auto &path : toremove_)
|
||||
fs::remove(path);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
remove(const Branches::CPtr &branches_,
|
||||
const std::string &relpath_)
|
||||
{
|
||||
std::string fullpath;
|
||||
|
||||
for(auto &branch : *branches_)
|
||||
{
|
||||
fullpath = fs::path::make(branch.path,relpath_);
|
||||
fs::remove(fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
rename_create_path(const Policy::Search &searchPolicy_,
|
||||
const Policy::Action &actionPolicy_,
|
||||
const Branches::CPtr &branches_,
|
||||
const char *oldfusepath,
|
||||
const char *newfusepath)
|
||||
{
|
||||
const gfs::path &oldfusepath_,
|
||||
const gfs::path &newfusepath_)
|
||||
{
|
||||
int rv;
|
||||
int error;
|
||||
string newfusedirpath;
|
||||
StrVec toremove;
|
||||
StrVec newbasepath;
|
||||
StrVec oldbasepaths;
|
||||
StrVec branches;
|
||||
gfs::path oldfullpath;
|
||||
gfs::path newfullpath;
|
||||
|
||||
rv = actionFunc(branches_,oldfusepath,&oldbasepaths);
|
||||
rv = actionPolicy_(branches_,oldfusepath_,&oldbasepaths);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
|
||||
newfusedirpath = fs::path::dirname(newfusepath);
|
||||
|
||||
rv = searchFunc(branches_,newfusedirpath.c_str(),&newbasepath);
|
||||
rv = searchPolicy_(branches_,newfusepath_.parent_path(),&newbasepath);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
|
||||
branches_->to_paths(branches);
|
||||
|
||||
error = -1;
|
||||
for(size_t i = 0, ei = branches.size(); i != ei; i++)
|
||||
for(auto &branch : *branches_)
|
||||
{
|
||||
const string &oldbasepath = branches[i];
|
||||
newfullpath = branch.path;
|
||||
newfullpath += newfusepath_;
|
||||
|
||||
_rename_create_path_core(oldbasepaths,
|
||||
oldbasepath,newbasepath[0],
|
||||
oldfusepath,newfusepath,
|
||||
newfusedirpath,
|
||||
error,toremove);
|
||||
if(!l::contains(oldbasepaths,branch.path))
|
||||
{
|
||||
toremove.push_back(newfullpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(error == 0)
|
||||
_remove(toremove);
|
||||
|
||||
return -error;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_clonepath(const Policy::Search &searchFunc,
|
||||
const Branches::CPtr &branches_,
|
||||
const string &dstbasepath,
|
||||
const string &fusedirpath)
|
||||
{
|
||||
int rv;
|
||||
StrVec srcbasepath;
|
||||
|
||||
rv = searchFunc(branches_,fusedirpath.c_str(),&srcbasepath);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
|
||||
fs::clonepath_as_root(srcbasepath[0],dstbasepath,fusedirpath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_clonepath_if_would_create(const Policy::Search &searchFunc,
|
||||
const Policy::Create &createFunc,
|
||||
const Branches::CPtr &branches_,
|
||||
const string &oldbasepath,
|
||||
const char *oldfusepath,
|
||||
const char *newfusepath)
|
||||
{
|
||||
int rv;
|
||||
string newfusedirpath;
|
||||
StrVec newbasepath;
|
||||
|
||||
newfusedirpath = fs::path::dirname(newfusepath);
|
||||
|
||||
rv = createFunc(branches_,newfusedirpath.c_str(),&newbasepath);
|
||||
if(rv == -1)
|
||||
return rv;
|
||||
|
||||
if(oldbasepath == newbasepath[0])
|
||||
return _clonepath(searchFunc,branches_,oldbasepath,newfusedirpath);
|
||||
|
||||
return (errno=EXDEV,-1);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_rename_preserve_path_core(const Policy::Search &searchFunc,
|
||||
const Policy::Create &createFunc,
|
||||
const Branches::CPtr &branches_,
|
||||
const StrVec &oldbasepaths,
|
||||
const string &oldbasepath,
|
||||
const char *oldfusepath,
|
||||
const char *newfusepath,
|
||||
int &error,
|
||||
StrVec &toremove)
|
||||
{
|
||||
int rv;
|
||||
bool ismember;
|
||||
string newfullpath;
|
||||
|
||||
newfullpath = fs::path::make(oldbasepath,newfusepath);
|
||||
|
||||
ismember = member(oldbasepaths,oldbasepath);
|
||||
if(ismember)
|
||||
{
|
||||
string oldfullpath;
|
||||
|
||||
oldfullpath = fs::path::make(oldbasepath,oldfusepath);
|
||||
oldfullpath = branch.path;
|
||||
oldfullpath += oldfusepath_;
|
||||
|
||||
rv = fs::rename(oldfullpath,newfullpath);
|
||||
if((rv == -1) && (errno == ENOENT))
|
||||
if(rv == -1)
|
||||
{
|
||||
rv = _clonepath_if_would_create(searchFunc,createFunc,
|
||||
branches_,oldbasepath,
|
||||
oldfusepath,newfusepath);
|
||||
rv = fs::clonepath_as_root(newbasepath[0],branch.path,newfusepath_.parent_path());
|
||||
if(rv == 0)
|
||||
rv = fs::rename(oldfullpath,newfullpath);
|
||||
}
|
||||
|
@ -240,73 +155,254 @@ _rename_preserve_path_core(const Policy::Search &searchFunc,
|
|||
if(rv == -1)
|
||||
toremove.push_back(oldfullpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
toremove.push_back(newfullpath);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_rename_preserve_path(const Policy::Search &searchFunc,
|
||||
const Policy::Action &actionFunc,
|
||||
const Policy::Create &createFunc,
|
||||
if(error == 0)
|
||||
l::remove(toremove);
|
||||
|
||||
return -error;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
rename_preserve_path(const Policy::Action &actionPolicy_,
|
||||
const Branches::CPtr &branches_,
|
||||
const char *oldfusepath,
|
||||
const char *newfusepath)
|
||||
{
|
||||
const gfs::path &oldfusepath_,
|
||||
const gfs::path &newfusepath_)
|
||||
{
|
||||
int rv;
|
||||
int error;
|
||||
bool success;
|
||||
StrVec toremove;
|
||||
StrVec oldbasepaths;
|
||||
StrVec branches;
|
||||
gfs::path oldfullpath;
|
||||
gfs::path newfullpath;
|
||||
|
||||
rv = actionFunc(branches_,oldfusepath,&oldbasepaths);
|
||||
rv = actionPolicy_(branches_,oldfusepath_,&oldbasepaths);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
|
||||
branches_->to_paths(branches);
|
||||
|
||||
error = -1;
|
||||
for(size_t i = 0, ei = branches.size(); i != ei; i++)
|
||||
success = false;
|
||||
for(auto &branch : *branches_)
|
||||
{
|
||||
const string &oldbasepath = branches[i];
|
||||
newfullpath = branch.path;
|
||||
newfullpath += newfusepath_;
|
||||
|
||||
_rename_preserve_path_core(searchFunc,createFunc,
|
||||
branches_,
|
||||
oldbasepaths,oldbasepath,
|
||||
oldfusepath,newfusepath,
|
||||
error,toremove);
|
||||
if(!l::contains(oldbasepaths,branch.path))
|
||||
{
|
||||
toremove.push_back(newfullpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(error == 0)
|
||||
_remove(toremove);
|
||||
oldfullpath = branch.path;
|
||||
oldfullpath += oldfusepath_;
|
||||
|
||||
return -error;
|
||||
rv = fs::rename(oldfullpath,newfullpath);
|
||||
if(rv == -1)
|
||||
{
|
||||
toremove.push_back(oldfullpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
// 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 gfs::path &oldfusepath_,
|
||||
const gfs::path &newfusepath_)
|
||||
{
|
||||
int rv;
|
||||
StrVec basepaths;
|
||||
gfs::path target;
|
||||
gfs::path linkpath;
|
||||
|
||||
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_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
rename_exdev_abs_symlink(const Policy::Action &actionPolicy_,
|
||||
const Branches::CPtr &branches_,
|
||||
const std::string &mount_,
|
||||
const gfs::path &oldfusepath_,
|
||||
const gfs::path &newfusepath_)
|
||||
{
|
||||
int rv;
|
||||
StrVec basepaths;
|
||||
gfs::path target;
|
||||
gfs::path linkpath;
|
||||
|
||||
rv = l::rename_exdev_rename_target(actionPolicy_,branches_,oldfusepath_,&basepaths);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
linkpath = newfusepath_;
|
||||
target = mount_;
|
||||
target /= ".mergerfs_rename_exdev";
|
||||
target += oldfusepath_;
|
||||
|
||||
rv = FUSE::symlink(target.c_str(),linkpath.c_str());
|
||||
if(rv < 0)
|
||||
l::rename_exdev_rename_back(basepaths,oldfusepath_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
rename_exdev(Config::Read &cfg_,
|
||||
const gfs::path &oldfusepath_,
|
||||
const gfs::path &newfusepath_)
|
||||
{
|
||||
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_);
|
||||
}
|
||||
|
||||
return -EXDEV;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
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_);
|
||||
|
||||
return l::rename_create_path(cfg_->func.getattr.policy,
|
||||
cfg_->func.rename.policy,
|
||||
cfg_->branches,
|
||||
oldpath_,
|
||||
newpath_);
|
||||
}
|
||||
}
|
||||
|
||||
namespace FUSE
|
||||
{
|
||||
int
|
||||
rename(const char *oldpath,
|
||||
const char *newpath)
|
||||
rename(const char *oldfusepath_,
|
||||
const char *newfusepath_)
|
||||
{
|
||||
int rv;
|
||||
Config::Read cfg;
|
||||
gfs::path oldfusepath(oldfusepath_);
|
||||
gfs::path newfusepath(newfusepath_);
|
||||
const fuse_context *fc = fuse_get_context();
|
||||
const ugid::Set ugid(fc->uid,fc->gid);
|
||||
|
||||
if(cfg->func.create.policy.path_preserving() && !cfg->ignorepponrename)
|
||||
return _rename_preserve_path(cfg->func.getattr.policy,
|
||||
cfg->func.rename.policy,
|
||||
cfg->func.create.policy,
|
||||
cfg->branches,
|
||||
oldpath,
|
||||
newpath);
|
||||
rv = l::rename(cfg,oldfusepath,newfusepath);
|
||||
if(rv == -EXDEV)
|
||||
return l::rename_exdev(cfg,oldfusepath,newfusepath);
|
||||
|
||||
return _rename_create_path(cfg->func.getattr.policy,
|
||||
cfg->func.rename.policy,
|
||||
cfg->branches,
|
||||
oldpath,
|
||||
newpath);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
#include "config.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fs_rmdir.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_rmdir.hpp"
|
||||
#include "fs_unlink.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
@ -50,8 +51,20 @@ namespace l
|
|||
{
|
||||
static
|
||||
int
|
||||
rmdir_loop_core(const string &basepath_,
|
||||
should_unlink(int rv_,
|
||||
int errno_,
|
||||
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;
|
||||
|
@ -60,6 +73,8 @@ namespace l
|
|||
fullpath = fs::path::make(basepath_,fusepath_);
|
||||
|
||||
rv = fs::rmdir(fullpath);
|
||||
if(l::should_unlink(rv,errno,followsymlinks_))
|
||||
rv = fs::unlink(fullpath);
|
||||
|
||||
return error::calc(rv,error_,errno);
|
||||
}
|
||||
|
@ -67,14 +82,15 @@ namespace l
|
|||
static
|
||||
int
|
||||
rmdir_loop(const StrVec &basepaths_,
|
||||
const char *fusepath_)
|
||||
const char *fusepath_,
|
||||
const FollowSymlinks followsymlinks_)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = 0;
|
||||
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;
|
||||
|
@ -84,6 +100,7 @@ namespace l
|
|||
int
|
||||
rmdir(const Policy::Action &actionFunc_,
|
||||
const Branches &branches_,
|
||||
const FollowSymlinks followsymlinks_,
|
||||
const char *fusepath_)
|
||||
{
|
||||
int rv;
|
||||
|
@ -93,7 +110,7 @@ namespace l
|
|||
if(rv == -1)
|
||||
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,
|
||||
cfg->branches,
|
||||
cfg->follow_symlinks,
|
||||
fusepath_);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
|
||||
#include "config.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fs_symlink.hpp"
|
||||
#include "fs_clonepath.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_symlink.hpp"
|
||||
#include "fuse_getattr.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
@ -56,16 +57,16 @@ namespace l
|
|||
static
|
||||
int
|
||||
symlink_loop_core(const string &newbasepath_,
|
||||
const char *oldpath_,
|
||||
const char *newpath_,
|
||||
const char *target_,
|
||||
const char *linkpath_,
|
||||
const int error_)
|
||||
{
|
||||
int rv;
|
||||
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);
|
||||
}
|
||||
|
@ -74,8 +75,8 @@ namespace l
|
|||
int
|
||||
symlink_loop(const string &existingpath_,
|
||||
const StrVec &newbasepaths_,
|
||||
const char *oldpath_,
|
||||
const char *newpath_,
|
||||
const char *target_,
|
||||
const char *linkpath_,
|
||||
const string &newdirpath_)
|
||||
{
|
||||
int rv;
|
||||
|
@ -89,8 +90,8 @@ namespace l
|
|||
error = error::calc(rv,error,errno);
|
||||
else
|
||||
error = l::symlink_loop_core(newbasepaths_[i],
|
||||
oldpath_,
|
||||
newpath_,
|
||||
target_,
|
||||
linkpath_,
|
||||
error);
|
||||
}
|
||||
|
||||
|
@ -102,15 +103,15 @@ namespace l
|
|||
symlink(const Policy::Search &searchFunc_,
|
||||
const Policy::Create &createFunc_,
|
||||
const Branches &branches_,
|
||||
const char *oldpath_,
|
||||
const char *newpath_)
|
||||
const char *target_,
|
||||
const char *linkpath_)
|
||||
{
|
||||
int rv;
|
||||
string newdirpath;
|
||||
StrVec newbasepaths;
|
||||
StrVec existingpaths;
|
||||
|
||||
newdirpath = fs::path::dirname(newpath_);
|
||||
newdirpath = fs::path::dirname(linkpath_);
|
||||
|
||||
rv = searchFunc_(branches_,newdirpath,&existingpaths);
|
||||
if(rv == -1)
|
||||
|
@ -121,15 +122,15 @@ namespace l
|
|||
return -errno;
|
||||
|
||||
return l::symlink_loop(existingpaths[0],newbasepaths,
|
||||
oldpath_,newpath_,newdirpath);
|
||||
target_,linkpath_,newdirpath);
|
||||
}
|
||||
}
|
||||
|
||||
namespace FUSE
|
||||
{
|
||||
int
|
||||
symlink(const char *oldpath_,
|
||||
const char *newpath_)
|
||||
symlink(const char *target_,
|
||||
const char *linkpath_)
|
||||
{
|
||||
Config::Read cfg;
|
||||
const fuse_context *fc = fuse_get_context();
|
||||
|
@ -138,7 +139,22 @@ namespace FUSE
|
|||
return l::symlink(cfg->func.getattr.policy,
|
||||
cfg->func.symlink.policy,
|
||||
cfg->branches,
|
||||
oldpath_,
|
||||
newpath_);
|
||||
target_,
|
||||
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_);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -16,10 +16,19 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace FUSE
|
||||
{
|
||||
int
|
||||
symlink(const char *oldpath,
|
||||
const char *newpath);
|
||||
symlink(const char *target,
|
||||
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
5742
src/ghc/filesystem.hpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user