| /* |
| * Copyright (c) 2019, 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. |
| */ |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <err.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <libgen.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "translate.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 |
| |
| /** |
| * 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_attr[32]; |
| char path[PATH_MAX]; |
| struct dirent *de; |
| int firmware_fd; |
| DIR *class_dir; |
| int class_fd; |
| ssize_t n; |
| int fd = -1; |
| |
| 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"); |
| goto close_class; |
| } |
| |
| 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; |
| } |
| |
| if (strlen(FIRMWARE_BASE) + strlen(firmware_value) + 1 + |
| strlen(file) + 1 > sizeof(path)) |
| continue; |
| |
| strcpy(path, FIRMWARE_BASE); |
| strcat(path, dirname(firmware_value)); |
| strcat(path, "/"); |
| strcat(path, file); |
| |
| fd = open(path, O_RDONLY); |
| if (fd >= 0) |
| break; |
| |
| if (errno != ENOENT) |
| warn("failed to open %s", path); |
| } |
| |
| closedir(class_dir); |
| |
| close_class: |
| close(class_fd); |
| |
| 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; |
| } |