]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ksmbd: preserve open change time across rename
authorNamjae Jeon <linkinjeon@kernel.org>
Sun, 21 Jun 2026 10:36:40 +0000 (19:36 +0900)
committerSteve French <stfrench@microsoft.com>
Tue, 23 Jun 2026 01:15:05 +0000 (20:15 -0500)
inode ctime is updated when a file is renamed. ksmbd returned that
ctime directly as SMB2 ChangeTime for handle-based query information.
This makes ChangeTime change after a rename through an already-open
handle, while Windows keeps the handle's ChangeTime stable for this
case.

Store the SMB ChangeTime in struct ksmbd_file when the handle is opened
and use that value for create, close, and handle-based query information
responses.  If a client explicitly sets FILE_BASIC_INFORMATION
ChangeTime, update the stored value as well.

This fixes smbtorture smb2.rename.simple_modtime, which expects
ChangeTime and LastWriteTime to remain unchanged after renaming an
already-open file.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/smb2pdu.c
fs/smb/server/vfs_cache.h

index a21760394637be70dec243635e872245f1909373..f2ba52c7e325f0aef9d1cb98f3579492cce18d67 100644 (file)
@@ -3783,6 +3783,7 @@ int smb2_open(struct ksmbd_work *work)
                fp->create_time = ksmbd_UnixTimeToNT(stat.btime);
        else
                fp->create_time = ksmbd_UnixTimeToNT(stat.ctime);
+       fp->change_time = ksmbd_UnixTimeToNT(stat.ctime);
        if (req->FileAttributes || fp->f_ci->m_fattr == 0)
                fp->f_ci->m_fattr =
                        cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes)));
@@ -3832,8 +3833,7 @@ reconnected_fp:
        rsp->LastAccessTime = cpu_to_le64(time);
        time = ksmbd_UnixTimeToNT(stat.mtime);
        rsp->LastWriteTime = cpu_to_le64(time);
-       time = ksmbd_UnixTimeToNT(stat.ctime);
-       rsp->ChangeTime = cpu_to_le64(time);
+       rsp->ChangeTime = cpu_to_le64(fp->change_time);
        rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 :
                cpu_to_le64(stat.blocks << 9);
        rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size);
@@ -5102,8 +5102,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp,
        basic_info->LastAccessTime = cpu_to_le64(time);
        time = ksmbd_UnixTimeToNT(stat.mtime);
        basic_info->LastWriteTime = cpu_to_le64(time);
-       time = ksmbd_UnixTimeToNT(stat.ctime);
-       basic_info->ChangeTime = cpu_to_le64(time);
+       basic_info->ChangeTime = cpu_to_le64(fp->change_time);
        basic_info->Attributes = fp->f_ci->m_fattr;
        basic_info->Pad = 0;
        rsp->OutputBufferLength =
@@ -5205,8 +5204,7 @@ static int get_file_all_info(struct ksmbd_work *work,
        file_info->LastAccessTime = cpu_to_le64(time);
        time = ksmbd_UnixTimeToNT(stat.mtime);
        file_info->LastWriteTime = cpu_to_le64(time);
-       time = ksmbd_UnixTimeToNT(stat.ctime);
-       file_info->ChangeTime = cpu_to_le64(time);
+       file_info->ChangeTime = cpu_to_le64(fp->change_time);
        file_info->Attributes = fp->f_ci->m_fattr;
        file_info->Pad1 = 0;
        if (ksmbd_stream_fd(fp) == false) {
@@ -5415,8 +5413,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp,
        file_info->LastAccessTime = cpu_to_le64(time);
        time = ksmbd_UnixTimeToNT(stat.mtime);
        file_info->LastWriteTime = cpu_to_le64(time);
-       time = ksmbd_UnixTimeToNT(stat.ctime);
-       file_info->ChangeTime = cpu_to_le64(time);
+       file_info->ChangeTime = cpu_to_le64(fp->change_time);
        file_info->Attributes = fp->f_ci->m_fattr;
        if (ksmbd_stream_fd(fp) == false) {
                file_info->AllocationSize = cpu_to_le64(stat.blocks << 9);
@@ -5547,8 +5544,7 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
        file_info->LastAccessTime = cpu_to_le64(time);
        time = ksmbd_UnixTimeToNT(stat.mtime);
        file_info->LastWriteTime = cpu_to_le64(time);
-       time = ksmbd_UnixTimeToNT(stat.ctime);
-       file_info->ChangeTime = cpu_to_le64(time);
+       file_info->ChangeTime = cpu_to_le64(fp->change_time);
        file_info->DosAttributes = fp->f_ci->m_fattr;
        file_info->Inode = cpu_to_le64(stat.ino);
        if (ksmbd_stream_fd(fp) == false) {
@@ -6271,8 +6267,7 @@ int smb2_close(struct ksmbd_work *work)
                rsp->LastAccessTime = cpu_to_le64(time);
                time = ksmbd_UnixTimeToNT(stat.mtime);
                rsp->LastWriteTime = cpu_to_le64(time);
-               time = ksmbd_UnixTimeToNT(stat.ctime);
-               rsp->ChangeTime = cpu_to_le64(time);
+               rsp->ChangeTime = cpu_to_le64(fp->change_time);
                ksmbd_fd_put(work, fp);
        } else {
                rsp->Flags = 0;
@@ -6485,9 +6480,11 @@ static int set_file_basic_info(struct ksmbd_file *fp,
                attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
        }
 
-       if (file_info->ChangeTime)
+       if (file_info->ChangeTime) {
+               fp->change_time = le64_to_cpu(file_info->ChangeTime);
                inode_set_ctime_to_ts(inode,
                                ksmbd_NTtimeToUnix(file_info->ChangeTime));
+       }
 
        if (file_info->LastWriteTime) {
                attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime);
index ca391d597e2e3d03a0bf6ae19086c6a605483094..8c456484cab26906752c2e463039ff40bdd885ef 100644 (file)
@@ -97,6 +97,7 @@ struct ksmbd_file {
        __le32                          coption;
        __le32                          cdoption;
        __u64                           create_time;
+       __u64                           change_time;
        __u64                           itime;
 
        bool                            is_nt_open;