public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] fat: Add FS_IOC_GETFSLABEL / FS_IOC_SETFSLABEL ioctls
@ 2026-02-17 23:06 Ethan Ferguson
  2026-02-17 23:06 ` [PATCH v2 1/2] fat: Add FS_IOC_GETFSLABEL ioctl Ethan Ferguson
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Ethan Ferguson @ 2026-02-17 23:06 UTC (permalink / raw)
  To: hirofumi; +Cc: linux-fsdevel, linux-kernel, Ethan Ferguson

Add support for reading / writing to the volume label of a FAT filesystem
via the FS_IOC_GETFSLABEL and FS_IOC_SETFSLABEL ioctls.

Volume label changes are persisted in the volume label dentry in the root
directory as well as the bios parameter block.

v2:
* Create volume label dentry if not present
* Respect msdos shortname rules
* Add locking
* Fixed bugs (EFAULT from copy_to_user, uninitialized buffer_head)

Ethan Ferguson (2):
  fat: Add FS_IOC_GETFSLABEL ioctl
  fat: Add FS_IOC_SETFSLABEL ioctl

 fs/fat/dir.c         | 51 ++++++++++++++++++++++++++++
 fs/fat/fat.h         |  7 ++++
 fs/fat/file.c        | 79 ++++++++++++++++++++++++++++++++++++++++++++
 fs/fat/inode.c       | 26 +++++++++++++--
 fs/fat/namei_msdos.c |  4 +--
 5 files changed, 163 insertions(+), 4 deletions(-)

base-commit: 9f2693489ef8558240d9e80bfad103650daed0af
-- 
2.43.0


^ permalink raw reply	[flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] fat: Add FS_IOC_SETFSLABEL ioctl
@ 2026-02-18 16:01 Ethan Ferguson
  2026-02-18 18:42 ` OGAWA Hirofumi
  0 siblings, 1 reply; 11+ messages in thread
From: Ethan Ferguson @ 2026-02-18 16:01 UTC (permalink / raw)
  To: Hirofumi OGAWA; +Cc: linux-fsdevel, linux-kernel


