cifs: fix error handling in parse_DFS_referrals
[safe/jmp/linux-2.6] / fs / cifs / cifssmb.c
index bc09c99..5759ba5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifssmb.c
  *
- *   Copyright (C) International Business Machines  Corp., 2002,2008
+ *   Copyright (C) International Business Machines  Corp., 2002,2009
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   Contains the routines for constructing the SMB PDUs themselves
@@ -81,41 +81,6 @@ 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 */
 static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
@@ -484,6 +449,14 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                cFYI(1, ("Kerberos only mechanism, enable extended security"));
                pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
        }
+#ifdef CONFIG_CIFS_EXPERIMENTAL
+       else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
+               pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
+       else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
+               cFYI(1, ("NTLMSSP only mechanism, enable extended security"));
+               pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
+       }
+#endif
 
        count = 0;
        for (i = 0; i < CIFS_NUM_PROT; i++) {
@@ -620,6 +593,8 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                server->secType = NTLMv2;
        else if (secFlags & CIFSSEC_MAY_KRB5)
                server->secType = Kerberos;
+       else if (secFlags & CIFSSEC_MAY_NTLMSSP)
+               server->secType = NTLMSSP;
        else if (secFlags & CIFSSEC_MAY_LANMAN)
                server->secType = LANMAN;
 /* #ifdef CONFIG_CIFS_EXPERIMENTAL
@@ -1626,6 +1601,8 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
        int smb_hdr_len;
        int resp_buf_type = 0;
 
+       *nbytes = 0;
+
        cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
 
        if (tcon->ses->capabilities & CAP_LARGE_FILES) {
@@ -1682,11 +1659,9 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
        cifs_stats_inc(&tcon->num_writes);
        if (rc) {
                cFYI(1, ("Send error Write2 = %d", rc));
-               *nbytes = 0;
        } else if (resp_buf_type == 0) {
                /* presumably this can not happen, but best to be safe */
                rc = -EIO;
-               *nbytes = 0;
        } else {
                WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
                *nbytes = le16_to_cpu(pSMBr->CountHigh);
@@ -2417,8 +2392,7 @@ winCreateHardLinkRetry:
 
 int
 CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
-                       const unsigned char *searchName,
-                       char *symlinkinfo, const int buflen,
+                       const unsigned char *searchName, char **symlinkinfo,
                        const struct nls_table *nls_codepage)
 {
 /* SMB_QUERY_FILE_UNIX_LINK */
@@ -2428,6 +2402,7 @@ CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
        int bytes_returned;
        int name_len;
        __u16 params, byte_count;
+       char *data_start;
 
        cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
 
@@ -2482,30 +2457,26 @@ querySymLinkRetry:
                /* 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 */
+               if (rc || (pSMBr->ByteCount < 2))
+                       rc = -EIO;
                else {
-                       __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
-                       __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+                       bool is_unicode;
+                       u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+
+                       data_start = ((char *) &pSMBr->hdr.Protocol) +
+                                          le16_to_cpu(pSMBr->t2.DataOffset);
+
+                       if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
+                               is_unicode = true;
+                       else
+                               is_unicode = false;
 
-                       if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
-                               name_len = UniStrnlen((wchar_t *) ((char *)
-                                       &pSMBr->hdr.Protocol + data_offset),
-                                       min_t(const int, buflen, count) / 2);
                        /* BB FIXME investigate remapping reserved chars here */
-                               cifs_strfromUCS_le(symlinkinfo,
-                                       (__le16 *) ((char *)&pSMBr->hdr.Protocol
-                                                       + data_offset),
-                                       name_len, nls_codepage);
-                       } else {
-                               strncpy(symlinkinfo,
-                                       (char *) &pSMBr->hdr.Protocol +
-                                               data_offset,
-                                       min_t(const int, buflen, count));
-                       }
-                       symlinkinfo[buflen] = 0;
-       /* just in case so calling code does not go off the end of buffer */
+                       *symlinkinfo = cifs_strndup_from_ucs(data_start, count,
+                                                   is_unicode, nls_codepage);
+                       if (!symlinkinfo)
+                               rc = -ENOMEM;
                }
        }
        cifs_buf_release(pSMB);
