// ---------------------------------------------------------------------- // File: S3Store.cc // Author: Andreas-Joachim Peters & Justin Lewis Salmon - 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/http/HttpServer.hh" #include "mgm/http/s3/S3Store.hh" #include "mgm/http/s3/S3Handler.hh" #include "mgm/XrdMgmOfs.hh" #include "mgm/XrdMgmOfsDirectory.hh" #include "namespace/interface/IView.hh" #include "namespace/utils/Checksum.hh" #include "common/http/PlainHttpResponse.hh" #include "common/Logging.hh" #include "common/LayoutId.hh" #include "common/FileId.hh" #include "common/Timing.hh" EOSMGMNAMESPACE_BEGIN #define XML_V1_UTF8 "" /*----------------------------------------------------------------------------*/ S3Store::S3Store(const char* s3defpath) { mS3DefContainer = s3defpath; mStoreModificationTime = 1; mStoreReloadTime = 1; } /*----------------------------------------------------------------------------*/ void S3Store::Refresh() { // Refresh the S3 id, keys, container definitions time_t now = time(NULL); time_t srtime; { eos::common::RWMutexReadLock sLock(mStoreMutex); srtime = mStoreReloadTime; } // Attempt refresh only once per minute if ((now - srtime) > 60) { eos::common::RWMutexWriteLock sLock(mStoreMutex); mStoreReloadTime = now; XrdOucErrInfo error; eos::common::VirtualIdentity vid = eos::common::VirtualIdentity::Root(); eos::IContainerMD::XAttrMap map; struct stat buf; if (gOFS->_stat(mS3DefContainer.c_str(), &buf, error, vid, (const char*) 0) == SFS_OK) { // check last modification time if (buf.st_ctime != mStoreModificationTime) { // clear all mS3ContainerSet.clear(); mS3Keys.clear(); mS3ContainerPath.clear(); if (gOFS->_attr_ls(mS3DefContainer.c_str(), error, vid, 0, map) != SFS_OK) { eos_static_err("unable to list attributes of % s", mS3DefContainer.c_str()); } else { // parse the attributes into the store for (auto it = map.begin(); it != map.end(); it++) { eos_static_info("parsing %s=>%s", it->first.c_str(), it->second.c_str()); if (it->first.substr(0, 6) == "sys.s3") { // the s3 attributes are built as // sys.s3.id. => secret key // sys.s3.bucket. => bucket list // sys.s3.path. => path if (it->first.substr(0, 10) == "sys.s3.id.") { std::string id = it->first.substr(10); mS3Keys[id] = it->second; eos_static_info("id=%s key=", id.c_str()); } if (it->first.substr(0, 14) == "sys.s3.bucket.") { std::string id = it->first.substr(14); std::vector svec; eos::common::StringConversion::Tokenize(it->second, svec, "|"); for (size_t i = 0; i < svec.size(); i++) { if (svec[i][0] == '\"') { svec[i].erase(0, 1); } if (svec[i][svec[i].length() - 1] == '\"') { svec[i].erase(svec[i].length() - 1); } mS3ContainerSet[id].insert(svec[i]); eos_static_debug("id=%s bucket=%s", id.c_str(), svec[i].c_str()); } } if (it->first.substr(0, 12) == "sys.s3.path.") { std::string bucket = it->first.substr(12); mS3ContainerPath[bucket] = it->second; eos_static_info("bucket=%s path=%s", bucket.c_str(), it->second.c_str()); } } } // store the modification time of the loaded s3 definitions mStoreModificationTime = buf.st_ctime; } } else { eos_static_info("skipping S3 configuration reload. " "Reason: no change detected since last refresh"); } } else { eos_static_err("unable to stat S3 configuration container %s", mS3DefContainer.c_str()); } } else { eos_static_info("skipping S3 configuration reload. " "Reason: refresh performed recently"); } } /*----------------------------------------------------------------------------*/ eos::common::HttpResponse* S3Store::ListBuckets(const std::string& id) { eos::common::RWMutexReadLock sLock(mStoreMutex); eos::common::HttpResponse* response = 0; std::string result = XML_V1_UTF8; result += ""; result += ""; result += id; result += ""; result += ""; result += id; result += ""; result += ""; result += ""; for (auto it = mS3ContainerSet[id].begin(); it != mS3ContainerSet[id].end(); it++) { if (mS3ContainerPath.count(*it)) { // check if we know how to map a bucket name into our regular namespace std::string bucketpath = mS3ContainerPath[*it]; XrdOucErrInfo error; eos::common::VirtualIdentity vid = eos::common::VirtualIdentity::Root(); eos::IContainerMD::XAttrMap map; struct stat buf; if (gOFS->_stat(bucketpath.c_str(), &buf, error, vid, (const char*) 0) == SFS_OK) { result += ""; result += ""; result += *it; result += ""; result += ""; result += eos::common::Timing::UnixTimestamp_to_ISO8601(buf.st_ctime); result += ""; result += ""; } else { std::string errmsg = "cannot find bucket path "; errmsg += bucketpath; errmsg += " for bucket "; errmsg += *it; return eos::common::S3Handler::RestErrorResponse( eos::common::HttpResponse::NOT_FOUND, "NoSuchBucket", errmsg, *it, ""); } } } result += ""; result += ""; response = new eos::common::PlainHttpResponse(); response->AddHeader("Content-Type", "application/xml"); response->AddHeader("x-amz-id-2", "unknown"); response->AddHeader("x-amz-request-id", "unknown"); response->SetBody(result); return response; } /*----------------------------------------------------------------------------*/ eos::common::HttpResponse* S3Store::ListBucket(const std::string& bucket, const std::string& query) { using namespace eos::common; XrdOucErrInfo error; VirtualIdentity vid = VirtualIdentity::Root(); RWMutexReadLock sLock(mStoreMutex); HttpResponse* response = 0; if (!mS3ContainerPath.count(bucket)) { // check if this bucket is configured return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchBucket", "Bucket does not exist!", bucket.c_str(), ""); } else { // check if this bucket is mapped struct stat buf; if (gOFS->_stat(mS3ContainerPath[bucket].c_str(), &buf, error, vid, (const char*) 0) != SFS_OK) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchBucket", "Bucket is not mapped into the " "namespace!", bucket.c_str(), ""); } } XrdOucEnv parameter(query.c_str()); XrdOucString lPrefix, lBucket; XrdMgmOfsDirectory bucketdir; uint64_t cnt = 0; uint64_t max_keys = 1000; std::string marker = ""; std::string prefix = ""; bool marker_reached = true; //!< indicates start of output const char* val = 0; if ((val = parameter.Get("max-keys"))) { max_keys = strtoull(val, 0, 10); } if ((val = parameter.Get("marker"))) { marker = val; if (marker == "(null)") { marker = ""; } } if ((val = parameter.Get("prefix"))) { prefix = val; } if (marker.length()) { marker_reached = false; } // handle ending slash in bucket and prefix paths lBucket = mS3ContainerPath[bucket].c_str(); if (!lBucket.endswith("/")) { lBucket += "/"; } lPrefix = prefix.c_str(); if (lPrefix.length() && !lPrefix.endswith("/")) { lPrefix += "/"; } eos_static_info("msg=\"listing\" bucket=%s prefix=%s", bucket.c_str(), lPrefix.c_str()); // Construct listing response std::string result = XML_V1_UTF8; result += ""; result += ""; result += bucket; result += ""; if (!prefix.length()) { result += ""; } else { result += ""; result += prefix; result += ""; } if ((!marker.length() || (!marker.c_str()))) { result += ""; } else { result += ""; result += marker; result += ""; } result += "/"; result += ""; char smaxkeys[16]; snprintf(smaxkeys, sizeof(smaxkeys) - 1, "%llu", (unsigned long long) max_keys); result += smaxkeys; result += ""; bool truncated = false; size_t truncate_pos = result.length() + 13; result += "false"; // list directory std::string directory = lBucket.c_str(); directory += lPrefix.c_str(); int listrc = bucketdir.open(directory.c_str(), vid, (const char*) 0); if (!listrc) { const char* name = 0; // loop over the directory contents while ((name = bucketdir.nextEntry())) { std::string entry = ""; std::string sname = name; if ((sname == ".") || (sname == "..")) { continue; } // don't return more than max-keys if (cnt++ > max_keys) { truncated = true; break; } // construct object name std::string objectname = lPrefix.c_str(); objectname += sname; std::string fullname = lBucket.c_str(); fullname += objectname; // check if output should begin if (!marker_reached) { if (marker == objectname) { marker_reached = true; } continue; } { // attempt file metadata retrieval eos::common::RWMutexReadLock lock(gOFS->eosViewRWMutex); std::shared_ptr cmd; std::shared_ptr fmd; int errc = 0; try { fmd = gOFS->eosView->getFile(fullname); entry = ""; entry += ""; entry += objectname.c_str(); entry += ""; entry += ""; eos::IFileMD::ctime_t mtime; fmd->getMTime(mtime); entry += Timing::UnixTimestamp_to_ISO8601(mtime.tv_sec); entry += ""; entry += "\""; eos::appendChecksumOnStringAsHex(fmd.get(), entry); entry += "\""; entry += ""; std::string sconv; entry += StringConversion::GetSizeString(sconv, (unsigned long long) fmd->getSize()); entry += ""; entry += "STANDARD"; entry += ""; entry += ""; entry += Mapping::UidToUserName(fmd->getCUid(), errc); entry += ""; entry += ""; entry += Mapping::UidToUserName(fmd->getCUid(), errc); entry += ":"; entry += Mapping::GidToGroupName(fmd->getCGid(), errc); entry += ""; entry += ""; entry += ""; } catch (eos::MDException& e) { fmd.reset(); if (e.getErrno() != ENOENT) { errno = e.getErrno(); eos_static_err("msg=\"could not open file\" ec=%d emsg=\"%s\" filepath=%s\n", e.getErrno(), e.getMessage().str().c_str(), fullname.c_str()); return S3Handler::RestErrorResponse( eos::common::HttpResponse::INTERNAL_SERVER_ERROR, "Internal Error", "Unable to open path", fullname.c_str(), ""); } } // should be a container if (!fmd) { // attempt container metadata retrieval try { cmd = gOFS->eosView->getContainer(fullname); entry = ""; entry += ""; entry += objectname.c_str(); entry += "/"; entry += ""; entry += ""; eos::IContainerMD::ctime_t mtime; cmd->getMTime(mtime); entry += Timing::UnixTimestamp_to_ISO8601(mtime.tv_sec); entry += ""; entry += ""; entry += ""; entry += "0"; entry += "STANDARD"; entry += ""; entry += ""; entry += Mapping::UidToUserName(cmd->getCUid(), errc); entry += ""; entry += ""; entry += Mapping::UidToUserName(cmd->getCUid(), errc); entry += ":"; entry += Mapping::GidToGroupName(cmd->getCGid(), errc); entry += ""; entry += ""; entry += ""; } catch (eos::MDException& e) { cmd.reset(); errno = e.getErrno(); eos_static_err("msg=\"could not open directory\" ec=%d emsg=\"%s\" dirpath=%s\n", e.getErrno(), e.getMessage().str().c_str(), fullname.c_str()); return S3Handler::RestErrorResponse( eos::common::HttpResponse::INTERNAL_SERVER_ERROR, "Internal Error", "Unable to open path", fullname.c_str(), ""); } } } // append this entry to the final result result += entry; } } bucketdir.close(); if (truncated) { result.replace(truncate_pos, 18, "true"); } result += ""; response = new PlainHttpResponse(); response->AddHeader("Content-Type", "application/xml"); response->AddHeader("Connection", "close"); response->SetBody(result); return response; } /*----------------------------------------------------------------------------*/ eos::common::HttpResponse* S3Store::HeadBucket(const std::string& id, const std::string& bucket, const std::string& date) { using namespace eos::common; HttpResponse* response = 0; XrdOucErrInfo error; VirtualIdentity vid = VirtualIdentity::Nobody(); int errc = 0; std::string username = id; uid_t uid = Mapping::UserNameToUid(username, errc); if (errc) { // error mapping the s3 id to unix id return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to map bucket id to virtual id", id.c_str(), ""); } // set the bucket id as vid vid.uid = uid; vid.allowed_uids.insert(uid); struct stat buf; // build the bucket path std::string bucketpath = mS3ContainerPath[bucket]; // stat this object if (gOFS->_stat(bucketpath.c_str(), &buf, error, vid, (const char*) 0) != SFS_OK) { if (error.getErrInfo() == ENOENT) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchBucket", "Unable stat requested bucket", id.c_str(), ""); } else { return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to stat requested bucket!", id.c_str(), ""); } } else { if (!S_ISDIR(buf.st_mode)) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchBucket", "Unable stat requested object - is " "an object", id.c_str(), ""); } response = new PlainHttpResponse(); // shift back the inode number to the original file id buf.st_ino = eos::common::FileId::InodeToFid(buf.st_ino); std::string sinode; response->AddHeader("x-amz-id-2", StringConversion::GetSizeString(sinode, (unsigned long long) buf.st_ino)); response->AddHeader("x-amz-request-id", StringConversion::GetSizeString(sinode, (unsigned long long) buf.st_ino)); response->AddHeader("ETag", StringConversion::GetSizeString(sinode, (unsigned long long) buf.st_ino)); response->AddHeader("Last-Modified", Timing::UnixTimestamp_to_ISO8601(buf.st_mtime)); response->AddHeader("Date", date); response->AddHeader("Connection", "Keep-Alive"); response->AddHeader("Server", gOFS->HostName); response->SetResponseCode(response->OK); return response; } } /*----------------------------------------------------------------------------*/ eos::common::HttpResponse* S3Store::HeadObject(const std::string& id, const std::string& bucket, const std::string& path, const std::string& date) { using namespace eos::common; XrdOucErrInfo error; VirtualIdentity vid = VirtualIdentity::Nobody(); HttpResponse* response = 0; int errc = 0; std::string username = id; uid_t uid = Mapping::UserNameToUid(username, errc); if (errc) { // error mapping the s3 id to unix id return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to map bucket id to virtual id", id.c_str(), ""); } // set the bucket id as vid vid.uid = uid; vid.allowed_uids.insert(uid); struct stat buf; // build the full path for the request std::string objectpath = mS3ContainerPath[bucket]; if (objectpath[objectpath.length() - 1] == '/') { objectpath.erase(objectpath.length() - 1); } objectpath += path; // stat this object if (gOFS->_stat(objectpath.c_str(), &buf, error, vid, (const char*) 0) != SFS_OK) { if (error.getErrInfo() == ENOENT) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchKey", "Unable stat requested object", id.c_str(), ""); } else { return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to stat requested object!", id.c_str(), ""); } } else { if (S_ISDIR(buf.st_mode)) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchKey", "Unable stat requested object - " "is a bucket subdirectory", id.c_str(), ""); } // shift back the inode number to the original file id buf.st_ino = eos::common::FileId::InodeToFid(buf.st_ino); std::string sinode; response = new PlainHttpResponse(); response->AddHeader("x-amz-id-2", StringConversion::GetSizeString(sinode, (unsigned long long) buf.st_ino)); response->AddHeader("x-amz-request-id", StringConversion::GetSizeString(sinode, (unsigned long long) buf.st_ino)); response->AddHeader("x-amz-version-id", StringConversion::GetSizeString(sinode, (unsigned long long) buf.st_ino)); response->AddHeader("ETag", StringConversion::GetSizeString(sinode, (unsigned long long) buf.st_ino)); response->AddHeader("Content-Length", StringConversion::GetSizeString(sinode, (unsigned long long) buf.st_size)); response->AddHeader("Last-Modified", Timing::UnixTimestamp_to_ISO8601(buf.st_mtime)); response->AddHeader("Date", date); response->AddHeader("Content-Type", HttpResponse::ContentType(path)); response->AddHeader("Connection", "close"); response->AddHeader("Server", gOFS->HostName); response->SetResponseCode(response->OK); return response; } } /*----------------------------------------------------------------------------*/ eos::common::HttpResponse* S3Store::GetObject(eos::common::HttpRequest* request, const std::string& id, const std::string& bucket, const std::string& path, const std::string& query) { using namespace eos::common; std::string result; XrdOucErrInfo error; VirtualIdentity vid = VirtualIdentity::Nobody(); HttpResponse* response = 0; int errc = 0; std::string username = id; uid_t uid = Mapping::UserNameToUid(username, errc); if (errc) { // error mapping the s3 id to unix id return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to map bucket id to virtual id", id.c_str(), ""); } // set the bucket id as vid vid.uid = uid; vid.allowed_uids.insert(uid); struct stat buf; // build the full path for the request std::string objectpath = mS3ContainerPath[bucket]; if (objectpath[objectpath.length() - 1] == '/') { objectpath.erase(objectpath.length() - 1); } objectpath += path; // evalutate If-XX requests time_t modified_since = 0; time_t unmodified_since = 0; unsigned long long inode_match = 0; unsigned long long inode_none_match = 0; if (request->GetHeaders().count("if-modified-since")) { modified_since = Timing::ISO8601_to_UnixTimestamp(request->GetHeaders() ["if-modified-since"]); } if (request->GetHeaders().count("if-unmodified-since")) { unmodified_since = Timing::ISO8601_to_UnixTimestamp(request->GetHeaders() ["if-unmodified-since"]); } if (request->GetHeaders().count("if-match")) { inode_match = strtoull(request->GetHeaders()["if-match"].c_str(), 0, 10); } if (request->GetHeaders().count("if-none-match")) { inode_none_match = strtoull(request->GetHeaders()["if-none-match"].c_str(), 0, 10); } // stat this object if (gOFS->_stat(objectpath.c_str(), &buf, error, vid, (const char*) 0) != SFS_OK) { if (error.getErrInfo() == ENOENT) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchKey", "Unable stat requested object", id.c_str(), ""); } else { return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to stat requested object!", id.c_str(), ""); } } else { // check if modified since was asked if (modified_since && (buf.st_mtime <= modified_since)) { return S3Handler::RestErrorResponse( eos::common::HttpResponse::PRECONDITION_FAILED, "PreconditionFailed", "Object was not modified since " "specified time!", path.c_str(), ""); } // check if unmodified since was asekd if (unmodified_since && (buf.st_mtime != unmodified_since)) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_MODIFIED, "NotModified", "Object was modified since specified " "time!", path.c_str(), ""); } // check if the matching inode was given if (inode_match && (buf.st_ino != inode_match)) { return S3Handler::RestErrorResponse( eos::common::HttpResponse::PRECONDITION_FAILED, "PreconditionFailed", "Object was modified!", path.c_str(), ""); } // check if a non matching inode was given if (inode_none_match && (buf.st_ino == inode_none_match)) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_MODIFIED, "NotModified", "Object was not modified!", path.c_str(), ""); } if (S_ISDIR(buf.st_mode)) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchKey", "Unable stat requested object - is a " "bucket subdirectory", id.c_str(), ""); } // FILE requests XrdSfsFile* file = gOFS->newFile((char*) id.c_str()); if (file) { XrdSecEntity client("unix"); client.name = strdup(id.c_str()); client.host = strdup(request->GetHeaders()["host"].c_str()); client.tident = strdup("http"); snprintf(client.prot, sizeof(client.prot) - 1, "https"); int rc = file->open(objectpath.c_str(), 0, 0, &client, query.c_str()); if (rc == SFS_REDIRECT) { response = HttpServer::HttpRedirect(objectpath, file->error.getErrText(), file->error.getErrInfo(), false); response->AddHeader("x-amz-website-redirect-location", response->GetHeaders()["Location"]); std::string body = XML_V1_UTF8; body += "" "TemporaryRedirect" "Please re-send this request to the specified temporary " "endpoint. Continue to use the original request endpoint for " "future requests." ""; body += response->GetHeaders()["Location"]; body += "" ""; response->SetBody(body); eos_static_info("\n\n%s\n\n", response->GetBody().c_str()); } else if (rc == SFS_ERROR) { if (file->error.getErrInfo() == ENOENT) { response = S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchKey", "The specified key does not exist", path, ""); } else if (file->error.getErrInfo() == EPERM) { response = S3Handler::RestErrorResponse(eos::common::HttpResponse::FORBIDDEN, "AccessDenied", "Access Denied", path, ""); } else { response = S3Handler::RestErrorResponse( eos::common::HttpResponse::INTERNAL_SERVER_ERROR, "Internal Error", "File currently unavailable", path, ""); } } else { response = S3Handler::RestErrorResponse( eos::common::HttpResponse::INTERNAL_SERVER_ERROR, "Internal Error", "File not accessible in this way", path, ""); } // clean up the object delete file; } } return response; } /*----------------------------------------------------------------------------*/ eos::common::HttpResponse* S3Store::PutObject(eos::common::HttpRequest* request, const std::string& id, const std::string& bucket, const std::string& path, const std::string& query) { using namespace eos::common; std::string result; XrdOucErrInfo error; VirtualIdentity vid = VirtualIdentity::Nobody(); HttpResponse* response = 0; int errc = 0; std::string username = id; uid_t uid = Mapping::UserNameToUid(username, errc); if (errc) { // error mapping the s3 id to unix id return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to map bucket id to virtual id", id.c_str(), ""); } // set the bucket id as vid vid.uid = uid; vid.allowed_uids.insert(uid); // build the full path for the request std::string objectpath = mS3ContainerPath[bucket]; if (objectpath[objectpath.length() - 1] == '/') { objectpath.erase(objectpath.length() - 1); } objectpath += path; // FILE requests XrdSfsFile* file = gOFS->newFile((char*) id.c_str()); if (file) { XrdSecEntity client("unix"); client.name = strdup(id.c_str()); client.host = strdup(request->GetHeaders()["host"].c_str()); client.tident = strdup("http"); snprintf(client.prot, sizeof(client.prot) - 1, "https"); // force MD5 checksums for S3 file creation std::string newquery = query; newquery.insert(0, "&eos.checksum.noforce=1&eos.layout.checksum=md5"); int rc = file->open(objectpath.c_str(), SFS_O_TRUNC, SFS_O_MKPTH, &client, newquery.c_str()); if (rc == SFS_REDIRECT) { response = HttpServer::HttpRedirect(objectpath, file->error.getErrText(), file->error.getErrInfo(), false); response->AddHeader("x-amz-website-redirect-location", response->GetHeaders()["Location"]); std::string body = XML_V1_UTF8; body += "" "TemporaryRedirect" "Please re-send this request to the specified temporary " "endpoint. Continue to use the original request endpoint for " "future requests." ""; body += response->GetHeaders()["Location"]; body += "" ""; response->SetBody(body); eos_static_info("\n\n%s\n\n", response->GetBody().c_str()); } else if (rc == SFS_ERROR) { if (file->error.getErrInfo() == EPERM) { response = S3Handler::RestErrorResponse(eos::common::HttpResponse::FORBIDDEN, "AccessDenied", "Access Denied", path, ""); } else { response = S3Handler::RestErrorResponse( eos::common::HttpResponse::INTERNAL_SERVER_ERROR, "Internal Error", "File creation currently " "unavailable", path, ""); } } else { response = S3Handler::RestErrorResponse( eos::common::HttpResponse::INTERNAL_SERVER_ERROR, "Internal Error", "File not accessible in this way", path, ""); } // clean up the object delete file; } return response; } /*----------------------------------------------------------------------------*/ eos::common::HttpResponse* S3Store::DeleteObject(eos::common::HttpRequest* request, const std::string& id, const std::string& bucket, const std::string& path) { using namespace eos::common; XrdOucErrInfo error; VirtualIdentity vid = VirtualIdentity::Nobody(); HttpResponse* response = 0; int errc = 0; std::string username = id; uid_t uid = Mapping::UserNameToUid(username, errc); if (errc) { // error mapping the s3 id to unix id return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to map bucket id to virtual id", id.c_str(), ""); } // set the bucket id as vid vid.uid = uid; vid.allowed_uids.insert(uid); struct stat buf; // build the full path for the request std::string objectpath = mS3ContainerPath[bucket]; if (objectpath[objectpath.length() - 1] == '/') { objectpath.erase(objectpath.length() - 1); } objectpath += path; // stat this object if (gOFS->_stat(objectpath.c_str(), &buf, error, vid, (const char*) 0) != SFS_OK) { if (error.getErrInfo() == ENOENT) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::NOT_FOUND, "NoSuchKey", "Unable to delete requested object", id.c_str(), ""); } else { return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to delete requested object", id.c_str(), ""); } } else { XrdOucString info = "mgm.cmd=rm&mgm.path="; info += objectpath.c_str(); if (S_ISDIR(buf.st_mode)) { info += "&mgm.option=r"; } ProcCommand cmd; cmd.open("/proc/user", info.c_str(), vid, &error); cmd.close(); int rc = cmd.GetRetc(); if (rc != SFS_OK) { if (error.getErrInfo() == EPERM) { return S3Handler::RestErrorResponse(eos::common::HttpResponse::FORBIDDEN, "AccessDenied", "Access Denied", path, ""); } else { return S3Handler::RestErrorResponse(eos::common::HttpResponse::BAD_REQUEST, "InvalidArgument", "Unable to delete requested object", id.c_str(), ""); } } else { response = new eos::common::PlainHttpResponse(); response->AddHeader("Connection", "close"); response->AddHeader("Server", gOFS->HostName); response->SetResponseCode(response->NO_CONTENT); } } return response; } EOSMGMNAMESPACE_END