/* * s3fs - FUSE-based file system backed by Amazon S3 * * Copyright(C) 2007 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. */ #ifndef S3FS_CURL_H_ #define S3FS_CURL_H_ #include #include "psemaphore.h" //---------------------------------------------- // Avoid dependency on libcurl version //---------------------------------------------- // [NOTE] // The following symbols (enum) depend on the version of libcurl. // CURLOPT_TCP_KEEPALIVE 7.25.0 and later // CURLOPT_SSL_ENABLE_ALPN 7.36.0 and later // CURLOPT_KEEP_SENDING_ON_ERROR 7.51.0 and later // // s3fs uses these, if you build s3fs with the old libcurl, // substitute the following symbols to avoid errors. // If the version of libcurl linked at runtime is old, // curl_easy_setopt results in an error(CURLE_UNKNOWN_OPTION) and // a message is output. // #if defined(HAVE_CURLOPT_TCP_KEEPALIVE) && (HAVE_CURLOPT_TCP_KEEPALIVE == 1) #define S3FS_CURLOPT_TCP_KEEPALIVE CURLOPT_TCP_KEEPALIVE #else #define S3FS_CURLOPT_TCP_KEEPALIVE static_cast(213) #endif #if defined(HAVE_CURLOPT_SSL_ENABLE_ALPN) && (HAVE_CURLOPT_SSL_ENABLE_ALPN == 1) #define S3FS_CURLOPT_SSL_ENABLE_ALPN CURLOPT_SSL_ENABLE_ALPN #else #define S3FS_CURLOPT_SSL_ENABLE_ALPN static_cast(226) #endif #if defined(HAVE_CURLOPT_KEEP_SENDING_ON_ERROR) && (HAVE_CURLOPT_KEEP_SENDING_ON_ERROR == 1) #define S3FS_CURLOPT_KEEP_SENDING_ON_ERROR CURLOPT_KEEP_SENDING_ON_ERROR #else #define S3FS_CURLOPT_KEEP_SENDING_ON_ERROR static_cast(245) #endif //---------------------------------------------- // Symbols //---------------------------------------------- static const int MIN_MULTIPART_SIZE = 5 * 1024 * 1024; //---------------------------------------------- // class BodyData //---------------------------------------------- // memory class for curl write memory callback // class BodyData { private: char* text; size_t lastpos; size_t bufsize; private: bool IsSafeSize(size_t addbytes) const { return ((lastpos + addbytes + 1) > bufsize ? false : true); } bool Resize(size_t addbytes); public: BodyData() : text(NULL), lastpos(0), bufsize(0) {} ~BodyData() { Clear(); } void Clear(void); bool Append(void* ptr, size_t bytes); bool Append(void* ptr, size_t blockSize, size_t numBlocks) { return Append(ptr, (blockSize * numBlocks)); } const char* str() const; size_t size() const { return lastpos; } }; //---------------------------------------------- // Utility structs & typedefs //---------------------------------------------- typedef std::vector etaglist_t; // Each part information for Multipart upload struct filepart { bool uploaded; // does finish uploading std::string etag; // expected etag value int fd; // base file(temporary full file) descriptor off_t startpos; // seek fd point for uploading off_t size; // uploading size etaglist_t* etaglist; // use only parallel upload int etagpos; // use only parallel upload filepart() : uploaded(false), fd(-1), startpos(0), size(-1), etaglist(NULL), etagpos(-1) {} ~filepart() { clear(); } void clear(void) { uploaded = false; etag = ""; fd = -1; startpos = 0; size = -1; etaglist = NULL; etagpos = - 1; } void add_etag_list(etaglist_t* list) { if(list){ list->push_back(std::string("")); etaglist = list; etagpos = list->size() - 1; }else{ etaglist = NULL; etagpos = - 1; } } }; // for progress struct case_insensitive_compare_func { bool operator()(const std::string& a, const std::string& b) const { return strcasecmp(a.c_str(), b.c_str()) < 0; } }; typedef std::map mimes_t; typedef std::pair progress_t; typedef std::map curltime_t; typedef std::map curlprogress_t; class S3fsMultiCurl; //---------------------------------------------- // class CurlHandlerPool //---------------------------------------------- typedef std::list hcurllist_t; class CurlHandlerPool { public: explicit CurlHandlerPool(int maxHandlers) : mMaxHandlers(maxHandlers) { assert(maxHandlers > 0); } bool Init(); bool Destroy(); CURL* GetHandler(bool only_pool); void ReturnHandler(CURL* hCurl, bool restore_pool); private: int mMaxHandlers; pthread_mutex_t mLock; hcurllist_t mPool; }; //---------------------------------------------- // class S3fsCurl //---------------------------------------------- class PageList; class S3fsCurl; // Prototype function for lazy setup options for curl handle typedef bool (*s3fscurl_lazy_setup)(S3fsCurl* s3fscurl); typedef std::map iamcredmap_t; typedef std::map sseckeymap_t; typedef std::list sseckeylist_t; // storage class(rrs) enum storage_class_t { STANDARD, STANDARD_IA, ONEZONE_IA, REDUCED_REDUNDANCY, INTELLIGENT_TIERING }; enum acl_t { PRIVATE, PUBLIC_READ, PUBLIC_READ_WRITE, AWS_EXEC_READ, AUTHENTICATED_READ, BUCKET_OWNER_READ, BUCKET_OWNER_FULL_CONTROL, LOG_DELIVERY_WRITE, INVALID_ACL }; // sse type enum sse_type_t { SSE_DISABLE = 0, // not use server side encrypting SSE_S3, // server side encrypting by S3 key SSE_C, // server side encrypting by custom key SSE_KMS // server side encrypting by kms id }; // share enum { SHARE_MUTEX_DNS = 0, SHARE_MUTEX_SSL_SESSION = 1, SHARE_MUTEX_MAX = 2, }; // Class for lapping curl // class S3fsCurl { friend class S3fsMultiCurl; private: enum REQTYPE { REQTYPE_UNSET = -1, REQTYPE_DELETE = 0, REQTYPE_HEAD, REQTYPE_PUTHEAD, REQTYPE_PUT, REQTYPE_GET, REQTYPE_CHKBUCKET, REQTYPE_LISTBUCKET, REQTYPE_PREMULTIPOST, REQTYPE_COMPLETEMULTIPOST, REQTYPE_UPLOADMULTIPOST, REQTYPE_COPYMULTIPOST, REQTYPE_MULTILIST, REQTYPE_IAMCRED, REQTYPE_ABORTMULTIUPLOAD, REQTYPE_IAMROLE }; // class variables static pthread_mutex_t curl_handles_lock; static pthread_mutex_t curl_share_lock[SHARE_MUTEX_MAX]; static bool is_initglobal_done; static CurlHandlerPool* sCurlPool; static int sCurlPoolSize; static CURLSH* hCurlShare; static bool is_cert_check; static bool is_dns_cache; static bool is_ssl_session_cache; static long connect_timeout; static time_t readwrite_timeout; static int retries; static bool is_public_bucket; static acl_t default_acl; static storage_class_t storage_class; static sseckeylist_t sseckeys; static std::string ssekmsid; static sse_type_t ssetype; static bool is_content_md5; static bool is_verbose; static std::string AWSAccessKeyId; static std::string AWSSecretAccessKey; static std::string AWSAccessToken; static time_t AWSAccessTokenExpire; static bool is_ecs; static bool is_use_session_token; static bool is_ibm_iam_auth; static std::string IAM_cred_url; static size_t IAM_field_count; static std::string IAM_token_field; static std::string IAM_expiry_field; static std::string IAM_role; static long ssl_verify_hostname; static curltime_t curl_times; static curlprogress_t curl_progress; static std::string curl_ca_bundle; static mimes_t mimeTypes; static std::string userAgent; static int max_parallel_cnt; static int max_multireq; static off_t multipart_size; static bool is_sigv4; static bool is_ua; // User-Agent static bool requester_pays; // variables CURL* hCurl; REQTYPE type; // type of request std::string path; // target object path std::string base_path; // base path (for multi curl head request) std::string saved_path; // saved path = cache key (for multi curl head request) std::string url; // target object path(url) struct curl_slist* requestHeaders; headers_t responseHeaders; // header data by HeaderCallback BodyData bodydata; // body data by WriteMemoryCallback BodyData headdata; // header data by WriteMemoryCallback volatile long LastResponseCode; const unsigned char* postdata; // use by post method and read callback function. int postdata_remaining; // use by post method and read callback function. filepart partdata; // use by multipart upload/get object callback bool is_use_ahbe; // additional header by extension int retry_count; // retry count for multipart FILE* b_infile; // backup for retrying const unsigned char* b_postdata; // backup for retrying int b_postdata_remaining; // backup for retrying off_t b_partdata_startpos; // backup for retrying ssize_t b_partdata_size; // backup for retrying int b_ssekey_pos; // backup for retrying std::string b_ssevalue; // backup for retrying sse_type_t b_ssetype; // backup for retrying std::string b_from; // backup for retrying(for copy request) headers_t b_meta; // backup for retrying(for copy request) std::string op; // the HTTP verb of the request ("PUT", "GET", etc.) std::string query_string; // request query string Semaphore *sem; pthread_mutex_t *completed_tids_lock; std::vector *completed_tids; s3fscurl_lazy_setup fpLazySetup; // curl options for lazy setting function public: // constructor/destructor explicit S3fsCurl(bool ahbe = false); ~S3fsCurl(); private: // class methods static bool InitGlobalCurl(void); static bool DestroyGlobalCurl(void); static bool InitShareCurl(void); static bool DestroyShareCurl(void); static void LockCurlShare(CURL* handle, curl_lock_data nLockData, curl_lock_access laccess, void* useptr); static void UnlockCurlShare(CURL* handle, curl_lock_data nLockData, void* useptr); static bool InitCryptMutex(void); static bool DestroyCryptMutex(void); static int CurlProgress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); static bool InitMimeType(const char* MimeFile = NULL); static bool LocateBundle(void); static size_t HeaderCallback(void *data, size_t blockSize, size_t numBlocks, void *userPtr); static size_t WriteMemoryCallback(void *ptr, size_t blockSize, size_t numBlocks, void *data); static size_t ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp); static size_t UploadReadCallback(void *ptr, size_t size, size_t nmemb, void *userp); static size_t DownloadWriteCallback(void* ptr, size_t size, size_t nmemb, void* userp); static bool UploadMultipartPostCallback(S3fsCurl* s3fscurl); static bool CopyMultipartPostCallback(S3fsCurl* s3fscurl); static bool MixMultipartPostCallback(S3fsCurl* s3fscurl); static S3fsCurl* UploadMultipartPostRetryCallback(S3fsCurl* s3fscurl); static S3fsCurl* CopyMultipartPostRetryCallback(S3fsCurl* s3fscurl); static S3fsCurl* MixMultipartPostRetryCallback(S3fsCurl* s3fscurl); static S3fsCurl* ParallelGetObjectRetryCallback(S3fsCurl* s3fscurl); // lazy functions for set curl options static bool UploadMultipartPostSetCurlOpts(S3fsCurl* s3fscurl); static bool CopyMultipartPostSetCurlOpts(S3fsCurl* s3fscurl); static bool PreGetObjectRequestSetCurlOpts(S3fsCurl* s3fscurl); static bool PreHeadRequestSetCurlOpts(S3fsCurl* s3fscurl); static bool ParseIAMCredentialResponse(const char* response, iamcredmap_t& keyval); static bool SetIAMCredentials(const char* response); static bool ParseIAMRoleFromMetaDataResponse(const char* response, std::string& rolename); static bool SetIAMRoleFromMetaData(const char* response); static bool LoadEnvSseCKeys(void); static bool LoadEnvSseKmsid(void); static bool PushbackSseKeys(std::string& onekey); static bool AddUserAgent(CURL* hCurl); static int CurlDebugFunc(CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr); // methods bool ResetHandle(void); bool RemakeHandle(void); bool ClearInternalData(void); void insertV4Headers(); void insertV2Headers(); void insertIBMIAMHeaders(); void insertAuthHeaders(); std::string CalcSignatureV2(const std::string& method, const std::string& strMD5, const std::string& content_type, const std::string& date, const std::string& resource); std::string CalcSignature(const std::string& method, const std::string& canonical_uri, const std::string& query_string, const std::string& strdate, const std::string& payload_hash, const std::string& date8601); int GetIAMCredentials(void); int UploadMultipartPostSetup(const char* tpath, int part_num, const std::string& upload_id); int CopyMultipartPostSetup(const char* from, const char* to, int part_num, const std::string& upload_id, headers_t& meta); bool UploadMultipartPostComplete(); bool CopyMultipartPostComplete(); bool MixMultipartPostComplete(); public: // class methods static bool InitS3fsCurl(const char* MimeFile = NULL); static bool DestroyS3fsCurl(void); static int ParallelMultipartUploadRequest(const char* tpath, headers_t& meta, int fd); static int ParallelMixMultipartUploadRequest(const char* tpath, headers_t& meta, int fd, const PageList& pagelist); static int ParallelGetObjectRequest(const char* tpath, int fd, off_t start, ssize_t size); static bool CheckIAMCredentialUpdate(void); // class methods(variables) static std::string LookupMimeType(const std::string& name); static bool SetCheckCertificate(bool isCertCheck); static bool SetDnsCache(bool isCache); static bool SetSslSessionCache(bool isCache); static long SetConnectTimeout(long timeout); static time_t SetReadwriteTimeout(time_t timeout); static time_t GetReadwriteTimeout(void) { return S3fsCurl::readwrite_timeout; } static int SetRetries(int count); static bool SetPublicBucket(bool flag); static bool IsPublicBucket(void) { return S3fsCurl::is_public_bucket; } static acl_t SetDefaultAcl(acl_t acl); static acl_t GetDefaultAcl(); static storage_class_t SetStorageClass(storage_class_t storage_class); static storage_class_t GetStorageClass() { return S3fsCurl::storage_class; } static bool LoadEnvSse(void) { return (S3fsCurl::LoadEnvSseCKeys() && S3fsCurl::LoadEnvSseKmsid()); } static sse_type_t SetSseType(sse_type_t type); static sse_type_t GetSseType(void) { return S3fsCurl::ssetype; } static bool IsSseDisable(void) { return (SSE_DISABLE == S3fsCurl::ssetype); } static bool IsSseS3Type(void) { return (SSE_S3 == S3fsCurl::ssetype); } static bool IsSseCType(void) { return (SSE_C == S3fsCurl::ssetype); } static bool IsSseKmsType(void) { return (SSE_KMS == S3fsCurl::ssetype); } static bool FinalCheckSse(void); static bool SetSseCKeys(const char* filepath); static bool SetSseKmsid(const char* kmsid); static bool IsSetSseKmsId(void) { return !S3fsCurl::ssekmsid.empty(); } static const char* GetSseKmsId(void) { return S3fsCurl::ssekmsid.c_str(); } static bool GetSseKey(std::string& md5, std::string& ssekey); static bool GetSseKeyMd5(int pos, std::string& md5); static int GetSseKeyCount(void); static bool SetContentMd5(bool flag); static bool SetVerbose(bool flag); static bool GetVerbose(void) { return S3fsCurl::is_verbose; } static bool SetAccessKey(const char* AccessKeyId, const char* SecretAccessKey); static bool SetAccessKeyWithSessionToken(const char* AccessKeyId, const char* SecretAccessKey, const char * SessionToken); static bool IsSetAccessKeyID(void){ return (0 < S3fsCurl::AWSAccessKeyId.size()); } static bool IsSetAccessKeys(void){ return (0 < S3fsCurl::IAM_role.size() || ((0 < S3fsCurl::AWSAccessKeyId.size() || S3fsCurl::is_ibm_iam_auth) && 0 < S3fsCurl::AWSSecretAccessKey.size())); } static long SetSslVerifyHostname(long value); static long GetSslVerifyHostname(void) { return S3fsCurl::ssl_verify_hostname; } // maximum parallel GET and PUT requests static int SetMaxParallelCount(int value); static int GetMaxParallelCount(void) { return S3fsCurl::max_parallel_cnt; } // maximum parallel HEAD requests static int SetMaxMultiRequest(int max); static int GetMaxMultiRequest(void) { return S3fsCurl::max_multireq; } static bool SetIsECS(bool flag); static bool SetIsIBMIAMAuth(bool flag); static size_t SetIAMFieldCount(size_t field_count); static std::string SetIAMCredentialsURL(const char* url); static std::string SetIAMTokenField(const char* token_field); static std::string SetIAMExpiryField(const char* expiry_field); static std::string SetIAMRole(const char* role); static const char* GetIAMRole(void) { return S3fsCurl::IAM_role.c_str(); } static bool SetMultipartSize(off_t size); static off_t GetMultipartSize(void) { return S3fsCurl::multipart_size; } static bool SetSignatureV4(bool isset) { bool bresult = S3fsCurl::is_sigv4; S3fsCurl::is_sigv4 = isset; return bresult; } static bool IsSignatureV4(void) { return S3fsCurl::is_sigv4; } static bool SetUserAgentFlag(bool isset) { bool bresult = S3fsCurl::is_ua; S3fsCurl::is_ua = isset; return bresult; } static bool IsUserAgentFlag(void) { return S3fsCurl::is_ua; } static void InitUserAgent(void); static bool SetRequesterPays(bool flag) { bool old_flag = S3fsCurl::requester_pays; S3fsCurl::requester_pays = flag; return old_flag; } static bool IsRequesterPays(void) { return S3fsCurl::requester_pays; } // methods bool CreateCurlHandle(bool only_pool = false, bool remake = false); bool DestroyCurlHandle(bool restore_pool = true, bool clear_internal_data = true); bool LoadIAMRoleFromMetaData(void); bool AddSseRequestHead(sse_type_t ssetype, std::string& ssevalue, bool is_only_c, bool is_copy); bool GetResponseCode(long& responseCode, bool from_curl_handle = true); int RequestPerform(void); int DeleteRequest(const char* tpath); bool PreHeadRequest(const char* tpath, const char* bpath = NULL, const char* savedpath = NULL, int ssekey_pos = -1); bool PreHeadRequest(std::string& tpath, std::string& bpath, std::string& savedpath, int ssekey_pos = -1) { return PreHeadRequest(tpath.c_str(), bpath.c_str(), savedpath.c_str(), ssekey_pos); } int HeadRequest(const char* tpath, headers_t& meta); int PutHeadRequest(const char* tpath, headers_t& meta, bool is_copy); int PutRequest(const char* tpath, headers_t& meta, int fd); int PreGetObjectRequest(const char* tpath, int fd, off_t start, ssize_t size, sse_type_t ssetype, std::string& ssevalue); int GetObjectRequest(const char* tpath, int fd, off_t start = -1, ssize_t size = -1); int CheckBucket(void); int ListBucketRequest(const char* tpath, const char* query); int PreMultipartPostRequest(const char* tpath, headers_t& meta, std::string& upload_id, bool is_copy); int CompleteMultipartPostRequest(const char* tpath, const std::string& upload_id, etaglist_t& parts); int UploadMultipartPostRequest(const char* tpath, int part_num, const std::string& upload_id); int MultipartListRequest(std::string& body); int AbortMultipartUpload(const char* tpath, const std::string& upload_id); int MultipartHeadRequest(const char* tpath, off_t size, headers_t& meta, bool is_copy); int MultipartUploadRequest(const char* tpath, headers_t& meta, int fd, bool is_copy); int MultipartUploadRequest(const std::string& upload_id, const char* tpath, int fd, off_t offset, off_t size, etaglist_t& list); int MultipartRenameRequest(const char* from, const char* to, headers_t& meta, off_t size); // methods(variables) CURL* GetCurlHandle(void) const { return hCurl; } std::string GetPath(void) const { return path; } std::string GetBasePath(void) const { return base_path; } std::string GetSpacialSavedPath(void) const { return saved_path; } std::string GetUrl(void) const { return url; } std::string GetOp(void) const { return op; } headers_t* GetResponseHeaders(void) { return &responseHeaders; } BodyData* GetBodyData(void) { return &bodydata; } BodyData* GetHeadData(void) { return &headdata; } long GetLastResponseCode(void) const { return LastResponseCode; } bool SetUseAhbe(bool ahbe); bool EnableUseAhbe(void) { return SetUseAhbe(true); } bool DisableUseAhbe(void) { return SetUseAhbe(false); } bool IsUseAhbe(void) const { return is_use_ahbe; } int GetMultipartRetryCount(void) const { return retry_count; } void SetMultipartRetryCount(int retrycnt) { retry_count = retrycnt; } bool IsOverMultipartRetryCount(void) const { return (retry_count >= S3fsCurl::retries); } int GetLastPreHeadSeecKeyPos(void) const { return b_ssekey_pos; } }; //---------------------------------------------- // class S3fsMultiCurl //---------------------------------------------- // Class for lapping multi curl // typedef std::vector s3fscurllist_t; typedef bool (*S3fsMultiSuccessCallback)(S3fsCurl* s3fscurl); // callback for succeed multi request typedef S3fsCurl* (*S3fsMultiRetryCallback)(S3fsCurl* s3fscurl); // callback for failure and retrying class S3fsMultiCurl { private: const int maxParallelism; s3fscurllist_t clist_all; // all of curl requests s3fscurllist_t clist_req; // curl requests are sent S3fsMultiSuccessCallback SuccessCallback; S3fsMultiRetryCallback RetryCallback; pthread_mutex_t completed_tids_lock; std::vector completed_tids; private: bool ClearEx(bool is_all); int MultiPerform(void); int MultiRead(void); static void* RequestPerformWrapper(void* arg); public: explicit S3fsMultiCurl(int maxParallelism); ~S3fsMultiCurl(); int GetMaxParallelism() { return maxParallelism; } S3fsMultiSuccessCallback SetSuccessCallback(S3fsMultiSuccessCallback function); S3fsMultiRetryCallback SetRetryCallback(S3fsMultiRetryCallback function); bool Clear(void) { return ClearEx(true); } bool SetS3fsCurlObject(S3fsCurl* s3fscurl); int Request(void); }; //---------------------------------------------- // Utility Functions //---------------------------------------------- std::string GetContentMD5(int fd); unsigned char* md5hexsum(int fd, off_t start, ssize_t size); std::string md5sum(int fd, off_t start, ssize_t size); struct curl_slist* curl_slist_sort_insert(struct curl_slist* list, const char* data); struct curl_slist* curl_slist_sort_insert(struct curl_slist* list, const char* key, const char* value); std::string get_sorted_header_keys(const struct curl_slist* list); std::string get_canonical_headers(const struct curl_slist* list, bool only_amz = false); std::string get_header_value(const struct curl_slist* list, const std::string &key); bool MakeUrlResource(const char* realpath, std::string& resourcepath, std::string& url); std::string prepare_url(const char* url); bool get_object_sse_type(const char* path, sse_type_t& ssetype, std::string& ssevalue); // implement in s3fs.cpp const char *acl_to_string(acl_t acl); acl_t string_to_acl(const char *acl); #endif // S3FS_CURL_H_ /* * 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 */