All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Add support for the SquashFS filesystem
@ 2020-07-09 17:51 Joao Marcos Costa
  2020-07-09 17:51 ` [PATCH 1/4] fs/squashfs: new filesystem Joao Marcos Costa
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: Joao Marcos Costa @ 2020-07-09 17:51 UTC (permalink / raw)
  To: u-boot

Hello!

This series adds support for the SquashFS filesystem. For now, zlib is the only
supported compression type. This is my first contribution to U-Boot as well as
to a major Open Source project.

Best regards,
Joao Marcos Costa

Joao Marcos Costa (4):
  fs/squashfs: new filesystem
  fs/squashfs: add filesystem commands
  fs/squashfs: add sources for zlib decompression
  fs/squashfs: add support for zlib decompression

 cmd/Kconfig                     |    6 +
 cmd/Makefile                    |    1 +
 cmd/sqfs.c                      |   43 +
 common/spl/Kconfig              |    9 +
 fs/Kconfig                      |    2 +
 fs/Makefile                     |    2 +
 fs/fs.c                         |   15 +
 fs/squashfs/Kconfig             |   10 +
 fs/squashfs/Makefile            |    7 +
 fs/squashfs/sqfs.c              | 1625 +++++++++++++++++++++++++++++++
 fs/squashfs/sqfs_decompressor.c |   53 +
 fs/squashfs/sqfs_decompressor.h |   58 ++
 fs/squashfs/sqfs_dir.c          |  107 ++
 fs/squashfs/sqfs_filesystem.h   |  301 ++++++
 fs/squashfs/sqfs_inode.c        |  142 +++
 fs/squashfs/sqfs_utils.h        |   49 +
 include/fs.h                    |   13 +
 include/squashfs.h              |   24 +
 include/u-boot/zlib.h           |   32 +
 lib/zlib/uncompr.c              |   93 ++
 lib/zlib/zlib.c                 |    1 +
 21 files changed, 2593 insertions(+)
 create mode 100644 cmd/sqfs.c
 create mode 100644 fs/squashfs/Kconfig
 create mode 100644 fs/squashfs/Makefile
 create mode 100644 fs/squashfs/sqfs.c
 create mode 100644 fs/squashfs/sqfs_decompressor.c
 create mode 100644 fs/squashfs/sqfs_decompressor.h
 create mode 100644 fs/squashfs/sqfs_dir.c
 create mode 100644 fs/squashfs/sqfs_filesystem.h
 create mode 100644 fs/squashfs/sqfs_inode.c
 create mode 100644 fs/squashfs/sqfs_utils.h
 create mode 100644 include/squashfs.h
 create mode 100644 lib/zlib/uncompr.c

--
2.17.1

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/4] fs/squashfs: new filesystem
  2020-07-09 17:51 [PATCH 0/4] Add support for the SquashFS filesystem Joao Marcos Costa
@ 2020-07-09 17:51 ` Joao Marcos Costa
  2020-07-09 19:50   ` Rasmus Villemoes
  2020-07-15 21:55   ` Tom Rini
  2020-07-09 17:51 ` [PATCH 2/4] fs/squashfs: add filesystem commands Joao Marcos Costa
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 20+ messages in thread
From: Joao Marcos Costa @ 2020-07-09 17:51 UTC (permalink / raw)
  To: u-boot

Add support for SquashFS filesystem. Right now, it does not support
compression but support for zlib will be added in a follow-up commit.

Signed-off-by: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
---
 common/spl/Kconfig              |    9 +
 fs/Kconfig                      |    2 +
 fs/Makefile                     |    2 +
 fs/fs.c                         |   15 +
 fs/squashfs/Kconfig             |   10 +
 fs/squashfs/Makefile            |    7 +
 fs/squashfs/sqfs.c              | 1625 +++++++++++++++++++++++++++++++
 fs/squashfs/sqfs_decompressor.c |   29 +
 fs/squashfs/sqfs_decompressor.h |   58 ++
 fs/squashfs/sqfs_dir.c          |  107 ++
 fs/squashfs/sqfs_filesystem.h   |  301 ++++++
 fs/squashfs/sqfs_inode.c        |  142 +++
 fs/squashfs/sqfs_utils.h        |   49 +
 include/fs.h                    |   13 +
 include/squashfs.h              |   24 +
 15 files changed, 2393 insertions(+)
 create mode 100644 fs/squashfs/Kconfig
 create mode 100644 fs/squashfs/Makefile
 create mode 100644 fs/squashfs/sqfs.c
 create mode 100644 fs/squashfs/sqfs_decompressor.c
 create mode 100644 fs/squashfs/sqfs_decompressor.h
 create mode 100644 fs/squashfs/sqfs_dir.c
 create mode 100644 fs/squashfs/sqfs_filesystem.h
 create mode 100644 fs/squashfs/sqfs_inode.c
 create mode 100644 fs/squashfs/sqfs_utils.h
 create mode 100644 include/squashfs.h

diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index b03a476b9f..426b783d9d 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -566,6 +566,15 @@ config SPL_FS_EXT4
 	  filesystem from within SPL. Support for the underlying block
 	  device (e.g. MMC or USB) must be enabled separately.

+config SPL_FS_SQUASHFS
+	bool "Support SquashFS filesystems"
+	select FS_SQUASHFS
+	help
+	  Enable support for SquashFS filesystems with SPL. This permits
+	  U-Boot (or Linux in Falcon mode) to be loaded from a SquashFS
+	  filesystem from within SPL. Support for the underlying block
+	  device (e.g. MMC or USB) must be enabled separately.
+
 config SPL_FS_FAT
 	bool "Support FAT filesystems"
 	select FS_FAT
diff --git a/fs/Kconfig b/fs/Kconfig
index 1cb9831be8..620af7f044 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -22,4 +22,6 @@ source "fs/cramfs/Kconfig"

 source "fs/yaffs2/Kconfig"

+source "fs/squashfs/Kconfig"
+
 endmenu
diff --git a/fs/Makefile b/fs/Makefile
index 42e669c40c..937cbcf6e8 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_FS_LOADER) += fs.o
 obj-$(CONFIG_SPL_FS_FAT) += fat/
 obj-$(CONFIG_SPL_FS_EXT4) += ext4/
 obj-$(CONFIG_SPL_FS_CBFS) += cbfs/
+obj-$(CONFIG_SPL_FS_SQUASHFS) += squashfs/
 else
 obj-y				+= fs.o

@@ -23,5 +24,6 @@ obj-$(CONFIG_SANDBOX) += sandbox/
 obj-$(CONFIG_CMD_UBIFS) += ubifs/
 obj-$(CONFIG_YAFFS2) += yaffs2/
 obj-$(CONFIG_CMD_ZFS) += zfs/
+obj-$(CONFIG_FS_SQUASHFS) += squashfs/
 endif
 obj-y += fs_internal.o
diff --git a/fs/fs.c b/fs/fs.c
index 0c66d60477..2684e6ccce 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -19,6 +19,7 @@
 #include <div64.h>
 #include <linux/math64.h>
 #include <efi_loader.h>
+#include <squashfs.h>

 DECLARE_GLOBAL_DATA_PTR;

@@ -273,6 +274,20 @@ static struct fstype_info fstypes[] = {
 		.mkdir = fs_mkdir_unsupported,
 		.ln = fs_ln_unsupported,
 	},
+#endif
+#ifdef CONFIG_FS_SQUASHFS
+	{
+		.fstype = FS_TYPE_SQUASHFS,
+		.name = "squashfs",
+		.probe = sqfs_probe,
+		.opendir = sqfs_opendir,
+		.readdir = sqfs_readdir,
+		.ls = sqfs_ls,
+		.read = sqfs_read,
+		.size = sqfs_size,
+		.close = sqfs_close,
+		.closedir = sqfs_closedir,
+	},
 #endif
 	{
 		.fstype = FS_TYPE_ANY,
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
new file mode 100644
index 0000000000..b9772e5619
--- /dev/null
+++ b/fs/squashfs/Kconfig
@@ -0,0 +1,10 @@
+config FS_SQUASHFS
+	bool "Enable SquashFS filesystem support"
+	help
+	  This provides support for reading images from SquashFS filesystem.
+	  Squashfs is a compressed read-only filesystem for Linux.
+	  It uses zlib, lz4, lzo, or xz compression to compress files, inodes
+	  and directories. Squashfs is intended for general read-only
+	  filesystem use, for archival use (i.e. in cases where a .tar.gz file
+	  may be used), and in constrained block device/memory systems (e.g.
+	  embedded systems) where low overhead is needed.
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
new file mode 100644
index 0000000000..ba66ee821c
--- /dev/null
+++ b/fs/squashfs/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_$(SPL_)FS_SQUASHFS) = sqfs.o \
+				sqfs_inode.o \
+				sqfs_dir.o \
+				sqfs_decompressor.o
diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
new file mode 100644
index 0000000000..3302a63cca
--- /dev/null
+++ b/fs/squashfs/sqfs.c
@@ -0,0 +1,1625 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ *
+ * sqfs.c: SquashFS filesystem implementation
+ */
+
+#include <asm/unaligned.h>
+#include <errno.h>
+#include <fs.h>
+#include <linux/types.h>
+#include <linux/byteorder/little_endian.h>
+#include <linux/byteorder/generic.h>
+#include <memalign.h>
+#include <stdlib.h>
+#include <string.h>
+#include <squashfs.h>
+
+#include "sqfs_decompressor.h"
+#include "sqfs_filesystem.h"
+#include "sqfs_utils.h"
+
+static disk_partition_t cur_part_info;
+static struct blk_desc *cur_dev;
+
+static int sqfs_disk_read(__u32 block, __u32 nr_blocks, void *buf)
+{
+	ulong ret;
+
+	if (!cur_dev)
+		return -1;
+
+	ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
+	if (ret != nr_blocks)
+		return -1;
+
+	return ret;
+}
+
+static int sqfs_read_sblk(struct squashfs_super_block **sblk)
+{
+	*sblk = malloc_cache_aligned(cur_dev->blksz);
+	if (!*sblk)
+		return -ENOMEM;
+
+	if (sqfs_disk_read(0, 1, *sblk) != 1) {
+		free(*sblk);
+		cur_dev = NULL;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int sqfs_count_tokens(const char *path)
+{
+	int token_count = 1, l;
+
+	for (l = 1; l < strlen(path); l++) {
+		if (path[l] == '/')
+			token_count++;
+	}
+
+	return token_count;
+}
+
+static int sqfs_tokenize_path(const char *filename)
+{
+	int token_count;
+
+	token_count = sqfs_count_tokens(filename);
+	if (token_count < 0)
+		return -EINVAL;
+
+	/* Ignore trailing '/' in path */
+	if (filename[strlen(filename) - 1] == '/')
+		token_count--;
+
+	if (!token_count)
+		token_count = 1;
+
+	return token_count;
+}
+
+/*
+ * Calculates how many blocks are needed for the buffer used in sqfs_disk_read.
+ * The memory section (e.g. inode table) start offset and its end (i.e. the next
+ * table start) must be specified. It also calculates the offset from which to
+ * start reading the buffer.
+ */
+static int sqfs_calc_n_blks(__le64 start, __le64 end, u64 *offset)
+{
+	u64 start_, table_size;
+
+	table_size = le64_to_cpu(end) - le64_to_cpu(start);
+	start_ = le64_to_cpu(start) / cur_dev->blksz;
+	*offset = le64_to_cpu(start) - (start_ * cur_dev->blksz);
+
+	return DIV_ROUND_UP(table_size + *offset, cur_dev->blksz);
+}
+
+/*
+ * Retrieves fragment block entry and returns true if the fragment block is
+ * compressed
+ */
+static int sqfs_frag_lookup(u32 inode_fragment_index,
+			    struct squashfs_fragment_block_entry *e)
+{
+	u64 start, n_blks, src_len, table_offset, start_block;
+	unsigned char *metadata_buffer, *metadata, *table;
+	struct squashfs_fragment_block_entry *entries;
+	struct squashfs_super_block *sblk;
+	unsigned long dest_len;
+	int block, offset, ret;
+	u16 header, comp_type;
+
+	ret = sqfs_read_sblk(&sblk);
+	if (ret)
+		return ret;
+
+	comp_type = le16_to_cpu(sblk->compression);
+
+	if (inode_fragment_index >= le32_to_cpu(sblk->fragments)) {
+		ret = -EINVAL;
+		goto free_sblk;
+	}
+
+	start = le64_to_cpu(sblk->fragment_table_start) / cur_dev->blksz;
+	n_blks = sqfs_calc_n_blks(sblk->fragment_table_start,
+				  sblk->export_table_start, &table_offset);
+
+	/* Allocate a proper sized buffer to store the fragment index table */
+	table = malloc_cache_aligned(n_blks * cur_dev->blksz);
+	if (!table) {
+		ret = -ENOMEM;
+		goto free_sblk;
+	}
+
+	if (sqfs_disk_read(start, n_blks, table) < 0) {
+		free(sblk);
+		free(table);
+		return -EINVAL;
+	}
+
+	block = SQFS_FRAGMENT_INDEX(inode_fragment_index);
+	offset = SQFS_FRAGMENT_INDEX_OFFSET(inode_fragment_index);
+
+	/*
+	 * Get the start offset of the metadata block that contains the right
+	 * fragment block entry
+	 */
+	start_block = get_unaligned((u64 *)&table[table_offset] + block);
+
+	start = start_block / cur_dev->blksz;
+	n_blks = sqfs_calc_n_blks(cpu_to_le64(start_block),
+				  sblk->fragment_table_start, &table_offset);
+
+	metadata_buffer = malloc_cache_aligned(n_blks * cur_dev->blksz);
+	if (!metadata_buffer) {
+		ret = -ENOMEM;
+		goto free_table;
+	}
+
+	if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) {
+		ret = -EINVAL;
+		goto free_buffer;
+	}
+
+	/* Every metadata block starts with a 16-bit header */
+	header = get_unaligned((u16 *)&metadata_buffer[table_offset]);
+	metadata = &metadata_buffer[table_offset + SQFS_HEADER_SIZE];
+
+	entries = malloc(SQFS_METADATA_BLOCK_SIZE);
+	if (!entries) {
+		ret = -ENOMEM;
+		goto free_buffer;
+	}
+
+	if (SQFS_COMPRESSED_METADATA(header)) {
+		src_len = SQFS_METADATA_SIZE(header);
+		dest_len = SQFS_METADATA_BLOCK_SIZE;
+		ret = sqfs_decompress(comp_type, entries, &dest_len, metadata,
+				      src_len);
+		if (ret) {
+			ret = -EINVAL;
+			goto free_entries;
+		}
+	} else {
+		memcpy(entries, metadata, dest_len);
+	}
+
+	*e = entries[offset];
+	ret = SQFS_COMPRESSED_BLOCK(e->size);
+
+free_entries:
+	free(entries);
+free_buffer:
+	free(metadata_buffer);
+free_table:
+	free(table);
+free_sblk:
+	free(sblk);
+	return ret;
+}
+
+/*
+ * The entry name is a flexible array member, and we don't know its size before
+ * actually reading the entry. So we need a first copy to retrieve this size so
+ * we can finally copy the whole struct.
+ */
+static int sqfs_read_entry(struct squashfs_directory_entry **dest, void *src)
+{
+	struct squashfs_directory_entry tmp;
+
+	memcpy(&tmp, src, sizeof(tmp));
+	/*
+	 * name_size property is actually the string length - 1, so adding 2
+	 * compensates this difference and adds space for the trailling null
+	 * byte.
+	 */
+	*dest = malloc(sizeof(tmp) + tmp.name_size + 2);
+	if (!*dest)
+		return -ENOMEM;
+
+	memcpy(*dest, src, sizeof(tmp) + tmp.name_size + 1);
+	(*dest)->name[tmp.name_size + 1] = '\0';
+
+	return 0;
+}
+
+/* Takes a token list and returns a single string with '/' as separator. */
+static char *sqfs_concat_tokens(char **token_list, int token_count)
+{
+	char *result;
+	int i, length = 0, offset = 0;
+
+	for (i = 0; i < token_count; i++)
+		length += strlen(token_list[i]) + 1;
+
+	result = malloc(length + 1);
+	result[length] = '\0';
+
+	for (i = 0; i < token_count; i++) {
+		strcpy(result + offset, token_list[i]);
+		offset += strlen(token_list[i]);
+		result[offset++] = '/';
+	}
+
+	return result;
+}
+
+/*
+ * Given the base ("current dir.") path and the relative one, generate the
+ * absolute path.
+ */
+static char *sqfs_get_abs_path(const char *base, const char *rel)
+{
+	char **base_tokens, **rel_tokens, *aux, *resolved = NULL, *basec, *relc;
+	int bc, rc, i, j, updir = 0, resolved_size = 0, offset;
+
+	/* create copies of parameters */
+	basec = strdup(base);
+	if (!basec)
+		return NULL;
+
+	relc = strdup(rel);
+	if (!relc)
+		goto free_basec;
+
+	/* count tokens in paths */
+	bc = sqfs_tokenize_path(basec);
+	rc = sqfs_tokenize_path(relc);
+	if (bc < 1 || rc < 1)
+		goto free_relc;
+
+	base_tokens = malloc(bc * sizeof(char *));
+	if (!base_tokens)
+		goto free_relc;
+
+	rel_tokens = malloc(rc * sizeof(char *));
+	if (!rel_tokens)
+		goto free_b_tokens;
+
+	/* Allocate and fill base tokens list */
+	if (!strcmp(basec, "/")) {
+		base_tokens[0] = strdup(basec);
+		if (!base_tokens[0])
+			goto free_r_tokens;
+	} else {
+		for (j = 0; j < bc; j++) {
+			aux = strtok(!j ? basec : NULL, "/");
+			base_tokens[j] = strdup(aux);
+			if (!base_tokens[j]) {
+				for (i = 0; i < j; i++)
+					free(base_tokens[i]);
+				goto free_r_tokens;
+			}
+		}
+	}
+
+	/* Allocate and fill rel. tokens list */
+	if (!strcmp(relc, "/")) {
+		rel_tokens[0] = strdup(relc);
+		if (!rel_tokens[0])
+			goto free_b_tokens_loop;
+	} else {
+		for (j = 0; j < rc; j++) {
+			aux = strtok(!j ? relc : NULL, "/");
+			rel_tokens[j] = strdup(aux);
+			if (!rel_tokens[j]) {
+				for (i = 0; i < j; i++)
+					free(rel_tokens[i]);
+				goto free_b_tokens_loop;
+			}
+		}
+	}
+
+	/* ignore file name in base path, e.g. /dir/file -> dir */
+	free(base_tokens[bc - 1]);
+	bc--;
+
+	/* count '..' occurrences in target path */
+	for (i = 0; i < rc; i++) {
+		if (!strcmp(rel_tokens[i], ".."))
+			updir++;
+	}
+
+	for (i = bc - updir; i < bc; i++)
+		free(base_tokens[i]);
+
+	bc -= updir;
+	if (bc < 0)
+		bc = 0;
+
+	if (!bc)
+		resolved_size++;
+
+	for (i = 0; i < bc; i++)
+		resolved_size += strlen(base_tokens[i]) + 1;
+
+	for (i = updir; i < rc; i++)
+		resolved_size += strlen(rel_tokens[i])  + 1;
+
+	resolved = malloc(resolved_size + 1);
+	if (!resolved)
+		goto free_r_tokens_loop;
+
+	resolved[0] = '/';
+	memset(resolved + 1, '\0', resolved_size);
+
+	offset = 1;
+	for (i = 0; i < bc; i++) {
+		strncpy(resolved + offset, base_tokens[i],
+			strlen(base_tokens[i]));
+		offset += strlen(base_tokens[i]);
+		resolved[offset++] = '/';
+	}
+
+	for (i = updir; i < rc; i++) {
+		strncpy(resolved + offset, rel_tokens[i],
+			strlen(rel_tokens[i]));
+		offset += strlen(rel_tokens[i]);
+		if (offset >= resolved_size - 1)
+			break;
+		resolved[offset++] = '/';
+	}
+
+free_r_tokens_loop:
+	for (i = 0; i < rc; i++)
+		free(rel_tokens[i]);
+free_b_tokens_loop:
+	for (i = 0; i < bc; i++)
+		free(base_tokens[i]);
+free_r_tokens:
+	free(rel_tokens);
+free_b_tokens:
+	free(base_tokens);
+free_relc:
+	free(relc);
+free_basec:
+	free(basec);
+
+	return resolved;
+}
+
+static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym,
+				  unsigned char *table, const char *base_path)
+{
+	char *resolved, *target;
+
+	target = malloc(sym->symlink_size + 1);
+	if (!target)
+		return NULL;
+
+	target[sym->symlink_size] = '\0';
+	/* Get target name (relative path) */
+	strncpy(target, (char *)table + sizeof(*sym), sym->symlink_size);
+
+	/* Relative -> absolute path conversion */
+	resolved = sqfs_get_abs_path(base_path, target);
+
+	free(target);
+
+	return resolved;
+}
+
+/*
+ * m_list contains each metadata block's position, and m_count is the number of
+ * elements of m_list. Those metadata blocks come from the compressed directory
+ * table.
+ */
+static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
+			   int token_count, u32 *m_list, int m_count)
+{
+	int j, ret, new_inode_number, offset;
+	struct squashfs_symlink_inode sym;
+	struct squashfs_super_block *sblk;
+	struct squashfs_ldir_inode ldir;
+	struct squashfs_dir_inode dir;
+	struct fs_dir_stream *dirsp;
+	char  *path, *filename;
+	struct fs_dirent dent;
+	unsigned char *table;
+
+	/* Read SquashFS super block */
+	ret = sqfs_read_sblk(&sblk);
+	if (ret)
+		return ret;
+
+	dirsp = (struct fs_dir_stream *)dirs;
+	dirs->dentp = &dent;
+
+	/* Start by root inode */
+	table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
+				sblk->inodes, sblk->block_size);
+
+	/* root is a regular directory, not an extended one */
+	memcpy(&dir, table, sizeof(dir));
+
+	/* get directory offset in directory table */
+	offset = sqfs_dir_offset(table, m_list, m_count);
+	dirs->table = &dirs->dir_table[offset];
+
+	/* Setup directory header */
+	dirs->dir_header = malloc(SQFS_DIR_HEADER_SIZE);
+	if (!dirs->dir_header) {
+		free(sblk);
+		return -ENOMEM;
+	}
+
+	memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
+	dirs->table += SQFS_DIR_HEADER_SIZE;
+
+	dirs->size = le16_to_cpu(dir.file_size);
+	dirs->entry_count = dirs->dir_header->count + 1;
+	dirs->size -= SQFS_DIR_HEADER_SIZE;
+
+	/* No path given -> root directory */
+	if (!strcmp(token_list[0], "/")) {
+		dirs->table = &dirs->dir_table[offset];
+		memcpy(&dirs->i_dir, &dir, sizeof(dir));
+		free(sblk);
+		return 0;
+	}
+
+	for (j = 0; j < token_count; j++) {
+		if (!sqfs_is_dir(dir.inode_type)) {
+			printf("Directory not found: %s\n", token_list[j]);
+			free(sblk);
+			return -EINVAL;
+		}
+
+		while (sqfs_readdir(dirsp, &dirs->dentp)) {
+			ret = strcmp(dent.name, token_list[j]);
+			if (!ret)
+				break;
+			free(dirs->entry);
+		}
+
+		if (ret) {
+			printf("Directory not found: %s\n", token_list[j]);
+			free(dirs->entry);
+			free(sblk);
+			return -EINVAL;
+		}
+
+		/* Redefine inode as the found token */
+		new_inode_number = dirs->entry->inode_offset +
+			dirs->dir_header->inode_number;
+
+		/* Get reference to inode in the inode table */
+		table = sqfs_find_inode(dirs->inode_table, new_inode_number,
+					sblk->inodes, sblk->block_size);
+		memcpy(&dir, table, sizeof(dir));
+
+		/* Check for symbolic link and inode type sanity */
+		if (le16_to_cpu(dir.inode_type) == SQFS_SYMLINK_TYPE) {
+			memcpy(&sym, table, sizeof(sym));
+			path = sqfs_concat_tokens(token_list, token_count);
+			filename = sqfs_resolve_symlink(&sym, table, path);
+			printf("%s - > %s\n", token_list[j], filename);
+			free(dirs->entry);
+			free(sblk);
+			return SQFS_SYMLINK_TYPE;
+		} else if (!sqfs_is_dir(dir.inode_type)) {
+			printf("%s is not a directory.\n", token_list[j]);
+			free(dirs->entry);
+			free(sblk);
+			return -EINVAL;
+		}
+
+		/* Check if it is an extended dir. */
+		if (dir.inode_type == SQFS_LDIR_TYPE)
+			memcpy(&ldir, table, sizeof(ldir));
+
+		/* Get dir. offset into the directory table */
+		offset = sqfs_dir_offset(table, m_list, m_count);
+		dirs->table = &dirs->dir_table[offset];
+
+		/* Copy directory header */
+		memcpy(dirs->dir_header, &dirs->dir_table[offset],
+		       SQFS_DIR_HEADER_SIZE);
+
+		/* Check for empty directory */
+		if (sqfs_is_empty_dir(table)) {
+			printf("Empty directory.\n");
+			free(dirs->entry);
+			free(sblk);
+			return SQFS_EMPTY_DIR;
+		}
+
+		dirs->table += SQFS_DIR_HEADER_SIZE;
+		dirs->size = le16_to_cpu(dir.file_size);
+		dirs->entry_count = dirs->dir_header->count + 1;
+		dirs->size -= SQFS_DIR_HEADER_SIZE;
+		free(dirs->entry);
+	}
+
+	offset = sqfs_dir_offset(table, m_list, m_count);
+	dirs->table = &dirs->dir_table[offset];
+
+	if (dir.inode_type == SQFS_DIR_TYPE)
+		memcpy(&dirs->i_dir, &dir, sizeof(dir));
+	else
+		memcpy(&dirs->i_ldir, &ldir, sizeof(ldir));
+
+	free(sblk);
+
+	return 0;
+}
+
+/*
+ * Inode and directory tables are stored as a series of metadata blocks, and
+ * given the compressed size of this table, we can calculate how much metadata
+ * blocks are needed to store the result of the decompression, since a
+ * decompressed metadata block should have a size of 8KiB.
+ */
+static int sqfs_count_metablks(void *table, u32 offset, int table_size)
+{
+	int count = 0, cur_size = 0, ret;
+	u32 data_size;
+	bool comp;
+
+	do {
+		ret = sqfs_read_metablock(table, offset + cur_size, &comp,
+					  &data_size);
+		if (ret)
+			return -EINVAL;
+		cur_size += data_size + SQFS_HEADER_SIZE;
+		count++;
+	} while (cur_size < table_size);
+
+	return count;
+}
+
+/*
+ * Storing the metadata blocks header's positions will be useful while looking
+ * for an entry in the directory table, using the reference (index and offset)
+ * given by its inode.
+ */
+static int sqfs_get_metablk_pos(u32 *pos_list, void *table, u32 offset,
+				int metablks_count)
+{
+	u32 data_size, cur_size = 0;
+	int j, ret = 0;
+	bool comp;
+
+	if (!metablks_count)
+		return -EINVAL;
+
+	for (j = 0; j < metablks_count; j++) {
+		ret = sqfs_read_metablock(table, offset + cur_size, &comp,
+					  &data_size);
+		if (ret)
+			return -EINVAL;
+
+		cur_size += data_size + SQFS_HEADER_SIZE;
+		pos_list[j] = cur_size;
+	}
+
+	return ret;
+}
+
+static int sqfs_read_inode_table(unsigned char **inode_table)
+{
+	u64 start, n_blks, table_offset, table_size;
+	int j, ret = 0, metablks_count, comp_type;
+	struct squashfs_super_block *sblk;
+	unsigned char *src_table, *itb;
+	u32 src_len, dest_offset = 0;
+	unsigned long dest_len;
+	bool compressed;
+
+	/* Read SquashFS super block */
+	ret = sqfs_read_sblk(&sblk);
+	if (ret)
+		return ret;
+
+	comp_type = sblk->compression;
+	table_size = le64_to_cpu(sblk->directory_table_start -
+				 sblk->inode_table_start);
+	start = sblk->inode_table_start / cur_dev->blksz;
+	n_blks = sqfs_calc_n_blks(sblk->inode_table_start,
+				  sblk->directory_table_start, &table_offset);
+
+	/* Allocate a proper sized buffer (itb) to store the inode table */
+	itb = malloc_cache_aligned(n_blks * cur_dev->blksz);
+	if (!itb) {
+		ret = -ENOMEM;
+		goto free_sblk;
+	}
+
+	if (sqfs_disk_read(start, n_blks, itb) < 0) {
+		ret = -EINVAL;
+		goto free_itb;
+	}
+
+	/* Parse inode table (metadata block) header */
+	ret = sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
+	if (ret) {
+		ret = -EINVAL;
+		goto free_itb;
+	}
+
+	/* Calculate size to store the whole decompressed table */
+	metablks_count = sqfs_count_metablks(itb, table_offset, table_size);
+	if (metablks_count < 1) {
+		ret = -EINVAL;
+		goto free_itb;
+	}
+
+	*inode_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
+	if (!*inode_table) {
+		ret = -ENOMEM;
+		goto free_itb;
+	}
+
+	src_table = itb + table_offset + SQFS_HEADER_SIZE;
+
+	/* Extract compressed Inode table */
+	for (j = 0; j < metablks_count; j++) {
+		sqfs_read_metablock(itb, table_offset, &compressed, &src_len);
+		if (compressed) {
+			dest_len = SQFS_METADATA_BLOCK_SIZE;
+			ret = sqfs_decompress(comp_type, *inode_table +
+					      dest_offset, &dest_len,
+					      src_table, src_len);
+			if (ret) {
+				free(inode_table);
+				goto free_itb;
+			}
+
+		} else {
+			memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE),
+			       src_table, src_len);
+		}
+
+		/*
+		 * Offsets to the decompression destination, to the metadata
+		 * buffer 'itb' and to the decompression source, respectively.
+		 */
+		dest_offset += dest_len;
+		table_offset += src_len + SQFS_HEADER_SIZE;
+		src_table += src_len + SQFS_HEADER_SIZE;
+	}
+
+free_itb:
+	free(itb);
+free_sblk:
+	free(sblk);
+
+	return ret;
+}
+
+static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
+{
+	u64 start, n_blks, table_offset, table_size;
+	int j, ret = 0, metablks_count = -1, comp_type;
+	struct squashfs_super_block *sblk;
+	unsigned char *src_table, *dtb;
+	u32 src_len, dest_offset = 0;
+	unsigned long dest_len;
+	bool compressed;
+
+	/* Read SquashFS super block */
+	ret = sqfs_read_sblk(&sblk);
+	if (ret)
+		return ret;
+
+	comp_type = sblk->compression;
+
+	/* DIRECTORY TABLE */
+	table_size = le64_to_cpu(sblk->fragment_table_start -
+				 sblk->directory_table_start);
+	start = sblk->directory_table_start / cur_dev->blksz;
+	n_blks = sqfs_calc_n_blks(sblk->directory_table_start,
+				  sblk->fragment_table_start, &table_offset);
+
+	/* Allocate a proper sized buffer (dtb) to store the directory table */
+	dtb = malloc_cache_aligned(n_blks * cur_dev->blksz);
+
+	if (!dtb)
+		goto free_sblk;
+
+	if (sqfs_disk_read(start, n_blks, dtb) < 0)
+		goto free_dtb;
+
+	/* Parse directory table (metadata block) header */
+	ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
+	if (ret)
+		goto free_dtb;
+
+	/* Calculate total size to store the whole decompressed table */
+	metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
+	if (metablks_count < 1)
+		goto free_dtb;
+
+	*dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
+	if (!*dir_table)
+		goto free_dtb;
+
+	*pos_list = malloc(metablks_count * sizeof(u32));
+	if (!*pos_list) {
+		free(*dir_table);
+		goto free_dtb;
+	}
+
+	ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
+				   metablks_count);
+	if (ret) {
+		metablks_count = -1;
+		free(*dir_table);
+		free(*pos_list);
+		goto free_dtb;
+	}
+
+	src_table = dtb + table_offset + SQFS_HEADER_SIZE;
+
+	/* Extract compressed Directory table */
+	dest_offset = 0;
+	for (j = 0; j < metablks_count; j++) {
+		sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
+		if (compressed) {
+			dest_len = SQFS_METADATA_BLOCK_SIZE;
+			ret = sqfs_decompress(comp_type, *dir_table +
+					      (j * SQFS_METADATA_BLOCK_SIZE),
+					      &dest_len, src_table, src_len);
+			if (ret) {
+				metablks_count = -1;
+				free(*dir_table);
+				goto free_dtb;
+			}
+
+			if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
+				dest_offset += dest_len;
+				break;
+			}
+		} else {
+			memcpy(*dir_table + (j * SQFS_METADATA_BLOCK_SIZE),
+			       src_table, src_len);
+		}
+
+		/*
+		 * Offsets to the decompression destination, to the metadata
+		 * buffer 'dtb' and to the decompression source, respectively.
+		 */
+		dest_offset += dest_len;
+		table_offset += src_len + SQFS_HEADER_SIZE;
+		src_table += src_len + SQFS_HEADER_SIZE;
+	}
+
+free_dtb:
+	free(dtb);
+free_sblk:
+	free(sblk);
+
+	return metablks_count;
+}
+
+int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
+{
+	unsigned char *inode_table = NULL, *dir_table = NULL;
+	int i, j, token_count, ret = 0, metablks_count;
+	struct squashfs_super_block *sblk;
+	struct squashfs_dir_stream *dirs;
+	char **token_list, *path, *aux;
+	u32 *pos_list = NULL;
+
+	/* Read SquashFS super block */
+	ret = sqfs_read_sblk(&sblk);
+	if (ret)
+		return -EINVAL;
+
+	ret = sqfs_read_inode_table(&inode_table);
+	if (ret) {
+		ret = -EINVAL;
+		goto free_sblk;
+	}
+
+	metablks_count = sqfs_read_directory_table(&dir_table, &pos_list);
+	if (metablks_count < 1) {
+		ret = -EINVAL;
+		goto free_sblk;
+	}
+
+	/* Tokenize filename */
+	token_count = sqfs_tokenize_path(filename);
+	if (token_count < 0) {
+		ret = -EINVAL;
+		goto free_sblk;
+	}
+
+	path = strdup(filename);
+	if (!path) {
+		ret = -ENOMEM;
+		goto free_sblk;
+	}
+
+	token_list = malloc(token_count * sizeof(char *));
+	if (!token_list) {
+		ret = -EINVAL;
+		goto free_path;
+	}
+
+	/* Allocate and fill token list */
+	if (!strcmp(path, "/")) {
+		token_list[0] = strdup(path);
+		if (!token_list[0]) {
+			free(token_list);
+			ret = -ENOMEM;
+			goto free_path;
+		}
+
+	} else {
+		for (j = 0; j < token_count; j++) {
+			aux = strtok(!j ? path : NULL, "/");
+			token_list[j] = strdup(aux);
+			if (!token_list[j]) {
+				for (i = 0; i < j; i++)
+					free(token_list[i]);
+				free(token_list);
+				ret = -ENOMEM;
+				goto free_list;
+			}
+		}
+	}
+
+	dirs = (struct squashfs_dir_stream *)dirsp;
+	/*
+	 * ldir's (extended directory) size is greater than dir, so it works as
+	 * a general solution for the malloc size, since 'i' is a union.
+	 */
+	dirs->inode_table = inode_table;
+	dirs->dir_table = dir_table;
+	ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
+			      metablks_count);
+	if (ret)
+		goto free_tokens;
+
+	if (le16_to_cpu(dirs->i_dir.inode_type) == SQFS_DIR_TYPE)
+		dirs->size = le16_to_cpu(dirs->i_dir.file_size);
+	else
+		dirs->size = le32_to_cpu(dirs->i_ldir.file_size);
+
+	/* Setup directory header */
+	memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE);
+	dirs->entry_count = dirs->dir_header->count + 1;
+	dirs->size -= SQFS_DIR_HEADER_SIZE;
+
+	/* Setup entry */
+	dirs->entry = NULL;
+	dirs->table += SQFS_DIR_HEADER_SIZE;
+
+free_tokens:
+	for (j = 0; j < token_count; j++)
+		free(token_list[j]);
+	free(token_list);
+free_list:
+	free(pos_list);
+free_path:
+	free(path);
+free_sblk:
+	free(sblk);
+
+	return ret;
+}
+
+int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
+{
+	struct squashfs_dir_stream *dirs;
+	struct squashfs_lreg_inode lreg;
+	struct squashfs_base_inode base;
+	struct squashfs_reg_inode reg;
+	int offset = 0, ret;
+
+	dirs = (struct squashfs_dir_stream *)fs_dirs;
+	if (!dirs->size)
+		return SQFS_STOP_READDIR;
+
+	if (!dirs->entry_count) {
+		if (dirs->size > SQFS_DIR_HEADER_SIZE) {
+			dirs->size -= SQFS_DIR_HEADER_SIZE;
+		} else {
+			dirs->size = 0;
+			return SQFS_STOP_READDIR;
+		}
+
+		if (dirs->size > SQFS_EMPTY_FILE_SIZE) {
+			/* Read follow-up (emitted) dir. header */
+			memcpy(dirs->dir_header, dirs->table,
+			       SQFS_DIR_HEADER_SIZE);
+			dirs->entry_count = dirs->dir_header->count + 1;
+			ret = sqfs_read_entry(&dirs->entry, dirs->table +
+					      SQFS_DIR_HEADER_SIZE);
+			if (ret)
+				return SQFS_STOP_READDIR;
+
+			dirs->table += SQFS_DIR_HEADER_SIZE;
+		}
+	} else {
+		ret = sqfs_read_entry(&dirs->entry, dirs->table);
+		if (ret)
+			return SQFS_STOP_READDIR;
+	}
+
+	memcpy(&base, &dirs->inode_table[dirs->entry->offset], sizeof(base));
+
+	/* Set entry type and size */
+	switch (dirs->entry->type) {
+	case SQFS_DIR_TYPE:
+	case SQFS_LDIR_TYPE:
+		(*dentp)->type = FS_DT_DIR;
+		break;
+	case SQFS_REG_TYPE:
+	case SQFS_LREG_TYPE:
+		/*
+		 * Entries do not differentiate extended from regular types, so
+		 * it needs to be verified manually.
+		 */
+		if (base.inode_type == SQFS_LREG_TYPE) {
+			memcpy(&lreg, &dirs->inode_table[dirs->entry->offset],
+			       sizeof(lreg));
+			(*dentp)->size = (loff_t)le64_to_cpu(lreg.file_size);
+		} else {
+			memcpy(&reg, &dirs->inode_table[dirs->entry->offset],
+			       sizeof(reg));
+			(*dentp)->size = (loff_t)le32_to_cpu(reg.file_size);
+		}
+
+		(*dentp)->type = FS_DT_REG;
+		break;
+	case SQFS_BLKDEV_TYPE:
+	case SQFS_CHRDEV_TYPE:
+	case SQFS_LBLKDEV_TYPE:
+	case SQFS_LCHRDEV_TYPE:
+	case SQFS_FIFO_TYPE:
+	case SQFS_SOCKET_TYPE:
+	case SQFS_LFIFO_TYPE:
+	case SQFS_LSOCKET_TYPE:
+		(*dentp)->type = SQFS_MISC_ENTRY_TYPE;
+		break;
+	case SQFS_SYMLINK_TYPE:
+	case SQFS_LSYMLINK_TYPE:
+		(*dentp)->type = FS_DT_LNK;
+		break;
+	default:
+		/*
+		 * This macro evaluates to 0, which usually means a successful
+		 * execution, but in this case it returns 0 to stop the while
+		 * loop.
+		 */
+		return SQFS_STOP_READDIR;
+	}
+
+	/* Set entry name */
+	strncpy((*dentp)->name, dirs->entry->name, dirs->entry->name_size + 1);
+	(*dentp)->name[dirs->entry->name_size + 1] = '\0';
+
+	offset = dirs->entry->name_size + 1 + SQFS_ENTRY_BASE_LENGTH;
+	dirs->entry_count--;
+
+	/* Decrement size to be read */
+	if (dirs->size > offset)
+		dirs->size -= offset;
+	else
+		dirs->size = 0;
+
+	/* Keep a reference to the current entry before incrementing it */
+	dirs->table += offset;
+
+	return SQFS_CONTINUE_READDIR;
+}
+
+int sqfs_probe(struct blk_desc *fs_dev_desc, disk_partition_t *fs_partition)
+{
+	struct squashfs_super_block *sblk;
+
+	cur_dev = fs_dev_desc;
+	cur_part_info = *fs_partition;
+	sblk = malloc_cache_aligned(cur_dev->blksz);
+
+	if (!sblk)
+		return -ENOMEM;
+
+	/* Read SquashFS super block */
+	if (sqfs_disk_read(0, 1, sblk) != 1) {
+		free(sblk);
+		cur_dev = NULL;
+		return -EINVAL;
+	}
+
+	/* Make sure it has a valid SquashFS magic number*/
+	if (sblk->s_magic != SQFS_MAGIC_NUMBER) {
+		printf("Bad magic number for SquashFS image.\n");
+		cur_dev = NULL;
+		return -EINVAL;
+	}
+
+	free(sblk);
+
+	return 0;
+}
+
+static char *sqfs_basename(char *path)
+{
+	char *fname;
+
+	fname = path + strlen(path) - 1;
+	while (fname >= path) {
+		if (*fname == '/') {
+			fname++;
+			break;
+		}
+
+		fname--;
+	}
+
+	return fname;
+}
+
+static char *sqfs_dirname(char *path)
+{
+	char *fname;
+
+	fname = sqfs_basename(path);
+	--fname;
+	*fname = '\0';
+
+	return path;
+}
+
+/*
+ * Takes a path to file and splits it in two parts: the filename itself and the
+ * directory's path, e.g.:
+ * path: /path/to/file.txt
+ * file: file.txt
+ * dir: /path/to
+ */
+static int sqfs_split_path(char **file, char **dir, const char *path)
+{
+	char *dirc, *basec, *bname, *dname, *tmp_path;
+	int ret = 0;
+
+	/* check for first slash in path*/
+	if (path[0] == '/') {
+		tmp_path = strdup(path);
+		if (!tmp_path)
+			return -ENOMEM;
+	} else {
+		tmp_path = malloc(strlen(path) + 2);
+		if (!tmp_path)
+			return -ENOMEM;
+		tmp_path[0] = '/';
+		strcpy(tmp_path + 1, path);
+	}
+
+	/* String duplicates */
+	dirc = strdup(tmp_path);
+	if (!dirc) {
+		ret = -ENOMEM;
+		goto free_tmp;
+	}
+
+	basec = strdup(tmp_path);
+	if (!basec) {
+		ret = -ENOMEM;
+		goto free_dirc;
+	}
+
+	dname = sqfs_dirname(dirc);
+	bname = sqfs_basename(basec);
+
+	*file = strdup(bname);
+
+	if (!*file) {
+		ret = -ENOMEM;
+		goto free_basec;
+	}
+
+	if (*dname == '\0') {
+		*dir = malloc(2);
+		if (!*dir) {
+			ret = -ENOMEM;
+			goto free_basec;
+		}
+
+		(*dir)[0] = '/';
+		(*dir)[1] = '\0';
+	} else {
+		*dir = strdup(dname);
+		if (!*dir) {
+			ret = -ENOMEM;
+			goto free_basec;
+		}
+	}
+
+free_basec:
+	free(basec);
+free_dirc:
+	free(dirc);
+free_tmp:
+	free(tmp_path);
+
+	return ret;
+}
+
+static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
+				 struct squashfs_file_info *finfo,
+				 struct squashfs_fragment_block_entry *fentry,
+				 __le32 blksz)
+{
+	int datablk_count = 0, ret;
+
+	finfo->size = le32_to_cpu(reg->file_size);
+	finfo->offset = le32_to_cpu(reg->offset);
+	finfo->start = le32_to_cpu(reg->start_block);
+	finfo->frag = SQFS_IS_FRAGMENTED(le32_to_cpu(reg->fragment));
+
+	if (finfo->frag) {
+		datablk_count = finfo->size / le32_to_cpu(blksz);
+		ret = sqfs_frag_lookup(reg->fragment, fentry);
+		if (ret < 0)
+			return -EINVAL;
+		finfo->comp = true;
+	} else {
+		datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
+	}
+
+	finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
+	if (!finfo->blk_sizes)
+		return -ENOMEM;
+
+	return datablk_count;
+}
+
+static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
+				  struct squashfs_file_info *finfo,
+				  struct squashfs_fragment_block_entry *fentry,
+				 __le32 blksz)
+{
+	int datablk_count = 0, ret;
+
+	finfo->size = le64_to_cpu(lreg->file_size);
+	finfo->offset = le32_to_cpu(lreg->offset);
+	finfo->start = le64_to_cpu(lreg->start_block);
+	finfo->frag = SQFS_IS_FRAGMENTED(le32_to_cpu(lreg->fragment));
+
+	if (finfo->frag) {
+		datablk_count = finfo->size / le32_to_cpu(blksz);
+		ret = sqfs_frag_lookup(lreg->fragment, fentry);
+		if (ret < 0)
+			return -EINVAL;
+		finfo->comp = true;
+	} else {
+		datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
+	}
+
+	finfo->blk_sizes = malloc(datablk_count * sizeof(u32));
+	if (!finfo->blk_sizes)
+		return -ENOMEM;
+
+	return datablk_count;
+}
+
+int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
+	      loff_t *actread)
+{
+	char *dir, *fragment_block, *datablock = NULL, *data_buffer = NULL;
+	char *fragment, *file, *resolved, *data;
+	u64 start, n_blks, table_size, data_offset, table_offset;
+	int ret, j, i_number, comp_type, datablk_count = 0;
+	struct squashfs_fragment_block_entry frag_entry;
+	struct squashfs_directory_entry *entry = NULL;
+	struct squashfs_file_info finfo = {0};
+	struct squashfs_symlink_inode symlink;
+	struct fs_dir_stream *dirsp = NULL;
+	struct squashfs_super_block *sblk;
+	struct squashfs_dir_stream dirs;
+	struct squashfs_lreg_inode lreg;
+	struct squashfs_base_inode base;
+	struct squashfs_reg_inode reg;
+	unsigned long dest_len;
+	struct fs_dirent dent;
+	unsigned char *ipos;
+
+	*actread = 0;
+
+	/* Read SquashFS super block */
+	ret = sqfs_read_sblk(&sblk);
+	if (ret)
+		return ret;
+
+	comp_type = le16_to_cpu(sblk->compression);
+
+	/*
+	 * sqfs_opendir will uncompress inode and directory tables, and will
+	 * return a pointer to the directory that contains the requested file.
+	 */
+	sqfs_split_path(&file, &dir, filename);
+	ret = sqfs_opendir(dir, &dirs.fs_dirs);
+	if (ret) {
+		ret = CMD_RET_FAILURE;
+		goto free_paths;
+	}
+
+	dirsp = (struct fs_dir_stream *)&dirs;
+	dirs.dentp = &dent;
+	/* For now, only regular files are able to be loaded */
+	while (sqfs_readdir(dirsp, &dirs.dentp)) {
+		if (!strncmp(dent.name, file, strlen(dent.name))) {
+			ret = sqfs_read_entry(&entry, dirs.entry);
+			if (ret) {
+				ret = CMD_RET_FAILURE;
+				free(dirs.entry);
+				sqfs_closedir(dirsp);
+				goto free_paths;
+			}
+
+			break;
+		}
+
+		free(dirs.entry);
+	}
+
+	if (!entry) {
+		printf("File not found.\n");
+		*actread = 0;
+		sqfs_closedir(dirsp);
+		ret = CMD_RET_FAILURE;
+		goto free_paths;
+	}
+
+	i_number = dirs.dir_header->inode_number + entry->inode_offset;
+	ipos = sqfs_find_inode(dirs.inode_table, i_number, sblk->inodes,
+			       sblk->block_size);
+
+	memcpy(&base, ipos, sizeof(base));
+	switch (base.inode_type) {
+	case SQFS_REG_TYPE:
+		memcpy(&reg, ipos, sizeof(reg));
+		datablk_count = sqfs_get_regfile_info(&reg, &finfo, &frag_entry,
+						      sblk->block_size);
+		memcpy(finfo.blk_sizes, ipos + sizeof(reg),
+		       datablk_count * sizeof(u32));
+		break;
+	case SQFS_LREG_TYPE:
+		memcpy(&lreg, ipos, sizeof(lreg));
+		datablk_count = sqfs_get_lregfile_info(&lreg, &finfo,
+						       &frag_entry,
+						       sblk->block_size);
+		memcpy(finfo.blk_sizes, ipos + sizeof(lreg),
+		       datablk_count * sizeof(u32));
+		break;
+	case SQFS_SYMLINK_TYPE:
+	case SQFS_LSYMLINK_TYPE:
+		memcpy(&symlink, ipos, sizeof(symlink));
+		resolved = sqfs_resolve_symlink(&symlink, ipos, filename);
+		printf("%s - > %s\n", filename, resolved);
+		ret = sqfs_read(resolved, buf, offset, len, actread);
+		free(resolved);
+		if (ret)
+			ret = CMD_RET_FAILURE;
+		ret = CMD_RET_SUCCESS;
+		goto free_entry;
+	case SQFS_BLKDEV_TYPE:
+	case SQFS_CHRDEV_TYPE:
+	case SQFS_LBLKDEV_TYPE:
+	case SQFS_LCHRDEV_TYPE:
+	case SQFS_FIFO_TYPE:
+	case SQFS_SOCKET_TYPE:
+	case SQFS_LFIFO_TYPE:
+	case SQFS_LSOCKET_TYPE:
+	default:
+		printf("Unsupported entry type\n");
+		ret = CMD_RET_FAILURE;
+		goto free_entry;
+	}
+
+	if (datablk_count < 0) {
+		ret = CMD_RET_FAILURE;
+		goto free_entry;
+	}
+
+	/* If the user specifies a length, check its sanity */
+	if (len) {
+		if (len > finfo.size) {
+			ret = CMD_RET_FAILURE;
+			goto free_entry;
+		}
+
+		finfo.size = len;
+	}
+
+	if (datablk_count) {
+		data_offset = finfo.start;
+		datablock = malloc(sblk->block_size);
+		if (!datablock) {
+			ret = CMD_RET_FAILURE;
+			goto free_entry;
+		}
+	}
+
+	for (j = 0; j < datablk_count; j++) {
+		start = data_offset / cur_dev->blksz;
+		table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]);
+		table_offset = data_offset - (start * cur_dev->blksz);
+		n_blks = DIV_ROUND_UP(table_size + table_offset,
+				      cur_dev->blksz);
+
+		data_buffer = malloc_cache_aligned(n_blks * cur_dev->blksz);
+
+		if (!data_buffer) {
+			ret = CMD_RET_FAILURE;
+			goto free_datablk;
+		}
+
+		if (sqfs_disk_read(start, n_blks, data_buffer) < 0) {
+			/*
+			 * Tip: re-compile the SquashFS image with mksquashfs's
+			 * -b <block_size> option.
+			 */
+			printf("Error: too many data blocks or too large"\
+			       "SquashFS block size.\n");
+			ret = CMD_RET_FAILURE;
+			goto free_buffer;
+		}
+
+		data = data_buffer + table_offset;
+
+		/* Load the data */
+		if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) {
+			dest_len = sblk->block_size;
+			ret = sqfs_decompress(comp_type, datablock, &dest_len,
+					      data, table_size);
+			if (ret) {
+				ret = CMD_RET_FAILURE;
+				goto free_buffer;
+			}
+
+			memcpy(buf + offset + *actread, datablock, dest_len);
+			*actread += dest_len;
+		} else {
+			memcpy(buf + offset + *actread, data, table_size);
+			*actread += table_size;
+		}
+
+		data_offset += table_size;
+	}
+
+	free(finfo.blk_sizes);
+
+	/*
+	 * There is no need to continue if the file is not fragmented.
+	 */
+	if (!finfo.frag) {
+		ret = CMD_RET_SUCCESS;
+		goto free_buffer;
+	}
+
+	start = frag_entry.start / cur_dev->blksz;
+	table_size = SQFS_BLOCK_SIZE(frag_entry.size);
+	table_offset = frag_entry.start - (start * cur_dev->blksz);
+	n_blks = DIV_ROUND_UP(table_size + table_offset, cur_dev->blksz);
+
+	fragment = malloc_cache_aligned(n_blks * cur_dev->blksz);
+
+	if (!fragment) {
+		ret = CMD_RET_FAILURE;
+		goto free_buffer;
+	}
+
+	if (sqfs_disk_read(start, n_blks, fragment) < 0) {
+		ret = CMD_RET_FAILURE;
+		goto free_fragment;
+	}
+
+	/* File compressed and fragmented */
+	if (finfo.frag && finfo.comp) {
+		dest_len = sblk->block_size;
+		fragment_block = malloc(sblk->block_size);
+		if (!fragment_block) {
+			ret = CMD_RET_FAILURE;
+			goto free_fragment;
+		}
+
+		ret = sqfs_decompress(comp_type, fragment_block, &dest_len,
+				      (void *)fragment  + table_offset,
+				      frag_entry.size);
+		if (ret) {
+			free(fragment_block);
+			ret = CMD_RET_FAILURE;
+			goto free_fragment;
+		}
+
+		for (j = offset + *actread; j < finfo.size; j++) {
+			memcpy(buf + j, &fragment_block[finfo.offset + j], 1);
+			(*actread)++;
+		}
+
+		free(fragment_block);
+
+	} else if (finfo.frag && !finfo.comp) {
+		fragment_block = (void *)fragment + table_offset;
+
+		for (j = offset + *actread; j < finfo.size; j++) {
+			memcpy(buf + j, &fragment_block[finfo.offset + j], 1);
+			(*actread)++;
+		}
+	}
+
+free_fragment:
+	free(fragment);
+free_buffer:
+	if (datablk_count)
+		free(data_buffer);
+free_datablk:
+	if (datablk_count)
+		free(datablock);
+free_entry:
+	free(entry);
+free_paths:
+	free(file);
+	free(dir);
+	free(sblk);
+
+	return ret;
+}
+
+int sqfs_ls(const char *filename)
+{
+	int ret = 0, nfiles = 0, ndirs = 0;
+	struct squashfs_dir_stream dirs;
+	struct fs_dir_stream *dirsp;
+	struct fs_dirent dent;
+
+	dirsp = (struct fs_dir_stream *)&dirs;
+	ret = sqfs_opendir(filename, &dirs.fs_dirs);
+	if (ret) {
+		sqfs_closedir(dirsp);
+		return CMD_RET_FAILURE;
+	}
+
+	dirs.dentp = &dent;
+	dirsp = (struct fs_dir_stream *)&dirs;
+	while (sqfs_readdir(dirsp, &dirs.dentp)) {
+		switch (dent.type) {
+		case FS_DT_DIR:
+			printf("            %s/\n", dent.name);
+			ndirs++;
+			break;
+		case FS_DT_REG:
+			printf("%8lld    %s\n", dent.size, dent.name);
+			nfiles++;
+			break;
+		case FS_DT_LNK:
+			printf("<SYMLINK>   %s\n", dent.name);
+			nfiles++;
+			break;
+		case SQFS_MISC_ENTRY_TYPE:
+			printf("            %s\n", dent.name);
+			nfiles++;
+			break;
+		default:
+			break;
+		}
+
+		free(dirs.entry);
+	}
+
+	sqfs_closedir(dirsp);
+
+	return ret;
+}
+
+int sqfs_size(const char *filename, loff_t *size)
+{
+	struct squashfs_symlink_inode symlink;
+	struct fs_dir_stream *dirsp = NULL;
+	struct squashfs_super_block *sblk;
+	struct squashfs_base_inode base;
+	struct squashfs_dir_stream dirs;
+	struct squashfs_lreg_inode lreg;
+	struct squashfs_reg_inode reg;
+	char *dir, *file, *resolved;
+	struct fs_dirent dent;
+	unsigned char *ipos;
+	int ret, i_number;
+
+	/* Read SquashFS super block */
+	ret = sqfs_read_sblk(&sblk);
+	if (ret)
+		return ret;
+
+	sqfs_split_path(&file, &dir, filename);
+	/*
+	 * sqfs_opendir will uncompress inode and directory tables, and will
+	 * return a pointer to the directory that contains the requested file.
+	 */
+	ret = sqfs_opendir(dir, &dirs.fs_dirs);
+	if (ret) {
+		sqfs_closedir(dirsp);
+		ret = CMD_RET_FAILURE;
+		goto free_strings;
+	}
+
+	dirsp = (struct fs_dir_stream *)&dirs;
+	dirs.dentp = &dent;
+
+	while (sqfs_readdir(dirsp, &dirs.dentp)) {
+		ret = strcmp(dent.name, file);
+		if (!ret)
+			break;
+		free(dirs.entry);
+	}
+
+	if (ret) {
+		sqfs_closedir(dirsp);
+		ret = CMD_RET_FAILURE;
+		goto free_strings;
+	}
+
+	i_number = dirs.dir_header->inode_number + dirs.entry->inode_offset;
+	ipos = sqfs_find_inode(dirs.inode_table, i_number, sblk->inodes,
+			       sblk->block_size);
+	free(dirs.entry);
+
+	memcpy(&base, ipos, sizeof(base));
+	switch (base.inode_type) {
+	case SQFS_REG_TYPE:
+		memcpy(&reg, ipos, sizeof(reg));
+		*size = reg.file_size;
+		break;
+	case SQFS_LREG_TYPE:
+		memcpy(&lreg, ipos, sizeof(lreg));
+		*size = lreg.file_size;
+		break;
+	case SQFS_SYMLINK_TYPE:
+	case SQFS_LSYMLINK_TYPE:
+		memcpy(&symlink, ipos, sizeof(symlink));
+		resolved = sqfs_resolve_symlink(&symlink, ipos, filename);
+		ret = sqfs_size(resolved, size);
+		free(resolved);
+		if (ret)
+			ret = CMD_RET_FAILURE;
+		ret = CMD_RET_SUCCESS;
+		break;
+	case SQFS_BLKDEV_TYPE:
+	case SQFS_CHRDEV_TYPE:
+	case SQFS_LBLKDEV_TYPE:
+	case SQFS_LCHRDEV_TYPE:
+	case SQFS_FIFO_TYPE:
+	case SQFS_SOCKET_TYPE:
+	case SQFS_LFIFO_TYPE:
+	case SQFS_LSOCKET_TYPE:
+	default:
+		printf("Unable to recover entry's size.\n");
+		*size = 0;
+		break;
+	}
+
+free_strings:
+	free(dir);
+	free(file);
+
+	free(sblk);
+	sqfs_closedir(dirsp);
+
+	return 0;
+}
+
+void sqfs_close(void)
+{
+}
+
+void sqfs_closedir(struct fs_dir_stream *dirs)
+{
+	struct squashfs_dir_stream *sqfs_dirs;
+
+	sqfs_dirs = (struct squashfs_dir_stream *)dirs;
+	free(sqfs_dirs->inode_table);
+	free(sqfs_dirs->dir_table);
+	free(sqfs_dirs->dir_header);
+}
diff --git a/fs/squashfs/sqfs_decompressor.c b/fs/squashfs/sqfs_decompressor.c
new file mode 100644
index 0000000000..a899a5704b
--- /dev/null
+++ b/fs/squashfs/sqfs_decompressor.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sqfs_decompressor.h"
+#include "sqfs_filesystem.h"
+#include "sqfs_utils.h"
+
+int sqfs_decompress(u16 comp_type, void *dest, unsigned long *dest_len,
+		    void *source, u32 lenp)
+{
+	int ret = 0;
+
+	switch (comp_type) {
+	default:
+		printf("Error: unknown compression type.\n");
+		return -EINVAL;
+	}
+
+	return ret;
+}
diff --git a/fs/squashfs/sqfs_decompressor.h b/fs/squashfs/sqfs_decompressor.h
new file mode 100644
index 0000000000..378965dda8
--- /dev/null
+++ b/fs/squashfs/sqfs_decompressor.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#ifndef SQFS_DECOMPRESSOR_H
+#define SQFS_DECOMPRESSOR_H
+
+#include <stdint.h>
+
+#define SQFS_COMP_ZLIB 1
+#define SQFS_COMP_LZMA 2
+#define SQFS_COMP_LZO 3
+#define SQFS_COMP_XZ 4
+#define SQFS_COMP_LZ4 5
+#define SQFS_COMP_ZSTD 6
+
+/* LZMA does not support any compression options */
+
+struct squashfs_gzip_opts {
+	u32 compression_level;
+	u16 window_size;
+	u16 strategies;
+};
+
+struct squashfs_xz_opts {
+	u32 dictionary_size;
+	u32 executable_filters;
+};
+
+struct squashfs_lz4_opts {
+	u32 version;
+	u32 flags;
+};
+
+struct squashfs_zstd_opts {
+	u32 compression_level;
+};
+
+struct squashfs_lzo_opts {
+	u32 algorithm;
+	u32 level;
+};
+
+union squashfs_compression_opts {
+	struct squashfs_gzip_opts *gzip;
+	struct squashfs_xz_opts *xz;
+	struct squashfs_lz4_opts *lz4;
+	struct squashfs_zstd_opts *zstd;
+	struct squashfs_lzo_opts *lzo;
+};
+
+int sqfs_decompress(u16 comp_type, void *dest, unsigned long *dest_len,
+		    void *source, u32 lenp);
+
+#endif /* SQFS_DECOMPRESSOR_H */
diff --git a/fs/squashfs/sqfs_dir.c b/fs/squashfs/sqfs_dir.c
new file mode 100644
index 0000000000..4a72191f88
--- /dev/null
+++ b/fs/squashfs/sqfs_dir.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#include <errno.h>
+#include <linux/types.h>
+#include <linux/byteorder/little_endian.h>
+#include <linux/byteorder/generic.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sqfs_filesystem.h"
+#include "sqfs_utils.h"
+
+bool sqfs_is_dir(__le16 type)
+{
+	return (le16_to_cpu(type) == SQFS_DIR_TYPE) ||
+		(le16_to_cpu(type) == SQFS_LDIR_TYPE);
+}
+
+/*
+ * Receives a pointer (void *) to a position in the inode table containing the
+ * directory's inode. Returns directory inode offset into the directory table.
+ * m_list contains each metadata block's position, and m_count is the number of
+ * elements of m_list. Those metadata blocks come from the compressed directory
+ * table.
+ */
+int sqfs_dir_offset(void *dir_i, u32 *m_list, int m_count)
+{
+	struct squashfs_base_inode base;
+	struct squashfs_ldir_inode ldir;
+	struct squashfs_dir_inode dir;
+	u32 start_block;
+	u16 offset;
+	int j;
+
+	memcpy(&base, dir_i, sizeof(base));
+
+	switch (base.inode_type) {
+	case SQFS_DIR_TYPE:
+		memcpy(&dir, dir_i, sizeof(dir));
+		start_block = le32_to_cpu(dir.start_block);
+		offset = le16_to_cpu(dir.offset);
+		break;
+	case SQFS_LDIR_TYPE:
+		memcpy(&ldir, dir_i, sizeof(ldir));
+		start_block = le32_to_cpu(ldir.start_block);
+		offset = le16_to_cpu(ldir.offset);
+		break;
+	default:
+		printf("Error: this is not a directory.\n");
+		return -EINVAL;
+	}
+
+	for (j = 0; j < m_count; j++) {
+		if (m_list[j] == start_block)
+			return (++j * SQFS_METADATA_BLOCK_SIZE) + offset;
+	}
+
+	if (start_block == 0)
+		return offset;
+
+	printf("Error: invalid inode reference to directory table.\n");
+
+	return -EINVAL;
+}
+
+bool sqfs_is_empty_dir(void *dir_i)
+{
+	struct squashfs_base_inode *base;
+	struct squashfs_ldir_inode *ldir;
+	struct squashfs_dir_inode *dir;
+	u32 file_size;
+
+	base = malloc(sizeof(*base));
+	if (!base)
+		return errno;
+
+	memcpy(base, dir_i, sizeof(*base));
+
+	switch (le16_to_cpu(base->inode_type)) {
+	case SQFS_DIR_TYPE:
+		dir = malloc(sizeof(*dir));
+		memcpy(dir, dir_i, sizeof(*dir));
+		file_size = le16_to_cpu(dir->file_size);
+		free(dir);
+		break;
+	case SQFS_LDIR_TYPE:
+		ldir = malloc(sizeof(*ldir));
+		memcpy(ldir, dir_i, sizeof(*ldir));
+		file_size = le32_to_cpu(ldir->file_size);
+		free(ldir);
+		break;
+	default:
+		printf("Error: this is not a directory.\n");
+		free(base);
+		return false;
+	}
+
+	free(base);
+
+	return file_size == SQFS_EMPTY_FILE_SIZE;
+}
diff --git a/fs/squashfs/sqfs_filesystem.h b/fs/squashfs/sqfs_filesystem.h
new file mode 100644
index 0000000000..638d4f7d72
--- /dev/null
+++ b/fs/squashfs/sqfs_filesystem.h
@@ -0,0 +1,301 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#ifndef SQFS_FILESYSTEM_H
+#define SQFS_FILESYSTEM_H
+
+#include <asm/unaligned.h>
+#include <stdint.h>
+#include <fs.h>
+
+#define SQFS_UNCOMPRESSED_DATA 0x0002
+#define SQFS_MAGIC_NUMBER 0x73717368
+/* The three first members of squashfs_dir_index make a total of 12 bytes */
+#define SQFS_DIR_INDEX_BASE_LENGTH 12
+/* size of metadata (inode and directory) blocks */
+#define SQFS_METADATA_BLOCK_SIZE 8192
+/* Max. number of fragment entries in a metadata block is 512 */
+#define SQFS_MAX_ENTRIES 512
+/* Metadata blocks start by a 2-byte length header */
+#define SQFS_HEADER_SIZE 2
+#define SQFS_LREG_INODE_MIN_SIZE 56
+#define SQFS_DIR_HEADER_SIZE 12
+#define SQFS_MISC_ENTRY_TYPE -1
+#define SQFS_EMPTY_FILE_SIZE 3
+#define SQFS_STOP_READDIR 0
+#define SQFS_CONTINUE_READDIR 1
+#define SQFS_EMPTY_DIR -1
+/*
+ * A directory entry object has a fixed length of 8 bytes, corresponding to its
+ * first four members, plus the size of the entry name, which is equal to
+ * 'entry_name' + 1 bytes.
+ */
+#define SQFS_ENTRY_BASE_LENGTH 8
+/* Inode types */
+#define SQFS_DIR_TYPE 1
+#define SQFS_REG_TYPE 2
+#define SQFS_SYMLINK_TYPE 3
+#define SQFS_BLKDEV_TYPE 4
+#define SQFS_CHRDEV_TYPE 5
+#define SQFS_FIFO_TYPE 6
+#define SQFS_SOCKET_TYPE 7
+#define SQFS_LDIR_TYPE 8
+#define SQFS_LREG_TYPE 9
+#define SQFS_LSYMLINK_TYPE 10
+#define SQFS_LBLKDEV_TYPE 11
+#define SQFS_LCHRDEV_TYPE 12
+#define SQFS_LFIFO_TYPE 13
+#define SQFS_LSOCKET_TYPE 14
+
+struct squashfs_super_block {
+	__le32 s_magic;
+	__le32 inodes;
+	__le32 mkfs_time;
+	__le32 block_size;
+	__le32 fragments;
+	__le16 compression;
+	__le16 block_log;
+	__le16 flags;
+	__le16 no_ids;
+	__le16 s_major;
+	__le16 s_minor;
+	__le64 root_inode;
+	__le64 bytes_used;
+	__le64 id_table_start;
+	__le64 xattr_id_table_start;
+	__le64 inode_table_start;
+	__le64 directory_table_start;
+	__le64 fragment_table_start;
+	__le64 export_table_start;
+};
+
+struct squashfs_directory_index {
+	u32 index;
+	u32 start;
+	u32 size;
+	char name[0];
+};
+
+struct squashfs_base_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+};
+
+struct squashfs_ipc_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le32 nlink;
+};
+
+struct squashfs_lipc_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le32 nlink;
+	__le32 xattr;
+};
+
+struct squashfs_dev_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le32 nlink;
+	__le32 rdev;
+};
+
+struct squashfs_ldev_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le32 nlink;
+	__le32 rdev;
+	__le32 xattr;
+};
+
+struct squashfs_symlink_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le32 nlink;
+	__le32 symlink_size;
+	char symlink[0];
+};
+
+struct squashfs_reg_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le32 start_block;
+	__le32 fragment;
+	__le32 offset;
+	__le32 file_size;
+	__le32 block_list[0];
+};
+
+struct squashfs_lreg_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le64 start_block;
+	__le64 file_size;
+	__le64 sparse;
+	__le32 nlink;
+	__le32 fragment;
+	__le32 offset;
+	__le32 xattr;
+	__le32 block_list[0];
+};
+
+struct squashfs_dir_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le32 start_block;
+	__le32 nlink;
+	__le16 file_size;
+	__le16 offset;
+	__le32 parent_inode;
+};
+
+struct squashfs_ldir_inode {
+	__le16 inode_type;
+	__le16 mode;
+	__le16 uid;
+	__le16 guid;
+	__le32 mtime;
+	__le32 inode_number;
+	__le32 nlink;
+	__le32 file_size;
+	__le32 start_block;
+	__le32 parent_inode;
+	__le16 i_count;
+	__le16 offset;
+	__le32 xattr;
+	struct squashfs_directory_index index[0];
+};
+
+union squashfs_inode {
+	struct squashfs_base_inode *base;
+	struct squashfs_dev_inode *dev;
+	struct squashfs_ldev_inode *ldev;
+	struct squashfs_symlink_inode *symlink;
+	struct squashfs_reg_inode *reg;
+	struct squashfs_lreg_inode *lreg;
+	struct squashfs_dir_inode *dir;
+	struct squashfs_ldir_inode *ldir;
+	struct squashfs_ipc_inode *ipc;
+	struct squashfs_lipc_inode *lipc;
+};
+
+struct squashfs_directory_entry {
+	u16 offset;
+	u16 inode_offset;
+	u16 type;
+	u16 name_size;
+	char name[0];
+};
+
+struct squashfs_directory_header {
+	u32 count;
+	u32 start;
+	u32 inode_number;
+};
+
+struct squashfs_fragment_block_entry {
+	u64 start;
+	u32 size;
+	u32 _unused;
+};
+
+struct squashfs_dir_stream {
+	struct fs_dir_stream *fs_dirs;
+	struct fs_dirent *dentp;
+	/*
+	 * 'size' is the uncompressed size of the entire listing, including
+	 * headers. 'entry_count' is the number of entries following a
+	 * specific header. Both variables are decremented in sqfs_readdir() so
+	 * the function knows when the end of the directory is reached.
+	 */
+	size_t size;
+	int entry_count;
+	/* SquashFS structures */
+	struct squashfs_directory_header *dir_header;
+	struct squashfs_directory_entry *entry;
+	/*
+	 * 'table' points to a position into the directory table. Both 'table'
+	 * and 'inode' are defined for the first time in sqfs_opendir().
+	 * 'table's value changes in sqfs_readdir().
+	 */
+	unsigned char *table;
+	union squashfs_inode i;
+	struct squashfs_dir_inode i_dir;
+	struct squashfs_ldir_inode i_ldir;
+	/*
+	 * References to the tables' beginnings. They are assigned in
+	 * sqfs_opendir() and freed in sqfs_closedir().
+	 */
+	unsigned char *inode_table;
+	unsigned char *dir_table;
+};
+
+struct squashfs_file_info {
+	/* File size in bytes (uncompressed) */
+	size_t size;
+	/* Reference to list of data blocks's sizes */
+	u32 *blk_sizes;
+	/* Offset into the fragment block */
+	u32 offset;
+	/* Offset in which the data blocks begin */
+	u64 start;
+	/* Is file fragmented? */
+	bool frag;
+	/* Compressed fragment */
+	bool comp;
+};
+
+void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count,
+		      __le32 block_size);
+
+int sqfs_dir_offset(void *dir_i, u32 *m_list, int m_count);
+
+int sqfs_read_metablock(unsigned char *file_mapping, int offset,
+			bool *compressed, u32 *data_size);
+
+bool sqfs_is_empty_dir(void *dir_i);
+
+bool sqfs_is_dir(__le16 type);
+
+#endif /* SQFS_FILESYSTEM_H */
diff --git a/fs/squashfs/sqfs_inode.c b/fs/squashfs/sqfs_inode.c
new file mode 100644
index 0000000000..dbde74a750
--- /dev/null
+++ b/fs/squashfs/sqfs_inode.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#include <asm/unaligned.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sqfs_decompressor.h"
+#include "sqfs_filesystem.h"
+#include "sqfs_utils.h"
+
+/*
+ * Given the uncompressed inode table, the inode to be found and the number of
+ * inodes in the table, return inode position in case of success.
+ */
+void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count,
+		      __le32 block_size)
+{
+	int k, l, blk_list_size = 0, offset = 0, index_list_size = 0;
+	struct squashfs_symlink_inode symlink;
+	struct squashfs_base_inode base;
+	struct squashfs_ldev_inode ldev;
+	struct squashfs_lreg_inode lreg;
+	struct squashfs_ldir_inode ldir;
+	struct squashfs_lipc_inode lipc;
+	struct squashfs_dev_inode dev;
+	struct squashfs_reg_inode reg;
+	struct squashfs_dir_inode dir;
+	struct squashfs_ipc_inode ipc;
+
+	if (!inode_table) {
+		printf("%s: Invalid pointer to inode table.\n", __func__);
+		return NULL;
+	}
+
+	for (k = 0; k < le32_to_cpu(inode_count); k++) {
+		memcpy(&base, inode_table + offset, sizeof(base));
+		if (le32_to_cpu(base.inode_number) == inode_number)
+			return (void *)(inode_table + offset);
+
+		switch (base.inode_type) {
+		case SQFS_DIR_TYPE:
+			memcpy(&dir, inode_table + offset, sizeof(dir));
+			offset += sizeof(dir);
+			break;
+		case SQFS_REG_TYPE:
+			memcpy(&reg, inode_table + offset, sizeof(reg));
+			if (SQFS_IS_FRAGMENTED(reg.fragment)) {
+				blk_list_size = (reg.file_size / block_size);
+			} else {
+				blk_list_size = DIV_ROUND_UP(reg.file_size,
+							     block_size);
+			}
+
+			offset += sizeof(reg) + blk_list_size * sizeof(u32);
+			break;
+		case SQFS_LDIR_TYPE:
+			memcpy(&ldir, inode_table + offset, sizeof(ldir));
+			if (ldir.i_count == 0) {
+				offset += sizeof(ldir);
+				break;
+			}
+
+			for (l = 0; l < ldir.i_count + 1; l++)
+				index_list_size += ldir.index[l].size + 1;
+
+			offset += sizeof(ldir) + index_list_size +
+				(ldir.i_count + 1) * SQFS_DIR_INDEX_BASE_LENGTH;
+			index_list_size = 0;
+			break;
+		case SQFS_LREG_TYPE:
+			memcpy(&lreg, inode_table + offset, sizeof(lreg));
+			if (lreg.fragment == 0xFFFFFFFF) {
+				blk_list_size = DIV_ROUND_UP(lreg.file_size,
+							     block_size);
+			} else {
+				blk_list_size = (lreg.file_size / block_size);
+			}
+
+			offset += sizeof(lreg) + blk_list_size * sizeof(u32);
+			break;
+		case SQFS_SYMLINK_TYPE:
+		case SQFS_LSYMLINK_TYPE:
+			memcpy(&symlink, inode_table + offset, sizeof(symlink));
+			offset += sizeof(symlink) + symlink.symlink_size;
+			break;
+		case SQFS_BLKDEV_TYPE:
+		case SQFS_CHRDEV_TYPE:
+			memcpy(&dev, inode_table + offset, sizeof(dev));
+			offset += sizeof(dev);
+			break;
+		case SQFS_LBLKDEV_TYPE:
+		case SQFS_LCHRDEV_TYPE:
+			memcpy(&ldev, inode_table + offset, sizeof(ldev));
+			offset += sizeof(ldev);
+			break;
+		case SQFS_FIFO_TYPE:
+		case SQFS_SOCKET_TYPE:
+			memcpy(&ipc, inode_table + offset, sizeof(ipc));
+			offset += sizeof(ipc);
+			break;
+		case SQFS_LFIFO_TYPE:
+		case SQFS_LSOCKET_TYPE:
+			memcpy(&lipc, inode_table + offset, sizeof(lipc));
+			offset += sizeof(lipc);
+			break;
+		default:
+			printf("Error while searching inode: unknown type.\n");
+			return NULL;
+		}
+	}
+
+	printf("Inode not found.\n");
+
+	return NULL;
+}
+
+int sqfs_read_metablock(unsigned char *file_mapping, int offset,
+			bool *compressed, u32 *data_size)
+{
+	unsigned char *data;
+	u16 header;
+
+	data = file_mapping + offset;
+	header = get_unaligned((u16 *)data);
+	*compressed = SQFS_COMPRESSED_METADATA(header);
+	*data_size = SQFS_METADATA_SIZE(header);
+
+	if (*data_size > SQFS_METADATA_BLOCK_SIZE) {
+		printf("Invalid metatada block size: %d bytes.\n", *data_size);
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/fs/squashfs/sqfs_utils.h b/fs/squashfs/sqfs_utils.h
new file mode 100644
index 0000000000..1260abe22b
--- /dev/null
+++ b/fs/squashfs/sqfs_utils.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ */
+
+#ifndef SQFS_UTILS_H
+#define SQFS_UTILS_H
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <stdbool.h>
+
+#define SQFS_FRAGMENT_INDEX_OFFSET(A) ((A) % SQFS_MAX_ENTRIES)
+#define SQFS_FRAGMENT_INDEX(A) ((A) / SQFS_MAX_ENTRIES)
+#define SQFS_BLOCK_SIZE(A) ((A) & GENMASK(23, 0))
+#define SQFS_CHECK_FLAG(flag, bit) (((flag) >> (bit)) & 1)
+/* Useful for both fragment and data blocks */
+#define SQFS_COMPRESSED_BLOCK(A) (!((A) & BIT(24)))
+/* SQFS_COMPRESSED_DATA strictly used with super block's 'flags' member */
+#define SQFS_COMPRESSED_DATA(A) (!((A) & 0x0002))
+#define SQFS_IS_FRAGMENTED(A) ((A) != 0xFFFFFFFF)
+/*
+ * These two macros work as getters for a metada block header, retrieving the
+ * data size and if it is compressed/uncompressed
+ */
+#define SQFS_COMPRESSED_METADATA(A) (!((A) & BIT(15)))
+#define SQFS_METADATA_SIZE(A) ((A) & GENMASK(14, 0))
+
+struct squashfs_super_block_flags {
+	/* check: unused
+	 * uncompressed_ids: not supported
+	 */
+	bool uncompressed_inodes;
+	bool uncompressed_data;
+	bool check;
+	bool uncompressed_frags;
+	bool no_frags;
+	bool always_frags;
+	bool duplicates;
+	bool exportable;
+	bool uncompressed_xattrs;
+	bool no_xattrs;
+	bool compressor_options;
+	bool uncompressed_ids;
+};
+
+#endif /* SQFS_UTILS_H  */
diff --git a/include/fs.h b/include/fs.h
index 37e35c2120..5ee3b0a651 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -13,6 +13,7 @@
 #define FS_TYPE_SANDBOX	3
 #define FS_TYPE_UBIFS	4
 #define FS_TYPE_BTRFS	5
+#define FS_TYPE_SQUASHFS 6

 /**
  * do_fat_fsload - Run the fatload command
@@ -36,6 +37,18 @@ int do_fat_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
  */
 int do_ext2load(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);

+
+/**
+ * do_sqfs_load - Run the sqfsload command
+ *
+ * @cmdtp: Command information for sqfsload
+ * @flag: Command flags (CMD_FLAG_...)
+ * @argc: Number of arguments
+ * @argv: List of arguments
+ * @return result (see enum command_ret_t)
+ */
+int do_sqfs_load(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
+
 /*
  * Tell the fs layer which block device an partition to use for future
  * commands. This also internally identifies the filesystem that is present
diff --git a/include/squashfs.h b/include/squashfs.h
new file mode 100644
index 0000000000..ec59eee8d8
--- /dev/null
+++ b/include/squashfs.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ *
+ * squashfs.h: SquashFS filesystem implementation.
+ */
+
+#ifndef _SQFS_H_
+#define _SQFS_H_
+
+int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp);
+int sqfs_ls(const char *filename);
+int sqfs_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
+int sqfs_probe(struct blk_desc *fs_dev_desc,
+	       disk_partition_t *fs_partition);
+int sqfs_read(const char *filename, void *buf, loff_t offset,
+	      loff_t len, loff_t *actread);
+int sqfs_size(const char *filename, loff_t *size);
+void sqfs_close(void);
+void sqfs_closedir(struct fs_dir_stream *dirs);
+
+#endif /* SQFS_H  */
--
2.17.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/4] fs/squashfs: add filesystem commands
  2020-07-09 17:51 [PATCH 0/4] Add support for the SquashFS filesystem Joao Marcos Costa
  2020-07-09 17:51 ` [PATCH 1/4] fs/squashfs: new filesystem Joao Marcos Costa
