[CIFS] Fix minor endian error in durable handle patch series
[deliverable/linux.git] / fs / cifs / smb2pdu.c
index 2b95ce2b54e80c5a2f60c84b7029e43270b524f6..abc9c2809b519c50623209d341733c31a752b2b3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/smb2pdu.c
  *
- *   Copyright (C) International Business Machines  Corp., 2009, 2012
+ *   Copyright (C) International Business Machines  Corp., 2009, 2013
  *                 Etersoft, 2012
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *              Pavel Shilovsky (pshilovsky@samba.org) 2012
@@ -108,19 +108,33 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
        if (!tcon)
                goto out;
 
+       /* BB FIXME when we do write > 64K add +1 for every 64K in req or rsp */
+       /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */
+       /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
+       if ((tcon->ses) &&
+           (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
+               hdr->CreditCharge = cpu_to_le16(1);
+       /* else CreditCharge MBZ */
+
        hdr->TreeId = tcon->tid;
        /* Uid is not converted */
        if (tcon->ses)
                hdr->SessionId = tcon->ses->Suid;
-       /* BB check following DFS flags BB */
-       /* BB do we have to add check for SHI1005_FLAGS_DFS_ROOT too? */
-       if (tcon->share_flags & SHI1005_FLAGS_DFS)
-               hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS;
-       /* BB how does SMB2 do case sensitive? */
-       /* if (tcon->nocase)
-               hdr->Flags |= SMBFLG_CASELESS; */
-       if (tcon->ses && tcon->ses->server &&
-           (tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED))
+
+       /*
+        * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have
+        * to pass the path on the Open SMB prefixed by \\server\share.
+        * Not sure when we would need to do the augmented path (if ever) and
+        * setting this flag breaks the SMB2 open operation since it is
+        * illegal to send an empty path name (without \\server\share prefix)
+        * when the DFS flag is set in the SMB open header. We could
+        * consider setting the flag on all operations other than open
+        * but it is safer to net set it for now.
+        */
+/*     if (tcon->share_flags & SHI1005_FLAGS_DFS)
+               hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
+
+       if (tcon->ses && tcon->ses->server && tcon->ses->server->sign)
                hdr->Flags |= SMB2_FLAGS_SIGNED;
 out:
        pdu->StructureSize2 = cpu_to_le16(parmsize);
@@ -328,34 +342,22 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
        struct kvec iov[1];
        int rc = 0;
        int resp_buftype;
-       struct TCP_Server_Info *server;
-       unsigned int sec_flags;
-       u16 temp = 0;
+       struct TCP_Server_Info *server = ses->server;
        int blob_offset, blob_length;
        char *security_blob;
        int flags = CIFS_NEG_OP;
 
        cifs_dbg(FYI, "Negotiate protocol\n");
 
-       if (ses->server)
-               server = ses->server;
-       else {
-               rc = -EIO;
-               return rc;
+       if (!server) {
+               WARN(1, "%s: server is NULL!\n", __func__);
+               return -EIO;
        }
 
        rc = small_smb2_init(SMB2_NEGOTIATE, NULL, (void **) &req);
        if (rc)
                return rc;
 
-       /* if any of auth flags (ie not sign or seal) are overriden use them */
-       if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
-               sec_flags = ses->overrideSecFlg;  /* BB FIXME fix sign flags?*/
-       else /* if override flags set only sign/seal OR them with global auth */
-               sec_flags = global_secflags | ses->overrideSecFlg;
-
-       cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags);
-
        req->hdr.SessionId = 0;
 
        req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
@@ -364,12 +366,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
        inc_rfc1001_len(req, 2);
 
        /* only one of SMB2 signing flags may be set in SMB2 request */
-       if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN)
-               temp = SMB2_NEGOTIATE_SIGNING_REQUIRED;
-       else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */
-               temp = SMB2_NEGOTIATE_SIGNING_ENABLED;
-
-       req->SecurityMode = cpu_to_le16(temp);
+       if (ses->sign)
+               req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
+       else if (global_secflags & CIFSSEC_MAY_SIGN)
+               req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
+       else
+               req->SecurityMode = 0;
 
        req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities);
 
@@ -399,6 +401,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
                cifs_dbg(FYI, "negotiated smb2.1 dialect\n");
        else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
                cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
