Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 14 Nov 2015 00:40:36 +0000 (16:40 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 14 Nov 2015 00:40:36 +0000 (16:40 -0800)
Pull SMB3 updates from Steve French:
 "A collection of SMB3 patches adding some reliability features
  (persistent and resilient handles) and improving SMB3 copy offload.

  I will have some additional patches for SMB3 encryption and SMB3.1.1
  signing (important security features), and also for improving SMB3
  persistent handle reconnection (setting ChannelSequence number e.g.)
  that I am still working on but wanted to get this set in since they
  can stand alone"

* 'for-next' of git://git.samba.org/sfrench/cifs-2.6:
  Allow copy offload (CopyChunk) across shares
  Add resilienthandles mount parm
  [SMB3] Send durable handle v2 contexts when use of persistent handles required
  [SMB3] Display persistenthandles in /proc/mounts for SMB3 shares if enabled
  [SMB3] Enable checking for continuous availability and persistent handle support
  [SMB3] Add parsing for new mount option controlling persistent handles
  Allow duplicate extents in SMB3 not just SMB3.1.1

1  2 
fs/cifs/connect.c
fs/cifs/smb2pdu.c

diff --combined fs/cifs/connect.c
index 3f2228570d441840b7e6d11c10a3b814a2b60962,4042996ddc7c6f6a7ccc5c389b20c5147751800b..ecb0803bdb0e50479f50b0e874a4e87cb064e2d0
@@@ -87,6 -87,8 +87,8 @@@ enum 
        Opt_sign, Opt_seal, Opt_noac,
        Opt_fsc, Opt_mfsymlinks,
        Opt_multiuser, Opt_sloppy, Opt_nosharesock,
+       Opt_persistent, Opt_nopersistent,
+       Opt_resilient, Opt_noresilient,
  
        /* Mount options which take numeric value */
        Opt_backupuid, Opt_backupgid, Opt_uid,
@@@ -169,6 -171,10 +171,10 @@@ static const match_table_t cifs_mount_o
        { Opt_multiuser, "multiuser" },
        { Opt_sloppy, "sloppy" },
        { Opt_nosharesock, "nosharesock" },
+       { Opt_persistent, "persistenthandles"},
+       { Opt_nopersistent, "nopersistenthandles"},
+       { Opt_resilient, "resilienthandles"},
+       { Opt_noresilient, "noresilienthandles"},
  
        { Opt_backupuid, "backupuid=%s" },
        { Opt_backupgid, "backupgid=%s" },
@@@ -1497,6 -1503,33 +1503,33 @@@ cifs_parse_mount_options(const char *mo
                case Opt_nosharesock:
                        vol->nosharesock = true;
                        break;
+               case Opt_nopersistent:
+                       vol->nopersistent = true;
+                       if (vol->persistent) {
+                               cifs_dbg(VFS,
+                                 "persistenthandles mount options conflict\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_persistent:
+                       vol->persistent = true;
+                       if ((vol->nopersistent) || (vol->resilient)) {
+                               cifs_dbg(VFS,
+                                 "persistenthandles mount options conflict\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_resilient:
+                       vol->resilient = true;
+                       if (vol->persistent) {
+                               cifs_dbg(VFS,
+                                 "persistenthandles mount options conflict\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_noresilient:
+                       vol->resilient = false; /* already the default */
+                       break;
  
                /* Numeric Values */
                case Opt_backupuid:
@@@ -2325,14 -2358,13 +2358,14 @@@ static in
  cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
  {
        int rc = 0;
 -      char *desc, *delim, *payload;
 +      const char *delim, *payload;
 +      char *desc;
        ssize_t len;
        struct key *key;
        struct TCP_Server_Info *server = ses->server;
        struct sockaddr_in *sa;
        struct sockaddr_in6 *sa6;
 -      struct user_key_payload *upayload;
 +      const struct user_key_payload *upayload;
  
        desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL);
        if (!desc)
        }
  
        down_read(&key->sem);
 -      upayload = key->payload.data;
 +      upayload = user_key_payload(key);
        if (IS_ERR_OR_NULL(upayload)) {
                rc = upayload ? PTR_ERR(upayload) : -EINVAL;
                goto out_key_put;
        }
  
        /* find first : in payload */
 -      payload = (char *)upayload->data;
 +      payload = upayload->data;
        delim = strnchr(payload, upayload->datalen, ':');
        cifs_dbg(FYI, "payload=%s\n", payload);
        if (!delim) {
@@@ -2655,6 -2687,42 +2688,42 @@@ cifs_get_tcon(struct cifs_ses *ses, str
                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) {
+               if (ses->server->vals->protocol_id == 0) {
+                       cifs_dbg(VFS,
+                            "SMB3 or later required for persistent handles\n");
+                       rc = -EOPNOTSUPP;
+                       goto out_fail;
+ #ifdef CONFIG_CIFS_SMB2
+               } else if (ses->server->capabilities &
+                          SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
+                       tcon->use_persistent = true;
+               else /* persistent handles requested but not supported */ {
+                       cifs_dbg(VFS,
+                               "Persistent handles not supported on share\n");
+                       rc = -EOPNOTSUPP;
+                       goto out_fail;
+ #endif /* CONFIG_CIFS_SMB2 */
+               }
+ #ifdef CONFIG_CIFS_SMB2
+       } else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY)
+            && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
+            && (volume_info->nopersistent == false)) {
+               cifs_dbg(FYI, "enabling persistent handles\n");
+               tcon->use_persistent = true;
+ #endif /* CONFIG_CIFS_SMB2 */
+       } else if (volume_info->resilient) {
+               if (ses->server->vals->protocol_id == 0) {
+                       cifs_dbg(VFS,
+                            "SMB2.1 or later required for resilient handles\n");
+                       rc = -EOPNOTSUPP;
+                       goto out_fail;
+               }
+               tcon->use_resilient = true;
+       }
        /*
         * 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
@@@ -3503,6 -3571,15 +3572,15 @@@ try_mount_again
                goto mount_fail_check;
        }
  
+ #ifdef CONFIG_CIFS_SMB2
+       if ((volume_info->persistent == true) && ((ses->server->capabilities &
+               SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == 0)) {
+               cifs_dbg(VFS, "persistent handles not supported by server\n");
+               rc = -EOPNOTSUPP;
+               goto mount_fail_check;
+       }
+ #endif /* CONFIG_CIFS_SMB2*/
        /* search for existing tcon to this server share */
        tcon = cifs_get_tcon(ses, volume_info);
        if (IS_ERR(tcon)) {
diff --combined fs/cifs/smb2pdu.c
index 61276929d139f64d1309481e69f2dfce2c8f1fe5,2520ed5ded48b7bb20d0187c8d56dc6723cb6fef..767555518d40ccd357b6f5540f2758bb8be6e654
@@@ -660,7 -660,7 +660,7 @@@ ssetup_ntlmssp_authenticate
                        goto ssetup_exit;
                }
  
 -              msg = spnego_key->payload.data;
 +              msg = spnego_key->payload.data[0];
                /*
                 * check version field to make sure that cifs.upcall is
                 * sending us a response in an expected form
@@@ -1151,13 -1151,130 +1151,130 @@@ add_lease_context(struct TCP_Server_Inf
        return 0;
  }
  
+ static struct create_durable_v2 *
+ create_durable_v2_buf(struct cifs_fid *pfid)
+ {
+       struct create_durable_v2 *buf;
+       buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+       buf->ccontext.DataOffset = cpu_to_le16(offsetof
+                                       (struct create_durable_v2, dcontext));
+       buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2));
+       buf->ccontext.NameOffset = cpu_to_le16(offsetof
+                               (struct create_durable_v2, Name));
+       buf->ccontext.NameLength = cpu_to_le16(4);
+       buf->dcontext.Timeout = 0; /* Should this be configurable by workload */
+       buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
+       get_random_bytes(buf->dcontext.CreateGuid, 16);
+       memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
+       /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
+       buf->Name[0] = 'D';
+       buf->Name[1] = 'H';
+       buf->Name[2] = '2';
+       buf->Name[3] = 'Q';
+       return buf;
+ }
+ static struct create_durable_handle_reconnect_v2 *
+ create_reconnect_durable_v2_buf(struct cifs_fid *fid)
+ {
+       struct create_durable_handle_reconnect_v2 *buf;
+       buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2),
+                       GFP_KERNEL);
+       if (!buf)
+               return NULL;
+       buf->ccontext.DataOffset =
+               cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2,
+                                    dcontext));
+       buf->ccontext.DataLength =
+               cpu_to_le32(sizeof(struct durable_reconnect_context_v2));
+       buf->ccontext.NameOffset =
+               cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2,
+                           Name));
+       buf->ccontext.NameLength = cpu_to_le16(4);
+       buf->dcontext.Fid.PersistentFileId = fid->persistent_fid;
+       buf->dcontext.Fid.VolatileFileId = fid->volatile_fid;
+       buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
+       memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16);
+       /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */
+       buf->Name[0] = 'D';
+       buf->Name[1] = 'H';
+       buf->Name[2] = '2';
+       buf->Name[3] = 'C';
+       return buf;
+ }
  static int
