Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / fs / fat / inode.c
index 8ccee84..3c2cbd3 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 <asm/unaligned.h>
 
 #ifndef CONFIG_FAT_DEFAULT_IOCHARSET
@@ -48,51 +50,97 @@ static int fat_add_cluster(struct inode *inode)
        return err;
 }
 
-static int fat_get_block(struct inode *inode, sector_t iblock,
-                        struct buffer_head *bh_result, int create)
+static int __fat_get_blocks(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);
        sector_t phys;
-       int err;
+       unsigned long mapped_blocks;
+       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);
                return -EIO;
        }
-       if (!((unsigned long)iblock & (MSDOS_SB(sb)->sec_per_clus - 1))) {
+
+       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_blocks(struct inode *inode, sector_t iblock,
+                         struct buffer_head *bh_result, int create)
+{
+       struct super_block *sb = inode->i_sb;
+       int err;
+       unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
+
+       err = __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create);
+       if (err)
+               return err;
+       bh_result->b_size = max_blocks << sb->s_blocksize_bits;
+       return 0;
+}
+
+static int fat_get_block(struct inode *inode, sector_t iblock,
+                        struct buffer_head *bh_result, int create)
+{
+       unsigned long max_blocks = 1;
+       return __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create);
+}
+
 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,
@@ -102,6 +150,47 @@ 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_blocks, NULL);
+}
+
 static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
 {
        return generic_block_bmap(mapping, block, fat_get_block);
@@ -109,10 +198,13 @@ static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
 
 static 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
 };
 
@@ -169,7 +261,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)
 {
@@ -180,7 +272,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)
 {
@@ -287,9 +379,9 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
        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_mtime.tv_nsec = 0;
        if (sbi->options.isvfat) {
                int secs = de->ctime_cs / 100;
                int csecs = de->ctime_cs % 100;
@@ -297,8 +389,11 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
                        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(le16_to_cpu(0), le16_to_cpu(de->adate));
+               inode->i_atime.tv_nsec = 0;
        } else
-               inode->i_ctime = inode->i_mtime;
+               inode->i_ctime = inode->i_atime = inode->i_mtime;
 
        return 0;
 }
@@ -331,10 +426,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);
@@ -356,12 +453,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);
@@ -416,7 +518,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;
@@ -498,7 +601,9 @@ retry:
        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);
+               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;
        }
@@ -517,7 +622,7 @@ int fat_sync_inode(struct inode *inode)
        return fat_write_inode(inode, 1);
 }
 
-EXPORT_SYMBOL(fat_sync_inode);
+EXPORT_SYMBOL_GPL(fat_sync_inode);
 
 static int fat_show_options(struct seq_file *m, struct vfsmount *mnt);
 static struct super_operations fat_sops = {
@@ -526,6 +631,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,
@@ -826,7 +932,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;
@@ -847,7 +953,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;
@@ -988,12 +1095,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");
@@ -1071,7 +1181,7 @@ 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;
 
@@ -1324,19 +1434,25 @@ out_fail:
        return error;
 }
 
-EXPORT_SYMBOL(fat_fill_super);
-
-int __init fat_cache_init(void);
-void __exit fat_cache_destroy(void);
+EXPORT_SYMBOL_GPL(fat_fill_super);
 
 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)