2 * (C) Copyright 2011 - 2012 Samsung Electronics
3 * EXT4 filesystem implementation in Uboot by
4 * Uma Shankar <uma.shankar@samsung.com>
5 * Manjunatha C Achar <a.manjunatha@samsung.com>
7 * Journal data structures and headers for Journaling feature of ext4
8 * have been referred from JBD2 (Journaling Block device 2)
9 * implementation in Linux Kernel.
10 * Written by Stephen C. Tweedie <sct@redhat.com>
12 * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
13 * This file is part of the Linux kernel and is made available under
14 * the terms of the GNU General Public License, version 2, or at your
15 * option, any later version, incorporated herein by reference.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 #include <ext_common.h>
36 #include "ext4_common.h"
38 static struct revoke_blk_list
*revk_blk_list
;
39 static struct revoke_blk_list
*prev_node
;
40 static int first_node
= true;
45 struct journal_log
*journal_ptr
[MAX_JOURNAL_ENTRIES
];
46 struct dirty_blocks
*dirty_block_ptr
[MAX_JOURNAL_ENTRIES
];
48 int ext4fs_init_journal(void)
52 struct ext_filesystem
*fs
= get_fs();
61 for (i
= 0; i
< MAX_JOURNAL_ENTRIES
; i
++) {
62 journal_ptr
[i
] = zalloc(sizeof(struct journal_log
));
65 dirty_block_ptr
[i
] = zalloc(sizeof(struct dirty_blocks
));
66 if (!dirty_block_ptr
[i
])
68 journal_ptr
[i
]->buf
= NULL
;
69 journal_ptr
[i
]->blknr
= -1;
71 dirty_block_ptr
[i
]->buf
= NULL
;
72 dirty_block_ptr
[i
]->blknr
= -1;
75 if (fs
->blksz
== 4096) {
76 temp
= zalloc(fs
->blksz
);
79 journal_ptr
[gindex
]->buf
= zalloc(fs
->blksz
);
80 if (!journal_ptr
[gindex
]->buf
)
82 ext4fs_devread(0, 0, fs
->blksz
, temp
);
83 memcpy(temp
+ SUPERBLOCK_SIZE
, fs
->sb
, SUPERBLOCK_SIZE
);
84 memcpy(journal_ptr
[gindex
]->buf
, temp
, fs
->blksz
);
85 journal_ptr
[gindex
++]->blknr
= 0;
88 journal_ptr
[gindex
]->buf
= zalloc(fs
->blksz
);
89 if (!journal_ptr
[gindex
]->buf
)
91 memcpy(journal_ptr
[gindex
]->buf
, fs
->sb
, SUPERBLOCK_SIZE
);
92 journal_ptr
[gindex
++]->blknr
= 1;
95 /* Check the file system state using journal super block */
96 if (ext4fs_check_journal_state(SCAN
))
98 /* Check the file system state using journal super block */
99 if (ext4fs_check_journal_state(RECOVER
))
107 void ext4fs_dump_metadata(void)
109 struct ext_filesystem
*fs
= get_fs();
111 for (i
= 0; i
< MAX_JOURNAL_ENTRIES
; i
++) {
112 if (dirty_block_ptr
[i
]->blknr
== -1)
114 put_ext4((uint64_t) ((uint64_t)dirty_block_ptr
[i
]->blknr
*
115 (uint64_t)fs
->blksz
), dirty_block_ptr
[i
]->buf
,
120 void ext4fs_free_journal(void)
123 for (i
= 0; i
< MAX_JOURNAL_ENTRIES
; i
++) {
124 if (dirty_block_ptr
[i
]->blknr
== -1)
126 if (dirty_block_ptr
[i
]->buf
)
127 free(dirty_block_ptr
[i
]->buf
);
130 for (i
= 0; i
< MAX_JOURNAL_ENTRIES
; i
++) {
131 if (journal_ptr
[i
]->blknr
== -1)
133 if (journal_ptr
[i
]->buf
)
134 free(journal_ptr
[i
]->buf
);
137 for (i
= 0; i
< MAX_JOURNAL_ENTRIES
; i
++) {
139 free(journal_ptr
[i
]);
140 if (dirty_block_ptr
[i
])
141 free(dirty_block_ptr
[i
]);
148 int ext4fs_log_gdt(char *gd_table
)
150 struct ext_filesystem
*fs
= get_fs();
152 long int var
= fs
->gdtable_blkno
;
153 for (i
= 0; i
< fs
->no_blk_pergdt
; i
++) {
154 journal_ptr
[gindex
]->buf
= zalloc(fs
->blksz
);
155 if (!journal_ptr
[gindex
]->buf
)
157 memcpy(journal_ptr
[gindex
]->buf
, gd_table
, fs
->blksz
);
158 gd_table
+= fs
->blksz
;
159 journal_ptr
[gindex
++]->blknr
= var
++;
166 * This function stores the backup copy of meta data in RAM
167 * journal_buffer -- Buffer containing meta data
168 * blknr -- Block number on disk of the meta data buffer
170 int ext4fs_log_journal(char *journal_buffer
, long int blknr
)
172 struct ext_filesystem
*fs
= get_fs();
175 if (!journal_buffer
) {
176 printf("Invalid input arguments %s\n", __func__
);
180 for (i
= 0; i
< MAX_JOURNAL_ENTRIES
; i
++) {
181 if (journal_ptr
[i
]->blknr
== -1)
183 if (journal_ptr
[i
]->blknr
== blknr
)
187 journal_ptr
[gindex
]->buf
= zalloc(fs
->blksz
);
188 if (!journal_ptr
[gindex
]->buf
)
191 memcpy(journal_ptr
[gindex
]->buf
, journal_buffer
, fs
->blksz
);
192 journal_ptr
[gindex
++]->blknr
= blknr
;
198 * This function stores the modified meta data in RAM
199 * metadata_buffer -- Buffer containing meta data
200 * blknr -- Block number on disk of the meta data buffer
202 int ext4fs_put_metadata(char *metadata_buffer
, long int blknr
)
204 struct ext_filesystem
*fs
= get_fs();
205 if (!metadata_buffer
) {
206 printf("Invalid input arguments %s\n", __func__
);
209 dirty_block_ptr
[gd_index
]->buf
= zalloc(fs
->blksz
);
210 if (!dirty_block_ptr
[gd_index
]->buf
)
212 memcpy(dirty_block_ptr
[gd_index
]->buf
, metadata_buffer
, fs
->blksz
);
213 dirty_block_ptr
[gd_index
++]->blknr
= blknr
;
218 void print_revoke_blks(char *revk_blk
)
223 struct journal_revoke_header_t
*header
;
225 if (revk_blk
== NULL
)
228 header
= (struct journal_revoke_header_t
*) revk_blk
;
229 offset
= sizeof(struct journal_revoke_header_t
);
230 max
= be32_to_cpu(header
->r_count
);
231 printf("total bytes %d\n", max
);
233 while (offset
< max
) {
234 blocknr
= be32_to_cpu(*((long int *)(revk_blk
+ offset
)));
235 printf("revoke blknr is %ld\n", blocknr
);
240 static struct revoke_blk_list
*_get_node(void)
242 struct revoke_blk_list
*tmp_node
;
243 tmp_node
= zalloc(sizeof(struct revoke_blk_list
));
244 if (tmp_node
== NULL
)
246 tmp_node
->content
= NULL
;
247 tmp_node
->next
= NULL
;
252 void ext4fs_push_revoke_blk(char *buffer
)
254 struct revoke_blk_list
*node
= NULL
;
255 struct ext_filesystem
*fs
= get_fs();
256 if (buffer
== NULL
) {
257 printf("buffer ptr is NULL\n");
262 printf("_get_node: malloc failed\n");
266 node
->content
= zalloc(fs
->blksz
);
267 if (node
->content
== NULL
)
269 memcpy(node
->content
, buffer
, fs
->blksz
);
271 if (first_node
== true) {
272 revk_blk_list
= node
;
276 prev_node
->next
= node
;
281 void ext4fs_free_revoke_blks(void)
283 struct revoke_blk_list
*tmp_node
= revk_blk_list
;
284 struct revoke_blk_list
*next_node
= NULL
;
286 while (tmp_node
!= NULL
) {
287 if (tmp_node
->content
)
288 free(tmp_node
->content
);
289 tmp_node
= tmp_node
->next
;
292 tmp_node
= revk_blk_list
;
293 while (tmp_node
!= NULL
) {
294 next_node
= tmp_node
->next
;
296 tmp_node
= next_node
;
299 revk_blk_list
= NULL
;
304 int check_blknr_for_revoke(long int blknr
, int sequence_no
)
306 struct journal_revoke_header_t
*header
;
311 struct revoke_blk_list
*tmp_revk_node
= revk_blk_list
;
312 while (tmp_revk_node
!= NULL
) {
313 revk_blk
= tmp_revk_node
->content
;
315 header
= (struct journal_revoke_header_t
*) revk_blk
;
316 if (sequence_no
< be32_to_cpu(header
->r_header
.h_sequence
)) {
317 offset
= sizeof(struct journal_revoke_header_t
);
318 max
= be32_to_cpu(header
->r_count
);
320 while (offset
< max
) {
321 blocknr
= be32_to_cpu(*((long int *)
322 (revk_blk
+ offset
)));
323 if (blocknr
== blknr
)
328 tmp_revk_node
= tmp_revk_node
->next
;
338 * This function parses the journal blocks and replays the
339 * suceessful transactions. A transaction is successfull
340 * if commit block is found for a descriptor block
341 * The tags in descriptor block contain the disk block
342 * numbers of the metadata to be replayed
344 void recover_transaction(int prev_desc_logical_no
)
346 struct ext2_inode inode_journal
;
347 struct ext_filesystem
*fs
= get_fs();
348 struct journal_header_t
*jdb
;
353 struct ext3_journal_block_tag
*tag
;
354 char *temp_buff
= zalloc(fs
->blksz
);
355 char *metadata_buff
= zalloc(fs
->blksz
);
356 if (!temp_buff
|| !metadata_buff
)
358 i
= prev_desc_logical_no
;
359 ext4fs_read_inode(ext4fs_root
, EXT2_JOURNAL_INO
,
360 (struct ext2_inode
*)&inode_journal
);
361 blknr
= read_allocated_block((struct ext2_inode
*)
363 ext4fs_devread(blknr
* fs
->sect_perblk
, 0, fs
->blksz
, temp_buff
);
364 p_jdb
= (char *)temp_buff
;
365 jdb
= (struct journal_header_t
*) temp_buff
;
366 ofs
= sizeof(struct journal_header_t
);
369 tag
= (struct ext3_journal_block_tag
*)&p_jdb
[ofs
];
370 ofs
+= sizeof(struct ext3_journal_block_tag
);
375 flags
= be32_to_cpu(tag
->flags
);
376 if (!(flags
& EXT3_JOURNAL_FLAG_SAME_UUID
))
380 debug("\t\ttag %u\n", be32_to_cpu(tag
->block
));
381 if (revk_blk_list
!= NULL
) {
382 if (check_blknr_for_revoke(be32_to_cpu(tag
->block
),
383 be32_to_cpu(jdb
->h_sequence
)) == 0)
386 blknr
= read_allocated_block(&inode_journal
, i
);
387 ext4fs_devread(blknr
* fs
->sect_perblk
, 0,
388 fs
->blksz
, metadata_buff
);
389 put_ext4((uint64_t)(be32_to_cpu(tag
->block
) * fs
->blksz
),
390 metadata_buff
, (uint32_t) fs
->blksz
);
391 } while (!(flags
& EXT3_JOURNAL_FLAG_LAST_TAG
));
397 void print_jrnl_status(int recovery_flag
)
399 if (recovery_flag
== RECOVER
)
400 printf("Journal Recovery Completed\n");
402 printf("Journal Scan Completed\n");
405 int ext4fs_check_journal_state(int recovery_flag
)
410 int transaction_state
= TRANSACTION_COMPLETE
;
411 int prev_desc_logical_no
= 0;
412 int curr_desc_logical_no
= 0;
414 struct ext2_inode inode_journal
;
415 struct journal_superblock_t
*jsb
= NULL
;
416 struct journal_header_t
*jdb
= NULL
;
418 struct ext3_journal_block_tag
*tag
= NULL
;
419 char *temp_buff
= NULL
;
420 char *temp_buff1
= NULL
;
421 struct ext_filesystem
*fs
= get_fs();
423 temp_buff
= zalloc(fs
->blksz
);
426 temp_buff1
= zalloc(fs
->blksz
);
432 ext4fs_read_inode(ext4fs_root
, EXT2_JOURNAL_INO
, &inode_journal
);
433 blknr
= read_allocated_block(&inode_journal
, EXT2_JOURNAL_SUPERBLOCK
);
434 ext4fs_devread(blknr
* fs
->sect_perblk
, 0, fs
->blksz
, temp_buff
);
435 jsb
= (struct journal_superblock_t
*) temp_buff
;
437 if (fs
->sb
->feature_incompat
& EXT3_FEATURE_INCOMPAT_RECOVER
) {
438 if (recovery_flag
== RECOVER
)
439 printf("Recovery required\n");
441 if (recovery_flag
== RECOVER
)
442 printf("File System is consistent\n");
446 if (be32_to_cpu(jsb
->s_start
) == 0)
449 if (!(jsb
->s_feature_compat
&
450 cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM
)))
451 jsb
->s_feature_compat
|=
452 cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM
);
454 i
= be32_to_cpu(jsb
->s_first
);
456 blknr
= read_allocated_block(&inode_journal
, i
);
457 memset(temp_buff1
, '\0', fs
->blksz
);
458 ext4fs_devread(blknr
* fs
->sect_perblk
,
459 0, fs
->blksz
, temp_buff1
);
460 jdb
= (struct journal_header_t
*) temp_buff1
;
462 if (be32_to_cpu(jdb
->h_blocktype
) ==
463 EXT3_JOURNAL_DESCRIPTOR_BLOCK
) {
464 if (be32_to_cpu(jdb
->h_sequence
) !=
465 be32_to_cpu(jsb
->s_sequence
)) {
466 print_jrnl_status(recovery_flag
);
470 curr_desc_logical_no
= i
;
471 if (transaction_state
== TRANSACTION_COMPLETE
)
472 transaction_state
= TRANSACTION_RUNNING
;
475 p_jdb
= (char *)temp_buff1
;
476 ofs
= sizeof(struct journal_header_t
);
478 tag
= (struct ext3_journal_block_tag
*)
480 ofs
+= sizeof(struct ext3_journal_block_tag
);
483 flags
= be32_to_cpu(tag
->flags
);
484 if (!(flags
& EXT3_JOURNAL_FLAG_SAME_UUID
))
487 debug("\t\ttag %u\n", be32_to_cpu(tag
->block
));
488 } while (!(flags
& EXT3_JOURNAL_FLAG_LAST_TAG
));
491 } else if (be32_to_cpu(jdb
->h_blocktype
) ==
492 EXT3_JOURNAL_COMMIT_BLOCK
) {
493 if (be32_to_cpu(jdb
->h_sequence
) !=
494 be32_to_cpu(jsb
->s_sequence
)) {
495 print_jrnl_status(recovery_flag
);
499 if (transaction_state
== TRANSACTION_RUNNING
||
501 transaction_state
= TRANSACTION_COMPLETE
;
504 cpu_to_be32(be32_to_cpu(
505 jsb
->s_sequence
) + 1);
507 prev_desc_logical_no
= curr_desc_logical_no
;
508 if ((recovery_flag
== RECOVER
) && (DB_FOUND
== YES
))
509 recover_transaction(prev_desc_logical_no
);
512 } else if (be32_to_cpu(jdb
->h_blocktype
) ==
513 EXT3_JOURNAL_REVOKE_BLOCK
) {
514 if (be32_to_cpu(jdb
->h_sequence
) !=
515 be32_to_cpu(jsb
->s_sequence
)) {
516 print_jrnl_status(recovery_flag
);
519 if (recovery_flag
== SCAN
)
520 ext4fs_push_revoke_blk((char *)jdb
);
523 debug("Else Case\n");
524 if (be32_to_cpu(jdb
->h_sequence
) !=
525 be32_to_cpu(jsb
->s_sequence
)) {
526 print_jrnl_status(recovery_flag
);
533 if (recovery_flag
== RECOVER
) {
534 jsb
->s_start
= cpu_to_be32(1);
535 jsb
->s_sequence
= cpu_to_be32(be32_to_cpu(jsb
->s_sequence
) + 1);
536 /* get the superblock */
537 ext4fs_devread(SUPERBLOCK_SECTOR
, 0, SUPERBLOCK_SIZE
,
539 fs
->sb
->feature_incompat
|= EXT3_FEATURE_INCOMPAT_RECOVER
;
541 /* Update the super block */
542 put_ext4((uint64_t) (SUPERBLOCK_SIZE
),
543 (struct ext2_sblock
*)fs
->sb
,
544 (uint32_t) SUPERBLOCK_SIZE
);
545 ext4fs_devread(SUPERBLOCK_SECTOR
, 0, SUPERBLOCK_SIZE
,
548 blknr
= read_allocated_block(&inode_journal
,
549 EXT2_JOURNAL_SUPERBLOCK
);
550 put_ext4((uint64_t) (blknr
* fs
->blksz
),
551 (struct journal_superblock_t
*)temp_buff
,
552 (uint32_t) fs
->blksz
);
553 ext4fs_free_revoke_blks();
561 static void update_descriptor_block(long int blknr
)
565 struct journal_header_t jdb
;
566 struct ext3_journal_block_tag tag
;
567 struct ext2_inode inode_journal
;
568 struct journal_superblock_t
*jsb
= NULL
;
571 struct ext_filesystem
*fs
= get_fs();
572 char *temp_buff
= zalloc(fs
->blksz
);
576 ext4fs_read_inode(ext4fs_root
, EXT2_JOURNAL_INO
, &inode_journal
);
577 jsb_blknr
= read_allocated_block(&inode_journal
,
578 EXT2_JOURNAL_SUPERBLOCK
);
579 ext4fs_devread(jsb_blknr
* fs
->sect_perblk
, 0, fs
->blksz
, temp_buff
);
580 jsb
= (struct journal_superblock_t
*) temp_buff
;
582 jdb
.h_blocktype
= cpu_to_be32(EXT3_JOURNAL_DESCRIPTOR_BLOCK
);
583 jdb
.h_magic
= cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER
);
584 jdb
.h_sequence
= jsb
->s_sequence
;
585 buf
= zalloc(fs
->blksz
);
591 memcpy(buf
, &jdb
, sizeof(struct journal_header_t
));
592 temp
+= sizeof(struct journal_header_t
);
594 for (i
= 0; i
< MAX_JOURNAL_ENTRIES
; i
++) {
595 if (journal_ptr
[i
]->blknr
== -1)
598 tag
.block
= cpu_to_be32(journal_ptr
[i
]->blknr
);
599 tag
.flags
= cpu_to_be32(EXT3_JOURNAL_FLAG_SAME_UUID
);
600 memcpy(temp
, &tag
, sizeof(struct ext3_journal_block_tag
));
601 temp
= temp
+ sizeof(struct ext3_journal_block_tag
);
604 tag
.block
= cpu_to_be32(journal_ptr
[--i
]->blknr
);
605 tag
.flags
= cpu_to_be32(EXT3_JOURNAL_FLAG_LAST_TAG
);
606 memcpy(temp
- sizeof(struct ext3_journal_block_tag
), &tag
,
607 sizeof(struct ext3_journal_block_tag
));
608 put_ext4((uint64_t) (blknr
* fs
->blksz
), buf
, (uint32_t) fs
->blksz
);
614 static void update_commit_block(long int blknr
)
616 struct journal_header_t jdb
;
617 struct ext_filesystem
*fs
= get_fs();
619 struct ext2_inode inode_journal
;
620 struct journal_superblock_t
*jsb
;
622 char *temp_buff
= zalloc(fs
->blksz
);
626 ext4fs_read_inode(ext4fs_root
, EXT2_JOURNAL_INO
, &inode_journal
);
627 jsb_blknr
= read_allocated_block(&inode_journal
,
628 EXT2_JOURNAL_SUPERBLOCK
);
629 ext4fs_devread(jsb_blknr
* fs
->sect_perblk
, 0, fs
->blksz
, temp_buff
);
630 jsb
= (struct journal_superblock_t
*) temp_buff
;
632 jdb
.h_blocktype
= cpu_to_be32(EXT3_JOURNAL_COMMIT_BLOCK
);
633 jdb
.h_magic
= cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER
);
634 jdb
.h_sequence
= jsb
->s_sequence
;
635 buf
= zalloc(fs
->blksz
);
640 memcpy(buf
, &jdb
, sizeof(struct journal_header_t
));
641 put_ext4((uint64_t) (blknr
* fs
->blksz
), buf
, (uint32_t) fs
->blksz
);
647 void ext4fs_update_journal(void)
649 struct ext2_inode inode_journal
;
650 struct ext_filesystem
*fs
= get_fs();
653 ext4fs_read_inode(ext4fs_root
, EXT2_JOURNAL_INO
, &inode_journal
);
654 blknr
= read_allocated_block(&inode_journal
, jrnl_blk_idx
++);
655 update_descriptor_block(blknr
);
656 for (i
= 0; i
< MAX_JOURNAL_ENTRIES
; i
++) {
657 if (journal_ptr
[i
]->blknr
== -1)
659 blknr
= read_allocated_block(&inode_journal
, jrnl_blk_idx
++);
660 put_ext4((uint64_t) ((uint64_t)blknr
* (uint64_t)fs
->blksz
),
661 journal_ptr
[i
]->buf
, fs
->blksz
);
663 blknr
= read_allocated_block(&inode_journal
, jrnl_blk_idx
++);
664 update_commit_block(blknr
);
665 printf("update journal finished\n");