Skip to content

Commit 850f603

Browse files
committed
Merge tag 'exfat-for-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon: - fix referencing wrong parent directory information during rename - introduce a sys_tz mount option to use system timezone - improve performance while zeroing a cluster with dirsync mount option - fix slab-out-bounds in exat_clear_bitmap() reported from syzbot * tag 'exfat-for-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat: exfat: check if cluster num is valid exfat: reduce block requests when zeroing a cluster block: add sync_blockdev_range() exfat: introduce mount option 'sys_tz' exfat: fix referencing wrong parent directory information after renaming
2 parents f30fabe + 64ba4b1 commit 850f603

File tree

8 files changed

+55
-61
lines changed

8 files changed

+55
-61
lines changed

block/bdev.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,13 @@ int sync_blockdev(struct block_device *bdev)
200200
}
201201
EXPORT_SYMBOL(sync_blockdev);
202202

203+
int sync_blockdev_range(struct block_device *bdev, loff_t lstart, loff_t lend)
204+
{
205+
return filemap_write_and_wait_range(bdev->bd_inode->i_mapping,
206+
lstart, lend);
207+
}
208+
EXPORT_SYMBOL(sync_blockdev_range);
209+
203210
/*
204211
* Write out and wait upon all dirty data associated with this
205212
* device. Filesystem data as well as the underlying block

fs/exfat/balloc.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
148148
struct super_block *sb = inode->i_sb;
149149
struct exfat_sb_info *sbi = EXFAT_SB(sb);
150150

151-
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
151+
if (!is_valid_cluster(sbi, clu))
152+
return -EINVAL;
153+
152154
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
153155
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
154156
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
@@ -166,7 +168,9 @@ void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync)
166168
struct exfat_sb_info *sbi = EXFAT_SB(sb);
167169
struct exfat_mount_options *opts = &sbi->options;
168170

169-
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
171+
if (!is_valid_cluster(sbi, clu))
172+
return;
173+
170174
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
171175
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
172176
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);

fs/exfat/exfat_fs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ struct exfat_mount_options {
203203
/* on error: continue, panic, remount-ro */
204204
enum exfat_error_mode errors;
205205
unsigned utf8:1, /* Use of UTF-8 character set */
206+
sys_tz:1, /* Use local timezone */
206207
discard:1, /* Issue discard requests on deletions */
207208
keep_last_dots:1; /* Keep trailing periods in paths */
208209
int time_offset; /* Offset of timestamps from UTC (in minutes) */
@@ -381,6 +382,12 @@ static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
381382
EXFAT_RESERVED_CLUSTERS;
382383
}
383384

385+
static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
386+
unsigned int clus)
387+
{
388+
return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters;
389+
}
390+
384391
/* super.c */
385392
int exfat_set_volume_dirty(struct super_block *sb);
386393
int exfat_clear_volume_dirty(struct super_block *sb);

fs/exfat/fatent.c

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/slab.h>
77
#include <asm/unaligned.h>
88
#include <linux/buffer_head.h>
9+
#include <linux/blkdev.h>
910

1011
#include "exfat_raw.h"
1112
#include "exfat_fs.h"
@@ -81,12 +82,6 @@ int exfat_ent_set(struct super_block *sb, unsigned int loc,
8182
return 0;
8283
}
8384

84-
static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
85-
unsigned int clus)
86-
{
87-
return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters;
88-
}
89-
9085
int exfat_ent_get(struct super_block *sb, unsigned int loc,
9186
unsigned int *content)
9287
{
@@ -274,10 +269,9 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
274269
{
275270
struct super_block *sb = dir->i_sb;
276271
struct exfat_sb_info *sbi = EXFAT_SB(sb);
277-
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
278-
int nr_bhs = MAX_BUF_PER_PAGE;
272+
struct buffer_head *bh;
279273
sector_t blknr, last_blknr;
280-
int err, i, n;
274+
int i;
281275

282276
blknr = exfat_cluster_to_sector(sbi, clu);
283277
last_blknr = blknr + sbi->sect_per_clus;
@@ -291,30 +285,23 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
291285
}
292286

293287
/* Zeroing the unused blocks on this cluster */
294-
while (blknr < last_blknr) {
295-
for (n = 0; n < nr_bhs && blknr < last_blknr; n++, blknr++) {
296-
bhs[n] = sb_getblk(sb, blknr);
297-
if (!bhs[n]) {
298-
err = -ENOMEM;
299-
goto release_bhs;
300-
}
301-
memset(bhs[n]->b_data, 0, sb->s_blocksize);
302-
}
303-
304-
err = exfat_update_bhs(bhs, n, IS_DIRSYNC(dir));
305-
if (err)
306-
goto release_bhs;
288+
for (i = blknr; i < last_blknr; i++) {
289+
bh = sb_getblk(sb, i);
290+
if (!bh)
291+
return -ENOMEM;
307292

308-
for (i = 0; i < n; i++)
309-
brelse(bhs[i]);
293+
memset(bh->b_data, 0, sb->s_blocksize);
294+
set_buffer_uptodate(bh);
295+
mark_buffer_dirty(bh);
296+
brelse(bh);
310297
}
311-
return 0;
312298

313-
release_bhs:
314-
exfat_err(sb, "failed zeroed sect %llu\n", (unsigned long long)blknr);
315-
for (i = 0; i < n; i++)
316-
bforget(bhs[i]);
317-
return err;
299+
if (IS_DIRSYNC(dir))
300+
return sync_blockdev_range(sb->s_bdev,
301+
EXFAT_BLK_TO_B(blknr, sb),
302+
EXFAT_BLK_TO_B(last_blknr, sb) - 1);
303+
304+
return 0;
318305
}
319306

320307
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,

fs/exfat/misc.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off)
7474
ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off);
7575
}
7676

