Logo Search packages:      
Sourcecode: parted version File versions

ext2_mkfs.c

/*
    ext2_mkfs.c -- ext2 fs creator
    Copyright (C) 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

#define USE_EXT2_IS_DATA_BLOCK

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

/* formula grabbed from linux ext2 kernel source
 * 
 * returns 1 iff:
 *    x == y^N,       N is some natural number
 * OR x == 0
 */
static __inline__ 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;
      }
}

static __inline__ int is_group_sparse(int sparsesbfs, int group)
{
      if (!sparsesbfs)
            return 1;

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

      return 0;
}

/* has implicit parameter 'sb' !! */
#define is_sparse(group) is_group_sparse(EXT2_SUPER_FEATURE_RO_COMPAT(*sb) \
                              & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, (group))

static int ext2_mkfs_write_main(struct ext2_dev_handle *handle,
                        struct ext2_super_block *sb,
                        struct ext2_group_desc *gd)
{
      int freeit;
      int i;
      int numgroups;
      int gdblocks;
      unsigned char *sbbuf;
      struct ext2_super_block *sb_for_io;

      freeit = 0;
      sbbuf = (unsigned char *)sb;
      sb_for_io = sb;
      if (EXT2_SUPER_LOG_BLOCK_SIZE(*sb))
      {
            sbbuf = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
            if (!handle->ops->read(handle->cookie, sbbuf, 0, 1))
                  return 0;
            memcpy(sbbuf+1024, sb, 1024);
            freeit = 1;
            sb_for_io = (struct ext2_super_block*) (sbbuf + 1024);
      }

      numgroups = howmany(EXT2_SUPER_BLOCKS_COUNT(*sb)
                        - EXT2_SUPER_FIRST_DATA_BLOCK(*sb),
                      EXT2_SUPER_BLOCKS_PER_GROUP(*sb));
      gdblocks = howmany(numgroups * sizeof(struct ext2_group_desc),
                     1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));

      for (i=0;i<numgroups;i++)
      {
            if (is_sparse(i))
            {
                  int offset;

                  offset = EXT2_SUPER_FIRST_DATA_BLOCK(*sb)
                         + i * EXT2_SUPER_BLOCKS_PER_GROUP(*sb);

                  sb_for_io->s_block_group_nr = PED_CPU_TO_LE16 (i);

                  if (!handle->ops->write(handle->cookie, sbbuf,
                                      offset, 1))
                        return 0;
                  if (!handle->ops->write(handle->cookie, gd, offset+1,
                                    gdblocks))
                        return 0;
            }
      }

      sb_for_io->s_block_group_nr = 0;

      if (freeit)
            ped_free(sbbuf);
      return 1;
}

