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