1 Commits
Author SHA1 Message Date
Konrad Dybcio 96f76d8481 treewide: meson 2024-04-23 19:30:30 +02:00
7 changed files with 40 additions and 321 deletions

No files matched your search

-53
View File
@@ -1,53 +0,0 @@
# tqftpserv
The tqftpserv software is an implementation of a TFTP (Trivial File Transfer Protocol) server which runs on top of the AF_QIPCRTR (a.k.a QRTR) socket type.
The main purpose of tqftpserv is to serve files from the Linux file system to other processors on the Qualcomm SoCs as requested.
The protocol implemented here is (loosely) based on RFC 1350 including some extensions to the protocol.
In basic terms, the protocol supports RRQ (Read Request) and WRQ (Write Request) messages which read and write files respectively. A request can have some extra options, like `blksize` ior `wsize`. The meaning of those is documented in the `parse_options` function.
For reference, the proprietary implementation of this that is used on practically all Qualcomm-based Android devices is called "tftp_server".
## File paths
There's two different virtual paths prefixes that are supported:
* `/readonly/firmware/image/` for read-only files such as firmware files
* `/readwrite/` for read-write (temporary) files
Requests to those paths are "translated" (see `translate.c`) to paths in the Linux filesystem. In a regular setup readonly requests go to `/lib/firmware/` and readwrite requests go to `/tmp/tqftpserv/`.
Translating those readonly request paths tries to take into account custom Linux firmware paths, custom "firmware-name" paths for remoteprocs, .zstd compressed firmware and more. In case of doubt, please consult the source code.
For example on a QCM6490 Fairphone 5 smartphone the path for `/readonly/firmware/image/modem_pr/so/901_0_0.mbn` will be translated to `/lib/firmware/qcom/qcm6490/fairphone5/modem_pr/so/901_0_0.mbn`, based on the devicetree property `firmware-name = "qcom/qcm6490/fairphone5/modem.mbn";` in the modem/mpss DT node.
The actual paths that are used and requested are dependent on the TFTP clients, which is usually the firmware that is running on the Hexagon-based modem processor on the SoC.
## Example requests
The following are some example requests which are grabbed from the tqftpserv log during modem bootup.
* Write content to a file called "server_check.txt":
```
[TQFTP] WRQ: /readwrite/server_check.txt (octet)
```
* Read a file called "ota_firewall/ruleset" which does not exist:
```
[TQFTP] RRQ: /readwrite/ota_firewall/ruleset (mode=octet rsize=0 seek=0)
tqftpserv: failed to open ota_firewall/ruleset: No such file or directory
[TQFTP] unable to open /readwrite/ota_firewall/ruleset (2), reject
```
* Stat a file with the path "modem_pr/so/901_0_0.mbn", and then read it:
```
[TQFTP] RRQ: /readonly/firmware/image/modem_pr/so/901_0_0.mbn (mode=octet rsize=0 seek=0)
[TQFTP] Remote returned END OF TRANSFER: 9 - End of Transfer
[TQFTP] RRQ: /readonly/firmware/image/modem_pr/so/901_0_0.mbn (mode=octet rsize=52 seek=0)
```
In binary such a request can look like the following:
```
\0\1/readonly/firmware/image/modem_pr/so/901_0_0.mbn\0octet\0blksize\0007680\0timeoutms\0001000\0tsize\0000\0wsize\00010\0
```
+15 -28
View File
@@ -1,45 +1,32 @@
# SPDX-License-Identifier: BSD-3-Clause
project('tqftpserv',
'c',
version: '1.1.1',
default_options: [
'warning_level=1',
'buildtype=release',
])
prefix = get_option('prefix')
zstd_dep = dependency('libzstd')
add_project_arguments('-DHAVE_ZSTD', language : 'c')
# Not required to build the executable,
# only to automatically retrieve the install dir for unit files
systemd = dependency('systemd', required : false)
if get_option('systemd-unit-prefix') != ''
systemd_system_unit_dir = get_option('systemd-unit-prefix')
elif systemd.found()
systemd_system_unit_dir = systemd.get_variable(
pkgconfig : 'systemdsystemunitdir',
pkgconfig_define: ['prefix', prefix])
systemd = dependency('systemd')
systemd_system_unit_dir = get_option('systemd-unit-prefix')
if systemd_system_unit_dir == ''
systemd_system_unit_dir = systemd.get_variable(
pkgconfig : 'systemdsystemunitdir',
pkgconfig_define: ['prefix', prefix])
endif
qrtr_dep = dependency('qrtr')
tqftpserv_srcs = ['translate.c',
'tqftpserv.c',
'zstd-decompress.c']
'tqftpserv.c']
executable('tqftpserv',
tqftpserv_srcs,
dependencies : [qrtr_dep, zstd_dep],
dependencies : qrtr_dep,
install : true)
if systemd_system_unit_dir != ''
systemd_unit_conf = configuration_data()
systemd_unit_conf.set('prefix', prefix)
configure_file(
input : 'tqftpserv.service.in',
output : 'tqftpserv.service',
configuration : systemd_unit_conf,
install_dir : systemd_system_unit_dir)
endif
systemd_unit_conf = configuration_data()
systemd_unit_conf.set('prefix', prefix)
configure_file(
input : 'tqftpserv.service.in',
output : 'tqftpserv.service',
configuration : systemd_unit_conf,
install_dir : systemd_system_unit_dir)
+20 -79
View File
@@ -46,12 +46,6 @@ struct tftp_client {
size_t wsize;
unsigned int timeoutms;
off_t seek;
uint8_t *blk_buf;
uint8_t *rw_buf;
size_t rw_buf_size;
size_t blk_offset;
uint16_t blk_expected;
};
static struct list_head readers = LIST_INIT(readers);
@@ -62,8 +56,11 @@ static ssize_t tftp_send_data(struct tftp_client *client,
{
ssize_t len;
size_t send_len;
char *buf = client->blk_buf;
char *p = buf;
char *buf;
char *p;
buf = malloc(4 + client->blksize);
p = buf;
*p++ = 0;
*p++ = OP_DATA;
@@ -94,7 +91,11 @@ static ssize_t tftp_send_data(struct tftp_client *client,
}
// printf("[TQFTP] Sending %zd bytes of DATA\n", send_len);
return send(client->sock, buf, send_len, 0);
len = send(client->sock, buf, send_len, 0);
free(buf);
return len;
}
@@ -250,7 +251,7 @@ static void handle_rrq(const char *buf, size_t len, struct sockaddr_qrtr *sq)
size_t blksize = 512;
unsigned int timeoutms = 1000;
size_t rsize = 0;
size_t wsize = 1;
size_t wsize = 0;
off_t seek = 0;
bool do_oack = false;
int sock;
@@ -314,19 +315,6 @@ static void handle_rrq(const char *buf, size_t len, struct sockaddr_qrtr *sq)
client->wsize = wsize;
client->timeoutms = timeoutms;
client->seek = seek;
client->rw_buf_size = blksize * wsize;
client->blk_buf = calloc(1, blksize + 4);
if (!client->blk_buf) {
printf("[TQFTP] Memory allocation failure\n");
return;
}
client->rw_buf = calloc(1, client->rw_buf_size);
if (!client->rw_buf) {
printf("[TQFTP] Memory allocation failure\n");
return;
}
// printf("[TQFTP] new reader added\n");
@@ -354,7 +342,7 @@ static void handle_wrq(const char *buf, size_t len, struct sockaddr_qrtr *sq)
size_t blksize = 512;
unsigned int timeoutms = 1000;
size_t rsize = 0;
size_t wsize = 1;
size_t wsize = 0;
off_t seek = 0;
bool do_oack = false;
int sock;
@@ -409,20 +397,6 @@ static void handle_wrq(const char *buf, size_t len, struct sockaddr_qrtr *sq)
client->wsize = wsize;
client->timeoutms = timeoutms;
client->seek = seek;
client->rw_buf_size = blksize * wsize;
client->blk_expected = 1;
client->blk_buf = calloc(1, blksize + 4);
if (!client->blk_buf) {
printf("[TQFTP] Memory allocation failure\n");
return;
}
client->rw_buf = calloc(1, client->rw_buf_size);
if (!client->rw_buf) {
printf("[TQFTP] Memory allocation failure\n");
return;
}
// printf("[TQFTP] new writer added\n");
@@ -519,14 +493,14 @@ static int handle_writer(struct tftp_client *client)
struct sockaddr_qrtr sq;
uint16_t block;
size_t payload;
uint8_t *buf = client->blk_buf;
char buf[516];
socklen_t sl;
ssize_t len;
int opcode;
int ret;
sl = sizeof(sq);
len = recvfrom(client->sock, buf, client->blksize + 4, 0, (void *)&sq, &sl);
len = recvfrom(client->sock, buf, sizeof(buf), 0, (void *)&sq, &sl);
if (len < 0) {
ret = -errno;
if (ret != -ENETRESET)
@@ -548,48 +522,17 @@ static int handle_writer(struct tftp_client *client)
}
payload = len - 4;
buf += 4;
/* Check if we recieved expected block */
if (block != client->blk_expected) {
uint16_t blk_expected = client->blk_expected;
printf("[TQFTP] Block number out of sequence: %d (expected %d)\n",
block, blk_expected);
tftp_send_error(client->sock, 4, "Block number out of sequence");
/* Set blk_expected to beginning of current window */
if ((blk_expected % client->wsize) == 0)
blk_expected -= client->wsize + 1;
else
blk_expected -= (blk_expected % client->wsize) - 1;
client->blk_expected = blk_expected;
client->blk_offset = 0;
ret = write(client->fd, buf + 4, payload);
if (ret < 0) {
/* XXX: report error */
printf("[TQFTP] failed to write data\n");
return -1;
}
client->blk_expected++;
tftp_send_ack(client->sock, block);
/* Copy the data to the destination buffer */
memcpy(client->rw_buf + client->blk_offset, buf, payload);
client->blk_offset += payload;
/* Write to file if all the wsize blocks are recieved */
if (block % client->wsize == 0) {
ret = write(client->fd, client->rw_buf, client->blk_offset);
if (ret < 0) {
/* XXX: report error */
printf("[TQFTP] failed to write data\n");
return -1;
}
client->blk_offset = 0;
tftp_send_ack(client->sock, block);
}
return payload == client->blksize ? 1 : 0;
return payload == 512 ? 1 : 0;
}
static void client_close_and_free(struct tftp_client *client)
@@ -597,8 +540,6 @@ static void client_close_and_free(struct tftp_client *client)
list_del(&client->node);
close(client->sock);
close(client->fd);
free (client->blk_buf);
free (client->rw_buf);
free(client);
}
+2
View File
@@ -1,5 +1,7 @@
[Unit]
Description=QRTR TFTP service
Requires=qrtr-ns.service
After=qrtr-ns.service
[Service]
ExecStart=@prefix@/bin/tqftpserv
+3 -43
View File
@@ -16,7 +16,6 @@
#include <unistd.h>
#include "translate.h"
#include "zstd-decompress.h"
#define READONLY_PATH "/readonly/firmware/image/"
#define READWRITE_PATH "/readwrite/"
@@ -24,14 +23,11 @@
#ifndef ANDROID
#define FIRMWARE_BASE "/lib/firmware/"
#define TQFTPSERV_TMP "/tmp/tqftpserv"
#define UPDATES_DIR "updates/"
#else
#define FIRMWARE_BASE "/vendor/firmware/"
#define TQFTPSERV_TMP "/data/vendor/tmp/tqftpserv"
#endif
static int open_maybe_compressed(const char *path);
static void read_fw_path_from_sysfs(char *outbuffer, size_t bufsize)
{
size_t pathsize;
@@ -122,7 +118,7 @@ static int translate_readonly(const char *file)
strcat(path, "/");
strcat(path, file);
fd = open_maybe_compressed(path);
fd = open(path, O_RDONLY);
if (fd >= 0)
break;
if (errno != ENOENT)
@@ -130,25 +126,16 @@ static int translate_readonly(const char *file)
}
/* now try with base path */
if (strlen(FIRMWARE_BASE) + strlen(UPDATES_DIR) + strlen(firmware_value) + 1 +
if (strlen(FIRMWARE_BASE) + strlen(firmware_value) + 1 +
strlen(file) + 1 > sizeof(path))
continue;
strcpy(path, FIRMWARE_BASE);
strcat(path, UPDATES_DIR);
strcat(path, firmware_path);
strcat(path, "/");
strcat(path, file);
fd = open_maybe_compressed(path);
if (fd < 0) {
strcpy(path, FIRMWARE_BASE);
strcat(path, firmware_path);
strcat(path, "/");
strcat(path, file);
fd = open_maybe_compressed(path);
}
fd = open(path, O_RDONLY);
if (fd >= 0)
break;
@@ -214,30 +201,3 @@ int translate_open(const char *path, int flags)
errno = ENOENT;
return -1;
}
/* linux-firmware uses .zst as file extension */
#define ZSTD_EXTENSION ".zst"
/**
* open_maybe_compressed() - open a file and maybe decompress it if necessary
* @filename: path to a file that may be compressed (should not include compression format extension)
*
* Return: opened fd on success, -1 on error
*/
static int open_maybe_compressed(const char *path)
{
char *path_with_zstd_extension = NULL;
int fd = -1;
if (access(path, F_OK) == 0)
return open(path, O_RDONLY);
asprintf(&path_with_zstd_extension, "%s%s", path, ZSTD_EXTENSION);
if (access(path_with_zstd_extension, F_OK) == 0)
fd = zstd_decompress_file(path_with_zstd_extension);
free(path_with_zstd_extension);
return fd;
}
-97
View File
@@ -1,97 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2024, Stefan Hansson
* Copyright (c) 2024, Emil Velikov
*/
/* For memfd_create */
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zstd.h>
#include "zstd-decompress.h"
/**
* zstd_decompress_file() - decompress a zstd-compressed file
* @filename: path to a file to decompress
*
* Return: opened fd on success, -1 on error
*/
int zstd_decompress_file(const char *filename)
{
/* Figure out the size of the file. */
struct stat file_stat;
if (stat(filename, &file_stat) == -1) {
fprintf(stderr, "stat %s failed (%s)\n", filename, strerror(errno));
return -1;
}
const size_t file_size = file_stat.st_size;
const int input_file_fd = open(filename, O_RDONLY);
if (input_file_fd == -1) {
perror("open failed");
return -1;
}
void* const compressed_buffer = mmap(NULL, file_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, input_file_fd, 0);
if (compressed_buffer == MAP_FAILED) {
perror("mmap failed");
close(input_file_fd);
return -1;
}
close(input_file_fd);
const unsigned long long decompressed_size = ZSTD_getFrameContentSize(compressed_buffer, file_size);
if (decompressed_size == ZSTD_CONTENTSIZE_UNKNOWN) {
fprintf(stderr, "Content size could not be determined for %s\n", filename);
munmap(compressed_buffer, file_size);
return -1;
}
if (decompressed_size == ZSTD_CONTENTSIZE_ERROR) {
fprintf(stderr, "Error getting content size for %s\n", filename);
munmap(compressed_buffer, file_size);
return -1;
}
void* const decompressed_buffer = malloc((size_t)decompressed_size);
if (decompressed_buffer == NULL) {
perror("malloc failed");
munmap(compressed_buffer, file_size);
return -1;
}
const size_t return_size = ZSTD_decompress(decompressed_buffer, decompressed_size, compressed_buffer, file_size);
if (ZSTD_isError(return_size)) {
fprintf(stderr, "ZSTD_decompress failed: %s\n", ZSTD_getErrorName(return_size));
free(decompressed_buffer);
munmap(compressed_buffer, file_size);
return -1;
}
const int output_file_fd = memfd_create(filename, 0);
if (output_file_fd == -1) {
perror("memfd_create failed");
free(decompressed_buffer);
munmap(compressed_buffer, file_size);
return -1;
}
if (write(output_file_fd, decompressed_buffer, decompressed_size) != decompressed_size) {
perror("write failed");
close(output_file_fd);
free(decompressed_buffer);
munmap(compressed_buffer, file_size);
return -1;
}
return output_file_fd;
}
-21
View File
@@ -1,21 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2024, Stefan Hansson
*/
#ifndef __ZSTD_DECOMPRESS_H__
#define __ZSTD_DECOMPRESS_H__
#include <stdbool.h>
#ifdef HAVE_ZSTD
int zstd_decompress_file(const char *filename);
#else
static int zstd_decompress_file(const char *filename)
{
fprintf(stderr, "Built without ZSTD support\n");
return -1;
}
#endif
#endif