This commit adds mount option 'zero_size_dir'. If this option enabled, don't allocate a cluster to directory when creating it, and set the directory size to 0. On Windows, a cluster is allocated for a directory when it is created, so the mount option is disabled by default. Signed-off-by: Yuezhang Mo <Yuezhang.Mo@xxxxxxxx> Reviewed-by: Andy Wu <Andy.Wu@xxxxxxxx> Reviewed-by: Aoyama Wataru <wataru.aoyama@xxxxxxxx> --- fs/exfat/dir.c | 12 ++++++------ fs/exfat/exfat_fs.h | 2 ++ fs/exfat/namei.c | 7 +++++-- fs/exfat/super.c | 7 +++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 598081d0d059..9ad5ff01456e 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -417,11 +417,13 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type) } static void exfat_init_stream_entry(struct exfat_dentry *ep, - unsigned char flags, unsigned int start_clu, - unsigned long long size) + unsigned int start_clu, unsigned long long size) { exfat_set_entry_type(ep, TYPE_STREAM); - ep->dentry.stream.flags = flags; + if (size == 0) + ep->dentry.stream.flags = ALLOC_FAT_CHAIN; + else + ep->dentry.stream.flags = ALLOC_NO_FAT_CHAIN; ep->dentry.stream.start_clu = cpu_to_le32(start_clu); ep->dentry.stream.valid_size = cpu_to_le64(size); ep->dentry.stream.size = cpu_to_le64(size); @@ -487,9 +489,7 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir, if (!ep) return -EIO; - exfat_init_stream_entry(ep, - (type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN, - start_clu, size); + exfat_init_stream_entry(ep, start_clu, size); exfat_update_bh(bh, IS_DIRSYNC(inode)); brelse(bh); diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 729ada9e26e8..cbf32949ff67 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -234,6 +234,8 @@ struct exfat_mount_options { discard:1, /* Issue discard requests on deletions */ keep_last_dots:1; /* Keep trailing periods in paths */ int time_offset; /* Offset of timestamps from UTC (in minutes) */ + /* Support creating zero-size directory, default: false */ + bool zero_size_dir; }; /* diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 43774693f65f..23220dd874e3 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -518,7 +518,7 @@ static int exfat_add_entry(struct inode *inode, const char *path, goto out; } - if (type == TYPE_DIR) { + if (type == TYPE_DIR && !sbi->options.zero_size_dir) { ret = exfat_alloc_new_dir(inode, &clu); if (ret) goto out; @@ -551,7 +551,10 @@ static int exfat_add_entry(struct inode *inode, const char *path, info->num_subdirs = 0; } else { info->attr = ATTR_SUBDIR; - info->start_clu = start_clu; + if (sbi->options.zero_size_dir) + info->start_clu = EXFAT_EOF_CLUSTER; + else + info->start_clu = start_clu; info->size = clu_size; info->num_subdirs = EXFAT_MIN_SUBDIR; } diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 8c32460e031e..b1e03276e0a0 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -174,6 +174,8 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root) seq_puts(m, ",sys_tz"); else if (opts->time_offset) seq_printf(m, ",time_offset=%d", opts->time_offset); + if (opts->zero_size_dir) + seq_puts(m, ",zero_size_dir"); return 0; } @@ -218,6 +220,7 @@ enum { Opt_keep_last_dots, Opt_sys_tz, Opt_time_offset, + Opt_zero_size_dir, /* Deprecated options */ Opt_utf8, @@ -246,6 +249,7 @@ static const struct fs_parameter_spec exfat_parameters[] = { fsparam_flag("keep_last_dots", Opt_keep_last_dots), fsparam_flag("sys_tz", Opt_sys_tz), fsparam_s32("time_offset", Opt_time_offset), + fsparam_flag("zero_size_dir", Opt_zero_size_dir), __fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated, NULL), __fsparam(NULL, "debug", Opt_debug, fs_param_deprecated, @@ -314,6 +318,9 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param) return -EINVAL; opts->time_offset = result.int_32; break; + case Opt_zero_size_dir: + opts->zero_size_dir = true; + break; case Opt_utf8: case Opt_debug: case Opt_namecase: -- 2.25.1