aboutsummaryrefslogtreecommitdiff
path: root/fs/ext3
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext3')
-rw-r--r--fs/ext3/dir.c20
-rw-r--r--fs/ext3/ioctl.c12
-rw-r--r--fs/ext3/namei.c84
-rw-r--r--fs/ext3/super.c68
4 files changed, 93 insertions, 91 deletions
diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c
index 4c82531ea0a..5853f4440af 100644
--- a/fs/ext3/dir.c
+++ b/fs/ext3/dir.c
@@ -456,17 +456,8 @@ static int ext3_dx_readdir(struct file * filp,
if (info->extra_fname) {
if (call_filldir(filp, dirent, filldir, info->extra_fname))
goto finished;
-
info->extra_fname = NULL;
- info->curr_node = rb_next(info->curr_node);
- if (!info->curr_node) {
- if (info->next_hash == ~0) {
- filp->f_pos = EXT3_HTREE_EOF;
- goto finished;
- }
- info->curr_hash = info->next_hash;
- info->curr_minor_hash = 0;
- }
+ goto next_node;
} else if (!info->curr_node)
info->curr_node = rb_first(&info->root);
@@ -498,9 +489,14 @@ static int ext3_dx_readdir(struct file * filp,
info->curr_minor_hash = fname->minor_hash;
if (call_filldir(filp, dirent, filldir, fname))
break;
-
+ next_node:
info->curr_node = rb_next(info->curr_node);
- if (!info->curr_node) {
+ if (info->curr_node) {
+ fname = rb_entry(info->curr_node, struct fname,
+ rb_hash);
+ info->curr_hash = fname->hash;
+ info->curr_minor_hash = fname->minor_hash;
+ } else {
if (info->next_hash == ~0) {
filp->f_pos = EXT3_HTREE_EOF;
break;
diff --git a/fs/ext3/ioctl.c b/fs/ext3/ioctl.c
index 0d0c7015164..b7394d05ee8 100644
--- a/fs/ext3/ioctl.c
+++ b/fs/ext3/ioctl.c
@@ -239,7 +239,7 @@ setrsvsz_out:
case EXT3_IOC_GROUP_EXTEND: {
ext3_fsblk_t n_blocks_count;
struct super_block *sb = inode->i_sb;
- int err;
+ int err, err2;
if (!capable(CAP_SYS_RESOURCE))
return -EPERM;
@@ -254,8 +254,10 @@ setrsvsz_out:
}
err = ext3_group_extend(sb, EXT3_SB(sb)->s_es, n_blocks_count);
journal_lock_updates(EXT3_SB(sb)->s_journal);
- journal_flush(EXT3_SB(sb)->s_journal);
+ err2 = journal_flush(EXT3_SB(sb)->s_journal);
journal_unlock_updates(EXT3_SB(sb)->s_journal);
+ if (err == 0)
+ err = err2;
group_extend_out:
mnt_drop_write(filp->f_path.mnt);
return err;
@@ -263,7 +265,7 @@ group_extend_out:
case EXT3_IOC_GROUP_ADD: {
struct ext3_new_group_data input;
struct super_block *sb = inode->i_sb;
- int err;
+ int err, err2;
if (!capable(CAP_SYS_RESOURCE))
return -EPERM;
@@ -280,8 +282,10 @@ group_extend_out:
err = ext3_group_add(sb, &input);
journal_lock_updates(EXT3_SB(sb)->s_journal);
- journal_flush(EXT3_SB(sb)->s_journal);
+ err2 = journal_flush(EXT3_SB(sb)->s_journal);
journal_unlock_updates(EXT3_SB(sb)->s_journal);
+ if (err == 0)
+ err = err2;
group_add_out:
mnt_drop_write(filp->f_path.mnt);
return err;
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
index de13e919cd8..3e5edc92aa0 100644
--- a/fs/ext3/namei.c
+++ b/fs/ext3/namei.c
@@ -159,7 +159,7 @@ static void dx_set_count (struct dx_entry *entries, unsigned value);
static void dx_set_limit (struct dx_entry *entries, unsigned value);
static unsigned dx_root_limit (struct inode *dir, unsigned infosize);
static unsigned dx_node_limit (struct inode *dir);
-static struct dx_frame *dx_probe(struct dentry *dentry,
+static struct dx_frame *dx_probe(struct qstr *entry,
struct inode *dir,
struct dx_hash_info *hinfo,
struct dx_frame *frame,
@@ -176,8 +176,9 @@ static int ext3_htree_next_block(struct inode *dir, __u32 hash,
struct dx_frame *frame,
struct dx_frame *frames,
__u32 *start_hash);
-static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,
- struct ext3_dir_entry_2 **res_dir, int *err);
+static struct buffer_head * ext3_dx_find_entry(struct inode *dir,
+ struct qstr *entry, struct ext3_dir_entry_2 **res_dir,
+ int *err);
static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
struct inode *inode);
@@ -342,7 +343,7 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
* back to userspace.
*/
static struct dx_frame *
-dx_probe(struct dentry *dentry, struct inode *dir,
+dx_probe(struct qstr *entry, struct inode *dir,
struct dx_hash_info *hinfo, struct dx_frame *frame_in, int *err)
{
unsigned count, indirect;
@@ -353,8 +354,6 @@ dx_probe(struct dentry *dentry, struct inode *dir,
u32 hash;
frame->bh = NULL;
- if (dentry)
- dir = dentry->d_parent->d_inode;
if (!(bh = ext3_bread (NULL,dir, 0, 0, err)))
goto fail;
root = (struct dx_root *) bh->b_data;
@@ -370,8 +369,8 @@ dx_probe(struct dentry *dentry, struct inode *dir,
}
hinfo->hash_version = root->info.hash_version;
hinfo->seed = EXT3_SB(dir->i_sb)->s_hash_seed;
- if (dentry)
- ext3fs_dirhash(dentry->d_name.name, dentry->d_name.len, hinfo);
+ if (entry)
+ ext3fs_dirhash(entry->name, entry->len, hinfo);
hash = hinfo->hash;
if (root->info.unused_flags & 1) {
@@ -803,15 +802,15 @@ static inline int ext3_match (int len, const char * const name,
*/
static inline int search_dirblock(struct buffer_head * bh,
struct inode *dir,
- struct dentry *dentry,
+ struct qstr *child,
unsigned long offset,
struct ext3_dir_entry_2 ** res_dir)
{
struct ext3_dir_entry_2 * de;
char * dlimit;
int de_len;
- const char *name = dentry->d_name.name;
- int namelen = dentry->d_name.len;
+ const char *name = child->name;
+ int namelen = child->len;
de = (struct ext3_dir_entry_2 *) bh->b_data;
dlimit = bh->b_data + dir->i_sb->s_blocksize;
@@ -850,8 +849,9 @@ static inline int search_dirblock(struct buffer_head * bh,
* The returned buffer_head has ->b_count elevated. The caller is expected
* to brelse() it when appropriate.
*/
-static struct buffer_head * ext3_find_entry (struct dentry *dentry,
- struct ext3_dir_entry_2 ** res_dir)
+static struct buffer_head *ext3_find_entry(struct inode *dir,
+ struct qstr *entry,
+ struct ext3_dir_entry_2 **res_dir)
{
struct super_block * sb;
struct buffer_head * bh_use[NAMEI_RA_SIZE];
@@ -863,16 +863,15 @@ static struct buffer_head * ext3_find_entry (struct dentry *dentry,
buffer */
int num = 0;
int nblocks, i, err;
- struct inode *dir = dentry->d_parent->d_inode;
int namelen;
*res_dir = NULL;
sb = dir->i_sb;
- namelen = dentry->d_name.len;
+ namelen = entry->len;
if (namelen > EXT3_NAME_LEN)
return NULL;
if (is_dx(dir)) {
- bh = ext3_dx_find_entry(dentry, res_dir, &err);
+ bh = ext3_dx_find_entry(dir, entry, res_dir, &err);
/*
* On success, or if the error was file not found,
* return. Otherwise, fall back to doing a search the
@@ -923,7 +922,7 @@ restart:
brelse(bh);
goto next;
}
- i = search_dirblock(bh, dir, dentry,
+ i = search_dirblock(bh, dir, entry,
block << EXT3_BLOCK_SIZE_BITS(sb), res_dir);
if (i == 1) {
EXT3_I(dir)->i_dir_start_lookup = block;
@@ -957,8 +956,9 @@ cleanup_and_exit:
return ret;
}
-static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,
- struct ext3_dir_entry_2 **res_dir, int *err)
+static struct buffer_head * ext3_dx_find_entry(struct inode *dir,
+ struct qstr *entry, struct ext3_dir_entry_2 **res_dir,
+ int *err)
{
struct super_block * sb;
struct dx_hash_info hinfo;
@@ -968,14 +968,13 @@ static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry,
struct buffer_head *bh;
unsigned long block;
int retval;
- int namelen = dentry->d_name.len;
- const u8 *name = dentry->d_name.name;
- struct inode *dir = dentry->d_parent->d_inode;
+ int namelen = entry->len;
+ const u8 *name = entry->name;
sb = dir->i_sb;
/* NFS may look up ".." - look at dx_root directory block */
- if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
- if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))
+ if (namelen > 2 || name[0] != '.'|| (namelen == 2 && name[1] != '.')) {
+ if (!(frame = dx_probe(entry, dir, &hinfo, frames, err)))
return NULL;
} else {
frame = frames;
@@ -1036,7 +1035,7 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str
if (dentry->d_name.len > EXT3_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
- bh = ext3_find_entry(dentry, &de);
+ bh = ext3_find_entry(dir, &dentry->d_name, &de);
inode = NULL;
if (bh) {
unsigned long ino = le32_to_cpu(de->inode);
@@ -1057,18 +1056,11 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str
struct dentry *ext3_get_parent(struct dentry *child)
{
unsigned long ino;
- struct dentry *parent;
- struct inode *inode;
- struct dentry dotdot;
+ struct qstr dotdot = {.name = "..", .len = 2};
struct ext3_dir_entry_2 * de;
struct buffer_head *bh;
- dotdot.d_name.name = "..";
- dotdot.d_name.len = 2;
- dotdot.d_parent = child; /* confusing, isn't it! */
-
- bh = ext3_find_entry(&dotdot, &de);
- inode = NULL;
+ bh = ext3_find_entry(child->d_inode, &dotdot, &de);
if (!bh)
return ERR_PTR(-ENOENT);
ino = le32_to_cpu(de->inode);
@@ -1080,16 +1072,7 @@ struct dentry *ext3_get_parent(struct dentry *child)
return ERR_PTR(-EIO);
}
- inode = ext3_iget(child->d_inode->i_sb, ino);
- if (IS_ERR(inode))
- return ERR_CAST(inode);
-
- parent = d_alloc_anon(inode);
- if (!parent) {
- iput(inode);
- parent = ERR_PTR(-ENOMEM);
- }
- return parent;
+ return d_obtain_alias(ext3_iget(child->d_inode->i_sb, ino));
}
#define S_SHIFT 12
@@ -1503,7 +1486,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
struct ext3_dir_entry_2 *de;
int err;
- frame = dx_probe(dentry, NULL, &hinfo, frames, &err);
+ frame = dx_probe(&dentry->d_name, dir, &hinfo, frames, &err);
if (!frame)
return err;
entries = frame->entries;
@@ -2056,7 +2039,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
return PTR_ERR(handle);
retval = -ENOENT;
- bh = ext3_find_entry (dentry, &de);
+ bh = ext3_find_entry(dir, &dentry->d_name, &de);
if (!bh)
goto end_rmdir;
@@ -2118,7 +2101,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)
handle->h_sync = 1;
retval = -ENOENT;
- bh = ext3_find_entry (dentry, &de);
+ bh = ext3_find_entry(dir, &dentry->d_name, &de);
if (!bh)
goto end_unlink;
@@ -2276,7 +2259,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
handle->h_sync = 1;
- old_bh = ext3_find_entry (old_dentry, &old_de);
+ old_bh = ext3_find_entry(old_dir, &old_dentry->d_name, &old_de);
/*
* Check for inode number is _not_ due to possible IO errors.
* We might rmdir the source, keep it as pwd of some process
@@ -2289,7 +2272,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
goto end_rename;
new_inode = new_dentry->d_inode;
- new_bh = ext3_find_entry (new_dentry, &new_de);
+ new_bh = ext3_find_entry(new_dir, &new_dentry->d_name, &new_de);
if (new_bh) {
if (!new_inode) {
brelse (new_bh);
@@ -2355,7 +2338,8 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
struct buffer_head *old_bh2;
struct ext3_dir_entry_2 *old_de2;
- old_bh2 = ext3_find_entry(old_dentry, &old_de2);
+ old_bh2 = ext3_find_entry(old_dir, &old_dentry->d_name,
+ &old_de2);
if (old_bh2) {
retval = ext3_delete_entry(handle, old_dir,
old_de2, old_bh2);
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 3a260af5544..5dec6d1356c 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -281,7 +281,8 @@ void ext3_abort (struct super_block * sb, const char * function,
EXT3_SB(sb)->s_mount_state |= EXT3_ERROR_FS;
sb->s_flags |= MS_RDONLY;
EXT3_SB(sb)->s_mount_opt |= EXT3_MOUNT_ABORT;
- journal_abort(EXT3_SB(sb)->s_journal, -EIO);
+ if (EXT3_SB(sb)->s_journal)
+ journal_abort(EXT3_SB(sb)->s_journal, -EIO);
}
void ext3_warning (struct super_block * sb, const char * function,
@@ -347,7 +348,7 @@ fail:
static int ext3_blkdev_put(struct block_device *bdev)
{
bd_release(bdev);
- return blkdev_put(bdev);
+ return blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
}
static int ext3_blkdev_remove(struct ext3_sb_info *sbi)
@@ -390,10 +391,14 @@ static void ext3_put_super (struct super_block * sb)
{
struct ext3_sb_info *sbi = EXT3_SB(sb);
struct ext3_super_block *es = sbi->s_es;
- int i;
+ int i, err;
ext3_xattr_put_super(sb);
- journal_destroy(sbi->s_journal);
+ err = journal_destroy(sbi->s_journal);
+ sbi->s_journal = NULL;
+ if (err < 0)
+ ext3_abort(sb, __func__, "Couldn't clean up the journal");
+
if (!(sb->s_flags & MS_RDONLY)) {
EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
es->s_state = cpu_to_le16(sbi->s_mount_state);
@@ -2066,7 +2071,7 @@ static journal_t *ext3_get_dev_journal(struct super_block *sb,
if (bd_claim(bdev, sb)) {
printk(KERN_ERR
"EXT3: failed to claim external journal device.\n");
- blkdev_put(bdev);
+ blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
return NULL;
}
@@ -2296,7 +2301,9 @@ static void ext3_mark_recovery_complete(struct super_block * sb,
journal_t *journal = EXT3_SB(sb)->s_journal;
journal_lock_updates(journal);
- journal_flush(journal);
+ if (journal_flush(journal) < 0)
+ goto out;
+
lock_super(sb);
if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER) &&
sb->s_flags & MS_RDONLY) {
@@ -2305,6 +2312,8 @@ static void ext3_mark_recovery_complete(struct super_block * sb,
ext3_commit_super(sb, es, 1);
}
unlock_super(sb);
+
+out:
journal_unlock_updates(journal);
}
@@ -2381,13 +2390,12 @@ static void ext3_write_super (struct super_block * sb)
static int ext3_sync_fs(struct super_block *sb, int wait)
{
- tid_t target;
-
sb->s_dirt = 0;
- if (journal_start_commit(EXT3_SB(sb)->s_journal, &target)) {
- if (wait)
- log_wait_commit(EXT3_SB(sb)->s_journal, target);
- }
+ if (wait)
+ ext3_force_commit(sb);
+ else
+ journal_start_commit(EXT3_SB(sb)->s_journal, NULL);
+
return 0;
}
@@ -2404,7 +2412,13 @@ static void ext3_write_super_lockfs(struct super_block *sb)
/* Now we set up the journal barrier. */
journal_lock_updates(journal);
- journal_flush(journal);
+
+ /*
+ * We don't want to clear needs_recovery flag when we failed
+ * to flush the journal.
+ */
+ if (journal_flush(journal) < 0)
+ return;
/* Journal blocked and flushed, clear needs_recovery flag. */
EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
@@ -2783,30 +2797,30 @@ static int ext3_quota_on_mount(struct super_block *sb, int type)
* Standard function to be called on quota_on
*/
static int ext3_quota_on(struct super_block *sb, int type, int format_id,
- char *path, int remount)
+ char *name, int remount)
{
int err;
- struct nameidata nd;
+ struct path path;
if (!test_opt(sb, QUOTA))
return -EINVAL;
- /* When remounting, no checks are needed and in fact, path is NULL */
+ /* When remounting, no checks are needed and in fact, name is NULL */
if (remount)
- return vfs_quota_on(sb, type, format_id, path, remount);
+ return vfs_quota_on(sb, type, format_id, name, remount);
- err = path_lookup(path, LOOKUP_FOLLOW, &nd);
+ err = kern_path(name, LOOKUP_FOLLOW, &path);
if (err)
return err;
/* Quotafile not on the same filesystem? */
- if (nd.path.mnt->mnt_sb != sb) {
- path_put(&nd.path);
+ if (path.mnt->mnt_sb != sb) {
+ path_put(&path);
return -EXDEV;
}
/* Journaling quota? */
if (EXT3_SB(sb)->s_qf_names[type]) {
/* Quotafile not of fs root? */
- if (nd.path.dentry->d_parent->d_inode != sb->s_root->d_inode)
+ if (path.dentry->d_parent != sb->s_root)
printk(KERN_WARNING
"EXT3-fs: Quota file not on filesystem root. "
"Journaled quota will not work.\n");
@@ -2816,18 +2830,22 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
* When we journal data on quota file, we have to flush journal to see
* all updates to the file when we bypass pagecache...
*/
- if (ext3_should_journal_data(nd.path.dentry->d_inode)) {
+ if (ext3_should_journal_data(path.dentry->d_inode)) {
/*
* We don't need to lock updates but journal_flush() could
* otherwise be livelocked...
*/
journal_lock_updates(EXT3_SB(sb)->s_journal);
- journal_flush(EXT3_SB(sb)->s_journal);
+ err = journal_flush(EXT3_SB(sb)->s_journal);
journal_unlock_updates(EXT3_SB(sb)->s_journal);
+ if (err) {
+ path_put(&path);
+ return err;
+ }
}
- err = vfs_quota_on_path(sb, type, format_id, &nd.path);
- path_put(&nd.path);
+ err = vfs_quota_on_path(sb, type, format_id, &path);
+ path_put(&path);
return err;
}