add support for IBM IAM authentication

This commit is contained in:
Or Ozeri 2017-11-23 10:46:24 +02:00
parent 890c1d53ff
commit bd4bc0e7f1
6 changed files with 211 additions and 47 deletions

View File

@ -100,6 +100,12 @@ or(fstab)
s3fs#mybucket /path/to/mountpoint fuse _netdev,allow_other,use_path_request_style,url=http://url.to.s3/ 0 0
```
To use IBM IAM Authentication, use the `-o ibm_iam_auth` option, and specify the Service Instance ID and API Key in your credentials file:
```
echo SERVICEINSTANCEID:APIKEY > /path/to/passwd
```
The Service Instance ID is only required when using the `-o create_bucket` option.
Note: You may also want to create the global credential file first
```

View File

@ -219,6 +219,9 @@ Because s3fs always checks MD5 when uploading large object, this option does not
\fB\-o\fR iam_role ( default is no IAM role )
This option requires the IAM role name or "auto". If you specify "auto", s3fs will automatically use the IAM role names that are set to an instance. If you specify this option without any argument, it is the same as that you have specified the "auto".
.TP
\fB\-o\fR ibm_iam_auth ( default is not using IBM IAM authentication )
This option instructs s3fs to use IBM IAM authentication. In this mode, the AWSAccessKey and AWSSecretKey will be used as IBM's Service-Instance-ID and APIKey, respectively.
.TP
\fB\-o\fR use_xattr ( default is not handling the extended attribute )
Enable to handle the extended attribute(xattrs).
If you set this option, you can use the extended attribute.

View File

