/* * (C) Copyright 2011 - 2012 Samsung Electronics * EXT4 filesystem implementation in Uboot by * Uma Shankar * Manjunatha C Achar * * ext4ls and ext4load : Based on ext2 ls and load support in Uboot. * Ext4 read optimization taken from Open-Moko * Qi bootloader * * (C) Copyright 2004 * esd gmbh * Reinhard Arlt * * based on code from grub2 fs/ext2.c and fs/fshelp.c by * GRUB -- GRand Unified Bootloader * Copyright (C) 2003, 2004 Free Software Foundation, Inc. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include "ext4_common.h" void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot) { if ((node != &node->data->diropen) && (node != currroot)) free(node); } /* * Taken from openmoko-kernel mailing list: By Andy green * Optimized read file API : collects and defers contiguous sector * reads into one potentially more efficient larger sequential read action */ int ext4fs_read_file(struct ext2fs_node *node, int pos, unsigned int len, char *buf) { int i; int blockcnt; int log2blocksize = LOG2_EXT2_BLOCK_SIZE(node->data); int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS); unsigned int filesize = le32_to_cpu(node->inode.size); int previous_block_number = -1; int delayed_start = 0; int delayed_extent = 0; int delayed_skipfirst = 0; int delayed_next = 0; char *delayed_buf = NULL; short ret; struct ext_filesystem *fs = node->data->fs; /* Adjust len so it we can't read past the end of the file. */ if (len > filesize) len = filesize; blockcnt = ((len + pos) + blocksize - 1) / blocksize; for (i = pos / blocksize; i < blockcnt; i++) { int blknr; int blockoff = pos % blocksize; int blockend = blocksize; int skipfirst = 0; blknr = read_allocated_block(node, i); if (blknr < 0) return blknr; blknr = blknr << log2blocksize; /* Last block. */ if (i == blockcnt - 1) { blockend = (len + pos) % blocksize; /* The last portion is exactly blocksize. */ if (!blockend) blockend = blocksize; } /* First block. */ if (i == pos / blocksize) { skipfirst = blockoff; blockend -= skipfirst; } if (blknr) { if (previous_block_number != -1) { if (delayed_next == blknr) { delayed_extent += blockend; delayed_next += blockend >> SECTOR_BITS; } else { /* spill */ ret = ext4fs_devread(fs, delayed_start, delayed_skipfirst, delayed_extent, delayed_buf); if (ret) return ret; previous_block_number = blknr; delayed_start = blknr; delayed_extent = blockend; delayed_skipfirst = skipfirst; delayed_buf = buf; delayed_next = blknr + (blockend >> SECTOR_BITS); } } else { previous_block_number = blknr; delayed_start = blknr; delayed_extent = blockend; delayed_skipfirst = skipfirst; delayed_buf = buf; delayed_next = blknr + (blockend >> SECTOR_BITS); } } else { if (previous_block_number != -1) { /* spill */ ret = ext4fs_devread(fs, delayed_start, delayed_skipfirst, delayed_extent, delayed_buf); if (ret) return ret; previous_block_number = -1; } memset(buf, 0, blocksize - skipfirst); } buf += blocksize - skipfirst; } if (previous_block_number != -1) { /* spill */ ret = ext4fs_devread(fs, delayed_start, delayed_skipfirst, delayed_extent, delayed_buf); if (ret) return ret; previous_block_number = -1; } return len; }