summaryrefslogtreecommitdiffstats
path: root/fs/ext2
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext2')
-rw-r--r--fs/ext2/balloc.c98
-rw-r--r--fs/ext2/dir.c2
-rw-r--r--fs/ext2/ext2.h6
-rw-r--r--fs/ext2/file.c4
-rw-r--r--fs/ext2/inode.c37
-rw-r--r--fs/ext2/ioctl.c12
-rw-r--r--fs/ext2/namei.c12
-rw-r--r--fs/ext2/super.c66
8 files changed, 152 insertions, 85 deletions
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 377ad172d74..e7b2bafa1dd 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -69,9 +69,53 @@ struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
return desc + offset;
}
+static int ext2_valid_block_bitmap(struct super_block *sb,
+ struct ext2_group_desc *desc,
+ unsigned int block_group,
+ struct buffer_head *bh)
+{
+ ext2_grpblk_t offset;
+ ext2_grpblk_t next_zero_bit;
+ ext2_fsblk_t bitmap_blk;
+ ext2_fsblk_t group_first_block;
+
+ group_first_block = ext2_group_first_block_no(sb, block_group);
+
+ /* check whether block bitmap block number is set */
+ bitmap_blk = le32_to_cpu(desc->bg_block_bitmap);
+ offset = bitmap_blk - group_first_block;
+ if (!ext2_test_bit(offset, bh->b_data))
+ /* bad block bitmap */
+ goto err_out;
+
+ /* check whether the inode bitmap block number is set */
+ bitmap_blk = le32_to_cpu(desc->bg_inode_bitmap);
+ offset = bitmap_blk - group_first_block;
+ if (!ext2_test_bit(offset, bh->b_data))
+ /* bad block bitmap */
+ goto err_out;
+
+ /* check whether the inode table block number is set */
+ bitmap_blk = le32_to_cpu(desc->bg_inode_table);
+ offset = bitmap_blk - group_first_block;
+ next_zero_bit = ext2_find_next_zero_bit(bh->b_data,
+ offset + EXT2_SB(sb)->s_itb_per_group,
+ offset);
+ if (next_zero_bit >= offset + EXT2_SB(sb)->s_itb_per_group)
+ /* good bitmap for inode tables */
+ return 1;
+
+err_out:
+ ext2_error(sb, __FUNCTION__,
+ "Invalid block bitmap - "
+ "block_group = %d, block = %lu",
+ block_group, bitmap_blk);
+ return 0;
+}
+
/*
- * Read the bitmap for a given block_group, reading into the specified
- * slot in the superblock's bitmap cache.
+ * Read the bitmap for a given block_group,and validate the
+ * bits for block/inode/inode tables are set in the bitmaps
*
* Return buffer_head on success or NULL in case of failure.
*/
@@ -80,17 +124,36 @@ read_block_bitmap(struct super_block *sb, unsigned int block_group)
{
struct ext2_group_desc * desc;
struct buffer_head * bh = NULL;
-
- desc = ext2_get_group_desc (sb, block_group, NULL);
+ ext2_fsblk_t bitmap_blk;
+
+ desc = ext2_get_group_desc(sb, block_group, NULL);
if (!desc)
- goto error_out;
- bh = sb_bread(sb, le32_to_cpu(desc->bg_block_bitmap));
- if (!bh)
- ext2_error (sb, "read_block_bitmap",
+ return NULL;
+ bitmap_blk = le32_to_cpu(desc->bg_block_bitmap);
+ bh = sb_getblk(sb, bitmap_blk);
+ if (unlikely(!bh)) {
+ ext2_error(sb, __FUNCTION__,
+ "Cannot read block bitmap - "
+ "block_group = %d, block_bitmap = %u",
+ block_group, le32_to_cpu(desc->bg_block_bitmap));
+ return NULL;
+ }
+ if (likely(bh_uptodate_or_lock(bh)))
+ return bh;
+
+ if (bh_submit_read(bh) < 0) {
+ brelse(bh);
+ ext2_error(sb, __FUNCTION__,
"Cannot read block bitmap - "
"block_group = %d, block_bitmap = %u",
block_group, le32_to_cpu(desc->bg_block_bitmap));
-error_out:
+ return NULL;
+ }
+ if (!ext2_valid_block_bitmap(sb, desc, block_group, bh)) {
+ brelse(bh);
+ return NULL;
+ }
+
return bh;
}
@@ -474,11 +537,13 @@ do_more:
in_range (block, le32_to_cpu(desc->bg_inode_table),
sbi->s_itb_per_group) ||
in_range (block + count - 1, le32_to_cpu(desc->bg_inode_table),
- sbi->s_itb_per_group))
+ sbi->s_itb_per_group)) {
ext2_error (sb, "ext2_free_blocks",
"Freeing blocks in system zones - "
"Block = %lu, count = %lu",
block, count);
+ goto error_return;
+ }
for (i = 0, group_freed = 0; i < count; i++) {
if (!ext2_clear_bit_atomic(sb_bgl_lock(sbi, block_group),
@@ -1250,8 +1315,8 @@ retry_alloc:
smp_rmb();
/*
- * Now search the rest of the groups. We assume that
- * i and gdp correctly point to the last group visited.
+ * Now search the rest of the groups. We assume that
+ * group_no and gdp correctly point to the last group visited.
*/
for (bgi = 0; bgi < ngroups; bgi++) {
group_no++;
@@ -1311,11 +1376,13 @@ allocated:
in_range(ret_block, le32_to_cpu(gdp->bg_inode_table),
EXT2_SB(sb)->s_itb_per_group) ||
in_range(ret_block + num - 1, le32_to_cpu(gdp->bg_inode_table),
- EXT2_SB(sb)->s_itb_per_group))
+ EXT2_SB(sb)->s_itb_per_group)) {
ext2_error(sb, "ext2_new_blocks",
"Allocating block in system zone - "
"blocks from "E2FSBLK", length %lu",
ret_block, num);
+ goto out;
+ }
performed_allocation = 1;
@@ -1466,9 +1533,6 @@ int ext2_bg_has_super(struct super_block *sb, int group)
*/
unsigned long ext2_bg_num_gdb(struct super_block *sb, int group)
{
- if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)&&
- !ext2_group_sparse(group))
- return 0;
- return EXT2_SB(sb)->s_gdb_count;
+ return ext2_bg_has_super(sb, group) ? EXT2_SB(sb)->s_gdb_count : 0;
}
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index d868e26c15e..8dededd80fe 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -703,7 +703,7 @@ const struct file_operations ext2_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.readdir = ext2_readdir,
- .ioctl = ext2_ioctl,
+ .unlocked_ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext2_compat_ioctl,
#endif
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index c87ae29c19c..47d88da2d33 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -124,9 +124,8 @@ extern void ext2_check_inodes_bitmap (struct super_block *);
extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
/* inode.c */
-extern void ext2_read_inode (struct inode *);
+extern struct inode *ext2_iget (struct super_block *, unsigned long);
extern int ext2_write_inode (struct inode *, int);
-extern void ext2_put_inode (struct inode *);
extern void ext2_delete_inode (struct inode *);
extern int ext2_sync_inode (struct inode *);
extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
@@ -139,8 +138,7 @@ int __ext2_write_begin(struct file *file, struct address_space *mapping,
struct page **pagep, void **fsdata);
/* ioctl.c */
-extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
- unsigned long);
+extern long ext2_ioctl(struct file *, unsigned int, unsigned long);
extern long ext2_compat_ioctl(struct file *, unsigned int, unsigned long);
/* namei.c */
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index c051798459a..5f2fa9c3629 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -48,7 +48,7 @@ const struct file_operations ext2_file_operations = {
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write,
- .ioctl = ext2_ioctl,
+ .unlocked_ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext2_compat_ioctl,
#endif
@@ -65,7 +65,7 @@ const struct file_operations ext2_xip_file_operations = {
.llseek = generic_file_llseek,
.read = xip_file_read,
.write = xip_file_write,
- .ioctl = ext2_ioctl,
+ .unlocked_ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext2_compat_ioctl,
#endif
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index b1ab32ab5a7..c6200680542 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -286,15 +286,12 @@ static unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
* ext2_find_goal - find a prefered place for allocation.
* @inode: owner
* @block: block we want
- * @chain: chain of indirect blocks
* @partial: pointer to the last triple within a chain
*
* Returns preferred place for a block (the goal).
*/
-static inline int ext2_find_goal(struct inode *inode,
- long block,
- Indirect chain[4],
+static inline int ext2_find_goal(struct inode *inode, long block,
Indirect *partial)
{
struct ext2_block_alloc_info *block_i;
@@ -569,7 +566,6 @@ static void ext2_splice_branch(struct inode *inode,
*
* `handle' can be NULL if create == 0.
*
- * The BKL may not be held on entry here. Be sure to take it early.
* return > 0, # of blocks mapped or allocated.
* return = 0, if plain lookup failed.
* return < 0, error case.
@@ -639,7 +635,7 @@ reread:
if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info))
ext2_init_block_alloc_info(inode);
- goal = ext2_find_goal(inode, iblock, chain, partial);
+ goal = ext2_find_goal(inode, iblock, partial);
/* the number of blocks need to allocate for [d,t]indirect blocks */
indirect_blks = (chain + depth) - partial - 1;
@@ -1185,22 +1181,33 @@ void ext2_get_inode_flags(struct ext2_inode_info *ei)
ei->i_flags |= EXT2_DIRSYNC_FL;
}
-void ext2_read_inode (struct inode * inode)
+struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
{
- struct ext2_inode_info *ei = EXT2_I(inode);
- ino_t ino = inode->i_ino;
+ struct ext2_inode_info *ei;
struct buffer_head * bh;
- struct ext2_inode * raw_inode = ext2_get_inode(inode->i_sb, ino, &bh);
+ struct ext2_inode *raw_inode;
+ struct inode *inode;
+ long ret = -EIO;
int n;
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ ei = EXT2_I(inode);
#ifdef CONFIG_EXT2_FS_POSIX_ACL
ei->i_acl = EXT2_ACL_NOT_CACHED;
ei->i_default_acl = EXT2_ACL_NOT_CACHED;
#endif
ei->i_block_alloc_info = NULL;
- if (IS_ERR(raw_inode))
+ raw_inode = ext2_get_inode(inode->i_sb, ino, &bh);
+ if (IS_ERR(raw_inode)) {
+ ret = PTR_ERR(raw_inode);
goto bad_inode;
+ }
inode->i_mode = le16_to_cpu(raw_inode->i_mode);
inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
@@ -1224,6 +1231,7 @@ void ext2_read_inode (struct inode * inode)
if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) {
/* this inode is deleted */
brelse (bh);
+ ret = -ESTALE;
goto bad_inode;
}
inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
@@ -1290,11 +1298,12 @@ void ext2_read_inode (struct inode * inode)
}
brelse (bh);
ext2_set_inode_flags(inode);
- return;
+ unlock_new_inode(inode);
+ return inode;
bad_inode:
- make_bad_inode(inode);
- return;
+ iget_failed(inode);
+ return ERR_PTR(ret);
}
static int ext2_update_inode(struct inode * inode, int do_sync)
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 320b2cb3d4d..b8ea11fee5c 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -17,9 +17,9 @@
#include <asm/uaccess.h>
-int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
- unsigned long arg)
+long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
+ struct inode *inode = filp->f_dentry->d_inode;
struct ext2_inode_info *ei = EXT2_I(inode);
unsigned int flags;
unsigned short rsv_window_size;
@@ -141,9 +141,6 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
#ifdef CONFIG_COMPAT
long ext2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- int ret;
-
/* These are just misnamed, they actually get/put from/to user an int */
switch (cmd) {
case EXT2_IOC32_GETFLAGS:
@@ -161,9 +158,6 @@ long ext2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
default:
return -ENOIOCTLCMD;
}
- lock_kernel();
- ret = ext2_ioctl(inode, file, cmd, (unsigned long) compat_ptr(arg));
- unlock_kernel();
- return ret;
+ return ext2_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
}
#endif
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index e69beed839a..80c97fd8c57 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -63,9 +63,9 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, str
ino = ext2_inode_by_name(dir, dentry);
inode = NULL;
if (ino) {
- inode = iget(dir->i_sb, ino);
- if (!inode)
- return ERR_PTR(-EACCES);
+ inode = ext2_iget(dir->i_sb, ino);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
}
return d_splice_alias(inode, dentry);
}
@@ -83,10 +83,10 @@ struct dentry *ext2_get_parent(struct dentry *child)
ino = ext2_inode_by_name(child->d_inode, &dotdot);
if (!ino)
return ERR_PTR(-ENOENT);
- inode = iget(child->d_inode->i_sb, ino);
+ inode = ext2_iget(child->d_inode->i_sb, ino);
- if (!inode)
- return ERR_PTR(-EACCES);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
parent = d_alloc_anon(inode);
if (!parent) {
iput(inode);
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 6abaf75163f..088b011bb97 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -234,16 +234,16 @@ static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs)
le16_to_cpu(es->s_def_resgid) != EXT2_DEF_RESGID) {
seq_printf(seq, ",resgid=%u", sbi->s_resgid);
}
- if (test_opt(sb, ERRORS_CONT)) {
+ if (test_opt(sb, ERRORS_RO)) {
int def_errors = le16_to_cpu(es->s_errors);
if (def_errors == EXT2_ERRORS_PANIC ||
- def_errors == EXT2_ERRORS_RO) {
- seq_puts(seq, ",errors=continue");
+ def_errors == EXT2_ERRORS_CONTINUE) {
+ seq_puts(seq, ",errors=remount-ro");
}
}
- if (test_opt(sb, ERRORS_RO))
- seq_puts(seq, ",errors=remount-ro");
+ if (test_opt(sb, ERRORS_CONT))
+ seq_puts(seq, ",errors=continue");
if (test_opt(sb, ERRORS_PANIC))
seq_puts(seq, ",errors=panic");
if (test_opt(sb, NO_UID32))
@@ -285,6 +285,9 @@ static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs)
seq_puts(seq, ",xip");
#endif
+ if (!test_opt(sb, RESERVATION))
+ seq_puts(seq, ",noreservation");
+
return 0;
}
@@ -296,7 +299,6 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *da
static const struct super_operations ext2_sops = {
.alloc_inode = ext2_alloc_inode,
.destroy_inode = ext2_destroy_inode,
- .read_inode = ext2_read_inode,
.write_inode = ext2_write_inode,
.delete_inode = ext2_delete_inode,
.put_super = ext2_put_super,
@@ -326,11 +328,10 @@ static struct inode *ext2_nfs_get_inode(struct super_block *sb,
* it might be "neater" to call ext2_get_inode first and check
* if the inode is valid.....
*/
- inode = iget(sb, ino);
- if (inode == NULL)
- return ERR_PTR(-ENOMEM);
- if (is_bad_inode(inode) ||
- (generation && inode->i_generation != generation)) {
+ inode = ext2_iget(sb, ino);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
+ if (generation && inode->i_generation != generation) {
/* we didn't find the right inode.. */
iput(inode);
return ERR_PTR(-ESTALE);
@@ -617,27 +618,24 @@ static int ext2_setup_super (struct super_block * sb,
return res;
}
-static int ext2_check_descriptors (struct super_block * sb)
+static int ext2_check_descriptors(struct super_block *sb)
{
int i;
- int desc_block = 0;
struct ext2_sb_info *sbi = EXT2_SB(sb);
unsigned long first_block = le32_to_cpu(sbi->s_es->s_first_data_block);
unsigned long last_block;
- struct ext2_group_desc * gdp = NULL;
ext2_debug ("Checking group descriptors");
- for (i = 0; i < sbi->s_groups_count; i++)
- {
+ for (i = 0; i < sbi->s_groups_count; i++) {
+ struct ext2_group_desc *gdp = ext2_get_group_desc(sb, i, NULL);
+
if (i == sbi->s_groups_count - 1)
last_block = le32_to_cpu(sbi->s_es->s_blocks_count) - 1;
else
last_block = first_block +
(EXT2_BLOCKS_PER_GROUP(sb) - 1);
- if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
- gdp = (struct ext2_group_desc *) sbi->s_group_desc[desc_block++]->b_data;
if (le32_to_cpu(gdp->bg_block_bitmap) < first_block ||
le32_to_cpu(gdp->bg_block_bitmap) > last_block)
{
@@ -667,7 +665,6 @@ static int ext2_check_descriptors (struct super_block * sb)
return 0;
}
first_block += EXT2_BLOCKS_PER_GROUP(sb);
- gdp++;
}
return 1;
}
@@ -750,6 +747,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
unsigned long logic_sb_block;
unsigned long offset = 0;
unsigned long def_mount_opts;
+ long ret = -EINVAL;
int blocksize = BLOCK_SIZE;
int db_count;
int i, j;
@@ -820,10 +818,10 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC)
set_opt(sbi->s_mount_opt, ERRORS_PANIC);
- else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_RO)
- set_opt(sbi->s_mount_opt, ERRORS_RO);
- else
+ else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_CONTINUE)
set_opt(sbi->s_mount_opt, ERRORS_CONT);
+ else
+ set_opt(sbi->s_mount_opt, ERRORS_RO);
sbi->s_resuid = le16_to_cpu(es->s_def_resuid);
sbi->s_resgid = le16_to_cpu(es->s_def_resgid);
@@ -868,8 +866,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
- if ((ext2_use_xip(sb)) && ((blocksize != PAGE_SIZE) ||
- (sb->s_blocksize != blocksize))) {
+ if (ext2_use_xip(sb) && blocksize != PAGE_SIZE) {
if (!silent)
printk("XIP: Unsupported blocksize\n");
goto failed_mount;
@@ -1046,19 +1043,24 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
sb->s_op = &ext2_sops;
sb->s_export_op = &ext2_export_ops;
sb->s_xattr = ext2_xattr_handlers;
- root = iget(sb, EXT2_ROOT_INO);
- sb->s_root = d_alloc_root(root);
- if (!sb->s_root) {
- iput(root);
- printk(KERN_ERR "EXT2-fs: get root inode failed\n");
+ root = ext2_iget(sb, EXT2_ROOT_INO);
+ if (IS_ERR(root)) {
+ ret = PTR_ERR(root);
goto failed_mount3;
}
if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
- dput(sb->s_root);
- sb->s_root = NULL;
+ iput(root);
printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n");
goto failed_mount3;
}
+
+ sb->s_root = d_alloc_root(root);
+ if (!sb->s_root) {
+ iput(root);
+ printk(KERN_ERR "EXT2-fs: get root inode failed\n");
+ ret = -ENOMEM;
+ goto failed_mount3;
+ }
if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL))
ext2_warning(sb, __FUNCTION__,
"mounting ext3 filesystem as ext2");
@@ -1085,7 +1087,7 @@ failed_mount:
failed_sbi:
sb->s_fs_info = NULL;
kfree(sbi);
- return -EINVAL;
+ return ret;
}
static void ext2_commit_super (struct super_block * sb,