]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - fs/ext4/ext4fs.c
Merge branch 'u-boot-imx/master' into 'u-boot-arm/master'
[karo-tx-uboot.git] / fs / ext4 / ext4fs.c
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 }