fs/squashfs: new filesystem
Add support for SquashFS filesystem. Right now, it does not support compression but support for zlib will be added in a follow-up commit. Signed-off-by: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
This commit is contained in:
parent
550a9e7902
commit
c510061303
@ -969,6 +969,12 @@ S: Maintained
|
||||
F: drivers/spmi/
|
||||
F: include/spmi/
|
||||
|
||||
SQUASHFS
|
||||
M: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
|
||||
S: Maintained
|
||||
F: fs/squashfs/
|
||||
F: include/sqfs.h
|
||||
|
||||
TARGET_BCMNS3
|
||||
M: Bharat Gooty <bharat.gooty@broadcom.com>
|
||||
M: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
|
||||
|
@ -577,6 +577,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
|
||||
|
@ -22,4 +22,6 @@ source "fs/cramfs/Kconfig"
|
||||
|
||||
source "fs/yaffs2/Kconfig"
|
||||
|
||||
source "fs/squashfs/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -9,6 +9,7 @@ obj-$(CONFIG_FS_LOADER) += fs.o
|
||||
obj-$(CONFIG_SPL_FS_FAT) += fat/
|
||||
obj-$(CONFIG_SPL_FS_EXT4) += ext4/
|
||||
obj-$(CONFIG_SPL_FS_CBFS) += cbfs/
|
||||
obj-$(CONFIG_SPL_FS_SQUASHFS) += squashfs/
|
||||
else
|
||||
obj-y += fs.o
|
||||
|
||||
@ -23,5 +24,6 @@ obj-$(CONFIG_SANDBOX) += sandbox/
|
||||
obj-$(CONFIG_CMD_UBIFS) += ubifs/
|
||||
obj-$(CONFIG_YAFFS2) += yaffs2/
|
||||
obj-$(CONFIG_CMD_ZFS) += zfs/
|
||||
obj-$(CONFIG_FS_SQUASHFS) += squashfs/
|
||||
endif
|
||||
obj-y += fs_internal.o
|
||||
|
15
fs/fs.c
15
fs/fs.c
@ -22,6 +22,7 @@
|
||||
#include <div64.h>
|
||||
#include <linux/math64.h>
|
||||
#include <efi_loader.h>
|
||||
#include <squashfs.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
@ -276,6 +277,20 @@ 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",
|
||||
.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,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.fstype = FS_TYPE_ANY,
|
||||
|
10
fs/squashfs/Kconfig
Normal file
10
fs/squashfs/Kconfig
Normal file
@ -0,0 +1,10 @@
|
||||
config FS_SQUASHFS
|
||||
bool "Enable SquashFS filesystem support"
|
||||
help
|
||||
This provides support for reading images from SquashFS filesystem.
|
||||
Squashfs is a compressed read-only filesystem for Linux.
|
||||
It uses zlib, lz4, lzo, or xz compression to compress files, inodes
|
||||
and directories. Squashfs is intended for general read-only
|
||||
filesystem use, for archival use (i.e. in cases where a .tar.gz file
|
||||
may be used), and in constrained block device/memory systems (e.g.
|
||||
embedded systems) where low overhead is needed.
|
7
fs/squashfs/Makefile
Normal file
7
fs/squashfs/Makefile
Normal file
@ -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
|
1538
fs/squashfs/sqfs.c
Normal file
1538
fs/squashfs/sqfs.c
Normal file
File diff suppressed because it is too large
Load Diff
29
fs/squashfs/sqfs_decompressor.c
Normal file
29
fs/squashfs/sqfs_decompressor.c
Normal file
@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2020 Bootlin
|
||||
*
|
||||
* Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sqfs_decompressor.h"
|
||||
#include "sqfs_filesystem.h"
|
||||
#include "sqfs_utils.h"
|
||||
|
||||
int sqfs_decompress(u16 comp_type, void *dest, unsigned long *dest_len,
|
||||
void *source, u32 lenp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (comp_type) {
|
||||
default:
|
||||
printf("Error: unknown compression type.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
58
fs/squashfs/sqfs_decompressor.h
Normal file
58
fs/squashfs/sqfs_decompressor.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2020 Bootlin
|
||||
*
|
||||
* Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef SQFS_DECOMPRESSOR_H
|
||||
#define SQFS_DECOMPRESSOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SQFS_COMP_ZLIB 1
|
||||
#define SQFS_COMP_LZMA 2
|
||||
#define SQFS_COMP_LZO 3
|
||||
#define SQFS_COMP_XZ 4
|
||||
#define SQFS_COMP_LZ4 5
|
||||
#define SQFS_COMP_ZSTD 6
|
||||
|
||||
/* LZMA does not support any compression options */
|
||||
|
||||
struct squashfs_gzip_opts {
|
||||
u32 compression_level;
|
||||
u16 window_size;
|
||||
u16 strategies;
|
||||
};
|
||||
|
||||
struct squashfs_xz_opts {
|
||||
u32 dictionary_size;
|
||||
u32 executable_filters;
|
||||
};
|
||||
|
||||
struct squashfs_lz4_opts {
|
||||
u32 version;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct squashfs_zstd_opts {
|
||||
u32 compression_level;
|
||||
};
|
||||
|
||||
struct squashfs_lzo_opts {
|
||||
u32 algorithm;
|
||||
u32 level;
|
||||
};
|
||||
|
||||
union squashfs_compression_opts {
|
||||
struct squashfs_gzip_opts *gzip;
|
||||
struct squashfs_xz_opts *xz;
|
||||
struct squashfs_lz4_opts *lz4;
|
||||
struct squashfs_zstd_opts *zstd;
|
||||
struct squashfs_lzo_opts *lzo;
|
||||
};
|
||||
|
||||
int sqfs_decompress(u16 comp_type, void *dest, unsigned long *dest_len,
|
||||
void *source, u32 lenp);
|
||||
|
||||
#endif /* SQFS_DECOMPRESSOR_H */
|
91
fs/squashfs/sqfs_dir.c
Normal file
91
fs/squashfs/sqfs_dir.c
Normal file
@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2020 Bootlin
|
||||
*
|
||||
* Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/byteorder/little_endian.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sqfs_filesystem.h"
|
||||
#include "sqfs_utils.h"
|
||||
|
||||
bool sqfs_is_dir(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;
|
||||
u16 offset;
|
||||
int j;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
300
fs/squashfs/sqfs_filesystem.h
Normal file
300
fs/squashfs/sqfs_filesystem.h
Normal file
@ -0,0 +1,300 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2020 Bootlin
|
||||
*
|
||||
* Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef SQFS_FILESYSTEM_H
|
||||
#define SQFS_FILESYSTEM_H
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <stdint.h>
|
||||
#include <fs.h>
|
||||
|
||||
#define SQFS_UNCOMPRESSED_DATA 0x0002
|
||||
#define SQFS_MAGIC_NUMBER 0x73717368
|
||||
/* The three first members of squashfs_dir_index make a total of 12 bytes */
|
||||
#define SQFS_DIR_INDEX_BASE_LENGTH 12
|
||||
/* size of metadata (inode and directory) blocks */
|
||||
#define SQFS_METADATA_BLOCK_SIZE 8192
|
||||
/* Max. number of fragment entries in a metadata block is 512 */
|
||||
#define SQFS_MAX_ENTRIES 512
|
||||
/* Metadata blocks start by a 2-byte length header */
|
||||
#define SQFS_HEADER_SIZE 2
|
||||
#define SQFS_LREG_INODE_MIN_SIZE 56
|
||||
#define SQFS_DIR_HEADER_SIZE 12
|
||||
#define SQFS_MISC_ENTRY_TYPE -1
|
||||
#define SQFS_EMPTY_FILE_SIZE 3
|
||||
#define SQFS_STOP_READDIR 1
|
||||
#define SQFS_EMPTY_DIR -1
|
||||
/*
|
||||
* A directory entry object has a fixed length of 8 bytes, corresponding to its
|
||||
* first four members, plus the size of the entry name, which is equal to
|
||||
* 'entry_name' + 1 bytes.
|
||||
*/
|
||||
#define SQFS_ENTRY_BASE_LENGTH 8
|
||||
/* Inode types */
|
||||
#define SQFS_DIR_TYPE 1
|
||||
#define SQFS_REG_TYPE 2
|
||||
#define SQFS_SYMLINK_TYPE 3
|
||||
#define SQFS_BLKDEV_TYPE 4
|
||||
#define SQFS_CHRDEV_TYPE 5
|
||||
#define SQFS_FIFO_TYPE 6
|
||||
#define SQFS_SOCKET_TYPE 7
|
||||
#define SQFS_LDIR_TYPE 8
|
||||
#define SQFS_LREG_TYPE 9
|
||||
#define SQFS_LSYMLINK_TYPE 10
|
||||
#define SQFS_LBLKDEV_TYPE 11
|
||||
#define SQFS_LCHRDEV_TYPE 12
|
||||
#define SQFS_LFIFO_TYPE 13
|
||||
#define SQFS_LSOCKET_TYPE 14
|
||||
|
||||
struct squashfs_super_block {
|
||||
__le32 s_magic;
|
||||
__le32 inodes;
|
||||
__le32 mkfs_time;
|
||||
__le32 block_size;
|
||||
__le32 fragments;
|
||||
__le16 compression;
|
||||
__le16 block_log;
|
||||
__le16 flags;
|
||||
__le16 no_ids;
|
||||
__le16 s_major;
|
||||
__le16 s_minor;
|
||||
__le64 root_inode;
|
||||
__le64 bytes_used;
|
||||
__le64 id_table_start;
|
||||
__le64 xattr_id_table_start;
|
||||
__le64 inode_table_start;
|
||||
__le64 directory_table_start;
|
||||
__le64 fragment_table_start;
|
||||
__le64 export_table_start;
|
||||
};
|
||||
|
||||
struct squashfs_directory_index {
|
||||
u32 index;
|
||||
u32 start;
|
||||
u32 size;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
struct squashfs_base_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
};
|
||||
|
||||
struct squashfs_ipc_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le32 nlink;
|
||||
};
|
||||
|
||||
struct squashfs_lipc_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le32 nlink;
|
||||
__le32 xattr;
|
||||
};
|
||||
|
||||
struct squashfs_dev_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le32 nlink;
|
||||
__le32 rdev;
|
||||
};
|
||||
|
||||
struct squashfs_ldev_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le32 nlink;
|
||||
__le32 rdev;
|
||||
__le32 xattr;
|
||||
};
|
||||
|
||||
struct squashfs_symlink_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le32 nlink;
|
||||
__le32 symlink_size;
|
||||
char symlink[0];
|
||||
};
|
||||
|
||||
struct squashfs_reg_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le32 start_block;
|
||||
__le32 fragment;
|
||||
__le32 offset;
|
||||
__le32 file_size;
|
||||
__le32 block_list[0];
|
||||
};
|
||||
|
||||
struct squashfs_lreg_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le64 start_block;
|
||||
__le64 file_size;
|
||||
__le64 sparse;
|
||||
__le32 nlink;
|
||||
__le32 fragment;
|
||||
__le32 offset;
|
||||
__le32 xattr;
|
||||
__le32 block_list[0];
|
||||
};
|
||||
|
||||
struct squashfs_dir_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le32 start_block;
|
||||
__le32 nlink;
|
||||
__le16 file_size;
|
||||
__le16 offset;
|
||||
__le32 parent_inode;
|
||||
};
|
||||
|
||||
struct squashfs_ldir_inode {
|
||||
__le16 inode_type;
|
||||
__le16 mode;
|
||||
__le16 uid;
|
||||
__le16 guid;
|
||||
__le32 mtime;
|
||||
__le32 inode_number;
|
||||
__le32 nlink;
|
||||
__le32 file_size;
|
||||
__le32 start_block;
|
||||
__le32 parent_inode;
|
||||
__le16 i_count;
|
||||
__le16 offset;
|
||||
__le32 xattr;
|
||||
struct squashfs_directory_index index[0];
|
||||
};
|
||||
|
||||
union squashfs_inode {
|
||||
struct squashfs_base_inode *base;
|
||||
struct squashfs_dev_inode *dev;
|
||||
struct squashfs_ldev_inode *ldev;
|
||||
struct squashfs_symlink_inode *symlink;
|
||||
struct squashfs_reg_inode *reg;
|
||||
struct squashfs_lreg_inode *lreg;
|
||||
struct squashfs_dir_inode *dir;
|
||||
struct squashfs_ldir_inode *ldir;
|
||||
struct squashfs_ipc_inode *ipc;
|
||||
struct squashfs_lipc_inode *lipc;
|
||||
};
|
||||
|
||||
struct squashfs_directory_entry {
|
||||
u16 offset;
|
||||
u16 inode_offset;
|
||||
u16 type;
|
||||
u16 name_size;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
struct squashfs_directory_header {
|
||||
u32 count;
|
||||
u32 start;
|
||||
u32 inode_number;
|
||||
};
|
||||
|
||||
struct squashfs_fragment_block_entry {
|
||||
u64 start;
|
||||
u32 size;
|
||||
u32 _unused;
|
||||
};
|
||||
|
||||
struct squashfs_dir_stream {
|
||||
struct fs_dir_stream fs_dirs;
|
||||
struct fs_dirent dentp;
|
||||
/*
|
||||
* 'size' is the uncompressed size of the entire listing, including
|
||||
* headers. 'entry_count' is the number of entries following a
|
||||
* specific header. Both variables are decremented in sqfs_readdir() so
|
||||
* the function knows when the end of the directory is reached.
|
||||
*/
|
||||
size_t size;
|
||||
int entry_count;
|
||||
/* SquashFS structures */
|
||||
struct squashfs_directory_header *dir_header;
|
||||
struct squashfs_directory_entry *entry;
|
||||
/*
|
||||
* 'table' points to a position into the directory table. Both 'table'
|
||||
* and 'inode' are defined for the first time in sqfs_opendir().
|
||||
* 'table's value changes in sqfs_readdir().
|
||||
*/
|
||||
unsigned char *table;
|
||||
union squashfs_inode i;
|
||||
struct squashfs_dir_inode i_dir;
|
||||
struct squashfs_ldir_inode i_ldir;
|
||||
/*
|
||||
* References to the tables' beginnings. They are assigned in
|
||||
* sqfs_opendir() and freed in sqfs_closedir().
|
||||
*/
|
||||
unsigned char *inode_table;
|
||||
unsigned char *dir_table;
|
||||
};
|
||||
|
||||
struct squashfs_file_info {
|
||||
/* File size in bytes (uncompressed) */
|
||||
size_t size;
|
||||
/* Reference to list of data blocks's sizes */
|
||||
u32 *blk_sizes;
|
||||
/* Offset into the fragment block */
|
||||
u32 offset;
|
||||
/* Offset in which the data blocks begin */
|
||||
u64 start;
|
||||
/* Is file fragmented? */
|
||||
bool frag;
|
||||
/* Compressed fragment */
|
||||
bool comp;
|
||||
};
|
||||
|
||||
void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count,
|
||||
__le32 block_size);
|
||||
|
||||
int sqfs_dir_offset(void *dir_i, u32 *m_list, int m_count);
|
||||
|
||||
int sqfs_read_metablock(unsigned char *file_mapping, int offset,
|
||||
bool *compressed, u32 *data_size);
|
||||
|
||||
bool sqfs_is_empty_dir(void *dir_i);
|
||||
|
||||
bool sqfs_is_dir(u16 type);
|
||||
|
||||
#endif /* SQFS_FILESYSTEM_H */
|
155
fs/squashfs/sqfs_inode.c
Normal file
155
fs/squashfs/sqfs_inode.c
Normal file
@ -0,0 +1,155 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2020 Bootlin
|
||||
*
|
||||
* Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sqfs_decompressor.h"
|
||||
#include "sqfs_filesystem.h"
|
||||
#include "sqfs_utils.h"
|
||||
|
||||
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 + 1) {
|
||||
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 + 1) * 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)
|
||||
{
|
||||
unsigned char *data;
|
||||
u16 header;
|
||||
|
||||
data = file_mapping + offset;
|
||||
header = get_unaligned((u16 *)data);
|
||||
*compressed = SQFS_COMPRESSED_METADATA(header);
|
||||
*data_size = SQFS_METADATA_SIZE(header);
|
||||
|
||||
if (*data_size > SQFS_METADATA_BLOCK_SIZE) {
|
||||
printf("Invalid metatada block size: %d bytes.\n", *data_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
49
fs/squashfs/sqfs_utils.h
Normal file
49
fs/squashfs/sqfs_utils.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2020 Bootlin
|
||||
*
|
||||
* Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef SQFS_UTILS_H
|
||||
#define SQFS_UTILS_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SQFS_FRAGMENT_INDEX_OFFSET(A) ((A) % SQFS_MAX_ENTRIES)
|
||||
#define SQFS_FRAGMENT_INDEX(A) ((A) / SQFS_MAX_ENTRIES)
|
||||
#define SQFS_BLOCK_SIZE(A) ((A) & GENMASK(23, 0))
|
||||
#define SQFS_CHECK_FLAG(flag, bit) (((flag) >> (bit)) & 1)
|
||||
/* Useful for both fragment and data blocks */
|
||||
#define SQFS_COMPRESSED_BLOCK(A) (!((A) & BIT(24)))
|
||||
/* SQFS_COMPRESSED_DATA strictly used with super block's 'flags' member */
|
||||
#define SQFS_COMPRESSED_DATA(A) (!((A) & 0x0002))
|
||||
#define SQFS_IS_FRAGMENTED(A) ((A) != 0xFFFFFFFF)
|
||||
/*
|
||||
* These two macros work as getters for a metada block header, retrieving the
|
||||
* data size and if it is compressed/uncompressed
|
||||
*/
|
||||
#define SQFS_COMPRESSED_METADATA(A) (!((A) & BIT(15)))
|
||||
#define SQFS_METADATA_SIZE(A) ((A) & GENMASK(14, 0))
|
||||
|
||||
struct squashfs_super_block_flags {
|
||||
/* check: unused
|
||||
* uncompressed_ids: not supported
|
||||
*/
|
||||
bool uncompressed_inodes;
|
||||
bool uncompressed_data;
|
||||
bool check;
|
||||
bool uncompressed_frags;
|
||||
bool no_frags;
|
||||
bool always_frags;
|
||||
bool duplicates;
|
||||
bool exportable;
|
||||
bool uncompressed_xattrs;
|
||||
bool no_xattrs;
|
||||
bool compressor_options;
|
||||
bool uncompressed_ids;
|
||||
};
|
||||
|
||||
#endif /* SQFS_UTILS_H */
|
@ -15,6 +15,7 @@ struct cmd_tbl;
|
||||
#define FS_TYPE_SANDBOX 3
|
||||
#define FS_TYPE_UBIFS 4
|
||||
#define FS_TYPE_BTRFS 5
|
||||
#define FS_TYPE_SQUASHFS 6
|
||||
|
||||
struct blk_desc;
|
||||
|
||||
|
25
include/squashfs.h
Normal file
25
include/squashfs.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2020 Bootlin
|
||||
*
|
||||
* Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
|
||||
*
|
||||
* squashfs.h: SquashFS filesystem implementation.
|
||||
*/
|
||||
|
||||
#ifndef _SQFS_H_
|
||||
#define _SQFS_H_
|
||||
|
||||
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);
|
||||
void sqfs_close(void);
|
||||
void sqfs_closedir(struct fs_dir_stream *dirs);
|
||||
|
||||
#endif /* SQFS_H */
|
Loading…
Reference in New Issue
Block a user