aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorRob Landley <rlandley@parallels.com>2011-01-22 16:44:05 -0500
committerSteve French <sfrench@us.ibm.com>2011-01-23 23:28:51 -0500
commitf1d0c998653f1eeec60ee6420e550135b62dbab4 (patch)
tree1db07e198f4c0a86bca7160f9a6a3255f4b4f4a9 /fs/cifs
parent3f391c79b0686ce183668c6e2b7d02f3e716766c (diff)
Make CIFS mount work in a container.
Teach cifs about network namespaces, so mounting uses adresses/routing visible from the container rather than from init context. A container is a chroot on steroids that changes more than just the root filesystem the new processes see. One thing containers can isolate is "network namespaces", meaning each container can have its own set of ethernet interfaces, each with its own own IP address and routing to the outside world. And if you open a socket in _userspace_ from processes within such a container, this works fine. But sockets opened from within the kernel still use a single global networking context in a lot of places, meaning the new socket's address and routing are correct for PID 1 on the host, but are _not_ what userspace processes in the container get to use. So when you mount a network filesystem from within in a container, the mount code in the CIFS driver uses the host's networking context and not the container's networking context, so it gets the wrong address, uses the wrong routing, and may even try to go out an interface that the container can't even access... Bad stuff. This patch copies the mount process's network context into the CIFS structure that stores the rest of the server information for that mount point, and changes the socket open code to use the saved network context instead of the global network context. I.E. "when you attempt to use these addresses, do so relative to THIS set of network interfaces and routing rules, not the old global context from back before we supported containers". The big long HOWTO sets up a test environment on the assumption you've never used ocntainers before. It basically says: 1) configure and build a new kernel that has container support 2) build a new root filesystem that includes the userspace container control package (LXC) 3) package/run them under KVM (so you don't have to mess up your host system in order to play with containers). 4) set up some containers under the KVM system 5) set up contradictory routing in the KVM system and the container so that the host and the container see different things for the same address 6) try to mount a CIFS share from both contexts so you can both force it to work and force it to fail. For a long drawn out test reproduction sequence, see: http://landley.livejournal.com/47024.html http://landley.livejournal.com/47205.html http://landley.livejournal.com/47476.html Signed-off-by: Rob Landley <rlandley@parallels.com> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifsglob.h33
-rw-r--r--fs/cifs/connect.c12
2 files changed, 43 insertions, 2 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 5bfb75346cb0..edd5b29b53c9 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -166,6 +166,9 @@ struct TCP_Server_Info {
166 struct socket *ssocket; 166 struct socket *ssocket;
167 struct sockaddr_storage dstaddr; 167 struct sockaddr_storage dstaddr;
168 struct sockaddr_storage srcaddr; /* locally bind to this IP */ 168 struct sockaddr_storage srcaddr; /* locally bind to this IP */
169#ifdef CONFIG_NET_NS
170 struct net *net;
171#endif
169 wait_queue_head_t response_q; 172 wait_queue_head_t response_q;
170 wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ 173 wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
171 struct list_head pending_mid_q; 174 struct list_head pending_mid_q;
@@ -217,6 +220,36 @@ struct TCP_Server_Info {
217}; 220};
218 221
219/* 222/*
223 * Macros to allow the TCP_Server_Info->net field and related code to drop out
224 * when CONFIG_NET_NS isn't set.
225 */
226
227#ifdef CONFIG_NET_NS
228
229static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv)
230{
231 return srv->net;
232}
233
234static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
235{
236 srv->net = net;
237}
238
239#else
240
241static inline struct net *cifs_net_ns(struct TCP_Server_Info *srv)
242{
243 return &init_net;
244}
245
246static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net)
247{
248}
249
250#endif
251
252/*
220 * Session structure. One of these for each uid session with a particular host 253 * Session structure. One of these for each uid session with a particular host
221 */ 254 */
222struct cifsSesInfo { 255struct cifsSesInfo {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 18d3c7724d6e..0cc3b81c2e84 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1568,6 +1568,9 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)
1568 1568
1569 spin_lock(&cifs_tcp_ses_lock); 1569 spin_lock(&cifs_tcp_ses_lock);
1570 list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { 1570 list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
1571 if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
1572 continue;
1573
1571 if (!match_address(server, addr, 1574 if (!match_address(server, addr,
1572 (struct sockaddr *)&vol->srcaddr)) 1575 (struct sockaddr *)&vol->srcaddr))
1573 continue; 1576 continue;
@@ -1598,6 +1601,8 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
1598 return; 1601 return;
1599 } 1602 }
1600 1603
1604 put_net(cifs_net_ns(server));
1605
1601 list_del_init(&server->tcp_ses_list); 1606 list_del_init(&server->tcp_ses_list);
1602 spin_unlock(&cifs_tcp_ses_lock); 1607 spin_unlock(&cifs_tcp_ses_lock);
1603 1608
@@ -1672,6 +1677,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
1672 goto out_err; 1677 goto out_err;
1673 } 1678 }
1674 1679
1680 cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
1675 tcp_ses->hostname = extract_hostname(volume_info->UNC); 1681 tcp_ses->hostname = extract_hostname(volume_info->UNC);
1676 if (IS_ERR(tcp_ses->hostname)) { 1682 if (IS_ERR(tcp_ses->hostname)) {
1677 rc = PTR_ERR(tcp_ses->hostname); 1683 rc = PTR_ERR(tcp_ses->hostname);
@@ -1752,6 +1758,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
1752out_err_crypto_release: 1758out_err_crypto_release:
1753 cifs_crypto_shash_release(tcp_ses); 1759 cifs_crypto_shash_release(tcp_ses);
1754 1760
1761 put_net(cifs_net_ns(tcp_ses));
1762
1755out_err: 1763out_err:
1756 if (tcp_ses) { 1764 if (tcp_ses) {
1757 if (!IS_ERR(tcp_ses->hostname)) 1765 if (!IS_ERR(tcp_ses->hostname))
@@ -2263,8 +2271,8 @@ generic_ip_connect(struct TCP_Server_Info *server)
2263 } 2271 }
2264 2272
2265 if (socket == NULL) { 2273 if (socket == NULL) {
2266 rc = sock_create_kern(sfamily, SOCK_STREAM, 2274 rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM,
2267 IPPROTO_TCP, &socket); 2275 IPPROTO_TCP, &socket, 1);
2268 if (rc < 0) { 2276 if (rc < 0) {
2269 cERROR(1, "Error %d creating socket", rc); 2277 cERROR(1, "Error %d creating socket", rc);
2270 server->ssocket = NULL; 2278 server->ssocket = NULL;