From 81d10c1086c57cd726694a549b68c04e74a9ab63 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 26 Jul 2004 06:58:23 -0500 Subject: [PATCH] [CIFS] xattr support part 3 add query EA support to retrieve individual xattr values Signed-off-by: Steve French (sfrench@us.ibm.com) --- fs/cifs/cifsproto.h | 4 ++ fs/cifs/cifssmb.c | 152 ++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/xattr.c | 105 ++++++++++++++++++++++++++++-- 3 files changed, 255 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7dca9b9695a46..e5ed8292cc977 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -225,4 +225,8 @@ extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon, extern ssize_t CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char * EAData, size_t bufsize, const struct nls_table *nls_codepage); +extern ssize_t CIFSSMBQueryEA(const int xid,struct cifsTconInfo * tcon, + const unsigned char * searchName,const unsigned char * ea_name, + unsigned char * ea_value, size_t buf_size, + const struct nls_table *nls_codepage); #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index b027b972090c9..962adc7ab9daa 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3086,6 +3086,8 @@ QAllEAsRetry: ea_response_data = (struct fealist *) (((char *) &pSMBr->hdr.Protocol) + pSMBr->DataOffset); + ea_response_data->list_len = + cpu_to_le32(ea_response_data->list_len); cFYI(1,("ea length %d",ea_response_data->list_len)); name_len = ea_response_data->list_len; if(name_len <= 8) { @@ -3122,8 +3124,10 @@ QAllEAsRetry: /* account for trailing null */ name_len--; temp_ptr++; + temp_fea->value_len = cpu_to_le16(temp_fea->value_len); name_len -= temp_fea->value_len; temp_ptr += temp_fea->value_len; + /* BB check that temp_ptr is still within smb BB*/ /* no trailing null to account for in value len */ /* go on to next EA */ temp_fea = (struct fea *)temp_ptr; @@ -3138,4 +3142,152 @@ QAllEAsRetry: return rc; } + +ssize_t CIFSSMBQueryEA(const int xid,struct cifsTconInfo * tcon, + const unsigned char * searchName,const unsigned char * ea_name, + unsigned char * ea_value, size_t buf_size, + const struct nls_table *nls_codepage) +{ + TRANSACTION2_QPI_REQ *pSMB = NULL; + TRANSACTION2_QPI_RSP *pSMBr = NULL; + int rc = 0; + int bytes_returned; + int name_len; + struct fea * temp_fea; + char * temp_ptr; + + cFYI(1, ("In Query EA path %s", searchName)); +QEARetry: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifs_strtoUCS((wchar_t *) pSMB->FileName, searchName, 530 + /* find define for this maxpathcomponent */ + , nls_codepage); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB improve the check for buffer overruns BB */ + name_len = strnlen(searchName, 530); + name_len++; /* trailing null */ + strncpy(pSMB->FileName, searchName, name_len); + } + + pSMB->TotalParameterCount = 2 /* level */ + 4 /* reserved */ + + name_len /* includes null */ ; + pSMB->TotalDataCount = 0; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + pSMB->ParameterOffset = cpu_to_le16(offsetof( + struct smb_com_transaction2_qpi_req ,InformationLevel) - 4); + pSMB->DataCount = 0; + pSMB->DataOffset = 0; + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION); + pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ; + pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount); + pSMB->ParameterCount = pSMB->TotalParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS); + pSMB->Reserved4 = 0; + pSMB->hdr.smb_buf_length += pSMB->ByteCount; + pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount); + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Send error in Query EA = %d", rc)); + } else { /* decode response */ + pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset); + /* BB also check enough total bytes returned */ + /* BB we need to improve the validity checking + of these trans2 responses */ + if ((pSMBr->ByteCount < 4) || (pSMBr->DataOffset > 512)) + rc = -EIO; /* bad smb */ + /* else if (pFindData){ + memcpy((char *) pFindData, + (char *) &pSMBr->hdr.Protocol + + pSMBr->DataOffset, kl); + }*/ else { + /* check that length of list is not more than bcc */ + /* check that each entry does not go beyond length + of list */ + /* check that each element of each entry does not + go beyond end of list */ + struct fealist * ea_response_data; + rc = -ENOENT; + /* validate_trans2_offsets() */ + /* BB to check if(start of smb + pSMBr->DataOffset > &bcc+ bcc)*/ + ea_response_data = (struct fealist *) + (((char *) &pSMBr->hdr.Protocol) + + pSMBr->DataOffset); + ea_response_data->list_len = + cpu_to_le32(ea_response_data->list_len); + cFYI(1,("ea length %d",ea_response_data->list_len)); + name_len = ea_response_data->list_len; + if(name_len <= 8) { + /* returned EA size zeroed at top of function */ + cFYI(1,("empty EA list returned from server")); + } else { + /* account for ea list len */ + name_len -= 4; + temp_fea = ea_response_data->list; + temp_ptr = (char *)temp_fea; + /* loop through checking if we have a matching + name and then return the associated value */ + while(name_len > 0) { + name_len -= 4; + temp_ptr += 4; + temp_fea->value_len = cpu_to_le16(temp_fea->value_len); + /* BB validate that value_len falls within SMB, + even though maximum for name_len is 255 */ + if(memcmp(temp_fea->name,ea_name, + temp_fea->name_len) == 0) { + /* found a match */ + rc = temp_fea->value_len; + /* account for prefix user. and trailing null */ + if(rc<=buf_size) { + memcpy(ea_value, + temp_fea->name+temp_fea->name_len+1, + rc); + /* ea values, unlike ea names, + are not null terminated */ + } else if(buf_size == 0) { + /* skip copy - calc size only */ + } else { + /* stop before overrun buffer */ + rc = -ERANGE; + } + break; + } + name_len -= temp_fea->name_len; + temp_ptr += temp_fea->name_len; + /* account for trailing null */ + name_len--; + temp_ptr++; + name_len -= temp_fea->value_len; + temp_ptr += temp_fea->value_len; + /* no trailing null to account for in value len */ + /* go on to next EA */ + temp_fea = (struct fea *)temp_ptr; + } + } + } + } + if (pSMB) + cifs_buf_release(pSMB); + if (rc == -EAGAIN) + goto QEARetry; + + return rc; +} + #endif diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 7c0bfea9bd0e3..0fd073d398eff 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -26,27 +26,117 @@ #include "cifsproto.h" #include "cifs_debug.h" +#define CIFS_XATTR_DOS_ATTRIB "user.DOSATTRIB" +#define CIFS_XATTR_USER_PREFIX "user." +#define CIFS_XATTR_SYSTEM_PREFIX "system." +#define CIFS_XATTR_OS2_PREFIX "OS2." /* BB should check for this someday */ +/* also note could add check for security prefix XATTR_SECURITY_PREFIX */ + + int cifs_removexattr(struct dentry * direntry, const char * name) { int rc = -EOPNOTSUPP; return rc; } -int cifs_setxattr(struct dentry * direntry, const char * name, - const void * value, size_t size, int flags) +int cifs_setxattr(struct dentry * direntry, const char * ea_name, + const void * ea_value, size_t buf_size, int flags) { int rc = -EOPNOTSUPP; +#ifdef CONFIG_CIFS_XATTR + int xid; + struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *pTcon; + struct super_block * sb; + char * full_path; + + if(direntry == NULL) + return -EIO; + if(direntry->d_inode == NULL) + return -EIO; + sb = direntry->d_inode->i_sb; + if(sb == NULL) + return -EIO; + xid = GetXid(); + + cifs_sb = CIFS_SB(sb); + pTcon = cifs_sb->tcon; + + down(&sb->s_vfs_rename_sem); + full_path = build_path_from_dentry(direntry); + up(&sb->s_vfs_rename_sem); + if(full_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } + /* return dos attributes as pseudo xattr */ + /* return alt name if available as pseudo attr */ + + /* if proc/fs/cifs/streamstoxattr is set then + search server for EAs or streams to + returns as xattrs */ +/* rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,ea_value,buf_size, + cifs_sb->local_nls);*/ + if (full_path) + kfree(full_path); + FreeXid(xid); +#endif return rc; } -ssize_t cifs_getxattr(struct dentry * direntry, const char * name, - void * value, size_t size) +ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name, + void * ea_value, size_t buf_size) { ssize_t rc = -EOPNOTSUPP; +#ifdef CONFIG_CIFS_XATTR + int xid; + struct cifs_sb_info *cifs_sb; + struct cifsTconInfo *pTcon; + struct super_block * sb; + char * full_path; + + if(direntry == NULL) + return -EIO; + if(direntry->d_inode == NULL) + return -EIO; + sb = direntry->d_inode->i_sb; + if(sb == NULL) + return -EIO; + xid = GetXid(); + + cifs_sb = CIFS_SB(sb); + pTcon = cifs_sb->tcon; + + down(&sb->s_vfs_rename_sem); + full_path = build_path_from_dentry(direntry); + up(&sb->s_vfs_rename_sem); + if(full_path == NULL) { + FreeXid(xid); + return -ENOMEM; + } + /* return dos attributes as pseudo xattr */ + /* return alt name if available as pseudo attr */ + if(memcmp(ea_name,CIFS_XATTR_USER_PREFIX,5)) { + cFYI(1,("illegal xattr namespace %s (only user namespace supported)",ea_name)); + /* BB what if no namespace prefix? */ + /* Should we just pass them to server, except for system? */ + } else { + /* We could add a check here + if proc/fs/cifs/streamstoxattr is set then + search server for EAs or streams to + returns as xattrs */ + ea_name+=5; /* skip past user. */ + rc = CIFSSMBQueryEA(xid,pTcon,full_path,ea_name,ea_value, + buf_size, cifs_sb->local_nls); + } + if (full_path) + kfree(full_path); + FreeXid(xid); +#endif return rc; } -ssize_t cifs_listxattr(struct dentry * direntry, char * ea_data, size_t ea_size) +ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size) { ssize_t rc = -EOPNOTSUPP; #ifdef CONFIG_CIFS_XATTR @@ -81,8 +171,11 @@ ssize_t cifs_listxattr(struct dentry * direntry, char * ea_data, size_t ea_size) /* if proc/fs/cifs/streamstoxattr is set then search server for EAs or streams to returns as xattrs */ - rc = CIFSSMBQAllEAs(xid,pTcon,full_path,ea_data,ea_size, + rc = CIFSSMBQAllEAs(xid,pTcon,full_path,data,buf_size, cifs_sb->local_nls); + + if (full_path) + kfree(full_path); FreeXid(xid); #endif return rc; -- 2.53.0