From d23d753c5666571d068c466d80e030063f1c8c73 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Sat, 20 Feb 2021 20:40:17 -0500 Subject: [PATCH 01/12] u-boot: Pull squashfs support from upsream. --- ...uashfs-support-from-upstream-2021.01.patch | 3377 +++++++++++++++++ 1 file changed, 3377 insertions(+) create mode 100644 uboot/0001-Add-squashfs-support-from-upstream-2021.01.patch diff --git a/uboot/0001-Add-squashfs-support-from-upstream-2021.01.patch b/uboot/0001-Add-squashfs-support-from-upstream-2021.01.patch new file mode 100644 index 0000000..09395fe --- /dev/null +++ b/uboot/0001-Add-squashfs-support-from-upstream-2021.01.patch @@ -0,0 +1,3377 @@ +From 13c0c426e1b6333a4c3917979e7601aa5ea07649 Mon Sep 17 00:00:00 2001 +From: Justin Schneck +Date: Sat, 20 Feb 2021 17:37:28 -0500 +Subject: [PATCH] Add squashfs support from upstream 2021.01 + +--- + cmd/Kconfig | 6 + + cmd/Makefile | 1 + + cmd/sqfs.c | 42 + + common/spl/Kconfig | 9 + + fs/Kconfig | 2 + + fs/Makefile | 1 + + fs/fs.c | 22 + + fs/squashfs/Kconfig | 11 + + fs/squashfs/Makefile | 7 + + fs/squashfs/sqfs.c | 1708 +++++++++++++++++ + fs/squashfs/sqfs_decompressor.c | 155 ++ + fs/squashfs/sqfs_decompressor.h | 61 + + fs/squashfs/sqfs_dir.c | 93 + + fs/squashfs/sqfs_filesystem.h | 310 +++ + fs/squashfs/sqfs_inode.c | 161 ++ + fs/squashfs/sqfs_utils.h | 49 + + include/fs.h | 1 + + include/hang.h | 22 + + include/squashfs.h | 26 + + include/u-boot/zlib.h | 32 + + lib/Kconfig | 7 + + lib/zlib/inftrees.c | 19 +- + lib/zlib/trees.c | 31 +- + lib/zlib/uncompr.c | 97 + + lib/zlib/zlib.c | 3 + + lib/zlib/zutil.c | 3 +- + .../test_fs/test_squashfs/sqfs_common.py | 76 + + .../test_fs/test_squashfs/test_sqfs_load.py | 46 + + .../test_fs/test_squashfs/test_sqfs_ls.py | 36 + + 29 files changed, 3010 insertions(+), 27 deletions(-) + 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/hang.h + create mode 100644 include/squashfs.h + create mode 100644 lib/zlib/uncompr.c + create mode 100644 test/py/tests/test_fs/test_squashfs/sqfs_common.py + create mode 100644 test/py/tests/test_fs/test_squashfs/test_sqfs_load.py + create mode 100644 test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py + +diff --git a/cmd/Kconfig b/cmd/Kconfig +index 1e4cf146..5e4d9400 100644 +--- a/cmd/Kconfig ++++ b/cmd/Kconfig +@@ -1957,6 +1957,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 3ac71045..490c5abd 100644 +--- a/cmd/Makefile ++++ b/cmd/Makefile +@@ -60,6 +60,7 @@ obj-$(CONFIG_CMD_EXT2) += ext2.o + obj-$(CONFIG_CMD_FAT) += fat.o + obj-$(CONFIG_CMD_FDC) += fdc.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 00000000..107038c4 +--- /dev/null ++++ b/cmd/sqfs.c +@@ -0,0 +1,42 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 Bootlin ++ * ++ * Author: Joao Marcos Costa ++ * ++ * squashfs.c: implements SquashFS related commands ++ */ ++ ++#include ++#include ++#include ++ ++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); ++} ++ ++U_BOOT_CMD(sqfsls, 4, 1, do_sqfs_ls, ++ "List files in directory. Default: root (/).", ++ " [] [directory]\n" ++ " - list files from 'dev' on 'interface' in 'directory'\n" ++); ++ ++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); ++} ++ ++U_BOOT_CMD(sqfsload, 7, 0, do_sqfs_load, ++ "load binary file from a SquashFS filesystem", ++ " [ [ [ [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." ++); +diff --git a/common/spl/Kconfig b/common/spl/Kconfig +index a7241271..3765f387 100644 +--- a/common/spl/Kconfig ++++ b/common/spl/Kconfig +@@ -550,6 +550,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 1cb9831b..620af7f0 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 42e669c4..6980894d 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -23,5 +23,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 0c66d604..f7982710 100644 +--- a/fs/fs.c ++++ b/fs/fs.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + DECLARE_GLOBAL_DATA_PTR; + +@@ -273,6 +274,27 @@ static struct fstype_info fstypes[] = { + .mkdir = fs_mkdir_unsupported, + .ln = fs_ln_unsupported, + }, ++#endif ++#if IS_ENABLED(CONFIG_FS_SQUASHFS) ++ { ++ .fstype = FS_TYPE_SQUASHFS, ++ .name = "squashfs", ++ .null_dev_desc_ok = false, ++ .probe = sqfs_probe, ++ .opendir = sqfs_opendir, ++ .readdir = sqfs_readdir, ++ .ls = fs_ls_generic, ++ .read = sqfs_read, ++ .size = sqfs_size, ++ .close = sqfs_close, ++ .closedir = sqfs_closedir, ++ .exists = sqfs_exists, ++ .uuid = fs_uuid_unsupported, ++ .write = fs_write_unsupported, ++ .ln = fs_ln_unsupported, ++ .unlink = fs_unlink_unsupported, ++ .mkdir = fs_mkdir_unsupported, ++ }, + #endif + { + .fstype = FS_TYPE_ANY, +diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig +new file mode 100644 +index 00000000..54ab1618 +--- /dev/null ++++ b/fs/squashfs/Kconfig +@@ -0,0 +1,11 @@ ++config FS_SQUASHFS ++ bool "Enable SquashFS filesystem support" ++ select ZLIB_UNCOMPRESS ++ 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 00000000..ba66ee82 +--- /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 00000000..5de69ac3 +--- /dev/null ++++ b/fs/squashfs/sqfs.c +@@ -0,0 +1,1708 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 Bootlin ++ * ++ * Author: Joao Marcos Costa ++ * ++ * sqfs.c: SquashFS filesystem implementation ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sqfs_decompressor.h" ++#include "sqfs_filesystem.h" ++#include "sqfs_utils.h" ++ ++static struct squashfs_ctxt ctxt; ++ ++static int sqfs_disk_read(__u32 block, __u32 nr_blocks, void *buf) ++{ ++ ulong ret; ++ ++ if (!ctxt.cur_dev) ++ return -1; ++ ++ ret = blk_dread(ctxt.cur_dev, ctxt.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(ctxt.cur_dev->blksz); ++ if (!*sblk) ++ return -ENOMEM; ++ ++ if (sqfs_disk_read(0, 1, *sblk) != 1) { ++ free(*sblk); ++ sblk = NULL; ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int sqfs_count_tokens(const char *filename) ++{ ++ int token_count = 1, l; ++ ++ for (l = 1; l < strlen(filename); l++) { ++ if (filename[l] == '/') ++ token_count++; ++ } ++ ++ /* 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) / ctxt.cur_dev->blksz; ++ *offset = le64_to_cpu(start) - (start_ * ctxt.cur_dev->blksz); ++ ++ return DIV_ROUND_UP(table_size + *offset, ctxt.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 = ctxt.sblk; ++ unsigned long dest_len; ++ int block, offset, ret; ++ u16 header; ++ ++ metadata_buffer = NULL; ++ entries = NULL; ++ table = NULL; ++ ++ if (inode_fragment_index >= get_unaligned_le32(&sblk->fragments)) ++ return -EINVAL; ++ ++ start = get_unaligned_le64(&sblk->fragment_table_start) / ++ ctxt.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 * ctxt.cur_dev->blksz); ++ if (!table) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (sqfs_disk_read(start, n_blks, table) < 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ 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_le64(table + table_offset + block * ++ sizeof(u64)); ++ ++ start = start_block / ctxt.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 * ctxt.cur_dev->blksz); ++ if (!metadata_buffer) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Every metadata block starts with a 16-bit header */ ++ header = get_unaligned_le16(metadata_buffer + table_offset); ++ metadata = metadata_buffer + table_offset + SQFS_HEADER_SIZE; ++ ++ if (!metadata || !header) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ entries = malloc(SQFS_METADATA_BLOCK_SIZE); ++ if (!entries) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (SQFS_COMPRESSED_METADATA(header)) { ++ src_len = SQFS_METADATA_SIZE(header); ++ dest_len = SQFS_METADATA_BLOCK_SIZE; ++ ret = sqfs_decompress(&ctxt, entries, &dest_len, metadata, ++ src_len); ++ if (ret) { ++ ret = -EINVAL; ++ goto out; ++ } ++ } else { ++ memcpy(entries, metadata, SQFS_METADATA_SIZE(header)); ++ } ++ ++ *e = entries[offset]; ++ ret = SQFS_COMPRESSED_BLOCK(e->size); ++ ++out: ++ free(entries); ++ free(metadata_buffer); ++ free(table); ++ ++ 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; ++ u16 sz; ++ ++ tmp = src; ++ sz = get_unaligned_le16(src + sizeof(*tmp) - sizeof(u16)); ++ /* ++ * 'src' points to the begin of a directory entry, and 'sz' gets its ++ * 'name_size' member's value. name_size is actually the string ++ * length - 1, so adding 2 compensates this difference and adds space ++ * for the trailling null byte. ++ */ ++ *dest = malloc(sizeof(*tmp) + sz + 2); ++ if (!*dest) ++ return -ENOMEM; ++ ++ memcpy(*dest, src, sizeof(*tmp) + sz + 1); ++ (*dest)->name[sz + 1] = '\0'; ++ ++ return 0; ++} ++ ++static int sqfs_get_tokens_length(char **tokens, int count) ++{ ++ int length = 0, i; ++ ++ /* ++ * 1 is added to the result of strlen to consider the slash separator ++ * between the tokens. ++ */ ++ for (i = 0; i < count; i++) ++ length += strlen(tokens[i]) + 1; ++ ++ return length; ++} ++ ++/* 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; ++ ++ length = sqfs_get_tokens_length(token_list, token_count); ++ ++ result = malloc(length + 1); ++ if (!result) ++ return NULL; ++ ++ 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; ++} ++ ++/* ++ * Differently from sqfs_concat_tokens, sqfs_join writes the result into a ++ * previously allocated string, and returns the number of bytes written. ++ */ ++static int sqfs_join(char **strings, char *dest, int start, int end, ++ char separator) ++{ ++ int i, offset = 0; ++ ++ for (i = start; i < end; i++) { ++ strcpy(dest + offset, strings[i]); ++ offset += strlen(strings[i]); ++ if (i < end - 1) ++ dest[offset++] = separator; ++ } ++ ++ return offset; ++} ++ ++/* ++ * Fills the given token list using its size (count) and a source string (str) ++ */ ++static int sqfs_tokenize(char **tokens, int count, const char *str) ++{ ++ int i, j, ret = 0; ++ char *aux, *strc; ++ ++ strc = strdup(str); ++ if (!strc) ++ return -ENOMEM; ++ ++ if (!strcmp(strc, "/")) { ++ tokens[0] = strdup(strc); ++ if (!tokens[0]) { ++ ret = -ENOMEM; ++ goto free_strc; ++ } ++ } else { ++ for (j = 0; j < count; j++) { ++ aux = strtok(!j ? strc : NULL, "/"); ++ tokens[j] = strdup(aux); ++ if (!tokens[j]) { ++ for (i = 0; i < j; i++) ++ free(tokens[i]); ++ ret = -ENOMEM; ++ goto free_strc; ++ } ++ } ++ } ++ ++free_strc: ++ free(strc); ++ ++ return ret; ++} ++ ++/* ++ * Remove last 'updir + 1' tokens from the base path tokens list. This leaves us ++ * with a token list containing only the tokens needed to form the resolved ++ * path, and returns the decremented size of the token list. ++ */ ++static int sqfs_clean_base_path(char **base, int count, int updir) ++{ ++ int i; ++ ++ for (i = count - updir - 1; i < count; i++) ++ free(base[i]); ++ ++ return count - updir - 1; ++} ++ ++/* ++ * 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, *resolved = NULL; ++ int ret, bc, rc, i, updir = 0, resolved_size = 0, offset = 0; ++ ++ base_tokens = NULL; ++ rel_tokens = NULL; ++ ++ /* Memory allocation for the token lists */ ++ bc = sqfs_count_tokens(base); ++ rc = sqfs_count_tokens(rel); ++ if (bc < 1 || rc < 1) ++ return NULL; ++ ++ base_tokens = calloc(bc, sizeof(char *)); ++ if (!base_tokens) ++ return NULL; ++ ++ rel_tokens = calloc(rc, sizeof(char *)); ++ if (!rel_tokens) ++ goto out; ++ ++ /* Fill token lists */ ++ ret = sqfs_tokenize(base_tokens, bc, base); ++ if (ret) ++ goto out; ++ ++ ret = sqfs_tokenize(rel_tokens, rc, rel); ++ if (ret) ++ goto out; ++ ++ /* count '..' occurrences in target path */ ++ for (i = 0; i < rc; i++) { ++ if (!strcmp(rel_tokens[i], "..")) ++ updir++; ++ } ++ ++ /* Remove the last token and the '..' occurrences */ ++ bc = sqfs_clean_base_path(base_tokens, bc, updir); ++ if (bc < 0) ++ goto out; ++ ++ /* Calculate resolved path size */ ++ if (!bc) ++ resolved_size++; ++ ++ resolved_size += sqfs_get_tokens_length(base_tokens, bc) + ++ sqfs_get_tokens_length(rel_tokens, rc); ++ ++ resolved = malloc(resolved_size + 1); ++ if (!resolved) ++ goto out; ++ ++ /* Set resolved path */ ++ memset(resolved, '\0', resolved_size + 1); ++ offset += sqfs_join(base_tokens, resolved + offset, 0, bc, '/'); ++ resolved[offset++] = '/'; ++ offset += sqfs_join(rel_tokens, resolved + offset, updir, rc, '/'); ++ ++out: ++ if (rel_tokens) ++ for (i = 0; i < rc; i++) ++ free(rel_tokens[i]); ++ if (base_tokens) ++ for (i = 0; i < bc; i++) ++ free(base_tokens[i]); ++ ++ free(rel_tokens); ++ free(base_tokens); ++ ++ return resolved; ++} ++ ++static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym, ++ const char *base_path) ++{ ++ char *resolved, *target; ++ u32 sz; ++ ++ sz = get_unaligned_le32(&sym->symlink_size); ++ target = malloc(sz + 1); ++ if (!target) ++ return NULL; ++ ++ /* ++ * There is no trailling null byte in the symlink's target path, so a ++ * copy is made and a '\0' is added at its end. ++ */ ++ target[sz] = '\0'; ++ /* Get target name (relative path) */ ++ strncpy(target, sym->symlink, sz); ++ ++ /* 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) ++{ ++ struct squashfs_super_block *sblk = ctxt.sblk; ++ char *path, *target, **sym_tokens, *res, *rem; ++ int j, ret = 0, new_inode_number, offset; ++ struct squashfs_symlink_inode *sym; ++ struct squashfs_ldir_inode *ldir; ++ struct squashfs_dir_inode *dir; ++ struct fs_dir_stream *dirsp; ++ struct fs_dirent *dent; ++ unsigned char *table; ++ ++ res = NULL; ++ rem = NULL; ++ path = NULL; ++ target = NULL; ++ sym_tokens = NULL; ++ ++ dirsp = (struct fs_dir_stream *)dirs; ++ ++ /* Start by root inode */ ++ table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes), ++ sblk->inodes, sblk->block_size); ++ ++ dir = (struct squashfs_dir_inode *)table; ++ ldir = (struct squashfs_ldir_inode *)table; ++ ++ /* 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) ++ return -ENOMEM; ++ ++ memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE); ++ ++ /* Initialize squashfs_dir_stream members */ ++ dirs->table += SQFS_DIR_HEADER_SIZE; ++ dirs->size = get_unaligned_le16(&dir->file_size) - SQFS_DIR_HEADER_SIZE; ++ dirs->entry_count = dirs->dir_header->count + 1; ++ ++ /* No path given -> root directory */ ++ if (!strcmp(token_list[0], "/")) { ++ dirs->table = &dirs->dir_table[offset]; ++ memcpy(&dirs->i_dir, dir, sizeof(*dir)); ++ return 0; ++ } ++ ++ for (j = 0; j < token_count; j++) { ++ if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) { ++ printf("** Cannot find directory. **\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ while (!sqfs_readdir(dirsp, &dent)) { ++ ret = strcmp(dent->name, token_list[j]); ++ if (!ret) ++ break; ++ free(dirs->entry); ++ dirs->entry = NULL; ++ } ++ ++ if (ret) { ++ printf("** Cannot find directory. **\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* 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); ++ dir = (struct squashfs_dir_inode *)table; ++ ++ /* Check for symbolic link and inode type sanity */ ++ if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) { ++ sym = (struct squashfs_symlink_inode *)table; ++ /* Get first j + 1 tokens */ ++ path = sqfs_concat_tokens(token_list, j + 1); ++ if (!path) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ /* Resolve for these tokens */ ++ target = sqfs_resolve_symlink(sym, path); ++ if (!target) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ /* Join remaining tokens */ ++ rem = sqfs_concat_tokens(token_list + j + 1, token_count - ++ j - 1); ++ if (!rem) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ /* Concatenate remaining tokens and symlink's target */ ++ res = malloc(strlen(rem) + strlen(target) + 1); ++ if (!res) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ strcpy(res, target); ++ res[strlen(target)] = '/'; ++ strcpy(res + strlen(target) + 1, rem); ++ token_count = sqfs_count_tokens(res); ++ ++ if (token_count < 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ sym_tokens = malloc(token_count * sizeof(char *)); ++ if (!sym_tokens) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Fill tokens list */ ++ ret = sqfs_tokenize(sym_tokens, token_count, res); ++ if (ret) { ++ ret = -EINVAL; ++ goto out; ++ } ++ free(dirs->entry); ++ dirs->entry = NULL; ++ ++ ret = sqfs_search_dir(dirs, sym_tokens, token_count, ++ m_list, m_count); ++ goto out; ++ } else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) { ++ printf("** Cannot find directory. **\n"); ++ free(dirs->entry); ++ dirs->entry = NULL; ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Check if it is an extended dir. */ ++ if (get_unaligned_le16(&dir->inode_type) == SQFS_LDIR_TYPE) ++ ldir = (struct squashfs_ldir_inode *)table; ++ ++ /* 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); ++ dirs->entry = NULL; ++ ret = SQFS_EMPTY_DIR; ++ goto out; ++ } ++ ++ dirs->table += SQFS_DIR_HEADER_SIZE; ++ dirs->size = get_unaligned_le16(&dir->file_size); ++ dirs->entry_count = dirs->dir_header->count + 1; ++ dirs->size -= SQFS_DIR_HEADER_SIZE; ++ free(dirs->entry); ++ dirs->entry = NULL; ++ } ++ ++ offset = sqfs_dir_offset(table, m_list, m_count); ++ dirs->table = &dirs->dir_table[offset]; ++ ++ if (get_unaligned_le16(&dir->inode_type) == SQFS_DIR_TYPE) ++ memcpy(&dirs->i_dir, dir, sizeof(*dir)); ++ else ++ memcpy(&dirs->i_ldir, ldir, sizeof(*ldir)); ++ ++out: ++ free(res); ++ free(rem); ++ free(path); ++ free(target); ++ free(sym_tokens); ++ return ret; ++} ++ ++/* ++ * 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) ++{ ++ struct squashfs_super_block *sblk = ctxt.sblk; ++ u64 start, n_blks, table_offset, table_size; ++ int j, ret = 0, metablks_count; ++ unsigned char *src_table, *itb; ++ u32 src_len, dest_offset = 0; ++ unsigned long dest_len = 0; ++ bool compressed; ++ ++ table_size = get_unaligned_le64(&sblk->directory_table_start) - ++ get_unaligned_le64(&sblk->inode_table_start); ++ start = get_unaligned_le64(&sblk->inode_table_start) / ++ ctxt.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 * ctxt.cur_dev->blksz); ++ if (!itb) ++ return -ENOMEM; ++ ++ 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(&ctxt, *inode_table + ++ dest_offset, &dest_len, ++ src_table, src_len); ++ if (ret) { ++ free(*inode_table); ++ *inode_table = NULL; ++ goto free_itb; ++ } ++ ++ dest_offset += dest_len; ++ } 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. ++ */ ++ ++ table_offset += src_len + SQFS_HEADER_SIZE; ++ src_table += src_len + SQFS_HEADER_SIZE; ++ } ++ ++free_itb: ++ free(itb); ++ ++ return ret; ++} ++ ++static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list) ++{ ++ u64 start, n_blks, table_offset, table_size; ++ struct squashfs_super_block *sblk = ctxt.sblk; ++ int j, ret = 0, metablks_count = -1; ++ unsigned char *src_table, *dtb; ++ u32 src_len, dest_offset = 0; ++ unsigned long dest_len = 0; ++ bool compressed; ++ ++ *dir_table = NULL; ++ *pos_list = NULL; ++ /* DIRECTORY TABLE */ ++ table_size = get_unaligned_le64(&sblk->fragment_table_start) - ++ get_unaligned_le64(&sblk->directory_table_start); ++ start = get_unaligned_le64(&sblk->directory_table_start) / ++ ctxt.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 * ctxt.cur_dev->blksz); ++ if (!dtb) ++ return -ENOMEM; ++ ++ if (sqfs_disk_read(start, n_blks, dtb) < 0) ++ goto out; ++ ++ /* Parse directory table (metadata block) header */ ++ ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len); ++ if (ret) ++ goto out; ++ ++ /* Calculate total size to store the whole decompressed table */ ++ metablks_count = sqfs_count_metablks(dtb, table_offset, table_size); ++ if (metablks_count < 1) ++ goto out; ++ ++ *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE); ++ if (!*dir_table) ++ goto out; ++ ++ *pos_list = malloc(metablks_count * sizeof(u32)); ++ if (!*pos_list) ++ goto out; ++ ++ ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset, ++ metablks_count); ++ if (ret) { ++ metablks_count = -1; ++ goto out; ++ } ++ ++ 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(&ctxt, *dir_table + ++ (j * SQFS_METADATA_BLOCK_SIZE), ++ &dest_len, src_table, src_len); ++ if (ret) { ++ metablks_count = -1; ++ goto out; ++ } ++ ++ if (dest_len < SQFS_METADATA_BLOCK_SIZE) { ++ dest_offset += dest_len; ++ break; ++ } ++ ++ dest_offset += dest_len; ++ } 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. ++ */ ++ table_offset += src_len + SQFS_HEADER_SIZE; ++ src_table += src_len + SQFS_HEADER_SIZE; ++ } ++ ++out: ++ if (metablks_count < 1) { ++ free(*dir_table); ++ free(*pos_list); ++ *dir_table = NULL; ++ *pos_list = NULL; ++ } ++ free(dtb); ++ ++ return metablks_count; ++} ++ ++int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp) ++{ ++ unsigned char *inode_table = NULL, *dir_table = NULL; ++ int j, token_count = 0, ret = 0, metablks_count; ++ struct squashfs_dir_stream *dirs; ++ char **token_list = NULL, *path = NULL; ++ u32 *pos_list = NULL; ++ ++ dirs = malloc(sizeof(*dirs)); ++ if (!dirs) ++ return -EINVAL; ++ ++ /* these should be set to NULL to prevent dangling pointers */ ++ dirs->dir_header = NULL; ++ dirs->entry = NULL; ++ dirs->table = NULL; ++ dirs->inode_table = NULL; ++ dirs->dir_table = NULL; ++ ++ ret = sqfs_read_inode_table(&inode_table); ++ if (ret) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ metablks_count = sqfs_read_directory_table(&dir_table, &pos_list); ++ if (metablks_count < 1) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Tokenize filename */ ++ token_count = sqfs_count_tokens(filename); ++ if (token_count < 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ path = strdup(filename); ++ if (!path) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ token_list = malloc(token_count * sizeof(char *)); ++ if (!token_list) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Fill tokens list */ ++ ret = sqfs_tokenize(token_list, token_count, path); ++ if (ret) ++ goto out; ++ /* ++ * 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 out; ++ ++ 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; ++ ++ *dirsp = (struct fs_dir_stream *)dirs; ++ ++out: ++ for (j = 0; j < token_count; j++) ++ free(token_list[j]); ++ free(token_list); ++ free(pos_list); ++ free(path); ++ if (ret) { ++ free(inode_table); ++ free(dirs); ++ } ++ ++ return ret; ++} ++ ++int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) ++{ ++ struct squashfs_super_block *sblk = ctxt.sblk; ++ struct squashfs_dir_stream *dirs; ++ struct squashfs_lreg_inode *lreg; ++ struct squashfs_base_inode *base; ++ struct squashfs_reg_inode *reg; ++ int i_number, offset = 0, ret; ++ struct fs_dirent *dent; ++ unsigned char *ipos; ++ ++ dirs = (struct squashfs_dir_stream *)fs_dirs; ++ if (!dirs->size) { ++ *dentp = NULL; ++ return -SQFS_STOP_READDIR; ++ } ++ ++ dent = &dirs->dentp; ++ ++ if (!dirs->entry_count) { ++ if (dirs->size > SQFS_DIR_HEADER_SIZE) { ++ dirs->size -= SQFS_DIR_HEADER_SIZE; ++ } else { ++ *dentp = NULL; ++ 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; ++ } ++ ++ 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); ++ ++ base = (struct squashfs_base_inode *)ipos; ++ ++ /* Set entry type and size */ ++ switch (dirs->entry->type) { ++ case SQFS_DIR_TYPE: ++ case SQFS_LDIR_TYPE: ++ dent->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 (get_unaligned_le16(&base->inode_type) == SQFS_LREG_TYPE) { ++ lreg = (struct squashfs_lreg_inode *)ipos; ++ dent->size = get_unaligned_le64(&lreg->file_size); ++ } else { ++ reg = (struct squashfs_reg_inode *)ipos; ++ dent->size = get_unaligned_le32(®->file_size); ++ } ++ ++ dent->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: ++ dent->type = SQFS_MISC_ENTRY_TYPE; ++ break; ++ case SQFS_SYMLINK_TYPE: ++ case SQFS_LSYMLINK_TYPE: ++ dent->type = FS_DT_LNK; ++ break; ++ default: ++ return -SQFS_STOP_READDIR; ++ } ++ ++ /* Set entry name */ ++ strncpy(dent->name, dirs->entry->name, dirs->entry->name_size + 1); ++ dent->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; ++ ++ *dentp = dent; ++ ++ return 0; ++} ++ ++int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition) ++{ ++ struct squashfs_super_block *sblk; ++ int ret; ++ ++ ctxt.cur_dev = fs_dev_desc; ++ ctxt.cur_part_info = *fs_partition; ++ ++ ret = sqfs_read_sblk(&sblk); ++ if (ret) ++ goto error; ++ ++ /* Make sure it has a valid SquashFS magic number*/ ++ if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) { ++ printf("Bad magic number for SquashFS image.\n"); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ ctxt.sblk = sblk; ++ ++ ret = sqfs_decompressor_init(&ctxt); ++ if (ret) { ++ goto error; ++ } ++ ++ return 0; ++error: ++ ctxt.cur_dev = NULL; ++ free(ctxt.sblk); ++ ctxt.sblk = NULL; ++ return ret; ++} ++ ++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; ++ ++ *file = NULL; ++ *dir = NULL; ++ dirc = NULL; ++ basec = NULL; ++ bname = NULL; ++ dname = NULL; ++ tmp_path = NULL; ++ ++ /* check for first slash in path*/ ++ if (path[0] == '/') { ++ tmp_path = strdup(path); ++ if (!tmp_path) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ } else { ++ tmp_path = malloc(strlen(path) + 2); ++ if (!tmp_path) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ tmp_path[0] = '/'; ++ strcpy(tmp_path + 1, path); ++ } ++ ++ /* String duplicates */ ++ dirc = strdup(tmp_path); ++ if (!dirc) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ basec = strdup(tmp_path); ++ if (!basec) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ dname = sqfs_dirname(dirc); ++ bname = sqfs_basename(basec); ++ ++ *file = strdup(bname); ++ ++ if (!*file) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (*dname == '\0') { ++ *dir = malloc(2); ++ if (!*dir) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ (*dir)[0] = '/'; ++ (*dir)[1] = '\0'; ++ } else { ++ *dir = strdup(dname); ++ if (!*dir) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ } ++ ++out: ++ if (ret) { ++ free(*file); ++ free(*dir); ++ *dir = NULL; ++ *file = NULL; ++ } ++ free(basec); ++ free(dirc); ++ 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 = get_unaligned_le32(®->file_size); ++ finfo->offset = get_unaligned_le32(®->offset); ++ finfo->start = get_unaligned_le32(®->start_block); ++ finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(®->fragment)); ++ ++ if (finfo->frag && finfo->offset == 0xFFFFFFFF) ++ return -EINVAL; ++ ++ if (finfo->size < 1 || finfo->start == 0xFFFFFFFF) ++ return -EINVAL; ++ ++ if (finfo->frag) { ++ datablk_count = finfo->size / le32_to_cpu(blksz); ++ ret = sqfs_frag_lookup(get_unaligned_le32(®->fragment), ++ fentry); ++ if (ret < 0) ++ return -EINVAL; ++ finfo->comp = true; ++ if (fentry->size < 1 || fentry->start == 0x7FFFFFFF) ++ return -EINVAL; ++ } 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 = get_unaligned_le64(&lreg->file_size); ++ finfo->offset = get_unaligned_le32(&lreg->offset); ++ finfo->start = get_unaligned_le64(&lreg->start_block); ++ finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment)); ++ ++ if (finfo->frag && finfo->offset == 0xFFFFFFFF) ++ return -EINVAL; ++ ++ if (finfo->size < 1 || finfo->start == 0x7FFFFFFF) ++ return -EINVAL; ++ ++ if (finfo->frag) { ++ datablk_count = finfo->size / le32_to_cpu(blksz); ++ ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment), ++ fentry); ++ if (ret < 0) ++ return -EINVAL; ++ finfo->comp = true; ++ if (fentry->size < 1 || fentry->start == 0x7FFFFFFF) ++ return -EINVAL; ++ } 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 = NULL, *fragment_block, *datablock = NULL, *data_buffer = NULL; ++ char *fragment = NULL, *file = NULL, *resolved, *data; ++ u64 start, n_blks, table_size, data_offset, table_offset; ++ int ret, j, i_number, datablk_count = 0; ++ struct squashfs_super_block *sblk = ctxt.sblk; ++ struct squashfs_fragment_block_entry frag_entry; ++ struct squashfs_file_info finfo = {0}; ++ struct squashfs_symlink_inode *symlink; ++ struct fs_dir_stream *dirsp = NULL; ++ 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; ++ ++ if (offset) { ++ /* ++ * TODO: implement reading at an offset in file ++ */ ++ printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * 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, &dirsp); ++ if (ret) { ++ goto out; ++ } ++ ++ dirs = (struct squashfs_dir_stream *)dirsp; ++ ++ /* For now, only regular files are able to be loaded */ ++ while (!sqfs_readdir(dirsp, &dent)) { ++ ret = strcmp(dent->name, file); ++ if (!ret) ++ break; ++ ++ free(dirs->entry); ++ dirs->entry = NULL; ++ } ++ ++ if (ret) { ++ printf("File not found.\n"); ++ *actread = 0; ++ ret = -ENOENT; ++ goto out; ++ } ++ ++ 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); ++ ++ base = (struct squashfs_base_inode *)ipos; ++ switch (get_unaligned_le16(&base->inode_type)) { ++ case SQFS_REG_TYPE: ++ reg = (struct squashfs_reg_inode *)ipos; ++ datablk_count = sqfs_get_regfile_info(reg, &finfo, &frag_entry, ++ sblk->block_size); ++ if (datablk_count < 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ memcpy(finfo.blk_sizes, ipos + sizeof(*reg), ++ datablk_count * sizeof(u32)); ++ break; ++ case SQFS_LREG_TYPE: ++ lreg = (struct squashfs_lreg_inode *)ipos; ++ datablk_count = sqfs_get_lregfile_info(lreg, &finfo, ++ &frag_entry, ++ sblk->block_size); ++ if (datablk_count < 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ memcpy(finfo.blk_sizes, ipos + sizeof(*lreg), ++ datablk_count * sizeof(u32)); ++ break; ++ case SQFS_SYMLINK_TYPE: ++ case SQFS_LSYMLINK_TYPE: ++ symlink = (struct squashfs_symlink_inode *)ipos; ++ resolved = sqfs_resolve_symlink(symlink, filename); ++ ret = sqfs_read(resolved, buf, offset, len, actread); ++ free(resolved); ++ goto out; ++ 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 = -EINVAL; ++ goto out; ++ } ++ ++ /* If the user specifies a length, check its sanity */ ++ if (len) { ++ if (len > finfo.size) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ finfo.size = len; ++ } else { ++ len = finfo.size; ++ } ++ ++ if (datablk_count) { ++ data_offset = finfo.start; ++ datablock = malloc(get_unaligned_le32(&sblk->block_size)); ++ if (!datablock) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ for (j = 0; j < datablk_count; j++) { ++ start = data_offset / ctxt.cur_dev->blksz; ++ table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]); ++ table_offset = data_offset - (start * ctxt.cur_dev->blksz); ++ n_blks = DIV_ROUND_UP(table_size + table_offset, ++ ctxt.cur_dev->blksz); ++ ++ data_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz); ++ ++ if (!data_buffer) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = sqfs_disk_read(start, n_blks, data_buffer); ++ if (ret < 0) { ++ /* ++ * Possible causes: too many data blocks or too large ++ * SquashFS block size. Tip: re-compile the SquashFS ++ * image with mksquashfs's -b option. ++ */ ++ printf("Error: too many data blocks to be read.\n"); ++ goto out; ++ } ++ ++ data = data_buffer + table_offset; ++ ++ /* Load the data */ ++ if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) { ++ dest_len = get_unaligned_le32(&sblk->block_size); ++ ret = sqfs_decompress(&ctxt, datablock, &dest_len, ++ data, table_size); ++ if (ret) ++ goto out; ++ ++ if ((*actread + dest_len) > len) ++ dest_len = len - *actread; ++ memcpy(buf + *actread, datablock, dest_len); ++ *actread += dest_len; ++ } else { ++ if ((*actread + table_size) > len) ++ table_size = len - *actread; ++ memcpy(buf + *actread, data, table_size); ++ *actread += table_size; ++ } ++ ++ data_offset += table_size; ++ free(data_buffer); ++ data_buffer = NULL; ++ if (*actread >= len) ++ break; ++ } ++ ++ /* ++ * There is no need to continue if the file is not fragmented. ++ */ ++ if (!finfo.frag) { ++ ret = 0; ++ goto out; ++ } ++ ++ start = frag_entry.start / ctxt.cur_dev->blksz; ++ table_size = SQFS_BLOCK_SIZE(frag_entry.size); ++ table_offset = frag_entry.start - (start * ctxt.cur_dev->blksz); ++ n_blks = DIV_ROUND_UP(table_size + table_offset, ctxt.cur_dev->blksz); ++ ++ fragment = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz); ++ ++ if (!fragment) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = sqfs_disk_read(start, n_blks, fragment); ++ if (ret < 0) ++ goto out; ++ ++ /* File compressed and fragmented */ ++ if (finfo.frag && finfo.comp) { ++ dest_len = get_unaligned_le32(&sblk->block_size); ++ fragment_block = malloc(dest_len); ++ if (!fragment_block) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = sqfs_decompress(&ctxt, fragment_block, &dest_len, ++ (void *)fragment + table_offset, ++ frag_entry.size); ++ if (ret) { ++ free(fragment_block); ++ goto out; ++ } ++ ++ for (j = *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 = *actread; j < finfo.size; j++) { ++ memcpy(buf + j, &fragment_block[finfo.offset + j], 1); ++ (*actread)++; ++ } ++ } ++ ++out: ++ free(fragment); ++ if (datablk_count) { ++ free(data_buffer); ++ free(datablock); ++ } ++ free(file); ++ free(dir); ++ free(finfo.blk_sizes); ++ sqfs_closedir(dirsp); ++ ++ return ret; ++} ++ ++int sqfs_size(const char *filename, loff_t *size) ++{ ++ struct squashfs_super_block *sblk = ctxt.sblk; ++ struct squashfs_symlink_inode *symlink; ++ struct fs_dir_stream *dirsp = NULL; ++ 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; ++ ++ 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, &dirsp); ++ if (ret) { ++ ret = -EINVAL; ++ goto free_strings; ++ } ++ ++ dirs = (struct squashfs_dir_stream *)dirsp; ++ ++ while (!sqfs_readdir(dirsp, &dent)) { ++ ret = strcmp(dent->name, file); ++ if (!ret) ++ break; ++ free(dirs->entry); ++ dirs->entry = NULL; ++ } ++ ++ if (ret) { ++ printf("File not found.\n"); ++ *size = 0; ++ ret = -EINVAL; ++ 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); ++ dirs->entry = NULL; ++ ++ base = (struct squashfs_base_inode *)ipos; ++ switch (get_unaligned_le16(&base->inode_type)) { ++ case SQFS_REG_TYPE: ++ reg = (struct squashfs_reg_inode *)ipos; ++ *size = get_unaligned_le32(®->file_size); ++ break; ++ case SQFS_LREG_TYPE: ++ lreg = (struct squashfs_lreg_inode *)ipos; ++ *size = get_unaligned_le64(&lreg->file_size); ++ break; ++ case SQFS_SYMLINK_TYPE: ++ case SQFS_LSYMLINK_TYPE: ++ symlink = (struct squashfs_symlink_inode *)ipos; ++ resolved = sqfs_resolve_symlink(symlink, filename); ++ ret = sqfs_size(resolved, size); ++ free(resolved); ++ 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; ++ ret = -EINVAL; ++ break; ++ } ++ ++free_strings: ++ free(dir); ++ free(file); ++ ++ sqfs_closedir(dirsp); ++ ++ return ret; ++} ++ ++int sqfs_exists(const char *filename) ++{ ++ struct fs_dir_stream *dirsp = NULL; ++ struct squashfs_dir_stream *dirs; ++ char *dir, *file; ++ struct fs_dirent *dent; ++ int 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, &dirsp); ++ if (ret) { ++ ret = -EINVAL; ++ goto free_strings; ++ } ++ ++ dirs = (struct squashfs_dir_stream *)dirsp; ++ ++ while (!sqfs_readdir(dirsp, &dent)) { ++ ret = strcmp(dent->name, file); ++ if (!ret) ++ break; ++ free(dirs->entry); ++ dirs->entry = NULL; ++ } ++ ++ sqfs_closedir(dirsp); ++ ++free_strings: ++ free(dir); ++ free(file); ++ ++ return ret == 0; ++} ++ ++void sqfs_close(void) ++{ ++ sqfs_decompressor_cleanup(&ctxt); ++ free(ctxt.sblk); ++ ctxt.sblk = NULL; ++ ctxt.cur_dev = NULL; ++} ++ ++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); ++ free(sqfs_dirs); ++} +diff --git a/fs/squashfs/sqfs_decompressor.c b/fs/squashfs/sqfs_decompressor.c +new file mode 100644 +index 00000000..d69ddb24 +--- /dev/null ++++ b/fs/squashfs/sqfs_decompressor.c +@@ -0,0 +1,155 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 Bootlin ++ * ++ * Author: Joao Marcos Costa ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#if IS_ENABLED(CONFIG_LZO) ++#include ++#endif ++ ++#if IS_ENABLED(CONFIG_ZLIB) ++#include ++#endif ++ ++#if IS_ENABLED(CONFIG_ZSTD) ++#include ++#endif ++ ++#include "sqfs_decompressor.h" ++#include "sqfs_utils.h" ++ ++int sqfs_decompressor_init(struct squashfs_ctxt *ctxt) ++{ ++ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); ++ ++ switch (comp_type) { ++#if IS_ENABLED(CONFIG_LZO) ++ case SQFS_COMP_LZO: ++ break; ++#endif ++#if IS_ENABLED(CONFIG_ZLIB) ++ case SQFS_COMP_ZLIB: ++ break; ++#endif ++#if IS_ENABLED(CONFIG_ZSTD) ++ case SQFS_COMP_ZSTD: ++ ctxt->zstd_workspace = malloc(ZSTD_DCtxWorkspaceBound()); ++ if (!ctxt->zstd_workspace) ++ return -ENOMEM; ++ break; ++#endif ++ default: ++ printf("Error: unknown compression type.\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt) ++{ ++ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); ++ ++ switch (comp_type) { ++#if IS_ENABLED(CONFIG_LZO) ++ case SQFS_COMP_LZO: ++ break; ++#endif ++#if IS_ENABLED(CONFIG_ZLIB) ++ case SQFS_COMP_ZLIB: ++ break; ++#endif ++#if IS_ENABLED(CONFIG_ZSTD) ++ case SQFS_COMP_ZSTD: ++ free(ctxt->zstd_workspace); ++ break; ++#endif ++ } ++} ++ ++#if IS_ENABLED(CONFIG_ZLIB) ++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; ++ } ++} ++#endif ++ ++#if IS_ENABLED(CONFIG_ZSTD) ++static int sqfs_zstd_decompress(struct squashfs_ctxt *ctxt, void *dest, ++ unsigned long dest_len, void *source, u32 src_len) ++{ ++ ZSTD_DCtx *ctx; ++ size_t wsize; ++ int ret; ++ ++ wsize = ZSTD_DCtxWorkspaceBound(); ++ ctx = ZSTD_initDCtx(ctxt->zstd_workspace, wsize); ++ ret = ZSTD_decompressDCtx(ctx, dest, dest_len, source, src_len); ++ ++ return ZSTD_isError(ret); ++} ++#endif /* CONFIG_ZSTD */ ++ ++int sqfs_decompress(struct squashfs_ctxt *ctxt, void *dest, ++ unsigned long *dest_len, void *source, u32 src_len) ++{ ++ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); ++ int ret = 0; ++ ++ switch (comp_type) { ++#if IS_ENABLED(CONFIG_LZO) ++ case SQFS_COMP_LZO: { ++ size_t lzo_dest_len = *dest_len; ++ ret = lzo1x_decompress_safe(source, src_len, dest, &lzo_dest_len); ++ if (ret) { ++ printf("LZO decompression failed. Error code: %d\n", ret); ++ return -EINVAL; ++ } ++ ++ break; ++ } ++#endif ++#if IS_ENABLED(CONFIG_ZLIB) ++ case SQFS_COMP_ZLIB: ++ ret = uncompress(dest, dest_len, source, src_len); ++ if (ret) { ++ zlib_decompression_status(ret); ++ return -EINVAL; ++ } ++ ++ break; ++#endif ++#if IS_ENABLED(CONFIG_ZSTD) ++ case SQFS_COMP_ZSTD: ++ ret = sqfs_zstd_decompress(ctxt, dest, *dest_len, source, src_len); ++ if (ret) { ++ printf("ZSTD Error code: %d\n", ZSTD_getErrorCode(ret)); ++ return -EINVAL; ++ } ++ ++ break; ++#endif ++ 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 00000000..892cfb69 +--- /dev/null ++++ b/fs/squashfs/sqfs_decompressor.h +@@ -0,0 +1,61 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 Bootlin ++ * ++ * Author: Joao Marcos Costa ++ */ ++ ++#ifndef SQFS_DECOMPRESSOR_H ++#define SQFS_DECOMPRESSOR_H ++ ++#include ++#include "sqfs_filesystem.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(struct squashfs_ctxt *ctxt, void *dest, ++ unsigned long *dest_len, void *source, u32 src_len); ++int sqfs_decompressor_init(struct squashfs_ctxt *ctxt); ++void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt); ++ ++#endif /* SQFS_DECOMPRESSOR_H */ +diff --git a/fs/squashfs/sqfs_dir.c b/fs/squashfs/sqfs_dir.c +new file mode 100644 +index 00000000..a265b98f +--- /dev/null ++++ b/fs/squashfs/sqfs_dir.c +@@ -0,0 +1,93 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 Bootlin ++ * ++ * Author: Joao Marcos Costa ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sqfs_filesystem.h" ++#include "sqfs_utils.h" ++ ++bool sqfs_is_dir(u16 type) ++{ ++ return type == SQFS_DIR_TYPE || 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 = dir_i; ++ struct squashfs_ldir_inode *ldir; ++ struct squashfs_dir_inode *dir; ++ u32 start_block; ++ int j, offset; ++ ++ switch (get_unaligned_le16(&base->inode_type)) { ++ case SQFS_DIR_TYPE: ++ dir = (struct squashfs_dir_inode *)base; ++ start_block = get_unaligned_le32(&dir->start_block); ++ offset = get_unaligned_le16(&dir->offset); ++ break; ++ case SQFS_LDIR_TYPE: ++ ldir = (struct squashfs_ldir_inode *)base; ++ start_block = get_unaligned_le32(&ldir->start_block); ++ offset = get_unaligned_le16(&ldir->offset); ++ break; ++ default: ++ printf("Error: this is not a directory.\n"); ++ return -EINVAL; ++ } ++ ++ if (offset < 0) ++ 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 = dir_i; ++ struct squashfs_ldir_inode *ldir; ++ struct squashfs_dir_inode *dir; ++ u32 file_size; ++ ++ switch (get_unaligned_le16(&base->inode_type)) { ++ case SQFS_DIR_TYPE: ++ dir = (struct squashfs_dir_inode *)base; ++ file_size = get_unaligned_le16(&dir->file_size); ++ break; ++ case SQFS_LDIR_TYPE: ++ ldir = (struct squashfs_ldir_inode *)base; ++ file_size = get_unaligned_le16(&ldir->file_size); ++ break; ++ default: ++ printf("Error: this is not a directory.\n"); ++ return false; ++ } ++ ++ 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 00000000..856cd15e +--- /dev/null ++++ b/fs/squashfs/sqfs_filesystem.h +@@ -0,0 +1,310 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 Bootlin ++ * ++ * Author: Joao Marcos Costa ++ */ ++ ++#ifndef SQFS_FILESYSTEM_H ++#define SQFS_FILESYSTEM_H ++ ++#include ++#include ++#include ++#include ++ ++#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 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_ctxt { ++ struct disk_partition cur_part_info; ++ struct blk_desc *cur_dev; ++ struct squashfs_super_block *sblk; ++#if IS_ENABLED(CONFIG_ZSTD) ++ void *zstd_workspace; ++#endif ++}; ++ ++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(u16 type); ++ ++#endif /* SQFS_FILESYSTEM_H */ +diff --git a/fs/squashfs/sqfs_inode.c b/fs/squashfs/sqfs_inode.c +new file mode 100644 +index 00000000..e76ec7cb +--- /dev/null ++++ b/fs/squashfs/sqfs_inode.c +@@ -0,0 +1,161 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 Bootlin ++ * ++ * Author: Joao Marcos Costa ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sqfs_decompressor.h" ++#include "sqfs_filesystem.h" ++#include "sqfs_utils.h" ++ ++int sqfs_inode_size(struct squashfs_base_inode *inode, u32 blk_size) ++{ ++ switch (get_unaligned_le16(&inode->inode_type)) { ++ case SQFS_DIR_TYPE: ++ return sizeof(struct squashfs_dir_inode); ++ ++ case SQFS_REG_TYPE: { ++ struct squashfs_reg_inode *reg = ++ (struct squashfs_reg_inode *)inode; ++ u32 fragment = get_unaligned_le32(®->fragment); ++ u32 file_size = get_unaligned_le32(®->file_size); ++ unsigned int blk_list_size; ++ ++ if (SQFS_IS_FRAGMENTED(fragment)) ++ blk_list_size = file_size / blk_size; ++ else ++ blk_list_size = DIV_ROUND_UP(file_size, blk_size); ++ ++ return sizeof(*reg) + blk_list_size * sizeof(u32); ++ } ++ ++ case SQFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode *ldir = ++ (struct squashfs_ldir_inode *)inode; ++ u16 i_count = get_unaligned_le16(&ldir->i_count); ++ unsigned int index_list_size = 0, l = 0; ++ struct squashfs_directory_index *di; ++ u32 sz; ++ ++ if (i_count == 0) ++ return sizeof(*ldir); ++ ++ di = ldir->index; ++ while (l < i_count) { ++ sz = get_unaligned_le32(&di->size) + 1; ++ index_list_size += sz; ++ di = (void *)di + sizeof(*di) + sz; ++ l++; ++ } ++ ++ return sizeof(*ldir) + index_list_size + ++ i_count * SQFS_DIR_INDEX_BASE_LENGTH; ++ } ++ ++ case SQFS_LREG_TYPE: { ++ struct squashfs_lreg_inode *lreg = ++ (struct squashfs_lreg_inode *)inode; ++ u32 fragment = get_unaligned_le32(&lreg->fragment); ++ u64 file_size = get_unaligned_le64(&lreg->file_size); ++ unsigned int blk_list_size; ++ ++ if (fragment == 0xFFFFFFFF) ++ blk_list_size = DIV_ROUND_UP(file_size, blk_size); ++ else ++ blk_list_size = file_size / blk_size; ++ ++ return sizeof(*lreg) + blk_list_size * sizeof(u32); ++ } ++ ++ case SQFS_SYMLINK_TYPE: ++ case SQFS_LSYMLINK_TYPE: { ++ struct squashfs_symlink_inode *symlink = ++ (struct squashfs_symlink_inode *)inode; ++ ++ return sizeof(*symlink) + ++ get_unaligned_le32(&symlink->symlink_size); ++ } ++ ++ case SQFS_BLKDEV_TYPE: ++ case SQFS_CHRDEV_TYPE: ++ return sizeof(struct squashfs_dev_inode); ++ case SQFS_LBLKDEV_TYPE: ++ case SQFS_LCHRDEV_TYPE: ++ return sizeof(struct squashfs_ldev_inode); ++ case SQFS_FIFO_TYPE: ++ case SQFS_SOCKET_TYPE: ++ return sizeof(struct squashfs_ipc_inode); ++ case SQFS_LFIFO_TYPE: ++ case SQFS_LSOCKET_TYPE: ++ return sizeof(struct squashfs_lipc_inode); ++ default: ++ printf("Error while searching inode: unknown type.\n"); ++ return -EINVAL; ++ } ++} ++ ++/* ++ * 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) ++{ ++ struct squashfs_base_inode *base; ++ unsigned int offset = 0, k; ++ int sz; ++ ++ if (!inode_table) { ++ printf("%s: Invalid pointer to inode table.\n", __func__); ++ return NULL; ++ } ++ ++ for (k = 0; k < le32_to_cpu(inode_count); k++) { ++ base = inode_table + offset; ++ if (get_unaligned_le32(&base->inode_number) == inode_number) ++ return inode_table + offset; ++ ++ sz = sqfs_inode_size(base, le32_to_cpu(block_size)); ++ if (sz < 0) ++ return NULL; ++ ++ offset += sz; ++ } ++ ++ printf("Inode not found.\n"); ++ ++ return NULL; ++} ++ ++int sqfs_read_metablock(unsigned char *file_mapping, int offset, ++ bool *compressed, u32 *data_size) ++{ ++ const unsigned char *data; ++ u16 header; ++ ++ if (!file_mapping) ++ return -EFAULT; ++ data = file_mapping + offset; ++ ++ header = get_unaligned((u16 *)data); ++ if (!header) ++ return -EINVAL; ++ ++ *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 00000000..1260abe2 +--- /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 ++ */ ++ ++#ifndef SQFS_UTILS_H ++#define SQFS_UTILS_H ++ ++#include ++#include ++#include ++ ++#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 742a535b..d3d81f9f 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 + + /* + * Tell the fs layer which block device an partition to use for future +diff --git a/include/hang.h b/include/hang.h +new file mode 100644 +index 00000000..27cda493 +--- /dev/null ++++ b/include/hang.h +@@ -0,0 +1,22 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * (C) Copyright 2000-2009 ++ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. ++ */ ++ ++#ifndef __HANG_H ++#define __HANG_H ++ ++#ifndef __ASSEMBLY__ ++/** ++ * hang() - Print a message and stop execution ++ * ++ * This shows a 'hang' message where possible and then goes into an infinite ++ * loop. This is called by panic() if CONFIG_PANIC_HANG is enabled. ++ * ++ * This function does not return. ++ */ ++void hang(void) __attribute__ ((noreturn)); ++#endif ++ ++#endif +diff --git a/include/squashfs.h b/include/squashfs.h +new file mode 100644 +index 00000000..7489eefa +--- /dev/null ++++ b/include/squashfs.h +@@ -0,0 +1,26 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 Bootlin ++ * ++ * Author: Joao Marcos Costa ++ * ++ * squashfs.h: SquashFS filesystem implementation. ++ */ ++ ++#ifndef _SQFS_H_ ++#define _SQFS_H_ ++ ++struct disk_partition; ++ ++int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp); ++int sqfs_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp); ++int sqfs_probe(struct blk_desc *fs_dev_desc, ++ 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); ++int sqfs_exists(const char *filename); ++void sqfs_close(void); ++void sqfs_closedir(struct fs_dir_stream *dirs); ++ ++#endif /* SQFS_H */ +diff --git a/include/u-boot/zlib.h b/include/u-boot/zlib.h +index e23ceb50..a33cc878 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/Kconfig b/lib/Kconfig +index 965cf7bc..26410eb5 100644 +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -398,6 +398,13 @@ config GZIP + help + This enables support for GZIP compression algorithm. + ++config ZLIB_UNCOMPRESS ++ bool "Enables zlib's uncompress() functionality" ++ help ++ This enables an extra zlib functionality: the uncompress() function, ++ which decompresses data from a buffer into another, knowing their ++ sizes. Unlike gunzip(), there is no header parsing. ++ + config ZLIB + bool + default y +diff --git a/lib/zlib/inftrees.c b/lib/zlib/inftrees.c +index b71b9695..caee502f 100644 +--- a/lib/zlib/inftrees.c ++++ b/lib/zlib/inftrees.c +@@ -50,7 +50,7 @@ int inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ +- int end; /* use base and extra for symbol > end */ ++ unsigned match; /* use base and extra for symbol >= match */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ +@@ -178,19 +178,17 @@ int inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ +- end = 19; ++ match = 20; + break; + case LENS: + base = lbase; +- base -= 257; + extra = lext; +- extra -= 257; +- end = 256; ++ match = 257; + break; + default: /* DISTS */ + base = dbase; + extra = dext; +- end = -1; ++ match = 0; + } + + /* initialize state for loop */ +@@ -212,13 +210,12 @@ int inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); +- if ((int)(work[sym]) < end) { ++ if (work[sym] + 1 < match) { + this.op = (unsigned char)0; + this.val = work[sym]; +- } +- else if ((int)(work[sym]) > end) { +- this.op = (unsigned char)(extra[work[sym]]); +- this.val = base[work[sym]]; ++ } else if (work[sym] >= match) { ++ this.op = (unsigned char)(extra[work[sym] - match]); ++ this.val = base[work[sym] - match]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ +diff --git a/lib/zlib/trees.c b/lib/zlib/trees.c +index a0078d08..3e09517e 100644 +--- a/lib/zlib/trees.c ++++ b/lib/zlib/trees.c +@@ -7,27 +7,28 @@ + /* + * ALGORITHM + * +- * The "deflation" process uses several Huffman trees. The more +- * common source values are represented by shorter bit sequences. ++ * The "deflation" process uses several Huffman trees. The more ++ * common source values are represented by shorter bit sequences. + * +- * Each code tree is stored in a compressed form which is itself +- * a Huffman encoding of the lengths of all the code strings (in +- * ascending order by source values). The actual code strings are +- * reconstructed from the lengths in the inflate process, as described +- * in the deflate specification. ++ * Each code tree is stored in a compressed form which is itself ++ * a Huffman encoding of the lengths of all the code strings (in ++ * ascending order by source values). The actual code strings are ++ * reconstructed from the lengths in the inflate process, as ++ * described in the deflate specification. + * + * REFERENCES + * +- * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". +- * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc ++ * Deutsch, P. ++ * RFC 1951, DEFLATE Compressed Data Format Specification version 1.3 ++ * https://tools.ietf.org/html/rfc1951, 1996 + * +- * Storer, James A. +- * Data Compression: Methods and Theory, pp. 49-50. +- * Computer Science Press, 1988. ISBN 0-7167-8156-5. ++ * Storer, James A. ++ * Data Compression: Methods and Theory, pp. 49-50. ++ * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * +- * Sedgewick, R. +- * Algorithms, p290. +- * Addison-Wesley, 1983. ISBN 0-201-06672-6. ++ * Sedgewick, R. ++ * Algorithms, p290. ++ * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + + /* @(#) $Id$ */ +diff --git a/lib/zlib/uncompr.c b/lib/zlib/uncompr.c +new file mode 100644 +index 00000000..21e93933 +--- /dev/null ++++ b/lib/zlib/uncompr.c +@@ -0,0 +1,97 @@ ++/* 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; ++ /* for detection of incomplete stream when *destLen == 0 */ ++ Byte buf[1]; ++ ++ 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 7e157029..90e05e7d 100644 +--- a/lib/zlib/zlib.c ++++ b/lib/zlib/zlib.c +@@ -30,3 +30,6 @@ + #include "inflate.c" + #include "zutil.c" + #include "adler32.c" ++#if IS_ENABLED(CONFIG_ZLIB_UNCOMPRESS) ++#include "uncompr.c" ++#endif +diff --git a/lib/zlib/zutil.c b/lib/zlib/zutil.c +index 227343e4..609aac55 100644 +--- a/lib/zlib/zutil.c ++++ b/lib/zlib/zutil.c +@@ -6,6 +6,7 @@ + /* @(#) $Id$ */ + + #include "zutil.h" ++#include + + #ifndef NO_DUMMY_DECL + struct internal_state {int dummy;}; /* for buggy compilers */ +@@ -34,7 +35,7 @@ void z_error (m) + char *m; + { + fprintf(stderr, "%s\n", m); +- hang (); ++ hang(); + } + #endif + +diff --git a/test/py/tests/test_fs/test_squashfs/sqfs_common.py b/test/py/tests/test_fs/test_squashfs/sqfs_common.py +new file mode 100644 +index 00000000..c96f92c1 +--- /dev/null ++++ b/test/py/tests/test_fs/test_squashfs/sqfs_common.py +@@ -0,0 +1,76 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2020 Bootlin ++# Author: Joao Marcos Costa ++ ++import os ++import random ++import string ++import subprocess ++ ++def sqfs_get_random_letters(size): ++ letters = [] ++ for i in range(0, size): ++ letters.append(random.choice(string.ascii_letters)) ++ ++ return ''.join(letters) ++ ++def sqfs_generate_file(path, size): ++ content = sqfs_get_random_letters(size) ++ file = open(path, "w") ++ file.write(content) ++ file.close() ++ ++class Compression: ++ def __init__(self, name, files, sizes, block_size = 4096): ++ self.name = name ++ self.files = files ++ self.sizes = sizes ++ self.mksquashfs_opts = " -b " + str(block_size) + " -comp " + self.name ++ ++ def add_opt(self, opt): ++ self.mksquashfs_opts += " " + opt ++ ++ def gen_image(self, build_dir): ++ src = os.path.join(build_dir, "sqfs_src/") ++ os.mkdir(src) ++ for (f, s) in zip(self.files, self.sizes): ++ sqfs_generate_file(src + f, s) ++ ++ # the symbolic link always targets the first file ++ os.symlink(self.files[0], src + "sym") ++ ++ sqfs_img = os.path.join(build_dir, "sqfs-" + self.name) ++ i_o = src + " " + sqfs_img ++ opts = self.mksquashfs_opts ++ try: ++ subprocess.run(["mksquashfs " + i_o + opts], shell = True, check = True) ++ except: ++ print("mksquashfs error. Compression type: " + self.name) ++ raise RuntimeError ++ ++ def clean_source(self, build_dir): ++ src = os.path.join(build_dir, "sqfs_src/") ++ for f in self.files: ++ os.remove(src + f) ++ os.remove(src + "sym") ++ os.rmdir(src) ++ ++ def cleanup(self, build_dir): ++ self.clean_source(build_dir) ++ sqfs_img = os.path.join(build_dir, "sqfs-" + self.name) ++ os.remove(sqfs_img) ++ ++files = ["blks_only", "blks_frag", "frag_only"] ++sizes = [4096, 5100, 100] ++gzip = Compression("gzip", files, sizes) ++zstd = Compression("zstd", files, sizes) ++lzo = Compression("lzo", files, sizes) ++ ++# use fragment blocks for files larger than block_size ++gzip.add_opt("-always-use-fragments") ++zstd.add_opt("-always-use-fragments") ++ ++# avoid fragments if lzo is used ++lzo.add_opt("-no-fragments") ++ ++comp_opts = [gzip, zstd, lzo] +diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py b/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py +new file mode 100644 +index 00000000..9e900623 +--- /dev/null ++++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py +@@ -0,0 +1,46 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2020 Bootlin ++# Author: Joao Marcos Costa ++ ++import os ++import pytest ++from sqfs_common import * ++ ++@pytest.mark.boardspec('sandbox') ++@pytest.mark.buildconfigspec('cmd_fs_generic') ++@pytest.mark.buildconfigspec('cmd_squashfs') ++@pytest.mark.buildconfigspec('fs_squashfs') ++@pytest.mark.requiredtool('mksquashfs') ++def test_sqfs_load(u_boot_console): ++ build_dir = u_boot_console.config.build_dir ++ command = "sqfsload host 0 $kernel_addr_r " ++ ++ for opt in comp_opts: ++ # generate and load the squashfs image ++ try: ++ opt.gen_image(build_dir) ++ except RuntimeError: ++ opt.clean_source(build_dir) ++ # skip unsupported compression types ++ continue ++ ++ path = os.path.join(build_dir, "sqfs-" + opt.name) ++ output = u_boot_console.run_command("host bind 0 " + path) ++ ++ output = u_boot_console.run_command(command + "xxx") ++ assert "File not found." in output ++ ++ for (f, s) in zip(opt.files, opt.sizes): ++ try: ++ output = u_boot_console.run_command(command + f) ++ assert str(s) in output ++ except: ++ assert False ++ opt.cleanup(build_dir) ++ ++ # test symbolic link ++ output = u_boot_console.run_command(command + "sym") ++ assert str(opt.sizes[0]) in output ++ ++ # remove generated files ++ opt.cleanup(build_dir) +diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py +new file mode 100644 +index 00000000..a0dca2e2 +--- /dev/null ++++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py +@@ -0,0 +1,36 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (C) 2020 Bootlin ++# Author: Joao Marcos Costa ++ ++import os ++import pytest ++from sqfs_common import * ++ ++@pytest.mark.boardspec('sandbox') ++@pytest.mark.buildconfigspec('cmd_fs_generic') ++@pytest.mark.buildconfigspec('cmd_squashfs') ++@pytest.mark.buildconfigspec('fs_squashfs') ++@pytest.mark.requiredtool('mksquashfs') ++def test_sqfs_ls(u_boot_console): ++ build_dir = u_boot_console.config.build_dir ++ for opt in comp_opts: ++ try: ++ opt.gen_image(build_dir) ++ except RuntimeError: ++ opt.clean_source(build_dir) ++ # skip unsupported compression types ++ continue ++ path = os.path.join(build_dir, "sqfs-" + opt.name) ++ output = u_boot_console.run_command("host bind 0 " + path) ++ ++ try: ++ # list files in root directory ++ output = u_boot_console.run_command("sqfsls host 0") ++ assert str(len(opt.files) + 1) + " file(s), 0 dir(s)" in output ++ assert " sym" in output ++ output = u_boot_console.run_command("sqfsls host 0 xxx") ++ assert "** Cannot find directory. **" in output ++ except: ++ opt.cleanup(build_dir) ++ assert False ++ opt.cleanup(build_dir) +-- +2.25.1 + From 8a55ebe83c825a87ea8bee507340d76a46d698b5 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Sat, 20 Feb 2021 20:41:42 -0500 Subject: [PATCH 02/12] Pull the kernel and device tree from the rootfs --- fwup.conf | 14 ------------ nerves_defconfig | 5 +++- uboot/uboot.defconfig | 7 ++---- uboot/uboot.env | 53 +++++++++++++++++++++++++++++++++++-------- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/fwup.conf b/fwup.conf index 6334fa1..66d0f0b 100644 --- a/fwup.conf +++ b/fwup.conf @@ -41,8 +41,6 @@ define(ROOTFS, "${NERVES_SYSTEM}/images/rootfs.squashfs") # +----------------------------+ # | p0: Boot partition (FAT32) | # | u-boot.img | -# | zImage.a | -# | zImage.b | # +----------------------------+ # | p1: Rootfs A (squashfs) | # +----------------------------+ @@ -94,12 +92,6 @@ file-resource u-boot.bin { file-resource uboot-env.bin { host-path = "${NERVES_SYSTEM}/images/uboot-env.bin" } -file-resource zImage { - host-path = "${NERVES_SYSTEM}/images/zImage" -} -file-resource at91-sama5d27_wlsom1_ek.dtb { - host-path = "${NERVES_SYSTEM}/images/at91-sama5d27_wlsom1_ek.dtb" -} file-resource rootfs.img { host-path = ${ROOTFS} @@ -151,14 +143,10 @@ task complete { fat_mkfs(${BOOT_PART_OFFSET}, ${BOOT_PART_COUNT}) fat_setlabel(${BOOT_PART_OFFSET}, "BOOT") - fat_mkdir(${BOOT_PART_OFFSET}, "overlays") - fat_mkdir(${BOOT_PART_OFFSET}, "dtbs") } on-resource boot.bin { fat_write(${BOOT_PART_OFFSET}, "boot.bin") } on-resource u-boot.bin { fat_write(${BOOT_PART_OFFSET}, "u-boot.bin") } - on-resource at91-sama5d27_wlsom1_ek.dtb { fat_write(${BOOT_PART_OFFSET}, "dtbs/at91-sama5d27_wlsom1_ek.dtb") } - on-resource zImage { fat_write(${BOOT_PART_OFFSET}, "zImage.a") } on-resource uboot-env.bin { # Boot to the A partition first and don't fail back. @@ -229,7 +217,6 @@ task upgrade.a { trim(${ROOTFS_A_PART_OFFSET}, ${ROOTFS_A_PART_COUNT}) } - on-resource zImage { fat_write(${BOOT_PART_OFFSET}, "zImage.a") } on-resource rootfs.img { raw_write(${ROOTFS_A_PART_OFFSET}) } on-finish { @@ -280,7 +267,6 @@ task upgrade.b { trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT}) } - on-resource zImage { fat_write(${BOOT_PART_OFFSET}, "zImage.b") } on-resource rootfs.img { raw_write(${ROOTFS_B_PART_OFFSET}) } on-finish { diff --git a/nerves_defconfig b/nerves_defconfig index 719ec09..8f2ea09 100644 --- a/nerves_defconfig +++ b/nerves_defconfig @@ -29,6 +29,8 @@ BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${NERVES_DEFCONFIG_DIR}/linux/linux-5.4.defconfig" BR2_LINUX_KERNEL_DTS_SUPPORT=y BR2_LINUX_KERNEL_INTREE_DTS_NAME="at91-sama5d27_wlsom1_ek" +BR2_LINUX_KERNEL_DTB_OVERLAY_SUPPORT=y +BR2_LINUX_KERNEL_INSTALL_TARGET=y BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_NERVES_PATH}/board/nerves-common/busybox-1.22.config" BR2_PACKAGE_F2FS_TOOLS=y BR2_PACKAGE_LIBMNL=y @@ -48,10 +50,11 @@ BR2_TARGET_UBOOT=y BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y BR2_TARGET_UBOOT_CUSTOM_GIT=y BR2_TARGET_UBOOT_CUSTOM_REPO_URL="https://github.com/linux4sam/u-boot-at91.git" -BR2_TARGET_UBOOT_CUSTOM_REPO_VERSION="linux4sam-2020.04" +BR2_TARGET_UBOOT_CUSTOM_REPO_VERSION="linux4sam-2020.10.1" BR2_TARGET_UBOOT_PATCH="${NERVES_DEFCONFIG_DIR}/uboot" BR2_TARGET_UBOOT_USE_CUSTOM_CONFIG=y BR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE="${NERVES_DEFCONFIG_DIR}/uboot/uboot.defconfig" +BR2_PACKAGE_HOST_UBOOT_TOOLS=y BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE=y BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SOURCE="${NERVES_DEFCONFIG_DIR}/uboot/uboot.env" BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SIZE="131072" diff --git a/uboot/uboot.defconfig b/uboot/uboot.defconfig index fd0aefe..745483d 100644 --- a/uboot/uboot.defconfig +++ b/uboot/uboot.defconfig @@ -11,7 +11,6 @@ CONFIG_SPL_SERIAL_SUPPORT=y CONFIG_SPL_DRIVERS_MISC_SUPPORT=y CONFIG_ENV_OFFSET=0x00100000 CONFIG_SPL=y -CONFIG_DEBUG_UART_BOARD_INIT=y CONFIG_DEBUG_UART_BASE=0xf801c000 CONFIG_DEBUG_UART_CLOCK=82000000 CONFIG_SPL_FS_FAT=y @@ -27,9 +26,9 @@ CONFIG_USE_BOOTARGS=y CONFIG_MISC_INIT_R=y # CONFIG_DISPLAY_BOARDINFO is not set CONFIG_DISPLAY_BOARDINFO_LATE=y -CONFIG_SPL_SEPARATE_BSS=y CONFIG_SPL_DISPLAY_PRINT=y # CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR is not set +CONFIG_SPL_FS_SQUASHFS=y CONFIG_SPL_AT91_MCK_BYPASS=y CONFIG_HUSH_PARSER=y CONFIG_CMD_BOOTZ=y @@ -69,7 +68,7 @@ CONFIG_MMC_SDHCI_ATMEL=y CONFIG_MTD=y CONFIG_DM_SPI_FLASH=y CONFIG_SF_DEFAULT_BUS=2 -CONFIG_SF_DEFAULT_SPEED=50000000 +CONFIG_SF_DEFAULT_SPEED=66000000 CONFIG_SPI_FLASH_SFDP_SUPPORT=y CONFIG_SPI_FLASH_ATMEL=y CONFIG_SPI_FLASH_MACRONIX=y @@ -84,7 +83,6 @@ CONFIG_SPL_PINCTRL=y CONFIG_PINCTRL_AT91PIO4=y CONFIG_DM_SERIAL=y CONFIG_DEBUG_UART_ATMEL=y -CONFIG_DEBUG_UART_ANNOUNCE=y CONFIG_ATMEL_USART=y CONFIG_SPI=y CONFIG_DM_SPI=y @@ -102,6 +100,5 @@ CONFIG_W1=y CONFIG_W1_GPIO=y CONFIG_W1_EEPROM=y CONFIG_W1_EEPROM_DS24XXX=y -CONFIG_FAT_WRITE=y CONFIG_OF_LIBFDT_OVERLAY=y # CONFIG_EFI_LOADER_HII is not set diff --git a/uboot/uboot.env b/uboot/uboot.env index 196951f..363c017 100644 --- a/uboot/uboot.env +++ b/uboot/uboot.env @@ -49,7 +49,6 @@ nerves_init=\ fi;\ saveenv;\ fi;\ - setenv bootfile zImage.${nerves_fw_active};\ if test ${nerves_fw_active} = "a"; then\ setenv uenv_root /dev/mmcblk0p2;\ setenv bootpart 0:2;\ @@ -58,9 +57,7 @@ nerves_init=\ setenv bootpart 0:3;\ fi -# Main Device Tree -fdtfile=/dtbs/at91-sama5d27_wlsom1_ek.dtb - +video=0 console=ttyS0,115200 optargs=atmel.pm_modes=standby,ulp1 quiet @@ -69,14 +66,52 @@ fdt_high=0xffffffff fdtovaddr=0x21800000 loadaddr=0x22000000 fdtaddr=0x21000000 -video=0 -kernel_bootpart=0:1 +fdtfile=/boot/at91-sama5d27_wlsom1_ek.dtb +bootfile=/boot/zImage + +loadoverlay=load mmc ${bootpart} ${fdtovaddr} ${uboot_overlay_addr0} +loadoverlay1=load mmc ${bootpart} ${fdtovaddr} ${uboot_overlay_addr1} +loadoverlay2=load mmc ${bootpart} ${fdtovaddr} ${uboot_overlay_addr2} +loadoverlay3=load mmc ${bootpart} ${fdtovaddr} ${uboot_overlay_addr3} +loadoverlay4=load mmc ${bootpart} ${fdtovaddr} ${uboot_overlay_addr5} mmcargs=setenv bootargs console=${console} ${optargs} rootfstype=squashfs rootwait root=${uenv_root} rootwait video=${video} -loadzimage=load mmc ${kernel_bootpart} ${loadaddr} ${bootfile} -loadfdt=load mmc ${kernel_bootpart} ${fdtaddr} ${fdtfile} +loadzimage=load mmc ${bootpart} ${loadaddr} ${bootfile} +loadfdt=load mmc ${bootpart} ${fdtaddr} ${fdtfile} +loadoverlays=echo Checking for overlays ...;\ + if test -n $enable_uboot_overlays; then\ + fdt addr ${fdtaddr};\ + fdt resize 4096;\ + if test -n $uboot_overlay_addr0; then\ + run loadoverlay;\ + fdt apply ${fdtovaddr};\ + echo loaded ${uboot_overlay_addr0};\ + fi;\ + if test -n $uboot_overlay_addr1; then\ + run loadoverlay1;\ + fdt apply ${fdtovaddr};\ + echo loaded ${uboot_overlay_addr1};\ + fi;\ + if test -n $uboot_overlay_addr2; then\ + run loadoverlay2;\ + fdt apply ${fdtovaddr};\ + echo loaded ${uboot_overlay_addr2};\ + fi;\ + if test -n $uboot_overlay_addr3; then\ + run loadoverlay3;\ + fdt apply ${fdtovaddr};\ + echo loaded ${uboot_overlay_addr3};\ + fi;\ + if test -n $uboot_overlay_addr4; then\ + run loadoverlay4;\ + fdt apply ${fdtovaddr};\ + echo loaded ${uboot_overlay_addr4};\ + fi;\ + fi;true + mmcboot=echo Booting from mmc...;\ run mmcargs;\ bootz ${loadaddr} - ${fdtaddr} -bootcmd=run nerves_init loadzimage loadfdt mmcboot +bootcmd=run nerves_init loadzimage loadfdt loadoverlays mmcboot + From 09cb3f8ca2b222f99f1a76730c22caf4280d3683 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Sat, 20 Feb 2021 20:43:48 -0500 Subject: [PATCH 03/12] toolchain: bump to v1.4.1 --- nerves_defconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nerves_defconfig b/nerves_defconfig index 8f2ea09..a16d765 100644 --- a/nerves_defconfig +++ b/nerves_defconfig @@ -7,7 +7,7 @@ BR2_GLOBAL_PATCH_DIR="${BR2_EXTERNAL_NERVES_PATH}/patches" BR2_TOOLCHAIN_EXTERNAL=y BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y -BR2_TOOLCHAIN_EXTERNAL_URL="https://github.com/nerves-project/toolchains/releases/download/v1.4.0/nerves_toolchain_armv7_nerves_linux_gnueabihf-linux_x86_64-1.4.0-CE0FCC7.tar.xz" +BR2_TOOLCHAIN_EXTERNAL_URL="https://github.com/nerves-project/toolchains/releases/download/v1.4.1/nerves_toolchain_armv7_nerves_linux_gnueabihf-linux_x86_64-1.4.1-4C8725A.tar.xz" BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="armv7-nerves-linux-gnueabihf" BR2_TOOLCHAIN_EXTERNAL_HEADERS_4_4=y BR2_TOOLCHAIN_EXTERNAL_CUSTOM_GLIBC=y @@ -23,7 +23,7 @@ BR2_ROOTFS_POST_BUILD_SCRIPT="${NERVES_DEFCONFIG_DIR}/post-build.sh ${BR2_EXTERN BR2_ROOTFS_POST_IMAGE_SCRIPT="${NERVES_DEFCONFIG_DIR}/post-createfs.sh" BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_TARBALL=y -BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="$(call github,linux4sam,linux-at91,linux4sam-2020.10-rc5)/linux4sam-2020.10-rc5.tar.gz" +BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="$(call github,linux4sam,linux-at91,wilc_15_4_1)/wilc_15_4_1.tar.gz" BR2_LINUX_KERNEL_PATCH="${NERVES_DEFCONFIG_DIR}/linux" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${NERVES_DEFCONFIG_DIR}/linux/linux-5.4.defconfig" From a139a04edbef20d44a448be62f6bc05279e48501 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Sat, 20 Feb 2021 20:44:27 -0500 Subject: [PATCH 04/12] Limit the SDIO max frequency for the wilc The WILC3000 seems to struggle with transferring large files when operating at max speed at 50Mhz. The driver would stop working and result in an error message cfg_indicate_rx: Receive unknown message After this message the wifi no longer functions. --- .../0001-limit-speed-of-sdmmc1-to-48Mhz.patch | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 linux/0001-limit-speed-of-sdmmc1-to-48Mhz.patch diff --git a/linux/0001-limit-speed-of-sdmmc1-to-48Mhz.patch b/linux/0001-limit-speed-of-sdmmc1-to-48Mhz.patch new file mode 100644 index 0000000..68499c4 --- /dev/null +++ b/linux/0001-limit-speed-of-sdmmc1-to-48Mhz.patch @@ -0,0 +1,35 @@ +From f87ac6fa4a14fb9202232ee1c26ae797d1304f91 Mon Sep 17 00:00:00 2001 +From: Justin Schneck +Date: Sat, 20 Feb 2021 17:41:28 -0500 +Subject: [PATCH] limit speed of sdmmc1 to 48Mhz + +The WILC3000 seems to struggle with transferring large +files when operating at max speed at 50Mhz. The driver +would stop working and result in an error message + +cfg_indicate_rx: Receive unknown message + +After this message the wifi no longer functions. +--- + arch/arm/boot/dts/at91-sama5d27_wlsom1_ek.dts | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/at91-sama5d27_wlsom1_ek.dts b/arch/arm/boot/dts/at91-sama5d27_wlsom1_ek.dts +index 56645bf6f..ae6328a3e 100644 +--- a/arch/arm/boot/dts/at91-sama5d27_wlsom1_ek.dts ++++ b/arch/arm/boot/dts/at91-sama5d27_wlsom1_ek.dts +@@ -216,6 +216,11 @@ + status = "okay"; + }; + ++&sdmmc1 { ++ max-frequency=<4800000>; ++ status = "okay"; ++}; ++ + &shutdown_controller { + atmel,shdwc-debouncer = <976>; + atmel,wakeup-rtc-timer; +-- +2.25.1 + From 660653c30d3e89e520941649ffb24f0760567199 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Sat, 20 Feb 2021 20:44:54 -0500 Subject: [PATCH 05/12] Add note about configuring the eth0 mac address --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index af5ddea..8abcc35 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,17 @@ by loading the kernel module. iex> System.cmd("modprobe", ["wilc-sdio"]) ``` +# Ethernet + +A unique hardware address for the eth0 interface is programmed into the QSPI +flash memory from the factory and is read set by U-Boot. Inorder to read from +the flash memory, you will need to ensure the QSPI flash is being powered by +removing the shunt from jumper J8 labeled "Disable Boot". + +The hardware address of the interface can also be forced by setting the U-Boot +environment variable `ethaddr`. If neither of these locations are accessible, +Linux will assign a random hardware address to the interface on every boot. + # Supported USB WiFi Devices The base image includes drivers and firmware for Ralink RT53xx (rt2800usb From d45ed19b2cfd051361b7cfc63708bc079e10dbc9 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Sat, 20 Feb 2021 21:09:27 -0500 Subject: [PATCH 06/12] at91bootstrap: bump to v3.10.2 --- fwup.conf | 2 +- nerves_defconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fwup.conf b/fwup.conf index 66d0f0b..68b2783 100644 --- a/fwup.conf +++ b/fwup.conf @@ -100,7 +100,7 @@ file-resource rootfs.img { } mbr mbr { - bootstrap-code-host-path = "${NERVES_SYSTEM}/images/sama5d27_wlsom1_ek-sdcardboot-uboot-3.9.3.bin" + bootstrap-code-host-path = "${NERVES_SYSTEM}/images/sama5d27_wlsom1_ek-sdcardboot-uboot-3.10.2.bin" partition 0 { block-offset = ${BOOT_PART_OFFSET} block-count = ${BOOT_PART_COUNT} diff --git a/nerves_defconfig b/nerves_defconfig index a16d765..0f371a0 100644 --- a/nerves_defconfig +++ b/nerves_defconfig @@ -44,7 +44,7 @@ BR2_PACKAGE_WPA_SUPPLICANT_DEBUG_SYSLOG=y BR2_TARGET_AT91BOOTSTRAP3=y BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_GIT=y BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_REPO_URL="https://github.com/linux4sam/at91bootstrap.git" -BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_REPO_VERSION="v3.9.3" +BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_REPO_VERSION="v3.10.2" BR2_TARGET_AT91BOOTSTRAP3_DEFCONFIG="sama5d27_wlsom1_eksd_uboot" BR2_TARGET_UBOOT=y BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y From 6d08cd989f6351626fa7bbd90b2c8eee9672e5b3 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Wed, 23 Jun 2021 13:34:42 -0400 Subject: [PATCH 07/12] nerves_system_br: bump to v1.16.0 --- CHANGELOG.md | 15 +++++++++++++++ VERSION | 2 +- linux/linux-5.4.defconfig | 29 +++++++++++++++-------------- mix.exs | 2 +- mix.lock | 2 +- nerves_defconfig | 2 +- rootfs_overlay/etc/erlinit.config | 7 +++++++ 7 files changed, 41 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4576b90..b3fcc13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## v0.2.0 + +The device tree and linux kernel are now loaded from the squashfs root file +system. + +* Bug fixes + * `wlan0` could encouter an error indicated by the error `cfg_indicate_rx: Receive unknown message` + when transferring firmware or large file streams. The SDIO interface speed was reduced from + 50Mhz to 48Mhz. + +* Dependencies + * [nerves_system_br: v1.14.0](https://github.com/nerves-project/nerves_system_br/releases/tag/v1.14.4) + * [Erlang/OTP 23.2.4](https://erlang.org/download/OTP-23.2.4.README) + + ## v0.1.0 Initial release diff --git a/VERSION b/VERSION index 6e8bf73..0ea3a94 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0 +0.2.0 diff --git a/linux/linux-5.4.defconfig b/linux/linux-5.4.defconfig index e373607..d096f35 100644 --- a/linux/linux-5.4.defconfig +++ b/linux/linux-5.4.defconfig @@ -5,6 +5,7 @@ CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_CGROUPS=y +CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y CONFIG_EMBEDDED=y CONFIG_SLAB=y @@ -42,15 +43,6 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_IP_PNP_RARP=y # CONFIG_INET_DIAG is not set CONFIG_IPV6_SIT_6RD=y -CONFIG_NETFILTER=y -CONFIG_BRIDGE=m -CONFIG_NET_SCHED=y -CONFIG_NET_EMATCH=y -CONFIG_NET_CLS_ACT=y -CONFIG_DCB=y -CONFIG_NET_L3_MASTER_DEV=y -CONFIG_CGROUP_NET_PRIO=y -CONFIG_BPF_JIT=y CONFIG_CAN=y CONFIG_CAN_AT91=y CONFIG_CAN_M_CAN=y @@ -88,12 +80,13 @@ CONFIG_MACB=y # CONFIG_NET_VENDOR_INTEL is not set # CONFIG_NET_VENDOR_MARVELL is not set # CONFIG_NET_VENDOR_MICREL is not set -# CONFIG_NET_VENDOR_MICROCHIP is not set +CONFIG_ENC28J60=m # CONFIG_NET_VENDOR_NATSEMI is not set # CONFIG_NET_VENDOR_SEEQ is not set # CONFIG_NET_VENDOR_SMSC is not set # CONFIG_NET_VENDOR_STMICRO is not set -# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5100_SPI=m CONFIG_MICREL_PHY=y CONFIG_LIBERTAS_THINFIRM=m CONFIG_LIBERTAS_THINFIRM_USB=m @@ -127,6 +120,7 @@ CONFIG_I2C_GPIO=y CONFIG_SPI=y CONFIG_SPI_ATMEL=y CONFIG_SPI_GPIO=y +CONFIG_SPI_SPIDEV=y CONFIG_GPIO_SYSFS=y CONFIG_POWER_RESET=y CONFIG_POWER_SUPPLY=y @@ -166,6 +160,7 @@ CONFIG_SND_ATMEL_SOC_I2S=y # CONFIG_HID_GENERIC is not set CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_OTG=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y CONFIG_USB_ACM=y @@ -176,7 +171,7 @@ CONFIG_USB_SERIAL_FTDI_SIO=y CONFIG_USB_SERIAL_PL2303=y CONFIG_USB_GADGET=y CONFIG_USB_ATMEL_USBA=y -CONFIG_USB_G_SERIAL=y +CONFIG_USB_ETH=y CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y @@ -195,7 +190,10 @@ CONFIG_DMADEVICES=y CONFIG_AT_HDMAC=y CONFIG_AT_XDMAC=y CONFIG_STAGING=y -CONFIG_WILC_SDIO=m +CONFIG_FB_TFT=m +CONFIG_FB_TFT_HX8357D=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_WILC_SPI=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_IIO=y CONFIG_AT91_ADC=y @@ -210,12 +208,12 @@ CONFIG_PWM_ATMEL_TCB=y CONFIG_EXT4_FS=y CONFIG_F2FS_FS=y CONFIG_FANOTIFY=y +CONFIG_AUTOFS_FS=m CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_UBIFS_FS=y CONFIG_UBIFS_FS_ADVANCED_COMPR=y CONFIG_SQUASHFS=y -CONFIG_SQUASHFS_FILE_DIRECT=y CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y @@ -227,6 +225,9 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_DEV_ATMEL_AES=y CONFIG_CRYPTO_DEV_ATMEL_TDES=y CONFIG_CRYPTO_DEV_ATMEL_SHA=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=3 +CONFIG_CONSOLE_LOGLEVEL_QUIET=3 +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=3 CONFIG_STRIP_ASM_SYMS=y CONFIG_DEBUG_FS=y CONFIG_DEBUG_MEMORY_INIT=y diff --git a/mix.exs b/mix.exs index 4b0ae5a..90ee9b0 100644 --- a/mix.exs +++ b/mix.exs @@ -50,7 +50,7 @@ defmodule NervesSystemSAMA5D27WLSOM1EK.MixProject do defp deps do [ {:nerves, "~> 1.5.4 or ~> 1.6.0 or ~> 1.7.0", runtime: false}, - {:nerves_system_br, "1.14.4", runtime: false}, + {:nerves_system_br, "1.16.0", runtime: false}, {:nerves_toolchain_armv7_nerves_linux_gnueabihf, "~> 1.4.0", runtime: false}, {:nerves_system_linter, "~> 0.4", runtime: false}, {:ex_doc, "~> 0.18", only: [:dev, :test], runtime: false} diff --git a/mix.lock b/mix.lock index 31d964f..a69dee7 100644 --- a/mix.lock +++ b/mix.lock @@ -7,7 +7,7 @@ "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, "nerves": {:hex, :nerves, "1.7.4", "6dc9c9c79baf9af540e34638e2fd964700f68f8dcece7cad26048c3a89ef6788", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "ac99ab1aa27ace81fd6d49e311697e8027b5c34b00728523e0721a8e2f4414eb"}, - "nerves_system_br": {:hex, :nerves_system_br, "1.14.4", "cfab33432035f4ad92202a53d9067a02034d02144f7b33419e2636ef8feb4c3a", [:mix], [], "hexpm", "c04df862d040b9233a4c45e9f58633c70b70a43baf9c68eb983e41819eb95911"}, + "nerves_system_br": {:hex, :nerves_system_br, "1.16.0", "0d807de02b1e2bef1bdbdd04517fb22adcdc6e2ba84ff2e1569ceaf1478d3b96", [:mix], [], "hexpm", "23183ad49b463bec6901567b59c79c3f1dbe7962ef85530dddf3633f3f6f3522"}, "nerves_system_linter": {:hex, :nerves_system_linter, "0.4.0", "81e9a6f5018fe5fb67d7b43a04dca36156f62b55b5554eb2fa3964d3889d09cd", [:mix], [], "hexpm", "b5bd8480ce7a6317f4601ff41fd2f594bdf76aff0bdf6dcfac571c3fa1ec5f82"}, "nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "1.3.2", "c1adf1c067f46810a678946506f379b8d1694b154fcbc77b1de5fda97494fbfc", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.7.2", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "0200515efb0ddd6a2318234c7084f7cdb8691fbb83c47def527558b4921f3a3a"}, "nerves_toolchain_armv7_nerves_linux_gnueabihf": {:hex, :nerves_toolchain_armv7_nerves_linux_gnueabihf, "1.4.1", "bd3e3ae08727294c01456f0c6fa55ee11c67ed001bda71e817d3e5f960efd074", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.8.1", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "fdf4cd7b30d7d4aa49ee997c9bfc5fc4a34951850d18be41d854bf3889a0c749"}, diff --git a/nerves_defconfig b/nerves_defconfig index 0f371a0..13d4c78 100644 --- a/nerves_defconfig +++ b/nerves_defconfig @@ -37,9 +37,9 @@ BR2_PACKAGE_LIBMNL=y BR2_PACKAGE_WIRELESS_REGDB=y BR2_PACKAGE_WPA_SUPPLICANT=y BR2_PACKAGE_WPA_SUPPLICANT_AP_SUPPORT=y -BR2_PACKAGE_WPA_SUPPLICANT_EAP=y BR2_PACKAGE_WPA_SUPPLICANT_HOTSPOT=y BR2_PACKAGE_WPA_SUPPLICANT_DEBUG_SYSLOG=y +BR2_PACKAGE_WPA_SUPPLICANT_CTRL_IFACE=y # BR2_TARGET_ROOTFS_TAR is not set BR2_TARGET_AT91BOOTSTRAP3=y BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_GIT=y diff --git a/rootfs_overlay/etc/erlinit.config b/rootfs_overlay/etc/erlinit.config index 26f92dd..a4e9c11 100644 --- a/rootfs_overlay/etc/erlinit.config +++ b/rootfs_overlay/etc/erlinit.config @@ -14,6 +14,13 @@ # Use nbtty to improve terminal handling on serial ports. -s "/usr/bin/nbtty" +# There's a call to getrandom(2) when loading the crypto NIF that's before +# nerves_runtime can start rngd. This syscall can block the BEAM indefinitely +# if there's not enough entropy in the kernel. We have not observed blocking on +# this platform. However, we don't know that getrandom(2) will always have +# enough entropy, so start rngd here to be safe. +--pre-run-exec /usr/sbin/rngd + # Specify the user and group IDs for the Erlang VM #--uid 100 #--gid 200 From d2fb924c7784bb992fa59e4244de769a4b9b7b22 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Wed, 4 Aug 2021 17:27:31 -0400 Subject: [PATCH 08/12] nerves_system_br: bump to v1.16.3 --- .circleci/config.yml | 6 +++--- mix.exs | 3 +-- mix.lock | 9 +++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d63027d..3fac72b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ exec: &exec name: build-tools/nerves-system-br - version: 1.14.4 - elixir: 1.11.3-otp-23 + version: 1.16.3 + elixir: 1.12.2-otp-24 version: 2.1 orbs: - build-tools: nerves-project/build-tools@0.1.2 + build-tools: nerves-project/build-tools@0.1.4 workflows: version: 2 diff --git a/mix.exs b/mix.exs index 90ee9b0..c0a353f 100644 --- a/mix.exs +++ b/mix.exs @@ -50,8 +50,7 @@ defmodule NervesSystemSAMA5D27WLSOM1EK.MixProject do defp deps do [ {:nerves, "~> 1.5.4 or ~> 1.6.0 or ~> 1.7.0", runtime: false}, - {:nerves_system_br, "1.16.0", runtime: false}, - {:nerves_toolchain_armv7_nerves_linux_gnueabihf, "~> 1.4.0", runtime: false}, + {:nerves_system_br, "1.16.3", runtime: false}, {:nerves_system_linter, "~> 0.4", runtime: false}, {:ex_doc, "~> 0.18", only: [:dev, :test], runtime: false} ] diff --git a/mix.lock b/mix.lock index a69dee7..27369a8 100644 --- a/mix.lock +++ b/mix.lock @@ -1,13 +1,14 @@ %{ "distillery": {:hex, :distillery, "1.4.1", "546d851bf27ae8fe0727e10e4fc4e146ad836eecee138263a60431e688044ed3", [:mix], [], "hexpm"}, "earmark": {:hex, :earmark, "1.4.10", "bddce5e8ea37712a5bfb01541be8ba57d3b171d3fa4f80a0be9bcf1db417bcaf", [:mix], [{:earmark_parser, ">= 1.4.10", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "12dbfa80810478e521d3ffb941ad9fbfcbbd7debe94e1341b4c4a1b2411c1c27"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"}, - "ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"}, + "ex_doc": {:hex, :ex_doc, "0.25.1", "4b736fa38dc76488a937e5ef2944f5474f3eff921de771b25371345a8dc810bc", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3200b0a69ddb2028365281fbef3753ea9e728683863d8cdaa96580925c891f67"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, - "nerves": {:hex, :nerves, "1.7.4", "6dc9c9c79baf9af540e34638e2fd964700f68f8dcece7cad26048c3a89ef6788", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "ac99ab1aa27ace81fd6d49e311697e8027b5c34b00728523e0721a8e2f4414eb"}, - "nerves_system_br": {:hex, :nerves_system_br, "1.16.0", "0d807de02b1e2bef1bdbdd04517fb22adcdc6e2ba84ff2e1569ceaf1478d3b96", [:mix], [], "hexpm", "23183ad49b463bec6901567b59c79c3f1dbe7962ef85530dddf3633f3f6f3522"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "nerves": {:hex, :nerves, "1.7.10", "6a28a69bd671c708615e0b3caded2efaf7159679e3568d0a4eb3acb73ab2c6aa", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2137d3eaaf8ce0db9189eec26e7ee8d24183a680bc270c812d14d4e8c1a305a7"}, + "nerves_system_br": {:hex, :nerves_system_br, "1.16.3", "d0a901a313e642d4024aa77f173c1e115eca82813dd6e592037ee5eadf5f58aa", [:mix], [], "hexpm", "20968db0d4e311159f387c0909d412b8ae715d669111954c1b448eec10ff64b9"}, "nerves_system_linter": {:hex, :nerves_system_linter, "0.4.0", "81e9a6f5018fe5fb67d7b43a04dca36156f62b55b5554eb2fa3964d3889d09cd", [:mix], [], "hexpm", "b5bd8480ce7a6317f4601ff41fd2f594bdf76aff0bdf6dcfac571c3fa1ec5f82"}, "nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "1.3.2", "c1adf1c067f46810a678946506f379b8d1694b154fcbc77b1de5fda97494fbfc", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.7.2", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "0200515efb0ddd6a2318234c7084f7cdb8691fbb83c47def527558b4921f3a3a"}, "nerves_toolchain_armv7_nerves_linux_gnueabihf": {:hex, :nerves_toolchain_armv7_nerves_linux_gnueabihf, "1.4.1", "bd3e3ae08727294c01456f0c6fa55ee11c67ed001bda71e817d3e5f960efd074", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.8.1", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "fdf4cd7b30d7d4aa49ee997c9bfc5fc4a34951850d18be41d854bf3889a0c749"}, From 042b428393bf0a4ab39e8d15da56f14136c9d15f Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Wed, 4 Aug 2021 17:28:27 -0400 Subject: [PATCH 09/12] nerves-toolchain: bump to v1.4.3 This also bumps AT91Bootstrap3 to 4.0.0 for GCC 10 support. --- mix.exs | 1 + mix.lock | 4 +-- nerves_defconfig | 8 ++--- .../0001-Symlink-the-binaries-directory.patch | 34 +++++++++++++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 patches/at91bootstrap3/0001-Symlink-the-binaries-directory.patch diff --git a/mix.exs b/mix.exs index c0a353f..6d26f7b 100644 --- a/mix.exs +++ b/mix.exs @@ -51,6 +51,7 @@ defmodule NervesSystemSAMA5D27WLSOM1EK.MixProject do [ {:nerves, "~> 1.5.4 or ~> 1.6.0 or ~> 1.7.0", runtime: false}, {:nerves_system_br, "1.16.3", runtime: false}, + {:nerves_toolchain_armv7_nerves_linux_gnueabihf, "1.4.3", runtime: false}, {:nerves_system_linter, "~> 0.4", runtime: false}, {:ex_doc, "~> 0.18", only: [:dev, :test], runtime: false} ] diff --git a/mix.lock b/mix.lock index 27369a8..e11cfbf 100644 --- a/mix.lock +++ b/mix.lock @@ -11,7 +11,7 @@ "nerves_system_br": {:hex, :nerves_system_br, "1.16.3", "d0a901a313e642d4024aa77f173c1e115eca82813dd6e592037ee5eadf5f58aa", [:mix], [], "hexpm", "20968db0d4e311159f387c0909d412b8ae715d669111954c1b448eec10ff64b9"}, "nerves_system_linter": {:hex, :nerves_system_linter, "0.4.0", "81e9a6f5018fe5fb67d7b43a04dca36156f62b55b5554eb2fa3964d3889d09cd", [:mix], [], "hexpm", "b5bd8480ce7a6317f4601ff41fd2f594bdf76aff0bdf6dcfac571c3fa1ec5f82"}, "nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "1.3.2", "c1adf1c067f46810a678946506f379b8d1694b154fcbc77b1de5fda97494fbfc", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.7.2", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "0200515efb0ddd6a2318234c7084f7cdb8691fbb83c47def527558b4921f3a3a"}, - "nerves_toolchain_armv7_nerves_linux_gnueabihf": {:hex, :nerves_toolchain_armv7_nerves_linux_gnueabihf, "1.4.1", "bd3e3ae08727294c01456f0c6fa55ee11c67ed001bda71e817d3e5f960efd074", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.8.1", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "fdf4cd7b30d7d4aa49ee997c9bfc5fc4a34951850d18be41d854bf3889a0c749"}, - "nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.8.2", "66163f4fb90aa40cabae5700e516fdf40708d260334c4674514413d7cb585dbb", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}], "hexpm", "9734237608269d2d649cc67ebb7bcb605bb122259a7cea17dadcf8204f467638"}, + "nerves_toolchain_armv7_nerves_linux_gnueabihf": {:hex, :nerves_toolchain_armv7_nerves_linux_gnueabihf, "1.4.3", "ff5b8fed2a71daea7ac07a5a0a6ecec7a4985d2a617a89ab073591d441d9cda4", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.8.4", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "ffefca61b7282a5a10032e61b6dab6758d24eee732c5d8c16fe6aada52dd099a"}, + "nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.8.4", "2f6b4153e3904502d117f9d957c12eaafd490e1d2bdf20a85328ada46a1350da", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}], "hexpm", "6194be9b1364fdc1db6b2a0e98fa8dcb94fe1af373dcf8149298d62ce9b1b369"}, "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, } diff --git a/nerves_defconfig b/nerves_defconfig index 13d4c78..5c2a5dc 100644 --- a/nerves_defconfig +++ b/nerves_defconfig @@ -3,13 +3,13 @@ BR2_cortex_a5=y BR2_ARM_ENABLE_NEON=y BR2_ARM_ENABLE_VFP=y BR2_ARM_INSTRUCTIONS_THUMB2=y -BR2_GLOBAL_PATCH_DIR="${BR2_EXTERNAL_NERVES_PATH}/patches" +BR2_GLOBAL_PATCH_DIR="${BR2_EXTERNAL_NERVES_PATH}/patches ${NERVES_DEFCONFIG_DIR}/patches" BR2_TOOLCHAIN_EXTERNAL=y BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y -BR2_TOOLCHAIN_EXTERNAL_URL="https://github.com/nerves-project/toolchains/releases/download/v1.4.1/nerves_toolchain_armv7_nerves_linux_gnueabihf-linux_x86_64-1.4.1-4C8725A.tar.xz" +BR2_TOOLCHAIN_EXTERNAL_URL="https://github.com/nerves-project/toolchains/releases/download/v1.4.3/nerves_toolchain_armv7_nerves_linux_gnueabihf-linux_x86_64-1.4.3-C532A43.tar.xz" BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="armv7-nerves-linux-gnueabihf" -BR2_TOOLCHAIN_EXTERNAL_HEADERS_4_4=y +BR2_TOOLCHAIN_EXTERNAL_HEADERS_4_19=y BR2_TOOLCHAIN_EXTERNAL_CUSTOM_GLIBC=y # BR2_TOOLCHAIN_EXTERNAL_INET_RPC is not set BR2_TOOLCHAIN_EXTERNAL_CXX=y @@ -44,7 +44,7 @@ BR2_PACKAGE_WPA_SUPPLICANT_CTRL_IFACE=y BR2_TARGET_AT91BOOTSTRAP3=y BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_GIT=y BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_REPO_URL="https://github.com/linux4sam/at91bootstrap.git" -BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_REPO_VERSION="v3.10.2" +BR2_TARGET_AT91BOOTSTRAP3_CUSTOM_REPO_VERSION="v4.0.0" BR2_TARGET_AT91BOOTSTRAP3_DEFCONFIG="sama5d27_wlsom1_eksd_uboot" BR2_TARGET_UBOOT=y BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y diff --git a/patches/at91bootstrap3/0001-Symlink-the-binaries-directory.patch b/patches/at91bootstrap3/0001-Symlink-the-binaries-directory.patch new file mode 100644 index 0000000..11f59d1 --- /dev/null +++ b/patches/at91bootstrap3/0001-Symlink-the-binaries-directory.patch @@ -0,0 +1,34 @@ +From cd545856aa6aab1cb6a5c3c9b71fd06d81ee472a Mon Sep 17 00:00:00 2001 +From: Justin Schneck +Date: Wed, 4 Aug 2021 17:12:38 -0400 +Subject: [PATCH] Symlink the binaries directory + +--- + .gitignore | 1 - + binaries | 1 + + 2 files changed, 1 insertion(+), 1 deletion(-) + create mode 120000 binaries + +diff --git a/.gitignore b/.gitignore +index e688963..a6c7a8d 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -9,7 +9,6 @@ config/at91bootstrap-config/ + build + debug + result +-binaries + *~ + *.orig + *.o +diff --git a/binaries b/binaries +new file mode 120000 +index 0000000..204c670 +--- /dev/null ++++ b/binaries +@@ -0,0 +1 @@ ++build/binaries +\ No newline at end of file +-- +2.25.1 + From c91cd7f829048cd15d23e460dfbd21a91da562b8 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Wed, 4 Aug 2021 17:29:05 -0400 Subject: [PATCH 10/12] uboot: bump to linux4sam-2021.04 --- fwup.conf | 2 +- nerves_defconfig | 2 +- ...uashfs-support-from-upstream-2021.01.patch | 3377 ----------------- 3 files changed, 2 insertions(+), 3379 deletions(-) delete mode 100644 uboot/0001-Add-squashfs-support-from-upstream-2021.01.patch diff --git a/fwup.conf b/fwup.conf index 68b2783..1cd7c9e 100644 --- a/fwup.conf +++ b/fwup.conf @@ -100,7 +100,7 @@ file-resource rootfs.img { } mbr mbr { - bootstrap-code-host-path = "${NERVES_SYSTEM}/images/sama5d27_wlsom1_ek-sdcardboot-uboot-3.10.2.bin" + bootstrap-code-host-path = "${NERVES_SYSTEM}/images/sama5d27_wlsom1_ek-sdcardboot-uboot-4.0.0.bin" partition 0 { block-offset = ${BOOT_PART_OFFSET} block-count = ${BOOT_PART_COUNT} diff --git a/nerves_defconfig b/nerves_defconfig index 5c2a5dc..a6c169a 100644 --- a/nerves_defconfig +++ b/nerves_defconfig @@ -50,7 +50,7 @@ BR2_TARGET_UBOOT=y BR2_TARGET_UBOOT_BUILD_SYSTEM_KCONFIG=y BR2_TARGET_UBOOT_CUSTOM_GIT=y BR2_TARGET_UBOOT_CUSTOM_REPO_URL="https://github.com/linux4sam/u-boot-at91.git" -BR2_TARGET_UBOOT_CUSTOM_REPO_VERSION="linux4sam-2020.10.1" +BR2_TARGET_UBOOT_CUSTOM_REPO_VERSION="linux4sam-2021.04" BR2_TARGET_UBOOT_PATCH="${NERVES_DEFCONFIG_DIR}/uboot" BR2_TARGET_UBOOT_USE_CUSTOM_CONFIG=y BR2_TARGET_UBOOT_CUSTOM_CONFIG_FILE="${NERVES_DEFCONFIG_DIR}/uboot/uboot.defconfig" diff --git a/uboot/0001-Add-squashfs-support-from-upstream-2021.01.patch b/uboot/0001-Add-squashfs-support-from-upstream-2021.01.patch deleted file mode 100644 index 09395fe..0000000 --- a/uboot/0001-Add-squashfs-support-from-upstream-2021.01.patch +++ /dev/null @@ -1,3377 +0,0 @@ -From 13c0c426e1b6333a4c3917979e7601aa5ea07649 Mon Sep 17 00:00:00 2001 -From: Justin Schneck -Date: Sat, 20 Feb 2021 17:37:28 -0500 -Subject: [PATCH] Add squashfs support from upstream 2021.01 - ---- - cmd/Kconfig | 6 + - cmd/Makefile | 1 + - cmd/sqfs.c | 42 + - common/spl/Kconfig | 9 + - fs/Kconfig | 2 + - fs/Makefile | 1 + - fs/fs.c | 22 + - fs/squashfs/Kconfig | 11 + - fs/squashfs/Makefile | 7 + - fs/squashfs/sqfs.c | 1708 +++++++++++++++++ - fs/squashfs/sqfs_decompressor.c | 155 ++ - fs/squashfs/sqfs_decompressor.h | 61 + - fs/squashfs/sqfs_dir.c | 93 + - fs/squashfs/sqfs_filesystem.h | 310 +++ - fs/squashfs/sqfs_inode.c | 161 ++ - fs/squashfs/sqfs_utils.h | 49 + - include/fs.h | 1 + - include/hang.h | 22 + - include/squashfs.h | 26 + - include/u-boot/zlib.h | 32 + - lib/Kconfig | 7 + - lib/zlib/inftrees.c | 19 +- - lib/zlib/trees.c | 31 +- - lib/zlib/uncompr.c | 97 + - lib/zlib/zlib.c | 3 + - lib/zlib/zutil.c | 3 +- - .../test_fs/test_squashfs/sqfs_common.py | 76 + - .../test_fs/test_squashfs/test_sqfs_load.py | 46 + - .../test_fs/test_squashfs/test_sqfs_ls.py | 36 + - 29 files changed, 3010 insertions(+), 27 deletions(-) - 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/hang.h - create mode 100644 include/squashfs.h - create mode 100644 lib/zlib/uncompr.c - create mode 100644 test/py/tests/test_fs/test_squashfs/sqfs_common.py - create mode 100644 test/py/tests/test_fs/test_squashfs/test_sqfs_load.py - create mode 100644 test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py - -diff --git a/cmd/Kconfig b/cmd/Kconfig -index 1e4cf146..5e4d9400 100644 ---- a/cmd/Kconfig -+++ b/cmd/Kconfig -@@ -1957,6 +1957,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 3ac71045..490c5abd 100644 ---- a/cmd/Makefile -+++ b/cmd/Makefile -@@ -60,6 +60,7 @@ obj-$(CONFIG_CMD_EXT2) += ext2.o - obj-$(CONFIG_CMD_FAT) += fat.o - obj-$(CONFIG_CMD_FDC) += fdc.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 00000000..107038c4 ---- /dev/null -+++ b/cmd/sqfs.c -@@ -0,0 +1,42 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2020 Bootlin -+ * -+ * Author: Joao Marcos Costa -+ * -+ * squashfs.c: implements SquashFS related commands -+ */ -+ -+#include -+#include -+#include -+ -+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); -+} -+ -+U_BOOT_CMD(sqfsls, 4, 1, do_sqfs_ls, -+ "List files in directory. Default: root (/).", -+ " [] [directory]\n" -+ " - list files from 'dev' on 'interface' in 'directory'\n" -+); -+ -+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); -+} -+ -+U_BOOT_CMD(sqfsload, 7, 0, do_sqfs_load, -+ "load binary file from a SquashFS filesystem", -+ " [ [ [ [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." -+); -diff --git a/common/spl/Kconfig b/common/spl/Kconfig -index a7241271..3765f387 100644 ---- a/common/spl/Kconfig -+++ b/common/spl/Kconfig -@@ -550,6 +550,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 1cb9831b..620af7f0 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 42e669c4..6980894d 100644 ---- a/fs/Makefile -+++ b/fs/Makefile -@@ -23,5 +23,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 0c66d604..f7982710 100644 ---- a/fs/fs.c -+++ b/fs/fs.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - - DECLARE_GLOBAL_DATA_PTR; - -@@ -273,6 +274,27 @@ static struct fstype_info fstypes[] = { - .mkdir = fs_mkdir_unsupported, - .ln = fs_ln_unsupported, - }, -+#endif -+#if IS_ENABLED(CONFIG_FS_SQUASHFS) -+ { -+ .fstype = FS_TYPE_SQUASHFS, -+ .name = "squashfs", -+ .null_dev_desc_ok = false, -+ .probe = sqfs_probe, -+ .opendir = sqfs_opendir, -+ .readdir = sqfs_readdir, -+ .ls = fs_ls_generic, -+ .read = sqfs_read, -+ .size = sqfs_size, -+ .close = sqfs_close, -+ .closedir = sqfs_closedir, -+ .exists = sqfs_exists, -+ .uuid = fs_uuid_unsupported, -+ .write = fs_write_unsupported, -+ .ln = fs_ln_unsupported, -+ .unlink = fs_unlink_unsupported, -+ .mkdir = fs_mkdir_unsupported, -+ }, - #endif - { - .fstype = FS_TYPE_ANY, -diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig -new file mode 100644 -index 00000000..54ab1618 ---- /dev/null -+++ b/fs/squashfs/Kconfig -@@ -0,0 +1,11 @@ -+config FS_SQUASHFS -+ bool "Enable SquashFS filesystem support" -+ select ZLIB_UNCOMPRESS -+ 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 00000000..ba66ee82 ---- /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 00000000..5de69ac3 ---- /dev/null -+++ b/fs/squashfs/sqfs.c -@@ -0,0 +1,1708 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2020 Bootlin -+ * -+ * Author: Joao Marcos Costa -+ * -+ * sqfs.c: SquashFS filesystem implementation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "sqfs_decompressor.h" -+#include "sqfs_filesystem.h" -+#include "sqfs_utils.h" -+ -+static struct squashfs_ctxt ctxt; -+ -+static int sqfs_disk_read(__u32 block, __u32 nr_blocks, void *buf) -+{ -+ ulong ret; -+ -+ if (!ctxt.cur_dev) -+ return -1; -+ -+ ret = blk_dread(ctxt.cur_dev, ctxt.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(ctxt.cur_dev->blksz); -+ if (!*sblk) -+ return -ENOMEM; -+ -+ if (sqfs_disk_read(0, 1, *sblk) != 1) { -+ free(*sblk); -+ sblk = NULL; -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int sqfs_count_tokens(const char *filename) -+{ -+ int token_count = 1, l; -+ -+ for (l = 1; l < strlen(filename); l++) { -+ if (filename[l] == '/') -+ token_count++; -+ } -+ -+ /* 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) / ctxt.cur_dev->blksz; -+ *offset = le64_to_cpu(start) - (start_ * ctxt.cur_dev->blksz); -+ -+ return DIV_ROUND_UP(table_size + *offset, ctxt.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 = ctxt.sblk; -+ unsigned long dest_len; -+ int block, offset, ret; -+ u16 header; -+ -+ metadata_buffer = NULL; -+ entries = NULL; -+ table = NULL; -+ -+ if (inode_fragment_index >= get_unaligned_le32(&sblk->fragments)) -+ return -EINVAL; -+ -+ start = get_unaligned_le64(&sblk->fragment_table_start) / -+ ctxt.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 * ctxt.cur_dev->blksz); -+ if (!table) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ if (sqfs_disk_read(start, n_blks, table) < 0) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ 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_le64(table + table_offset + block * -+ sizeof(u64)); -+ -+ start = start_block / ctxt.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 * ctxt.cur_dev->blksz); -+ if (!metadata_buffer) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ if (sqfs_disk_read(start, n_blks, metadata_buffer) < 0) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ /* Every metadata block starts with a 16-bit header */ -+ header = get_unaligned_le16(metadata_buffer + table_offset); -+ metadata = metadata_buffer + table_offset + SQFS_HEADER_SIZE; -+ -+ if (!metadata || !header) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ entries = malloc(SQFS_METADATA_BLOCK_SIZE); -+ if (!entries) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ if (SQFS_COMPRESSED_METADATA(header)) { -+ src_len = SQFS_METADATA_SIZE(header); -+ dest_len = SQFS_METADATA_BLOCK_SIZE; -+ ret = sqfs_decompress(&ctxt, entries, &dest_len, metadata, -+ src_len); -+ if (ret) { -+ ret = -EINVAL; -+ goto out; -+ } -+ } else { -+ memcpy(entries, metadata, SQFS_METADATA_SIZE(header)); -+ } -+ -+ *e = entries[offset]; -+ ret = SQFS_COMPRESSED_BLOCK(e->size); -+ -+out: -+ free(entries); -+ free(metadata_buffer); -+ free(table); -+ -+ 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; -+ u16 sz; -+ -+ tmp = src; -+ sz = get_unaligned_le16(src + sizeof(*tmp) - sizeof(u16)); -+ /* -+ * 'src' points to the begin of a directory entry, and 'sz' gets its -+ * 'name_size' member's value. name_size is actually the string -+ * length - 1, so adding 2 compensates this difference and adds space -+ * for the trailling null byte. -+ */ -+ *dest = malloc(sizeof(*tmp) + sz + 2); -+ if (!*dest) -+ return -ENOMEM; -+ -+ memcpy(*dest, src, sizeof(*tmp) + sz + 1); -+ (*dest)->name[sz + 1] = '\0'; -+ -+ return 0; -+} -+ -+static int sqfs_get_tokens_length(char **tokens, int count) -+{ -+ int length = 0, i; -+ -+ /* -+ * 1 is added to the result of strlen to consider the slash separator -+ * between the tokens. -+ */ -+ for (i = 0; i < count; i++) -+ length += strlen(tokens[i]) + 1; -+ -+ return length; -+} -+ -+/* 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; -+ -+ length = sqfs_get_tokens_length(token_list, token_count); -+ -+ result = malloc(length + 1); -+ if (!result) -+ return NULL; -+ -+ 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; -+} -+ -+/* -+ * Differently from sqfs_concat_tokens, sqfs_join writes the result into a -+ * previously allocated string, and returns the number of bytes written. -+ */ -+static int sqfs_join(char **strings, char *dest, int start, int end, -+ char separator) -+{ -+ int i, offset = 0; -+ -+ for (i = start; i < end; i++) { -+ strcpy(dest + offset, strings[i]); -+ offset += strlen(strings[i]); -+ if (i < end - 1) -+ dest[offset++] = separator; -+ } -+ -+ return offset; -+} -+ -+/* -+ * Fills the given token list using its size (count) and a source string (str) -+ */ -+static int sqfs_tokenize(char **tokens, int count, const char *str) -+{ -+ int i, j, ret = 0; -+ char *aux, *strc; -+ -+ strc = strdup(str); -+ if (!strc) -+ return -ENOMEM; -+ -+ if (!strcmp(strc, "/")) { -+ tokens[0] = strdup(strc); -+ if (!tokens[0]) { -+ ret = -ENOMEM; -+ goto free_strc; -+ } -+ } else { -+ for (j = 0; j < count; j++) { -+ aux = strtok(!j ? strc : NULL, "/"); -+ tokens[j] = strdup(aux); -+ if (!tokens[j]) { -+ for (i = 0; i < j; i++) -+ free(tokens[i]); -+ ret = -ENOMEM; -+ goto free_strc; -+ } -+ } -+ } -+ -+free_strc: -+ free(strc); -+ -+ return ret; -+} -+ -+/* -+ * Remove last 'updir + 1' tokens from the base path tokens list. This leaves us -+ * with a token list containing only the tokens needed to form the resolved -+ * path, and returns the decremented size of the token list. -+ */ -+static int sqfs_clean_base_path(char **base, int count, int updir) -+{ -+ int i; -+ -+ for (i = count - updir - 1; i < count; i++) -+ free(base[i]); -+ -+ return count - updir - 1; -+} -+ -+/* -+ * 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, *resolved = NULL; -+ int ret, bc, rc, i, updir = 0, resolved_size = 0, offset = 0; -+ -+ base_tokens = NULL; -+ rel_tokens = NULL; -+ -+ /* Memory allocation for the token lists */ -+ bc = sqfs_count_tokens(base); -+ rc = sqfs_count_tokens(rel); -+ if (bc < 1 || rc < 1) -+ return NULL; -+ -+ base_tokens = calloc(bc, sizeof(char *)); -+ if (!base_tokens) -+ return NULL; -+ -+ rel_tokens = calloc(rc, sizeof(char *)); -+ if (!rel_tokens) -+ goto out; -+ -+ /* Fill token lists */ -+ ret = sqfs_tokenize(base_tokens, bc, base); -+ if (ret) -+ goto out; -+ -+ ret = sqfs_tokenize(rel_tokens, rc, rel); -+ if (ret) -+ goto out; -+ -+ /* count '..' occurrences in target path */ -+ for (i = 0; i < rc; i++) { -+ if (!strcmp(rel_tokens[i], "..")) -+ updir++; -+ } -+ -+ /* Remove the last token and the '..' occurrences */ -+ bc = sqfs_clean_base_path(base_tokens, bc, updir); -+ if (bc < 0) -+ goto out; -+ -+ /* Calculate resolved path size */ -+ if (!bc) -+ resolved_size++; -+ -+ resolved_size += sqfs_get_tokens_length(base_tokens, bc) + -+ sqfs_get_tokens_length(rel_tokens, rc); -+ -+ resolved = malloc(resolved_size + 1); -+ if (!resolved) -+ goto out; -+ -+ /* Set resolved path */ -+ memset(resolved, '\0', resolved_size + 1); -+ offset += sqfs_join(base_tokens, resolved + offset, 0, bc, '/'); -+ resolved[offset++] = '/'; -+ offset += sqfs_join(rel_tokens, resolved + offset, updir, rc, '/'); -+ -+out: -+ if (rel_tokens) -+ for (i = 0; i < rc; i++) -+ free(rel_tokens[i]); -+ if (base_tokens) -+ for (i = 0; i < bc; i++) -+ free(base_tokens[i]); -+ -+ free(rel_tokens); -+ free(base_tokens); -+ -+ return resolved; -+} -+ -+static char *sqfs_resolve_symlink(struct squashfs_symlink_inode *sym, -+ const char *base_path) -+{ -+ char *resolved, *target; -+ u32 sz; -+ -+ sz = get_unaligned_le32(&sym->symlink_size); -+ target = malloc(sz + 1); -+ if (!target) -+ return NULL; -+ -+ /* -+ * There is no trailling null byte in the symlink's target path, so a -+ * copy is made and a '\0' is added at its end. -+ */ -+ target[sz] = '\0'; -+ /* Get target name (relative path) */ -+ strncpy(target, sym->symlink, sz); -+ -+ /* 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) -+{ -+ struct squashfs_super_block *sblk = ctxt.sblk; -+ char *path, *target, **sym_tokens, *res, *rem; -+ int j, ret = 0, new_inode_number, offset; -+ struct squashfs_symlink_inode *sym; -+ struct squashfs_ldir_inode *ldir; -+ struct squashfs_dir_inode *dir; -+ struct fs_dir_stream *dirsp; -+ struct fs_dirent *dent; -+ unsigned char *table; -+ -+ res = NULL; -+ rem = NULL; -+ path = NULL; -+ target = NULL; -+ sym_tokens = NULL; -+ -+ dirsp = (struct fs_dir_stream *)dirs; -+ -+ /* Start by root inode */ -+ table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes), -+ sblk->inodes, sblk->block_size); -+ -+ dir = (struct squashfs_dir_inode *)table; -+ ldir = (struct squashfs_ldir_inode *)table; -+ -+ /* 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) -+ return -ENOMEM; -+ -+ memcpy(dirs->dir_header, dirs->table, SQFS_DIR_HEADER_SIZE); -+ -+ /* Initialize squashfs_dir_stream members */ -+ dirs->table += SQFS_DIR_HEADER_SIZE; -+ dirs->size = get_unaligned_le16(&dir->file_size) - SQFS_DIR_HEADER_SIZE; -+ dirs->entry_count = dirs->dir_header->count + 1; -+ -+ /* No path given -> root directory */ -+ if (!strcmp(token_list[0], "/")) { -+ dirs->table = &dirs->dir_table[offset]; -+ memcpy(&dirs->i_dir, dir, sizeof(*dir)); -+ return 0; -+ } -+ -+ for (j = 0; j < token_count; j++) { -+ if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) { -+ printf("** Cannot find directory. **\n"); -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ while (!sqfs_readdir(dirsp, &dent)) { -+ ret = strcmp(dent->name, token_list[j]); -+ if (!ret) -+ break; -+ free(dirs->entry); -+ dirs->entry = NULL; -+ } -+ -+ if (ret) { -+ printf("** Cannot find directory. **\n"); -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ /* 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); -+ dir = (struct squashfs_dir_inode *)table; -+ -+ /* Check for symbolic link and inode type sanity */ -+ if (get_unaligned_le16(&dir->inode_type) == SQFS_SYMLINK_TYPE) { -+ sym = (struct squashfs_symlink_inode *)table; -+ /* Get first j + 1 tokens */ -+ path = sqfs_concat_tokens(token_list, j + 1); -+ if (!path) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ /* Resolve for these tokens */ -+ target = sqfs_resolve_symlink(sym, path); -+ if (!target) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ /* Join remaining tokens */ -+ rem = sqfs_concat_tokens(token_list + j + 1, token_count - -+ j - 1); -+ if (!rem) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ /* Concatenate remaining tokens and symlink's target */ -+ res = malloc(strlen(rem) + strlen(target) + 1); -+ if (!res) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ strcpy(res, target); -+ res[strlen(target)] = '/'; -+ strcpy(res + strlen(target) + 1, rem); -+ token_count = sqfs_count_tokens(res); -+ -+ if (token_count < 0) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ sym_tokens = malloc(token_count * sizeof(char *)); -+ if (!sym_tokens) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ /* Fill tokens list */ -+ ret = sqfs_tokenize(sym_tokens, token_count, res); -+ if (ret) { -+ ret = -EINVAL; -+ goto out; -+ } -+ free(dirs->entry); -+ dirs->entry = NULL; -+ -+ ret = sqfs_search_dir(dirs, sym_tokens, token_count, -+ m_list, m_count); -+ goto out; -+ } else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) { -+ printf("** Cannot find directory. **\n"); -+ free(dirs->entry); -+ dirs->entry = NULL; -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ /* Check if it is an extended dir. */ -+ if (get_unaligned_le16(&dir->inode_type) == SQFS_LDIR_TYPE) -+ ldir = (struct squashfs_ldir_inode *)table; -+ -+ /* 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); -+ dirs->entry = NULL; -+ ret = SQFS_EMPTY_DIR; -+ goto out; -+ } -+ -+ dirs->table += SQFS_DIR_HEADER_SIZE; -+ dirs->size = get_unaligned_le16(&dir->file_size); -+ dirs->entry_count = dirs->dir_header->count + 1; -+ dirs->size -= SQFS_DIR_HEADER_SIZE; -+ free(dirs->entry); -+ dirs->entry = NULL; -+ } -+ -+ offset = sqfs_dir_offset(table, m_list, m_count); -+ dirs->table = &dirs->dir_table[offset]; -+ -+ if (get_unaligned_le16(&dir->inode_type) == SQFS_DIR_TYPE) -+ memcpy(&dirs->i_dir, dir, sizeof(*dir)); -+ else -+ memcpy(&dirs->i_ldir, ldir, sizeof(*ldir)); -+ -+out: -+ free(res); -+ free(rem); -+ free(path); -+ free(target); -+ free(sym_tokens); -+ return ret; -+} -+ -+/* -+ * 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) -+{ -+ struct squashfs_super_block *sblk = ctxt.sblk; -+ u64 start, n_blks, table_offset, table_size; -+ int j, ret = 0, metablks_count; -+ unsigned char *src_table, *itb; -+ u32 src_len, dest_offset = 0; -+ unsigned long dest_len = 0; -+ bool compressed; -+ -+ table_size = get_unaligned_le64(&sblk->directory_table_start) - -+ get_unaligned_le64(&sblk->inode_table_start); -+ start = get_unaligned_le64(&sblk->inode_table_start) / -+ ctxt.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 * ctxt.cur_dev->blksz); -+ if (!itb) -+ return -ENOMEM; -+ -+ 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(&ctxt, *inode_table + -+ dest_offset, &dest_len, -+ src_table, src_len); -+ if (ret) { -+ free(*inode_table); -+ *inode_table = NULL; -+ goto free_itb; -+ } -+ -+ dest_offset += dest_len; -+ } 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. -+ */ -+ -+ table_offset += src_len + SQFS_HEADER_SIZE; -+ src_table += src_len + SQFS_HEADER_SIZE; -+ } -+ -+free_itb: -+ free(itb); -+ -+ return ret; -+} -+ -+static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list) -+{ -+ u64 start, n_blks, table_offset, table_size; -+ struct squashfs_super_block *sblk = ctxt.sblk; -+ int j, ret = 0, metablks_count = -1; -+ unsigned char *src_table, *dtb; -+ u32 src_len, dest_offset = 0; -+ unsigned long dest_len = 0; -+ bool compressed; -+ -+ *dir_table = NULL; -+ *pos_list = NULL; -+ /* DIRECTORY TABLE */ -+ table_size = get_unaligned_le64(&sblk->fragment_table_start) - -+ get_unaligned_le64(&sblk->directory_table_start); -+ start = get_unaligned_le64(&sblk->directory_table_start) / -+ ctxt.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 * ctxt.cur_dev->blksz); -+ if (!dtb) -+ return -ENOMEM; -+ -+ if (sqfs_disk_read(start, n_blks, dtb) < 0) -+ goto out; -+ -+ /* Parse directory table (metadata block) header */ -+ ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len); -+ if (ret) -+ goto out; -+ -+ /* Calculate total size to store the whole decompressed table */ -+ metablks_count = sqfs_count_metablks(dtb, table_offset, table_size); -+ if (metablks_count < 1) -+ goto out; -+ -+ *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE); -+ if (!*dir_table) -+ goto out; -+ -+ *pos_list = malloc(metablks_count * sizeof(u32)); -+ if (!*pos_list) -+ goto out; -+ -+ ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset, -+ metablks_count); -+ if (ret) { -+ metablks_count = -1; -+ goto out; -+ } -+ -+ 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(&ctxt, *dir_table + -+ (j * SQFS_METADATA_BLOCK_SIZE), -+ &dest_len, src_table, src_len); -+ if (ret) { -+ metablks_count = -1; -+ goto out; -+ } -+ -+ if (dest_len < SQFS_METADATA_BLOCK_SIZE) { -+ dest_offset += dest_len; -+ break; -+ } -+ -+ dest_offset += dest_len; -+ } 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. -+ */ -+ table_offset += src_len + SQFS_HEADER_SIZE; -+ src_table += src_len + SQFS_HEADER_SIZE; -+ } -+ -+out: -+ if (metablks_count < 1) { -+ free(*dir_table); -+ free(*pos_list); -+ *dir_table = NULL; -+ *pos_list = NULL; -+ } -+ free(dtb); -+ -+ return metablks_count; -+} -+ -+int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp) -+{ -+ unsigned char *inode_table = NULL, *dir_table = NULL; -+ int j, token_count = 0, ret = 0, metablks_count; -+ struct squashfs_dir_stream *dirs; -+ char **token_list = NULL, *path = NULL; -+ u32 *pos_list = NULL; -+ -+ dirs = malloc(sizeof(*dirs)); -+ if (!dirs) -+ return -EINVAL; -+ -+ /* these should be set to NULL to prevent dangling pointers */ -+ dirs->dir_header = NULL; -+ dirs->entry = NULL; -+ dirs->table = NULL; -+ dirs->inode_table = NULL; -+ dirs->dir_table = NULL; -+ -+ ret = sqfs_read_inode_table(&inode_table); -+ if (ret) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ metablks_count = sqfs_read_directory_table(&dir_table, &pos_list); -+ if (metablks_count < 1) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ /* Tokenize filename */ -+ token_count = sqfs_count_tokens(filename); -+ if (token_count < 0) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ path = strdup(filename); -+ if (!path) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ token_list = malloc(token_count * sizeof(char *)); -+ if (!token_list) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ /* Fill tokens list */ -+ ret = sqfs_tokenize(token_list, token_count, path); -+ if (ret) -+ goto out; -+ /* -+ * 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 out; -+ -+ 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; -+ -+ *dirsp = (struct fs_dir_stream *)dirs; -+ -+out: -+ for (j = 0; j < token_count; j++) -+ free(token_list[j]); -+ free(token_list); -+ free(pos_list); -+ free(path); -+ if (ret) { -+ free(inode_table); -+ free(dirs); -+ } -+ -+ return ret; -+} -+ -+int sqfs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) -+{ -+ struct squashfs_super_block *sblk = ctxt.sblk; -+ struct squashfs_dir_stream *dirs; -+ struct squashfs_lreg_inode *lreg; -+ struct squashfs_base_inode *base; -+ struct squashfs_reg_inode *reg; -+ int i_number, offset = 0, ret; -+ struct fs_dirent *dent; -+ unsigned char *ipos; -+ -+ dirs = (struct squashfs_dir_stream *)fs_dirs; -+ if (!dirs->size) { -+ *dentp = NULL; -+ return -SQFS_STOP_READDIR; -+ } -+ -+ dent = &dirs->dentp; -+ -+ if (!dirs->entry_count) { -+ if (dirs->size > SQFS_DIR_HEADER_SIZE) { -+ dirs->size -= SQFS_DIR_HEADER_SIZE; -+ } else { -+ *dentp = NULL; -+ 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; -+ } -+ -+ 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); -+ -+ base = (struct squashfs_base_inode *)ipos; -+ -+ /* Set entry type and size */ -+ switch (dirs->entry->type) { -+ case SQFS_DIR_TYPE: -+ case SQFS_LDIR_TYPE: -+ dent->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 (get_unaligned_le16(&base->inode_type) == SQFS_LREG_TYPE) { -+ lreg = (struct squashfs_lreg_inode *)ipos; -+ dent->size = get_unaligned_le64(&lreg->file_size); -+ } else { -+ reg = (struct squashfs_reg_inode *)ipos; -+ dent->size = get_unaligned_le32(®->file_size); -+ } -+ -+ dent->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: -+ dent->type = SQFS_MISC_ENTRY_TYPE; -+ break; -+ case SQFS_SYMLINK_TYPE: -+ case SQFS_LSYMLINK_TYPE: -+ dent->type = FS_DT_LNK; -+ break; -+ default: -+ return -SQFS_STOP_READDIR; -+ } -+ -+ /* Set entry name */ -+ strncpy(dent->name, dirs->entry->name, dirs->entry->name_size + 1); -+ dent->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; -+ -+ *dentp = dent; -+ -+ return 0; -+} -+ -+int sqfs_probe(struct blk_desc *fs_dev_desc, struct disk_partition *fs_partition) -+{ -+ struct squashfs_super_block *sblk; -+ int ret; -+ -+ ctxt.cur_dev = fs_dev_desc; -+ ctxt.cur_part_info = *fs_partition; -+ -+ ret = sqfs_read_sblk(&sblk); -+ if (ret) -+ goto error; -+ -+ /* Make sure it has a valid SquashFS magic number*/ -+ if (get_unaligned_le32(&sblk->s_magic) != SQFS_MAGIC_NUMBER) { -+ printf("Bad magic number for SquashFS image.\n"); -+ ret = -EINVAL; -+ goto error; -+ } -+ -+ ctxt.sblk = sblk; -+ -+ ret = sqfs_decompressor_init(&ctxt); -+ if (ret) { -+ goto error; -+ } -+ -+ return 0; -+error: -+ ctxt.cur_dev = NULL; -+ free(ctxt.sblk); -+ ctxt.sblk = NULL; -+ return ret; -+} -+ -+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; -+ -+ *file = NULL; -+ *dir = NULL; -+ dirc = NULL; -+ basec = NULL; -+ bname = NULL; -+ dname = NULL; -+ tmp_path = NULL; -+ -+ /* check for first slash in path*/ -+ if (path[0] == '/') { -+ tmp_path = strdup(path); -+ if (!tmp_path) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ } else { -+ tmp_path = malloc(strlen(path) + 2); -+ if (!tmp_path) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ tmp_path[0] = '/'; -+ strcpy(tmp_path + 1, path); -+ } -+ -+ /* String duplicates */ -+ dirc = strdup(tmp_path); -+ if (!dirc) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ basec = strdup(tmp_path); -+ if (!basec) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ dname = sqfs_dirname(dirc); -+ bname = sqfs_basename(basec); -+ -+ *file = strdup(bname); -+ -+ if (!*file) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ if (*dname == '\0') { -+ *dir = malloc(2); -+ if (!*dir) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ (*dir)[0] = '/'; -+ (*dir)[1] = '\0'; -+ } else { -+ *dir = strdup(dname); -+ if (!*dir) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ } -+ -+out: -+ if (ret) { -+ free(*file); -+ free(*dir); -+ *dir = NULL; -+ *file = NULL; -+ } -+ free(basec); -+ free(dirc); -+ 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 = get_unaligned_le32(®->file_size); -+ finfo->offset = get_unaligned_le32(®->offset); -+ finfo->start = get_unaligned_le32(®->start_block); -+ finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(®->fragment)); -+ -+ if (finfo->frag && finfo->offset == 0xFFFFFFFF) -+ return -EINVAL; -+ -+ if (finfo->size < 1 || finfo->start == 0xFFFFFFFF) -+ return -EINVAL; -+ -+ if (finfo->frag) { -+ datablk_count = finfo->size / le32_to_cpu(blksz); -+ ret = sqfs_frag_lookup(get_unaligned_le32(®->fragment), -+ fentry); -+ if (ret < 0) -+ return -EINVAL; -+ finfo->comp = true; -+ if (fentry->size < 1 || fentry->start == 0x7FFFFFFF) -+ return -EINVAL; -+ } 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 = get_unaligned_le64(&lreg->file_size); -+ finfo->offset = get_unaligned_le32(&lreg->offset); -+ finfo->start = get_unaligned_le64(&lreg->start_block); -+ finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment)); -+ -+ if (finfo->frag && finfo->offset == 0xFFFFFFFF) -+ return -EINVAL; -+ -+ if (finfo->size < 1 || finfo->start == 0x7FFFFFFF) -+ return -EINVAL; -+ -+ if (finfo->frag) { -+ datablk_count = finfo->size / le32_to_cpu(blksz); -+ ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment), -+ fentry); -+ if (ret < 0) -+ return -EINVAL; -+ finfo->comp = true; -+ if (fentry->size < 1 || fentry->start == 0x7FFFFFFF) -+ return -EINVAL; -+ } 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 = NULL, *fragment_block, *datablock = NULL, *data_buffer = NULL; -+ char *fragment = NULL, *file = NULL, *resolved, *data; -+ u64 start, n_blks, table_size, data_offset, table_offset; -+ int ret, j, i_number, datablk_count = 0; -+ struct squashfs_super_block *sblk = ctxt.sblk; -+ struct squashfs_fragment_block_entry frag_entry; -+ struct squashfs_file_info finfo = {0}; -+ struct squashfs_symlink_inode *symlink; -+ struct fs_dir_stream *dirsp = NULL; -+ 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; -+ -+ if (offset) { -+ /* -+ * TODO: implement reading at an offset in file -+ */ -+ printf("Error: reading at a specific offset in a squashfs file is not supported yet.\n"); -+ return -EINVAL; -+ } -+ -+ /* -+ * 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, &dirsp); -+ if (ret) { -+ goto out; -+ } -+ -+ dirs = (struct squashfs_dir_stream *)dirsp; -+ -+ /* For now, only regular files are able to be loaded */ -+ while (!sqfs_readdir(dirsp, &dent)) { -+ ret = strcmp(dent->name, file); -+ if (!ret) -+ break; -+ -+ free(dirs->entry); -+ dirs->entry = NULL; -+ } -+ -+ if (ret) { -+ printf("File not found.\n"); -+ *actread = 0; -+ ret = -ENOENT; -+ goto out; -+ } -+ -+ 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); -+ -+ base = (struct squashfs_base_inode *)ipos; -+ switch (get_unaligned_le16(&base->inode_type)) { -+ case SQFS_REG_TYPE: -+ reg = (struct squashfs_reg_inode *)ipos; -+ datablk_count = sqfs_get_regfile_info(reg, &finfo, &frag_entry, -+ sblk->block_size); -+ if (datablk_count < 0) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ memcpy(finfo.blk_sizes, ipos + sizeof(*reg), -+ datablk_count * sizeof(u32)); -+ break; -+ case SQFS_LREG_TYPE: -+ lreg = (struct squashfs_lreg_inode *)ipos; -+ datablk_count = sqfs_get_lregfile_info(lreg, &finfo, -+ &frag_entry, -+ sblk->block_size); -+ if (datablk_count < 0) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ memcpy(finfo.blk_sizes, ipos + sizeof(*lreg), -+ datablk_count * sizeof(u32)); -+ break; -+ case SQFS_SYMLINK_TYPE: -+ case SQFS_LSYMLINK_TYPE: -+ symlink = (struct squashfs_symlink_inode *)ipos; -+ resolved = sqfs_resolve_symlink(symlink, filename); -+ ret = sqfs_read(resolved, buf, offset, len, actread); -+ free(resolved); -+ goto out; -+ 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 = -EINVAL; -+ goto out; -+ } -+ -+ /* If the user specifies a length, check its sanity */ -+ if (len) { -+ if (len > finfo.size) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ finfo.size = len; -+ } else { -+ len = finfo.size; -+ } -+ -+ if (datablk_count) { -+ data_offset = finfo.start; -+ datablock = malloc(get_unaligned_le32(&sblk->block_size)); -+ if (!datablock) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ } -+ -+ for (j = 0; j < datablk_count; j++) { -+ start = data_offset / ctxt.cur_dev->blksz; -+ table_size = SQFS_BLOCK_SIZE(finfo.blk_sizes[j]); -+ table_offset = data_offset - (start * ctxt.cur_dev->blksz); -+ n_blks = DIV_ROUND_UP(table_size + table_offset, -+ ctxt.cur_dev->blksz); -+ -+ data_buffer = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz); -+ -+ if (!data_buffer) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ ret = sqfs_disk_read(start, n_blks, data_buffer); -+ if (ret < 0) { -+ /* -+ * Possible causes: too many data blocks or too large -+ * SquashFS block size. Tip: re-compile the SquashFS -+ * image with mksquashfs's -b option. -+ */ -+ printf("Error: too many data blocks to be read.\n"); -+ goto out; -+ } -+ -+ data = data_buffer + table_offset; -+ -+ /* Load the data */ -+ if (SQFS_COMPRESSED_BLOCK(finfo.blk_sizes[j])) { -+ dest_len = get_unaligned_le32(&sblk->block_size); -+ ret = sqfs_decompress(&ctxt, datablock, &dest_len, -+ data, table_size); -+ if (ret) -+ goto out; -+ -+ if ((*actread + dest_len) > len) -+ dest_len = len - *actread; -+ memcpy(buf + *actread, datablock, dest_len); -+ *actread += dest_len; -+ } else { -+ if ((*actread + table_size) > len) -+ table_size = len - *actread; -+ memcpy(buf + *actread, data, table_size); -+ *actread += table_size; -+ } -+ -+ data_offset += table_size; -+ free(data_buffer); -+ data_buffer = NULL; -+ if (*actread >= len) -+ break; -+ } -+ -+ /* -+ * There is no need to continue if the file is not fragmented. -+ */ -+ if (!finfo.frag) { -+ ret = 0; -+ goto out; -+ } -+ -+ start = frag_entry.start / ctxt.cur_dev->blksz; -+ table_size = SQFS_BLOCK_SIZE(frag_entry.size); -+ table_offset = frag_entry.start - (start * ctxt.cur_dev->blksz); -+ n_blks = DIV_ROUND_UP(table_size + table_offset, ctxt.cur_dev->blksz); -+ -+ fragment = malloc_cache_aligned(n_blks * ctxt.cur_dev->blksz); -+ -+ if (!fragment) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ ret = sqfs_disk_read(start, n_blks, fragment); -+ if (ret < 0) -+ goto out; -+ -+ /* File compressed and fragmented */ -+ if (finfo.frag && finfo.comp) { -+ dest_len = get_unaligned_le32(&sblk->block_size); -+ fragment_block = malloc(dest_len); -+ if (!fragment_block) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ ret = sqfs_decompress(&ctxt, fragment_block, &dest_len, -+ (void *)fragment + table_offset, -+ frag_entry.size); -+ if (ret) { -+ free(fragment_block); -+ goto out; -+ } -+ -+ for (j = *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 = *actread; j < finfo.size; j++) { -+ memcpy(buf + j, &fragment_block[finfo.offset + j], 1); -+ (*actread)++; -+ } -+ } -+ -+out: -+ free(fragment); -+ if (datablk_count) { -+ free(data_buffer); -+ free(datablock); -+ } -+ free(file); -+ free(dir); -+ free(finfo.blk_sizes); -+ sqfs_closedir(dirsp); -+ -+ return ret; -+} -+ -+int sqfs_size(const char *filename, loff_t *size) -+{ -+ struct squashfs_super_block *sblk = ctxt.sblk; -+ struct squashfs_symlink_inode *symlink; -+ struct fs_dir_stream *dirsp = NULL; -+ 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; -+ -+ 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, &dirsp); -+ if (ret) { -+ ret = -EINVAL; -+ goto free_strings; -+ } -+ -+ dirs = (struct squashfs_dir_stream *)dirsp; -+ -+ while (!sqfs_readdir(dirsp, &dent)) { -+ ret = strcmp(dent->name, file); -+ if (!ret) -+ break; -+ free(dirs->entry); -+ dirs->entry = NULL; -+ } -+ -+ if (ret) { -+ printf("File not found.\n"); -+ *size = 0; -+ ret = -EINVAL; -+ 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); -+ dirs->entry = NULL; -+ -+ base = (struct squashfs_base_inode *)ipos; -+ switch (get_unaligned_le16(&base->inode_type)) { -+ case SQFS_REG_TYPE: -+ reg = (struct squashfs_reg_inode *)ipos; -+ *size = get_unaligned_le32(®->file_size); -+ break; -+ case SQFS_LREG_TYPE: -+ lreg = (struct squashfs_lreg_inode *)ipos; -+ *size = get_unaligned_le64(&lreg->file_size); -+ break; -+ case SQFS_SYMLINK_TYPE: -+ case SQFS_LSYMLINK_TYPE: -+ symlink = (struct squashfs_symlink_inode *)ipos; -+ resolved = sqfs_resolve_symlink(symlink, filename); -+ ret = sqfs_size(resolved, size); -+ free(resolved); -+ 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; -+ ret = -EINVAL; -+ break; -+ } -+ -+free_strings: -+ free(dir); -+ free(file); -+ -+ sqfs_closedir(dirsp); -+ -+ return ret; -+} -+ -+int sqfs_exists(const char *filename) -+{ -+ struct fs_dir_stream *dirsp = NULL; -+ struct squashfs_dir_stream *dirs; -+ char *dir, *file; -+ struct fs_dirent *dent; -+ int 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, &dirsp); -+ if (ret) { -+ ret = -EINVAL; -+ goto free_strings; -+ } -+ -+ dirs = (struct squashfs_dir_stream *)dirsp; -+ -+ while (!sqfs_readdir(dirsp, &dent)) { -+ ret = strcmp(dent->name, file); -+ if (!ret) -+ break; -+ free(dirs->entry); -+ dirs->entry = NULL; -+ } -+ -+ sqfs_closedir(dirsp); -+ -+free_strings: -+ free(dir); -+ free(file); -+ -+ return ret == 0; -+} -+ -+void sqfs_close(void) -+{ -+ sqfs_decompressor_cleanup(&ctxt); -+ free(ctxt.sblk); -+ ctxt.sblk = NULL; -+ ctxt.cur_dev = NULL; -+} -+ -+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); -+ free(sqfs_dirs); -+} -diff --git a/fs/squashfs/sqfs_decompressor.c b/fs/squashfs/sqfs_decompressor.c -new file mode 100644 -index 00000000..d69ddb24 ---- /dev/null -+++ b/fs/squashfs/sqfs_decompressor.c -@@ -0,0 +1,155 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2020 Bootlin -+ * -+ * Author: Joao Marcos Costa -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#if IS_ENABLED(CONFIG_LZO) -+#include -+#endif -+ -+#if IS_ENABLED(CONFIG_ZLIB) -+#include -+#endif -+ -+#if IS_ENABLED(CONFIG_ZSTD) -+#include -+#endif -+ -+#include "sqfs_decompressor.h" -+#include "sqfs_utils.h" -+ -+int sqfs_decompressor_init(struct squashfs_ctxt *ctxt) -+{ -+ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); -+ -+ switch (comp_type) { -+#if IS_ENABLED(CONFIG_LZO) -+ case SQFS_COMP_LZO: -+ break; -+#endif -+#if IS_ENABLED(CONFIG_ZLIB) -+ case SQFS_COMP_ZLIB: -+ break; -+#endif -+#if IS_ENABLED(CONFIG_ZSTD) -+ case SQFS_COMP_ZSTD: -+ ctxt->zstd_workspace = malloc(ZSTD_DCtxWorkspaceBound()); -+ if (!ctxt->zstd_workspace) -+ return -ENOMEM; -+ break; -+#endif -+ default: -+ printf("Error: unknown compression type.\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt) -+{ -+ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); -+ -+ switch (comp_type) { -+#if IS_ENABLED(CONFIG_LZO) -+ case SQFS_COMP_LZO: -+ break; -+#endif -+#if IS_ENABLED(CONFIG_ZLIB) -+ case SQFS_COMP_ZLIB: -+ break; -+#endif -+#if IS_ENABLED(CONFIG_ZSTD) -+ case SQFS_COMP_ZSTD: -+ free(ctxt->zstd_workspace); -+ break; -+#endif -+ } -+} -+ -+#if IS_ENABLED(CONFIG_ZLIB) -+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; -+ } -+} -+#endif -+ -+#if IS_ENABLED(CONFIG_ZSTD) -+static int sqfs_zstd_decompress(struct squashfs_ctxt *ctxt, void *dest, -+ unsigned long dest_len, void *source, u32 src_len) -+{ -+ ZSTD_DCtx *ctx; -+ size_t wsize; -+ int ret; -+ -+ wsize = ZSTD_DCtxWorkspaceBound(); -+ ctx = ZSTD_initDCtx(ctxt->zstd_workspace, wsize); -+ ret = ZSTD_decompressDCtx(ctx, dest, dest_len, source, src_len); -+ -+ return ZSTD_isError(ret); -+} -+#endif /* CONFIG_ZSTD */ -+ -+int sqfs_decompress(struct squashfs_ctxt *ctxt, void *dest, -+ unsigned long *dest_len, void *source, u32 src_len) -+{ -+ u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); -+ int ret = 0; -+ -+ switch (comp_type) { -+#if IS_ENABLED(CONFIG_LZO) -+ case SQFS_COMP_LZO: { -+ size_t lzo_dest_len = *dest_len; -+ ret = lzo1x_decompress_safe(source, src_len, dest, &lzo_dest_len); -+ if (ret) { -+ printf("LZO decompression failed. Error code: %d\n", ret); -+ return -EINVAL; -+ } -+ -+ break; -+ } -+#endif -+#if IS_ENABLED(CONFIG_ZLIB) -+ case SQFS_COMP_ZLIB: -+ ret = uncompress(dest, dest_len, source, src_len); -+ if (ret) { -+ zlib_decompression_status(ret); -+ return -EINVAL; -+ } -+ -+ break; -+#endif -+#if IS_ENABLED(CONFIG_ZSTD) -+ case SQFS_COMP_ZSTD: -+ ret = sqfs_zstd_decompress(ctxt, dest, *dest_len, source, src_len); -+ if (ret) { -+ printf("ZSTD Error code: %d\n", ZSTD_getErrorCode(ret)); -+ return -EINVAL; -+ } -+ -+ break; -+#endif -+ 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 00000000..892cfb69 ---- /dev/null -+++ b/fs/squashfs/sqfs_decompressor.h -@@ -0,0 +1,61 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) 2020 Bootlin -+ * -+ * Author: Joao Marcos Costa -+ */ -+ -+#ifndef SQFS_DECOMPRESSOR_H -+#define SQFS_DECOMPRESSOR_H -+ -+#include -+#include "sqfs_filesystem.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(struct squashfs_ctxt *ctxt, void *dest, -+ unsigned long *dest_len, void *source, u32 src_len); -+int sqfs_decompressor_init(struct squashfs_ctxt *ctxt); -+void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt); -+ -+#endif /* SQFS_DECOMPRESSOR_H */ -diff --git a/fs/squashfs/sqfs_dir.c b/fs/squashfs/sqfs_dir.c -new file mode 100644 -index 00000000..a265b98f ---- /dev/null -+++ b/fs/squashfs/sqfs_dir.c -@@ -0,0 +1,93 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2020 Bootlin -+ * -+ * Author: Joao Marcos Costa -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "sqfs_filesystem.h" -+#include "sqfs_utils.h" -+ -+bool sqfs_is_dir(u16 type) -+{ -+ return type == SQFS_DIR_TYPE || 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 = dir_i; -+ struct squashfs_ldir_inode *ldir; -+ struct squashfs_dir_inode *dir; -+ u32 start_block; -+ int j, offset; -+ -+ switch (get_unaligned_le16(&base->inode_type)) { -+ case SQFS_DIR_TYPE: -+ dir = (struct squashfs_dir_inode *)base; -+ start_block = get_unaligned_le32(&dir->start_block); -+ offset = get_unaligned_le16(&dir->offset); -+ break; -+ case SQFS_LDIR_TYPE: -+ ldir = (struct squashfs_ldir_inode *)base; -+ start_block = get_unaligned_le32(&ldir->start_block); -+ offset = get_unaligned_le16(&ldir->offset); -+ break; -+ default: -+ printf("Error: this is not a directory.\n"); -+ return -EINVAL; -+ } -+ -+ if (offset < 0) -+ 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 = dir_i; -+ struct squashfs_ldir_inode *ldir; -+ struct squashfs_dir_inode *dir; -+ u32 file_size; -+ -+ switch (get_unaligned_le16(&base->inode_type)) { -+ case SQFS_DIR_TYPE: -+ dir = (struct squashfs_dir_inode *)base; -+ file_size = get_unaligned_le16(&dir->file_size); -+ break; -+ case SQFS_LDIR_TYPE: -+ ldir = (struct squashfs_ldir_inode *)base; -+ file_size = get_unaligned_le16(&ldir->file_size); -+ break; -+ default: -+ printf("Error: this is not a directory.\n"); -+ return false; -+ } -+ -+ 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 00000000..856cd15e ---- /dev/null -+++ b/fs/squashfs/sqfs_filesystem.h -@@ -0,0 +1,310 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) 2020 Bootlin -+ * -+ * Author: Joao Marcos Costa -+ */ -+ -+#ifndef SQFS_FILESYSTEM_H -+#define SQFS_FILESYSTEM_H -+ -+#include -+#include -+#include -+#include -+ -+#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 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_ctxt { -+ struct disk_partition cur_part_info; -+ struct blk_desc *cur_dev; -+ struct squashfs_super_block *sblk; -+#if IS_ENABLED(CONFIG_ZSTD) -+ void *zstd_workspace; -+#endif -+}; -+ -+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(u16 type); -+ -+#endif /* SQFS_FILESYSTEM_H */ -diff --git a/fs/squashfs/sqfs_inode.c b/fs/squashfs/sqfs_inode.c -new file mode 100644 -index 00000000..e76ec7cb ---- /dev/null -+++ b/fs/squashfs/sqfs_inode.c -@@ -0,0 +1,161 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2020 Bootlin -+ * -+ * Author: Joao Marcos Costa -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "sqfs_decompressor.h" -+#include "sqfs_filesystem.h" -+#include "sqfs_utils.h" -+ -+int sqfs_inode_size(struct squashfs_base_inode *inode, u32 blk_size) -+{ -+ switch (get_unaligned_le16(&inode->inode_type)) { -+ case SQFS_DIR_TYPE: -+ return sizeof(struct squashfs_dir_inode); -+ -+ case SQFS_REG_TYPE: { -+ struct squashfs_reg_inode *reg = -+ (struct squashfs_reg_inode *)inode; -+ u32 fragment = get_unaligned_le32(®->fragment); -+ u32 file_size = get_unaligned_le32(®->file_size); -+ unsigned int blk_list_size; -+ -+ if (SQFS_IS_FRAGMENTED(fragment)) -+ blk_list_size = file_size / blk_size; -+ else -+ blk_list_size = DIV_ROUND_UP(file_size, blk_size); -+ -+ return sizeof(*reg) + blk_list_size * sizeof(u32); -+ } -+ -+ case SQFS_LDIR_TYPE: { -+ struct squashfs_ldir_inode *ldir = -+ (struct squashfs_ldir_inode *)inode; -+ u16 i_count = get_unaligned_le16(&ldir->i_count); -+ unsigned int index_list_size = 0, l = 0; -+ struct squashfs_directory_index *di; -+ u32 sz; -+ -+ if (i_count == 0) -+ return sizeof(*ldir); -+ -+ di = ldir->index; -+ while (l < i_count) { -+ sz = get_unaligned_le32(&di->size) + 1; -+ index_list_size += sz; -+ di = (void *)di + sizeof(*di) + sz; -+ l++; -+ } -+ -+ return sizeof(*ldir) + index_list_size + -+ i_count * SQFS_DIR_INDEX_BASE_LENGTH; -+ } -+ -+ case SQFS_LREG_TYPE: { -+ struct squashfs_lreg_inode *lreg = -+ (struct squashfs_lreg_inode *)inode; -+ u32 fragment = get_unaligned_le32(&lreg->fragment); -+ u64 file_size = get_unaligned_le64(&lreg->file_size); -+ unsigned int blk_list_size; -+ -+ if (fragment == 0xFFFFFFFF) -+ blk_list_size = DIV_ROUND_UP(file_size, blk_size); -+ else -+ blk_list_size = file_size / blk_size; -+ -+ return sizeof(*lreg) + blk_list_size * sizeof(u32); -+ } -+ -+ case SQFS_SYMLINK_TYPE: -+ case SQFS_LSYMLINK_TYPE: { -+ struct squashfs_symlink_inode *symlink = -+ (struct squashfs_symlink_inode *)inode; -+ -+ return sizeof(*symlink) + -+ get_unaligned_le32(&symlink->symlink_size); -+ } -+ -+ case SQFS_BLKDEV_TYPE: -+ case SQFS_CHRDEV_TYPE: -+ return sizeof(struct squashfs_dev_inode); -+ case SQFS_LBLKDEV_TYPE: -+ case SQFS_LCHRDEV_TYPE: -+ return sizeof(struct squashfs_ldev_inode); -+ case SQFS_FIFO_TYPE: -+ case SQFS_SOCKET_TYPE: -+ return sizeof(struct squashfs_ipc_inode); -+ case SQFS_LFIFO_TYPE: -+ case SQFS_LSOCKET_TYPE: -+ return sizeof(struct squashfs_lipc_inode); -+ default: -+ printf("Error while searching inode: unknown type.\n"); -+ return -EINVAL; -+ } -+} -+ -+/* -+ * 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) -+{ -+ struct squashfs_base_inode *base; -+ unsigned int offset = 0, k; -+ int sz; -+ -+ if (!inode_table) { -+ printf("%s: Invalid pointer to inode table.\n", __func__); -+ return NULL; -+ } -+ -+ for (k = 0; k < le32_to_cpu(inode_count); k++) { -+ base = inode_table + offset; -+ if (get_unaligned_le32(&base->inode_number) == inode_number) -+ return inode_table + offset; -+ -+ sz = sqfs_inode_size(base, le32_to_cpu(block_size)); -+ if (sz < 0) -+ return NULL; -+ -+ offset += sz; -+ } -+ -+ printf("Inode not found.\n"); -+ -+ return NULL; -+} -+ -+int sqfs_read_metablock(unsigned char *file_mapping, int offset, -+ bool *compressed, u32 *data_size) -+{ -+ const unsigned char *data; -+ u16 header; -+ -+ if (!file_mapping) -+ return -EFAULT; -+ data = file_mapping + offset; -+ -+ header = get_unaligned((u16 *)data); -+ if (!header) -+ return -EINVAL; -+ -+ *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 00000000..1260abe2 ---- /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 -+ */ -+ -+#ifndef SQFS_UTILS_H -+#define SQFS_UTILS_H -+ -+#include -+#include -+#include -+ -+#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 742a535b..d3d81f9f 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 - - /* - * Tell the fs layer which block device an partition to use for future -diff --git a/include/hang.h b/include/hang.h -new file mode 100644 -index 00000000..27cda493 ---- /dev/null -+++ b/include/hang.h -@@ -0,0 +1,22 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ -+/* -+ * (C) Copyright 2000-2009 -+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de. -+ */ -+ -+#ifndef __HANG_H -+#define __HANG_H -+ -+#ifndef __ASSEMBLY__ -+/** -+ * hang() - Print a message and stop execution -+ * -+ * This shows a 'hang' message where possible and then goes into an infinite -+ * loop. This is called by panic() if CONFIG_PANIC_HANG is enabled. -+ * -+ * This function does not return. -+ */ -+void hang(void) __attribute__ ((noreturn)); -+#endif -+ -+#endif -diff --git a/include/squashfs.h b/include/squashfs.h -new file mode 100644 -index 00000000..7489eefa ---- /dev/null -+++ b/include/squashfs.h -@@ -0,0 +1,26 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) 2020 Bootlin -+ * -+ * Author: Joao Marcos Costa -+ * -+ * squashfs.h: SquashFS filesystem implementation. -+ */ -+ -+#ifndef _SQFS_H_ -+#define _SQFS_H_ -+ -+struct disk_partition; -+ -+int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp); -+int sqfs_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp); -+int sqfs_probe(struct blk_desc *fs_dev_desc, -+ 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); -+int sqfs_exists(const char *filename); -+void sqfs_close(void); -+void sqfs_closedir(struct fs_dir_stream *dirs); -+ -+#endif /* SQFS_H */ -diff --git a/include/u-boot/zlib.h b/include/u-boot/zlib.h -index e23ceb50..a33cc878 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/Kconfig b/lib/Kconfig -index 965cf7bc..26410eb5 100644 ---- a/lib/Kconfig -+++ b/lib/Kconfig -@@ -398,6 +398,13 @@ config GZIP - help - This enables support for GZIP compression algorithm. - -+config ZLIB_UNCOMPRESS -+ bool "Enables zlib's uncompress() functionality" -+ help -+ This enables an extra zlib functionality: the uncompress() function, -+ which decompresses data from a buffer into another, knowing their -+ sizes. Unlike gunzip(), there is no header parsing. -+ - config ZLIB - bool - default y -diff --git a/lib/zlib/inftrees.c b/lib/zlib/inftrees.c -index b71b9695..caee502f 100644 ---- a/lib/zlib/inftrees.c -+++ b/lib/zlib/inftrees.c -@@ -50,7 +50,7 @@ int inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, - code FAR *next; /* next available space in table */ - const unsigned short FAR *base; /* base value table to use */ - const unsigned short FAR *extra; /* extra bits table to use */ -- int end; /* use base and extra for symbol > end */ -+ unsigned match; /* use base and extra for symbol >= match */ - unsigned short count[MAXBITS+1]; /* number of codes of each length */ - unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ - static const unsigned short lbase[31] = { /* Length codes 257..285 base */ -@@ -178,19 +178,17 @@ int inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, - switch (type) { - case CODES: - base = extra = work; /* dummy value--not used */ -- end = 19; -+ match = 20; - break; - case LENS: - base = lbase; -- base -= 257; - extra = lext; -- extra -= 257; -- end = 256; -+ match = 257; - break; - default: /* DISTS */ - base = dbase; - extra = dext; -- end = -1; -+ match = 0; - } - - /* initialize state for loop */ -@@ -212,13 +210,12 @@ int inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, - for (;;) { - /* create table entry */ - this.bits = (unsigned char)(len - drop); -- if ((int)(work[sym]) < end) { -+ if (work[sym] + 1 < match) { - this.op = (unsigned char)0; - this.val = work[sym]; -- } -- else if ((int)(work[sym]) > end) { -- this.op = (unsigned char)(extra[work[sym]]); -- this.val = base[work[sym]]; -+ } else if (work[sym] >= match) { -+ this.op = (unsigned char)(extra[work[sym] - match]); -+ this.val = base[work[sym] - match]; - } - else { - this.op = (unsigned char)(32 + 64); /* end of block */ -diff --git a/lib/zlib/trees.c b/lib/zlib/trees.c -index a0078d08..3e09517e 100644 ---- a/lib/zlib/trees.c -+++ b/lib/zlib/trees.c -@@ -7,27 +7,28 @@ - /* - * ALGORITHM - * -- * The "deflation" process uses several Huffman trees. The more -- * common source values are represented by shorter bit sequences. -+ * The "deflation" process uses several Huffman trees. The more -+ * common source values are represented by shorter bit sequences. - * -- * Each code tree is stored in a compressed form which is itself -- * a Huffman encoding of the lengths of all the code strings (in -- * ascending order by source values). The actual code strings are -- * reconstructed from the lengths in the inflate process, as described -- * in the deflate specification. -+ * Each code tree is stored in a compressed form which is itself -+ * a Huffman encoding of the lengths of all the code strings (in -+ * ascending order by source values). The actual code strings are -+ * reconstructed from the lengths in the inflate process, as -+ * described in the deflate specification. - * - * REFERENCES - * -- * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". -- * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc -+ * Deutsch, P. -+ * RFC 1951, DEFLATE Compressed Data Format Specification version 1.3 -+ * https://tools.ietf.org/html/rfc1951, 1996 - * -- * Storer, James A. -- * Data Compression: Methods and Theory, pp. 49-50. -- * Computer Science Press, 1988. ISBN 0-7167-8156-5. -+ * Storer, James A. -+ * Data Compression: Methods and Theory, pp. 49-50. -+ * Computer Science Press, 1988. ISBN 0-7167-8156-5. - * -- * Sedgewick, R. -- * Algorithms, p290. -- * Addison-Wesley, 1983. ISBN 0-201-06672-6. -+ * Sedgewick, R. -+ * Algorithms, p290. -+ * Addison-Wesley, 1983. ISBN 0-201-06672-6. - */ - - /* @(#) $Id$ */ -diff --git a/lib/zlib/uncompr.c b/lib/zlib/uncompr.c -new file mode 100644 -index 00000000..21e93933 ---- /dev/null -+++ b/lib/zlib/uncompr.c -@@ -0,0 +1,97 @@ -+/* 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; -+ /* for detection of incomplete stream when *destLen == 0 */ -+ Byte buf[1]; -+ -+ 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 7e157029..90e05e7d 100644 ---- a/lib/zlib/zlib.c -+++ b/lib/zlib/zlib.c -@@ -30,3 +30,6 @@ - #include "inflate.c" - #include "zutil.c" - #include "adler32.c" -+#if IS_ENABLED(CONFIG_ZLIB_UNCOMPRESS) -+#include "uncompr.c" -+#endif -diff --git a/lib/zlib/zutil.c b/lib/zlib/zutil.c -index 227343e4..609aac55 100644 ---- a/lib/zlib/zutil.c -+++ b/lib/zlib/zutil.c -@@ -6,6 +6,7 @@ - /* @(#) $Id$ */ - - #include "zutil.h" -+#include - - #ifndef NO_DUMMY_DECL - struct internal_state {int dummy;}; /* for buggy compilers */ -@@ -34,7 +35,7 @@ void z_error (m) - char *m; - { - fprintf(stderr, "%s\n", m); -- hang (); -+ hang(); - } - #endif - -diff --git a/test/py/tests/test_fs/test_squashfs/sqfs_common.py b/test/py/tests/test_fs/test_squashfs/sqfs_common.py -new file mode 100644 -index 00000000..c96f92c1 ---- /dev/null -+++ b/test/py/tests/test_fs/test_squashfs/sqfs_common.py -@@ -0,0 +1,76 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (C) 2020 Bootlin -+# Author: Joao Marcos Costa -+ -+import os -+import random -+import string -+import subprocess -+ -+def sqfs_get_random_letters(size): -+ letters = [] -+ for i in range(0, size): -+ letters.append(random.choice(string.ascii_letters)) -+ -+ return ''.join(letters) -+ -+def sqfs_generate_file(path, size): -+ content = sqfs_get_random_letters(size) -+ file = open(path, "w") -+ file.write(content) -+ file.close() -+ -+class Compression: -+ def __init__(self, name, files, sizes, block_size = 4096): -+ self.name = name -+ self.files = files -+ self.sizes = sizes -+ self.mksquashfs_opts = " -b " + str(block_size) + " -comp " + self.name -+ -+ def add_opt(self, opt): -+ self.mksquashfs_opts += " " + opt -+ -+ def gen_image(self, build_dir): -+ src = os.path.join(build_dir, "sqfs_src/") -+ os.mkdir(src) -+ for (f, s) in zip(self.files, self.sizes): -+ sqfs_generate_file(src + f, s) -+ -+ # the symbolic link always targets the first file -+ os.symlink(self.files[0], src + "sym") -+ -+ sqfs_img = os.path.join(build_dir, "sqfs-" + self.name) -+ i_o = src + " " + sqfs_img -+ opts = self.mksquashfs_opts -+ try: -+ subprocess.run(["mksquashfs " + i_o + opts], shell = True, check = True) -+ except: -+ print("mksquashfs error. Compression type: " + self.name) -+ raise RuntimeError -+ -+ def clean_source(self, build_dir): -+ src = os.path.join(build_dir, "sqfs_src/") -+ for f in self.files: -+ os.remove(src + f) -+ os.remove(src + "sym") -+ os.rmdir(src) -+ -+ def cleanup(self, build_dir): -+ self.clean_source(build_dir) -+ sqfs_img = os.path.join(build_dir, "sqfs-" + self.name) -+ os.remove(sqfs_img) -+ -+files = ["blks_only", "blks_frag", "frag_only"] -+sizes = [4096, 5100, 100] -+gzip = Compression("gzip", files, sizes) -+zstd = Compression("zstd", files, sizes) -+lzo = Compression("lzo", files, sizes) -+ -+# use fragment blocks for files larger than block_size -+gzip.add_opt("-always-use-fragments") -+zstd.add_opt("-always-use-fragments") -+ -+# avoid fragments if lzo is used -+lzo.add_opt("-no-fragments") -+ -+comp_opts = [gzip, zstd, lzo] -diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py b/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py -new file mode 100644 -index 00000000..9e900623 ---- /dev/null -+++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py -@@ -0,0 +1,46 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (C) 2020 Bootlin -+# Author: Joao Marcos Costa -+ -+import os -+import pytest -+from sqfs_common import * -+ -+@pytest.mark.boardspec('sandbox') -+@pytest.mark.buildconfigspec('cmd_fs_generic') -+@pytest.mark.buildconfigspec('cmd_squashfs') -+@pytest.mark.buildconfigspec('fs_squashfs') -+@pytest.mark.requiredtool('mksquashfs') -+def test_sqfs_load(u_boot_console): -+ build_dir = u_boot_console.config.build_dir -+ command = "sqfsload host 0 $kernel_addr_r " -+ -+ for opt in comp_opts: -+ # generate and load the squashfs image -+ try: -+ opt.gen_image(build_dir) -+ except RuntimeError: -+ opt.clean_source(build_dir) -+ # skip unsupported compression types -+ continue -+ -+ path = os.path.join(build_dir, "sqfs-" + opt.name) -+ output = u_boot_console.run_command("host bind 0 " + path) -+ -+ output = u_boot_console.run_command(command + "xxx") -+ assert "File not found." in output -+ -+ for (f, s) in zip(opt.files, opt.sizes): -+ try: -+ output = u_boot_console.run_command(command + f) -+ assert str(s) in output -+ except: -+ assert False -+ opt.cleanup(build_dir) -+ -+ # test symbolic link -+ output = u_boot_console.run_command(command + "sym") -+ assert str(opt.sizes[0]) in output -+ -+ # remove generated files -+ opt.cleanup(build_dir) -diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py -new file mode 100644 -index 00000000..a0dca2e2 ---- /dev/null -+++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py -@@ -0,0 +1,36 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (C) 2020 Bootlin -+# Author: Joao Marcos Costa -+ -+import os -+import pytest -+from sqfs_common import * -+ -+@pytest.mark.boardspec('sandbox') -+@pytest.mark.buildconfigspec('cmd_fs_generic') -+@pytest.mark.buildconfigspec('cmd_squashfs') -+@pytest.mark.buildconfigspec('fs_squashfs') -+@pytest.mark.requiredtool('mksquashfs') -+def test_sqfs_ls(u_boot_console): -+ build_dir = u_boot_console.config.build_dir -+ for opt in comp_opts: -+ try: -+ opt.gen_image(build_dir) -+ except RuntimeError: -+ opt.clean_source(build_dir) -+ # skip unsupported compression types -+ continue -+ path = os.path.join(build_dir, "sqfs-" + opt.name) -+ output = u_boot_console.run_command("host bind 0 " + path) -+ -+ try: -+ # list files in root directory -+ output = u_boot_console.run_command("sqfsls host 0") -+ assert str(len(opt.files) + 1) + " file(s), 0 dir(s)" in output -+ assert " sym" in output -+ output = u_boot_console.run_command("sqfsls host 0 xxx") -+ assert "** Cannot find directory. **" in output -+ except: -+ opt.cleanup(build_dir) -+ assert False -+ opt.cleanup(build_dir) --- -2.25.1 - From e07f243f8f30b412258cd3b44a93294f89374555 Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Wed, 4 Aug 2021 17:29:27 -0400 Subject: [PATCH 11/12] linux: bump to linux4sam-2021.04 --- linux/linux-5.4.defconfig | 9 +++------ nerves_defconfig | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/linux/linux-5.4.defconfig b/linux/linux-5.4.defconfig index d096f35..39f7806 100644 --- a/linux/linux-5.4.defconfig +++ b/linux/linux-5.4.defconfig @@ -14,8 +14,6 @@ CONFIG_SOC_SAMA5D2=y CONFIG_SOC_SAMA5D3=y CONFIG_SOC_SAMA5D4=y CONFIG_UACCESS_WITH_MEMCPY=y -CONFIG_ZBOOT_ROM_TEXT=0x0 -CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_ARM_APPENDED_DTB=y CONFIG_CMDLINE="console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw" CONFIG_KEXEC=y @@ -66,7 +64,6 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=4 CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_ATMEL_TCLIB=y CONFIG_ATMEL_SSC=y CONFIG_EEPROM_AT24=y CONFIG_SCSI=y @@ -93,6 +90,7 @@ CONFIG_LIBERTAS_THINFIRM_USB=m CONFIG_MWIFIEX=m CONFIG_MWIFIEX_SDIO=m CONFIG_MWIFIEX_USB=m +CONFIG_WILC_SDIO=m CONFIG_RT2X00=m CONFIG_RT2500USB=m CONFIG_RT73USB=m @@ -138,7 +136,6 @@ CONFIG_REGULATOR_ACT8865=y CONFIG_REGULATOR_ACT8945A=y CONFIG_REGULATOR_PWM=m CONFIG_MEDIA_SUPPORT=y -CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_ATMEL_ISI=y CONFIG_DRM=y @@ -146,7 +143,6 @@ CONFIG_DRM_ATMEL_HLCDC=y CONFIG_DRM_PANEL_SIMPLE=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_BACKLIGHT_CLASS_DEVICE=y -# CONFIG_BACKLIGHT_GENERIC is not set CONFIG_BACKLIGHT_PWM=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_SOUND=y @@ -193,7 +189,6 @@ CONFIG_STAGING=y CONFIG_FB_TFT=m CONFIG_FB_TFT_HX8357D=m CONFIG_FB_TFT_ILI9341=m -CONFIG_WILC_SPI=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_IIO=y CONFIG_AT91_ADC=y @@ -225,6 +220,8 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_DEV_ATMEL_AES=y CONFIG_CRYPTO_DEV_ATMEL_TDES=y CONFIG_CRYPTO_DEV_ATMEL_SHA=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y CONFIG_CONSOLE_LOGLEVEL_DEFAULT=3 CONFIG_CONSOLE_LOGLEVEL_QUIET=3 CONFIG_MESSAGE_LOGLEVEL_DEFAULT=3 diff --git a/nerves_defconfig b/nerves_defconfig index a6c169a..6529b13 100644 --- a/nerves_defconfig +++ b/nerves_defconfig @@ -23,7 +23,7 @@ BR2_ROOTFS_POST_BUILD_SCRIPT="${NERVES_DEFCONFIG_DIR}/post-build.sh ${BR2_EXTERN BR2_ROOTFS_POST_IMAGE_SCRIPT="${NERVES_DEFCONFIG_DIR}/post-createfs.sh" BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_TARBALL=y -BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="$(call github,linux4sam,linux-at91,wilc_15_4_1)/wilc_15_4_1.tar.gz" +BR2_LINUX_KERNEL_CUSTOM_TARBALL_LOCATION="$(call github,linux4sam,linux-at91,linux4sam-2021.04)/linux4sam-2021.04.tar.gz" BR2_LINUX_KERNEL_PATCH="${NERVES_DEFCONFIG_DIR}/linux" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${NERVES_DEFCONFIG_DIR}/linux/linux-5.4.defconfig" From 3c978ae9225bd05681e92fd2eaab64ae1ce7897c Mon Sep 17 00:00:00 2001 From: Justin Schneck Date: Wed, 4 Aug 2021 17:32:00 -0400 Subject: [PATCH 12/12] v0.3.0 release --- CHANGELOG.md | 9 +++++++++ VERSION | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3fcc13..9833e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v0.3.0 + +This version bumps the toolchain to v1.4.3 which includes updates to GCC 10. + +* Dependencies + * [linux](https://github.com/linux4sam/linux-at91/releases/tag/linux4sam-2021.04) + * [nerves_system_br: v1.16.3](https://github.com/nerves-project/nerves_system_br/releases/tag/v1.16.3) + * [Erlang/OTP 24.0.4](https://erlang.org/download/OTP-24.0.4.README) + ## v0.2.0 The device tree and linux kernel are now loaded from the squashfs root file diff --git a/VERSION b/VERSION index 0ea3a94..0d91a54 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.3.0