diff options
| author | Mark Fasheh <mfasheh@suse.de> | 2012-08-08 14:33:54 -0400 |
|---|---|---|
| committer | Chris Mason <chris.mason@fusionio.com> | 2012-10-09 09:15:01 -0400 |
| commit | d24bec3ae528a47149b838aad76c006d40fe8a39 (patch) | |
| tree | 5c89eba867198cfd7c04e2e1ed978415aa7a3023 /fs/btrfs | |
| parent | f186373fef005cee948a4a39e6a14c2e5f517298 (diff) | |
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: Mark Fasheh <mfasheh@suse.de>
Diffstat (limited to 'fs/btrfs')
| -rw-r--r-- | fs/btrfs/backref.c | 173 | ||||
| -rw-r--r-- | fs/btrfs/backref.h | 2 |
2 files changed, 138 insertions, 37 deletions
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index dc963121d1db..f3187938e081 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c | |||
| @@ -1177,26 +1177,12 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid, | |||
| 1177 | return ret; | 1177 | return ret; |
| 1178 | } | 1178 | } |
| 1179 | 1179 | ||
| 1180 | /* | 1180 | static char *ref_to_path(struct btrfs_root *fs_root, |
| 1181 | * this iterates to turn a btrfs_inode_ref into a full filesystem path. elements | 1181 | struct btrfs_path *path, |
| 1182 | * of the path are separated by '/' and the path is guaranteed to be | 1182 | u32 name_len, unsigned long name_off, |
| 1183 | * 0-terminated. the path is only given within the current file system. | ||
| 1184 | * Therefore, it never starts with a '/'. the caller is responsible to provide | ||
| 1185 | * "size" bytes in "dest". the dest buffer will be filled backwards. finally, | ||
| 1186 | * the start point of the resulting string is returned. this pointer is within | ||
| 1187 | * dest, normally. | ||
| 1188 | * in case the path buffer would overflow, the pointer is decremented further | ||
| 1189 | * as if output was written to the buffer, though no more output is actually | ||
| 1190 | * generated. that way, the caller can determine how much space would be | ||
| 1191 | * required for the path to fit into the buffer. in that case, the returned | ||
| 1192 | * value will be smaller than dest. callers must check this! | ||
| 1193 | */ | ||
| 1194 | char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, | ||
| 1195 | struct btrfs_inode_ref *iref, | ||
| 1196 | struct extent_buffer *eb_in, u64 parent, | 1183 | struct extent_buffer *eb_in, u64 parent, |
| 1197 | char *dest, u32 size) | 1184 | char *dest, u32 size) |
| 1198 | { | 1185 | { |
| 1199 | u32 len; | ||
| 1200 | int slot; | 1186 | int slot; |
| 1201 | u64 next_inum; | 1187 | u64 next_inum; |
| 1202 | int ret; | 1188 | int ret; |
| @@ -1204,17 +1190,17 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, | |||
| 1204 | struct extent_buffer *eb = eb_in; | 1190 | struct extent_buffer *eb = eb_in; |
| 1205 | struct btrfs_key found_key; | 1191 | struct btrfs_key found_key; |
| 1206 | int leave_spinning = path->leave_spinning; | 1192 | int leave_spinning = path->leave_spinning; |
| 1193 | struct btrfs_inode_ref *iref; | ||
| 1207 | 1194 | ||
| 1208 | if (bytes_left >= 0) | 1195 | if (bytes_left >= 0) |
| 1209 | dest[bytes_left] = '\0'; | 1196 | dest[bytes_left] = '\0'; |
| 1210 | 1197 | ||
| 1211 | path->leave_spinning = 1; | 1198 | path->leave_spinning = 1; |
| 1212 | while (1) { | 1199 | while (1) { |
| 1213 | len = btrfs_inode_ref_name_len(eb, iref); | 1200 | bytes_left -= name_len; |
| 1214 | bytes_left -= len; | ||
| 1215 | if (bytes_left >= 0) | 1201 | if (bytes_left >= 0) |
| 1216 | read_extent_buffer(eb, dest + bytes_left, | 1202 | read_extent_buffer(eb, dest + bytes_left, |
| 1217 | (unsigned long)(iref + 1), len); | 1203 | name_off, name_len); |
| 1218 | if (eb != eb_in) { | 1204 | if (eb != eb_in) { |
| 1219 | btrfs_tree_read_unlock_blocking(eb); | 1205 | btrfs_tree_read_unlock_blocking(eb); |
| 1220 | free_extent_buffer(eb); | 1206 | free_extent_buffer(eb); |
| @@ -1224,6 +1210,7 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, | |||
| 1224 | ret = -ENOENT; | 1210 | ret = -ENOENT; |
| 1225 | if (ret) | 1211 | if (ret) |
| 1226 | break; | 1212 | break; |
| 1213 | |||
| 1227 | next_inum = found_key.offset; | 1214 | next_inum = found_key.offset; |
| 1228 | 1215 | ||
| 1229 | /* regular exit ahead */ | 1216 | /* regular exit ahead */ |
| @@ -1239,8 +1226,11 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, | |||
| 1239 | btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); | 1226 | btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); |
| 1240 | } | 1227 | } |
| 1241 | btrfs_release_path(path); | 1228 | btrfs_release_path(path); |
| 1242 | |||
| 1243 | iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); | 1229 | iref = btrfs_item_ptr(eb, slot, struct btrfs_inode_ref); |
| 1230 | |||
| 1231 | name_len = btrfs_inode_ref_name_len(eb, iref); | ||
| 1232 | name_off = (unsigned long)(iref + 1); | ||
| 1233 | |||
| 1244 | parent = next_inum; | 1234 | parent = next_inum; |
| 1245 | --bytes_left; | 1235 | --bytes_left; |
| 1246 | if (bytes_left >= 0) | 1236 | if (bytes_left >= 0) |
| @@ -1257,6 +1247,32 @@ char *btrfs_iref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path, | |||
| 1257 | } | 1247 | } |
| 1258 | 1248 | ||
| 1259 | /* | 1249 | /* |
| 1250 | * this iterates to turn a btrfs_inode_ref into a full filesystem path. elements | ||
| 1251 | * of the path are separated by '/' and the path is guaranteed to be | ||
| 1252 | * 0-terminated. the path is only given within the current file system. | ||
| 1253 | * Therefore, it never starts with a '/'. the caller is responsible to provide | ||
| 1254 | * "size" bytes in "dest". the dest buffer will be filled backwards. finally, | ||
| 1255 | * the start point of the resulting string is returned. this pointer is within | ||
| 1256 | * dest, normally. | ||
| 1257 | * in case the path buffer would overflow, the pointer is decremented further | ||
| 1258 | * as if output was written to the buffer, though no more output is actually | ||
| 1259 | * generated. that way, the caller can determine how much space would be | ||
| 1260 | * required for the path to fit into the buffer. in that case, the returned | ||
| 1261 | * value will be smaller than dest. callers must check this! | ||
| 1262 | */ | ||
| 1263 | char *btrfs_iref_to_path(struct btrfs_root *fs_root, | ||
| 1264 | struct btrfs_path *path, | ||
| 1265 | struct btrfs_inode_ref *iref, | ||
| 1266 | struct extent_buffer *eb_in, u64 parent, | ||
| 1267 | char *dest, u32 size) | ||
| 1268 | { | ||
| 1269 | return ref_to_path(fs_root, path, | ||
| 1270 | btrfs_inode_ref_name_len(eb_in, iref), | ||
| 1271 | (unsigned long)(iref + 1), | ||
| 1272 | eb_in, parent, dest, size); | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | /* | ||
| 1260 | * this makes the path point to (logical EXTENT_ITEM *) | 1276 | * this makes the path point to (logical EXTENT_ITEM *) |
| 1261 | * returns BTRFS_EXTENT_FLAG_DATA for data, BTRFS_EXTENT_FLAG_TREE_BLOCK for | 1277 | * returns BTRFS_EXTENT_FLAG_DATA for data, BTRFS_EXTENT_FLAG_TREE_BLOCK for |
| 1262 | * tree blocks and <0 on error. | 1278 | * tree blocks and <0 on error. |
| @@ -1529,9 +1545,12 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info, | |||
| 1529 | return ret; | 1545 | return ret; |
| 1530 | } | 1546 | } |
| 1531 | 1547 | ||
| 1532 | static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, | 1548 | typedef int (iterate_irefs_t)(u64 parent, u32 name_len, unsigned long name_off, |
| 1533 | struct btrfs_path *path, | 1549 | struct extent_buffer *eb, void *ctx); |
| 1534 | iterate_irefs_t *iterate, void *ctx) | 1550 | |
| 1551 | static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root, | ||
| 1552 | struct btrfs_path *path, | ||
| 1553 | iterate_irefs_t *iterate, void *ctx) | ||
| 1535 | { | 1554 | { |
| 1536 | int ret = 0; | 1555 | int ret = 0; |
| 1537 | int slot; | 1556 | int slot; |
| @@ -1548,7 +1567,7 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, | |||
| 1548 | while (!ret) { | 1567 | while (!ret) { |
| 1549 | path->leave_spinning = 1; | 1568 | path->leave_spinning = 1; |
| 1550 | ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path, | 1569 | ret = inode_ref_info(inum, parent ? parent+1 : 0, fs_root, path, |
| 1551 | &found_key); | 1570 | &found_key); |
| 1552 | if (ret < 0) | 1571 | if (ret < 0) |
| 1553 | break; | 1572 | break; |
| 1554 | if (ret) { | 1573 | if (ret) { |
| @@ -1576,7 +1595,8 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, | |||
| 1576 | "tree %llu\n", cur, | 1595 | "tree %llu\n", cur, |
| 1577 | (unsigned long long)found_key.objectid, | 1596 | (unsigned long long)found_key.objectid, |
| 1578 | (unsigned long long)fs_root->objectid); | 1597 | (unsigned long long)fs_root->objectid); |
| 1579 | ret = iterate(parent, iref, eb, ctx); | 1598 | ret = iterate(parent, name_len, |
| 1599 | (unsigned long)(iref + 1), eb, ctx); | ||
| 1580 | if (ret) | 1600 | if (ret) |
| 1581 | break; | 1601 | break; |
| 1582 | len = sizeof(*iref) + name_len; | 1602 | len = sizeof(*iref) + name_len; |
| @@ -1591,12 +1611,98 @@ static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, | |||
| 1591 | return ret; | 1611 | return ret; |
| 1592 | } | 1612 | } |
| 1593 | 1613 | ||
| 1614 | static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root, | ||
| 1615 | struct btrfs_path *path, | ||
| 1616 | iterate_irefs_t *iterate, void *ctx) | ||
| 1617 | { | ||
| 1618 | int ret; | ||
| 1619 | int slot; | ||
| 1620 | u64 offset = 0; | ||
| 1621 | u64 parent; | ||
| 1622 | int found = 0; | ||
| 1623 | struct extent_buffer *eb; | ||
| 1624 | struct btrfs_inode_extref *extref; | ||
| 1625 | struct extent_buffer *leaf; | ||
| 1626 | u32 item_size; | ||
| 1627 | u32 cur_offset; | ||
| 1628 | unsigned long ptr; | ||
| 1629 | |||
| 1630 | while (1) { | ||
| 1631 | ret = btrfs_find_one_extref(fs_root, inum, offset, path, &extref, | ||
| 1632 | &offset); | ||
| 1633 | if (ret < 0) | ||
| 1634 | break; | ||
| 1635 | if (ret) { | ||
| 1636 | ret = found ? 0 : -ENOENT; | ||
| 1637 | break; | ||
| 1638 | } | ||
| 1639 | ++found; | ||
| 1640 | |||
| 1641 | slot = path->slots[0]; | ||
| 1642 | eb = path->nodes[0]; | ||
| 1643 | /* make sure we can use eb after releasing the path */ | ||
| 1644 | atomic_inc(&eb->refs); | ||
| 1645 | |||
| 1646 | btrfs_tree_read_lock(eb); | ||
| 1647 | btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); | ||
| 1648 | btrfs_release_path(path); | ||
| 1649 | |||
| 1650 | leaf = path->nodes[0]; | ||
| 1651 | item_size = btrfs_item_size_nr(leaf, path->slots[0]); | ||
| 1652 | ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); | ||
| 1653 | cur_offset = 0; | ||
| 1654 | |||
| 1655 | while (cur_offset < item_size) { | ||
| 1656 | u32 name_len; | ||
| 1657 | |||
| 1658 | extref = (struct btrfs_inode_extref *)(ptr + cur_offset); | ||
| 1659 | parent = btrfs_inode_extref_parent(eb, extref); | ||
| 1660 | name_len = btrfs_inode_extref_name_len(eb, extref); | ||
| 1661 | ret = iterate(parent, name_len, | ||
| 1662 | (unsigned long)&extref->name, eb, ctx); | ||
| 1663 | if (ret) | ||
| 1664 | break; | ||
| 1665 | |||
| 1666 | cur_offset += btrfs_inode_extref_name_len(leaf, extref); | ||
| 1667 | cur_offset += sizeof(*extref); | ||
| 1668 | } | ||
| 1669 | btrfs_tree_read_unlock_blocking(eb); | ||
| 1670 | free_extent_buffer(eb); | ||
| 1671 | |||
| 1672 | offset++; | ||
| 1673 | } | ||
| 1674 | |||
| 1675 | btrfs_release_path(path); | ||
| 1676 | |||
| 1677 | return ret; | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | static int iterate_irefs(u64 inum, struct btrfs_root *fs_root, | ||
| 1681 | struct btrfs_path *path, iterate_irefs_t *iterate, | ||
| 1682 | void *ctx) | ||
| 1683 | { | ||
| 1684 | int ret; | ||
| 1685 | int found_refs = 0; | ||
| 1686 | |||
| 1687 | ret = iterate_inode_refs(inum, fs_root, path, iterate, ctx); | ||
| 1688 | if (!ret) | ||
| 1689 | ++found_refs; | ||
| 1690 | else if (ret != -ENOENT) | ||
| 1691 | return ret; | ||
| 1692 | |||
| 1693 | ret = iterate_inode_extrefs(inum, fs_root, path, iterate, ctx); | ||
| 1694 | if (ret == -ENOENT && found_refs) | ||
| 1695 | return 0; | ||
| 1696 | |||
| 1697 | return ret; | ||
| 1698 | } | ||
| 1699 | |||
| 1594 | /* | 1700 | /* |
| 1595 | * returns 0 if the path could be dumped (probably truncated) | 1701 | * returns 0 if the path could be dumped (probably truncated) |
| 1596 | * returns <0 in case of an error | 1702 | * returns <0 in case of an error |
| 1597 | */ | 1703 | */ |
| 1598 | static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref, | 1704 | static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off, |
| 1599 | struct extent_buffer *eb, void *ctx) | 1705 | struct extent_buffer *eb, void *ctx) |
| 1600 | { | 1706 | { |
| 1601 | struct inode_fs_paths *ipath = ctx; | 1707 | struct inode_fs_paths *ipath = ctx; |
| 1602 | char *fspath; | 1708 | char *fspath; |
| @@ -1609,20 +1715,17 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref, | |||
| 1609 | ipath->fspath->bytes_left - s_ptr : 0; | 1715 | ipath->fspath->bytes_left - s_ptr : 0; |
| 1610 | 1716 | ||
| 1611 | fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr; | 1717 | fspath_min = (char *)ipath->fspath->val + (i + 1) * s_ptr; |
| 1612 | fspath = btrfs_iref_to_path(ipath->fs_root, ipath->btrfs_path, iref, eb, | 1718 | fspath = ref_to_path(ipath->fs_root, ipath->btrfs_path, name_len, |
| 1613 | inum, fspath_min, bytes_left); | 1719 | name_off, eb, inum, fspath_min, |
| 1720 | bytes_left); | ||
| 1614 | if (IS_ERR(fspath)) | 1721 | if (IS_ERR(fspath)) |
| 1615 | return PTR_ERR(fspath); | 1722 | return PTR_ERR(fspath); |
| 1616 | 1723 | ||
| 1617 | if (fspath > fspath_min) { | 1724 | if (fspath > fspath_min) { |
| 1618 | pr_debug("path resolved: %s\n", fspath); | ||
| 1619 | ipath->fspath->val[i] = (u64)(unsigned long)fspath; | 1725 | ipath->fspath->val[i] = (u64)(unsigned long)fspath; |
| 1620 | ++ipath->fspath->elem_cnt; | 1726 | ++ipath->fspath->elem_cnt; |
| 1621 | ipath->fspath->bytes_left = fspath - fspath_min; | 1727 | ipath->fspath->bytes_left = fspath - fspath_min; |
| 1622 | } else { | 1728 | } else { |
| 1623 | pr_debug("missed path, not enough space. missing bytes: %lu, " | ||
| 1624 | "constructed so far: %s\n", | ||
| 1625 | (unsigned long)(fspath_min - fspath), fspath_min); | ||
| 1626 | ++ipath->fspath->elem_missed; | 1729 | ++ipath->fspath->elem_missed; |
| 1627 | ipath->fspath->bytes_missing += fspath_min - fspath; | 1730 | ipath->fspath->bytes_missing += fspath_min - fspath; |
| 1628 | ipath->fspath->bytes_left = 0; | 1731 | ipath->fspath->bytes_left = 0; |
| @@ -1644,7 +1747,7 @@ static int inode_to_path(u64 inum, struct btrfs_inode_ref *iref, | |||
| 1644 | int paths_from_inode(u64 inum, struct inode_fs_paths *ipath) | 1747 | int paths_from_inode(u64 inum, struct inode_fs_paths *ipath) |
| 1645 | { | 1748 | { |
| 1646 | return iterate_irefs(inum, ipath->fs_root, ipath->btrfs_path, | 1749 | return iterate_irefs(inum, ipath->fs_root, ipath->btrfs_path, |
| 1647 | inode_to_path, ipath); | 1750 | inode_to_path, ipath); |
| 1648 | } | 1751 | } |
| 1649 | 1752 | ||
| 1650 | struct btrfs_data_container *init_data_container(u32 total_bytes) | 1753 | struct btrfs_data_container *init_data_container(u32 total_bytes) |
diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h index 0b920c113952..e75533043a5f 100644 --- a/fs/btrfs/backref.h +++ b/fs/btrfs/backref.h | |||
| @@ -33,8 +33,6 @@ struct inode_fs_paths { | |||
| 33 | 33 | ||
| 34 | typedef int (iterate_extent_inodes_t)(u64 inum, u64 offset, u64 root, | 34 | typedef int (iterate_extent_inodes_t)(u64 inum, u64 offset, u64 root, |
| 35 | void *ctx); | 35 | void *ctx); |
| 36 | typedef int (iterate_irefs_t)(u64 parent, struct btrfs_inode_ref *iref, | ||
| 37 | struct extent_buffer *eb, void *ctx); | ||
| 38 | 36 | ||
| 39 | int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root, | 37 | int inode_item_info(u64 inum, u64 ioff, struct btrfs_root *fs_root, |
| 40 | struct btrfs_path *path); | 38 | struct btrfs_path *path); |
