video/console: Fix DM_VIDEO font glyph array indexing
[oweals/u-boot.git] / fs / ext4 / ext4fs.c
index cbdc22026deb63a24968cbd3c0078407940bc5a2..26db677a1f172bde1f10ac5297bf7226b6f6ba15 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * (C) Copyright 2011 - 2012 Samsung Electronics
  * EXT4 filesystem implementation in Uboot by
  * Copyright (C) 2003, 2004  Free Software Foundation, Inc.
  *
  * ext4write : Based on generic ext4 protocol.
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <ext_common.h>
 #include <ext4fs.h>
 #include "ext4_common.h"
+#include <div64.h>
 
 int ext4fs_symlinknest;
 struct ext_filesystem ext_fs;
@@ -45,8 +45,8 @@ void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
  * 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 ext4fs_read_file(struct ext2fs_node *node, loff_t pos,
+               loff_t len, char *buf, loff_t *actread)
 {
        struct ext_filesystem *fs = get_fs();
        int i;
@@ -54,7 +54,7 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
        int log2blksz = fs->dev_desc->log2blksz;
        int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz;
        int blocksize = (1 << (log2_fs_blocksize + log2blksz));
-       unsigned int filesize = __le32_to_cpu(node->inode.size);
+       unsigned int filesize = le32_to_cpu(node->inode.size);
        lbaint_t previous_block_number = -1;
        lbaint_t delayed_start = 0;
        lbaint_t delayed_extent = 0;
@@ -62,27 +62,35 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
        lbaint_t delayed_next = 0;
        char *delayed_buf = NULL;
        short status;
+       struct ext_block_cache cache;
+
+       ext_cache_init(&cache);
+
+       if (blocksize <= 0)
+               return -1;
 
        /* Adjust len so it we can't read past the end of the file. */
-       if (len > filesize)
-               len = filesize;
+       if (len + pos > filesize)
+               len = (filesize - pos);
 
-       blockcnt = ((len + pos) + blocksize - 1) / blocksize;
+       blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
 
-       for (i = pos / blocksize; i < blockcnt; i++) {
-               lbaint_t blknr;
-               int blockoff = pos % blocksize;
+       for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
+               long int blknr;
+               int blockoff = pos - (blocksize * i);
                int blockend = blocksize;
                int skipfirst = 0;
-               blknr = read_allocated_block(&(node->inode), i);
-               if (blknr < 0)
+               blknr = read_allocated_block(&node->inode, i, &cache);
+               if (blknr < 0) {
+                       ext_cache_fini(&cache);
                        return -1;
+               }
 
                blknr = blknr << log2_fs_blocksize;
 
                /* Last block.  */
                if (i == blockcnt - 1) {
-                       blockend = (len + pos) % blocksize;
+                       blockend = (len + pos) - (blocksize * i);
 
                        /* The last portion is exactly blocksize. */
                        if (!blockend)
@@ -90,7 +98,7 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
                }
 
                /* First block. */
-               if (i == pos / blocksize) {
+               if (i == lldiv(pos, blocksize)) {
                        skipfirst = blockoff;
                        blockend -= skipfirst;
                }
@@ -106,8 +114,10 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
                                                        delayed_skipfirst,
                                                        delayed_extent,
                                                        delayed_buf);
-                                       if (status == 0)
+                                       if (status == 0) {
+                                               ext_cache_fini(&cache);
                                                return -1;
+                                       }
                                        previous_block_number = blknr;
                                        delayed_start = blknr;
                                        delayed_extent = blockend;
@@ -126,17 +136,24 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
                                        (blockend >> log2blksz);
                        }
                } else {
+                       int n;
                        if (previous_block_number != -1) {
                                /* spill */
                                status = ext4fs_devread(delayed_start,
                                                        delayed_skipfirst,
                                                        delayed_extent,
                                                        delayed_buf);
-                               if (status == 0)
+                               if (status == 0) {
+                                       ext_cache_fini(&cache);
                                        return -1;
+                               }
                                previous_block_number = -1;
                        }
-                       memset(buf, 0, blocksize - skipfirst);
+                       /* Zero no more than `len' bytes. */
+                       n = blocksize - skipfirst;
+                       if (n > len)
+                               n = len;
+                       memset(buf, 0, n);
                }
                buf += blocksize - skipfirst;
        }
