ext4_bcache: manipulate buffer refctr by two helpers
[lwext4.git] / lwext4 / ext4_journal.c
1 /*
2  * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistributions of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  * - Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  * - The name of the author may not be used to endorse or promote products
16  *   derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 /** @addtogroup lwext4
31  * @{
32  */
33 /**
34  * @file  ext4_journal.c
35  * @brief Journal handle functions
36  */
37
38 #include "ext4_config.h"
39 #include "ext4_types.h"
40 #include "ext4_fs.h"
41 #include "ext4_super.h"
42 #include "ext4_errno.h"
43 #include "ext4_blockdev.h"
44 #include "ext4_crc32c.h"
45 #include "ext4_debug.h"
46 #include "tree.h"
47
48 #include <string.h>
49 #include <stdlib.h>
50
51 struct revoke_entry {
52         ext4_fsblk_t block;
53         uint32_t trans_id;
54         RB_ENTRY(revoke_entry) revoke_node;
55 };
56
57 struct recover_info {
58         uint32_t start_trans_id;
59         uint32_t last_trans_id;
60         uint32_t this_trans_id;
61         RB_HEAD(jbd_revoke, revoke_entry) revoke_root;
62 };
63
64 struct replay_arg {
65         struct recover_info *info;
66         uint32_t *this_block;
67         uint32_t this_trans_id;
68 };
69
70 static int
71 jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
72 {
73         if (a->block > b->block)
74                 return 1;
75         else if (a->block < b->block)
76                 return -1;
77         return 0;
78 }
79
80 RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
81                      jbd_revoke_entry_cmp, static inline)
82
83 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
84 #define jbd_free_revoke_entry(addr) free(addr)
85
86 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
87                    ext4_lblk_t iblock,
88                    ext4_fsblk_t *fblock);
89
90 int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
91 {
92         int rc;
93         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
94         uint64_t offset;
95         ext4_fsblk_t fblock;
96         rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
97         if (rc != EOK)
98                 return rc;
99
100         offset = fblock * ext4_sb_get_block_size(&fs->sb);
101         return ext4_block_writebytes(fs->bdev, offset, s,
102                                      EXT4_SUPERBLOCK_SIZE);
103 }
104
105 int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
106 {
107         int rc;
108         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
109         uint64_t offset;
110         ext4_fsblk_t fblock;
111         rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
112         if (rc != EOK)
113                 return rc;
114
115         offset = fblock * ext4_sb_get_block_size(&fs->sb);
116         return ext4_block_readbytes(fs->bdev, offset, s,
117                                     EXT4_SUPERBLOCK_SIZE);
118 }
119
120 static bool jbd_verify_sb(struct jbd_sb *sb)
121 {
122         struct jbd_bhdr *header = &sb->header;
123         if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
124                 return false;
125
126         if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
127             jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
128                 return false;
129
130         return true;
131 }
132
133 int jbd_get_fs(struct ext4_fs *fs,
134                struct jbd_fs *jbd_fs)
135 {
136         int rc;
137         uint32_t journal_ino;
138
139         memset(jbd_fs, 0, sizeof(struct jbd_fs));
140         journal_ino = ext4_get32(&fs->sb, journal_inode_number);
141
142         rc = ext4_fs_get_inode_ref(fs,
143                                    journal_ino,
144                                    &jbd_fs->inode_ref);
145         if (rc != EOK) {
146                 memset(jbd_fs, 0, sizeof(struct jbd_fs));
147                 return rc;
148         }
149         rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
150         if (rc != EOK) {
151                 memset(jbd_fs, 0, sizeof(struct jbd_fs));
152                 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
153                 return rc;
154         }
155         if (!jbd_verify_sb(&jbd_fs->sb)) {
156                 memset(jbd_fs, 0, sizeof(struct jbd_fs));
157                 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
158                 rc = EIO;
159         }
160
161         return rc;
162 }
163
164 int jbd_put_fs(struct jbd_fs *jbd_fs)
165 {
166         int rc;
167         if (jbd_fs->dirty)
168                 jbd_sb_write(jbd_fs, &jbd_fs->sb);
169
170         rc = ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
171         return rc;
172 }
173
174 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
175                    ext4_lblk_t iblock,
176                    ext4_fsblk_t *fblock)
177 {
178         int rc = ext4_fs_get_inode_dblk_idx(
179                         &jbd_fs->inode_ref,
180                         iblock,
181                         fblock,
182                         false);
183         return rc;
184 }
185
186 int jbd_block_get(struct jbd_fs *jbd_fs,
187                   struct ext4_block *block,
188                   ext4_fsblk_t fblock)
189 {
190         /* TODO: journal device. */
191         int rc;
192         ext4_lblk_t iblock = (ext4_lblk_t)fblock;
193         rc = jbd_inode_bmap(jbd_fs, iblock,
194                             &fblock);
195         if (rc != EOK)
196                 return rc;
197
198         struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
199         rc = ext4_block_get(bdev, block, fblock);
200         return rc;
201 }
202
203 int jbd_block_get_noread(struct jbd_fs *jbd_fs,
204                          struct ext4_block *block,
205                          ext4_fsblk_t fblock)
206 {
207         /* TODO: journal device. */
208         int rc;
209         ext4_lblk_t iblock = (ext4_lblk_t)fblock;
210         rc = jbd_inode_bmap(jbd_fs, iblock,
211                             &fblock);
212         if (rc != EOK)
213                 return rc;
214
215         struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
216         rc = ext4_block_get_noread(bdev, block, fblock);
217         return rc;
218 }
219
220 int jbd_block_set(struct jbd_fs *jbd_fs,
221                   struct ext4_block *block)
222 {
223         return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
224                               block);
225 }
226
227 /*
228  * helper functions to deal with 32 or 64bit block numbers.
229  */
230 int jbd_tag_bytes(struct jbd_fs *jbd_fs)
231 {
232         int size;
233
234         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
235                                      JBD_FEATURE_INCOMPAT_CSUM_V3))
236                 return sizeof(struct jbd_block_tag3);
237
238         size = sizeof(struct jbd_block_tag);
239
240         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
241                                      JBD_FEATURE_INCOMPAT_CSUM_V2))
242                 size += sizeof(uint16_t);
243
244         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
245                                      JBD_FEATURE_INCOMPAT_64BIT))
246                 return size;
247
248         return size - sizeof(uint32_t);
249 }
250
251 static void
252 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
253                       uint32_t tag_bytes,
254                       void *__tag,
255                       ext4_fsblk_t *block,
256                       bool *uuid_exist,
257                       uint8_t *uuid,
258                       bool *last_tag)
259 {
260         char *uuid_start;
261         *uuid_exist = false;
262         *last_tag = false;
263         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
264                                      JBD_FEATURE_INCOMPAT_CSUM_V3)) {
265                 struct jbd_block_tag3 *tag = __tag;
266                 *block = jbd_get32(tag, blocknr);
267                 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
268                                              JBD_FEATURE_INCOMPAT_64BIT))
269                          *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
270
271                 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
272                         *block = 0;
273
274                 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
275                         uuid_start = (char *)tag + tag_bytes;
276                         *uuid_exist = true;
277                         memcpy(uuid, uuid_start, UUID_SIZE);
278                 }
279
280                 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
281                         *last_tag = true;
282
283         } else {
284                 struct jbd_block_tag *tag = __tag;
285                 *block = jbd_get32(tag, blocknr);
286                 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
287                                              JBD_FEATURE_INCOMPAT_64BIT))
288                          *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
289
290                 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
291                         *block = 0;
292
293                 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
294                         uuid_start = (char *)tag + tag_bytes;
295                         *uuid_exist = true;
296                         memcpy(uuid, uuid_start, UUID_SIZE);
297                 }
298
299                 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
300                         *last_tag = true;
301
302         }
303 }
304
305 static void
306 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
307                         void *__tag_start,
308                         uint32_t tag_tbl_size,
309                         void (*func)(struct jbd_fs * jbd_fs,
310                                         ext4_fsblk_t block,
311                                         uint8_t *uuid,
312                                         void *arg),
313                         void *arg)
314 {
315         ext4_fsblk_t block = 0;
316         uint8_t uuid[UUID_SIZE];
317         char *tag_start, *tag_ptr;
318         uint32_t tag_bytes = jbd_tag_bytes(jbd_fs);
319         tag_start = __tag_start;
320         tag_ptr = tag_start;
321
322         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
323                                      JBD_FEATURE_INCOMPAT_CSUM_V2) ||
324             JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
325                                      JBD_FEATURE_INCOMPAT_CSUM_V3))
326                 tag_tbl_size -= sizeof(struct jbd_block_tail);
327
328         while (tag_ptr - tag_start + tag_bytes <= tag_tbl_size) {
329                 bool uuid_exist;
330                 bool last_tag;
331                 jbd_extract_block_tag(jbd_fs,
332                                       tag_bytes,
333                                       tag_ptr,
334                                       &block,
335                                       &uuid_exist,
336                                       uuid,
337                                       &last_tag);
338                 if (func)
339                         func(jbd_fs, block, uuid, arg);
340
341                 if (last_tag)
342                         break;
343
344                 tag_ptr += tag_bytes;
345                 if (uuid_exist)
346                         tag_ptr += UUID_SIZE;
347
348         }
349 }
350
351 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
352                                    ext4_fsblk_t block,
353                                    uint8_t *uuid,
354                                    void *arg)
355 {
356         uint32_t *iblock = arg;
357         ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
358         (*iblock)++;
359         (void)jbd_fs;
360         (void)uuid;
361         return;
362 }
363
364 static struct revoke_entry *
365 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
366 {
367         struct revoke_entry tmp = {
368                 .block = block
369         };
370
371         return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
372 }
373
374 static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
375                                   ext4_fsblk_t block,
376                                   uint8_t *uuid __unused,
377                                   void *__arg)
378 {
379         int r;
380         struct replay_arg *arg = __arg;
381         struct recover_info *info = arg->info;
382         uint32_t *this_block = arg->this_block;
383         struct revoke_entry *revoke_entry;
384         struct ext4_block journal_block, ext4_block;
385         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
386
387         (*this_block)++;
388
389         revoke_entry = jbd_revoke_entry_lookup(info, block);
390         if (revoke_entry &&
391             arg->this_trans_id < revoke_entry->trans_id)
392                 return;
393
394         ext4_dbg(DEBUG_JBD,
395                  "Replaying block in block_tag: %" PRIu64 "\n",
396                  block);
397
398         r = jbd_block_get(jbd_fs, &journal_block, *this_block);
399         if (r != EOK)
400                 return;
401
402         if (block) {
403                 r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
404                 if (r != EOK) {
405                         jbd_block_set(jbd_fs, &journal_block);
406                         return;
407                 }
408
409                 memcpy(ext4_block.data,
410                         journal_block.data,
411                         jbd_get32(&jbd_fs->sb, blocksize));
412
413                 ext4_bcache_set_dirty(ext4_block.buf);
414                 ext4_block_set(fs->bdev, &ext4_block);
415         } else {
416                 uint16_t mount_count, state;
417                 mount_count = ext4_get16(&fs->sb, mount_count);
418                 state = ext4_get16(&fs->sb, state);
419
420                 memcpy(&fs->sb,
421                         journal_block.data + EXT4_SUPERBLOCK_OFFSET,
422                         EXT4_SUPERBLOCK_SIZE);
423
424                 /* Mark system as mounted */
425                 ext4_set16(&fs->sb, state, state);
426                 r = ext4_sb_write(fs->bdev, &fs->sb);
427                 if (r != EOK)
428                         return;
429
430                 /*Update mount count*/
431                 ext4_set16(&fs->sb, mount_count, mount_count);
432         }
433
434         jbd_block_set(jbd_fs, &journal_block);
435         
436         return;
437 }
438
439 static void jbd_add_revoke_block_tags(struct recover_info *info,
440                                       ext4_fsblk_t block)
441 {
442         struct revoke_entry *revoke_entry;
443
444         ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
445         revoke_entry = jbd_revoke_entry_lookup(info, block);
446         if (revoke_entry) {
447                 revoke_entry->trans_id = info->this_trans_id;
448                 return;
449         }
450
451         revoke_entry = jbd_alloc_revoke_entry();
452         ext4_assert(revoke_entry);
453         revoke_entry->block = block;
454         revoke_entry->trans_id = info->this_trans_id;
455         RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
456
457         return;
458 }
459
460 static void jbd_destroy_revoke_tree(struct recover_info *info)
461 {
462         while (!RB_EMPTY(&info->revoke_root)) {
463                 struct revoke_entry *revoke_entry =
464                         RB_MIN(jbd_revoke, &info->revoke_root);
465                 ext4_assert(revoke_entry);
466                 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
467                 jbd_free_revoke_entry(revoke_entry);
468         }
469 }
470
471 /* Make sure we wrap around the log correctly! */
472 #define wrap(sb, var)                                           \
473 do {                                                                    \
474         if (var >= jbd_get32((sb), maxlen))                                     \
475                 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first));      \
476 } while (0)
477
478 #define ACTION_SCAN 0
479 #define ACTION_REVOKE 1
480 #define ACTION_RECOVER 2
481
482
483 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
484                                   struct jbd_bhdr *header,
485                                   struct recover_info *info)
486 {
487         char *blocks_entry;
488         struct jbd_revoke_header *revoke_hdr =
489                 (struct jbd_revoke_header *)header;
490         uint32_t i, nr_entries, record_len = 4;
491         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
492                                      JBD_FEATURE_INCOMPAT_64BIT))
493                 record_len = 8;
494
495         nr_entries = (jbd_get32(revoke_hdr, count) -
496                         sizeof(struct jbd_revoke_header)) /
497                         record_len;
498
499         blocks_entry = (char *)(revoke_hdr + 1);
500
501         for (i = 0;i < nr_entries;i++) {
502                 if (record_len == 8) {
503                         uint64_t *blocks =
504                                 (uint64_t *)blocks_entry;
505                         jbd_add_revoke_block_tags(info, to_be64(*blocks));
506                 } else {
507                         uint32_t *blocks =
508                                 (uint32_t *)blocks_entry;
509                         jbd_add_revoke_block_tags(info, to_be32(*blocks));
510                 }
511                 blocks_entry += record_len;
512         }
513 }
514
515 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
516                                        struct jbd_bhdr *header,
517                                        uint32_t *iblock)
518 {
519         jbd_iterate_block_table(jbd_fs,
520                                 header + 1,
521                                 jbd_get32(&jbd_fs->sb, blocksize) -
522                                         sizeof(struct jbd_bhdr),
523                                 jbd_display_block_tags,
524                                 iblock);
525 }
526
527 static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
528                                         struct jbd_bhdr *header,
529                                         struct replay_arg *arg)
530 {
531         jbd_iterate_block_table(jbd_fs,
532                                 header + 1,
533                                 jbd_get32(&jbd_fs->sb, blocksize) -
534                                         sizeof(struct jbd_bhdr),
535                                 jbd_replay_block_tags,
536                                 arg);
537 }
538
539 int jbd_iterate_log(struct jbd_fs *jbd_fs,
540                     struct recover_info *info,
541                     int action)
542 {
543         int r = EOK;
544         bool log_end = false;
545         struct jbd_sb *sb = &jbd_fs->sb;
546         uint32_t start_trans_id, this_trans_id;
547         uint32_t start_block, this_block;
548
549         start_trans_id = this_trans_id = jbd_get32(sb, sequence);
550         start_block = this_block = jbd_get32(sb, start);
551
552         ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
553                             start_trans_id);
554
555         while (!log_end) {
556                 struct ext4_block block;
557                 struct jbd_bhdr *header;
558                 if (action != ACTION_SCAN)
559                         if (this_trans_id > info->last_trans_id) {
560                                 log_end = true;
561                                 continue;
562                         }
563
564                 r = jbd_block_get(jbd_fs, &block, this_block);
565                 if (r != EOK)
566                         break;
567
568                 header = (struct jbd_bhdr *)block.data;
569                 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
570                         jbd_block_set(jbd_fs, &block);
571                         log_end = true;
572                         continue;
573                 }
574
575                 if (jbd_get32(header, sequence) != this_trans_id) {
576                         if (action != ACTION_SCAN)
577                                 r = EIO;
578
579                         jbd_block_set(jbd_fs, &block);
580                         log_end = true;
581                         continue;
582                 }
583
584                 switch (jbd_get32(header, blocktype)) {
585                 case JBD_DESCRIPTOR_BLOCK:
586                         ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
587                                             "trans_id: %" PRIu32"\n",
588                                             this_block, this_trans_id);
589                         if (action == ACTION_RECOVER) {
590                                 struct replay_arg replay_arg;
591                                 replay_arg.info = info;
592                                 replay_arg.this_block = &this_block;
593                                 replay_arg.this_trans_id = this_trans_id;
594
595                                 jbd_replay_descriptor_block(jbd_fs,
596                                                 header, &replay_arg);
597                         } else
598                                 jbd_debug_descriptor_block(jbd_fs,
599                                                 header, &this_block);
600
601                         break;
602                 case JBD_COMMIT_BLOCK:
603                         ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
604                                             "trans_id: %" PRIu32"\n",
605                                             this_block, this_trans_id);
606                         this_trans_id++;
607                         break;
608                 case JBD_REVOKE_BLOCK:
609                         ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
610                                             "trans_id: %" PRIu32"\n",
611                                             this_block, this_trans_id);
612                         if (action == ACTION_REVOKE) {
613                                 info->this_trans_id = this_trans_id;
614                                 jbd_build_revoke_tree(jbd_fs,
615                                                 header, info);
616                         }
617                         break;
618                 default:
619                         log_end = true;
620                         break;
621                 }
622                 jbd_block_set(jbd_fs, &block);
623                 this_block++;
624                 wrap(sb, this_block);
625                 if (this_block == start_block)
626                         log_end = true;
627
628         }
629         ext4_dbg(DEBUG_JBD, "End of journal.\n");
630         if (r == EOK && action == ACTION_SCAN) {
631                 info->start_trans_id = start_trans_id;
632                 if (this_trans_id > start_trans_id)
633                         info->last_trans_id = this_trans_id - 1;
634                 else
635                         info->last_trans_id = this_trans_id;
636         }
637
638         return r;
639 }
640
641 int jbd_recover(struct jbd_fs *jbd_fs)
642 {
643         int r;
644         struct recover_info info;
645         struct jbd_sb *sb = &jbd_fs->sb;
646         if (!sb->start)
647                 return EOK;
648
649         RB_INIT(&info.revoke_root);
650
651         r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
652         if (r != EOK)
653                 return r;
654
655         r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
656         if (r != EOK)
657                 return r;
658
659         r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
660         if (r == EOK) {
661                 jbd_set32(&jbd_fs->sb, start, 0);
662                 jbd_fs->dirty = true;
663         }
664         jbd_destroy_revoke_tree(&info);
665         return r;
666 }
667
668 /**
669  * @}
670  */