fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / fat / inode.c
index 7245657..1f92c96 100644 (file)
 #include <linux/seq_file.h>
 #include <linux/msdos_fs.h>
 #include <linux/pagemap.h>
+#include <linux/mpage.h>
 #include <linux/buffer_head.h>
 #include <linux/mount.h>
 #include <linux/vfs.h>
 #include <linux/parser.h>
+#include <linux/uio.h>
+#include <linux/writeback.h>
 #include <asm/unaligned.h>
 
 #ifndef CONFIG_FAT_DEFAULT_IOCHARSET
@@ -33,42 +36,84 @@ static int fat_default_codepage = CONFIG_FAT_DEFAULT_CODEPAGE;
 static char fat_default_iocharset[] = CONFIG_FAT_DEFAULT_IOCHARSET;
 
 
-static int fat_get_block(struct inode *inode, sector_t iblock,
-                        struct buffer_head *bh_result, int create)
+static int fat_add_cluster(struct inode *inode)
+{
+       int err, cluster;
+
+       err = fat_alloc_clusters(inode, &cluster, 1);
+       if (err)
+               return err;
+       /* FIXME: this cluster should be added after data of this
+        * cluster is writed */
+       err = fat_chain_add(inode, cluster, 1);
+       if (err)
+               fat_free_clusters(inode, cluster);
+       return err;
+}
+
+static inline int __fat_get_block(struct inode *inode, sector_t iblock,
+                                 unsigned long *max_blocks,
+                                 struct buffer_head *bh_result, int create)
 {
        struct super_block *sb = inode->i_sb;
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       unsigned long mapped_blocks;
        sector_t phys;
-       int err;
+       int err, offset;
 
-       err = fat_bmap(inode, iblock, &phys);
+       err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
        if (err)
                return err;
        if (phys) {
                map_bh(bh_result, sb, phys);
+               *max_blocks = min(mapped_blocks, *max_blocks);
                return 0;
        }
        if (!create)
                return 0;
+
        if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
                fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)",
-                            MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
+                       MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
                return -EIO;
        }
-       if (!((unsigned long)iblock & (MSDOS_SB(sb)->sec_per_clus - 1))) {
-               int error;
 
-               error = fat_add_cluster(inode);
-               if (error < 0)
-                       return error;
+       offset = (unsigned long)iblock & (sbi->sec_per_clus - 1);
+       if (!offset) {
+               /* TODO: multiple cluster allocation would be desirable. */
+               err = fat_add_cluster(inode);
+               if (err)
+                       return err;
        }
-       MSDOS_I(inode)->mmu_private += sb->s_blocksize;
-       err = fat_bmap(inode, iblock, &phys);
+       /* available blocks on this cluster */
+       mapped_blocks = sbi->sec_per_clus - offset;
+
+       *max_blocks = min(mapped_blocks, *max_blocks);
+       MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits;
+
+       err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
        if (err)
                return err;
-       if (!phys)
-               BUG();
+
+       BUG_ON(!phys);
+       BUG_ON(*max_blocks != mapped_blocks);
        set_buffer_new(bh_result);
        map_bh(bh_result, sb, phys);
+
+       return 0;
+}
+
+static int fat_get_block(struct inode *inode, sector_t iblock,
+                        struct buffer_head *bh_result, int create)
+{
+       struct super_block *sb = inode->i_sb;
+       unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
+       int err;
+
+       err = __fat_get_block(inode, iblock, &max_blocks, bh_result, create);
+       if (err)
+               return err;
+       bh_result->b_size = max_blocks << sb->s_blocksize_bits;
        return 0;
 }
 
@@ -77,9 +122,21 @@ static int fat_writepage(struct page *page, struct writeback_control *wbc)
        return block_write_full_page(page, fat_get_block, wbc);
 }
 
+static int fat_writepages(struct address_space *mapping,
+                         struct writeback_control *wbc)
+{
+       return mpage_writepages(mapping, wbc, fat_get_block);
+}
+
 static int fat_readpage(struct file *file, struct page *page)
 {
-       return block_read_full_page(page, fat_get_block);
+       return mpage_readpage(page, fat_get_block);
+}
+
+static int fat_readpages(struct file *file, struct address_space *mapping,
+                        struct list_head *pages, unsigned nr_pages)
+{
+       return mpage_readpages(mapping, pages, nr_pages, fat_get_block);
 }
 
 static int fat_prepare_write(struct file *file, struct page *page,
@@ -89,17 +146,61 @@ static int fat_prepare_write(struct file *file, struct page *page,
                                  &MSDOS_I(page->mapping->host)->mmu_private);
 }
 
+static int fat_commit_write(struct file *file, struct page *page,
+                           unsigned from, unsigned to)
+{
+       struct inode *inode = page->mapping->host;
+       int err = generic_commit_write(file, page, from, to);
+       if (!err && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) {
+               inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+               MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
+               mark_inode_dirty(inode);
+       }
+       return err;
+}
+
+static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
+                            const struct iovec *iov,
+                            loff_t offset, unsigned long nr_segs)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file->f_mapping->host;
+
+       if (rw == WRITE) {
+               /*
+                * FIXME: blockdev_direct_IO() doesn't use ->prepare_write(),
+                * so we need to update the ->mmu_private to block boundary.
+                *
+                * But we must fill the remaining area or hole by nul for
+                * updating ->mmu_private.
+                */
+               loff_t size = offset + iov_length(iov, nr_segs);
+               if (MSDOS_I(inode)->mmu_private < size)
+                       return -EINVAL;
+       }
+
+       /*
+        * FAT need to use the DIO_LOCKING for avoiding the race
+        * condition of fat_get_block() and ->truncate().
+        */
+       return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
+                                 offset, nr_segs, fat_get_block, NULL);
+}
+
 static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
 {
        return generic_block_bmap(mapping, block, fat_get_block);
 }
 
-static struct address_space_operations fat_aops = {
+static const struct address_space_operations fat_aops = {
        .readpage       = fat_readpage,
+       .readpages      = fat_readpages,
        .writepage      = fat_writepage,
+       .writepages     = fat_writepages,
        .sync_page      = block_sync_page,
        .prepare_write  = fat_prepare_write,
-       .commit_write   = generic_commit_write,
+       .commit_write   = fat_commit_write,
+       .direct_IO      = fat_direct_IO,
        .bmap           = _fat_bmap
 };
 
@@ -156,7 +257,7 @@ void fat_attach(struct inode *inode, loff_t i_pos)
        spin_unlock(&sbi->inode_hash_lock);
 }
 
-EXPORT_SYMBOL(fat_attach);
+EXPORT_SYMBOL_GPL(fat_attach);
 
 void fat_detach(struct inode *inode)
 {
@@ -167,7 +268,7 @@ void fat_detach(struct inode *inode)
        spin_unlock(&sbi->inode_hash_lock);
 }
 
-EXPORT_SYMBOL(fat_detach);
+EXPORT_SYMBOL_GPL(fat_detach);
 
 struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
 {
@@ -220,8 +321,7 @@ static int fat_calc_dir_size(struct inode *inode)
 /* doesn't deal with root inode */
 static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
 {
-       struct super_block *sb = inode->i_sb;
-       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
        int error;
 
        MSDOS_I(inode)->i_pos = 0;
@@ -266,45 +366,52 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
                inode->i_mapping->a_ops = &fat_aops;
                MSDOS_I(inode)->mmu_private = inode->i_size;
        }
-       if(de->attr & ATTR_SYS)
+       if (de->attr & ATTR_SYS) {
                if (sbi->options.sys_immutable)
                        inode->i_flags |= S_IMMUTABLE;
+       }
        MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
-       /* this is as close to the truth as we can get ... */
-       inode->i_blksize = sbi->cluster_size;
        inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
                           & ~((loff_t)sbi->cluster_size - 1)) >> 9;
-       inode->i_mtime.tv_sec = inode->i_atime.tv_sec =
+       inode->i_mtime.tv_sec =
                date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date));
-       inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = 0;
-       inode->i_ctime.tv_sec =
-               MSDOS_SB(sb)->options.isvfat
-               ? date_dos2unix(le16_to_cpu(de->ctime), le16_to_cpu(de->cdate))
-               : inode->i_mtime.tv_sec;
-       inode->i_ctime.tv_nsec = de->ctime_ms * 1000000;
-       MSDOS_I(inode)->i_ctime_ms = de->ctime_ms;
+       inode->i_mtime.tv_nsec = 0;
+       if (sbi->options.isvfat) {
+               int secs = de->ctime_cs / 100;
+               int csecs = de->ctime_cs % 100;
+               inode->i_ctime.tv_sec  =
+                       date_dos2unix(le16_to_cpu(de->ctime),
+                                     le16_to_cpu(de->cdate)) + secs;
+               inode->i_ctime.tv_nsec = csecs * 10000000;
+               inode->i_atime.tv_sec =
+                       date_dos2unix(0, le16_to_cpu(de->adate));
+               inode->i_atime.tv_nsec = 0;
+       } else
+               inode->i_ctime = inode->i_atime = inode->i_mtime;
 
        return 0;
 }
 
 struct inode *fat_build_inode(struct super_block *sb,
-                       struct msdos_dir_entry *de, loff_t i_pos, int *res)
+                       struct msdos_dir_entry *de, loff_t i_pos)
 {
        struct inode *inode;
-       *res = 0;
+       int err;
+
        inode = fat_iget(sb, i_pos);
        if (inode)
                goto out;
        inode = new_inode(sb);
-       *res = -ENOMEM;
-       if (!inode)
+       if (!inode) {
+               inode = ERR_PTR(-ENOMEM);
                goto out;
+       }
        inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
        inode->i_version = 1;
-       *res = fat_fill_inode(inode, de);
-       if (*res < 0) {
+       err = fat_fill_inode(inode, de);
+       if (err) {
                iput(inode);
-               inode = NULL;
+               inode = ERR_PTR(err);
                goto out;
        }
        fat_attach(inode, i_pos);
@@ -313,10 +420,12 @@ out:
        return inode;
 }
 
-EXPORT_SYMBOL(fat_build_inode);
+EXPORT_SYMBOL_GPL(fat_build_inode);
 
 static void fat_delete_inode(struct inode *inode)
 {
+       truncate_inode_pages(&inode->i_data, 0);
+
        if (!is_bad_inode(inode)) {
                inode->i_size = 0;
                fat_truncate(inode);
@@ -338,12 +447,17 @@ static void fat_clear_inode(struct inode *inode)
        unlock_kernel();
 }
 
-static void fat_put_super(struct super_block *sb)
+static void fat_write_super(struct super_block *sb)
 {
-       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       sb->s_dirt = 0;
 
        if (!(sb->s_flags & MS_RDONLY))
                fat_clusters_flush(sb);
+}
+
+static void fat_put_super(struct super_block *sb)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
 
        if (sbi->nls_disk) {
                unload_nls(sbi->nls_disk);
@@ -363,12 +477,12 @@ static void fat_put_super(struct super_block *sb)
        kfree(sbi);
 }
 
-static kmem_cache_t *fat_inode_cachep;
+static struct kmem_cache *fat_inode_cachep;
 
 static struct inode *fat_alloc_inode(struct super_block *sb)
 {
        struct msdos_inode_info *ei;
-       ei = kmem_cache_alloc(fat_inode_cachep, SLAB_KERNEL);
+       ei = kmem_cache_alloc(fat_inode_cachep, GFP_KERNEL);
        if (!ei)
                return NULL;
        return &ei->vfs_inode;
@@ -379,7 +493,7 @@ static void fat_destroy_inode(struct inode *inode)
        kmem_cache_free(fat_inode_cachep, MSDOS_I(inode));
 }
 
-static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
 {
        struct msdos_inode_info *ei = (struct msdos_inode_info *)foo;
 
@@ -398,7 +512,8 @@ static int __init fat_init_inodecache(void)
 {
        fat_inode_cachep = kmem_cache_create("fat_inode_cache",
                                             sizeof(struct msdos_inode_info),
-                                            0, SLAB_RECLAIM_ACCOUNT,
+                                            0, (SLAB_RECLAIM_ACCOUNT|
+                                               SLAB_MEM_SPREAD),
                                             init_once, NULL);
        if (fat_inode_cachep == NULL)
                return -ENOMEM;
@@ -407,47 +522,32 @@ static int __init fat_init_inodecache(void)
 
 static void __exit fat_destroy_inodecache(void)
 {
-       if (kmem_cache_destroy(fat_inode_cachep))
-               printk(KERN_INFO "fat_inode_cache: not all structures were freed\n");
+       kmem_cache_destroy(fat_inode_cachep);
 }
 
 static int fat_remount(struct super_block *sb, int *flags, char *data)
 {
-       *flags |= MS_NODIRATIME;
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       *flags |= MS_NODIRATIME | (sbi->options.isvfat ? 0 : MS_NOATIME);
        return 0;
 }
 
-static int fat_statfs(struct super_block *sb, struct kstatfs *buf)
+static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
-       struct msdos_sb_info *sbi = MSDOS_SB(sb);
-       int free, nr, ret;
-
-       if (sbi->free_clusters != -1)
-               free = sbi->free_clusters;
-       else {
-               lock_fat(sb);
-               if (sbi->free_clusters != -1)
-                       free = sbi->free_clusters;
-               else {
-                       free = 0;
-                       for (nr = FAT_START_ENT; nr < sbi->max_cluster; nr++) {
-                               ret = fat_access(sb, nr, -1);
-                               if (ret < 0) {
-                                       unlock_fat(sb);
-                                       return ret;
-                               } else if (ret == FAT_ENT_FREE)
-                                       free++;
-                       }
-                       sbi->free_clusters = free;
-               }
-               unlock_fat(sb);
+       struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
+
+       /* If the count of free cluster is still unknown, counts it here. */
+       if (sbi->free_clusters == -1) {
+               int err = fat_count_free_clusters(dentry->d_sb);
+               if (err)
+                       return err;
        }
 
-       buf->f_type = sb->s_magic;
+       buf->f_type = dentry->d_sb->s_magic;
        buf->f_bsize = sbi->cluster_size;
        buf->f_blocks = sbi->max_cluster - FAT_START_ENT;
-       buf->f_bfree = free;
-       buf->f_bavail = free;
+       buf->f_bfree = sbi->free_clusters;
+       buf->f_bavail = sbi->free_clusters;
        buf->f_namelen = sbi->options.isvfat ? 260 : 12;
 
        return 0;
@@ -460,18 +560,20 @@ static int fat_write_inode(struct inode *inode, int wait)
        struct buffer_head *bh;
        struct msdos_dir_entry *raw_entry;
        loff_t i_pos;
+       int err = 0;
 
 retry:
        i_pos = MSDOS_I(inode)->i_pos;
-       if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) {
+       if (inode->i_ino == MSDOS_ROOT_INO || !i_pos)
                return 0;
-       }
+
        lock_kernel();
-       if (!(bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits))) {
+       bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits);
+       if (!bh) {
                printk(KERN_ERR "FAT: unable to read inode block "
                       "for updating (i_pos %lld)\n", i_pos);
-               unlock_kernel();
-               return -EIO;
+               err = -EIO;
+               goto out;
        }
        spin_lock(&sbi->inode_hash_lock);
        if (i_pos != MSDOS_I(inode)->i_pos) {
@@ -483,30 +585,38 @@ retry:
 
        raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
            [i_pos & (sbi->dir_per_block - 1)];
-       if (S_ISDIR(inode->i_mode)) {
-               raw_entry->attr = ATTR_DIR;
+       if (S_ISDIR(inode->i_mode))
                raw_entry->size = 0;
-       }
-       else {
-               raw_entry->attr = ATTR_NONE;
+       else
                raw_entry->size = cpu_to_le32(inode->i_size);
-       }
-       raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
-           MSDOS_I(inode)->i_attrs;
+       raw_entry->attr = fat_attr(inode);
        raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart);
        raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16);
        fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date);
        if (sbi->options.isvfat) {
+               __le16 atime;
                fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate);
-               raw_entry->ctime_ms = MSDOS_I(inode)->i_ctime_ms; /* use i_ctime.tv_nsec? */
+               fat_date_unix2dos(inode->i_atime.tv_sec,&atime,&raw_entry->adate);
+               raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 +
+                       inode->i_ctime.tv_nsec / 10000000;
        }
        spin_unlock(&sbi->inode_hash_lock);
        mark_buffer_dirty(bh);
+       if (wait)
+               err = sync_dirty_buffer(bh);
        brelse(bh);
+out:
        unlock_kernel();
-       return 0;
+       return err;
+}
+
+int fat_sync_inode(struct inode *inode)
+{
+       return fat_write_inode(inode, 1);
 }
 
+EXPORT_SYMBOL_GPL(fat_sync_inode);
+
 static int fat_show_options(struct seq_file *m, struct vfsmount *mnt);
 static struct super_operations fat_sops = {
        .alloc_inode    = fat_alloc_inode,
@@ -514,6 +624,7 @@ static struct super_operations fat_sops = {
        .write_inode    = fat_write_inode,
        .delete_inode   = fat_delete_inode,
        .put_super      = fat_put_super,
+       .write_super    = fat_write_super,
        .statfs         = fat_statfs,
        .clear_inode    = fat_clear_inode,
        .remount_fs     = fat_remount,
@@ -636,39 +747,35 @@ fat_encode_fh(struct dentry *de, __u32 *fh, int *lenp, int connectable)
 
 static struct dentry *fat_get_parent(struct dentry *child)
 {
-       struct buffer_head *bh=NULL;
-       struct msdos_dir_entry *de = NULL;
-       struct dentry *parent = NULL;
-       int res;
-       loff_t i_pos = 0;
+       struct buffer_head *bh;
+       struct msdos_dir_entry *de;
+       loff_t i_pos;
+       struct dentry *parent;
        struct inode *inode;
+       int err;
 
        lock_kernel();
-       res = fat_scan(child->d_inode, MSDOS_DOTDOT, &bh, &de, &i_pos);
 
-       if (res < 0)
+       err = fat_get_dotdot_entry(child->d_inode, &bh, &de, &i_pos);
+       if (err) {
+               parent = ERR_PTR(err);
                goto out;
-       inode = fat_build_inode(child->d_sb, de, i_pos, &res);
-       if (res)
+       }
+       inode = fat_build_inode(child->d_sb, de, i_pos);
+       brelse(bh);
+       if (IS_ERR(inode)) {
+               parent = ERR_PTR(PTR_ERR(inode));
                goto out;
-       if (!inode)
-               res = -EACCES;
-       else {
-               parent = d_alloc_anon(inode);
-               if (!parent) {
-                       iput(inode);
-                       res = -ENOMEM;
-               }
        }
-
- out:
-       if(bh)
-               brelse(bh);
+       parent = d_alloc_anon(inode);
+       if (!parent) {
+               iput(inode);
+               parent = ERR_PTR(-ENOMEM);
+       }
+out:
        unlock_kernel();
-       if (res)
-               return ERR_PTR(res);
-       else
-               return parent;
+
+       return parent;
 }
 
 static struct export_operations fat_export_ops = {
@@ -747,7 +854,7 @@ 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_err,
+       Opt_obsolate, Opt_flush, Opt_err,
 };
 
 static match_table_t fat_tokens = {
@@ -779,7 +886,8 @@ static match_table_t fat_tokens = {
        {Opt_obsolate, "cvf_format=%20s"},
        {Opt_obsolate, "cvf_options=%100s"},
        {Opt_obsolate, "posix"},
-       {Opt_err, NULL}
+       {Opt_flush, "flush"},
+       {Opt_err, NULL},
 };
 static match_table_t msdos_tokens = {
        {Opt_nodots, "nodots"},
@@ -818,7 +926,7 @@ static match_table_t vfat_tokens = {
        {Opt_err, NULL}
 };
 
-static int parse_options(char *options, int is_vfat, int *debug,
+static int parse_options(char *options, int is_vfat, int silent, int *debug,
                         struct fat_mount_options *opts)
 {
        char *p;
@@ -839,7 +947,8 @@ static int parse_options(char *options, int is_vfat, int *debug,
                opts->shortname = 0;
        opts->name_check = 'n';
        opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK =  0;
-       opts->utf8 = opts->unicode_xlate = 0;
+       opts->utf8 = 1;
+       opts->unicode_xlate = 0;
        opts->numtail = 1;
        opts->nocase = 0;
        *debug = 0;
@@ -920,6 +1029,9 @@ static int parse_options(char *options, int is_vfat, int *debug,
                                return 0;
                        opts->codepage = option;
                        break;
+               case Opt_flush:
+                       opts->flush = 1;
+                       break;
 
                /* msdos specific */
                case Opt_dots:
@@ -980,12 +1092,15 @@ static int parse_options(char *options, int is_vfat, int *debug,
                        break;
                /* unknown option */
                default:
-                       printk(KERN_ERR "FAT: Unrecognized mount option \"%s\" "
-                              "or missing value\n", p);
+                       if (!silent) {
+                               printk(KERN_ERR
+                                      "FAT: Unrecognized mount option \"%s\" "
+                                      "or missing value\n", p);
+                       }
                        return -EINVAL;
                }
        }
-       /* UTF8 doesn't provide FAT semantics */
+       /* UTF-8 doesn't provide FAT semantics */
        if (!strcmp(opts->iocharset, "utf8")) {
                printk(KERN_ERR "FAT: utf8 is not a recommended IO charset"
                       " for FAT filesystems, filesystem will be case sensitive!\n");
@@ -1020,16 +1135,14 @@ static int fat_read_root(struct inode *inode)
                MSDOS_I(inode)->i_start = 0;
                inode->i_size = sbi->dir_entries * sizeof(struct msdos_dir_entry);
        }
-       inode->i_blksize = sbi->cluster_size;
        inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
                           & ~((loff_t)sbi->cluster_size - 1)) >> 9;
        MSDOS_I(inode)->i_logstart = 0;
        MSDOS_I(inode)->mmu_private = inode->i_size;
 
-       MSDOS_I(inode)->i_attrs = 0;
+       MSDOS_I(inode)->i_attrs = ATTR_NONE;
        inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = 0;
        inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = 0;
-       MSDOS_I(inode)->i_ctime_ms = 0;
        inode->i_nlink = fat_subdirs(inode)+2;
 
        return 0;
@@ -1052,11 +1165,10 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
        long error;
        char buf[50];
 
-       sbi = kmalloc(sizeof(struct msdos_sb_info), GFP_KERNEL);
+       sbi = kzalloc(sizeof(struct msdos_sb_info), GFP_KERNEL);
        if (!sbi)
                return -ENOMEM;
        sb->s_fs_info = sbi;
-       memset(sbi, 0, sizeof(struct msdos_sb_info));
 
        sb->s_flags |= MS_NODIRATIME;
        sb->s_magic = MSDOS_SUPER_MAGIC;
@@ -1064,14 +1176,10 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
        sb->s_export_op = &fat_export_ops;
        sbi->dir_ops = fs_dir_inode_ops;
 
-       error = parse_options(data, isvfat, &debug, &sbi->options);
+       error = parse_options(data, isvfat, silent, &debug, &sbi->options);
        if (error)
                goto out_fail;
 
-       /* set up enough so that it can read an inode */
-       fat_hash_init(sb);
-       init_MUTEX(&sbi->fat_lock);
-
        error = -EIO;
        sb_min_blocksize(sb, 512);
        bh = sb_bread(sb, 0);
@@ -1161,7 +1269,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
        sbi->fat_length = le16_to_cpu(b->fat_length);
        sbi->root_cluster = 0;
        sbi->free_clusters = -1;        /* Don't know yet */
-       sbi->prev_free = -1;
+       sbi->prev_free = FAT_START_ENT;
 
        if (!sbi->fat_length && b->fat32_length) {
                struct fat_boot_fsinfo *fsinfo;
@@ -1245,9 +1353,17 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
        /* check the free_clusters, it's not necessarily correct */
        if (sbi->free_clusters != -1 && sbi->free_clusters > total_clusters)
                sbi->free_clusters = -1;
+       /* check the prev_free, it's not necessarily correct */
+       sbi->prev_free %= sbi->max_cluster;
+       if (sbi->prev_free < FAT_START_ENT)
+               sbi->prev_free = FAT_START_ENT;
 
        brelse(bh);
 
+       /* set up enough so that it can read an inode */
+       fat_hash_init(sb);
+       fat_ent_access_init(sb);
+
        /*
         * The low byte of FAT's first entry must have same value with
         * media-field.  But in real world, too many devices is
@@ -1313,19 +1429,75 @@ out_fail:
        return error;
 }
 
-EXPORT_SYMBOL(fat_fill_super);
+EXPORT_SYMBOL_GPL(fat_fill_super);
 
-int __init fat_cache_init(void);
-void __exit fat_cache_destroy(void);
+/*
+ * helper function for fat_flush_inodes.  This writes both the inode
+ * and the file data blocks, waiting for in flight data blocks before
+ * the start of the call.  It does not wait for any io started
+ * during the call
+ */
+static int writeback_inode(struct inode *inode)
+{
+
+       int ret;
+       struct address_space *mapping = inode->i_mapping;
+       struct writeback_control wbc = {
+              .sync_mode = WB_SYNC_NONE,
+             .nr_to_write = 0,
+       };
+       /* if we used WB_SYNC_ALL, sync_inode waits for the io for the
+       * inode to finish.  So WB_SYNC_NONE is sent down to sync_inode
+       * and filemap_fdatawrite is used for the data blocks
+       */
+       ret = sync_inode(inode, &wbc);
+       if (!ret)
+              ret = filemap_fdatawrite(mapping);
+       return ret;
+}
+
+/*
+ * write data and metadata corresponding to i1 and i2.  The io is
+ * started but we do not wait for any of it to finish.
+ *
+ * filemap_flush is used for the block device, so if there is a dirty
+ * page for a block already in flight, we will not wait and start the
+ * io over again
+ */
+int fat_flush_inodes(struct super_block *sb, struct inode *i1, struct inode *i2)
+{
+       int ret = 0;
+       if (!MSDOS_SB(sb)->options.flush)
+               return 0;
+       if (i1)
+               ret = writeback_inode(i1);
+       if (!ret && i2)
+               ret = writeback_inode(i2);
+       if (!ret) {
+               struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
+               ret = filemap_flush(mapping);
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(fat_flush_inodes);
 
 static int __init init_fat_fs(void)
 {
-       int ret;
+       int err;
 
-       ret = fat_cache_init();
-       if (ret < 0)
-               return ret;
-       return fat_init_inodecache();
+       err = fat_cache_init();
+       if (err)
+               return err;
+
+       err = fat_init_inodecache();
+       if (err)
+               goto failed;
+
+       return 0;
+
+failed:
+       fat_cache_destroy();
+       return err;
 }
 
 static void __exit exit_fat_fs(void)