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