diff --git a/LICENSE b/LICENSE index b4fdad4f..14b9ebd0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ /* ISC License - Copyright (c) 2021, Antonio SJ Musumeci + Copyright (c) 2022, Antonio SJ Musumeci Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/config.toml b/config.toml new file mode 100644 index 00000000..abf9c2b6 --- /dev/null +++ b/config.toml @@ -0,0 +1,89 @@ +# test file + +[filesystem] +name = 'foo' +mountpoint = '/tmp/test' +threads = 0 + +[fuse] +posix-acl = false +async-read = true +message-size = 256 + +[func] +inode-calc = 'hybrid-hash' +symlinkify = false +symlinkify-timeout = 0 +xattr = 'passthrough' + +[func.access] +policy = 'ff' + +[func.create] +policy = 'ff' + +[func.getattr] +policy = 'ff' +follow-symlinks = 'never' + +[func.rmdir] +policy = 'all' +follow-symlinks = 'never' + +[func.link] +policy = 'all' # 'preserve-paths' | 'create-paths' | 'per-branch' +exdev = 'passthrough' + +[func.write] +policy = 'null' +move-on-enospc = 'mfs' + +[func.release] +drop-cache = true + +[func.read] +policy = 'null' + +[func.getxattr] +security-capability = false + +[func.setxattr] +security-capability = false + +[func.open] +link-cow = true +nfs-hack = 'off' + +[func.rename] +policy = '' +exdev = 'passthrough' + +[func.statfs] +policy = 'base' +ignore = 'none' + +[cache] +files = 'off' +statfs = 0 +attr-timeout = 0 +entry-timeout = 0 +negative-entry-timeout = 0 +writeback = false +symlinks = false +readdir = false + +[branches] +min-free-space = 123 + +[[branches.group]] + +[[branches.group.branch]] +active = false +path = '/tmp/mergerfs/a' +path-type = 'literal' +mode = 'RW' +if-not-mountpoint = 'fail' # 'fail' | 'deactivate' ? + +[[branches.group.branch]] +path = '/tmp/mergerfs/*' +path-type = 'glob' diff --git a/src/branch.cpp b/src/branch.cpp index a693b8df..b663ac50 100644 --- a/src/branch.cpp +++ b/src/branch.cpp @@ -95,3 +95,25 @@ Branch::ro_or_nc(void) const return ((mode == Branch::Mode::RO) || (mode == Branch::Mode::NC)); } + +Branch2::Mode +Branch2::str2mode(const std::string &str_) +{ + if(str_ == "RW") + return Branch2::Mode::RW; + if(str_ == "RO") + return Branch2::Mode::RO; + if(str_ == "NC") + return Branch2::Mode::NC; + + return Branch2::Mode::RW; +} + +Branch2::Branch2(const toml::value &toml_, + const Branch2::Mode default_mode_, + const uint64_t default_minfreespace_) +{ + mode = toml::find_or(toml_,"mode",default_mode_); + minfreespace = toml::find_or(toml_,"min-free-space",default_minfreespace_); + path = toml::find(toml_,"path"); +} diff --git a/src/branch.hpp b/src/branch.hpp index 588aa79a..a511ca78 100644 --- a/src/branch.hpp +++ b/src/branch.hpp @@ -18,6 +18,10 @@ #pragma once +#include "from_toml.hpp" + +#include "ghc/filesystem.hpp" + #include "nonstd/optional.hpp" #include "strvec.hpp" #include "tofrom_string.hpp" @@ -65,3 +69,55 @@ private: nonstd::optional _minfreespace; const uint64_t *_default_minfreespace; }; + +class Branch2 +{ +public: + enum class Mode + { + INVALID, + RO, + RW, + NC + }; + + static Mode str2mode(const std::string &); + +public: + Branch2(const toml::value &toml, + const Branch2::Mode default_mode, + const uint64_t default_minfreespace); + +public: + bool ro(void) const; + bool nc(void) const; + bool ro_or_nc(void) const; + +public: + ghc::filesystem::path path; + Mode mode; + uint64_t minfreespace; +}; + +namespace toml +{ + template<> + struct from + { + static + Branch2::Mode + from_toml(const toml::value &v_) + { + std::string str = v_.as_string(); + + if(str == "RW") + return Branch2::Mode::RW; + if(str == "RO") + return Branch2::Mode::RO; + if(str == "NC") + return Branch2::Mode::NC; + + return Branch2::Mode::RW; + } + }; +} diff --git a/src/branch_group.cpp b/src/branch_group.cpp new file mode 100644 index 00000000..3570a8b0 --- /dev/null +++ b/src/branch_group.cpp @@ -0,0 +1,94 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "branch_group.hpp" + +#include "fs_glob.hpp" + +#include +#include + + +namespace gfs = ghc::filesystem; + + +namespace l +{ + static + void + add_literal(const toml::value &branch_, + const Branch2::Mode default_mode_, + const uint64_t default_minfreespace_, + BranchGroup *branch_group_) + { + branch_group_->emplace_back(branch_,default_mode_,default_minfreespace_); + } + + static + void + add_glob(const toml::value &branch_, + const Branch2::Mode default_mode_, + const uint64_t default_minfreespace_, + BranchGroup *branch_group_) + { + std::string pattern; + std::vector paths; + + pattern = toml::find(branch_,"path"); + + fs::glob(pattern,&paths); + + for(const auto &path : paths) + { + toml::value v = branch_; + + v["path"] = path.native(); + v["path-type"] = "literal"; + + l::add_literal(v,default_mode_,default_minfreespace_,branch_group_); + } + } +} + + +BranchGroup::BranchGroup(const toml::value &toml_, + const Branch2::Mode default_mode_, + const uint64_t default_minfreespace_) +{ + Branch2::Mode default_mode; + uint64_t default_minfreespace; + + default_mode = toml::find_or(toml_,"mode",default_mode_); + default_minfreespace = toml::find_or(toml_,"min-free-space",default_minfreespace_); + + for(const auto &branch : toml_.at("branch").as_array()) + { + std::string path_type; + + if(!toml::find_or(branch,"active",true)) + continue; + + path_type = toml::find_or(branch,"path-type","literal"); + if(path_type == "literal") + l::add_literal(branch,default_mode,default_minfreespace_,this); + else if(path_type == "glob") + l::add_glob(branch,default_mode,default_minfreespace,this); + else + throw std::runtime_error("invalid path-type = " + path_type); + } +} diff --git a/src/branch_group.hpp b/src/branch_group.hpp new file mode 100644 index 00000000..8f7cd3e2 --- /dev/null +++ b/src/branch_group.hpp @@ -0,0 +1,31 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "branch.hpp" + +#include + +class BranchGroup : public std::vector +{ +public: + BranchGroup(const toml::value &, + const Branch2::Mode, + const uint64_t); +}; diff --git a/src/branches.cpp b/src/branches.cpp index 99a0eaf8..9afc64a8 100644 --- a/src/branches.cpp +++ b/src/branches.cpp @@ -29,6 +29,7 @@ #include #include +#include using std::string; using std::vector; @@ -429,3 +430,23 @@ SrcMounts::to_string(void) const return rv; } + +Branches2::Branches2(const toml::value &toml_) +{ + toml::value branches; + Branch2::Mode default_mode; + uint64_t default_minfreespace; + + default_mode = toml::find_or(toml_,"branches","mode",Branch2::Mode::RW); + default_minfreespace = toml::find_or(toml_,"branches","min-free-space",0); + + branches = toml::find(toml_,"branches"); + + for(const auto &branch_group : branches.at("group").as_array()) + { + if(!toml::find_or(branch_group,"active",true)) + continue; + + emplace_back(branch_group,default_mode,default_minfreespace); + } +} diff --git a/src/branches.hpp b/src/branches.hpp index 28731847..102c8ab4 100644 --- a/src/branches.hpp +++ b/src/branches.hpp @@ -18,7 +18,11 @@ #pragma once +#include "from_toml.hpp" + #include "branch.hpp" +#include "branch_group.hpp" + #include "nonstd/optional.hpp" #include "strvec.hpp" #include "tofrom_string.hpp" @@ -30,6 +34,12 @@ #include +class Branches2 : public std::vector +{ +public: + Branches2(const toml::value &); +}; + class Branches final : public ToFromString { public: diff --git a/src/config.cpp b/src/config.cpp index fd10f71f..34847c48 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -14,6 +14,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "fuse_access.hpp" +#include "fuse_getattr.hpp" + #include "config.hpp" #include "ef.hpp" #include "errno.hpp" @@ -23,6 +26,9 @@ #include "str.hpp" #include "to_string.hpp" #include "version.hpp" +#include "toml.hpp" +#include "toml_verify.hpp" +//#include "nonstd/span.hpp" #include #include @@ -41,7 +47,6 @@ using std::string; #define IFERT(S) if(S == s_) return true -const std::string CONTROLFILE = "/.mergerfs"; Config Config::_singleton; @@ -90,7 +95,6 @@ Config::Config() ignorepponrename(false), inodecalc("hybrid-hash"), link_cow(false), - link_exdev(LinkEXDEV::ENUM::PASSTHROUGH), log_metrics(false), mount(), moveonenospc(false), @@ -155,7 +159,6 @@ Config::Config() _map["inodecalc"] = &inodecalc; _map["kernel_cache"] = &kernel_cache; _map["link_cow"] = &link_cow; - _map["link-exdev"] = &link_exdev; _map["log.metrics"] = &log_metrics; _map["minfreespace"] = &minfreespace; _map["mount"] = &mount; @@ -345,6 +348,35 @@ Config::from_file(const std::string &filepath_, return rv; } +int +Config::from_toml(const toml::value &toml_, + ErrVec *errs_) +{ + // toml::verify_bool(toml_,"cache","readdir",true); + // toml::verify_bool(toml_,"drop-cache-on-close",true); + // toml::verify_bool(toml_,"link-cow",false); + // toml::verify_bool(toml_,"null-rw",false); + // toml::verify_bool(toml_,"posix-acl",false); + // toml::verify_bool(toml_,"security-capability",false); + // toml::verify_bool(toml_,"symlinkify",false); + // toml::verify_enum(toml_,"follow-symlinks",true,{"never","directory","regular","all"}); + // toml::verify_enum(toml_,"inode-calc",true,{"foo","bar","baz"}); + // toml::verify_enum(toml_,"link-exdev",false,{"passthrough","rel-symlinks","abs-base-symlink","abs-pool-symlink"}); + // toml::verify_enum(toml_,"move-on-enospc",true,{"mfs","lfs"}); + // toml::verify_enum(toml_,"nfs-open-hack",false,{"off","git","all"}); + // toml::verify_enum(toml_,"rename-exdev",false,{"passthrough","rel-symlink","abs-symlink"}); + // toml::verify_enum(toml_,"statfs",true,{"base","full"}); + // toml::verify_enum(toml_,"statfs-ignore",true,{"none","ro","nc"}); + // toml::verify_enum(toml_,"xattr",false,{"passthrough","noattr","nosys"}); + // toml::verify_human_size(toml_,"min-free-space",false); + // toml::verify_integer(toml_,"fuse-msg-size",false,1,256); + // toml::verify_min_integer(toml_,"symlinkify-timeout",false,0); + // toml::verify_bool(toml_,"allow-other",false); + // toml::verify_string(toml_,"filesystem-name",true); + + return 0; +} + std::ostream& operator<<(std::ostream &os_, const Config &c_) diff --git a/src/config.hpp b/src/config.hpp index fe3ce3ee..642fb686 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -21,12 +21,10 @@ #include "config_cachefiles.hpp" #include "config_follow_symlinks.hpp" #include "config_inodecalc.hpp" -#include "config_link_exdev.hpp" #include "config_log_metrics.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" @@ -39,6 +37,8 @@ #include "fuse.h" +#include "toml.hpp" + #include #include #include @@ -53,7 +53,6 @@ typedef ToFromWrapper ConfigINT; typedef ToFromWrapper ConfigSTR; typedef std::map Str2TFStrMap; -extern const std::string CONTROLFILE; class Config { @@ -121,7 +120,6 @@ public: InodeCalc inodecalc; ConfigBOOL kernel_cache; ConfigBOOL link_cow; - LinkEXDEV link_exdev; LogMetrics log_metrics; ConfigSTR mount; MoveOnENOSPC moveonenospc; @@ -131,7 +129,6 @@ public: ConfigBOOL posix_acl; ReadDir readdir; ConfigBOOL readdirplus; - RenameEXDEV rename_exdev; ConfigBOOL security_capability; SrcMounts srcmounts; StatFS statfs; @@ -161,6 +158,7 @@ public: public: int from_stream(std::istream &istrm, ErrVec *errs); int from_file(const std::string &filepath, ErrVec *errs); + int from_toml(const toml::value &value, ErrVec *errs); private: Str2TFStrMap _map; @@ -203,3 +201,5 @@ Config::Write::operator->() { return &_cfg; } + +int process_toml_config(const std::string &filepath_); diff --git a/src/config_inodecalc.cpp b/src/config_inodecalc.cpp index aa21e048..b707fcee 100644 --- a/src/config_inodecalc.cpp +++ b/src/config_inodecalc.cpp @@ -18,7 +18,7 @@ #include "config_inodecalc.hpp" #include "fs_inode.hpp" - +#include "toml.hpp" InodeCalc::InodeCalc(const std::string &s_) { @@ -36,3 +36,24 @@ InodeCalc::from_string(const std::string &s_) { return fs::inode::set_algo(s_); } + +void +InodeCalc::from_toml(const toml::value &v_) +{ + int rv; + + rv = fs::inode::set_algo(v_.as_string()); + if(rv < 0) + throw toml::type_error("must be: " + "passthrough|" + "path-hash|" + "path-hash32|" + "devino-hash|" + "devino-hash32|" + "hybrid-hash|" + "hybrid-hash32" + , + v_.location()); + + return; +} diff --git a/src/config_inodecalc.hpp b/src/config_inodecalc.hpp index 8a882593..26138b1e 100644 --- a/src/config_inodecalc.hpp +++ b/src/config_inodecalc.hpp @@ -19,13 +19,16 @@ #pragma once #include "tofrom_string.hpp" - +#include "toml.hpp" class InodeCalc : public ToFromString { public: InodeCalc(const std::string &); +public: + void from_toml(const toml::value &); + public: std::string to_string(void) const final; int from_string(const std::string &) final; diff --git a/src/config_link_exdev.cpp b/src/config_link_exdev.cpp deleted file mode 100644 index ccd34f3a..00000000 --- a/src/config_link_exdev.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - ISC License - - Copyright (c) 2021, Antonio SJ Musumeci - - 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; -} diff --git a/src/from_toml.hpp b/src/from_toml.hpp new file mode 100644 index 00000000..c78c1c9e --- /dev/null +++ b/src/from_toml.hpp @@ -0,0 +1,27 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "toml.hpp" + +class FromTOML +{ +public: + virtual void operator=(const toml::value &) = 0; +}; diff --git a/src/fs_acl.cpp b/src/fs_acl.cpp index 3737bc16..a2cf0f72 100644 --- a/src/fs_acl.cpp +++ b/src/fs_acl.cpp @@ -18,9 +18,12 @@ #include "fs_lgetxattr.hpp" #include "fs_path.hpp" +#include "ghc/filesystem.hpp" #include +namespace gfs = ghc::filesystem; + const char POSIX_ACL_DEFAULT_XATTR[] = "system.posix_acl_default"; @@ -40,5 +43,15 @@ namespace fs return (rv != -1); } + + int + dir_has_defaults(const gfs::path &fullpath_) + { + int rv; + + rv = fs::lgetxattr(fullpath_.parent_path(),POSIX_ACL_DEFAULT_XATTR,NULL,0); + + return rv; + } } } diff --git a/src/fs_acl.hpp b/src/fs_acl.hpp index 92fef84b..56247100 100644 --- a/src/fs_acl.hpp +++ b/src/fs_acl.hpp @@ -18,6 +18,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include @@ -27,5 +29,8 @@ namespace fs { bool dir_has_defaults(const std::string &fullpath); + + int + dir_has_defaults(const ghc::filesystem::path &fullpath); } } diff --git a/src/fs_attr.hpp b/src/fs_attr.hpp index 96d9944b..80d606a9 100644 --- a/src/fs_attr.hpp +++ b/src/fs_attr.hpp @@ -16,7 +16,7 @@ #pragma once -#include +#include "ghc/filesystem.hpp" namespace fs @@ -25,7 +25,7 @@ namespace fs { int copy(const int fdin, const int fdout); - int copy(const std::string &from, - const std::string &to); + int copy(const ghc::filesystem::path &from, + const ghc::filesystem::path &to); } } diff --git a/src/fs_attr_linux.icpp b/src/fs_attr_linux.icpp index abae1e5e..2558b3a5 100644 --- a/src/fs_attr_linux.icpp +++ b/src/fs_attr_linux.icpp @@ -18,14 +18,13 @@ #include "fs_close.hpp" #include "fs_open.hpp" #include "fs_ioctl.hpp" - -#include +#include "ghc/filesystem.hpp" #include #include -using std::string; +namespace gfs = ghc::filesystem; namespace fs { @@ -33,38 +32,36 @@ namespace fs { static int - get_fs_ioc_flags(const int fd, - int &flags) + get_fs_ioc_flags(const int fd_, + int *flags_) { int rv; - rv = fs::ioctl(fd,FS_IOC_GETFLAGS,(void*)&flags); - if((rv == -1) && (errno == EINVAL)) - errno = ENOTSUP; + rv = fs::ioctl(fd_,FS_IOC_GETFLAGS,(void*)flags_); + if(rv == -EINVAL) + rv = -ENOTSUP; return rv; } static int - get_fs_ioc_flags(const string &file, - int &flags) + get_fs_ioc_flags(const gfs::path &file_, + int *flags_) { int fd; int rv; const int openflags = O_RDONLY|O_NONBLOCK; - fd = fs::open(file,openflags); - if(fd == -1) - return -1; + fd = fs::open(file_,openflags); + if(fd < 0) + return fd; - rv = get_fs_ioc_flags(fd,flags); - if(rv == -1) + rv = get_fs_ioc_flags(fd,flags_); + if(rv < 0) { - int error = errno; fs::close(fd); - errno = error; - return -1; + return rv; } return fs::close(fd); @@ -72,69 +69,67 @@ namespace fs static int - set_fs_ioc_flags(const int fd, - const int flags) + set_fs_ioc_flags(const int fd_, + const int flags_) { int rv; - rv = fs::ioctl(fd,FS_IOC_SETFLAGS,(void*)&flags); - if((rv == -1) && (errno == EINVAL)) - errno = ENOTSUP; + rv = fs::ioctl(fd_,FS_IOC_SETFLAGS,(void*)&flags_); + if(rv == -EINVAL) + rv = -ENOTSUP; return rv; } static int - set_fs_ioc_flags(const string &file, - const int flags) + set_fs_ioc_flags(const gfs::path &file_, + const int flags_) { int fd; int rv; const int openflags = O_RDONLY|O_NONBLOCK; - fd = fs::open(file,openflags); - if(fd == -1) - return -1; + fd = fs::open(file_,openflags); + if(fd < 0) + return fd; - rv = set_fs_ioc_flags(fd,flags); - if(rv == -1) + rv = set_fs_ioc_flags(fd,flags_); + if(rv < 0) { - int error = errno; fs::close(fd); - errno = error; - return -1; + return rv; } return fs::close(fd); } int - copy(const int fdin, - const int fdout) + copy(const int fdin_, + const int fdout_) { int rv; int flags; - rv = get_fs_ioc_flags(fdin,flags); - if(rv == -1) - return -1; + rv = get_fs_ioc_flags(fdin_,&flags); + if(rv < 0) + return rv; - return set_fs_ioc_flags(fdout,flags); + return set_fs_ioc_flags(fdout_,flags); } int - copy(const string &from, - const string &to) + copy(const gfs::path &from_, + const gfs::path &to_) { int rv; int flags; - rv = get_fs_ioc_flags(from,flags); - if(rv == -1) - return -1; + rv = get_fs_ioc_flags(from_,&flags); + if(rv < 0) + return rv; - return set_fs_ioc_flags(to,flags); + return set_fs_ioc_flags(to_,flags); } } } diff --git a/src/fs_clonepath.cpp b/src/fs_clonepath.cpp index 4fec3ae7..47ccbc68 100644 --- a/src/fs_clonepath.cpp +++ b/src/fs_clonepath.cpp @@ -25,10 +25,13 @@ #include "fs_xattr.hpp" #include "ugid.hpp" +#include "ghc/filesystem.hpp" + #include using std::string; +namespace gfs = ghc::filesystem; namespace l { @@ -38,10 +41,10 @@ namespace l { switch(err_) { - case ENOTTY: - case ENOTSUP: + case -ENOTTY: + case -ENOTSUP: #if ENOTSUP != EOPNOTSUPP - case EOPNOTSUPP: + case -EOPNOTSUPP: #endif return true; } @@ -50,111 +53,161 @@ namespace l } } -namespace fs +/* + Attempts to clone a path. + The directories which already exist are left alone. + The new directories have metadata set to match the original if + possible. Optionally ignore errors on metadata copies. +*/ +int +fs::clonepath(const string &fromsrc_, + const string &tosrc_, + const char *relative_, + const bool return_metadata_errors_) { - /* - Attempts to clone a path. - The directories which already exist are left alone. - The new directories have metadata set to match the original if - possible. Optionally ignore errors on metadata copies. - */ - int - clonepath(const string &fromsrc_, - const string &tosrc_, - const char *relative_, - const bool return_metadata_errors_) - { - int rv; - struct stat st; - string topath; - string frompath; - string dirname; + int rv; + struct stat st; + string topath; + string frompath; + string dirname; - if((relative_ == NULL) || (relative_[0] == '\0')) - return 0; + if((relative_ == NULL) || (relative_[0] == '\0')) + return 0; - dirname = fs::path::dirname(relative_); - if(dirname != "/") - { - rv = fs::clonepath(fromsrc_,tosrc_,dirname,return_metadata_errors_); - if(rv == -1) - return -1; - } + dirname = fs::path::dirname(relative_); + if(dirname != "/") + { + rv = fs::clonepath(fromsrc_,tosrc_,dirname,return_metadata_errors_); + if(rv == -1) + return -1; + } - frompath = fs::path::make(fromsrc_,relative_); - rv = fs::lstat(frompath,&st); - if(rv == -1) - return -1; - else if(!S_ISDIR(st.st_mode)) - return (errno=ENOTDIR,-1); + frompath = fs::path::make(fromsrc_,relative_); + rv = fs::lstat(frompath,&st); + if(rv == -1) + return -1; + else if(!S_ISDIR(st.st_mode)) + return (errno=ENOTDIR,-1); - topath = fs::path::make(tosrc_,relative_); - rv = fs::mkdir(topath,st.st_mode); - if(rv == -1) - { - if(errno == EEXIST) - return 0; - else - return -1; - } + topath = fs::path::make(tosrc_,relative_); + rv = fs::mkdir(topath,st.st_mode); + if(rv == -1) + { + if(errno == EEXIST) + return 0; + else + return -1; + } - // it may not support it... it's fine... - rv = fs::attr::copy(frompath,topath); - if(return_metadata_errors_ && (rv == -1) && !l::ignorable_error(errno)) - return -1; + // it may not support it... it's fine... + rv = fs::attr::copy(frompath,topath); + if(return_metadata_errors_ && (rv == -1) && !l::ignorable_error(errno)) + return -1; - // it may not support it... it's fine... - rv = fs::xattr::copy(frompath,topath); - if(return_metadata_errors_ && (rv == -1) && !l::ignorable_error(errno)) - return -1; + // it may not support it... it's fine... + rv = fs::xattr::copy(frompath,topath); + if(return_metadata_errors_ && (rv == -1) && !l::ignorable_error(errno)) + return -1; - rv = fs::lchown_check_on_error(topath,st); - if(return_metadata_errors_ && (rv == -1)) - return -1; + rv = fs::lchown_check_on_error(topath,st); + if(return_metadata_errors_ && (rv == -1)) + return -1; - rv = fs::lutimens(topath,st); - if(return_metadata_errors_ && (rv == -1)) - return -1; + rv = fs::lutimens(topath,st); + if(return_metadata_errors_ && (rv == -1)) + return -1; - return 0; - } + return 0; +} - int - clonepath(const string &from_, - const string &to_, - const string &relative_, - const bool return_metadata_errors_) - { - return fs::clonepath(from_, - to_, - relative_.c_str(), - return_metadata_errors_); - } +int +fs::clonepath(const gfs::path &fromsrc_, + const gfs::path &tosrc_, + const gfs::path &relative_) +{ + int rv; + struct stat st; + gfs::path topath; + gfs::path frompath; - int - clonepath_as_root(const string &from_, - const string &to_, - const char *relative_, - const bool return_metadata_errors_) - { - if((relative_ == NULL) || (relative_[0] == '\0')) - return 0; - if(from_ == to_) - return 0; + if(relative_.empty()) + return 0; + if(!relative_.parent_path().empty()) { - const ugid::SetRootGuard ugidGuard; - - return fs::clonepath(from_,to_,relative_,return_metadata_errors_); + rv = fs::clonepath(fromsrc_,tosrc_,relative_.parent_path()); + if(rv < 0) + return rv; } - } - int - clonepath_as_root(const string &from_, - const string &to_, - const string &relative_, - const bool return_metadata_errors_) + frompath = fromsrc_ / relative_; + rv = fs::lstat(frompath,&st); + if(rv < 0) + return rv; + else if(!S_ISDIR(st.st_mode)) + return -ENOTDIR; + + topath = tosrc_ / relative_; + rv = fs::mkdir(topath,st.st_mode); + if(rv == -EEXIST) + return 0; + else if(rv < 0) + return rv; + + // it may not support it... it's fine... + fs::attr::copy(frompath,topath); + fs::xattr::copy(frompath,topath); + fs::lchown_check_on_error(topath,st); + fs::lutimens(topath,st); + + return 0; +} + +int +fs::clonepath(const string &from_, + const string &to_, + const string &relative_, + const bool return_metadata_errors_) +{ + return fs::clonepath(from_, + to_, + relative_.c_str(), + return_metadata_errors_); +} + +int +fs::clonepath_as_root(const string &from_, + const string &to_, + const char *relative_, + const bool return_metadata_errors_) +{ + if((relative_ == NULL) || (relative_[0] == '\0')) + return 0; + if(from_ == to_) + return 0; + { - return fs::clonepath_as_root(from_,to_,relative_.c_str(),return_metadata_errors_); + const ugid::SetRootGuard ugidGuard; + + return fs::clonepath(from_,to_,relative_,return_metadata_errors_); } } + +int +fs::clonepath_as_root(const string &from_, + const string &to_, + const string &relative_, + const bool return_metadata_errors_) +{ + return fs::clonepath_as_root(from_,to_,relative_.c_str(),return_metadata_errors_); +} + +int +fs::clonepath_as_root(const gfs::path &from_, + const gfs::path &to_, + const gfs::path &relative_) +{ + const ugid::SetRootGuard ugidGuard; + + return fs::clonepath(from_,to_,relative_); +} diff --git a/src/fs_clonepath.hpp b/src/fs_clonepath.hpp index 8ad68c97..b890165d 100644 --- a/src/fs_clonepath.hpp +++ b/src/fs_clonepath.hpp @@ -16,6 +16,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include @@ -30,6 +32,10 @@ namespace fs const std::string &relative, const bool return_metadata_errors = false); + int clonepath(const ghc::filesystem::path &fromsrc, + const ghc::filesystem::path &tosrc, + const ghc::filesystem::path &relative); + int clonepath_as_root(const std::string &from, const std::string &to, const char *relative, @@ -38,4 +44,7 @@ namespace fs const std::string &to, const std::string &relative, const bool return_metadata_errors = false); + int clonepath_as_root(const ghc::filesystem::path &from, + const ghc::filesystem::path &to, + const ghc::filesystem::path &relative); } diff --git a/src/fs_clonepath_branches.cpp b/src/fs_clonepath_branches.cpp new file mode 100644 index 00000000..f7948bc4 --- /dev/null +++ b/src/fs_clonepath_branches.cpp @@ -0,0 +1,49 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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_clonepath_branches.hpp" + +#include "ugid.hpp" +#include "fs_clonepath.hpp" + + +namespace gfs = ghc::filesystem; + + +int +fs::clonepath_as_root(const Branches2 &branches_, + const gfs::path &to_, + const gfs::path &relative_) +{ + int rv; + const ugid::SetRootGuard ugidGuard; + + for(const auto &branch_group : branches_) + { + for(const auto &branch : branch_group) + { + rv = fs::clonepath(branch.path,to_,relative_); + if(rv == -ENOENT) + continue; + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fs_clonepath_branches.hpp b/src/fs_clonepath_branches.hpp new file mode 100644 index 00000000..d3f75fc1 --- /dev/null +++ b/src/fs_clonepath_branches.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "branches.hpp" +#include "ghc/filesystem.hpp" + +namespace fs +{ + int + clonepath_as_root(const Branches2 &branches_, + const ghc::filesystem::path &to_, + const ghc::filesystem::path &relative_); +} diff --git a/src/fs_close.hpp b/src/fs_close.hpp index 646482fa..233cfa3b 100644 --- a/src/fs_close.hpp +++ b/src/fs_close.hpp @@ -28,6 +28,10 @@ namespace fs int close(const int fd_) { - return ::close(fd_); + int rv; + + rv = ::close(fd_); + + return ((rv == -1) ? -errno : 0); } } diff --git a/src/fs_exists.hpp b/src/fs_exists.hpp index 2abdd646..0096a428 100644 --- a/src/fs_exists.hpp +++ b/src/fs_exists.hpp @@ -21,6 +21,8 @@ #include "fs_lstat.hpp" #include "fs_path.hpp" +#include "ghc/filesystem.hpp" + #include @@ -49,6 +51,16 @@ namespace fs return fs::exists(path_,&st); } + static + inline + bool + exists(const ghc::filesystem::path &path_) + { + struct stat st; + + return fs::exists(path_.c_str(),&st); + } + static inline bool diff --git a/src/fs_flistxattr.hpp b/src/fs_flistxattr.hpp index 1a9f6ae8..e788cd82 100644 --- a/src/fs_flistxattr.hpp +++ b/src/fs_flistxattr.hpp @@ -34,9 +34,13 @@ namespace fs const size_t size_) { #ifdef USE_XATTR - return ::flistxattr(fd_,list_,size_); + int rv; + + rv = ::flistxattr(fd_,list_,size_); + + return ((rv == -1) ? -errno : rv); #else - return (errno=ENOTSUP,-1); + return -ENOTSUP; #endif } } diff --git a/src/fs_glob.cpp b/src/fs_glob.cpp index fb7177dd..8354d9eb 100644 --- a/src/fs_glob.cpp +++ b/src/fs_glob.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "fs_glob.hpp" + #include #include @@ -23,22 +25,37 @@ using std::string; using std::vector; +namespace gfs = ghc::filesystem; + + +void +fs::glob(const string &pattern_, + vector *strs_) +{ + int flags; + glob_t gbuf = {0}; + + flags = GLOB_NOCHECK; + ::glob(pattern_.c_str(),flags,NULL,&gbuf); + + for(size_t i = 0; i < gbuf.gl_pathc; i++) + strs_->push_back(gbuf.gl_pathv[i]); + + ::globfree(&gbuf); +} -namespace fs +void +fs::glob(const string &pattern_, + vector *paths_) { - void - glob(const string &pattern_, - vector *strs_) - { - int flags; - glob_t gbuf = {0}; + int flags; + glob_t gbuf = {0}; - flags = GLOB_NOCHECK; - ::glob(pattern_.c_str(),flags,NULL,&gbuf); + flags = GLOB_NOCHECK; + ::glob(pattern_.c_str(),flags,NULL,&gbuf); - for(size_t i = 0; i < gbuf.gl_pathc; i++) - strs_->push_back(gbuf.gl_pathv[i]); + for(size_t i = 0; i < gbuf.gl_pathc; i++) + paths_->push_back(gbuf.gl_pathv[i]); - ::globfree(&gbuf); - } + ::globfree(&gbuf); } diff --git a/src/fs_glob.hpp b/src/fs_glob.hpp index 9d2b3b06..07df5e62 100644 --- a/src/fs_glob.hpp +++ b/src/fs_glob.hpp @@ -16,6 +16,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include #include @@ -25,4 +27,8 @@ namespace fs void glob(const std::string &pattern, std::vector *strs); + + void + glob(const std::string &pattern, + std::vector *paths); } diff --git a/src/fs_ioctl.hpp b/src/fs_ioctl.hpp index 86ee9bdf..b1de998c 100644 --- a/src/fs_ioctl.hpp +++ b/src/fs_ioctl.hpp @@ -29,7 +29,11 @@ namespace fs ioctl(const int fd_, const unsigned long request_) { - return ::ioctl(fd_,request_); + int rv; + + rv = ::ioctl(fd_,request_); + + return ((rv == -1) ? -errno : rv); } static @@ -39,7 +43,11 @@ namespace fs const unsigned long request_, void *data_) { - return ::ioctl(fd_,request_,data_); + int rv; + + rv = ::ioctl(fd_,request_,data_); + + return ((rv == -1) ? -errno : rv); } static @@ -49,6 +57,10 @@ namespace fs const unsigned long request_, const int int_) { - return ::ioctl(fd_,request_,int_); + int rv; + + rv = ::ioctl(fd_,request_,int_); + + return ((rv == -1) ? -errno : rv); } } diff --git a/src/fs_lchmod.hpp b/src/fs_lchmod.hpp index 95012ab0..706ac6b6 100644 --- a/src/fs_lchmod.hpp +++ b/src/fs_lchmod.hpp @@ -20,10 +20,14 @@ #include "fs_lstat.hpp" +#include "ghc/filesystem.hpp" + #include +#include #include + #define MODE_BITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) @@ -35,11 +39,15 @@ namespace fs lchmod(const char *pathname_, const mode_t mode_) { + int rv; + #if defined __linux__ - return ::chmod(pathname_,mode_); + rv = ::chmod(pathname_,mode_); #else - return ::lchmod(pathname_,mode_); + rv = ::lchmod(pathname_,mode_); #endif + + return ((rv == -1) ? -errno : 0); } static @@ -51,6 +59,15 @@ namespace fs return fs::lchmod(pathname_.c_str(),mode_); } + static + inline + int + lchmod(const ghc::filesystem::path &pathname_, + const mode_t mode_) + { + return fs::lchmod(pathname_.native(),mode_); + } + static inline int diff --git a/src/fs_lgetxattr.hpp b/src/fs_lgetxattr.hpp index 7d4fe8e1..3d558937 100644 --- a/src/fs_lgetxattr.hpp +++ b/src/fs_lgetxattr.hpp @@ -19,6 +19,7 @@ #pragma once #include "errno.hpp" +#include "ghc/filesystem.hpp" #include "xattr.hpp" #include @@ -37,12 +38,16 @@ namespace fs const size_t size_) { #ifdef USE_XATTR - return ::lgetxattr(path_, - attrname_, - value_, - size_); + int rv; + + rv = ::lgetxattr(path_, + attrname_, + value_, + size_); + + return ((rv == -1) ? -errno : rv); #else - return (errno=ENOTSUP,-1); + return -ENOTSUP; #endif } @@ -60,6 +65,34 @@ namespace fs size_); } + static + inline + int + lgetxattr(const ghc::filesystem::path &path_, + const char *attrname_, + void *value_, + const size_t size_) + { + return fs::lgetxattr(path_.c_str(), + attrname_, + value_, + size_); + } + + static + inline + int + lgetxattr(const ghc::filesystem::path &path_, + const std::string &attrname_, + void *value_, + const size_t size_) + { + return fs::lgetxattr(path_.c_str(), + attrname_.c_str(), + value_, + size_); + } + static inline int diff --git a/src/fs_link.hpp b/src/fs_link.hpp index 8b41d4a2..3050afa0 100644 --- a/src/fs_link.hpp +++ b/src/fs_link.hpp @@ -18,20 +18,46 @@ #pragma once +#include "ghc/filesystem.hpp" + #include +#include #include namespace fs { + static + inline + int + link(const char *oldpath_, + const char *newpath_) + { + int rv; + + rv = ::link(oldpath_,newpath_); + + return ((rv == -1) ? -errno : rv); + } + static inline int link(const std::string &oldpath_, const std::string &newpath_) { - return ::link(oldpath_.c_str(), - newpath_.c_str()); + return fs::link(oldpath_.c_str(), + newpath_.c_str()); + } + + static + inline + int + link(const ghc::filesystem::path &oldpath_, + const ghc::filesystem::path &newpath_) + { + return fs::link(oldpath_.c_str(), + newpath_.c_str()); } } diff --git a/src/fs_llistxattr.hpp b/src/fs_llistxattr.hpp index 9818a665..9a99f248 100644 --- a/src/fs_llistxattr.hpp +++ b/src/fs_llistxattr.hpp @@ -19,6 +19,7 @@ #pragma once #include "errno.hpp" +#include "ghc/filesystem.hpp" #include "xattr.hpp" #include @@ -36,19 +37,27 @@ namespace fs const size_t size_) { #ifdef USE_XATTR - return ::llistxattr(path_,list_,size_); + int rv; + + rv = ::llistxattr(path_,list_,size_); + + return ((rv == -1) ? -errno : rv); #else - return (errno=ENOTSUP,-1); + return -ENOTSUP; #endif } static inline int - llistxattr(const std::string &path_, - char *list_, - const size_t size_) + llistxattr(const ghc::filesystem::path &path_, + char *list_, + const size_t size_) { - return fs::llistxattr(path_.c_str(),list_,size_); + return fs::llistxattr(path_.c_str(), + list_, + size_); } + + } diff --git a/src/fs_lsetxattr.hpp b/src/fs_lsetxattr.hpp index ba3a7325..a3cc6fea 100644 --- a/src/fs_lsetxattr.hpp +++ b/src/fs_lsetxattr.hpp @@ -21,6 +21,8 @@ #include "errno.hpp" #include "xattr.hpp" +#include "ghc/filesystem.hpp" + #include #include @@ -38,27 +40,31 @@ namespace fs const int flags_) { #ifdef USE_XATTR - return ::lsetxattr(path_, - name_, - value_, - size_, - flags_); + int rv; + + rv = ::lsetxattr(path_, + name_, + value_, + size_, + flags_); + + return ((rv == -1) ? -errno : rv); #else - return (errno=ENOTSUP,-1); + return -ENOTSUP; #endif } static inline int - lsetxattr(const std::string &path_, - const std::string &name_, - const void *value_, - const size_t size_, - const int flags_) + lsetxattr(const ghc::filesystem::path &path_, + const char *name_, + const void *value_, + const size_t size_, + const int flags_) { return fs::lsetxattr(path_.c_str(), - name_.c_str(), + name_, value_, size_, flags_); diff --git a/src/fs_lstat.hpp b/src/fs_lstat.hpp index dd8e8384..29eb95eb 100644 --- a/src/fs_lstat.hpp +++ b/src/fs_lstat.hpp @@ -18,6 +18,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include #include @@ -33,7 +35,11 @@ namespace fs lstat(const char *path_, struct stat *st_) { - return ::lstat(path_,st_); + int rv; + + rv = ::lstat(path_,st_); + + return ((rv == -1) ? -errno : 0); } static @@ -44,4 +50,13 @@ namespace fs { return fs::lstat(path_.c_str(),st_); } + + static + inline + int + lstat(const ghc::filesystem::path &path_, + struct stat *st_) + { + return fs::lstat(path_.c_str(),st_); + } } diff --git a/src/fs_lutimens.hpp b/src/fs_lutimens.hpp index e2937a6d..a293f5c6 100644 --- a/src/fs_lutimens.hpp +++ b/src/fs_lutimens.hpp @@ -21,14 +21,16 @@ #include "fs_utimensat.hpp" #include "fs_stat_utils.hpp" +#include "ghc/filesystem.hpp" + namespace fs { static inline int - lutimens(const std::string &path_, - const struct timespec ts_[2]) + lutimens(const ghc::filesystem::path &path_, + const struct timespec ts_[2]) { return fs::utimensat(AT_FDCWD,path_,ts_,AT_SYMLINK_NOFOLLOW); } @@ -36,8 +38,8 @@ namespace fs static inline int - lutimens(const std::string &path_, - const struct stat &st_) + lutimens(const ghc::filesystem::path &path_, + const struct stat &st_) { struct timespec ts[2]; diff --git a/src/fs_mkdir.hpp b/src/fs_mkdir.hpp index d1fff438..19bb0e58 100644 --- a/src/fs_mkdir.hpp +++ b/src/fs_mkdir.hpp @@ -34,7 +34,11 @@ namespace fs mkdir(const char *path_, const mode_t mode_) { - return ::mkdir(path_,mode_); + int rv; + + rv = ::mkdir(path_,mode_); + + return ((rv == -1) ? -errno : 0); } static diff --git a/src/fs_open.hpp b/src/fs_open.hpp index 02364361..42b36347 100644 --- a/src/fs_open.hpp +++ b/src/fs_open.hpp @@ -18,8 +18,11 @@ #pragma once +#include "ghc/filesystem.hpp" + #include +#include #include #include #include @@ -33,7 +36,13 @@ namespace fs open(const char *path_, const int flags_) { - return ::open(path_,flags_); + int rv; + + rv = ::open(path_,flags_); + if(rv == -1) + return -errno; + + return rv; } static @@ -43,7 +52,23 @@ namespace fs const int flags_, const mode_t mode_) { - return ::open(path_,flags_,mode_); + int rv; + + rv = ::open(path_,flags_,mode_); + if(rv == -1) + return -errno; + + return rv; + } + + static + inline + int + open(const ghc::filesystem::path &path_, + const int flags_, + const mode_t mode_) + { + return fs::open(path_.c_str(),flags_,mode_); } static diff --git a/src/fs_path.hpp b/src/fs_path.hpp index d36b8fd6..0b99a36c 100644 --- a/src/fs_path.hpp +++ b/src/fs_path.hpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Antonio SJ Musumeci + Copyright (c) 2022, Antonio SJ Musumeci Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -19,6 +19,9 @@ #include #include +#include "ghc/filesystem.hpp" + +namespace gfs = ghc::filesystem; namespace fs { diff --git a/src/fs_readlink.hpp b/src/fs_readlink.hpp index 0cd2df0f..16ed9c09 100644 --- a/src/fs_readlink.hpp +++ b/src/fs_readlink.hpp @@ -18,6 +18,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include #include @@ -28,10 +30,24 @@ namespace fs static inline int - readlink(const std::string &path_, - char *buf_, - const size_t bufsiz_) + readlink(const char *pathname_, + char *buf_, + const size_t bufsiz_) + { + int rv; + + rv = ::readlink(pathname_,buf_,bufsiz_); + + return ((rv == -1) ? -errno : rv); + } + + static + inline + int + readlink(const ghc::filesystem::path &pathname_, + char *buf_, + const size_t bufsiz_) { - return ::readlink(path_.c_str(),buf_,bufsiz_); + return fs::readlink(pathname_.c_str(),buf_,bufsiz_); } } diff --git a/src/fs_symlink.hpp b/src/fs_symlink.hpp index b86e77b2..9cc2ed54 100644 --- a/src/fs_symlink.hpp +++ b/src/fs_symlink.hpp @@ -18,6 +18,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include #include @@ -31,7 +33,11 @@ namespace fs symlink(const char *target_, const char *linkpath_) { - return ::symlink(target_,linkpath_); + int rv; + + rv = ::symlink(target_,linkpath_); + + return ((rv == -1) ? -errno : rv); } static @@ -40,7 +46,7 @@ namespace fs symlink(const std::string &target_, const std::string &linkpath_) { - return ::symlink(target_.c_str(),linkpath_.c_str()); + return fs::symlink(target_.c_str(),linkpath_.c_str()); } static @@ -49,6 +55,15 @@ namespace fs symlink(const char *target_, const std::string &linkpath_) { - return ::symlink(target_,linkpath_.c_str()); + return fs::symlink(target_,linkpath_.c_str()); + } + + static + inline + int + symlink(const char *target_, + const ghc::filesystem::path &linkpath_) + { + return fs::symlink(target_,linkpath_.c_str()); } } diff --git a/src/fs_truncate.hpp b/src/fs_truncate.hpp index 9a88ba60..7e83cde6 100644 --- a/src/fs_truncate.hpp +++ b/src/fs_truncate.hpp @@ -18,6 +18,8 @@ #pragma once +#include "errno.hpp" + #include #include @@ -32,7 +34,11 @@ namespace fs truncate(const char *path_, const off_t length_) { - return ::truncate(path_,length_); + int rv; + + rv = ::truncate(path_,length_); + + return ((rv == -1) ? -errno : rv); } static @@ -43,4 +49,13 @@ namespace fs { return fs::truncate(path_.c_str(),length_); } + + static + inline + int + truncate(const ghc::filesystem::path &path_, + const off_t length_) + { + return fs::truncate(path_.c_str(),length_); + } } diff --git a/src/fs_unlink.hpp b/src/fs_unlink.hpp index 5fad40d2..4b537754 100644 --- a/src/fs_unlink.hpp +++ b/src/fs_unlink.hpp @@ -18,6 +18,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include #include @@ -30,7 +32,11 @@ namespace fs int unlink(const char *path_) { - return ::unlink(path_); + int rv; + + rv = ::unlink(path_); + + return ((rv == -1) ? -errno : rv); } static @@ -40,4 +46,12 @@ namespace fs { return fs::unlink(path_.c_str()); } + + static + inline + int + unlink(const ghc::filesystem::path &path_) + { + return fs::unlink(path_.c_str()); + } } diff --git a/src/fs_utimensat_freebsd.hpp b/src/fs_utimensat_freebsd.hpp index 251c3f56..7f389d05 100644 --- a/src/fs_utimensat_freebsd.hpp +++ b/src/fs_utimensat_freebsd.hpp @@ -18,6 +18,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include #include @@ -34,7 +36,11 @@ namespace fs const struct timespec times_[2], const int flags_) { - return ::utimensat(dirfd_,pathname_,times_,flags_); + int rv; + + rv = ::utimensat(dirfd_,pathname_,times_,flags_); + + return ((rv == -1) ? -errno : rv); } static @@ -47,4 +53,15 @@ namespace fs { return fs::utimensat(dirfd_,pathname_.c_str(),times_,flags_); } + + static + inline + int + utimensat(const int dirfd_, + const ghc::filesystem::path &pathname_, + const struct timespec times_[2], + const int flags_) + { + return fs::utimensat(dirfd_,pathname_.c_str(),times_,flags_); + } } diff --git a/src/fs_utimensat_generic.hpp b/src/fs_utimensat_generic.hpp index 2bd5bf26..62b9e30b 100644 --- a/src/fs_utimensat_generic.hpp +++ b/src/fs_utimensat_generic.hpp @@ -276,21 +276,21 @@ namespace fs struct timeval *tvp; if(l::flags_invalid(flags)) - return (errno=EINVAL,-1); + return -EINVAL; if(l::timespec_invalid(ts_)) - return (errno=EINVAL,-1); + return -EINVAL; if(l::should_ignore(ts_)) return 0; rv = l::convert_timespec_to_timeval(dirfd_,path_,ts_,tv,&tvp,flags_); if(rv == -1) - return -1; + return -errno; if((flags_ & AT_SYMLINK_NOFOLLOW) == 0) return fs::futimesat(dirfd_,path_,tvp); if(l::can_call_lutimes(dirfd_,path_,flags)) return fs::lutimes(path_,tvp); - return (errno=ENOTSUP,-1); + return -ENOTSUP; } } diff --git a/src/fs_utimensat_linux.hpp b/src/fs_utimensat_linux.hpp index 251c3f56..7f389d05 100644 --- a/src/fs_utimensat_linux.hpp +++ b/src/fs_utimensat_linux.hpp @@ -18,6 +18,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include #include @@ -34,7 +36,11 @@ namespace fs const struct timespec times_[2], const int flags_) { - return ::utimensat(dirfd_,pathname_,times_,flags_); + int rv; + + rv = ::utimensat(dirfd_,pathname_,times_,flags_); + + return ((rv == -1) ? -errno : rv); } static @@ -47,4 +53,15 @@ namespace fs { return fs::utimensat(dirfd_,pathname_.c_str(),times_,flags_); } + + static + inline + int + utimensat(const int dirfd_, + const ghc::filesystem::path &pathname_, + const struct timespec times_[2], + const int flags_) + { + return fs::utimensat(dirfd_,pathname_.c_str(),times_,flags_); + } } diff --git a/src/fs_xattr.cpp b/src/fs_xattr.cpp index e7ac84a3..3a4c32b1 100644 --- a/src/fs_xattr.cpp +++ b/src/fs_xattr.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "fs_xattr.hpp" + #include "errno.hpp" #include "fs_close.hpp" #include "fs_fgetxattr.hpp" @@ -24,6 +26,7 @@ #include "fs_lremovexattr.hpp" #include "fs_lsetxattr.hpp" #include "fs_open.hpp" +#include "ghc/filesystem.hpp" #include "str.hpp" #include @@ -37,6 +40,8 @@ using std::map; using std::istringstream; +namespace gfs = ghc::filesystem; + namespace fs { namespace xattr @@ -47,9 +52,8 @@ namespace fs { ssize_t rv; - rv = -1; - errno = ERANGE; - while((rv == -1) && (errno == ERANGE)) + rv = -ERANGE; + while(rv == -ERANGE) { rv = fs::flistxattr(fd_,NULL,0); if(rv <= 0) @@ -64,14 +68,13 @@ namespace fs } int - list(const string &path_, - vector *attrs_) + list(const gfs::path &path_, + vector *attrs_) { ssize_t rv; - rv = -1; - errno = ERANGE; - while((rv == -1) && (errno == ERANGE)) + rv = -ERANGE; + while(rv == -ERANGE) { rv = fs::llistxattr(path_,NULL,0); if(rv <= 0) @@ -93,7 +96,7 @@ namespace fs vector attrs; rv = fs::xattr::list(fd_,&attrs); - if(rv != -1) + if(rv > 0) { string tmp(attrs.begin(),attrs.end()); str::split(tmp,'\0',attrvector_); @@ -103,14 +106,14 @@ namespace fs } int - list(const string &path_, - vector *attrvector_) + list(const gfs::path &path_, + vector *attrvector_) { int rv; vector attrs; rv = fs::xattr::list(path_,&attrs); - if(rv != -1) + if(rv > 0) { string tmp(attrs.begin(),attrs.end()); str::split(tmp,'\0',attrvector_); @@ -127,21 +130,21 @@ namespace fs vector attrs; rv = fs::xattr::list(fd_,&attrs); - if(rv != -1) + if(rv > 0) *attrstr_ = string(attrs.begin(),attrs.end()); return rv; } int - list(const string &path_, - string *attrstr_) + list(const gfs::path &path_, + string *attrstr_) { int rv; vector attrs; rv = fs::xattr::list(path_,&attrs); - if(rv != -1) + if(rv > 0) *attrstr_ = string(attrs.begin(),attrs.end()); return rv; @@ -154,9 +157,8 @@ namespace fs { ssize_t rv; - rv = -1; - errno = ERANGE; - while((rv == -1) && (errno == ERANGE)) + rv = -ERANGE; + while(rv == -ERANGE) { rv = fs::fgetxattr(fd_,attr_,NULL,0); if(rv <= 0) @@ -171,15 +173,14 @@ namespace fs } int - get(const string &path_, - const string &attr_, - vector *value_) + get(const gfs::path &path_, + const string &attr_, + vector *value_) { ssize_t rv; - rv = -1; - errno = ERANGE; - while((rv == -1) && (errno == ERANGE)) + rv = -ERANGE; + while(rv == -ERANGE) { rv = fs::lgetxattr(path_,attr_,NULL,0); if(rv <= 0) @@ -201,23 +202,23 @@ namespace fs int rv; vector tmpvalue; - rv = get(fd_,attr_,&tmpvalue); - if(rv != -1) + rv = fs::xattr::get(fd_,attr_,&tmpvalue); + if(rv > 0) *value_ = string(tmpvalue.begin(),tmpvalue.end()); return rv; } int - get(const string &path_, - const string &attr_, - string *value_) + get(const gfs::path &path_, + const string &attr_, + string *value_) { int rv; vector tmpvalue; rv = fs::xattr::get(path_,attr_,&tmpvalue); - if(rv != -1) + if(rv > 0) *value_ = string(tmpvalue.begin(),tmpvalue.end()); return rv; @@ -231,8 +232,8 @@ namespace fs string attrstr; rv = fs::xattr::list(fd_,&attrstr); - if(rv == -1) - return -1; + if(rv <= 0) + return rv; { string key; @@ -243,7 +244,7 @@ namespace fs string value; rv = fs::xattr::get(fd_,key,&value); - if(rv != -1) + if(rv > 0) (*attrs_)[key] = value; } } @@ -252,15 +253,15 @@ namespace fs } int - get(const string &path_, + get(const gfs::path &path_, map *attrs_) { int rv; string attrstr; rv = fs::xattr::list(path_,&attrstr); - if(rv == -1) - return -1; + if(rv <= 0) + return rv; { string key; @@ -271,7 +272,7 @@ namespace fs string value; rv = fs::xattr::get(path_,key,&value); - if(rv != -1) + if(rv > 0) (*attrs_)[key] = value; } } @@ -293,13 +294,13 @@ namespace fs } int - set(const string &path_, - const string &key_, - const string &value_, - const int flags_) + set(const gfs::path &path_, + const string &key_, + const string &value_, + const int flags_) { return fs::lsetxattr(path_, - key_, + key_.c_str(), value_.data(), value_.size(), flags_); @@ -309,28 +310,23 @@ namespace fs set(const int fd_, const map &attrs_) { - int rv; - - for(map::const_iterator - i = attrs_.begin(), ei = attrs_.end(); i != ei; ++i) + for(const auto &kv : attrs_) { - rv = fs::xattr::set(fd_,i->first,i->second,0); - if(rv == -1) - return -1; + fs::xattr::set(fd_,kv.first,kv.second,0); } return 0; } int - set(const string &path_, + set(const gfs::path &path_, const map &attrs_) { int fd; fd = fs::open(path_,O_RDONLY|O_NONBLOCK); - if(fd == -1) - return -1; + if(fd < 0) + return fd; fs::xattr::set(fd,attrs_); @@ -345,22 +341,22 @@ namespace fs map attrs; rv = fs::xattr::get(fdin_,&attrs); - if(rv == -1) - return -1; + if(rv < 0) + return rv; return fs::xattr::set(fdout_,attrs); } int - copy(const string &from_, - const string &to_) + copy(const gfs::path &from_, + const gfs::path &to_) { int rv; map attrs; rv = fs::xattr::get(from_,&attrs); - if(rv == -1) - return -1; + if(rv < 0) + return rv; return fs::xattr::set(to_,attrs); } diff --git a/src/fs_xattr.hpp b/src/fs_xattr.hpp index da7d2ba3..1b403d64 100644 --- a/src/fs_xattr.hpp +++ b/src/fs_xattr.hpp @@ -16,6 +16,8 @@ #pragma once +#include "ghc/filesystem.hpp" + #include #include #include @@ -25,43 +27,38 @@ namespace fs { namespace xattr { - using std::string; - using std::vector; - using std::map; - - - int list(const string &path, - vector *attrs); - int list(const string &path, - string *attrs); - int list(const string &path, - vector *attrs); + int list(const ghc::filesystem::path &path, + std::vector *attrs); + int list(const ghc::filesystem::path &path, + std::string *attrs); + int list(const ghc::filesystem::path &path, + std::vector *attrs); - int get(const string &path, - const string &attr, - vector *value); - int get(const string &path, - const string &attr, - string *value); + int get(const ghc::filesystem::path &path, + const std::string &attr, + std::vector *value); + int get(const ghc::filesystem::path &path, + const std::string &attr, + std::string *value); - int get(const string &path, - map *attrs); + int get(const ghc::filesystem::path &path, + std::map *attrs); - int set(const string &path, - const string &key, - const string &value, - const int flags); - int set(const int fd, - const string &key, - const string &value, - const int flags); + int set(const ghc::filesystem::path &path, + const std::string &key, + const std::string &value, + const int flags); + int set(const int fd, + const std::string &key, + const std::string &value, + const int flags); - int set(const string &path, - const map &attrs); + int set(const ghc::filesystem::path &path, + const std::map &attrs); int copy(const int fdin, const int fdout); - int copy(const string &from, - const string &to); + int copy(const ghc::filesystem::path &from, + const ghc::filesystem::path &to); } } diff --git a/src/func.hpp b/src/func.hpp index 97ef6f64..65ba694a 100644 --- a/src/func.hpp +++ b/src/func.hpp @@ -18,8 +18,9 @@ #pragma once -#include "policy.hpp" +#include "from_toml.hpp" #include "policies.hpp" +#include "policy.hpp" #include "tofrom_string.hpp" #include diff --git a/src/fuse_access.cpp b/src/fuse_access.cpp index 6e196295..1c0d34e4 100644 --- a/src/fuse_access.cpp +++ b/src/fuse_access.cpp @@ -14,57 +14,24 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "config.hpp" -#include "errno.hpp" -#include "fs_eaccess.hpp" #include "fs_path.hpp" +#include "state.hpp" #include "ugid.hpp" -#include -#include +#include "fuse.h" -using std::string; -using std::vector; - -namespace l -{ - static - int - access(const Policy::Search &searchFunc_, - const Branches &branches_, - const char *fusepath_, - const int mask_) - { - int rv; - string fullpath; - StrVec basepaths; - - rv = searchFunc_(branches_,fusepath_,&basepaths); - if(rv == -1) - return -errno; - - fullpath = fs::path::make(basepaths[0],fusepath_); - - rv = fs::eaccess(fullpath,mask_); - - return ((rv == -1) ? -errno : 0); - } -} - -namespace FUSE +namespace FUSE::ACCESS { int access(const char *fusepath_, - int mask_) + int mode_) { - Config::Read cfg; + State s; + gfs::path fusepath(&fusepath_[1]); const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - return l::access(cfg->func.access.policy, - cfg->branches, - fusepath_, - mask_); + return s->access(fusepath,mode_); } } diff --git a/src/fuse_access.hpp b/src/fuse_access.hpp index 72386aae..bf1a7e36 100644 --- a/src/fuse_access.hpp +++ b/src/fuse_access.hpp @@ -16,8 +16,7 @@ #pragma once - -namespace FUSE +namespace FUSE::ACCESS { int access(const char *fusepath, diff --git a/src/fuse_access_func.cpp b/src/fuse_access_func.cpp new file mode 100644 index 00000000..04ce271e --- /dev/null +++ b/src/fuse_access_func.cpp @@ -0,0 +1,35 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_access_func.hpp" +#include "fuse_access_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::ACCESS::Func::Func(const toml::value &toml_) +{ + _access = FuncFactory(toml_); +} + +int +FUSE::ACCESS::Func::operator()(const gfs::path &fusepath_, + const int mask_) +{ + return (*_access)(fusepath_,mask_); +} diff --git a/src/fuse_access_func.hpp b/src/fuse_access_func.hpp new file mode 100644 index 00000000..66403dd6 --- /dev/null +++ b/src/fuse_access_func.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_access_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::ACCESS +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const gfs::path &fusepath, + const int mask); + + private: + FuncBase::Ptr _access; + }; +} diff --git a/src/fuse_access_func_base.hpp b/src/fuse_access_func_base.hpp new file mode 100644 index 00000000..096aed08 --- /dev/null +++ b/src/fuse_access_func_base.hpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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_path.hpp" + +#include + + +namespace FUSE::ACCESS +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const gfs::path &fusepath, + const int mask) = 0; + }; +} diff --git a/src/fuse_access_func_factory.cpp b/src/fuse_access_func_factory.cpp new file mode 100644 index 00000000..42ed2f69 --- /dev/null +++ b/src/fuse_access_func_factory.cpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_access_func_factory.hpp" +#include "fuse_access_func_ff.hpp" + +#include + +namespace FUSE::ACCESS +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","access","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_access_func_factory.hpp b/src/fuse_access_func_factory.hpp new file mode 100644 index 00000000..723576cb --- /dev/null +++ b/src/fuse_access_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_access_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::ACCESS +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_access_func_ff.cpp b/src/fuse_access_func_ff.cpp new file mode 100644 index 00000000..ff85a5e9 --- /dev/null +++ b/src/fuse_access_func_ff.cpp @@ -0,0 +1,52 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_access_func_ff.hpp" + +#include "fs_eaccess.hpp" + + +FUSE::ACCESS::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::ACCESS::FuncFF::operator()(const gfs::path &fusepath_, + const int mode_) +{ + int rv; + gfs::path fullpath; + + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath_; + + rv = fs::eaccess(fullpath,mode_); + if(rv != 0) + continue; + + return 0; + } + } + + return -errno; +} diff --git a/src/fuse_access_func_ff.hpp b/src/fuse_access_func_ff.hpp new file mode 100644 index 00000000..cab1af52 --- /dev/null +++ b/src/fuse_access_func_ff.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_access_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::ACCESS +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const gfs::path &fusepath, + const int mode) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_bmap.cpp b/src/fuse_bmap.cpp index 62359d42..c0ab21ec 100644 --- a/src/fuse_bmap.cpp +++ b/src/fuse_bmap.cpp @@ -16,6 +16,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "errno.hpp" #include @@ -23,8 +25,14 @@ #include -namespace FUSE +namespace FUSE::BMAP { + int + config(const toml::value &cfg_) + { + return 0; + } + int bmap(const char *fusepath_, size_t blocksize_, diff --git a/src/fuse_bmap.hpp b/src/fuse_bmap.hpp index 35152674..698964ff 100644 --- a/src/fuse_bmap.hpp +++ b/src/fuse_bmap.hpp @@ -18,10 +18,15 @@ #pragma once +#include "toml.hpp" + #include "fuse.h" -namespace FUSE +namespace FUSE::BMAP { + int + config(const toml::value &cfg); + int bmap(const char *fusepath, size_t blocksize, diff --git a/src/fuse_chmod.cpp b/src/fuse_chmod.cpp index c2e0e709..2aa62479 100644 --- a/src/fuse_chmod.cpp +++ b/src/fuse_chmod.cpp @@ -14,119 +14,24 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "config.hpp" -#include "errno.hpp" -#include "fs_lchmod.hpp" #include "fs_path.hpp" -#include "policy_rv.hpp" +#include "state.hpp" #include "ugid.hpp" #include "fuse.h" -#include -#include - -using std::string; - - -namespace l -{ - static - int - get_error(const PolicyRV &prv_, - const string &basepath_) - { - for(int i = 0, ei = prv_.success.size(); i < ei; i++) - { - if(prv_.success[i].basepath == basepath_) - return prv_.success[i].rv; - } - - for(int i = 0, ei = prv_.error.size(); i < ei; i++) - { - if(prv_.error[i].basepath == basepath_) - return prv_.error[i].rv; - } - - return 0; - } - - static - void - chmod_loop_core(const string &basepath_, - const char *fusepath_, - const mode_t mode_, - PolicyRV *prv_) - { - string fullpath; - - fullpath = fs::path::make(basepath_,fusepath_); - - errno = 0; - fs::lchmod(fullpath,mode_); - - prv_->insert(errno,basepath_); - } - - static - void - chmod_loop(const StrVec &basepaths_, - const char *fusepath_, - const mode_t mode_, - PolicyRV *prv_) - { - for(size_t i = 0, ei = basepaths_.size(); i != ei; i++) - { - l::chmod_loop_core(basepaths_[i],fusepath_,mode_,prv_); - } - } - - static - int - chmod(const Policy::Action &actionFunc_, - const Policy::Search &searchFunc_, - const Branches &branches_, - const char *fusepath_, - const mode_t mode_) - { - int rv; - PolicyRV prv; - StrVec basepaths; - - rv = actionFunc_(branches_,fusepath_,&basepaths); - if(rv == -1) - return -errno; - - l::chmod_loop(basepaths,fusepath_,mode_,&prv); - if(prv.error.empty()) - return 0; - if(prv.success.empty()) - return prv.error[0].rv; - - basepaths.clear(); - rv = searchFunc_(branches_,fusepath_,&basepaths); - if(rv == -1) - return -errno; - - return l::get_error(prv,basepaths[0]); - } -} - -namespace FUSE +namespace FUSE::CHMOD { int chmod(const char *fusepath_, mode_t mode_) { - Config::Read cfg; + State s; + gfs::path fusepath(&fusepath_[1]); const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - return l::chmod(cfg->func.chmod.policy, - cfg->func.getattr.policy, - cfg->branches, - fusepath_, - mode_); + return s->chmod(fusepath,mode_); } } diff --git a/src/fuse_chmod.hpp b/src/fuse_chmod.hpp index ea335a34..7b59f502 100644 --- a/src/fuse_chmod.hpp +++ b/src/fuse_chmod.hpp @@ -16,9 +16,15 @@ #pragma once +#include "toml.hpp" -namespace FUSE +#include + +namespace FUSE::CHMOD { + int + config(const toml::value &cfg); + int chmod(const char *fusepath, mode_t mode); diff --git a/src/fuse_chmod_err.hpp b/src/fuse_chmod_err.hpp new file mode 100644 index 00000000..f0fa6ad9 --- /dev/null +++ b/src/fuse_chmod_err.hpp @@ -0,0 +1,54 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +namespace FUSE::CHMOD +{ + class Err + { + public: + Err() + : _err(0) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err != 0) + { + if(_err == -ENOENT) + _err = err_; + } + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_chmod_func.cpp b/src/fuse_chmod_func.cpp new file mode 100644 index 00000000..37a59ec1 --- /dev/null +++ b/src/fuse_chmod_func.cpp @@ -0,0 +1,35 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chmod_func.hpp" +#include "fuse_chmod_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::CHMOD::Func::Func(const toml::value &toml_) +{ + _chmod = FuncFactory(toml_); +} + +int +FUSE::CHMOD::Func::operator()(const gfs::path &fusepath_, + const mode_t mode_) +{ + return (*_chmod)(fusepath_,mode_); +} diff --git a/src/fuse_chmod_func.hpp b/src/fuse_chmod_func.hpp new file mode 100644 index 00000000..30f405ca --- /dev/null +++ b/src/fuse_chmod_func.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chmod_func_base.hpp" + +#include "toml.hpp" + +#include + + +namespace FUSE::CHMOD +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const gfs::path &fusepath, + const mode_t mode); + + private: + FuncBase::Ptr _chmod; + }; +} diff --git a/src/fuse_chmod_func_all.cpp b/src/fuse_chmod_func_all.cpp new file mode 100644 index 00000000..5f1296e6 --- /dev/null +++ b/src/fuse_chmod_func_all.cpp @@ -0,0 +1,50 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chmod_func_all.hpp" +#include "fuse_chmod_err.hpp" + +#include "fs_lchmod.hpp" + + +FUSE::CHMOD::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::CHMOD::FuncALL::operator()(const gfs::path &fusepath_, + const mode_t mode_) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath_; + + rv = fs::lchmod(fullpath,mode_); + } + } + + return rv; +} diff --git a/src/fuse_chmod_func_all.hpp b/src/fuse_chmod_func_all.hpp new file mode 100644 index 00000000..4a910e95 --- /dev/null +++ b/src/fuse_chmod_func_all.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chmod_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::CHMOD +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const gfs::path &fusepath, + const mode_t mode) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_chmod_func_base.hpp b/src/fuse_chmod_func_base.hpp new file mode 100644 index 00000000..1e711d98 --- /dev/null +++ b/src/fuse_chmod_func_base.hpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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_path.hpp" + +#include + + +namespace FUSE::CHMOD +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const gfs::path &fusepath, + const mode_t mode) = 0; + }; +} diff --git a/src/fuse_chmod_func_factory.cpp b/src/fuse_chmod_func_factory.cpp new file mode 100644 index 00000000..a5f1ee4d --- /dev/null +++ b/src/fuse_chmod_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chmod_func_factory.hpp" +#include "fuse_chmod_func_all.hpp" + +#include + + +namespace FUSE::CHMOD +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","chmod","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_chmod_func_factory.hpp b/src/fuse_chmod_func_factory.hpp new file mode 100644 index 00000000..7f02193e --- /dev/null +++ b/src/fuse_chmod_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chmod_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::CHMOD +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_chown.cpp b/src/fuse_chown.cpp index 106545b4..9eca88d8 100644 --- a/src/fuse_chown.cpp +++ b/src/fuse_chown.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Antonio SJ Musumeci + Copyright (c) 2022, Antonio SJ Musumeci Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -14,124 +14,25 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "config.hpp" -#include "errno.hpp" -#include "fs_lchown.hpp" #include "fs_path.hpp" -#include "policy_rv.hpp" +#include "state.hpp" #include "ugid.hpp" #include "fuse.h" -#include -#include -using std::string; -using std::vector; - - -namespace l -{ - static - int - get_error(const PolicyRV &prv_, - const string &basepath_) - { - for(int i = 0, ei = prv_.success.size(); i < ei; i++) - { - if(prv_.success[i].basepath == basepath_) - return prv_.success[i].rv; - } - - for(int i = 0, ei = prv_.error.size(); i < ei; i++) - { - if(prv_.error[i].basepath == basepath_) - return prv_.error[i].rv; - } - - return 0; - } - - static - void - chown_loop_core(const string &basepath_, - const char *fusepath_, - const uid_t uid_, - const gid_t gid_, - PolicyRV *prv_) - { - string fullpath; - - fullpath = fs::path::make(basepath_,fusepath_); - - errno = 0; - fs::lchown(fullpath,uid_,gid_); - - prv_->insert(errno,basepath_); - } - - static - void - chown_loop(const vector &basepaths_, - const char *fusepath_, - const uid_t uid_, - const gid_t gid_, - PolicyRV *prv_) - { - for(size_t i = 0, ei = basepaths_.size(); i != ei; i++) - { - l::chown_loop_core(basepaths_[i],fusepath_,uid_,gid_,prv_); - } - } - - static - int - chown(const Policy::Action &actionFunc_, - const Policy::Search &searchFunc_, - const Branches &branches_, - const char *fusepath_, - const uid_t uid_, - const gid_t gid_) - { - int rv; - PolicyRV prv; - vector basepaths; - - rv = actionFunc_(branches_,fusepath_,&basepaths); - if(rv == -1) - return -errno; - - l::chown_loop(basepaths,fusepath_,uid_,gid_,&prv); - if(prv.error.empty()) - return 0; - if(prv.success.empty()) - return prv.error[0].rv; - - basepaths.clear(); - rv = searchFunc_(branches_,fusepath_,&basepaths); - if(rv == -1) - return -errno; - - return l::get_error(prv,basepaths[0]); - } -} - -namespace FUSE +namespace FUSE::CHOWN { int chown(const char *fusepath_, uid_t uid_, gid_t gid_) { - Config::Read cfg; + State s; + gfs::path fusepath(&fusepath_[1]); const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - return l::chown(cfg->func.chown.policy, - cfg->func.getattr.policy, - cfg->branches, - fusepath_, - uid_, - gid_); + return s->chown(fusepath,uid_,gid_); } } diff --git a/src/fuse_chown.hpp b/src/fuse_chown.hpp index 1a367acf..a4630981 100644 --- a/src/fuse_chown.hpp +++ b/src/fuse_chown.hpp @@ -16,8 +16,12 @@ #pragma once +#include "toml.hpp" -namespace FUSE +#include + + +namespace FUSE::CHOWN { int chown(const char *fusepath, diff --git a/src/fuse_chown_err.hpp b/src/fuse_chown_err.hpp new file mode 100644 index 00000000..04e6dbaa --- /dev/null +++ b/src/fuse_chown_err.hpp @@ -0,0 +1,51 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +namespace FUSE::CHOWN +{ + class Err + { + public: + Err() + : _err(-ENOENT) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == -ENOENT) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_chown_func.cpp b/src/fuse_chown_func.cpp new file mode 100644 index 00000000..5e38638e --- /dev/null +++ b/src/fuse_chown_func.cpp @@ -0,0 +1,36 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chown_func.hpp" +#include "fuse_chown_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::CHOWN::Func::Func(const toml::value &toml_) +{ + _chown = FuncFactory(toml_); +} + +int +FUSE::CHOWN::Func::operator()(const gfs::path &fusepath_, + const uid_t uid_, + const gid_t gid_) +{ + return (*_chown)(fusepath_,uid_,gid_); +} diff --git a/src/fuse_chown_func.hpp b/src/fuse_chown_func.hpp new file mode 100644 index 00000000..ae8c37fd --- /dev/null +++ b/src/fuse_chown_func.hpp @@ -0,0 +1,43 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chown_func_base.hpp" + +#include "toml.hpp" + +#include + + +namespace FUSE::CHOWN +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const gfs::path &fusepath, + const uid_t uid, + const gid_t gid); + + private: + FuncBase::Ptr _chown; + }; +} diff --git a/src/fuse_chown_func_all.cpp b/src/fuse_chown_func_all.cpp new file mode 100644 index 00000000..25151e69 --- /dev/null +++ b/src/fuse_chown_func_all.cpp @@ -0,0 +1,51 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chown_func_all.hpp" +#include "fuse_chown_err.hpp" + +#include "fs_lchown.hpp" + + +FUSE::CHOWN::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::CHOWN::FuncALL::operator()(const gfs::path &fusepath_, + const uid_t uid_, + const gid_t gid_) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath_; + + rv = fs::lchown(fullpath,uid_,gid_); + } + } + + return rv; +} diff --git a/src/fuse_chown_func_all.hpp b/src/fuse_chown_func_all.hpp new file mode 100644 index 00000000..61dce75c --- /dev/null +++ b/src/fuse_chown_func_all.hpp @@ -0,0 +1,41 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chown_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::CHOWN +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const gfs::path &fusepath, + const uid_t uid, + const gid_t gid) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_chown_func_base.hpp b/src/fuse_chown_func_base.hpp new file mode 100644 index 00000000..7f54d19e --- /dev/null +++ b/src/fuse_chown_func_base.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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_path.hpp" + +#include + +#include + + +namespace FUSE::CHOWN +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const gfs::path &fusepath, + const uid_t uid, + const gid_t gid) = 0; + }; +} diff --git a/src/fuse_chown_func_factory.cpp b/src/fuse_chown_func_factory.cpp new file mode 100644 index 00000000..732771c5 --- /dev/null +++ b/src/fuse_chown_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chown_func_factory.hpp" +#include "fuse_chown_func_all.hpp" + +#include + + +namespace FUSE::CHOWN +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","chown","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_chown_func_factory.hpp b/src/fuse_chown_func_factory.hpp new file mode 100644 index 00000000..df29da77 --- /dev/null +++ b/src/fuse_chown_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_chown_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::CHOWN +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_copy_file_range.cpp b/src/fuse_copy_file_range.cpp index 34988d86..1dfa3733 100644 --- a/src/fuse_copy_file_range.cpp +++ b/src/fuse_copy_file_range.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "errno.hpp" #include "fileinfo.hpp" #include "fs_copy_file_range.hpp" @@ -47,8 +49,14 @@ namespace l } } -namespace FUSE +namespace FUSE::COPY_FILE_RANGE { + int + config(const toml::value &cfg_) + { + return 0; + } + ssize_t copy_file_range(const fuse_file_info_t *ffi_in_, off_t offset_in_, diff --git a/src/fuse_copy_file_range.hpp b/src/fuse_copy_file_range.hpp index ef18a3e6..bcb4c8a9 100644 --- a/src/fuse_copy_file_range.hpp +++ b/src/fuse_copy_file_range.hpp @@ -16,9 +16,14 @@ #pragma once +#include "toml.hpp" -namespace FUSE + +namespace FUSE::COPY_FILE_RANGE { + int + config(const toml::value &cfg); + ssize_t copy_file_range(const fuse_file_info_t *ffi_in, off_t offset_in, diff --git a/src/fuse_create.cpp b/src/fuse_create.cpp index 5d66d5eb..628e7b98 100644 --- a/src/fuse_create.cpp +++ b/src/fuse_create.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "config.hpp" #include "errno.hpp" #include "fileinfo.hpp" @@ -21,6 +23,7 @@ #include "fs_clonepath.hpp" #include "fs_open.hpp" #include "fs_path.hpp" +#include "state.hpp" #include "ugid.hpp" #include "fuse.h" @@ -164,10 +167,10 @@ namespace l } } -namespace FUSE +namespace FUSE::CREATE { int - create(const char *fusepath_, + create2(const char *fusepath_, mode_t mode_, fuse_file_info_t *ffi_) { @@ -189,4 +192,19 @@ namespace FUSE ffi_->flags, &ffi_->fh); } + + int + create(const char *fusepath_, + mode_t mode_, + fuse_file_info_t *ffi_) + { + State s; + const fuse_context *fc = fuse_get_context(); + const ugid::Set ugid(fc->uid,fc->gid); + + if(s->writeback_cache) + l::tweak_flags_writeback_cache(&ffi_->flags); + + return s->create(fusepath_,mode_,fc->umask,ffi_); + } } diff --git a/src/fuse_create.hpp b/src/fuse_create.hpp index 36a74d17..4d37ddcc 100644 --- a/src/fuse_create.hpp +++ b/src/fuse_create.hpp @@ -16,12 +16,14 @@ #pragma once +#include "toml.hpp" + #include "fuse.h" #include -namespace FUSE +namespace FUSE::CREATE { int create(const char *fusepath, diff --git a/src/fuse_create_func.cpp b/src/fuse_create_func.cpp new file mode 100644 index 00000000..840cd2b2 --- /dev/null +++ b/src/fuse_create_func.cpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_create_func.hpp" +#include "fuse_create_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::CREATE::Func::Func(const toml::value &toml_) +{ + _create = FuncFactory(toml_); +} + +int +FUSE::CREATE::Func::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_, + fuse_file_info_t *ffi_) +{ + return (*_create)(fusepath_,mode_,umask_,ffi_); +} diff --git a/src/fuse_create_func.hpp b/src/fuse_create_func.hpp new file mode 100644 index 00000000..6871efc1 --- /dev/null +++ b/src/fuse_create_func.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_create_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::CREATE +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask, + fuse_file_info_t *ffi); + + private: + FuncBase::Ptr _create; + }; +} diff --git a/src/fuse_create_func_base.hpp b/src/fuse_create_func_base.hpp new file mode 100644 index 00000000..fcce1e56 --- /dev/null +++ b/src/fuse_create_func_base.hpp @@ -0,0 +1,39 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse.h" + +#include + + +namespace FUSE::CREATE +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask, + fuse_file_info_t *ffi) = 0; + }; +} diff --git a/src/fuse_create_func_epff.cpp b/src/fuse_create_func_epff.cpp new file mode 100644 index 00000000..33e16f39 --- /dev/null +++ b/src/fuse_create_func_epff.cpp @@ -0,0 +1,73 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_create_func_epff.hpp" + +#include "fileinfo.hpp" +#include "fs_acl.hpp" +#include "fs_open.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::CREATE::FuncEPFF::FuncEPFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::CREATE::FuncEPFF::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_, + fuse_file_info_t *ffi_) +{ + int rv; + mode_t mode; + gfs::path fusepath; + gfs::path fullpath; + + mode = mode_; + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::acl::dir_has_defaults(fullpath); + if(rv == -ENOENT) + continue; + if(rv >= 0) + mode &= umask_; + + rv = fs::open(fullpath,ffi_->flags,mode); + if(rv == -ENOENT) + continue; + if(rv < 0) + return rv; + + ffi_->fh = reinterpret_cast(new FileInfo(rv,fusepath_)); + + return 0; + } + } + + return -ENOENT; +} diff --git a/src/fuse_create_func_epff.hpp b/src/fuse_create_func_epff.hpp new file mode 100644 index 00000000..b3049763 --- /dev/null +++ b/src/fuse_create_func_epff.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_create_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::CREATE +{ + class FuncEPFF : public FuncBase + { + public: + FuncEPFF(const toml::value&); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask, + fuse_file_info_t *ffi) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_create_func_factory.cpp b/src/fuse_create_func_factory.cpp new file mode 100644 index 00000000..34b3f434 --- /dev/null +++ b/src/fuse_create_func_factory.cpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_create_func_factory.hpp" +#include "fuse_create_func_ff.hpp" +#include "fuse_create_func_epff.hpp" + +#include + +namespace FUSE::CREATE +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","create","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + if(str == "epff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_create_func_factory.hpp b/src/fuse_create_func_factory.hpp new file mode 100644 index 00000000..98ad0db5 --- /dev/null +++ b/src/fuse_create_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_create_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::CREATE +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_create_func_ff.cpp b/src/fuse_create_func_ff.cpp new file mode 100644 index 00000000..3d4c98f1 --- /dev/null +++ b/src/fuse_create_func_ff.cpp @@ -0,0 +1,91 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_create_func_ff.hpp" + +#include "fileinfo.hpp" +#include "fs_acl.hpp" +#include "fs_clonepath.hpp" +#include "fs_clonepath_branches.hpp" +#include "fs_open.hpp" +#include "ugid.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::CREATE::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::CREATE::FuncFF::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_, + fuse_file_info_t *ffi_) +{ + int rv; + mode_t mode; + gfs::path fusepath; + gfs::path fullpath; + + mode = mode_; + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::acl::dir_has_defaults(fullpath); + if(rv == -ENOENT) + { + rv = fs::clonepath_as_root(_branches,branch.path,fusepath); + if(rv >= 0) + rv = fs::acl::dir_has_defaults(fullpath); + } + + if(rv >= 0) + mode &= ~umask_; + + rv = fs::open(fullpath,ffi_->flags,mode); + if(rv == -ENOENT) + { + rv = fs::clonepath_as_root(_branches,branch.path,fusepath); + if(rv >= 0) + { + rv = fs::acl::dir_has_defaults(fullpath); + if(rv >= 0) + mode &= ~umask_; + rv = fs::open(fullpath,ffi_->flags,mode); + } + } + + if(rv < 0) + return rv; + + ffi_->fh = reinterpret_cast(new FileInfo(rv,fusepath_)); + + return 0; + } + } + + return -ENOENT; +} diff --git a/src/fuse_create_func_ff.hpp b/src/fuse_create_func_ff.hpp new file mode 100644 index 00000000..a959d34a --- /dev/null +++ b/src/fuse_create_func_ff.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_create_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::CREATE +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask, + fuse_file_info_t *ffi) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_destroy.cpp b/src/fuse_destroy.cpp index d1c5632b..7964ce38 100644 --- a/src/fuse_destroy.cpp +++ b/src/fuse_destroy.cpp @@ -14,7 +14,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -namespace FUSE +namespace FUSE::DESTROY { void destroy(void) diff --git a/src/fuse_destroy.hpp b/src/fuse_destroy.hpp index cb1cbeda..26275114 100644 --- a/src/fuse_destroy.hpp +++ b/src/fuse_destroy.hpp @@ -17,7 +17,7 @@ #pragma once -namespace FUSE +namespace FUSE::DESTROY { void destroy(void); diff --git a/src/fuse_fallocate.cpp b/src/fuse_fallocate.cpp index c1b4e82d..762b1074 100644 --- a/src/fuse_fallocate.cpp +++ b/src/fuse_fallocate.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "errno.hpp" #include "fileinfo.hpp" #include "fs_fallocate.hpp" @@ -38,8 +40,14 @@ namespace l } } -namespace FUSE +namespace FUSE::FALLOCATE { + int + config(const toml::value &cfg_) + { + return 0; + } + int fallocate(const fuse_file_info_t *ffi_, int mode_, diff --git a/src/fuse_fallocate.hpp b/src/fuse_fallocate.hpp index 978cc032..9e1f295b 100644 --- a/src/fuse_fallocate.hpp +++ b/src/fuse_fallocate.hpp @@ -16,11 +16,16 @@ #pragma once +#include "toml.hpp" + #include "fuse.h" -namespace FUSE +namespace FUSE::FALLOCATE { + int + config(const toml::value &cfg); + int fallocate(const fuse_file_info_t *ffi, int mode, diff --git a/src/fuse_fchmod.cpp b/src/fuse_fchmod.cpp index 27d802de..72becf4e 100644 --- a/src/fuse_fchmod.cpp +++ b/src/fuse_fchmod.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "errno.hpp" #include "fileinfo.hpp" #include "fs_fchmod.hpp" @@ -38,8 +40,14 @@ namespace l } } -namespace FUSE +namespace FUSE::FCHMOD { + int + config(const toml::value &cfg_) + { + return 0; + } + int fchmod(const fuse_file_info_t *ffi_, const mode_t mode_) diff --git a/src/fuse_fchmod.hpp b/src/fuse_fchmod.hpp index 64f9e7a5..ef4bdcff 100644 --- a/src/fuse_fchmod.hpp +++ b/src/fuse_fchmod.hpp @@ -16,13 +16,18 @@ #pragma once +#include "toml.hpp" + #include "fuse.h" #include -namespace FUSE +namespace FUSE::FCHMOD { + int + config(const toml::value &cfg); + int fchmod(const fuse_file_info_t *ffi, const mode_t mode); diff --git a/src/fuse_fchown.cpp b/src/fuse_fchown.cpp index 48e8aa16..b6f9597a 100644 --- a/src/fuse_fchown.cpp +++ b/src/fuse_fchown.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "errno.hpp" #include "fileinfo.hpp" #include "fs_fchown.hpp" @@ -41,8 +43,14 @@ namespace l } } -namespace FUSE +namespace FUSE::FCHOWN { + int + config(const toml::value &cfg_) + { + return 0; + } + int fchown(const fuse_file_info_t *ffi_, const uid_t uid_, diff --git a/src/fuse_fchown.hpp b/src/fuse_fchown.hpp index 991734f6..7422e7d2 100644 --- a/src/fuse_fchown.hpp +++ b/src/fuse_fchown.hpp @@ -16,11 +16,16 @@ #pragma once +#include "toml.hpp" + #include "fuse.h" -namespace FUSE +namespace FUSE::FCHOWN { + int + config(const toml::value &cfg); + int fchown(const fuse_file_info_t *ffi, uid_t uid, diff --git a/src/fuse_fgetattr.cpp b/src/fuse_fgetattr.cpp index bc8b44d4..b8653577 100644 --- a/src/fuse_fgetattr.cpp +++ b/src/fuse_fgetattr.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "config.hpp" #include "errno.hpp" #include "fileinfo.hpp" @@ -43,8 +45,14 @@ namespace l } } -namespace FUSE +namespace FUSE::FGETATTR { + int + config(const toml::value &cfg_) + { + return 0; + } + int fgetattr(const fuse_file_info_t *ffi_, struct stat *st_, diff --git a/src/fuse_fgetattr.hpp b/src/fuse_fgetattr.hpp index e58a061a..12962c79 100644 --- a/src/fuse_fgetattr.hpp +++ b/src/fuse_fgetattr.hpp @@ -16,6 +16,8 @@ #pragma once +#include "toml.hpp" + #include "fuse.h" #include @@ -23,8 +25,11 @@ #include -namespace FUSE +namespace FUSE::FGETATTR { + int + config(const toml::value &cfg); + int fgetattr(const fuse_file_info_t *ffi, struct stat *st, diff --git a/src/fuse_flock.cpp b/src/fuse_flock.cpp index 4a6a2bb3..4d9f0a3e 100644 --- a/src/fuse_flock.cpp +++ b/src/fuse_flock.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "errno.hpp" #include "fileinfo.hpp" #include "fs_flock.hpp" @@ -36,8 +38,14 @@ namespace l } } -namespace FUSE +namespace FUSE::FLOCK { + int + config(const toml::value &cfg_) + { + return 0; + } + int flock(const fuse_file_info_t *ffi_, int op_) diff --git a/src/fuse_flock.hpp b/src/fuse_flock.hpp index 194da4de..7bcbf053 100644 --- a/src/fuse_flock.hpp +++ b/src/fuse_flock.hpp @@ -16,11 +16,16 @@ #pragma once +#include "toml.hpp" + #include "fuse.h" -namespace FUSE +namespace FUSE::FLOCK { + int + config(const toml::value &cfg); + int flock(const fuse_file_info_t *ffi, int op); diff --git a/src/fuse_flush.cpp b/src/fuse_flush.cpp index 37dc87b2..4c1d92ce 100644 --- a/src/fuse_flush.cpp +++ b/src/fuse_flush.cpp @@ -14,6 +14,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "toml.hpp" + #include "errno.hpp" #include "fileinfo.hpp" #include "fs_close.hpp" @@ -40,8 +42,14 @@ namespace l } } -namespace FUSE +namespace FUSE::FLUSH { + int + config(const toml::value &cfg_) + { + return 0; + } + int flush(const fuse_file_info_t *ffi_) { diff --git a/src/fuse_flush.hpp b/src/fuse_flush.hpp index b8d447c3..92e74038 100644 --- a/src/fuse_flush.hpp +++ b/src/fuse_flush.hpp @@ -16,11 +16,16 @@ #pragma once +#include "toml.hpp" + #include "fuse.h" -namespace FUSE +namespace FUSE::FLUSH { + int + config(const toml::value &cfg); + int flush(const fuse_file_info_t *ffi); } diff --git a/src/fuse_free_hide.cpp b/src/fuse_free_hide.cpp index 4c057d5f..060683eb 100644 --- a/src/fuse_free_hide.cpp +++ b/src/fuse_free_hide.cpp @@ -22,7 +22,7 @@ #include -namespace FUSE +namespace FUSE::FREE_HIDE { int free_hide(const uint64_t fh_) diff --git a/src/fuse_free_hide.hpp b/src/fuse_free_hide.hpp index 8b6be9ee..77cf14c0 100644 --- a/src/fuse_free_hide.hpp +++ b/src/fuse_free_hide.hpp @@ -21,7 +21,7 @@ #include -namespace FUSE +namespace FUSE::FREE_HIDE { int free_hide(const uint64_t fh); diff --git a/src/fuse_fsync.cpp b/src/fuse_fsync.cpp index 1ab9824b..3a410c3b 100644 --- a/src/fuse_fsync.cpp +++ b/src/fuse_fsync.cpp @@ -42,7 +42,7 @@ namespace l } } -namespace FUSE +namespace FUSE::FSYNC { int fsync(const fuse_file_info_t *ffi_, diff --git a/src/fuse_fsync.hpp b/src/fuse_fsync.hpp index 58302504..bedec31d 100644 --- a/src/fuse_fsync.hpp +++ b/src/fuse_fsync.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::FSYNC { int fsync(const fuse_file_info_t *ffi, diff --git a/src/fuse_fsyncdir.cpp b/src/fuse_fsyncdir.cpp index 1388b410..e2255e5f 100644 --- a/src/fuse_fsyncdir.cpp +++ b/src/fuse_fsyncdir.cpp @@ -40,7 +40,7 @@ namespace l } } -namespace FUSE +namespace FUSE::FSYNCDIR { int fsyncdir(const fuse_file_info_t *ffi_, diff --git a/src/fuse_fsyncdir.hpp b/src/fuse_fsyncdir.hpp index e1eba800..4cb47bce 100644 --- a/src/fuse_fsyncdir.hpp +++ b/src/fuse_fsyncdir.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::FSYNCDIR { int fsyncdir(const fuse_file_info_t *ffi, diff --git a/src/fuse_ftruncate.cpp b/src/fuse_ftruncate.cpp index 4e3116d4..15c51000 100644 --- a/src/fuse_ftruncate.cpp +++ b/src/fuse_ftruncate.cpp @@ -36,7 +36,7 @@ namespace l } } -namespace FUSE +namespace FUSE::FTRUNCATE { int ftruncate(const fuse_file_info_t *ffi_, diff --git a/src/fuse_ftruncate.hpp b/src/fuse_ftruncate.hpp index 16d1c720..5bb11237 100644 --- a/src/fuse_ftruncate.hpp +++ b/src/fuse_ftruncate.hpp @@ -22,7 +22,7 @@ #include -namespace FUSE +namespace FUSE::FTRUNCATE { int ftruncate(const fuse_file_info_t *ffi, diff --git a/src/fuse_futimens.cpp b/src/fuse_futimens.cpp index 8dd27323..ceb4b5b4 100644 --- a/src/fuse_futimens.cpp +++ b/src/fuse_futimens.cpp @@ -40,7 +40,7 @@ namespace l } } -namespace FUSE +namespace FUSE::FUTIMENS { int futimens(const fuse_file_info_t *ffi_, diff --git a/src/fuse_futimens.hpp b/src/fuse_futimens.hpp index 7e373cc4..99f13031 100644 --- a/src/fuse_futimens.hpp +++ b/src/fuse_futimens.hpp @@ -21,7 +21,7 @@ #include -namespace FUSE +namespace FUSE::FUTIMENS { int futimens(const fuse_file_info_t *ffi, diff --git a/src/fuse_getattr.cpp b/src/fuse_getattr.cpp index e7431be3..93dc3051 100644 --- a/src/fuse_getattr.cpp +++ b/src/fuse_getattr.cpp @@ -14,175 +14,41 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "config.hpp" -#include "errno.hpp" +#include "state.hpp" + #include "fs_inode.hpp" -#include "fs_lstat.hpp" -#include "fs_path.hpp" -#include "fs_stat.hpp" #include "symlinkify.hpp" #include "ugid.hpp" #include "fuse.h" -#include - -using std::string; - -namespace l +namespace FUSE::GETATTR { - 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_) - { - static const uid_t uid = ::getuid(); - static const gid_t gid = ::getgid(); - static const time_t now = ::time(NULL); - - st_->st_dev = 0; - st_->st_ino = fs::inode::MAGIC; - st_->st_mode = (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); - st_->st_nlink = 1; - st_->st_uid = uid; - st_->st_gid = gid; - st_->st_rdev = 0; - st_->st_size = 0; - st_->st_blksize = 512; - st_->st_blocks = 0; - st_->st_atime = now; - st_->st_mtime = now; - st_->st_ctime = now; - - return 0; - } - - static - int - getattr(const Policy::Search &searchFunc_, - const Branches &branches_, - const char *fusepath_, - struct stat *st_, - const bool symlinkify_, - const time_t symlinkify_timeout_, - FollowSymlinks followsymlinks_) - { - int rv; - string fullpath; - StrVec basepaths; - - rv = searchFunc_(branches_,fusepath_,&basepaths); - if(rv == -1) - return -errno; - - fullpath = fs::path::make(basepaths[0],fusepath_); - - switch(followsymlinks_) - { - case FollowSymlinks::ENUM::NEVER: - rv = fs::lstat(fullpath,st_); - break; - case FollowSymlinks::ENUM::DIRECTORY: - rv = fs::lstat(fullpath,st_); - if(S_ISLNK(st_->st_mode)) - l::set_stat_if_leads_to_dir(fullpath,st_); - break; - case FollowSymlinks::ENUM::REGULAR: - rv = fs::lstat(fullpath,st_); - if(S_ISLNK(st_->st_mode)) - l::set_stat_if_leads_to_reg(fullpath,st_); - break; - case FollowSymlinks::ENUM::ALL: - rv = fs::stat(fullpath,st_); - if(rv != 0) - rv = fs::lstat(fullpath,st_); - break; - } - - if(rv == -1) - return -errno; - - if(symlinkify_ && symlinkify::can_be_symlink(*st_,symlinkify_timeout_)) - st_->st_mode = symlinkify::convert(st_->st_mode); - - fs::inode::calc(fusepath_,st_); - - return 0; - } - int getattr(const char *fusepath_, struct stat *st_, fuse_timeouts_t *timeout_) { + State s; int rv; - Config::Read cfg; const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - rv = l::getattr(cfg->func.getattr.policy, - cfg->branches, - fusepath_, - st_, - cfg->symlinkify, - cfg->symlinkify_timeout, - cfg->follow_symlinks); + rv = s->getattr(fusepath_,st_,timeout_); + if(rv < 0) + return rv; - timeout_->entry = ((rv >= 0) ? - cfg->cache_entry : - cfg->cache_negative_entry); - timeout_->attr = cfg->cache_attr; + if(s->symlinkify && symlinkify::can_be_symlink(*st_,s->symlinkify_timeout)) + st_->st_mode = symlinkify::convert(st_->st_mode); - return rv; - } -} + fs::inode::calc(fusepath_,st_); -namespace FUSE -{ - int - getattr(const char *fusepath_, - struct stat *st_, - fuse_timeouts_t *timeout_) - { - if(fusepath_ == CONTROLFILE) - return l::getattr_controlfile(st_); + timeout_->entry = ((rv >= 0) ? + s->entry_cache_timeout : + s->neg_entry_cache_timeout); + timeout_->attr = s->attr_cache_timeout; - return l::getattr(fusepath_,st_,timeout_); + return 0; } } diff --git a/src/fuse_getattr.hpp b/src/fuse_getattr.hpp index 03e4b6c1..8f0bbdbd 100644 --- a/src/fuse_getattr.hpp +++ b/src/fuse_getattr.hpp @@ -16,13 +16,20 @@ #pragma once +#include "fuse_timeouts.h" + +#include "toml.hpp" + #include #include #include -namespace FUSE +namespace FUSE::GETATTR { + int + config(const toml::value &cfg); + int getattr(const char *fusepath, struct stat *buf, diff --git a/src/fuse_getattr_func.cpp b/src/fuse_getattr_func.cpp new file mode 100644 index 00000000..3180c51e --- /dev/null +++ b/src/fuse_getattr_func.cpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func.hpp" +#include "fuse_getattr_func_factory.hpp" + +#include "state.hpp" + + +FUSE::GETATTR::Func::Func(const toml::value &toml_) +{ + _getattr = FuncFactory(toml_); +} + +void +FUSE::GETATTR::Func::operator=(const toml::value &toml_) +{ + _getattr = FuncFactory(toml_); +} + +int +FUSE::GETATTR::Func::operator()(const char *fusepath_, + struct stat *st_, + fuse_timeouts_t *timeout_) +{ + return (*_getattr)(fusepath_,st_,timeout_); +} diff --git a/src/fuse_getattr_func.hpp b/src/fuse_getattr_func.hpp new file mode 100644 index 00000000..9e67c934 --- /dev/null +++ b/src/fuse_getattr_func.hpp @@ -0,0 +1,48 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "from_toml.hpp" +#include "fuse_getattr_func_base.hpp" + +#include "fuse_timeouts.h" + +#include + +#include + +namespace FUSE::GETATTR +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + struct stat *st, + fuse_timeouts_t *timeout); + + + void operator=(const toml::value&); + + private: + FuncBase::Ptr _getattr; + }; +} diff --git a/src/fuse_getattr_func_aggregate.cpp b/src/fuse_getattr_func_aggregate.cpp new file mode 100644 index 00000000..f27e1dcd --- /dev/null +++ b/src/fuse_getattr_func_aggregate.cpp @@ -0,0 +1,79 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_aggregate.hpp" + +#include "timespec.hpp" + +#include + + +namespace gfs = ghc::filesystem; + + +FUSE::GETATTR::FuncAggregate::FuncAggregate(const toml::value &toml_) + : _branches(toml_) +{ + Stat::Func statfunc = Stat::no_follow; + + _statfunc = toml::find_or(toml_,"func","getattr","follow-symlinks",statfunc); +} + +int +FUSE::GETATTR::FuncAggregate::operator()(const char *fusepath_, + struct stat *st_, + fuse_timeouts_t *timeout_) +{ + int rv; + gfs::path fullpath; + struct stat st = {0}; + + st_->st_mtime = std::numeric_limits::min(); + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path; + fullpath /= &fusepath_[1]; + + rv = _statfunc(fullpath,&st); + if(rv != 0) + continue; + + if(st_->st_mtime == std::numeric_limits::min()) + { + *st_ = st; + } + else + { + st_->st_nlink += st.st_nlink; + if(st_->st_atim < st.st_atim) + st_->st_atim = st.st_atim; + if(st_->st_mtim < st.st_mtim) + st_->st_mtim = st.st_mtim; + if(st_->st_ctim < st.st_ctim) + st_->st_ctim = st.st_ctim; + } + } + } + + if(st_->st_mtime == std::numeric_limits::min()) + return -ENOENT; + + return 0; +} diff --git a/src/fuse_getattr_func_aggregate.hpp b/src/fuse_getattr_func_aggregate.hpp new file mode 100644 index 00000000..ab07d1f7 --- /dev/null +++ b/src/fuse_getattr_func_aggregate.hpp @@ -0,0 +1,44 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_base.hpp" + +#include "branches.hpp" +#include "stat_func.hpp" + +#include "toml.hpp" + +namespace FUSE::GETATTR +{ + class FuncAggregate : public FuncBase + { + public: + FuncAggregate(const toml::value&); + + public: + int operator()(const char *fusepath, + struct stat *st, + fuse_timeouts_t *timeout) final; + + private: + Stat::Func _statfunc; + Branches2 _branches; + }; +} diff --git a/src/fuse_getattr_func_base.hpp b/src/fuse_getattr_func_base.hpp new file mode 100644 index 00000000..333efe2d --- /dev/null +++ b/src/fuse_getattr_func_base.hpp @@ -0,0 +1,41 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "from_toml.hpp" + +#include "fuse_timeouts.h" + +#include + +#include + +namespace FUSE::GETATTR +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + struct stat *st, + fuse_timeouts_t *timeout) = 0; + }; +} diff --git a/src/fuse_getattr_func_check_ff.cpp b/src/fuse_getattr_func_check_ff.cpp new file mode 100644 index 00000000..c6af6f5a --- /dev/null +++ b/src/fuse_getattr_func_check_ff.cpp @@ -0,0 +1,100 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_check_ff.hpp" + +#include + + +namespace gfs = ghc::filesystem; + + +namespace l +{ + static + void + alert(const gfs::path &fullpath_, + const char *attr_, + const uint64_t val1_, + const uint64_t val2_) + { + fprintf(stderr, + "%s: %s - %lx != %lx", + fullpath_.c_str(), + attr_, + val1_, + val2_); + } +} + +FUSE::GETATTR::FuncCheckFF::FuncCheckFF(const toml::value &toml_) + : _branches(toml_) +{ + Stat::Func statfunc = Stat::no_follow; + + _statfunc = toml::find_or(toml_,"func","getattr","follow-symlinks",statfunc); +} + +int +FUSE::GETATTR::FuncCheckFF::operator()(const char *fusepath_, + struct stat *st_, + fuse_timeouts_t *timeout_) +{ + int rv; + size_t i, ei; + size_t j, ej; + gfs::path fullpath; + struct stat st = {0}; + + for(i = 0, ei = _branches.size(); i < ei; i++) + { + for(j = 0, ej = _branches[i].size(); j < ej; j++) + { + fullpath = _branches[i][j].path; + fullpath /= &fusepath_[1]; + + rv = _statfunc(fullpath,&st); + if(rv != 0) + continue; + + j++; + *st_ = st; + goto main_loop; + } + } + + main_loop: + for(; i < ei; i++) + { + for(; j < ej; j++) + { + fullpath = _branches[i][j].path; + fullpath /= &fusepath_[1]; + + rv = _statfunc(fullpath,&st); + if(rv != 0) + continue; + + if(st.st_mode != st_->st_mode) + l::alert(fullpath,"mode",st_->st_mode,st.st_mode); + } + } + + + return 0; +} diff --git a/src/fuse_getattr_func_check_ff.hpp b/src/fuse_getattr_func_check_ff.hpp new file mode 100644 index 00000000..084734fe --- /dev/null +++ b/src/fuse_getattr_func_check_ff.hpp @@ -0,0 +1,44 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_base.hpp" + +#include "branches.hpp" +#include "stat_func.hpp" + +#include "toml.hpp" + +namespace FUSE::GETATTR +{ + class FuncCheckFF : public FuncBase + { + public: + FuncCheckFF(const toml::value&); + + public: + int operator()(const char *fusepath, + struct stat *st, + fuse_timeouts_t *timeout) final; + + private: + Stat::Func _statfunc; + Branches2 _branches; + }; +} diff --git a/src/fuse_getattr_func_factory.cpp b/src/fuse_getattr_func_factory.cpp new file mode 100644 index 00000000..ae613c53 --- /dev/null +++ b/src/fuse_getattr_func_factory.cpp @@ -0,0 +1,48 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_factory.hpp" + +#include "fuse_getattr_func_base.hpp" +#include "fuse_getattr_func_ff.hpp" +#include "fuse_getattr_func_newest.hpp" +#include "fuse_getattr_func_aggregate.hpp" +#include "fuse_getattr_func_check_ff.hpp" + +#include + +namespace FUSE::GETATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","getattr","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + if(str == "newest") + return std::make_shared(toml_); + if(str == "aggregate") + return std::make_shared(toml_); + if(str == "check-ff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_getattr_func_factory.hpp b/src/fuse_getattr_func_factory.hpp new file mode 100644 index 00000000..11d97797 --- /dev/null +++ b/src/fuse_getattr_func_factory.hpp @@ -0,0 +1,29 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_base.hpp" + +#include "toml.hpp" + +namespace FUSE::GETATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_getattr_func_ff.cpp b/src/fuse_getattr_func_ff.cpp new file mode 100644 index 00000000..e53c33db --- /dev/null +++ b/src/fuse_getattr_func_ff.cpp @@ -0,0 +1,58 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_ff.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::GETATTR::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + Stat::Func statfunc = Stat::no_follow; + + _statfunc = toml::find_or(toml_,"func","getattr","follow-symlinks",statfunc); +} + +int +FUSE::GETATTR::FuncFF::operator()(const char *fusepath_, + struct stat *st_, + fuse_timeouts_t *timeout_) +{ + int rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = _statfunc(fullpath,st_); + if(rv == -ENOENT) + continue; + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_getattr_func_ff.hpp b/src/fuse_getattr_func_ff.hpp new file mode 100644 index 00000000..0c171a1d --- /dev/null +++ b/src/fuse_getattr_func_ff.hpp @@ -0,0 +1,44 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func.hpp" + +#include "branches.hpp" +#include "stat_func.hpp" + +#include "toml.hpp" + +namespace FUSE::GETATTR +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *fusepath, + struct stat *st, + fuse_timeouts_t *timeout) final; + + private: + Stat::Func _statfunc; + Branches2 _branches; + }; +} diff --git a/src/fuse_getattr_func_newest.cpp b/src/fuse_getattr_func_newest.cpp new file mode 100644 index 00000000..7bc59cd8 --- /dev/null +++ b/src/fuse_getattr_func_newest.cpp @@ -0,0 +1,66 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_newest.hpp" + +#include + + +namespace gfs = ghc::filesystem; + + +FUSE::GETATTR::FuncNewest::FuncNewest(const toml::value &toml_) + : _branches(toml_) +{ + Stat::Func statfunc = Stat::no_follow; + + _statfunc = toml::find_or(toml_,"func","getattr","follow-symlinks",statfunc); +} + +int +FUSE::GETATTR::FuncNewest::operator()(const char *fusepath_, + struct stat *st_, + fuse_timeouts_t *timeout_) +{ + int rv; + struct stat st; + gfs::path fullpath; + + st_->st_mtime = std::numeric_limits::min(); + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path; + fullpath /= &fusepath_[1]; + + rv = _statfunc(fullpath,&st); + if(rv != 0) + continue; + if(st_->st_mtime < st.st_mtime) + continue; + + *st_ = st; + } + } + + if(st_->st_mtime == std::numeric_limits::min()) + return -ENOENT; + + return 0; +} diff --git a/src/fuse_getattr_func_newest.hpp b/src/fuse_getattr_func_newest.hpp new file mode 100644 index 00000000..30ceac2b --- /dev/null +++ b/src/fuse_getattr_func_newest.hpp @@ -0,0 +1,44 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getattr_func_base.hpp" + +#include "branches.hpp" +#include "stat_func.hpp" + +#include "toml.hpp" + +namespace FUSE::GETATTR +{ + class FuncNewest : public FuncBase + { + public: + FuncNewest(const toml::value&); + + public: + int operator()(const char *fusepath, + struct stat *st, + fuse_timeouts_t *timeout) final; + + private: + Stat::Func _statfunc; + Branches2 _branches; + }; +} diff --git a/src/fuse_getxattr.cpp b/src/fuse_getxattr.cpp index 2f8145a2..8d2059bf 100644 --- a/src/fuse_getxattr.cpp +++ b/src/fuse_getxattr.cpp @@ -22,6 +22,7 @@ #include "fs_statvfs_cache.hpp" #include "str.hpp" #include "ugid.hpp" +#include "state.hpp" #include "version.hpp" #include "fuse.h" @@ -61,40 +62,6 @@ namespace l return ((rv == -1) ? -errno : rv); } - static - int - getxattr_controlfile(Config::Read &cfg_, - const char *attrname_, - char *buf_, - const size_t count_) - { - int rv; - size_t len; - string key; - string val; - StrVec attr; - - if(!str::startswith(attrname_,"user.mergerfs.")) - return -ENOATTR; - - key = &attrname_[14]; - rv = cfg_->get(key,&val); - if(rv < 0) - return rv; - - len = val.size(); - - if(count_ == 0) - return len; - - if(count_ < len) - return -ERANGE; - - memcpy(buf_,val.c_str(),len); - - return (int)len; - } - static int getxattr_from_string(char *destbuf_, @@ -192,22 +159,16 @@ namespace l } } -namespace FUSE +namespace FUSE::GETXATTR { int - getxattr(const char *fusepath_, - const char *attrname_, + getxattr2(const char *fusepath_, + const char *attrname_, char *buf_, size_t count_) { Config::Read cfg; - if(fusepath_ == CONTROLFILE) - return l::getxattr_controlfile(cfg, - attrname_, - buf_, - count_); - if((cfg->security_capability == false) && l::is_attrname_security_capability(attrname_)) return -ENOATTR; @@ -225,4 +186,21 @@ namespace FUSE buf_, count_); } + + int + getxattr(const char *fusepath_, + const char *attrname_, + char *buf_, + size_t count_) + { + State s; + + if((s->security_capability == false) && l::is_attrname_security_capability(attrname_)) + return -ENOATTR; + + const fuse_context *fc = fuse_get_context(); + const ugid::Set ugid(fc->uid,fc->gid); + + return s->getxattr(fusepath_,attrname_,buf_,count_); + } } diff --git a/src/fuse_getxattr.hpp b/src/fuse_getxattr.hpp index 60869c87..8293c1c1 100644 --- a/src/fuse_getxattr.hpp +++ b/src/fuse_getxattr.hpp @@ -17,7 +17,7 @@ #pragma once -namespace FUSE +namespace FUSE::GETXATTR { int getxattr(const char *fusepath, diff --git a/src/fuse_getxattr_func.cpp b/src/fuse_getxattr_func.cpp new file mode 100644 index 00000000..eee435e3 --- /dev/null +++ b/src/fuse_getxattr_func.cpp @@ -0,0 +1,43 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getxattr_func.hpp" +#include "fuse_getxattr_func_factory.hpp" + +#include "state.hpp" + + +FUSE::GETXATTR::Func::Func(const toml::value &toml_) +{ + _getxattr = FuncFactory(toml_); +} + +void +FUSE::GETXATTR::Func::operator=(const toml::value &toml_) +{ + _getxattr = FuncFactory(toml_); +} + +int +FUSE::GETXATTR::Func::operator()(const char *fusepath_, + const char *attrname_, + char *buf_, + size_t count_) +{ + return (*_getxattr)(fusepath_,attrname_,buf_,count_); +} diff --git a/src/fuse_getxattr_func.hpp b/src/fuse_getxattr_func.hpp new file mode 100644 index 00000000..a45db862 --- /dev/null +++ b/src/fuse_getxattr_func.hpp @@ -0,0 +1,48 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "from_toml.hpp" +#include "fuse_getxattr_func_base.hpp" + +#include "fuse_timeouts.h" + +#include + +#include + +namespace FUSE::GETXATTR +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + const char *attrname, + char *buf, + size_t count); + + void operator=(const toml::value&); + + private: + FuncBase::Ptr _getxattr; + }; +} diff --git a/src/fuse_getxattr_func_base.hpp b/src/fuse_getxattr_func_base.hpp new file mode 100644 index 00000000..e552e270 --- /dev/null +++ b/src/fuse_getxattr_func_base.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "from_toml.hpp" + +#include + +#include + +namespace FUSE::GETXATTR +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + const char *attrname, + char *buf, + size_t count) = 0; + }; +} diff --git a/src/fuse_getxattr_func_factory.cpp b/src/fuse_getxattr_func_factory.cpp new file mode 100644 index 00000000..92c65ffd --- /dev/null +++ b/src/fuse_getxattr_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getxattr_func_factory.hpp" + +#include "fuse_getxattr_func_ff.hpp" + +#include + +namespace FUSE::GETXATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","getxattr","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_getxattr_func_factory.hpp b/src/fuse_getxattr_func_factory.hpp new file mode 100644 index 00000000..d4b61166 --- /dev/null +++ b/src/fuse_getxattr_func_factory.hpp @@ -0,0 +1,29 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getxattr_func_base.hpp" + +#include "toml.hpp" + +namespace FUSE::GETXATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_getxattr_func_ff.cpp b/src/fuse_getxattr_func_ff.cpp new file mode 100644 index 00000000..80db25a8 --- /dev/null +++ b/src/fuse_getxattr_func_ff.cpp @@ -0,0 +1,59 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getxattr_func_ff.hpp" + +#include "fs_lgetxattr.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::GETXATTR::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::GETXATTR::FuncFF::operator()(const char *fusepath_, + const char *attrname_, + char *buf_, + size_t count_) +{ + int rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::lgetxattr(fullpath,attrname_,buf_,count_); + if(rv == -ENOENT) + continue; + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_getxattr_func_ff.hpp b/src/fuse_getxattr_func_ff.hpp new file mode 100644 index 00000000..dbc8c76d --- /dev/null +++ b/src/fuse_getxattr_func_ff.hpp @@ -0,0 +1,44 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_getxattr_func.hpp" + +#include "branches.hpp" +#include "stat_func.hpp" + +#include "toml.hpp" + +namespace FUSE::GETXATTR +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *fusepath, + const char *attrname, + char *buf, + size_t count) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index e46c727b..0f2bb0ef 100644 --- a/src/fuse_init.cpp +++ b/src/fuse_init.cpp @@ -79,7 +79,7 @@ namespace l } } -namespace FUSE +namespace FUSE::INIT { void * init(fuse_conn_info *conn_) diff --git a/src/fuse_init.hpp b/src/fuse_init.hpp index 5867d646..23156007 100644 --- a/src/fuse_init.hpp +++ b/src/fuse_init.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::INIT { void * init(fuse_conn_info *conn); diff --git a/src/fuse_ioctl.cpp b/src/fuse_ioctl.cpp index 6f480221..c249ed50 100644 --- a/src/fuse_ioctl.cpp +++ b/src/fuse_ioctl.cpp @@ -24,6 +24,7 @@ #include "fs_ioctl.hpp" #include "fs_open.hpp" #include "fs_path.hpp" +#include "state.hpp" #include "str.hpp" #include "ugid.hpp" @@ -330,7 +331,7 @@ namespace l } } -namespace FUSE +namespace FUSE::IOCTL { int ioctl(const fuse_file_info_t *ffi_, @@ -344,7 +345,7 @@ namespace FUSE return l::ioctl_custom(ffi_,cmd_,data_); if(flags_ & FUSE_IOCTL_DIR) - return l::ioctl_dir(ffi_,cmd_,data_,out_bufsz_); + return State()->ioctl(ffi_,cmd_,flags_,data_,out_bufsz_); return l::ioctl_file(ffi_,cmd_,data_,out_bufsz_); } diff --git a/src/fuse_ioctl.hpp b/src/fuse_ioctl.hpp index 11ea6233..538305dc 100644 --- a/src/fuse_ioctl.hpp +++ b/src/fuse_ioctl.hpp @@ -21,7 +21,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::IOCTL { int ioctl(const fuse_file_info_t *ffi, diff --git a/src/fuse_ioctl_err.hpp b/src/fuse_ioctl_err.hpp new file mode 100644 index 00000000..5b1e5267 --- /dev/null +++ b/src/fuse_ioctl_err.hpp @@ -0,0 +1,51 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +namespace FUSE::IOCTL +{ + class Err + { + public: + Err() + : _err(0) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == 0) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_ioctl_func.cpp b/src/fuse_ioctl_func.cpp new file mode 100644 index 00000000..9baf6e48 --- /dev/null +++ b/src/fuse_ioctl_func.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_ioctl_func.hpp" +#include "fuse_ioctl_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::IOCTL::Func::Func(const toml::value &toml_) +{ + _ioctl = FuncFactory(toml_); +} + +int +FUSE::IOCTL::Func::operator()(const fuse_file_info_t *ffi_, + const unsigned long cmd_, + const unsigned int flags_, + void *data_, + uint32_t *out_bufsz_) +{ + return (*_ioctl)(ffi_,cmd_,flags_,data_,out_bufsz_); +} diff --git a/src/fuse_ioctl_func.hpp b/src/fuse_ioctl_func.hpp new file mode 100644 index 00000000..de9ef91a --- /dev/null +++ b/src/fuse_ioctl_func.hpp @@ -0,0 +1,43 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_ioctl_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::IOCTL +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const fuse_file_info_t *ffi_, + const unsigned long cmd, + const unsigned int flags, + void *data, + uint32_t *out_bufsz); + + private: + FuncBase::Ptr _ioctl; + }; +} diff --git a/src/fuse_ioctl_func_all.cpp b/src/fuse_ioctl_func_all.cpp new file mode 100644 index 00000000..577c4838 --- /dev/null +++ b/src/fuse_ioctl_func_all.cpp @@ -0,0 +1,55 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_ioctl_func_all.hpp" +#include "fuse_ioctl_err.hpp" + +#include "fs_ioctl.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::IOCTL::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::IOCTL::FuncALL::operator()(const fuse_file_info_t *ffi_, + const unsigned long cmd_, + const unsigned int flags_, + void *data_, + uint32_t *out_bufsz_) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + } + } + + return rv; +} diff --git a/src/fuse_ioctl_func_all.hpp b/src/fuse_ioctl_func_all.hpp new file mode 100644 index 00000000..2a664ef4 --- /dev/null +++ b/src/fuse_ioctl_func_all.hpp @@ -0,0 +1,43 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_ioctl_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::IOCTL +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const fuse_file_info_t *ffi, + const unsigned long cmd, + const unsigned int flags, + void *data, + uint32_t *out_bufsz) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_ioctl_func_base.hpp b/src/fuse_ioctl_func_base.hpp new file mode 100644 index 00000000..d40d9e90 --- /dev/null +++ b/src/fuse_ioctl_func_base.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse.h" + +#include + + +namespace FUSE::IOCTL +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const fuse_file_info_t *ffi, + const unsigned long cmd, + const unsigned int flags, + void *data, + uint32_t *out_bufsz) = 0; + }; +} diff --git a/src/fuse_ioctl_func_factory.cpp b/src/fuse_ioctl_func_factory.cpp new file mode 100644 index 00000000..52bc7622 --- /dev/null +++ b/src/fuse_ioctl_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_ioctl_func_factory.hpp" +#include "fuse_ioctl_func_all.hpp" + +#include + + +namespace FUSE::IOCTL +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","ioctl","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_ioctl_func_factory.hpp b/src/fuse_ioctl_func_factory.hpp new file mode 100644 index 00000000..d2be09b1 --- /dev/null +++ b/src/fuse_ioctl_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_ioctl_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::IOCTL +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_link.cpp b/src/fuse_link.cpp index 7547b264..d87afaf1 100644 --- a/src/fuse_link.cpp +++ b/src/fuse_link.cpp @@ -23,205 +23,17 @@ #include "fuse_getattr.hpp" #include "fuse_symlink.hpp" #include "ghc/filesystem.hpp" +#include "state.hpp" #include "ugid.hpp" #include "fuse.h" -#include -#include -using std::string; -using std::vector; namespace gfs = ghc::filesystem; -namespace error -{ - static - inline - int - calc(const int rv_, - const int prev_, - const int cur_) - { - if(rv_ == -1) - { - if(prev_ == 0) - return 0; - return cur_; - } - - return 0; - } -} namespace l { - static - int - link_create_path_loop(const StrVec &oldbasepaths_, - const string &newbasepath_, - const char *oldfusepath_, - const char *newfusepath_, - const string &newfusedirpath_) - { - int rv; - int error; - string oldfullpath; - string newfullpath; - - error = -1; - for(auto &oldbasepath : oldbasepaths_) - { - 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); - } - - error = error::calc(rv,error,errno); - } - - return -error; - } - - static - int - link_create_path(const Policy::Search &searchFunc_, - const Policy::Action &actionFunc_, - const Branches &branches_, - const char *oldfusepath_, - const char *newfusepath_) - { - int rv; - string newfusedirpath; - StrVec oldbasepaths; - StrVec newbasepaths; - - rv = actionFunc_(branches_,oldfusepath_,&oldbasepaths); - if(rv == -1) - return -errno; - - newfusedirpath = fs::path::dirname(newfusepath_); - - rv = searchFunc_(branches_,newfusedirpath,&newbasepaths); - if(rv == -1) - return -errno; - - return l::link_create_path_loop(oldbasepaths,newbasepaths[0], - oldfusepath_,newfusepath_, - newfusedirpath); - } - - static - int - link_preserve_path_core(const string &oldbasepath_, - const char *oldfusepath_, - const char *newfusepath_, - struct stat *st_, - 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); - if((rv == -1) && (errno == ENOENT)) - 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 StrVec &oldbasepaths_, - const char *oldfusepath_, - const char *newfusepath_, - struct stat *st_) - { - int error; - - error = -1; - for(auto &oldbasepath : oldbasepaths_) - { - error = l::link_preserve_path_core(oldbasepath, - oldfusepath_, - newfusepath_, - st_, - error); - } - - return -error; - } - - static - int - link_preserve_path(const Policy::Action &actionFunc_, - const Branches &branches_, - const char *oldfusepath_, - const char *newfusepath_, - struct stat *st_) - { - int rv; - StrVec oldbasepaths; - - rv = actionFunc_(branches_,oldfusepath_,&oldbasepaths); - if(rv == -1) - return -errno; - - 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_); - } - - 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_); - if(rv < 0) - return rv; - - return FUSE::getattr(newpath_,st_,timeouts_); - } - static int link_exdev_rel_symlink(const char *oldpath_, @@ -235,9 +47,9 @@ namespace l target = target.lexically_relative(linkpath.parent_path()); - rv = FUSE::symlink(target.c_str(),linkpath.c_str()); + rv = FUSE::SYMLINK::symlink(target.c_str(),linkpath.c_str()); if(rv == 0) - rv = FUSE::getattr(oldpath_,st_,timeouts_); + rv = FUSE::GETATTR::getattr(oldpath_,st_,timeouts_); // Disable caching since we created a symlink but should be a regular. timeouts_->attr = 0; @@ -248,26 +60,20 @@ namespace l 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_) + link_exdev_abs_base_symlink(const char *oldpath_, + const char *newpath_, + struct stat *st_, + fuse_timeouts_t *timeouts_) { int rv; StrVec basepaths; - std::string target; + gfs::path target; - rv = openPolicy_(branches_,oldpath_,&basepaths); - if(rv == -1) - return -errno; + target = "FIXME"; - target = fs::path::make(basepaths[0],oldpath_); - - rv = FUSE::symlink(target.c_str(),newpath_); + rv = FUSE::SYMLINK::symlink(target.c_str(),newpath_); if(rv == 0) - rv = FUSE::getattr(oldpath_,st_,timeouts_); + rv = FUSE::GETATTR::getattr(oldpath_,st_,timeouts_); // Disable caching since we created a symlink but should be a regular. timeouts_->attr = 0; @@ -278,21 +84,20 @@ namespace l static int - link_exdev_abs_pool_symlink(const std::string &mount_, - const char *oldpath_, - const char *newpath_, - struct stat *st_, - fuse_timeouts_t *timeouts_) + link_exdev_abs_pool_symlink(const gfs::path &mount_, + const char *oldpath_, + const char *newpath_, + struct stat *st_, + fuse_timeouts_t *timeouts_) { int rv; - StrVec basepaths; - std::string target; + gfs::path target; - target = fs::path::make(mount_,oldpath_); + target = mount_ / &oldpath_[1]; - rv = FUSE::symlink(target.c_str(),newpath_); + rv = FUSE::SYMLINK::symlink(target.c_str(),newpath_); if(rv == 0) - rv = FUSE::getattr(oldpath_,st_,timeouts_); + rv = FUSE::GETATTR::getattr(oldpath_,st_,timeouts_); // Disable caching since we created a symlink but should be a regular. timeouts_->attr = 0; @@ -303,30 +108,29 @@ namespace l static int - link_exdev(Config::Read &cfg_, + link_exdev(State &s_, const char *oldpath_, const char *newpath_, struct stat *st_, fuse_timeouts_t *timeouts_) { - switch(cfg_->link_exdev) + switch(s_->link_exdev) { - case LinkEXDEV::ENUM::PASSTHROUGH: + case LinkEXDEV::INVALID: + case LinkEXDEV::PASSTHROUGH: return -EXDEV; - case LinkEXDEV::ENUM::REL_SYMLINK: + case LinkEXDEV::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_, + case LinkEXDEV::ABS_BASE_SYMLINK: + return l::link_exdev_abs_base_symlink(oldpath_, newpath_, st_, timeouts_); - case LinkEXDEV::ENUM::ABS_POOL_SYMLINK: - return l::link_exdev_abs_pool_symlink(cfg_->mount, + case LinkEXDEV::ABS_POOL_SYMLINK: + return l::link_exdev_abs_pool_symlink(s_->mountpoint, oldpath_, newpath_, st_, @@ -337,7 +141,7 @@ namespace l } } -namespace FUSE +namespace FUSE::LINK { int link(const char *oldpath_, @@ -346,14 +150,14 @@ namespace FUSE fuse_timeouts_t *timeouts_) { int rv; - Config::Read cfg; + State s; const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - rv = l::link(cfg,oldpath_,newpath_,st_,timeouts_); + rv = s->link(oldpath_,newpath_); if(rv == -EXDEV) - return l::link_exdev(cfg,oldpath_,newpath_,st_,timeouts_); + return l::link_exdev(s,oldpath_,newpath_,st_,timeouts_); - return rv; + return s->getattr(newpath_,st_,timeouts_); } } diff --git a/src/fuse_link.hpp b/src/fuse_link.hpp index 2711b931..ade6a0b9 100644 --- a/src/fuse_link.hpp +++ b/src/fuse_link.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::LINK { int link(const char *oldpath, diff --git a/src/fuse_link_err.hpp b/src/fuse_link_err.hpp new file mode 100644 index 00000000..7b899e42 --- /dev/null +++ b/src/fuse_link_err.hpp @@ -0,0 +1,54 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + + +namespace FUSE::LINK +{ + class Err + { + public: + Err() + : _err(-ENOENT) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err != 0) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_link_func.cpp b/src/fuse_link_func.cpp new file mode 100644 index 00000000..fd4d3075 --- /dev/null +++ b/src/fuse_link_func.cpp @@ -0,0 +1,28 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_link_func.hpp" +#include "fuse_link_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::LINK::Func::Func(const toml::value &toml_) +{ + _link = FuncFactory(toml_); +} diff --git a/src/fuse_link_func.hpp b/src/fuse_link_func.hpp new file mode 100644 index 00000000..c37a57c1 --- /dev/null +++ b/src/fuse_link_func.hpp @@ -0,0 +1,46 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_link_func_base.hpp" + +#include "toml.hpp" + +#include "fuse.h" + + +namespace FUSE::LINK +{ + class Func + { + public: + Func(const toml::value &); + + public: + int + operator()(const char *oldpath_, + const char *newpath_) + { + return (*_link)(oldpath_,newpath_); + } + + private: + FuncBase::Ptr _link; + }; +} diff --git a/src/fuse_link_func_all.cpp b/src/fuse_link_func_all.cpp new file mode 100644 index 00000000..dc07fa3f --- /dev/null +++ b/src/fuse_link_func_all.cpp @@ -0,0 +1,74 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_link_func_all.hpp" +#include "fuse_link_err.hpp" + +#include "fs_clonepath_branches.hpp" +#include "fs_exists.hpp" +#include "fs_link.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::LINK::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::LINK::FuncALL::operator()(const char *oldpath_, + const char *newpath_) +{ + int rv; + Err err; + gfs::path oldpath; + gfs::path newpath; + gfs::path fulloldpath; + gfs::path fullnewpath; + + oldpath = &oldpath_[1]; + newpath = &newpath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fulloldpath = branch.path / oldpath; + fullnewpath = branch.path / newpath;; + + rv = fs::link(fulloldpath,fullnewpath); + if(rv == -ENOENT) + { + if(!fs::exists(fulloldpath)) + continue; + + rv = fs::clonepath_as_root(_branches,branch.path,newpath.parent_path()); + if(rv < 0) + continue; + + rv = fs::link(fulloldpath,fullnewpath); + } + + err = rv; + } + } + + return err; +} diff --git a/src/fuse_link_func_all.hpp b/src/fuse_link_func_all.hpp new file mode 100644 index 00000000..335e2dfa --- /dev/null +++ b/src/fuse_link_func_all.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_link_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::LINK +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const char *oldpath, + const char *newpath) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_link_func_base.hpp b/src/fuse_link_func_base.hpp new file mode 100644 index 00000000..be26f9a6 --- /dev/null +++ b/src/fuse_link_func_base.hpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse.h" + +#include + + +namespace FUSE::LINK +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *oldpath, + const char *newpath) = 0; + + }; +} diff --git a/src/fuse_link_func_factory.cpp b/src/fuse_link_func_factory.cpp new file mode 100644 index 00000000..17f0b544 --- /dev/null +++ b/src/fuse_link_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_link_func_factory.hpp" +#include "fuse_link_func_all.hpp" + +#include + + +namespace FUSE::LINK +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","link","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_link_func_factory.hpp b/src/fuse_link_func_factory.hpp new file mode 100644 index 00000000..96e874c4 --- /dev/null +++ b/src/fuse_link_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_link_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::LINK +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_listxattr.cpp b/src/fuse_listxattr.cpp index 1f69b15c..01675a84 100644 --- a/src/fuse_listxattr.cpp +++ b/src/fuse_listxattr.cpp @@ -21,6 +21,7 @@ #include "fs_path.hpp" #include "ugid.hpp" #include "xattr.hpp" +#include "state.hpp" #include "fuse.h" @@ -33,26 +34,6 @@ using std::string; namespace l { - static - int - listxattr_controlfile(Config::Read &cfg_, - char *list_, - const size_t size_) - { - string xattrs; - - cfg_->keys_xattr(xattrs); - if(size_ == 0) - return xattrs.size(); - - if(size_ < xattrs.size()) - return -ERANGE; - - memcpy(list_,xattrs.c_str(),xattrs.size()); - - return xattrs.size(); - } - static int listxattr(const Policy::Search &searchFunc_, @@ -77,18 +58,15 @@ namespace l } } -namespace FUSE +namespace FUSE::LISTXATTR { int - listxattr(const char *fusepath_, + listxattr_old(const char *fusepath_, char *list_, size_t size_) { Config::Read cfg; - if(fusepath_ == CONTROLFILE) - return l::listxattr_controlfile(cfg,list_,size_); - switch(cfg->xattr) { case XAttr::ENUM::PASSTHROUGH: @@ -108,4 +86,16 @@ namespace FUSE list_, size_); } + + int + listxattr(const char *fusepath_, + char *list_, + size_t size_) + { + State s; + const fuse_context *fc = fuse_get_context(); + const ugid::Set ugid(fc->uid,fc->gid); + + return s->listxattr(fusepath_,list_,size_); + } } diff --git a/src/fuse_listxattr.hpp b/src/fuse_listxattr.hpp index f06c32c8..33d39e9a 100644 --- a/src/fuse_listxattr.hpp +++ b/src/fuse_listxattr.hpp @@ -17,10 +17,10 @@ #pragma once -namespace FUSE +namespace FUSE::LISTXATTR { int listxattr(const char *fusepath, - char *buf, - size_t count); + char *list, + size_t size); } diff --git a/src/fuse_listxattr_func.cpp b/src/fuse_listxattr_func.cpp new file mode 100644 index 00000000..21374542 --- /dev/null +++ b/src/fuse_listxattr_func.cpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_listxattr_func.hpp" +#include "fuse_listxattr_func_factory.hpp" + + +FUSE::LISTXATTR::Func::Func(const toml::value &toml_) +{ + _listxattr = FuncFactory(toml_); +} + +void +FUSE::LISTXATTR::Func::operator=(const toml::value &toml_) +{ + _listxattr = FuncFactory(toml_); +} + +int +FUSE::LISTXATTR::Func::operator()(const char *fusepath_, + char *list_, + const size_t size_) +{ + return (*_listxattr)(fusepath_,list_,size_); +} diff --git a/src/fuse_listxattr_func.hpp b/src/fuse_listxattr_func.hpp new file mode 100644 index 00000000..3df9ce06 --- /dev/null +++ b/src/fuse_listxattr_func.hpp @@ -0,0 +1,46 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "from_toml.hpp" +#include "fuse_listxattr_func_base.hpp" + +#include + +#include + +namespace FUSE::LISTXATTR +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + char *buf, + size_t count); + + + void operator=(const toml::value&); + + private: + FuncBase::Ptr _listxattr; + }; +} diff --git a/src/fuse_listxattr_func_base.hpp b/src/fuse_listxattr_func_base.hpp new file mode 100644 index 00000000..0b344741 --- /dev/null +++ b/src/fuse_listxattr_func_base.hpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + + +namespace FUSE::LISTXATTR +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + char *list, + const size_t size) = 0; + + }; +} diff --git a/src/fuse_listxattr_func_factory.cpp b/src/fuse_listxattr_func_factory.cpp new file mode 100644 index 00000000..3f0eda74 --- /dev/null +++ b/src/fuse_listxattr_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_listxattr_func_factory.hpp" + +#include "fuse_listxattr_func_ff.hpp" + +#include + +namespace FUSE::LISTXATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","listxattr","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_listxattr_func_factory.hpp b/src/fuse_listxattr_func_factory.hpp new file mode 100644 index 00000000..20247333 --- /dev/null +++ b/src/fuse_listxattr_func_factory.hpp @@ -0,0 +1,29 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_listxattr_func_base.hpp" + +#include "toml.hpp" + +namespace FUSE::LISTXATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_listxattr_func_ff.cpp b/src/fuse_listxattr_func_ff.cpp new file mode 100644 index 00000000..6173b462 --- /dev/null +++ b/src/fuse_listxattr_func_ff.cpp @@ -0,0 +1,58 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_listxattr_func_ff.hpp" + +#include "fs_llistxattr.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::LISTXATTR::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::LISTXATTR::FuncFF::operator()(const char *fusepath_, + char *list_, + const size_t size_) +{ + int rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::llistxattr(fullpath,list_,size_); + if(rv == -ENOENT) + continue; + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_listxattr_func_ff.hpp b/src/fuse_listxattr_func_ff.hpp new file mode 100644 index 00000000..d29d200c --- /dev/null +++ b/src/fuse_listxattr_func_ff.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_listxattr_func.hpp" + +#include "branches.hpp" + +#include "toml.hpp" + +namespace FUSE::LISTXATTR +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *fusepath, + char *list, + const size_t size) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_lock.cpp b/src/fuse_lock.cpp index 9d09a81f..cb2d776c 100644 --- a/src/fuse_lock.cpp +++ b/src/fuse_lock.cpp @@ -21,7 +21,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::LOCK { int lock(const fuse_file_info_t *ffi, diff --git a/src/fuse_lock.hpp b/src/fuse_lock.hpp index f6c08c63..b5cb3c2d 100644 --- a/src/fuse_lock.hpp +++ b/src/fuse_lock.hpp @@ -20,7 +20,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::LOCK { int lock(const fuse_file_info_t *ffi, diff --git a/src/fuse_mkdir.cpp b/src/fuse_mkdir.cpp index c8bc3e57..e8be0c7e 100644 --- a/src/fuse_mkdir.cpp +++ b/src/fuse_mkdir.cpp @@ -20,7 +20,9 @@ #include "fs_clonepath.hpp" #include "fs_mkdir.hpp" #include "fs_path.hpp" +#include "ghc/filesystem.hpp" #include "policy.hpp" +#include "state.hpp" #include "ugid.hpp" #include "fuse.h" @@ -144,11 +146,11 @@ namespace l } } -namespace FUSE +namespace FUSE::MKDIR { int - mkdir(const char *fusepath_, - mode_t mode_) + mkdir_old(const char *fusepath_, + mode_t mode_) { Config::Read cfg; const fuse_context *fc = fuse_get_context(); @@ -161,4 +163,15 @@ namespace FUSE mode_, fc->umask); } + + int + mkdir(const char *fusepath_, + mode_t mode_) + { + State s; + const fuse_context *fc = fuse_get_context(); + const ugid::Set ugid(fc->uid,fc->gid); + + return s->mkdir(fusepath_,mode_,fc->umask); + } } diff --git a/src/fuse_mkdir.hpp b/src/fuse_mkdir.hpp index f2c515b2..3b105c11 100644 --- a/src/fuse_mkdir.hpp +++ b/src/fuse_mkdir.hpp @@ -17,7 +17,7 @@ #pragma once -namespace FUSE +namespace FUSE::MKDIR { int mkdir(const char *fusepath, diff --git a/src/fuse_mkdir_func.cpp b/src/fuse_mkdir_func.cpp new file mode 100644 index 00000000..7abedfb0 --- /dev/null +++ b/src/fuse_mkdir_func.cpp @@ -0,0 +1,36 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_func.hpp" +#include "fuse_mkdir_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::MKDIR::Func::Func(const toml::value &toml_) +{ + _mkdir = FuncFactory(toml_); +} + +int +FUSE::MKDIR::Func::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_) +{ + return (*_mkdir)(fusepath_,mode_,umask_); +} diff --git a/src/fuse_mkdir_func.hpp b/src/fuse_mkdir_func.hpp new file mode 100644 index 00000000..73d48c54 --- /dev/null +++ b/src/fuse_mkdir_func.hpp @@ -0,0 +1,41 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::MKDIR +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask); + + private: + FuncBase::Ptr _mkdir; + }; +} diff --git a/src/fuse_mkdir_func_base.hpp b/src/fuse_mkdir_func_base.hpp new file mode 100644 index 00000000..2dfcaa9e --- /dev/null +++ b/src/fuse_mkdir_func_base.hpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse.h" + +#include + + +namespace FUSE::MKDIR +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask) = 0; + }; +} diff --git a/src/fuse_mkdir_func_epff.cpp b/src/fuse_mkdir_func_epff.cpp new file mode 100644 index 00000000..65c5dafc --- /dev/null +++ b/src/fuse_mkdir_func_epff.cpp @@ -0,0 +1,57 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_func_epff.hpp" +#include "fuse_mkdir_mkdir.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::MKDIR::FuncEPFF::FuncEPFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::MKDIR::FuncEPFF::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_) +{ + int rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = FUSE::MKDIR::mkdir(fullpath,mode_,umask_); + if(rv == -ENOENT) + continue; + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_mkdir_func_epff.hpp b/src/fuse_mkdir_func_epff.hpp new file mode 100644 index 00000000..90567beb --- /dev/null +++ b/src/fuse_mkdir_func_epff.hpp @@ -0,0 +1,41 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::MKDIR +{ + class FuncEPFF : public FuncBase + { + public: + FuncEPFF(const toml::value&); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_mkdir_func_factory.cpp b/src/fuse_mkdir_func_factory.cpp new file mode 100644 index 00000000..980916b6 --- /dev/null +++ b/src/fuse_mkdir_func_factory.cpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_func_factory.hpp" +#include "fuse_mkdir_func_ff.hpp" +#include "fuse_mkdir_func_epff.hpp" + +#include + +namespace FUSE::MKDIR +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","mkdir","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + if(str == "epff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_mkdir_func_factory.hpp b/src/fuse_mkdir_func_factory.hpp new file mode 100644 index 00000000..8d1a5d59 --- /dev/null +++ b/src/fuse_mkdir_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::MKDIR +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_mkdir_func_ff.cpp b/src/fuse_mkdir_func_ff.cpp new file mode 100644 index 00000000..85817050 --- /dev/null +++ b/src/fuse_mkdir_func_ff.cpp @@ -0,0 +1,63 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_func_ff.hpp" +#include "fuse_mkdir_mkdir.hpp" + +#include "fs_clonepath_branches.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::MKDIR::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::MKDIR::FuncFF::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_) +{ + int rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = FUSE::MKDIR::mkdir(fullpath,mode_,umask_); + if(rv == -ENOENT) + { + rv = fs::clonepath_as_root(_branches,branch.path,fusepath); + if(rv >= 0) + rv = FUSE::MKDIR::mkdir(fullpath,mode_,umask_); + } + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_mkdir_func_ff.hpp b/src/fuse_mkdir_func_ff.hpp new file mode 100644 index 00000000..3cc51ec4 --- /dev/null +++ b/src/fuse_mkdir_func_ff.hpp @@ -0,0 +1,41 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::MKDIR +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_mkdir_mkdir.cpp b/src/fuse_mkdir_mkdir.cpp new file mode 100644 index 00000000..532415d1 --- /dev/null +++ b/src/fuse_mkdir_mkdir.cpp @@ -0,0 +1,44 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mkdir_mkdir.hpp" + +#include "fs_acl.hpp" +#include "fs_mkdir.hpp" + + +namespace gfs = ghc::filesystem; + + +int +FUSE::MKDIR::mkdir(const gfs::path &fullpath_, + const mode_t mode_, + const mode_t umask_) +{ + int rv; + mode_t mode; + + mode = mode_; + rv = fs::acl::dir_has_defaults(fullpath_); + if(rv == -ENOENT) + return -ENOENT; + if(rv < 0) + mode &= ~umask_; + + return fs::mkdir(fullpath_,mode); +} diff --git a/src/fuse_mkdir_mkdir.hpp b/src/fuse_mkdir_mkdir.hpp new file mode 100644 index 00000000..e5ddd1ab --- /dev/null +++ b/src/fuse_mkdir_mkdir.hpp @@ -0,0 +1,33 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "ghc/filesystem.hpp" + +#include +#include + + +namespace FUSE::MKDIR +{ + int + mkdir(const ghc::filesystem::path &fullpath, + const mode_t mode, + const mode_t umask); +} diff --git a/src/fuse_mknod.cpp b/src/fuse_mknod.cpp index ad7b4f46..ba8ce0ae 100644 --- a/src/fuse_mknod.cpp +++ b/src/fuse_mknod.cpp @@ -17,9 +17,10 @@ #include "config.hpp" #include "errno.hpp" #include "fs_acl.hpp" -#include "fs_mknod.hpp" #include "fs_clonepath.hpp" +#include "fs_mknod.hpp" #include "fs_path.hpp" +#include "state.hpp" #include "ugid.hpp" #include "fuse.h" @@ -145,23 +146,17 @@ namespace l } } -namespace FUSE +namespace FUSE::MKNOD { int mknod(const char *fusepath_, mode_t mode_, dev_t rdev_) { - Config::Read cfg; + State s; 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_); + return s->mknod(fusepath_,mode_,fc->umask,rdev_); } } diff --git a/src/fuse_mknod.hpp b/src/fuse_mknod.hpp index 6350f310..9842b4c4 100644 --- a/src/fuse_mknod.hpp +++ b/src/fuse_mknod.hpp @@ -18,7 +18,7 @@ #pragma once -namespace FUSE +namespace FUSE::MKNOD { int mknod(const char *fusepath, diff --git a/src/fuse_mknod_func.cpp b/src/fuse_mknod_func.cpp new file mode 100644 index 00000000..69dbd477 --- /dev/null +++ b/src/fuse_mknod_func.cpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mknod_func.hpp" +#include "fuse_mknod_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::MKNOD::Func::Func(const toml::value &toml_) +{ + _mknod = FuncFactory(toml_); +} + +int +FUSE::MKNOD::Func::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_, + const dev_t dev_) +{ + return (*_mknod)(fusepath_,mode_,umask_,dev_); +} diff --git a/src/fuse_mknod_func.hpp b/src/fuse_mknod_func.hpp new file mode 100644 index 00000000..3f66eac2 --- /dev/null +++ b/src/fuse_mknod_func.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mknod_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::MKNOD +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask, + const dev_t dev); + + private: + FuncBase::Ptr _mknod; + }; +} diff --git a/src/fuse_mknod_func_base.hpp b/src/fuse_mknod_func_base.hpp new file mode 100644 index 00000000..610b6384 --- /dev/null +++ b/src/fuse_mknod_func_base.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +#include +#include +#include +#include + + +namespace FUSE::MKNOD +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask, + const dev_t dev) = 0; + }; +} diff --git a/src/fuse_mknod_func_epff.cpp b/src/fuse_mknod_func_epff.cpp new file mode 100644 index 00000000..cac3f60c --- /dev/null +++ b/src/fuse_mknod_func_epff.cpp @@ -0,0 +1,69 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mknod_func_epff.hpp" + +#include "fileinfo.hpp" +#include "fs_acl.hpp" +#include "fs_mknod.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::MKNOD::FuncEPFF::FuncEPFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::MKNOD::FuncEPFF::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_, + const dev_t dev_) +{ + int rv; + mode_t mode; + gfs::path fusepath; + gfs::path fullpath; + + mode = mode_; + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::acl::dir_has_defaults(fullpath); + if(rv == -ENOENT) + continue; + if(rv >= 0) + mode &= umask_; + + rv = fs::mknod(fullpath,mode,dev_); + if(rv == -ENOENT) + continue; + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_mknod_func_epff.hpp b/src/fuse_mknod_func_epff.hpp new file mode 100644 index 00000000..d11e2682 --- /dev/null +++ b/src/fuse_mknod_func_epff.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mknod_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::MKNOD +{ + class FuncEPFF : public FuncBase + { + public: + FuncEPFF(const toml::value&); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask, + const dev_t dev) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_mknod_func_factory.cpp b/src/fuse_mknod_func_factory.cpp new file mode 100644 index 00000000..e8add84d --- /dev/null +++ b/src/fuse_mknod_func_factory.cpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mknod_func_factory.hpp" +#include "fuse_mknod_func_ff.hpp" +#include "fuse_mknod_func_epff.hpp" + +#include + +namespace FUSE::MKNOD +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","mknod","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + if(str == "epff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_mknod_func_factory.hpp b/src/fuse_mknod_func_factory.hpp new file mode 100644 index 00000000..f96db87f --- /dev/null +++ b/src/fuse_mknod_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mknod_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::MKNOD +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_mknod_func_ff.cpp b/src/fuse_mknod_func_ff.cpp new file mode 100644 index 00000000..06683f97 --- /dev/null +++ b/src/fuse_mknod_func_ff.cpp @@ -0,0 +1,86 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mknod_func_ff.hpp" + +#include "fileinfo.hpp" +#include "fs_acl.hpp" +#include "fs_clonepath.hpp" +#include "fs_clonepath_branches.hpp" +#include "fs_mknod.hpp" +#include "ugid.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::MKNOD::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::MKNOD::FuncFF::operator()(const char *fusepath_, + const mode_t mode_, + const mode_t umask_, + const dev_t dev_) +{ + int rv; + mode_t mode; + gfs::path fusepath; + gfs::path fullpath; + + mode = mode_; + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::acl::dir_has_defaults(fullpath); + if(rv == -ENOENT) + { + rv = fs::clonepath_as_root(_branches,branch.path,fusepath); + if(rv >= 0) + rv = fs::acl::dir_has_defaults(fullpath); + } + + if(rv >= 0) + mode &= ~umask_; + + rv = fs::mknod(fullpath,mode,dev_); + if(rv == -ENOENT) + { + rv = fs::clonepath_as_root(_branches,branch.path,fusepath); + if(rv >= 0) + { + rv = fs::acl::dir_has_defaults(fullpath); + if(rv >= 0) + mode &= ~umask_; + rv = fs::mknod(fullpath,mode,dev_); + } + } + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_mknod_func_ff.hpp b/src/fuse_mknod_func_ff.hpp new file mode 100644 index 00000000..84114c32 --- /dev/null +++ b/src/fuse_mknod_func_ff.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_mknod_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::MKNOD +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *fusepath, + const mode_t mode, + const mode_t umask, + const dev_t dev) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_open.cpp b/src/fuse_open.cpp index 2e86fe5a..d3170da6 100644 --- a/src/fuse_open.cpp +++ b/src/fuse_open.cpp @@ -25,6 +25,7 @@ #include "fs_stat.hpp" #include "stat_util.hpp" #include "ugid.hpp" +#include "state.hpp" #include "fuse.h" @@ -199,11 +200,11 @@ namespace l } } -namespace FUSE +namespace FUSE::OPEN { int - open(const char *fusepath_, - fuse_file_info_t *ffi_) + open_old(const char *fusepath_, + fuse_file_info_t *ffi_) { Config::Read cfg; const fuse_context *fc = fuse_get_context(); @@ -222,4 +223,13 @@ namespace FUSE cfg->nfsopenhack, &ffi_->fh); } + + int + open(const char *fusepath_, + fuse_file_info_t *ffi_) + { + State s; + + return s->open(fusepath_,ffi_); + } } diff --git a/src/fuse_open.hpp b/src/fuse_open.hpp index 34c758bc..b25e5e17 100644 --- a/src/fuse_open.hpp +++ b/src/fuse_open.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::OPEN { int open(const char *fusepath, diff --git a/src/fuse_open_func.cpp b/src/fuse_open_func.cpp new file mode 100644 index 00000000..02e661cc --- /dev/null +++ b/src/fuse_open_func.cpp @@ -0,0 +1,41 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_open_func.hpp" +#include "fuse_open_func_factory.hpp" + +#include "state.hpp" + + +FUSE::OPEN::Func::Func(const toml::value &toml_) +{ + _open = FuncFactory(toml_); +} + +void +FUSE::OPEN::Func::operator=(const toml::value &toml_) +{ + _open = FuncFactory(toml_); +} + +int +FUSE::OPEN::Func::operator()(const char *fusepath_, + fuse_file_info_t *ffi_) +{ + return (*_open)(fusepath_,ffi_); +} diff --git a/src/fuse_open_func.hpp b/src/fuse_open_func.hpp new file mode 100644 index 00000000..12fc52ee --- /dev/null +++ b/src/fuse_open_func.hpp @@ -0,0 +1,47 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_open_func_base.hpp" + +#include "fuse.h" + +#include "toml.hpp" + +#include + + +namespace FUSE::OPEN +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + fuse_file_info_t *ffi); + + + void operator=(const toml::value&); + + private: + FuncBase::Ptr _open; + }; +} diff --git a/src/fuse_open_func_base.hpp b/src/fuse_open_func_base.hpp new file mode 100644 index 00000000..bdcb8d8a --- /dev/null +++ b/src/fuse_open_func_base.hpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse.h" + +#include + + +namespace FUSE::OPEN +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + fuse_file_info_t *ffi) = 0; + }; +} diff --git a/src/fuse_open_func_factory.cpp b/src/fuse_open_func_factory.cpp new file mode 100644 index 00000000..be923860 --- /dev/null +++ b/src/fuse_open_func_factory.cpp @@ -0,0 +1,39 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_open_func_factory.hpp" + +#include "fuse_open_func_ff.hpp" + +#include + + +namespace FUSE::OPEN +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","open","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_open_func_factory.hpp b/src/fuse_open_func_factory.hpp new file mode 100644 index 00000000..4a4d8cff --- /dev/null +++ b/src/fuse_open_func_factory.hpp @@ -0,0 +1,29 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_open_func_base.hpp" + +#include "toml.hpp" + +namespace FUSE::OPEN +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_open_func_ff.cpp b/src/fuse_open_func_ff.cpp new file mode 100644 index 00000000..463a7710 --- /dev/null +++ b/src/fuse_open_func_ff.cpp @@ -0,0 +1,62 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_open_func_ff.hpp" + +#include "fs_open.hpp" +#include "fileinfo.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::OPEN::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::OPEN::FuncFF::operator()(const char *fusepath_, + fuse_file_info_t *ffi_) +{ + int rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::open(fullpath,ffi_->flags); + if(rv == -ENOENT) + continue; + if(rv < 0) + return rv; + + ffi_->fh = reinterpret_cast(new FileInfo(rv,fusepath_)); + + return 0; + } + } + + return -ENOENT; +} diff --git a/src/fuse_open_func_ff.hpp b/src/fuse_open_func_ff.hpp new file mode 100644 index 00000000..0edaca7f --- /dev/null +++ b/src/fuse_open_func_ff.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_open_func.hpp" + +#include "branches.hpp" + +#include "toml.hpp" + + +namespace FUSE::OPEN +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *fusepath, + fuse_file_info_t *ffi) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_opendir.cpp b/src/fuse_opendir.cpp index 43c706ae..6cae92a1 100644 --- a/src/fuse_opendir.cpp +++ b/src/fuse_opendir.cpp @@ -20,7 +20,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::OPENDIR { int opendir(const char *fusepath_, diff --git a/src/fuse_opendir.hpp b/src/fuse_opendir.hpp index 1a2dca84..4507a43d 100644 --- a/src/fuse_opendir.hpp +++ b/src/fuse_opendir.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::OPENDIR { int opendir(const char *fusepath, diff --git a/src/fuse_poll.cpp b/src/fuse_poll.cpp index ad5b0a64..1fd8618a 100644 --- a/src/fuse_poll.cpp +++ b/src/fuse_poll.cpp @@ -21,7 +21,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::POLL { int poll(const fuse_file_info_t *ffi_, diff --git a/src/fuse_poll.hpp b/src/fuse_poll.hpp index 37c1be2d..15e944e9 100644 --- a/src/fuse_poll.hpp +++ b/src/fuse_poll.hpp @@ -21,7 +21,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::POLL { int poll(const fuse_file_info_t *ffi, diff --git a/src/fuse_prepare_hide.cpp b/src/fuse_prepare_hide.cpp index fbd741ea..26ed39b1 100644 --- a/src/fuse_prepare_hide.cpp +++ b/src/fuse_prepare_hide.cpp @@ -23,7 +23,7 @@ #include -namespace FUSE +namespace FUSE::PREPARE_HIDE { int prepare_hide(const char *fusepath_, @@ -33,7 +33,7 @@ namespace FUSE fuse_file_info_t ffi = {0}; ffi.flags = O_RDONLY|O_NOFOLLOW; - rv = FUSE::open(fusepath_,&ffi); + rv = FUSE::OPEN::open(fusepath_,&ffi); if(rv < 0) return rv; diff --git a/src/fuse_prepare_hide.hpp b/src/fuse_prepare_hide.hpp index 9e38760b..a0c1501f 100644 --- a/src/fuse_prepare_hide.hpp +++ b/src/fuse_prepare_hide.hpp @@ -21,7 +21,7 @@ #include -namespace FUSE +namespace FUSE::PREPARE_HIDE { int prepare_hide(const char *name, diff --git a/src/fuse_read_buf.cpp b/src/fuse_read_buf.cpp index 08a781b8..b86d806d 100644 --- a/src/fuse_read_buf.cpp +++ b/src/fuse_read_buf.cpp @@ -52,7 +52,7 @@ namespace l } } -namespace FUSE +namespace FUSE::READ_BUF { int read_buf(const fuse_file_info_t *ffi_, diff --git a/src/fuse_read_buf.hpp b/src/fuse_read_buf.hpp index cbd07cb4..cadabbd6 100644 --- a/src/fuse_read_buf.hpp +++ b/src/fuse_read_buf.hpp @@ -21,7 +21,7 @@ #include -namespace FUSE +namespace FUSE::READ_BUF { int read_buf(const fuse_file_info_t *ffi, diff --git a/src/fuse_readdir.cpp b/src/fuse_readdir.cpp index 2ef0b3e4..61b007b0 100644 --- a/src/fuse_readdir.cpp +++ b/src/fuse_readdir.cpp @@ -27,7 +27,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::READDIR { int readdir(const fuse_file_info_t *ffi_, @@ -41,10 +41,10 @@ namespace FUSE switch(cfg->readdir) { case ReadDir::ENUM::LINUX: - return FUSE::readdir_linux(cfg->branches,di->fusepath.c_str(),buf_); + return FUSE::READDIR_LINUX::readdir_linux(cfg->branches,di->fusepath.c_str(),buf_); default: case ReadDir::ENUM::POSIX: - return FUSE::readdir_posix(cfg->branches,di->fusepath.c_str(),buf_); + return FUSE::READDIR_POSIX::readdir_posix(cfg->branches,di->fusepath.c_str(),buf_); } } } diff --git a/src/fuse_readdir.hpp b/src/fuse_readdir.hpp index c82937c3..c075393a 100644 --- a/src/fuse_readdir.hpp +++ b/src/fuse_readdir.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::READDIR { int readdir(const fuse_file_info_t *ffi, diff --git a/src/fuse_readdir_linux.cpp b/src/fuse_readdir_linux.cpp index 6f857b1b..dfd49a10 100644 --- a/src/fuse_readdir_linux.cpp +++ b/src/fuse_readdir_linux.cpp @@ -123,7 +123,7 @@ namespace l } } -namespace FUSE +namespace FUSE::READDIR_LINUX { int readdir_linux(const Branches::CPtr &branches_, diff --git a/src/fuse_readdir_linux.hpp b/src/fuse_readdir_linux.hpp index 26cfff6a..637c9578 100644 --- a/src/fuse_readdir_linux.hpp +++ b/src/fuse_readdir_linux.hpp @@ -25,7 +25,7 @@ #include -namespace FUSE +namespace FUSE::READDIR_LINUX { int readdir_linux(const Branches::CPtr &branches, diff --git a/src/fuse_readdir_plus.cpp b/src/fuse_readdir_plus.cpp index e9870fa2..7363bf18 100644 --- a/src/fuse_readdir_plus.cpp +++ b/src/fuse_readdir_plus.cpp @@ -27,7 +27,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::READDIR_PLUS { int readdir_plus(const fuse_file_info_t *ffi_, @@ -41,18 +41,18 @@ namespace FUSE switch(cfg->readdir) { case ReadDir::ENUM::LINUX: - return FUSE::readdir_plus_linux(cfg->branches, - di->fusepath.c_str(), - cfg->cache_entry, - cfg->cache_attr, - buf_); + return FUSE::READDIR_PLUS_LINUX::readdir_plus_linux(cfg->branches, + di->fusepath.c_str(), + cfg->cache_entry, + cfg->cache_attr, + buf_); default: case ReadDir::ENUM::POSIX: - return FUSE::readdir_plus_posix(cfg->branches, - di->fusepath.c_str(), - cfg->cache_entry, - cfg->cache_attr, - buf_); + return FUSE::READDIR_PLUS_POSIX::readdir_plus_posix(cfg->branches, + di->fusepath.c_str(), + cfg->cache_entry, + cfg->cache_attr, + buf_); } } } diff --git a/src/fuse_readdir_plus.hpp b/src/fuse_readdir_plus.hpp index 8e298e61..83cee2b1 100644 --- a/src/fuse_readdir_plus.hpp +++ b/src/fuse_readdir_plus.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::READDIR_PLUS { int readdir_plus(const fuse_file_info_t *ffi, diff --git a/src/fuse_readdir_plus_linux.cpp b/src/fuse_readdir_plus_linux.cpp index 880f158b..222cd7a5 100644 --- a/src/fuse_readdir_plus_linux.cpp +++ b/src/fuse_readdir_plus_linux.cpp @@ -137,7 +137,7 @@ namespace l } } -namespace FUSE +namespace FUSE::READDIR_PLUS_LINUX { int readdir_plus_linux(const Branches::CPtr &branches_, diff --git a/src/fuse_readdir_plus_linux.hpp b/src/fuse_readdir_plus_linux.hpp index a0bca539..8fa270ab 100644 --- a/src/fuse_readdir_plus_linux.hpp +++ b/src/fuse_readdir_plus_linux.hpp @@ -25,7 +25,7 @@ #include -namespace FUSE +namespace FUSE::READDIR_PLUS_LINUX { int readdir_plus_linux(const Branches::CPtr &branches, diff --git a/src/fuse_readdir_plus_posix.cpp b/src/fuse_readdir_plus_posix.cpp index 0d974e6e..edd0213b 100644 --- a/src/fuse_readdir_plus_posix.cpp +++ b/src/fuse_readdir_plus_posix.cpp @@ -128,7 +128,7 @@ namespace l } } -namespace FUSE +namespace FUSE::READDIR_PLUS_POSIX { int readdir_plus_posix(const Branches::CPtr &branches_, diff --git a/src/fuse_readdir_plus_posix.hpp b/src/fuse_readdir_plus_posix.hpp index 910cab5f..8fa0e2b9 100644 --- a/src/fuse_readdir_plus_posix.hpp +++ b/src/fuse_readdir_plus_posix.hpp @@ -25,7 +25,7 @@ #include -namespace FUSE +namespace FUSE::READDIR_PLUS_POSIX { int readdir_plus_posix(const Branches::CPtr &branches, diff --git a/src/fuse_readdir_posix.cpp b/src/fuse_readdir_posix.cpp index 0e200073..95ee657d 100644 --- a/src/fuse_readdir_posix.cpp +++ b/src/fuse_readdir_posix.cpp @@ -112,7 +112,7 @@ namespace l } } -namespace FUSE +namespace FUSE::READDIR_POSIX { int readdir_posix(const Branches::CPtr &branches_, diff --git a/src/fuse_readdir_posix.hpp b/src/fuse_readdir_posix.hpp index 36d08dbb..55c9635c 100644 --- a/src/fuse_readdir_posix.hpp +++ b/src/fuse_readdir_posix.hpp @@ -25,7 +25,7 @@ #include -namespace FUSE +namespace FUSE::READDIR_POSIX { int readdir_posix(const Branches::CPtr &branches, diff --git a/src/fuse_readlink.cpp b/src/fuse_readlink.cpp index e24354f4..82b36730 100644 --- a/src/fuse_readlink.cpp +++ b/src/fuse_readlink.cpp @@ -21,6 +21,7 @@ #include "fs_readlink.hpp" #include "symlinkify.hpp" #include "ugid.hpp" +#include "state.hpp" #include "fuse.h" @@ -89,46 +90,19 @@ namespace l return l::readlink_core_standard(fullpath,buf_,size_); } - - static - int - readlink(const Policy::Search &searchFunc_, - const Branches &branches_, - const char *fusepath_, - char *buf_, - const size_t size_, - const bool symlinkify_, - const time_t symlinkify_timeout_) - { - int rv; - StrVec basepaths; - - rv = searchFunc_(branches_,fusepath_,&basepaths); - if(rv == -1) - return -errno; - - return l::readlink_core(basepaths[0],fusepath_,buf_,size_, - symlinkify_,symlinkify_timeout_); - } } -namespace FUSE +namespace FUSE::READLINK { int readlink(const char *fusepath_, char *buf_, - size_t size_) + size_t bufsiz_) { - Config::Read cfg; + State s; const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - return l::readlink(cfg->func.readlink.policy, - cfg->branches, - fusepath_, - buf_, - size_, - cfg->symlinkify, - cfg->symlinkify_timeout); + return s->readlink(fusepath_,buf_,bufsiz_); } } diff --git a/src/fuse_readlink.hpp b/src/fuse_readlink.hpp index b7e5e6f2..94aca718 100644 --- a/src/fuse_readlink.hpp +++ b/src/fuse_readlink.hpp @@ -17,10 +17,10 @@ #pragma once -namespace FUSE +namespace FUSE::READLINK { int readlink(const char *fusepath, char *buf, - size_t size); + size_t bufsiz); } diff --git a/src/fuse_readlink_func.cpp b/src/fuse_readlink_func.cpp new file mode 100644 index 00000000..e4d8465e --- /dev/null +++ b/src/fuse_readlink_func.cpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_readlink_func.hpp" +#include "fuse_readlink_func_factory.hpp" + +#include "state.hpp" + + +FUSE::READLINK::Func::Func(const toml::value &toml_) +{ + _readlink = FuncFactory(toml_); +} + +void +FUSE::READLINK::Func::operator=(const toml::value &toml_) +{ + _readlink = FuncFactory(toml_); +} + +int +FUSE::READLINK::Func::operator()(const char *fusepath_, + char *buf_, + const size_t bufsiz_) +{ + return (*_readlink)(fusepath_,buf_,bufsiz_); +} diff --git a/src/fuse_readlink_func.hpp b/src/fuse_readlink_func.hpp new file mode 100644 index 00000000..596084d1 --- /dev/null +++ b/src/fuse_readlink_func.hpp @@ -0,0 +1,47 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "from_toml.hpp" +#include "fuse_readlink_func_base.hpp" + +#include "fuse_timeouts.h" + +#include + +#include + +namespace FUSE::READLINK +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + char *buf_, + const size_t bufsiz_); + + void operator=(const toml::value&); + + private: + FuncBase::Ptr _readlink; + }; +} diff --git a/src/fuse_readlink_func_base.hpp b/src/fuse_readlink_func_base.hpp new file mode 100644 index 00000000..33f3e6a1 --- /dev/null +++ b/src/fuse_readlink_func_base.hpp @@ -0,0 +1,36 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + + +namespace FUSE::READLINK +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + char *buf, + const size_t bufsiz) = 0; + }; +} diff --git a/src/fuse_readlink_func_factory.cpp b/src/fuse_readlink_func_factory.cpp new file mode 100644 index 00000000..49830c6c --- /dev/null +++ b/src/fuse_readlink_func_factory.cpp @@ -0,0 +1,39 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_readlink_func_factory.hpp" + +#include "fuse_readlink_func_ff.hpp" + +#include + + +namespace FUSE::READLINK +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","readlink","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_readlink_func_factory.hpp b/src/fuse_readlink_func_factory.hpp new file mode 100644 index 00000000..75ca5360 --- /dev/null +++ b/src/fuse_readlink_func_factory.hpp @@ -0,0 +1,29 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_readlink_func_base.hpp" + +#include "toml.hpp" + +namespace FUSE::READLINK +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_readlink_func_ff.cpp b/src/fuse_readlink_func_ff.cpp new file mode 100644 index 00000000..8ee5ea89 --- /dev/null +++ b/src/fuse_readlink_func_ff.cpp @@ -0,0 +1,58 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_readlink_func_ff.hpp" + +#include "fs_readlink.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::READLINK::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::READLINK::FuncFF::operator()(const char *fusepath_, + char *buf_, + const size_t bufsiz_) +{ + int rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::readlink(fullpath,buf_,bufsiz_); + if(rv == -ENOENT) + continue; + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_readlink_func_ff.hpp b/src/fuse_readlink_func_ff.hpp new file mode 100644 index 00000000..f1dc48f2 --- /dev/null +++ b/src/fuse_readlink_func_ff.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_readlink_func.hpp" + +#include "branches.hpp" + +#include "toml.hpp" + +namespace FUSE::READLINK +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *fusepath, + char *buf, + const size_t bufsiz) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_release.cpp b/src/fuse_release.cpp index 03b6dcdb..33f9a478 100644 --- a/src/fuse_release.cpp +++ b/src/fuse_release.cpp @@ -48,8 +48,18 @@ namespace l } } -namespace FUSE +namespace FUSE::RELEASE { + int + config(const toml::value &toml_) + { + Config::Write cfg; + + cfg->dropcacheonclose = toml::find(toml_,"func","release","drop-cache"); + + return 0; + } + int release(const fuse_file_info_t *ffi_) { diff --git a/src/fuse_release.hpp b/src/fuse_release.hpp index 94858839..b9b90cdc 100644 --- a/src/fuse_release.hpp +++ b/src/fuse_release.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::RELEASE { int release(const fuse_file_info_t *ffi); diff --git a/src/fuse_releasedir.cpp b/src/fuse_releasedir.cpp index 8a7c15c5..e70c325b 100644 --- a/src/fuse_releasedir.cpp +++ b/src/fuse_releasedir.cpp @@ -32,7 +32,7 @@ namespace l } } -namespace FUSE +namespace FUSE::RELEASEDIR { int releasedir(const fuse_file_info_t *ffi_) diff --git a/src/fuse_releasedir.hpp b/src/fuse_releasedir.hpp index 28d278e0..870358c0 100644 --- a/src/fuse_releasedir.hpp +++ b/src/fuse_releasedir.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::RELEASEDIR { int releasedir(const fuse_file_info_t *ffi); diff --git a/src/fuse_removexattr.cpp b/src/fuse_removexattr.cpp index d22367ac..78a75215 100644 --- a/src/fuse_removexattr.cpp +++ b/src/fuse_removexattr.cpp @@ -19,6 +19,7 @@ #include "fs_lremovexattr.hpp" #include "fs_path.hpp" #include "policy_rv.hpp" +#include "state.hpp" #include "ugid.hpp" #include "fuse.h" @@ -113,17 +114,14 @@ namespace l } } -namespace FUSE +namespace FUSE::REMOVEXATTR { int - removexattr(const char *fusepath_, - const char *attrname_) + removexattr_old(const char *fusepath_, + const char *attrname_) { Config::Read cfg; - if(fusepath_ == CONTROLFILE) - return -ENOATTR; - if(cfg->xattr.to_int()) return -cfg->xattr.to_int(); @@ -136,4 +134,15 @@ namespace FUSE fusepath_, attrname_); } + + int + removexattr(const char *fusepath_, + const char *attrname_) + { + State s; + const fuse_context *fc = fuse_get_context(); + const ugid::Set ugid(fc->uid,fc->gid); + + return s->removexattr(fusepath_,attrname_); + } } diff --git a/src/fuse_removexattr.hpp b/src/fuse_removexattr.hpp index 4c7357f9..889328b8 100644 --- a/src/fuse_removexattr.hpp +++ b/src/fuse_removexattr.hpp @@ -17,7 +17,7 @@ #pragma once -namespace FUSE +namespace FUSE::REMOVEXATTR { int removexattr(const char *fusepath, diff --git a/src/fuse_removexattr_err.hpp b/src/fuse_removexattr_err.hpp new file mode 100644 index 00000000..6cd954fd --- /dev/null +++ b/src/fuse_removexattr_err.hpp @@ -0,0 +1,51 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +namespace FUSE::REMOVEXATTR +{ + class Err + { + public: + Err() + : _err(-ENOENT) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == -ENOENT) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_removexattr_func.cpp b/src/fuse_removexattr_func.cpp new file mode 100644 index 00000000..0115f1db --- /dev/null +++ b/src/fuse_removexattr_func.cpp @@ -0,0 +1,35 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_removexattr_func.hpp" +#include "fuse_removexattr_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::REMOVEXATTR::Func::Func(const toml::value &toml_) +{ + _removexattr = FuncFactory(toml_); +} + +int +FUSE::REMOVEXATTR::Func::operator()(const char *fusepath_, + const char *attrname_) +{ + return (*_removexattr)(fusepath_,attrname_); +} diff --git a/src/fuse_removexattr_func.hpp b/src/fuse_removexattr_func.hpp new file mode 100644 index 00000000..417cfac6 --- /dev/null +++ b/src/fuse_removexattr_func.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_removexattr_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::REMOVEXATTR +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + const char *attrname); + + private: + FuncBase::Ptr _removexattr; + }; +} diff --git a/src/fuse_removexattr_func_all.cpp b/src/fuse_removexattr_func_all.cpp new file mode 100644 index 00000000..e3662bdc --- /dev/null +++ b/src/fuse_removexattr_func_all.cpp @@ -0,0 +1,54 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_removexattr_func_all.hpp" +#include "fuse_removexattr_err.hpp" + +#include "fs_lremovexattr.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::REMOVEXATTR::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::REMOVEXATTR::FuncALL::operator()(const char *fusepath_, + const char *attrname_) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::lremovexattr(fullpath,attrname_); + } + } + + return rv; +} diff --git a/src/fuse_removexattr_func_all.hpp b/src/fuse_removexattr_func_all.hpp new file mode 100644 index 00000000..6dadcb5d --- /dev/null +++ b/src/fuse_removexattr_func_all.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_removexattr_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::REMOVEXATTR +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const char *fusepath, + const char *attrname) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_removexattr_func_base.hpp b/src/fuse_removexattr_func_base.hpp new file mode 100644 index 00000000..e2eda000 --- /dev/null +++ b/src/fuse_removexattr_func_base.hpp @@ -0,0 +1,36 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + + +namespace FUSE::REMOVEXATTR +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + const char *attrname) = 0; + }; +} diff --git a/src/fuse_removexattr_func_factory.cpp b/src/fuse_removexattr_func_factory.cpp new file mode 100644 index 00000000..e18daceb --- /dev/null +++ b/src/fuse_removexattr_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_removexattr_func_factory.hpp" +#include "fuse_removexattr_func_all.hpp" + +#include + + +namespace FUSE::REMOVEXATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","removexattr","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_removexattr_func_factory.hpp b/src/fuse_removexattr_func_factory.hpp new file mode 100644 index 00000000..83be21f9 --- /dev/null +++ b/src/fuse_removexattr_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_removexattr_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::REMOVEXATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_rename.cpp b/src/fuse_rename.cpp index ee2384e8..10c77450 100644 --- a/src/fuse_rename.cpp +++ b/src/fuse_rename.cpp @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Antonio SJ Musumeci + Copyright (c) 2022, Antonio SJ Musumeci Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -25,9 +25,10 @@ #include "fs_symlink.hpp" #include "fs_unlink.hpp" #include "fuse_symlink.hpp" +#include "state.hpp" #include "ugid.hpp" -#include "ghc/filesystem.hpp" +#include "fs_path.hpp" #include #include @@ -36,185 +37,9 @@ #include using std::string; -namespace gfs = ghc::filesystem; - -namespace error -{ - static - inline - int - calc(const int rv_, - const int prev_, - const int cur_) - { - if(rv_ == -1) - { - if(prev_ == 0) - return 0; - return cur_; - } - - return 0; - } -} namespace l { - static - bool - contains(const StrVec &haystack_, - const char *needle_) - { - for(auto &hay : haystack_) - { - if(hay == needle_) - return true; - } - - return false; - } - - static - bool - contains(const StrVec &haystack_, - const string &needle_) - { - return l::contains(haystack_,needle_.c_str()); - } - - static - void - remove(const StrVec &toremove_) - { - for(auto &path : toremove_) - fs::remove(path); - } - - static - void - remove(const Branches::CPtr &branches_, - const std::string &relpath_) - { - std::string fullpath; - - for(auto &branch : *branches_) - { - fullpath = fs::path::make(branch.path,relpath_); - fs::remove(fullpath); - } - } - - static - int - rename_create_path(const Policy::Search &searchPolicy_, - const Policy::Action &actionPolicy_, - const Branches::CPtr &branches_, - const gfs::path &oldfusepath_, - const gfs::path &newfusepath_) - { - int rv; - int error; - StrVec toremove; - StrVec newbasepath; - StrVec oldbasepaths; - gfs::path oldfullpath; - gfs::path newfullpath; - - rv = actionPolicy_(branches_,oldfusepath_,&oldbasepaths); - if(rv == -1) - return -errno; - - rv = searchPolicy_(branches_,newfusepath_.parent_path(),&newbasepath); - if(rv == -1) - return -errno; - - error = -1; - for(auto &branch : *branches_) - { - newfullpath = branch.path; - newfullpath += newfusepath_; - - if(!l::contains(oldbasepaths,branch.path)) - { - toremove.push_back(newfullpath); - continue; - } - - oldfullpath = branch.path; - oldfullpath += oldfusepath_; - - 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 = error::calc(rv,error,errno); - if(rv == -1) - toremove.push_back(oldfullpath); - } - - if(error == 0) - l::remove(toremove); - - return -error; - } - - 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; - - rv = actionPolicy_(branches_,oldfusepath_,&oldbasepaths); - if(rv == -1) - return -errno; - - success = false; - for(auto &branch : *branches_) - { - newfullpath = branch.path; - newfullpath += newfusepath_; - - if(!l::contains(oldbasepaths,branch.path)) - { - toremove.push_back(newfullpath); - continue; - } - - oldfullpath = branch.path; - oldfullpath += oldfusepath_; - - rv = fs::rename(oldfullpath,newfullpath); - if(rv == -1) - { - toremove.push_back(oldfullpath); - continue; - } - - success = true; - } - - // TODO: probably should try to be nuanced here. - if(success == false) - return -EXDEV; - - l::remove(toremove); - - return 0; - } - - static void rename_exdev_rename_back(const StrVec &basepaths_, const gfs::path &oldfusepath_) @@ -237,18 +62,16 @@ namespace l static int - rename_exdev_rename_target(const Policy::Action &actionPolicy_, - const Branches::CPtr &branches_, - const gfs::path &oldfusepath_, - StrVec *basepaths_) + rename_exdev_rename_target(const gfs::path &oldfusepath_, + StrVec *basepaths_) { int rv; gfs::path clonesrc; gfs::path clonetgt; - rv = actionPolicy_(branches_,oldfusepath_,basepaths_); - if(rv == -1) - return -errno; + // rv = actionPolicy_(branches_,oldfusepath_,basepaths_); + // if(rv == -1) + // return -errno; ugid::SetRootGuard ugidGuard; for(auto &basepath : *basepaths_) @@ -295,16 +118,16 @@ namespace l gfs::path target; gfs::path linkpath; - rv = l::rename_exdev_rename_target(actionPolicy_,branches_,oldfusepath_,&basepaths); - if(rv < 0) - return rv; + // 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()); + rv = FUSE::SYMLINK::symlink(target.c_str(),linkpath.c_str()); if(rv < 0) l::rename_exdev_rename_back(basepaths,oldfusepath_); @@ -313,27 +136,25 @@ namespace l static int - rename_exdev_abs_symlink(const Policy::Action &actionPolicy_, - const Branches::CPtr &branches_, - const std::string &mount_, - const gfs::path &oldfusepath_, - const gfs::path &newfusepath_) + rename_exdev_abs_symlink(const gfs::path &mountpoint_, + const gfs::path &oldfusepath_, + const gfs::path &newfusepath_) { int rv; StrVec basepaths; gfs::path target; gfs::path linkpath; - rv = l::rename_exdev_rename_target(actionPolicy_,branches_,oldfusepath_,&basepaths); - if(rv < 0) - return rv; + // rv = l::rename_exdev_rename_target(actionPolicy_,branches_,oldfusepath_,&basepaths); + // if(rv < 0) + // return rv; linkpath = newfusepath_; - target = mount_; + target = mountpoint_; target /= ".mergerfs_rename_exdev"; target += oldfusepath_; - rv = FUSE::symlink(target.c_str(),linkpath.c_str()); + rv = FUSE::SYMLINK::symlink(target.c_str(),linkpath.c_str()); if(rv < 0) l::rename_exdev_rename_back(basepaths,oldfusepath_); @@ -342,66 +163,42 @@ namespace l static int - rename_exdev(Config::Read &cfg_, + rename_exdev(State &s_, const gfs::path &oldfusepath_, const gfs::path &newfusepath_) { - switch(cfg_->rename_exdev) + switch(s_->rename_exdev) { - case RenameEXDEV::ENUM::PASSTHROUGH: + case RenameEXDEV::INVALID: + case RenameEXDEV::PASSTHROUGH: + return -EXDEV; + case RenameEXDEV::REL_SYMLINK: return -EXDEV; - case RenameEXDEV::ENUM::REL_SYMLINK: - return l::rename_exdev_rel_symlink(cfg_->func.rename.policy, - cfg_->branches, - oldfusepath_, - newfusepath_); - case RenameEXDEV::ENUM::ABS_SYMLINK: - return l::rename_exdev_abs_symlink(cfg_->func.rename.policy, - cfg_->branches, - cfg_->mount, - oldfusepath_, - newfusepath_); + // return l::rename_exdev_rel_symlink(oldfusepath_,newfusepath_); + case RenameEXDEV::ABS_SYMLINK: + return l::rename_exdev_abs_symlink(s_->mountpoint,oldfusepath_,newfusepath_); } return -EXDEV; } - - static - int - rename(Config::Read &cfg_, - const gfs::path &oldpath_, - const gfs::path &newpath_) - { - if(cfg_->func.create.policy.path_preserving() && !cfg_->ignorepponrename) - return l::rename_preserve_path(cfg_->func.rename.policy, - cfg_->branches, - oldpath_, - newpath_); - - return l::rename_create_path(cfg_->func.getattr.policy, - cfg_->func.rename.policy, - cfg_->branches, - oldpath_, - newpath_); - } } -namespace FUSE +namespace FUSE::RENAME { int rename(const char *oldfusepath_, const char *newfusepath_) { int rv; - Config::Read cfg; - gfs::path oldfusepath(oldfusepath_); - gfs::path newfusepath(newfusepath_); + State s; + gfs::path oldfusepath(&oldfusepath_[1]); + gfs::path newfusepath(&newfusepath_[1]); const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - rv = l::rename(cfg,oldfusepath,newfusepath); + rv = s->rename(oldfusepath_,newfusepath_); if(rv == -EXDEV) - return l::rename_exdev(cfg,oldfusepath,newfusepath); + return l::rename_exdev(s,oldfusepath,newfusepath); return rv; } diff --git a/src/fuse_rename.hpp b/src/fuse_rename.hpp index abbcf515..f0070ef8 100644 --- a/src/fuse_rename.hpp +++ b/src/fuse_rename.hpp @@ -17,7 +17,7 @@ #pragma once -namespace FUSE +namespace FUSE::RENAME { int rename(const char *from, diff --git a/src/fuse_rename_err.hpp b/src/fuse_rename_err.hpp new file mode 100644 index 00000000..9a967926 --- /dev/null +++ b/src/fuse_rename_err.hpp @@ -0,0 +1,51 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +namespace FUSE::RENAME +{ + class Err + { + public: + Err() + : _err(0) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == 0) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_rename_func.cpp b/src/fuse_rename_func.cpp new file mode 100644 index 00000000..5374a590 --- /dev/null +++ b/src/fuse_rename_func.cpp @@ -0,0 +1,28 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rename_func.hpp" +#include "fuse_rename_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::RENAME::Func::Func(const toml::value &toml_) +{ + _rename = FuncFactory(toml_); +} diff --git a/src/fuse_rename_func.hpp b/src/fuse_rename_func.hpp new file mode 100644 index 00000000..1fa3488f --- /dev/null +++ b/src/fuse_rename_func.hpp @@ -0,0 +1,45 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rename_func_base.hpp" + +#include "ghc/filesystem.hpp" +#include "toml.hpp" + + +namespace FUSE::RENAME +{ + class Func + { + public: + Func(const toml::value &); + + public: + int + operator()(const gfs::path &oldpath_, + const gfs::path &newpath_) + { + return (*_rename)(oldpath_,newpath_); + } + + private: + FuncBase::Ptr _rename; + }; +} diff --git a/src/fuse_rename_func_all.cpp b/src/fuse_rename_func_all.cpp new file mode 100644 index 00000000..b89e2188 --- /dev/null +++ b/src/fuse_rename_func_all.cpp @@ -0,0 +1,51 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rename_func_all.hpp" +#include "fuse_rename_err.hpp" + +#include "fs_rename.hpp" + +#include "fs_path.hpp" + + +FUSE::RENAME::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::RENAME::FuncALL::operator()(const gfs::path &oldpath_, + const gfs::path &newpath_) +{ + Err rv; + gfs::path fullpath; + + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / oldpath_; + + rv = 0; + } + } + + return rv; +} diff --git a/src/fuse_rename_func_all.hpp b/src/fuse_rename_func_all.hpp new file mode 100644 index 00000000..b02a3bd3 --- /dev/null +++ b/src/fuse_rename_func_all.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rename_func_base.hpp" + +#include "branches.hpp" + +#include "fs_path.hpp" + + +namespace FUSE::RENAME +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const gfs::path &oldpath, + const gfs::path &newpath) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_rename_func_base.hpp b/src/fuse_rename_func_base.hpp new file mode 100644 index 00000000..80a06904 --- /dev/null +++ b/src/fuse_rename_func_base.hpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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_path.hpp" + +#include + + +namespace FUSE::RENAME +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const gfs::path &oldpath, + const gfs::path &newpath) = 0; + }; +} diff --git a/src/fuse_rename_func_factory.cpp b/src/fuse_rename_func_factory.cpp new file mode 100644 index 00000000..a538cab9 --- /dev/null +++ b/src/fuse_rename_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rename_func_factory.hpp" +#include "fuse_rename_func_all.hpp" + +#include + + +namespace FUSE::RENAME +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","rename","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_rename_func_factory.hpp b/src/fuse_rename_func_factory.hpp new file mode 100644 index 00000000..2177e650 --- /dev/null +++ b/src/fuse_rename_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rename_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::RENAME +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_rmdir.cpp b/src/fuse_rmdir.cpp index cbfc83c6..8ae53670 100644 --- a/src/fuse_rmdir.cpp +++ b/src/fuse_rmdir.cpp @@ -19,6 +19,7 @@ #include "fs_path.hpp" #include "fs_rmdir.hpp" #include "fs_unlink.hpp" +#include "state.hpp" #include "ugid.hpp" #include "fuse.h" @@ -114,18 +115,15 @@ namespace l } } -namespace FUSE +namespace FUSE::RMDIR { int rmdir(const char *fusepath_) { - Config::Read cfg; + State s; const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - return l::rmdir(cfg->func.rmdir.policy, - cfg->branches, - cfg->follow_symlinks, - fusepath_); + return s->rmdir(fusepath_); } } diff --git a/src/fuse_rmdir.hpp b/src/fuse_rmdir.hpp index d2782468..553bf07f 100644 --- a/src/fuse_rmdir.hpp +++ b/src/fuse_rmdir.hpp @@ -17,7 +17,7 @@ #pragma once -namespace FUSE +namespace FUSE::RMDIR { int rmdir(const char *fusepath); diff --git a/src/fuse_rmdir_err.hpp b/src/fuse_rmdir_err.hpp new file mode 100644 index 00000000..8e9952ef --- /dev/null +++ b/src/fuse_rmdir_err.hpp @@ -0,0 +1,54 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "errno.hpp" + + +namespace FUSE::RMDIR +{ + class Err + { + public: + Err() + : _err(-ENOENT) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == -ENOENT) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_rmdir_func.cpp b/src/fuse_rmdir_func.cpp new file mode 100644 index 00000000..fe944329 --- /dev/null +++ b/src/fuse_rmdir_func.cpp @@ -0,0 +1,34 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rmdir_func.hpp" +#include "fuse_rmdir_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::RMDIR::Func::Func(const toml::value &toml_) +{ + _rmdir = FuncFactory(toml_); +} + +int +FUSE::RMDIR::Func::operator()(const char *fusepath_) +{ + return (*_rmdir)(fusepath_); +} diff --git a/src/fuse_rmdir_func.hpp b/src/fuse_rmdir_func.hpp new file mode 100644 index 00000000..30f7498b --- /dev/null +++ b/src/fuse_rmdir_func.hpp @@ -0,0 +1,39 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rmdir_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::RMDIR +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath); + + private: + FuncBase::Ptr _rmdir; + }; +} diff --git a/src/fuse_rmdir_func_all.cpp b/src/fuse_rmdir_func_all.cpp new file mode 100644 index 00000000..940ccff9 --- /dev/null +++ b/src/fuse_rmdir_func_all.cpp @@ -0,0 +1,53 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rmdir_func_all.hpp" +#include "fuse_rmdir_err.hpp" + +#include "fs_rmdir.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::RMDIR::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::RMDIR::FuncALL::operator()(const char *fusepath_) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::rmdir(fullpath); + } + } + + return rv; +} diff --git a/src/fuse_rmdir_func_all.hpp b/src/fuse_rmdir_func_all.hpp new file mode 100644 index 00000000..133ab9ee --- /dev/null +++ b/src/fuse_rmdir_func_all.hpp @@ -0,0 +1,39 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rmdir_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::RMDIR +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const char *fusepath) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_rmdir_func_base.hpp b/src/fuse_rmdir_func_base.hpp new file mode 100644 index 00000000..b1ab5328 --- /dev/null +++ b/src/fuse_rmdir_func_base.hpp @@ -0,0 +1,35 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + + +namespace FUSE::RMDIR +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath) = 0; + }; +} diff --git a/src/fuse_rmdir_func_factory.cpp b/src/fuse_rmdir_func_factory.cpp new file mode 100644 index 00000000..51ff6513 --- /dev/null +++ b/src/fuse_rmdir_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rmdir_func_factory.hpp" +#include "fuse_rmdir_func_all.hpp" + +#include + + +namespace FUSE::RMDIR +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","rmdir","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_rmdir_func_factory.hpp b/src/fuse_rmdir_func_factory.hpp new file mode 100644 index 00000000..2d42a292 --- /dev/null +++ b/src/fuse_rmdir_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_rmdir_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::RMDIR +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_setxattr.cpp b/src/fuse_setxattr.cpp index c46bc7c1..082cbba6 100644 --- a/src/fuse_setxattr.cpp +++ b/src/fuse_setxattr.cpp @@ -22,6 +22,7 @@ #include "fs_statvfs_cache.hpp" #include "num.hpp" #include "policy_rv.hpp" +#include "state.hpp" #include "str.hpp" #include "ugid.hpp" @@ -67,36 +68,6 @@ namespace l return (strcmp(attrname_,SECURITY_CAPABILITY) == 0); } - static - int - setxattr_controlfile(const string &attrname_, - const string &attrval_, - const int flags_) - { - int rv; - string key; - Config::Write cfg; - - if(!str::startswith(attrname_,"user.mergerfs.")) - return -ENOATTR; - - key = &attrname_[14]; - - if(cfg->has_key(key) == false) - return -ENOATTR; - - if((flags_ & XATTR_CREATE) == XATTR_CREATE) - return -EEXIST; - - rv = cfg->set(key,attrval_); - if(rv < 0) - return rv; - - fs::statvfs_cache_timeout(cfg->cache_statfs); - - return rv; - } - static void setxattr_loop_core(const string &basepath_, @@ -198,8 +169,18 @@ namespace l } } -namespace FUSE +namespace FUSE::SETXATTR { + int + setxattr_old(const char *fusepath_, + const char *attrname_, + const char *attrval_, + size_t attrvalsize_, + int flags_) + { + return l::setxattr(fusepath_,attrname_,attrval_,attrvalsize_,flags_); + } + int setxattr(const char *fusepath_, const char *attrname_, @@ -207,11 +188,12 @@ namespace FUSE size_t attrvalsize_, int flags_) { - if(fusepath_ == CONTROLFILE) - return l::setxattr_controlfile(attrname_, - string(attrval_,attrvalsize_), - flags_); + State s; - return l::setxattr(fusepath_,attrname_,attrval_,attrvalsize_,flags_); + return s->setxattr(fusepath_, + attrname_, + attrval_, + attrvalsize_, + flags_); } } diff --git a/src/fuse_setxattr.hpp b/src/fuse_setxattr.hpp index 6ae1f52e..7c139a0c 100644 --- a/src/fuse_setxattr.hpp +++ b/src/fuse_setxattr.hpp @@ -17,7 +17,7 @@ #pragma once -namespace FUSE +namespace FUSE::SETXATTR { int setxattr(const char *fusepath, diff --git a/src/fuse_setxattr_err.hpp b/src/fuse_setxattr_err.hpp new file mode 100644 index 00000000..e9512927 --- /dev/null +++ b/src/fuse_setxattr_err.hpp @@ -0,0 +1,54 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "errno.hpp" + + +namespace FUSE::SETXATTR +{ + class Err + { + public: + Err() + : _err(-ENOENT) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == -ENOENT) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_setxattr_func.cpp b/src/fuse_setxattr_func.cpp new file mode 100644 index 00000000..f7412794 --- /dev/null +++ b/src/fuse_setxattr_func.cpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_setxattr_func.hpp" +#include "fuse_setxattr_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::SETXATTR::Func::Func(const toml::value &toml_) +{ + _setxattr = FuncFactory(toml_); +} + +int +FUSE::SETXATTR::Func::operator()(const char *fusepath_, + const char *attrname_, + const char *attrval_, + const size_t attrvalsize_, + const int flags_) +{ + return (*_setxattr)(fusepath_, + attrname_, + attrval_, + attrvalsize_, + flags_); +} diff --git a/src/fuse_setxattr_func.hpp b/src/fuse_setxattr_func.hpp new file mode 100644 index 00000000..54f6e48f --- /dev/null +++ b/src/fuse_setxattr_func.hpp @@ -0,0 +1,43 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_setxattr_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::SETXATTR +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + const char *attrname, + const char *attrval, + const size_t attrvalsize, + const int flags); + + private: + FuncBase::Ptr _setxattr; + }; +} diff --git a/src/fuse_setxattr_func_all.cpp b/src/fuse_setxattr_func_all.cpp new file mode 100644 index 00000000..8fb12055 --- /dev/null +++ b/src/fuse_setxattr_func_all.cpp @@ -0,0 +1,61 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_setxattr_func_all.hpp" +#include "fuse_setxattr_err.hpp" + +#include "fs_lsetxattr.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::SETXATTR::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::SETXATTR::FuncALL::operator()(const char *fusepath_, + const char *attrname_, + const char *attrval_, + const size_t attrvalsize_, + const int flags_) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::lsetxattr(fullpath, + attrname_, + attrval_, + attrvalsize_, + flags_); + } + } + + return rv; +} diff --git a/src/fuse_setxattr_func_all.hpp b/src/fuse_setxattr_func_all.hpp new file mode 100644 index 00000000..e97a8822 --- /dev/null +++ b/src/fuse_setxattr_func_all.hpp @@ -0,0 +1,43 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_setxattr_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::SETXATTR +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const char *fusepath, + const char *attrname, + const char *attrval, + const size_t attrvalsize, + const int flags) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_setxattr_func_base.hpp b/src/fuse_setxattr_func_base.hpp new file mode 100644 index 00000000..70a2b9bc --- /dev/null +++ b/src/fuse_setxattr_func_base.hpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + + +namespace FUSE::SETXATTR +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + const char *attrname, + const char *attrval, + const size_t attrvalsize, + const int flags) = 0; + }; +} diff --git a/src/fuse_setxattr_func_factory.cpp b/src/fuse_setxattr_func_factory.cpp new file mode 100644 index 00000000..cd16fdf4 --- /dev/null +++ b/src/fuse_setxattr_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_setxattr_func_factory.hpp" +#include "fuse_setxattr_func_all.hpp" + +#include + + +namespace FUSE::SETXATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","setxattr","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_setxattr_func_factory.hpp b/src/fuse_setxattr_func_factory.hpp new file mode 100644 index 00000000..a5317836 --- /dev/null +++ b/src/fuse_setxattr_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_setxattr_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::SETXATTR +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_statfs.cpp b/src/fuse_statfs.cpp index 68ffb2cf..68e96fe5 100644 --- a/src/fuse_statfs.cpp +++ b/src/fuse_statfs.cpp @@ -145,8 +145,23 @@ namespace l } } -namespace FUSE +namespace FUSE::STATFS { + int + config(const toml::value &toml_) + { + std::string val; + Config::Write cfg; + + val = toml::find(toml_,"func","statfs","policy"); + cfg->statfs.from_string(val); + + val = toml::find(toml_,"func","statfs","ignore"); + cfg->statfs_ignore.from_string(val); + + return 0; + } + int statfs(const char *fusepath_, struct statvfs *st_) diff --git a/src/fuse_statfs.hpp b/src/fuse_statfs.hpp index c34ac43a..ba901ee9 100644 --- a/src/fuse_statfs.hpp +++ b/src/fuse_statfs.hpp @@ -16,11 +16,16 @@ #pragma once +#include "toml.hpp" + #include -namespace FUSE +namespace FUSE::STATFS { + int + config(const toml::value &); + int statfs(const char *fusepath, struct statvfs *fsstat); diff --git a/src/fuse_symlink.cpp b/src/fuse_symlink.cpp index e413f6b6..344e44e4 100644 --- a/src/fuse_symlink.cpp +++ b/src/fuse_symlink.cpp @@ -23,6 +23,7 @@ #include "fs_symlink.hpp" #include "fuse_getattr.hpp" #include "ugid.hpp" +#include "state.hpp" #include "fuse.h" @@ -138,7 +139,7 @@ namespace l } } -namespace FUSE +namespace FUSE::SYMLINK { int symlink(const char *target_, diff --git a/src/fuse_symlink.hpp b/src/fuse_symlink.hpp index ba568633..d00232ec 100644 --- a/src/fuse_symlink.hpp +++ b/src/fuse_symlink.hpp @@ -18,9 +18,11 @@ #include "fuse.h" +#include "toml.hpp" + #include -namespace FUSE +namespace FUSE::SYMLINK { int symlink(const char *target, diff --git a/src/fuse_symlink_func.cpp b/src/fuse_symlink_func.cpp new file mode 100644 index 00000000..c0da6ace --- /dev/null +++ b/src/fuse_symlink_func.cpp @@ -0,0 +1,35 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_symlink_func.hpp" +#include "fuse_symlink_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::SYMLINK::Func::Func(const toml::value &toml_) +{ + _symlink = FuncFactory(toml_); +} + +int +FUSE::SYMLINK::Func::operator()(const char *target_, + const char *linkpath_) +{ + return (*_symlink)(target_,linkpath_); +} diff --git a/src/fuse_symlink_func.hpp b/src/fuse_symlink_func.hpp new file mode 100644 index 00000000..68766d26 --- /dev/null +++ b/src/fuse_symlink_func.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_symlink_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::SYMLINK +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *target, + const char *linkpath); + + private: + FuncBase::Ptr _symlink; + }; +} diff --git a/src/fuse_symlink_func_base.hpp b/src/fuse_symlink_func_base.hpp new file mode 100644 index 00000000..f82ea2d8 --- /dev/null +++ b/src/fuse_symlink_func_base.hpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse.h" + +#include + + +namespace FUSE::SYMLINK +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *target, + const char *linkpath) = 0; + }; +} diff --git a/src/fuse_symlink_func_epff.cpp b/src/fuse_symlink_func_epff.cpp new file mode 100644 index 00000000..9f605e8a --- /dev/null +++ b/src/fuse_symlink_func_epff.cpp @@ -0,0 +1,57 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_symlink_func_epff.hpp" + +#include "fs_symlink.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::SYMLINK::FuncEPFF::FuncEPFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::SYMLINK::FuncEPFF::operator()(const char *target_, + const char *linkpath_) +{ + int rv; + gfs::path linkpath; + gfs::path fullpath; + + linkpath = &linkpath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / linkpath; + + rv = fs::symlink(target_,fullpath); + if(rv == -ENOENT) + continue; + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_symlink_func_epff.hpp b/src/fuse_symlink_func_epff.hpp new file mode 100644 index 00000000..0e06c89c --- /dev/null +++ b/src/fuse_symlink_func_epff.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_symlink_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::SYMLINK +{ + class FuncEPFF : public FuncBase + { + public: + FuncEPFF(const toml::value&); + + public: + int operator()(const char *target, + const char *linkpath) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_symlink_func_factory.cpp b/src/fuse_symlink_func_factory.cpp new file mode 100644 index 00000000..da4057af --- /dev/null +++ b/src/fuse_symlink_func_factory.cpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_symlink_func_factory.hpp" +#include "fuse_symlink_func_ff.hpp" +#include "fuse_symlink_func_epff.hpp" + +#include + +namespace FUSE::SYMLINK +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","symlink","policy","ff"); + if(str == "ff") + return std::make_shared(toml_); + if(str == "epff") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_symlink_func_factory.hpp b/src/fuse_symlink_func_factory.hpp new file mode 100644 index 00000000..903598e8 --- /dev/null +++ b/src/fuse_symlink_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_symlink_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::SYMLINK +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_symlink_func_ff.cpp b/src/fuse_symlink_func_ff.cpp new file mode 100644 index 00000000..b62c70f8 --- /dev/null +++ b/src/fuse_symlink_func_ff.cpp @@ -0,0 +1,64 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_symlink_func_ff.hpp" + +#include "fs_clonepath.hpp" +#include "fs_clonepath_branches.hpp" +#include "fs_symlink.hpp" +#include "ugid.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::SYMLINK::FuncFF::FuncFF(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::SYMLINK::FuncFF::operator()(const char *target_, + const char *linkpath_) +{ + int rv; + gfs::path linkpath; + gfs::path fullpath; + + linkpath = &linkpath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / linkpath; + + rv = fs::symlink(target_,fullpath); + if(rv == -ENOENT) + { + rv = fs::clonepath_as_root(_branches,branch.path,linkpath_); + if(rv >= 0) + rv = fs::symlink(target_,fullpath); + } + + return rv; + } + } + + return -ENOENT; +} diff --git a/src/fuse_symlink_func_ff.hpp b/src/fuse_symlink_func_ff.hpp new file mode 100644 index 00000000..f33730a2 --- /dev/null +++ b/src/fuse_symlink_func_ff.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_symlink_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::SYMLINK +{ + class FuncFF : public FuncBase + { + public: + FuncFF(const toml::value&); + + public: + int operator()(const char *target, + const char *linkpath) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_truncate.cpp b/src/fuse_truncate.cpp index 669715a8..61aecd5a 100644 --- a/src/fuse_truncate.cpp +++ b/src/fuse_truncate.cpp @@ -20,9 +20,12 @@ #include "fs_truncate.hpp" #include "policy_rv.hpp" #include "ugid.hpp" +#include "state.hpp" #include "fuse.h" +#include "toml.hpp" + #include #include @@ -114,20 +117,16 @@ namespace l } } -namespace FUSE +namespace FUSE::TRUNCATE { int truncate(const char *fusepath_, off_t size_) { - Config::Read cfg; + State s; const fuse_context *fc = fuse_get_context(); const ugid::Set ugid(fc->uid,fc->gid); - return l::truncate(cfg->func.truncate.policy, - cfg->func.getattr.policy, - cfg->branches, - fusepath_, - size_); + return s->truncate(fusepath_,size_); } } diff --git a/src/fuse_truncate.hpp b/src/fuse_truncate.hpp index 61331295..957e587a 100644 --- a/src/fuse_truncate.hpp +++ b/src/fuse_truncate.hpp @@ -16,12 +16,14 @@ #pragma once +#include "toml.hpp" + #include -namespace FUSE +namespace FUSE::TRUNCATE { int truncate(const char *fusepath, - off_t size); + off_t length); } diff --git a/src/fuse_truncate_err.hpp b/src/fuse_truncate_err.hpp new file mode 100644 index 00000000..7836c48c --- /dev/null +++ b/src/fuse_truncate_err.hpp @@ -0,0 +1,54 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "errno.hpp" + + +namespace FUSE::TRUNCATE +{ + class Err + { + public: + Err() + : _err(-ENOENT) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == -ENOENT) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_truncate_func.cpp b/src/fuse_truncate_func.cpp new file mode 100644 index 00000000..3bea6e0c --- /dev/null +++ b/src/fuse_truncate_func.cpp @@ -0,0 +1,35 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_truncate_func.hpp" +#include "fuse_truncate_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::TRUNCATE::Func::Func(const toml::value &toml_) +{ + _truncate = FuncFactory(toml_); +} + +int +FUSE::TRUNCATE::Func::operator()(const char *fusepath_, + const off_t length_) +{ + return (*_truncate)(fusepath_,length_); +} diff --git a/src/fuse_truncate_func.hpp b/src/fuse_truncate_func.hpp new file mode 100644 index 00000000..6410dcb1 --- /dev/null +++ b/src/fuse_truncate_func.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_truncate_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::TRUNCATE +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + const off_t length); + + private: + FuncBase::Ptr _truncate; + }; +} diff --git a/src/fuse_truncate_func_all.cpp b/src/fuse_truncate_func_all.cpp new file mode 100644 index 00000000..db60a0c6 --- /dev/null +++ b/src/fuse_truncate_func_all.cpp @@ -0,0 +1,54 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_truncate_func_all.hpp" +#include "fuse_truncate_err.hpp" + +#include "fs_truncate.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::TRUNCATE::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::TRUNCATE::FuncALL::operator()(const char *fusepath_, + const off_t length_) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::truncate(fullpath,length_); + } + } + + return rv; +} diff --git a/src/fuse_truncate_func_all.hpp b/src/fuse_truncate_func_all.hpp new file mode 100644 index 00000000..82ee0814 --- /dev/null +++ b/src/fuse_truncate_func_all.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_truncate_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::TRUNCATE +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const char *fusepath, + const off_t length) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_truncate_func_base.hpp b/src/fuse_truncate_func_base.hpp new file mode 100644 index 00000000..5f140452 --- /dev/null +++ b/src/fuse_truncate_func_base.hpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +#include + + +namespace FUSE::TRUNCATE +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + const off_t length) = 0; + }; +} diff --git a/src/fuse_truncate_func_factory.cpp b/src/fuse_truncate_func_factory.cpp new file mode 100644 index 00000000..b5704ae4 --- /dev/null +++ b/src/fuse_truncate_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_truncate_func_factory.hpp" +#include "fuse_truncate_func_all.hpp" + +#include + + +namespace FUSE::TRUNCATE +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","truncate","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_truncate_func_factory.hpp b/src/fuse_truncate_func_factory.hpp new file mode 100644 index 00000000..056c625d --- /dev/null +++ b/src/fuse_truncate_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_truncate_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::TRUNCATE +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_unlink.cpp b/src/fuse_unlink.cpp index 5f551935..0e623d33 100644 --- a/src/fuse_unlink.cpp +++ b/src/fuse_unlink.cpp @@ -19,6 +19,7 @@ #include "fs_path.hpp" #include "fs_unlink.hpp" #include "ugid.hpp" +#include "state.hpp" #include "fuse.h" @@ -99,10 +100,10 @@ namespace l } } -namespace FUSE +namespace FUSE::UNLINK { int - unlink(const char *fusepath_) + unlink_old(const char *fusepath_) { Config::Read cfg; const fuse_context *fc = fuse_get_context(); @@ -112,4 +113,12 @@ namespace FUSE cfg->branches, fusepath_); } + + int + unlink(const char *fusepath_) + { + State s; + + return s->unlink(fusepath_); + } } diff --git a/src/fuse_unlink.hpp b/src/fuse_unlink.hpp index 845ac186..6834c720 100644 --- a/src/fuse_unlink.hpp +++ b/src/fuse_unlink.hpp @@ -16,9 +16,14 @@ #pragma once +#include "toml.hpp" -namespace FUSE + +namespace FUSE::UNLINK { + int + config(const toml::value &); + int unlink(const char *fusepath); } diff --git a/src/fuse_unlink_err.hpp b/src/fuse_unlink_err.hpp new file mode 100644 index 00000000..58b80680 --- /dev/null +++ b/src/fuse_unlink_err.hpp @@ -0,0 +1,51 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +namespace FUSE::UNLINK +{ + class Err + { + public: + Err() + : _err(-ENOENT) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == -ENOENT) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_unlink_func.cpp b/src/fuse_unlink_func.cpp new file mode 100644 index 00000000..2d28214e --- /dev/null +++ b/src/fuse_unlink_func.cpp @@ -0,0 +1,34 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_unlink_func.hpp" +#include "fuse_unlink_func_factory.hpp" + +#include "toml.hpp" + + +FUSE::UNLINK::Func::Func(const toml::value &toml_) +{ + _unlink = FuncFactory(toml_); +} + +int +FUSE::UNLINK::Func::operator()(const char *fusepath_) +{ + return (*_unlink)(fusepath_); +} diff --git a/src/fuse_unlink_func.hpp b/src/fuse_unlink_func.hpp new file mode 100644 index 00000000..7951aba0 --- /dev/null +++ b/src/fuse_unlink_func.hpp @@ -0,0 +1,39 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_unlink_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::UNLINK +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath); + + private: + FuncBase::Ptr _unlink; + }; +} diff --git a/src/fuse_unlink_func_all.cpp b/src/fuse_unlink_func_all.cpp new file mode 100644 index 00000000..b053b260 --- /dev/null +++ b/src/fuse_unlink_func_all.cpp @@ -0,0 +1,53 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_unlink_func_all.hpp" +#include "fuse_unlink_err.hpp" + +#include "fs_unlink.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::UNLINK::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::UNLINK::FuncALL::operator()(const char *fusepath_) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::unlink(fullpath); + } + } + + return rv; +} diff --git a/src/fuse_unlink_func_all.hpp b/src/fuse_unlink_func_all.hpp new file mode 100644 index 00000000..2afce42b --- /dev/null +++ b/src/fuse_unlink_func_all.hpp @@ -0,0 +1,39 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_unlink_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::UNLINK +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const char *fusepath) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_unlink_func_base.hpp b/src/fuse_unlink_func_base.hpp new file mode 100644 index 00000000..e8e1bd00 --- /dev/null +++ b/src/fuse_unlink_func_base.hpp @@ -0,0 +1,35 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + + +namespace FUSE::UNLINK +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath) = 0; + }; +} diff --git a/src/fuse_unlink_func_factory.cpp b/src/fuse_unlink_func_factory.cpp new file mode 100644 index 00000000..9a2e5083 --- /dev/null +++ b/src/fuse_unlink_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_unlink_func_factory.hpp" +#include "fuse_unlink_func_all.hpp" + +#include + + +namespace FUSE::UNLINK +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","unlink","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_unlink_func_factory.hpp b/src/fuse_unlink_func_factory.hpp new file mode 100644 index 00000000..9f744b27 --- /dev/null +++ b/src/fuse_unlink_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_unlink_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::UNLINK +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_utimens.cpp b/src/fuse_utimens.cpp index 83c4b584..7a3f85d8 100644 --- a/src/fuse_utimens.cpp +++ b/src/fuse_utimens.cpp @@ -113,8 +113,21 @@ namespace l } } -namespace FUSE +namespace FUSE::UTIMENS { + int + config(const toml::value &toml_) + { + std::string val; + Config::Write cfg; + + val = toml::find(toml_,"func","utimens","policy"); + + cfg->func.utimens.from_string(val); + + return 0; + } + int utimens(const char *fusepath_, const timespec ts_[2]) diff --git a/src/fuse_utimens.hpp b/src/fuse_utimens.hpp index 2b24258e..05b18148 100644 --- a/src/fuse_utimens.hpp +++ b/src/fuse_utimens.hpp @@ -16,10 +16,12 @@ #pragma once +#include "toml.hpp" + #include -namespace FUSE +namespace FUSE::UTIMENS { int utimens(const char *fusepath, diff --git a/src/fuse_utimens_err.hpp b/src/fuse_utimens_err.hpp new file mode 100644 index 00000000..94f727c3 --- /dev/null +++ b/src/fuse_utimens_err.hpp @@ -0,0 +1,51 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +namespace FUSE::UTIMENS +{ + class Err + { + public: + Err() + : _err(0) + { + } + + public: + inline + Err& + operator=(const int err_) + { + if(_err == -ENOENT) + _err = err_; + return *this; + } + + public: + inline + operator int() + { + return _err; + } + + private: + int _err; + }; +} diff --git a/src/fuse_utimens_func.cpp b/src/fuse_utimens_func.cpp new file mode 100644 index 00000000..88b8b1d3 --- /dev/null +++ b/src/fuse_utimens_func.cpp @@ -0,0 +1,37 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_utimens_func.hpp" +#include "fuse_utimens_func_factory.hpp" + +#include "toml.hpp" + +#include + + +FUSE::UTIMENS::Func::Func(const toml::value &toml_) +{ + _utimens = FuncFactory(toml_); +} + +int +FUSE::UTIMENS::Func::operator()(const char *fusepath_, + const timespec ts_[2]) +{ + return (*_utimens)(fusepath_,ts_); +} diff --git a/src/fuse_utimens_func.hpp b/src/fuse_utimens_func.hpp new file mode 100644 index 00000000..c05651aa --- /dev/null +++ b/src/fuse_utimens_func.hpp @@ -0,0 +1,42 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_utimens_func_base.hpp" + +#include "toml.hpp" + +#include + + +namespace FUSE::UTIMENS +{ + class Func + { + public: + Func(const toml::value &); + + public: + int operator()(const char *fusepath, + const timespec ts_[2]); + + private: + FuncBase::Ptr _utimens; + }; +} diff --git a/src/fuse_utimens_func_all.cpp b/src/fuse_utimens_func_all.cpp new file mode 100644 index 00000000..e169c93d --- /dev/null +++ b/src/fuse_utimens_func_all.cpp @@ -0,0 +1,54 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_utimens_func_all.hpp" +#include "fuse_utimens_err.hpp" + +#include "fs_lutimens.hpp" + + +namespace gfs = ghc::filesystem; + + +FUSE::UTIMENS::FuncALL::FuncALL(const toml::value &toml_) + : _branches(toml_) +{ + +} + +int +FUSE::UTIMENS::FuncALL::operator()(const char *fusepath_, + const timespec ts_[2]) +{ + Err rv; + gfs::path fusepath; + gfs::path fullpath; + + fusepath = &fusepath_[1]; + for(const auto &branch_group : _branches) + { + for(const auto &branch : branch_group) + { + fullpath = branch.path / fusepath; + + rv = fs::lutimens(fullpath,ts_); + } + } + + return rv; +} diff --git a/src/fuse_utimens_func_all.hpp b/src/fuse_utimens_func_all.hpp new file mode 100644 index 00000000..7707c349 --- /dev/null +++ b/src/fuse_utimens_func_all.hpp @@ -0,0 +1,40 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_utimens_func_base.hpp" + +#include "branches.hpp" + + +namespace FUSE::UTIMENS +{ + class FuncALL : public FuncBase + { + public: + FuncALL(const toml::value &); + + public: + int operator()(const char *fusepath, + const timespec ts[2]) final; + + private: + Branches2 _branches; + }; +} diff --git a/src/fuse_utimens_func_base.hpp b/src/fuse_utimens_func_base.hpp new file mode 100644 index 00000000..069be495 --- /dev/null +++ b/src/fuse_utimens_func_base.hpp @@ -0,0 +1,36 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + + +namespace FUSE::UTIMENS +{ + class FuncBase + { + public: + typedef std::shared_ptr Ptr; + + public: + virtual int operator()(const char *fusepath, + const timespec ts[2]) = 0; + }; +} diff --git a/src/fuse_utimens_func_factory.cpp b/src/fuse_utimens_func_factory.cpp new file mode 100644 index 00000000..f60cfde4 --- /dev/null +++ b/src/fuse_utimens_func_factory.cpp @@ -0,0 +1,38 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_utimens_func_factory.hpp" +#include "fuse_utimens_func_all.hpp" + +#include + + +namespace FUSE::UTIMENS +{ + FuncBase::Ptr + FuncFactory(const toml::value &toml_) + { + std::string str; + + str = toml::find_or(toml_,"func","utimens","policy","all"); + if(str == "all") + return std::make_shared(toml_); + + throw std::runtime_error(""); + } +} diff --git a/src/fuse_utimens_func_factory.hpp b/src/fuse_utimens_func_factory.hpp new file mode 100644 index 00000000..327ba7ce --- /dev/null +++ b/src/fuse_utimens_func_factory.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_utimens_func_base.hpp" + +#include "toml.hpp" + + +namespace FUSE::UTIMENS +{ + FuncBase::Ptr + FuncFactory(const toml::value &); +} diff --git a/src/fuse_write.cpp b/src/fuse_write.cpp index 303b6cba..f17c76c1 100644 --- a/src/fuse_write.cpp +++ b/src/fuse_write.cpp @@ -122,7 +122,7 @@ namespace l } } -namespace FUSE +namespace FUSE::WRITE { int write(const fuse_file_info_t *ffi_, diff --git a/src/fuse_write.hpp b/src/fuse_write.hpp index abbfd093..934047a3 100644 --- a/src/fuse_write.hpp +++ b/src/fuse_write.hpp @@ -19,7 +19,7 @@ #include "fuse.h" -namespace FUSE +namespace FUSE::WRITE { int write(const fuse_file_info_t *ffi, diff --git a/src/fuse_write_buf.cpp b/src/fuse_write_buf.cpp index fb06e4de..67eb82ae 100644 --- a/src/fuse_write_buf.cpp +++ b/src/fuse_write_buf.cpp @@ -84,7 +84,7 @@ namespace l } } -namespace FUSE +namespace FUSE::WRITE_BUF { int write_buf(const fuse_file_info_t *ffi_, diff --git a/src/fuse_write_buf.hpp b/src/fuse_write_buf.hpp index be4779df..4aab2119 100644 --- a/src/fuse_write_buf.hpp +++ b/src/fuse_write_buf.hpp @@ -21,7 +21,7 @@ #include -namespace FUSE +namespace FUSE::WRITE_BUF { int write_buf(const fuse_file_info_t *ffi, diff --git a/src/humanize.cpp b/src/humanize.cpp new file mode 100644 index 00000000..0a9f56ee --- /dev/null +++ b/src/humanize.cpp @@ -0,0 +1,93 @@ +/* + ISC License + + Copyright (c) 2021, Antonio SJ Musumeci + + 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 "ef.hpp" + +#include +#include + +#define KB (1024UL) +#define MB (KB * 1024UL) +#define GB (MB * 1024UL) +#define TB (GB * 1024UL) + +namespace humanize +{ + int + from(const std::string &str_, + uint64_t *u_) + { + char *endptr; + uint64_t tmp; + + tmp = ::strtoll(str_.c_str(),&endptr,10); + switch(*endptr) + { + case 'k': + case 'K': + tmp *= 1024ULL; + break; + + case 'm': + case 'M': + tmp *= (1024ULL * 1024ULL); + break; + + case 'g': + case 'G': + tmp *= (1024ULL * 1024ULL * 1024ULL); + break; + + case 't': + case 'T': + tmp *= (1024ULL * 1024ULL * 1024ULL * 1024ULL); + break; + + case '\0': + break; + + default: + return -EINVAL; + } + + *u_ = tmp; + + return 0; + } + + std::string + to(const uint64_t u_) + { + char buf[64]; + + if(u_ < KB) + sprintf(buf,"%lu",u_); + ef(((u_ / TB) * TB) == u_) + sprintf(buf,"%luT",u_ / TB); + ef(((u_ / GB) * GB) == u_) + sprintf(buf,"%luG",u_ / GB); + ef(((u_ / MB) * MB) == u_) + sprintf(buf,"%luM",u_ / MB); + ef(((u_ / KB) * KB) == u_) + sprintf(buf,"%luK",u_ / KB); + else + sprintf(buf,"%lu",u_); + + return std::string(buf); + } +} diff --git a/src/config_link_exdev.hpp b/src/humanize.hpp similarity index 81% rename from src/config_link_exdev.hpp rename to src/humanize.hpp index 45f5dfe7..a0b87267 100644 --- a/src/config_link_exdev.hpp +++ b/src/humanize.hpp @@ -18,13 +18,16 @@ #pragma once -#include "enum.hpp" +#include +#include -enum class LinkEXDEVEnum - { - PASSTHROUGH, - REL_SYMLINK, - ABS_BASE_SYMLINK, - ABS_POOL_SYMLINK - }; -typedef Enum LinkEXDEV; +namespace humanize +{ + int + from(const std::string &str, + uint64_t *u); + + int + to(const uint64_t u, + std::string &str); +} diff --git a/src/config_rename_exdev.cpp b/src/link_exdev_enum.hpp similarity index 54% rename from src/config_rename_exdev.cpp rename to src/link_exdev_enum.hpp index b2fd46d9..ceceeee4 100644 --- a/src/config_rename_exdev.cpp +++ b/src/link_exdev_enum.hpp @@ -16,39 +16,41 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "config_rename_exdev.hpp" -#include "ef.hpp" -#include "errno.hpp" +#pragma once -template<> -std::string -RenameEXDEV::to_string(void) const +#include "toml.hpp" + +enum class LinkEXDEV + { + INVALID, + PASSTHROUGH, + REL_SYMLINK, + ABS_BASE_SYMLINK, + ABS_POOL_SYMLINK + }; + +namespace toml { - switch(_data) + template<> + struct from + { + static + LinkEXDEV + from_toml(const toml::value &v_) { - case RenameEXDEV::ENUM::PASSTHROUGH: - return "passthrough"; - case RenameEXDEV::ENUM::REL_SYMLINK: - return "rel-symlink"; - case RenameEXDEV::ENUM::ABS_SYMLINK: - return "abs-symlink"; + const std::string &s = v_.as_string(); + + if(s == "passthrough") + return LinkEXDEV::PASSTHROUGH; + if(s == "rel-symlink") + return LinkEXDEV::REL_SYMLINK; + if(s == "abs-base-symlink") + return LinkEXDEV::ABS_BASE_SYMLINK; + if(s == "abs-pool-symlink") + return LinkEXDEV::ABS_POOL_SYMLINK; + + return LinkEXDEV::INVALID; } - - 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; -} diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index 56a52e8e..1a924bcd 100644 --- a/src/mergerfs.cpp +++ b/src/mergerfs.cpp @@ -20,6 +20,8 @@ #include "resources.hpp" #include "strvec.hpp" +#include "state.hpp" + #include "fuse_access.hpp" #include "fuse_bmap.hpp" #include "fuse_chmod.hpp" @@ -70,6 +72,9 @@ #include "fuse.h" +#include "toml.hpp" +#include "toml_verify.hpp" + #include #include @@ -83,53 +88,53 @@ namespace l get_fuse_operations(struct fuse_operations &ops_, const bool nullrw_) { - ops_.access = FUSE::access; - ops_.bmap = FUSE::bmap; - ops_.chmod = FUSE::chmod; - ops_.chown = FUSE::chown; - ops_.copy_file_range = FUSE::copy_file_range; - ops_.create = FUSE::create; - ops_.destroy = FUSE::destroy; - ops_.fallocate = FUSE::fallocate; - ops_.fchmod = FUSE::fchmod; - ops_.fchown = FUSE::fchown; - ops_.fgetattr = FUSE::fgetattr; - ops_.flock = FUSE::flock; - ops_.flush = FUSE::flush; - ops_.free_hide = FUSE::free_hide; - ops_.fsync = FUSE::fsync; - ops_.fsyncdir = FUSE::fsyncdir; - ops_.ftruncate = FUSE::ftruncate; - ops_.futimens = FUSE::futimens; - ops_.getattr = FUSE::getattr; - ops_.getxattr = FUSE::getxattr; - ops_.init = FUSE::init; - ops_.ioctl = FUSE::ioctl; - ops_.link = FUSE::link; - ops_.listxattr = FUSE::listxattr; - ops_.lock = FUSE::lock; - ops_.mkdir = FUSE::mkdir; - ops_.mknod = FUSE::mknod; - ops_.open = FUSE::open; - ops_.opendir = FUSE::opendir; - ops_.poll = FUSE::poll;; - ops_.prepare_hide = FUSE::prepare_hide; - ops_.read_buf = (nullrw_ ? FUSE::read_buf_null : FUSE::read_buf); - ops_.readdir = FUSE::readdir; - ops_.readdir_plus = FUSE::readdir_plus; - ops_.readlink = FUSE::readlink; - ops_.release = FUSE::release; - ops_.releasedir = FUSE::releasedir; - ops_.removexattr = FUSE::removexattr; - ops_.rename = FUSE::rename; - ops_.rmdir = FUSE::rmdir; - ops_.setxattr = FUSE::setxattr; - ops_.statfs = FUSE::statfs; - ops_.symlink = FUSE::symlink; - ops_.truncate = FUSE::truncate; - ops_.unlink = FUSE::unlink; - ops_.utimens = FUSE::utimens; - ops_.write_buf = (nullrw_ ? FUSE::write_buf_null : FUSE::write_buf); + ops_.access = FUSE::ACCESS::access; + ops_.bmap = FUSE::BMAP::bmap; + ops_.chmod = FUSE::CHMOD::chmod; + ops_.chown = FUSE::CHOWN::chown; + ops_.copy_file_range = FUSE::COPY_FILE_RANGE::copy_file_range; + ops_.create = FUSE::CREATE::create; + ops_.destroy = FUSE::DESTROY::destroy; + ops_.fallocate = FUSE::FALLOCATE::fallocate; + ops_.fchmod = FUSE::FCHMOD::fchmod; + ops_.fchown = FUSE::FCHOWN::fchown; + ops_.fgetattr = FUSE::FGETATTR::fgetattr; + ops_.flock = FUSE::FLOCK::flock; + ops_.flush = FUSE::FLUSH::flush; + ops_.free_hide = FUSE::FREE_HIDE::free_hide; + ops_.fsync = FUSE::FSYNC::fsync; + ops_.fsyncdir = FUSE::FSYNCDIR::fsyncdir; + ops_.ftruncate = FUSE::FTRUNCATE::ftruncate; + ops_.futimens = FUSE::FUTIMENS::futimens; + ops_.getattr = FUSE::GETATTR::getattr; + ops_.getxattr = FUSE::GETXATTR::getxattr; + ops_.init = FUSE::INIT::init; + ops_.ioctl = FUSE::IOCTL::ioctl; + ops_.link = FUSE::LINK::link; + ops_.listxattr = FUSE::LISTXATTR::listxattr; + ops_.lock = FUSE::LOCK::lock; + ops_.mkdir = FUSE::MKDIR::mkdir; + ops_.mknod = FUSE::MKNOD::mknod; + ops_.open = FUSE::OPEN::open; + ops_.opendir = FUSE::OPENDIR::opendir; + ops_.poll = FUSE::POLL::poll;; + ops_.prepare_hide = FUSE::PREPARE_HIDE::prepare_hide; + ops_.read_buf = (nullrw_ ? FUSE::READ_BUF::read_buf_null : FUSE::READ_BUF::read_buf); + ops_.readdir = FUSE::READDIR::readdir; + ops_.readdir_plus = FUSE::READDIR_PLUS::readdir_plus; + ops_.readlink = FUSE::READLINK::readlink; + ops_.release = FUSE::RELEASE::release; + ops_.releasedir = FUSE::RELEASEDIR::releasedir; + ops_.removexattr = FUSE::REMOVEXATTR::removexattr; + ops_.rename = FUSE::RENAME::rename; + ops_.rmdir = FUSE::RMDIR::rmdir; + ops_.setxattr = FUSE::SETXATTR::setxattr; + ops_.statfs = FUSE::STATFS::statfs; + ops_.symlink = FUSE::SYMLINK::symlink; + ops_.truncate = FUSE::TRUNCATE::truncate; + ops_.unlink = FUSE::UNLINK::unlink; + ops_.utimens = FUSE::UTIMENS::utimens; + ops_.write_buf = (nullrw_ ? FUSE::WRITE_BUF::write_buf_null : FUSE::WRITE_BUF::write_buf); return; } @@ -179,5 +184,27 @@ int main(int argc_, char **argv_) { + State s; + std::vector errs; + + const auto doc = toml::parse("config.toml"); + + s = doc; + + try + { + //toml::verify(doc,errs); + } + catch(const toml::exception &e) + { + std::cout << e.what() + << std::endl; + } + + for(const auto &err : errs) + { + std::cout << err << std::endl; + } + return l::main(argc_,argv_); } diff --git a/src/rename_exdev_enum.hpp b/src/rename_exdev_enum.hpp new file mode 100644 index 00000000..77a65eca --- /dev/null +++ b/src/rename_exdev_enum.hpp @@ -0,0 +1,50 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +enum class RenameEXDEV + { + INVALID, + PASSTHROUGH, + REL_SYMLINK, + ABS_SYMLINK + }; + +namespace toml +{ + template<> + struct from + { + static + RenameEXDEV + from_toml(const toml::value &v_) + { + const std::string &s = v_.as_string(); + + if(s == "passthrough") + return RenameEXDEV::PASSTHROUGH; + if(s == "rel-symlink") + return RenameEXDEV::REL_SYMLINK; + if(s == "abs-symlink") + return RenameEXDEV::ABS_SYMLINK; + + return RenameEXDEV::INVALID; + } + }; +} diff --git a/src/stat_func.cpp b/src/stat_func.cpp new file mode 100644 index 00000000..eaacab09 --- /dev/null +++ b/src/stat_func.cpp @@ -0,0 +1,130 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "stat_func.hpp" + +#include "fs_lstat.hpp" +#include "fs_stat.hpp" + +namespace gfs = ghc::filesystem; + +namespace l +{ + static + void + stat_if_leads_to_dir(const gfs::path &fullpath_, + struct stat *st_) + { + int rv; + struct stat st; + + rv = fs::stat(fullpath_,&st); + if(rv == -1) + return; + + if(S_ISDIR(st.st_mode)) + *st_ = st; + + return; + } + + static + void + stat_if_leads_to_reg(const gfs::path &fullpath_, + struct stat *st_) + { + int rv; + struct stat st; + + rv = fs::stat(fullpath_,&st); + if(rv == -1) + return; + + if(S_ISREG(st.st_mode)) + *st_ = st; + + return; + } +} + +int +Stat::no_follow(const gfs::path &fullpath_, + struct stat *st_) +{ + return fs::lstat(fullpath_,st_); +} + +int +Stat::follow_all(const gfs::path &fullpath_, + struct stat *st_) +{ + int rv; + + rv = fs::stat(fullpath_,st_); + if(rv == -1) + rv = fs::lstat(fullpath_,st_); + + return rv; +} + +int +Stat::follow_dir(const gfs::path &fullpath_, + struct stat *st_) +{ + int rv; + + rv = fs::lstat(fullpath_,st_); + if(rv == -1) + return -1; + + if(S_ISLNK(st_->st_mode)) + l::stat_if_leads_to_dir(fullpath_,st_); + + return 0; +} + +int +Stat::follow_reg(const gfs::path &fullpath_, + struct stat *st_) +{ + int rv; + + rv = fs::lstat(fullpath_,st_); + if(rv == -1) + return -1; + + if(S_ISLNK(st_->st_mode)) + l::stat_if_leads_to_reg(fullpath_,st_); + + return 0; +} + +Stat::Func +Stat::factory(const std::string &str_) +{ + if(str_ == "never") + return Stat::no_follow; + if(str_ == "all") + return Stat::follow_all; + if(str_ == "regular") + return Stat::follow_reg; + if(str_ == "directory") + return Stat::follow_dir; + + return Stat::no_follow; +} diff --git a/src/stat_func.hpp b/src/stat_func.hpp new file mode 100644 index 00000000..db841f5e --- /dev/null +++ b/src/stat_func.hpp @@ -0,0 +1,60 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "ghc/filesystem.hpp" +#include "toml.hpp" + +#include + +#include + + +//typedef int (*StatFunc)(const ghc::filesystem::path&,struct stat*); +//typedef std::function StatFunc; + +namespace Stat +{ + typedef std::function Func; + + int no_follow(const ghc::filesystem::path &fullpath,struct stat *st); + int follow_all(const ghc::filesystem::path &fullpath,struct stat *st); + int follow_dir(const ghc::filesystem::path &fullpath,struct stat *st); + int follow_reg(const ghc::filesystem::path &fullpath,struct stat *st); + + Func factory(const std::string&); +} + +namespace toml +{ + template<> + struct from + { + static + Stat::Func + from_toml(const toml::value &v_) + { + Stat::Func func; + + func = Stat::factory(v_.as_string()); + + return func; + } + }; +} diff --git a/src/state.cpp b/src/state.cpp new file mode 100644 index 00000000..6272dec6 --- /dev/null +++ b/src/state.cpp @@ -0,0 +1,63 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "state.hpp" + +#define DEFAULT_LINK_EXDEV LinkEXDEV::PASSTHROUGH +#define DEFAULT_RENAME_EXDEV RenameEXDEV::PASSTHROUGH + +StateBase::Ptr g_STATE; + + +StateBase::StateBase(const toml::value &toml_) + : + branches(toml_), + access(toml_), + chmod(toml_), + chown(toml_), + create(toml_), + getattr(toml_), + getxattr(toml_), + ioctl(toml_), + link(toml_), + listxattr(toml_), + mkdir(toml_), + mknod(toml_), + open(toml_), + readlink(toml_), + removexattr(toml_), + rename(toml_), + rmdir(toml_), + setxattr(toml_), + symlink(toml_), + truncate(toml_), + unlink(toml_) +{ + mountpoint = toml::find(toml_,"filesystem","mountpoint"); + + link_exdev = toml::find_or(toml_,"func","link","exdev",DEFAULT_LINK_EXDEV); + rename_exdev = toml::find_or(toml_,"func","rename","exdev",DEFAULT_RENAME_EXDEV); + + entry_cache_timeout = toml::find_or(toml_,"cache","entry-timeout",60); + neg_entry_cache_timeout = toml::find_or(toml_,"cache","negative-entry-timeout",0); + attr_cache_timeout = toml::find_or(toml_,"cache","attr-timeout",60); + writeback_cache = toml::find_or(toml_,"cache","writeback",false); + symlinkify = toml::find_or(toml_,"func","symlinkify",false); + symlinkify_timeout = toml::find_or(toml_,"func","symlinkify-timeout",3600); + security_capability = toml::find_or(toml_,"xattr","security-capability",true); +} diff --git a/src/state.hpp b/src/state.hpp new file mode 100644 index 00000000..5e14c15b --- /dev/null +++ b/src/state.hpp @@ -0,0 +1,144 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 "fuse_access_func.hpp" +#include "fuse_chmod_func.hpp" +#include "fuse_chown_func.hpp" +#include "fuse_create_func.hpp" +#include "fuse_getattr_func.hpp" +#include "fuse_getxattr_func.hpp" +#include "fuse_ioctl_func.hpp" +#include "fuse_link_func.hpp" +#include "fuse_listxattr_func.hpp" +#include "fuse_mkdir_func.hpp" +#include "fuse_mknod_func.hpp" +#include "fuse_open_func.hpp" +#include "fuse_readlink_func.hpp" +#include "fuse_removexattr_func.hpp" +#include "fuse_rename_func.hpp" +#include "fuse_rmdir_func.hpp" +#include "fuse_setxattr_func.hpp" +#include "fuse_symlink_func.hpp" +#include "fuse_truncate_func.hpp" +#include "fuse_unlink_func.hpp" + +#include "branches.hpp" + +#include "link_exdev_enum.hpp" +#include "rename_exdev_enum.hpp" + +#include "ghc/filesystem.hpp" + +#include +#include + +class StateBase; +class State; + +extern std::shared_ptr g_STATE; + +class StateBase +{ +public: + typedef std::shared_ptr Ptr; + +public: + StateBase(const toml::value &); + +public: + int entry_cache_timeout; + int neg_entry_cache_timeout; + int attr_cache_timeout; + +public: + bool symlinkify; + uint64_t symlinkify_timeout; + +public: + bool writeback_cache; + +public: + bool security_capability; + +public: + ghc::filesystem::path mountpoint; + Branches2 branches; + +public: + LinkEXDEV link_exdev; + RenameEXDEV rename_exdev; + +public: + FUSE::ACCESS::Func access; + FUSE::CHMOD::Func chmod; + FUSE::CHOWN::Func chown; + FUSE::CREATE::Func create; + FUSE::GETATTR::Func getattr; + FUSE::GETXATTR::Func getxattr; + FUSE::IOCTL::Func ioctl; + FUSE::LINK::Func link; + FUSE::LISTXATTR::Func listxattr; + FUSE::MKDIR::Func mkdir; + FUSE::MKNOD::Func mknod; + FUSE::OPEN::Func open; + FUSE::READLINK::Func readlink; + FUSE::REMOVEXATTR::Func removexattr; + FUSE::RENAME::Func rename; + FUSE::RMDIR::Func rmdir; + FUSE::SETXATTR::Func setxattr; + FUSE::SYMLINK::Func symlink; + FUSE::TRUNCATE::Func truncate; + FUSE::UNLINK::Func unlink; + +public: + const toml::value _toml; +}; + +class State +{ +public: + State() + { + std::atomic_store(&_state,g_STATE); + } + +public: + State& + operator=(const toml::value &toml_) + { + StateBase::Ptr s; + + s = std::make_shared(toml_); + + std::atomic_store(&g_STATE,s); + + return *this; + } + +public: + StateBase* + operator->() + { + return _state.get(); + } + +private: + StateBase::Ptr _state; +}; diff --git a/src/timespec.hpp b/src/timespec.hpp new file mode 100644 index 00000000..73ac8b17 --- /dev/null +++ b/src/timespec.hpp @@ -0,0 +1,30 @@ +/* + ISC License + + Copyright (c) 2022, Antonio SJ Musumeci + + 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 + +static +bool +operator<(const timespec &lhs_, + const timespec &rhs_) +{ + if(lhs_.tv_sec == rhs_.tv_sec) + return (lhs_.tv_nsec < rhs_.tv_nsec); + + return (lhs_.tv_sec < rhs_.tv_sec); +} diff --git a/src/toml.hpp b/src/toml.hpp new file mode 100644 index 00000000..f34cfccc --- /dev/null +++ b/src/toml.hpp @@ -0,0 +1,46 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017 Toru Niina + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef TOML_FOR_MODERN_CPP +#define TOML_FOR_MODERN_CPP + +#ifndef __cplusplus +# error "__cplusplus is not defined" +#endif + +#if __cplusplus < 201103L && _MSC_VER < 1900 +# error "toml11 requires C++11 or later." +#endif + +#define TOML11_VERSION_MAJOR 3 +#define TOML11_VERSION_MINOR 7 +#define TOML11_VERSION_PATCH 0 + +#include "toml/parser.hpp" +#include "toml/literal.hpp" +#include "toml/serializer.hpp" +#include "toml/get.hpp" +#include "toml/macros.hpp" + +#endif// TOML_FOR_MODERN_CPP diff --git a/src/toml/color.hpp b/src/toml/color.hpp new file mode 100644 index 00000000..4cb572cb --- /dev/null +++ b/src/toml/color.hpp @@ -0,0 +1,64 @@ +#ifndef TOML11_COLOR_HPP +#define TOML11_COLOR_HPP +#include +#include + +#ifdef TOML11_COLORIZE_ERROR_MESSAGE +#define TOML11_ERROR_MESSAGE_COLORIZED true +#else +#define TOML11_ERROR_MESSAGE_COLORIZED false +#endif + +namespace toml +{ + +// put ANSI escape sequence to ostream +namespace color_ansi +{ +namespace detail +{ +inline int colorize_index() +{ + static const int index = std::ios_base::xalloc(); + return index; +} +} // detail + +inline std::ostream& colorize(std::ostream& os) +{ + // by default, it is zero. + os.iword(detail::colorize_index()) = 1; + return os; +} +inline std::ostream& nocolorize(std::ostream& os) +{ + os.iword(detail::colorize_index()) = 0; + return os; +} +inline std::ostream& reset (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;} +inline std::ostream& bold (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;} +inline std::ostream& grey (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;} +inline std::ostream& red (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;} +inline std::ostream& green (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;} +inline std::ostream& yellow (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;} +inline std::ostream& blue (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;} +inline std::ostream& magenta(std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;} +inline std::ostream& cyan (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} +inline std::ostream& white (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} +} // color_ansi + +// ANSI escape sequence is the only and default colorization method currently +namespace color = color_ansi; + +} // toml +#endif// TOML11_COLOR_HPP diff --git a/src/toml/combinator.hpp b/src/toml/combinator.hpp new file mode 100644 index 00000000..e250188f --- /dev/null +++ b/src/toml/combinator.hpp @@ -0,0 +1,306 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_COMBINATOR_HPP +#define TOML11_COMBINATOR_HPP +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "region.hpp" +#include "result.hpp" +#include "traits.hpp" +#include "utility.hpp" + +// they scans characters and returns region if it matches to the condition. +// when they fail, it does not change the location. +// in lexer.hpp, these are used. + +namespace toml +{ +namespace detail +{ + +// to output character as an error message. +inline std::string show_char(const char c) +{ + // It supress an error that occurs only in Debug mode of MSVC++ on Windows. + // I'm not completely sure but they check the value of char to be in the + // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes + // has negative value (if char has sign). So here it re-interprets c as + // unsigned char through pointer. In general, converting pointer to a + // pointer that has different type cause UB, but `(signed|unsigned)?char` + // are one of the exceptions. Converting pointer only to char and std::byte + // (c++17) are valid. + if(std::isgraph(*reinterpret_cast(std::addressof(c)))) + { + return std::string(1, c); + } + else + { + std::array buf; + buf.fill('\0'); + const auto r = std::snprintf( + buf.data(), buf.size(), "0x%02x", static_cast(c) & 0xFF); + (void) r; // Unused variable warning + assert(r == static_cast(buf.size()) - 1); + return std::string(buf.data()); + } +} + +template +struct character +{ + static constexpr char target = C; + + static result + invoke(location& loc) + { + if(loc.iter() == loc.end()) {return none();} + const auto first = loc.iter(); + + const char c = *(loc.iter()); + if(c != target) + { + return none(); + } + loc.advance(); // update location + + return ok(region(loc, first, loc.iter())); + } +}; +template +constexpr char character::target; + +// closed interval [Low, Up]. both Low and Up are included. +template +struct in_range +{ + // assuming ascii part of UTF-8... + static_assert(Low <= Up, "lower bound should be less than upper bound."); + + static constexpr char upper = Up; + static constexpr char lower = Low; + + static result + invoke(location& loc) + { + if(loc.iter() == loc.end()) {return none();} + const auto first = loc.iter(); + + const char c = *(loc.iter()); + if(c < lower || upper < c) + { + return none(); + } + + loc.advance(); + return ok(region(loc, first, loc.iter())); + } +}; +template constexpr char in_range::upper; +template constexpr char in_range::lower; + +// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. +// for detecting invalid characters, like control sequences in toml string. +template +struct exclude +{ + static result + invoke(location& loc) + { + if(loc.iter() == loc.end()) {return none();} + auto first = loc.iter(); + + auto rslt = Combinator::invoke(loc); + if(rslt.is_ok()) + { + loc.reset(first); + return none(); + } + loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but... + return ok(region(loc, first, loc.iter())); + } +}; + +// increment `iter`, if matches. otherwise, just return empty string. +template +struct maybe +{ + static result + invoke(location& loc) + { + const auto rslt = Combinator::invoke(loc); + if(rslt.is_ok()) + { + return rslt; + } + return ok(region(loc)); + } +}; + +template +struct sequence; + +template +struct sequence +{ + static result + invoke(location& loc) + { + const auto first = loc.iter(); + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.reset(first); + return none(); + } + return sequence::invoke(loc, std::move(rslt.unwrap()), first); + } + + // called from the above function only, recursively. + template + static result + invoke(location& loc, region reg, Iterator first) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.reset(first); + return none(); + } + reg += rslt.unwrap(); // concat regions + return sequence::invoke(loc, std::move(reg), first); + } +}; + +template +struct sequence +{ + // would be called from sequence::invoke only. + template + static result + invoke(location& loc, region reg, Iterator first) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.reset(first); + return none(); + } + reg += rslt.unwrap(); // concat regions + return ok(reg); + } +}; + +template +struct either; + +template +struct either +{ + static result + invoke(location& loc) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_ok()) {return rslt;} + return either::invoke(loc); + } +}; +template +struct either +{ + static result + invoke(location& loc) + { + return Head::invoke(loc); + } +}; + +template +struct repeat; + +template struct exactly{}; +template struct at_least{}; +struct unlimited{}; + +template +struct repeat> +{ + static result + invoke(location& loc) + { + region retval(loc); + const auto first = loc.iter(); + for(std::size_t i=0; i +struct repeat> +{ + static result + invoke(location& loc) + { + region retval(loc); + + const auto first = loc.iter(); + for(std::size_t i=0; i +struct repeat +{ + static result + invoke(location& loc) + { + region retval(loc); + while(true) + { + auto rslt = T::invoke(loc); + if(rslt.is_err()) + { + return ok(std::move(retval)); + } + retval += rslt.unwrap(); + } + } +}; + +} // detail +} // toml +#endif// TOML11_COMBINATOR_HPP diff --git a/src/toml/comments.hpp b/src/toml/comments.hpp new file mode 100644 index 00000000..b9ce8061 --- /dev/null +++ b/src/toml/comments.hpp @@ -0,0 +1,472 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_COMMENTS_HPP +#define TOML11_COMMENTS_HPP +#include +#include +#include +#include +#include +#include +#include + +#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT +# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments +#else +# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments +#endif + +// This file provides mainly two classes, `preserve_comments` and `discard_comments`. +// Those two are a container that have the same interface as `std::vector` +// but bahaves in the opposite way. `preserve_comments` is just the same as +// `std::vector` and each `std::string` corresponds to a comment line. +// Conversely, `discard_comments` discards all the strings and ignores everything +// assigned in it. `discard_comments` is always empty and you will encounter an +// error whenever you access to the element. +namespace toml +{ +struct discard_comments; // forward decl + +// use it in the following way +// +// const toml::basic_value data = +// toml::parse("example.toml"); +// +// the interface is almost the same as std::vector. +struct preserve_comments +{ + // `container_type` is not provided in discard_comments. + // do not use this inner-type in a generic code. + using container_type = std::vector; + + using size_type = container_type::size_type; + using difference_type = container_type::difference_type; + using value_type = container_type::value_type; + using reference = container_type::reference; + using const_reference = container_type::const_reference; + using pointer = container_type::pointer; + using const_pointer = container_type::const_pointer; + using iterator = container_type::iterator; + using const_iterator = container_type::const_iterator; + using reverse_iterator = container_type::reverse_iterator; + using const_reverse_iterator = container_type::const_reverse_iterator; + + preserve_comments() = default; + ~preserve_comments() = default; + preserve_comments(preserve_comments const&) = default; + preserve_comments(preserve_comments &&) = default; + preserve_comments& operator=(preserve_comments const&) = default; + preserve_comments& operator=(preserve_comments &&) = default; + + explicit preserve_comments(const std::vector& c): comments(c){} + explicit preserve_comments(std::vector&& c) + : comments(std::move(c)) + {} + preserve_comments& operator=(const std::vector& c) + { + comments = c; + return *this; + } + preserve_comments& operator=(std::vector&& c) + { + comments = std::move(c); + return *this; + } + + explicit preserve_comments(const discard_comments&) {} + + explicit preserve_comments(size_type n): comments(n) {} + preserve_comments(size_type n, const std::string& x): comments(n, x) {} + preserve_comments(std::initializer_list x): comments(x) {} + template + preserve_comments(InputIterator first, InputIterator last) + : comments(first, last) + {} + + template + void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} + void assign(std::initializer_list ini) {comments.assign(ini);} + void assign(size_type n, const std::string& val) {comments.assign(n, val);} + + // Related to the issue #97. + // + // It is known that `std::vector::insert` and `std::vector::erase` in + // the standard library implementation included in GCC 4.8.5 takes + // `std::vector::iterator` instead of `std::vector::const_iterator`. + // Because of the const-correctness, we cannot convert a `const_iterator` to + // an `iterator`. It causes compilation error in GCC 4.8.5. +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) +# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 +# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION +# endif +#endif + +#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION + iterator insert(iterator p, const std::string& x) + { + return comments.insert(p, x); + } + iterator insert(iterator p, std::string&& x) + { + return comments.insert(p, std::move(x)); + } + void insert(iterator p, size_type n, const std::string& x) + { + return comments.insert(p, n, x); + } + template + void insert(iterator p, InputIterator first, InputIterator last) + { + return comments.insert(p, first, last); + } + void insert(iterator p, std::initializer_list ini) + { + return comments.insert(p, ini); + } + + template + iterator emplace(iterator p, Ts&& ... args) + { + return comments.emplace(p, std::forward(args)...); + } + + iterator erase(iterator pos) {return comments.erase(pos);} + iterator erase(iterator first, iterator last) + { + return comments.erase(first, last); + } +#else + iterator insert(const_iterator p, const std::string& x) + { + return comments.insert(p, x); + } + iterator insert(const_iterator p, std::string&& x) + { + return comments.insert(p, std::move(x)); + } + iterator insert(const_iterator p, size_type n, const std::string& x) + { + return comments.insert(p, n, x); + } + template + iterator insert(const_iterator p, InputIterator first, InputIterator last) + { + return comments.insert(p, first, last); + } + iterator insert(const_iterator p, std::initializer_list ini) + { + return comments.insert(p, ini); + } + + template + iterator emplace(const_iterator p, Ts&& ... args) + { + return comments.emplace(p, std::forward(args)...); + } + + iterator erase(const_iterator pos) {return comments.erase(pos);} + iterator erase(const_iterator first, const_iterator last) + { + return comments.erase(first, last); + } +#endif + + void swap(preserve_comments& other) {comments.swap(other.comments);} + + void push_back(const std::string& v) {comments.push_back(v);} + void push_back(std::string&& v) {comments.push_back(std::move(v));} + void pop_back() {comments.pop_back();} + + template + void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} + + void clear() {comments.clear();} + + size_type size() const noexcept {return comments.size();} + size_type max_size() const noexcept {return comments.max_size();} + size_type capacity() const noexcept {return comments.capacity();} + bool empty() const noexcept {return comments.empty();} + + void reserve(size_type n) {comments.reserve(n);} + void resize(size_type n) {comments.resize(n);} + void resize(size_type n, const std::string& c) {comments.resize(n, c);} + void shrink_to_fit() {comments.shrink_to_fit();} + + reference operator[](const size_type n) noexcept {return comments[n];} + const_reference operator[](const size_type n) const noexcept {return comments[n];} + reference at(const size_type n) {return comments.at(n);} + const_reference at(const size_type n) const {return comments.at(n);} + reference front() noexcept {return comments.front();} + const_reference front() const noexcept {return comments.front();} + reference back() noexcept {return comments.back();} + const_reference back() const noexcept {return comments.back();} + + pointer data() noexcept {return comments.data();} + const_pointer data() const noexcept {return comments.data();} + + iterator begin() noexcept {return comments.begin();} + iterator end() noexcept {return comments.end();} + const_iterator begin() const noexcept {return comments.begin();} + const_iterator end() const noexcept {return comments.end();} + const_iterator cbegin() const noexcept {return comments.cbegin();} + const_iterator cend() const noexcept {return comments.cend();} + + reverse_iterator rbegin() noexcept {return comments.rbegin();} + reverse_iterator rend() noexcept {return comments.rend();} + const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} + const_reverse_iterator rend() const noexcept {return comments.rend();} + const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} + const_reverse_iterator crend() const noexcept {return comments.crend();} + + friend bool operator==(const preserve_comments&, const preserve_comments&); + friend bool operator!=(const preserve_comments&, const preserve_comments&); + friend bool operator< (const preserve_comments&, const preserve_comments&); + friend bool operator<=(const preserve_comments&, const preserve_comments&); + friend bool operator> (const preserve_comments&, const preserve_comments&); + friend bool operator>=(const preserve_comments&, const preserve_comments&); + + friend void swap(preserve_comments&, std::vector&); + friend void swap(std::vector&, preserve_comments&); + + private: + + container_type comments; +}; + +inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} +inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} +inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} +inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} +inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} +inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} + +inline void swap(preserve_comments& lhs, preserve_comments& rhs) +{ + lhs.swap(rhs); + return; +} +inline void swap(preserve_comments& lhs, std::vector& rhs) +{ + lhs.comments.swap(rhs); + return; +} +inline void swap(std::vector& lhs, preserve_comments& rhs) +{ + lhs.swap(rhs.comments); + return; +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const preserve_comments& com) +{ + for(const auto& c : com) + { + os << '#' << c << '\n'; + } + return os; +} + +namespace detail +{ + +// To provide the same interface with `preserve_comments`, `discard_comments` +// should have an iterator. But it does not contain anything, so we need to +// add an iterator that points nothing. +// +// It always points null, so DO NOT unwrap this iterator. It always crashes +// your program. +template +struct empty_iterator +{ + using value_type = T; + using reference_type = typename std::conditional::type; + using pointer_type = typename std::conditional::type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + empty_iterator() = default; + ~empty_iterator() = default; + empty_iterator(empty_iterator const&) = default; + empty_iterator(empty_iterator &&) = default; + empty_iterator& operator=(empty_iterator const&) = default; + empty_iterator& operator=(empty_iterator &&) = default; + + // DO NOT call these operators. + reference_type operator*() const noexcept {std::terminate();} + pointer_type operator->() const noexcept {return nullptr;} + reference_type operator[](difference_type) const noexcept {return this->operator*();} + + // These operators do nothing. + empty_iterator& operator++() noexcept {return *this;} + empty_iterator operator++(int) noexcept {return *this;} + empty_iterator& operator--() noexcept {return *this;} + empty_iterator operator--(int) noexcept {return *this;} + + empty_iterator& operator+=(difference_type) noexcept {return *this;} + empty_iterator& operator-=(difference_type) noexcept {return *this;} + + empty_iterator operator+(difference_type) const noexcept {return *this;} + empty_iterator operator-(difference_type) const noexcept {return *this;} +}; + +template +bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} +template +bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} +template +bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} +template +bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} + +template +typename empty_iterator::difference_type +operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} + +template +empty_iterator +operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} +template +empty_iterator +operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} + +} // detail + +// The default comment type. It discards all the comments. It requires only one +// byte to contain, so the memory footprint is smaller than preserve_comments. +// +// It just ignores `push_back`, `insert`, `erase`, and any other modifications. +// IT always returns size() == 0, the iterator taken by `begin()` is always the +// same as that of `end()`, and accessing through `operator[]` or iterators +// always causes a segmentation fault. DO NOT access to the element of this. +// +// Why this is chose as the default type is because the last version (2.x.y) +// does not contain any comments in a value. To minimize the impact on the +// efficiency, this is choosed as a default. +// +// To reduce the memory footprint, later we can try empty base optimization (EBO). +struct discard_comments +{ + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using reference = std::string&; + using const_reference = std::string const&; + using pointer = std::string*; + using const_pointer = std::string const*; + using iterator = detail::empty_iterator; + using const_iterator = detail::empty_iterator; + using reverse_iterator = detail::empty_iterator; + using const_reverse_iterator = detail::empty_iterator; + + discard_comments() = default; + ~discard_comments() = default; + discard_comments(discard_comments const&) = default; + discard_comments(discard_comments &&) = default; + discard_comments& operator=(discard_comments const&) = default; + discard_comments& operator=(discard_comments &&) = default; + + explicit discard_comments(const std::vector&) noexcept {} + explicit discard_comments(std::vector&&) noexcept {} + discard_comments& operator=(const std::vector&) noexcept {return *this;} + discard_comments& operator=(std::vector&&) noexcept {return *this;} + + explicit discard_comments(const preserve_comments&) noexcept {} + + explicit discard_comments(size_type) noexcept {} + discard_comments(size_type, const std::string&) noexcept {} + discard_comments(std::initializer_list) noexcept {} + template + discard_comments(InputIterator, InputIterator) noexcept {} + + template + void assign(InputIterator, InputIterator) noexcept {} + void assign(std::initializer_list) noexcept {} + void assign(size_type, const std::string&) noexcept {} + + iterator insert(const_iterator, const std::string&) {return iterator{};} + iterator insert(const_iterator, std::string&&) {return iterator{};} + iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} + template + iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} + iterator insert(const_iterator, std::initializer_list) {return iterator{};} + + template + iterator emplace(const_iterator, Ts&& ...) {return iterator{};} + iterator erase(const_iterator) {return iterator{};} + iterator erase(const_iterator, const_iterator) {return iterator{};} + + void swap(discard_comments&) {return;} + + void push_back(const std::string&) {return;} + void push_back(std::string&& ) {return;} + void pop_back() {return;} + + template + void emplace_back(Ts&& ...) {return;} + + void clear() {return;} + + size_type size() const noexcept {return 0;} + size_type max_size() const noexcept {return 0;} + size_type capacity() const noexcept {return 0;} + bool empty() const noexcept {return true;} + + void reserve(size_type) {return;} + void resize(size_type) {return;} + void resize(size_type, const std::string&) {return;} + void shrink_to_fit() {return;} + + // DO NOT access to the element of this container. This container is always + // empty, so accessing through operator[], front/back, data causes address + // error. + + reference operator[](const size_type) noexcept {return *data();} + const_reference operator[](const size_type) const noexcept {return *data();} + reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} + const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} + reference front() noexcept {return *data();} + const_reference front() const noexcept {return *data();} + reference back() noexcept {return *data();} + const_reference back() const noexcept {return *data();} + + pointer data() noexcept {return nullptr;} + const_pointer data() const noexcept {return nullptr;} + + iterator begin() noexcept {return iterator{};} + iterator end() noexcept {return iterator{};} + const_iterator begin() const noexcept {return const_iterator{};} + const_iterator end() const noexcept {return const_iterator{};} + const_iterator cbegin() const noexcept {return const_iterator{};} + const_iterator cend() const noexcept {return const_iterator{};} + + reverse_iterator rbegin() noexcept {return iterator{};} + reverse_iterator rend() noexcept {return iterator{};} + const_reverse_iterator rbegin() const noexcept {return const_iterator{};} + const_reverse_iterator rend() const noexcept {return const_iterator{};} + const_reverse_iterator crbegin() const noexcept {return const_iterator{};} + const_reverse_iterator crend() const noexcept {return const_iterator{};} +}; + +inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} +inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} +inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} +inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} + +inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const discard_comments&) +{ + return os; +} + +} // toml11 +#endif// TOML11_COMMENTS_HPP diff --git a/src/toml/datetime.hpp b/src/toml/datetime.hpp new file mode 100644 index 00000000..d8127c15 --- /dev/null +++ b/src/toml/datetime.hpp @@ -0,0 +1,631 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_DATETIME_HPP +#define TOML11_DATETIME_HPP +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace toml +{ + +// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is +// provided in the absolutely same purpose, but C++11 is actually not compatible +// with C11. We need to dispatch the function depending on the OS. +namespace detail +{ +// TODO: find more sophisticated way to handle this +#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) +inline std::tm localtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::localtime_r(src, &dst); + if (!result) { throw std::runtime_error("localtime_r failed."); } + return dst; +} +inline std::tm gmtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::gmtime_r(src, &dst); + if (!result) { throw std::runtime_error("gmtime_r failed."); } + return dst; +} +#elif defined(_MSC_VER) +inline std::tm localtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::localtime_s(&dst, src); + if (result) { throw std::runtime_error("localtime_s failed."); } + return dst; +} +inline std::tm gmtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::gmtime_s(&dst, src); + if (result) { throw std::runtime_error("gmtime_s failed."); } + return dst; +} +#else // fallback. not threadsafe +inline std::tm localtime_s(const std::time_t* src) +{ + const auto result = std::localtime(src); + if (!result) { throw std::runtime_error("localtime failed."); } + return *result; +} +inline std::tm gmtime_s(const std::time_t* src) +{ + const auto result = std::gmtime(src); + if (!result) { throw std::runtime_error("gmtime failed."); } + return *result; +} +#endif +} // detail + +enum class month_t : std::uint8_t +{ + Jan = 0, + Feb = 1, + Mar = 2, + Apr = 3, + May = 4, + Jun = 5, + Jul = 6, + Aug = 7, + Sep = 8, + Oct = 9, + Nov = 10, + Dec = 11 +}; + +struct local_date +{ + std::int16_t year; // A.D. (like, 2018) + std::uint8_t month; // [0, 11] + std::uint8_t day; // [1, 31] + + local_date(int y, month_t m, int d) + : year (static_cast(y)), + month(static_cast(m)), + day (static_cast(d)) + {} + + explicit local_date(const std::tm& t) + : year (static_cast(t.tm_year + 1900)), + month(static_cast(t.tm_mon)), + day (static_cast(t.tm_mday)) + {} + + explicit local_date(const std::chrono::system_clock::time_point& tp) + { + const auto t = std::chrono::system_clock::to_time_t(tp); + const auto time = detail::localtime_s(&t); + *this = local_date(time); + } + + explicit local_date(const std::time_t t) + : local_date(std::chrono::system_clock::from_time_t(t)) + {} + + operator std::chrono::system_clock::time_point() const + { + // std::mktime returns date as local time zone. no conversion needed + std::tm t; + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = static_cast(this->day); + t.tm_mon = static_cast(this->month); + t.tm_year = static_cast(this->year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + return std::chrono::system_clock::from_time_t(std::mktime(&t)); + } + + operator std::time_t() const + { + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); + } + + local_date() = default; + ~local_date() = default; + local_date(local_date const&) = default; + local_date(local_date&&) = default; + local_date& operator=(local_date const&) = default; + local_date& operator=(local_date&&) = default; +}; + +inline bool operator==(const local_date& lhs, const local_date& rhs) +{ + return std::make_tuple(lhs.year, lhs.month, lhs.day) == + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +inline bool operator!=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_date& lhs, const local_date& rhs) +{ + return std::make_tuple(lhs.year, lhs.month, lhs.day) < + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +inline bool operator<=(const local_date& lhs, const local_date& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_date& lhs, const local_date& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs < rhs); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_date& date) +{ + os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; + return os; +} + +struct local_time +{ + std::uint8_t hour; // [0, 23] + std::uint8_t minute; // [0, 59] + std::uint8_t second; // [0, 60] + std::uint16_t millisecond; // [0, 999] + std::uint16_t microsecond; // [0, 999] + std::uint16_t nanosecond; // [0, 999] + + local_time(int h, int m, int s, + int ms = 0, int us = 0, int ns = 0) + : hour (static_cast(h)), + minute(static_cast(m)), + second(static_cast(s)), + millisecond(static_cast(ms)), + microsecond(static_cast(us)), + nanosecond (static_cast(ns)) + {} + + explicit local_time(const std::tm& t) + : hour (static_cast(t.tm_hour)), + minute(static_cast(t.tm_min)), + second(static_cast(t.tm_sec)), + millisecond(0), microsecond(0), nanosecond(0) + {} + + template + explicit local_time(const std::chrono::duration& t) + { + const auto h = std::chrono::duration_cast(t); + this->hour = static_cast(h.count()); + const auto t2 = t - h; + const auto m = std::chrono::duration_cast(t2); + this->minute = static_cast(m.count()); + const auto t3 = t2 - m; + const auto s = std::chrono::duration_cast(t3); + this->second = static_cast(s.count()); + const auto t4 = t3 - s; + const auto ms = std::chrono::duration_cast(t4); + this->millisecond = static_cast(ms.count()); + const auto t5 = t4 - ms; + const auto us = std::chrono::duration_cast(t5); + this->microsecond = static_cast(us.count()); + const auto t6 = t5 - us; + const auto ns = std::chrono::duration_cast(t6); + this->nanosecond = static_cast(ns.count()); + } + + operator std::chrono::nanoseconds() const + { + return std::chrono::nanoseconds (this->nanosecond) + + std::chrono::microseconds(this->microsecond) + + std::chrono::milliseconds(this->millisecond) + + std::chrono::seconds(this->second) + + std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); + } + + local_time() = default; + ~local_time() = default; + local_time(local_time const&) = default; + local_time(local_time&&) = default; + local_time& operator=(local_time const&) = default; + local_time& operator=(local_time&&) = default; +}; + +inline bool operator==(const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); +} +inline bool operator!=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); +} +inline bool operator<=(const local_time& lhs, const local_time& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_time& lhs, const local_time& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs < rhs); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_time& time) +{ + os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.second); + if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) + { + os << '.'; + os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); + if(time.microsecond != 0 || time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); + if(time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); + } + } + } + return os; +} + +struct time_offset +{ + std::int8_t hour; // [-12, 12] + std::int8_t minute; // [-59, 59] + + time_offset(int h, int m) + : hour (static_cast(h)), + minute(static_cast(m)) + {} + + operator std::chrono::minutes() const + { + return std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); + } + + time_offset() = default; + ~time_offset() = default; + time_offset(time_offset const&) = default; + time_offset(time_offset&&) = default; + time_offset& operator=(time_offset const&) = default; + time_offset& operator=(time_offset&&) = default; +}; + +inline bool operator==(const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) == + std::make_tuple(rhs.hour, rhs.minute); +} +inline bool operator!=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) < + std::make_tuple(rhs.hour, rhs.minute); +} +inline bool operator<=(const time_offset& lhs, const time_offset& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs < rhs); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const time_offset& offset) +{ + if(offset.hour == 0 && offset.minute == 0) + { + os << 'Z'; + return os; + } + int minute = static_cast(offset.hour) * 60 + offset.minute; + if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} + os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; + os << std::setfill('0') << std::setw(2) << minute % 60; + return os; +} + +struct local_datetime +{ + local_date date; + local_time time; + + local_datetime(local_date d, local_time t): date(d), time(t) {} + + explicit local_datetime(const std::tm& t): date(t), time(t){} + + explicit local_datetime(const std::chrono::system_clock::time_point& tp) + { + const auto t = std::chrono::system_clock::to_time_t(tp); + std::tm ltime = detail::localtime_s(&t); + + this->date = local_date(ltime); + this->time = local_time(ltime); + + // std::tm lacks subsecond information, so diff between tp and tm + // can be used to get millisecond & microsecond information. + const auto t_diff = tp - + std::chrono::system_clock::from_time_t(std::mktime(<ime)); + this->time.millisecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + this->time.microsecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + this->time.nanosecond = static_cast( + std::chrono::duration_cast(t_diff).count()); + } + + explicit local_datetime(const std::time_t t) + : local_datetime(std::chrono::system_clock::from_time_t(t)) + {} + + operator std::chrono::system_clock::time_point() const + { + using internal_duration = + typename std::chrono::system_clock::time_point::duration; + + // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator + // of local_date and local_time independently, the conversion fails if + // it is the day when DST begins or ends. Since local_date considers the + // time is 00:00 A.M. and local_time does not consider DST because it + // does not have any date information. We need to consider both date and + // time information at the same time to convert it correctly. + + std::tm t; + t.tm_sec = static_cast(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(this->date.year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + + // std::mktime returns date as local time zone. no conversion needed + auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); + dt += std::chrono::duration_cast( + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); + return dt; + } + + operator std::time_t() const + { + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); + } + + local_datetime() = default; + ~local_datetime() = default; + local_datetime(local_datetime const&) = default; + local_datetime(local_datetime&&) = default; + local_datetime& operator=(local_datetime const&) = default; + local_datetime& operator=(local_datetime&&) = default; +}; + +inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) == + std::make_tuple(rhs.date, rhs.time); +} +inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) < + std::make_tuple(rhs.date, rhs.time); +} +inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs < rhs); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_datetime& dt) +{ + os << dt.date << 'T' << dt.time; + return os; +} + +struct offset_datetime +{ + local_date date; + local_time time; + time_offset offset; + + offset_datetime(local_date d, local_time t, time_offset o) + : date(d), time(t), offset(o) + {} + offset_datetime(const local_datetime& dt, time_offset o) + : date(dt.date), time(dt.time), offset(o) + {} + explicit offset_datetime(const local_datetime& ld) + : date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) + // use the current local timezone offset + {} + explicit offset_datetime(const std::chrono::system_clock::time_point& tp) + : offset(0, 0) // use gmtime + { + const auto timet = std::chrono::system_clock::to_time_t(tp); + const auto tm = detail::gmtime_s(&timet); + this->date = local_date(tm); + this->time = local_time(tm); + } + explicit offset_datetime(const std::time_t& t) + : offset(0, 0) // use gmtime + { + const auto tm = detail::gmtime_s(&t); + this->date = local_date(tm); + this->time = local_time(tm); + } + explicit offset_datetime(const std::tm& t) + : offset(0, 0) // assume gmtime + { + this->date = local_date(t); + this->time = local_time(t); + } + + operator std::chrono::system_clock::time_point() const + { + // get date-time + using internal_duration = + typename std::chrono::system_clock::time_point::duration; + + // first, convert it to local date-time information in the same way as + // local_datetime does. later we will use time_t to adjust time offset. + std::tm t; + t.tm_sec = static_cast(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(this->date.year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + const std::time_t tp_loc = std::mktime(std::addressof(t)); + + auto tp = std::chrono::system_clock::from_time_t(tp_loc); + tp += std::chrono::duration_cast( + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); + + // Since mktime uses local time zone, it should be corrected. + // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if + // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need + // to add `+09:00` to `03:00:00Z`. + // Here, it uses the time_t converted from date-time info to handle + // daylight saving time. + const auto ofs = get_local_offset(std::addressof(tp_loc)); + tp += std::chrono::hours (ofs.hour); + tp += std::chrono::minutes(ofs.minute); + + // We got `12:00:00Z` by correcting local timezone applied by mktime. + // Then we will apply the offset. Let's say `12:00:00-08:00` is given. + // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. + // So we need to subtract the offset. + tp -= std::chrono::minutes(this->offset); + return tp; + } + + operator std::time_t() const + { + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); + } + + offset_datetime() = default; + ~offset_datetime() = default; + offset_datetime(offset_datetime const&) = default; + offset_datetime(offset_datetime&&) = default; + offset_datetime& operator=(offset_datetime const&) = default; + offset_datetime& operator=(offset_datetime&&) = default; + + private: + + static time_offset get_local_offset(const std::time_t* tp) + { + // get local timezone with the same date-time information as mktime + const auto t = detail::localtime_s(tp); + + std::array buf; + const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 + if(result != 5) + { + throw std::runtime_error("toml::offset_datetime: cannot obtain " + "timezone information of current env"); + } + const int ofs = std::atoi(buf.data()); + const int ofs_h = ofs / 100; + const int ofs_m = ofs - (ofs_h * 100); + return time_offset(ofs_h, ofs_m); + } +}; + +inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) == + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) < + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs < rhs); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const offset_datetime& dt) +{ + os << dt.date << 'T' << dt.time << dt.offset; + return os; +} + +}//toml +#endif// TOML11_DATETIME diff --git a/src/toml/exception.hpp b/src/toml/exception.hpp new file mode 100644 index 00000000..c64651d0 --- /dev/null +++ b/src/toml/exception.hpp @@ -0,0 +1,65 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_EXCEPTION_HPP +#define TOML11_EXCEPTION_HPP +#include +#include + +#include "source_location.hpp" + +namespace toml +{ + +struct exception : public std::exception +{ + public: + explicit exception(const source_location& loc): loc_(loc) {} + virtual ~exception() noexcept override = default; + virtual const char* what() const noexcept override {return "";} + virtual source_location const& location() const noexcept {return loc_;} + + protected: + source_location loc_; +}; + +struct syntax_error : public toml::exception +{ + public: + explicit syntax_error(const std::string& what_arg, const source_location& loc) + : exception(loc), what_(what_arg) + {} + virtual ~syntax_error() noexcept override = default; + virtual const char* what() const noexcept override {return what_.c_str();} + + protected: + std::string what_; +}; + +struct type_error : public toml::exception +{ + public: + explicit type_error(const std::string& what_arg, const source_location& loc) + : exception(loc), what_(what_arg) + {} + virtual ~type_error() noexcept override = default; + virtual const char* what() const noexcept override {return what_.c_str();} + + protected: + std::string what_; +}; + +struct internal_error : public toml::exception +{ + public: + explicit internal_error(const std::string& what_arg, const source_location& loc) + : exception(loc), what_(what_arg) + {} + virtual ~internal_error() noexcept override = default; + virtual const char* what() const noexcept override {return what_.c_str();} + + protected: + std::string what_; +}; + +} // toml +#endif // TOML_EXCEPTION diff --git a/src/toml/from.hpp b/src/toml/from.hpp new file mode 100644 index 00000000..10815caf --- /dev/null +++ b/src/toml/from.hpp @@ -0,0 +1,19 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_FROM_HPP +#define TOML11_FROM_HPP + +namespace toml +{ + +template +struct from; +// { +// static T from_toml(const toml::value& v) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_FROM_HPP diff --git a/src/toml/get.hpp b/src/toml/get.hpp new file mode 100644 index 00000000..6669e340 --- /dev/null +++ b/src/toml/get.hpp @@ -0,0 +1,1117 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_GET_HPP +#define TOML11_GET_HPP +#include + +#include "from.hpp" +#include "result.hpp" +#include "value.hpp" + +namespace toml +{ + +// ============================================================================ +// exact toml::* type + +template class M, template class V> +detail::enable_if_t>::value, T> & +get(basic_value& v) +{ + return v.template cast>::value>(); +} + +template class M, template class V> +detail::enable_if_t>::value, T> const& +get(const basic_value& v) +{ + return v.template cast>::value>(); +} + +template class M, template class V> +detail::enable_if_t>::value, T> +get(basic_value&& v) +{ + return T(std::move(v).template cast>::value>()); +} + +// ============================================================================ +// T == toml::value; identity transformation. + +template class M, template class V> +inline detail::enable_if_t>::value, T>& +get(basic_value& v) +{ + return v; +} + +template class M, template class V> +inline detail::enable_if_t>::value, T> const& +get(const basic_value& v) +{ + return v; +} + +template class M, template class V> +inline detail::enable_if_t>::value, T> +get(basic_value&& v) +{ + return basic_value(std::move(v)); +} + +// ============================================================================ +// T == toml::basic_value; basic_value -> basic_value + +template class M, template class V> +inline detail::enable_if_t, + detail::negation>> + >::value, T> +get(const basic_value& v) +{ + return T(v); +} + +// ============================================================================ +// integer convertible from toml::Integer + +template class M, template class V> +inline detail::enable_if_t, // T is integral + detail::negation>, // but not bool + detail::negation< // but not toml::integer + detail::is_exact_toml_type>> + >::value, T> +get(const basic_value& v) +{ + return static_cast(v.as_integer()); +} + +// ============================================================================ +// floating point convertible from toml::Float + +template class M, template class V> +inline detail::enable_if_t, // T is floating_point + detail::negation< // but not toml::floating + detail::is_exact_toml_type>> + >::value, T> +get(const basic_value& v) +{ + return static_cast(v.as_floating()); +} + +// ============================================================================ +// std::string; toml uses its own toml::string, but it should be convertible to +// std::string seamlessly + +template class M, template class V> +inline detail::enable_if_t::value, std::string>& +get(basic_value& v) +{ + return v.as_string().str; +} + +template class M, template class V> +inline detail::enable_if_t::value, std::string> const& +get(const basic_value& v) +{ + return v.as_string().str; +} + +template class M, template class V> +inline detail::enable_if_t::value, std::string> +get(basic_value&& v) +{ + return std::string(std::move(v.as_string().str)); +} + +// ============================================================================ +// std::string_view + +#if __cplusplus >= 201703L +template class M, template class V> +inline detail::enable_if_t::value, std::string_view> +get(const basic_value& v) +{ + return std::string_view(v.as_string().str); +} +#endif + +// ============================================================================ +// std::chrono::duration from toml::local_time. + +template class M, template class V> +inline detail::enable_if_t::value, T> +get(const basic_value& v) +{ + return std::chrono::duration_cast( + std::chrono::nanoseconds(v.as_local_time())); +} + +// ============================================================================ +// std::chrono::system_clock::time_point from toml::datetime variants + +template class M, template class V> +inline detail::enable_if_t< + std::is_same::value, T> +get(const basic_value& v) +{ + switch(v.type()) + { + case value_t::local_date: + { + return std::chrono::system_clock::time_point(v.as_local_date()); + } + case value_t::local_datetime: + { + return std::chrono::system_clock::time_point(v.as_local_datetime()); + } + case value_t::offset_datetime: + { + return std::chrono::system_clock::time_point(v.as_offset_datetime()); + } + default: + { + throw type_error(detail::format_underline("toml::value: " + "bad_cast to std::chrono::system_clock::time_point", { + {v.location(), concat_to_string("the actual type is ", v.type())} + }), v.location()); + } + } +} + +// ============================================================================ +// forward declaration to use this recursively. ignore this and go ahead. + +// array-like type with push_back(value) method +template class M, template class V> +detail::enable_if_t, // T is a container + detail::has_push_back_method, // T::push_back(value) works + detail::negation< // but not toml::array + detail::is_exact_toml_type>> + >::value, T> +get(const basic_value&); + +// array-like type without push_back(value) method +template class M, template class V> +detail::enable_if_t, // T is a container + detail::negation>, // w/o push_back(...) + detail::negation< // not toml::array + detail::is_exact_toml_type>> + >::value, T> +get(const basic_value&); + +// std::pair +template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value&); + +// std::tuple +template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value&); + +// map-like classes +template class M, template class V> +detail::enable_if_t, // T is map + detail::negation< // but not toml::table + detail::is_exact_toml_type>> + >::value, T> +get(const basic_value&); + +// T.from_toml(v) +template class M, template class V> +detail::enable_if_t>>, + detail::has_from_toml_method, // but has from_toml(toml::value) + std::is_default_constructible // and default constructible + >::value, T> +get(const basic_value&); + +// toml::from::from_toml(v) +template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value&); + +// T(const toml::value&) and T is not toml::basic_value, +// and it does not have `from` nor `from_toml`. +template class M, template class V> +detail::enable_if_t>, + std::is_constructible&>, + detail::negation>, + detail::negation> + >::value, T> +get(const basic_value&); + +// ============================================================================ +// array-like types; most likely STL container, like std::vector, etc. + +template class M, template class V> +detail::enable_if_t, // T is a container + detail::has_push_back_method, // container.push_back(elem) works + detail::negation< // but not toml::array + detail::is_exact_toml_type>> + >::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + const auto& ary = v.as_array(); + + T container; + try_reserve(container, ary.size()); + + for(const auto& elem : ary) + { + container.push_back(get(elem)); + } + return container; +} + +// ============================================================================ +// std::forward_list does not have push_back, insert, or emplace. +// It has insert_after, emplace_after, push_front. + +template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + T container; + for(const auto& elem : v.as_array()) + { + container.push_front(get(elem)); + } + container.reverse(); + return container; +} + +// ============================================================================ +// array-like types, without push_back(). most likely [std|boost]::array. + +template class M, template class V> +detail::enable_if_t, // T is a container + detail::negation>, // w/o push_back + detail::negation< // T is not toml::array + detail::is_exact_toml_type>> + >::value, T> +get(const basic_value& v) +{ + using value_type = typename T::value_type; + const auto& ar = v.as_array(); + + T container; + if(ar.size() != container.size()) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "toml::get: specified container size is ", container.size(), + " but there are ", ar.size(), " elements in toml array."), { + {v.location(), "here"} + })); + } + for(std::size_t i=0; i(ar[i]); + } + return container; +} + +// ============================================================================ +// std::pair. + +template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value& v) +{ + using first_type = typename T::first_type; + using second_type = typename T::second_type; + + const auto& ar = v.as_array(); + if(ar.size() != 2) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "toml::get: specified std::pair but there are ", ar.size(), + " elements in toml array."), {{v.location(), "here"}})); + } + return std::make_pair(::toml::get(ar.at(0)), + ::toml::get(ar.at(1))); +} + +// ============================================================================ +// std::tuple. + +namespace detail +{ +template +T get_tuple_impl(const Array& a, index_sequence) +{ + return std::make_tuple( + ::toml::get::type>(a.at(I))...); +} +} // detail + +template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value& v) +{ + const auto& ar = v.as_array(); + if(ar.size() != std::tuple_size::value) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "toml::get: specified std::tuple with ", + std::tuple_size::value, " elements, but there are ", ar.size(), + " elements in toml array."), {{v.location(), "here"}})); + } + return detail::get_tuple_impl(ar, + detail::make_index_sequence::value>{}); +} + +// ============================================================================ +// map-like types; most likely STL map, like std::map or std::unordered_map. + +template class M, template class V> +detail::enable_if_t, // T is map + detail::negation< // but not toml::array + detail::is_exact_toml_type>> + >::value, T> +get(const basic_value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + static_assert(std::is_convertible::value, + "toml::get only supports map type of which key_type is " + "convertible from std::string."); + T map; + for(const auto& kv : v.as_table()) + { + map.emplace(key_type(kv.first), get(kv.second)); + } + return map; +} + +// ============================================================================ +// user-defined, but compatible types. + +template class M, template class V> +detail::enable_if_t>>, + detail::has_from_toml_method, // but has from_toml(toml::value) memfn + std::is_default_constructible // and default constructible + >::value, T> +get(const basic_value& v) +{ + T ud; + ud.from_toml(v); + return ud; +} +template class M, template class V> +detail::enable_if_t::value, T> +get(const basic_value& v) +{ + return ::toml::from::from_toml(v); +} + +template class M, template class V> +detail::enable_if_t>, // T is not a toml::value + std::is_constructible&>, // T is constructible from toml::value + detail::negation>, // and T does not have T.from_toml(v); + detail::negation> // and T does not have toml::from{}; + >::value, T> +get(const basic_value& v) +{ + return T(v); +} + +// ============================================================================ +// find + +// ---------------------------------------------------------------------------- +// these overloads do not require to set T. and returns value itself. +template class M, template class V> +basic_value const& find(const basic_value& v, const key& ky) +{ + const auto& tab = v.as_table(); + if(tab.count(ky) == 0) + { + detail::throw_key_not_found_error(v, ky); + } + return tab.at(ky); +} +template class M, template class V> +basic_value& find(basic_value& v, const key& ky) +{ + auto& tab = v.as_table(); + if(tab.count(ky) == 0) + { + detail::throw_key_not_found_error(v, ky); + } + return tab.at(ky); +} +template class M, template class V> +basic_value find(basic_value&& v, const key& ky) +{ + typename basic_value::table_type tab = std::move(v).as_table(); + if(tab.count(ky) == 0) + { + detail::throw_key_not_found_error(v, ky); + } + return basic_value(std::move(tab.at(ky))); +} + +// ---------------------------------------------------------------------------- +// find(value, idx) +template class M, template class V> +basic_value const& +find(const basic_value& v, const std::size_t idx) +{ + const auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "index ", idx, " is out of range"), {{v.location(), "in this array"}})); + } + return ary.at(idx); +} +template class M, template class V> +basic_value& find(basic_value& v, const std::size_t idx) +{ + auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "index ", idx, " is out of range"), {{v.location(), "in this array"}})); + } + return ary.at(idx); +} +template class M, template class V> +basic_value find(basic_value&& v, const std::size_t idx) +{ + auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "index ", idx, " is out of range"), {{v.location(), "in this array"}})); + } + return basic_value(std::move(ary.at(idx))); +} + +// ---------------------------------------------------------------------------- +// find(value, key); + +template class M, template class V> +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const key& ky) +{ + const auto& tab = v.as_table(); + if(tab.count(ky) == 0) + { + detail::throw_key_not_found_error(v, ky); + } + return ::toml::get(tab.at(ky)); +} + +template class M, template class V> +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const key& ky) +{ + auto& tab = v.as_table(); + if(tab.count(ky) == 0) + { + detail::throw_key_not_found_error(v, ky); + } + return ::toml::get(tab.at(ky)); +} + +template class M, template class V> +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const key& ky) +{ + typename basic_value::table_type tab = std::move(v).as_table(); + if(tab.count(ky) == 0) + { + detail::throw_key_not_found_error(v, ky); + } + return ::toml::get(std::move(tab.at(ky))); +} + +// ---------------------------------------------------------------------------- +// find(value, idx) +template class M, template class V> +decltype(::toml::get(std::declval const&>())) +find(const basic_value& v, const std::size_t idx) +{ + const auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "index ", idx, " is out of range"), {{v.location(), "in this array"}})); + } + return ::toml::get(ary.at(idx)); +} +template class M, template class V> +decltype(::toml::get(std::declval&>())) +find(basic_value& v, const std::size_t idx) +{ + auto& ary = v.as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "index ", idx, " is out of range"), {{v.location(), "in this array"}})); + } + return ::toml::get(ary.at(idx)); +} +template class M, template class V> +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, const std::size_t idx) +{ + typename basic_value::array_type ary = std::move(v).as_array(); + if(ary.size() <= idx) + { + throw std::out_of_range(detail::format_underline(concat_to_string( + "index ", idx, " is out of range"), {{v.location(), "in this array"}})); + } + return ::toml::get(std::move(ary.at(idx))); +} + +// -------------------------------------------------------------------------- +// toml::find(toml::value, toml::key, Ts&& ... keys) + +namespace detail +{ +// It suppresses warnings by -Wsign-conversion. Let's say we have the following +// code. +// ```cpp +// const auto x = toml::find(data, "array", 0); +// ``` +// Here, the type of literal number `0` is `int`. `int` is a signed integer. +// `toml::find` takes `std::size_t` as an index. So it causes implicit sign +// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0` +// suppresses the warning, but it makes user code messy. +// To suppress this warning, we need to be aware of type conversion caused +// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of +// keys can be any combination of {string-like, size_t-like}. Of course we can't +// write down all the combinations. Thus we need to use some function that +// recognize the type of argument and cast it into `std::string` or +// `std::size_t` depending on the context. +// `key_cast` does the job. It has 2 overloads. One is invoked when the +// argument type is an integer and cast the argument into `std::size_t`. The +// other is invoked when the argument type is not an integer, possibly one of +// std::string, const char[N] or const char*, and construct std::string from +// the argument. +// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to +// `toml::find(v, k)` to suppress -Wsign-conversion. + +template +enable_if_t>, + negation, bool>>>::value, std::size_t> +key_cast(T&& v) noexcept +{ + return std::size_t(v); +} +template +enable_if_t>, + negation, bool>>>>::value, std::string> +key_cast(T&& v) noexcept +{ + return std::string(std::forward(v)); +} +} // detail + +template class M, template class V, + typename Key1, typename Key2, typename ... Keys> +const basic_value& +find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) +{ + return ::toml::find(::toml::find(v, detail::key_cast(k1)), + detail::key_cast(k2), std::forward(keys)...); +} +template class M, template class V, + typename Key1, typename Key2, typename ... Keys> +basic_value& +find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) +{ + return ::toml::find(::toml::find(v, detail::key_cast(k1)), + detail::key_cast(k2), std::forward(keys)...); +} +template class M, template class V, + typename Key1, typename Key2, typename ... Keys> +basic_value +find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) +{ + return ::toml::find(::toml::find(std::move(v), std::forward(k1)), + detail::key_cast(k2), std::forward(keys)...); +} + +template class M, template class V, + typename Key1, typename Key2, typename ... Keys> +decltype(::toml::get(std::declval&>())) +find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) +{ + return ::toml::find(::toml::find(v, detail::key_cast(k1)), + detail::key_cast(k2), std::forward(keys)...); +} +template class M, template class V, + typename Key1, typename Key2, typename ... Keys> +decltype(::toml::get(std::declval&>())) +find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) +{ + return ::toml::find(::toml::find(v, detail::key_cast(k1)), + detail::key_cast(k2), std::forward(keys)...); +} +template class M, template class V, + typename Key1, typename Key2, typename ... Keys> +decltype(::toml::get(std::declval&&>())) +find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) +{ + return ::toml::find(::toml::find(std::move(v), detail::key_cast(k1)), + detail::key_cast(k2), std::forward(keys)...); +} + +// ============================================================================ +// get_or(value, fallback) + +template class M, template class V> +basic_value const& +get_or(const basic_value& v, const basic_value&) +{ + return v; +} +template class M, template class V> +basic_value& +get_or(basic_value& v, basic_value&) +{ + return v; +} +template class M, template class V> +basic_value +get_or(basic_value&& v, basic_value&&) +{ + return v; +} + +// ---------------------------------------------------------------------------- +// specialization for the exact toml types (return type becomes lvalue ref) + +template class M, template class V> +detail::enable_if_t< + detail::is_exact_toml_type>::value, T> const& +get_or(const basic_value& v, const T& opt) +{ + try + { + return get>(v); + } + catch(...) + { + return opt; + } +} +template class M, template class V> +detail::enable_if_t< + detail::is_exact_toml_type>::value, T>& +get_or(basic_value& v, T& opt) +{ + try + { + return get>(v); + } + catch(...) + { + return opt; + } +} +template class M, template class V> +detail::enable_if_t, + basic_value>::value, detail::remove_cvref_t> +get_or(basic_value&& v, T&& opt) +{ + try + { + return get>(std::move(v)); + } + catch(...) + { + return detail::remove_cvref_t(std::forward(opt)); + } +} + +// ---------------------------------------------------------------------------- +// specialization for std::string (return type becomes lvalue ref) + +template class M, template class V> +detail::enable_if_t, std::string>::value, + std::string> const& +get_or(const basic_value& v, const T& opt) +{ + try + { + return v.as_string().str; + } + catch(...) + { + return opt; + } +} +template class M, template class V> +detail::enable_if_t::value, std::string>& +get_or(basic_value& v, T& opt) +{ + try + { + return v.as_string().str; + } + catch(...) + { + return opt; + } +} +template class M, template class V> +detail::enable_if_t< + std::is_same, std::string>::value, std::string> +get_or(basic_value&& v, T&& opt) +{ + try + { + return std::move(v.as_string().str); + } + catch(...) + { + return std::string(std::forward(opt)); + } +} + +// ---------------------------------------------------------------------------- +// specialization for string literal + +template class M, template class V> +detail::enable_if_t::type>::value, std::string> +get_or(const basic_value& v, T&& opt) +{ + try + { + return std::move(v.as_string().str); + } + catch(...) + { + return std::string(std::forward(opt)); + } +} + +// ---------------------------------------------------------------------------- +// others (require type conversion and return type cannot be lvalue reference) + +template class M, template class V> +detail::enable_if_t, + basic_value>>, + detail::negation>>, + detail::negation::type>> + >::value, detail::remove_cvref_t> +get_or(const basic_value& v, T&& opt) +{ + try + { + return get>(v); + } + catch(...) + { + return detail::remove_cvref_t(std::forward(opt)); + } +} + +// =========================================================================== +// find_or(value, key, fallback) + +template class M, template class V> +basic_value const& +find_or(const basic_value& v, const key& ky, + const basic_value& opt) +{ + if(!v.is_table()) {return opt;} + const auto& tab = v.as_table(); + if(tab.count(ky) == 0) {return opt;} + return tab.at(ky); +} + +template class M, template class V> +basic_value& +find_or(basic_value& v, const toml::key& ky, basic_value& opt) +{ + if(!v.is_table()) {return opt;} + auto& tab = v.as_table(); + if(tab.count(ky) == 0) {return opt;} + return tab.at(ky); +} + +template class M, template class V> +basic_value +find_or(basic_value&& v, const toml::key& ky, basic_value&& opt) +{ + if(!v.is_table()) {return opt;} + auto tab = std::move(v).as_table(); + if(tab.count(ky) == 0) {return opt;} + return basic_value(std::move(tab.at(ky))); +} + +// --------------------------------------------------------------------------- +// exact types (return type can be a reference) +template class M, template class V> +detail::enable_if_t< + detail::is_exact_toml_type>::value, T> const& +find_or(const basic_value& v, const key& ky, const T& opt) +{ + if(!v.is_table()) {return opt;} + const auto& tab = v.as_table(); + if(tab.count(ky) == 0) {return opt;} + return get_or(tab.at(ky), opt); +} + +template class M, template class V> +detail::enable_if_t< + detail::is_exact_toml_type>::value, T>& +find_or(basic_value& v, const toml::key& ky, T& opt) +{ + if(!v.is_table()) {return opt;} + auto& tab = v.as_table(); + if(tab.count(ky) == 0) {return opt;} + return get_or(tab.at(ky), opt); +} + +template class M, template class V> +detail::enable_if_t< + detail::is_exact_toml_type>::value, + detail::remove_cvref_t> +find_or(basic_value&& v, const toml::key& ky, T&& opt) +{ + if(!v.is_table()) {return std::forward(opt);} + auto tab = std::move(v).as_table(); + if(tab.count(ky) == 0) {return std::forward(opt);} + return get_or(std::move(tab.at(ky)), std::forward(opt)); +} + +// --------------------------------------------------------------------------- +// std::string (return type can be a reference) + +template class M, template class V> +detail::enable_if_t::value, std::string> const& +find_or(const basic_value& v, const key& ky, const T& opt) +{ + if(!v.is_table()) {return opt;} + const auto& tab = v.as_table(); + if(tab.count(ky) == 0) {return opt;} + return get_or(tab.at(ky), opt); +} +template class M, template class V> +detail::enable_if_t::value, std::string>& +find_or(basic_value& v, const toml::key& ky, T& opt) +{ + if(!v.is_table()) {return opt;} + auto& tab = v.as_table(); + if(tab.count(ky) == 0) {return opt;} + return get_or(tab.at(ky), opt); +} +template class M, template class V> +detail::enable_if_t::value, std::string> +find_or(basic_value&& v, const toml::key& ky, T&& opt) +{ + if(!v.is_table()) {return std::forward(opt);} + auto tab = std::move(v).as_table(); + if(tab.count(ky) == 0) {return std::forward(opt);} + return get_or(std::move(tab.at(ky)), std::forward(opt)); +} + +// --------------------------------------------------------------------------- +// string literal (deduced as std::string) +template class M, template class V> +detail::enable_if_t< + detail::is_string_literal::type>::value, + std::string> +find_or(const basic_value& v, const toml::key& ky, T&& opt) +{ + if(!v.is_table()) {return std::string(opt);} + const auto& tab = v.as_table(); + if(tab.count(ky) == 0) {return std::string(opt);} + return get_or(tab.at(ky), std::forward(opt)); +} + +// --------------------------------------------------------------------------- +// others (require type conversion and return type cannot be lvalue reference) +template class M, template class V> +detail::enable_if_t, basic_value>>, + // T is not std::string + detail::negation>>, + // T is not a string literal + detail::negation::type>> + >::value, detail::remove_cvref_t> +find_or(const basic_value& v, const toml::key& ky, T&& opt) +{ + if(!v.is_table()) {return std::forward(opt);} + const auto& tab = v.as_table(); + if(tab.count(ky) == 0) {return std::forward(opt);} + return get_or(tab.at(ky), std::forward(opt)); +} + +// --------------------------------------------------------------------------- +// recursive find-or with type deduction (find_or(value, keys, opt)) + +template 1), std::nullptr_t> = nullptr> + // here we need to add SFINAE in the template parameter to avoid + // infinite recursion in type deduction on gcc +auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) + -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) +{ + if(!v.is_table()) + { + return detail::last_one(std::forward(keys)...); + } + auto&& tab = std::forward(v).as_table(); + if(tab.count(ky) == 0) + { + return detail::last_one(std::forward(keys)...); + } + return find_or(std::forward(tab).at(ky), std::forward(keys)...); +} + +// --------------------------------------------------------------------------- +// recursive find_or with explicit type specialization, find_or(value, keys...) + +template 1), std::nullptr_t> = nullptr> + // here we need to add SFINAE in the template parameter to avoid + // infinite recursion in type deduction on gcc +auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) + -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) +{ + if(!v.is_table()) + { + return detail::last_one(std::forward(keys)...); + } + auto&& tab = std::forward(v).as_table(); + if(tab.count(ky) == 0) + { + return detail::last_one(std::forward(keys)...); + } + return find_or(std::forward(tab).at(ky), std::forward(keys)...); +} + +// ============================================================================ +// expect + +template class M, template class V> +result expect(const basic_value& v) noexcept +{ + try + { + return ok(get(v)); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template class M, template class V> +result +expect(const basic_value& v, const toml::key& k) noexcept +{ + try + { + return ok(find(v, k)); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} + +} // toml +#endif// TOML11_GET diff --git a/src/toml/into.hpp b/src/toml/into.hpp new file mode 100644 index 00000000..74495560 --- /dev/null +++ b/src/toml/into.hpp @@ -0,0 +1,19 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_INTO_HPP +#define TOML11_INTO_HPP + +namespace toml +{ + +template +struct into; +// { +// static toml::value into_toml(const T& user_defined_type) +// { +// // User-defined conversions ... +// } +// }; + +} // toml +#endif // TOML11_INTO_HPP diff --git a/src/toml/lexer.hpp b/src/toml/lexer.hpp new file mode 100644 index 00000000..2eea996e --- /dev/null +++ b/src/toml/lexer.hpp @@ -0,0 +1,270 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_LEXER_HPP +#define TOML11_LEXER_HPP +#include +#include +#include +#include + +#include "combinator.hpp" + +namespace toml +{ +namespace detail +{ + +// these scans contents from current location in a container of char +// and extract a region that matches their own pattern. +// to see the implementation of each component, see combinator.hpp. + +using lex_wschar = either, character<'\t'>>; +using lex_ws = repeat>; +using lex_newline = either, + sequence, character<'\n'>>>; +using lex_lower = in_range<'a', 'z'>; +using lex_upper = in_range<'A', 'Z'>; +using lex_alpha = either; +using lex_digit = in_range<'0', '9'>; +using lex_nonzero = in_range<'1', '9'>; +using lex_oct_dig = in_range<'0', '7'>; +using lex_bin_dig = in_range<'0', '1'>; +using lex_hex_dig = either, in_range<'a', 'f'>>; + +using lex_hex_prefix = sequence, character<'x'>>; +using lex_oct_prefix = sequence, character<'o'>>; +using lex_bin_prefix = sequence, character<'b'>>; +using lex_underscore = character<'_'>; +using lex_plus = character<'+'>; +using lex_minus = character<'-'>; +using lex_sign = either; + +// digit | nonzero 1*(digit | _ digit) +using lex_unsigned_dec_int = either>, at_least<1>>>, + lex_digit>; +// (+|-)? unsigned_dec_int +using lex_dec_int = sequence, lex_unsigned_dec_int>; + +// hex_prefix hex_dig *(hex_dig | _ hex_dig) +using lex_hex_int = sequence>, unlimited>>>; +// oct_prefix oct_dig *(oct_dig | _ oct_dig) +using lex_oct_int = sequence>, unlimited>>>; +// bin_prefix bin_dig *(bin_dig | _ bin_dig) +using lex_bin_int = sequence>, unlimited>>>; + +// (dec_int | hex_int | oct_int | bin_int) +using lex_integer = either; + +// =========================================================================== + +using lex_inf = sequence, character<'n'>, character<'f'>>; +using lex_nan = sequence, character<'a'>, character<'n'>>; +using lex_special_float = sequence, either>; + +using lex_zero_prefixable_int = sequence>, unlimited>>; + +using lex_fractional_part = sequence, lex_zero_prefixable_int>; + +using lex_exponent_part = sequence, character<'E'>>, + maybe, lex_zero_prefixable_int>; + +using lex_float = either>>>>; + +// =========================================================================== + +using lex_true = sequence, character<'r'>, + character<'u'>, character<'e'>>; +using lex_false = sequence, character<'a'>, character<'l'>, + character<'s'>, character<'e'>>; +using lex_boolean = either; + +// =========================================================================== + +using lex_date_fullyear = repeat>; +using lex_date_month = repeat>; +using lex_date_mday = repeat>; +using lex_time_delim = either, character<'t'>, character<' '>>; +using lex_time_hour = repeat>; +using lex_time_minute = repeat>; +using lex_time_second = repeat>; +using lex_time_secfrac = sequence, + repeat>>; + +using lex_time_numoffset = sequence, character<'-'>>, + sequence, + lex_time_minute>>; +using lex_time_offset = either, character<'z'>, + lex_time_numoffset>; + +using lex_partial_time = sequence, + lex_time_minute, character<':'>, + lex_time_second, maybe>; +using lex_full_date = sequence, + lex_date_month, character<'-'>, + lex_date_mday>; +using lex_full_time = sequence; + +using lex_offset_date_time = sequence; +using lex_local_date_time = sequence; +using lex_local_date = lex_full_date; +using lex_local_time = lex_partial_time; + +// =========================================================================== + +using lex_quotation_mark = character<'"'>; +using lex_basic_unescaped = exclude, // 0x09 (tab) + in_range<0x0a, 0x1F>, // is allowed + character<0x22>, character<0x5C>, + character<0x7F>>>; + +using lex_escape = character<'\\'>; +using lex_escape_unicode_short = sequence, + repeat>>; +using lex_escape_unicode_long = sequence, + repeat>>; +using lex_escape_seq_char = either, character<'\\'>, + character<'b'>, character<'f'>, + character<'n'>, character<'r'>, + character<'t'>, + lex_escape_unicode_short, + lex_escape_unicode_long + >; +using lex_escaped = sequence; +using lex_basic_char = either; +using lex_basic_string = sequence, + lex_quotation_mark>; + +// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings +// are allowed to be used. +// After this, the following strings are *explicitly* allowed. +// - One or two `"`s in a multi-line basic string is allowed wherever it is. +// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter. +// - One or two `"`s can appear just before or after the delimiter. +// ```toml +// str4 = """Here are two quotation marks: "". Simple enough.""" +// str5 = """Here are three quotation marks: ""\".""" +// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" +// str7 = """"This," she said, "is just a pointless statement."""" +// ``` +// In the current implementation (v3.3.0), it is difficult to parse `str7` in +// the above example. It is difficult to recognize `"` at the end of string body +// collectly. It will be misunderstood as a `"""` delimiter and an additional, +// invalid `"`. Like this: +// ```console +// what(): [error] toml::parse_table: invalid line format +// --> hoge.toml +// | +// 13 | str7 = """"This," she said, "is just a pointless statement."""" +// | ^- expected newline, but got '"'. +// ``` +// As a quick workaround for this problem, `lex_ml_basic_string_delim` was +// splitted into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`. +// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s. +// In parse_ml_basic_string() function, the trailing `"`s will be attached to +// the string body. +// +using lex_ml_basic_string_delim = repeat>; +using lex_ml_basic_string_open = lex_ml_basic_string_delim; +using lex_ml_basic_string_close = sequence< + repeat>, + maybe, maybe + >; + +using lex_ml_basic_unescaped = exclude, // 0x09 + in_range<0x0a, 0x1F>, // is tab + character<0x5C>, // backslash + character<0x7F>, // DEL + lex_ml_basic_string_delim>>; + +using lex_ml_basic_escaped_newline = sequence< + lex_escape, maybe, lex_newline, + repeat, unlimited>>; + +using lex_ml_basic_char = either; +using lex_ml_basic_body = repeat, + unlimited>; +using lex_ml_basic_string = sequence; + +using lex_literal_char = exclude, + in_range<0x10, 0x19>, character<0x27>>>; +using lex_apostrophe = character<'\''>; +using lex_literal_string = sequence, + lex_apostrophe>; + +// the same reason as above. +using lex_ml_literal_string_delim = repeat>; +using lex_ml_literal_string_open = lex_ml_literal_string_delim; +using lex_ml_literal_string_close = sequence< + repeat>, + maybe, maybe + >; + +using lex_ml_literal_char = exclude, + in_range<0x10, 0x1F>, + character<0x7F>, + lex_ml_literal_string_delim>>; +using lex_ml_literal_body = repeat, + unlimited>; +using lex_ml_literal_string = sequence; + +using lex_string = either; + +// =========================================================================== + +using lex_comment_start_symbol = character<'#'>; +using lex_non_eol = either, exclude>>; +using lex_comment = sequence>; + +using lex_dot_sep = sequence, character<'.'>, maybe>; + +using lex_unquoted_key = repeat, character<'_'>>, + at_least<1>>; +using lex_quoted_key = either; +using lex_simple_key = either; +using lex_dotted_key = sequence, + at_least<1> + > + >; +using lex_key = either; + +using lex_keyval_sep = sequence, + character<'='>, + maybe>; + +using lex_std_table_open = character<'['>; +using lex_std_table_close = character<']'>; +using lex_std_table = sequence, + lex_key, + maybe, + lex_std_table_close>; + +using lex_array_table_open = sequence; +using lex_array_table_close = sequence; +using lex_array_table = sequence, + lex_key, + maybe, + lex_array_table_close>; + +} // detail +} // toml +#endif // TOML_LEXER_HPP diff --git a/src/toml/literal.hpp b/src/toml/literal.hpp new file mode 100644 index 00000000..1d338b7e --- /dev/null +++ b/src/toml/literal.hpp @@ -0,0 +1,112 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_LITERAL_HPP +#define TOML11_LITERAL_HPP +#include "parser.hpp" + +namespace toml +{ +inline namespace literals +{ +inline namespace toml_literals +{ + +// implementation +inline ::toml::basic_value +literal_internal_impl(::toml::detail::location loc) +{ + using value_type = ::toml::basic_value< + TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>; + // if there are some comments or empty lines, skip them. + using skip_line = ::toml::detail::repeat, + ::toml::detail::maybe<::toml::detail::lex_comment>, + ::toml::detail::lex_newline + >, ::toml::detail::at_least<1>>; + skip_line::invoke(loc); + + // if there are some whitespaces before a value, skip them. + using skip_ws = ::toml::detail::repeat< + ::toml::detail::lex_ws, ::toml::detail::at_least<1>>; + skip_ws::invoke(loc); + + // to distinguish arrays and tables, first check it is a table or not. + // + // "[1,2,3]"_toml; // this is an array + // "[table]"_toml; // a table that has an empty table named "table" inside. + // "[[1,2,3]]"_toml; // this is an array of arrays + // "[[table]]"_toml; // this is a table that has an array of tables inside. + // + // "[[1]]"_toml; // this can be both... (currently it becomes a table) + // "1 = [{}]"_toml; // this is a table that has an array of table named 1. + // "[[1,]]"_toml; // this is an array of arrays. + // "[[1],]"_toml; // this also. + + const auto the_front = loc.iter(); + + const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc); + loc.reset(the_front); + + const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc); + loc.reset(the_front); + + // If it is neither a table-key or a array-of-table-key, it may be a value. + if(!is_table_key && !is_aots_key) + { + if(auto data = ::toml::detail::parse_value(loc)) + { + return data.unwrap(); + } + } + + // Note that still it can be a table, because the literal might be something + // like the following. + // ```cpp + // R"( // c++11 raw string literals + // key = "value" + // int = 42 + // )"_toml; + // ``` + // It is a valid toml file. + // It should be parsed as if we parse a file with this content. + + if(auto data = ::toml::detail::parse_toml_file(loc)) + { + return data.unwrap(); + } + else // none of them. + { + throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); + } + +} + +inline ::toml::basic_value +operator"" _toml(const char* str, std::size_t len) +{ + ::toml::detail::location loc( + std::string("TOML literal encoded in a C++ code"), + std::vector(str, str + len)); + return literal_internal_impl(std::move(loc)); +} + +// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers. +// So here we use the feature test macro for `char8_t` itself. +#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L +// value of u8"" literal has been changed from char to char8_t and char8_t is +// NOT compatible to char +inline ::toml::basic_value +operator"" _toml(const char8_t* str, std::size_t len) +{ + ::toml::detail::location loc( + std::string("TOML literal encoded in a C++ code"), + std::vector(reinterpret_cast(str), + reinterpret_cast(str) + len)); + return literal_internal_impl(std::move(loc)); +} +#endif + +} // toml_literals +} // literals +} // toml +#endif//TOML11_LITERAL_HPP diff --git a/src/toml/macros.hpp b/src/toml/macros.hpp new file mode 100644 index 00000000..e8f91aec --- /dev/null +++ b/src/toml/macros.hpp @@ -0,0 +1,121 @@ +#ifndef TOML11_MACROS_HPP +#define TOML11_MACROS_HPP + +#define TOML11_STRINGIZE_AUX(x) #x +#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) + +#define TOML11_CONCATENATE_AUX(x, y) x##y +#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) + +// ============================================================================ +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE + +#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE + +// ---------------------------------------------------------------------------- +// TOML11_ARGS_SIZE + +#define TOML11_INDEX_RSEQ() \ + 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ + 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +#define TOML11_ARGS_SIZE_IMPL(\ + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ + ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ + ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ + ARG31, ARG32, N, ...) N +#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) +#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) + +// ---------------------------------------------------------------------------- +// TOML11_FOR_EACH_VA_ARGS + +#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) +#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) +#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) + +#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ + TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) + +// ---------------------------------------------------------------------------- +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE + +// use it in the following way. +// ```cpp +// namespace foo +// { +// struct Foo +// { +// std::string s; +// double d; +// int i; +// }; +// } // foo +// +// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) +// ``` +// And then you can use `toml::find(file, "foo");` +// +#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ + obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); + +#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ + v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; + +#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ + namespace toml { \ + template<> \ + struct from \ + { \ + template class T, \ + template class A> \ + static NAME from_toml(const basic_value& v) \ + { \ + NAME obj; \ + TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ + return obj; \ + } \ + }; \ + template<> \ + struct into \ + { \ + static value into_toml(const NAME& obj) \ + { \ + ::toml::value v = ::toml::table{}; \ + TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ + return v; \ + } \ + }; \ + } /* toml */ + +#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE + +#endif// TOML11_MACROS_HPP diff --git a/src/toml/parser.hpp b/src/toml/parser.hpp new file mode 100644 index 00000000..f6d6d83d --- /dev/null +++ b/src/toml/parser.hpp @@ -0,0 +1,2179 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_PARSER_HPP +#define TOML11_PARSER_HPP +#include +#include +#include + +#include "combinator.hpp" +#include "lexer.hpp" +#include "region.hpp" +#include "result.hpp" +#include "types.hpp" +#include "value.hpp" + +#ifndef TOML11_DISABLE_STD_FILESYSTEM +#ifdef __cpp_lib_filesystem +#if __has_include() +#define TOML11_HAS_STD_FILESYSTEM +#include +#endif // has_include() +#endif // __cpp_lib_filesystem +#endif // TOML11_DISABLE_STD_FILESYSTEM + +namespace toml +{ +namespace detail +{ + +inline result, std::string> +parse_boolean(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_boolean::invoke(loc)) + { + const auto reg = token.unwrap(); + if (reg.str() == "true") {return ok(std::make_pair(true, reg));} + else if(reg.str() == "false") {return ok(std::make_pair(false, reg));} + else // internal error. + { + throw internal_error(format_underline( + "toml::parse_boolean: internal error", + {{source_location(reg), "invalid token"}}), + source_location(reg)); + } + } + loc.reset(first); //rollback + return err(format_underline("toml::parse_boolean: ", + {{source_location(loc), "the next token is not a boolean"}})); +} + +inline result, std::string> +parse_binary_integer(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_bin_int::invoke(loc)) + { + auto str = token.unwrap().str(); + assert(str.size() > 2); // minimum -> 0b1 + integer retval(0), base(1); + for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i) + { + if (*i == '1'){retval += base; base *= 2;} + else if(*i == '0'){base *= 2;} + else if(*i == '_'){/* do nothing. */} + else // internal error. + { + throw internal_error(format_underline( + "toml::parse_integer: internal error", + {{source_location(token.unwrap()), "invalid token"}}), + source_location(loc)); + } + } + return ok(std::make_pair(retval, token.unwrap())); + } + loc.reset(first); + return err(format_underline("toml::parse_binary_integer:", + {{source_location(loc), "the next token is not an integer"}})); +} + +inline result, std::string> +parse_octal_integer(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_oct_int::invoke(loc)) + { + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix + + std::istringstream iss(str); + integer retval(0); + iss >> std::oct >> retval; + return ok(std::make_pair(retval, token.unwrap())); + } + loc.reset(first); + return err(format_underline("toml::parse_octal_integer:", + {{source_location(loc), "the next token is not an integer"}})); +} + +inline result, std::string> +parse_hexadecimal_integer(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_hex_int::invoke(loc)) + { + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix + + std::istringstream iss(str); + integer retval(0); + iss >> std::hex >> retval; + return ok(std::make_pair(retval, token.unwrap())); + } + loc.reset(first); + return err(format_underline("toml::parse_hexadecimal_integer", + {{source_location(loc), "the next token is not an integer"}})); +} + +inline result, std::string> +parse_integer(location& loc) +{ + const auto first = loc.iter(); + if(first != loc.end() && *first == '0') + { + const auto second = std::next(first); + if(second == loc.end()) // the token is just zero. + { + loc.advance(); + return ok(std::make_pair(0, region(loc, first, second))); + } + + if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100 + if(*second == 'o') {return parse_octal_integer (loc);} // 0o775 + if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE + + if(std::isdigit(*second)) + { + return err(format_underline("toml::parse_integer: " + "leading zero in an Integer is not allowed.", + {{source_location(loc), "leading zero"}})); + } + else if(std::isalpha(*second)) + { + return err(format_underline("toml::parse_integer: " + "unknown integer prefix appeared.", + {{source_location(loc), "none of 0x, 0o, 0b"}})); + } + } + + if(const auto token = lex_dec_int::invoke(loc)) + { + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + std::istringstream iss(str); + integer retval(0); + iss >> retval; + return ok(std::make_pair(retval, token.unwrap())); + } + loc.reset(first); + return err(format_underline("toml::parse_integer: ", + {{source_location(loc), "the next token is not an integer"}})); +} + +inline result, std::string> +parse_floating(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_float::invoke(loc)) + { + auto str = token.unwrap().str(); + if(str == "inf" || str == "+inf") + { + if(std::numeric_limits::has_infinity) + { + return ok(std::make_pair( + std::numeric_limits::infinity(), token.unwrap())); + } + else + { + throw std::domain_error("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } + } + else if(str == "-inf") + { + if(std::numeric_limits::has_infinity) + { + return ok(std::make_pair( + -std::numeric_limits::infinity(), token.unwrap())); + } + else + { + throw std::domain_error("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } + } + else if(str == "nan" || str == "+nan") + { + if(std::numeric_limits::has_quiet_NaN) + { + return ok(std::make_pair( + std::numeric_limits::quiet_NaN(), token.unwrap())); + } + else if(std::numeric_limits::has_signaling_NaN) + { + return ok(std::make_pair( + std::numeric_limits::signaling_NaN(), token.unwrap())); + } + else + { + throw std::domain_error("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } + } + else if(str == "-nan") + { + if(std::numeric_limits::has_quiet_NaN) + { + return ok(std::make_pair( + -std::numeric_limits::quiet_NaN(), token.unwrap())); + } + else if(std::numeric_limits::has_signaling_NaN) + { + return ok(std::make_pair( + -std::numeric_limits::signaling_NaN(), token.unwrap())); + } + else + { + throw std::domain_error("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } + } + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + std::istringstream iss(str); + floating v(0.0); + iss >> v; + return ok(std::make_pair(v, token.unwrap())); + } + loc.reset(first); + return err(format_underline("toml::parse_floating: ", + {{source_location(loc), "the next token is not a float"}})); +} + +inline std::string read_utf8_codepoint(const region& reg, const location& loc) +{ + const auto str = reg.str().substr(1); + std::uint_least32_t codepoint; + std::istringstream iss(str); + iss >> std::hex >> codepoint; + + const auto to_char = [](const std::uint_least32_t i) noexcept -> char { + const auto uc = static_cast(i); + return *reinterpret_cast(std::addressof(uc)); + }; + + std::string character; + if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. + { + character += static_cast(codepoint); + } + else if(codepoint < 0x800) //U+0080 ... U+07FF + { + // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 + character += to_char(0xC0| codepoint >> 6); + character += to_char(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x10000) // U+0800...U+FFFF + { + if(0xD800 <= codepoint && codepoint <= 0xDFFF) + { + throw syntax_error(format_underline( + "toml::read_utf8_codepoint: codepoints in the range " + "[0xD800, 0xDFFF] are not valid UTF-8.", {{ + source_location(loc), "not a valid UTF-8 codepoint" + }}), source_location(loc)); + } + assert(codepoint < 0xD800 || 0xDFFF < codepoint); + // 1110yyyy 10yxxxxx 10xxxxxx + character += to_char(0xE0| codepoint >> 12); + character += to_char(0x80|(codepoint >> 6 & 0x3F)); + character += to_char(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x110000) // U+010000 ... U+10FFFF + { + // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx + character += to_char(0xF0| codepoint >> 18); + character += to_char(0x80|(codepoint >> 12 & 0x3F)); + character += to_char(0x80|(codepoint >> 6 & 0x3F)); + character += to_char(0x80|(codepoint & 0x3F)); + } + else // out of UTF-8 region + { + throw syntax_error(format_underline("toml::read_utf8_codepoint:" + " input codepoint is too large.", + {{source_location(loc), "should be in [0x00..0x10FFFF]"}}), + source_location(loc)); + } + return character; +} + +inline result parse_escape_sequence(location& loc) +{ + const auto first = loc.iter(); + if(first == loc.end() || *first != '\\') + { + return err(format_underline("toml::parse_escape_sequence: ", {{ + source_location(loc), "the next token is not a backslash \"\\\""}})); + } + loc.advance(); + switch(*loc.iter()) + { + case '\\':{loc.advance(); return ok(std::string("\\"));} + case '"' :{loc.advance(); return ok(std::string("\""));} + case 'b' :{loc.advance(); return ok(std::string("\b"));} + case 't' :{loc.advance(); return ok(std::string("\t"));} + case 'n' :{loc.advance(); return ok(std::string("\n"));} + case 'f' :{loc.advance(); return ok(std::string("\f"));} + case 'r' :{loc.advance(); return ok(std::string("\r"));} + case 'u' : + { + if(const auto token = lex_escape_unicode_short::invoke(loc)) + { + return ok(read_utf8_codepoint(token.unwrap(), loc)); + } + else + { + return err(format_underline("parse_escape_sequence: " + "invalid token found in UTF-8 codepoint uXXXX.", + {{source_location(loc), "here"}})); + } + } + case 'U': + { + if(const auto token = lex_escape_unicode_long::invoke(loc)) + { + return ok(read_utf8_codepoint(token.unwrap(), loc)); + } + else + { + return err(format_underline("parse_escape_sequence: " + "invalid token found in UTF-8 codepoint Uxxxxxxxx", + {{source_location(loc), "here"}})); + } + } + } + + const auto msg = format_underline("parse_escape_sequence: " + "unknown escape sequence appeared.", {{source_location(loc), + "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, + /* Hints = */{"if you want to write backslash as just one backslash, " + "use literal string like: regex = '<\\i\\c*\\s*>'"}); + loc.reset(first); + return err(msg); +} + +inline result, std::string> +parse_ml_basic_string(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_ml_basic_string::invoke(loc)) + { + auto inner_loc = loc; + inner_loc.reset(first); + + std::string retval; + retval.reserve(token.unwrap().size()); + + auto delim = lex_ml_basic_string_open::invoke(inner_loc); + if(!delim) + { + throw internal_error(format_underline( + "parse_ml_basic_string: invalid token", + {{source_location(inner_loc), "should be \"\"\""}}), + source_location(inner_loc)); + } + // immediate newline is ignored (if exists) + /* discard return value */ lex_newline::invoke(inner_loc); + + delim = none(); + while(!delim) + { + using lex_unescaped_seq = repeat< + either, unlimited>; + if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) + { + retval += unescaped.unwrap().str(); + } + if(auto escaped = parse_escape_sequence(inner_loc)) + { + retval += escaped.unwrap(); + } + if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc)) + { + // ignore newline after escape until next non-ws char + } + if(inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline( + "parse_ml_basic_string: unexpected end of region", + {{source_location(inner_loc), "not sufficient token"}}), + source_location(inner_loc)); + } + delim = lex_ml_basic_string_close::invoke(inner_loc); + } + // `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s + // at just before the delimiter. Here, we need to attach `"`s at the + // end of the string body, if it exists. + // For detail, see the definition of `lex_ml_basic_string_close`. + assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(), + [](const char c) noexcept {return c == '\"';})); + switch(delim.unwrap().size()) + { + case 3: {break;} + case 4: {retval += "\""; break;} + case 5: {retval += "\"\""; break;} + default: + { + throw internal_error(format_underline( + "parse_ml_basic_string: closing delimiter has invalid length", + {{source_location(inner_loc), "end of this"}}), + source_location(inner_loc)); + } + } + return ok(std::make_pair(toml::string(retval), token.unwrap())); + } + else + { + loc.reset(first); + return err(format_underline("toml::parse_ml_basic_string: " + "the next token is not a valid multiline string", + {{source_location(loc), "here"}})); + } +} + +inline result, std::string> +parse_basic_string(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_basic_string::invoke(loc)) + { + auto inner_loc = loc; + inner_loc.reset(first); + + auto quot = lex_quotation_mark::invoke(inner_loc); + if(!quot) + { + throw internal_error(format_underline("parse_basic_string: " + "invalid token", {{source_location(inner_loc), "should be \""}}), + source_location(inner_loc)); + } + + std::string retval; + retval.reserve(token.unwrap().size()); + + quot = none(); + while(!quot) + { + using lex_unescaped_seq = repeat; + if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) + { + retval += unescaped.unwrap().str(); + } + if(auto escaped = parse_escape_sequence(inner_loc)) + { + retval += escaped.unwrap(); + } + if(inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline( + "parse_basic_string: unexpected end of region", + {{source_location(inner_loc), "not sufficient token"}}), + source_location(inner_loc)); + } + quot = lex_quotation_mark::invoke(inner_loc); + } + return ok(std::make_pair(toml::string(retval), token.unwrap())); + } + else + { + loc.reset(first); // rollback + return err(format_underline("toml::parse_basic_string: " + "the next token is not a valid string", + {{source_location(loc), "here"}})); + } +} + +inline result, std::string> +parse_ml_literal_string(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_ml_literal_string::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_ml_literal_string_open::invoke(inner_loc); + if(!open) + { + throw internal_error(format_underline( + "parse_ml_literal_string: invalid token", + {{source_location(inner_loc), "should be '''"}}), + source_location(inner_loc)); + } + // immediate newline is ignored (if exists) + /* discard return value */ lex_newline::invoke(inner_loc); + + const auto body = lex_ml_literal_body::invoke(inner_loc); + + const auto close = lex_ml_literal_string_close::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline( + "parse_ml_literal_string: invalid token", + {{source_location(inner_loc), "should be '''"}}), + source_location(inner_loc)); + } + // `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s + // at just before the delimiter. Here, we need to attach `'`s at the + // end of the string body, if it exists. + // For detail, see the definition of `lex_ml_basic_string_close`. + + std::string retval = body.unwrap().str(); + assert(std::all_of(close.unwrap().first(), close.unwrap().last(), + [](const char c) noexcept {return c == '\'';})); + switch(close.unwrap().size()) + { + case 3: {break;} + case 4: {retval += "'"; break;} + case 5: {retval += "''"; break;} + default: + { + throw internal_error(format_underline( + "parse_ml_literal_string: closing delimiter has invalid length", + {{source_location(inner_loc), "end of this"}}), + source_location(inner_loc)); + } + } + return ok(std::make_pair(toml::string(retval, toml::string_t::literal), + token.unwrap())); + } + else + { + loc.reset(first); // rollback + return err(format_underline("toml::parse_ml_literal_string: " + "the next token is not a valid multiline literal string", + {{source_location(loc), "here"}})); + } +} + +inline result, std::string> +parse_literal_string(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_literal_string::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_apostrophe::invoke(inner_loc); + if(!open) + { + throw internal_error(format_underline( + "parse_literal_string: invalid token", + {{source_location(inner_loc), "should be '"}}), + source_location(inner_loc)); + } + + const auto body = repeat::invoke(inner_loc); + + const auto close = lex_apostrophe::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline( + "parse_literal_string: invalid token", + {{source_location(inner_loc), "should be '"}}), + source_location(inner_loc)); + } + return ok(std::make_pair( + toml::string(body.unwrap().str(), toml::string_t::literal), + token.unwrap())); + } + else + { + loc.reset(first); // rollback + return err(format_underline("toml::parse_literal_string: " + "the next token is not a valid literal string", + {{source_location(loc), "here"}})); + } +} + +inline result, std::string> +parse_string(location& loc) +{ + if(loc.iter() != loc.end() && *(loc.iter()) == '"') + { + if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' && + loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"') + { + return parse_ml_basic_string(loc); + } + else + { + return parse_basic_string(loc); + } + } + else if(loc.iter() != loc.end() && *(loc.iter()) == '\'') + { + if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' && + loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'') + { + return parse_ml_literal_string(loc); + } + else + { + return parse_literal_string(loc); + } + } + return err(format_underline("toml::parse_string: ", + {{source_location(loc), "the next token is not a string"}})); +} + +inline result, std::string> +parse_local_date(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_local_date::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto y = lex_date_fullyear::invoke(inner_loc); + if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') + { + throw internal_error(format_underline( + "toml::parse_inner_local_date: invalid year format", + {{source_location(inner_loc), "should be `-`"}}), + source_location(inner_loc)); + } + inner_loc.advance(); + const auto m = lex_date_month::invoke(inner_loc); + if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') + { + throw internal_error(format_underline( + "toml::parse_local_date: invalid month format", + {{source_location(inner_loc), "should be `-`"}}), + source_location(inner_loc)); + } + inner_loc.advance(); + const auto d = lex_date_mday::invoke(inner_loc); + if(!d) + { + throw internal_error(format_underline( + "toml::parse_local_date: invalid day format", + {{source_location(inner_loc), "here"}}), + source_location(inner_loc)); + } + return ok(std::make_pair(local_date( + static_cast(from_string(y.unwrap().str(), 0)), + static_cast( + static_cast(from_string(m.unwrap().str(), 0)-1)), + static_cast(from_string(d.unwrap().str(), 0))), + token.unwrap())); + } + else + { + loc.reset(first); + return err(format_underline("toml::parse_local_date: ", + {{source_location(loc), "the next token is not a local_date"}})); + } +} + +inline result, std::string> +parse_local_time(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_local_time::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto h = lex_time_hour::invoke(inner_loc); + if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') + { + throw internal_error(format_underline( + "toml::parse_local_time: invalid year format", + {{source_location(inner_loc), "should be `:`"}}), + source_location(inner_loc)); + } + inner_loc.advance(); + const auto m = lex_time_minute::invoke(inner_loc); + if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') + { + throw internal_error(format_underline( + "toml::parse_local_time: invalid month format", + {{source_location(inner_loc), "should be `:`"}}), + source_location(inner_loc)); + } + inner_loc.advance(); + const auto s = lex_time_second::invoke(inner_loc); + if(!s) + { + throw internal_error(format_underline( + "toml::parse_local_time: invalid second format", + {{source_location(inner_loc), "here"}}), + source_location(inner_loc)); + } + local_time time( + from_string(h.unwrap().str(), 0), + from_string(m.unwrap().str(), 0), + from_string(s.unwrap().str(), 0), 0, 0); + + const auto before_secfrac = inner_loc.iter(); + if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) + { + auto sf = secfrac.unwrap().str(); + sf.erase(sf.begin()); // sf.front() == '.' + switch(sf.size() % 3) + { + case 2: sf += '0'; break; + case 1: sf += "00"; break; + case 0: break; + default: break; + } + if(sf.size() >= 9) + { + time.millisecond = from_string(sf.substr(0, 3), 0u); + time.microsecond = from_string(sf.substr(3, 3), 0u); + time.nanosecond = from_string(sf.substr(6, 3), 0u); + } + else if(sf.size() >= 6) + { + time.millisecond = from_string(sf.substr(0, 3), 0u); + time.microsecond = from_string(sf.substr(3, 3), 0u); + } + else if(sf.size() >= 3) + { + time.millisecond = from_string(sf, 0u); + time.microsecond = 0u; + } + } + else + { + if(before_secfrac != inner_loc.iter()) + { + throw internal_error(format_underline( + "toml::parse_local_time: invalid subsecond format", + {{source_location(inner_loc), "here"}}), + source_location(inner_loc)); + } + } + return ok(std::make_pair(time, token.unwrap())); + } + else + { + loc.reset(first); + return err(format_underline("toml::parse_local_time: ", + {{source_location(loc), "the next token is not a local_time"}})); + } +} + +inline result, std::string> +parse_local_datetime(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_local_date_time::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + const auto date = parse_local_date(inner_loc); + if(!date || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline( + "toml::parse_local_datetime: invalid datetime format", + {{source_location(inner_loc), "date, not datetime"}}), + source_location(inner_loc)); + } + const char delim = *(inner_loc.iter()); + if(delim != 'T' && delim != 't' && delim != ' ') + { + throw internal_error(format_underline( + "toml::parse_local_datetime: invalid datetime format", + {{source_location(inner_loc), "should be `T` or ` ` (space)"}}), + source_location(inner_loc)); + } + inner_loc.advance(); + const auto time = parse_local_time(inner_loc); + if(!time) + { + throw internal_error(format_underline( + "toml::parse_local_datetime: invalid datetime format", + {{source_location(inner_loc), "invalid time fomrat"}}), + source_location(inner_loc)); + } + return ok(std::make_pair( + local_datetime(date.unwrap().first, time.unwrap().first), + token.unwrap())); + } + else + { + loc.reset(first); + return err(format_underline("toml::parse_local_datetime: ", + {{source_location(loc), "the next token is not a local_datetime"}})); + } +} + +inline result, std::string> +parse_offset_datetime(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_offset_date_time::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + const auto datetime = parse_local_datetime(inner_loc); + if(!datetime || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline( + "toml::parse_offset_datetime: invalid datetime format", + {{source_location(inner_loc), "date, not datetime"}}), + source_location(inner_loc)); + } + time_offset offset(0, 0); + if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) + { + const auto str = ofs.unwrap().str(); + if(str.front() == '+') + { + offset = time_offset(from_string(str.substr(1,2), 0), + from_string(str.substr(4,2), 0)); + } + else + { + offset = time_offset(-from_string(str.substr(1,2), 0), + -from_string(str.substr(4,2), 0)); + } + } + else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') + { + throw internal_error(format_underline( + "toml::parse_offset_datetime: invalid datetime format", + {{source_location(inner_loc), "should be `Z` or `+HH:MM`"}}), + source_location(inner_loc)); + } + return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), + token.unwrap())); + } + else + { + loc.reset(first); + return err(format_underline("toml::parse_offset_datetime: ", + {{source_location(loc), "the next token is not a offset_datetime"}})); + } +} + +inline result, std::string> +parse_simple_key(location& loc) +{ + if(const auto bstr = parse_basic_string(loc)) + { + return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second)); + } + if(const auto lstr = parse_literal_string(loc)) + { + return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second)); + } + if(const auto bare = lex_unquoted_key::invoke(loc)) + { + const auto reg = bare.unwrap(); + return ok(std::make_pair(reg.str(), reg)); + } + return err(format_underline("toml::parse_simple_key: ", + {{source_location(loc), "the next token is not a simple key"}})); +} + +// dotted key become vector of keys +inline result, region>, std::string> +parse_key(location& loc) +{ + const auto first = loc.iter(); + // dotted key -> `foo.bar.baz` where several single keys are chained by + // dots. Whitespaces between keys and dots are allowed. + if(const auto token = lex_dotted_key::invoke(loc)) + { + const auto reg = token.unwrap(); + location inner_loc(loc.name(), reg.str()); + std::vector keys; + + while(inner_loc.iter() != inner_loc.end()) + { + lex_ws::invoke(inner_loc); + if(const auto k = parse_simple_key(inner_loc)) + { + keys.push_back(k.unwrap().first); + } + else + { + throw internal_error(format_underline( + "toml::detail::parse_key: dotted key contains invalid key", + {{source_location(inner_loc), k.unwrap_err()}}), + source_location(inner_loc)); + } + + lex_ws::invoke(inner_loc); + if(inner_loc.iter() == inner_loc.end()) + { + break; + } + else if(*inner_loc.iter() == '.') + { + inner_loc.advance(); // to skip `.` + } + else + { + throw internal_error(format_underline("toml::parse_key: " + "dotted key contains invalid key ", + {{source_location(inner_loc), "should be `.`"}}), + source_location(inner_loc)); + } + } + return ok(std::make_pair(keys, reg)); + } + loc.reset(first); + + // simple_key: a single (basic_string|literal_string|bare key) + if(const auto smpl = parse_simple_key(loc)) + { + return ok(std::make_pair(std::vector(1, smpl.unwrap().first), + smpl.unwrap().second)); + } + return err(format_underline("toml::parse_key: an invalid key appeared.", + {{source_location(loc), "is not a valid key"}}, { + "bare keys : non-empty strings composed only of [A-Za-z0-9_-].", + "quoted keys: same as \"basic strings\" or 'literal strings'.", + "dotted keys: sequence of bare or quoted keys joined with a dot." + })); +} + +// forward-decl to implement parse_array and parse_table +template +result parse_value(location&); + +template +result, std::string> +parse_array(location& loc) +{ + using value_type = Value; + using array_type = typename value_type::array_type; + + const auto first = loc.iter(); + if(loc.iter() == loc.end()) + { + return err("toml::parse_array: input is empty"); + } + if(*loc.iter() != '[') + { + return err("toml::parse_array: token is not an array"); + } + loc.advance(); + + using lex_ws_comment_newline = repeat< + either, unlimited>; + + array_type retval; + while(loc.iter() != loc.end()) + { + lex_ws_comment_newline::invoke(loc); // skip + + if(loc.iter() != loc.end() && *loc.iter() == ']') + { + loc.advance(); // skip ']' + return ok(std::make_pair(retval, + region(loc, first, loc.iter()))); + } + + if(auto val = parse_value(loc)) + { + // After TOML v1.0.0-rc.1, array becomes to be able to have values + // with different types. So here we will omit this by default. + // + // But some of the test-suite checks if the parser accepts a hetero- + // geneous arrays, so we keep this for a while. +#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS + if(!retval.empty() && retval.front().type() != val.as_ok().type()) + { + auto array_start_loc = loc; + array_start_loc.reset(first); + + throw syntax_error(format_underline("toml::parse_array: " + "type of elements should be the same each other.", { + {source_location(array_start_loc), "array starts here"}, + { + retval.front().location(), + "value has type " + stringize(retval.front().type()) + }, + { + val.unwrap().location(), + "value has different type, " + stringize(val.unwrap().type()) + } + }), source_location(loc)); + } +#endif + retval.push_back(std::move(val.unwrap())); + } + else + { + auto array_start_loc = loc; + array_start_loc.reset(first); + + throw syntax_error(format_underline("toml::parse_array: " + "value having invalid format appeared in an array", { + {source_location(array_start_loc), "array starts here"}, + {source_location(loc), "it is not a valid value."} + }), source_location(loc)); + } + + using lex_array_separator = sequence, character<','>>; + const auto sp = lex_array_separator::invoke(loc); + if(!sp) + { + lex_ws_comment_newline::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == ']') + { + loc.advance(); // skip ']' + return ok(std::make_pair(retval, + region(loc, first, loc.iter()))); + } + else + { + auto array_start_loc = loc; + array_start_loc.reset(first); + + throw syntax_error(format_underline("toml::parse_array:" + " missing array separator `,` after a value", { + {source_location(array_start_loc), "array starts here"}, + {source_location(loc), "should be `,`"} + }), source_location(loc)); + } + } + } + loc.reset(first); + throw syntax_error(format_underline("toml::parse_array: " + "array did not closed by `]`", + {{source_location(loc), "should be closed"}}), + source_location(loc)); +} + +template +result, region>, Value>, std::string> +parse_key_value_pair(location& loc) +{ + using value_type = Value; + + const auto first = loc.iter(); + auto key_reg = parse_key(loc); + if(!key_reg) + { + std::string msg = std::move(key_reg.unwrap_err()); + // if the next token is keyvalue-separator, it means that there are no + // key. then we need to show error as "empty key is not allowed". + if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) + { + loc.reset(first); + msg = format_underline("toml::parse_key_value_pair: " + "empty key is not allowed.", + {{source_location(loc), "key expected before '='"}}); + } + return err(std::move(msg)); + } + + const auto kvsp = lex_keyval_sep::invoke(loc); + if(!kvsp) + { + std::string msg; + // if the line contains '=' after the invalid sequence, possibly the + // error is in the key (like, invalid character in bare key). + const auto line_end = std::find(loc.iter(), loc.end(), '\n'); + if(std::find(loc.iter(), line_end, '=') != line_end) + { + msg = format_underline("toml::parse_key_value_pair: " + "invalid format for key", + {{source_location(loc), "invalid character in key"}}, + {"Did you forget '.' to separate dotted-key?", + "Allowed characters for bare key are [0-9a-zA-Z_-]."}); + } + else // if not, the error is lack of key-value separator. + { + msg = format_underline("toml::parse_key_value_pair: " + "missing key-value separator `=`", + {{source_location(loc), "should be `=`"}}); + } + loc.reset(first); + return err(std::move(msg)); + } + + const auto after_kvsp = loc.iter(); // err msg + auto val = parse_value(loc); + if(!val) + { + std::string msg; + loc.reset(after_kvsp); + // check there is something not a comment/whitespace after `=` + if(sequence, maybe, lex_newline>::invoke(loc)) + { + loc.reset(after_kvsp); + msg = format_underline("toml::parse_key_value_pair: " + "missing value after key-value separator '='", + {{source_location(loc), "expected value, but got nothing"}}); + } + else // there is something not a comment/whitespace, so invalid format. + { + msg = std::move(val.unwrap_err()); + } + loc.reset(first); + return err(msg); + } + return ok(std::make_pair(std::move(key_reg.unwrap()), + std::move(val.unwrap()))); +} + +// for error messages. +template +std::string format_dotted_keys(InputIterator first, const InputIterator last) +{ + static_assert(std::is_same::value_type>::value,""); + + std::string retval(*first++); + for(; first != last; ++first) + { + retval += '.'; + retval += *first; + } + return retval; +} + +// forward decl for is_valid_forward_table_definition +result, region>, std::string> +parse_table_key(location& loc); + +// The following toml file is allowed. +// ```toml +// [a.b.c] # here, table `a` has element `b`. +// foo = "bar" +// [a] # merge a = {baz = "qux"} to a = {b = {...}} +// baz = "qux" +// ``` +// But the following is not allowed. +// ```toml +// [a] +// b.c.foo = "bar" +// [a] # error! the same table [a] defined! +// baz = "qux" +// ``` +// The following is neither allowed. +// ```toml +// a = { b.c.foo = "bar"} +// [a] # error! the same table [a] defined! +// baz = "qux" +// ``` +// Here, it parses region of `tab->at(k)` as a table key and check the depth +// of the key. If the key region points deeper node, it would be allowed. +// Otherwise, the key points the same node. It would be rejected. +template +bool is_valid_forward_table_definition(const Value& fwd, + Iterator key_first, Iterator key_curr, Iterator key_last) +{ + std::string internal = ""; + if(const auto ptr = detail::get_region(fwd)) + { + internal = ptr->str(); + } + location def("internal", std::move(internal)); + if(const auto tabkeys = parse_table_key(def)) + { + // table keys always contains all the nodes from the root. + const auto& tks = tabkeys.unwrap().first; + if(std::size_t(std::distance(key_first, key_last)) == tks.size() && + std::equal(tks.begin(), tks.end(), key_first)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + if(const auto dotkeys = parse_key(def)) + { + // consider the following case. + // [a] + // b.c = {d = 42} + // [a.b.c] + // e = 2.71 + // this defines the table [a.b.c] twice. no? + + // a dotted key starts from the node representing a table in which the + // dotted key belongs to. + const auto& dks = dotkeys.unwrap().first; + if(std::size_t(std::distance(key_curr, key_last)) == dks.size() && + std::equal(dks.begin(), dks.end(), key_curr)) + { + // the keys are equivalent. it is not allowed. + return false; + } + // the keys are not equivalent. it is allowed. + return true; + } + return false; +} + +template +result +insert_nested_key(typename Value::table_type& root, const Value& v, + InputIterator iter, const InputIterator last, + region key_reg, + const bool is_array_of_table = false) +{ + static_assert(std::is_same::value_type>::value,""); + + using value_type = Value; + using table_type = typename value_type::table_type; + using array_type = typename value_type::array_type; + + const auto first = iter; + assert(iter != last); + + table_type* tab = std::addressof(root); + for(; iter != last; ++iter) // search recursively + { + const key& k = *iter; + if(std::next(iter) == last) // k is the last key + { + // XXX if the value is array-of-tables, there can be several + // tables that are in the same array. in that case, we need to + // find the last element and insert it to there. + if(is_array_of_table) + { + if(tab->count(k) == 1) // there is already an array of table + { + if(tab->at(k).is_table()) + { + // show special err msg for conflicting table + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: array of table (\"", + format_dotted_keys(first, last), + "\") cannot be defined"), { + {tab->at(k).location(), "table already defined"}, + {v.location(), "this conflicts with the previous table"} + }), v.location()); + } + else if(!(tab->at(k).is_array())) + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") collides with" + " existing value"), { + {tab->at(k).location(), + concat_to_string("this ", tab->at(k).type(), + " value already exists")}, + {v.location(), + "while inserting this array-of-tables"} + }), v.location()); + } + // the above if-else-if checks tab->at(k) is an array + auto& a = tab->at(k).as_array(); + if(!(a.front().is_table())) + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") collides with" + " existing value"), { + {tab->at(k).location(), + concat_to_string("this ", tab->at(k).type(), + " value already exists")}, + {v.location(), + "while inserting this array-of-tables"} + }), v.location()); + } + // avoid conflicting array of table like the following. + // ```toml + // a = [{b = 42}] # define a as an array of *inline* tables + // [[a]] # a is an array of *multi-line* tables + // b = 54 + // ``` + // Here, from the type information, these cannot be detected + // bacause inline table is also a table. + // But toml v0.5.0 explicitly says it is invalid. The above + // array-of-tables has a static size and appending to the + // array is invalid. + // In this library, multi-line table value has a region + // that points to the key of the table (e.g. [[a]]). By + // comparing the first two letters in key, we can detect + // the array-of-table is inline or multiline. + if(const auto ptr = detail::get_region(a.front())) + { + if(ptr->str().substr(0,2) != "[[") + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") collides " + "with existing array-of-tables"), { + {tab->at(k).location(), + concat_to_string("this ", tab->at(k).type(), + " value has static size")}, + {v.location(), + "appending it to the statically sized array"} + }), v.location()); + } + } + a.push_back(v); + return ok(true); + } + else // if not, we need to create the array of table + { + // XXX: Consider the following array of tables. + // ```toml + // # This is a comment. + // [[aot]] + // foo = "bar" + // ``` + // Here, the comment is for `aot`. But here, actually two + // values are defined. An array that contains tables, named + // `aot`, and the 0th element of the `aot`, `{foo = "bar"}`. + // Those two are different from each other. But both of them + // points to the same portion of the TOML file, `[[aot]]`, + // so `key_reg.comments()` returns `# This is a comment`. + // If it is assigned as a comment of `aot` defined here, the + // comment will be duplicated. Both the `aot` itself and + // the 0-th element will have the same comment. This causes + // "duplication of the same comments" bug when the data is + // serialized. + // Next, consider the following. + // ```toml + // # comment 1 + // aot = [ + // # coment 2 + // {foo = "bar"}, + // ] + // ``` + // In this case, we can distinguish those two comments. So + // here we need to add "comment 1" to the `aot` and + // "comment 2" to the 0th element of that. + // To distinguish those two, we check the key region. + std::vector comments{/* empty by default */}; + if(key_reg.str().substr(0, 2) != "[[") + { + comments = key_reg.comments(); + } + value_type aot(array_type(1, v), key_reg, std::move(comments)); + tab->insert(std::make_pair(k, aot)); + return ok(true); + } + } // end if(array of table) + + if(tab->count(k) == 1) + { + if(tab->at(k).is_table() && v.is_table()) + { + if(!is_valid_forward_table_definition( + tab->at(k), first, iter, last)) + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: table (\"", + format_dotted_keys(first, last), + "\") already exists."), { + {tab->at(k).location(), "table already exists here"}, + {v.location(), "table defined twice"} + }), v.location()); + } + // to allow the following toml file. + // [a.b.c] + // d = 42 + // [a] + // e = 2.71 + auto& t = tab->at(k).as_table(); + for(const auto& kv : v.as_table()) + { + t[kv.first] = kv.second; + } + detail::change_region(tab->at(k), key_reg); + return ok(true); + } + else if(v.is_table() && + tab->at(k).is_array() && + tab->at(k).as_array().size() > 0 && + tab->at(k).as_array().front().is_table()) + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: array of tables (\"", + format_dotted_keys(first, last), "\") already exists."), { + {tab->at(k).location(), "array of tables defined here"}, + {v.location(), "table conflicts with the previous array of table"} + }), v.location()); + } + else + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: value (\"", + format_dotted_keys(first, last), "\") already exists."), { + {tab->at(k).location(), "value already exists here"}, + {v.location(), "value defined twice"} + }), v.location()); + } + } + tab->insert(std::make_pair(k, v)); + return ok(true); + } + else // k is not the last one, we should insert recursively + { + // if there is no corresponding value, insert it first. + // related: you don't need to write + // # [x] + // # [x.y] + // to write + // [x.y.z] + if(tab->count(k) == 0) + { + // a table that is defined implicitly doesn't have any comments. + (*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/}); + } + + // type checking... + if(tab->at(k).is_table()) + { + // According to toml-lang/toml:36d3091b3 "Clarify that inline + // tables are immutable", check if it adds key-value pair to an + // inline table. + if(const auto* ptr = get_region(tab->at(k))) + { + // here, if the value is a (multi-line) table, the region + // should be something like `[table-name]`. + if(ptr->front() == '{') + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: inserting to an inline table (", + format_dotted_keys(first, std::next(iter)), + ") but inline tables are immutable"), { + {tab->at(k).location(), "inline tables are immutable"}, + {v.location(), "inserting this"} + }), v.location()); + } + } + tab = std::addressof((*tab)[k].as_table()); + } + else if(tab->at(k).is_array()) // inserting to array-of-tables? + { + auto& a = (*tab)[k].as_array(); + if(!a.back().is_table()) + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: target (", + format_dotted_keys(first, std::next(iter)), + ") is neither table nor an array of tables"), { + {a.back().location(), concat_to_string( + "actual type is ", a.back().type())}, + {v.location(), "inserting this"} + }), v.location()); + } + tab = std::addressof(a.back().as_table()); + } + else + { + throw syntax_error(format_underline(concat_to_string( + "toml::insert_value: target (", + format_dotted_keys(first, std::next(iter)), + ") is neither table nor an array of tables"), { + {tab->at(k).location(), concat_to_string( + "actual type is ", tab->at(k).type())}, + {v.location(), "inserting this"} + }), v.location()); + } + } + } + return err(std::string("toml::detail::insert_nested_key: never reach here")); +} + +template +result, std::string> +parse_inline_table(location& loc) +{ + using value_type = Value; + using table_type = typename value_type::table_type; + + const auto first = loc.iter(); + table_type retval; + if(!(loc.iter() != loc.end() && *loc.iter() == '{')) + { + return err(format_underline("toml::parse_inline_table: ", + {{source_location(loc), "the next token is not an inline table"}})); + } + loc.advance(); + // it starts from "{". it should be formatted as inline-table + while(loc.iter() != loc.end()) + { + maybe::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == '}') + { + loc.advance(); // skip `}` + return ok(std::make_pair(retval, + region(loc, first, loc.iter()))); + } + + const auto kv_r = parse_key_value_pair(loc); + if(!kv_r) + { + return err(kv_r.unwrap_err()); + } + const auto& kvpair = kv_r.unwrap(); + const std::vector& keys = kvpair.first.first; + const auto& key_reg = kvpair.first.second; + const value_type& val = kvpair.second; + + const auto inserted = + insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg); + if(!inserted) + { + throw internal_error("toml::parse_inline_table: " + "failed to insert value into table: " + inserted.unwrap_err(), + source_location(loc)); + } + + using lex_table_separator = sequence, character<','>>; + const auto sp = lex_table_separator::invoke(loc); + if(!sp) + { + maybe::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == '}') + { + loc.advance(); // skip `}` + return ok(std::make_pair( + retval, region(loc, first, loc.iter()))); + } + else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n') + { + throw syntax_error(format_underline( + "toml::parse_inline_table: missing curly brace `}`", + {{source_location(loc), "should be `}`"}}), + source_location(loc)); + } + else + { + throw syntax_error(format_underline( + "toml::parse_inline_table: missing table separator `,` ", + {{source_location(loc), "should be `,`"}}), + source_location(loc)); + } + } + } + loc.reset(first); + throw syntax_error(format_underline("toml::parse_inline_table: " + "inline table did not closed by `}`", + {{source_location(loc), "should be closed"}}), + source_location(loc)); +} + +inline result guess_number_type(const location& l) +{ + // This function tries to find some (common) mistakes by checking characters + // that follows the last character of a value. But it is often difficult + // because some non-newline characters can appear after a value. E.g. + // spaces, tabs, commas (in an array or inline table), closing brackets + // (of an array or inline table), comment-sign (#). Since this function + // does not parse further, those characters are always allowed to be there. + location loc = l; + + if(lex_offset_date_time::invoke(loc)) {return ok(value_t::offset_datetime);} + loc.reset(l.iter()); + + if(lex_local_date_time::invoke(loc)) + { + // bad offset may appear after this. + if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-' + || *loc.iter() == 'Z' || *loc.iter() == 'z')) + { + return err(format_underline("bad offset: should be [+-]HH:MM or Z", + {{source_location(loc), "[+-]HH:MM or Z"}}, + {"pass: +09:00, -05:30", "fail: +9:00, -5:30"})); + } + return ok(value_t::local_datetime); + } + loc.reset(l.iter()); + + if(lex_local_date::invoke(loc)) + { + // bad time may appear after this. + // A space is allowed as a delimiter between local time. But there are + // both cases in which a space becomes valid or invalid. + // - invalid: 2019-06-16 7:00:00 + // - valid : 2019-06-16 07:00:00 + if(loc.iter() != loc.end()) + { + const auto c = *loc.iter(); + if(c == 'T' || c == 't') + { + return err(format_underline("bad time: should be HH:MM:SS.subsec", + {{source_location(loc), "HH:MM:SS.subsec"}}, + {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", + "fail: 1979-05-27T7:32:00, 1979-05-27 17:32"})); + } + if('0' <= c && c <= '9') + { + return err(format_underline("bad time: missing T", + {{source_location(loc), "T or space required here"}}, + {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", + "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); + } + if(c == ' ' && std::next(loc.iter()) != loc.end() && + ('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9')) + { + loc.advance(); + return err(format_underline("bad time: should be HH:MM:SS.subsec", + {{source_location(loc), "HH:MM:SS.subsec"}}, + {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", + "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); + } + } + return ok(value_t::local_date); + } + loc.reset(l.iter()); + + if(lex_local_time::invoke(loc)) {return ok(value_t::local_time);} + loc.reset(l.iter()); + + if(lex_float::invoke(loc)) + { + if(loc.iter() != loc.end() && *loc.iter() == '_') + { + return err(format_underline("bad float: `_` should be surrounded by digits", + {{source_location(loc), "here"}}, + {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", + "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); + } + return ok(value_t::floating); + } + loc.reset(l.iter()); + + if(lex_integer::invoke(loc)) + { + if(loc.iter() != loc.end()) + { + const auto c = *loc.iter(); + if(c == '_') + { + return err(format_underline("bad integer: `_` should be surrounded by digits", + {{source_location(loc), "here"}}, + {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", + "fail: 1__000, 0123"})); + } + if('0' <= c && c <= '9') + { + // leading zero. point '0' + loc.retrace(); + return err(format_underline("bad integer: leading zero", + {{source_location(loc), "here"}}, + {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", + "fail: 1__000, 0123"})); + } + if(c == ':' || c == '-') + { + return err(format_underline("bad datetime: invalid format", + {{source_location(loc), "here"}}, + {"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z", + "fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"})); + } + if(c == '.' || c == 'e' || c == 'E') + { + return err(format_underline("bad float: invalid format", + {{source_location(loc), "here"}}, + {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", + "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); + } + } + return ok(value_t::integer); + } + if(loc.iter() != loc.end() && *loc.iter() == '.') + { + return err(format_underline("bad float: invalid format", + {{source_location(loc), "integer part required before this"}}, + {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", + "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); + } + if(loc.iter() != loc.end() && *loc.iter() == '_') + { + return err(format_underline("bad number: `_` should be surrounded by digits", + {{source_location(loc), "`_` is not surrounded by digits"}}, + {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", + "fail: 1__000, 0123"})); + } + return err(format_underline("bad format: unknown value appeared", + {{source_location(loc), "here"}})); +} + +inline result guess_value_type(const location& loc) +{ + switch(*loc.iter()) + { + case '"' : {return ok(value_t::string); } + case '\'': {return ok(value_t::string); } + case 't' : {return ok(value_t::boolean); } + case 'f' : {return ok(value_t::boolean); } + case '[' : {return ok(value_t::array); } + case '{' : {return ok(value_t::table); } + case 'i' : {return ok(value_t::floating);} // inf. + case 'n' : {return ok(value_t::floating);} // nan. + default : {return guess_number_type(loc);} + } +} + +template +result +parse_value_helper(result, std::string> rslt) +{ + if(rslt.is_ok()) + { + auto comments = rslt.as_ok().second.comments(); + return ok(Value(std::move(rslt.as_ok()), std::move(comments))); + } + else + { + return err(std::move(rslt.as_err())); + } +} + +template +result parse_value(location& loc) +{ + const auto first = loc.iter(); + if(first == loc.end()) + { + return err(format_underline("toml::parse_value: input is empty", + {{source_location(loc), ""}})); + } + + const auto type = guess_value_type(loc); + if(!type) + { + return err(type.unwrap_err()); + } + + switch(type.unwrap()) + { + case value_t::boolean : {return parse_value_helper(parse_boolean(loc) );} + case value_t::integer : {return parse_value_helper(parse_integer(loc) );} + case value_t::floating : {return parse_value_helper(parse_floating(loc) );} + case value_t::string : {return parse_value_helper(parse_string(loc) );} + case value_t::offset_datetime: {return parse_value_helper(parse_offset_datetime(loc) );} + case value_t::local_datetime : {return parse_value_helper(parse_local_datetime(loc) );} + case value_t::local_date : {return parse_value_helper(parse_local_date(loc) );} + case value_t::local_time : {return parse_value_helper(parse_local_time(loc) );} + case value_t::array : {return parse_value_helper(parse_array(loc) );} + case value_t::table : {return parse_value_helper(parse_inline_table(loc));} + default: + { + const auto msg = format_underline("toml::parse_value: " + "unknown token appeared", {{source_location(loc), "unknown"}}); + loc.reset(first); + return err(msg); + } + } +} + +inline result, region>, std::string> +parse_table_key(location& loc) +{ + if(auto token = lex_std_table::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_std_table_open::invoke(inner_loc); + if(!open || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline( + "toml::parse_table_key: no `[`", + {{source_location(inner_loc), "should be `[`"}}), + source_location(inner_loc)); + } + // to skip [ a . b . c ] + // ^----------- this whitespace + lex_ws::invoke(inner_loc); + const auto keys = parse_key(inner_loc); + if(!keys) + { + throw internal_error(format_underline( + "toml::parse_table_key: invalid key", + {{source_location(inner_loc), "not key"}}), + source_location(inner_loc)); + } + // to skip [ a . b . c ] + // ^-- this whitespace + lex_ws::invoke(inner_loc); + const auto close = lex_std_table_close::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline( + "toml::parse_table_key: no `]`", + {{source_location(inner_loc), "should be `]`"}}), + source_location(inner_loc)); + } + + // after [table.key], newline or EOF(empty table) requried. + if(loc.iter() != loc.end()) + { + using lex_newline_after_table_key = + sequence, maybe, lex_newline>; + const auto nl = lex_newline_after_table_key::invoke(loc); + if(!nl) + { + throw syntax_error(format_underline( + "toml::parse_table_key: newline required after [table.key]", + {{source_location(loc), "expected newline"}}), + source_location(loc)); + } + } + return ok(std::make_pair(keys.unwrap().first, token.unwrap())); + } + else + { + return err(format_underline("toml::parse_table_key: " + "not a valid table key", {{source_location(loc), "here"}})); + } +} + +inline result, region>, std::string> +parse_array_table_key(location& loc) +{ + if(auto token = lex_array_table::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_array_table_open::invoke(inner_loc); + if(!open || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline( + "toml::parse_array_table_key: no `[[`", + {{source_location(inner_loc), "should be `[[`"}}), + source_location(inner_loc)); + } + lex_ws::invoke(inner_loc); + const auto keys = parse_key(inner_loc); + if(!keys) + { + throw internal_error(format_underline( + "toml::parse_array_table_key: invalid key", + {{source_location(inner_loc), "not a key"}}), + source_location(inner_loc)); + } + lex_ws::invoke(inner_loc); + const auto close = lex_array_table_close::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline( + "toml::parse_table_key: no `]]`", + {{source_location(inner_loc), "should be `]]`"}}), + source_location(inner_loc)); + } + + // after [[table.key]], newline or EOF(empty table) requried. + if(loc.iter() != loc.end()) + { + using lex_newline_after_table_key = + sequence, maybe, lex_newline>; + const auto nl = lex_newline_after_table_key::invoke(loc); + if(!nl) + { + throw syntax_error(format_underline("toml::" + "parse_array_table_key: newline required after [[table.key]]", + {{source_location(loc), "expected newline"}}), + source_location(loc)); + } + } + return ok(std::make_pair(keys.unwrap().first, token.unwrap())); + } + else + { + return err(format_underline("toml::parse_array_table_key: " + "not a valid table key", {{source_location(loc), "here"}})); + } +} + +// parse table body (key-value pairs until the iter hits the next [tablekey]) +template +result +parse_ml_table(location& loc) +{ + using value_type = Value; + using table_type = typename value_type::table_type; + + const auto first = loc.iter(); + if(first == loc.end()) + { + return ok(table_type{}); + } + + // XXX at lest one newline is needed. + using skip_line = repeat< + sequence, maybe, lex_newline>, at_least<1>>; + skip_line::invoke(loc); + lex_ws::invoke(loc); + + table_type tab; + while(loc.iter() != loc.end()) + { + lex_ws::invoke(loc); + const auto before = loc.iter(); + if(const auto tmp = parse_array_table_key(loc)) // next table found + { + loc.reset(before); + return ok(tab); + } + if(const auto tmp = parse_table_key(loc)) // next table found + { + loc.reset(before); + return ok(tab); + } + + if(const auto kv = parse_key_value_pair(loc)) + { + const auto& kvpair = kv.unwrap(); + const std::vector& keys = kvpair.first.first; + const auto& key_reg = kvpair.first.second; + const value_type& val = kvpair.second; + const auto inserted = + insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg); + if(!inserted) + { + return err(inserted.unwrap_err()); + } + } + else + { + return err(kv.unwrap_err()); + } + + // comment lines are skipped by the above function call. + // However, since the `skip_line` requires at least 1 newline, it fails + // if the file ends with ws and/or comment without newline. + // `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment` + // itself. To skip the last ws and/or comment, call lexers. + // It does not matter if these fails, so the return value is discarded. + lex_ws::invoke(loc); + lex_comment::invoke(loc); + + // skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines + // and comments after the last key-value pairs are allowed. + const auto newline = skip_line::invoke(loc); + if(!newline && loc.iter() != loc.end()) + { + const auto before2 = loc.iter(); + lex_ws::invoke(loc); // skip whitespace + const auto msg = format_underline("toml::parse_table: " + "invalid line format", {{source_location(loc), concat_to_string( + "expected newline, but got '", show_char(*loc.iter()), "'.")}}); + loc.reset(before2); + return err(msg); + } + + // the skip_lines only matches with lines that includes newline. + // to skip the last line that includes comment and/or whitespace + // but no newline, call them one more time. + lex_ws::invoke(loc); + lex_comment::invoke(loc); + } + return ok(tab); +} + +template +result parse_toml_file(location& loc) +{ + using value_type = Value; + using table_type = typename value_type::table_type; + + const auto first = loc.iter(); + if(first == loc.end()) + { + // For empty files, return an empty table with an empty region (zero-length). + // Without the region, error messages would miss the filename. + return ok(value_type(table_type{}, region(loc, first, first), {})); + } + + // put the first line as a region of a file + // Here first != loc.end(), so taking std::next is okay + const region file(loc, first, std::next(loc.iter())); + + // The first successive comments that are separated from the first value + // by an empty line are for a file itself. + // ```toml + // # this is a comment for a file. + // + // key = "the first value" + // ``` + // ```toml + // # this is a comment for "the first value". + // key = "the first value" + // ``` + std::vector comments; + using lex_first_comments = sequence< + repeat, lex_comment, lex_newline>, at_least<1>>, + sequence, lex_newline> + >; + if(const auto token = lex_first_comments::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + while(inner_loc.iter() != inner_loc.end()) + { + maybe::invoke(inner_loc); // remove ws if exists + if(lex_newline::invoke(inner_loc)) + { + assert(inner_loc.iter() == inner_loc.end()); + break; // empty line found. + } + auto com = lex_comment::invoke(inner_loc).unwrap().str(); + com.erase(com.begin()); // remove # sign + comments.push_back(std::move(com)); + lex_newline::invoke(inner_loc); + } + } + + table_type data; + // root object is also a table, but without [tablename] + if(auto tab = parse_ml_table(loc)) + { + data = std::move(tab.unwrap()); + } + else // failed (empty table is regarded as success in parse_ml_table) + { + return err(tab.unwrap_err()); + } + while(loc.iter() != loc.end()) + { + // here, the region of [table] is regarded as the table-key because + // the table body is normally too big and it is not so informative + // if the first key-value pair of the table is shown in the error + // message. + if(const auto tabkey = parse_array_table_key(loc)) + { + const auto tab = parse_ml_table(loc); + if(!tab){return err(tab.unwrap_err());} + + const auto& tk = tabkey.unwrap(); + const auto& keys = tk.first; + const auto& reg = tk.second; + + const auto inserted = insert_nested_key(data, + value_type(tab.unwrap(), reg, reg.comments()), + keys.begin(), keys.end(), reg, + /*is_array_of_table=*/ true); + if(!inserted) {return err(inserted.unwrap_err());} + + continue; + } + if(const auto tabkey = parse_table_key(loc)) + { + const auto tab = parse_ml_table(loc); + if(!tab){return err(tab.unwrap_err());} + + const auto& tk = tabkey.unwrap(); + const auto& keys = tk.first; + const auto& reg = tk.second; + + const auto inserted = insert_nested_key(data, + value_type(tab.unwrap(), reg, reg.comments()), + keys.begin(), keys.end(), reg); + if(!inserted) {return err(inserted.unwrap_err());} + + continue; + } + return err(format_underline("toml::parse_toml_file: " + "unknown line appeared", {{source_location(loc), "unknown format"}})); + } + + return ok(Value(std::move(data), file, comments)); +} + +} // detail + +template class Table = std::unordered_map, + template class Array = std::vector> +basic_value +parse(std::istream& is, const std::string& fname = "unknown file") +{ + using value_type = basic_value; + + const auto beg = is.tellg(); + is.seekg(0, std::ios::end); + const auto end = is.tellg(); + const auto fsize = end - beg; + is.seekg(beg); + + // read whole file as a sequence of char + assert(fsize >= 0); + std::vector letters(static_cast(fsize)); + is.read(letters.data(), fsize); + + while(!letters.empty() && letters.back() == '\0') + { + letters.pop_back(); + } + assert(letters.empty() || letters.back() != '\0'); + + detail::location loc(std::move(fname), std::move(letters)); + + // skip BOM if exists. + // XXX component of BOM (like 0xEF) exceeds the representable range of + // signed char, so on some (actually, most) of the environment, these cannot + // be compared to char. However, since we are always out of luck, we need to + // check our chars are equivalent to BOM. To do this, first we need to + // convert char to unsigned char to guarantee the comparability. + if(loc.source()->size() >= 3) + { + std::array BOM; + std::memcpy(BOM.data(), loc.source()->data(), 3); + if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) + { + loc.advance(3); // BOM found. skip. + } + } + + const auto data = detail::parse_toml_file(loc); + if(!data) + { + throw syntax_error(data.unwrap_err(), source_location(loc)); + } + return data.unwrap(); +} + +template class Table = std::unordered_map, + template class Array = std::vector> +basic_value parse(const std::string& fname) +{ + std::ifstream ifs(fname.c_str(), std::ios_base::binary); + if(!ifs.good()) + { + throw std::runtime_error("toml::parse: file open error -> " + fname); + } + return parse(ifs, fname); +} + +#ifdef TOML11_HAS_STD_FILESYSTEM +// This function just forwards `parse("filename.toml")` to std::string version +// to avoid the ambiguity in overload resolution. +// +// Both std::string and std::filesystem::path are convertible from const char*. +// Without this, both parse(std::string) and parse(std::filesystem::path) +// matches to parse("filename.toml"). This breaks the existing code. +// +// This function exactly matches to the invokation with c-string. +// So this function is preferred than others and the ambiguity disappears. +template class Table = std::unordered_map, + template class Array = std::vector> +basic_value parse(const char* fname) +{ + return parse(std::string(fname)); +} + +template class Table = std::unordered_map, + template class Array = std::vector> +basic_value parse(const std::filesystem::path& fpath) +{ + std::ifstream ifs(fpath, std::ios_base::binary); + if(!ifs.good()) + { + throw std::runtime_error("toml::parse: file open error -> " + + fpath.string()); + } + return parse(ifs, fpath.string()); +} +#endif // TOML11_HAS_STD_FILESYSTEM + +} // toml +#endif// TOML11_PARSER_HPP diff --git a/src/toml/region.hpp b/src/toml/region.hpp new file mode 100644 index 00000000..2e01e51d --- /dev/null +++ b/src/toml/region.hpp @@ -0,0 +1,417 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_REGION_HPP +#define TOML11_REGION_HPP +#include +#include +#include +#include +#include +#include +#include +#include "color.hpp" + +namespace toml +{ +namespace detail +{ + +// helper function to avoid std::string(0, 'c') or std::string(iter, iter) +template +std::string make_string(Iterator first, Iterator last) +{ + if(first == last) {return "";} + return std::string(first, last); +} +inline std::string make_string(std::size_t len, char c) +{ + if(len == 0) {return "";} + return std::string(len, c); +} + +// region_base is a base class of location and region that are defined below. +// it will be used to generate better error messages. +struct region_base +{ + region_base() = default; + virtual ~region_base() = default; + region_base(const region_base&) = default; + region_base(region_base&& ) = default; + region_base& operator=(const region_base&) = default; + region_base& operator=(region_base&& ) = default; + + virtual bool is_ok() const noexcept {return false;} + virtual char front() const noexcept {return '\0';} + + virtual std::string str() const {return std::string("unknown region");} + virtual std::string name() const {return std::string("unknown file");} + virtual std::string line() const {return std::string("unknown line");} + virtual std::string line_num() const {return std::string("?");} + + // length of the region + virtual std::size_t size() const noexcept {return 0;} + // number of characters in the line before the region + virtual std::size_t before() const noexcept {return 0;} + // number of characters in the line after the region + virtual std::size_t after() const noexcept {return 0;} + + virtual std::vector comments() const {return {};} + // ```toml + // # comment_before + // key = "value" # comment_inline + // ``` +}; + +// location represents a position in a container, which contains a file content. +// it can be considered as a region that contains only one character. +// +// it contains pointer to the file content and iterator that points the current +// location. +struct location final : public region_base +{ + using const_iterator = typename std::vector::const_iterator; + using difference_type = typename const_iterator::difference_type; + using source_ptr = std::shared_ptr>; + + location(std::string source_name, std::vector cont) + : source_(std::make_shared>(std::move(cont))), + line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) + {} + location(std::string source_name, const std::string& cont) + : source_(std::make_shared>(cont.begin(), cont.end())), + line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) + {} + + location(const location&) = default; + location(location&&) = default; + location& operator=(const location&) = default; + location& operator=(location&&) = default; + ~location() = default; + + bool is_ok() const noexcept override {return static_cast(source_);} + char front() const noexcept override {return *iter_;} + + // this const prohibits codes like `++(loc.iter())`. + const const_iterator iter() const noexcept {return iter_;} + + const_iterator begin() const noexcept {return source_->cbegin();} + const_iterator end() const noexcept {return source_->cend();} + + // XXX `location::line_num()` used to be implemented using `std::count` to + // count a number of '\n'. But with a long toml file (typically, 10k lines), + // it becomes intolerably slow because each time it generates error messages, + // it counts '\n' from thousands of characters. To workaround it, I decided + // to introduce `location::line_number_` member variable and synchronize it + // to the location changes the point to look. So an overload of `iter()` + // which returns mutable reference is removed and `advance()`, `retrace()` + // and `reset()` is added. + void advance(difference_type n = 1) noexcept + { + this->line_number_ += static_cast( + std::count(this->iter_, std::next(this->iter_, n), '\n')); + this->iter_ += n; + return; + } + void retrace(difference_type n = 1) noexcept + { + this->line_number_ -= static_cast( + std::count(std::prev(this->iter_, n), this->iter_, '\n')); + this->iter_ -= n; + return; + } + void reset(const_iterator rollback) noexcept + { + // since c++11, std::distance works in both ways for random-access + // iterators and returns a negative value if `first > last`. + if(0 <= std::distance(rollback, this->iter_)) // rollback < iter + { + this->line_number_ -= static_cast( + std::count(rollback, this->iter_, '\n')); + } + else // iter < rollback [[unlikely]] + { + this->line_number_ += static_cast( + std::count(this->iter_, rollback, '\n')); + } + this->iter_ = rollback; + return; + } + + std::string str() const override {return make_string(1, *this->iter());} + std::string name() const override {return source_name_;} + + std::string line_num() const override + { + return std::to_string(this->line_number_); + } + + std::string line() const override + { + return make_string(this->line_begin(), this->line_end()); + } + + const_iterator line_begin() const noexcept + { + using reverse_iterator = std::reverse_iterator; + return std::find(reverse_iterator(this->iter()), + reverse_iterator(this->begin()), '\n').base(); + } + const_iterator line_end() const noexcept + { + return std::find(this->iter(), this->end(), '\n'); + } + + // location is always points a character. so the size is 1. + std::size_t size() const noexcept override + { + return 1u; + } + std::size_t before() const noexcept override + { + const auto sz = std::distance(this->line_begin(), this->iter()); + assert(sz >= 0); + return static_cast(sz); + } + std::size_t after() const noexcept override + { + const auto sz = std::distance(this->iter(), this->line_end()); + assert(sz >= 0); + return static_cast(sz); + } + + source_ptr const& source() const& noexcept {return source_;} + source_ptr&& source() && noexcept {return std::move(source_);} + + private: + + source_ptr source_; + std::size_t line_number_; + std::string source_name_; + const_iterator iter_; +}; + +// region represents a range in a container, which contains a file content. +// +// it contains pointer to the file content and iterator that points the first +// and last location. +struct region final : public region_base +{ + using const_iterator = typename std::vector::const_iterator; + using source_ptr = std::shared_ptr>; + + // delete default constructor. source_ never be null. + region() = delete; + + explicit region(const location& loc) + : source_(loc.source()), source_name_(loc.name()), + first_(loc.iter()), last_(loc.iter()) + {} + explicit region(location&& loc) + : source_(loc.source()), source_name_(loc.name()), + first_(loc.iter()), last_(loc.iter()) + {} + + region(const location& loc, const_iterator f, const_iterator l) + : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) + {} + region(location&& loc, const_iterator f, const_iterator l) + : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) + {} + + region(const region&) = default; + region(region&&) = default; + region& operator=(const region&) = default; + region& operator=(region&&) = default; + ~region() = default; + + region& operator+=(const region& other) + { + // different regions cannot be concatenated + assert(this->begin() == other.begin() && this->end() == other.end() && + this->last_ == other.first_); + + this->last_ = other.last_; + return *this; + } + + bool is_ok() const noexcept override {return static_cast(source_);} + char front() const noexcept override {return *first_;} + + std::string str() const override {return make_string(first_, last_);} + std::string line() const override + { + if(this->contain_newline()) + { + return make_string(this->line_begin(), + std::find(this->line_begin(), this->last(), '\n')); + } + return make_string(this->line_begin(), this->line_end()); + } + std::string line_num() const override + { + return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); + } + + std::size_t size() const noexcept override + { + const auto sz = std::distance(first_, last_); + assert(sz >= 0); + return static_cast(sz); + } + std::size_t before() const noexcept override + { + const auto sz = std::distance(this->line_begin(), this->first()); + assert(sz >= 0); + return static_cast(sz); + } + std::size_t after() const noexcept override + { + const auto sz = std::distance(this->last(), this->line_end()); + assert(sz >= 0); + return static_cast(sz); + } + + bool contain_newline() const noexcept + { + return std::find(this->first(), this->last(), '\n') != this->last(); + } + + const_iterator line_begin() const noexcept + { + using reverse_iterator = std::reverse_iterator; + return std::find(reverse_iterator(this->first()), + reverse_iterator(this->begin()), '\n').base(); + } + const_iterator line_end() const noexcept + { + return std::find(this->last(), this->end(), '\n'); + } + + const_iterator begin() const noexcept {return source_->cbegin();} + const_iterator end() const noexcept {return source_->cend();} + const_iterator first() const noexcept {return first_;} + const_iterator last() const noexcept {return last_;} + + source_ptr const& source() const& noexcept {return source_;} + source_ptr&& source() && noexcept {return std::move(source_);} + + std::string name() const override {return source_name_;} + + std::vector comments() const override + { + // assuming the current region (`*this`) points a value. + // ```toml + // a = "value" + // ^^^^^^^- this region + // ``` + using rev_iter = std::reverse_iterator; + + std::vector com{}; + { + // find comments just before the current region. + // ```toml + // # this should be collected. + // # this also. + // a = value # not this. + // ``` + + // # this is a comment for `a`, not array elements. + // a = [1, 2, 3, 4, 5] + if(this->first() == std::find_if(this->line_begin(), this->first(), + [](const char c) noexcept -> bool {return c == '[' || c == '{';})) + { + auto iter = this->line_begin(); // points the first character + while(iter != this->begin()) + { + iter = std::prev(iter); + + // range [line_start, iter) represents the previous line + const auto line_start = std::find( + rev_iter(iter), rev_iter(this->begin()), '\n').base(); + const auto comment_found = std::find(line_start, iter, '#'); + if(comment_found == iter) + { + break; // comment not found. + } + + // exclude the following case. + // > a = "foo" # comment // <-- this is not a comment for b but a. + // > b = "current value" + if(std::all_of(line_start, comment_found, + [](const char c) noexcept -> bool { + return c == ' ' || c == '\t'; + })) + { + // unwrap the first '#' by std::next. + auto s = make_string(std::next(comment_found), iter); + if(!s.empty() && s.back() == '\r') {s.pop_back();} + com.push_back(std::move(s)); + } + else + { + break; + } + iter = line_start; + } + } + } + + if(com.size() > 1) + { + std::reverse(com.begin(), com.end()); + } + + { + // find comments just after the current region. + // ```toml + // # not this. + // a = value # this one. + // a = [ # not this (technically difficult) + // + // ] # and this. + // ``` + // The reason why it's difficult is that it requires parsing in the + // following case. + // ```toml + // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`. + // # ... + // ] # this is apparently a comment for a. + // + // b = [ + // 3.14 ] # there is no way to add a comment to `3.14` currently. + // + // c = [ + // 3.14 # do this if you need a comment here. + // ] + // ``` + const auto comment_found = + std::find(this->last(), this->line_end(), '#'); + if(comment_found != this->line_end()) // '#' found + { + // table = {key = "value"} # what is this for? + // the above comment is not for "value", but {key="value"}. + if(comment_found == std::find_if(this->last(), comment_found, + [](const char c) noexcept -> bool { + return !(c == ' ' || c == '\t' || c == ','); + })) + { + // unwrap the first '#' by std::next. + auto s = make_string(std::next(comment_found), this->line_end()); + if(!s.empty() && s.back() == '\r') {s.pop_back();} + com.push_back(std::move(s)); + } + } + } + return com; + } + + private: + + source_ptr source_; + std::string source_name_; + const_iterator first_, last_; +}; + +} // detail +} // toml +#endif// TOML11_REGION_H diff --git a/src/toml/result.hpp b/src/toml/result.hpp new file mode 100644 index 00000000..77cd46c6 --- /dev/null +++ b/src/toml/result.hpp @@ -0,0 +1,717 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_RESULT_HPP +#define TOML11_RESULT_HPP +#include "traits.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace toml +{ + +template +struct success +{ + using value_type = T; + value_type value; + + explicit success(const value_type& v) + noexcept(std::is_nothrow_copy_constructible::value) + : value(v) + {} + explicit success(value_type&& v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template + explicit success(U&& v): value(std::forward(v)) {} + + template + explicit success(const success& v): value(v.value) {} + template + explicit success(success&& v): value(std::move(v.value)) {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; +}; + +template +struct failure +{ + using value_type = T; + value_type value; + + explicit failure(const value_type& v) + noexcept(std::is_nothrow_copy_constructible::value) + : value(v) + {} + explicit failure(value_type&& v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template + explicit failure(U&& v): value(std::forward(v)) {} + + template + explicit failure(const failure& v): value(v.value) {} + template + explicit failure(failure&& v): value(std::move(v.value)) {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; +}; + +template +success::type>::type> +ok(T&& v) +{ + return success< + typename std::remove_cv::type>::type + >(std::forward(v)); +} +template +failure::type>::type> +err(T&& v) +{ + return failure< + typename std::remove_cv::type>::type + >(std::forward(v)); +} + +inline success ok(const char* literal) +{ + return success(std::string(literal)); +} +inline failure err(const char* literal) +{ + return failure(std::string(literal)); +} + + +template +struct result +{ + using value_type = T; + using error_type = E; + using success_type = success; + using failure_type = failure; + + result(const success_type& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(s); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + result(const failure_type& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(f); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + result(success_type&& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + result(failure_type&& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + + template + result(const success& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + template + result(const failure& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + template + result(success&& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + template + result(failure&& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + + result& operator=(const success_type& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(s); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + return *this; + } + result& operator=(const failure_type& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(f); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + return *this; + } + result& operator=(success_type&& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + return *this; + } + result& operator=(failure_type&& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + return *this; + } + + template + result& operator=(const success& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + return *this; + } + template + result& operator=(const failure& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + return *this; + } + template + result& operator=(success&& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + return *this; + } + template + result& operator=(failure&& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + return *this; + } + + ~result() noexcept {this->cleanup();} + + result(const result& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + } + result(result&& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + } + + template + result(const result& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + } + template + result(result&& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + } + + result& operator=(const result& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + result& operator=(result&& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + + template + result& operator=(const result& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + template + result& operator=(result&& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + (void)tmp; + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + (void)tmp; + } + is_ok_ = other.is_ok(); + return *this; + } + + bool is_ok() const noexcept {return is_ok_;} + bool is_err() const noexcept {return !is_ok_;} + + operator bool() const noexcept {return is_ok_;} + + value_type& unwrap() & + { + if(is_err()) + { + throw std::runtime_error("toml::result: bad unwrap: " + + format_error(this->as_err())); + } + return this->succ.value; + } + value_type const& unwrap() const& + { + if(is_err()) + { + throw std::runtime_error("toml::result: bad unwrap: " + + format_error(this->as_err())); + } + return this->succ.value; + } + value_type&& unwrap() && + { + if(is_err()) + { + throw std::runtime_error("toml::result: bad unwrap: " + + format_error(this->as_err())); + } + return std::move(this->succ.value); + } + + value_type& unwrap_or(value_type& opt) & + { + if(is_err()) {return opt;} + return this->succ.value; + } + value_type const& unwrap_or(value_type const& opt) const& + { + if(is_err()) {return opt;} + return this->succ.value; + } + value_type unwrap_or(value_type opt) && + { + if(is_err()) {return opt;} + return this->succ.value; + } + + error_type& unwrap_err() & + { + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} + return this->fail.value; + } + error_type const& unwrap_err() const& + { + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} + return this->fail.value; + } + error_type&& unwrap_err() && + { + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} + return std::move(this->fail.value); + } + + value_type& as_ok() & noexcept {return this->succ.value;} + value_type const& as_ok() const& noexcept {return this->succ.value;} + value_type&& as_ok() && noexcept {return std::move(this->succ.value);} + + error_type& as_err() & noexcept {return this->fail.value;} + error_type const& as_err() const& noexcept {return this->fail.value;} + error_type&& as_err() && noexcept {return std::move(this->fail.value);} + + + // prerequisities + // F: T -> U + // retval: result + template + result, error_type> + map(F&& f) & + { + if(this->is_ok()){return ok(f(this->as_ok()));} + return err(this->as_err()); + } + template + result, error_type> + map(F&& f) const& + { + if(this->is_ok()){return ok(f(this->as_ok()));} + return err(this->as_err()); + } + template + result, error_type> + map(F&& f) && + { + if(this->is_ok()){return ok(f(std::move(this->as_ok())));} + return err(std::move(this->as_err())); + } + + // prerequisities + // F: E -> F + // retval: result + template + result> + map_err(F&& f) & + { + if(this->is_err()){return err(f(this->as_err()));} + return ok(this->as_ok()); + } + template + result> + map_err(F&& f) const& + { + if(this->is_err()){return err(f(this->as_err()));} + return ok(this->as_ok()); + } + template + result> + map_err(F&& f) && + { + if(this->is_err()){return err(f(std::move(this->as_err())));} + return ok(std::move(this->as_ok())); + } + + // prerequisities + // F: T -> U + // retval: U + template + detail::return_type_of_t + map_or_else(F&& f, U&& opt) & + { + if(this->is_err()){return std::forward(opt);} + return f(this->as_ok()); + } + template + detail::return_type_of_t + map_or_else(F&& f, U&& opt) const& + { + if(this->is_err()){return std::forward(opt);} + return f(this->as_ok()); + } + template + detail::return_type_of_t + map_or_else(F&& f, U&& opt) && + { + if(this->is_err()){return std::forward(opt);} + return f(std::move(this->as_ok())); + } + + // prerequisities + // F: E -> U + // retval: U + template + detail::return_type_of_t + map_err_or_else(F&& f, U&& opt) & + { + if(this->is_ok()){return std::forward(opt);} + return f(this->as_err()); + } + template + detail::return_type_of_t + map_err_or_else(F&& f, U&& opt) const& + { + if(this->is_ok()){return std::forward(opt);} + return f(this->as_err()); + } + template + detail::return_type_of_t + map_err_or_else(F&& f, U&& opt) && + { + if(this->is_ok()){return std::forward(opt);} + return f(std::move(this->as_err())); + } + + // prerequisities: + // F: func T -> U + // toml::err(error_type) should be convertible to U. + // normally, type U is another result and E is convertible to F + template + detail::return_type_of_t + and_then(F&& f) & + { + if(this->is_ok()){return f(this->as_ok());} + return err(this->as_err()); + } + template + detail::return_type_of_t + and_then(F&& f) const& + { + if(this->is_ok()){return f(this->as_ok());} + return err(this->as_err()); + } + template + detail::return_type_of_t + and_then(F&& f) && + { + if(this->is_ok()){return f(std::move(this->as_ok()));} + return err(std::move(this->as_err())); + } + + // prerequisities: + // F: func E -> U + // toml::ok(value_type) should be convertible to U. + // normally, type U is another result and T is convertible to S + template + detail::return_type_of_t + or_else(F&& f) & + { + if(this->is_err()){return f(this->as_err());} + return ok(this->as_ok()); + } + template + detail::return_type_of_t + or_else(F&& f) const& + { + if(this->is_err()){return f(this->as_err());} + return ok(this->as_ok()); + } + template + detail::return_type_of_t + or_else(F&& f) && + { + if(this->is_err()){return f(std::move(this->as_err()));} + return ok(std::move(this->as_ok())); + } + + // if *this is error, returns *this. otherwise, returns other. + result and_other(const result& other) const& + { + return this->is_err() ? *this : other; + } + result and_other(result&& other) && + { + return this->is_err() ? std::move(*this) : std::move(other); + } + + // if *this is okay, returns *this. otherwise, returns other. + result or_other(const result& other) const& + { + return this->is_ok() ? *this : other; + } + result or_other(result&& other) && + { + return this->is_ok() ? std::move(*this) : std::move(other); + } + + void swap(result& other) + { + result tmp(std::move(*this)); + *this = std::move(other); + other = std::move(tmp); + return ; + } + + private: + + static std::string format_error(std::exception const& excpt) + { + return std::string(excpt.what()); + } + template::value, std::nullptr_t>::type = nullptr> + static std::string format_error(U const& others) + { + std::ostringstream oss; oss << others; + return oss.str(); + } + + void cleanup() noexcept + { + if(this->is_ok_) {this->succ.~success_type();} + else {this->fail.~failure_type();} + return; + } + + private: + + bool is_ok_; + union + { + success_type succ; + failure_type fail; + }; +}; + +template +void swap(result& lhs, result& rhs) +{ + lhs.swap(rhs); + return; +} + +// this might be confusing because it eagerly evaluated, while in the other +// cases operator && and || are short-circuited. +// +// template +// inline result +// operator&&(const result& lhs, const result& rhs) noexcept +// { +// return lhs.is_ok() ? rhs : lhs; +// } +// +// template +// inline result +// operator||(const result& lhs, const result& rhs) noexcept +// { +// return lhs.is_ok() ? lhs : rhs; +// } + +// ---------------------------------------------------------------------------- +// re-use result as a optional with none_t + +namespace detail +{ +struct none_t {}; +inline bool operator==(const none_t&, const none_t&) noexcept {return true;} +inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} +inline bool operator< (const none_t&, const none_t&) noexcept {return false;} +inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} +inline bool operator> (const none_t&, const none_t&) noexcept {return false;} +inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const none_t&) +{ + os << "none"; + return os; +} +inline failure none() noexcept {return failure{none_t{}};} +} // detail +} // toml11 +#endif// TOML11_RESULT_H diff --git a/src/toml/serializer.hpp b/src/toml/serializer.hpp new file mode 100644 index 00000000..b27abfd9 --- /dev/null +++ b/src/toml/serializer.hpp @@ -0,0 +1,853 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_SERIALIZER_HPP +#define TOML11_SERIALIZER_HPP +#include + +#include + +#include "lexer.hpp" +#include "value.hpp" + +namespace toml +{ + +// This function serialize a key. It checks a string is a bare key and +// escapes special characters if the string is not compatible to a bare key. +// ```cpp +// std::string k("non.bare.key"); // the key itself includes `.`s. +// std::string formatted = toml::format_key(k); +// assert(formatted == "\"non.bare.key\""); +// ``` +// +// This function is exposed to make it easy to write a user-defined serializer. +// Since toml restricts characters available in a bare key, generally a string +// should be escaped. But checking whether a string needs to be surrounded by +// a `"` and escaping some special character is boring. +template +std::basic_string +format_key(const std::basic_string& k) +{ + // check the key can be a bare (unquoted) key + detail::location loc(k, std::vector(k.begin(), k.end())); + detail::lex_unquoted_key::invoke(loc); + if(loc.iter() == loc.end()) + { + return k; // all the tokens are consumed. the key is unquoted-key. + } + + //if it includes special characters, then format it in a "quoted" key. + std::basic_string serialized("\""); + for(const char c : k) + { + switch(c) + { + case '\\': {serialized += "\\\\"; break;} + case '\"': {serialized += "\\\""; break;} + case '\b': {serialized += "\\b"; break;} + case '\t': {serialized += "\\t"; break;} + case '\f': {serialized += "\\f"; break;} + case '\n': {serialized += "\\n"; break;} + case '\r': {serialized += "\\r"; break;} + default : {serialized += c; break;} + } + } + serialized += "\""; + return serialized; +} + +template +std::basic_string +format_keys(const std::vector>& keys) +{ + std::basic_string serialized; + if(keys.empty()) {return serialized;} + + for(const auto& ky : keys) + { + serialized += format_key(ky); + serialized += charT('.'); + } + serialized.pop_back(); // remove the last dot '.' + return serialized; +} + +template +struct serializer +{ + static_assert(detail::is_basic_value::value, + "toml::serializer is for toml::value and its variants, " + "toml::basic_value<...>."); + + using value_type = Value; + using key_type = typename value_type::key_type ; + using comment_type = typename value_type::comment_type ; + using boolean_type = typename value_type::boolean_type ; + using integer_type = typename value_type::integer_type ; + using floating_type = typename value_type::floating_type ; + using string_type = typename value_type::string_type ; + using local_time_type = typename value_type::local_time_type ; + using local_date_type = typename value_type::local_date_type ; + using local_datetime_type = typename value_type::local_datetime_type ; + using offset_datetime_type = typename value_type::offset_datetime_type; + using array_type = typename value_type::array_type ; + using table_type = typename value_type::table_type ; + + serializer(const std::size_t w = 80u, + const int float_prec = std::numeric_limits::max_digits10, + const bool can_be_inlined = false, + const bool no_comment = false, + std::vector ks = {}, + const bool value_has_comment = false) + : can_be_inlined_(can_be_inlined), no_comment_(no_comment), + value_has_comment_(value_has_comment && !no_comment), + float_prec_(float_prec), width_(w), keys_(std::move(ks)) + {} + ~serializer() = default; + + std::string operator()(const boolean_type& b) const + { + return b ? "true" : "false"; + } + std::string operator()(const integer_type i) const + { + return std::to_string(i); + } + std::string operator()(const floating_type f) const + { + const auto fmt = "%.*g"; + const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); + // +1 for null character(\0) + std::vector buf(static_cast(bsz + 1), '\0'); + std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); + + std::string token(buf.begin(), std::prev(buf.end())); + if(!token.empty() && token.back() == '.') // 1. => 1.0 + { + token += '0'; + } + + const auto e = std::find_if( + token.cbegin(), token.cend(), [](const char c) noexcept -> bool { + return c == 'e' || c == 'E'; + }); + const auto has_exponent = (token.cend() != e); + const auto has_fraction = (token.cend() != std::find( + token.cbegin(), token.cend(), '.')); + + if(!has_exponent && !has_fraction) + { + // the resulting value does not have any float specific part! + token += ".0"; + } + return token; + } + std::string operator()(const string_type& s) const + { + if(s.kind == string_t::basic) + { + if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || + std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) + { + // if linefeed or double-quote is contained, + // make it multiline basic string. + const auto escaped = this->escape_ml_basic_string(s.str); + std::string open("\"\"\""); + std::string close("\"\"\""); + if(escaped.find('\n') != std::string::npos || + this->width_ < escaped.size() + 6) + { + // if the string body contains newline or is enough long, + // add newlines after and before delimiters. + open += "\n"; + close = std::string("\\\n") + close; + } + return open + escaped + close; + } + + // no linefeed. try to make it oneline-string. + std::string oneline = this->escape_basic_string(s.str); + if(oneline.size() + 2 < width_ || width_ < 2) + { + const std::string quote("\""); + return quote + oneline + quote; + } + + // the line is too long compared to the specified width. + // split it into multiple lines. + std::string token("\"\"\"\n"); + while(!oneline.empty()) + { + if(oneline.size() < width_) + { + token += oneline; + oneline.clear(); + } + else if(oneline.at(width_-2) == '\\') + { + token += oneline.substr(0, width_-2); + token += "\\\n"; + oneline.erase(0, width_-2); + } + else + { + token += oneline.substr(0, width_-1); + token += "\\\n"; + oneline.erase(0, width_-1); + } + } + return token + std::string("\\\n\"\"\""); + } + else // the string `s` is literal-string. + { + if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || + std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) + { + std::string open("'''"); + if(this->width_ + 6 < s.str.size()) + { + open += '\n'; // the first newline is ignored by TOML spec + } + const std::string close("'''"); + return open + s.str + close; + } + else + { + const std::string quote("'"); + return quote + s.str + quote; + } + } + } + + std::string operator()(const local_date_type& d) const + { + std::ostringstream oss; + oss << d; + return oss.str(); + } + std::string operator()(const local_time_type& t) const + { + std::ostringstream oss; + oss << t; + return oss.str(); + } + std::string operator()(const local_datetime_type& dt) const + { + std::ostringstream oss; + oss << dt; + return oss.str(); + } + std::string operator()(const offset_datetime_type& odt) const + { + std::ostringstream oss; + oss << odt; + return oss.str(); + } + + std::string operator()(const array_type& v) const + { + if(v.empty()) + { + return std::string("[]"); + } + if(this->is_array_of_tables(v)) + { + return make_array_of_tables(v); + } + + // not an array of tables. normal array. + // first, try to make it inline if none of the elements have a comment. + if( ! this->has_comment_inside(v)) + { + const auto inl = this->make_inline_array(v); + if(inl.size() < this->width_ && + std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend()) + { + return inl; + } + } + + // if the length exceeds this->width_, print multiline array. + // key = [ + // # ... + // 42, + // ... + // ] + std::string token; + std::string current_line; + token += "[\n"; + for(const auto& item : v) + { + if( ! item.comments().empty() && !no_comment_) + { + // if comment exists, the element must be the only element in the line. + // e.g. the following is not allowed. + // ```toml + // array = [ + // # comment for what? + // 1, 2, 3, 4, 5 + // ] + // ``` + if(!current_line.empty()) + { + if(current_line.back() != '\n') + { + current_line += '\n'; + } + token += current_line; + current_line.clear(); + } + for(const auto& c : item.comments()) + { + token += '#'; + token += c; + token += '\n'; + } + token += toml::visit(*this, item); + if(!token.empty() && token.back() == '\n') {token.pop_back();} + token += ",\n"; + continue; + } + std::string next_elem; + next_elem += toml::visit(*this, item); + + // comma before newline. + if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} + + // if current line does not exceeds the width limit, continue. + if(current_line.size() + next_elem.size() + 1 < this->width_) + { + current_line += next_elem; + current_line += ','; + } + else if(current_line.empty()) + { + // if current line was empty, force put the next_elem because + // next_elem is not splittable + token += next_elem; + token += ",\n"; + // current_line is kept empty + } + else // reset current_line + { + assert(current_line.back() == ','); + token += current_line; + token += '\n'; + current_line = next_elem; + current_line += ','; + } + } + if(!current_line.empty()) + { + if(!current_line.empty() && current_line.back() != '\n') + { + current_line += '\n'; + } + token += current_line; + } + token += "]\n"; + return token; + } + + // templatize for any table-like container + std::string operator()(const table_type& v) const + { + // if an element has a comment, then it can't be inlined. + // table = {# how can we write a comment for this? key = "value"} + if(this->can_be_inlined_ && !(this->has_comment_inside(v))) + { + std::string token; + if(!this->keys_.empty()) + { + token += format_key(this->keys_.back()); + token += " = "; + } + token += this->make_inline_table(v); + if(token.size() < this->width_ && + token.end() == std::find(token.begin(), token.end(), '\n')) + { + return token; + } + } + + std::string token; + if(!keys_.empty()) + { + token += '['; + token += format_keys(keys_); + token += "]\n"; + } + token += this->make_multiline_table(v); + return token; + } + + private: + + std::string escape_basic_string(const std::string& s) const + { + //XXX assuming `s` is a valid utf-8 sequence. + std::string retval; + for(const char c : s) + { + switch(c) + { + case '\\': {retval += "\\\\"; break;} + case '\"': {retval += "\\\""; break;} + case '\b': {retval += "\\b"; break;} + case '\t': {retval += "\\t"; break;} + case '\f': {retval += "\\f"; break;} + case '\n': {retval += "\\n"; break;} + case '\r': {retval += "\\r"; break;} + default : {retval += c; break;} + } + } + return retval; + } + + std::string escape_ml_basic_string(const std::string& s) const + { + std::string retval; + for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i) + { + switch(*i) + { + case '\\': {retval += "\\\\"; break;} + // One or two consecutive "s are allowed. + // Later we will check there are no three consecutive "s. + // case '\"': {retval += "\\\""; break;} + case '\b': {retval += "\\b"; break;} + case '\t': {retval += "\\t"; break;} + case '\f': {retval += "\\f"; break;} + case '\n': {retval += "\n"; break;} + case '\r': + { + if(std::next(i) != e && *std::next(i) == '\n') + { + retval += "\r\n"; + ++i; + } + else + { + retval += "\\r"; + } + break; + } + default: {retval += *i; break;} + } + } + // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. + // 3 consecutive `"`s are considered as a closing delimiter. + // We need to check if there are 3 or more consecutive `"`s and insert + // backslash to break them down into several short `"`s like the `str6` + // in the following example. + // ```toml + // str4 = """Here are two quotation marks: "". Simple enough.""" + // # str5 = """Here are three quotation marks: """.""" # INVALID + // str5 = """Here are three quotation marks: ""\".""" + // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" + // ``` + auto found_3_quotes = retval.find("\"\"\""); + while(found_3_quotes != std::string::npos) + { + retval.replace(found_3_quotes, 3, "\"\"\\\""); + found_3_quotes = retval.find("\"\"\""); + } + return retval; + } + + // if an element of a table or an array has a comment, it cannot be inlined. + bool has_comment_inside(const array_type& a) const noexcept + { + // if no_comment is set, comments would not be written. + if(this->no_comment_) {return false;} + + for(const auto& v : a) + { + if(!v.comments().empty()) {return true;} + } + return false; + } + bool has_comment_inside(const table_type& t) const noexcept + { + // if no_comment is set, comments would not be written. + if(this->no_comment_) {return false;} + + for(const auto& kv : t) + { + if(!kv.second.comments().empty()) {return true;} + } + return false; + } + + std::string make_inline_array(const array_type& v) const + { + assert(!has_comment_inside(v)); + std::string token; + token += '['; + bool is_first = true; + for(const auto& item : v) + { + if(is_first) {is_first = false;} else {token += ',';} + token += visit(serializer( + (std::numeric_limits::max)(), this->float_prec_, + /* inlined */ true, /*no comment*/ false, /*keys*/ {}, + /*has_comment*/ !item.comments().empty()), item); + } + token += ']'; + return token; + } + + std::string make_inline_table(const table_type& v) const + { + assert(!has_comment_inside(v)); + assert(this->can_be_inlined_); + std::string token; + token += '{'; + bool is_first = true; + for(const auto& kv : v) + { + // in inline tables, trailing comma is not allowed (toml-lang #569). + if(is_first) {is_first = false;} else {token += ',';} + token += format_key(kv.first); + token += '='; + token += visit(serializer( + (std::numeric_limits::max)(), this->float_prec_, + /* inlined */ true, /*no comment*/ false, /*keys*/ {}, + /*has_comment*/ !kv.second.comments().empty()), kv.second); + } + token += '}'; + return token; + } + + std::string make_multiline_table(const table_type& v) const + { + std::string token; + + // print non-table elements first. + // ```toml + // [foo] # a table we're writing now here + // key = "value" # <- non-table element, "key" + // # ... + // [foo.bar] # <- table element, "bar" + // ``` + // because after printing [foo.bar], the remaining non-table values will + // be assigned into [foo.bar], not [foo]. Those values should be printed + // earlier. + for(const auto& kv : v) + { + if(kv.second.is_table() || is_array_of_tables(kv.second)) + { + continue; + } + + token += write_comments(kv.second); + + const auto key_and_sep = format_key(kv.first) + " = "; + const auto residual_width = (this->width_ > key_and_sep.size()) ? + this->width_ - key_and_sep.size() : 0; + token += key_and_sep; + token += visit(serializer(residual_width, this->float_prec_, + /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, + /*has_comment*/ !kv.second.comments().empty()), kv.second); + + if(token.back() != '\n') + { + token += '\n'; + } + } + + // normal tables / array of tables + + // after multiline table appeared, the other tables cannot be inline + // because the table would be assigned into the table. + // [foo] + // ... + // bar = {...} # <- bar will be a member of [foo]. + bool multiline_table_printed = false; + for(const auto& kv : v) + { + if(!kv.second.is_table() && !is_array_of_tables(kv.second)) + { + continue; // other stuff are already serialized. skip them. + } + + std::vector ks(this->keys_); + ks.push_back(kv.first); + + auto tmp = visit(serializer(this->width_, this->float_prec_, + !multiline_table_printed, this->no_comment_, ks, + /*has_comment*/ !kv.second.comments().empty()), kv.second); + + // If it is the first time to print a multi-line table, it would be + // helpful to separate normal key-value pair and subtables by a + // newline. + // (this checks if the current key-value pair contains newlines. + // but it is not perfect because multi-line string can also contain + // a newline. in such a case, an empty line will be written) TODO + if((!multiline_table_printed) && + std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) + { + multiline_table_printed = true; + token += '\n'; // separate key-value pairs and subtables + + token += write_comments(kv.second); + token += tmp; + + // care about recursive tables (all tables in each level prints + // newline and there will be a full of newlines) + if(tmp.substr(tmp.size() - 2, 2) != "\n\n" && + tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" ) + { + token += '\n'; + } + } + else + { + token += write_comments(kv.second); + token += tmp; + token += '\n'; + } + } + return token; + } + + std::string make_array_of_tables(const array_type& v) const + { + // if it's not inlined, we need to add `[[table.key]]`. + // but if it can be inlined, we can format it as the following. + // ``` + // table.key = [ + // {...}, + // # comment + // {...}, + // ] + // ``` + // This function checks if inlinization is possible or not, and then + // format the array-of-tables in a proper way. + // + // Note about comments: + // + // If the array itself has a comment (value_has_comment_ == true), we + // should try to make it inline. + // ```toml + // # comment about array + // array = [ + // # comment about table element + // {of = "table"} + // ] + // ``` + // If it is formatted as a multiline table, the two comments becomes + // indistinguishable. + // ```toml + // # comment about array + // # comment about table element + // [[array]] + // of = "table" + // ``` + // So we need to try to make it inline, and it force-inlines regardless + // of the line width limit. + // It may fail if the element of a table has comment. In that case, + // the array-of-tables will be formatted as a multiline table. + if(this->can_be_inlined_ || this->value_has_comment_) + { + std::string token; + if(!keys_.empty()) + { + token += format_key(keys_.back()); + token += " = "; + } + + bool failed = false; + token += "[\n"; + for(const auto& item : v) + { + // if an element of the table has a comment, the table + // cannot be inlined. + if(this->has_comment_inside(item.as_table())) + { + failed = true; + break; + } + // write comments for the table itself + token += write_comments(item); + + const auto t = this->make_inline_table(item.as_table()); + + if(t.size() + 1 > width_ || // +1 for the last comma {...}, + std::find(t.cbegin(), t.cend(), '\n') != t.cend()) + { + // if the value itself has a comment, ignore the line width limit + if( ! this->value_has_comment_) + { + failed = true; + break; + } + } + token += t; + token += ",\n"; + } + + if( ! failed) + { + token += "]\n"; + return token; + } + // if failed, serialize them as [[array.of.tables]]. + } + + std::string token; + for(const auto& item : v) + { + token += write_comments(item); + token += "[["; + token += format_keys(keys_); + token += "]]\n"; + token += this->make_multiline_table(item.as_table()); + } + return token; + } + + std::string write_comments(const value_type& v) const + { + std::string retval; + if(this->no_comment_) {return retval;} + + for(const auto& c : v.comments()) + { + retval += '#'; + retval += c; + retval += '\n'; + } + return retval; + } + + bool is_array_of_tables(const value_type& v) const + { + if(!v.is_array() || v.as_array().empty()) {return false;} + return is_array_of_tables(v.as_array()); + } + bool is_array_of_tables(const array_type& v) const + { + // Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to + // check all the element in an array to check if the array is an array + // of tables. + return std::all_of(v.begin(), v.end(), [](const value_type& elem) { + return elem.is_table(); + }); + } + + private: + + bool can_be_inlined_; + bool no_comment_; + bool value_has_comment_; + int float_prec_; + std::size_t width_; + std::vector keys_; +}; + +template class M, template class V> +std::string +format(const basic_value& v, std::size_t w = 80u, + int fprec = std::numeric_limits::max_digits10, + bool no_comment = false, bool force_inline = false) +{ + using value_type = basic_value; + // if value is a table, it is considered to be a root object. + // the root object can't be an inline table. + if(v.is_table()) + { + std::ostringstream oss; + if(!v.comments().empty()) + { + oss << v.comments(); + oss << '\n'; // to split the file comment from the first element + } + const auto serialized = visit(serializer(w, fprec, no_comment, false), v); + oss << serialized; + return oss.str(); + } + return visit(serializer(w, fprec, force_inline), v); +} + +namespace detail +{ +template +int comment_index(std::basic_ostream&) +{ + static const int index = std::ios_base::xalloc(); + return index; +} +} // detail + +template +std::basic_ostream& +nocomment(std::basic_ostream& os) +{ + // by default, it is zero. and by defalut, it shows comments. + os.iword(detail::comment_index(os)) = 1; + return os; +} + +template +std::basic_ostream& +showcomment(std::basic_ostream& os) +{ + // by default, it is zero. and by defalut, it shows comments. + os.iword(detail::comment_index(os)) = 0; + return os; +} + +template class M, template class V> +std::basic_ostream& +operator<<(std::basic_ostream& os, const basic_value& v) +{ + using value_type = basic_value; + + // get status of std::setw(). + const auto w = static_cast(os.width()); + const int fprec = static_cast(os.precision()); + os.width(0); + + // by defualt, iword is initialized byl 0. And by default, toml11 outputs + // comments. So `0` means showcomment. 1 means nocommnet. + const bool no_comment = (1 == os.iword(detail::comment_index(os))); + + if(!no_comment && v.is_table() && !v.comments().empty()) + { + os << v.comments(); + os << '\n'; // to split the file comment from the first element + } + // the root object can't be an inline table. so pass `false`. + const auto serialized = visit(serializer(w, fprec, no_comment, false), v); + os << serialized; + + // if v is a non-table value, and has only one comment, then + // put a comment just after a value. in the following way. + // + // ```toml + // key = "value" # comment. + // ``` + // + // Since the top-level toml object is a table, one who want to put a + // non-table toml value must use this in a following way. + // + // ```cpp + // toml::value v; + // std::cout << "user-defined-key = " << v << std::endl; + // ``` + // + // In this case, it is impossible to put comments before key-value pair. + // The only way to preserve comments is to put all of them after a value. + if(!no_comment && !v.is_table() && !v.comments().empty()) + { + os << " #"; + for(const auto& c : v.comments()) {os << c;} + } + return os; +} + +} // toml +#endif// TOML11_SERIALIZER_HPP diff --git a/src/toml/source_location.hpp b/src/toml/source_location.hpp new file mode 100644 index 00000000..fa175b5b --- /dev/null +++ b/src/toml/source_location.hpp @@ -0,0 +1,233 @@ +// Copyright Toru Niina 2019. +// Distributed under the MIT License. +#ifndef TOML11_SOURCE_LOCATION_HPP +#define TOML11_SOURCE_LOCATION_HPP +#include +#include + +#include "region.hpp" + +namespace toml +{ + +// A struct to contain location in a toml file. +// The interface imitates std::experimental::source_location, +// but not completely the same. +// +// It would be constructed by toml::value. It can be used to generate +// user-defined error messages. +// +// - std::uint_least32_t line() const noexcept +// - returns the line number where the region is on. +// - std::uint_least32_t column() const noexcept +// - returns the column number where the region starts. +// - std::uint_least32_t region() const noexcept +// - returns the size of the region. +// +// +-- line() +-- region of interest (region() == 9) +// v .---+---. +// 12 | value = "foo bar" +// ^ +// +-- column() +// +// - std::string const& file_name() const noexcept; +// - name of the file. +// - std::string const& line_str() const noexcept; +// - the whole line that contains the region of interest. +// +struct source_location +{ + public: + + source_location() + : line_num_(1), column_num_(1), region_size_(1), + file_name_("unknown file"), line_str_("") + {} + + explicit source_location(const detail::region_base* reg) + : line_num_(1), column_num_(1), region_size_(1), + file_name_("unknown file"), line_str_("") + { + if(reg) + { + if(reg->line_num() != detail::region_base().line_num()) + { + line_num_ = static_cast( + std::stoul(reg->line_num())); + } + column_num_ = static_cast(reg->before() + 1); + region_size_ = static_cast(reg->size()); + file_name_ = reg->name(); + line_str_ = reg->line(); + } + } + + explicit source_location(const detail::region& reg) + : line_num_(static_cast(std::stoul(reg.line_num()))), + column_num_(static_cast(reg.before() + 1)), + region_size_(static_cast(reg.size())), + file_name_(reg.name()), + line_str_ (reg.line()) + {} + explicit source_location(const detail::location& loc) + : line_num_(static_cast(std::stoul(loc.line_num()))), + column_num_(static_cast(loc.before() + 1)), + region_size_(static_cast(loc.size())), + file_name_(loc.name()), + line_str_ (loc.line()) + {} + + ~source_location() = default; + source_location(source_location const&) = default; + source_location(source_location &&) = default; + source_location& operator=(source_location const&) = default; + source_location& operator=(source_location &&) = default; + + std::uint_least32_t line() const noexcept {return line_num_;} + std::uint_least32_t column() const noexcept {return column_num_;} + std::uint_least32_t region() const noexcept {return region_size_;} + + std::string const& file_name() const noexcept {return file_name_;} + std::string const& line_str() const noexcept {return line_str_;} + + private: + + std::uint_least32_t line_num_; + std::uint_least32_t column_num_; + std::uint_least32_t region_size_; + std::string file_name_; + std::string line_str_; +}; + +namespace detail +{ + +// internal error message generation. +inline std::string format_underline(const std::string& message, + const std::vector>& loc_com, + const std::vector& helps = {}, + const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) +{ + std::size_t line_num_width = 0; + for(const auto& lc : loc_com) + { + std::uint_least32_t line = lc.first.line(); + std::size_t digit = 0; + while(line != 0) + { + line /= 10; + digit += 1; + } + line_num_width = (std::max)(line_num_width, digit); + } + // 1 is the minimum width + line_num_width = std::max(line_num_width, 1); + + std::ostringstream retval; + + if(colorize) + { + retval << color::colorize; // turn on ANSI color + } + + // XXX + // Here, before `colorize` support, it does not output `[error]` prefix + // automatically. So some user may output it manually and this change may + // duplicate the prefix. To avoid it, check the first 7 characters and + // if it is "[error]", it removes that part from the message shown. + if(message.size() > 7 && message.substr(0, 7) == "[error]") + { + retval << color::bold << color::red << "[error]" << color::reset + << color::bold << message.substr(7) << color::reset << '\n'; + } + else + { + retval << color::bold << color::red << "[error] " << color::reset + << color::bold << message << color::reset << '\n'; + } + + const auto format_one_location = [line_num_width] + (std::ostringstream& oss, + const source_location& loc, const std::string& comment) -> void + { + oss << ' ' << color::bold << color::blue + << std::setw(static_cast(line_num_width)) + << std::right << loc.line() << " | " << color::reset + << loc.line_str() << '\n'; + + oss << make_string(line_num_width + 1, ' ') + << color::bold << color::blue << " | " << color::reset + << make_string(loc.column()-1 /*1-origin*/, ' '); + + if(loc.region() == 1) + { + // invalid + // ^------ + oss << color::bold << color::red << "^---" << color::reset; + } + else + { + // invalid + // ~~~~~~~ + const auto underline_len = (std::min)( + static_cast(loc.region()), loc.line_str().size()); + oss << color::bold << color::red + << make_string(underline_len, '~') << color::reset; + } + oss << ' '; + oss << comment; + return; + }; + + assert(!loc_com.empty()); + + // --> example.toml + // | + retval << color::bold << color::blue << " --> " << color::reset + << loc_com.front().first.file_name() << '\n'; + retval << make_string(line_num_width + 1, ' ') + << color::bold << color::blue << " |\n" << color::reset; + // 1 | key value + // | ^--- missing = + format_one_location(retval, loc_com.front().first, loc_com.front().second); + + // process the rest of the locations + for(std::size_t i=1; i filename.toml" again + { + retval << color::bold << color::blue << " --> " << color::reset + << curr.first.file_name() << '\n'; + retval << make_string(line_num_width + 1, ' ') + << color::bold << color::blue << " |\n" << color::reset; + } + + format_one_location(retval, curr.first, curr.second); + } + + if(!helps.empty()) + { + retval << '\n'; + retval << make_string(line_num_width + 1, ' '); + retval << color::bold << color::blue << " |" << color::reset; + for(const auto& help : helps) + { + retval << color::bold << "\nHint: " << color::reset; + retval << help; + } + } + return retval.str(); +} + +} // detail +} // toml +#endif// TOML11_SOURCE_LOCATION_HPP diff --git a/src/toml/storage.hpp b/src/toml/storage.hpp new file mode 100644 index 00000000..202f9035 --- /dev/null +++ b/src/toml/storage.hpp @@ -0,0 +1,43 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_STORAGE_HPP +#define TOML11_STORAGE_HPP +#include "utility.hpp" + +namespace toml +{ +namespace detail +{ + +// this contains pointer and deep-copy the content if copied. +// to avoid recursive pointer. +template +struct storage +{ + using value_type = T; + + explicit storage(value_type const& v): ptr(toml::make_unique(v)) {} + explicit storage(value_type&& v): ptr(toml::make_unique(std::move(v))) {} + ~storage() = default; + storage(const storage& rhs): ptr(toml::make_unique(*rhs.ptr)) {} + storage& operator=(const storage& rhs) + { + this->ptr = toml::make_unique(*rhs.ptr); + return *this; + } + storage(storage&&) = default; + storage& operator=(storage&&) = default; + + bool is_ok() const noexcept {return static_cast(ptr);} + + value_type& value() & noexcept {return *ptr;} + value_type const& value() const& noexcept {return *ptr;} + value_type&& value() && noexcept {return std::move(*ptr);} + + private: + std::unique_ptr ptr; +}; + +} // detail +} // toml +#endif// TOML11_STORAGE_HPP diff --git a/src/toml/string.hpp b/src/toml/string.hpp new file mode 100644 index 00000000..a6ed0801 --- /dev/null +++ b/src/toml/string.hpp @@ -0,0 +1,224 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_STRING_HPP +#define TOML11_STRING_HPP +#include + +#include +#include + +#if __cplusplus >= 201703L +#if __has_include() +#include +#endif +#endif + +namespace toml +{ + +enum class string_t : std::uint8_t +{ + basic = 0, + literal = 1, +}; + +struct string +{ + string() = default; + ~string() = default; + string(const string& s) = default; + string(string&& s) = default; + string& operator=(const string& s) = default; + string& operator=(string&& s) = default; + + string(const std::string& s): kind(string_t::basic), str(s){} + string(const std::string& s, string_t k): kind(k), str(s){} + string(const char* s): kind(string_t::basic), str(s){} + string(const char* s, string_t k): kind(k), str(s){} + + string(std::string&& s): kind(string_t::basic), str(std::move(s)){} + string(std::string&& s, string_t k): kind(k), str(std::move(s)){} + + string& operator=(const std::string& s) + {kind = string_t::basic; str = s; return *this;} + string& operator=(std::string&& s) + {kind = string_t::basic; str = std::move(s); return *this;} + + operator std::string& () & noexcept {return str;} + operator std::string const& () const& noexcept {return str;} + operator std::string&& () && noexcept {return std::move(str);} + + string& operator+=(const char* rhs) {str += rhs; return *this;} + string& operator+=(const char rhs) {str += rhs; return *this;} + string& operator+=(const std::string& rhs) {str += rhs; return *this;} + string& operator+=(const string& rhs) {str += rhs.str; return *this;} + +#if __cplusplus >= 201703L + explicit string(std::string_view s): kind(string_t::basic), str(s){} + string(std::string_view s, string_t k): kind(k), str(s){} + + string& operator=(std::string_view s) + {kind = string_t::basic; str = s; return *this;} + + explicit operator std::string_view() const noexcept + {return std::string_view(str);} + + string& operator+=(const std::string_view& rhs) {str += rhs; return *this;} +#endif + + string_t kind; + std::string str; +}; + +inline bool operator==(const string& lhs, const string& rhs) +{ + return lhs.kind == rhs.kind && lhs.str == rhs.str; +} +inline bool operator!=(const string& lhs, const string& rhs) +{ + return !(lhs == rhs); +} +inline bool operator<(const string& lhs, const string& rhs) +{ + return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); +} +inline bool operator>(const string& lhs, const string& rhs) +{ + return rhs < lhs; +} +inline bool operator<=(const string& lhs, const string& rhs) +{ + return !(rhs < lhs); +} +inline bool operator>=(const string& lhs, const string& rhs) +{ + return !(lhs < rhs); +} + +inline bool +operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} +inline bool +operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} +inline bool +operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;} +inline bool +operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;} +inline bool +operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} +inline bool +operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} + +inline bool +operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} +inline bool +operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} +inline bool +operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;} +inline bool +operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;} +inline bool +operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} +inline bool +operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} + +inline bool +operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} +inline bool +operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} +inline bool +operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);} +inline bool +operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);} +inline bool +operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} +inline bool +operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} + +inline bool +operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} +inline bool +operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} +inline bool +operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;} +inline bool +operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;} +inline bool +operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} +inline bool +operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const string& s) +{ + if(s.kind == string_t::basic) + { + if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend()) + { + // it contains newline. make it multiline string. + os << "\"\"\"\n"; + for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i) + { + switch(*i) + { + case '\\': {os << "\\\\"; break;} + case '\"': {os << "\\\""; break;} + case '\b': {os << "\\b"; break;} + case '\t': {os << "\\t"; break;} + case '\f': {os << "\\f"; break;} + case '\n': {os << '\n'; break;} + case '\r': + { + // since it is a multiline string, + // CRLF is not needed to be escaped. + if(std::next(i) != e && *std::next(i) == '\n') + { + os << "\r\n"; + ++i; + } + else + { + os << "\\r"; + } + break; + } + default: {os << *i; break;} + } + } + os << "\\\n\"\"\""; + return os; + } + // no newline. make it inline. + os << "\""; + for(const auto c : s.str) + { + switch(c) + { + case '\\': {os << "\\\\"; break;} + case '\"': {os << "\\\""; break;} + case '\b': {os << "\\b"; break;} + case '\t': {os << "\\t"; break;} + case '\f': {os << "\\f"; break;} + case '\n': {os << "\\n"; break;} + case '\r': {os << "\\r"; break;} + default : {os << c; break;} + } + } + os << "\""; + return os; + } + // the string `s` is literal-string. + if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || + std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) + { + // contains newline or single quote. make it multiline. + os << "'''\n" << s.str << "'''"; + return os; + } + // normal literal string + os << '\'' << s.str << '\''; + return os; +} + +} // toml +#endif// TOML11_STRING_H diff --git a/src/toml/traits.hpp b/src/toml/traits.hpp new file mode 100644 index 00000000..0064e374 --- /dev/null +++ b/src/toml/traits.hpp @@ -0,0 +1,325 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_TRAITS_HPP +#define TOML11_TRAITS_HPP + +#include "from.hpp" +#include "into.hpp" + +#include +#include +#include +#include +#include +#include + +#if __cplusplus >= 201703L +#if __has_include() +#include +#endif // has_include() +#endif // cplusplus >= C++17 + +namespace toml +{ +template class T, template class A> +class basic_value; + +namespace detail +{ +// --------------------------------------------------------------------------- +// check whether type T is a kind of container/map class + +struct has_iterator_impl +{ + template static std::true_type check(typename T::iterator*); + template static std::false_type check(...); +}; +struct has_value_type_impl +{ + template static std::true_type check(typename T::value_type*); + template static std::false_type check(...); +}; +struct has_key_type_impl +{ + template static std::true_type check(typename T::key_type*); + template static std::false_type check(...); +}; +struct has_mapped_type_impl +{ + template static std::true_type check(typename T::mapped_type*); + template static std::false_type check(...); +}; +struct has_reserve_method_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval().reserve(std::declval()))*); +}; +struct has_push_back_method_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval().push_back(std::declval()))*); +}; +struct is_comparable_impl +{ + template static std::false_type check(...); + template static std::true_type check( + decltype(std::declval() < std::declval())*); +}; + +struct has_from_toml_method_impl +{ + template class Tb, template class A> + static std::true_type check( + decltype(std::declval().from_toml( + std::declval<::toml::basic_value>()))*); + + template class Tb, template class A> + static std::false_type check(...); +}; +struct has_into_toml_method_impl +{ + template + static std::true_type check(decltype(std::declval().into_toml())*); + template + static std::false_type check(...); +}; + +struct has_specialized_from_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::from*); +}; +struct has_specialized_into_impl +{ + template + static std::false_type check(...); + template)> + static std::true_type check(::toml::from*); +}; + + +/// Intel C++ compiler can not use decltype in parent class declaration, here +/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 +#ifdef __INTEL_COMPILER +#define decltype(...) std::enable_if::type +#endif + +template +struct has_iterator : decltype(has_iterator_impl::check(nullptr)){}; +template +struct has_value_type : decltype(has_value_type_impl::check(nullptr)){}; +template +struct has_key_type : decltype(has_key_type_impl::check(nullptr)){}; +template +struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; +template +struct has_reserve_method : decltype(has_reserve_method_impl::check(nullptr)){}; +template +struct has_push_back_method : decltype(has_push_back_method_impl::check(nullptr)){}; +template +struct is_comparable : decltype(is_comparable_impl::check(nullptr)){}; + +template class Tb, template class A> +struct has_from_toml_method +: decltype(has_from_toml_method_impl::check(nullptr)){}; + +template +struct has_into_toml_method +: decltype(has_into_toml_method_impl::check(nullptr)){}; + +template +struct has_specialized_from : decltype(has_specialized_from_impl::check(nullptr)){}; +template +struct has_specialized_into : decltype(has_specialized_into_impl::check(nullptr)){}; + +#ifdef __INTEL_COMPILER +#undef decltype +#endif + +// --------------------------------------------------------------------------- +// C++17 and/or/not + +#if __cplusplus >= 201703L + +using std::conjunction; +using std::disjunction; +using std::negation; + +#else + +template struct conjunction : std::true_type{}; +template struct conjunction : T{}; +template +struct conjunction : + std::conditional(T::value), conjunction, T>::type +{}; + +template struct disjunction : std::false_type{}; +template struct disjunction : T {}; +template +struct disjunction : + std::conditional(T::value), T, disjunction>::type +{}; + +template +struct negation : std::integral_constant(T::value)>{}; + +#endif + +// --------------------------------------------------------------------------- +// type checkers + +template struct is_std_pair : std::false_type{}; +template +struct is_std_pair> : std::true_type{}; + +template struct is_std_tuple : std::false_type{}; +template +struct is_std_tuple> : std::true_type{}; + +template struct is_std_forward_list : std::false_type{}; +template +struct is_std_forward_list> : std::true_type{}; + +template struct is_chrono_duration: std::false_type{}; +template +struct is_chrono_duration>: std::true_type{}; + +template +struct is_map : conjunction< // map satisfies all the following conditions + has_iterator, // has T::iterator + has_value_type, // has T::value_type + has_key_type, // has T::key_type + has_mapped_type // has T::mapped_type + >{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; + +template +struct is_container : conjunction< + negation>, // not a map + negation>, // not a std::string +#if __cplusplus >= 201703L + negation>, // not a std::string_view +#endif + has_iterator, // has T::iterator + has_value_type // has T::value_type + >{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; + +template +struct is_basic_value: std::false_type{}; +template struct is_basic_value : is_basic_value{}; +template struct is_basic_value : is_basic_value{}; +template struct is_basic_value : is_basic_value{}; +template struct is_basic_value : is_basic_value{}; +template class M, template class V> +struct is_basic_value<::toml::basic_value>: std::true_type{}; + +// --------------------------------------------------------------------------- +// C++14 index_sequence + +#if __cplusplus >= 201402L + +using std::index_sequence; +using std::make_index_sequence; + +#else + +template struct index_sequence{}; + +template struct push_back_index_sequence{}; +template +struct push_back_index_sequence, N> +{ + typedef index_sequence type; +}; + +template +struct index_sequence_maker +{ + typedef typename push_back_index_sequence< + typename index_sequence_maker::type, N>::type type; +}; +template<> +struct index_sequence_maker<0> +{ + typedef index_sequence<0> type; +}; +template +using make_index_sequence = typename index_sequence_maker::type; + +#endif // __cplusplus >= 2014 + +// --------------------------------------------------------------------------- +// C++14 enable_if_t + +#if __cplusplus >= 201402L + +using std::enable_if_t; + +#else + +template +using enable_if_t = typename std::enable_if::type; + +#endif // __cplusplus >= 2014 + +// --------------------------------------------------------------------------- +// return_type_of_t + +#if __cplusplus >= 201703L + +template +using return_type_of_t = std::invoke_result_t; + +#else +// result_of is deprecated after C++17 +template +using return_type_of_t = typename std::result_of::type; + +#endif + +// --------------------------------------------------------------------------- +// is_string_literal +// +// to use this, pass `typename remove_reference::type` to T. + +template +struct is_string_literal: +disjunction< + std::is_same, + conjunction< + std::is_array, + std::is_same::type> + > + >{}; + +// --------------------------------------------------------------------------- +// C++20 remove_cvref_t + +template +struct remove_cvref +{ + using type = typename std::remove_cv< + typename std::remove_reference::type>::type; +}; + +template +using remove_cvref_t = typename remove_cvref::type; + +}// detail +}//toml +#endif // TOML_TRAITS diff --git a/src/toml/types.hpp b/src/toml/types.hpp new file mode 100644 index 00000000..7bf4b2e8 --- /dev/null +++ b/src/toml/types.hpp @@ -0,0 +1,173 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_TYPES_HPP +#define TOML11_TYPES_HPP +#include +#include + +#include "comments.hpp" +#include "datetime.hpp" +#include "string.hpp" +#include "traits.hpp" + +namespace toml +{ + +template class Table, // map-like class + template class Array> // vector-like class +class basic_value; + +using character = char; +using key = std::string; + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wshadow" +#endif + +using boolean = bool; +using integer = std::int64_t; +using floating = double; // "float" is a keyward, cannot use it here. +// the following stuffs are structs defined here, so aliases are not needed. +// - string +// - offset_datetime +// - offset_datetime +// - local_datetime +// - local_date +// - local_time + +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif + +// default toml::value and default array/table. these are defined after defining +// basic_value itself. +// using value = basic_value; +// using array = typename value::array_type; +// using table = typename value::table_type; + +// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in +// GCC -Wshadow=global. +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# if 7 <= __GNUC__ +# pragma GCC diagnostic ignored "-Wshadow=global" +# else // gcc-6 or older +# pragma GCC diagnostic ignored "-Wshadow" +# endif +#endif +enum class value_t : std::uint8_t +{ + empty = 0, + boolean = 1, + integer = 2, + floating = 3, + string = 4, + offset_datetime = 5, + local_datetime = 6, + local_date = 7, + local_time = 8, + array = 9, + table = 10, +}; +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif + +template +inline std::basic_ostream& +operator<<(std::basic_ostream& os, value_t t) +{ + switch(t) + { + case value_t::boolean : os << "boolean"; return os; + case value_t::integer : os << "integer"; return os; + case value_t::floating : os << "floating"; return os; + case value_t::string : os << "string"; return os; + case value_t::offset_datetime : os << "offset_datetime"; return os; + case value_t::local_datetime : os << "local_datetime"; return os; + case value_t::local_date : os << "local_date"; return os; + case value_t::local_time : os << "local_time"; return os; + case value_t::array : os << "array"; return os; + case value_t::table : os << "table"; return os; + case value_t::empty : os << "empty"; return os; + default : os << "unknown"; return os; + } +} + +template, + typename alloc = std::allocator> +inline std::basic_string stringize(value_t t) +{ + std::basic_ostringstream oss; + oss << t; + return oss.str(); +} + +namespace detail +{ + +// helper to define a type that represents a value_t value. +template +using value_t_constant = std::integral_constant; + +// meta-function that convertes from value_t to the exact toml type that corresponds to. +// It takes toml::basic_value type because array and table types depend on it. +template struct enum_to_type {using type = void ;}; +template struct enum_to_type{using type = void ;}; +template struct enum_to_type{using type = boolean ;}; +template struct enum_to_type{using type = integer ;}; +template struct enum_to_type{using type = floating ;}; +template struct enum_to_type{using type = string ;}; +template struct enum_to_type{using type = offset_datetime ;}; +template struct enum_to_type{using type = local_datetime ;}; +template struct enum_to_type{using type = local_date ;}; +template struct enum_to_type{using type = local_time ;}; +template struct enum_to_type{using type = typename Value::array_type;}; +template struct enum_to_type{using type = typename Value::table_type;}; + +// meta-function that converts from an exact toml type to the enum that corresponds to. +template +struct type_to_enum : std::conditional< + std::is_same::value, // if T == array_type, + value_t_constant, // then value_t::array + typename std::conditional< // else... + std::is_same::value, // if T == table_type + value_t_constant, // then value_t::table + value_t_constant // else value_t::empty + >::type + >::type {}; +template struct type_to_enum: value_t_constant {}; +template struct type_to_enum: value_t_constant {}; +template struct type_to_enum: value_t_constant {}; +template struct type_to_enum: value_t_constant {}; +template struct type_to_enum: value_t_constant {}; +template struct type_to_enum: value_t_constant {}; +template struct type_to_enum: value_t_constant {}; +template struct type_to_enum: value_t_constant {}; + +// meta-function that checks the type T is the same as one of the toml::* types. +template +struct is_exact_toml_type : disjunction< + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same + >{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type: is_exact_toml_type{}; + +} // detail +} // toml + +#endif// TOML11_TYPES_H diff --git a/src/toml/utility.hpp b/src/toml/utility.hpp new file mode 100644 index 00000000..4a6b4309 --- /dev/null +++ b/src/toml/utility.hpp @@ -0,0 +1,149 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_UTILITY_HPP +#define TOML11_UTILITY_HPP +#include +#include +#include + +#include "traits.hpp" + +#if __cplusplus >= 201402L +# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] +#elif defined(__GNUC__) +# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg))) +#elif defined(_MSC_VER) +# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg)) +#else +# define TOML11_MARK_AS_DEPRECATED +#endif + +namespace toml +{ + +#if __cplusplus >= 201402L + +using std::make_unique; + +#else + +template +inline std::unique_ptr make_unique(Ts&& ... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +#endif // __cplusplus >= 2014 + +namespace detail +{ +template +void try_reserve_impl(Container& container, std::size_t N, std::true_type) +{ + container.reserve(N); + return; +} +template +void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept +{ + return; +} +} // detail + +template +void try_reserve(Container& container, std::size_t N) +{ + if(N <= container.size()) {return;} + detail::try_reserve_impl(container, N, detail::has_reserve_method{}); + return; +} + +namespace detail +{ +inline std::string concat_to_string_impl(std::ostringstream& oss) +{ + return oss.str(); +} +template +std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) +{ + oss << std::forward(head); + return concat_to_string_impl(oss, std::forward(tail) ... ); +} +} // detail + +template +std::string concat_to_string(Ts&& ... args) +{ + std::ostringstream oss; + oss << std::boolalpha << std::fixed; + return detail::concat_to_string_impl(oss, std::forward(args) ...); +} + +template +T from_string(const std::string& str, T opt) +{ + T v(opt); + std::istringstream iss(str); + iss >> v; + return v; +} + +namespace detail +{ +#if __cplusplus >= 201402L +template +decltype(auto) last_one(T&& tail) noexcept +{ + return std::forward(tail); +} + +template +decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept +{ + return last_one(std::forward(tail)...); +} +#else // C++11 +// The following code +// ```cpp +// 1 | template +// 2 | auto last_one(T&& /*head*/, Ts&& ... tail) +// 3 | -> decltype(last_one(std::forward(tail)...)) +// 4 | { +// 5 | return last_one(std::forward(tail)...); +// 6 | } +// ``` +// does not work because the function `last_one(...)` is not yet defined at +// line #3, so `decltype()` cannot deduce the type returned from `last_one`. +// So we need to determine return type in a different way, like a meta func. + +template +struct last_one_in_pack +{ + using type = typename last_one_in_pack::type; +}; +template +struct last_one_in_pack +{ + using type = T; +}; +template +using last_one_in_pack_t = typename last_one_in_pack::type; + +template +T&& last_one(T&& tail) noexcept +{ + return std::forward(tail); +} +template +enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t> +last_one(T&& /*head*/, Ts&& ... tail) +{ + return last_one(std::forward(tail)...); +} + +#endif +} // detail + +}// toml +#endif // TOML11_UTILITY diff --git a/src/toml/value.hpp b/src/toml/value.hpp new file mode 100644 index 00000000..7c68514d --- /dev/null +++ b/src/toml/value.hpp @@ -0,0 +1,2035 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_VALUE_HPP +#define TOML11_VALUE_HPP +#include + +#include "comments.hpp" +#include "exception.hpp" +#include "into.hpp" +#include "region.hpp" +#include "source_location.hpp" +#include "storage.hpp" +#include "traits.hpp" +#include "types.hpp" +#include "utility.hpp" + +namespace toml +{ + +namespace detail +{ + +// to show error messages. not recommended for users. +template +inline region_base const* get_region(const Value& v) +{ + return v.region_info_.get(); +} + +template +void change_region(Value& v, region reg) +{ + v.region_info_ = std::make_shared(std::move(reg)); + return; +} + +template +[[noreturn]] inline void +throw_bad_cast(const std::string& funcname, value_t actual, const Value& v) +{ + throw type_error(detail::format_underline( + concat_to_string(funcname, "bad_cast to ", Expected), { + {v.location(), concat_to_string("the actual type is ", actual)} + }), v.location()); +} + +// Throw `out_of_range` from `toml::value::at()` and `toml::find()` +// after generating an error message. +// +// The implementation is a bit complicated and there are many edge-cases. +// If you are not interested in the error message generation, just skip this. +template +[[noreturn]] void +throw_key_not_found_error(const Value& v, const key& ky) +{ + // The top-level table has its region at the first character of the file. + // That means that, in the case when a key is not found in the top-level + // table, the error message points to the first character. If the file has + // its first table at the first line, the error message would be like this. + // ```console + // [error] key "a" not found + // --> example.toml + // | + // 1 | [table] + // | ^------ in this table + // ``` + // It actually points to the top-level table at the first character, + // not `[table]`. But it is too confusing. To avoid the confusion, the error + // message should explicitly say "key not found in the top-level table", + // or "the parsed file is empty" if there is no content at all (0 bytes in file). + const auto loc = v.location(); + if(loc.line() == 1 && loc.region() == 0) + { + // First line with a zero-length region means "empty file". + // The region will be generated at `parse_toml_file` function + // if the file contains no bytes. + throw std::out_of_range(format_underline(concat_to_string( + "key \"", ky, "\" not found in the top-level table"), { + {loc, "the parsed file is empty"} + })); + } + else if(loc.line() == 1 && loc.region() == 1) + { + // Here it assumes that top-level table starts at the first character. + // The region corresponds to the top-level table will be generated at + // `parse_toml_file` function. + // It also assumes that the top-level table size is just one and + // the line number is `1`. It is always satisfied. And those conditions + // are satisfied only if the table is the top-level table. + // + // 1. one-character dot-key at the first line + // ```toml + // a.b = "c" + // ``` + // toml11 counts whole key as the table key. Here, `a.b` is the region + // of the table "a". It could be counter intuitive, but it works. + // The size of the region is 3, not 1. The above example is the shortest + // dot-key example. The size cannot be 1. + // + // 2. one-character inline-table at the first line + // ```toml + // a = {b = "c"} + // ``` + // toml11 consideres the inline table body as the table region. Here, + // `{b = "c"}` is the region of the table "a". The size of the region + // is 9, not 1. The shotest inline table still has two characters, `{` + // and `}`. The size cannot be 1. + // + // 3. one-character table declaration at the first line + // ```toml + // [a] + // ``` + // toml11 consideres the whole table key as the table region. Here, + // `[a]` is the table region. The size is 3, not 1. + // + throw std::out_of_range(format_underline(concat_to_string( + "key \"", ky, "\" not found in the top-level table"), { + {loc, "the top-level table starts here"} + })); + } + else + { + // normal table. + throw std::out_of_range(format_underline(concat_to_string( + "key \"", ky, "\" not found"), { {loc, "in this table"} })); + } +} + +// switch by `value_t` at the compile time. +template +struct switch_cast {}; +#define TOML11_GENERATE_SWITCH_CASTER(TYPE) \ + template<> \ + struct switch_cast \ + { \ + template \ + static typename Value::TYPE##_type& invoke(Value& v) \ + { \ + return v.as_##TYPE(); \ + } \ + template \ + static typename Value::TYPE##_type const& invoke(const Value& v) \ + { \ + return v.as_##TYPE(); \ + } \ + template \ + static typename Value::TYPE##_type&& invoke(Value&& v) \ + { \ + return std::move(v).as_##TYPE(); \ + } \ + }; \ + /**/ +TOML11_GENERATE_SWITCH_CASTER(boolean) +TOML11_GENERATE_SWITCH_CASTER(integer) +TOML11_GENERATE_SWITCH_CASTER(floating) +TOML11_GENERATE_SWITCH_CASTER(string) +TOML11_GENERATE_SWITCH_CASTER(offset_datetime) +TOML11_GENERATE_SWITCH_CASTER(local_datetime) +TOML11_GENERATE_SWITCH_CASTER(local_date) +TOML11_GENERATE_SWITCH_CASTER(local_time) +TOML11_GENERATE_SWITCH_CASTER(array) +TOML11_GENERATE_SWITCH_CASTER(table) + +#undef TOML11_GENERATE_SWITCH_CASTER + +}// detail + +template class Table = std::unordered_map, + template class Array = std::vector> +class basic_value +{ + template + static void assigner(T& dst, U&& v) + { + const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); + assert(tmp == std::addressof(dst)); + (void)tmp; + } + + using region_base = detail::region_base; + + template class T, + template class A> + friend class basic_value; + + public: + + using comment_type = Comment; + using key_type = ::toml::key; + using value_type = basic_value; + using boolean_type = ::toml::boolean; + using integer_type = ::toml::integer; + using floating_type = ::toml::floating; + using string_type = ::toml::string; + using local_time_type = ::toml::local_time; + using local_date_type = ::toml::local_date; + using local_datetime_type = ::toml::local_datetime; + using offset_datetime_type = ::toml::offset_datetime; + using array_type = Array; + using table_type = Table; + + public: + + basic_value() noexcept + : type_(value_t::empty), + region_info_(std::make_shared(region_base{})) + {} + ~basic_value() noexcept {this->cleanup();} + + basic_value(const basic_value& v) + : type_(v.type()), region_info_(v.region_info_), comments_(v.comments_) + { + switch(v.type()) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default: break; + } + } + basic_value(basic_value&& v) + : type_(v.type()), region_info_(std::move(v.region_info_)), + comments_(std::move(v.comments_)) + { + switch(this->type_) // here this->type_ is already initialized + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default: break; + } + } + basic_value& operator=(const basic_value& v) + { + this->cleanup(); + this->region_info_ = v.region_info_; + this->comments_ = v.comments_; + this->type_ = v.type(); + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default: break; + } + return *this; + } + basic_value& operator=(basic_value&& v) + { + this->cleanup(); + this->region_info_ = std::move(v.region_info_); + this->comments_ = std::move(v.comments_); + this->type_ = v.type(); + switch(this->type_) + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default: break; + } + return *this; + } + + // overwrite comments ---------------------------------------------------- + + basic_value(const basic_value& v, std::vector com) + : type_(v.type()), region_info_(v.region_info_), + comments_(std::move(com)) + { + switch(v.type()) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : assigner(array_ , v.array_ ); break; + case value_t::table : assigner(table_ , v.table_ ); break; + default: break; + } + } + + basic_value(basic_value&& v, std::vector com) + : type_(v.type()), region_info_(std::move(v.region_info_)), + comments_(std::move(com)) + { + switch(this->type_) // here this->type_ is already initialized + { + case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::string : assigner(string_ , std::move(v.string_ )); break; + case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::array : assigner(array_ , std::move(v.array_ )); break; + case value_t::table : assigner(table_ , std::move(v.table_ )); break; + default: break; + } + } + + // ----------------------------------------------------------------------- + // conversion between different basic_values. + template class T, + template class A> + basic_value(const basic_value& v) + : type_(v.type()), region_info_(v.region_info_), comments_(v.comments()) + { + switch(v.type()) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : + { + array_type tmp(v.as_array(std::nothrow).begin(), + v.as_array(std::nothrow).end()); + assigner(array_, std::move(tmp)); + break; + } + case value_t::table : + { + table_type tmp(v.as_table(std::nothrow).begin(), + v.as_table(std::nothrow).end()); + assigner(table_, std::move(tmp)); + break; + } + default: break; + } + } + template class T, + template class A> + basic_value(const basic_value& v, std::vector com) + : type_(v.type()), region_info_(v.region_info_), + comments_(std::move(com)) + { + switch(v.type()) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : + { + array_type tmp(v.as_array(std::nothrow).begin(), + v.as_array(std::nothrow).end()); + assigner(array_, std::move(tmp)); + break; + } + case value_t::table : + { + table_type tmp(v.as_table(std::nothrow).begin(), + v.as_table(std::nothrow).end()); + assigner(table_, std::move(tmp)); + break; + } + default: break; + } + } + template class T, + template class A> + basic_value& operator=(const basic_value& v) + { + this->region_info_ = v.region_info_; + this->comments_ = comment_type(v.comments()); + this->type_ = v.type(); + switch(v.type()) + { + case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::integer : assigner(integer_ , v.integer_ ); break; + case value_t::floating : assigner(floating_ , v.floating_ ); break; + case value_t::string : assigner(string_ , v.string_ ); break; + case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; + case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; + case value_t::array : + { + array_type tmp(v.as_array(std::nothrow).begin(), + v.as_array(std::nothrow).end()); + assigner(array_, std::move(tmp)); + break; + } + case value_t::table : + { + table_type tmp(v.as_table(std::nothrow).begin(), + v.as_table(std::nothrow).end()); + assigner(table_, std::move(tmp)); + break; + } + default: break; + } + return *this; + } + + // boolean ============================================================== + + basic_value(boolean b) + : type_(value_t::boolean), + region_info_(std::make_shared(region_base{})) + { + assigner(this->boolean_, b); + } + basic_value& operator=(boolean b) + { + this->cleanup(); + this->type_ = value_t::boolean; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->boolean_, b); + return *this; + } + basic_value(boolean b, std::vector com) + : type_(value_t::boolean), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->boolean_, b); + } + + // integer ============================================================== + + template, detail::negation>>::value, + std::nullptr_t>::type = nullptr> + basic_value(T i) + : type_(value_t::integer), + region_info_(std::make_shared(region_base{})) + { + assigner(this->integer_, static_cast(i)); + } + + template, detail::negation>>::value, + std::nullptr_t>::type = nullptr> + basic_value& operator=(T i) + { + this->cleanup(); + this->type_ = value_t::integer; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->integer_, static_cast(i)); + return *this; + } + + template, detail::negation>>::value, + std::nullptr_t>::type = nullptr> + basic_value(T i, std::vector com) + : type_(value_t::integer), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->integer_, static_cast(i)); + } + + // floating ============================================================= + + template::value, std::nullptr_t>::type = nullptr> + basic_value(T f) + : type_(value_t::floating), + region_info_(std::make_shared(region_base{})) + { + assigner(this->floating_, static_cast(f)); + } + + + template::value, std::nullptr_t>::type = nullptr> + basic_value& operator=(T f) + { + this->cleanup(); + this->type_ = value_t::floating; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->floating_, static_cast(f)); + return *this; + } + + template::value, std::nullptr_t>::type = nullptr> + basic_value(T f, std::vector com) + : type_(value_t::floating), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->floating_, f); + } + + // string =============================================================== + + basic_value(toml::string s) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, std::move(s)); + } + basic_value& operator=(toml::string s) + { + this->cleanup(); + this->type_ = value_t::string ; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, s); + return *this; + } + basic_value(toml::string s, std::vector com) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->string_, std::move(s)); + } + + basic_value(std::string s) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::move(s))); + } + basic_value& operator=(std::string s) + { + this->cleanup(); + this->type_ = value_t::string ; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, toml::string(std::move(s))); + return *this; + } + basic_value(std::string s, string_t kind) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::move(s), kind)); + } + basic_value(std::string s, std::vector com) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->string_, toml::string(std::move(s))); + } + basic_value(std::string s, string_t kind, std::vector com) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->string_, toml::string(std::move(s), kind)); + } + + basic_value(const char* s) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::string(s))); + } + basic_value& operator=(const char* s) + { + this->cleanup(); + this->type_ = value_t::string ; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, toml::string(std::string(s))); + return *this; + } + basic_value(const char* s, string_t kind) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::string(s), kind)); + } + basic_value(const char* s, std::vector com) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->string_, toml::string(std::string(s))); + } + basic_value(const char* s, string_t kind, std::vector com) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->string_, toml::string(std::string(s), kind)); + } + +#if __cplusplus >= 201703L + basic_value(std::string_view s) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(s)); + } + basic_value& operator=(std::string_view s) + { + this->cleanup(); + this->type_ = value_t::string ; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, toml::string(s)); + return *this; + } + basic_value(std::string_view s, std::vector com) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->string_, toml::string(s)); + } + basic_value(std::string_view s, string_t kind) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(s, kind)); + } + basic_value(std::string_view s, string_t kind, std::vector com) + : type_(value_t::string), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->string_, toml::string(s, kind)); + } +#endif + + // local date =========================================================== + + basic_value(const local_date& ld) + : type_(value_t::local_date), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_date_, ld); + } + basic_value& operator=(const local_date& ld) + { + this->cleanup(); + this->type_ = value_t::local_date; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_date_, ld); + return *this; + } + basic_value(const local_date& ld, std::vector com) + : type_(value_t::local_date), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->local_date_, ld); + } + + // local time =========================================================== + + basic_value(const local_time& lt) + : type_(value_t::local_time), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_time_, lt); + } + basic_value(const local_time& lt, std::vector com) + : type_(value_t::local_time), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->local_time_, lt); + } + basic_value& operator=(const local_time& lt) + { + this->cleanup(); + this->type_ = value_t::local_time; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_time_, lt); + return *this; + } + + template + basic_value(const std::chrono::duration& dur) + : type_(value_t::local_time), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_time_, local_time(dur)); + } + template + basic_value(const std::chrono::duration& dur, + std::vector com) + : type_(value_t::local_time), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->local_time_, local_time(dur)); + } + template + basic_value& operator=(const std::chrono::duration& dur) + { + this->cleanup(); + this->type_ = value_t::local_time; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_time_, local_time(dur)); + return *this; + } + + // local datetime ======================================================= + + basic_value(const local_datetime& ldt) + : type_(value_t::local_datetime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_datetime_, ldt); + } + basic_value(const local_datetime& ldt, std::vector com) + : type_(value_t::local_datetime), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->local_datetime_, ldt); + } + basic_value& operator=(const local_datetime& ldt) + { + this->cleanup(); + this->type_ = value_t::local_datetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_datetime_, ldt); + return *this; + } + + // offset datetime ====================================================== + + basic_value(const offset_datetime& odt) + : type_(value_t::offset_datetime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->offset_datetime_, odt); + } + basic_value(const offset_datetime& odt, std::vector com) + : type_(value_t::offset_datetime), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->offset_datetime_, odt); + } + basic_value& operator=(const offset_datetime& odt) + { + this->cleanup(); + this->type_ = value_t::offset_datetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->offset_datetime_, odt); + return *this; + } + basic_value(const std::chrono::system_clock::time_point& tp) + : type_(value_t::offset_datetime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->offset_datetime_, offset_datetime(tp)); + } + basic_value(const std::chrono::system_clock::time_point& tp, + std::vector com) + : type_(value_t::offset_datetime), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->offset_datetime_, offset_datetime(tp)); + } + basic_value& operator=(const std::chrono::system_clock::time_point& tp) + { + this->cleanup(); + this->type_ = value_t::offset_datetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->offset_datetime_, offset_datetime(tp)); + return *this; + } + + // array ================================================================ + + basic_value(const array_type& ary) + : type_(value_t::array), + region_info_(std::make_shared(region_base{})) + { + assigner(this->array_, ary); + } + basic_value(const array_type& ary, std::vector com) + : type_(value_t::array), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->array_, ary); + } + basic_value& operator=(const array_type& ary) + { + this->cleanup(); + this->type_ = value_t::array ; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->array_, ary); + return *this; + } + + // array (initializer_list) ---------------------------------------------- + + template::value, + std::nullptr_t>::type = nullptr> + basic_value(std::initializer_list list) + : type_(value_t::array), + region_info_(std::make_shared(region_base{})) + { + array_type ary(list.begin(), list.end()); + assigner(this->array_, std::move(ary)); + } + template::value, + std::nullptr_t>::type = nullptr> + basic_value(std::initializer_list list, std::vector com) + : type_(value_t::array), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + array_type ary(list.begin(), list.end()); + assigner(this->array_, std::move(ary)); + } + template::value, + std::nullptr_t>::type = nullptr> + basic_value& operator=(std::initializer_list list) + { + this->cleanup(); + this->type_ = value_t::array; + this->region_info_ = std::make_shared(region_base{}); + + array_type ary(list.begin(), list.end()); + assigner(this->array_, std::move(ary)); + return *this; + } + + // array (STL Containers) ------------------------------------------------ + + template>, + detail::is_container + >::value, std::nullptr_t>::type = nullptr> + basic_value(const T& list) + : type_(value_t::array), + region_info_(std::make_shared(region_base{})) + { + static_assert(std::is_convertible::value, + "elements of a container should be convertible to toml::value"); + + array_type ary(list.size()); + std::copy(list.begin(), list.end(), ary.begin()); + assigner(this->array_, std::move(ary)); + } + template>, + detail::is_container + >::value, std::nullptr_t>::type = nullptr> + basic_value(const T& list, std::vector com) + : type_(value_t::array), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + static_assert(std::is_convertible::value, + "elements of a container should be convertible to toml::value"); + + array_type ary(list.size()); + std::copy(list.begin(), list.end(), ary.begin()); + assigner(this->array_, std::move(ary)); + } + template>, + detail::is_container + >::value, std::nullptr_t>::type = nullptr> + basic_value& operator=(const T& list) + { + static_assert(std::is_convertible::value, + "elements of a container should be convertible to toml::value"); + + this->cleanup(); + this->type_ = value_t::array; + this->region_info_ = std::make_shared(region_base{}); + + array_type ary(list.size()); + std::copy(list.begin(), list.end(), ary.begin()); + assigner(this->array_, std::move(ary)); + return *this; + } + + // table ================================================================ + + basic_value(const table_type& tab) + : type_(value_t::table), + region_info_(std::make_shared(region_base{})) + { + assigner(this->table_, tab); + } + basic_value(const table_type& tab, std::vector com) + : type_(value_t::table), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + assigner(this->table_, tab); + } + basic_value& operator=(const table_type& tab) + { + this->cleanup(); + this->type_ = value_t::table; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->table_, tab); + return *this; + } + + // initializer-list ------------------------------------------------------ + + basic_value(std::initializer_list> list) + : type_(value_t::table), + region_info_(std::make_shared(region_base{})) + { + table_type tab; + for(const auto& elem : list) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + } + + basic_value(std::initializer_list> list, + std::vector com) + : type_(value_t::table), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + table_type tab; + for(const auto& elem : list) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + } + basic_value& operator=(std::initializer_list> list) + { + this->cleanup(); + this->type_ = value_t::table; + this->region_info_ = std::make_shared(region_base{}); + + table_type tab; + for(const auto& elem : list) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + return *this; + } + + // other table-like ----------------------------------------------------- + + template>, + detail::is_map + >::value, std::nullptr_t>::type = nullptr> + basic_value(const Map& mp) + : type_(value_t::table), + region_info_(std::make_shared(region_base{})) + { + table_type tab; + for(const auto& elem : mp) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + } + template>, + detail::is_map + >::value, std::nullptr_t>::type = nullptr> + basic_value(const Map& mp, std::vector com) + : type_(value_t::table), + region_info_(std::make_shared(region_base{})), + comments_(std::move(com)) + { + table_type tab; + for(const auto& elem : mp) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + } + template>, + detail::is_map + >::value, std::nullptr_t>::type = nullptr> + basic_value& operator=(const Map& mp) + { + this->cleanup(); + this->type_ = value_t::table; + this->region_info_ = std::make_shared(region_base{}); + + table_type tab; + for(const auto& elem : mp) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + return *this; + } + + // user-defined ========================================================= + + // convert using into_toml() method ------------------------------------- + + template::value, std::nullptr_t>::type = nullptr> + basic_value(const T& ud): basic_value(ud.into_toml()) {} + + template::value, std::nullptr_t>::type = nullptr> + basic_value(const T& ud, std::vector com) + : basic_value(ud.into_toml(), std::move(com)) + {} + template::value, std::nullptr_t>::type = nullptr> + basic_value& operator=(const T& ud) + { + *this = ud.into_toml(); + return *this; + } + + // convert using into struct ----------------------------------------- + + template)> + basic_value(const T& ud): basic_value(::toml::into::into_toml(ud)) {} + template)> + basic_value(const T& ud, std::vector com) + : basic_value(::toml::into::into_toml(ud), std::move(com)) + {} + template)> + basic_value& operator=(const T& ud) + { + *this = ::toml::into::into_toml(ud); + return *this; + } + + // for internal use ------------------------------------------------------ + // + // Those constructors take detail::region that contains parse result. + + basic_value(boolean b, detail::region reg, std::vector cm) + : type_(value_t::boolean), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->boolean_, b); + } + template, detail::negation> + >::value, std::nullptr_t>::type = nullptr> + basic_value(T i, detail::region reg, std::vector cm) + : type_(value_t::integer), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->integer_, static_cast(i)); + } + template::value, std::nullptr_t>::type = nullptr> + basic_value(T f, detail::region reg, std::vector cm) + : type_(value_t::floating), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->floating_, static_cast(f)); + } + basic_value(toml::string s, detail::region reg, + std::vector cm) + : type_(value_t::string), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->string_, std::move(s)); + } + basic_value(const local_date& ld, detail::region reg, + std::vector cm) + : type_(value_t::local_date), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->local_date_, ld); + } + basic_value(const local_time& lt, detail::region reg, + std::vector cm) + : type_(value_t::local_time), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->local_time_, lt); + } + basic_value(const local_datetime& ldt, detail::region reg, + std::vector cm) + : type_(value_t::local_datetime), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->local_datetime_, ldt); + } + basic_value(const offset_datetime& odt, detail::region reg, + std::vector cm) + : type_(value_t::offset_datetime), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->offset_datetime_, odt); + } + basic_value(const array_type& ary, detail::region reg, + std::vector cm) + : type_(value_t::array), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->array_, ary); + } + basic_value(const table_type& tab, detail::region reg, + std::vector cm) + : type_(value_t::table), + region_info_(std::make_shared(std::move(reg))), + comments_(std::move(cm)) + { + assigner(this->table_, tab); + } + + template::value, + std::nullptr_t>::type = nullptr> + basic_value(std::pair parse_result, std::vector com) + : basic_value(std::move(parse_result.first), + std::move(parse_result.second), + std::move(com)) + {} + + // type checking and casting ============================================ + + template::value, + std::nullptr_t>::type = nullptr> + bool is() const noexcept + { + return detail::type_to_enum::value == this->type_; + } + bool is(value_t t) const noexcept {return t == this->type_;} + + bool is_uninitialized() const noexcept {return this->is(value_t::empty );} + bool is_boolean() const noexcept {return this->is(value_t::boolean );} + bool is_integer() const noexcept {return this->is(value_t::integer );} + bool is_floating() const noexcept {return this->is(value_t::floating );} + bool is_string() const noexcept {return this->is(value_t::string );} + bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} + bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} + bool is_local_date() const noexcept {return this->is(value_t::local_date );} + bool is_local_time() const noexcept {return this->is(value_t::local_time );} + bool is_array() const noexcept {return this->is(value_t::array );} + bool is_table() const noexcept {return this->is(value_t::table );} + + value_t type() const noexcept {return type_;} + + template + typename detail::enum_to_type::type& cast() & + { + if(this->type_ != T) + { + detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); + } + return detail::switch_cast::invoke(*this); + } + template + typename detail::enum_to_type::type const& cast() const& + { + if(this->type_ != T) + { + detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); + } + return detail::switch_cast::invoke(*this); + } + template + typename detail::enum_to_type::type&& cast() && + { + if(this->type_ != T) + { + detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); + } + return detail::switch_cast::invoke(std::move(*this)); + } + + // ------------------------------------------------------------------------ + // nothrow version + + boolean const& as_boolean (const std::nothrow_t&) const& noexcept {return this->boolean_;} + integer const& as_integer (const std::nothrow_t&) const& noexcept {return this->integer_;} + floating const& as_floating (const std::nothrow_t&) const& noexcept {return this->floating_;} + string const& as_string (const std::nothrow_t&) const& noexcept {return this->string_;} + offset_datetime const& as_offset_datetime(const std::nothrow_t&) const& noexcept {return this->offset_datetime_;} + local_datetime const& as_local_datetime (const std::nothrow_t&) const& noexcept {return this->local_datetime_;} + local_date const& as_local_date (const std::nothrow_t&) const& noexcept {return this->local_date_;} + local_time const& as_local_time (const std::nothrow_t&) const& noexcept {return this->local_time_;} + array_type const& as_array (const std::nothrow_t&) const& noexcept {return this->array_.value();} + table_type const& as_table (const std::nothrow_t&) const& noexcept {return this->table_.value();} + + boolean & as_boolean (const std::nothrow_t&) & noexcept {return this->boolean_;} + integer & as_integer (const std::nothrow_t&) & noexcept {return this->integer_;} + floating & as_floating (const std::nothrow_t&) & noexcept {return this->floating_;} + string & as_string (const std::nothrow_t&) & noexcept {return this->string_;} + offset_datetime& as_offset_datetime(const std::nothrow_t&) & noexcept {return this->offset_datetime_;} + local_datetime & as_local_datetime (const std::nothrow_t&) & noexcept {return this->local_datetime_;} + local_date & as_local_date (const std::nothrow_t&) & noexcept {return this->local_date_;} + local_time & as_local_time (const std::nothrow_t&) & noexcept {return this->local_time_;} + array_type & as_array (const std::nothrow_t&) & noexcept {return this->array_.value();} + table_type & as_table (const std::nothrow_t&) & noexcept {return this->table_.value();} + + boolean && as_boolean (const std::nothrow_t&) && noexcept {return std::move(this->boolean_);} + integer && as_integer (const std::nothrow_t&) && noexcept {return std::move(this->integer_);} + floating && as_floating (const std::nothrow_t&) && noexcept {return std::move(this->floating_);} + string && as_string (const std::nothrow_t&) && noexcept {return std::move(this->string_);} + offset_datetime&& as_offset_datetime(const std::nothrow_t&) && noexcept {return std::move(this->offset_datetime_);} + local_datetime && as_local_datetime (const std::nothrow_t&) && noexcept {return std::move(this->local_datetime_);} + local_date && as_local_date (const std::nothrow_t&) && noexcept {return std::move(this->local_date_);} + local_time && as_local_time (const std::nothrow_t&) && noexcept {return std::move(this->local_time_);} + array_type && as_array (const std::nothrow_t&) && noexcept {return std::move(this->array_.value());} + table_type && as_table (const std::nothrow_t&) && noexcept {return std::move(this->table_.value());} + + // ======================================================================== + // throw version + // ------------------------------------------------------------------------ + // const reference {{{ + + boolean const& as_boolean() const& + { + if(this->type_ != value_t::boolean) + { + detail::throw_bad_cast( + "toml::value::as_boolean(): ", this->type_, *this); + } + return this->boolean_; + } + integer const& as_integer() const& + { + if(this->type_ != value_t::integer) + { + detail::throw_bad_cast( + "toml::value::as_integer(): ", this->type_, *this); + } + return this->integer_; + } + floating const& as_floating() const& + { + if(this->type_ != value_t::floating) + { + detail::throw_bad_cast( + "toml::value::as_floating(): ", this->type_, *this); + } + return this->floating_; + } + string const& as_string() const& + { + if(this->type_ != value_t::string) + { + detail::throw_bad_cast( + "toml::value::as_string(): ", this->type_, *this); + } + return this->string_; + } + offset_datetime const& as_offset_datetime() const& + { + if(this->type_ != value_t::offset_datetime) + { + detail::throw_bad_cast( + "toml::value::as_offset_datetime(): ", this->type_, *this); + } + return this->offset_datetime_; + } + local_datetime const& as_local_datetime() const& + { + if(this->type_ != value_t::local_datetime) + { + detail::throw_bad_cast( + "toml::value::as_local_datetime(): ", this->type_, *this); + } + return this->local_datetime_; + } + local_date const& as_local_date() const& + { + if(this->type_ != value_t::local_date) + { + detail::throw_bad_cast( + "toml::value::as_local_date(): ", this->type_, *this); + } + return this->local_date_; + } + local_time const& as_local_time() const& + { + if(this->type_ != value_t::local_time) + { + detail::throw_bad_cast( + "toml::value::as_local_time(): ", this->type_, *this); + } + return this->local_time_; + } + array_type const& as_array() const& + { + if(this->type_ != value_t::array) + { + detail::throw_bad_cast( + "toml::value::as_array(): ", this->type_, *this); + } + return this->array_.value(); + } + table_type const& as_table() const& + { + if(this->type_ != value_t::table) + { + detail::throw_bad_cast( + "toml::value::as_table(): ", this->type_, *this); + } + return this->table_.value(); + } + // }}} + // ------------------------------------------------------------------------ + // nonconst reference {{{ + + boolean & as_boolean() & + { + if(this->type_ != value_t::boolean) + { + detail::throw_bad_cast( + "toml::value::as_boolean(): ", this->type_, *this); + } + return this->boolean_; + } + integer & as_integer() & + { + if(this->type_ != value_t::integer) + { + detail::throw_bad_cast( + "toml::value::as_integer(): ", this->type_, *this); + } + return this->integer_; + } + floating & as_floating() & + { + if(this->type_ != value_t::floating) + { + detail::throw_bad_cast( + "toml::value::as_floating(): ", this->type_, *this); + } + return this->floating_; + } + string & as_string() & + { + if(this->type_ != value_t::string) + { + detail::throw_bad_cast( + "toml::value::as_string(): ", this->type_, *this); + } + return this->string_; + } + offset_datetime & as_offset_datetime() & + { + if(this->type_ != value_t::offset_datetime) + { + detail::throw_bad_cast( + "toml::value::as_offset_datetime(): ", this->type_, *this); + } + return this->offset_datetime_; + } + local_datetime & as_local_datetime() & + { + if(this->type_ != value_t::local_datetime) + { + detail::throw_bad_cast( + "toml::value::as_local_datetime(): ", this->type_, *this); + } + return this->local_datetime_; + } + local_date & as_local_date() & + { + if(this->type_ != value_t::local_date) + { + detail::throw_bad_cast( + "toml::value::as_local_date(): ", this->type_, *this); + } + return this->local_date_; + } + local_time & as_local_time() & + { + if(this->type_ != value_t::local_time) + { + detail::throw_bad_cast( + "toml::value::as_local_time(): ", this->type_, *this); + } + return this->local_time_; + } + array_type & as_array() & + { + if(this->type_ != value_t::array) + { + detail::throw_bad_cast( + "toml::value::as_array(): ", this->type_, *this); + } + return this->array_.value(); + } + table_type & as_table() & + { + if(this->type_ != value_t::table) + { + detail::throw_bad_cast( + "toml::value::as_table(): ", this->type_, *this); + } + return this->table_.value(); + } + + // }}} + // ------------------------------------------------------------------------ + // rvalue reference {{{ + + boolean && as_boolean() && + { + if(this->type_ != value_t::boolean) + { + detail::throw_bad_cast( + "toml::value::as_boolean(): ", this->type_, *this); + } + return std::move(this->boolean_); + } + integer && as_integer() && + { + if(this->type_ != value_t::integer) + { + detail::throw_bad_cast( + "toml::value::as_integer(): ", this->type_, *this); + } + return std::move(this->integer_); + } + floating && as_floating() && + { + if(this->type_ != value_t::floating) + { + detail::throw_bad_cast( + "toml::value::as_floating(): ", this->type_, *this); + } + return std::move(this->floating_); + } + string && as_string() && + { + if(this->type_ != value_t::string) + { + detail::throw_bad_cast( + "toml::value::as_string(): ", this->type_, *this); + } + return std::move(this->string_); + } + offset_datetime && as_offset_datetime() && + { + if(this->type_ != value_t::offset_datetime) + { + detail::throw_bad_cast( + "toml::value::as_offset_datetime(): ", this->type_, *this); + } + return std::move(this->offset_datetime_); + } + local_datetime && as_local_datetime() && + { + if(this->type_ != value_t::local_datetime) + { + detail::throw_bad_cast( + "toml::value::as_local_datetime(): ", this->type_, *this); + } + return std::move(this->local_datetime_); + } + local_date && as_local_date() && + { + if(this->type_ != value_t::local_date) + { + detail::throw_bad_cast( + "toml::value::as_local_date(): ", this->type_, *this); + } + return std::move(this->local_date_); + } + local_time && as_local_time() && + { + if(this->type_ != value_t::local_time) + { + detail::throw_bad_cast( + "toml::value::as_local_time(): ", this->type_, *this); + } + return std::move(this->local_time_); + } + array_type && as_array() && + { + if(this->type_ != value_t::array) + { + detail::throw_bad_cast( + "toml::value::as_array(): ", this->type_, *this); + } + return std::move(this->array_.value()); + } + table_type && as_table() && + { + if(this->type_ != value_t::table) + { + detail::throw_bad_cast( + "toml::value::as_table(): ", this->type_, *this); + } + return std::move(this->table_.value()); + } + // }}} + + // accessors ============================================================= + // + // may throw type_error or out_of_range + // + value_type& at(const key& k) + { + if(!this->is_table()) + { + detail::throw_bad_cast( + "toml::value::at(key): ", this->type_, *this); + } + if(this->as_table(std::nothrow).count(k) == 0) + { + detail::throw_key_not_found_error(*this, k); + } + return this->as_table(std::nothrow).at(k); + } + value_type const& at(const key& k) const + { + if(!this->is_table()) + { + detail::throw_bad_cast( + "toml::value::at(key): ", this->type_, *this); + } + if(this->as_table(std::nothrow).count(k) == 0) + { + detail::throw_key_not_found_error(*this, k); + } + return this->as_table(std::nothrow).at(k); + } + value_type& operator[](const key& k) + { + if(this->is_uninitialized()) + { + *this = table_type{}; + } + else if(!this->is_table()) // initialized, but not a table + { + detail::throw_bad_cast( + "toml::value::operator[](key): ", this->type_, *this); + } + return this->as_table(std::nothrow)[k]; + } + + value_type& at(const std::size_t idx) + { + if(!this->is_array()) + { + detail::throw_bad_cast( + "toml::value::at(idx): ", this->type_, *this); + } + if(this->as_array(std::nothrow).size() <= idx) + { + throw std::out_of_range(detail::format_underline( + "toml::value::at(idx): no element corresponding to the index", { + {this->location(), concat_to_string("the length is ", + this->as_array(std::nothrow).size(), + ", and the specified index is ", idx)} + })); + } + return this->as_array().at(idx); + } + value_type const& at(const std::size_t idx) const + { + if(!this->is_array()) + { + detail::throw_bad_cast( + "toml::value::at(idx): ", this->type_, *this); + } + if(this->as_array(std::nothrow).size() <= idx) + { + throw std::out_of_range(detail::format_underline( + "toml::value::at(idx): no element corresponding to the index", { + {this->location(), concat_to_string("the length is ", + this->as_array(std::nothrow).size(), + ", and the specified index is ", idx)} + })); + } + return this->as_array(std::nothrow).at(idx); + } + + value_type& operator[](const std::size_t idx) noexcept + { + // no check... + return this->as_array(std::nothrow)[idx]; + } + value_type const& operator[](const std::size_t idx) const noexcept + { + // no check... + return this->as_array(std::nothrow)[idx]; + } + + void push_back(const value_type& x) + { + if(!this->is_array()) + { + detail::throw_bad_cast( + "toml::value::push_back(value): ", this->type_, *this); + } + this->as_array(std::nothrow).push_back(x); + return; + } + void push_back(value_type&& x) + { + if(!this->is_array()) + { + detail::throw_bad_cast( + "toml::value::push_back(value): ", this->type_, *this); + } + this->as_array(std::nothrow).push_back(std::move(x)); + return; + } + + template + value_type& emplace_back(Ts&& ... args) + { + if(!this->is_array()) + { + detail::throw_bad_cast( + "toml::value::emplace_back(...): ", this->type_, *this); + } + this->as_array(std::nothrow).emplace_back(std::forward(args) ...); + return this->as_array(std::nothrow).back(); + } + + std::size_t size() const + { + switch(this->type_) + { + case value_t::array: + { + return this->as_array(std::nothrow).size(); + } + case value_t::table: + { + return this->as_table(std::nothrow).size(); + } + case value_t::string: + { + return this->as_string(std::nothrow).str.size(); + } + default: + { + throw type_error(detail::format_underline( + "toml::value::size(): bad_cast to container types", { + {this->location(), + concat_to_string("the actual type is ", this->type_)} + }), this->location()); + } + } + } + + std::size_t count(const key_type& k) const + { + if(!this->is_table()) + { + detail::throw_bad_cast( + "toml::value::count(key): ", this->type_, *this); + } + return this->as_table(std::nothrow).count(k); + } + + bool contains(const key_type& k) const + { + if(!this->is_table()) + { + detail::throw_bad_cast( + "toml::value::contains(key): ", this->type_, *this); + } + return (this->as_table(std::nothrow).count(k) != 0); + } + + source_location location() const + { + return source_location(this->region_info_.get()); + } + + comment_type const& comments() const noexcept {return this->comments_;} + comment_type& comments() noexcept {return this->comments_;} + + private: + + void cleanup() noexcept + { + switch(this->type_) + { + case value_t::string : {string_.~string(); return;} + case value_t::array : {array_.~array_storage(); return;} + case value_t::table : {table_.~table_storage(); return;} + default : return; + } + } + + // for error messages + template + friend region_base const* detail::get_region(const Value& v); + + template + friend void detail::change_region(Value& v, detail::region reg); + + private: + + using array_storage = detail::storage; + using table_storage = detail::storage; + + value_t type_; + union + { + boolean boolean_; + integer integer_; + floating floating_; + string string_; + offset_datetime offset_datetime_; + local_datetime local_datetime_; + local_date local_date_; + local_time local_time_; + array_storage array_; + table_storage table_; + }; + std::shared_ptr region_info_; + comment_type comments_; +}; + +// default toml::value and default array/table. +// TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp +using value = basic_value; +using array = typename value::array_type; +using table = typename value::table_type; + +template class T, template class A> +inline bool +operator==(const basic_value& lhs, const basic_value& rhs) +{ + if(lhs.type() != rhs.type()) {return false;} + if(lhs.comments() != rhs.comments()) {return false;} + + switch(lhs.type()) + { + case value_t::boolean : + { + return lhs.as_boolean() == rhs.as_boolean(); + } + case value_t::integer : + { + return lhs.as_integer() == rhs.as_integer(); + } + case value_t::floating : + { + return lhs.as_floating() == rhs.as_floating(); + } + case value_t::string : + { + return lhs.as_string() == rhs.as_string(); + } + case value_t::offset_datetime: + { + return lhs.as_offset_datetime() == rhs.as_offset_datetime(); + } + case value_t::local_datetime: + { + return lhs.as_local_datetime() == rhs.as_local_datetime(); + } + case value_t::local_date: + { + return lhs.as_local_date() == rhs.as_local_date(); + } + case value_t::local_time: + { + return lhs.as_local_time() == rhs.as_local_time(); + } + case value_t::array : + { + return lhs.as_array() == rhs.as_array(); + } + case value_t::table : + { + return lhs.as_table() == rhs.as_table(); + } + case value_t::empty : {return true; } + default: {return false;} + } +} + +template class T, template class A> +inline bool operator!=(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs == rhs); +} + +template class T, template class A> +typename std::enable_if::array_type>, + detail::is_comparable::table_type> + >::value, bool>::type +operator<(const basic_value& lhs, const basic_value& rhs) +{ + if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());} + switch(lhs.type()) + { + case value_t::boolean : + { + return lhs.as_boolean() < rhs.as_boolean() || + (lhs.as_boolean() == rhs.as_boolean() && + lhs.comments() < rhs.comments()); + } + case value_t::integer : + { + return lhs.as_integer() < rhs.as_integer() || + (lhs.as_integer() == rhs.as_integer() && + lhs.comments() < rhs.comments()); + } + case value_t::floating : + { + return lhs.as_floating() < rhs.as_floating() || + (lhs.as_floating() == rhs.as_floating() && + lhs.comments() < rhs.comments()); + } + case value_t::string : + { + return lhs.as_string() < rhs.as_string() || + (lhs.as_string() == rhs.as_string() && + lhs.comments() < rhs.comments()); + } + case value_t::offset_datetime: + { + return lhs.as_offset_datetime() < rhs.as_offset_datetime() || + (lhs.as_offset_datetime() == rhs.as_offset_datetime() && + lhs.comments() < rhs.comments()); + } + case value_t::local_datetime: + { + return lhs.as_local_datetime() < rhs.as_local_datetime() || + (lhs.as_local_datetime() == rhs.as_local_datetime() && + lhs.comments() < rhs.comments()); + } + case value_t::local_date: + { + return lhs.as_local_date() < rhs.as_local_date() || + (lhs.as_local_date() == rhs.as_local_date() && + lhs.comments() < rhs.comments()); + } + case value_t::local_time: + { + return lhs.as_local_time() < rhs.as_local_time() || + (lhs.as_local_time() == rhs.as_local_time() && + lhs.comments() < rhs.comments()); + } + case value_t::array : + { + return lhs.as_array() < rhs.as_array() || + (lhs.as_array() == rhs.as_array() && + lhs.comments() < rhs.comments()); + } + case value_t::table : + { + return lhs.as_table() < rhs.as_table() || + (lhs.as_table() == rhs.as_table() && + lhs.comments() < rhs.comments()); + } + case value_t::empty : + { + return lhs.comments() < rhs.comments(); + } + default: + { + return lhs.comments() < rhs.comments(); + } + } +} + +template class T, template class A> +typename std::enable_if::array_type>, + detail::is_comparable::table_type> + >::value, bool>::type +operator<=(const basic_value& lhs, const basic_value& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +template class T, template class A> +typename std::enable_if::array_type>, + detail::is_comparable::table_type> + >::value, bool>::type +operator>(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs <= rhs); +} +template class T, template class A> +typename std::enable_if::array_type>, + detail::is_comparable::table_type> + >::value, bool>::type +operator>=(const basic_value& lhs, const basic_value& rhs) +{ + return !(lhs < rhs); +} + +template class T, template class A> +inline std::string format_error(const std::string& err_msg, + const basic_value& v, const std::string& comment, + std::vector hints = {}, + const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) +{ + return detail::format_underline(err_msg, {{v.location(), comment}}, + std::move(hints), colorize); +} + +template class T, template class A> +inline std::string format_error(const std::string& err_msg, + const toml::basic_value& v1, const std::string& comment1, + const toml::basic_value& v2, const std::string& comment2, + std::vector hints = {}, + const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) +{ + return detail::format_underline(err_msg, { + {v1.location(), comment1}, {v2.location(), comment2} + }, std::move(hints), colorize); +} + +template class T, template class A> +inline std::string format_error(const std::string& err_msg, + const toml::basic_value& v1, const std::string& comment1, + const toml::basic_value& v2, const std::string& comment2, + const toml::basic_value& v3, const std::string& comment3, + std::vector hints = {}, + const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) +{ + return detail::format_underline(err_msg, {{v1.location(), comment1}, + {v2.location(), comment2}, {v3.location(), comment3} + }, std::move(hints), colorize); +} + +template class T, template class A> +detail::return_type_of_t +visit(Visitor&& visitor, const toml::basic_value& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(v.as_boolean ());} + case value_t::integer : {return visitor(v.as_integer ());} + case value_t::floating : {return visitor(v.as_floating ());} + case value_t::string : {return visitor(v.as_string ());} + case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} + case value_t::local_datetime : {return visitor(v.as_local_datetime ());} + case value_t::local_date : {return visitor(v.as_local_date ());} + case value_t::local_time : {return visitor(v.as_local_time ());} + case value_t::array : {return visitor(v.as_array ());} + case value_t::table : {return visitor(v.as_table ());} + case value_t::empty : break; + default: break; + } + throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid basic_value.", v, "here")); +} + +template class T, template class A> +detail::return_type_of_t +visit(Visitor&& visitor, toml::basic_value& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(v.as_boolean ());} + case value_t::integer : {return visitor(v.as_integer ());} + case value_t::floating : {return visitor(v.as_floating ());} + case value_t::string : {return visitor(v.as_string ());} + case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} + case value_t::local_datetime : {return visitor(v.as_local_datetime ());} + case value_t::local_date : {return visitor(v.as_local_date ());} + case value_t::local_time : {return visitor(v.as_local_time ());} + case value_t::array : {return visitor(v.as_array ());} + case value_t::table : {return visitor(v.as_table ());} + case value_t::empty : break; + default: break; + } + throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid basic_value.", v, "here")); +} + +template class T, template class A> +detail::return_type_of_t +visit(Visitor&& visitor, toml::basic_value&& v) +{ + switch(v.type()) + { + case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} + case value_t::integer : {return visitor(std::move(v.as_integer ()));} + case value_t::floating : {return visitor(std::move(v.as_floating ()));} + case value_t::string : {return visitor(std::move(v.as_string ()));} + case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} + case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} + case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} + case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} + case value_t::array : {return visitor(std::move(v.as_array ()));} + case value_t::table : {return visitor(std::move(v.as_table ()));} + case value_t::empty : break; + default: break; + } + throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " + "does not have any valid basic_value.", v, "here")); +} + +}// toml +#endif// TOML11_VALUE diff --git a/src/toml_verify.cpp b/src/toml_verify.cpp new file mode 100644 index 00000000..2a14782d --- /dev/null +++ b/src/toml_verify.cpp @@ -0,0 +1,532 @@ +/* + ISC License + + Copyright (c) 2021, Antonio SJ Musumeci + + 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 +#include +#include +#include + +typedef std::vector 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 + 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::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 + 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_); + } +} diff --git a/src/config_rename_exdev.hpp b/src/toml_verify.hpp similarity index 83% rename from src/config_rename_exdev.hpp rename to src/toml_verify.hpp index 4be91114..a69c1c8e 100644 --- a/src/config_rename_exdev.hpp +++ b/src/toml_verify.hpp @@ -18,12 +18,14 @@ #pragma once -#include "enum.hpp" +#include "toml.hpp" -enum class RenameEXDEVEnum - { - PASSTHROUGH, - REL_SYMLINK, - ABS_SYMLINK - }; -typedef Enum RenameEXDEV; +#include +#include + +namespace toml +{ + void + verify(const toml::value &doc, + std::vector &errs); +}