Logo Search packages:      
Sourcecode: ldiskfsprogs version File versions  Download package

block.c

/*
 * block.c --- iterate over all blocks in an inode
 * 
 * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
 *
 * %Begin-Header%
 * This file may be redistributed under the terms of the GNU Public
 * License.
 * %End-Header%
 */

#include <stdio.h>
#include <string.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "ext2_fs.h"
#include "ext2fs.h"

struct block_context {
      ext2_filsys fs;
      int (*func)(ext2_filsys fs,
                blk_t   *blocknr,
                e2_blkcnt_t   bcount,
                blk_t   ref_blk,
                int           ref_offset,
                void    *priv_data);
      e2_blkcnt_t bcount;
      int         bsize;
      int         flags;
      errcode_t   errcode;
      char  *ind_buf;
      char  *dind_buf;
      char  *tind_buf;
      void  *priv_data;
};

static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
                       int ref_offset, struct block_context *ctx)
{
      int   ret = 0, changed = 0;
      int   i, flags, limit, offset;
      blk_t *block_nr;

      limit = ctx->fs->blocksize >> 2;
      if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
          !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
            ret = (*ctx->func)(ctx->fs, ind_block,
                           BLOCK_COUNT_IND, ref_block,
                           ref_offset, ctx->priv_data);
      if (!*ind_block || (ret & BLOCK_ABORT)) {
            ctx->bcount += limit;
            return ret;
      }
      if (*ind_block >= ctx->fs->super->s_blocks_count ||
          *ind_block < ctx->fs->super->s_first_data_block) {
            ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
            ret |= BLOCK_ERROR;
            return ret;
      }
      ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, 
                                   ctx->ind_buf);
      if (ctx->errcode) {
            ret |= BLOCK_ERROR;
            return ret;
      }

      block_nr = (blk_t *) ctx->ind_buf;
      offset = 0;
      if (ctx->flags & BLOCK_FLAG_APPEND) {
            for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
                  flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
                                   *ind_block, offset, 
                                   ctx->priv_data);
                  changed     |= flags;
                  if (flags & BLOCK_ABORT) {
                        ret |= BLOCK_ABORT;
                        break;
                  }
                  offset += sizeof(blk_t);
            }
      } else {
            for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
                  if (*block_nr == 0)
                        continue;
                  flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
                                   *ind_block, offset, 
                                   ctx->priv_data);
                  changed     |= flags;
                  if (flags & BLOCK_ABORT) {
                        ret |= BLOCK_ABORT;
                        break;
                  }
                  offset += sizeof(blk_t);
            }
      }
      if (changed & BLOCK_CHANGED) {
            ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
                                          ctx->ind_buf);
            if (ctx->errcode)
                  ret |= BLOCK_ERROR | BLOCK_ABORT;
      }
      if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
          !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
          !(ret & BLOCK_ABORT))
            ret |= (*ctx->func)(ctx->fs, ind_block,
                            BLOCK_COUNT_IND, ref_block,
                            ref_offset, ctx->priv_data);
      return ret;
}
      
static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
                        int ref_offset, struct block_context *ctx)
{
      int   ret = 0, changed = 0;
      int   i, flags, limit, offset;
      blk_t *block_nr;

      limit = ctx->fs->blocksize >> 2;
      if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
                      BLOCK_FLAG_DATA_ONLY)))
            ret = (*ctx->func)(ctx->fs, dind_block,
                           BLOCK_COUNT_DIND, ref_block,
                           ref_offset, ctx->priv_data);
      if (!*dind_block || (ret & BLOCK_ABORT)) {
            ctx->bcount += limit*limit;
            return ret;
      }
      if (*dind_block >= ctx->fs->super->s_blocks_count ||
          *dind_block < ctx->fs->super->s_first_data_block) {
            ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
            ret |= BLOCK_ERROR;
            return ret;
      }
      ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, 
                                   ctx->dind_buf);
      if (ctx->errcode) {
            ret |= BLOCK_ERROR;
            return ret;
      }

      block_nr = (blk_t *) ctx->dind_buf;
      offset = 0;
      if (ctx->flags & BLOCK_FLAG_APPEND) {
            for (i = 0; i < limit; i++, block_nr++) {
                  flags = block_iterate_ind(block_nr,
                                      *dind_block, offset,
                                      ctx);
                  changed |= flags;
                  if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
                        ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
                        break;
                  }
                  offset += sizeof(blk_t);
            }
      } else {
            for (i = 0; i < limit; i++, block_nr++) {
                  if (*block_nr == 0) {
                        ctx->bcount += limit;
                        continue;
                  }
                  flags = block_iterate_ind(block_nr,
                                      *dind_block, offset,
                                      ctx);
                  changed |= flags;
                  if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
                        ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
                        break;
                  }
                  offset += sizeof(blk_t);
            }
      }
      if (changed & BLOCK_CHANGED) {
            ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
                                          ctx->dind_buf);
            if (ctx->errcode)
                  ret |= BLOCK_ERROR | BLOCK_ABORT;
      }
      if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
          !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
          !(ret & BLOCK_ABORT))
            ret |= (*ctx->func)(ctx->fs, dind_block,
                            BLOCK_COUNT_DIND, ref_block,
                            ref_offset, ctx->priv_data);
      return ret;
}
      
static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
                        int ref_offset, struct block_context *ctx)
{
      int   ret = 0, changed = 0;
      int   i, flags, limit, offset;
      blk_t *block_nr;

      limit = ctx->fs->blocksize >> 2;
      if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
                      BLOCK_FLAG_DATA_ONLY)))
            ret = (*ctx->func)(ctx->fs, tind_block,
                           BLOCK_COUNT_TIND, ref_block,
                           ref_offset, ctx->priv_data);
      if (!*tind_block || (ret & BLOCK_ABORT)) {
            ctx->bcount += limit*limit*limit;
            return ret;
      }
      if (*tind_block >= ctx->fs->super->s_blocks_count ||
          *tind_block < ctx->fs->super->s_first_data_block) {
            ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
            ret |= BLOCK_ERROR;
            return ret;
      }
      ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, 
                                   ctx->tind_buf);
      if (ctx->errcode) {
            ret |= BLOCK_ERROR;
            return ret;
      }

      block_nr = (blk_t *) ctx->tind_buf;
      offset = 0;
      if (ctx->flags & BLOCK_FLAG_APPEND) {
            for (i = 0; i < limit; i++, block_nr++) {
                  flags = block_iterate_dind(block_nr,
                                       *tind_block,
                                       offset, ctx);
                  changed |= flags;
                  if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
                        ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
                        break;
                  }
                  offset += sizeof(blk_t);
            }
      } else {
            for (i = 0; i < limit; i++, block_nr++) {
                  if (*block_nr == 0) {
                        ctx->bcount += limit*limit;
                        continue;
                  }
                  flags = block_iterate_dind(block_nr,
                                       *tind_block,
                                       offset, ctx);
                  changed |= flags;
                  if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
                        ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
                        break;
                  }
                  offset += sizeof(blk_t);
            }
      }
      if (changed & BLOCK_CHANGED) {
            ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
                                          ctx->tind_buf);
            if (ctx->errcode)
                  ret |= BLOCK_ERROR | BLOCK_ABORT;
      }
      if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
          !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
          !(ret & BLOCK_ABORT))
            ret |= (*ctx->func)(ctx->fs, tind_block,
                            BLOCK_COUNT_TIND, ref_block,
                            ref_offset, ctx->priv_data);
      
      return ret;
}
      
errcode_t ext2fs_block_iterate2(ext2_filsys fs,
                        ext2_ino_t ino,
                        int   flags,
                        char *block_buf,
                        int (*func)(ext2_filsys fs,
                                  blk_t   *blocknr,
                                  e2_blkcnt_t   blockcnt,
                                  blk_t   ref_blk,
                                  int           ref_offset,
                                  void    *priv_data),
                        void *priv_data)
{
      int   i;
      int   got_inode = 0;
      int   ret = 0;
      blk_t blocks[EXT2_N_BLOCKS];  /* directory data blocks */
      struct ext2_inode inode;
      errcode_t   retval;
      struct block_context ctx;
      int   limit;

      EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

      /*
       * Check to see if we need to limit large files
       */
      if (flags & BLOCK_FLAG_NO_LARGE) {
            ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
            if (ctx.errcode)
                  return ctx.errcode;
            got_inode = 1;
            if (!LINUX_S_ISDIR(inode.i_mode) &&
                (inode.i_size_high != 0))
                  return EXT2_ET_FILE_TOO_BIG;
      }

      retval = ext2fs_get_blocks(fs, ino, blocks);
      if (retval)
            return retval;

      limit = fs->blocksize >> 2;

      ctx.fs = fs;
      ctx.func = func;
      ctx.priv_data = priv_data;
      ctx.flags = flags;
      ctx.bcount = 0;
      if (block_buf) {
            ctx.ind_buf = block_buf;
      } else {
            retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf);
            if (retval)
                  return retval;
      }
      ctx.dind_buf = ctx.ind_buf + fs->blocksize;
      ctx.tind_buf = ctx.dind_buf + fs->blocksize;

      /*
       * Iterate over the HURD translator block (if present)
       */
      if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
          !(flags & BLOCK_FLAG_DATA_ONLY)) {
            ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
            if (ctx.errcode)
                  goto abort_exit;
            got_inode = 1;
            if (inode.osd1.hurd1.h_i_translator) {
                  ret |= (*ctx.func)(fs,
                                 &inode.osd1.hurd1.h_i_translator,
                                 BLOCK_COUNT_TRANSLATOR,
                                 0, 0, priv_data);
                  if (ret & BLOCK_ABORT)
                        goto abort_exit;
            }
      }
      
      /*
       * Iterate over normal data blocks
       */
      for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
            if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
                  ret |= (*ctx.func)(fs, &blocks[i],
                                  ctx.bcount, 0, i, priv_data);
                  if (ret & BLOCK_ABORT)
                        goto abort_exit;
            }
      }
      if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
            ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
                               0, EXT2_IND_BLOCK, &ctx);
            if (ret & BLOCK_ABORT)
                  goto abort_exit;
      } else
            ctx.bcount += limit;
      if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
            ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
                                0, EXT2_DIND_BLOCK, &ctx);
            if (ret & BLOCK_ABORT)
                  goto abort_exit;
      } else
            ctx.bcount += limit * limit;
      if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
            ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
                                0, EXT2_TIND_BLOCK, &ctx);
            if (ret & BLOCK_ABORT)
                  goto abort_exit;
      }

abort_exit:
      if (ret & BLOCK_CHANGED) {
            if (!got_inode) {
                  retval = ext2fs_read_inode(fs, ino, &inode);
                  if (retval)
                        return retval;
            }
            for (i=0; i < EXT2_N_BLOCKS; i++)
                  inode.i_block[i] = blocks[i];
            retval = ext2fs_write_inode(fs, ino, &inode);
            if (retval)
                  return retval;
      }

      if (!block_buf)
            ext2fs_free_mem(&ctx.ind_buf);

      return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
}

/*
 * Emulate the old ext2fs_block_iterate function!
 */

struct xlate {
      int (*func)(ext2_filsys fs,
                blk_t   *blocknr,
                int           bcount,
                void    *priv_data);
      void *real_private;
};

#ifdef __TURBOC__
 #pragma argsused
#endif
static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
                  blk_t ref_block EXT2FS_ATTR((unused)),
                  int ref_offset EXT2FS_ATTR((unused)),
                  void *priv_data)
{
      struct xlate *xl = (struct xlate *) priv_data;

      return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
}

errcode_t ext2fs_block_iterate(ext2_filsys fs,
                         ext2_ino_t ino,
                         int  flags,
                         char *block_buf,
                         int (*func)(ext2_filsys fs,
                                 blk_t    *blocknr,
                                 int      blockcnt,
                                 void     *priv_data),
                         void *priv_data)
{
      struct xlate xl;
      
      xl.real_private = priv_data;
      xl.func = func;

      return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
                             block_buf, xlate_func, &xl);
}


Generated by  Doxygen 1.6.0   Back to index