xref: /openbmc/u-boot/fs/ext4/ext4fs.c (revision cf0bcd7d)
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  * SPDX-License-Identifier:	GPL-2.0+
22  */
23 
24 #include <common.h>
25 #include <ext_common.h>
26 #include <ext4fs.h>
27 #include "ext4_common.h"
28 #include <div64.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 	short status;
66 
67 	if (blocksize <= 0)
68 		return -1;
69 
70 	/* Adjust len so it we can't read past the end of the file. */
71 	if (len + pos > filesize)
72 		len = (filesize - pos);
73 
74 	blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
75 
76 	for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
77 		long int blknr;
78 		int blockoff = pos - (blocksize * i);
79 		int blockend = blocksize;
80 		int skipfirst = 0;
81 		blknr = read_allocated_block(&(node->inode), i);
82 		if (blknr < 0)
83 			return -1;
84 
85 		blknr = blknr << log2_fs_blocksize;
86 
87 		/* Last block.  */
88 		if (i == blockcnt - 1) {
89 			blockend = (len + pos) - (blocksize * i);
90 
91 			/* The last portion is exactly blocksize. */
92 			if (!blockend)
93 				blockend = blocksize;
94 		}
95 
96 		/* First block. */
97 		if (i == lldiv(pos, blocksize)) {
98 			skipfirst = blockoff;
99 			blockend -= skipfirst;
100 		}
101 		if (blknr) {
102 			int status;
103 
104 			if (previous_block_number != -1) {
105 				if (delayed_next == blknr) {
106 					delayed_extent += blockend;
107 					delayed_next += blockend >> log2blksz;
108 				} else {	/* spill */
109 					status = ext4fs_devread(delayed_start,
110 							delayed_skipfirst,
111 							delayed_extent,
112 							delayed_buf);
113 					if (status == 0)
114 						return -1;
115 					previous_block_number = blknr;
116 					delayed_start = blknr;
117 					delayed_extent = blockend;
118 					delayed_skipfirst = skipfirst;
119 					delayed_buf = buf;
120 					delayed_next = blknr +
121 						(blockend >> log2blksz);
122 				}
123 			} else {
124 				previous_block_number = blknr;
125 				delayed_start = blknr;
126 				delayed_extent = blockend;
127 				delayed_skipfirst = skipfirst;
128 				delayed_buf = buf;
129 				delayed_next = blknr +
130 					(blockend >> log2blksz);
131 			}
132 		} else {
133 			int n;
134 			if (previous_block_number != -1) {
135 				/* spill */
136 				status = ext4fs_devread(delayed_start,
137 							delayed_skipfirst,
138 							delayed_extent,
139 							delayed_buf);
140 				if (status == 0)
141 					return -1;
142 				previous_block_number = -1;
143 			}
144 			/* Zero no more than `len' bytes. */
145 			n = blocksize - skipfirst;
146 			if (n > len)
147 				n = len;
148 			memset(buf, 0, n);
149 		}
150 		buf += blocksize - skipfirst;
151 	}
152 	if (previous_block_number != -1) {
153 		/* spill */
154 		status = ext4fs_devread(delayed_start,
155 					delayed_skipfirst, delayed_extent,
156 					delayed_buf);
157 		if (status == 0)
158 			return -1;
159 		previous_block_number = -1;
160 	}
161 
162 	*actread  = len;
163 	return 0;
164 }
165 
166 int ext4fs_ls(const char *dirname)
167 {
168 	struct ext2fs_node *dirnode;
169 	int status;
170 
171 	if (dirname == NULL)
172 		return 0;
173 
174 	status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
175 				  FILETYPE_DIRECTORY);
176 	if (status != 1) {
177 		printf("** Can not find directory. **\n");
178 		ext4fs_free_node(dirnode, &ext4fs_root->diropen);
179 		return 1;
180 	}
181 
182 	ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
183 	ext4fs_free_node(dirnode, &ext4fs_root->diropen);
184 
185 	return 0;
186 }
187 
188 int ext4fs_exists(const char *filename)
189 {
190 	loff_t file_len;
191 	int ret;
192 
193 	ret = ext4fs_open(filename, &file_len);
194 	return ret == 0;
195 }
196 
197 int ext4fs_size(const char *filename, loff_t *size)
198 {
199 	return ext4fs_open(filename, size);
200 }
201 
202 int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread)
203 {
204 	if (ext4fs_root == NULL || ext4fs_file == NULL)
205 		return -1;
206 
207 	return ext4fs_read_file(ext4fs_file, offset, len, buf, actread);
208 }
209 
210 int ext4fs_probe(struct blk_desc *fs_dev_desc,
211 		 disk_partition_t *fs_partition)
212 {
213 	ext4fs_set_blk_dev(fs_dev_desc, fs_partition);
214 
215 	if (!ext4fs_mount(fs_partition->size)) {
216 		ext4fs_close();
217 		return -1;
218 	}
219 
220 	return 0;
221 }
222 
223 int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
224 		   loff_t *len_read)
225 {
226 	loff_t file_len;
227 	int ret;
228 
229 	ret = ext4fs_open(filename, &file_len);
230 	if (ret < 0) {
231 		printf("** File not found %s **\n", filename);
232 		return -1;
233 	}
234 
235 	if (len == 0)
236 		len = file_len;
237 
238 	return ext4fs_read(buf, offset, len, len_read);
239 }
240 
241 int ext4fs_uuid(char *uuid_str)
242 {
243 	if (ext4fs_root == NULL)
244 		return -1;
245 
246 #ifdef CONFIG_LIB_UUID
247 	uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id,
248 			uuid_str, UUID_STR_FORMAT_STD);
249 
250 	return 0;
251 #else
252 	return -ENOSYS;
253 #endif
254 }
255