183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2a1596438SUma Shankar /*
3a1596438SUma Shankar * (C) Copyright 2011 - 2012 Samsung Electronics
4a1596438SUma Shankar * EXT4 filesystem implementation in Uboot by
5a1596438SUma Shankar * Uma Shankar <uma.shankar@samsung.com>
6a1596438SUma Shankar * Manjunatha C Achar <a.manjunatha@samsung.com>
7a1596438SUma Shankar *
8a1596438SUma Shankar * ext4ls and ext4load : Based on ext2 ls and load support in Uboot.
9a1596438SUma Shankar * Ext4 read optimization taken from Open-Moko
10a1596438SUma Shankar * Qi bootloader
11a1596438SUma Shankar *
12a1596438SUma Shankar * (C) Copyright 2004
13a1596438SUma Shankar * esd gmbh <www.esd-electronics.com>
14a1596438SUma Shankar * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
15a1596438SUma Shankar *
16a1596438SUma Shankar * based on code from grub2 fs/ext2.c and fs/fshelp.c by
17a1596438SUma Shankar * GRUB -- GRand Unified Bootloader
18a1596438SUma Shankar * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
19a1596438SUma Shankar *
20ed34f34dSUma Shankar * ext4write : Based on generic ext4 protocol.
21a1596438SUma Shankar */
22a1596438SUma Shankar
23a1596438SUma Shankar #include <common.h>
24a1596438SUma Shankar #include <ext_common.h>
25a1596438SUma Shankar #include <ext4fs.h>
26a1596438SUma Shankar #include "ext4_common.h"
279e374e7bSTom Rini #include <div64.h>
28a1596438SUma Shankar
29a1596438SUma Shankar int ext4fs_symlinknest;
3094501062SRob Herring struct ext_filesystem ext_fs;
31a1596438SUma Shankar
get_fs(void)32a1596438SUma Shankar struct ext_filesystem *get_fs(void)
33a1596438SUma Shankar {
3494501062SRob Herring return &ext_fs;
35a1596438SUma Shankar }
36a1596438SUma Shankar
ext4fs_free_node(struct ext2fs_node * node,struct ext2fs_node * currroot)37a1596438SUma Shankar void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
38a1596438SUma Shankar {
39a1596438SUma Shankar if ((node != &ext4fs_root->diropen) && (node != currroot))
40a1596438SUma Shankar free(node);
41a1596438SUma Shankar }
42a1596438SUma Shankar
43a1596438SUma Shankar /*
44a1596438SUma Shankar * Taken from openmoko-kernel mailing list: By Andy green
45a1596438SUma Shankar * Optimized read file API : collects and defers contiguous sector
46a1596438SUma Shankar * reads into one potentially more efficient larger sequential read action
47a1596438SUma Shankar */
ext4fs_read_file(struct ext2fs_node * node,loff_t pos,loff_t len,char * buf,loff_t * actread)489f12cd0eSSuriyan Ramasami int ext4fs_read_file(struct ext2fs_node *node, loff_t pos,
499f12cd0eSSuriyan Ramasami loff_t len, char *buf, loff_t *actread)
50a1596438SUma Shankar {
5150ce4c07SEgbert Eich struct ext_filesystem *fs = get_fs();
52a1596438SUma Shankar int i;
5304735e9cSFrederic Leroy lbaint_t blockcnt;
5450ce4c07SEgbert Eich int log2blksz = fs->dev_desc->log2blksz;
5550ce4c07SEgbert Eich int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz;
5650ce4c07SEgbert Eich int blocksize = (1 << (log2_fs_blocksize + log2blksz));
577f101be3SMichael Walle unsigned int filesize = le32_to_cpu(node->inode.size);
5804735e9cSFrederic Leroy lbaint_t previous_block_number = -1;
5904735e9cSFrederic Leroy lbaint_t delayed_start = 0;
6004735e9cSFrederic Leroy lbaint_t delayed_extent = 0;
6104735e9cSFrederic Leroy lbaint_t delayed_skipfirst = 0;
6204735e9cSFrederic Leroy lbaint_t delayed_next = 0;
63a1596438SUma Shankar char *delayed_buf = NULL;
64a1596438SUma Shankar short status;
65a1596438SUma Shankar
66ecdfb419SIan Ray if (blocksize <= 0)
67ecdfb419SIan Ray return -1;
68ecdfb419SIan Ray
69a1596438SUma Shankar /* Adjust len so it we can't read past the end of the file. */
7066a47ff2SStefan Brüns if (len + pos > filesize)
7166a47ff2SStefan Brüns len = (filesize - pos);
72a1596438SUma Shankar
739e374e7bSTom Rini blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
74a1596438SUma Shankar
759e374e7bSTom Rini for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
76509b498aSLokesh Vutla long int blknr;
779e374e7bSTom Rini int blockoff = pos - (blocksize * i);
78a1596438SUma Shankar int blockend = blocksize;
79a1596438SUma Shankar int skipfirst = 0;
80a1596438SUma Shankar blknr = read_allocated_block(&(node->inode), i);
81715b56feSTom Rini if (blknr < 0)
82715b56feSTom Rini return -1;
83a1596438SUma Shankar
8450ce4c07SEgbert Eich blknr = blknr << log2_fs_blocksize;
85a1596438SUma Shankar
86a1596438SUma Shankar /* Last block. */
87a1596438SUma Shankar if (i == blockcnt - 1) {
889e374e7bSTom Rini blockend = (len + pos) - (blocksize * i);
89a1596438SUma Shankar
90a1596438SUma Shankar /* The last portion is exactly blocksize. */
91a1596438SUma Shankar if (!blockend)
92a1596438SUma Shankar blockend = blocksize;
93a1596438SUma Shankar }
94a1596438SUma Shankar
95a1596438SUma Shankar /* First block. */
969e374e7bSTom Rini if (i == lldiv(pos, blocksize)) {
97a1596438SUma Shankar skipfirst = blockoff;
98a1596438SUma Shankar blockend -= skipfirst;
99a1596438SUma Shankar }
100a1596438SUma Shankar if (blknr) {
101a1596438SUma Shankar int status;
102a1596438SUma Shankar
103a1596438SUma Shankar if (previous_block_number != -1) {
104a1596438SUma Shankar if (delayed_next == blknr) {
105a1596438SUma Shankar delayed_extent += blockend;
10650ce4c07SEgbert Eich delayed_next += blockend >> log2blksz;
107a1596438SUma Shankar } else { /* spill */
108a1596438SUma Shankar status = ext4fs_devread(delayed_start,
109a1596438SUma Shankar delayed_skipfirst,
110a1596438SUma Shankar delayed_extent,
111a1596438SUma Shankar delayed_buf);
112715b56feSTom Rini if (status == 0)
113715b56feSTom Rini return -1;
114a1596438SUma Shankar previous_block_number = blknr;
115a1596438SUma Shankar delayed_start = blknr;
116a1596438SUma Shankar delayed_extent = blockend;
117a1596438SUma Shankar delayed_skipfirst = skipfirst;
118a1596438SUma Shankar delayed_buf = buf;
119a1596438SUma Shankar delayed_next = blknr +
12050ce4c07SEgbert Eich (blockend >> log2blksz);
121a1596438SUma Shankar }
122a1596438SUma Shankar } else {
123a1596438SUma Shankar previous_block_number = blknr;
124a1596438SUma Shankar delayed_start = blknr;
125a1596438SUma Shankar delayed_extent = blockend;
126a1596438SUma Shankar delayed_skipfirst = skipfirst;
127a1596438SUma Shankar delayed_buf = buf;
128a1596438SUma Shankar delayed_next = blknr +
12950ce4c07SEgbert Eich (blockend >> log2blksz);
130a1596438SUma Shankar }
131a1596438SUma Shankar } else {
132ecdfb419SIan Ray int n;
133a1596438SUma Shankar if (previous_block_number != -1) {
134a1596438SUma Shankar /* spill */
135a1596438SUma Shankar status = ext4fs_devread(delayed_start,
136a1596438SUma Shankar delayed_skipfirst,
137a1596438SUma Shankar delayed_extent,
138a1596438SUma Shankar delayed_buf);
139715b56feSTom Rini if (status == 0)
140715b56feSTom Rini return -1;
141a1596438SUma Shankar previous_block_number = -1;
142a1596438SUma Shankar }
143ecdfb419SIan Ray /* Zero no more than `len' bytes. */
144ecdfb419SIan Ray n = blocksize - skipfirst;
145ecdfb419SIan Ray if (n > len)
146ecdfb419SIan Ray n = len;
147ecdfb419SIan Ray memset(buf, 0, n);
148a1596438SUma Shankar }
149a1596438SUma Shankar buf += blocksize - skipfirst;
150a1596438SUma Shankar }
151a1596438SUma Shankar if (previous_block_number != -1) {
152a1596438SUma Shankar /* spill */
153a1596438SUma Shankar status = ext4fs_devread(delayed_start,
154a1596438SUma Shankar delayed_skipfirst, delayed_extent,
155a1596438SUma Shankar delayed_buf);
156715b56feSTom Rini if (status == 0)
157715b56feSTom Rini return -1;
158a1596438SUma Shankar previous_block_number = -1;
159a1596438SUma Shankar }
160a1596438SUma Shankar
1619f12cd0eSSuriyan Ramasami *actread = len;
1629f12cd0eSSuriyan Ramasami return 0;
163a1596438SUma Shankar }
164a1596438SUma Shankar
ext4fs_ls(const char * dirname)165a1596438SUma Shankar int ext4fs_ls(const char *dirname)
166a1596438SUma Shankar {
167*e71a969cSEugen Hristev struct ext2fs_node *dirnode = NULL;
168a1596438SUma Shankar int status;
169a1596438SUma Shankar
170a1596438SUma Shankar if (dirname == NULL)
171a1596438SUma Shankar return 0;
172a1596438SUma Shankar
173a1596438SUma Shankar status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
174a1596438SUma Shankar FILETYPE_DIRECTORY);
175a1596438SUma Shankar if (status != 1) {
176a1596438SUma Shankar printf("** Can not find directory. **\n");
177*e71a969cSEugen Hristev if (dirnode)
178fa9ca8a5STom Rini ext4fs_free_node(dirnode, &ext4fs_root->diropen);
179a1596438SUma Shankar return 1;
180a1596438SUma Shankar }
181a1596438SUma Shankar
182a1596438SUma Shankar ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
183a1596438SUma Shankar ext4fs_free_node(dirnode, &ext4fs_root->diropen);
184a1596438SUma Shankar
185a1596438SUma Shankar return 0;
186a1596438SUma Shankar }
187a1596438SUma Shankar
ext4fs_exists(const char * filename)18855af5c93SStephen Warren int ext4fs_exists(const char *filename)
18955af5c93SStephen Warren {
1909f12cd0eSSuriyan Ramasami loff_t file_len;
1919f12cd0eSSuriyan Ramasami int ret;
19255af5c93SStephen Warren
1939f12cd0eSSuriyan Ramasami ret = ext4fs_open(filename, &file_len);
1949f12cd0eSSuriyan Ramasami return ret == 0;
19555af5c93SStephen Warren }
19655af5c93SStephen Warren
ext4fs_size(const char * filename,loff_t * size)197d455d878SSuriyan Ramasami int ext4fs_size(const char *filename, loff_t *size)
198cf659819SStephen Warren {
199d455d878SSuriyan Ramasami return ext4fs_open(filename, size);
200cf659819SStephen Warren }
201cf659819SStephen Warren
ext4fs_read(char * buf,loff_t offset,loff_t len,loff_t * actread)20266a47ff2SStefan Brüns int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread)
203a1596438SUma Shankar {
204a1596438SUma Shankar if (ext4fs_root == NULL || ext4fs_file == NULL)
20566a47ff2SStefan Brüns return -1;
206a1596438SUma Shankar
20766a47ff2SStefan Brüns return ext4fs_read_file(ext4fs_file, offset, len, buf, actread);
208a1596438SUma Shankar }
209e6d52415SSimon Glass
ext4fs_probe(struct blk_desc * fs_dev_desc,disk_partition_t * fs_partition)2104101f687SSimon Glass int ext4fs_probe(struct blk_desc *fs_dev_desc,
211e6d52415SSimon Glass disk_partition_t *fs_partition)
212e6d52415SSimon Glass {
213e6d52415SSimon Glass ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
214e6d52415SSimon Glass
215e6d52415SSimon Glass if (!ext4fs_mount(fs_partition->size)) {
216e6d52415SSimon Glass ext4fs_close();
217e6d52415SSimon Glass return -1;
218e6d52415SSimon Glass }
219e6d52415SSimon Glass
220e6d52415SSimon Glass return 0;
221e6d52415SSimon Glass }
222e6d52415SSimon Glass
ext4_read_file(const char * filename,void * buf,loff_t offset,loff_t len,loff_t * len_read)223d455d878SSuriyan Ramasami int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
224d455d878SSuriyan Ramasami loff_t *len_read)
225e6d52415SSimon Glass {
2269f12cd0eSSuriyan Ramasami loff_t file_len;
2279f12cd0eSSuriyan Ramasami int ret;
228e6d52415SSimon Glass
2299f12cd0eSSuriyan Ramasami ret = ext4fs_open(filename, &file_len);
2309f12cd0eSSuriyan Ramasami if (ret < 0) {
231e6d52415SSimon Glass printf("** File not found %s **\n", filename);
232e6d52415SSimon Glass return -1;
233e6d52415SSimon Glass }
234e6d52415SSimon Glass
235e6d52415SSimon Glass if (len == 0)
236e6d52415SSimon Glass len = file_len;
237e6d52415SSimon Glass
23866a47ff2SStefan Brüns return ext4fs_read(buf, offset, len, len_read);
239e6d52415SSimon Glass }
24059e890efSChristian Gmeiner
ext4fs_uuid(char * uuid_str)24159e890efSChristian Gmeiner int ext4fs_uuid(char *uuid_str)
24259e890efSChristian Gmeiner {
24359e890efSChristian Gmeiner if (ext4fs_root == NULL)
24459e890efSChristian Gmeiner return -1;
24559e890efSChristian Gmeiner
24659e890efSChristian Gmeiner #ifdef CONFIG_LIB_UUID
24759e890efSChristian Gmeiner uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id,
24859e890efSChristian Gmeiner uuid_str, UUID_STR_FORMAT_STD);
24959e890efSChristian Gmeiner
25059e890efSChristian Gmeiner return 0;
25159e890efSChristian Gmeiner #else
25259e890efSChristian Gmeiner return -ENOSYS;
25359e890efSChristian Gmeiner #endif
25459e890efSChristian Gmeiner }
255