Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:31:46

0001 #include "Utilities/DavixAdaptor/interface/DavixFile.h"
0002 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0003 #include "FWCore/Utilities/interface/EDMException.h"
0004 #include "FWCore/Utilities/interface/Exception.h"
0005 #include <cassert>
0006 #include <davix.hpp>
0007 #include <cerrno>
0008 #include <fcntl.h>
0009 #include <cstdlib>
0010 #include <unistd.h>
0011 #include <vector>
0012 #include <mutex>
0013 
0014 static std::once_flag davixDebugInit;
0015 
0016 using namespace Davix;
0017 using namespace edm::storage;
0018 
0019 DavixFile::DavixFile(void) {}
0020 
0021 DavixFile::DavixFile(const char *name, int flags /* = IOFlags::OpenRead */, int perms /* = 066 */) {
0022   open(name, flags, perms);
0023 }
0024 
0025 DavixFile::DavixFile(const std::string &name, int flags /* = IOFlags::OpenRead */, int perms /* = 066 */) {
0026   open(name.c_str(), flags, perms);
0027 }
0028 
0029 DavixFile::~DavixFile(void) {
0030   close();
0031   return;
0032 }
0033 
0034 void DavixFile::close(void) {
0035   if (m_davixPosix && m_fd) {
0036     auto davixPosix = std::move(m_davixPosix);
0037     DavixError *err = nullptr;
0038     davixPosix->close(m_fd, &err);
0039     m_fd = nullptr;
0040     if (err) {
0041       std::unique_ptr<DavixError> davixErrManaged(err);
0042       cms::Exception ex("FileCloseError");
0043       ex << "Davix::close(name='" << m_name << ") failed with error " << err->getErrMsg().c_str() << " and error code "
0044          << err->getStatus();
0045       ex.addContext("Calling DavixFile::close()");
0046       throw ex;
0047     }
0048   }
0049   return;
0050 }
0051 
0052 void DavixFile::abort(void) {
0053   if (m_davixPosix && m_fd) {
0054     DavixError *err = nullptr;
0055     m_davixPosix->close(m_fd, &err);
0056     if (err) {
0057       std::unique_ptr<DavixError> davixErrManaged(err);
0058       cms::Exception ex("FileAbortError");
0059       ex << "Davix::abort(name='" << m_name << ") failed with error " << err->getErrMsg().c_str() << " and error code "
0060          << err->getStatus();
0061       ex.addContext("Calling DavixFile::abort()");
0062       throw ex;
0063     }
0064   }
0065   return;
0066 }
0067 
0068 void DavixFile::configureDavixLogLevel() {
0069   long logLevel = 0;
0070   char *logptr = nullptr;
0071   char const *const davixDebug = std::getenv("Davix_Debug");
0072   if (davixDebug != nullptr) {
0073     logLevel = strtol(davixDebug, &logptr, 0);
0074     if (errno) {
0075       edm::LogWarning("DavixFile") << "Got error while converting "
0076                                    << "Davix_Debug env variable to integer. "
0077                                       "Will use default log level 0";
0078       logLevel = 0;
0079     }
0080     if (logptr == davixDebug) {
0081       edm::LogWarning("DavixFile") << "Failed to convert to integer "
0082                                    << "Davix_Debug env variable; Will use default log level 0";
0083       logLevel = 0;
0084     } else if (*logptr != '\0') {
0085       edm::LogWarning("DavixFile") << "Failed to parse extra junk "
0086                                    << "from Davix_Debug env variable. Will use default log level 0";
0087       logLevel = 0;
0088     }
0089   }
0090   switch (logLevel) {
0091     case 0:
0092       std::call_once(davixDebugInit, davix_set_log_level, 0);
0093       break;
0094     case 1:
0095       std::call_once(davixDebugInit, davix_set_log_level, DAVIX_LOG_WARNING);
0096       break;
0097     case 2:
0098       std::call_once(davixDebugInit, davix_set_log_level, DAVIX_LOG_VERBOSE);
0099       break;
0100     case 3:
0101       std::call_once(davixDebugInit, davix_set_log_level, DAVIX_LOG_DEBUG);
0102       break;
0103     default:
0104       std::call_once(davixDebugInit, davix_set_log_level, DAVIX_LOG_ALL);
0105       break;
0106   }
0107 }
0108 
0109 static int X509Authentication(void *userdata, const SessionInfo &info, X509Credential *cert, DavixError **davixErr) {
0110   std::string ucert, ukey;
0111   char default_proxy[64];
0112   snprintf(default_proxy, sizeof(default_proxy), "/tmp/x509up_u%d", geteuid());
0113   // X509_USER_PROXY
0114   if (std::getenv("X509_USER_PROXY")) {
0115     edm::LogInfo("DavixFile") << "X509_USER_PROXY found in environment."
0116                               << " Will use it for authentication";
0117     ucert = ukey = std::getenv("X509_USER_PROXY");
0118   }
0119   // Default proxy location
0120   else if (access(default_proxy, R_OK) == 0) {
0121     edm::LogInfo("DavixFile") << "Found proxy in default location " << default_proxy
0122                               << " Will use it for authentication";
0123     ucert = ukey = default_proxy;
0124   }
0125   // X509_USER_CERT
0126   else if (std::getenv("X509_USER_CERT")) {
0127     ucert = std::getenv("X509_USER_CERT");
0128   }
0129   // X509_USER_KEY only if X509_USER_CERT was found
0130   if (!ucert.empty() && std::getenv("X509_USER_KEY")) {
0131     edm::LogInfo("DavixFile") << "X509_USER_{CERT|KEY} found in environment"
0132                               << " Will use it for authentication";
0133     ukey = std::getenv("X509_USER_KEY");
0134   }
0135   // Check if vars are set...
0136   if (ucert.empty() || ukey.empty()) {
0137     edm::LogWarning("DavixFile") << "Was not able to find proxy in $X509_USER_PROXY, "
0138                                  << "X509_USER_{CERT|KEY} or default proxy creation location. "
0139                                  << "Will try without authentication";
0140     return -1;
0141   }
0142   return cert->loadFromFilePEM(ukey, ucert, "", davixErr);
0143 }
0144 
0145 void DavixFile::create(const char *name, bool exclusive /* = false */, int perms /* = 066 */) {
0146   open(name,
0147        (IOFlags::OpenCreate | IOFlags::OpenWrite | IOFlags::OpenTruncate | (exclusive ? IOFlags::OpenExclusive : 0)),
0148        perms);
0149 }
0150 
0151 void DavixFile::create(const std::string &name, bool exclusive /* = false */, int perms /* = 066 */) {
0152   open(name.c_str(),
0153        (IOFlags::OpenCreate | IOFlags::OpenWrite | IOFlags::OpenTruncate | (exclusive ? IOFlags::OpenExclusive : 0)),
0154        perms);
0155 }
0156 
0157 void DavixFile::open(const std::string &name, int flags /* = IOFlags::OpenRead */, int perms /* = 066 */) {
0158   open(name.c_str(), flags, perms);
0159 }
0160 
0161 void DavixFile::open(const char *name, int flags /* = IOFlags::OpenRead */, int perms /* = 066 */) {
0162   // Actual open
0163   if ((name == nullptr) || (*name == 0)) {
0164     edm::Exception ex(edm::errors::FileOpenError);
0165     ex << "Cannot open a file without name";
0166     ex.addContext("Calling DavixFile::open()");
0167     throw ex;
0168   }
0169   m_name = name;
0170 
0171   if ((flags & IOFlags::OpenRead) == 0) {
0172     edm::Exception ex(edm::errors::FileOpenError);
0173     ex << "Must open file '" << name << "' at least for read";
0174     ex.addContext("Calling DavixFile::open()");
0175     throw ex;
0176   }
0177 
0178   if (m_davixPosix && m_fd) {
0179     edm::Exception ex(edm::errors::FileOpenError);
0180     ex << "Davix::open(name='" << m_name << "') failed on already open file";
0181     ex.addContext("Calling DavixFile::open()");
0182     throw ex;
0183   }
0184   configureDavixLogLevel();
0185   // Translate our flags to system flags
0186   int openflags = 0;
0187 
0188   if (flags & IOFlags::OpenRead)
0189     openflags |= O_RDONLY;
0190 
0191   DavixError *davixErr = nullptr;
0192   RequestParams davixReqParams;
0193   // Set up X509 authentication
0194   davixReqParams.setClientCertCallbackX509(&X509Authentication, nullptr);
0195   // Set also CERT_DIR if it is set in envinroment, otherwise use default
0196   const char *cert_dir = nullptr;
0197   if ((cert_dir = std::getenv("X509_CERT_DIR")) == nullptr)
0198     cert_dir = "/etc/grid-security/certificates";
0199   davixReqParams.addCertificateAuthorityPath(cert_dir);
0200 
0201   m_davixPosix = std::make_unique<DavPosix>(new Context());
0202   m_fd = m_davixPosix->open(&davixReqParams, name, openflags, &davixErr);
0203 
0204   // Check Davix Error
0205   if (davixErr) {
0206     std::unique_ptr<DavixError> davixErrManaged(davixErr);
0207     edm::Exception ex(edm::errors::FileOpenError);
0208     ex << "Davix::open(name='" << m_name << "') failed with "
0209        << "error '" << davixErr->getErrMsg().c_str() << " and error code " << davixErr->getStatus();
0210     ex.addContext("Calling DavixFile::open()");
0211     throw ex;
0212   }
0213   if (!m_fd) {
0214     edm::Exception ex(edm::errors::FileOpenError);
0215     ex << "Davix::open(name='" << m_name << "') failed as fd is NULL";
0216     ex.addContext("Calling DavixFile::open()");
0217     throw ex;
0218   }
0219 }
0220 
0221 IOSize DavixFile::readv(IOBuffer *into, IOSize buffers) {
0222   assert(!buffers || into);
0223 
0224   // Davix does not support 0 buffers;
0225   if (buffers == 0)
0226     return 0;
0227 
0228   DavixError *davixErr = nullptr;
0229 
0230   std::vector<DavIOVecInput> input_vector(buffers);
0231   std::vector<DavIOVecOuput> output_vector(buffers);
0232   IOSize total = 0;  // Total requested bytes
0233   for (IOSize i = 0; i < buffers; ++i) {
0234     input_vector[i].diov_size = into[i].size();
0235     input_vector[i].diov_buffer = static_cast<char *>(into[i].data());
0236     total += into[i].size();
0237   }
0238 
0239   ssize_t s = m_davixPosix->preadVec(m_fd, input_vector.data(), output_vector.data(), buffers, &davixErr);
0240   if (davixErr) {
0241     std::unique_ptr<DavixError> davixErrManaged(davixErr);
0242     edm::Exception ex(edm::errors::FileReadError);
0243     ex << "Davix::readv(name='" << m_name << "', buffers=" << (buffers) << ") failed with error "
0244        << davixErr->getErrMsg().c_str() << " and error code " << davixErr->getStatus() << " and call returned " << s
0245        << " bytes";
0246     ex.addContext("Calling DavixFile::readv()");
0247     throw ex;
0248   }
0249   // Davix limits number of requests sent to the server
0250   // to improve performance and it does range coalescing.
0251   // So we can`t check what preadVec returns with what was requested.
0252   // Example: If two ranges are overlapping, [10, 20] and [20, 30] which is
0253   // coalesced into [10, 30] and it will contain one less byte than was requested.
0254   // Only check if returned val <= 0 and make proper actions.
0255   if (s < 0) {
0256     edm::Exception ex(edm::errors::FileReadError);
0257     ex << "Davix::readv(name='" << m_name << "') failed and call returned " << s;
0258     ex.addContext("Calling DavixFile::readv()");
0259     throw ex;
0260   } else if (s == 0) {
0261     // end of file
0262     return 0;
0263   }
0264   return total;
0265 }
0266 
0267 IOSize DavixFile::readv(IOPosBuffer *into, IOSize buffers) {
0268   assert(!buffers || into);
0269 
0270   // Davix does not support 0 buffers;
0271   if (buffers == 0)
0272     return 0;
0273 
0274   DavixError *davixErr = nullptr;
0275 
0276   std::vector<DavIOVecInput> input_vector(buffers);
0277   std::vector<DavIOVecOuput> output_vector(buffers);
0278   IOSize total = 0;
0279   for (IOSize i = 0; i < buffers; ++i) {
0280     input_vector[i].diov_offset = into[i].offset();
0281     input_vector[i].diov_size = into[i].size();
0282     input_vector[i].diov_buffer = static_cast<char *>(into[i].data());
0283     total += into[i].size();
0284   }
0285   ssize_t s = m_davixPosix->preadVec(m_fd, input_vector.data(), output_vector.data(), buffers, &davixErr);
0286   if (davixErr) {
0287     std::unique_ptr<DavixError> davixErrManaged(davixErr);
0288     edm::Exception ex(edm::errors::FileReadError);
0289     ex << "Davix::readv(name='" << m_name << "', n=" << buffers << ") failed with error "
0290        << davixErr->getErrMsg().c_str() << " and error code " << davixErr->getStatus() << " and call returned " << s
0291        << " bytes";
0292     ex.addContext("Calling DavixFile::readv()");
0293     throw ex;
0294   }
0295   // Davix limits number of requests sent to the server
0296   // to improve performance and it does range coalescing.
0297   // So we can`t check what preadVec returns with what was requested.
0298   // Example: If two ranges are overlapping, [10, 20] and [20, 30] which is
0299   // coalesced into [10, 30] and it will contain one less byte than was requested.
0300   // Only check if returned val <= 0 and make proper actions.
0301   if (s < 0) {
0302     edm::Exception ex(edm::errors::FileReadError);
0303     ex << "Davix::readv(name='" << m_name << "', n=" << buffers << ") failed and call returned " << s;
0304     ex.addContext("Calling DavixFile::readv()");
0305     throw ex;
0306   } else if (s == 0) {
0307     // end of file
0308     return 0;
0309   }
0310   return total;
0311 }
0312 
0313 IOSize DavixFile::read(void *into, IOSize n) {
0314   DavixError *davixErr = nullptr;
0315   m_davixPosix->fadvise(m_fd, 0, n, AdviseRandom);
0316   IOSize done = 0;
0317   while (done < n) {
0318     ssize_t s = m_davixPosix->read(m_fd, (char *)into + done, n - done, &davixErr);
0319     if (davixErr) {
0320       std::unique_ptr<DavixError> davixErrManaged(davixErr);
0321       edm::Exception ex(edm::errors::FileReadError);
0322       ex << "Davix::read(name='" << m_name << "', n=" << (n - done) << ") failed with error "
0323          << davixErr->getErrMsg().c_str() << " and error code " << davixErr->getStatus() << " and call returned " << s
0324          << " bytes";
0325       ex.addContext("Calling DavixFile::read()");
0326       throw ex;
0327     }
0328     if (s < 0) {
0329       edm::Exception ex(edm::errors::FileReadError);
0330       ex << "Davix::read(name='" << m_name << "', n=" << (n - done) << ") failed and call returned " << s;
0331       ex.addContext("Calling DavixFile::read()");
0332       throw ex;
0333     } else if (s == 0) {
0334       // end of file
0335       break;
0336     }
0337     done += s;
0338   }
0339   return done;
0340 }
0341 
0342 IOSize DavixFile::write(const void *from, IOSize n) {
0343   edm::Exception ex(edm::errors::FileWriteError);
0344   ex << "DavixFile::write(name='" << m_name << "') not implemented";
0345   ex.addContext("Calling DavixFile::write()");
0346   throw ex;
0347 }
0348 
0349 IOOffset DavixFile::position(IOOffset offset, Relative whence /* = SET */) {
0350   DavixError *davixErr = nullptr;
0351   if (whence != CURRENT && whence != SET && whence != END) {
0352     cms::Exception ex("FilePositionError");
0353     ex << "DavixFile::position() called with incorrect 'whence' parameter";
0354     ex.addContext("Calling DavixFile::position()");
0355     throw ex;
0356   }
0357   IOOffset result;
0358   size_t mywhence = (whence == SET ? SEEK_SET : whence == CURRENT ? SEEK_CUR : SEEK_END);
0359 
0360   if ((result = m_davixPosix->lseek(m_fd, offset, mywhence, &davixErr)) == -1) {
0361     cms::Exception ex("FilePositionError");
0362     ex << "Davix::lseek(name='" << m_name << "', offset=" << offset << ", whence=" << mywhence << ") failed with error "
0363        << davixErr->getErrMsg().c_str() << " and error code " << davixErr->getStatus() << " and "
0364        << "call returned " << result;
0365     ex.addContext("Calling DavixFile::position()");
0366     throw ex;
0367   }
0368 
0369   return result;
0370 }
0371 
0372 void DavixFile::resize(IOOffset /* size */) {
0373   cms::Exception ex("FileResizeError");
0374   ex << "DavixFile::resize(name='" << m_name << "') not implemented";
0375   ex.addContext("Calling DavixFile::resize()");
0376   throw ex;
0377 }