diff options
Diffstat (limited to 'fs/btrfs/inode.c')
| -rw-r--r-- | fs/btrfs/inode.c | 46 | 
1 files changed, 36 insertions, 10 deletions
| diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2f5975954ccf..08dfc57e2270 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3435,10 +3435,10 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)  		found_key.offset = 0;  		inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL);  		ret = PTR_ERR_OR_ZERO(inode); -		if (ret && ret != -ESTALE) +		if (ret && ret != -ENOENT)  			goto out; -		if (ret == -ESTALE && root == root->fs_info->tree_root) { +		if (ret == -ENOENT && root == root->fs_info->tree_root) {  			struct btrfs_root *dead_root;  			struct btrfs_fs_info *fs_info = root->fs_info;  			int is_dead_root = 0; @@ -3474,7 +3474,7 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)  		 * Inode is already gone but the orphan item is still there,  		 * kill the orphan item.  		 */ -		if (ret == -ESTALE) { +		if (ret == -ENOENT) {  			trans = btrfs_start_transaction(root, 1);  			if (IS_ERR(trans)) {  				ret = PTR_ERR(trans); @@ -3633,7 +3633,7 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,  /*   * read an inode from the btree into the in-memory inode   */ -static void btrfs_read_locked_inode(struct inode *inode) +static int btrfs_read_locked_inode(struct inode *inode)  {  	struct btrfs_path *path;  	struct extent_buffer *leaf; @@ -3652,14 +3652,19 @@ static void btrfs_read_locked_inode(struct inode *inode)  		filled = true;  	path = btrfs_alloc_path(); -	if (!path) +	if (!path) { +		ret = -ENOMEM;  		goto make_bad; +	}  	memcpy(&location, &BTRFS_I(inode)->location, sizeof(location));  	ret = btrfs_lookup_inode(NULL, root, path, &location, 0); -	if (ret) +	if (ret) { +		if (ret > 0) +			ret = -ENOENT;  		goto make_bad; +	}  	leaf = path->nodes[0]; @@ -3812,11 +3817,12 @@ cache_acl:  	}  	btrfs_update_iflags(inode); -	return; +	return 0;  make_bad:  	btrfs_free_path(path);  	make_bad_inode(inode); +	return ret;  }  /* @@ -4204,6 +4210,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)  	int err = 0;  	struct btrfs_root *root = BTRFS_I(dir)->root;  	struct btrfs_trans_handle *trans; +	u64 last_unlink_trans;  	if (inode->i_size > BTRFS_EMPTY_DIR_SIZE)  		return -ENOTEMPTY; @@ -4226,11 +4233,27 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)  	if (err)  		goto out; +	last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; +  	/* now the directory is empty */  	err = btrfs_unlink_inode(trans, root, dir, d_inode(dentry),  				 dentry->d_name.name, dentry->d_name.len); -	if (!err) +	if (!err) {  		btrfs_i_size_write(inode, 0); +		/* +		 * Propagate the last_unlink_trans value of the deleted dir to +		 * its parent directory. This is to prevent an unrecoverable +		 * log tree in the case we do something like this: +		 * 1) create dir foo +		 * 2) create snapshot under dir foo +		 * 3) delete the snapshot +		 * 4) rmdir foo +		 * 5) mkdir foo +		 * 6) fsync foo or some file inside foo +		 */ +		if (last_unlink_trans >= trans->transid) +			BTRFS_I(dir)->last_unlink_trans = last_unlink_trans; +	}  out:  	btrfs_end_transaction(trans, root);  	btrfs_btree_balance_dirty(root); @@ -5606,7 +5629,9 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,  		return ERR_PTR(-ENOMEM);  	if (inode->i_state & I_NEW) { -		btrfs_read_locked_inode(inode); +		int ret; + +		ret = btrfs_read_locked_inode(inode);  		if (!is_bad_inode(inode)) {  			inode_tree_add(inode);  			unlock_new_inode(inode); @@ -5615,7 +5640,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,  		} else {  			unlock_new_inode(inode);  			iput(inode); -			inode = ERR_PTR(-ESTALE); +			ASSERT(ret < 0); +			inode = ERR_PTR(ret < 0 ? ret : -ESTALE);  		}  	} | 
