diff options
| author | Namjae Jeon <linkinjeon@kernel.org> | 2026-07-17 12:30:00 +0900 |
|---|---|---|
| committer | Namjae Jeon <linkinjeon@kernel.org> | 2026-08-17 15:00:49 +0900 |
| commit | d6bf101da7dd5d2c2fd6e21de67d4ac43900110e (patch) | |
| tree | aaa33004c6e214e4bff188be77110c9cb848dd4e | |
| parent | bc2f3f3dd69424f76e2ed3dc53d29bfd6c9966d4 (diff) | |
| download | linux-d6bf101da7dd5d2c2fd6e21de67d4ac43900110e.tar.gz linux-d6bf101da7dd5d2c2fd6e21de67d4ac43900110e.zip | |
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>
| -rw-r--r-- | fs/smb/server/oplock.c | 6 | ||||
| -rw-r--r-- | fs/smb/server/smb2pdu.c | 38 | ||||
| -rw-r--r-- | fs/smb/server/vfs_cache.c | 2 |
3 files changed, 36 insertions, 10 deletions
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)) + 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 + } 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; |
