]> git.karo-electronics.de Git - linux-beck.git/blobdiff - fs/fat/inode.c
fat (exportfs): rebuild inode if ilookup() fails
[linux-beck.git] / fs / fat / inode.c
index f8f491677a4ac6cbce51a3acb3e6d78e54235cc1..971ba7d549da4960032d43ae7d7e507a607a9874 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/pagemap.h>
 #include <linux/mpage.h>
 #include <linux/buffer_head.h>
-#include <linux/exportfs.h>
 #include <linux/mount.h>
 #include <linux/vfs.h>
 #include <linux/parser.h>
@@ -341,12 +340,11 @@ struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
-       struct hlist_node *_p;
        struct msdos_inode_info *i;
        struct inode *inode = NULL;
 
        spin_lock(&sbi->inode_hash_lock);
-       hlist_for_each_entry(i, _p, head, i_fat_hash) {
+       hlist_for_each_entry(i, head, i_fat_hash) {
                BUG_ON(i->vfs_inode.i_sb != sb);
                if (i->i_pos != i_pos)
                        continue;
@@ -445,12 +443,25 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
        return 0;
 }
 
+static inline void fat_lock_build_inode(struct msdos_sb_info *sbi)
+{
+       if (sbi->options.nfs == FAT_NFS_NOSTALE_RO)
+               mutex_lock(&sbi->nfs_build_inode_lock);
+}
+
+static inline void fat_unlock_build_inode(struct msdos_sb_info *sbi)
+{
+       if (sbi->options.nfs == FAT_NFS_NOSTALE_RO)
+               mutex_unlock(&sbi->nfs_build_inode_lock);
+}
+
 struct inode *fat_build_inode(struct super_block *sb,
                        struct msdos_dir_entry *de, loff_t i_pos)
 {
        struct inode *inode;
        int err;
 
+       fat_lock_build_inode(MSDOS_SB(sb));
        inode = fat_iget(sb, i_pos);
        if (inode)
                goto out;
@@ -470,6 +481,7 @@ struct inode *fat_build_inode(struct super_block *sb,
        fat_attach(inode, i_pos);
        insert_inode_hash(inode);
 out:
+       fat_unlock_build_inode(MSDOS_SB(sb));
        return inode;
 }
 
@@ -488,10 +500,59 @@ static void fat_evict_inode(struct inode *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)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
 
+       fat_set_state(sb, 0, 0);
+
        iput(sbi->fsinfo_inode);
        iput(sbi->fat_inode);
 
@@ -566,8 +627,18 @@ static void __exit fat_destroy_inodecache(void)
 
 static int fat_remount(struct super_block *sb, int *flags, char *data)
 {
+       int new_rdonly;
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        *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;
 }
 
@@ -597,20 +668,6 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
        return 0;
 }
 
-static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
-                                   struct inode *inode)
-{
-       loff_t i_pos;
-#if BITS_PER_LONG == 32
-       spin_lock(&sbi->inode_hash_lock);
-#endif
-       i_pos = MSDOS_I(inode)->i_pos;
-#if BITS_PER_LONG == 32
-       spin_unlock(&sbi->inode_hash_lock);
-#endif
-       return i_pos;
-}
-
 static int __fat_write_inode(struct inode *inode, int wait)
 {
        struct super_block *sb = inode->i_sb;
@@ -618,7 +675,8 @@ 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;
+       sector_t blocknr;
+       int err, offset;
 
        if (inode->i_ino == MSDOS_ROOT_INO)
                return 0;
@@ -628,7 +686,8 @@ retry:
        if (!i_pos)
                return 0;
 
-       bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits);
+       fat_get_blknr_offset(sbi, i_pos, &blocknr, &offset);
+       bh = sb_bread(sb, blocknr);
        if (!bh) {
                fat_msg(sb, KERN_ERR, "unable to read inode block "
                       "for updating (i_pos %lld)", i_pos);
@@ -641,8 +700,7 @@ retry:
                goto retry;
        }
 
-       raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
-           [i_pos & (sbi->dir_per_block - 1)];
+       raw_entry = &((struct msdos_dir_entry *) (bh->b_data))[offset];
        if (S_ISDIR(inode->i_mode))
                raw_entry->size = 0;
        else
@@ -703,12 +761,6 @@ static const struct super_operations fat_sops = {
        .show_options   = fat_show_options,
 };
 
-static const struct export_operations fat_export_ops = {
-       .fh_to_dentry   = fat_fh_to_dentry,
-       .fh_to_parent   = fat_fh_to_parent,
-       .get_parent     = fat_get_parent,
-};
-
 static int fat_show_options(struct seq_file *m, struct dentry *root)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(root->d_sb);
@@ -756,8 +808,6 @@ static int fat_show_options(struct seq_file *m, struct dentry *root)
                seq_puts(m, ",usefree");
        if (opts->quiet)
                seq_puts(m, ",quiet");
-       if (opts->nfs)
-               seq_puts(m, ",nfs");
        if (opts->showexec)
                seq_puts(m, ",showexec");
        if (opts->sys_immutable)
@@ -791,6 +841,10 @@ static int fat_show_options(struct seq_file *m, struct dentry *root)
                seq_puts(m, ",errors=panic");
        else
                seq_puts(m, ",errors=remount-ro");
+       if (opts->nfs == FAT_NFS_NOSTALE_RO)
+               seq_puts(m, ",nfs=nostale_ro");
+       else if (opts->nfs)
+               seq_puts(m, ",nfs=stale_rw");
        if (opts->discard)
                seq_puts(m, ",discard");
 
@@ -807,7 +861,7 @@ enum {
        Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
        Opt_obsolete, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont,
        Opt_err_panic, Opt_err_ro, Opt_discard, Opt_nfs, Opt_time_offset,
-       Opt_err,
+       Opt_nfs_stale_rw, Opt_nfs_nostale_ro, Opt_err,
 };
 
 static const match_table_t fat_tokens = {
@@ -837,7 +891,9 @@ static const match_table_t fat_tokens = {
        {Opt_err_panic, "errors=panic"},
        {Opt_err_ro, "errors=remount-ro"},
        {Opt_discard, "discard"},
-       {Opt_nfs, "nfs"},
+       {Opt_nfs_stale_rw, "nfs"},
+       {Opt_nfs_stale_rw, "nfs=stale_rw"},
+       {Opt_nfs_nostale_ro, "nfs=nostale_ro"},
        {Opt_obsolete, "conv=binary"},
        {Opt_obsolete, "conv=text"},
        {Opt_obsolete, "conv=auto"},
@@ -1034,6 +1090,12 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat,
                case Opt_err_ro:
                        opts->errors = FAT_ERRORS_RO;
                        break;
+               case Opt_nfs_stale_rw:
+                       opts->nfs = FAT_NFS_STALE_RW;
+                       break;
+               case Opt_nfs_nostale_ro:
+                       opts->nfs = FAT_NFS_NOSTALE_RO;
+                       break;
 
                /* msdos specific */
                case Opt_dots:
@@ -1092,9 +1154,6 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat,
                case Opt_discard:
                        opts->discard = 1;
                        break;
-               case Opt_nfs:
-                       opts->nfs = 1;
-                       break;
 
                /* obsolete mount options */
                case Opt_obsolete:
@@ -1125,6 +1184,10 @@ out:
                opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH);
        if (opts->unicode_xlate)
                opts->utf8 = 0;
+       if (opts->nfs == FAT_NFS_NOSTALE_RO) {
+               sb->s_flags |= MS_RDONLY;
+               sb->s_export_op = &fat_export_ops_nostale;
+       }
 
        return 0;
 }
@@ -1135,7 +1198,7 @@ static int fat_read_root(struct inode *inode)
        struct msdos_sb_info *sbi = MSDOS_SB(sb);
        int error;
 
-       MSDOS_I(inode)->i_pos = 0;
+       MSDOS_I(inode)->i_pos = MSDOS_ROOT_INO;
        inode->i_uid = sbi->options.fs_uid;
        inode->i_gid = sbi->options.fs_gid;
        inode->i_version++;
@@ -1198,6 +1261,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
        sb->s_magic = MSDOS_SUPER_MAGIC;
        sb->s_op = &fat_sops;
        sb->s_export_op = &fat_export_ops;
+       mutex_init(&sbi->nfs_build_inode_lock);
        ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL,
                             DEFAULT_RATELIMIT_BURST);
 
@@ -1298,17 +1362,17 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
        sbi->prev_free = FAT_START_ENT;
        sb->s_maxbytes = 0xffffffff;
 
-       if (!sbi->fat_length && b->fat32_length) {
+       if (!sbi->fat_length && b->fat32.length) {
                struct fat_boot_fsinfo *fsinfo;
                struct buffer_head *fsinfo_bh;
 
                /* Must be FAT32 */
                sbi->fat_bits = 32;
-               sbi->fat_length = le32_to_cpu(b->fat32_length);
-               sbi->root_cluster = le32_to_cpu(b->root_cluster);
+               sbi->fat_length = le32_to_cpu(b->fat32.length);
+               sbi->root_cluster = le32_to_cpu(b->fat32.root_cluster);
 
                /* MC - if info_sector is 0, don't multiply by 0 */
-               sbi->fsinfo_sector = le16_to_cpu(b->info_sector);
+               sbi->fsinfo_sector = le16_to_cpu(b->fat32.info_sector);
                if (sbi->fsinfo_sector == 0)
                        sbi->fsinfo_sector = 1;
 
@@ -1362,6 +1426,12 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
        if (sbi->fat_bits != 32)
                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 */
        fat_clusters = sbi->fat_length * sb->s_blocksize * 8 / sbi->fat_bits;
        total_clusters = min(total_clusters, fat_clusters - FAT_START_ENT);
@@ -1456,6 +1526,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
                                        "the device does not support discard");
        }
 
+       fat_set_state(sb, 1, 0);
        return 0;
 
 out_invalid: