mirror of
https://github.com/trapexit/mergerfs.git
synced 2024-11-22 11:39:52 +08:00
Create functions can set branches RO on EROFS
This commit is contained in:
parent
36a4b7a23a
commit
707d298d7c
|
@ -747,6 +747,12 @@ If all branches are filtered an error will be returned. Typically
|
|||
device) depending on the most recent reason for filtering a
|
||||
branch. **ENOENT** will be returned if no eligible branch is found.
|
||||
|
||||
If **create**, **mkdir**, **mknod**, or **symlink** fail with `EROFS`
|
||||
or other fundimental errors then mergerfs will mark any branch found
|
||||
to be read-only as such (IE will set the mode `RO`) and will rerun the
|
||||
policy and try again. This is mostly for `ext4` filesystems that can
|
||||
suddenly become read-only when it encounters an error.
|
||||
|
||||
|
||||
#### Path Preservation
|
||||
|
||||
|
|
|
@ -311,12 +311,12 @@ reading FUSE messages which are dispatched to process threads.
|
|||
-1 means disabled otherwise acts like \f[C]read-thread-count\f[R].
|
||||
(default: -1)
|
||||
.IP \[bu] 2
|
||||
\f[B]process-thread-queue-depth=INT\f[R]: Sets the number of requests
|
||||
\f[B]process-thread-queue-depth=UINT\f[R]: Sets the number of requests
|
||||
any single process thread can have queued up at one time.
|
||||
Meaning the total memory usage of the queues is queue depth multiplied
|
||||
by the number of process threads plus read thread count.
|
||||
-1 sets the depth to the same as the process thread count.
|
||||
(default: -1)
|
||||
0 sets the depth to the same as the process thread count.
|
||||
(default: 0)
|
||||
.IP \[bu] 2
|
||||
\f[B]pin-threads=STR\f[R]: Selects a strategy to pin threads to CPUs
|
||||
(default: unset)
|
||||
|
@ -937,6 +937,13 @@ Typically \f[B]EROFS\f[R] (read-only filesystem) or \f[B]ENOSPC\f[R] (no
|
|||
space left on device) depending on the most recent reason for filtering
|
||||
a branch.
|
||||
\f[B]ENOENT\f[R] will be returned if no eligible branch is found.
|
||||
.PP
|
||||
If \f[B]create\f[R], \f[B]mkdir\f[R], \f[B]mknod\f[R], or
|
||||
\f[B]symlink\f[R] fail with \f[C]EROFS\f[R] or other fundimental errors
|
||||
then mergerfs will mark any branch found to be read-only as such (IE
|
||||
will set the mode \f[C]RO\f[R]) and will rerun the policy and try again.
|
||||
This is mostly for \f[C]ext4\f[R] filesystems that can suddenly become
|
||||
read-only when it encounters an error.
|
||||
.SS Path Preservation
|
||||
.PP
|
||||
Policies, as described below, are of two basic classifications.
|
||||
|
|
|
@ -21,10 +21,12 @@
|
|||
#include "errno.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "fs_glob.hpp"
|
||||
#include "fs_is_rofs.hpp"
|
||||
#include "fs_realpathize.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "num.hpp"
|
||||
#include "str.hpp"
|
||||
#include "syslog.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -415,6 +417,24 @@ Branches::to_string(void) const
|
|||
return _impl->to_string();
|
||||
}
|
||||
|
||||
void
|
||||
Branches::find_and_set_mode_ro()
|
||||
{
|
||||
for(auto &branch : *_impl)
|
||||
{
|
||||
if(branch.mode != Branch::Mode::RW)
|
||||
continue;
|
||||
|
||||
if(!fs::is_rofs_but_not_mounted_ro(branch.path))
|
||||
continue;
|
||||
|
||||
syslog_warning("Branch %s found to be readonly - setting its mode to RO",
|
||||
branch.path.c_str());
|
||||
|
||||
branch.mode = Branch::Mode::RO;
|
||||
}
|
||||
}
|
||||
|
||||
SrcMounts::SrcMounts(Branches &b_)
|
||||
: _branches(b_)
|
||||
{
|
||||
|
|
|
@ -76,6 +76,9 @@ public:
|
|||
operator CPtr() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
|
||||
CPtr operator->() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
|
||||
|
||||
public:
|
||||
void find_and_set_mode_ro();
|
||||
|
||||
private:
|
||||
mutable std::mutex _mutex;
|
||||
Ptr _impl;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "fs_lstat.hpp"
|
||||
#include "fs_mktemp.hpp"
|
||||
#include "fs_open.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_rename.hpp"
|
||||
#include "fs_unlink.hpp"
|
||||
|
||||
|
@ -109,16 +110,14 @@ namespace fs
|
|||
int rv;
|
||||
int src_fd;
|
||||
int dst_fd;
|
||||
string dst_fullpath;
|
||||
std::string dst_fullpath;
|
||||
|
||||
src_fd = fs::open(src_fullpath_,O_RDONLY|O_NOFOLLOW);
|
||||
if(src_fd == -1)
|
||||
return -1;
|
||||
|
||||
dst_fullpath = src_fullpath_;
|
||||
|
||||
dst_fd = fs::mktemp(&dst_fullpath,O_WRONLY);
|
||||
if(dst_fd == -1)
|
||||
std::tie(dst_fd,dst_fullpath) = fs::mktemp(src_fullpath_,O_WRONLY);
|
||||
if(dst_fd < 0)
|
||||
return l::cleanup_on_error(src_fd);
|
||||
|
||||
rv = fs::clonefile(src_fd,dst_fd);
|
||||
|
|
75
src/fs_is_rofs.hpp
Normal file
75
src/fs_is_rofs.hpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, 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 "fs_mktemp.hpp"
|
||||
#include "fs_statvfs.hpp"
|
||||
#include "fs_unlink.hpp"
|
||||
#include "statvfs_util.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_mounted_rofs(const std::string path_)
|
||||
{
|
||||
int rv;
|
||||
struct statvfs st;
|
||||
|
||||
rv = fs::statvfs(path_,&st);
|
||||
|
||||
return ((rv == 0) ? StatVFS::readonly(st) : false);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_rofs(std::string path_)
|
||||
{
|
||||
ugid::SetRootGuard const ugid;
|
||||
|
||||
int fd;
|
||||
std::string tmp_filepath;
|
||||
|
||||
std::tie(fd,tmp_filepath) = fs::mktemp_in_dir(path_,O_WRONLY);
|
||||
if(fd < 0)
|
||||
return (fd == -EROFS);
|
||||
|
||||
fs::close(fd);
|
||||
fs::unlink(tmp_filepath);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_rofs_but_not_mounted_ro(const std::string path_)
|
||||
{
|
||||
if(fs::is_mounted_rofs(path_))
|
||||
return false;
|
||||
|
||||
return fs::is_rofs(path_);
|
||||
}
|
||||
}
|
|
@ -18,61 +18,74 @@
|
|||
|
||||
#include "errno.hpp"
|
||||
#include "fs_open.hpp"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include <limits.h>
|
||||
#define PAD_LEN 16
|
||||
#define MAX_ATTEMPTS 3
|
||||
|
||||
using std::string;
|
||||
static char const CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
#define PADLEN 6
|
||||
#define MAX_ATTEMPTS 10
|
||||
|
||||
|
||||
static
|
||||
string
|
||||
generate_tmp_path(const string &base_)
|
||||
namespace l
|
||||
{
|
||||
string tmp;
|
||||
static
|
||||
std::string
|
||||
generate_tmp_path(std::string const base_)
|
||||
{
|
||||
fs::Path path;
|
||||
std::string filename;
|
||||
|
||||
tmp = base_;
|
||||
if((tmp.size() + PADLEN + 1) > PATH_MAX)
|
||||
tmp.resize(tmp.size() - PADLEN - 1);
|
||||
tmp += '.';
|
||||
for(int i = 0; i < PADLEN; i++)
|
||||
tmp += ('A' + (std::rand() % 26));
|
||||
filename = '.';
|
||||
for(int i = 0; i < PAD_LEN; i++)
|
||||
filename += CHARS[std::rand() % (sizeof(CHARS) - 1)];
|
||||
|
||||
return tmp;
|
||||
path = base_;
|
||||
path /= filename;
|
||||
|
||||
return path.string();
|
||||
}
|
||||
}
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int
|
||||
mktemp(string *base_,
|
||||
const int flags_)
|
||||
std::tuple<int,std::string>
|
||||
mktemp_in_dir(std::string const dirpath_,
|
||||
int const flags_)
|
||||
{
|
||||
int fd;
|
||||
int count;
|
||||
int flags;
|
||||
string tmppath;
|
||||
std::string tmp_filepath;
|
||||
|
||||
fd = -1;
|
||||
count = MAX_ATTEMPTS;
|
||||
flags = (flags_ | O_EXCL | O_CREAT | O_TRUNC);
|
||||
while(count-- > 0)
|
||||
{
|
||||
tmppath = generate_tmp_path(*base_);
|
||||
tmp_filepath = l::generate_tmp_path(dirpath_);
|
||||
|
||||
fd = fs::open(tmppath,flags,S_IWUSR);
|
||||
fd = fs::open(tmp_filepath,flags,S_IWUSR);
|
||||
if((fd == -1) && (errno == EEXIST))
|
||||
continue;
|
||||
else if(fd != -1)
|
||||
*base_ = tmppath;
|
||||
if(fd == -1)
|
||||
return {-errno,std::string{}};
|
||||
|
||||
return fd;
|
||||
return {fd,tmp_filepath};
|
||||
}
|
||||
|
||||
return (errno=EEXIST,-1);
|
||||
return {-EEXIST,std::string{}};
|
||||
}
|
||||
|
||||
std::tuple<int,std::string>
|
||||
mktemp(std::string const filepath_,
|
||||
int const flags_)
|
||||
{
|
||||
ghc::filesystem::path filepath{filepath_};
|
||||
|
||||
return fs::mktemp_in_dir(filepath.parent_path(),flags_);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int
|
||||
mktemp(std::string *base,
|
||||
const int flags);
|
||||
std::tuple<int,std::string>
|
||||
mktemp(std::string const filepath,
|
||||
int const flags);
|
||||
|
||||
std::tuple<int,std::string>
|
||||
mktemp_in_dir(std::string const dirpath,
|
||||
int const flags);
|
||||
}
|
||||
|
|
|
@ -117,9 +117,8 @@ namespace l
|
|||
|
||||
dstfd_filepath = dstfd_branch[0];
|
||||
fs::path::append(dstfd_filepath,fusepath_);
|
||||
dstfd_tmp_filepath = dstfd_filepath;
|
||||
dstfd = fs::mktemp(&dstfd_tmp_filepath,O_WRONLY);
|
||||
if(dstfd == -1)
|
||||
std::tie(dstfd,dstfd_tmp_filepath) = fs::mktemp(dstfd_filepath,O_WRONLY);
|
||||
if(dstfd < 0)
|
||||
{
|
||||
fs::close(srcfd);
|
||||
return -ENOSPC;
|
||||
|
|
|
@ -207,6 +207,17 @@ namespace FUSE
|
|||
ffi_,
|
||||
mode_,
|
||||
fc->umask);
|
||||
if(rv == -EROFS)
|
||||
{
|
||||
Config::Write()->branches.find_and_set_mode_ro();
|
||||
rv = l::create(cfg->func.getattr.policy,
|
||||
cfg->func.create.policy,
|
||||
cfg->branches,
|
||||
fusepath_,
|
||||
ffi_,
|
||||
mode_,
|
||||
fc->umask);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -352,7 +352,7 @@ namespace FUSE
|
|||
|
||||
rv = l::link(cfg,oldpath_,newpath_,st_,timeouts_);
|
||||
if(rv == -EXDEV)
|
||||
return l::link_exdev(cfg,oldpath_,newpath_,st_,timeouts_);
|
||||
rv = l::link_exdev(cfg,oldpath_,newpath_,st_,timeouts_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -150,15 +150,28 @@ namespace FUSE
|
|||
mkdir(const char *fusepath_,
|
||||
mode_t mode_)
|
||||
{
|
||||
int rv;
|
||||
Config::Read cfg;
|
||||
const fuse_context *fc = fuse_get_context();
|
||||
const ugid::Set ugid(fc->uid,fc->gid);
|
||||
|
||||
return l::mkdir(cfg->func.getattr.policy,
|
||||
cfg->func.mkdir.policy,
|
||||
cfg->branches,
|
||||
fusepath_,
|
||||
mode_,
|
||||
fc->umask);
|
||||
rv = l::mkdir(cfg->func.getattr.policy,
|
||||
cfg->func.mkdir.policy,
|
||||
cfg->branches,
|
||||
fusepath_,
|
||||
mode_,
|
||||
fc->umask);
|
||||
if(rv == -EROFS)
|
||||
{
|
||||
Config::Write()->branches.find_and_set_mode_ro();
|
||||
rv = l::mkdir(cfg->func.getattr.policy,
|
||||
cfg->func.mkdir.policy,
|
||||
cfg->branches,
|
||||
fusepath_,
|
||||
mode_,
|
||||
fc->umask);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,16 +152,30 @@ namespace FUSE
|
|||
mode_t mode_,
|
||||
dev_t rdev_)
|
||||
{
|
||||
int rv;
|
||||
Config::Read cfg;
|
||||
const fuse_context *fc = fuse_get_context();
|
||||
const ugid::Set ugid(fc->uid,fc->gid);
|
||||
|
||||
return l::mknod(cfg->func.getattr.policy,
|
||||
cfg->func.mknod.policy,
|
||||
cfg->branches,
|
||||
fusepath_,
|
||||
mode_,
|
||||
fc->umask,
|
||||
rdev_);
|
||||
rv = l::mknod(cfg->func.getattr.policy,
|
||||
cfg->func.mknod.policy,
|
||||
cfg->branches,
|
||||
fusepath_,
|
||||
mode_,
|
||||
fc->umask,
|
||||
rdev_);
|
||||
if(rv == -EROFS)
|
||||
{
|
||||
Config::Write()->branches.find_and_set_mode_ro();
|
||||
rv = l::mknod(cfg->func.getattr.policy,
|
||||
cfg->func.mknod.policy,
|
||||
cfg->branches,
|
||||
fusepath_,
|
||||
mode_,
|
||||
fc->umask,
|
||||
rdev_);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,6 +157,16 @@ namespace FUSE
|
|||
target_,
|
||||
linkpath_,
|
||||
st_);
|
||||
if(rv == -EROFS)
|
||||
{
|
||||
Config::Write()->branches.find_and_set_mode_ro();
|
||||
rv = l::symlink(cfg->func.getattr.policy,
|
||||
cfg->func.symlink.policy,
|
||||
cfg->branches,
|
||||
target_,
|
||||
linkpath_,
|
||||
st_);
|
||||
}
|
||||
|
||||
if(timeouts_ != NULL)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user