[PATCH v3 1/1] smbinfo: add notify subcommand

Steve French smfrench at gmail.com
Wed Jan 7 19:42:34 UTC 2026


done - added to cifs-utils for-next, pending any additional review
comments and/or testing

On Tue, Jan 6, 2026 at 10:32 PM <chenxiaosong.chenxiaosong at linux.dev> wrote:
>
> From: ChenXiaoSong <chenxiaosong at kylinos.cn>
>
> Add `notify` subcommand to query a directory for change notifications.
>
> Example:
>
>   ./smbinfo notify /mnt/dir
>   # Then create a new file `/server/export/dir/file` on SMB server
>   Notify completed, returned data_len is 20
>   00000000:  00 00 00 00 01 00 00 00  08 00 00 00 66 00 69 00  ............f.i.
>   00000010:  6c 00 65 00                                       l.e.
>   # Call `ioctl()` again
>
> Press `Ctrl+C` to exit `smbinfo`.
>
> Link: https://lore.kernel.org/linux-cifs/CAH2r5msHiZWzP5hdtPgb+wV3DL3J31RtgQRLQeuhCa_ULt3PfA@mail.gmail.com/
> Suggested-by: Steve French <stfrench at microsoft.com>
> Signed-off-by: ChenXiaoSong <chenxiaosong at kylinos.cn>
> ---
>  smbinfo     | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  smbinfo.rst |  2 ++
>  2 files changed, 80 insertions(+)
>
> diff --git a/smbinfo b/smbinfo
> index 2e9e42d..57e8a0a 100755
> --- a/smbinfo
> +++ b/smbinfo
> @@ -27,6 +27,7 @@ import struct
>  import stat
>  import datetime
>  import calendar
> +import threading
>
>  VERBOSE = False
>
> @@ -36,6 +37,7 @@ CIFS_ENUMERATE_SNAPSHOTS = 0x800ccf06
>  CIFS_DUMP_KEY            = 0xc03acf08
>  CIFS_DUMP_FULL_KEY       = 0xc011cf0a
>  CIFS_GET_TCON_INFO       = 0x800ccf0c
> +CIFS_IOC_NOTIFY_INFO     = 0xc009cf0b
>
>  # large enough input buffer length
>  INPUT_BUFFER_LENGTH = 16384
> @@ -294,6 +296,10 @@ def main():
>      sap.add_argument("file")
>      sap.set_defaults(func=cmd_gettconinfo)
>
> +    sap = subp.add_parser("notify", help="Query a directory for change notifications")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_notify)
> +
>      # parse arguments
>      args = ap.parse_args()
>
> @@ -905,5 +911,77 @@ def cmd_gettconinfo(args):
>      print("TCON Id: 0x%x"%tcon.tid)
>      print("Session Id: 0x%x"%tcon.session_id)
>
> +def cmd_notify(args):
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    thread = threading.Thread(target=notify_thread, args=(fd,))
> +    thread.start()
> +
> +    try:
> +        thread.join()
> +    except KeyboardInterrupt:
> +        pass
> +    finally:
> +        os.close(fd)
> +        return False
> +
> +def notify_thread(fd):
> +    fmt = "<IBI"
> +    # See `struct smb3_notify_info` in linux kernel fs/smb/client/cifs_ioctl.h
> +    completion_filter = 0xFFF
> +    watch_tree = True
> +    data_len = 1000
> +
> +    while True:
> +        buf = bytearray(struct.pack(fmt, completion_filter, watch_tree, data_len))
> +        buf.extend(bytearray(data_len))
> +
> +        try:
> +            fcntl.ioctl(fd, CIFS_IOC_NOTIFY_INFO, buf, True)
> +        except Exception as e:
> +            print("syscall failed: %s"%e)
> +            return False
> +
> +        _, _, data_len_returned = struct.unpack_from(fmt, buf, 0)
> +        print("Notify completed, returned data_len is", data_len_returned)
> +        notify_data, = struct.unpack_from(f'{data_len_returned}s', buf, struct.calcsize(fmt))
> +        print_notify(notify_data)
> +
> +def print_notify(notify_data):
> +    if notify_data is None:
> +        return
> +
> +    data_size = len(notify_data)
> +    if data_size == 0:
> +        return
> +
> +    BYTES_PER_LINE = 16
> +    for offset in range(0, data_size, BYTES_PER_LINE):
> +        chunk = notify_data[offset:offset + BYTES_PER_LINE]
> +
> +        # raw hex data
> +        hex_bytes = "".join(
> +            (" " if i % 8 == 0 else "") + f"{x:02x} "
> +            for i, x in enumerate(chunk)
> +        )
> +
> +        # padding
> +        pad_len = BYTES_PER_LINE - len(chunk)
> +        pad = "   " * pad_len
> +        if pad_len >= 8:
> +            pad += " " * (pad_len // 8)
> +
> +        # ASCII
> +        ascii_part = "".join(
> +            chr(x) if 31 < x < 127 else "."
> +            for x in chunk
> +        )
> +
> +        print(f"{offset:08x}: {hex_bytes}{pad} {ascii_part}")
> +
>  if __name__ == '__main__':
>      main()
> diff --git a/smbinfo.rst b/smbinfo.rst
> index 17270c5..91b8895 100644
> --- a/smbinfo.rst
> +++ b/smbinfo.rst
> @@ -98,6 +98,8 @@ the SMB3 traffic of this mount can be decryped e.g. via wireshark
>
>  `gettconinfo`: Prints both the TCON Id and Session Id for a cifs file.
>
> +`notify`: Query a directory for change notifications.
> +
>  *****
>  NOTES
>  *****
> --
> 2.43.0
>


-- 
Thanks,

Steve



More information about the samba-technical mailing list