Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d24bec3a authored by Mark Fasheh's avatar Mark Fasheh Committed by Chris Mason
Browse files

btrfs: extended inode ref iteration



The iterate_irefs in backref.c is used to build path components from inode
refs. This patch adds code to iterate extended refs as well.

I had modify the callback function signature to abstract out some of the
differences between ref structures. iref_to_path() also needed similar
changes.

Signed-off-by: default avatarMark Fasheh <mfasheh@suse.de>
parent f186373f
Loading
Loading
Loading
Loading
+138 −35
Original line number Diff line number Diff line
@@ -1177,26 +1177,12 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
	return ret;
}

/*
 * this iterates to turn a btrfs_inode_ref into a full filesystem path. elements
 * of the path are separated by '/' and the path is guaranteed to be
 * 0-terminated. the path is only given within the current file system.
 * Therefore, it never starts with a '/'. the caller is responsible to provide
 * "size" bytes in "dest". the dest buffer will be filled backwards. finally,
 * the start point of the resulting string is returned. this pointer is within
 * dest, normally.
 * in case the path buffer would overflow, the pointer is decremented further
 * as if output was written to the buffer, though no more output is actually
 * generated. that way, the caller can determine how much space would be
 * required for the path to fit into the buffer. in that case, the returned
 * value will be smaller than dest. callers must check this!
 */
char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
			 struct btrfs_inode_ref *iref,
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)
{
	u32 len;
	int slot;
	u64 next_inum;
	int ret;
@@ -1204,17 +1190,17 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
	struct extent_buffer *eb = eb_in;
	struct btrfs_key found_key;
	int leave_spinning = path->leave_spinning;
	struct btrfs_inode_ref *iref;

	if (bytes_left >= 0)
		dest[bytes_left] = '\0';

	path->leave_spinning = 1;
	while (1) {
		len = btrfs_inode_ref_name_len(eb, iref);
		bytes_left -= len;
		bytes_left -= name_len;
		if (bytes_left >= 0)
			read_extent_buffer(eb, dest + bytes_left,
						(unsigned long)(iref + 1), len);
					   name_off, name_len);
		if (eb != eb_in) {
			btrfs_tree_read_unlock_blocking(eb);
			free_extent_buffer(eb);
@@ -1224,6 +1210,7 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
			ret = -ENOENT;
		if (ret)
			break;

		next_inum = found_key.offset;

		/* regular exit ahead */
@@ -1239,8 +1226,11 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
			btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
		}
		btrfs_release_path(path);

		iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref);

		name_len = btrfs_inode_ref_name_len(eb, iref);
		name_off = (unsigned long)(iref + 1);

		parent = next_inum;
		--bytes_left;
		if (bytes_left >= 0)
@@ -1256,6 +1246,32 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,
	return dest + bytes_left;
}

/*
 * this iterates to turn a btrfs_inode_ref into a full filesystem path. elements
 * of the path are separated by '/' and the path is guaranteed to be
 * 0-terminated. the path is only given within the current file system.
 * Therefore, it never starts with a '/'. the caller is responsible to provide
 * "size" bytes in "dest". the dest buffer will be filled backwards. finally,
 * the start point of the resulting string is returned. this pointer is within
 * dest, normally.
 * in case the path buffer would overflow, the pointer is decremented further
 * as if output was written to the buffer, though no more output is actually
 * generated. that way, the caller can determine how much space would be
 * required for the path to fit into the buffer. in that case, the returned
 * value will be smaller than dest. callers must check this!
 */
char *btrfs_iref_to_path(struct btrfs_root *fs_root,
			 struct btrfs_path *path,
			 struct btrfs_inode_ref *iref,
			 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);
}