@ 2020-07-09 17:51 ` Joao Marcos Costa
  2020-07-09 17:51 ` [PATCH 3/4] fs/squashfs: add sources for zlib decompression Joao Marcos Costa
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 20+ messages in thread
From: Joao Marcos Costa @ 2020-07-09 17:51 UTC (permalink / raw)
  To: u-boot

Add 'ls' (sqfsls) and 'load' (sqfsload) commands.

Signed-off-by: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
---
 cmd/Kconfig  |  6 ++++++
 cmd/Makefile |  1 +
 cmd/sqfs.c   | 43 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+)
 create mode 100644 cmd/sqfs.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 6403bc45a5..fa2a697dea 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1993,6 +1993,12 @@ config CMD_FAT
 	help
 	  Support for the FAT fs

+config CMD_SQUASHFS
+	bool "SquashFS command support"
+	select FS_SQUASHFS
+	help
+	 Enables SquashFS filesystem commands (e.g. load, ls).
+
 config CMD_FS_GENERIC
 	bool "filesystem commands"
 	help
diff --git a/cmd/Makefile b/cmd/Makefile
index f1dd513a4b..0553b0b70e 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_CMD_EXT4) += ext4.o
 obj-$(CONFIG_CMD_EXT2) += ext2.o
 obj-$(CONFIG_CMD_FAT) += fat.o
 obj-$(CONFIG_CMD_FDT) += fdt.o
+obj-$(CONFIG_CMD_SQUASHFS) += sqfs.o
 obj-$(CONFIG_CMD_FITUPD) += fitupd.o
 obj-$(CONFIG_CMD_FLASH) += flash.o
 obj-$(CONFIG_CMD_FPGA) += fpga.o
diff --git a/cmd/sqfs.c b/cmd/sqfs.c
new file mode 100644
index 0000000000..af42df728b
--- /dev/null
+++ b/cmd/sqfs.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Bootlin
+ *
+ * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+ *
+ * squashfs.c:	implements SquashFS related commands
+ */
+
+#include <fs.h>
+#include <squashfs.h>
+
+int do_sqfs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	return do_ls(cmdtp, flag, argc, argv, FS_TYPE_SQUASHFS);
+}
+
+U_BOOT_CMD(
+	sqfsls,	4, 1, do_sqfs_ls,
+	"List files in directory. Default: root (/).",
+	"<interface> [<dev[:part]>] [directory]\n"\
+	"    - list files from 'dev' on 'interface' in 'directory'\n"
+);
+
+int do_sqfs_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	return do_load(cmdtp, flag, argc, argv, FS_TYPE_SQUASHFS);
+}
+
+U_BOOT_CMD(
+	sqfsload, 7, 0, do_sqfs_load,
+	"load binary file from a SquashFS filesystem",
+	"<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n"
+	"    - Load binary file 'filename' from 'dev' on 'interface'\n"
+	"      to address 'addr' from SquashFS filesystem.\n"
+	"      'pos' gives the file position to start loading from.\n"
+	"      If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
+	"      'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
+	"      the load stops on end of file.\n"
+	"      If either 'pos' or 'bytes' are not aligned to\n"
+	"      ARCH_DMA_MINALIGN then a misaligned buffer warning will\n"
+	"      be printed and performance will suffer for the load."
+);
--
2.17.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 3/4] fs/squashfs: add sources for zlib decompression
  2020-07-09 17:51 [PATCH 0/4] Add support for the SquashFS filesystem Joao Marcos Costa
  2020-07-09 17:51 ` [PATCH 1/4] fs/squashfs: new filesystem Joao Marcos Costa
  2020-07-09 17:51 ` [PATCH 2/4] fs/squashfs: add filesystem commands Joao Marcos Costa
