mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-12 12:22:42 +00:00
CIFS: Request SMB2.1 leases
if server supports them and we need oplocks. Signed-off-by: Pavel Shilovsky <piastryyy@gmail.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
parent
579f905323
commit
b8c32dbb0d
@ -36,6 +36,7 @@
|
|||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/freezer.h>
|
#include <linux/freezer.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
|
#include <linux/random.h>
|
||||||
#include <net/ipv6.h>
|
#include <net/ipv6.h>
|
||||||
#include "cifsfs.h"
|
#include "cifsfs.h"
|
||||||
#include "cifspdu.h"
|
#include "cifspdu.h"
|
||||||
@ -88,6 +89,10 @@ extern mempool_t *cifs_mid_poolp;
|
|||||||
|
|
||||||
struct workqueue_struct *cifsiod_wq;
|
struct workqueue_struct *cifsiod_wq;
|
||||||
|
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
__u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cifs_read_super(struct super_block *sb)
|
cifs_read_super(struct super_block *sb)
|
||||||
{
|
{
|
||||||
@ -218,9 +223,10 @@ cifs_alloc_inode(struct super_block *sb)
|
|||||||
return NULL;
|
return NULL;
|
||||||
cifs_inode->cifsAttrs = 0x20; /* default */
|
cifs_inode->cifsAttrs = 0x20; /* default */
|
||||||
cifs_inode->time = 0;
|
cifs_inode->time = 0;
|
||||||
/* Until the file is open and we have gotten oplock
|
/*
|
||||||
info back from the server, can not assume caching of
|
* Until the file is open and we have gotten oplock info back from the
|
||||||
file data or metadata */
|
* server, can not assume caching of file data or metadata.
|
||||||
|
*/
|
||||||
cifs_set_oplock_level(cifs_inode, 0);
|
cifs_set_oplock_level(cifs_inode, 0);
|
||||||
cifs_inode->delete_pending = false;
|
cifs_inode->delete_pending = false;
|
||||||
cifs_inode->invalid_mapping = false;
|
cifs_inode->invalid_mapping = false;
|
||||||
@ -228,10 +234,14 @@ cifs_alloc_inode(struct super_block *sb)
|
|||||||
cifs_inode->server_eof = 0;
|
cifs_inode->server_eof = 0;
|
||||||
cifs_inode->uniqueid = 0;
|
cifs_inode->uniqueid = 0;
|
||||||
cifs_inode->createtime = 0;
|
cifs_inode->createtime = 0;
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
/* Can not set i_flags here - they get immediately overwritten
|
get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
to zero by the VFS */
|
#endif
|
||||||
/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;*/
|
/*
|
||||||
|
* Can not set i_flags here - they get immediately overwritten to zero
|
||||||
|
* by the VFS.
|
||||||
|
*/
|
||||||
|
/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */
|
||||||
INIT_LIST_HEAD(&cifs_inode->openFileList);
|
INIT_LIST_HEAD(&cifs_inode->openFileList);
|
||||||
INIT_LIST_HEAD(&cifs_inode->llist);
|
INIT_LIST_HEAD(&cifs_inode->llist);
|
||||||
return &cifs_inode->vfs_inode;
|
return &cifs_inode->vfs_inode;
|
||||||
@ -1107,6 +1117,10 @@ init_cifs(void)
|
|||||||
spin_lock_init(&cifs_file_list_lock);
|
spin_lock_init(&cifs_file_list_lock);
|
||||||
spin_lock_init(&GlobalMid_Lock);
|
spin_lock_init(&GlobalMid_Lock);
|
||||||
|
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
get_random_bytes(cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (cifs_max_pending < 2) {
|
if (cifs_max_pending < 2) {
|
||||||
cifs_max_pending = 2;
|
cifs_max_pending = 2;
|
||||||
cFYI(1, "cifs_max_pending set to min of 2");
|
cFYI(1, "cifs_max_pending set to min of 2");
|
||||||
|
@ -361,6 +361,12 @@ struct smb_version_operations {
|
|||||||
const unsigned int);
|
const unsigned int);
|
||||||
/* push brlocks from the cache to the server */
|
/* push brlocks from the cache to the server */
|
||||||
int (*push_mand_locks)(struct cifsFileInfo *);
|
int (*push_mand_locks)(struct cifsFileInfo *);
|
||||||
|
/* get lease key of the inode */
|
||||||
|
void (*get_lease_key)(struct inode *, struct cifs_fid *fid);
|
||||||
|
/* set lease key of the inode */
|
||||||
|
void (*set_lease_key)(struct inode *, struct cifs_fid *fid);
|
||||||
|
/* generate new lease key */
|
||||||
|
void (*new_lease_key)(struct cifs_fid *fid);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values {
|
struct smb_version_values {
|
||||||
@ -895,6 +901,7 @@ struct cifs_fid {
|
|||||||
#ifdef CONFIG_CIFS_SMB2
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
__u64 persistent_fid; /* persist file id for smb2 */
|
__u64 persistent_fid; /* persist file id for smb2 */
|
||||||
__u64 volatile_fid; /* volatile file id for smb2 */
|
__u64 volatile_fid; /* volatile file id for smb2 */
|
||||||
|
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1012,6 +1019,9 @@ struct cifsInodeInfo {
|
|||||||
u64 server_eof; /* current file size on server -- protected by i_lock */
|
u64 server_eof; /* current file size on server -- protected by i_lock */
|
||||||
u64 uniqueid; /* server inode number */
|
u64 uniqueid; /* server inode number */
|
||||||
u64 createtime; /* creation time on server */
|
u64 createtime; /* creation time on server */
|
||||||
|
#ifdef CONFIG_CIFS_SMB2
|
||||||
|
__u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_CIFS_FSCACHE
|
#ifdef CONFIG_CIFS_FSCACHE
|
||||||
struct fscache_cookie *fscache;
|
struct fscache_cookie *fscache;
|
||||||
#endif
|
#endif
|
||||||
|
@ -340,6 +340,8 @@ cifs_create_get_file_info:
|
|||||||
rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb,
|
rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb,
|
||||||
xid, &fid->netfid);
|
xid, &fid->netfid);
|
||||||
if (newinode) {
|
if (newinode) {
|
||||||
|
if (server->ops->set_lease_key)
|
||||||
|
server->ops->set_lease_key(newinode, fid);
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
|
||||||
newinode->i_mode = mode;
|
newinode->i_mode = mode;
|
||||||
if ((*oplock & CIFS_CREATE_ACTION) &&
|
if ((*oplock & CIFS_CREATE_ACTION) &&
|
||||||
@ -418,6 +420,9 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
|
|||||||
tcon = tlink_tcon(tlink);
|
tcon = tlink_tcon(tlink);
|
||||||
server = tcon->ses->server;
|
server = tcon->ses->server;
|
||||||
|
|
||||||
|
if (server->ops->new_lease_key)
|
||||||
|
server->ops->new_lease_key(&fid);
|
||||||
|
|
||||||
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
||||||
&oplock, &fid, opened);
|
&oplock, &fid, opened);
|
||||||
|
|
||||||
@ -473,10 +478,14 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode,
|
|||||||
if (IS_ERR(tlink))
|
if (IS_ERR(tlink))
|
||||||
goto out_free_xid;
|
goto out_free_xid;
|
||||||
|
|
||||||
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
|
||||||
&oplock, &fid, &created);
|
|
||||||
tcon = tlink_tcon(tlink);
|
tcon = tlink_tcon(tlink);
|
||||||
server = tcon->ses->server;
|
server = tcon->ses->server;
|
||||||
|
|
||||||
|
if (server->ops->new_lease_key)
|
||||||
|
server->ops->new_lease_key(&fid);
|
||||||
|
|
||||||
|
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
|
||||||
|
&oplock, &fid, &created);
|
||||||
if (!rc && server->ops->close)
|
if (!rc && server->ops->close)
|
||||||
server->ops->close(xid, tcon, &fid);
|
server->ops->close(xid, tcon, &fid);
|
||||||
|
|
||||||
|
@ -177,8 +177,9 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
|
|||||||
int disposition;
|
int disposition;
|
||||||
int create_options = CREATE_NOT_DIR;
|
int create_options = CREATE_NOT_DIR;
|
||||||
FILE_ALL_INFO *buf;
|
FILE_ALL_INFO *buf;
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
|
||||||
if (!tcon->ses->server->ops->open)
|
if (!server->ops->open)
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
|
|
||||||
desired_access = cifs_convert_flags(f_flags);
|
desired_access = cifs_convert_flags(f_flags);
|
||||||
@ -218,9 +219,9 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
|
|||||||
if (backup_cred(cifs_sb))
|
if (backup_cred(cifs_sb))
|
||||||
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
||||||
|
|
||||||
rc = tcon->ses->server->ops->open(xid, tcon, full_path, disposition,
|
rc = server->ops->open(xid, tcon, full_path, disposition,
|
||||||
desired_access, create_options, fid,
|
desired_access, create_options, fid, oplock, buf,
|
||||||
oplock, buf, cifs_sb);
|
cifs_sb);
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
@ -372,6 +373,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
__u32 oplock;
|
__u32 oplock;
|
||||||
struct cifs_sb_info *cifs_sb;
|
struct cifs_sb_info *cifs_sb;
|
||||||
|
struct TCP_Server_Info *server;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
struct tcon_link *tlink;
|
struct tcon_link *tlink;
|
||||||
struct cifsFileInfo *cfile = NULL;
|
struct cifsFileInfo *cfile = NULL;
|
||||||
@ -388,6 +390,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||||||
return PTR_ERR(tlink);
|
return PTR_ERR(tlink);
|
||||||
}
|
}
|
||||||
tcon = tlink_tcon(tlink);
|
tcon = tlink_tcon(tlink);
|
||||||
|
server = tcon->ses->server;
|
||||||
|
|
||||||
full_path = build_path_from_dentry(file->f_path.dentry);
|
full_path = build_path_from_dentry(file->f_path.dentry);
|
||||||
if (full_path == NULL) {
|
if (full_path == NULL) {
|
||||||
@ -432,6 +435,9 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!posix_open_ok) {
|
if (!posix_open_ok) {
|
||||||
|
if (server->ops->get_lease_key)
|
||||||
|
server->ops->get_lease_key(inode, &fid);
|
||||||
|
|
||||||
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
|
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
|
||||||
file->f_flags, &oplock, &fid, xid);
|
file->f_flags, &oplock, &fid, xid);
|
||||||
if (rc)
|
if (rc)
|
||||||
@ -440,8 +446,8 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||||||
|
|
||||||
cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
|
cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
|
||||||
if (cfile == NULL) {
|
if (cfile == NULL) {
|
||||||
if (tcon->ses->server->ops->close)
|
if (server->ops->close)
|
||||||
tcon->ses->server->ops->close(xid, tcon, &fid);
|
server->ops->close(xid, tcon, &fid);
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -567,6 +573,9 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
|||||||
if (backup_cred(cifs_sb))
|
if (backup_cred(cifs_sb))
|
||||||
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
create_options |= CREATE_OPEN_BACKUP_INTENT;
|
||||||
|
|
||||||
|
if (server->ops->get_lease_key)
|
||||||
|
server->ops->get_lease_key(inode, &fid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Can not refresh inode by passing in file_info buf to be returned by
|
* Can not refresh inode by passing in file_info buf to be returned by
|
||||||
* CIFSSMBOpen and then calling get_inode_info with returned buf since
|
* CIFSSMBOpen and then calling get_inode_info with returned buf since
|
||||||
|
@ -63,6 +63,7 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
|
|||||||
int rc;
|
int rc;
|
||||||
__le16 *smb2_path;
|
__le16 *smb2_path;
|
||||||
struct smb2_file_all_info *smb2_data = NULL;
|
struct smb2_file_all_info *smb2_data = NULL;
|
||||||
|
__u8 smb2_oplock[17];
|
||||||
|
|
||||||
smb2_path = cifs_convert_path_to_utf16(path, cifs_sb);
|
smb2_path = cifs_convert_path_to_utf16(path, cifs_sb);
|
||||||
if (smb2_path == NULL) {
|
if (smb2_path == NULL) {
|
||||||
@ -78,11 +79,14 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
desired_access |= FILE_READ_ATTRIBUTES;
|
desired_access |= FILE_READ_ATTRIBUTES;
|
||||||
*oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
|
*smb2_oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
|
||||||
|
|
||||||
|
if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
|
||||||
|
memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
|
||||||
rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid,
|
rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid,
|
||||||
&fid->volatile_fid, desired_access, disposition,
|
&fid->volatile_fid, desired_access, disposition,
|
||||||
0, 0, (__u8 *)oplock, smb2_data);
|
0, 0, smb2_oplock, smb2_data);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -99,6 +103,7 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
|
|||||||
move_smb2_info_to_cifs(buf, smb2_data);
|
move_smb2_info_to_cifs(buf, smb2_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*oplock = *smb2_oplock;
|
||||||
out:
|
out:
|
||||||
kfree(smb2_data);
|
kfree(smb2_data);
|
||||||
kfree(smb2_path);
|
kfree(smb2_path);
|
||||||
|
@ -555,6 +555,24 @@ smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
|
|||||||
current->tgid, length, offset, type, wait);
|
current->tgid, length, offset, type, wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smb2_new_lease_key(struct cifs_fid *fid)
|
||||||
|
{
|
||||||
|
get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
struct smb_version_operations smb21_operations = {
|
struct smb_version_operations smb21_operations = {
|
||||||
.compare_fids = smb2_compare_fids,
|
.compare_fids = smb2_compare_fids,
|
||||||
.setup_request = smb2_setup_request,
|
.setup_request = smb2_setup_request,
|
||||||
@ -616,6 +634,9 @@ struct smb_version_operations smb21_operations = {
|
|||||||
.mand_lock = smb2_mand_lock,
|
.mand_lock = smb2_mand_lock,
|
||||||
.mand_unlock_range = smb2_unlock_range,
|
.mand_unlock_range = smb2_unlock_range,
|
||||||
.push_mand_locks = smb2_push_mandatory_locks,
|
.push_mand_locks = smb2_push_mandatory_locks,
|
||||||
|
.get_lease_key = smb2_get_lease_key,
|
||||||
|
.set_lease_key = smb2_set_lease_key,
|
||||||
|
.new_lease_key = smb2_new_lease_key,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values smb21_values = {
|
struct smb_version_values smb21_values = {
|
||||||
|
@ -304,7 +304,7 @@ free_rsp_buf(int resp_buftype, void *rsp)
|
|||||||
cifs_buf_release(rsp);
|
cifs_buf_release(rsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SMB2_NUM_PROT 1
|
#define SMB2_NUM_PROT 2
|
||||||
|
|
||||||
#define SMB2_PROT 0
|
#define SMB2_PROT 0
|
||||||
#define SMB21_PROT 1
|
#define SMB21_PROT 1
|
||||||
@ -393,6 +393,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
|||||||
|
|
||||||
req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS);
|
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;
|
iov[0].iov_base = (char *)req;
|
||||||
/* 4 for rfc1002 length field */
|
/* 4 for rfc1002 length field */
|
||||||
iov[0].iov_len = get_rfc1002_length(req) + 4;
|
iov[0].iov_len = get_rfc1002_length(req) + 4;
|
||||||
@ -868,6 +870,83 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
|
|||||||
return rc;
|
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;
|
||||||
|
__u8 oplock = 0;
|
||||||
|
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 oplock;
|
||||||
|
|
||||||
|
if (le32_to_cpu(lc->lcontext.LeaseState) & SMB2_LEASE_WRITE_CACHING) {
|
||||||
|
if (le32_to_cpu(lc->lcontext.LeaseState) &
|
||||||
|
SMB2_LEASE_HANDLE_CACHING)
|
||||||
|
oplock = SMB2_OPLOCK_LEVEL_BATCH;
|
||||||
|
else
|
||||||
|
oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
|
||||||
|
} else if (le32_to_cpu(lc->lcontext.LeaseState) &
|
||||||
|
SMB2_LEASE_READ_CACHING)
|
||||||
|
oplock = SMB2_OPLOCK_LEVEL_II;
|
||||||
|
|
||||||
|
return oplock;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
|
SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
|
||||||
u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access,
|
u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access,
|
||||||
@ -878,9 +957,11 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
|
|||||||
struct smb2_create_rsp *rsp;
|
struct smb2_create_rsp *rsp;
|
||||||
struct TCP_Server_Info *server;
|
struct TCP_Server_Info *server;
|
||||||
struct cifs_ses *ses = tcon->ses;
|
struct cifs_ses *ses = tcon->ses;
|
||||||
struct kvec iov[2];
|
struct kvec iov[3];
|
||||||
int resp_buftype;
|
int resp_buftype;
|
||||||
int uni_path_len;
|
int uni_path_len;
|
||||||
|
__le16 *copy_path = NULL;
|
||||||
|
int copy_size;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int num_iovecs = 2;
|
int num_iovecs = 2;
|
||||||
|
|
||||||
@ -895,10 +976,6 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
|
|||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if (server->oplocks)
|
|
||||||
req->RequestedOplockLevel = *oplock;
|
|
||||||
else
|
|
||||||
req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
|
|
||||||
req->ImpersonationLevel = IL_IMPERSONATION;
|
req->ImpersonationLevel = IL_IMPERSONATION;
|
||||||
req->DesiredAccess = cpu_to_le32(desired_access);
|
req->DesiredAccess = cpu_to_le32(desired_access);
|
||||||
/* File attributes ignored on open (used in create though) */
|
/* File attributes ignored on open (used in create though) */
|
||||||
@ -908,7 +985,7 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
|
|||||||
req->CreateOptions = cpu_to_le32(create_options);
|
req->CreateOptions = cpu_to_le32(create_options);
|
||||||
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
|
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
|
||||||
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)
|
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;
|
iov[0].iov_base = (char *)req;
|
||||||
/* 4 for rfc1002 length field */
|
/* 4 for rfc1002 length field */
|
||||||
@ -919,6 +996,20 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
|
|||||||
req->NameLength = cpu_to_le16(uni_path_len - 2);
|
req->NameLength = cpu_to_le16(uni_path_len - 2);
|
||||||
/* -1 since last byte is buf[0] which is sent below (path) */
|
/* -1 since last byte is buf[0] which is sent below (path) */
|
||||||
iov[0].iov_len--;
|
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_len = uni_path_len;
|
||||||
iov[1].iov_base = path;
|
iov[1].iov_base = path;
|
||||||
/*
|
/*
|
||||||
@ -927,10 +1018,37 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
|
|||||||
*/
|
*/
|
||||||
inc_rfc1001_len(req, uni_path_len - 1);
|
inc_rfc1001_len(req, uni_path_len - 1);
|
||||||
} else {
|
} 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;
|
num_iovecs = 1;
|
||||||
req->NameLength = 0;
|
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);
|
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
|
||||||
rsp = (struct smb2_create_rsp *)iov[0].iov_base;
|
rsp = (struct smb2_create_rsp *)iov[0].iov_base;
|
||||||
|
|
||||||
@ -955,8 +1073,12 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
|
|||||||
buf->DeletePending = 0;
|
buf->DeletePending = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*oplock = rsp->OplockLevel;
|
if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
|
||||||
|
*oplock = parse_lease_state(rsp);
|
||||||
|
else
|
||||||
|
*oplock = rsp->OplockLevel;
|
||||||
creat_exit:
|
creat_exit:
|
||||||
|
kfree(copy_path);
|
||||||
free_rsp_buf(resp_buftype, rsp);
|
free_rsp_buf(resp_buftype, rsp);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -150,6 +150,10 @@ struct smb2_err_rsp {
|
|||||||
__u8 ErrorData[1]; /* variable length */
|
__u8 ErrorData[1]; /* variable length */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define SMB2_CLIENT_GUID_SIZE 16
|
||||||
|
|
||||||
|
extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
|
||||||
|
|
||||||
struct smb2_negotiate_req {
|
struct smb2_negotiate_req {
|
||||||
struct smb2_hdr hdr;
|
struct smb2_hdr hdr;
|
||||||
__le16 StructureSize; /* Must be 36 */
|
__le16 StructureSize; /* Must be 36 */
|
||||||
@ -157,7 +161,7 @@ struct smb2_negotiate_req {
|
|||||||
__le16 SecurityMode;
|
__le16 SecurityMode;
|
||||||
__le16 Reserved; /* MBZ */
|
__le16 Reserved; /* MBZ */
|
||||||
__le32 Capabilities;
|
__le32 Capabilities;
|
||||||
__u8 ClientGUID[16]; /* MBZ */
|
__u8 ClientGUID[SMB2_CLIENT_GUID_SIZE];
|
||||||
__le64 ClientStartTime; /* MBZ */
|
__le64 ClientStartTime; /* MBZ */
|
||||||
__le16 Dialects[2]; /* variable length */
|
__le16 Dialects[2]; /* variable length */
|
||||||
} __packed;
|
} __packed;
|
||||||
@ -307,6 +311,8 @@ struct smb2_tree_disconnect_rsp {
|
|||||||
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
|
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
|
||||||
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
|
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
|
||||||
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
|
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
|
||||||
|
/* Non-spec internal type */
|
||||||
|
#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99
|
||||||
|
|
||||||
/* Desired Access Flags */
|
/* Desired Access Flags */
|
||||||
#define FILE_READ_DATA_LE cpu_to_le32(0x00000001)
|
#define FILE_READ_DATA_LE cpu_to_le32(0x00000001)
|
||||||
@ -404,7 +410,7 @@ struct smb2_create_req {
|
|||||||
__le16 NameLength;
|
__le16 NameLength;
|
||||||
__le32 CreateContextsOffset;
|
__le32 CreateContextsOffset;
|
||||||
__le32 CreateContextsLength;
|
__le32 CreateContextsLength;
|
||||||
__u8 Buffer[1];
|
__u8 Buffer[8];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
struct smb2_create_rsp {
|
struct smb2_create_rsp {
|
||||||
@ -428,6 +434,39 @@ struct smb2_create_rsp {
|
|||||||
__u8 Buffer[1];
|
__u8 Buffer[1];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct create_context {
|
||||||
|
__le32 Next;
|
||||||
|
__le16 NameOffset;
|
||||||
|
__le16 NameLength;
|
||||||
|
__le16 Reserved;
|
||||||
|
__le16 DataOffset;
|
||||||
|
__le32 DataLength;
|
||||||
|
__u8 Buffer[0];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define SMB2_LEASE_NONE __constant_cpu_to_le32(0x00)
|
||||||
|
#define SMB2_LEASE_READ_CACHING __constant_cpu_to_le32(0x01)
|
||||||
|
#define SMB2_LEASE_HANDLE_CACHING __constant_cpu_to_le32(0x02)
|
||||||
|
#define SMB2_LEASE_WRITE_CACHING __constant_cpu_to_le32(0x04)
|
||||||
|
|
||||||
|
#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS __constant_cpu_to_le32(0x02)
|
||||||
|
|
||||||
|
#define SMB2_LEASE_KEY_SIZE 16
|
||||||
|
|
||||||
|
struct lease_context {
|
||||||
|
__le64 LeaseKeyLow;
|
||||||
|
__le64 LeaseKeyHigh;
|
||||||
|
__le32 LeaseState;
|
||||||
|
__le32 LeaseFlags;
|
||||||
|
__le64 LeaseDuration;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct create_lease {
|
||||||
|
struct create_context ccontext;
|
||||||
|
__u8 Name[8];
|
||||||
|
struct lease_context lcontext;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* Currently defined values for close flags */
|
/* Currently defined values for close flags */
|
||||||
#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001)
|
#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001)
|
||||||
struct smb2_close_req {
|
struct smb2_close_req {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user