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