xref: /openbmc/u-boot/fs/ext4/ext4fs.c (revision ecdfb419)
1a1596438SUma Shankar /*
2a1596438SUma Shankar  * (C) Copyright 2011 - 2012 Samsung Electronics
3a1596438SUma Shankar  * EXT4 filesystem implementation in Uboot by
4a1596438SUma Shankar  * Uma Shankar <uma.shankar@samsung.com>
5a1596438SUma Shankar  * Manjunatha C Achar <a.manjunatha@samsung.com>
6a1596438SUma Shankar  *
7a1596438SUma Shankar  * ext4ls and ext4load : Based on ext2 ls and load support in Uboot.
8a1596438SUma Shankar  *		       Ext4 read optimization taken from Open-Moko
9a1596438SUma Shankar  *		       Qi bootloader
10a1596438SUma Shankar  *
11a1596438SUma Shankar  * (C) Copyright 2004
12a1596438SUma Shankar  * esd gmbh <www.esd-electronics.com>
13a1596438SUma Shankar  * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
14a1596438SUma Shankar  *
15a1596438SUma Shankar  * based on code from grub2 fs/ext2.c and fs/fshelp.c by
16a1596438SUma Shankar  * GRUB  --  GRand Unified Bootloader
17a1596438SUma Shankar  * Copyright (C) 2003, 2004  Free Software Foundation, Inc.
18a1596438SUma Shankar  *
19ed34f34dSUma Shankar  * ext4write : Based on generic ext4 protocol.
20ed34f34dSUma Shankar  *
211a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
22a1596438SUma Shankar  */
23a1596438SUma Shankar 
24a1596438SUma Shankar #include <common.h>
25a1596438SUma Shankar #include <ext_common.h>
26a1596438SUma Shankar #include <ext4fs.h>
27a1596438SUma Shankar #include "ext4_common.h"
289e374e7bSTom Rini #include <div64.h>
29a1596438SUma Shankar 
30a1596438SUma Shankar int ext4fs_symlinknest;
3194501062SRob Herring struct ext_filesystem ext_fs;
32a1596438SUma Shankar 
33a1596438SUma Shankar struct ext_filesystem *get_fs(void)
34a1596438SUma Shankar {
3594501062SRob Herring 	return &ext_fs;
36a1596438SUma Shankar }
37a1596438SUma Shankar 
38a1596438SUma Shankar void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
39a1596438SUma Shankar {
40a1596438SUma Shankar 	if ((node != &ext4fs_root->diropen) && (node != currroot))
41a1596438SUma Shankar 		free(node);
42a1596438SUma Shankar }
43a1596438SUma Shankar 
44a1596438SUma Shankar /*
45a1596438SUma Shankar  * Taken from openmoko-kernel mailing list: By Andy green
46a1596438SUma Shankar  * Optimized read file API : collects and defers contiguous sector
47a1596438SUma Shankar  * reads into one potentially more efficient larger sequential read action
48a1596438SUma Shankar  */
499f12cd0eSSuriyan Ramasami int ext4fs_read_file(struct ext2fs_node *node, loff_t pos,
509f12cd0eSSuriyan Ramasami 		loff_t len, char *buf, loff_t *actread)
51a1596438SUma Shankar {
5250ce4c07SEgbert Eich 	struct ext_filesystem *fs = get_fs();
53a1596438SUma Shankar 	int i;
5404735e9cSFrederic Leroy 	lbaint_t blockcnt;
5550ce4c07SEgbert Eich 	int log2blksz = fs->dev_desc->log2blksz;
5650ce4c07SEgbert Eich 	int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz;
5750ce4c07SEgbert Eich 	int blocksize = (1 << (log2_fs_blocksize + log2blksz));
587f101be3SMichael Walle 	unsigned int filesize = le32_to_cpu(node->inode.size);
5904735e9cSFrederic Leroy 	lbaint_t previous_block_number = -1;
6004735e9cSFrederic Leroy 	lbaint_t delayed_start = 0;
6104735e9cSFrederic Leroy 	lbaint_t delayed_extent = 0;
6204735e9cSFrederic Leroy 	lbaint_t delayed_skipfirst = 0;
6304735e9cSFrederic Leroy 	lbaint_t delayed_next = 0;
64a1596438SUma Shankar 	char *delayed_buf = NULL;
65a1596438SUma Shankar 	short status;
66a1596438SUma Shankar 
67*ecdfb419SIan Ray 	if (blocksize <= 0)
68*ecdfb419SIan Ray 		return -1;
69*ecdfb419SIan Ray 
70a1596438SUma Shankar 	/* Adjust len so it we can't read past the end of the file. */
7166a47ff2SStefan Brüns 	if (len + pos > filesize)
7266a47ff2SStefan Brüns 		len = (filesize - pos);
73a1596438SUma Shankar 
749e374e7bSTom Rini 	blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
75a1596438SUma Shankar 
769e374e7bSTom Rini 	for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
77509b498aSLokesh Vutla 		long int blknr;
789e374e7bSTom Rini 		int blockoff = pos - (blocksize * i);
79a1596438SUma Shankar 		int blockend = blocksize;
80a1596438SUma Shankar 		int skipfirst = 0;
81a1596438SUma Shankar 		blknr = read_allocated_block(&(node->inode), i);
82715b56feSTom Rini 		if (blknr < 0)
83715b56feSTom Rini 			return -1;
84a1596438SUma Shankar 
8550ce4c07SEgbert Eich 		blknr = blknr << log2_fs_blocksize;
86a1596438SUma Shankar 
87a1596438SUma Shankar 		/* Last block.  */
88a1596438SUma Shankar 		if (i == blockcnt - 1) {
899e374e7bSTom Rini 			blockend = (len + pos) - (blocksize * i);
90a1596438SUma Shankar 
91a1596438SUma Shankar 			/* The last portion is exactly blocksize. */
92a1596438SUma Shankar 			if (!blockend)
93a1596438SUma Shankar 				blockend = blocksize;
94a1596438SUma Shankar 		}
95a1596438SUma Shankar 
96a1596438SUma Shankar 		/* First block. */
979e374e7bSTom Rini 		if (i == lldiv(pos, blocksize)) {
98a1596438SUma Shankar 			skipfirst = blockoff;
99a1596438SUma Shankar 			blockend -= skipfirst;
100a1596438SUma Shankar 		}
101a1596438SUma Shankar 		if (blknr) {
102a1596438SUma Shankar 			int status;
103a1596438SUma Shankar 
104a1596438SUma Shankar 			if (previous_block_number != -1) {
105a1596438SUma Shankar 				if (delayed_next == blknr) {
106a1596438SUma Shankar 					delayed_extent += blockend;
10750ce4c07SEgbert Eich 					delayed_next += blockend >> log2blksz;
108a1596438SUma Shankar 				} else {	/* spill */
109a1596438SUma Shankar 					status = ext4fs_devread(delayed_start,
110a1596438SUma Shankar 							delayed_skipfirst,
111a1596438SUma Shankar 							delayed_extent,
112a1596438SUma Shankar 							delayed_buf);
113715b56feSTom Rini 					if (status == 0)
114715b56feSTom Rini 						return -1;
115a1596438SUma Shankar 					previous_block_number = blknr;
116a1596438SUma Shankar 					delayed_start = blknr;
117a1596438SUma Shankar 					delayed_extent = blockend;
118a1596438SUma Shankar 					delayed_skipfirst = skipfirst;
119a1596438SUma Shankar 					delayed_buf = buf;
120a1596438SUma Shankar 					delayed_next = blknr +
12150ce4c07SEgbert Eich 						(blockend >> log2blksz);
122a1596438SUma Shankar 				}
123a1596438SUma Shankar 			} else {
124a1596438SUma Shankar 				previous_block_number = blknr;
125a1596438SUma Shankar 				delayed_start = blknr;
126a1596438SUma Shankar 				delayed_extent = blockend;
127a1596438SUma Shankar 				delayed_skipfirst = skipfirst;
128a1596438SUma Shankar 				delayed_buf = buf;
129a1596438SUma Shankar 				delayed_next = blknr +
13050ce4c07SEgbert Eich 					(blockend >> log2blksz);
131a1596438SUma Shankar 			}
132a1596438SUma Shankar 		} else {
133*ecdfb419SIan Ray 			int n;
134a1596438SUma Shankar 			if (previous_block_number != -1) {
135a1596438SUma Shankar 				/* spill */
136a1596438SUma Shankar 				status = ext4fs_devread(delayed_start,
137a1596438SUma Shankar 							delayed_skipfirst,
138a1596438SUma Shankar 							delayed_extent,
139a1596438SUma Shankar 							delayed_buf);
140715b56feSTom Rini 				if (status == 0)
141715b56feSTom Rini 					return -1;
142a1596438SUma Shankar 				previous_block_number = -1;
143a1596438SUma Shankar 			}
144*ecdfb419SIan Ray 			/* Zero no more than `len' bytes. */
145*ecdfb419SIan Ray 			n = blocksize - skipfirst;
146*ecdfb419SIan Ray 			if (n > len)
147*ecdfb419SIan Ray 				n = len;
148*ecdfb419SIan Ray 			memset(buf, 0, n);
149a1596438SUma Shankar 		}
150a1596438SUma Shankar 		buf += blocksize - skipfirst;
151a1596438SUma Shankar 	}
152a1596438SUma Shankar 	if (previous_block_number != -1) {
153a1596438SUma Shankar 		/* spill */
154a1596438SUma Shankar 		status = ext4fs_devread(delayed_start,
155a1596438SUma Shankar 					delayed_skipfirst, delayed_extent,
156a1596438SUma Shankar 					delayed_buf);
157715b56feSTom Rini 		if (status == 0)
158715b56feSTom Rini 			return -1;
159a1596438SUma Shankar 		previous_block_number = -1;
160a1596438SUma Shankar 	}
161a1596438SUma Shankar 
1629f12cd0eSSuriyan Ramasami 	*actread  = len;
1639f12cd0eSSuriyan Ramasami 	return 0;
164a1596438SUma Shankar }
165a1596438SUma Shankar 
166a1596438SUma Shankar int ext4fs_ls(const char *dirname)
167a1596438SUma Shankar {
168a1596438SUma Shankar 	struct ext2fs_node *dirnode;
169a1596438SUma Shankar 	int status;
170a1596438SUma Shankar 
171a1596438SUma Shankar 	if (dirname == NULL)
172a1596438SUma Shankar 		return 0;
173a1596438SUma Shankar 
174a1596438SUma Shankar 	status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
175a1596438SUma Shankar 				  FILETYPE_DIRECTORY);
176a1596438SUma Shankar 	if (status != 1) {
177a1596438SUma Shankar 		printf("** Can not find directory. **\n");
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 
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 
197d455d878SSuriyan Ramasami int ext4fs_size(const char *filename, loff_t *size)
198cf659819SStephen Warren {
199d455d878SSuriyan Ramasami 	return ext4fs_open(filename, size);
200cf659819SStephen Warren }
201cf659819SStephen Warren 
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 
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 
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 
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