static int ext2_mkfs_write_meta(struct ext2_dev_handle *handle,
                        struct ext2_super_block *sb,
                        struct ext2_group_desc *gd,
                        PedTimer* timer)
{
      int blocksize;
      int gdtsize;
      int i;
      int itsize;
      int numgroups;
      unsigned char *bb;
      unsigned char *ib;
      unsigned char *zero;

      blocksize = 1 << (EXT2_SUPER_LOG_BLOCK_SIZE(*sb) + 13);

      numgroups = howmany(EXT2_SUPER_BLOCKS_COUNT(*sb)
                        - EXT2_SUPER_FIRST_DATA_BLOCK(*sb),
                      EXT2_SUPER_BLOCKS_PER_GROUP(*sb));
      itsize = howmany(sizeof(struct ext2_inode)
                        * EXT2_SUPER_INODES_PER_GROUP(*sb),
                   (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)));
      gdtsize = howmany(sizeof(struct ext2_group_desc) * numgroups,
                    (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)));

      bb = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
      if (!bb) goto error;
      ib = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
      if (!ib) goto error_free_bb;
      zero = ped_malloc((1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)) * itsize);
      if (!zero) goto error_free_zero;

      memset(zero, 0, (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)) * itsize);

      ped_timer_reset (timer);
      ped_timer_set_state_name (timer, _("writing per-group metadata"));

      for (i=0;i<numgroups;i++)
      {
            int admin;
            blk_t bbblock;
            int groupsize;
            int groupoffset;
            blk_t ibblock;
            int j;

            ped_timer_update (timer, 1.0 * i / numgroups);

            groupoffset = i*EXT2_SUPER_BLOCKS_PER_GROUP(*sb)
                        + EXT2_SUPER_FIRST_DATA_BLOCK(*sb);
            groupsize = PED_MIN(EXT2_SUPER_BLOCKS_COUNT(*sb) - groupoffset,
                            EXT2_SUPER_BLOCKS_PER_GROUP(*sb));

            admin = itsize + 2;
            bbblock = groupoffset;
            ibblock = groupoffset + 1;
            if (is_sparse(i))
            {
                  admin += gdtsize + 1;
                  bbblock = groupoffset + gdtsize + 1;
                  ibblock = groupoffset + gdtsize + 2;
            }

            {
                  memset(bb, 0, 1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
                  if (is_sparse(i))
                        for (j=0;j<gdtsize+1;j++)
                              bb[j>>3] |= _bitmap[j&7];

                  j = bbblock - groupoffset;
                  bb[j>>3] |= _bitmap[j&7];

                  j = ibblock - groupoffset;
                  bb[j>>3] |= _bitmap[j&7];

                  for (j=0;j<itsize;j++)
                  {
                        int k = j + gdtsize + 3;

                        bb[k>>3] |= _bitmap[k&7];
                  }

                  for (j=groupsize;j<blocksize;j++)
                        bb[j>>3] |= _bitmap[j&7];

                  if (!handle->ops->write(handle->cookie, bb, bbblock, 1))
                        goto error_free_zero;
            }

            {
                  memset(ib, 0, 1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));

                  for (j=EXT2_SUPER_INODES_PER_GROUP(*sb);j<blocksize;j++)
                        bb[j>>3] |= _bitmap[j&7];

                  if (!handle->ops->write(handle->cookie, ib, ibblock, 1))
                        goto error_free_zero;
            }

            if (!handle->ops->write(handle->cookie, zero,
                              groupoffset + gdtsize + 3, itsize))
                  goto error_free_zero;

            gd[i].bg_block_bitmap = PED_CPU_TO_LE32(bbblock);
            gd[i].bg_inode_bitmap = PED_CPU_TO_LE32(ibblock);
            gd[i].bg_inode_table = PED_CPU_TO_LE32(groupoffset + gdtsize
                                           + 3);
            gd[i].bg_free_blocks_count = PED_CPU_TO_LE16(groupsize - admin);
            gd[i].bg_free_inodes_count = PED_CPU_TO_LE16(
                  EXT2_SUPER_INODES_PER_GROUP(*sb));
            gd[i].bg_used_dirs_count = 0;
            gd[i].bg_used_dirs_count = 0;
            gd[i].bg_pad = 0;
            gd[i].bg_reserved[0] = 0;
            gd[i].bg_reserved[1] = 0;
            gd[i].bg_reserved[2] = 0;

            sb->s_free_blocks_count = PED_CPU_TO_LE32 (
                  EXT2_SUPER_FREE_BLOCKS_COUNT(*sb)
                  + EXT2_GROUP_FREE_BLOCKS_COUNT(gd[i]));
      }

      ped_timer_update (timer, 1.0);

      ped_free(zero);
      ped_free(ib);
      ped_free(bb);
      return 1;

error_free_zero:
      ped_free(zero);
error_free_ib:
      ped_free(ib);
error_free_bb:
      ped_free(bb);
error:
      return 0;
}

/* returns the offset into the buffer of the start of the next dir entry */
static int _set_dirent(void* buf, int offset, int block_size, int is_last,
                   uint32_t inode, char* name, int file_type)
{
      struct ext2_dir_entry_2 *dirent = (void*) (((char*)buf) + offset);
      int name_len = strlen(name);
      int rec_len;

      if (is_last)
            rec_len = block_size - offset;
      else
            rec_len = ped_round_up_to(name_len + 1 + 8, 4);

      memset (dirent, 0, rec_len);

      dirent->inode = PED_CPU_TO_LE32(inode);
      dirent->name_len = name_len;
      dirent->rec_len = PED_CPU_TO_LE16(rec_len);
      dirent->file_type = file_type;
      strcpy(dirent->name, name);

      return offset + rec_len;
}

