diff options
Diffstat (limited to 'fs/ocfs2/namei.c')
| -rw-r--r-- | fs/ocfs2/namei.c | 199 | 
1 files changed, 178 insertions, 21 deletions
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index be3f8676a43..8add6f1030d 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -205,6 +205,21 @@ static struct inode *ocfs2_get_init_inode(struct inode *dir, umode_t mode)  	return inode;  } +static void ocfs2_cleanup_add_entry_failure(struct ocfs2_super *osb, +		struct dentry *dentry, struct inode *inode) +{ +	struct ocfs2_dentry_lock *dl = dentry->d_fsdata; + +	ocfs2_simple_drop_lockres(osb, &dl->dl_lockres); +	ocfs2_lock_res_free(&dl->dl_lockres); +	BUG_ON(dl->dl_count != 1); +	spin_lock(&dentry_attach_lock); +	dentry->d_fsdata = NULL; +	spin_unlock(&dentry_attach_lock); +	kfree(dl); +	iput(inode); +} +  static int ocfs2_mknod(struct inode *dir,  		       struct dentry *dentry,  		       umode_t mode, @@ -230,6 +245,8 @@ static int ocfs2_mknod(struct inode *dir,  	struct ocfs2_dir_lookup_result lookup = { NULL, };  	sigset_t oldset;  	int did_block_signals = 0; +	struct posix_acl *default_acl = NULL, *acl = NULL; +	struct ocfs2_dentry_lock *dl = NULL;  	trace_ocfs2_mknod(dir, dentry, dentry->d_name.len, dentry->d_name.name,  			  (unsigned long long)OCFS2_I(dir)->ip_blkno, @@ -331,6 +348,12 @@ static int ocfs2_mknod(struct inode *dir,  		goto leave;  	} +	status = posix_acl_create(dir, &mode, &default_acl, &acl); +	if (status) { +		mlog_errno(status); +		goto leave; +	} +  	handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(osb->sb,  							    S_ISDIR(mode),  							    xattr_credits)); @@ -379,8 +402,17 @@ static int ocfs2_mknod(struct inode *dir,  		inc_nlink(dir);  	} -	status = ocfs2_init_acl(handle, inode, dir, new_fe_bh, parent_fe_bh, -				meta_ac, data_ac); +	if (default_acl) { +		status = ocfs2_set_acl(handle, inode, new_fe_bh, +				       ACL_TYPE_DEFAULT, default_acl, +				       meta_ac, data_ac); +	} +	if (!status && acl) { +		status = ocfs2_set_acl(handle, inode, new_fe_bh, +				       ACL_TYPE_ACCESS, acl, +				       meta_ac, data_ac); +	} +  	if (status < 0) {  		mlog_errno(status);  		goto leave; @@ -407,6 +439,8 @@ static int ocfs2_mknod(struct inode *dir,  		goto leave;  	} +	dl = dentry->d_fsdata; +  	status = ocfs2_add_entry(handle, dentry, inode,  				 OCFS2_I(inode)->ip_blkno, parent_fe_bh,  				 &lookup); @@ -419,6 +453,10 @@ static int ocfs2_mknod(struct inode *dir,  	d_instantiate(dentry, inode);  	status = 0;  leave: +	if (default_acl) +		posix_acl_release(default_acl); +	if (acl) +		posix_acl_release(acl);  	if (status < 0 && did_quota_inode)  		dquot_free_inode(inode);  	if (handle) @@ -430,7 +468,6 @@ leave:  	brelse(new_fe_bh);  	brelse(parent_fe_bh); -	kfree(si.name);  	kfree(si.value);  	ocfs2_free_dir_lookup_result(&lookup); @@ -450,6 +487,9 @@ leave:  	 * ocfs2_delete_inode will mutex_lock again.  	 */  	if ((status < 0) && inode) { +		if (dl) +			ocfs2_cleanup_add_entry_failure(osb, dentry, inode); +  		OCFS2_I(inode)->ip_flags |= OCFS2_INODE_SKIP_ORPHAN_DIR;  		clear_nlink(inode);  		iput(inode); @@ -475,6 +515,7 @@ static int __ocfs2_mknod_locked(struct inode *dir,  	struct ocfs2_dinode *fe = NULL;  	struct ocfs2_extent_list *fel;  	u16 feat; +	struct ocfs2_inode_info *oi = OCFS2_I(inode);  	*new_fe_bh = NULL; @@ -489,7 +530,7 @@ static int __ocfs2_mknod_locked(struct inode *dir,  	*new_fe_bh = sb_getblk(osb->sb, fe_blkno);  	if (!*new_fe_bh) { -		status = -EIO; +		status = -ENOMEM;  		mlog_errno(status);  		goto leave;  	} @@ -556,8 +597,8 @@ static int __ocfs2_mknod_locked(struct inode *dir,  			mlog_errno(status);  	} -	status = 0; /* error in ocfs2_create_new_inode_locks is not -		     * critical */ +	oi->i_sync_tid = handle->h_transaction->t_tid; +	oi->i_datasync_tid = handle->h_transaction->t_tid;  leave:  	if (status < 0) { @@ -644,6 +685,7 @@ static int ocfs2_link(struct dentry *old_dentry,  	struct ocfs2_super *osb = OCFS2_SB(dir->i_sb);  	struct ocfs2_dir_lookup_result lookup = { NULL, };  	sigset_t oldset; +	u64 old_de_ino;  	trace_ocfs2_link((unsigned long long)OCFS2_I(inode)->ip_blkno,  			 old_dentry->d_name.len, old_dentry->d_name.name, @@ -666,6 +708,22 @@ static int ocfs2_link(struct dentry *old_dentry,  		goto out;  	} +	err = ocfs2_lookup_ino_from_name(dir, old_dentry->d_name.name, +			old_dentry->d_name.len, &old_de_ino); +	if (err) { +		err = -ENOENT; +		goto out; +	} + +	/* +	 * Check whether another node removed the source inode while we +	 * were in the vfs. +	 */ +	if (old_de_ino != OCFS2_I(inode)->ip_blkno) { +		err = -ENOENT; +		goto out; +	} +  	err = ocfs2_check_dir_for_entry(dir, dentry->d_name.name,  					dentry->d_name.len);  	if (err) @@ -948,12 +1006,71 @@ leave:  	ocfs2_free_dir_lookup_result(&orphan_insert);  	ocfs2_free_dir_lookup_result(&lookup); -	if (status && (status != -ENOTEMPTY)) +	if (status && (status != -ENOTEMPTY) && (status != -ENOENT))  		mlog_errno(status);  	return status;  } +static int ocfs2_check_if_ancestor(struct ocfs2_super *osb, +		u64 src_inode_no, u64 dest_inode_no) +{ +	int ret = 0, i = 0; +	u64 parent_inode_no = 0; +	u64 child_inode_no = src_inode_no; +	struct inode *child_inode; + +#define MAX_LOOKUP_TIMES 32 +	while (1) { +		child_inode = ocfs2_iget(osb, child_inode_no, 0, 0); +		if (IS_ERR(child_inode)) { +			ret = PTR_ERR(child_inode); +			break; +		} + +		ret = ocfs2_inode_lock(child_inode, NULL, 0); +		if (ret < 0) { +			iput(child_inode); +			if (ret != -ENOENT) +				mlog_errno(ret); +			break; +		} + +		ret = ocfs2_lookup_ino_from_name(child_inode, "..", 2, +				&parent_inode_no); +		ocfs2_inode_unlock(child_inode, 0); +		iput(child_inode); +		if (ret < 0) { +			ret = -ENOENT; +			break; +		} + +		if (parent_inode_no == dest_inode_no) { +			ret = 1; +			break; +		} + +		if (parent_inode_no == osb->root_inode->i_ino) { +			ret = 0; +			break; +		} + +		child_inode_no = parent_inode_no; + +		if (++i >= MAX_LOOKUP_TIMES) { +			mlog(ML_NOTICE, "max lookup times reached, filesystem " +					"may have nested directories, " +					"src inode: %llu, dest inode: %llu.\n", +					(unsigned long long)src_inode_no, +					(unsigned long long)dest_inode_no); +			ret = 0; +			break; +		} +	} + +	return ret; +} +  /*   * The only place this should be used is rename!   * if they have the same id, then the 1st one is the only one locked. @@ -965,6 +1082,7 @@ static int ocfs2_double_lock(struct ocfs2_super *osb,  			     struct inode *inode2)  {  	int status; +	int inode1_is_ancestor, inode2_is_ancestor;  	struct ocfs2_inode_info *oi1 = OCFS2_I(inode1);  	struct ocfs2_inode_info *oi2 = OCFS2_I(inode2);  	struct buffer_head **tmpbh; @@ -978,9 +1096,26 @@ static int ocfs2_double_lock(struct ocfs2_super *osb,  	if (*bh2)  		*bh2 = NULL; -	/* we always want to lock the one with the lower lockid first. */ +	/* we always want to lock the one with the lower lockid first. +	 * and if they are nested, we lock ancestor first */  	if (oi1->ip_blkno != oi2->ip_blkno) { -		if (oi1->ip_blkno < oi2->ip_blkno) { +		inode1_is_ancestor = ocfs2_check_if_ancestor(osb, oi2->ip_blkno, +				oi1->ip_blkno); +		if (inode1_is_ancestor < 0) { +			status = inode1_is_ancestor; +			goto bail; +		} + +		inode2_is_ancestor = ocfs2_check_if_ancestor(osb, oi1->ip_blkno, +				oi2->ip_blkno); +		if (inode2_is_ancestor < 0) { +			status = inode2_is_ancestor; +			goto bail; +		} + +		if ((inode1_is_ancestor == 1) || +				(oi1->ip_blkno < oi2->ip_blkno && +				inode2_is_ancestor == 0)) {  			/* switch id1 and id2 around */  			tmpbh = bh2;  			bh2 = bh1; @@ -1061,6 +1196,7 @@ static int ocfs2_rename(struct inode *old_dir,  	struct ocfs2_dir_lookup_result old_entry_lookup = { NULL, };  	struct ocfs2_dir_lookup_result orphan_insert = { NULL, };  	struct ocfs2_dir_lookup_result target_insert = { NULL, }; +	bool should_add_orphan = false;  	/* At some point it might be nice to break this function up a  	 * bit. */ @@ -1097,6 +1233,21 @@ static int ocfs2_rename(struct inode *old_dir,  			goto bail;  		}  		rename_lock = 1; + +		/* here we cannot guarantee the inodes haven't just been +		 * changed, so check if they are nested again */ +		status = ocfs2_check_if_ancestor(osb, new_dir->i_ino, +				old_inode->i_ino); +		if (status < 0) { +			mlog_errno(status); +			goto bail; +		} else if (status == 1) { +			status = -EPERM; +			trace_ocfs2_rename_not_permitted( +					(unsigned long long)old_inode->i_ino, +					(unsigned long long)new_dir->i_ino); +			goto bail; +		}  	}  	/* if old and new are the same, this'll just do one lock. */ @@ -1267,6 +1418,7 @@ static int ocfs2_rename(struct inode *old_dir,  				mlog_errno(status);  				goto bail;  			} +			should_add_orphan = true;  		}  	} else {  		BUG_ON(new_dentry->d_parent->d_inode != new_dir); @@ -1311,17 +1463,6 @@ static int ocfs2_rename(struct inode *old_dir,  			goto bail;  		} -		if (S_ISDIR(new_inode->i_mode) || -		    (ocfs2_read_links_count(newfe) == 1)) { -			status = ocfs2_orphan_add(osb, handle, new_inode, -						  newfe_bh, orphan_name, -						  &orphan_insert, orphan_dir); -			if (status < 0) { -				mlog_errno(status); -				goto bail; -			} -		} -  		/* change the dirent to point to the correct inode */  		status = ocfs2_update_entry(new_dir, handle, &target_lookup_res,  					    old_inode); @@ -1336,6 +1477,15 @@ static int ocfs2_rename(struct inode *old_dir,  		else  			ocfs2_add_links_count(newfe, -1);  		ocfs2_journal_dirty(handle, newfe_bh); +		if (should_add_orphan) { +			status = ocfs2_orphan_add(osb, handle, new_inode, +					newfe_bh, orphan_name, +					&orphan_insert, orphan_dir); +			if (status < 0) { +				mlog_errno(status); +				goto bail; +			} +		}  	} else {  		/* if the name was not found in new_dir, add it now */  		status = ocfs2_add_entry(handle, new_dentry, old_inode, @@ -1605,6 +1755,7 @@ static int ocfs2_symlink(struct inode *dir,  	struct ocfs2_dir_lookup_result lookup = { NULL, };  	sigset_t oldset;  	int did_block_signals = 0; +	struct ocfs2_dentry_lock *dl = NULL;  	trace_ocfs2_symlink_begin(dir, dentry, symname,  				  dentry->d_name.len, dentry->d_name.name); @@ -1793,6 +1944,8 @@ static int ocfs2_symlink(struct inode *dir,  		goto bail;  	} +	dl = dentry->d_fsdata; +  	status = ocfs2_add_entry(handle, dentry, inode,  				 le64_to_cpu(fe->i_blkno), parent_fe_bh,  				 &lookup); @@ -1818,7 +1971,6 @@ bail:  	brelse(new_fe_bh);  	brelse(parent_fe_bh); -	kfree(si.name);  	kfree(si.value);  	ocfs2_free_dir_lookup_result(&lookup);  	if (inode_ac) @@ -1828,6 +1980,9 @@ bail:  	if (xattr_ac)  		ocfs2_free_alloc_context(xattr_ac);  	if ((status < 0) && inode) { +		if (dl) +			ocfs2_cleanup_add_entry_failure(osb, dentry, inode); +  		OCFS2_I(inode)->ip_flags |= OCFS2_INODE_SKIP_ORPHAN_DIR;  		clear_nlink(inode);  		iput(inode); @@ -2444,6 +2599,7 @@ int ocfs2_mv_orphaned_inode_to_new(struct inode *dir,  	di->i_orphaned_slot = 0;  	set_nlink(inode, 1);  	ocfs2_set_links_count(di, inode->i_nlink); +	ocfs2_update_inode_fsync_trans(handle, inode, 1);  	ocfs2_journal_dirty(handle, di_bh);  	status = ocfs2_add_entry(handle, dentry, inode, @@ -2504,4 +2660,5 @@ const struct inode_operations ocfs2_dir_iops = {  	.removexattr	= generic_removexattr,  	.fiemap         = ocfs2_fiemap,  	.get_acl	= ocfs2_iop_get_acl, +	.set_acl	= ocfs2_iop_set_acl,  };  | 
