xref: /rk3399_rockchip-uboot/fs/ext4/ext4fs.c (revision a1596438a68921d2c9b1fdec70a720d38c85ca7d)
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  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 2 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33 
34 #include <common.h>
35 #include <malloc.h>
36 #include <ext_common.h>
37 #include <ext4fs.h>
38 #include <linux/stat.h>
39 #include <linux/time.h>
40 #include <asm/byteorder.h>
41 #include "ext4_common.h"
42 
43 int ext4fs_symlinknest;
44 block_dev_desc_t *ext4_dev_desc;
45 
46 struct ext_filesystem *get_fs(void)
47 {
48 	if (ext4_dev_desc == NULL || ext4_dev_desc->priv == NULL)
49 		printf("Invalid Input Arguments %s\n", __func__);
50 
51 	return ext4_dev_desc->priv;
52 }
53 
54 int init_fs(block_dev_desc_t *dev_desc)
55 {
56 	struct ext_filesystem *fs;
57 	if (dev_desc == NULL) {
58 		printf("Invalid Input Arguments %s\n", __func__);
59 		return -EINVAL;
60 	}
61 
62 	fs = zalloc(sizeof(struct ext_filesystem));
63 	if (fs == NULL) {
64 		printf("malloc failed: %s\n", __func__);
65 		return -ENOMEM;
66 	}
67 
68 	fs->dev_desc = dev_desc;
69 	dev_desc->priv = fs;
70 
71 	return 0;
72 }
73 
74 void deinit_fs(block_dev_desc_t *dev_desc)
75 {
76 	if (dev_desc == NULL) {
77 		printf("Invalid Input Arguments %s\n", __func__);
78 		return;
79 	}
80 	free(dev_desc->priv);
81 	dev_desc->priv = NULL;
82 }
83 
84 void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
85 {
86 	if ((node != &ext4fs_root->diropen) && (node != currroot))
87 		free(node);
88 }
89 
90 /*
91  * Taken from openmoko-kernel mailing list: By Andy green
92  * Optimized read file API : collects and defers contiguous sector
93  * reads into one potentially more efficient larger sequential read action
94  */
95 int ext4fs_read_file(struct ext2fs_node *node, int pos,
96 		unsigned int len, char *buf)
97 {
98 	int i;
99 	int blockcnt;
100 	int log2blocksize = LOG2_EXT2_BLOCK_SIZE(node->data);
101 	int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS);
102 	unsigned int filesize = __le32_to_cpu(node->inode.size);
103 	int previous_block_number = -1;
104 	int delayed_start = 0;
105 	int delayed_extent = 0;
106 	int delayed_skipfirst = 0;
107 	int delayed_next = 0;
108 	char *delayed_buf = NULL;
109 	short status;
110 
111 	/* Adjust len so it we can't read past the end of the file. */
112 	if (len > filesize)
113 		len = filesize;
114 
115 	blockcnt = ((len + pos) + blocksize - 1) / blocksize;
116 
117 	for (i = pos / blocksize; i < blockcnt; i++) {
118 		int blknr;
119 		int blockoff = pos % blocksize;
120 		int blockend = blocksize;
121 		int skipfirst = 0;
122 		blknr = read_allocated_block(&(node->inode), i);
123 		if (blknr < 0)
124 			return -1;
125 
126 		blknr = blknr << log2blocksize;
127 
128 		/* Last block.  */
129 		if (i == blockcnt - 1) {
130 			blockend = (len + pos) % blocksize;
131 
132 			/* The last portion is exactly blocksize. */
133 			if (!blockend)
134 				blockend = blocksize;
135 		}
136 
137 		/* First block. */
138 		if (i == pos / blocksize) {
139 			skipfirst = blockoff;
140 			blockend -= skipfirst;
141 		}
142 		if (blknr) {
143 			int status;
144 
145 			if (previous_block_number != -1) {
146 				if (delayed_next == blknr) {
147 					delayed_extent += blockend;
148 					delayed_next += blockend >> SECTOR_BITS;
149 				} else {	/* spill */
150 					status = ext4fs_devread(delayed_start,
151 							delayed_skipfirst,
152 							delayed_extent,
153 							delayed_buf);
154 					if (status == 0)
155 						return -1;
156 					previous_block_number = blknr;
157 					delayed_start = blknr;
158 					delayed_extent = blockend;
159 					delayed_skipfirst = skipfirst;
160 					delayed_buf = buf;
161 					delayed_next = blknr +
162 						(blockend >> SECTOR_BITS);
163 				}
164 			} else {
165 				previous_block_number = blknr;
166 				delayed_start = blknr;
167 				delayed_extent = blockend;
168 				delayed_skipfirst = skipfirst;
169 				delayed_buf = buf;
170 				delayed_next = blknr +
171 					(blockend >> SECTOR_BITS);
172 			}
173 		} else {
174 			if (previous_block_number != -1) {
175 				/* spill */
176 				status = ext4fs_devread(delayed_start,
177 							delayed_skipfirst,
178 							delayed_extent,
179 							delayed_buf);
180 				if (status == 0)
181 					return -1;
182 				previous_block_number = -1;
183 			}
184 			memset(buf, 0, blocksize - skipfirst);
185 		}
186 		buf += blocksize - skipfirst;
187 	}
188 	if (previous_block_number != -1) {
189 		/* spill */
190 		status = ext4fs_devread(delayed_start,
191 					delayed_skipfirst, delayed_extent,
192 					delayed_buf);
193 		if (status == 0)
194 			return -1;
195 		previous_block_number = -1;
196 	}
197 
198 	return len;
199 }
200 
201 int ext4fs_ls(const char *dirname)
202 {
203 	struct ext2fs_node *dirnode;
204 	int status;
205 
206 	if (dirname == NULL)
207 		return 0;
208 
209 	status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
210 				  FILETYPE_DIRECTORY);
211 	if (status != 1) {
212 		printf("** Can not find directory. **\n");
213 		return 1;
214 	}
215 
216 	ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
217 	ext4fs_free_node(dirnode, &ext4fs_root->diropen);
218 
219 	return 0;
220 }
221 
222 int ext4fs_read(char *buf, unsigned len)
223 {
224 	if (ext4fs_root == NULL || ext4fs_file == NULL)
225 		return 0;
226 
227 	return ext4fs_read_file(ext4fs_file, 0, len, buf);
228 }
229