@ 2020-07-09 17:51 ` Joao Marcos Costa
  2020-07-15 19:37   ` Thomas Petazzoni
  2020-07-09 17:51 ` [PATCH 4/4] fs/squashfs: add support " Joao Marcos Costa
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: Joao Marcos Costa @ 2020-07-09 17:51 UTC (permalink / raw)
  To: u-boot

Add zlib (v1.2.11) functions to U-Boot. SquashFS depends on those
functions to decompress data from a raw disk image. The actual support
for zlib into SquashFS sources will be added in a follow-up commit.

Signed-off-by: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
---
 include/u-boot/zlib.h | 32 +++++++++++++++
 lib/zlib/uncompr.c    | 93 +++++++++++++++++++++++++++++++++++++++++++
 lib/zlib/zlib.c       |  1 +
 3 files changed, 126 insertions(+)
 create mode 100644 lib/zlib/uncompr.c

diff --git a/include/u-boot/zlib.h b/include/u-boot/zlib.h
index e23ceb50ca..65a9e1c9a5 100644
--- a/include/u-boot/zlib.h
+++ b/include/u-boot/zlib.h
@@ -110,6 +110,12 @@ extern "C" {
 #  define voidp                 z_voidp
 #endif

+#if defined(ZLIB_CONST) && !defined(z_const)
+#  define z_const const
+#else
+#  define z_const
+#endif
+
 #if defined(__MSDOS__) && !defined(MSDOS)
 #  define MSDOS
 #endif
@@ -710,6 +716,32 @@ ZEXTERN  uInt ZEXPORT crc32  OF((uInt crc, const Bytef *buf, uInt len));
      if (crc != original_crc) error();
 */

+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
+                                   const Bytef *source, uLong sourceLen));
+/*
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer.  Upon entry, destLen is the total size
+   of the destination buffer, which must be large enough to hold the entire
+   uncompressed data.  (The size of the uncompressed data must have been saved
+   previously by the compressor and transmitted to the decompressor by some
+   mechanism outside the scope of this compression library.) Upon exit, destLen
+   is the actual size of the uncompressed data.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.  In
+   the case where there is not enough room, uncompress() will fill the output
+   buffer with the uncompressed data up to that point.
+*/
+
+ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest,   uLongf *destLen,
+                                    const Bytef *source, uLong *sourceLen));
+/*
+     Same as uncompress, except that sourceLen is a pointer, where the
+   length of the source is *sourceLen.  On return, *sourceLen is the number of
+   source bytes consumed.
+*/
+
 ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
                                       const char *version, int stream_size));
 #define inflateInit(strm) \
diff --git a/lib/zlib/uncompr.c b/lib/zlib/uncompr.c
new file mode 100644
index 0000000000..f03a1a865e
--- /dev/null
+++ b/lib/zlib/uncompr.c
@@ -0,0 +1,93 @@
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+     Decompresses the source buffer into the destination buffer.  *sourceLen is
+   the byte length of the source buffer. Upon entry, *destLen is the total size
+   of the destination buffer, which must be large enough to hold the entire
+   uncompressed data. (The size of the uncompressed data must have been saved
+   previously by the compressor and transmitted to the decompressor by some
+   mechanism outside the scope of this compression library.) Upon exit,
+   *destLen is the size of the decompressed data and *sourceLen is the number
+   of source bytes consumed. Upon return, source + *sourceLen points to the
+   first unused input byte.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer, or
+   Z_DATA_ERROR if the input data was corrupted, including if the input data is
+   an incomplete zlib stream.
+*/
+int ZEXPORT uncompress2 (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong *sourceLen;
+{
+    z_stream stream;
+    int err;
+    const uInt max = (uInt)-1;
+    uLong len, left;
+    Byte buf[1];    /* for detection of incomplete stream when *destLen == 0 */
+
+    len = *sourceLen;
+    if (*destLen) {
+        left = *destLen;
+        *destLen = 0;
+    }
+    else {
+        left = 1;
+        dest = buf;
+    }
+
+    stream.next_in = (z_const Bytef *)source;
+    stream.avail_in = 0;
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+    stream.opaque = (voidpf)0;
+
+    err = inflateInit(&stream);
+    if (err != Z_OK) return err;
+
+    stream.next_out = dest;
+    stream.avail_out = 0;
+
+    do {
+        if (stream.avail_out == 0) {
+            stream.avail_out = left > (uLong)max ? max : (uInt)left;
+            left -= stream.avail_out;
+        }
+        if (stream.avail_in == 0) {
+            stream.avail_in = len > (uLong)max ? max : (uInt)len;
+            len -= stream.avail_in;
+        }
+        err = inflate(&stream, Z_NO_FLUSH);
+    } while (err == Z_OK);
+
+    *sourceLen -= len + stream.avail_in;
+    if (dest != buf)
+        *destLen = stream.total_out;
+    else if (stream.total_out && err == Z_BUF_ERROR)
+        left = 1;
+
+    inflateEnd(&stream);
+    return err == Z_STREAM_END ? Z_OK :
+           err == Z_NEED_DICT ? Z_DATA_ERROR  :
+           err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR :
+           err;
+}
+
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    return uncompress2(dest, destLen, source, &sourceLen);
+}
diff --git a/lib/zlib/zlib.c b/lib/zlib/zlib.c
index 7e1570292c..05a2734086 100644
--- a/lib/zlib/zlib.c
+++ b/lib/zlib/zlib.c
@@ -30,3 +30,4 @@
 #include "inflate.c"
 #include "zutil.c"
 #include "adler32.c"
+#include "uncompr.c"
--
2.17.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 4/4] fs/squashfs: add support for zlib decompression
  2020-07-09 17:51 [PATCH 0/4] Add support for the SquashFS filesystem Joao Marcos Costa
                   ` (2 preceding siblings ...)
  2020-07-09 17:51 ` [PATCH 3/4] fs/squashfs: add sources for zlib decompression Joao Marcos Costa
@ 2020-07-09 17:51 ` Joao Marcos Costa
  2020-07-15 20:11 ` [PATCH 0/3] squashfs fixes Thomas Petazzoni
  2020-07-15 20:22 ` [PATCH] fs/squashfs/sqfs.c: use sqfs_read_sblk() in sqfs_probe() Thomas Petazzoni
  5 siblings, 0 replies; 20+ messages in thread
