Imported Upstream version 1.47.0
[platform/upstream/e2fsprogs.git] / lib / ext2fs / extent.c
index a9cdae7..82e75cc 100644 (file)
@@ -47,6 +47,7 @@ struct extent_path {
        int             visit_num;
        int             flags;
        blk64_t         end_blk;
+       blk64_t         blk;
        void            *curr;
 };
 
@@ -286,6 +287,7 @@ errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino,
        handle->path[0].end_blk =
                (EXT2_I_SIZE(handle->inode) + fs->blocksize - 1) >>
                 EXT2_BLOCK_SIZE_BITS(fs->super);
+       handle->path[0].blk = 0;
        handle->path[0].visit_num = 1;
        handle->level = 0;
        handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE;
@@ -305,14 +307,14 @@ errout:
 errcode_t ext2fs_extent_get(ext2_extent_handle_t handle,
                            int flags, struct ext2fs_extent *extent)
 {
-       struct extent_path      *path, *newpath;
+       struct extent_path      *path, *newpath, *tp;
        struct ext3_extent_header       *eh;
        struct ext3_extent_idx          *ix = 0;
        struct ext3_extent              *ex;
        errcode_t                       retval;
        blk64_t                         blk;
        blk64_t                         end_blk;
-       int                             orig_op, op;
+       int                             orig_op, op, l;
        int                             failed_csum = 0;
 
        EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE);
@@ -467,6 +469,11 @@ retry:
                }
                blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
                        ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
+               for (l = handle->level, tp = path; l > 0; l--, tp--) {
+                       if (blk == tp->blk)
+                               return EXT2_ET_EXTENT_CYCLE;
+               }
+               newpath->blk = blk;
                if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) &&
                    (handle->fs->io != handle->fs->image_io))
                        memset(newpath->buf, 0, handle->fs->blocksize);
@@ -495,6 +502,10 @@ retry:
                        ext2fs_le16_to_cpu(eh->eh_entries);
                newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max);
 
+               /* Make sure there is at least one extent present */
+               if (newpath->left <= 0)
+                       return EXT2_ET_EXTENT_NO_DOWN;
+
                if (path->left > 0) {
                        ix++;
                        newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block);
@@ -1630,6 +1641,10 @@ errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags)
 
        cp = path->curr;
 
+       /* Sanity check before memmove() */
+       if (path->left < 0)
+               return EXT2_ET_EXTENT_LEAF_BAD;
+
        if (path->left) {
                memmove(cp, cp + sizeof(struct ext3_extent_idx),
                        path->left * sizeof(struct ext3_extent_idx));
@@ -1737,6 +1752,121 @@ size_t ext2fs_max_extent_depth(ext2_extent_handle_t handle)
        return last_result;
 }
 
+errcode_t ext2fs_fix_extents_checksums(ext2_filsys fs, ext2_ino_t ino,
+                                      struct ext2_inode *inode)
+{
+       ext2_extent_handle_t    handle;
+       struct ext2fs_extent    extent;
+       errcode_t               errcode;
+       int                     save_flags = fs->flags;
+
+       if (!ext2fs_has_feature_metadata_csum(fs->super) ||
+           (inode && !(inode->i_flags & EXT4_EXTENTS_FL)))
+               return 0;
+
+       errcode = ext2fs_extent_open2(fs, ino, inode, &handle);
+       if (errcode) {
+               if (errcode == EXT2_ET_INODE_NOT_EXTENT)
+                       errcode = 0;
+               return errcode;
+       }
+
+       fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+       errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
+       if (errcode)
+               goto out;
+
+       do {
+               /* Skip to the end of a block of leaf nodes */
+               if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
+                       errcode = ext2fs_extent_get(handle,
+                                                   EXT2_EXTENT_LAST_SIB,
+                                                   &extent);
+                       if (errcode)
+                               break;
+               }
+
+               errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
+               if (errcode == EXT2_ET_EXTENT_CSUM_INVALID)
+                       errcode = update_path(handle);
+       } while (errcode == 0);
+
+out:
+       /* Ok if we run off the end */
+       if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+               errcode = 0;
+       ext2fs_extent_free(handle);
+       fs->flags = save_flags;
+       return errcode;
+}
+
+errcode_t ext2fs_decode_extent(struct ext2fs_extent *to, void *addr, int len)
+{
+       struct ext3_extent *from = (struct ext3_extent *)addr;
+
+       if (len != sizeof(struct ext3_extent))
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       to->e_pblk = ext2fs_le32_to_cpu(from->ee_start) +
+               ((__u64) ext2fs_le16_to_cpu(from->ee_start_hi)
+                       << 32);
+       to->e_lblk = ext2fs_le32_to_cpu(from->ee_block);
+       to->e_len = ext2fs_le16_to_cpu(from->ee_len);
+       to->e_flags = EXT2_EXTENT_FLAGS_LEAF;
+       if (to->e_len > EXT_INIT_MAX_LEN) {
+               to->e_len -= EXT_INIT_MAX_LEN;
+               to->e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
+       }
+
+       return 0;
+}
+
+errcode_t ext2fs_count_blocks(ext2_filsys fs, ext2_ino_t ino,
+                             struct ext2_inode *inode, blk64_t *ret_count)
+{
+       ext2_extent_handle_t    handle = NULL;
+       struct ext2fs_extent    extent;
+       errcode_t               errcode;
+       int                     i;
+       blk64_t                 blkcount = 0;
+       blk64_t                 *intermediate_nodes;
+
+       errcode = ext2fs_extent_open2(fs, ino, inode, &handle);
+       if (errcode)
+               goto out;
+
+       errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
+       if (errcode)
+               goto out;
+
+       errcode = ext2fs_get_array(handle->max_depth, sizeof(blk64_t),
+                                  &intermediate_nodes);
+       if (errcode)
+               goto out;
+
+       blkcount = handle->level;
+       while (!errcode) {
+               if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
+                       blkcount += extent.e_len;
+                       for (i = 0; i < handle->level; i++) {
+                               if (intermediate_nodes[i] !=
+                                       handle->path[i].end_blk) {
+                                       blkcount++;
+                                       intermediate_nodes[i] =
+                                               handle->path[i].end_blk;
+                               }
+                       }
+               }
+               errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
+       }
+       ext2fs_free_mem(&intermediate_nodes);
+out:
+       *ret_count = blkcount;
+       ext2fs_extent_free(handle);
+
+       return 0;
+}
+
 #ifdef DEBUG
 /*
  * Override debugfs's prompt