uboot/fs/ext4/ext4fs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2011 - 2012 Samsung Electronics
   4 * EXT4 filesystem implementation in Uboot by
   5 * Uma Shankar <uma.shankar@samsung.com>
   6 * Manjunatha C Achar <a.manjunatha@samsung.com>
   7 *
   8 * ext4ls and ext4load : Based on ext2 ls and load support in Uboot.
   9 *                     Ext4 read optimization taken from Open-Moko
  10 *                     Qi bootloader
  11 *
  12 * (C) Copyright 2004
  13 * esd gmbh <www.esd-electronics.com>
  14 * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
  15 *
  16 * based on code from grub2 fs/ext2.c and fs/fshelp.c by
  17 * GRUB  --  GRand Unified Bootloader
  18 * Copyright (C) 2003, 2004  Free Software Foundation, Inc.
  19 *
  20 * ext4write : Based on generic ext4 protocol.
  21 */
  22
  23#include <common.h>
  24#include <blk.h>
  25#include <ext_common.h>
  26#include <ext4fs.h>
  27#include "ext4_common.h"
  28#include <div64.h>
  29#include <malloc.h>
  30#include <part.h>
  31#include <uuid.h>
  32
  33int ext4fs_symlinknest;
  34struct ext_filesystem ext_fs;
  35
  36struct ext_filesystem *get_fs(void)
  37{
  38        return &ext_fs;
  39}
  40
  41void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
  42{
  43        if ((node != &ext4fs_root->diropen) && (node != currroot))
  44                free(node);
  45}
  46
  47/*
  48 * Taken from openmoko-kernel mailing list: By Andy green
  49 * Optimized read file API : collects and defers contiguous sector
  50 * reads into one potentially more efficient larger sequential read action
  51 */
  52int ext4fs_read_file(struct ext2fs_node *node, loff_t pos,
  53                loff_t len, char *buf, loff_t *actread)
  54{
  55        struct ext_filesystem *fs = get_fs();
  56        int i;
  57        lbaint_t blockcnt;
  58        int log2blksz = fs->dev_desc->log2blksz;
  59        int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz;
  60        int blocksize = (1 << (log2_fs_blocksize + log2blksz));
  61        unsigned int filesize = le32_to_cpu(node->inode.size);
  62        lbaint_t previous_block_number = -1;
  63        lbaint_t delayed_start = 0;
  64        lbaint_t delayed_extent = 0;
  65        lbaint_t delayed_skipfirst = 0;
  66        lbaint_t delayed_next = 0;
  67        char *delayed_buf = NULL;
  68        char *start_buf = buf;
  69        short status;
  70        struct ext_block_cache cache;
  71
  72        ext_cache_init(&cache);
  73
  74        /* Adjust len so it we can't read past the end of the file. */
  75        if (len + pos > filesize)
  76                len = (filesize - pos);
  77
  78        if (blocksize <= 0 || len <= 0) {
  79                ext_cache_fini(&cache);
  80                return -1;
  81        }
  82
  83        blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
  84
  85        for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
  86                long int blknr;
  87                int blockoff = pos - (blocksize * i);
  88                int blockend = blocksize;
  89                int skipfirst = 0;
  90                blknr = read_allocated_block(&node->inode, i, &cache);
  91                if (blknr < 0) {
  92                        ext_cache_fini(&cache);
  93                        return -1;
  94                }
  95
  96                blknr = blknr << log2_fs_blocksize;
  97
  98                /* Last block.  */
  99                if (i == blockcnt - 1) {
 100                        blockend = (len + pos) - (blocksize * i);
 101
 102                        /* The last portion is exactly blocksize. */
 103                        if (!blockend)
 104                                blockend = blocksize;
 105                }
 106
 107                /* First block. */
 108                if (i == lldiv(pos, blocksize)) {
 109                        skipfirst = blockoff;
 110                        blockend -= skipfirst;
 111                }
 112                if (blknr) {
 113                        int status;
 114
 115                        if (previous_block_number != -1) {
 116                                if (delayed_next == blknr) {
 117                                        delayed_extent += blockend;
 118                                        delayed_next += blockend >> log2blksz;
 119                                } else {        /* spill */
 120                                        status = ext4fs_devread(delayed_start,
 121                                                        delayed_skipfirst,
 122                                                        delayed_extent,
 123                                                        delayed_buf);
 124                                        if (status == 0) {
 125                                                ext_cache_fini(&cache);
 126                                                return -1;
 127                                        }
 128                                        previous_block_number = blknr;
 129                                        delayed_start = blknr;
 130                                        delayed_extent = blockend;
 131                                        delayed_skipfirst = skipfirst;
 132                                        delayed_buf = buf;
 133                                        delayed_next = blknr +
 134                                                (blockend >> log2blksz);
 135                                }
 136                        } else {
 137                                previous_block_number = blknr;
 138                                delayed_start = blknr;
 139                                delayed_extent = blockend;
 140                                delayed_skipfirst = skipfirst;
 141                                delayed_buf = buf;
 142                                delayed_next = blknr +
 143                                        (blockend >> log2blksz);
 144                        }
 145                } else {
 146                        int n;
 147                        int n_left;
 148                        if (previous_block_number != -1) {
 149                                /* spill */
 150                                status = ext4fs_devread(delayed_start,
 151                                                        delayed_skipfirst,
 152                                                        delayed_extent,
 153                                                        delayed_buf);
 154                                if (status == 0) {
 155                                        ext_cache_fini(&cache);
 156                                        return -1;
 157                                }
 158                                previous_block_number = -1;
 159                        }
 160                        /* Zero no more than `len' bytes. */
 161                        n = blocksize - skipfirst;
 162                        n_left = len - ( buf - start_buf );
 163                        if (n > n_left)
 164                                n = n_left;
 165                        memset(buf, 0, n);
 166                }
 167                buf += blocksize - skipfirst;
 168        }
 169        if (previous_block_number != -1) {
 170                /* spill */
 171                status = ext4fs_devread(delayed_start,
 172                                        delayed_skipfirst, delayed_extent,
 173                                        delayed_buf);
 174                if (status == 0) {
 175                        ext_cache_fini(&cache);
 176                        return -1;
 177                }
 178                previous_block_number = -1;
 179        }
 180
 181        *actread  = len;
 182        ext_cache_fini(&cache);
 183        return 0;
 184}
 185
 186int ext4fs_ls(const char *dirname)
 187{
 188        struct ext2fs_node *dirnode = NULL;
 189        int status;
 190
 191        if (dirname == NULL)
 192                return 0;
 193
 194        status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
 195                                  FILETYPE_DIRECTORY);
 196        if (status != 1) {
 197                printf("** Can not find directory. **\n");
 198                if (dirnode)
 199                        ext4fs_free_node(dirnode, &ext4fs_root->diropen);
 200                return 1;
 201        }
 202
 203        ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
 204        ext4fs_free_node(dirnode, &ext4fs_root->diropen);
 205
 206        return 0;
 207}
 208
 209int ext4fs_exists(const char *filename)
 210{
 211        loff_t file_len;
 212        int ret;
 213
 214        ret = ext4fs_open(filename, &file_len);
 215        return ret == 0;
 216}
 217
 218int ext4fs_size(const char *filename, loff_t *size)
 219{
 220        return ext4fs_open(filename, size);
 221}
 222
 223int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread)
 224{
 225        if (ext4fs_root == NULL || ext4fs_file == NULL)
 226                return -1;
 227
 228        return ext4fs_read_file(ext4fs_file, offset, len, buf, actread);
 229}
 230
 231int ext4fs_probe(struct blk_desc *fs_dev_desc,
 232                 struct disk_partition *fs_partition)
 233{
 234        ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
 235
 236        if (!ext4fs_mount(fs_partition->size)) {
 237                ext4fs_close();
 238                return -1;
 239        }
 240
 241        return 0;
 242}
 243
 244int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
 245                   loff_t *len_read)
 246{
 247        loff_t file_len;
 248        int ret;
 249
 250        ret = ext4fs_open(filename, &file_len);
 251        if (ret < 0) {
 252                printf("** File not found %s **\n", filename);
 253                return -1;
 254        }
 255
 256        if (len == 0)
 257                len = file_len;
 258
 259        return ext4fs_read(buf, offset, len, len_read);
 260}
 261
 262int ext4fs_uuid(char *uuid_str)
 263{
 264        if (ext4fs_root == NULL)
 265                return -1;
 266
 267#ifdef CONFIG_LIB_UUID
 268        uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id,
 269                        uuid_str, UUID_STR_FORMAT_STD);
 270
 271        return 0;
 272#else
 273        return -ENOSYS;
 274#endif
 275}
 276
 277void ext_cache_init(struct ext_block_cache *cache)
 278{
 279        memset(cache, 0, sizeof(*cache));
 280}
 281
 282void ext_cache_fini(struct ext_block_cache *cache)
 283{
 284        free(cache->buf);
 285        ext_cache_init(cache);
 286}
 287
 288int ext_cache_read(struct ext_block_cache *cache, lbaint_t block, int size)
 289{
 290        /* This could be more lenient, but this is simple and enough for now */
 291        if (cache->buf && cache->block == block && cache->size == size)
 292                return 1;
 293        ext_cache_fini(cache);
 294        cache->buf = memalign(ARCH_DMA_MINALIGN, size);
 295        if (!cache->buf)
 296                return 0;
 297        if (!ext4fs_devread(block, 0, size, cache->buf)) {
 298                ext_cache_fini(cache);
 299                return 0;
 300        }
 301        cache->block = block;
 302        cache->size = size;
 303        return 1;
 304}
 305