Re: [PATCH] fs/ntfs3: reject FS_IOC_SETFSLABEL on readonly mounts

liubaolin <[email protected]> Tue, 30 Jun 2026 09:07:10 +0800
Newsgroups dev.linux.lists.ntfs3,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Dear maintainer,

This patch fixes how `ntfs3` handles `FS_IOC_SETFSLABEL` on readonly mounts.

While looking at this ioctl path, I noticed that on a readonly-mounted 
`ntfs3` filesystem, users can still call `FS_IOC_SETFSLABEL` to change 
the volume label, and the operation returns success. If the label is 
read again immediately afterwards, the current mount session shows the 
new value, but that value is not actually written to disk. After 
unmounting and mounting the filesystem again, the label goes back to the 
old one.

For a readonly mount, this operation should not be allowed in the first 
place, so the more reasonable behavior here is to return `-EROFS` directly.



To reproduce the issue, I wrote a simple test program, 
`ntfs3_label_ioctl.c`, which uses `FS_IOC_GETFSLABEL` and 
`FS_IOC_SETFSLABEL` directly to read and write the volume label. Running 
this program against a readonly-mounted `ntfs3` image makes the problem 
easy to observe.
(The source code for ntfs3_label_ioctl.c is listed at the end of the 
email.)

*********************************************************************************************************

First, build the test program:
gcc -O2 -Wall -o ntfs3_label_ioctl ntfs3_label_ioctl.c

Then prepare an NTFS image:
truncate -s 128M /tmp/ntfs3.img
mkntfs -F -q /tmp/ntfs3.img
mkdir -p /tmp/ntfs3-mnt

Mount it read-write once and explicitly set the label to `OLDLABEL`:
sudo mount -o loop -t ntfs3 /tmp/ntfs3.img /tmp/ntfs3-mnt
./ntfs3_label_ioctl set OLDLABEL /tmp/ntfs3-mnt
./ntfs3_label_ioctl get /tmp/ntfs3-mnt
sudo umount /tmp/ntfs3-mnt

At this point, the output should be:
$ ./ntfs3_label_ioctl set OLDLABEL /tmp/ntfs3-mnt
SETFSLABEL succeeded

$ ./ntfs3_label_ioctl get /tmp/ntfs3-mnt
label='OLDLABEL'

Then mount the image readonly:
sudo mount -o loop,ro -t ntfs3 /tmp/ntfs3.img /tmp/ntfs3-mnt

Read the current label:
./ntfs3_label_ioctl get /tmp/ntfs3-mnt

Try to set a new label:
./ntfs3_label_ioctl set NEWLABEL /tmp/ntfs3-mnt

Read the label again:
./ntfs3_label_ioctl get /tmp/ntfs3-mnt

Finally, unmount and mount it readonly again, then read the label once more:
sudo umount /tmp/ntfs3-mnt
sudo mount -o loop,ro -t ntfs3 /tmp/ntfs3.img /tmp/ntfs3-mnt
./ntfs3_label_ioctl get /tmp/ntfs3-mnt
**********************************************************************************************************

On the unpatched kernel, I see the following behavior:

$ ./ntfs3_label_ioctl get /tmp/ntfs3-mnt
label='OLDLABEL'

$ ./ntfs3_label_ioctl set NEWLABEL /tmp/ntfs3-mnt
SETFSLABEL succeeded

$ ./ntfs3_label_ioctl get /tmp/ntfs3-mnt
label='NEWLABEL'

$ sudo umount /tmp/ntfs3-mnt
$ sudo mount -o loop,ro -t ntfs3 /tmp/ntfs3.img /tmp/ntfs3-mnt
$ ./ntfs3_label_ioctl get /tmp/ntfs3-mnt
label='OLDLABEL'

So `SETFSLABEL` succeeds on a readonly mount, and the label visible in 
the current mount session changes to `NEWLABEL`, but after remounting it 
goes back to `OLDLABEL`. This shows that the change was not actually 
persisted to disk, and only affected the current mount session.
**********************************************************************************************************

