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 <ext_common.h> 25 #include <ext4fs.h> 26 #include "ext4_common.h" 27 #include <div64.h> 28 29 int ext4fs_symlinknest; 30 struct ext_filesystem ext_fs; 31 32 struct ext_filesystem *get_fs(void) 33 { 34 return &ext_fs; 35 } 36 37 void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot) 38 { 39 if ((node != &ext4fs_root->diropen) && (node != currroot)) 40 free(node); 41 } 42 43 /* 44 * Taken from openmoko-kernel mailing list: By Andy green 45 * Optimized read file API : collects and defers contiguous sector 46 * reads into one potentially more efficient larger sequential read action 47 */ 48 int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, 49 loff_t len, char *buf, loff_t *actread) 50 { 51 struct ext_filesystem *fs = get_fs(); 52 int i; 53 lbaint_t blockcnt; 54 int log2blksz = fs->dev_desc->log2blksz; 55 int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz; 56 int blocksize = (1 << (log2_fs_blocksize + log2blksz)); 57 unsigned int filesize = le32_to_cpu(node->inode.size); 58 lbaint_t previous_block_number = -1; 59 lbaint_t delayed_start = 0; 60 lbaint_t delayed_extent = 0; 61 lbaint_t delayed_skipfirst = 0; 62 lbaint_t delayed_next = 0; 63 char *delayed_buf = NULL; 64 short status; 65 66 if (blocksize <= 0) 67 return -1; 68 69 /* Adjust len so it we can't read past the end of the file. */ 70 if (len + pos > filesize) 71 len = (filesize - pos); 72 73 blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize); 74 75 for (i = lldiv(pos, blocksize); i < blockcnt; i++) { 76 long int blknr; 77 int blockoff = pos - (blocksize * i); 78 int blockend = blocksize; 79 int skipfirst = 0; 80 blknr = read_allocated_block(&(node->inode), i); 81 if (blknr < 0) 82 return -1; 83 84 blknr = blknr << log2_fs_blocksize; 85 86 /* Last block. */ 87 if (i == blockcnt - 1) { 88 blockend = (len + pos) - (blocksize * i); 89 90 /* The last portion is exactly blocksize. */ 91 if (!blockend) 92 blockend = blocksize; 93 } 94 95 /* First block. */ 96 if (i == lldiv(pos, blocksize)) { 97 skipfirst = blockoff; 98 blockend -= skipfirst; 99 } 100 if (blknr) { 101 int status; 102 103 if (previous_block_number != -1) { 104 if (delayed_next == blknr) { 105 delayed_extent += blockend; 106 delayed_next += blockend >> log2blksz; 107 } else { /* spill */ 108 status = ext4fs_devread(delayed_start, 109 delayed_skipfirst, 110 delayed_extent, 111 delayed_buf); 112 if (status == 0) 113 return -1; 114 previous_block_number = blknr; 115 delayed_start = blknr; 116 delayed_extent = blockend; 117 delayed_skipfirst = skipfirst; 118 delayed_buf = buf; 119 delayed_next = blknr + 120 (blockend >> log2blksz); 121 } 122 } else { 123 previous_block_number = blknr; 124 delayed_start = blknr; 125 delayed_extent = blockend; 126 delayed_skipfirst = skipfirst; 127 delayed_buf = buf; 128 delayed_next = blknr + 129 (blockend >> log2blksz); 130 } 131 } else { 132 int n; 133 if (previous_block_number != -1) { 134 /* spill */ 135 status = ext4fs_devread(delayed_start, 136 delayed_skipfirst, 137 delayed_extent, 138 delayed_buf); 139 if (status == 0) 140 return -1; 141 previous_block_number = -1; 142 } 143 /* Zero no more than `len' bytes. */ 144 n = blocksize - skipfirst; 145 if (n > len) 146 n = len; 147 memset(buf, 0, n); 148 } 149 buf += blocksize - skipfirst; 150 } 151 if (previous_block_number != -1) { 152 /* spill */ 153 status = ext4fs_devread(delayed_start, 154 delayed_skipfirst, delayed_extent, 155 delayed_buf); 156 if (status == 0) 157 return -1; 158 previous_block_number = -1; 159 } 160 161 *actread = len; 162 return 0; 163 } 164 165 int ext4fs_ls(const char *dirname) 166 { 167 struct ext2fs_node *dirnode = NULL; 168 int status; 169 170 if (dirname == NULL) 171 return 0; 172 173 status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode, 174 FILETYPE_DIRECTORY); 175 if (status != 1) { 176 printf("** Can not find directory. **\n"); 177 if (dirnode) 178 ext4fs_free_node(dirnode, &ext4fs_root->diropen); 179 return 1; 180 } 181 182 ext4fs_iterate_dir(dirnode, NULL, NULL, NULL); 183 ext4fs_free_node(dirnode, &ext4fs_root->diropen); 184 185 return 0; 186 } 187 188 int ext4fs_exists(const char *filename) 189 { 190 loff_t file_len; 191 int ret; 192 193 ret = ext4fs_open(filename, &file_len); 194 return ret == 0; 195 } 196 197 int ext4fs_size(const char *filename, loff_t *size) 198 { 199 return ext4fs_open(filename, size); 200 } 201 202 int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread) 203 { 204 if (ext4fs_root == NULL || ext4fs_file == NULL) 205 return -1; 206 207 return ext4fs_read_file(ext4fs_file, offset, len, buf, actread); 208 } 209 210 int ext4fs_probe(struct blk_desc *fs_dev_desc, 211 disk_partition_t *fs_partition) 212 { 213 ext4fs_set_blk_dev(fs_dev_desc, fs_partition); 214 215 if (!ext4fs_mount(fs_partition->size)) { 216 ext4fs_close(); 217 return -1; 218 } 219 220 return 0; 221 } 222 223 int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len, 224 loff_t *len_read) 225 { 226 loff_t file_len; 227 int ret; 228 229 ret = ext4fs_open(filename, &file_len); 230 if (ret < 0) { 231 printf("** File not found %s **\n", filename); 232 return -1; 233 } 234 235 if (len == 0) 236 len = file_len; 237 238 return ext4fs_read(buf, offset, len, len_read); 239 } 240 241 int ext4fs_uuid(char *uuid_str) 242 { 243 if (ext4fs_root == NULL) 244 return -1; 245 246 #ifdef CONFIG_LIB_UUID 247 uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id, 248 uuid_str, UUID_STR_FORMAT_STD); 249 250 return 0; 251 #else 252 return -ENOSYS; 253 #endif 254 } 255