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 @@
#include "unicode.h"
#include "vfs_cache.h"
#define KSMBD_VERSION "3.4.2"
extern int ksmbd_debug_types;
#define KSMBD_DEBUG_SMB BIT(0)
......
......@@ -75,6 +75,7 @@ struct ksmbd_heartbeat {
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1)
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2)
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3)
#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4)
/*
* IPC request for ksmbd server startup
......
......@@ -156,7 +156,7 @@ void ksmbd_session_destroy(struct ksmbd_session *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;
......@@ -305,6 +305,32 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
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,
unsigned long long id)
{
......
......@@ -88,8 +88,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
int ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess);
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,
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,
u64 sess_id);
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)
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
op_entry);
if (opinfo) {
if (!atomic_inc_not_zero(&opinfo->refcount))
if (opinfo->conn == NULL ||
!atomic_inc_not_zero(&opinfo->refcount))
opinfo = NULL;
else {
atomic_inc(&opinfo->conn->r_count);
......@@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
*/
read_lock(&ci->m_lock);
list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
if (!opinfo->is_lease || !opinfo->conn)
continue;
read_unlock(&ci->m_lock);
lease = opinfo->o_lease;
......@@ -641,7 +642,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
struct smb2_hdr *rsp_hdr;
struct ksmbd_file *fp;
fp = ksmbd_lookup_durable_fd(br_info->fid);
fp = ksmbd_lookup_global_fd(br_info->fid);
if (!fp)
goto out;
......@@ -1106,7 +1107,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
read_lock(&p_ci->m_lock);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
if (opinfo->conn == NULL || !opinfo->is_lease)
continue;
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
......@@ -1142,7 +1143,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
opinfo = rcu_dereference(fp->f_opinfo);
rcu_read_unlock();
if (!opinfo->is_lease || opinfo->o_lease->version != 2)
if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2)
return;
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)
read_lock(&p_ci->m_lock);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
if (opinfo->conn == NULL || !opinfo->is_lease)
continue;
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,
rcu_read_lock();
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))
continue;
......@@ -1496,11 +1500,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
/**
* parse_lease_state() - parse lease context containted in file open 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
*/
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 smb2_create_req *req = (struct smb2_create_req *)open_req;
......@@ -1518,11 +1521,6 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
if (is_dir) {
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->epoch = lc->lcontext.Epoch;
......@@ -1646,6 +1644,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
buf->Name[3] = 'Q';
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,
read_unlock(&lease_list_lock);
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);
/* Lease related functions */
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);
int lease_read_to_write(struct oplock_info *opinfo);
......@@ -130,4 +130,9 @@ void destroy_lease_table(struct ksmbd_conn *conn);
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
struct lease_ctx_info *lctx);
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 */
......@@ -625,7 +625,6 @@ static void __exit ksmbd_server_exit(void)
}
MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>");
MODULE_VERSION(KSMBD_VERSION);
MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
MODULE_LICENSE("GPL");
MODULE_SOFTDEP("pre: ecb");
......
......@@ -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);
break;
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);
break;
case SMB2_CREATE:
{
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 =
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,
break;
}
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);
break;
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);
break;
case SMB2_READ:
......@@ -142,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
case SMB2_WRITE:
if (((struct smb2_write_req *)hdr)->DataOffset ||
((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),
offsetof(struct smb2_write_req, Buffer));
*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,
*len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
break;
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);
break;
case SMB2_LOCK:
......@@ -168,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break;
}
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);
break;
default:
......
......@@ -256,6 +256,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
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)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
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);
return 0;
}
......
......@@ -607,30 +607,6 @@ int smb2_check_user_session(struct ksmbd_work *work)
return -ENOENT;
}
static void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id)
{
struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id);
struct ksmbd_user *prev_user;
struct channel *chann;
long index;
if (!prev_sess)
return;
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))
return;
prev_sess->state = SMB2_SESSION_EXPIRED;
xa_for_each(&prev_sess->ksmbd_chann_list, index, chann)
ksmbd_conn_set_exiting(chann->conn);
}
/**
* smb2_get_name() - get filename string from on the wire smb format
* @src: source buffer
......@@ -1951,7 +1927,7 @@ int smb2_tree_connect(struct ksmbd_work *work)
WORK_BUFFERS(work, req, rsp);
treename = smb_strndup_from_utf16(req->Buffer,
treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset),
le16_to_cpu(req->PathLength), true,
conn->local_nls);
if (IS_ERR(treename)) {
......@@ -2642,6 +2618,165 @@ static void ksmbd_acls_fattr(struct smb_fattr *fattr,
}
}
enum {
DURABLE_RECONN_V2 = 1,
DURABLE_RECONN,
DURABLE_REQ_V2,
DURABLE_REQ,
};
struct durable_info {
struct ksmbd_file *fp;
unsigned short int type;
bool persistent;
bool reconnected;
unsigned int timeout;
char *CreateGuid;
};
static int parse_durable_handle_context(struct ksmbd_work *work,
struct smb2_create_req *req,
struct lease_ctx_info *lc,
struct durable_info *dh_info)
{
struct ksmbd_conn *conn = work->conn;
struct create_context *context;
int dh_idx, err = 0;
u64 persistent_id = 0;
int req_op_level;
static const char * const durable_arr[] = {"DH2C", "DHnC", "DH2Q", "DHnQ"};
req_op_level = req->RequestedOplockLevel;
for (dh_idx = DURABLE_RECONN_V2; dh_idx <= ARRAY_SIZE(durable_arr);
dh_idx++) {
context = smb2_find_context_vals(req, durable_arr[dh_idx - 1], 4);
if (IS_ERR(context)) {
err = PTR_ERR(context);
goto out;
}
if (!context)
continue;
switch (dh_idx) {
case DURABLE_RECONN_V2:
{
struct create_durable_reconn_v2_req *recon_v2;
if (dh_info->type == DURABLE_RECONN ||
dh_info->type == DURABLE_REQ_V2) {
err = -EINVAL;
goto out;
}
recon_v2 = (struct create_durable_reconn_v2_req *)context;
persistent_id = recon_v2->Fid.PersistentFileId;
dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
if (!dh_info->fp) {
ksmbd_debug(SMB, "Failed to get durable handle state\n");
err = -EBADF;
goto out;
}
if (memcmp(dh_info->fp->create_guid, recon_v2->CreateGuid,
SMB2_CREATE_GUID_SIZE)) {
err = -EBADF;
ksmbd_put_durable_fd(dh_info->fp);
goto out;
}
dh_info->type = dh_idx;
dh_info->reconnected = true;
ksmbd_debug(SMB,
"reconnect v2 Persistent-id from reconnect = %llu\n",
persistent_id);
break;
}
case DURABLE_RECONN:
{
struct create_durable_reconn_req *recon;
if (dh_info->type == DURABLE_RECONN_V2 ||
dh_info->type == DURABLE_REQ_V2) {
err = -EINVAL;
goto out;
}
recon = (struct create_durable_reconn_req *)context;
persistent_id = recon->Data.Fid.PersistentFileId;
dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
if (!dh_info->fp) {
ksmbd_debug(SMB, "Failed to get durable handle state\n");
err = -EBADF;
goto out;
}
dh_info->type = dh_idx;
dh_info->reconnected = true;
ksmbd_debug(SMB, "reconnect Persistent-id from reconnect = %llu\n",
persistent_id);
break;
}
case DURABLE_REQ_V2:
{
struct create_durable_req_v2 *durable_v2_blob;
if (dh_info->type == DURABLE_RECONN ||
dh_info->type == DURABLE_RECONN_V2) {
err = -EINVAL;
goto out;
}
durable_v2_blob =
(struct create_durable_req_v2 *)context;
ksmbd_debug(SMB, "Request for durable v2 open\n");
dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid);
if (dh_info->fp) {
if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid,
SMB2_CLIENT_GUID_SIZE)) {
if (!(req->hdr.Flags & SMB2_FLAGS_REPLAY_OPERATION)) {
err = -ENOEXEC;
goto out;
}
dh_info->fp->conn = conn;
dh_info->reconnected = true;
goto out;
}
}
if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) {
dh_info->CreateGuid =
durable_v2_blob->CreateGuid;
dh_info->persistent =
le32_to_cpu(durable_v2_blob->Flags);
dh_info->timeout =
le32_to_cpu(durable_v2_blob->Timeout);
dh_info->type = dh_idx;
}
break;
}
case DURABLE_REQ:
if (dh_info->type == DURABLE_RECONN)
goto out;
if (dh_info->type == DURABLE_RECONN_V2 ||
dh_info->type == DURABLE_REQ_V2) {
err = -EINVAL;
goto out;
}
if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) {
ksmbd_debug(SMB, "Request for durable open\n");
dh_info->type = dh_idx;
}
}
}
out:
return err;
}
/**
* smb2_open() - handler for smb file open request
* @work: smb work containing request buffer
......@@ -2665,6 +2800,7 @@ int smb2_open(struct ksmbd_work *work)
struct lease_ctx_info *lc = NULL;
struct create_ea_buf_req *ea_buf = NULL;
struct oplock_info *opinfo;
struct durable_info dh_info = {0};
__le32 *next_ptr = NULL;
int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
int rc = 0;
......@@ -2704,7 +2840,7 @@ int smb2_open(struct ksmbd_work *work)
goto err_out2;
}
name = smb2_get_name(req->Buffer,
name = smb2_get_name((char *)req + le16_to_cpu(req->NameOffset),
le16_to_cpu(req->NameLength),
work->conn->local_nls);
if (IS_ERR(name)) {
......@@ -2745,6 +2881,49 @@ int smb2_open(struct ksmbd_work *work)
}
}
req_op_level = req->RequestedOplockLevel;
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE &&
req->CreateContextsOffset) {
lc = parse_lease_state(req);
rc = parse_durable_handle_context(work, req, lc, &dh_info);
if (rc) {
ksmbd_debug(SMB, "error parsing durable handle context\n");
goto err_out2;
}
if (dh_info.reconnected == true) {
rc = smb2_check_durable_oplock(conn, share, dh_info.fp, lc, name);
if (rc) {
ksmbd_put_durable_fd(dh_info.fp);
goto err_out2;
}
rc = ksmbd_reopen_durable_fd(work, dh_info.fp);
if (rc) {
ksmbd_put_durable_fd(dh_info.fp);
goto err_out2;
}
if (ksmbd_override_fsids(work)) {
rc = -ENOMEM;
ksmbd_put_durable_fd(dh_info.fp);
goto err_out2;
}
fp = dh_info.fp;
file_info = FILE_OPENED;
rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
if (rc)
goto err_out2;
ksmbd_put_durable_fd(fp);
goto reconnected_fp;
}
} else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
lc = parse_lease_state(req);
if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) {
pr_err("Invalid impersonationlevel : 0x%x\n",
le32_to_cpu(req->ImpersonationLevel));
......@@ -3207,10 +3386,6 @@ int smb2_open(struct ksmbd_work *work)
need_truncate = 1;
}
req_op_level = req->RequestedOplockLevel;
if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE)
lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode));
share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp);
if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) ||
(req_op_level == SMB2_OPLOCK_LEVEL_LEASE &&
......@@ -3221,6 +3396,11 @@ int smb2_open(struct ksmbd_work *work)
}
} else {
if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) {
if (S_ISDIR(file_inode(filp)->i_mode)) {
lc->req_state &= ~SMB2_LEASE_WRITE_CACHING_LE;
lc->is_dir = true;
}
/*
* Compare parent lease using parent key. If there is no
* a lease that has same parent key, Send lease break
......@@ -3317,6 +3497,24 @@ int smb2_open(struct ksmbd_work *work)
memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) {
if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent)
fp->is_persistent = true;
else
fp->is_durable = true;
if (dh_info.type == DURABLE_REQ_V2) {
memcpy(fp->create_guid, dh_info.CreateGuid,
SMB2_CREATE_GUID_SIZE);
if (dh_info.timeout)
fp->durable_timeout = min(dh_info.timeout,
300000);
else
fp->durable_timeout = 60;
}
}
reconnected_fp:
rsp->StructureSize = cpu_to_le16(89);
rcu_read_lock();
opinfo = rcu_dereference(fp->f_opinfo);
......@@ -3403,6 +3601,33 @@ int smb2_open(struct ksmbd_work *work)
next_off = conn->vals->create_disk_id_size;
}
if (dh_info.type == DURABLE_REQ || dh_info.type == DURABLE_REQ_V2) {
struct create_context *durable_ccontext;
durable_ccontext = (struct create_context *)(rsp->Buffer +
le32_to_cpu(rsp->CreateContextsLength));
contxt_cnt++;
if (dh_info.type == DURABLE_REQ) {
create_durable_rsp_buf(rsp->Buffer +
le32_to_cpu(rsp->CreateContextsLength));
le32_add_cpu(&rsp->CreateContextsLength,
conn->vals->create_durable_size);
iov_len += conn->vals->create_durable_size;
} else {
create_durable_v2_rsp_buf(rsp->Buffer +
le32_to_cpu(rsp->CreateContextsLength),
fp);
le32_add_cpu(&rsp->CreateContextsLength,
conn->vals->create_durable_v2_size);
iov_len += conn->vals->create_durable_v2_size;
}
if (next_ptr)
*next_ptr = cpu_to_le32(next_off);
next_ptr = &durable_ccontext->Next;
next_off = conn->vals->create_durable_size;
}
if (posix_ctxt) {
contxt_cnt++;
create_posix_rsp_buf(rsp->Buffer +
......@@ -3828,11 +4053,16 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv)
}
ksmbd_kstat.kstat = &kstat;
if (priv->info_level != FILE_NAMES_INFORMATION)
ksmbd_vfs_fill_dentry_attrs(priv->work,
if (priv->info_level != FILE_NAMES_INFORMATION) {
rc = ksmbd_vfs_fill_dentry_attrs(priv->work,
idmap,
dent,
&ksmbd_kstat);
if (rc) {
dput(dent);
continue;
}
}
rc = smb2_populate_readdir_entry(priv->work->conn,
priv->info_level,
......@@ -4075,7 +4305,7 @@ int smb2_query_dir(struct ksmbd_work *work)
}
srch_flag = req->Flags;
srch_ptr = smb_strndup_from_utf16(req->Buffer,
srch_ptr = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->FileNameOffset),
le16_to_cpu(req->FileNameLength), 1,
conn->local_nls);
if (IS_ERR(srch_ptr)) {
......@@ -4335,7 +4565,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
sizeof(struct smb2_ea_info_req))
return -EINVAL;
ea_req = (struct smb2_ea_info_req *)req->Buffer;
ea_req = (struct smb2_ea_info_req *)((char *)req +
le16_to_cpu(req->InputBufferOffset));
} else {
/* need to send all EAs, if no specific EA is requested*/
if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY)
......@@ -4480,6 +4711,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
struct smb2_file_basic_info *basic_info;
struct kstat stat;
u64 time;
int ret;
if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
pr_err("no right to read the attributes : 0x%x\n",
......@@ -4487,9 +4719,12 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
return -EACCES;
}
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
return ret;
basic_info = (struct smb2_file_basic_info *)rsp->Buffer;
generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS,
file_inode(fp->filp), &stat);
basic_info->CreationTime = cpu_to_le64(fp->create_time);
time = ksmbd_UnixTimeToNT(stat.atime);
basic_info->LastAccessTime = cpu_to_le64(time);
......@@ -4504,27 +4739,31 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
return 0;
}
static void get_file_standard_info(struct smb2_query_info_rsp *rsp,
static int get_file_standard_info(struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp, void *rsp_org)
{
struct smb2_file_standard_info *sinfo;
unsigned int delete_pending;
struct inode *inode;
struct kstat stat;
int ret;
inode = file_inode(fp->filp);
generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat);
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
return ret;
sinfo = (struct smb2_file_standard_info *)rsp->Buffer;
delete_pending = ksmbd_inode_pending_delete(fp);
sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9);
sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9);
sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending);
sinfo->DeletePending = delete_pending;
sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0;
rsp->OutputBufferLength =
cpu_to_le32(sizeof(struct smb2_file_standard_info));
return 0;
}
static void get_file_alignment_info(struct smb2_query_info_rsp *rsp,
......@@ -4546,11 +4785,11 @@ static int get_file_all_info(struct ksmbd_work *work,
struct ksmbd_conn *conn = work->conn;
struct smb2_file_all_info *file_info;
unsigned int delete_pending;
struct inode *inode;
struct kstat stat;
int conv_len;
char *filename;
u64 time;
int ret;
if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n",
......@@ -4562,8 +4801,10 @@ static int get_file_all_info(struct ksmbd_work *work,
if (IS_ERR(filename))
return PTR_ERR(filename);
inode = file_inode(fp->filp);
generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat);
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
return ret;
ksmbd_debug(SMB, "filename = %s\n", filename);
delete_pending = ksmbd_inode_pending_delete(fp);
......@@ -4579,7 +4820,7 @@ static int get_file_all_info(struct ksmbd_work *work,
file_info->Attributes = fp->f_ci->m_fattr;
file_info->Pad1 = 0;
file_info->AllocationSize =
cpu_to_le64(inode->i_blocks << 9);
cpu_to_le64(stat.blocks << 9);
file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
file_info->NumberOfLinks =
cpu_to_le32(get_nlink(&stat) - delete_pending);
......@@ -4623,7 +4864,7 @@ static void get_file_alternate_info(struct ksmbd_work *work,
cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len);
}
static void get_file_stream_info(struct ksmbd_work *work,
static int get_file_stream_info(struct ksmbd_work *work,
struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp,
void *rsp_org)
......@@ -4637,9 +4878,13 @@ static void get_file_stream_info(struct ksmbd_work *work,
int nbytes = 0, streamlen, stream_name_len, next, idx = 0;
int buf_free_len;
struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
int ret;
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
return ret;
generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS,
file_inode(fp->filp), &stat);
file_info = (struct smb2_file_stream_info *)rsp->Buffer;
buf_free_len =
......@@ -4720,29 +4965,37 @@ static void get_file_stream_info(struct ksmbd_work *work,
kvfree(xattr_list);
rsp->OutputBufferLength = cpu_to_le32(nbytes);
return 0;
}
static void get_file_internal_info(struct smb2_query_info_rsp *rsp,
static int get_file_internal_info(struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp, void *rsp_org)
{
struct smb2_file_internal_info *file_info;
struct kstat stat;
int ret;
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
return ret;
generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS,
file_inode(fp->filp), &stat);
file_info = (struct smb2_file_internal_info *)rsp->Buffer;
file_info->IndexNumber = cpu_to_le64(stat.ino);
rsp->OutputBufferLength =
cpu_to_le32(sizeof(struct smb2_file_internal_info));
return 0;
}
static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp, void *rsp_org)
{
struct smb2_file_ntwrk_info *file_info;
struct inode *inode;
struct kstat stat;
u64 time;
int ret;
if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
pr_err("no right to read the attributes : 0x%x\n",
......@@ -4750,10 +5003,12 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
return -EACCES;
}
file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer;
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
return ret;
inode = file_inode(fp->filp);
generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat);
file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer;
file_info->CreationTime = cpu_to_le64(fp->create_time);
time = ksmbd_UnixTimeToNT(stat.atime);
......@@ -4763,8 +5018,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
time = ksmbd_UnixTimeToNT(stat.ctime);
file_info->ChangeTime = cpu_to_le64(time);
file_info->Attributes = fp->f_ci->m_fattr;
file_info->AllocationSize =
cpu_to_le64(inode->i_blocks << 9);
file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
file_info->Reserved = cpu_to_le32(0);
rsp->OutputBufferLength =
......@@ -4804,14 +5058,17 @@ static void get_file_mode_info(struct smb2_query_info_rsp *rsp,
cpu_to_le32(sizeof(struct smb2_file_mode_info));
}
static void get_file_compression_info(struct smb2_query_info_rsp *rsp,
static int get_file_compression_info(struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp, void *rsp_org)
{
struct smb2_file_comp_info *file_info;
struct kstat stat;
int ret;
generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS,
file_inode(fp->filp), &stat);
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
return ret;
file_info = (struct smb2_file_comp_info *)rsp->Buffer;
file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9);
......@@ -4823,6 +5080,8 @@ static void get_file_compression_info(struct smb2_query_info_rsp *rsp,
rsp->OutputBufferLength =
cpu_to_le32(sizeof(struct smb2_file_comp_info));
return 0;
}
static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp,
......@@ -4844,7 +5103,7 @@ static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp,
return 0;
}
static void find_file_posix_info(struct smb2_query_info_rsp *rsp,
static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
struct ksmbd_file *fp, void *rsp_org)
{
struct smb311_posix_qinfo *file_info;
......@@ -4852,24 +5111,31 @@ static void find_file_posix_info(struct smb2_query_info_rsp *rsp,
struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode);
vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);
struct kstat stat;
u64 time;
int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32;
int ret;
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret)
return ret;
file_info = (struct smb311_posix_qinfo *)rsp->Buffer;
file_info->CreationTime = cpu_to_le64(fp->create_time);
time = ksmbd_UnixTimeToNT(inode_get_atime(inode));
time = ksmbd_UnixTimeToNT(stat.atime);
file_info->LastAccessTime = cpu_to_le64(time);
time = ksmbd_UnixTimeToNT(inode_get_mtime(inode));
time = ksmbd_UnixTimeToNT(stat.mtime);
file_info->LastWriteTime = cpu_to_le64(time);
time = ksmbd_UnixTimeToNT(inode_get_ctime(inode));
time = ksmbd_UnixTimeToNT(stat.ctime);
file_info->ChangeTime = cpu_to_le64(time);
file_info->DosAttributes = fp->f_ci->m_fattr;
file_info->Inode = cpu_to_le64(inode->i_ino);
file_info->EndOfFile = cpu_to_le64(inode->i_size);
file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9);
file_info->HardLinks = cpu_to_le32(inode->i_nlink);
file_info->Mode = cpu_to_le32(inode->i_mode & 0777);
file_info->DeviceId = cpu_to_le32(inode->i_rdev);
file_info->Inode = cpu_to_le64(stat.ino);
file_info->EndOfFile = cpu_to_le64(stat.size);
file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
file_info->HardLinks = cpu_to_le32(stat.nlink);
file_info->Mode = cpu_to_le32(stat.mode & 0777);
file_info->DeviceId = cpu_to_le32(stat.rdev);
/*
* Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)).
......@@ -4882,6 +5148,8 @@ static void find_file_posix_info(struct smb2_query_info_rsp *rsp,
SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]);
rsp->OutputBufferLength = cpu_to_le32(out_buf_len);
return 0;
}
static int smb2_get_info_file(struct ksmbd_work *work,
......@@ -4930,7 +5198,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
break;
case FILE_STANDARD_INFORMATION:
get_file_standard_info(rsp, fp, work->response_buf);
rc = get_file_standard_info(rsp, fp, work->response_buf);
break;
case FILE_ALIGNMENT_INFORMATION:
......@@ -4946,11 +5214,11 @@ static int smb2_get_info_file(struct ksmbd_work *work,
break;
case FILE_STREAM_INFORMATION:
get_file_stream_info(work, rsp, fp, work->response_buf);
rc = get_file_stream_info(work, rsp, fp, work->response_buf);
break;
case FILE_INTERNAL_INFORMATION:
get_file_internal_info(rsp, fp, work->response_buf);
rc = get_file_internal_info(rsp, fp, work->response_buf);
break;
case FILE_NETWORK_OPEN_INFORMATION:
......@@ -4974,7 +5242,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
break;
case FILE_COMPRESSION_INFORMATION:
get_file_compression_info(rsp, fp, work->response_buf);
rc = get_file_compression_info(rsp, fp, work->response_buf);
break;
case FILE_ATTRIBUTE_TAG_INFORMATION:
......@@ -4985,7 +5253,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,
pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n");
rc = -EOPNOTSUPP;
} else {
find_file_posix_info(rsp, fp, work->response_buf);
rc = find_file_posix_info(rsp, fp, work->response_buf);
}
break;
default:
......@@ -5398,7 +5666,6 @@ int smb2_close(struct ksmbd_work *work)
struct smb2_close_rsp *rsp;
struct ksmbd_conn *conn = work->conn;
struct ksmbd_file *fp;
struct inode *inode;
u64 time;
int err = 0;
......@@ -5453,24 +5720,33 @@ int smb2_close(struct ksmbd_work *work)
rsp->Reserved = 0;
if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) {
struct kstat stat;
int ret;
fp = ksmbd_lookup_fd_fast(work, volatile_id);
if (!fp) {
err = -ENOENT;
goto out;
}
inode = file_inode(fp->filp);
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (ret) {
ksmbd_fd_put(work, fp);
goto out;
}
rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB;
rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 :
cpu_to_le64(inode->i_blocks << 9);
rsp->EndOfFile = cpu_to_le64(inode->i_size);
rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 :
cpu_to_le64(stat.blocks << 9);
rsp->EndOfFile = cpu_to_le64(stat.size);
rsp->Attributes = fp->f_ci->m_fattr;
rsp->CreationTime = cpu_to_le64(fp->create_time);
time = ksmbd_UnixTimeToNT(inode_get_atime(inode));
time = ksmbd_UnixTimeToNT(stat.atime);
rsp->LastAccessTime = cpu_to_le64(time);
time = ksmbd_UnixTimeToNT(inode_get_mtime(inode));
time = ksmbd_UnixTimeToNT(stat.mtime);
rsp->LastWriteTime = cpu_to_le64(time);
time = ksmbd_UnixTimeToNT(inode_get_ctime(inode));
time = ksmbd_UnixTimeToNT(stat.ctime);
rsp->ChangeTime = cpu_to_le64(time);
ksmbd_fd_put(work, fp);
} else {
......@@ -5759,15 +6035,21 @@ static int set_file_allocation_info(struct ksmbd_work *work,
loff_t alloc_blks;
struct inode *inode;
struct kstat stat;
int rc;
if (!(fp->daccess & FILE_WRITE_DATA_LE))
return -EACCES;
rc = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
AT_STATX_SYNC_AS_STAT);
if (rc)
return rc;
alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
inode = file_inode(fp->filp);
if (alloc_blks > inode->i_blocks) {
if (alloc_blks > stat.blocks) {
smb_break_all_levII_oplock(work, fp, 1);
rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0,
alloc_blks * 512);
......@@ -5775,7 +6057,7 @@ static int set_file_allocation_info(struct ksmbd_work *work,
pr_err("vfs_fallocate is failed : %d\n", rc);
return rc;
}
} else if (alloc_blks < inode->i_blocks) {
} else if (alloc_blks < stat.blocks) {
loff_t size;
/*
......@@ -5930,6 +6212,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
struct ksmbd_share_config *share)
{
unsigned int buf_len = le32_to_cpu(req->BufferLength);
char *buffer = (char *)req + le16_to_cpu(req->BufferOffset);
switch (req->FileInfoClass) {
case FILE_BASIC_INFORMATION:
......@@ -5937,7 +6220,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
if (buf_len < sizeof(struct smb2_file_basic_info))
return -EINVAL;
return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share);
return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share);
}
case FILE_ALLOCATION_INFORMATION:
{
......@@ -5945,7 +6228,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL;
return set_file_allocation_info(work, fp,
(struct smb2_file_alloc_info *)req->Buffer);
(struct smb2_file_alloc_info *)buffer);
}
case FILE_END_OF_FILE_INFORMATION:
{
......@@ -5953,7 +6236,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL;
return set_end_of_file_info(work, fp,
(struct smb2_file_eof_info *)req->Buffer);
(struct smb2_file_eof_info *)buffer);
}
case FILE_RENAME_INFORMATION:
{
......@@ -5961,7 +6244,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL;
return set_rename_info(work, fp,
(struct smb2_file_rename_info *)req->Buffer,
(struct smb2_file_rename_info *)buffer,
buf_len);
}
case FILE_LINK_INFORMATION:
......@@ -5970,7 +6253,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL;
return smb2_create_link(work, work->tcon->share_conf,
(struct smb2_file_link_info *)req->Buffer,
(struct smb2_file_link_info *)buffer,
buf_len, fp->filp,
work->conn->local_nls);
}
......@@ -5980,7 +6263,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL;
return set_file_disposition_info(fp,
(struct smb2_file_disposition_info *)req->Buffer);
(struct smb2_file_disposition_info *)buffer);
}
case FILE_FULL_EA_INFORMATION:
{
......@@ -5993,7 +6276,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
if (buf_len < sizeof(struct smb2_ea_info))
return -EINVAL;
return smb2_set_ea((struct smb2_ea_info *)req->Buffer,
return smb2_set_ea((struct smb2_ea_info *)buffer,
buf_len, &fp->filp->f_path, true);
}
case FILE_POSITION_INFORMATION:
......@@ -6001,14 +6284,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
if (buf_len < sizeof(struct smb2_file_pos_info))
return -EINVAL;
return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer);
return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer);
}
case FILE_MODE_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_mode_info))
return -EINVAL;
return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer);
return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer);
}
}
......@@ -6089,7 +6372,7 @@ int smb2_set_info(struct ksmbd_work *work)
}
rc = smb2_set_info_sec(fp,
le32_to_cpu(req->AdditionalInformation),
req->Buffer,
(char *)req + le16_to_cpu(req->BufferOffset),
le32_to_cpu(req->BufferLength));
ksmbd_revert_fsids(work);
break;
......@@ -7535,7 +7818,7 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id,
struct smb2_ioctl_rsp *rsp)
{
struct ksmbd_rpc_command *rpc_resp;
char *data_buf = (char *)&req->Buffer[0];
char *data_buf = (char *)req + le32_to_cpu(req->InputOffset);
int nbytes = 0;
rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf,
......@@ -7648,6 +7931,7 @@ int smb2_ioctl(struct ksmbd_work *work)
u64 id = KSMBD_NO_FID;
struct ksmbd_conn *conn = work->conn;
int ret = 0;
char *buffer;
if (work->next_smb2_rcv_hdr_off) {
req = ksmbd_req_buf_next(work);
......@@ -7670,6 +7954,8 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out;
}
buffer = (char *)req + le32_to_cpu(req->InputOffset);
cnt_code = le32_to_cpu(req->CtlCode);
ret = smb2_calc_max_out_buf_len(work, 48,
le32_to_cpu(req->MaxOutputResponse));
......@@ -7727,7 +8013,7 @@ int smb2_ioctl(struct ksmbd_work *work)
}
ret = fsctl_validate_negotiate_info(conn,
(struct validate_negotiate_info_req *)&req->Buffer[0],
(struct validate_negotiate_info_req *)buffer,
(struct validate_negotiate_info_rsp *)&rsp->Buffer[0],
in_buf_len);
if (ret < 0)
......@@ -7780,7 +8066,7 @@ int smb2_ioctl(struct ksmbd_work *work)
rsp->VolatileFileId = req->VolatileFileId;
rsp->PersistentFileId = req->PersistentFileId;
fsctl_copychunk(work,
(struct copychunk_ioctl_req *)&req->Buffer[0],
(struct copychunk_ioctl_req *)buffer,
le32_to_cpu(req->CtlCode),
le32_to_cpu(req->InputCount),
req->VolatileFileId,
......@@ -7793,8 +8079,7 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out;
}
ret = fsctl_set_sparse(work, id,
(struct file_sparse *)&req->Buffer[0]);
ret = fsctl_set_sparse(work, id, (struct file_sparse *)buffer);
if (ret < 0)
goto out;
break;
......@@ -7817,7 +8102,7 @@ int smb2_ioctl(struct ksmbd_work *work)
}
zero_data =
(struct file_zero_data_information *)&req->Buffer[0];
(struct file_zero_data_information *)buffer;
off = le64_to_cpu(zero_data->FileOffset);
bfz = le64_to_cpu(zero_data->BeyondFinalZero);
......@@ -7848,7 +8133,7 @@ int smb2_ioctl(struct ksmbd_work *work)
}
ret = fsctl_query_allocated_ranges(work, id,
(struct file_allocated_range_buffer *)&req->Buffer[0],
(struct file_allocated_range_buffer *)buffer,
(struct file_allocated_range_buffer *)&rsp->Buffer[0],
out_buf_len /
sizeof(struct file_allocated_range_buffer), &nbytes);
......@@ -7892,7 +8177,7 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out;
}
dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0];
dup_ext = (struct duplicate_extents_to_file *)buffer;
fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle,
dup_ext->PersistentFileHandle);
......
......@@ -72,6 +72,18 @@ struct create_durable_req_v2 {
__u8 CreateGuid[16];
} __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_context ccontext;
__u8 Name[8];
......@@ -98,6 +110,9 @@ struct create_durable_rsp {
} Data;
} __packed;
/* See MS-SMB2 2.2.13.2.11 */
/* Flags */
#define SMB2_DHANDLE_FLAG_PERSISTENT 0x00000002
struct create_durable_v2_rsp {
struct create_context ccontext;
__u8 Name[8];
......
......@@ -457,10 +457,13 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
}
ksmbd_kstat.kstat = &kstat;
ksmbd_vfs_fill_dentry_attrs(work,
rc = ksmbd_vfs_fill_dentry_attrs(work,
idmap,
dentry,
&ksmbd_kstat);
if (rc)
break;
rc = fn(conn, info_level, d_info, &ksmbd_kstat);
if (rc)
break;
......
......@@ -1682,11 +1682,19 @@ int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
struct dentry *dentry,
struct ksmbd_kstat *ksmbd_kstat)
{
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
u64 time;
int rc;
struct path path = {
.mnt = share_conf->vfs_path.mnt,
.dentry = dentry,
};
generic_fillattr(idmap, STATX_BASIC_STATS, d_inode(dentry),
ksmbd_kstat->kstat);
rc = vfs_getattr(&path, ksmbd_kstat->kstat,
STATX_BASIC_STATS | STATX_BTIME,
AT_STATX_SYNC_AS_STAT);
if (rc)
return rc;
time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime);
ksmbd_kstat->create_time = time;
......
......@@ -305,6 +305,7 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
fd_limit_close();
__ksmbd_remove_durable_fd(fp);
if (ft)
__ksmbd_remove_fd(ft, fp);
close_id_del_oplock(fp);
......@@ -465,11 +466,32 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
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);
}
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 *fp = NULL;
......@@ -639,6 +661,32 @@ __close_file_table_ids(struct ksmbd_file_table *ft,
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,
struct ksmbd_file *fp)
{
......@@ -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,
struct ksmbd_file *fp)
{
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)
......@@ -687,6 +756,68 @@ void ksmbd_free_global_file_table(void)
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)
{
ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL);
......
......@@ -14,6 +14,7 @@
#include <linux/workqueue.h>
#include "vfs.h"
#include "mgmt/share_config.h"
/* Windows style file permissions for extended response */
#define FILE_GENERIC_ALL 0x1F01FF
......@@ -106,6 +107,9 @@ struct ksmbd_file {
int dot_dotdot[2];
unsigned int f_state;
bool reserve_lease_break;
bool is_durable;
bool is_persistent;
bool is_resilient;
};
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,
void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
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);
void ksmbd_put_durable_fd(struct ksmbd_file *fp);
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
unsigned int ksmbd_open_durable_fd(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_fd_set_delete_on_close(struct ksmbd_file *fp,
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);
void ksmbd_exit_file_cache(void);
#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