+       else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
+               cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
        else {
                cifs_dbg(VFS, "Illegal dialect returned by server %d\n",
                         le16_to_cpu(rsp->DialectRevision));
@@ -407,6 +411,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
        }
        server->dialect = le16_to_cpu(rsp->DialectRevision);
 
+       /* SMB2 only has an extended negflavor */
+       server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
        server->maxBuf = le32_to_cpu(rsp->MaxTransactSize);
        server->max_read = le32_to_cpu(rsp->MaxReadSize);
        server->max_write = le32_to_cpu(rsp->MaxWriteSize);
@@ -418,44 +424,22 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 
        security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
                                               &rsp->hdr);
-       if (blob_length == 0) {
-               cifs_dbg(VFS, "missing security blob on negprot\n");
-               rc = -EIO;
-               goto neg_exit;
-       }
-
-       cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags);
-       if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
-               cifs_dbg(FYI, "Signing required\n");
-               if (!(server->sec_mode & (SMB2_NEGOTIATE_SIGNING_REQUIRED |
-                     SMB2_NEGOTIATE_SIGNING_ENABLED))) {
-                       cifs_dbg(VFS, "signing required but server lacks support\n");
-                       rc = -EOPNOTSUPP;
-                       goto neg_exit;
-               }
-               server->sec_mode |= SECMODE_SIGN_REQUIRED;
-       } else if (sec_flags & CIFSSEC_MAY_SIGN) {
-               cifs_dbg(FYI, "Signing optional\n");
-               if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
-                       cifs_dbg(FYI, "Server requires signing\n");
-                       server->sec_mode |= SECMODE_SIGN_REQUIRED;
-               } else {
-                       server->sec_mode &=
-                               ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
-               }
-       } else {
-               cifs_dbg(FYI, "Signing disabled\n");
-               if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
-                       cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n");
-                       rc = -EOPNOTSUPP;
-                       goto neg_exit;
-               }
-               server->sec_mode &=
-                       ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
-       }
+       /*
+        * See MS-SMB2 section 2.2.4: if no blob, client picks default which
+        * for us will be
+        *      ses->sectype = RawNTLMSSP;
+        * but for time being this is our only auth choice so doesn't matter.
+        * We just found a server which sets blob length to zero expecting raw.
+        */
+       if (blob_length == 0)
+               cifs_dbg(FYI, "missing security blob on negprot\n");
 
+       rc = cifs_enable_signing(server, ses->sign);
 #ifdef CONFIG_SMB2_ASN1  /* BB REMOVEME when updated asn1.c ready */
-       rc = decode_neg_token_init(security_blob, blob_length,
+       if (rc)
+               goto neg_exit;
+       if (blob_length)
+               rc = decode_neg_token_init(security_blob, blob_length,
                                   &server->sec_type);
        if (rc == 1)
                rc = 0;
@@ -480,9 +464,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
        int rc = 0;
        int resp_buftype;
        __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
-       struct TCP_Server_Info *server;
-       unsigned int sec_flags;
-       u8 temp = 0;
+       struct TCP_Server_Info *server = ses->server;
        u16 blob_length = 0;
        char *security_blob;
        char *ntlmssp_blob = NULL;
@@ -490,11 +472,9 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
 
        cifs_dbg(FYI, "Session Setup\n");
 
-       if (ses->server)
-               server = ses->server;
-       else {
-               rc = -EIO;
-               return rc;
+       if (!server) {
+               WARN(1, "%s: server is NULL!\n", __func__);
+               return -EIO;
        }
 
        /*
@@ -505,7 +485,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
        if (!ses->ntlmssp)
                return -ENOMEM;
 
-       ses->server->secType = RawNTLMSSP;
+       /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
+       ses->sectype = RawNTLMSSP;
 
 ssetup_ntlmssp_authenticate:
        if (phase == NtLmChallenge)
@@ -515,28 +496,19 @@ ssetup_ntlmssp_authenticate:
        if (rc)
                return rc;
 
-       /* if any of auth flags (ie not sign or seal) are overriden use them */
-       if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
-               sec_flags = ses->overrideSecFlg;  /* BB FIXME fix sign flags?*/
-       else /* if override flags set only sign/seal OR them with global auth */
-               sec_flags = global_secflags | ses->overrideSecFlg;
-
-       cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags);
-
        req->hdr.SessionId = 0; /* First session, not a reauthenticate */
        req->VcNumber = 0; /* MBZ */
        /* to enable echos and oplocks */
        req->hdr.CreditRequest = cpu_to_le16(3);
 
        /* only one of SMB2 signing flags may be set in SMB2 request */
-       if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN)
-               temp = SMB2_NEGOTIATE_SIGNING_REQUIRED;
-       else if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
-               temp = SMB2_NEGOTIATE_SIGNING_REQUIRED;
-       else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */
-               temp = SMB2_NEGOTIATE_SIGNING_ENABLED;
-
-       req->SecurityMode = temp;
+       if (server->sign)
+               req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
+       else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */
+               req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+       else
+               req->SecurityMode = 0;
+
        req->Capabilities = 0;
        req->Channel = 0; /* MBZ */
 
@@ -679,7 +651,7 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
 
         /* since no tcon, smb2_init can not do this, so do here */
        req->hdr.SessionId = ses->Suid;
-       if (server->sec_mode & SECMODE_SIGN_REQUIRED)
+       if (server->sign)
                req->hdr.Flags |= SMB2_FLAGS_SIGNED;
 
        rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0);
@@ -788,11 +760,12 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
        }
 
        tcon->share_flags = le32_to_cpu(rsp->ShareFlags);
+       tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */
        tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
        tcon->tidStatus = CifsGood;
        tcon->need_reconnect = false;
        tcon->tid = rsp->hdr.TreeId;
-       strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
+       strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
 
        if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
            ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
@@ -874,29 +847,76 @@ create_lease_buf(u8 *lease_key, u8 oplock)
        return buf;
 }
 
+static struct create_durable *
+create_durable_buf(void)
+{
+       struct create_durable *buf;
+
+       buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->ccontext.DataOffset = cpu_to_le16(offsetof
+                                       (struct create_durable, Data));
+       buf->ccontext.DataLength = cpu_to_le32(16);
+       buf->ccontext.NameOffset = cpu_to_le16(offsetof
+                               (struct create_durable, Name));
+       buf->ccontext.NameLength = cpu_to_le16(4);
+       buf->Name[0] = 'D';
+       buf->Name[1] = 'H';
+       buf->Name[2] = 'n';
+       buf->Name[3] = 'Q';
+       return buf;
+}
+
+static struct create_durable *
+create_reconnect_durable_buf(struct cifs_fid *fid)
+{
+       struct create_durable *buf;
+
+       buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->ccontext.DataOffset = cpu_to_le16(offsetof
+                                       (struct create_durable, Data));
+       buf->ccontext.DataLength = cpu_to_le32(16);
+       buf->ccontext.NameOffset = cpu_to_le16(offsetof
+                               (struct create_durable, Name));
+       buf->ccontext.NameLength = cpu_to_le16(4);
+       buf->Data.Fid.PersistentFileId = fid->persistent_fid;
+       buf->Data.Fid.VolatileFileId = fid->volatile_fid;
+       buf->Name[0] = 'D';
+       buf->Name[1] = 'H';
+       buf->Name[2] = 'n';
+       buf->Name[3] = 'C';
+       return buf;
+}
+
 static __u8
 parse_lease_state(struct smb2_create_rsp *rsp)
 {
        char *data_offset;
        struct create_lease *lc;
        bool found = false;
+       unsigned int next = 0;
+       char *name;
 
-       data_offset = (char *)rsp;
-       data_offset += 4 + le32_to_cpu(rsp->CreateContextsOffset);
+       data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset);
        lc = (struct create_lease *)data_offset;
        do {
-               char *name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc;
+               lc = (struct create_lease *)((char *)lc + next);
+               name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc;
                if (le16_to_cpu(lc->ccontext.NameLength) != 4 ||
                    strncmp(name, "RqLs", 4)) {
-                       lc = (struct create_lease *)((char *)lc
-                                       + le32_to_cpu(lc->ccontext.Next));
+                       next = le32_to_cpu(lc->ccontext.Next);
                        continue;
                }
                if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
                        return SMB2_OPLOCK_LEVEL_NOCHANGE;
                found = true;
                break;
-       } while (le32_to_cpu(lc->ccontext.Next) != 0);
+       } while (next != 0);
 
        if (!found)
                return 0;
