From 665e6d9d5b3c251bd3aa85bc2cd0999c17b6d267 Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:42:35 -0500 Subject: [PATCH 01/18] Feature: Allow plugin to use multiple credentials to access multiple buckets in S3.Added new structure to hold S3 bucket information, enhanced config file, plumbed into existing file access functions. --- CMakeLists.txt | 2 +- src/S3AccessInfo.cc | 53 ++++++++++++++++++++++++++++++++++++ src/S3AccessInfo.hh | 46 +++++++++++++++++++++++++++++++ src/S3Commands.cc | 7 ++--- src/S3Commands.hh | 8 +++--- src/S3File.cc | 61 +++++++++++++++++++---------------------- src/S3File.hh | 2 +- src/S3FileSystem.cc | 66 +++++++++++++++++++++++++-------------------- src/S3FileSystem.hh | 39 ++++++++++++++------------- 9 files changed, 192 insertions(+), 92 deletions(-) create mode 100644 src/S3AccessInfo.cc create mode 100644 src/S3AccessInfo.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 91e9b6f..9cecd44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ endif() include_directories(${XROOTD_INCLUDES} ${CURL_INCLUDE_DIRS} ${LIBCRYPTO_INCLUDE_DIRS}) -add_library(XrdS3 SHARED src/S3File.cc src/S3FileSystem.cc src/AWSv4-impl.cc src/S3Commands.cc src/HTTPCommands.cc src/stl_string_utils.cc src/shortfile.cc src/logging.cc) +add_library(XrdS3 SHARED src/S3File.cc src/S3AccessInfo.cc src/S3FileSystem.cc src/AWSv4-impl.cc src/S3Commands.cc src/HTTPCommands.cc src/stl_string_utils.cc src/shortfile.cc src/logging.cc) add_library(XrdHTTPServer SHARED src/HTTPFile.cc src/HTTPFileSystem.cc src/HTTPCommands.cc src/stl_string_utils.cc src/shortfile.cc src/logging.cc) target_link_libraries(XrdS3 -ldl ${XROOTD_UTILS_LIB} ${XROOTD_SERVER_LIB} ${CURL_LIBRARIES} ${LIBCRYPTO_LIBRARIES}) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc new file mode 100644 index 0000000..60e2adf --- /dev/null +++ b/src/S3AccessInfo.cc @@ -0,0 +1,53 @@ +// +// Created by Rich Wellner on 2/29/24. +// + +#include "S3AccessInfo.hh" + +const std::string &S3AccessInfo::getS3BucketName() const { + return s3_bucket_name; +} + +void S3AccessInfo::setS3BucketName(const std::string &s3BucketName) { + s3_bucket_name = s3BucketName; +} + +const std::string &S3AccessInfo::getS3ServiceName() const { + return s3_service_name; +} + +void S3AccessInfo::setS3ServiceName(const std::string &s3ServiceName) { + s3_service_name = s3ServiceName; +} + +const std::string &S3AccessInfo::getS3Region() const { + return s3_region; +} + +void S3AccessInfo::setS3Region(const std::string &s3Region) { + s3_region = s3Region; +} + +const std::string &S3AccessInfo::getS3ServiceUrl() const { + return s3_service_url; +} + +void S3AccessInfo::setS3ServiceUrl(const std::string &s3ServiceUrl) { + s3_service_url = s3ServiceUrl; +} + +const std::string &S3AccessInfo::getS3AccessKeyFile() const { + return s3_access_key_file; +} + +void S3AccessInfo::setS3AccessKeyFile(const std::string &s3AccessKeyFile) { + s3_access_key_file = s3AccessKeyFile; +} + +const std::string &S3AccessInfo::getS3SecretKeyFile() const { + return s3_secret_key_file; +} + +void S3AccessInfo::setS3SecretKeyFile(const std::string &s3SecretKeyFile) { + s3_secret_key_file = s3SecretKeyFile; +} diff --git a/src/S3AccessInfo.hh b/src/S3AccessInfo.hh new file mode 100644 index 0000000..853bee7 --- /dev/null +++ b/src/S3AccessInfo.hh @@ -0,0 +1,46 @@ +// +// Created by Rich Wellner on 2/29/24. +// + +#ifndef XROOTD_S3_HTTP_S3ACCESSINFO_HH +#define XROOTD_S3_HTTP_S3ACCESSINFO_HH + + +#include + +class S3AccessInfo { +public: + const std::string &getS3BucketName() const; + + void setS3BucketName(const std::string &s3BucketName); + + const std::string &getS3ServiceName() const; + + void setS3ServiceName(const std::string &s3ServiceName); + + const std::string &getS3Region() const; + + void setS3Region(const std::string &s3Region); + + const std::string &getS3ServiceUrl() const; + + void setS3ServiceUrl(const std::string &s3ServiceUrl); + + const std::string &getS3AccessKeyFile() const; + + void setS3AccessKeyFile(const std::string &s3AccessKeyFile); + + const std::string &getS3SecretKeyFile() const; + + void setS3SecretKeyFile(const std::string &s3SecretKeyFile); + +private: + std::string s3_bucket_name; + std::string s3_service_name; + std::string s3_region; + std::string s3_service_url; + std::string s3_access_key_file; + std::string s3_secret_key_file; +}; + +#endif //XROOTD_S3_HTTP_S3ACCESSINFO_HH diff --git a/src/S3Commands.cc b/src/S3Commands.cc index 9006712..6ea5858 100644 --- a/src/S3Commands.cc +++ b/src/S3Commands.cc @@ -55,11 +55,8 @@ std::string AmazonRequest::canonicalizeQueryString() { // Takes in the configured `s3.service_url` and uses the bucket/object requested to generate // the virtual host URL, as well as the canonical URI (which is the path to the object). -bool AmazonRequest::parseURL( const std::string & url, - const std::string & bucket, - const std::string & object, - std::string & host, - std::string & path ) { +bool AmazonRequest::parseURL( const std::string & url, + std::string & path ) { auto i = url.find( "://" ); if( i == std::string::npos ) { return false; } //protocol = substring( url, 0, i ); diff --git a/src/S3Commands.hh b/src/S3Commands.hh index 15a7d1c..d2f9b6b 100644 --- a/src/S3Commands.hh +++ b/src/S3Commands.hh @@ -46,7 +46,7 @@ public: // Start off by parsing the hostUrl, which we use in conjunction with the bucket to fill in the host (for setting host header). // For example, if the incoming hostUrl (which we get from config) is "https://my-url.com:443", the bucket is "my-bucket", and // the object is "my-object", then the host will be "my-bucket.my-url.com:443" and the canonicalURI will be "/my-object". - if (! parseURL(hostUrl, b, o, host, canonicalURI)) { + if (! parseURL(hostUrl, canonicalURI)) { errorCode = "E_INVALID_SERVICE_URL"; errorMessage = "Failed to parse host and canonicalURI from service URL."; } @@ -75,9 +75,9 @@ public: return &secretKeyFile; } bool parseURL( const std::string & url, - const std::string & bucket, - const std::string & object, - std::string & host, + //const std::string & bucket, + //const std::string & object, + //std::string & host, std::string & path ); virtual bool SendRequest(); diff --git a/src/S3File.cc b/src/S3File.cc index 068351d..ed55b5f 100644 --- a/src/S3File.cc +++ b/src/S3File.cc @@ -54,42 +54,29 @@ S3File::S3File(XrdSysError &log, S3FileSystem *oss) : int -parse_path( const S3FileSystem & fs, const char * path, std::string & bucket, std::string & object ) { - const std::string & configured_s3_service_name = fs.getS3ServiceName(); - const std::string & configured_s3_region = fs.getS3Region(); - +parse_path( const S3FileSystem & fs, const char * fullPath, std::string & exposedPath, std::string & object ) { // // Check the path for validity. // - std::filesystem::path p(path); + std::filesystem::path p(fullPath); auto pathComponents = p.begin(); ++pathComponents; - if(pathComponents == p.end()) { return -ENOENT; } - if (* pathComponents != configured_s3_service_name) { - return -ENOENT; - } - - ++pathComponents; - if (pathComponents == p.end()) { return -ENOENT; } - if (*pathComponents != configured_s3_region) { - return -ENOENT; - } - - ++pathComponents; - if (pathComponents == p.end()) { return -ENOENT; } - bucket = *pathComponents; + if( pathComponents == p.end() ) { return -ENOENT; } + exposedPath = *pathComponents; // Objects names may contain path separators. ++pathComponents; - if (pathComponents == p.end()) { return -ENOENT; } + if( pathComponents == p.end() ) { return -ENOENT; } std::filesystem::path objectPath = *pathComponents++; - for ( ; pathComponents != p.end(); ++pathComponents) { - objectPath /= (*pathComponents); + for( ; pathComponents != p.end(); ++pathComponents ) { + objectPath /= (* pathComponents); } object = objectPath.string(); + fprintf( stderr, "object = %s\n", object.c_str() ); + return 0; } @@ -97,19 +84,25 @@ parse_path( const S3FileSystem & fs, const char * path, std::string & bucket, st int S3File::Open(const char *path, int Oflag, mode_t Mode, XrdOucEnv &env) { - std::string configured_s3_region = m_oss->getS3Region(); - - // - // Check the path for validity. - // - std::string bucket, object; - int rv = parse_path( * m_oss, path, bucket, object ); + std::string exposedPath, object; + int rv = parse_path( * m_oss, path, exposedPath, object ); if( rv != 0 ) { return rv; } + if(!m_oss->exposedPathExists(exposedPath)) return -ENOENT; + std::string configured_s3_region = m_oss->getS3Region(exposedPath); + std::string configured_s3_service_url = m_oss->getS3ServiceURL(exposedPath); + std::string configured_s3_access_key = m_oss->getS3AccessKeyFile(exposedPath); + std::string configured_s3_secret_key = m_oss->getS3SecretKeyFile(exposedPath); + std::string configured_s3_bucket_name = m_oss->getS3BucketName(exposedPath); + + // We used to query S3 here to see if the object existed, but of course + // if you're creating a file on upload, you don't care. - std::string configured_s3_service_url = m_oss->getS3ServiceURL(); - std::string configured_s3_access_key = m_oss->getS3AccessKeyFile(); - std::string configured_s3_secret_key = m_oss->getS3SecretKeyFile(); + this->s3_object_name = object; + this->s3_bucket_name = configured_s3_bucket_name; + this->s3_service_url = configured_s3_service_url; + this->s3_access_key = configured_s3_access_key; + this->s3_secret_key = configured_s3_secret_key; std::string configured_s3_url_style = m_oss->getS3URLStyle(); @@ -117,7 +110,7 @@ S3File::Open(const char *path, int Oflag, mode_t Mode, XrdOucEnv &env) // if you're creating a file on upload, you don't care. this->s3_object_name = object; - this->s3_bucket_name = bucket; + this->s3_bucket_name = configured_s3_bucket_name; this->s3_service_url = configured_s3_service_url; this->s3_access_key = configured_s3_access_key; this->s3_secret_key = configured_s3_secret_key; @@ -186,7 +179,7 @@ S3File::Fstat(struct stat *buff) size_t last_character = headers.size(); while( current_newline != std::string::npos && current_newline != last_character - 1 ) { next_newline = headers.find( "\r\n", current_newline + 2); - std::string line = substring( headers, current_newline + 2, next_newline ); + line = substring( headers, current_newline + 2, next_newline ); size_t colon = line.find(":"); if( colon != std::string::npos && colon != line.size() ) { diff --git a/src/S3File.hh b/src/S3File.hh index 65cb53e..d59c6ad 100644 --- a/src/S3File.hh +++ b/src/S3File.hh @@ -28,7 +28,7 @@ #include -int parse_path( const S3FileSystem & fs, const char * path, std::string & bucket, std::string & object ); +int parse_path( const S3FileSystem & fs, const char * path, std::string & exposedPath, std::string & object ); class S3File : public XrdOssDF { public: diff --git a/src/S3FileSystem.cc b/src/S3FileSystem.cc index ea27094..800f031 100644 --- a/src/S3FileSystem.cc +++ b/src/S3FileSystem.cc @@ -19,6 +19,7 @@ #include "S3Directory.hh" #include "S3File.hh" #include "S3FileSystem.hh" +#include "S3AccessInfo.hh" #include "stl_string_utils.hh" #include @@ -52,21 +53,14 @@ S3FileSystem::~S3FileSystem() { bool S3FileSystem::handle_required_config( - const std::string & name_from_config, - const char * desired_name, - const std::string & source, - std::string & target -) { - if( name_from_config != desired_name ) { return true; } - + const char * desired_name, + const std::string & source) { if( source.empty() ) { std::string error; formatstr( error, "%s must specify a value", desired_name ); m_log.Emsg( "Config", error.c_str() ); return false; } - - target = source; return true; } @@ -87,34 +81,48 @@ S3FileSystem::Config(XrdSysLogger *lp, const char *configfn) std::string value; std::string attribute; Config.Attach(cfgFD); + S3AccessInfo *newAccessInfo = new S3AccessInfo(); + std::string exposedPath; while ((temporary = Config.GetMyFirstWord())) { attribute = temporary; temporary = Config.GetWord(); + if(attribute == "s3.end") { + s3_access_map[exposedPath] = newAccessInfo; + if(newAccessInfo->getS3ServiceName().empty()) { + m_log.Emsg("Config", "s3.service_name not specified"); + return false; + } + if(newAccessInfo->getS3Region().empty()) { + m_log.Emsg("Config", "s3.region not specified"); + return false; + } + newAccessInfo = new S3AccessInfo(); + exposedPath = ""; + continue; + } if(! temporary) { continue; } value = temporary; - if(! handle_required_config( attribute, "s3.service_name", - value, this->s3_service_name ) ) { Config.Close(); return false; } - if(! handle_required_config( attribute, "s3.region", - value, this->s3_region ) ) { Config.Close(); return false; } - if(! handle_required_config( attribute, "s3.service_url", - value, this->s3_service_url ) ) { Config.Close(); return false; } - if(! handle_required_config( attribute, "s3.access_key_file", - value, this->s3_access_key_file ) ) { Config.Close(); return false; } - if(! handle_required_config( attribute, "s3.secret_key_file", - value, this->s3_secret_key_file ) ) { Config.Close(); return false; } - if(! handle_required_config( attribute, "s3.url_style", - value, this->s3_url_style ) ) { Config.Close(); return false; } - } + if(!handle_required_config("s3.path_name",value) ) { Config.Close(); return false; } + if(!handle_required_config("s3.bucket_name",value) ) { Config.Close(); return false; } + if(!handle_required_config("s3.service_name",value) ) { Config.Close(); return false; } + if(!handle_required_config("s3.region", value ) ) { Config.Close(); return false; } + if(!handle_required_config("s3.service_url", value) ) { Config.Close(); return false; } + if(!handle_required_config("s3.access_key_file", value) ) { Config.Close(); return false; } + if(!handle_required_config("s3.secret_key_file", value) ) { Config.Close(); return false; } + if(!handle_required_config("s3.url_style", value) ) { Config.Close(); return false; } + + if(attribute == "s3.path_name") exposedPath = value; + else if(attribute == "s3.bucket_name") newAccessInfo->setS3BucketName(value); + else if(attribute == "s3.service_name") newAccessInfo->setS3ServiceName(value); + else if(attribute == "s3.region") newAccessInfo->setS3Region(value); + else if(attribute == "s3.access_key_file") newAccessInfo->setS3AccessKeyFile(value); + else if(attribute == "s3.secret_key_file") newAccessInfo->setS3SecretKeyFile(value); + else if(attribute == "s3.service_url") newAccessInfo->setS3ServiceUrl(value); + else if(attribute == "s3.url_style") this->s3_url_style = value; - if( this->s3_service_name.empty() ) { - m_log.Emsg("Config", "s3.service_name not specified"); - return false; - } - if( this->s3_region.empty() ) { - m_log.Emsg("Config", "s3.region not specified"); - return false; } + if( this->s3_url_style.empty() ) { m_log.Emsg("Config", "s3.url_style not specified"); return false; diff --git a/src/S3FileSystem.hh b/src/S3FileSystem.hh index 975189e..7df357e 100644 --- a/src/S3FileSystem.hh +++ b/src/S3FileSystem.hh @@ -22,9 +22,12 @@ #include #include #include +#include "S3AccessInfo.hh" #include #include +#include + class S3FileSystem : public XrdOss { public: @@ -75,31 +78,31 @@ public: int Lfn2Pfn(const char *Path, char *buff, int blen) {return -ENOSYS;} const char *Lfn2Pfn(const char *Path, char *buff, int blen, int &rc) {return nullptr;} - const std::string & getS3ServiceName() const { return s3_service_name; } - const std::string & getS3Region() const { return s3_region; } - const std::string & getS3ServiceURL() const { return s3_service_url; } + int exposedPathExists(const std::string &exposedPath) + { return s3_access_map.count(exposedPath);} + const std::string & getS3ServiceName (const std::string &exposedPath) + const { return s3_access_map.at(exposedPath)->getS3ServiceName(); } + const std::string &getS3Region(const std::string &exposedPath) + const { return s3_access_map.at(exposedPath)->getS3Region(); } + const std::string &getS3ServiceURL(const std::string &exposedPath) + const { return s3_access_map.at(exposedPath)->getS3ServiceUrl(); } + const std::string &getS3BucketName(const std::string &exposedPath) + const { return s3_access_map.at(exposedPath)->getS3BucketName(); } + const std::string &getS3AccessKeyFile(const std::string &exposedPath) + const { return s3_access_map.at(exposedPath)->getS3AccessKeyFile(); } + const std::string &getS3SecretKeyFile(const std::string &exposedPath) + const { return s3_access_map.at(exposedPath)->getS3SecretKeyFile(); } const std::string & getS3URLStyle() const { return s3_url_style; } - const std::string & getS3AccessKeyFile() const { return s3_access_key_file; } - const std::string & getS3SecretKeyFile() const { return s3_secret_key_file; } - private: XrdOucEnv *m_env; XrdSysError m_log; bool handle_required_config( - const std::string & name_from_config, - const char * desired_name, - const std::string & source, - std::string & target + const char * desired_name, + const std::string & source ); - - std::string s3_service_name; - std::string s3_region; - std::string s3_service_url; - - std::string s3_access_key_file; - std::string s3_secret_key_file; - + std::map s3_access_map; std::string s3_url_style; + }; From f141551bff5a771a1eeb8492b0344ba2cf677d82 Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:46:38 -0500 Subject: [PATCH 02/18] Feature: Allow plugin to use multiple credentials to access multiple buckets in S3.Added new structure to hold S3 bucket information, enhanced config file, plumbed into existing file access functions. --- README.md | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c9e73d9..cf94c59 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ ofs.osslib libXrdHTTPServer.so # Upon last testing, the plugin did not yet work in async mode xrootd.async off + + # Configure the upstream HTTP server that XRootD is to treat as a filesystem httpserver.host_name httpserver.host_url @@ -127,13 +129,30 @@ ofs.osslib libXrdS3.so # Upon last testing, the plugin did not yet work in async mode xrootd.async off -# Configure the upstream HTTP server that XRootD is to treat as a filesystem -s3.service_name -s3.region -s3.service_url -# For buckets requiring an access/secret key: -# s3.access_key_file -# s3.secret_key_file +#example url +#https:///my-magic-path/bar/foo +# these must be in this order to allow parsing of multiple entries +s3.begin +s3.path_name my-magic-path +s3.bucket_name hubzero-private-rich +s3.service_name s3.amazonaws.com +s3.region us-east-1 +s3.access_key_file /xrootd-dev/access-key +s3.secret_key_file /xrootd-dev/secret-key +s3.service_url https://s3.us-east-1.amazonaws.com +s3.end + +s3.begin +s3.path_name my-other-magic-path +s3.bucket_name hubzero-private-rich-2 +s3.service_name s3.amazonaws.com +s3.region us-east-1 +s3.access_key_file /xrootd-dev/access-key-2 +s3.secret_key_file /xrootd-dev/secret-key-2 +s3.service_url https://s3.us-east-1.amazonaws.com +s3.end + +s3.url_style virtual ``` From 3949e40ca9ff484e29981fc44700706d63662036 Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:54:15 -0500 Subject: [PATCH 03/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index 60e2adf..47adbdd 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -5,7 +5,7 @@ #include "S3AccessInfo.hh" const std::string &S3AccessInfo::getS3BucketName() const { - return s3_bucket_name; + return s3_bucket_name; } void S3AccessInfo::setS3BucketName(const std::string &s3BucketName) { From 6ac258c74b7ee2fb442ed77361e82c69ea8b33d9 Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:54:23 -0500 Subject: [PATCH 04/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index 47adbdd..8a73355 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -9,7 +9,7 @@ const std::string &S3AccessInfo::getS3BucketName() const { } void S3AccessInfo::setS3BucketName(const std::string &s3BucketName) { - s3_bucket_name = s3BucketName; + s3_bucket_name = s3BucketName; } const std::string &S3AccessInfo::getS3ServiceName() const { From 12b43fdb6714606d6bfe7db0c580a6f4c19ed1d0 Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:54:32 -0500 Subject: [PATCH 05/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index 8a73355..02aa1de 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -13,7 +13,7 @@ void S3AccessInfo::setS3BucketName(const std::string &s3BucketName) { } const std::string &S3AccessInfo::getS3ServiceName() const { - return s3_service_name; + return s3_service_name; } void S3AccessInfo::setS3ServiceName(const std::string &s3ServiceName) { From 68dcc45bd23b66070d0cbccb8e354304f8247515 Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:54:39 -0500 Subject: [PATCH 06/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index 02aa1de..d2ee5e3 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -17,7 +17,7 @@ const std::string &S3AccessInfo::getS3ServiceName() const { } void S3AccessInfo::setS3ServiceName(const std::string &s3ServiceName) { - s3_service_name = s3ServiceName; + s3_service_name = s3ServiceName; } const std::string &S3AccessInfo::getS3Region() const { From 6381ed96a93b9dddc5f570efbc002d1939cfae36 Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:54:53 -0500 Subject: [PATCH 07/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index d2ee5e3..f0e6899 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -20,9 +20,7 @@ void S3AccessInfo::setS3ServiceName(const std::string &s3ServiceName) { s3_service_name = s3ServiceName; } -const std::string &S3AccessInfo::getS3Region() const { - return s3_region; -} +const std::string &S3AccessInfo::getS3Region() const { return s3_region; } void S3AccessInfo::setS3Region(const std::string &s3Region) { s3_region = s3Region; From 84f15c4e6a57a118667bb5b7523536386bcf4ec3 Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:55:00 -0500 Subject: [PATCH 08/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index f0e6899..58646de 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -47,5 +47,5 @@ const std::string &S3AccessInfo::getS3SecretKeyFile() const { } void S3AccessInfo::setS3SecretKeyFile(const std::string &s3SecretKeyFile) { - s3_secret_key_file = s3SecretKeyFile; + s3_secret_key_file = s3SecretKeyFile; } From 518facf4dd190dabc87ba9feadfe4fd1975f539d Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:57:51 -0500 Subject: [PATCH 09/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index 58646de..fc7e1b9 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -23,7 +23,7 @@ void S3AccessInfo::setS3ServiceName(const std::string &s3ServiceName) { const std::string &S3AccessInfo::getS3Region() const { return s3_region; } void S3AccessInfo::setS3Region(const std::string &s3Region) { - s3_region = s3Region; + s3_region = s3Region; } const std::string &S3AccessInfo::getS3ServiceUrl() const { From 3c888846d734c0312dcffc6a807f0a87452432fe Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:58:04 -0500 Subject: [PATCH 10/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index fc7e1b9..be8d0f7 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -27,7 +27,7 @@ void S3AccessInfo::setS3Region(const std::string &s3Region) { } const std::string &S3AccessInfo::getS3ServiceUrl() const { - return s3_service_url; + return s3_service_url; } void S3AccessInfo::setS3ServiceUrl(const std::string &s3ServiceUrl) { From 6322dd0c2896bf40f0de1c376865368d9e5f1e9f Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:58:23 -0500 Subject: [PATCH 11/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index be8d0f7..e310ea2 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -31,7 +31,7 @@ const std::string &S3AccessInfo::getS3ServiceUrl() const { } void S3AccessInfo::setS3ServiceUrl(const std::string &s3ServiceUrl) { - s3_service_url = s3ServiceUrl; + s3_service_url = s3ServiceUrl; } const std::string &S3AccessInfo::getS3AccessKeyFile() const { From 4fe017ae6292f8e06782fd47c52f41efc04fb6cd Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:58:34 -0500 Subject: [PATCH 12/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index e310ea2..620504d 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -35,7 +35,7 @@ void S3AccessInfo::setS3ServiceUrl(const std::string &s3ServiceUrl) { } const std::string &S3AccessInfo::getS3AccessKeyFile() const { - return s3_access_key_file; + return s3_access_key_file; } void S3AccessInfo::setS3AccessKeyFile(const std::string &s3AccessKeyFile) { From 01286be9301d7c03a05ceae136e985d17ed96bef Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:58:58 -0500 Subject: [PATCH 13/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index 620504d..195b1e7 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -39,7 +39,7 @@ const std::string &S3AccessInfo::getS3AccessKeyFile() const { } void S3AccessInfo::setS3AccessKeyFile(const std::string &s3AccessKeyFile) { - s3_access_key_file = s3AccessKeyFile; + s3_access_key_file = s3AccessKeyFile; } const std::string &S3AccessInfo::getS3SecretKeyFile() const { From 202fcd374212367b0a90f994875f672694264c8c Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:59:08 -0500 Subject: [PATCH 14/18] Update src/S3AccessInfo.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3AccessInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/S3AccessInfo.cc b/src/S3AccessInfo.cc index 195b1e7..783076d 100644 --- a/src/S3AccessInfo.cc +++ b/src/S3AccessInfo.cc @@ -43,7 +43,7 @@ void S3AccessInfo::setS3AccessKeyFile(const std::string &s3AccessKeyFile) { } const std::string &S3AccessInfo::getS3SecretKeyFile() const { - return s3_secret_key_file; + return s3_secret_key_file; } void S3AccessInfo::setS3SecretKeyFile(const std::string &s3SecretKeyFile) { From e895d17a327546d4f468ea64a866fa8950c7ceac Mon Sep 17 00:00:00 2001 From: Rich Wellner Date: Thu, 28 Mar 2024 19:59:40 -0500 Subject: [PATCH 15/18] Update src/S3FileSystem.cc Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/S3FileSystem.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/S3FileSystem.cc b/src/S3FileSystem.cc index 800f031..1b8ee98 100644 --- a/src/S3FileSystem.cc +++ b/src/S3FileSystem.cc @@ -20,6 +20,8 @@ #include "S3File.hh" #include "S3FileSystem.hh" #include "S3AccessInfo.hh" +#include "S3Directory.hh" +#include "S3File.hh" #include "stl_string_utils.hh" #include From 4cdccb88a2f3964cecfd0e434827aa9d87a10846 Mon Sep 17 00:00:00 2001 From: Justin Hiemstra Date: Mon, 1 Apr 2024 21:53:18 +0000 Subject: [PATCH 16/18] Update path parsing to handle mult-level paths --- src/S3File.cc | 18 +++++++++++++++--- src/S3FileSystem.cc | 13 ++++++++++--- src/S3FileSystem.hh | 4 ++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/S3File.cc b/src/S3File.cc index ed55b5f..c2f1217 100644 --- a/src/S3File.cc +++ b/src/S3File.cc @@ -61,9 +61,21 @@ parse_path( const S3FileSystem & fs, const char * fullPath, std::string & expose std::filesystem::path p(fullPath); auto pathComponents = p.begin(); - ++pathComponents; - if( pathComponents == p.end() ) { return -ENOENT; } - exposedPath = *pathComponents; + // Iterate through components of the fullPath until we either find a match + // or we've reached the end of the path. + std::filesystem::path currentPath = *pathComponents; + while (pathComponents != p.end()) { + if (fs.exposedPathExists(currentPath.string())) { + exposedPath = currentPath.string(); + break; + } + ++pathComponents; + if (pathComponents != p.end()) { + currentPath /= *pathComponents; + } else { + return -ENOENT; + } + } // Objects names may contain path separators. ++pathComponents; diff --git a/src/S3FileSystem.cc b/src/S3FileSystem.cc index 1b8ee98..6e98cce 100644 --- a/src/S3FileSystem.cc +++ b/src/S3FileSystem.cc @@ -114,7 +114,14 @@ S3FileSystem::Config(XrdSysLogger *lp, const char *configfn) if(!handle_required_config("s3.secret_key_file", value) ) { Config.Close(); return false; } if(!handle_required_config("s3.url_style", value) ) { Config.Close(); return false; } - if(attribute == "s3.path_name") exposedPath = value; + if (attribute == "s3.path_name") { + // Normalize paths so that they all start with / + if (value[0] != '/') { + exposedPath = "/" + value; + } else { + exposedPath = value; + } + } else if(attribute == "s3.bucket_name") newAccessInfo->setS3BucketName(value); else if(attribute == "s3.service_name") newAccessInfo->setS3ServiceName(value); else if(attribute == "s3.region") newAccessInfo->setS3Region(value); @@ -194,8 +201,8 @@ S3FileSystem::Create( const char *tid, const char *path, mode_t mode, XrdOucEnv &env, int opts ) { // Is path valid? - std::string bucket, object; - int rv = parse_path( * this, path, bucket, object ); + std::string exposedPath, object; + int rv = parse_path( * this, path, exposedPath, object ); if( rv != 0 ) { return rv; } // diff --git a/src/S3FileSystem.hh b/src/S3FileSystem.hh index 7df357e..e32272b 100644 --- a/src/S3FileSystem.hh +++ b/src/S3FileSystem.hh @@ -78,8 +78,8 @@ public: int Lfn2Pfn(const char *Path, char *buff, int blen) {return -ENOSYS;} const char *Lfn2Pfn(const char *Path, char *buff, int blen, int &rc) {return nullptr;} - int exposedPathExists(const std::string &exposedPath) - { return s3_access_map.count(exposedPath);} + bool exposedPathExists(const std::string &exposedPath) const + { return s3_access_map.count(exposedPath) > 0;} const std::string & getS3ServiceName (const std::string &exposedPath) const { return s3_access_map.at(exposedPath)->getS3ServiceName(); } const std::string &getS3Region(const std::string &exposedPath) From 1c5fd9e83dca0f890c83cb085d270fa6b0d335a4 Mon Sep 17 00:00:00 2001 From: Justin Hiemstra Date: Tue, 2 Apr 2024 14:00:17 +0000 Subject: [PATCH 17/18] Minor updates to README --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cf94c59..13ed37f 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ xrootd.async off #example url #https:///my-magic-path/bar/foo # these must be in this order to allow parsing of multiple entries +# To export a bucket requiring an access/private key: s3.begin s3.path_name my-magic-path s3.bucket_name hubzero-private-rich @@ -142,16 +143,20 @@ s3.secret_key_file /xrootd-dev/secret-key s3.service_url https://s3.us-east-1.amazonaws.com s3.end +# To export an unauthenticated (public) bucket, remove +# the key-related directives s3.begin s3.path_name my-other-magic-path s3.bucket_name hubzero-private-rich-2 s3.service_name s3.amazonaws.com s3.region us-east-1 -s3.access_key_file /xrootd-dev/access-key-2 -s3.secret_key_file /xrootd-dev/secret-key-2 s3.service_url https://s3.us-east-1.amazonaws.com s3.end +# Specify the path style for URL queries at the endpoint. Valid +# options are `path` and `virtual`, where path corresponds to URLs +# like `https://my-service-url.com/bucket/object` and virtual +# corresponds to URLs like `https://bucket.my-service-url.com/object` s3.url_style virtual ``` From b69e288f242a63d892dd7df094d0007ce33d0a81 Mon Sep 17 00:00:00 2001 From: Justin Hiemstra Date: Tue, 2 Apr 2024 14:10:33 +0000 Subject: [PATCH 18/18] Remove several pieces of commented code --- src/S3Commands.hh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/S3Commands.hh b/src/S3Commands.hh index d2f9b6b..38e10a1 100644 --- a/src/S3Commands.hh +++ b/src/S3Commands.hh @@ -75,14 +75,9 @@ public: return &secretKeyFile; } bool parseURL( const std::string & url, - //const std::string & bucket, - //const std::string & object, - //std::string & host, std::string & path ); virtual bool SendRequest(); - // virtual bool SendURIRequest(); - // virtual bool SendJSONRequest( const std::string & payload ); virtual bool SendS3Request( const std::string & payload ); protected: