loader: Add forgotten xz.c and zstdfs.c

These were overlooked when I added compression support.

Fixes: 86d719ae68 ("loader: Add xzfs, like gzipfs but with xz.")
Fixes: c61ee49cd0 ("loader: zstd based filesystem, zstdfs like gzipfs")
Sponsored by:		Netflix
This commit is contained in:
Warner Losh committed 2026-07-11 21:53:29 -06:00
1 parent 7e06c33151
commit df9d6403ca
2 files changed
+329

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2026 Netflix, Inc. Written by Warner Losh
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "stand.h"
#include "xz.h"
#include "xz_malloc.h"
void *
xz_malloc(unsigned long size)
{
return (malloc(size));
}
void
xz_free(void *addr)
{
free(addr);
}
+308
View File
@@ -0,0 +1,308 @@
/*
* Copyright (c) 1998 Michael Smith.
* Copyright (c) 2026 Netflix, Inc.
* 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.
*/
/*
* Stacked filesystem for .zst compressed files, structured like gzipfs.c and
* bzipfs.c above it. Only built when the zstd decompressor is already being
* pulled into libsa for ZFS (see libsa/Makefile), since that's the only
* source of the ZSTD_* symbols in this environment.
*/
#include "stand.h"
#include <sys/stat.h>
#include <string.h>
#include <zstd.h>
#define ZSTD_BUFSIZE 2048 /* XXX larger? */
struct zstd_file
{
int zstdf_rawfd;
ZSTD_DStream *zstdf_strm;
ZSTD_inBuffer zstdf_in;
unsigned char zstdf_inbuf[ZSTD_BUFSIZE];
int zstdf_endseen;
off_t zstdf_total_out;
};
static int zstdf_fill(struct zstd_file *zstdf);
static int zstdf_open(const char *path, struct open_file *f);
static int zstdf_close(struct open_file *f);
static int zstdf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
static off_t zstdf_seek(struct open_file *f, off_t offset, int where);
static int zstdf_stat(struct open_file *f, struct stat *sb);
struct fs_ops zstdfs_fsops = {
.fs_name = "zstd",
.fs_flags = 0,
.fo_open = zstdf_open,
.fo_close = zstdf_close,
.fo_read = zstdf_read,
.fo_write = null_write,
.fo_seek = zstdf_seek,
.fo_stat = zstdf_stat,
.fo_readdir = null_readdir,
};
static int
zstdf_fill(struct zstd_file *zstdf)
{
int result;
int avail_in;
int req;
avail_in = zstdf->zstdf_in.size - zstdf->zstdf_in.pos;
req = ZSTD_BUFSIZE - avail_in;
result = 0;
/* If we need more */
if (req > 0) {
/* move old data to bottom of buffer */
if (avail_in > 0)
bcopy(zstdf->zstdf_inbuf + zstdf->zstdf_in.pos, zstdf->zstdf_inbuf, avail_in);
/* read to fill buffer and update availibility data */
result = read(zstdf->zstdf_rawfd, zstdf->zstdf_inbuf + avail_in, req);
zstdf->zstdf_in.src = zstdf->zstdf_inbuf;
zstdf->zstdf_in.pos = 0;
zstdf->zstdf_in.size = avail_in + (result >= 0 ? result : 0);
}
return (result);
}
static const unsigned char zstd_magic[4] = {0x28, 0xb5, 0x2f, 0xfd};
/*
* Peek at the fixed-size zstd frame magic without consuming it, so the
* header remains for ZSTD_decompressStream() to parse normally on the
* first read.
*
* Returns 0 if the header is OK, nonzero if not.
*/
static int
check_header(struct zstd_file *zstdf)
{
if (zstdf->zstdf_in.size - zstdf->zstdf_in.pos < sizeof(zstd_magic) &&
zstdf_fill(zstdf) == -1)
return (1);
if (zstdf->zstdf_in.size - zstdf->zstdf_in.pos < sizeof(zstd_magic))
return (1);
return (memcmp((const unsigned char *)zstdf->zstdf_in.src + zstdf->zstdf_in.pos,
zstd_magic, sizeof(zstd_magic)) != 0);
}
static int
zstdf_open(const char *fname, struct open_file *f)
{
static char *zstdfname;
int rawfd;
struct zstd_file *zstdf;
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 */
zstdfname = malloc(strlen(fname) + 5);
if (zstdfname == NULL)
return(ENOMEM);
sprintf(zstdfname, "%s.zst", fname);
/* Try to open the compressed datafile */
rawfd = open(zstdfname, O_RDONLY);
free(zstdfname);
if (rawfd == -1)
return(ENOENT);
if (fstat(rawfd, &sb) < 0) {
printf("zstdf_open: stat failed\n");
close(rawfd);
return(ENOENT);
}
if (!S_ISREG(sb.st_mode)) {
printf("zstdf_open: not a file\n");
close(rawfd);
return(EISDIR); /* best guess */
}
/* Allocate a zstd_file structure, populate it */
zstdf = malloc(sizeof(struct zstd_file));
if (zstdf == NULL) {
close(rawfd);
return(ENOMEM);
}
bzero(zstdf, sizeof(struct zstd_file));
zstdf->zstdf_rawfd = rawfd;
zstdf->zstdf_in.src = zstdf->zstdf_inbuf;
/* Verify that the file is zstd compressed */
if (check_header(zstdf)) {
close(zstdf->zstdf_rawfd);
free(zstdf);
return(EFTYPE);
}
/* Initialise the inflation engine */
zstdf->zstdf_strm = ZSTD_createDStream();
if (zstdf->zstdf_strm == NULL) {
close(zstdf->zstdf_rawfd);
free(zstdf);
return(ENOMEM);
}
if (ZSTD_isError(ZSTD_initDStream(zstdf->zstdf_strm))) {
ZSTD_freeDStream(zstdf->zstdf_strm);
close(zstdf->zstdf_rawfd);
free(zstdf);
return(EIO);
}
/* Looks OK, we'll take it */
f->f_fsdata = zstdf;
return(0);
}
static int
zstdf_close(struct open_file *f)
{
struct zstd_file *zstdf = (struct zstd_file *)f->f_fsdata;
ZSTD_freeDStream(zstdf->zstdf_strm);
close(zstdf->zstdf_rawfd);
free(zstdf);
return(0);
}
static int
zstdf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
{
struct zstd_file *zstdf = (struct zstd_file *)f->f_fsdata;
ZSTD_outBuffer out = { buf, size, 0 };
size_t ret;
while (out.pos < out.size && zstdf->zstdf_endseen == 0) {
if ((zstdf->zstdf_in.pos == zstdf->zstdf_in.size) && (zstdf_fill(zstdf) == -1)) {
printf("zstdf_read: fill error\n");
return(EIO);
}
if (zstdf->zstdf_in.pos == zstdf->zstdf_in.size) { /* oops, unexpected EOF */
printf("zstdf_read: unexpected EOF\n");
if (out.pos == 0)
return(EIO);
break;
}
ret = ZSTD_decompressStream(zstdf->zstdf_strm, &out, &zstdf->zstdf_in);
if (ZSTD_isError(ret)) { /* argh, decompression error */
printf("zstdf_read: %s\n", ZSTD_getErrorName(ret));
return(EIO);
}
if (ret == 0) { /* EOF, all done */
zstdf->zstdf_endseen = 1;
break;
}
}
zstdf->zstdf_total_out += out.pos;
if (resid != NULL)
*resid = out.size - out.pos;
return(0);
}
static int
zstdf_rewind(struct open_file *f)
{
struct zstd_file *zstdf = (struct zstd_file *)f->f_fsdata;
if (lseek(zstdf->zstdf_rawfd, 0, SEEK_SET) == -1)
return(-1);
zstdf->zstdf_in.src = zstdf->zstdf_inbuf;
zstdf->zstdf_in.pos = 0;
zstdf->zstdf_in.size = 0;
zstdf->zstdf_endseen = 0;
zstdf->zstdf_total_out = 0;
if (ZSTD_isError(ZSTD_initDStream(zstdf->zstdf_strm)))
return(-1);
return(0);
}
static off_t
zstdf_seek(struct open_file *f, off_t offset, int where)
{
struct zstd_file *zstdf = (struct zstd_file *)f->f_fsdata;
off_t target;
char discard[16];
switch (where) {
case SEEK_SET:
target = offset;
break;
case SEEK_CUR:
target = offset + zstdf->zstdf_total_out;
break;
default:
errno = EINVAL;
return(-1);
}
/* rewind if required */
if (target < zstdf->zstdf_total_out && zstdf_rewind(f) != 0)
return(-1);
/* skip forwards if required */
while (target > zstdf->zstdf_total_out) {
errno = zstdf_read(f, discard, min(sizeof(discard),
target - zstdf->zstdf_total_out), NULL);
if (errno)
return(-1);
/* Break out of loop if end of file has been reached. */
if (zstdf->zstdf_endseen)
break;
}
/* This is where we are (be honest if we overshot) */
return(zstdf->zstdf_total_out);
}
static int
zstdf_stat(struct open_file *f, struct stat *sb)
{
struct zstd_file *zstdf = (struct zstd_file *)f->f_fsdata;
int result;
/* stat as normal, but indicate that size is unknown */
if ((result = fstat(zstdf->zstdf_rawfd, sb)) == 0)
sb->st_size = -1;
return(result);
}