]>
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 | ||
ad0cdb6b | 139 | if (proc_cmdline_key_streq(key, "systemd.verity")) { |
2f3dfc6f LP |
140 | |
141 | r = value ? parse_boolean(value) : 1; | |
142 | if (r < 0) | |
143 | log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value); | |
144 | else | |
145 | arg_enabled = r; | |
146 | ||
08b04ec7 GP |
147 | } else if (streq(key, "veritytab")) { |
148 | ||
149 | r = value ? parse_boolean(value) : 1; | |
150 | if (r < 0) | |
151 | log_warning("Failed to parse veritytab= kernel command line switch %s. Ignoring.", value); | |
152 | else | |
153 | arg_read_veritytab = r; | |
154 | ||
ad0cdb6b | 155 | } else if (proc_cmdline_key_streq(key, "roothash")) { |
2f3dfc6f LP |
156 | |
157 | if (proc_cmdline_value_missing(key, value)) | |
158 | return 0; | |
159 | ||
160 | r = free_and_strdup(&arg_root_hash, value); | |
161 | if (r < 0) | |
162 | return log_oom(); | |
163 | ||
ad0cdb6b | 164 | } else if (proc_cmdline_key_streq(key, "systemd.verity_root_data")) { |
2f3dfc6f LP |
165 | |
166 | if (proc_cmdline_value_missing(key, value)) | |
167 | return 0; | |
168 | ||
78b408d2 | 169 | r = free_and_strdup(&arg_root_data_what, value); |
2f3dfc6f LP |
170 | if (r < 0) |
171 | return log_oom(); | |
172 | ||
ad0cdb6b | 173 | } else if (proc_cmdline_key_streq(key, "systemd.verity_root_hash")) { |
2f3dfc6f LP |
174 | |
175 | if (proc_cmdline_value_missing(key, value)) | |
176 | return 0; | |
177 | ||
78b408d2 | 178 | r = free_and_strdup(&arg_root_hash_what, value); |
2f3dfc6f LP |
179 | if (r < 0) |
180 | return log_oom(); | |
0141102f GP |
181 | |
182 | } else if (proc_cmdline_key_streq(key, "systemd.verity_root_options")) { | |
183 | ||
184 | if (proc_cmdline_value_missing(key, value)) | |
185 | return 0; | |
186 | ||
78b408d2 MB |
187 | r = free_and_strdup(&arg_root_options, value); |
188 | if (r < 0) | |
189 | return log_oom(); | |
190 | ||
191 | } else if (proc_cmdline_key_streq(key, "usrhash")) { | |
93e0d320 | 192 | |
78b408d2 MB |
193 | if (proc_cmdline_value_missing(key, value)) |
194 | return 0; | |
195 | ||
196 | r = free_and_strdup(&arg_usr_hash, value); | |
197 | if (r < 0) | |
198 | return log_oom(); | |
199 | ||
200 | } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_data")) { | |
201 | ||
202 | if (proc_cmdline_value_missing(key, value)) | |
203 | return 0; | |
204 | ||
205 | r = free_and_strdup(&arg_usr_data_what, value); | |
206 | if (r < 0) | |
207 | return log_oom(); | |
208 | ||
209 | } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_hash")) { | |
210 | ||
211 | if (proc_cmdline_value_missing(key, value)) | |
212 | return 0; | |
213 | ||
214 | r = free_and_strdup(&arg_usr_hash_what, value); | |
215 | if (r < 0) | |
216 | return log_oom(); | |
217 | ||
218 | } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_options")) { | |
219 | ||
220 | if (proc_cmdline_value_missing(key, value)) | |
221 | return 0; | |
222 | ||
223 | r = free_and_strdup(&arg_usr_options, value); | |
0141102f GP |
224 | if (r < 0) |
225 | return log_oom(); | |
226 | ||
2f3dfc6f LP |
227 | } |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
78b408d2 MB |
232 | static int determine_device( |
233 | const char *name, | |
234 | const char *hash, | |
235 | char **data_what, | |
236 | char **hash_what) { | |
237 | ||
238 | sd_id128_t data_uuid, verity_uuid; | |
2f3dfc6f | 239 | _cleanup_free_ void *m = NULL; |
2f3dfc6f LP |
240 | size_t l; |
241 | int r; | |
242 | ||
78b408d2 MB |
243 | assert(name); |
244 | assert(data_what); | |
245 | assert(hash_what); | |
2f3dfc6f | 246 | |
78b408d2 | 247 | if (!hash) |
2f3dfc6f LP |
248 | return 0; |
249 | ||
78b408d2 | 250 | if (*data_what && *hash_what) |
2f3dfc6f LP |
251 | return 0; |
252 | ||
78b408d2 | 253 | r = unhexmem(hash, strlen(hash), &m, &l); |
2f3dfc6f | 254 | if (r < 0) |
78b408d2 | 255 | return log_error_errno(r, "Failed to parse hash: %s", hash); |
2f3dfc6f | 256 | if (l < sizeof(sd_id128_t)) { |
78b408d2 | 257 | log_debug("Root hash for %s is shorter than 128 bits (32 characters), ignoring for discovering verity partition.", name); |
2f3dfc6f LP |
258 | return 0; |
259 | } | |
260 | ||
78b408d2 MB |
261 | if (!*data_what) { |
262 | memcpy(&data_uuid, m, sizeof(data_uuid)); | |
2f3dfc6f | 263 | |
b7416360 | 264 | *data_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(data_uuid)); |
78b408d2 | 265 | if (!*data_what) |
2f3dfc6f LP |
266 | return log_oom(); |
267 | } | |
268 | ||
78b408d2 | 269 | if (!*hash_what) { |
2f3dfc6f LP |
270 | memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid)); |
271 | ||
b7416360 | 272 | *hash_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(verity_uuid)); |
78b408d2 | 273 | if (!*hash_what) |
2f3dfc6f LP |
274 | return log_oom(); |
275 | } | |
276 | ||
78b408d2 MB |
277 | log_info("Using data device %s and hash device %s for %s.", *data_what, *hash_what, name); |
278 | ||
2f3dfc6f LP |
279 | return 1; |
280 | } | |
281 | ||
78b408d2 MB |
282 | static int determine_devices(void) { |
283 | int r; | |
284 | ||
285 | r = determine_device("root", arg_root_hash, &arg_root_data_what, &arg_root_hash_what); | |
286 | if (r < 0) | |
287 | return r; | |
288 | ||
289 | return determine_device("usr", arg_usr_hash, &arg_usr_data_what, &arg_usr_hash_what); | |
290 | } | |
291 | ||
bf1484c7 LP |
292 | static bool attach_in_initrd(const char *name, const char *options) { |
293 | assert(name); | |
294 | ||
295 | /* Imply x-initrd.attach in case the volume name is among those defined in the Discoverable Partition | |
296 | * Specification for partitions that we require to be mounted during the initrd → host transition, | |
297 | * i.e. for the root fs itself, and /usr/. This mirrors similar behaviour in | |
298 | * systemd-fstab-generator. */ | |
299 | ||
300 | return fstab_test_option(options, "x-initrd.attach\0") || | |
301 | STR_IN_SET(name, "root", "usr"); | |
302 | } | |
303 | ||
deb60ef9 | 304 | static int create_veritytab_device( |
08b04ec7 GP |
305 | const char *name, |
306 | const char *data_device, | |
307 | const char *hash_device, | |
308 | const char *roothash, | |
309 | const char *options, | |
310 | const char *source) { | |
311 | ||
312 | _cleanup_free_ char *n = NULL, *dd = NULL, *du = NULL, *hd = NULL, *hu = NULL, *e = NULL, | |
313 | *du_escaped = NULL, *hu_escaped = NULL, *name_escaped = NULL; | |
314 | _cleanup_fclose_ FILE *f = NULL; | |
315 | const char *dmname; | |
5dd02147 | 316 | bool noauto, nofail, netdev, need_loop = false; |
08b04ec7 GP |
317 | int r; |
318 | ||
deb60ef9 LP |
319 | /* Creates a systemd-veritysetup@.service instance for volumes specified in /etc/veritytab. */ |
320 | ||
08b04ec7 GP |
321 | assert(name); |
322 | assert(data_device); | |
323 | assert(hash_device); | |
324 | assert(roothash); | |
325 | ||
326 | noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); | |
327 | nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); | |
328 | netdev = fstab_test_option(options, "_netdev\0"); | |
08b04ec7 GP |
329 | |
330 | name_escaped = specifier_escape(name); | |
331 | if (!name_escaped) | |
332 | return log_oom(); | |
333 | ||
334 | e = unit_name_escape(name); | |
335 | if (!e) | |
336 | return log_oom(); | |
337 | ||
338 | du = fstab_node_to_udev_node(data_device); | |
339 | if (!du) | |
340 | return log_oom(); | |
341 | ||
342 | hu = fstab_node_to_udev_node(hash_device); | |
343 | if (!hu) | |
344 | return log_oom(); | |
345 | ||
346 | r = unit_name_build("systemd-veritysetup", e, ".service", &n); | |
347 | if (r < 0) | |
348 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
349 | ||
350 | du_escaped = specifier_escape(du); | |
351 | if (!du_escaped) | |
352 | return log_oom(); | |
353 | ||
354 | hu_escaped = specifier_escape(hu); | |
355 | if (!hu_escaped) | |
356 | return log_oom(); | |
357 | ||
358 | r = unit_name_from_path(du, ".device", &dd); | |
359 | if (r < 0) | |
360 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
361 | ||
362 | r = unit_name_from_path(hu, ".device", &hd); | |
363 | if (r < 0) | |
364 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
365 | ||
366 | r = generator_open_unit_file(arg_dest, NULL, n, &f); | |
367 | if (r < 0) | |
368 | return r; | |
369 | ||
370 | r = generator_write_veritysetup_unit_section(f, source); | |
371 | if (r < 0) | |
372 | return r; | |
373 | ||
374 | if (netdev) | |
375 | fprintf(f, "After=remote-fs-pre.target\n"); | |
376 | ||
377 | /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */ | |
bf1484c7 LP |
378 | if (!attach_in_initrd(name, options)) |
379 | fprintf(f, | |
380 | "Conflicts=umount.target\n" | |
381 | "Before=umount.target\n"); | |
08b04ec7 GP |
382 | |
383 | if (!nofail) | |
384 | fprintf(f, | |
385 | "Before=%s\n", | |
386 | netdev ? "remote-veritysetup.target" : "veritysetup.target"); | |
387 | ||
388 | if (path_startswith(du, "/dev/")) | |
389 | fprintf(f, | |
390 | "BindsTo=%s\n" | |
bf1484c7 | 391 | "After=%s\n", |
08b04ec7 | 392 | dd, dd); |
5dd02147 LP |
393 | else { |
394 | fprintf(f, "RequiresMountsFor=%s\n", du_escaped); | |
395 | need_loop = true; | |
396 | } | |
08b04ec7 GP |
397 | |
398 | if (path_startswith(hu, "/dev/")) | |
399 | fprintf(f, | |
400 | "BindsTo=%s\n" | |
bf1484c7 | 401 | "After=%s\n", |
08b04ec7 | 402 | hd, hd); |
5dd02147 LP |
403 | else { |
404 | fprintf(f, "RequiresMountsFor=%s\n", hu_escaped); | |
405 | need_loop = true; | |
406 | } | |
407 | ||
408 | if (need_loop) | |
ce49a479 LP |
409 | /* For loopback devices make sure to explicitly load loop.ko, as this code might run very |
410 | * early where device nodes created via systemd-tmpfiles-setup-dev.service might not be | |
411 | * around yet. Hence let's sync on the module itself. */ | |
08b04ec7 | 412 | fprintf(f, |
ce49a479 LP |
413 | "Wants=modprobe@loop.service\n" |
414 | "After=modprobe@loop.service\n"); | |
08b04ec7 GP |
415 | |
416 | r = generator_write_veritysetup_service_section(f, name, du_escaped, hu_escaped, roothash, options); | |
417 | if (r < 0) | |
418 | return r; | |
419 | ||
420 | r = fflush_and_check(f); | |
421 | if (r < 0) | |
422 | return log_error_errno(r, "Failed to write unit file %s: %m", n); | |
423 | ||
424 | if (!noauto) { | |
425 | r = generator_add_symlink(arg_dest, | |
426 | netdev ? "remote-veritysetup.target" : "veritysetup.target", | |
427 | nofail ? "wants" : "requires", n); | |
428 | if (r < 0) | |
429 | return r; | |
430 | } | |
431 | ||
432 | dmname = strjoina("dev-mapper-", e, ".device"); | |
433 | return generator_add_symlink(arg_dest, dmname, "requires", n); | |
434 | } | |
435 | ||
436 | static int add_veritytab_devices(void) { | |
437 | _cleanup_fclose_ FILE *f = NULL; | |
438 | unsigned veritytab_line = 0; | |
439 | int r; | |
440 | ||
441 | if (!arg_read_veritytab) | |
442 | return 0; | |
443 | ||
444 | r = fopen_unlocked(arg_veritytab, "re", &f); | |
445 | if (r < 0) { | |
446 | if (errno != ENOENT) | |
447 | log_error_errno(errno, "Failed to open %s: %m", arg_veritytab); | |
448 | return 0; | |
449 | } | |
450 | ||
451 | for (;;) { | |
452 | _cleanup_free_ char *line = NULL, *name = NULL, *data_device = NULL, *hash_device = NULL, | |
453 | *roothash = NULL, *options = NULL; | |
08b04ec7 | 454 | char *l, *data_uuid, *hash_uuid; |
08b04ec7 GP |
455 | |
456 | r = read_line(f, LONG_LINE_MAX, &line); | |
457 | if (r < 0) | |
458 | return log_error_errno(r, "Failed to read %s: %m", arg_veritytab); | |
459 | if (r == 0) | |
460 | break; | |
461 | ||
462 | veritytab_line++; | |
463 | ||
464 | l = strstrip(line); | |
465 | if (IN_SET(l[0], 0, '#')) | |
466 | continue; | |
467 | ||
71ce3ba2 YW |
468 | r = sscanf(l, "%ms %ms %ms %ms %ms", &name, &data_device, &hash_device, &roothash, &options); |
469 | if (!IN_SET(r, 4, 5)) { | |
08b04ec7 GP |
470 | log_error("Failed to parse %s:%u, ignoring.", arg_veritytab, veritytab_line); |
471 | continue; | |
472 | } | |
473 | ||
474 | data_uuid = startswith(data_device, "UUID="); | |
475 | if (!data_uuid) | |
476 | data_uuid = path_startswith(data_device, "/dev/disk/by-uuid/"); | |
477 | ||
478 | hash_uuid = startswith(hash_device, "UUID="); | |
479 | if (!hash_uuid) | |
480 | hash_uuid = path_startswith(hash_device, "/dev/disk/by-uuid/"); | |
481 | ||
deb60ef9 LP |
482 | r = create_veritytab_device( |
483 | name, | |
08b04ec7 GP |
484 | data_device, |
485 | hash_device, | |
486 | roothash, | |
487 | options, | |
488 | arg_veritytab); | |
489 | if (r < 0) | |
490 | return r; | |
08b04ec7 GP |
491 | } |
492 | ||
493 | return 0; | |
494 | } | |
495 | ||
7a44c7e3 | 496 | static int run(const char *dest, const char *dest_early, const char *dest_late) { |
2f3dfc6f LP |
497 | int r; |
498 | ||
7a44c7e3 | 499 | assert_se(arg_dest = dest); |
2f3dfc6f | 500 | |
08b04ec7 GP |
501 | arg_veritytab = getenv("SYSTEMD_VERITYTAB") ?: "/etc/veritytab"; |
502 | ||
2f3dfc6f | 503 | r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); |
d850ad9a YW |
504 | if (r < 0) |
505 | return log_warning_errno(r, "Failed to parse kernel command line: %m"); | |
2f3dfc6f | 506 | |
d850ad9a YW |
507 | if (!arg_enabled) |
508 | return 0; | |
2f3dfc6f | 509 | |
08b04ec7 GP |
510 | r = add_veritytab_devices(); |
511 | if (r < 0) | |
512 | return r; | |
513 | ||
2f3dfc6f LP |
514 | r = determine_devices(); |
515 | if (r < 0) | |
d850ad9a | 516 | return r; |
2f3dfc6f | 517 | |
78b408d2 MB |
518 | r = create_root_device(); |
519 | if (r < 0) | |
520 | return r; | |
521 | ||
522 | return create_usr_device(); | |
2f3dfc6f | 523 | } |
d850ad9a | 524 | |
7a44c7e3 | 525 | DEFINE_MAIN_GENERATOR_FUNCTION(run); |