Commit f7d522c3 authored by Steven Murray's avatar Steven Murray
Browse files

The MockNameServer now stores for each entry in the archive namespace

the uid and gid of the CTA user that owns the file.
parent 15a9b63d
......@@ -7,7 +7,7 @@ set (COMMON_LIB_SRC_FILES
exception/Backtrace.cpp
exception/Errnum.cpp
exception/Exception.cpp
exception/strerror_r_wrapper.cpp
strerror_r_wrapper.cpp
threading/ChildProcess.cpp
threading/Mutex.cpp
threading/Threading.cpp)
......
......@@ -19,7 +19,6 @@
#include "common/exception/Exception.hpp"
#include "common/SmartFd.hpp"
#include <errno.h>
#include <unistd.h>
//-----------------------------------------------------------------------------
......
......@@ -17,9 +17,14 @@
*/
#include "common/exception/Exception.hpp"
#include "common/strerror_r_wrapper.hpp"
#include "common/Utils.hpp"
#include <attr/xattr.h>
#include <memory>
#include <sstream>
#include <stdlib.h>
#include <sys/types.h>
#include <uuid/uuid.h>
using cta::exception::Exception;
......@@ -221,3 +226,142 @@ bool cta::Utils::endsWith(const std::string &str, const char c) {
return c == str.at(str.length() - 1);
}
}
//------------------------------------------------------------------------------
// setXattr
//------------------------------------------------------------------------------
void cta::Utils::setXattr(const std::string &path, const std::string &name,
const std::string &value) {
if(setxattr(path.c_str(), name.c_str(), value.c_str(), value.length(), 0)) {
const int savedErrno = errno;
std::ostringstream msg;
msg << "Call to setxattr() failed: path=" << path << " name=" << name <<
" value=" << value << ": " << Utils::errnoToString(savedErrno);
throw exception::Exception(msg.str());
}
}
//------------------------------------------------------------------------------
// getXattr
//------------------------------------------------------------------------------
std::string cta::Utils::getXattr(const std::string &path,
const std::string &name) {
const auto sizeOfValue = getxattr(path.c_str(), name.c_str(), NULL, 0);
if(0 > sizeOfValue) {
const int savedErrno = errno;
std::stringstream msg;
msg << "Call to getxattr() failed: path=" << path << " name=" << name <<
": " << Utils::errnoToString(savedErrno);
throw exception::Exception(msg.str());
}
if(0 == sizeOfValue) {
return "";
}
std::unique_ptr<char[]> value(new char[sizeOfValue]);
if(0 > getxattr(path.c_str(), name.c_str(), (void *)value.get(),
sizeOfValue)) {
const int savedErrno = errno;
std::stringstream msg;
msg << "Call to getxattr() failed: path=" << path << " name=" << name <<
": " << Utils::errnoToString(savedErrno);
throw exception::Exception(msg.str());
}
return value.get();
}
//------------------------------------------------------------------------------
// errnoToString
//------------------------------------------------------------------------------
std::string cta::Utils::errnoToString(const int errnoValue) throw() {
char buf[100];
if(!strerror_r_wrapper(errnoValue, buf, sizeof(buf))) {
return buf;
} else {
const int errnoSetByStrerror_r_wrapper = errno;
std::ostringstream oss;
switch(errnoSetByStrerror_r_wrapper) {
case EINVAL:
oss << "Failed to convert errnoValue to string: Invalid errnoValue"
": errnoValue=" << errnoValue;
break;
case ERANGE:
oss << "Failed to convert errnoValue to string"
": Destination buffer for error string is too small"
": errnoValue=" << errnoValue;
break;
default:
oss << "Failed to convert errnoValue to string"
": strerror_r_wrapper failed in an unknown way"
": errnoValue=" << errnoValue;
break;
}
return oss.str();
}
}
//------------------------------------------------------------------------------
// toUint16
//------------------------------------------------------------------------------
uint16_t cta::Utils::toUint16(const std::string &str) {
if(str.empty()) {
std::ostringstream msg;
msg << "Failed to convert empty string to uint16_t: An empty string is not"
" a valid unsigned integer";
throw exception::Exception(msg.str());
}
errno = 0;
const long int value = strtol(str.c_str(), (char **) NULL, 10);
const int savedErrno = errno;
if(savedErrno) {
std::ostringstream msg;
msg << "Failed to convert \'" << str << "' to uint16_t: " <<
errnoToString(savedErrno);
throw exception::Exception(msg.str());
}
if(0 > value) {
std::ostringstream msg;
msg << "Failed to convert \'" << str << "' to uint16_t: Negative number";
throw exception::Exception(msg.str());
}
if(65535 < value) {
std::ostringstream msg;
msg << "Failed to convert \'" << str << "' to uint16_t: Number too big";
throw exception::Exception(msg.str());
}
return value;
}
//------------------------------------------------------------------------------
// isValidUInt
//------------------------------------------------------------------------------
bool cta::Utils::isValidUInt(const std::string &str)
throw() {
// An empty string is not a valid unsigned integer
if(str.empty()) {
return false;
}
// For each character in the string
for(std::string::const_iterator itor = str.begin(); itor != str.end();
itor++) {
// If the current character is not a valid numerical digit
if(*itor < '0' || *itor > '9') {
return false;
}
}
return true;
}
......@@ -19,6 +19,7 @@
#pragma once
#include <list>
#include <sstream>
#include <string>
#include <vector>
......@@ -111,6 +112,63 @@ public:
*/
static bool endsWith(const std::string &str, const char c);
/**
* Returns the string reprsentation of the specified value.
*
* @param value The value whose string representation is to be returned.
* @return The string reprsentation of the specified value.
*/
template <typename T> static std::string toString(const T& value) {
std::ostringstream oss;
oss << value;
return oss.str();
}
/**
* C++ wrapper around the setxtarr() function.
*
* @param path The path to file to which the extended attribute belongs.
* @param name The name of the extended attribute.
* @param value The value of the extended attribute.
*/
static void setXattr(const std::string &path, const std::string &name,
const std::string &value);
/**
* C++ wrapper around the getxattr() function.
*
* @param path The path to file to which the extended attribute belongs.
* @param name The name of the extended attribute.
* @return The value of the extended attribute.
*/
static std::string getXattr(const std::string &path, const std::string &name);
/**
* Determines the string representation of the specified error number.
*
* Please note this method is thread safe.
*
* @param errnoValue The errno value.
* @return The string representation.
*/
static std::string errnoToString(const int errnoValue) throw();
/**
* Converts the specified string to an unsigned integer.
*
* @param str The string.
* @return The unisgned integer.
*/
static uint16_t toUint16(const std::string &str);
/**
* Checks if the specified string is a valid unsigned integer.
*
* @param str The string to be checked.
* @returns true if the string is a valid unsigned integer, else false.
*/
static bool isValidUInt(const std::string &str) throw();
}; // class Utils
} // namespace cta
......@@ -259,4 +259,80 @@ TEST_F(cta_UtilsTest, endsWith_slash_just_a_slash) {
ASSERT_TRUE(Utils::endsWith(str, '/'));
}
TEST_F(cta_UtilsTest, errnoToString_EACCESS) {
using namespace cta;
const std::string str = Utils::errnoToString(EACCES);
ASSERT_EQ(std::string("Permission denied"), str);
}
TEST_F(cta_UtilsTest, toUint16_uint16_t) {
using namespace cta;
uint16_t i = 0;
ASSERT_NO_THROW(i = Utils::toUint16("12345"));
ASSERT_EQ((uint16_t)12345, i);
}
TEST_F(cta_UtilsTest, toUint16_zero) {
using namespace cta;
uint16_t i = 0;
ASSERT_NO_THROW(i = Utils::toUint16("0"));
ASSERT_EQ((uint16_t)0, i);
}
TEST_F(cta_UtilsTest, toUint16_65535) {
using namespace cta;
uint16_t i = 0;
ASSERT_NO_THROW(i = Utils::toUint16("65535"));
ASSERT_EQ((uint16_t)65535, i);
}
TEST_F(cta_UtilsTest, toUint16_empty_string) {
using namespace cta;
ASSERT_THROW(Utils::toUint16(""), std::exception);
}
TEST_F(cta_UtilsTest, toUint16_negative) {
using namespace cta;
ASSERT_THROW(Utils::toUint16("-12345"), std::exception);
}
TEST_F(cta_UtilsTest, toUint16_too_big) {
using namespace cta;
ASSERT_THROW(Utils::toUint16("65536"), std::exception);
}
TEST_F(cta_UtilsTest, isValidUInt_unsigned_int) {
using namespace cta;
ASSERT_TRUE(Utils::isValidUInt("12345"));
}
TEST_F(cta_UtilsTest, isValidUInt_empty_string) {
using namespace cta;
ASSERT_FALSE(Utils::isValidUInt(""));
}
TEST_F(cta_UtilsTest, isValidUInt_signed_int) {
using namespace cta;
ASSERT_FALSE(Utils::isValidUInt("-12345"));
}
TEST_F(cta_UtilsTest, isValidUInt_not_a_number) {
using namespace cta;
ASSERT_FALSE(Utils::isValidUInt("one"));
}
} // namespace unitTests
......@@ -5,5 +5,4 @@ include_directories(${PROJECT_SOURCE_DIR})
add_library(CTAException
Backtrace.cpp
Errnum.cpp
Exception.cpp
strerror_r_wrapper.cpp)
Exception.cpp)
......@@ -41,7 +41,7 @@
*****************************************************************************/
#include "common/exception/Errnum.hpp"
#include "common/exception/strerror_r_wrapper.h"
#include "common/strerror_r_wrapper.hpp"
#include <string.h>
#include <errno.h>
......
......@@ -15,31 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#include "strerror_r_wrapper.h"
#include "common/strerror_r_wrapper.hpp"
/*
* Undefine _GNU_SOURCE and define _XOPEN_SOURCE as being 600 so that the
......@@ -53,16 +29,8 @@
/*******************************************************************************
* strerror_r_wrapper
******************************************************************************/
extern "C" int strerror_r_wrapper(int errnum, char *buf, size_t buflen) {
int strerror_r_wrapper(int errnum, char *buf, size_t buflen) {
/* This function should be compiled using a C++ compiler and not a C compiler.
*
* C++ compilers are better at spotting whether the GNU version or the
* XSI complicant version of sterror_() is being used. This is because the
* difference between the two versions is their return types. The GNU
* version returns a 'char *' whereas the XSI compliant version returns an
* 'int'. A C compiler may allow the strerror_r() function to return a
* 'char *' and have that 'char *' assigned to an 'int'. A C++ compiler
* usually gives an error if this is tried.
*/
return strerror_r(errnum, buf, buflen);
}
/******************************************************************************
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
* 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.
*
* Copyright (C) 2003 CERN
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stddef.h>
/* The following EXTERN_C marco has been intentionally copied from */
/* h/osdep.h instead of just including h/osdep.h. The reason for this is */
/* this header file must include the minimum number of header files because */
/* the implementation file common/strerror_r_wrapper.cpp will undefine */
/* _GNU_SOURCE and define _XOPEN_SOURCE as being 600. */
/* */
/* Macros for externalization (UNIX) (J.-D.Durand) */
#if defined(__cplusplus)
#define EXTERN_C extern "C"
#else
#define EXTERN_C extern
#endif
/**
* This function wraps the XSI compliant version of strerror_r() and therefore
* writes the string representation of the specified error number to the
......@@ -45,14 +27,6 @@
*
* This function should be compiled using a C++ compiler and not a C compiler.
*
* C++ compilers are better at spotting whether the GNU version or the
* XSI complicant version of sterror_() is being used. This is because the
* difference between the two versions is their return types. The GNU
* version returns a 'char *' whereas the XSI compliant version returns an
* 'int'. A C compiler may allow the strerror_r() function to return a
* 'char *' and have that 'char *' assigned to an 'int'. A C++ compiler
* usually gives an error if this is tried.
*
* @param errnum The error number.
* @param buf The buffer.
* @param buflen The length of the buffer.
......@@ -60,5 +34,4 @@
* to either EINVAL to indicate the error number is invalid, or to ERANGE to
* indicate the supplied error buffer is not large enough.
*/
EXTERN_C int strerror_r_wrapper(int errnum, char *buf, size_t buflen);
int strerror_r_wrapper(int errnum, char *buf, size_t buflen);
......@@ -33,7 +33,6 @@
#include "common/SmartFd.hpp"
#include "common/Utils.hpp"
#include "nameserver/MockNameServer.hpp"
using cta::exception::Exception;
//------------------------------------------------------------------------------
// assertFsDirExists
......@@ -42,17 +41,17 @@ void cta::MockNameServer::assertFsDirExists(const std::string &path) const {
struct stat stat_result;
if(::stat(path.c_str(), &stat_result)) {
char buf[256];
std::ostringstream message;
message << "assertFsDirExists() - " << path << " stat error. Reason: "
<< strerror_r(errno, buf, sizeof(buf));
throw(Exception(message.str()));
const int savedErrno = errno;
std::ostringstream msg;
msg << __FUNCTION__ << " - " << path << " stat error. Reason: " <<
Utils::errnoToString(savedErrno);
throw(exception::Exception(msg.str()));
}
if(!S_ISDIR(stat_result.st_mode)) {
std::ostringstream message;
message << "assertFsDirExists() - " << path << " is not a directory";
throw(Exception(message.str()));
std::ostringstream msg;
msg << "assertFsDirExists() - " << path << " is not a directory";
throw(exception::Exception(msg.str()));
}
}
......@@ -64,9 +63,9 @@ void cta::MockNameServer::assertFsPathDoesNotExist(const std::string &path)
struct stat stat_result;
if(::stat(path.c_str(), &stat_result) == 0) {
std::ostringstream message;
message << "assertFsPathExist() - " << path << " exists.";
throw(Exception(message.str()));
std::ostringstream msg;
msg << "assertFsPathExist() - " << path << " exists.";
throw(exception::Exception(msg.str()));
}
}
......@@ -119,10 +118,10 @@ void cta::MockNameServer::assertStorageClassIsNotInUse(
const std::string &path) const {
if(getDirStorageClass(requester, path) == storageClass) {
std::ostringstream message;
message << "assertStorageClassIsNotInUse() - " << path << " has the " <<
std::ostringstream msg;
msg << "assertStorageClassIsNotInUse() - " << path << " has the " <<
storageClass << " storage class.";
throw(Exception(message.str()));
throw(exception::Exception(msg.str()));
}
const std::string fsPath = m_fsDir + path;
......@@ -130,11 +129,11 @@ void cta::MockNameServer::assertStorageClassIsNotInUse(
struct dirent *entry;
DIR *const dp = opendir(fsPath.c_str());
if (dp == NULL) {
char buf[256];
std::ostringstream message;
message << "assertStorageClassIsNotInUse() - opendir " << fsPath <<
" error. Reason: \n" << strerror_r(errno, buf, sizeof(buf));
throw(Exception(message.str()));
const int savedErrno = errno;
std::ostringstream msg;
msg << __FUNCTION__ << " - opendir " << fsPath << " error."
" Reason: \n" << Utils::errnoToString(savedErrno);
throw(exception::Exception(msg.str()));
}
const bool pathEndsWithASlash = path.at(path.length()-1) == '/';
......@@ -161,13 +160,20 @@ void cta::MockNameServer::assertStorageClassIsNotInUse(
cta::MockNameServer::MockNameServer() {
char path[100];
strncpy(path, "/tmp/CTATmpFsXXXXXX", 100);
cta::exception::Errnum::throwOnNull(
exception::Errnum::throwOnNull(
mkdtemp(path),
"MockNameServer() - Failed to create temporary directory");
m_fsDir = path;
cta::exception::Errnum::throwOnNonZero(
exception::Errnum::throwOnNonZero(
setxattr(m_fsDir.c_str(), "user.CTAStorageClass", "", 0, 0),
std::string("MockNameServer() - Failed to setxattr error for ")+m_fsDir);
std::string(__FUNCTION__) + " - Failed to setxattr user.CTAStorageClass on "
+ m_fsDir);
exception::Errnum::throwOnNonZero(
setxattr(m_fsDir.c_str(), "user.uid", "", 0, 0),
std::string(__FUNCTION__) + " - Failed to setxattr user.uid on " + m_fsDir);
exception::Errnum::throwOnNonZero(
setxattr(m_fsDir.c_str(), "user.gid", "", 0, 0),
std::string(__FUNCTION__) + " - Failed to setxattr user.gid on " + m_fsDir);
}
//------------------------------------------------------------------------------
......@@ -188,10 +194,11 @@ void cta::MockNameServer::setDirStorageClass(const SecurityIdentity &requester,
assertFsDirExists(fsPath);
if(setxattr(fsPath.c_str(), "user.CTAStorageClass", storageClassName.c_str(), storageClassName.length(), 0)) {
char buf[256];
std::ostringstream message;
message << "setDirStorageClass() - " << fsPath << " setxattr error. Reason: " << strerror_r(errno, buf, sizeof(buf));
throw(Exception(message.str()));
const int savedErrno = errno;
std::ostringstream msg;
msg << __FUNCTION__ << " - " << fsPath << " setxattr error. Reason: " <<
Utils::errnoToString(savedErrno);
throw(exception::Exception(msg.str()));
}
}
......@@ -206,10 +213,11 @@ void cta::MockNameServer::clearDirStorageClass(
assertFsDirExists(fsPath);
if(setxattr(fsPath.c_str(), "user.CTAStorageClass", "", 0, 0)) {
char buf[256];
std::ostringstream message;
message << "clearDirStorageClass() - " << fsPath << " setxattr error. Reason: " << strerror_r(errno, buf, sizeof(buf));
throw(Exception(message.str()));
const int savedErrno = errno;
std::ostringstream msg;
msg << __FUNCTION__ << " - " << fsPath << " setxattr error. Reason: " <<
Utils::errnoToString(savedErrno);
throw(exception::Exception(msg.str()));
}
}
......@@ -227,9 +235,9 @@ std::string cta::MockNameServer::getDirStorageClass(
bzero(value, sizeof(value));
if(0 > getxattr(fsPath.c_str(), "user.CTAStorageClass", value, sizeof(value))) {
char buf[256];
std::ostringstream message;
message << "getDirStorageClass() - " << fsPath << " getxattr error. Reason: " << strerror_r(errno, buf, sizeof(buf));
throw(Exception(message.str()));
std::ostringstream msg;
msg << "getDirStorageClass() - " << fsPath << " getxattr error. Reason: " << strerror_r(errno, buf, sizeof(buf));
throw(exception::Exception(msg.str()));
}