Description for this pull request:
- 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. -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmKLhPkWHGxpbmtpbmpl b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCOeXD/9nJofEx/n9KK0pA1WP2zKoLBnP YgLfnWTBlXfErInklW4kg057S6q8M0pDm0iASLw9P6GZNe0VFV5PigrTyAjy5ghW hm3JiFAHIZgaOlOk2NQd/1Qv/IdlnkbRkngXqHcizxEX/LcKZpP+pb1mdW6NzWrt /HagLClFTUhb0Su3DT7TCqiam5lI+lkarRI0Jo4Scstgsn4aT+25jk9N0bfUih7f hGKJpii+5UWCLlBJnyyghrBRQiiPdsETadJdRnHgeDdzKg/UNWxMP0C+G6PmSko/ mScrR+FeH2toURSUESi1Q558z1+3Fhb8rMbl3aWV70FJDmzMwn9YyhPgBrX3x6Gb AF7UBHFvORStYRUmmSMbX9XkY2gNoI9qZMXghDRlgF8t/WWY8VeVnyslaWwqDQhw qXyOIThiCuhLfKTD+r+MM08oUPcyFBtuGvdzDOH7/b56zEDwzab+hSHZz94xPWEz ESk0hNhaJCEvcgEr7IlSSF4k5Ff+hWVKN4R/DD78yxmjHveOuNMibTeE2YgDIFEX SiKFaAiXUuWGVsAPuAeJ+np/7rW3OEBG8yKhtri5vsoXk+Mqd56rp0EkEHzqbVHQ Ki5gua549KNRuxnbXRtWLjCKucwN86mE45WD0P0ORnBOjlfmgg8adp6BBSW5yVD2 SZkfgI0FL8rWdBwRcQ== =fr78 -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
850f6033cd
|
@ -200,6 +200,13 @@ int sync_blockdev(struct block_device *bdev)
|
|||
}
|
||||
EXPORT_SYMBOL(sync_blockdev);
|
||||
|
||||
int sync_blockdev_range(struct block_device *bdev, loff_t lstart, loff_t lend)
|
||||
{
|
||||
return filemap_write_and_wait_range(bdev->bd_inode->i_mapping,
|
||||
lstart, lend);
|
||||
}
|
||||
EXPORT_SYMBOL(sync_blockdev_range);
|
||||
|
||||
/*
|
||||
* Write out and wait upon all dirty data associated with this
|
||||
* device. Filesystem data as well as the underlying block
|
||||
|
|
|
@ -148,7 +148,9 @@ int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync)
|
|||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
|
||||
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
|
||||
if (!is_valid_cluster(sbi, clu))
|
||||
return -EINVAL;
|
||||
|
||||
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
|
||||
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
|
||||
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)
|
|||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_mount_options *opts = &sbi->options;
|
||||
|
||||
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
|
||||
if (!is_valid_cluster(sbi, clu))
|
||||
return;
|
||||
|
||||
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
|
||||
i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
|
||||
b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx);
|
||||
|
|
|
@ -203,6 +203,7 @@ struct exfat_mount_options {
|
|||
/* on error: continue, panic, remount-ro */
|
||||
enum exfat_error_mode errors;
|
||||
unsigned utf8:1, /* Use of UTF-8 character set */
|
||||
sys_tz:1, /* Use local timezone */
|
||||
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) */
|
||||
|
@ -381,6 +382,12 @@ static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
|
|||
EXFAT_RESERVED_CLUSTERS;
|
||||
}
|
||||
|
||||
static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
|
||||
unsigned int clus)
|
||||
{
|
||||
return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters;
|
||||
}
|
||||
|
||||
/* super.c */
|
||||
int exfat_set_volume_dirty(struct super_block *sb);
|
||||
int exfat_clear_volume_dirty(struct super_block *sb);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
@ -81,12 +82,6 @@ int exfat_ent_set(struct super_block *sb, unsigned int loc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
|
||||
unsigned int clus)
|
||||
{
|
||||
return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters;
|
||||
}
|
||||
|
||||
int exfat_ent_get(struct super_block *sb, unsigned int loc,
|
||||
unsigned int *content)
|
||||
{
|
||||
|
@ -274,10 +269,9 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
|
|||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
|
||||
int nr_bhs = MAX_BUF_PER_PAGE;
|
||||
struct buffer_head *bh;
|
||||
sector_t blknr, last_blknr;
|
||||
int err, i, n;
|
||||
int i;
|
||||
|
||||
blknr = exfat_cluster_to_sector(sbi, clu);
|
||||
last_blknr = blknr + sbi->sect_per_clus;
|
||||
|
@ -291,30 +285,23 @@ int exfat_zeroed_cluster(struct inode *dir, unsigned int clu)
|
|||
}
|
||||
|
||||
/* Zeroing the unused blocks on this cluster */
|
||||
while (blknr < last_blknr) {
|
||||
for (n = 0; n < nr_bhs && blknr < last_blknr; n++, blknr++) {
|
||||
bhs[n] = sb_getblk(sb, blknr);
|
||||
if (!bhs[n]) {
|
||||
err = -ENOMEM;
|
||||
goto release_bhs;
|
||||
}
|
||||
memset(bhs[n]->b_data, 0, sb->s_blocksize);
|
||||
}
|
||||
for (i = blknr; i < last_blknr; i++) {
|
||||
bh = sb_getblk(sb, i);
|
||||
if (!bh)
|
||||
return -ENOMEM;
|
||||
|
||||
err = exfat_update_bhs(bhs, n, IS_DIRSYNC(dir));
|
||||
if (err)
|
||||
goto release_bhs;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
brelse(bhs[i]);
|
||||
memset(bh->b_data, 0, sb->s_blocksize);
|
||||
set_buffer_uptodate(bh);
|
||||
mark_buffer_dirty(bh);
|
||||
brelse(bh);
|
||||
}
|
||||
return 0;
|
||||
|
||||
release_bhs:
|
||||
exfat_err(sb, "failed zeroed sect %llu\n", (unsigned long long)blknr);
|
||||
for (i = 0; i < n; i++)
|
||||
bforget(bhs[i]);
|
||||
return err;
|
||||
if (IS_DIRSYNC(dir))
|
||||
return sync_blockdev_range(sb->s_bdev,
|
||||
EXFAT_BLK_TO_B(blknr, sb),
|
||||
EXFAT_BLK_TO_B(last_blknr, sb) - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
|
||||
|
|
|
@ -74,6 +74,13 @@ static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off)
|
|||
ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off);
|
||||
}
|
||||
|
||||
static inline int exfat_tz_offset(struct exfat_sb_info *sbi)
|
||||
{
|
||||
if (sbi->options.sys_tz)
|
||||
return -sys_tz.tz_minuteswest;
|
||||
return sbi->options.time_offset;
|
||||
}
|
||||
|
||||
/* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */
|
||||
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
|
||||
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,
|
|||
/* Adjust timezone to UTC0. */
|
||||
exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID);
|
||||
else
|
||||
/* Convert from local time to UTC using time_offset. */
|
||||
ts->tv_sec -= sbi->options.time_offset * SECS_PER_MIN;
|
||||
ts->tv_sec -= exfat_tz_offset(sbi) * SECS_PER_MIN;
|
||||
}
|
||||
|
||||
/* Convert linear UNIX date to a EXFAT time/date pair. */
|
||||
|
|
|
@ -1080,6 +1080,7 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
|||
|
||||
exfat_remove_entries(inode, p_dir, oldentry, 0,
|
||||
num_old_entries);
|
||||
ei->dir = *p_dir;
|
||||
ei->entry = newentry;
|
||||
} else {
|
||||
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,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void exfat_update_parent_info(struct exfat_inode_info *ei,
|
||||
struct inode *parent_inode)
|
||||
{
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(parent_inode->i_sb);
|
||||
struct exfat_inode_info *parent_ei = EXFAT_I(parent_inode);
|
||||
loff_t parent_isize = i_size_read(parent_inode);
|
||||
|
||||
/*
|
||||
* the problem that struct exfat_inode_info caches wrong parent info.
|
||||
*
|
||||
* because of flag-mismatch of ei->dir,
|
||||
* there is abnormal traversing cluster chain.
|
||||
*/
|
||||
if (unlikely(parent_ei->flags != ei->dir.flags ||
|
||||
parent_isize != EXFAT_CLU_TO_B(ei->dir.size, sbi) ||
|
||||
parent_ei->start_clu != ei->dir.dir)) {
|
||||
exfat_chain_set(&ei->dir, parent_ei->start_clu,
|
||||
EXFAT_B_TO_CLU_ROUND_UP(parent_isize, sbi),
|
||||
parent_ei->flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* rename or move a old file into a new file */
|
||||
static int __exfat_rename(struct inode *old_parent_inode,
|
||||
struct exfat_inode_info *ei, struct inode *new_parent_inode,
|
||||
|
@ -1219,8 +1198,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
exfat_update_parent_info(ei, old_parent_inode);
|
||||
|
||||
exfat_chain_dup(&olddir, &ei->dir);
|
||||
dentry = ei->entry;
|
||||
|
||||
|
@ -1241,8 +1218,6 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
|||
goto out;
|
||||
}
|
||||
|
||||
exfat_update_parent_info(new_ei, new_parent_inode);
|
||||
|
||||
p_dir = &(new_ei->dir);
|
||||
new_entry = new_ei->entry;
|
||||
ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh);
|
||||
|
|
|
@ -170,7 +170,9 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
|
|||
seq_puts(m, ",discard");
|
||||
if (opts->keep_last_dots)
|
||||
seq_puts(m, ",keep_last_dots");
|
||||
if (opts->time_offset)
|
||||
if (opts->sys_tz)
|
||||
seq_puts(m, ",sys_tz");
|
||||
else if (opts->time_offset)
|
||||
seq_printf(m, ",time_offset=%d", opts->time_offset);
|
||||
return 0;
|
||||
}
|
||||
|
@ -214,6 +216,7 @@ enum {
|
|||
Opt_errors,
|
||||
Opt_discard,
|
||||
Opt_keep_last_dots,
|
||||
Opt_sys_tz,
|
||||
Opt_time_offset,
|
||||
|
||||
/* Deprecated options */
|
||||
|
@ -241,6 +244,7 @@ static const struct fs_parameter_spec exfat_parameters[] = {
|
|||
fsparam_enum("errors", Opt_errors, exfat_param_enums),
|
||||
fsparam_flag("discard", Opt_discard),
|
||||
fsparam_flag("keep_last_dots", Opt_keep_last_dots),
|
||||
fsparam_flag("sys_tz", Opt_sys_tz),
|
||||
fsparam_s32("time_offset", Opt_time_offset),
|
||||
__fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated,
|
||||
NULL),
|
||||
|
@ -298,6 +302,9 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
|||
case Opt_keep_last_dots:
|
||||
opts->keep_last_dots = 1;
|
||||
break;
|
||||
case Opt_sys_tz:
|
||||
opts->sys_tz = 1;
|
||||
break;
|
||||
case Opt_time_offset:
|
||||
/*
|
||||
* Make the limit 24 just in case someone invents something
|
||||
|
|
|
@ -1535,6 +1535,7 @@ int truncate_bdev_range(struct block_device *bdev, fmode_t mode, loff_t lstart,
|
|||
#ifdef CONFIG_BLOCK
|
||||
void invalidate_bdev(struct block_device *bdev);
|
||||
int sync_blockdev(struct block_device *bdev);
|
||||
int sync_blockdev_range(struct block_device *bdev, loff_t lstart, loff_t lend);
|
||||
int sync_blockdev_nowait(struct block_device *bdev);
|
||||
void sync_bdevs(bool wait);
|
||||
void printk_all_partitions(void);
|
||||
|
|
Loading…
Reference in New Issue