/*
 * this makes the path point to (logical EXTENT_ITEM *)
 * returns BTRFS_EXTENT_FLAG_DATA for data, BTRFS_EXTENT_FLAG_TREE_BLOCK for
@@ -1529,7 +1545,10 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
	return ret;
}

static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
typedef int (iterate_irefs_t)(u64 parent, u32 name_len, unsigned long name_off,
			      struct extent_buffer *eb, void *ctx);

static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,
			      struct btrfs_path *path,
			      iterate_irefs_t *iterate, void *ctx)
{
@@ -1576,7 +1595,8 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
				 "tree %llu\n", cur,
				 (unsigned long long)found_key.objectid,
				 (unsigned long long)fs_root->objectid);
			ret = iterate(parent, iref, eb, ctx);
			ret = iterate(parent, name_len,
				      (unsigned long)(iref + 1), eb, ctx);
			if (ret)
				break;
			len = sizeof(*iref) + name_len;
@@ -1591,11 +1611,97 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
	return ret;
}

static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root,
				 struct btrfs_path *path,
				 iterate_irefs_t *iterate, void *ctx)
{
	int ret;
	int slot;
	u64 offset = 0;
	u64 parent;
	int found = 0;
	struct extent_buffer *eb;
	struct btrfs_inode_extref *extref;
	struct extent_buffer *leaf;
	u32 item_size;
	u32 cur_offset;
	unsigned long ptr;

	while (1) {
		ret = btrfs_find_one_extref(fs_root, inum, offset, path, &extref,
					    &offset);
		if (ret < 0)
			break;
		if (ret) {
			ret = found ? 0 : -ENOENT;
			break;
		}
		++found;

		slot = path->slots[0];
		eb = path->nodes[0];
		/* make sure we can use eb after releasing the path */
		atomic_inc(&eb->refs);

		btrfs_tree_read_lock(eb);
		btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
		btrfs_release_path(path);

		leaf = path->nodes[0];
		item_size = btrfs_item_size_nr(leaf, path->slots[0]);
		ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
		cur_offset = 0;

		while (cur_offset < item_size) {
			u32 name_len;

			extref = (struct btrfs_inode_extref *)(ptr + cur_offset);
			parent = btrfs_inode_extref_parent(eb, extref);
			name_len = btrfs_inode_extref_name_len(eb, extref);
			ret = iterate(parent, name_len,
				      (unsigned long)&extref->name, eb, ctx);
			if (ret)
				break;

			cur_offset += btrfs_inode_extref_name_len(leaf, extref);
			cur_offset += sizeof(*extref);
		}
		btrfs_tree_read_unlock_blocking(eb);
		free_extent_buffer(eb);

		offset++;
	}

	btrfs_release_path(path);

	return ret;
}

static int iterate_irefs(u64 inum, struct btrfs_root *fs_root,
			 struct btrfs_path *path, iterate_irefs_t *iterate,
			 void *ctx)
{
	int ret;
	int found_refs = 0;

	ret = iterate_inode_refs(inum, fs_root, path, iterate, ctx);
	if (!ret)
		++found_refs;
	else if (ret != -ENOENT)
		return ret;

	ret = iterate_inode_extrefs(inum, fs_root, path, iterate, ctx);
	if (ret == -ENOENT && found_refs)
		return 0;

	return ret;
}

/*
 * returns 0 if the path could be dumped (probably truncated)
 * returns <0 in case of an error
 */
static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref,
static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,
			 struct extent_buffer *eb, void *ctx)
{
	struct inode_fs_paths *ipath = ctx;
@@ -1609,20 +1715,17 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref,
					ipath->fspath->bytes_left - s_ptr : 0;

	fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr;
	fspath = btrfs_iref_to_path(ipath->fs_root, ipath->btrfs_path, iref, eb,
				inum, fspath_min, bytes_left);
	fspath = 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);

	if (fspath > fspath_min) {
		pr_debug("path resolved: %s\n", fspath);
		ipath->fspath->val[i] = (u64)(unsigned long)fspath;
		++ipath->fspath->elem_cnt;
		ipath->fspath->bytes_left = fspath - fspath_min;
	} else {
		pr_debug("missed path, not enough space. missing bytes: %lu, "
			 "constructed so far: %s\n",
			 (unsigned long)(fspath_min - fspath), fspath_min);
		++ipath->fspath->elem_missed;
		ipath->fspath->bytes_missing += fspath_min - fspath;
		ipath->fspath->bytes_left = 0;
+0 −2
Original line number Diff line number Diff line
@@ -33,8 +33,6 @@ struct inode_fs_paths {

typedef int (iterate_extent_inodes_t)(u64 inum, u64 offset, u64 root,
		void *ctx);
typedef int (iterate_irefs_t)(u64 parent, struct btrfs_inode_ref *iref,
				struct extent_buffer *eb, void *ctx);

int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
			struct btrfs_path *path);