1 /* 2 * (C) Copyright 2011 - 2012 Samsung Electronics 3 * EXT4 filesystem implementation in Uboot by 4 * Uma Shankar <uma.shankar@samsung.com> 5 * Manjunatha C Achar <a.manjunatha@samsung.com> 6 * 7 * ext4ls and ext4load : Based on ext2 ls and load support in Uboot. 8 * Ext4 read optimization taken from Open-Moko 9 * Qi bootloader 10 * 11 * (C) Copyright 2004 12 * esd gmbh <www.esd-electronics.com> 13 * Reinhard Arlt <reinhard.arlt@esd-electronics.com> 14 * 15 * based on code from grub2 fs/ext2.c and fs/fshelp.c by 16 * GRUB -- GRand Unified Bootloader 17 * Copyright (C) 2003, 2004 Free Software Foundation, Inc. 18 * 19 * ext4write : Based on generic ext4 protocol. 20 * 21 * SPDX-License-Identifier: GPL-2.0+ 22 */ 23 24 #include <common.h> 25 #include <ext_common.h> 26 #include <ext4fs.h> 27 #include "ext4_common.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, int pos, 49 unsigned int len, char *buf) 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 (le32_to_cpu(node->inode.flags) & EXT4_EXTENTS_FL) { 67 if (ext4fs_build_extent_cache(&node->inode)) { 68 printf("Error building extent cache!\n"); 69 len = -1; 70 goto out_exit; 71 } 72 } 73 74 /* Adjust len so it we can't read past the end of the file. */ 75 if (len > filesize) 76 len = filesize; 77 78 blockcnt = ((len + pos) + blocksize - 1) / blocksize; 79 80 for (i = pos / blocksize; i < blockcnt; i++) { 81 lbaint_t blknr; 82 int blockoff = pos % blocksize; 83 int blockend = blocksize; 84 int skipfirst = 0; 85 blknr = read_allocated_block(&(node->inode), i); 86 if (blknr < 0) { 87 len = -1; 88 goto out_exit; 89 } 90 91 blknr = blknr << log2_fs_blocksize; 92 93 /* Last block. */ 94 if (i == blockcnt - 1) { 95 blockend = (len + pos) % blocksize; 96 97 /* The last portion is exactly blocksize. */ 98 if (!blockend) 99 blockend = blocksize; 100 } 101 102 /* First block. */ 103 if (i == pos / blocksize) { 104 skipfirst = blockoff; 105 blockend -= skipfirst; 106 } 107 if (blknr) { 108 int status; 109 110 if (previous_block_number != -1) { 111 if (delayed_next == blknr) { 112 delayed_extent += blockend; 113 delayed_next += blockend >> log2blksz; 114 } else { /* spill */ 115 status = ext4fs_devread(delayed_start, 116 delayed_skipfirst, 117 delayed_extent, 118 delayed_buf); 119 if (status == 0) { 120 len = -1; 121 goto out_exit; 122 } 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 previous_block_number = blknr; 133 delayed_start = blknr; 134 delayed_extent = blockend; 135 delayed_skipfirst = skipfirst; 136 delayed_buf = buf; 137 delayed_next = blknr + 138 (blockend >> log2blksz); 139 } 140 } else { 141 if (previous_block_number != -1) { 142 /* spill */ 143 status = ext4fs_devread(delayed_start, 144 delayed_skipfirst, 145 delayed_extent, 146 delayed_buf); 147 if (status == 0) { 148 len = -1; 149 goto out_exit; 150 } 151 previous_block_number = -1; 152 } 153 memset(buf, 0, blocksize - skipfirst); 154 } 155 buf += blocksize - skipfirst; 156 } 157 if (previous_block_number != -1) { 158 /* spill */ 159 status = ext4fs_devread(delayed_start, 160 delayed_skipfirst, delayed_extent, 161 delayed_buf); 162 if (status == 0) { 163 len = -1; 164 goto out_exit; 165 } 166 previous_block_number = -1; 167 } 168 169 170 out_exit: 171 ext4fs_free_extent_cache(); 172 173 return len; 174 } 175 176 int ext4fs_ls(const char *dirname) 177 { 178 struct ext2fs_node *dirnode; 179 int status; 180 181 if (dirname == NULL) 182 return 0; 183 184 status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode, 185 FILETYPE_DIRECTORY); 186 if (status != 1) { 187 printf("** Can not find directory. **\n"); 188 return 1; 189 } 190 191 ext4fs_iterate_dir(dirnode, NULL, NULL, NULL); 192 ext4fs_free_node(dirnode, &ext4fs_root->diropen); 193 194 return 0; 195 } 196 197 int ext4fs_exists(const char *filename) 198 { 199 int file_len; 200 201 file_len = ext4fs_open(filename); 202 return file_len >= 0; 203 } 204 205 int ext4fs_read(char *buf, unsigned len) 206 { 207 if (ext4fs_root == NULL || ext4fs_file == NULL) 208 return 0; 209 210 return ext4fs_read_file(ext4fs_file, 0, len, buf); 211 } 212 213 int ext4fs_probe(block_dev_desc_t *fs_dev_desc, 214 disk_partition_t *fs_partition) 215 { 216 ext4fs_set_blk_dev(fs_dev_desc, fs_partition); 217 218 if (!ext4fs_mount(fs_partition->size)) { 219 ext4fs_close(); 220 return -1; 221 } 222 223 return 0; 224 } 225 226 int ext4_read_file(const char *filename, void *buf, int offset, int len) 227 { 228 int file_len; 229 int len_read; 230 231 if (offset != 0) { 232 printf("** Cannot support non-zero offset **\n"); 233 return -1; 234 } 235 236 file_len = ext4fs_open(filename); 237 if (file_len < 0) { 238 printf("** File not found %s **\n", filename); 239 return -1; 240 } 241 242 if (len == 0) 243 len = file_len; 244 245 len_read = ext4fs_read(buf, len); 246 247 return len_read; 248 } 249