All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Sungjong Seo" <sj1557.seo@samsung.com>
To: "'Ethan Ferguson'" <ethan.ferguson@zetier.com>,
	<linkinjeon@kernel.org>, <yuezhang.mo@sony.com>
Cc: <linux-fsdevel@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<cpgs@samsung.com>, "'Yuezhang Mo'" <Yuezhang.Mo@sony.com>,
	<sj1557.seo@samsung.com>
Subject: RE: [PATCH v7 1/1] exfat: Add support for FS_IOC_{GET,SET}FSLABEL
Date: Mon, 15 Sep 2025 19:39:49 +0900	[thread overview]
Message-ID: <8f2b01dc262d$0ff524a0$2fdf6de0$@samsung.com> (raw)
In-Reply-To: <20250912032619.9846-2-ethan.ferguson@zetier.com>

> Add support for reading / writing to the exfat volume label from the
> FS_IOC_GETFSLABEL and FS_IOC_SETFSLABEL ioctls
> 
> Co-developed-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
> Signed-off-by: Yuezhang Mo <Yuezhang.Mo@sony.com>
> Signed-off-by: Ethan Ferguson <ethan.ferguson@zetier.com>

Looks great! Thanks.
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>

> ---
>  fs/exfat/dir.c       | 158 +++++++++++++++++++++++++++++++++++++++++++
>  fs/exfat/exfat_fs.h  |   7 ++
>  fs/exfat/exfat_raw.h |   6 ++
>  fs/exfat/file.c      |  52 ++++++++++++++
>  fs/exfat/namei.c     |   2 +-
>  5 files changed, 224 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c
> index ee060e26f51d..55d826f6475d 100644
> --- a/fs/exfat/dir.c
> +++ b/fs/exfat/dir.c
> @@ -1244,3 +1244,161 @@ int exfat_count_dir_entries(struct super_block
*sb,
> struct exfat_chain *p_dir)
> 
>  	return count;
>  }
> +
> +static int exfat_get_volume_label_dentry(struct super_block *sb,
> +		struct exfat_entry_set_cache *es)
> +{
> +	int i;
> +	int dentry = 0;
> +	unsigned int type;
> +	struct exfat_sb_info *sbi = EXFAT_SB(sb);
> +	struct exfat_hint_femp hint_femp;
> +	struct exfat_inode_info *ei = EXFAT_I(sb->s_root->d_inode);
> +	struct exfat_chain clu;
> +	struct exfat_dentry *ep;
> +	struct buffer_head *bh;
> +
> +	hint_femp.eidx = EXFAT_HINT_NONE;
> +	exfat_chain_set(&clu, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
> +
> +	while (clu.dir != EXFAT_EOF_CLUSTER) {
> +		for (i = 0; i < sbi->dentries_per_clu; i++, dentry++) {
> +			ep = exfat_get_dentry(sb, &clu, i, &bh);
> +			if (!ep)
> +				return -EIO;
> +
> +			type = exfat_get_entry_type(ep);
> +			if (hint_femp.eidx == EXFAT_HINT_NONE) {
> +				if (type == TYPE_DELETED || type ==
TYPE_UNUSED)
> {
> +					hint_femp.cur = clu;
> +					hint_femp.eidx = dentry;
> +					hint_femp.count = 1;
> +				}
> +			}
> +
> +			if (type == TYPE_UNUSED) {
> +				brelse(bh);
> +				goto not_found;
> +			}
> +
> +			if (type != TYPE_VOLUME) {
> +				brelse(bh);
> +				continue;
> +			}
> +
> +			memset(es, 0, sizeof(*es));
> +			es->sb = sb;
> +			es->bh = es->__bh;
> +			es->bh[0] = bh;
> +			es->num_bh = 1;
> +			es->start_off = EXFAT_DEN_TO_B(i) % sb->s_blocksize;
> +
> +			return 0;
> +		}
> +
> +		if (exfat_get_next_cluster(sb, &(clu.dir)))
> +			return -EIO;
> +	}
> +
> +not_found:
> +	if (hint_femp.eidx == EXFAT_HINT_NONE) {
> +		hint_femp.cur.dir = EXFAT_EOF_CLUSTER;
> +		hint_femp.eidx = dentry;
> +		hint_femp.count = 0;
> +	}
> +
> +	ei->hint_femp = hint_femp;
> +
> +	return -ENOENT;
> +}
> +
> +int exfat_read_volume_label(struct super_block *sb, struct exfat_uni_name
> *label_out)
> +{
> +	int ret, i;
> +	struct exfat_sb_info *sbi = EXFAT_SB(sb);
> +	struct exfat_entry_set_cache es;
> +	struct exfat_dentry *ep;
> +
> +	mutex_lock(&sbi->s_lock);
> +
> +	memset(label_out, 0, sizeof(*label_out));
> +	ret = exfat_get_volume_label_dentry(sb, &es);
> +	if (ret < 0) {
> +		/*
> +		 * ENOENT signifies that a volume label dentry doesn't exist
> +		 * We will treat this as an empty volume label and not fail.
> +		 */
> +		if (ret == -ENOENT)
> +			ret = 0;
> +
> +		goto unlock;
> +	}
> +
> +	ep = exfat_get_dentry_cached(&es, 0);
> +	label_out->name_len = ep->dentry.volume_label.char_count;
> +	if (label_out->name_len > EXFAT_VOLUME_LABEL_LEN) {
> +		ret = -EIO;
> +		goto unlock;
> +	}
> +
> +	for (i = 0; i < label_out->name_len; i++)
> +		label_out->name[i] = le16_to_cpu(ep-
> >dentry.volume_label.volume_label[i]);
> +
> +unlock:
> +	mutex_unlock(&sbi->s_lock);
> +	return ret;
> +}
> +
> +int exfat_write_volume_label(struct super_block *sb,
> +			     struct exfat_uni_name *label)
> +{
> +	int ret, i;
> +	struct exfat_sb_info *sbi = EXFAT_SB(sb);
> +	struct inode *root_inode = sb->s_root->d_inode;
> +	struct exfat_entry_set_cache es;
> +	struct exfat_chain clu;
> +	struct exfat_dentry *ep;
> +
> +	if (label->name_len > EXFAT_VOLUME_LABEL_LEN)
> +		return -EINVAL;
> +
> +	mutex_lock(&sbi->s_lock);
> +
> +	ret = exfat_get_volume_label_dentry(sb, &es);
> +	if (ret == -ENOENT) {
> +		if (label->name_len == 0) {
> +			/* No volume label dentry, no need to clear */
> +			ret = 0;
> +			goto unlock;
> +		}
> +
> +		ret = exfat_find_empty_entry(root_inode, &clu, 1, &es);
> +	}
> +
> +	if (ret < 0)
> +		goto unlock;
> +
> +	ep = exfat_get_dentry_cached(&es, 0);
> +
> +	if (label->name_len == 0 && ep->dentry.volume_label.char_count == 0)
> {
> +		/* volume label had been cleared */
> +		exfat_put_dentry_set(&es, 0);
> +		goto unlock;
> +	}
> +
> +	memset(ep, 0, sizeof(*ep));
> +	ep->type = EXFAT_VOLUME;
> +
> +	for (i = 0; i < label->name_len; i++)
> +		ep->dentry.volume_label.volume_label[i] =
> +			cpu_to_le16(label->name[i]);
> +
> +	ep->dentry.volume_label.char_count = label->name_len;
> +	es.modified = true;
> +
> +	ret = exfat_put_dentry_set(&es, IS_DIRSYNC(root_inode));
> +
> +unlock:
> +	mutex_unlock(&sbi->s_lock);
> +	return ret;
> +}
> diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
> index f8ead4d47ef0..329697c89d09 100644
> --- a/fs/exfat/exfat_fs.h
> +++ b/fs/exfat/exfat_fs.h
> @@ -477,6 +477,9 @@ int exfat_force_shutdown(struct super_block *sb, u32
> flags);
>  /* namei.c */
>  extern const struct dentry_operations exfat_dentry_ops;
>  extern const struct dentry_operations exfat_utf8_dentry_ops;
> +int exfat_find_empty_entry(struct inode *inode,
> +		struct exfat_chain *p_dir, int num_entries,
> +			   struct exfat_entry_set_cache *es);
> 
>  /* cache.c */
>  int exfat_cache_init(void);
> @@ -517,6 +520,10 @@ int exfat_get_empty_dentry_set(struct
> exfat_entry_set_cache *es,
>  		unsigned int num_entries);
>  int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync);
>  int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain
> *p_dir);
> +int exfat_read_volume_label(struct super_block *sb,
> +			    struct exfat_uni_name *label_out);
> +int exfat_write_volume_label(struct super_block *sb,
> +			     struct exfat_uni_name *label);
> 
>  /* inode.c */
>  extern const struct inode_operations exfat_file_inode_operations;
> diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
> index 971a1ccd0e89..4082fa7b8c14 100644
> --- a/fs/exfat/exfat_raw.h
> +++ b/fs/exfat/exfat_raw.h
> @@ -80,6 +80,7 @@
>  #define BOOTSEC_OLDBPB_LEN		53
> 
>  #define EXFAT_FILE_NAME_LEN		15
> +#define EXFAT_VOLUME_LABEL_LEN		11
> 
>  #define EXFAT_MIN_SECT_SIZE_BITS		9
>  #define EXFAT_MAX_SECT_SIZE_BITS		12
> @@ -159,6 +160,11 @@ struct exfat_dentry {
>  			__le32 start_clu;
>  			__le64 size;
>  		} __packed upcase; /* up-case table directory entry */
> +		struct {
> +			__u8 char_count;
> +			__le16 volume_label[EXFAT_VOLUME_LABEL_LEN];
> +			__u8 reserved[8];
> +		} __packed volume_label; /* volume label directory entry */
>  		struct {
>  			__u8 flags;
>  			__u8 vendor_guid[16];
> diff --git a/fs/exfat/file.c b/fs/exfat/file.c
> index 538d2b6ac2ec..f246cf439588 100644
> --- a/fs/exfat/file.c
> +++ b/fs/exfat/file.c
> @@ -486,6 +486,54 @@ static int exfat_ioctl_shutdown(struct super_block
> *sb, unsigned long arg)
>  	return exfat_force_shutdown(sb, flags);
>  }
> 
> +static int exfat_ioctl_get_volume_label(struct super_block *sb, unsigned
> long arg)
> +{
> +	int ret;
> +	char label[FSLABEL_MAX] = {0};
> +	struct exfat_uni_name uniname;
> +
> +	ret = exfat_read_volume_label(sb, &uniname);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = exfat_utf16_to_nls(sb, &uniname, label, uniname.name_len);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (copy_to_user((char __user *)arg, label, ret + 1))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int exfat_ioctl_set_volume_label(struct super_block *sb,
> +					unsigned long arg)
> +{
> +	int ret = 0, lossy;
> +	char label[FSLABEL_MAX];
> +	struct exfat_uni_name uniname;
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +
> +	if (copy_from_user(label, (char __user *)arg, FSLABEL_MAX))
> +		return -EFAULT;
> +
> +	memset(&uniname, 0, sizeof(uniname));
> +	if (label[0]) {
> +		ret = exfat_nls_to_utf16(sb, label, FSLABEL_MAX,
> +					 &uniname, &lossy);
> +		if (ret < 0)
> +			return ret;
> +		else if (lossy & NLS_NAME_LOSSY)
> +			return -EINVAL;
> +	}
> +
> +	uniname.name_len = ret;
> +
> +	return exfat_write_volume_label(sb, &uniname);
> +}
> +
>  long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	struct inode *inode = file_inode(filp);
> @@ -500,6 +548,10 @@ long exfat_ioctl(struct file *filp, unsigned int cmd,
> unsigned long arg)
>  		return exfat_ioctl_shutdown(inode->i_sb, arg);
>  	case FITRIM:
>  		return exfat_ioctl_fitrim(inode, arg);
> +	case FS_IOC_GETFSLABEL:
> +		return exfat_ioctl_get_volume_label(inode->i_sb, arg);
> +	case FS_IOC_SETFSLABEL:
> +		return exfat_ioctl_set_volume_label(inode->i_sb, arg);
>  	default:
>  		return -ENOTTY;
>  	}
> diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c
> index f5f1c4e8a29f..eaa781d6263c 100644
> --- a/fs/exfat/namei.c
> +++ b/fs/exfat/namei.c
> @@ -300,7 +300,7 @@ static int exfat_check_max_dentries(struct inode
> *inode)
>   *   the directory entry index in p_dir is returned on succeeds
>   *   -error code is returned on failure
>   */
> -static int exfat_find_empty_entry(struct inode *inode,
> +int exfat_find_empty_entry(struct inode *inode,
>  		struct exfat_chain *p_dir, int num_entries,
>  		struct exfat_entry_set_cache *es)
>  {
> --
> 2.34.1



  reply	other threads:[~2025-09-15 10:39 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-12  3:26 [PATCH v7 0/1] exfat: Add support for FS_IOC_{GET,SET}FSLABEL Ethan Ferguson
2025-09-12  3:26 ` [PATCH v7 1/1] " Ethan Ferguson
2025-09-15 10:39   ` Sungjong Seo [this message]
2025-09-16  6:34   ` Namjae Jeon
2025-09-28 10:28   ` Yuezhang.Mo
2025-10-01 16:34     ` Ethan Ferguson
2025-10-01 23:08       ` Namjae Jeon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='8f2b01dc262d$0ff524a0$2fdf6de0$@samsung.com' \
    --to=sj1557.seo@samsung.com \
    --cc=cpgs@samsung.com \
    --cc=ethan.ferguson@zetier.com \
    --cc=linkinjeon@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=yuezhang.mo@sony.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.