]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/umount.c
libblkid: make XFS Log visible for wipefs
[thirdparty/util-linux.git] / sys-utils / umount.c
CommitLineData
db216e68
KZ
1/*
2 * umount(8) -- mount a filesystem
3 *
4 * Copyright (C) 2011 Red Hat, Inc. All rights reserved.
5 * Written by Karel Zak <kzak@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
7cebf0bb
SK
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
db216e68
KZ
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <errno.h>
25#include <string.h>
26#include <getopt.h>
27#include <unistd.h>
28#include <sys/types.h>
29
30#include <libmount.h>
31
32#include "nls.h"
33#include "c.h"
34#include "env.h"
35#include "optutils.h"
73f9a114 36#include "exitcodes.h"
efb8854f 37#include "closestream.h"
13ee1c91 38#include "pathnames.h"
cc8cc8f3 39#include "canonicalize.h"
4eb49f63 40#include "xalloc.h"
db216e68
KZ
41
42static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
43 const char *filename, int line)
44{
45 if (filename)
46 warnx(_("%s: parse error: ignore entry at line %d."),
47 filename, line);
48 return 0;
49}
50
ac8ecab4 51
db216e68
KZ
52static void __attribute__((__noreturn__)) print_version(void)
53{
54 const char *ver = NULL;
ac8ecab4 55 const char **features = NULL, **p;
db216e68
KZ
56
57 mnt_get_library_version(&ver);
ac8ecab4 58 mnt_get_library_features(&features);
db216e68 59
ac8ecab4
KZ
60 printf(_("%s from %s (libmount %s"),
61 program_invocation_short_name,
62 PACKAGE_STRING,
63 ver);
64 p = features;
65 while (p && *p) {
66 fputs(p == features ? ": " : ", ", stdout);
67 fputs(*p++, stdout);
68 }
69 fputs(")\n", stdout);
73f9a114 70 exit(MOUNT_EX_SUCCESS);
db216e68 71}
db216e68
KZ
72static void __attribute__((__noreturn__)) usage(FILE *out)
73{
74 fputs(USAGE_HEADER, out);
75 fprintf(out, _(
76 " %1$s [-hV]\n"
77 " %1$s -a [options]\n"
78 " %1$s [options] <source> | <directory>\n"),
79 program_invocation_short_name);
80
451dbcfa
BS
81 fputs(USAGE_SEPARATOR, out);
82 fputs(_("Unmount filesystems.\n"), out);
83
db216e68 84 fputs(USAGE_OPTIONS, out);
4eb49f63 85 fputs(_(" -a, --all unmount all filesystems\n"), out);
8356d27d 86 fputs(_(" -A, --all-targets unmount all mountpoints for the given device in the\n"
4ce393f4 87 " current namespace\n"), out);
83d91100
SK
88 fputs(_(" -c, --no-canonicalize don't canonicalize paths\n"), out);
89 fputs(_(" -d, --detach-loop if mounted loop device, also free this loop device\n"), out);
90 fputs(_(" --fake dry run; skip the umount(2) syscall\n"), out);
91 fputs(_(" -f, --force force unmount (in case of an unreachable NFS system)\n"), out);
92 fputs(_(" -i, --internal-only don't call the umount.<type> helpers\n"), out);
93 fputs(_(" -n, --no-mtab don't write to /etc/mtab\n"), out);
4ce393f4 94 fputs(_(" -l, --lazy detach the filesystem now, clean up things later\n"), out);
83d91100
SK
95 fputs(_(" -O, --test-opts <list> limit the set of filesystems (use with -a)\n"), out);
96 fputs(_(" -R, --recursive recursively unmount a target with all its children\n"), out);
4ce393f4 97 fputs(_(" -r, --read-only in case unmounting fails, try to remount read-only\n"), out);
83d91100
SK
98 fputs(_(" -t, --types <list> limit the set of filesystem types\n"), out);
99 fputs(_(" -v, --verbose say what is being done\n"), out);
db216e68
KZ
100
101 fputs(USAGE_SEPARATOR, out);
102 fputs(USAGE_HELP, out);
103 fputs(USAGE_VERSION, out);
104 fprintf(out, USAGE_MAN_TAIL("umount(8)"));
105
73f9a114 106 exit(out == stderr ? MOUNT_EX_USAGE : MOUNT_EX_SUCCESS);
db216e68
KZ
107}
108
109static void __attribute__((__noreturn__)) exit_non_root(const char *option)
110{
111 const uid_t ruid = getuid();
112 const uid_t euid = geteuid();
113
114 if (ruid == 0 && euid != 0) {
115 /* user is root, but setuid to non-root */
116 if (option)
73f9a114 117 errx(MOUNT_EX_USAGE,
db216e68
KZ
118 _("only root can use \"--%s\" option "
119 "(effective UID is %u)"),
120 option, euid);
73f9a114 121 errx(MOUNT_EX_USAGE, _("only root can do that "
db216e68
KZ
122 "(effective UID is %u)"), euid);
123 }
124 if (option)
73f9a114
KZ
125 errx(MOUNT_EX_USAGE, _("only root can use \"--%s\" option"), option);
126 errx(MOUNT_EX_USAGE, _("only root can do that"));
127}
128
84600ddc
KZ
129static void success_message(struct libmnt_context *cxt)
130{
131 const char *tgt, *src;
132
133 if (mnt_context_helper_executed(cxt)
134 || mnt_context_get_status(cxt) != 1)
135 return;
136
137 tgt = mnt_context_get_target(cxt);
138 if (!tgt)
139 return;
140
141 src = mnt_context_get_source(cxt);
142 if (src)
143 warnx(_("%s (%s) unmounted"), tgt, src);
144 else
145 warnx(_("%s unmounted"), tgt);
146}
147
73f9a114
KZ
148/*
149 * Handles generic errors like ENOMEM, ...
150 *
151 * rc = 0 success
152 * <0 error (usually -errno)
153 *
154 * Returns exit status (MOUNT_EX_*) and prints error message.
155 */
156static int handle_generic_errors(int rc, const char *msg, ...)
157{
158 va_list va;
159
160 va_start(va, msg);
161 errno = -rc;
162
163 switch(errno) {
164 case EINVAL:
165 case EPERM:
166 vwarn(msg, va);
167 rc = MOUNT_EX_USAGE;
168 break;
169 case ENOMEM:
170 vwarn(msg, va);
171 rc = MOUNT_EX_SYSERR;
172 break;
173 default:
174 vwarn(msg, va);
175 rc = MOUNT_EX_FAIL;
176 break;
177 }
178 va_end(va);
179 return rc;
180}
181
182static int mk_exit_code(struct libmnt_context *cxt, int rc)
183{
184 int syserr;
185 const char *tgt = mnt_context_get_target(cxt);
186
187 if (mnt_context_helper_executed(cxt))
188 /*
189 * /sbin/umount.<type> called, return status
190 */
191 return mnt_context_get_helper_status(cxt);
192
193 if (rc == 0 && mnt_context_get_status(cxt) == 1)
194 /*
195 * Libmount success && syscall success.
196 */
197 return MOUNT_EX_SUCCESS;
198
199
200 if (!mnt_context_syscall_called(cxt)) {
201 /*
202 * libmount errors (extra library checks)
203 */
726f9fbf
KZ
204 if (rc == -EPERM && !mnt_context_tab_applied(cxt)) {
205 /* failed to evaluate permissions because not found
206 * relevant entry in mtab */
207 warnx(_("%s: not mounted"), tgt);
208 return MOUNT_EX_USAGE;
209 }
73f9a114
KZ
210 return handle_generic_errors(rc, _("%s: umount failed"), tgt);
211
212 } else if (mnt_context_get_syscall_errno(cxt) == 0) {
213 /*
214 * umount(2) syscall success, but something else failed
215 * (probably error in mtab processing).
216 */
217 if (rc < 0)
218 return handle_generic_errors(rc,
4ce393f4 219 _("%s: filesystem was unmounted, but mount(8) failed"),
73f9a114
KZ
220 tgt);
221
222 return MOUNT_EX_SOFTWARE; /* internal error */
223
224 }
225
226 /*
227 * umount(2) errors
228 */
229 syserr = mnt_context_get_syscall_errno(cxt);
230
231 switch(syserr) {
232 case ENXIO:
233 warnx(_("%s: invalid block device"), tgt); /* ??? */
234 break;
235 case EINVAL:
236 warnx(_("%s: not mounted"), tgt);
237 break;
238 case EIO:
239 warnx(_("%s: can't write superblock"), tgt);
240 break;
241 case EBUSY:
4ce393f4
BS
242 warnx(_("%s: target is busy\n"
243 " (In some cases useful info about processes that\n"
244 " use the device is found by lsof(8) or fuser(1).)"),
73f9a114 245 tgt);
7e1b1446 246 break;
73f9a114 247 case ENOENT:
7ba207e7
KZ
248 if (tgt && *tgt)
249 warnx(_("%s: mountpoint not found"), tgt);
250 else
251 warnx(_("undefined mountpoint"));
73f9a114
KZ
252 break;
253 case EPERM:
4ce393f4 254 warnx(_("%s: must be superuser to unmount"), tgt);
73f9a114
KZ
255 break;
256 case EACCES:
4ce393f4 257 warnx(_("%s: block devices are not permitted on filesystem"), tgt);
73f9a114
KZ
258 break;
259 default:
260 errno = syserr;
4ce393f4 261 warn("%s", tgt);
73f9a114
KZ
262 break;
263 }
264 return MOUNT_EX_FAIL;
db216e68
KZ
265}
266
190c342a 267static int umount_all(struct libmnt_context *cxt)
db216e68 268{
190c342a
KZ
269 struct libmnt_iter *itr;
270 struct libmnt_fs *fs;
271 int mntrc, ignored, rc = 0;
272
273 itr = mnt_new_iter(MNT_ITER_BACKWARD);
274 if (!itr) {
275 warn(_("failed to initialize libmount iterator"));
06069d5f 276 return MOUNT_EX_SYSERR;
190c342a
KZ
277 }
278
279 while (mnt_context_next_umount(cxt, itr, &fs, &mntrc, &ignored) == 0) {
280
281 const char *tgt = mnt_fs_get_target(fs);
282
283 if (ignored) {
284 if (mnt_context_is_verbose(cxt))
285 printf(_("%-25s: ignored\n"), tgt);
190c342a 286 } else {
0ce2fe87 287 int xrc = mk_exit_code(cxt, mntrc);
73f9a114 288
0ce2fe87
KZ
289 if (xrc == MOUNT_EX_SUCCESS
290 && mnt_context_is_verbose(cxt))
4ce393f4 291 printf("%-25s: successfully unmounted\n", tgt);
0ce2fe87 292 rc |= xrc;
190c342a
KZ
293 }
294 }
295
0f2d6476 296 mnt_free_iter(itr);
190c342a 297 return rc;
db216e68
KZ
298}
299
300static int umount_one(struct libmnt_context *cxt, const char *spec)
301{
302 int rc;
303
304 if (!spec)
06069d5f 305 return MOUNT_EX_SOFTWARE;
db216e68
KZ
306
307 if (mnt_context_set_target(cxt, spec))
73f9a114 308 err(MOUNT_EX_SYSERR, _("failed to set umount target"));
db216e68
KZ
309
310 rc = mnt_context_umount(cxt);
73f9a114 311 rc = mk_exit_code(cxt, rc);
db216e68 312
84600ddc
KZ
313 if (rc == MOUNT_EX_SUCCESS && mnt_context_is_verbose(cxt))
314 success_message(cxt);
315
db216e68
KZ
316 mnt_reset_context(cxt);
317 return rc;
318}
319
4eb49f63
KZ
320static struct libmnt_table *new_mountinfo(struct libmnt_context *cxt)
321{
322 struct libmnt_table *tb = mnt_new_table();
323 if (!tb)
324 err(MOUNT_EX_SYSERR, _("libmount table allocation failed"));
325
326 mnt_table_set_parser_errcb(tb, table_parser_errcb);
327 mnt_table_set_cache(tb, mnt_context_get_cache(cxt));
328
329 if (mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO)) {
330 warn(_("failed to parse %s"), _PATH_PROC_MOUNTINFO);
50fccba1 331 mnt_unref_table(tb);
4eb49f63
KZ
332 tb = NULL;
333 }
334
335 return tb;
336}
337
338/*
339 * like umount_one() but does not return error is @spec not mounted
340 */
341static int umount_one_if_mounted(struct libmnt_context *cxt, const char *spec)
342{
343 int rc;
344 struct libmnt_fs *fs;
345
346 rc = mnt_context_find_umount_fs(cxt, spec, &fs);
347 if (rc == 1) {
348 rc = MOUNT_EX_SUCCESS; /* alredy unmounted */
349 mnt_reset_context(cxt);
350 } else if (rc < 0) {
351 rc = mk_exit_code(cxt, rc); /* error */
352 mnt_reset_context(cxt);
353 } else
354 rc = umount_one(cxt, mnt_fs_get_target(fs));
355
356 return rc;
357}
358
13ee1c91 359static int umount_do_recurse(struct libmnt_context *cxt,
4eb49f63 360 struct libmnt_table *tb, struct libmnt_fs *fs)
13ee1c91 361{
13ee1c91 362 struct libmnt_fs *child;
13ee1c91 363 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
4eb49f63 364 int rc;
13ee1c91
DR
365
366 if (!itr)
367 err(MOUNT_EX_SYSERR, _("libmount iterator allocation failed"));
4eb49f63
KZ
368
369 /* umount all childern */
13ee1c91 370 for (;;) {
4eb49f63 371 rc = mnt_table_next_child_fs(tb, itr, fs, &child);
13ee1c91 372 if (rc < 0) {
4eb49f63
KZ
373 warnx(_("failed to get child fs of %s"),
374 mnt_fs_get_target(fs));
13ee1c91
DR
375 rc = MOUNT_EX_SOFTWARE;
376 goto done;
377 } else if (rc == 1)
378 break; /* no more children */
379
380 rc = umount_do_recurse(cxt, tb, child);
381 if (rc != MOUNT_EX_SUCCESS)
382 goto done;
383 }
384
4eb49f63 385 rc = umount_one_if_mounted(cxt, mnt_fs_get_target(fs));
13ee1c91
DR
386done:
387 mnt_free_iter(itr);
388 return rc;
389}
390
391static int umount_recursive(struct libmnt_context *cxt, const char *spec)
392{
393 struct libmnt_table *tb;
4eb49f63 394 struct libmnt_fs *fs;
13ee1c91
DR
395 int rc;
396
4eb49f63
KZ
397 tb = new_mountinfo(cxt);
398 if (!tb)
399 return MOUNT_EX_SOFTWARE;
400
ae978c4d
KZ
401 /* it's always real mountpoint, don't assume that the target maybe a device */
402 mnt_context_disable_swapmatch(cxt, 1);
403
4eb49f63
KZ
404 fs = mnt_table_find_target(tb, spec, MNT_ITER_BACKWARD);
405 if (fs)
406 rc = umount_do_recurse(cxt, tb, fs);
407 else {
408 rc = MOUNT_EX_USAGE;
409 warnx(access(spec, F_OK) == 0 ?
410 _("%s: not mounted") :
411 _("%s: not found"), spec);
412 }
a8cc72de 413
50fccba1 414 mnt_unref_table(tb);
4eb49f63
KZ
415 return rc;
416}
7b4a2697 417
4eb49f63
KZ
418static int umount_alltargets(struct libmnt_context *cxt, const char *spec, int rec)
419{
420 struct libmnt_fs *fs;
421 struct libmnt_table *tb;
422 struct libmnt_iter *itr = NULL;
f697d61b 423 dev_t devno = 0;
4eb49f63
KZ
424 int rc;
425
426 /* Convert @spec to device name, Use the same logic like regular
427 * "umount <spec>".
13ee1c91 428 */
4eb49f63
KZ
429 rc = mnt_context_find_umount_fs(cxt, spec, &fs);
430 if (rc == 1) {
431 rc = MOUNT_EX_USAGE;
432 warnx(access(spec, F_OK) == 0 ?
433 _("%s: not mounted") :
434 _("%s: not found"), spec);
435 return rc;
436 }
437 if (rc < 0)
438 return mk_exit_code(cxt, rc); /* error */
13ee1c91 439
f697d61b 440 if (!mnt_fs_get_srcpath(fs) || !mnt_fs_get_devno(fs))
fd7c4924
KZ
441 errx(MOUNT_EX_USAGE, _("%s: failed to determine source "
442 "(--all-targets is unsupported on systems with "
443 "regular mtab file)."), spec);
4eb49f63
KZ
444
445 itr = mnt_new_iter(MNT_ITER_BACKWARD);
446 if (!itr)
447 err(MOUNT_EX_SYSERR, _("libmount iterator allocation failed"));
448
449 /* get on @cxt independent mountinfo */
450 tb = new_mountinfo(cxt);
86c58c4a
KZ
451 if (!tb) {
452 rc = MOUNT_EX_SOFTWARE;
453 goto done;
454 }
4eb49f63
KZ
455
456 /* Note that @fs is from mount context and the context will be reseted
457 * after each umount() call */
f697d61b 458 devno = mnt_fs_get_devno(fs);
4eb49f63
KZ
459 fs = NULL;
460
461 mnt_reset_context(cxt);
462
463 while (mnt_table_next_fs(tb, itr, &fs) == 0) {
f697d61b 464 if (mnt_fs_get_devno(fs) != devno)
4eb49f63
KZ
465 continue;
466 mnt_context_disable_swapmatch(cxt, 1);
467 if (rec)
13ee1c91 468 rc = umount_do_recurse(cxt, tb, fs);
4eb49f63
KZ
469 else
470 rc = umount_one_if_mounted(cxt, mnt_fs_get_target(fs));
471
472 if (rc != MOUNT_EX_SUCCESS)
473 break;
13ee1c91
DR
474 }
475
86c58c4a 476done:
4eb49f63 477 mnt_free_iter(itr);
50fccba1 478 mnt_unref_table(tb);
4eb49f63 479
13ee1c91
DR
480 return rc;
481}
482
cc8cc8f3
KZ
483/*
484 * Check path -- non-root user should not be able to resolve path which is
485 * unreadable for him.
486 */
487static char *sanitize_path(const char *path)
488{
489 char *p;
490
491 if (!path)
492 return NULL;
493
494 p = canonicalize_path_restricted(path);
495 if (!p)
496 err(MOUNT_EX_USAGE, "%s", path);
497
498 return p;
499}
500
db216e68
KZ
501int main(int argc, char **argv)
502{
4eb49f63 503 int c, rc = 0, all = 0, recursive = 0, alltargets = 0;
db216e68
KZ
504 struct libmnt_context *cxt;
505 char *types = NULL;
506
507 enum {
508 UMOUNT_OPT_FAKE = CHAR_MAX + 1,
509 };
510
511 static const struct option longopts[] = {
512 { "all", 0, 0, 'a' },
4eb49f63 513 { "all-targets", 0, 0, 'A' },
db216e68
KZ
514 { "detach-loop", 0, 0, 'd' },
515 { "fake", 0, 0, UMOUNT_OPT_FAKE },
516 { "force", 0, 0, 'f' },
517 { "help", 0, 0, 'h' },
518 { "internal-only", 0, 0, 'i' },
519 { "lazy", 0, 0, 'l' },
520 { "no-canonicalize", 0, 0, 'c' },
521 { "no-mtab", 0, 0, 'n' },
522 { "read-only", 0, 0, 'r' },
13ee1c91 523 { "recursive", 0, 0, 'R' },
db216e68
KZ
524 { "test-opts", 1, 0, 'O' },
525 { "types", 1, 0, 't' },
526 { "verbose", 0, 0, 'v' },
527 { "version", 0, 0, 'V' },
528 { NULL, 0, 0, 0 }
529 };
530
a8cc72de 531 static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
4eb49f63 532 { 'A','a' }, /* all-targets,all */
a8cc72de
KZ
533 { 'R','a' }, /* recursive,all */
534 { 'O','R','t'}, /* options,recursive,types */
535 { 'R','r' }, /* recursive,read-only */
536 { 0 }
537 };
538 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
539
db216e68
KZ
540 sanitize_env();
541 setlocale(LC_ALL, "");
542 bindtextdomain(PACKAGE, LOCALEDIR);
543 textdomain(PACKAGE);
efb8854f 544 atexit(close_stdout);
db216e68
KZ
545
546 mnt_init_debug(0);
547 cxt = mnt_new_context();
548 if (!cxt)
73f9a114 549 err(MOUNT_EX_SYSERR, _("libmount context allocation failed"));
db216e68
KZ
550
551 mnt_context_set_tables_errcb(cxt, table_parser_errcb);
552
4eb49f63 553 while ((c = getopt_long(argc, argv, "aAcdfhilnRrO:t:vV",
db216e68
KZ
554 longopts, NULL)) != -1) {
555
556
557 /* only few options are allowed for non-root users */
558 if (mnt_context_is_restricted(cxt) && !strchr("hdilVv", c))
559 exit_non_root(option_to_longopt(c, longopts));
560
a8cc72de
KZ
561 err_exclusive_options(c, longopts, excl, excl_st);
562
db216e68
KZ
563 switch(c) {
564 case 'a':
565 all = 1;
566 break;
4eb49f63
KZ
567 case 'A':
568 alltargets = 1;
569 break;
db216e68
KZ
570 case 'c':
571 mnt_context_disable_canonicalize(cxt, TRUE);
572 break;
573 case 'd':
574 mnt_context_enable_loopdel(cxt, TRUE);
575 break;
576 case UMOUNT_OPT_FAKE:
577 mnt_context_enable_fake(cxt, TRUE);
578 break;
579 case 'f':
580 mnt_context_enable_force(cxt, TRUE);
581 break;
582 case 'h':
583 usage(stdout);
584 break;
585 case 'i':
586 mnt_context_disable_helpers(cxt, TRUE);
587 break;
588 case 'l':
589 mnt_context_enable_lazy(cxt, TRUE);
590 break;
591 case 'n':
592 mnt_context_disable_mtab(cxt, TRUE);
593 break;
594 case 'r':
595 mnt_context_enable_rdonly_umount(cxt, TRUE);
596 break;
13ee1c91
DR
597 case 'R':
598 recursive = TRUE;
599 break;
db216e68
KZ
600 case 'O':
601 if (mnt_context_set_options_pattern(cxt, optarg))
73f9a114 602 err(MOUNT_EX_SYSERR, _("failed to set options pattern"));
db216e68
KZ
603 break;
604 case 't':
605 types = optarg;
606 break;
607 case 'v':
608 mnt_context_enable_verbose(cxt, TRUE);
609 break;
610 case 'V':
611 print_version();
612 break;
613 default:
614 usage(stderr);
615 break;
616 }
617 }
618
619 argc -= optind;
620 argv += optind;
621
622 if (all) {
623 if (!types)
624 types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd";
625
626 mnt_context_set_fstype_pattern(cxt, types);
627 rc = umount_all(cxt);
628
629 } else if (argc < 1) {
630 usage(stderr);
631
4eb49f63
KZ
632 } else if (alltargets) {
633 while (argc--)
634 rc += umount_alltargets(cxt, *argv++, recursive);
13ee1c91
DR
635 } else if (recursive) {
636 while (argc--)
637 rc += umount_recursive(cxt, *argv++);
638 } else {
cc8cc8f3 639 while (argc--) {
d41acf74 640 char *path = *argv;
cc8cc8f3 641
d41acf74
KZ
642 if (mnt_context_is_restricted(cxt)
643 && !mnt_tag_is_valid(path))
cc8cc8f3
KZ
644 path = sanitize_path(path);
645
646 rc += umount_one(cxt, path);
647
d41acf74 648 if (path != *argv)
cc8cc8f3 649 free(path);
d41acf74 650 argv++;
cc8cc8f3 651 }
13ee1c91 652 }
db216e68
KZ
653
654 mnt_free_context(cxt);
a9add961 655 return (rc < 256) ? rc : 255;
db216e68
KZ
656}
657