Merge tag 'efi-2020-07-rc6' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
[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 <blk.h>
25 #include <ext_common.h>
26 #include <ext4fs.h>
27 #include "ext4_common.h"
28 #include <div64.h>
29 #include <malloc.h>
30 #include <part.h>
31 #include <uuid.h>
32
33 int ext4fs_symlinknest;
34 struct ext_filesystem ext_fs;
35
36 struct ext_filesystem *get_fs(void)
37 {
38         return &ext_fs;
39 }
40
41 void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
42 {
43         if ((node != &ext4fs_root->diropen) && (node != currroot))
44                 free(node);
45 }
46
47 /*
48  * Taken from openmoko-kernel mailing list: By Andy green
49  * Optimized read file API : collects and defers contiguous sector
50  * reads into one potentially more efficient larger sequential read action
51  */
52 int ext4fs_read_file(struct ext2fs_node *node, loff_t pos,
53                 loff_t len, char *buf, loff_t *actread)
54 {
55         struct ext_filesystem *fs = get_fs();
56         int i;
57         lbaint_t blockcnt;
58         int log2blksz = fs->dev_desc->log2blksz;
59         int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz;
60         int blocksize = (1 << (log2_fs_blocksize + log2blksz));
61         unsigned int filesize = le32_to_cpu(node->inode.size);
62         lbaint_t previous_block_number = -1;
63         lbaint_t delayed_start = 0;
64         lbaint_t delayed_extent = 0;
65         lbaint_t delayed_skipfirst = 0;
66         lbaint_t delayed_next = 0;
67         char *delayed_buf = NULL;
68         char *start_buf = buf;
69         short status;
70         struct ext_block_cache cache;
71
72         ext_cache_init(&cache);
73
74         /* Adjust len so it we can't read past the end of the file. */
75         if (len + pos > filesize)
76                 len = (filesize - pos);
77
78         if (blocksize <= 0 || len <= 0) {
79                 ext_cache_fini(&cache);
80                 return -1;
81         }
82
83         blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
84
85         for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
86                 long int blknr;
87                 int blockoff = pos - (blocksize * i);
88                 int blockend = blocksize;
89                 int skipfirst = 0;
90                 blknr = read_allocated_block(&node->inode, i, &cache);
91                 if (blknr < 0) {
92                         ext_cache_fini(&cache);
93                         return -1;
94                 }
95
96                 blknr = blknr << log2_fs_blocksize;
97
98                 /* Last block.  */
99                 if (i == blockcnt - 1) {
100                         blockend = (len + pos) - (blocksize * i);
101
102                         /* The last portion is exactly blocksize. */
103                         if (!blockend)
104                                 blockend = blocksize;
105                 }
106
107                 /* First block. */
108                 if (i == lldiv(pos, blocksize)) {
109                         skipfirst = blockoff;
110                         blockend -= skipfirst;
111                 }
112                 if (blknr) {
113                         int status;
114
115                         if (previous_block_number != -1) {
116                                 if (delayed_next == blknr) {
117                                         delayed_extent += blockend;
118                                         delayed_next += blockend >> log2blksz;
119                                 } else {        /* spill */
120                                         status = ext4fs_devread(delayed_start,
121                                                         delayed_skipfirst,
122                                                         delayed_extent,
123                                                         delayed_buf);
124                                         if (status == 0) {
125                                                 ext_cache_fini(&cache);
126                                                 return -1;
127                                         }
128                                         previous_block_number = blknr;
129                                         delayed_start = blknr;
130                                         delayed_extent = blockend;
131                                         delayed_skipfirst = skipfirst;
132                                         delayed_buf = buf;
133                                         delayed_next = blknr +
134                                                 (blockend >> log2blksz);
135                                 }
136                         } else {
137                                 previous_block_number = blknr;
138                                 delayed_start = blknr;
139                                 delayed_extent = blockend;
140                                 delayed_skipfirst = skipfirst;
141                                 delayed_buf = buf;
142                                 delayed_next = blknr +
143                                         (blockend >> log2blksz);
144                         }
145                 } else {
146                         int n;
147                         int n_left;
148                         if (previous_block_number != -1) {
149                                 /* spill */
150                                 status = ext4fs_devread(delayed_start,
151                                                         delayed_skipfirst,
152                                                         delayed_extent,
153                                                         delayed_buf);
154                                 if (status == 0) {
155                                         ext_cache_fini(&cache);
156                                         return -1;
157                                 }
158                                 previous_block_number = -1;
159                         }
160                         /* Zero no more than `len' bytes. */
161                         n = blocksize - skipfirst;
162                         n_left = len - ( buf - start_buf );
163                         if (n > n_left)
164                                 n = n_left;
165                         memset(buf, 0, n);
166                 }
167                 buf += blocksize - skipfirst;
168         }
169         if (previous_block_number != -1) {
170                 /* spill */
171                 status = ext4fs_devread(delayed_start,
172                                         delayed_skipfirst, delayed_extent,
173                                         delayed_buf);
174                 if (status == 0) {
175                         ext_cache_fini(&cache);
176                         return -1;
177                 }
178                 previous_block_number = -1;
179         }
180
181         *actread  = len;
182         ext_cache_fini(&cache);
183         return 0;
184 }
185
186 int ext4fs_ls(const char *dirname)
187 {
188         struct ext2fs_node *dirnode = NULL;
189         int status;
190
191         if (dirname == NULL)
192                 return 0;
193
194         status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
195                                   FILETYPE_DIRECTORY);
196         if (status != 1) {
197                 printf("** Can not find directory. **\n");
198                 if (dirnode)
199                         ext4fs_free_node(dirnode, &ext4fs_root->diropen);
200                 return 1;
201         }
202
203         ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
204         ext4fs_free_node(dirnode, &ext4fs_root->diropen);
205
206         return 0;
207 }
208
209 int ext4fs_exists(const char *filename)
210 {
211         loff_t file_len;
212         int ret;
213
214         ret = ext4fs_open(filename, &file_len);
215         return ret == 0;
216 }
217
218 int ext4fs_size(const char *filename, loff_t *size)
219 {
220         return ext4fs_open(filename, size);
221 }
222
223 int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread)
224 {
225         if (ext4fs_root == NULL || ext4fs_file == NULL)
226                 return -1;
227
228         return ext4fs_read_file(ext4fs_file, offset, len, buf, actread);
229 }
230
231 int ext4fs_probe(struct blk_desc *fs_dev_desc,
232                  struct disk_partition *fs_partition)
233 {
234         ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
235
236         if (!ext4fs_mount(fs_partition->size)) {
237                 ext4fs_close();
238                 return -1;
239         }
240
241         return 0;
242 }
243
244 int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
245                    loff_t *len_read)
246 {
247         loff_t file_len;
248         int ret;
249
250         ret = ext4fs_open(filename, &file_len);
251         if (ret < 0) {
252                 printf("** File not found %s **\n", filename);
253                 return -1;
254         }
255
256         if (len == 0)
257                 len = file_len;
258
259         return ext4fs_read(buf, offset, len, len_read);
260 }
261
262 int ext4fs_uuid(char *uuid_str)
263 {
264         if (ext4fs_root == NULL)
265                 return -1;
266
267 #ifdef CONFIG_LIB_UUID
268         uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id,
269                         uuid_str, UUID_STR_FORMAT_STD);
270
271         return 0;
272 #else
273         return -ENOSYS;
274 #endif
275 }
276
277 void ext_cache_init(struct ext_block_cache *cache)
278 {
279         memset(cache, 0, sizeof(*cache));
280 }
281
282 void ext_cache_fini(struct ext_block_cache *cache)
283 {
284         free(cache->buf);
285         ext_cache_init(cache);
286 }
287
288 int ext_cache_read(struct ext_block_cache *cache, lbaint_t block, int size)
289 {
290         /* This could be more lenient, but this is simple and enough for now */
291         if (cache->buf && cache->block == block && cache->size == size)
292                 return 1;
293         ext_cache_fini(cache);
294         cache->buf = memalign(ARCH_DMA_MINALIGN, size);
295         if (!cache->buf)
296                 return 0;
297         if (!ext4fs_devread(block, 0, size, cache->buf)) {
298                 ext_cache_fini(cache);
299                 return 0;
300         }
301         cache->block = block;
302         cache->size = size;
303         return 1;
304 }