From: Lawrence S. <ljs...@us...> - 2013-05-16 00:02:49
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "A pseudo Operating System for the Dreamcast.". The branch, master has been updated via 87003ea0be91297fd777f846544096be3a9c0964 (commit) from f4796a2464b352b4b660bcb68af4c0e825365805 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 87003ea0be91297fd777f846544096be3a9c0964 Author: Lawrence Sebald <ljs...@us...> Date: Wed May 15 20:02:22 2013 -0400 Add a mke2fs program to the examples. ----------------------------------------------------------------------- Summary of changes: examples/dreamcast/sd/Makefile | 6 +- examples/dreamcast/sd/{ext2fs => mke2fs}/Makefile | 13 +- examples/dreamcast/sd/mke2fs/Makefile.nonkos | 17 + examples/dreamcast/sd/mke2fs/mke2fs.c | 991 +++++++++++++++++++++ 4 files changed, 1020 insertions(+), 7 deletions(-) copy examples/dreamcast/sd/{ext2fs => mke2fs}/Makefile (51%) create mode 100644 examples/dreamcast/sd/mke2fs/Makefile.nonkos create mode 100644 examples/dreamcast/sd/mke2fs/mke2fs.c diff --git a/examples/dreamcast/sd/Makefile b/examples/dreamcast/sd/Makefile index 032f319..31bfab0 100644 --- a/examples/dreamcast/sd/Makefile +++ b/examples/dreamcast/sd/Makefile @@ -5,9 +5,13 @@ all: $(KOS_MAKE) -C ext2fs + $(KOS_MAKE) -C mke2fs clean: $(KOS_MAKE) -C ext2fs clean - + $(KOS_MAKE) -C mke2fs clean + dist: $(KOS_MAKE) -C ext2fs dist + $(KOS_MAKE) -C mke2fs dist + diff --git a/examples/dreamcast/sd/ext2fs/Makefile b/examples/dreamcast/sd/mke2fs/Makefile similarity index 51% copy from examples/dreamcast/sd/ext2fs/Makefile copy to examples/dreamcast/sd/mke2fs/Makefile index 8d6a71d..75a0045 100644 --- a/examples/dreamcast/sd/ext2fs/Makefile +++ b/examples/dreamcast/sd/mke2fs/Makefile @@ -1,13 +1,14 @@ # KallistiOS ##version## # -# examples/dreamcast/sd/ext2fs/Makefile +# examples/dreamcast/sd/mke2fs/Makefile # -TARGET = sd-ext2fs.elf -OBJS = sd-ext2fs.o +TARGET = mke2fs.elf +OBJS = mke2fs.o -# Uncomment the next line to enable write support in the example. -#KOS_CFLAGS += -DENABLE_WRITE +# We need the private headers from libkosext2fs, since we're not using the +# facilities of fs_ext2 here. +KOS_CFLAGS += -I$(KOS_BASE)/addons/libkosext2fs -Werror -W -std=gnu99 all: rm-elf $(TARGET) @@ -20,7 +21,7 @@ rm-elf: -rm -f $(TARGET) romdisk.* $(TARGET): $(OBJS) - kos-cc -o $(TARGET) $(OBJS) -lkosext2fs + kos-cc -o $(TARGET) $(OBJS) run: $(TARGET) $(KOS_LOADER) $(TARGET) diff --git a/examples/dreamcast/sd/mke2fs/Makefile.nonkos b/examples/dreamcast/sd/mke2fs/Makefile.nonkos new file mode 100644 index 0000000..c824416 --- /dev/null +++ b/examples/dreamcast/sd/mke2fs/Makefile.nonkos @@ -0,0 +1,17 @@ +# KallistiOS ##version## +# +# mke2fs/Makefile.nonkos +# Copyright (C) 2013 Lawrence Sebald +# + +all: mke2fs.kos +CFLAGS += -I$(KOS_BASE)/addons/libkosext2fs -DEXT2_NOT_IN_KOS -Wall -std=gnu99 +#CFLAGS += -DEXT2FS_DEBUG + +mke2fs.kos: mke2fs.c + $(CC) $(CFLAGS) -g -o mke2fs.kos mke2fs.c + +clean: + -rm -f mke2fs.kos + -rm -rf mke2fs.kos.dSYM + diff --git a/examples/dreamcast/sd/mke2fs/mke2fs.c b/examples/dreamcast/sd/mke2fs/mke2fs.c new file mode 100644 index 0000000..18fcd6e --- /dev/null +++ b/examples/dreamcast/sd/mke2fs/mke2fs.c @@ -0,0 +1,991 @@ +/* KallistiOS ##version## + + mke2fs.c + Copyright (C) 2013 Lawrence Sebald + + This example shows how to format a SD card with a new ext2 filesystem using + pretty much no functionality from libkosext2fs (other than the definitions in + the headers). No functions in the library itself are called (hence the + library isn't linked in). + + At some point I'll probably move some of this functionality into libkosext2fs + so that there's less manual work to be done, but for the time being, this + gets the job done. +*/ + +#include <time.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <unistd.h> + +#ifdef _arch_dreamcast +#include <kos/dbgio.h> +#include <kos/blockdev.h> + +#include <arch/arch.h> + +#include <dc/sd.h> +#include <dc/maple.h> +#include <dc/maple/controller.h> +#endif + +#include "ext2fs.h" +#include "block.h" +#include "inode.h" +#include "superblock.h" +#include "utils.h" + +#define KiB 1024LLU +#define MiB (KiB * 1024LLU) +#define GiB (MiB * 1024LLU) + +/* We don't make any blocks more than 4KiB in size, so this works as a full-size + block buffer quite nicely. */ +static uint8_t block[4096] __attribute__((aligned(4))); + +static ext2_bg_desc_t *bg_descs; +static int bg_count; +static uint32_t *rsvd_inodes, *rsvd_blocks; + +/* <= 128MiB -> 1024 byte blocks + <= 4GiB -> 2048 byte blocks + > 4GiB -> 4096 byte blocks + + While libkosext2fs should handle larger block sizes than 4096 bytes, Linux on + most platforms will not, so we don't go above it. + + Note that these ranges are somewhat arbitrary, but work out nicely. */ +static inline uint32_t pick_ext2_bs(uint64_t total_size) { + if(total_size > 4 * GiB) + return 4096; + else if(total_size > 128 * MiB) + return 2048; + else + return 1024; +} + +static inline int sb_tst(int group, int root) { + for(;;) { + if(group == 1) + return 1; + if(group % root) + return 0; + group /= root; + } +} + +static inline int has_superblock(int group) { + if(group == 0 || sb_tst(group, 3) || sb_tst(group, 5) || sb_tst(group, 7)) + return 1; + return 0; +} + +static void __attribute__((__noreturn__)) exit_with_error(const char *err) { +#ifdef _arch_dreamcast + maple_device_t *dev; + cont_state_t *state; + + printf("%s\n\nPress any button to exit.\n", err); + + for(;;) { + dev = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); + + if(dev) { + state = (cont_state_t *)maple_dev_status(dev); + + if(state) { + if(state->buttons) + arch_exit(); + } + } + } +#else + printf("%s\n", err); + exit(EXIT_FAILURE); +#endif +} + +static int write_fs_block(ext2_superblock_t *sb, kos_blockdev_t *bd, + uint32_t block_num, const uint8_t *buf) { + int fs_per_block = 10 + sb->s_log_block_size - bd->l_block_size; + + if(fs_per_block < 0) + /* This should never happen, as the ext2 block size must be at least + as large as the sector size of the block device itself. */ + return -EINVAL; + + if(sb->s_blocks_count <= block_num) + return -EINVAL; + + if(bd->write_blocks(bd, block_num << fs_per_block, 1 << fs_per_block, buf)) + return -EIO; + + return 0; +} + +static int read_fs_block(ext2_superblock_t *sb, kos_blockdev_t *bd, + uint32_t block_num, uint8_t *buf) { + int fs_per_block = 10 + sb->s_log_block_size - bd->l_block_size; + + if(fs_per_block < 0) + /* This should never happen, as the ext2 block size must be at least + as large as the sector size of the block device itself. */ + return -EINVAL; + + if(sb->s_blocks_count <= block_num) + return -EINVAL; + + if(bd->read_blocks(bd, block_num << fs_per_block, 1 << fs_per_block, buf)) + return -EIO; + + return 0; +} + +static int read_inode_block(ext2_superblock_t *sb, kos_blockdev_t *bd, + uint32_t inode_num, uint8_t *buf, + ext2_inode_t **rino, uint32_t *rblk) { + uint32_t bg, index, blk; + uint16_t ino_sz; + uint32_t block_size = 1024 << sb->s_log_block_size; + int rv; + + if(sb->s_rev_level >= EXT2_DYNAMIC_REV) + ino_sz = sb->s_inode_size; + else + ino_sz = 128; + + bg = (inode_num - 1) / sb->s_inodes_per_group; + index = (inode_num - 1) % sb->s_inodes_per_group; + + blk = index / (block_size / ino_sz); + index %= (block_size / ino_sz); + *rblk = blk + bg_descs[bg].bg_inode_table; + + if((rv = read_fs_block(sb, bd, *rblk, buf))) + return rv; + + *rino = (ext2_inode_t *)(buf + index * ino_sz); + + return 0; +} + +static int write_superblock(ext2_superblock_t *bsb, kos_blockdev_t *bd, + uint32_t bg) { + uint8_t *buf; + ext2_superblock_t *sb; + uint32_t blk, nblks; + int fs_per_block = 10 + bsb->s_log_block_size - bd->l_block_size; + int rv; + + /* Allocate enough space for one filesystem block. */ + if(!(buf = (uint8_t *)malloc(1024 << bsb->s_log_block_size))) + return -ENOMEM; + + sb = (ext2_superblock_t *)buf; + memset(buf, 0, 1024 << bsb->s_log_block_size); + + /* If we're working with the first block group, we need to offset within + the block, potentially. */ + if(!bg) { + if(bd->l_block_size > 10) { + /* Read what's there already, in case we have a boot block or some + other nonsense. */ + if(bd->read_blocks(bd, 0, 1, buf)) + return -EIO; + + /* Fix the pointer. */ + sb = (ext2_superblock_t *)(buf + 1024); + + /* Clear out anything after the superblock */ + if(bd->l_block_size > 11) + memset(buf + 2048, 0, (1 << bd->l_block_size) - 2048); + + blk = 0; + nblks = 1; + } + else { + nblks = blk = 1024 >> bd->l_block_size; + } + } + else { + blk = (bg * bsb->s_blocks_per_group + bsb->s_first_data_block) << + (fs_per_block); + nblks = 1 << fs_per_block; + } + + /* Copy in the superblock */ + memcpy(sb, bsb, sizeof(ext2_superblock_t)); + + /* Fix things up, depending on the revision of the filesystem. */ + if(bsb->s_rev_level >= EXT2_DYNAMIC_REV) + /* Write the block group number. */ + sb->s_block_group_nr = (uint16_t)bg; + else + /* Clear everything that's not in rev0 out. */ + memset(&sb->s_first_ino, 0, 176); + + printf("Writing superblock for group %" PRIu32 " @ %" PRIu32 "\n", bg, + blk >> fs_per_block); + + /* Write the data. */ + rv = bd->write_blocks(bd, blk, nblks, buf); + + /* Clean up, we're done. */ + free(buf); + return rv; +} + +static int write_bg_descs(ext2_superblock_t *sb, kos_blockdev_t *bd, + uint32_t bg) { + uint32_t blk, nblks, bg_per_block, i; + uint32_t block_size = (1024 << sb->s_log_block_size); + uint8_t *buf; + int rv = 0; + + /* The block group descriptors appear right after the superblock (backup) */ + blk = bg * sb->s_blocks_per_group + sb->s_first_data_block + 1; + + /* Figure out how big each superblock (or backup thereof) is */ + bg_per_block = block_size / sizeof(ext2_bg_desc_t); + nblks = bg_count / bg_per_block; + + if(bg_count % bg_per_block) + ++nblks; + + if(!(buf = (uint8_t *)malloc(block_size * nblks))) + return -ENOMEM; + + memset(buf, 0, block_size * nblks); + memcpy(buf, bg_descs, bg_count * sizeof(ext2_bg_desc_t)); + + printf("Writing block group descriptors for group %" PRIu32 " @ %" PRIu32 + " (%" PRIu32 " block(s))\n", bg, blk, nblks); + + /* Write them */ + for(i = 0; i < nblks && !rv; ++i) { + rv = write_fs_block(sb, bd, blk + i, buf); + } + + free(buf); + return rv; +} + +static int write_superblocks(ext2_superblock_t *sb, kos_blockdev_t *bd) { + int i; + + printf("Writing superblocks\n"); + + if(sb->s_rev_level < EXT2_DYNAMIC_REV || + !(sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + for(i = 0; i < bg_count; ++i) { + if(write_superblock(sb, bd, i)) + exit_with_error("Error writing superblock\n"); + + if(write_bg_descs(sb, bd, i)) + exit_with_error("Error writing block group descriptors\n"); + } + } + else { + if(write_superblock(sb, bd, 0)) + exit_with_error("Error writing superblock\n"); + + if(write_bg_descs(sb, bd, 0)) + exit_with_error("Error writing block group descriptors\n"); + + for(i = 1; i < bg_count; ++i) { + if(has_superblock(i)) { + if(write_superblock(sb, bd, i)) + exit_with_error("Error writing superblock\n"); + + if(write_bg_descs(sb, bd, i)) + exit_with_error("Error writing block group descriptors\n"); + } + } + } + + return -ENOSYS; +} + +static int write_blank_inode_tables(ext2_superblock_t *sb, kos_blockdev_t *bd) { + int i, rv; + uint32_t j, blk; + + memset(block, 0, 1024 << sb->s_log_block_size); + + for(i = 0; i < bg_count; ++i) { + blk = i * sb->s_blocks_per_group + rsvd_blocks[i] + + sb->s_first_data_block; + printf("Writing inode tables for block group %d\n" + "\t%" PRIu32 " blocks (%" PRIu16 " inodes), start @ block %" + PRIu32 "\n", i, rsvd_inodes[i], bg_descs[i].bg_free_inodes_count, + blk); + bg_descs[i].bg_inode_table = blk; + + for(j = 0; j < rsvd_inodes[i]; ++j) { + if((rv = write_fs_block(sb, bd, blk++, block))) + exit_with_error("Error writing inode tables!\n"); + } + } + + return 0; +} + +static int create_bg_descs(ext2_superblock_t *sb) { + uint32_t bc = sb->s_blocks_count - sb->s_first_data_block; + int odd_count = 0, i; + + /* Figure out how many block groups we'll have. */ + bg_count = bc / sb->s_blocks_per_group; + if(bc % sb->s_blocks_per_group) { + odd_count = 1; + ++bg_count; + } + + /* Allocate space for them */ + if(!(bg_descs = (ext2_bg_desc_t *)malloc(sizeof(ext2_bg_desc_t) * + bg_count))) + return -ENOMEM; + + if(!(rsvd_blocks = (uint32_t *)malloc(sizeof(uint32_t) * bg_count))) { + free(bg_descs); + return -ENOMEM; + } + + if(!(rsvd_inodes = (uint32_t *)malloc(sizeof(uint32_t) * bg_count))) { + free(rsvd_blocks); + free(bg_descs); + return -ENOMEM; + } + + memset(bg_descs, 0, sizeof(ext2_bg_desc_t) * bg_count); + memset(rsvd_blocks, 0, sizeof(uint32_t) * bg_count); + memset(rsvd_inodes, 0, sizeof(uint32_t) * bg_count); + + sb->s_inodes_per_group = ((sb->s_inodes_count / bg_count) + 7) & ~7; + sb->s_inodes_count = sb->s_inodes_per_group * bg_count; + sb->s_free_inodes_count = sb->s_inodes_count - 11; + + /* Set up what we know for sure, we'll get the rest later. */ + for(i = 0; i < bg_count - odd_count; ++i) { + bg_descs[i].bg_free_blocks_count = sb->s_blocks_per_group; + bg_descs[i].bg_free_inodes_count = sb->s_inodes_per_group; + } + + /* Handle the last group specially... */ + if(odd_count) { + bg_descs[i].bg_free_blocks_count = sb->s_blocks_count - + (sb->s_blocks_per_group * (bg_count - 1)) - + sb->s_first_data_block; + bg_descs[i].bg_free_inodes_count = sb->s_inodes_per_group; + } + + return 0; +} + +static int reserve_blocks(ext2_superblock_t *sb) { + int i; + uint32_t bc = sb->s_blocks_count - sb->s_first_data_block; + uint32_t block_size = (1024 << sb->s_log_block_size); + uint32_t bg_per_block, sb_blocks, in_per_block, inode_blocks; + uint32_t total_reserved = 0; + + /* Figure out how big each superblock (or backup thereof) is */ + bg_per_block = block_size / sizeof(ext2_bg_desc_t); + sb_blocks = 1 + (bg_count / bg_per_block); + + if(bg_count % bg_per_block) + ++sb_blocks; + + /* Figure out how many blocks we have to reserve beyond that in each + block group for inodes. */ + in_per_block = block_size / sizeof(ext2_inode_t); + inode_blocks = sb->s_inodes_per_group / in_per_block; + + if(sb->s_inodes_per_group % in_per_block) + ++inode_blocks; + + /* Make sure we have sufficient space in the last block group and that we + aren't going to have to readjust things... */ + if(bc % sb->s_blocks_per_group) { + /* Make sure there's actually enough blocks in the last block group to + make things work properly... */ + if(bg_descs[bg_count - 1].bg_free_blocks_count <= + sb_blocks + inode_blocks + 32) { + printf("Dropping last block group due to insufficient space!\n"); + printf("This lowers the filesystem size by %" PRIu16 " blocks\n", + bg_descs[bg_count - 1].bg_free_blocks_count); ...<truncated>... hooks/post-receive -- A pseudo Operating System for the Dreamcast. |