From f427dfa949750f59e4dacb6e282fd4e35b0b3e35 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 31 Aug 2026 15:16:38 +0900 Subject: ntfs: add named stream ioctls support NTFS supports multiple named $DATA attributes, commonly known as alternate data streams. Add NTFS-specific ioctls to list, read, write, and remove named streams through an opened base file or directory. The UAPI provides separate commands for each operation. - NTFS_IOC_STREAM_READ - NTFS_IOC_STREAM_WRITE - NTFS_IOC_STREAM_REMOVE - NTFS_IOC_LIST_STREAMS Read and write requests use struct ntfs_stream to specify the stream name, byte offset, and transfer length. Writes create the stream if it does not already exist. A write failure after creation may leave the new stream visible. It is not rolled back because another caller may already have opened it. Userspace can remove it with NTFS_IOC_STREAM_REMOVE. The list command returns variable-length entries containing each named stream's data size, allocated size, and name. If the buffer is too small, the command returns -ENOSPC without copying entries. bytes_returned reports the required buffer size, and stream_count reports the number of streams. Stream names use the mounted filesystem NLS by default. Set NTFS_STREAM_FL_UTF16 to pass or receive raw UTF-16LE names. In NLS mode, names that cannot be represented by the mounted character set are omitted from the list. Use UTF-16 mode for lossless enumeration. Read and write transfers are limited to 16 MiB per request. Signed-off-by: Namjae Jeon --- include/uapi/linux/ntfs.h | 130 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 include/uapi/linux/ntfs.h (limited to 'include') diff --git a/include/uapi/linux/ntfs.h b/include/uapi/linux/ntfs.h new file mode 100644 index 000000000000..f5b3d4997a56 --- /dev/null +++ b/include/uapi/linux/ntfs.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2026 LG Electronics Co., Ltd. + */ + +#ifndef _UAPI_LINUX_NTFS_H +#define _UAPI_LINUX_NTFS_H +#include +#include + +#define NTFS_IOC_MAGIC 0xEF + +/* + * Flags for ntfs_stream.flags and ntfs_list_streams.flags. + * + * NTFS_STREAM_FL_UTF16 makes stream names raw UTF-16LE, exactly as stored on + * disk, instead of encoding them with the mounted filesystem NLS. This + * allows lossless round-tripping of stream names whose characters are not + * representable by the mount NLS (e.g. for Wine, which works in UTF-16 + * natively). When set, the name length fields count bytes of UTF-16LE and + * must therefore be even. + */ +#define NTFS_STREAM_FL_UTF16 0x1 + +/* + * ntfs named stream read, write, and remove ioctl structure. + * + * @stream_offset: Offset within the named stream for read/write. + * @io_len: Number of bytes to read/write. Must be zero for remove. + * @bytes_returned: Actual bytes transferred on success (out). + * @name_len: Stream name length in bytes, not including any + * terminating NUL. When NTFS_STREAM_FL_UTF16 is set, + * this counts UTF-16LE bytes and must be even. + * @flags: Bit mask of NTFS_STREAM_FL_* flags. Other bits must + * be zero. + * @reserved: Must be zero. + * @buffer: Bare stream name followed by stream data for read/write. + * + * The stream name is encoded with the mounted filesystem NLS, or as raw + * UTF-16LE when NTFS_STREAM_FL_UTF16 is set. A write creates the stream if + * it does not already exist. A write failure after creation may leave the + * new stream behind; it can be removed separately. + * + * For NTFS_IOC_STREAM_READ, a successful short read indicates EOF. If a read + * fails, the ioctl returns an error and copies no stream data to userspace. + * + * NTFS_IOC_STREAM_WRITE is not atomic. Write errors may leave partial data + * changes and an extended stream size. On error, bytes_returned is not valid. + * The ioctl does not report the completed byte count. + */ +struct ntfs_stream { + __aligned_u64 stream_offset; + __aligned_u64 io_len; + __aligned_u64 bytes_returned; + __u32 name_len; + __u32 flags; + __aligned_u64 reserved; + __u8 buffer[]; +}; + +/* + * Single stream entry returned by NTFS_IOC_LIST_STREAMS. + * + * @next_entry_off: Byte offset from the start of this entry to the next + * entry, or zero for the last entry. Always a multiple + * of 8. Consumers must use this to advance instead of + * computing the stride themselves. + * @size: Stream data size in bytes. + * @alloc_size: Bytes allocated for the stream (cluster aligned for + * non-resident streams). + * @name_len: Stream name length in bytes, not including a NUL + * terminator (none is stored). Counts UTF-16LE bytes + * when NTFS_STREAM_FL_UTF16 was requested. + * @name_offset: Byte offset from the start of this entry to @name. + * @reserved: Must be zero. + * @name: Bare stream name; NLS encoded, or raw UTF-16LE when + * NTFS_STREAM_FL_UTF16 was requested. + * + * New fixed fields may be added after @reserved and before @name. Consumers + * must use @name_offset to locate the name and @next_entry_off to advance to + * the next entry. + */ +struct ntfs_stream_entry { + __aligned_u64 next_entry_off; + __aligned_u64 size; + __aligned_u64 alloc_size; + __u32 name_len; + __u32 name_offset; + __u32 reserved; + __u8 name[]; +}; + +/* + * ntfs list streams ioctl structure. + * + * @buffer_size: user buffer size(in). + * @bytes_returned: actual bytes written or required(out). + * @stream_count: number of streams(out). + * @flags: Bit mask of NTFS_STREAM_FL_* flags controlling the + * encoding of the returned names; other bits must be zero. + * @reserved: Must be zero. + * @buffer: ntfs_stream_entry array. + * + * The variable-length entries follow the header in @buffer, each aligned on + * an 8-byte boundary and chained via ntfs_stream_entry.next_entry_off. If + * @buffer_size is too small, no data is copied, @stream_count and + * @bytes_returned report the required values and the ioctl fails with + * -ENOSPC. In NLS mode, names that cannot be represented by the mounted + * character set are omitted; use NTFS_STREAM_FL_UTF16 to list all + * names without conversion loss. + */ +struct ntfs_list_streams { + __aligned_u64 buffer_size; + __aligned_u64 bytes_returned; + __aligned_u64 stream_count; + __u32 flags; + __u32 reserved; + __u8 buffer[]; +}; + +#define NTFS_IOC_STREAM_READ \ + _IOWR(NTFS_IOC_MAGIC, 1, struct ntfs_stream) +#define NTFS_IOC_STREAM_WRITE \ + _IOWR(NTFS_IOC_MAGIC, 2, struct ntfs_stream) +#define NTFS_IOC_STREAM_REMOVE \ + _IOWR(NTFS_IOC_MAGIC, 3, struct ntfs_stream) +#define NTFS_IOC_LIST_STREAMS \ + _IOWR(NTFS_IOC_MAGIC, 4, struct ntfs_list_streams) + +#endif /* _UAPI_LINUX_NTFS_H */ -- cgit v1.2.3 From 212e79d58aafdfcfd280806ef38e86dcdc7e2b47 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 13 Oct 2026 22:49:17 +0200 Subject: ntfs: avoid unnecessary padding in uapi structure Building the uapi headers with -Wpadded shows a warning that may indicate an incompatibility: ./usr/include/linux/ntfs.h:91:1: error: padding struct size to alignment boundary with 4 bytes [-Werror=padded] This is relatively harmless here as the 64-bit members force natural alignment, so this is not a problem for compat ioctls, and I checked that the padding word is still initialized by means of using kvzalloc() to allocate it, but it's better to avoid the padding out of principle. Change the reserved member to use a 64-bit type so the name field starts after the structure and all padding is explicit. Signed-off-by: Arnd Bergmann Reviewed-by: Baolin Liu Signed-off-by: Namjae Jeon --- include/uapi/linux/ntfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/ntfs.h b/include/uapi/linux/ntfs.h index f5b3d4997a56..89467ba07262 100644 --- a/include/uapi/linux/ntfs.h +++ b/include/uapi/linux/ntfs.h @@ -86,7 +86,7 @@ struct ntfs_stream_entry { __aligned_u64 alloc_size; __u32 name_len; __u32 name_offset; - __u32 reserved; + __aligned_u64 reserved; __u8 name[]; }; -- cgit v1.2.3