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