Merge git://git.kernel.org/pub/scm/linux/kernel/git/hirofumi/fatfs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Jun 2009 18:29:44 +0000 (11:29 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 16 Jun 2009 18:29:44 +0000 (11:29 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/hirofumi/fatfs-2.6:
  fat: split fat_generic_ioctl
  FAT: add 'errors' mount option

1  2 
Documentation/filesystems/vfat.txt
fs/fat/dir.c
fs/fat/fat.h
fs/fat/fatent.c
fs/fat/file.c
fs/fat/inode.c
fs/fat/namei_msdos.c
fs/fat/namei_vfat.c

@@@ -124,14 -124,19 +124,19 @@@ sys_immutable -- If set, ATTR_SYS attri
  flush         -- If set, the filesystem will try to flush to disk more
                 early than normal. Not set by default.
  
 -rodir       -- FAT has the ATTR_RO (read-only) attribute. But on Windows,
 -               the ATTR_RO of the directory will be just ignored actually,
 -               and is used by only applications as flag. E.g. it's setted
 -               for the customized folder.
 +rodir       -- FAT has the ATTR_RO (read-only) attribute. On Windows,
 +               the ATTR_RO of the directory will just be ignored,
 +               and is used only by applications as a flag (e.g. it's set
 +               for the customized folder).
  
                 If you want to use ATTR_RO as read-only flag even for
                 the directory, set this option.
  
+ errors=panic|continue|remount-ro
+             -- specify FAT behavior on critical errors: panic, continue
+                without doing anything or remount the partition in
+                read-only mode (default behavior).
  <bool>: 0,1,yes,no,true,false
  
  TODO
diff --combined fs/fat/dir.c
@@@ -840,7 -840,7 +840,7 @@@ const struct file_operations fat_dir_op
  #ifdef CONFIG_COMPAT
        .compat_ioctl   = fat_compat_dir_ioctl,
  #endif
 -      .fsync          = file_fsync,
 +      .fsync          = fat_file_fsync,
  };
  
  static int fat_get_short_entry(struct inode *dir, loff_t *pos,
@@@ -967,7 -967,7 +967,7 @@@ static int __fat_remove_entries(struct 
                        de++;
                        nr_slots--;
                }
 -              mark_buffer_dirty(bh);
 +              mark_buffer_dirty_inode(bh, dir);
                if (IS_DIRSYNC(dir))
                        err = sync_dirty_buffer(bh);
                brelse(bh);
@@@ -1001,7 -1001,7 +1001,7 @@@ int fat_remove_entries(struct inode *di
                de--;
                nr_slots--;
        }
 -      mark_buffer_dirty(bh);
 +      mark_buffer_dirty_inode(bh, dir);
        if (IS_DIRSYNC(dir))
                err = sync_dirty_buffer(bh);
        brelse(bh);
@@@ -1051,7 -1051,7 +1051,7 @@@ static int fat_zeroed_cluster(struct in
                }
                memset(bhs[n]->b_data, 0, sb->s_blocksize);
                set_buffer_uptodate(bhs[n]);
 -              mark_buffer_dirty(bhs[n]);
 +              mark_buffer_dirty_inode(bhs[n], dir);
  
                n++;
                blknr++;
@@@ -1131,7 -1131,7 +1131,7 @@@ int fat_alloc_new_dir(struct inode *dir
        de[0].size = de[1].size = 0;
        memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de));
        set_buffer_uptodate(bhs[0]);
 -      mark_buffer_dirty(bhs[0]);
 +      mark_buffer_dirty_inode(bhs[0], dir);
  
        err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE);
        if (err)
@@@ -1193,7 -1193,7 +1193,7 @@@ static int fat_add_new_entries(struct i
                        slots += copy;
                        size -= copy;
                        set_buffer_uptodate(bhs[n]);
 -                      mark_buffer_dirty(bhs[n]);
 +                      mark_buffer_dirty_inode(bhs[n], dir);
                        if (!size)
                                break;
                        n++;
@@@ -1293,7 -1293,7 +1293,7 @@@ found
                for (i = 0; i < long_bhs; i++) {
                        int copy = min_t(int, sb->s_blocksize - offset, size);
                        memcpy(bhs[i]->b_data + offset, slots, copy);
 -                      mark_buffer_dirty(bhs[i]);
 +                      mark_buffer_dirty_inode(bhs[i], dir);
                        offset = 0;
                        slots += copy;
                        size -= copy;
                        /* Fill the short name slot. */
                        int copy = min_t(int, sb->s_blocksize - offset, size);
                        memcpy(bhs[i]->b_data + offset, slots, copy);
 -                      mark_buffer_dirty(bhs[i]);
 +                      mark_buffer_dirty_inode(bhs[i], dir);
                        if (IS_DIRSYNC(dir))
                                err = sync_dirty_buffer(bhs[i]);
                }
                        goto error_remove;
                }
                if (dir->i_size & (sbi->cluster_size - 1)) {
-                       fat_fs_panic(sb, "Odd directory size");
+                       fat_fs_error(sb, "Odd directory size");
                        dir->i_size = (dir->i_size + sbi->cluster_size - 1)
                                & ~((loff_t)sbi->cluster_size - 1);
                }
diff --combined fs/fat/fat.h
  #define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */
  #define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */
  
+ #define FAT_ERRORS_CONT               1      /* ignore error and continue */
+ #define FAT_ERRORS_PANIC      2      /* panic on error */
+ #define FAT_ERRORS_RO         3      /* remount r/o on error */
  struct fat_mount_options {
        uid_t fs_uid;
        gid_t fs_gid;
@@@ -26,6 -30,7 +30,7 @@@
        char *iocharset;          /* Charset used for filename input/display */
        unsigned short shortname; /* flags for shortname display/create rule */
        unsigned char name_check; /* r = relaxed, n = normal, s = strict */
+       unsigned char errors;     /* On error: continue, panic, remount-ro */
        unsigned short allow_utime;/* permission for setting the [am]time */
        unsigned quiet:1,         /* set = fake successful chmods and chowns */
                 showexec:1,      /* set = only set x bit for com/exe/bat */
@@@ -74,7 -79,6 +79,7 @@@ struct msdos_sb_info 
  
        int fatent_shift;
        struct fatent_operations *fatent_ops;
 +      struct inode *fat_inode;
  
        spinlock_t inode_hash_lock;
        struct hlist_head inode_hashtable[FAT_HASH_SIZE];
@@@ -252,7 -256,6 +257,7 @@@ struct fat_entry 
        } u;
        int nr_bhs;
        struct buffer_head *bhs[2];
 +      struct inode *fat_inode;
  };
  
  static inline void fatent_init(struct fat_entry *fatent)
        fatent->entry = 0;
        fatent->u.ent32_p = NULL;
        fatent->bhs[0] = fatent->bhs[1] = NULL;
 +      fatent->fat_inode = NULL;
  }
  
  static inline void fatent_set_entry(struct fat_entry *fatent, int entry)
@@@ -278,7 -280,6 +283,7 @@@ static inline void fatent_brelse(struc
                brelse(fatent->bhs[i]);
        fatent->nr_bhs = 0;
        fatent->bhs[0] = fatent->bhs[1] = NULL;
 +      fatent->fat_inode = NULL;
  }
  
  extern void fat_ent_access_init(struct super_block *sb);
@@@ -300,8 -301,6 +305,8 @@@ extern int fat_setattr(struct dentry * 
  extern void fat_truncate(struct inode *inode);
  extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry,
                       struct kstat *stat);
 +extern int fat_file_fsync(struct file *file, struct dentry *dentry,
 +                        int datasync);
  
  /* fat/inode.c */
  extern void fat_attach(struct inode *inode, loff_t i_pos);
@@@ -316,7 -315,7 +321,7 @@@ extern int fat_fill_super(struct super_
  extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
                            struct inode *i2);
  /* fat/misc.c */
- extern void fat_fs_panic(struct super_block *s, const char *fmt, ...)
+ extern void fat_fs_error(struct super_block *s, const char *fmt, ...)
        __attribute__ ((format (printf, 2, 3))) __cold;
  extern void fat_clusters_flush(struct super_block *sb);
  extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
diff --combined fs/fat/fatent.c
@@@ -73,8 -73,6 +73,8 @@@ static int fat12_ent_bread(struct super
        struct buffer_head **bhs = fatent->bhs;
  
        WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
 +      fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
 +
        bhs[0] = sb_bread(sb, blocknr);
        if (!bhs[0])
                goto err;
@@@ -105,7 -103,6 +105,7 @@@ static int fat_ent_bread(struct super_b
        struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
  
        WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
 +      fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
        fatent->bhs[0] = sb_bread(sb, blocknr);
        if (!fatent->bhs[0]) {
                printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
@@@ -170,9 -167,9 +170,9 @@@ static void fat12_ent_put(struct fat_en
        }
        spin_unlock(&fat12_entry_lock);
  
 -      mark_buffer_dirty(fatent->bhs[0]);
 +      mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
        if (fatent->nr_bhs == 2)
 -              mark_buffer_dirty(fatent->bhs[1]);
 +              mark_buffer_dirty_inode(fatent->bhs[1], fatent->fat_inode);
  }
  
  static void fat16_ent_put(struct fat_entry *fatent, int new)
                new = EOF_FAT16;
  
        *fatent->u.ent16_p = cpu_to_le16(new);
 -      mark_buffer_dirty(fatent->bhs[0]);
 +      mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
  }
  
  static void fat32_ent_put(struct fat_entry *fatent, int new)
        WARN_ON(new & 0xf0000000);
        new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff;
        *fatent->u.ent32_p = cpu_to_le32(new);
 -      mark_buffer_dirty(fatent->bhs[0]);
 +      mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
  }
  
  static int fat12_ent_next(struct fat_entry *fatent)
@@@ -348,7 -345,7 +348,7 @@@ int fat_ent_read(struct inode *inode, s
  
        if (entry < FAT_START_ENT || sbi->max_cluster <= entry) {
                fatent_brelse(fatent);
-               fat_fs_panic(sb, "invalid access to FAT (entry 0x%08x)", entry);
+               fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry);
                return -EIO;
        }
  
@@@ -384,7 -381,7 +384,7 @@@ static int fat_mirror_bhs(struct super_
                        }
                        memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
                        set_buffer_uptodate(c_bh);
 -                      mark_buffer_dirty(c_bh);
 +                      mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
                        if (sb->s_flags & MS_SYNCHRONOUS)
                                err = sync_dirty_buffer(c_bh);
                        brelse(c_bh);
@@@ -560,7 -557,7 +560,7 @@@ int fat_free_clusters(struct inode *ino
                        err = cluster;
                        goto error;
                } else if (cluster == FAT_ENT_FREE) {
-                       fat_fs_panic(sb, "%s: deleting FAT entry beyond EOF",
+                       fat_fs_error(sb, "%s: deleting FAT entry beyond EOF",
                                     __func__);
                        err = -EIO;
                        goto error;
diff --combined fs/fat/file.c
  #include <linux/security.h>
  #include "fat.h"
  
- int fat_generic_ioctl(struct inode *inode, struct file *filp,
-                     unsigned int cmd, unsigned long arg)
+ static int fat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr)
+ {
+       u32 attr;
+       mutex_lock(&inode->i_mutex);
+       attr = fat_make_attrs(inode);
+       mutex_unlock(&inode->i_mutex);
+       return put_user(attr, user_attr);
+ }
+ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
  {
+       struct inode *inode = file->f_path.dentry->d_inode;
        struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
-       u32 __user *user_attr = (u32 __user *)arg;
+       int is_dir = S_ISDIR(inode->i_mode);
+       u32 attr, oldattr;
+       struct iattr ia;
+       int err;
  
-       switch (cmd) {
-       case FAT_IOCTL_GET_ATTRIBUTES:
-       {
-               u32 attr;
+       err = get_user(attr, user_attr);
+       if (err)
+               goto out;
  
-               mutex_lock(&inode->i_mutex);
-               attr = fat_make_attrs(inode);
-               mutex_unlock(&inode->i_mutex);
+       mutex_lock(&inode->i_mutex);
+       err = mnt_want_write(file->f_path.mnt);
+       if (err)
+               goto out_unlock_inode;
+       /*
+        * ATTR_VOLUME and ATTR_DIR cannot be changed; this also
+        * prevents the user from turning us into a VFAT
+        * longname entry.  Also, we obviously can't set
+        * any of the NTFS attributes in the high 24 bits.
+        */
+       attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR);
+       /* Merge in ATTR_VOLUME and ATTR_DIR */
+       attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
+               (is_dir ? ATTR_DIR : 0);
+       oldattr = fat_make_attrs(inode);
+       /* Equivalent to a chmod() */
+       ia.ia_valid = ATTR_MODE | ATTR_CTIME;
+       ia.ia_ctime = current_fs_time(inode->i_sb);
+       if (is_dir)
+               ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO);
+       else {
+               ia.ia_mode = fat_make_mode(sbi, attr,
+                       S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO));
+       }
  
-               return put_user(attr, user_attr);
+       /* The root directory has no attributes */
+       if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
+               err = -EINVAL;
+               goto out_drop_write;
        }
-       case FAT_IOCTL_SET_ATTRIBUTES:
-       {
-               u32 attr, oldattr;
-               int err, is_dir = S_ISDIR(inode->i_mode);
-               struct iattr ia;
  
-               err = get_user(attr, user_attr);
-               if (err)
-                       return err;
+       if (sbi->options.sys_immutable &&
+           ((attr | oldattr) & ATTR_SYS) &&
+           !capable(CAP_LINUX_IMMUTABLE)) {
+               err = -EPERM;
+               goto out_drop_write;
+       }
  
-               mutex_lock(&inode->i_mutex);
-               err = mnt_want_write(filp->f_path.mnt);
-               if (err)
-                       goto up_no_drop_write;
-               /*
-                * ATTR_VOLUME and ATTR_DIR cannot be changed; this also
-                * prevents the user from turning us into a VFAT
-                * longname entry.  Also, we obviously can't set
-                * any of the NTFS attributes in the high 24 bits.
-                */
-               attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR);
-               /* Merge in ATTR_VOLUME and ATTR_DIR */
-               attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
-                       (is_dir ? ATTR_DIR : 0);
-               oldattr = fat_make_attrs(inode);
-               /* Equivalent to a chmod() */
-               ia.ia_valid = ATTR_MODE | ATTR_CTIME;
-               ia.ia_ctime = current_fs_time(inode->i_sb);
-               if (is_dir)
-                       ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO);
-               else {
-                       ia.ia_mode = fat_make_mode(sbi, attr,
-                               S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO));
-               }
+       /*
+        * The security check is questionable...  We single
+        * out the RO attribute for checking by the security
+        * module, just because it maps to a file mode.
+        */
+       err = security_inode_setattr(file->f_path.dentry, &ia);
+       if (err)
+               goto out_drop_write;
  
-               /* The root directory has no attributes */
-               if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
-                       err = -EINVAL;
-                       goto up;
-               }
+       /* This MUST be done before doing anything irreversible... */
+       err = fat_setattr(file->f_path.dentry, &ia);
+       if (err)
+               goto out_drop_write;
+       fsnotify_change(file->f_path.dentry, ia.ia_valid);
+       if (sbi->options.sys_immutable) {
+               if (attr & ATTR_SYS)
+                       inode->i_flags |= S_IMMUTABLE;
+               else
+                       inode->i_flags &= S_IMMUTABLE;
+       }
  
