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 <kohada.t2@gmail.com> Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
This commit is contained in:
parent
4dc7d35e09
commit
7018ec68f0
|
@ -224,7 +224,8 @@ struct exfat_sb_info {
|
||||||
unsigned int num_FAT_sectors; /* num of FAT sectors */
|
unsigned int num_FAT_sectors; /* num of FAT sectors */
|
||||||
unsigned int root_dir; /* root dir cluster */
|
unsigned int root_dir; /* root dir cluster */
|
||||||
unsigned int dentries_per_clu; /* num of dentries per cluster */
|
unsigned int dentries_per_clu; /* num of dentries per cluster */
|
||||||
unsigned int vol_flag; /* volume dirty flag */
|
unsigned int vol_flags; /* volume flags */
|
||||||
|
unsigned int vol_flags_persistent; /* volume flags to retain */
|
||||||
struct buffer_head *boot_bh; /* buffer_head of BOOT sector */
|
struct buffer_head *boot_bh; /* buffer_head of BOOT sector */
|
||||||
|
|
||||||
unsigned int map_clu; /* allocation bitmap start cluster */
|
unsigned int map_clu; /* allocation bitmap start cluster */
|
||||||
|
@ -380,7 +381,8 @@ static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* super.c */
|
/* super.c */
|
||||||
int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag);
|
int exfat_set_volume_dirty(struct super_block *sb);
|
||||||
|
int exfat_clear_volume_dirty(struct super_block *sb);
|
||||||
|
|
||||||
/* fatent.c */
|
/* fatent.c */
|
||||||
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
|
#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
|
||||||
|
|
|
@ -14,9 +14,8 @@
|
||||||
|
|
||||||
#define EXFAT_MAX_FILE_LEN 255
|
#define EXFAT_MAX_FILE_LEN 255
|
||||||
|
|
||||||
#define VOL_CLEAN 0x0000
|
#define VOLUME_DIRTY 0x0002
|
||||||
#define VOL_DIRTY 0x0002
|
#define MEDIA_FAILURE 0x0004
|
||||||
#define ERR_MEDIUM 0x0004
|
|
||||||
|
|
||||||
#define EXFAT_EOF_CLUSTER 0xFFFFFFFFu
|
#define EXFAT_EOF_CLUSTER 0xFFFFFFFFu
|
||||||
#define EXFAT_BAD_CLUSTER 0xFFFFFFF7u
|
#define EXFAT_BAD_CLUSTER 0xFFFFFFF7u
|
||||||
|
|
|
@ -106,7 +106,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
||||||
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
|
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
exfat_set_volume_dirty(sb);
|
||||||
|
|
||||||
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
|
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
|
||||||
num_clusters_phys =
|
num_clusters_phys =
|
||||||
|
@ -220,7 +220,7 @@ int __exfat_truncate(struct inode *inode, loff_t new_size)
|
||||||
if (exfat_free_cluster(inode, &clu))
|
if (exfat_free_cluster(inode, &clu))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
exfat_clear_volume_dirty(sb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ static int __exfat_write_inode(struct inode *inode, int sync)
|
||||||
if (is_dir && ei->dir.dir == sbi->root_dir && ei->entry == -1)
|
if (is_dir && ei->dir.dir == sbi->root_dir && ei->entry == -1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
exfat_set_volume_dirty(sb);
|
||||||
|
|
||||||
/* get the directory entry of given file or directory */
|
/* get the directory entry of given file or directory */
|
||||||
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES);
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*clu == EXFAT_EOF_CLUSTER) {
|
if (*clu == EXFAT_EOF_CLUSTER) {
|
||||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
exfat_set_volume_dirty(sb);
|
||||||
|
|
||||||
new_clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ?
|
new_clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ?
|
||||||
EXFAT_EOF_CLUSTER : last_clu + 1;
|
EXFAT_EOF_CLUSTER : last_clu + 1;
|
||||||
|
|
|
@ -562,10 +562,10 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
exfat_set_volume_dirty(sb);
|
||||||
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,
|
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,
|
||||||
&info);
|
&info);
|
||||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
exfat_clear_volume_dirty(sb);
|
||||||
if (err)
|
if (err)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
|
@ -834,7 +834,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
num_entries++;
|
num_entries++;
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
|
|
||||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
exfat_set_volume_dirty(sb);
|
||||||
/* update the directory entry */
|
/* update the directory entry */
|
||||||
if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) {
|
if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) {
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
|
@ -843,7 +843,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
|
|
||||||
/* This doesn't modify ei */
|
/* This doesn't modify ei */
|
||||||
ei->dir.dir = DIR_DELETED;
|
ei->dir.dir = DIR_DELETED;
|
||||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
exfat_clear_volume_dirty(sb);
|
||||||
|
|
||||||
inode_inc_iversion(dir);
|
inode_inc_iversion(dir);
|
||||||
dir->i_mtime = dir->i_atime = current_time(dir);
|
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)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
exfat_set_volume_dirty(sb);
|
||||||
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR,
|
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR,
|
||||||
&info);
|
&info);
|
||||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
exfat_clear_volume_dirty(sb);
|
||||||
if (err)
|
if (err)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
|
@ -1001,14 +1001,14 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
num_entries++;
|
num_entries++;
|
||||||
brelse(bh);
|
brelse(bh);
|
||||||
|
|
||||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
exfat_set_volume_dirty(sb);
|
||||||
err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries);
|
err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries);
|
||||||
if (err) {
|
if (err) {
|
||||||
exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err);
|
exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
ei->dir.dir = DIR_DELETED;
|
ei->dir.dir = DIR_DELETED;
|
||||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
exfat_clear_volume_dirty(sb);
|
||||||
|
|
||||||
inode_inc_iversion(dir);
|
inode_inc_iversion(dir);
|
||||||
dir->i_mtime = dir->i_atime = current_time(dir);
|
dir->i_mtime = dir->i_atime = current_time(dir);
|
||||||
|
@ -1300,7 +1300,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
exfat_set_vol_flags(sb, VOL_DIRTY);
|
exfat_set_volume_dirty(sb);
|
||||||
|
|
||||||
if (olddir.dir == newdir.dir)
|
if (olddir.dir == newdir.dir)
|
||||||
ret = exfat_rename_file(new_parent_inode, &olddir, dentry,
|
ret = exfat_rename_file(new_parent_inode, &olddir, dentry,
|
||||||
|
@ -1355,7 +1355,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
||||||
*/
|
*/
|
||||||
new_ei->dir.dir = DIR_DELETED;
|
new_ei->dir.dir = DIR_DELETED;
|
||||||
}
|
}
|
||||||
exfat_set_vol_flags(sb, VOL_CLEAN);
|
exfat_clear_volume_dirty(sb);
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ static int exfat_sync_fs(struct super_block *sb, int wait)
|
||||||
/* If there are some dirty buffers in the bdev inode */
|
/* If there are some dirty buffers in the bdev inode */
|
||||||
mutex_lock(&sbi->s_lock);
|
mutex_lock(&sbi->s_lock);
|
||||||
sync_blockdev(sb->s_bdev);
|
sync_blockdev(sb->s_bdev);
|
||||||
if (exfat_set_vol_flags(sb, VOL_CLEAN))
|
if (exfat_clear_volume_dirty(sb))
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
mutex_unlock(&sbi->s_lock);
|
mutex_unlock(&sbi->s_lock);
|
||||||
return err;
|
return err;
|
||||||
|
@ -96,17 +96,20 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
|
static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags)
|
||||||
{
|
{
|
||||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||||
struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
|
struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data;
|
||||||
bool sync;
|
bool sync;
|
||||||
|
|
||||||
|
/* retain persistent-flags */
|
||||||
|
new_flags |= sbi->vol_flags_persistent;
|
||||||
|
|
||||||
/* flags are not changed */
|
/* flags are not changed */
|
||||||
if (sbi->vol_flag == new_flag)
|
if (sbi->vol_flags == new_flags)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
sbi->vol_flag = new_flag;
|
sbi->vol_flags = new_flags;
|
||||||
|
|
||||||
/* skip updating volume dirty flag,
|
/* skip updating volume dirty flag,
|
||||||
* if this volume has been mounted with read-only
|
* if this volume has been mounted with read-only
|
||||||
|
@ -114,9 +117,9 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
|
||||||
if (sb_rdonly(sb))
|
if (sb_rdonly(sb))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
p_boot->vol_flags = cpu_to_le16(new_flag);
|
p_boot->vol_flags = cpu_to_le16(new_flags);
|
||||||
|
|
||||||
if (new_flag == VOL_DIRTY && !buffer_dirty(sbi->boot_bh))
|
if ((new_flags & VOLUME_DIRTY) && !buffer_dirty(sbi->boot_bh))
|
||||||
sync = true;
|
sync = true;
|
||||||
else
|
else
|
||||||
sync = false;
|
sync = false;
|
||||||
|
@ -129,6 +132,20 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int exfat_set_volume_dirty(struct super_block *sb)
|
||||||
|
{
|
||||||
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||||
|
|
||||||
|
return exfat_set_vol_flags(sb, sbi->vol_flags | VOLUME_DIRTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int exfat_clear_volume_dirty(struct super_block *sb)
|
||||||
|
{
|
||||||
|
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||||
|
|
||||||
|
return exfat_set_vol_flags(sb, sbi->vol_flags & ~VOLUME_DIRTY);
|
||||||
|
}
|
||||||
|
|
||||||
static int exfat_show_options(struct seq_file *m, struct dentry *root)
|
static int exfat_show_options(struct seq_file *m, struct dentry *root)
|
||||||
{
|
{
|
||||||
struct super_block *sb = root->d_sb;
|
struct super_block *sb = root->d_sb;
|
||||||
|
@ -457,7 +474,8 @@ static int exfat_read_boot_sector(struct super_block *sb)
|
||||||
sbi->dentries_per_clu = 1 <<
|
sbi->dentries_per_clu = 1 <<
|
||||||
(sbi->cluster_size_bits - DENTRY_SIZE_BITS);
|
(sbi->cluster_size_bits - DENTRY_SIZE_BITS);
|
||||||
|
|
||||||
sbi->vol_flag = le16_to_cpu(p_boot->vol_flags);
|
sbi->vol_flags = le16_to_cpu(p_boot->vol_flags);
|
||||||
|
sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE);
|
||||||
sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
|
sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER;
|
||||||
sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED;
|
sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED;
|
||||||
|
|
||||||
|
@ -472,9 +490,9 @@ static int exfat_read_boot_sector(struct super_block *sb)
|
||||||
exfat_err(sb, "bogus data start sector");
|
exfat_err(sb, "bogus data start sector");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (sbi->vol_flag & VOL_DIRTY)
|
if (sbi->vol_flags & VOLUME_DIRTY)
|
||||||
exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
|
exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
|
||||||
if (sbi->vol_flag & ERR_MEDIUM)
|
if (sbi->vol_flags & MEDIA_FAILURE)
|
||||||
exfat_warn(sb, "Medium has reported failures. Some data may be lost.");
|
exfat_warn(sb, "Medium has reported failures. Some data may be lost.");
|
||||||
|
|
||||||
/* exFAT file size is limited by a disk volume size */
|
/* exFAT file size is limited by a disk volume size */
|
||||||
|
|
Loading…
Reference in New Issue