@@ -904,23 +924,74 @@ parse_lease_state(struct smb2_create_rsp *rsp)
        return smb2_map_lease_to_oplock(lc->lcontext.LeaseState);
 }
 
+static int
+add_lease_context(struct kvec *iov, unsigned int *num_iovec, __u8 *oplock)
+{
+       struct smb2_create_req *req = iov[0].iov_base;
+       unsigned int num = *num_iovec;
+
+       iov[num].iov_base = create_lease_buf(oplock+1, *oplock);
+       if (iov[num].iov_base == NULL)
+               return -ENOMEM;
+       iov[num].iov_len = sizeof(struct create_lease);
+       req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
+       if (!req->CreateContextsOffset)
+               req->CreateContextsOffset = cpu_to_le32(
+                               sizeof(struct smb2_create_req) - 4 +
+                               iov[num - 1].iov_len);
+       req->CreateContextsLength = cpu_to_le32(
+                               le32_to_cpu(req->CreateContextsLength) +
+                               sizeof(struct create_lease));
+       inc_rfc1001_len(&req->hdr, sizeof(struct create_lease));
+       *num_iovec = num + 1;
+       return 0;
+}
+
+static int
+add_durable_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;
+
+       if (oparms->reconnect) {
+               iov[num].iov_base = create_reconnect_durable_buf(oparms->fid);
+               /* indicate that we don't need to relock the file */
+               oparms->reconnect = false;
+       } else
+               iov[num].iov_base = create_durable_buf();
+       if (iov[num].iov_base == NULL)
+               return -ENOMEM;
+       iov[num].iov_len = sizeof(struct create_durable);
+       if (!req->CreateContextsOffset)
+               req->CreateContextsOffset =
+                       cpu_to_le32(sizeof(struct smb2_create_req) - 4 +
+                                                               iov[1].iov_len);
+       req->CreateContextsLength =
+                       cpu_to_le32(le32_to_cpu(req->CreateContextsLength) +
+                                               sizeof(struct create_durable));
+       inc_rfc1001_len(&req->hdr, sizeof(struct create_durable));
+       *num_iovec = num + 1;
+       return 0;
+}
+
 int
-SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
-         u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access,
-         __u32 create_disposition, __u32 file_attributes, __u32 create_options,
+SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
          __u8 *oplock, struct smb2_file_all_info *buf)
 {
        struct smb2_create_req *req;
        struct smb2_create_rsp *rsp;
        struct TCP_Server_Info *server;
+       struct cifs_tcon *tcon = oparms->tcon;
        struct cifs_ses *ses = tcon->ses;
-       struct kvec iov[3];
+       struct kvec iov[4];
        int resp_buftype;
        int uni_path_len;
        __le16 *copy_path = NULL;
        int copy_size;
        int rc = 0;
-       int num_iovecs = 2;
+       unsigned int num_iovecs = 2;
+       __u32 file_attributes = 0;
 
        cifs_dbg(FYI, "create/open\n");
 
@@ -933,55 +1004,47 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
        if (rc)
                return rc;
 
+       if (oparms->create_options & CREATE_OPTION_READONLY)
+               file_attributes |= ATTR_READONLY;
+
        req->ImpersonationLevel = IL_IMPERSONATION;
-       req->DesiredAccess = cpu_to_le32(desired_access);
+       req->DesiredAccess = cpu_to_le32(oparms->desired_access);
        /* File attributes ignored on open (used in create though) */
        req->FileAttributes = cpu_to_le32(file_attributes);
        req->ShareAccess = FILE_SHARE_ALL_LE;
-       req->CreateDisposition = cpu_to_le32(create_disposition);
-       req->CreateOptions = cpu_to_le32(create_options);
+       req->CreateDisposition = cpu_to_le32(oparms->disposition);
+       req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
        uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
-       req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)
-                       - 8 /* pad */ - 4 /* do not count rfc1001 len field */);
+       /* do not count rfc1001 len field */
+       req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
 
        iov[0].iov_base = (char *)req;
        /* 4 for rfc1002 length field */
        iov[0].iov_len = get_rfc1002_length(req) + 4;
 
        /* MUST set path len (NameLength) to 0 opening root of share */
