Commit 53dc20b9 authored by Xue jiufei's avatar Xue jiufei Committed by Linus Torvalds

ocfs2: fix the wrong directory passed to ocfs2_lookup_ino_from_name() when link file

In ocfs2_link(), the parent directory inode passed to function
ocfs2_lookup_ino_from_name() is wrong.  Parameter dir is the parent of
new_dentry not old_dentry.  We should get old_dir from old_dentry and
lookup old_dentry in old_dir in case another node remove the old dentry.

With this change, hard linking works again, when paths are relative with
at least one subdirectory.  This is how the problem was reproducable:

  # mkdir a
  # mkdir b
  # touch a/test
  # ln a/test b/test
  ln: failed to create hard link `b/test' => `a/test': No such file or  directory

However when creating links in the same dir, it worked well.

Now the link gets created.

Fixes: 0e048316 ("ocfs2: check existence of old dentry in ocfs2_link()")
Signed-off-by: default avatarjoyce.xue <xuejiufei@huawei.com>
Reported-by: default avatarSzabo Aron - UBIT <aron@ubit.hu>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Joel Becker <jlbec@evilplan.org>
Tested-by: default avatarAron Szabo <aron@ubit.hu>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 75dd112a
...@@ -94,6 +94,14 @@ static int ocfs2_create_symlink_data(struct ocfs2_super *osb, ...@@ -94,6 +94,14 @@ static int ocfs2_create_symlink_data(struct ocfs2_super *osb,
struct inode *inode, struct inode *inode,
const char *symname); const char *symname);
static int ocfs2_double_lock(struct ocfs2_super *osb,
struct buffer_head **bh1,
struct inode *inode1,
struct buffer_head **bh2,
struct inode *inode2,
int rename);
static void ocfs2_double_unlock(struct inode *inode1, struct inode *inode2);
/* An orphan dir name is an 8 byte value, printed as a hex string */ /* An orphan dir name is an 8 byte value, printed as a hex string */
#define OCFS2_ORPHAN_NAMELEN ((int)(2 * sizeof(u64))) #define OCFS2_ORPHAN_NAMELEN ((int)(2 * sizeof(u64)))
...@@ -678,8 +686,10 @@ static int ocfs2_link(struct dentry *old_dentry, ...@@ -678,8 +686,10 @@ static int ocfs2_link(struct dentry *old_dentry,
{ {
handle_t *handle; handle_t *handle;
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
struct inode *old_dir = old_dentry->d_parent->d_inode;
int err; int err;
struct buffer_head *fe_bh = NULL; struct buffer_head *fe_bh = NULL;
struct buffer_head *old_dir_bh = NULL;
struct buffer_head *parent_fe_bh = NULL; struct buffer_head *parent_fe_bh = NULL;
struct ocfs2_dinode *fe = NULL; struct ocfs2_dinode *fe = NULL;
struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);
...@@ -696,19 +706,33 @@ static int ocfs2_link(struct dentry *old_dentry, ...@@ -696,19 +706,33 @@ static int ocfs2_link(struct dentry *old_dentry,
dquot_initialize(dir); dquot_initialize(dir);
err = ocfs2_inode_lock_nested(dir, &parent_fe_bh, 1, OI_LS_PARENT); err = ocfs2_double_lock(osb, &old_dir_bh, old_dir,
&parent_fe_bh, dir, 0);
if (err < 0) { if (err < 0) {
if (err != -ENOENT) if (err != -ENOENT)
mlog_errno(err); mlog_errno(err);
return err; return err;
} }
/* make sure both dirs have bhs
* get an extra ref on old_dir_bh if old==new */
if (!parent_fe_bh) {
if (old_dir_bh) {
parent_fe_bh = old_dir_bh;
get_bh(parent_fe_bh);
} else {
mlog(ML_ERROR, "%s: no old_dir_bh!\n", osb->uuid_str);
err = -EIO;
goto out;
}
}
if (!dir->i_nlink) { if (!dir->i_nlink) {
err = -ENOENT; err = -ENOENT;
goto out; goto out;
} }
err = ocfs2_lookup_ino_from_name(dir, old_dentry->d_name.name, err = ocfs2_lookup_ino_from_name(old_dir, old_dentry->d_name.name,
old_dentry->d_name.len, &old_de_ino); old_dentry->d_name.len, &old_de_ino);
if (err) { if (err) {
err = -ENOENT; err = -ENOENT;
...@@ -801,10 +825,11 @@ static int ocfs2_link(struct dentry *old_dentry, ...@@ -801,10 +825,11 @@ static int ocfs2_link(struct dentry *old_dentry,
ocfs2_inode_unlock(inode, 1); ocfs2_inode_unlock(inode, 1);
out: out:
ocfs2_inode_unlock(dir, 1); ocfs2_double_unlock(old_dir, dir);
brelse(fe_bh); brelse(fe_bh);
brelse(parent_fe_bh); brelse(parent_fe_bh);
brelse(old_dir_bh);
ocfs2_free_dir_lookup_result(&lookup); ocfs2_free_dir_lookup_result(&lookup);
...@@ -1072,14 +1097,15 @@ static int ocfs2_check_if_ancestor(struct ocfs2_super *osb, ...@@ -1072,14 +1097,15 @@ static int ocfs2_check_if_ancestor(struct ocfs2_super *osb,
} }
/* /*
* The only place this should be used is rename! * The only place this should be used is rename and link!
* if they have the same id, then the 1st one is the only one locked. * if they have the same id, then the 1st one is the only one locked.
*/ */
static int ocfs2_double_lock(struct ocfs2_super *osb, static int ocfs2_double_lock(struct ocfs2_super *osb,
struct buffer_head **bh1, struct buffer_head **bh1,
struct inode *inode1, struct inode *inode1,
struct buffer_head **bh2, struct buffer_head **bh2,
struct inode *inode2) struct inode *inode2,
int rename)
{ {
int status; int status;
int inode1_is_ancestor, inode2_is_ancestor; int inode1_is_ancestor, inode2_is_ancestor;
...@@ -1127,7 +1153,7 @@ static int ocfs2_double_lock(struct ocfs2_super *osb, ...@@ -1127,7 +1153,7 @@ static int ocfs2_double_lock(struct ocfs2_super *osb,
} }
/* lock id2 */ /* lock id2 */
status = ocfs2_inode_lock_nested(inode2, bh2, 1, status = ocfs2_inode_lock_nested(inode2, bh2, 1,
OI_LS_RENAME1); rename == 1 ? OI_LS_RENAME1 : OI_LS_PARENT);
if (status < 0) { if (status < 0) {
if (status != -ENOENT) if (status != -ENOENT)
mlog_errno(status); mlog_errno(status);
...@@ -1136,7 +1162,8 @@ static int ocfs2_double_lock(struct ocfs2_super *osb, ...@@ -1136,7 +1162,8 @@ static int ocfs2_double_lock(struct ocfs2_super *osb,
} }
/* lock id1 */ /* lock id1 */
status = ocfs2_inode_lock_nested(inode1, bh1, 1, OI_LS_RENAME2); status = ocfs2_inode_lock_nested(inode1, bh1, 1,
rename == 1 ? OI_LS_RENAME2 : OI_LS_PARENT);
if (status < 0) { if (status < 0) {
/* /*
* An error return must mean that no cluster locks * An error return must mean that no cluster locks
...@@ -1252,7 +1279,7 @@ static int ocfs2_rename(struct inode *old_dir, ...@@ -1252,7 +1279,7 @@ static int ocfs2_rename(struct inode *old_dir,
/* if old and new are the same, this'll just do one lock. */ /* if old and new are the same, this'll just do one lock. */
status = ocfs2_double_lock(osb, &old_dir_bh, old_dir, status = ocfs2_double_lock(osb, &old_dir_bh, old_dir,
&new_dir_bh, new_dir); &new_dir_bh, new_dir, 1);
if (status < 0) { if (status < 0) {
mlog_errno(status); mlog_errno(status);
goto bail; goto bail;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment