1 /*
2  * Copyright (c) 2019, Linaro Ltd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <dirent.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <libgen.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "translate.h"
44 
45 #define READONLY_PATH	"/readonly/firmware/image/"
46 #define READWRITE_PATH	"/readwrite/"
47 
48 #ifndef ANDROID
49 #define FIRMWARE_BASE	"/lib/firmware/"
50 #define TQFTPSERV_TMP	"/tmp/tqftpserv"
51 #else
52 #define FIRMWARE_BASE	"/vendor/firmware/"
53 #define TQFTPSERV_TMP	"/data/vendor/tmp/tqftpserv"
54 #endif
55 
56 /**
57  * translate_readonly() - open "file" residing with remoteproc firmware
58  * @file:	file requested, stripped of "/readonly/image/" prefix
59  *
60  * It is assumed that the readonly files requested by the client resides under
61  * /lib/firmware in the same place as its associated remoteproc firmware.  This
62  * function scans through all entries under /sys/class/remoteproc and read the
63  * dirname of each "firmware" file in an attempt to find, and open(2), the
64  * requested file.
65  *
66  * As these files are readonly, it's not possible to pass flags to open(2).
67  *
68  * Return: opened fd on success, -1 otherwise
69  */
translate_readonly(const char * file)70 static int translate_readonly(const char *file)
71 {
72 	char firmware_value[PATH_MAX];
73 	char firmware_attr[32];
74 	char path[PATH_MAX];
75 	struct dirent *de;
76 	int firmware_fd;
77 	DIR *class_dir;
78 	int class_fd;
79 	ssize_t n;
80 	int fd = -1;
81 
82 	class_fd = open("/sys/class/remoteproc", O_RDONLY | O_DIRECTORY);
83 	if (class_fd < 0) {
84 		warn("failed to open remoteproc class");
85 		return -1;
86 	}
87 
88 	class_dir = fdopendir(class_fd);
89 	if (!class_dir) {
90 		warn("failed to opendir");
91 		goto close_class;
92 	}
93 
94 	while ((de = readdir(class_dir)) != NULL) {
95 		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
96 			continue;
97 
98 		if (strlen(de->d_name) + sizeof("/firmware") > sizeof(firmware_attr))
99 			continue;
100 		strcpy(firmware_attr, de->d_name);
101 		strcat(firmware_attr, "/firmware");
102 
103 		firmware_fd = openat(class_fd, firmware_attr, O_RDONLY);
104 		if (firmware_fd < 0)
105 			continue;
106 
107 		n = read(firmware_fd, firmware_value, sizeof(firmware_value));
108 		close(firmware_fd);
109 		if (n < 0) {
110 			continue;
111 		}
112 		firmware_value[n] = '\0';
113 
114 		if (strlen(FIRMWARE_BASE) + strlen(firmware_value) + 1 +
115 		    strlen(file) + 1 > sizeof(path))
116 			continue;
117 
118 		strcpy(path, FIRMWARE_BASE);
119 		strcat(path, dirname(firmware_value));
120 		strcat(path, "/");
121 		strcat(path, file);
122 
123 		fd = open(path, O_RDONLY);
124 		if (fd >= 0)
125 			break;
126 
127 		if (errno != ENOENT)
128 			warn("failed to open %s", path);
129 	}
130 
131 	closedir(class_dir);
132 
133 close_class:
134 	close(class_fd);
135 
136 	return fd;
137 }
138 
139 /**
140  * translate_readwrite() - open "file" from a temporary directory
141  * @file:	relative path of the requested file, with /readwrite/ stripped
142  * @flags:	flags to be passed to open(2)
143  *
144  * Return: opened fd on success, -1 otherwise
145  */
translate_readwrite(const char * file,int flags)146 static int translate_readwrite(const char *file, int flags)
147 {
148 	int base;
149 	int ret;
150 	int fd;
151 
152 	ret = mkdir(TQFTPSERV_TMP, 0700);
153 	if (ret < 0 && errno != EEXIST) {
154 		warn("failed to create temporary tqftpserv directory");
155 		return -1;
156 	}
157 
158 	base = open(TQFTPSERV_TMP, O_RDONLY | O_DIRECTORY);
159 	if (base < 0) {
160 		warn("failed top open temporary tqftpserv directory");
161 		return -1;
162 	}
163 
164 	fd = openat(base, file, flags, 0600);
165 	close(base);
166 	if (fd < 0)
167 		warn("failed to open %s", file);
168 
169 	return fd;
170 }
171 
172 /**
173  * translate_open() - open file after translating path
174  *
175 
176  * Strips /readonly/firmware/image and search among remoteproc firmware.
177  * Replaces /readwrite with a temporary directory.
178 
179  */
translate_open(const char * path,int flags)180 int translate_open(const char *path, int flags)
181 {
182 	if (!strncmp(path, READONLY_PATH, strlen(READONLY_PATH)))
183 		return translate_readonly(path + strlen(READONLY_PATH));
184 	else if (!strncmp(path, READWRITE_PATH, strlen(READWRITE_PATH)))
185 		return translate_readwrite(path + strlen(READWRITE_PATH), flags);
186 
187 	fprintf(stderr, "invalid path %s, rejecting\n", path);
188 	errno = ENOENT;
189 	return -1;
190 }
191