ext4_journal: add static keyword to all private functions
[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_journal.h"
43 #include "ext4_errno.h"
44 #include "ext4_blockdev.h"
45 #include "ext4_crc32c.h"
46 #include "ext4_debug.h"
47 #include "tree.h"
48
49 #include <string.h>
50 #include <stdlib.h>
51
52 /**@brief  Revoke entry during journal replay.*/
53 struct revoke_entry {
54         /**@brief  Block number not to be replayed.*/
55         ext4_fsblk_t block;
56
57         /**@brief  For any transaction id smaller
58          *         than trans_id, records of @block
59          *         in those transactions should not
60          *         be replayed.*/
61         uint32_t trans_id;
62
63         /**@brief  Revoke tree node.*/
64         RB_ENTRY(revoke_entry) revoke_node;
65 };
66
67 /**@brief  Valid journal replay information.*/
68 struct recover_info {
69         /**@brief  Starting transaction id.*/
70         uint32_t start_trans_id;
71
72         /**@brief  Ending transaction id.*/
73         uint32_t last_trans_id;
74
75         /**@brief  Used as internal argument.*/
76         uint32_t this_trans_id;
77
78         /**@brief  RB-Tree storing revoke entries.*/
79         RB_HEAD(jbd_revoke, revoke_entry) revoke_root;
80 };
81
82 /**@brief  Journal replay internal arguments.*/
83 struct replay_arg {
84         /**@brief  Journal replay information.*/
85         struct recover_info *info;
86
87         /**@brief  Current block we are on.*/
88         uint32_t *this_block;
89
90         /**@brief  Current trans_id we are on.*/
91         uint32_t this_trans_id;
92 };
93
94 static int
95 jbd_revoke_entry_cmp(struct revoke_entry *a, struct revoke_entry *b)
96 {
97         if (a->block > b->block)
98                 return 1;
99         else if (a->block < b->block)
100                 return -1;
101         return 0;
102 }
103
104 RB_GENERATE_INTERNAL(jbd_revoke, revoke_entry, revoke_node,
105                      jbd_revoke_entry_cmp, static inline)
106
107 #define jbd_alloc_revoke_entry() calloc(1, sizeof(struct revoke_entry))
108 #define jbd_free_revoke_entry(addr) free(addr)
109
110 /**@brief  Write jbd superblock to disk.
111  * @param  jbd_fs jbd filesystem
112  * @param  s jbd superblock
113  * @return standard error code*/
114 static int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
115 {
116         int rc;
117         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
118         uint64_t offset;
119         ext4_fsblk_t fblock;
120         rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
121         if (rc != EOK)
122                 return rc;
123
124         offset = fblock * ext4_sb_get_block_size(&fs->sb);
125         return ext4_block_writebytes(fs->bdev, offset, s,
126                                      EXT4_SUPERBLOCK_SIZE);
127 }
128
129 /**@brief  Read jbd superblock from disk.
130  * @param  jbd_fs jbd filesystem
131  * @param  s jbd superblock
132  * @return standard error code*/
133 static int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
134 {
135         int rc;
136         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
137         uint64_t offset;
138         ext4_fsblk_t fblock;
139         rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
140         if (rc != EOK)
141                 return rc;
142
143         offset = fblock * ext4_sb_get_block_size(&fs->sb);
144         return ext4_block_readbytes(fs->bdev, offset, s,
145                                     EXT4_SUPERBLOCK_SIZE);
146 }
147
148 /**@brief  Verify jbd superblock.
149  * @param  sb jbd superblock
150  * @return true if jbd superblock is valid */
151 static bool jbd_verify_sb(struct jbd_sb *sb)
152 {
153         struct jbd_bhdr *header = &sb->header;
154         if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER)
155                 return false;
156
157         if (jbd_get32(header, blocktype) != JBD_SUPERBLOCK &&
158             jbd_get32(header, blocktype) != JBD_SUPERBLOCK_V2)
159                 return false;
160
161         return true;
162 }
163
164 /**@brief  Write back dirty jbd superblock to disk.
165  * @param  jbd_fs jbd filesystem
166  * @return standard error code*/
167 static int jbd_write_sb(struct jbd_fs *jbd_fs)
168 {
169         int rc = EOK;
170         if (jbd_fs->dirty) {
171                 rc = jbd_sb_write(jbd_fs, &jbd_fs->sb);
172                 if (rc != EOK)
173                         return rc;
174
175                 jbd_fs->dirty = false;
176         }
177         return rc;
178 }
179
180 /**@brief  Get reference to jbd filesystem.
181  * @param  fs Filesystem to load journal of
182  * @param  jbd_fs jbd filesystem
183  * @return standard error code*/
184 int jbd_get_fs(struct ext4_fs *fs,
185                struct jbd_fs *jbd_fs)
186 {
187         int rc;
188         uint32_t journal_ino;
189
190         memset(jbd_fs, 0, sizeof(struct jbd_fs));
191         /* See if there is journal inode on this filesystem.*/
192         /* FIXME: detection on existance ofbkejournal bdev is
193          *        missing.*/
194         journal_ino = ext4_get32(&fs->sb, journal_inode_number);
195
196         rc = ext4_fs_get_inode_ref(fs,
197                                    journal_ino,
198                                    &jbd_fs->inode_ref);
199         if (rc != EOK) {
200                 memset(jbd_fs, 0, sizeof(struct jbd_fs));
201                 return rc;
202         }
203         rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
204         if (rc != EOK) {
205                 memset(jbd_fs, 0, sizeof(struct jbd_fs));
206                 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
207                 return rc;
208         }
209         if (!jbd_verify_sb(&jbd_fs->sb)) {
210                 memset(jbd_fs, 0, sizeof(struct jbd_fs));
211                 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
212                 rc = EIO;
213         }
214
215         return rc;
216 }
217
218 /**@brief  Put reference of jbd filesystem.
219  * @param  jbd_fs jbd filesystem
220  * @return standard error code*/
221 int jbd_put_fs(struct jbd_fs *jbd_fs)
222 {
223         int rc = EOK;
224         rc = jbd_write_sb(jbd_fs);
225
226         ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
227         return rc;
228 }
229
230 /**@brief  Data block lookup helper.
231  * @param  jbd_fs jbd filesystem
232  * @param  iblock block index
233  * @param  fblock logical block address
234  * @return standard error code*/
235 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
236                    ext4_lblk_t iblock,
237                    ext4_fsblk_t *fblock)
238 {
239         int rc = ext4_fs_get_inode_dblk_idx(
240                         &jbd_fs->inode_ref,
241                         iblock,
242                         fblock,
243                         false);
244         return rc;
245 }
246
247 /**@brief   jbd block get function (through cache).
248  * @param   jbd_fs jbd filesystem
249  * @param   block block descriptor
250  * @param   fblock jbd logical block address
251  * @return  standard error code*/
252 static int jbd_block_get(struct jbd_fs *jbd_fs,
253                   struct ext4_block *block,
254                   ext4_fsblk_t fblock)
255 {
256         /* TODO: journal device. */
257         int rc;
258         ext4_lblk_t iblock = (ext4_lblk_t)fblock;
259
260         /* Lookup the logical block address of
261          * fblock.*/
262         rc = jbd_inode_bmap(jbd_fs, iblock,
263                             &fblock);
264         if (rc != EOK)
265                 return rc;
266
267         struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
268         rc = ext4_block_get(bdev, block, fblock);
269
270         /* If succeeded, mark buffer as BC_FLUSH to indicate
271          * that data should be written to disk immediately.*/
272         if (rc == EOK)
273                 ext4_bcache_set_flag(block->buf, BC_FLUSH);
274
275         return rc;
276 }
277
278 /**@brief   jbd block get function (through cache, don't read).
279  * @param   jbd_fs jbd filesystem
280  * @param   block block descriptor
281  * @param   fblock jbd logical block address
282  * @return  standard error code*/
283 static int jbd_block_get_noread(struct jbd_fs *jbd_fs,
284                          struct ext4_block *block,
285                          ext4_fsblk_t fblock)
286 {
287         /* TODO: journal device. */
288         int rc;
289         ext4_lblk_t iblock = (ext4_lblk_t)fblock;
290         rc = jbd_inode_bmap(jbd_fs, iblock,
291                             &fblock);
292         if (rc != EOK)
293                 return rc;
294
295         struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
296         rc = ext4_block_get_noread(bdev, block, fblock);
297         if (rc == EOK)
298                 ext4_bcache_set_flag(block->buf, BC_FLUSH);
299
300         return rc;
301 }
302
303 /**@brief   jbd block set procedure (through cache).
304  * @param   jbd_fs jbd filesystem
305  * @param   block block descriptor
306  * @return  standard error code*/
307 static int jbd_block_set(struct jbd_fs *jbd_fs,
308                   struct ext4_block *block)
309 {
310         return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
311                               block);
312 }
313
314 /**@brief  helper functions to calculate
315  *         block tag size, not including UUID part.
316  * @param  jbd_fs jbd filesystem
317  * @return tag size in bytes*/
318 static int jbd_tag_bytes(struct jbd_fs *jbd_fs)
319 {
320         int size;
321
322         /* It is very easy to deal with the case which
323          * JBD_FEATURE_INCOMPAT_CSUM_V3 is enabled.*/
324         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
325                                      JBD_FEATURE_INCOMPAT_CSUM_V3))
326                 return sizeof(struct jbd_block_tag3);
327
328         size = sizeof(struct jbd_block_tag);
329
330         /* If JBD_FEATURE_INCOMPAT_CSUM_V2 is enabled,
331          * add 2 bytes to size.*/
332         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
333                                      JBD_FEATURE_INCOMPAT_CSUM_V2))
334                 size += sizeof(uint16_t);
335
336         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
337                                      JBD_FEATURE_INCOMPAT_64BIT))
338                 return size;
339
340         /* If block number is 4 bytes in size,
341          * minus 4 bytes from size */
342         return size - sizeof(uint32_t);
343 }
344
345 /**@brief  Tag information. */
346 struct tag_info {
347         /**@brief  Tag size in bytes, including UUID part.*/
348         int tag_bytes;
349
350         /**@brief  block number stored in this tag.*/
351         ext4_fsblk_t block;
352
353         /**@brief  whether UUID part exists or not.*/
354         bool uuid_exist;
355
356         /**@brief  UUID content if UUID part exists.*/
357         uint8_t uuid[UUID_SIZE];
358
359         /**@brief  Is this the last tag? */
360         bool last_tag;
361 };
362
363 /**@brief  Extract information from a block tag.
364  * @param  __tag pointer to the block tag
365  * @param  tag_bytes block tag size of this jbd filesystem
366  * @param  remaining size in buffer containing the block tag
367  * @param  tag_info information of this tag.
368  * @return  EOK when succeed, otherwise return EINVAL.*/
369 static int
370 jbd_extract_block_tag(struct jbd_fs *jbd_fs,
371                       void *__tag,
372                       int tag_bytes,
373                       int32_t remain_buf_size,
374                       struct tag_info *tag_info)
375 {
376         char *uuid_start;
377         tag_info->tag_bytes = tag_bytes;
378         tag_info->uuid_exist = false;
379         tag_info->last_tag = false;
380
381         /* See whether it is possible to hold a valid block tag.*/
382         if (remain_buf_size - tag_bytes < 0)
383                 return EINVAL;
384
385         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
386                                      JBD_FEATURE_INCOMPAT_CSUM_V3)) {
387                 struct jbd_block_tag3 *tag = __tag;
388                 tag_info->block = jbd_get32(tag, blocknr);
389                 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
390                                              JBD_FEATURE_INCOMPAT_64BIT))
391                          tag_info->block |=
392                                  (uint64_t)jbd_get32(tag, blocknr_high) << 32;
393
394                 if (jbd_get32(tag, flags) & JBD_FLAG_ESCAPE)
395                         tag_info->block = 0;
396
397                 if (!(jbd_get32(tag, flags) & JBD_FLAG_SAME_UUID)) {
398                         /* See whether it is possible to hold UUID part.*/
399                         if (remain_buf_size - tag_bytes < UUID_SIZE)
400                                 return EINVAL;
401
402                         uuid_start = (char *)tag + tag_bytes;
403                         tag_info->uuid_exist = true;
404                         tag_info->tag_bytes += UUID_SIZE;
405                         memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
406                 }
407
408                 if (jbd_get32(tag, flags) & JBD_FLAG_LAST_TAG)
409                         tag_info->last_tag = true;
410
411         } else {
412                 struct jbd_block_tag *tag = __tag;
413                 tag_info->block = jbd_get32(tag, blocknr);
414                 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
415                                              JBD_FEATURE_INCOMPAT_64BIT))
416                          tag_info->block |=
417                                  (uint64_t)jbd_get32(tag, blocknr_high) << 32;
418
419                 if (jbd_get16(tag, flags) & JBD_FLAG_ESCAPE)
420                         tag_info->block = 0;
421
422                 if (!(jbd_get16(tag, flags) & JBD_FLAG_SAME_UUID)) {
423                         /* See whether it is possible to hold UUID part.*/
424                         if (remain_buf_size - tag_bytes < UUID_SIZE)
425                                 return EINVAL;
426
427                         uuid_start = (char *)tag + tag_bytes;
428                         tag_info->uuid_exist = true;
429                         tag_info->tag_bytes += UUID_SIZE;
430                         memcpy(tag_info->uuid, uuid_start, UUID_SIZE);
431                 }
432
433                 if (jbd_get16(tag, flags) & JBD_FLAG_LAST_TAG)
434                         tag_info->last_tag = true;
435
436         }
437         return EOK;
438 }
439
440 /**@brief  Write information to a block tag.
441  * @param  __tag pointer to the block tag
442  * @param  remaining size in buffer containing the block tag
443  * @param  tag_info information of this tag.
444  * @return  EOK when succeed, otherwise return EINVAL.*/
445 static int
446 jbd_write_block_tag(struct jbd_fs *jbd_fs,
447                     void *__tag,
448                     int32_t remain_buf_size,
449                     struct tag_info *tag_info)
450 {
451         char *uuid_start;
452         int tag_bytes = jbd_tag_bytes(jbd_fs);
453
454         tag_info->tag_bytes = tag_bytes;
455
456         /* See whether it is possible to hold a valid block tag.*/
457         if (remain_buf_size - tag_bytes < 0)
458                 return EINVAL;
459
460         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
461                                      JBD_FEATURE_INCOMPAT_CSUM_V3)) {
462                 struct jbd_block_tag3 *tag = __tag;
463                 jbd_set32(tag, blocknr, tag_info->block);
464                 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
465                                              JBD_FEATURE_INCOMPAT_64BIT))
466                         jbd_set32(tag, blocknr_high, tag_info->block >> 32);
467
468                 if (tag_info->uuid_exist) {
469                         /* See whether it is possible to hold UUID part.*/
470                         if (remain_buf_size - tag_bytes < UUID_SIZE)
471                                 return EINVAL;
472
473                         uuid_start = (char *)tag + tag_bytes;
474                         tag_info->tag_bytes += UUID_SIZE;
475                         memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
476                 } else
477                         jbd_set32(tag, flags,
478                                   jbd_get32(tag, flags) | JBD_FLAG_SAME_UUID);
479
480                 if (tag_info->last_tag)
481                         jbd_set32(tag, flags,
482                                   jbd_get32(tag, flags) | JBD_FLAG_LAST_TAG);
483
484         } else {
485                 struct jbd_block_tag *tag = __tag;
486                 jbd_set32(tag, blocknr, tag_info->block);
487                 if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
488                                              JBD_FEATURE_INCOMPAT_64BIT))
489                         jbd_set32(tag, blocknr_high, tag_info->block >> 32);
490
491                 if (tag_info->uuid_exist) {
492                         /* See whether it is possible to hold UUID part.*/
493                         if (remain_buf_size - tag_bytes < UUID_SIZE)
494                                 return EINVAL;
495
496                         uuid_start = (char *)tag + tag_bytes;
497                         tag_info->tag_bytes += UUID_SIZE;
498                         memcpy(uuid_start, tag_info->uuid, UUID_SIZE);
499                 } else
500                         jbd_set16(tag, flags,
501                                   jbd_get16(tag, flags) | JBD_FLAG_SAME_UUID);
502
503                 if (tag_info->last_tag)
504                         jbd_set16(tag, flags,
505                                   jbd_get16(tag, flags) | JBD_FLAG_LAST_TAG);
506
507         }
508         return EOK;
509 }
510
511 /**@brief  Iterate all block tags in a block.
512  * @param  jbd_fs jbd filesystem
513  * @param  __tag_start pointer to the block
514  * @param  tag_tbl_size size of the block
515  * @param  func callback routine to indicate that
516  *         a block tag is found
517  * @param  arg additional argument to be passed to func */
518 static void
519 jbd_iterate_block_table(struct jbd_fs *jbd_fs,
520                         void *__tag_start,
521                         int32_t tag_tbl_size,
522                         void (*func)(struct jbd_fs * jbd_fs,
523                                         ext4_fsblk_t block,
524                                         uint8_t *uuid,
525                                         void *arg),
526                         void *arg)
527 {
528         char *tag_start, *tag_ptr;
529         int tag_bytes = jbd_tag_bytes(jbd_fs);
530         tag_start = __tag_start;
531         tag_ptr = tag_start;
532
533         /* Cut off the size of block tail storing checksum. */
534         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
535                                      JBD_FEATURE_INCOMPAT_CSUM_V2) ||
536             JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
537                                      JBD_FEATURE_INCOMPAT_CSUM_V3))
538                 tag_tbl_size -= sizeof(struct jbd_block_tail);
539
540         while (tag_tbl_size) {
541                 struct tag_info tag_info;
542                 int rc = jbd_extract_block_tag(jbd_fs,
543                                       tag_ptr,
544                                       tag_bytes,
545                                       tag_tbl_size,
546                                       &tag_info);
547                 if (rc != EOK)
548                         break;
549
550                 if (func)
551                         func(jbd_fs, tag_info.block, tag_info.uuid, arg);
552
553                 /* Stop the iteration when we reach the last tag. */
554                 if (tag_info.last_tag)
555                         break;
556
557                 tag_ptr += tag_info.tag_bytes;
558                 tag_tbl_size -= tag_info.tag_bytes;
559         }
560 }
561
562 static void jbd_display_block_tags(struct jbd_fs *jbd_fs,
563                                    ext4_fsblk_t block,
564                                    uint8_t *uuid,
565                                    void *arg)
566 {
567         uint32_t *iblock = arg;
568         ext4_dbg(DEBUG_JBD, "Block in block_tag: %" PRIu64 "\n", block);
569         (*iblock)++;
570         (void)jbd_fs;
571         (void)uuid;
572         return;
573 }
574
575 static struct revoke_entry *
576 jbd_revoke_entry_lookup(struct recover_info *info, ext4_fsblk_t block)
577 {
578         struct revoke_entry tmp = {
579                 .block = block
580         };
581
582         return RB_FIND(jbd_revoke, &info->revoke_root, &tmp);
583 }
584
585 /**@brief  Replay a block in a transaction.
586  * @param  jbd_fs jbd filesystem
587  * @param  block  block address to be replayed.*/
588 static void jbd_replay_block_tags(struct jbd_fs *jbd_fs,
589                                   ext4_fsblk_t block,
590                                   uint8_t *uuid __unused,
591                                   void *__arg)
592 {
593         int r;
594         struct replay_arg *arg = __arg;
595         struct recover_info *info = arg->info;
596         uint32_t *this_block = arg->this_block;
597         struct revoke_entry *revoke_entry;
598         struct ext4_block journal_block, ext4_block;
599         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
600
601         (*this_block)++;
602
603         /* We replay this block only if the current transaction id
604          * is equal or greater than that in revoke entry.*/
605         revoke_entry = jbd_revoke_entry_lookup(info, block);
606         if (revoke_entry &&
607             arg->this_trans_id < revoke_entry->trans_id)
608                 return;
609
610         ext4_dbg(DEBUG_JBD,
611                  "Replaying block in block_tag: %" PRIu64 "\n",
612                  block);
613
614         r = jbd_block_get(jbd_fs, &journal_block, *this_block);
615         if (r != EOK)
616                 return;
617
618         /* We need special treatment for ext4 superblock. */
619         if (block) {
620                 r = ext4_block_get_noread(fs->bdev, &ext4_block, block);
621                 if (r != EOK) {
622                         jbd_block_set(jbd_fs, &journal_block);
623                         return;
624                 }
625
626                 memcpy(ext4_block.data,
627                         journal_block.data,
628                         jbd_get32(&jbd_fs->sb, blocksize));
629
630                 ext4_bcache_set_dirty(ext4_block.buf);
631                 ext4_block_set(fs->bdev, &ext4_block);
632         } else {
633                 uint16_t mount_count, state;
634                 mount_count = ext4_get16(&fs->sb, mount_count);
635                 state = ext4_get16(&fs->sb, state);
636
637                 memcpy(&fs->sb,
638                         journal_block.data + EXT4_SUPERBLOCK_OFFSET,
639                         EXT4_SUPERBLOCK_SIZE);
640
641                 /* Mark system as mounted */
642                 ext4_set16(&fs->sb, state, state);
643                 r = ext4_sb_write(fs->bdev, &fs->sb);
644                 if (r != EOK)
645                         return;
646
647                 /*Update mount count*/
648                 ext4_set16(&fs->sb, mount_count, mount_count);
649         }
650
651         jbd_block_set(jbd_fs, &journal_block);
652         
653         return;
654 }
655
656 /**@brief  Add block address to revoke tree, along with
657  *         its transaction id.
658  * @param  info  journal replay info
659  * @param  block  block address to be replayed.*/
660 static void jbd_add_revoke_block_tags(struct recover_info *info,
661                                       ext4_fsblk_t block)
662 {
663         struct revoke_entry *revoke_entry;
664
665         ext4_dbg(DEBUG_JBD, "Add block %" PRIu64 " to revoke tree\n", block);
666         /* If the revoke entry with respect to the block address
667          * exists already, update its transaction id.*/
668         revoke_entry = jbd_revoke_entry_lookup(info, block);
669         if (revoke_entry) {
670                 revoke_entry->trans_id = info->this_trans_id;
671                 return;
672         }
673
674         revoke_entry = jbd_alloc_revoke_entry();
675         ext4_assert(revoke_entry);
676         revoke_entry->block = block;
677         revoke_entry->trans_id = info->this_trans_id;
678         RB_INSERT(jbd_revoke, &info->revoke_root, revoke_entry);
679
680         return;
681 }
682
683 static void jbd_destroy_revoke_tree(struct recover_info *info)
684 {
685         while (!RB_EMPTY(&info->revoke_root)) {
686                 struct revoke_entry *revoke_entry =
687                         RB_MIN(jbd_revoke, &info->revoke_root);
688                 ext4_assert(revoke_entry);
689                 RB_REMOVE(jbd_revoke, &info->revoke_root, revoke_entry);
690                 jbd_free_revoke_entry(revoke_entry);
691         }
692 }
693
694 /* Make sure we wrap around the log correctly! */
695 #define wrap(sb, var)                                           \
696 do {                                                                    \
697         if (var >= jbd_get32((sb), maxlen))                                     \
698                 var -= (jbd_get32((sb), maxlen) - jbd_get32((sb), first));      \
699 } while (0)
700
701 #define ACTION_SCAN 0
702 #define ACTION_REVOKE 1
703 #define ACTION_RECOVER 2
704
705 /**@brief  Add entries in a revoke block to revoke tree.
706  * @param  jbd_fs jbd filesystem
707  * @param  header revoke block header
708  * @param  recover_info  journal replay info*/
709 static void jbd_build_revoke_tree(struct jbd_fs *jbd_fs,
710                                   struct jbd_bhdr *header,
711                                   struct recover_info *info)
712 {
713         char *blocks_entry;
714         struct jbd_revoke_header *revoke_hdr =
715                 (struct jbd_revoke_header *)header;
716         uint32_t i, nr_entries, record_len = 4;
717
718         /* If we are working on a 64bit jbd filesystem, */
719         if (JBD_HAS_INCOMPAT_FEATURE(&jbd_fs->sb,
720                                      JBD_FEATURE_INCOMPAT_64BIT))
721                 record_len = 8;
722
723         nr_entries = (jbd_get32(revoke_hdr, count) -
724                         sizeof(struct jbd_revoke_header)) /
725                         record_len;
726
727         blocks_entry = (char *)(revoke_hdr + 1);
728
729         for (i = 0;i < nr_entries;i++) {
730                 if (record_len == 8) {
731                         uint64_t *blocks =
732                                 (uint64_t *)blocks_entry;
733                         jbd_add_revoke_block_tags(info, to_be64(*blocks));
734                 } else {
735                         uint32_t *blocks =
736                                 (uint32_t *)blocks_entry;
737                         jbd_add_revoke_block_tags(info, to_be32(*blocks));
738                 }
739                 blocks_entry += record_len;
740         }
741 }
742
743 static void jbd_debug_descriptor_block(struct jbd_fs *jbd_fs,
744                                        struct jbd_bhdr *header,
745                                        uint32_t *iblock)
746 {
747         jbd_iterate_block_table(jbd_fs,
748                                 header + 1,
749                                 jbd_get32(&jbd_fs->sb, blocksize) -
750                                         sizeof(struct jbd_bhdr),
751                                 jbd_display_block_tags,
752                                 iblock);
753 }
754
755 static void jbd_replay_descriptor_block(struct jbd_fs *jbd_fs,
756                                         struct jbd_bhdr *header,
757                                         struct replay_arg *arg)
758 {
759         jbd_iterate_block_table(jbd_fs,
760                                 header + 1,
761                                 jbd_get32(&jbd_fs->sb, blocksize) -
762                                         sizeof(struct jbd_bhdr),
763                                 jbd_replay_block_tags,
764                                 arg);
765 }
766
767 /**@brief  The core routine of journal replay.
768  * @param  jbd_fs jbd filesystem
769  * @param  recover_info  journal replay info
770  * @param  action action needed to be taken
771  * @return standard error code*/
772 static int jbd_iterate_log(struct jbd_fs *jbd_fs,
773                            struct recover_info *info,
774                            int action)
775 {
776         int r = EOK;
777         bool log_end = false;
778         struct jbd_sb *sb = &jbd_fs->sb;
779         uint32_t start_trans_id, this_trans_id;
780         uint32_t start_block, this_block;
781
782         /* We start iterating valid blocks in the whole journal.*/
783         start_trans_id = this_trans_id = jbd_get32(sb, sequence);
784         start_block = this_block = jbd_get32(sb, start);
785
786         ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
787                             start_trans_id);
788
789         while (!log_end) {
790                 struct ext4_block block;
791                 struct jbd_bhdr *header;
792                 /* If we are not scanning for the last
793                  * valid transaction in the journal,
794                  * we will stop when we reach the end of
795                  * the journal.*/
796                 if (action != ACTION_SCAN)
797                         if (this_trans_id > info->last_trans_id) {
798                                 log_end = true;
799                                 continue;
800                         }
801
802                 r = jbd_block_get(jbd_fs, &block, this_block);
803                 if (r != EOK)
804                         break;
805
806                 header = (struct jbd_bhdr *)block.data;
807                 /* This block does not have a valid magic number,
808                  * so we have reached the end of the journal.*/
809                 if (jbd_get32(header, magic) != JBD_MAGIC_NUMBER) {
810                         jbd_block_set(jbd_fs, &block);
811                         log_end = true;
812                         continue;
813                 }
814
815                 /* If the transaction id we found is not expected,
816                  * we may have reached the end of the journal.
817                  *
818                  * If we are not scanning the journal, something
819                  * bad might have taken place. :-( */
820                 if (jbd_get32(header, sequence) != this_trans_id) {
821                         if (action != ACTION_SCAN)
822                                 r = EIO;
823
824                         jbd_block_set(jbd_fs, &block);
825                         log_end = true;
826                         continue;
827                 }
828
829                 switch (jbd_get32(header, blocktype)) {
830                 case JBD_DESCRIPTOR_BLOCK:
831                         ext4_dbg(DEBUG_JBD, "Descriptor block: %" PRIu32", "
832                                             "trans_id: %" PRIu32"\n",
833                                             this_block, this_trans_id);
834                         if (action == ACTION_RECOVER) {
835                                 struct replay_arg replay_arg;
836                                 replay_arg.info = info;
837                                 replay_arg.this_block = &this_block;
838                                 replay_arg.this_trans_id = this_trans_id;
839
840                                 jbd_replay_descriptor_block(jbd_fs,
841                                                 header, &replay_arg);
842                         } else
843                                 jbd_debug_descriptor_block(jbd_fs,
844                                                 header, &this_block);
845
846                         break;
847                 case JBD_COMMIT_BLOCK:
848                         ext4_dbg(DEBUG_JBD, "Commit block: %" PRIu32", "
849                                             "trans_id: %" PRIu32"\n",
850                                             this_block, this_trans_id);
851                         /* This is the end of a transaction,
852                          * we may now proceed to the next transaction.
853                          */
854                         this_trans_id++;
855                         break;
856                 case JBD_REVOKE_BLOCK:
857                         ext4_dbg(DEBUG_JBD, "Revoke block: %" PRIu32", "
858                                             "trans_id: %" PRIu32"\n",
859                                             this_block, this_trans_id);
860                         if (action == ACTION_REVOKE) {
861                                 info->this_trans_id = this_trans_id;
862                                 jbd_build_revoke_tree(jbd_fs,
863                                                 header, info);
864                         }
865                         break;
866                 default:
867                         log_end = true;
868                         break;
869                 }
870                 jbd_block_set(jbd_fs, &block);
871                 this_block++;
872                 wrap(sb, this_block);
873                 if (this_block == start_block)
874                         log_end = true;
875
876         }
877         ext4_dbg(DEBUG_JBD, "End of journal.\n");
878         if (r == EOK && action == ACTION_SCAN) {
879                 /* We have finished scanning the journal. */
880                 info->start_trans_id = start_trans_id;
881                 if (this_trans_id > start_trans_id)
882                         info->last_trans_id = this_trans_id - 1;
883                 else
884                         info->last_trans_id = this_trans_id;
885         }
886
887         return r;
888 }
889
890 /**@brief  Replay journal.
891  * @param  jbd_fs jbd filesystem
892  * @return standard error code*/
893 int jbd_recover(struct jbd_fs *jbd_fs)
894 {
895         int r;
896         struct recover_info info;
897         struct jbd_sb *sb = &jbd_fs->sb;
898         if (!sb->start)
899                 return EOK;
900
901         RB_INIT(&info.revoke_root);
902
903         r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
904         if (r != EOK)
905                 return r;
906
907         r = jbd_iterate_log(jbd_fs, &info, ACTION_REVOKE);
908         if (r != EOK)
909                 return r;
910
911         r = jbd_iterate_log(jbd_fs, &info, ACTION_RECOVER);
912         if (r == EOK) {
913                 /* If we successfully replay the journal,
914                  * clear EXT4_FINCOM_RECOVER flag on the
915                  * ext4 superblock, and set the start of
916                  * journal to 0.*/
917                 uint32_t features_incompatible =
918                         ext4_get32(&jbd_fs->inode_ref.fs->sb,
919                                    features_incompatible);
920                 jbd_set32(&jbd_fs->sb, start, 0);
921                 features_incompatible &= ~EXT4_FINCOM_RECOVER;
922                 ext4_set32(&jbd_fs->inode_ref.fs->sb,
923                            features_incompatible,
924                            features_incompatible);
925                 jbd_fs->dirty = true;
926                 r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
927                                   &jbd_fs->inode_ref.fs->sb);
928         }
929         jbd_destroy_revoke_tree(&info);
930         return r;
931 }
932
933 static void jbd_journal_write_sb(struct jbd_journal *journal)
934 {
935         struct jbd_fs *jbd_fs = journal->jbd_fs;
936         jbd_set32(&jbd_fs->sb, start, journal->start);
937         jbd_set32(&jbd_fs->sb, sequence, journal->trans_id);
938         jbd_fs->dirty = true;
939 }
940
941 /**@brief  Start accessing the journal.
942  * @param  jbd_fs jbd filesystem
943  * @param  journal current journal session
944  * @return standard error code*/
945 int jbd_journal_start(struct jbd_fs *jbd_fs,
946                       struct jbd_journal *journal)
947 {
948         int r;
949         uint32_t features_incompatible =
950                         ext4_get32(&jbd_fs->inode_ref.fs->sb,
951                                    features_incompatible);
952         features_incompatible |= EXT4_FINCOM_RECOVER;
953         ext4_set32(&jbd_fs->inode_ref.fs->sb,
954                         features_incompatible,
955                         features_incompatible);
956         r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
957                         &jbd_fs->inode_ref.fs->sb);
958         if (r != EOK)
959                 return r;
960
961         journal->first = jbd_get32(&jbd_fs->sb, first);
962         journal->start = journal->first;
963         journal->last = journal->first;
964         journal->trans_id = 1;
965         journal->alloc_trans_id = 1;
966
967         journal->block_size = jbd_get32(&jbd_fs->sb, blocksize);
968
969         TAILQ_INIT(&journal->trans_queue);
970         TAILQ_INIT(&journal->cp_queue);
971         journal->jbd_fs = jbd_fs;
972         jbd_journal_write_sb(journal);
973         return jbd_write_sb(jbd_fs);
974 }
975
976 /**@brief  Stop accessing the journal.
977  * @param  journal current journal session
978  * @return standard error code*/
979 int jbd_journal_stop(struct jbd_journal *journal)
980 {
981         int r;
982         struct jbd_fs *jbd_fs = journal->jbd_fs;
983         uint32_t features_incompatible;
984
985         /* Commit all the transactions to the journal.*/
986         jbd_journal_commit_all(journal);
987         /* Make sure that journalled content have reached
988          * the disk.*/
989         ext4_block_cache_flush(jbd_fs->inode_ref.fs->bdev);
990
991         features_incompatible =
992                 ext4_get32(&jbd_fs->inode_ref.fs->sb,
993                            features_incompatible);
994         features_incompatible &= ~EXT4_FINCOM_RECOVER;
995         ext4_set32(&jbd_fs->inode_ref.fs->sb,
996                         features_incompatible,
997                         features_incompatible);
998         r = ext4_sb_write(jbd_fs->inode_ref.fs->bdev,
999                         &jbd_fs->inode_ref.fs->sb);
1000         if (r != EOK)
1001                 return r;
1002
1003         journal->start = 0;
1004         journal->trans_id = 0;
1005         jbd_journal_write_sb(journal);
1006         return jbd_write_sb(journal->jbd_fs);
1007 }
1008
1009 /**@brief  Allocate a block in the journal.
1010  * @param  journal current journal session
1011  * @param  trans transaction
1012  * @return allocated block address*/
1013 static uint32_t jbd_journal_alloc_block(struct jbd_journal *journal,
1014                                         struct jbd_trans *trans)
1015 {
1016         uint32_t start_block;
1017
1018         start_block = journal->last++;
1019         trans->alloc_blocks++;
1020         wrap(&journal->jbd_fs->sb, journal->last);
1021         
1022         /* If there is no space left, flush all journalled
1023          * blocks to disk first.*/
1024         if (journal->last == journal->start)
1025                 ext4_block_cache_flush(journal->jbd_fs->inode_ref.fs->bdev);
1026
1027         return start_block;
1028 }
1029
1030 /**@brief  Allocate a new transaction
1031  * @param  journal current journal session
1032  * @return transaction allocated*/
1033 struct jbd_trans *
1034 jbd_journal_new_trans(struct jbd_journal *journal)
1035 {
1036         struct jbd_trans *trans = calloc(1, sizeof(struct jbd_trans));
1037         if (!trans)
1038                 return NULL;
1039
1040         /* We will assign a trans_id to this transaction,
1041          * once it has been committed.*/
1042         trans->journal = journal;
1043         trans->error = EOK;
1044         return trans;
1045 }
1046
1047 static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
1048                           struct ext4_buf *buf __unused,
1049                           int res,
1050                           void *arg);
1051
1052 /**@brief  Add block to a transaction
1053  * @param  trans transaction
1054  * @param  block block descriptor
1055  * @return standard error code*/
1056 int jbd_trans_add_block(struct jbd_trans *trans,
1057                         struct ext4_block *block)
1058 {
1059         struct jbd_buf *buf;
1060         /* We do not need to add those unmodified buffer to
1061          * a transaction. */
1062         if (!ext4_bcache_test_flag(block->buf, BC_DIRTY))
1063                 return EOK;
1064
1065         buf = calloc(1, sizeof(struct jbd_buf));
1066         if (!buf)
1067                 return ENOMEM;
1068
1069         buf->trans = trans;
1070         buf->block = *block;
1071         ext4_bcache_inc_ref(block->buf);
1072
1073         /* If the content reach the disk, notify us
1074          * so that we may do a checkpoint. */
1075         block->buf->end_write = jbd_trans_end_write;
1076         block->buf->end_write_arg = trans;
1077
1078         trans->data_cnt++;
1079         LIST_INSERT_HEAD(&trans->buf_list, buf, buf_node);
1080         return EOK;
1081 }
1082
1083 /**@brief  Add block to be revoked to a transaction
1084  * @param  trans transaction
1085  * @param  lba logical block address
1086  * @return standard error code*/
1087 int jbd_trans_revoke_block(struct jbd_trans *trans,
1088                            ext4_fsblk_t lba)
1089 {
1090         struct jbd_revoke_rec *rec =
1091                 calloc(1, sizeof(struct jbd_revoke_rec));
1092         if (!rec)
1093                 return ENOMEM;
1094
1095         rec->lba = lba;
1096         LIST_INSERT_HEAD(&trans->revoke_list, rec, revoke_node);
1097         return EOK;
1098 }
1099
1100 /**@brief  Free a transaction
1101  * @param  journal current journal session
1102  * @param  trans transaction
1103  * @param  abort discard all the modifications on the block?
1104  * @return standard error code*/
1105 void jbd_journal_free_trans(struct jbd_journal *journal,
1106                             struct jbd_trans *trans,
1107                             bool abort)
1108 {
1109         struct jbd_buf *jbd_buf, *tmp;
1110         struct jbd_revoke_rec *rec, *tmp2;
1111         struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
1112         LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
1113                           tmp) {
1114                 if (abort) {
1115                         ext4_bcache_clear_dirty(jbd_buf->block.buf);
1116                         ext4_block_set(fs->bdev, &jbd_buf->block);
1117                 }
1118
1119                 LIST_REMOVE(jbd_buf, buf_node);
1120                 free(jbd_buf);
1121         }
1122         LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
1123                           tmp2) {
1124                 LIST_REMOVE(rec, revoke_node);
1125                 free(rec);
1126         }
1127
1128         free(trans);
1129 }
1130
1131 /**@brief  Write commit block for a transaction
1132  * @param  trans transaction
1133  * @return standard error code*/
1134 static int jbd_trans_write_commit_block(struct jbd_trans *trans)
1135 {
1136         int rc;
1137         struct jbd_commit_header *header;
1138         uint32_t commit_iblock = 0;
1139         struct ext4_block commit_block;
1140         struct jbd_journal *journal = trans->journal;
1141
1142         commit_iblock = jbd_journal_alloc_block(journal, trans);
1143         rc = jbd_block_get_noread(journal->jbd_fs,
1144                         &commit_block, commit_iblock);
1145         if (rc != EOK)
1146                 return rc;
1147
1148         header = (struct jbd_commit_header *)commit_block.data;
1149         jbd_set32(&header->header, magic, JBD_MAGIC_NUMBER);
1150         jbd_set32(&header->header, blocktype, JBD_COMMIT_BLOCK);
1151         jbd_set32(&header->header, sequence, trans->trans_id);
1152
1153         ext4_bcache_set_dirty(commit_block.buf);
1154         rc = jbd_block_set(journal->jbd_fs, &commit_block);
1155         if (rc != EOK)
1156                 return rc;
1157
1158         return EOK;
1159 }
1160
1161 /**@brief  Write descriptor block for a transaction
1162  * @param  journal current journal session
1163  * @param  trans transaction
1164  * @return standard error code*/
1165 static int jbd_journal_prepare(struct jbd_journal *journal,
1166                                struct jbd_trans *trans)
1167 {
1168         int rc = EOK, i = 0;
1169         int32_t tag_tbl_size;
1170         uint32_t desc_iblock = 0;
1171         uint32_t data_iblock = 0;
1172         char *tag_start = NULL, *tag_ptr = NULL;
1173         struct jbd_buf *jbd_buf;
1174         struct ext4_block desc_block, data_block;
1175
1176         LIST_FOREACH(jbd_buf, &trans->buf_list, buf_node) {
1177                 struct tag_info tag_info;
1178                 bool uuid_exist = false;
1179 again:
1180                 if (!desc_iblock) {
1181                         struct jbd_bhdr *bhdr;
1182                         desc_iblock = jbd_journal_alloc_block(journal, trans);
1183                         rc = jbd_block_get_noread(journal->jbd_fs,
1184                                            &desc_block, desc_iblock);
1185                         if (rc != EOK)
1186                                 break;
1187
1188                         ext4_bcache_set_dirty(desc_block.buf);
1189
1190                         bhdr = (struct jbd_bhdr *)desc_block.data;
1191                         jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER);
1192                         jbd_set32(bhdr, blocktype, JBD_DESCRIPTOR_BLOCK);
1193                         jbd_set32(bhdr, sequence, trans->trans_id);
1194
1195                         tag_start = (char *)(bhdr + 1);
1196                         tag_ptr = tag_start;
1197                         uuid_exist = true;
1198                         tag_tbl_size = journal->block_size -
1199                                 sizeof(struct jbd_bhdr);
1200
1201                         if (!trans->start_iblock)
1202                                 trans->start_iblock = desc_iblock;
1203
1204                 }
1205                 tag_info.block = jbd_buf->block.lb_id;
1206                 tag_info.uuid_exist = uuid_exist;
1207                 if (i == trans->data_cnt - 1)
1208                         tag_info.last_tag = true;
1209
1210                 if (uuid_exist)
1211                         memcpy(tag_info.uuid, journal->jbd_fs->sb.uuid,
1212                                         UUID_SIZE);
1213
1214                 rc = jbd_write_block_tag(journal->jbd_fs,
1215                                 tag_ptr,
1216                                 tag_tbl_size,
1217                                 &tag_info);
1218                 if (rc != EOK) {
1219                         jbd_block_set(journal->jbd_fs, &desc_block);
1220                         desc_iblock = 0;
1221                         goto again;
1222                 }
1223
1224                 data_iblock = jbd_journal_alloc_block(journal, trans);
1225                 rc = jbd_block_get_noread(journal->jbd_fs,
1226                                 &data_block, data_iblock);
1227                 if (rc != EOK)
1228                         break;
1229
1230                 ext4_bcache_set_dirty(data_block.buf);
1231
1232                 memcpy(data_block.data, jbd_buf->block.data,
1233                         journal->block_size);
1234
1235                 rc = jbd_block_set(journal->jbd_fs, &data_block);
1236                 if (rc != EOK)
1237                         break;
1238
1239                 tag_ptr += tag_info.tag_bytes;
1240                 tag_tbl_size -= tag_info.tag_bytes;
1241
1242                 i++;
1243         }
1244         if (rc == EOK && desc_iblock)
1245                 jbd_block_set(journal->jbd_fs, &desc_block);
1246
1247         return rc;
1248 }
1249
1250 /**@brief  Write revoke block for a transaction
1251  * @param  journal current journal session
1252  * @param  trans transaction
1253  * @return standard error code*/
1254 static int
1255 jbd_journal_prepare_revoke(struct jbd_journal *journal,
1256                            struct jbd_trans *trans)
1257 {
1258         int rc = EOK, i = 0;
1259         int32_t tag_tbl_size;
1260         uint32_t desc_iblock = 0;
1261         char *blocks_entry = NULL;
1262         struct jbd_revoke_rec *rec, *tmp;
1263         struct ext4_block desc_block;
1264         struct jbd_revoke_header *header = NULL;
1265         int32_t record_len = 4;
1266
1267         if (JBD_HAS_INCOMPAT_FEATURE(&journal->jbd_fs->sb,
1268                                      JBD_FEATURE_INCOMPAT_64BIT))
1269                 record_len = 8;
1270
1271         LIST_FOREACH_SAFE(rec, &trans->revoke_list, revoke_node,
1272                           tmp) {
1273 again:
1274                 if (!desc_iblock) {
1275                         struct jbd_bhdr *bhdr;
1276                         desc_iblock = jbd_journal_alloc_block(journal, trans);
1277                         rc = jbd_block_get_noread(journal->jbd_fs,
1278                                            &desc_block, desc_iblock);
1279                         if (rc != EOK) {
1280                                 break;
1281                         }
1282
1283                         ext4_bcache_set_dirty(desc_block.buf);
1284
1285                         bhdr = (struct jbd_bhdr *)desc_block.data;
1286                         jbd_set32(bhdr, magic, JBD_MAGIC_NUMBER);
1287                         jbd_set32(bhdr, blocktype, JBD_REVOKE_BLOCK);
1288                         jbd_set32(bhdr, sequence, trans->trans_id);
1289                         
1290                         header = (struct jbd_revoke_header *)bhdr;
1291                         blocks_entry = (char *)(header + 1);
1292                         tag_tbl_size = journal->block_size -
1293                                 sizeof(struct jbd_revoke_header);
1294
1295                         if (!trans->start_iblock)
1296                                 trans->start_iblock = desc_iblock;
1297
1298                 }
1299
1300                 if (tag_tbl_size < record_len) {
1301                         jbd_set32(header, count,
1302                                   journal->block_size - tag_tbl_size);
1303                         jbd_block_set(journal->jbd_fs, &desc_block);
1304                         desc_iblock = 0;
1305                         header = NULL;
1306                         goto again;
1307                 }
1308                 if (record_len == 8) {
1309                         uint64_t *blocks =
1310                                 (uint64_t *)blocks_entry;
1311                         *blocks = to_be64(rec->lba);
1312                 } else {
1313                         uint32_t *blocks =
1314                                 (uint32_t *)blocks_entry;
1315                         *blocks = to_be32(rec->lba);
1316                 }
1317                 blocks_entry += record_len;
1318                 tag_tbl_size -= record_len;
1319
1320                 i++;
1321         }
1322         if (rc == EOK && desc_iblock) {
1323                 if (header != NULL)
1324                         jbd_set32(header, count,
1325                                   journal->block_size - tag_tbl_size);
1326
1327                 jbd_block_set(journal->jbd_fs, &desc_block);
1328         }
1329
1330         return rc;
1331 }
1332
1333 /**@brief  Submit the transaction to transaction queue.
1334  * @param  journal current journal session
1335  * @param  trans transaction*/
1336 void
1337 jbd_journal_submit_trans(struct jbd_journal *journal,
1338                          struct jbd_trans *trans)
1339 {
1340         TAILQ_INSERT_TAIL(&journal->trans_queue,
1341                           trans,
1342                           trans_node);
1343 }
1344
1345 /**@brief  Put references of block descriptors in a transaction.
1346  * @param  journal current journal session
1347  * @param  trans transaction*/
1348 void jbd_journal_cp_trans(struct jbd_journal *journal, struct jbd_trans *trans)
1349 {
1350         struct jbd_buf *jbd_buf, *tmp;
1351         struct ext4_fs *fs = journal->jbd_fs->inode_ref.fs;
1352         LIST_FOREACH_SAFE(jbd_buf, &trans->buf_list, buf_node,
1353                         tmp) {
1354                 struct ext4_block block = jbd_buf->block;
1355                 ext4_block_set(fs->bdev, &block);
1356         }
1357 }
1358
1359 /**@brief  Update the start block of the journal when
1360  *         all the contents in a transaction reach the disk.*/
1361 static void jbd_trans_end_write(struct ext4_bcache *bc __unused,
1362                           struct ext4_buf *buf __unused,
1363                           int res,
1364                           void *arg)
1365 {
1366         struct jbd_trans *trans = arg;
1367         struct jbd_journal *journal = trans->journal;
1368         bool first_in_queue =
1369                 trans == TAILQ_FIRST(&journal->cp_queue);
1370         if (res != EOK)
1371                 trans->error = res;
1372
1373         trans->written_cnt++;
1374         if (trans->written_cnt == trans->data_cnt) {
1375                 TAILQ_REMOVE(&journal->cp_queue, trans, trans_node);
1376
1377                 if (first_in_queue) {
1378                         journal->start = trans->start_iblock +
1379                                 trans->alloc_blocks;
1380                         wrap(&journal->jbd_fs->sb, journal->start);
1381                         journal->trans_id = trans->trans_id + 1;
1382                 }
1383                 jbd_journal_free_trans(journal, trans, false);
1384
1385                 if (first_in_queue) {
1386                         while ((trans = TAILQ_FIRST(&journal->cp_queue))) {
1387                                 if (!trans->data_cnt) {
1388                                         TAILQ_REMOVE(&journal->cp_queue,
1389                                                      trans,
1390                                                      trans_node);
1391                                         journal->start = trans->start_iblock +
1392                                                 trans->alloc_blocks;
1393                                         wrap(&journal->jbd_fs->sb, journal->start);
1394                                         journal->trans_id = trans->trans_id + 1;
1395                                         jbd_journal_free_trans(journal,
1396                                                                trans, false);
1397                                 } else {
1398                                         journal->start = trans->start_iblock;
1399                                         wrap(&journal->jbd_fs->sb, journal->start);
1400                                         journal->trans_id = trans->trans_id;
1401                                         break;
1402                                 }
1403                         }
1404                         jbd_journal_write_sb(journal);
1405                         jbd_write_sb(journal->jbd_fs);
1406                 }
1407         }
1408 }
1409
1410 /**@brief  Commit a transaction to the journal immediately.
1411  * @param  journal current journal session
1412  * @param  trans transaction
1413  * @return standard error code*/
1414 int jbd_journal_commit_trans(struct jbd_journal *journal,
1415                              struct jbd_trans *trans)
1416 {
1417         int rc = EOK;
1418         uint32_t last = journal->last;
1419
1420         trans->trans_id = journal->alloc_trans_id;
1421         rc = jbd_journal_prepare(journal, trans);
1422         if (rc != EOK)
1423                 goto Finish;
1424
1425         rc = jbd_journal_prepare_revoke(journal, trans);
1426         if (rc != EOK)
1427                 goto Finish;
1428
1429         rc = jbd_trans_write_commit_block(trans);
1430         if (rc != EOK)
1431                 goto Finish;
1432
1433         journal->alloc_trans_id++;
1434         if (TAILQ_EMPTY(&journal->cp_queue)) {
1435                 if (trans->data_cnt) {
1436                         journal->start = trans->start_iblock;
1437                         wrap(&journal->jbd_fs->sb, journal->start);
1438                         journal->trans_id = trans->trans_id;
1439                         jbd_journal_write_sb(journal);
1440                         jbd_write_sb(journal->jbd_fs);
1441                         TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
1442                                         trans_node);
1443                         jbd_journal_cp_trans(journal, trans);
1444                 } else {
1445                         journal->start = trans->start_iblock +
1446                                 trans->alloc_blocks;
1447                         wrap(&journal->jbd_fs->sb, journal->start);
1448                         journal->trans_id = trans->trans_id + 1;
1449                         jbd_journal_write_sb(journal);
1450                         jbd_journal_free_trans(journal, trans, false);
1451                 }
1452         } else {
1453                 TAILQ_INSERT_TAIL(&journal->cp_queue, trans,
1454                                 trans_node);
1455                 if (trans->data_cnt)
1456                         jbd_journal_cp_trans(journal, trans);
1457
1458         }
1459 Finish:
1460         if (rc != EOK) {
1461                 journal->last = last;
1462                 jbd_journal_free_trans(journal, trans, true);
1463         }
1464         return rc;
1465 }
1466
1467 /**@brief  Commit one transaction on transaction queue
1468  *         to the journal.
1469  * @param  journal current journal session.*/
1470 void jbd_journal_commit_one(struct jbd_journal *journal)
1471 {
1472         struct jbd_trans *trans;
1473
1474         if ((trans = TAILQ_FIRST(&journal->trans_queue))) {
1475                 TAILQ_REMOVE(&journal->trans_queue, trans, trans_node);
1476                 jbd_journal_commit_trans(journal, trans);
1477         }
1478 }
1479
1480 /**@brief  Commit all the transactions on transaction queue
1481  *         to the journal.
1482  * @param  journal current journal session.*/
1483 void jbd_journal_commit_all(struct jbd_journal *journal)
1484 {
1485         while (!TAILQ_EMPTY(&journal->trans_queue)) {
1486                 jbd_journal_commit_one(journal);
1487         }
1488 }
1489
1490 /**
1491  * @}
1492  */