Add ext4_sb_set_blocks_cnt
[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_block_group.h"
40 #include "ext4_fs.h"
41 #include "ext4_debug.h"
42 #include "ext4_mkfs.h"
43
44 #include <inttypes.h>
45 #include <string.h>
46 #include <stdlib.h>
47
48 #define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
49 #define EXT4_ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
50
51 struct fs_aux_info {
52         struct ext4_sblock *sb;
53         struct ext4_bgroup *bg_desc;
54         struct xattr_list_element *xattrs;
55         uint32_t first_data_block;
56         uint64_t len_blocks;
57         uint32_t inode_table_blocks;
58         uint32_t groups;
59         uint32_t bg_desc_blocks;
60         uint32_t default_i_flags;
61         uint32_t blocks_per_ind;
62         uint32_t blocks_per_dind;
63         uint32_t blocks_per_tind;
64 };
65
66 static inline int log_2(int j)
67 {
68         int i;
69
70         for (i = 0; j > 0; i++)
71                 j >>= 1;
72
73         return i - 1;
74 }
75
76 static int sb2info(struct ext4_sblock *sb, struct ext4_mkfs_info *info)
77 {
78         if (to_le16(sb->magic) != EXT4_SUPERBLOCK_MAGIC)
79                 return EINVAL;
80
81         info->block_size = 1024 << to_le32(sb->log_block_size);
82         info->blocks_per_group = to_le32(sb->blocks_per_group);
83         info->inodes_per_group = to_le32(sb->inodes_per_group);
84         info->inode_size = to_le16(sb->inode_size);
85         info->inodes = to_le32(sb->inodes_count);
86         info->feat_ro_compat = to_le32(sb->features_read_only);
87         info->feat_compat = to_le32(sb->features_compatible);
88         info->feat_incompat = to_le32(sb->features_incompatible);
89         info->bg_desc_reserve_blocks = to_le16(sb->s_reserved_gdt_blocks);
90         info->label = sb->volume_name;
91         info->len = (uint64_t)info->block_size * ext4_sb_get_blocks_cnt(sb);
92
93         return EOK;
94 }
95
96 static uint32_t compute_blocks_per_group(struct ext4_mkfs_info *info)
97 {
98         return info->block_size * 8;
99 }
100
101 static uint32_t compute_inodes(struct ext4_mkfs_info *info)
102 {
103         return DIV_ROUND_UP(info->len, info->block_size) / 4;
104 }
105
106 static uint32_t compute_inodes_per_group(struct ext4_mkfs_info *info)
107 {
108         uint32_t blocks = DIV_ROUND_UP(info->len, info->block_size);
109         uint32_t block_groups = DIV_ROUND_UP(blocks, info->blocks_per_group);
110         uint32_t inodes = DIV_ROUND_UP(info->inodes, block_groups);
111         inodes = EXT4_ALIGN(inodes, (info->block_size / info->inode_size));
112
113         /* After properly rounding up the number of inodes/group,
114          * make sure to update the total inodes field in the info struct.
115          */
116         info->inodes = inodes * block_groups;
117
118         return inodes;
119 }
120
121 static uint32_t compute_bg_desc_reserve_blocks(struct ext4_mkfs_info *info)
122 {
123         uint32_t blocks = DIV_ROUND_UP(info->len, info->block_size);
124         uint32_t block_groups = DIV_ROUND_UP(blocks, info->blocks_per_group);
125         uint32_t bg_desc_blocks = DIV_ROUND_UP(block_groups *
126                         sizeof(struct ext4_bgroup), info->block_size);
127
128         uint32_t bg_desc_reserve_blocks = DIV_ROUND_UP(block_groups * 1024 *
129                 sizeof(struct ext4_bgroup), info->block_size) - bg_desc_blocks;
130
131         if (bg_desc_reserve_blocks > info->block_size / sizeof(uint32_t))
132                 bg_desc_reserve_blocks = info->block_size / sizeof(uint32_t);
133
134         return bg_desc_reserve_blocks;
135 }
136
137 static uint32_t compute_journal_blocks(struct ext4_mkfs_info *info)
138 {
139         uint32_t journal_blocks = DIV_ROUND_UP(info->len, info->block_size) / 64;
140         if (journal_blocks < 1024)
141                 journal_blocks = 1024;
142         if (journal_blocks > 32768)
143                 journal_blocks = 32768;
144         return journal_blocks;
145 }
146
147 static bool has_superblock(struct ext4_mkfs_info *info, uint32_t bgid)
148 {
149         if (!(info->feat_ro_compat & EXT4_FRO_COM_SPARSE_SUPER))
150                 return true;
151
152         return ext4_sb_sparse(bgid);
153 }
154
155 static int create_fs_aux_info(struct fs_aux_info *aux_info,
156                               struct ext4_mkfs_info *info)
157 {
158         aux_info->first_data_block = (info->block_size > 1024) ? 0 : 1;
159         aux_info->len_blocks = info->len / info->block_size;
160         aux_info->inode_table_blocks = DIV_ROUND_UP(info->inodes_per_group *
161                         info->inode_size, info->block_size);
162         aux_info->groups = DIV_ROUND_UP(aux_info->len_blocks -
163                         aux_info->first_data_block, info->blocks_per_group);
164         aux_info->blocks_per_ind = info->block_size / sizeof(uint32_t);
165         aux_info->blocks_per_dind =
166                         aux_info->blocks_per_ind * aux_info->blocks_per_ind;
167         aux_info->blocks_per_tind =
168                         aux_info->blocks_per_dind * aux_info->blocks_per_dind;
169
170         aux_info->bg_desc_blocks =
171                 DIV_ROUND_UP(aux_info->groups * sizeof(struct ext4_bgroup),
172                         info->block_size);
173
174         aux_info->default_i_flags = EXT4_INODE_FLAG_NOATIME;
175
176         uint32_t last_group_size = aux_info->len_blocks % info->blocks_per_group;
177         uint32_t last_header_size = 2 + aux_info->inode_table_blocks;
178         if (has_superblock(info, aux_info->groups - 1))
179                 last_header_size += 1 + aux_info->bg_desc_blocks +
180                         info->bg_desc_reserve_blocks;
181
182         if (last_group_size > 0 && last_group_size < last_header_size) {
183                 aux_info->groups--;
184                 aux_info->len_blocks -= last_group_size;
185         }
186
187         aux_info->sb = calloc(1, EXT4_SUPERBLOCK_SIZE);
188         if (!aux_info->sb)
189                 return ENOMEM;
190
191         aux_info->bg_desc = calloc(aux_info->groups, sizeof(struct ext4_bgroup));
192         if (!aux_info->bg_desc)
193                 return ENOMEM;
194
195         aux_info->xattrs = NULL;
196         return EOK;
197 }
198
199 static void release_fs_aux_info(struct fs_aux_info *aux_info)
200 {
201         if (aux_info->sb)
202                 free(aux_info->sb);
203         if (aux_info->bg_desc)
204                 free(aux_info->bg_desc);
205 }
206
207
208 /* Fill in the superblock memory buffer based on the filesystem parameters */
209 static void fill_in_sb(struct fs_aux_info *aux_info, struct ext4_mkfs_info *info)
210 {
211         struct ext4_sblock *sb = aux_info->sb;
212
213         sb->inodes_count = info->inodes_per_group * aux_info->groups;
214         sb->blocks_count_lo = aux_info->len_blocks;
215         sb->reserved_blocks_count_lo = 0;
216         sb->free_blocks_count_lo = 0;
217         sb->free_inodes_count = 0;
218         sb->first_data_block = aux_info->first_data_block;
219         sb->log_block_size = log_2(info->block_size / 1024);
220         sb->log_cluster_size = log_2(info->block_size / 1024);
221         sb->blocks_per_group = info->blocks_per_group;
222         sb->frags_per_group = info->blocks_per_group;
223         sb->inodes_per_group = info->inodes_per_group;
224         sb->mount_time = 0;
225         sb->write_time = 0;
226         sb->mount_count = 0;
227         sb->max_mount_count = 0xFFFF;
228         sb->magic = EXT4_SUPERBLOCK_MAGIC;
229         sb->state = EXT4_SUPERBLOCK_STATE_VALID_FS;
230         sb->errors = EXT4_SUPERBLOCK_ERRORS_RO;
231         sb->minor_rev_level = 0;
232         sb->last_check_time = 0;
233         sb->check_interval = 0;
234         sb->creator_os = EXT4_SUPERBLOCK_OS_LINUX;
235         sb->rev_level = 1;
236         sb->def_resuid = 0;
237         sb->def_resgid = 0;
238
239         sb->first_inode = EXT4_GOOD_OLD_FIRST_INO;
240         sb->inode_size = info->inode_size;
241         sb->block_group_index = 0;
242         sb->features_compatible = info->feat_compat;
243         sb->features_incompatible = info->feat_incompat;
244         sb->features_read_only = info->feat_ro_compat;
245
246         memset(sb->uuid, 0, sizeof(sb->uuid));
247
248         memset(sb->volume_name, 0, sizeof(sb->volume_name));
249         strncpy(sb->volume_name, info->label, sizeof(sb->volume_name));
250         memset(sb->last_mounted, 0, sizeof(sb->last_mounted));
251         sb->algorithm_usage_bitmap = 0;
252
253         sb->s_reserved_gdt_blocks = info->bg_desc_reserve_blocks;
254         sb->s_prealloc_blocks = 0;
255         sb->s_prealloc_dir_blocks = 0;
256
257         //memcpy(sb->journal_uuid, sb->uuid, sizeof(sb->journal_uuid));
258         if (info->feat_compat & EXT4_FCOM_HAS_JOURNAL)
259                 sb->journal_inode_number = EXT4_JOURNAL_INO;
260         sb->journal_dev = 0;
261         sb->last_orphan = 0;
262         sb->hash_seed[0] = 0; /* FIXME */
263         sb->default_hash_version = EXT2_HTREE_HALF_MD4;
264         sb->checksum_type = 1;
265         sb->desc_size = EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE;
266         sb->default_mount_opts = 0; /* FIXME */
267         sb->first_meta_bg = 0;
268         sb->mkfs_time = 0;
269         //sb->jnl_blocks[17]; /* FIXME */
270
271         sb->blocks_count_hi = aux_info->len_blocks >> 32;
272         sb->reserved_blocks_count_hi = 0;
273         sb->free_blocks_count_hi = 0;
274         sb->min_extra_isize = sizeof(struct ext4_inode) -
275                 EXT4_GOOD_OLD_INODE_SIZE;
276         sb->want_extra_isize = sizeof(struct ext4_inode) -
277                 EXT4_GOOD_OLD_INODE_SIZE;
278         sb->flags = 2;
279         sb->raid_stride = 0;
280         sb->mmp_interval = 0;
281         sb->mmp_block = 0;
282         sb->raid_stripe_width = 0;
283         sb->log_groups_per_flex = 0;
284         sb->kbytes_written = 0;
285         sb->block_group_index = 0;
286 }
287
288 static void fill_bgroups(struct fs_aux_info *aux_info,
289                          struct ext4_mkfs_info *info)
290 {
291         uint32_t i;
292
293         for (i = 0; i < aux_info->groups; i++) {
294
295                 uint64_t bg_start_block = aux_info->first_data_block +
296                         aux_info->first_data_block + i * info->blocks_per_group;
297                 uint32_t blk_off = 0;
298
299                 if (has_superblock(info, i)) {
300                         bg_start_block++;
301                         blk_off = info->bg_desc_reserve_blocks +
302                                   aux_info->bg_desc_blocks ;
303                 }
304
305                 ext4_bg_set_block_bitmap(&aux_info->bg_desc[i], aux_info->sb,
306                                 bg_start_block + blk_off + 1);
307
308                 ext4_bg_set_inode_bitmap(&aux_info->bg_desc[i], aux_info->sb,
309                                 bg_start_block + blk_off + 2);
310
311                 ext4_bg_set_inode_table_first_block(&aux_info->bg_desc[i],
312                                 aux_info->sb,
313                                 bg_start_block + blk_off + 3);
314
315                 ext4_bg_set_free_blocks_count(&aux_info->bg_desc[i],
316                                 aux_info->sb, aux_info->sb->blocks_per_group);
317
318                 ext4_bg_set_free_inodes_count(&aux_info->bg_desc[i],
319                                 aux_info->sb, aux_info->sb->inodes_per_group);
320
321                 ext4_bg_set_used_dirs_count(&aux_info->bg_desc[i], aux_info->sb,
322                                             0);
323         }
324
325 }
326
327
328 static int write_bgroups(struct ext4_blockdev *bd, struct fs_aux_info *aux_info,
329                          struct ext4_mkfs_info *info)
330 {
331         int r;
332         uint32_t i;
333         struct ext4_block b;
334         for (i = 0; i < aux_info->groups; i++) {
335                 uint64_t bg_start_block = aux_info->first_data_block +
336                         aux_info->first_data_block + i * info->blocks_per_group;
337                 uint32_t blk_off = 0;
338
339                 if (has_superblock(info, i)) {
340                         bg_start_block++;
341                         blk_off = info->bg_desc_reserve_blocks +
342                                   aux_info->bg_desc_blocks;
343                 }
344
345                 uint32_t block_size = ext4_sb_get_block_size(aux_info->sb);
346                 uint32_t dsc_pos = 0;
347                 uint32_t dsc_id = 0;
348                 uint32_t dsc_size = ext4_sb_get_desc_size(aux_info->sb);
349                 uint32_t dsc_blk_cnt = aux_info->bg_desc_blocks;
350                 uint64_t dsc_blk = bg_start_block;
351
352                 while (dsc_blk_cnt--) {
353                         r = ext4_block_get(bd, &b, dsc_blk++);
354                         if (r != EOK)
355                                 return r;
356
357                         while (dsc_pos + dsc_size < block_size) {
358                                 memcpy(b.data + dsc_pos,
359                                        &aux_info->bg_desc[dsc_id],
360                                        dsc_size);
361
362                                 dsc_pos += dsc_size;
363                                 dsc_id++;
364                         }
365
366                         b.dirty = true;
367                         r = ext4_block_set(bd, &b);
368                         if (r != EOK)
369                                 return r;
370                 }
371
372                 r = ext4_block_get_noread(bd, &b, bg_start_block + blk_off + 1);
373                 if (r != EOK)
374                         return r;
375                 memset(b.data, 0, block_size);
376                 b.dirty = true;
377                 r = ext4_block_set(bd, &b);
378                 if (r != EOK)
379                         return r;
380                 r = ext4_block_get_noread(bd, &b, bg_start_block + blk_off + 2);
381                 if (r != EOK)
382                         return r;
383                 memset(b.data, 0, block_size);
384                 b.dirty = true;
385                 r = ext4_block_set(bd, &b);
386                 if (r != EOK)
387                         return r;
388         }
389
390         return r;
391 }
392
393 static int write_sblocks(struct ext4_blockdev *bd, struct fs_aux_info *aux_info,
394                           struct ext4_mkfs_info *info)
395 {
396         uint64_t offset;
397         uint32_t i;
398         int r;
399
400         /* write out the backup superblocks */
401         for (i = 1; i < aux_info->groups; i++) {
402                 if (has_superblock(info, i)) {
403                         offset = info->block_size * (aux_info->first_data_block
404                                 + i * info->blocks_per_group);
405
406                         aux_info->sb->block_group_index = i;
407                         r = ext4_block_writebytes(bd, offset, aux_info->sb,
408                                                   EXT4_SUPERBLOCK_SIZE);
409                         if (r != EOK)
410                                 return r;
411                 }
412         }
413
414         /* write out the primary superblock */
415         aux_info->sb->block_group_index = 0;
416         return ext4_block_writebytes(bd, 1024, aux_info->sb,
417                                      EXT4_SUPERBLOCK_SIZE);
418 }
419
420
421 int ext4_mkfs_read_info(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
422 {
423         int r;
424         struct ext4_sblock *sb = NULL;
425         r = ext4_block_init(bd);
426         if (r != EOK)
427                 return r;
428
429         sb = malloc(EXT4_SUPERBLOCK_SIZE);
430         if (!sb)
431                 goto Finish;
432
433
434         r = ext4_sb_read(bd, sb);
435         if (r != EOK)
436                 goto Finish;
437
438         r = sb2info(sb, info);
439
440 Finish:
441         if (sb)
442                 free(sb);
443         ext4_block_fini(bd);
444         return r;
445 }
446
447 static int mkfs_initial(struct ext4_blockdev *bd, struct ext4_mkfs_info *info)
448 {
449         int r;
450         struct fs_aux_info aux_info;
451         memset(&aux_info, 0, sizeof(struct fs_aux_info));
452
453         r = create_fs_aux_info(&aux_info, info);
454         if (r != EOK)
455                 goto Finish;
456
457         fill_in_sb(&aux_info, info);
458         fill_bgroups(&aux_info, info);
459
460
461         r = write_bgroups(bd, &aux_info, info);
462         if (r != EOK)
463                 goto Finish;
464
465         r = write_sblocks(bd, &aux_info, info);
466         if (r != EOK)
467                 goto Finish;
468
469         Finish:
470         release_fs_aux_info(&aux_info);
471         return r;
472 }
473
474 static int alloc_inodes(struct ext4_fs *fs)
475 {
476         int r = EOK;
477         int i;
478         struct ext4_inode_ref inode_ref;
479
480         int filetype = EXT4_DIRENTRY_REG_FILE;
481         for (i = 1; i < 12; ++i) {
482                 switch (i) {
483                 case EXT4_ROOT_INO:
484                 case EXT4_UNDEL_DIR_INO:
485                 case EXT4_GOOD_OLD_FIRST_INO:
486                          filetype = EXT4_DIRENTRY_DIR;
487                         break;
488                 }
489
490                 r = ext4_fs_alloc_inode(fs, &inode_ref, filetype);
491                 if (r != EOK)
492                         return r;
493
494                 ext4_fs_put_inode_ref(&inode_ref);
495         }
496
497         return r;
498 }
499
500 int ext4_mkfs(struct ext4_fs *fs, struct ext4_blockdev *bd,
501               struct ext4_mkfs_info *info)
502 {
503         int r;
504
505         r = ext4_block_init(bd);
506         if (r != EOK)
507                 return r;
508
509         if (info->len == 0)
510                 info->len = bd->ph_bcnt * bd->ph_bsize;
511
512         if (info->block_size == 0)
513                 info->block_size = 4096; /*Set block size to default value*/
514
515         /* Round down the filesystem length to be a multiple of the block size */
516         info->len &= ~((uint64_t)info->block_size - 1);
517
518         if (info->journal_blocks == 0)
519                 info->journal_blocks = compute_journal_blocks(info);
520
521         if (info->blocks_per_group == 0)
522                 info->blocks_per_group = compute_blocks_per_group(info);
523
524         if (info->inodes == 0)
525                 info->inodes = compute_inodes(info);
526
527         if (info->inode_size == 0)
528                 info->inode_size = 256;
529
530         if (info->label == NULL)
531                 info->label = "";
532
533         info->inodes_per_group = compute_inodes_per_group(info);
534
535         info->feat_compat = EXT2_SUPPORTED_FCOM | EXT4_FCOM_RESIZE_INODE;
536         info->feat_ro_compat = EXT2_SUPPORTED_FRO_COM;
537         info->feat_incompat = EXT2_SUPPORTED_FINCOM;
538
539         if (info->no_journal == 0)
540                 info->feat_compat |= 0;
541
542         info->bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(info);
543
544         ext4_dbg(DEBUG_MKFS, DBG_INFO "Creating filesystem with parameters:\n");
545         ext4_dbg(DEBUG_MKFS, DBG_NONE "Size: %"PRIu64"\n", info->len);
546         ext4_dbg(DEBUG_MKFS, DBG_NONE "Block size: %"PRIu32"\n",
547                         info->block_size);
548         ext4_dbg(DEBUG_MKFS, DBG_NONE "Blocks per group: %"PRIu32"\n",
549                         info->blocks_per_group);
550         ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes per group: %"PRIu32"\n",
551                         info->inodes_per_group);
552         ext4_dbg(DEBUG_MKFS, DBG_NONE "Inode size: %"PRIu32"\n",
553                         info->inode_size);
554         ext4_dbg(DEBUG_MKFS, DBG_NONE "Inodes: %"PRIu32"\n", info->inodes);
555         ext4_dbg(DEBUG_MKFS, DBG_NONE "Journal blocks: %"PRIu32"\n",
556                         info->journal_blocks);
557         ext4_dbg(DEBUG_MKFS, DBG_NONE "Features ro_compat: 0x%x\n",
558                         info->feat_ro_compat);
559         ext4_dbg(DEBUG_MKFS, DBG_NONE "Features compat: 0x%x\n",
560                         info->feat_compat);
561         ext4_dbg(DEBUG_MKFS, DBG_NONE "Features incompat: 0x%x\n",
562                         info->feat_incompat);
563         ext4_dbg(DEBUG_MKFS, DBG_NONE "BG desc reserve: %"PRIu32"\n",
564                         info->bg_desc_reserve_blocks);
565         ext4_dbg(DEBUG_MKFS, DBG_NONE "journal: %s\n",
566                         !info->no_journal ? "yes" : "no");
567         ext4_dbg(DEBUG_MKFS, DBG_NONE "Label: %s\n", info->label);
568
569         struct ext4_bcache bc;
570         memset(&bc, 0, sizeof(struct ext4_bcache));
571         ext4_block_set_lb_size(bd, info->block_size);
572         r = ext4_bcache_init_dynamic(&bc, CONFIG_BLOCK_DEV_CACHE_SIZE,
573                                       info->block_size);
574         if (r != EOK)
575                 goto block_fini;
576
577         /*Bind block cache to block device*/
578         r = ext4_block_bind_bcache(bd, &bc);
579         if (r != EOK)
580                 goto cache_fini;
581
582         r = ext4_block_cache_write_back(bd, 0);
583         if (r != EOK)
584                 goto cache_fini;
585
586         r = mkfs_initial(bd, info);
587         if (r != EOK)
588                 goto cache_fini;
589
590         r = ext4_fs_init(fs, bd);
591         if (r != EOK)
592                 goto cache_fini;
593
594         r = alloc_inodes(fs);
595         if (r != EOK)
596                 goto fs_fini;
597
598         fs_fini:
599         ext4_fs_fini(fs);
600
601         cache_fini:
602         ext4_bcache_fini_dynamic(&bc);
603
604         block_fini:
605         ext4_block_fini(bd);
606
607         return r;
608 }
609
610 /**
611  * @}
612  */