@@ -251,37 +251,45 @@ out:
int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len,
loff_t *actread)
{
- struct __btrfs_root root = btrfs_info.fs_root;
- struct btrfs_inode_item inode;
- u64 inr, rd;
+ struct btrfs_fs_info *fs_info = current_fs_info;
+ struct btrfs_root *root;
+ loff_t real_size;
+ u64 ino;
u8 type;
+ int ret;
- inr = __btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode,
- 40);
-
- if (inr == -1ULL) {
+ ASSERT(fs_info);
+ ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID,
+ file, &root, &ino, &type, 40);
+ if (ret < 0) {
printf("Cannot lookup file %s\n", file);
- return -1;
+ return ret;
}
if (type != BTRFS_FT_REG_FILE) {
printf("Not a regular file: %s\n", file);
- return -1;
+ return -EINVAL;
}
- if (!len)
- len = inode.size;
+ if (!len) {
+ ret = btrfs_size(file, &real_size);
+ if (ret < 0) {
+ printf("Failed to get inode size: %s\n", file);
+ return ret;
+ }
+ len = real_size;
+ }
- if (len > inode.size - offset)
- len = inode.size - offset;
+ if (len > real_size - offset)
+ len = real_size - offset;
- rd = __btrfs_file_read(&root, inr, offset, len, buf);
- if (rd == -1ULL) {
+ ret = btrfs_file_read(root, ino, offset, len, buf);
+ if (ret < 0) {
printf("An error occured while reading file %s\n", file);
- return -1;
+ return ret;
}
- *actread = rd;
+ *actread = len;
return 0;
}
@@ -59,6 +59,8 @@ int btrfs_readlink(struct btrfs_root *root, u64 ino, char *target);
u64 __btrfs_lookup_path(struct __btrfs_root *, u64, const char *, u8 *,
struct btrfs_inode_item *, int);
u64 __btrfs_file_read(const struct __btrfs_root *, u64, u64, u64, char *);
+int btrfs_file_read(struct btrfs_root *root, u64 ino, u64 file_offset, u64 len,
+ char *dest);
/* subvolume.c */
u64 btrfs_get_default_subvol_objectid(void);
@@ -915,3 +915,149 @@ check_next:
*next_offset = key.offset;
return 1;
}
+
+static int read_and_truncate_page(struct btrfs_path *path,
+ struct btrfs_file_extent_item *fi,
+ int start, int len, char *dest)
+{
+ struct extent_buffer *leaf = path->nodes[0];
+ struct btrfs_fs_info *fs_info = leaf->fs_info;
+ u64 aligned_start = round_down(start, fs_info->sectorsize);
+ u8 extent_type;
+ char *buf;
+ int page_off = start - aligned_start;
+ int page_len = fs_info->sectorsize - page_off;
+ int ret;
+
+ ASSERT(start + len <= aligned_start + fs_info->sectorsize);
+ buf = malloc_cache_aligned(fs_info->sectorsize);
+ if (!buf)
+ return -ENOMEM;
+
+ extent_type = btrfs_file_extent_type(leaf, fi);
+ if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = btrfs_read_extent_inline(path, fi, buf);
+ memcpy(dest, buf + page_off, min(page_len, ret));
+ free(buf);
+ return len;
+ }
+
+ ret = btrfs_read_extent_reg(path, fi,
+ round_down(start, fs_info->sectorsize),
+ fs_info->sectorsize, buf);
+ if (ret < 0) {
+ free(buf);
+ return ret;
+ }
+ memcpy(dest, buf + page_off, page_len);
+ free(buf);
+ return len;
+}
+
+int btrfs_file_read(struct btrfs_root *root, u64 ino, u64 file_offset, u64 len,
+ char *dest)
+{
+ struct btrfs_fs_info *fs_info = root->fs_info;
+ struct btrfs_file_extent_item *fi;
+ struct btrfs_path path;
+ struct btrfs_key key;
+ u64 aligned_start = round_down(file_offset, fs_info->sectorsize);
+ u64 aligned_end = round_down(file_offset + len, fs_info->sectorsize);
+ u64 next_offset;
+ u64 cur = aligned_start;
+ int ret = 0;
+
+ btrfs_init_path(&path);
+
+ /* Set the whole dest all zero, so we won't need to bother holes */
+ memset(dest, 0, len);
+
+ /* Read out the leading unaligned part */
+ if (aligned_start != file_offset) {
+ ret = lookup_data_extent(root, &path, ino, aligned_start,
+ &next_offset);
+ if (ret < 0)
+ goto out;
+ if (ret == 0) {
+ /* Read the unaligned part out*/
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_file_extent_item);
+ ret = read_and_truncate_page(&path, fi, file_offset,
+ round_up(file_offset, fs_info->sectorsize) -
+ file_offset, dest);
+ if (ret < 0)
+ goto out;
+ cur += fs_info->sectorsize;
+ } else {
+ /* The whole file is a hole */
+ if (!next_offset) {
+ memset(dest, 0, len);
+ return len;
+ }
+ cur = next_offset;
+ }
+ }
+
+ /* Read the aligned part */
+ while (cur < aligned_end) {
+ u64 extent_num_bytes;
+ u8 type;
+
+ btrfs_release_path(&path);
+ ret = lookup_data_extent(root, &path, ino, cur, &next_offset);
+ if (ret < 0)
+ goto out;
+ if (ret > 0) {
+ /* No next, direct exit */
+ if (!next_offset) {
+ ret = 0;
+ goto out;
+ }
+ }
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_file_extent_item);
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ type = btrfs_file_extent_type(path.nodes[0], fi);
+ if (type == BTRFS_FILE_EXTENT_INLINE) {
+ ret = btrfs_read_extent_inline(&path, fi, dest);
+ goto out;
+ }
+ /* Skip holes, as we have zeroed the dest */
+ if (type == BTRFS_FILE_EXTENT_PREALLOC ||
+ btrfs_file_extent_disk_bytenr(path.nodes[0], fi) == 0) {
+ cur = key.offset + btrfs_file_extent_num_bytes(
+ path.nodes[0], fi);
+ continue;
+ }
+
+ /* Read the remaining part of the extent */
+ extent_num_bytes = btrfs_file_extent_num_bytes(path.nodes[0],
+ fi);
+ ret = btrfs_read_extent_reg(&path, fi, cur,
+ min(extent_num_bytes, aligned_end - cur),
+ dest + cur - file_offset);
+ if (ret < 0)
+ goto out;
+ cur += min(extent_num_bytes, aligned_end - cur);
+ }
+
+ /* Read the tailing unaligned part*/
+ if (file_offset + len != aligned_end) {
+ btrfs_release_path(&path);
+ ret = lookup_data_extent(root, &path, ino, aligned_end,
+ &next_offset);
+ /* <0 is error, >0 means no extent */
+ if (ret)
+ goto out;
+ fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+ struct btrfs_file_extent_item);
+ ret = read_and_truncate_page(&path, fi, aligned_end,
+ file_offset + len - aligned_end,
+ dest + aligned_end - file_offset);
+ }
+out:
+ btrfs_release_path(&path);
+ if (ret < 0)
+ return ret;
+ return len;
+}
This version of btrfs_file_read() will have the following new features: - Try all mirrors - More hanlding on unaligned size - Better compressed extent handling The old implementation doesn't handle compressed extent with offset properly. As we need to read out the whole compressed extent, then decompress the whole extent, then only copy the referred part. Signed-off-by: Qu Wenruo <wqu at suse.com> --- fs/btrfs/btrfs.c | 42 ++++++++------ fs/btrfs/btrfs.h | 2 + fs/btrfs/inode.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 17 deletions(-)