u-boot/fs/ext4/ext4_journal.c
Tom Rini 83d290c56f SPDX: Convert all of our single license tags to Linux Kernel style
When U-Boot started using SPDX tags we were among the early adopters and
there weren't a lot of other examples to borrow from.  So we picked the
area of the file that usually had a full license text and replaced it
with an appropriate SPDX-License-Identifier: entry.  Since then, the
Linux Kernel has adopted SPDX tags and they place it as the very first
line in a file (except where shebangs are used, then it's second line)
and with slightly different comment styles than us.

In part due to community overlap, in part due to better tag visibility
and in part for other minor reasons, switch over to that style.

This commit changes all instances where we have a single declared
license in the tag as both the before and after are identical in tag
contents.  There's also a few places where I found we did not have a tag
and have introduced one.

Signed-off-by: Tom Rini <trini@konsulko.com>
2018-05-07 09:34:12 -04:00

660 lines
17 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2011 - 2012 Samsung Electronics
* EXT4 filesystem implementation in Uboot by
* Uma Shankar <uma.shankar@samsung.com>
* Manjunatha C Achar <a.manjunatha@samsung.com>
*
* Journal data structures and headers for Journaling feature of ext4
* have been referred from JBD2 (Journaling Block device 2)
* implementation in Linux Kernel.
* Written by Stephen C. Tweedie <sct@redhat.com>
*
* Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
*/
#include <common.h>
#include <ext4fs.h>
#include <malloc.h>
#include <ext_common.h>
#include "ext4_common.h"
static struct revoke_blk_list *revk_blk_list;
static struct revoke_blk_list *prev_node;
static int first_node = true;
int gindex;
int gd_index;
int jrnl_blk_idx;
struct journal_log *journal_ptr[MAX_JOURNAL_ENTRIES];
struct dirty_blocks *dirty_block_ptr[MAX_JOURNAL_ENTRIES];
int ext4fs_init_journal(void)
{
int i;
char *temp = NULL;
struct ext_filesystem *fs = get_fs();
/* init globals */
revk_blk_list = NULL;
prev_node = NULL;
gindex = 0;
gd_index = 0;
jrnl_blk_idx = 1;
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
journal_ptr[i] = zalloc(sizeof(struct journal_log));
if (!journal_ptr[i])
goto fail;
dirty_block_ptr[i] = zalloc(sizeof(struct dirty_blocks));
if (!dirty_block_ptr[i])
goto fail;
journal_ptr[i]->buf = NULL;
journal_ptr[i]->blknr = -1;
dirty_block_ptr[i]->buf = NULL;
dirty_block_ptr[i]->blknr = -1;
}
if (fs->blksz == 4096) {
temp = zalloc(fs->blksz);
if (!temp)
goto fail;
journal_ptr[gindex]->buf = zalloc(fs->blksz);
if (!journal_ptr[gindex]->buf)
goto fail;
ext4fs_devread(0, 0, fs->blksz, temp);
memcpy(temp + SUPERBLOCK_SIZE, fs->sb, SUPERBLOCK_SIZE);
memcpy(journal_ptr[gindex]->buf, temp, fs->blksz);
journal_ptr[gindex++]->blknr = 0;
free(temp);
} else {
journal_ptr[gindex]->buf = zalloc(fs->blksz);
if (!journal_ptr[gindex]->buf)
goto fail;
memcpy(journal_ptr[gindex]->buf, fs->sb, SUPERBLOCK_SIZE);
journal_ptr[gindex++]->blknr = 1;
}
/* Check the file system state using journal super block */
if (ext4fs_check_journal_state(SCAN))
goto fail;
/* Check the file system state using journal super block */
if (ext4fs_check_journal_state(RECOVER))
goto fail;
return 0;
fail:
return -1;
}
void ext4fs_dump_metadata(void)
{
struct ext_filesystem *fs = get_fs();
int i;
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (dirty_block_ptr[i]->blknr == -1)
break;
put_ext4((uint64_t) ((uint64_t)dirty_block_ptr[i]->blknr *
(uint64_t)fs->blksz), dirty_block_ptr[i]->buf,
fs->blksz);
}
}
void ext4fs_free_journal(void)
{
int i;
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (dirty_block_ptr[i]->blknr == -1)
break;
if (dirty_block_ptr[i]->buf)
free(dirty_block_ptr[i]->buf);
}
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (journal_ptr[i]->blknr == -1)
break;
if (journal_ptr[i]->buf)
free(journal_ptr[i]->buf);
}
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (journal_ptr[i])
free(journal_ptr[i]);
if (dirty_block_ptr[i])
free(dirty_block_ptr[i]);
}
gindex = 0;
gd_index = 0;
jrnl_blk_idx = 1;
}
int ext4fs_log_gdt(char *gd_table)
{
struct ext_filesystem *fs = get_fs();
short i;
long int var = fs->gdtable_blkno;
for (i = 0; i < fs->no_blk_pergdt; i++) {
journal_ptr[gindex]->buf = zalloc(fs->blksz);
if (!journal_ptr[gindex]->buf)
return -ENOMEM;
memcpy(journal_ptr[gindex]->buf, gd_table, fs->blksz);
gd_table += fs->blksz;
journal_ptr[gindex++]->blknr = var++;
}
return 0;
}
/*
* This function stores the backup copy of meta data in RAM
* journal_buffer -- Buffer containing meta data
* blknr -- Block number on disk of the meta data buffer
*/
int ext4fs_log_journal(char *journal_buffer, uint32_t blknr)
{
struct ext_filesystem *fs = get_fs();
short i;
if (!journal_buffer) {
printf("Invalid input arguments %s\n", __func__);
return -EINVAL;
}
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (journal_ptr[i]->blknr == -1)
break;
if (journal_ptr[i]->blknr == blknr)
return 0;
}
journal_ptr[gindex]->buf = zalloc(fs->blksz);
if (!journal_ptr[gindex]->buf)
return -ENOMEM;
memcpy(journal_ptr[gindex]->buf, journal_buffer, fs->blksz);
journal_ptr[gindex++]->blknr = blknr;
return 0;
}
/*
* This function stores the modified meta data in RAM
* metadata_buffer -- Buffer containing meta data
* blknr -- Block number on disk of the meta data buffer
*/
int ext4fs_put_metadata(char *metadata_buffer, uint32_t blknr)
{
struct ext_filesystem *fs = get_fs();
if (!metadata_buffer) {
printf("Invalid input arguments %s\n", __func__);
return -EINVAL;
}
if (dirty_block_ptr[gd_index]->buf)
assert(dirty_block_ptr[gd_index]->blknr == blknr);
else
dirty_block_ptr[gd_index]->buf = zalloc(fs->blksz);
if (!dirty_block_ptr[gd_index]->buf)
return -ENOMEM;
memcpy(dirty_block_ptr[gd_index]->buf, metadata_buffer, fs->blksz);
dirty_block_ptr[gd_index++]->blknr = blknr;
return 0;
}
void print_revoke_blks(char *revk_blk)
{
int offset;
int max;
long int blocknr;
struct journal_revoke_header_t *header;
if (revk_blk == NULL)
return;
header = (struct journal_revoke_header_t *) revk_blk;
offset = sizeof(struct journal_revoke_header_t);
max = be32_to_cpu(header->r_count);
printf("total bytes %d\n", max);
while (offset < max) {
blocknr = be32_to_cpu(*((__be32 *)(revk_blk + offset)));
printf("revoke blknr is %ld\n", blocknr);
offset += 4;
}
}
static struct revoke_blk_list *_get_node(void)
{
struct revoke_blk_list *tmp_node;
tmp_node = zalloc(sizeof(struct revoke_blk_list));
if (tmp_node == NULL)
return NULL;
tmp_node->content = NULL;
tmp_node->next = NULL;
return tmp_node;
}
void ext4fs_push_revoke_blk(char *buffer)
{
struct revoke_blk_list *node = NULL;
struct ext_filesystem *fs = get_fs();
if (buffer == NULL) {
printf("buffer ptr is NULL\n");
return;
}
node = _get_node();
if (!node) {
printf("_get_node: malloc failed\n");
return;
}
node->content = zalloc(fs->blksz);
if (node->content == NULL)
return;
memcpy(node->content, buffer, fs->blksz);
if (first_node == true) {
revk_blk_list = node;
prev_node = node;
first_node = false;
} else {
prev_node->next = node;
prev_node = node;
}
}
void ext4fs_free_revoke_blks(void)
{
struct revoke_blk_list *tmp_node = revk_blk_list;
struct revoke_blk_list *next_node = NULL;
while (tmp_node != NULL) {
if (tmp_node->content)
free(tmp_node->content);
tmp_node = tmp_node->next;
}
tmp_node = revk_blk_list;
while (tmp_node != NULL) {
next_node = tmp_node->next;
free(tmp_node);
tmp_node = next_node;
}
revk_blk_list = NULL;
prev_node = NULL;
first_node = true;
}
int check_blknr_for_revoke(long int blknr, int sequence_no)
{
struct journal_revoke_header_t *header;
int offset;
int max;
long int blocknr;
char *revk_blk;
struct revoke_blk_list *tmp_revk_node = revk_blk_list;
while (tmp_revk_node != NULL) {
revk_blk = tmp_revk_node->content;
header = (struct journal_revoke_header_t *) revk_blk;
if (sequence_no < be32_to_cpu(header->r_header.h_sequence)) {
offset = sizeof(struct journal_revoke_header_t);
max = be32_to_cpu(header->r_count);
while (offset < max) {
blocknr = be32_to_cpu(*((__be32 *)
(revk_blk + offset)));
if (blocknr == blknr)
goto found;
offset += 4;
}
}
tmp_revk_node = tmp_revk_node->next;
}
return -1;
found:
return 0;
}
/*
* This function parses the journal blocks and replays the
* suceessful transactions. A transaction is successfull
* if commit block is found for a descriptor block
* The tags in descriptor block contain the disk block
* numbers of the metadata to be replayed
*/
void recover_transaction(int prev_desc_logical_no)
{
struct ext2_inode inode_journal;
struct ext_filesystem *fs = get_fs();
struct journal_header_t *jdb;
long int blknr;
char *p_jdb;
int ofs, flags;
int i;
struct ext3_journal_block_tag *tag;
char *temp_buff = zalloc(fs->blksz);
char *metadata_buff = zalloc(fs->blksz);
if (!temp_buff || !metadata_buff)
goto fail;
i = prev_desc_logical_no;
ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO,
(struct ext2_inode *)&inode_journal);
blknr = read_allocated_block((struct ext2_inode *)
&inode_journal, i);
ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz,
temp_buff);
p_jdb = (char *)temp_buff;
jdb = (struct journal_header_t *) temp_buff;
ofs = sizeof(struct journal_header_t);
do {
tag = (struct ext3_journal_block_tag *)(p_jdb + ofs);
ofs += sizeof(struct ext3_journal_block_tag);
if (ofs > fs->blksz)
break;
flags = be32_to_cpu(tag->flags);
if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID))
ofs += 16;
i++;
debug("\t\ttag %u\n", be32_to_cpu(tag->block));
if (revk_blk_list != NULL) {
if (check_blknr_for_revoke(be32_to_cpu(tag->block),
be32_to_cpu(jdb->h_sequence)) == 0)
continue;
}
blknr = read_allocated_block(&inode_journal, i);
ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0,
fs->blksz, metadata_buff);
put_ext4((uint64_t)((uint64_t)be32_to_cpu(tag->block) * (uint64_t)fs->blksz),
metadata_buff, (uint32_t) fs->blksz);
} while (!(flags & EXT3_JOURNAL_FLAG_LAST_TAG));
fail:
free(temp_buff);
free(metadata_buff);
}
void print_jrnl_status(int recovery_flag)
{
if (recovery_flag == RECOVER)
printf("Journal Recovery Completed\n");
else
printf("Journal Scan Completed\n");
}
int ext4fs_check_journal_state(int recovery_flag)
{
int i;
int DB_FOUND = NO;
long int blknr;
int transaction_state = TRANSACTION_COMPLETE;
int prev_desc_logical_no = 0;
int curr_desc_logical_no = 0;
int ofs, flags;
struct ext2_inode inode_journal;
struct journal_superblock_t *jsb = NULL;
struct journal_header_t *jdb = NULL;
char *p_jdb = NULL;
struct ext3_journal_block_tag *tag = NULL;
char *temp_buff = NULL;
char *temp_buff1 = NULL;
struct ext_filesystem *fs = get_fs();
temp_buff = zalloc(fs->blksz);
if (!temp_buff)
return -ENOMEM;
temp_buff1 = zalloc(fs->blksz);
if (!temp_buff1) {
free(temp_buff);
return -ENOMEM;
}
ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal);
blknr = read_allocated_block(&inode_journal, EXT2_JOURNAL_SUPERBLOCK);
ext4fs_devread((lbaint_t)blknr * fs->sect_perblk, 0, fs->blksz,
temp_buff);
jsb = (struct journal_superblock_t *) temp_buff;
if (le32_to_cpu(fs->sb->feature_incompat) & EXT3_FEATURE_INCOMPAT_RECOVER) {
if (recovery_flag == RECOVER)
printf("Recovery required\n");
} else {
if (recovery_flag == RECOVER)
printf("File System is consistent\n");
goto end;
}
if (be32_to_cpu(jsb->s_start) == 0)
goto end;
if (!(jsb->s_feature_compat &
cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM)))
jsb->s_feature_compat |=
cpu_to_be32(JBD2_FEATURE_COMPAT_CHECKSUM);
i = be32_to_cpu(jsb->s_first);
while (1) {
blknr = read_allocated_block(&inode_journal, i);
memset(temp_buff1, '\0', fs->blksz);
ext4fs_devread((lbaint_t)blknr * fs->sect_perblk,
0, fs->blksz, temp_buff1);
jdb = (struct journal_header_t *) temp_buff1;
if (be32_to_cpu(jdb->h_blocktype) ==
EXT3_JOURNAL_DESCRIPTOR_BLOCK) {
if (be32_to_cpu(jdb->h_sequence) !=
be32_to_cpu(jsb->s_sequence)) {
print_jrnl_status(recovery_flag);
break;
}
curr_desc_logical_no = i;
if (transaction_state == TRANSACTION_COMPLETE)
transaction_state = TRANSACTION_RUNNING;
else
return -1;
p_jdb = (char *)temp_buff1;
ofs = sizeof(struct journal_header_t);
do {
tag = (struct ext3_journal_block_tag *)
(p_jdb + ofs);
ofs += sizeof(struct ext3_journal_block_tag);
if (ofs > fs->blksz)
break;
flags = be32_to_cpu(tag->flags);
if (!(flags & EXT3_JOURNAL_FLAG_SAME_UUID))
ofs += 16;
i++;
debug("\t\ttag %u\n", be32_to_cpu(tag->block));
} while (!(flags & EXT3_JOURNAL_FLAG_LAST_TAG));
i++;
DB_FOUND = YES;
} else if (be32_to_cpu(jdb->h_blocktype) ==
EXT3_JOURNAL_COMMIT_BLOCK) {
if (be32_to_cpu(jdb->h_sequence) !=
be32_to_cpu(jsb->s_sequence)) {
print_jrnl_status(recovery_flag);
break;
}
if (transaction_state == TRANSACTION_RUNNING ||
(DB_FOUND == NO)) {
transaction_state = TRANSACTION_COMPLETE;
i++;
jsb->s_sequence =
cpu_to_be32(be32_to_cpu(
jsb->s_sequence) + 1);
}
prev_desc_logical_no = curr_desc_logical_no;
if ((recovery_flag == RECOVER) && (DB_FOUND == YES))
recover_transaction(prev_desc_logical_no);
DB_FOUND = NO;
} else if (be32_to_cpu(jdb->h_blocktype) ==
EXT3_JOURNAL_REVOKE_BLOCK) {
if (be32_to_cpu(jdb->h_sequence) !=
be32_to_cpu(jsb->s_sequence)) {
print_jrnl_status(recovery_flag);
break;
}
if (recovery_flag == SCAN)
ext4fs_push_revoke_blk((char *)jdb);
i++;
} else {
debug("Else Case\n");
if (be32_to_cpu(jdb->h_sequence) !=
be32_to_cpu(jsb->s_sequence)) {
print_jrnl_status(recovery_flag);
break;
}
}
}
end:
if (recovery_flag == RECOVER) {
uint32_t new_feature_incompat;
jsb->s_start = cpu_to_be32(1);
jsb->s_sequence = cpu_to_be32(be32_to_cpu(jsb->s_sequence) + 1);
/* get the superblock */
ext4_read_superblock((char *)fs->sb);
new_feature_incompat = le32_to_cpu(fs->sb->feature_incompat);
new_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER;
fs->sb->feature_incompat = cpu_to_le32(new_feature_incompat);
/* Update the super block */
put_ext4((uint64_t) (SUPERBLOCK_SIZE),
(struct ext2_sblock *)fs->sb,
(uint32_t) SUPERBLOCK_SIZE);
ext4_read_superblock((char *)fs->sb);
blknr = read_allocated_block(&inode_journal,
EXT2_JOURNAL_SUPERBLOCK);
put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
(struct journal_superblock_t *)temp_buff,
(uint32_t) fs->blksz);
ext4fs_free_revoke_blks();
}
free(temp_buff);
free(temp_buff1);
return 0;
}
static void update_descriptor_block(long int blknr)
{
int i;
long int jsb_blknr;
struct journal_header_t jdb;
struct ext3_journal_block_tag tag;
struct ext2_inode inode_journal;
struct journal_superblock_t *jsb = NULL;
char *buf = NULL;
char *temp = NULL;
struct ext_filesystem *fs = get_fs();
char *temp_buff = zalloc(fs->blksz);
if (!temp_buff)
return;
ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal);
jsb_blknr = read_allocated_block(&inode_journal,
EXT2_JOURNAL_SUPERBLOCK);
ext4fs_devread((lbaint_t)jsb_blknr * fs->sect_perblk, 0, fs->blksz,
temp_buff);
jsb = (struct journal_superblock_t *) temp_buff;
jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_DESCRIPTOR_BLOCK);
jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER);
jdb.h_sequence = jsb->s_sequence;
buf = zalloc(fs->blksz);
if (!buf) {
free(temp_buff);
return;
}
temp = buf;
memcpy(buf, &jdb, sizeof(struct journal_header_t));
temp += sizeof(struct journal_header_t);
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (journal_ptr[i]->blknr == -1)
break;
tag.block = cpu_to_be32(journal_ptr[i]->blknr);
tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_SAME_UUID);
memcpy(temp, &tag, sizeof(struct ext3_journal_block_tag));
temp = temp + sizeof(struct ext3_journal_block_tag);
}
tag.block = cpu_to_be32(journal_ptr[--i]->blknr);
tag.flags = cpu_to_be32(EXT3_JOURNAL_FLAG_LAST_TAG);
memcpy(temp - sizeof(struct ext3_journal_block_tag), &tag,
sizeof(struct ext3_journal_block_tag));
put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), buf, (uint32_t) fs->blksz);
free(temp_buff);
free(buf);
}
static void update_commit_block(long int blknr)
{
struct journal_header_t jdb;
struct ext_filesystem *fs = get_fs();
char *buf = NULL;
struct ext2_inode inode_journal;
struct journal_superblock_t *jsb;
long int jsb_blknr;
char *temp_buff = zalloc(fs->blksz);
if (!temp_buff)
return;
ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO,
&inode_journal);
jsb_blknr = read_allocated_block(&inode_journal,
EXT2_JOURNAL_SUPERBLOCK);
ext4fs_devread((lbaint_t)jsb_blknr * fs->sect_perblk, 0, fs->blksz,
temp_buff);
jsb = (struct journal_superblock_t *) temp_buff;
jdb.h_blocktype = cpu_to_be32(EXT3_JOURNAL_COMMIT_BLOCK);
jdb.h_magic = cpu_to_be32(EXT3_JOURNAL_MAGIC_NUMBER);
jdb.h_sequence = jsb->s_sequence;
buf = zalloc(fs->blksz);
if (!buf) {
free(temp_buff);
return;
}
memcpy(buf, &jdb, sizeof(struct journal_header_t));
put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz), buf, (uint32_t) fs->blksz);
free(temp_buff);
free(buf);
}
void ext4fs_update_journal(void)
{
struct ext2_inode inode_journal;
struct ext_filesystem *fs = get_fs();
long int blknr;
int i;
ext4fs_read_inode(ext4fs_root, EXT2_JOURNAL_INO, &inode_journal);
blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++);
update_descriptor_block(blknr);
for (i = 0; i < MAX_JOURNAL_ENTRIES; i++) {
if (journal_ptr[i]->blknr == -1)
break;
blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++);
put_ext4((uint64_t) ((uint64_t)blknr * (uint64_t)fs->blksz),
journal_ptr[i]->buf, fs->blksz);
}
blknr = read_allocated_block(&inode_journal, jrnl_blk_idx++);
update_commit_block(blknr);
printf("update journal finished\n");
}