Add support for writing to the volume label of a FAT filesystem via the FS_IOC_SETFSLABEL ioctl. Signed-off-by: Ethan Ferguson --- fs/fat/dir.c | 22 ++++++++++++++++++++++ fs/fat/fat.h | 1 + fs/fat/file.c | 19 +++++++++++++++++++ fs/fat/inode.c | 15 +++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 92b091783966..13e87f4c6bf3 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -1423,3 +1423,25 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots, return err; } EXPORT_SYMBOL_GPL(fat_add_entries); + +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; + struct msdos_dir_entry *de; + loff_t cpos = 0; + + while (1) { + if (fat_get_entry(root_inode, &cpos, &bh, &de) == -1) + return -ENOENT; + + if (de->attr == ATTR_VOLUME) { + memcpy(de->name, vol_label, MSDOS_NAME); + mark_inode_dirty(root_inode); + return 0; + } + + brelse(bh); + } +} +EXPORT_SYMBOL_GPL(fat_rename_volume_label_dentry); diff --git a/fs/fat/fat.h b/fs/fat/fat.h index db9c854ddef8..5f1536c21adf 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -341,6 +341,7 @@ extern int fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts); extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots, struct fat_slot_info *sinfo); extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo); +extern int fat_rename_volume_label_dentry(struct super_block *sb, char *vol_label); /* fat/fatent.c */ struct fat_entry { diff --git a/fs/fat/file.c b/fs/fat/file.c index c55a99009a9c..2475a8f58596 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -160,6 +160,23 @@ static int fat_ioctl_get_volume_label(struct inode *inode, char __user *arg) return copy_to_user(arg, sbi->vol_label, MSDOS_NAME); } +static int fat_ioctl_set_volume_label(struct inode *inode, char __user *arg) +{ + struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + char new_vol_label[MSDOS_NAME]; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(new_vol_label, arg, MSDOS_NAME)) + return -EFAULT; + + fat_rename_volume_label_dentry(inode->i_sb, new_vol_label); + + memcpy(sbi->vol_label, new_vol_label, MSDOS_NAME); + return 0; +} + long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -174,6 +191,8 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return fat_ioctl_get_volume_id(inode, user_attr); case FS_IOC_GETFSLABEL: return fat_ioctl_get_volume_label(inode, (char __user *) arg); + case FS_IOC_SETFSLABEL: + return fat_ioctl_set_volume_label(inode, (char __user *) arg); case FITRIM: return fat_ioctl_fitrim(inode, arg); default: diff --git a/fs/fat/inode.c b/fs/fat/inode.c index f6bd3f079e74..b40abb2b0010 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); fat_set_state(sb, 0, 0); -- 2.53.0