fs: btrfs: Introduce lookup_data_extent() for later use
authorQu Wenruo <wqu@suse.com>
Wed, 24 Jun 2020 16:03:10 +0000 (18:03 +0200)
committerTom Rini <trini@konsulko.com>
Tue, 8 Sep 2020 00:57:27 +0000 (20:57 -0400)
This implements lookup_data_extent() function for the incoming
new implementation of btrfs_file_read().

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Marek BehĂșn <marek.behun@nic.cz>
fs/btrfs/inode.c

index ab45db8..11eb30c 100644 (file)
@@ -825,3 +825,104 @@ out:
        free(dbuf);
        return ret;
 }
+
+/*
+ * Get the first file extent that covers bytenr @file_offset.
+ *
+ * @file_offset must be aligned to sectorsize.
+ *
+ * return 0 for found, and path points to the file extent.
+ * return >0 for not found, and fill @next_offset.
+ * @next_offset can be 0 if there is no next file extent.
+ * return <0 for error.
+ */
+static int lookup_data_extent(struct btrfs_root *root, struct btrfs_path *path,
+                             u64 ino, u64 file_offset, u64 *next_offset)
+{
+       struct btrfs_key key;
+       struct btrfs_file_extent_item *fi;
+       u8 extent_type;
+       int ret = 0;
+
+       ASSERT(IS_ALIGNED(file_offset, root->fs_info->sectorsize));
+       key.objectid = ino;
+       key.type = BTRFS_EXTENT_DATA_KEY;
+       key.offset = file_offset;
+
+       ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+       /* Error or we're already at the file extent */
+       if (ret <= 0)
+               return ret;
+       if (ret > 0) {
+               /* Check previous file extent */
+               ret = btrfs_previous_item(root, path, ino,
+                                         BTRFS_EXTENT_DATA_KEY);
+               if (ret < 0)
+                       return ret;
+               if (ret > 0)
+                       goto check_next;
+       }
+       /* Now the key.offset must be smaller than @file_offset */
+       btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+       if (key.objectid != ino ||
+           key.type != BTRFS_EXTENT_DATA_KEY)
+               goto check_next;
+
+       fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+                           struct btrfs_file_extent_item);
+       extent_type = btrfs_file_extent_type(path->nodes[0], fi);
+       if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+               if (file_offset == 0)
+                       return 0;
+               /* Inline extent should be the only extent, no next extent. */
+               *next_offset = 0;
+               return 1;
+       }
+
+       /* This file extent covers @file_offset */
+       if (key.offset <= file_offset && key.offset +
+           btrfs_file_extent_num_bytes(path->nodes[0], fi) > file_offset)
+               return 0;
+check_next:
+       ret = btrfs_next_item(root, path);
+       if (ret < 0)
+               return ret;
+       if (ret > 0) {
+               *next_offset = 0;
+               return 1;
+       }
+
+       btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+       fi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+                           struct btrfs_file_extent_item);
+       /* Next next data extent */
+       if (key.objectid != ino ||
+           key.type != BTRFS_EXTENT_DATA_KEY) {
+               *next_offset = 0;
+               return 1;
+       }
+       /* Current file extent already beyond @file_offset */
+       if (key.offset > file_offset) {
+               *next_offset = key.offset;
+               return 1;
+       }
+       /* This file extent covers @file_offset */
+       if (key.offset <= file_offset && key.offset +
+           btrfs_file_extent_num_bytes(path->nodes[0], fi) > file_offset)
+               return 0;
+       /* This file extent ends before @file_offset, check next */
+       ret = btrfs_next_item(root, path);
+       if (ret < 0)
+               return ret;
+       if (ret > 0) {
+               *next_offset = 0;
+               return 1;
+       }
+       btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+       if (key.type != BTRFS_EXTENT_DATA_KEY || key.objectid != ino) {
+               *next_offset = 0;
+               return 1;
+       }
+       *next_offset = key.offset;
+       return 1;
+}