From: Joao Marcos Costa @ 2020-07-09 17:51 UTC (permalink / raw)
  To: u-boot

Add call to zlib's 'uncompress' function. Add function to display the
right error message depending on the decompression's return value.

Signed-off-by: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
---
 fs/squashfs/sqfs_decompressor.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/fs/squashfs/sqfs_decompressor.c b/fs/squashfs/sqfs_decompressor.c
index a899a5704b..a8c37f73b7 100644
--- a/fs/squashfs/sqfs_decompressor.c
+++ b/fs/squashfs/sqfs_decompressor.c
@@ -9,17 +9,41 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <u-boot/zlib.h>

 #include "sqfs_decompressor.h"
 #include "sqfs_filesystem.h"
 #include "sqfs_utils.h"

+static void zlib_decompression_status(int ret)
+{
+	switch (ret) {
+	case Z_BUF_ERROR:
+		printf("Error: 'dest' buffer is not large enough.\n");
+		break;
+	case Z_DATA_ERROR:
+		printf("Error: corrupted compressed data.\n");
+		break;
+	case Z_MEM_ERROR:
+		printf("Error: insufficient memory.\n");
+		break;
+	}
+}
+
 int sqfs_decompress(u16 comp_type, void *dest, unsigned long *dest_len,
 		    void *source, u32 lenp)
 {
 	int ret = 0;

 	switch (comp_type) {
+	case SQFS_COMP_ZLIB:
+		ret = uncompress(dest, dest_len, source, lenp);
+		if (ret) {
+			zlib_decompression_status(ret);
+			return -EINVAL;
+		}
+
+		break;
 	default:
 		printf("Error: unknown compression type.\n");
 		return -EINVAL;
--
2.17.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 1/4] fs/squashfs: new filesystem
  2020-07-09 17:51 ` [PATCH 1/4] fs/squashfs: new filesystem Joao Marcos Costa
@ 2020-07-09 19:50   ` Rasmus Villemoes
  2020-07-09 20:19     ` Joao Marcos Costa
  2020-07-10  8:29     ` Thomas Petazzoni
  2020-07-15 21:55   ` Tom Rini
  1 sibling, 2 replies; 20+ messages in thread
From: Rasmus Villemoes @ 2020-07-09 19:50 UTC (permalink / raw)
  To: u-boot

On 09/07/2020 19.51, Joao Marcos Costa wrote:
> Add support for SquashFS filesystem. Right now, it does not support
> compression but support for zlib will be added in a follow-up commit.

Cool, thanks for proposing this. While I'm not really in a position to
review these, just one comment below.

> 
> +config SPL_FS_SQUASHFS
> +	bool "Support SquashFS filesystems"
> +	select FS_SQUASHFS

Is there any reason U-Boot proper must support squashfs if the SPL does?
This isn't quite specific to this patch; I see a number of 'config
SPL_FOO' that either depends on or selects FOO (e.g. SPL_FS_FAT), and I
wonder why.

Rasmus

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/4] fs/squashfs: new filesystem
  2020-07-09 19:50   ` Rasmus Villemoes
@ 2020-07-09 20:19     ` Joao Marcos Costa
  2020-07-10  8:29     ` Thomas Petazzoni
  1 sibling, 0 replies; 20+ messages in thread
From: Joao Marcos Costa @ 2020-07-09 20:19 UTC (permalink / raw)
  To: u-boot

Hello!

On Thu, 9 Jul 2020 21:50:03 +0200
Rasmus Villemoes <rasmus.villemoes@prevas.dk> wrote:

> On 09/07/2020 19.51, Joao Marcos Costa wrote:
> > Add support for SquashFS filesystem. Right now, it does not support
> > compression but support for zlib will be added in a follow-up
> > commit.  
> 
> Cool, thanks for proposing this. While I'm not really in a position to
> review these, just one comment below.
> 
> > 
> > +config SPL_FS_SQUASHFS
> > +	bool "Support SquashFS filesystems"
> > +	select FS_SQUASHFS  
> 
> Is there any reason U-Boot proper must support squashfs if the SPL
> does? This isn't quite specific to this patch; I see a number of
> 'config SPL_FOO' that either depends on or selects FOO (e.g.
> SPL_FS_FAT), and I wonder why.
> 
> Rasmus

Well, I don't have enough experience to answer this question properly,
but precisely as you mentioned, other filesystems do the same
concerning SPL, and I simply decided to follow their example.

Best regards,

Joao Marcos

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/4] fs/squashfs: new filesystem
  2020-07-09 19:50   ` Rasmus Villemoes
  2020-07-09 20:19     ` Joao Marcos Costa
