ext4_journal: fix wrong parsing of revoke block.
[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         }
154
155         return rc;
156 }
157
158 int jbd_put_fs(struct jbd_fs *jbd_fs)
159 {
160         int rc;
161         if (jbd_fs->dirty)
162                 jbd_sb_write(jbd_fs, &jbd_fs->sb);
163
164         rc = ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
165         return rc;
166 }
167
168 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
169                    ext4_lblk_t iblock,
170                    ext4_fsblk_t *fblock)
171 {
172         int rc = ext4_fs_get_inode_data_block_index(
173                         &jbd_fs->inode_ref,
174                         iblock,
175                         fblock,
176                         false);
177         return rc;
178 }
179
180 int jbd_block_get(struct jbd_fs *jbd_fs,
181                   struct ext4_block *block,
182                   ext4_fsblk_t fblock)
183 {
184         /* TODO: journal device. */
185         int rc;
186         ext4_lblk_t iblock = (ext4_lblk_t)fblock;
187         rc = jbd_inode_bmap(jbd_fs, iblock,
188                             &fblock);
189         if (rc != EOK)
190                 return rc;
191
192         struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
193         rc = ext4_block_get(bdev, block, fblock);
194         return rc;
195 }
196
197 int jbd_block_get_noread(struct jbd_fs *jbd_fs,
198                          struct ext4_block *block,
199                          ext4_fsblk_t fblock)
200 {
201         /* TODO: journal device. */
202         int rc;
203         ext4_lblk_t iblock = (ext4_lblk_t)fblock;
204         rc = jbd_inode_bmap(jbd_fs, iblock,
205                             &fblock);
206         if (rc != EOK)
207                 return rc;
208
209         struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
210         rc = ext4_block_get_noread(bdev, block, fblock);
211         return rc;
212 }
213
214 int jbd_block_set(struct jbd_fs *jbd_fs,
215                   struct ext4_block *block)
216 {
217         return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
218                               block);
219 }
220
221 /*
222  * helper functions to deal with 32 or 64bit block numbers.
223  */
224 int jbd_tag_bytes(struct jbd_fs *jbd_fs)
225 {
226         int size;
227
228         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
229                                      JBD_FEATURE_INCOMPAT_CSUM_V3))
230                 return sizeof(struct jbd_block_tag3);
231
232         size = sizeof(struct jbd_block_tag);
233
234         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
235                                      JBD_FEATURE_INCOMPAT_CSUM_V2))
236                 size += sizeof(uint16_t);
237
238         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
239                                      JBD_FEATURE_INCOMPAT_64BIT))
240                 return size;
241
242         return size - sizeof(uint32_t);
243 }
244
245 static void
246 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
247                       uint32_t tag_bytes,
248                       void *__tag,
249                       ext4_fsblk_t *block,
250                       bool *uuid_exist,
251                       uint8_t *uuid,
252                       bool *last_tag)
253 {
254         char *uuid_start;
255         *uuid_exist = false;
256         *last_tag = false;
257         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
258                                      JBD_FEATURE_INCOMPAT_CSUM_V3)) {
259                 struct jbd_block_tag3 *tag = __tag;
260                 *block = jbd_get32(tag, blocknr);
261                 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
262                                              JBD_FEATURE_INCOMPAT_64BIT))
263                          *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
264
265                 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
266                         *block = 0;
267
268                 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
269                         uuid_start = (char *)tag + tag_bytes;
270                         *uuid_exist = true;
271                         memcpy(uuid, uuid_start, UUID_SIZE);
272                 }
273
274                 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
275                         *last_tag = true;
276
277         } else {
278                 struct jbd_block_tag *tag = __tag;
279                 *block = jbd_get32(tag, blocknr);
280                 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
281                                              JBD_FEATURE_INCOMPAT_64BIT))
282                          *block |= (uint64_t)jbd_get32(tag, blocknr_high) << 32;
283
284                 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
285                         *block = 0;
286
287                 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
288                         uuid_start = (char *)tag + tag_bytes;
289                         *uuid_exist = true;
290                         memcpy(uuid, uuid_start, UUID_SIZE);
291                 }
292
293                 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
294                         *last_tag = true;
295
296         }
297 }
298
299 static void
300 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
301                         void *__tag_start,
302                         uint32_t tag_tbl_size,
303                         void (*func)(struct jbd_fs * jbd_fs,
304                                         ext4_fsblk_t block,
305                                         uint8_t *uuid,
306                                         void *arg),
307                         void *arg)
308 {
309         ext4_fsblk_t block = 0;
310         uint8_t uuid[UUID_SIZE];
311         char *tag_start, *tag_ptr;
312         uint32_t tag_bytes = jbd_tag_bytes(jbd_fs);
313         tag_start = __tag_start;
314         tag_ptr = tag_start;
315
316         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
317                                      JBD_FEATURE_INCOMPAT_CSUM_V2) ||
318             JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
319                                      JBD_FEATURE_INCOMPAT_CSUM_V3))
320                 tag_tbl_size -= sizeof(struct jbd_block_tail);
321
322         while (tag_ptr - tag_start + tag_bytes <= tag_tbl_size) {
323                 bool uuid_exist;
324                 bool last_tag;
325                 jbd_extract_block_tag(jbd_fs,
326                                       tag_bytes,
327                                       tag_ptr,
328                                       &block,
329                                       &uuid_exist,
330                                       uuid,
331                                       &last_tag);
332                 if (func)
333                         func(jbd_fs, block, uuid, arg);
334
335                 if (last_tag)
336                         break;
337
338                 tag_ptr += tag_bytes;
339                 if (uuid_exist)
340                         tag_ptr += UUID_SIZE;
341
342         }
343 }
344
345 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
346                                    ext4_fsblk_t block,
347                                    uint8_t *uuid,
348                                    void *arg)
349 {
350         uint32_t *iblock = arg;
351         ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
352         (*iblock)++;
353         (void)jbd_fs;
354         (void)uuid;
355         return;
356 }
357
358 static struct revoke_entry *
359 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
360 {
361         struct revoke_entry tmp = {
362                 .block = block
363         };
364
365         return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
366 }
367
368 static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
369                                   ext4_fsblk_t block,
370                                   uint8_t *uuid __unused,
371                                   void *__arg)
372 {
373         int r;
374         struct replay_arg *arg = __arg;
375         struct recover_info *info = arg->info;
376         uint32_t *this_block = arg->this_block;
377         struct revoke_entry *revoke_entry;
378         struct ext4_block journal_block, ext4_block;
379         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
380
381         (*this_block)++;
382
383         revoke_entry = jbd_revoke_entry_lookup(info, block);
384         if (revoke_entry &&
385             arg->this_trans_id < revoke_entry->trans_id)
386                 return;
387
388         ext4_dbg(DEBUG_JBD,
389                  "Replaying block in block_tag: %" PRIu64 "\n",
390                  block);
391
392         r = jbd_block_get(jbd_fs, &journal_block, *this_block);
393         if (r != EOK)
394                 return;
395
396         if (block) {
397                 r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
398                 if (r != EOK) {
399                         jbd_block_set(jbd_fs, &journal_block);
400                         return;
401                 }
402
403                 memcpy(ext4_block.data,
404                         journal_block.data,
405                         jbd_get32(&jbd_fs->sb, blocksize));
406
407                 ext4_block.dirty = true;
408                 ext4_block_set(fs->bdev, &ext4_block);
409         } else {
410                 uint16_t mount_count, state;
411                 mount_count = ext4_get16(&fs->sb, mount_count);
412                 state = ext4_get16(&fs->sb, state);
413
414                 memcpy(&fs->sb,
415                         journal_block.data + EXT4_SUPERBLOCK_OFFSET,
416                         EXT4_SUPERBLOCK_SIZE);
417
418                 /* Mark system as mounted */
419                 ext4_set16(&fs->sb, state, state);
420                 r = ext4_sb_write(fs->bdev, &fs->sb);
421                 if (r != EOK)
422                         return;
423
424                 /*Update mount count*/
425                 ext4_set16(&fs->sb, mount_count, mount_count);
426         }
427
428         jbd_block_set(jbd_fs, &journal_block);
429         
430         return;
431 }
432
433 static void jbd_add_revoke_block_tags(struct recover_info *info,
434                                       ext4_fsblk_t block)
435 {
436         struct revoke_entry *revoke_entry;
437
438         ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
439         revoke_entry = jbd_revoke_entry_lookup(info, block);
440         if (revoke_entry) {
441                 revoke_entry->trans_id = info->this_trans_id;
442                 return;
443         }
444
445         revoke_entry = jbd_alloc_revoke_entry();
446         ext4_assert(revoke_entry);
447         revoke_entry->block = block;
448         revoke_entry->trans_id = info->this_trans_id;
449         RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
450
451         return;
452 }
453
454 static void jbd_destroy_revoke_tree(struct recover_info *info)
455 {
456         while (!RB_EMPTY(&info->revoke_root)) {
457                 struct revoke_entry *revoke_entry =
458                         RB_MIN(jbd_revoke, &info->revoke_root);
459                 ext4_assert(revoke_entry);
460                 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
461                 jbd_free_revoke_entry(revoke_entry);
462         }
463 }
464
465 /* Make sure we wrap around the log correctly! */
466 #define wrap(sb, var)                                           \
467 do {                                                                    \
468         if (var >= jbd_get32((sb), maxlen))                                     \
469                 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first));      \
470 } while (0)
471
472 #define ACTION_SCAN 0
473 #define ACTION_REVOKE 1
474 #define ACTION_RECOVER 2
475
476
477 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
478                                   struct jbd_bhdr *header,
479                                   struct recover_info *info)
480 {
481         char *blocks_entry;
482         struct jbd_revoke_header *revoke_hdr =
483                 (struct jbd_revoke_header *)header;
484         uint32_t i, nr_entries, record_len = 4;
485         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
486                                      JBD_FEATURE_INCOMPAT_64BIT))
487                 record_len = 8;
488
489         nr_entries = (jbd_get32(revoke_hdr, count) -
490                         sizeof(struct jbd_revoke_header)) /
491                         record_len;
492
493         blocks_entry = (char *)(revoke_hdr + 1);
494
495         for (i = 0;i < nr_entries;i++) {
496                 if (record_len == 8) {
497                         uint64_t *blocks =
498                                 (uint64_t *)blocks_entry;
499                         jbd_add_revoke_block_tags(info, to_be64(*blocks));
500                 } else {
501                         uint32_t *blocks =
502                                 (uint32_t *)blocks_entry;
503                         jbd_add_revoke_block_tags(info, to_be32(*blocks));
504                 }
505                 blocks_entry += record_len;
506         }
507 }
508
509 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
510                                        struct jbd_bhdr *header,
511                                        uint32_t *iblock)
512 {
513         jbd_iterate_block_table(jbd_fs,
514                                 header + 1,
515                                 jbd_get32(&jbd_fs->sb, blocksize) -
516                                         sizeof(struct jbd_bhdr),
517                                 jbd_display_block_tags,
518                                 iblock);
519 }
520
521 static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
522                                         struct jbd_bhdr *header,
523                                         struct replay_arg *arg)
524 {
525         jbd_iterate_block_table(jbd_fs,
526                                 header + 1,
527                                 jbd_get32(&jbd_fs->sb, blocksize) -
528                                         sizeof(struct jbd_bhdr),
529                                 jbd_replay_block_tags,
530                                 arg);
531 }
532
533 int jbd_iterate_log(struct jbd_fs *jbd_fs,
534                     struct recover_info *info,
535                     int action)
536 {
537         int r = EOK;
538         bool log_end = false;
539         struct jbd_sb *sb = &jbd_fs->sb;
540         uint32_t start_trans_id, this_trans_id;
541         uint32_t start_block, this_block;
542
543         start_trans_id = this_trans_id = jbd_get32(sb, sequence);
544         start_block = this_block = jbd_get32(sb, start);
545
546         ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
547                             start_trans_id);
548
549         while (!log_end) {
550                 struct ext4_block block;
551                 struct jbd_bhdr *header;
552                 if (action != ACTION_SCAN)
553                         if (this_trans_id > info->last_trans_id) {
554                                 log_end = true;
555                                 continue;
556                         }
557
558                 r = jbd_block_get(jbd_fs, &block, this_block);
559                 if (r != EOK)
560                         break;
561
562                 header = (struct jbd_bhdr *)block.data;
563                 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
564                         jbd_block_set(jbd_fs, &block);
565                         log_end = true;
566                         continue;
567                 }
568
569                 if (jbd_get32(header, sequence) != this_trans_id) {
570                         if (action != ACTION_SCAN)
571                                 r = EIO;
572
573                         jbd_block_set(jbd_fs, &block);
574                         log_end = true;
575                         continue;
576                 }
577
578                 switch (jbd_get32(header, blocktype)) {
579                 case JBD_DESCRIPTOR_BLOCK:
580                         ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
581                                             "trans_id: %" PRIu32"\n",
582                                             this_block, this_trans_id);
583                         if (action == ACTION_RECOVER) {
584                                 struct replay_arg replay_arg;
585                                 replay_arg.info = info;
586                                 replay_arg.this_block = &this_block;
587                                 replay_arg.this_trans_id = this_trans_id;
588
589                                 jbd_replay_descriptor_block(jbd_fs,
590                                                 header, &replay_arg);
591                         } else
592                                 jbd_debug_descriptor_block(jbd_fs,
593                                                 header, &this_block);
594
595                         break;
596                 case JBD_COMMIT_BLOCK:
597                         ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
598                                             "trans_id: %" PRIu32"\n",
599                                             this_block, this_trans_id);
600                         this_trans_id++;
601                         break;
602                 case JBD_REVOKE_BLOCK:
603                         ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
604                                             "trans_id: %" PRIu32"\n",
605                                             this_block, this_trans_id);
606                         if (action == ACTION_REVOKE) {
607                                 info->this_trans_id = this_trans_id;
608                                 jbd_build_revoke_tree(jbd_fs,
609                                                 header, info);
610                         }
611                         break;
612                 default:
613                         log_end = true;
614                         break;
615                 }
616                 jbd_block_set(jbd_fs, &block);
617                 this_block++;
618                 wrap(sb, this_block);
619                 if (this_block == start_block)
620                         log_end = true;
621
622         }
623         ext4_dbg(DEBUG_JBD, "End of journal.\n");
624         if (r == EOK && action == ACTION_SCAN) {
625                 info->start_trans_id = start_trans_id;
626                 if (this_trans_id > start_trans_id)
627                         info->last_trans_id = this_trans_id - 1;
628                 else
629                         info->last_trans_id = this_trans_id;
630         }
631
632         return r;
633 }
634
635 int jbd_recover(struct jbd_fs *jbd_fs)
636 {
637         int r;
638         struct recover_info info;
639         struct jbd_sb *sb = &jbd_fs->sb;
640         if (!sb->start)
641                 return EOK;
642
643         RB_INIT(&info.revoke_root);
644
645         r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
646         if (r != EOK)
647                 return r;
648
649         r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
650         if (r != EOK)
651                 return r;
652
653         r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
654         if (r == EOK) {
655                 jbd_set32(&jbd_fs->sb, start, 0);
656                 jbd_fs->dirty = true;
657         }
658         jbd_destroy_revoke_tree(&info);
659         return r;
660 }
661
662 /**
663  * @}
664  */