Fixed parsing of mount options when doing DFS submount

Since these hit the same routines, and are relatively small, it is easier to review
them as one patch.

Fixed incorrect handling of the last option in some cases
Fixed prefixpath handling convert path_consumed into host depended string length (in bytes)
Use non default separator if it is provided in the original mount options

Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Igor Mammedov <niallain@gmail.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>

authored by Igor Mammedov and committed by Steve French 2c55608f ab3f9929

+83 -27
+47 -24
fs/cifs/cifs_dfs_ref.c
··· 106 106 /** 107 107 * compose_mount_options - creates mount options for refferral 108 108 * @sb_mountdata: parent/root DFS mount options (template) 109 - * @ref_unc: refferral server UNC 109 + * @dentry: point where we are going to mount 110 + * @ref: server's referral 110 111 * @devname: pointer for saving device name 111 112 * 112 113 * creates mount options for submount based on template options sb_mountdata ··· 117 116 * Caller is responcible for freeing retunrned value if it is not error. 118 117 */ 119 118 static char *compose_mount_options(const char *sb_mountdata, 120 - const char *ref_unc, 119 + struct dentry *dentry, 120 + const struct dfs_info3_param *ref, 121 121 char **devname) 122 122 { 123 123 int rc; ··· 128 126 char *srvIP = NULL; 129 127 char sep = ','; 130 128 int off, noff; 129 + char *fullpath; 131 130 132 131 if (sb_mountdata == NULL) 133 132 return ERR_PTR(-EINVAL); 134 133 135 - *devname = cifs_get_share_name(ref_unc); 134 + *devname = cifs_get_share_name(ref->node_name); 136 135 rc = dns_resolve_server_name_to_ip(*devname, &srvIP); 137 136 if (rc != 0) { 138 137 cERROR(1, ("%s: Failed to resolve server part of %s to IP", ··· 141 138 mountdata = ERR_PTR(rc); 142 139 goto compose_mount_options_out; 143 140 } 144 - md_len = strlen(sb_mountdata) + strlen(srvIP) + strlen(ref_unc) + 3; 141 + /* md_len = strlen(...) + 12 for 'sep+prefixpath=' 142 + * assuming that we have 'unc=' and 'ip=' in 143 + * the original sb_mountdata 144 + */ 145 + md_len = strlen(sb_mountdata) + strlen(srvIP) + 146 + strlen(ref->node_name) + 12; 145 147 mountdata = kzalloc(md_len+1, GFP_KERNEL); 146 148 if (mountdata == NULL) { 147 149 mountdata = ERR_PTR(-ENOMEM); ··· 160 152 strncpy(mountdata, sb_mountdata, 5); 161 153 off += 5; 162 154 } 163 - while ((tkn_e = strchr(sb_mountdata+off, sep))) { 164 - noff = (tkn_e - (sb_mountdata+off)) + 1; 165 - if (strnicmp(sb_mountdata+off, "unc=", 4) == 0) { 155 + 156 + do { 157 + tkn_e = strchr(sb_mountdata + off, sep); 158 + if (tkn_e == NULL) 159 + noff = strlen(sb_mountdata + off); 160 + else 161 + noff = tkn_e - (sb_mountdata + off) + 1; 162 + 163 + if (strnicmp(sb_mountdata + off, "unc=", 4) == 0) { 166 164 off += noff; 167 165 continue; 168 166 } 169 - if (strnicmp(sb_mountdata+off, "ip=", 3) == 0) { 167 + if (strnicmp(sb_mountdata + off, "ip=", 3) == 0) { 170 168 off += noff; 171 169 continue; 172 170 } 173 - if (strnicmp(sb_mountdata+off, "prefixpath=", 3) == 0) { 171 + if (strnicmp(sb_mountdata + off, "prefixpath=", 11) == 0) { 174 172 off += noff; 175 173 continue; 176 174 } 177 - strncat(mountdata, sb_mountdata+off, noff); 175 + strncat(mountdata, sb_mountdata + off, noff); 178 176 off += noff; 179 - } 180 - strcat(mountdata, sb_mountdata+off); 177 + } while (tkn_e); 178 + strcat(mountdata, sb_mountdata + off); 181 179 mountdata[md_len] = '\0'; 182 180 183 181 /* copy new IP and ref share name */ 184 - strcat(mountdata, ",ip="); 182 + if (mountdata[strlen(mountdata) - 1] != sep) 183 + strncat(mountdata, &sep, 1); 184 + strcat(mountdata, "ip="); 185 185 strcat(mountdata, srvIP); 186 - strcat(mountdata, ",unc="); 186 + strncat(mountdata, &sep, 1); 187 + strcat(mountdata, "unc="); 187 188 strcat(mountdata, *devname); 188 189 189 190 /* find & copy prefixpath */ 190 - tkn_e = strchr(ref_unc+2, '\\'); 191 - if (tkn_e) { 192 - tkn_e = strchr(tkn_e+1, '\\'); 193 - if (tkn_e) { 194 - strcat(mountdata, ",prefixpath="); 195 - strcat(mountdata, tkn_e+1); 196 - } 191 + tkn_e = strchr(ref->node_name + 2, '\\'); 192 + if (tkn_e == NULL) /* invalid unc, missing share name*/ 193 + goto compose_mount_options_out; 194 + 195 + fullpath = build_path_from_dentry(dentry); 196 + tkn_e = strchr(tkn_e + 1, '\\'); 197 + if (tkn_e || strlen(fullpath) - (ref->path_consumed)) { 198 + strncat(mountdata, &sep, 1); 199 + strcat(mountdata, "prefixpath="); 200 + if (tkn_e) 201 + strcat(mountdata, tkn_e + 1); 202 + strcat(mountdata, fullpath + (ref->path_consumed)); 197 203 } 204 + kfree(fullpath); 198 205 199 206 /*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/ 200 207 /*cFYI(1, ("%s: submount mountdata: %s", __func__, mountdata ));*/ ··· 221 198 222 199 223 200 static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, 224 - struct dentry *dentry, char *ref_unc) 201 + struct dentry *dentry, const struct dfs_info3_param *ref) 225 202 { 226 203 struct cifs_sb_info *cifs_sb; 227 204 struct vfsmount *mnt; ··· 230 207 231 208 cifs_sb = CIFS_SB(dentry->d_inode->i_sb); 232 209 mountdata = compose_mount_options(cifs_sb->mountdata, 233 - ref_unc, &devname); 210 + dentry, ref, &devname); 234 211 235 212 if (IS_ERR(mountdata)) 236 213 return (struct vfsmount *)mountdata; ··· 333 310 } 334 311 mnt = cifs_dfs_do_refmount(nd->path.mnt, 335 312 nd->path.dentry, 336 - referrals[i].node_name); 313 + referrals + i); 337 314 cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", 338 315 __func__, 339 316 referrals[i].node_name, mnt));
+36 -3
fs/cifs/cifssmb.c
··· 3899 3899 return rc; 3900 3900 } 3901 3901 3902 + /* computes length of UCS string converted to host codepage 3903 + * @src: UCS string 3904 + * @maxlen: length of the input string in UCS characters 3905 + * (not in bytes) 3906 + * 3907 + * return: size of input string in host codepage 3908 + */ 3909 + static int hostlen_fromUCS(const __le16 *src, const int maxlen, 3910 + const struct nls_table *nls_codepage) { 3911 + int i; 3912 + int hostlen = 0; 3913 + char to[4]; 3914 + int charlen; 3915 + for (i = 0; (i < maxlen) && src[i]; ++i) { 3916 + charlen = nls_codepage->uni2char(le16_to_cpu(src[i]), 3917 + to, NLS_MAX_CHARSET_SIZE); 3918 + hostlen += charlen > 0 ? charlen : 1; 3919 + } 3920 + return hostlen; 3921 + } 3922 + 3902 3923 /* parses DFS refferal V3 structure 3903 3924 * caller is responsible for freeing target_nodes 3904 3925 * returns: ··· 3930 3909 parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, 3931 3910 unsigned int *num_of_nodes, 3932 3911 struct dfs_info3_param **target_nodes, 3933 - const struct nls_table *nls_codepage) 3912 + const struct nls_table *nls_codepage, int remap, 3913 + const char *searchName) 3934 3914 { 3935 3915 int i, rc = 0; 3936 3916 char *data_end; ··· 3982 3960 struct dfs_info3_param *node = (*target_nodes)+i; 3983 3961 3984 3962 node->flags = le16_to_cpu(pSMBr->DFSFlags); 3985 - node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); 3963 + if (is_unicode) { 3964 + __le16 *tmp = kmalloc(strlen(searchName)*2, GFP_KERNEL); 3965 + cifsConvertToUCS((__le16 *) tmp, searchName, 3966 + PATH_MAX, nls_codepage, remap); 3967 + node->path_consumed = hostlen_fromUCS(tmp, 3968 + le16_to_cpu(pSMBr->PathConsumed)/2, 3969 + nls_codepage); 3970 + kfree(tmp); 3971 + } else 3972 + node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); 3973 + 3986 3974 node->server_type = le16_to_cpu(ref->ServerType); 3987 3975 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); 3988 3976 ··· 4125 4093 4126 4094 /* parse returned result into more usable form */ 4127 4095 rc = parse_DFS_referrals(pSMBr, num_of_nodes, 4128 - target_nodes, nls_codepage); 4096 + target_nodes, nls_codepage, remap, 4097 + searchName); 4129 4098 4130 4099 GetDFSRefExit: 4131 4100 cifs_buf_release(pSMB);