]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - fs/cifs/connect.c
UBUNTU: [Config] updateconfigs after removing powerpc builds
[mirror_ubuntu-zesty-kernel.git] / fs / cifs / connect.c
index f7563c88c917d2174f2259b35191992752217247..777ad9f4fc3c84acbb5d96878939c589341b7f86 100644 (file)
@@ -34,7 +34,7 @@
 #include <linux/pagevec.h>
 #include <linux/freezer.h>
 #include <linux/namei.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include <asm/processor.h>
 #include <linux/inet.h>
 #include <linux/module.h>
@@ -53,6 +53,9 @@
 #include "nterr.h"
 #include "rfc1002pdu.h"
 #include "fscache.h"
+#ifdef CONFIG_CIFS_SMB2
+#include "smb2proto.h"
+#endif
 
 #define CIFS_PORT 445
 #define RFC1001_PORT 139
@@ -89,6 +92,7 @@ enum {
        Opt_multiuser, Opt_sloppy, Opt_nosharesock,
        Opt_persistent, Opt_nopersistent,
        Opt_resilient, Opt_noresilient,
+       Opt_domainauto,
 
        /* Mount options which take numeric value */
        Opt_backupuid, Opt_backupgid, Opt_uid,
@@ -96,6 +100,7 @@ enum {
        Opt_dirmode, Opt_port,
        Opt_rsize, Opt_wsize, Opt_actimeo,
        Opt_echo_interval, Opt_max_credits,
+       Opt_snapshot,
 
        /* Mount options which take string value */
        Opt_user, Opt_pass, Opt_ip,
@@ -177,6 +182,7 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_nopersistent, "nopersistenthandles"},
        { Opt_resilient, "resilienthandles"},
        { Opt_noresilient, "noresilienthandles"},
+       { Opt_domainauto, "domainauto"},
 
        { Opt_backupuid, "backupuid=%s" },
        { Opt_backupgid, "backupgid=%s" },
@@ -192,6 +198,7 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_actimeo, "actimeo=%s" },
        { Opt_echo_interval, "echo_interval=%s" },
        { Opt_max_credits, "max_credits=%s" },
+       { Opt_snapshot, "snapshot=%s" },
 
        { Opt_blank_user, "user=" },
        { Opt_blank_user, "username=" },
@@ -780,6 +787,15 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 
        dump_smb(buf, server->total_read);
 
+       return cifs_handle_standard(server, mid);
+}
+
+int
+cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
+{
+       char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
+       int length;
+
        /*
         * We know that we received enough to get to the MID as we
         * checked the pdu_length earlier. Now check to see
@@ -865,12 +881,19 @@ cifs_demultiplex_thread(void *p)
                        continue;
                server->total_read += length;
 
-               mid_entry = server->ops->find_mid(server, buf);
+               if (server->ops->is_transform_hdr &&
+                   server->ops->receive_transform &&
+                   server->ops->is_transform_hdr(buf)) {
+                       length = server->ops->receive_transform(server,
+                                                               &mid_entry);
+               } else {
+                       mid_entry = server->ops->find_mid(server, buf);
 
-               if (!mid_entry || !mid_entry->receive)
-                       length = standard_receive3(server, mid_entry);
-               else
-                       length = mid_entry->receive(server, mid_entry);
+                       if (!mid_entry || !mid_entry->receive)
+                               length = standard_receive3(server, mid_entry);
+                       else
+                               length = mid_entry->receive(server, mid_entry);
+               }
 
                if (length < 0)
                        continue;
@@ -1500,6 +1523,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                case Opt_noresilient:
                        vol->resilient = false; /* already the default */
                        break;
+               case Opt_domainauto:
+                       vol->domainauto = true;
+                       break;
 
                /* Numeric Values */
                case Opt_backupuid:
@@ -1602,6 +1628,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        }
                        vol->echo_interval = option;
                        break;