77+
static inline int exfat_tz_offset(struct exfat_sb_info *sbi)
78+
{
79+
if (sbi->options.sys_tz)
80+
return -sys_tz.tz_minuteswest;
81+
return sbi->options.time_offset;
82+
}
83+
7784
/* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */
7885
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
7986
u8 tz, __le16 time, __le16 date, u8 time_cs)
@@ -96,8 +103,7 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
96103
/* Adjust timezone to UTC0. */
97104
exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID);
98105
else
99-
/* Convert from local time to UTC using time_offset. */
100-
ts->tv_sec -= sbi->options.time_offset * SECS_PER_MIN;
106+
ts->tv_sec -= exfat_tz_offset(sbi) * SECS_PER_MIN;
101107
}
102108

103109
/* Convert linear UNIX date to a EXFAT time/date pair. */

fs/exfat/namei.c

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
10801080

10811081
exfat_remove_entries(inode, p_dir, oldentry, 0,
10821082
num_old_entries);
1083+
ei->dir = *p_dir;
10831084
ei->entry = newentry;
10841085
} else {
10851086
if (exfat_get_entry_type(epold) == TYPE_FILE) {
@@ -1167,28 +1168,6 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
11671168
return 0;
11681169
}
11691170

1170-
static void exfat_update_parent_info(struct exfat_inode_info *ei,
1171-
struct inode *parent_inode)
1172-
{
1173-
struct exfat_sb_info *sbi = EXFAT_SB(parent_inode->i_sb);
1174-
struct exfat_inode_info *parent_ei = EXFAT_I(parent_inode);
1175-
loff_t parent_isize = i_size_read(parent_inode);
1176-
1177-
/*
1178-
* the problem that struct exfat_inode_info caches wrong parent info.
1179-
*
1180-
* because of flag-mismatch of ei->dir,
1181-
* there is abnormal traversing cluster chain.
1182-
*/
1183-
if (unlikely(parent_ei->flags != ei->dir.flags ||
1184-
parent_isize != EXFAT_CLU_TO_B(ei->dir.size, sbi) ||
1185-
parent_ei->start_clu != ei->dir.dir)) {
1186-
exfat_chain_set(&ei->dir, parent_ei->start_clu,
1187-
EXFAT_B_TO_CLU_ROUND_UP(parent_isize, sbi),
1188-
parent_ei->flags);
1189-
}
1190-
}
1191-
11921171
/* rename or move a old file into a new file */
11931172
static int __exfat_rename(struct inode *old_parent_inode,
11941173
struct exfat_inode_info *ei, struct inode *new_parent_inode,
@@ -1219,8 +1198,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
12191198
return -ENOENT;
12201199
}
12211200

1222-
exfat_update_parent_info(ei, old_parent_inode);
1223-
12241201
exfat_chain_dup(&olddir, &ei->dir);
12251202
dentry = ei->entry;
12261203

@@ -1241,8 +1218,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
12411218
goto out;
12421219
}
12431220

1244-
exfat_update_parent_info(new_ei, new_parent_inode);
1245-
12461221
p_dir = &(new_ei->dir);
12471222
new_entry = new_ei->entry;
12481223
ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);

fs/exfat/super.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
170170
seq_puts(m, ",discard");
171171
if (opts->keep_last_dots)
172172
seq_puts(m, ",keep_last_dots");
173-
if (opts->time_offset)
173+
if (opts->sys_tz)
174+
seq_puts(m, ",sys_tz");
175+
else if (opts->time_offset)
174176
seq_printf(m, ",time_offset=%d", opts->time_offset);
175177
return 0;
176178
}
@@ -214,6 +216,7 @@ enum {
214216
Opt_errors,
215217
Opt_discard,
216218
Opt_keep_last_dots,
219+
Opt_sys_tz,
217220
Opt_time_offset,
218221

219222
/* Deprecated options */
@@ -241,6 +244,7 @@ static const struct fs_parameter_spec exfat_parameters[] = {
241244
fsparam_enum("errors", Opt_errors, exfat_param_enums),
242245
fsparam_flag("discard", Opt_discard),
243246
fsparam_flag("keep_last_dots", Opt_keep_last_dots),
247+
fsparam_flag("sys_tz", Opt_sys_tz),
244248
fsparam_s32("time_offset", Opt_time_offset),
245249
__fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated,
246250
NULL),
@@ -298,6 +302,9 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
298302
case Opt_keep_last_dots:
299303
opts->keep_last_dots = 1;
300304
break;
305+
case Opt_sys_tz:
306+
opts->sys_tz = 1;
307+
break;
301308
case Opt_time_offset:
302309
/*
303310
* Make the limit 24 just in case someone invents something

include/linux/blkdev.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,6 +1535,7 @@ int truncate_bdev_range(struct block_device *bdev, fmode_t mode, loff_t lstart,
15351535
#ifdef CONFIG_BLOCK
15361536
void invalidate_bdev(struct block_device *bdev);
15371537
int sync_blockdev(struct block_device *bdev);
1538+
int sync_blockdev_range(struct block_device *bdev, loff_t lstart, loff_t lend);
15381539
int sync_blockdev_nowait(struct block_device *bdev);
15391540
void sync_bdevs(bool wait);
15401541
void printk_all_partitions(void);

0 commit comments

Comments
 (0)