1
2
3#include "fs.h"
4#include "util.h"
5
6#include <dirent.h>
7#include <errno.h>
8#include <fcntl.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <sys/utsname.h>
15#include <unistd.h>
16
17struct ModuleNode {
18 char *name;
19 char *path;
20 char **deps;
21 size_t ndeps;
22#if FEATURE_DEPMOD_ALIAS
23 char **aliases;
24 size_t naliases;
25#endif
26#if FEATURE_DEPMOD_SYMBOLS
27 char **symbols;
28 size_t nsymbols;
29#endif
30 struct ModuleNode *next;
31};
32
33static struct ModuleNode *modules_head = NULL;
34
35static void
36normalize_name(char *dst, const char *src)
37{
38 const char *base;
39 int i;
40
41 base = strrchr(src, '/');
42 if (base)
43 base++;
44 else
45 base = src;
46
47 for (i = 0; i < 255 && base[i] && base[i] != '.'; i++)
48 dst[i] = (base[i] == '-') ? '_' : base[i];
49 dst[i] = '\0';
50}
51
52static struct ModuleNode *
53find_module(const char *name)
54{
55 struct ModuleNode *m;
56
57 for (m = modules_head; m; m = m->next) {
58 if (strcmp(m->name, name) == 0)
59 return m;
60 }
61 return NULL;
62}
63
64static char *
65get_str(const char *buf, size_t len, size_t offset, size_t maxlen)
66{
67 size_t i;
68
69 for (i = 0; i < maxlen && offset + i < len; i++) {
70 if (buf[offset + i] == '\0') {
71 if (i == 0)
72 return NULL;
73 return estrndup(buf + offset, i);
74 }
75 if (buf[offset + i] < 32 || buf[offset + i] > 126)
76 return NULL;
77 }
78 return NULL;
79}
80
81static void
82parse_ko(struct ModuleNode *m, const char *buf, size_t len)
83{
84 size_t i;
85 char *val, *tok, *save;
86
87 for (i = 0; i + 8 < len; i++) {
88 if (memcmp(buf + i, "depends=", 8) == 0) {
89 val = get_str(buf, len, i + 8, 1024);
90 if (val) {
91 save = val;
92 while ((tok = strsep(&save, ","))) {
93 if (*tok) {
94 m->deps = reallocarray(m->deps, m->ndeps + 1, sizeof(char *));
95 m->deps[m->ndeps] = estrdup(tok);
96 normalize_name(m->deps[m->ndeps], tok);
97 m->ndeps++;
98 }
99 }
100 i += 8 + strlen(val);
101 free(val);
102 }
103 }
104#if FEATURE_DEPMOD_ALIAS
105 else if (memcmp(buf + i, "alias=", 6) == 0) {
106 val = get_str(buf, len, i + 6, 512);
107 if (val) {
108 m->aliases = reallocarray(m->aliases, m->naliases + 1, sizeof(char *));
109 m->aliases[m->naliases] = val;
110 i += 6 + strlen(val);
111 m->naliases++;
112 }
113 }
114#endif
115#if FEATURE_DEPMOD_SYMBOLS
116 else if (memcmp(buf + i, "__ksymtab_", 10) == 0) {
117 val = get_str(buf, len, i + 10, 256);
118 if (val) {
119 m->symbols = reallocarray(m->symbols, m->nsymbols + 1, sizeof(char *));
120 m->symbols[m->nsymbols] = val;
121 i += 10 + strlen(val);
122 m->nsymbols++;
123 }
124 }
125#endif
126 }
127}
128
129static char *
130read_all(FILE *fp, size_t *out_len)
131{
132 char *buf = NULL;
133 size_t cap = 0;
134 size_t len = 0;
135 size_t n;
136
137 while (1) {
138 if (len >= cap) {
139 cap = cap ? cap * 2 : 65536;
140 buf = erealloc(buf, cap);
141 }
142 n = fread(buf + len, 1, cap - len, fp);
143 if (n == 0)
144 break;
145 len += n;
146 }
147 *out_len = len;
148 return buf;
149}
150
151static void
152scan_cb(int fd, const char *name, struct stat *st, void *data, struct recursor *r)
153{
154 struct ModuleNode *m;
155 char *buf;
156 size_t len;
157 const char *ext;
158 const char *comp;
159 FILE *fp = NULL;
160 int is_pipe = 0;
161 char cmd[PATH_MAX + 32];
162
163 (void)fd;
164 (void)data;
165 (void)r;
166
167 if (S_ISDIR(st->st_mode)) {
168 recurse(fd, name, NULL, r);
169 return;
170 }
171
172 if (!S_ISREG(st->st_mode))
173 return;
174
175 ext = strstr(r->path, ".ko");
176 if (!ext)
177 return;
178
179 comp = ext + 3;
180 if (strcmp(comp, "") == 0) {
181 fp = fopen(r->path, "r");
182 } else if (strcmp(comp, ".gz") == 0) {
183 snprintf(cmd, sizeof(cmd), "gzip -dc '%s'", r->path);
184 fp = popen(cmd, "r");
185 is_pipe = 1;
186 } else if (strcmp(comp, ".xz") == 0) {
187 snprintf(cmd, sizeof(cmd), "xz -dc '%s'", r->path);
188 fp = popen(cmd, "r");
189 is_pipe = 1;
190 } else if (strcmp(comp, ".zst") == 0) {
191 snprintf(cmd, sizeof(cmd), "zstd -dc '%s'", r->path);
192 fp = popen(cmd, "r");
193 is_pipe = 1;
194 } else {
195 fp = fopen(r->path, "r");
196 }
197
198 if (!fp) {
199 weprintf("open %s:", r->path);
200 return;
201 }
202
203 buf = read_all(fp, &len);
204 if (is_pipe)
205 pclose(fp);
206 else
207 fclose(fp);
208
209 if (len == 0) {
210 free(buf);
211 return;
212 }
213
214 m = ecalloc(1, sizeof(*m));
215 m->path = estrdup(r->path);
216 if (strncmp(m->path, "./", 2) == 0)
217 memmove(m->path, m->path + 2, strlen(m->path) - 1);
218 m->name = emalloc(256);
219 normalize_name(m->name, r->path);
220
221 parse_ko(m, buf, len);
222 free(buf);
223
224 m->next = modules_head;
225 modules_head = m;
226}
227
228static void
229resolve_deps(struct ModuleNode *m, char ***out_list, size_t *out_count)
230{
231 size_t i, j;
232 struct ModuleNode *dep_node;
233 int already_exists;
234
235 for (i = 0; i < m->ndeps; i++) {
236 dep_node = find_module(m->deps[i]);
237 if (!dep_node)
238 continue;
239
240 already_exists = 0;
241 for (j = 0; j < *out_count; j++) {
242 if (strcmp((*out_list)[j], dep_node->path) == 0) {
243 already_exists = 1;
244 break;
245 }
246 }
247
248 if (!already_exists) {
249 resolve_deps(dep_node, out_list, out_count);
250 *out_list = reallocarray(*out_list, *out_count + 1, sizeof(char *));
251 (*out_list)[*out_count] = estrdup(dep_node->path);
252 (*out_count)++;
253 }
254 }
255}
256
257static void
258usage(void)
259{
260 eprintf("usage: %s [-n] [-b basedir] [version]\n", argv0);
261}
262
263// ?man depmod: generate modules.dep and map files
264// ?man arguments: version
265// ?man depmod generates modules.dep containing dependency information for modprobe
266// ?man // ?man -n: dry run print results to stdout instead of writing files
267// ?man // ?man -b basedir: use basedir as prefix for module directories
268int
269main(int argc, char *argv[])
270{
271 struct utsname uts;
272 struct recursor r = { .fn = scan_cb, .maxdepth = 0, .follow = 'H', .flags = DIRFIRST };
273 struct ModuleNode *m;
274 char *basedir = "/";
275 char *version = NULL;
276 char path[PATH_MAX];
277 int nflag = 0;
278 size_t i;
279 char **resolved;
280 size_t nresolved;
281 FILE *f_dep, *f_alias, *f_sym;
282
283 ARGBEGIN {
284 // ?man -n: specify n option
285 case 'n':
286 nflag = 1;
287 break;
288 // ?man -b:dir: specify b option
289 case 'b':
290 basedir = EARGF(usage());
291 break;
292 default:
293 usage();
294 } ARGEND;
295
296 if (argc > 1)
297 usage();
298
299 if (argc == 1) {
300 version = argv[0];
301 } else {
302 if (uname(&uts) < 0)
303 eprintf("uname:");
304 version = uts.release;
305 }
306
307 snprintf(path, sizeof(path), "%s/lib/modules/%s", basedir, version);
308 if (chdir(path) < 0)
309 eprintf("chdir %s:", path);
310
311 recurse(AT_FDCWD, ".", NULL, &r);
312
313 f_dep = stdout;
314 f_alias = stdout;
315 f_sym = stdout;
316
317 if (!nflag) {
318 f_dep = fopen("modules.dep", "w");
319 if (!f_dep)
320 eprintf("fopen modules.dep:");
321#if FEATURE_DEPMOD_ALIAS
322 f_alias = fopen("modules.alias", "w");
323 if (!f_alias)
324 eprintf("fopen modules.alias:");
325#endif
326#if FEATURE_DEPMOD_SYMBOLS
327 f_sym = fopen("modules.symbols", "w");
328 if (!f_sym)
329 eprintf("fopen modules.symbols:");
330#endif
331 }
332
333 /* write modules.dep */
334 for (m = modules_head; m; m = m->next) {
335 resolved = NULL;
336 nresolved = 0;
337 resolve_deps(m, &resolved, &nresolved);
338
339 fprintf(f_dep, "%s:", m->path);
340 for (i = 0; i < nresolved; i++) {
341 fprintf(f_dep, " %s", resolved[i]);
342 free(resolved[i]);
343 }
344 free(resolved);
345 fprintf(f_dep, "\n");
346 }
347
348#if FEATURE_DEPMOD_ALIAS
349 /* write modules.alias */
350 for (m = modules_head; m; m = m->next) {
351 for (i = 0; i < m->naliases; i++)
352 fprintf(f_alias, "alias %s %s\n", m->aliases[i], m->name);
353 }
354#endif
355
356#if FEATURE_DEPMOD_SYMBOLS
357 /* write modules.symbols */
358 for (m = modules_head; m; m = m->next) {
359 for (i = 0; i < m->nsymbols; i++)
360 fprintf(f_sym, "alias symbol:%s %s\n", m->symbols[i], m->name);
361 }
362#endif
363
364 if (!nflag) {
365 fclose(f_dep);
366#if FEATURE_DEPMOD_ALIAS
367 fclose(f_alias);
368#endif
369#if FEATURE_DEPMOD_SYMBOLS
370 fclose(f_sym);
371#endif
372 }
373
374 /* free memory */
375 while (modules_head) {
376 m = modules_head;
377 modules_head = m->next;
378 free(m->name);
379 free(m->path);
380 for (i = 0; i < m->ndeps; i++)
381 free(m->deps[i]);
382 free(m->deps);
383#if FEATURE_DEPMOD_ALIAS
384 for (i = 0; i < m->naliases; i++)
385 free(m->aliases[i]);
386 free(m->aliases);
387#endif
388#if FEATURE_DEPMOD_SYMBOLS
389 for (i = 0; i < m->nsymbols; i++)
390 free(m->symbols[i]);
391 free(m->symbols);
392#endif
393 free(m);
394 }
395
396 if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
397 return 2;
398
399 return 0;
400}