Commit 23956900 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'v6.9-rc-smb3-server-fixes' of git://git.samba.org/ksmbd

Pull smb server updates from Steve French:

 - add support for durable file handles (an important data integrity
   feature)

 - fixes for potential out of bounds issues

 - fix possible null dereference in close

 - getattr fixes

 - trivial typo fix and minor cleanup

* tag 'v6.9-rc-smb3-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: remove module version
  ksmbd: fix potencial out-of-bounds when buffer offset is invalid
  ksmbd: fix slab-out-of-bounds in smb_strndup_from_utf16()
  ksmbd: Fix spelling mistake "connction" -> "connection"
  ksmbd: fix possible null-deref in smb_lazy_parent_lease_break_close
  ksmbd: add support for durable handles v1/v2
  ksmbd: mark SMB2_SESSION_EXPIRED to session when destroying previous session
  ksmbd: retrieve number of blocks using vfs_getattr in set_file_allocation_info
  ksmbd: replace generic_fillattr with vfs_getattr
parents 42c2a756 def30e72
...@@ -12,8 +12,6 @@ ...@@ -12,8 +12,6 @@
#include "unicode.h" #include "unicode.h"
#include "vfs_cache.h" #include "vfs_cache.h"
#define KSMBD_VERSION "3.4.2"
extern int ksmbd_debug_types; extern int ksmbd_debug_types;
#define KSMBD_DEBUG_SMB BIT(0) #define KSMBD_DEBUG_SMB BIT(0)
......
...@@ -75,6 +75,7 @@ struct ksmbd_heartbeat { ...@@ -75,6 +75,7 @@ struct ksmbd_heartbeat {
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1)
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) #define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2)
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3) #define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3)
#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4)
/* /*
* IPC request for ksmbd server startup * IPC request for ksmbd server startup
......
...@@ -156,7 +156,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) ...@@ -156,7 +156,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
kfree(sess); kfree(sess);
} }
static struct ksmbd_session *__session_lookup(unsigned long long id) struct ksmbd_session *__session_lookup(unsigned long long id)
{ {
struct ksmbd_session *sess; struct ksmbd_session *sess;
...@@ -305,6 +305,32 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, ...@@ -305,6 +305,32 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
return sess; return sess;
} }
void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id)
{
struct ksmbd_session *prev_sess;
struct ksmbd_user *prev_user;
down_write(&sessions_table_lock);
down_write(&conn->session_lock);
prev_sess = __session_lookup(id);
if (!prev_sess || prev_sess->state == SMB2_SESSION_EXPIRED)
goto out;
prev_user = prev_sess->user;
if (!prev_user ||
strcmp(user->name, prev_user->name) ||
user->passkey_sz != prev_user->passkey_sz ||
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
goto out;
ksmbd_destroy_file_table(&prev_sess->file_table);
prev_sess->state = SMB2_SESSION_EXPIRED;
out:
up_write(&conn->session_lock);
up_write(&sessions_table_lock);
}
static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, static bool ksmbd_preauth_session_id_match(struct preauth_session *sess,
unsigned long long id) unsigned long long id)
{ {
......
...@@ -88,8 +88,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, ...@@ -88,8 +88,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
int ksmbd_session_register(struct ksmbd_conn *conn, int ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess); struct ksmbd_session *sess);
void ksmbd_sessions_deregister(struct ksmbd_conn *conn); void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
struct ksmbd_session *__session_lookup(unsigned long long id);
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
unsigned long long id); unsigned long long id);
void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id);
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
u64 sess_id); u64 sess_id);
struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
......
...@@ -159,7 +159,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) ...@@ -159,7 +159,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
op_entry); op_entry);
if (opinfo) { if (opinfo) {
if (!atomic_inc_not_zero(&opinfo->refcount)) if (opinfo->conn == NULL ||
!atomic_inc_not_zero(&opinfo->refcount))
opinfo = NULL; opinfo = NULL;
else { else {
atomic_inc(&opinfo->conn->r_count); atomic_inc(&opinfo->conn->r_count);
...@@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, ...@@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
*/ */
read_lock(&ci->m_lock); read_lock(&ci->m_lock);
list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
if (!opinfo->is_lease) if (!opinfo->is_lease || !opinfo->conn)
continue; continue;
read_unlock(&ci->m_lock); read_unlock(&ci->m_lock);
lease = opinfo->o_lease; lease = opinfo->o_lease;
...@@ -641,7 +642,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk) ...@@ -641,7 +642,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
struct smb2_hdr *rsp_hdr; struct smb2_hdr *rsp_hdr;
struct ksmbd_file *fp; struct ksmbd_file *fp;
fp = ksmbd_lookup_durable_fd(br_info->fid); fp = ksmbd_lookup_global_fd(br_info->fid);
if (!fp) if (!fp)
goto out; goto out;
...@@ -1106,7 +1107,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, ...@@ -1106,7 +1107,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
read_lock(&p_ci->m_lock); read_lock(&p_ci->m_lock);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease) if (opinfo->conn == NULL || !opinfo->is_lease)
continue; continue;
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE && if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
...@@ -1142,7 +1143,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) ...@@ -1142,7 +1143,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
opinfo = rcu_dereference(fp->f_opinfo); opinfo = rcu_dereference(fp->f_opinfo);
rcu_read_unlock(); rcu_read_unlock();
if (!opinfo->is_lease || opinfo->o_lease->version != 2) if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2)
return; return;
p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent); p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
...@@ -1151,7 +1152,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) ...@@ -1151,7 +1152,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
read_lock(&p_ci->m_lock); read_lock(&p_ci->m_lock);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) { list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease) if (opinfo->conn == NULL || !opinfo->is_lease)
continue; continue;
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) { if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
...@@ -1361,6 +1362,9 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -1361,6 +1362,9 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
if (brk_op->conn == NULL)
continue;
if (!atomic_inc_not_zero(&brk_op->refcount)) if (!atomic_inc_not_zero(&brk_op->refcount))
continue; continue;
...@@ -1496,11 +1500,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) ...@@ -1496,11 +1500,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
/** /**
* parse_lease_state() - parse lease context containted in file open request * parse_lease_state() - parse lease context containted in file open request
* @open_req: buffer containing smb2 file open(create) request * @open_req: buffer containing smb2 file open(create) request
* @is_dir: whether leasing file is directory
* *
* Return: oplock state, -ENOENT if create lease context not found * Return: oplock state, -ENOENT if create lease context not found
*/ */
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) struct lease_ctx_info *parse_lease_state(void *open_req)
{ {
struct create_context *cc; struct create_context *cc;
struct smb2_create_req *req = (struct smb2_create_req *)open_req; struct smb2_create_req *req = (struct smb2_create_req *)open_req;
...@@ -1518,12 +1521,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir) ...@@ -1518,12 +1521,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
if (is_dir) { lreq->req_state = lc->lcontext.LeaseState;
lreq->req_state = lc->lcontext.LeaseState &
~SMB2_LEASE_WRITE_CACHING_LE;
lreq->is_dir = true;
} else
lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags; lreq->flags = lc->lcontext.LeaseFlags;
lreq->epoch = lc->lcontext.Epoch; lreq->epoch = lc->lcontext.Epoch;
lreq->duration = lc->lcontext.LeaseDuration; lreq->duration = lc->lcontext.LeaseDuration;
...@@ -1646,6 +1644,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) ...@@ -1646,6 +1644,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
buf->Name[3] = 'Q'; buf->Name[3] = 'Q';
buf->Timeout = cpu_to_le32(fp->durable_timeout); buf->Timeout = cpu_to_le32(fp->durable_timeout);
if (fp->is_persistent)
buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
} }
/** /**
...@@ -1813,3 +1813,71 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, ...@@ -1813,3 +1813,71 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
read_unlock(&lease_list_lock); read_unlock(&lease_list_lock);
return ret_op; return ret_op;
} }
int smb2_check_durable_oplock(struct ksmbd_conn *conn,
struct ksmbd_share_config *share,
struct ksmbd_file *fp,
struct lease_ctx_info *lctx,
char *name)
{
struct oplock_info *opinfo = opinfo_get(fp);
int ret = 0;
if (!opinfo)
return 0;
if (opinfo->is_lease == false) {
if (lctx) {
pr_err("create context include lease\n");
ret = -EBADF;
goto out;
}
if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) {
pr_err("oplock level is not equal to SMB2_OPLOCK_LEVEL_BATCH\n");
ret = -EBADF;
}
goto out;
}
if (memcmp(conn->ClientGUID, fp->client_guid,
SMB2_CLIENT_GUID_SIZE)) {
ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connection\n");
ret = -EBADF;
goto out;
}
if (!lctx) {
ksmbd_debug(SMB, "create context does not include lease\n");
ret = -EBADF;
goto out;
}
if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key,
SMB2_LEASE_KEY_SIZE)) {
ksmbd_debug(SMB,
"lease key of fp does not match lease key in create context\n");
ret = -EBADF;
goto out;
}
if (!(opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)) {
ksmbd_debug(SMB, "lease state does not contain SMB2_LEASE_HANDLE_CACHING\n");
ret = -EBADF;
goto out;
}
if (opinfo->o_lease->version != lctx->version) {
ksmbd_debug(SMB,
"lease version of fp does not match the one in create context\n");
ret = -EBADF;
goto out;
}
if (!ksmbd_inode_pending_delete(fp))
ret = ksmbd_validate_name_reconnect(share, fp, name);
out:
opinfo_put(opinfo);
return ret;
}
...@@ -111,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo); ...@@ -111,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo);
/* Lease related functions */ /* Lease related functions */
void create_lease_buf(u8 *rbuf, struct lease *lease); void create_lease_buf(u8 *rbuf, struct lease *lease);
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir); struct lease_ctx_info *parse_lease_state(void *open_req);
__u8 smb2_map_lease_to_oplock(__le32 lease_state); __u8 smb2_map_lease_to_oplock(__le32 lease_state);
int lease_read_to_write(struct oplock_info *opinfo); int lease_read_to_write(struct oplock_info *opinfo);
...@@ -130,4 +130,9 @@ void destroy_lease_table(struct ksmbd_conn *conn); ...@@ -130,4 +130,9 @@ void destroy_lease_table(struct ksmbd_conn *conn);
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
struct lease_ctx_info *lctx); struct lease_ctx_info *lctx);
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp); void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
int smb2_check_durable_oplock(struct ksmbd_conn *conn,
struct ksmbd_share_config *share,
struct ksmbd_file *fp,
struct lease_ctx_info *lctx,
char *name);
#endif /* __KSMBD_OPLOCK_H */ #endif /* __KSMBD_OPLOCK_H */
...@@ -625,7 +625,6 @@ static void __exit ksmbd_server_exit(void) ...@@ -625,7 +625,6 @@ static void __exit ksmbd_server_exit(void)
} }
MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>"); MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>");
MODULE_VERSION(KSMBD_VERSION);
MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_SOFTDEP("pre: ecb"); MODULE_SOFTDEP("pre: ecb");
......
...@@ -101,13 +101,17 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -101,13 +101,17 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
*len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength);
break; break;
case SMB2_TREE_CONNECT: case SMB2_TREE_CONNECT:
*off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); *off = max_t(unsigned short int,
le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset),
offsetof(struct smb2_tree_connect_req, Buffer));
*len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength);
break; break;
case SMB2_CREATE: case SMB2_CREATE:
{ {
unsigned short int name_off = unsigned short int name_off =
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); max_t(unsigned short int,
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset),
offsetof(struct smb2_create_req, Buffer));
unsigned short int name_len = unsigned short int name_len =
le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
...@@ -128,11 +132,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -128,11 +132,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break; break;
} }
case SMB2_QUERY_INFO: case SMB2_QUERY_INFO:
*off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); *off = max_t(unsigned int,
le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset),
offsetof(struct smb2_query_info_req, Buffer));
*len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength);
break; break;
case SMB2_SET_INFO: case SMB2_SET_INFO:
*off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); *off = max_t(unsigned int,
le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset),
offsetof(struct smb2_set_info_req, Buffer));
*len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength);
break; break;
case SMB2_READ: case SMB2_READ:
...@@ -142,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -142,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
case SMB2_WRITE: case SMB2_WRITE:
if (((struct smb2_write_req *)hdr)->DataOffset || if (((struct smb2_write_req *)hdr)->DataOffset ||
((struct smb2_write_req *)hdr)->Length) { ((struct smb2_write_req *)hdr)->Length) {
*off = max_t(unsigned int, *off = max_t(unsigned short int,
le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset),
offsetof(struct smb2_write_req, Buffer)); offsetof(struct smb2_write_req, Buffer));
*len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length);
...@@ -153,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -153,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
*len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
break; break;
case SMB2_QUERY_DIRECTORY: case SMB2_QUERY_DIRECTORY:
*off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); *off = max_t(unsigned short int,
le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset),
offsetof(struct smb2_query_directory_req, Buffer));
*len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength);
break; break;
case SMB2_LOCK: case SMB2_LOCK:
...@@ -168,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -168,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break; break;
} }
case SMB2_IOCTL: case SMB2_IOCTL:
*off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); *off = max_t(unsigned int,
le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset),
offsetof(struct smb2_ioctl_req, Buffer));
*len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount);
break; break;
default: default:
......
...@@ -256,6 +256,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn) ...@@ -256,6 +256,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
} }
/** /**
...@@ -283,6 +286,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn) ...@@ -283,6 +286,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
INIT_LIST_HEAD(&conn->preauth_sess_table); INIT_LIST_HEAD(&conn->preauth_sess_table);
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -72,6 +72,18 @@ struct create_durable_req_v2 { ...@@ -72,6 +72,18 @@ struct create_durable_req_v2 {
__u8 CreateGuid[16]; __u8 CreateGuid[16];
} __packed; } __packed;
struct create_durable_reconn_req {
struct create_context ccontext;
__u8 Name[8];
union {
__u8 Reserved[16];
struct {
__u64 PersistentFileId;
__u64 VolatileFileId;
} Fid;
} Data;
} __packed;
struct create_durable_reconn_v2_req { struct create_durable_reconn_v2_req {
struct create_context ccontext; struct create_context ccontext;
__u8 Name[8]; __u8 Name[8];
...@@ -98,6 +110,9 @@ struct create_durable_rsp { ...@@ -98,6 +110,9 @@ struct create_durable_rsp {
} Data; } Data;
} __packed; } __packed;
/* See MS-SMB2 2.2.13.2.11 */
/* Flags */
#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
struct create_durable_v2_rsp { struct create_durable_v2_rsp {
struct create_context ccontext; struct create_context ccontext;
__u8 Name[8]; __u8 Name[8];
......
...@@ -457,10 +457,13 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, ...@@ -457,10 +457,13 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
} }
ksmbd_kstat.kstat = &kstat; ksmbd_kstat.kstat = &kstat;
ksmbd_vfs_fill_dentry_attrs(work, rc = ksmbd_vfs_fill_dentry_attrs(work,
idmap, idmap,
dentry, dentry,
&ksmbd_kstat); &ksmbd_kstat);
if (rc)
break;
rc = fn(conn, info_level, d_info, &ksmbd_kstat); rc = fn(conn, info_level, d_info, &ksmbd_kstat);
if (rc) if (rc)
break; break;
......
...@@ -1682,11 +1682,19 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, ...@@ -1682,11 +1682,19 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
struct dentry *dentry, struct dentry *dentry,
struct ksmbd_kstat *ksmbd_kstat) struct ksmbd_kstat *ksmbd_kstat)
{ {
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
u64 time; u64 time;
int rc; int rc;
struct path path = {
.mnt = share_conf->vfs_path.mnt,
.dentry = dentry,
};
generic_fillattr(idmap, STATX_BASIC_STATS, d_inode(dentry), rc = vfs_getattr(&path, ksmbd_kstat->kstat,
ksmbd_kstat->kstat); STATX_BASIC_STATS | STATX_BTIME,
AT_STATX_SYNC_AS_STAT);
if (rc)
return rc;
time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime);
ksmbd_kstat->create_time = time; ksmbd_kstat->create_time = time;
......
...@@ -305,7 +305,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) ...@@ -305,7 +305,8 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
fd_limit_close(); fd_limit_close();
__ksmbd_remove_durable_fd(fp); __ksmbd_remove_durable_fd(fp);
__ksmbd_remove_fd(ft, fp); if (ft)
__ksmbd_remove_fd(ft, fp);
close_id_del_oplock(fp); close_id_del_oplock(fp);
filp = fp->filp; filp = fp->filp;
...@@ -465,11 +466,32 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, ...@@ -465,11 +466,32 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
return fp; return fp;
} }
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id)
{ {
return __ksmbd_lookup_fd(&global_ft, id); return __ksmbd_lookup_fd(&global_ft, id);
} }
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id)
{
struct ksmbd_file *fp;
fp = __ksmbd_lookup_fd(&global_ft, id);
if (fp && fp->conn) {
ksmbd_put_durable_fd(fp);
fp = NULL;
}
return fp;
}
void ksmbd_put_durable_fd(struct ksmbd_file *fp)
{
if (!atomic_dec_and_test(&fp->refcount))
return;
__ksmbd_close_fd(NULL, fp);
}
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid)
{ {
struct ksmbd_file *fp = NULL; struct ksmbd_file *fp = NULL;
...@@ -639,6 +661,32 @@ __close_file_table_ids(struct ksmbd_file_table *ft, ...@@ -639,6 +661,32 @@ __close_file_table_ids(struct ksmbd_file_table *ft,
return num; return num;
} }
static inline bool is_reconnectable(struct ksmbd_file *fp)
{
struct oplock_info *opinfo = opinfo_get(fp);
bool reconn = false;
if (!opinfo)
return false;
if (opinfo->op_state != OPLOCK_STATE_NONE) {
opinfo_put(opinfo);
return false;
}
if (fp->is_resilient || fp->is_persistent)
reconn = true;
else if (fp->is_durable && opinfo->is_lease &&
opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
reconn = true;
else if (fp->is_durable && opinfo->level == SMB2_OPLOCK_LEVEL_BATCH)
reconn = true;
opinfo_put(opinfo);
return reconn;
}
static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
struct ksmbd_file *fp) struct ksmbd_file *fp)
{ {
...@@ -648,7 +696,28 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, ...@@ -648,7 +696,28 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
static bool session_fd_check(struct ksmbd_tree_connect *tcon, static bool session_fd_check(struct ksmbd_tree_connect *tcon,
struct ksmbd_file *fp) struct ksmbd_file *fp)
{ {
return false; struct ksmbd_inode *ci;
struct oplock_info *op;
struct ksmbd_conn *conn;
if (!is_reconnectable(fp))
return false;
conn = fp->conn;
ci = fp->f_ci;
write_lock(&ci->m_lock);
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
if (op->conn != conn)
continue;
op->conn = NULL;
}
write_unlock(&ci->m_lock);
fp->conn = NULL;
fp->tcon = NULL;
fp->volatile_id = KSMBD_NO_FID;
return true;
} }
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
...@@ -687,6 +756,68 @@ void ksmbd_free_global_file_table(void) ...@@ -687,6 +756,68 @@ void ksmbd_free_global_file_table(void)
ksmbd_destroy_file_table(&global_ft); ksmbd_destroy_file_table(&global_ft);
} }
int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
struct ksmbd_file *fp, char *name)
{
char *pathname, *ab_pathname;
int ret = 0;
pathname = kmalloc(PATH_MAX, GFP_KERNEL);
if (!pathname)
return -EACCES;
ab_pathname = d_path(&fp->filp->f_path, pathname, PATH_MAX);
if (IS_ERR(ab_pathname)) {
kfree(pathname);
return -EACCES;
}
if (name && strcmp(&ab_pathname[share->path_sz + 1], name)) {
ksmbd_debug(SMB, "invalid name reconnect %s\n", name);
ret = -EINVAL;
}
kfree(pathname);
return ret;
}
int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
{
struct ksmbd_inode *ci;
struct oplock_info *op;
if (!fp->is_durable || fp->conn || fp->tcon) {
pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon);
return -EBADF;
}
if (has_file_id(fp->volatile_id)) {
pr_err("Still in use durable fd: %llu\n", fp->volatile_id);
return -EBADF;
}
fp->conn = work->conn;
fp->tcon = work->tcon;
ci = fp->f_ci;
write_lock(&ci->m_lock);
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
if (op->conn)
continue;
op->conn = fp->conn;
}
write_unlock(&ci->m_lock);
__open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
if (!has_file_id(fp->volatile_id)) {
fp->conn = NULL;
fp->tcon = NULL;
return -EBADF;
}
return 0;
}
int ksmbd_init_file_table(struct ksmbd_file_table *ft) int ksmbd_init_file_table(struct ksmbd_file_table *ft)
{ {
ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL);
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include "vfs.h" #include "vfs.h"
#include "mgmt/share_config.h"
/* Windows style file permissions for extended response */ /* Windows style file permissions for extended response */
#define FILE_GENERIC_ALL 0x1F01FF #define FILE_GENERIC_ALL 0x1F01FF
...@@ -106,6 +107,9 @@ struct ksmbd_file { ...@@ -106,6 +107,9 @@ struct ksmbd_file {
int dot_dotdot[2]; int dot_dotdot[2];
unsigned int f_state; unsigned int f_state;
bool reserve_lease_break; bool reserve_lease_break;
bool is_durable;
bool is_persistent;
bool is_resilient;
}; };
static inline void set_ctx_actor(struct dir_context *ctx, static inline void set_ctx_actor(struct dir_context *ctx,
...@@ -141,7 +145,9 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, ...@@ -141,7 +145,9 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d); struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
void ksmbd_inode_put(struct ksmbd_inode *ci); void ksmbd_inode_put(struct ksmbd_inode *ci);
struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id);
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
void ksmbd_put_durable_fd(struct ksmbd_file *fp);
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
...@@ -173,6 +179,9 @@ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); ...@@ -173,6 +179,9 @@ void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp);
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
int file_info); int file_info);
int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp);
int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share,
struct ksmbd_file *fp, char *name);
int ksmbd_init_file_cache(void); int ksmbd_init_file_cache(void);
void ksmbd_exit_file_cache(void); void ksmbd_exit_file_cache(void);
#endif /* __VFS_CACHE_H__ */ #endif /* __VFS_CACHE_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment