sparc64: Export flush_ptrace_access() (needed by lustre)
[cascardo/linux.git] / fs / cifs / smb2pdu.c
index 2b312e4..abc9c28 100644 (file)
@@ -847,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;
@@ -877,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");
 
@@ -906,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;
 
@@ -962,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);
@@ -987,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);