| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/30 17:11 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"NTFS_FS"
],
"FocusSymbols": [
"ntfs_ioctl"
],
"Reasoning": "The patch adds new ioctls to the NTFS filesystem to read, write, remove, and list named streams (Alternate Data Streams). The ioctls are reachable via `ntfs_ioctl`.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/30 17:11 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit c2d55637f37b63f4b71081b7804ad9ce794f2aa9\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Jul 30 17:11:32 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst\nindex 3f0ef1e27eb07..2b9869aeb811b 100644\n--- a/Documentation/userspace-api/ioctl/ioctl-number.rst\n+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst\n@@ -399,6 +399,7 @@ Code Seq# Include File Comments\n 0xE5 00-3F linux/fuse.h\n 0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver\n 0xEE 00-09 uapi/linux/pfrut.h Platform Firmware Runtime Update and Telemetry\n+0xEF 00-0F uapi/linux/ntfs.h NTFS\n 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development)\n \u003cmailto:thomas@winischhofer.net\u003e\n 0xF6 all LTTng Linux Trace Toolkit Next Generation\ndiff --git a/fs/ntfs/file.c b/fs/ntfs/file.c\nindex d4282822b3cef..961b94f545f2c 100644\n--- a/fs/ntfs/file.c\n+++ b/fs/ntfs/file.c\n@@ -15,6 +15,8 @@\n #include \u003clinux/posix_acl_xattr.h\u003e\n #include \u003clinux/compat.h\u003e\n #include \u003clinux/falloc.h\u003e\n+#include \u003clinux/overflow.h\u003e\n+#include \u003cuapi/linux/ntfs.h\u003e\n \n #include \"lcnalloc.h\"\n #include \"ntfs.h\"\n@@ -23,6 +25,7 @@\n #include \"iomap.h\"\n #include \"bitmap.h\"\n #include \"volume.h\"\n+#include \"mft.h\"\n \n #include \u003clinux/filelock.h\u003e\n \n@@ -839,6 +842,504 @@ static int ntfs_ioctl_fitrim(struct ntfs_volume *vol, unsigned long arg)\n \treturn 0;\n }\n \n+#define NTFS_STREAM_MAX_IO\t(16 * 1024 * 1024) /* 16MB limit */\n+\n+/*\n+ * ntfs_read_named_stream - Read data from a named $DATA stream.\n+ *\n+ * @ni:\t\tNTFS inode\n+ * @uname:\tStream name\n+ * @uname_len:\tLength of name in Unicode characters\n+ * @offset:\tByte offset in the stream\n+ * @len:\tNumber of bytes to read\n+ * @data_buf:\tBuffer to copy data to\n+ *\n+ * Return:\t0 on success, negative error code otherwise.\n+ *\t\tIf offset \u003e= stream size, returns 0.\n+ */\n+static int ntfs_read_named_stream(struct ntfs_inode *ni, __le16 *uname,\n+\t\tu32 uname_len, u64 offset, u64 len, void *data_buf,\n+\t\tu64 *rbytes)\n+{\n+\tstruct ntfs_attr_search_ctx *ctx;\n+\tstruct inode *attr_vi;\n+\tint err = 0;\n+\tu64 data_size;\n+\ts64 bytes;\n+\tchar *rbuf = NULL;\n+\n+\tif (!ni || !uname || uname_len == 0)\n+\t\treturn -EINVAL;\n+\n+\tif (len == 0 || len \u003e NTFS_STREAM_MAX_IO)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(\u0026ni-\u003emrec_lock);\n+\tctx = ntfs_attr_get_search_ctx(ni, NULL);\n+\tif (!ctx) {\n+\t\tmutex_unlock(\u0026ni-\u003emrec_lock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\terr = ntfs_attr_lookup(AT_DATA, uname, uname_len, CASE_SENSITIVE, 0,\n+\t\t\tNULL, 0, ctx);\n+\tif (err)\n+\t\tgoto out_lock;\n+\n+\tif (ctx-\u003eattr-\u003enon_resident)\n+\t\tdata_size = le64_to_cpu(ctx-\u003eattr-\u003edata.non_resident.data_size);\n+\telse\n+\t\tdata_size = le32_to_cpu(ctx-\u003eattr-\u003edata.resident.value_length);\n+\n+\tif (offset \u003e= data_size) {\n+\t\t*rbytes = 0;\n+\t\t/* EOF: nothing to read */\n+\t\tgoto out_lock;\n+\t}\n+\n+\t/* Adjust length to not read beyond EOF */\n+\tif (len \u003e data_size - offset)\n+\t\tlen = data_size - offset;\n+\n+\tattr_vi = ntfs_attr_iget(VFS_I(ni), AT_DATA, uname, uname_len);\n+\tntfs_attr_put_search_ctx(ctx);\n+\tmutex_unlock(\u0026ni-\u003emrec_lock);\n+\n+\tif (IS_ERR(attr_vi)) {\n+\t\terr = PTR_ERR(attr_vi);\n+\t\tgoto out_free;\n+\t}\n+\n+\trbuf = kvzalloc(len, GFP_NOFS);\n+\tif (!rbuf) {\n+\t\terr = -ENOMEM;\n+\t\tiput(attr_vi);\n+\t\tgoto out_free;\n+\t}\n+\n+\tbytes = ntfs_inode_attr_pread(attr_vi, offset, len, rbuf);\n+\tiput(attr_vi);\n+\tif (bytes \u003c 0) {\n+\t\terr = bytes;\n+\t\tgoto out_free;\n+\t}\n+\n+\t*rbytes = bytes;\n+\tmemcpy(data_buf, rbuf, bytes);\n+\n+out_free:\n+\tkvfree(rbuf);\n+\treturn err;\n+\n+out_lock:\n+\tntfs_attr_put_search_ctx(ctx);\n+\tmutex_unlock(\u0026ni-\u003emrec_lock);\n+\treturn err;\n+}\n+\n+/*\n+ * ntfs_write_named_stream - Write data from a named $DATA stream.\n+ *\n+ * @ni:\t\tNTFS inode\n+ * @uname:\tStream name\n+ * @uname_len:\tLength of name in Unicode characters\n+ * @offset:\tByte offset in the stream\n+ * @len:\tNumber of bytes to write\n+ * @data_stream: Data to write\n+ *\n+ * Return:\t0 on success, negative error code otherwise.\n+ */\n+static int ntfs_write_named_stream(struct ntfs_inode *ni, __le16 *uname,\n+\t\tu32 uname_len, u64 offset, u64 len, void *data_stream,\n+\t\tu64 *wbytes)\n+{\n+\tstruct ntfs_attr_search_ctx *ctx;\n+\tstruct inode *attr_vi;\n+\tint err;\n+\ts64 bytes;\n+\n+\tif (!ni || !uname || uname_len == 0 || !data_stream)\n+\t\treturn -EINVAL;\n+\n+\tif (len == 0 || len \u003e NTFS_STREAM_MAX_IO)\n+\t\treturn -EINVAL;\n+\n+\tmutex_lock(\u0026ni-\u003emrec_lock);\n+\tctx = ntfs_attr_get_search_ctx(ni, NULL);\n+\tif (!ctx) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\terr = ntfs_attr_lookup(AT_DATA, uname, uname_len, CASE_SENSITIVE, 0,\n+\t\t\tNULL, 0, ctx);\n+\tif (err) {\n+\t\tif (err != -ENOENT)\n+\t\t\tgoto out_unlock;\n+\t\terr = ntfs_attr_add(ni, AT_DATA, uname, uname_len, NULL, 0);\n+\t\tif (err)\n+\t\t\tgoto out_unlock;\n+\t\tmark_mft_record_dirty(ni);\n+\t}\n+\n+\tattr_vi = ntfs_attr_iget(VFS_I(ni), AT_DATA, uname, uname_len);\n+\tntfs_attr_put_search_ctx(ctx);\n+\tmutex_unlock(\u0026ni-\u003emrec_lock);\n+\n+\tif (IS_ERR(attr_vi)) {\n+\t\terr = PTR_ERR(attr_vi);\n+\t\tgoto out;\n+\t}\n+\n+\tbytes = ntfs_inode_attr_pwrite(attr_vi, offset, len, data_stream,\n+\t\t\t\t false);\n+\tif (bytes \u003c 0)\n+\t\terr = bytes;\n+\telse\n+\t\t*wbytes = bytes;\n+\tiput(attr_vi);\n+\n+out:\n+\treturn err;\n+\n+out_unlock:\n+\tif (ctx)\n+\t\tntfs_attr_put_search_ctx(ctx);\n+\tmutex_unlock(\u0026ni-\u003emrec_lock);\n+\treturn err;\n+}\n+\n+/*\n+ * ntfs_remove_named_stream - Remove a named $DATA stream.\n+ *\n+ * @ni:\t\tNTFS inode\n+ * @uname:\tStream name\n+ * @uname_len:\tName length in Unicode characters\n+ *\n+ * Return:\t0 on success, negative error\n+ * -ENOENT if stream not found\n+ */\n+static int ntfs_remove_named_stream(struct ntfs_inode *ni, __le16 *uname,\n+\t\tu32 uname_len)\n+{\n+\tif (!ni || !uname || uname_len == 0)\n+\t\treturn -EINVAL;\n+\n+\treturn ntfs_attr_remove(ni, AT_DATA, uname, uname_len);\n+}\n+\n+enum ntfs_stream_op {\n+\tNTFS_STREAM_OP_READ,\n+\tNTFS_STREAM_OP_WRITE,\n+\tNTFS_STREAM_OP_REMOVE,\n+};\n+\n+static long ntfs_ioctl_stream(struct file *filp, unsigned long arg,\n+\t\t\t enum ntfs_stream_op op)\n+{\n+\tstruct inode *inode = file_inode(filp);\n+\tstruct ntfs_inode *ni = NTFS_I(inode);\n+\tstruct ntfs_stream __user *ureq =\n+\t\t(struct ntfs_stream __user *)arg;\n+\tstruct ntfs_stream *req;\n+\tsize_t header_size = sizeof(struct ntfs_stream);\n+\tsize_t data_size, total_size;\n+\tchar *kname, *kdata;\n+\t__le16 *uname = NULL, *sname;\n+\tint err = 0, sname_len;\n+\tbool utf16;\n+\tstruct ntfs_stream hdr;\n+\n+\t/* First copy fixed header */\n+\tif (copy_from_user(\u0026hdr, ureq, header_size))\n+\t\treturn -EFAULT;\n+\n+\tif (hdr.io_len \u003e NTFS_STREAM_MAX_IO ||\n+\t (hdr.flags \u0026 ~NTFS_STREAM_FL_UTF16) || hdr.reserved)\n+\t\treturn -EINVAL;\n+\n+\tutf16 = hdr.flags \u0026 NTFS_STREAM_FL_UTF16;\n+\n+\tif (hdr.name_len == 0)\n+\t\treturn -EINVAL;\n+\tif (utf16) {\n+\t\t/* UTF-16LE byte count: must be even and within the limit. */\n+\t\tif ((hdr.name_len \u0026 1) ||\n+\t\t hdr.name_len \u003e NTFS_MAX_NAME_LEN * sizeof(__le16))\n+\t\t\treturn -EINVAL;\n+\t} else if (hdr.name_len \u003e NTFS_MAX_NAME_LEN) {\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tswitch (op) {\n+\tcase NTFS_STREAM_OP_READ:\n+\t\tif (!(filp-\u003ef_mode \u0026 FMODE_READ) || hdr.io_len == 0)\n+\t\t\treturn -EINVAL;\n+\t\tdata_size = hdr.io_len;\n+\t\tbreak;\n+\tcase NTFS_STREAM_OP_WRITE:\n+\t\tif (!(filp-\u003ef_mode \u0026 FMODE_WRITE) || hdr.io_len == 0)\n+\t\t\treturn -EINVAL;\n+\t\tdata_size = hdr.io_len;\n+\t\tbreak;\n+\tcase NTFS_STREAM_OP_REMOVE:\n+\t\tif (!(filp-\u003ef_mode \u0026 FMODE_WRITE) || hdr.io_len != 0)\n+\t\t\treturn -EINVAL;\n+\t\tdata_size = 0;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -ENOTTY;\n+\t}\n+\n+\tif (check_add_overflow(header_size, (size_t)hdr.name_len, \u0026total_size) ||\n+\t check_add_overflow(total_size, data_size, \u0026total_size))\n+\t\treturn -EOVERFLOW;\n+\n+\treq = memdup_user(ureq, total_size);\n+\tif (IS_ERR(req))\n+\t\treturn PTR_ERR(req);\n+\n+\treq-\u003ebytes_returned = 0;\n+\n+\tkname = (char *)req-\u003ebuffer;\n+\tkdata = (char *)req-\u003ebuffer + req-\u003ename_len;\n+\n+\tif (req-\u003ename_len != hdr.name_len || req-\u003eio_len != hdr.io_len ||\n+\t req-\u003eflags != hdr.flags || req-\u003ereserved) {\n+\t\terr = -EINVAL;\n+\t\tgoto out_free;\n+\t}\n+\n+\tif (utf16) {\n+\t\t/* Name is already on-disk UTF-16LE; use it in place. */\n+\t\tsname = (__le16 *)kname;\n+\t\tsname_len = req-\u003ename_len / sizeof(__le16);\n+\t} else {\n+\t\tif (memchr(kname, '\\0', req-\u003ename_len)) {\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto out_free;\n+\t\t}\n+\t\tsname_len = ntfs_nlstoucs(ni-\u003evol, kname, req-\u003ename_len,\n+\t\t\t\t\u0026uname, NTFS_MAX_NAME_LEN);\n+\t\tif (sname_len \u003c 0) {\n+\t\t\terr = sname_len;\n+\t\t\tgoto out_free;\n+\t\t}\n+\t\tsname = uname;\n+\t}\n+\n+\tswitch (op) {\n+\tcase NTFS_STREAM_OP_READ:\n+\t\terr = ntfs_read_named_stream(ni, sname, sname_len,\n+\t\t\t\t\t req-\u003estream_offset, req-\u003eio_len,\n+\t\t\t\t\t kdata,\n+\t\t\t\t\t \u0026req-\u003ebytes_returned);\n+\t\tif (!err) {\n+\t\t\tif (copy_to_user(ureq, req,\n+\t\t\t\t\theader_size + req-\u003ename_len + req-\u003ebytes_returned))\n+\t\t\t\terr = -EFAULT;\n+\t\t}\n+\t\tbreak;\n+\tcase NTFS_STREAM_OP_WRITE:\n+\t\terr = mnt_want_write_file(filp);\n+\t\tif (err)\n+\t\t\tbreak;\n+\t\terr = ntfs_write_named_stream(ni, sname, sname_len,\n+\t\t\t\t\t req-\u003estream_offset, req-\u003eio_len,\n+\t\t\t\t\t kdata,\n+\t\t\t\t\t \u0026req-\u003ebytes_returned);\n+\t\tmnt_drop_write_file(filp);\n+\t\tif (!err) {\n+\t\t\tif (copy_to_user(\u0026ureq-\u003ebytes_returned,\n+\t\t\t\t\t \u0026req-\u003ebytes_returned,\n+\t\t\t\t\t sizeof(req-\u003ebytes_returned)))\n+\t\t\t\terr = -EFAULT;\n+\t\t}\n+\t\tbreak;\n+\tcase NTFS_STREAM_OP_REMOVE:\n+\t\terr = mnt_want_write_file(filp);\n+\t\tif (err)\n+\t\t\tbreak;\n+\t\terr = ntfs_remove_named_stream(ni, sname, sname_len);\n+\t\tmnt_drop_write_file(filp);\n+\t\tbreak;\n+\tdefault:\n+\t\terr = -ENOTTY;\n+\t}\n+\n+out_free:\n+\tif (uname)\n+\t\tkmem_cache_free(ntfs_name_cache, uname);\n+\tkfree(req);\n+\treturn err;\n+}\n+\n+static int ntfs_ioctl_list_streams(struct file *filp, unsigned long arg)\n+{\n+\tstruct inode *inode = file_inode(filp);\n+\tstruct ntfs_inode *ni = NTFS_I(inode);\n+\tstruct ntfs_list_streams hdr;\n+\tstruct ntfs_stream_entry *entry, *last_entry = NULL;\n+\tsize_t required = 0;\n+\tvoid *kbuf = NULL;\n+\tvoid __user *ubuf;\n+\tstruct attr_record *a;\n+\tstruct ntfs_attr_search_ctx *actx;\n+\tint ret = 0, err, count = 0;\n+\tsize_t entry_size, offset = 0;\n+\tunsigned int name_len;\n+\tunsigned char *sn;\n+\tbool utf16;\n+\n+\tif (copy_from_user(\u0026hdr, (void __user *)arg, sizeof(hdr)))\n+\t\treturn -EFAULT;\n+\n+\tif ((hdr.flags \u0026 ~NTFS_STREAM_FL_UTF16) || hdr.reserved)\n+\t\treturn -EINVAL;\n+\n+\tutf16 = hdr.flags \u0026 NTFS_STREAM_FL_UTF16;\n+\n+\tubuf = (void __user *)arg + sizeof(hdr);\n+\n+\tmutex_lock(\u0026ni-\u003emrec_lock);\n+\tactx = ntfs_attr_get_search_ctx(ni, NULL);\n+\tif (!actx) {\n+\t\tmutex_unlock(\u0026ni-\u003emrec_lock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\twhile ((err = ntfs_attrs_walk(actx)) == 0) {\n+\t\ta = actx-\u003eattr;\n+\t\tif (a-\u003etype != AT_DATA || !a-\u003ename_length)\n+\t\t\tcontinue;\n+\n+\t\tif (utf16) {\n+\t\t\tname_len = a-\u003ename_length * sizeof(__le16);\n+\t\t} else {\n+\t\t\tsn = ntfs_attr_name_get(ni-\u003evol,\n+\t\t\t\t\t(__le16 *)((u8 *)a + le16_to_cpu(a-\u003ename_offset)),\n+\t\t\t\t\ta-\u003ename_length);\n+\t\t\tif (!sn) {\n+\t\t\t\tret = -EIO;\n+\t\t\t\tgoto out;\n+\t\t\t}\n+\t\t\tname_len = strlen(sn);\n+\t\t\tntfs_attr_name_free(\u0026sn);\n+\t\t}\n+\n+\t\tentry_size = sizeof(*entry) + name_len;\n+\t\tentry_size = ALIGN(entry_size, 8);\n+\n+\t\tif (required \u003e SIZE_MAX - entry_size) {\n+\t\t\tret = -EOVERFLOW;\n+\t\t\tgoto out;\n+\t\t}\n+\t\trequired += entry_size;\n+\t\tcount++;\n+\t}\n+\tif (err != -ENOENT) {\n+\t\tret = err;\n+\t\tgoto out;\n+\t}\n+\n+\thdr.stream_count = count;\n+\thdr.bytes_returned = required;\n+\n+\tif (!count)\n+\t\tgoto out;\n+\n+\tif (hdr.buffer_size \u003c required) {\n+\t\tret = -ENOSPC;\n+\t\tgoto out;\n+\t}\n+\n+\tkbuf = kvzalloc(required, GFP_NOFS);\n+\tif (!kbuf) {\n+\t\tret = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\n+\tntfs_attr_reinit_search_ctx(actx);\n+\twhile ((err = ntfs_attrs_walk(actx)) == 0) {\n+\t\ta = actx-\u003eattr;\n+\t\tif (a-\u003etype != AT_DATA || !a-\u003ename_length)\n+\t\t\tcontinue;\n+\t\tif (utf16) {\n+\t\t\tsn = NULL;\n+\t\t\tname_len = a-\u003ename_length * sizeof(__le16);\n+\t\t} else {\n+\t\t\tsn = ntfs_attr_name_get(ni-\u003evol,\n+\t\t\t\t\t(__le16 *)((u8 *)a + le16_to_cpu(a-\u003ename_offset)),\n+\t\t\t\t\ta-\u003ename_length);\n+\t\t\tif (!sn) {\n+\t\t\t\tret = -EIO;\n+\t\t\t\tgoto out;\n+\t\t\t}\n+\t\t\tname_len = strlen(sn);\n+\t\t}\n+\n+\t\tentry_size = sizeof(*entry) + name_len;\n+\t\tentry_size = ALIGN(entry_size, 8);\n+\t\tif (offset \u003e required - entry_size) {\n+\t\t\tret = -EOVERFLOW;\n+\t\t\tntfs_attr_name_free(\u0026sn);\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tentry = (struct ntfs_stream_entry *)(kbuf + offset);\n+\n+\t\tif (a-\u003enon_resident) {\n+\t\t\tentry-\u003esize = le64_to_cpu(a-\u003edata.non_resident.data_size);\n+\t\t\tentry-\u003ealloc_size =\n+\t\t\t\tle64_to_cpu(a-\u003edata.non_resident.allocated_size);\n+\n+\t\t} else {\n+\t\t\tentry-\u003esize = le32_to_cpu(a-\u003edata.resident.value_length);\n+\t\t\tentry-\u003ealloc_size = le32_to_cpu(a-\u003elength) -\n+\t\t\t\tle16_to_cpu(a-\u003edata.resident.value_offset);\n+\t\t}\n+\n+\t\tentry-\u003ename_len = name_len;\n+\t\tif (utf16)\n+\t\t\tmemcpy(entry-\u003ename,\n+\t\t\t (u8 *)a + le16_to_cpu(a-\u003ename_offset), name_len);\n+\t\telse\n+\t\t\tmemcpy(entry-\u003ename, sn, name_len);\n+\t\tntfs_attr_name_free(\u0026sn);\n+\n+\t\tentry-\u003enext_entry_off = entry_size;\n+\t\tlast_entry = entry;\n+\t\toffset += entry_size;\n+\t}\n+\tif (err != -ENOENT) {\n+\t\tret = err;\n+\t} else {\n+\t\t/* The chain terminates at the last entry. */\n+\t\tif (last_entry)\n+\t\t\tlast_entry-\u003enext_entry_off = 0;\n+\t\thdr.bytes_returned = offset;\n+\t}\n+\n+out:\n+\tmutex_unlock(\u0026ni-\u003emrec_lock);\n+\tntfs_attr_put_search_ctx(actx);\n+\n+\tif (ret \u0026\u0026 ret != -ENOSPC)\n+\t\tgoto out_free;\n+\n+\tif (!ret \u0026\u0026 kbuf \u0026\u0026 copy_to_user(ubuf, kbuf, hdr.bytes_returned)) {\n+\t\tret = -EFAULT;\n+\t\tgoto out_free;\n+\t}\n+\n+\tif (copy_to_user((void __user *)arg, \u0026hdr, sizeof(hdr)))\n+\t\tret = -EFAULT;\n+\n+out_free:\n+\tkvfree(kbuf);\n+\treturn ret;\n+}\n+\n long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)\n {\n \tswitch (cmd) {\n@@ -850,6 +1351,14 @@ long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)\n \t\treturn ntfs_ioctl_set_volume_label(filp, arg);\n \tcase FITRIM:\n \t\treturn ntfs_ioctl_fitrim(NTFS_SB(file_inode(filp)-\u003ei_sb), arg);\n+\tcase NTFS_IOC_STREAM_READ:\n+\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_READ);\n+\tcase NTFS_IOC_STREAM_WRITE:\n+\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_WRITE);\n+\tcase NTFS_IOC_STREAM_REMOVE:\n+\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_REMOVE);\n+\tcase NTFS_IOC_LIST_STREAMS:\n+\t\treturn ntfs_ioctl_list_streams(filp, arg);\n \tdefault:\n \t\treturn -ENOTTY;\n \t}\ndiff --git a/include/uapi/linux/ntfs.h b/include/uapi/linux/ntfs.h\nnew file mode 100644\nindex 0000000000000..a5783dc3b9054\n--- /dev/null\n+++ b/include/uapi/linux/ntfs.h\n@@ -0,0 +1,115 @@\n+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */\n+/*\n+ * Copyright (c) 2026 LG Electronics Co., Ltd.\n+ */\n+\n+#ifndef _UAPI_LINUX_NTFS_H\n+#define _UAPI_LINUX_NTFS_H\n+#include \u003clinux/types.h\u003e\n+#include \u003clinux/ioctl.h\u003e\n+\n+#define NTFS_IOC_MAGIC\t0xEF\n+\n+/*\n+ * Flags for ntfs_stream.flags and ntfs_list_streams.flags.\n+ *\n+ * The stream name is raw UTF-16LE, exactly as stored on disk, instead of\n+ * being encoded with the mounted filesystem NLS. This allows lossless\n+ * round-tripping of stream names whose characters are not representable\n+ * in the mount NLS (e.g. for Wine, which works in UTF-16 natively).\n+ * When set, the name length fields count bytes of UTF-16LE and must\n+ * therefore be even.\n+ */\n+#define NTFS_STREAM_FL_UTF16\t0x1\n+\n+/*\n+ * ntfs named stream ioctl structure.\n+ *\n+ * @stream_offset:\tOffset within the named stream.\n+ * @io_len:\t\tNumber of bytes to read/write.\n+ * @bytes_returned:\tActual bytes transferred (out).\n+ * @name_len:\t\tStream name length in bytes, not including any\n+ *\t\t\tterminating NUL (which must not be present). When\n+ *\t\t\tNTFS_STREAM_FL_UTF16 is set this is a UTF-16LE byte\n+ *\t\t\tcount and must be even.\n+ * @flags:\t\tBit mask of NTFS_STREAM_FL_* flags. other bits must be\n+ *\t\t\tzero.\n+ * @reserved:\t\tMust be zero.\n+ * @buffer:\t\tStream name followed by stream data.\n+ *\n+ * The stream name is the bare stream name, without the leading ':' or the\n+ * trailing \":$DATA\" decoration used by Windows. By default it is encoded with\n+ * the mounted filesystem NLS. If NTFS_STREAM_FL_UTF16 is set in @flags it is\n+ * raw UTF-16LE instead. For read and write, @io_len bytes of data follow the\n+ * name. For remove, only the name is required and @io_len must be zero.\n+ */\n+struct ntfs_stream {\n+\t__aligned_u64 stream_offset;\n+\t__aligned_u64 io_len;\n+\t__aligned_u64 bytes_returned;\n+\t__u32 name_len;\n+\t__u32 flags;\n+\t__aligned_u64 reserved;\n+\t__u8 buffer[]; /* name + data */\n+};\n+\n+/*\n+ * Single stream entry returned by NTFS_IOC_LIST_STREAMS.\n+ *\n+ * @next_entry_off:\tByte offset from the start of this entry to the next\n+ *\t\t\tentry, or zero for the last entry. Always a multiple\n+ *\t\t\tof 8. Consumers must use this to advance instead of\n+ *\t\t\tcomputing the stride themselves, so the entry can grow\n+ *\t\t\tnew fields without breaking the ABI.\n+ * @size:\t\tStream data size in bytes.\n+ * @alloc_size:\t\tBytes allocated for the stream (cluster aligned for\n+ *\t\t\tnon-resident streams).\n+ * @name_len:\t\tStream name length in bytes, not including a NUL\n+ *\t\t\tterminator (none is stored). Counts UTF-16LE bytes\n+ *\t\t\twhen NTFS_STREAM_FL_UTF16 was requested.\n+ * @reserved:\t\tMust be zero.\n+ * @name:\t\tBare stream name; NLS encoded, or raw UTF-16LE when\n+ *\t\t\tNTFS_STREAM_FL_UTF16 was requested (see struct\n+ *\t\t\tntfs_stream).\n+ */\n+struct ntfs_stream_entry {\n+\t__aligned_u64 next_entry_off;\n+\t__aligned_u64 size;\n+\t__aligned_u64 alloc_size;\n+\t__u32 name_len;\n+\t__u32 reserved;\n+\t__u8 name[];\n+};\n+\n+/*\n+ * ntfs list streams ioctl structure.\n+ *\n+ * @buffer_size:\tuser buffer size(in).\n+ * @bytes_returned:\tactual bytes written or required(out).\n+ * @stream_count:\tnumber of streams(out).\n+ * @flags:\t\tBit mask of NTFS_STREAM_FL_* flags controlling the\n+ *\t\t\tencoding of the returned names; other bits must be zero.\n+ * @reserved:\t\tMust be zero.\n+ * @buffer:\t\tntfs_stream_entry array.\n+ *\n+ * The variable-length entries follow the header in @buffer, each aligned on\n+ * an 8-byte boundary and chained via ntfs_stream_entry.next_entry_off. If\n+ * @buffer_size is too small, no data is copied, @stream_count and\n+ * @bytes_returned report the required values and the ioctl fails with\n+ * -ENOSPC.\n+ */\n+struct ntfs_list_streams {\n+\t__aligned_u64 buffer_size;\n+\t__aligned_u64 bytes_returned;\n+\t__aligned_u64 stream_count;\n+\t__u32 flags;\n+\t__u32 reserved;\n+\t__u8 buffer[];\n+};\n+\n+#define NTFS_IOC_STREAM_READ\t\t_IOWR(NTFS_IOC_MAGIC, 1, struct ntfs_stream)\n+#define NTFS_IOC_STREAM_WRITE\t\t_IOWR(NTFS_IOC_MAGIC, 2, struct ntfs_stream)\n+#define NTFS_IOC_STREAM_REMOVE\t\t_IOWR(NTFS_IOC_MAGIC, 3, struct ntfs_stream)\n+#define NTFS_IOC_LIST_STREAMS\t\t_IOWR(NTFS_IOC_MAGIC, 4, struct ntfs_list_streams)\n+\n+#endif /* _UAPI_LINUX_NTFS_H */\n"
} |
| 2/1 | 2026/07/30 17:11 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"NTFS_FS"
],
"FocusSymbols": [
"ntfs_ioctl"
],
"Reasoning": "The patch adds new ioctls to the NTFS filesystem to read, write, remove, and list named streams (Alternate Data Streams). The ioctls are reachable via `ntfs_ioctl`.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit c2d55637f37b63f4b71081b7804ad9ce794f2aa9
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Jul 30 17:11:32 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 3f0ef1e27eb07..2b9869aeb811b 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -399,6 +399,7 @@ Code Seq# Include File Comments
0xE5 00-3F linux/fuse.h
0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver
0xEE 00-09 uapi/linux/pfrut.h Platform Firmware Runtime Update and Telemetry
+0xEF 00-0F uapi/linux/ntfs.h NTFS
0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development)
<mailto:thomas@winischhofer.net>
0xF6 all LTTng Linux Trace Toolkit Next Generation
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index d4282822b3cef..961b94f545f2c 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -15,6 +15,8 @@
#include <linux/posix_acl_xattr.h>
#include <linux/compat.h>
#include <linux/falloc.h>
+#include <linux/overflow.h>
+#include <uapi/linux/ntfs.h>
#include "lcnalloc.h"
#include "ntfs.h"
@@ -23,6 +25,7 @@
#include "iomap.h"
#include "bitmap.h"
#include "volume.h"
+#include "mft.h"
#include <linux/filelock.h>
@@ -839,6 +842,504 @@ static int ntfs_ioctl_fitrim(struct ntfs_volume *vol, unsigned long arg)
return 0;
}
+#define NTFS_STREAM_MAX_IO (16 * 1024 * 1024) /* 16MB limit */
+
+/*
+ * ntfs_read_named_stream - Read data from a named $DATA stream.
+ *
+ * @ni: NTFS inode
+ * @uname: Stream name
+ * @uname_len: Length of name in Unicode characters
+ * @offset: Byte offset in the stream
+ * @len: Number of bytes to read
+ * @data_buf: Buffer to copy data to
+ *
+ * Return: 0 on success, negative error code otherwise.
+ * If offset >= stream size, returns 0.
+ */
+static int ntfs_read_named_stream(struct ntfs_inode *ni, __le16 *uname,
+ u32 uname_len, u64 offset, u64 len, void *data_buf,
+ u64 *rbytes)
+{
+ struct ntfs_attr_search_ctx *ctx;
+ struct inode *attr_vi;
+ int err = 0;
+ u64 data_size;
+ s64 bytes;
+ char *rbuf = NULL;
+
+ if (!ni || !uname || uname_len == 0)
+ return -EINVAL;
+
+ if (len == 0 || len > NTFS_STREAM_MAX_IO)
+ return -EINVAL;
+
+ mutex_lock(&ni->mrec_lock);
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx) {
+ mutex_unlock(&ni->mrec_lock);
+ return -ENOMEM;
+ }
+
+ err = ntfs_attr_lookup(AT_DATA, uname, uname_len, CASE_SENSITIVE, 0,
+ NULL, 0, ctx);
+ if (err)
+ goto out_lock;
+
+ if (ctx->attr->non_resident)
+ data_size = le64_to_cpu(ctx->attr->data.non_resident.data_size);
+ else
+ data_size = le32_to_cpu(ctx->attr->data.resident.value_length);
+
+ if (offset >= data_size) {
+ *rbytes = 0;
+ /* EOF: nothing to read */
+ goto out_lock;
+ }
+
+ /* Adjust length to not read beyond EOF */
+ if (len > data_size - offset)
+ len = data_size - offset;
+
+ attr_vi = ntfs_attr_iget(VFS_I(ni), AT_DATA, uname, uname_len);
+ ntfs_attr_put_search_ctx(ctx);
+ mutex_unlock(&ni->mrec_lock);
+
+ if (IS_ERR(attr_vi)) {
+ err = PTR_ERR(attr_vi);
+ goto out_free;
+ }
+
+ rbuf = kvzalloc(len, GFP_NOFS);
+ if (!rbuf) {
+ err = -ENOMEM;
+ iput(attr_vi);
+ goto out_free;
+ }
+
+ bytes = ntfs_inode_attr_pread(attr_vi, offset, len, rbuf);
+ iput(attr_vi);
+ if (bytes < 0) {
+ err = bytes;
+ goto out_free;
+ }
+
+ *rbytes = bytes;
+ memcpy(data_buf, rbuf, bytes);
+
+out_free:
+ kvfree(rbuf);
+ return err;
+
+out_lock:
+ ntfs_attr_put_search_ctx(ctx);
+ mutex_unlock(&ni->mrec_lock);
+ return err;
+}
+
+/*
+ * ntfs_write_named_stream - Write data from a named $DATA stream.
+ *
+ * @ni: NTFS inode
+ * @uname: Stream name
+ * @uname_len: Length of name in Unicode characters
+ * @offset: Byte offset in the stream
+ * @len: Number of bytes to write
+ * @data_stream: Data to write
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+static int ntfs_write_named_stream(struct ntfs_inode *ni, __le16 *uname,
+ u32 uname_len, u64 offset, u64 len, void *data_stream,
+ u64 *wbytes)
+{
+ struct ntfs_attr_search_ctx *ctx;
+ struct inode *attr_vi;
+ int err;
+ s64 bytes;
+
+ if (!ni || !uname || uname_len == 0 || !data_stream)
+ return -EINVAL;
+
+ if (len == 0 || len > NTFS_STREAM_MAX_IO)
+ return -EINVAL;
+
+ mutex_lock(&ni->mrec_lock);
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ err = ntfs_attr_lookup(AT_DATA, uname, uname_len, CASE_SENSITIVE, 0,
+ NULL, 0, ctx);
+ if (err) {
+ if (err != -ENOENT)
+ goto out_unlock;
+ err = ntfs_attr_add(ni, AT_DATA, uname, uname_len, NULL, 0);
+ if (err)
+ goto out_unlock;
+ mark_mft_record_dirty(ni);
+ }
+
+ attr_vi = ntfs_attr_iget(VFS_I(ni), AT_DATA, uname, uname_len);
+ ntfs_attr_put_search_ctx(ctx);
+ mutex_unlock(&ni->mrec_lock);
+
+ if (IS_ERR(attr_vi)) {
+ err = PTR_ERR(attr_vi);
+ goto out;
+ }
+
+ bytes = ntfs_inode_attr_pwrite(attr_vi, offset, len, data_stream,
+ false);
+ if (bytes < 0)
+ err = bytes;
+ else
+ *wbytes = bytes;
+ iput(attr_vi);
+
+out:
+ return err;
+
+out_unlock:
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ mutex_unlock(&ni->mrec_lock);
+ return err;
+}
+
+/*
+ * ntfs_remove_named_stream - Remove a named $DATA stream.
+ *
+ * @ni: NTFS inode
+ * @uname: Stream name
+ * @uname_len: Name length in Unicode characters
+ *
+ * Return: 0 on success, negative error
+ * -ENOENT if stream not found
+ */
+static int ntfs_remove_named_stream(struct ntfs_inode *ni, __le16 *uname,
+ u32 uname_len)
+{
+ if (!ni || !uname || uname_len == 0)
+ return -EINVAL;
+
+ return ntfs_attr_remove(ni, AT_DATA, uname, uname_len);
+}
+
+enum ntfs_stream_op {
+ NTFS_STREAM_OP_READ,
+ NTFS_STREAM_OP_WRITE,
+ NTFS_STREAM_OP_REMOVE,
+};
+
+static long ntfs_ioctl_stream(struct file *filp, unsigned long arg,
+ enum ntfs_stream_op op)
+{
+ struct inode *inode = file_inode(filp);
+ struct ntfs_inode *ni = NTFS_I(inode);
+ struct ntfs_stream __user *ureq =
+ (struct ntfs_stream __user *)arg;
+ struct ntfs_stream *req;
+ size_t header_size = sizeof(struct ntfs_stream);
+ size_t data_size, total_size;
+ char *kname, *kdata;
+ __le16 *uname = NULL, *sname;
+ int err = 0, sname_len;
+ bool utf16;
+ struct ntfs_stream hdr;
+
+ /* First copy fixed header */
+ if (copy_from_user(&hdr, ureq, header_size))
+ return -EFAULT;
+
+ if (hdr.io_len > NTFS_STREAM_MAX_IO ||
+ (hdr.flags & ~NTFS_STREAM_FL_UTF16) || hdr.reserved)
+ return -EINVAL;
+
+ utf16 = hdr.flags & NTFS_STREAM_FL_UTF16;
+
+ if (hdr.name_len == 0)
+ return -EINVAL;
+ if (utf16) {
+ /* UTF-16LE byte count: must be even and within the limit. */
+ if ((hdr.name_len & 1) ||
+ hdr.name_len > NTFS_MAX_NAME_LEN * sizeof(__le16))
+ return -EINVAL;
+ } else if (hdr.name_len > NTFS_MAX_NAME_LEN) {
+ return -EINVAL;
+ }
+
+ switch (op) {
+ case NTFS_STREAM_OP_READ:
+ if (!(filp->f_mode & FMODE_READ) || hdr.io_len == 0)
+ return -EINVAL;
+ data_size = hdr.io_len;
+ break;
+ case NTFS_STREAM_OP_WRITE:
+ if (!(filp->f_mode & FMODE_WRITE) || hdr.io_len == 0)
+ return -EINVAL;
+ data_size = hdr.io_len;
+ break;
+ case NTFS_STREAM_OP_REMOVE:
+ if (!(filp->f_mode & FMODE_WRITE) || hdr.io_len != 0)
+ return -EINVAL;
+ data_size = 0;
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ if (check_add_overflow(header_size, (size_t)hdr.name_len, &total_size) ||
+ check_add_overflow(total_size, data_size, &total_size))
+ return -EOVERFLOW;
+
+ req = memdup_user(ureq, total_size);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ req->bytes_returned = 0;
+
+ kname = (char *)req->buffer;
+ kdata = (char *)req->buffer + req->name_len;
+
+ if (req->name_len != hdr.name_len || req->io_len != hdr.io_len ||
+ req->flags != hdr.flags || req->reserved) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ if (utf16) {
+ /* Name is already on-disk UTF-16LE; use it in place. */
+ sname = (__le16 *)kname;
+ sname_len = req->name_len / sizeof(__le16);
+ } else {
+ if (memchr(kname, '\0', req->name_len)) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ sname_len = ntfs_nlstoucs(ni->vol, kname, req->name_len,
+ &uname, NTFS_MAX_NAME_LEN);
+ if (sname_len < 0) {
+ err = sname_len;
+ goto out_free;
+ }
+ sname = uname;
+ }
+
+ switch (op) {
+ case NTFS_STREAM_OP_READ:
+ err = ntfs_read_named_stream(ni, sname, sname_len,
+ req->stream_offset, req->io_len,
+ kdata,
+ &req->bytes_returned);
+ if (!err) {
+ if (copy_to_user(ureq, req,
+ header_size + req->name_len + req->bytes_returned))
+ err = -EFAULT;
+ }
+ break;
+ case NTFS_STREAM_OP_WRITE:
+ err = mnt_want_write_file(filp);
+ if (err)
+ break;
+ err = ntfs_write_named_stream(ni, sname, sname_len,
+ req->stream_offset, req->io_len,
+ kdata,
+ &req->bytes_returned);
+ mnt_drop_write_file(filp);
+ if (!err) {
+ if (copy_to_user(&ureq->bytes_returned,
+ &req->bytes_returned,
+ sizeof(req->bytes_returned)))
+ err = -EFAULT;
+ }
+ break;
+ case NTFS_STREAM_OP_REMOVE:
+ err = mnt_want_write_file(filp);
+ if (err)
+ break;
+ err = ntfs_remove_named_stream(ni, sname, sname_len);
+ mnt_drop_write_file(filp);
+ break;
+ default:
+ err = -ENOTTY;
+ }
+
+out_free:
+ if (uname)
+ kmem_cache_free(ntfs_name_cache, uname);
+ kfree(req);
+ return err;
+}
+
+static int ntfs_ioctl_list_streams(struct file *filp, unsigned long arg)
+{
+ struct inode *inode = file_inode(filp);
+ struct ntfs_inode *ni = NTFS_I(inode);
+ struct ntfs_list_streams hdr;
+ struct ntfs_stream_entry *entry, *last_entry = NULL;
+ size_t required = 0;
+ void *kbuf = NULL;
+ void __user *ubuf;
+ struct attr_record *a;
+ struct ntfs_attr_search_ctx *actx;
+ int ret = 0, err, count = 0;
+ size_t entry_size, offset = 0;
+ unsigned int name_len;
+ unsigned char *sn;
+ bool utf16;
+
+ if (copy_from_user(&hdr, (void __user *)arg, sizeof(hdr)))
+ return -EFAULT;
+
+ if ((hdr.flags & ~NTFS_STREAM_FL_UTF16) || hdr.reserved)
+ return -EINVAL;
+
+ utf16 = hdr.flags & NTFS_STREAM_FL_UTF16;
+
+ ubuf = (void __user *)arg + sizeof(hdr);
+
+ mutex_lock(&ni->mrec_lock);
+ actx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!actx) {
+ mutex_unlock(&ni->mrec_lock);
+ return -ENOMEM;
+ }
+
+ while ((err = ntfs_attrs_walk(actx)) == 0) {
+ a = actx->attr;
+ if (a->type != AT_DATA || !a->name_length)
+ continue;
+
+ if (utf16) {
+ name_len = a->name_length * sizeof(__le16);
+ } else {
+ sn = ntfs_attr_name_get(ni->vol,
+ (__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)),
+ a->name_length);
+ if (!sn) {
+ ret = -EIO;
+ goto out;
+ }
+ name_len = strlen(sn);
+ ntfs_attr_name_free(&sn);
+ }
+
+ entry_size = sizeof(*entry) + name_len;
+ entry_size = ALIGN(entry_size, 8);
+
+ if (required > SIZE_MAX - entry_size) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+ required += entry_size;
+ count++;
+ }
+ if (err != -ENOENT) {
+ ret = err;
+ goto out;
+ }
+
+ hdr.stream_count = count;
+ hdr.bytes_returned = required;
+
+ if (!count)
+ goto out;
+
+ if (hdr.buffer_size < required) {
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ kbuf = kvzalloc(required, GFP_NOFS);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ntfs_attr_reinit_search_ctx(actx);
+ while ((err = ntfs_attrs_walk(actx)) == 0) {
+ a = actx->attr;
+ if (a->type != AT_DATA || !a->name_length)
+ continue;
+ if (utf16) {
+ sn = NULL;
+ name_len = a->name_length * sizeof(__le16);
+ } else {
+ sn = ntfs_attr_name_get(ni->vol,
+ (__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)),
+ a->name_length);
+ if (!sn) {
+ ret = -EIO;
+ goto out;
+ }
+ name_len = strlen(sn);
+ }
+
+ entry_size = sizeof(*entry) + name_len;
+ entry_size = ALIGN(entry_size, 8);
+ if (offset > required - entry_size) {
+ ret = -EOVERFLOW;
+ ntfs_attr_name_free(&sn);
+ goto out;
+ }
+
+ entry = (struct ntfs_stream_entry *)(kbuf + offset);
+
+ if (a->non_resident) {
+ entry->size = le64_to_cpu(a->data.non_resident.data_size);
+ entry->alloc_size =
+ le64_to_cpu(a->data.non_resident.allocated_size);
+
+ } else {
+ entry->size = le32_to_cpu(a->data.resident.value_length);
+ entry->alloc_size = le32_to_cpu(a->length) -
+ le16_to_cpu(a->data.resident.value_offset);
+ }
+
+ entry->name_len = name_len;
+ if (utf16)
+ memcpy(entry->name,
+ (u8 *)a + le16_to_cpu(a->name_offset), name_len);
+ else
+ memcpy(entry->name, sn, name_len);
+ ntfs_attr_name_free(&sn);
+
+ entry->next_entry_off = entry_size;
+ last_entry = entry;
+ offset += entry_size;
+ }
+ if (err != -ENOENT) {
+ ret = err;
+ } else {
+ /* The chain terminates at the last entry. */
+ if (last_entry)
+ last_entry->next_entry_off = 0;
+ hdr.bytes_returned = offset;
+ }
+
+out:
+ mutex_unlock(&ni->mrec_lock);
+ ntfs_attr_put_search_ctx(actx);
+
+ if (ret && ret != -ENOSPC)
+ goto out_free;
+
+ if (!ret && kbuf && copy_to_user(ubuf, kbuf, hdr.bytes_returned)) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ if (copy_to_user((void __user *)arg, &hdr, sizeof(hdr)))
+ ret = -EFAULT;
+
+out_free:
+ kvfree(kbuf);
+ return ret;
+}
+
long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
@@ -850,6 +1351,14 @@ long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return ntfs_ioctl_set_volume_label(filp, arg);
case FITRIM:
return ntfs_ioctl_fitrim(NTFS_SB(file_inode(filp)->i_sb), arg);
+ case NTFS_IOC_STREAM_READ:
+ return ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_READ);
+ case NTFS_IOC_STREAM_WRITE:
+ return ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_WRITE);
+ case NTFS_IOC_STREAM_REMOVE:
+ return ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_REMOVE);
+ case NTFS_IOC_LIST_STREAMS:
+ return ntfs_ioctl_list_streams(filp, arg);
default:
return -ENOTTY;
}
diff --git a/include/uapi/linux/ntfs.h b/include/uapi/linux/ntfs.h
new file mode 100644
index 0000000000000..a5783dc3b9054
--- /dev/null
+++ b/include/uapi/linux/ntfs.h
@@ -0,0 +1,115 @@
+/* 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 <linux/types.h>
+#include <linux/ioctl.h>
+
+#define NTFS_IOC_MAGIC 0xEF
+
+/*
+ * Flags for ntfs_stream.flags and ntfs_list_streams.flags.
+ *
+ * The stream name is raw UTF-16LE, exactly as stored on disk, instead of
+ * being encoded with the mounted filesystem NLS. This allows lossless
+ * round-tripping of stream names whose characters are not representable
+ * in 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 ioctl structure.
+ *
+ * @stream_offset: Offset within the named stream.
+ * @io_len: Number of bytes to read/write.
+ * @bytes_returned: Actual bytes transferred (out).
+ * @name_len: Stream name length in bytes, not including any
+ * terminating NUL (which must not be present). When
+ * NTFS_STREAM_FL_UTF16 is set this is a UTF-16LE byte
+ * count and must be even.
+ * @flags: Bit mask of NTFS_STREAM_FL_* flags. other bits must be
+ * zero.
+ * @reserved: Must be zero.
+ * @buffer: Stream name followed by stream data.
+ *
+ * The stream name is the bare stream name, without the leading ':' or the
+ * trailing ":$DATA" decoration used by Windows. By default it is encoded with
+ * the mounted filesystem NLS. If NTFS_STREAM_FL_UTF16 is set in @flags it is
+ * raw UTF-16LE instead. For read and write, @io_len bytes of data follow the
+ * name. For remove, only the name is required and @io_len must be zero.
+ */
+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[]; /* name + data */
+};
+
+/*
+ * 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, so the entry can grow
+ * new fields without breaking the ABI.
+ * @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.
+ * @reserved: Must be zero.
+ * @name: Bare stream name; NLS encoded, or raw UTF-16LE when
+ * NTFS_STREAM_FL_UTF16 was requested (see struct
+ * ntfs_stream).
+ */
+struct ntfs_stream_entry {
+ __aligned_u64 next_entry_off;
+ __aligned_u64 size;
+ __aligned_u64 alloc_size;
+ __u32 name_len;
+ __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.
+ */
+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 */
|
| 3/2 | 2026/07/30 17:11 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9471 output: 31 thoughts: 189 Thoughts: **Analyzing the NTFS Named Stream Patch** Okay, so this patch is introducing ioctls for NTFS, specifically handling named streams or Alternate Data Streams. I see we have `NTFS_IOC_STREAM_READ`, `NTFS_IOC_STREAM_WRITE`, `NTFS_IOC_STREAM_REMOVE`, and `NTFS_IOC_LIST_STREAMS`. That's standard functionality, really; it covers the basic read, write, remove, and listing operations. Good. The important files are `fs/ntfs/file.c` which, as an expert, I would expect handles a lot of the core file operations, and a new header, `include/uapi/linux/ntfs.h` which will define the new ioctl numbers and structures. Makes sense. But wait a minute… This is `fs/ntfs/file.c`… Is this the *old* NTFS driver or the newer `ntfs3`? I need to clarify that to understand if this is going into a legacy code path or something more modern. I'm going to quickly check if the old `fs/ntfs` is still even enabled, and how it’s configured. I'll need to use `grepper` to search through the Kconfig files for `NTFS_FS` or a similar configuration symbol. That should clear things up fast. |
| 4/2 | 2026/07/30 17:11 | tool | grepper |
0mArgs:{
"Expression": "config NTFS_FS",
"PathPrefix": "fs/ntfs/Kconfig"
}Results: {
"Output": "fs/ntfs/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/ntfs/Kconfig:2:config NTFS_FS\nfs/ntfs/Kconfig-3-\ttristate \"NTFS file system support\"\n--\nfs/ntfs/Kconfig=13=config NTFS_DEBUG\n--\nfs/ntfs/Kconfig-34-\nfs/ntfs/Kconfig:35:config NTFS_FS_POSIX_ACL\nfs/ntfs/Kconfig-36-\tbool \"NTFS POSIX Access Control Lists\"\n"
} |
| 5/2 | 2026/07/30 17:11 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10129 output: 32 thoughts: 156 Thoughts: **Analyzing the NTFS Driver Patch** Okay, so this patch is working with the *old* NTFS driver, specifically `fs/ntfs`. I need to remember that this legacy driver is still around. It looks like the core of the change lies in adding new ioctls to `fs/ntfs/file.c`. Let's see what those new ioctls are... right, the patch adds: * `ntfs_read_named_stream` * `ntfs_write_named_stream` * `ntfs_remove_named_stream` * `ntfs_ioctl_stream` * `ntfs_ioctl_list_streams` Now, the important question is: are these actually *reachable*? Ah, yes, they are, via `ntfs_ioctl`. That function would be the `.unlocked_ioctl` entry point for NTFS files, which makes sense. Just to be absolutely sure, I should verify the implementation of `ntfs_ioctl` itself in `fs/ntfs/file.c`. Gotta be thorough with these driver changes, especially with something as fundamental as file system I/O. |
| 6/2 | 2026/07/30 17:11 | tool | grepper |
0mArgs:{
"Expression": "ntfs_ioctl",
"PathPrefix": "fs/ntfs/file.c"
}Results: {
"Output": "fs/ntfs/file.c=740=static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,\n--\nfs/ntfs/file.c-748-\nfs/ntfs/file.c:749:static int ntfs_ioctl_shutdown(struct super_block *sb, unsigned long arg)\nfs/ntfs/file.c-750-{\n--\nfs/ntfs/file.c-761-\nfs/ntfs/file.c:762:static int ntfs_ioctl_get_volume_label(struct file *filp, unsigned long arg)\nfs/ntfs/file.c-763-{\n--\nfs/ntfs/file.c-784-\nfs/ntfs/file.c:785:static int ntfs_ioctl_set_volume_label(struct file *filp, unsigned long arg)\nfs/ntfs/file.c-786-{\n--\nfs/ntfs/file.c-808-\nfs/ntfs/file.c:809:static int ntfs_ioctl_fitrim(struct ntfs_volume *vol, unsigned long arg)\nfs/ntfs/file.c-810-{\n--\nfs/ntfs/file.c=1031=enum ntfs_stream_op {\n--\nfs/ntfs/file.c-1036-\nfs/ntfs/file.c:1037:static long ntfs_ioctl_stream(struct file *filp, unsigned long arg,\nfs/ntfs/file.c-1038-\t\t\t enum ntfs_stream_op op)\n--\nfs/ntfs/file.c-1176-\nfs/ntfs/file.c:1177:static int ntfs_ioctl_list_streams(struct file *filp, unsigned long arg)\nfs/ntfs/file.c-1178-{\n--\nfs/ntfs/file.c-1342-\nfs/ntfs/file.c:1343:long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)\nfs/ntfs/file.c-1344-{\n--\nfs/ntfs/file.c-1346-\tcase FS_IOC_SHUTDOWN:\nfs/ntfs/file.c:1347:\t\treturn ntfs_ioctl_shutdown(file_inode(filp)-\u003ei_sb, arg);\nfs/ntfs/file.c-1348-\tcase FS_IOC_GETFSLABEL:\nfs/ntfs/file.c:1349:\t\treturn ntfs_ioctl_get_volume_label(filp, arg);\nfs/ntfs/file.c-1350-\tcase FS_IOC_SETFSLABEL:\nfs/ntfs/file.c:1351:\t\treturn ntfs_ioctl_set_volume_label(filp, arg);\nfs/ntfs/file.c-1352-\tcase FITRIM:\nfs/ntfs/file.c:1353:\t\treturn ntfs_ioctl_fitrim(NTFS_SB(file_inode(filp)-\u003ei_sb), arg);\nfs/ntfs/file.c-1354-\tcase NTFS_IOC_STREAM_READ:\nfs/ntfs/file.c:1355:\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_READ);\nfs/ntfs/file.c-1356-\tcase NTFS_IOC_STREAM_WRITE:\nfs/ntfs/file.c:1357:\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_WRITE);\nfs/ntfs/file.c-1358-\tcase NTFS_IOC_STREAM_REMOVE:\nfs/ntfs/file.c:1359:\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_REMOVE);\nfs/ntfs/file.c-1360-\tcase NTFS_IOC_LIST_STREAMS:\nfs/ntfs/file.c:1361:\t\treturn ntfs_ioctl_list_streams(filp, arg);\nfs/ntfs/file.c-1362-\tdefault:\n--\nfs/ntfs/file.c=1368=long ntfs_compat_ioctl(struct file *filp, unsigned int cmd,\n--\nfs/ntfs/file.c-1370-{\nfs/ntfs/file.c:1371:\treturn ntfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));\nfs/ntfs/file.c-1372-}\n--\nfs/ntfs/file.c=1684=const struct file_operations ntfs_file_ops = {\n--\nfs/ntfs/file.c-1693-\t.splice_write\t= iter_file_splice_write,\nfs/ntfs/file.c:1694:\t.unlocked_ioctl\t= ntfs_ioctl,\nfs/ntfs/file.c-1695-#ifdef CONFIG_COMPAT\n"
} |
| 7/2 | 2026/07/30 17:11 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11841 output: 31 thoughts: 360 Thoughts: **Analyzing NTFS IOCTLs** Okay, so I'm digging into these NTFS IOCTLs. First thought is, yeah, the IOCTL entry points *are* definitely accessible, so let's start there. Right, `ntfs_ioctl` is the initial point of entry. Now, which functions are the key players in this area? I think it boils down to: `ntfs_ioctl_stream`, `ntfs_ioctl_list_streams`, `ntfs_read_named_stream`, `ntfs_write_named_stream`, and `ntfs_remove_named_stream`. Wait a minute... both `ntfs_ioctl_stream` and `ntfs_ioctl_list_streams` are static functions, while `ntfs_ioctl` itself isn't. I'm going to have to see if `ntfs_ioctl` is exported as a symbol or if it's only called within `ntfs_file_ops`. Alright, it's used within `ntfs_file_ops`. I should be able to specify `ntfs_ioctl` as a focus symbol. Can I specify `ntfs_ioctl_stream` directly? Since it's static, it might be inlined during compilation, meaning it might not have its own symbol. Actually, wait a minute, `ntfs_ioctl_stream` is a fairly sizable function. It might not be inlined. But to be safe, I'm going to include `ntfs_ioctl` along with it. Ah, and those other stream functions – `ntfs_read_named_stream`, `ntfs_write_named_stream`, and `ntfs_remove_named_stream` – those are also static. Let me just confirm that. Yep, they are. So really, my focus should be on `ntfs_ioctl` then. That's the key entry point. Okay, let's switch gears for a moment and see which config option enables the NTFS driver. It's the `NTFS_FS` config. Wait, does `NTFS_RW` exist? It's time to check the `fs/ntfs/Kconfig` file. |
| 8/2 | 2026/07/30 17:11 | tool | grepper |
0mError:no matches Args: {
"Expression": "config NTFS_RW",
"PathPrefix": "fs/ntfs/Kconfig"
}Results: {
"error": "no matches"
} |
| 9/2 | 2026/07/30 17:11 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12715 output: 30 thoughts: 24 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Investigating NTFS Write Support Configuration** Alright, let's dive in. Given the task at hand, my first instinct is to zero in on the configuration options for NTFS within the kernel. Specifically, I'm thinking about write support. Where would that be defined? My gut tells me `fs/ntfs/Kconfig` is the likely place. It's the standard location for filesystem-specific configuration. Time to check if the requisite options for read/write functionality are exposed, along with any relevant dependencies or potential caveats. It's likely that write support, if present, might be contingent on certain kernel features or external libraries. This initial exploration will guide my next steps. |
| 10/2 | 2026/07/30 17:11 | tool | grepper |
0mArgs:{
"Expression": "config NTFS_",
"PathPrefix": "fs/ntfs/Kconfig"
}Results: {
"Output": "fs/ntfs/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/ntfs/Kconfig:2:config NTFS_FS\nfs/ntfs/Kconfig-3-\ttristate \"NTFS file system support\"\n--\nfs/ntfs/Kconfig-12-\nfs/ntfs/Kconfig:13:config NTFS_DEBUG\nfs/ntfs/Kconfig-14-\tbool \"NTFS debugging support\"\n--\nfs/ntfs/Kconfig-34-\nfs/ntfs/Kconfig:35:config NTFS_FS_POSIX_ACL\nfs/ntfs/Kconfig-36-\tbool \"NTFS POSIX Access Control Lists\"\n"
} |
| 11/2 | 2026/07/30 17:11 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13109 output: 29 thoughts: 45 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Examining NTFS Write Support** Alright, hang on a second... `NTFS_RW` is pointing to the *new* `fs/ntfs3` driver, not the legacy `fs/ntfs`? Interesting. I need to quickly confirm something about write capabilities here. Is write support for the older `fs/ntfs` driver enabled by default, or is it gated behind a separate configuration option, like `CONFIG_NTFS_WRITE`? I need to check the kernel config options to get a proper handle on the current landscape. This is likely due to the need for a stable and more feature-rich solution with ntfs3. Knowing the write support status of both drivers will inform the future direction. |
| 12/2 | 2026/07/30 17:11 | tool | grepper |
0mError:no matches Args: {
"Expression": "config NTFS_RW",
"PathPrefix": "fs/ntfs/"
}Results: {
"error": "no matches"
} |
| 13/2 | 2026/07/30 17:11 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13366 output: 28 thoughts: 58 Thoughts: **Examining the NTFS Configuration** Okay, so I'm looking into the NTFS file system configuration and, based on what I see in `fs/ntfs/Kconfig`, it seems pretty limited. I'm only seeing options for `NTFS_FS`, `NTFS_DEBUG`, and `NTFS_FS_POSIX_ACL`. That feels sparse, right? I was expecting more granular control here. Now, I need to confirm whether read-write capabilities are explicitly configured. Let me quickly double-check that `fs/ntfs/Kconfig` for the `NTFS_RW` option. I can't be sure, but I feel like I'm missing something important. |
| 14/2 | 2026/07/30 17:12 | tool | grepper |
0mError:no matches Args: {
"Expression": "RW",
"PathPrefix": "fs/ntfs/Kconfig"
}Results: {
"error": "no matches"
} |
| 15/2 | 2026/07/30 17:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13614 output: 33 thoughts: 51 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person: **Navigating the NTFS Landscape** Right, so I'm digging into the NTFS driver situation, trying to figure out some configuration options. I remember seeing something about read-write capabilities... and initially, I was looking at the old `fs/ntfs/Kconfig`, but *that's* not quite right. Hmm, no `NTFS_RW` defined there. Duh, of course! That's legacy code. I need to be in the new `ntfs3` driver now. Let's pivot and check `fs/ntfs3/Kconfig` instead. That's where the relevant configuration options *should* reside, and where I should find the settings for read-write support, now that the rewrite is fully integrated. |
| 16/2 | 2026/07/30 17:12 | tool | grepper |
0mArgs:{
"Expression": "config NTFS3_FS",
"PathPrefix": "fs/ntfs3/Kconfig"
}Results: {
"Output": "fs/ntfs3/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/ntfs3/Kconfig:2:config NTFS3_FS\nfs/ntfs3/Kconfig-3-\ttristate \"NTFS Read-Write file system support\"\n--\nfs/ntfs3/Kconfig=28=config NTFS3_LZX_XPRESS\n--\nfs/ntfs3/Kconfig-37-\nfs/ntfs3/Kconfig:38:config NTFS3_FS_POSIX_ACL\nfs/ntfs3/Kconfig-39-\tbool \"NTFS POSIX Access Control Lists\"\n"
} |
| 17/2 | 2026/07/30 17:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14045 output: 28 thoughts: 35 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating the NTFS Patch** Alright, the patch landed in `fs/ntfs/file.c`. That's a key file, but I need a quick sanity check before diving deep. My initial thought is to confirm where write support exists, let's make sure that that is present within `fs/ntfs/Kconfig`. |
| 18/2 | 2026/07/30 17:12 | tool | grepper |
0mError:no matches Args: {
"Expression": "write",
"PathPrefix": "fs/ntfs/Kconfig"
}Results: {
"error": "no matches"
} |
| 19/2 | 2026/07/30 17:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14216 output: 34 thoughts: 27 Thoughts: Here's my thought process, summarized as you requested: **Kconfig File Examination** Hmm, strange. I was expecting to find something related to "write" functionality in `fs/ntfs/Kconfig`, but a quick scan suggests it's not present. Okay, I need to be thorough. I can't just assume I'm right, even with my experience. I'll need to go back and methodically check the entire file to make absolutely sure the keyword isn't used. I don't want to miss anything subtle hidden within a dependency statement or a comment. Gotta be meticulous here. This Kconfig file is a crucial element of filesystem configuration, and missing a relevant configuration option can introduce issues down the line, so careful inspection is paramount. I'll take my time and make sure I haven't missed it. |
| 20/2 | 2026/07/30 17:12 | tool | read-file |
0mArgs:{
"File": "fs/ntfs/Kconfig",
"FirstLine": 1,
"LineCount": 100
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\tconfig NTFS_FS\n 3:\t\ttristate \"NTFS file system support\"\n 4:\t\tselect NLS\n 5:\t\tselect FS_IOMAP\n 6:\t\thelp\n 7:\t\t NTFS is the file system of Microsoft Windows NT, 2000, XP and 2003.\n 8:\t\t This allows you to mount devices formatted with the ntfs file system.\n 9:\t\n 10:\t\t To compile this as a module, choose M here: the module will be called\n 11:\t\t ntfs.\n 12:\t\n 13:\tconfig NTFS_DEBUG\n 14:\t\tbool \"NTFS debugging support\"\n 15:\t\tdepends on NTFS_FS\n 16:\t\thelp\n 17:\t\t If you are experiencing any problems with the NTFS file system, say\n 18:\t\t Y here. This will result in additional consistency checks to be\n 19:\t\t performed by the driver as well as additional debugging messages to\n 20:\t\t be written to the system log. Note that debugging messages are\n 21:\t\t disabled by default. To enable them, supply the option debug_msgs=1\n 22:\t\t at the kernel command line when booting the kernel or as an option\n 23:\t\t to insmod when loading the ntfs module. Once the driver is active,\n 24:\t\t you can enable debugging messages by doing (as root):\n 25:\t\t echo 1 \u003e /proc/sys/fs/ntfs-debug\n 26:\t\t Replacing the \"1\" with \"0\" would disable debug messages.\n 27:\t\n 28:\t\t If you leave debugging messages disabled, this results in little\n 29:\t\t overhead, but enabling debug messages results in very significant\n 30:\t\t slowdown of the system.\n 31:\t\n 32:\t\t When reporting bugs, please try to have available a full dump of\n 33:\t\t debugging messages while the misbehaviour was occurring.\n 34:\t\n 35:\tconfig NTFS_FS_POSIX_ACL\n 36:\t\tbool \"NTFS POSIX Access Control Lists\"\n 37:\t\tdepends on NTFS_FS\n 38:\t\tselect FS_POSIX_ACL\n 39:\t\thelp\n 40:\t\t POSIX Access Control Lists (ACLs) support additional access rights\n 41:\t\t for users and groups beyond the standard owner/group/world scheme.\n 42:\t\n 43:\t\t This option enables ACL support for ntfs, providing functional parity\n 44:\t\t with ntfs3 drivier.\n 45:\t\n 46:\t\t NOTE: this is linux only feature. Windows will ignore these ACLs.\n 47:\t\n 48:\t\t If you don't know what Access Control Lists are, say N.\n"
} |
| 21/2 | 2026/07/30 17:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15175 output: 33 thoughts: 54 Thoughts: Okay, here's my take on those thoughts, framed as my own mental process: **Diving into the NTFS Landscape** Hmm, interesting. It seems like `fs/ntfs` has write support enabled right out of the box now. That's a departure from how things used to be. I seem to recall that in earlier kernels, the NTFS filesystem module had a `NTFS_RW` flag...right? Was it dropped, or perhaps merged into something else? It's been a while since I had to really dig into this particular bit of kernel code. Best way to figure out what's changed is to go directly to the source. Let me take a look at the `fs/ntfs/Makefile` and see if the clues are in there. |
| 22/2 | 2026/07/30 17:12 | tool | read-file |
0mArgs:{
"File": "fs/ntfs/Makefile",
"FirstLine": 1,
"LineCount": 100
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t\n 3:\tobj-$(CONFIG_NTFS_FS) += ntfs.o\n 4:\t\n 5:\tntfs-y := aops.o attrib.o collate.o dir.o file.o index.o inode.o \\\n 6:\t\t mft.o mst.o namei.o runlist.o super.o unistr.o attrlist.o ea.o \\\n 7:\t\t upcase.o bitmap.o lcnalloc.o logfile.o reparse.o compress.o \\\n 8:\t\t iomap.o debug.o sysctl.o object_id.o bdev-io.o\n 9:\t\n 10:\tccflags-$(CONFIG_NTFS_DEBUG) += -DDEBUG\n"
} |
| 23/2 | 2026/07/30 17:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15640 output: 29 thoughts: 254 Thoughts: **Fuzzing NTFS: A Focused Approach** Alright, it looks like `NTFS_FS` is the only configuration option I need to consider for this. Straightforward enough. But hold on, this patch is introducing ioctls within `fs/ntfs/file.c`. Let's see... we've got `NTFS_IOC_STREAM_READ`, `NTFS_IOC_STREAM_WRITE`, `NTFS_IOC_STREAM_REMOVE`, and `NTFS_IOC_LIST_STREAMS`. Interesting. These definitely warrant a closer look with fuzzing. My primary focus here will be `ntfs_ioctl`. It appears to be the entry point for handling these new ioctls. Let me double-check the code diff quickly to confirm there aren't any other non-static functions. Okay, `ntfs_read_named_stream`, `ntfs_write_named_stream`, `ntfs_remove_named_stream`, `ntfs_ioctl_stream`, and `ntfs_ioctl_list_streams` are all static helpers. Just `ntfs_ioctl` is non-static. Now, let's just make sure `ntfs_ioctl` is actually exported and used outside of `fs/ntfs/file.c`. This is where the real fuzzing potential lies. |
| 24/2 | 2026/07/30 17:12 | tool | grepper |
0mArgs:{
"Expression": "ntfs_ioctl",
"PathPrefix": "fs/ntfs/"
}Results: {
"Output": "fs/ntfs/dir.c=1195=const struct file_operations ntfs_dir_ops = {\n--\nfs/ntfs/dir.c-1201-\t.release\t= ntfs_dir_release,\nfs/ntfs/dir.c:1202:\t.unlocked_ioctl\t= ntfs_ioctl,\nfs/ntfs/dir.c-1203-#ifdef CONFIG_COMPAT\n--\nfs/ntfs/file.c=740=static ssize_t ntfs_file_splice_read(struct file *in, loff_t *ppos,\n--\nfs/ntfs/file.c-748-\nfs/ntfs/file.c:749:static int ntfs_ioctl_shutdown(struct super_block *sb, unsigned long arg)\nfs/ntfs/file.c-750-{\n--\nfs/ntfs/file.c-761-\nfs/ntfs/file.c:762:static int ntfs_ioctl_get_volume_label(struct file *filp, unsigned long arg)\nfs/ntfs/file.c-763-{\n--\nfs/ntfs/file.c-784-\nfs/ntfs/file.c:785:static int ntfs_ioctl_set_volume_label(struct file *filp, unsigned long arg)\nfs/ntfs/file.c-786-{\n--\nfs/ntfs/file.c-808-\nfs/ntfs/file.c:809:static int ntfs_ioctl_fitrim(struct ntfs_volume *vol, unsigned long arg)\nfs/ntfs/file.c-810-{\n--\nfs/ntfs/file.c=1031=enum ntfs_stream_op {\n--\nfs/ntfs/file.c-1036-\nfs/ntfs/file.c:1037:static long ntfs_ioctl_stream(struct file *filp, unsigned long arg,\nfs/ntfs/file.c-1038-\t\t\t enum ntfs_stream_op op)\n--\nfs/ntfs/file.c-1176-\nfs/ntfs/file.c:1177:static int ntfs_ioctl_list_streams(struct file *filp, unsigned long arg)\nfs/ntfs/file.c-1178-{\n--\nfs/ntfs/file.c-1342-\nfs/ntfs/file.c:1343:long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)\nfs/ntfs/file.c-1344-{\n--\nfs/ntfs/file.c-1346-\tcase FS_IOC_SHUTDOWN:\nfs/ntfs/file.c:1347:\t\treturn ntfs_ioctl_shutdown(file_inode(filp)-\u003ei_sb, arg);\nfs/ntfs/file.c-1348-\tcase FS_IOC_GETFSLABEL:\nfs/ntfs/file.c:1349:\t\treturn ntfs_ioctl_get_volume_label(filp, arg);\nfs/ntfs/file.c-1350-\tcase FS_IOC_SETFSLABEL:\nfs/ntfs/file.c:1351:\t\treturn ntfs_ioctl_set_volume_label(filp, arg);\nfs/ntfs/file.c-1352-\tcase FITRIM:\nfs/ntfs/file.c:1353:\t\treturn ntfs_ioctl_fitrim(NTFS_SB(file_inode(filp)-\u003ei_sb), arg);\nfs/ntfs/file.c-1354-\tcase NTFS_IOC_STREAM_READ:\nfs/ntfs/file.c:1355:\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_READ);\nfs/ntfs/file.c-1356-\tcase NTFS_IOC_STREAM_WRITE:\nfs/ntfs/file.c:1357:\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_WRITE);\nfs/ntfs/file.c-1358-\tcase NTFS_IOC_STREAM_REMOVE:\nfs/ntfs/file.c:1359:\t\treturn ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_REMOVE);\nfs/ntfs/file.c-1360-\tcase NTFS_IOC_LIST_STREAMS:\nfs/ntfs/file.c:1361:\t\treturn ntfs_ioctl_list_streams(filp, arg);\nfs/ntfs/file.c-1362-\tdefault:\n--\nfs/ntfs/file.c=1368=long ntfs_compat_ioctl(struct file *filp, unsigned int cmd,\n--\nfs/ntfs/file.c-1370-{\nfs/ntfs/file.c:1371:\treturn ntfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));\nfs/ntfs/file.c-1372-}\n--\nfs/ntfs/file.c=1684=const struct file_operations ntfs_file_ops = {\n--\nfs/ntfs/file.c-1693-\t.splice_write\t= iter_file_splice_write,\nfs/ntfs/file.c:1694:\t.unlocked_ioctl\t= ntfs_ioctl,\nfs/ntfs/file.c-1695-#ifdef CONFIG_COMPAT\n--\nfs/ntfs/ntfs.h=253=int ntfs_force_shutdown(struct super_block *sb, u32 flags);\nfs/ntfs/ntfs.h:254:long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);\nfs/ntfs/ntfs.h-255-#ifdef CONFIG_COMPAT\n"
} |
| 25/2 | 2026/07/30 17:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17681 output: 83 thoughts: 951 Thoughts: **Analysis of NTFS Ioctl Patch for Fuzzing**
Okay, let's break this down for fuzzing. `ntfs_ioctl` immediately jumps out as the central point here. It's non-static and already utilized within `ntfs_dir_ops` and `ntfs_file_ops`, so it's clearly the right symbol to focus on.
First, I quickly confirmed that `fs/ntfs` is still present in the kernel, and the patch, which introduces the new ioctls, was indeed applied to `fs/ntfs/file.c`. This patch introduces functionality for named streams, adding `NTFS_IOC_STREAM_READ`, `NTFS_IOC_STREAM_WRITE`, `NTFS_IOC_STREAM_REMOVE`, and `NTFS_IOC_LIST_STREAMS`, as well as a corresponding `uapi/linux/ntfs.h` header.
I checked the configuration options, and `NTFS_FS` is the only one required. No dependence on `NTFS_FS_POSIX_ACL` or any other obscure settings. This simplifies things.
A quick review of the new functions added showed that they are all static, except for `ntfs_ioctl`, which is a key modification to handle the new ioctls.
Let me take another look at the patch itself to confirm my understanding:
```c
+static int ntfs_read_named_stream(struct ntfs_inode *ni, __le16 *uname,
+ u32 uname_len, u64 offset, u64 len, void *data_buf,
+ u64 *rbytes)
...
+static int ntfs_write_named_stream(struct ntfs_inode *ni, __le16 *uname,
+ u32 uname_len, u64 offset, u64 len, void *data_stream,
+ u64 *wbytes)
...
+static int ntfs_remove_named_stream(struct ntfs_inode *ni, __le16 *uname,
+ u32 uname_len)
...
+static long ntfs_ioctl_stream(struct file *filp, unsigned long arg,
+ enum ntfs_stream_op op)
...
+static int ntfs_ioctl_list_streams(struct file *filp, unsigned long arg)
...
long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
@@ -850,6 +1351,14 @@ long ntfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return ntfs_ioctl_set_volume_label(filp, arg);
case FITRIM:
return ntfs_ioctl_fitrim(NTFS_SB(file_inode(filp)->i_sb), arg);
+ case NTFS_IOC_STREAM_READ:
+ return ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_READ);
+ case NTFS_IOC_STREAM_WRITE:
+ return ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_WRITE);
+ case NTFS_IOC_STREAM_REMOVE:
+ return ntfs_ioctl_stream(filp, arg, NTFS_STREAM_OP_REMOVE);
+ case NTFS_IOC_LIST_STREAMS:
+ return ntfs_ioctl_list_streams(filp, arg);
default:
return -ENOTTY;
}
```
Indeed, the patch is functional and adds functionality related to named streams (Alternate Data Streams) to NTFS, all reachable through `ntfs_ioctl`. It's clearly a prime target for fuzzing.
My conclusion is:
Configs: `NTFS_FS`
Focus Symbol: `ntfs_ioctl`
Reasoning: The patch adds new ioctls to the NTFS filesystem to read, write, remove, and list named streams. These ioctls are all handled within `ntfs_ioctl`. `NTFS_FS` is the only config needed.
I'll now provide the relevant results.
|
| 26/2 | 2026/07/30 17:12 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"NTFS_FS"
],
"FocusSymbols": [
"ntfs_ioctl"
],
"Reasoning": "The patch adds new ioctls to the NTFS filesystem to read, write, remove, and list named streams (Alternate Data Streams). The ioctls are reachable via `ntfs_ioctl`.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"NTFS_FS"
],
"FocusSymbols": [
"ntfs_ioctl"
],
"Reasoning": "The patch adds new ioctls to the NTFS filesystem to read, write, remove, and list named streams (Alternate Data Streams). The ioctls are reachable via `ntfs_ioctl`.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|