/* * 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 #include #define NAME "mkminix" #define FREE 0 #define USED 1 #define UPPER(n, size) (((n) / (size)) + (((n) % (size)) != 0)) #define NEXT_DENTRY(p, dirsize) (p += dirsize) #define FIRST_ZONE(bs) (1 + (bs) / MFS_MIN_BLOCKSIZE) #define CONVERT_1K_OFF(off, bs) ((off) * ((bs) / MFS_MIN_BLOCKSIZE)) typedef enum { HELP_SHORT, HELP_LONG } help_level_t; /*Generic MFS superblock*/ struct mfs_sb_info { uint64_t n_inodes; uint64_t n_zones; aoff64_t dev_nblocks; unsigned long ibmap_blocks; unsigned long zbmap_blocks; unsigned long first_data_zone; unsigned long itable_size; int log2_zone_size; int ino_per_block; int dirsize; uint32_t max_file_size; uint16_t magic; uint32_t block_size; int fs_version; bool longnames; }; static void help_cmd_mkminix(help_level_t level); static int num_of_set_bits(uint32_t n); static int init_superblock(struct mfs_sb_info *sb); static int write_superblock(const struct mfs_sb_info *sbi); static int write_superblock3(const struct mfs_sb_info *sbi); static int init_bitmaps(const struct mfs_sb_info *sb); static int init_inode_table(const struct mfs_sb_info *sb); static int make_root_ino(const struct mfs_sb_info *sb); static int make_root_ino3(const struct mfs_sb_info *sb); static void mark_bmap(uint32_t *bmap, int idx, int v); static int insert_dentries(const struct mfs_sb_info *sb); static inline int write_1k_block(aoff64_t off, size_t size, const void *data); static devmap_handle_t handle; static struct option const long_options[] = { { "help", no_argument, 0, 'h' }, { "long-names", no_argument, 0, 'l' }, { "block-size", 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; aoff64_t devblock_size; struct mfs_sb_info sb; /*Default is MinixFS V3*/ sb.magic = MFS_MAGIC_V3; sb.fs_version = 3; /*Default block size is 4Kb*/ sb.block_size = MFS_MAX_BLOCKSIZE; sb.dirsize = MFS3_DIRSIZE; sb.n_inodes = 0; sb.longnames = false; sb.ino_per_block = V3_INODES_PER_BLOCK(MFS_MAX_BLOCKSIZE); 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': sb.magic = MFS_MAGIC_V1; sb.block_size = MFS_BLOCKSIZE; sb.fs_version = 1; sb.ino_per_block = V1_INODES_PER_BLOCK; sb.dirsize = MFS_DIRSIZE; break; case '2': sb.magic = MFS_MAGIC_V2; sb.block_size = MFS_BLOCKSIZE; sb.fs_version = 2; sb.ino_per_block = V2_INODES_PER_BLOCK; sb.dirsize = MFS_DIRSIZE; break; case '3': sb.magic = MFS_MAGIC_V3; sb.fs_version = 3; sb.block_size = MFS_MAX_BLOCKSIZE; sb.dirsize = MFS3_DIRSIZE; break; case 'b': sb.block_size = (uint32_t) strtol(optarg, NULL, 10); break; case 'i': sb.n_inodes = (uint64_t) strtol(optarg, NULL, 10); break; case 'l': sb.longnames = true; sb.dirsize = MFSL_DIRSIZE; break; } } if (sb.block_size < MFS_MIN_BLOCKSIZE || sb.block_size > MFS_MAX_BLOCKSIZE) { printf(NAME ":Error! Invalid block size.\n"); exit(0); } else if (num_of_set_bits(sb.block_size) != 1) { /*Block size must be a power of 2.*/ printf(NAME ":Error! Invalid block size.\n"); exit(0); } else if (sb.block_size > MFS_BLOCKSIZE && sb.fs_version != 3) { printf(NAME ":Error! Block size > 1024 is supported by V3 filesystem only.\n"); exit(0); } else if (sb.fs_version == 3 && sb.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, &devblock_size); if (rc != EOK) { printf(NAME ": Error determining device block size.\n"); return 2; } rc = block_get_nblocks(handle, &sb.dev_nblocks); if (rc != EOK) { printf(NAME ": Warning, failed to obtain block device size.\n"); } else { printf(NAME ": Block device has %" PRIuOFF64 " blocks.\n", sb.dev_nblocks); } if (devblock_size != 512) { printf(NAME ": Error. Device block size is not 512 bytes.\n"); return 2; } /*Minimum block size is 1 Kb*/ sb.dev_nblocks /= 2; printf(NAME ": Creating Minix file system on device\n"); /*Initialize superblock*/ if (init_superblock(&sb) != EOK) { printf(NAME ": Error. Superblock initialization failed\n"); return 2; } /*Initialize bitmaps*/ if (init_bitmaps(&sb) != EOK) { printf(NAME ": Error. Bitmaps initialization failed\n"); return 2; } /*Init inode table*/ if (init_inode_table(&sb) != EOK) { printf(NAME ": Error. Inode table initialization failed\n"); return 2; } /*Make the root inode*/ if (sb.fs_version == 3) rc = make_root_ino3(&sb); else rc = make_root_ino(&sb); if (rc != EOK) { printf(NAME ": Error. Root inode initialization failed\n"); return 2; } /*Insert directory entries . and ..*/ if (insert_dentries(&sb) != EOK) { printf(NAME ": Error. Root directory initialization failed\n"); return 2; } return 0; } static int insert_dentries(const struct mfs_sb_info *sb) { void *root_block; int rc; const long root_dblock = CONVERT_1K_OFF(sb->first_data_zone, sb->block_size); root_block = (void *) malloc(MFS_MIN_BLOCKSIZE); memset(root_block, 0x00, MFS_MIN_BLOCKSIZE); if (!root_block) return ENOMEM; if (sb->fs_version != 3) { /*Directory entries for V1/V2 filesystem*/ struct mfs_dentry *dentry = root_block; dentry->d_inum = MFS_ROOT_INO; memcpy(dentry->d_name, ".\0", 2); NEXT_DENTRY(dentry, sb->dirsize); dentry->d_inum = MFS_ROOT_INO; memcpy(dentry->d_name, "..\0", 3); } else { /*Directory entries for V3 filesystem*/ struct mfs3_dentry *dentry = root_block; dentry->d_inum = MFS_ROOT_INO; memcpy(dentry->d_name, ".\0", 2); NEXT_DENTRY(dentry, sb->dirsize); dentry->d_inum = MFS_ROOT_INO; memcpy(dentry->d_name, "..\0", 3); } rc = write_1k_block(root_dblock, 1, root_block); free(root_block); return rc; } static int init_inode_table(const struct mfs_sb_info *sb) { unsigned int i; uint8_t *itable_buf; int rc; long itable_off; unsigned long itable_size; itable_off = sb->zbmap_blocks + sb->ibmap_blocks; /*Convert to 1K offset*/ itable_off = CONVERT_1K_OFF(itable_off, sb->block_size) + FIRST_ZONE(sb->block_size); itable_size = CONVERT_1K_OFF(sb->itable_size, sb->block_size); itable_buf = malloc(MFS_MIN_BLOCKSIZE); if (!itable_buf) return ENOMEM; memset(itable_buf, 0x00, MFS_MIN_BLOCKSIZE); for (i = 0; i < itable_size; ++i, ++itable_off) { rc = write_1k_block(itable_off, 1, itable_buf); if (rc != EOK) break; } free(itable_buf); return rc; } static int make_root_ino(const struct mfs_sb_info *sb) { struct mfs_inode *ino_buf; long itable_off; int rc; itable_off = FIRST_ZONE(MFS_BLOCKSIZE); itable_off += sb->zbmap_blocks + sb->ibmap_blocks; const time_t sec = time(NULL); ino_buf = (struct mfs_inode *) malloc(MFS_BLOCKSIZE); if (!ino_buf) return ENOMEM; memset(ino_buf, 0x00, MFS_BLOCKSIZE); ino_buf[MFS_ROOT_INO].i_mode = S_IFDIR; ino_buf[MFS_ROOT_INO].i_uid = 0; ino_buf[MFS_ROOT_INO].i_gid = 0; ino_buf[MFS_ROOT_INO].i_size = (sb->longnames ? MFSL_DIRSIZE : MFS_DIRSIZE) * 2; ino_buf[MFS_ROOT_INO].i_mtime = sec; ino_buf[MFS_ROOT_INO].i_nlinks = 2; ino_buf[MFS_ROOT_INO].i_dzone[0] = sb->first_data_zone; rc = write_1k_block(itable_off, 1, ino_buf); free(ino_buf); return rc; } static int make_root_ino3(const struct mfs_sb_info *sb) { struct mfs2_inode *ino_buf; long itable_off; int rc; /*Compute offset of the first inode table block*/ itable_off = sb->zbmap_blocks + sb->ibmap_blocks; /*Convert to 1K block offset*/ itable_off = CONVERT_1K_OFF(itable_off, sb->block_size) + FIRST_ZONE(sb->block_size); const time_t sec = time(NULL); ino_buf = (struct mfs2_inode *) malloc(MFS_MIN_BLOCKSIZE); if (!ino_buf) return ENOMEM; memset(ino_buf, 0x00, MFS_MIN_BLOCKSIZE); ino_buf[MFS_ROOT_INO].i_mode = S_IFDIR; ino_buf[MFS_ROOT_INO].i_uid = 0; ino_buf[MFS_ROOT_INO].i_gid = 0; ino_buf[MFS_ROOT_INO].i_size = MFS3_DIRSIZE * 2; ino_buf[MFS_ROOT_INO].i_mtime = sec; ino_buf[MFS_ROOT_INO].i_atime = sec; ino_buf[MFS_ROOT_INO].i_ctime = sec; ino_buf[MFS_ROOT_INO].i_nlinks = 2; ino_buf[MFS_ROOT_INO].i_dzone[0] = sb->first_data_zone; rc = write_1k_block(itable_off, 1, ino_buf); free(ino_buf); return rc; } static int init_superblock(struct mfs_sb_info *sb) { aoff64_t inodes; int rc; if (sb->longnames) sb->magic = sb->fs_version == 1 ? MFS_MAGIC_V1L : MFS_MAGIC_V2L; /*Compute the number of zones on disk*/ if (sb->fs_version == 1) { /*Valid only for MFS V1*/ sb->n_zones = sb->dev_nblocks > UINT16_MAX ? UINT16_MAX : sb->dev_nblocks; } else { /*Valid for MFS V2/V3*/ sb->n_zones = sb->dev_nblocks > UINT32_MAX ? UINT32_MAX : sb->dev_nblocks; if (sb->fs_version == 3) { sb->ino_per_block = V3_INODES_PER_BLOCK(sb->block_size); sb->n_zones /= (sb->block_size / MFS_MIN_BLOCKSIZE); } } /*Round up the number of inodes to fill block size*/ if (sb->n_inodes == 0) inodes = sb->dev_nblocks / 3; else inodes = sb->n_inodes; if (inodes % sb->ino_per_block) inodes = ((inodes / sb->ino_per_block) + 1) * sb->ino_per_block; if (sb->fs_version < 3) sb->n_inodes = inodes > UINT16_MAX ? UINT16_MAX : inodes; else sb->n_inodes = inodes > UINT32_MAX ? UINT32_MAX : inodes; /*Compute inode bitmap size in blocks*/ sb->ibmap_blocks = UPPER(sb->n_inodes, sb->block_size * 8); /*Compute inode table size*/ sb->itable_size = sb->n_inodes / sb->ino_per_block; /*Compute zone bitmap size in blocks*/ sb->zbmap_blocks = UPPER(sb->n_zones, sb->block_size * 8); /*Compute first data zone position*/ sb->first_data_zone = 2 + sb->itable_size + sb->zbmap_blocks + sb->ibmap_blocks; /*Set log2 of zone to block ratio to zero*/ sb->log2_zone_size = 0; /*Check for errors*/ if (sb->first_data_zone >= sb->n_zones) { printf(NAME ": Error! Insufficient disk space"); return ENOMEM; } /*Superblock is now ready to be written on disk*/ printf(NAME ": %d inodes\n", (uint32_t) sb->n_inodes); printf(NAME ": %d zones\n", (uint32_t) sb->n_zones); printf(NAME ": inode table blocks = %ld\n", sb->itable_size); printf(NAME ": inode bitmap blocks = %ld\n", sb->ibmap_blocks); printf(NAME ": zone bitmap blocks = %ld\n", sb->zbmap_blocks); printf(NAME ": first data zone = %d\n", (uint32_t) sb->first_data_zone); printf(NAME ": long fnames = %s\n", sb->longnames ? "Yes" : "No"); if (sb->fs_version == 3) rc = write_superblock3(sb); else rc = write_superblock(sb); return rc; } static int write_superblock(const struct mfs_sb_info *sbi) { struct mfs_superblock *sb; int rc; sb = (struct mfs_superblock *) malloc(MFS_SUPERBLOCK_SIZE);; if (!sb) return ENOMEM; sb->s_ninodes = (uint16_t) sbi->n_inodes; sb->s_nzones = (uint16_t) sbi->n_zones; sb->s_nzones2 = (uint32_t) sbi->n_zones; sb->s_ibmap_blocks = (uint16_t) sbi->ibmap_blocks; sb->s_zbmap_blocks = (uint16_t) sbi->zbmap_blocks; sb->s_first_data_zone = (uint16_t) sbi->first_data_zone; sb->s_log2_zone_size = sbi->log2_zone_size; sb->s_max_file_size = UINT32_MAX; sb->s_magic = sbi->magic; sb->s_state = MFS_VALID_FS; rc = write_1k_block(MFS_SUPERBLOCK, 1, sb); free(sb); return rc; } static int write_superblock3(const struct mfs_sb_info *sbi) { struct mfs3_superblock *sb; int rc; sb = (struct mfs3_superblock *) malloc(MFS_SUPERBLOCK_SIZE); if (!sb) return ENOMEM; sb->s_ninodes = (uint32_t) sbi->n_inodes; sb->s_nzones = (uint32_t) sbi->n_zones; sb->s_ibmap_blocks = (uint16_t) sbi->ibmap_blocks; sb->s_zbmap_blocks = (uint16_t) sbi->zbmap_blocks; sb->s_first_data_zone = (uint16_t) sbi->first_data_zone; sb->s_log2_zone_size = sbi->log2_zone_size; sb->s_max_file_size = UINT32_MAX; sb->s_magic = sbi->magic; sb->s_block_size = sbi->block_size; sb->s_disk_version = 3; rc = write_1k_block(MFS_SUPERBLOCK, 1, sb); free(sb); return rc; } static int init_bitmaps(const struct mfs_sb_info *sb) { uint32_t *ibmap_buf, *zbmap_buf; uint8_t *ibmap_buf8, *zbmap_buf8; unsigned int ibmap_nblocks = sb->ibmap_blocks; unsigned int zbmap_nblocks = sb->zbmap_blocks; unsigned int i; int rc; ibmap_buf = (uint32_t *) malloc(ibmap_nblocks * sb->block_size); zbmap_buf = (uint32_t *) malloc(zbmap_nblocks * sb->block_size); if (!ibmap_buf || !zbmap_buf) return ENOMEM; memset(ibmap_buf, 0xFF, ibmap_nblocks * sb->block_size); memset(zbmap_buf, 0xFF, zbmap_nblocks * sb->block_size); for (i = 2; i < sb->n_inodes; ++i) mark_bmap(ibmap_buf, i, FREE); for (i = sb->first_data_zone + 1; i < sb->n_zones; ++i) mark_bmap(zbmap_buf, i, FREE); /*Convert to 1K block offsets*/ ibmap_nblocks = CONVERT_1K_OFF(ibmap_nblocks, sb->block_size); zbmap_nblocks = CONVERT_1K_OFF(zbmap_nblocks, sb->block_size); ibmap_buf8 = (uint8_t *) ibmap_buf; zbmap_buf8 = (uint8_t *) zbmap_buf; int start_block = FIRST_ZONE(sb->block_size); for (i = 0; i < ibmap_nblocks; ++i) { if ((rc = write_1k_block(start_block + i, 1, (ibmap_buf8 + i * MFS_BLOCKSIZE))) != EOK) return rc; } start_block = FIRST_ZONE(sb->block_size) + ibmap_nblocks; for (i = 0; i < zbmap_nblocks; ++i) { if ((rc = write_1k_block(start_block + i, 1, (zbmap_buf8 + i * MFS_BLOCKSIZE))) != EOK) return rc; } free(ibmap_buf); free(zbmap_buf); return rc; } static void mark_bmap(uint32_t *bmap, int idx, int v) { if (v == FREE) bmap[idx / 32] &= ~(1 << (idx % 32)); else bmap[idx / 32] |= 1 << (idx % 32); } static inline int write_1k_block(aoff64_t off, size_t size, const void *data) { return block_write_direct(handle, off * 2, size * 2, data); } 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; } /** * @} */