> On Feb 18, 2026, at 02:22, OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> wrote:
> 
> Ethan Ferguson <ethan.ferguson@zetier.com> writes:
> 
>> Add support for writing to the volume label of a FAT filesystem via the
>> FS_IOC_SETFSLABEL ioctl.
>> Signed-off-by: Ethan Ferguson <ethan.ferguson@zetier.com>
>> ---
>> fs/fat/dir.c         | 51 +++++++++++++++++++++++++++++++++++
>> fs/fat/fat.h         |  6 +++++
>> fs/fat/file.c        | 63 ++++++++++++++++++++++++++++++++++++++++++++
>> fs/fat/inode.c       | 15 +++++++++++
>> fs/fat/namei_msdos.c |  4 +--
>> 5 files changed, 137 insertions(+), 2 deletions(-)
>> diff --git a/fs/fat/dir.c b/fs/fat/dir.c
>> index 07d95f1442c8..1b11713309ae 100644
>> --- a/fs/fat/dir.c
>> +++ b/fs/fat/dir.c
>> @@ -1425,3 +1425,54 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
>>  return err;
>> }
>> EXPORT_SYMBOL_GPL(fat_add_entries);
>> +
>> +static int fat_create_volume_label_dentry(struct super_block *sb, char *vol_label)
>> +{
>> +    struct msdos_sb_info *sbi = MSDOS_SB(sb);
>> +    struct inode *root_inode = sb->s_root->d_inode;
>> +    struct msdos_dir_entry de;
>> +    struct fat_slot_info sinfo;
>> +    struct timespec64 ts = current_time(root_inode);
>> +    __le16 date, time;
>> +    u8 time_cs;
>> +
>> +    memcpy(de.name, vol_label, MSDOS_NAME);
>> +    de.attr = ATTR_VOLUME;
>> +    de.starthi = de.start = de.size = de.lcase = 0;
>> +
>> +    fat_time_unix2fat(sbi, &ts, &time, &date, &time_cs);
>> +    de.time = time;
>> +    de.date = date;
>> +    if (sbi->options.isvfat) {
>> +        de.cdate = de.adate = date;
>> +        de.ctime = time;
>> +        de.ctime_cs = time_cs;
>> +    } else
>> +        de.cdate = de.adate = de.ctime = de.ctime_cs = 0;
>> +
>> +    return fat_add_entries(root_inode, &de, 1, &sinfo);
>> +}
>> +
>> +int fat_rename_volume_label_dentry(struct super_block *sb, char *vol_label)
>> +{
>> +    struct inode *root_inode = sb->s_root->d_inode;
>> +    struct buffer_head *bh = NULL;
>> +    struct msdos_dir_entry *de;
>> +    loff_t cpos = 0;
>> +    int err = 0;
>> +
>> +    while (1) {
>> +        if (fat_get_entry(root_inode, &cpos, &bh, &de) == -1)
>> +            return fat_create_volume_label_dentry(sb, vol_label);
>> +
>> +        if (de->attr == ATTR_VOLUME) {
>> +            memcpy(de->name, vol_label, MSDOS_NAME);
>> +            mark_buffer_dirty_inode(bh, root_inode);
>> +            if (IS_DIRSYNC(root_inode))
>> +                err = sync_dirty_buffer(bh);
>> +            brelse(bh);
>> +            return err;
>> +        }
>> +    }
> 
> I didn't check how to know the label though, the label is only if
> ATTR_VOLUME? IOW, any other attributes are disallowed?
I'm pretty sure ATTR_VOLUME is disallowed except for:
* Volume labels, where it is the only flag present
* Long file name entries, where it is /not/ the only flag present
This is why I check if attr == ATTR_VOLUME, not attr & ATTR_VOLUME
> What if label is marked as deleted?
As far as I know, a Volume label can never be marked as deleted, but if you want me to change the behavior of my patch, just let me know how you would like me to handle it and I'd be happy to change it.
> I'm not sure though, no need to update timestamps? (need to investigate
> spec or windows behavior)
It's not in the spec that I know either, I'm happy to remove if you deem this unnecessary.
>> +static int fat_convert_volume_label_str(struct msdos_sb_info *sbi, char *in,
>> +                    char *out)
>> +{
>> +    int ret, in_len = max(strnlen(in, FSLABEL_MAX), 11);
>> +    char *needle;
> 
> Silently truncate is the common way for this ioctl?
When I implemented this in exfat, I returned -EINVAL for names that were longer than allowed, but only after converting from nls to UTF16. I can copy this behavior here as well.
> 
>> +    /*
>> +     * '.' is not included in any bad_chars list in this driver,
>> +     * but it is specifically not allowed for volume labels
>> +     */
>> +    for (needle = in; needle - in < in_len; needle++)
>> +        if (*needle == '.')
>> +            return -EINVAL;
> 
> memchr() or such?
Noted, will use, thanks.
>> +    ret = msdos_format_name(in, in_len, out, &sbi->options);
>> +    if (ret)
>> +        return ret;
> 
>> +    /*
>> +     * msdos_format_name assumes we're translating an 8.3 name, but
>> +     * we can handle 11 chars
>> +     */
>> +    if (in_len > 8)
>> +        ret = msdos_format_name(in + 8, in_len - 8, out + 8,
>> +                    &sbi->options);
>> +    return ret;
> 
> fat module should not import msdos module.
Fair. How would you implement checking the validity of the new volume label?
> 
>> +static int fat_ioctl_set_volume_label(struct super_block *sb, char __user *arg)
>> +{
>> +    struct msdos_sb_info *sbi = MSDOS_SB(sb);
>> +    struct inode *root_inode = sb->s_root->d_inode;
>> +    char from_user[FSLABEL_MAX];
>> +    char new_vol_label[MSDOS_NAME];
>> +    int ret;
>> +
>> +    if (!capable(CAP_SYS_ADMIN))
>> +        return -EPERM;
>> +
>> +    if (sb_rdonly(sb))
>> +        return -EROFS;
>> +
>> +    if (copy_from_user(from_user, arg, FSLABEL_MAX))
>> +        return -EFAULT;
>> +
>> +    ret = fat_convert_volume_label_str(sbi, from_user, new_vol_label);
>> +    if (ret)
>> +        return ret;
>> +
>> +    inode_lock(root_inode);
>> +    ret = fat_rename_volume_label_dentry(sb, new_vol_label);
>> +    inode_unlock(root_inode);
>> +    if (ret)
>> +        return ret;
> 
> This rename will have to take same or similar locks with rename(2)?
Sure, so should I only lock on sbi->s_lock through the whole function?
>> diff --git a/fs/fat/inode.c b/fs/fat/inode.c
>> index 6f9a8cc1ad2a..a7528937383b 100644
>> --- a/fs/fat/inode.c
>> +++ b/fs/fat/inode.c
>> @@ -736,6 +736,21 @@ static void delayed_free(struct rcu_head *p)
>> static void fat_put_super(struct super_block *sb)
>> {
>>  struct msdos_sb_info *sbi = MSDOS_SB(sb);
>> +    struct buffer_head *bh = NULL;
>> +    struct fat_boot_sector *bs;
>> +
>> +    bh = sb_bread(sb, 0);
>> +    if (bh == NULL)
>> +        fat_msg(sb, KERN_ERR, "unable to read boot sector");
>> +    else if (!sb_rdonly(sb)) {
>> +        bs = (struct fat_boot_sector *)bh->b_data;
>> +        if (is_fat32(sbi))
>> +            memcpy(bs->fat32.vol_label, sbi->vol_label, MSDOS_NAME);
>> +        else
>> +            memcpy(bs->fat16.vol_label, sbi->vol_label, MSDOS_NAME);
>> +        mark_buffer_dirty(bh);
>> +    }
>> +    brelse(bh);
> 
> Why this unconditionally update the vol_label at unmount?
I can add a dirty bit to msdos_sb_info, and only write if it's present.
> Thanks.
> --
> OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-02-18 18:42 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-17 23:06 [PATCH v2 0/2] fat: Add FS_IOC_GETFSLABEL / FS_IOC_SETFSLABEL ioctls Ethan Ferguson
2026-02-17 23:06 ` [PATCH v2 1/2] fat: Add FS_IOC_GETFSLABEL ioctl Ethan Ferguson
2026-02-17 23:06 ` [PATCH v2 2/2] fat: Add FS_IOC_SETFSLABEL ioctl Ethan Ferguson
2026-02-18  6:50   ` kernel test robot
2026-02-18  7:21   ` kernel test robot
2026-02-18  7:22   ` OGAWA Hirofumi
2026-02-18 10:22   ` kernel test robot
2026-02-18 16:26   ` kernel test robot
2026-02-18  8:46 ` [syzbot ci] Re: fat: Add FS_IOC_GETFSLABEL / FS_IOC_SETFSLABEL ioctls syzbot ci
  -- strict thread matches above, loose matches on Subject: below --
2026-02-18 16:01 [PATCH v2 2/2] fat: Add FS_IOC_SETFSLABEL ioctl Ethan Ferguson
2026-02-18 18:42 ` OGAWA Hirofumi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox