Whamcloud - gitweb
tune2fs: allow tune2fs to be built as a static library for Android
[tools/e2fsprogs.git] / lib / ext2fs / block.c
index fc76ff2..601129d 100644 (file)
@@ -4,11 +4,12 @@
  * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
  *
  * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
  * %End-Header%
  */
 
+#include "config.h"
 #include <stdio.h>
 #include <string.h>
 #if HAVE_UNISTD_H
@@ -78,7 +79,7 @@ static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
                ctx->bcount += limit;
                return ret;
        }
-       if (*ind_block >= ctx->fs->super->s_blocks_count ||
+       if (*ind_block >= ext2fs_blocks_count(ctx->fs->super) ||
            *ind_block < ctx->fs->super->s_first_data_block) {
                ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
                ret |= BLOCK_ERROR;
@@ -110,7 +111,7 @@ static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
        } else {
                for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
                        if (*block_nr == 0)
-                               continue;
+                               goto skip_sparse;
                        blk64 = *block_nr;
                        flags = (*ctx->func)(ctx->fs, &blk64, ctx->bcount,
                                             *ind_block, offset,
@@ -121,6 +122,7 @@ static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
                                ret |= BLOCK_ABORT;
                                break;
                        }
+               skip_sparse:
                        offset += sizeof(blk_t);
                }
        }
@@ -166,7 +168,7 @@ static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
                ctx->bcount += limit*limit;
                return ret;
        }
-       if (*dind_block >= ctx->fs->super->s_blocks_count ||
+       if (*dind_block >= ext2fs_blocks_count(ctx->fs->super) ||
            *dind_block < ctx->fs->super->s_first_data_block) {
                ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
                ret |= BLOCK_ERROR;
@@ -252,7 +254,7 @@ static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
                ctx->bcount += limit*limit*limit;
                return ret;
        }
-       if (*tind_block >= ctx->fs->super->s_blocks_count ||
+       if (*tind_block >= ext2fs_blocks_count(ctx->fs->super) ||
            *tind_block < ctx->fs->super->s_first_data_block) {
                ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
                ret |= BLOCK_ERROR;
@@ -343,6 +345,13 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
                return ctx.errcode;
 
        /*
+        * An inode with inline data has no blocks over which to
+        * iterate, so return an error code indicating this fact.
+        */
+       if (inode.i_flags & EXT4_INLINE_DATA_FL)
+               return EXT2_ET_INLINE_DATA_CANT_ITERATE;
+
+       /*
         * Check to see if we need to limit large files
         */
        if (flags & BLOCK_FLAG_NO_LARGE) {
@@ -387,7 +396,7 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
 
        if (inode.i_flags & EXT4_EXTENTS_FL) {
                ext2_extent_handle_t    handle;
-               struct ext2fs_extent    extent;
+               struct ext2fs_extent    extent, next;
                e2_blkcnt_t             blockcnt = 0;
                blk64_t                 blk, new_blk;
                int                     op = EXT2_EXTENT_ROOT;
@@ -399,19 +408,24 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
                        goto abort_exit;
 
                while (1) {
-                       ctx.errcode = ext2fs_extent_get(handle, op, &extent);
+                       if (op == EXT2_EXTENT_CURRENT)
+                               ctx.errcode = 0;
+                       else
+                               ctx.errcode = ext2fs_extent_get(handle, op,
+                                                               &extent);
                        if (ctx.errcode) {
                                if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT)
                                        break;
                                ctx.errcode = 0;
                                if (!(flags & BLOCK_FLAG_APPEND))
                                        break;
+                       next_block_set:
                                blk = 0;
                                r = (*ctx.func)(fs, &blk, blockcnt,
                                                0, 0, priv_data);
                                ret |= r;
                                check_for_ro_violation_goto(&ctx, ret,
-                                                           extent_errout);
+                                                           extent_done);
                                if (r & BLOCK_CHANGED) {
                                        ctx.errcode =
                                                ext2fs_extent_set_bmap(handle,
@@ -419,7 +433,8 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
                                                       (blk64_t) blk, 0);
                                        if (ctx.errcode || (ret & BLOCK_ABORT))
                                                break;
-                                       continue;
+                                       if (blk)
+                                               goto next_block_set;
                                }
                                break;
                        }
@@ -444,12 +459,32 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
                                                if (ctx.errcode)
                                                        break;
                                        }
+                                       if (ret & BLOCK_ABORT)
+                                               break;
                                }
                                continue;
                        }
                        uninit = 0;
                        if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
                                uninit = EXT2_EXTENT_SET_BMAP_UNINIT;
+
+                       /* 
+                        * Get the next extent before we start messing
+                        * with the current extent
+                        */
+                       retval = ext2fs_extent_get(handle, op, &next);
+
+#if 0
+                       printf("lblk %llu pblk %llu len %d blockcnt %llu\n",
+                              extent.e_lblk, extent.e_pblk,
+                              extent.e_len, blockcnt);
+#endif
+                       if (extent.e_lblk + extent.e_len <= (blk64_t) blockcnt)
+                               continue;
+                       if (extent.e_lblk > (blk64_t) blockcnt)
+                               blockcnt = extent.e_lblk;
+                       j = blockcnt - extent.e_lblk;
+                       blk += j;
                        for (blockcnt = extent.e_lblk, j = 0;
                             j < extent.e_len;
                             blk++, blockcnt++, j++) {
@@ -458,23 +493,27 @@ errcode_t ext2fs_block_iterate3(ext2_filsys fs,
                                                0, 0, priv_data);
                                ret |= r;
                                check_for_ro_violation_goto(&ctx, ret,
-                                                           extent_errout);
+                                                           extent_done);
                                if (r & BLOCK_CHANGED) {
                                        ctx.errcode =
                                                ext2fs_extent_set_bmap(handle,
                                                       (blk64_t) blockcnt,
                                                       new_blk, uninit);
                                        if (ctx.errcode)
-                                               goto extent_errout;
+                                               goto extent_done;
                                }
                                if (ret & BLOCK_ABORT)
-                                       break;
+                                       goto extent_done;
+                       }
+                       if (retval == 0) {
+                               extent = next;
+                               op = EXT2_EXTENT_CURRENT;
                        }
                }
 
-       extent_errout:
+       extent_done:
                ext2fs_extent_free(handle);
-               ret |= BLOCK_ERROR | BLOCK_ABORT;
+               ret |= BLOCK_ERROR; /* ctx.errcode is always valid here */
                goto errout;
        }