]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
1a14a53c | 2 | |
1a14a53c | 3 | #include <stdlib.h> |
41bc4849 | 4 | #include <sys/file.h> |
cf0fbc49 | 5 | #include <unistd.h> |
1a14a53c | 6 | |
85624f01 | 7 | #include "sd-device.h" |
07630cea LP |
8 | #include "sd-id128.h" |
9 | ||
b5efdb8a | 10 | #include "alloc-util.h" |
07630cea | 11 | #include "blkid-util.h" |
18c528e9 | 12 | #include "blockdev-util.h" |
07630cea | 13 | #include "btrfs-util.h" |
133432cc | 14 | #include "device-util.h" |
7176f06c | 15 | #include "devnum-util.h" |
a0956174 | 16 | #include "dirent-util.h" |
72e18a98 | 17 | #include "dissect-image.h" |
1fac34b9 | 18 | #include "dropin.h" |
0bb2f0f1 | 19 | #include "efi-loader.h" |
3ffd4af2 | 20 | #include "fd-util.h" |
07630cea | 21 | #include "fileio.h" |
2bef2582 | 22 | #include "fs-util.h" |
07630cea LP |
23 | #include "fstab-util.h" |
24 | #include "generator.h" | |
25 | #include "gpt.h" | |
baa6a42d | 26 | #include "initrd-util.h" |
07630cea | 27 | #include "mkdir.h" |
049af8ad | 28 | #include "mountpoint-util.h" |
6bedfcbb | 29 | #include "parse-util.h" |
07630cea | 30 | #include "path-util.h" |
4e731273 | 31 | #include "proc-cmdline.h" |
1a14a53c | 32 | #include "special.h" |
98bad05e | 33 | #include "specifier.h" |
8fcde012 | 34 | #include "stat-util.h" |
07630cea | 35 | #include "string-util.h" |
85624f01 | 36 | #include "strv.h" |
1a14a53c | 37 | #include "unit-name.h" |
9a5cb137 | 38 | #include "virt.h" |
1a14a53c | 39 | |
ec6e9597 | 40 | static const char *arg_dest = NULL; |
73b80ec2 LP |
41 | static bool arg_enabled = true; |
42 | static bool arg_root_enabled = true; | |
cf451f38 LP |
43 | static char *arg_root_fstype = NULL; |
44 | static char *arg_root_options = NULL; | |
c94b2417 | 45 | static int arg_root_rw = -1; |
1a14a53c | 46 | |
cf451f38 LP |
47 | STATIC_DESTRUCTOR_REGISTER(arg_root_fstype, freep); |
48 | STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep); | |
49 | ||
06648fa9 LP |
50 | static int add_cryptsetup( |
51 | const char *id, | |
52 | const char *what, | |
53 | bool rw, | |
54 | bool require, | |
ff386f98 | 55 | bool measure, |
06648fa9 LP |
56 | char **ret_device) { |
57 | ||
5b137503 | 58 | #if HAVE_LIBCRYPTSETUP |
ff386f98 | 59 | _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *options = NULL; |
1af72119 | 60 | _cleanup_fclose_ FILE *f = NULL; |
1af72119 LP |
61 | int r; |
62 | ||
63 | assert(id); | |
64 | assert(what); | |
1af72119 | 65 | |
7410616c LP |
66 | r = unit_name_from_path(what, ".device", &d); |
67 | if (r < 0) | |
68 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
1af72119 LP |
69 | |
70 | e = unit_name_escape(id); | |
71 | if (!e) | |
72 | return log_oom(); | |
73 | ||
7410616c LP |
74 | r = unit_name_build("systemd-cryptsetup", e, ".service", &n); |
75 | if (r < 0) | |
76 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
1af72119 | 77 | |
a7e88558 LP |
78 | r = generator_open_unit_file(arg_dest, NULL, n, &f); |
79 | if (r < 0) | |
80 | return r; | |
98bad05e | 81 | |
a7e88558 LP |
82 | r = generator_write_cryptsetup_unit_section(f, NULL); |
83 | if (r < 0) | |
84 | return r; | |
1af72119 LP |
85 | |
86 | fprintf(f, | |
1af72119 | 87 | "Before=umount.target cryptsetup.target\n" |
a7e88558 LP |
88 | "Conflicts=umount.target\n" |
89 | "BindsTo=%s\n" | |
90 | "After=%s\n", | |
91 | d, d); | |
92 | ||
ff386f98 LP |
93 | if (!rw) { |
94 | options = strdup("read-only"); | |
95 | if (!options) | |
96 | return log_oom(); | |
97 | } | |
98 | ||
99 | if (measure) { | |
100 | /* We only measure the root volume key into PCR 15 if we are booted with sd-stub (i.e. in a | |
101 | * UKI), and sd-stub measured the UKI. We do this in order not to step into people's own PCR | |
102 | * assignment, under the assumption that people who are fine to use sd-stub with its PCR | |
103 | * assignments are also OK with our PCR 15 use here. */ | |
104 | ||
6c51b49c LP |
105 | r = efi_stub_measured(); |
106 | if (r < 0) | |
107 | log_warning_errno(r, "Failed to determine whether booted via systemd-stub with measurements enabled, ignoring: %m"); | |
108 | else if (r == 0) | |
109 | log_debug("Will not measure volume key of volume '%s', because not booted via systemd-stub with measurements enabled.", id); | |
ff386f98 LP |
110 | else if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes")) |
111 | return log_oom(); | |
112 | } | |
113 | ||
114 | r = generator_write_cryptsetup_service_section(f, id, what, NULL, options); | |
a7e88558 LP |
115 | if (r < 0) |
116 | return r; | |
1af72119 | 117 | |
dacd6cee LP |
118 | r = fflush_and_check(f); |
119 | if (r < 0) | |
a7e88558 | 120 | return log_error_errno(r, "Failed to write file %s: %m", n); |
1af72119 | 121 | |
9cdcf368 ZJS |
122 | r = generator_add_symlink(arg_dest, d, "wants", n); |
123 | if (r < 0) | |
124 | return r; | |
1af72119 | 125 | |
1fac34b9 ZJS |
126 | const char *dmname; |
127 | dmname = strjoina("dev-mapper-", e, ".device"); | |
1af72119 | 128 | |
1fac34b9 | 129 | if (require) { |
9cdcf368 ZJS |
130 | r = generator_add_symlink(arg_dest, "cryptsetup.target", "requires", n); |
131 | if (r < 0) | |
132 | return r; | |
01af8c01 | 133 | |
9cdcf368 ZJS |
134 | r = generator_add_symlink(arg_dest, dmname, "requires", n); |
135 | if (r < 0) | |
136 | return r; | |
01af8c01 | 137 | } |
1af72119 | 138 | |
1fac34b9 ZJS |
139 | r = write_drop_in_format(arg_dest, dmname, 50, "job-timeout", |
140 | "# Automatically generated by systemd-gpt-auto-generator\n\n" | |
141 | "[Unit]\n" | |
142 | "JobTimeoutSec=0"); /* the binary handles timeouts anyway */ | |
23bbb0de | 143 | if (r < 0) |
1fac34b9 | 144 | log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m"); |
1af72119 | 145 | |
06648fa9 LP |
146 | if (ret_device) { |
147 | char *s; | |
2aa2860b | 148 | |
06648fa9 LP |
149 | s = path_join("/dev/mapper", id); |
150 | if (!s) | |
2aa2860b | 151 | return log_oom(); |
1af72119 | 152 | |
06648fa9 | 153 | *ret_device = s; |
2aa2860b ZJS |
154 | } |
155 | ||
1af72119 | 156 | return 0; |
5b137503 YG |
157 | #else |
158 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Partition is encrypted, but the project was compiled without libcryptsetup support"); | |
159 | #endif | |
1af72119 LP |
160 | } |
161 | ||
73b80ec2 LP |
162 | static int add_mount( |
163 | const char *id, | |
164 | const char *what, | |
165 | const char *where, | |
166 | const char *fstype, | |
cca1dfdd | 167 | bool rw, |
400c1e8f | 168 | bool growfs, |
ff386f98 | 169 | bool measure, |
59512f21 | 170 | const char *options, |
73b80ec2 LP |
171 | const char *description, |
172 | const char *post) { | |
173 | ||
9cdcf368 | 174 | _cleanup_free_ char *unit = NULL, *crypto_what = NULL, *p = NULL; |
1a14a53c | 175 | _cleanup_fclose_ FILE *f = NULL; |
e48fdd84 | 176 | int r; |
1a14a53c | 177 | |
98bad05e LP |
178 | /* Note that we don't apply specifier escaping on the input strings here, since we know they are not configured |
179 | * externally, but all originate from our own sources here, and hence we know they contain no % characters that | |
180 | * could potentially be understood as specifiers. */ | |
181 | ||
1af72119 LP |
182 | assert(id); |
183 | assert(what); | |
184 | assert(where); | |
1af72119 LP |
185 | assert(description); |
186 | ||
074cdb95 | 187 | log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)"); |
1a14a53c | 188 | |
73b80ec2 | 189 | if (streq_ptr(fstype, "crypto_LUKS")) { |
ff386f98 | 190 | r = add_cryptsetup(id, what, rw, /* require= */ true, measure, &crypto_what); |
1af72119 LP |
191 | if (r < 0) |
192 | return r; | |
193 | ||
194 | what = crypto_what; | |
195 | fstype = NULL; | |
80ce8580 LP |
196 | } else if (fstype) { |
197 | r = dissect_fstype_ok(fstype); | |
198 | if (r < 0) | |
199 | return log_error_errno(r, "Unable to determine of dissected file system type '%s' is permitted: %m", fstype); | |
200 | if (!r) | |
201 | return log_error_errno( | |
202 | SYNTHETIC_ERRNO(EIDRM), | |
203 | "Refusing to automatically mount uncommon file system '%s' to '%s'.", | |
204 | fstype, where); | |
1af72119 LP |
205 | } |
206 | ||
7410616c LP |
207 | r = unit_name_from_path(where, ".mount", &unit); |
208 | if (r < 0) | |
209 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
1a14a53c | 210 | |
657ee2d8 | 211 | p = path_join(empty_to_root(arg_dest), unit); |
e48fdd84 LP |
212 | if (!p) |
213 | return log_oom(); | |
214 | ||
215 | f = fopen(p, "wxe"); | |
4a62c710 MS |
216 | if (!f) |
217 | return log_error_errno(errno, "Failed to create unit file %s: %m", unit); | |
1a14a53c LP |
218 | |
219 | fprintf(f, | |
220 | "# Automatically generated by systemd-gpt-auto-generator\n\n" | |
221 | "[Unit]\n" | |
c3834f9b LP |
222 | "Description=%s\n" |
223 | "Documentation=man:systemd-gpt-auto-generator(8)\n", | |
e48fdd84 LP |
224 | description); |
225 | ||
73b80ec2 LP |
226 | if (post) |
227 | fprintf(f, "Before=%s\n", post); | |
228 | ||
e48fdd84 LP |
229 | r = generator_write_fsck_deps(f, arg_dest, what, where, fstype); |
230 | if (r < 0) | |
231 | return r; | |
232 | ||
a7e88558 LP |
233 | r = generator_write_blockdev_dependency(f, what); |
234 | if (r < 0) | |
235 | return r; | |
236 | ||
e48fdd84 LP |
237 | fprintf(f, |
238 | "\n" | |
1a14a53c LP |
239 | "[Mount]\n" |
240 | "What=%s\n" | |
1af72119 LP |
241 | "Where=%s\n", |
242 | what, where); | |
243 | ||
73b80ec2 LP |
244 | if (fstype) |
245 | fprintf(f, "Type=%s\n", fstype); | |
246 | ||
59512f21 KS |
247 | if (options) |
248 | fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro"); | |
249 | else | |
250 | fprintf(f, "Options=%s\n", rw ? "rw" : "ro"); | |
1a14a53c | 251 | |
dacd6cee LP |
252 | r = fflush_and_check(f); |
253 | if (r < 0) | |
254 | return log_error_errno(r, "Failed to write unit file %s: %m", p); | |
1a14a53c | 255 | |
400c1e8f LP |
256 | if (growfs) { |
257 | r = generator_hook_up_growfs(arg_dest, where, post); | |
258 | if (r < 0) | |
259 | return r; | |
260 | } | |
261 | ||
04959faa LP |
262 | if (measure) { |
263 | r = generator_hook_up_pcrfs(arg_dest, where, post); | |
264 | if (r < 0) | |
265 | return r; | |
266 | } | |
267 | ||
400c1e8f LP |
268 | if (post) { |
269 | r = generator_add_symlink(arg_dest, post, "requires", unit); | |
270 | if (r < 0) | |
271 | return r; | |
272 | } | |
273 | ||
1a14a53c LP |
274 | return 0; |
275 | } | |
276 | ||
e137880b | 277 | static int path_is_busy(const char *where) { |
59512f21 KS |
278 | int r; |
279 | ||
280 | /* already a mountpoint; generators run during reload */ | |
e1873695 | 281 | r = path_is_mount_point(where, NULL, AT_SYMLINK_FOLLOW); |
59512f21 KS |
282 | if (r > 0) |
283 | return false; | |
284 | ||
285 | /* the directory might not exist on a stateless system */ | |
286 | if (r == -ENOENT) | |
287 | return false; | |
288 | ||
289 | if (r < 0) | |
e137880b | 290 | return log_warning_errno(r, "Cannot check if \"%s\" is a mount point: %m", where); |
59512f21 KS |
291 | |
292 | /* not a mountpoint but it contains files */ | |
db55bbf2 | 293 | r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false); |
e137880b ZJS |
294 | if (r < 0) |
295 | return log_warning_errno(r, "Cannot check if \"%s\" is empty: %m", where); | |
296 | if (r > 0) | |
297 | return false; | |
59512f21 | 298 | |
e137880b ZJS |
299 | log_debug("\"%s\" already populated, ignoring.", where); |
300 | return true; | |
59512f21 KS |
301 | } |
302 | ||
72e18a98 LP |
303 | static int add_partition_mount( |
304 | DissectedPartition *p, | |
61331eab | 305 | const char *id, |
61331eab | 306 | const char *where, |
72e18a98 | 307 | const char *description) { |
61331eab | 308 | |
e137880b | 309 | int r; |
72e18a98 | 310 | assert(p); |
61331eab | 311 | |
e137880b ZJS |
312 | r = path_is_busy(where); |
313 | if (r != 0) | |
314 | return r < 0 ? r : 0; | |
61331eab | 315 | |
61331eab LP |
316 | return add_mount( |
317 | id, | |
72e18a98 | 318 | p->node, |
61331eab | 319 | where, |
72e18a98 LP |
320 | p->fstype, |
321 | p->rw, | |
400c1e8f | 322 | p->growfs, |
ff386f98 | 323 | /* measure= */ STR_IN_SET(id, "root", "var"), /* by default measure rootfs and /var, since they contain the "identity" of the system */ |
59512f21 | 324 | NULL, |
61331eab | 325 | description, |
72e18a98 | 326 | SPECIAL_LOCAL_FS_TARGET); |
61331eab LP |
327 | } |
328 | ||
235ae69c | 329 | static int add_partition_swap(DissectedPartition *p) { |
8859b8f7 HOB |
330 | const char *what; |
331 | _cleanup_free_ char *name = NULL, *unit = NULL, *crypto_what = NULL; | |
59512f21 KS |
332 | _cleanup_fclose_ FILE *f = NULL; |
333 | int r; | |
334 | ||
8859b8f7 HOB |
335 | assert(p); |
336 | assert(p->node); | |
59512f21 | 337 | |
fc5bc384 FB |
338 | /* Disable the swap auto logic if at least one swap is defined in /etc/fstab, see #6192. */ |
339 | r = fstab_has_fstype("swap"); | |
340 | if (r < 0) | |
341 | return log_error_errno(r, "Failed to parse fstab: %m"); | |
1a680ae3 | 342 | if (r > 0) { |
fc5bc384 FB |
343 | log_debug("swap specified in fstab, ignoring."); |
344 | return 0; | |
345 | } | |
346 | ||
8859b8f7 | 347 | if (streq_ptr(p->fstype, "crypto_LUKS")) { |
ff386f98 | 348 | r = add_cryptsetup("swap", p->node, /* rw= */ true, /* require= */ true, /* measure= */ false, &crypto_what); |
8859b8f7 HOB |
349 | if (r < 0) |
350 | return r; | |
351 | what = crypto_what; | |
352 | } else | |
353 | what = p->node; | |
354 | ||
355 | log_debug("Adding swap: %s", what); | |
59512f21 | 356 | |
8859b8f7 | 357 | r = unit_name_from_path(what, ".swap", &name); |
59512f21 KS |
358 | if (r < 0) |
359 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
360 | ||
657ee2d8 | 361 | unit = path_join(empty_to_root(arg_dest), name); |
59512f21 KS |
362 | if (!unit) |
363 | return log_oom(); | |
364 | ||
365 | f = fopen(unit, "wxe"); | |
366 | if (!f) | |
367 | return log_error_errno(errno, "Failed to create unit file %s: %m", unit); | |
368 | ||
369 | fprintf(f, | |
370 | "# Automatically generated by systemd-gpt-auto-generator\n\n" | |
371 | "[Unit]\n" | |
372 | "Description=Swap Partition\n" | |
a7e88558 LP |
373 | "Documentation=man:systemd-gpt-auto-generator(8)\n"); |
374 | ||
8859b8f7 | 375 | r = generator_write_blockdev_dependency(f, what); |
a7e88558 LP |
376 | if (r < 0) |
377 | return r; | |
378 | ||
379 | fprintf(f, | |
380 | "\n" | |
59512f21 KS |
381 | "[Swap]\n" |
382 | "What=%s\n", | |
8859b8f7 | 383 | what); |
59512f21 | 384 | |
dacd6cee LP |
385 | r = fflush_and_check(f); |
386 | if (r < 0) | |
387 | return log_error_errno(r, "Failed to write unit file %s: %m", unit); | |
59512f21 | 388 | |
9cdcf368 | 389 | return generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, "wants", name); |
59512f21 KS |
390 | } |
391 | ||
7a1494aa TG |
392 | static int add_automount( |
393 | const char *id, | |
394 | const char *what, | |
395 | const char *where, | |
396 | const char *fstype, | |
397 | bool rw, | |
400c1e8f | 398 | bool growfs, |
7a1494aa TG |
399 | const char *options, |
400 | const char *description, | |
401 | usec_t timeout) { | |
402 | ||
8e7e4a73 | 403 | _cleanup_free_ char *unit = NULL, *p = NULL; |
7a1494aa TG |
404 | _cleanup_fclose_ FILE *f = NULL; |
405 | int r; | |
406 | ||
407 | assert(id); | |
408 | assert(where); | |
409 | assert(description); | |
410 | ||
7a1494aa TG |
411 | r = add_mount(id, |
412 | what, | |
413 | where, | |
414 | fstype, | |
415 | rw, | |
400c1e8f | 416 | growfs, |
ff386f98 | 417 | /* measure= */ false, |
d22771fc | 418 | options, |
7a1494aa TG |
419 | description, |
420 | NULL); | |
421 | if (r < 0) | |
422 | return r; | |
423 | ||
424 | r = unit_name_from_path(where, ".automount", &unit); | |
425 | if (r < 0) | |
426 | return log_error_errno(r, "Failed to generate unit name: %m"); | |
427 | ||
8e7e4a73 LP |
428 | p = path_join(arg_dest, unit); |
429 | if (!p) | |
430 | return log_oom(); | |
431 | ||
7a1494aa TG |
432 | f = fopen(p, "wxe"); |
433 | if (!f) | |
434 | return log_error_errno(errno, "Failed to create unit file %s: %m", unit); | |
435 | ||
436 | fprintf(f, | |
437 | "# Automatically generated by systemd-gpt-auto-generator\n\n" | |
438 | "[Unit]\n" | |
439 | "Description=%s\n" | |
440 | "Documentation=man:systemd-gpt-auto-generator(8)\n" | |
441 | "[Automount]\n" | |
442 | "Where=%s\n" | |
70887c5f | 443 | "TimeoutIdleSec="USEC_FMT"\n", |
7a1494aa TG |
444 | description, |
445 | where, | |
70887c5f | 446 | timeout / USEC_PER_SEC); |
7a1494aa TG |
447 | |
448 | r = fflush_and_check(f); | |
449 | if (r < 0) | |
450 | return log_error_errno(r, "Failed to write unit file %s: %m", p); | |
451 | ||
9cdcf368 | 452 | return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit); |
7a1494aa TG |
453 | } |
454 | ||
4f084066 LP |
455 | static const char *esp_or_xbootldr_options(const DissectedPartition *p) { |
456 | assert(p); | |
457 | ||
d09df6b9 | 458 | /* Discovered ESP and XBOOTLDR partition are always hardened with "noexec,nosuid,nodev". |
d708293d MY |
459 | * If we probed vfat or have no idea about the file system then assume these file systems are vfat |
460 | * and thus understand "umask=0077". */ | |
4f084066 LP |
461 | |
462 | if (!p->fstype || streq(p->fstype, "vfat")) | |
d708293d | 463 | return "umask=0077,noexec,nosuid,nodev"; |
4f084066 | 464 | |
d708293d | 465 | return "noexec,nosuid,nodev"; |
4f084066 LP |
466 | } |
467 | ||
235ae69c | 468 | static int add_partition_xbootldr(DissectedPartition *p) { |
9f1cb0c1 LP |
469 | int r; |
470 | ||
471 | assert(p); | |
472 | ||
473 | if (in_initrd()) { | |
474 | log_debug("In initrd, ignoring the XBOOTLDR partition."); | |
475 | return 0; | |
476 | } | |
477 | ||
478 | r = fstab_is_mount_point("/boot"); | |
479 | if (r < 0) | |
480 | return log_error_errno(r, "Failed to parse fstab: %m"); | |
481 | if (r > 0) { | |
482 | log_debug("/boot specified in fstab, ignoring XBOOTLDR partition."); | |
483 | return 0; | |
484 | } | |
485 | ||
486 | r = path_is_busy("/boot"); | |
487 | if (r < 0) | |
488 | return r; | |
489 | if (r > 0) | |
490 | return 0; | |
491 | ||
492 | return add_automount("boot", | |
493 | p->node, | |
494 | "/boot", | |
495 | p->fstype, | |
400c1e8f LP |
496 | /* rw= */ true, |
497 | /* growfs= */ false, | |
4f084066 | 498 | esp_or_xbootldr_options(p), |
9f1cb0c1 LP |
499 | "Boot Loader Partition", |
500 | 120 * USEC_PER_SEC); | |
501 | } | |
502 | ||
503 | #if ENABLE_EFI | |
235ae69c | 504 | static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) { |
9f1cb0c1 | 505 | const char *esp_path = NULL, *id = NULL; |
59512f21 KS |
506 | int r; |
507 | ||
72e18a98 | 508 | assert(p); |
59512f21 | 509 | |
59512f21 | 510 | if (in_initrd()) { |
b52a109a | 511 | log_debug("In initrd, ignoring the ESP."); |
59512f21 KS |
512 | return 0; |
513 | } | |
514 | ||
9f1cb0c1 LP |
515 | /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice, but |
516 | * only if there's no explicit XBOOTLDR partition around. */ | |
517 | if (access("/efi", F_OK) < 0) { | |
518 | if (errno != ENOENT) | |
519 | return log_error_errno(errno, "Failed to determine whether /efi exists: %m"); | |
520 | ||
521 | /* Use /boot as fallback, but only if there's no XBOOTLDR partition */ | |
522 | if (!has_xbootldr) { | |
523 | esp_path = "/boot"; | |
524 | id = "boot"; | |
525 | } | |
526 | } | |
527 | if (!esp_path) | |
528 | esp_path = "/efi"; | |
529 | if (!id) | |
530 | id = "efi"; | |
59512f21 | 531 | |
0b6b6787 | 532 | /* We create an .automount which is not overridden by the .mount from the fstab generator. */ |
9f1cb0c1 | 533 | r = fstab_is_mount_point(esp_path); |
b9088048 FB |
534 | if (r < 0) |
535 | return log_error_errno(r, "Failed to parse fstab: %m"); | |
39b6a511 | 536 | if (r > 0) { |
9f1cb0c1 | 537 | log_debug("%s specified in fstab, ignoring.", esp_path); |
59512f21 KS |
538 | return 0; |
539 | } | |
540 | ||
9f1cb0c1 LP |
541 | r = path_is_busy(esp_path); |
542 | if (r < 0) | |
543 | return r; | |
544 | if (r > 0) | |
545 | return 0; | |
59512f21 | 546 | |
7ba25ab5 | 547 | if (is_efi_boot()) { |
72e18a98 | 548 | sd_id128_t loader_uuid; |
59512f21 | 549 | |
7ba25ab5 | 550 | /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */ |
59512f21 | 551 | |
7ba25ab5 LP |
552 | r = efi_loader_get_device_part_uuid(&loader_uuid); |
553 | if (r == -ENOENT) { | |
554 | log_debug("EFI loader partition unknown."); | |
555 | return 0; | |
556 | } | |
e28973ee ZJS |
557 | if (r < 0) |
558 | return log_error_errno(r, "Failed to read ESP partition UUID: %m"); | |
7ba25ab5 | 559 | |
72e18a98 | 560 | if (!sd_id128_equal(p->uuid, loader_uuid)) { |
9f1cb0c1 | 561 | log_debug("Partition for %s does not appear to be the partition we are booted from.", p->node); |
7ba25ab5 LP |
562 | return 0; |
563 | } | |
564 | } else | |
565 | log_debug("Not an EFI boot, skipping ESP check."); | |
566 | ||
9f1cb0c1 | 567 | return add_automount(id, |
72e18a98 | 568 | p->node, |
9f1cb0c1 | 569 | esp_path, |
72e18a98 | 570 | p->fstype, |
400c1e8f LP |
571 | /* rw= */ true, |
572 | /* growfs= */ false, | |
4f084066 | 573 | esp_or_xbootldr_options(p), |
72e18a98 LP |
574 | "EFI System Partition Automount", |
575 | 120 * USEC_PER_SEC); | |
7a1494aa | 576 | } |
59512f21 | 577 | #else |
235ae69c | 578 | static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) { |
59512f21 | 579 | return 0; |
59512f21 | 580 | } |
7a1494aa | 581 | #endif |
59512f21 | 582 | |
235ae69c | 583 | static int add_partition_root_rw(DissectedPartition *p) { |
fd89051e LP |
584 | const char *path; |
585 | int r; | |
586 | ||
587 | assert(p); | |
588 | ||
589 | if (in_initrd()) { | |
590 | log_debug("In initrd, not generating drop-in for systemd-remount-fs.service."); | |
591 | return 0; | |
592 | } | |
593 | ||
594 | if (arg_root_rw >= 0) { | |
595 | log_debug("Parameter ro/rw specified on kernel command line, not generating drop-in for systemd-remount-fs.service."); | |
596 | return 0; | |
597 | } | |
598 | ||
599 | if (!p->rw) { | |
600 | log_debug("Root partition marked read-only in GPT partition table, not generating drop-in for systemd-remount-fs.service."); | |
601 | return 0; | |
602 | } | |
603 | ||
9b69569d ZJS |
604 | (void) generator_enable_remount_fs_service(arg_dest); |
605 | ||
fd89051e | 606 | path = strjoina(arg_dest, "/systemd-remount-fs.service.d/50-remount-rw.conf"); |
fd89051e LP |
607 | |
608 | r = write_string_file(path, | |
609 | "# Automatically generated by systemd-gpt-generator\n\n" | |
fd89051e LP |
610 | "[Service]\n" |
611 | "Environment=SYSTEMD_REMOUNT_ROOT_RW=1\n", | |
e82e549f | 612 | WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_NOFOLLOW|WRITE_STRING_FILE_MKDIR_0755); |
fd89051e LP |
613 | if (r < 0) |
614 | return log_error_errno(r, "Failed to write drop-in file %s: %m", path); | |
615 | ||
616 | return 0; | |
617 | } | |
618 | ||
9fe6f5cc ZJS |
619 | #if ENABLE_EFI |
620 | static int add_root_cryptsetup(void) { | |
d0523bb0 | 621 | #if HAVE_LIBCRYPTSETUP |
1a14a53c | 622 | |
9fe6f5cc ZJS |
623 | /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which |
624 | * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */ | |
1a14a53c | 625 | |
ff386f98 | 626 | return add_cryptsetup("root", "/dev/gpt-auto-root-luks", /* rw= */ true, /* require= */ false, /* measure= */ true, NULL); |
d0523bb0 DS |
627 | #else |
628 | return 0; | |
629 | #endif | |
9fe6f5cc ZJS |
630 | } |
631 | #endif | |
d2a62382 | 632 | |
9fe6f5cc ZJS |
633 | static int add_root_mount(void) { |
634 | #if ENABLE_EFI | |
635 | int r; | |
1a14a53c | 636 | |
9fe6f5cc | 637 | if (!is_efi_boot()) { |
387f6955 | 638 | log_debug("Not an EFI boot, not creating root mount."); |
8090b41e | 639 | return 0; |
fa041593 | 640 | } |
61331eab | 641 | |
9fe6f5cc ZJS |
642 | r = efi_loader_get_device_part_uuid(NULL); |
643 | if (r == -ENOENT) { | |
b50a3a15 ZJS |
644 | log_notice("EFI loader partition unknown, exiting.\n" |
645 | "(The boot loader did not set EFI variable LoaderDevicePartUUID.)"); | |
8090b41e | 646 | return 0; |
9fe6f5cc ZJS |
647 | } else if (r < 0) |
648 | return log_error_errno(r, "Failed to read ESP partition UUID: %m"); | |
61331eab | 649 | |
235ae69c LP |
650 | /* OK, we have an ESP partition, this is fantastic, so let's wait for a root device to show up. A |
651 | * udev rule will create the link for us under the right name. */ | |
61331eab | 652 | |
9fe6f5cc ZJS |
653 | if (in_initrd()) { |
654 | r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root"); | |
655 | if (r < 0) | |
656 | return 0; | |
61331eab | 657 | |
9fe6f5cc ZJS |
658 | r = add_root_cryptsetup(); |
659 | if (r < 0) | |
660 | return r; | |
61331eab LP |
661 | } |
662 | ||
9fe6f5cc ZJS |
663 | /* Note that we do not need to enable systemd-remount-fs.service here. If |
664 | * /etc/fstab exists, systemd-fstab-generator will pull it in for us. */ | |
61331eab | 665 | |
9fe6f5cc ZJS |
666 | return add_mount( |
667 | "root", | |
668 | "/dev/gpt-auto-root", | |
669 | in_initrd() ? "/sysroot" : "/", | |
cf451f38 | 670 | arg_root_fstype, |
400c1e8f LP |
671 | /* rw= */ arg_root_rw > 0, |
672 | /* growfs= */ false, | |
ff386f98 | 673 | /* measure= */ true, |
cf451f38 | 674 | arg_root_options, |
9fe6f5cc ZJS |
675 | "Root Partition", |
676 | in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET); | |
677 | #else | |
678 | return 0; | |
679 | #endif | |
72e18a98 | 680 | } |
cb971249 | 681 | |
72e18a98 | 682 | static int enumerate_partitions(dev_t devnum) { |
72e18a98 | 683 | _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; |
55a065cd YW |
684 | _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL; |
685 | _cleanup_free_ char *devname = NULL; | |
72e18a98 | 686 | int r, k; |
61331eab | 687 | |
55a065cd YW |
688 | r = block_get_whole_disk(devnum, &devnum); |
689 | if (r < 0) | |
690 | return log_debug_errno(r, "Failed to get whole block device for " DEVNUM_FORMAT_STR ": %m", | |
691 | DEVNUM_FORMAT_VAL(devnum)); | |
692 | ||
693 | r = devname_from_devnum(S_IFBLK, devnum, &devname); | |
694 | if (r < 0) | |
695 | return log_debug_errno(r, "Failed to get device node of " DEVNUM_FORMAT_STR ": %m", | |
696 | DEVNUM_FORMAT_VAL(devnum)); | |
61331eab | 697 | |
41bc4849 LP |
698 | /* Let's take a LOCK_SH lock on the block device, in case udevd is already running. If we don't take |
699 | * the lock, udevd might end up issuing BLKRRPART in the middle, and we don't want that, since that | |
700 | * might remove all partitions while we are operating on them. */ | |
de3b7f16 | 701 | r = loop_device_open_from_path(devname, O_RDONLY, LOCK_SH, &loop); |
55a065cd | 702 | if (r < 0) |
af3d3873 | 703 | return log_debug_errno(r, "Failed to open %s: %m", devname); |
41bc4849 | 704 | |
55a065cd YW |
705 | r = dissect_loop_device( |
706 | loop, | |
d04faa4e LP |
707 | NULL, NULL, |
708 | DISSECT_IMAGE_GPT_ONLY| | |
8716a76c LP |
709 | DISSECT_IMAGE_USR_NO_ROOT| |
710 | DISSECT_IMAGE_DISKSEQ_DEVNODE, | |
73d88b80 LP |
711 | /* NB! Unlike most other places where we dissect block devices we do not use |
712 | * DISSECT_IMAGE_ADD_PARTITION_DEVICES here: we want that the kernel finds the | |
713 | * devices, and udev probes them before we mount them via .mount units much later | |
714 | * on. And thus we also don't set DISSECT_IMAGE_PIN_PARTITION_DEVICES here, because | |
715 | * we don't actually mount anything immediately. */ | |
d04faa4e | 716 | &m); |
72e18a98 | 717 | if (r == -ENOPKG) { |
2d935bbd | 718 | log_debug_errno(r, "No suitable partition table found on block device %s, ignoring.", devname); |
72e18a98 | 719 | return 0; |
61331eab | 720 | } |
23bbb0de | 721 | if (r < 0) |
2d935bbd | 722 | return log_error_errno(r, "Failed to dissect partition table of block device %s: %m", devname); |
0238d4c6 | 723 | |
72e18a98 | 724 | if (m->partitions[PARTITION_SWAP].found) { |
235ae69c | 725 | k = add_partition_swap(m->partitions + PARTITION_SWAP); |
72e18a98 LP |
726 | if (k < 0) |
727 | r = k; | |
1a14a53c LP |
728 | } |
729 | ||
9f1cb0c1 | 730 | if (m->partitions[PARTITION_XBOOTLDR].found) { |
235ae69c | 731 | k = add_partition_xbootldr(m->partitions + PARTITION_XBOOTLDR); |
9f1cb0c1 LP |
732 | if (k < 0) |
733 | r = k; | |
734 | } | |
735 | ||
72e18a98 | 736 | if (m->partitions[PARTITION_ESP].found) { |
235ae69c | 737 | k = add_partition_esp(m->partitions + PARTITION_ESP, m->partitions[PARTITION_XBOOTLDR].found); |
59512f21 KS |
738 | if (k < 0) |
739 | r = k; | |
740 | } | |
741 | ||
72e18a98 LP |
742 | if (m->partitions[PARTITION_HOME].found) { |
743 | k = add_partition_mount(m->partitions + PARTITION_HOME, "home", "/home", "Home Partition"); | |
73b80ec2 LP |
744 | if (k < 0) |
745 | r = k; | |
746 | } | |
e48fdd84 | 747 | |
72e18a98 LP |
748 | if (m->partitions[PARTITION_SRV].found) { |
749 | k = add_partition_mount(m->partitions + PARTITION_SRV, "srv", "/srv", "Server Data Partition"); | |
73b80ec2 LP |
750 | if (k < 0) |
751 | r = k; | |
752 | } | |
1a14a53c | 753 | |
d4dffb85 LP |
754 | if (m->partitions[PARTITION_VAR].found) { |
755 | k = add_partition_mount(m->partitions + PARTITION_VAR, "var", "/var", "Variable Data Partition"); | |
756 | if (k < 0) | |
757 | r = k; | |
758 | } | |
759 | ||
760 | if (m->partitions[PARTITION_TMP].found) { | |
761 | k = add_partition_mount(m->partitions + PARTITION_TMP, "var-tmp", "/var/tmp", "Temporary Data Partition"); | |
762 | if (k < 0) | |
763 | r = k; | |
764 | } | |
765 | ||
fd89051e | 766 | if (m->partitions[PARTITION_ROOT].found) { |
235ae69c | 767 | k = add_partition_root_rw(m->partitions + PARTITION_ROOT); |
fd89051e LP |
768 | if (k < 0) |
769 | r = k; | |
770 | } | |
771 | ||
1a14a53c LP |
772 | return r; |
773 | } | |
774 | ||
9fe6f5cc | 775 | static int add_mounts(void) { |
b00651cf | 776 | _cleanup_free_ char *p = NULL; |
9fe6f5cc | 777 | int r; |
b00651cf | 778 | dev_t devno; |
9fe6f5cc | 779 | |
b00651cf KK |
780 | /* If the root mount has been replaced by some form of volatile file system (overlayfs), the |
781 | * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that | |
782 | * here. */ | |
783 | r = readlink_malloc("/run/systemd/volatile-root", &p); | |
784 | if (r == -ENOENT) { /* volatile-root not found */ | |
785 | r = get_block_device_harder("/", &devno); | |
67f0ac8c | 786 | if (r == -EUCLEAN) |
b00651cf | 787 | return btrfs_log_dev_root(LOG_ERR, r, "root file system"); |
9fe6f5cc | 788 | if (r < 0) |
b00651cf | 789 | return log_error_errno(r, "Failed to determine block device of root file system: %m"); |
d5cb053c | 790 | if (r == 0) { /* Not backed by a single block device. (Could be NFS or so, or could be multi-device RAID or so) */ |
b00651cf KK |
791 | r = get_block_device_harder("/usr", &devno); |
792 | if (r == -EUCLEAN) | |
793 | return btrfs_log_dev_root(LOG_ERR, r, "/usr"); | |
9fe6f5cc | 794 | if (r < 0) |
d5cb053c LP |
795 | return log_error_errno(r, "Failed to determine block device of /usr/ file system: %m"); |
796 | if (r == 0) { /* /usr/ not backed by single block device, either. */ | |
797 | log_debug("Neither root nor /usr/ file system are on a (single) block device."); | |
798 | return 0; | |
799 | } | |
9fe6f5cc | 800 | } |
b00651cf KK |
801 | } else if (r < 0) |
802 | return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m"); | |
803 | else { | |
804 | mode_t m; | |
805 | r = device_path_parse_major_minor(p, &m, &devno); | |
806 | if (r < 0) | |
807 | return log_error_errno(r, "Failed to parse major/minor device node: %m"); | |
808 | if (!S_ISBLK(m)) | |
809 | return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type."); | |
9fe6f5cc ZJS |
810 | } |
811 | ||
812 | return enumerate_partitions(devno); | |
813 | } | |
814 | ||
96287a49 | 815 | static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { |
73b80ec2 | 816 | int r; |
1a14a53c | 817 | |
73b80ec2 | 818 | assert(key); |
1a14a53c | 819 | |
8a9c44ed LP |
820 | if (proc_cmdline_key_streq(key, "systemd.gpt_auto") || |
821 | proc_cmdline_key_streq(key, "rd.systemd.gpt_auto")) { | |
1a14a53c | 822 | |
1d84ad94 | 823 | r = value ? parse_boolean(value) : 1; |
73b80ec2 | 824 | if (r < 0) |
0a1b9449 | 825 | log_warning_errno(r, "Failed to parse gpt-auto switch \"%s\", ignoring: %m", value); |
8086ffac ZJS |
826 | else |
827 | arg_enabled = r; | |
1a14a53c | 828 | |
8a9c44ed | 829 | } else if (proc_cmdline_key_streq(key, "root")) { |
1d84ad94 LP |
830 | |
831 | if (proc_cmdline_value_missing(key, value)) | |
832 | return 0; | |
73b80ec2 LP |
833 | |
834 | /* Disable root disk logic if there's a root= value | |
835 | * specified (unless it happens to be "gpt-auto") */ | |
836 | ||
074cdb95 ZJS |
837 | if (!streq(value, "gpt-auto")) { |
838 | arg_root_enabled = false; | |
839 | log_debug("Disabling root partition auto-detection, root= is defined."); | |
840 | } | |
73b80ec2 | 841 | |
8a9c44ed | 842 | } else if (proc_cmdline_key_streq(key, "roothash")) { |
2f3dfc6f LP |
843 | |
844 | if (proc_cmdline_value_missing(key, value)) | |
845 | return 0; | |
846 | ||
847 | /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */ | |
848 | ||
849 | arg_root_enabled = false; | |
850 | ||
cf451f38 LP |
851 | } else if (streq(key, "rootfstype")) { |
852 | ||
853 | if (proc_cmdline_value_missing(key, value)) | |
854 | return 0; | |
855 | ||
856 | return free_and_strdup_warn(&arg_root_fstype, value); | |
857 | ||
858 | } else if (streq(key, "rootflags")) { | |
859 | ||
860 | if (proc_cmdline_value_missing(key, value)) | |
861 | return 0; | |
862 | ||
863 | if (!strextend_with_separator(&arg_root_options, ",", value)) | |
864 | return log_oom(); | |
865 | ||
8a9c44ed | 866 | } else if (proc_cmdline_key_streq(key, "rw") && !value) |
73b80ec2 | 867 | arg_root_rw = true; |
8a9c44ed | 868 | else if (proc_cmdline_key_streq(key, "ro") && !value) |
73b80ec2 | 869 | arg_root_rw = false; |
73b80ec2 LP |
870 | |
871 | return 0; | |
872 | } | |
873 | ||
ec6e9597 | 874 | static int run(const char *dest, const char *dest_early, const char *dest_late) { |
8f50e86a | 875 | int r, k; |
73b80ec2 | 876 | |
ec6e9597 | 877 | assert_se(arg_dest = dest_late); |
73b80ec2 | 878 | |
75f86906 | 879 | if (detect_container() > 0) { |
73b80ec2 | 880 | log_debug("In a container, exiting."); |
ec6e9597 | 881 | return 0; |
1a14a53c | 882 | } |
3db604b9 | 883 | |
1d84ad94 | 884 | r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); |
b5884878 | 885 | if (r < 0) |
da927ba9 | 886 | log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); |
1a14a53c | 887 | |
73b80ec2 LP |
888 | if (!arg_enabled) { |
889 | log_debug("Disabled, exiting."); | |
ec6e9597 | 890 | return 0; |
73b80ec2 LP |
891 | } |
892 | ||
893 | if (arg_root_enabled) | |
894 | r = add_root_mount(); | |
895 | ||
896 | if (!in_initrd()) { | |
73b80ec2 | 897 | k = add_mounts(); |
ec6e9597 | 898 | if (r >= 0) |
73b80ec2 LP |
899 | r = k; |
900 | } | |
901 | ||
ec6e9597 | 902 | return r; |
1a14a53c | 903 | } |
ec6e9597 ZJS |
904 | |
905 | DEFINE_MAIN_GENERATOR_FUNCTION(run); |