aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fat/inode.c
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2013-03-31 18:10:34 +0200
committerPatrick McHardy <kaber@trash.net>2013-03-31 18:10:34 +0200
commit70711d223510ba1773cfe1d7770a56141c815ff8 (patch)
tree4a71f38a3a554ddecaa31b7d8c6bc49b7d1705b4 /fs/fat/inode.c
parentd53b4ed072d9779cdf53582c46436dec06d0961f (diff)
parent19f949f52599ba7c3f67a5897ac6be14bfcb1200 (diff)
Merge tag 'v3.8' of /home/kaber/src/repos/linux
Linux 3.8 Signed-off-by: Patrick McHardy <kaber@trash.net> Conflicts: include/linux/Kbuild include/linux/netlink.h
Diffstat (limited to 'fs/fat/inode.c')
-rw-r--r--fs/fat/inode.c278
1 files changed, 116 insertions, 162 deletions
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 0038b32cb36..f8f491677a4 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -26,6 +26,7 @@
#include <linux/writeback.h>
#include <linux/log2.h>
#include <linux/hash.h>
+#include <linux/blkdev.h>
#include <asm/unaligned.h>
#include "fat.h"
@@ -281,15 +282,42 @@ static inline unsigned long fat_hash(loff_t i_pos)
return hash_32(i_pos, FAT_HASH_BITS);
}
+static void dir_hash_init(struct super_block *sb)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ int i;
+
+ spin_lock_init(&sbi->dir_hash_lock);
+ for (i = 0; i < FAT_HASH_SIZE; i++)
+ INIT_HLIST_HEAD(&sbi->dir_hashtable[i]);
+}
+
void fat_attach(struct inode *inode, loff_t i_pos)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
- struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
- spin_lock(&sbi->inode_hash_lock);
- MSDOS_I(inode)->i_pos = i_pos;
- hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head);
- spin_unlock(&sbi->inode_hash_lock);
+ if (inode->i_ino != MSDOS_ROOT_INO) {
+ struct hlist_head *head = sbi->inode_hashtable
+ + fat_hash(i_pos);
+
+ spin_lock(&sbi->inode_hash_lock);
+ MSDOS_I(inode)->i_pos = i_pos;
+ hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head);
+ spin_unlock(&sbi->inode_hash_lock);
+ }
+
+ /* If NFS support is enabled, cache the mapping of start cluster
+ * to directory inode. This is used during reconnection of
+ * dentries to the filesystem root.
+ */
+ if (S_ISDIR(inode->i_mode) && sbi->options.nfs) {
+ struct hlist_head *d_head = sbi->dir_hashtable;
+ d_head += fat_dir_hash(MSDOS_I(inode)->i_logstart);
+
+ spin_lock(&sbi->dir_hash_lock);
+ hlist_add_head(&MSDOS_I(inode)->i_dir_hash, d_head);
+ spin_unlock(&sbi->dir_hash_lock);
+ }
}
EXPORT_SYMBOL_GPL(fat_attach);
@@ -300,6 +328,12 @@ void fat_detach(struct inode *inode)
MSDOS_I(inode)->i_pos = 0;
hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
spin_unlock(&sbi->inode_hash_lock);
+
+ if (S_ISDIR(inode->i_mode) && sbi->options.nfs) {
+ spin_lock(&sbi->dir_hash_lock);
+ hlist_del_init(&MSDOS_I(inode)->i_dir_hash);
+ spin_unlock(&sbi->dir_hash_lock);
+ }
}
EXPORT_SYMBOL_GPL(fat_detach);
@@ -369,10 +403,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
inode->i_op = sbi->dir_ops;
inode->i_fop = &fat_dir_operations;
- MSDOS_I(inode)->i_start = le16_to_cpu(de->start);
- if (sbi->fat_bits == 32)
- MSDOS_I(inode)->i_start |= (le16_to_cpu(de->starthi) << 16);
-
+ MSDOS_I(inode)->i_start = fat_get_start(sbi, de);
MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
error = fat_calc_dir_size(inode);
if (error < 0)
@@ -385,9 +416,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
inode->i_mode = fat_make_mode(sbi, de->attr,
((sbi->options.showexec && !is_exec(de->name + 8))
? S_IRUGO|S_IWUGO : S_IRWXUGO));
- MSDOS_I(inode)->i_start = le16_to_cpu(de->start);
- if (sbi->fat_bits == 32)
- MSDOS_I(inode)->i_start |= (le16_to_cpu(de->starthi) << 16);
+ MSDOS_I(inode)->i_start = fat_get_start(sbi, de);
MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
inode->i_size = le32_to_cpu(de->size);
@@ -509,6 +538,7 @@ static void init_once(void *foo)
ei->cache_valid_id = FAT_CACHE_VALID + 1;
INIT_LIST_HEAD(&ei->cache_lru);
INIT_HLIST_NODE(&ei->i_fat_hash);
+ INIT_HLIST_NODE(&ei->i_dir_hash);
inode_init_once(&ei->vfs_inode);
}
@@ -526,6 +556,11 @@ static int __init fat_init_inodecache(void)
static void __exit fat_destroy_inodecache(void)
{
+ /*
+ * Make sure all delayed rcu free inodes are flushed before we
+ * destroy cache.
+ */
+ rcu_barrier();
kmem_cache_destroy(fat_inode_cachep);
}
@@ -613,8 +648,7 @@ retry:
else
raw_entry->size = cpu_to_le32(inode->i_size);
raw_entry->attr = fat_make_attrs(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_set_start(raw_entry, MSDOS_I(inode)->i_logstart);
fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time,
&raw_entry->date, NULL);
if (sbi->options.isvfat) {
@@ -640,9 +674,9 @@ static int fat_write_inode(struct inode *inode, struct writeback_control *wbc)
if (inode->i_ino == MSDOS_FSINFO_INO) {
struct super_block *sb = inode->i_sb;
- lock_super(sb);
+ mutex_lock(&MSDOS_SB(sb)->s_lock);
err = fat_clusters_flush(sb);
- unlock_super(sb);
+ mutex_unlock(&MSDOS_SB(sb)->s_lock);
} else
err = __fat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
@@ -669,125 +703,9 @@ static const struct super_operations fat_sops = {
.show_options = fat_show_options,
};
-/*
- * a FAT file handle with fhtype 3 is
- * 0/ i_ino - for fast, reliable lookup if still in the cache
- * 1/ i_generation - to see if i_ino is still valid
- * bit 0 == 0 iff directory
- * 2/ i_pos(8-39) - if ino has changed, but still in cache
- * 3/ i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
- * 4/ i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
- *
- * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
- * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
- * of i_logstart is used to store the directory entry offset.
- */
-
-static struct dentry *fat_fh_to_dentry(struct super_block *sb,
- struct fid *fid, int fh_len, int fh_type)
-{
- struct inode *inode = NULL;
- u32 *fh = fid->raw;
-
- if (fh_len < 5 || fh_type != 3)
- return NULL;
-
- inode = ilookup(sb, fh[0]);
- if (!inode || inode->i_generation != fh[1]) {
- if (inode)
- iput(inode);
- inode = NULL;
- }
- if (!inode) {
- loff_t i_pos;
- int i_logstart = fh[3] & 0x0fffffff;
-
- i_pos = (loff_t)fh[2] << 8;
- i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
-
- /* try 2 - see if i_pos is in F-d-c
- * require i_logstart to be the same
- * Will fail if you truncate and then re-write
- */
-
- inode = fat_iget(sb, i_pos);
- if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
- iput(inode);
- inode = NULL;
- }
- }
-
- /*
- * For now, do nothing if the inode is not found.
- *
- * What we could do is:
- *
- * - follow the file starting at fh[4], and record the ".." entry,
- * and the name of the fh[2] entry.
- * - then follow the ".." file finding the next step up.
- *
- * This way we build a path to the root of the tree. If this works, we
- * lookup the path and so get this inode into the cache. Finally try
- * the fat_iget lookup again. If that fails, then we are totally out
- * of luck. But all that is for another day
- */
- return d_obtain_alias(inode);
-}
-
-static int
-fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent)
-{
- int len = *lenp;
- struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
- loff_t i_pos;
-
- if (len < 5) {
- *lenp = 5;
- return 255; /* no room */
- }
-
- i_pos = fat_i_pos_read(sbi, inode);
- *lenp = 5;
- fh[0] = inode->i_ino;
- fh[1] = inode->i_generation;
- fh[2] = i_pos >> 8;
- fh[3] = ((i_pos & 0xf0) << 24) | MSDOS_I(inode)->i_logstart;
- fh[4] = (i_pos & 0x0f) << 28;
- if (parent)
- fh[4] |= MSDOS_I(parent)->i_logstart;
- return 3;
-}
-
-static struct dentry *fat_get_parent(struct dentry *child)
-{
- struct super_block *sb = child->d_sb;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
- loff_t i_pos;
- struct dentry *parent;
- struct inode *inode;
- int err;
-
- lock_super(sb);
-
- err = fat_get_dotdot_entry(child->d_inode, &bh, &de, &i_pos);
- if (err) {
- parent = ERR_PTR(err);
- goto out;
- }
- inode = fat_build_inode(sb, de, i_pos);
- brelse(bh);
-
- parent = d_obtain_alias(inode);
-out:
- unlock_super(sb);
-
- return parent;
-}
-
static const struct export_operations fat_export_ops = {
- .encode_fh = fat_encode_fh,
.fh_to_dentry = fat_fh_to_dentry,
+ .fh_to_parent = fat_fh_to_parent,
.get_parent = fat_get_parent,
};
@@ -797,16 +715,19 @@ static int fat_show_options(struct seq_file *m, struct dentry *root)
struct fat_mount_options *opts = &sbi->options;
int isvfat = opts->isvfat;
- if (opts->fs_uid != 0)
- seq_printf(m, ",uid=%u", opts->fs_uid);
- if (opts->fs_gid != 0)
- seq_printf(m, ",gid=%u", opts->fs_gid);
+ if (!uid_eq(opts->fs_uid, GLOBAL_ROOT_UID))
+ seq_printf(m, ",uid=%u",
+ from_kuid_munged(&init_user_ns, opts->fs_uid));
+ if (!gid_eq(opts->fs_gid, GLOBAL_ROOT_GID))
+ seq_printf(m, ",gid=%u",
+ from_kgid_munged(&init_user_ns, opts->fs_gid));
seq_printf(m, ",fmask=%04o", opts->fs_fmask);
seq_printf(m, ",dmask=%04o", opts->fs_dmask);
if (opts->allow_utime)
seq_printf(m, ",allow_utime=%04o", opts->allow_utime);
if (sbi->nls_disk)
- seq_printf(m, ",codepage=%s", sbi->nls_disk->charset);
+ /* strip "cp" prefix from displayed option */
+ seq_printf(m, ",codepage=%s", &sbi->nls_disk->charset[2]);
if (isvfat) {
if (sbi->nls_io)
seq_printf(m, ",iocharset=%s", sbi->nls_io->charset);
@@ -835,6 +756,8 @@ 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)
@@ -856,8 +779,12 @@ static int fat_show_options(struct seq_file *m, struct dentry *root)
}
if (opts->flush)
seq_puts(m, ",flush");
- if (opts->tz_utc)
- seq_puts(m, ",tz=UTC");
+ if (opts->tz_set) {
+ if (opts->time_offset)
+ seq_printf(m, ",time_offset=%d", opts->time_offset);
+ else
+ seq_puts(m, ",tz=UTC");
+ }
if (opts->errors == FAT_ERRORS_CONT)
seq_puts(m, ",errors=continue");
else if (opts->errors == FAT_ERRORS_PANIC)
@@ -879,7 +806,8 @@ enum {
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_obsolete, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont,
- Opt_err_panic, Opt_err_ro, Opt_discard, Opt_err,
+ Opt_err_panic, Opt_err_ro, Opt_discard, Opt_nfs, Opt_time_offset,
+ Opt_err,
};
static const match_table_t fat_tokens = {
@@ -904,10 +832,12 @@ static const match_table_t fat_tokens = {
{Opt_immutable, "sys_immutable"},
{Opt_flush, "flush"},
{Opt_tz_utc, "tz=UTC"},
+ {Opt_time_offset, "time_offset=%d"},
{Opt_err_cont, "errors=continue"},
{Opt_err_panic, "errors=panic"},
{Opt_err_ro, "errors=remount-ro"},
{Opt_discard, "discard"},
+ {Opt_nfs, "nfs"},
{Opt_obsolete, "conv=binary"},
{Opt_obsolete, "conv=text"},
{Opt_obsolete, "conv=auto"},
@@ -987,7 +917,8 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat,
opts->utf8 = opts->unicode_xlate = 0;
opts->numtail = 1;
opts->usefree = opts->nocase = 0;
- opts->tz_utc = 0;
+ opts->tz_set = 0;
+ opts->nfs = 0;
opts->errors = FAT_ERRORS_RO;
*debug = 0;
@@ -1042,44 +973,57 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat,
break;
case Opt_uid:
if (match_int(&args[0], &option))
- return 0;
- opts->fs_uid = option;
+ return -EINVAL;
+ opts->fs_uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(opts->fs_uid))
+ return -EINVAL;
break;
case Opt_gid:
if (match_int(&args[0], &option))
- return 0;
- opts->fs_gid = option;
+ return -EINVAL;
+ opts->fs_gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(opts->fs_gid))
+ return -EINVAL;
break;
case Opt_umask:
if (match_octal(&args[0], &option))
- return 0;
+ return -EINVAL;
opts->fs_fmask = opts->fs_dmask = option;
break;
case Opt_dmask:
if (match_octal(&args[0], &option))
- return 0;
+ return -EINVAL;
opts->fs_dmask = option;
break;
case Opt_fmask:
if (match_octal(&args[0], &option))
- return 0;
+ return -EINVAL;
opts->fs_fmask = option;
break;
case Opt_allow_utime:
if (match_octal(&args[0], &option))
- return 0;
+ return -EINVAL;
opts->allow_utime = option & (S_IWGRP | S_IWOTH);
break;
case Opt_codepage:
if (match_int(&args[0], &option))
- return 0;
+ return -EINVAL;
opts->codepage = option;
break;
case Opt_flush:
opts->flush = 1;
break;
+ case Opt_time_offset:
+ if (match_int(&args[0], &option))
+ return -EINVAL;
+ if (option < -12 * 60 || option > 12 * 60)
+ return -EINVAL;
+ opts->tz_set = 1;
+ opts->time_offset = option;
+ break;
case Opt_tz_utc:
- opts->tz_utc = 1;
+ opts->tz_set = 1;
+ opts->time_offset = 0;
break;
case Opt_err_cont:
opts->errors = FAT_ERRORS_CONT;
@@ -1148,6 +1092,9 @@ 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:
@@ -1338,6 +1285,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
b = (struct fat_boot_sector *) bh->b_data;
}
+ mutex_init(&sbi->s_lock);
sbi->cluster_size = sb->s_blocksize * sbi->sec_per_clus;
sbi->cluster_bits = ffs(sbi->cluster_size) - 1;
sbi->fats = b->fats;
@@ -1396,7 +1344,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
sbi->dir_entries = get_unaligned_le16(&b->dir_entries);
if (sbi->dir_entries & (sbi->dir_per_block - 1)) {
if (!silent)
- fat_msg(sb, KERN_ERR, "bogus directroy-entries per block"
+ fat_msg(sb, KERN_ERR, "bogus directory-entries per block"
" (%u)", sbi->dir_entries);
brelse(bh);
goto out_invalid;
@@ -1438,6 +1386,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
/* set up enough so that it can read an inode */
fat_hash_init(sb);
+ dir_hash_init(sb);
fat_ent_access_init(sb);
/*
@@ -1492,12 +1441,21 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
}
error = -ENOMEM;
insert_inode_hash(root_inode);
+ fat_attach(root_inode, 0);
sb->s_root = d_make_root(root_inode);
if (!sb->s_root) {
fat_msg(sb, KERN_ERR, "get root inode failed");
goto out_fail;
}
+ if (sbi->options.discard) {
+ struct request_queue *q = bdev_get_queue(sb->s_bdev);
+ if (!blk_queue_discard(q))
+ fat_msg(sb, KERN_WARNING,
+ "mounting with \"discard\" option, but "
+ "the device does not support discard");
+ }
+
return 0;
out_invalid:
@@ -1531,18 +1489,14 @@ 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
+
+ /* if we used wait=1, sync_inode_metadata waits for the io for the
+ * inode to finish. So wait=0 is sent down to sync_inode_metadata
* and filemap_fdatawrite is used for the data blocks
*/
- ret = sync_inode(inode, &wbc);
+ ret = sync_inode_metadata(inode, 0);
if (!ret)
- ret = filemap_fdatawrite(mapping);
+ ret = filemap_fdatawrite(inode->i_mapping);
return ret;
}