]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2f3dfc6f | 2 | |
dccca82b | 3 | #include <errno.h> |
2f3dfc6f LP |
4 | #include <stdbool.h> |
5 | #include <stdlib.h> | |
6 | #include <sys/stat.h> | |
7 | #include <unistd.h> | |
8 | ||
9 | #include "alloc-util.h" | |
10 | #include "fd-util.h" | |
11 | #include "fileio.h" | |
12 | #include "fstab-util.h" | |
fb883e75 | 13 | #include "generator.h" |
2f3dfc6f LP |
14 | #include "hexdecoct.h" |
15 | #include "id128-util.h" | |
d850ad9a | 16 | #include "main-func.h" |
2f3dfc6f LP |
17 | #include "mkdir.h" |
18 | #include "parse-util.h" | |
2d9b74ba | 19 | #include "path-util.h" |
2f3dfc6f | 20 | #include "proc-cmdline.h" |
98bad05e | 21 | #include "specifier.h" |
2f3dfc6f LP |
22 | #include "string-util.h" |
23 | #include "unit-name.h" | |
24 | ||
78b408d2 MB |
25 | #define SYSTEMD_VERITYSETUP_SERVICE_ROOT "systemd-veritysetup@root.service" |
26 | #define SYSTEMD_VERITYSETUP_SERVICE_USR "systemd-veritysetup@usr.service" | |
fb883e75 | 27 | |
2cb52121 | 28 | static const char *arg_dest = NULL; |
2f3dfc6f | 29 | static bool arg_enabled = true; |
08b04ec7 GP |
30 | static bool arg_read_veritytab = true; |
31 | static const char *arg_veritytab = NULL; | |
2f3dfc6f | 32 | static char *arg_root_hash = NULL; |
78b408d2 MB |
33 | static char *arg_root_data_what = NULL; |
34 | static char *arg_root_hash_what = NULL; | |
35 | static char *arg_root_options = NULL; | |
36 | static char *arg_usr_hash = NULL; | |
37 | static char *arg_usr_data_what = NULL; | |
38 | static char *arg_usr_hash_what = NULL; | |
39 | static char *arg_usr_options = NULL; | |
2f3dfc6f | 40 | |
d850ad9a | 41 | STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); |
78b408d2 MB |
42 | STATIC_DESTRUCTOR_REGISTER(arg_root_data_what, freep); |
43 | STATIC_DESTRUCTOR_REGISTER(arg_root_hash_what, freep); | |
44 | STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep); | |
45 | STATIC_DESTRUCTOR_REGISTER(arg_usr_hash, freep); | |
46 | STATIC_DESTRUCTOR_REGISTER(arg_usr_data_what, freep); | |
47 | STATIC_DESTRUCTOR_REGISTER(arg_usr_hash_what, freep); | |
48 | STATIC_DESTRUCTOR_REGISTER(arg_usr_options, freep); | |
49 | ||
deb60ef9 | 50 | static int create_special_device( |
78b408d2 MB |
51 | const char *name, |
52 | const char *service, | |
953006dc | 53 | const char *roothash, |
78b408d2 MB |
54 | const char *data_what, |
55 | const char *hash_what, | |
56 | const char *options) { | |
d850ad9a | 57 | |
953006dc | 58 | _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL; |
2f3dfc6f | 59 | _cleanup_fclose_ FILE *f = NULL; |
2f3dfc6f LP |
60 | int r; |
61 | ||
deb60ef9 LP |
62 | /* Creates a systemd-veritysetup@.service instance for the special kernel cmdline specified root + usr devices. */ |
63 | ||
78b408d2 MB |
64 | assert(name); |
65 | assert(service); | |
66 | ||
2f3dfc6f | 67 | /* If all three pieces of information are missing, then verity is turned off */ |
953006dc | 68 | if (!roothash && !data_what && !hash_what) |
2f3dfc6f LP |
69 | return 0; |
70 | ||
71 | /* if one of them is missing however, the data is simply incomplete and this is an error */ | |
953006dc LP |
72 | if (!roothash) |
73 | log_error("Verity information for %s incomplete, root hash unspecified.", name); | |
78b408d2 MB |
74 | if (!data_what) |
75 | log_error("Verity information for %s incomplete, data device unspecified.", name); | |
76 | if (!hash_what) | |
77 | log_error("Verity information for %s incomplete, hash device unspecified.", name); | |
78 | ||
953006dc | 79 | if (!roothash || !data_what || !hash_what) |
2f3dfc6f LP |
80 | return -EINVAL; |
81 | ||
953006dc | 82 | log_debug("Using %s verity data device %s, hash device %s, options %s, and hash %s.", name, data_what, hash_what, options, roothash); |
2f3dfc6f | 83 | |
78b408d2 | 84 | u = fstab_node_to_udev_node(data_what); |
2f3dfc6f LP |
85 | if (!u) |
86 | return log_oom(); | |
78b408d2 | 87 | v = fstab_node_to_udev_node(hash_what); |
2f3dfc6f LP |
88 | if (!v) |
89 | return log_oom(); | |
90 | ||
91 | r = unit_name_from_path(u, ".device", &d); | |
92 | if (r < 0) | |
1a012455 | 93 | return log_error_errno(r, "Failed to generate unit name: %m"); |
2f3dfc6f LP |
94 | r = unit_name_from_path(v, ".device", &e); |
95 | if (r < 0) | |
1a012455 | 96 | return log_error_errno(r, "Failed to generate unit name: %m"); |
2f3dfc6f | 97 | |
78b408d2 | 98 | r = generator_open_unit_file(arg_dest, NULL, service, &f); |
fb883e75 ZJS |
99 | if (r < 0) |
100 | return r; | |
2f3dfc6f | 101 | |
953006dc LP |
102 | r = generator_write_veritysetup_unit_section(f, "/proc/cmdline"); |
103 | if (r < 0) | |
104 | return r; | |
105 | ||
2f3dfc6f | 106 | fprintf(f, |
bf1484c7 | 107 | "Before=veritysetup.target\n" |
2f3dfc6f | 108 | "BindsTo=%s %s\n" |
bf1484c7 | 109 | "After=%s %s\n", |
2f3dfc6f | 110 | d, e, |
953006dc LP |
111 | d, e); |
112 | ||
113 | r = generator_write_veritysetup_service_section(f, name, u, v, roothash, options); | |
114 | if (r < 0) | |
115 | return r; | |
2f3dfc6f LP |
116 | |
117 | r = fflush_and_check(f); | |
118 | if (r < 0) | |
78b408d2 | 119 | return log_error_errno(r, "Failed to write file unit %s: %m", service); |
2f3dfc6f | 120 | |
953006dc LP |
121 | r = generator_add_symlink(arg_dest, "veritysetup.target", "requires", service); |
122 | if (r < 0) | |
123 | return r; | |
2f3dfc6f LP |
124 | |
125 | return 0; | |
126 | } | |
127 | ||
78b408d2 | 128 | static int create_root_device(void) { |
deb60ef9 | 129 | return create_special_device("root", SYSTEMD_VERITYSETUP_SERVICE_ROOT, arg_root_hash, arg_root_data_what, arg_root_hash_what, arg_root_options); |
78b408d2 MB |
130 | } |
131 | ||
132 | static int create_usr_device(void) { | |
deb60ef9 | 133 | return create_special_device("usr", SYSTEMD_VERITYSETUP_SERVICE_USR, arg_usr_hash, arg_usr_data_what, arg_usr_hash_what, arg_usr_options); |
78b408d2 MB |
134 | } |
135 | ||
2f3dfc6f LP |
136 | static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { |
137 | int r; | |
138 | ||
a8d3315b YW |
139 | assert(key); |
140 | ||
141 | if (streq(key, "systemd.verity")) { | |
2f3dfc6f LP |
142 | |
143 | r = value ? parse_boolean(value) : 1; | |
144 | if (r < 0) | |
145 | log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value); | |
146 | else | |
147 | arg_enabled = r; | |
148 | ||
08b04ec7 GP |
149 | } else if (streq(key, "veritytab")) { |
150 | ||
151 | r = value ? parse_boolean(value) : 1; | |
152 | if (r < 0) | |
153 | log_warning("Failed to parse veritytab= kernel command line switch %s. Ignoring.", value); | |
154 | else | |
155 | arg_read_veritytab = r; | |
156 | ||
a8d3315b | 157 | } else if (streq(key, "roothash")) { |
2f3dfc6f LP |
158 | |
159 | if (proc_cmdline_value_missing(key, value)) | |
160 | return 0; | |
161 | ||
162 | r = free_and_strdup(&arg_root_hash, value); | |
163 | if (r < 0) | |
164 | return log_oom(); | |
165 | ||
ad0cdb6b | 166 | } else if (proc_cmdline_key_streq(key, "systemd.verity_root_data")) { |
2f3dfc6f LP |
167 | |
168 | if (proc_cmdline_value_missing(key, value)) | |
169 | return 0; | |
170 | ||
78b408d2 | 171 | r = free_and_strdup(&arg_root_data_what, value); |
2f3dfc6f LP |
172 | if (r < 0) |
173 | return log_oom(); | |
174 | ||
ad0cdb6b | 175 | } else if (proc_cmdline_key_streq(key, "systemd.verity_root_hash")) { |
2f3dfc6f LP |
176 | |
177 | if (proc_cmdline_value_missing(key, value)) | |
178 | return 0; | |
179 | ||
78b408d2 | 180 | r = free_and_strdup(&arg_root_hash_what, value); |
2f3dfc6f LP |
181 | if (r < 0) |
182 | return log_oom(); | |
0141102f GP |
183 | |
184 | } else if (proc_cmdline_key_streq(key, "systemd.verity_root_options")) { | |
185 | ||
186 | if (proc_cmdline_value_missing(key, value)) | |
187 | return 0; | |
188 | ||
78b408d2 MB |
189 | r = free_and_strdup(&arg_root_options, value); |
190 | if (r < 0) | |
191 | return log_oom(); | |
192 | ||
a8d3315b | 193 | } else if (streq(key, "usrhash")) { |
93e0d320 | 194 | |
78b408d2 MB |
195 | if (proc_cmdline_value_missing(key, value)) |
196 | return 0; | |
197 | ||
198 | r = free_and_strdup(&arg_usr_hash, value); | |
199 | if (r < 0) | |
200 | return log_oom(); | |
201 | ||
202 | } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_data")) { | |
203 | ||
204 | if (proc_cmdline_value_missing(key, value)) | |
205 | return 0; | |
206 | ||
207 | r = free_and_strdup(&arg_usr_data_what, value); | |
208 | if (r < 0) | |
209 | return log_oom(); | |
210 | ||
211 | } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_hash")) { | |
212 | ||
213 | if (proc_cmdline_value_missing(key, value)) | |
214 | return 0; | |
215 | ||
216 | r = free_and_strdup(&arg_usr_hash_what, value); | |
217 | if (r < 0) | |
218 | return log_oom(); | |
219 | ||
220 | } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_options")) { | |
221 | ||
222 | if (proc_cmdline_value_missing(key, value)) | |
223 | return 0; | |
224 | ||
225 | r = free_and_strdup(&arg_usr_options, value); | |
0141102f GP |
226 | if (r < 0) |
227 | return log_oom(); | |
228 | ||
2f3dfc6f LP |
229 | } |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
78b408d2 MB |
234 | static int determine_device( |
235 | const char *name, | |
236 | const char *hash, | |
237 | char **data_what, | |
238 | char **hash_what) { | |
239 | ||
240 | sd_id128_t data_uuid, verity_uuid; | |
2f3dfc6f | 241 | _cleanup_free_ void *m = NULL; |
2f3dfc6f LP |
242 | size_t l; |
243 | int r; | |
244 | ||
78b408d2 MB |
245 | assert(name); |
246 | assert(data_what); | |
247 | assert(hash_what); | |
2f3dfc6f | 248 | |
78b408d2 | 249 | if (!hash) |
2f3dfc6f LP |
250 | return 0; |
251 | ||
78b408d2 | 252 | if (*data_what && *hash_what) |
2f3dfc6f LP |
253 | return 0; |
254 | ||
bdd2036e | 255 | r = unhexmem(hash, &m, &l); |
2f3dfc6f | 256 | if (r < 0) |
78b408d2 | 257 | return log_error_errno(r, "Failed to parse hash: %s", hash); |
2f3dfc6f | 258 | if (l < sizeof(sd_id128_t)) { |
78b408d2 | 259 | log_debug("Root hash for %s is shorter than 128 bits (32 characters), ignoring for discovering verity partition.", name); |
2f3dfc6f LP |
260 | return 0; |
261 | } | |
262 | ||
78b408d2 MB |
263 | if (!*data_what) { |
264 | memcpy(&data_uuid, m, sizeof(data_uuid)); | |
2f3dfc6f | 265 | |
b7416360 | 266 | *data_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(data_uuid)); |
78b408d2 | 267 | if (!*data_what) |
2f3dfc6f LP |
268 | return log_oom(); |
269 | } | |
270 | ||
78b408d2 | 271 | if (!*hash_what) { |
2f3dfc6f LP |
272 | memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid)); |
273 | ||
b7416360 | 274 | *hash_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(verity_uuid)); |
78b408d2 | 275 | if (!*hash_what) |
2f3dfc6f LP |
276 | return log_oom(); |
277 | } | |
278 | ||
78b408d2 MB |
279 | log_info("Using data device %s and hash device %s for %s.", *data_what, *hash_what, name); |
280 | ||
2f3dfc6f LP |
281 | return 1; |
282 | } | |
283 | ||
78b408d2 MB |
284 | static int determine_devices(void) { |
285 | int r; | |
286 | ||
287 | r = determine_device("root", arg_root_hash, &arg_root_data_what, &arg_root_hash_what); | |
288 | if (r < 0) | |
289 | return r; | |
290 | ||
291 | return determine_device("usr", arg_usr_hash, &arg_usr_data_what, &arg_usr_hash_what); | |
292 | } | |
293 | ||
bf1484c7 LP |
294 | static bool attach_in_initrd(const char *name, const char *options) { |
295 | assert(name); | |
296 | ||
297 | /* Imply x-initrd.attach in case the volume name is among those defined in the Discoverable Partition | |
298 | * Specification for partitions that we require to be mounted during the initrd → host transition, | |
299 | * i.e. for the root fs itself, and /usr/. This mirrors similar behaviour in | |
300 | * systemd-fstab-generator. */ | |
301 | ||
302 | return fstab_test_option(options, "x-initrd.attach\0") || | |
303 | STR_IN_SET(name, "root", "usr"); | |
304 | } | |
305 | ||
deb60ef9 | 306 | static int create_veritytab_device( |
08b04ec7 GP |
307 | const char *name, |
308 | const char *data_device, | |
309 | const char *hash_device, | |
310 | const char *roothash, | |
311 | const char *options, | |
312 | const char *source) { | |
313 | ||
314 | _cleanup_free_ char *n = NULL, *dd = NULL, *du = NULL, *hd = NULL, *hu = NULL, *e = NULL, | |
315 | *du_escaped = NULL, *hu_escaped = NULL, *name_escaped = NULL; | |
316 | _cleanup_fclose_ FILE *f = NULL; | |
317 | const char *dmname; | |
5dd02147 | 318 | bool noauto, nofail, netdev, need_loop = false; |
08b04ec7 GP |
319 | int r; |
320 | ||
deb60ef9 LP |
321 | /* Creates a systemd-veritysetup@.service instance for volumes specified in /etc/veritytab. */ |
322 | ||
08b04ec7 GP |
323 | assert(name); |
324 | assert(data_device); | |
325 | assert(hash_device); | |
326 | assert(roothash); | |
327 | ||
328 | noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); | |
329 | nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); | |
330 | netdev = fstab_test_option(options, "_netdev\0"); | |
08b04ec7 GP |
331 | |
332 | name_escaped = specifier_escape(name); | |
333 | if (!name_escaped) | |
334 | return log_oom(); | |
335 | ||
336 | e = unit_name_escape(name); | |
337 | if (!e) | |
338 | return log_oom(); | |
339 | ||
340 | du = fstab_node_to_udev_node(data_device); | |
341 | if (!du) | |
342 | return log_oom(); | |
343 | ||
344 | hu = fstab_node_to_udev_node(hash_device); | |
345 | if (!hu) | |
346 | return log_oom(); | |
347 | ||
348 | r = unit_name_build("systemd-veritysetup", e, ".service", &n); | |
349 | if (r < 0) | |
350 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
351 | ||
352 | du_escaped = specifier_escape(du); | |
353 | if (!du_escaped) | |
354 | return log_oom(); | |
355 | ||
356 | hu_escaped = specifier_escape(hu); | |
357 | if (!hu_escaped) | |
358 | return log_oom(); | |
359 | ||
360 | r = unit_name_from_path(du, ".device", &dd); | |
361 | if (r < 0) | |
362 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
363 | ||
364 | r = unit_name_from_path(hu, ".device", &hd); | |
365 | if (r < 0) | |
366 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
367 | ||
368 | r = generator_open_unit_file(arg_dest, NULL, n, &f); | |
369 | if (r < 0) | |
370 | return r; | |
371 | ||
372 | r = generator_write_veritysetup_unit_section(f, source); | |
373 | if (r < 0) | |
374 | return r; | |
375 | ||
376 | if (netdev) | |
377 | fprintf(f, "After=remote-fs-pre.target\n"); | |
378 | ||
379 | /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */ | |
bf1484c7 LP |
380 | if (!attach_in_initrd(name, options)) |
381 | fprintf(f, | |
382 | "Conflicts=umount.target\n" | |
383 | "Before=umount.target\n"); | |
08b04ec7 GP |
384 | |
385 | if (!nofail) | |
386 | fprintf(f, | |
387 | "Before=%s\n", | |
388 | netdev ? "remote-veritysetup.target" : "veritysetup.target"); | |
389 | ||
390 | if (path_startswith(du, "/dev/")) | |
391 | fprintf(f, | |
392 | "BindsTo=%s\n" | |
bf1484c7 | 393 | "After=%s\n", |
08b04ec7 | 394 | dd, dd); |
5dd02147 LP |
395 | else { |
396 | fprintf(f, "RequiresMountsFor=%s\n", du_escaped); | |
397 | need_loop = true; | |
398 | } | |
08b04ec7 GP |
399 | |
400 | if (path_startswith(hu, "/dev/")) | |
401 | fprintf(f, | |
402 | "BindsTo=%s\n" | |
bf1484c7 | 403 | "After=%s\n", |
08b04ec7 | 404 | hd, hd); |
5dd02147 LP |
405 | else { |
406 | fprintf(f, "RequiresMountsFor=%s\n", hu_escaped); | |
407 | need_loop = true; | |
408 | } | |
409 | ||
410 | if (need_loop) | |
ce49a479 LP |
411 | /* For loopback devices make sure to explicitly load loop.ko, as this code might run very |
412 | * early where device nodes created via systemd-tmpfiles-setup-dev.service might not be | |
413 | * around yet. Hence let's sync on the module itself. */ | |
08b04ec7 | 414 | fprintf(f, |
ce49a479 LP |
415 | "Wants=modprobe@loop.service\n" |
416 | "After=modprobe@loop.service\n"); | |
08b04ec7 | 417 | |
45e3406e | 418 | r = generator_write_veritysetup_service_section(f, name, du, hu, roothash, options); |
08b04ec7 GP |
419 | if (r < 0) |
420 | return r; | |
421 | ||
422 | r = fflush_and_check(f); | |
423 | if (r < 0) | |
424 | return log_error_errno(r, "Failed to write unit file %s: %m", n); | |
425 | ||
426 | if (!noauto) { | |
427 | r = generator_add_symlink(arg_dest, | |
428 | netdev ? "remote-veritysetup.target" : "veritysetup.target", | |
429 | nofail ? "wants" : "requires", n); | |
430 | if (r < 0) | |
431 | return r; | |
432 | } | |
433 | ||
434 | dmname = strjoina("dev-mapper-", e, ".device"); | |
435 | return generator_add_symlink(arg_dest, dmname, "requires", n); | |
436 | } | |
437 | ||
438 | static int add_veritytab_devices(void) { | |
439 | _cleanup_fclose_ FILE *f = NULL; | |
440 | unsigned veritytab_line = 0; | |
441 | int r; | |
442 | ||
443 | if (!arg_read_veritytab) | |
444 | return 0; | |
445 | ||
446 | r = fopen_unlocked(arg_veritytab, "re", &f); | |
447 | if (r < 0) { | |
448 | if (errno != ENOENT) | |
449 | log_error_errno(errno, "Failed to open %s: %m", arg_veritytab); | |
450 | return 0; | |
451 | } | |
452 | ||
453 | for (;;) { | |
454 | _cleanup_free_ char *line = NULL, *name = NULL, *data_device = NULL, *hash_device = NULL, | |
455 | *roothash = NULL, *options = NULL; | |
0ff6ff2b | 456 | char *data_uuid, *hash_uuid; |
08b04ec7 | 457 | |
0ff6ff2b | 458 | r = read_stripped_line(f, LONG_LINE_MAX, &line); |
08b04ec7 GP |
459 | if (r < 0) |
460 | return log_error_errno(r, "Failed to read %s: %m", arg_veritytab); | |
461 | if (r == 0) | |
462 | break; | |
463 | ||
464 | veritytab_line++; | |
465 | ||
0ff6ff2b | 466 | if (IN_SET(line[0], 0, '#')) |
08b04ec7 GP |
467 | continue; |
468 | ||
0ff6ff2b | 469 | r = sscanf(line, "%ms %ms %ms %ms %ms", &name, &data_device, &hash_device, &roothash, &options); |
71ce3ba2 | 470 | if (!IN_SET(r, 4, 5)) { |
08b04ec7 GP |
471 | log_error("Failed to parse %s:%u, ignoring.", arg_veritytab, veritytab_line); |
472 | continue; | |
473 | } | |
474 | ||
475 | data_uuid = startswith(data_device, "UUID="); | |
476 | if (!data_uuid) | |
477 | data_uuid = path_startswith(data_device, "/dev/disk/by-uuid/"); | |
478 | ||
479 | hash_uuid = startswith(hash_device, "UUID="); | |
480 | if (!hash_uuid) | |
481 | hash_uuid = path_startswith(hash_device, "/dev/disk/by-uuid/"); | |
482 | ||
deb60ef9 LP |
483 | r = create_veritytab_device( |
484 | name, | |
08b04ec7 GP |
485 | data_device, |
486 | hash_device, | |
487 | roothash, | |
488 | options, | |
489 | arg_veritytab); | |
490 | if (r < 0) | |
491 | return r; | |
08b04ec7 GP |
492 | } |
493 | ||
494 | return 0; | |
495 | } | |
496 | ||
7a44c7e3 | 497 | static int run(const char *dest, const char *dest_early, const char *dest_late) { |
2f3dfc6f LP |
498 | int r; |
499 | ||
7a44c7e3 | 500 | assert_se(arg_dest = dest); |
2f3dfc6f | 501 | |
08b04ec7 GP |
502 | arg_veritytab = getenv("SYSTEMD_VERITYTAB") ?: "/etc/veritytab"; |
503 | ||
2f3dfc6f | 504 | r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); |
d850ad9a YW |
505 | if (r < 0) |
506 | return log_warning_errno(r, "Failed to parse kernel command line: %m"); | |
2f3dfc6f | 507 | |
d850ad9a YW |
508 | if (!arg_enabled) |
509 | return 0; | |
2f3dfc6f | 510 | |
08b04ec7 GP |
511 | r = add_veritytab_devices(); |
512 | if (r < 0) | |
513 | return r; | |
514 | ||
2f3dfc6f LP |
515 | r = determine_devices(); |
516 | if (r < 0) | |
d850ad9a | 517 | return r; |
2f3dfc6f | 518 | |
78b408d2 MB |
519 | r = create_root_device(); |
520 | if (r < 0) | |
521 | return r; | |
522 | ||
523 | return create_usr_device(); | |
2f3dfc6f | 524 | } |
d850ad9a | 525 | |
7a44c7e3 | 526 | DEFINE_MAIN_GENERATOR_FUNCTION(run); |