static int ext2_mkfs_create_lost_and_found_inode(struct ext2_fs *fs)
{
      struct ext2_buffer_head *bh;
      blk_t blocks[12];
      uint32_t data[fs->blocksize / 4];
      int i;
      struct ext2_inode inode;
      int offset;

      for (i=0;i<12;i++)
      {
            if (!(blocks[i] = ext2_find_free_block(fs)))
                  return 0;

            if (!ext2_set_block_state(fs, blocks[i], 1, 1))
                  return 0;
      }

      /* create the directory entries, preallocating lots of blocks */
      /* first block contains . and .. */
      bh = ext2_bcreate(fs, blocks[0]);
      if (!bh)
            return 0;
      memset(bh->data, 0, fs->blocksize);
      offset = _set_dirent(bh->data, 0, fs->blocksize, 0,
                       11, ".", EXT2_FT_DIR);
      offset = _set_dirent(bh->data, offset, fs->blocksize, 1,
                       EXT2_ROOT_INO, "..", EXT2_FT_DIR);
      bh->dirty = 1;
      ext2_brelse(bh, 1);

      /* subsequent blocks are empty */
      memset(data, 0, fs->blocksize);
      data[0] = 0;
      data[1] = PED_CPU_TO_LE32(fs->blocksize);
      for (i=1;i<12;i++)
      {
            bh = ext2_bcreate(fs, blocks[i]);
            memcpy(bh->data, data, fs->blocksize);
            bh->dirty = 1;
            ext2_brelse(bh, 1);
      }

      /* create inode */
      memset(&inode, 0, sizeof(struct ext2_inode));
      inode.i_mode = PED_CPU_TO_LE16(S_IFDIR | 0755);
      inode.i_uid = 0;
      inode.i_size = PED_CPU_TO_LE32(12 * fs->blocksize);
      inode.i_atime = PED_CPU_TO_LE32(time(NULL));
      inode.i_ctime = PED_CPU_TO_LE32(time(NULL));
      inode.i_mtime = PED_CPU_TO_LE32(time(NULL));
      inode.i_dtime = 0;
      inode.i_gid = 0;
      inode.i_links_count = PED_CPU_TO_LE16(2);
      inode.i_blocks = PED_CPU_TO_LE32((12 * fs->blocksize) >> 9);
      inode.i_flags = 0;
      for (i=0;i<12;i++)
            inode.i_block[i] = PED_CPU_TO_LE32(blocks[i]);

      if (!ext2_write_inode(fs, 11, &inode))
            return 0;
      fs->gd[0].bg_used_dirs_count = PED_CPU_TO_LE16(
            EXT2_GROUP_USED_DIRS_COUNT(fs->gd[0]) + 1);
      fs->metadirty |= EXT2_META_GD;

      return 1;
}

static int ext2_mkfs_create_root_inode(struct ext2_fs *fs)
{
      struct ext2_buffer_head *bh;
      blk_t block;
      struct ext2_inode inode;
      int offset;

      if (!(block = ext2_find_free_block(fs)))
            return 0;
      if (!ext2_set_block_state(fs, block, 1, 1))
            return 0;

      /* create directory entries */
      bh = ext2_bcreate(fs, block);
      memset(bh->data, 0, fs->blocksize);
      offset = _set_dirent(bh->data, 0, fs->blocksize, 0,
                       EXT2_ROOT_INO, ".", EXT2_FT_DIR);
      offset = _set_dirent(bh->data, offset, fs->blocksize, 0,
                       EXT2_ROOT_INO, "..", EXT2_FT_DIR);
      offset = _set_dirent(bh->data, offset, fs->blocksize, 1,
                       11, "lost+found", EXT2_FT_DIR);
      bh->dirty = 1;
      if (!ext2_brelse(bh, 1))
            return 0;

      /* create inode */
      memset(&inode, 0, sizeof(struct ext2_inode));
      inode.i_mode = PED_CPU_TO_LE16(S_IFDIR | 0755);
      inode.i_uid = 0;
      inode.i_size = PED_CPU_TO_LE32(fs->blocksize);
      inode.i_atime = PED_CPU_TO_LE32(time(NULL));
      inode.i_ctime = PED_CPU_TO_LE32(time(NULL));
      inode.i_mtime = PED_CPU_TO_LE32(time(NULL));
      inode.i_dtime = 0;
      inode.i_gid = 0;
      inode.i_links_count = PED_CPU_TO_LE16(3);
      inode.i_blocks = PED_CPU_TO_LE32(fs->blocksize >> 9);
      inode.i_flags = 0;
      inode.i_block[0] = PED_CPU_TO_LE32(block);

      if (!ext2_write_inode(fs, 2, &inode))
            return 0;
      fs->gd[0].bg_used_dirs_count = PED_CPU_TO_LE16 (
            EXT2_GROUP_USED_DIRS_COUNT(fs->gd[0]) + 1);
      fs->metadirty |= EXT2_META_GD;

      return 1;
}

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

      for (i=1;i<12;i++)
            if (!ext2_set_inode_state(fs, i, 1, 1))
                  return 0;
      return 1;
}

