ext4fs.c 5.55 KB
Newer Older
Uma Shankar committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * (C) Copyright 2011 - 2012 Samsung Electronics
 * EXT4 filesystem implementation in Uboot by
 * Uma Shankar <uma.shankar@samsung.com>
 * Manjunatha C Achar <a.manjunatha@samsung.com>
 *
 * ext4ls and ext4load : Based on ext2 ls and load support in Uboot.
 *		       Ext4 read optimization taken from Open-Moko
 *		       Qi bootloader
 *
 * (C) Copyright 2004
 * esd gmbh <www.esd-electronics.com>
 * Reinhard Arlt <reinhard.arlt@esd-electronics.com>
 *
 * based on code from grub2 fs/ext2.c and fs/fshelp.c by
 * GRUB  --  GRand Unified Bootloader
 * Copyright (C) 2003, 2004  Free Software Foundation, Inc.
 *
Uma Shankar committed
19 20
 * ext4write : Based on generic ext4 protocol.
 *
21
 * SPDX-License-Identifier:	GPL-2.0+
Uma Shankar committed
22 23 24 25 26 27
 */

#include <common.h>
#include <ext_common.h>
#include <ext4fs.h>
#include "ext4_common.h"
28
#include <div64.h>
Uma Shankar committed
29 30

int ext4fs_symlinknest;
31
struct ext_filesystem ext_fs;
Uma Shankar committed
32 33 34

struct ext_filesystem *get_fs(void)
{
35
	return &ext_fs;
Uma Shankar committed
36 37 38 39 40 41 42 43 44 45 46 47 48
}

void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot)
{
	if ((node != &ext4fs_root->diropen) && (node != currroot))
		free(node);
}

/*
 * Taken from openmoko-kernel mailing list: By Andy green
 * Optimized read file API : collects and defers contiguous sector
 * reads into one potentially more efficient larger sequential read action
 */
49 50
int ext4fs_read_file(struct ext2fs_node *node, loff_t pos,
		loff_t len, char *buf, loff_t *actread)
Uma Shankar committed
51
{
52
	struct ext_filesystem *fs = get_fs();
Uma Shankar committed
53
	int i;
54
	lbaint_t blockcnt;
55 56 57
	int log2blksz = fs->dev_desc->log2blksz;
	int log2_fs_blocksize = LOG2_BLOCK_SIZE(node->data) - log2blksz;
	int blocksize = (1 << (log2_fs_blocksize + log2blksz));
58
	unsigned int filesize = le32_to_cpu(node->inode.size);
59 60 61 62 63
	lbaint_t previous_block_number = -1;
	lbaint_t delayed_start = 0;
	lbaint_t delayed_extent = 0;
	lbaint_t delayed_skipfirst = 0;
	lbaint_t delayed_next = 0;
Uma Shankar committed
64 65 66 67
	char *delayed_buf = NULL;
	short status;

	/* Adjust len so it we can't read past the end of the file. */
68 69
	if (len + pos > filesize)
		len = (filesize - pos);
Uma Shankar committed
70

71
	blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize);
Uma Shankar committed
72

