// ----------------------------------------------------------------------
// File: Macros.hh
// Author: Andreas-Joachim Peters - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
//------------------------------------------------------------------------------
//! @file Macros.hh
//!
//! @brief XRootD OFS macros
//!
//! The Macros short-cut most of the MgmOfs... functions to apply redirection
//! or stall settings.
//------------------------------------------------------------------------------
#ifndef __EOSMGM_MACROS__HH__
#define __EOSMGM_MACROS__HH__
#include "mgm/Stat.hh"
#include "mgm/XrdMgmOfs.hh"
USE_EOSMGMNAMESPACE
extern XrdMgmOfs* gOFS; //< global handle to XrdMgmOfs object
//------------------------------------------------------------------------------
// Macro definitions
//------------------------------------------------------------------------------
/// define read access
#define ACCESS_R 0
/// define write access
#define ACCESS_W 1
/// defines operation mode to be read
#define ACCESSMODE_R int __AccessMode__ = 0
/// defines operation mode to be write
#define ACCESSMODE_W int __AccessMode__ = 1
/// defines operation mode to be read on master
#define ACCESSMODE_R_MASTER int __AccessMode__ = 2
/// set's operation mode to be write
#define SET_ACCESSMODE_W __AccessMode__ = 1
/// set's operation mode to be read
#define SET_ACCESSMODE_R_MASTER __AccessMode__ = 2
/// check if we are in read access mode
#define IS_ACCESSMODE_R (__AccessMode__ == 0)
/// check if we are in write access mode
#define IS_ACCESSMODE_W (__AccessMode__ == 1)
/// check if we are in master read access mode
#define IS_ACCESSMODE_R_MASTER (__AccessMode__ == 2)
#define WAIT_BOOT while (true) {if (gOFS->IsNsBooted()) break; \
std::this_thread::sleep_for(std::chrono::seconds(5)); \
}
//------------------------------------------------------------------------------
//! Stall Macro
//------------------------------------------------------------------------------
#define MAYSTALL \
std::unique_ptr tracker_helper; \
if(gOFS != nullptr){ \
tracker_helper = std::make_unique(gOFS->mTracker, vid); \
if (gOFS->IsStall) { \
XrdOucString stallmsg=""; \
int stalltime=0; \
if (gOFS->ShouldStall(__FUNCTION__,__AccessMode__, vid, stalltime, stallmsg)) { \
if (stalltime) { \
return gOFS->Stall(error,stalltime, stallmsg.c_str()); \
} else { \
return gOFS->Emsg("maystall", error, EPERM, stallmsg.c_str(), ""); \
} \
} else { \
if (!tracker_helper->IsOK()) { \
stallmsg="track request, stall the client 5 seconds"; \
stalltime = 5; \
return gOFS->Stall(error,stalltime, stallmsg.c_str()); \
} \
} \
} \
}
#define RECURSIVE_STALL(FUNCTION, VID) { \
if (gOFS->IsStall) { \
XrdOucString stallmsg=""; \
int stalltime=0; \
for (size_t i=0; i<20;++i) { \
if (gOFS->ShouldStall((FUNCTION),__AccessMode__, (VID), stalltime, stallmsg)) { \
std::this_thread::sleep_for(std::chrono::milliseconds(5)); \
} else { \
break; \
} \
} \
} \
}
#define FUNCTIONMAYSTALL(FUNCTION, VID, ERROR) eos::mgm::InFlightRegistration tracker_helper(gOFS->mTracker, (VID) ); \
if (gOFS->IsStall) { \
XrdOucString stallmsg=""; \
int stalltime=0; \
if (gOFS->ShouldStall((FUNCTION),__AccessMode__, (VID), stalltime, stallmsg)) { \
if (stalltime) { \
return gOFS->Stall((ERROR),stalltime, stallmsg.c_str()); \
} else { \
return gOFS->Emsg("maystall", (ERROR), EPERM, stallmsg.c_str(), ""); \
} \
} else { \
if (!tracker_helper.IsOK()) { \
stallmsg="track request, stall the client 5 seconds"; \
stalltime = 5; \
return gOFS->Stall((ERROR),stalltime, stallmsg.c_str()); \
} \
} \
}
//------------------------------------------------------------------------------
//! Redirect Macro
//------------------------------------------------------------------------------
#define MAYREDIRECT { \
if (gOFS != nullptr) { \
if (gOFS->IsRedirect) { \
int port{0}; \
std::string host{""}; \
int stall_timeout{0}; \
bool collapse = false; \
std::string stall_msg{"No master MGM available"}; \
if (gOFS->ShouldRedirect(__FUNCTION__, __AccessMode__, vid, host, port, collapse)) { \
return gOFS->Redirect(error, host.c_str(), port, path, collapse); \
} \
if (gOFS->ShouldRoute(__FUNCTION__, __AccessMode__, vid, path, ininfo, \
host, port, stall_timeout)) { \
if (stall_timeout) { \
return gOFS->Stall(error, stall_timeout, stall_msg.c_str()); \
} else { \
XrdCl::URL url; url.SetParams(ininfo ? ininfo : ""); \
if (gOFS->Tried(url, host, "enoent")) \
return gOFS->Emsg("redirect", error, ENOENT, "no such file or directory", path); \
return gOFS->Redirect(error, host.c_str(), port); \
} \
} \
} \
} \
}
//------------------------------------------------------------------------------
//! ENOENT Redirect Macro
//------------------------------------------------------------------------------
#define MAYREDIRECT_ENOENT { if (gOFS->IsRedirect) { \
int port {0}; \
std::string host {""}; \
if (gOFS->HasRedirect(path, "ENOENT:*", host, port)) { \
XrdCl::URL url; url.SetParams(ininfo?ininfo:""); \
if (gOFS->Tried(url, host, "enoent")) \
return gOFS->Emsg("redirect", error, ENOENT, "no such file or directory", path); \
return gOFS->Redirect(error, host.c_str(), port) ; \
} \
} \
}
//------------------------------------------------------------------------------
//! ENONET Redirect Macro
//------------------------------------------------------------------------------
#define MAYREDIRECT_ENONET { if (gOFS->IsRedirect) { \
int port {0}; \
std::string host {""}; \
if (gOFS->HasRedirect(path,"ENONET:*",host,port)) { \
return gOFS->Redirect(error, host.c_str(), port) ; \
} \
} \
}
//------------------------------------------------------------------------------
//! ENETUNREACH Redirect Macro
//------------------------------------------------------------------------------
#define MAYREDIRECT_ENETUNREACH { if (gOFS->IsRedirect) { \
int port {0}; \
std::string host {""}; \
if (gOFS->HasRedirect(path,"ENETUNREACH:*",host,port)) { \
return gOFS->Redirect(error, host.c_str(), port) ; \
} \
} \
}
//------------------------------------------------------------------------------
//! ENOENT Stall Macro
//------------------------------------------------------------------------------
#define MAYSTALL_ENOENT { if (gOFS->IsStall) { \
XrdOucString stallmsg=""; \
int stalltime; \
if (gOFS->HasStall(path, "ENOENT:*", stalltime, stallmsg)) { \
return gOFS->Stall(error, stalltime, stallmsg.c_str()) ; \
} \
} \
}
//------------------------------------------------------------------------------
//! ENONET Stall Macro
// -----------------------------------------------------------------------------
#define MAYSTALL_ENONET { if (gOFS->IsStall) { \
XrdOucString stallmsg=""; \
int stalltime; \
if (gOFS->HasStall(path,"ENONET:*", stalltime, stallmsg)) { \
return gOFS->Stall(error, stalltime, stallmsg.c_str()) ; \
} \
} \
}
//------------------------------------------------------------------------------
//! ENETUNREACH Stall Macro
//------------------------------------------------------------------------------
#define MAYSTALL_ENETUNREACH { if (gOFS->IsStall) { \
XrdOucString stallmsg=""; \
int stalltime; \
if (gOFS->HasStall(path,"ENETUNREACH:*", stalltime, stallmsg)) { \
return gOFS->Stall(error, stalltime, stallmsg.c_str()) ; \
} \
} \
}
//------------------------------------------------------------------------------
//! Namespace Map MACRO
//! - checks validity of path names
//! - checks for prefixing and rewrites path name
//! - remap's path names according to the configured path map
//------------------------------------------------------------------------------
#define NAMESPACEMAP \
const char* path = inpath; \
XrdOucString store_path=path; \
if(gOFS != nullptr) { \
if (inpath && ininfo && strstr(ininfo, "eos.encodepath")) { \
store_path = eos::common::StringConversion::curl_unescaped(inpath).c_str(); \
} else { \
eos::common::StringConversion::UnsealXrdPath(store_path); \
} \
if (vid.token && vid.token->Valid()) { \
if (!strncmp(path, "/zteos64:", 9)) { \
store_path = vid.token->Path().c_str(); \
} \
} \
if (inpath && (!(ininfo) || (ininfo && (!strstr(ininfo, "eos.prefix"))))) { \
XrdOucString iinpath = store_path; \
gOFS->PathRemap(iinpath.c_str(), store_path); \
} \
size_t __i = 0; \
size_t __n = store_path.length(); \
if (gOFS->UTF8) { \
for (__i = 0; __i < __n; __i++) { \
if (((store_path[__i] != 0xa) && (store_path[__i] != 0xd)) /* CR,LF*/) { \
continue; \
} else { \
break; \
} \
} \
} else { \
for (__i = 0; __i < __n; __i++) { \
if (((store_path[__i] >= 97) && (store_path[__i] <= 122)) || /* a-z */ \
((store_path[__i] >= 64) && (store_path[__i] <= 90)) || /* @,A-Z */ \
((store_path[__i] >= 48) && (store_path[__i] <= 57)) || /* 0-9 */ \
(store_path[__i] == 47) || /* / */ \
(store_path[__i] == 46) || /* . */ \
(store_path[__i] == 32) || /* SPACE */ \
(store_path[__i] == 45) || /* - */ \
(store_path[__i] == 95) || /* _ */ \
(store_path[__i] == 126) || /* ~ */ \
(store_path[__i] == 35) || /* # */ \
(store_path[__i] == 58) || /* : */ \
(store_path[__i] == 43) || /* + */ \
(store_path[__i] == 94) /* ^ */ \
) { \
continue; \
} else { \
break; \
} \
} \
} \
/* root can use all letters */ \
if ((vid.uid != 0) && (__i != (__n))) { \
path = 0; \
} else { \
const char* pf = 0; \
/* check for redirection with prefixes */ \
if (ininfo && (pf = strstr(ininfo, "eos.prefix="))) { \
if (!store_path.beginswith("/proc/")) { \
XrdOucEnv env(pf); \
/* check for redirection with LFN rewrite */ \
store_path.insert(env.Get("eos.prefix"), 0); \
} \
} \
if (ininfo && (pf = strstr(ininfo, "eos.lfn="))) { \
if ((!store_path.beginswith("/proc/"))) { \
XrdOucEnv env(pf); \
store_path = env.Get("eos.lfn"); \
} \
} \
path = store_path.c_str(); \
} \
}
//------------------------------------------------------------------------------
//! Define scope for tokens
//------------------------------------------------------------------------------
#define TOKEN_SCOPE \
vid.scope = path;
#define PROC_TOKEN_SCOPE \
pVid->scope = path;
#define PROC_MVID_TOKEN_SCOPE \
mVid.scope = path;
#define NAMESPACE_NO_TRAILING_SLASH \
if (store_path.endswith("/")) { \
store_path.erase(store_path.length()-1,1); \
path = store_path.c_str(); \
}
#define PROC_MOVE_TOKENSCOPE(a,b) \
mVid.scope = eos::common::Path::Overlap( (a), (b) );
//------------------------------------------------------------------------------
//! Bounce Illegal Name Macro
//------------------------------------------------------------------------------
#define BOUNCE_ILLEGAL_NAMES \
if (!path) { \
eos_err("illegal character in %s", store_path.c_str()); \
return Emsg(epname, error, EILSEQ,"accept path name - illegal characters " \
"- use only A-Z a-z 0-9 / SPACE .-_~#:^", store_path.c_str()); \
}
//------------------------------------------------------------------------------
//! Bounce Illegal Name in proc request Macro
//------------------------------------------------------------------------------
#define PROC_BOUNCE_ILLEGAL_NAMES \
if (!path) { \
eos_err("illegal character in %s", store_path.c_str()); \
retc = EILSEQ; \
stdErr += "error: illegal characters - use only use only A-Z a-z 0-9 SPACE .-_~#:^\n"; \
return SFS_OK; \
}
//------------------------------------------------------------------------------
//! Require System Auth (SSS or localhost) Macro
//------------------------------------------------------------------------------
#define REQUIRE_SSS_OR_LOCAL_AUTH \
if ((vid.prot!="sss") && \
((vid.host != "localhost") && \
(vid.host != "localhost.localdomain")) ){ \
eos_err("system access restricted - unauthorized identity used"); \
gOFS->MgmStats.Add("EAccess", vid.uid, vid.gid, 1); \
return Emsg(epname, error, EACCES,"give access - system access " \
"restricted - unauthorized identity used"); \
}
//------------------------------------------------------------------------------
//! Bounce not-allowed-users Macro - for root, bin, daemon, admin we allow
//! localhost connects or sss authentication always
//------------------------------------------------------------------------------
#define BOUNCE_NOT_ALLOWED \
if (((vid.uid > 3) || \
((vid.prot != "sss") && (vid.host != "localhost") && \
(vid.host != "localhost.localdomain")))) { \
eos::common::RWMutexReadLock lock(Access::gAccessMutex); \
if (Access::gAllowedTokens.size() && vid.token && !Access::gAllowedTokens.count(vid.token->Voucher()) ) { \
eos_err("user access restricted - unauthorized token vid.token->Voucher=\"%s\"" \
"path=\"%s\"", vid.token->Voucher().c_str(), \
inpath); \
gOFS->MgmStats.Add("EAccess", vid.uid, vid.gid, 1); \
return Emsg(epname, error, EACCES,"give access - user access " \
"restricted - unauthorized token used"); \
} \
if (Access::gAllowedUsers.size() || Access::gAllowedGroups.size() || \
Access::gAllowedHosts.size() || Access::gAllowedDomains.size()) { \
if ((!Access::gAllowedGroups.count(vid.gid)) && \
(!Access::gAllowedUsers.count(vid.uid)) && \
(!Access::gAllowedHosts.count(vid.host)) && \
(!Access::gAllowedDomains.count(vid.getUserAtDomain()))) { \
eos_err("user access restricted - unauthorized identity vid.uid=" \
"%d, vid.gid=%d, vid.host=\"%s\", vid.tident=\"%s\" for " \
"path=\"%s\" user@domain=\"%s\"", vid.uid, vid.gid, vid.host.c_str(), \
(vid.tident.c_str() ? vid.tident.c_str() : ""), inpath, \
vid.getUserAtDomain().c_str()); \
gOFS->MgmStats.Add("EAccess", vid.uid, vid.gid, 1); \
return Emsg(epname, error, EACCES,"give access - user access " \
"restricted - unauthorized identity used"); \
} \
} \
if (Access::gAllowedDomains.size() && \
(!Access::gAllowedDomains.count("-")) && \
(!Access::gAllowedDomains.count(vid.domain))) { \
gOFS->MgmStats.Add("EAccess", vid.uid, vid.gid, 1); \
eos_err("domain access restricted - unauthorized identity " \
"vid.domain=\"%s\"for " \
"path=\"%s\"", vid.domain.c_str(), \
inpath); \
return Emsg(epname, error, EACCES,"give access - domain access " \
"restricted - unauthorized identity used"); \
} \
}
//------------------------------------------------------------------------------
//! Bounce not-allowed-users in proc request Macro
//------------------------------------------------------------------------------
#define PROC_BOUNCE_NOT_ALLOWED \
{ /* reduce scope of this mutex */ \
eos::common::RWMutexReadLock lock(Access::gAccessMutex); \
if ((vid.uid > 3) && \
(Access::gAllowedUsers.size() || \
Access::gAllowedGroups.size() || \
Access::gAllowedDomains.size() || \
Access::gAllowedHosts.size()) ) { \
if (Access::gAllowedUsers.size() || Access::gAllowedGroups.size() || \
Access::gAllowedHosts.size()) { \
if ( (!Access::gAllowedGroups.count(vid.gid)) && \
(!Access::gAllowedUsers.count(vid.uid)) && \
(!Access::gAllowedHosts.count(vid.host)) && \
(!Access::gAllowedDomains.count(vid.getUserAtDomain()))) { \
eos_err("user access restricted - unauthorized identity vid.uid=" \
"%d, vid.gid=%d, vid.host=\"%s\", vid.tident=\"%s\" for " \
"path=\"%s\" user@domain=\"%s\"", vid.uid, vid.gid, vid.host.c_str(), \
(vid.tident.c_str() ? vid.tident.c_str() : ""), inpath, \
vid.getUserAtDomain().c_str()); \
retc = EACCES; \
gOFS->MgmStats.Add("EAccess", vid.uid, vid.gid, 1); \
stdErr += "error: user access restricted - unauthorized identity used";\
return SFS_OK; \
} \
} \
if (Access::gAllowedDomains.size() && \
(!Access::gAllowedDomains.count("-")) && \
(!Access::gAllowedDomains.count(vid.domain))) { \
eos_err("domain access restricted - unauthorized identity " \
"vid.domain=\"%s\"for " \
"path=\"%s\"", vid.domain.c_str(), \
inpath); \
retc = EACCES; \
gOFS->MgmStats.Add("EAccess", vid.uid, vid.gid, 1); \
stdErr += "error: domain access restricted - unauthorized identity used";\
return SFS_OK; \
} \
} \
}
EOSMGMNAMESPACE_BEGIN
//------------------------------------------------------------------------------
//! Namespace map functionality i.e check validity of the paths, check for
//! prefix and path rewrite options, remap path's names according to the
//! configured path map.
//!
//! @param path input path, will hold the final re-mapped path
//! @param ininfo optional opaque information
//! @param vid virtual identify of the client
//------------------------------------------------------------------------------
void NamespaceMap(std::string& path, const char* ininfo,
const eos::common::VirtualIdentity& vid);
//------------------------------------------------------------------------------
//! Bounce illegal path names
//!
//! @param path input path
//! @param err_msg error message
//! @param retc errno value
//!
//! @return true if request should be bounced, otherwise false
//------------------------------------------------------------------------------
bool ProcBounceIllegalNames(const std::string& path, std::string& err_check,
int& errno_check);
//------------------------------------------------------------------------------
//! Bounce not-allowed-users in proc requests
//!
//! @param path input path
//! @param vid virtual identity of the client
//! @param err_msg error message
//! @param retc errno value
//!
//! @return true if requests should be bounced, otherwise false
//------------------------------------------------------------------------------
bool ProcBounceNotAllowed(const std::string& path,
const eos::common::VirtualIdentity& vid,
std::string& err_check, int& errno_check);
EOSMGMNAMESPACE_END
#endif