blob: 4dddde247649f9e3d0214a74416cb706279a5144 [file] [log] [blame]
Uma Shankar71014b62012-05-25 21:21:44 +05301/*
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 *
Uma Shankara74a99a2012-05-25 21:22:49 +053019 * ext4write : Based on generic ext4 protocol.
20 *
Uma Shankar71014b62012-05-25 21:21:44 +053021 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 */
35
36#include <common.h>
Uma Shankar71014b62012-05-25 21:21:44 +053037#include <ext_common.h>
38#include <ext4fs.h>
Uma Shankar71014b62012-05-25 21:21:44 +053039#include "ext4_common.h"
40
41int ext4fs_symlinknest;
Rob Herring30f7fd72012-08-23 11:31:45 +000042struct ext_filesystem ext_fs;
Uma Shankar71014b62012-05-25 21:21:44 +053043
44struct ext_filesystem *get_fs(void)
45{
Rob Herring30f7fd72012-08-23 11:31:45 +000046 return &ext_fs;
Uma Shankar71014b62012-05-25 21:21:44 +053047}
48
49void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
50{
51 if ((node != &ext4fs_root->diropen) && (node != currroot))
52 free(node);
53}
54
55/*
56 * Taken from openmoko-kernel mailing list: By Andy green
57 * Optimized read file API : collects and defers contiguous sector
58 * reads into one potentially more efficient larger sequential read action
59 */
60int ext4fs_read_file(struct ext2fs_node *node, int pos,
61 unsigned int len, char *buf)
62{
63 int i;
64 int blockcnt;
65 int log2blocksize = LOG2_EXT2_BLOCK_SIZE(node->data);
66 int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS);
67 unsigned int filesize = __le32_to_cpu(node->inode.size);
68 int previous_block_number = -1;
69 int delayed_start = 0;
70 int delayed_extent = 0;
71 int delayed_skipfirst = 0;
72 int delayed_next = 0;
73 char *delayed_buf = NULL;
74 short status;
75
76 /* Adjust len so it we can't read past the end of the file. */
77 if (len > filesize)
78 len = filesize;
79
80 blockcnt = ((len + pos) + blocksize - 1) / blocksize;
81
82 for (i = pos / blocksize; i < blockcnt; i++) {
83 int blknr;
84 int blockoff = pos % blocksize;
85 int blockend = blocksize;
86 int skipfirst = 0;
87 blknr = read_allocated_block(&(node->inode), i);
88 if (blknr < 0)
89 return -1;
90
91 blknr = blknr << log2blocksize;
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 >> SECTOR_BITS;
114 } else { /* spill */
115 status = ext4fs_devread(delayed_start,
116 delayed_skipfirst,
117 delayed_extent,
118 delayed_buf);
119 if (status == 0)
120 return -1;
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 >> SECTOR_BITS);
128 }
129 } else {
130 previous_block_number = blknr;
131 delayed_start = blknr;
132 delayed_extent = blockend;
133 delayed_skipfirst = skipfirst;
134 delayed_buf = buf;
135 delayed_next = blknr +
136 (blockend >> SECTOR_BITS);
137 }
138 } else {
139 if (previous_block_number != -1) {
140 /* spill */
141 status = ext4fs_devread(delayed_start,
142 delayed_skipfirst,
143 delayed_extent,
144 delayed_buf);
145 if (status == 0)
146 return -1;
147 previous_block_number = -1;
148 }
149 memset(buf, 0, blocksize - skipfirst);
150 }
151 buf += blocksize - skipfirst;
152 }
153 if (previous_block_number != -1) {
154 /* spill */
155 status = ext4fs_devread(delayed_start,
156 delayed_skipfirst, delayed_extent,
157 delayed_buf);
158 if (status == 0)
159 return -1;
160 previous_block_number = -1;
161 }
162
163 return len;
164}
165
166int ext4fs_ls(const char *dirname)
167{
168 struct ext2fs_node *dirnode;
169 int status;
170
171 if (dirname == NULL)
172 return 0;
173
174 status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
175 FILETYPE_DIRECTORY);
176 if (status != 1) {
177 printf("** Can not find directory. **\n");
178 return 1;
179 }
180
181 ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
182 ext4fs_free_node(dirnode, &ext4fs_root->diropen);
183
184 return 0;
185}
186
187int ext4fs_read(char *buf, unsigned len)
188{
189 if (ext4fs_root == NULL || ext4fs_file == NULL)
190 return 0;
191
192 return ext4fs_read_file(ext4fs_file, 0, len, buf);
193}
Simon Glass19e38582012-12-26 09:53:33 +0000194
195int ext4fs_probe(block_dev_desc_t *fs_dev_desc,
196 disk_partition_t *fs_partition)
197{
198 ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
199
200 if (!ext4fs_mount(fs_partition->size)) {
201 ext4fs_close();
202 return -1;
203 }
204
205 return 0;
206}
207
208int ext4_read_file(const char *filename, void *buf, int offset, int len)
209{
210 int file_len;
211 int len_read;
212
213 if (offset != 0) {
214 printf("** Cannot support non-zero offset **\n");
215 return -1;
216 }
217
218 file_len = ext4fs_open(filename);
219 if (file_len < 0) {
220 printf("** File not found %s **\n", filename);
221 return -1;
222 }
223
224 if (len == 0)
225 len = file_len;
226
227 len_read = ext4fs_read(buf, len);
228
229 return len_read;
230}