- add_durable_context(struct kvec *iov, unsigned int *num_iovec,
+ add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec,
+                   struct cifs_open_parms *oparms)
+ {
+       struct smb2_create_req *req = iov[0].iov_base;
+       unsigned int num = *num_iovec;
+       iov[num].iov_base = create_durable_v2_buf(oparms->fid);
+       if (iov[num].iov_base == NULL)
+               return -ENOMEM;
+       iov[num].iov_len = sizeof(struct create_durable_v2);
+       if (!req->CreateContextsOffset)
+               req->CreateContextsOffset =
+                       cpu_to_le32(sizeof(struct smb2_create_req) - 4 +
+                                                               iov[1].iov_len);
+       le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable_v2));
+       inc_rfc1001_len(&req->hdr, sizeof(struct create_durable_v2));
+       *num_iovec = num + 1;
+       return 0;
+ }
+ static int
+ add_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec,
                    struct cifs_open_parms *oparms)
  {
        struct smb2_create_req *req = iov[0].iov_base;
        unsigned int num = *num_iovec;
  
+       /* indicate that we don't need to relock the file */
+       oparms->reconnect = false;
+       iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid);
+       if (iov[num].iov_base == NULL)
+               return -ENOMEM;
+       iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2);
+       if (!req->CreateContextsOffset)
+               req->CreateContextsOffset =
+                       cpu_to_le32(sizeof(struct smb2_create_req) - 4 +
+                                                               iov[1].iov_len);
+       le32_add_cpu(&req->CreateContextsLength,
+                       sizeof(struct create_durable_handle_reconnect_v2));
+       inc_rfc1001_len(&req->hdr,
+                       sizeof(struct create_durable_handle_reconnect_v2));
+       *num_iovec = num + 1;
+       return 0;
+ }
+ static int
+ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
+                   struct cifs_open_parms *oparms, bool use_persistent)
+ {
+       struct smb2_create_req *req = iov[0].iov_base;
+       unsigned int num = *num_iovec;
+       if (use_persistent) {
+               if (oparms->reconnect)
+                       return add_durable_reconnect_v2_context(iov, num_iovec,
+                                                               oparms);
+               else
+                       return add_durable_v2_context(iov, num_iovec, oparms);
+       }
        if (oparms->reconnect) {
                iov[num].iov_base = create_reconnect_durable_buf(oparms->fid);
                /* indicate that we don't need to relock the file */
@@@ -1275,7 -1392,9 +1392,9 @@@ SMB2_open(const unsigned int xid, struc
                        ccontext->Next =
                                cpu_to_le32(server->vals->create_lease_size);
                }
-               rc = add_durable_context(iov, &num_iovecs, oparms);
+               rc = add_durable_context(iov, &num_iovecs, oparms,
+                                       tcon->use_persistent);
                if (rc) {
                        cifs_small_buf_release(req);
                        kfree(copy_path);