+               case Opt_snapshot:
+                       if (get_option_ul(args, &option)) {
+                               cifs_dbg(VFS, "%s: Invalid snapshot time\n",
+                                        __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->snapshot_time = option;
+                       break;
                case Opt_max_credits:
                        if (get_option_ul(args, &option) || (option < 20) ||
                            (option > 60000)) {
@@ -2101,8 +2135,8 @@ cifs_find_tcp_session(struct smb_vol *vol)
        return NULL;
 }
 
-static void
-cifs_put_tcp_session(struct TCP_Server_Info *server)
+void
+cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
 {
        struct task_struct *task;
 
@@ -2119,11 +2153,24 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
 
        cancel_delayed_work_sync(&server->echo);
 
+#ifdef CONFIG_CIFS_SMB2
+       if (from_reconnect)
+               /*
+                * Avoid deadlock here: reconnect work calls
+                * cifs_put_tcp_session() at its end. Need to be sure
+                * that reconnect work does nothing with server pointer after
+                * that step.
+                */
+               cancel_delayed_work(&server->reconnect);
+       else
+               cancel_delayed_work_sync(&server->reconnect);
+#endif
+
        spin_lock(&GlobalMid_Lock);
        server->tcpStatus = CifsExiting;
        spin_unlock(&GlobalMid_Lock);
 
-       cifs_crypto_shash_release(server);
+       cifs_crypto_secmech_release(server);
        cifs_fscache_release_client_cookie(server);
 
        kfree(server->session_key.response);
@@ -2183,6 +2230,10 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
        INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
        INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
        INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
+#ifdef CONFIG_CIFS_SMB2
+       INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
+       mutex_init(&tcp_ses->reconnect_mutex);
+#endif
        memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
               sizeof(tcp_ses->srcaddr));
        memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
@@ -2238,7 +2289,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
        return tcp_ses;
 
 out_err_crypto_release:
-       cifs_crypto_shash_release(tcp_ses);
+       cifs_crypto_secmech_release(tcp_ses);
 
        put_net(cifs_net_ns(tcp_ses));
 
@@ -2341,7 +2392,7 @@ cifs_put_smb_ses(struct cifs_ses *ses)
        spin_unlock(&cifs_tcp_ses_lock);
 
        sesInfoFree(ses);
-       cifs_put_tcp_session(server);
+       cifs_put_tcp_session(server, 0);
 }
 
 #ifdef CONFIG_KEYS
@@ -2515,7 +2566,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
                mutex_unlock(&ses->session_mutex);
 
                /* existing SMB ses has a server reference already */
-               cifs_put_tcp_session(server);
+               cifs_put_tcp_session(server, 0);
                free_xid(xid);
                return ses;
        }
@@ -2549,6 +2600,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
                if (!ses->domainName)
                        goto get_ses_fail;
        }
+       if (volume_info->domainauto)
+               ses->domainAuto = volume_info->domainauto;
        ses->cred_uid = volume_info->cred_uid;
        ses->linux_uid = volume_info->linux_uid;
 
@@ -2577,17 +2630,23 @@ get_ses_fail:
        return ERR_PTR(rc);
 }
 
-static int match_tcon(struct cifs_tcon *tcon, const char *unc)
+static int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info)
 {
        if (tcon->tidStatus == CifsExiting)
                return 0;
-       if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
+       if (strncmp(tcon->treeName, volume_info->UNC, MAX_TREE_SIZE))
                return 0;
+       if (tcon->seal != volume_info->seal)
+               return 0;
+#ifdef CONFIG_CIFS_SMB2
+       if (tcon->snapshot_time != volume_info->snapshot_time)
+               return 0;
+#endif /* CONFIG_CIFS_SMB2 */
        return 1;
 }
 
 static struct cifs_tcon *
-cifs_find_tcon(struct cifs_ses *ses, const char *unc)
+cifs_find_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
 {
        struct list_head *tmp;
        struct cifs_tcon *tcon;
@@ -2595,7 +2654,7 @@ cifs_find_tcon(struct cifs_ses *ses, const char *unc)
        spin_lock(&cifs_tcp_ses_lock);
        list_for_each(tmp, &ses->tcon_list) {
                tcon = list_entry(tmp, struct cifs_tcon, tcon_list);
-               if (!match_tcon(tcon, unc))
+               if (!match_tcon(tcon, volume_info))
                        continue;
                ++tcon->tc_count;
                spin_unlock(&cifs_tcp_ses_lock);
@@ -2605,7 +2664,7 @@ cifs_find_tcon(struct cifs_ses *ses, const char *unc)
        return NULL;
 }
 
-static void
+void
 cifs_put_tcon(struct cifs_tcon *tcon)
 {
        unsigned int xid;
@@ -2637,13 +2696,11 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
        int rc, xid;
        struct cifs_tcon *tcon;
 
-       tcon = cifs_find_tcon(ses, volume_info->UNC);
+       tcon = cifs_find_tcon(ses, volume_info);
        if (tcon) {
                cifs_dbg(FYI, "Found match on UNC path\n");
                /* existing tcon already has a reference */
                cifs_put_smb_ses(ses);
-               if (tcon->seal != volume_info->seal)
-                       cifs_dbg(VFS, "transport encryption setting conflicts with existing tid\n");
                return tcon;
        }
 
@@ -2658,6 +2715,22 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
                goto out_fail;
        }
 
+       if (volume_info->snapshot_time) {
+#ifdef CONFIG_CIFS_SMB2
+               if (ses->server->vals->protocol_id == 0) {
+                       cifs_dbg(VFS,
+                            "Use SMB2 or later for snapshot mount option\n");
+                       rc = -EOPNOTSUPP;
+                       goto out_fail;
+               } else
+                       tcon->snapshot_time = volume_info->snapshot_time;
+#else
+               cifs_dbg(VFS, "Snapshot mount option requires SMB2 support\n");
+               rc = -EOPNOTSUPP;
+               goto out_fail;
+#endif /* CONFIG_CIFS_SMB2 */
+       }
+
        tcon->ses = ses;
        if (volume_info->password) {
                tcon->password = kstrdup(volume_info->password, GFP_KERNEL);
@@ -2683,7 +2756,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
                tcon->Flags &= ~SMB_SHARE_IS_IN_DFS;
                cifs_dbg(FYI, "DFS disabled (%d)\n", tcon->Flags);
        }
-       tcon->seal = volume_info->seal;
        tcon->use_persistent = false;
        /* check if SMB2 or later, CIFS does not support persistent handles */
        if (volume_info->persistent) {
@@ -2720,6 +2792,24 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
                tcon->use_resilient = true;
        }
 
+       if (volume_info->seal) {
+               if (ses->server->vals->protocol_id == 0) {
+                       cifs_dbg(VFS,
+                                "SMB3 or later required for encryption\n");
+                       rc = -EOPNOTSUPP;
+                       goto out_fail;
+#ifdef CONFIG_CIFS_SMB2
+               } else if (tcon->ses->server->capabilities &
+                                       SMB2_GLOBAL_CAP_ENCRYPTION)
+                       tcon->seal = true;
+               else {
+                       cifs_dbg(VFS, "Encryption is not supported on share\n");
+                       rc = -EOPNOTSUPP;
+                       goto out_fail;
+#endif /* CONFIG_CIFS_SMB2 */
+               }
+       }
+
        /*
         * We can have only one retry value for a connection to a share so for
         * resources mounted more than once to the same server share the last
@@ -2851,7 +2941,7 @@ cifs_match_super(struct super_block *sb, void *data)
 
        if (!match_server(tcp_srv, volume_info) ||
            !match_session(ses, volume_info) ||
-           !match_tcon(tcon, volume_info->UNC) ||
+           !match_tcon(tcon, volume_info) ||
            !match_prepath(sb, mnt_data)) {
                rc = 0;
                goto out;
@@ -3707,7 +3797,8 @@ remote_path_check:
                /*
                 * cifs_build_path_to_root works only when we have a valid tcon
                 */
-               full_path = cifs_build_path_to_root(volume_info, cifs_sb, tcon);
+               full_path = cifs_build_path_to_root(volume_info, cifs_sb, tcon,
+                                       tcon->Flags & SMB_SHARE_IS_IN_DFS);
                if (full_path == NULL) {
                        rc = -ENOMEM;
                        goto mount_fail_check;
@@ -3793,7 +3884,7 @@ mount_fail_check:
                else if (ses)
                        cifs_put_smb_ses(ses);
                else
-                       cifs_put_tcp_session(server);
+                       cifs_put_tcp_session(server, 0);
                bdi_destroy(&cifs_sb->bdi);
        }
 
@@ -4104,7 +4195,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
        ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
        if (IS_ERR(ses)) {
                tcon = (struct cifs_tcon *)ses;
-               cifs_put_tcp_session(master_tcon->ses->server);
+               cifs_put_tcp_session(master_tcon->ses->server, 0);
                goto out;
        }