ksmbd: fix durable V2 persistent handle handling

Correct the durable-handle V2 response context layout and use the V2
context size when chaining a following CREATE response context. Validate
the only defined DH2Q/DH2C flag, require the reconnect request type to
match the saved open type, and process the application instance identifier
before durable V2 state.

Persistent opens are durable opens as required by MS-SMB2. Permit the
durable reconnect path to rebind either type of disconnected open.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Namjae Jeon 2026-07-17 12:30:00 +09:00
parent bc2f3f3dd6
commit d6bf101da7
3 changed files with 37 additions and 11 deletions

View File

@ -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';

View File

@ -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) {

View File

@ -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;