| /* |
| ext2_mkfs.c -- ext2 fs creator |
| Copyright (C) 1999, 2000, 2001, 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(*sb) |
| - EXT2_SUPER_FIRST_DATA_BLOCK(*sb), |
| EXT2_SUPER_BLOCKS_PER_GROUP(*sb)); |
| gdblocks = ped_div_round_up (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 = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(*sb) |
| - EXT2_SUPER_FIRST_DATA_BLOCK(*sb), |
| EXT2_SUPER_BLOCKS_PER_GROUP(*sb)); |
| itsize = ped_div_round_up (sizeof(struct ext2_inode) |
| * EXT2_SUPER_INODES_PER_GROUP(*sb), |
| (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb))); |
| gdtsize = ped_div_round_up (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); |
| 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 = ped_malloc ((fs->blocksize / 4) * sizeof(uint32_t)); |
| 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(((uint64_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; |
| } |
| |
| /* FIXME: block size must be > MAX(logicalbs, physicalbs) |
| * to avoid modify-on-write. |
| * -- Leslie |
| */ |
| |
| |
| 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 = ped_div_round_up (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; |
| |
| /* FIXME: 5% not appropriate for modern drive sizes */ |
| 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 |
| + ped_div_round_up (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 */ |