diff mbox

[v2,1/7] added helper functions to iterate backrefs

Message ID 4e836f2ec21820d3be2022fa2a6d6a441fdff4b8.1308397267.git.list.btrfs@jan-o-sch.net (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Schmidt June 18, 2011, 11:45 a.m. UTC
These helper functions iterate back references and call a function for each
backref. There is also a function to resolve an inode to a path in the
file system.

Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 fs/btrfs/Makefile  |    3 +-
 fs/btrfs/backref.c |  445 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/btrfs/backref.h |   59 +++++++
 3 files changed, 506 insertions(+), 1 deletions(-)

Comments

David Sterba June 21, 2011, 2:37 p.m. UTC | #1
On Sat, Jun 18, 2011 at 01:45:58PM +0200, Jan Schmidt wrote:
> +/*
> + * 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,
> +				struct extent_buffer *eb_ir, int slot,
> +				void *ctx)
> +{
> +	struct inode_fs_paths *ipath = ctx;
> +	struct extent_buffer *eb_i = ipath->eb;
> +	u32 path_len;
> +	char *fs_path;
> +
> +	if (ipath->bytes_left < 2)
> +		return -EOVERFLOW;
> +
> +	*ipath->dest++ = ' ';
> +	--ipath->bytes_left;

this actually prepends a space before the first entry.

I've played a bit with this interface, it's the missing piece to
implement the 'inode nubmer -> directory names' search :)
All the names are placed into one buffer, 4k sized, separated by a
space. Not all files fit, so I suggest to print one filename at a time.
Me-user wants to see all the filenames, even if the list is potentially
long.

I've also noticed that in iref_to_path()

130                 len = btrfs_inode_ref_name_len(eb, iref);

returns very large values, which do not fit into the 4k buffer and the
function returns. When trying to print the data by read_extent_buffer to
a temporary array, a pagefault occurs (and seems to come from reading
the extent buffer). The numbers are like 19000 or higher, which are way
above path or filename maximum size. I don't think this is the
intentional behaviour, it relies on some side effect rather than clear
logic.

Example:
ipath buffer and scratch are 32K, each, ie. the overly sized
ref_name_len will fit there:

[ 8766.928232] btrfs: ino2name: 266  p1/
[ 8767.440964] i2p: [4] namelen 10, left 32766
[ 8767.446417] i2p: [7] path:
[ 8767.450445] i2p: [4] namelen 2, left 32755
[ 8767.455733] i2p: [7] path: /
[ 8767.459834] i2p: [2] p1/
[ 8767.463593] i2p: [4] namelen 0, left 32752
[ 8767.468903] i2p: [7] path:
[ 8767.472908] i2p: [4] namelen 2, left 32751
[ 8767.478188] i2p: [7] path: /
[ 8767.482280] i2p: [2] p1/
[ 8767.486024] i2p: [4] namelen 0, left 32748
[ 8767.491293] i2p: [7] path:
[ 8767.495283] i2p: [4] namelen 2, left 32747
[ 8767.500587] i2p: [7] path: /
[ 8767.504680] i2p: [2] p1/
[ 8767.508430] i2p: [4] namelen 0, left 32744
[ 8767.513708] i2p: [7] path:
[ 8767.517702] i2p: [4] namelen 2, left 32743
[ 8767.522969] i2p: [7] path: /
[ 8767.527042] i2p: [2] p1/
[ 8767.530787] i2p: [4] namelen 0, left 32740
[ 8767.536049] i2p: [7] path:
[ 8767.539991] i2p: [4] namelen 2, left 32739
[ 8767.545282] i2p: [7] path: /
[ 8767.549374] i2p: [2] p1/
[ 8767.553109] i2p: [4] namelen 0, left 32736
[ 8767.558377] i2p: [7] path:
[ 8767.562354] i2p: [4] namelen 2, left 32735
[ 8767.567615] i2p: [7] path: /
[ 8767.571713] i2p: [2] p1/
[ 8767.575443] i2p: [4] namelen 0, left 32732
[ 8767.580701] i2p: [7] path:
[ 8767.584678] i2p: [4] namelen 2, left 32731
[ 8767.589933] i2p: [7] path: /
[ 8767.594007] i2p: [2] p1/
[ 8767.597713] i2p: [4] namelen 0, left 32728
[ 8767.602908] i2p: [7] path:
[ 8767.606832] i2p: [4] namelen 2, left 32727
[ 8767.612030] i2p: [7] path: /
[ 8767.616050] i2p: [2] p1/
[ 8767.619676] i2p: [4] namelen 0, left 32724
[ 8767.624873] i2p: [7] path:
[ 8767.628782] i2p: [4] namelen 2, left 32723
[ 8767.633970] i2p: [7] path: /
[ 8767.637962] i2p: [2] p1/
[ 8767.641639] i2p: [4] namelen 0, left 32720
[ 8767.646838] i2p: [7] path:
[ 8767.650757] i2p: [4] namelen 2, left 32719
[ 8767.655952] i2p: [7] path: /
[ 8767.659940] i2p: [2] p1/
[ 8767.663604] i2p: [4] namelen 0, left 32716
[ 8767.668790] i2p: [7] path:
[ 8767.672696] i2p: [4] namelen 2, left 32715
[ 8767.677881] i2p: [7] path: /
[ 8767.681878] i2p: [2] p1/
[ 8767.685549] i2p: [4] namelen 0, left 32712
[ 8767.690742] i2p: [7] path:
[ 8767.694655] i2p: [4] namelen 2, left 32711
[ 8767.699843] i2p: [7] path: /
[ 8767.703840] i2p: [2] p1/
[ 8767.707520] i2p: [4] namelen 19967, left 32708
[ 8767.713057] i2p: [7] path:
[ 8767.716955] BUG: unable to handle kernel NULL pointer dereference at           (null)
[ 8767.720932] IP: [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
[ 8767.720932] PGD 77bd0067 PUD 79d35067 PMD 0
[ 8767.720932] Oops: 0000 [#1] SMP
[ 8767.720932] CPU 1
[ 8767.720932] Modules linked in: btrfs loop
[ 8767.720932]
[ 8767.720932] Pid: 10859, comm: btrfs-ino2path Not tainted 3.0.0-rc3-default+ #75 Intel Corporation Santa Rosa platform/Matanzas
[ 8767.720932] RIP: 0010:[<ffffffffa005f1d2>]  [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
[ 8767.720932] RSP: 0018:ffff880074acbc58  EFLAGS: 00010202
[ 8767.720932] RAX: 0000000000000000 RBX: 0000000000003deb RCX: 0000000000001000
[ 8767.720932] RDX: 00000000000001e0 RSI: 0000000000000000 RDI: 0000000000000246
[ 8767.720932] RBP: ffff880074acbcb8 R08: 0000000000000000 R09: 0000000000000001
[ 8767.720932] R10: 0000000000000000 R11: 0000000000001c04 R12: 0000000000000002
[ 8767.720932] R13: 0000000000000000 R14: ffff880079bac1d9 R15: ffff880074acbfd8
[ 8767.720932] FS:  00007f1399198740(0000) GS:ffff88007de00000(0000) knlGS:0000000000000000
[ 8767.720932] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[ 8767.720932] CR2: 0000000000000000 CR3: 0000000074b13000 CR4: 00000000000006e0
[ 8767.720932] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 8767.720932] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 8767.720932] Process btrfs-ino2path (pid: 10859, threadinfo ffff880074aca000, task ffff880077085340)
[ 8767.720932] Stack:
[ 8767.720932]  ffffffffa005f282 ffffffff81b21e74 ffff88002f2ef668 0000000000000000
[ 8767.720932]  ffff880073f94dd8 ffff880074acbfd8 ffff8800780bd000 00000000000031c5
[ 8767.720932]  00000000000031c5 0000000000000104 ffff880073f94dd8 ffff880074132130
[ 8767.720932] Call Trace:
[ 8767.720932]  [<ffffffffa005f282>] ? read_extent_buffer+0x152/0x220 [btrfs]
[ 8767.720932]  [<ffffffff81b21e74>] ? printk+0x68/0x6c
[ 8767.720932]  [<ffffffffa008924d>] inode_to_path+0x10d/0x2d0 [btrfs]
[ 8767.720932]  [<ffffffffa0089883>] paths_from_inode+0xe3/0x120 [btrfs]
[ 8767.720932]  [<ffffffffa006de9f>] btrfs_ioctl+0xb5f/0xf80 [btrfs]
[ 8767.720932]  [<ffffffff810d3e55>] ? trace_hardirqs_on_caller+0x155/0x1a0
[ 8767.720932]  [<ffffffff81080e4c>] ? finish_task_switch+0x7c/0x110
[ 8767.720932]  [<ffffffff81080e0f>] ? finish_task_switch+0x3f/0x110
[ 8767.720932]  [<ffffffff81193578>] do_vfs_ioctl+0x98/0x570
[ 8767.720932]  [<ffffffff811828be>] ? fget_light+0x33e/0x430
[ 8767.720932]  [<ffffffff81b2ed3a>] ? sysret_check+0x2e/0x69
[ 8767.720932]  [<ffffffff81193a9f>] sys_ioctl+0x4f/0x80
[ 8767.720932]  [<ffffffff81b2ed02>] system_call_fastpath+0x16/0x1b
[ 8767.720932] Code: 66 0f 1f 84 00 00 00 00 00 48 8b 55 c0 48 8b 42 30 48 89 c6 b9 00 10 00 00 4c 29 e9 48 39 d9 48 0f 47 cb 41 83 87 44 e0 ff ff 01
[ 8767.720932]  8b 00 48 c1 e8 2d 48 89 c2 48 c1 ea 08 48 8b 3c d5 00 95 f7
[ 8767.720932] RIP  [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
[ 8767.720932]  RSP <ffff880074acbc58>
[ 8767.720932] CR2: 0000000000000000
[ 8768.067994] ---[ end trace b9afe483f6289b6f ]---

Let's see the inode 266 by hand:

dsterba@:/mnt/sda10$ find . -inum 266
./p1/d6/d7/d1f/c41

something is wrong ...

The file hierarchy is a leftover from fs_mark runs, directories to depth 9,
short names.


david


> +
> +	fs_path = iref_to_path(ipath->fs_root, ipath->path, iref, eb_i,
> +				inum, ipath->scratch_buf, ipath->bytes_left);
> +	if (IS_ERR(fs_path))
> +		return PTR_ERR(fs_path);
> +	path_len = ipath->scratch_buf + ipath->bytes_left - fs_path - 1;
> +	if (path_len+1 > ipath->bytes_left)
> +		return -EOVERFLOW;
> +	memcpy(ipath->dest, fs_path, path_len+1);
> +	ipath->bytes_left -= path_len;
> +	ipath->dest += path_len;
> +
> +	return 0;
> +}
> +
> +int paths_from_inode(u64 inum, struct inode_fs_paths *ipath)
> +{
> +	return iterate_irefs(inum, ipath->eb, ipath->fs_root, ipath->path,
> +				inode_to_path, ipath);
> +}
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Schmidt June 21, 2011, 3:12 p.m. UTC | #2
On 21.06.2011 16:37, David Sterba wrote:
> On Sat, Jun 18, 2011 at 01:45:58PM +0200, Jan Schmidt wrote:
>> +/*
>> + * 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,
>> +				struct extent_buffer *eb_ir, int slot,
>> +				void *ctx)
>> +{
>> +	struct inode_fs_paths *ipath = ctx;
>> +	struct extent_buffer *eb_i = ipath->eb;
>> +	u32 path_len;
>> +	char *fs_path;
>> +
>> +	if (ipath->bytes_left < 2)
>> +		return -EOVERFLOW;
>> +
>> +	*ipath->dest++ = ' ';
>> +	--ipath->bytes_left;
> 
> this actually prepends a space before the first entry.

Which is fine because we print the buffer with "(path:%s)" in
scrub.c:257, and a space is well appropriate there. That way we don't
need to handle the first path differently. (Makes only sense as long as
we stick to multiple files per line).

> I've played a bit with this interface, it's the missing piece to
> implement the 'inode nubmer -> directory names' search :)
> All the names are placed into one buffer, 4k sized, separated by a
> space. Not all files fit, so I suggest to print one filename at a time.

It does print all references to one *inode* to the same buffer, right.
If there are more inodes refering to that extent, those paths are
printed to a separate buffer. You can make some reflinks to try that out.

> Me-user wants to see all the filenames, even if the list is potentially
> long.

Well, maybe. However, for me, the number of hardlinks created is either
very low, or I know why a file has a whole bunch of hardlinks myself
(e.g. some kind of backup strategy). In both cases, this approach would
fit. I don't want to see some thousands of printk messages for that one
file I know to have a lot of hardlinks - maybe even missing some error
of a non-hardlinked file due to rate limiting.

On the other hand, you could argue, when I deliberately create a bunch
of reflinks, I will get the same problem. Another approach would be to
put it all into one buffer, no matter which inode it comes from. Either
way shouldn't be hard to accomplish.

> I've also noticed that in iref_to_path()
> 
> 130                 len = btrfs_inode_ref_name_len(eb, iref);
> 
> returns very large values, which do not fit into the 4k buffer and the
> function returns. When trying to print the data by read_extent_buffer to
> a temporary array, a pagefault occurs (and seems to come from reading
> the extent buffer). The numbers are like 19000 or higher, which are way
> above path or filename maximum size. I don't think this is the
> intentional behaviour, it relies on some side effect rather than clear
> logic.

Something is going wrong here:

> Example:
> ipath buffer and scratch are 32K, each, ie. the overly sized
> ref_name_len will fit there:
> 
> [ 8766.928232] btrfs: ino2name: 266  p1/
> [ 8767.440964] i2p: [4] namelen 10, left 32766
> [ 8767.446417] i2p: [7] path:
> [ 8767.450445] i2p: [4] namelen 2, left 32755
> [ 8767.455733] i2p: [7] path: /
> [ 8767.459834] i2p: [2] p1/

Do I get that right? This is inode 266, which should refer to
./p1/d6/d7/d1f/c41 (as you state below). Already on second iteration,
we're at something named "pl", which shouldn't happen as we construct
the path bottom-up. And those namelen 0 in the following lines shouldn't
happen, either. Furthermore, the path should change on every iteration
(I guess, that might depend on the printk behind, of course).

> [ 8767.463593] i2p: [4] namelen 0, left 32752
> [ 8767.468903] i2p: [7] path:
> [ 8767.472908] i2p: [4] namelen 2, left 32751
> [ 8767.478188] i2p: [7] path: /
> [ 8767.482280] i2p: [2] p1/
> [ 8767.486024] i2p: [4] namelen 0, left 32748
> [ 8767.491293] i2p: [7] path:
> [ 8767.495283] i2p: [4] namelen 2, left 32747
> [ 8767.500587] i2p: [7] path: /
> [ 8767.504680] i2p: [2] p1/
> [ 8767.508430] i2p: [4] namelen 0, left 32744
> [ 8767.513708] i2p: [7] path:
> [ 8767.517702] i2p: [4] namelen 2, left 32743
> [ 8767.522969] i2p: [7] path: /
> [ 8767.527042] i2p: [2] p1/
> [ 8767.530787] i2p: [4] namelen 0, left 32740
> [ 8767.536049] i2p: [7] path:
> [ 8767.539991] i2p: [4] namelen 2, left 32739
> [ 8767.545282] i2p: [7] path: /
> [ 8767.549374] i2p: [2] p1/
> [ 8767.553109] i2p: [4] namelen 0, left 32736
> [ 8767.558377] i2p: [7] path:
> [ 8767.562354] i2p: [4] namelen 2, left 32735
> [ 8767.567615] i2p: [7] path: /
> [ 8767.571713] i2p: [2] p1/
> [ 8767.575443] i2p: [4] namelen 0, left 32732
> [ 8767.580701] i2p: [7] path:
> [ 8767.584678] i2p: [4] namelen 2, left 32731
> [ 8767.589933] i2p: [7] path: /
> [ 8767.594007] i2p: [2] p1/
> [ 8767.597713] i2p: [4] namelen 0, left 32728
> [ 8767.602908] i2p: [7] path:
> [ 8767.606832] i2p: [4] namelen 2, left 32727
> [ 8767.612030] i2p: [7] path: /
> [ 8767.616050] i2p: [2] p1/
> [ 8767.619676] i2p: [4] namelen 0, left 32724
> [ 8767.624873] i2p: [7] path:
> [ 8767.628782] i2p: [4] namelen 2, left 32723
> [ 8767.633970] i2p: [7] path: /
> [ 8767.637962] i2p: [2] p1/
> [ 8767.641639] i2p: [4] namelen 0, left 32720
> [ 8767.646838] i2p: [7] path:
> [ 8767.650757] i2p: [4] namelen 2, left 32719
> [ 8767.655952] i2p: [7] path: /
> [ 8767.659940] i2p: [2] p1/
> [ 8767.663604] i2p: [4] namelen 0, left 32716
> [ 8767.668790] i2p: [7] path:
> [ 8767.672696] i2p: [4] namelen 2, left 32715
> [ 8767.677881] i2p: [7] path: /
> [ 8767.681878] i2p: [2] p1/
> [ 8767.685549] i2p: [4] namelen 0, left 32712
> [ 8767.690742] i2p: [7] path:
> [ 8767.694655] i2p: [4] namelen 2, left 32711
> [ 8767.699843] i2p: [7] path: /
> [ 8767.703840] i2p: [2] p1/
> [ 8767.707520] i2p: [4] namelen 19967, left 32708
> [ 8767.713057] i2p: [7] path:
> [ 8767.716955] BUG: unable to handle kernel NULL pointer dereference at           (null)
> [ 8767.720932] IP: [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
> [ 8767.720932] PGD 77bd0067 PUD 79d35067 PMD 0
> [ 8767.720932] Oops: 0000 [#1] SMP
> [ 8767.720932] CPU 1
> [ 8767.720932] Modules linked in: btrfs loop
> [ 8767.720932]
> [ 8767.720932] Pid: 10859, comm: btrfs-ino2path Not tainted 3.0.0-rc3-default+ #75 Intel Corporation Santa Rosa platform/Matanzas
> [ 8767.720932] RIP: 0010:[<ffffffffa005f1d2>]  [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
> [ 8767.720932] RSP: 0018:ffff880074acbc58  EFLAGS: 00010202
> [ 8767.720932] RAX: 0000000000000000 RBX: 0000000000003deb RCX: 0000000000001000
> [ 8767.720932] RDX: 00000000000001e0 RSI: 0000000000000000 RDI: 0000000000000246
> [ 8767.720932] RBP: ffff880074acbcb8 R08: 0000000000000000 R09: 0000000000000001
> [ 8767.720932] R10: 0000000000000000 R11: 0000000000001c04 R12: 0000000000000002
> [ 8767.720932] R13: 0000000000000000 R14: ffff880079bac1d9 R15: ffff880074acbfd8
> [ 8767.720932] FS:  00007f1399198740(0000) GS:ffff88007de00000(0000) knlGS:0000000000000000
> [ 8767.720932] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
> [ 8767.720932] CR2: 0000000000000000 CR3: 0000000074b13000 CR4: 00000000000006e0
> [ 8767.720932] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> [ 8767.720932] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> [ 8767.720932] Process btrfs-ino2path (pid: 10859, threadinfo ffff880074aca000, task ffff880077085340)
> [ 8767.720932] Stack:
> [ 8767.720932]  ffffffffa005f282 ffffffff81b21e74 ffff88002f2ef668 0000000000000000
> [ 8767.720932]  ffff880073f94dd8 ffff880074acbfd8 ffff8800780bd000 00000000000031c5
> [ 8767.720932]  00000000000031c5 0000000000000104 ffff880073f94dd8 ffff880074132130
> [ 8767.720932] Call Trace:
> [ 8767.720932]  [<ffffffffa005f282>] ? read_extent_buffer+0x152/0x220 [btrfs]
> [ 8767.720932]  [<ffffffff81b21e74>] ? printk+0x68/0x6c
> [ 8767.720932]  [<ffffffffa008924d>] inode_to_path+0x10d/0x2d0 [btrfs]
> [ 8767.720932]  [<ffffffffa0089883>] paths_from_inode+0xe3/0x120 [btrfs]
> [ 8767.720932]  [<ffffffffa006de9f>] btrfs_ioctl+0xb5f/0xf80 [btrfs]
> [ 8767.720932]  [<ffffffff810d3e55>] ? trace_hardirqs_on_caller+0x155/0x1a0
> [ 8767.720932]  [<ffffffff81080e4c>] ? finish_task_switch+0x7c/0x110
> [ 8767.720932]  [<ffffffff81080e0f>] ? finish_task_switch+0x3f/0x110
> [ 8767.720932]  [<ffffffff81193578>] do_vfs_ioctl+0x98/0x570
> [ 8767.720932]  [<ffffffff811828be>] ? fget_light+0x33e/0x430
> [ 8767.720932]  [<ffffffff81b2ed3a>] ? sysret_check+0x2e/0x69
> [ 8767.720932]  [<ffffffff81193a9f>] sys_ioctl+0x4f/0x80
> [ 8767.720932]  [<ffffffff81b2ed02>] system_call_fastpath+0x16/0x1b
> [ 8767.720932] Code: 66 0f 1f 84 00 00 00 00 00 48 8b 55 c0 48 8b 42 30 48 89 c6 b9 00 10 00 00 4c 29 e9 48 39 d9 48 0f 47 cb 41 83 87 44 e0 ff ff 01
> [ 8767.720932]  8b 00 48 c1 e8 2d 48 89 c2 48 c1 ea 08 48 8b 3c d5 00 95 f7
> [ 8767.720932] RIP  [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
> [ 8767.720932]  RSP <ffff880074acbc58>
> [ 8767.720932] CR2: 0000000000000000
> [ 8768.067994] ---[ end trace b9afe483f6289b6f ]---
> 
> Let's see the inode 266 by hand:
> 
> dsterba@:/mnt/sda10$ find . -inum 266
> ./p1/d6/d7/d1f/c41
> 
> something is wrong ...
> 
> The file hierarchy is a leftover from fs_mark runs, directories to depth 9,
> short names.

Sounds like I missed something. There is definitely a bug in the path
construction or lower iteration code I added.

I used a fs_mark created btrfs for my tests as well, but those worked
well. If you could share that file system (image or debug-tree output),
that'd be great help debugging this.

Thanks!
Jan
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Schmidt June 23, 2011, 8:20 a.m. UTC | #3
On 21.06.2011 17:12, Jan Schmidt wrote:
> On 21.06.2011 16:37, David Sterba wrote:
>> [...]
> Something is going wrong here:
> 
>> Example:
>> ipath buffer and scratch are 32K, each, ie. the overly sized
>> ref_name_len will fit there:
>>
>> [ 8766.928232] btrfs: ino2name: 266  p1/
>> [ 8767.440964] i2p: [4] namelen 10, left 32766
>> [ 8767.446417] i2p: [7] path:
>> [ 8767.450445] i2p: [4] namelen 2, left 32755
>> [ 8767.455733] i2p: [7] path: /
>> [ 8767.459834] i2p: [2] p1/
> 
> Do I get that right? This is inode 266, which should refer to
> ./p1/d6/d7/d1f/c41 (as you state below). Already on second iteration,
> we're at something named "pl", which shouldn't happen as we construct
> the path bottom-up. And those namelen 0 in the following lines shouldn't
> happen, either. Furthermore, the path should change on every iteration
> (I guess, that might depend on the printk behind, of course).
> 
>> [ 8767.463593] i2p: [4] namelen 0, left 32752
>> [ 8767.468903] i2p: [7] path:
>> [ 8767.472908] i2p: [4] namelen 2, left 32751
>> [ 8767.478188] i2p: [7] path: /
>> [ 8767.482280] i2p: [2] p1/
>> [ 8767.486024] i2p: [4] namelen 0, left 32748
>> [ 8767.491293] i2p: [7] path:
>> [ 8767.495283] i2p: [4] namelen 2, left 32747
>> [ 8767.500587] i2p: [7] path: /
>> [ 8767.504680] i2p: [2] p1/
>> [ 8767.508430] i2p: [4] namelen 0, left 32744
>> [ 8767.513708] i2p: [7] path:
>> [ 8767.517702] i2p: [4] namelen 2, left 32743
>> [ 8767.522969] i2p: [7] path: /
>> [ 8767.527042] i2p: [2] p1/
>> [ 8767.530787] i2p: [4] namelen 0, left 32740
>> [ 8767.536049] i2p: [7] path:
>> [ 8767.539991] i2p: [4] namelen 2, left 32739
>> [ 8767.545282] i2p: [7] path: /
>> [ 8767.549374] i2p: [2] p1/
>> [ 8767.553109] i2p: [4] namelen 0, left 32736
>> [ 8767.558377] i2p: [7] path:
>> [ 8767.562354] i2p: [4] namelen 2, left 32735
>> [ 8767.567615] i2p: [7] path: /
>> [ 8767.571713] i2p: [2] p1/
>> [ 8767.575443] i2p: [4] namelen 0, left 32732
>> [ 8767.580701] i2p: [7] path:
>> [ 8767.584678] i2p: [4] namelen 2, left 32731
>> [ 8767.589933] i2p: [7] path: /
>> [ 8767.594007] i2p: [2] p1/
>> [ 8767.597713] i2p: [4] namelen 0, left 32728
>> [ 8767.602908] i2p: [7] path:
>> [ 8767.606832] i2p: [4] namelen 2, left 32727
>> [ 8767.612030] i2p: [7] path: /
>> [ 8767.616050] i2p: [2] p1/
>> [ 8767.619676] i2p: [4] namelen 0, left 32724
>> [ 8767.624873] i2p: [7] path:
>> [ 8767.628782] i2p: [4] namelen 2, left 32723
>> [ 8767.633970] i2p: [7] path: /
>> [ 8767.637962] i2p: [2] p1/
>> [ 8767.641639] i2p: [4] namelen 0, left 32720
>> [ 8767.646838] i2p: [7] path:
>> [ 8767.650757] i2p: [4] namelen 2, left 32719
>> [ 8767.655952] i2p: [7] path: /
>> [ 8767.659940] i2p: [2] p1/
>> [ 8767.663604] i2p: [4] namelen 0, left 32716
>> [ 8767.668790] i2p: [7] path:
>> [ 8767.672696] i2p: [4] namelen 2, left 32715
>> [ 8767.677881] i2p: [7] path: /
>> [ 8767.681878] i2p: [2] p1/
>> [ 8767.685549] i2p: [4] namelen 0, left 32712
>> [ 8767.690742] i2p: [7] path:
>> [ 8767.694655] i2p: [4] namelen 2, left 32711
>> [ 8767.699843] i2p: [7] path: /
>> [ 8767.703840] i2p: [2] p1/
>> [ 8767.707520] i2p: [4] namelen 19967, left 32708
>> [ 8767.713057] i2p: [7] path:
>> [ 8767.716955] BUG: unable to handle kernel NULL pointer dereference at           (null)
>> [ 8767.720932] IP: [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
>> [ 8767.720932] PGD 77bd0067 PUD 79d35067 PMD 0
>> [ 8767.720932] Oops: 0000 [#1] SMP
>> [ 8767.720932] CPU 1
>> [ 8767.720932] Modules linked in: btrfs loop
>> [ 8767.720932]
>> [ 8767.720932] Pid: 10859, comm: btrfs-ino2path Not tainted 3.0.0-rc3-default+ #75 Intel Corporation Santa Rosa platform/Matanzas
>> [ 8767.720932] RIP: 0010:[<ffffffffa005f1d2>]  [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
>> [ 8767.720932] RSP: 0018:ffff880074acbc58  EFLAGS: 00010202
>> [ 8767.720932] RAX: 0000000000000000 RBX: 0000000000003deb RCX: 0000000000001000
>> [ 8767.720932] RDX: 00000000000001e0 RSI: 0000000000000000 RDI: 0000000000000246
>> [ 8767.720932] RBP: ffff880074acbcb8 R08: 0000000000000000 R09: 0000000000000001
>> [ 8767.720932] R10: 0000000000000000 R11: 0000000000001c04 R12: 0000000000000002
>> [ 8767.720932] R13: 0000000000000000 R14: ffff880079bac1d9 R15: ffff880074acbfd8
>> [ 8767.720932] FS:  00007f1399198740(0000) GS:ffff88007de00000(0000) knlGS:0000000000000000
>> [ 8767.720932] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
>> [ 8767.720932] CR2: 0000000000000000 CR3: 0000000074b13000 CR4: 00000000000006e0
>> [ 8767.720932] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>> [ 8767.720932] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
>> [ 8767.720932] Process btrfs-ino2path (pid: 10859, threadinfo ffff880074aca000, task ffff880077085340)
>> [ 8767.720932] Stack:
>> [ 8767.720932]  ffffffffa005f282 ffffffff81b21e74 ffff88002f2ef668 0000000000000000
>> [ 8767.720932]  ffff880073f94dd8 ffff880074acbfd8 ffff8800780bd000 00000000000031c5
>> [ 8767.720932]  00000000000031c5 0000000000000104 ffff880073f94dd8 ffff880074132130
>> [ 8767.720932] Call Trace:
>> [ 8767.720932]  [<ffffffffa005f282>] ? read_extent_buffer+0x152/0x220 [btrfs]
>> [ 8767.720932]  [<ffffffff81b21e74>] ? printk+0x68/0x6c
>> [ 8767.720932]  [<ffffffffa008924d>] inode_to_path+0x10d/0x2d0 [btrfs]
>> [ 8767.720932]  [<ffffffffa0089883>] paths_from_inode+0xe3/0x120 [btrfs]
>> [ 8767.720932]  [<ffffffffa006de9f>] btrfs_ioctl+0xb5f/0xf80 [btrfs]
>> [ 8767.720932]  [<ffffffff810d3e55>] ? trace_hardirqs_on_caller+0x155/0x1a0
>> [ 8767.720932]  [<ffffffff81080e4c>] ? finish_task_switch+0x7c/0x110
>> [ 8767.720932]  [<ffffffff81080e0f>] ? finish_task_switch+0x3f/0x110
>> [ 8767.720932]  [<ffffffff81193578>] do_vfs_ioctl+0x98/0x570
>> [ 8767.720932]  [<ffffffff811828be>] ? fget_light+0x33e/0x430
>> [ 8767.720932]  [<ffffffff81b2ed3a>] ? sysret_check+0x2e/0x69
>> [ 8767.720932]  [<ffffffff81193a9f>] sys_ioctl+0x4f/0x80
>> [ 8767.720932]  [<ffffffff81b2ed02>] system_call_fastpath+0x16/0x1b
>> [ 8767.720932] Code: 66 0f 1f 84 00 00 00 00 00 48 8b 55 c0 48 8b 42 30 48 89 c6 b9 00 10 00 00 4c 29 e9 48 39 d9 48 0f 47 cb 41 83 87 44 e0 ff ff 01
>> [ 8767.720932]  8b 00 48 c1 e8 2d 48 89 c2 48 c1 ea 08 48 8b 3c d5 00 95 f7
>> [ 8767.720932] RIP  [<ffffffffa005f1d2>] read_extent_buffer+0xa2/0x220 [btrfs]
>> [ 8767.720932]  RSP <ffff880074acbc58>
>> [ 8767.720932] CR2: 0000000000000000
>> [ 8768.067994] ---[ end trace b9afe483f6289b6f ]---
>>
>> Let's see the inode 266 by hand:
>>
>> dsterba@:/mnt/sda10$ find . -inum 266
>> ./p1/d6/d7/d1f/c41
>>
>> something is wrong ...
>>
>> The file hierarchy is a leftover from fs_mark runs, directories to depth 9,
>> short names.
> 
> Sounds like I missed something. There is definitely a bug in the path
> construction or lower iteration code I added.
> 
> I used a fs_mark created btrfs for my tests as well, but those worked
> well. If you could share that file system (image or debug-tree output),
> that'd be great help debugging this.

I got that fs from David (thanks) and it turned out that there was a
silly mistake in iref_to_path() in my code. I used the wrong extent
buffer which worked only as long as INODE_ITEM and INODE_REF resided in
the same leaf.

That's fixed now and I'll send a new version later. This will also
contain a fix to find EXTENT_DATA_REF that are not inline refs but
separately in the tree (v2 will miss some paths once you have more than
6 reflinks).

Thanks,
-Jan
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 9b72dcf..c63f649 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -7,4 +7,5 @@  btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
 	   extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \
 	   extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
 	   export.o tree-log.o acl.o free-space-cache.o zlib.o lzo.o \
-	   compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o
+	   compression.o delayed-ref.o relocation.o delayed-inode.o backref.o \
+	   scrub.o
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
new file mode 100644
index 0000000..64611e6
--- /dev/null
+++ b/fs/btrfs/backref.c
@@ -0,0 +1,445 @@ 
+/*
+ * Copyright (C) 2011 STRATO.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include "ctree.h"
+#include "backref.h"
+
+static int __inode_info(u64 inum, u64 ioff, u8 key_type,
+			struct btrfs_root *fs_root, struct btrfs_path *path,
+			struct btrfs_key *found_key)
+{
+	int ret;
+	struct btrfs_key key;
+	struct extent_buffer *eb;
+
+	key.type = key_type;
+	key.objectid = inum;
+	key.offset = ioff;
+
+	ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
+	if (ret < 0)
+		return ret;
+
+	eb = path->nodes[0];
+	if (ret && path->slots[0] >= btrfs_header_nritems(eb)) {
+		ret = btrfs_next_leaf(fs_root, path);
+		if (ret)
+			return ret;
+		eb = path->nodes[0];
+	}
+
+	btrfs_item_key_to_cpu(eb, found_key, path->slots[0]);
+	if (found_key->type != key.type || found_key->objectid != key.objectid)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * this makes the path point to (inum INODE_ITEM ioff)
+ */
+int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
+			struct btrfs_path *path)
+{
+	struct btrfs_key key;
+	return __inode_info(inum, ioff, BTRFS_INODE_ITEM_KEY, fs_root, path,
+				&key);
+}
+
+static int inode_ref_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
+				struct btrfs_path *path, int strict,
+				u64 *out_parent_inum,
+				struct extent_buffer **out_iref_eb,
+				int *out_slot)
+{
+	int ret;
+	struct btrfs_key found_key;
+
+	ret = __inode_info(inum, ioff, BTRFS_INODE_REF_KEY, fs_root, path,
+				&found_key);
+
+	if (!ret) {
+		if (out_slot)
+			*out_slot = path->slots[0];
+		if (out_iref_eb)
+			*out_iref_eb = path->nodes[0];
+		if (out_parent_inum)
+			*out_parent_inum = found_key.offset;
+	}
+
+	btrfs_release_path(path);
+	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! the idea is
+ * that in case of an overflow, the lower part in the hierarchie is most
+ * important to the user. finally, the start point of the resulting string is
+ * returned. in case the path would overflow, "..." is added at the front of
+ * the string and iteration stops regularly.
+ */
+static char *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)
+{
+	u32 len;
+	int slot;
+	u64 inum;
+	int ret;
+	u32 bytes_left = size - 1;
+
+	dest[bytes_left] = '\0';
+
+	while (1) {
+		len = btrfs_inode_ref_name_len(eb, iref);
+		if (len > bytes_left) {
+			if (size < 4)
+				break;
+			if (bytes_left > 3)
+				bytes_left -= 3;
+			else
+				bytes_left = 0;
+			memcpy(dest + bytes_left, "...", 3);
+			break;
+		}
+		bytes_left -= len;
+		read_extent_buffer(eb, dest + bytes_left,
+					(unsigned long)(iref + 1), len);
+
+		ret = inode_item_info(parent, 0, fs_root, path);
+		if (ret)
+			return ERR_PTR(ret);
+		eb = path->nodes[0];
+		btrfs_release_path(path);
+
+		ret = inode_ref_info(parent, 0, fs_root, path, 0,
+					&inum, NULL, &slot);
+		if (ret)
+			return ERR_PTR(ret);
+
+		/* regular exit ahead */
+		if (parent == inum)
+			break;
+
+		iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref);
+		parent = inum;
+		if (bytes_left > 0) {
+			--bytes_left;
+			dest[bytes_left] = '/';
+		}
+	}
+
+	return dest + bytes_left;
+}
+
+/*
+ * this makes the path point to (logical EXTENT_ITEM *)
+ * returns 0 for data blocks, 1 for tree blocks and <0 on error
+ */
+int data_extent_from_logical(struct btrfs_root *root, u64 logical,
+				struct btrfs_path *path,
+				struct btrfs_key *found_key)
+{
+	int ret;
+	u64 flags;
+	u32 item_size;
+	struct extent_buffer *eb;
+	struct btrfs_extent_item *ei;
+	struct btrfs_key key;
+
+	key.type = BTRFS_EXTENT_ITEM_KEY;
+	key.objectid = logical;
+	key.offset = (u64)-1;
+
+	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+	if (ret < 0)
+		return ret;
+	ret = btrfs_previous_item(root->fs_info->extent_root, path,
+					0, BTRFS_EXTENT_ITEM_KEY);
+	if (ret < 0)
+		return ret;
+	btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]);
+	if (found_key->type != BTRFS_EXTENT_ITEM_KEY ||
+	    found_key->objectid > logical ||
+	    found_key->objectid + found_key->offset <= logical)
+		return -ENOENT;
+
+	eb = path->nodes[0];
+	item_size = btrfs_item_size_nr(eb, path->slots[0]);
+	BUG_ON(item_size < sizeof(*ei));
+
+	ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item);
+	flags = btrfs_extent_flags(eb, ei);
+
+	if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)
+		return 1;
+
+	return 0;
+}
+
+/*
+ * helper function to iterate extent backrefs. ptr must point to a 0 value for
+ * the first call and may be modified. it is used to track state.
+ * if more backrefs exist, 0 is returned and the next call to __get_extent_ref
+ * must pass the modified ptr parameter to get to the next backref.
+ * after the last backref was processed, 1 is returned.
+ * returns <0 on error
+ */
+static int __get_extent_ref(u64 flags_wanted, u8 type_wanted,
+				unsigned long *ptr, struct extent_buffer *eb,
+				struct btrfs_extent_item *ei, u32 item_size,
+				struct btrfs_extent_inline_ref **eiref)
+{
+	int type;
+	unsigned long end;
+	u64 flags;
+	struct btrfs_tree_block_info *info;
+
+	if (!*ptr) {
+		/* first call */
+		flags = btrfs_extent_flags(eb, ei);
+		if (!(flags & flags_wanted))
+			return -EINVAL;
+		if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+			info = (struct btrfs_tree_block_info *)(ei + 1);
+			*eiref = (struct btrfs_extent_inline_ref *)(info + 1);
+		} else {
+			*eiref = (struct btrfs_extent_inline_ref *)(ei + 1);
+		}
+		*ptr = (unsigned long)*eiref;
+	}
+
+	end = (unsigned long)ei + item_size;
+
+	do {
+		*eiref = (struct btrfs_extent_inline_ref *)*ptr;
+		type = btrfs_extent_inline_ref_type(eb, *eiref);
+
+		*ptr += btrfs_extent_inline_ref_size(type);
+
+		WARN_ON(*ptr > end);
+		if (*ptr == end)
+			return 1; /* last */
+	} while (type != type_wanted);
+
+	return 0;
+}
+
+/*
+ * reads the tree block backref for an extent. tree level and root are returned
+ * through out_level and out_root. ptr must point to a 0 value for the first
+ * call and may be modified (see __get_extent_ref comment).
+ * returns 0 on success, <0 on error. note: in contrast to __get_extent_ref this
+ * one never returns 1!
+ */
+int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
+				struct btrfs_extent_item *ei, u32 item_size,
+				u64 *out_root, u8 *out_level)
+{
+	int ret;
+	struct btrfs_tree_block_info *info;
+	struct btrfs_extent_inline_ref *eiref;
+
+	ret = __get_extent_ref(BTRFS_EXTENT_FLAG_TREE_BLOCK,
+				BTRFS_TREE_BLOCK_REF_KEY, ptr, eb, ei,
+				item_size, &eiref);
+	if (ret < 0)
+		return ret;
+
+	if (!ret) {
+		printk(KERN_ERR "btrfs: tree_backtef_for_extent detected "
+			"multiple tree backrefs for an extent at %llu\n",
+			(unsigned long long)eb->start);
+		WARN_ON(1);
+	}
+
+	info = (struct btrfs_tree_block_info *)(ei + 1);
+	*out_root = btrfs_extent_inline_ref_offset(eb, eiref);
+	*out_level = btrfs_tree_block_level(eb, info);
+
+	return 0;
+}
+
+static int data_inode_for_extent(unsigned long *ptr, struct extent_buffer *eb,
+					struct btrfs_extent_item *ei,
+					u32 item_size, u64 *out_inum,
+					u64 *out_ioff)
+{
+	int ret;
+	struct btrfs_extent_inline_ref *eiref;
+	struct btrfs_extent_data_ref *dref;
+
+	ret = __get_extent_ref(BTRFS_EXTENT_FLAG_DATA,
+				BTRFS_EXTENT_DATA_REF_KEY, ptr, eb, ei,
+				item_size, &eiref);
+	if (ret < 0)
+		return ret;
+
+	dref = (struct btrfs_extent_data_ref *)(&eiref->offset);
+	if (btrfs_extent_data_ref_root(eb, dref) != BTRFS_FS_TREE_OBJECTID) {
+		WARN_ON(1);
+		return -EIO;
+	}
+
+	*out_inum = btrfs_extent_data_ref_objectid(eb, dref);
+	*out_ioff = btrfs_extent_data_ref_offset(eb, dref);
+
+	return ret;
+}
+
+/*
+ * calls iterate() for every inode that references the extent identified by
+ * the given parameters.
+ * when the iterator function returns a non-zero value, iteration stops.
+ */
+int iterate_extent_inodes(struct extent_buffer *eb,
+				struct btrfs_extent_item *ei,
+				u64 extent_item_offset, u32 item_size,
+				iterate_extent_inodes_t *iterate, void *ctx)
+{
+	int last;
+	u64 inum;
+	unsigned long ptr = 0;
+	u64 extent_data_item_offset;
+	int ret;
+
+	do {
+		last = data_inode_for_extent(&ptr, eb, ei, item_size, &inum,
+						&extent_data_item_offset);
+		if (last < 0)
+			return last;
+
+		ret = iterate(inum, extent_item_offset+extent_data_item_offset,
+				ctx);
+		if (ret)
+			return ret;
+
+	} while (!last);
+
+	return 0;
+}
+
+int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
+				struct btrfs_path *path,
+				iterate_extent_inodes_t *iterate, void *ctx)
+{
+	int ret;
+	u32 item_size;
+	struct extent_buffer *l;
+	struct btrfs_extent_item *extent;
+	u64 offset;
+	struct btrfs_key found_key;
+
+	ret = data_extent_from_logical(fs_info->extent_root, logical, path,
+					&found_key);
+	if (ret)
+		return ret;
+
+	offset = logical - found_key.objectid;
+	l = path->nodes[0];
+	extent = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
+	item_size = btrfs_item_size_nr(l, path->slots[0]);
+	btrfs_release_path(path);
+
+	ret = iterate_extent_inodes(l, extent, offset, item_size, iterate, ctx);
+
+	return ret;
+}
+
+static int iterate_irefs(u64 inum, struct extent_buffer *eb_i,
+				struct btrfs_root *fs_root,
+				struct btrfs_path *path,
+				iterate_irefs_t *iterate, void *ctx)
+{
+	int ret;
+	int slot;
+	u32 cur;
+	u32 len;
+	u32 name_len;
+	u64 parent = 0;
+	struct extent_buffer *eb_ir;
+	struct btrfs_item *item;
+	struct btrfs_inode_ref *iref;
+
+	while (1) {
+		ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path,
+					1, &parent, &eb_ir, &slot);
+		if (ret < 0)
+			return ret;
+		if (ret)
+			break;
+
+		item = btrfs_item_nr(eb_i, slot);
+		iref = btrfs_item_ptr(eb_i, slot, struct btrfs_inode_ref);
+
+		for (cur = 0; cur < btrfs_item_size(eb_i, item); cur += len) {
+			name_len = btrfs_inode_ref_name_len(eb_i, iref);
+			ret = iterate(parent, iref, eb_ir, slot, ctx);
+			if (ret)
+				return ret;
+			len = sizeof(*iref) + name_len;
+			iref = (struct btrfs_inode_ref *)((char *)iref + len);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * 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,
+				struct extent_buffer *eb_ir, int slot,
+				void *ctx)
+{
+	struct inode_fs_paths *ipath = ctx;
+	struct extent_buffer *eb_i = ipath->eb;
+	u32 path_len;
+	char *fs_path;
+
+	if (ipath->bytes_left < 2)
+		return -EOVERFLOW;
+
+	*ipath->dest++ = ' ';
+	--ipath->bytes_left;
+
+	fs_path = iref_to_path(ipath->fs_root, ipath->path, iref, eb_i,
+				inum, ipath->scratch_buf, ipath->bytes_left);
+	if (IS_ERR(fs_path))
+		return PTR_ERR(fs_path);
+	path_len = ipath->scratch_buf + ipath->bytes_left - fs_path - 1;
+	if (path_len+1 > ipath->bytes_left)
+		return -EOVERFLOW;
+	memcpy(ipath->dest, fs_path, path_len+1);
+	ipath->bytes_left -= path_len;
+	ipath->dest += path_len;
+
+	return 0;
+}
+
+int paths_from_inode(u64 inum, struct inode_fs_paths *ipath)
+{
+	return iterate_irefs(inum, ipath->eb, ipath->fs_root, ipath->path,
+				inode_to_path, ipath);
+}
diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h
new file mode 100644
index 0000000..d208321
--- /dev/null
+++ b/fs/btrfs/backref.h
@@ -0,0 +1,59 @@ 
+/*
+ * Copyright (C) 2011 STRATO.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef __BTRFS_BACKREF__
+#define __BTRFS_BACKREF__
+
+struct inode_fs_paths {
+	int			bytes_left;
+	char			*dest;
+	struct btrfs_path	*path;
+	char			*scratch_buf;
+	struct btrfs_root	*fs_root;
+	int			scratch_bufsize;
+	struct extent_buffer	*eb;
+};
+
+typedef int (iterate_extent_inodes_t)(u64 inum, u64 offset, void *ctx);
+typedef int (iterate_irefs_t)(u64 parent, struct btrfs_inode_ref *iref,
+				struct extent_buffer *eb_ir,
+				int slot, void *ctx);
+
+int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root,
+			struct btrfs_path *path);
+
+int data_extent_from_logical(struct btrfs_root *root, u64 logical,
+				struct btrfs_path *path,
+				struct btrfs_key *found_key);
+
+int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
+				struct btrfs_extent_item *ei, u32 item_size,
+				u64 *out_root, u8 *out_level);
+
+int iterate_extent_inodes(struct extent_buffer *eb,
+				struct btrfs_extent_item *ei,
+				u64 extent_item_offset, u32 item_size,
+				iterate_extent_inodes_t *iterate, void *ctx);
+
+int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
+				struct btrfs_path *path,
+				iterate_extent_inodes_t *iterate, void *ctx);
+
+int paths_from_inode(u64 inum, struct inode_fs_paths *ipath);
+
+#endif