@@ -145,17 +162,21 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
                status = ext4fs_devread(delayed_start,
                                        delayed_skipfirst, delayed_extent,
                                        delayed_buf);
-               if (status == 0)
+               if (status == 0) {
+                       ext_cache_fini(&cache);
                        return -1;
+               }
                previous_block_number = -1;
        }
 
-       return len;
+       *actread  = len;
+       ext_cache_fini(&cache);
+       return 0;
 }
 
 int ext4fs_ls(const char *dirname)
 {
-       struct ext2fs_node *dirnode;
+       struct ext2fs_node *dirnode = NULL;
        int status;
 
        if (dirname == NULL)
@@ -165,6 +186,8 @@ int ext4fs_ls(const char *dirname)
                                  FILETYPE_DIRECTORY);
        if (status != 1) {
                printf("** Can not find directory. **\n");
+               if (dirnode)
+                       ext4fs_free_node(dirnode, &ext4fs_root->diropen);
                return 1;
        }
 
@@ -176,26 +199,27 @@ int ext4fs_ls(const char *dirname)
 
 int ext4fs_exists(const char *filename)
 {
-       int file_len;
+       loff_t file_len;
+       int ret;
 
-       file_len = ext4fs_open(filename);
-       return file_len >= 0;
+       ret = ext4fs_open(filename, &file_len);
+       return ret == 0;
 }
 
-int ext4fs_size(const char *filename)
+int ext4fs_size(const char *filename, loff_t *size)
 {
-       return ext4fs_open(filename);
+       return ext4fs_open(filename, size);
 }
 
-int ext4fs_read(char *buf, unsigned len)
+int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread)
 {
        if (ext4fs_root == NULL || ext4fs_file == NULL)
-               return 0;
+               return -1;
 
-       return ext4fs_read_file(ext4fs_file, 0, len, buf);
+       return ext4fs_read_file(ext4fs_file, offset, len, buf, actread);
 }
 
-int ext4fs_probe(block_dev_desc_t *fs_dev_desc,
+int ext4fs_probe(struct blk_desc *fs_dev_desc,
                 disk_partition_t *fs_partition)
 {
        ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
@@ -208,18 +232,14 @@ int ext4fs_probe(block_dev_desc_t *fs_dev_desc,
        return 0;
 }
 
-int ext4_read_file(const char *filename, void *buf, int offset, int len)
+int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
+                  loff_t *len_read)
 {
-       int file_len;
-       int len_read;
-
-       if (offset != 0) {
-               printf("** Cannot support non-zero offset **\n");
-               return -1;
-       }
+       loff_t file_len;
+       int ret;
 
-       file_len = ext4fs_open(filename);
-       if (file_len < 0) {
+       ret = ext4fs_open(filename, &file_len);
+       if (ret < 0) {
                printf("** File not found %s **\n", filename);
                return -1;
        }
@@ -227,7 +247,49 @@ int ext4_read_file(const char *filename, void *buf, int offset, int len)
        if (len == 0)
                len = file_len;
 
-       len_read = ext4fs_read(buf, len);
+       return ext4fs_read(buf, offset, len, len_read);
+}
+
+int ext4fs_uuid(char *uuid_str)
+{
+       if (ext4fs_root == NULL)
+               return -1;
+
+#ifdef CONFIG_LIB_UUID
+       uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id,
+                       uuid_str, UUID_STR_FORMAT_STD);
+
+       return 0;
+#else
+       return -ENOSYS;
+#endif
+}
+
+void ext_cache_init(struct ext_block_cache *cache)
+{
+       memset(cache, 0, sizeof(*cache));
+}
+
+void ext_cache_fini(struct ext_block_cache *cache)
+{
+       free(cache->buf);
+       ext_cache_init(cache);
+}
 
-       return len_read;
+int ext_cache_read(struct ext_block_cache *cache, lbaint_t block, int size)
+{
+       /* This could be more lenient, but this is simple and enough for now */
+       if (cache->buf && cache->block == block && cache->size == size)
+               return 1;
+       ext_cache_fini(cache);
+       cache->buf = malloc(size);
+       if (!cache->buf)
+               return 0;
+       if (!ext4fs_devread(block, 0, size, cache->buf)) {
+               free(cache->buf);
+               return 0;
+       }
+       cache->block = block;
+       cache->size = size;
+       return 1;
 }