pax_global_header00006660000000000000000000000064146477202130014520gustar00rootroot0000000000000052 comment=f803b4d573006c013fcb4b76af98a363f0904112 tqftpserv-1.1/000077500000000000000000000000001464772021300134175ustar00rootroot00000000000000tqftpserv-1.1/Android.bp000066400000000000000000000002261464772021300153220ustar00rootroot00000000000000cc_binary { name: "tqftpserv", vendor: true, srcs: [ "tqftpserv.c", "translate.c", ], shared_libs: ["libqrtr"], } tqftpserv-1.1/LICENSE000066400000000000000000000030401464772021300144210ustar00rootroot00000000000000/* * Copyright (c) 2018, Linaro Ltd. * 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. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. */ tqftpserv-1.1/list.h000066400000000000000000000036201464772021300145440ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2016, Linaro Ltd. */ #ifndef __LIST_H__ #define __LIST_H__ #include #include #ifndef container_of #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member)*__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)); \ }) #endif struct list_head { struct list_head *prev; struct list_head *next; }; #define LIST_INIT(list) { &(list), &(list) } static inline void list_init(struct list_head *list) { list->prev = list->next = list; } static inline bool list_empty(struct list_head *list) { return list->next == list; } static inline void list_add(struct list_head *list, struct list_head *item) { struct list_head *prev = list->prev; item->next = list; item->prev = prev; prev->next = list->prev = item; } static inline void list_del(struct list_head *item) { item->prev->next = item->next; item->next->prev = item->prev; } #define list_for_each(item, list) \ for (item = (list)->next; item != list; item = item->next) #define list_for_each_safe(item, next, list) \ for (item = (list)->next, next = item->next; item != list; item = next, next = item->next) #define list_entry(item, type, member) \ container_of(item, type, member) #define list_entry_first(list, type, member) \ container_of((list)->next, type, member) #define list_entry_next(item, member) \ container_of((item)->member.next, typeof(*(item)), member) #define list_for_each_entry(item, list, member) \ for (item = list_entry_first(list, typeof(*(item)), member); \ &item->member != list; \ item = list_entry_next(item, member)) #define list_for_each_entry_safe(item, next, list, member) \ for (item = list_entry_first(list, typeof(*(item)), member), \ next = list_entry_next(item, member); \ &item->member != list; \ item = next, \ next = list_entry_next(item, member)) \ #endif tqftpserv-1.1/meson.build000066400000000000000000000024571464772021300155710ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause project('tqftpserv', 'c', version: '1.1', default_options: [ 'warning_level=1', 'buildtype=release', ]) prefix = get_option('prefix') zstd_dep = dependency('libzstd') # Not required to build the executable, only to install unit file systemd = dependency('systemd', required : false) if systemd.found() 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 endif qrtr_dep = dependency('qrtr') tqftpserv_srcs = ['translate.c', 'tqftpserv.c', 'zstd-decompress.c'] executable('tqftpserv', tqftpserv_srcs, dependencies : [qrtr_dep, zstd_dep], install : true) if systemd.found() 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 tqftpserv-1.1/meson_options.txt000066400000000000000000000001531464772021300170530ustar00rootroot00000000000000option('systemd-unit-prefix', type: 'string', description: 'Directory for systemd system unit files' ) tqftpserv-1.1/tqftpserv.c000066400000000000000000000337351464772021300156340ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2018, Linaro Ltd. */ #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "translate.h" #include "zstd-decompress.h" #define MAX(x, y) ((x) > (y) ? (x) : (y)) enum { OP_RRQ = 1, OP_WRQ, OP_DATA, OP_ACK, OP_ERROR, OP_OACK, }; enum { ERROR_END_OF_TRANSFER = 9, }; struct tftp_client { struct list_head node; struct sockaddr_qrtr sq; int sock; int fd; size_t block; size_t blksize; size_t rsize; size_t wsize; unsigned int timeoutms; off_t seek; }; static struct list_head readers = LIST_INIT(readers); static struct list_head writers = LIST_INIT(writers); static ssize_t tftp_send_data(struct tftp_client *client, unsigned int block, size_t offset, size_t response_size) { ssize_t len; size_t send_len; char *buf; char *p; buf = malloc(4 + client->blksize); p = buf; *p++ = 0; *p++ = OP_DATA; *p++ = (block >> 8) & 0xff; *p++ = block & 0xff; len = pread(client->fd, p, client->blksize, offset); if (len < 0) { printf("[TQFTP] failed to read data\n"); free(buf); return len; } p += len; /* If rsize was set, we should limit the data in the response to n bytes */ if (response_size != 0) { /* Header (4 bytes) + data size */ send_len = 4 + response_size; if (send_len > p - buf) { printf("[TQFTP] requested data of %ld bytes but only read %ld bytes from file, rejecting\n", response_size, len); free(buf); return -EINVAL; } } else { send_len = p - buf; } // printf("[TQFTP] Sending %zd bytes of DATA\n", send_len); len = send(client->sock, buf, send_len, 0); free(buf); return len; } static int tftp_send_ack(int sock, int block) { struct { uint16_t opcode; uint16_t block; } ack = { htons(OP_ACK), htons(block) }; return send(sock, &ack, sizeof(ack), 0); } static int tftp_send_oack(int sock, size_t *blocksize, size_t *tsize, size_t *wsize, unsigned int *timeoutms, size_t *rsize, off_t *seek) { char buf[512]; char *p = buf; int n; *p++ = 0; *p++ = OP_OACK; if (blocksize) { strcpy(p, "blksize"); p += 8; n = sprintf(p, "%zd", *blocksize); p += n; *p++ = '\0'; } if (timeoutms) { strcpy(p, "timeoutms"); p += 10; n = sprintf(p, "%d", *timeoutms); p += n; *p++ = '\0'; } if (tsize && *tsize != -1) { strcpy(p, "tsize"); p += 6; n = sprintf(p, "%zd", *tsize); p += n; *p++ = '\0'; } if (wsize) { strcpy(p, "wsize"); p += 6; n = sprintf(p, "%zd", *wsize); p += n; *p++ = '\0'; } if (rsize) { strcpy(p, "rsize"); p += 6; n = sprintf(p, "%zd", *rsize); p += n; *p++ = '\0'; } if (seek) { strcpy(p, "seek"); p += 5; n = sprintf(p, "%zd", *seek); p += n; *p++ = '\0'; } return send(sock, buf, p - buf, 0); } static int tftp_send_error(int sock, int code, const char *msg) { size_t len; char *buf; int rc; len = 4 + strlen(msg) + 1; buf = calloc(1, len); if (!buf) return -1; *(uint16_t*)buf = htons(OP_ERROR); *(uint16_t*)(buf + 2) = htons(code); strcpy(buf + 4, msg); rc = send(sock, buf, len, 0); free(buf); return rc; } static void parse_options(const char *buf, size_t len, size_t *blksize, ssize_t *tsize, size_t *wsize, unsigned int *timeoutms, size_t *rsize, off_t *seek) { const char *opt, *value; const char *p = buf; while (p < buf + len) { /* XXX: ensure we're not running off the end */ opt = p; p += strlen(p) + 1; /* XXX: ensure we're not running off the end */ value = p; p += strlen(p) + 1; /* * blksize: block size - how many bytes to send at once * timeoutms: timeout in milliseconds * tsize: total size - request to get file size in bytes * rsize: read size - how many bytes to send, not full file * wsize: window size - how many blocks to send without ACK * seek: offset from beginning of file in bytes to start reading */ if (!strcmp(opt, "blksize")) { *blksize = atoi(value); } else if (!strcmp(opt, "timeoutms")) { *timeoutms = atoi(value); } else if (!strcmp(opt, "tsize")) { *tsize = atoi(value); } else if (!strcmp(opt, "rsize")) { *rsize = atoi(value); } else if (!strcmp(opt, "wsize")) { *wsize = atoi(value); } else if (!strcmp(opt, "seek")) { *seek = atoi(value); } else { printf("[TQFTP] Ignoring unknown option '%s' with value '%s'\n", opt, value); } } } static void handle_rrq(const char *buf, size_t len, struct sockaddr_qrtr *sq) { struct tftp_client *client; const char *filename; const char *mode; struct stat sb; const char *p; ssize_t tsize = -1; size_t blksize = 512; unsigned int timeoutms = 1000; size_t rsize = 0; size_t wsize = 0; off_t seek = 0; bool do_oack = false; int sock; int ret; int fd; p = buf + 2; filename = p; p += strlen(p) + 1; mode = p; p += strlen(p) + 1; if (strcasecmp(mode, "octet")) { /* XXX: error */ printf("[TQFTP] not octet, reject\n"); return; } if (p < buf + len) { do_oack = true; parse_options(p, len - (p - buf), &blksize, &tsize, &wsize, &timeoutms, &rsize, &seek); } printf("[TQFTP] RRQ: %s (mode=%s rsize=%ld seek=%ld)\n", filename, mode, rsize, seek); sock = qrtr_open(0); if (sock < 0) { /* XXX: error */ printf("[TQFTP] unable to create new qrtr socket, reject\n"); return; } ret = connect(sock, (struct sockaddr *)sq, sizeof(*sq)); if (ret < 0) { /* XXX: error */ printf("[TQFTP] unable to connect new qrtr socket to remote\n"); return; } fd = translate_open(filename, O_RDONLY); if (fd < 0) { printf("[TQFTP] unable to open %s (%d), reject\n", filename, errno); tftp_send_error(sock, 1, "file not found"); return; } if (tsize != -1) { fstat(fd, &sb); tsize = sb.st_size; } client = calloc(1, sizeof(*client)); client->sq = *sq; client->sock = sock; client->fd = fd; client->blksize = blksize; client->rsize = rsize; client->wsize = wsize; client->timeoutms = timeoutms; client->seek = seek; // printf("[TQFTP] new reader added\n"); list_add(&readers, &client->node); if (do_oack) { tftp_send_oack(client->sock, &blksize, tsize ? (size_t*)&tsize : NULL, wsize ? &wsize : NULL, &client->timeoutms, rsize ? &rsize : NULL, seek ? &seek : NULL); } else { tftp_send_data(client, 1, 0, 0); } } static void handle_wrq(const char *buf, size_t len, struct sockaddr_qrtr *sq) { struct tftp_client *client; const char *filename; const char *mode; const char *p; ssize_t tsize = -1; size_t blksize = 512; unsigned int timeoutms = 1000; size_t rsize = 0; size_t wsize = 0; off_t seek = 0; bool do_oack = false; int sock; int ret; int fd; filename = buf + 2; mode = buf + 2 + strlen(filename) + 1; p = mode + strlen(mode) + 1; if (strcasecmp(mode, "octet")) { /* XXX: error */ printf("[TQFTP] not octet, reject\n"); return; } printf("[TQFTP] WRQ: %s (%s)\n", filename, mode); if (p < buf + len) { do_oack = true; parse_options(p, len - (p - buf), &blksize, &tsize, &wsize, &timeoutms, &rsize, &seek); } fd = translate_open(filename, O_WRONLY | O_CREAT); if (fd < 0) { /* XXX: error */ printf("[TQFTP] unable to open %s (%d), reject\n", filename, errno); return; } sock = qrtr_open(0); if (sock < 0) { /* XXX: error */ printf("[TQFTP] unable to create new qrtr socket, reject\n"); return; } ret = connect(sock, (struct sockaddr *)sq, sizeof(*sq)); if (ret < 0) { /* XXX: error */ printf("[TQFTP] unable to connect new qrtr socket to remote\n"); return; } client = calloc(1, sizeof(*client)); client->sq = *sq; client->sock = sock; client->fd = fd; client->blksize = blksize; client->rsize = rsize; client->wsize = wsize; client->timeoutms = timeoutms; client->seek = seek; // printf("[TQFTP] new writer added\n"); list_add(&writers, &client->node); if (do_oack) { tftp_send_oack(client->sock, &blksize, tsize ? (size_t*)&tsize : NULL, wsize ? &wsize : NULL, &client->timeoutms, rsize ? &rsize : NULL, seek ? &seek : NULL); } else { tftp_send_data(client, 1, 0, 0); } } static int handle_reader(struct tftp_client *client) { struct sockaddr_qrtr sq; uint16_t block; uint16_t last; char buf[128]; socklen_t sl; ssize_t len; ssize_t n = 0; int opcode; int ret; sl = sizeof(sq); len = recvfrom(client->sock, buf, sizeof(buf), 0, (void *)&sq, &sl); if (len < 0) { ret = -errno; if (ret != -ENETRESET) fprintf(stderr, "[TQFTP] recvfrom failed: %d\n", ret); return -1; } /* Drop unsolicited messages */ if (sq.sq_node != client->sq.sq_node || sq.sq_port != client->sq.sq_port) { printf("[TQFTP] Discarding spoofed message\n"); return -1; } opcode = buf[0] << 8 | buf[1]; if (opcode == OP_ERROR) { buf[len] = '\0'; int err = buf[2] << 8 | buf[3]; /* "End of Transfer" is not an error, used with stat(2)-like calls */ if (err == ERROR_END_OF_TRANSFER) printf("[TQFTP] Remote returned END OF TRANSFER: %d - %s\n", err, buf + 4); else printf("[TQFTP] Remote returned an error: %d - %s\n", err, buf + 4); return -1; } else if (opcode != OP_ACK) { printf("[TQFTP] Expected ACK, got %d\n", opcode); return -1; } last = buf[2] << 8 | buf[3]; // printf("[TQFTP] Got ack for %d\n", last); /* We've sent enough data for rsize already */ if (last * client->blksize > client->rsize) return 0; for (block = last; block < last + client->wsize; block++) { size_t offset = client->seek + block * client->blksize; size_t response_size = 0; /* Check if need to limit response size based for requested rsize */ if ((block + 1) * client->blksize > client->rsize) response_size = client->rsize % client->blksize; n = tftp_send_data(client, block + 1, offset, response_size); if (n < 0) { printf("[TQFTP] Sent block %d failed: %zd\n", block + 1, n); break; } // printf("[TQFTP] Sent block %d of %zd\n", block + 1, n); if (n == 0) break; /* We've sent enough data for rsize already */ if ((block + 1) * client->blksize > client->rsize) break; } return 1; } static int handle_writer(struct tftp_client *client) { struct sockaddr_qrtr sq; uint16_t block; size_t payload; char buf[516]; socklen_t sl; ssize_t len; int opcode; int ret; sl = sizeof(sq); len = recvfrom(client->sock, buf, sizeof(buf), 0, (void *)&sq, &sl); if (len < 0) { ret = -errno; if (ret != -ENETRESET) fprintf(stderr, "[TQFTP] recvfrom failed: %d\n", ret); return -1; } /* Drop unsolicited messages */ if (sq.sq_node != client->sq.sq_node || sq.sq_port != client->sq.sq_port) return -1; opcode = buf[0] << 8 | buf[1]; block = buf[2] << 8 | buf[3]; if (opcode != OP_DATA) { printf("[TQFTP] Expected DATA opcode, got %d\n", opcode); tftp_send_error(client->sock, 4, "Expected DATA opcode"); return -1; } payload = len - 4; ret = write(client->fd, buf + 4, payload); if (ret < 0) { /* XXX: report error */ printf("[TQFTP] failed to write data\n"); return -1; } tftp_send_ack(client->sock, block); return payload == 512 ? 1 : 0; } static void client_close_and_free(struct tftp_client *client) { list_del(&client->node); close(client->sock); close(client->fd); free(client); } int main(int argc, char **argv) { struct tftp_client *client; struct tftp_client *next; struct sockaddr_qrtr sq; struct qrtr_packet pkt; socklen_t sl; ssize_t len; char buf[4096]; fd_set rfds; int nfds; int opcode; int ret; int fd; fd = qrtr_open(0); if (fd < 0) { fprintf(stderr, "failed to open qrtr socket\n"); exit(1); } ret = qrtr_publish(fd, 4096, 1, 0); if (ret < 0) { fprintf(stderr, "failed to publish service registry service\n"); exit(1); } zstd_init(); for (;;) { FD_ZERO(&rfds); FD_SET(fd, &rfds); nfds = fd; list_for_each_entry(client, &writers, node) { FD_SET(client->sock, &rfds); nfds = MAX(nfds, client->sock); } list_for_each_entry(client, &readers, node) { FD_SET(client->sock, &rfds); nfds = MAX(nfds, client->sock); } ret = select(nfds + 1, &rfds, NULL, NULL, NULL); if (ret < 0) { if (errno == EINTR) { continue; } else { fprintf(stderr, "select failed\n"); break; } } list_for_each_entry_safe(client, next, &writers, node) { if (FD_ISSET(client->sock, &rfds)) { ret = handle_writer(client); if (ret <= 0) client_close_and_free(client); } } list_for_each_entry_safe(client, next, &readers, node) { if (FD_ISSET(client->sock, &rfds)) { ret = handle_reader(client); if (ret <= 0) client_close_and_free(client); } } if (FD_ISSET(fd, &rfds)) { sl = sizeof(sq); len = recvfrom(fd, buf, sizeof(buf), 0, (void *)&sq, &sl); if (len < 0) { ret = -errno; if (ret != -ENETRESET) fprintf(stderr, "[TQFTP] recvfrom failed: %d\n", ret); return ret; } /* Ignore control messages */ if (sq.sq_port == QRTR_PORT_CTRL) { ret = qrtr_decode(&pkt, buf, len, &sq); if (ret < 0) { fprintf(stderr, "[TQFTP] unable to decode qrtr packet\n"); return ret; } switch (pkt.type) { case QRTR_TYPE_BYE: // fprintf(stderr, "[TQFTP] got bye\n"); list_for_each_entry_safe(client, next, &writers, node) { if (client->sq.sq_node == sq.sq_node) client_close_and_free(client); } break; case QRTR_TYPE_DEL_CLIENT: // fprintf(stderr, "[TQFTP] got del_client\n"); list_for_each_entry_safe(client, next, &writers, node) { if (!memcmp(&client->sq, &sq, sizeof(sq))) client_close_and_free(client); } break; } } else { if (len < 2) continue; opcode = buf[0] << 8 | buf[1]; switch (opcode) { case OP_RRQ: handle_rrq(buf, len, &sq); break; case OP_WRQ: // printf("[TQFTP] write\n"); handle_wrq(buf, len, &sq); break; case OP_ERROR: buf[len] = '\0'; printf("[TQFTP] received error: %d - %s\n", buf[2] << 8 | buf[3], buf + 4); break; default: printf("[TQFTP] unhandled op %d\n", opcode); break; } } } } close(fd); zstd_free(); return 0; } tqftpserv-1.1/tqftpserv.service.in000066400000000000000000000002661464772021300174500ustar00rootroot00000000000000[Unit] Description=QRTR TFTP service Requires=qrtr-ns.service After=qrtr-ns.service [Service] ExecStart=@prefix@/bin/tqftpserv Restart=always [Install] WantedBy=multi-user.target tqftpserv-1.1/translate.c000066400000000000000000000132411464772021300155610ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2019, Linaro Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "translate.h" #include "zstd-decompress.h" #define READONLY_PATH "/readonly/firmware/image/" #define READWRITE_PATH "/readwrite/" #ifndef ANDROID #define FIRMWARE_BASE "/lib/firmware/" #define TQFTPSERV_TMP "/tmp/tqftpserv" #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; FILE *f = fopen("/sys/module/firmware_class/parameters/path", "rt"); if (!f) return; pathsize = fread(outbuffer, sizeof(char), bufsize, f); fclose(f); if (pathsize == 0) return; /* truncate newline */ outbuffer[pathsize - 1] = '\0'; } /** * translate_readonly() - open "file" residing with remoteproc firmware * @file: file requested, stripped of "/readonly/image/" prefix * * It is assumed that the readonly files requested by the client resides under * /lib/firmware in the same place as its associated remoteproc firmware. This * function scans through all entries under /sys/class/remoteproc and read the * dirname of each "firmware" file in an attempt to find, and open(2), the * requested file. * * As these files are readonly, it's not possible to pass flags to open(2). * * Return: opened fd on success, -1 otherwise */ static int translate_readonly(const char *file) { char firmware_value[PATH_MAX]; char *firmware_value_copy = NULL; char *firmware_path; char firmware_attr[32]; char path[PATH_MAX]; char fw_sysfs_path[PATH_MAX]; struct dirent *de; int firmware_fd; DIR *class_dir; int class_fd; ssize_t n; int fd = -1; read_fw_path_from_sysfs(fw_sysfs_path, sizeof(fw_sysfs_path)); class_fd = open("/sys/class/remoteproc", O_RDONLY | O_DIRECTORY); if (class_fd < 0) { warn("failed to open remoteproc class"); return -1; } class_dir = fdopendir(class_fd); if (!class_dir) { warn("failed to opendir"); close(class_fd); return -1; } while ((de = readdir(class_dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (strlen(de->d_name) + sizeof("/firmware") > sizeof(firmware_attr)) continue; strcpy(firmware_attr, de->d_name); strcat(firmware_attr, "/firmware"); firmware_fd = openat(class_fd, firmware_attr, O_RDONLY); if (firmware_fd < 0) continue; n = read(firmware_fd, firmware_value, sizeof(firmware_value)); close(firmware_fd); if (n < 0) { continue; } firmware_value[n] = '\0'; firmware_value_copy = strdup(firmware_value); firmware_path = dirname(firmware_value_copy); /* first try path from sysfs */ if ((strlen(fw_sysfs_path) > 0) && (strlen(fw_sysfs_path) + 1 + strlen(firmware_value) + 1 + strlen(file) + 1 < sizeof(path))) { strcpy(path, fw_sysfs_path); strcat(path, "/"); strcat(path, firmware_path); strcat(path, "/"); strcat(path, file); fd = open_maybe_compressed(path); if (fd >= 0) break; if (errno != ENOENT) warn("failed to open %s", path); } /* now try with base path */ if (strlen(FIRMWARE_BASE) + strlen(firmware_value) + 1 + strlen(file) + 1 > sizeof(path)) continue; strcpy(path, FIRMWARE_BASE); strcat(path, firmware_path); strcat(path, "/"); strcat(path, file); fd = open_maybe_compressed(path); if (fd >= 0) break; if (errno != ENOENT) warn("failed to open %s", path); } free(firmware_value_copy); closedir(class_dir); return fd; } /** * translate_readwrite() - open "file" from a temporary directory * @file: relative path of the requested file, with /readwrite/ stripped * @flags: flags to be passed to open(2) * * Return: opened fd on success, -1 otherwise */ static int translate_readwrite(const char *file, int flags) { int base; int ret; int fd; ret = mkdir(TQFTPSERV_TMP, 0700); if (ret < 0 && errno != EEXIST) { warn("failed to create temporary tqftpserv directory"); return -1; } base = open(TQFTPSERV_TMP, O_RDONLY | O_DIRECTORY); if (base < 0) { warn("failed top open temporary tqftpserv directory"); return -1; } fd = openat(base, file, flags, 0600); close(base); if (fd < 0) warn("failed to open %s", file); return fd; } /** * translate_open() - open file after translating path * * Strips /readonly/firmware/image and search among remoteproc firmware. * Replaces /readwrite with a temporary directory. */ int translate_open(const char *path, int flags) { if (!strncmp(path, READONLY_PATH, strlen(READONLY_PATH))) return translate_readonly(path + strlen(READONLY_PATH)); else if (!strncmp(path, READWRITE_PATH, strlen(READWRITE_PATH))) return translate_readwrite(path + strlen(READWRITE_PATH), flags); fprintf(stderr, "invalid path %s, rejecting\n", path); 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) { int fd = -1; if (access(path, F_OK) == 0) { fd = open(path, O_RDONLY); } else { char *path_with_zstd_extension = NULL; asprintf(&path_with_zstd_extension, "%s%s", path, ZSTD_EXTENSION); fd = zstd_decompress_file(path_with_zstd_extension); free(path_with_zstd_extension); } return fd; } tqftpserv-1.1/translate.h000066400000000000000000000001521464772021300155630ustar00rootroot00000000000000#ifndef __TRANSLATE_H__ #define __TRANSLATE_H__ int translate_open(const char *path, int flags); #endif tqftpserv-1.1/zstd-decompress.c000066400000000000000000000050151464772021300167120ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2024, Stefan Hansson */ /* For memfd_create */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "zstd-decompress.h" static ZSTD_DCtx *zstd_context = NULL; /** * zstd_init() - set up state for decompression. Needs to be called before zstd_decompress_file() */ void zstd_init() { zstd_context = ZSTD_createDCtx(); } /** * zstd_free() - free state used for decompression. zstd_decompress_file() may not be called after this */ void zstd_free() { ZSTD_freeDCtx(zstd_context); } /** * 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) { perror("stat failed"); return -1; } const size_t file_size = file_stat.st_size; const int input_file_fd = open(filename, 0); 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); return -1; } if (decompressed_size == ZSTD_CONTENTSIZE_ERROR) { fprintf(stderr, "Error getting content size for %s\n", filename); return -1; } void* const decompressed_buffer = malloc((size_t)decompressed_size); if (decompressed_buffer == NULL) { perror("malloc failed"); return -1; } const size_t return_size = ZSTD_decompressDCtx(zstd_context, decompressed_buffer, decompressed_size, compressed_buffer, file_size); if (ZSTD_isError(return_size)) { fprintf(stderr, "ZSTD_decompress failed: %s\n", ZSTD_getErrorName(return_size)); return -1; } const int output_file_fd = memfd_create(filename, 0); if (output_file_fd == -1) { perror("memfd_create failed"); return -1; } if (write(output_file_fd, decompressed_buffer, decompressed_size) != decompressed_size) { perror("write failed"); close(output_file_fd); return -1; } return output_file_fd; } tqftpserv-1.1/zstd-decompress.h000066400000000000000000000004061464772021300167160ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2024, Stefan Hansson */ #ifndef __ZSTD_DECOMPRESS_H__ #define __ZSTD_DECOMPRESS_H__ #include void zstd_init(); void zstd_free(); int zstd_decompress_file(const char *filename); #endif