Page MenuHomeFreeBSD

D50841.id157017.diff
No OneTemporary

D50841.id157017.diff

diff --git a/include/Makefile b/include/Makefile
--- a/include/Makefile
+++ b/include/Makefile
@@ -55,7 +55,7 @@
dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/pwm \
dev/smbus dev/speaker dev/tcp_log dev/veriexec dev/vkbd dev/wg \
fs/devfs fs/fdescfs fs/msdosfs fs/nfs fs/nullfs \
- fs/procfs fs/smbfs fs/udf fs/unionfs \
+ fs/procfs fs/smbfs fs/squashfs fs/udf fs/unionfs \
geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \
geom/mirror geom/mountver geom/multipath geom/nop \
geom/raid geom/raid3 geom/shsec geom/stripe geom/union geom/virstor \
diff --git a/share/man/man4/squashfs.4 b/share/man/man4/squashfs.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/squashfs.4
@@ -0,0 +1,87 @@
+.\"-
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2025 Chuck Tuffli
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd June 12, 2025
+.Dt SQUASHFS 5
+.Os
+.Sh NAME
+.Nm squashfs
+.Nd squash filesystem
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following line in
+your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "options SQUASHFS"
+.Ed
+.Pp
+Alternatively, to load the driver as a module at boot time, place the
+following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+squashfs_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for mounting and accessing SquashFS compressed
+file system images on FreeBSD. SquashFS is a compressed read-only file
+system designed for general read-only file system use and in constrained
+block device/memory systems.
+.Sh EXAMPLES
+Mount the SquashFS image
+.Ar image.sqfs
+:
+.Bd -literal -offset indent
+# mount -t squashfs image.sqfs /mnt
+.Ed
+.Sh SEE ALSO
+.Xr gzip 1 ,
+.Xr xz 1 ,
+.Xr zstd 1 ,
+.Xr mount 8 ,
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was developed by
+.An Raghav Sharma Aq Mt raghav@FreeBSD.org
+and enhanced by
+.An Kyle Evans Aq Mt kevans@FreeBSD.org
+as a Google Summer of Code project.
+
+This manual page was written by
+.An Chuck Tuffli Aq Mt chuck@FreeBSD.org
+.Sh BUGS
+The squashfs format allows several compression formats the driver does
+not support including
+.Bl -bullet -compact -width indent
+.It
+LZMA
+.It
+LZO
+.It
+LZ4
+.El
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3639,6 +3639,13 @@
fs/tmpfs/tmpfs_fifoops.c optional tmpfs
fs/tmpfs/tmpfs_vfsops.c optional tmpfs
fs/tmpfs/tmpfs_subr.c optional tmpfs
+fs/squashfs/squashfs_vfsops.c optional squashfs
+fs/squashfs/squashfs_vnops.c optional squashfs
+fs/squashfs/squashfs_decompressor.c optional squashfs
+fs/squashfs/squashfs_block.c optional squashfs
+fs/squashfs/squashfs_init.c optional squashfs
+fs/squashfs/squashfs_inode.c optional squashfs
+fs/squashfs/squashfs_io.c optional squashfs
gdb/gdb_cons.c optional gdb
gdb/gdb_main.c optional gdb
gdb/gdb_packet.c optional gdb
diff --git a/sys/conf/options b/sys/conf/options
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -277,6 +277,7 @@
PROCFS opt_dontuse.h
PSEUDOFS opt_dontuse.h
SMBFS opt_dontuse.h
+SQUASHFS opt_dontuse.h
TARFS opt_dontuse.h
TMPFS opt_dontuse.h
UDF opt_dontuse.h
@@ -287,6 +288,9 @@
# Pseudofs debugging
PSEUDOFS_TRACE opt_pseudofs.h
+# squashfs debugging
+SQUASHFS_DEBUG opt_squashfs.h
+
# Tarfs debugging
TARFS_DEBUG opt_tarfs.h
diff --git a/sys/fs/squashfs/squashfs.h b/sys/fs/squashfs/squashfs.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs.h
@@ -0,0 +1,358 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef SQUASHFS_H
+#define SQUASHFS_H
+
+#include <sys/kassert.h>
+
+#ifdef _KERNEL
+#define SQUASHFS_MALLOC(sz, type, flags) malloc(sz, type, flags)
+#define SQUASHFS_FREE(obj, type) free(obj, type)
+#else
+#define SQUASHFS_MALLOC(sz, type, flags) malloc(sz)
+#define SQUASHFS_FREE(obj, type) free(obj)
+#endif /* _KERNEL */
+
+#define SQUASHFS_MAGIC 0x73717368
+#define SQUASHFS_MAGIC_SWAP 0x68737173
+
+#define SQUASHFS_CACHED_FRAGMENTS 3
+#define SQUASHFS_MAJOR 4
+#define SQUASHFS_MINOR 0
+#define SQUASHFS_START 0
+
+#define SQUASHFS_METADATA_SIZE 8192
+#define SQUASHFS_METADATA_LOG 13
+
+#define SQUASHFS_FILE_SIZE 131072
+#define SQUASHFS_FILE_LOG 17
+
+#define SQUASHFS_FILE_MAX_SIZE 1048576
+#define SQUASHFS_FILE_MAX_LOG 20
+
+#define SQUASHFS_IDS 65536
+
+#define SQUASHFS_MAX_NAME_LEN 256
+
+#define SQUASHFS_INVALID_FRAG (0xffffffffU)
+#define SQUASHFS_INVALID_XATTR (0xffffffffU)
+#define SQUASHFS_INVALID_BLK ((int64_t)-1)
+
+#define SQUASHFS_COOKIE_DOT 0
+#define SQUASHFS_COOKIE_DOTDOT 1
+#define SQUASHFS_COOKIE_EOF ~((off_t)1 << (sizeof(off_t) * 8 - 1))
+
+#define SQUASHFS_TYPE_MIN_VALID 1
+#define SQUASHFS_TYPE_MAX_VALID 14
+#define SQUASHFS_INODE_MIN_COUNT 1
+
+/* Filesystem flags */
+#define SQUASHFS_NOI 0
+#define SQUASHFS_NOD 1
+#define SQUASHFS_NOF 3
+#define SQUASHFS_NO_FRAG 4
+#define SQUASHFS_ALWAYS_FRAG 5
+#define SQUASHFS_DUPLICATE 6
+#define SQUASHFS_EXPORT 7
+#define SQUASHFS_COMP_OPT 10
+
+/* Max number of types and file types */
+#define SQUASHFS_DIR_TYPE 1
+#define SQUASHFS_REG_TYPE 2
+#define SQUASHFS_SYMLINK_TYPE 3
+#define SQUASHFS_BLKDEV_TYPE 4
+#define SQUASHFS_CHRDEV_TYPE 5
+#define SQUASHFS_FIFO_TYPE 6
+#define SQUASHFS_SOCKET_TYPE 7
+#define SQUASHFS_LDIR_TYPE 8
+#define SQUASHFS_LREG_TYPE 9
+#define SQUASHFS_LSYMLINK_TYPE 10
+#define SQUASHFS_LBLKDEV_TYPE 11
+#define SQUASHFS_LCHRDEV_TYPE 12
+#define SQUASHFS_LFIFO_TYPE 13
+#define SQUASHFS_LSOCKET_TYPE 14
+
+#define SQUASHFS_COMPRESSED_BIT (1 << 15)
+#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
+
+/* cached data constants for filesystem */
+#define SQUASHFS_CACHED_BLKS 8
+
+#define SQUASHFS_MAX_FILE_SIZE_LOG 64
+
+#define SQUASHFS_MAX_FILE_SIZE (1LL << (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
+
+/* meta index cache */
+#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
+#define SQUASHFS_META_ENTRIES 127
+#define SQUASHFS_META_SLOTS 8
+
+/* definitions for structures on disk */
+#define ZLIB_COMPRESSION 1
+#define LZMA_COMPRESSION 2
+#define LZO_COMPRESSION 3
+#define XZ_COMPRESSION 4
+#define LZ4_COMPRESSION 5
+#define ZSTD_COMPRESSION 6
+
+/* Xattr types */
+#define SQUASHFS_XATTR_USER 0
+#define SQUASHFS_XATTR_TRUSTED 1
+#define SQUASHFS_XATTR_SECURITY 2
+#define SQUASHFS_XATTR_VALUE_OOL 256
+#define SQUASHFS_XATTR_PREFIX_MASK 0xff
+#define SQFS_XATTR_PREFIX_MAX SQUASHFS_XATTR_SECURITY
+
+struct sqsh_sb {
+ uint32_t s_magic;
+ uint32_t inodes;
+ uint32_t mkfs_time;
+ uint32_t block_size;
+ uint32_t fragments;
+ uint16_t compression;
+ uint16_t block_log;
+ uint16_t flags;
+ uint16_t no_ids;
+ uint16_t s_major;
+ uint16_t s_minor;
+ uint64_t root_inode;
+ uint64_t bytes_used;
+ uint64_t id_table_start;
+ uint64_t xattr_id_table_start;
+ uint64_t inode_table_start;
+ uint64_t directory_table_start;
+ uint64_t fragment_table_start;
+ uint64_t lookup_table_start;
+};
+
+struct sqsh_dir_index {
+ uint32_t index;
+ uint32_t start_block;
+ uint32_t size;
+};
+
+struct sqsh_base_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+};
+
+struct sqsh_ipc_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint32_t nlink;
+};
+
+struct sqsh_lipc_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint32_t nlink;
+ uint32_t xattr;
+};
+
+struct sqsh_dev_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint32_t nlink;
+ uint32_t rdev;
+};
+
+struct sqsh_ldev_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint32_t nlink;
+ uint32_t rdev;
+ uint32_t xattr;
+};
+
+struct sqsh_symlink_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint32_t nlink;
+ uint32_t symlink_size;
+};
+
+struct sqsh_reg_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint32_t start_block;
+ uint32_t fragment;
+ uint32_t offset;
+ uint32_t file_size;
+};
+
+struct sqsh_lreg_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint64_t start_block;
+ uint64_t file_size;
+ uint64_t sparse;
+ uint32_t nlink;
+ uint32_t fragment;
+ uint32_t offset;
+ uint32_t xattr;
+};
+
+struct sqsh_dir_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint32_t start_block;
+ uint32_t nlink;
+ uint16_t file_size;
+ uint16_t offset;
+ uint32_t parent_inode;
+};
+
+struct sqsh_ldir_inode {
+ uint16_t inode_type;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t guid;
+ uint32_t mtime;
+ uint32_t inode_number;
+ uint32_t nlink;
+ uint32_t file_size;
+ uint32_t start_block;
+ uint32_t parent_inode;
+ uint16_t i_count;
+ uint16_t offset;
+ uint32_t xattr;
+};
+
+struct squashfs_dir_entry {
+ uint16_t offset;
+ uint16_t inode_number;
+ uint16_t type;
+ uint16_t size;
+};
+
+struct sqsh_dir_header {
+ uint32_t count;
+ uint32_t start_block;
+ uint32_t inode_number;
+};
+
+struct sqsh_fragment_entry {
+ uint64_t start_block;
+ uint32_t size;
+ uint32_t unused;
+};
+
+struct sqsh_xattr_entry {
+ uint16_t type;
+ uint16_t size;
+};
+
+struct sqsh_xattr_val {
+ uint32_t vsize;
+};
+
+struct sqsh_xattr_id {
+ uint64_t xattr;
+ uint32_t count;
+ uint32_t size;
+};
+
+struct sqsh_xattr_id_table {
+ uint64_t xattr_table_start;
+ uint32_t xattr_ids;
+ uint32_t unused;
+};
+
+/* This struct handles tables which contains inodes, inode ID and fragments */
+struct sqsh_table {
+ size_t each;
+ uint64_t *blocks;
+};
+
+/* This overlays the fid structure (see mount.h) */
+struct sqsh_fid {
+ uint16_t len;
+ uint16_t pad;
+ ino_t ino;
+ uint32_t gen;
+};
+
+extern struct vop_vector squashfs_vnodeops;
+
+#ifdef SQUASHFS_DEBUG
+#define TRACE(x...) printf("\n\033[0;34msquashfs:\33[0m " x)
+#else /* !SQUASHFS_DEBUG */
+#define TRACE(x...)
+#endif /* SQUASHFS_DEBUG */
+#define ERROR(x...) printf("\n\033[0;31msquashfs:\33[0m " x)
+
+typedef enum {
+ SQFS_OK, /* everything fine */
+ SQFS_BADFORMAT, /* unsupported file format */
+ SQFS_BADVERSION, /* unsupported squashfs version */
+ SQFS_BADCOMP, /* unsupported compression method */
+ SQFS_UNSUP, /* unsupported feature */
+ SQFS_END_OF_DIRECTORY, /* reached end of directory */
+ SQFS_ERR /* error in operation */
+} sqsh_err;
+
+#endif /* SQUASHFS_H */
diff --git a/sys/fs/squashfs/squashfs_block.h b/sys/fs/squashfs/squashfs_block.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_block.h
@@ -0,0 +1,80 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef SQUASHFS_BLOCK_H
+#define SQUASHFS_BLOCK_H
+
+struct sqsh_block {
+ size_t size;
+ void *data;
+};
+
+struct sqsh_block_run {
+ off_t block;
+ size_t offset;
+};
+
+struct sqsh_dir {
+ struct sqsh_block_run cur;
+ off_t offset;
+ off_t total;
+ struct sqsh_dir_header header;
+};
+
+struct sqsh_dir_entry {
+ uint64_t inode_id;
+ uint32_t inode_number;
+ char name[100];
+ size_t name_size;
+ off_t offset;
+ off_t next_offset;
+};
+
+/* Helper functions to check if metadata/data block is compressed and its size
+ */
+void sqsh_metadata_header(uint16_t hdr, bool *compressed, uint16_t *size);
+void sqsh_data_header(uint32_t hdr, bool *compressed, uint32_t *size);
+
+sqsh_err sqsh_block_read(struct sqsh_mount *ump, off_t pos, bool compressed,
+ uint32_t size, size_t outsize, struct sqsh_block **block);
+void sqsh_free_block(struct sqsh_block *block);
+
+sqsh_err sqsh_metadata_read(struct sqsh_mount *ump, off_t pos,
+ size_t *data_size, struct sqsh_block **block);
+sqsh_err sqsh_data_read(struct sqsh_mount *ump, off_t pos, uint32_t hdr,
+ struct sqsh_block **block);
+
+sqsh_err sqsh_metadata_get(struct sqsh_mount *ump, struct sqsh_block_run *cur,
+ void *buf, size_t size);
+
+/* Number of groups of size "group" required to hold size "total" */
+size_t sqsh_ceil(uint64_t total, size_t group);
+
+#endif
\ No newline at end of file
diff --git a/sys/fs/squashfs/squashfs_block.c b/sys/fs/squashfs/squashfs_block.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_block.c
@@ -0,0 +1,239 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+
+#include <sys/systm.h>
+#ifdef _KERNEL
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+#else /* !_KERNEL */
+#include <stdlib.h>
+#endif
+
+#include <squashfs.h>
+#include <squashfs_io.h>
+#include <squashfs_mount.h>
+#include <squashfs_inode.h>
+#include <squashfs_decompressor.h>
+#include <squashfs_block.h>
+
+#ifdef _KERNEL
+static MALLOC_DEFINE(M_SQUASHFSBLOCK, "SQUASHFS block",
+ "SQUASHFS block structure");
+static MALLOC_DEFINE(M_SQUASHFSBLOCKD, "SQUASHFS block data",
+ "SQUASHFS block data buffer");
+#endif
+
+void
+sqsh_metadata_header(uint16_t hdr, bool *compressed, uint16_t *size)
+{
+ /* Bit is set if block is uncompressed */
+ *compressed = !(hdr & SQUASHFS_COMPRESSED_BIT);
+ *size = hdr & ~SQUASHFS_COMPRESSED_BIT;
+ if (!*size)
+ *size = SQUASHFS_COMPRESSED_BIT;
+}
+
+void
+sqsh_data_header(uint32_t hdr, bool *compressed, uint32_t *size)
+{
+ *compressed = !(hdr & SQUASHFS_COMPRESSED_BIT_BLOCK);
+ *size = hdr & ~SQUASHFS_COMPRESSED_BIT_BLOCK;
+}
+
+sqsh_err
+sqsh_block_read(struct sqsh_mount *ump, off_t pos, bool compressed,
+ uint32_t size, size_t outsize, struct sqsh_block **block)
+{
+ sqsh_err err;
+ /* allocate block on heap */
+ *block = SQUASHFS_MALLOC(sizeof(**block), M_SQUASHFSBLOCK, M_WAITOK);
+ if (*block == NULL)
+ return (SQFS_ERR);
+ (*block)->data = SQUASHFS_MALLOC(size, M_SQUASHFSBLOCKD, M_WAITOK);
+ if ((*block)->data == NULL)
+ goto error;
+
+ if (sqsh_io_read_buf(ump, (*block)->data, pos, size) != size) {
+ ERROR("Failed to read data, I/O error");
+ goto error;
+ }
+
+ /* if block is compressed, first decompressed it and then initialize
+ * block */
+ if (compressed) {
+ char *decomp = SQUASHFS_MALLOC(outsize, M_SQUASHFSBLOCKD,
+ M_WAITOK);
+ if (decomp == NULL)
+ goto error;
+
+ err = ump->decompressor->decompressor((*block)->data, size,
+ decomp, &outsize);
+ if (err != SQFS_OK) {
+ SQUASHFS_FREE(decomp, M_SQUASHFSBLOCKD);
+ goto error;
+ }
+ SQUASHFS_FREE((*block)->data, M_SQUASHFSBLOCKD);
+ (*block)->data = decomp;
+ (*block)->size = outsize;
+ } else {
+ (*block)->size = size;
+ }
+
+ return (SQFS_OK);
+
+error:
+ sqsh_free_block(*block);
+ *block = NULL;
+ return (SQFS_ERR);
+}
+
+void
+sqsh_free_block(struct sqsh_block *block)
+{
+ SQUASHFS_FREE(block->data, M_SQUASHFSBLOCKD);
+ SQUASHFS_FREE(block, M_SQUASHFSBLOCK);
+}
+
+sqsh_err
+sqsh_metadata_read(struct sqsh_mount *ump, off_t pos, size_t *data_size,
+ struct sqsh_block **block)
+{
+ uint16_t hdr;
+ bool compressed;
+ uint16_t size;
+ sqsh_err err;
+
+ *data_size = 0;
+
+ if (sqsh_io_read_buf(ump, &hdr, pos, sizeof(hdr)) != sizeof(hdr)) {
+ ERROR("Failed to read data, I/O error");
+ return (SQFS_ERR);
+ }
+
+ pos += sizeof(hdr);
+ *data_size += sizeof(hdr);
+ hdr = le16toh(hdr);
+
+ sqsh_metadata_header(hdr, &compressed, &size);
+
+ err = sqsh_block_read(ump, pos, compressed, size,
+ SQUASHFS_METADATA_SIZE, block);
+ *data_size += size;
+
+ return (err);
+}
+
+sqsh_err
+sqsh_data_read(struct sqsh_mount *ump, off_t pos, uint32_t hdr,
+ struct sqsh_block **block)
+{
+ bool compressed;
+ uint32_t size;
+ sqsh_err err;
+
+ sqsh_data_header(hdr, &compressed, &size);
+ err = sqsh_block_read(ump, pos, compressed, size, ump->sb.block_size,
+ block);
+
+ return (err);
+}
+
+sqsh_err
+sqsh_metadata_get(struct sqsh_mount *ump, struct sqsh_block_run *cur, void *buf,
+ size_t size)
+{
+ off_t pos;
+
+ pos = cur->block;
+ while (size > 0) {
+ struct sqsh_block *block;
+ size_t take;
+ size_t data_size;
+ sqsh_err err;
+
+ data_size = 0;
+ err = sqsh_metadata_read(ump, pos, &data_size, &block);
+ if (err != SQFS_OK)
+ return (err);
+
+ pos += data_size;
+
+ take = block->size - cur->offset;
+ if (take > size)
+ take = size;
+ if (buf)
+ memcpy(buf, (char *)block->data + cur->offset, take);
+
+ if (buf)
+ buf = (char *)buf + take;
+ size -= take;
+ cur->offset += take;
+ if (cur->offset == block->size) {
+ cur->block = pos;
+ cur->offset = 0;
+ }
+
+ /* Free block since currently we have no cache */
+ sqsh_free_block(block);
+ }
+ return (SQFS_OK);
+}
+
+/* This is a normal ceil function */
+size_t
+sqsh_ceil(uint64_t total, size_t group)
+{
+ size_t ans;
+
+ ans = (size_t)(total / group);
+ if (total % group)
+ ans += 1;
+
+ return (ans);
+}
diff --git a/sys/fs/squashfs/squashfs_decompressor.h b/sys/fs/squashfs/squashfs_decompressor.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_decompressor.h
@@ -0,0 +1,44 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef SQUASHFS_DECOMPRESSOR_H
+#define SQUASHFS_DECOMPRESSOR_H
+
+struct sqsh_decompressor {
+ sqsh_err (*decompressor)(void *input, size_t input_size, void *output,
+ size_t *output_size);
+
+ int id;
+ char *name;
+};
+
+const struct sqsh_decompressor *sqsh_lookup_decompressor(int id);
+
+#endif /* SQUASHFS_DECOMPRESSOR_H */
diff --git a/sys/fs/squashfs/squashfs_decompressor.c b/sys/fs/squashfs/squashfs_decompressor.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_decompressor.c
@@ -0,0 +1,189 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_squashfs.h"
+
+#ifdef _KERNEL
+#include "opt_gzio.h"
+#include "opt_zstdio.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+
+#include <squashfs.h>
+#include <squashfs_mount.h>
+#include <squashfs_decompressor.h>
+
+#ifdef GZIO
+#include <contrib/zlib/zlib.h>
+#endif
+#include <contrib/xz-embedded/linux/include/linux/xz.h>
+#ifdef ZSTDIO
+#define ZSTD_STATIC_LINKING_ONLY
+#include <contrib/zstd/lib/zstd.h>
+#endif
+
+/* Support for zlib compressor */
+#ifdef GZIO
+static sqsh_err
+zlib_decompressor(void *input, size_t input_size, void *output,
+ size_t *output_size)
+{
+ uLongf zout;
+ int zerr;
+
+ zout = *output_size;
+ zerr = uncompress((Bytef *)output, &zout, input, input_size);
+ if (zerr != Z_OK)
+ return (SQFS_ERR);
+ *output_size = zout;
+ return (SQFS_OK);
+}
+#endif /* GZIO */
+
+#ifdef _KERNEL
+static sqsh_err
+xz_decompressor(void *input, size_t input_size, void *output,
+ size_t *output_size)
+{
+ static struct xz_dec *sqsh_xz_dec;
+ struct xz_buf xzb;
+ enum xz_ret ret;
+
+ /* XXX FIXME: Need per-mount */
+ if (sqsh_xz_dec == NULL) {
+ sqsh_xz_dec = xz_dec_init(XZ_SINGLE, 0);
+ if (sqsh_xz_dec == NULL)
+ return (SQFS_ERR);
+ }
+
+ xzb.in = input;
+ xzb.in_pos = 0;
+ xzb.in_size = input_size;
+
+ xzb.out = output;
+ xzb.out_pos = 0;
+ xzb.out_size = *output_size;
+
+ ret = xz_dec_run(sqsh_xz_dec, &xzb);
+ if (ret != XZ_STREAM_END)
+ return (SQFS_ERR);
+
+ *output_size = xzb.out_size;
+ return (SQFS_OK);
+}
+#endif
+
+#ifdef ZSTDIO
+static sqsh_err
+zstd_decompressor(void *input, size_t input_size, void *output,
+ size_t *output_size)
+{
+ size_t zstdout;
+
+ zstdout = ZSTD_decompress(output, *output_size, input, input_size);
+ if (ZSTD_isError(zstdout))
+ return (SQFS_ERR);
+ *output_size = zstdout;
+ return (SQFS_OK);
+}
+#endif /* ZSTDIO */
+
+static const struct sqsh_decompressor decompressors[] = {
+ {
+#ifdef GZIO
+ .decompressor = zlib_decompressor,
+#endif
+ .id = ZLIB_COMPRESSION,
+ .name = "zlib",
+ },
+ {
+ .decompressor = NULL,
+ .id = LZMA_COMPRESSION,
+ .name = "lzma",
+ },
+ {
+ .decompressor = NULL,
+ .id = LZO_COMPRESSION,
+ .name = "lzo",
+ },
+ {
+#ifdef _KERNEL
+ .decompressor = xz_decompressor,
+#endif
+ .id = XZ_COMPRESSION,
+ .name = "xz",
+ },
+ {
+ .decompressor = NULL,
+ .id = LZ4_COMPRESSION,
+ .name = "lz4",
+ },
+ {
+#ifdef ZSTDIO
+ .decompressor = zstd_decompressor,
+#endif
+ .id = ZSTD_COMPRESSION,
+ .name = "zstd",
+ },
+};
+
+const struct sqsh_decompressor *
+sqsh_lookup_decompressor(int id)
+{
+ const struct sqsh_decompressor *decom;
+
+ for (size_t i = 0; i < nitems(decompressors); i++) {
+ decom = &decompressors[i];
+
+ if (id == decom->id)
+ return (decom);
+ }
+
+ return (NULL);
+}
diff --git a/sys/fs/squashfs/squashfs_dir.h b/sys/fs/squashfs/squashfs_dir.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_dir.h
@@ -0,0 +1,61 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef SQUASHFS_DIR_H
+#define SQUASHFS_DIR_H
+
+/* Helper for sqsh_dir_lookup */
+struct sqsh_dir_ff_name_t {
+ const char *cmp;
+ size_t cmplen;
+ char *name;
+};
+
+/* Initialise directory from inode */
+sqsh_err sqsh_dir_init(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_dir *dir);
+
+/* Directory indexing helper functions */
+sqsh_err sqsh_dir_f_header(struct sqsh_mount *ump, struct sqsh_block_run *cur,
+ struct sqsh_dir_index *idx, bool *stop, void *arg);
+sqsh_err sqsh_dir_ff_header(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_dir *dir, void *arg);
+
+sqsh_err sqsh_dir_metadata_read(struct sqsh_mount *mnt, struct sqsh_dir *dir,
+ void *buf, size_t size);
+
+/* Directory traverse helper functions for vnops readdir and lookup */
+sqsh_err sqsh_dir_getnext(struct sqsh_mount *ump, struct sqsh_dir *dir,
+ struct sqsh_dir_entry *entry);
+sqsh_err sqsh_dir_lookup(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ const char *name, size_t namelen, struct sqsh_dir_entry *entry,
+ bool *found);
+
+#endif /* SQUASHFS_DIR_H */
\ No newline at end of file
diff --git a/sys/fs/squashfs/squashfs_dir.c b/sys/fs/squashfs/squashfs_dir.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_dir.c
@@ -0,0 +1,266 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+#else
+#include <string.h>
+#endif
+
+#include <squashfs.h>
+#include <squashfs_io.h>
+#include <squashfs_mount.h>
+#include <squashfs_inode.h>
+#include <squashfs_block.h>
+#include <squashfs_dir.h>
+
+void swapendian_dir_header(struct sqsh_dir_header *hdr);
+void swapendian_dir_index(struct sqsh_dir_index *idx);
+void swapendian_dir_entry(struct squashfs_dir_entry *entry);
+
+sqsh_err
+sqsh_dir_init(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_dir *dir)
+{
+ memset(dir, 0, sizeof(*dir));
+ dir->cur.block = inode->xtra.dir.start_block +
+ ump->sb.directory_table_start;
+ dir->cur.offset = inode->xtra.dir.offset;
+ dir->offset = 0;
+
+ /*
+ * For better compression '.' and '..' entries
+ * are not there in squashfs but Inode entires
+ * does keep directory count including them.
+ * Here we are just chekcing for that and updating
+ * dir count accordingly.
+ */
+ dir->total = inode->size <= 3 ? 0 : inode->size - 3;
+
+ return SQFS_OK;
+}
+
+sqsh_err
+sqsh_dir_f_header(struct sqsh_mount *ump, struct sqsh_block_run *cur,
+ struct sqsh_dir_index *idx, bool *stop, void *arg)
+{
+ off_t offset = *(off_t *)arg;
+
+ if (idx->index >= offset) {
+ *stop = true;
+ return SQFS_OK;
+ }
+
+ return sqsh_metadata_get(ump, cur, NULL, idx->size + 1);
+}
+
+sqsh_err
+sqsh_dir_ff_header(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_dir *dir, void *arg)
+{
+ struct sqsh_dir_index idx;
+ struct sqsh_block_run cur;
+ size_t count;
+
+ cur = inode->next;
+ count = inode->xtra.dir.idx_count;
+
+ if (count == 0)
+ return SQFS_OK;
+
+ while (count--) {
+ sqsh_err err;
+ bool stop;
+
+ stop = false;
+
+ err = sqsh_metadata_get(ump, &cur, &idx, sizeof(idx));
+
+ if (err != SQFS_OK)
+ return err;
+
+ swapendian_dir_index(&idx);
+
+ err = sqsh_dir_f_header(ump, &cur, &idx, &stop, arg);
+
+ if (err != SQFS_OK)
+ return err;
+ if (stop)
+ break;
+
+ dir->cur.block = idx.start_block +
+ ump->sb.directory_table_start;
+ dir->offset = idx.index;
+ }
+
+ dir->cur.offset = (dir->cur.offset + dir->offset) %
+ SQUASHFS_METADATA_SIZE;
+ return SQFS_OK;
+}
+
+sqsh_err
+sqsh_dir_metadata_read(struct sqsh_mount *ump, struct sqsh_dir *dir, void *buf,
+ size_t size)
+{
+ dir->offset += size;
+ return sqsh_metadata_get(ump, &dir->cur, buf, size);
+}
+
+sqsh_err
+sqsh_dir_getnext(struct sqsh_mount *ump, struct sqsh_dir *dir,
+ struct sqsh_dir_entry *entry)
+{
+ struct squashfs_dir_entry e;
+ sqsh_err err;
+
+ entry->offset = dir->offset;
+
+ while (dir->header.count == 0) {
+ if (dir->offset >= dir->total) {
+ err = SQFS_END_OF_DIRECTORY;
+ return err;
+ }
+
+ err = sqsh_dir_metadata_read(ump, dir, &dir->header,
+ sizeof(dir->header));
+
+ if (err != SQFS_OK)
+ return err;
+ swapendian_dir_header(&dir->header);
+ ++(dir->header.count);
+ }
+
+ err = sqsh_dir_metadata_read(ump, dir, &e, sizeof(e));
+
+ if (err != SQFS_OK)
+ return err;
+ swapendian_dir_entry(&e);
+ --(dir->header.count);
+
+ /* Initialise new entry fields */
+ entry->name_size = e.size + 1;
+ entry->inode_id = ((uint64_t)dir->header.start_block << 16) + e.offset;
+ entry->inode_number = dir->header.inode_number +
+ (int16_t)e.inode_number;
+
+ err = sqsh_dir_metadata_read(ump, dir, entry->name, entry->name_size);
+ if (err != SQFS_OK)
+ return err;
+
+ entry->next_offset = dir->offset;
+
+ return SQFS_OK;
+}
+
+sqsh_err
+sqsh_dir_lookup(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ const char *name, size_t namelen, struct sqsh_dir_entry *entry, bool *found)
+{
+ sqsh_err err;
+ struct sqsh_dir dir;
+ struct sqsh_dir_ff_name_t arg;
+
+ *found = false;
+
+ err = sqsh_dir_init(ump, inode, &dir);
+
+ if (err != SQFS_OK)
+ return err;
+
+ /* Fast forward to header */
+ arg.cmp = name;
+ arg.cmplen = namelen;
+ arg.name = entry->name;
+
+ err = sqsh_dir_ff_header(ump, inode, &dir, &arg);
+ if (err != SQFS_OK)
+ return err;
+
+ /* Iterate to find the right entry */
+ while (sqsh_dir_getnext(ump, &dir, entry) == SQFS_OK) {
+ int order;
+
+ if (entry->name_size != namelen)
+ continue;
+
+ order = strncmp(entry->name, name, namelen);
+ if (order == 0 && entry->name_size == namelen) {
+ *found = true;
+ break;
+ }
+ }
+
+ return SQFS_OK;
+}
+
+void
+swapendian_dir_header(struct sqsh_dir_header *hdr)
+{
+ hdr->count = le32toh(hdr->count);
+ hdr->start_block = le32toh(hdr->start_block);
+ hdr->inode_number = le32toh(hdr->inode_number);
+}
+
+void
+swapendian_dir_index(struct sqsh_dir_index *idx)
+{
+ idx->index = le32toh(idx->index);
+ idx->start_block = le32toh(idx->start_block);
+ idx->size = le32toh(idx->size);
+}
+
+void
+swapendian_dir_entry(struct squashfs_dir_entry *entry)
+{
+ entry->offset = le16toh(entry->offset);
+ entry->inode_number = le16toh(entry->inode_number);
+ entry->type = le16toh(entry->type);
+ entry->size = le16toh(entry->size);
+}
diff --git a/sys/fs/squashfs/squashfs_file.h b/sys/fs/squashfs/squashfs_file.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_file.h
@@ -0,0 +1,56 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef SQUASHFS_FILE_H
+#define SQUASHFS_FILE_H
+
+struct sqsh_blocklist {
+ struct sqsh_mount *ump;
+ size_t remain;
+ struct sqsh_block_run cur;
+ bool started;
+ uint64_t pos;
+ uint64_t block;
+ uint32_t header;
+ uint32_t input_size;
+};
+
+struct sqsh_blockidx_entry {
+ uint64_t data_block;
+ uint32_t md_block;
+};
+
+/* sqsh_blocklist helper functions */
+size_t sqsh_blocklist_count(struct sqsh_mount *ump, struct sqsh_inode *inode);
+
+sqsh_err sqsh_read_file(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ off_t start, off_t *size, struct uio *uiop);
+
+#endif
diff --git a/sys/fs/squashfs/squashfs_file.c b/sys/fs/squashfs/squashfs_file.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_file.c
@@ -0,0 +1,361 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+#else
+#include <sys/uio.h>
+
+#include <stdlib.h>
+#endif
+
+#include <squashfs.h>
+#include <squashfs_io.h>
+#include <squashfs_mount.h>
+#include <squashfs_inode.h>
+#include <squashfs_block.h>
+#include <squashfs_file.h>
+
+#ifdef _KERNEL
+static MALLOC_DEFINE(M_SQSHBLKIDX, "Sqsh Blk idx", "Squashfs block index");
+#endif
+
+static void
+swapendian_fragment_entry(struct sqsh_fragment_entry *temp)
+{
+ temp->start_block = le64toh(temp->start_block);
+ temp->size = le32toh(temp->size);
+ temp->unused = le32toh(temp->unused);
+}
+
+size_t
+sqsh_blocklist_count(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ uint64_t size = inode->size;
+ size_t block = ump->sb.block_size;
+ if (inode->xtra.reg.frag_idx == SQUASHFS_INVALID_FRAG) {
+ return sqsh_ceil(size, block);
+ } else {
+ return (size_t)(size / block);
+ }
+}
+
+static void
+sqsh_blocklist_init(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_blocklist *bl)
+{
+ bl->ump = ump;
+ bl->remain = sqsh_blocklist_count(ump, inode);
+ bl->cur = inode->next;
+ bl->started = false;
+ bl->pos = 0;
+ bl->block = inode->xtra.reg.start_block;
+ bl->input_size = 0;
+}
+
+static sqsh_err
+sqsh_blocklist_next(struct sqsh_blocklist *bl)
+{
+ sqsh_err err;
+ bool compressed;
+
+ if (bl->remain == 0)
+ return SQFS_ERR;
+ --(bl->remain);
+
+ err = sqsh_metadata_get(bl->ump, &bl->cur, &bl->header,
+ sizeof(bl->header));
+ if (err != SQFS_OK)
+ return err;
+ bl->header = le32toh(bl->header);
+ bl->block += bl->input_size;
+ sqsh_data_header(bl->header, &compressed, &bl->input_size);
+
+ if (bl->started)
+ bl->pos += bl->ump->sb.block_size;
+ bl->started = true;
+
+ return SQFS_OK;
+}
+
+static bool
+sqsh_blockidx_indexable(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ size_t blocks = sqsh_blocklist_count(ump, inode);
+ size_t md_size = blocks * sizeof(uint32_t);
+ return md_size >= SQUASHFS_METADATA_SIZE;
+}
+
+static sqsh_err
+sqsh_blockidx_add(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_blockidx_entry **out)
+{
+ size_t blocks;
+ size_t md_size;
+ size_t count;
+
+ struct sqsh_blockidx_entry *blockidx;
+ struct sqsh_blocklist bl;
+
+ size_t i;
+ bool first;
+
+ i = 0;
+ first = true;
+ *out = NULL;
+
+ blocks = sqsh_blocklist_count(ump, inode);
+ md_size = blocks * sizeof(uint32_t);
+ count = (inode->next.offset + md_size - 1) / SQUASHFS_METADATA_SIZE;
+ blockidx = SQUASHFS_MALLOC(count * sizeof(struct sqsh_blockidx_entry),
+ M_SQSHBLKIDX, M_WAITOK | M_ZERO);
+ if (blockidx == NULL)
+ return SQFS_ERR;
+
+ sqsh_blocklist_init(ump, inode, &bl);
+ while (bl.remain && i < count) {
+ sqsh_err err;
+ /* skip the first metadata block since its stored in inode */
+ if (bl.cur.offset < sizeof(uint32_t) && !first) {
+ blockidx[i].data_block = bl.block + bl.input_size;
+ blockidx[i++].md_block = (uint32_t)(bl.cur.block -
+ ump->sb.inode_table_start);
+ }
+ first = false;
+
+ err = sqsh_blocklist_next(&bl);
+ if (err != SQFS_OK) {
+ SQUASHFS_FREE(blockidx, M_SQSHBLKIDX);
+ return SQFS_ERR;
+ }
+ }
+
+ *out = blockidx;
+ return SQFS_OK;
+}
+
+static sqsh_err
+sqsh_blockidx_blocklist(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_blocklist *bl, off_t start)
+{
+ size_t block, metablock, skipped;
+ struct sqsh_blockidx_entry *blockidx, *blockpos;
+ sqsh_err err;
+
+ sqsh_blocklist_init(ump, inode, bl);
+ block = (size_t)(start / ump->sb.block_size);
+ /* fragment */
+ if (block > bl->remain) {
+ bl->remain = 0;
+ return SQFS_OK;
+ }
+
+ /* Total blocks we need to skip */
+ metablock = (bl->cur.offset + block * sizeof(uint32_t)) /
+ SQUASHFS_METADATA_SIZE;
+ if (metablock == 0)
+ return SQFS_OK;
+ if (!sqsh_blockidx_indexable(ump, inode))
+ return SQFS_OK;
+
+ err = sqsh_blockidx_add(ump, inode, &blockidx);
+ if (err != SQFS_OK) {
+ return err;
+ }
+
+ skipped = (metablock * SQUASHFS_METADATA_SIZE / sizeof(uint32_t)) -
+ (bl->cur.offset / sizeof(uint32_t));
+
+ blockpos = blockidx + (metablock - 1);
+ bl->cur.block = blockpos->md_block + ump->sb.inode_table_start;
+ bl->cur.offset %= sizeof(uint32_t);
+ bl->remain -= skipped;
+ bl->pos = (uint64_t)skipped * ump->sb.block_size;
+ bl->block = blockpos->data_block;
+
+ /* free blockidx */
+ SQUASHFS_FREE(blockidx, M_SQSHBLKIDX);
+
+ return SQFS_OK;
+}
+
+static sqsh_err
+sqsh_frag_entry(struct sqsh_mount *ump, struct sqsh_fragment_entry *frag,
+ uint32_t idx)
+{
+ sqsh_err err;
+
+ if (idx == SQUASHFS_INVALID_FRAG)
+ return SQFS_ERR;
+
+ err = sqsh_get_table(&ump->frag_table, ump, idx, frag);
+ swapendian_fragment_entry(frag);
+ return err;
+}
+
+static sqsh_err
+sqsh_frag_block(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ size_t *offset, size_t *size, struct sqsh_block **block)
+{
+ struct sqsh_fragment_entry frag;
+ sqsh_err err;
+
+#ifdef _KERNEL
+ if (inode->type != VREG)
+ return SQFS_ERR;
+#endif
+
+ err = sqsh_frag_entry(ump, &frag, inode->xtra.reg.frag_idx);
+ if (err != SQFS_OK)
+ return err;
+
+ err = sqsh_data_read(ump, frag.start_block, frag.size, block);
+ if (err != SQFS_OK)
+ return SQFS_ERR;
+
+ *offset = inode->xtra.reg.frag_off;
+ *size = inode->size % ump->sb.block_size;
+
+ return (err);
+}
+
+sqsh_err
+sqsh_read_file(struct sqsh_mount *ump, struct sqsh_inode *inode, off_t start,
+ off_t *size, struct uio *uiop)
+{
+ sqsh_err err;
+ off_t file_size;
+ size_t block_size;
+ struct sqsh_blocklist bl;
+ size_t read_off;
+ off_t data_read;
+ int error;
+
+ data_read = 0;
+ file_size = inode->size;
+ block_size = ump->sb.block_size;
+
+ if (*size < 0 || start > file_size)
+ return SQFS_ERR;
+ if (start == file_size) {
+ *size = 0;
+ return SQFS_OK;
+ }
+
+ err = sqsh_blockidx_blocklist(ump, inode, &bl, start);
+ if (err != SQFS_OK)
+ return err;
+
+ read_off = start % block_size;
+ while (*size > 0) {
+ struct sqsh_block *block;
+ size_t data_off, data_size;
+ size_t take;
+ bool fragment;
+
+ block = NULL;
+ fragment = (bl.remain == 0);
+
+ if (fragment) {
+ if (inode->xtra.reg.frag_idx == SQUASHFS_INVALID_FRAG)
+ break;
+ err = sqsh_frag_block(ump, inode, &data_off, &data_size,
+ &block);
+ if (err != SQFS_OK)
+ return err;
+ } else {
+ err = sqsh_blocklist_next(&bl);
+ if (err != SQFS_OK)
+ return err;
+ if (bl.pos + block_size <= start)
+ continue;
+
+ data_off = 0;
+ if (bl.input_size == 0) {
+ data_size = (size_t)(file_size - bl.pos);
+ if (data_size > block_size)
+ data_size = block_size;
+ } else {
+ err = sqsh_data_read(ump, bl.block, bl.header,
+ &block);
+ if (err != SQFS_OK)
+ return err;
+ data_size = block->size;
+ }
+ }
+
+ take = data_size - read_off;
+ if (take > *size)
+ take = (size_t)(*size);
+ if (block != NULL) {
+ error = uiomove((char *)block->data + data_off +
+ read_off,
+ take, uiop);
+ if (error != 0)
+ return SQFS_ERR;
+ /* free the allocated block since we have no cache now
+ */
+ sqsh_free_block(block);
+ } else {
+ error = uiomove(__DECONST(void *, zero_region), take,
+ uiop);
+ if (error != 0)
+ return SQFS_ERR;
+ }
+ read_off = 0;
+ *size -= take;
+ data_read += take;
+
+ if (fragment)
+ break;
+ }
+
+ return data_read ? SQFS_OK : SQFS_ERR;
+}
diff --git a/sys/fs/squashfs/squashfs_init.c b/sys/fs/squashfs/squashfs_init.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_init.c
@@ -0,0 +1,222 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+
+#include <vm/vm_param.h>
+
+#include <geom/geom.h>
+#include <geom/geom_vfs.h>
+
+#include <squashfs.h>
+#include <squashfs_io.h>
+#include <squashfs_mount.h>
+#include <squashfs_inode.h>
+#include <squashfs_decompressor.h>
+#include <squashfs_xattr.h>
+
+static void
+squashfs_swapendian_sb(struct sqsh_sb *sb)
+{
+ sb->s_magic = le32toh(sb->s_magic);
+ sb->inodes = le32toh(sb->inodes);
+ sb->mkfs_time = le32toh(sb->mkfs_time);
+ sb->block_size = le32toh(sb->block_size);
+ sb->fragments = le32toh(sb->fragments);
+ sb->compression = le16toh(sb->compression);
+ sb->block_log = le16toh(sb->block_log);
+ sb->flags = le16toh(sb->flags);
+ sb->no_ids = le16toh(sb->no_ids);
+ sb->s_major = le16toh(sb->s_major);
+ sb->s_minor = le16toh(sb->s_minor);
+ sb->root_inode = le64toh(sb->root_inode);
+ sb->bytes_used = le64toh(sb->bytes_used);
+ sb->id_table_start = le64toh(sb->id_table_start);
+ sb->xattr_id_table_start = le64toh(sb->xattr_id_table_start);
+ sb->inode_table_start = le64toh(sb->inode_table_start);
+ sb->directory_table_start = le64toh(sb->directory_table_start);
+ sb->fragment_table_start = le64toh(sb->fragment_table_start);
+ sb->lookup_table_start = le64toh(sb->lookup_table_start);
+}
+
+static sqsh_err
+is_valid_superblock(struct sqsh_sb *sb)
+{
+ /* Check magic number */
+ if (sb->s_magic != SQUASHFS_MAGIC &&
+ sb->s_magic != SQUASHFS_MAGIC_SWAP) {
+#ifdef _KERNEL
+ ERROR("Bad superblock magic number");
+#endif
+ return (SQFS_BADFORMAT);
+ }
+
+ /* Check for version of mounted fs */
+ if (sb->s_major != SQUASHFS_MAJOR || sb->s_minor > SQUASHFS_MINOR) {
+#ifdef _KERNEL
+ ERROR("Unsupported version of squashfs is mounted");
+#endif
+ return (SQFS_BADVERSION);
+ }
+
+ /* Check if filesystem size is not negative for sanity */
+ if (sb->bytes_used < 0) {
+ ERROR("Filesystem size is negative!");
+ return (SQFS_ERR);
+ }
+
+ /* Check block size for sanity */
+ if (sb->block_size > SQUASHFS_FILE_MAX_SIZE) {
+ ERROR("Invalid block size");
+ return (SQFS_ERR);
+ }
+
+ /* Check block log for sanity */
+ if (sb->block_log > SQUASHFS_FILE_MAX_LOG) {
+ ERROR("Invalid block log");
+ return (SQFS_ERR);
+ }
+
+ /* Check that block_size and block_log match */
+ if (sb->block_size != (1 << sb->block_log)) {
+ ERROR("Block size and log mismatch");
+ return (SQFS_ERR);
+ }
+
+ /* Check the root inode for sanity */
+ if (SQUASHFS_INODE_OFFSET(sb->root_inode) > SQUASHFS_METADATA_SIZE) {
+ ERROR("Invalid root inode size");
+ return (SQFS_ERR);
+ }
+
+ /* A valid superblock is detected */
+ TRACE("A valid superblock is detected");
+ return (SQFS_OK);
+}
+
+sqsh_err
+squashfs_init(struct sqsh_mount *ump)
+{
+ sqsh_err error;
+
+ /* squashfs superblock is at offset zero */
+ if (sqsh_io_read_buf(ump, &ump->sb, 0, sizeof(struct sqsh_sb)) !=
+ sizeof(struct sqsh_sb)) {
+#ifdef _KERNEL
+ ERROR("Failed to read superblock, I/O error");
+#endif
+ return (SQFS_ERR);
+ }
+ squashfs_swapendian_sb(&ump->sb);
+
+ /* check superblock to see if everything is fine */
+ error = is_valid_superblock(&ump->sb);
+ if (error != SQFS_OK)
+ return (error);
+
+ /* Init decompressor for squashfs and check if it is unknown or
+ * supported? */
+ ump->decompressor = sqsh_lookup_decompressor(ump->sb.compression);
+ if (ump->decompressor == NULL) {
+ ERROR("Filesystem compression type not found");
+ return (SQFS_BADCOMP);
+ } else if (ump->decompressor->decompressor == NULL) {
+ ERROR(
+ "Filesystem uses \"%s\" compression, which is not included in this kernel.",
+ ump->decompressor->name);
+ return (SQFS_BADCOMP);
+ }
+
+ error = sqsh_init_table(&ump->id_table, ump, ump->sb.id_table_start,
+ sizeof(uint32_t), ump->sb.no_ids);
+ if (error != SQFS_OK)
+ goto id_table_fail;
+
+ error = sqsh_init_table(&ump->frag_table, ump,
+ ump->sb.fragment_table_start, sizeof(struct sqsh_fragment_entry),
+ ump->sb.fragments);
+ if (error != SQFS_OK)
+ goto frag_table_fail;
+
+ if (sqsh_export_ok(ump)) {
+ error = sqsh_init_table(&ump->export_table, ump,
+ ump->sb.lookup_table_start, sizeof(uint64_t),
+ ump->sb.inodes);
+ if (error != SQFS_OK)
+ goto export_table_fail;
+ }
+
+ error = sqsh_init_xattr(ump);
+ if (error != SQFS_OK)
+ goto xattrs_fail;
+
+ TRACE("Table init() passed!");
+
+ /* Everything fine */
+ return (SQFS_OK);
+
+id_table_fail:
+ sqsh_free_table(&ump->id_table);
+ return (error);
+
+frag_table_fail:
+ sqsh_free_table(&ump->id_table);
+ sqsh_free_table(&ump->frag_table);
+ return (error);
+
+export_table_fail:
+ sqsh_free_table(&ump->id_table);
+ sqsh_free_table(&ump->frag_table);
+ sqsh_free_table(&ump->export_table);
+ return (error);
+xattrs_fail:
+ sqsh_free_table(&ump->id_table);
+ sqsh_free_table(&ump->frag_table);
+ sqsh_free_table(&ump->export_table);
+ sqsh_free_table(&ump->xattr_table);
+ return (error);
+}
diff --git a/sys/fs/squashfs/squashfs_inode.h b/sys/fs/squashfs/squashfs_inode.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_inode.h
@@ -0,0 +1,107 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef SQUASHFS_INODE_H
+#define SQUASHFS_INODE_H
+
+#include <squashfs_block.h>
+
+#define SQUASHFS_INODE_OFFSET(A) ((unsigned int)((A) & 0xffff))
+
+struct sqsh_inode {
+ struct sqsh_base_inode base;
+ int nlink;
+ uint32_t xattr;
+ size_t size;
+
+#ifdef _KERNEL
+ __enum_uint8(vtype) type;
+#else
+ int type;
+#endif
+
+ struct sqsh_block_run next;
+
+ union {
+ struct {
+ int major;
+ int minor;
+ } dev;
+ struct {
+ uint64_t start_block;
+ uint64_t sparse;
+ uint32_t frag_idx;
+ uint32_t frag_off;
+ } reg;
+ struct {
+ uint32_t start_block;
+ uint16_t offset;
+ uint16_t idx_count;
+ uint32_t parent_inode;
+ struct sqsh_dir d;
+ struct sqsh_dir_entry entry;
+ } dir;
+ } xtra;
+
+ uint64_t ino_id;
+ uint64_t parent_id;
+ struct vnode *vnode;
+ struct sqsh_mount *ump;
+};
+
+/* helper functions to query on table */
+sqsh_err sqsh_init_table(struct sqsh_table *table, struct sqsh_mount *ump,
+ off_t start, size_t each, size_t count);
+void sqsh_free_table(struct sqsh_table *table);
+sqsh_err sqsh_get_table(struct sqsh_table *table, struct sqsh_mount *ump,
+ size_t idx, void *buf);
+
+/* helper functions to query on inode */
+void sqsh_metadata_run_inode(struct sqsh_block_run *cur, uint64_t id,
+ off_t base);
+sqsh_err sqsh_get_inode(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ uint64_t id);
+
+#ifdef _KERNEL
+__enum_uint8(vtype)
+ sqsh_inode_type_from_id(struct sqsh_mount *ump, uint64_t inode_id);
+#else
+int sqsh_inode_type_from_id(struct sqsh_mount *ump, uint64_t inode_id);
+#endif
+
+sqsh_err sqsh_get_inode_id(struct sqsh_mount *ump, uint16_t idx, uint32_t *id);
+
+bool sqsh_export_ok(struct sqsh_mount *ump);
+sqsh_err sqsh_export_inode(struct sqsh_mount *ump, uint32_t n, uint64_t *i);
+
+uint64_t sqsh_root_inode(struct sqsh_mount *ump);
+sqsh_err sqsh_verify_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+
+#endif
diff --git a/sys/fs/squashfs/squashfs_inode.c b/sys/fs/squashfs/squashfs_inode.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_inode.c
@@ -0,0 +1,677 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+#else
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#include <squashfs.h>
+#include <squashfs_io.h>
+#include <squashfs_mount.h>
+#include <squashfs_inode.h>
+#include <squashfs_decompressor.h>
+#include <squashfs_block.h>
+#include <squashfs_dir.h>
+
+/* Swapendian functions for all types of inodes */
+void swapendian_base_inode(struct sqsh_base_inode *temp);
+void swapendian_reg_inode(struct sqsh_reg_inode *temp);
+void swapendian_lreg_inode(struct sqsh_lreg_inode *temp);
+void swapendian_dir_inode(struct sqsh_dir_inode *temp);
+void swapendian_ldir_inode(struct sqsh_ldir_inode *temp);
+
+/* init functions for all types of inodes */
+sqsh_err sqsh_init_reg_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+sqsh_err sqsh_init_lreg_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+sqsh_err sqsh_init_dir_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+sqsh_err sqsh_init_ldir_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+sqsh_err sqsh_init_symlink_inode(struct sqsh_mount *ump,
+ struct sqsh_inode *inode);
+sqsh_err sqsh_init_dev_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+sqsh_err sqsh_init_ldev_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+sqsh_err sqsh_init_ipc_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+sqsh_err sqsh_init_lipc_inode(struct sqsh_mount *ump, struct sqsh_inode *inode);
+
+#ifdef _KERNEL
+static MALLOC_DEFINE(M_SQUASHFSTABLEBLK, "SQUASHFS tab blk",
+ "SQUASHFS table block");
+#endif
+
+sqsh_err
+sqsh_init_table(struct sqsh_table *table, struct sqsh_mount *ump, off_t start,
+ size_t each, size_t count)
+{
+ size_t i;
+ size_t nblocks, bread;
+
+ if (count == 0)
+ return (SQFS_OK);
+
+ nblocks = sqsh_ceil(each * count, SQUASHFS_METADATA_SIZE);
+ bread = nblocks * sizeof(uint64_t);
+
+ table->each = each;
+ table->blocks = SQUASHFS_MALLOC(bread, M_SQUASHFSTABLEBLK, M_WAITOK);
+ if (table->blocks == NULL)
+ return (SQFS_ERR);
+
+ if (sqsh_io_read_buf(ump, table->blocks, start, bread) != bread) {
+ ERROR("Failed to read data, I/O error");
+ sqsh_free_table(table);
+ return (SQFS_ERR);
+ }
+
+ /* SwapEndian data to host */
+ for (i = 0; i < nblocks; ++i)
+ table->blocks[i] = le64toh(table->blocks[i]);
+
+ return (SQFS_OK);
+}
+
+void
+sqsh_free_table(struct sqsh_table *table)
+{
+ SQUASHFS_FREE(table->blocks, M_SQUASHFSTABLEBLK);
+ table->blocks = NULL;
+}
+
+sqsh_err
+sqsh_get_table(struct sqsh_table *table, struct sqsh_mount *ump, size_t idx,
+ void *buf)
+{
+ struct sqsh_block *block;
+ size_t pos, bnum, off, data_size;
+ off_t bpos;
+
+ pos = idx * table->each;
+ bnum = pos / SQUASHFS_METADATA_SIZE;
+ off = pos % SQUASHFS_METADATA_SIZE;
+
+ bpos = table->blocks[bnum];
+ data_size = 0;
+ if (sqsh_metadata_read(ump, bpos, &data_size, &block))
+ return (SQFS_ERR);
+
+ memcpy(buf, (char *)(block->data) + off, table->each);
+ /* Free block since currently we have no cache */
+ sqsh_free_block(block);
+ return (SQFS_OK);
+}
+
+bool
+sqsh_export_ok(struct sqsh_mount *ump)
+{
+ return (ump->sb.lookup_table_start != SQUASHFS_INVALID_BLK);
+}
+
+void
+sqsh_metadata_run_inode(struct sqsh_block_run *cur, uint64_t id, off_t base)
+{
+ cur->block = (id >> 16) + base;
+ cur->offset = id & 0xffff;
+}
+
+#ifdef _KERNEL
+static __enum_uint8(vtype)
+#else
+static int
+#endif
+ sqsh_inode_type(int inode_type)
+{
+ switch (inode_type) {
+ case SQUASHFS_DIR_TYPE:
+ case SQUASHFS_LDIR_TYPE:
+#ifdef _KERNEL
+ return (VDIR);
+#else
+ return (DT_DIR);
+#endif
+ case SQUASHFS_REG_TYPE:
+ case SQUASHFS_LREG_TYPE:
+#ifdef _KERNEL
+ return (VREG);
+#else
+ return (DT_REG);
+#endif
+ case SQUASHFS_SYMLINK_TYPE:
+ case SQUASHFS_LSYMLINK_TYPE:
+#ifdef _KERNEL
+ return (VLNK);
+#else
+ return (DT_LNK);
+#endif
+ case SQUASHFS_BLKDEV_TYPE:
+ case SQUASHFS_LBLKDEV_TYPE:
+#ifdef _KERNEL
+ return (VBLK);
+#else
+ return (DT_BLK);
+#endif
+ case SQUASHFS_CHRDEV_TYPE:
+ case SQUASHFS_LCHRDEV_TYPE:
+#ifdef _KERNEL
+ return (VCHR);
+#else
+ return (DT_CHR);
+#endif
+ case SQUASHFS_FIFO_TYPE:
+ case SQUASHFS_LFIFO_TYPE:
+#ifdef _KERNEL
+ return (VFIFO);
+#else
+ return (DT_FIFO);
+#endif
+ case SQUASHFS_SOCKET_TYPE:
+ case SQUASHFS_LSOCKET_TYPE:
+#ifdef _KERNEL
+ return (VSOCK);
+#else
+ return (DT_SOCK);
+#endif
+ }
+#ifdef _KERNEL
+ return (VBAD);
+#else
+ return (DT_UNKNOWN);
+#endif
+}
+
+#ifdef _KERNEL
+__enum_uint8(vtype)
+#else
+int
+#endif
+ sqsh_inode_type_from_id(struct sqsh_mount *ump, uint64_t inode_id)
+{
+ struct sqsh_block_run cur;
+ struct sqsh_base_inode base;
+ sqsh_err err;
+
+ sqsh_metadata_run_inode(&cur, inode_id, ump->sb.inode_table_start);
+
+ err = sqsh_metadata_get(ump, &cur, &base, sizeof(base));
+ if (err != SQFS_OK)
+#ifdef _KERNEL
+ return (VBAD);
+#else
+ return (DT_UNKNOWN);
+#endif
+ swapendian_base_inode(&base);
+ return sqsh_inode_type(base.inode_type);
+}
+
+sqsh_err
+sqsh_verify_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ /* check for inode type */
+ if (inode->base.inode_type < SQUASHFS_TYPE_MIN_VALID ||
+ inode->base.inode_type > SQUASHFS_TYPE_MAX_VALID)
+ return (SQFS_ERR);
+
+ /*
+ * check for inode_number.
+ * The inode numbers are from 1 to the total number of inodes.
+ * Note that 0 is always invalid because we will
+ * always have at least a root inode.
+ */
+ if (inode->base.inode_number < SQUASHFS_INODE_MIN_COUNT ||
+ inode->base.inode_number > ump->sb.inodes)
+ return (SQFS_ERR);
+
+ /*
+ * If inode type is directory then check for parent inode.
+ * Note that we add +1 because for root inode parent_inode
+ * is total inodes + 1.
+ * For some squashfs archives it is 0 too.
+ */
+ if (inode->base.inode_type == SQUASHFS_DIR_TYPE) {
+ if ((inode->xtra.dir.parent_inode < SQUASHFS_INODE_MIN_COUNT ||
+ inode->xtra.dir.parent_inode > ump->sb.inodes + 1) &&
+ inode->xtra.dir.parent_inode != 0)
+ return (SQFS_ERR);
+ }
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_get_inode_id(struct sqsh_mount *ump, uint16_t idx, uint32_t *id)
+{
+ uint32_t rid;
+ sqsh_err err;
+
+ err = sqsh_get_table(&ump->id_table, ump, idx, &rid);
+ if (err != SQFS_OK)
+ return (err);
+ rid = le32toh(rid);
+ *id = rid;
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_export_inode(struct sqsh_mount *ump, uint32_t n, uint64_t *i)
+{
+ uint64_t r;
+ sqsh_err err;
+
+ if (!sqsh_export_ok(ump))
+ return (SQFS_ERR);
+
+ err = sqsh_get_table(&ump->export_table, ump, n - 1, &r);
+ if (err)
+ return (err);
+ r = le64toh(r);
+ *i = r;
+ return (SQFS_OK);
+}
+
+uint64_t
+sqsh_root_inode(struct sqsh_mount *ump)
+{
+ return (ump->sb.root_inode);
+}
+
+sqsh_err
+sqsh_get_inode(struct sqsh_mount *ump, struct sqsh_inode *inode, uint64_t id)
+{
+ struct sqsh_block_run cur;
+ sqsh_err err;
+
+ KASSERT(inode != NULL, ("Inode: NULL"));
+
+ memset(inode, 0, sizeof(*inode));
+ inode->xattr = SQUASHFS_INVALID_XATTR;
+
+ sqsh_metadata_run_inode(&cur, id, ump->sb.inode_table_start);
+ inode->next = cur;
+
+ err = sqsh_metadata_get(ump, &cur, &inode->base, sizeof(inode->base));
+ if (err != SQFS_OK)
+ return (err);
+
+ swapendian_base_inode(&inode->base);
+ inode->type = sqsh_inode_type(inode->base.inode_type);
+
+ inode->vnode = NULL;
+ inode->ump = ump;
+ inode->ino_id = id;
+ inode->parent_id = 0;
+
+ switch (inode->base.inode_type) {
+ case SQUASHFS_REG_TYPE: {
+ err = sqsh_init_reg_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ case SQUASHFS_LREG_TYPE: {
+ err = sqsh_init_lreg_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ case SQUASHFS_DIR_TYPE: {
+ err = sqsh_init_dir_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ case SQUASHFS_LDIR_TYPE: {
+ err = sqsh_init_ldir_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ case SQUASHFS_SYMLINK_TYPE:
+ case SQUASHFS_LSYMLINK_TYPE: {
+ err = sqsh_init_symlink_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ case SQUASHFS_BLKDEV_TYPE:
+ case SQUASHFS_CHRDEV_TYPE: {
+ err = sqsh_init_dev_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ case SQUASHFS_LBLKDEV_TYPE:
+ case SQUASHFS_LCHRDEV_TYPE: {
+ err = sqsh_init_ldev_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ case SQUASHFS_SOCKET_TYPE:
+ case SQUASHFS_FIFO_TYPE: {
+ err = sqsh_init_ipc_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ case SQUASHFS_LSOCKET_TYPE:
+ case SQUASHFS_LFIFO_TYPE: {
+ err = sqsh_init_lipc_inode(ump, inode);
+ if (err != SQFS_OK)
+ return (err);
+ break;
+ }
+ default:
+ return (SQFS_ERR);
+ }
+
+ err = sqsh_verify_inode(ump, inode);
+
+ return (err);
+}
+
+sqsh_err
+sqsh_init_reg_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_reg_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+ swapendian_reg_inode(&temp);
+
+ inode->nlink = 1;
+ inode->xtra.reg.start_block = temp.start_block;
+ inode->size = temp.file_size;
+ inode->xtra.reg.frag_idx = temp.fragment;
+ inode->xtra.reg.frag_off = temp.offset;
+ inode->xtra.reg.sparse = 0;
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_init_lreg_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_lreg_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+ swapendian_lreg_inode(&temp);
+
+ inode->nlink = temp.nlink;
+ inode->xtra.reg.start_block = temp.start_block;
+ inode->size = temp.file_size;
+ inode->xtra.reg.frag_idx = temp.fragment;
+ inode->xtra.reg.frag_off = temp.offset;
+ inode->xattr = temp.xattr;
+ inode->xtra.reg.sparse = temp.sparse;
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_init_dir_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_dir_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+ swapendian_dir_inode(&temp);
+
+ inode->nlink = temp.nlink;
+ inode->xtra.dir.start_block = temp.start_block;
+ inode->xtra.dir.offset = temp.offset;
+ inode->size = temp.file_size;
+ inode->xtra.dir.idx_count = 0;
+ inode->xtra.dir.parent_inode = temp.parent_inode;
+
+ sqsh_dir_init(ump, inode, &inode->xtra.dir.d);
+ memset(&inode->xtra.dir.entry, 0, sizeof(struct sqsh_dir_entry));
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_init_ldir_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_ldir_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+ swapendian_ldir_inode(&temp);
+
+ inode->nlink = temp.nlink;
+ inode->xtra.dir.start_block = temp.start_block;
+ inode->xtra.dir.offset = temp.offset;
+ inode->size = temp.file_size;
+ inode->xtra.dir.idx_count = temp.i_count;
+ inode->xtra.dir.parent_inode = temp.parent_inode;
+ inode->xattr = temp.xattr;
+
+ sqsh_dir_init(ump, inode, &inode->xtra.dir.d);
+ memset(&inode->xtra.dir.entry, 0, sizeof(struct sqsh_dir_entry));
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_init_symlink_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_symlink_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+
+ inode->nlink = le32toh(temp.nlink);
+ inode->size = le32toh(temp.symlink_size);
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_init_dev_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_dev_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+
+ inode->size = 0;
+ inode->nlink = le32toh(temp.nlink);
+ temp.rdev = le32toh(temp.rdev);
+ inode->xtra.dev.major = (temp.rdev >> 8) & 0xfff;
+ inode->xtra.dev.minor = (temp.rdev & 0xff) |
+ ((temp.rdev >> 12) & 0xfff00);
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_init_ldev_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_ldev_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+
+ inode->size = 0;
+ inode->nlink = le32toh(temp.nlink);
+ inode->xattr = le32toh(temp.xattr);
+ temp.rdev = le32toh(temp.rdev);
+ inode->xtra.dev.major = (temp.rdev >> 8) & 0xfff;
+ inode->xtra.dev.minor = (temp.rdev & 0xff) |
+ ((temp.rdev >> 12) & 0xfff00);
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_init_ipc_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_ipc_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+
+ inode->size = 0;
+ inode->nlink = le32toh(temp.nlink);
+
+ return (SQFS_OK);
+}
+
+sqsh_err
+sqsh_init_lipc_inode(struct sqsh_mount *ump, struct sqsh_inode *inode)
+{
+ struct sqsh_lipc_inode temp;
+ sqsh_err err;
+
+ err = sqsh_metadata_get(ump, &inode->next, &temp, sizeof(temp));
+ if (err != SQFS_OK)
+ return (err);
+
+ inode->size = 0;
+ inode->nlink = le32toh(temp.nlink);
+ inode->xattr = le32toh(temp.xattr);
+
+ return (SQFS_OK);
+}
+
+void
+swapendian_base_inode(struct sqsh_base_inode *temp)
+{
+ temp->inode_type = le16toh(temp->inode_type);
+ temp->mode = le16toh(temp->mode);
+ temp->uid = le16toh(temp->uid);
+ temp->guid = le16toh(temp->guid);
+ temp->mtime = le32toh(temp->mtime);
+ temp->inode_number = le32toh(temp->inode_number);
+}
+
+void
+swapendian_reg_inode(struct sqsh_reg_inode *temp)
+{
+ temp->inode_type = le16toh(temp->inode_type);
+ temp->mode = le16toh(temp->mode);
+ temp->uid = le16toh(temp->uid);
+ temp->guid = le16toh(temp->guid);
+ temp->mtime = le32toh(temp->mtime);
+ temp->inode_number = le32toh(temp->inode_number);
+ temp->start_block = le32toh(temp->start_block);
+ temp->fragment = le32toh(temp->fragment);
+ temp->offset = le32toh(temp->offset);
+ temp->file_size = le32toh(temp->file_size);
+}
+
+void
+swapendian_lreg_inode(struct sqsh_lreg_inode *temp)
+{
+ temp->inode_type = le16toh(temp->inode_type);
+ temp->mode = le16toh(temp->mode);
+ temp->uid = le16toh(temp->uid);
+ temp->guid = le16toh(temp->guid);
+ temp->mtime = le32toh(temp->mtime);
+ temp->inode_number = le32toh(temp->inode_number);
+ temp->start_block = le64toh(temp->start_block);
+ temp->file_size = le64toh(temp->file_size);
+ temp->sparse = le64toh(temp->sparse);
+ temp->nlink = le32toh(temp->nlink);
+ temp->fragment = le32toh(temp->fragment);
+ temp->offset = le32toh(temp->offset);
+ temp->xattr = le32toh(temp->xattr);
+}
+
+void
+swapendian_dir_inode(struct sqsh_dir_inode *temp)
+{
+ temp->inode_type = le16toh(temp->inode_type);
+ temp->mode = le16toh(temp->mode);
+ temp->uid = le16toh(temp->uid);
+ temp->guid = le16toh(temp->guid);
+ temp->mtime = le32toh(temp->mtime);
+ temp->inode_number = le32toh(temp->inode_number);
+ temp->start_block = le32toh(temp->start_block);
+ temp->nlink = le32toh(temp->nlink);
+ temp->file_size = le16toh(temp->file_size);
+ temp->offset = le16toh(temp->offset);
+ temp->parent_inode = le32toh(temp->parent_inode);
+}
+
+void
+swapendian_ldir_inode(struct sqsh_ldir_inode *temp)
+{
+ temp->inode_type = le16toh(temp->inode_type);
+ temp->mode = le16toh(temp->mode);
+ temp->uid = le16toh(temp->uid);
+ temp->guid = le16toh(temp->guid);
+ temp->mtime = le32toh(temp->mtime);
+ temp->inode_number = le32toh(temp->inode_number);
+ temp->nlink = le32toh(temp->nlink);
+ temp->file_size = le32toh(temp->file_size);
+ temp->start_block = le32toh(temp->start_block);
+ temp->parent_inode = le32toh(temp->parent_inode);
+ temp->i_count = le16toh(temp->i_count);
+ temp->offset = le16toh(temp->offset);
+ temp->xattr = le32toh(temp->xattr);
+}
diff --git a/sys/fs/squashfs/squashfs_io.h b/sys/fs/squashfs/squashfs_io.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_io.h
@@ -0,0 +1,38 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef SQUASHFS_IO
+#define SQUASHFS_IO
+
+#include <squashfs_mount.h>
+
+ssize_t sqsh_io_read_buf(struct sqsh_mount *ump, void *buf, off_t off,
+ size_t len);
+
+#endif
diff --git a/sys/fs/squashfs/squashfs_io.c b/sys/fs/squashfs/squashfs_io.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_io.c
@@ -0,0 +1,163 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+
+#include <geom/geom.h>
+
+#include <squashfs.h>
+#include <squashfs_mount.h>
+#include <squashfs_io.h>
+
+sqsh_err sqsh_io_read(struct sqsh_mount *ump, struct uio *uiop);
+
+/*
+ * Reads data according to the provided uio.
+ * This function reads directly from disk file
+ * and all decompression reads are handled by seperate
+ * functions in squashfs_block.h file.
+ */
+sqsh_err
+sqsh_io_read(struct sqsh_mount *ump, struct uio *uiop)
+{
+ struct vnode *vp;
+ off_t off;
+ size_t len;
+ int error;
+
+ vp = ump->um_vp;
+
+ if (vp->v_type == VREG) {
+ void *rl;
+
+ off = uiop->uio_offset;
+ len = uiop->uio_resid;
+
+ rl = vn_rangelock_rlock(vp, off, off + len);
+ error = vn_lock(vp, LK_SHARED);
+ if (error == 0) {
+ error = VOP_READ(vp, uiop, IO_NODELOCKED,
+ uiop->uio_td->td_ucred);
+ }
+ VOP_UNLOCK(vp);
+ vn_rangelock_unlock(vp, rl);
+ } else {
+ struct buf *bp;
+ size_t sectorsz;
+ daddr_t lbn;
+ off_t trim;
+
+ sectorsz = ump->cp->provider->sectorsize;
+
+ off = uiop->uio_offset & ~PAGE_MASK;
+ trim = uiop->uio_offset - off;
+
+ do {
+ lbn = btodb(off);
+
+ len = MIN(MAXBSIZE,
+ roundup2(uiop->uio_resid + trim, sectorsz));
+ error = bread(vp, lbn, len, NOCRED, &bp);
+ if (error != 0)
+ break;
+
+ error = uiomove(bp->b_data + trim, len - trim, uiop);
+ brelse(bp);
+
+ off = uiop->uio_offset;
+ trim = 0;
+ } while (error == 0 && uiop->uio_resid > 0);
+ }
+
+ return (error != 0 ? SQFS_ERR : SQFS_OK);
+}
+
+/*
+ * Reads data into the provided buffer.
+ * This function reads directly from disk file
+ * and all decompression reads are handled by seperate
+ * functions in squashfs_block.h file.
+ * On succes it return number of bytes read else negative
+ * value on failure.
+ */
+ssize_t
+sqsh_io_read_buf(struct sqsh_mount *ump, void *buf, off_t off, size_t len)
+{
+ struct uio auio;
+ struct iovec aiov;
+ sqsh_err error;
+ ssize_t res;
+
+ /* return success and reading zero bytes of data */
+ if (len == 0)
+ return (0);
+
+ /* initialize iovec */
+ aiov.iov_base = buf;
+ aiov.iov_len = len;
+
+ /* initialize uio */
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = off;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_resid = len;
+ auio.uio_td = curthread;
+
+ error = sqsh_io_read(ump, &auio);
+
+ /* return negative value on reading failure */
+ if (error != SQFS_OK)
+ return (-1);
+
+ res = len - auio.uio_resid;
+
+ return (res);
+}
diff --git a/sys/fs/squashfs/squashfs_mount.h b/sys/fs/squashfs/squashfs_mount.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_mount.h
@@ -0,0 +1,73 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef SQUASHFS_MOUNT_H
+#define SQUASHFS_MOUNT_H
+
+#if defined(_KERNEL) || defined(_STANDALONE)
+
+/* This structure describes squashfs mount structure data */
+struct sqsh_mount {
+#ifdef _KERNEL
+ struct mount *um_mountp;
+ struct vnode *um_vp;
+#endif
+ struct sqsh_sb sb;
+ struct sqsh_table id_table;
+ struct sqsh_table frag_table;
+ struct sqsh_table export_table;
+ struct sqsh_table xattr_table;
+ struct sqsh_xattr_id_table xattr_info;
+#ifdef _KERNEL
+ struct g_consumer *cp;
+#endif
+ const struct sqsh_decompressor *decompressor;
+};
+
+#ifdef _KERNEL
+
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_SQUASHFS_NODE);
+#endif
+
+static inline struct sqsh_mount *
+MP_TO_SQSH_MOUNT(struct mount *mp)
+{
+ MPASS(mp != NULL && mp->mnt_data != NULL);
+ return (mp->mnt_data);
+}
+
+#endif /* _KERNEL */
+
+sqsh_err squashfs_init(struct sqsh_mount *);
+
+#endif /* _KERNEL || _STANDALONE */
+
+#endif /* SQUASHFS_MOUNT_H */
diff --git a/sys/fs/squashfs/squashfs_vfsops.c b/sys/fs/squashfs/squashfs_vfsops.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_vfsops.c
@@ -0,0 +1,407 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+
+#include <vm/vm_param.h>
+
+#include <geom/geom.h>
+#include <geom/geom_vfs.h>
+
+#include <squashfs.h>
+#include <squashfs_io.h>
+#include <squashfs_mount.h>
+#include <squashfs_inode.h>
+#include <squashfs_decompressor.h>
+#include <squashfs_xattr.h>
+
+MALLOC_DEFINE(M_SQUASHFS_NODE, "SQUASHFS inode", "SQUASHFS vnode private data");
+static MALLOC_DEFINE(M_SQUASHFSMNT, "SQUASHFS mount",
+ "SQUASHFS mount structure");
+
+static vfs_mount_t squashfs_mount;
+static vfs_unmount_t squashfs_unmount;
+static vfs_root_t squashfs_root;
+static vfs_statfs_t squashfs_statfs;
+static vfs_vget_t squashfs_vget;
+static vfs_fhtovp_t squashfs_fhtovp;
+
+static const char *squashfs_opts[] = {
+ "as",
+ "from",
+ NULL,
+};
+
+static const char *squashfs_updateopts[] = {
+ "from",
+ NULL,
+};
+
+/* VFS operations */
+static int
+squashfs_mount(struct mount *mp)
+{
+ struct nameidata nd;
+ struct sqsh_mount *ump = NULL;
+ struct vnode *vp;
+ struct thread *td = curthread;
+ char *from, *as;
+ struct g_consumer *cp;
+ int error, isverified, len, aslen;
+ accmode_t accmode;
+ sqsh_err err;
+
+ cp = NULL;
+ TRACE("squashfs_mount(mp = %p)\n", mp);
+
+ if (mp->mnt_flag & MNT_UPDATE) {
+ if (vfs_filteropt(mp->mnt_optnew, squashfs_updateopts))
+ return (EOPNOTSUPP);
+
+ /*
+ * We don't really allow any meaningful options at the moment,
+ * so just succeed. Perhaps later we'll allow, e.g., noexec.
+ */
+ return (EOPNOTSUPP);
+ }
+
+ if (vfs_filteropt(mp->mnt_optnew, squashfs_opts))
+ return (EINVAL);
+
+ /* Get argument */
+ error = vfs_getopt(mp->mnt_optnew, "from", (void **)&from, &len);
+ if (error != 0 || from[len - 1] != '\0')
+ return (EINVAL);
+ error = vfs_getopt(mp->mnt_optnew, "as", (void **)&as, &aslen);
+ if (error || as[aslen - 1] != '\0')
+ as = from;
+
+ /* find and initialise squashfs disk file vnode vp */
+ NDINIT(&nd, LOOKUP, ISOPEN | FOLLOW | LOCKLEAF, UIO_SYSSPACE, from);
+ error = namei(&nd);
+ if (error != 0)
+ return (error);
+ NDFREE_PNBUF(&nd);
+ vp = nd.ni_vp;
+ /* vp is now held and locked */
+
+ /* check if vnode is of file type (squashfs disk is always of regular
+ * file type) */
+ if (vp->v_type != VREG && !vn_isdisk(vp)) {
+ ERROR("Squashfs disk is not a regular file or disk");
+ error = EOPNOTSUPP;
+ vput(vp);
+ return (error);
+ }
+
+ /* check if file is not private */
+ accmode = VREAD;
+ error = VOP_ACCESS(vp, accmode, td->td_ucred, td);
+ if (error != 0)
+ error = priv_check(td, PRIV_VFS_MOUNT_PERM);
+ if (error != 0) {
+ ERROR("insufficient permissions to mount disk");
+ error = EOPNOTSUPP;
+ vput(vp);
+ return (error);
+ }
+
+ isverified = 0;
+ if (vp->v_type != VREG) {
+ struct cdev *dev;
+
+ dev = vp->v_rdev;
+
+ dev_ref(dev);
+ g_topology_lock();
+ error = g_vfs_open(vp, &cp, "squashfs", 0);
+ if (error == 0)
+ g_getattr("MNT::verified", cp, &isverified);
+ g_topology_unlock();
+
+ if (error != 0) {
+ ERROR("failed to open disk");
+ vput(vp);
+ return (error);
+ }
+ }
+ VOP_UNLOCK(vp);
+
+ /* Create squashfs mount */
+ ump = SQUASHFS_MALLOC(sizeof(struct sqsh_mount), M_SQUASHFSMNT,
+ M_WAITOK | M_ZERO);
+ ump->um_mountp = mp;
+ ump->um_vp = vp;
+ ump->cp = cp;
+
+ err = squashfs_init(ump);
+
+ switch (err) {
+ case SQFS_OK:
+ break;
+ case SQFS_BADFORMAT:
+ ERROR("Wrong squashfs image");
+ break;
+ case SQFS_BADVERSION:
+ ERROR("Squashfs 4.0 to 4.%d is supported", SQUASHFS_MINOR);
+ break;
+ case SQFS_BADCOMP:
+ break;
+ default:
+ ERROR(
+ "Some unknown error happend while mounting squashfs image");
+ }
+
+ if (err != SQFS_OK)
+ goto failed_mount;
+
+ mp->mnt_data = ump;
+ mp->mnt_iosize_max = vp->v_mount->mnt_iosize_max;
+
+ /* Unconditionally mount squashfs as read only */
+ MNT_ILOCK(mp);
+ mp->mnt_flag |= (MNT_LOCAL | MNT_RDONLY);
+ if (isverified)
+ mp->mnt_flag |= MNT_VERIFIED;
+ MNT_IUNLOCK(mp);
+
+ vfs_getnewfsid(mp);
+ vfs_mountedfrom(mp, as);
+ TRACE("Squashfs mount successful");
+ return (0);
+
+failed_mount:
+ TRACE("Squashfs mount failed");
+ if (cp != NULL) {
+ g_topology_lock();
+ g_vfs_close(cp);
+ g_topology_unlock();
+ }
+ if (vp->v_type != VREG)
+ dev_rel(vp->v_rdev);
+ vrele(vp);
+ SQUASHFS_FREE(ump, M_SQUASHFSMNT);
+ return (EINVAL);
+}
+
+static int
+squashfs_unmount(struct mount *mp, int mntflags)
+{
+ TRACE("%s:", __func__);
+ struct sqsh_mount *ump;
+ struct vnode *vp;
+ int flags;
+ int error;
+
+ flags = 0;
+
+ /* Handle forced unmounts */
+ if (mntflags & MNT_FORCE)
+ flags |= FORCECLOSE;
+
+ error = vflush(mp, 0, flags, curthread);
+ if (error != 0)
+ return (error);
+
+ ump = MP_TO_SQSH_MOUNT(mp);
+ vp = ump->um_vp;
+
+ if (ump->cp != NULL) {
+ g_topology_lock();
+ g_vfs_close(ump->cp);
+ g_topology_unlock();
+ ump->cp = NULL;
+
+ dev_rel(vp->v_rdev);
+ }
+
+ vrele(vp);
+
+ /* destroy fs internals */
+ sqsh_free_table(&ump->id_table);
+ sqsh_free_table(&ump->frag_table);
+ if (sqsh_export_ok(ump))
+ sqsh_free_table(&ump->export_table);
+
+ SQUASHFS_FREE(ump, M_SQUASHFSMNT);
+ mp->mnt_data = NULL;
+ TRACE("%s: completed", __func__);
+
+ return (0);
+}
+
+static int
+squashfs_root(struct mount *mp, int flags, struct vnode **vpp)
+{
+ TRACE("%s:", __func__);
+ struct vnode *nvp;
+ struct sqsh_mount *ump;
+ int error;
+
+ ump = MP_TO_SQSH_MOUNT(mp);
+
+ error = VFS_VGET(mp, sqsh_root_inode(ump), LK_EXCLUSIVE, &nvp);
+ if (error != 0)
+ return (error);
+
+ nvp->v_vflag |= VV_ROOT;
+ *vpp = nvp;
+ TRACE("%s: completed", __func__);
+ return (0);
+}
+
+static int
+squashfs_statfs(struct mount *mp, struct statfs *sbp)
+{
+ struct sqsh_mount *ump;
+
+ TRACE("%s:", __func__);
+
+ ump = MP_TO_SQSH_MOUNT(mp);
+
+ sbp->f_bsize = ump->sb.block_size;
+ sbp->f_iosize = PAGE_SIZE;
+ sbp->f_blocks = ump->sb.bytes_used / ump->sb.block_size;
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = ump->sb.inodes;
+ sbp->f_ffree = 0;
+
+ return (0);
+}
+
+static int
+squashfs_vget(struct mount *mp, ino_t ino, int lkflags, struct vnode **vpp)
+{
+ TRACE("%s:", __func__);
+ struct sqsh_mount *ump;
+ struct sqsh_inode *inode;
+ struct thread *td;
+ struct vnode *vp;
+ int error;
+ sqsh_err err;
+
+ td = curthread;
+ error = vfs_hash_get(mp, ino, lkflags, td, vpp, NULL, NULL);
+ if (error || *vpp != NULL)
+ return (error);
+
+ ump = MP_TO_SQSH_MOUNT(mp);
+ inode = SQUASHFS_MALLOC(sizeof(struct sqsh_inode), M_SQUASHFS_NODE,
+ M_WAITOK | M_ZERO);
+
+ /* populate inode data as per inode number */
+ err = sqsh_get_inode(ump, inode, ino);
+ if (err != SQFS_OK) {
+ *vpp = NULL;
+ SQUASHFS_FREE(inode, M_SQUASHFS_NODE);
+ return (EINVAL);
+ }
+
+ error = getnewvnode("squashfs", mp, &squashfs_vnodeops, &vp);
+ if (error != 0) {
+ *vpp = NULL;
+ SQUASHFS_FREE(inode, M_SQUASHFS_NODE);
+ return (error);
+ }
+
+ vp->v_data = inode;
+ vp->v_type = inode->type;
+ inode->vnode = vp;
+
+ lockmgr(vp->v_vnlock, lkflags, NULL);
+ error = insmntque(vp, mp);
+ if (error != 0) {
+ *vpp = NULL;
+ SQUASHFS_FREE(inode, M_SQUASHFS_NODE);
+ return (error);
+ }
+ error = vfs_hash_insert(vp, ino, lkflags, td, vpp, NULL, NULL);
+ if (error != 0 || *vpp != NULL)
+ return (error);
+
+ /* vn_set_state(vp, VSTATE_CONSTRUCTED); */
+ TRACE("%s: completed", __func__);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+squashfs_fhtovp(struct mount *mp, struct fid *fhp, int flags,
+ struct vnode **vpp)
+{
+ TRACE("%s:", __func__);
+ struct sqsh_fid *tfp;
+ struct vnode *vp;
+ int error;
+
+ tfp = (struct sqsh_fid *)fhp;
+
+ error = VFS_VGET(mp, tfp->ino, LK_EXCLUSIVE, &vp);
+ if (error != 0) {
+ *vpp = NULL;
+ return (error);
+ }
+ /* TODO : add checks for inode */
+
+ *vpp = vp;
+ TRACE("%s: completed", __func__);
+ return (0);
+}
+
+static struct vfsops squashfs_vfsops = {
+ .vfs_fhtovp = squashfs_fhtovp,
+ .vfs_mount = squashfs_mount,
+ .vfs_root = squashfs_root,
+ .vfs_statfs = squashfs_statfs,
+ .vfs_unmount = squashfs_unmount,
+ .vfs_vget = squashfs_vget,
+};
+
+VFS_SET(squashfs_vfsops, squashfs, VFCF_READONLY);
+MODULE_VERSION(squashfs, 1);
+MODULE_DEPEND(squashfs, xz, 1, 1, 1);
diff --git a/sys/fs/squashfs/squashfs_vnops.c b/sys/fs/squashfs/squashfs_vnops.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_vnops.c
@@ -0,0 +1,813 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/extattr.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/kdb.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/dirent.h>
+#include <sys/proc.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/vnode_pager.h>
+
+#include <squashfs.h>
+#include <squashfs_mount.h>
+#include <squashfs_inode.h>
+#include <squashfs_dir.h>
+#include <squashfs_file.h>
+#include <squashfs_xattr.h>
+
+static int
+squashfs_open(struct vop_open_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_inode *inode;
+ struct vnode *vp;
+
+ vp = ap->a_vp;
+ MPASS(VOP_ISLOCKED(vp));
+ inode = vp->v_data;
+
+ if (vp->v_type != VREG && vp->v_type != VDIR)
+ return (EOPNOTSUPP);
+
+ vnode_create_vobject(vp, inode->size, ap->a_td);
+ return (0);
+}
+
+static int
+squashfs_close(struct vop_close_args *ap)
+{
+ TRACE("%s:", __func__);
+ return (0);
+}
+
+static int
+squashfs_access(struct vop_access_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_inode *inode;
+ struct vnode *vp;
+ accmode_t accmode;
+ struct ucred *cred;
+ int error;
+
+ vp = ap->a_vp;
+ accmode = ap->a_accmode;
+ cred = ap->a_cred;
+
+ MPASS(VOP_ISLOCKED(vp));
+ inode = vp->v_data;
+
+ switch (vp->v_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ if ((accmode & VWRITE) != 0)
+ return (EROFS);
+ break;
+ case VBLK:
+ case VCHR:
+ case VFIFO:
+ case VSOCK:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((accmode & VWRITE) != 0)
+ return (EPERM);
+
+ error = vaccess(vp->v_type, inode->base.mode, inode->base.uid,
+ inode->base.guid, accmode, cred);
+
+ return (error);
+}
+
+static int
+squashfs_bmap(struct vop_bmap_args *ap)
+{
+ struct vnode *vp;
+ struct sqsh_inode *inode;
+ uint64_t ahead, behind, iosize;
+ int rmax;
+
+ vp = ap->a_vp;
+ inode = vp->v_data;
+ if (ap->a_bop != NULL)
+ *ap->a_bop = &ap->a_vp->v_bufobj;
+ if (ap->a_bnp == NULL)
+ return (0);
+
+ iosize = vp->v_mount->mnt_stat.f_iosize;
+
+ *ap->a_bnp = ap->a_bn * btodb(iosize);
+ if (ap->a_runb == NULL)
+ return (0);
+
+ ahead = behind = 0;
+
+ /*
+ * Punt on sparse files for now; we need to poll the block index to
+ * determine how large the hole that we're in is, but that's still on
+ * disk because we don't cache it at all.
+ */
+ if (inode->xtra.reg.sparse == 0) {
+ off_t nextblk, off;
+ uint64_t blksz;
+
+ blksz = inode->ump->sb.block_size;
+
+ off = ap->a_bn * iosize;
+
+ /*
+ * All of the file data is contiguous on the disk, with
+ * exception to the very end of the file if it's not a multiple
+ * of the block size.
+ */
+ nextblk = roundup2(off, blksz);
+ if (inode->xtra.reg.frag_idx == SQUASHFS_INVALID_FRAG) {
+ /* No fragment - read on ahead. */
+ nextblk = MIN(inode->size, nextblk);
+ } else {
+ off_t startoff;
+
+ /*
+ * Fragment - read up to the last block, unless we are
+ * in the middle of the fragment.
+ */
+ startoff = rounddown2(inode->size, blksz);
+ if (off > startoff) {
+ /*
+ * In the fragment, we can only read behind to
+ * the start of the fragment or read ahead to
+ * the end of the file.
+ */
+ startoff = 0;
+ nextblk = MIN(inode->size, nextblk);
+ } else {
+ nextblk = MIN(startoff, nextblk);
+ }
+ }
+
+ behind = howmany(off - rounddown2(off, blksz), iosize);
+ if (nextblk - iosize < off)
+ ahead = 0;
+ else
+ ahead = howmany(nextblk - (off + iosize), iosize);
+ }
+
+ rmax = vp->v_mount->mnt_iosize_max / iosize - 1;
+
+ *ap->a_runb = imin(rmax, behind);
+ *ap->a_runp = imin(rmax, ahead);
+
+ return (0);
+}
+
+static int
+squashfs_getattr(struct vop_getattr_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_inode *inode;
+ struct vnode *vp;
+ struct vattr *vap;
+
+ vp = ap->a_vp;
+ vap = ap->a_vap;
+ inode = vp->v_data;
+
+ /* fill up vattr for squashfs inode */
+ vap->va_type = vp->v_type;
+ vap->va_mode = inode->base.mode;
+ vap->va_nlink = inode->nlink;
+ vap->va_gid = inode->base.guid;
+ vap->va_uid = inode->base.uid;
+ if (sqsh_get_inode_id(inode->ump, inode->base.guid, &vap->va_gid) !=
+ SQFS_OK)
+ return (EINVAL);
+
+ if (sqsh_get_inode_id(inode->ump, inode->base.uid, &vap->va_uid) !=
+ SQFS_OK)
+ return (EINVAL);
+
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_fileid = inode->base.inode_number;
+ vap->va_size = inode->size;
+ vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
+ vap->va_atime.tv_sec = inode->base.mtime;
+ vap->va_atime.tv_nsec = 0;
+ vap->va_ctime.tv_sec = inode->base.mtime;
+ vap->va_ctime.tv_nsec = 0;
+ vap->va_mtime.tv_sec = inode->base.mtime;
+ vap->va_mtime.tv_nsec = 0;
+ vap->va_birthtime.tv_sec = inode->base.mtime;
+ vap->va_birthtime.tv_nsec = 0;
+ vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
+ inode->xtra.dev.major :
+ NODEV;
+ vap->va_bytes = inode->size;
+ vap->va_filerev = 0;
+ vap->va_flags = 0;
+
+ return (0);
+}
+
+static int
+squashfs_read(struct vop_read_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_mount *ump;
+ struct sqsh_inode *inode;
+ struct uio *uiop;
+ struct vnode *vp;
+ off_t resid;
+ size_t len;
+ sqsh_err err;
+
+ uiop = ap->a_uio;
+ vp = ap->a_vp;
+
+ if (vp->v_type == VCHR || vp->v_type == VBLK)
+ return (EOPNOTSUPP);
+
+ if (vp->v_type != VREG)
+ return (EISDIR);
+
+ if (uiop->uio_offset < 0)
+ return (EINVAL);
+
+ inode = vp->v_data;
+ ump = inode->ump;
+ err = SQFS_OK;
+
+ while ((resid = uiop->uio_resid) > 0) {
+ if (inode->size <= uiop->uio_offset)
+ break;
+ len = MIN(inode->size - uiop->uio_offset, resid);
+ if (len == 0)
+ break;
+
+ err = sqsh_read_file(ump, inode, uiop->uio_offset, &len, uiop);
+ if (err != SQFS_OK)
+ break;
+ }
+
+ if (err != SQFS_OK)
+ return (EINVAL);
+
+ return (0);
+}
+
+static int
+squashfs_lookup(struct vop_cachedlookup_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_mount *ump;
+ struct sqsh_inode *inode;
+ struct sqsh_inode *child_inode;
+ struct componentname *cnp;
+ struct vnode *dvp, **vpp;
+ int error;
+ sqsh_err err;
+
+ dvp = ap->a_dvp;
+ vpp = ap->a_vpp;
+ cnp = ap->a_cnp;
+
+ *vpp = NULLVP;
+ inode = dvp->v_data;
+ ump = inode->ump;
+
+ error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, curthread);
+ if (error != 0)
+ return (error);
+
+ if (cnp->cn_flags & ISDOTDOT) {
+ /* Do not allow .. on the root node */
+ if (inode->xtra.dir.parent_inode == ump->sb.inodes + 1 ||
+ inode->xtra.dir.parent_inode == 0)
+ return (ENOENT);
+
+ /* Allocate a new vnode on the matching entry */
+ error = vn_vget_ino(dvp, inode->parent_id, cnp->cn_lkflags,
+ vpp);
+ if (error != 0)
+ return (error);
+ } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
+ VREF(dvp);
+ *vpp = dvp;
+ } else {
+ struct sqsh_dir_entry entry;
+ bool found;
+
+ found = false;
+
+ /* Lookup for entry in directory, if found populate entry */
+ err = sqsh_dir_lookup(ump, inode, cnp->cn_nameptr,
+ cnp->cn_namelen, &entry, &found);
+ if (err != SQFS_OK)
+ return (EINVAL);
+
+ if (found == false)
+ return (ENOENT);
+
+ error = VFS_VGET(ump->um_mountp, entry.inode_id,
+ cnp->cn_lkflags, vpp);
+ if (error != 0)
+ return (error);
+ child_inode = (*vpp)->v_data;
+ child_inode->parent_id = inode->ino_id;
+ }
+
+ /* Store the result the the cache if MAKEENTRY is specified in flags */
+ if ((cnp->cn_flags & MAKEENTRY) != 0 && cnp->cn_nameiop != CREATE)
+ cache_enter(dvp, *vpp, cnp);
+
+ return (error);
+}
+
+static int
+squashfs_readdir(struct vop_readdir_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_mount *ump;
+ struct dirent cde = { 0 };
+ struct sqsh_inode *inode;
+ struct vnode *vp;
+ struct uio *uio;
+ int *eofflag;
+#if 0
+ uint64_t **cookies;
+ int *ncookies;
+ off_t off;
+#endif
+ u_int ndirents;
+ int error;
+ sqsh_err err = SQFS_OK;
+
+ vp = ap->a_vp;
+ uio = ap->a_uio;
+ eofflag = ap->a_eofflag;
+#if 0
+ cookies = ap->a_cookies;
+ ncookies = ap->a_ncookies;
+#endif
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ inode = vp->v_data;
+ ump = inode->ump;
+#if 0
+ off = uio->uio_offset;
+#endif
+ ndirents = 0;
+
+ error = 0;
+ if (uio->uio_offset == SQUASHFS_COOKIE_EOF)
+ return (0);
+
+ if (uio->uio_offset == SQUASHFS_COOKIE_DOT) {
+ /* fake . entry */
+ cde.d_fileno = inode->base.inode_number;
+ cde.d_type = DT_DIR;
+ cde.d_namlen = 1;
+ cde.d_name[0] = '.';
+ cde.d_name[1] = '\0';
+ cde.d_reclen = GENERIC_DIRSIZ(&cde);
+ if (cde.d_reclen > uio->uio_resid)
+ goto full;
+ dirent_terminate(&cde);
+ error = uiomove(&cde, cde.d_reclen, uio);
+ if (error)
+ return (error);
+ /* next is .. */
+ uio->uio_offset = SQUASHFS_COOKIE_DOTDOT;
+ ndirents++;
+ }
+
+ if (uio->uio_offset == SQUASHFS_COOKIE_DOTDOT) {
+ /* fake .. entry */
+ /* Get inode number of parent inode */
+
+ cde.d_fileno = inode->xtra.dir.parent_inode;
+ cde.d_type = DT_DIR;
+ cde.d_namlen = 2;
+ cde.d_name[0] = '.';
+ cde.d_name[1] = '.';
+ cde.d_name[2] = '\0';
+ cde.d_reclen = GENERIC_DIRSIZ(&cde);
+ if (cde.d_reclen > uio->uio_resid)
+ goto full;
+ dirent_terminate(&cde);
+ error = uiomove(&cde, cde.d_reclen, uio);
+ if (error)
+ return (error);
+ /* next is first child */
+ err = sqsh_dir_getnext(ump, &inode->xtra.dir.d,
+ &inode->xtra.dir.entry);
+ if (err == SQFS_END_OF_DIRECTORY)
+ goto done;
+ if (err != SQFS_OK) {
+ error = EINVAL;
+ goto done;
+ }
+ uio->uio_offset = inode->xtra.dir.entry.inode_id;
+ ndirents++;
+ }
+
+ for (;;) {
+ __enum_uint8(vtype) type;
+
+ cde.d_fileno = inode->xtra.dir.entry.inode_number;
+ type = sqsh_inode_type_from_id(ump,
+ inode->xtra.dir.entry.inode_id);
+ switch (type) {
+ case VBLK:
+ cde.d_type = DT_BLK;
+ break;
+ case VCHR:
+ cde.d_type = DT_CHR;
+ break;
+ case VDIR:
+ cde.d_type = DT_DIR;
+ break;
+ case VFIFO:
+ cde.d_type = DT_FIFO;
+ break;
+ case VLNK:
+ cde.d_type = DT_LNK;
+ break;
+ case VREG:
+ cde.d_type = DT_REG;
+ break;
+ default:
+ panic("%s: inode_type %d\n", __func__, type);
+ }
+ cde.d_namlen = inode->xtra.dir.entry.name_size;
+ /* XXX Really crash? */
+ MPASS(inode->xtra.dir.entry.name_size < sizeof(cde.d_name));
+ (void)memcpy(cde.d_name, inode->xtra.dir.entry.name,
+ inode->xtra.dir.entry.name_size);
+ cde.d_name[inode->xtra.dir.entry.name_size] = '\0';
+ cde.d_reclen = GENERIC_DIRSIZ(&cde);
+ if (cde.d_reclen > uio->uio_resid)
+ goto full;
+ dirent_terminate(&cde);
+ error = uiomove(&cde, cde.d_reclen, uio);
+ if (error != 0)
+ goto done;
+ ndirents++;
+ /* next sibling */
+ err = sqsh_dir_getnext(ump, &inode->xtra.dir.d,
+ &inode->xtra.dir.entry);
+ if (err == SQFS_END_OF_DIRECTORY)
+ goto done;
+ if (err != SQFS_OK) {
+ error = EINVAL;
+ goto done;
+ }
+ uio->uio_offset = inode->xtra.dir.entry.inode_id;
+ }
+
+full:
+ if (cde.d_reclen > uio->uio_resid)
+ error = (ndirents == 0) ? EINVAL : 0;
+done:
+ TRACE("%s: %u entries written\n", __func__, ndirents);
+
+ if (err == SQFS_END_OF_DIRECTORY) {
+ uio->uio_offset = SQUASHFS_COOKIE_EOF;
+ /* Restart the directory */
+ sqsh_dir_init(ump, inode, &inode->xtra.dir.d);
+ }
+
+ if (eofflag != NULL) {
+ TRACE("%s: Setting EOF flag\n", __func__);
+ *eofflag = (error == 0 &&
+ uio->uio_offset == SQUASHFS_COOKIE_EOF);
+ }
+
+ return (error);
+}
+
+static int
+squashfs_readlink(struct vop_readlink_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_inode *inode;
+ struct uio *uiop;
+ struct vnode *vp;
+ int error;
+ size_t want;
+ struct sqsh_block_run cur;
+ sqsh_err err;
+
+ uiop = ap->a_uio;
+ vp = ap->a_vp;
+
+ MPASS(uiop->uio_offset == 0);
+ MPASS(vp->v_type == VLNK);
+
+ inode = vp->v_data;
+
+ char buf[inode->size];
+
+ want = inode->size;
+ cur = inode->next;
+ err = sqsh_metadata_get(inode->ump, &cur, buf, want);
+ if (err != SQFS_OK)
+ return (EINVAL);
+
+ error = uiomove(buf, MIN(inode->size, uiop->uio_resid), uiop);
+
+ return (error);
+}
+
+static int
+squashfs_reclaim(struct vop_reclaim_args *ap)
+{
+ TRACE("%s:", __func__);
+ struct vnode *vp;
+
+ vp = ap->a_vp;
+
+ vfs_hash_remove(vp);
+
+ SQUASHFS_FREE(vp->v_data, M_SQUASHFS_NODE);
+ vp->v_data = NULL;
+
+ TRACE("%s: completed", __func__);
+ return (0);
+}
+
+static int
+squashfs_print(struct vop_print_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_inode *inode;
+ struct vnode *vp;
+
+ vp = ap->a_vp;
+ inode = vp->v_data;
+
+ printf("tag squashfs, squashfs_inode %p, links %lu\n", inode,
+ (unsigned long)inode->nlink);
+ printf("\tmode 0%o, owner %d, group %d, size %zd\n", inode->base.mode,
+ inode->base.uid, inode->base.guid, inode->size);
+
+ if (vp->v_type == VFIFO)
+ fifo_printinfo(vp);
+
+ printf("\n");
+
+ return (0);
+}
+
+static int
+squashfs_strategy(struct vop_strategy_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct sqsh_mount *ump;
+ struct vnode *vp;
+ struct sqsh_inode *inode;
+ struct buf *bp;
+ off_t off;
+ size_t len;
+ int error;
+ struct uio auio;
+ struct iovec iov;
+ sqsh_err err;
+
+ error = 0;
+ vp = ap->a_vp;
+ bp = ap->a_bp;
+ MPASS(bp->b_iocmd == BIO_READ);
+ MPASS(bp->b_iooffset >= 0);
+ MPASS(bp->b_bcount > 0);
+ MPASS(bp->b_bufsize >= bp->b_bcount);
+
+ iov.iov_base = bp->b_data;
+ iov.iov_len = bp->b_bcount;
+ inode = vp->v_data;
+ ump = inode->ump;
+ off = bp->b_iooffset;
+ len = bp->b_bcount;
+ bp->b_resid = len;
+
+ if (off > inode->size) {
+ error = EIO;
+ goto out;
+ }
+ if (off + len > inode->size)
+ len = inode->size - off;
+
+ auio.uio_iov = &iov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = off;
+ auio.uio_resid = len;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_td = curthread;
+
+ err = sqsh_read_file(ump, inode, off, &bp->b_resid, &auio);
+ if (err != SQFS_OK) {
+ error = EINVAL;
+ goto out;
+ }
+out:
+ if (error != 0) {
+ bp->b_ioflags |= BIO_ERROR;
+ bp->b_error = error;
+ }
+ bp->b_flags |= B_DONE;
+ return (0);
+}
+
+static int
+squashfs_vptofh(struct vop_vptofh_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct vnode *vp;
+ struct sqsh_fid *tfp;
+ struct sqsh_inode *inode;
+ sqsh_err err;
+
+ tfp = (struct sqsh_fid *)ap->a_fhp;
+ vp = ap->a_vp;
+ inode = vp->v_data;
+
+ uint64_t i_ino;
+ err = sqsh_export_inode(inode->ump, inode->base.inode_number, &i_ino);
+ if (err != SQFS_OK)
+ return (EINVAL);
+
+ tfp->len = sizeof(struct sqsh_fid);
+ tfp->ino = i_ino;
+
+ return (0);
+}
+
+static int
+squashfs_getextattr(struct vop_getextattr_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct vnode *vp;
+ struct sqsh_inode *inode;
+ struct sqsh_mount *ump;
+ sqsh_err err;
+ int error;
+
+ vp = ap->a_vp;
+ inode = vp->v_data;
+ ump = inode->ump;
+
+ error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred,
+ ap->a_td, VREAD);
+
+ if (error != 0)
+ return (error);
+
+ if (ap->a_size != NULL)
+ *ap->a_size = 0;
+
+ error = ENOATTR;
+
+ err = sqsh_xattr_lookup(ump, inode, ap->a_name, ap->a_uio, ap->a_size);
+
+ if (err != SQFS_OK || (ap->a_size != NULL && ap->a_size == 0))
+ return (error);
+
+ return (0);
+}
+
+static int
+squashfs_listextattr(struct vop_listextattr_args *ap)
+{
+ TRACE("%s:", __func__);
+
+ struct vnode *vp;
+ struct sqsh_inode *inode;
+ struct sqsh_mount *ump;
+ struct uio *uio;
+ struct sqsh_xattr attr;
+ sqsh_err err;
+ int error;
+
+ vp = ap->a_vp;
+ inode = vp->v_data;
+ ump = inode->ump;
+ uio = ap->a_uio;
+
+ error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred,
+ ap->a_td, VREAD);
+
+ if (error != 0)
+ return (error);
+
+ if (ap->a_size != NULL)
+ *ap->a_size = 0;
+
+ err = sqsh_xattr_open(ump, inode, &attr);
+ if (err != SQFS_OK)
+ return (ENOATTR);
+
+ while ((err = sqsh_xattr_read(&attr)) == SQFS_OK) {
+ size_t name_len = sqsh_xattr_name_size(&attr);
+ char name[name_len + 1];
+ name[0] = name_len;
+
+ if (ap->a_size != NULL)
+ *ap->a_size += name_len + 1;
+
+ err = sqsh_xattr_name(&attr, name + 1, false);
+ if (err != SQFS_OK)
+ return (ENOATTR);
+
+ error = uiomove(name, name_len + 1, uio);
+ if (error != 0)
+ return (error);
+ }
+
+ if (err == SQFS_ERR)
+ return (ENOATTR);
+
+ return (0);
+}
+
+struct vop_vector squashfs_vnodeops = {
+ .vop_default = &default_vnodeops,
+
+ .vop_access = squashfs_access,
+ .vop_bmap = squashfs_bmap,
+ .vop_cachedlookup = squashfs_lookup,
+ .vop_close = squashfs_close,
+ .vop_getattr = squashfs_getattr,
+ .vop_lookup = vfs_cache_lookup,
+ .vop_open = squashfs_open,
+ .vop_print = squashfs_print,
+ .vop_read = squashfs_read,
+ .vop_readdir = squashfs_readdir,
+ .vop_readlink = squashfs_readlink,
+ .vop_reclaim = squashfs_reclaim,
+ .vop_strategy = squashfs_strategy,
+ .vop_vptofh = squashfs_vptofh,
+ .vop_getextattr = squashfs_getextattr,
+ .vop_listextattr = squashfs_listextattr,
+};
+
+VFS_VOP_VECTOR_REGISTER(squashfs_vnodeops);
diff --git a/sys/fs/squashfs/squashfs_xattr.h b/sys/fs/squashfs/squashfs_xattr.h
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_xattr.h
@@ -0,0 +1,81 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef SQUASHFS_XATTR_H
+#define SQUASHFS_XATTR_H
+
+struct sqsh_prefix {
+ const char *pref;
+ size_t len;
+};
+
+static struct sqsh_prefix sqsh_xattr_prefixes[] = {
+ { "user.", 5 },
+ { "trusted.", 8 },
+ { "security.", 9 },
+};
+
+typedef enum { CURS_VSIZE = 1, CURS_VAL = 2, CURS_NEXT = 4 } sqsh_xattr_curs;
+
+struct sqsh_xattr {
+ struct sqsh_mount *ump;
+ int cursors;
+ struct sqsh_block_run c_name;
+ struct sqsh_block_run c_vsize;
+ struct sqsh_block_run c_val;
+ struct sqsh_block_run c_next;
+ size_t remain;
+ struct sqsh_xattr_id info;
+ uint16_t type;
+ bool ool;
+ struct sqsh_xattr_entry entry;
+ struct sqsh_xattr_val val;
+};
+
+sqsh_err sqsh_init_xattr(struct sqsh_mount *ump);
+
+sqsh_err sqsh_xattr_open(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_xattr *x);
+
+sqsh_err sqsh_xattr_read(struct sqsh_xattr *x);
+
+/* Helper functions on sqsh_xattr */
+size_t sqsh_xattr_name_size(struct sqsh_xattr *x);
+sqsh_err sqsh_xattr_name(struct sqsh_xattr *x, char *name, bool prefix);
+sqsh_err sqsh_xattr_value_size(struct sqsh_xattr *x, size_t *size);
+sqsh_err sqsh_xattr_value(struct sqsh_xattr *x, void *buf);
+
+static sqsh_err sqsh_xattr_find_prefix(const char *name, uint16_t *type);
+
+sqsh_err sqsh_xattr_find(struct sqsh_xattr *x, const char *name, bool *found);
+sqsh_err sqsh_xattr_lookup(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ const char *name, struct uio *uio, size_t *size);
+
+#endif
\ No newline at end of file
diff --git a/sys/fs/squashfs/squashfs_xattr.c b/sys/fs/squashfs/squashfs_xattr.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/squashfs/squashfs_xattr.c
@@ -0,0 +1,369 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Raghav Sharma <raghav@freebsd.org>
+ * Parts Copyright (c) 2014 Dave Vasilevsky <dave@vasilevsky.ca>
+ * Obtained from the squashfuse project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_squashfs.h"
+
+#include <sys/param.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+#else
+#include <stdlib.h>
+#endif
+
+#include <squashfs.h>
+#include <squashfs_io.h>
+#include <squashfs_mount.h>
+#include <squashfs_inode.h>
+#include <squashfs_block.h>
+#include <squashfs_xattr.h>
+
+#ifdef _KERNEL
+static MALLOC_DEFINE(M_SQUASHFSEXT, "SQUASHFS xattrs",
+ "SQUASHFS Extended attributes");
+#endif
+
+void swapendian_xattr_id_table(struct sqsh_xattr_id_table *temp);
+void swapendian_xattr_id(struct sqsh_xattr_id *temp);
+void swapendian_xattr_entry(struct sqsh_xattr_entry *temp);
+void swapendian_xattr_value(struct sqsh_xattr_val *temp);
+
+sqsh_err
+sqsh_init_xattr(struct sqsh_mount *ump)
+{
+ off_t start;
+ size_t data_read;
+
+ start = ump->sb.xattr_id_table_start;
+ if (start == SQUASHFS_INVALID_BLK)
+ return SQFS_OK;
+ data_read = sqsh_io_read_buf(ump, &ump->xattr_info,
+ sizeof(ump->xattr_info), start);
+ if (data_read != sizeof(ump->xattr_info))
+ return SQFS_ERR;
+ swapendian_xattr_id_table(&ump->xattr_info);
+ return sqsh_init_table(&ump->xattr_table, ump,
+ start + sizeof(ump->xattr_info), sizeof(struct sqsh_xattr_id),
+ ump->xattr_info.xattr_ids);
+}
+
+sqsh_err
+sqsh_xattr_open(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ struct sqsh_xattr *x)
+{
+ sqsh_err err;
+
+ x->remain = 0;
+ if (ump->xattr_info.xattr_ids == 0 ||
+ inode->xattr == SQUASHFS_INVALID_XATTR)
+ return SQFS_OK;
+
+ err = sqsh_get_table(&ump->xattr_table, ump, inode->xattr, &x->info);
+ if (err != SQFS_OK)
+ return SQFS_ERR;
+ swapendian_xattr_id(&x->info);
+
+ sqsh_metadata_run_inode(&x->c_next, x->info.xattr,
+ ump->xattr_info.xattr_table_start);
+
+ x->ump = ump;
+ x->remain = x->info.count;
+ x->cursors = CURS_NEXT;
+ return SQFS_OK;
+}
+
+sqsh_err
+sqsh_xattr_read(struct sqsh_xattr *x)
+{
+ sqsh_err err;
+
+ if (x->remain == 0)
+ return SQFS_END_OF_DIRECTORY;
+
+ if (!(x->cursors & CURS_NEXT)) {
+ x->ool = false;
+ err = sqsh_xattr_value(x, NULL);
+ if (err != SQFS_OK)
+ return err;
+ }
+
+ x->c_name = x->c_next;
+ err = sqsh_metadata_get(x->ump, &x->c_name, &x->entry,
+ sizeof(x->entry));
+ if (err != SQFS_OK)
+ return err;
+ swapendian_xattr_entry(&x->entry);
+
+ x->type = x->entry.type & SQUASHFS_XATTR_PREFIX_MASK;
+ x->ool = x->entry.type & SQUASHFS_XATTR_VALUE_OOL;
+ if (x->type > SQFS_XATTR_PREFIX_MAX)
+ return SQFS_ERR;
+
+ --(x->remain);
+ x->cursors = 0;
+ return err;
+}
+
+size_t
+sqsh_xattr_name_size(struct sqsh_xattr *x)
+{
+ return x->entry.size;
+}
+
+sqsh_err
+sqsh_xattr_name(struct sqsh_xattr *x, char *name, bool prefix)
+{
+ sqsh_err err;
+
+ if (name && prefix) {
+ struct sqsh_prefix *p = &sqsh_xattr_prefixes[x->type];
+ memcpy(name, p->pref, p->len);
+ name += p->len;
+ }
+
+ x->c_vsize = x->c_name;
+ err = sqsh_metadata_get(x->ump, &x->c_vsize, name, x->entry.size);
+ if (err != SQFS_OK)
+ return err;
+
+ x->cursors |= CURS_VSIZE;
+ return err;
+}
+
+sqsh_err
+sqsh_xattr_value_size(struct sqsh_xattr *x, size_t *size)
+{
+ sqsh_err err;
+
+ if (!(x->cursors & CURS_VSIZE)) {
+ err = sqsh_xattr_name(x, NULL, false);
+ if (err != SQFS_OK)
+ return err;
+ }
+
+ x->c_val = x->c_vsize;
+ err = sqsh_metadata_get(x->ump, &x->c_val, &x->val, sizeof(x->val));
+ if (err != SQFS_OK)
+ return err;
+ swapendian_xattr_value(&x->val);
+
+ if (x->ool) {
+ uint64_t pos;
+ x->c_next = x->c_val;
+ err = sqsh_metadata_get(x->ump, &x->c_next, &pos, sizeof(pos));
+ if (err != SQFS_OK)
+ return err;
+ pos = le64toh(pos);
+ x->cursors |= CURS_NEXT;
+
+ sqsh_metadata_run_inode(&x->c_val, pos,
+ x->ump->xattr_info.xattr_table_start);
+ err = sqsh_metadata_get(x->ump, &x->c_val, &x->val,
+ sizeof(x->val));
+ if (err != SQFS_OK)
+ return err;
+ swapendian_xattr_value(&x->val);
+ }
+
+ if (size)
+ *size = x->val.vsize;
+ x->cursors |= CURS_VAL;
+ return err;
+}
+
+sqsh_err
+sqsh_xattr_value(struct sqsh_xattr *x, void *buf)
+{
+ sqsh_err err;
+ struct sqsh_block_run c;
+
+ if (!(x->cursors & CURS_VAL)) {
+ err = sqsh_xattr_value_size(x, NULL);
+ if (err != SQFS_OK)
+ return err;
+ }
+
+ c = x->c_val;
+ err = sqsh_metadata_get(x->ump, &c, buf, x->val.vsize);
+ if (err != SQFS_OK)
+ return err;
+
+ if (!x->ool) {
+ x->c_next = c;
+ x->cursors |= CURS_NEXT;
+ }
+ return err;
+}
+
+static sqsh_err
+sqsh_xattr_find_prefix(const char *name, uint16_t *type)
+{
+ int i;
+ for (i = 0; i <= SQFS_XATTR_PREFIX_MAX; ++i) {
+ struct sqsh_prefix *p = &sqsh_xattr_prefixes[i];
+ if (strncmp(name, p->pref, p->len) == 0) {
+ *type = i;
+ return SQFS_OK;
+ }
+ }
+ return SQFS_ERR;
+}
+
+sqsh_err
+sqsh_xattr_find(struct sqsh_xattr *x, const char *name, bool *found)
+{
+ sqsh_err err;
+ char *cmp = NULL;
+ uint16_t type;
+ size_t len;
+
+ err = sqsh_xattr_find_prefix(name, &type);
+
+ if (err != SQFS_OK) {
+ *found = false;
+ return SQFS_OK;
+ }
+
+ name += sqsh_xattr_prefixes[type].len;
+ len = strlen(name);
+ cmp = SQUASHFS_MALLOC(len, M_SQUASHFSEXT, M_WAITOK | M_ZERO);
+
+ while (x->remain) {
+ err = sqsh_xattr_read(x);
+ if (err != SQFS_OK)
+ goto done;
+ if (x->type != type && x->entry.size != len)
+ continue;
+ err = sqsh_xattr_name(x, cmp, false);
+ if (err != SQFS_OK)
+ goto done;
+ if (strncmp(name, cmp, len) == 0) {
+ *found = true;
+ goto done;
+ }
+ }
+ *found = false;
+
+done:
+ SQUASHFS_FREE(cmp, M_SQUASHFSEXT);
+ return err;
+}
+
+sqsh_err
+sqsh_xattr_lookup(struct sqsh_mount *ump, struct sqsh_inode *inode,
+ const char *name, struct uio *uio, size_t *size)
+{
+ sqsh_err err;
+ bool found;
+ char *buf = NULL;
+
+ struct sqsh_xattr xattr;
+ err = sqsh_xattr_open(ump, inode, &xattr);
+ if (err != SQFS_OK)
+ return err;
+
+ found = false;
+ err = sqsh_xattr_find(&xattr, name, &found);
+ if (err != SQFS_OK)
+ return err;
+ if (!found) {
+ if (size != NULL)
+ *size = 0;
+ return err;
+ }
+
+ size_t real;
+ err = sqsh_xattr_value_size(&xattr, &real);
+ if (err != SQFS_OK)
+ return err;
+
+ buf = SQUASHFS_MALLOC(real, M_SQUASHFSEXT, M_WAITOK | M_ZERO);
+
+ if (buf) {
+ err = sqsh_xattr_value(&xattr, buf);
+ if (err != SQFS_OK) {
+ SQUASHFS_FREE(buf, M_SQUASHFSEXT);
+ return err;
+ }
+ }
+
+ if (size != NULL)
+ *size = real;
+ if (uiomove(buf, real, uio) != 0)
+ err = SQFS_ERR;
+
+ SQUASHFS_FREE(buf, M_SQUASHFSEXT);
+ return err;
+}
+
+void
+swapendian_xattr_id_table(struct sqsh_xattr_id_table *temp)
+{
+ temp->xattr_table_start = le64toh(temp->xattr_table_start);
+ temp->xattr_ids = le32toh(temp->xattr_ids);
+ temp->unused = le32toh(temp->unused);
+}
+
+void
+swapendian_xattr_id(struct sqsh_xattr_id *temp)
+{
+ temp->xattr = le64toh(temp->xattr);
+ temp->count = le32toh(temp->count);
+ temp->size = le32toh(temp->size);
+}
+
+void
+swapendian_xattr_entry(struct sqsh_xattr_entry *temp)
+{
+ temp->type = le16toh(temp->type);
+ temp->size = le16toh(temp->size);
+}
+
+void
+swapendian_xattr_value(struct sqsh_xattr_val *temp)
+{
+ temp->vsize = le32toh(temp->vsize);
+}
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -382,6 +382,7 @@
${_speaker} \
spi \
${_splash} \
+ squashfs \
ste \
stge \
${_sume} \
diff --git a/sys/modules/squashfs/Makefile b/sys/modules/squashfs/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/squashfs/Makefile
@@ -0,0 +1,33 @@
+.PATH: ${.CURDIR:H:H}/fs/squashfs
+
+KMOD= squashfs
+SRCS= \
+ opt_squashfs.h \
+ vnode_if.h \
+ squashfs_vfsops.c \
+ squashfs_vnops.c \
+ squashfs_decompressor.c \
+ squashfs_block.c \
+ squashfs_init.c \
+ squashfs_inode.c \
+ squashfs_io.c \
+ squashfs_dir.c \
+ squashfs_file.c \
+ squashfs_xattr.c
+
+.if !defined(KERNBUILDDIR)
+CFLAGS+= -DZSTDIO
+CFLAGS+= -DGZIO
+.ifdef SQUASHFS_DEBUG
+CFLAGS+= -DSQUASHFS_DEBUG
+.endif
+.endif
+
+SRCS+= opt_gzio.h
+SRCS+= opt_zstdio.h
+
+CFLAGS+= -I${SRCTOP}/sys/fs/squashfs
+CFLAGS+= -I${SRCTOP}/sys/contrib/zstd/lib/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/zlib
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Tue, Jun 23, 12:41 AM (5 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34221801
Default Alt Text
D50841.id157017.diff (128 KB)

Event Timeline