static int ext2_mkfs_init_sb (struct ext2_super_block *sb, blk_t numblocks,
                        int numgroups, int first_block,
                        int log_block_size, blk_t blocks_per_group,
                        int inodes_per_group, int sparse_sb,
                        int reserved_block_percentage)
{
      /* catch a bug in gcc 2.95.2 */
      PED_ASSERT(numgroups != 0, return 0);

      memset(sb, 0, 1024);

      sb->s_inodes_count = PED_CPU_TO_LE32(numgroups * inodes_per_group);
      sb->s_blocks_count = PED_CPU_TO_LE32(numblocks);
      sb->s_r_blocks_count = PED_CPU_TO_LE32(((u_int64_t)numblocks
                        * reserved_block_percentage) / 100);

      /* hack: this get's inc'd as we go through each group in
       * ext2_mkfs_write_meta()
       */
      sb->s_free_blocks_count = 0;
      sb->s_free_inodes_count = PED_CPU_TO_LE32 (numgroups
                                          * inodes_per_group);
      sb->s_first_data_block = PED_CPU_TO_LE32(first_block);
      sb->s_log_block_size = PED_CPU_TO_LE32(log_block_size - 10);
      sb->s_log_frag_size = sb->s_log_block_size;
      sb->s_blocks_per_group = PED_CPU_TO_LE32(blocks_per_group);
      sb->s_frags_per_group = PED_CPU_TO_LE32(blocks_per_group);
      sb->s_inodes_per_group = PED_CPU_TO_LE32(inodes_per_group);
      sb->s_mtime = 0;
      sb->s_wtime = 0;
      sb->s_mnt_count = 0;
      sb->s_max_mnt_count = PED_CPU_TO_LE16(30);
      sb->s_magic = PED_CPU_TO_LE16(0xEF53);
      sb->s_state = PED_CPU_TO_LE16(EXT2_VALID_FS);
      sb->s_errors = PED_CPU_TO_LE16(EXT2_ERRORS_DEFAULT);
      sb->s_minor_rev_level = 0;
      sb->s_lastcheck = 0;
      sb->s_checkinterval = 0;
      sb->s_creator_os = 0;
      sb->s_rev_level = PED_CPU_TO_LE32(1);
      sb->s_def_resuid = 0;
      sb->s_def_resgid = 0;
      sb->s_first_ino = PED_CPU_TO_LE32(11);
      sb->s_inode_size = PED_CPU_TO_LE16(128);
      sb->s_block_group_nr = 0;
      sb->s_feature_compat = 0;
      sb->s_feature_incompat = 0;
      sb->s_feature_ro_compat = 0;
      if (sparse_sb)
            sb->s_feature_ro_compat
                  |= PED_CPU_TO_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);

