X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fcifs%2Fcifssmb.c;h=4511b708f0f312d57677e2947276c2063f142446;hb=5dfd06215b951de70b3e610de47813811c822a44;hp=fc297383cb0e349bb1519b59848b3135f60af270;hpb=c2cf07d591ef7bc25c220249822d9bdf0f44c75c;p=linux-2.6 diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index fc297383cb..4511b708f0 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -81,6 +81,40 @@ static struct { #endif /* CONFIG_CIFS_WEAK_PW_HASH */ #endif /* CIFS_POSIX */ +/* Allocates buffer into dst and copies smb string from src to it. + * caller is responsible for freeing dst if function returned 0. + * returns: + * on success - 0 + * on failure - errno + */ +static int +cifs_strncpy_to_host(char **dst, const char *src, const int maxlen, + const bool is_unicode, const struct nls_table *nls_codepage) +{ + int plen; + + if (is_unicode) { + plen = UniStrnlen((wchar_t *)src, maxlen); + *dst = kmalloc(plen + 2, GFP_KERNEL); + if (!*dst) + goto cifs_strncpy_to_host_ErrExit; + cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage); + } else { + plen = strnlen(src, maxlen); + *dst = kmalloc(plen + 2, GFP_KERNEL); + if (!*dst) + goto cifs_strncpy_to_host_ErrExit; + strncpy(*dst, src, plen); + } + (*dst)[plen] = 0; + (*dst)[plen+1] = 0; /* harmless for ASCII case, needed for Unicode */ + return 0; + +cifs_strncpy_to_host_ErrExit: + cERROR(1, ("Failed to allocate buffer for string\n")); + return -ENOMEM; +} + /* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */ @@ -1694,7 +1728,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, { int rc = 0; LOCK_REQ *pSMB = NULL; - LOCK_RSP *pSMBr = NULL; +/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */ int bytes_returned; int timeout = 0; __u16 count; @@ -1705,8 +1739,6 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, if (rc) return rc; - pSMBr = (LOCK_RSP *)pSMB; /* BB removeme BB */ - if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { timeout = CIFS_ASYNC_OP; /* no response expected */ pSMB->Timeout = 0; @@ -1740,7 +1772,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, if (waitFlag) { rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned); + (struct smb_hdr *) pSMB, &bytes_returned); cifs_small_buf_release(pSMB); } else { rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB, @@ -2125,8 +2157,7 @@ copyRetry: cFYI(1, ("Send error in copy = %d with %d files copied", rc, le16_to_cpu(pSMBr->CopyCount))); } - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto copyRetry; @@ -2215,8 +2246,7 @@ createSymLinkRetry: if (rc) cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc)); - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto createSymLinkRetry; @@ -3867,6 +3897,96 @@ GetInodeNumOut: return rc; } +/* parses DFS refferal V3 structure + * caller is responsible for freeing target_nodes + * returns: + * on success - 0 + * on failure - errno + */ +static int +parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, + unsigned int *num_of_nodes, + struct dfs_info3_param **target_nodes, + const struct nls_table *nls_codepage) +{ + int i, rc = 0; + char *data_end; + bool is_unicode; + struct dfs_referral_level_3 *ref; + + is_unicode = pSMBr->hdr.Flags2 & SMBFLG2_UNICODE; + *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals); + + if (*num_of_nodes < 1) { + cERROR(1, ("num_referrals: must be at least > 0," + "but we get num_referrals = %d\n", *num_of_nodes)); + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals); + if (ref->VersionNumber != cpu_to_le16(3)) { + cERROR(1, ("Referrals of V%d version are not supported," + "should be V3", le16_to_cpu(ref->VersionNumber))); + rc = -EINVAL; + goto parse_DFS_referrals_exit; + } + + /* get the upper boundary of the resp buffer */ + data_end = (char *)(&(pSMBr->PathConsumed)) + + le16_to_cpu(pSMBr->t2.DataCount); + + cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n", + *num_of_nodes, + le16_to_cpu(pSMBr->DFSFlags))); + + *target_nodes = kzalloc(sizeof(struct dfs_info3_param) * + *num_of_nodes, GFP_KERNEL); + if (*target_nodes == NULL) { + cERROR(1, ("Failed to allocate buffer for target_nodes\n")); + rc = -ENOMEM; + goto parse_DFS_referrals_exit; + } + + /* collect neccessary data from referrals */ + for (i = 0; i < *num_of_nodes; i++) { + char *temp; + int max_len; + struct dfs_info3_param *node = (*target_nodes)+i; + + node->flags = le16_to_cpu(pSMBr->DFSFlags); + node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); + node->server_type = le16_to_cpu(ref->ServerType); + node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); + + /* copy DfsPath */ + temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); + max_len = data_end - temp; + rc = cifs_strncpy_to_host(&(node->path_name), temp, + max_len, is_unicode, nls_codepage); + if (rc) + goto parse_DFS_referrals_exit; + + /* copy link target UNC */ + temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); + max_len = data_end - temp; + rc = cifs_strncpy_to_host(&(node->node_name), temp, + max_len, is_unicode, nls_codepage); + if (rc) + goto parse_DFS_referrals_exit; + + ref += le16_to_cpu(ref->Size); + } + +parse_DFS_referrals_exit: + if (rc) { + free_dfs_info_array(*target_nodes, *num_of_nodes); + *target_nodes = NULL; + *num_of_nodes = 0; + } + return rc; +} + int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, const unsigned char *searchName, @@ -3877,12 +3997,9 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, /* TRANS2_GET_DFS_REFERRAL */ TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; - struct dfs_referral_level_3 *referrals = NULL; int rc = 0; int bytes_returned; int name_len; - unsigned int i; - char *temp; __u16 params, byte_count; *num_of_nodes = 0; *target_nodes = NULL; @@ -3960,83 +4077,21 @@ getDFSRetry: rc = validate_t2((struct smb_t2_rsp *)pSMBr); /* BB Also check if enough total bytes returned? */ - if (rc || (pSMBr->ByteCount < 17)) + if (rc || (pSMBr->ByteCount < 17)) { rc = -EIO; /* bad smb */ - else { - __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - __u16 data_count = le16_to_cpu(pSMBr->t2.DataCount); - - cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d", - pSMBr->ByteCount, data_offset)); - referrals = - (struct dfs_referral_level_3 *) - (8 /* sizeof start of data block */ + - data_offset + - (char *) &pSMBr->hdr.Protocol); - cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n" - "for referral one refer size: 0x%x srv " - "type: 0x%x refer flags: 0x%x ttl: 0x%x", - le16_to_cpu(pSMBr->NumberOfReferrals), - le16_to_cpu(pSMBr->DFSFlags), - le16_to_cpu(referrals->ReferralSize), - le16_to_cpu(referrals->ServerType), - le16_to_cpu(referrals->ReferralFlags), - le16_to_cpu(referrals->TimeToLive))); - /* BB This field is actually two bytes in from start of - data block so we could do safety check that DataBlock - begins at address of pSMBr->NumberOfReferrals */ - *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals); - - /* BB Fix below so can return more than one referral */ - if (*num_of_nodes > 1) - *num_of_nodes = 1; - - /* get the length of the strings describing refs */ - name_len = 0; - for (i = 0; i < *num_of_nodes; i++) { - /* make sure that DfsPathOffset not past end */ - __u16 offset = le16_to_cpu(referrals->DfsPathOffset); - if (offset > data_count) { - /* if invalid referral, stop here and do - not try to copy any more */ - *num_of_nodes = i; - break; - } - temp = ((char *)referrals) + offset; + goto GetDFSRefExit; + } - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { - name_len += UniStrnlen((wchar_t *)temp, - data_count); - } else { - name_len += strnlen(temp, data_count); - } - referrals++; - /* BB add check that referral pointer does - not fall off end PDU */ - } - /* BB add check for name_len bigger than bcc */ - *target_nodes = - kmalloc(name_len+1+(*num_of_nodes), - GFP_KERNEL); - if (*target_nodes == NULL) { - rc = -ENOMEM; - goto GetDFSRefExit; - } + cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d", + pSMBr->ByteCount, + le16_to_cpu(pSMBr->t2.DataOffset))); - referrals = (struct dfs_referral_level_3 *) - (8 /* sizeof data hdr */ + data_offset + - (char *) &pSMBr->hdr.Protocol); + /* parse returned result into more usable form */ + rc = parse_DFS_referrals(pSMBr, num_of_nodes, + target_nodes, nls_codepage); - for (i = 0; i < *num_of_nodes; i++) { - temp = ((char *)referrals) + - le16_to_cpu(referrals->DfsPathOffset); - /* BB update target_uncs pointers */ - referrals++; - } - } GetDFSRefExit: - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto getDFSRetry; @@ -5057,8 +5112,7 @@ setPermsRetry: if (rc) cFYI(1, ("SetPathInfo (perms) returned %d", rc)); - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto setPermsRetry; return rc; @@ -5280,8 +5334,7 @@ QAllEAsRetry: } } } - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QAllEAsRetry; @@ -5430,8 +5483,7 @@ QEARetry: } } } - if (pSMB) - cifs_buf_release(pSMB); + cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QEARetry;