SMB2 CHANGE_NOTIFY feature

中文分析过程请点击这里查看

1 Requirements description

Please see GitHub issue.

2 KSMBD development environment

Install ksmbd-tools from source:

apt install -y git gcc pkgconf autoconf automake libtool make meson ninja-build gawk libnl-3-dev libnl-genl-3-dev libglib2.0-dev # debian
dnf install -y git gcc pkgconf autoconf automake libtool make meson ninja-build gawk libnl3-devel glib2-devel # fedora
git clone https://github.com/cifsd-team/ksmbd-tools.git
cd ksmbd-tools
./autogen.sh
./configure --with-rundir=/run # --prefix=/usr/local/sbin --sysconfdir=/usr/local/etc
make -j`nproc`
make install -j`nproc`

create smb user:

# We are testing in a virtual machine, so we just use the root user.
sudo ksmbd.adduser --add root

Then create config file /usr/local/etc/ksmbd/ksmbd.conf:

[global]
        writeable = yes
        public = yes

[TEST]
        comment = test dir
        ; Note: there should not be a space after the path.
        path = /tmp/s_test

Start ksmbd:

mkdir /tmp/s_test
systemctl stop smbd.service # stop samba on debian
systemctl stop smb.service # stop samba on fedora
chmod 777 /tmp/s_test # just for test
systemctl restart ksmbd

3 Samba development environment

Install samba from source:

git clone https://gitlab.com/samba-team/devel/samba.git 
cd samba/bootstrap/generated-dists/fedora41/ # You can replace fedora41 with your own distribution
./bootstrap.sh # It may take some time to install the dependencies
cd ../../../
./configure --with-systemd --with-libunwind
make -j`nproc`
make install -j`nproc`
export PATH=/usr/local/samba/bin/:/usr/local/samba/sbin/:$PATH

When you only want to build and install smbd:

make -j`nproc` bin/smbd && rm -rf /usr/local/samba/sbin/smbd; cp bin/smbd /usr/local/samba/sbin/smbd

Create or update /usr/lib/systemd/system/smb.service:

[Unit]
Description=Samba SMB Daemon
Documentation=man:smbd(8) man:samba(7) man:smb.conf(5)
Wants=network-online.target
After=network.target network-online.target nmb.service winbind.service

[Service]
Type=notify
PIDFile=/run/smbd.pid
LimitNOFILE=16384
EnvironmentFile=-/etc/sysconfig/samba
ExecStart=/usr/local/samba/sbin/smbd --foreground --no-process-group $SMBDOPTIONS
ExecReload=/bin/kill -HUP $MAINPID
LimitCORE=infinity
Environment=KRB5CCNAME=FILE:/run/samba/krb5cc_samba

[Install]
WantedBy=multi-user.target

Create config file /usr/local/samba/etc/smb.conf:

[TEST]
    comment = test dir
    path = /tmp/s_test
    public = yes
    read only = no
    writeable = yes

Create smb user:

# We are testing in a virtual machine, so we just use the root user.
pdbedit -a -u root

Start samba:

# stop the ksmbd service before starting samba
systemctl stop ksmbd.service

mkdir /tmp/s_test
chmod 777 /tmp/s_test # just for test

systemctl restart smbd.service # debian
systemctl restart smb.service # fedora

4 Windows environment

10.42.20.210 is the IP address of the SMB server.

Enter the following path in “File Explorer”:

# Windows is case-insensitive, so you can use either "TEST" or "test"
\\10.42.20.210\test

When switching between samba and ksmbd, Windows may fail to mount. In that case, open PowerShell on Windows and run the following commands:

# View existing connections
net use
# Delete a specific connection
net use \\10.42.20.210\IPC$ /delete
net use \\10.42.20.210\test /delete
# Delete all connections (not recommended)
net use * /delete

5 Analysis of packets captured by tcpdump

5.1 samba 20251110-2016

The full capture file can be found on GitHub (or on gitee).

Windows “File Explorer” enter root directory of smb server:

Windows “File Explorer” enter dir/:

Samba server executes touch dir/file1:

6 samba code analysis

Some code (e.g., the definition of NT_STATUS_PENDING) is generated during compilation. To see the full code, the project must be compiled first.

You can use macros like DBG_ERR(), …, DBG_DEBUG(), etc., to print debug information.

Use log_stack_trace() to print the function stack. If you get a compilation error indicating that log_stack_trace() cannot be found, you can refer to the changes in the patch 0001-dump-stack-of-smbd_parent_loop.patch.

The logs can be found in /usr/local/samba/var/log.smbd.

When samba receive Create Request:

smbd_smb2_request_dispatch
  smbd_smb2_request_process_create
    smbd_smb2_create_send
      smbd_smb2_create_finish
        // save the fsp, and return immediately when file_fsp_smb2() is called later
        smb2req->compat_chain_fsp = smb1req->chain_fsp

When samba receive Notify Request:

smbd_smb2_request_dispatch
  smbd_smb2_request_process_notify
    // both persistent_id and volatile_id are -1 when `Create Request` and `Notify Request` are in the same compound request
    file_fsp_smb2
      return smb2req->compat_chain_fsp
    smbd_smb2_notify_send
      change_notify_create
      if (change_notify_fsp_has_changes(fsp) // have change information
      change_notify_reply // notify immediately
        // reply NT_STATUS_OK
      change_notify_add_request // No changes for now, wait in the queue
    smbd_smb2_request_pending_queue // nothing to notify, start the timer

Start the timer:

smbd_smb2_request_pending_timer
  // reply NT_STATUS_PENDING
  // SMB2_HDR_OPCODE defines the offset of the Command field in struct smb2_hdr

When Windows exits the directory, samba receive Cancel Request:

smbd_smb2_request_dispatch
  smbd_smb2_request_process_cancel
    _tevent_req_cancel
      smbd_smb2_notify_cancel
        smbd_notify_cancel_by_smbreq
          smbd_notify_cancel_by_map
            change_notify_reply
              // reply NT_STATUS_CANCELLED

Samba send Notify Response when change information is available:

messaging_dispatch_classic
  notify_handler
    notify_callback
      files_forall
        notify_fsp_cb
          notify_fsp
            change_notify_reply
              // reply NT_STATUS_OK