@@ -2603,7 +2574,6 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata,
        *pparmlen = parm_count;
        return 0;
 }
-#endif /* CIFS_EXPERIMENTAL */
 
 int
 CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
@@ -2613,7 +2583,6 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
 {
        int rc = 0;
        int bytes_returned;
-       int name_len;
        struct smb_com_transaction_ioctl_req *pSMB;
        struct smb_com_transaction_ioctl_rsp *pSMBr;
 
@@ -2650,59 +2619,55 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
        } else {                /* decode response */
                __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
                __u32 data_count = le32_to_cpu(pSMBr->DataCount);
-               if ((pSMBr->ByteCount < 2) || (data_offset > 512))
+               if ((pSMBr->ByteCount < 2) || (data_offset > 512)) {
                /* BB also check enough total bytes returned */
                        rc = -EIO;      /* bad smb */
-               else {
-                       if (data_count && (data_count < 2048)) {
-                               char *end_of_smb = 2 /* sizeof byte count */ +
-                                               pSMBr->ByteCount +
-                                               (char *)&pSMBr->ByteCount;
+                       goto qreparse_out;
+               }
+               if (data_count && (data_count < 2048)) {
+                       char *end_of_smb = 2 /* sizeof byte count */ +
+                               pSMBr->ByteCount + (char *)&pSMBr->ByteCount;
 
-                               struct reparse_data *reparse_buf =
+                       struct reparse_data *reparse_buf =
                                                (struct reparse_data *)
                                                ((char *)&pSMBr->hdr.Protocol
                                                                 + data_offset);
-                               if ((char *)reparse_buf >= end_of_smb) {
-                                       rc = -EIO;
-                                       goto qreparse_out;
-                               }
-                               if ((reparse_buf->LinkNamesBuf +
-                                       reparse_buf->TargetNameOffset +
-                                       reparse_buf->TargetNameLen) >
-                                               end_of_smb) {
-                                       cFYI(1, ("reparse buf beyond SMB"));
-                                       rc = -EIO;
-                                       goto qreparse_out;
-                               }
+                       if ((char *)reparse_buf >= end_of_smb) {
+                               rc = -EIO;
+                               goto qreparse_out;
+                       }
+                       if ((reparse_buf->LinkNamesBuf +
+                               reparse_buf->TargetNameOffset +
+                               reparse_buf->TargetNameLen) > end_of_smb) {
+                               cFYI(1, ("reparse buf beyond SMB"));
+                               rc = -EIO;
+                               goto qreparse_out;
+                       }
 
-                               if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
-                                       name_len = UniStrnlen((wchar_t *)
+                       if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
+                               cifs_from_ucs2(symlinkinfo, (__le16 *)
                                                (reparse_buf->LinkNamesBuf +
                                                reparse_buf->TargetNameOffset),
-                                               min(buflen/2,
-                                               reparse_buf->TargetNameLen / 2));
-                                       cifs_strfromUCS_le(symlinkinfo,
-                                               (__le16 *) (reparse_buf->LinkNamesBuf +
-                                               reparse_buf->TargetNameOffset),
-                                               name_len, nls_codepage);
-                               } else { /* ASCII names */
-                                       strncpy(symlinkinfo,
-                                               reparse_buf->LinkNamesBuf +
-                                               reparse_buf->TargetNameOffset,
-                                               min_t(const int, buflen,
-                                                  reparse_buf->TargetNameLen));
-                               }
-                       } else {
-                               rc = -EIO;
-                               cFYI(1, ("Invalid return data count on "
-                                        "get reparse info ioctl"));
+                                               buflen,
+                                               reparse_buf->TargetNameLen,
+                                               nls_codepage, 0);
+                       } else { /* ASCII names */
+                               strncpy(symlinkinfo,
+                                       reparse_buf->LinkNamesBuf +
+                                       reparse_buf->TargetNameOffset,
+                                       min_t(const int, buflen,
+                                          reparse_buf->TargetNameLen));
                        }
-                       symlinkinfo[buflen] = 0; /* just in case so the caller
-                                       does not go off the end of the buffer */
-                       cFYI(1, ("readlink result - %s", symlinkinfo));
+               } else {
+                       rc = -EIO;
+                       cFYI(1, ("Invalid return data count on "
+                                "get reparse info ioctl"));
                }
+               symlinkinfo[buflen] = 0; /* just in case so the caller
+                                       does not go off the end of the buffer */
+               cFYI(1, ("readlink result - %s", symlinkinfo));
        }