@ 2020-07-10  8:29     ` Thomas Petazzoni
  2020-07-10  8:54       ` Rasmus Villemoes
  1 sibling, 1 reply; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-10  8:29 UTC (permalink / raw)
  To: u-boot

Hello Rasmus,

On Thu, 9 Jul 2020 21:50:03 +0200
Rasmus Villemoes <rasmus.villemoes@prevas.dk> wrote:

> > +config SPL_FS_SQUASHFS
> > +	bool "Support SquashFS filesystems"
> > +	select FS_SQUASHFS  
> 
> Is there any reason U-Boot proper must support squashfs if the SPL does?
> This isn't quite specific to this patch; I see a number of 'config
> SPL_FOO' that either depends on or selects FOO (e.g. SPL_FS_FAT), and I
> wonder why.

Well, if your Linux kernel image and Device Tree are stored in a
squashfs filesystem, then U-Boot proper needs SquashFS support, right ?

And that is completely independent of whether the SPL has SquashFS
support to be able to load U-Boot proper (which potentially could be
stored on SquashFS as well).

It's very much like the FAT filesystem case: if you have U-Boot proper
and your Linux kernel image in a FAT filesystem, then the SPL needs FAT
filesystem support to load U-Boot proper, and U-Boot proper needs FAT
filesystem support to load the Linux kernel image.

Am I missing something here ?

