Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160188432
D50841.id157017.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
128 KB
Referenced Files
None
Subscribers
None
D50841.id157017.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D50841: fs/squashfs: Add the squashfs file system
Attached
Detach File
Event Timeline
Log In to Comment