git: 86d719ae68aa - main - loader: Add xzfs, like gzipfs but with xz.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 11 Jul 2026 19:30:25 UTC
The branch main has been updated by imp:
URL: https://cgit.FreeBSD.org/src/commit/?id=86d719ae68aa5f6db055e1fa7ede24b70e612ec7
commit 86d719ae68aa5f6db055e1fa7ede24b70e612ec7
Author: Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-07-11 14:46:14 +0000
Commit: Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-07-11 19:28:47 +0000
loader: Add xzfs, like gzipfs but with xz.
This is just like gzipfs or bzipfs, except done with the newer xz
program. This is off by default for the moment.
Sponsored by: Netflix
---
stand/efi/loader/conf.c | 1 +
stand/i386/loader/conf.c | 3 +
stand/libsa/Makefile | 3 +-
stand/libsa/bzipfs.c | 5 +-
stand/libsa/gzipfs.c | 5 +-
stand/libsa/stand.h | 1 +
stand/libsa/xzfs.c | 309 ++++++++++++++++++++++++++++++++++++++++
stand/loader.mk | 4 +
stand/powerpc/ofw/conf.c | 3 +
stand/uboot/arch/arm/conf.c | 3 +
stand/uboot/arch/powerpc/conf.c | 3 +
stand/userboot/userboot/conf.c | 1 +
12 files changed, 336 insertions(+), 5 deletions(-)
diff --git a/stand/efi/loader/conf.c b/stand/efi/loader/conf.c
index 0a0476873fe1..4f257dc5ba23 100644
--- a/stand/efi/loader/conf.c
+++ b/stand/efi/loader/conf.c
@@ -66,6 +66,7 @@ struct fs_ops *file_system[] = {
&nfs_fsops,
&gzipfs_fsops,
&bzipfs_fsops,
+ &xzfs_fsops,
NULL
};
diff --git a/stand/i386/loader/conf.c b/stand/i386/loader/conf.c
index bba33a2707d5..643990699ca1 100644
--- a/stand/i386/loader/conf.c
+++ b/stand/i386/loader/conf.c
@@ -87,6 +87,9 @@ struct fs_ops *file_system[] = {
#ifdef LOADER_BZIP2_SUPPORT
&bzipfs_fsops,
#endif
+#ifdef LOADER_XZ_SUPPORT
+ &xzfs_fsops,
+#endif
#ifdef LOADER_SPLIT_SUPPORT
&splitfs_fsops,
#endif
diff --git a/stand/libsa/Makefile b/stand/libsa/Makefile
index ab7a0f5a59f2..2bb33060ee2a 100644
--- a/stand/libsa/Makefile
+++ b/stand/libsa/Makefile
@@ -142,7 +142,7 @@ SRCS+= arp.c ether.c ip.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c
SRCS+= bootp.c rarp.c bootparam.c
# boot filesystems
-SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c
+SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c xzfs.c
SRCS+= dosfs.c ext2fs.c
SRCS+= splitfs.c
SRCS+= pkgfs.c
@@ -168,6 +168,7 @@ CFLAGS.ffs_subr.c+= -DSTANDALONE_SMALL
CFLAGS.gzipfs.c+= ${ZLIB_CFLAGS}
CFLAGS.pkgfs.c+= ${ZLIB_CFLAGS}
CFLAGS.bzipfs.c+= ${BZIP2_CFLAGS}
+CFLAGS.xzfs.c+= ${XZ_CFLAGS}
# explicit_bzero and calculate_crc32c
.PATH: ${SYSDIR}/libkern
diff --git a/stand/libsa/bzipfs.c b/stand/libsa/bzipfs.c
index ff7ec16e7dc6..f548475f32f2 100644
--- a/stand/libsa/bzipfs.c
+++ b/stand/libsa/bzipfs.c
@@ -158,9 +158,10 @@ bzf_open(const char *fname, struct open_file *f)
if (f->f_flags != F_READ)
return(EPERM);
- /* If the name already ends in .gz or .bz2, ignore it */
+ /* If the name already ends in a known compressed suffix, ignore it */
if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
- || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
+ || !strcmp(cp, ".bz2") || !strcmp(cp, ".xz")
+ || !strcmp(cp, ".zst") || !strcmp(cp, ".split")))
return(ENOENT);
/* Construct new name */
diff --git a/stand/libsa/gzipfs.c b/stand/libsa/gzipfs.c
index 6b22f750f3ef..c81335bc0321 100644
--- a/stand/libsa/gzipfs.c
+++ b/stand/libsa/gzipfs.c
@@ -167,9 +167,10 @@ zf_open(const char *fname, struct open_file *f)
if (f->f_flags != F_READ)
return(EPERM);
- /* If the name already ends in .gz or .bz2, ignore it */
+ /* If the name already ends in a known compressed suffix, ignore it */
if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
- || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
+ || !strcmp(cp, ".bz2") || !strcmp(cp, ".xz")
+ || !strcmp(cp, ".zst") || !strcmp(cp, ".split")))
return(ENOENT);
/* Construct new name */
diff --git a/stand/libsa/stand.h b/stand/libsa/stand.h
index 4f7f21867cea..3cb227ce2079 100644
--- a/stand/libsa/stand.h
+++ b/stand/libsa/stand.h
@@ -130,6 +130,7 @@ extern struct fs_ops nfs_fsops;
extern struct fs_ops cd9660_fsops;
extern struct fs_ops gzipfs_fsops;
extern struct fs_ops bzipfs_fsops;
+extern struct fs_ops xzfs_fsops;
extern struct fs_ops dosfs_fsops;
extern struct fs_ops ext2fs_fsops;
extern struct fs_ops splitfs_fsops;
diff --git a/stand/libsa/xzfs.c b/stand/libsa/xzfs.c
new file mode 100644
index 000000000000..8880203ddeb2
--- /dev/null
+++ b/stand/libsa/xzfs.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 1998 Michael Smith.
+ * All rights reserved.
+ * Copyright (c) 2026 Netflix, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Stacked filesystem for .xz compressed files, structured like gzipfs.c and
+ * bzipfs.c above it.
+ */
+
+#include "stand.h"
+
+#include <sys/stat.h>
+#include <string.h>
+#include <xz.h>
+
+#define XZ_BUFSIZE 2048 /* XXX larger? */
+
+struct xz_file
+{
+ int xzf_rawfd;
+ struct xz_dec *xzf_strm;
+ struct xz_buf xzf_buf;
+ unsigned char xzf_inbuf[XZ_BUFSIZE];
+ int xzf_endseen;
+ off_t xzf_total_out;
+};
+
+static int xzf_fill(struct xz_file *xzf);
+static int xzf_open(const char *path, struct open_file *f);
+static int xzf_close(struct open_file *f);
+static int xzf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
+static off_t xzf_seek(struct open_file *f, off_t offset, int where);
+static int xzf_stat(struct open_file *f, struct stat *sb);
+
+struct fs_ops xzfs_fsops = {
+ .fs_name = "xz",
+ .fs_flags = 0,
+ .fo_open = xzf_open,
+ .fo_close = xzf_close,
+ .fo_read = xzf_read,
+ .fo_write = null_write,
+ .fo_seek = xzf_seek,
+ .fo_stat = xzf_stat,
+ .fo_readdir = null_readdir,
+};
+
+static int
+xzf_fill(struct xz_file *xzf)
+{
+ int result;
+ int avail_in;
+ int req;
+
+ avail_in = xzf->xzf_buf.in_size - xzf->xzf_buf.in_pos;
+ req = XZ_BUFSIZE - avail_in;
+ result = 0;
+
+ /* If we need more */
+ if (req > 0) {
+ /* move old data to bottom of buffer */
+ if (avail_in > 0)
+ bcopy(xzf->xzf_inbuf + xzf->xzf_buf.in_pos, xzf->xzf_inbuf, avail_in);
+
+ /* read to fill buffer and update availibility data */
+ result = read(xzf->xzf_rawfd, xzf->xzf_inbuf + avail_in, req);
+ xzf->xzf_buf.in = xzf->xzf_inbuf;
+ xzf->xzf_buf.in_pos = 0;
+ xzf->xzf_buf.in_size = avail_in + (result >= 0 ? result : 0);
+ }
+ return (result);
+}
+
+static const unsigned char xz_magic[6] = {0xfd, '7', 'z', 'X', 'Z', 0x00};
+
+/*
+ * Peek at the fixed-size .xz magic without consuming it, so the header
+ * remains for xz_dec_run() to parse normally on the first read.
+ *
+ * Returns 0 if the header is OK, nonzero if not.
+ */
+static int
+check_header(struct xz_file *xzf)
+{
+ if (xzf->xzf_buf.in_size - xzf->xzf_buf.in_pos < (int)sizeof(xz_magic) &&
+ xzf_fill(xzf) == -1)
+ return (1);
+ if (xzf->xzf_buf.in_size - xzf->xzf_buf.in_pos < (int)sizeof(xz_magic))
+ return (1);
+ return (memcmp(xzf->xzf_buf.in + xzf->xzf_buf.in_pos, xz_magic,
+ sizeof(xz_magic)) != 0);
+}
+
+static int
+xzf_open(const char *fname, struct open_file *f)
+{
+ static char *xzfname;
+ int rawfd;
+ struct xz_file *xzf;
+ char *cp;
+ struct stat sb;
+
+ /* Have to be in "just read it" mode */
+ if (f->f_flags != F_READ)
+ return(EPERM);
+
+ /* If the name already ends in a known compressed suffix, ignore it */
+ if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
+ || !strcmp(cp, ".bz2") || !strcmp(cp, ".xz")
+ || !strcmp(cp, ".zst") || !strcmp(cp, ".split")))
+ return(ENOENT);
+
+ /* Construct new name */
+ xzfname = malloc(strlen(fname) + 4);
+ if (xzfname == NULL)
+ return(ENOMEM);
+ sprintf(xzfname, "%s.xz", fname);
+
+ /* Try to open the compressed datafile */
+ rawfd = open(xzfname, O_RDONLY);
+ free(xzfname);
+ if (rawfd == -1)
+ return(ENOENT);
+
+ if (fstat(rawfd, &sb) < 0) {
+ printf("xzf_open: stat failed\n");
+ close(rawfd);
+ return(ENOENT);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ printf("xzf_open: not a file\n");
+ close(rawfd);
+ return(EISDIR); /* best guess */
+ }
+
+ /* Allocate an xz_file structure, populate it */
+ xzf = malloc(sizeof(struct xz_file));
+ if (xzf == NULL) {
+ close(rawfd);
+ return(ENOMEM);
+ }
+ bzero(xzf, sizeof(struct xz_file));
+ xzf->xzf_rawfd = rawfd;
+ xzf->xzf_buf.in = xzf->xzf_inbuf;
+
+ /*
+ * The embedded xz decoder's internal CRC32/CRC64 tables start out
+ * zeroed and must be built before any stream can be decoded, or the
+ * Stream Header CRC32 check fails on the very first read.
+ */
+ xz_crc32_init();
+ xz_crc64_init();
+
+ /* Verify that the file is xz compressed */
+ if (check_header(xzf)) {
+ close(xzf->xzf_rawfd);
+ free(xzf);
+ return(EFTYPE);
+ }
+
+ /* Initialise the inflation engine */
+ xzf->xzf_strm = xz_dec_init(XZ_DYNALLOC, (uint32_t)-1);
+ if (xzf->xzf_strm == NULL) {
+ close(xzf->xzf_rawfd);
+ free(xzf);
+ return(EIO);
+ }
+
+ /* Looks OK, we'll take it */
+ f->f_fsdata = xzf;
+ return(0);
+}
+
+static int
+xzf_close(struct open_file *f)
+{
+ struct xz_file *xzf = (struct xz_file *)f->f_fsdata;
+
+ xz_dec_end(xzf->xzf_strm);
+ close(xzf->xzf_rawfd);
+ free(xzf);
+ return(0);
+}
+
+static int
+xzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ struct xz_file *xzf = (struct xz_file *)f->f_fsdata;
+ enum xz_ret ret;
+
+ xzf->xzf_buf.out = buf; /* where and how much */
+ xzf->xzf_buf.out_pos = 0;
+ xzf->xzf_buf.out_size = size;
+
+ while (xzf->xzf_buf.out_pos < xzf->xzf_buf.out_size && xzf->xzf_endseen == 0) {
+ if ((xzf->xzf_buf.in_pos == xzf->xzf_buf.in_size) && (xzf_fill(xzf) == -1)) {
+ printf("xzf_read: fill error\n");
+ return(EIO);
+ }
+ if (xzf->xzf_buf.in_pos == xzf->xzf_buf.in_size) { /* oops, unexpected EOF */
+ printf("xzf_read: unexpected EOF\n");
+ if (xzf->xzf_buf.out_pos == 0)
+ return(EIO);
+ break;
+ }
+
+ ret = xz_dec_run(xzf->xzf_strm, &xzf->xzf_buf); /* decompression pass */
+ if (ret == XZ_STREAM_END) { /* EOF, all done */
+ xzf->xzf_endseen = 1;
+ break;
+ }
+ if (ret != XZ_OK) { /* argh, decompression error */
+ printf("xzf_read: xz_dec_run returned %d\n", ret);
+ return(EIO);
+ }
+ }
+ xzf->xzf_total_out += xzf->xzf_buf.out_pos;
+ if (resid != NULL)
+ *resid = xzf->xzf_buf.out_size - xzf->xzf_buf.out_pos;
+ return(0);
+}
+
+static int
+xzf_rewind(struct open_file *f)
+{
+ struct xz_file *xzf = (struct xz_file *)f->f_fsdata;
+
+ if (lseek(xzf->xzf_rawfd, 0, SEEK_SET) == -1)
+ return(-1);
+ xzf->xzf_buf.in = xzf->xzf_inbuf;
+ xzf->xzf_buf.in_pos = 0;
+ xzf->xzf_buf.in_size = 0;
+ xzf->xzf_endseen = 0;
+ xzf->xzf_total_out = 0;
+ xz_dec_reset(xzf->xzf_strm);
+
+ return(0);
+}
+
+static off_t
+xzf_seek(struct open_file *f, off_t offset, int where)
+{
+ struct xz_file *xzf = (struct xz_file *)f->f_fsdata;
+ off_t target;
+ char discard[16];
+
+ switch (where) {
+ case SEEK_SET:
+ target = offset;
+ break;
+ case SEEK_CUR:
+ target = offset + xzf->xzf_total_out;
+ break;
+ default:
+ errno = EINVAL;
+ return(-1);
+ }
+
+ /* rewind if required */
+ if (target < xzf->xzf_total_out && xzf_rewind(f) != 0)
+ return(-1);
+
+ /* skip forwards if required */
+ while (target > xzf->xzf_total_out) {
+ errno = xzf_read(f, discard, min(sizeof(discard),
+ target - xzf->xzf_total_out), NULL);
+ if (errno)
+ return(-1);
+ /* Break out of loop if end of file has been reached. */
+ if (xzf->xzf_endseen)
+ break;
+ }
+ /* This is where we are (be honest if we overshot) */
+ return(xzf->xzf_total_out);
+}
+
+static int
+xzf_stat(struct open_file *f, struct stat *sb)
+{
+ struct xz_file *xzf = (struct xz_file *)f->f_fsdata;
+ int result;
+
+ /* stat as normal, but indicate that size is unknown */
+ if ((result = fstat(xzf->xzf_rawfd, sb)) == 0)
+ sb->st_size = -1;
+ return(result);
+}
diff --git a/stand/loader.mk b/stand/loader.mk
index b34ba08a8e5d..0ff5bd144d93 100644
--- a/stand/loader.mk
+++ b/stand/loader.mk
@@ -53,6 +53,7 @@ SRCS+= metadata.c
# LOADER_NFS_SUPPORT Add NFS support
# LOADER_TFTP_SUPPORT Add TFTP support
# LOADER_UFS_SUPPORT Add support for UFS filesystems
+# LOADER_XZ_SUPPORT Add support for xz compressed files
# LOADER_ZFS_SUPPORT Add support for ZFS filesystems
#
@@ -130,6 +131,9 @@ CFLAGS+= -DLOADER_GZIP_SUPPORT
.if ${LOADER_BZIP2_SUPPORT:Uno} == "yes"
CFLAGS+= -DLOADER_BZIP2_SUPPORT
.endif
+.if ${LOADER_XZ_SUPPORT:Uno} == "yes"
+CFLAGS+= -DLOADER_XZ_SUPPORT
+.endif
# Network related things
.if ${LOADER_NET_SUPPORT:Uno} == "yes"
diff --git a/stand/powerpc/ofw/conf.c b/stand/powerpc/ofw/conf.c
index c96048b5a09c..a77afe203719 100644
--- a/stand/powerpc/ofw/conf.c
+++ b/stand/powerpc/ofw/conf.c
@@ -80,6 +80,9 @@ struct fs_ops *file_system[] = {
#endif
#if defined(LOADER_BZIP2_SUPPORT)
&bzipfs_fsops,
+#endif
+#if defined(LOADER_XZ_SUPPORT)
+ &xzfs_fsops,
#endif
NULL
};
diff --git a/stand/uboot/arch/arm/conf.c b/stand/uboot/arch/arm/conf.c
index 7d36e6d6629e..535c3b494e3c 100644
--- a/stand/uboot/arch/arm/conf.c
+++ b/stand/uboot/arch/arm/conf.c
@@ -67,6 +67,9 @@ struct fs_ops *file_system[] = {
#endif
#if defined(LOADER_BZIP2_SUPPORT)
&bzipfs_fsops,
+#endif
+#if defined(LOADER_XZ_SUPPORT)
+ &xzfs_fsops,
#endif
NULL
};
diff --git a/stand/uboot/arch/powerpc/conf.c b/stand/uboot/arch/powerpc/conf.c
index 963a4ded4dcf..543b4fb643b2 100644
--- a/stand/uboot/arch/powerpc/conf.c
+++ b/stand/uboot/arch/powerpc/conf.c
@@ -76,6 +76,9 @@ struct fs_ops *file_system[] = {
#endif
#if defined(LOADER_BZIP2_SUPPORT)
&bzipfs_fsops,
+#endif
+#if defined(LOADER_XZ_SUPPORT)
+ &xzfs_fsops,
#endif
NULL
};
diff --git a/stand/userboot/userboot/conf.c b/stand/userboot/userboot/conf.c
index 63f64cb97fbe..8d37f241c9bf 100644
--- a/stand/userboot/userboot/conf.c
+++ b/stand/userboot/userboot/conf.c
@@ -70,6 +70,7 @@ struct fs_ops *file_system[] = {
#endif
&gzipfs_fsops,
&bzipfs_fsops,
+ &xzfs_fsops,
NULL
};