2 * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
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.
29 /** @addtogroup lwext4
37 #include "ext4_config.h"
38 #include "ext4_super.h"
39 #include "ext4_debug.h"
40 #include "ext4_mkfs.h"
46 #define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
47 #define EXT4_ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
50 struct ext4_sblock *sb;
51 struct ext4_sblock *sb_block;
52 struct ext4_bgroup *bg_desc;
53 struct xattr_list_element *xattrs;
54 uint32_t first_data_block;
56 uint32_t inode_table_blocks;
58 uint32_t bg_desc_blocks;
59 uint32_t default_i_flags;
60 uint32_t blocks_per_ind;
61 uint32_t blocks_per_dind;
62 uint32_t blocks_per_tind;
65 static int sb2info(struct ext4_sblock *sb, struct ext4_mkfs_info *info)
67 if (to_le16(sb->magic) != EXT4_SUPERBLOCK_MAGIC)
70 info->block_size = 1024 << to_le32(sb->log_block_size);
71 info->blocks_per_group = to_le32(sb->blocks_per_group);
72 info->inodes_per_group = to_le32(sb->inodes_per_group);
73 info->inode_size = to_le16(sb->inode_size);
74 info->inodes = to_le32(sb->inodes_count);
75 info->feat_ro_compat = to_le32(sb->features_read_only);
76 info->feat_compat = to_le32(sb->features_compatible);
77 info->feat_incompat = to_le32(sb->features_incompatible);
78 info->bg_desc_reserve_blocks = to_le16(sb->s_reserved_gdt_blocks);
79 info->label = sb->volume_name;
80 info->len = (uint64_t)info->block_size * ext4_sb_get_blocks_cnt(sb);
85 static int info2sb(struct ext4_mkfs_info *info, struct ext4_sblock *sb)
88 memset(sb, 0, sizeof(struct ext4_sblock));
89 /*TODO: Fill superblock with proper values*/
94 static uint32_t compute_blocks_per_group(struct ext4_mkfs_info *info)
96 return info->block_size * 8;
99 static uint32_t compute_inodes(struct ext4_mkfs_info *info)
101 return DIV_ROUND_UP(info->len, info->block_size) / 4;
104 static uint32_t compute_inodes_per_group(struct ext4_mkfs_info *info)
106 uint32_t blocks = DIV_ROUND_UP(info->len, info->block_size);
107 uint32_t block_groups = DIV_ROUND_UP(blocks, info->blocks_per_group);
108 uint32_t inodes = DIV_ROUND_UP(info->inodes, block_groups);
109 inodes = EXT4_ALIGN(inodes, (info->block_size / info->inode_size));
111 /* After properly rounding up the number of inodes/group,
112 * make sure to update the total inodes field in the info struct.
114 info->inodes = inodes * block_groups;
119 static uint32_t compute_bg_desc_reserve_blocks(struct ext4_mkfs_info *info)
121 uint32_t blocks = DIV_ROUND_UP(info->len, info->block_size);
122 uint32_t block_groups = DIV_ROUND_UP(blocks, info->blocks_per_group);
123 uint32_t bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext4_bgroup),
126 uint32_t bg_desc_reserve_blocks =
127 DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext4_bgroup),
128 info->block_size) - bg_desc_blocks;
130 if (bg_desc_reserve_blocks > info->block_size / sizeof(uint32_t))
131 bg_desc_reserve_blocks = info->block_size / sizeof(uint32_t);
133 return bg_desc_reserve_blocks;
136 static uint32_t compute_journal_blocks(struct ext4_mkfs_info *info)
138 uint32_t journal_blocks = DIV_ROUND_UP(info->len, info->block_size) / 64;
139 if (journal_blocks < 1024)
140 journal_blocks = 1024;
141 if (journal_blocks > 32768)
142 journal_blocks = 32768;
143 return journal_blocks;
146 static void create_fs_aux_info(struct fs_aux_info *aux_info, struct ext4_mkfs_info *info)
148 aux_info->first_data_block = (info->block_size > 1024) ? 0 : 1;
149 aux_info->len_blocks = info->len / info->block_size;
150 aux_info->inode_table_blocks = DIV_ROUND_UP(info->inodes_per_group * info->inode_size,
152 aux_info->groups = DIV_ROUND_UP(aux_info->len_blocks - aux_info->first_data_block,
153 info->blocks_per_group);
154 aux_info->blocks_per_ind = info->block_size / sizeof(uint32_t);
155 aux_info->blocks_per_dind = aux_info->blocks_per_ind * aux_info->blocks_per_ind;
156 aux_info->blocks_per_tind = aux_info->blocks_per_dind * aux_info->blocks_per_dind;
158 aux_info->bg_desc_blocks =
159 DIV_ROUND_UP(aux_info->groups * sizeof(struct ext4_bgroup),
162 aux_info->default_i_flags = EXT4_INODE_FLAG_NOATIME;
164 uint32_t last_group_size = aux_info->len_blocks % info->blocks_per_group;
165 uint32_t last_header_size = 2 + aux_info->inode_table_blocks;
166 if (!(info->feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
167 ext4_sb_sparse(aux_info->groups - 1))
168 last_header_size += 1 + aux_info->bg_desc_blocks +
169 info->bg_desc_reserve_blocks;
171 if (last_group_size > 0 && last_group_size < last_header_size) {
173 aux_info->len_blocks -= last_group_size;
176 /* The write_data* functions expect only block aligned calls.
177 * This is not an issue, except when we write out the super
178 * block on a system with a block size > 1K. So, we need to
179 * deal with that here.
181 aux_info->sb_block = calloc(1, info->block_size);
182 ext4_assert(aux_info->sb_block);
185 if (info->block_size > 1024)
186 aux_info->sb = (struct ext4_sblock *)((char *)aux_info->sb_block + 1024);
188 aux_info->sb = aux_info->sb_block;
190 aux_info->bg_desc = calloc(info->block_size, aux_info->bg_desc_blocks);
191 ext4_assert(aux_info->bg_desc);
193 aux_info->xattrs = NULL;
198 int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
201 struct ext4_sblock *sb = NULL;
202 r = ext4_block_init(bd);
206 sb = malloc(sizeof(struct ext4_sblock));
211 r = ext4_sb_read(bd, sb);
215 r = sb2info(sb, info);
224 int ext4_mkfs(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
227 struct ext4_sblock *sb = NULL;
228 r = ext4_block_init(bd);
232 sb = malloc(sizeof(struct ext4_sblock));
237 info->len = bd->ph_bcnt * bd->ph_bsize;
239 if (info->block_size == 0)
240 info->block_size = 4096; /*Set block size to default value*/
242 /* Round down the filesystem length to be a multiple of the block size */
243 info->len &= ~((uint64_t)info->block_size - 1);
245 if (info->journal_blocks == 0)
246 info->journal_blocks = compute_journal_blocks(info);
248 if (info->blocks_per_group == 0)
249 info->blocks_per_group = compute_blocks_per_group(info);
251 if (info->inodes == 0)
252 info->inodes = compute_inodes(info);
254 if (info->inode_size == 0)
255 info->inode_size = 256;
257 if (info->label == NULL)
260 info->inodes_per_group = compute_inodes_per_group(info);
262 info->feat_compat = CONFIG_FEATURE_COMPAT_SUPP;
263 info->feat_ro_compat = CONFIG_FEATURE_RO_COMPAT_SUPP;
264 info->feat_incompat = CONFIG_FEATURE_INCOMPAT_SUPP;
266 if (info->no_journal == 0)
267 info->feat_compat |= EXT4_FEATURE_COMPAT_HAS_JOURNAL;
269 info->bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(info);
271 r = info2sb(info, sb);
275 ext4_dbg(DEBUG_MKFS, DBG_INFO "Creating filesystem with parameters:\n");
276 ext4_dbg(DEBUG_MKFS, DBG_NONE "Size: %"PRIu64"\n", info->len);
277 ext4_dbg(DEBUG_MKFS, DBG_NONE "Block size: %d\n", info->block_size);
278 ext4_dbg(DEBUG_MKFS, DBG_NONE "Blocks per group: %d\n",
279 info->blocks_per_group);
280 ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes per group: %d\n",
281 info->inodes_per_group);
282 ext4_dbg(DEBUG_MKFS, DBG_NONE "Inode size: %d\n", info->inode_size);
283 ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes: %d\n", info->inodes);
284 ext4_dbg(DEBUG_MKFS, DBG_NONE "Journal blocks: %d\n",
285 info->journal_blocks);
286 ext4_dbg(DEBUG_MKFS, DBG_NONE "Features ro_compat: 0x%x\n",
287 info->feat_ro_compat);
288 ext4_dbg(DEBUG_MKFS, DBG_NONE "Features compat: 0x%x\n",
290 ext4_dbg(DEBUG_MKFS, DBG_NONE "Features incompat: 0x%x\n",
291 info->feat_incompat);
292 ext4_dbg(DEBUG_MKFS, DBG_NONE "BG desc reserve: 0x%x\n",
293 info->bg_desc_reserve_blocks);
294 ext4_dbg(DEBUG_MKFS, DBG_NONE "journal: %s\n",
295 !info->no_journal ? "yes" : "no");
296 ext4_dbg(DEBUG_MKFS, DBG_NONE "Label: %s\n", info->label);
298 struct fs_aux_info aux_info;
299 create_fs_aux_info(&aux_info, info);
301 /*TODO: write filesystem metadata*/