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
|
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
|
||||||
|
|
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=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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
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:
|
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
|
||||||
|
|
|
@ -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
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
|
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 :
|
||||||
|
|
|
@ -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_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
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