]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/mount/mount-tool.c
util-lib: share plymouth client code
[thirdparty/systemd.git] / src / mount / mount-tool.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
450442cf
LP
2
3#include <getopt.h>
4
450442cf 5#include "sd-bus.h"
dcd26523 6#include "sd-device.h"
450442cf 7
d6b4d1c7 8#include "build.h"
450442cf 9#include "bus-error.h"
9b71e4ab 10#include "bus-locator.h"
450442cf 11#include "bus-unit-util.h"
e45c81b8 12#include "bus-wait-for-jobs.h"
f461a28d 13#include "chase.h"
8437c059 14#include "device-util.h"
6f6165bf 15#include "dirent-util.h"
450442cf 16#include "escape.h"
9017f5d8 17#include "fd-util.h"
6f6165bf 18#include "fileio.h"
6ae6ea55 19#include "format-table.h"
ca78ad1d 20#include "format-util.h"
f4938c2e 21#include "fs-util.h"
450442cf 22#include "fstab-util.h"
7d991d48 23#include "libmount-util.h"
f2fae6fb 24#include "main-func.h"
e2be442e 25#include "mount-util.h"
049af8ad 26#include "mountpoint-util.h"
450442cf 27#include "pager.h"
599c7c54 28#include "parse-argument.h"
450442cf
LP
29#include "parse-util.h"
30#include "path-util.h"
294bf0c3 31#include "pretty-print.h"
2306d177 32#include "process-util.h"
760877e9 33#include "sort-util.h"
450442cf 34#include "spawn-polkit-agent.h"
6f6165bf 35#include "stat-util.h"
450442cf 36#include "strv.h"
760877e9 37#include "terminal-util.h"
9269296d 38#include "umask-util.h"
89ada3ba 39#include "unit-def.h"
450442cf 40#include "unit-name.h"
e09fc884 41#include "user-util.h"
450442cf
LP
42
43enum {
44 ACTION_DEFAULT,
45 ACTION_MOUNT,
46 ACTION_AUTOMOUNT,
c37fb55b 47 ACTION_UMOUNT,
450442cf
LP
48 ACTION_LIST,
49} arg_action = ACTION_DEFAULT;
50
51static bool arg_no_block = false;
0221d68a 52static PagerFlags arg_pager_flags = 0;
a5279634 53static bool arg_legend = true;
a362c069 54static bool arg_full = false;
450442cf
LP
55static bool arg_ask_password = true;
56static bool arg_quiet = false;
57static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
4870133b 58static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
450442cf
LP
59static const char *arg_host = NULL;
60static bool arg_discover = false;
61static char *arg_mount_what = NULL;
62static char *arg_mount_where = NULL;
63static char *arg_mount_type = NULL;
64static char *arg_mount_options = NULL;
65static char *arg_description = NULL;
66static char **arg_property = NULL;
67static usec_t arg_timeout_idle = USEC_INFINITY;
68static bool arg_timeout_idle_set = false;
69static char **arg_automount_property = NULL;
70static int arg_bind_device = -1;
e09fc884
ZJS
71static uid_t arg_uid = UID_INVALID;
72static gid_t arg_gid = GID_INVALID;
450442cf 73static bool arg_fsck = true;
dc336483 74static bool arg_aggressive_gc = false;
2322c6c7 75static bool arg_tmpfs = false;
450442cf 76
f2fae6fb
YW
77STATIC_DESTRUCTOR_REGISTER(arg_mount_what, freep);
78STATIC_DESTRUCTOR_REGISTER(arg_mount_where, freep);
79STATIC_DESTRUCTOR_REGISTER(arg_mount_type, freep);
80STATIC_DESTRUCTOR_REGISTER(arg_mount_options, freep);
81STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
82STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
83STATIC_DESTRUCTOR_REGISTER(arg_automount_property, strv_freep);
84
2322c6c7 85static int parse_where(const char *input, char **ret_where) {
2322c6c7
LB
86 int r;
87
88 assert(input);
89 assert(ret_where);
90
91 if (arg_transport == BUS_TRANSPORT_LOCAL) {
660087dc 92 r = chase(input, NULL, CHASE_NONEXISTENT, ret_where, NULL);
2322c6c7
LB
93 if (r < 0)
94 return log_error_errno(r, "Failed to make path %s absolute: %m", input);
95 } else {
660087dc 96 if (!path_is_absolute(input))
2322c6c7
LB
97 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
98 "Path must be absolute when operating remotely: %s",
660087dc
ZJS
99 input);
100
101 r = path_simplify_alloc(input, ret_where);
102 if (r < 0)
103 return log_error_errno(r, "Failed to simplify path %s: %m", input);
2322c6c7
LB
104 }
105
2322c6c7
LB
106 return 0;
107}
108
37ec0fdd
LP
109static int help(void) {
110 _cleanup_free_ char *link = NULL;
111 int r;
112
113 r = terminal_urlify_man("systemd-mount", "1", &link);
114 if (r < 0)
115 return log_oom();
116
9017f5d8 117 printf("systemd-mount [OPTIONS...] WHAT [WHERE]\n"
2322c6c7 118 "systemd-mount [OPTIONS...] --tmpfs [NAME] WHERE\n"
9017f5d8
YW
119 "systemd-mount [OPTIONS...] --list\n"
120 "%s [OPTIONS...] %sWHAT|WHERE...\n\n"
450442cf
LP
121 "Establish a mount or auto-mount point transiently.\n\n"
122 " -h --help Show this help\n"
123 " --version Show package version\n"
124 " --no-block Do not wait until operation finished\n"
125 " --no-pager Do not pipe output into a pager\n"
a5279634 126 " --no-legend Do not show the headers\n"
a362c069 127 " -l --full Do not ellipsize output\n"
450442cf
LP
128 " --no-ask-password Do not prompt for password\n"
129 " -q --quiet Suppress information messages during runtime\n"
130 " --user Run as user unit\n"
131 " -H --host=[USER@]HOST Operate on remote host\n"
132 " -M --machine=CONTAINER Operate on local container\n"
133 " --discover Discover mount device metadata\n"
134 " -t --type=TYPE File system type\n"
135 " -o --options=OPTIONS Mount options\n"
e09fc884 136 " --owner=USER Add uid= and gid= options for USER\n"
450442cf
LP
137 " --fsck=no Don't run file system check before mount\n"
138 " --description=TEXT Description for unit\n"
139 " -p --property=NAME=VALUE Set mount unit property\n"
140 " -A --automount=BOOL Create an auto-mount point\n"
141 " --timeout-idle-sec=SEC Specify automount idle timeout\n"
142 " --automount-property=NAME=VALUE\n"
143 " Set automount unit property\n"
144 " --bind-device Bind automount unit to device\n"
145 " --list List mountable block devices\n"
dc336483 146 " -u --umount Unmount mount points\n"
37ec0fdd 147 " -G --collect Unload unit after it stopped, even when failed\n"
2322c6c7 148 " -T --tmpfs Create a new tmpfs on the mount point\n"
bc556335
DDM
149 "\nSee the %s for details.\n",
150 program_invocation_short_name,
151 streq(program_invocation_short_name, "systemd-umount") ? "" : "--umount ",
152 link);
37ec0fdd
LP
153
154 return 0;
450442cf
LP
155}
156
157static int parse_argv(int argc, char *argv[]) {
158
159 enum {
160 ARG_VERSION = 0x100,
161 ARG_NO_BLOCK,
162 ARG_NO_PAGER,
a5279634 163 ARG_NO_LEGEND,
450442cf
LP
164 ARG_NO_ASK_PASSWORD,
165 ARG_USER,
166 ARG_SYSTEM,
167 ARG_DISCOVER,
168 ARG_MOUNT_TYPE,
169 ARG_MOUNT_OPTIONS,
e09fc884 170 ARG_OWNER,
450442cf
LP
171 ARG_FSCK,
172 ARG_DESCRIPTION,
173 ARG_TIMEOUT_IDLE,
174 ARG_AUTOMOUNT,
175 ARG_AUTOMOUNT_PROPERTY,
176 ARG_BIND_DEVICE,
177 ARG_LIST,
178 };
179
180 static const struct option options[] = {
181 { "help", no_argument, NULL, 'h' },
182 { "version", no_argument, NULL, ARG_VERSION },
183 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
184 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
a5279634 185 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
a362c069 186 { "full", no_argument, NULL, 'l' },
450442cf
LP
187 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
188 { "quiet", no_argument, NULL, 'q' },
189 { "user", no_argument, NULL, ARG_USER },
190 { "system", no_argument, NULL, ARG_SYSTEM },
191 { "host", required_argument, NULL, 'H' },
192 { "machine", required_argument, NULL, 'M' },
193 { "discover", no_argument, NULL, ARG_DISCOVER },
194 { "type", required_argument, NULL, 't' },
195 { "options", required_argument, NULL, 'o' },
e09fc884 196 { "owner", required_argument, NULL, ARG_OWNER },
0f923832 197 { "fsck", required_argument, NULL, ARG_FSCK },
450442cf
LP
198 { "description", required_argument, NULL, ARG_DESCRIPTION },
199 { "property", required_argument, NULL, 'p' },
200 { "automount", required_argument, NULL, ARG_AUTOMOUNT },
201 { "timeout-idle-sec", required_argument, NULL, ARG_TIMEOUT_IDLE },
202 { "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY },
203 { "bind-device", no_argument, NULL, ARG_BIND_DEVICE },
204 { "list", no_argument, NULL, ARG_LIST },
c37fb55b 205 { "umount", no_argument, NULL, 'u' },
52a1e91e 206 { "unmount", no_argument, NULL, 'u' }, /* Compat spelling */
dc336483 207 { "collect", no_argument, NULL, 'G' },
2322c6c7 208 { "tmpfs", no_argument, NULL, 'T' },
450442cf
LP
209 {},
210 };
211
212 int r, c;
213
214 assert(argc >= 0);
215 assert(argv);
216
2306d177
LP
217 if (invoked_as(argv, "systemd-umount"))
218 arg_action = ACTION_UMOUNT;
c37fb55b 219
2322c6c7 220 while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:AuGlT", options, NULL)) >= 0)
450442cf
LP
221
222 switch (c) {
223
224 case 'h':
37ec0fdd 225 return help();
450442cf
LP
226
227 case ARG_VERSION:
228 return version();
229
230 case ARG_NO_BLOCK:
231 arg_no_block = true;
232 break;
233
234 case ARG_NO_PAGER:
0221d68a 235 arg_pager_flags |= PAGER_DISABLE;
450442cf
LP
236 break;
237
a5279634
YW
238 case ARG_NO_LEGEND:
239 arg_legend = false;
240 break;
241
a362c069
YW
242 case 'l':
243 arg_full = true;
244 break;
245
450442cf
LP
246 case ARG_NO_ASK_PASSWORD:
247 arg_ask_password = false;
248 break;
249
250 case 'q':
251 arg_quiet = true;
252 break;
253
254 case ARG_USER:
4870133b 255 arg_runtime_scope = RUNTIME_SCOPE_USER;
450442cf
LP
256 break;
257
258 case ARG_SYSTEM:
4870133b 259 arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
450442cf
LP
260 break;
261
262 case 'H':
263 arg_transport = BUS_TRANSPORT_REMOTE;
264 arg_host = optarg;
265 break;
266
267 case 'M':
268 arg_transport = BUS_TRANSPORT_MACHINE;
269 arg_host = optarg;
270 break;
271
272 case ARG_DISCOVER:
273 arg_discover = true;
274 break;
275
276 case 't':
b3f9c17a
YW
277 r = free_and_strdup_warn(&arg_mount_type, optarg);
278 if (r < 0)
279 return r;
450442cf
LP
280 break;
281
282 case 'o':
b3f9c17a
YW
283 r = free_and_strdup_warn(&arg_mount_options, optarg);
284 if (r < 0)
285 return r;
450442cf
LP
286 break;
287
e09fc884
ZJS
288 case ARG_OWNER: {
289 const char *user = optarg;
290
fafff8f1 291 r = get_user_creds(&user, &arg_uid, &arg_gid, NULL, NULL, 0);
e09fc884
ZJS
292 if (r < 0)
293 return log_error_errno(r,
294 r == -EBADMSG ? "UID or GID of user %s are invalid."
295 : "Cannot use \"%s\" as owner: %m",
296 optarg);
297 break;
298 }
299
450442cf 300 case ARG_FSCK:
599c7c54 301 r = parse_boolean_argument("--fsck=", optarg, &arg_fsck);
450442cf 302 if (r < 0)
599c7c54 303 return r;
450442cf
LP
304 break;
305
306 case ARG_DESCRIPTION:
b3f9c17a
YW
307 r = free_and_strdup_warn(&arg_description, optarg);
308 if (r < 0)
309 return r;
450442cf
LP
310 break;
311
312 case 'p':
313 if (strv_extend(&arg_property, optarg) < 0)
314 return log_oom();
315
316 break;
317
318 case 'A':
319 arg_action = ACTION_AUTOMOUNT;
320 break;
321
322 case ARG_AUTOMOUNT:
c3470872 323 r = parse_boolean_argument("--automount=", optarg, NULL);
450442cf 324 if (r < 0)
c3470872 325 return r;
450442cf
LP
326
327 arg_action = r ? ACTION_AUTOMOUNT : ACTION_MOUNT;
328 break;
329
330 case ARG_TIMEOUT_IDLE:
331 r = parse_sec(optarg, &arg_timeout_idle);
332 if (r < 0)
333 return log_error_errno(r, "Failed to parse timeout: %s", optarg);
334
335 break;
336
337 case ARG_AUTOMOUNT_PROPERTY:
338 if (strv_extend(&arg_automount_property, optarg) < 0)
339 return log_oom();
340
341 break;
342
343 case ARG_BIND_DEVICE:
344 arg_bind_device = true;
345 break;
346
347 case ARG_LIST:
348 arg_action = ACTION_LIST;
349 break;
350
c37fb55b
LR
351 case 'u':
352 arg_action = ACTION_UMOUNT;
353 break;
354
dc336483
YW
355 case 'G':
356 arg_aggressive_gc = true;
357 break;
358
2322c6c7
LB
359 case 'T':
360 arg_tmpfs = true;
361 break;
362
450442cf
LP
363 case '?':
364 return -EINVAL;
365
366 default:
04499a70 367 assert_not_reached();
450442cf
LP
368 }
369
4870133b 370 if (arg_runtime_scope == RUNTIME_SCOPE_USER) {
966f3a24
LP
371 arg_ask_password = false;
372
4870133b
LP
373 if (arg_transport != BUS_TRANSPORT_LOCAL)
374 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
375 "Execution in user context is not supported on non-local systems.");
376 }
450442cf
LP
377
378 if (arg_action == ACTION_LIST) {
baaa35ad
ZJS
379 if (optind < argc)
380 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
381 "Too many arguments.");
450442cf 382
baaa35ad
ZJS
383 if (arg_transport != BUS_TRANSPORT_LOCAL)
384 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
385 "Listing devices only supported locally.");
9017f5d8 386 } else if (arg_action == ACTION_UMOUNT) {
baaa35ad
ZJS
387 if (optind >= argc)
388 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
389 "At least one argument required.");
9017f5d8 390
857127f1
ZJS
391 if (arg_transport != BUS_TRANSPORT_LOCAL)
392 for (int i = optind; i < argc; i++)
393 if (!path_is_absolute(argv[i]))
baaa35ad 394 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2322c6c7
LB
395 "Path must be absolute when operating remotely: %s",
396 argv[i]);
450442cf 397 } else {
baaa35ad
ZJS
398 if (optind >= argc)
399 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
400 "At least one argument required.");
450442cf 401
baaa35ad
ZJS
402 if (argc > optind+2)
403 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
52a1e91e 404 "More than two arguments are not allowed.");
450442cf 405
2322c6c7
LB
406 if (arg_tmpfs) {
407 if (argc <= optind+1) {
408 arg_mount_what = strdup("tmpfs");
409 if (!arg_mount_what)
410 return log_oom();
e2be442e 411
2322c6c7
LB
412 r = parse_where(argv[optind], &arg_mount_where);
413 if (r < 0)
414 return r;
415 } else {
416 arg_mount_what = strdup(argv[optind]);
417 if (!arg_mount_what)
418 return log_oom();
419 }
450442cf 420
2322c6c7
LB
421 if (!strv_contains(arg_property, "Type=tmpfs") &&
422 strv_extend(&arg_property, "Type=tmpfs") < 0)
afde5b16 423 return log_oom();
afde5b16 424 } else {
2322c6c7
LB
425 if (arg_mount_type && !fstype_is_blockdev_backed(arg_mount_type)) {
426 arg_mount_what = strdup(argv[optind]);
427 if (!arg_mount_what)
428 return log_oom();
afde5b16 429
2322c6c7
LB
430 } else if (arg_transport == BUS_TRANSPORT_LOCAL) {
431 _cleanup_free_ char *u = NULL;
afde5b16 432
2322c6c7
LB
433 u = fstab_node_to_udev_node(argv[optind]);
434 if (!u)
435 return log_oom();
afde5b16 436
2322c6c7 437 r = chase(u, NULL, 0, &arg_mount_what, NULL);
afde5b16 438 if (r < 0)
2322c6c7 439 return log_error_errno(r, "Failed to make path %s absolute: %m", u);
afde5b16 440
660087dc
ZJS
441 } else {
442 if (!path_is_absolute(argv[optind]))
baaa35ad 443 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2322c6c7 444 "Path must be absolute when operating remotely: %s",
660087dc
ZJS
445 argv[optind]);
446
447 r = path_simplify_alloc(argv[optind], &arg_mount_what);
448 if (r < 0)
449 return log_error_errno(r, "Failed to simplify path: %m");
afde5b16 450 }
2322c6c7
LB
451 }
452
453 if (argc > optind+1) {
454 r = parse_where(argv[optind+1], &arg_mount_where);
455 if (r < 0)
456 return r;
457 } else if (!arg_tmpfs)
450442cf
LP
458 arg_discover = true;
459
baaa35ad
ZJS
460 if (arg_discover && arg_transport != BUS_TRANSPORT_LOCAL)
461 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
462 "Automatic mount location discovery is only supported locally.");
450442cf
LP
463 }
464
465 return 1;
466}
467
89ada3ba 468static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
450442cf
LP
469 int r;
470
05b4d3b5
AK
471 if (!isempty(arg_description)) {
472 r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description);
473 if (r < 0)
474 return r;
475 }
450442cf
LP
476
477 if (arg_bind_device && is_device_path(arg_mount_what)) {
478 _cleanup_free_ char *device_unit = NULL;
479
480 r = unit_name_from_path(arg_mount_what, ".device", &device_unit);
481 if (r < 0)
482 return r;
483
484 r = sd_bus_message_append(m, "(sv)(sv)",
485 "After", "as", 1, device_unit,
486 "BindsTo", "as", 1, device_unit);
487 if (r < 0)
488 return r;
489 }
490
dc336483
YW
491 if (arg_aggressive_gc) {
492 r = sd_bus_message_append(m, "(sv)", "CollectMode", "s", "inactive-or-failed");
493 if (r < 0)
494 return r;
495 }
496
89ada3ba 497 r = bus_append_unit_property_assignment_many(m, t, properties);
450442cf
LP
498 if (r < 0)
499 return r;
500
501 return 0;
502}
503
504static int transient_mount_set_properties(sd_bus_message *m) {
505 int r;
506
507 assert(m);
508
89ada3ba 509 r = transient_unit_set_properties(m, UNIT_MOUNT, arg_property);
450442cf
LP
510 if (r < 0)
511 return r;
512
513 if (arg_mount_what) {
514 r = sd_bus_message_append(m, "(sv)", "What", "s", arg_mount_what);
515 if (r < 0)
516 return r;
517 }
518
519 if (arg_mount_type) {
520 r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_mount_type);
521 if (r < 0)
522 return r;
523 }
524
9269296d
ZJS
525 _cleanup_free_ char *options = NULL;
526
e09fc884
ZJS
527 /* Prepend uid=…,gid=… if arg_uid is set */
528 if (arg_uid != UID_INVALID) {
9269296d
ZJS
529 r = strextendf_with_separator(&options, ",",
530 "uid="UID_FMT",gid="GID_FMT, arg_uid, arg_gid);
450442cf 531 if (r < 0)
9269296d 532 return r;
450442cf
LP
533 }
534
9269296d
ZJS
535 /* Override the default for tmpfs mounts. The kernel sets the sticky bit on the root directory by
536 * default. This makes sense for the case when the user does 'mount -t tmpfs tmpfs /tmp', but less so
537 * for other directories.
538 *
539 * Let's also set some reasonable limits. We use the current umask, to match what a command to create
540 * directory would use, e.g. mkdir. */
541 if (arg_tmpfs) {
542 mode_t mask;
543
544 r = get_process_umask(0, &mask);
545 if (r < 0)
546 return r;
547
548 assert((mask & ~0777) == 0);
549 r = strextendf_with_separator(&options, ",",
550 "mode=0%o,nodev,nosuid%s", 0777 & ~mask, NESTED_TMPFS_LIMITS);
551 if (r < 0)
552 return r;
553 }
554
555 if (arg_mount_options)
556 if (!strextend_with_separator(&options, ",", arg_mount_options))
82007efa 557 return -ENOMEM;
e09fc884 558
9269296d
ZJS
559 if (options) {
560 log_debug("Using mount options: %s", options);
561 r = sd_bus_message_append(m, "(sv)", "Options", "s", options);
e09fc884
ZJS
562 if (r < 0)
563 return r;
564 } else
565 log_debug("Not using any mount options");
566
450442cf
LP
567 if (arg_fsck) {
568 _cleanup_free_ char *fsck = NULL;
569
570 r = unit_name_from_path_instance("systemd-fsck", arg_mount_what, ".service", &fsck);
571 if (r < 0)
572 return r;
573
574 r = sd_bus_message_append(m,
575 "(sv)(sv)",
576 "Requires", "as", 1, fsck,
577 "After", "as", 1, fsck);
578 if (r < 0)
579 return r;
580 }
581
582 return 0;
583}
584
585static int transient_automount_set_properties(sd_bus_message *m) {
586 int r;
587
588 assert(m);
589
89ada3ba 590 r = transient_unit_set_properties(m, UNIT_AUTOMOUNT, arg_automount_property);
450442cf
LP
591 if (r < 0)
592 return r;
593
594 if (arg_timeout_idle != USEC_INFINITY) {
595 r = sd_bus_message_append(m, "(sv)", "TimeoutIdleUSec", "t", arg_timeout_idle);
596 if (r < 0)
597 return r;
598 }
599
600 return 0;
601}
602
603static int start_transient_mount(
604 sd_bus *bus,
605 char **argv) {
606
607 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
608 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
609 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
610 _cleanup_free_ char *mount_unit = NULL;
611 int r;
612
613 if (!arg_no_block) {
614 r = bus_wait_for_jobs_new(bus, &w);
615 if (r < 0)
616 return log_error_errno(r, "Could not watch jobs: %m");
617 }
618
619 r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
620 if (r < 0)
621 return log_error_errno(r, "Failed to make mount unit name: %m");
622
92cb8ebc 623 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
450442cf
LP
624 if (r < 0)
625 return bus_log_create_error(r);
626
627 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
628 if (r < 0)
629 return bus_log_create_error(r);
630
631 /* Name and mode */
632 r = sd_bus_message_append(m, "ss", mount_unit, "fail");
633 if (r < 0)
634 return bus_log_create_error(r);
635
636 /* Properties */
637 r = sd_bus_message_open_container(m, 'a', "(sv)");
638 if (r < 0)
639 return bus_log_create_error(r);
640
641 r = transient_mount_set_properties(m);
642 if (r < 0)
643 return bus_log_create_error(r);
644
645 r = sd_bus_message_close_container(m);
646 if (r < 0)
647 return bus_log_create_error(r);
648
649 /* Auxiliary units */
650 r = sd_bus_message_append(m, "a(sa(sv))", 0);
651 if (r < 0)
652 return bus_log_create_error(r);
653
8a4b13c5 654 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
450442cf
LP
655
656 r = sd_bus_call(bus, m, 0, &error, &reply);
657 if (r < 0)
658 return log_error_errno(r, "Failed to start transient mount unit: %s", bus_error_message(&error, r));
659
660 if (w) {
661 const char *object;
662
663 r = sd_bus_message_read(reply, "o", &object);
664 if (r < 0)
665 return bus_log_parse_error(r);
666
86980de6 667 r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
450442cf
LP
668 if (r < 0)
669 return r;
670 }
671
672 if (!arg_quiet)
673 log_info("Started unit %s%s%s for mount point: %s%s%s",
674 ansi_highlight(), mount_unit, ansi_normal(),
675 ansi_highlight(), arg_mount_where, ansi_normal());
676
677 return 0;
678}
679
680static int start_transient_automount(
681 sd_bus *bus,
682 char **argv) {
683
684 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
685 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
686 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
687 _cleanup_free_ char *automount_unit = NULL, *mount_unit = NULL;
688 int r;
689
690 if (!arg_no_block) {
691 r = bus_wait_for_jobs_new(bus, &w);
692 if (r < 0)
693 return log_error_errno(r, "Could not watch jobs: %m");
694 }
695
696 r = unit_name_from_path(arg_mount_where, ".automount", &automount_unit);
697 if (r < 0)
698 return log_error_errno(r, "Failed to make automount unit name: %m");
699
700 r = unit_name_from_path(arg_mount_where, ".mount", &mount_unit);
701 if (r < 0)
702 return log_error_errno(r, "Failed to make mount unit name: %m");
703
92cb8ebc 704 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
450442cf
LP
705 if (r < 0)
706 return bus_log_create_error(r);
707
708 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
709 if (r < 0)
710 return bus_log_create_error(r);
711
712 /* Name and mode */
713 r = sd_bus_message_append(m, "ss", automount_unit, "fail");
714 if (r < 0)
715 return bus_log_create_error(r);
716
717 /* Properties */
718 r = sd_bus_message_open_container(m, 'a', "(sv)");
719 if (r < 0)
720 return bus_log_create_error(r);
721
722 r = transient_automount_set_properties(m);
723 if (r < 0)
724 return bus_log_create_error(r);
725
726 r = sd_bus_message_close_container(m);
727 if (r < 0)
728 return bus_log_create_error(r);
729
730 /* Auxiliary units */
731 r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
732 if (r < 0)
733 return bus_log_create_error(r);
734
735 r = sd_bus_message_open_container(m, 'r', "sa(sv)");
736 if (r < 0)
737 return bus_log_create_error(r);
738
739 r = sd_bus_message_append(m, "s", mount_unit);
740 if (r < 0)
741 return bus_log_create_error(r);
742
743 r = sd_bus_message_open_container(m, 'a', "(sv)");
744 if (r < 0)
745 return bus_log_create_error(r);
746
747 r = transient_mount_set_properties(m);
748 if (r < 0)
749 return bus_log_create_error(r);
750
751 r = sd_bus_message_close_container(m);
752 if (r < 0)
753 return bus_log_create_error(r);
754
755 r = sd_bus_message_close_container(m);
756 if (r < 0)
757 return bus_log_create_error(r);
758
759 r = sd_bus_message_close_container(m);
760 if (r < 0)
761 return bus_log_create_error(r);
762
8a4b13c5 763 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
450442cf
LP
764
765 r = sd_bus_call(bus, m, 0, &error, &reply);
766 if (r < 0)
767 return log_error_errno(r, "Failed to start transient automount unit: %s", bus_error_message(&error, r));
768
769 if (w) {
770 const char *object;
771
772 r = sd_bus_message_read(reply, "o", &object);
773 if (r < 0)
774 return bus_log_parse_error(r);
775
86980de6 776 r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
450442cf
LP
777 if (r < 0)
778 return r;
779 }
780
781 if (!arg_quiet)
782 log_info("Started unit %s%s%s for mount point: %s%s%s",
783 ansi_highlight(), automount_unit, ansi_normal(),
784 ansi_highlight(), arg_mount_where, ansi_normal());
785
786 return 0;
787}
788
f0aac575 789static int find_mount_points(const char *what, char ***list) {
7d991d48
ZJS
790 _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
791 _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
f0aac575 792 _cleanup_strv_free_ char **l = NULL;
319a4f4b 793 size_t n = 0;
7d991d48 794 int r;
f0aac575
YW
795
796 assert(what);
797 assert(list);
798
799 /* Returns all mount points obtained from /proc/self/mountinfo in *list,
800 * and the number of mount points as return value. */
801
e2857b3d 802 r = libmount_parse(NULL, NULL, &table, &iter);
7d991d48
ZJS
803 if (r < 0)
804 return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m");
f0aac575
YW
805
806 for (;;) {
7d991d48
ZJS
807 struct libmnt_fs *fs;
808 const char *source, *target;
f0aac575 809
7d991d48
ZJS
810 r = mnt_table_next_fs(table, iter, &fs);
811 if (r == 1)
812 break;
813 if (r < 0)
814 return log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
f0aac575 815
7d991d48
ZJS
816 source = mnt_fs_get_source(fs);
817 target = mnt_fs_get_target(fs);
818 if (!source || !target)
f0aac575
YW
819 continue;
820
7d991d48 821 if (!path_equal(source, what))
f0aac575
YW
822 continue;
823
824 /* one extra slot is needed for the terminating NULL */
319a4f4b 825 if (!GREEDY_REALLOC0(l, n + 2))
f0aac575
YW
826 return log_oom();
827
7d991d48
ZJS
828 l[n] = strdup(target);
829 if (!l[n])
830 return log_oom();
831 n++;
f0aac575
YW
832 }
833
319a4f4b 834 if (!GREEDY_REALLOC0(l, n + 1))
290843c3
LP
835 return log_oom();
836
ae2a15bc 837 *list = TAKE_PTR(l);
f0aac575
YW
838 return n;
839}
840
1480c231
YW
841static int find_loop_device(const char *backing_file, sd_device **ret) {
842 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
1480c231 843 int r;
f0aac575
YW
844
845 assert(backing_file);
1480c231 846 assert(ret);
f0aac575 847
1480c231
YW
848 r = sd_device_enumerator_new(&e);
849 if (r < 0)
850 return log_oom();
f0aac575 851
1480c231
YW
852 r = sd_device_enumerator_add_match_subsystem(e, "block", /* match = */ true);
853 if (r < 0)
854 return log_error_errno(r, "Failed to add subsystem match: %m");
f0aac575 855
1480c231
YW
856 r = sd_device_enumerator_add_match_property(e, "ID_FS_USAGE", "filesystem");
857 if (r < 0)
858 return log_error_errno(r, "Failed to add property match: %m");
f0aac575 859
1480c231
YW
860 r = sd_device_enumerator_add_match_sysname(e, "loop*");
861 if (r < 0)
862 return log_error_errno(r, "Failed to add sysname match: %m");
f0aac575 863
1480c231
YW
864 r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", /* value = */ NULL, /* match = */ true);
865 if (r < 0)
866 return log_error_errno(r, "Failed to add sysattr match: %m");
867
868 FOREACH_DEVICE(e, dev) {
869 const char *s;
f0aac575 870
1480c231 871 r = sd_device_get_sysattr_value(dev, "loop/backing_file", &s);
a53dceb7 872 if (r < 0) {
1480c231 873 log_device_debug_errno(dev, r, "Failed to read \"loop/backing_file\" sysattr, ignoring: %m");
f0aac575 874 continue;
a53dceb7 875 }
f0aac575 876
563e6846 877 if (inode_same(s, backing_file, 0) <= 0)
f0aac575
YW
878 continue;
879
1480c231
YW
880 *ret = sd_device_ref(dev);
881 return 0;
f0aac575
YW
882 }
883
1480c231 884 return -ENXIO;
f0aac575
YW
885}
886
c37fb55b
LR
887static int stop_mount(
888 sd_bus *bus,
9017f5d8 889 const char *where,
6f6165bf 890 const char *suffix) {
c37fb55b
LR
891
892 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
893 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
894 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
895 _cleanup_free_ char *mount_unit = NULL;
896 int r;
897
898 if (!arg_no_block) {
899 r = bus_wait_for_jobs_new(bus, &w);
900 if (r < 0)
901 return log_error_errno(r, "Could not watch jobs: %m");
902 }
903
9017f5d8 904 r = unit_name_from_path(where, suffix, &mount_unit);
c37fb55b 905 if (r < 0)
6442c210 906 return log_error_errno(r, "Failed to make %s unit name from path %s: %m", suffix + 1, where);
c37fb55b 907
92cb8ebc 908 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StopUnit");
c37fb55b
LR
909 if (r < 0)
910 return bus_log_create_error(r);
911
912 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
913 if (r < 0)
914 return bus_log_create_error(r);
915
916 /* Name and mode */
917 r = sd_bus_message_append(m, "ss", mount_unit, "fail");
918 if (r < 0)
919 return bus_log_create_error(r);
920
8a4b13c5 921 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
c37fb55b
LR
922
923 r = sd_bus_call(bus, m, 0, &error, &reply);
6442c210
YW
924 if (r < 0) {
925 if (streq(suffix, ".automount") &&
926 sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit"))
927 return 0;
928 return log_error_errno(r, "Failed to stop %s unit: %s", suffix + 1, bus_error_message(&error, r));
929 }
c37fb55b
LR
930
931 if (w) {
932 const char *object;
933
934 r = sd_bus_message_read(reply, "o", &object);
935 if (r < 0)
936 return bus_log_parse_error(r);
937
86980de6 938 r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
c37fb55b
LR
939 if (r < 0)
940 return r;
941 }
942
943 if (!arg_quiet)
944 log_info("Stopped unit %s%s%s for mount point: %s%s%s",
945 ansi_highlight(), mount_unit, ansi_normal(),
9017f5d8 946 ansi_highlight(), where, ansi_normal());
c37fb55b
LR
947
948 return 0;
949}
950
951static int stop_mounts(
952 sd_bus *bus,
6f6165bf 953 const char *where) {
c37fb55b
LR
954
955 int r;
956
baaa35ad
ZJS
957 if (path_equal(where, "/"))
958 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
959 "Refusing to operate on root directory: %s", where);
9017f5d8 960
baaa35ad
ZJS
961 if (!path_is_normalized(where))
962 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
963 "Path contains non-normalized components: %s", where);
9017f5d8 964
6f6165bf 965 r = stop_mount(bus, where, ".mount");
c37fb55b
LR
966 if (r < 0)
967 return r;
968
6f6165bf 969 r = stop_mount(bus, where, ".automount");
c37fb55b
LR
970 if (r < 0)
971 return r;
972
973 return 0;
974}
975
961d08ca 976static int umount_by_device(sd_bus *bus, sd_device *dev) {
dcd26523 977 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
f0aac575 978 _cleanup_strv_free_ char **list = NULL;
9017f5d8 979 const char *v;
961d08ca 980 int r, ret = 0;
9017f5d8 981
961d08ca
YW
982 assert(bus);
983 assert(dev);
9017f5d8 984
961d08ca
YW
985 if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE", &v) >= 0)
986 ret = stop_mounts(bus, v);
9017f5d8 987
961d08ca 988 r = sd_device_get_devname(dev, &v);
dcd26523 989 if (r < 0)
961d08ca 990 return r;
9017f5d8 991
961d08ca 992 r = find_mount_points(v, &list);
f0aac575
YW
993 if (r < 0)
994 return r;
995
961d08ca 996 STRV_FOREACH(l, list) {
f0aac575 997 r = stop_mounts(bus, *l);
6f6165bf 998 if (r < 0)
961d08ca 999 ret = r;
9017f5d8
YW
1000 }
1001
961d08ca
YW
1002 return ret;
1003}
1004
1005static int umount_by_device_node(sd_bus *bus, const char *node) {
1006 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
1007 const char *v;
1008 int r;
1009
1010 assert(bus);
1011 assert(node);
1012
1013 r = sd_device_new_from_devname(&dev, node);
1014 if (r < 0)
1015 return log_error_errno(r, "Failed to get device from %s: %m", node);
1016
1017 r = sd_device_get_property_value(dev, "ID_FS_USAGE", &v);
1018 if (r < 0)
1019 return log_device_error_errno(dev, r, "Failed to get \"ID_FS_USAGE\" device property: %m");
1020
1021 if (!streq(v, "filesystem"))
1022 return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL),
1023 "%s does not contain a known file system.", node);
1024
1025 return umount_by_device(bus, dev);
6f6165bf
YW
1026}
1027
1028static int umount_loop(sd_bus *bus, const char *backing_file) {
1480c231 1029 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
f0aac575 1030 int r;
6f6165bf
YW
1031
1032 assert(backing_file);
1033
1480c231 1034 r = find_loop_device(backing_file, &dev);
f0aac575 1035 if (r < 0)
aa46fa64 1036 return log_error_errno(r, r == -ENXIO ? "File %s is not mounted." : "Can't get loop device for %s: %m", backing_file);
6f6165bf 1037
1480c231 1038 return umount_by_device(bus, dev);
9017f5d8
YW
1039}
1040
1041static int action_umount(
1042 sd_bus *bus,
1043 int argc,
1044 char **argv) {
1045
857127f1 1046 int r, r2 = 0;
9017f5d8 1047
3747daa2 1048 if (arg_transport != BUS_TRANSPORT_LOCAL) {
857127f1 1049 for (int i = optind; i < argc; i++) {
3747daa2
YW
1050 _cleanup_free_ char *p = NULL;
1051
660087dc
ZJS
1052 r = path_simplify_alloc(argv[i], &p);
1053 if (r < 0)
1054 return r;
3747daa2
YW
1055
1056 r = stop_mounts(bus, p);
1057 if (r < 0)
1058 r2 = r;
1059 }
1060 return r2;
1061 }
1062
857127f1 1063 for (int i = optind; i < argc; i++) {
5bc9f949 1064 _cleanup_free_ char *u = NULL, *p = NULL;
6f6165bf 1065 struct stat st;
9017f5d8
YW
1066
1067 u = fstab_node_to_udev_node(argv[i]);
1068 if (!u)
1069 return log_oom();
9017f5d8 1070
f461a28d 1071 r = chase(u, NULL, 0, &p, NULL);
9017f5d8 1072 if (r < 0) {
02a8bd65 1073 r2 = log_error_errno(r, "Failed to make path %s absolute: %m", argv[i]);
9017f5d8
YW
1074 continue;
1075 }
1076
6f6165bf 1077 if (stat(p, &st) < 0)
02a8bd65 1078 return log_error_errno(errno, "Can't stat %s (from %s): %m", p, argv[i]);
9017f5d8 1079
6f6165bf 1080 if (S_ISBLK(st.st_mode))
961d08ca 1081 r = umount_by_device_node(bus, p);
6f6165bf
YW
1082 else if (S_ISREG(st.st_mode))
1083 r = umount_loop(bus, p);
1084 else if (S_ISDIR(st.st_mode))
1085 r = stop_mounts(bus, p);
1086 else {
02a8bd65 1087 log_error("Invalid file type: %s (from %s)", p, argv[i]);
6f6165bf
YW
1088 r = -EINVAL;
1089 }
1090
1091 if (r < 0)
9017f5d8
YW
1092 r2 = r;
1093 }
1094
1095 return r2;
1096}
1097
dcd26523 1098static int acquire_mount_type(sd_device *d) {
450442cf
LP
1099 const char *v;
1100
1101 assert(d);
1102
1103 if (arg_mount_type)
1104 return 0;
1105
dcd26523 1106 if (sd_device_get_property_value(d, "ID_FS_TYPE", &v) < 0)
450442cf
LP
1107 return 0;
1108
1109 arg_mount_type = strdup(v);
1110 if (!arg_mount_type)
1111 return log_oom();
1112
1113 log_debug("Discovered type=%s", arg_mount_type);
1114 return 1;
1115}
1116
dcd26523 1117static int acquire_mount_options(sd_device *d) {
450442cf
LP
1118 const char *v;
1119
dcd26523
YW
1120 assert(d);
1121
450442cf
LP
1122 if (arg_mount_options)
1123 return 0;
1124
dcd26523 1125 if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_OPTIONS", &v) < 0)
450442cf
LP
1126 return 0;
1127
1128 arg_mount_options = strdup(v);
1129 if (!arg_mount_options)
1130 return log_oom();
1131
1132 log_debug("Discovered options=%s", arg_mount_options);
1133 return 1;
1134}
1135
dcd26523 1136static const char *get_model(sd_device *d) {
450442cf
LP
1137 const char *model;
1138
1139 assert(d);
1140
dcd26523 1141 if (sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model) >= 0)
450442cf
LP
1142 return model;
1143
dcd26523
YW
1144 if (sd_device_get_property_value(d, "ID_MODEL", &model) >= 0)
1145 return model;
1146
1147 return NULL;
450442cf
LP
1148}
1149
dcd26523 1150static const char* get_label(sd_device *d) {
450442cf
LP
1151 const char *label;
1152
1153 assert(d);
1154
dcd26523
YW
1155 if (sd_device_get_property_value(d, "ID_FS_LABEL", &label) >= 0)
1156 return label;
1157
1158 if (sd_device_get_property_value(d, "ID_PART_ENTRY_NAME", &label) >= 0)
450442cf
LP
1159 return label;
1160
dcd26523 1161 return NULL;
450442cf
LP
1162}
1163
dcd26523 1164static int acquire_mount_where(sd_device *d) {
450442cf 1165 const char *v;
451f0dba 1166 int r;
450442cf
LP
1167
1168 if (arg_mount_where)
1169 return 0;
1170
dcd26523 1171 if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE", &v) < 0) {
451f0dba 1172 _cleanup_free_ char *escaped = NULL, *devname_bn = NULL;
450442cf
LP
1173 const char *name;
1174
1175 name = get_label(d);
1176 if (!name)
1177 name = get_model(d);
1178 if (!name) {
1179 const char *dn;
1180
dcd26523 1181 if (sd_device_get_devname(d, &dn) < 0)
450442cf
LP
1182 return 0;
1183
451f0dba
LP
1184 r = path_extract_filename(dn, &devname_bn);
1185 if (r < 0)
1186 return log_error_errno(r, "Failed to extract file name from '%s': %m", dn);
1187
1188 name = devname_bn;
450442cf
LP
1189 }
1190
1191 escaped = xescape(name, "\\");
f0aac575
YW
1192 if (!escaped)
1193 return log_oom();
450442cf
LP
1194 if (!filename_is_valid(escaped))
1195 return 0;
1196
657ee2d8 1197 arg_mount_where = path_join("/run/media/system", escaped);
450442cf
LP
1198 } else
1199 arg_mount_where = strdup(v);
1200
1201 if (!arg_mount_where)
1202 return log_oom();
1203
1204 log_debug("Discovered where=%s", arg_mount_where);
1205 return 1;
1206}
1207
95a45a87 1208static int acquire_mount_where_for_loop_dev(sd_device *dev) {
f0aac575 1209 _cleanup_strv_free_ char **list = NULL;
95a45a87 1210 const char *node;
f0aac575
YW
1211 int r;
1212
95a45a87
YW
1213 assert(dev);
1214
f0aac575
YW
1215 if (arg_mount_where)
1216 return 0;
1217
95a45a87 1218 r = sd_device_get_devname(dev, &node);
f0aac575
YW
1219 if (r < 0)
1220 return r;
95a45a87
YW
1221
1222 r = find_mount_points(node, &list);
1223 if (r < 0)
1224 return r;
1225 if (r == 0)
1226 return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL),
1227 "Can't find mount point of %s. It is expected that %s is already mounted on a place.",
1228 node, node);
1229 if (r >= 2)
1230 return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL),
1231 "%s is mounted on %d places. It is expected that %s is mounted on a place.",
1232 node, r, node);
f0aac575
YW
1233
1234 arg_mount_where = strdup(list[0]);
1235 if (!arg_mount_where)
1236 return log_oom();
1237
1238 log_debug("Discovered where=%s", arg_mount_where);
1239 return 1;
1240}
1241
dcd26523 1242static int acquire_description(sd_device *d) {
450442cf
LP
1243 const char *model, *label;
1244
1245 if (arg_description)
1246 return 0;
1247
1248 model = get_model(d);
1249
1250 label = get_label(d);
1251 if (!label)
dcd26523 1252 (void) sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &label);
450442cf
LP
1253
1254 if (model && label)
605405c6 1255 arg_description = strjoin(model, " ", label);
450442cf
LP
1256 else if (label)
1257 arg_description = strdup(label);
1258 else if (model)
1259 arg_description = strdup(model);
1260 else
05b4d3b5 1261 return 0;
450442cf
LP
1262
1263 if (!arg_description)
1264 return log_oom();
1265
1266 log_debug("Discovered description=%s", arg_description);
1267 return 1;
1268}
1269
dcd26523 1270static int acquire_removable(sd_device *d) {
450442cf
LP
1271 const char *v;
1272
1273 /* Shortcut this if there's no reason to check it */
1274 if (arg_action != ACTION_DEFAULT && arg_timeout_idle_set && arg_bind_device >= 0)
1275 return 0;
1276
1277 for (;;) {
1f22fc38 1278 if (sd_device_get_sysattr_value(d, "removable", &v) >= 0)
450442cf
LP
1279 break;
1280
dcd26523 1281 if (sd_device_get_parent(d, &d) < 0)
450442cf
LP
1282 return 0;
1283
dcd26523 1284 if (sd_device_get_subsystem(d, &v) < 0 || !streq(v, "block"))
450442cf
LP
1285 return 0;
1286 }
1287
1288 if (parse_boolean(v) <= 0)
1289 return 0;
1290
1291 log_debug("Discovered removable device.");
1292
1293 if (arg_action == ACTION_DEFAULT) {
1294 log_debug("Automatically turning on automount.");
1295 arg_action = ACTION_AUTOMOUNT;
1296 }
1297
1298 if (!arg_timeout_idle_set) {
1299 log_debug("Setting idle timeout to 1s.");
1300 arg_timeout_idle = USEC_PER_SEC;
1301 }
1302
1303 if (arg_bind_device < 0) {
1304 log_debug("Binding automount unit to device.");
1305 arg_bind_device = true;
1306 }
1307
1308 return 1;
1309}
1310
f0aac575 1311static int discover_loop_backing_file(void) {
dcd26523 1312 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
450442cf
LP
1313 int r;
1314
1480c231 1315 r = find_loop_device(arg_mount_what, &d);
aa46fa64 1316 if (r < 0 && r != -ENXIO)
f0aac575
YW
1317 return log_error_errno(errno, "Can't get loop device for %s: %m", arg_mount_what);
1318
aa46fa64 1319 if (r == -ENXIO) {
451f0dba 1320 _cleanup_free_ char *escaped = NULL, *bn = NULL;
f0aac575
YW
1321
1322 if (arg_mount_where)
1323 return 0;
1324
451f0dba
LP
1325 r = path_extract_filename(arg_mount_what, &bn);
1326 if (r < 0)
1327 return log_error_errno(r, "Failed to extract file name from backing file path '%s': %m", arg_mount_what);
1328
1329 escaped = xescape(bn, "\\");
f0aac575
YW
1330 if (!escaped)
1331 return log_oom();
d7a0f1f4
FS
1332 if (!filename_is_valid(escaped))
1333 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1334 "Escaped name %s is not a valid filename.",
1335 escaped);
f0aac575 1336
657ee2d8 1337 arg_mount_where = path_join("/run/media/system", escaped);
f0aac575
YW
1338 if (!arg_mount_where)
1339 return log_oom();
1340
1341 log_debug("Discovered where=%s", arg_mount_where);
450442cf 1342 return 0;
f0aac575
YW
1343 }
1344
f0aac575
YW
1345 r = acquire_mount_type(d);
1346 if (r < 0)
1347 return r;
1348
1349 r = acquire_mount_options(d);
1350 if (r < 0)
1351 return r;
1352
95a45a87 1353 r = acquire_mount_where_for_loop_dev(d);
f0aac575
YW
1354 if (r < 0)
1355 return r;
1356
1357 r = acquire_description(d);
1358 if (r < 0)
1359 return r;
1360
1361 return 0;
1362}
1363
1364static int discover_device(void) {
dcd26523 1365 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
f0aac575
YW
1366 struct stat st;
1367 const char *v;
1368 int r;
1369
450442cf
LP
1370 if (stat(arg_mount_what, &st) < 0)
1371 return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what);
1372
f0aac575
YW
1373 if (S_ISREG(st.st_mode))
1374 return discover_loop_backing_file();
1375
d7a0f1f4
FS
1376 if (!S_ISBLK(st.st_mode))
1377 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1378 "Invalid file type: %s",
1379 arg_mount_what);
450442cf 1380
930aa88f 1381 r = sd_device_new_from_stat_rdev(&d, &st);
dcd26523
YW
1382 if (r < 0)
1383 return log_error_errno(r, "Failed to get device from device number: %m");
450442cf 1384
d7a0f1f4
FS
1385 if (sd_device_get_property_value(d, "ID_FS_USAGE", &v) < 0 || !streq(v, "filesystem"))
1386 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1387 "%s does not contain a known file system.",
1388 arg_mount_what);
450442cf
LP
1389
1390 r = acquire_mount_type(d);
1391 if (r < 0)
1392 return r;
1393
1394 r = acquire_mount_options(d);
1395 if (r < 0)
1396 return r;
1397
1398 r = acquire_mount_where(d);
1399 if (r < 0)
1400 return r;
1401
1402 r = acquire_description(d);
1403 if (r < 0)
1404 return r;
1405
1406 r = acquire_removable(d);
1407 if (r < 0)
1408 return r;
1409
1410 return 0;
1411}
1412
1413enum {
1414 COLUMN_NODE,
1415 COLUMN_PATH,
1416 COLUMN_MODEL,
1417 COLUMN_WWN,
1418 COLUMN_FSTYPE,
1419 COLUMN_LABEL,
1420 COLUMN_UUID,
1421 _COLUMN_MAX,
1422};
1423
450442cf 1424static int list_devices(void) {
dcd26523 1425 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
6ae6ea55 1426 _cleanup_(table_unrefp) Table *table = NULL;
450442cf
LP
1427 int r;
1428
dcd26523
YW
1429 r = sd_device_enumerator_new(&e);
1430 if (r < 0)
450442cf
LP
1431 return log_oom();
1432
dcd26523 1433 r = sd_device_enumerator_add_match_subsystem(e, "block", true);
450442cf
LP
1434 if (r < 0)
1435 return log_error_errno(r, "Failed to add block match: %m");
1436
dcd26523 1437 r = sd_device_enumerator_add_match_property(e, "ID_FS_USAGE", "filesystem");
450442cf
LP
1438 if (r < 0)
1439 return log_error_errno(r, "Failed to add property match: %m");
1440
6ae6ea55
YW
1441 table = table_new("NODE", "PATH", "MODEL", "WWN", "TYPE", "LABEL", "UUID");
1442 if (!table)
1443 return log_oom();
450442cf 1444
a362c069
YW
1445 if (arg_full)
1446 table_set_width(table, 0);
1447
ef1e0b9a 1448 r = table_set_sort(table, (size_t) 0);
6ae6ea55
YW
1449 if (r < 0)
1450 return log_error_errno(r, "Failed to set sort index: %m");
450442cf 1451
a5279634
YW
1452 table_set_header(table, arg_legend);
1453
6ae6ea55 1454 FOREACH_DEVICE(e, d) {
857127f1 1455 for (unsigned c = 0; c < _COLUMN_MAX; c++) {
6fd667e5 1456 const char *x = NULL;
450442cf
LP
1457
1458 switch (c) {
1459
1460 case COLUMN_NODE:
dcd26523 1461 (void) sd_device_get_devname(d, &x);
450442cf
LP
1462 break;
1463
1464 case COLUMN_PATH:
dcd26523 1465 (void) sd_device_get_property_value(d, "ID_PATH", &x);
450442cf
LP
1466 break;
1467
1468 case COLUMN_MODEL:
1469 x = get_model(d);
1470 break;
1471
1472 case COLUMN_WWN:
dcd26523 1473 (void) sd_device_get_property_value(d, "ID_WWN", &x);
450442cf
LP
1474 break;
1475
1476 case COLUMN_FSTYPE:
dcd26523 1477 (void) sd_device_get_property_value(d, "ID_FS_TYPE", &x);
450442cf
LP
1478 break;
1479
1480 case COLUMN_LABEL:
1481 x = get_label(d);
1482 break;
1483
1484 case COLUMN_UUID:
dcd26523 1485 (void) sd_device_get_property_value(d, "ID_FS_UUID", &x);
450442cf
LP
1486 break;
1487 }
1488
6ae6ea55
YW
1489 r = table_add_cell(table, NULL, c == COLUMN_NODE ? TABLE_PATH : TABLE_STRING, strna(x));
1490 if (r < 0)
bd17fa8c 1491 return table_log_add_error(r);
450442cf
LP
1492 }
1493 }
1494
384c2c32 1495 pager_open(arg_pager_flags);
450442cf 1496
6ae6ea55
YW
1497 r = table_print(table, NULL);
1498 if (r < 0)
4b6607d9 1499 return table_log_print_error(r);
450442cf 1500
6ae6ea55 1501 return 0;
450442cf
LP
1502}
1503
f2fae6fb
YW
1504static int run(int argc, char* argv[]) {
1505 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
450442cf
LP
1506 int r;
1507
1a043959 1508 log_show_color(true);
450442cf
LP
1509 log_parse_environment();
1510 log_open();
1511
1512 r = parse_argv(argc, argv);
1513 if (r <= 0)
f2fae6fb 1514 return r;
450442cf 1515
f2fae6fb
YW
1516 if (arg_action == ACTION_LIST)
1517 return list_devices();
450442cf 1518
4870133b 1519 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_runtime_scope, &bus);
f2fae6fb 1520 if (r < 0)
10a7340a 1521 return bus_log_connect_error(r, arg_transport);
9017f5d8 1522
f2fae6fb
YW
1523 if (arg_action == ACTION_UMOUNT)
1524 return action_umount(bus, argc, argv);
9017f5d8 1525
c15ab81e 1526 if ((!arg_mount_type || fstype_is_blockdev_backed(arg_mount_type))
d7a0f1f4
FS
1527 && !path_is_normalized(arg_mount_what))
1528 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1529 "Path contains non-normalized components: %s",
1530 arg_mount_what);
f0aac575
YW
1531
1532 if (arg_discover) {
1533 r = discover_device();
1534 if (r < 0)
f2fae6fb 1535 return r;
f0aac575
YW
1536 }
1537
d7a0f1f4
FS
1538 if (!arg_mount_where)
1539 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1540 "Can't figure out where to mount %s.",
1541 arg_mount_what);
450442cf 1542
d7a0f1f4
FS
1543 if (path_equal(arg_mount_where, "/"))
1544 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1545 "Refusing to operate on root directory.");
450442cf 1546
d7a0f1f4
FS
1547 if (!path_is_normalized(arg_mount_where))
1548 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1549 "Path contains non-normalized components: %s",
1550 arg_mount_where);
450442cf
LP
1551
1552 if (streq_ptr(arg_mount_type, "auto"))
1553 arg_mount_type = mfree(arg_mount_type);
1554 if (streq_ptr(arg_mount_options, "defaults"))
1555 arg_mount_options = mfree(arg_mount_options);
1556
1557 if (!is_device_path(arg_mount_what))
1558 arg_fsck = false;
1559
1560 if (arg_fsck && arg_mount_type && arg_transport == BUS_TRANSPORT_LOCAL) {
13556724 1561 r = fsck_exists_for_fstype(arg_mount_type);
450442cf
LP
1562 if (r < 0)
1563 log_warning_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", arg_mount_type);
1564 else if (r == 0) {
1565 log_debug("Disabling file system check as fsck for %s doesn't exist.", arg_mount_type);
1566 arg_fsck = false; /* fsck doesn't exist, let's not attempt it */
1567 }
1568 }
1569
e09fc884
ZJS
1570 /* The kernel (properly) refuses mounting file systems with unknown uid=,gid= options,
1571 * but not for all filesystem types. Let's try to catch the cases where the option
1572 * would be used if the file system does not support it. It is also possible to
1573 * autodetect the file system, but that's only possible with disk-based file systems
1574 * which incidentally seem to be implemented more carefully and reject unknown options,
1575 * so it's probably OK that we do the check only when the type is specified.
1576 */
1577 if (arg_mount_type &&
1578 !streq(arg_mount_type, "auto") &&
1579 arg_uid != UID_INVALID &&
d7a0f1f4
FS
1580 !fstype_can_uid_gid(arg_mount_type))
1581 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1582 "File system type %s is not known to support uid=/gid=, refusing.",
1583 arg_mount_type);
e09fc884 1584
450442cf
LP
1585 switch (arg_action) {
1586
1587 case ACTION_MOUNT:
1588 case ACTION_DEFAULT:
1589 r = start_transient_mount(bus, argv + optind);
1590 break;
1591
1592 case ACTION_AUTOMOUNT:
1593 r = start_transient_automount(bus, argv + optind);
1594 break;
1595
1596 default:
04499a70 1597 assert_not_reached();
450442cf
LP
1598 }
1599
f2fae6fb 1600 return r;
450442cf 1601}
f2fae6fb
YW
1602
1603DEFINE_MAIN_FUNCTION(run);