With this patch applied, the same test behaves like this:

$ ./ntfs3_label_ioctl get /tmp/ntfs3-mnt
label='OLDLABEL'

$ ./ntfs3_label_ioctl set NEWLABEL /tmp/ntfs3-mnt
SETFSLABEL failed: errno=30 (Read-only file system)

$ ./ntfs3_label_ioctl get /tmp/ntfs3-mnt
label='OLDLABEL'

$ sudo umount /tmp/ntfs3-mnt
$ sudo mount -o loop,ro -t ntfs3 /tmp/ntfs3.img /tmp/ntfs3-mnt
$ ./ntfs3_label_ioctl get /tmp/ntfs3-mnt
label='OLDLABEL'

After the patch, the ioctl correctly returns `-EROFS` on readonly 
mounts, and the inconsistent behavior between the current mount session 
and a remount is gone.

Thanks,
Baolin


**********************************************************************************************************
ntfs3_label_ioctl.c is as follows:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

static int do_get(int fd)
{
	char label[FSLABEL_MAX] = {0};
	int ret;

	ret = ioctl(fd, FS_IOC_GETFSLABEL, label);
	if (ret < 0) {
		perror("GETFSLABEL");
		return 1;
	}

	printf("label='%s'\n", label);
	return 0;
}

static int do_set(int fd, const char *newlabel)
{
	char label[FSLABEL_MAX] = {0};
	int ret;

	strncpy(label, newlabel, sizeof(label) - 1);

	ret = ioctl(fd, FS_IOC_SETFSLABEL, label);
	if (ret < 0) {
		printf("SETFSLABEL failed: errno=%d (%s)\n", errno, strerror(errno));
		return 1;
	}

	printf("SETFSLABEL succeeded\n");
	return 0;
}

static void usage(const char *prog)
{
	fprintf(stderr,
		"Usage:\n"
		"  %s get [path]\n"
		"  %s set <newlabel> [path]\n",
		prog, prog);
}

int main(int argc, char *argv[])
{
	const char *path = "/tmp/ntfs3-mnt";
	int fd, ret;

	if (argc < 2) {
		usage(argv[0]);
		return 1;
	}

	if (!strcmp(argv[1], "get")) {
		if (argc >= 3)
			path = argv[2];
	} else if (!strcmp(argv[1], "set")) {
		if (argc < 3) {
			usage(argv[0]);
			return 1;
		}
		if (argc >= 4)
			path = argv[3];
	} else {
		usage(argv[0]);
		return 1;
	}

	fd = open(path, O_RDONLY | O_DIRECTORY);
	if (fd < 0) {
		perror("open");
		return 1;
	}

	if (!strcmp(argv[1], "get"))
		ret = do_get(fd);
	else
		ret = do_set(fd, argv[2]);

	close(fd);
	return ret;
}

在 2026/6/30 08:59, Baolin Liu 写道:
> From: Baolin Liu <[email protected]>
> 
> FS_IOC_SETFSLABEL does not check for readonly mounts and
> can report success without persisting the label change.
> 
> Return -EROFS on readonly mounts.
> 
> Fixes: 21dc07ac9c25 ("ntfs3: add FS_IOC_SETFSLABEL ioctl")
> Signed-off-by: Baolin Liu <[email protected]>
> ---
>   fs/ntfs3/file.c | 3 +++
>   1 file changed, 3 insertions(+)
> 
> diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
> index b041639ab406..f43baa46ec5c 100644
> --- a/fs/ntfs3/file.c
> +++ b/fs/ntfs3/file.c
> @@ -105,6 +105,9 @@ static int ntfs_ioctl_set_volume_label(struct ntfs_sb_info *sbi, u8 __user *buf)
>   	if (!capable(CAP_SYS_ADMIN))
>   		return -EPERM;
>   
> +	if (sb_rdonly(sbi->sb))
> +		return -EROFS;
> +
>   	if (copy_from_user(user, buf, FSLABEL_MAX))
>   		return -EFAULT;
>