Commit b88a1058 authored by Oleksij Rempel's avatar Oleksij Rempel Committed by Linus Torvalds

fat: mark fs as dirty on mount and clean on umount

There is no documented methods to mark FAT as dirty.  Unofficially MS
started to use reserved Byte in boot sector for this purpose, at least
since Win 2000.  With Win 7 user is warned if fs is dirty and asked to
clean it.

Different versions of Win, handle it in different ways, but always have
same meaning:

- Win 2000 and XP, set it on write operations and
  remove it after operation was finnished
- Win 7, set dirty flag on first write and remove it on umount.

We will do it as follows:

- set dirty flag on mount. If fs was initially dirty, warn user,
  remember it and do not do any changes to boot sector.
- clean it on umount. If fs was initially dirty, leave it dirty.
- do not do any thing if fs mounted read-only.
- TODO: leave fs dirty if we found some error after mount.
Signed-off-by: default avatarOleksij Rempel <bug-track@fisher-privat.net>
Signed-off-by: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 6b46419b
...@@ -95,6 +95,8 @@ struct msdos_sb_info { ...@@ -95,6 +95,8 @@ struct msdos_sb_info {
spinlock_t dir_hash_lock; spinlock_t dir_hash_lock;
struct hlist_head dir_hashtable[FAT_HASH_SIZE]; struct hlist_head dir_hashtable[FAT_HASH_SIZE];
unsigned int dirty; /* fs state before mount */
}; };
#define FAT_CACHE_VALID 0 /* special case for valid cache */ #define FAT_CACHE_VALID 0 /* special case for valid cache */
......
...@@ -488,10 +488,59 @@ static void fat_evict_inode(struct inode *inode) ...@@ -488,10 +488,59 @@ static void fat_evict_inode(struct inode *inode)
fat_detach(inode); fat_detach(inode);
} }
static void fat_set_state(struct super_block *sb,
unsigned int set, unsigned int force)
{
struct buffer_head *bh;
struct fat_boot_sector *b;
struct msdos_sb_info *sbi = sb->s_fs_info;
/* do not change any thing if mounted read only */
if ((sb->s_flags & MS_RDONLY) && !force)
return;
/* do not change state if fs was dirty */
if (sbi->dirty) {
/* warn only on set (mount). */
if (set)
fat_msg(sb, KERN_WARNING, "Volume was not properly "
"unmounted. Some data may be corrupt. "
"Please run fsck.");
return;
}
bh = sb_bread(sb, 0);
if (bh == NULL) {
fat_msg(sb, KERN_ERR, "unable to read boot sector "
"to mark fs as dirty");
return;
}
b = (struct fat_boot_sector *) bh->b_data;
if (sbi->fat_bits == 32) {
if (set)
b->fat32.state |= FAT_STATE_DIRTY;
else
b->fat32.state &= ~FAT_STATE_DIRTY;
} else /* fat 16 and 12 */ {
if (set)
b->fat16.state |= FAT_STATE_DIRTY;
else
b->fat16.state &= ~FAT_STATE_DIRTY;
}
mark_buffer_dirty(bh);
sync_dirty_buffer(bh);
brelse(bh);
}
static void fat_put_super(struct super_block *sb) static void fat_put_super(struct super_block *sb)
{ {
struct msdos_sb_info *sbi = MSDOS_SB(sb); struct msdos_sb_info *sbi = MSDOS_SB(sb);
fat_set_state(sb, 0, 0);
iput(sbi->fsinfo_inode); iput(sbi->fsinfo_inode);
iput(sbi->fat_inode); iput(sbi->fat_inode);
...@@ -566,8 +615,18 @@ static void __exit fat_destroy_inodecache(void) ...@@ -566,8 +615,18 @@ static void __exit fat_destroy_inodecache(void)
static int fat_remount(struct super_block *sb, int *flags, char *data) static int fat_remount(struct super_block *sb, int *flags, char *data)
{ {
int new_rdonly;
struct msdos_sb_info *sbi = MSDOS_SB(sb); struct msdos_sb_info *sbi = MSDOS_SB(sb);
*flags |= MS_NODIRATIME | (sbi->options.isvfat ? 0 : MS_NOATIME); *flags |= MS_NODIRATIME | (sbi->options.isvfat ? 0 : MS_NOATIME);
/* make sure we update state on remount. */
new_rdonly = *flags & MS_RDONLY;
if (new_rdonly != (sb->s_flags & MS_RDONLY)) {
if (new_rdonly)
fat_set_state(sb, 0, 0);
else
fat_set_state(sb, 1, 1);
}
return 0; return 0;
} }
...@@ -1362,6 +1421,12 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, ...@@ -1362,6 +1421,12 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
if (sbi->fat_bits != 32) if (sbi->fat_bits != 32)
sbi->fat_bits = (total_clusters > MAX_FAT12) ? 16 : 12; sbi->fat_bits = (total_clusters > MAX_FAT12) ? 16 : 12;
/* some OSes set FAT_STATE_DIRTY and clean it on unmount. */
if (sbi->fat_bits == 32)
sbi->dirty = b->fat32.state & FAT_STATE_DIRTY;
else /* fat 16 or 12 */
sbi->dirty = b->fat16.state & FAT_STATE_DIRTY;
/* check that FAT table does not overflow */ /* check that FAT table does not overflow */
fat_clusters = sbi->fat_length * sb->s_blocksize * 8 / sbi->fat_bits; fat_clusters = sbi->fat_length * sb->s_blocksize * 8 / sbi->fat_bits;
total_clusters = min(total_clusters, fat_clusters - FAT_START_ENT); total_clusters = min(total_clusters, fat_clusters - FAT_START_ENT);
...@@ -1456,6 +1521,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, ...@@ -1456,6 +1521,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
"the device does not support discard"); "the device does not support discard");
} }
fat_set_state(sb, 1, 0);
return 0; return 0;
out_invalid: out_invalid:
......
...@@ -87,6 +87,8 @@ ...@@ -87,6 +87,8 @@
#define IS_FSINFO(x) (le32_to_cpu((x)->signature1) == FAT_FSINFO_SIG1 \ #define IS_FSINFO(x) (le32_to_cpu((x)->signature1) == FAT_FSINFO_SIG1 \
&& le32_to_cpu((x)->signature2) == FAT_FSINFO_SIG2) && le32_to_cpu((x)->signature2) == FAT_FSINFO_SIG2)
#define FAT_STATE_DIRTY 0x01
struct __fat_dirent { struct __fat_dirent {
long d_ino; long d_ino;
__kernel_off_t d_off; __kernel_off_t d_off;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment