diff options
Diffstat (limited to 'fs/cifs/netmisc.c')
-rw-r--r-- | fs/cifs/netmisc.c | 63 |
1 files changed, 44 insertions, 19 deletions
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index d35d52889cb5..f97851119e6c 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c | |||
@@ -61,6 +61,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = { | |||
61 | {ERRremcd, -EACCES}, | 61 | {ERRremcd, -EACCES}, |
62 | {ERRdiffdevice, -EXDEV}, | 62 | {ERRdiffdevice, -EXDEV}, |
63 | {ERRnofiles, -ENOENT}, | 63 | {ERRnofiles, -ENOENT}, |
64 | {ERRwriteprot, -EROFS}, | ||
64 | {ERRbadshare, -ETXTBSY}, | 65 | {ERRbadshare, -ETXTBSY}, |
65 | {ERRlock, -EACCES}, | 66 | {ERRlock, -EACCES}, |
66 | {ERRunsup, -EINVAL}, | 67 | {ERRunsup, -EINVAL}, |
@@ -139,17 +140,18 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = { | |||
139 | * Returns 0 on failure. | 140 | * Returns 0 on failure. |
140 | */ | 141 | */ |
141 | static int | 142 | static int |
142 | cifs_inet_pton(const int address_family, const char *cp, void *dst) | 143 | cifs_inet_pton(const int address_family, const char *cp, int len, void *dst) |
143 | { | 144 | { |
144 | int ret = 0; | 145 | int ret = 0; |
145 | 146 | ||
146 | /* calculate length by finding first slash or NULL */ | 147 | /* calculate length by finding first slash or NULL */ |
147 | if (address_family == AF_INET) | 148 | if (address_family == AF_INET) |
148 | ret = in4_pton(cp, -1 /* len */, dst, '\\', NULL); | 149 | ret = in4_pton(cp, len, dst, '\\', NULL); |
149 | else if (address_family == AF_INET6) | 150 | else if (address_family == AF_INET6) |
150 | ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL); | 151 | ret = in6_pton(cp, len, dst , '\\', NULL); |
151 | 152 | ||
152 | cFYI(DBG2, "address conversion returned %d for %s", ret, cp); | 153 | cFYI(DBG2, "address conversion returned %d for %*.*s", |
154 | ret, len, len, cp); | ||
153 | if (ret > 0) | 155 | if (ret > 0) |
154 | ret = 1; | 156 | ret = 1; |
155 | return ret; | 157 | return ret; |
@@ -164,43 +166,66 @@ cifs_inet_pton(const int address_family, const char *cp, void *dst) | |||
164 | * Returns 0 on failure. | 166 | * Returns 0 on failure. |
165 | */ | 167 | */ |
166 | int | 168 | int |
167 | cifs_convert_address(char *src, void *dst) | 169 | cifs_convert_address(struct sockaddr *dst, const char *src, int len) |
168 | { | 170 | { |
169 | int rc; | 171 | int rc, alen, slen; |
170 | char *pct, *endp; | 172 | const char *pct; |
173 | char *endp, scope_id[13]; | ||
171 | struct sockaddr_in *s4 = (struct sockaddr_in *) dst; | 174 | struct sockaddr_in *s4 = (struct sockaddr_in *) dst; |
172 | struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; | 175 | struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; |
173 | 176 | ||
174 | /* IPv4 address */ | 177 | /* IPv4 address */ |
175 | if (cifs_inet_pton(AF_INET, src, &s4->sin_addr.s_addr)) { | 178 | if (cifs_inet_pton(AF_INET, src, len, &s4->sin_addr.s_addr)) { |
176 | s4->sin_family = AF_INET; | 179 | s4->sin_family = AF_INET; |
177 | return 1; | 180 | return 1; |
178 | } | 181 | } |
179 | 182 | ||
180 | /* temporarily terminate string */ | 183 | /* attempt to exclude the scope ID from the address part */ |
181 | pct = strchr(src, '%'); | 184 | pct = memchr(src, '%', len); |
182 | if (pct) | 185 | alen = pct ? pct - src : len; |
183 | *pct = '\0'; | ||
184 | |||
185 | rc = cifs_inet_pton(AF_INET6, src, &s6->sin6_addr.s6_addr); | ||
186 | |||
187 | /* repair temp termination (if any) and make pct point to scopeid */ | ||
188 | if (pct) | ||
189 | *pct++ = '%'; | ||
190 | 186 | ||
187 | rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr); | ||
191 | if (!rc) | 188 | if (!rc) |
192 | return rc; | 189 | return rc; |
193 | 190 | ||
194 | s6->sin6_family = AF_INET6; | 191 | s6->sin6_family = AF_INET6; |
195 | if (pct) { | 192 | if (pct) { |
193 | /* grab the scope ID */ | ||
194 | slen = len - (alen + 1); | ||
195 | if (slen <= 0 || slen > 12) | ||
196 | return 0; | ||
197 | memcpy(scope_id, pct + 1, slen); | ||
198 | scope_id[slen] = '\0'; | ||
199 | |||
196 | s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0); | 200 | s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0); |
197 | if (!*pct || *endp) | 201 | if (endp != scope_id + slen) |
198 | return 0; | 202 | return 0; |
199 | } | 203 | } |
200 | 204 | ||
201 | return rc; | 205 | return rc; |
202 | } | 206 | } |
203 | 207 | ||
208 | int | ||
209 | cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len, | ||
210 | const unsigned short int port) | ||
211 | { | ||
212 | if (!cifs_convert_address(dst, src, len)) | ||
213 | return 0; | ||
214 | |||
215 | switch (dst->sa_family) { | ||
216 | case AF_INET: | ||
217 | ((struct sockaddr_in *)dst)->sin_port = htons(port); | ||
218 | break; | ||
219 | case AF_INET6: | ||
220 | ((struct sockaddr_in6 *)dst)->sin6_port = htons(port); | ||
221 | break; | ||
222 | default: | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | return 1; | ||
227 | } | ||
228 | |||
204 | /***************************************************************************** | 229 | /***************************************************************************** |
205 | convert a NT status code to a dos class/code | 230 | convert a NT status code to a dos class/code |
206 | *****************************************************************************/ | 231 | *****************************************************************************/ |