• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libkmod - interface to kernel built-in modules
3  *
4  * Copyright (C) 2019  Alexey Gladkov <gladkov.alexey@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 
29 #include "libkmod.h"
30 #include "libkmod-internal.h"
31 
32 #define MODULES_BUILTIN_MODINFO "modules.builtin.modinfo"
33 
34 struct kmod_builtin_iter {
35 	struct kmod_ctx *ctx;
36 
37 	// The file descriptor.
38 	int file;
39 
40 	// The total size in bytes.
41 	ssize_t size;
42 
43 	// The offset of current module.
44 	off_t pos;
45 
46 	// The offset at which the next module is located.
47 	off_t next;
48 
49 	// Number of strings in the current block.
50 	ssize_t nstrings;
51 
52 	// Internal buffer and its size.
53 	size_t bufsz;
54 	char *buf;
55 };
56 
kmod_builtin_iter_new(struct kmod_ctx * ctx)57 struct kmod_builtin_iter *kmod_builtin_iter_new(struct kmod_ctx *ctx)
58 {
59 	char path[PATH_MAX];
60 	int file, sv_errno;
61 	struct stat sb;
62 	struct kmod_builtin_iter *iter = NULL;
63 	const char *dirname = kmod_get_dirname(ctx);
64 	size_t len = strlen(dirname);
65 
66 	file = -1;
67 
68 	if ((len + 1 + strlen(MODULES_BUILTIN_MODINFO) + 1) >= PATH_MAX) {
69 		sv_errno = ENAMETOOLONG;
70 		goto fail;
71 	}
72 
73 	snprintf(path, PATH_MAX, "%s/%s", dirname, MODULES_BUILTIN_MODINFO);
74 
75 	file = open(path, O_RDONLY|O_CLOEXEC);
76 	if (file < 0) {
77 		sv_errno = errno;
78 		goto fail;
79 	}
80 
81 	if (fstat(file, &sb) < 0) {
82 		sv_errno = errno;
83 		goto fail;
84 	}
85 
86 	iter = malloc(sizeof(*iter));
87 	if (!iter) {
88 		sv_errno = ENOMEM;
89 		goto fail;
90 	}
91 
92 	iter->ctx = ctx;
93 	iter->file = file;
94 	iter->size = sb.st_size;
95 	iter->nstrings = 0;
96 	iter->pos = 0;
97 	iter->next = 0;
98 	iter->bufsz = 0;
99 	iter->buf = NULL;
100 
101 	return iter;
102 fail:
103 	if (file >= 0)
104 		close(file);
105 
106 	errno = sv_errno;
107 
108 	return iter;
109 }
110 
kmod_builtin_iter_free(struct kmod_builtin_iter * iter)111 void kmod_builtin_iter_free(struct kmod_builtin_iter *iter)
112 {
113 	close(iter->file);
114 	free(iter->buf);
115 	free(iter);
116 }
117 
get_string(struct kmod_builtin_iter * iter,off_t offset,char ** line,size_t * size)118 static off_t get_string(struct kmod_builtin_iter *iter, off_t offset,
119 			char **line, size_t *size)
120 {
121 	int sv_errno;
122 	char *nullp = NULL;
123 	size_t linesz = 0;
124 
125 	while (!nullp) {
126 		char buf[BUFSIZ];
127 		ssize_t sz;
128 		size_t partsz;
129 
130 		sz = pread(iter->file, buf, BUFSIZ, offset);
131 		if (sz < 0) {
132 			sv_errno = errno;
133 			goto fail;
134 		} else if (sz == 0) {
135 			offset = 0;
136 			break;
137 		}
138 
139 		nullp = memchr(buf, '\0', (size_t) sz);
140 		partsz = (size_t)((nullp) ? (nullp - buf) + 1 : sz);
141 		offset += (off_t) partsz;
142 
143 		if (iter->bufsz < linesz + partsz) {
144 			iter->bufsz = linesz + partsz;
145 			iter->buf = realloc(iter->buf, iter->bufsz);
146 
147 			if (!iter->buf) {
148 				sv_errno = errno;
149 				goto fail;
150 			}
151 		}
152 
153 		strncpy(iter->buf + linesz, buf, partsz);
154 		linesz += partsz;
155 	}
156 
157 	if (linesz) {
158 		*line = iter->buf;
159 		*size = linesz;
160 	}
161 
162 	return offset;
163 fail:
164 	errno = sv_errno;
165 	return -1;
166 }
167 
kmod_builtin_iter_next(struct kmod_builtin_iter * iter)168 bool kmod_builtin_iter_next(struct kmod_builtin_iter *iter)
169 {
170 	char *line,  *modname;
171 	size_t linesz;
172 	off_t pos, offset, modlen;
173 
174 	modname = NULL;
175 
176 	iter->nstrings = 0;
177 	offset = pos = iter->next;
178 
179 	while (offset < iter->size) {
180 		char *dot;
181 		off_t len;
182 
183 		offset = get_string(iter, pos, &line, &linesz);
184 		if (offset <= 0) {
185 			if (offset)
186 				ERR(iter->ctx, "get_string: %s\n", strerror(errno));
187 			pos = iter->size;
188 			break;
189 		}
190 
191 		dot = strchr(line, '.');
192 		if (!dot) {
193 			ERR(iter->ctx, "kmod_builtin_iter_next: unexpected string without modname prefix\n");
194 			pos = iter->size;
195 			break;
196 		}
197 
198 		len = dot - line;
199 
200 		if (!modname) {
201 			modname = strdup(line);
202 			modlen = len;
203 		} else if (modlen != len || strncmp(modname, line, len)) {
204 			break;
205 		}
206 
207 		iter->nstrings++;
208 		pos = offset;
209 	}
210 
211 	iter->pos = iter->next;
212 	iter->next = pos;
213 
214 	free(modname);
215 
216 	return (iter->pos < iter->size);
217 }
218 
kmod_builtin_iter_get_modname(struct kmod_builtin_iter * iter,char modname[static PATH_MAX])219 bool kmod_builtin_iter_get_modname(struct kmod_builtin_iter *iter,
220 				char modname[static PATH_MAX])
221 {
222 	int sv_errno;
223 	char *line, *dot;
224 	size_t linesz, len;
225 	off_t offset;
226 
227 	if (iter->pos == iter->size)
228 		return false;
229 
230 	line = NULL;
231 
232 	offset = get_string(iter, iter->pos, &line, &linesz);
233 	if (offset <= 0) {
234 		sv_errno = errno;
235 		if (offset)
236 			ERR(iter->ctx, "get_string: %s\n", strerror(errno));
237 		goto fail;
238 	}
239 
240 	dot = strchr(line, '.');
241 	if (!dot) {
242 		sv_errno = errno;
243 		ERR(iter->ctx, "kmod_builtin_iter_get_modname: unexpected string without modname prefix\n");
244 		goto fail;
245 	}
246 
247 	len = dot - line;
248 
249 	if (len >= PATH_MAX) {
250 		sv_errno = ENAMETOOLONG;
251 		goto fail;
252 	}
253 
254 	strncpy(modname, line, len);
255 	modname[len] = '\0';
256 
257 	return true;
258 fail:
259 	errno = sv_errno;
260 	return false;
261 }
262 
263 /* array will be allocated with strings in a single malloc, just free *array */
kmod_builtin_get_modinfo(struct kmod_ctx * ctx,const char * modname,char *** modinfo)264 ssize_t kmod_builtin_get_modinfo(struct kmod_ctx *ctx, const char *modname,
265 				char ***modinfo)
266 {
267 	ssize_t count = 0;
268 	char *s, *line = NULL;
269 	size_t i, n, linesz, modlen, size;
270 	off_t pos, offset;
271 
272 	char *name = NULL;
273 	char buf[PATH_MAX];
274 
275 	struct kmod_builtin_iter *iter = kmod_builtin_iter_new(ctx);
276 
277 	if (!iter)
278 		return -errno;
279 
280 	while (!name && kmod_builtin_iter_next(iter)) {
281 		if (!kmod_builtin_iter_get_modname(iter, buf)) {
282 			count = -errno;
283 			goto fail;
284 		}
285 
286 		if (strcmp(modname, buf))
287 			continue;
288 
289 		name = buf;
290 	}
291 
292 	if (!name) {
293 		count = -ENOSYS;
294 		goto fail;
295 	}
296 
297 	modlen = strlen(modname) + 1;
298 	count = iter->nstrings;
299 	size = iter->next - iter->pos - (modlen * count);
300 
301 	*modinfo = malloc(size + sizeof(char *) * (count + 1));
302 	if (!*modinfo) {
303 		count = -errno;
304 		goto fail;
305 	}
306 
307 	s = (char *)(*modinfo + count + 1);
308 	i = 0;
309 
310 	n = 0;
311 	offset = pos = iter->pos;
312 
313 	while (offset < iter->next) {
314 		offset = get_string(iter, pos, &line, &linesz);
315 		if (offset <= 0) {
316 			count = (offset) ? -errno : -EINVAL;
317 			free(*modinfo);
318 			goto fail;
319 		}
320 
321 		strcpy(s + i, line + modlen);
322 		(*modinfo)[n++] = s + i;
323 		i += linesz - modlen;
324 
325 		pos = offset;
326 	}
327 fail:
328 	kmod_builtin_iter_free(iter);
329 	return count;
330 }
331