/* * Copyright (c) 2010 Jiri Svoboda * Copyright (c) 2011 Maurizio Lombardi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup fs * @{ */ /** * @file mkminix.c * @brief Tool for creating new Minix file systems. * */ #include #include #include #include #include #include #include #include #include #include #include #define NAME "mkminix" #define FREE 0 #define USED 1 #define UPPER(n, size) (((n) / (size)) + (((n) % (size)) != 0)) typedef enum { HELP_SHORT, HELP_LONG } help_level_t; typedef struct mfs_params { uint16_t fs_magic; uint32_t block_size; size_t devblock_size; unsigned long n_inodes; aoff64_t dev_nblocks; bool fs_longnames; } mfs_params_t; static void help_cmd_mkminix(help_level_t level); static int num_of_set_bits(uint32_t n); static void setup_superblock(struct mfs_superblock *sb, mfs_params_t *opt); static void setup_superblock_v3(struct mfs3_superblock *sb, mfs_params_t *opt); static void setup_bitmaps(devmap_handle_t handle, uint32_t ninodes, uint32_t nzones, int bsize); static void mark_bmap(uint8_t *bmap, int idx, int v); static struct option const long_options[] = { { "help", no_argument, 0, 'h' }, { "long-names", no_argument, 0, 'l' }, { "blocks", required_argument, 0, 'b' }, { "inodes", required_argument, 0, 'i' }, { NULL, no_argument, 0, '1' }, { NULL, no_argument, 0, '2' }, { NULL, no_argument, 0, '3' }, { 0, 0, 0, 0 } }; int main (int argc, char **argv) { int rc, c, opt_ind; char *device_name; devmap_handle_t handle; struct mfs_superblock *sb; struct mfs3_superblock *sb3; mfs_params_t opt; /*Default is MinixFS V3*/ opt.fs_magic = MFS_MAGIC_V3; /*Default block size is 4Kb*/ opt.block_size = MFS_MAX_BLOCKSIZE; opt.n_inodes = 0; opt.fs_longnames = false; if (argc == 1) { help_cmd_mkminix(HELP_SHORT); printf("Incorrect number of arguments, try `mkminix --help'\n"); exit(0); } for (c = 0, optind = 0, opt_ind = 0; c != -1;) { c = getopt_long(argc, argv, "lh123b:i:", long_options, &opt_ind); switch (c) { case 'h': help_cmd_mkminix(HELP_LONG); exit(0); case '1': opt.fs_magic = MFS_MAGIC_V1; opt.block_size = MFS_BLOCKSIZE; break; case '2': opt.fs_magic = MFS_MAGIC_V2; opt.block_size = MFS_BLOCKSIZE; break; case '3': opt.fs_magic = MFS_MAGIC_V3; break; case 'b': opt.block_size = (uint32_t) strtol(optarg, NULL, 10); break; case 'i': opt.n_inodes = (unsigned long) strtol(optarg, NULL, 10); break; case 'l': opt.fs_longnames = true; break; } } if (opt.block_size < MFS_MIN_BLOCKSIZE || opt.block_size > MFS_MAX_BLOCKSIZE) { printf(NAME ":Error! Invalid block size.\n"); exit(0); } else if (num_of_set_bits(opt.block_size) != 1) { /*Block size must be a power of 2.*/ printf(NAME ":Error! Invalid block size.\n"); exit(0); } else if (opt.block_size > MFS_BLOCKSIZE && opt.fs_magic != MFS_MAGIC_V3) { printf(NAME ":Error! Block size > 1024 is supported by V3 filesystem only.\n"); exit(0); } else if (opt.fs_magic == MFS_MAGIC_V3 && opt.fs_longnames) { printf(NAME ":Error! Long filenames are supported by V1/V2 filesystem only.\n"); exit(0); } argv += optind; device_name = argv[0]; if (!device_name) { help_cmd_mkminix(HELP_LONG); exit(0); } rc = devmap_device_get_handle(device_name, &handle, 0); if (rc != EOK) { printf(NAME ": Error resolving device `%s'.\n", device_name); return 2; } rc = block_init(handle, MFS_MIN_BLOCKSIZE); if (rc != EOK) { printf(NAME ": Error initializing libblock.\n"); return 2; } rc = block_get_bsize(handle, &opt.devblock_size); if (rc != EOK) { printf(NAME ": Error determining device block size.\n"); return 2; } rc = block_get_nblocks(handle, &opt.dev_nblocks); if (rc != EOK) { printf(NAME ": Warning, failed to obtain block device size.\n"); } else { printf(NAME ": Block device has %" PRIuOFF64 " blocks.\n", opt.dev_nblocks); } if (opt.devblock_size != 512) { printf(NAME ": Error. Device block size is not 512 bytes.\n"); return 2; } /*Minimum block size is 1 Kb*/ opt.dev_nblocks /= 2; printf(NAME ": Creating Minix file system on device\n"); /*Setting up superblock*/ if (opt.fs_magic == MFS_MAGIC_V3) { sb3 = (struct mfs3_superblock *) malloc(sizeof(struct mfs3_superblock)); if (!sb3) { printf(NAME ": Error, not enough memory"); return 2; } setup_superblock_v3(sb3, &opt); block_write_direct(handle, MFS_SUPERBLOCK, 1, sb3); setup_bitmaps(handle, sb3->s_ninodes, sb3->s_nzones, sb3->s_block_size); } else { sb = (struct mfs_superblock *) malloc(sizeof(struct mfs_superblock)); if (!sb) { printf(NAME ": Error, not enough memory"); return 2; } setup_superblock(sb, &opt); block_write_direct(handle, MFS_SUPERBLOCK, 1, sb); setup_bitmaps(handle, sb->s_ninodes, sb->s_nzones, MFS_BLOCKSIZE); } return 0; } static void setup_superblock(struct mfs_superblock *sb, mfs_params_t *opt) { int ino_per_block = 0; int fs_version; aoff64_t inodes; if (opt->fs_magic == MFS_MAGIC_V1) { fs_version = 1; ino_per_block = V1_INODES_PER_BLOCK; if (opt->fs_longnames) opt->fs_magic = MFS_MAGIC_V1L; } else { fs_version = 2; ino_per_block = V2_INODES_PER_BLOCK; if (opt->fs_longnames) opt->fs_magic = MFS_MAGIC_V2L; } sb->s_magic = opt->fs_magic; /*Compute the number of zones on disk*/ /*Valid only for MFS V1*/ sb->s_nzones = opt->dev_nblocks > UINT16_MAX ? UINT16_MAX : opt->dev_nblocks; /*Valid only for MFS V2*/ sb->s_nzones2 = opt->dev_nblocks > UINT32_MAX ? UINT32_MAX : opt->dev_nblocks; /*Round up the number of inodes to fill block size*/ if (opt->n_inodes == 0) inodes = opt->dev_nblocks / 3; else inodes = opt->n_inodes; if (inodes % ino_per_block) inodes = ((inodes / ino_per_block) + 1) * ino_per_block; sb->s_ninodes = inodes > UINT16_MAX ? UINT16_MAX : inodes; /*Compute inode bitmap size in blocks*/ sb->s_ibmap_blocks = UPPER(sb->s_ninodes, MFS_BLOCKSIZE * 8); /*Compute zone bitmap size in blocks*/ if (fs_version == 1) sb->s_zbmap_blocks = UPPER(sb->s_nzones, MFS_BLOCKSIZE * 8); else sb->s_zbmap_blocks = UPPER(sb->s_nzones2, MFS_BLOCKSIZE * 8); /*Compute inode table size*/ unsigned long ninodes_blocks = sb->s_ninodes / ino_per_block; /*Compute first data zone position*/ sb->s_first_data_zone = 2 + ninodes_blocks + sb->s_zbmap_blocks + sb->s_ibmap_blocks; /*Set log2 of zone to block ratio to zero*/ sb->s_log2_zone_size = 0; /*Superblock is now ready to be written on disk*/ printf(NAME ": %d inodes\n", sb->s_ninodes); printf(NAME ": %d zones\n", sb->s_nzones2); printf(NAME ": inode table blocks = %ld\n", ninodes_blocks); printf(NAME ": first data zone = %d\n", sb->s_first_data_zone); printf(NAME ": long fnames = %s\n", opt->fs_longnames ? "Yes" : "No"); } static void setup_superblock_v3(struct mfs3_superblock *sb, mfs_params_t *opt) { int ino_per_block; int bs; aoff64_t inodes; sb->s_magic = opt->fs_magic; bs = opt->block_size; if (opt->n_inodes == 0) inodes = opt->dev_nblocks / 3; else inodes = opt->n_inodes; /*Round up the number of inodes to fill block size*/ ino_per_block = V3_INODES_PER_BLOCK(bs); if (inodes % ino_per_block) inodes = ((inodes / ino_per_block) + 1) * ino_per_block; sb->s_ninodes = inodes > UINT32_MAX ? UINT32_MAX : inodes; /*Compute the number of zones on disk*/ sb->s_nzones = opt->dev_nblocks > UINT32_MAX ? UINT32_MAX : opt->dev_nblocks; sb->s_nzones /= (bs / MFS_MIN_BLOCKSIZE); /*Compute inode bitmap size in blocks*/ sb->s_ibmap_blocks = UPPER(sb->s_ninodes, bs * 8); /*Compute zone bitmap size in blocks*/ sb->s_zbmap_blocks = UPPER(sb->s_nzones, bs * 8); /*Compute inode table size*/ unsigned long ninodes_blocks = sb->s_ninodes / ino_per_block; /*Compute first data zone position*/ sb->s_first_data_zone = 2 + ninodes_blocks + sb->s_zbmap_blocks + sb->s_ibmap_blocks; /*Set log2 of zone to block ratio to zero*/ sb->s_log2_zone_size = 0; sb->s_disk_version = 3; sb->s_block_size = bs; /*Superblock is now ready to be written on disk*/ printf(NAME ": %d inodes\n", sb->s_ninodes); printf(NAME ": %d zones\n", sb->s_nzones); printf(NAME ": block size = %d\n", sb->s_block_size); printf(NAME ": inode table blocks = %ld\n", ninodes_blocks); printf(NAME ": first data zone = %d\n", sb->s_first_data_zone); } static void setup_bitmaps(devmap_handle_t handle, uint32_t ninodes, uint32_t nzones, int bsize) { uint8_t *ibmap_buf, *zbmap_buf; int ibmap_nblocks = 1 + (ninodes / 8) / bsize; int zbmap_nblocks = 1 + (nzones / 8) / bsize; unsigned int i; ibmap_buf = (uint8_t *) malloc(ibmap_nblocks * bsize); zbmap_buf = (uint8_t *) malloc(zbmap_nblocks * bsize); memset(ibmap_buf, 0xFF, ibmap_nblocks * bsize); memset(zbmap_buf, 0xFF, zbmap_nblocks * bsize); for (i = 2; i < ninodes; ++i) { mark_bmap(ibmap_buf, i, FREE); mark_bmap(zbmap_buf, i, FREE); } ibmap_nblocks *= bsize / MFS_BLOCKSIZE; zbmap_nblocks *= bsize / MFS_BLOCKSIZE; block_write_direct(handle, 2, ibmap_nblocks, ibmap_buf); block_write_direct(handle, 2 + ibmap_nblocks, zbmap_nblocks, zbmap_buf); } static void mark_bmap(uint8_t *bmap, int idx, int v) { if (v == FREE) bmap[idx / 8] &= ~(1 << (idx % 8)); else bmap[idx / 8] |= 1 << (idx % 8); } static void help_cmd_mkminix(help_level_t level) { if (level == HELP_SHORT) { printf(NAME": tool to create new Minix file systems\n"); } else { printf("Usage: [options] device\n" "-1 Make a Minix version 1 filesystem\n" "-2 Make a Minix version 2 filesystem\n" "-3 Make a Minix version 3 filesystem\n" "-b ## Specify the block size in bytes (V3 only),\n" " valid block size values are 1024, 2048 and 4096 bytes per block\n" "-i ## Specify the number of inodes for the filesystem\n" "-l Use 30-char long filenames (V1/V2 only)\n"); } } static int num_of_set_bits(uint32_t n) { n = n - ((n >> 1) & 0x55555555); n = (n & 0x33333333) + ((n >> 2) & 0x33333333); return (((n + (n >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; } /** * @} */