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