cifs_buf_release(rsp);
}
-#define SMB2_NUM_PROT 1
+#define SMB2_NUM_PROT 2
#define SMB2_PROT 0
#define SMB21_PROT 1
req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS);
+ memcpy(req->ClientGUID, cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
+
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
return rc;
}
+static struct create_lease *
+create_lease_buf(u8 *lease_key, u8 oplock)
+{
+ struct create_lease *buf;
+
+ buf = kmalloc(sizeof(struct create_lease), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ memset(buf, 0, sizeof(struct create_lease));
+
+ buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key));
+ buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8)));
+ if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE)
+ buf->lcontext.LeaseState = SMB2_LEASE_WRITE_CACHING |
+ SMB2_LEASE_READ_CACHING;
+ else if (oplock == SMB2_OPLOCK_LEVEL_II)
+ buf->lcontext.LeaseState = SMB2_LEASE_READ_CACHING;
+ else if (oplock == SMB2_OPLOCK_LEVEL_BATCH)
+ buf->lcontext.LeaseState = SMB2_LEASE_HANDLE_CACHING |
+ SMB2_LEASE_READ_CACHING |
+ SMB2_LEASE_WRITE_CACHING;
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_lease, lcontext));
+ buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context));
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_lease, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ buf->Name[0] = 'R';
+ buf->Name[1] = 'q';
+ buf->Name[2] = 'L';
+ buf->Name[3] = 's';
+ return buf;
+}
+
+static __u8
+parse_lease_state(struct smb2_create_rsp *rsp)
+{
+ char *data_offset;
+ struct create_lease *lc;
+ bool found = false;
+
+ data_offset = (char *)rsp;
+ data_offset += 4 + le32_to_cpu(rsp->CreateContextsOffset);
+ lc = (struct create_lease *)data_offset;
+ do {
+ char *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));
+ 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);
+
+ if (!found)
+ return 0;
+
+ return smb2_map_lease_to_oplock(lc->lcontext.LeaseState);
+}
+
int
SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access,
struct smb2_create_rsp *rsp;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
- struct kvec iov[2];
+ struct kvec iov[3];
int resp_buftype;
int uni_path_len;
+ __le16 *copy_path = NULL;
+ int copy_size;
int rc = 0;
int num_iovecs = 2;
if (rc)
return rc;
- if (server->oplocks)
- req->RequestedOplockLevel = *oplock;
- else
- req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
req->ImpersonationLevel = IL_IMPERSONATION;
req->DesiredAccess = cpu_to_le32(desired_access);
/* File attributes ignored on open (used in create though) */
req->CreateOptions = cpu_to_le32(create_options);
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)
- - 1 /* pad */ - 4 /* do not count rfc1001 len field */);
+ - 8 /* pad */ - 4 /* do not count rfc1001 len field */);
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
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;
/*
*/
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;
}
+ if (!server->oplocks)
+ *oplock = SMB2_OPLOCK_LEVEL_NONE;
+
+ if (!(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
+ *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) {
+ cifs_small_buf_release(req);
+ kfree(copy_path);
+ return -ENOMEM;
+ }
+ 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);
rsp = (struct smb2_create_rsp *)iov[0].iov_base;
buf->DeletePending = 0;
}
- *oplock = rsp->OplockLevel;
+ if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
+ *oplock = parse_lease_state(rsp);
+ else
+ *oplock = rsp->OplockLevel;
creat_exit:
+ kfree(copy_path);
free_rsp_buf(resp_buftype, rsp);
return rc;
}
free_rsp_buf(resp_buftype, iov.iov_base);
return rc;
}
+
+int
+smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
+ const __u32 num_lock, struct smb2_lock_element *buf)
+{
+ int rc = 0;
+ struct smb2_lock_req *req = NULL;
+ struct kvec iov[2];
+ int resp_buf_type;
+ unsigned int count;
+
+ cFYI(1, "smb2_lockv num lock %d", num_lock);
+
+ rc = small_smb2_init(SMB2_LOCK, tcon, (void **) &req);
+ if (rc)
+ return rc;
+
+ req->hdr.ProcessId = cpu_to_le32(pid);
+ req->LockCount = cpu_to_le16(num_lock);
+
+ req->PersistentFileId = persist_fid;
+ req->VolatileFileId = volatile_fid;
+
+ count = num_lock * sizeof(struct smb2_lock_element);
+ inc_rfc1001_len(req, count - sizeof(struct smb2_lock_element));
+
+ iov[0].iov_base = (char *)req;
+ /* 4 for rfc1002 length field and count for all locks */
+ iov[0].iov_len = get_rfc1002_length(req) + 4 - count;
+ iov[1].iov_base = (char *)buf;
+ iov[1].iov_len = count;
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
+ rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP);
+ if (rc) {
+ cFYI(1, "Send error in smb2_lockv = %d", rc);
+ cifs_stats_fail_inc(tcon, SMB2_LOCK_HE);
+ }
+
+ return rc;
+}
+
+int
+SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
+ const __u64 length, const __u64 offset, const __u32 lock_flags,
+ const bool wait)
+{
+ struct smb2_lock_element lock;
+
+ lock.Offset = cpu_to_le64(offset);
+ lock.Length = cpu_to_le64(length);
+ lock.Flags = cpu_to_le32(lock_flags);
+ if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK)
+ lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
+
+ return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock);
+}
+
+int
+SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+ __u8 *lease_key, const __le32 lease_state)
+{
+ int rc;
+ struct smb2_lease_ack *req = NULL;
+
+ cFYI(1, "SMB2_lease_break");
+ rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req);
+
+ if (rc)
+ return rc;
+
+ req->hdr.CreditRequest = cpu_to_le16(1);
+ req->StructureSize = cpu_to_le16(36);
+ inc_rfc1001_len(req, 12);
+
+ memcpy(req->LeaseKey, lease_key, 16);
+ req->LeaseState = lease_state;
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
+ /* SMB2 buffer freed by function above */
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
+ cFYI(1, "Send error in Lease Break = %d", rc);
+ }
+
+ return rc;
+}