/* * s3fs - FUSE-based file system backed by Amazon S3 * * Copyright 2007-2008 Randy Rizun * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "curl.h" #include "string_util.h" #include "s3fs.h" #include "s3fs_util.h" #include "s3fs_auth.h" using namespace std; static const std::string empty_payload_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; //------------------------------------------------------------------- // Utilities //------------------------------------------------------------------- // [TODO] // This function uses tempolary file, but should not use it. // For not using it, we implement function in each auth file(openssl, nss. gnutls). // static bool make_md5_from_string(const char* pstr, string& md5) { if(!pstr || '\0' == pstr[0]){ DPRN("Parameter is wrong."); return false; } FILE* fp; if(NULL == (fp = tmpfile())){ FPRN("Could not make tmpfile."); return false; } size_t length = strlen(pstr); if(length != fwrite(pstr, sizeof(char), length, fp)){ FPRN("Failed to write tmpfile."); fclose(fp); return false; } int fd; if(0 != fflush(fp) || 0 != fseek(fp, 0L, SEEK_SET) || -1 == (fd = fileno(fp))){ FPRN("Failed to make MD5."); fclose(fp); return false; } // base64 md5 md5 = s3fs_get_content_md5(fd); if(0 == md5.length()){ FPRN("Failed to make MD5."); fclose(fp); return false; } fclose(fp); return true; } static string url_to_host(const std::string &url) { DPRNNN("url is %s", url.c_str()); static const string http = "http://"; static const string https = "https://"; std::string host; if (url.compare(0, http.size(), http) == 0) { host = url.substr(http.size()); } else if (url.compare(0, https.size(), https) == 0) { host = url.substr(https.size()); } else { assert(!"url does not begin with http:// or https://"); } size_t idx; if ((idx = host.find(':')) != string::npos || (idx = host.find('/')) != string::npos) { return host.substr(0, idx); } else { return host; } } static string get_bucket_host() { if(!pathrequeststyle){ return bucket + "." + url_to_host(host); } return url_to_host(host) + "/" + bucket; } #if 0 // noused static string tolower_header_name(const char* head) { string::size_type pos; string name = head; string value(""); if(string::npos != (pos = name.find(':'))){ value= name.substr(pos); name = name.substr(0, pos); } name = lower(name); name += value; return name; } #endif //------------------------------------------------------------------- // Class BodyData //------------------------------------------------------------------- #define BODYDATA_RESIZE_APPEND_MIN (1 * 1024) // 1KB #define BODYDATA_RESIZE_APPEND_MID (1 * 1024 * 1024) // 1MB #define BODYDATA_RESIZE_APPEND_MAX (10 * 1024 * 1024) // 10MB #define AJUST_BLOCK(bytes, block) (((bytes / block) + ((bytes % block) ? 1 : 0)) * block) bool BodyData::Resize(size_t addbytes) { if(IsSafeSize(addbytes)){ return true; } // New size size_t need_size = AJUST_BLOCK((lastpos + addbytes + 1) - bufsize, sizeof(off_t)); if(BODYDATA_RESIZE_APPEND_MAX < bufsize){ need_size = (BODYDATA_RESIZE_APPEND_MAX < need_size ? need_size : BODYDATA_RESIZE_APPEND_MAX); }else if(BODYDATA_RESIZE_APPEND_MID < bufsize){ need_size = (BODYDATA_RESIZE_APPEND_MID < need_size ? need_size : BODYDATA_RESIZE_APPEND_MID); }else if(BODYDATA_RESIZE_APPEND_MIN < bufsize){ need_size = ((bufsize * 2) < need_size ? need_size : (bufsize * 2)); }else{ need_size = (BODYDATA_RESIZE_APPEND_MIN < need_size ? need_size : BODYDATA_RESIZE_APPEND_MIN); } // realloc char* newtext; if(NULL == (newtext = (char*)realloc(text, (bufsize + need_size)))){ DPRNCRIT("not enough memory (realloc returned NULL)"); free(text); text = NULL; return false; } text = newtext; bufsize += need_size; return true; } void BodyData::Clear(void) { if(text){ free(text); text = NULL; } lastpos = 0; bufsize = 0; } bool BodyData::Append(void* ptr, size_t bytes) { if(!ptr){ return false; } if(0 == bytes){ return true; } if(!Resize(bytes)){ return false; } memcpy(&text[lastpos], ptr, bytes); lastpos += bytes; text[lastpos] = '\0'; return true; } const char* BodyData::str(void) const { static const char* strnull = ""; if(!text){ return strnull; } return text; } //------------------------------------------------------------------- // Class S3fsCurl //------------------------------------------------------------------- #define MULTIPART_SIZE 10485760 // 10MB #define MAX_MULTI_COPY_SOURCE_SIZE 524288000 // 500MB #define IAM_EXPIRE_MERGIN (20 * 60) // update timming #define IAM_CRED_URL "http://169.254.169.254/latest/meta-data/iam/security-credentials/" #define IAMCRED_ACCESSKEYID "AccessKeyId" #define IAMCRED_SECRETACCESSKEY "SecretAccessKey" #define IAMCRED_ACCESSTOKEN "Token" #define IAMCRED_EXPIRATION "Expiration" #define IAMCRED_KEYCOUNT 4 // [NOTICE] // This symbol is for libcurl under 7.23.0 #ifndef CURLSHE_NOT_BUILT_IN #define CURLSHE_NOT_BUILT_IN 5 #endif pthread_mutex_t S3fsCurl::curl_handles_lock; pthread_mutex_t S3fsCurl::curl_share_lock[SHARE_MUTEX_MAX]; bool S3fsCurl::is_initglobal_done = false; CURLSH* S3fsCurl::hCurlShare = NULL; bool S3fsCurl::is_dns_cache = true; // default bool S3fsCurl::is_ssl_session_cache= true; // default long S3fsCurl::connect_timeout = 300; // default time_t S3fsCurl::readwrite_timeout = 60; // default int S3fsCurl::retries = 3; // default bool S3fsCurl::is_public_bucket = false; string S3fsCurl::default_acl = "private"; bool S3fsCurl::is_use_rrs = false; sseckeylist_t S3fsCurl::sseckeys; bool S3fsCurl::is_use_sse = false; bool S3fsCurl::is_content_md5 = false; bool S3fsCurl::is_verbose = false; string S3fsCurl::AWSAccessKeyId; string S3fsCurl::AWSSecretAccessKey; string S3fsCurl::AWSAccessToken; time_t S3fsCurl::AWSAccessTokenExpire= 0; string S3fsCurl::IAM_role; long S3fsCurl::ssl_verify_hostname = 1; // default(original code...) curltime_t S3fsCurl::curl_times; curlprogress_t S3fsCurl::curl_progress; string S3fsCurl::curl_ca_bundle; mimes_t S3fsCurl::mimeTypes; int S3fsCurl::max_parallel_cnt = 5; // default off_t S3fsCurl::multipart_size = MULTIPART_SIZE; // default bool S3fsCurl::is_sigv4 = true; // default //------------------------------------------------------------------- // Class methods for S3fsCurl //------------------------------------------------------------------- bool S3fsCurl::InitS3fsCurl(const char* MimeFile) { if(0 != pthread_mutex_init(&S3fsCurl::curl_handles_lock, NULL)){ return false; } if(0 != pthread_mutex_init(&S3fsCurl::curl_share_lock[SHARE_MUTEX_DNS], NULL)){ return false; } if(0 != pthread_mutex_init(&S3fsCurl::curl_share_lock[SHARE_MUTEX_SSL_SESSION], NULL)){ return false; } if(!S3fsCurl::InitMimeType(MimeFile)){ return false; } if(!S3fsCurl::InitGlobalCurl()){ return false; } if(!S3fsCurl::InitShareCurl()){ return false; } if(!S3fsCurl::InitCryptMutex()){ return false; } return true; } bool S3fsCurl::DestroyS3fsCurl(void) { int result = true; if(!S3fsCurl::DestroyCryptMutex()){ result = false; } if(!S3fsCurl::DestroyShareCurl()){ result = false; } if(!S3fsCurl::DestroyGlobalCurl()){ result = false; } if(0 != pthread_mutex_destroy(&S3fsCurl::curl_share_lock[SHARE_MUTEX_DNS])){ result = false; } if(0 != pthread_mutex_destroy(&S3fsCurl::curl_share_lock[SHARE_MUTEX_SSL_SESSION])){ result = false; } if(0 != pthread_mutex_destroy(&S3fsCurl::curl_handles_lock)){ result = false; } return result; } bool S3fsCurl::InitGlobalCurl(void) { if(S3fsCurl::is_initglobal_done){ return false; } if(CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)){ DPRN("init_curl_global_all returns error."); return false; } S3fsCurl::is_initglobal_done = true; return true; } bool S3fsCurl::DestroyGlobalCurl(void) { if(!S3fsCurl::is_initglobal_done){ return false; } curl_global_cleanup(); S3fsCurl::is_initglobal_done = false; return true; } bool S3fsCurl::InitShareCurl(void) { CURLSHcode nSHCode; if(!S3fsCurl::is_dns_cache && !S3fsCurl::is_ssl_session_cache){ DPRN("Curl does not share DNS data."); return true; } if(S3fsCurl::hCurlShare){ DPRN("already initiated."); return false; } if(NULL == (S3fsCurl::hCurlShare = curl_share_init())){ DPRN("curl_share_init failed"); return false; } if(CURLSHE_OK != (nSHCode = curl_share_setopt(S3fsCurl::hCurlShare, CURLSHOPT_LOCKFUNC, S3fsCurl::LockCurlShare))){ DPRN("curl_share_setopt(LOCKFUNC) returns %d(%s)", nSHCode, curl_share_strerror(nSHCode)); return false; } if(CURLSHE_OK != (nSHCode = curl_share_setopt(S3fsCurl::hCurlShare, CURLSHOPT_UNLOCKFUNC, S3fsCurl::UnlockCurlShare))){ DPRN("curl_share_setopt(UNLOCKFUNC) returns %d(%s)", nSHCode, curl_share_strerror(nSHCode)); return false; } if(S3fsCurl::is_dns_cache){ nSHCode = curl_share_setopt(S3fsCurl::hCurlShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); if(CURLSHE_OK != nSHCode && CURLSHE_BAD_OPTION != nSHCode && CURLSHE_NOT_BUILT_IN != nSHCode){ DPRN("curl_share_setopt(DNS) returns %d(%s)", nSHCode, curl_share_strerror(nSHCode)); return false; }else if(CURLSHE_BAD_OPTION == nSHCode || CURLSHE_NOT_BUILT_IN == nSHCode){ DPRN("curl_share_setopt(DNS) returns %d(%s), but continue without shared dns data.", nSHCode, curl_share_strerror(nSHCode)); } } if(S3fsCurl::is_ssl_session_cache){ nSHCode = curl_share_setopt(S3fsCurl::hCurlShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); if(CURLSHE_OK != nSHCode && CURLSHE_BAD_OPTION != nSHCode && CURLSHE_NOT_BUILT_IN != nSHCode){ DPRN("curl_share_setopt(SSL SESSION) returns %d(%s)", nSHCode, curl_share_strerror(nSHCode)); return false; }else if(CURLSHE_BAD_OPTION == nSHCode || CURLSHE_NOT_BUILT_IN == nSHCode){ DPRN("curl_share_setopt(SSL SESSION) returns %d(%s), but continue without shared ssl session data.", nSHCode, curl_share_strerror(nSHCode)); } } if(CURLSHE_OK != (nSHCode = curl_share_setopt(S3fsCurl::hCurlShare, CURLSHOPT_USERDATA, (void*)&S3fsCurl::curl_share_lock[0]))){ DPRN("curl_share_setopt(USERDATA) returns %d(%s)", nSHCode, curl_share_strerror(nSHCode)); return false; } return true; } bool S3fsCurl::DestroyShareCurl(void) { if(!S3fsCurl::hCurlShare){ if(!S3fsCurl::is_dns_cache && !S3fsCurl::is_ssl_session_cache){ return true; } DPRN("already destroy share curl."); return false; } if(CURLSHE_OK != curl_share_cleanup(S3fsCurl::hCurlShare)){ return false; } S3fsCurl::hCurlShare = NULL; return true; } void S3fsCurl::LockCurlShare(CURL* handle, curl_lock_data nLockData, curl_lock_access laccess, void* useptr) { if(!hCurlShare){ return; } pthread_mutex_t* lockmutex = static_cast(useptr); if(CURL_LOCK_DATA_DNS == nLockData){ pthread_mutex_lock(&lockmutex[SHARE_MUTEX_DNS]); }else if(CURL_LOCK_DATA_SSL_SESSION == nLockData){ pthread_mutex_lock(&lockmutex[SHARE_MUTEX_SSL_SESSION]); } } void S3fsCurl::UnlockCurlShare(CURL* handle, curl_lock_data nLockData, void* useptr) { if(!hCurlShare){ return; } pthread_mutex_t* lockmutex = static_cast(useptr); if(CURL_LOCK_DATA_DNS == nLockData){ pthread_mutex_unlock(&lockmutex[SHARE_MUTEX_DNS]); }else if(CURL_LOCK_DATA_SSL_SESSION == nLockData){ pthread_mutex_unlock(&lockmutex[SHARE_MUTEX_SSL_SESSION]); } } bool S3fsCurl::InitCryptMutex(void) { return s3fs_init_crypt_mutex(); } bool S3fsCurl::DestroyCryptMutex(void) { return s3fs_destroy_crypt_mutex(); } // homegrown timeout mechanism int S3fsCurl::CurlProgress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { CURL* curl = static_cast(clientp); time_t now = time(0); progress_t p(dlnow, ulnow); pthread_mutex_lock(&S3fsCurl::curl_handles_lock); // any progress? if(p != S3fsCurl::curl_progress[curl]){ // yes! S3fsCurl::curl_times[curl] = now; S3fsCurl::curl_progress[curl] = p; }else{ // timeout? if(now - S3fsCurl::curl_times[curl] > readwrite_timeout){ pthread_mutex_unlock(&S3fsCurl::curl_handles_lock); DPRN("timeout now: %jd, curl_times[curl]: %jd, readwrite_timeout: %jd", (intmax_t)now, (intmax_t)(S3fsCurl::curl_times[curl]), (intmax_t)readwrite_timeout); return CURLE_ABORTED_BY_CALLBACK; } } pthread_mutex_unlock(&S3fsCurl::curl_handles_lock); return 0; } bool S3fsCurl::InitMimeType(const char* MimeFile) { if(!MimeFile){ MimeFile = "/etc/mime.types"; // default } string line; ifstream MT(MimeFile); if(MT.good()){ while(getline(MT, line)){ if(line[0]=='#'){ continue; } if(line.size() == 0){ continue; } stringstream tmp(line); string mimeType; tmp >> mimeType; while(tmp){ string ext; tmp >> ext; if(ext.size() == 0){ continue; } S3fsCurl::mimeTypes[ext] = mimeType; } } } return true; } // // @param s e.g., "index.html" // @return e.g., "text/html" // string S3fsCurl::LookupMimeType(string name) { string result("application/octet-stream"); string::size_type last_pos = name.find_last_of('.'); string::size_type first_pos = name.find_first_of('.'); string prefix, ext, ext2; // No dots in name, just return if(last_pos == string::npos){ return result; } // extract the last extension if(last_pos != string::npos){ ext = name.substr(1+last_pos, string::npos); } if (last_pos != string::npos) { // one dot was found, now look for another if (first_pos != string::npos && first_pos < last_pos) { prefix = name.substr(0, last_pos); // Now get the second to last file extension string::size_type next_pos = prefix.find_last_of('.'); if (next_pos != string::npos) { ext2 = prefix.substr(1+next_pos, string::npos); } } } // if we get here, then we have an extension (ext) mimes_t::const_iterator iter = S3fsCurl::mimeTypes.find(ext); // if the last extension matches a mimeType, then return // that mime type if (iter != S3fsCurl::mimeTypes.end()) { result = (*iter).second; return result; } // return with the default result if there isn't a second extension if(first_pos == last_pos){ return result; } // Didn't find a mime-type for the first extension // Look for second extension in mimeTypes, return if found iter = S3fsCurl::mimeTypes.find(ext2); if (iter != S3fsCurl::mimeTypes.end()) { result = (*iter).second; return result; } // neither the last extension nor the second-to-last extension // matched a mimeType, return the default mime type return result; } bool S3fsCurl::LocateBundle(void) { // See if environment variable CURL_CA_BUNDLE is set // if so, check it, if it is a good path, then set the // curl_ca_bundle variable to it char *CURL_CA_BUNDLE; if(0 == S3fsCurl::curl_ca_bundle.size()){ CURL_CA_BUNDLE = getenv("CURL_CA_BUNDLE"); if(CURL_CA_BUNDLE != NULL) { // check for existence and readability of the file ifstream BF(CURL_CA_BUNDLE); if(!BF.good()){ DPRN("%s: file specified by CURL_CA_BUNDLE environment variable is not readable", program_name.c_str()); return false; } BF.close(); S3fsCurl::curl_ca_bundle.assign(CURL_CA_BUNDLE); return true; } } // not set via environment variable, look in likely locations /////////////////////////////////////////// // from curl's (7.21.2) acinclude.m4 file /////////////////////////////////////////// // dnl CURL_CHECK_CA_BUNDLE // dnl ------------------------------------------------- // dnl Check if a default ca-bundle should be used // dnl // dnl regarding the paths this will scan: // dnl /etc/ssl/certs/ca-certificates.crt Debian systems // dnl /etc/pki/tls/certs/ca-bundle.crt Redhat and Mandriva // dnl /usr/share/ssl/certs/ca-bundle.crt old(er) Redhat // dnl /usr/local/share/certs/ca-root.crt FreeBSD // dnl /etc/ssl/cert.pem OpenBSD // dnl /etc/ssl/certs/ (ca path) SUSE ifstream BF("/etc/pki/tls/certs/ca-bundle.crt"); if(BF.good()){ BF.close(); S3fsCurl::curl_ca_bundle.assign("/etc/pki/tls/certs/ca-bundle.crt"); }else{ DPRN("%s: /etc/pki/tls/certs/ca-bundle.crt is not readable", program_name.c_str()); return false; } return true; } size_t S3fsCurl::WriteMemoryCallback(void* ptr, size_t blockSize, size_t numBlocks, void* data) { BodyData* body = (BodyData*)data; if(!body->Append(ptr, blockSize, numBlocks)){ DPRNCRIT("BodyData.Append() returned false."); S3FS_FUSE_EXIT(); return -1; } return (blockSize * numBlocks); } size_t S3fsCurl::ReadCallback(void* ptr, size_t size, size_t nmemb, void* userp) { S3fsCurl* pCurl = reinterpret_cast(userp); if(1 > (size * nmemb)){ return 0; } if(0 >= pCurl->postdata_remaining){ return 0; } int copysize = std::min((int)(size * nmemb), pCurl->postdata_remaining); memcpy(ptr, pCurl->postdata, copysize); pCurl->postdata_remaining = (pCurl->postdata_remaining > copysize ? (pCurl->postdata_remaining - copysize) : 0); pCurl->postdata += static_cast(copysize); return copysize; } size_t S3fsCurl::HeaderCallback(void* data, size_t blockSize, size_t numBlocks, void* userPtr) { headers_t* headers = reinterpret_cast(userPtr); string header(reinterpret_cast(data), blockSize * numBlocks); string key; stringstream ss(header); if(getline(ss, key, ':')){ // Force to lower, only "x-amz" string lkey = key; transform(lkey.begin(), lkey.end(), lkey.begin(), static_cast(std::tolower)); if(lkey.substr(0, 5) == "x-amz"){ key = lkey; } string value; getline(ss, value); (*headers)[key] = trim(value); } return blockSize * numBlocks; } size_t S3fsCurl::UploadReadCallback(void* ptr, size_t size, size_t nmemb, void* userp) { S3fsCurl* pCurl = reinterpret_cast(userp); if(1 > (size * nmemb)){ return 0; } if(-1 == pCurl->partdata.fd || 0 >= pCurl->partdata.size){ return 0; } // read size ssize_t copysize = (size * nmemb) < (size_t)pCurl->partdata.size ? (size * nmemb) : (size_t)pCurl->partdata.size; ssize_t readbytes; ssize_t totalread; // read and set for(totalread = 0, readbytes = 0; totalread < copysize; totalread += readbytes){ readbytes = pread(pCurl->partdata.fd, &((char*)ptr)[totalread], (copysize - totalread), pCurl->partdata.startpos + totalread); if(0 == readbytes){ // eof break; }else if(-1 == readbytes){ // error DPRN("read file error(%d).", errno); return 0; } } pCurl->partdata.startpos += totalread; pCurl->partdata.size -= totalread; return totalread; } size_t S3fsCurl::DownloadWriteCallback(void* ptr, size_t size, size_t nmemb, void* userp) { S3fsCurl* pCurl = reinterpret_cast(userp); if(1 > (size * nmemb)){ return 0; } if(-1 == pCurl->partdata.fd || 0 >= pCurl->partdata.size){ return 0; } // write size ssize_t copysize = (size * nmemb) < (size_t)pCurl->partdata.size ? (size * nmemb) : (size_t)pCurl->partdata.size; ssize_t writebytes; ssize_t totalwrite; // write for(totalwrite = 0, writebytes = 0; totalwrite < copysize; totalwrite += writebytes){ writebytes = pwrite(pCurl->partdata.fd, &((char*)ptr)[totalwrite], (copysize - totalwrite), pCurl->partdata.startpos + totalwrite); if(0 == writebytes){ // eof? break; }else if(-1 == writebytes){ // error DPRN("write file error(%d).", errno); return 0; } } pCurl->partdata.startpos += totalwrite; pCurl->partdata.size -= totalwrite; return totalwrite; } bool S3fsCurl::SetDnsCache(bool isCache) { bool old = S3fsCurl::is_dns_cache; S3fsCurl::is_dns_cache = isCache; return old; } bool S3fsCurl::SetSslSessionCache(bool isCache) { bool old = S3fsCurl::is_ssl_session_cache; S3fsCurl::is_ssl_session_cache = isCache; return old; } long S3fsCurl::SetConnectTimeout(long timeout) { long old = S3fsCurl::connect_timeout; S3fsCurl::connect_timeout = timeout; return old; } time_t S3fsCurl::SetReadwriteTimeout(time_t timeout) { time_t old = S3fsCurl::readwrite_timeout; S3fsCurl::readwrite_timeout = timeout; return old; } int S3fsCurl::SetRetries(int count) { int old = S3fsCurl::retries; S3fsCurl::retries = count; return old; } bool S3fsCurl::SetPublicBucket(bool flag) { bool old = S3fsCurl::is_public_bucket; S3fsCurl::is_public_bucket = flag; return old; } string S3fsCurl::SetDefaultAcl(const char* acl) { string old = S3fsCurl::default_acl; S3fsCurl::default_acl = acl ? acl : ""; return old; } bool S3fsCurl::SetUseRrs(bool flag) { bool old = S3fsCurl::is_use_rrs; S3fsCurl::is_use_rrs = flag; return old; } bool S3fsCurl::PushbackSseKeys(string& onekey) { onekey = trim(onekey); if(0 == onekey.size()){ return false; } if('#' == onekey[0]){ return false; } // make base64 char* pbase64_key; if(NULL == (pbase64_key = s3fs_base64((unsigned char*)onekey.c_str(), onekey.length()))){ FPRN("Failed to convert base64 from sse-c key %s", onekey.c_str()); return false; } string base64_key = pbase64_key; free(pbase64_key); // make MD5 string strMd5; if(!make_md5_from_string(onekey.c_str(), strMd5)){ FPRN("Could not make MD5 from SSE-C keys(%s).", onekey.c_str()); return false; } // mapped MD5 = SSE Key sseckeymap_t md5map; md5map.clear(); md5map[strMd5] = base64_key; S3fsCurl::sseckeys.push_back(md5map); return true; } bool S3fsCurl::SetSseKeys(const char* filepath) { if(!filepath){ DPRN("SSE-C keys filepath is empty."); return false; } S3fsCurl::sseckeys.clear(); ifstream ssefs(filepath); if(!ssefs.good()){ FPRN("Could not open SSE-C keys file(%s).", filepath); return false; } string line; while(getline(ssefs, line)){ S3fsCurl::PushbackSseKeys(line); } if(0 == S3fsCurl::sseckeys.size()){ FPRN("There is no SSE Key in file(%s).", filepath); return false; } return true; } bool S3fsCurl::LoadEnvSseKeys(void) { char* envkeys = getenv("AWSSSECKEYS"); if(NULL == envkeys){ return false; } S3fsCurl::sseckeys.clear(); istringstream fullkeys(envkeys); string onekey; while(getline(fullkeys, onekey, ':')){ S3fsCurl::PushbackSseKeys(onekey); } if(0 == S3fsCurl::sseckeys.size()){ FPRN("There is no SSE Key in environment(AWSSSECKEYS=%s).", envkeys); return false; } return true; } // // If md5 is empty, returns first(current) sse key. // bool S3fsCurl::GetSseKey(string& md5, string& ssekey) { for(sseckeylist_t::const_iterator iter = S3fsCurl::sseckeys.begin(); iter != S3fsCurl::sseckeys.end(); iter++){ if(0 == md5.length() || md5 == (*iter).begin()->first){ md5 = iter->begin()->first; ssekey = iter->begin()->second; return true; } } return false; } bool S3fsCurl::GetSseKeyMd5(int pos, string& md5) { if(pos < 0){ return false; } if(S3fsCurl::sseckeys.size() <= static_cast(pos)){ return false; } int cnt = 0; for(sseckeylist_t::const_iterator iter = S3fsCurl::sseckeys.begin(); iter != S3fsCurl::sseckeys.end(); iter++, cnt++){ if(pos == cnt){ md5 = iter->begin()->first; return true; } } return false; } int S3fsCurl::GetSseKeyCount(void) { return S3fsCurl::sseckeys.size(); } bool S3fsCurl::IsSseCustomMode(void) { return (0 < S3fsCurl::sseckeys.size()); } bool S3fsCurl::SetUseSse(bool flag) { bool old = S3fsCurl::is_use_sse; S3fsCurl::is_use_sse = flag; return old; } bool S3fsCurl::SetContentMd5(bool flag) { bool old = S3fsCurl::is_content_md5; S3fsCurl::is_content_md5 = flag; return old; } bool S3fsCurl::SetVerbose(bool flag) { bool old = S3fsCurl::is_verbose; S3fsCurl::is_verbose = flag; return old; } bool S3fsCurl::SetAccessKey(const char* AccessKeyId, const char* SecretAccessKey) { if(!AccessKeyId || '\0' == AccessKeyId[0] || !SecretAccessKey || '\0' == SecretAccessKey[0]){ return false; } AWSAccessKeyId = AccessKeyId; AWSSecretAccessKey = SecretAccessKey; return true; } long S3fsCurl::SetSslVerifyHostname(long value) { if(0 != value && 1 != value){ return -1; } long old = S3fsCurl::ssl_verify_hostname; S3fsCurl::ssl_verify_hostname = value; return old; } string S3fsCurl::SetIAMRole(const char* role) { string old = S3fsCurl::IAM_role; S3fsCurl::IAM_role = role ? role : ""; return old; } bool S3fsCurl::SetMultipartSize(off_t size) { size = size * 1024 * 1024; if(size < MULTIPART_SIZE){ return false; } S3fsCurl::multipart_size = size; return true; } int S3fsCurl::SetMaxParallelCount(int value) { int old = S3fsCurl::max_parallel_cnt; S3fsCurl::max_parallel_cnt = value; return old; } bool S3fsCurl::UploadMultipartPostCallback(S3fsCurl* s3fscurl) { if(!s3fscurl){ return false; } // check etag(md5); if(NULL == strstr(s3fscurl->headdata->str(), s3fscurl->partdata.etag.c_str())){ return false; } s3fscurl->partdata.etaglist->at(s3fscurl->partdata.etagpos).assign(s3fscurl->partdata.etag); s3fscurl->partdata.uploaded = true; return true; } S3fsCurl* S3fsCurl::UploadMultipartPostRetryCallback(S3fsCurl* s3fscurl) { if(!s3fscurl){ return NULL; } // parse and get part_num, upload_id. string upload_id; string part_num_str; int part_num; if(!get_keyword_value(s3fscurl->url, "uploadId", upload_id)){ return NULL; } if(!get_keyword_value(s3fscurl->url, "partNumber", part_num_str)){ return NULL; } part_num = atoi(part_num_str.c_str()); if(s3fscurl->retry_count >= S3fsCurl::retries){ DPRN("Over retry count(%d) limit(%s:%d).", s3fscurl->retry_count, s3fscurl->path.c_str(), part_num); return NULL; } // duplicate request S3fsCurl* newcurl = new S3fsCurl(s3fscurl->IsUseAhbe()); newcurl->partdata.etaglist = s3fscurl->partdata.etaglist; newcurl->partdata.etagpos = s3fscurl->partdata.etagpos; newcurl->partdata.fd = s3fscurl->partdata.fd; newcurl->partdata.startpos = s3fscurl->b_partdata_startpos; newcurl->partdata.size = s3fscurl->b_partdata_size; newcurl->b_partdata_startpos = s3fscurl->b_partdata_startpos; newcurl->b_partdata_size = s3fscurl->b_partdata_size; newcurl->retry_count = s3fscurl->retry_count + 1; // setup new curl object if(0 != newcurl->UploadMultipartPostSetup(s3fscurl->path.c_str(), part_num, upload_id)){ DPRN("Could not duplicate curl object(%s:%d).", s3fscurl->path.c_str(), part_num); delete newcurl; return NULL; } return newcurl; } int S3fsCurl::ParallelMultipartUploadRequest(const char* tpath, headers_t& meta, int fd) { int result; string upload_id; struct stat st; int fd2; etaglist_t list; off_t remaining_bytes; S3fsCurl s3fscurl(true); FPRNNN("[tpath=%s][fd=%d]", SAFESTRPTR(tpath), fd); // duplicate fd if(-1 == (fd2 = dup(fd)) || 0 != lseek(fd2, 0, SEEK_SET)){ DPRN("Cloud not duplicate file discriptor(errno=%d)", errno); if(-1 != fd2){ close(fd2); } return -errno; } if(-1 == fstat(fd2, &st)){ DPRN("Invalid file discriptor(errno=%d)", errno); close(fd2); return -errno; } if(0 != (result = s3fscurl.PreMultipartPostRequest(tpath, meta, upload_id, false))){ close(fd2); return result; } s3fscurl.DestroyCurlHandle(); // cycle through open fd, pulling off 10MB chunks at a time for(remaining_bytes = st.st_size; 0 < remaining_bytes; ){ S3fsMultiCurl curlmulti; int para_cnt; off_t chunk; // Initialize S3fsMultiCurl curlmulti.SetSuccessCallback(S3fsCurl::UploadMultipartPostCallback); curlmulti.SetRetryCallback(S3fsCurl::UploadMultipartPostRetryCallback); // Loop for setup parallel upload(multipart) request. for(para_cnt = 0; para_cnt < S3fsCurl::max_parallel_cnt && 0 < remaining_bytes; para_cnt++, remaining_bytes -= chunk){ // chunk size chunk = remaining_bytes > S3fsCurl::multipart_size ? S3fsCurl::multipart_size : remaining_bytes; // s3fscurl sub object S3fsCurl* s3fscurl_para = new S3fsCurl(true); s3fscurl_para->partdata.fd = fd2; s3fscurl_para->partdata.startpos = st.st_size - remaining_bytes; s3fscurl_para->partdata.size = chunk; s3fscurl_para->b_partdata_startpos = s3fscurl_para->partdata.startpos; s3fscurl_para->b_partdata_size = s3fscurl_para->partdata.size; s3fscurl_para->partdata.add_etag_list(&list); // initiate upload part for parallel if(0 != (result = s3fscurl_para->UploadMultipartPostSetup(tpath, list.size(), upload_id))){ DPRN("failed uploading part setup(%d)", result); close(fd2); delete s3fscurl_para; return result; } // set into parallel object if(!curlmulti.SetS3fsCurlObject(s3fscurl_para)){ DPRN("Could not make curl object into multi curl(%s).", tpath); close(fd2); delete s3fscurl_para; return -1; } } // Multi request if(0 != (result = curlmulti.Request())){ DPRN("error occuered in multi request(errno=%d).", result); break; } // reinit for loop. curlmulti.Clear(); } close(fd2); if(0 != (result = s3fscurl.CompleteMultipartPostRequest(tpath, upload_id, list))){ return result; } return 0; } S3fsCurl* S3fsCurl::ParallelGetObjectRetryCallback(S3fsCurl* s3fscurl) { int result; if(!s3fscurl){ return NULL; } if(s3fscurl->retry_count >= S3fsCurl::retries){ DPRN("Over retry count(%d) limit(%s).", s3fscurl->retry_count, s3fscurl->path.c_str()); return NULL; } // duplicate request(setup new curl object) S3fsCurl* newcurl = new S3fsCurl(s3fscurl->IsUseAhbe()); if(0 != (result = newcurl->PreGetObjectRequest( s3fscurl->path.c_str(), s3fscurl->partdata.fd, s3fscurl->partdata.startpos, s3fscurl->partdata.size, s3fscurl->b_ssekey_md5))){ DPRN("failed downloading part setup(%d)", result); delete newcurl; return NULL;; } newcurl->retry_count = s3fscurl->retry_count + 1; return newcurl; } int S3fsCurl::ParallelGetObjectRequest(const char* tpath, int fd, off_t start, ssize_t size) { FPRNNN("[tpath=%s][fd=%d]", SAFESTRPTR(tpath), fd); string sseckeymd5(""); char* psseckeymd5; if(NULL != (psseckeymd5 = get_object_sseckey_md5(tpath))){ sseckeymd5 = psseckeymd5; free(psseckeymd5); } int result = 0; ssize_t remaining_bytes; // cycle through open fd, pulling off 10MB chunks at a time for(remaining_bytes = size; 0 < remaining_bytes; ){ S3fsMultiCurl curlmulti; int para_cnt; off_t chunk; // Initialize S3fsMultiCurl //curlmulti.SetSuccessCallback(NULL); // not need to set success callback curlmulti.SetRetryCallback(S3fsCurl::ParallelGetObjectRetryCallback); // Loop for setup parallel upload(multipart) request. for(para_cnt = 0; para_cnt < S3fsCurl::max_parallel_cnt && 0 < remaining_bytes; para_cnt++, remaining_bytes -= chunk){ // chunk size chunk = remaining_bytes > S3fsCurl::multipart_size ? S3fsCurl::multipart_size : remaining_bytes; // s3fscurl sub object S3fsCurl* s3fscurl_para = new S3fsCurl(); if(0 != (result = s3fscurl_para->PreGetObjectRequest(tpath, fd, (start + size - remaining_bytes), chunk, sseckeymd5))){ DPRN("failed downloading part setup(%d)", result); delete s3fscurl_para; return result; } // set into parallel object if(!curlmulti.SetS3fsCurlObject(s3fscurl_para)){ DPRN("Could not make curl object into multi curl(%s).", tpath); delete s3fscurl_para; return -1; } } // Multi request if(0 != (result = curlmulti.Request())){ DPRN("error occuered in multi request(errno=%d).", result); break; } // reinit for loop. curlmulti.Clear(); } return result; } bool S3fsCurl::ParseIAMCredentialResponse(const char* response, iamcredmap_t& keyval) { if(!response){ return false; } istringstream sscred(response); string oneline; keyval.clear(); while(getline(sscred, oneline, '\n')){ string::size_type pos; string key; string val; if(string::npos != (pos = oneline.find(IAMCRED_ACCESSKEYID))){ key = IAMCRED_ACCESSKEYID; }else if(string::npos != (pos = oneline.find(IAMCRED_SECRETACCESSKEY))){ key = IAMCRED_SECRETACCESSKEY; }else if(string::npos != (pos = oneline.find(IAMCRED_ACCESSTOKEN))){ key = IAMCRED_ACCESSTOKEN; }else if(string::npos != (pos = oneline.find(IAMCRED_EXPIRATION))){ key = IAMCRED_EXPIRATION; }else{ continue; } if(string::npos == (pos = oneline.find(':', pos + key.length()))){ continue; } if(string::npos == (pos = oneline.find('\"', pos))){ continue; } oneline = oneline.substr(pos + sizeof(char)); if(string::npos == (pos = oneline.find('\"'))){ continue; } val = oneline.substr(0, pos); keyval[key] = val; } return true; } bool S3fsCurl::SetIAMCredentials(const char* response) { FPRNINFO("IAM credential response = \"%s\"", response); iamcredmap_t keyval; if(!ParseIAMCredentialResponse(response, keyval)){ return false; } if(IAMCRED_KEYCOUNT != keyval.size()){ return false; } S3fsCurl::AWSAccessKeyId = keyval[string(IAMCRED_ACCESSKEYID)]; S3fsCurl::AWSSecretAccessKey = keyval[string(IAMCRED_SECRETACCESSKEY)]; S3fsCurl::AWSAccessToken = keyval[string(IAMCRED_ACCESSTOKEN)]; S3fsCurl::AWSAccessTokenExpire = cvtIAMExpireStringToTime(keyval[string(IAMCRED_EXPIRATION)].c_str()); return true; } bool S3fsCurl::CheckIAMCredentialUpdate(void) { if(0 == S3fsCurl::IAM_role.size()){ return true; } if(time(NULL) + IAM_EXPIRE_MERGIN <= S3fsCurl::AWSAccessTokenExpire){ return true; } // update S3fsCurl s3fscurl; if(0 != s3fscurl.GetIAMCredentials()){ return false; } return true; } //------------------------------------------------------------------- // Methods for S3fsCurl //------------------------------------------------------------------- S3fsCurl::S3fsCurl(bool ahbe) : hCurl(NULL), path(""), base_path(""), saved_path(""), url(""), requestHeaders(NULL), bodydata(NULL), headdata(NULL), LastResponseCode(-1), postdata(NULL), postdata_remaining(0), is_use_ahbe(ahbe), retry_count(0), b_infile(NULL), b_postdata(NULL), b_postdata_remaining(0), b_partdata_startpos(0), b_partdata_size(0), b_ssekey_pos(-1), b_ssekey_md5("") { type = REQTYPE_UNSET; } S3fsCurl::~S3fsCurl() { DestroyCurlHandle(); } bool S3fsCurl::ResetHandle(void) { curl_easy_reset(hCurl); curl_easy_setopt(hCurl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(hCurl, CURLOPT_FOLLOWLOCATION, true); curl_easy_setopt(hCurl, CURLOPT_CONNECTTIMEOUT, S3fsCurl::connect_timeout); curl_easy_setopt(hCurl, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(hCurl, CURLOPT_PROGRESSFUNCTION, S3fsCurl::CurlProgress); curl_easy_setopt(hCurl, CURLOPT_PROGRESSDATA, hCurl); // curl_easy_setopt(hCurl, CURLOPT_FORBID_REUSE, 1); if(type != REQTYPE_IAMCRED){ // REQTYPE_IAMCRED is always HTTP if(0 == S3fsCurl::ssl_verify_hostname){ curl_easy_setopt(hCurl, CURLOPT_SSL_VERIFYHOST, 0); } if(S3fsCurl::curl_ca_bundle.size() != 0){ curl_easy_setopt(hCurl, CURLOPT_CAINFO, S3fsCurl::curl_ca_bundle.c_str()); } } if((S3fsCurl::is_dns_cache || S3fsCurl::is_ssl_session_cache) && S3fsCurl::hCurlShare){ curl_easy_setopt(hCurl, CURLOPT_SHARE, S3fsCurl::hCurlShare); } if(S3fsCurl::is_verbose){ curl_easy_setopt(hCurl, CURLOPT_VERBOSE, true); } S3fsCurl::curl_times[hCurl] = time(0); S3fsCurl::curl_progress[hCurl] = progress_t(-1, -1); return true; } bool S3fsCurl::CreateCurlHandle(bool force) { pthread_mutex_lock(&S3fsCurl::curl_handles_lock); if(hCurl){ if(!force){ DPRN("already create handle."); return false; } if(!DestroyCurlHandle()){ DPRN("could not destroy handle."); return false; } DPRN("already has handle, so destroied it."); } if(NULL == (hCurl = curl_easy_init())){ DPRN("Failed to create handle."); return false; } type = REQTYPE_UNSET; ResetHandle(); pthread_mutex_unlock(&S3fsCurl::curl_handles_lock); return true; } bool S3fsCurl::DestroyCurlHandle(void) { if(!hCurl){ return false; } pthread_mutex_lock(&S3fsCurl::curl_handles_lock); S3fsCurl::curl_times.erase(hCurl); S3fsCurl::curl_progress.erase(hCurl); curl_easy_cleanup(hCurl); hCurl = NULL; ClearInternalData(); pthread_mutex_unlock(&S3fsCurl::curl_handles_lock); return true; } bool S3fsCurl::ClearInternalData(void) { if(hCurl){ return false; } type = REQTYPE_UNSET; path = ""; base_path = ""; saved_path= ""; url = ""; if(requestHeaders){ curl_slist_free_all(requestHeaders); requestHeaders = NULL; } responseHeaders.clear(); if(bodydata){ delete bodydata; bodydata = NULL; } if(headdata){ delete headdata; headdata = NULL; } LastResponseCode = -1; postdata = NULL; postdata_remaining = 0; retry_count = 0; b_infile = NULL; b_postdata = NULL; b_postdata_remaining = 0; b_partdata_startpos = 0; b_partdata_size = 0; partdata.clear(); S3FS_MALLOCTRIM(0); return true; } bool S3fsCurl::SetUseAhbe(bool ahbe) { bool old = is_use_ahbe; is_use_ahbe = ahbe; return old; } bool S3fsCurl::GetResponseCode(long& responseCode) { if(!hCurl){ return false; } responseCode = -1; if(CURLE_OK != curl_easy_getinfo(hCurl, CURLINFO_RESPONSE_CODE, &LastResponseCode)){ return false; } responseCode = LastResponseCode; return true; } // // Reset all options for retrying // bool S3fsCurl::RemakeHandle(void) { DPRNNN("Retry request. [type=%d][url=%s][path=%s]", type, url.c_str(), path.c_str()); if(REQTYPE_UNSET == type){ return false; } // rewind file struct stat st; if(b_infile){ rewind(b_infile); if(-1 == fstat(fileno(b_infile), &st)){ DPRNNN("Could not get file stat(fd=%d)", fileno(b_infile)); return false; } } // reinitialize internal data responseHeaders.clear(); if(bodydata){ bodydata->Clear(); } if(headdata){ headdata->Clear(); } LastResponseCode = -1; // count up(only use for multipart) retry_count++; // set from backup postdata = b_postdata; postdata_remaining = b_postdata_remaining; partdata.startpos = b_partdata_startpos; partdata.size = b_partdata_size; // reset handle ResetHandle(); // set options switch(type){ case REQTYPE_DELETE: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; case REQTYPE_HEAD: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_NOBODY, true); curl_easy_setopt(hCurl, CURLOPT_FILETIME, true); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); // responseHeaders curl_easy_setopt(hCurl, CURLOPT_HEADERDATA, (void*)&responseHeaders); curl_easy_setopt(hCurl, CURLOPT_HEADERFUNCTION, HeaderCallback); break; case REQTYPE_PUTHEAD: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_UPLOAD, true); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_INFILESIZE, 0); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; case REQTYPE_PUT: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_UPLOAD, true); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); if(b_infile){ curl_easy_setopt(hCurl, CURLOPT_INFILESIZE_LARGE, static_cast(st.st_size)); curl_easy_setopt(hCurl, CURLOPT_INFILE, b_infile); }else{ curl_easy_setopt(hCurl, CURLOPT_INFILESIZE, 0); } break; case REQTYPE_GET: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, S3fsCurl::DownloadWriteCallback); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)this); break; case REQTYPE_CHKBUCKET: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; case REQTYPE_LISTBUCKET: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; case REQTYPE_PREMULTIPOST: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_POST, true); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_POSTFIELDSIZE, 0); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; case REQTYPE_COMPLETEMULTIPOST: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); curl_easy_setopt(hCurl, CURLOPT_POST, true); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_POSTFIELDSIZE, static_cast(postdata_remaining)); curl_easy_setopt(hCurl, CURLOPT_READDATA, (void*)this); curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, S3fsCurl::ReadCallback); break; case REQTYPE_UPLOADMULTIPOST: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_UPLOAD, true); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HEADERDATA, (void*)headdata); curl_easy_setopt(hCurl, CURLOPT_HEADERFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_INFILESIZE_LARGE, static_cast(partdata.size)); curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, S3fsCurl::UploadReadCallback); curl_easy_setopt(hCurl, CURLOPT_READDATA, (void*)this); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; case REQTYPE_COPYMULTIPOST: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_UPLOAD, true); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HEADERDATA, (void*)headdata); curl_easy_setopt(hCurl, CURLOPT_HEADERFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_INFILESIZE, 0); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; case REQTYPE_MULTILIST: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; case REQTYPE_IAMCRED: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); break; case REQTYPE_ABORTMULTIUPLOAD: curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); break; default: DPRNNN("request type is unknown(%d)", type); return false; } return true; } // // returns curl return code // int S3fsCurl::RequestPerform(void) { if(debug){ char* ptr_url = NULL; curl_easy_getinfo(hCurl, CURLINFO_EFFECTIVE_URL , &ptr_url); DPRNNN("connecting to URL %s", SAFESTRPTR(ptr_url)); } // 1 attempt + retries... for(int retrycnt = S3fsCurl::retries; 0 < retrycnt; retrycnt--){ // Requests CURLcode curlCode = curl_easy_perform(hCurl); // Check result switch(curlCode){ case CURLE_OK: // Need to look at the HTTP response code if(0 != curl_easy_getinfo(hCurl, CURLINFO_RESPONSE_CODE, &LastResponseCode)){ DPRNNN("curl_easy_getinfo failed while trying to retrieve HTTP response code"); return -EIO; } if(400 > LastResponseCode){ DPRNNN("HTTP response code %ld", LastResponseCode); return 0; } if(500 <= LastResponseCode){ DPRNNN("###HTTP response=%ld", LastResponseCode); sleep(4); break; } // Service response codes which are >= 400 && < 500 switch(LastResponseCode){ case 400: DPRNNN("HTTP response code 400 was returned, returing EIO."); DPRNINFO("Body Text: %s", (bodydata ? bodydata->str() : "")); return -EIO; case 403: DPRNNN("HTTP response code 403 was returned, returning EPERM"); DPRNINFO("Body Text: %s", (bodydata ? bodydata->str() : "")); return -EPERM; case 404: DPRNNN("HTTP response code 404 was returned, returning ENOENT"); DPRNINFO("Body Text: %s", (bodydata ? bodydata->str() : "")); return -ENOENT; default: DPRNNN("HTTP response code = %ld, returning EIO", LastResponseCode); DPRNINFO("Body Text: %s", (bodydata ? bodydata->str() : "")); return -EIO; } break; case CURLE_WRITE_ERROR: DPRN("### CURLE_WRITE_ERROR"); sleep(2); break; case CURLE_OPERATION_TIMEDOUT: DPRN("### CURLE_OPERATION_TIMEDOUT"); sleep(2); break; case CURLE_COULDNT_RESOLVE_HOST: DPRN("### CURLE_COULDNT_RESOLVE_HOST"); sleep(2); break; case CURLE_COULDNT_CONNECT: DPRN("### CURLE_COULDNT_CONNECT"); sleep(4); break; case CURLE_GOT_NOTHING: DPRN("### CURLE_GOT_NOTHING"); sleep(4); break; case CURLE_ABORTED_BY_CALLBACK: DPRN("### CURLE_ABORTED_BY_CALLBACK"); sleep(4); S3fsCurl::curl_times[hCurl] = time(0); break; case CURLE_PARTIAL_FILE: DPRN("### CURLE_PARTIAL_FILE"); sleep(4); break; case CURLE_SEND_ERROR: DPRN("### CURLE_SEND_ERROR"); sleep(2); break; case CURLE_RECV_ERROR: DPRN("### CURLE_RECV_ERROR"); sleep(2); break; case CURLE_SSL_CONNECT_ERROR: DPRN("### CURLE_SSL_CONNECT_ERROR"); sleep(2); break; case CURLE_SSL_CACERT: DPRN("### CURLE_SSL_CACERT"); // try to locate cert, if successful, then set the // option and continue if(0 == S3fsCurl::curl_ca_bundle.size()){ if(!S3fsCurl::LocateBundle()){ DPRNCRIT("could not get CURL_CA_BUNDLE."); exit(EXIT_FAILURE); } break; // retry with CAINFO } DPRNCRIT("curlCode: %d msg: %s", curlCode, curl_easy_strerror(curlCode)); exit(EXIT_FAILURE); break; #ifdef CURLE_PEER_FAILED_VERIFICATION case CURLE_PEER_FAILED_VERIFICATION: DPRN("### CURLE_PEER_FAILED_VERIFICATION"); first_pos = bucket.find_first_of("."); if(first_pos != string::npos){ FPRNNN("curl returned a CURL_PEER_FAILED_VERIFICATION error"); FPRNNN("security issue found: buckets with periods in their name are incompatible with http"); FPRNNN("This check can be over-ridden by using the -o ssl_verify_hostname=0"); FPRNNN("The certificate will still be checked but the hostname will not be verified."); FPRNNN("A more secure method would be to use a bucket name without periods."); }else DPRNNN("my_curl_easy_perform: curlCode: %d -- %s", curlCode, curl_easy_strerror(curlCode)); } exit(EXIT_FAILURE); break; #endif // This should be invalid since curl option HTTP FAILONERROR is now off case CURLE_HTTP_RETURNED_ERROR: DPRN("### CURLE_HTTP_RETURNED_ERROR"); if(0 != curl_easy_getinfo(hCurl, CURLINFO_RESPONSE_CODE, &LastResponseCode)){ return -EIO; } DPRN("HTTP response code =%ld", LastResponseCode); // Let's try to retrieve the if(404 == LastResponseCode){ return -ENOENT; } if(500 > LastResponseCode){ return -EIO; } break; // Unknown CURL return code default: DPRNCRIT("###curlCode: %d msg: %s", curlCode, curl_easy_strerror(curlCode)); exit(EXIT_FAILURE); break; } DPRNNN("### retrying..."); if(!RemakeHandle()){ DPRNNN("Failed to reset handle and internal data for retrying."); return -EIO; } } DPRN("### giving up"); return -EIO; } // // Returns the Amazon AWS signature for the given parameters. // // @param method e.g., "GET" // @param content_type e.g., "application/x-directory" // @param date e.g., get_date_rfc850() // @param resource e.g., "/pub" // string S3fsCurl::CalcSignatureV2(string method, string strMD5, string content_type, string date, string resource) { string Signature; string StringToSign; if(0 < S3fsCurl::IAM_role.size()){ if(!S3fsCurl::CheckIAMCredentialUpdate()){ DPRN("Something error occurred in checking IAM credential."); return Signature; // returns empty string, then it occures error. } requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-security-token", S3fsCurl::AWSAccessToken.c_str()); } StringToSign += method + "\n"; StringToSign += strMD5 + "\n"; // md5 StringToSign += content_type + "\n"; StringToSign += date + "\n"; StringToSign += get_canonical_headers(requestHeaders, true); StringToSign += resource; const void* key = S3fsCurl::AWSSecretAccessKey.data(); int key_len = S3fsCurl::AWSSecretAccessKey.size(); const unsigned char* sdata = reinterpret_cast(StringToSign.data()); int sdata_len = StringToSign.size(); unsigned char* md = NULL; unsigned int md_len = 0;; s3fs_HMAC(key, key_len, sdata, sdata_len, &md, &md_len); char* base64; if(NULL == (base64 = s3fs_base64(md, md_len))){ free(md); return string(""); // ENOMEM } free(md); Signature = base64; free(base64); return Signature; } string S3fsCurl::CalcSignature(string method, string canonical_uri, string query_string, string strdate, string payload_hash, string date8601) { string Signature, StringCQ, StringToSign; string uriencode; if(0 < S3fsCurl::IAM_role.size()){ if(!S3fsCurl::CheckIAMCredentialUpdate()){ DPRN("Something error occurred in checking IAM credential."); return Signature; // returns empty string, then it occures error. } requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-security-token", S3fsCurl::AWSAccessToken.c_str()); } uriencode = urlEncode(canonical_uri); StringCQ = method + "\n"; if(0 == strcmp(method.c_str(),"HEAD") || 0 == strcmp(method.c_str(),"PUT") || 0 == strcmp(method.c_str(),"DELETE")){ StringCQ += uriencode + "\n" + query_string + "\n"; }else if (0 == strcmp(method.c_str(), "GET") && 0 == strcmp(uriencode.c_str(), "")) { StringCQ +="/\n\n"; }else if (0 == strcmp(method.c_str(), "GET") && 0 == strncmp(uriencode.c_str(), "/", 1)) { StringCQ += uriencode +"\n\n"; }else if (0 == strcmp(method.c_str(), "GET") && 0 != strncmp(uriencode.c_str(), "/", 1)) { StringCQ += "/\n" + urlEncode2(canonical_uri) +"\n"; }else if (0 == strcmp(method.c_str(), "POST")) { StringCQ += uriencode +"\n" + query_string +"\n"; } StringCQ += get_canonical_headers(requestHeaders) + "\n"; StringCQ += get_sorted_header_keys(requestHeaders) + "\n"; StringCQ += payload_hash; char kSecret[128]; unsigned char *kDate, *kRegion, *kService, *kSigning, *sRequest = NULL; unsigned int kDate_len,kRegion_len, kService_len, kSigning_len, sRequest_len = 0; char hexsRequest[64 + 1]; int kSecret_len = snprintf(kSecret, sizeof(kSecret), "AWS4%s", S3fsCurl::AWSSecretAccessKey.c_str()); unsigned int cnt; s3fs_HMAC256(kSecret, kSecret_len, reinterpret_cast(strdate.data()), strdate.size(), &kDate, &kDate_len); s3fs_HMAC256(kDate, kDate_len, reinterpret_cast(endpoint.c_str()), endpoint.size(), &kRegion, &kRegion_len); s3fs_HMAC256(kRegion, kRegion_len, reinterpret_cast("s3"), sizeof("s3") - 1, &kService, &kService_len); s3fs_HMAC256(kService, kService_len, reinterpret_cast("aws4_request"), sizeof("aws4_request") - 1, &kSigning, &kSigning_len); free(kDate); free(kRegion); free(kService); const unsigned char* cRequest = reinterpret_cast(StringCQ.c_str()); unsigned int cRequest_len = StringCQ.size(); s3fs_sha256(cRequest, cRequest_len, &sRequest, &sRequest_len); for(cnt = 0; cnt < sRequest_len; cnt++){ sprintf(&hexsRequest[cnt * 2], "%02x", sRequest[cnt]); } free(sRequest); StringToSign = "AWS4-HMAC-SHA256\n"; StringToSign += date8601 + "\n"; StringToSign += strdate + "/" + endpoint + "/s3/aws4_request\n"; StringToSign += hexsRequest; const unsigned char* cscope = reinterpret_cast(StringToSign.c_str()); unsigned int cscope_len = StringToSign.size(); unsigned char* md = NULL; unsigned int md_len = 0; char hexSig[64 + 1]; s3fs_HMAC256(kSigning, kSigning_len, cscope, cscope_len, &md, &md_len); for(cnt = 0; cnt < md_len; cnt++){ sprintf(&hexSig[cnt * 2], "%02x", md[cnt]); } free(kSigning); free(md); Signature = hexSig; return Signature; } // XML in BodyData has UploadId, Parse XML body for UploadId bool S3fsCurl::GetUploadId(string& upload_id) { bool result = false; if(!bodydata){ return result; } upload_id.clear(); xmlDocPtr doc; if(NULL == (doc = xmlReadMemory(bodydata->str(), bodydata->size(), "", NULL, 0))){ return result; } if(NULL == doc->children){ S3FS_XMLFREEDOC(doc); return result; } for(xmlNodePtr cur_node = doc->children->children; NULL != cur_node; cur_node = cur_node->next){ // For DEBUG // string cur_node_name(reinterpret_cast(cur_node->name)); // printf("cur_node_name: %s\n", cur_node_name.c_str()); if(XML_ELEMENT_NODE == cur_node->type){ string elementName = reinterpret_cast(cur_node->name); // For DEBUG // printf("elementName: %s\n", elementName.c_str()); if(cur_node->children){ if(XML_TEXT_NODE == cur_node->children->type){ if(elementName == "UploadId") { upload_id = reinterpret_cast(cur_node->children->content); result = true; break; } } } } } S3FS_XMLFREEDOC(doc); return result; } void S3fsCurl::insertV4Headers(const string &op, const string &path, const string &query_string, const string &payload_hash) { DPRNNN("computing signature [%s] [%s] [%s] [%s]", op.c_str(), path.c_str(), query_string.c_str(), payload_hash.c_str()); string strdate; string date8601; get_date_sigv3(strdate, date8601); string contentSHA256 = payload_hash.empty() ? empty_payload_hash : payload_hash; //string canonical_headers, signed_headers; requestHeaders = curl_slist_sort_insert(requestHeaders, "host", get_bucket_host().c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-content-sha256", contentSHA256.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-date", date8601.c_str()); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignature(op, path, query_string, strdate, contentSHA256, date8601); string auth = "AWS4-HMAC-SHA256 Credential=" + AWSAccessKeyId + "/" + strdate + "/" + endpoint + "/s3/aws4_request, SignedHeaders=" + get_sorted_header_keys(requestHeaders) + ", Signature=" + Signature; requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", auth.c_str()); } } int S3fsCurl::DeleteRequest(const char* tpath) { FPRNNN("[tpath=%s]", SAFESTRPTR(tpath)); if(!tpath){ return -1; } if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; responseHeaders.clear(); if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Type", NULL); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("DELETE", "", "", date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("DELETE", path, "", ""); } curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_DELETE; return RequestPerform(); } // // Get AccessKeyId/SecretAccessKey/AccessToken/Expiration by IAM role, // and Set these value to class valiable. // int S3fsCurl::GetIAMCredentials(void) { FPRNINFO("[IAM role=%s]", S3fsCurl::IAM_role.c_str()); if(0 == S3fsCurl::IAM_role.size()){ DPRN("IAM role name is empty."); return -EIO; } // at first set type for handle type = REQTYPE_IAMCRED; if(!CreateCurlHandle(true)){ return -EIO; } // url url = string(IAM_CRED_URL) + S3fsCurl::IAM_role; requestHeaders = NULL; responseHeaders.clear(); bodydata = new BodyData(); curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); int result = RequestPerform(); // analizing response if(0 == result && !S3fsCurl::SetIAMCredentials(bodydata->str())){ DPRN("Something error occurred, could not get IAM credential."); } delete bodydata; bodydata = NULL; return result; } // // If md5 is empty, build by first(current) sse key // bool S3fsCurl::AddSseKeyRequestHead(string& md5, bool is_copy) { if(!S3fsCurl::IsSseCustomMode()){ // Nothing to do return true; } string sseckey; if(S3fsCurl::GetSseKey(md5, sseckey)){ if(is_copy){ requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-copy-source-server-side-encryption-customer-algorithm", "AES256"); requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-copy-source-server-side-encryption-customer-key", sseckey.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-copy-source-server-side-encryption-customer-key-md5", md5.c_str()); }else{ requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-server-side-encryption-customer-algorithm", "AES256"); requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-server-side-encryption-customer-key", sseckey.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-server-side-encryption-customer-key-md5", md5.c_str()); } } return true; } // // tpath : target path for head request // bpath : saved into base_path // savedpath : saved into saved_path // ssekey_pos : -1 means "not use sse", 0 - X means "use sseckey" and "sseckey position". // sseckey position 0 is latest key. // bool S3fsCurl::PreHeadRequest(const char* tpath, const char* bpath, const char* savedpath, int ssekey_pos) { FPRNINFO("[tpath=%s][bpath=%s][save=%s]", SAFESTRPTR(tpath), SAFESTRPTR(bpath), SAFESTRPTR(savedpath)); if(!tpath){ return false; } if(!CreateCurlHandle(true)){ return false; } string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); // libcurl 7.17 does deep copy of url, deep copy "stable" url url = prepare_url(turl.c_str()); path = get_realpath(tpath); base_path = SAFESTRPTR(bpath); saved_path = SAFESTRPTR(savedpath); requestHeaders = NULL; responseHeaders.clear(); // requestHeaders if(0 <= ssekey_pos && S3fsCurl::IsSseCustomMode()){ string md5; if(!S3fsCurl::GetSseKeyMd5(ssekey_pos, md5) || !AddSseKeyRequestHead(md5, false)){ DPRN("Failed to set SSE-C headers for md5(%s).", md5.c_str()); } } b_ssekey_pos = ssekey_pos; if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Type", NULL); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("HEAD", "", "", date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("HEAD", path, "", ""); } curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_NOBODY, true); // HEAD curl_easy_setopt(hCurl, CURLOPT_FILETIME, true); // Last-Modified curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); // responseHeaders curl_easy_setopt(hCurl, CURLOPT_HEADERDATA, (void*)&responseHeaders); curl_easy_setopt(hCurl, CURLOPT_HEADERFUNCTION, HeaderCallback); type = REQTYPE_HEAD; return true; } int S3fsCurl::HeadRequest(const char* tpath, headers_t& meta) { int result = -1; FPRNNN("[tpath=%s]", SAFESTRPTR(tpath)); if(S3fsCurl::IsSseCustomMode()){ // SSE-C mode, check all sse-c key at first int pos; for(pos = 0; static_cast(pos) < S3fsCurl::sseckeys.size(); pos++){ if(0 != pos && !DestroyCurlHandle()){ return result; } if(!PreHeadRequest(tpath, NULL, NULL, pos)){ return result; } if(0 == (result = RequestPerform())){ break; } } if(S3fsCurl::sseckeys.size() <= static_cast(pos)){ // If sse-c mode is enable, s3fs fails to get head request for normal and sse object. // So try to get head without sse-c header. if(!DestroyCurlHandle() || !PreHeadRequest(tpath, NULL, NULL, -1) || 0 != (result = RequestPerform())){ return result; } } }else{ // Not sse-c mode if(!PreHeadRequest(tpath) || 0 != (result = RequestPerform())){ return result; } } // file exists in s3 // fixme: clean this up. meta.clear(); for(headers_t::iterator iter = responseHeaders.begin(); iter != responseHeaders.end(); ++iter){ string key = lower(iter->first); string value = iter->second; if(key == "content-type"){ meta[iter->first] = value; }else if(key == "content-length"){ meta[iter->first] = value; }else if(key == "etag"){ meta[iter->first] = value; }else if(key == "last-modified"){ meta[iter->first] = value; }else if(key.substr(0, 5) == "x-amz"){ meta[key] = value; // key is lower case for "x-amz" } } return 0; } int S3fsCurl::PutHeadRequest(const char* tpath, headers_t& meta, bool is_copy) { FPRNNN("[tpath=%s]", SAFESTRPTR(tpath)); if(!tpath){ return -1; } if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; responseHeaders.clear(); bodydata = new BodyData(); // Make request headers string ContentType; for(headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter){ string key = lower(iter->first); string value = iter->second; if(key == "content-type"){ ContentType = value; requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); }else if(key.substr(0, 9) == "x-amz-acl"){ // not set value, but after set it. }else if(key.substr(0, 10) == "x-amz-meta"){ requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); }else if(key == "x-amz-copy-source"){ requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); }else if(key == "x-amz-server-side-encryption"){ // skip this header, because this header is specified after logic. }else if(key == "x-amz-server-side-encryption-customer-algorithm"){ // skip this header, because this header is specified with "x-amz-...-customer-key-md5". }else if(is_copy && key == "x-amz-server-side-encryption-customer-key-md5"){ // Only copy mode. if(!AddSseKeyRequestHead(value, is_copy)){ DPRNNN("Failed to insert sse(-c) header."); } } } // "x-amz-acl", rrs, sse requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-acl", S3fsCurl::default_acl.c_str()); if(S3fsCurl::is_use_rrs){ requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-storage-class", "REDUCED_REDUNDANCY"); } if(S3fsCurl::is_use_sse){ requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-server-side-encryption", "AES256"); }else if(S3fsCurl::IsSseCustomMode()){ string md5; if(!AddSseKeyRequestHead(md5, false)){ DPRNNN("Failed to insert sse(-c) header."); } } if(is_use_ahbe){ // set additional header by ahbe conf requestHeaders = AdditionalHeader::get()->AddHeader(requestHeaders, tpath); } if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("PUT", "", ContentType, date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("PUT", path, "", ""); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_UPLOAD, true); // HTTP PUT curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_INFILESIZE, 0); // Content-Length curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_PUTHEAD; DPRNNN("copying... [path=%s]", tpath); int result = RequestPerform(); delete bodydata; bodydata = NULL; return result; } int S3fsCurl::PutRequest(const char* tpath, headers_t& meta, int fd) { struct stat st; FILE* file = NULL; int fd2; FPRNNN("[tpath=%s]", SAFESTRPTR(tpath)); if(!tpath){ return -1; } if(-1 != fd){ // duplicate fd if(-1 == (fd2 = dup(fd)) || -1 == fstat(fd2, &st) || 0 != lseek(fd2, 0, SEEK_SET) || NULL == (file = fdopen(fd2, "rb"))){ DPRN("Could not duplicate file discriptor(errno=%d)", errno); return -errno; } b_infile = file; }else{ // This case is creating zero byte obejct.(calling by create_file_object()) DPRNNN("create zero byte file object."); } if(!CreateCurlHandle(true)){ if(file){ fclose(file); } return -1; } string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; responseHeaders.clear(); bodydata = new BodyData(); // Make request headers string strMD5; if(-1 != fd && S3fsCurl::is_content_md5){ strMD5 = s3fs_get_content_md5(fd); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-MD5", strMD5.c_str()); } string ContentType; for(headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter){ string key = lower(iter->first); string value = iter->second; if(key == "content-type"){ ContentType = value; requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); }else if(key.substr(0, 9) == "x-amz-acl"){ // not set value, but after set it. }else if(key.substr(0, 10) == "x-amz-meta"){ requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); }else if(key == "x-amz-server-side-encryption"){ // skip this header, because this header is specified after logic. }else if(key == "x-amz-server-side-encryption-customer-algorithm"){ // skip this header, because this header is specified after logic. }else if(key == "x-amz-server-side-encryption-customer-key-md5"){ // skip this header, because this header is specified after logic. } } // "x-amz-acl", rrs, sse requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-acl", S3fsCurl::default_acl.c_str()); if(S3fsCurl::is_use_rrs){ requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-storage-class", "REDUCED_REDUNDANCY"); } if(S3fsCurl::is_use_sse){ requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-server-side-encryption", "AES256"); }else if(S3fsCurl::IsSseCustomMode()){ string md5; if(!AddSseKeyRequestHead(md5, false)){ DPRNNN("Failed to insert sse(-c) header."); } } if(is_use_ahbe){ // set additional header by ahbe conf requestHeaders = AdditionalHeader::get()->AddHeader(requestHeaders, tpath); } if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("PUT", strMD5, ContentType, date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ string payload_hash = s3fs_sha256sum(fd, 0, -1); insertV4Headers("PUT", path, "", payload_hash); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_UPLOAD, true); // HTTP PUT curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); if(file){ curl_easy_setopt(hCurl, CURLOPT_INFILESIZE_LARGE, static_cast(st.st_size)); // Content-Length curl_easy_setopt(hCurl, CURLOPT_INFILE, file); }else{ curl_easy_setopt(hCurl, CURLOPT_INFILESIZE, 0); // Content-Length: 0 } type = REQTYPE_PUT; DPRNNN("uploading... [path=%s][fd=%d][size=%jd]", tpath, fd, (intmax_t)(-1 != fd ? st.st_size : 0)); int result = RequestPerform(); delete bodydata; bodydata = NULL; if(file){ fclose(file); } return result; } int S3fsCurl::PreGetObjectRequest(const char* tpath, int fd, off_t start, ssize_t size, string& ssekeymd5) { FPRNNN("[tpath=%s][start=%jd][size=%zd]", SAFESTRPTR(tpath), (intmax_t)start, size); if(!tpath || -1 == fd || 0 > start || 0 >= size){ return -1; } if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; responseHeaders.clear(); if(-1 != start && -1 != size){ string range = "bytes="; range += str(start); range += "-"; range += str(start + size - 1); requestHeaders = curl_slist_sort_insert(requestHeaders, "Range", range.c_str()); } if(0 < ssekeymd5.length()){ if(!AddSseKeyRequestHead(ssekeymd5, false)){ DPRNNN("Failed to insert sse(-c) header."); } } if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Type", NULL); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("GET", "", "", date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("GET", path, "", ""); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, S3fsCurl::DownloadWriteCallback); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)this); // set info for callback func. // (use only fd, startpos and size, other member is not used.) partdata.clear(); partdata.fd = fd; partdata.startpos = start; partdata.size = size; b_partdata_startpos = start; b_partdata_size = size; b_ssekey_md5 = ssekeymd5; type = REQTYPE_GET; return 0; } int S3fsCurl::GetObjectRequest(const char* tpath, int fd, off_t start, ssize_t size) { int result; FPRNNN("[tpath=%s][start=%jd][size=%zd]", SAFESTRPTR(tpath), (intmax_t)start, size); if(!tpath){ return -1; } string sseckeymd5(""); char* psseckeymd5; if(NULL != (psseckeymd5 = get_object_sseckey_md5(tpath))){ sseckeymd5 = psseckeymd5; free(psseckeymd5); } if(0 != (result = PreGetObjectRequest(tpath, fd, start, size, sseckeymd5))){ return result; } DPRNNN("downloading... [path=%s][fd=%d]", tpath, fd); result = RequestPerform(); partdata.clear(); return result; } int S3fsCurl::CheckBucket(void) { FPRNNN("check a bucket."); if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; MakeUrlResource(get_realpath("/").c_str(), resource, turl); url = prepare_url(turl.c_str()); path = get_realpath("/"); requestHeaders = NULL; responseHeaders.clear(); bodydata = new BodyData(); if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("GET", "", "", date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("GET", path, "", ""); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_CHKBUCKET; int result = RequestPerform(); if (result != 0) { DPRN("Check bucket failed, S3 response: %s", (bodydata ? bodydata->str() : "")); } return result; } int S3fsCurl::ListBucketRequest(const char* tpath, const char* query) { FPRNNN("[tpath=%s]", SAFESTRPTR(tpath)); if(!tpath){ return -1; } if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; MakeUrlResource("", resource, turl); // NOTICE: path is "". if(query){ turl += "?"; turl += query; } url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; responseHeaders.clear(); bodydata = new BodyData(); if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Type", NULL); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("GET", "", "", date, (resource + "/")); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("GET", query, "", ""); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_LISTBUCKET; return RequestPerform(); } // // Initialize multipart upload // // Example : // POST /example-object?uploads HTTP/1.1 // Host: example-bucket.s3.amazonaws.com // Date: Mon, 1 Nov 2010 20:34:56 GMT // Authorization: AWS VGhpcyBtZXNzYWdlIHNpZ25lZCBieSBlbHZpbmc= // int S3fsCurl::PreMultipartPostRequest(const char* tpath, headers_t& meta, string& upload_id, bool is_copy) { FPRNNN("[tpath=%s]", SAFESTRPTR(tpath)); if(!tpath){ return -1; } if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); string query_string = "uploads"; if(S3fsCurl::is_sigv4){ query_string += "="; } turl += "?" + query_string; resource += "?" + query_string; url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; bodydata = new BodyData(); responseHeaders.clear(); string contype = S3fsCurl::LookupMimeType(string(tpath)); for(headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter){ string key = lower(iter->first); string value = iter->second; if(key.substr(0, 9) == "x-amz-acl"){ // not set value, but after set it. }else if(key.substr(0, 10) == "x-amz-meta"){ requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); }else if(key == "x-amz-server-side-encryption"){ // skip this header, because this header is specified after logic. }else if(key == "x-amz-server-side-encryption-customer-algorithm"){ // skip this header, because this header is specified with "x-amz-...-customer-key-md5". }else if(is_copy && key == "x-amz-server-side-encryption-customer-key-md5"){ // Only copy mode. if(!AddSseKeyRequestHead(value, is_copy)){ DPRNNN("Failed to insert sse(-c) header."); } } } // "x-amz-acl", rrs, sse requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-acl", S3fsCurl::default_acl.c_str()); if(S3fsCurl::is_use_rrs){ requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-storage-class", "REDUCED_REDUNDANCY"); } if(S3fsCurl::is_use_sse){ requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-server-side-encryption", "AES256"); }else if(S3fsCurl::IsSseCustomMode()){ string md5; if(!AddSseKeyRequestHead(md5, false)){ DPRNNN("Failed to insert sse(-c) header."); } } if(is_use_ahbe){ // set additional header by ahbe conf requestHeaders = AdditionalHeader::get()->AddHeader(requestHeaders, tpath); } if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Accept", NULL); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Length", NULL); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Type", contype.c_str()); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("POST", "", contype, date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ requestHeaders = curl_slist_sort_insert(requestHeaders, "Accept", NULL); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Length", NULL); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Type", contype.c_str()); insertV4Headers("POST", path, query_string, ""); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_POST, true); // POST curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_POSTFIELDSIZE, 0); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_PREMULTIPOST; // request int result; if(0 != (result = RequestPerform())){ delete bodydata; bodydata = NULL; return result; } // Parse XML body for UploadId if(!S3fsCurl::GetUploadId(upload_id)){ delete bodydata; bodydata = NULL; return -1; } delete bodydata; bodydata = NULL; return 0; } int S3fsCurl::CompleteMultipartPostRequest(const char* tpath, string& upload_id, etaglist_t& parts) { FPRNNN("[tpath=%s][parts=%zu]", SAFESTRPTR(tpath), parts.size()); if(!tpath){ return -1; } // make contents string postContent; postContent += "\n"; for(int cnt = 0; cnt < (int)parts.size(); cnt++){ if(0 == parts[cnt].length()){ DPRN("%d file part is not finished uploading.", cnt + 1); return -1; } postContent += "\n"; postContent += " " + IntToStr(cnt + 1) + "\n"; postContent += " \"" + parts[cnt] + "\"\n"; postContent += "\n"; } postContent += "\n"; // set postdata postdata = reinterpret_cast(postContent.c_str()); b_postdata = postdata; postdata_remaining = postContent.size(); // without null b_postdata_remaining = postdata_remaining; if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); string query_string = "uploadId=" + upload_id; turl += "?" + query_string; resource += "?" + query_string; url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; bodydata = new BodyData(); responseHeaders.clear(); string contype = S3fsCurl::LookupMimeType(string(tpath)); if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Accept", NULL); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Type", contype.c_str()); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("POST", "", contype, date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ string payload_hash; const unsigned char* cRequest = reinterpret_cast(postContent.c_str()); unsigned int cRequest_len = postContent.size(); unsigned char* sRequest = NULL; unsigned int sRequest_len = 0; char hexsRequest[64 + 1]; unsigned int cnt; s3fs_sha256(cRequest, cRequest_len, &sRequest, &sRequest_len); for(cnt = 0; cnt < sRequest_len; cnt++){ sprintf(&hexsRequest[cnt * 2], "%02x", sRequest[cnt]); } payload_hash.assign(hexsRequest, &hexsRequest[sRequest_len * 2]); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", get_date_rfc850().c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Accept", NULL); requestHeaders = curl_slist_sort_insert(requestHeaders, "Content-Type", contype.c_str()); insertV4Headers("POST", path, query_string, payload_hash); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); curl_easy_setopt(hCurl, CURLOPT_POST, true); // POST curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_POSTFIELDSIZE, static_cast(postdata_remaining)); curl_easy_setopt(hCurl, CURLOPT_READDATA, (void*)this); curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, S3fsCurl::ReadCallback); type = REQTYPE_COMPLETEMULTIPOST; // request int result = RequestPerform(); delete bodydata; bodydata = NULL; postdata = NULL; return result; } int S3fsCurl::MultipartListRequest(string& body) { FPRNNN("list request(multipart)"); if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; path = get_realpath("/"); MakeUrlResource(path.c_str(), resource, turl); turl += "?uploads"; resource += "?uploads"; url = prepare_url(turl.c_str()); requestHeaders = NULL; responseHeaders.clear(); bodydata = new BodyData(); if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Accept", NULL); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("GET", "", "", date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("GET", path, "", ""); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_MULTILIST; int result; if(0 == (result = RequestPerform()) && 0 < bodydata->size()){ body = bodydata->str(); }else{ body = ""; } delete bodydata; bodydata = NULL; return result; } int S3fsCurl::AbortMultipartUpload(const char* tpath, string& upload_id) { FPRNNN("[tpath=%s]", SAFESTRPTR(tpath)); if(!tpath){ return -1; } if(!CreateCurlHandle(true)){ return -1; } string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); turl += "?uploadId=" + upload_id; resource += "?uploadId=" + upload_id; url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; responseHeaders.clear(); if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("DELETE", "", "", date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("DELETE", path, "", ""); } curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_ABORTMULTIUPLOAD; return RequestPerform(); } // // PUT /ObjectName?partNumber=PartNumber&uploadId=UploadId HTTP/1.1 // Host: BucketName.s3.amazonaws.com // Date: date // Content-Length: Size // Authorization: Signature // // PUT /my-movie.m2ts?partNumber=1&uploadId=VCVsb2FkIElEIGZvciBlbZZpbmcncyBteS1tb3ZpZS5tMnRzIHVwbG9hZR HTTP/1.1 // Host: example-bucket.s3.amazonaws.com // Date: Mon, 1 Nov 2010 20:34:56 GMT // Content-Length: 10485760 // Content-MD5: pUNXr/BjKK5G2UKvaRRrOA== // Authorization: AWS VGhpcyBtZXNzYWdlIHNpZ25lZGGieSRlbHZpbmc= // int S3fsCurl::UploadMultipartPostSetup(const char* tpath, int part_num, string& upload_id) { FPRNNN("[tpath=%s][start=%jd][size=%zd][part=%d]", SAFESTRPTR(tpath), (intmax_t)(partdata.startpos), partdata.size, part_num); if(-1 == partdata.fd || -1 == partdata.startpos || -1 == partdata.size){ return -1; } // make md5 and file pointer partdata.etag = s3fs_md5sum(partdata.fd, partdata.startpos, partdata.size); if(partdata.etag.empty()){ DPRN("Could not make md5 for file(part %d)", part_num); return -1; } // create handle if(!CreateCurlHandle(true)){ return -1; } // make request string request_uri = "partNumber=" + IntToStr(part_num) + "&uploadId=" + upload_id; string urlargs = "?" + request_uri; string resource; string turl; MakeUrlResource(get_realpath(tpath).c_str(), resource, turl); resource += urlargs; turl += urlargs; url = prepare_url(turl.c_str()); path = get_realpath(tpath); requestHeaders = NULL; bodydata = new BodyData(); headdata = new BodyData(); responseHeaders.clear(); if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); requestHeaders = curl_slist_sort_insert(requestHeaders, "Accept", NULL); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("PUT", "", "", date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ string payload_hash = s3fs_sha256sum(partdata.fd, partdata.startpos, partdata.size); insertV4Headers("PUT", path, request_uri, payload_hash); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_UPLOAD, true); // HTTP PUT curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HEADERDATA, (void*)headdata); curl_easy_setopt(hCurl, CURLOPT_HEADERFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_INFILESIZE_LARGE, static_cast(partdata.size)); // Content-Length curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, S3fsCurl::UploadReadCallback); curl_easy_setopt(hCurl, CURLOPT_READDATA, (void*)this); curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_UPLOADMULTIPOST; return 0; } int S3fsCurl::UploadMultipartPostRequest(const char* tpath, int part_num, string& upload_id) { int result; FPRNNN("[tpath=%s][start=%jd][size=%zd][part=%d]", SAFESTRPTR(tpath), (intmax_t)(partdata.startpos), partdata.size, part_num); // setup if(0 != (result = S3fsCurl::UploadMultipartPostSetup(tpath, part_num, upload_id))){ return result; } // request if(0 == (result = RequestPerform())){ // check etag if(NULL != strstr(headdata->str(), partdata.etag.c_str())){ partdata.uploaded = true; }else{ result = -1; } } // closing delete bodydata; bodydata = NULL; delete headdata; headdata = NULL; return result; } int S3fsCurl::CopyMultipartPostRequest(const char* from, const char* to, int part_num, string& upload_id, headers_t& meta) { FPRNNN("[from=%s][to=%s][part=%d]", SAFESTRPTR(from), SAFESTRPTR(to), part_num); if(!from || !to){ return -1; } if(!CreateCurlHandle(true)){ return -1; } string urlargs = "?partNumber=" + IntToStr(part_num) + "&uploadId=" + upload_id; string resource; string turl; MakeUrlResource(get_realpath(to).c_str(), resource, turl); resource += urlargs; turl += urlargs; url = prepare_url(turl.c_str()); path = get_realpath(to); requestHeaders = NULL; responseHeaders.clear(); bodydata = new BodyData(); headdata = new BodyData(); // Make request headers string ContentType; for(headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter){ string key = lower(iter->first); string value = iter->second; if(key == "content-type"){ ContentType = value; requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); }else if(key == "x-amz-copy-source"){ requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); }else if(key == "x-amz-copy-source-range"){ requestHeaders = curl_slist_sort_insert(requestHeaders, iter->first.c_str(), value.c_str()); } // NOTICE: x-amz-acl, x-amz-server-side-encryption is not set! } if(!S3fsCurl::is_sigv4){ string date = get_date_rfc850(); requestHeaders = curl_slist_sort_insert(requestHeaders, "Date", date.c_str()); if(!S3fsCurl::IsPublicBucket()){ string Signature = CalcSignatureV2("PUT", "", ContentType, date, resource); requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", string("AWS " + AWSAccessKeyId + ":" + Signature).c_str()); } }else{ insertV4Headers("PUT", path, "", ""); } // setopt curl_easy_setopt(hCurl, CURLOPT_URL, url.c_str()); curl_easy_setopt(hCurl, CURLOPT_UPLOAD, true); // HTTP PUT curl_easy_setopt(hCurl, CURLOPT_WRITEDATA, (void*)bodydata); curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_HEADERDATA, (void*)headdata); curl_easy_setopt(hCurl, CURLOPT_HEADERFUNCTION, WriteMemoryCallback); curl_easy_setopt(hCurl, CURLOPT_INFILESIZE, 0); // Content-Length curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, requestHeaders); type = REQTYPE_COPYMULTIPOST; // request DPRNNN("copying... [from=%s][to=%s][part=%d]", from, to, part_num); int result = RequestPerform(); if(0 == result){ const char* start_etag= strstr(bodydata->str(), "ETag"); const char* end_etag = strstr(bodydata->str(), "/ETag>"); partdata.etag.assign((start_etag + 11), (size_t)(end_etag - (start_etag + 11) - 7)); partdata.uploaded = true; } delete bodydata; bodydata = NULL; delete headdata; headdata = NULL; return result; } int S3fsCurl::MultipartHeadRequest(const char* tpath, off_t size, headers_t& meta, bool is_copy) { int result; string upload_id; off_t chunk; off_t bytes_remaining; etaglist_t list; stringstream strrange; FPRNNN("[tpath=%s]", SAFESTRPTR(tpath)); if(0 != (result = PreMultipartPostRequest(tpath, meta, upload_id, is_copy))){ return result; } DestroyCurlHandle(); for(bytes_remaining = size, chunk = 0; 0 < bytes_remaining; bytes_remaining -= chunk){ chunk = bytes_remaining > MAX_MULTI_COPY_SOURCE_SIZE ? MAX_MULTI_COPY_SOURCE_SIZE : bytes_remaining; strrange << "bytes=" << (size - bytes_remaining) << "-" << (size - bytes_remaining + chunk - 1); meta["x-amz-copy-source-range"] = strrange.str(); strrange.str(""); strrange.clear(stringstream::goodbit); if(0 != (result = CopyMultipartPostRequest(tpath, tpath, (list.size() + 1), upload_id, meta))){ return result; } list.push_back(partdata.etag); DestroyCurlHandle(); } if(0 != (result = CompleteMultipartPostRequest(tpath, upload_id, list))){ return result; } return 0; } int S3fsCurl::MultipartUploadRequest(const char* tpath, headers_t& meta, int fd, bool is_copy) { int result; string upload_id; struct stat st; int fd2; etaglist_t list; off_t remaining_bytes; off_t chunk; FPRNNN("[tpath=%s][fd=%d]", SAFESTRPTR(tpath), fd); // duplicate fd if(-1 == (fd2 = dup(fd)) || 0 != lseek(fd2, 0, SEEK_SET)){ DPRN("Cloud not duplicate file discriptor(errno=%d)", errno); if(-1 != fd2){ close(fd2); } return -errno; } if(-1 == fstat(fd2, &st)){ DPRN("Invalid file discriptor(errno=%d)", errno); close(fd2); return -errno; } if(0 != (result = PreMultipartPostRequest(tpath, meta, upload_id, is_copy))){ close(fd2); return result; } DestroyCurlHandle(); // cycle through open fd, pulling off 10MB chunks at a time for(remaining_bytes = st.st_size; 0 < remaining_bytes; remaining_bytes -= chunk){ // chunk size chunk = remaining_bytes > S3fsCurl::multipart_size ? S3fsCurl::multipart_size : remaining_bytes; // set partdata.fd = fd2; partdata.startpos = st.st_size - remaining_bytes; partdata.size = chunk; b_partdata_startpos = partdata.startpos; b_partdata_size = partdata.size; // upload part if(0 != (result = UploadMultipartPostRequest(tpath, (list.size() + 1), upload_id))){ DPRN("failed uploading part(%d)", result); close(fd2); return result; } list.push_back(partdata.etag); DestroyCurlHandle(); } close(fd2); if(0 != (result = CompleteMultipartPostRequest(tpath, upload_id, list))){ return result; } return 0; } int S3fsCurl::MultipartRenameRequest(const char* from, const char* to, headers_t& meta, off_t size) { int result; string upload_id; off_t chunk; off_t bytes_remaining; etaglist_t list; stringstream strrange; FPRNNN("[from=%s][to=%s]", SAFESTRPTR(from), SAFESTRPTR(to)); string srcresource; string srcurl; MakeUrlResource(get_realpath(from).c_str(), srcresource, srcurl); meta["Content-Type"] = S3fsCurl::LookupMimeType(string(to)); meta["x-amz-copy-source"] = srcresource; if(0 != (result = PreMultipartPostRequest(to, meta, upload_id, true))){ return result; } DestroyCurlHandle(); for(bytes_remaining = size, chunk = 0; 0 < bytes_remaining; bytes_remaining -= chunk){ chunk = bytes_remaining > MAX_MULTI_COPY_SOURCE_SIZE ? MAX_MULTI_COPY_SOURCE_SIZE : bytes_remaining; strrange << "bytes=" << (size - bytes_remaining) << "-" << (size - bytes_remaining + chunk - 1); meta["x-amz-copy-source-range"] = strrange.str(); strrange.str(""); strrange.clear(stringstream::goodbit); if(0 != (result = CopyMultipartPostRequest(from, to, (list.size() + 1), upload_id, meta))){ return result; } list.push_back(partdata.etag); DestroyCurlHandle(); } if(0 != (result = CompleteMultipartPostRequest(to, upload_id, list))){ return result; } return 0; } //------------------------------------------------------------------- // Class S3fsMultiCurl //------------------------------------------------------------------- #define MAX_MULTI_HEADREQ 20 // default: max request count in readdir curl_multi. //------------------------------------------------------------------- // Class method for S3fsMultiCurl //------------------------------------------------------------------- int S3fsMultiCurl::max_multireq = MAX_MULTI_HEADREQ; int S3fsMultiCurl::SetMaxMultiRequest(int max) { int old = S3fsMultiCurl::max_multireq; S3fsMultiCurl::max_multireq= max; return old; } //------------------------------------------------------------------- // method for S3fsMultiCurl //------------------------------------------------------------------- S3fsMultiCurl::S3fsMultiCurl() : hMulti(NULL), SuccessCallback(NULL), RetryCallback(NULL) { } S3fsMultiCurl::~S3fsMultiCurl() { Clear(); } bool S3fsMultiCurl::ClearEx(bool is_all) { s3fscurlmap_t::iterator iter; for(iter = cMap_req.begin(); iter != cMap_req.end(); cMap_req.erase(iter++)){ CURL* hCurl = (*iter).first; S3fsCurl* s3fscurl = (*iter).second; if(hMulti && hCurl){ curl_multi_remove_handle(hMulti, hCurl); } if(s3fscurl){ s3fscurl->DestroyCurlHandle(); delete s3fscurl; // with destroy curl handle. } } if(hMulti){ curl_multi_cleanup(hMulti); hMulti = NULL; } if(is_all){ for(iter = cMap_all.begin(); iter != cMap_all.end(); cMap_all.erase(iter++)){ S3fsCurl* s3fscurl = (*iter).second; s3fscurl->DestroyCurlHandle(); delete s3fscurl; } } S3FS_MALLOCTRIM(0); return true; } S3fsMultiSuccessCallback S3fsMultiCurl::SetSuccessCallback(S3fsMultiSuccessCallback function) { S3fsMultiSuccessCallback old = SuccessCallback; SuccessCallback = function; return old; } S3fsMultiRetryCallback S3fsMultiCurl::SetRetryCallback(S3fsMultiRetryCallback function) { S3fsMultiRetryCallback old = RetryCallback; RetryCallback = function; return old; } bool S3fsMultiCurl::SetS3fsCurlObject(S3fsCurl* s3fscurl) { if(hMulti){ DPRN("Internal error: hMulti is not null"); return false; } if(!s3fscurl){ return false; } if(cMap_all.end() != cMap_all.find(s3fscurl->hCurl)){ return false; } cMap_all[s3fscurl->hCurl] = s3fscurl; return true; } int S3fsMultiCurl::MultiPerform(void) { CURLMcode curlm_code; int still_running; if(!hMulti){ return -1; } // Send multi request. do{ // Start making requests and check running. still_running = 0; do { curlm_code = curl_multi_perform(hMulti, &still_running); } while(curlm_code == CURLM_CALL_MULTI_PERFORM); if(curlm_code != CURLM_OK) { DPRNNN("curl_multi_perform code: %d msg: %s", curlm_code, curl_multi_strerror(curlm_code)); } // Set timer when still running if(still_running) { long milliseconds; fd_set r_fd; fd_set w_fd; fd_set e_fd; FD_ZERO(&r_fd); FD_ZERO(&w_fd); FD_ZERO(&e_fd); if(CURLM_OK != (curlm_code = curl_multi_timeout(hMulti, &milliseconds))){ DPRNNN("curl_multi_timeout code: %d msg: %s", curlm_code, curl_multi_strerror(curlm_code)); } if(milliseconds < 0){ milliseconds = 50; } if(milliseconds > 0) { int max_fd; struct timeval timeout; timeout.tv_sec = 1000 * milliseconds / 1000000; timeout.tv_usec = 1000 * milliseconds % 1000000; if(CURLM_OK != (curlm_code = curl_multi_fdset(hMulti, &r_fd, &w_fd, &e_fd, &max_fd))){ DPRN("curl_multi_fdset code: %d msg: %s", curlm_code, curl_multi_strerror(curlm_code)); return -EIO; } if(-1 == select(max_fd + 1, &r_fd, &w_fd, &e_fd, &timeout)){ DPRN("failed select - errno(%d)", errno); return -errno; } } } }while(still_running); return 0; } int S3fsMultiCurl::MultiRead(void) { CURLMsg* msg; int remaining_messages; CURL* hCurl = NULL; S3fsCurl* s3fscurl = NULL; S3fsCurl* retrycurl= NULL; while(NULL != (msg = curl_multi_info_read(hMulti, &remaining_messages))){ if(CURLMSG_DONE != msg->msg){ DPRN("curl_multi_info_read code: %d", msg->msg); return -EIO; } hCurl = msg->easy_handle; if(cMap_req.end() != cMap_req.find(hCurl)){ s3fscurl = cMap_req[hCurl]; }else{ s3fscurl = NULL; } retrycurl= NULL; if(s3fscurl){ bool isRetry = false; if(CURLE_OK == msg->data.result){ long responseCode = -1; if(s3fscurl->GetResponseCode(responseCode)){ if(400 > responseCode){ // add into stat cache if(SuccessCallback && !SuccessCallback(s3fscurl)){ DPRN("error from callback function(%s).", s3fscurl->url.c_str()); } }else if(400 == responseCode){ // as possibly in multipart DPRN("failed a request(%ld: %s)", responseCode, s3fscurl->url.c_str()); isRetry = true; }else if(404 == responseCode){ // not found DPRN("failed a request(%ld: %s)", responseCode, s3fscurl->url.c_str()); }else if(500 == responseCode){ // case of all other result, do retry.(11/13/2013) // because it was found that s3fs got 500 error from S3, but could success // to retry it. DPRN("failed a request(%ld: %s)", responseCode, s3fscurl->url.c_str()); isRetry = true; }else{ // Retry in other case. DPRN("failed a request(%ld: %s)", responseCode, s3fscurl->url.c_str()); isRetry = true; } }else{ DPRN("failed a request(Unknown respons code: %s)", s3fscurl->url.c_str()); } }else{ DPRN("failed to read(remaining: %d code: %d msg: %s), so retry this.", remaining_messages, msg->data.result, curl_easy_strerror(msg->data.result)); isRetry = true; } if(!isRetry){ cMap_req.erase(hCurl); curl_multi_remove_handle(hMulti, hCurl); s3fscurl->DestroyCurlHandle(); delete s3fscurl; }else{ cMap_req.erase(hCurl); curl_multi_remove_handle(hMulti, hCurl); // For retry if(RetryCallback){ if(NULL != (retrycurl = RetryCallback(s3fscurl))){ cMap_all[retrycurl->hCurl] = retrycurl; }else{ // Could not set up callback. return -EIO; } } if(s3fscurl != retrycurl){ s3fscurl->DestroyCurlHandle(); delete s3fscurl; } } }else{ assert(false); } } return 0; } int S3fsMultiCurl::Request(void) { int result; CURLMcode curlm_code; FPRNNN("[count=%zu]", cMap_all.size()); if(hMulti){ DPRNNN("Warning: hMulti is not null, thus clear itself."); ClearEx(false); } // Make request list. // // Send multi request loop( with retry ) // (When many request is sends, sometimes gets "Couldn't connect to server") // while(0 < cMap_all.size()){ // populate the multi interface with an initial set of requests if(NULL == (hMulti = curl_multi_init())){ Clear(); return -1; } // set curl handle to multi handle int cnt; s3fscurlmap_t::iterator iter; for(cnt = 0, iter = cMap_all.begin(); cnt < S3fsMultiCurl::max_multireq && iter != cMap_all.end(); cMap_all.erase(iter++), cnt++){ CURL* hCurl = (*iter).first; S3fsCurl* s3fscurl = (*iter).second; if(CURLM_OK != (curlm_code = curl_multi_add_handle(hMulti, hCurl))){ DPRN("curl_multi_add_handle code: %d msg: %s", curlm_code, curl_multi_strerror(curlm_code)); Clear(); return -EIO; } cMap_req[hCurl] = s3fscurl; } // Send multi request. if(0 != (result = MultiPerform())){ Clear(); return result; } // Read the result if(0 != (result = MultiRead())){ Clear(); return result; } // Cleanup curl handle in multi handle ClearEx(false); } return 0; } //------------------------------------------------------------------- // Class AdditionalHeader //------------------------------------------------------------------- AdditionalHeader AdditionalHeader::singleton; //------------------------------------------------------------------- // Class AdditionalHeader method //------------------------------------------------------------------- AdditionalHeader::AdditionalHeader() { if(this == AdditionalHeader::get()){ is_enable = false; }else{ assert(false); } } AdditionalHeader::~AdditionalHeader() { if(this == AdditionalHeader::get()){ Unload(); }else{ assert(false); } } bool AdditionalHeader::Load(const char* file) { if(!file){ DPRNNN("file is NULL."); return false; } Unload(); ifstream AH(file); if(!AH.good()){ DPRNNN("Could not open file(%s).", file); return false; } // read file string line; while(getline(AH, line)){ if('#' == line[0]){ continue; } if(0 == line.size()){ continue; } // load a line stringstream ss(line); string key(""); // suffix(key) string head; // additional HTTP header string value; // header value if(0 == isblank(line[0])){ ss >> key; } if(ss){ ss >> head; if(ss && static_cast(ss.tellg()) < line.size()){ value = line.substr(static_cast(ss.tellg()) + 1); } } // check it if(0 == head.size()){ if(0 == key.size()){ continue; } DPRNNN("file format error: %s key(suffix) is no HTTP header value.", key.c_str()); Unload(); return false; } // set charcntlist int keylen = key.size(); charcnt_list_t::iterator iter; for(iter = charcntlist.begin(); iter != charcntlist.end(); ++iter){ if(keylen == (*iter)){ break; } } if(iter == charcntlist.end()){ charcntlist.push_back(keylen); } // set addheader if(addheader.end() == addheader.find(key)){ headerpair_t hpair; hpair[head] = value; addheader[key] = hpair; }else{ (addheader[key])[head] = value; } // set flag if(!is_enable){ is_enable = true; } } return true; } void AdditionalHeader::Unload(void) { is_enable = false; charcntlist.clear(); addheader.clear(); } bool AdditionalHeader::AddHeader(headers_t& meta, const char* path) const { if(!is_enable){ return true; } if(!path){ DPRNNN("path is NULL."); return false; } int nPathLen = strlen(path); for(charcnt_list_t::const_iterator iter = charcntlist.begin(); iter != charcntlist.end(); ++iter){ // get target character count if(nPathLen < (*iter)){ continue; } // make target suffix(same character count) & find string suffix(&path[nPathLen - (*iter)]); if(addheader.end() == addheader.find(suffix)){ continue; } for(headerpair_t::const_iterator piter = addheader.at(suffix).begin(); piter != addheader.at(suffix).end(); ++piter){ // Adding header meta[(*piter).first] = (*piter).second; } } return true; } struct curl_slist* AdditionalHeader::AddHeader(struct curl_slist* list, const char* path) const { headers_t meta; if(!AddHeader(meta, path)){ return list; } for(headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter){ // Adding header list = curl_slist_sort_insert(list, iter->first.c_str(), iter->second.c_str()); } meta.clear(); S3FS_MALLOCTRIM(0); return list; } bool AdditionalHeader::Dump(void) const { if(!foreground2){ return true; } // character count list stringstream ssdbg; ssdbg << "Character count list[" << charcntlist.size() << "] = {"; for(charcnt_list_t::const_iterator citer = charcntlist.begin(); citer != charcntlist.end(); ++citer){ ssdbg << " " << (*citer); } ssdbg << " }\n"; // additional header ssdbg << "Additional Header list[" << addheader.size() << "] = {\n"; for(addheader_t::const_iterator aiter = addheader.begin(); aiter != addheader.end(); ++aiter){ string key = (*aiter).first; if(0 == key.size()){ key = "*"; } for(headerpair_t::const_iterator piter = (*aiter).second.begin(); piter != (*aiter).second.end(); ++piter){ ssdbg << " " << key << "\t--->\t" << (*piter).first << ": " << (*piter).second << "\n"; } } ssdbg << "}"; // print all FPRNINFO("%s", ssdbg.str().c_str()); return true; } //------------------------------------------------------------------- // Utility functions //------------------------------------------------------------------- // // curl_slist_sort_insert // This function is like curl_slist_append function, but this adds data by a-sorting. // Because AWS signature needs sorted header. // struct curl_slist* curl_slist_sort_insert(struct curl_slist* list, const char* data) { if(!data){ return list; } string strkey = data; string strval = ""; string::size_type pos = strkey.find(':', 0); if(string::npos != pos){ strval = strkey.substr(pos + 1); strkey = strkey.substr(0, pos); } return curl_slist_sort_insert(list, strkey.c_str(), strval.c_str()); } struct curl_slist* curl_slist_sort_insert(struct curl_slist* list, const char* key, const char* value) { struct curl_slist* curpos; struct curl_slist* lastpos; struct curl_slist* new_item; if(!key){ return list; } if(NULL == (new_item = (struct curl_slist*)malloc(sizeof(struct curl_slist)))){ return list; } // key & value are trimed and lower(only key) string strkey = trim(string(key)); string strval = trim(string(value ? value : "")); string strnew = key + string(": ") + strval; if(NULL == (new_item->data = strdup(strnew.c_str()))){ free(new_item); return list; } new_item->next = NULL; for(lastpos = NULL, curpos = list; curpos; lastpos = curpos, curpos = curpos->next){ string strcur = curpos->data; size_t pos; if(string::npos != (pos = strcur.find(':', 0))){ strcur = strcur.substr(0, pos); } int result = strcasecmp(strkey.c_str(), strcur.c_str()); if(0 == result){ // same data, so replace it. if(lastpos){ lastpos->next = new_item; }else{ list = new_item; } new_item->next = curpos->next; free(curpos->data); free(curpos); break; }else if(0 > result){ // add data before curpos. if(lastpos){ lastpos->next = new_item; }else{ list = new_item; } new_item->next = curpos; break; } } if(!curpos){ // append to last pos if(lastpos){ lastpos->next = new_item; }else{ // a case of list is null list = new_item; } } return list; } string get_sorted_header_keys(const struct curl_slist* list) { string sorted_headers; if(!list){ return sorted_headers; } for( ; list; list = list->next){ string strkey = list->data; size_t pos; if(string::npos != (pos = strkey.find(':', 0))){ strkey = strkey.substr(0, pos); } if(0 < sorted_headers.length()){ sorted_headers += ";"; } sorted_headers += lower(strkey); } return sorted_headers; } string get_canonical_headers(const struct curl_slist* list) { string canonical_headers; if(!list){ canonical_headers = "\n"; return canonical_headers; } for( ; list; list = list->next){ string strhead = list->data; size_t pos; if(string::npos != (pos = strhead.find(':', 0))){ string strkey = trim(lower(strhead.substr(0, pos))); string strval = trim(strhead.substr(pos + 1)); strhead = strkey + string(":") + strval; }else{ strhead = trim(lower(strhead)); } canonical_headers += strhead; canonical_headers += "\n"; } return canonical_headers; } string get_canonical_headers(const struct curl_slist* list, bool only_amz) { string canonical_headers; if(!list){ canonical_headers = "\n"; return canonical_headers; } for( ; list; list = list->next){ string strhead = list->data; size_t pos; if(string::npos != (pos = strhead.find(':', 0))){ string strkey = trim(lower(strhead.substr(0, pos))); string strval = trim(strhead.substr(pos + 1)); strhead = strkey + string(":") + strval; }else{ strhead = trim(lower(strhead)); } if(only_amz && strhead.substr(0, 5) != "x-amz"){ continue; } canonical_headers += strhead; canonical_headers += "\n"; } return canonical_headers; } // function for using global values bool MakeUrlResource(const char* realpath, string& resourcepath, string& url) { if(!realpath){ return false; } resourcepath = urlEncode(service_path + bucket + realpath); url = host + resourcepath; return true; } string prepare_url(const char* url) { FPRNINFO("URL is %s", url); string uri; string host; string path; string url_str = str(url); string token = str("/" + bucket); int bucket_pos = url_str.find(token); int bucket_length = token.size(); int uri_length = 0; if(!strncasecmp(url_str.c_str(), "https://", 8)){ uri_length = 8; } else if(!strncasecmp(url_str.c_str(), "http://", 7)) { uri_length = 7; } uri = url_str.substr(0, uri_length); if(!pathrequeststyle){ host = bucket + "." + url_str.substr(uri_length, bucket_pos - uri_length).c_str(); path = url_str.substr((bucket_pos + bucket_length)); }else{ host = url_str.substr(uri_length, bucket_pos - uri_length).c_str(); string part = url_str.substr((bucket_pos + bucket_length)); if('/' != part[0]){ part = "/" + part; } path = "/" + bucket + part; } url_str = uri + host + path; FPRNINFO("URL changed is %s", url_str.c_str()); return str(url_str); } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */