diff --git a/src/common.h b/src/common.h index fe51785..8cbce63 100644 --- a/src/common.h +++ b/src/common.h @@ -84,9 +84,22 @@ typedef std::map headers_t; // // Header "x-amz-meta-xattr" is for extended attributes. // This header is url encoded string which is json formated. -// x-amz-meta-xattr:urlencod({"xattr-1":"value-1","xattr-2":"value-2","xattr-3":"value-3"}) +// x-amz-meta-xattr:urlencod({"xattr-1":"base64(value-1)","xattr-2":"base64(value-2)","xattr-3":"base64(value-3)"}) // -typedef std::map xattrs_t; +typedef struct xattr_value{ + unsigned char* pvalue; + size_t length; + + xattr_value(unsigned char* pval = NULL, size_t len = 0) : pvalue(pval), length(len) {} + ~xattr_value() + { + if(pvalue){ + free(pvalue); + } + } +}XATTRVAL, *PXATTRVAL; + +typedef std::map xattrs_t; // // Global valiables diff --git a/src/common_auth.cpp b/src/common_auth.cpp index ab8d7ee..e6af4ac 100644 --- a/src/common_auth.cpp +++ b/src/common_auth.cpp @@ -30,7 +30,7 @@ using namespace std; //------------------------------------------------------------------- // Utility Function //------------------------------------------------------------------- -char* s3fs_base64(unsigned char* input, size_t length) +char* s3fs_base64(const unsigned char* input, size_t length) { static const char* base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; char* result; @@ -61,6 +61,61 @@ char* s3fs_base64(unsigned char* input, size_t length) return result; } +inline unsigned char char_decode64(const char ch) +{ + unsigned char by; + if('A' <= ch && ch <= 'Z'){ // A - Z + by = static_cast(ch - 'A'); + }else if('a' <= ch && ch <= 'z'){ // a - z + by = static_cast(ch - 'a' + 26); + }else if('0' <= ch && ch <= '9'){ // 0 - 9 + by = static_cast(ch - '0' + 52); + }else if('+' == ch){ // + + by = 62; + }else if('/' == ch){ // / + by = 63; + }else if('=' == ch){ // = + by = 64; + }else{ // something wrong + by = 64; + } + return by; +} + +unsigned char* s3fs_decode64(const char* input, size_t* plength) +{ + unsigned char* result; + if(!input || 0 == strlen(input) || !plength){ + return NULL; + } + if(NULL == (result = (unsigned char*)malloc((strlen(input) + 1)))){ + return NULL; // ENOMEM + } + + unsigned char parts[4]; + size_t input_len = strlen(input); + size_t rpos; + size_t wpos; + for(rpos = 0, wpos = 0; rpos < input_len; rpos += 4){ + parts[0] = char_decode64(input[rpos]); + parts[1] = (rpos + 1) < input_len ? char_decode64(input[rpos + 1]) : 64; + parts[2] = (rpos + 2) < input_len ? char_decode64(input[rpos + 2]) : 64; + parts[3] = (rpos + 3) < input_len ? char_decode64(input[rpos + 3]) : 64; + + result[wpos++] = ((parts[0] << 2) & 0xfc) | ((parts[1] >> 4) & 0x03); + if(64 == parts[2]){ + break; + } + result[wpos++] = ((parts[1] << 4) & 0xf0) | ((parts[2] >> 2) & 0x0f); + if(64 == parts[3]){ + break; + } + result[wpos++] = ((parts[2] << 6) & 0xc0) | (parts[3] & 0x3f); + } + *plength = wpos; + return result; +} + string s3fs_get_content_md5(int fd) { unsigned char* md5hex; diff --git a/src/s3fs.cpp b/src/s3fs.cpp index 4aa6b57..91384be 100644 --- a/src/s3fs.cpp +++ b/src/s3fs.cpp @@ -158,7 +158,8 @@ static xmlChar* get_exp_value_xml(xmlDocPtr doc, xmlXPathContextPtr ctx, const c static void print_uncomp_mp_list(uncomp_mp_list_t& list); static bool abort_uncomp_mp_list(uncomp_mp_list_t& list); static bool get_uncomp_mp_list(xmlDocPtr doc, uncomp_mp_list_t& list); -static bool parse_xattr_keyval(const std::string& xattrpair, string& key, string& val); +static void free_xattrs(xattrs_t& xattrs); +static bool parse_xattr_keyval(const std::string& xattrpair, string& key, PXATTRVAL& pval); static size_t parse_xattrs(const std::string& strxattrs, xattrs_t& xattrs); static std::string build_xattrs(const xattrs_t& xattrs); static int s3fs_utility_mode(void); @@ -2720,21 +2721,37 @@ static int remote_mountpath_exists(const char* path) return 0; } -static bool parse_xattr_keyval(const std::string& xattrpair, string& key, string& val) + +static void free_xattrs(xattrs_t& xattrs) +{ + for(xattrs_t::iterator iter = xattrs.begin(); iter != xattrs.end(); xattrs.erase(iter++)){ + if(iter->second){ + delete iter->second; + } + } +} + +static bool parse_xattr_keyval(const std::string& xattrpair, string& key, PXATTRVAL& pval) { // parse key and value size_t pos; + string tmpval; if(string::npos == (pos = xattrpair.find_first_of(":"))){ DPRNNN("one of xattr pair(%s) is wrong format.", xattrpair.c_str()); return false; } - key = xattrpair.substr(0, pos); - val = xattrpair.substr(pos + 1); + key = xattrpair.substr(0, pos); + tmpval = xattrpair.substr(pos + 1); - if(!takeout_str_dquart(key) || !takeout_str_dquart(val)){ + if(!takeout_str_dquart(key) || !takeout_str_dquart(tmpval)){ DPRNNN("one of xattr pair(%s) is wrong format.", xattrpair.c_str()); return false; } + + pval = new XATTRVAL; + pval->length = 0; + pval->pvalue = s3fs_decode64(tmpval.c_str(), &pval->length); + return true; } @@ -2742,33 +2759,35 @@ static size_t parse_xattrs(const std::string& strxattrs, xattrs_t& xattrs) { xattrs.clear(); + // decode + string jsonxattrs = urlDecode(strxattrs); + // get from "{" to "}" string restxattrs; { size_t startpos = string::npos; size_t endpos = string::npos; - if(string::npos != (startpos = strxattrs.find_first_of("{"))){ - endpos = strxattrs.find_last_of("}"); + if(string::npos != (startpos = jsonxattrs.find_first_of("{"))){ + endpos = jsonxattrs.find_last_of("}"); } if(startpos == string::npos || endpos == string::npos || endpos <= startpos){ - DPRNNN("xattr header(%s) is not json format.", strxattrs.c_str()); + DPRNNN("xattr header(%s) is not json format.", jsonxattrs.c_str()); return 0; } - restxattrs = strxattrs.substr(startpos + 1, endpos - (startpos + 1)); + restxattrs = jsonxattrs.substr(startpos + 1, endpos - (startpos + 1)); } // parse each key:val for(size_t pair_nextpos = restxattrs.find_first_of(","); 0 < restxattrs.length(); restxattrs = (pair_nextpos != string::npos ? restxattrs.substr(pair_nextpos + 1) : string("")), pair_nextpos = restxattrs.find_first_of(",")){ string pair = pair_nextpos != string::npos ? restxattrs.substr(0, pair_nextpos) : restxattrs; - string key = ""; - string val = ""; - if(!parse_xattr_keyval(pair, key, val)){ + string key = ""; + PXATTRVAL pval = NULL; + if(!parse_xattr_keyval(pair, key, pval)){ // something format error, so skip this. continue; } - xattrs[key] = val; + xattrs[key] = pval; } - return xattrs.size(); } @@ -2786,11 +2805,20 @@ static std::string build_xattrs(const xattrs_t& xattrs) strxattrs += '\"'; strxattrs += iter->first; strxattrs += "\":\""; - strxattrs += iter->second; + + if(iter->second){ + char* base64val = s3fs_base64((iter->second)->pvalue, (iter->second)->length); + if(base64val){ + strxattrs += base64val; + free(base64val); + } + } strxattrs += '\"'; } strxattrs += '}'; + strxattrs = urlEncode(strxattrs); + return strxattrs; } @@ -2809,24 +2837,48 @@ static int set_xattrs_to_header(headers_t& meta, const char* name, const char* v // found xattr header but flags is only creating, so failure. return -EEXIST; } - strxattrs = urlDecode(meta["x-amz-meta-xattr"]); + strxattrs = meta["x-amz-meta-xattr"]; } // get map as xattrs_t parse_xattrs(strxattrs, xattrs); // add name(do not care overwrite and empty name/value) - xattrs[string(name)] = value ? string(value, size) : string(""); + if(xattrs.end() != xattrs.find(string(name))){ + // found same head. free value. + delete xattrs[string(name)]; + } + + PXATTRVAL pval = new XATTRVAL; + pval->length = size; + if(0 < size){ + if(NULL == (pval->pvalue = (unsigned char*)malloc(size))){ + delete pval; + free_xattrs(xattrs); + return -ENOMEM; + } + memcpy(pval->pvalue, value, size); + }else{ + pval->pvalue = NULL; + } + xattrs[string(name)] = pval; // build new strxattrs(not encoded) and set it to headers_t meta["x-amz-meta-xattr"] = build_xattrs(xattrs); + free_xattrs(xattrs); + return 0; } static int s3fs_setxattr(const char* path, const char* name, const char* value, size_t size, int flags) { - FPRN("[path=%s][name=%s][value=%s][size=%zu][flags=%d]", path, name, value, size, flags); + FPRN("[path=%s][name=%s][value=%p][size=%zu][flags=%d]", path, name, value, size, flags); + + if((value && 0 == size) || (!value && 0 < size)){ + DPRN("Wrong parameter: value(%p), size(%zu)", value, size); + return 0; + } int result; string strpath; @@ -2925,26 +2977,39 @@ static int s3fs_getxattr(const char* path, const char* name, char* value, size_t // object does not have xattrs return -ENOATTR; } - string strxattrs = urlDecode(meta["x-amz-meta-xattr"]); + string strxattrs = meta["x-amz-meta-xattr"]; + parse_xattrs(strxattrs, xattrs); // search name string strname = name; if(xattrs.end() == xattrs.find(strname)){ // not found name in xattrs + free_xattrs(xattrs); return -ENOATTR; } - if(size <= 0){ - return static_cast(xattrs[strname].length() + 1); + // decode + size_t length = 0; + unsigned char* pvalue = NULL; + if(NULL != xattrs[strname]){ + length = xattrs[strname]->length; + pvalue = xattrs[strname]->pvalue; } - if(!value || size <= xattrs[strname].length()){ - // over buffer size - return -ERANGE; - } - strcpy(value, xattrs[strname].c_str()); - return static_cast(strlen(value) + 1); + if(0 < size){ + if(static_cast(size) < length){ + // over buffer size + free_xattrs(xattrs); + return -ERANGE; + } + if(pvalue){ + memcpy(value, pvalue, length); + } + } + free_xattrs(xattrs); + + return static_cast(length); } static int s3fs_listxattr(const char* path, char* list, size_t size) @@ -2974,7 +3039,8 @@ static int s3fs_listxattr(const char* path, char* list, size_t size) // object does not have xattrs return 0; } - string strxattrs = urlDecode(meta["x-amz-meta-xattr"]); + string strxattrs = meta["x-amz-meta-xattr"]; + parse_xattrs(strxattrs, xattrs); // calculate total name length @@ -2984,15 +3050,19 @@ static int s3fs_listxattr(const char* path, char* list, size_t size) total += iter->first.length() + 1; } } + if(0 == total){ + free_xattrs(xattrs); return 0; } // check parameters if(size <= 0){ + free_xattrs(xattrs); return total; } if(!list || size < total){ + free_xattrs(xattrs); return -ERANGE; } @@ -3004,6 +3074,8 @@ static int s3fs_listxattr(const char* path, char* list, size_t size) setpos = &setpos[strlen(setpos) + 1]; } } + free_xattrs(xattrs); + return total; } @@ -3051,18 +3123,29 @@ static int s3fs_removexattr(const char* path, const char* name) // object does not have xattrs return -ENOATTR; } - string strxattrs = urlDecode(meta["x-amz-meta-xattr"]); + string strxattrs = meta["x-amz-meta-xattr"]; + parse_xattrs(strxattrs, xattrs); // check name xattrs string strname = name; if(xattrs.end() == xattrs.find(strname)){ + free_xattrs(xattrs); return -ENOATTR; } // make new header_t after deleting name xattr + if(xattrs[strname]){ + delete xattrs[strname]; + } xattrs.erase(strname); - meta["x-amz-meta-xattr"] = build_xattrs(xattrs); + + // build new xattr + if(0 < xattrs.size()){ + meta["x-amz-meta-xattr"] = build_xattrs(xattrs); + }else{ + meta.erase("x-amz-meta-xattr"); + } if(S_ISDIR(stbuf.st_mode) && IS_REPLACEDIR(nDirType)){ // Should rebuild directory object(except new type) @@ -3072,6 +3155,7 @@ static int s3fs_removexattr(const char* path, const char* name) if(IS_RMTYPEDIR(nDirType)){ S3fsCurl s3fscurl; if(0 != (result = s3fscurl.DeleteRequest(strpath.c_str()))){ + free_xattrs(xattrs); return result; } } @@ -3079,6 +3163,7 @@ static int s3fs_removexattr(const char* path, const char* name) // Make new directory object("dir/") if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, stbuf.st_mtime, stbuf.st_uid, stbuf.st_gid))){ + free_xattrs(xattrs); return result; } @@ -3092,10 +3177,13 @@ static int s3fs_removexattr(const char* path, const char* name) meta["x-amz-metadata-directive"] = "REPLACE"; if(0 != put_headers(strpath.c_str(), meta, true)){ + free_xattrs(xattrs); return -EIO; } StatCache::getStatCacheData()->DelStat(nowcache); + free_xattrs(xattrs); + return 0; } diff --git a/src/s3fs_auth.h b/src/s3fs_auth.h index 6893db5..99b8118 100644 --- a/src/s3fs_auth.h +++ b/src/s3fs_auth.h @@ -26,7 +26,8 @@ // // in common_auth.cpp // -char* s3fs_base64(unsigned char* input, size_t length); +char* s3fs_base64(const unsigned char* input, size_t length); +unsigned char* s3fs_decode64(const char* input, size_t* plength); std::string s3fs_get_content_md5(int fd); std::string s3fs_md5sum(int fd, off_t start, ssize_t size); std::string s3fs_sha256sum(int fd, off_t start, ssize_t size); diff --git a/src/string_util.cpp b/src/string_util.cpp index 8f588cc..cd19a04 100644 --- a/src/string_util.cpp +++ b/src/string_util.cpp @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include +#include #include #include @@ -185,12 +186,12 @@ string urlDecode(const string& s) if(s.length() <= ++i){ break; // wrong format. } - ch += ('0' <= s[i] && s[i] <= '9') ? (s[i] - '0') : ('A' <= s[i] && s[i] <= 'F') ? (s[i] - 'A' + 0x10) : ('a' <= s[i] && s[i] <= 'f') ? (s[i] - 'a' + 0x10) : 0x00; + ch += ('0' <= s[i] && s[i] <= '9') ? (s[i] - '0') : ('A' <= s[i] && s[i] <= 'F') ? (s[i] - 'A' + 0x0a) : ('a' <= s[i] && s[i] <= 'f') ? (s[i] - 'a' + 0x0a) : 0x00; if(s.length() <= ++i){ break; // wrong format. } ch *= 16; - ch += ('0' <= s[i] && s[i] <= '9') ? (s[i] - '0') : ('A' <= s[i] && s[i] <= 'F') ? (s[i] - 'A' + 0x10) : ('a' <= s[i] && s[i] <= 'f') ? (s[i] - 'a' + 0x10) : 0x00; + ch += ('0' <= s[i] && s[i] <= '9') ? (s[i] - '0') : ('A' <= s[i] && s[i] <= 'F') ? (s[i] - 'A' + 0x0a) : ('a' <= s[i] && s[i] <= 'f') ? (s[i] - 'a' + 0x0a) : 0x00; result += ch; } }