// ----------------------------------------------------------------------
// File: proc/user/Attr.cc
// 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 .*
************************************************************************/
#include "mgm/proc/ProcInterface.hh"
#include "mgm/XrdMgmOfs.hh"
#include "mgm/Access.hh"
#include "mgm/Macros.hh"
#include "common/Path.hh"
#include "common/LayoutId.hh"
#include "namespace/interface/IView.hh"
#include "namespace/Resolver.hh"
EOSMGMNAMESPACE_BEGIN
//------------------------------------------------------------------------------
//! Make sure the input given by the client makes sense
//!
//! @return true if successful, otherwise false
//------------------------------------------------------------------------------
bool SanitizeXattr(const std::string& key, const std::string& value)
{
if ((key == "sys.forced.blocksize") || (key == "user.forced.blocksize")) {
std::string out_val;
(void)eos::common::SymKey::DeBase64(value, out_val);
return eos::common::LayoutId::IsValidBlocksize(out_val);
}
return true;
}
int
ProcCommand::Attr()
{
XrdOucString spath = pOpaque->Get("mgm.path");
XrdOucString option = pOpaque->Get("mgm.option");
const char* inpath = spath.c_str();
uint64_t identifier = 0;
bool exclusive = false;
ACCESSMODE_R;
NAMESPACEMAP;
PROC_BOUNCE_ILLEGAL_NAMES;
PROC_BOUNCE_NOT_ALLOWED;
WAIT_BOOT;
if ((spath.beginswith("fid:") || spath.beginswith("fxid:"))) {
identifier = Resolver::retrieveFileIdentifier(spath).getUnderlyingUInt64();
spath = "";
GetPathFromFid(spath, identifier, "error: ");
} else if (spath.beginswith("pid:") || spath.beginswith("pxid:") ||
spath.beginswith("cid:") || spath.beginswith("cxid:")) {
if (spath.beginswith("pid:") || spath.beginswith("pxid:")) {
spath.replace('p', 'f', 0, 1);
} else {
spath.replace('c', 'f', 0, 1);
}
identifier = Resolver::retrieveFileIdentifier(spath).getUnderlyingUInt64();
spath = "";
GetPathFromCid(spath, identifier, "error: ");
} else {
spath = eos::common::Path(path).GetPath();
eos::common::StringConversion::UnsealXrdPath(spath);
}
path = spath.c_str();
PROC_TOKEN_SCOPE;
if ((!spath.length()) && (!identifier)) {
// Empty path or invalid numeric identifier
stdErr = "error: please give a valid identifier (|fid:"
"|fxid:|cid:|cxid:)";
retc = EINVAL;
} else if ((!spath.length())) {
// Retrieval of path from numeric identifier failed
retc = errno;
} else if ((mSubCmd != "set") && (mSubCmd != "get") && (mSubCmd != "ls") &&
(mSubCmd != "rm") && (mSubCmd != "fold")) {
// Unrecognized subcommand
stdErr = "error: the subcommand must be one of 'ls', 'get', 'set', 'rm' or 'fold'!";
retc = EINVAL;
} else {
if (((mSubCmd == "set") && ((!pOpaque->Get("mgm.attr.key")) ||
((!pOpaque->Get("mgm.attr.value"))))) ||
((mSubCmd == "get") && ((!pOpaque->Get("mgm.attr.key")))) ||
((mSubCmd == "rm") && ((!pOpaque->Get("mgm.attr.key"))))) {
stdErr = "error: you have to provide 'mgm.attr.key' for set,get,rm and 'mgm.attr.value' for set commands!";
retc = EINVAL;
} else {
retc = 0;
XrdOucString key = pOpaque->Get("mgm.attr.key");
XrdOucString val = pOpaque->Get("mgm.attr.value");
while (val.replace("\"", "")) {
}
if (val.length() && !SanitizeXattr(key.c_str(), val.c_str())) {
stdErr = "error: invalid input";
retc = EINVAL;
return SFS_OK;
}
// Find everything to be modified i.e. directories only
std::map > found;
if (option.find("r") != STR_NPOS) {
if (gOFS->_find(spath.c_str(), *mError, stdErr, *pVid, found, nullptr,
nullptr, true)) {
stdErr += "error: unable to search in path";
retc = errno;
} else {
// Path may be a file, so add it to the list
if (found.empty()) {
(void) found[spath.c_str()].size();
}
}
} else {
// the single dir case
(void) found[spath.c_str()].size();
}
if (option.find("c") != STR_NPOS) {
exclusive = true;
}
if ((mSubCmd == "set") || (mSubCmd == "rm")) {
SET_ACCESSMODE_W;
}
if (!retc) {
// apply to directories starting at the highest level
for (auto foundit = found.begin(); foundit != found.end(); foundit++) {
{
eos::IContainerMD::XAttrMap map;
eos::IContainerMD::XAttrMap linkmap;
if ((mSubCmd == "ls")) {
RECURSIVE_STALL("AttrLs", (*pVid));
if (gOFS->_access(foundit->first.c_str(), R_OK, *mError, *pVid, 0)) {
stdErr += "error: unable to get attributes ";
stdErr += foundit->first.c_str();
retc = errno;
return SFS_OK;
}
XrdOucString partialStdOut = "";
if (gOFS->_attr_ls(foundit->first.c_str(), *mError, *pVid, (const char*) 0, map, true)) {
stdErr += "error: unable to list attributes of ";
stdErr += foundit->first.c_str();
stdErr += "\n";
retc = errno;
} else {
eos::IContainerMD::XAttrMap::const_iterator it;
if (option == "r") {
stdOut += foundit->first.c_str();
stdOut += ":\n";
}
for (it = map.begin(); it != map.end(); ++it) {
partialStdOut += (it->first).c_str();
if (option.find("V")== STR_NPOS) {
if (it->first != "sys.file.buffer") {
partialStdOut += "=";
partialStdOut += "\"";
partialStdOut += (it->second).c_str();
} else {
partialStdOut += "=\"[";
partialStdOut += (std::to_string(it->second.size()).c_str());
partialStdOut += "] bytes";
}
partialStdOut += "\"";
partialStdOut += "\n";
} else {
partialStdOut += "\n";
}
}
stdOut += partialStdOut;
if (option == "r") {
stdOut += "\n";
}
}
}
if (mSubCmd == "set") {
RECURSIVE_STALL("AttrSet", (*pVid));
if (key == "user.acl") {
XrdOucString evalacl;
// If someone wants to set a user.acl and the tag sys.eval.useracl
// is not there, we return an error ...
if ((pVid->uid != 0) && gOFS->_attr_get(foundit->first.c_str(),
*mError, *pVid,
(const char*) 0,
"sys.eval.useracl", evalacl)) {
stdErr += "error: unable to set user.acl - the file/directory does not "
"evaluate user acls (sys.eval.useracl is undefined)!\n";
retc = EINVAL;
return SFS_OK;
}
}
// Check if the origin exists and is a directory
if (key == "sys.attr.link") {
eos::common::RWMutexReadLock ns_rd_lock(gOFS->eosViewRWMutex);
try {
auto cmd = gOFS->eosView->getContainer(val.c_str());
} catch (eos::MDException& e) {
std::ostringstream oss;
oss << "error: " << val.c_str()
<< " must be an existing directory" << std::endl;
stdErr = oss.str().c_str();
retc = EINVAL;
return SFS_OK;
}
}
if (gOFS->_attr_set(foundit->first.c_str(), *mError, *pVid, (const char*) 0,
key.c_str(), val.c_str(), exclusive)) {
stdErr += "error: unable to set attribute in file/directory ";
stdErr += foundit->first.c_str();
if (mError != 0) {
stdErr += ": ";
stdErr += mError->getErrText();
}
stdErr += "\n";
retc = errno;
}
}
if (mSubCmd == "get") {
RECURSIVE_STALL("AttrGet", (*pVid));
if (gOFS->_access(foundit->first.c_str(), R_OK, *mError, *pVid, 0)) {
stdErr += "error: unable to get attributes of ";
stdErr += foundit->first.c_str();
retc = errno;
return SFS_OK;
}
if (gOFS->_attr_get(foundit->first.c_str(), *mError, *pVid, (const char*) 0,
key.c_str(), val)) {
stdErr += "error: unable to get attribute ";
stdErr += key;
stdErr += " in file/directory ";
stdErr += foundit->first.c_str();
stdErr += "\n";
retc = errno;
} else {
if (option.find("V") != STR_NPOS) {
stdOut += val;
} else {
stdOut += key;
stdOut += "=\"";
stdOut += val;
stdOut += "\"\n";
}
}
}
if (mSubCmd == "rm") {
RECURSIVE_STALL("AttrRm", (*pVid));
if (gOFS->_attr_rem(foundit->first.c_str(), *mError, *pVid, (const char*) 0,
key.c_str())) {
stdErr += "error: unable to remove attribute '";
stdErr += key;
stdErr += "' in file/directory ";
stdErr += foundit->first.c_str();
stdErr += "\n";
retc = errno;
} else {
stdOut += "success: removed attribute '";
stdOut += key;
stdOut += "' from file/directory ";
stdOut += foundit->first.c_str();
stdOut += "\n";
}
}
if (mSubCmd == "fold") {
RECURSIVE_STALL("AttrLs", (*pVid));
int retc = gOFS->_attr_ls(foundit->first.c_str(), *mError, *pVid,
(const char*) 0, map, false);
if ((!retc) && map.count("sys.attr.link")) {
retc |= gOFS->_attr_ls(map["sys.attr.link"].c_str(), *mError, *pVid,
(const char*) 0, linkmap, true);
}
if (retc) {
stdErr += "error: unable to list attributes in file/directory ";
stdErr += foundit->first.c_str();
stdErr += "\n";
retc = errno;
} else {
XrdOucString partialStdOut;
eos::IContainerMD::XAttrMap::const_iterator it;
if (option == "r") {
stdOut += foundit->first.c_str();
stdOut += ":\n";
}
for (it = map.begin(); it != map.end(); ++it) {
if (linkmap.count(it->first) &&
linkmap[it->first] == map[it->first]) {
if (gOFS->_attr_rem(foundit->first.c_str(), *mError, *pVid, (const char*) 0,
it->first.c_str())) {
stdErr += "error [ attr fold ] : unable to remove local attribute ";
stdErr += it->first.c_str();
stdErr += "\n";
retc = errno;
} else {
stdOut += "info [ attr fold ] : removing local attribute ";
stdOut += (it->first).c_str();
stdOut += "=";
stdOut += "\"";
stdOut += (it->second).c_str();
stdOut += "\"";
stdOut += "\n";
}
}
}
if (option == "r") {
stdOut += "\n";
}
}
}
}
}
}
}
}
return SFS_OK;
}
EOSMGMNAMESPACE_END