Port create_fs_aux_info form ext4-utils
[lwext4.git] / lwext4 / ext4_mkfs.c
1 /*
2  * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
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.
16  *
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.
27  */
28
29 /** @addtogroup lwext4
30  * @{
31  */
32 /**
33  * @file  ext4_mkfs.c
34  * @brief
35  */
36
37 #include "ext4_config.h"
38 #include "ext4_super.h"
39 #include "ext4_debug.h"
40 #include "ext4_mkfs.h"
41
42 #include <inttypes.h>
43 #include <string.h>
44 #include <stdlib.h>
45
46 #define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
47 #define EXT4_ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
48
49 struct fs_aux_info {
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;
55         uint64_t len_blocks;
56         uint32_t inode_table_blocks;
57         uint32_t groups;
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;
63 };
64
65 static int sb2info(struct ext4_sblock *sb, struct ext4_mkfs_info *info)
66 {
67         if (to_le16(sb->magic) != EXT4_SUPERBLOCK_MAGIC)
68                 return EINVAL;
69
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);
81
82         return EOK;
83 }
84
85 static int info2sb(struct ext4_mkfs_info *info, struct ext4_sblock *sb)
86 {
87         (void)info;
88         memset(sb, 0, sizeof(struct ext4_sblock));
89         /*TODO: Fill superblock with proper values*/
90
91         return EOK;
92 }
93
94 static uint32_t compute_blocks_per_group(struct ext4_mkfs_info *info)
95 {
96         return info->block_size * 8;
97 }
98
99 static uint32_t compute_inodes(struct ext4_mkfs_info *info)
100 {
101         return DIV_ROUND_UP(info->len, info->block_size) / 4;
102 }
103
104 static uint32_t compute_inodes_per_group(struct ext4_mkfs_info *info)
105 {
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));
110
111         /* After properly rounding up the number of inodes/group,
112          * make sure to update the total inodes field in the info struct.
113          */
114         info->inodes = inodes * block_groups;
115
116         return inodes;
117 }
118
119 static uint32_t compute_bg_desc_reserve_blocks(struct ext4_mkfs_info *info)
120 {
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),
124                         info->block_size);
125
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;
129
130         if (bg_desc_reserve_blocks > info->block_size / sizeof(uint32_t))
131                 bg_desc_reserve_blocks = info->block_size / sizeof(uint32_t);
132
133         return bg_desc_reserve_blocks;
134 }
135
136 static uint32_t compute_journal_blocks(struct ext4_mkfs_info *info)
137 {
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;
144 }
145
146 static void create_fs_aux_info(struct fs_aux_info *aux_info, struct ext4_mkfs_info *info)
147 {
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,
151                 info->block_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;
157
158         aux_info->bg_desc_blocks =
159                 DIV_ROUND_UP(aux_info->groups * sizeof(struct ext4_bgroup),
160                         info->block_size);
161
162         aux_info->default_i_flags = EXT4_INODE_FLAG_NOATIME;
163
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;
170
171         if (last_group_size > 0 && last_group_size < last_header_size) {
172                 aux_info->groups--;
173                 aux_info->len_blocks -= last_group_size;
174         }
175
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.
180          */
181         aux_info->sb_block = calloc(1, info->block_size);
182         ext4_assert(aux_info->sb_block);
183
184
185         if (info->block_size > 1024)
186                 aux_info->sb = (struct ext4_sblock *)((char *)aux_info->sb_block + 1024);
187         else
188                 aux_info->sb = aux_info->sb_block;
189
190         aux_info->bg_desc = calloc(info->block_size, aux_info->bg_desc_blocks);
191         ext4_assert(aux_info->bg_desc);
192
193         aux_info->xattrs = NULL;
194 }
195
196
197
198 int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
199 {
200         int r;
201         struct ext4_sblock *sb = NULL;
202         r = ext4_block_init(bd);
203         if (r != EOK)
204                 return r;
205
206         sb = malloc(sizeof(struct ext4_sblock));
207         if (!sb)
208                 goto Finish;
209
210
211         r = ext4_sb_read(bd, sb);
212         if (r != EOK)
213                 goto Finish;
214
215         r = sb2info(sb, info);
216
217 Finish:
218         if (sb)
219                 free(sb);
220         ext4_block_fini(bd);
221         return r;
222 }
223
224 int ext4_mkfs(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
225 {
226         int r;
227         struct ext4_sblock *sb = NULL;
228         r = ext4_block_init(bd);
229         if (r != EOK)
230                 return r;
231
232         sb = malloc(sizeof(struct ext4_sblock));
233         if (!sb)
234                 goto Finish;
235
236         if (info->len == 0)
237                 info->len = bd->ph_bcnt * bd->ph_bsize;
238
239         if (info->block_size == 0)
240                 info->block_size = 4096; /*Set block size to default value*/
241
242         /* Round down the filesystem length to be a multiple of the block size */
243         info->len &= ~((uint64_t)info->block_size - 1);
244
245         if (info->journal_blocks == 0)
246                 info->journal_blocks = compute_journal_blocks(info);
247
248         if (info->blocks_per_group == 0)
249                 info->blocks_per_group = compute_blocks_per_group(info);
250
251         if (info->inodes == 0)
252                 info->inodes = compute_inodes(info);
253
254         if (info->inode_size == 0)
255                 info->inode_size = 256;
256
257         if (info->label == NULL)
258                 info->label = "";
259
260         info->inodes_per_group = compute_inodes_per_group(info);
261
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;
265
266         if (info->no_journal == 0)
267                 info->feat_compat |= EXT4_FEATURE_COMPAT_HAS_JOURNAL;
268
269         info->bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(info);
270
271         r = info2sb(info, sb);
272         if (r != EOK)
273                 return r;
274
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",
289                         info->feat_compat);
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);
297
298         struct fs_aux_info aux_info;
299         create_fs_aux_info(&aux_info, info);
300
301         /*TODO: write filesystem metadata*/
302
303         Finish:
304         if (sb)
305                 free(sb);
306         ext4_block_fini(bd);
307         return r;
308 }
309
310 /**
311  * @}
312  */