Best regards,

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/4] fs/squashfs: new filesystem
  2020-07-10  8:29     ` Thomas Petazzoni
@ 2020-07-10  8:54       ` Rasmus Villemoes
  2020-07-10  9:13         ` Thomas Petazzoni
  0 siblings, 1 reply; 20+ messages in thread
From: Rasmus Villemoes @ 2020-07-10  8:54 UTC (permalink / raw)
  To: u-boot

On 10/07/2020 10.29, Thomas Petazzoni wrote:
> Hello Rasmus,
> 
> On Thu, 9 Jul 2020 21:50:03 +0200
> Rasmus Villemoes <rasmus.villemoes@prevas.dk> wrote:
> 
>>> +config SPL_FS_SQUASHFS
>>> +	bool "Support SquashFS filesystems"
>>> +	select FS_SQUASHFS  
>>
>> Is there any reason U-Boot proper must support squashfs if the SPL does?
>> This isn't quite specific to this patch; I see a number of 'config
>> SPL_FOO' that either depends on or selects FOO (e.g. SPL_FS_FAT), and I
>> wonder why.
> 
> Well, if your Linux kernel image and Device Tree are stored in a
> squashfs filesystem, then U-Boot proper needs SquashFS support, right ?

Exactly.

> And that is completely independent of whether the SPL has SquashFS
> support to be able to load U-Boot proper (which potentially could be
> stored on SquashFS as well).

Exactly.

> It's very much like the FAT filesystem case: if you have U-Boot proper
> and your Linux kernel image in a FAT filesystem, 

No, this is very much _not_ like the above. In this paragraph, you
combine "U-Boot proper and your Linux kernel", imposing an implicit
assumption that they are stored in the same way. Sure, _if_ both these
items are stored in squashfs images (possibly the same, possibly
distinct), then the thing that loads the respective images obviously
needs squashfs (or FAT, or whatnot) support.

My point is that it's possible that, say, U-Boot proper is stored in a
FAT file system, and the kernel is stored in a UBI volume. So SPL needs
FAT support. Why should I be forced to compile FAT support into U-Boot
proper if U-Boot proper never needs to access a FAT filesystem? And the
same for squashfs. Or any of the drivers or DM_ frameworks that do that
"depends on" or "select".

I can see why things like bloblist that are very much about passing info
from one stage to the next only make sense if both sides have that
configured in. But the drivers/filesystems that are needed in SPL need
not have anything to do with that which is needed in U-Boot proper.

Rasmus

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/4] fs/squashfs: new filesystem
  2020-07-10  8:54       ` Rasmus Villemoes
@ 2020-07-10  9:13         ` Thomas Petazzoni
  2020-07-15 21:56           ` Tom Rini
  0 siblings, 1 reply; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-10  9:13 UTC (permalink / raw)
  To: u-boot

On Fri, 10 Jul 2020 10:54:24 +0200
Rasmus Villemoes <rasmus.villemoes@prevas.dk> wrote:

> > It's very much like the FAT filesystem case: if you have U-Boot proper
> > and your Linux kernel image in a FAT filesystem,   
> 
> No, this is very much _not_ like the above. In this paragraph, you
> combine "U-Boot proper and your Linux kernel", imposing an implicit
> assumption that they are stored in the same way. Sure, _if_ both these
> items are stored in squashfs images (possibly the same, possibly
> distinct), then the thing that loads the respective images obviously
> needs squashfs (or FAT, or whatnot) support.
> 
> My point is that it's possible that, say, U-Boot proper is stored in a
> FAT file system, and the kernel is stored in a UBI volume. So SPL needs
> FAT support. Why should I be forced to compile FAT support into U-Boot
> proper if U-Boot proper never needs to access a FAT filesystem? And the
> same for squashfs. Or any of the drivers or DM_ frameworks that do that
> "depends on" or "select".

Ah, I absolutely agree that it should be possible to have Squashfs in
both SPL and U-Boot proper, or only in SPL or only in U-Boot proper.

It was not clear in your initial e-mail that this was the issue you
were pointing.

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 3/4] fs/squashfs: add sources for zlib decompression
  2020-07-09 17:51 ` [PATCH 3/4] fs/squashfs: add sources for zlib decompression Joao Marcos Costa
@ 2020-07-15 19:37   ` Thomas Petazzoni
  0 siblings, 0 replies; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-15 19:37 UTC (permalink / raw)
  To: u-boot

Hello Joao,

On Thu,  9 Jul 2020 19:51:47 +0200
Joao Marcos Costa <joaomarcos.costa@bootlin.com> wrote:

> Add zlib (v1.2.11) functions to U-Boot. SquashFS depends on those
> functions to decompress data from a raw disk image. The actual support
> for zlib into SquashFS sources will be added in a follow-up commit.
> 
> Signed-off-by: Joao Marcos Costa <joaomarcos.costa@bootlin.com>

The header of the commit title is wrong, this commit is not touching
fs/squashfs at all, but lib/zlib.

I think the rest of the commit title are also misleading: I believe
zlib decompression was already supported in U-Boot.

But looking at the code, lib/gunzip.c already has a function called
zunzip(), which seems to do pretty much the same thing: decompress from
a memory buffer into another memory buffer.

Are you sure you cannot use this existing zunzip() function ? zunzip()
is already used by UBIFS.

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 0/3] squashfs fixes
  2020-07-09 17:51 [PATCH 0/4] Add support for the SquashFS filesystem Joao Marcos Costa
                   ` (3 preceding siblings ...)
  2020-07-09 17:51 ` [PATCH 4/4] fs/squashfs: add support " Joao Marcos Costa
@ 2020-07-15 20:11 ` Thomas Petazzoni
  2020-07-15 20:11   ` [PATCH 1/3] cmd/sqfs.c, include/fs.h: fix build failures, don't expose do_sqfs_*() functions globally Thomas Petazzoni
                     ` (3 more replies)
  2020-07-15 20:22 ` [PATCH] fs/squashfs/sqfs.c: use sqfs_read_sblk() in sqfs_probe() Thomas Petazzoni
  5 siblings, 4 replies; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-15 20:11 UTC (permalink / raw)
  To: u-boot

Hello Joao,

As I started reviewing your patch series, I encountered a number of
build issues after applying your patches, and building on the latest
master. The following patches address the build failures I have
encountered. Could you squash them into your commits, so that those
fixes are part of your v2 ? Of course, please do some testing, as I
have only done build testing so far.

Also, even with those fixes in place, I do still get a worrying gcc
warning (from gcc 10):

fs/squashfs/sqfs_inode.c: In function ?sqfs_find_inode?:
fs/squashfs/sqfs_inode.c:72:34: warning: array subscript l is outside array bounds of ?struct squashfs_directory_index[0]? [-Warray-bounds]
   72 |     index_list_size += ldir.index[l].size + 1;
      |                        ~~~~~~~~~~^~~
In file included from fs/squashfs/sqfs_inode.c:16:
fs/squashfs/sqfs_filesystem.h:207:34: note: while referencing ?index?
  207 |  struct squashfs_directory_index index[0];
      |                                  ^~~~~
fs/squashfs/sqfs_inode.c:31:29: note: defined here ?ldir?
   31 |  struct squashfs_ldir_inode ldir;
      |                             ^~~~

Could you investigate this ?

Thanks,

Thomas

Thomas Petazzoni (3):
  cmd/sqfs.c, include/fs.h: fix build failures, don't expose do_sqfs_*()
    functions globally
  fs/squashfs: use "struct disk_partition" instead of "disk_partition_t"
  fs/squashfs: do not use CMD_RET_* defines in the filesystem code

 cmd/sqfs.c         |  5 ++--
 fs/squashfs/sqfs.c | 66 ++++++++++++++++++++--------------------------
 include/fs.h       | 12 ---------
 include/squashfs.h |  4 ++-
 4 files changed, 34 insertions(+), 53 deletions(-)

-- 
2.26.2

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/3] cmd/sqfs.c, include/fs.h: fix build failures, don't expose do_sqfs_*() functions globally
  2020-07-15 20:11 ` [PATCH 0/3] squashfs fixes Thomas Petazzoni
@ 2020-07-15 20:11   ` Thomas Petazzoni
  2020-07-15 20:11   ` [PATCH 2/3] fs/squashfs: use "struct disk_partition" instead of "disk_partition_t" Thomas Petazzoni
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-15 20:11 UTC (permalink / raw)
  To: u-boot

The cmd_tbl_t type has been removed upstream since commit 09140113108
("command: Remove the cmd_tbl_t typedef"), so we must use "struct
cmd_tbl" instead.

The <command.h> include was missing in cmd/sqfs.c, without which
U_BOOT_CMD() is not defined.

Finally, there is no reason to globally expose do_sqfs_ls() and
do_sqfs_load(), they are only used within cmd/sqfs.c. This allows to
drop the do_sqfs_load() prototype from include/fs.h.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 cmd/sqfs.c   |  5 +++--
 include/fs.h | 12 ------------
 2 files changed, 3 insertions(+), 14 deletions(-)

diff --git a/cmd/sqfs.c b/cmd/sqfs.c
index af42df728b..177008e802 100644
--- a/cmd/sqfs.c
+++ b/cmd/sqfs.c
@@ -7,10 +7,11 @@
  * squashfs.c:	implements SquashFS related commands
  */
 
+#include <command.h>
 #include <fs.h>
 #include <squashfs.h>
 
-int do_sqfs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+static int do_sqfs_ls(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
 {
 	return do_ls(cmdtp, flag, argc, argv, FS_TYPE_SQUASHFS);
 }
@@ -22,7 +23,7 @@ U_BOOT_CMD(
 	"    - list files from 'dev' on 'interface' in 'directory'\n"
 );
 
-int do_sqfs_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+static int do_sqfs_load(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
 {
 	return do_load(cmdtp, flag, argc, argv, FS_TYPE_SQUASHFS);
 }
diff --git a/include/fs.h b/include/fs.h
index 19f4afc482..0794b50d10 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -42,18 +42,6 @@ int do_fat_fsload(struct cmd_tbl *cmdtp, int flag, int argc,
  */
 int do_ext2load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 
-
-/**
- * do_sqfs_load - Run the sqfsload command
- *
- * @cmdtp: Command information for sqfsload
- * @flag: Command flags (CMD_FLAG_...)
- * @argc: Number of arguments
- * @argv: List of arguments
- * @return result (see enum command_ret_t)
- */
-int do_sqfs_load(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
-
 /*
  * Tell the fs layer which block device an partition to use for future
  * commands. This also internally identifies the filesystem that is present
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/3] fs/squashfs: use "struct disk_partition" instead of "disk_partition_t"
  2020-07-15 20:11 ` [PATCH 0/3] squashfs fixes Thomas Petazzoni
  2020-07-15 20:11   ` [PATCH 1/3] cmd/sqfs.c, include/fs.h: fix build failures, don't expose do_sqfs_*() functions globally Thomas Petazzoni
@ 2020-07-15 20:11   ` Thomas Petazzoni
  2020-07-15 20:11   ` [PATCH 3/3] fs/squashfs: do not use CMD_RET_* defines in the filesystem code Thomas Petazzoni
  2020-07-16  7:51   ` [PATCH 0/3] squashfs fixes Joao Marcos Costa
  3 siblings, 0 replies; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-15 20:11 UTC (permalink / raw)
  To: u-boot

disk_partition_t was dropped in 0528979fa7ab ("part: Drop
disk_partition_t typedef").

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 fs/squashfs/sqfs.c | 5 +++--
 include/squashfs.h | 4 +++-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
index 3302a63cca..8d43564b50 100644
--- a/fs/squashfs/sqfs.c
+++ b/fs/squashfs/sqfs.c
@@ -17,12 +17,13 @@
 #include <stdlib.h>
 #include <string.h>
 #include <squashfs.h>
+#include <part.h>
 
 #include "sqfs_decompressor.h"
 #include "sqfs_filesystem.h"
 #include "sqfs_utils.h"
 
-static disk_partition_t cur_part_info;
+static struct disk_partition cur_part_info;
 static struct blk_desc *cur_dev;
 
 static int sqfs_disk_read(__u32 block, __u32 nr_blocks, void *buf)
@@ -1013,7 +1014,7 @@ int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
 	return SQFS_CONTINUE_READDIR;
 }
 
-int sqfs_probe(struct blk_desc *fs_dev_desc, disk_partition_t *fs_partition)
+int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
 {
 	struct squashfs_super_block *sblk;
 
diff --git a/include/squashfs.h b/include/squashfs.h
index ec59eee8d8..4dd390d8b6 100644
--- a/include/squashfs.h
+++ b/include/squashfs.h
@@ -10,11 +10,13 @@
 #ifndef _SQFS_H_
 #define _SQFS_H_
 
+struct disk_partition;
+
 int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp);
 int sqfs_ls(const char *filename);
 int sqfs_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
 int sqfs_probe(struct blk_desc *fs_dev_desc,
-	       disk_partition_t *fs_partition);
+	       struct disk_partition *fs_partition);
 int sqfs_read(const char *filename, void *buf, loff_t offset,
 	      loff_t len, loff_t *actread);
 int sqfs_size(const char *filename, loff_t *size);
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 3/3] fs/squashfs: do not use CMD_RET_* defines in the filesystem code
  2020-07-15 20:11 ` [PATCH 0/3] squashfs fixes Thomas Petazzoni
  2020-07-15 20:11   ` [PATCH 1/3] cmd/sqfs.c, include/fs.h: fix build failures, don't expose do_sqfs_*() functions globally Thomas Petazzoni
  2020-07-15 20:11   ` [PATCH 2/3] fs/squashfs: use "struct disk_partition" instead of "disk_partition_t" Thomas Petazzoni
@ 2020-07-15 20:11   ` Thomas Petazzoni
  2020-07-16  7:51   ` [PATCH 0/3] squashfs fixes Joao Marcos Costa
  3 siblings, 0 replies; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-15 20:11 UTC (permalink / raw)
  To: u-boot

Using CMD_RET_* defines in fs/squashfs/ breaks the build as they are
not defined. These defines are really meant to be used as return
values of commands, not internally in filesystem code. Instead,
appropriate errno codes should be used.

This patch changes fs/squashfs to use proper errno code. In most
places, it is straightforward. There are a few places where it is not:

 - In sqfs_read(), the value of datablk_count was checked *after* it
   was used to do a memcpy(). So the memcpy() could have used a
   negative size. The check was moved prior to the memcpy(), right
   after calling sqfs_get_regfile_info() and sqfs_get_lregfile_info().

 - The sqfs_size() function is modified to return "ret" at the end, so
   that the error code is properly propagated.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 fs/squashfs/sqfs.c | 61 +++++++++++++++++++---------------------------
 1 file changed, 25 insertions(+), 36 deletions(-)

diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
index 8d43564b50..27f0b33a92 100644
--- a/fs/squashfs/sqfs.c
+++ b/fs/squashfs/sqfs.c
@@ -1241,10 +1241,8 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 	 */
 	sqfs_split_path(&file, &dir, filename);
 	ret = sqfs_opendir(dir, &dirs.fs_dirs);
-	if (ret) {
-		ret = CMD_RET_FAILURE;
+	if (ret)
 		goto free_paths;
-	}
 
 	dirsp = (struct fs_dir_stream *)&dirs;
 	dirs.dentp = &dent;
@@ -1253,7 +1251,6 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 		if (!strncmp(dent.name, file, strlen(dent.name))) {
 			ret = sqfs_read_entry(&entry, dirs.entry);
 			if (ret) {
-				ret = CMD_RET_FAILURE;
 				free(dirs.entry);
 				sqfs_closedir(dirsp);
 				goto free_paths;
@@ -1269,7 +1266,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 		printf("File not found.\n");
 		*actread = 0;
 		sqfs_closedir(dirsp);
-		ret = CMD_RET_FAILURE;
+		ret = -ENOENT;
 		goto free_paths;
 	}
 
@@ -1283,6 +1280,10 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 		memcpy(&reg, ipos, sizeof(reg));
 		datablk_count = sqfs_get_regfile_info(&reg, &finfo, &frag_entry,
 						      sblk->block_size);
+		if (datablk_count < 0) {
+			ret = -EINVAL;
+			goto free_entry;
+		}
 		memcpy(finfo.blk_sizes, ipos + sizeof(reg),
 		       datablk_count * sizeof(u32));
 		break;
@@ -1291,6 +1292,10 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 		datablk_count = sqfs_get_lregfile_info(&lreg, &finfo,
 						       &frag_entry,
 						       sblk->block_size);
+		if (datablk_count < 0) {
+			ret = -EINVAL;
+			goto free_entry;
+		}
 		memcpy(finfo.blk_sizes, ipos + sizeof(lreg),
 		       datablk_count * sizeof(u32));
 		break;
@@ -1301,9 +1306,6 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 		printf("%s - > %s\n", filename, resolved);
 		ret = sqfs_read(resolved, buf, offset, len, actread);
 		free(resolved);
-		if (ret)
-			ret = CMD_RET_FAILURE;
-		ret = CMD_RET_SUCCESS;
 		goto free_entry;
 	case SQFS_BLKDEV_TYPE:
 	case SQFS_CHRDEV_TYPE:
@@ -1315,19 +1317,14 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 	case SQFS_LSOCKET_TYPE:
 	default:
 		printf("Unsupported entry type\n");
-		ret = CMD_RET_FAILURE;
-		goto free_entry;
-	}
-
-	if (datablk_count < 0) {
-		ret = CMD_RET_FAILURE;
+		ret = -EINVAL;
 		goto free_entry;
 	}
 
 	/* If the user specifies a length, check its sanity */
 	if (len) {
 		if (len > finfo.size) {
-			ret = CMD_RET_FAILURE;
+			ret = -EINVAL;
 			goto free_entry;
 		}
 
@@ -1338,7 +1335,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 		data_offset = finfo.start;
 		datablock = malloc(sblk->block_size);
 		if (!datablock) {
-			ret = CMD_RET_FAILURE;
+			ret = -ENOMEM;
 			goto free_entry;
 		}
 	}
@@ -1353,18 +1350,18 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 		data_buffer = malloc_cache_aligned(n_blks * cur_dev->blksz);
 
 		if (!data_buffer) {
-			ret = CMD_RET_FAILURE;
+			ret = -ENOMEM;
 			goto free_datablk;
 		}
 
-		if (sqfs_disk_read(start, n_blks, data_buffer) < 0) {
+		ret = sqfs_disk_read(start, n_blks, data_buffer);
+		if (ret < 0) {
 			/*
 			 * Tip: re-compile the SquashFS image with mksquashfs's
 			 * -b <block_size> option.
 			 */
 			printf("Error: too many data blocks or too large"\
 			       "SquashFS block size.\n");
-			ret = CMD_RET_FAILURE;
 			goto free_buffer;
 		}
 
@@ -1375,10 +1372,8 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 			dest_len = sblk->block_size;
 			ret = sqfs_decompress(comp_type, datablock, &dest_len,
 					      data, table_size);
-			if (ret) {
-				ret = CMD_RET_FAILURE;
+			if (ret)
 				goto free_buffer;
-			}
 
 			memcpy(buf + offset + *actread, datablock, dest_len);
 			*actread += dest_len;
@@ -1396,7 +1391,7 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 	 * There is no need to continue if the file is not fragmented.
 	 */
 	if (!finfo.frag) {
-		ret = CMD_RET_SUCCESS;
+		ret = 0;
 		goto free_buffer;
 	}
 
@@ -1408,21 +1403,20 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 	fragment = malloc_cache_aligned(n_blks * cur_dev->blksz);
 
 	if (!fragment) {
-		ret = CMD_RET_FAILURE;
+		ret = -ENOMEM;
 		goto free_buffer;
 	}
 
-	if (sqfs_disk_read(start, n_blks, fragment) < 0) {
-		ret = CMD_RET_FAILURE;
+	ret = sqfs_disk_read(start, n_blks, fragment);
+	if (ret < 0)
 		goto free_fragment;
-	}
 
 	/* File compressed and fragmented */
 	if (finfo.frag && finfo.comp) {
 		dest_len = sblk->block_size;
 		fragment_block = malloc(sblk->block_size);
 		if (!fragment_block) {
-			ret = CMD_RET_FAILURE;
+			ret = -ENOMEM;
 			goto free_fragment;
 		}
 
@@ -1431,7 +1425,6 @@ int sqfs_read(const char *filename, void *buf, loff_t offset, loff_t len,
 				      frag_entry.size);
 		if (ret) {
 			free(fragment_block);
-			ret = CMD_RET_FAILURE;
 			goto free_fragment;
 		}
 
@@ -1480,7 +1473,7 @@ int sqfs_ls(const char *filename)
 	ret = sqfs_opendir(filename, &dirs.fs_dirs);
 	if (ret) {
 		sqfs_closedir(dirsp);
-		return CMD_RET_FAILURE;
+		return ret;
 	}
 
 	dirs.dentp = &dent;
@@ -1542,7 +1535,6 @@ int sqfs_size(const char *filename, loff_t *size)
 	ret = sqfs_opendir(dir, &dirs.fs_dirs);
 	if (ret) {
 		sqfs_closedir(dirsp);
-		ret = CMD_RET_FAILURE;
 		goto free_strings;
 	}
 
@@ -1558,7 +1550,6 @@ int sqfs_size(const char *filename, loff_t *size)
 
 	if (ret) {
 		sqfs_closedir(dirsp);
-		ret = CMD_RET_FAILURE;
 		goto free_strings;
 	}
 
@@ -1583,9 +1574,6 @@ int sqfs_size(const char *filename, loff_t *size)
 		resolved = sqfs_resolve_symlink(&symlink, ipos, filename);
 		ret = sqfs_size(resolved, size);
 		free(resolved);
-		if (ret)
-			ret = CMD_RET_FAILURE;
-		ret = CMD_RET_SUCCESS;
 		break;
 	case SQFS_BLKDEV_TYPE:
 	case SQFS_CHRDEV_TYPE:
@@ -1598,6 +1586,7 @@ int sqfs_size(const char *filename, loff_t *size)
 	default:
 		printf("Unable to recover entry's size.\n");
 		*size = 0;
+		ret = -EINVAL;
 		break;
 	}
 
@@ -1608,7 +1597,7 @@ free_strings:
 	free(sblk);
 	sqfs_closedir(dirsp);
 
-	return 0;
+	return ret;
 }
 
 void sqfs_close(void)
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH] fs/squashfs/sqfs.c: use sqfs_read_sblk() in sqfs_probe()
  2020-07-09 17:51 [PATCH 0/4] Add support for the SquashFS filesystem Joao Marcos Costa
                   ` (4 preceding siblings ...)
  2020-07-15 20:11 ` [PATCH 0/3] squashfs fixes Thomas Petazzoni
@ 2020-07-15 20:22 ` Thomas Petazzoni
  5 siblings, 0 replies; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-15 20:22 UTC (permalink / raw)
  To: u-boot

sqfs_probe() currently duplicates what sqfs_read_sblk() is doing, so
use the latter in the former to avoid some small code duplication.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 fs/squashfs/sqfs.c | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c
index 27f0b33a92..cd3fbade7e 100644
--- a/fs/squashfs/sqfs.c
+++ b/fs/squashfs/sqfs.c
@@ -1017,20 +1017,14 @@ int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
 int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition)
 {
 	struct squashfs_super_block *sblk;
+	int ret;
 
 	cur_dev = fs_dev_desc;
 	cur_part_info = *fs_partition;
-	sblk = malloc_cache_aligned(cur_dev->blksz);
 
-	if (!sblk)
-		return -ENOMEM;
-
-	/* Read SquashFS super block */
-	if (sqfs_disk_read(0, 1, sblk) != 1) {
-		free(sblk);
-		cur_dev = NULL;
-		return -EINVAL;
-	}
+	ret = sqfs_read_sblk(&sblk);
+	if (ret)
+		return ret;
 
 	/* Make sure it has a valid SquashFS magic number*/
 	if (sblk->s_magic != SQFS_MAGIC_NUMBER) {
-- 
2.26.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 1/4] fs/squashfs: new filesystem
  2020-07-09 17:51 ` [PATCH 1/4] fs/squashfs: new filesystem Joao Marcos Costa
  2020-07-09 19:50   ` Rasmus Villemoes
@ 2020-07-15 21:55   ` Tom Rini
  1 sibling, 0 replies; 20+ messages in thread
From: Tom Rini @ 2020-07-15 21:55 UTC (permalink / raw)
  To: u-boot

On Thu, Jul 09, 2020 at 07:51:45PM +0200, Joao Marcos Costa wrote:

> Add support for SquashFS filesystem. Right now, it does not support
> compression but support for zlib will be added in a follow-up commit.
> 
> Signed-off-by: Joao Marcos Costa <joaomarcos.costa@bootlin.com>

Sorry for the delay.  If you run this patch through checkpatch.pl it
notes a few problems that need to be addressed.  Thanks!

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 659 bytes
Desc: not available
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20200715/f3825680/attachment.sig>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/4] fs/squashfs: new filesystem
  2020-07-10  9:13         ` Thomas Petazzoni
@ 2020-07-15 21:56           ` Tom Rini
  0 siblings, 0 replies; 20+ messages in thread
From: Tom Rini @ 2020-07-15 21:56 UTC (permalink / raw)
  To: u-boot

On Fri, Jul 10, 2020 at 11:13:50AM +0200, Thomas Petazzoni wrote:
> On Fri, 10 Jul 2020 10:54:24 +0200
> Rasmus Villemoes <rasmus.villemoes@prevas.dk> wrote:
> 
> > > It's very much like the FAT filesystem case: if you have U-Boot proper
> > > and your Linux kernel image in a FAT filesystem,   
> > 
> > No, this is very much _not_ like the above. In this paragraph, you
> > combine "U-Boot proper and your Linux kernel", imposing an implicit
> > assumption that they are stored in the same way. Sure, _if_ both these
> > items are stored in squashfs images (possibly the same, possibly
> > distinct), then the thing that loads the respective images obviously
> > needs squashfs (or FAT, or whatnot) support.
> > 
> > My point is that it's possible that, say, U-Boot proper is stored in a
> > FAT file system, and the kernel is stored in a UBI volume. So SPL needs
> > FAT support. Why should I be forced to compile FAT support into U-Boot
> > proper if U-Boot proper never needs to access a FAT filesystem? And the
> > same for squashfs. Or any of the drivers or DM_ frameworks that do that
> > "depends on" or "select".
> 
> Ah, I absolutely agree that it should be possible to have Squashfs in
> both SPL and U-Boot proper, or only in SPL or only in U-Boot proper.
> 
> It was not clear in your initial e-mail that this was the issue you
> were pointing.

Note that on this point the question is, do we have a use case for
falcon mode and loading linux from squashfs?  I assume the answer is
yes, and that's why we would want to have squashfs be enabled in SPL.

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 659 bytes
Desc: not available
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20200715/cac9a7a6/attachment.sig>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 0/3] squashfs fixes
  2020-07-15 20:11 ` [PATCH 0/3] squashfs fixes Thomas Petazzoni
                     ` (2 preceding siblings ...)
  2020-07-15 20:11   ` [PATCH 3/3] fs/squashfs: do not use CMD_RET_* defines in the filesystem code Thomas Petazzoni
@ 2020-07-16  7:51   ` Joao Marcos Costa
  2020-07-16  8:33     ` Thomas Petazzoni
  3 siblings, 1 reply; 20+ messages in thread
From: Joao Marcos Costa @ 2020-07-16  7:51 UTC (permalink / raw)
  To: u-boot

Hello, Thomas!

On Wed, 15 Jul 2020 22:11:40 +0200
Thomas Petazzoni <thomas.petazzoni@bootlin.com> wrote:

> Hello Joao,
> 
> As I started reviewing your patch series, I encountered a number of
> build issues after applying your patches, and building on the latest
> master. The following patches address the build failures I have
> encountered. Could you squash them into your commits, so that those
> fixes are part of your v2 ? Of course, please do some testing, as I
> have only done build testing so far.

Thank you for this series, I was actually working on the v2020.04
branch, and I think this explains why I was not seeing such errors. 

> Also, even with those fixes in place, I do still get a worrying gcc
> warning (from gcc 10):

I will update my gcc version, because currently I am using gcc 7.5.

> 
> fs/squashfs/sqfs_inode.c: In function ?sqfs_find_inode?:
> fs/squashfs/sqfs_inode.c:72:34: warning: array subscript l is outside
> array bounds of ?struct squashfs_directory_index[0]? [-Warray-bounds]
> 72 |     index_list_size += ldir.index[l].size + 1;
> |                        ~~~~~~~~~~^~~ In file included from
> fs/squashfs/sqfs_inode.c:16: fs/squashfs/sqfs_filesystem.h:207:34:
> note: while referencing ?index? 207 |  struct
> squashfs_directory_index index[0]; |
> ^~~~~ fs/squashfs/sqfs_inode.c:31:29: note: defined here ?ldir?
>    31 |  struct squashfs_ldir_inode ldir;
>       |                             ^~~~
> 
> Could you investigate this ?

Surely I will, thanks!
 
> Thanks,
> 
> Thomas
> 
> Thomas Petazzoni (3):
>   cmd/sqfs.c, include/fs.h: fix build failures, don't expose
> do_sqfs_*() functions globally
>   fs/squashfs: use "struct disk_partition" instead of
> "disk_partition_t" fs/squashfs: do not use CMD_RET_* defines in the
> filesystem code
> 
>  cmd/sqfs.c         |  5 ++--
>  fs/squashfs/sqfs.c | 66
> ++++++++++++++++++++-------------------------- include/fs.h       |
> 12 --------- include/squashfs.h |  4 ++-
>  4 files changed, 34 insertions(+), 53 deletions(-)
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 0/3] squashfs fixes
  2020-07-16  7:51   ` [PATCH 0/3] squashfs fixes Joao Marcos Costa
@ 2020-07-16  8:33     ` Thomas Petazzoni
  0 siblings, 0 replies; 20+ messages in thread
From: Thomas Petazzoni @ 2020-07-16  8:33 UTC (permalink / raw)
  To: u-boot

On Thu, 16 Jul 2020 09:51:25 +0200
Joao Marcos Costa <joaomarcos.costa@bootlin.com> wrote:

> > As I started reviewing your patch series, I encountered a number of
> > build issues after applying your patches, and building on the latest
> > master. The following patches address the build failures I have
> > encountered. Could you squash them into your commits, so that those
> > fixes are part of your v2 ? Of course, please do some testing, as I
> > have only done build testing so far.  
> 
> Thank you for this series, I was actually working on the v2020.04
> branch, and I think this explains why I was not seeing such errors. 

OK. When you're submitting patches to U-Boot, since the patches will
very likely be applied on master, the patches you send should be based
on master. Otherwise, the maintainer who will apply your patches will
face the sort of problems that I did face.

> > Also, even with those fixes in place, I do still get a worrying gcc
> > warning (from gcc 10):  
> 
> I will update my gcc version, because currently I am using gcc 7.5.

You could also keep gcc 7.x on your system, but simply use a gcc 10.x
toolchain when cross-compiling U-Boot for ARM for example, as I know
you're also testing this on an ARM platform.

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2020-07-16  8:33 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-09 17:51 [PATCH 0/4] Add support for the SquashFS filesystem Joao Marcos Costa
2020-07-09 17:51 ` [PATCH 1/4] fs/squashfs: new filesystem Joao Marcos Costa
2020-07-09 19:50   ` Rasmus Villemoes
2020-07-09 20:19     ` Joao Marcos Costa
2020-07-10  8:29     ` Thomas Petazzoni
2020-07-10  8:54       ` Rasmus Villemoes
2020-07-10  9:13         ` Thomas Petazzoni
2020-07-15 21:56           ` Tom Rini
2020-07-15 21:55   ` Tom Rini
2020-07-09 17:51 ` [PATCH 2/4] fs/squashfs: add filesystem commands Joao Marcos Costa
2020-07-09 17:51 ` [PATCH 3/4] fs/squashfs: add sources for zlib decompression Joao Marcos Costa
2020-07-15 19:37   ` Thomas Petazzoni
2020-07-09 17:51 ` [PATCH 4/4] fs/squashfs: add support " Joao Marcos Costa
2020-07-15 20:11 ` [PATCH 0/3] squashfs fixes Thomas Petazzoni
2020-07-15 20:11   ` [PATCH 1/3] cmd/sqfs.c, include/fs.h: fix build failures, don't expose do_sqfs_*() functions globally Thomas Petazzoni
2020-07-15 20:11   ` [PATCH 2/3] fs/squashfs: use "struct disk_partition" instead of "disk_partition_t" Thomas Petazzoni
2020-07-15 20:11   ` [PATCH 3/3] fs/squashfs: do not use CMD_RET_* defines in the filesystem code Thomas Petazzoni
2020-07-16  7:51   ` [PATCH 0/3] squashfs fixes Joao Marcos Costa
2020-07-16  8:33     ` Thomas Petazzoni
2020-07-15 20:22 ` [PATCH] fs/squashfs/sqfs.c: use sqfs_read_sblk() in sqfs_probe() Thomas Petazzoni

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.