Skip to content

Commit 7018ec6

Browse files
Tetsuhiro Kohadanamjaejeon
authored andcommitted
exfat: retain 'VolumeFlags' properly
MediaFailure and VolumeDirty should be retained if these are set before mounting. In '3.1.13.3 Media Failure Field' of exfat specification describe: If, upon mounting a volume, the value of this field is 1, implementations which scan the entire volume for media failures and record all failures as "bad" clusters in the FAT (or otherwise resolve media failures) may clear the value of this field to 0. Therefore, We should not clear MediaFailure without scanning volume. In '8.1 Recommended Write Ordering' of exfat specification describe: Clear the value of the VolumeDirty field to 0, if its value prior to the first step was 0. Therefore, We should not clear VolumeDirty after mounting. Also rename ERR_MEDIUM to MEDIA_FAILURE. Signed-off-by: Tetsuhiro Kohada <[email protected]> Signed-off-by: Namjae Jeon <[email protected]>
1 parent 4dc7d35 commit 7018ec6

File tree

6 files changed

+47
-28
lines changed

6 files changed

+47
-28
lines changed

fs/exfat/exfat_fs.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ struct exfat_sb_info {
224224
unsigned int num_FAT_sectors; /* num of FAT sectors */
225225
unsigned int root_dir; /* root dir cluster */
226226
unsigned int dentries_per_clu; /* num of dentries per cluster */
227-
unsigned int vol_flag; /* volume dirty flag */
227+
unsigned int vol_flags; /* volume flags */
228+
unsigned int vol_flags_persistent; /* volume flags to retain */
228229
struct buffer_head *boot_bh; /* buffer_head of BOOT sector */
229230

230231
unsigned int map_clu; /* allocation bitmap start cluster */
@@ -380,7 +381,8 @@ static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
380381
}
381382

382383
/* super.c */
383-
int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag);
384+
int exfat_set_volume_dirty(struct super_block *sb);
385+
int exfat_clear_volume_dirty(struct super_block *sb);
384386

385387
/* fatent.c */
386388
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)

fs/exfat/exfat_raw.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414

1515
#define EXFAT_MAX_FILE_LEN 255
1616

17-
#define VOL_CLEAN 0x0000
18-
#define VOL_DIRTY 0x0002
19-
#define ERR_MEDIUM 0x0004
17+
#define VOLUME_DIRTY 0x0002
18+
#define MEDIA_FAILURE 0x0004
2019

2120
#define EXFAT_EOF_CLUSTER 0xFFFFFFFFu
2221
#define EXFAT_BAD_CLUSTER 0xFFFFFFF7u

fs/exfat/file.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
106106
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
107107
return -EPERM;
108108

109-
exfat_set_vol_flags(sb, VOL_DIRTY);
109+
exfat_set_volume_dirty(sb);
110110

111111
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
112112
num_clusters_phys =
@@ -220,7 +220,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
220220
if (exfat_free_cluster(inode, &clu))
221221
return -EIO;
222222

223-
exfat_set_vol_flags(sb, VOL_CLEAN);
223+
exfat_clear_volume_dirty(sb);
224224

225225
return 0;
226226
}

fs/exfat/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ static int __exfat_write_inode(struct inode *inode, int sync)
3939
if (is_dir && ei->dir.dir == sbi->root_dir && ei->entry == -1)
4040
return 0;
4141

42-
exfat_set_vol_flags(sb, VOL_DIRTY);
42+
exfat_set_volume_dirty(sb);
4343

4444
/* get the directory entry of given file or directory */
4545
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES);
@@ -167,7 +167,7 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
167167
}
168168

169169
if (*clu == EXFAT_EOF_CLUSTER) {
170-
exfat_set_vol_flags(sb, VOL_DIRTY);
170+
exfat_set_volume_dirty(sb);
171171

172172
new_clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ?
173173
EXFAT_EOF_CLUSTER : last_clu + 1;

fs/exfat/namei.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -562,10 +562,10 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
562562
int err;
563563

564564
mutex_lock(&EXFAT_SB(sb)->s_lock);
565-
exfat_set_vol_flags(sb, VOL_DIRTY);
565+
exfat_set_volume_dirty(sb);
566566
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,
567567
&info);
568-
exfat_set_vol_flags(sb, VOL_CLEAN);
568+
exfat_clear_volume_dirty(sb);
569569
if (err)
570570
goto unlock;
571571

@@ -834,7 +834,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
834834
num_entries++;
835835
brelse(bh);
836836

837-
exfat_set_vol_flags(sb, VOL_DIRTY);
837+
exfat_set_volume_dirty(sb);
838838
/* update the directory entry */
839839
if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) {
840840
err = -EIO;
@@ -843,7 +843,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
843843

844844
/* This doesn't modify ei */
845845
ei->dir.dir = DIR_DELETED;
846-
exfat_set_vol_flags(sb, VOL_CLEAN);
846+
exfat_clear_volume_dirty(sb);
847847

848848
inode_inc_iversion(dir);
849849
dir->i_mtime = dir->i_atime = current_time(dir);
@@ -873,10 +873,10 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
873873
int err;
874874

875875
mutex_lock(&EXFAT_SB(sb)->s_lock);
876-
exfat_set_vol_flags(sb, VOL_DIRTY);
876+
exfat_set_volume_dirty(sb);
877877
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR,
878878
&info);
879-
exfat_set_vol_flags(sb, VOL_CLEAN);
879+
exfat_clear_volume_dirty(sb);
880880
if (err)
881881
goto unlock;
882882

@@ -1001,14 +1001,14 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
10011001
num_entries++;
10021002
brelse(bh);
10031003

1004-
exfat_set_vol_flags(sb, VOL_DIRTY);
1004+
exfat_set_volume_dirty(sb);
10051005
err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries);
10061006
if (err) {
10071007
exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err);
10081008
goto unlock;
10091009
}
10101010
ei->dir.dir = DIR_DELETED;
1011-
exfat_set_vol_flags(sb, VOL_CLEAN);
1011+
exfat_clear_volume_dirty(sb);
10121012

10131013
inode_inc_iversion(dir);
10141014
dir->i_mtime = dir->i_atime = current_time(dir);
@@ -1300,7 +1300,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
13001300
if (ret)
13011301
goto out;
13021302

1303-
exfat_set_vol_flags(sb, VOL_DIRTY);
1303+
exfat_set_volume_dirty(sb);
13041304

13051305
if (olddir.dir == newdir.dir)
13061306
ret = exfat_rename_file(new_parent_inode, &olddir, dentry,
@@ -1355,7 +1355,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
13551355
*/
13561356
new_ei->dir.dir = DIR_DELETED;
13571357
}
1358-
exfat_set_vol_flags(sb, VOL_CLEAN);
1358+
exfat_clear_volume_dirty(sb);
13591359
out:
13601360
return ret;
13611361
}

fs/exfat/super.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ static int exfat_sync_fs(struct super_block *sb, int wait)
6363
/* If there are some dirty buffers in the bdev inode */
6464
mutex_lock(&sbi->s_lock);
6565
sync_blockdev(sb->s_bdev);
66-
if (exfat_set_vol_flags(sb, VOL_CLEAN))
66+
if (exfat_clear_volume_dirty(sb))
6767
err = -EIO;
6868
mutex_unlock(&sbi->s_lock);
6969
return err;
@@ -96,27 +96,30 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
9696
return 0;
9797
}
9898

99-
int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
99+
static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags)
100100
{
101101
struct exfat_sb_info *sbi = EXFAT_SB(sb);
102102
struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
103103
bool sync;
104104

105+
/* retain persistent-flags */
106+
new_flags |= sbi->vol_flags_persistent;
107+
105108
/* flags are not changed */
106-
if (sbi->vol_flag == new_flag)
109+
if (sbi->vol_flags == new_flags)
107110
return 0;
108111

109-
sbi->vol_flag = new_flag;
112+
sbi->vol_flags = new_flags;
110113

111114
/* skip updating volume dirty flag,
112115
* if this volume has been mounted with read-only
113116
*/
114117
if (sb_rdonly(sb))
115118
return 0;
116119

117-
p_boot->vol_flags = cpu_to_le16(new_flag);
120+
p_boot->vol_flags = cpu_to_le16(new_flags);
118121

119-
if (new_flag == VOL_DIRTY && !buffer_dirty(sbi->boot_bh))
122+
if ((new_flags & VOLUME_DIRTY) && !buffer_dirty(sbi->boot_bh))
120123
sync = true;
121124
else
122125
sync = false;
@@ -129,6 +132,20 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
129132
return 0;
130133
}
131134

135+
int exfat_set_volume_dirty(struct super_block *sb)
136+
{
137+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
138+
139+
return exfat_set_vol_flags(sb, sbi->vol_flags | VOLUME_DIRTY);
140+
}
141+
142+
int exfat_clear_volume_dirty(struct super_block *sb)
143+
{
144+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
145+
146+
return exfat_set_vol_flags(sb, sbi->vol_flags & ~VOLUME_DIRTY);
147+
}
148+
132149
static int exfat_show_options(struct seq_file *m, struct dentry *root)
133150
{
134151
struct super_block *sb = root->d_sb;
@@ -457,7 +474,8 @@ static int exfat_read_boot_sector(struct super_block *sb)
457474
sbi->dentries_per_clu = 1 <<
458475
(sbi->cluster_size_bits - DENTRY_SIZE_BITS);
459476

460-
sbi->vol_flag = le16_to_cpu(p_boot->vol_flags);
477+
sbi->vol_flags = le16_to_cpu(p_boot->vol_flags);
478+
sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE);
461479
sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
462480
sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED;
463481

@@ -472,9 +490,9 @@ static int exfat_read_boot_sector(struct super_block *sb)
472490
exfat_err(sb, "bogus data start sector");
473491
return -EINVAL;
474492
}
475-
if (sbi->vol_flag & VOL_DIRTY)
493+
if (sbi->vol_flags & VOLUME_DIRTY)
476494
exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
477-
if (sbi->vol_flag & ERR_MEDIUM)
495+
if (sbi->vol_flags & MEDIA_FAILURE)
478496
exfat_warn(sb, "Medium has reported failures. Some data may be lost.");
479497

480498
/* exFAT file size is limited by a disk volume size */

0 commit comments

Comments
 (0)