+#ifdef CONFIG_CIFS_POSIX
+
+/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
+static void cifs_convert_ace(posix_acl_xattr_entry * ace, struct cifs_posix_ace * cifs_ace)
+{
+ /* u8 cifs fields do not need le conversion */
+ ace->e_perm = (__u16)cifs_ace->cifs_e_perm;
+ ace->e_tag = (__u16)cifs_ace->cifs_e_tag;
+ ace->e_id = (__u32)le64_to_cpu(cifs_ace->cifs_uid);
+ /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */
+
+ return;
+}
+
+/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
+static int cifs_copy_posix_acl(char * trgt,char * src, const int buflen,const int acl_type,const int size_of_data_area)
+{
+ int size = 0;
+ int i;
+ __u16 count;
+ struct cifs_posix_ace * pACE;
+ struct cifs_posix_acl * cifs_acl = (struct cifs_posix_acl *)src;
+ posix_acl_xattr_header * local_acl = (posix_acl_xattr_header *)trgt;
+
+ if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
+ return -EOPNOTSUPP;
+
+ if(acl_type & ACL_TYPE_ACCESS) {
+ count = le16_to_cpu(cifs_acl->access_entry_count);
+ pACE = &cifs_acl->ace_array[0];
+ size = sizeof(struct cifs_posix_acl);
+ size += sizeof(struct cifs_posix_ace) * count;
+ /* check if we would go beyond end of SMB */
+ if(size_of_data_area < size) {
+ cFYI(1,("bad CIFS POSIX ACL size %d vs. %d",size_of_data_area,size));
+ return -EINVAL;
+ }
+ } else if(acl_type & ACL_TYPE_DEFAULT) {
+ count = le16_to_cpu(cifs_acl->access_entry_count);
+ size = sizeof(struct cifs_posix_acl);
+ size += sizeof(struct cifs_posix_ace) * count;
+/* skip past access ACEs to get to default ACEs */
+ pACE = &cifs_acl->ace_array[count];
+ count = le16_to_cpu(cifs_acl->default_entry_count);
+ size += sizeof(struct cifs_posix_ace) * count;
+ /* check if we would go beyond end of SMB */
+ if(size_of_data_area < size)
+ return -EINVAL;
+ } else {
+ /* illegal type */
+ return -EINVAL;
+ }
+
+ size = posix_acl_xattr_size(count);
+ if((buflen == 0) || (local_acl == NULL)) {
+ /* used to query ACL EA size */
+ } else if(size > buflen) {
+ return -ERANGE;
+ } else /* buffer big enough */ {
+ local_acl->a_version = POSIX_ACL_XATTR_VERSION;
+ for(i = 0;i < count ;i++) {
+ cifs_convert_ace(&local_acl->a_entries[i],pACE);
+ pACE ++;
+ }
+ }
+ return size;
+}
+
+__u16 convert_ace_to_cifs_ace(struct cifs_posix_ace * cifs_ace,
+ const posix_acl_xattr_entry * local_ace)
+{
+ __u16 rc = 0; /* 0 = ACL converted ok */
+
+ cifs_ace->cifs_e_perm = (__u8)cpu_to_le16(local_ace->e_perm);
+ cifs_ace->cifs_e_tag = (__u8)cpu_to_le16(local_ace->e_tag);
+ /* BB is there a better way to handle the large uid? */
+ if(local_ace->e_id == -1) {
+ /* Probably no need to le convert -1 on any arch but can not hurt */
+ cifs_ace->cifs_uid = cpu_to_le64(-1);
+ } else
+ cifs_ace->cifs_uid = (__u64)cpu_to_le32(local_ace->e_id);
+ /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/
+ return rc;
+}
+
+/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
+__u16 ACL_to_cifs_posix(char * parm_data,const char * pACL,const int buflen,
+ const int acl_type)
+{
+ __u16 rc = 0;
+ struct cifs_posix_acl * cifs_acl = (struct cifs_posix_acl *)parm_data;
+ posix_acl_xattr_header * local_acl = (posix_acl_xattr_header *)pACL;
+ int count;
+ int i;
+
+ if((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
+ return 0;
+
+ count = posix_acl_xattr_count((size_t)buflen);
+ cFYI(1,("setting acl with %d entries from buf of length %d and version of %d",
+ count,buflen,local_acl->a_version));
+ if(local_acl->a_version != 2) {
+ cFYI(1,("unknown POSIX ACL version %d",local_acl->a_version));
+ return 0;
+ }
+ cifs_acl->version = cpu_to_le16(1);
+ if(acl_type == ACL_TYPE_ACCESS)
+ cifs_acl->access_entry_count = count;
+ else if(acl_type == ACL_TYPE_DEFAULT)
+ cifs_acl->default_entry_count = count;
+ else {
+ cFYI(1,("unknown ACL type %d",acl_type));
+ return 0;
+ }
+ for(i=0;i<count;i++) {
+ rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
+ &local_acl->a_entries[i]);
+ if(rc != 0) {
+ /* ACE not converted */
+ break;
+ }
+ }
+ if(rc == 0) {
+ rc = (__u16)(count * sizeof(struct cifs_posix_ace));
+ rc += sizeof(struct cifs_posix_acl);
+ /* BB add check to make sure ACL does not overflow SMB */
+ }
+ return rc;
+}
+
+int
+CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *searchName,
+ char *acl_inf, const int buflen, const int acl_type,
+ const struct nls_table *nls_codepage)
+{
+/* SMB_QUERY_POSIX_ACL */
+ TRANSACTION2_QPI_REQ *pSMB = NULL;
+ TRANSACTION2_QPI_RSP *pSMBr = NULL;
+ int rc = 0;
+ int bytes_returned;
+ int name_len;
+ __u16 params, byte_count;
+
+ cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName));
+
+queryAclRetry:
+ 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, PATH_MAX
+ /* BB fixme find define for this maxpathcomponent */
+ , nls_codepage);
+ name_len++; /* trailing null */
+ name_len *= 2;
+ pSMB->FileName[name_len] = 0;
+ pSMB->FileName[name_len+1] = 0;
+ } else { /* BB improve the check for buffer overruns BB */
+ name_len = strnlen(searchName, PATH_MAX /* BB fixme */);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, searchName, name_len);
+ }
+
+ params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ /* BB find exact max data count below from sess structure BB */
+ pSMB->MaxDataCount = cpu_to_le16(4000);
+ 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);
+ byte_count = params + 1 /* pad */ ;
+ pSMB->TotalParameterCount = cpu_to_le16(params);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
+ } else {
+ /* decode response */
+
+ rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+ if (rc || (pSMBr->ByteCount < 2))
+ /* BB also check enough total bytes returned */
+ rc = -EIO; /* bad smb */
+ else {
+ __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+ __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+ rc = cifs_copy_posix_acl(acl_inf,
+ (char *)&pSMBr->hdr.Protocol+data_offset,
+ buflen,acl_type,count);
+ }
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto queryAclRetry;
+ return rc;
+}
+
+int
+CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
+ const unsigned char *fileName,
+ const char *local_acl, const int buflen, const int acl_type,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_com_transaction2_spi_req *pSMB = NULL;
+ struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
+ char *parm_data;
+ int name_len;
+ int rc = 0;
+ int bytes_returned = 0;
+ __u16 params, byte_count, data_count, param_offset, offset;
+
+ cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName));
+setAclRetry:
+ 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, fileName, PATH_MAX
+ /* BB fixme 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(fileName, PATH_MAX);
+ name_len++; /* trailing null */
+ strncpy(pSMB->FileName, fileName, name_len);
+ }
+ params = 6 + name_len;
+ pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */
+ pSMB->MaxSetupCount = 0;
+ pSMB->Reserved = 0;
+ pSMB->Flags = 0;
+ pSMB->Timeout = 0;
+ pSMB->Reserved2 = 0;
+ param_offset = offsetof(struct smb_com_transaction2_spi_req,
+ InformationLevel) - 4;
+ offset = param_offset + params;
+ parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
+ pSMB->ParameterOffset = cpu_to_le16(param_offset);
+
+ /* convert to on the wire format for POSIX ACL */
+ data_count = ACL_to_cifs_posix(parm_data,local_acl,buflen,acl_type);
+
+ if(data_count == 0) {
+ rc = -EOPNOTSUPP;
+ goto setACLerrorExit;
+ }
+ pSMB->DataOffset = cpu_to_le16(offset);
+ pSMB->SetupCount = 1;
+ pSMB->Reserved3 = 0;
+ pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
+ pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
+ byte_count = 3 /* pad */ + params + data_count;
+ pSMB->DataCount = cpu_to_le16(data_count);
+ pSMB->TotalDataCount = pSMB->DataCount;
+ pSMB->ParameterCount = cpu_to_le16(params);
+ pSMB->TotalParameterCount = pSMB->ParameterCount;
+ pSMB->Reserved4 = 0;
+ pSMB->hdr.smb_buf_length += byte_count;
+ pSMB->ByteCount = cpu_to_le16(byte_count);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Set POSIX ACL returned %d", rc));
+ }
+
+setACLerrorExit:
+ if (pSMB)
+ cifs_buf_release(pSMB);
+ if (rc == -EAGAIN)
+ goto setAclRetry;
+ return rc;
+}
+
+#endif
+