1c616a26a272ba7c15736b317bbd9f50a2524dbe
[oweals/u-boot.git] / fs / ext4 / ext4fs.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2011 - 2012 Samsung Electronics
4  * EXT4 filesystem implementation in Uboot by
5  * Uma Shankar <uma.shankar@samsung.com>
6  * Manjunatha C Achar <a.manjunatha@samsung.com>
7  *
8  * ext4ls and ext4load : Based on ext2 ls and load support in Uboot.
9  *                     Ext4 read optimization taken from Open-Moko
10  *                     Qi bootloader
11  *
12  * (C) Copyright 2004
13  * esd gmbh <www.esd-electronics.com>
14  * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
15  *
16  * based on code from grub2 fs/ext2.c and fs/fshelp.c by
17  * GRUB  --  GRand Unified Bootloader
18  * Copyright (C) 2003, 2004  Free Software Foundation, Inc.
19  *
20  * ext4write : Based on generic ext4 protocol.
21  */
22
23 #include <common.h>
24 #include <ext_common.h>
25 #include <ext4fs.h>
26 #include "ext4_common.h"
27 #include <div64.h>
28 #include <malloc.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         char *start_buf = buf;
66         short status;
67         struct ext_block_cache cache;
68
69         ext_cache_init(&cache);
70
71         /* Adjust len so it we can't read past the end of the file. */
72         if (len + pos > filesize)
73                 len = (filesize - pos);
74
75         if (blocksize <= 0 || len <= 0) {
76                 ext_cache_fini(&cache);
77                 return -1;
78         }
79
80         blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
81
82         for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
83                 long int blknr;
84                 int blockoff = pos - (blocksize * i);
85                 int blockend = blocksize;
86                 int skipfirst = 0;
87                 blknr = read_allocated_block(&node->inode, i, &cache);
88                 if (blknr < 0) {
89                         ext_cache_fini(&cache);
90                         return -1;
91                 }
92
93                 blknr = blknr << log2_fs_blocksize;
94
95                 /* Last block.  */
96                 if (i == blockcnt - 1) {
97                         blockend = (len + pos) - (blocksize * i);
98
99                         /* The last portion is exactly blocksize. */
100                         if (!blockend)
101                                 blockend = blocksize;
102                 }
103
104                 /* First block. */
105                 if (i == lldiv(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                                                 ext_cache_fini(&cache);
123                                                 return -1;
124                                         }
125                                         previous_block_number = blknr;
126                                         delayed_start = blknr;
127                                         delayed_extent = blockend;
128                                         delayed_skipfirst = skipfirst;
129                                         delayed_buf = buf;
130                                         delayed_next = blknr +
131                                                 (blockend >> log2blksz);
132                                 }
133                         } else {
134                                 previous_block_number = blknr;
135                                 delayed_start = blknr;
136                                 delayed_extent = blockend;
137                                 delayed_skipfirst = skipfirst;
138                                 delayed_buf = buf;
139                                 delayed_next = blknr +
140                                         (blockend >> log2blksz);
141                         }
142                 } else {
143                         int n;
144                         int n_left;
145                         if (previous_block_number != -1) {
146                                 /* spill */
147                                 status = ext4fs_devread(delayed_start,
148                                                         delayed_skipfirst,
149                                                         delayed_extent,
150                                                         delayed_buf);
151                                 if (status == 0) {
152                                         ext_cache_fini(&cache);
153                                         return -1;
154                                 }
155                                 previous_block_number = -1;
156                         }
157                         /* Zero no more than `len' bytes. */
158                         n = blocksize - skipfirst;
159                         n_left = len - ( buf - start_buf );
160                         if (n > n_left)
161                                 n = n_left;
162                         memset(buf, 0, n);
163                 }
164                 buf += blocksize - skipfirst;
165         }
166         if (previous_block_number != -1) {
167                 /* spill */
168                 status = ext4fs_devread(delayed_start,
169                                         delayed_skipfirst, delayed_extent,
170                                         delayed_buf);
171                 if (status == 0) {
172                         ext_cache_fini(&cache);
173                         return -1;
174                 }
175                 previous_block_number = -1;
176         }
177
178         *actread  = len;
179         ext_cache_fini(&cache);
180         return 0;
181 }
182
183 int ext4fs_ls(const char *dirname)
184 {
185         struct ext2fs_node *dirnode = NULL;
186         int status;
187
188         if (dirname == NULL)
189                 return 0;
190
191         status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
192                                   FILETYPE_DIRECTORY);
193         if (status != 1) {
194                 printf("** Can not find directory. **\n");
195                 if (dirnode)
196                         ext4fs_free_node(dirnode, &ext4fs_root->diropen);
197                 return 1;
198         }
199
200         ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
201         ext4fs_free_node(dirnode, &ext4fs_root->diropen);
202
203         return 0;
204 }
205
206 int ext4fs_exists(const char *filename)
207 {
208         loff_t file_len;
209         int ret;
210
211         ret = ext4fs_open(filename, &file_len);
212         return ret == 0;
213 }
214
215 int ext4fs_size(const char *filename, loff_t *size)
216 {
217         return ext4fs_open(filename, size);
218 }
219
220 int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread)
221 {
222         if (ext4fs_root == NULL || ext4fs_file == NULL)
223                 return -1;
224
225         return ext4fs_read_file(ext4fs_file, offset, len, buf, actread);
226 }
227
228 int ext4fs_probe(struct blk_desc *fs_dev_desc,
229                  disk_partition_t *fs_partition)
230 {
231         ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
232
233         if (!ext4fs_mount(fs_partition->size)) {
234                 ext4fs_close();
235                 return -1;
236         }
237
238         return 0;
239 }
240
241 int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
242                    loff_t *len_read)
243 {
244         loff_t file_len;
245         int ret;
246
247         ret = ext4fs_open(filename, &file_len);
248         if (ret < 0) {
249                 printf("** File not found %s **\n", filename);
250                 return -1;
251         }
252
253         if (len == 0)
254                 len = file_len;
255
256         return ext4fs_read(buf, offset, len, len_read);
257 }
258
259 int ext4fs_uuid(char *uuid_str)
260 {
261         if (ext4fs_root == NULL)
262                 return -1;
263
264 #ifdef CONFIG_LIB_UUID
265         uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id,
266                         uuid_str, UUID_STR_FORMAT_STD);
267
268         return 0;
269 #else
270         return -ENOSYS;
271 #endif
272 }
273
274 void ext_cache_init(struct ext_block_cache *cache)
275 {
276         memset(cache, 0, sizeof(*cache));
277 }
278
279 void ext_cache_fini(struct ext_block_cache *cache)
280 {
281         free(cache->buf);
282         ext_cache_init(cache);
283 }
284
285 int ext_cache_read(struct ext_block_cache *cache, lbaint_t block, int size)
286 {
287         /* This could be more lenient, but this is simple and enough for now */
288         if (cache->buf && cache->block == block && cache->size == size)
289                 return 1;
290         ext_cache_fini(cache);
291         cache->buf = malloc(size);
292         if (!cache->buf)
293                 return 0;
294         if (!ext4fs_devread(block, 0, size, cache->buf)) {
295                 ext_cache_fini(cache);
296                 return 0;
297         }
298         cache->block = block;
299         cache->size = size;
300         return 1;
301 }