mirror of
https://github.com/trapexit/mergerfs.git
synced 2024-04-21 12:31:43 +00:00
533 lines
14 KiB
C++
533 lines
14 KiB
C++
/*
|
|
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 "humanize.hpp"
|
|
|
|
#include "toml.hpp"
|
|
|
|
#include <initializer_list>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
typedef std::vector<std::string> strvec;
|
|
|
|
namespace toml
|
|
{
|
|
enum class Status
|
|
{
|
|
OPTIONAL,
|
|
REQUIRED
|
|
};
|
|
|
|
constexpr auto INODE_CALC_ENUMS =
|
|
{
|
|
"passthrough",
|
|
"path-hash",
|
|
"path-hash32",
|
|
"devino-hash",
|
|
"devino-hash32",
|
|
"hybrid-hash",
|
|
"hybrid-hash32"
|
|
};
|
|
|
|
constexpr auto XATTR_ENUMS =
|
|
{
|
|
"passthrough",
|
|
"noattr",
|
|
"nosys"
|
|
};
|
|
|
|
constexpr auto STATFS_ENUMS =
|
|
{
|
|
"base",
|
|
"full"
|
|
};
|
|
|
|
constexpr auto STATFS_IGNORE_ENUMS =
|
|
{
|
|
"none",
|
|
"ro",
|
|
"nc"
|
|
};
|
|
|
|
constexpr auto NFS_OPEN_HACK_ENUMS =
|
|
{
|
|
"off",
|
|
"git",
|
|
"all"
|
|
};
|
|
|
|
constexpr auto FOLLOW_SYMLINKS_ENUMS =
|
|
{
|
|
"never",
|
|
"directory",
|
|
"regular",
|
|
"all"
|
|
};
|
|
|
|
constexpr auto LINK_EXDEV_ENUMS =
|
|
{
|
|
"passthrough",
|
|
"rel-symlink",
|
|
"abs-base-symlink",
|
|
"abs-pool-symlink"
|
|
};
|
|
|
|
constexpr auto RENAME_EXDEV_ENUMS =
|
|
{
|
|
"passthrough",
|
|
"rel-symlink",
|
|
"abs-symlink"
|
|
};
|
|
|
|
constexpr auto ACTION_POLICY_ENUMS =
|
|
{
|
|
"all",
|
|
"epall",
|
|
"epff",
|
|
"eplfs",
|
|
"eplus",
|
|
"epmfs",
|
|
"eppfrd",
|
|
"eprand",
|
|
"erofs",
|
|
"ff",
|
|
"lfs",
|
|
"lus",
|
|
"mfs",
|
|
"msplfs",
|
|
"msplus",
|
|
"mspmfs",
|
|
"msppfrd",
|
|
"newest",
|
|
"pfrd",
|
|
"rand"
|
|
};
|
|
|
|
constexpr auto CREATE_POLICY_ENUMS =
|
|
{
|
|
"all",
|
|
"epall",
|
|
"epff",
|
|
"eplfs",
|
|
"eplus",
|
|
"epmfs",
|
|
"eppfrd",
|
|
"eprand",
|
|
"erofs",
|
|
"ff",
|
|
"lfs",
|
|
"lus",
|
|
"mfs",
|
|
"msplfs",
|
|
"msplus",
|
|
"mspmfs",
|
|
"msppfrd",
|
|
"newest",
|
|
"pfrd",
|
|
"rand"
|
|
};
|
|
|
|
constexpr auto SEARCH_POLICY_ENUMS =
|
|
{
|
|
"all",
|
|
"epall",
|
|
"epff",
|
|
"eplfs",
|
|
"eplus",
|
|
"epmfs",
|
|
"eppfrd",
|
|
"eprand",
|
|
"erofs",
|
|
"ff",
|
|
"lfs",
|
|
"lus",
|
|
"mfs",
|
|
"msplfs",
|
|
"msplus",
|
|
"mspmfs",
|
|
"msppfrd",
|
|
"newest",
|
|
"pfrd",
|
|
"rand"
|
|
};
|
|
|
|
constexpr auto CACHE_FILES_ENUMS =
|
|
{
|
|
"off",
|
|
"partial",
|
|
"full",
|
|
"auto"
|
|
};
|
|
|
|
constexpr auto BRANCH_MODE_ENUMS =
|
|
{
|
|
"RW",
|
|
"RO",
|
|
"NC"
|
|
};
|
|
|
|
void
|
|
required(const toml::value &val_,
|
|
const toml::key &key_,
|
|
strvec &errs_)
|
|
{
|
|
std::string str;
|
|
|
|
str = "[error] required key: " + key_;
|
|
str = toml::format_error(str,val_,"");
|
|
|
|
errs_.push_back(str);
|
|
}
|
|
|
|
static
|
|
void
|
|
invalid_type(const toml::value &val_,
|
|
const toml::value_t type_,
|
|
strvec &errs_)
|
|
{
|
|
std::string str;
|
|
|
|
str = "[error] invalid type";
|
|
str = toml::format_error(str,val_,"should be " + toml::stringize(type_));
|
|
|
|
errs_.push_back(str);
|
|
}
|
|
|
|
template<class T>
|
|
static
|
|
void
|
|
invalid_enum(const toml::value &val_,
|
|
const T &enums_,
|
|
strvec &errs_)
|
|
{
|
|
std::string str;
|
|
strvec hints;
|
|
|
|
for(const auto &e : enums_)
|
|
hints.push_back(e);
|
|
|
|
str = "[error] invalid enum";
|
|
str = toml::format_error(str,val_,"",hints);
|
|
|
|
errs_.push_back(str);
|
|
}
|
|
|
|
static
|
|
void
|
|
invalid_human_size(const toml::value &val_,
|
|
strvec &errs_)
|
|
{
|
|
std::string str;
|
|
const strvec hints = {"'1048576'","'1024K'","'512M'","'4G'","'1T'"};
|
|
|
|
str = "[error] invalid 'humanize' integer";
|
|
str = toml::format_error(str,val_,"",hints);
|
|
|
|
errs_.push_back(str);
|
|
}
|
|
|
|
void
|
|
verify_int(const toml::value &doc_,
|
|
const toml::key &key_,
|
|
const int64_t min_,
|
|
const int64_t max_,
|
|
toml::Status status_,
|
|
strvec &errs_)
|
|
{
|
|
const bool key_exists = doc_.contains(key_);
|
|
|
|
if((status_ == Status::REQUIRED) && (key_exists == false))
|
|
return toml::required(doc_,key_,errs_);
|
|
if(key_exists == false)
|
|
return;
|
|
|
|
const auto &val = doc_.at(key_);
|
|
if(val.is_integer() == false)
|
|
{
|
|
std::string str;
|
|
|
|
str = toml::format_error("[error] wrong type: should be integer",
|
|
val,
|
|
"",
|
|
{"min = " + std::to_string(min_),
|
|
"max = " + std::to_string(max_)});
|
|
errs_.push_back(str);
|
|
|
|
return;
|
|
}
|
|
|
|
if(val.as_integer() < min_)
|
|
{
|
|
std::string str;
|
|
|
|
str = toml::format_error("[error] value below valid minimum",
|
|
val,
|
|
"min = " + std::to_string(min_));
|
|
errs_.push_back(str);
|
|
|
|
return;
|
|
}
|
|
|
|
if(val.as_integer() > max_)
|
|
{
|
|
std::string str;
|
|
|
|
str = toml::format_error("[error] value above valid maximum",
|
|
val,
|
|
"max = " + std::to_string(max_));
|
|
errs_.push_back(str);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
verify_uint(const toml::value &doc_,
|
|
const toml::key &key_,
|
|
const int64_t max_,
|
|
toml::Status status_,
|
|
strvec &errs_)
|
|
{
|
|
constexpr int64_t min = 0;
|
|
|
|
toml::verify_int(doc_,key_,min,max_,status_,errs_);
|
|
}
|
|
|
|
void
|
|
verify_uint(const toml::value &doc_,
|
|
const toml::key &key_,
|
|
toml::Status status_,
|
|
strvec &errs_)
|
|
{
|
|
constexpr int64_t max = std::numeric_limits<int64_t>::max();
|
|
|
|
toml::verify_uint(doc_,key_,max,status_,errs_);
|
|
}
|
|
|
|
void
|
|
verify_string(const toml::value &doc_,
|
|
const toml::key &key_,
|
|
toml::Status status_,
|
|
strvec &errs_)
|
|
{
|
|
if((status_ == Status::REQUIRED) && (doc_.contains(key_) == false))
|
|
return toml::required(doc_,key_,errs_);
|
|
if(doc_.contains(key_) == false)
|
|
return;
|
|
if(doc_.at(key_).is_string() == false)
|
|
return toml::invalid_type(doc_.at(key_),toml::value_t::string,errs_);
|
|
}
|
|
|
|
void
|
|
verify_human_size(const toml::value &doc_,
|
|
const toml::key &key_,
|
|
toml::Status status_,
|
|
strvec &errs_)
|
|
{
|
|
if((status_ == Status::REQUIRED) && (doc_.contains(key_) == false))
|
|
return toml::required(doc_,key_,errs_);
|
|
if(doc_.contains(key_) == false)
|
|
return;
|
|
|
|
const auto &val = doc_.at(key_);
|
|
if(val.is_integer() == true)
|
|
return;
|
|
|
|
if((val.is_string() == false) && (val.is_integer() == false))
|
|
return toml::invalid_human_size(val,errs_);
|
|
|
|
uint64_t u;
|
|
if(humanize::from(val.as_string(),&u) != 0)
|
|
return toml::invalid_human_size(val,errs_);
|
|
}
|
|
|
|
static
|
|
void
|
|
verify_boolean(const toml::value &doc_,
|
|
const toml::key &key_,
|
|
toml::Status status_,
|
|
strvec &errs_)
|
|
{
|
|
const bool key_exists = doc_.contains(key_);
|
|
|
|
if((status_ == Status::REQUIRED) && (key_exists == false))
|
|
return toml::required(doc_,key_,errs_);
|
|
if(key_exists == false)
|
|
return;
|
|
if(doc_.at(key_).is_boolean() == false)
|
|
return toml::invalid_type(doc_.at(key_),toml::value_t::boolean,errs_);
|
|
}
|
|
|
|
template<class T>
|
|
static
|
|
void
|
|
verify_enum(const toml::value &doc_,
|
|
const toml::key &key_,
|
|
const T &enums_,
|
|
toml::Status status_,
|
|
strvec &errs_)
|
|
{
|
|
const bool key_exists = doc_.contains(key_);
|
|
|
|
if((status_ == Status::REQUIRED) && (key_exists == false))
|
|
return toml::required(doc_,key_,errs_);
|
|
if(key_exists == false)
|
|
return;
|
|
if(doc_.at(key_).is_string() == false)
|
|
return toml::invalid_enum(doc_.at(key_),enums_,errs_);
|
|
|
|
const std::string &val = doc_.at(key_).as_string();
|
|
for(const auto &e : enums_)
|
|
{
|
|
if(val == e)
|
|
return;
|
|
}
|
|
|
|
return toml::invalid_enum(doc_.at(key_),enums_,errs_);
|
|
}
|
|
|
|
static
|
|
void
|
|
verify_cache_table(const toml::value &doc_,
|
|
strvec &errs_)
|
|
{
|
|
if(doc_.contains("cache") == false)
|
|
return toml::required(doc_,"cache",errs_);
|
|
|
|
const auto &cache_table = doc_.at("cache");
|
|
|
|
toml::verify_enum(cache_table,"files",CACHE_FILES_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_uint(cache_table,"open",Status::OPTIONAL,errs_);
|
|
toml::verify_uint(cache_table,"statfs",Status::OPTIONAL,errs_);
|
|
toml::verify_uint(cache_table,"attr",Status::OPTIONAL,errs_);
|
|
toml::verify_uint(cache_table,"entry",Status::OPTIONAL,errs_);
|
|
toml::verify_uint(cache_table,"negative-entry",Status::OPTIONAL,errs_);
|
|
toml::verify_boolean(cache_table,"writeback",Status::OPTIONAL,errs_);
|
|
toml::verify_boolean(cache_table,"symlinks",Status::OPTIONAL,errs_);
|
|
toml::verify_boolean(cache_table,"readdir",Status::OPTIONAL,errs_);
|
|
}
|
|
|
|
static
|
|
void
|
|
verify_policy_table(const toml::value &doc_,
|
|
strvec &errs_)
|
|
{
|
|
if(doc_.contains("policy") == false)
|
|
return toml::required(doc_,"policy",errs_);
|
|
|
|
const auto &policy_table = doc_.at("policy");
|
|
|
|
if(policy_table.is_table() == false)
|
|
return toml::invalid_type(policy_table,toml::value_t::table,errs_);
|
|
|
|
toml::verify_enum(policy_table,"chmod",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"chown",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"link",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"removexattr",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"rename",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"rmdir",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"setxattr",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"truncate",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"unlink",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"utimens",ACTION_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
|
|
toml::verify_enum(policy_table,"create",CREATE_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"mkdir",CREATE_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"mknod",CREATE_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"symlink",CREATE_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
|
|
toml::verify_enum(policy_table,"access",SEARCH_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"getattr",SEARCH_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"getxattr",SEARCH_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"listxattr",SEARCH_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"open",SEARCH_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(policy_table,"readlink",SEARCH_POLICY_ENUMS,Status::REQUIRED,errs_);
|
|
}
|
|
|
|
static
|
|
void
|
|
verify_branch_table(const toml::value &branch_,
|
|
strvec &errs_)
|
|
{
|
|
toml::verify_string(branch_,"path",Status::REQUIRED,errs_);
|
|
toml::verify_human_size(branch_,"min-free-space",Status::OPTIONAL,errs_);
|
|
toml::verify_enum(branch_,"mode",BRANCH_MODE_ENUMS,Status::OPTIONAL,errs_);
|
|
toml::verify_boolean(branch_,"glob",Status::OPTIONAL,errs_);
|
|
}
|
|
|
|
static
|
|
void
|
|
verify_branch_array(const toml::value &doc_,
|
|
strvec &errs_)
|
|
{
|
|
if(doc_.contains("branch") == false)
|
|
return toml::required(doc_,"branch",errs_);
|
|
|
|
const auto &branches = doc_.at("branch");
|
|
if(branches.is_array() == false)
|
|
return toml::invalid_type(branches,toml::value_t::array,errs_);
|
|
|
|
for(const auto &branch : branches.as_array())
|
|
{
|
|
toml::verify_branch_table(branch,errs_);
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
verify_branches(const toml::value &doc_,
|
|
strvec &errs_)
|
|
{
|
|
if(doc_.contains("branches") == false)
|
|
return toml::required(doc_,"branches",errs_);
|
|
|
|
const auto &branches = doc_.at("branches");
|
|
if(branches.is_table() == false)
|
|
return toml::invalid_type(branches,toml::value_t::table,errs_);
|
|
|
|
toml::verify_human_size(branches,"min-free-space",Status::REQUIRED,errs_);
|
|
toml::verify_branch_array(branches,errs_);
|
|
}
|
|
|
|
void
|
|
verify(const toml::value &doc_,
|
|
strvec &errs_)
|
|
{
|
|
std::cout << doc_ << std::endl;
|
|
|
|
toml::verify_branches(doc_,errs_);
|
|
|
|
return;
|
|
|
|
toml::verify_boolean(doc_,"link-cow",Status::OPTIONAL,errs_);
|
|
toml::verify_cache_table(doc_,errs_);
|
|
toml::verify_enum(doc_,"follow-symlinks",FOLLOW_SYMLINKS_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(doc_,"inode-calc",INODE_CALC_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(doc_,"move-on-enospc",CREATE_POLICY_ENUMS,Status::OPTIONAL,errs_);
|
|
toml::verify_enum(doc_,"nfs-open-hack",NFS_OPEN_HACK_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(doc_,"statfs",STATFS_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(doc_,"statfs-ignore",STATFS_IGNORE_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_enum(doc_,"xattr",XATTR_ENUMS,Status::REQUIRED,errs_);
|
|
toml::verify_human_size(doc_,"min-free-space",Status::OPTIONAL,errs_);
|
|
toml::verify_policy_table(doc_,errs_);
|
|
toml::verify_string(doc_,"filesystem-name",Status::REQUIRED,errs_);
|
|
}
|
|
}
|