Merge pull request #832 from trapexit/rework-errors

rework some function error handling
This commit is contained in:
trapexit 2020-09-23 00:26:03 -04:00 committed by GitHub
commit 9c352ac555
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 492 additions and 167 deletions

View File

@ -18,7 +18,7 @@
#include "errno.hpp"
#include "fs_lchmod.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "policy_rv.hpp"
#include "ugid.hpp"
#include "fuse.h"
@ -31,57 +31,87 @@
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
chmod_loop_core(const string &basepath_,
const char *fusepath_,
const mode_t mode_,
const int error_)
PolicyRV *prv_)
{
int rv;
string fullpath;
fullpath = fs::path::make(basepath_,fusepath_);
rv = fs::lchmod(fullpath,mode_);
errno = 0;
fs::lchmod(fullpath,mode_);
return error::calc(rv,error_,errno);
prv_->insert(errno,basepath_);
}
static
int
void
chmod_loop(const vector<string> &basepaths_,
const char *fusepath_,
const mode_t mode_)
const mode_t mode_,
PolicyRV *prv_)
{
int error;
error = -1;
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{
error = l::chmod_loop_core(basepaths_[i],fusepath_,mode_,error);
l::chmod_loop_core(basepaths_[i],fusepath_,mode_,prv_);
}
return -error;
}
static
int
chmod(Policy::Func::Action actionFunc_,
Policy::Func::Search searchFunc_,
const Branches &branches_,
const char *fusepath_,
const mode_t mode_)
{
int rv;
PolicyRV prv;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,&basepaths);
if(rv == -1)
return -errno;
return l::chmod_loop(basepaths,fusepath_,mode_);
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]);
}
}
@ -96,6 +126,7 @@ namespace FUSE
const ugid::Set ugid(fc->uid,fc->gid);
return l::chmod(config.func.chmod.policy,
config.func.getattr.policy,
config.branches,
fusepath_,
mode_);

View File

@ -18,7 +18,7 @@
#include "errno.hpp"
#include "fs_lchown.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "policy_rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -29,60 +29,90 @@
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_,
const int error_)
PolicyRV *prv_)
{
int rv;
string fullpath;
fullpath = fs::path::make(basepath_,fusepath_);
rv = fs::lchown(fullpath,uid_,gid_);
errno = 0;
fs::lchown(fullpath,uid_,gid_);
return error::calc(rv,error_,errno);
prv_->insert(errno,basepath_);
}
static
int
void
chown_loop(const vector<string> &basepaths_,
const char *fusepath_,
const uid_t uid_,
const gid_t gid_)
const gid_t gid_,
PolicyRV *prv_)
{
int error;
error = -1;
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{
error = l::chown_loop_core(basepaths_[i],fusepath_,uid_,gid_,error);
l::chown_loop_core(basepaths_[i],fusepath_,uid_,gid_,prv_);
}
return -error;
}
static
int
chown(Policy::Func::Action actionFunc_,
Policy::Func::Search searchFunc_,
const Branches &branches_,
const char *fusepath_,
const uid_t uid_,
const gid_t gid_)
{
int rv;
PolicyRV prv;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,&basepaths);
if(rv == -1)
return -errno;
return l::chown_loop(basepaths,fusepath_,uid_,gid_);
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]);
}
}
@ -98,6 +128,7 @@ namespace FUSE
const ugid::Set ugid(fc->uid,fc->gid);
return l::chown(config.func.chown.policy,
config.func.getattr.policy,
config.branches,
fusepath_,
uid_,

View File

@ -19,7 +19,6 @@
#include "fs_clonepath.hpp"
#include "fs_link.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -30,6 +29,26 @@
using std::string;
using std::vector;
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

View File

@ -20,7 +20,6 @@
#include "fs_clonepath.hpp"
#include "fs_mkdir.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -31,6 +30,26 @@
using std::string;
using std::vector;
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

View File

@ -20,7 +20,6 @@
#include "fs_mknod.hpp"
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -31,6 +30,26 @@
using std::string;
using std::vector;
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

View File

@ -18,7 +18,7 @@
#include "errno.hpp"
#include "fs_lremovexattr.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "policy_rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -29,58 +29,87 @@
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
removexattr_loop_core(const string &basepath_,
const char *fusepath_,
const char *attrname_,
const int error_)
PolicyRV *prv_)
{
int rv;
string fullpath;
fullpath = fs::path::make(basepath_,fusepath_);
rv = fs::lremovexattr(fullpath,attrname_);
errno = 0;
fs::lremovexattr(fullpath,attrname_);
return error::calc(rv,error_,errno);
prv_->insert(errno,basepath_);
}
static
int
void
removexattr_loop(const vector<string> &basepaths_,
const char *fusepath_,
const char *attrname_)
const char *attrname_,
PolicyRV *prv_)
{
int error;
error = -1;
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{
error = l::removexattr_loop_core(basepaths_[i],fusepath_,attrname_,error);
l::removexattr_loop_core(basepaths_[i],fusepath_,attrname_,prv_);
}
return -error;
}
static
int
removexattr(Policy::Func::Action actionFunc_,
Policy::Func::Search searchFunc_,
const Branches &branches_,
const char *fusepath_,
const char *attrname_)
{
int rv;
PolicyRV prv;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,&basepaths);
if(rv == -1)
return -errno;
return l::removexattr_loop(basepaths,fusepath_,attrname_);
l::removexattr_loop(basepaths,fusepath_,attrname_,&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]);
}
}
@ -101,6 +130,7 @@ namespace FUSE
const ugid::Set ugid(fc->uid,fc->gid);
return l::removexattr(config.func.removexattr.policy,
config.func.getxattr.policy,
config.branches,
fusepath_,
attrname_);

