diff options
author | J. Bruce Fields <bfields@citi.umich.edu> | 2008-08-20 16:10:23 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-10-07 18:17:47 -0400 |
commit | ea31a4437c59219bf3ea946d58984b01a45a289c (patch) | |
tree | d386f91c39461c7505e614f0b281f0e7e9b99298 /fs/nfs | |
parent | f0c929251e01a7a86b6254c775cb6b65c6457f10 (diff) |
nfs: Fix misparsing of nfsv4 fs_locations attribute
The code incorrectly assumes here that the server name (or ip address)
is null-terminated. This can cause referrals to fail in some cases.
Also support ipv6 addresses.
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/internal.h | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4namespace.c | 44 | ||||
-rw-r--r-- | fs/nfs/super.c | 4 |
3 files changed, 21 insertions, 29 deletions
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 8d91bd88e310..5d2a5d3c4241 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -153,6 +153,7 @@ extern void nfs4_clear_inode(struct inode *); | |||
153 | void nfs_zap_acl_cache(struct inode *inode); | 153 | void nfs_zap_acl_cache(struct inode *inode); |
154 | 154 | ||
155 | /* super.c */ | 155 | /* super.c */ |
156 | void nfs_parse_ip_address(char *, size_t, struct sockaddr *, size_t *); | ||
156 | extern struct file_system_type nfs_xdev_fs_type; | 157 | extern struct file_system_type nfs_xdev_fs_type; |
157 | #ifdef CONFIG_NFS_V4 | 158 | #ifdef CONFIG_NFS_V4 |
158 | extern struct file_system_type nfs4_xdev_fs_type; | 159 | extern struct file_system_type nfs4_xdev_fs_type; |
@@ -276,6 +277,7 @@ unsigned int nfs_page_array_len(unsigned int base, size_t len) | |||
276 | PAGE_SIZE - 1) >> PAGE_SHIFT; | 277 | PAGE_SIZE - 1) >> PAGE_SHIFT; |
277 | } | 278 | } |
278 | 279 | ||
280 | #define IPV6_SCOPE_DELIMITER '%' | ||
279 | 281 | ||
280 | /* | 282 | /* |
281 | * Set the port number in an address. Be agnostic about the address | 283 | * Set the port number in an address. Be agnostic about the address |
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c index 6bcc5696f911..30befc39b3c6 100644 --- a/fs/nfs/nfs4namespace.c +++ b/fs/nfs/nfs4namespace.c | |||
@@ -93,50 +93,42 @@ static int nfs4_validate_fspath(const struct vfsmount *mnt_parent, | |||
93 | return 0; | 93 | return 0; |
94 | } | 94 | } |
95 | 95 | ||
96 | /* | ||
97 | * Check if the string represents a "valid" IPv4 address | ||
98 | */ | ||
99 | static inline int valid_ipaddr4(const char *buf) | ||
100 | { | ||
101 | int rc, count, in[4]; | ||
102 | |||
103 | rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]); | ||
104 | if (rc != 4) | ||
105 | return -EINVAL; | ||
106 | for (count = 0; count < 4; count++) { | ||
107 | if (in[count] > 255) | ||
108 | return -EINVAL; | ||
109 | } | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, | 96 | static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, |
114 | char *page, char *page2, | 97 | char *page, char *page2, |
115 | const struct nfs4_fs_location *location) | 98 | const struct nfs4_fs_location *location) |
116 | { | 99 | { |
117 | struct vfsmount *mnt = ERR_PTR(-ENOENT); | 100 | struct vfsmount *mnt = ERR_PTR(-ENOENT); |
118 | char *mnt_path; | 101 | char *mnt_path; |
102 | int page2len; | ||
119 | unsigned int s; | 103 | unsigned int s; |
120 | 104 | ||
121 | mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); | 105 | mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); |
122 | if (IS_ERR(mnt_path)) | 106 | if (IS_ERR(mnt_path)) |
123 | return mnt; | 107 | return mnt; |
124 | mountdata->mnt_path = mnt_path; | 108 | mountdata->mnt_path = mnt_path; |
109 | page2 += strlen(mnt_path) + 1; | ||
110 | page2len = PAGE_SIZE - strlen(mnt_path) - 1; | ||
125 | 111 | ||
126 | for (s = 0; s < location->nservers; s++) { | 112 | for (s = 0; s < location->nservers; s++) { |
127 | struct sockaddr_in addr = { | 113 | const struct nfs4_string *buf = &location->servers[s]; |
128 | .sin_family = AF_INET, | 114 | struct sockaddr_storage addr; |
129 | .sin_port = htons(NFS_PORT), | ||
130 | }; | ||
131 | 115 | ||
132 | if (location->servers[s].len <= 0 || | 116 | if (buf->len <= 0 || buf->len >= PAGE_SIZE) |
133 | valid_ipaddr4(location->servers[s].data) < 0) | ||
134 | continue; | 117 | continue; |
135 | 118 | ||
136 | mountdata->hostname = location->servers[s].data; | ||
137 | addr.sin_addr.s_addr = in_aton(mountdata->hostname), | ||
138 | mountdata->addr = (struct sockaddr *)&addr; | 119 | mountdata->addr = (struct sockaddr *)&addr; |
139 | mountdata->addrlen = sizeof(addr); | 120 | |
121 | if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len)) | ||
122 | continue; | ||
123 | nfs_parse_ip_address(buf->data, buf->len, | ||
124 | mountdata->addr, &mountdata->addrlen); | ||
125 | if (mountdata->addr->sa_family == AF_UNSPEC) | ||
126 | continue; | ||
127 | nfs_set_port(mountdata->addr, NFS_PORT); | ||
128 | |||
129 | strncpy(page2, buf->data, page2len); | ||
130 | page2[page2len] = '\0'; | ||
131 | mountdata->hostname = page2; | ||
140 | 132 | ||
141 | snprintf(page, PAGE_SIZE, "%s:%s", | 133 | snprintf(page, PAGE_SIZE, "%s:%s", |
142 | mountdata->hostname, | 134 | mountdata->hostname, |
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index b99096b8e827..20dc4ccdff56 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -716,8 +716,6 @@ static void nfs_parse_ipv4_address(char *string, size_t str_len, | |||
716 | *addr_len = 0; | 716 | *addr_len = 0; |
717 | } | 717 | } |
718 | 718 | ||
719 | #define IPV6_SCOPE_DELIMITER '%' | ||
720 | |||
721 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 719 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
722 | static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len, | 720 | static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len, |
723 | const char *delim, | 721 | const char *delim, |
@@ -790,7 +788,7 @@ static void nfs_parse_ipv6_address(char *string, size_t str_len, | |||
790 | * If there is a problem constructing the new sockaddr, set the address | 788 | * If there is a problem constructing the new sockaddr, set the address |
791 | * family to AF_UNSPEC. | 789 | * family to AF_UNSPEC. |
792 | */ | 790 | */ |
793 | static void nfs_parse_ip_address(char *string, size_t str_len, | 791 | void nfs_parse_ip_address(char *string, size_t str_len, |
794 | struct sockaddr *sap, size_t *addr_len) | 792 | struct sockaddr *sap, size_t *addr_len) |
795 | { | 793 | { |
796 | unsigned int i, colons; | 794 | unsigned int i, colons; |