Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tetsuhiro Kohada and committed by
Namjae Jeon
7018ec68 4dc7d35e

+47 -28
+4 -2
fs/exfat/exfat_fs.h
··· 224 224 unsigned int num_FAT_sectors; /* num of FAT sectors */ 225 225 unsigned int root_dir; /* root dir cluster */ 226 226 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 */ 228 229 struct buffer_head *boot_bh; /* buffer_head of BOOT sector */ 229 230 230 231 unsigned int map_clu; /* allocation bitmap start cluster */ ··· 381 380 } 382 381 383 382 /* super.c */ 384 - int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag); 383 + int exfat_set_volume_dirty(struct super_block *sb); 384 + int exfat_clear_volume_dirty(struct super_block *sb); 385 385 386 386 /* fatent.c */ 387 387 #define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
+2 -3
fs/exfat/exfat_raw.h
··· 14 14 15 15 #define EXFAT_MAX_FILE_LEN 255 16 16 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 20 19 21 20 #define EXFAT_EOF_CLUSTER 0xFFFFFFFFu 22 21 #define EXFAT_BAD_CLUSTER 0xFFFFFFF7u
+2 -2
fs/exfat/file.c
··· 106 106 if (ei->type != TYPE_FILE && ei->type != TYPE_DIR) 107 107 return -EPERM; 108 108 109 - exfat_set_vol_flags(sb, VOL_DIRTY); 109 + exfat_set_volume_dirty(sb); 110 110 111 111 num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi); 112 112 num_clusters_phys = ··· 220 220 if (exfat_free_cluster(inode, &clu)) 221 221 return -EIO; 222 222 223 - exfat_set_vol_flags(sb, VOL_CLEAN); 223 + exfat_clear_volume_dirty(sb); 224 224 225 225 return 0; 226 226 }
+2 -2
fs/exfat/inode.c
··· 39 39 if (is_dir && ei->dir.dir == sbi->root_dir && ei->entry == -1) 40 40 return 0; 41 41 42 - exfat_set_vol_flags(sb, VOL_DIRTY); 42 + exfat_set_volume_dirty(sb); 43 43 44 44 /* get the directory entry of given file or directory */ 45 45 es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES); ··· 167 167 } 168 168 169 169 if (*clu == EXFAT_EOF_CLUSTER) { 170 - exfat_set_vol_flags(sb, VOL_DIRTY); 170 + exfat_set_volume_dirty(sb); 171 171 172 172 new_clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ? 173 173 EXFAT_EOF_CLUSTER : last_clu + 1;
+10 -10
fs/exfat/namei.c
··· 562 562 int err; 563 563 564 564 mutex_lock(&EXFAT_SB(sb)->s_lock); 565 - exfat_set_vol_flags(sb, VOL_DIRTY); 565 + exfat_set_volume_dirty(sb); 566 566 err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE, 567 567 &info); 568 - exfat_set_vol_flags(sb, VOL_CLEAN); 568 + exfat_clear_volume_dirty(sb); 569 569 if (err) 570 570 goto unlock; 571 571 ··· 834 834 num_entries++; 835 835 brelse(bh); 836 836 837 - exfat_set_vol_flags(sb, VOL_DIRTY); 837 + exfat_set_volume_dirty(sb); 838 838 /* update the directory entry */ 839 839 if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) { 840 840 err = -EIO; ··· 843 843 844 844 /* This doesn't modify ei */ 845 845 ei->dir.dir = DIR_DELETED; 846 - exfat_set_vol_flags(sb, VOL_CLEAN); 846 + exfat_clear_volume_dirty(sb); 847 847 848 848 inode_inc_iversion(dir); 849 849 dir->i_mtime = dir->i_atime = current_time(dir); ··· 873 873 int err; 874 874 875 875 mutex_lock(&EXFAT_SB(sb)->s_lock); 876 - exfat_set_vol_flags(sb, VOL_DIRTY); 876 + exfat_set_volume_dirty(sb); 877 877 err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR, 878 878 &info); 879 - exfat_set_vol_flags(sb, VOL_CLEAN); 879 + exfat_clear_volume_dirty(sb); 880 880 if (err) 881 881 goto unlock; 882 882 ··· 1001 1001 num_entries++; 1002 1002 brelse(bh); 1003 1003 1004 - exfat_set_vol_flags(sb, VOL_DIRTY); 1004 + exfat_set_volume_dirty(sb); 1005 1005 err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries); 1006 1006 if (err) { 1007 1007 exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err); 1008 1008 goto unlock; 1009 1009 } 1010 1010 ei->dir.dir = DIR_DELETED; 1011 - exfat_set_vol_flags(sb, VOL_CLEAN); 1011 + exfat_clear_volume_dirty(sb); 1012 1012 1013 1013 inode_inc_iversion(dir); 1014 1014 dir->i_mtime = dir->i_atime = current_time(dir); ··· 1300 1300 if (ret) 1301 1301 goto out; 1302 1302 1303 - exfat_set_vol_flags(sb, VOL_DIRTY); 1303 + exfat_set_volume_dirty(sb); 1304 1304 1305 1305 if (olddir.dir == newdir.dir) 1306 1306 ret = exfat_rename_file(new_parent_inode, &olddir, dentry, ··· 1355 1355 */ 1356 1356 new_ei->dir.dir = DIR_DELETED; 1357 1357 } 1358 - exfat_set_vol_flags(sb, VOL_CLEAN); 1358 + exfat_clear_volume_dirty(sb); 1359 1359 out: 1360 1360 return ret; 1361 1361 }
+27 -9
fs/exfat/super.c
··· 63 63 /* If there are some dirty buffers in the bdev inode */ 64 64 mutex_lock(&sbi->s_lock); 65 65 sync_blockdev(sb->s_bdev); 66 - if (exfat_set_vol_flags(sb, VOL_CLEAN)) 66 + if (exfat_clear_volume_dirty(sb)) 67 67 err = -EIO; 68 68 mutex_unlock(&sbi->s_lock); 69 69 return err; ··· 96 96 return 0; 97 97 } 98 98 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) 100 100 { 101 101 struct exfat_sb_info *sbi = EXFAT_SB(sb); 102 102 struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data; 103 103 bool sync; 104 104 105 + /* retain persistent-flags */ 106 + new_flags |= sbi->vol_flags_persistent; 107 + 105 108 /* flags are not changed */ 106 - if (sbi->vol_flag == new_flag) 109 + if (sbi->vol_flags == new_flags) 107 110 return 0; 108 111 109 - sbi->vol_flag = new_flag; 112 + sbi->vol_flags = new_flags; 110 113 111 114 /* skip updating volume dirty flag, 112 115 * if this volume has been mounted with read-only ··· 117 114 if (sb_rdonly(sb)) 118 115 return 0; 119 116 120 - p_boot->vol_flags = cpu_to_le16(new_flag); 117 + p_boot->vol_flags = cpu_to_le16(new_flags); 121 118 122 - if (new_flag == VOL_DIRTY && !buffer_dirty(sbi->boot_bh)) 119 + if ((new_flags & VOLUME_DIRTY) && !buffer_dirty(sbi->boot_bh)) 123 120 sync = true; 124 121 else 125 122 sync = false; ··· 130 127 if (sync) 131 128 sync_dirty_buffer(sbi->boot_bh); 132 129 return 0; 130 + } 131 + 132 + int exfat_set_volume_dirty(struct super_block *sb) 133 + { 134 + struct exfat_sb_info *sbi = EXFAT_SB(sb); 135 + 136 + return exfat_set_vol_flags(sb, sbi->vol_flags | VOLUME_DIRTY); 137 + } 138 + 139 + int exfat_clear_volume_dirty(struct super_block *sb) 140 + { 141 + struct exfat_sb_info *sbi = EXFAT_SB(sb); 142 + 143 + return exfat_set_vol_flags(sb, sbi->vol_flags & ~VOLUME_DIRTY); 133 144 } 134 145 135 146 static int exfat_show_options(struct seq_file *m, struct dentry *root) ··· 474 457 sbi->dentries_per_clu = 1 << 475 458 (sbi->cluster_size_bits - DENTRY_SIZE_BITS); 476 459 477 - sbi->vol_flag = le16_to_cpu(p_boot->vol_flags); 460 + sbi->vol_flags = le16_to_cpu(p_boot->vol_flags); 461 + sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE); 478 462 sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER; 479 463 sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED; 480 464 ··· 490 472 exfat_err(sb, "bogus data start sector"); 491 473 return -EINVAL; 492 474 } 493 - if (sbi->vol_flag & VOL_DIRTY) 475 + if (sbi->vol_flags & VOLUME_DIRTY) 494 476 exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck."); 495 - if (sbi->vol_flag & ERR_MEDIUM) 477 + if (sbi->vol_flags & MEDIA_FAILURE) 496 478 exfat_warn(sb, "Medium has reported failures. Some data may be lost."); 497 479 498 480 /* exFAT file size is limited by a disk volume size */