/* FIXME: let the user decide? _set_dirent() assumes FILETYPE */
      sb->s_feature_incompat
            |= PED_CPU_TO_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE);

      uuid_generate(sb->s_uuid);
      memset(sb->s_volume_name, 0, 16);
      memset(sb->s_last_mounted, 0, 64);
      sb->s_algorithm_usage_bitmap = 0;
      sb->s_prealloc_blocks = 0;
      sb->s_prealloc_dir_blocks = 0;
      sb->s_padding1 = 0;

      return 1;
}

struct ext2_fs *ext2_mkfs(struct ext2_dev_handle *handle,
                    blk_t numblocks,
                    int log_block_size,
                    blk_t blocks_per_group,
                    int inodes_per_group,
                    int sparse_sb,
                    int reserved_block_percentage,
                    PedTimer* timer)
{
      struct ext2_fs *fs;
      struct ext2_super_block sb;
      struct ext2_group_desc *gd;
      int numgroups;
      int first_block;
      int non_sparse_admin;
      int sparse_admin;
      int last_group_blocks;
      int last_group_admin;

      /* if the FS is > 512Mb, use 4k blocks, otherwise 1k blocks */
      if (log_block_size == 0) {
            handle->ops->set_blocksize(handle->cookie, 12);
            if (handle->ops->get_size(handle->cookie) > (512 * 1024))
                  log_block_size = 12;
            else
                  log_block_size = 10;
      }

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

      if (numblocks == 0)
            numblocks = handle->ops->get_size(handle->cookie);

      if (blocks_per_group == (unsigned int) 0)
            blocks_per_group = 8 << log_block_size;

      first_block = (log_block_size == 10) ? 1 : 0;

      numgroups = howmany(numblocks - first_block, blocks_per_group);

      if (inodes_per_group == 0)
            inodes_per_group = ped_round_up_to (
                  numblocks / numgroups / 2,
                  (1 << log_block_size) / sizeof(struct ext2_inode));

      if (sparse_sb == -1)
            sparse_sb = 1;

      if (reserved_block_percentage == -1)
            reserved_block_percentage = 5;

      last_group_blocks = (numblocks - first_block) % blocks_per_group;
      if (!last_group_blocks) last_group_blocks = blocks_per_group;
      non_sparse_admin = 2
                     + inodes_per_group * sizeof(struct ext2_inode)
                        / (1 << log_block_size);
      sparse_admin = non_sparse_admin
                   + howmany (numgroups * sizeof(struct ext2_group_desc),
                          1 << log_block_size);
      last_group_admin = is_group_sparse(sparse_sb, numgroups - 1)
                     ? sparse_admin : non_sparse_admin;
      if (last_group_admin >= last_group_blocks) {
            numgroups--;
            numblocks -= last_group_blocks;
      }
      if (!numgroups
          || (numgroups == 1
                && (last_group_blocks - last_group_admin < 8
                  || inodes_per_group < 16))) {
            ped_exception_throw (
                  PED_EXCEPTION_ERROR,
                  PED_EXCEPTION_CANCEL,
                  _("File system too small for ext2."));
            goto error;
      }

      gd = ped_malloc(numgroups * sizeof(struct ext2_group_desc)
                  + (1 << log_block_size));
      if (!gd)
            goto error;

      if (!ext2_mkfs_init_sb(&sb, numblocks, numgroups, first_block,
                         log_block_size, blocks_per_group,
                         inodes_per_group, sparse_sb,
                         reserved_block_percentage))
                  goto error_free_gd;
      if (!ext2_mkfs_write_meta(handle, &sb, gd, timer))
                  goto error_free_gd;
      if (!ext2_mkfs_write_main(handle, &sb, gd))
                  goto error_free_gd;

      fs = ext2_open(handle, 0);
      if (!fs) goto error_close_fs;
      if (!ext2_reserve_inodes(fs)) goto error_close_fs;
      if (!ext2_mkfs_create_root_inode(fs)) goto error_close_fs;
      if (!ext2_mkfs_create_lost_and_found_inode(fs))
            goto error_close_fs;
      if (!ext2_sync(fs)) goto error_close_fs;
      ped_free(gd);
      return fs;

error_close_fs:
      ext2_close(fs);
error_free_gd:
      ped_free (gd);
error:
      return NULL;
}
#endif /* !DISCOVER_ONLY */

Generated by  Doxygen 1.6.0   Back to index