diff options
Diffstat (limited to 'fs/btrfs')
| -rw-r--r-- | fs/btrfs/backref.c | 28 | ||||
| -rw-r--r-- | fs/btrfs/backref.h | 4 | ||||
| -rw-r--r-- | fs/btrfs/ctree.c | 70 | ||||
| -rw-r--r-- | fs/btrfs/ctree.h | 3 | ||||
| -rw-r--r-- | fs/btrfs/extent_io.c | 4 | ||||
| -rw-r--r-- | fs/btrfs/inode.c | 7 | ||||
| -rw-r--r-- | fs/btrfs/ioctl.c | 6 | ||||
| -rw-r--r-- | fs/btrfs/qgroup.c | 17 | ||||
| -rw-r--r-- | fs/btrfs/send.c | 156 | ||||
| -rw-r--r-- | fs/btrfs/transaction.c | 2 | ||||
| -rw-r--r-- | fs/btrfs/volumes.c | 7 | 
11 files changed, 199 insertions, 105 deletions
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index f3187938e08..208d8aa5b07 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -283,9 +283,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,  		goto out;  	} -	rcu_read_lock(); -	root_level = btrfs_header_level(root->node); -	rcu_read_unlock(); +	root_level = btrfs_old_root_level(root, time_seq);  	if (root_level + 1 == level)  		goto out; @@ -1177,16 +1175,15 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,  	return ret;  } -static char *ref_to_path(struct btrfs_root *fs_root, -			 struct btrfs_path *path, -			 u32 name_len, unsigned long name_off, -			 struct extent_buffer *eb_in, u64 parent, -			 char *dest, u32 size) +char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, +			u32 name_len, unsigned long name_off, +			struct extent_buffer *eb_in, u64 parent, +			char *dest, u32 size)  {  	int slot;  	u64 next_inum;  	int ret; -	s64 bytes_left = size - 1; +	s64 bytes_left = ((s64)size) - 1;  	struct extent_buffer *eb = eb_in;  	struct btrfs_key found_key;  	int leave_spinning = path->leave_spinning; @@ -1266,10 +1263,10 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root,  			 struct extent_buffer *eb_in, u64 parent,  			 char *dest, u32 size)  { -	return ref_to_path(fs_root, path, -			   btrfs_inode_ref_name_len(eb_in, iref), -			   (unsigned long)(iref + 1), -			   eb_in, parent, dest, size); +	return btrfs_ref_to_path(fs_root, path, +				 btrfs_inode_ref_name_len(eb_in, iref), +				 (unsigned long)(iref + 1), +				 eb_in, parent, dest, size);  }  /* @@ -1715,9 +1712,8 @@ static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,  					ipath->fspath->bytes_left - s_ptr : 0;  	fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr; -	fspath = ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len, -			     name_off, eb, inum, fspath_min, -			     bytes_left); +	fspath = btrfs_ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len, +				   name_off, eb, inum, fspath_min, bytes_left);  	if (IS_ERR(fspath))  		return PTR_ERR(fspath); diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h index e75533043a5..d61feca7945 100644 --- a/fs/btrfs/backref.h +++ b/fs/btrfs/backref.h @@ -62,6 +62,10 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,  char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,  			 struct btrfs_inode_ref *iref, struct extent_buffer *eb,  			 u64 parent, char *dest, u32 size); +char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, +			u32 name_len, unsigned long name_off, +			struct extent_buffer *eb_in, u64 parent, +			char *dest, u32 size);  struct btrfs_data_container *init_data_container(u32 total_bytes);  struct inode_fs_paths *init_ipath(s32 total_bytes, struct btrfs_root *fs_root, diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index b3343621100..cdfb4c49a80 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -596,6 +596,11 @@ tree_mod_log_insert_move(struct btrfs_fs_info *fs_info,  	if (tree_mod_dont_log(fs_info, eb))  		return 0; +	/* +	 * When we override something during the move, we log these removals. +	 * This can only happen when we move towards the beginning of the +	 * buffer, i.e. dst_slot < src_slot. +	 */  	for (i = 0; i + dst_slot < src_slot && i < nr_items; i++) {  		ret = tree_mod_log_insert_key_locked(fs_info, eb, i + dst_slot,  					      MOD_LOG_KEY_REMOVE_WHILE_MOVING); @@ -647,8 +652,6 @@ tree_mod_log_insert_root(struct btrfs_fs_info *fs_info,  	if (tree_mod_dont_log(fs_info, NULL))  		return 0; -	__tree_mod_log_free_eb(fs_info, old_root); -  	ret = tree_mod_alloc(fs_info, flags, &tm);  	if (ret < 0)  		goto out; @@ -926,12 +929,7 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans,  			ret = btrfs_dec_ref(trans, root, buf, 1, 1);  			BUG_ON(ret); /* -ENOMEM */  		} -		/* -		 * don't log freeing in case we're freeing the root node, this -		 * is done by tree_mod_log_set_root_pointer later -		 */ -		if (buf != root->node && btrfs_header_level(buf) != 0) -			tree_mod_log_free_eb(root->fs_info, buf); +		tree_mod_log_free_eb(root->fs_info, buf);  		clean_tree_block(trans, root, buf);  		*last_ref = 1;  	} @@ -1225,6 +1223,8 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,  	free_extent_buffer(eb);  	__tree_mod_log_rewind(eb_rewin, time_seq, tm); +	WARN_ON(btrfs_header_nritems(eb_rewin) > +		BTRFS_NODEPTRS_PER_BLOCK(fs_info->fs_root));  	return eb_rewin;  } @@ -1241,9 +1241,11 @@ get_old_root(struct btrfs_root *root, u64 time_seq)  {  	struct tree_mod_elem *tm;  	struct extent_buffer *eb; +	struct extent_buffer *old;  	struct tree_mod_root *old_root = NULL;  	u64 old_generation = 0;  	u64 logical; +	u32 blocksize;  	eb = btrfs_read_lock_root_node(root);  	tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); @@ -1259,14 +1261,32 @@ get_old_root(struct btrfs_root *root, u64 time_seq)  	}  	tm = tree_mod_log_search(root->fs_info, logical, time_seq); -	if (old_root) +	if (old_root && tm && tm->op != MOD_LOG_KEY_REMOVE_WHILE_FREEING) { +		btrfs_tree_read_unlock(root->node); +		free_extent_buffer(root->node); +		blocksize = btrfs_level_size(root, old_root->level); +		old = read_tree_block(root, logical, blocksize, 0); +		if (!old) { +			pr_warn("btrfs: failed to read tree block %llu from get_old_root\n", +				logical); +			WARN_ON(1); +		} else { +			eb = btrfs_clone_extent_buffer(old); +			free_extent_buffer(old); +		} +	} else if (old_root) { +		btrfs_tree_read_unlock(root->node); +		free_extent_buffer(root->node);  		eb = alloc_dummy_extent_buffer(logical, root->nodesize); -	else +	} else {  		eb = btrfs_clone_extent_buffer(root->node); -	btrfs_tree_read_unlock(root->node); -	free_extent_buffer(root->node); +		btrfs_tree_read_unlock(root->node); +		free_extent_buffer(root->node); +	} +  	if (!eb)  		return NULL; +	extent_buffer_get(eb);  	btrfs_tree_read_lock(eb);  	if (old_root) {  		btrfs_set_header_bytenr(eb, eb->start); @@ -1279,11 +1299,28 @@ get_old_root(struct btrfs_root *root, u64 time_seq)  		__tree_mod_log_rewind(eb, time_seq, tm);  	else  		WARN_ON(btrfs_header_level(eb) != 0); -	extent_buffer_get(eb); +	WARN_ON(btrfs_header_nritems(eb) > BTRFS_NODEPTRS_PER_BLOCK(root));  	return eb;  } +int btrfs_old_root_level(struct btrfs_root *root, u64 time_seq) +{ +	struct tree_mod_elem *tm; +	int level; + +	tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); +	if (tm && tm->op == MOD_LOG_ROOT_REPLACE) { +		level = tm->old_root.level; +	} else { +		rcu_read_lock(); +		level = btrfs_header_level(root->node); +		rcu_read_unlock(); +	} + +	return level; +} +  static inline int should_cow_block(struct btrfs_trans_handle *trans,  				   struct btrfs_root *root,  				   struct extent_buffer *buf) @@ -1725,6 +1762,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,  			goto enospc;  		} +		tree_mod_log_free_eb(root->fs_info, root->node);  		tree_mod_log_set_root_pointer(root, child);  		rcu_assign_pointer(root->node, child); @@ -2970,8 +3008,10 @@ static int push_node_left(struct btrfs_trans_handle *trans,  			   push_items * sizeof(struct btrfs_key_ptr));  	if (push_items < src_nritems) { -		tree_mod_log_eb_move(root->fs_info, src, 0, push_items, -				     src_nritems - push_items); +		/* +		 * don't call tree_mod_log_eb_move here, key removal was already +		 * fully logged by tree_mod_log_eb_copy above. +		 */  		memmove_extent_buffer(src, btrfs_node_key_ptr_offset(0),  				      btrfs_node_key_ptr_offset(push_items),  				      (src_nritems - push_items) * diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 926c9ffc66d..c72ead86950 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3120,6 +3120,7 @@ static inline u64 btrfs_inc_tree_mod_seq(struct btrfs_fs_info *fs_info)  {  	return atomic_inc_return(&fs_info->tree_mod_seq);  } +int btrfs_old_root_level(struct btrfs_root *root, u64 time_seq);  /* root-item.c */  int btrfs_find_root_ref(struct btrfs_root *tree_root, @@ -3338,6 +3339,8 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,  int btrfs_update_inode(struct btrfs_trans_handle *trans,  			      struct btrfs_root *root,  			      struct inode *inode); +int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, +				struct btrfs_root *root, struct inode *inode);  int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode);  int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode);  int btrfs_orphan_cleanup(struct btrfs_root *root); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 8036d3a8485..472873a94d9 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4110,8 +4110,8 @@ struct extent_buffer *alloc_dummy_extent_buffer(u64 start, unsigned long len)  	return eb;  err: -	for (i--; i >= 0; i--) -		__free_page(eb->pages[i]); +	for (; i > 0; i--) +		__free_page(eb->pages[i - 1]);  	__free_extent_buffer(eb);  	return NULL;  } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 85a1e5053fe..95542a1b3df 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -94,8 +94,6 @@ static noinline int cow_file_range(struct inode *inode,  				   struct page *locked_page,  				   u64 start, u64 end, int *page_started,  				   unsigned long *nr_written, int unlock); -static noinline int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, -				struct btrfs_root *root, struct inode *inode);  static int btrfs_init_inode_security(struct btrfs_trans_handle *trans,  				     struct inode *inode,  struct inode *dir, @@ -2746,8 +2744,9 @@ noinline int btrfs_update_inode(struct btrfs_trans_handle *trans,  	return btrfs_update_inode_item(trans, root, inode);  } -static noinline int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, -				struct btrfs_root *root, struct inode *inode) +noinline int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, +					 struct btrfs_root *root, +					 struct inode *inode)  {  	int ret; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 61168805f17..8fcf9a59c28 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -343,7 +343,8 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)  		return -EOPNOTSUPP;  	if (copy_from_user(&range, arg, sizeof(range)))  		return -EFAULT; -	if (range.start > total_bytes) +	if (range.start > total_bytes || +	    range.len < fs_info->sb->s_blocksize)  		return -EINVAL;  	range.len = min(range.len, total_bytes - range.start); @@ -570,7 +571,8 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,  		ret = btrfs_commit_transaction(trans,  					       root->fs_info->extent_root);  	} -	BUG_ON(ret); +	if (ret) +		goto fail;  	ret = pending_snapshot->error;  	if (ret) diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 5039686df6a..fe9d02c45f8 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -790,8 +790,10 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,  	}  	path = btrfs_alloc_path(); -	if (!path) -		return -ENOMEM; +	if (!path) { +		ret = -ENOMEM; +		goto out_free_root; +	}  	key.objectid = 0;  	key.type = BTRFS_QGROUP_STATUS_KEY; @@ -800,7 +802,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,  	ret = btrfs_insert_empty_item(trans, quota_root, path, &key,  				      sizeof(*ptr));  	if (ret) -		goto out; +		goto out_free_path;  	leaf = path->nodes[0];  	ptr = btrfs_item_ptr(leaf, path->slots[0], @@ -818,8 +820,15 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,  	fs_info->quota_root = quota_root;  	fs_info->pending_quota_state = 1;  	spin_unlock(&fs_info->qgroup_lock); -out: +out_free_path:  	btrfs_free_path(path); +out_free_root: +	if (ret) { +		free_extent_buffer(quota_root->node); +		free_extent_buffer(quota_root->commit_root); +		kfree(quota_root); +	} +out:  	return ret;  } diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index c7beb543a4a..e78b297b0b0 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -745,31 +745,36 @@ typedef int (*iterate_inode_ref_t)(int num, u64 dir, int index,  				   void *ctx);  /* - * Helper function to iterate the entries in ONE btrfs_inode_ref. + * Helper function to iterate the entries in ONE btrfs_inode_ref or + * btrfs_inode_extref.   * The iterate callback may return a non zero value to stop iteration. This can   * be a negative value for error codes or 1 to simply stop it.   * - * path must point to the INODE_REF when called. + * path must point to the INODE_REF or INODE_EXTREF when called.   */  static int iterate_inode_ref(struct send_ctx *sctx,  			     struct btrfs_root *root, struct btrfs_path *path,  			     struct btrfs_key *found_key, int resolve,  			     iterate_inode_ref_t iterate, void *ctx)  { -	struct extent_buffer *eb; +	struct extent_buffer *eb = path->nodes[0];  	struct btrfs_item *item;  	struct btrfs_inode_ref *iref; +	struct btrfs_inode_extref *extref;  	struct btrfs_path *tmp_path;  	struct fs_path *p; -	u32 cur; -	u32 len; +	u32 cur = 0;  	u32 total; -	int slot; +	int slot = path->slots[0];  	u32 name_len;  	char *start;  	int ret = 0; -	int num; +	int num = 0;  	int index; +	u64 dir; +	unsigned long name_off; +	unsigned long elem_size; +	unsigned long ptr;  	p = fs_path_alloc_reversed(sctx);  	if (!p) @@ -781,24 +786,40 @@ static int iterate_inode_ref(struct send_ctx *sctx,  		return -ENOMEM;  	} -	eb = path->nodes[0]; -	slot = path->slots[0]; -	item = btrfs_item_nr(eb, slot); -	iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); -	cur = 0; -	len = 0; -	total = btrfs_item_size(eb, item); -	num = 0; +	if (found_key->type == BTRFS_INODE_REF_KEY) { +		ptr = (unsigned long)btrfs_item_ptr(eb, slot, +						    struct btrfs_inode_ref); +		item = btrfs_item_nr(eb, slot); +		total = btrfs_item_size(eb, item); +		elem_size = sizeof(*iref); +	} else { +		ptr = btrfs_item_ptr_offset(eb, slot); +		total = btrfs_item_size_nr(eb, slot); +		elem_size = sizeof(*extref); +	} +  	while (cur < total) {  		fs_path_reset(p); -		name_len = btrfs_inode_ref_name_len(eb, iref); -		index = btrfs_inode_ref_index(eb, iref); +		if (found_key->type == BTRFS_INODE_REF_KEY) { +			iref = (struct btrfs_inode_ref *)(ptr + cur); +			name_len = btrfs_inode_ref_name_len(eb, iref); +			name_off = (unsigned long)(iref + 1); +			index = btrfs_inode_ref_index(eb, iref); +			dir = found_key->offset; +		} else { +			extref = (struct btrfs_inode_extref *)(ptr + cur); +			name_len = btrfs_inode_extref_name_len(eb, extref); +			name_off = (unsigned long)&extref->name; +			index = btrfs_inode_extref_index(eb, extref); +			dir = btrfs_inode_extref_parent(eb, extref); +		} +  		if (resolve) { -			start = btrfs_iref_to_path(root, tmp_path, iref, eb, -						found_key->offset, p->buf, -						p->buf_len); +			start = btrfs_ref_to_path(root, tmp_path, name_len, +						  name_off, eb, dir, +						  p->buf, p->buf_len);  			if (IS_ERR(start)) {  				ret = PTR_ERR(start);  				goto out; @@ -809,9 +830,10 @@ static int iterate_inode_ref(struct send_ctx *sctx,  						p->buf_len + p->buf - start);  				if (ret < 0)  					goto out; -				start = btrfs_iref_to_path(root, tmp_path, iref, -						eb, found_key->offset, p->buf, -						p->buf_len); +				start = btrfs_ref_to_path(root, tmp_path, +							  name_len, name_off, +							  eb, dir, +							  p->buf, p->buf_len);  				if (IS_ERR(start)) {  					ret = PTR_ERR(start);  					goto out; @@ -820,21 +842,16 @@ static int iterate_inode_ref(struct send_ctx *sctx,  			}  			p->start = start;  		} else { -			ret = fs_path_add_from_extent_buffer(p, eb, -					(unsigned long)(iref + 1), name_len); +			ret = fs_path_add_from_extent_buffer(p, eb, name_off, +							     name_len);  			if (ret < 0)  				goto out;  		} - -		len = sizeof(*iref) + name_len; -		iref = (struct btrfs_inode_ref *)((char *)iref + len); -		cur += len; - -		ret = iterate(num, found_key->offset, index, p, ctx); +		cur += elem_size + name_len; +		ret = iterate(num, dir, index, p, ctx);  		if (ret)  			goto out; -  		num++;  	} @@ -998,7 +1015,8 @@ static int get_inode_path(struct send_ctx *sctx, struct btrfs_root *root,  	}  	btrfs_item_key_to_cpu(p->nodes[0], &found_key, p->slots[0]);  	if (found_key.objectid != ino || -		found_key.type != BTRFS_INODE_REF_KEY) { +	    (found_key.type != BTRFS_INODE_REF_KEY && +	     found_key.type != BTRFS_INODE_EXTREF_KEY)) {  		ret = -ENOENT;  		goto out;  	} @@ -1551,8 +1569,8 @@ static int get_first_ref(struct send_ctx *sctx,  	struct btrfs_key key;  	struct btrfs_key found_key;  	struct btrfs_path *path; -	struct btrfs_inode_ref *iref;  	int len; +	u64 parent_dir;  	path = alloc_path_for_send();  	if (!path) @@ -1568,27 +1586,41 @@ static int get_first_ref(struct send_ctx *sctx,  	if (!ret)  		btrfs_item_key_to_cpu(path->nodes[0], &found_key,  				path->slots[0]); -	if (ret || found_key.objectid != key.objectid || -	    found_key.type != key.type) { +	if (ret || found_key.objectid != ino || +	    (found_key.type != BTRFS_INODE_REF_KEY && +	     found_key.type != BTRFS_INODE_EXTREF_KEY)) {  		ret = -ENOENT;  		goto out;  	} -	iref = btrfs_item_ptr(path->nodes[0], path->slots[0], -			struct btrfs_inode_ref); -	len = btrfs_inode_ref_name_len(path->nodes[0], iref); -	ret = fs_path_add_from_extent_buffer(name, path->nodes[0], -			(unsigned long)(iref + 1), len); +	if (key.type == BTRFS_INODE_REF_KEY) { +		struct btrfs_inode_ref *iref; +		iref = btrfs_item_ptr(path->nodes[0], path->slots[0], +				      struct btrfs_inode_ref); +		len = btrfs_inode_ref_name_len(path->nodes[0], iref); +		ret = fs_path_add_from_extent_buffer(name, path->nodes[0], +						     (unsigned long)(iref + 1), +						     len); +		parent_dir = found_key.offset; +	} else { +		struct btrfs_inode_extref *extref; +		extref = btrfs_item_ptr(path->nodes[0], path->slots[0], +					struct btrfs_inode_extref); +		len = btrfs_inode_extref_name_len(path->nodes[0], extref); +		ret = fs_path_add_from_extent_buffer(name, path->nodes[0], +					(unsigned long)&extref->name, len); +		parent_dir = btrfs_inode_extref_parent(path->nodes[0], extref); +	}  	if (ret < 0)  		goto out;  	btrfs_release_path(path); -	ret = get_inode_info(root, found_key.offset, NULL, dir_gen, NULL, NULL, +	ret = get_inode_info(root, parent_dir, NULL, dir_gen, NULL, NULL,  			NULL, NULL);  	if (ret < 0)  		goto out; -	*dir = found_key.offset; +	*dir = parent_dir;  out:  	btrfs_free_path(path); @@ -2430,7 +2462,8 @@ verbose_printk("btrfs: send_create_inode %llu\n", ino);  		TLV_PUT_PATH(sctx, BTRFS_SEND_A_PATH_LINK, p);  	} else if (S_ISCHR(mode) || S_ISBLK(mode) ||  		   S_ISFIFO(mode) || S_ISSOCK(mode)) { -		TLV_PUT_U64(sctx, BTRFS_SEND_A_RDEV, rdev); +		TLV_PUT_U64(sctx, BTRFS_SEND_A_RDEV, new_encode_dev(rdev)); +		TLV_PUT_U64(sctx, BTRFS_SEND_A_MODE, mode);  	}  	ret = send_cmd(sctx); @@ -3226,7 +3259,8 @@ static int process_all_refs(struct send_ctx *sctx,  		btrfs_item_key_to_cpu(eb, &found_key, slot);  		if (found_key.objectid != key.objectid || -		    found_key.type != key.type) +		    (found_key.type != BTRFS_INODE_REF_KEY && +		     found_key.type != BTRFS_INODE_EXTREF_KEY))  			break;  		ret = iterate_inode_ref(sctx, root, path, &found_key, 0, cb, @@ -3987,7 +4021,7 @@ static int process_recorded_refs_if_needed(struct send_ctx *sctx, int at_end)  	if (sctx->cur_ino == 0)  		goto out;  	if (!at_end && sctx->cur_ino == sctx->cmp_key->objectid && -	    sctx->cmp_key->type <= BTRFS_INODE_REF_KEY) +	    sctx->cmp_key->type <= BTRFS_INODE_EXTREF_KEY)  		goto out;  	if (list_empty(&sctx->new_refs) && list_empty(&sctx->deleted_refs))  		goto out; @@ -4033,22 +4067,21 @@ static int finish_inode_if_needed(struct send_ctx *sctx, int at_end)  	if (ret < 0)  		goto out; -	if (!S_ISLNK(sctx->cur_inode_mode)) { -		if (!sctx->parent_root || sctx->cur_inode_new) { +	if (!sctx->parent_root || sctx->cur_inode_new) { +		need_chown = 1; +		if (!S_ISLNK(sctx->cur_inode_mode))  			need_chmod = 1; -			need_chown = 1; -		} else { -			ret = get_inode_info(sctx->parent_root, sctx->cur_ino, -					NULL, NULL, &right_mode, &right_uid, -					&right_gid, NULL); -			if (ret < 0) -				goto out; +	} else { +		ret = get_inode_info(sctx->parent_root, sctx->cur_ino, +				NULL, NULL, &right_mode, &right_uid, +				&right_gid, NULL); +		if (ret < 0) +			goto out; -			if (left_uid != right_uid || left_gid != right_gid) -				need_chown = 1; -			if (left_mode != right_mode) -				need_chmod = 1; -		} +		if (left_uid != right_uid || left_gid != right_gid) +			need_chown = 1; +		if (!S_ISLNK(sctx->cur_inode_mode) && left_mode != right_mode) +			need_chmod = 1;  	}  	if (S_ISREG(sctx->cur_inode_mode)) { @@ -4335,7 +4368,8 @@ static int changed_cb(struct btrfs_root *left_root,  	if (key->type == BTRFS_INODE_ITEM_KEY)  		ret = changed_inode(sctx, result); -	else if (key->type == BTRFS_INODE_REF_KEY) +	else if (key->type == BTRFS_INODE_REF_KEY || +		 key->type == BTRFS_INODE_EXTREF_KEY)  		ret = changed_ref(sctx, result);  	else if (key->type == BTRFS_XATTR_ITEM_KEY)  		ret = changed_xattr(sctx, result); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 77db875b511..04bbfb1052e 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -1200,7 +1200,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,  	btrfs_i_size_write(parent_inode, parent_inode->i_size +  					 dentry->d_name.len * 2);  	parent_inode->i_mtime = parent_inode->i_ctime = CURRENT_TIME; -	ret = btrfs_update_inode(trans, parent_root, parent_inode); +	ret = btrfs_update_inode_fallback(trans, parent_root, parent_inode);  	if (ret)  		btrfs_abort_transaction(trans, root, ret);  fail: diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 029b903a4ae..0f5ebb72a5e 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1819,6 +1819,13 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)  				    "Failed to relocate sys chunks after "  				    "device initialization. This can be fixed "  				    "using the \"btrfs balance\" command."); +		trans = btrfs_attach_transaction(root); +		if (IS_ERR(trans)) { +			if (PTR_ERR(trans) == -ENOENT) +				return 0; +			return PTR_ERR(trans); +		} +		ret = btrfs_commit_transaction(trans, root);  	}  	return ret;  |