Commit b88a105802e9aeb6e234e8106659f5d1271081bb

Authored by Oleksij Rempel
Committed by Linus Torvalds
1 parent 6b46419b04

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: Oleksij Rempel <bug-track@fisher-privat.net>
Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 3 changed files with 70 additions and 0 deletions Side-by-side Diff

... ... @@ -95,6 +95,8 @@
95 95  
96 96 spinlock_t dir_hash_lock;
97 97 struct hlist_head dir_hashtable[FAT_HASH_SIZE];
  98 +
  99 + unsigned int dirty; /* fs state before mount */
98 100 };
99 101  
100 102 #define FAT_CACHE_VALID 0 /* special case for valid cache */
... ... @@ -488,10 +488,59 @@
488 488 fat_detach(inode);
489 489 }
490 490  
  491 +static void fat_set_state(struct super_block *sb,
  492 + unsigned int set, unsigned int force)
  493 +{
  494 + struct buffer_head *bh;
  495 + struct fat_boot_sector *b;
  496 + struct msdos_sb_info *sbi = sb->s_fs_info;
  497 +
  498 + /* do not change any thing if mounted read only */
  499 + if ((sb->s_flags & MS_RDONLY) && !force)
  500 + return;
  501 +
  502 + /* do not change state if fs was dirty */
  503 + if (sbi->dirty) {
  504 + /* warn only on set (mount). */
  505 + if (set)
  506 + fat_msg(sb, KERN_WARNING, "Volume was not properly "
  507 + "unmounted. Some data may be corrupt. "
  508 + "Please run fsck.");
  509 + return;
  510 + }
  511 +
  512 + bh = sb_bread(sb, 0);
  513 + if (bh == NULL) {
  514 + fat_msg(sb, KERN_ERR, "unable to read boot sector "
  515 + "to mark fs as dirty");
  516 + return;
  517 + }
  518 +
  519 + b = (struct fat_boot_sector *) bh->b_data;
  520 +
  521 + if (sbi->fat_bits == 32) {
  522 + if (set)
  523 + b->fat32.state |= FAT_STATE_DIRTY;
  524 + else
  525 + b->fat32.state &= ~FAT_STATE_DIRTY;
  526 + } else /* fat 16 and 12 */ {
  527 + if (set)
  528 + b->fat16.state |= FAT_STATE_DIRTY;
  529 + else
  530 + b->fat16.state &= ~FAT_STATE_DIRTY;
  531 + }
  532 +
  533 + mark_buffer_dirty(bh);
  534 + sync_dirty_buffer(bh);
  535 + brelse(bh);
  536 +}
  537 +
491 538 static void fat_put_super(struct super_block *sb)
492 539 {
493 540 struct msdos_sb_info *sbi = MSDOS_SB(sb);
494 541  
  542 + fat_set_state(sb, 0, 0);
  543 +
495 544 iput(sbi->fsinfo_inode);
496 545 iput(sbi->fat_inode);
497 546  
498 547  
... ... @@ -566,8 +615,18 @@
566 615  
567 616 static int fat_remount(struct super_block *sb, int *flags, char *data)
568 617 {
  618 + int new_rdonly;
569 619 struct msdos_sb_info *sbi = MSDOS_SB(sb);
570 620 *flags |= MS_NODIRATIME | (sbi->options.isvfat ? 0 : MS_NOATIME);
  621 +
  622 + /* make sure we update state on remount. */
  623 + new_rdonly = *flags & MS_RDONLY;
  624 + if (new_rdonly != (sb->s_flags & MS_RDONLY)) {
  625 + if (new_rdonly)
  626 + fat_set_state(sb, 0, 0);
  627 + else
  628 + fat_set_state(sb, 1, 1);
  629 + }
571 630 return 0;
572 631 }
573 632  
... ... @@ -1362,6 +1421,12 @@
1362 1421 if (sbi->fat_bits != 32)
1363 1422 sbi->fat_bits = (total_clusters > MAX_FAT12) ? 16 : 12;
1364 1423  
  1424 + /* some OSes set FAT_STATE_DIRTY and clean it on unmount. */
  1425 + if (sbi->fat_bits == 32)
  1426 + sbi->dirty = b->fat32.state & FAT_STATE_DIRTY;
  1427 + else /* fat 16 or 12 */
  1428 + sbi->dirty = b->fat16.state & FAT_STATE_DIRTY;
  1429 +
1365 1430 /* check that FAT table does not overflow */
1366 1431 fat_clusters = sbi->fat_length * sb->s_blocksize * 8 / sbi->fat_bits;
1367 1432 total_clusters = min(total_clusters, fat_clusters - FAT_START_ENT);
... ... @@ -1456,6 +1521,7 @@
1456 1521 "the device does not support discard");
1457 1522 }
1458 1523  
  1524 + fat_set_state(sb, 1, 0);
1459 1525 return 0;
1460 1526  
1461 1527 out_invalid:
include/uapi/linux/msdos_fs.h
... ... @@ -87,6 +87,8 @@
87 87 #define IS_FSINFO(x) (le32_to_cpu((x)->signature1) == FAT_FSINFO_SIG1 \
88 88 && le32_to_cpu((x)->signature2) == FAT_FSINFO_SIG2)
89 89  
  90 +#define FAT_STATE_DIRTY 0x01
  91 +
90 92 struct __fat_dirent {
91 93 long d_ino;
92 94 __kernel_off_t d_off;