View File

@ -20,17 +20,35 @@
#include "fs_path.hpp"
#include "fs_remove.hpp"
#include "fs_rename.hpp"
#include "rv.hpp"
#include "ugid.hpp"
#include <algorithm>
#include <set>
#include <string>
#include <vector>
using std::string;
using std::vector;
using std::set;
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;
}
}
static
bool

View File

@ -18,7 +18,6 @@
#include "errno.hpp"
#include "fs_rmdir.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -30,6 +29,23 @@
using std::string;
using std::vector;
namespace error
{
static
int
calc(const int rv_,
const int prev_,
const int cur_)
{
if(prev_ != 0)
return prev_;
if(rv_ == -1)
return cur_;
return 0;
}
}
namespace l
{
static
@ -48,7 +64,6 @@ namespace l
return error::calc(rv,error_,errno);
}
static
int
rmdir_loop(const vector<string> &basepaths_,
@ -56,7 +71,7 @@ namespace l
{
int error;
error = -1;
error = 0;
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{
error = l::rmdir_loop_core(basepaths_[i],fusepath_,error);

View File

@ -21,13 +21,12 @@
#include "fs_path.hpp"
#include "fs_statvfs_cache.hpp"
#include "num.hpp"
#include "rv.hpp"
#include "policy_rv.hpp"
#include "str.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <sstream>
#include <string>
#include <vector>
@ -38,8 +37,29 @@ static const char SECURITY_CAPABILITY[] = "security.capability";
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
bool
is_attrname_security_capability(const char *attrname_)
@ -49,117 +69,126 @@ namespace l
static
int
setxattr_controlfile(Config &config,
const string &attrname,
const string &attrval,
const int flags)
setxattr_controlfile(Config &config_,
const string &attrname_,
const string &attrval_,
const int flags_)
{
int rv;
string key;
if(!str::startswith(attrname,"user.mergerfs."))
if(!str::startswith(attrname_,"user.mergerfs."))
return -ENOATTR;
key = &attrname[14];
key = &attrname_[14];
if(config.has_key(key) == false)
if(config_.has_key(key) == false)
return -ENOATTR;
if((flags & XATTR_CREATE) == XATTR_CREATE)
if((flags_ & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
rv = config.set(key,attrval);
rv = config_.set(key,attrval_);
if(rv < 0)
return rv;
config.open_cache.clear();
fs::statvfs_cache_timeout(config.cache_statfs);
config_.open_cache.clear();
fs::statvfs_cache_timeout(config_.cache_statfs);
return rv;
}
static
int
setxattr_loop_core(const string &basepath,
const char *fusepath,
const char *attrname,
const char *attrval,
const size_t attrvalsize,
const int flags,
const int error)
void
setxattr_loop_core(const string &basepath_,
const char *fusepath_,
const char *attrname_,
const char *attrval_,
const size_t attrvalsize_,
const int flags_,
PolicyRV *prv_)
{
int rv;
string fullpath;
fullpath = fs::path::make(basepath,fusepath);
fullpath = fs::path::make(basepath_,fusepath_);
rv = fs::lsetxattr(fullpath,attrname,attrval,attrvalsize,flags);
errno = 0;
fs::lsetxattr(fullpath,attrname_,attrval_,attrvalsize_,flags_);
return error::calc(rv,error,errno);
prv_->insert(errno,basepath_);
}
static
int
setxattr_loop(const vector<string> &basepaths,
const char *fusepath,
const char *attrname,
const char *attrval,
const size_t attrvalsize,
const int flags)
void
setxattr_loop(const vector<string> &basepaths_,
const char *fusepath_,
const char *attrname_,
const char *attrval_,
const size_t attrvalsize_,
const int flags_,
PolicyRV *prv_)
{
int error;
error = -1;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{
error = l::setxattr_loop_core(basepaths[i],fusepath,
attrname,attrval,attrvalsize,flags,
error);
l::setxattr_loop_core(basepaths_[i],fusepath_,
attrname_,attrval_,attrvalsize_,
flags_,prv_);
}
return -error;
}
static
int
setxattr(Policy::Func::Action actionFunc,
setxattr(Policy::Func::Action actionFunc_,
Policy::Func::Search searchFunc_,
const Branches &branches_,
const char *fusepath,
const char *attrname,
const char *attrval,
const size_t attrvalsize,
const int flags)
const char *fusepath_,
const char *attrname_,
const char *attrval_,
const size_t attrvalsize_,
const int flags_)
{
int rv;
PolicyRV prv;
vector<string> basepaths;
rv = actionFunc(branches_,fusepath,&basepaths);
rv = actionFunc_(branches_,fusepath_,&basepaths);
if(rv == -1)
return -errno;
return l::setxattr_loop(basepaths,fusepath,attrname,attrval,attrvalsize,flags);
l::setxattr_loop(basepaths,fusepath_,attrname_,attrval_,attrvalsize_,flags_,&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
{
int
setxattr(const char *fusepath,
const char *attrname,
const char *attrval,
size_t attrvalsize,
int flags)
setxattr(const char *fusepath_,
const char *attrname_,
const char *attrval_,
size_t attrvalsize_,
int flags_)
{
Config &config = Config::rw();
if(fusepath == config.controlfile)
if(fusepath_ == config.controlfile)
return l::setxattr_controlfile(config,
attrname,
string(attrval,attrvalsize),
flags);
attrname_,
string(attrval_,attrvalsize_),
flags_);
if((config.security_capability == false) &&
l::is_attrname_security_capability(attrname))
l::is_attrname_security_capability(attrname_))
return -ENOATTR;
if(config.xattr.to_int())
@ -169,11 +198,12 @@ namespace FUSE
const ugid::Set ugid(fc->uid,fc->gid);
return l::setxattr(config.func.setxattr.policy,
config.func.getxattr.policy,
config.branches,
fusepath,
attrname,
attrval,
attrvalsize,
flags);
fusepath_,
attrname_,
attrval_,
attrvalsize_,
flags_);
}
}

View File

@ -19,7 +19,6 @@
#include "fs_symlink.hpp"
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -32,6 +31,27 @@
using std::string;
using std::vector;
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

View File

@ -16,9 +16,9 @@
#include "config.hpp"
#include "errno.hpp"
#include "fs_truncate.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "fs_truncate.hpp"
#include "policy_rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -32,57 +32,87 @@
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
truncate_loop_core(const string &basepath_,
const char *fusepath_,
const off_t size_,
const int error_)
PolicyRV *prv_)
{
int rv;
string fullpath;
fullpath = fs::path::make(basepath_,fusepath_);
rv = fs::truncate(fullpath,size_);
errno = 0;
fs::truncate(fullpath,size_);
return error::calc(rv,error_,errno);
prv_->insert(errno,basepath_);
}
static
int
void
truncate_loop(const vector<string> &basepaths_,
const char *fusepath_,
const off_t size_)
const off_t size_,
PolicyRV *prv_)
{
int error;
error = -1;
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{
error = l::truncate_loop_core(basepaths_[i],fusepath_,size_,error);
l::truncate_loop_core(basepaths_[i],fusepath_,size_,prv_);
}
return -error;
}
static
int
truncate(Policy::Func::Action actionFunc_,
Policy::Func::Search searchFunc_,
const Branches &branches_,
const char *fusepath_,
const off_t size_)
{
int rv;
PolicyRV prv;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,&basepaths);
if(rv == -1)
return -errno;
return l::truncate_loop(basepaths,fusepath_,size_);
l::truncate_loop(basepaths,fusepath_,size_,&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]);
}
}
@ -97,6 +127,7 @@ namespace FUSE
const ugid::Set ugid(fc->uid,fc->gid);
return l::truncate(config.func.truncate.policy,
config.func.getattr.policy,
config.branches,
fusepath_,
size_);

View File

@ -16,9 +16,8 @@
#include "config.hpp"
#include "errno.hpp"
#include "fs_unlink.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "fs_unlink.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -31,6 +30,24 @@
using std::string;
using std::vector;
namespace error
{
static
inline
int
calc(const int rv_,
const int prev_,
const int cur_)
{
if(prev_ != 0)
return prev_;
if(rv_ == -1)
return cur_;
return 0;
}
}
namespace l
{
static
@ -56,7 +73,7 @@ namespace l
{
int error;
error = -1;
error = 0;
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{
error = l::unlink_loop_core(basepaths_[i],fusepath_,error);

View File

@ -18,7 +18,7 @@
#include "errno.hpp"
#include "fs_lutimens.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "policy_rv.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -31,57 +31,87 @@
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
utimens_loop_core(const string &basepath_,
const char *fusepath_,
const timespec ts_[2],
const int error_)
PolicyRV *prv_)
{
int rv;
string fullpath;
fullpath = fs::path::make(basepath_,fusepath_);
rv = fs::lutimens(fullpath,ts_);
errno = 0;
fs::lutimens(fullpath,ts_);
return error::calc(rv,error_,errno);
prv_->insert(errno,basepath_);
}
static
int
void
utimens_loop(const vector<string> &basepaths_,
const char *fusepath_,
const timespec ts_[2])
const timespec ts_[2],
PolicyRV *prv_)
{
int error;
error = -1;
for(size_t i = 0, ei = basepaths_.size(); i != ei; i++)
{
error = l::utimens_loop_core(basepaths_[i],fusepath_,ts_,error);
l::utimens_loop_core(basepaths_[i],fusepath_,ts_,prv_);
}
return -error;
}
static
int
utimens(Policy::Func::Action actionFunc_,
Policy::Func::Search searchFunc_,
const Branches &branches_,
const char *fusepath_,
const timespec ts_[2])
{
int rv;
PolicyRV prv;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,&basepaths);
if(rv == -1)
return -errno;
return l::utimens_loop(basepaths,fusepath_,ts_);
l::utimens_loop(basepaths,fusepath_,ts_,&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]);
}
}
@ -96,6 +126,7 @@ namespace FUSE
const ugid::Set ugid(fc->uid,fc->gid);
return l::utimens(config.func.utimens.policy,
config.func.getattr.policy,
config.branches,
fusepath_,
ts_);

View File

@ -1,5 +1,7 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@ -16,22 +18,34 @@
#pragma once
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_;
}
#include <string>
#include <vector>
return 0;
struct PolicyRV
{
struct RV
{
RV(const int rv_,
const std::string &basepath_)
: rv(rv_),
basepath(basepath_)
{
}
int rv;
std::string basepath;
};
std::vector<RV> success;
std::vector<RV> error;
void
insert(const int err_,
const std::string &basepath_)
{
if(err_ == 0)
success.push_back(RV(err_,basepath_));
else
error.push_back(RV(-err_,basepath_));
}
}
};