Mirror the zstd handling for xz: add an "xz" matrix dimension that
installs the liblzma development package for the "enabled" variant and
omits it for the "disabled" one, and verify with check-xz.sh that
xz-decompress.c is compiled iff liblzma was available. liblzma ships in
the Arch base image, so the xz-disabled variant is excluded there.
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Some distributions (e.g. Fedora) use xz compression for the firmware
files stored in /lib/firmware. Add an xz decompression path using
liblzma, mirroring the existing zstd support, and try the .xz variant
when opening a possibly-compressed file.
Closes: #46
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Add an Android job to the CI that cross-compiles tqftpserv with the
Android NDK for the arm64-v8a, armeabi-v7a and x86_64 ABIs. libqrtr is
cross-built from source and linked in, matching the Android.bp build.
The NDK download and the per-ABI libqrtr build are cached.
Define the ANDROID macro when building for an Android host so the vendor
firmware paths in translate.c are selected.
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
The seek option value is an off_t, but it was formatted with %zd, which
consumes an ssize_t. On 32-bit platforms with _FILE_OFFSET_BITS=64 off_t
is 64-bit while ssize_t is 32-bit, so the wrong value is emitted (and the
NDK build flags it as a -Wformat error). Cast to size_t to match the
conversion specifier, as is done for the other numeric options.
Fixes: 7557ca4754 ("tftp: Ensure that the oack message stays within bounds")
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Add a GitHub Actions workflow modeled on the cdba project's CI. It builds
tqftpserv across a matrix of distributions (Alpine, Arch, Debian, Fedora,
Ubuntu) and compilers (gcc, clang).
The matrix additionally toggles zstd support: the "enabled" variant
installs the libzstd development package, the "disabled" variant does
not, so that both autodetected configurations are exercised. A small
check verifies that the autodetection matched the requested variant.
qrtr is installed from the distribution packages where available; on Arch
(where it only exists in the AUR) it is built from source. zstd is part
of the Arch base image and cannot be removed, so the zstd-disabled
variant is excluded there.
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Some devices (motorola-dubai) need to access readwrite files found in the
persist partition in `rfs/msm/mpss` to have working modem.
Create a new systemd unit that calls a shell script to copy the files
into the readwrite folder (currently /var/lib/tqftpserv).
The units runs only if and after /dev/disk/by-partlabel/persist is available.
Signed-off-by: Federico Amedeo Izzo <federico@izzo.pro>
Support old libqrtr that does not ship a qrtr.pc pkg-config file by
locating the library and its header directly when the pkg-config lookup
fails.
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Make libzstd an optional, autodetected dependency instead of a hard
requirement: build in the zstd decompression path when libzstd is
available and fall back to the stub in zstd-decompress.h otherwise. This
lets tqftpserv be built on systems without libzstd and makes it possible
to exercise both configurations in CI.
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
The fallback zstd_decompress_file() stub, used when built without zstd
support, calls fprintf() but relied on the includer having pulled in
stdio.h already. Include it directly so the header is self-contained.
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
systemd_system_unit_dir was only assigned inside the if/elif handling the
systemd-unit-prefix option and a found systemd pkg-config file. When
neither applies (e.g. on musl-based distros without a systemd .pc file),
the variable was left undefined and referenced later, aborting configure.
Add an else branch that sets it to an empty string.
Fixes: d4c7f06435 ("build: allow building systemd unit files without systemd dependency")
Assisted-by: Claude Code:claude-opus-4-8
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Replace strcpy() with memcpy() when copying the error message into
the TFTP error packet buffer. The buffer length is already computed
as (4 + strlen(msg) + 1), so memcpy with (len - 4) copies exactly
the right amount. This avoids reliance on strcpy's null-terminator
scanning behavior for a fixed-length copy.
Signed-off-by: OrbisAI Security <mediratta01.pally@gmail.com>
The translate_readwrite() function opens files relative to the
persistent readwrite directory based on client-supplied paths.
A malicious TFTP client could use '..' sequences to escape the
intended directory and read/write arbitrary files on the host.
Add a strstr() check to reject any path containing '..' before
opening the file.
Signed-off-by: OrbisAI Security <mediratta01.pally@gmail.com>
asprintf() can fail, keeping the path_with_zstd_extension unchanged,
which can lead to further errors / crashes. Return early if asprintf
fails.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Some Qualcomm modem firmware (Xiaomi raphael on sm8150, db845c, ...)
requests its protection-domain firmware blobs via TFTP-over-QRTR at
Android-style vendor partition paths, which tqftpserv currently
rejects with "invalid path ... rejecting", leaving the wifi (or other)
protection domain unloaded.
Add two more readonly prefixes that delegate to translate_readonly()
(the existing remoteproc-firmware-dir search):
/readonly/vendor/firmware/ - Xiaomi raphael (sm8150), ...
/readonly/vendor/firmware_mnt/image/ - db845c (per issue #7)
For this to actually serve a blob, the file needs to exist in one of
the firmware directories that translate_readonly() iterates (i.e.
alongside a remoteproc's firmware= attribute). On postmarketOS
xiaomi-raphael, the firmware-xiaomi-raphael-wlan package currently
ships wlanmdsp.mbn at /lib/firmware/wlanmdsp.mbn; once that is moved
(or symlinked) into /lib/firmware/qcom/sm8150/xiaomi/raphael/
(alongside modem.mbn), ath10k_snoc completes its probe, phy0 appears,
and NetworkManager auto-creates wlan0.
Refs: https://github.com/linux-msm/tqftpserv/issues/7
Signed-off-by: Astro Enigma <astronazer@gmail.com>
On qcm6490, the inbuilt modem requests firmware images via TFTP
RRQ using the path prefix /readonly/firmware/modem_pr/so/ instead of the
previously expected /readonly/firmware/image/. The server fails to match
this path and does not invoke translate_readonly, causing the modem boot
to fail.
Add READONLY_FW_BASE and READONLY_MODEM_PATH macros to explicitly match
/readonly/firmware/modem_pr and reuse translate_readonly() by passing
the modem_pr relative path (e.g., modem_pr/so/file.mbn).
translate_readonly() then searches the remoteproc firmware directory
and constructs the full path as:
READONLY_FW_BASE/<fw_dir>/modem_pr/so/file.mbn
With this fix, the modem boots successfully and the link comes up.
Signed-off-by: Bhaskar Valaboju <bhaskar.valaboju@oss.qualcomm.com>
The macro name TQFTPSERV_TMP is misleading since /var/lib/tqftpserv
is persistent storage, not a temporary directory. Rename it to
TQFTPSERV_RW_DIR to reflect its role as the base directory for
/readwrite/ path requests (WRQ operations).
Also update the warn() messages and kdoc comment in
translate_readwrite() to drop the stale "temporary" wording.
Signed-off-by: Bhaskar Valaboju <bhaskar.valaboju@oss.qualcomm.com>
Modem subsystems write calibration data and other persistent files via
TFTP to the readwrite path. These files must survive system reboots to
avoid re-calibration on every boot cycle.
Change TQFTPSERV_TMP from /tmp/tqftpserv to /var/lib/tqftpserv on
non-Android builds. The /tmp directory is typically a tmpfs that is
cleared on reboot, which would cause loss of modem calibration data.
/var/lib/tqftpserv provides persistent storage that survives reboots
and is the appropriate location for application state data.
Update tqftpserv.service.in and include StateDirectory to let systemd
create /var/lib/tqftpserv if it is not present.
Signed-off-by: Bhaskar Valaboju <bhaskar.valaboju@oss.qualcomm.com>