+
 qreparse_out:
        cifs_buf_release(pSMB);
 
@@ -2711,6 +2676,7 @@ qreparse_out:
 
        return rc;
 }
+#endif /* CIFS_EXPERIMENTAL */
 
 #ifdef CONFIG_CIFS_POSIX
 
@@ -3918,7 +3884,7 @@ GetInodeNumberRetry:
                        }
                        pfinfo = (struct file_internal_info *)
                                (data_offset + (char *) &pSMBr->hdr.Protocol);
-                       *inode_number = pfinfo->UniqueId;
+                       *inode_number = le64_to_cpu(pfinfo->UniqueId);
                }
        }
 GetInodeNumOut:
@@ -3928,27 +3894,6 @@ GetInodeNumOut:
        return rc;
 }
 
-/* computes length of UCS string converted to host codepage
- * @src:       UCS string
- * @maxlen:    length of the input string in UCS characters
- *             (not in bytes)
- *
- * return:     size of input string in host codepage
- */
-static int hostlen_fromUCS(const __le16 *src, const int maxlen,
-               const struct nls_table *nls_codepage) {
-       int i;
-       int hostlen = 0;
-       char to[4];
-       int charlen;
-       for (i = 0; (i < maxlen) && src[i]; ++i) {
-               charlen = nls_codepage->uni2char(le16_to_cpu(src[i]),
-                               to, NLS_MAX_CHARSET_SIZE);
-               hostlen += charlen > 0 ? charlen : 1;
-       }
-       return hostlen;
-}
-
 /* parses DFS refferal V3 structure
  * caller is responsible for freeing target_nodes
  * returns:
@@ -3994,7 +3939,7 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
 
        cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n",
                        *num_of_nodes,
-                       le16_to_cpu(pSMBr->DFSFlags)));
+                       le32_to_cpu(pSMBr->DFSFlags)));
 
        *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
                        *num_of_nodes, GFP_KERNEL);
@@ -4010,14 +3955,14 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
                int max_len;
                struct dfs_info3_param *node = (*target_nodes)+i;
 
-               node->flags = le16_to_cpu(pSMBr->DFSFlags);
+               node->flags = le32_to_cpu(pSMBr->DFSFlags);
                if (is_unicode) {
                        __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
                                                GFP_KERNEL);
                        cifsConvertToUCS((__le16 *) tmp, searchName,
                                        PATH_MAX, nls_codepage, remap);
-                       node->path_consumed = hostlen_fromUCS(tmp,
-                                       le16_to_cpu(pSMBr->PathConsumed)/2,
+                       node->path_consumed = cifs_ucs2_bytes(tmp,
+                                       le16_to_cpu(pSMBr->PathConsumed),
                                        nls_codepage);
                        kfree(tmp);
                } else
@@ -4029,20 +3974,20 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
                /* 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)
+               node->path_name = cifs_strndup_from_ucs(temp, max_len,
+                                                     is_unicode, nls_codepage);
+               if (!node->path_name) {
+                       rc = -ENOMEM;
                        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);
+               node->node_name = cifs_strndup_from_ucs(temp, max_len,
+                                                     is_unicode, nls_codepage);
+               if (!node->node_name)
+                       rc = -ENOMEM;
        }
 
 parse_DFS_referrals_exit: