Logo Search packages:      
Sourcecode: parted version File versions

ext2.c

/*
    ext2.c -- generic ext2 stuff
    Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
  
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "config.h"

#ifndef DISCOVER_ONLY

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <uuid/uuid.h>
#include "ext2.h"

/* ext2 stuff ****************************************************************/

unsigned char _bitmap[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

int ext2_copy_block(struct ext2_fs *fs, blk_t from, blk_t to)
{
      unsigned char buf[fs->blocksize];

      if (!ext2_bcache_flush(fs, from)) return 0;
      if (!ext2_bcache_flush(fs, to)) return 0;

      if (!ext2_read_blocks(fs, buf, from, 1)) return 0;
      if (!ext2_write_blocks(fs, buf, to, 1)) return 0;

      return 1;
}

int ext2_get_block_state(struct ext2_fs *fs, blk_t block)
{
      struct ext2_buffer_head *bh;
      int group;
      int offset;
      int state;

      block -= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
      group = block / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
      offset = block % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);

      bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]));
      state = bh->data[offset>>3] & _bitmap[offset&7];
      ext2_brelse(bh, 0);

      return state;
}

blk_t ext2_find_free_block(struct ext2_fs *fs)
{
      int i;

      for (i=0;i<fs->numgroups;i++)
            if (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[i]))
            {
                  blk_t j;
                  blk_t offset;

                  offset = i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)
                         + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
                  for (j=fs->adminblocks;
                       j<EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
                       j++)
                        if (ext2_is_data_block(fs, offset + j) &&
                            !ext2_get_block_state(fs, offset + j))
                              return offset + j;

                  ped_exception_throw (PED_EXCEPTION_ERROR,
                        PED_EXCEPTION_CANCEL,
                        _("Inconsistent group descriptors!"));
            }

      ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
                       _("Filesystem full!"));
      return 0;
}

ino_t ext2_find_free_inode(struct ext2_fs *fs)
{
      int i;

      for (i=0;i<fs->numgroups;i++)
            if (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[i]))
            {
                  ino_t j;
                  ino_t offset;

                  offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1;
                  for (j=0;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++)
                        if (!ext2_get_inode_state(fs, offset + j))
                              return offset + j;

                  ped_exception_throw (PED_EXCEPTION_ERROR,
                        PED_EXCEPTION_CANCEL,
                        _("Inconsistent group descriptors!"));
            }

      ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
                       _("Filesystem full!"));
      return 0;
}

int ext2_move_blocks(struct ext2_fs *fs, blk_t src, blk_t num, blk_t dest)
{
      unsigned char *buf;
      blk_t i;

      ped_exception_fetch_all();
      if ((buf = ped_malloc(num << fs->logsize)) != NULL)
      {
            ped_exception_leave_all();

            if (!ext2_bcache_flush_range(fs, src, num)) return 0;
            if (!ext2_bcache_flush_range(fs, dest, num)) return 0;

            if (!ext2_read_blocks(fs, buf, src, num)) return 0;
            if (!ext2_write_blocks(fs, buf, dest, num)) return 0;

            ped_free(buf);
            return 1;
      }
      ped_exception_catch();
      ped_exception_leave_all();

      if (src > dest)
      {
            for (i=0;i<num;i++)
                  if (!ext2_copy_block(fs, src+i, dest+i))
                        return 0;
      }
      else
      {
            for (i=num;i>0;i--)
                  if (!ext2_copy_block(fs, src+i, dest+i))
                        return 0;
      }
      return 1;
}

int ext2_read_blocks(struct ext2_fs *fs, void *ptr, blk_t block, blk_t num)
{
      return fs->devhandle->ops->read(fs->devhandle->cookie, ptr, block, num);
}

int ext2_set_block_state(struct ext2_fs *fs, blk_t block, int state, int updatemetadata)
{
      struct ext2_buffer_head *bh;
      int                      group;
      int                      offset;

      block -= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
      group = block / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
      offset = block % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);

      bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]));
      bh->dirty = 1;
      if (state)
            bh->data[offset>>3] |= _bitmap[offset&7];
      else
            bh->data[offset>>3] &= ~_bitmap[offset&7];
      ext2_brelse(bh, 0);

      if (updatemetadata)
      {
            int diff;

            diff = state ? -1 : 1;

            fs->gd[group].bg_free_blocks_count = PED_CPU_TO_LE16
                  (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[group]) + diff);
            fs->sb.s_free_blocks_count = PED_CPU_TO_LE32
                  (EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) + diff);
            fs->metadirty |= EXT2_META_SB | EXT2_META_GD;
      }
      return 1;
}

int ext2_write_blocks(struct ext2_fs *fs, void *ptr, blk_t block, blk_t num)
{
      return fs->devhandle->ops->write(fs->devhandle->cookie, ptr, block, num);
}

int ext2_zero_blocks(struct ext2_fs *fs, blk_t block, blk_t num)
{
      unsigned char *buf;
      blk_t i;

      ped_exception_fetch_all();
      buf = ped_malloc (num << fs->logsize);
      if (buf)
      {
            ped_exception_leave_all();

            memset(buf, 0, num << fs->logsize);
            if (!ext2_bcache_flush_range(fs, block, num))
                  goto error_free_buf;
            if (!ext2_write_blocks(fs, buf, block, num))
                  goto error_free_buf;
            ped_free(buf);
            return 1;
      }
      ped_exception_catch();

      buf = ped_malloc (fs->blocksize);
      if (buf)
      {
            ped_exception_leave_all();

            memset(buf, 0, fs->blocksize);

            for (i=0;i<num;i++)
            {
                  if (!ext2_bcache_flush(fs, block+i))
                        goto error_free_buf;
                  if (!ext2_write_blocks(fs, buf, block+i, 1))
                        goto error_free_buf;
            }

            ped_free(buf);
            return 1;
      }
      ped_exception_catch();
      ped_exception_leave_all();

      for (i=0;i<num;i++)
      {
            struct ext2_buffer_head *bh;

            bh = ext2_bcreate(fs, block+i);
            if (!bh)
                  goto error;
            bh->dirty = 1;
            if (!ext2_brelse(bh, 1))
                  goto error;
      }
      return 1;

error_free_buf:
      ped_free(buf);
error:
      return 0;
}

off_t ext2_get_inode_offset(struct ext2_fs *fs, ino_t inode, blk_t *block)
{
      int group;
      int offset;

      inode--;

      group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
      offset = (inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb))
                   * sizeof(struct ext2_inode);

      *block = EXT2_GROUP_INODE_TABLE(fs->gd[group])
               + (offset >> fs->logsize);

      return offset & (fs->blocksize - 1);
}

int ext2_get_inode_state(struct ext2_fs *fs, ino_t inode)
{
      struct ext2_buffer_head *bh;
      int                      group;
      int                      offset;
      int                      ret;

      inode--;
      group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
      offset = inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb);

      bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]));
      ret = bh->data[offset>>3] & _bitmap[offset&7];
      ext2_brelse(bh, 0);

      return ret;
}

int ext2_read_inode(struct ext2_fs *fs, ino_t inode, struct ext2_inode *data)
{
      struct ext2_buffer_head *bh;
      blk_t              blk;
      off_t              off;

      off = ext2_get_inode_offset(fs, inode, &blk);

      bh = ext2_bread(fs, blk);
      if (!bh)
            return 0;

      memcpy(data, bh->data + off, sizeof(struct ext2_inode));
      ext2_brelse(bh, 0);
      return 1;
}

int ext2_set_inode_state(struct ext2_fs *fs, ino_t inode, int state, int updatemetadata)
{
      struct ext2_buffer_head *bh;
      int                      group;
      int                      offset;

      inode--;
      group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
      offset = inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb);

      bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]));
      if (!bh)
            return 0;
      bh->dirty = 1;
      if (state)
            bh->data[offset>>3] |= _bitmap[offset&7];
      else
            bh->data[offset>>3] &= ~_bitmap[offset&7];
      ext2_brelse(bh, 0);

      if (updatemetadata)
      {
            int diff;

            diff = state ? -1 : 1;

            fs->gd[group].bg_free_inodes_count = PED_CPU_TO_LE16
                  (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[group]) + diff);
            fs->sb.s_free_inodes_count = PED_CPU_TO_LE32
                  (EXT2_SUPER_FREE_INODES_COUNT(fs->sb) + diff);
            fs->metadirty = EXT2_META_SB | EXT2_META_GD;
      }
      return 1;
}

static void
_inode_update_size(struct ext2_fs *fs, struct ext2_inode *inode, int delta)
{
      int         i512perblock = 1 << (fs->logsize - 9);
      uint64_t    size;

      /* i_blocks is in 512 byte blocks */
      inode->i_blocks = PED_CPU_TO_LE32(EXT2_INODE_BLOCKS(*inode)
                                      + delta * i512perblock);
      size = EXT2_INODE_SIZE(*inode) + delta * fs->blocksize;
      inode->i_size = PED_CPU_TO_LE32(size % (1LL << 32));
      inode->i_size_high = PED_CPU_TO_LE32(size / (1LL << 32));
      inode->i_mtime = PED_CPU_TO_LE32(time(NULL));
}

int ext2_do_inode(struct ext2_fs *fs, struct ext2_inode *inode, blk_t block,
               int action)
{
      struct ext2_buffer_head *bh;
      uint32_t          *udata;
      blk_t              count = 0;
      int                i;
      int                u32perblock = fs->blocksize >> 2;
      int                i512perblock = 1 << (fs->logsize - 9);

      if (block == 0 || EXT2_INODE_MODE(*inode) == 0)
            return -1;

      if (fs->opt_debug)
            switch (action)
            {
                  case EXT2_ACTION_ADD:
                        fprintf(stderr,"adding 0x%04x to inode\n",
                              block);
                        break;
                  case EXT2_ACTION_DELETE:
                        fprintf(stderr,"deleting 0x%04x from inode\n",
                              block);
                        break;
                  case EXT2_ACTION_FIND:
                        fprintf(stderr,"finding 0x%04x in inode\n",
                              block);
                        break;
            }

      /* Direct blocks for first 12 blocks */
      for (i = 0; i < EXT2_NDIR_BLOCKS; i++)
      {
            if (action == EXT2_ACTION_ADD && !EXT2_INODE_BLOCK(*inode, i))
            {
                  inode->i_block[i] = PED_CPU_TO_LE32(block);
                  _inode_update_size (fs, inode, 1);
                  ext2_set_block_state(fs, block, 1, 1);
                  return i;
            }
            if (EXT2_INODE_BLOCK(*inode, i) == block)
            {
                  if (action == EXT2_ACTION_DELETE)
                  {
                        inode->i_block[i] = 0;
                        _inode_update_size (fs, inode, -1);
                        ext2_set_block_state(fs, block, 0, 1);
                  }
                  return i;
            }
            if (EXT2_INODE_BLOCK(*inode, i))
                  count += i512perblock;
      }

      count += EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK) ? i512perblock : 0;
      count += EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK) ? i512perblock : 0;
      count += EXT2_INODE_BLOCK(*inode, EXT2_TIND_BLOCK) ? i512perblock : 0;

      if (!EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK) ||
          (count >= EXT2_INODE_BLOCKS(*inode) && action != EXT2_ACTION_ADD))
            return -1;

      bh = ext2_bread(fs, EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK));
      udata = (uint32_t *)bh->data;

      /* Indirect blocks for next 256/512/1024 blocks (for 1k/2k/4k blocks) */
      for (i = 0; i < u32perblock; i++) {
            if (action == EXT2_ACTION_ADD && !udata[i]) {
                  bh->dirty = 1;
                  udata[i] = PED_CPU_TO_LE32(block);
                  _inode_update_size (fs, inode, 1);
                  ext2_set_block_state(fs, block, 1, 1);
                  ext2_brelse(bh, 0);
                  return EXT2_NDIR_BLOCKS + i;
            }
            if (PED_LE32_TO_CPU(udata[i]) == block) {
                  if (action == EXT2_ACTION_DELETE) {
                        bh->dirty = 1;
                        udata[i] = 0;
                        _inode_update_size (fs, inode, -1);
                        ext2_set_block_state(fs, block, 0, 1);
                  }
                  ext2_brelse(bh, 0);
                  return EXT2_NDIR_BLOCKS + i;
            }
            if (udata[i])
            {
                  count += i512perblock;
                  if (count >= EXT2_INODE_BLOCKS(*inode) &&
                      action != EXT2_ACTION_ADD)
                        return -1;
            }
      }

      ext2_brelse(bh, 0);

      if (!EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK) ||
          (count >= EXT2_INODE_BLOCKS(*inode) && action != EXT2_ACTION_ADD))
            return -1;
      bh = ext2_bread(fs, EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK));
      udata = (uint32_t *)bh->data;

      /* Double indirect blocks for next 2^16/2^18/2^20 1k/2k/4k blocks */
      for (i = 0; i < u32perblock; i++) {
            struct ext2_buffer_head *bh2;
            uint32_t                *udata2;
            int                j;

            if (!udata[i]) {
                  ext2_brelse(bh, 0);
                  return -1;
            }
            bh2 = ext2_bread(fs, PED_LE32_TO_CPU(udata[i]));
            udata2 = (uint32_t *)bh2->data;
            count += i512perblock;

            for (j = 0; j < u32perblock; j++) {
                  if (action == EXT2_ACTION_ADD && !udata2[j]) {
                        bh2->dirty = 1;
                        udata2[j] = PED_CPU_TO_LE32(block);
                        _inode_update_size (fs, inode, 1);
                        ext2_set_block_state(fs, block, 1, 1);
                        ext2_brelse(bh, 0);
                        ext2_brelse(bh2, 0);
                        return EXT2_NDIR_BLOCKS + i * u32perblock + j;
                  }
                  if (PED_LE32_TO_CPU(udata2[j]) == block) {
                        if (action == EXT2_ACTION_DELETE) {
                              bh2->dirty = 1;
                              udata2[j] = 0;
                              _inode_update_size (fs, inode, -1);
                              ext2_set_block_state(fs, block, 0, 1);
                        }
                        ext2_brelse(bh, 0);
                        ext2_brelse(bh2, 0);
                        return EXT2_NDIR_BLOCKS + i * u32perblock + j;
                  }
                  if (udata2[j])
                  {
                        count += i512perblock;
                        if (count >= EXT2_INODE_BLOCKS(*inode) &&
                            action != EXT2_ACTION_ADD)
                              return -1;
                  }
            }
            ext2_brelse(bh2, 0);
      }
      ext2_brelse(bh, 0);

      /* FIXME: we should check for triple-indirect blocks here, but it
       * would be nice to have a better routine to traverse blocks, and
       * filesystems that need triple-indirect blocks for the resize
       * inode are too big to worry about yet.
       */

      return -1;
}

int ext2_write_inode(struct ext2_fs *fs, ino_t inode, const struct ext2_inode *data)
{
      struct ext2_buffer_head *bh;
      blk_t              blk;
      off_t              off;

      off = ext2_get_inode_offset(fs, inode, &blk);

      bh = ext2_bread(fs, blk);
      if (!bh)
            return 0;
      bh->dirty = 1;
      memcpy(bh->data + off, data, sizeof(struct ext2_inode));
      ext2_brelse(bh, 0);

      return 1;
}

int ext2_zero_inode(struct ext2_fs *fs, ino_t inode)
{
      struct ext2_inode buf;

      memset(&buf, 0, sizeof(struct ext2_inode));
      return ext2_write_inode(fs, inode, &buf);
}





/* formula grabbed from linux ext2 kernel source */
static int is_root(int x, int y)
{
      if (!x)
            return 1;

      while (1)
      {
            if (x == 1)
                  return 1;

            if (x % y)
                  return 0;

            x /= y;
      }
}

int ext2_is_group_sparse(struct ext2_fs *fs, int group)
{
      if (!fs->sparse)
            return 1;

      if (is_root(group, 3) || is_root(group, 5) || is_root(group, 7))
            return 1;

      return 0;
}

void ext2_close(struct ext2_fs *fs)
{
      ext2_commit_metadata(fs, EXT2_META_PRIMARY | EXT2_META_BACKUP);
      ext2_sync(fs);

      ext2_bcache_deinit(fs);

      fs->devhandle->ops->close(fs->devhandle->cookie);

      ped_free(fs->gd);
      ped_free(fs);
}

int ext2_commit_metadata(struct ext2_fs *fs, int copies)
{
      int         i;
      int         num;
      int         wmeta = fs->metadirty & copies;
      unsigned char     sb[fs->blocksize];
      struct ext2_super_block *sb_for_io;
      int         sb_block;

      /* See if there is even anything to write... */
      if (wmeta == EXT2_META_CLEAN)
            return 1;

      fs->sb.s_r_blocks_count = PED_CPU_TO_LE32 (
            fs->r_frac * (loff_t)EXT2_SUPER_BLOCKS_COUNT(fs->sb)
                          / 100);

      if (!ext2_read_blocks (fs, sb, 0, 1))
            return 0;

      if (EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)) {
            memcpy(sb, &fs->sb, 1024);
            sb_for_io = (struct ext2_super_block *) sb;
      } else {
            memcpy(sb+1024, &fs->sb, 1024);
            sb_for_io = (struct ext2_super_block *) (sb + 1024);
      }

      num = copies & EXT2_META_BACKUP ? fs->numgroups : 1;

      for (i = 0, sb_block = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); i < num;
           i++, sb_block += EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
      {

            if (!ext2_is_group_sparse(fs, i))
                  continue;

            if (fs->dynamic_version)
                  sb_for_io->s_block_group_nr = PED_CPU_TO_LE16 (i);

            if ((i == 0 && wmeta & EXT2_META_PRIMARY_SB) ||
                (i != 0 && wmeta & EXT2_META_SB))
            {
                  if (!ext2_bcache_flush_range(fs, sb_block, 1))
                        return 0;
                  if (!ext2_write_blocks(fs, sb, sb_block, 1))
                        return 0;
            }
            if ((i == 0 && wmeta & EXT2_META_PRIMARY_GD) ||
                (i != 0 && wmeta & EXT2_META_GD))
            {
                  if (!ext2_bcache_flush_range(fs, sb_block + 1,
                                         fs->gdblocks))
                        return 0;
                  if (!ext2_write_blocks(fs, fs->gd, sb_block + 1,
                                     fs->gdblocks))
                        return 0;
            }
      }

      sb_for_io->s_block_group_nr = 0;

      /* Clear the flags of the components we just finished writing. */
      fs->metadirty &= ~copies;

      return 1;
}

static int ext2_determine_itoffset(struct ext2_fs *fs)
{
      int i;

      fs->itoffset = EXT2_GROUP_INODE_TABLE(fs->gd[0])
                   - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);

      for (i=0;i<fs->numgroups;i++)
      {
            blk_t start;
            blk_t bb;
            blk_t ib;
            blk_t it;

            start = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)
                  + (i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
            it = start + fs->itoffset;

            if (ext2_is_group_sparse(fs, i))
            {
                  bb = it - 2;
                  ib = it - 1;
            }
            else
            {
                  bb = start;
                  ib = start + 1;
            }

            if (EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) != bb ||
                EXT2_GROUP_INODE_BITMAP(fs->gd[i]) != ib ||
                EXT2_GROUP_INODE_TABLE(fs->gd[i]) != it)
            {
                  ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
                        PED_EXCEPTION_CANCEL,
                  _("This ext2 filesystem has a rather strange layout!  "
                    "Parted can't resize this (yet)."));
                  return 0;
            }
      }

      return 1;
}

int ext2_sync(struct ext2_fs *fs)
{
      if (!ext2_commit_metadata(fs, EXT2_META_PRIMARY)) return 0;
      if (!ext2_bcache_sync(fs)) return 0;
      if (!fs->devhandle->ops->sync(fs->devhandle->cookie)) return 0;
      return 1;
}

struct ext2_fs *ext2_open(struct ext2_dev_handle *handle, int state)
{
      struct ext2_fs *fs;

      if ((fs = (struct ext2_fs *) ped_malloc(sizeof(struct ext2_fs)))
            == NULL)
            goto error;

      handle->ops->set_blocksize(handle->cookie, 10);

      if (!handle->ops->read(handle->cookie, &fs->sb, 1, 1)
          || EXT2_SUPER_MAGIC(fs->sb) != EXT2_SUPER_MAGIC_CONST)
      {
            ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
                  _("Invalid superblock.  Are you sure this is an ext2 "
                    "filesystem?"));
            goto error_free_fs;
      }


      fs->opt_debug = 1;
      fs->opt_safe = 1;
      fs->opt_verbose = 0;

      if (EXT2_SUPER_STATE(fs->sb) & EXT2_ERROR_FS & ~(state & EXT2_ERROR_FS))
      {
            if (ped_exception_throw (
                  PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE_CANCEL,
                  _("Filesystem has errors!  You should run e2fsck."))
                        == PED_EXCEPTION_CANCEL)
                  goto error_free_fs;
      }

      if (!((EXT2_SUPER_STATE(fs->sb) | state) & EXT2_VALID_FS)
          || (EXT2_SUPER_FEATURE_INCOMPAT(fs->sb)
            & EXT3_FEATURE_INCOMPAT_RECOVER))
      {
            if (ped_exception_throw (
                  PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL,
                  _("Filesystem was not cleanly unmounted!  "
                    "You should e2fsck.  Modifying an unclean filesystem "
                    "could cause severe corruption."))
                        != PED_EXCEPTION_IGNORE)
                  goto error_free_fs;
      }

      fs->dynamic_version = EXT2_SUPER_REV_LEVEL (fs->sb) > 0;

      if ((EXT2_SUPER_FEATURE_COMPAT(fs->sb)
                      & ~(EXT3_FEATURE_COMPAT_HAS_JOURNAL |
                      EXT2_FEATURE_COMPAT_HAS_DIR_INDEX)) ||
          (EXT2_SUPER_FEATURE_INCOMPAT(fs->sb)
                  & ~(EXT2_FEATURE_INCOMPAT_FILETYPE |
                      EXT3_FEATURE_INCOMPAT_RECOVER)) ||
          (EXT2_SUPER_FEATURE_RO_COMPAT(fs->sb)
                  & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
                      EXT2_FEATURE_RO_COMPAT_LARGE_FILE)))
      {
            ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
                 _("Filesystem has incompatible feature enabled"));
            goto error_free_fs;
      }

      fs->devhandle = handle;
      fs->logsize = EXT2_SUPER_LOG_BLOCK_SIZE(fs->sb) + 10;
      handle->ops->set_blocksize(handle->cookie, fs->logsize);

      if (!ext2_bcache_init(fs))
      {
            ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
                             _("Error allocating buffer cache."));
            goto error_free_fs;
      }

      fs->blocksize = 1 << fs->logsize;

      fs->numgroups = howmany(EXT2_SUPER_BLOCKS_COUNT(fs->sb)
                              - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb),
                        EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
      fs->gdblocks = howmany(fs->numgroups * sizeof(struct ext2_group_desc),
                         fs->blocksize);
      fs->inodeblocks = howmany(EXT2_SUPER_INODES_PER_GROUP(fs->sb)
                                * sizeof(struct ext2_inode),
                          fs->blocksize);
      fs->r_frac = howmany(100 * (loff_t)EXT2_SUPER_R_BLOCKS_COUNT(fs->sb),
                             EXT2_SUPER_BLOCKS_COUNT(fs->sb));
      fs->adminblocks = 3 + fs->gdblocks + fs->inodeblocks;

      fs->sparse = 0;
      if (EXT2_SUPER_FEATURE_RO_COMPAT(fs->sb)
                  & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
            fs->sparse = 1;

      fs->has_journal = 0 < (EXT2_SUPER_FEATURE_COMPAT(fs->sb)
                         & EXT3_FEATURE_COMPAT_HAS_JOURNAL);
      fs->has_internal_journal
            = fs->has_journal
                  && uuid_is_null(EXT2_SUPER_JOURNAL_UUID(fs->sb))
                  && EXT2_SUPER_JOURNAL_INUM(fs->sb);

      fs->gd = ped_malloc (fs->numgroups * sizeof (struct ext2_group_desc)
                        + fs->blocksize);
      if (!fs->gd)
            goto error_deinit_bcache;

      ext2_read_blocks(fs, fs->gd, EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + 1,
                   fs->gdblocks);

      if (!ext2_determine_itoffset(fs))
            goto error_free_gd;

      fs->metadirty = 0;
      return fs;

error_free_gd:
      ped_free(fs->gd);
error_deinit_bcache:
      ext2_bcache_deinit(fs);
error_free_fs:
      ped_free(fs);
error:
      return NULL;
}

#endif /* !DISCOVER_ONLY */

Generated by  Doxygen 1.6.0   Back to index