diff options
Diffstat (limited to 'fs/cifs/cifssmb.c')
-rw-r--r-- | fs/cifs/cifssmb.c | 110 |
1 files changed, 51 insertions, 59 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a89c4cb4e6cf..a3d74fea1623 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -3067,7 +3067,6 @@ querySymLinkRetry: | |||
3067 | return rc; | 3067 | return rc; |
3068 | } | 3068 | } |
3069 | 3069 | ||
3070 | #ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL | ||
3071 | /* | 3070 | /* |
3072 | * Recent Windows versions now create symlinks more frequently | 3071 | * Recent Windows versions now create symlinks more frequently |
3073 | * and they use the "reparse point" mechanism below. We can of course | 3072 | * and they use the "reparse point" mechanism below. We can of course |
@@ -3079,18 +3078,22 @@ querySymLinkRetry: | |||
3079 | * it is not compiled in by default until callers fixed up and more tested. | 3078 | * it is not compiled in by default until callers fixed up and more tested. |
3080 | */ | 3079 | */ |
3081 | int | 3080 | int |
3082 | CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon, | 3081 | CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, |
3083 | const unsigned char *searchName, | 3082 | __u16 fid, char **symlinkinfo, |
3084 | char *symlinkinfo, const int buflen, __u16 fid, | 3083 | const struct nls_table *nls_codepage) |
3085 | const struct nls_table *nls_codepage) | ||
3086 | { | 3084 | { |
3087 | int rc = 0; | 3085 | int rc = 0; |
3088 | int bytes_returned; | 3086 | int bytes_returned; |
3089 | struct smb_com_transaction_ioctl_req *pSMB; | 3087 | struct smb_com_transaction_ioctl_req *pSMB; |
3090 | struct smb_com_transaction_ioctl_rsp *pSMBr; | 3088 | struct smb_com_transaction_ioctl_rsp *pSMBr; |
3089 | bool is_unicode; | ||
3090 | unsigned int sub_len; | ||
3091 | char *sub_start; | ||
3092 | struct reparse_data *reparse_buf; | ||
3093 | __u32 data_offset, data_count; | ||
3094 | char *end_of_smb; | ||
3091 | 3095 | ||
3092 | cifs_dbg(FYI, "In Windows reparse style QueryLink for path %s\n", | 3096 | cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid); |
3093 | searchName); | ||
3094 | rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, | 3097 | rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, |
3095 | (void **) &pSMBr); | 3098 | (void **) &pSMBr); |
3096 | if (rc) | 3099 | if (rc) |
@@ -3119,66 +3122,55 @@ CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon, | |||
3119 | (struct smb_hdr *) pSMBr, &bytes_returned, 0); | 3122 | (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
3120 | if (rc) { | 3123 | if (rc) { |
3121 | cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); | 3124 | cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); |
3122 | } else { /* decode response */ | 3125 | goto qreparse_out; |
3123 | __u32 data_offset = le32_to_cpu(pSMBr->DataOffset); | 3126 | } |
3124 | __u32 data_count = le32_to_cpu(pSMBr->DataCount); | ||
3125 | if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { | ||
3126 | /* BB also check enough total bytes returned */ | ||
3127 | rc = -EIO; /* bad smb */ | ||
3128 | goto qreparse_out; | ||
3129 | } | ||
3130 | if (data_count && (data_count < 2048)) { | ||
3131 | char *end_of_smb = 2 /* sizeof byte count */ + | ||
3132 | get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; | ||
3133 | |||
3134 | struct reparse_data *reparse_buf = | ||
3135 | (struct reparse_data *) | ||
3136 | ((char *)&pSMBr->hdr.Protocol | ||
3137 | + data_offset); | ||
3138 | if ((char *)reparse_buf >= end_of_smb) { | ||
3139 | rc = -EIO; | ||
3140 | goto qreparse_out; | ||
3141 | } | ||
3142 | if ((reparse_buf->LinkNamesBuf + | ||
3143 | reparse_buf->TargetNameOffset + | ||
3144 | reparse_buf->TargetNameLen) > end_of_smb) { | ||
3145 | cifs_dbg(FYI, "reparse buf beyond SMB\n"); | ||
3146 | rc = -EIO; | ||
3147 | goto qreparse_out; | ||
3148 | } | ||
3149 | 3127 | ||
3150 | if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { | 3128 | data_offset = le32_to_cpu(pSMBr->DataOffset); |
3151 | cifs_from_ucs2(symlinkinfo, (__le16 *) | 3129 | data_count = le32_to_cpu(pSMBr->DataCount); |
3152 | (reparse_buf->LinkNamesBuf + | 3130 | if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { |
3153 | reparse_buf->TargetNameOffset), | 3131 | /* BB also check enough total bytes returned */ |
3154 | buflen, | 3132 | rc = -EIO; /* bad smb */ |
3155 | reparse_buf->TargetNameLen, | 3133 | goto qreparse_out; |
3156 | nls_codepage, 0); | 3134 | } |
3157 | } else { /* ASCII names */ | 3135 | if (!data_count || (data_count > 2048)) { |
3158 | strncpy(symlinkinfo, | 3136 | rc = -EIO; |
3159 | reparse_buf->LinkNamesBuf + | 3137 | cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); |
3160 | reparse_buf->TargetNameOffset, | 3138 | goto qreparse_out; |
3161 | min_t(const int, buflen, | 3139 | } |
3162 | reparse_buf->TargetNameLen)); | 3140 | end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; |
3163 | } | 3141 | reparse_buf = (struct reparse_data *) |
3164 | } else { | 3142 | ((char *)&pSMBr->hdr.Protocol + data_offset); |
3165 | rc = -EIO; | 3143 | if ((char *)reparse_buf >= end_of_smb) { |
3166 | cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); | 3144 | rc = -EIO; |
3167 | } | 3145 | goto qreparse_out; |
3168 | symlinkinfo[buflen] = 0; /* just in case so the caller | ||
3169 | does not go off the end of the buffer */ | ||
3170 | cifs_dbg(FYI, "readlink result - %s\n", symlinkinfo); | ||
3171 | } | 3146 | } |
3147 | if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset + | ||
3148 | reparse_buf->PrintNameLength) > end_of_smb) { | ||
3149 | cifs_dbg(FYI, "reparse buf beyond SMB\n"); | ||
3150 | rc = -EIO; | ||
3151 | goto qreparse_out; | ||
3152 | } | ||
3153 | sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer; | ||
3154 | sub_len = reparse_buf->SubstituteNameLength; | ||
3155 | if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) | ||
3156 | is_unicode = true; | ||
3157 | else | ||
3158 | is_unicode = false; | ||
3172 | 3159 | ||
3160 | /* BB FIXME investigate remapping reserved chars here */ | ||
3161 | *symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, | ||
3162 | nls_codepage); | ||
3163 | if (!*symlinkinfo) | ||
3164 | rc = -ENOMEM; | ||
3173 | qreparse_out: | 3165 | qreparse_out: |
3174 | cifs_buf_release(pSMB); | 3166 | cifs_buf_release(pSMB); |
3175 | 3167 | ||
3176 | /* Note: On -EAGAIN error only caller can retry on handle based calls | 3168 | /* |
3177 | since file handle passed in no longer valid */ | 3169 | * Note: On -EAGAIN error only caller can retry on handle based calls |
3178 | 3170 | * since file handle passed in no longer valid. | |
3171 | */ | ||
3179 | return rc; | 3172 | return rc; |
3180 | } | 3173 | } |
3181 | #endif /* CIFS_SYMLINK_EXPERIMENTAL */ /* BB temporarily unused */ | ||
3182 | 3174 | ||
3183 | #ifdef CONFIG_CIFS_POSIX | 3175 | #ifdef CONFIG_CIFS_POSIX |
3184 | 3176 | ||