73
	for (i = lldiv(pos, blocksize); i < blockcnt; i++) {
74
		lbaint_t blknr;
75
		int blockoff = pos - (blocksize * i);
Uma Shankar committed
76 77 78
		int blockend = blocksize;
		int skipfirst = 0;
		blknr = read_allocated_block(&(node->inode), i);
79 80
		if (blknr < 0)
			return -1;
Uma Shankar committed
81

82
		blknr = blknr << log2_fs_blocksize;
Uma Shankar committed
83 84 85

		/* Last block.  */
		if (i == blockcnt - 1) {
86
			blockend = (len + pos) - (blocksize * i);
Uma Shankar committed
87 88 89 90 91 92 93

			/* The last portion is exactly blocksize. */
			if (!blockend)
				blockend = blocksize;
		}

		/* First block. */
94
		if (i == lldiv(pos, blocksize)) {
Uma Shankar committed
95 96 97 98 99 100 101 102 103
			skipfirst = blockoff;
			blockend -= skipfirst;
		}
		if (blknr) {
			int status;

			if (previous_block_number != -1) {
				if (delayed_next == blknr) {
					delayed_extent += blockend;
104
					delayed_next += blockend >> log2blksz;
Uma Shankar committed
105 106 107 108 109
				} else {	/* spill */
					status = ext4fs_devread(delayed_start,
							delayed_skipfirst,
							delayed_extent,
							delayed_buf);
110 111
					if (status == 0)
						return -1;
Uma Shankar committed
112 113 114 115 116 117
					previous_block_number = blknr;
					delayed_start = blknr;
					delayed_extent = blockend;
					delayed_skipfirst = skipfirst;
					delayed_buf = buf;
					delayed_next = blknr +
118
						(blockend >> log2blksz);
Uma Shankar committed
119 120 121 122 123 124 125 126
				}
			} else {
				previous_block_number = blknr;
				delayed_start = blknr;
				delayed_extent = blockend;
				delayed_skipfirst = skipfirst;
				delayed_buf = buf;
				delayed_next = blknr +
127
					(blockend >> log2blksz);
Uma Shankar committed
128 129 130 131 132 133 134 135
			}
		} else {
			if (previous_block_number != -1) {
				/* spill */
				status = ext4fs_devread(delayed_start,
							delayed_skipfirst,
							delayed_extent,
							delayed_buf);
136 137
				if (status == 0)
					return -1;
Uma Shankar committed
138 139 140 141 142 143 144 145 146 147 148
				previous_block_number = -1;
			}
			memset(buf, 0, blocksize - skipfirst);
		}
		buf += blocksize - skipfirst;
	}
	if (previous_block_number != -1) {
		/* spill */
		status = ext4fs_devread(delayed_start,
					delayed_skipfirst, delayed_extent,
					delayed_buf);
149 150
		if (status == 0)
			return -1;
Uma Shankar committed
151 152 153
		previous_block_number = -1;
	}

154 155
	*actread  = len;
	return 0;
Uma Shankar committed
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
}

int ext4fs_ls(const char *dirname)
{
	struct ext2fs_node *dirnode;
	int status;

	if (dirname == NULL)
		return 0;

	status = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dirnode,
				  FILETYPE_DIRECTORY);
	if (status != 1) {
		printf("** Can not find directory. **\n");
		return 1;
	}

	ext4fs_iterate_dir(dirnode, NULL, NULL, NULL);
	ext4fs_free_node(dirnode, &ext4fs_root->diropen);

	return 0;
}

179 180
int ext4fs_exists(const char *filename)
{
181 182
	loff_t file_len;
	int ret;
183

184 185
	ret = ext4fs_open(filename, &file_len);
	return ret == 0;
186 187
}

188
int ext4fs_size(const char *filename, loff_t *size)
189
{
190
	return ext4fs_open(filename, size);
191 192
}

193
int ext4fs_read(char *buf, loff_t offset, loff_t len, loff_t *actread)
Uma Shankar committed
194 195
{
	if (ext4fs_root == NULL || ext4fs_file == NULL)
196
		return -1;
Uma Shankar committed
197

198
	return ext4fs_read_file(ext4fs_file, offset, len, buf, actread);
Uma Shankar committed
199
}
200

201
int ext4fs_probe(struct blk_desc *fs_dev_desc,
202 203 204 205 206 207 208 209 210 211 212 213
		 disk_partition_t *fs_partition)
{
	ext4fs_set_blk_dev(fs_dev_desc, fs_partition);

	if (!ext4fs_mount(fs_partition->size)) {
		ext4fs_close();
		return -1;
	}

	return 0;
}

214 215
int ext4_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
		   loff_t *len_read)
216
{
217 218
	loff_t file_len;
	int ret;
219

220 221
	ret = ext4fs_open(filename, &file_len);
	if (ret < 0) {
222 223 224 225 226 227 228
		printf("** File not found %s **\n", filename);
		return -1;
	}

	if (len == 0)
		len = file_len;

229
	return ext4fs_read(buf, offset, len, len_read);
230
}
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

int ext4fs_uuid(char *uuid_str)
{
	if (ext4fs_root == NULL)
		return -1;

#ifdef CONFIG_LIB_UUID
	uuid_bin_to_str((unsigned char *)ext4fs_root->sblock.unique_id,
			uuid_str, UUID_STR_FORMAT_STD);

	return 0;
#else
	return -ENOSYS;
#endif
}