summaryrefslogtreecommitdiff
path: root/fs/ocfs2/namei.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ocfs2/namei.c')
-rw-r--r--fs/ocfs2/namei.c148
1 files changed, 96 insertions, 52 deletions
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 0673862c8bd..849c3b4bb94 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -56,6 +56,7 @@
#include "journal.h"
#include "namei.h"
#include "suballoc.h"
+#include "super.h"
#include "symlink.h"
#include "sysfile.h"
#include "uptodate.h"
@@ -178,7 +179,7 @@ static struct dentry *ocfs2_lookup(struct inode *dir, struct dentry *dentry,
if (status < 0)
goto bail_add;
- inode = ocfs2_iget(OCFS2_SB(dir->i_sb), blkno);
+ inode = ocfs2_iget(OCFS2_SB(dir->i_sb), blkno, 0);
if (IS_ERR(inode)) {
mlog(ML_ERROR, "Unable to create inode %llu\n",
(unsigned long long)blkno);
@@ -198,10 +199,32 @@ static struct dentry *ocfs2_lookup(struct inode *dir, struct dentry *dentry,
spin_unlock(&oi->ip_lock);
bail_add:
-
dentry->d_op = &ocfs2_dentry_ops;
ret = d_splice_alias(inode, dentry);
+ if (inode) {
+ /*
+ * If d_splice_alias() finds a DCACHE_DISCONNECTED
+ * dentry, it will d_move() it on top of ourse. The
+ * return value will indicate this however, so in
+ * those cases, we switch them around for the locking
+ * code.
+ *
+ * NOTE: This dentry already has ->d_op set from
+ * ocfs2_get_parent() and ocfs2_get_dentry()
+ */
+ if (ret)
+ dentry = ret;
+
+ status = ocfs2_dentry_attach_lock(dentry, inode,
+ OCFS2_I(dir)->ip_blkno);
+ if (status) {
+ mlog_errno(status);
+ ret = ERR_PTR(status);
+ goto bail_unlock;
+ }
+ }
+
bail_unlock:
/* Don't drop the cluster lock until *after* the d_add --
* unlink on another node will message us to remove that
@@ -310,13 +333,6 @@ static int ocfs2_mknod(struct inode *dir,
/* get our super block */
osb = OCFS2_SB(dir->i_sb);
- if (S_ISDIR(mode) && (dir->i_nlink >= OCFS2_LINK_MAX)) {
- mlog(ML_ERROR, "inode %llu has i_nlink of %u\n",
- (unsigned long long)OCFS2_I(dir)->ip_blkno, dir->i_nlink);
- status = -EMLINK;
- goto leave;
- }
-
handle = ocfs2_alloc_handle(osb);
if (handle == NULL) {
status = -ENOMEM;
@@ -331,6 +347,11 @@ static int ocfs2_mknod(struct inode *dir,
goto leave;
}
+ if (S_ISDIR(mode) && (dir->i_nlink >= OCFS2_LINK_MAX)) {
+ status = -EMLINK;
+ goto leave;
+ }
+
dirfe = (struct ocfs2_dinode *) parent_fe_bh->b_data;
if (!dirfe->i_links_count) {
/* can't make a file in a deleted directory. */
@@ -419,6 +440,13 @@ static int ocfs2_mknod(struct inode *dir,
goto leave;
}
+ status = ocfs2_dentry_attach_lock(dentry, inode,
+ OCFS2_I(dir)->ip_blkno);
+ if (status) {
+ mlog_errno(status);
+ goto leave;
+ }
+
insert_inode_hash(inode);
dentry->d_op = &ocfs2_dentry_ops;
d_instantiate(dentry, inode);
@@ -643,11 +671,6 @@ static int ocfs2_link(struct dentry *old_dentry,
goto bail;
}
- if (inode->i_nlink >= OCFS2_LINK_MAX) {
- err = -EMLINK;
- goto bail;
- }
-
handle = ocfs2_alloc_handle(osb);
if (handle == NULL) {
err = -ENOMEM;
@@ -661,6 +684,11 @@ static int ocfs2_link(struct dentry *old_dentry,
goto bail;
}
+ if (!dir->i_nlink) {
+ err = -ENOENT;
+ goto bail;
+ }
+
err = ocfs2_check_dir_for_entry(dir, dentry->d_name.name,
dentry->d_name.len);
if (err)
@@ -726,6 +754,12 @@ static int ocfs2_link(struct dentry *old_dentry,
goto bail;
}
+ err = ocfs2_dentry_attach_lock(dentry, inode, OCFS2_I(dir)->ip_blkno);
+ if (err) {
+ mlog_errno(err);
+ goto bail;
+ }
+
atomic_inc(&inode->i_count);
dentry->d_op = &ocfs2_dentry_ops;
d_instantiate(dentry, inode);
@@ -744,6 +778,23 @@ bail:
return err;
}
+/*
+ * Takes and drops an exclusive lock on the given dentry. This will
+ * force other nodes to drop it.
+ */
+static int ocfs2_remote_dentry_delete(struct dentry *dentry)
+{
+ int ret;
+
+ ret = ocfs2_dentry_lock(dentry, 1);
+ if (ret)
+ mlog_errno(ret);
+ else
+ ocfs2_dentry_unlock(dentry, 1);
+
+ return ret;
+}
+
static int ocfs2_unlink(struct inode *dir,
struct dentry *dentry)
{
@@ -833,8 +884,7 @@ static int ocfs2_unlink(struct inode *dir,
else
inode->i_nlink--;
- status = ocfs2_request_unlink_vote(inode, dentry,
- (unsigned int) inode->i_nlink);
+ status = ocfs2_remote_dentry_delete(dentry);
if (status < 0) {
/* This vote should succeed under all normal
* circumstances. */
@@ -1020,7 +1070,6 @@ static int ocfs2_rename(struct inode *old_dir,
struct buffer_head *old_inode_de_bh = NULL; // if old_dentry is a dir,
// this is the 1st dirent bh
nlink_t old_dir_nlink = old_dir->i_nlink, new_dir_nlink = new_dir->i_nlink;
- unsigned int links_count;
/* At some point it might be nice to break this function up a
* bit. */
@@ -1094,23 +1143,26 @@ static int ocfs2_rename(struct inode *old_dir,
}
}
- if (S_ISDIR(old_inode->i_mode)) {
- /* Directories actually require metadata updates to
- * the directory info so we can't get away with not
- * doing node locking on it. */
- status = ocfs2_meta_lock(old_inode, handle, NULL, 1);
- if (status < 0) {
- if (status != -ENOENT)
- mlog_errno(status);
- goto bail;
- }
-
- status = ocfs2_request_rename_vote(old_inode, old_dentry);
- if (status < 0) {
+ /*
+ * Though we don't require an inode meta data update if
+ * old_inode is not a directory, we lock anyway here to ensure
+ * the vote thread on other nodes won't have to concurrently
+ * downconvert the inode and the dentry locks.
+ */
+ status = ocfs2_meta_lock(old_inode, handle, NULL, 1);
+ if (status < 0) {
+ if (status != -ENOENT)
mlog_errno(status);
- goto bail;
- }
+ goto bail;
+ }
+
+ status = ocfs2_remote_dentry_delete(old_dentry);
+ if (status < 0) {
+ mlog_errno(status);
+ goto bail;
+ }
+ if (S_ISDIR(old_inode->i_mode)) {
status = -EIO;
old_inode_de_bh = ocfs2_bread(old_inode, 0, &status, 0);
if (!old_inode_de_bh)
@@ -1124,14 +1176,6 @@ static int ocfs2_rename(struct inode *old_dir,
if (!new_inode && new_dir!=old_dir &&
new_dir->i_nlink >= OCFS2_LINK_MAX)
goto bail;
- } else {
- /* Ah, the simple case - we're a file so just send a
- * message. */
- status = ocfs2_request_rename_vote(old_inode, old_dentry);
- if (status < 0) {
- mlog_errno(status);
- goto bail;
- }
}
status = -ENOENT;
@@ -1203,13 +1247,7 @@ static int ocfs2_rename(struct inode *old_dir,
goto bail;
}
- if (S_ISDIR(new_inode->i_mode))
- links_count = 0;
- else
- links_count = (unsigned int) (new_inode->i_nlink - 1);
-
- status = ocfs2_request_unlink_vote(new_inode, new_dentry,
- links_count);
+ status = ocfs2_remote_dentry_delete(new_dentry);
if (status < 0) {
mlog_errno(status);
goto bail;
@@ -1388,6 +1426,7 @@ static int ocfs2_rename(struct inode *old_dir,
}
}
+ ocfs2_dentry_move(old_dentry, new_dentry, old_dir, new_dir);
status = 0;
bail:
if (rename_lock)
@@ -1676,6 +1715,12 @@ static int ocfs2_symlink(struct inode *dir,
goto bail;
}
+ status = ocfs2_dentry_attach_lock(dentry, inode, OCFS2_I(dir)->ip_blkno);
+ if (status) {
+ mlog_errno(status);
+ goto bail;
+ }
+
insert_inode_hash(inode);
dentry->d_op = &ocfs2_dentry_ops;
d_instantiate(dentry, inode);
@@ -1964,13 +2009,8 @@ restart:
}
num++;
- /* XXX: questionable readahead stuff here */
bh = ocfs2_bread(dir, b++, &err, 1);
bh_use[ra_max] = bh;
-#if 0 // ???
- if (bh)
- ll_rw_block(READ, 1, &bh);
-#endif
}
}
if ((bh = bh_use[ra_ptr++]) == NULL)
@@ -1978,6 +2018,10 @@ restart:
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
/* read error, skip block & hope for the best */
+ ocfs2_error(dir->i_sb, "reading directory %llu, "
+ "offset %lu\n",
+ (unsigned long long)OCFS2_I(dir)->ip_blkno,
+ block);
brelse(bh);
goto next;
}