mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-26 19:01:56 -04: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
26 changed files with 6980 additions and 450 deletions
|
|
@ -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_);
|
||||
|
||||
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)
|
||||
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)
|
||||
error = error::calc(rv,error,errno);
|
||||
else
|
||||
error = l::link_create_path_core(oldbasepaths_[i],newbasepath_,
|
||||
oldfusepath_,newfusepath_,
|
||||
error);
|
||||
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);
|
||||
}
|
||||
|
||||
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,47 +119,17 @@ 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_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_,
|
||||
const int error_)
|
||||
link_preserve_path_core(const string &oldbasepath_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_,
|
||||
struct stat *st_,
|
||||
const int error_)
|
||||
{
|
||||
int rv;
|
||||
string oldfullpath;
|
||||
|
|
@ -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_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_,
|
||||
const StrVec &oldbasepaths_)
|
||||
link_preserve_path_loop(const StrVec &oldbasepaths_,
|
||||
const char *oldfusepath_,
|
||||
const char *newfusepath_,
|
||||
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,265 +58,351 @@ namespace error
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
member(const StrVec &haystack,
|
||||
const string &needle)
|
||||
namespace l
|
||||
{
|
||||
for(size_t i = 0, ei = haystack.size(); i != ei; i++)
|
||||
{
|
||||
if(haystack[i] == needle)
|
||||
return true;
|
||||
}
|
||||
static
|
||||
bool
|
||||
contains(const StrVec &haystack_,
|
||||
const char *needle_)
|
||||
{
|
||||
for(auto &hay : haystack_)
|
||||
{
|
||||
if(hay == needle_)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_remove(const StrVec &toremove)
|
||||
{
|
||||
for(size_t i = 0, ei = toremove.size(); i != ei; i++)
|
||||
fs::remove(toremove[i]);
|
||||
}
|
||||
static
|
||||
bool
|
||||
contains(const StrVec &haystack_,
|
||||
const string &needle_)
|
||||
{
|
||||
return l::contains(haystack_,needle_.c_str());
|
||||
}
|
||||
|
||||
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;
|
||||
static
|
||||
void
|
||||
remove(const StrVec &toremove_)
|
||||
{
|
||||
for(auto &path : toremove_)
|
||||
fs::remove(path);
|
||||
}
|
||||
|
||||
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);
|
||||
static
|
||||
void
|
||||
remove(const Branches::CPtr &branches_,
|
||||
const std::string &relpath_)
|
||||
{
|
||||
std::string fullpath;
|
||||
|
||||
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);
|
||||
if(rv == -1)
|
||||
tounlink.push_back(oldfullpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
newfullpath = fs::path::make(oldbasepath,newfusepath);
|
||||
static
|
||||
int
|
||||
rename_create_path(const Policy::Search &searchPolicy_,
|
||||
const Policy::Action &actionPolicy_,
|
||||
const Branches::CPtr &branches_,
|
||||
const gfs::path &oldfusepath_,
|
||||
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
|
||||
int
|
||||
_rename_create_path(const Policy::Search &searchFunc,
|
||||
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 = searchPolicy_(branches_,newfusepath_.parent_path(),&newbasepath);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
|
||||
rv = actionFunc(branches_,oldfusepath,&oldbasepaths);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
error = -1;
|
||||
for(auto &branch : *branches_)
|
||||
{
|
||||
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);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
oldfullpath = branch.path;
|
||||
oldfullpath += oldfusepath_;
|
||||
|
||||
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;
|
||||
for(size_t i = 0, ei = branches.size(); i != ei; i++)
|
||||
{
|
||||
const string &oldbasepath = branches[i];
|
||||
error = error::calc(rv,error,errno);
|
||||
if(rv == -1)
|
||||
toremove.push_back(oldfullpath);
|
||||
}
|
||||
|
||||
_rename_create_path_core(oldbasepaths,
|
||||
oldbasepath,newbasepath[0],
|
||||
oldfusepath,newfusepath,
|
||||
newfusedirpath,
|
||||
error,toremove);
|
||||
}
|
||||
if(error == 0)
|
||||
l::remove(toremove);
|
||||
|
||||
return -error;
|
||||
}
|
||||
|
||||
if(error == 0)
|
||||
_remove(toremove);
|
||||
static
|
||||
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
|
||||
int
|
||||
_clonepath(const Policy::Search &searchFunc,
|
||||
const Branches::CPtr &branches_,
|
||||
const string &dstbasepath,
|
||||
const string &fusedirpath)
|
||||
{
|
||||
int rv;
|
||||
StrVec srcbasepath;
|
||||
success = false;
|
||||
for(auto &branch : *branches_)
|
||||
{
|
||||
newfullpath = branch.path;
|
||||
newfullpath += newfusepath_;
|
||||
|
||||
rv = searchFunc(branches_,fusedirpath.c_str(),&srcbasepath);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
if(!l::contains(oldbasepaths,branch.path))
|
||||
{
|
||||
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
|
||||
int
|
||||
_clonepath_if_would_create(const Policy::Search &searchFunc,
|
||||
const Policy::Create &createFunc,
|
||||
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 string &oldbasepath,
|
||||
const char *oldfusepath,
|
||||
const char *newfusepath)
|
||||
{
|
||||
int rv;
|
||||
string newfusedirpath;
|
||||
StrVec newbasepath;
|
||||
const gfs::path &oldfusepath_,
|
||||
const gfs::path &newfusepath_)
|
||||
{
|
||||
int rv;
|
||||
StrVec basepaths;
|
||||
gfs::path target;
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
static
|
||||
int
|
||||
rename_exdev_abs_symlink(const Policy::Action &actionPolicy_,
|
||||
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;
|
||||
const std::string &mount_,
|
||||
const gfs::path &oldfusepath_,
|
||||
const gfs::path &newfusepath_)
|
||||
{
|
||||
int rv;
|
||||
StrVec basepaths;
|
||||
gfs::path target;
|
||||
gfs::path linkpath;
|
||||
|
||||
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);
|
||||
if(ismember)
|
||||
{
|
||||
string oldfullpath;
|
||||
linkpath = newfusepath_;
|
||||
target = mount_;
|
||||
target /= ".mergerfs_rename_exdev";
|
||||
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);
|
||||
if((rv == -1) && (errno == ENOENT))
|
||||
{
|
||||
rv = _clonepath_if_would_create(searchFunc,createFunc,
|
||||
branches_,oldbasepath,
|
||||
oldfusepath,newfusepath);
|
||||
if(rv == 0)
|
||||
rv = fs::rename(oldfullpath,newfullpath);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
error = error::calc(rv,error,errno);
|
||||
if(rv == -1)
|
||||
toremove.push_back(oldfullpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
toremove.push_back(newfullpath);
|
||||
}
|
||||
}
|
||||
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_);
|
||||
}
|
||||
|
||||
static
|
||||
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;
|
||||
return -EXDEV;
|
||||
}
|
||||
|
||||
rv = actionFunc(branches_,oldfusepath,&oldbasepaths);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
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_);
|
||||
|
||||
branches_->to_paths(branches);
|
||||
|
||||
error = -1;
|
||||
for(size_t i = 0, ei = branches.size(); i != ei; i++)
|
||||
{
|
||||
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;
|
||||
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,9 +51,21 @@ namespace l
|
|||
{
|
||||
static
|
||||
int
|
||||
rmdir_loop_core(const string &basepath_,
|
||||
const char *fusepath_,
|
||||
const int error_)
|
||||
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;
|
||||
string fullpath;
|
||||
|
|
@ -60,21 +73,24 @@ 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);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
rmdir_loop(const StrVec &basepaths_,
|
||||
const char *fusepath_)
|
||||
rmdir_loop(const StrVec &basepaths_,
|
||||
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…
Add table
Add a link
Reference in a new issue