@ -322,16 +322,10 @@ static const int MULTIPART_SIZE = 10 * 1024 * 1024;
static const int MAX_MULTI_COPY_SOURCE_SIZE = 500 * 1024 * 1024;
static const int IAM_EXPIRE_MERGIN = 20 * 60; // update timing
static const std::string IAM_CRED_URL_ECS = "http://169.254.170.2";
static const std::string IAM_CRED_URL = "http://169.254.169.254/latest/meta-data/iam/security-credentials/";
static const std::string ECS_IAM_ENV_VAR = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
static const std::string IAMCRED_ACCESSKEYID = "AccessKeyId";
static const std::string IAMCRED_SECRETACCESSKEY = "SecretAccessKey";
static const std::string IAMCRED_ACCESSTOKEN = "Token";
static const std::string IAMCRED_EXPIRATION = "Expiration";
static const std::string IAMCRED_ROLEARN = "RoleArn";
static const size_t IAMCRED_KEYCOUNT = 4;
static const size_t IAMCRED_KEYCOUNT_ECS = 5;
// [NOTICE]
// This symbol is for libcurl under 7.23.0
@ -364,6 +358,11 @@ string S3fsCurl::AWSSecretAccessKey;
string S3fsCurl::AWSAccessToken;
time_t S3fsCurl::AWSAccessTokenExpire= 0;
bool S3fsCurl::is_ecs = false;
bool S3fsCurl::is_ibm_iam_auth = false;
string S3fsCurl::IAM_cred_url = "http://169.254.169.254/latest/meta-data/iam/security-credentials/";
size_t S3fsCurl::IAM_field_count = 4;
string S3fsCurl::IAM_token_field = "Token";
string S3fsCurl::IAM_expiry_field = "Expiration";
string S3fsCurl::IAM_role;
long S3fsCurl::ssl_verify_hostname = 1; // default(original code...)
curltime_t S3fsCurl::curl_times;
@ -941,6 +940,11 @@ string S3fsCurl::SetDefaultAcl(const char* acl)
return old;
}
string S3fsCurl::GetDefaultAcl()
{
return S3fsCurl::default_acl;
}
storage_class_t S3fsCurl::SetStorageClass(storage_class_t storage_class)
{
storage_class_t old = S3fsCurl::storage_class;
@ -1148,7 +1152,7 @@ bool S3fsCurl::SetVerbose(bool flag)
bool S3fsCurl::SetAccessKey(const char* AccessKeyId, const char* SecretAccessKey)
{
if(!AccessKeyId || '\0' == AccessKeyId[0] || !SecretAccessKey || '\0' == SecretAccessKey[0]){
if((!S3fsCurl::is_ibm_iam_auth && (!AccessKeyId || '\0' == AccessKeyId[0])) || !SecretAccessKey || '\0' == SecretAccessKey[0]){
return false;
}
AWSAccessKeyId = AccessKeyId;
@ -1166,6 +1170,13 @@ long S3fsCurl::SetSslVerifyHostname(long value)
return old;
}
bool S3fsCurl::SetIsIBMIAMAuth(bool flag)
{
bool old = S3fsCurl::is_ibm_iam_auth;
S3fsCurl::is_ibm_iam_auth = flag;
return old;
}
bool S3fsCurl::SetIsECS(bool flag)
{
bool old = S3fsCurl::is_ecs;
@ -1180,6 +1191,34 @@ string S3fsCurl::SetIAMRole(const char* role)
return old;
}
size_t S3fsCurl::SetIAMFieldCount(size_t field_count)
{
size_t old = S3fsCurl::IAM_field_count;
S3fsCurl::IAM_field_count = field_count;
return old;
}
string S3fsCurl::SetIAMCredentialsURL(const char* url)
{
string old = S3fsCurl::IAM_cred_url;
S3fsCurl::IAM_cred_url = url ? url : "";
return old;
}
string S3fsCurl::SetIAMTokenField(const char* token_field)
{
string old = S3fsCurl::IAM_token_field;
S3fsCurl::IAM_token_field = token_field ? token_field : "";
return old;
}
string S3fsCurl::SetIAMExpiryField(const char* expiry_field)
{
string old = S3fsCurl::IAM_expiry_field;
S3fsCurl::IAM_expiry_field = expiry_field ? expiry_field : "";
return old;
}
bool S3fsCurl::SetMultipartSize(off_t size)
{
size = size * 1024 * 1024;
@ -1427,7 +1466,7 @@ bool S3fsCurl::ParseIAMCredentialResponse(const char* response, iamcredmap_t& ke
istringstream sscred(response);
string oneline;
keyval.clear();
while(getline(sscred, oneline, '\n')){
while(getline(sscred, oneline, ',')){
string::size_type pos;
string key;
string val;
@ -1435,10 +1474,10 @@ bool S3fsCurl::ParseIAMCredentialResponse(const char* response, iamcredmap_t& ke
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 if(string::npos != (pos = oneline.find(S3fsCurl::IAM_token_field))){
key = S3fsCurl::IAM_token_field;
}else if(string::npos != (pos = oneline.find(S3fsCurl::IAM_expiry_field))){
key = S3fsCurl::IAM_expiry_field;
}else if(string::npos != (pos = oneline.find(IAMCRED_ROLEARN))){
key = IAMCRED_ROLEARN;
}else{
@ -1447,14 +1486,28 @@ bool S3fsCurl::ParseIAMCredentialResponse(const char* response, iamcredmap_t& ke
if(string::npos == (pos = oneline.find(':', pos + key.length()))){
continue;
}
if(string::npos == (pos = oneline.find('\"', pos))){
continue;
if(S3fsCurl::is_ibm_iam_auth && key == S3fsCurl::IAM_expiry_field){
// parse integer value
if(string::npos == (pos = oneline.find_first_of("0123456789", pos))){
continue;
}
oneline = oneline.substr(pos);
if(string::npos == (pos = oneline.find_last_of("0123456789"))){
continue;
}
val = oneline.substr(0, pos+1);
}else{
// parse string value (starts and ends with quotes)
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);
}
oneline = oneline.substr(pos + sizeof(char));
if(string::npos == (pos = oneline.find('\"'))){
continue;
}
val = oneline.substr(0, pos);
keyval[key] = val;
}
return true;
@ -1470,21 +1523,26 @@ bool S3fsCurl::SetIAMCredentials(const char* response)
return false;
}
if((S3fsCurl::is_ecs ? IAMCRED_KEYCOUNT_ECS : IAMCRED_KEYCOUNT) != keyval.size()){
if(S3fsCurl::IAM_field_count != 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());
S3fsCurl::AWSAccessToken = keyval[string(S3fsCurl::IAM_token_field)];
if(S3fsCurl::is_ibm_iam_auth){
S3fsCurl::AWSAccessTokenExpire = strtol(keyval[string(S3fsCurl::IAM_expiry_field)].c_str(), NULL, 10);
}else{
S3fsCurl::AWSAccessKeyId = keyval[string(IAMCRED_ACCESSKEYID)];
S3fsCurl::AWSSecretAccessKey = keyval[string(IAMCRED_SECRETACCESSKEY)];
S3fsCurl::AWSAccessTokenExpire = cvtIAMExpireStringToTime(keyval[S3fsCurl::IAM_expiry_field].c_str());
}
return true;
}
bool S3fsCurl::CheckIAMCredentialUpdate(void)
{
if(0 == S3fsCurl::IAM_role.size() && !S3fsCurl::is_ecs){
if(0 == S3fsCurl::IAM_role.size() && !S3fsCurl::is_ecs && !S3fsCurl::is_ibm_iam_auth){
return true;
}
if(time(NULL) + IAM_EXPIRE_MERGIN <= S3fsCurl::AWSAccessTokenExpire){
@ -1935,6 +1993,13 @@ bool S3fsCurl::RemakeHandle(void)
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);
if(S3fsCurl::is_ibm_iam_auth){
curl_easy_setopt(hCurl, CURLOPT_POST, true);
curl_easy_setopt(hCurl, CURLOPT_POSTFIELDSIZE, static_cast<curl_off_t>(postdata_remaining));
curl_easy_setopt(hCurl, CURLOPT_READDATA, (void*)this);
curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, S3fsCurl::ReadCallback);
}
break;
case REQTYPE_ABORTMULTIUPLOAD:
@ -2151,10 +2216,6 @@ string S3fsCurl::CalcSignatureV2(const string& method, const string& strMD5, con
string StringToSign;
if(0 < S3fsCurl::IAM_role.size() || S3fsCurl::is_ecs){
if(!S3fsCurl::CheckIAMCredentialUpdate()){
S3FS_PRN_ERR("Something error occurred in checking IAM credential.");
return Signature; // returns empty string, then it occurs error.
}
requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-security-token", S3fsCurl::AWSAccessToken.c_str());
}
@ -2193,10 +2254,6 @@ string S3fsCurl::CalcSignature(const string& method, const string& canonical_uri
string uriencode;
if(0 < S3fsCurl::IAM_role.size() || S3fsCurl::is_ecs){
if(!S3fsCurl::CheckIAMCredentialUpdate()){
S3FS_PRN_ERR("Something error occurred in checking IAM credential.");
return Signature; // returns empty string, then it occurs error.
}
requestHeaders = curl_slist_sort_insert(requestHeaders, "x-amz-security-token", S3fsCurl::AWSAccessToken.c_str());
}
@ -2383,9 +2440,26 @@ void S3fsCurl::insertV2Headers()
}
}
void S3fsCurl::insertIBMIAMHeaders()
{
requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", ("Bearer " + S3fsCurl::AWSAccessToken).c_str());
if(op == "PUT" && path == mount_prefix + "/"){
// ibm-service-instance-id header is required for bucket creation requests
requestHeaders = curl_slist_sort_insert(requestHeaders, "ibm-service-instance-id", S3fsCurl::AWSAccessKeyId.c_str());
}
}
void S3fsCurl::insertAuthHeaders()
{
if(!S3fsCurl::is_sigv4){
if(!S3fsCurl::CheckIAMCredentialUpdate()){
S3FS_PRN_ERR("An error occurred in checking IAM credential.");
return; // do not insert auth headers on error
}
if(S3fsCurl::is_ibm_iam_auth){
insertIBMIAMHeaders();
}else if(!S3fsCurl::is_sigv4){
insertV2Headers();
}else{
insertV4Headers();
@ -2429,7 +2503,7 @@ int S3fsCurl::DeleteRequest(const char* tpath)
//
int S3fsCurl::GetIAMCredentials(void)
{
if (!S3fsCurl::is_ecs) {
if (!S3fsCurl::is_ecs && !S3fsCurl::is_ibm_iam_auth) {
S3FS_PRN_INFO3("[IAM role=%s]", S3fsCurl::IAM_role.c_str());
if(0 == S3fsCurl::IAM_role.size()) {
@ -2447,19 +2521,43 @@ int S3fsCurl::GetIAMCredentials(void)
// url
if (is_ecs) {
url = string(IAM_CRED_URL_ECS) + std::getenv(ECS_IAM_ENV_VAR.c_str());
url = string(S3fsCurl::IAM_cred_url) + std::getenv(ECS_IAM_ENV_VAR.c_str());
}
else {
url = string(IAM_CRED_URL) + S3fsCurl::IAM_role;
url = string(S3fsCurl::IAM_cred_url) + S3fsCurl::IAM_role;
}
requestHeaders = NULL;
responseHeaders.clear();
bodydata = new BodyData();
string postContent;
if(S3fsCurl::is_ibm_iam_auth){
url = string(S3fsCurl::IAM_cred_url);
// make contents
postContent += "grant_type=urn:ibm:params:oauth:grant-type:apikey";
postContent += "&response_type=cloud_iam";
postContent += "&apikey=" + S3fsCurl::AWSSecretAccessKey;
// set postdata
postdata = reinterpret_cast<const unsigned char*>(postContent.c_str());
b_postdata = postdata;
postdata_remaining = postContent.size(); // without null
b_postdata_remaining = postdata_remaining;
requestHeaders = curl_slist_sort_insert(requestHeaders, "Authorization", "Basic Yng6Yng=");
curl_easy_setopt(hCurl, CURLOPT_POST, true); // POST
curl_easy_setopt(hCurl, CURLOPT_POSTFIELDSIZE, static_cast<curl_off_t>(postdata_remaining));
curl_easy_setopt(hCurl, CURLOPT_READDATA, (void*)this);
curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, S3fsCurl::ReadCallback);
}
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);
S3fsCurl::AddUserAgent(hCurl); // put User-Agent
int result = RequestPerform();
@ -2467,6 +2565,7 @@ int S3fsCurl::GetIAMCredentials(void)
// analyzing response
if(0 == result && !S3fsCurl::SetIAMCredentials(bodydata->str())){
S3FS_PRN_ERR("Something error occurred, could not get IAM credential.");
result = -EIO;
}
delete bodydata;
bodydata = NULL;
@ -2489,7 +2588,7 @@ bool S3fsCurl::LoadIAMRoleFromMetaData(void)
}
// url
url = string(IAM_CRED_URL);
url = string(S3fsCurl::IAM_cred_url);
requestHeaders = NULL;
responseHeaders.clear();
bodydata = new BodyData();

View File

@ -233,6 +233,11 @@ class S3fsCurl
static std::string AWSAccessToken;
static time_t AWSAccessTokenExpire;
static bool is_ecs;
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;
@ -319,6 +324,7 @@ class S3fsCurl
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);
@ -349,6 +355,7 @@ class S3fsCurl
static bool SetPublicBucket(bool flag);
static bool IsPublicBucket(void) { return S3fsCurl::is_public_bucket; }
static std::string SetDefaultAcl(const char* acl);
static std::string 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()); }
@ -370,14 +377,22 @@ class S3fsCurl
static bool SetVerbose(bool flag);
static bool GetVerbose(void) { return S3fsCurl::is_verbose; }
static bool SetAccessKey(const char* AccessKeyId, const char* SecretAccessKey);
static bool IsSetAccessKeyId(void){
return (0 < S3fsCurl::IAM_role.size() || (0 < S3fsCurl::AWSAccessKeyId.size() && 0 < S3fsCurl::AWSSecretAccessKey.size()));
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; }
static int SetMaxParallelCount(int value);
static int GetMaxParallelCount(void) { return S3fsCurl::max_parallel_cnt; }
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);

View File

@ -128,6 +128,7 @@ static bool is_s3fs_gid = false;// default does not set.
static bool is_s3fs_umask = false;// default does not set.
static bool is_remove_cache = false;
static bool is_ecs = false;
static bool is_ibm_iam_auth = false;
static bool is_use_xattr = false;
static bool create_bucket = false;
static int64_t singlepart_copy_limit = FIVE_GB;
@ -4110,7 +4111,7 @@ static int get_access_keys(void)
}
// 1 - keys specified on the command line
if(S3fsCurl::IsSetAccessKeyId()){
if(S3fsCurl::IsSetAccessKeys()){
return EXIT_SUCCESS;
}
@ -4174,7 +4175,7 @@ static int get_access_keys(void)
// It is possible that the user's file was there but
// contained no key pairs i.e. commented out
// in that case, go look in the final location
if(S3fsCurl::IsSetAccessKeyId()){
if(S3fsCurl::IsSetAccessKeys()){
return EXIT_SUCCESS;
}
}
@ -4540,14 +4541,29 @@ static int my_fuse_opt_proc(void* data, const char* arg, int key, struct fuse_ar
passwd_file = strchr(arg, '=') + sizeof(char);
return 0;
}
if(0 == strcmp(arg, "ibm_iam_auth")){
S3fsCurl::SetIsIBMIAMAuth(true);
S3fsCurl::SetIAMCredentialsURL("https://iam.bluemix.net/oidc/token");
S3fsCurl::SetIAMTokenField("access_token");
S3fsCurl::SetIAMExpiryField("expiration");
S3fsCurl::SetIAMFieldCount(2);
is_ibm_iam_auth = true;
return 0;
}
if(0 == strcmp(arg, "ecs")){
if (is_ibm_iam_auth) {
S3FS_PRN_EXIT("option ecs cannot be used in conjunction with ibm");
return -1;
}
S3fsCurl::SetIsECS(true);
S3fsCurl::SetIAMCredentialsURL("http://169.254.170.2");
S3fsCurl::SetIAMFieldCount(5);
is_ecs = true;
return 0;
}
if(0 == STR2NCMP(arg, "iam_role")){
if (is_ecs) {
S3FS_PRN_EXIT("option iam_role cannot be used in conjunction with ecs");
if (is_ecs || is_ibm_iam_auth) {
S3FS_PRN_EXIT("option iam_role cannot be used in conjunction with ecs or ibm");
return -1;
}
if(0 == strcmp(arg, "iam_role") || 0 == strcmp(arg, "iam_role=auto")){
@ -4941,11 +4957,11 @@ int main(int argc, char* argv[])
}
// error checking of command line arguments for compatibility
if(S3fsCurl::IsPublicBucket() && S3fsCurl::IsSetAccessKeyId()){
if(S3fsCurl::IsPublicBucket() && S3fsCurl::IsSetAccessKeys()){
S3FS_PRN_EXIT("specifying both public_bucket and the access keys options is invalid.");
exit(EXIT_FAILURE);
}
if(passwd_file.size() > 0 && S3fsCurl::IsSetAccessKeyId()){
if(passwd_file.size() > 0 && S3fsCurl::IsSetAccessKeys()){
S3FS_PRN_EXIT("specifying both passwd_file and the access keys options is invalid.");
exit(EXIT_FAILURE);
}
@ -4953,7 +4969,7 @@ int main(int argc, char* argv[])
if(EXIT_SUCCESS != get_access_keys()){
exit(EXIT_FAILURE);
}
if(!S3fsCurl::IsSetAccessKeyId()){
if(!S3fsCurl::IsSetAccessKeys()){
S3FS_PRN_EXIT("could not establish security credentials, check documentation.");
exit(EXIT_FAILURE);
}
@ -4967,6 +4983,26 @@ int main(int argc, char* argv[])
exit(EXIT_FAILURE);
}
// check IBM IAM requirements
if(is_ibm_iam_auth){
// check that default ACL is either public-read or private
string defaultACL = S3fsCurl::GetDefaultAcl();
if(defaultACL == "private"){
// IBM's COS default ACL is private
// set acl as empty string to avoid sending x-amz-acl header
S3fsCurl::SetDefaultAcl("");
}else if(defaultACL != "public-read"){
S3FS_PRN_EXIT("can only use 'public-read' or 'private' ACL while using ibm_iam_auth");
return -1;
}
if(create_bucket && !S3fsCurl::IsSetAccessKeyID()){
S3FS_PRN_EXIT("missing service instance ID for bucket creation");
return -1;
}
}
// set user agent
S3fsCurl::InitUserAgent();

View File

@ -1177,6 +1177,11 @@ void show_help (void)
" to an instance. If you specify this option without any argument, it\n"
" is the same as that you have specified the \"auto\".\n"
"\n"
" ibm_iam_auth\n"
" - This option instructs s3fs to use IBM IAM authentication.\n"
" In this mode, the AWSAccessKey and AWSSecretKey will be used as\n"
" IBM's Service-Instance-ID and APIKey, respectively.\n"
"\n"
" use_xattr (default is not handling the extended attribute)\n"
" Enable to handle the extended attribute(xattrs).\n"
" If you set this option, you can use the extended attribute.\n"