-               if (sbi->options.sys_immutable) {
-                       if ((attr | oldattr) & ATTR_SYS) {
-                               if (!capable(CAP_LINUX_IMMUTABLE)) {
-                                       err = -EPERM;
-                                       goto up;
-                               }
-                       }
-               }
+       fat_save_attrs(inode, attr);
+       mark_inode_dirty(inode);
+ out_drop_write:
+       mnt_drop_write(file->f_path.mnt);
+ out_unlock_inode:
+       mutex_unlock(&inode->i_mutex);
+ out:
+       return err;
+ }
  
-               /*
-                * The security check is questionable...  We single
-                * out the RO attribute for checking by the security
-                * module, just because it maps to a file mode.
-                */
-               err = security_inode_setattr(filp->f_path.dentry, &ia);
-               if (err)
-                       goto up;
-               /* This MUST be done before doing anything irreversible... */
-               err = fat_setattr(filp->f_path.dentry, &ia);
-               if (err)
-                       goto up;
-               fsnotify_change(filp->f_path.dentry, ia.ia_valid);
-               if (sbi->options.sys_immutable) {
-                       if (attr & ATTR_SYS)
-                               inode->i_flags |= S_IMMUTABLE;
-                       else
-                               inode->i_flags &= S_IMMUTABLE;
-               }
+ int fat_generic_ioctl(struct inode *inode, struct file *filp,
+                     unsigned int cmd, unsigned long arg)
+ {
+       u32 __user *user_attr = (u32 __user *)arg;
  
-               fat_save_attrs(inode, attr);
-               mark_inode_dirty(inode);
- up:
-               mnt_drop_write(filp->f_path.mnt);
- up_no_drop_write:
-               mutex_unlock(&inode->i_mutex);
-               return err;
-       }
+       switch (cmd) {
+       case FAT_IOCTL_GET_ATTRIBUTES:
+               return fat_ioctl_get_attributes(inode, user_attr);
+       case FAT_IOCTL_SET_ATTRIBUTES:
+               return fat_ioctl_set_attributes(filp, user_attr);
        default:
                return -ENOTTY; /* Inappropriate ioctl for device */
        }
@@@ -133,18 -139,6 +139,18 @@@ static int fat_file_release(struct inod
        return 0;
  }
  
 +int fat_file_fsync(struct file *filp, struct dentry *dentry, int datasync)
 +{
 +      struct inode *inode = dentry->d_inode;
 +      int res, err;
 +
 +      res = simple_fsync(filp, dentry, datasync);
 +      err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping);
 +
 +      return res ? res : err;
 +}
 +
 +
  const struct file_operations fat_file_operations = {
        .llseek         = generic_file_llseek,
        .read           = do_sync_read,
        .mmap           = generic_file_mmap,
        .release        = fat_file_release,
        .ioctl          = fat_generic_ioctl,
 -      .fsync          = file_fsync,
 +      .fsync          = fat_file_fsync,
        .splice_read    = generic_file_splice_read,
  };
  
@@@ -225,7 -219,7 +231,7 @@@ static int fat_free(struct inode *inode
                        fatent_brelse(&fatent);
                        return 0;
                } else if (ret == FAT_ENT_FREE) {
-                       fat_fs_panic(sb,
+                       fat_fs_error(sb,
                                     "%s: invalid cluster chain (i_pos %lld)",
                                     __func__, MSDOS_I(inode)->i_pos);
                        ret = -EIO;
diff --combined fs/fat/inode.c
@@@ -76,7 -76,7 +76,7 @@@ static inline int __fat_get_block(struc
                return 0;
  
        if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
-               fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)",
+               fat_fs_error(sb, "corrupted file size (i_pos %lld, %lld)",
                        MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
                return -EIO;
        }
@@@ -441,35 -441,16 +441,35 @@@ static void fat_clear_inode(struct inod
  
  static void fat_write_super(struct super_block *sb)
  {
 +      lock_super(sb);
        sb->s_dirt = 0;
  
        if (!(sb->s_flags & MS_RDONLY))
                fat_clusters_flush(sb);
 +      unlock_super(sb);
 +}
 +
 +static int fat_sync_fs(struct super_block *sb, int wait)
 +{
 +      lock_super(sb);
 +      fat_clusters_flush(sb);
 +      sb->s_dirt = 0;
 +      unlock_super(sb);
 +
 +      return 0;
  }
  
  static void fat_put_super(struct super_block *sb)
  {
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
  
 +      lock_kernel();
 +
 +      if (sb->s_dirt)
 +              fat_write_super(sb);
 +
 +      iput(sbi->fat_inode);
 +
        if (sbi->nls_disk) {
                unload_nls(sbi->nls_disk);
                sbi->nls_disk = NULL;
  
        sb->s_fs_info = NULL;
        kfree(sbi);
 +
 +      unlock_kernel();
  }
  
  static struct kmem_cache *fat_inode_cachep;
@@@ -653,7 -632,6 +653,7 @@@ static const struct super_operations fa
        .delete_inode   = fat_delete_inode,
        .put_super      = fat_put_super,
        .write_super    = fat_write_super,
 +      .sync_fs        = fat_sync_fs,
        .statfs         = fat_statfs,
        .clear_inode    = fat_clear_inode,
        .remount_fs     = fat_remount,
@@@ -856,6 -834,12 +856,12 @@@ static int fat_show_options(struct seq_
                seq_puts(m, ",flush");
        if (opts->tz_utc)
                seq_puts(m, ",tz=UTC");
+       if (opts->errors == FAT_ERRORS_CONT)
+               seq_puts(m, ",errors=continue");
+       else if (opts->errors == FAT_ERRORS_PANIC)
+               seq_puts(m, ",errors=panic");
+       else
+               seq_puts(m, ",errors=remount-ro");
  
        return 0;
  }
@@@ -868,7 -852,8 +874,8 @@@ enum 
        Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
        Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
        Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
-       Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err,
+       Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont,
+       Opt_err_panic, Opt_err_ro, Opt_err,
  };
  
  static const match_table_t fat_tokens = {
        {Opt_showexec, "showexec"},
        {Opt_debug, "debug"},
        {Opt_immutable, "sys_immutable"},
+       {Opt_flush, "flush"},
+       {Opt_tz_utc, "tz=UTC"},
+       {Opt_err_cont, "errors=continue"},
+       {Opt_err_panic, "errors=panic"},
+       {Opt_err_ro, "errors=remount-ro"},
        {Opt_obsolate, "conv=binary"},
        {Opt_obsolate, "conv=text"},
        {Opt_obsolate, "conv=auto"},
        {Opt_obsolate, "cvf_format=%20s"},
        {Opt_obsolate, "cvf_options=%100s"},
        {Opt_obsolate, "posix"},
-       {Opt_flush, "flush"},
-       {Opt_tz_utc, "tz=UTC"},
        {Opt_err, NULL},
  };
  static const match_table_t msdos_tokens = {
@@@ -973,6 -961,7 +983,7 @@@ static int parse_options(char *options
        opts->numtail = 1;
        opts->usefree = opts->nocase = 0;
        opts->tz_utc = 0;
+       opts->errors = FAT_ERRORS_RO;
        *debug = 0;
  
        if (!options)
                case Opt_tz_utc:
                        opts->tz_utc = 1;
                        break;
+               case Opt_err_cont:
+                       opts->errors = FAT_ERRORS_CONT;
+                       break;
+               case Opt_err_panic:
+                       opts->errors = FAT_ERRORS_PANIC;
+                       break;
+               case Opt_err_ro:
+                       opts->errors = FAT_ERRORS_RO;
+                       break;
  
                /* msdos specific */
                case Opt_dots:
@@@ -1196,7 -1194,7 +1216,7 @@@ static int fat_read_root(struct inode *
  int fat_fill_super(struct super_block *sb, void *data, int silent,
                   const struct inode_operations *fs_dir_inode_ops, int isvfat)
  {
 -      struct inode *root_inode = NULL;
 +      struct inode *root_inode = NULL, *fat_inode = NULL;
        struct buffer_head *bh;
        struct fat_boot_sector *b;
        struct msdos_sb_info *sbi;
        }
  
        error = -ENOMEM;
 +      fat_inode = new_inode(sb);
 +      if (!fat_inode)
 +              goto out_fail;
 +      MSDOS_I(fat_inode)->i_pos = 0;
 +      sbi->fat_inode = fat_inode;
        root_inode = new_inode(sb);
        if (!root_inode)
                goto out_fail;
@@@ -1466,8 -1459,6 +1486,8 @@@ out_invalid
                       " on dev %s.\n", sb->s_id);
  
  out_fail:
 +      if (fat_inode)
 +              iput(fat_inode);
        if (root_inode)
                iput(root_inode);
        if (sbi->nls_io)
diff --combined fs/fat/namei_msdos.c
@@@ -544,7 -544,7 +544,7 @@@ static int do_msdos_rename(struct inod
                int start = MSDOS_I(new_dir)->i_logstart;
                dotdot_de->start = cpu_to_le16(start);
                dotdot_de->starthi = cpu_to_le16(start >> 16);
 -              mark_buffer_dirty(dotdot_bh);
 +              mark_buffer_dirty_inode(dotdot_bh, old_inode);
                if (IS_DIRSYNC(new_dir)) {
                        err = sync_dirty_buffer(dotdot_bh);
                        if (err)
@@@ -586,7 -586,7 +586,7 @@@ error_dotdot
                int start = MSDOS_I(old_dir)->i_logstart;
                dotdot_de->start = cpu_to_le16(start);
                dotdot_de->starthi = cpu_to_le16(start >> 16);
 -              mark_buffer_dirty(dotdot_bh);
 +              mark_buffer_dirty_inode(dotdot_bh, old_inode);
                corrupt |= sync_dirty_buffer(dotdot_bh);
        }
  error_inode:
                sinfo.bh = NULL;
        }
        if (corrupt < 0) {
-               fat_fs_panic(new_dir->i_sb,
+               fat_fs_error(new_dir->i_sb,
                             "%s: Filesystem corrupted (i_pos %lld)",
                             __func__, sinfo.i_pos);
        }
diff --combined fs/fat/namei_vfat.c
@@@ -965,7 -965,7 +965,7 @@@ static int vfat_rename(struct inode *ol
                int start = MSDOS_I(new_dir)->i_logstart;
                dotdot_de->start = cpu_to_le16(start);
                dotdot_de->starthi = cpu_to_le16(start >> 16);
 -              mark_buffer_dirty(dotdot_bh);
 +              mark_buffer_dirty_inode(dotdot_bh, old_inode);
                if (IS_DIRSYNC(new_dir)) {
                        err = sync_dirty_buffer(dotdot_bh);
                        if (err)
@@@ -1009,7 -1009,7 +1009,7 @@@ error_dotdot
                int start = MSDOS_I(old_dir)->i_logstart;
                dotdot_de->start = cpu_to_le16(start);
                dotdot_de->starthi = cpu_to_le16(start >> 16);
 -              mark_buffer_dirty(dotdot_bh);
 +              mark_buffer_dirty_inode(dotdot_bh, old_inode);
                corrupt |= sync_dirty_buffer(dotdot_bh);
        }
  error_inode:
                sinfo.bh = NULL;
        }
        if (corrupt < 0) {
-               fat_fs_panic(new_dir->i_sb,
+               fat_fs_error(new_dir->i_sb,
                             "%s: Filesystem corrupted (i_pos %lld)",
                             __func__, sinfo.i_pos);
        }