mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/legion/kbd.git
synced 2026-01-26 14:13:24 +00:00
Merge branch 'use-compression-libraries'
In some configurations, the use of utilities is redundant. Also, popen() requires /bin/sh. However, not all configurations utilize the libz and libbz2 libraries. For example, busybox has its own implementations of gzip and bzip2. A solution that would suit everyone is to add support for ELF_DLOPEN_METADATA[1]. This will avoid unnecessary dependencies where utilities are available and avoid using utilities where libraries are available. [1] https://github.com/systemd/systemd/blob/main/docs/ELF_DLOPEN_METADATA.md Link: https://github.com/legionus/kbd/pull/141 Signed-off-by: Alexey Gladkov <legion@kernel.org>
This commit is contained in:
commit
eddc753fd9
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@ -55,9 +55,6 @@ jobs:
|
||||
- name: "Configure"
|
||||
run: |
|
||||
tests/configure.sh --datadir="$PWD/data" --enable-memcheck
|
||||
- name: "Check sparse"
|
||||
run: |
|
||||
make check-sparse V=1
|
||||
- name: "Clean"
|
||||
run: |
|
||||
make clean
|
||||
@ -87,28 +84,27 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
cc: gcc
|
||||
cflags:
|
||||
compiler: gcc
|
||||
libc: glibc
|
||||
configure:
|
||||
check: unittest e2e
|
||||
- os: ubuntu-latest
|
||||
cc: clang
|
||||
cflags:
|
||||
compiler: clang
|
||||
libc: glibc
|
||||
configure:
|
||||
check: unittest e2e
|
||||
- os: ubuntu-latest
|
||||
cc: musl-gcc -static -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/
|
||||
cflags: -idirafter /usr/include/ -idirafter /usr/include/x86_64-linux-gnu/
|
||||
compiler: gcc
|
||||
libc: musl
|
||||
configure: --disable-libkeymap --disable-vlock
|
||||
configure: --host=x86_64-linux-musl --disable-vlock --without-zlib --without-bzip2 --without-lzma --without-zstd
|
||||
check: unittest e2e
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [ distcheck_job ]
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
CHECK_KEYWORDS: ${{ matrix.check }}
|
||||
SANDBOX: priviliged
|
||||
TTY: /dev/tty60
|
||||
@ -126,7 +122,7 @@ jobs:
|
||||
tests/configure.sh --datadir="$PWD/tests/data" --enable-memcheck ${{ matrix.configure }}
|
||||
- name: "Build"
|
||||
run: |
|
||||
make V=1 CFLAGS+="-g -O0"
|
||||
make V=1 CFLAGS+="-g -O0 ${{ matrix.cflags }}"
|
||||
- name: "Check"
|
||||
run: |
|
||||
sudo -E tests/check.sh
|
||||
|
||||
60
configure.ac
60
configure.ac
@ -220,6 +220,66 @@ if test "$VLOCK_PROG" = "yes"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_ARG_WITH([zlib],
|
||||
[AS_HELP_STRING([--with-zlib],
|
||||
[support zlib compression @<:@default=auto@:>@])],
|
||||
[],
|
||||
[: m4_divert_text([DEFAULTS], [with_zlib=yes])])
|
||||
|
||||
AS_IF([test "x$with_zlib" != xno],
|
||||
[PKG_CHECK_MODULES(ZLIB, zlib, [HAVE_ZLIB=yes], [HAVE_ZLIB=no])],
|
||||
[HAVE_ZLIB=no])
|
||||
AS_IF([test "x$HAVE_ZLIB" = "xyes"],
|
||||
[AC_DEFINE([HAVE_ZLIB], [1], [support zlib compression])])
|
||||
AM_CONDITIONAL(USE_ZLIB, test "x$HAVE_ZLIB" = "xyes")
|
||||
|
||||
AC_ARG_WITH([bzip2],
|
||||
[AS_HELP_STRING([--with-bzip2],
|
||||
[support bzip2 compression @<:@default=auto@:>@])],
|
||||
[],
|
||||
[: m4_divert_text([DEFAULTS], [with_bzip2=yes])])
|
||||
|
||||
AS_IF([test "x$with_bzip2" != xno],
|
||||
[PKG_CHECK_MODULES(BZIP2, bzip2, [HAVE_BZIP2=yes], [HAVE_BZIP2=no])],
|
||||
[HAVE_BZIP2=no])
|
||||
|
||||
if test "x$HAVE_BZIP2" = xno; then
|
||||
AC_CHECK_LIB(bz2, BZ2_bzDecompressInit, [
|
||||
HAVE_BZIP2=yes
|
||||
BZIP2_LIBS=-lbz2
|
||||
BZIP2_CFLAGS=''
|
||||
], [HAVE_BZIP2=no])
|
||||
fi
|
||||
AS_IF([test "x$HAVE_BZIP2" = "xyes"],
|
||||
[AC_DEFINE([HAVE_BZIP2], [1], [support bzip2 compression])])
|
||||
AM_CONDITIONAL(USE_BZIP2, test "$HAVE_BZIP2" = "yes")
|
||||
|
||||
AC_ARG_WITH([lzma],
|
||||
[AS_HELP_STRING([--with-lzma],
|
||||
[support lzma compression @<:@default=auto@:>@])],
|
||||
[],
|
||||
[: m4_divert_text([DEFAULTS], [with_lzma=yes])])
|
||||
|
||||
AS_IF([test "x$with_lzma" != xno],
|
||||
[PKG_CHECK_MODULES(LZMA, liblzma, [HAVE_LZMA=yes], [HAVE_LZMA=no])],
|
||||
[HAVE_LZMA=no])
|
||||
AS_IF([test "x$HAVE_LZMA" = "xyes"],
|
||||
[AC_DEFINE([HAVE_LZMA], [1], [support lzma compression])])
|
||||
AM_CONDITIONAL(USE_LZMA, test "$HAVE_LZMA" = "yes")
|
||||
|
||||
AC_ARG_WITH([zstd],
|
||||
[AS_HELP_STRING([--with-zstd],
|
||||
[support zstd compression @<:@default=auto@:>@])],
|
||||
[],
|
||||
[: m4_divert_text([DEFAULTS], [with_zstd=yes])])
|
||||
|
||||
AS_IF([test "x$with_zstd" != xno],
|
||||
[PKG_CHECK_MODULES(ZSTD, libzstd, [HAVE_ZSTD=yes], [HAVE_ZSTD=no])],
|
||||
[HAVE_ZSTD=no])
|
||||
AS_IF([test "x$HAVE_ZSTD" = "xyes"],
|
||||
[AC_DEFINE([HAVE_ZSTD], [1], [support zstd compression])])
|
||||
AM_CONDITIONAL(USE_ZSTD, test "$HAVE_ZSTD" = "yes")
|
||||
|
||||
AC_ARG_ENABLE(tests,
|
||||
[AS_HELP_STRING([--disable-tests], [do not build tests])],
|
||||
[build_tests=$enableval], [build_tests=auto])
|
||||
|
||||
@ -10,9 +10,34 @@ headers = \
|
||||
libkbdfile_la_SOURCES = \
|
||||
$(headers) \
|
||||
contextP.h \
|
||||
elf-note.h \
|
||||
elf-note.c \
|
||||
init.c \
|
||||
kbdfile.c
|
||||
|
||||
libkbdfile_la_LIBADD =
|
||||
libkbdfile_la_CFLAGS =
|
||||
|
||||
if USE_ZLIB
|
||||
libkbdfile_la_SOURCES += kbdfile-zlib.c
|
||||
libkbdfile_la_CFLAGS += $(ZLIB_CFLAGS)
|
||||
endif
|
||||
|
||||
if USE_BZIP2
|
||||
libkbdfile_la_SOURCES += kbdfile-bzip2.c
|
||||
libkbdfile_la_CFLAGS += $(BZIP2_CFLAGS)
|
||||
endif
|
||||
|
||||
if USE_LZMA
|
||||
libkbdfile_la_SOURCES += kbdfile-lzma.c
|
||||
libkbdfile_la_CFLAGS += $(LZMA_CFLAGS)
|
||||
endif
|
||||
|
||||
if USE_ZSTD
|
||||
libkbdfile_la_SOURCES += kbdfile-zstd.c
|
||||
libkbdfile_la_CFLAGS += $(ZSTD_CFLAGS)
|
||||
endif
|
||||
|
||||
KBDFILE_CURRENT = 1
|
||||
KBDFILE_REVISION = 0
|
||||
KBDFILE_AGE = 0
|
||||
|
||||
@ -33,6 +33,7 @@ struct kbdfile {
|
||||
|
||||
#define KBDFILE_CTX_INITIALIZED 0x01
|
||||
#define KBDFILE_PIPE 0x02
|
||||
#define KBDFILE_COMPRESSED 0x04
|
||||
|
||||
#define kbdfile_log_cond(ctx, level, arg...) \
|
||||
do { \
|
||||
@ -68,4 +69,36 @@ struct kbdfile {
|
||||
*/
|
||||
#define ERR(ctx, arg...) kbdfile_log_cond(ctx, LOG_ERR, ##arg)
|
||||
|
||||
char *kbd_strerror(int errnum, char *buf, size_t buflen);
|
||||
|
||||
static inline FILE *kbdfile_decompressor_dummy(struct kbdfile *file KBD_ATTR_UNUSED)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define kbdfile_decompressor_zlib kbdfile_decompressor_dummy
|
||||
#define kbdfile_decompressor_bzip2 kbdfile_decompressor_dummy
|
||||
#define kbdfile_decompressor_lzma kbdfile_decompressor_dummy
|
||||
#define kbdfile_decompressor_zstd kbdfile_decompressor_dummy
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
#undef kbdfile_decompressor_zlib
|
||||
FILE *kbdfile_decompressor_zlib(struct kbdfile *file) KBD_ATTR_MUST_CHECK;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BZIP2
|
||||
#undef kbdfile_decompressor_bzip2
|
||||
FILE *kbdfile_decompressor_bzip2(struct kbdfile *file) KBD_ATTR_MUST_CHECK;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LZMA
|
||||
#undef kbdfile_decompressor_lzma
|
||||
FILE *kbdfile_decompressor_lzma(struct kbdfile *file) KBD_ATTR_MUST_CHECK;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ZSTD
|
||||
#undef kbdfile_decompressor_zstd
|
||||
FILE *kbdfile_decompressor_zstd(struct kbdfile *file) KBD_ATTR_MUST_CHECK;
|
||||
#endif
|
||||
|
||||
#endif /* KBDFILE_CONTEXTP_H */
|
||||
|
||||
54
src/libkbdfile/elf-note.c
Normal file
54
src/libkbdfile/elf-note.c
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "elf-note.h"
|
||||
|
||||
static int dlsym_manyv(void *dl, va_list ap)
|
||||
{
|
||||
void (**fn)(void);
|
||||
|
||||
while ((fn = va_arg(ap, typeof(fn)))) {
|
||||
const char *symbol;
|
||||
|
||||
symbol = va_arg(ap, typeof(symbol));
|
||||
*fn = dlsym(dl, symbol);
|
||||
if (!*fn)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dlsym_many(void **dlp, const char *filename, ...)
|
||||
{
|
||||
va_list ap;
|
||||
void *dl;
|
||||
int r;
|
||||
|
||||
if (*dlp)
|
||||
return 0;
|
||||
|
||||
dl = dlopen(filename, RTLD_LAZY);
|
||||
if (!dl)
|
||||
return -ENOENT;
|
||||
|
||||
va_start(ap, filename);
|
||||
r = dlsym_manyv(dl, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (r < 0) {
|
||||
dlclose(dl);
|
||||
return r;
|
||||
}
|
||||
|
||||
*dlp = dl;
|
||||
|
||||
return 1;
|
||||
}
|
||||
90
src/libkbdfile/elf-note.h
Normal file
90
src/libkbdfile/elf-note.h
Normal file
@ -0,0 +1,90 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define XSTRINGIFY(x) #x
|
||||
#define STRINGIFY(x) XSTRINGIFY(x)
|
||||
|
||||
#define XCONCATENATE(x, y) x##y
|
||||
#define CONCATENATE(x, y) XCONCATENATE(x, y)
|
||||
#define UNIQ(x) CONCATENATE(x, __COUNTER__)
|
||||
#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
|
||||
|
||||
/*
|
||||
* Load many various symbols from @filename.
|
||||
* @dlp: pointer to the previous results of this call: it's set when it succeeds
|
||||
* @filename: the library to dlopen() and look for symbols
|
||||
* @...: or 1 more tuples created by DLSYM_ARG() with ( &var, "symbol name" ).
|
||||
*/
|
||||
int dlsym_many(void **dlp, const char *filename, ...);
|
||||
|
||||
/*
|
||||
* Helper to create tuples passed as arguments to dlsym_many().
|
||||
* @symbol__: symbol to create arguments for. Example: DLSYM_ARG(foo) expands to
|
||||
* `&sym_foo, "foo"`
|
||||
*/
|
||||
#define DLSYM_ARG(symbol__) &sym_##symbol__, STRINGIFY(symbol__),
|
||||
|
||||
/* For symbols being dynamically loaded */
|
||||
#define DECLARE_DLSYM(symbol) static typeof(symbol) *sym_##symbol
|
||||
|
||||
/*
|
||||
* Helper defines, to be done locally before including this header to switch between
|
||||
* implementations
|
||||
*/
|
||||
#define DECLARE_SYM(sym__) DECLARE_DLSYM(sym__);
|
||||
|
||||
/*
|
||||
* Originally from systemd codebase.
|
||||
*
|
||||
* Reference: https://systemd.io/ELF_PACKAGE_METADATA/
|
||||
*/
|
||||
|
||||
#define ELF_NOTE_DLOPEN_VENDOR "FDO"
|
||||
#define ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a)
|
||||
#define ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required"
|
||||
#define ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended"
|
||||
#define ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested"
|
||||
|
||||
/*
|
||||
* Add an ".note.dlopen" ELF note to our binary that declares our weak dlopen()
|
||||
* dependency. This information can be read from an ELF file via
|
||||
* "readelf -p .note.dlopen" or an equivalent command.
|
||||
*/
|
||||
#define _ELF_NOTE_DLOPEN(json, variable_name) \
|
||||
__attribute__((used, section(".note.dlopen"))) _Alignas(sizeof(uint32_t)) \
|
||||
static const struct { \
|
||||
struct { \
|
||||
uint32_t n_namesz, n_descsz, n_type; \
|
||||
} nhdr; \
|
||||
char name[sizeof(ELF_NOTE_DLOPEN_VENDOR)]; \
|
||||
_Alignas(sizeof(uint32_t)) char dlopen_json[sizeof(json)]; \
|
||||
} variable_name = { \
|
||||
.nhdr = { \
|
||||
.n_namesz = sizeof(ELF_NOTE_DLOPEN_VENDOR), \
|
||||
.n_descsz = sizeof(json), \
|
||||
.n_type = ELF_NOTE_DLOPEN_TYPE, \
|
||||
}, \
|
||||
.name = ELF_NOTE_DLOPEN_VENDOR, \
|
||||
.dlopen_json = json, \
|
||||
}
|
||||
|
||||
#define _SONAME_ARRAY1(a) "[\""a"\"]"
|
||||
#define _SONAME_ARRAY2(a, b) "[\""a"\",\""b"\"]"
|
||||
#define _SONAME_ARRAY3(a, b, c) "[\""a"\",\""b"\",\""c"\"]"
|
||||
#define _SONAME_ARRAY4(a, b, c, d) "[\""a"\",\""b"\",\""c"\"",\""d"\"]"
|
||||
#define _SONAME_ARRAY5(a, b, c, d, e) "[\""a"\",\""b"\",\""c"\"",\""d"\",\""e"\"]"
|
||||
#define _SONAME_ARRAY_GET(_1,_2,_3,_4,_5,NAME,...) NAME
|
||||
#define _SONAME_ARRAY(...) _SONAME_ARRAY_GET(__VA_ARGS__, _SONAME_ARRAY5, _SONAME_ARRAY4, _SONAME_ARRAY3, _SONAME_ARRAY2, _SONAME_ARRAY1)(__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* The 'priority' must be one of 'required', 'recommended' or 'suggested' as per
|
||||
* specification, use the macro defined above to specify it.
|
||||
* Multiple sonames can be passed and they will be automatically constructed
|
||||
* into a json array (but note that due to preprocessor language limitations if
|
||||
* more than the limit defined above is used, a new _SONAME_ARRAY<X+1> will need
|
||||
* to be added).
|
||||
*/
|
||||
#define ELF_NOTE_DLOPEN(feature, description, priority, ...) \
|
||||
_ELF_NOTE_DLOPEN("[{\"feature\":\"" feature "\",\"description\":\"" description "\",\"priority\":\"" priority "\",\"soname\":" _SONAME_ARRAY(__VA_ARGS__) "}]", UNIQ_T(s, UNIQ))
|
||||
123
src/libkbdfile/kbdfile-bzip2.c
Normal file
123
src/libkbdfile/kbdfile-bzip2.c
Normal file
@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <bzlib.h>
|
||||
|
||||
#include <kbdfile.h>
|
||||
|
||||
#include "contextP.h"
|
||||
#include "elf-note.h"
|
||||
|
||||
#define DL_SYMBOL_TABLE(M) \
|
||||
M(BZ2_bzopen) \
|
||||
M(BZ2_bzclose) \
|
||||
M(BZ2_bzread) \
|
||||
M(BZ2_bzerror)
|
||||
|
||||
DL_SYMBOL_TABLE(DECLARE_SYM)
|
||||
|
||||
static int dlopen_note(void)
|
||||
{
|
||||
static void *dl;
|
||||
|
||||
ELF_NOTE_DLOPEN("bzip2",
|
||||
"Support for uncompressing bzip2-compressed files",
|
||||
ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
|
||||
"libbz2.so.1");
|
||||
|
||||
return dlsym_many(&dl, "libbz2.so.1", DL_SYMBOL_TABLE(DLSYM_ARG) NULL);
|
||||
}
|
||||
|
||||
FILE *kbdfile_decompressor_bzip2(struct kbdfile *file)
|
||||
{
|
||||
char errbuf[200];
|
||||
int retcode;
|
||||
BZFILE *zf = NULL;
|
||||
FILE *outf = NULL;
|
||||
int memfd = -1;
|
||||
|
||||
retcode = dlopen_note();
|
||||
if (retcode < 0) {
|
||||
ERR(file->ctx, "bzip2: can't load and resolve symbols: %s",
|
||||
kbd_strerror(-retcode, errbuf, sizeof(errbuf)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retcode = -1;
|
||||
|
||||
memfd = memfd_create(file->pathname, MFD_CLOEXEC);
|
||||
if (memfd < 0) {
|
||||
ERR(file->ctx, "unable to open in-memory file: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
zf = sym_BZ2_bzopen(file->pathname, "rb");
|
||||
if (!zf) {
|
||||
ERR(file->ctx, "bzip2: unable to open archive: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int read_bytes;
|
||||
char outbuf[BUFSIZ];
|
||||
|
||||
read_bytes = sym_BZ2_bzread(zf, outbuf, sizeof(outbuf));
|
||||
if (read_bytes < 0) {
|
||||
int zerrno;
|
||||
ERR(file->ctx, "bzip2: read error: %s",
|
||||
sym_BZ2_bzerror(zf, &zerrno));
|
||||
goto cleanup;
|
||||
}
|
||||
if (read_bytes == 0) {
|
||||
retcode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t to_write = (size_t) read_bytes;
|
||||
size_t written = 0;
|
||||
|
||||
while (written < to_write) {
|
||||
ssize_t w = write(memfd, outbuf + written, to_write - written);
|
||||
|
||||
if (w < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
ERR(file->ctx, "unable to write data: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
written += (size_t) w;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (zf)
|
||||
sym_BZ2_bzclose(zf);
|
||||
|
||||
if (retcode == 0) {
|
||||
lseek(memfd, 0L, SEEK_SET);
|
||||
|
||||
outf = fdopen(memfd, "r");
|
||||
if (!outf) {
|
||||
ERR(file->ctx, "unable to create file stream from file descriptor: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
|
||||
if (memfd >= 0)
|
||||
close(memfd);
|
||||
}
|
||||
} else if (memfd >= 0) {
|
||||
close(memfd);
|
||||
}
|
||||
|
||||
return outf;
|
||||
}
|
||||
150
src/libkbdfile/kbdfile-lzma.c
Normal file
150
src/libkbdfile/kbdfile-lzma.c
Normal file
@ -0,0 +1,150 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <lzma.h>
|
||||
|
||||
#include <kbdfile.h>
|
||||
|
||||
#include "contextP.h"
|
||||
#include "elf-note.h"
|
||||
|
||||
#define DL_SYMBOL_TABLE(M) \
|
||||
M(lzma_stream_decoder) \
|
||||
M(lzma_code) \
|
||||
M(lzma_end)
|
||||
|
||||
DL_SYMBOL_TABLE(DECLARE_SYM)
|
||||
|
||||
static int dlopen_lzma(void)
|
||||
{
|
||||
static void *dl;
|
||||
|
||||
ELF_NOTE_DLOPEN("lzma",
|
||||
"Support for uncompressing xz-compressed files",
|
||||
ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
|
||||
"liblzma.so.5");
|
||||
|
||||
return dlsym_many(&dl, "liblzma.so.5", DL_SYMBOL_TABLE(DLSYM_ARG) NULL);
|
||||
}
|
||||
|
||||
FILE *kbdfile_decompressor_lzma(struct kbdfile *file)
|
||||
{
|
||||
char errbuf[200];
|
||||
int retcode;
|
||||
FILE *outf = NULL;
|
||||
int infd = -1;
|
||||
int memfd = -1;
|
||||
|
||||
lzma_stream strm = LZMA_STREAM_INIT;
|
||||
lzma_action action = LZMA_RUN;
|
||||
|
||||
retcode = dlopen_lzma();
|
||||
if (retcode < 0) {
|
||||
ERR(file->ctx, "lzma: can't load and resolve symbols: %s",
|
||||
kbd_strerror(-retcode, errbuf, sizeof(errbuf)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retcode = -1;
|
||||
|
||||
if (sym_lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED) != LZMA_OK)
|
||||
goto cleanup;
|
||||
|
||||
infd = open(file->pathname, O_RDONLY);
|
||||
if (infd < 0) {
|
||||
ERR(file->ctx, "unable to open xz-archive file: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memfd = memfd_create(file->pathname, MFD_CLOEXEC);
|
||||
if (memfd < 0) {
|
||||
ERR(file->ctx, "unable to open in-memory file: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uint8_t inpbuf[BUFSIZ];
|
||||
uint8_t outbuf[BUFSIZ * 4];
|
||||
int eof = 0;
|
||||
|
||||
while (1) {
|
||||
if (!eof && strm.avail_in == 0) {
|
||||
ssize_t read_bytes = read(infd, inpbuf, sizeof(inpbuf));
|
||||
|
||||
if (read_bytes < 0) {
|
||||
ERR(file->ctx, "unable to read xz-archive: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
} else if (read_bytes == 0) {
|
||||
eof = 1;
|
||||
action = LZMA_FINISH;
|
||||
} else {
|
||||
strm.next_in = inpbuf;
|
||||
strm.avail_in = (size_t) read_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
strm.next_out = outbuf;
|
||||
strm.avail_out = sizeof(outbuf);
|
||||
|
||||
lzma_ret code_ret = sym_lzma_code(&strm, action);
|
||||
|
||||
if (code_ret != LZMA_OK && code_ret != LZMA_STREAM_END) {
|
||||
ERR(file->ctx, "lzma: decode error (return code %d)", code_ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
size_t to_write = sizeof(outbuf) - strm.avail_out;
|
||||
size_t written = 0;
|
||||
|
||||
while (written < to_write) {
|
||||
ssize_t w = write(memfd, outbuf + written, to_write - written);
|
||||
if (w < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
ERR(file->ctx, "unable to write data: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
written += (size_t) w;
|
||||
}
|
||||
|
||||
if (code_ret == LZMA_STREAM_END) {
|
||||
retcode = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (infd >= 0)
|
||||
close(infd);
|
||||
|
||||
sym_lzma_end(&strm);
|
||||
|
||||
if (retcode == 0) {
|
||||
lseek(memfd, 0L, SEEK_SET);
|
||||
|
||||
outf = fdopen(memfd, "r");
|
||||
if (!outf) {
|
||||
ERR(file->ctx, "unable to create file stream from file descriptor: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
|
||||
if (memfd >= 0)
|
||||
close(memfd);
|
||||
}
|
||||
} else if (memfd >= 0) {
|
||||
close(memfd);
|
||||
}
|
||||
|
||||
return outf;
|
||||
}
|
||||
123
src/libkbdfile/kbdfile-zlib.c
Normal file
123
src/libkbdfile/kbdfile-zlib.c
Normal file
@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <kbdfile.h>
|
||||
|
||||
#include "contextP.h"
|
||||
#include "elf-note.h"
|
||||
|
||||
#define DL_SYMBOL_TABLE(M) \
|
||||
M(gzclose) \
|
||||
M(gzopen) \
|
||||
M(gzerror) \
|
||||
M(gzread)
|
||||
|
||||
DL_SYMBOL_TABLE(DECLARE_SYM)
|
||||
|
||||
static int dlopen_note(void)
|
||||
{
|
||||
static void *dl;
|
||||
|
||||
ELF_NOTE_DLOPEN("zlib",
|
||||
"Support for uncompressing zlib-compressed files",
|
||||
ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
|
||||
"libz.so.1");
|
||||
|
||||
return dlsym_many(&dl, "libz.so.1", DL_SYMBOL_TABLE(DLSYM_ARG) NULL);
|
||||
}
|
||||
|
||||
FILE *kbdfile_decompressor_zlib(struct kbdfile *file)
|
||||
{
|
||||
char errbuf[200];
|
||||
int retcode;
|
||||
gzFile gzf = NULL;
|
||||
FILE *outf = NULL;
|
||||
int memfd = -1;
|
||||
|
||||
retcode = dlopen_note();
|
||||
if (retcode < 0) {
|
||||
ERR(file->ctx, "zlib: can't load and resolve symbols: %s",
|
||||
kbd_strerror(-retcode, errbuf, sizeof(errbuf)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retcode = -1;
|
||||
|
||||
memfd = memfd_create(file->pathname, MFD_CLOEXEC);
|
||||
if (memfd < 0) {
|
||||
ERR(file->ctx, "unable to open in-memory file: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
gzf = sym_gzopen(file->pathname, "rb");
|
||||
if (!gzf) {
|
||||
ERR(file->ctx, "zlib: unable to open archive: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int read_bytes;
|
||||
char outbuf[BUFSIZ];
|
||||
|
||||
read_bytes = sym_gzread(gzf, outbuf, sizeof(outbuf));
|
||||
|
||||
if (read_bytes < 0) {
|
||||
int gzerr;
|
||||
ERR(file->ctx, "zlib: read error: %s",
|
||||
sym_gzerror(gzf, &gzerr));
|
||||
goto cleanup;
|
||||
}
|
||||
if (read_bytes == 0) {
|
||||
retcode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
size_t to_write = (size_t) read_bytes;
|
||||
size_t written = 0;
|
||||
|
||||
while (written < to_write) {
|
||||
ssize_t w = write(memfd, outbuf + written, to_write - written);
|
||||
|
||||
if (w < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
ERR(file->ctx, "unable to write data: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
written += (size_t) w;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
sym_gzclose(gzf);
|
||||
|
||||
if (retcode == 0) {
|
||||
lseek(memfd, 0L, SEEK_SET);
|
||||
|
||||
outf = fdopen(memfd, "r");
|
||||
if (!outf) {
|
||||
ERR(file->ctx, "unable to create file stream from file descriptor: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
|
||||
if (memfd >= 0)
|
||||
close(memfd);
|
||||
}
|
||||
} else if (memfd >= 0) {
|
||||
close(memfd);
|
||||
}
|
||||
|
||||
return outf;
|
||||
}
|
||||
162
src/libkbdfile/kbdfile-zstd.c
Normal file
162
src/libkbdfile/kbdfile-zstd.c
Normal file
@ -0,0 +1,162 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <zstd.h>
|
||||
|
||||
#include <kbdfile.h>
|
||||
|
||||
#include "contextP.h"
|
||||
#include "elf-note.h"
|
||||
|
||||
#define DL_SYMBOL_TABLE(M) \
|
||||
M(ZSTD_createDStream) \
|
||||
M(ZSTD_decompressStream) \
|
||||
M(ZSTD_isError) \
|
||||
M(ZSTD_getErrorName) \
|
||||
M(ZSTD_freeDStream)
|
||||
|
||||
DL_SYMBOL_TABLE(DECLARE_SYM)
|
||||
|
||||
static int dlopen_note(void)
|
||||
{
|
||||
static void *dl;
|
||||
|
||||
ELF_NOTE_DLOPEN("zstd",
|
||||
"Support for uncompressing zstd-compressed files",
|
||||
ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
|
||||
"libzstd.so.1");
|
||||
|
||||
return dlsym_many(&dl, "libzstd.so.1", DL_SYMBOL_TABLE(DLSYM_ARG) NULL);
|
||||
}
|
||||
|
||||
FILE *kbdfile_decompressor_zstd(struct kbdfile *file)
|
||||
{
|
||||
char errbuf[200];
|
||||
int retcode;
|
||||
FILE *outf = NULL;
|
||||
int infd = -1;
|
||||
int memfd = -1;
|
||||
|
||||
ZSTD_DStream *dstream = NULL;
|
||||
|
||||
retcode = dlopen_note();
|
||||
if (retcode < 0) {
|
||||
ERR(file->ctx, "zstd: can't load and resolve symbols: %s",
|
||||
kbd_strerror(-retcode, errbuf, sizeof(errbuf)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retcode = -1;
|
||||
|
||||
infd = open(file->pathname, O_RDONLY);
|
||||
if (infd < 0) {
|
||||
ERR(file->ctx, "unable to open xz-archive file: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memfd = memfd_create(file->pathname, MFD_CLOEXEC);
|
||||
if (memfd < 0) {
|
||||
ERR(file->ctx, "unable to open in-memory file: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dstream = sym_ZSTD_createDStream();
|
||||
if (!dstream) {
|
||||
ERR(file->ctx, "prepare zstd streaming decompressor failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char inpbuf[BUFSIZ];
|
||||
char outbuf[BUFSIZ * 4];
|
||||
int eof = 0;
|
||||
|
||||
ZSTD_inBuffer input = { inpbuf, 0, 0 };
|
||||
ZSTD_outBuffer output = { outbuf, sizeof(outbuf), 0 };
|
||||
|
||||
while (!eof || input.pos < input.size) {
|
||||
if (!eof && input.pos == input.size) {
|
||||
ssize_t r = read(infd, inpbuf, sizeof(inpbuf));
|
||||
|
||||
if (r < 0) {
|
||||
ERR(file->ctx, "unable to read zstd-archive: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
goto cleanup;
|
||||
} else if (r == 0) {
|
||||
eof = 1;
|
||||
input.src = inpbuf;
|
||||
input.size = 0;
|
||||
input.pos = 0;
|
||||
} else {
|
||||
input.src = inpbuf;
|
||||
input.size = (size_t) r;
|
||||
input.pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
output.dst = outbuf;
|
||||
output.size = sizeof(outbuf);
|
||||
output.pos = 0;
|
||||
|
||||
size_t res_decompress = sym_ZSTD_decompressStream(dstream, &output, &input);
|
||||
|
||||
if (sym_ZSTD_isError(res_decompress)) {
|
||||
ERR(file->ctx, "zstd: unable to decompress stream: %s",
|
||||
sym_ZSTD_getErrorName(res_decompress));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
size_t to_write = output.pos;
|
||||
size_t written = 0;
|
||||
|
||||
while (written < to_write) {
|
||||
ssize_t w = write(memfd, outbuf + written, to_write - written);
|
||||
|
||||
if (w < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
ERR(file->ctx, "unable to write data: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
written += (size_t) w;
|
||||
}
|
||||
}
|
||||
|
||||
if (eof && input.pos == input.size)
|
||||
retcode = 0;
|
||||
|
||||
cleanup:
|
||||
if (infd >= 0)
|
||||
close(infd);
|
||||
|
||||
if (dstream)
|
||||
sym_ZSTD_freeDStream(dstream);
|
||||
|
||||
if (retcode == 0) {
|
||||
lseek(memfd, 0, SEEK_SET);
|
||||
|
||||
outf = fdopen(memfd, "r");
|
||||
if (!outf) {
|
||||
ERR(file->ctx, "unable to create file stream from file descriptor: %s",
|
||||
kbd_strerror(errno, errbuf, sizeof(errbuf)));
|
||||
|
||||
if (memfd >= 0)
|
||||
close(memfd);
|
||||
}
|
||||
} else if (memfd >= 0) {
|
||||
close(memfd);
|
||||
}
|
||||
|
||||
return outf;
|
||||
}
|
||||
@ -7,6 +7,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
@ -16,14 +17,17 @@
|
||||
#include "contextP.h"
|
||||
|
||||
static struct decompressor {
|
||||
unsigned char magic[2];
|
||||
FILE *(*decompressor)(struct kbdfile *fp);
|
||||
const char *ext; /* starts with `.', has no other dots */
|
||||
const char *cmd;
|
||||
} decompressors[] = {
|
||||
{ ".gz", "gzip -d -c" },
|
||||
{ ".bz2", "bzip2 -d -c" },
|
||||
{ ".xz", "xz -d -c" },
|
||||
{ ".zst", "zstd -d -q -c" },
|
||||
{ NULL, NULL }
|
||||
{ { 0x1f, 0x8b }, kbdfile_decompressor_zlib, ".gz", "gzip -d -c" },
|
||||
{ { 0x1f, 0x9e }, kbdfile_decompressor_zlib, ".gz", "gzip -d -c" },
|
||||
{ { 0x42, 0x5a }, kbdfile_decompressor_bzip2, ".bz2", "bzip2 -d -c" },
|
||||
{ { 0xfd, 0x37 }, kbdfile_decompressor_lzma, ".xz", "xz -d -c" },
|
||||
{ { 0x28, 0xb5 }, kbdfile_decompressor_zstd, ".zst", "zstd -d -q -c" },
|
||||
{ { 0, 0 }, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
struct kbdfile *
|
||||
@ -74,6 +78,29 @@ kbdfile_set_pathname(struct kbdfile *fp, const char *pathname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int KBD_ATTR_PRINTF(2, 3)
|
||||
kbdfile_pathname_sprintf(struct kbdfile *fp, const char *fmt, ...)
|
||||
{
|
||||
ssize_t size;
|
||||
va_list ap;
|
||||
|
||||
if (fp == NULL || fmt == NULL)
|
||||
return -1;
|
||||
|
||||
va_start(ap, fmt);
|
||||
size = vsnprintf(NULL, 0, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (size < 0 || (size_t) size >= sizeof(fp->pathname))
|
||||
return -1;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(fp->pathname, sizeof(fp->pathname), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *
|
||||
kbdfile_get_file(struct kbdfile *fp)
|
||||
{
|
||||
@ -102,7 +129,7 @@ kbdfile_close(struct kbdfile *fp)
|
||||
fp->pathname[0] = '\0';
|
||||
}
|
||||
|
||||
static char *
|
||||
char *
|
||||
kbd_strerror(int errnum, char *buf, size_t buflen)
|
||||
{
|
||||
*buf = '\0';
|
||||
@ -131,7 +158,7 @@ pipe_open(const struct decompressor *dc, struct kbdfile *fp)
|
||||
sprintf(pipe_cmd, "%s %s", dc->cmd, fp->pathname);
|
||||
|
||||
fp->fd = popen(pipe_cmd, "r");
|
||||
fp->flags |= KBDFILE_PIPE;
|
||||
fp->flags |= KBDFILE_PIPE | KBDFILE_COMPRESSED;
|
||||
|
||||
if (!(fp->fd)) {
|
||||
char buf[200];
|
||||
@ -144,78 +171,130 @@ pipe_open(const struct decompressor *dc, struct kbdfile *fp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If a file PATHNAME exists, then open it.
|
||||
If is has a `compressed' extension, then open a pipe reading it */
|
||||
static int
|
||||
maybe_pipe_open(struct kbdfile *fp)
|
||||
open_pathname(struct kbdfile *fp)
|
||||
{
|
||||
char *t;
|
||||
FILE *f;
|
||||
char buf[200];
|
||||
unsigned char magic[2];
|
||||
struct stat st;
|
||||
struct decompressor *dc;
|
||||
|
||||
if (stat(fp->pathname, &st) == -1 || !S_ISREG(st.st_mode) || access(fp->pathname, R_OK) == -1)
|
||||
if (!fp || stat(fp->pathname, &st) < 0 || !S_ISREG(st.st_mode))
|
||||
return -1;
|
||||
|
||||
t = strrchr(fp->pathname, '.');
|
||||
if (t) {
|
||||
for (dc = &decompressors[0]; dc->cmd; dc++) {
|
||||
if (strcmp(t, dc->ext) == 0)
|
||||
return pipe_open(dc, fp);
|
||||
}
|
||||
}
|
||||
errno = 0;
|
||||
f = fopen(fp->pathname, "r");
|
||||
|
||||
fp->flags &= ~KBDFILE_PIPE;
|
||||
|
||||
if ((fp->fd = fopen(fp->pathname, "r")) == NULL) {
|
||||
char buf[200];
|
||||
if (!f) {
|
||||
ERR(fp->ctx, "fopen: %s: %s", fp->pathname, kbd_strerror(errno, buf, sizeof(buf)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fp->flags &= ~KBDFILE_PIPE;
|
||||
fp->flags &= ~KBDFILE_COMPRESSED;
|
||||
|
||||
if ((size_t) st.st_size > sizeof(magic)) {
|
||||
struct decompressor *dc;
|
||||
|
||||
errno = 0;
|
||||
|
||||
if (fread(magic, sizeof(magic), 1, f) != 1) {
|
||||
ERR(fp->ctx, "fread: %s: %s", fp->pathname, kbd_strerror(errno, buf, sizeof(buf)));
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We ignore the suffix and use archive magics to avoid problems
|
||||
* with incorrect file naming.
|
||||
*/
|
||||
for (dc = &decompressors[0]; dc->cmd; dc++) {
|
||||
if (memcmp(magic, dc->magic, sizeof(magic)) != 0)
|
||||
continue;
|
||||
fclose(f);
|
||||
|
||||
if (dc->decompressor && (f = dc->decompressor(fp)) != NULL) {
|
||||
fp->flags |= KBDFILE_COMPRESSED;
|
||||
goto uncompressed;
|
||||
}
|
||||
|
||||
if (getenv("KBDFILE_IGNORE_DECOMP_UTILS") != NULL)
|
||||
return -1;
|
||||
|
||||
return pipe_open(dc, fp);
|
||||
}
|
||||
|
||||
rewind(f);
|
||||
}
|
||||
|
||||
uncompressed:
|
||||
fp->fd = f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a file PATHNAME exists, then open it.
|
||||
* If is has a `compressed' extension, then open a pipe reading it.
|
||||
*/
|
||||
static int
|
||||
maybe_pipe_open(struct kbdfile *fp)
|
||||
{
|
||||
size_t len;
|
||||
struct decompressor *dc;
|
||||
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
if (!open_pathname(fp))
|
||||
return 0;
|
||||
|
||||
len = strlen(fp->pathname);
|
||||
|
||||
/*
|
||||
* We no longer rely on suffixes to select a decompressor, but we still
|
||||
* need to check the suffix for backward compatibility.
|
||||
*/
|
||||
for (dc = &decompressors[0]; dc->cmd; dc++) {
|
||||
if (len + strlen(dc->ext) >= sizeof(fp->pathname))
|
||||
continue;
|
||||
|
||||
fp->pathname[len] = '\0';
|
||||
strcat(fp->pathname, dc->ext);
|
||||
|
||||
if (!open_pathname(fp))
|
||||
return 0;
|
||||
}
|
||||
|
||||
fp->pathname[len] = '\0';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
findfile_by_fullname(const char *fnam, const char *const *suffixes, struct kbdfile *fp)
|
||||
{
|
||||
int i;
|
||||
struct stat st;
|
||||
struct decompressor *dc;
|
||||
size_t fnam_len, sp_len;
|
||||
|
||||
fp->flags &= ~KBDFILE_PIPE;
|
||||
fnam_len = strlen(fnam);
|
||||
fp->flags &= ~KBDFILE_COMPRESSED;
|
||||
|
||||
for (i = 0; suffixes[i]; i++) {
|
||||
if (suffixes[i] == NULL)
|
||||
continue; /* we tried it already */
|
||||
|
||||
sp_len = strlen(suffixes[i]);
|
||||
|
||||
if (fnam_len + sp_len + 1 > sizeof(fp->pathname))
|
||||
if (kbdfile_pathname_sprintf(fp, "%s%s", fnam, suffixes[i]) < 0)
|
||||
continue;
|
||||
|
||||
snprintf(fp->pathname, sizeof(fp->pathname), "%s%s", fnam, suffixes[i]);
|
||||
|
||||
if (stat(fp->pathname, &st) == 0 && S_ISREG(st.st_mode) && (fp->fd = fopen(fp->pathname, "r")) != NULL)
|
||||
if (!maybe_pipe_open(fp))
|
||||
return 0;
|
||||
|
||||
for (dc = &decompressors[0]; dc->cmd; dc++) {
|
||||
if (fnam_len + sp_len + strlen(dc->ext) + 1 > sizeof(fp->pathname))
|
||||
continue;
|
||||
|
||||
snprintf(fp->pathname, sizeof(fp->pathname), "%s%s%s", fnam, suffixes[i], dc->ext);
|
||||
|
||||
if (stat(fp->pathname, &st) == 0 && S_ISREG(st.st_mode) && access(fp->pathname, R_OK) == 0)
|
||||
return pipe_open(dc, fp);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
filecmp(const char *fname, const char *name, const char *const *suf, unsigned int *index, struct decompressor **d)
|
||||
filecmp(const char *fname, const char *name, const char *const *suf, unsigned int *index)
|
||||
{
|
||||
/* Does d_name start right? */
|
||||
const char *p = name;
|
||||
@ -232,7 +311,6 @@ filecmp(const char *fname, const char *name, const char *const *suf, unsigned in
|
||||
if (!strcmp(p, suf[i])) {
|
||||
if (i < *index) {
|
||||
*index = i;
|
||||
*d = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -246,7 +324,6 @@ filecmp(const char *fname, const char *name, const char *const *suf, unsigned in
|
||||
if (!strcmp(p + l, dc->ext)) {
|
||||
if (i < *index) {
|
||||
*index = i;
|
||||
*d = dc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -266,6 +343,7 @@ findfile_in_dir(const char *fnam, const char *dir, const int recdepth, const cha
|
||||
|
||||
fp->fd = NULL;
|
||||
fp->flags &= ~KBDFILE_PIPE;
|
||||
fp->flags &= ~KBDFILE_COMPRESSED;
|
||||
|
||||
dir_len = strlen(dir);
|
||||
|
||||
@ -291,7 +369,6 @@ findfile_in_dir(const char *fnam, const char *dir, const int recdepth, const cha
|
||||
goto EndScan;
|
||||
}
|
||||
|
||||
struct decompressor *dc = NULL;
|
||||
unsigned int index = UINT_MAX;
|
||||
|
||||
// Scan the directory twice: first for files, then
|
||||
@ -343,35 +420,35 @@ StartScan:
|
||||
if (secondpass || ff)
|
||||
continue;
|
||||
|
||||
snprintf(fp->pathname, sizeof(fp->pathname), "%s/%s", dir, namelist[n]->d_name);
|
||||
if (kbdfile_pathname_sprintf(fp, "%s/%s", dir, namelist[n]->d_name) < 0)
|
||||
continue;
|
||||
|
||||
if (stat(fp->pathname, &st) || !S_ISREG(st.st_mode))
|
||||
continue;
|
||||
|
||||
if (!filecmp(fnam, namelist[n]->d_name, suf, &index, &dc)) {
|
||||
if (!filecmp(fnam, namelist[n]->d_name, suf, &index)) {
|
||||
/*
|
||||
* We cannot immediately try to open the file because
|
||||
* the suffixes are specified in order of priority. We
|
||||
* need to find the lowest index.
|
||||
*/
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!secondpass && index != UINT_MAX) {
|
||||
snprintf(fp->pathname, sizeof(fp->pathname), "%s/%s%s%s", dir, fnam, suf[index], (dc ? dc->ext : ""));
|
||||
|
||||
if (!dc) {
|
||||
rc = maybe_pipe_open(fp);
|
||||
if (!secondpass) {
|
||||
if (index != UINT_MAX) {
|
||||
rc = rc ?: kbdfile_pathname_sprintf(fp, "%s/%s%s", dir, fnam, suf[index]);
|
||||
rc = rc ?: maybe_pipe_open(fp);
|
||||
goto EndScan;
|
||||
}
|
||||
|
||||
if (pipe_open(dc, fp) < 0) {
|
||||
rc = -1;
|
||||
goto EndScan;
|
||||
if (recdepth > 0) {
|
||||
secondpass = 1;
|
||||
goto StartScan;
|
||||
}
|
||||
}
|
||||
|
||||
if (recdepth > 0 && !secondpass) {
|
||||
secondpass = 1;
|
||||
goto StartScan;
|
||||
}
|
||||
|
||||
EndScan:
|
||||
if (namelist != NULL) {
|
||||
for (int n = 0; n < dirents; n++)
|
||||
@ -396,9 +473,10 @@ kbdfile_find(const char *fnam, const char *const *dirpath, const char *const *su
|
||||
}
|
||||
|
||||
fp->flags &= ~KBDFILE_PIPE;
|
||||
fp->flags &= ~KBDFILE_COMPRESSED;
|
||||
|
||||
/* Try explicitly given name first */
|
||||
strncpy(fp->pathname, fnam, sizeof(fp->pathname) - 1);
|
||||
kbdfile_set_pathname(fp, fnam);
|
||||
|
||||
if (!maybe_pipe_open(fp))
|
||||
return 0;
|
||||
@ -466,5 +544,5 @@ kbdfile_open(struct kbdfile_ctx *ctx, const char *filename)
|
||||
int
|
||||
kbdfile_is_compressed(struct kbdfile *fp)
|
||||
{
|
||||
return (fp->flags & KBDFILE_PIPE);
|
||||
return (fp->flags & KBDFILE_COMPRESSED);
|
||||
}
|
||||
|
||||
@ -40,4 +40,5 @@ message "system info"
|
||||
apt_get_install \
|
||||
autoconf automake autopoint libtool libtool-bin pkg-config \
|
||||
make bison flex gettext kbd strace valgrind libpam0g-dev \
|
||||
libz-dev libbz2-dev liblzma-dev libzstd-dev \
|
||||
gcc
|
||||
|
||||
@ -0,0 +1 @@
|
||||
qwerty
|
||||
Binary file not shown.
Binary file not shown.
BIN
tests/data/findfile/test_0/keymaps/i386/qwerty/test3.map.xz
Normal file
BIN
tests/data/findfile/test_0/keymaps/i386/qwerty/test3.map.xz
Normal file
Binary file not shown.
BIN
tests/data/findfile/test_0/keymaps/i386/qwerty/test3.map.zst
Normal file
BIN
tests/data/findfile/test_0/keymaps/i386/qwerty/test3.map.zst
Normal file
Binary file not shown.
@ -62,7 +62,7 @@ AT_CLEANUP
|
||||
|
||||
AT_SETUP([test 13])
|
||||
AT_KEYWORDS([libkbdfile unittest])
|
||||
AT_SKIP_IF([ test "$(arch)" != "ppc64el" ])
|
||||
AT_SKIP_IF([ test "$(arch)" = "ppc64el" ])
|
||||
UNITTEST_MEMCHECK([$abs_builddir/libkbdfile/libkbdfile-test13])
|
||||
AT_CLEANUP
|
||||
|
||||
@ -70,3 +70,8 @@ AT_SETUP([test 14])
|
||||
AT_KEYWORDS([libkbdfile unittest])
|
||||
UNITTEST_MEMCHECK([$abs_builddir/libkbdfile/libkbdfile-test14])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([test 15])
|
||||
AT_KEYWORDS([libkbdfile unittest])
|
||||
UNITTEST_MEMCHECK([$abs_builddir/libkbdfile/libkbdfile-test15])
|
||||
AT_CLEANUP
|
||||
|
||||
@ -28,4 +28,5 @@ noinst_PROGRAMS = \
|
||||
libkbdfile-test12 \
|
||||
libkbdfile-test13 \
|
||||
libkbdfile-test14 \
|
||||
libkbdfile-test15 \
|
||||
$(NULL)
|
||||
|
||||
@ -17,14 +17,20 @@ main(int argc KBD_ATTR_UNUSED, char **argv KBD_ATTR_UNUSED)
|
||||
const char *const suffixes[] = { "", ".kmap", ".map", NULL };
|
||||
|
||||
const char *expect = TESTDIR "/data/findfile/test_0/keymaps/i386/qwertz/test2";
|
||||
const char *const searches[] = { "test2", "qwertz/test2", "i386/qwertz/test2", NULL };
|
||||
|
||||
int rc = kbdfile_find("test2", dirpath, suffixes, fp);
|
||||
for (int i = 0; searches[i]; i++) {
|
||||
int rc;
|
||||
|
||||
if (rc != 0)
|
||||
kbd_error(EXIT_FAILURE, 0, "unable to find file");
|
||||
rc = kbdfile_find(searches[i], dirpath, suffixes, fp);
|
||||
if (rc != 0)
|
||||
kbd_error(EXIT_FAILURE, 0, "unable to find file: %s", searches[i]);
|
||||
|
||||
if (strcmp(expect, kbdfile_get_pathname(fp)) != 0)
|
||||
kbd_error(EXIT_FAILURE, 0, "unexpected file: %s (expected %s)", kbdfile_get_pathname(fp), expect);
|
||||
if (strcmp(expect, kbdfile_get_pathname(fp)) != 0)
|
||||
kbd_error(EXIT_FAILURE, 0, "unexpected file: %s (expected %s)", kbdfile_get_pathname(fp), expect);
|
||||
|
||||
kbdfile_close(fp);
|
||||
}
|
||||
|
||||
kbdfile_free(fp);
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ main(int argc KBD_ATTR_UNUSED, char **argv KBD_ATTR_UNUSED)
|
||||
|
||||
int rc = 0;
|
||||
|
||||
rc = kbdfile_find("simple-1.psf.gz", dirpath, suffixes, fp);
|
||||
rc = kbdfile_find("simple-1.psf", dirpath, suffixes, fp);
|
||||
|
||||
if (rc != 0)
|
||||
kbd_error(EXIT_FAILURE, 0, "unable to find file");
|
||||
|
||||
86
tests/libkbdfile/libkbdfile-test15.c
Normal file
86
tests/libkbdfile/libkbdfile-test15.c
Normal file
@ -0,0 +1,86 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <kbdfile.h>
|
||||
#include "libcommon.h"
|
||||
|
||||
int
|
||||
main(int argc KBD_ATTR_UNUSED, char **argv KBD_ATTR_UNUSED)
|
||||
{
|
||||
struct kbdfile *fp = kbdfile_new(NULL);
|
||||
if (!fp)
|
||||
kbd_error(EXIT_FAILURE, 0, "unable to create kbdfile");
|
||||
|
||||
const char *const dirpath[] = { "", TESTDIR "/data/findfile/test_0/keymaps/**", NULL };
|
||||
const char *const suffixes[] = { ".map", NULL };
|
||||
|
||||
struct testcase {
|
||||
const char *file;
|
||||
const char *text;
|
||||
int compressed;
|
||||
} cases[] = {
|
||||
#ifdef HAVE_ZLIB
|
||||
{ TESTDIR "/data/findfile/test_0/keymaps/i386/qwerty/test3.map.gz", "qwerty zlib", 1 },
|
||||
#endif
|
||||
#ifdef HAVE_BZIP2
|
||||
{ TESTDIR "/data/findfile/test_0/keymaps/i386/qwerty/test3.map.bz2", "qwerty bzip2", 1 },
|
||||
#endif
|
||||
#ifdef HAVE_LZMA
|
||||
{ TESTDIR "/data/findfile/test_0/keymaps/i386/qwerty/test3.map.xz", "qwerty lzma", 1 },
|
||||
#endif
|
||||
#ifdef HAVE_ZSTD
|
||||
{ TESTDIR "/data/findfile/test_0/keymaps/i386/qwerty/test3.map.zst", "qwerty zstd", 1 },
|
||||
#endif
|
||||
{ TESTDIR "/data/findfile/test_0/keymaps/i386/qwerty/test3.map", "qwerty", 0 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
struct testcase *ts = &cases[0];
|
||||
|
||||
setenv("KBDFILE_IGNORE_DECOMP_UTILS", "1", 1);
|
||||
|
||||
for (ts = &cases[0]; ts->file; ts++) {
|
||||
char buf[256];
|
||||
size_t len;
|
||||
FILE *f;
|
||||
|
||||
//kbd_warning(0, "Check: %s", ts->file);
|
||||
|
||||
int rc = kbdfile_find(ts->file, dirpath, suffixes, fp);
|
||||
|
||||
if (rc != 0)
|
||||
kbd_error(EXIT_FAILURE, 0, "unable to find file: %s", ts->file);
|
||||
|
||||
if (strcmp(ts->file, kbdfile_get_pathname(fp)) != 0)
|
||||
kbd_error(EXIT_FAILURE, 0, "unexpected file: %s (expected %s)",
|
||||
kbdfile_get_pathname(fp), ts->file);
|
||||
|
||||
if (ts->compressed && !kbdfile_is_compressed(fp))
|
||||
kbd_error(EXIT_FAILURE, 0, "not compressed: %s",
|
||||
kbdfile_get_pathname(fp));
|
||||
|
||||
f = kbdfile_get_file(fp);
|
||||
if (!f)
|
||||
kbd_error(EXIT_FAILURE, 0, "unable to get file: %s", ts->file);
|
||||
|
||||
if (fgets(buf, sizeof(buf), f) == NULL)
|
||||
kbd_error(EXIT_FAILURE, 0, "unable to read file: %s", ts->file);
|
||||
|
||||
len = strlen(buf);
|
||||
|
||||
if (buf[len - 1] == '\n')
|
||||
buf[len - 1] = '\0';
|
||||
|
||||
if (strcmp(buf, ts->text))
|
||||
kbd_error(EXIT_FAILURE, 0, "unexpected content of file: %s", ts->file);
|
||||
|
||||
kbdfile_close(fp);
|
||||
}
|
||||
|
||||
kbdfile_free(fp);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user