Commit d9deef0a3f38bcc1c155e8d9a8b522404e5e648c
Committed by
Steve French
1 parent
9c9c29e1af
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
cifs: fix composing of mount options for DFS referrals
With the change to ignore the unc= and prefixpath= mount options, there is no longer any need to add them to the options string when mounting. By the same token, we now need to build a device name that includes the prefixpath when mounting. To make things neater, the delimiters on the devicename are changed to '/' since that's preferred when mounting anyway. v2: fix some comments and don't bother looking at whether there is a prepath in the ref->node_name when deciding whether to pass a prepath to cifs_build_devname. v3: rebase on top of potential buffer overrun fix for stable Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Showing 2 changed files with 73 additions and 72 deletions Side-by-side Diff
fs/cifs/cifs_dfs_ref.c
... | ... | @@ -49,58 +49,74 @@ |
49 | 49 | } |
50 | 50 | |
51 | 51 | /** |
52 | - * cifs_get_share_name - extracts share name from UNC | |
53 | - * @node_name: pointer to UNC string | |
52 | + * cifs_build_devname - build a devicename from a UNC and optional prepath | |
53 | + * @nodename: pointer to UNC string | |
54 | + * @prepath: pointer to prefixpath (or NULL if there isn't one) | |
54 | 55 | * |
55 | - * Extracts sharename form full UNC. | |
56 | - * i.e. strips from UNC trailing path that is not part of share | |
57 | - * name and fixup missing '\' in the beginning of DFS node refferal | |
58 | - * if necessary. | |
59 | - * Returns pointer to share name on success or ERR_PTR on error. | |
60 | - * Caller is responsible for freeing returned string. | |
56 | + * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer | |
57 | + * big enough to hold the final thing. Copy the UNC from the nodename, and | |
58 | + * concatenate the prepath onto the end of it if there is one. | |
59 | + * | |
60 | + * Returns pointer to the built string, or a ERR_PTR. Caller is responsible | |
61 | + * for freeing the returned string. | |
61 | 62 | */ |
62 | -static char *cifs_get_share_name(const char *node_name) | |
63 | +static char * | |
64 | +cifs_build_devname(char *nodename, const char *prepath) | |
63 | 65 | { |
64 | - int len; | |
65 | - char *UNC; | |
66 | - char *pSep; | |
66 | + size_t pplen; | |
67 | + size_t unclen; | |
68 | + char *dev; | |
69 | + char *pos; | |
67 | 70 | |
68 | - len = strlen(node_name); | |
69 | - UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */, | |
70 | - GFP_KERNEL); | |
71 | - if (!UNC) | |
72 | - return ERR_PTR(-ENOMEM); | |
71 | + /* skip over any preceding delimiters */ | |
72 | + nodename += strspn(nodename, "\\"); | |
73 | + if (!*nodename) | |
74 | + return ERR_PTR(-EINVAL); | |
73 | 75 | |
74 | - /* get share name and server name */ | |
75 | - if (node_name[1] != '\\') { | |
76 | - UNC[0] = '\\'; | |
77 | - strncpy(UNC+1, node_name, len); | |
78 | - len++; | |
79 | - UNC[len] = 0; | |
80 | - } else { | |
81 | - strncpy(UNC, node_name, len); | |
82 | - UNC[len] = 0; | |
83 | - } | |
76 | + /* get length of UNC and set pos to last char */ | |
77 | + unclen = strlen(nodename); | |
78 | + pos = nodename + unclen - 1; | |
84 | 79 | |
85 | - /* find server name end */ | |
86 | - pSep = memchr(UNC+2, '\\', len-2); | |
87 | - if (!pSep) { | |
88 | - cifs_dbg(VFS, "%s: no server name end in node name: %s\n", | |
89 | - __func__, node_name); | |
90 | - kfree(UNC); | |
91 | - return ERR_PTR(-EINVAL); | |
80 | + /* trim off any trailing delimiters */ | |
81 | + while (*pos == '\\') { | |
82 | + --pos; | |
83 | + --unclen; | |
92 | 84 | } |
93 | 85 | |
94 | - /* find sharename end */ | |
95 | - pSep++; | |
96 | - pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC)); | |
97 | - if (pSep) { | |
98 | - /* trim path up to sharename end | |
99 | - * now we have share name in UNC */ | |
100 | - *pSep = 0; | |
86 | + /* allocate a buffer: | |
87 | + * +2 for preceding "//" | |
88 | + * +1 for delimiter between UNC and prepath | |
89 | + * +1 for trailing NULL | |
90 | + */ | |
91 | + pplen = prepath ? strlen(prepath) : 0; | |
92 | + dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL); | |
93 | + if (!dev) | |
94 | + return ERR_PTR(-ENOMEM); | |
95 | + | |
96 | + pos = dev; | |
97 | + /* add the initial "//" */ | |
98 | + *pos = '/'; | |
99 | + ++pos; | |
100 | + *pos = '/'; | |
101 | + ++pos; | |
102 | + | |
103 | + /* copy in the UNC portion from referral */ | |
104 | + memcpy(pos, nodename, unclen); | |
105 | + pos += unclen; | |
106 | + | |
107 | + /* copy the prefixpath remainder (if there is one) */ | |
108 | + if (pplen) { | |
109 | + *pos = '/'; | |
110 | + ++pos; | |
111 | + memcpy(pos, prepath, pplen); | |
112 | + pos += pplen; | |
101 | 113 | } |
102 | 114 | |
103 | - return UNC; | |
115 | + /* NULL terminator */ | |
116 | + *pos = '\0'; | |
117 | + | |
118 | + convert_delimiter(dev, '/'); | |
119 | + return dev; | |
104 | 120 | } |
105 | 121 | |
106 | 122 | |
... | ... | @@ -124,6 +140,7 @@ |
124 | 140 | { |
125 | 141 | int rc; |
126 | 142 | char *mountdata = NULL; |
143 | + const char *prepath = NULL; | |
127 | 144 | int md_len; |
128 | 145 | char *tkn_e; |
129 | 146 | char *srvIP = NULL; |
... | ... | @@ -133,7 +150,10 @@ |
133 | 150 | if (sb_mountdata == NULL) |
134 | 151 | return ERR_PTR(-EINVAL); |
135 | 152 | |
136 | - *devname = cifs_get_share_name(ref->node_name); | |
153 | + if (strlen(fullpath) - ref->path_consumed) | |
154 | + prepath = fullpath + ref->path_consumed; | |
155 | + | |
156 | + *devname = cifs_build_devname(ref->node_name, prepath); | |
137 | 157 | if (IS_ERR(*devname)) { |
138 | 158 | rc = PTR_ERR(*devname); |
139 | 159 | *devname = NULL; |
140 | 160 | |
... | ... | @@ -147,13 +167,14 @@ |
147 | 167 | goto compose_mount_options_err; |
148 | 168 | } |
149 | 169 | |
150 | - /* md_len = strlen(...) + 12 for 'sep+prefixpath=' | |
151 | - * assuming that we have 'unc=' and 'ip=' in | |
152 | - * the original sb_mountdata | |
170 | + /* | |
171 | + * In most cases, we'll be building a shorter string than the original, | |
172 | + * but we do have to assume that the address in the ip= option may be | |
173 | + * much longer than the original. Add the max length of an address | |
174 | + * string to the length of the original string to allow for worst case. | |
153 | 175 | */ |
154 | - md_len = strlen(sb_mountdata) + rc + strlen(ref->node_name) + 12 + | |
155 | - INET6_ADDRSTRLEN; | |
156 | - mountdata = kzalloc(md_len+1, GFP_KERNEL); | |
176 | + md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN; | |
177 | + mountdata = kzalloc(md_len + 1, GFP_KERNEL); | |
157 | 178 | if (mountdata == NULL) { |
158 | 179 | rc = -ENOMEM; |
159 | 180 | goto compose_mount_options_err; |
... | ... | @@ -197,26 +218,6 @@ |
197 | 218 | strncat(mountdata, &sep, 1); |
198 | 219 | strcat(mountdata, "ip="); |
199 | 220 | strcat(mountdata, srvIP); |
200 | - strncat(mountdata, &sep, 1); | |
201 | - strcat(mountdata, "unc="); | |
202 | - strcat(mountdata, *devname); | |
203 | - | |
204 | - /* find & copy prefixpath */ | |
205 | - tkn_e = strchr(ref->node_name + 2, '\\'); | |
206 | - if (tkn_e == NULL) { | |
207 | - /* invalid unc, missing share name*/ | |
208 | - rc = -EINVAL; | |
209 | - goto compose_mount_options_err; | |
210 | - } | |
211 | - | |
212 | - tkn_e = strchr(tkn_e + 1, '\\'); | |
213 | - if (tkn_e || (strlen(fullpath) - ref->path_consumed)) { | |
214 | - strncat(mountdata, &sep, 1); | |
215 | - strcat(mountdata, "prefixpath="); | |
216 | - if (tkn_e) | |
217 | - strcat(mountdata, tkn_e + 1); | |
218 | - strcat(mountdata, fullpath + ref->path_consumed); | |
219 | - } | |
220 | 221 | |
221 | 222 | /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ |
222 | 223 | /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ |
fs/cifs/dns_resolve.c
... | ... | @@ -34,7 +34,7 @@ |
34 | 34 | |
35 | 35 | /** |
36 | 36 | * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address. |
37 | - * @unc: UNC path specifying the server | |
37 | + * @unc: UNC path specifying the server (with '/' as delimiter) | |
38 | 38 | * @ip_addr: Where to return the IP address. |
39 | 39 | * |
40 | 40 | * The IP address will be returned in string form, and the caller is |
... | ... | @@ -64,7 +64,7 @@ |
64 | 64 | hostname = unc + 2; |
65 | 65 | |
66 | 66 | /* Search for server name delimiter */ |
67 | - sep = memchr(hostname, '\\', len); | |
67 | + sep = memchr(hostname, '/', len); | |
68 | 68 | if (sep) |
69 | 69 | len = sep - hostname; |
70 | 70 | else |