diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index a534fe6c26b2..591b2fca1d4e 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -2068,12 +2068,12 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) struct create_durable_rsp_v2 *buf; buf = (struct create_durable_rsp_v2 *)cc; - memset(buf, 0, sizeof(struct create_durable_rsp)); + memset(buf, 0, sizeof(*buf)); buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Data)); + (struct create_durable_rsp_v2, dcontext)); buf->ccontext.DataLength = cpu_to_le32(8); buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_rsp, Name)); + (struct create_durable_rsp_v2, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */ buf->Name[0] = 'D'; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index d1cc29ae95a5..2c1418201708 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3430,6 +3430,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work, case DURABLE_RECONN_V2: { struct create_durable_handle_reconnect_v2 *recon_v2; + u32 flags; if (dh_info->type == DURABLE_RECONN || dh_info->type == DURABLE_REQ_V2) { @@ -3444,6 +3445,12 @@ static int parse_durable_handle_context(struct ksmbd_work *work, } recon_v2 = (struct create_durable_handle_reconnect_v2 *)context; + flags = le32_to_cpu(recon_v2->dcontext.Flags); + if (flags & ~SMB2_DHANDLE_FLAG_PERSISTENT) { + err = -EINVAL; + goto out; + } + dh_info->persistent = flags & SMB2_DHANDLE_FLAG_PERSISTENT; persistent_id = recon_v2->dcontext.Fid.PersistentFileId; dh_info->fp = ksmbd_lookup_durable_fd(persistent_id); if (!dh_info->fp) { @@ -3466,6 +3473,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } + /* A persistent reconnect must match the original open type. */ + if (dh_info->fp->is_persistent != dh_info->persistent) { + err = dh_info->persistent ? -EINVAL : -EBADF; + ksmbd_put_durable_fd(dh_info->fp); + goto out; + } + dh_info->type = dh_idx; dh_info->reconnected = true; ksmbd_debug(SMB, @@ -3529,6 +3543,11 @@ static int parse_durable_handle_context(struct ksmbd_work *work, durable_v2_blob = (struct create_durable_req_v2 *)context; + if (le32_to_cpu(durable_v2_blob->dcontext.Flags) & + ~SMB2_DHANDLE_FLAG_PERSISTENT) { + err = -EINVAL; + goto out; + } ksmbd_debug(SMB, "Request for durable v2 open\n"); dh_info->CreateGuid = durable_v2_blob->dcontext.CreateGuid; dh_info->persistent = @@ -3812,14 +3831,14 @@ int smb2_open(struct ksmbd_work *work) if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) req_op_level = SMB2_OPLOCK_LEVEL_NONE; } + rc = parse_app_instance_id(req, &dh_info); + if (rc) + goto err_out2; rc = parse_durable_handle_context(work, req, lc, &dh_info); if (rc) { ksmbd_debug(SMB, "error parsing durable handle context\n"); goto err_out2; } - rc = parse_app_instance_id(req, &dh_info); - if (rc) - goto err_out2; if (dh_info.replay == true) { fp = dh_info.fp; @@ -4594,10 +4613,15 @@ int smb2_open(struct ksmbd_work *work) if (dh_info.type == DURABLE_REQ_V2 || dh_info.type == DURABLE_REQ) { if (dh_info.type == DURABLE_REQ_V2 && dh_info.persistent && test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY)) - fp->is_persistent = true; - else + KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY) && + (conn->vals->req_capabilities & + SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)) { + /* MS-SMB2 3.3.5.9.10: a persistent open is durable too. */ fp->is_durable = true; + fp->is_persistent = true; + } else { + fp->is_durable = true; + } if (dh_info.type == DURABLE_REQ_V2) { if (dh_info.app_instance_id) memcpy(fp->app_instance_id, @@ -4760,7 +4784,9 @@ int smb2_open(struct ksmbd_work *work) if (next_ptr) *next_ptr = cpu_to_le32(next_off); next_ptr = &durable_ccontext->Next; - next_off = conn->vals->create_durable_size; + next_off = dh_info.type == DURABLE_REQ ? + conn->vals->create_durable_size : + conn->vals->create_durable_v2_size; } if (posix_ctxt) { diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 08b15f528d88..0a8c3c6e1c81 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -1845,7 +1845,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) unsigned int old_f_state; write_lock(&global_ft.lock); - if (!fp->is_durable || fp->conn || fp->tcon) { + if ((!fp->is_durable && !fp->is_persistent) || fp->conn || fp->tcon) { write_unlock(&global_ft.lock); pr_err("Invalid durable fd [%p:%p]\n", fp->conn, fp->tcon); return -EBADF;