xref: /openbmc/u-boot/fs/ext4/ext4fs.c (revision b46694df)
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  * 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>
37 #include <ext_common.h>
38 #include <ext4fs.h>
39 #include "ext4_common.h"
40 
41 int ext4fs_symlinknest;
42 struct ext_filesystem ext_fs;
43 
44 struct ext_filesystem *get_fs(void)
45 {
46 	return &ext_fs;
47 }
48 
49 void 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  */
60 int ext4fs_read_file(struct ext2fs_node *node, int pos,
61 		unsigned int len, char *buf)
62 {
63 	struct ext_filesystem *fs = get_fs();
64 	int i;
65 	int blockcnt;
66 	int log2blksz = fs->dev_desc->log2blksz;
67 	int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz;
68 	int blocksize = (1 << (log2_fs_blocksize + log2blksz));
69 	unsigned int filesize = __le32_to_cpu(node->inode.size);
70 	int previous_block_number = -1;
71 	int delayed_start = 0;
72 	int delayed_extent = 0;
73 	int delayed_skipfirst = 0;
74 	int delayed_next = 0;
75 	char *delayed_buf = NULL;
76 	short status;
77 
78 	/* Adjust len so it we can't read past the end of the file. */
79 	if (len > filesize)
80 		len = filesize;
81 
82 	blockcnt = ((len + pos) + blocksize - 1) / blocksize;
83 
84 	for (i = pos / blocksize; i < blockcnt; i++) {
85 		int blknr;
86 		int blockoff = pos % blocksize;
87 		int blockend = blocksize;
88 		int skipfirst = 0;
89 		blknr = read_allocated_block(&(node->inode), i);
90 		if (blknr < 0)
91 			return -1;
92 
93 		blknr = blknr << log2_fs_blocksize;
94 
95 		/* Last block.  */
96 		if (i == blockcnt - 1) {
97 			blockend = (len + pos) % blocksize;
98 
99 			/* The last portion is exactly blocksize. */
100 			if (!blockend)
101 				blockend = blocksize;
102 		}
103 
104 		/* First block. */
105 		if (i == pos / blocksize) {
106 			skipfirst = blockoff;
107 			blockend -= skipfirst;
108 		}
109 		if (blknr) {
110 			int status;
111 
112 			if (previous_block_number != -1) {
113 				if (delayed_next == blknr) {
114 					delayed_extent += blockend;
115 					delayed_next += blockend >> log2blksz;
116 				} else {	/* spill */
117 					status = ext4fs_devread(delayed_start,
118 							delayed_skipfirst,
119 							delayed_extent,
120 							delayed_buf);
121 					if (status == 0)
122 						return -1;
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 					return -1;
149 				previous_block_number = -1;
150 			}
151 			memset(buf, 0, blocksize - skipfirst);
152 		}
153 		buf += blocksize - skipfirst;
154 	}
155 	if (previous_block_number != -1) {
156 		/* spill */
157 		status = ext4fs_devread(delayed_start,
158 					delayed_skipfirst, delayed_extent,
159 					delayed_buf);
160 		if (status == 0)
161 			return -1;
162 		previous_block_number = -1;
163 	}
164 
165 	return len;
166 }
167 
168 int ext4fs_ls(const char *dirname)
169 {
170 	struct ext2fs_node *dirnode;
171 	int status;
172 
173 	if (dirname == NULL)
174 		return 0;
175 
176 	status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
177 				  FILETYPE_DIRECTORY);
178 	if (status != 1) {
179 		printf("** Can not find directory. **\n");
180 		return 1;
181 	}
182 
183 	ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
184 	ext4fs_free_node(dirnode, &ext4fs_root->diropen);
185 
186 	return 0;
187 }
188 
189 int ext4fs_read(char *buf, unsigned len)
190 {
191 	if (ext4fs_root == NULL || ext4fs_file == NULL)
192 		return 0;
193 
194 	return ext4fs_read_file(ext4fs_file, 0, len, buf);
195 }
196 
197 int ext4fs_probe(block_dev_desc_t *fs_dev_desc,
198 		 disk_partition_t *fs_partition)
199 {
200 	ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
201 
202 	if (!ext4fs_mount(fs_partition->size)) {
203 		ext4fs_close();
204 		return -1;
205 	}
206 
207 	return 0;
208 }
209 
210 int ext4_read_file(const char *filename, void *buf, int offset, int len)
211 {
212 	int file_len;
213 	int len_read;
214 
215 	if (offset != 0) {
216 		printf("** Cannot support non-zero offset **\n");
217 		return -1;
218 	}
219 
220 	file_len = ext4fs_open(filename);
221 	if (file_len < 0) {
222 		printf("** File not found %s **\n", filename);
223 		return -1;
224 	}
225 
226 	if (len == 0)
227 		len = file_len;
228 
229 	len_read = ext4fs_read(buf, len);
230 
231 	return len_read;
232 }
233