diff options
Diffstat (limited to 'sys-kernel/hardened-sources-3.14/ocfs2-hardlink.patch')
-rw-r--r-- | sys-kernel/hardened-sources-3.14/ocfs2-hardlink.patch | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/sys-kernel/hardened-sources-3.14/ocfs2-hardlink.patch b/sys-kernel/hardened-sources-3.14/ocfs2-hardlink.patch new file mode 100644 index 0000000..fe8f841 --- /dev/null +++ b/sys-kernel/hardened-sources-3.14/ocfs2-hardlink.patch @@ -0,0 +1,125 @@ +diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c +index 8add6f1030d7..a997d7f40608 100644 +--- a/fs/ocfs2/namei.c ++++ b/fs/ocfs2/namei.c +@@ -94,6 +94,14 @@ static int ocfs2_create_symlink_data(struct ocfs2_super *osb, + struct inode *inode, + 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 */ + #define OCFS2_ORPHAN_NAMELEN ((int)(2 * sizeof(u64))) + +@@ -678,8 +686,10 @@ static int ocfs2_link(struct dentry *old_dentry, + { + handle_t *handle; + struct inode *inode = old_dentry->d_inode; ++ struct inode *old_dir = old_dentry->d_parent->d_inode; + int err; + struct buffer_head *fe_bh = NULL; ++ struct buffer_head *old_dir_bh = NULL; + struct buffer_head *parent_fe_bh = NULL; + struct ocfs2_dinode *fe = NULL; + struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); +@@ -696,19 +706,33 @@ static int ocfs2_link(struct dentry *old_dentry, + + 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 != -ENOENT) + mlog_errno(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) { + err = -ENOENT; + 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); + if (err) { + err = -ENOENT; +@@ -801,10 +825,11 @@ out_unlock_inode: + ocfs2_inode_unlock(inode, 1); + + out: +- ocfs2_inode_unlock(dir, 1); ++ ocfs2_double_unlock(old_dir, dir); + + brelse(fe_bh); + brelse(parent_fe_bh); ++ brelse(old_dir_bh); + + ocfs2_free_dir_lookup_result(&lookup); + +@@ -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. + */ + static int ocfs2_double_lock(struct ocfs2_super *osb, + struct buffer_head **bh1, + struct inode *inode1, + struct buffer_head **bh2, +- struct inode *inode2) ++ struct inode *inode2, ++ int rename) + { + int status; + int inode1_is_ancestor, inode2_is_ancestor; +@@ -1127,7 +1153,7 @@ static int ocfs2_double_lock(struct ocfs2_super *osb, + } + /* lock id2 */ + status = ocfs2_inode_lock_nested(inode2, bh2, 1, +- OI_LS_RENAME1); ++ rename == 1 ? OI_LS_RENAME1 : OI_LS_PARENT); + if (status < 0) { + if (status != -ENOENT) + mlog_errno(status); +@@ -1136,7 +1162,8 @@ static int ocfs2_double_lock(struct ocfs2_super *osb, + } + + /* 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) { + /* + * An error return must mean that no cluster locks +@@ -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. */ + status = ocfs2_double_lock(osb, &old_dir_bh, old_dir, +- &new_dir_bh, new_dir); ++ &new_dir_bh, new_dir, 1); + if (status < 0) { + mlog_errno(status); + goto bail; |