-       if (uni_path_len >= 4) {
-               req->NameLength = cpu_to_le16(uni_path_len - 2);
-               /* -1 since last byte is buf[0] which is sent below (path) */
-               iov[0].iov_len--;
-               if (uni_path_len % 8 != 0) {
-                       copy_size = uni_path_len / 8 * 8;
-                       if (copy_size < uni_path_len)
-                               copy_size += 8;
-
-                       copy_path = kzalloc(copy_size, GFP_KERNEL);
-                       if (!copy_path)
-                               return -ENOMEM;
-                       memcpy((char *)copy_path, (const char *)path,
-                               uni_path_len);
-                       uni_path_len = copy_size;
-                       path = copy_path;
-               }
-
-               iov[1].iov_len = uni_path_len;
-               iov[1].iov_base = path;
-               /*
-                * -1 since last byte is buf[0] which was counted in
-                * smb2_buf_len.
-                */
-               inc_rfc1001_len(req, uni_path_len - 1);
-       } else {
-               iov[0].iov_len += 7;
-               req->hdr.smb2_buf_length = cpu_to_be32(be32_to_cpu(
-                               req->hdr.smb2_buf_length) + 8 - 1);
-               num_iovecs = 1;
-               req->NameLength = 0;
+       req->NameLength = cpu_to_le16(uni_path_len - 2);
+       /* -1 since last byte is buf[0] which is sent below (path) */
+       iov[0].iov_len--;
+       if (uni_path_len % 8 != 0) {
+               copy_size = uni_path_len / 8 * 8;
+               if (copy_size < uni_path_len)
+                       copy_size += 8;
+
+               copy_path = kzalloc(copy_size, GFP_KERNEL);
+               if (!copy_path)
+                       return -ENOMEM;
+               memcpy((char *)copy_path, (const char *)path,
+                       uni_path_len);
+               uni_path_len = copy_size;
+               path = copy_path;
        }
 
+       iov[1].iov_len = uni_path_len;
+       iov[1].iov_base = path;
+       /* -1 since last byte is buf[0] which was counted in smb2_buf_len */
+       inc_rfc1001_len(req, uni_path_len - 1);
+
        if (!server->oplocks)
                *oplock = SMB2_OPLOCK_LEVEL_NONE;
 
@@ -989,21 +1052,29 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
            *oplock == SMB2_OPLOCK_LEVEL_NONE)
                req->RequestedOplockLevel = *oplock;
        else {
-               iov[num_iovecs].iov_base = create_lease_buf(oplock+1, *oplock);
-               if (iov[num_iovecs].iov_base == NULL) {
+               rc = add_lease_context(iov, &num_iovecs, oplock);
+               if (rc) {
                        cifs_small_buf_release(req);
                        kfree(copy_path);
-                       return -ENOMEM;
+                       return rc;
+               }
+       }
+
+       if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
+               /* need to set Next field of lease context if we request it */
+               if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) {
+                       struct create_context *ccontext =
+                           (struct create_context *)iov[num_iovecs-1].iov_base;
+                       ccontext->Next =
+                               cpu_to_le32(sizeof(struct create_lease));
+               }
+               rc = add_durable_context(iov, &num_iovecs, oparms);
+               if (rc) {
+                       cifs_small_buf_release(req);
+                       kfree(copy_path);
+                       kfree(iov[num_iovecs-1].iov_base);
+                       return rc;
                }
-               iov[num_iovecs].iov_len = sizeof(struct create_lease);
-               req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
-               req->CreateContextsOffset = cpu_to_le32(
-                       sizeof(struct smb2_create_req) - 4 - 8 +
-                       iov[num_iovecs-1].iov_len);
-               req->CreateContextsLength = cpu_to_le32(
-                       sizeof(struct create_lease));
-               inc_rfc1001_len(&req->hdr, sizeof(struct create_lease));
-               num_iovecs++;
        }
 
        rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
@@ -1014,8 +1085,8 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
                goto creat_exit;
        }
 
-       *persistent_fid = rsp->PersistentFileId;
-       *volatile_fid = rsp->VolatileFileId;
+       oparms->fid->persistent_fid = rsp->PersistentFileId;
+       oparms->fid->volatile_fid = rsp->VolatileFileId;
 
        if (buf) {
                memcpy(buf, &rsp->CreationTime, 32);
@@ -1036,6 +1107,122 @@ creat_exit:
        return rc;
 }
 
+/*
+ *     SMB2 IOCTL is used for both IOCTLs and FSCTLs
+ */
+int
+SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+          u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data,
+          u32 indatalen, char **out_data, u32 *plen /* returned data len */)
+{
+       struct smb2_ioctl_req *req;
+       struct smb2_ioctl_rsp *rsp;
+       struct TCP_Server_Info *server;
+       struct cifs_ses *ses = tcon->ses;
+       struct kvec iov[2];
+       int resp_buftype;
+       int num_iovecs;
+       int rc = 0;
+
+       cifs_dbg(FYI, "SMB2 IOCTL\n");
+
+       /* zero out returned data len, in case of error */
+       if (plen)
+               *plen = 0;
+
+       if (ses && (ses->server))
+               server = ses->server;
+       else
+               return -EIO;
+
+       rc = small_smb2_init(SMB2_IOCTL, tcon, (void **) &req);
+       if (rc)
+               return rc;
+
+       req->CtlCode = cpu_to_le32(opcode);
+       req->PersistentFileId = persistent_fid;
+       req->VolatileFileId = volatile_fid;
+
+       if (indatalen) {
+               req->InputCount = cpu_to_le32(indatalen);
+               /* do not set InputOffset if no input data */
+               req->InputOffset =
+                      cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer) - 4);
+               iov[1].iov_base = in_data;
+               iov[1].iov_len = indatalen;
+               num_iovecs = 2;
+       } else
+               num_iovecs = 1;
+
+       req->OutputOffset = 0;
+       req->OutputCount = 0; /* MBZ */
+
+       /*
+        * Could increase MaxOutputResponse, but that would require more
+        * than one credit. Windows typically sets this smaller, but for some
+        * ioctls it may be useful to allow server to send more. No point
+        * limiting what the server can send as long as fits in one credit
+        */
+       req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */
+
+       if (is_fsctl)
+               req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
+       else
+               req->Flags = 0;
+
+       iov[0].iov_base = (char *)req;
+       /* 4 for rfc1002 length field */
+       iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+       if (indatalen)
+               inc_rfc1001_len(req, indatalen);
+
+       rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
+       rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
+
+       if (rc != 0) {
+               if (tcon)
+                       cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+               goto ioctl_exit;
+       }
+
+       /* check if caller wants to look at return data or just return rc */
+       if ((plen == NULL) || (out_data == NULL))
+               goto ioctl_exit;
+
+       *plen = le32_to_cpu(rsp->OutputCount);
+
+       /* We check for obvious errors in the output buffer length and offset */
+       if (*plen == 0)
+               goto ioctl_exit; /* server returned no data */
+       else if (*plen > 0xFF00) {
+               cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
+               *plen = 0;
+               rc = -EIO;
+               goto ioctl_exit;
+       }
+
+       if (get_rfc1002_length(rsp) < le32_to_cpu(rsp->OutputOffset) + *plen) {
+               cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen,
+                       le32_to_cpu(rsp->OutputOffset));
+               *plen = 0;
+               rc = -EIO;
+               goto ioctl_exit;
+       }
+
+       *out_data = kmalloc(*plen, GFP_KERNEL);
+       if (*out_data == NULL) {
+               rc = -ENOMEM;
+               goto ioctl_exit;
+       }
+
+       memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset),
+              *plen);
+ioctl_exit:
+       free_rsp_buf(resp_buftype, rsp);
+       return rc;
+}
+
 int
 SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
           u64 persistent_fid, u64 volatile_fid)
@@ -1384,8 +1571,7 @@ smb2_readv_callback(struct mid_q_entry *mid)
        case MID_RESPONSE_RECEIVED:
                credits_received = le16_to_cpu(buf->CreditRequest);
                /* result already set, check signature */
-               if (server->sec_mode &
-                   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
+               if (server->sign) {
                        int rc;
 
                        rc = smb2_verify_signature(&rqst, server);
This page took 0.03142 seconds and 5 git commands to generate.