aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-08-07 15:54:46 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-08-07 15:54:46 -0400
commit1fc7995d19139d6f99203b43c161968f3f554a15 (patch)
tree8ad219a60e85935df878f2f9927a58b84f63433e /fs/cifs
parent3b7433b8a8a83c87972065b1852b7dcae691e464 (diff)
parent5227bbb008fa6d2efddd86170bdfac2020cf571d (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6: [DNS RESOLVER] Minor typo correction DNS: Fixes for the DNS query module cifs: Include linux/err.h for IS_ERR and PTR_ERR DNS: Make AFS go to the DNS for AFSDB records for unknown cells DNS: Separate out CIFS DNS Resolver code cifs: account for new creduid=0x%x parameter in spnego upcall string cifs: reduce false positives with inode aliasing serverino autodisable CIFS: Make cifs_convert_address() take a const src pointer and a length cifs: show features compiled in as part of DebugData cifs: update README Fix up trivial conflicts in fs/cifs/cifsfs.c due to workqueue changes
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/Kconfig17
-rw-r--r--fs/cifs/README5
-rw-r--r--fs/cifs/cifs_debug.c25
-rw-r--r--fs/cifs/cifs_dfs_ref.c5
-rw-r--r--fs/cifs/cifs_spnego.c4
-rw-r--r--fs/cifs/cifsfs.c13
-rw-r--r--fs/cifs/cifsproto.h4
-rw-r--r--fs/cifs/connect.c1
-rw-r--r--fs/cifs/dns_resolve.c231
-rw-r--r--fs/cifs/dns_resolve.h2
-rw-r--r--fs/cifs/inode.c43
-rw-r--r--fs/cifs/netmisc.c45
12 files changed, 148 insertions, 247 deletions
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index efcb3a78a327..917b7d449bb2 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -70,14 +70,14 @@ config CIFS_WEAK_PW_HASH
70 If unsure, say N. 70 If unsure, say N.
71 71
72config CIFS_UPCALL 72config CIFS_UPCALL
73 bool "Kerberos/SPNEGO advanced session setup" 73 bool "Kerberos/SPNEGO advanced session setup"
74 depends on CIFS && KEYS 74 depends on CIFS && KEYS
75 help 75 select DNS_RESOLVER
76 Enables an upcall mechanism for CIFS which accesses 76 help
77 userspace helper utilities to provide SPNEGO packaged (RFC 4178) 77 Enables an upcall mechanism for CIFS which accesses userspace helper
78 Kerberos tickets which are needed to mount to certain secure servers 78 utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets
79 (for which more secure Kerberos authentication is required). If 79 which are needed to mount to certain secure servers (for which more
80 unsure, say N. 80 secure Kerberos authentication is required). If unsure, say N.
81 81
82config CIFS_XATTR 82config CIFS_XATTR
83 bool "CIFS extended attributes" 83 bool "CIFS extended attributes"
@@ -121,6 +121,7 @@ config CIFS_DEBUG2
121config CIFS_DFS_UPCALL 121config CIFS_DFS_UPCALL
122 bool "DFS feature support" 122 bool "DFS feature support"
123 depends on CIFS && KEYS 123 depends on CIFS && KEYS
124 select DNS_RESOLVER
124 help 125 help
125 Distributed File System (DFS) support is used to access shares 126 Distributed File System (DFS) support is used to access shares
126 transparently in an enterprise name space, even if the share 127 transparently in an enterprise name space, even if the share
diff --git a/fs/cifs/README b/fs/cifs/README
index a727b7cb075f..a7081eeeb85d 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -568,8 +568,9 @@ module can be displayed via modinfo.
568Misc /proc/fs/cifs Flags and Debug Info 568Misc /proc/fs/cifs Flags and Debug Info
569======================================= 569=======================================
570Informational pseudo-files: 570Informational pseudo-files:
571DebugData Displays information about active CIFS sessions 571DebugData Displays information about active CIFS sessions and
572 and shares, as well as the cifs.ko version. 572 shares, features enabled as well as the cifs.ko
573 version.
573Stats Lists summary resource usage information as well as per 574Stats Lists summary resource usage information as well as per
574 share statistics, if CONFIG_CIFS_STATS in enabled 575 share statistics, if CONFIG_CIFS_STATS in enabled
575 in the kernel configuration. 576 in the kernel configuration.
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 4fce6e61b34e..eb1ba493489f 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -119,6 +119,31 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
119 "Display Internal CIFS Data Structures for Debugging\n" 119 "Display Internal CIFS Data Structures for Debugging\n"
120 "---------------------------------------------------\n"); 120 "---------------------------------------------------\n");
121 seq_printf(m, "CIFS Version %s\n", CIFS_VERSION); 121 seq_printf(m, "CIFS Version %s\n", CIFS_VERSION);
122 seq_printf(m, "Features: ");
123#ifdef CONFIG_CIFS_DFS_UPCALL
124 seq_printf(m, "dfs");
125 seq_putc(m, ' ');
126#endif
127#ifdef CONFIG_CIFS_FSCACHE
128 seq_printf(m, "fscache");
129 seq_putc(m, ' ');
130#endif
131#ifdef CONFIG_CIFS_WEAK_PW_HASH
132 seq_printf(m, "lanman");
133 seq_putc(m, ' ');
134#endif
135#ifdef CONFIG_CIFS_POSIX
136 seq_printf(m, "posix");
137 seq_putc(m, ' ');
138#endif
139#ifdef CONFIG_CIFS_UPCALL
140 seq_printf(m, "spnego");
141 seq_putc(m, ' ');
142#endif
143#ifdef CONFIG_CIFS_XATTR
144 seq_printf(m, "xattr");
145#endif
146 seq_putc(m, '\n');
122 seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); 147 seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid);
123 seq_printf(m, "Servers:"); 148 seq_printf(m, "Servers:");
124 149
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index dc1ed50ea06e..d6ced7aa23cf 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -141,7 +141,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
141 } 141 }
142 142
143 rc = dns_resolve_server_name_to_ip(*devname, &srvIP); 143 rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
144 if (rc != 0) { 144 if (rc < 0) {
145 cERROR(1, "%s: Failed to resolve server part of %s to IP: %d", 145 cERROR(1, "%s: Failed to resolve server part of %s to IP: %d",
146 __func__, *devname, rc); 146 __func__, *devname, rc);
147 goto compose_mount_options_err; 147 goto compose_mount_options_err;
@@ -150,8 +150,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
150 * assuming that we have 'unc=' and 'ip=' in 150 * assuming that we have 'unc=' and 'ip=' in
151 * the original sb_mountdata 151 * the original sb_mountdata
152 */ 152 */
153 md_len = strlen(sb_mountdata) + strlen(srvIP) + 153 md_len = strlen(sb_mountdata) + rc + strlen(ref->node_name) + 12;
154 strlen(ref->node_name) + 12;
155 mountdata = kzalloc(md_len+1, GFP_KERNEL); 154 mountdata = kzalloc(md_len+1, GFP_KERNEL);
156 if (mountdata == NULL) { 155 if (mountdata == NULL) {
157 rc = -ENOMEM; 156 rc = -ENOMEM;
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index 6effccff85a5..87044906cd1f 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -84,6 +84,9 @@ struct key_type cifs_spnego_key_type = {
84/* strlen of ";uid=0x" */ 84/* strlen of ";uid=0x" */
85#define UID_KEY_LEN 7 85#define UID_KEY_LEN 7
86 86
87/* strlen of ";creduid=0x" */
88#define CREDUID_KEY_LEN 11
89
87/* strlen of ";user=" */ 90/* strlen of ";user=" */
88#define USER_KEY_LEN 6 91#define USER_KEY_LEN 6
89 92
@@ -107,6 +110,7 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
107 IP_KEY_LEN + INET6_ADDRSTRLEN + 110 IP_KEY_LEN + INET6_ADDRSTRLEN +
108 MAX_MECH_STR_LEN + 111 MAX_MECH_STR_LEN +
109 UID_KEY_LEN + (sizeof(uid_t) * 2) + 112 UID_KEY_LEN + (sizeof(uid_t) * 2) +
113 CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
110 USER_KEY_LEN + strlen(sesInfo->userName) + 114 USER_KEY_LEN + strlen(sesInfo->userName) +
111 PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; 115 PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
112 116
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 5ac541a30c1d..a5ed10c9afef 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -45,7 +45,6 @@
45#include "cifs_fs_sb.h" 45#include "cifs_fs_sb.h"
46#include <linux/mm.h> 46#include <linux/mm.h>
47#include <linux/key-type.h> 47#include <linux/key-type.h>
48#include "dns_resolve.h"
49#include "cifs_spnego.h" 48#include "cifs_spnego.h"
50#include "fscache.h" 49#include "fscache.h"
51#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ 50#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
@@ -934,22 +933,13 @@ init_cifs(void)
934 if (rc) 933 if (rc)
935 goto out_unregister_filesystem; 934 goto out_unregister_filesystem;
936#endif 935#endif
937#ifdef CONFIG_CIFS_DFS_UPCALL
938 rc = cifs_init_dns_resolver();
939 if (rc)
940 goto out_unregister_key_type;
941#endif
942 936
943 return 0; 937 return 0;
944 938
945#ifdef CONFIG_CIFS_DFS_UPCALL
946 out_unregister_key_type:
947#endif
948#ifdef CONFIG_CIFS_UPCALL 939#ifdef CONFIG_CIFS_UPCALL
949 unregister_key_type(&cifs_spnego_key_type);
950 out_unregister_filesystem: 940 out_unregister_filesystem:
951#endif
952 unregister_filesystem(&cifs_fs_type); 941 unregister_filesystem(&cifs_fs_type);
942#endif
953 out_destroy_request_bufs: 943 out_destroy_request_bufs:
954 cifs_destroy_request_bufs(); 944 cifs_destroy_request_bufs();
955 out_destroy_mids: 945 out_destroy_mids:
@@ -971,7 +961,6 @@ exit_cifs(void)
971 cifs_fscache_unregister(); 961 cifs_fscache_unregister();
972#ifdef CONFIG_CIFS_DFS_UPCALL 962#ifdef CONFIG_CIFS_DFS_UPCALL
973 cifs_dfs_release_automount_timer(); 963 cifs_dfs_release_automount_timer();
974 cifs_exit_dns_resolver();
975#endif 964#endif
976#ifdef CONFIG_CIFS_UPCALL 965#ifdef CONFIG_CIFS_UPCALL
977 unregister_key_type(&cifs_spnego_key_type); 966 unregister_key_type(&cifs_spnego_key_type);
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 2eaebbd31132..1f5450814087 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -86,8 +86,8 @@ extern unsigned int smbCalcSize(struct smb_hdr *ptr);
86extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); 86extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr);
87extern int decode_negTokenInit(unsigned char *security_blob, int length, 87extern int decode_negTokenInit(unsigned char *security_blob, int length,
88 struct TCP_Server_Info *server); 88 struct TCP_Server_Info *server);
89extern int cifs_convert_address(struct sockaddr *dst, char *src); 89extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
90extern int cifs_fill_sockaddr(struct sockaddr *dst, char *src, 90extern int cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len,
91 unsigned short int port); 91 unsigned short int port);
92extern int map_smb_to_linux_error(struct smb_hdr *smb, int logErr); 92extern int map_smb_to_linux_error(struct smb_hdr *smb, int logErr);
93extern void header_assemble(struct smb_hdr *, char /* command */ , 93extern void header_assemble(struct smb_hdr *, char /* command */ ,
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 2a43a0aca965..95c2ea67edfb 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1543,6 +1543,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
1543 if (volume_info->UNCip && volume_info->UNC) { 1543 if (volume_info->UNCip && volume_info->UNC) {
1544 rc = cifs_fill_sockaddr((struct sockaddr *)&addr, 1544 rc = cifs_fill_sockaddr((struct sockaddr *)&addr,
1545 volume_info->UNCip, 1545 volume_info->UNCip,
1546 strlen(volume_info->UNCip),
1546 volume_info->port); 1547 volume_info->port);
1547 if (!rc) { 1548 if (!rc) {
1548 /* we failed translating address */ 1549 /* we failed translating address */
diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
index 3ad7f4300c45..0eb87026cad3 100644
--- a/fs/cifs/dns_resolve.c
+++ b/fs/cifs/dns_resolve.c
@@ -4,6 +4,8 @@
4 * Copyright (c) 2007 Igor Mammedov 4 * Copyright (c) 2007 Igor Mammedov
5 * Author(s): Igor Mammedov (niallain@gmail.com) 5 * Author(s): Igor Mammedov (niallain@gmail.com)
6 * Steve French (sfrench@us.ibm.com) 6 * Steve French (sfrench@us.ibm.com)
7 * Wang Lei (wang840925@gmail.com)
8 * David Howells (dhowells@redhat.com)
7 * 9 *
8 * Contains the CIFS DFS upcall routines used for hostname to 10 * Contains the CIFS DFS upcall routines used for hostname to
9 * IP address translation. 11 * IP address translation.
@@ -24,214 +26,73 @@
24 */ 26 */
25 27
26#include <linux/slab.h> 28#include <linux/slab.h>
27#include <linux/keyctl.h> 29#include <linux/dns_resolver.h>
28#include <linux/key-type.h>
29#include <keys/user-type.h>
30#include "dns_resolve.h" 30#include "dns_resolve.h"
31#include "cifsglob.h" 31#include "cifsglob.h"
32#include "cifsproto.h" 32#include "cifsproto.h"
33#include "cifs_debug.h" 33#include "cifs_debug.h"
34 34
35static const struct cred *dns_resolver_cache; 35/**
36 36 * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
37/* Checks if supplied name is IP address 37 * @unc: UNC path specifying the server
38 * returns: 38 * @ip_addr: Where to return the IP address.
39 * 1 - name is IP 39 *
40 * 0 - name is not IP 40 * The IP address will be returned in string form, and the caller is
41 */ 41 * responsible for freeing it.
42static int 42 *
43is_ip(char *name) 43 * Returns length of result on success, -ve on error.
44{
45 struct sockaddr_storage ss;
46
47 return cifs_convert_address((struct sockaddr *)&ss, name);
48}
49
50static int
51dns_resolver_instantiate(struct key *key, const void *data,
52 size_t datalen)
53{
54 int rc = 0;
55 char *ip;
56
57 ip = kmalloc(datalen + 1, GFP_KERNEL);
58 if (!ip)
59 return -ENOMEM;
60
61 memcpy(ip, data, datalen);
62 ip[datalen] = '\0';
63
64 /* make sure this looks like an address */
65 if (!is_ip(ip)) {
66 kfree(ip);
67 return -EINVAL;
68 }
69
70 key->type_data.x[0] = datalen;
71 key->payload.data = ip;
72
73 return rc;
74}
75
76static void
77dns_resolver_destroy(struct key *key)
78{
79 kfree(key->payload.data);
80}
81
82struct key_type key_type_dns_resolver = {
83 .name = "dns_resolver",
84 .def_datalen = sizeof(struct in_addr),
85 .describe = user_describe,
86 .instantiate = dns_resolver_instantiate,
87 .destroy = dns_resolver_destroy,
88 .match = user_match,
89};
90
91/* Resolves server name to ip address.
92 * input:
93 * unc - server UNC
94 * output:
95 * *ip_addr - pointer to server ip, caller responcible for freeing it.
96 * return 0 on success
97 */ 44 */
98int 45int
99dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) 46dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
100{ 47{
101 const struct cred *saved_cred; 48 struct sockaddr_storage ss;
102 int rc = -EAGAIN; 49 const char *hostname, *sep;
103 struct key *rkey = ERR_PTR(-EAGAIN);
104 char *name; 50 char *name;
105 char *data = NULL; 51 int len, rc;
106 int len;
107 52
108 if (!ip_addr || !unc) 53 if (!ip_addr || !unc)
109 return -EINVAL; 54 return -EINVAL;
110 55
111 /* search for server name delimiter */
112 len = strlen(unc); 56 len = strlen(unc);
113 if (len < 3) { 57 if (len < 3) {
114 cFYI(1, "%s: unc is too short: %s", __func__, unc); 58 cFYI(1, "%s: unc is too short: %s", __func__, unc);
115 return -EINVAL; 59 return -EINVAL;
116 } 60 }
117 len -= 2;
118 name = memchr(unc+2, '\\', len);
119 if (!name) {
120 cFYI(1, "%s: probably server name is whole unc: %s",
121 __func__, unc);
122 } else {
123 len = (name - unc) - 2/* leading // */;
124 }
125
126 name = kmalloc(len+1, GFP_KERNEL);
127 if (!name) {
128 rc = -ENOMEM;
129 return rc;
130 }
131 memcpy(name, unc+2, len);
132 name[len] = 0;
133
134 if (is_ip(name)) {
135 cFYI(1, "%s: it is IP, skipping dns upcall: %s",
136 __func__, name);
137 data = name;
138 goto skip_upcall;
139 }
140 61
141 saved_cred = override_creds(dns_resolver_cache); 62 /* Discount leading slashes for cifs */
142 rkey = request_key(&key_type_dns_resolver, name, ""); 63 len -= 2;
143 revert_creds(saved_cred); 64 hostname = unc + 2;
144 if (!IS_ERR(rkey)) {
145 if (!(rkey->perm & KEY_USR_VIEW)) {
146 down_read(&rkey->sem);
147 rkey->perm |= KEY_USR_VIEW;
148 up_read(&rkey->sem);
149 }
150 len = rkey->type_data.x[0];
151 data = rkey->payload.data;
152 } else {
153 cERROR(1, "%s: unable to resolve: %s", __func__, name);
154 goto out;
155 }
156
157skip_upcall:
158 if (data) {
159 *ip_addr = kmalloc(len + 1, GFP_KERNEL);
160 if (*ip_addr) {
161 memcpy(*ip_addr, data, len + 1);
162 if (!IS_ERR(rkey))
163 cFYI(1, "%s: resolved: %s to %s", __func__,
164 name,
165 *ip_addr
166 );
167 rc = 0;
168 } else {
169 rc = -ENOMEM;
170 }
171 if (!IS_ERR(rkey))
172 key_put(rkey);
173 }
174 65
175out: 66 /* Search for server name delimiter */
176 kfree(name); 67 sep = memchr(hostname, '\\', len);
68 if (sep)
69 len = sep - unc;
70 else
71 cFYI(1, "%s: probably server name is whole unc: %s",
72 __func__, unc);
73
74 /* Try to interpret hostname as an IPv4 or IPv6 address */
75 rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len);
76 if (rc > 0)
77 goto name_is_IP_address;
78
79 /* Perform the upcall */
80 rc = dns_query(NULL, hostname, len, NULL, ip_addr, NULL);
81 if (rc < 0)
82 cERROR(1, "%s: unable to resolve: %*.*s",
83 __func__, len, len, hostname);
84 else
85 cFYI(1, "%s: resolved: %*.*s to %s",
86 __func__, len, len, hostname, *ip_addr);
177 return rc; 87 return rc;
178}
179 88
180int __init cifs_init_dns_resolver(void) 89name_is_IP_address:
181{ 90 name = kmalloc(len + 1, GFP_KERNEL);
182 struct cred *cred; 91 if (!name)
183 struct key *keyring;
184 int ret;
185
186 printk(KERN_NOTICE "Registering the %s key type\n",
187 key_type_dns_resolver.name);
188
189 /* create an override credential set with a special thread keyring in
190 * which DNS requests are cached
191 *
192 * this is used to prevent malicious redirections from being installed
193 * with add_key().
194 */
195 cred = prepare_kernel_cred(NULL);
196 if (!cred)
197 return -ENOMEM; 92 return -ENOMEM;
198 93 memcpy(name, hostname, len);
199 keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, 94 name[len] = 0;
200 (KEY_POS_ALL & ~KEY_POS_SETATTR) | 95 cFYI(1, "%s: unc is IP, skipping dns upcall: %s", __func__, name);
201 KEY_USR_VIEW | KEY_USR_READ, 96 *ip_addr = name;
202 KEY_ALLOC_NOT_IN_QUOTA);
203 if (IS_ERR(keyring)) {
204 ret = PTR_ERR(keyring);
205 goto failed_put_cred;
206 }
207
208 ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
209 if (ret < 0)
210 goto failed_put_key;
211
212 ret = register_key_type(&key_type_dns_resolver);
213 if (ret < 0)
214 goto failed_put_key;
215
216 /* instruct request_key() to use this special keyring as a cache for
217 * the results it looks up */
218 cred->thread_keyring = keyring;
219 cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
220 dns_resolver_cache = cred;
221 return 0; 97 return 0;
222
223failed_put_key:
224 key_put(keyring);
225failed_put_cred:
226 put_cred(cred);
227 return ret;
228}
229
230void cifs_exit_dns_resolver(void)
231{
232 key_revoke(dns_resolver_cache->thread_keyring);
233 unregister_key_type(&key_type_dns_resolver);
234 put_cred(dns_resolver_cache);
235 printk(KERN_NOTICE "Unregistered %s key type\n",
236 key_type_dns_resolver.name);
237} 98}
diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h
index 5d7f291df162..d3f5d27f4d06 100644
--- a/fs/cifs/dns_resolve.h
+++ b/fs/cifs/dns_resolve.h
@@ -24,8 +24,6 @@
24#define _DNS_RESOLVE_H 24#define _DNS_RESOLVE_H
25 25
26#ifdef __KERNEL__ 26#ifdef __KERNEL__
27extern int __init cifs_init_dns_resolver(void);
28extern void cifs_exit_dns_resolver(void);
29extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr); 27extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
30#endif /* KERNEL */ 28#endif /* KERNEL */
31 29
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index a15b3a9bbff4..dc4c47ab9588 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -732,15 +732,9 @@ cifs_find_inode(struct inode *inode, void *opaque)
732 if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT)) 732 if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT))
733 return 0; 733 return 0;
734 734
735 /* 735 /* if it's not a directory or has no dentries, then flag it */
736 * uh oh -- it's a directory. We can't use it since hardlinked dirs are 736 if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry))
737 * verboten. Disable serverino and return it as if it were found, the
738 * caller can discard it, generate a uniqueid and retry the find
739 */
740 if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) {
741 fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; 737 fattr->cf_flags |= CIFS_FATTR_INO_COLLISION;
742 cifs_autodisable_serverino(CIFS_SB(inode->i_sb));
743 }
744 738
745 return 1; 739 return 1;
746} 740}
@@ -754,6 +748,27 @@ cifs_init_inode(struct inode *inode, void *opaque)
754 return 0; 748 return 0;
755} 749}
756 750
751/*
752 * walk dentry list for an inode and report whether it has aliases that
753 * are hashed. We use this to determine if a directory inode can actually
754 * be used.
755 */
756static bool
757inode_has_hashed_dentries(struct inode *inode)
758{
759 struct dentry *dentry;
760
761 spin_lock(&dcache_lock);
762 list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
763 if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
764 spin_unlock(&dcache_lock);
765 return true;
766 }
767 }
768 spin_unlock(&dcache_lock);
769 return false;
770}
771
757/* Given fattrs, get a corresponding inode */ 772/* Given fattrs, get a corresponding inode */
758struct inode * 773struct inode *
759cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) 774cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
@@ -769,12 +784,16 @@ retry_iget5_locked:
769 784
770 inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); 785 inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr);
771 if (inode) { 786 if (inode) {
772 /* was there a problematic inode number collision? */ 787 /* was there a potentially problematic inode collision? */
773 if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { 788 if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) {
774 iput(inode);
775 fattr->cf_uniqueid = iunique(sb, ROOT_I);
776 fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; 789 fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
777 goto retry_iget5_locked; 790
791 if (inode_has_hashed_dentries(inode)) {
792 cifs_autodisable_serverino(CIFS_SB(sb));
793 iput(inode);
794 fattr->cf_uniqueid = iunique(sb, ROOT_I);
795 goto retry_iget5_locked;
796 }
778 } 797 }
779 798
780 cifs_fattr_to_inode(inode, fattr); 799 cifs_fattr_to_inode(inode, fattr);
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index c6721ee26dbc..f97851119e6c 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -140,17 +140,18 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = {
140 * Returns 0 on failure. 140 * Returns 0 on failure.
141 */ 141 */
142static int 142static int
143cifs_inet_pton(const int address_family, const char *cp, void *dst) 143cifs_inet_pton(const int address_family, const char *cp, int len, void *dst)
144{ 144{
145 int ret = 0; 145 int ret = 0;
146 146
147 /* calculate length by finding first slash or NULL */ 147 /* calculate length by finding first slash or NULL */
148 if (address_family == AF_INET) 148 if (address_family == AF_INET)
149 ret = in4_pton(cp, -1 /* len */, dst, '\\', NULL); 149 ret = in4_pton(cp, len, dst, '\\', NULL);
150 else if (address_family == AF_INET6) 150 else if (address_family == AF_INET6)
151 ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL); 151 ret = in6_pton(cp, len, dst , '\\', NULL);
152 152
153 cFYI(DBG2, "address conversion returned %d for %s", ret, cp); 153 cFYI(DBG2, "address conversion returned %d for %*.*s",
154 ret, len, len, cp);
154 if (ret > 0) 155 if (ret > 0)
155 ret = 1; 156 ret = 1;
156 return ret; 157 return ret;
@@ -165,37 +166,39 @@ cifs_inet_pton(const int address_family, const char *cp, void *dst)
165 * Returns 0 on failure. 166 * Returns 0 on failure.
166 */ 167 */
167int 168int
168cifs_convert_address(struct sockaddr *dst, char *src) 169cifs_convert_address(struct sockaddr *dst, const char *src, int len)
169{ 170{
170 int rc; 171 int rc, alen, slen;
171 char *pct, *endp; 172 const char *pct;
173 char *endp, scope_id[13];
172 struct sockaddr_in *s4 = (struct sockaddr_in *) dst; 174 struct sockaddr_in *s4 = (struct sockaddr_in *) dst;
173 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; 175 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst;
174 176
175 /* IPv4 address */ 177 /* IPv4 address */
176 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)) {
177 s4->sin_family = AF_INET; 179 s4->sin_family = AF_INET;
178 return 1; 180 return 1;
179 } 181 }
180 182
181 /* temporarily terminate string */ 183 /* attempt to exclude the scope ID from the address part */
182 pct = strchr(src, '%'); 184 pct = memchr(src, '%', len);
183 if (pct) 185 alen = pct ? pct - src : len;
184 *pct = '\0';
185
186 rc = cifs_inet_pton(AF_INET6, src, &s6->sin6_addr.s6_addr);
187
188 /* repair temp termination (if any) and make pct point to scopeid */
189 if (pct)
190 *pct++ = '%';
191 186
187 rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr);
192 if (!rc) 188 if (!rc)
193 return rc; 189 return rc;
194 190
195 s6->sin6_family = AF_INET6; 191 s6->sin6_family = AF_INET6;
196 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
197 s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0); 200 s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0);
198 if (!*pct || *endp) 201 if (endp != scope_id + slen)
199 return 0; 202 return 0;
200 } 203 }
201 204
@@ -203,10 +206,10 @@ cifs_convert_address(struct sockaddr *dst, char *src)
203} 206}
204 207
205int 208int
206cifs_fill_sockaddr(struct sockaddr *dst, char *src, 209cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len,
207 const unsigned short int port) 210 const unsigned short int port)
208{ 211{
209 if (!cifs_convert_address(dst, src)) 212 if (!cifs_convert_address(dst, src, len))
210 return 0; 213 return 0;
211 214
212 switch (dst->sa_family) { 215 switch (dst->sa_family) {