2 * Copyright (c) 2011 Maurizio Lombardi
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 * @brief Tool for creating new Minix file systems.
43 #include <sys/typefmt.h>
56 #define UPPER(n, size) (((n) / (size)) + (((n) % (size)) != 0))
57 #define NEXT_DENTRY(p, dirsize) (p += (dirsize))
64 /* Generic MFS superblock */
69 unsigned long ibmap_blocks
;
70 unsigned long zbmap_blocks
;
71 unsigned long first_data_zone
;
72 unsigned long itable_size
;
76 uint32_t max_file_size
;
83 static void help_cmd_mkmfs(help_level_t level
);
84 static bool is_power_of_two(uint32_t n
);
85 static int init_superblock(struct mfs_sb_info
*sb
);
86 static int write_superblock(const struct mfs_sb_info
*sbi
);
87 static int write_superblock3(const struct mfs_sb_info
*sbi
);
88 static int init_bitmaps(const struct mfs_sb_info
*sb
);
89 static int init_inode_table(const struct mfs_sb_info
*sb
);
90 static int make_root_ino(const struct mfs_sb_info
*sb
);
91 static int make_root_ino2(const struct mfs_sb_info
*sb
);
92 static void mark_bmap(uint32_t *bmap
, int idx
, int v
);
93 static int insert_dentries(const struct mfs_sb_info
*sb
);
95 static inline int write_block(aoff64_t off
, size_t size
, const void *data
);
97 static service_id_t service_id
;
100 static struct option
const long_options
[] = {
101 { "help", no_argument
, 0, 'h' },
102 { "long-names", no_argument
, 0, 'l' },
103 { "block-size", required_argument
, 0, 'b' },
104 { "inodes", required_argument
, 0, 'i' },
105 { NULL
, no_argument
, 0, '1' },
106 { NULL
, no_argument
, 0, '2' },
110 int main (int argc
, char **argv
)
114 size_t devblock_size
;
116 struct mfs_sb_info sb
;
118 /* Default is MinixFS V3 */
119 sb
.magic
= MFS_MAGIC_V3
;
122 /* Default block size is 4Kb */
123 sb
.block_size
= MFS_MAX_BLOCKSIZE
;
124 sb
.dirsize
= MFS3_DIRSIZE
;
126 sb
.longnames
= false;
127 sb
.ino_per_block
= V3_INODES_PER_BLOCK(MFS_MAX_BLOCKSIZE
);
130 help_cmd_mkmfs(HELP_SHORT
);
131 printf("Incorrect number of arguments, try `mkmfs --help'\n");
135 for (c
= 0, optind
= 0, opt_ind
= 0; c
!= -1;) {
136 c
= getopt_long(argc
, argv
, "lh12b:i:",
137 long_options
, &opt_ind
);
140 help_cmd_mkmfs(HELP_LONG
);
143 sb
.magic
= MFS_MAGIC_V1
;
144 sb
.block_size
= MFS_BLOCKSIZE
;
146 sb
.ino_per_block
= V1_INODES_PER_BLOCK
;
147 sb
.dirsize
= MFS_DIRSIZE
;
150 sb
.magic
= MFS_MAGIC_V2
;
151 sb
.block_size
= MFS_BLOCKSIZE
;
153 sb
.ino_per_block
= V2_INODES_PER_BLOCK
;
154 sb
.dirsize
= MFS_DIRSIZE
;
157 sb
.block_size
= (uint32_t) strtol(optarg
, NULL
, 10);
160 sb
.n_inodes
= (uint64_t) strtol(optarg
, NULL
, 10);
164 sb
.dirsize
= MFSL_DIRSIZE
;
169 if (sb
.block_size
< MFS_MIN_BLOCKSIZE
||
170 sb
.block_size
> MFS_MAX_BLOCKSIZE
) {
171 printf(NAME
":Error! Invalid block size.\n");
173 } else if (!is_power_of_two(sb
.block_size
)) {
174 /* Block size must be a power of 2. */
175 printf(NAME
":Error! Invalid block size.\n");
177 } else if (sb
.block_size
> MFS_BLOCKSIZE
&&
178 sb
.fs_version
!= 3) {
179 printf(NAME
":Error! Block size > 1024 is "
180 "supported by V3 filesystem only.\n");
182 } else if (sb
.fs_version
== 3 && sb
.longnames
) {
183 printf(NAME
":Error! Long filenames are supported "
184 "by V1/V2 filesystem only.\n");
188 if (sb
.block_size
== MFS_MIN_BLOCKSIZE
)
190 else if (sb
.block_size
== MFS_MAX_BLOCKSIZE
)
197 device_name
= argv
[0];
200 help_cmd_mkmfs(HELP_LONG
);
204 rc
= loc_service_get_id(device_name
, &service_id
, 0);
206 printf(NAME
": Error resolving device `%s'.\n", device_name
);
210 rc
= block_init(service_id
, 2048);
212 printf(NAME
": Error initializing libblock.\n");
216 rc
= block_get_bsize(service_id
, &devblock_size
);
218 printf(NAME
": Error determining device block size.\n");
222 rc
= block_get_nblocks(service_id
, &sb
.dev_nblocks
);
224 printf(NAME
": Warning, failed to obtain "
225 "block device size.\n");
227 printf(NAME
": Block device has %" PRIuOFF64
" blocks.\n",
231 if (devblock_size
!= 512) {
232 printf(NAME
": Error. Device block size is not 512 bytes.\n");
236 /* Minimum block size is 1 Kb */
239 printf(NAME
": Creating Minix file system on device\n");
240 printf(NAME
": Writing superblock\n");
242 /* Initialize superblock */
243 if (init_superblock(&sb
) != EOK
) {
244 printf(NAME
": Error. Superblock initialization failed\n");
248 printf(NAME
": Initializing bitmaps\n");
250 /* Initialize bitmaps */
251 if (init_bitmaps(&sb
) != EOK
) {
252 printf(NAME
": Error. Bitmaps initialization failed\n");
256 printf(NAME
": Initializing the inode table\n");
258 /* Init inode table */
259 if (init_inode_table(&sb
) != EOK
) {
260 printf(NAME
": Error. Inode table initialization failed\n");
264 printf(NAME
": Creating the root directory inode\n");
266 /* Make the root inode */
267 if (sb
.fs_version
== 1)
268 rc
= make_root_ino(&sb
);
270 rc
= make_root_ino2(&sb
);
273 printf(NAME
": Error. Root inode initialization failed\n");
277 /* Insert directory entries . and .. */
278 if (insert_dentries(&sb
) != EOK
) {
279 printf(NAME
": Error. Root directory initialization failed\n");
283 block_fini(service_id
);
288 /**Inserts the '.' and '..' directory entries in the root directory.
290 * @param sb Pointer to the superblock structure.
292 * @return EOK on success or a negative error code.
294 static int insert_dentries(const struct mfs_sb_info
*sb
)
299 const long root_dblock
= sb
->first_data_zone
;
301 root_block
= malloc(sb
->block_size
);
302 memset(root_block
, 0x00, sb
->block_size
);
307 dentry_ptr
= root_block
;
309 if (sb
->fs_version
!= 3) {
310 /* Directory entries for V1/V2 filesystem */
311 struct mfs_dentry
*dentry
= root_block
;
313 dentry
->d_inum
= MFS_ROOT_INO
;
314 memcpy(dentry
->d_name
, ".\0", 2);
316 dentry
= (struct mfs_dentry
*) NEXT_DENTRY(dentry_ptr
,
319 dentry
->d_inum
= MFS_ROOT_INO
;
320 memcpy(dentry
->d_name
, "..\0", 3);
322 /* Directory entries for V3 filesystem */
323 struct mfs3_dentry
*dentry
= root_block
;
325 dentry
->d_inum
= MFS_ROOT_INO
;
326 memcpy(dentry
->d_name
, ".\0", 2);
328 dentry
= (struct mfs3_dentry
*) NEXT_DENTRY(dentry_ptr
,
331 dentry
->d_inum
= MFS_ROOT_INO
;
332 memcpy(dentry
->d_name
, "..\0", 3);
335 rc
= write_block(root_dblock
, 1, root_block
);
341 /**Initialize the inode table.
343 * @param sb Pointer to the superblock structure.
345 * @return EOK on success or a negative error code.
347 static int init_inode_table(const struct mfs_sb_info
*sb
)
353 long itable_off
= sb
->zbmap_blocks
+ sb
->ibmap_blocks
+ 2;
354 unsigned long itable_size
= sb
->itable_size
;
356 itable_buf
= malloc(sb
->block_size
);
361 memset(itable_buf
, 0x00, sb
->block_size
);
363 for (i
= 0; i
< itable_size
; ++i
, ++itable_off
) {
364 rc
= write_block(itable_off
, 1, itable_buf
);
374 /**Initialize a V1 root inode.
376 * @param sb Ponter to the superblock structure.
378 * @return EOK on success or a negative error code.
380 static int make_root_ino(const struct mfs_sb_info
*sb
)
382 struct mfs_inode
*ino_buf
;
385 const long itable_off
= sb
->zbmap_blocks
+ sb
->ibmap_blocks
+ 2;
387 const time_t sec
= time(NULL
);
389 ino_buf
= malloc(MFS_BLOCKSIZE
);
394 memset(ino_buf
, 0x00, MFS_BLOCKSIZE
);
396 ino_buf
[MFS_ROOT_INO
- 1].i_mode
= S_IFDIR
;
397 ino_buf
[MFS_ROOT_INO
- 1].i_uid
= 0;
398 ino_buf
[MFS_ROOT_INO
- 1].i_gid
= 0;
399 ino_buf
[MFS_ROOT_INO
- 1].i_size
= (sb
->longnames
? MFSL_DIRSIZE
:
401 ino_buf
[MFS_ROOT_INO
- 1].i_mtime
= sec
;
402 ino_buf
[MFS_ROOT_INO
- 1].i_nlinks
= 2;
403 ino_buf
[MFS_ROOT_INO
- 1].i_dzone
[0] = sb
->first_data_zone
;
405 rc
= write_block(itable_off
, 1, ino_buf
);
411 /**Initialize a Minix V2 root inode on disk, also valid for V3 filesystem.
413 * @param sb Pointer to the superblock structure.
415 * @return EOK on success or a negative error code.
417 static int make_root_ino2(const struct mfs_sb_info
*sb
)
419 struct mfs2_inode
*ino_buf
;
422 /* Compute offset of the first inode table block */
423 const long itable_off
= sb
->zbmap_blocks
+ sb
->ibmap_blocks
+ 2;
425 const time_t sec
= time(NULL
);
427 ino_buf
= malloc(sb
->block_size
);
432 memset(ino_buf
, 0x00, sb
->block_size
);
434 ino_buf
[MFS_ROOT_INO
- 1].i_mode
= S_IFDIR
;
435 ino_buf
[MFS_ROOT_INO
- 1].i_uid
= 0;
436 ino_buf
[MFS_ROOT_INO
- 1].i_gid
= 0;
437 ino_buf
[MFS_ROOT_INO
- 1].i_size
= MFS3_DIRSIZE
* 2;
438 ino_buf
[MFS_ROOT_INO
- 1].i_mtime
= sec
;
439 ino_buf
[MFS_ROOT_INO
- 1].i_atime
= sec
;
440 ino_buf
[MFS_ROOT_INO
- 1].i_ctime
= sec
;
441 ino_buf
[MFS_ROOT_INO
- 1].i_nlinks
= 2;
442 ino_buf
[MFS_ROOT_INO
- 1].i_dzone
[0] = sb
->first_data_zone
;
444 rc
= write_block(itable_off
, 1, ino_buf
);
450 /**Initialize the superblock structure on disk.
452 * @param sb Pointer to the superblock structure.
454 * @return EOK on success or a negative error code.
456 static int init_superblock(struct mfs_sb_info
*sb
)
465 sb
->magic
= sb
->fs_version
== 1 ? MFS_MAGIC_V1L
:
468 /* Compute the number of zones on disk */
470 if (sb
->fs_version
== 1) {
471 /* Valid only for MFS V1 */
472 sb
->n_zones
= sb
->dev_nblocks
> UINT16_MAX
?
473 UINT16_MAX
: sb
->dev_nblocks
;
474 ind
= MFS_BLOCKSIZE
/ sizeof(uint16_t);
476 sb
->max_file_size
= (V1_NR_DIRECT_ZONES
+ ind
+ ind2
) *
479 /*Valid for MFS V2/V3*/
481 if (sb
->fs_version
== 2)
482 ptrsize
= sizeof(uint16_t);
484 ptrsize
= sizeof(uint32_t);
486 ind
= sb
->block_size
/ ptrsize
;
488 zones
= V2_NR_DIRECT_ZONES
+ ind
+ ind2
;
489 sb
->max_file_size
= zones
* sb
->block_size
;
490 sb
->n_zones
= sb
->dev_nblocks
> UINT32_MAX
?
491 UINT32_MAX
: sb
->dev_nblocks
;
493 if (sb
->fs_version
== 3) {
494 if(INT32_MAX
/ sb
->block_size
< zones
)
495 sb
->max_file_size
= INT32_MAX
;
496 sb
->ino_per_block
= V3_INODES_PER_BLOCK(sb
->block_size
);
497 sb
->n_zones
/= (sb
->block_size
/ MFS_MIN_BLOCKSIZE
);
501 /* Round up the number of inodes to fill block size */
502 if (sb
->n_inodes
== 0)
503 inodes
= sb
->dev_nblocks
/ 3;
505 inodes
= sb
->n_inodes
;
507 if (inodes
% sb
->ino_per_block
)
508 inodes
= ((inodes
/ sb
->ino_per_block
) + 1) *
511 if (sb
->fs_version
< 3)
512 sb
->n_inodes
= inodes
> UINT16_MAX
? UINT16_MAX
: inodes
;
514 sb
->n_inodes
= inodes
> UINT32_MAX
? UINT32_MAX
: inodes
;
516 /* Compute inode bitmap size in blocks */
517 sb
->ibmap_blocks
= UPPER(sb
->n_inodes
, sb
->block_size
* 8);
519 /* Compute inode table size */
520 sb
->itable_size
= sb
->n_inodes
/ sb
->ino_per_block
;
522 /* Compute zone bitmap size in blocks */
523 sb
->zbmap_blocks
= UPPER(sb
->n_zones
, sb
->block_size
* 8);
525 /* Compute first data zone position */
526 sb
->first_data_zone
= 2 + sb
->itable_size
+
527 sb
->zbmap_blocks
+ sb
->ibmap_blocks
;
529 /* Set log2 of zone to block ratio to zero */
530 sb
->log2_zone_size
= 0;
532 /* Check for errors */
533 if (sb
->first_data_zone
>= sb
->n_zones
) {
534 printf(NAME
": Error! Insufficient disk space");
538 /* Superblock is now ready to be written on disk */
539 printf(NAME
": %d block size\n", sb
->block_size
);
540 printf(NAME
": %d inodes\n", (uint32_t) sb
->n_inodes
);
541 printf(NAME
": %d zones\n", (uint32_t) sb
->n_zones
);
542 printf(NAME
": inode table blocks = %ld\n", sb
->itable_size
);
543 printf(NAME
": inode bitmap blocks = %ld\n", sb
->ibmap_blocks
);
544 printf(NAME
": zone bitmap blocks = %ld\n", sb
->zbmap_blocks
);
545 printf(NAME
": first data zone = %d\n", (uint32_t)sb
->first_data_zone
);
546 printf(NAME
": max file size = %u\n", sb
->max_file_size
);
547 printf(NAME
": long fnames = %s\n", sb
->longnames
? "Yes" : "No");
549 if (sb
->fs_version
== 3)
550 rc
= write_superblock3(sb
);
552 rc
= write_superblock(sb
);
557 /**Write the V1/V2 superblock on disk.
559 * @param sbi Pointer to the superblock structure to write on disk.
561 * @return EOK on success or a negative error code.
563 static int write_superblock(const struct mfs_sb_info
*sbi
)
565 struct mfs_superblock
*sb
;
568 sb
= malloc(MFS_SUPERBLOCK_SIZE
);;
573 sb
->s_ninodes
= (uint16_t) sbi
->n_inodes
;
574 sb
->s_nzones
= (uint16_t) sbi
->n_zones
;
575 sb
->s_nzones2
= (uint32_t) sbi
->n_zones
;
576 sb
->s_ibmap_blocks
= (uint16_t) sbi
->ibmap_blocks
;
577 sb
->s_zbmap_blocks
= (uint16_t) sbi
->zbmap_blocks
;
578 sb
->s_first_data_zone
= (uint16_t) sbi
->first_data_zone
;
579 sb
->s_log2_zone_size
= sbi
->log2_zone_size
;
580 sb
->s_max_file_size
= sbi
->max_file_size
;
581 sb
->s_magic
= sbi
->magic
;
582 sb
->s_state
= MFS_VALID_FS
;
584 rc
= write_block(MFS_SUPERBLOCK
, 1, sb
);
590 /**Write the V3s superblock on disk.
592 * @param sbi Pointer to the superblock structure to write on disk.
594 * @return EOK on success or a negative error code.
596 static int write_superblock3(const struct mfs_sb_info
*sbi
)
598 struct mfs3_superblock
*sb
;
601 sb
= malloc(MFS_SUPERBLOCK_SIZE
);
606 sb
->s_ninodes
= (uint32_t) sbi
->n_inodes
;
607 sb
->s_nzones
= (uint32_t) sbi
->n_zones
;
608 sb
->s_ibmap_blocks
= (uint16_t) sbi
->ibmap_blocks
;
609 sb
->s_zbmap_blocks
= (uint16_t) sbi
->zbmap_blocks
;
610 sb
->s_first_data_zone
= (uint16_t) sbi
->first_data_zone
;
611 sb
->s_log2_zone_size
= sbi
->log2_zone_size
;
612 sb
->s_max_file_size
= sbi
->max_file_size
;
613 sb
->s_magic
= sbi
->magic
;
614 sb
->s_block_size
= sbi
->block_size
;
615 sb
->s_disk_version
= 3;
617 rc
= block_write_direct(service_id
, MFS_SUPERBLOCK
<< 1, 1 << 1, sb
);
623 /**Initialize the inode and block bitmaps on disk.
625 * @param sb Pointer to the superblock structure.
627 * @return EOK on success or a negative error code.
629 static int init_bitmaps(const struct mfs_sb_info
*sb
)
631 uint32_t *ibmap_buf
, *zbmap_buf
;
632 uint8_t *ibmap_buf8
, *zbmap_buf8
;
633 const unsigned int ibmap_nblocks
= sb
->ibmap_blocks
;
634 const unsigned int zbmap_nblocks
= sb
->zbmap_blocks
;
638 ibmap_buf
= malloc(ibmap_nblocks
* sb
->block_size
);
639 zbmap_buf
= malloc(zbmap_nblocks
* sb
->block_size
);
641 if (!ibmap_buf
|| !zbmap_buf
) {
646 memset(ibmap_buf
, 0xFF, ibmap_nblocks
* sb
->block_size
);
647 memset(zbmap_buf
, 0xFF, zbmap_nblocks
* sb
->block_size
);
649 for (i
= 2; i
< sb
->n_inodes
+ 1; ++i
)
650 mark_bmap(ibmap_buf
, i
, FREE
);
652 for (i
= 2; i
< sb
->n_zones
- sb
->first_data_zone
; ++i
)
653 mark_bmap(zbmap_buf
, i
, FREE
);
655 ibmap_buf8
= (uint8_t *) ibmap_buf
;
656 zbmap_buf8
= (uint8_t *) zbmap_buf
;
660 for (i
= 0; i
< ibmap_nblocks
; ++i
) {
661 if ((rc
= write_block(start_block
+ i
,
662 1, (ibmap_buf8
+ i
* sb
->block_size
))) != EOK
)
666 start_block
= 2 + ibmap_nblocks
;
668 for (i
= 0; i
< zbmap_nblocks
; ++i
) {
669 if ((rc
= write_block(start_block
+ i
,
670 1, (zbmap_buf8
+ i
* sb
->block_size
))) != EOK
)
681 /**Mark a bitmap entry as used or free.
683 * @param bmap 32-bit pointer to the bitmap in memory.
684 * @param idx The index in the bitmap of the bit to set at 1 or 0.
685 * @param v FREE to clear the bit, USED to set the bit.
687 static void mark_bmap(uint32_t *bmap
, int idx
, int v
)
690 bmap
[idx
/ 32] &= ~(1 << (idx
% 32));
692 bmap
[idx
/ 32] |= 1 << (idx
% 32);
695 /**Write a block on disk.
697 * @param off 64-bit block offset on disk.
698 * @param size size of the block.
699 * @param data Pointer to the block content.
701 * @return EOK on success or a negative error number.
703 static inline int write_block(aoff64_t off
, size_t size
, const void *data
)
707 aoff64_t tmp_off
= off
<< 1;
708 uint8_t *data_ptr
= (uint8_t *) data
;
710 rc
= block_write_direct(service_id
, tmp_off
<< 2,
711 size
<< 2, data_ptr
);
719 return block_write_direct(service_id
, tmp_off
<< 2,
720 size
<< 2, data_ptr
);
722 return block_write_direct(service_id
, off
<< shift
,
723 size
<< shift
, data
);
726 static void help_cmd_mkmfs(help_level_t level
)
728 if (level
== HELP_SHORT
) {
729 printf(NAME
": tool to create new Minix file systems\n");
731 printf("Usage: [options] device\n"
732 "-1 Make a Minix version 1 filesystem\n"
733 "-2 Make a Minix version 2 filesystem\n"
734 "-b ## Specify the block size in bytes (V3 only),\n"
735 " valid block size values are 1024, 2048 and"
736 " 4096 bytes per block\n"
737 "-i ## Specify the number of inodes"
738 " for the filesystem\n"
739 "-l Use 30-char long filenames (V1/V2 only)\n");
743 /** Check if a given number is a power of two.
745 * @param n The number to check.
747 * @return true if it is a power of two, false otherwise.
749 static bool is_power_of_two(uint32_t n
)
754 return (n
& (n
- 1)) == 0;