]>
Commit | Line | Data |
---|---|---|
2c37ca7c | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
ea8f06f9 | 2 | /* |
2c37ca7c | 3 | * This file is part of libmount from util-linux project. |
ea8f06f9 | 4 | * |
2c37ca7c KZ |
5 | * Copyright (C) 2010-2018 Karel Zak <kzak@redhat.com> |
6 | * | |
7 | * libmount is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU Lesser General Public License as published by | |
9 | * the Free Software Foundation; either version 2.1 of the License, or | |
10 | * (at your option) any later version. | |
ea8f06f9 KZ |
11 | */ |
12 | ||
63de90d4 KZ |
13 | /** |
14 | * SECTION: context-umount | |
15 | * @title: Umount context | |
16 | * @short_description: high-level API to umount operation. | |
17 | */ | |
18 | ||
ea8f06f9 KZ |
19 | #include <sys/wait.h> |
20 | #include <sys/mount.h> | |
21 | ||
ea8f06f9 | 22 | #include "pathnames.h" |
a10d6ad6 | 23 | #include "loopdev.h" |
ea8f06f9 KZ |
24 | #include "strutils.h" |
25 | #include "mountP.h" | |
26 | ||
701b48cf KZ |
27 | /* |
28 | * umount2 flags | |
29 | */ | |
30 | #ifndef MNT_FORCE | |
d58b3157 | 31 | # define MNT_FORCE 0x00000001 /* Attempt to forcibly umount */ |
db9bd703 KZ |
32 | #endif |
33 | ||
701b48cf KZ |
34 | #ifndef MNT_DETACH |
35 | # define MNT_DETACH 0x00000002 /* Just detach from the tree */ | |
36 | #endif | |
37 | ||
38 | #ifndef UMOUNT_NOFOLLOW | |
39 | # define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */ | |
40 | #endif | |
db9bd703 | 41 | |
701b48cf KZ |
42 | #ifndef UMOUNT_UNUSED |
43 | # define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */ | |
db9bd703 KZ |
44 | #endif |
45 | ||
a9717463 KZ |
46 | /* search in mountinfo/mtab */ |
47 | static int __mtab_find_umount_fs(struct libmnt_context *cxt, | |
48 | const char *tgt, | |
49 | struct libmnt_fs **pfs) | |
ea8f06f9 | 50 | { |
6506a866 | 51 | int rc; |
cddd2eaa | 52 | struct libmnt_ns *ns_old; |
309139c7 | 53 | struct libmnt_table *mtab = NULL; |
68164f6c | 54 | struct libmnt_fs *fs; |
7deae03f | 55 | char *loopdev = NULL; |
ea8f06f9 | 56 | |
a9717463 KZ |
57 | assert(cxt); |
58 | assert(tgt); | |
59 | assert(pfs); | |
4709c9e6 | 60 | |
a9717463 KZ |
61 | *pfs = NULL; |
62 | DBG(CXT, ul_debugobj(cxt, " search %s in mountinfo", tgt)); | |
7ba207e7 | 63 | |
4709c9e6 | 64 | /* |
f8416301 KZ |
65 | * The mount table may be huge, and on systems with utab we have to |
66 | * merge userspace mount options into /proc/self/mountinfo. This all is | |
67 | * expensive. The tab filter allows to filter out entries, then a mount | |
68 | * table and utab are very tiny files. | |
4709c9e6 | 69 | * |
f8416301 KZ |
70 | * The filter uses mnt_fs_streq_{target,srcpath} function where all |
71 | * paths should be absolute and canonicalized. This is done within | |
72 | * mnt_context_get_mtab_for_target() where LABEL, UUID or symlinks are | |
73 | * canonicalized. If --no-canonicalize is enabled than the target path | |
74 | * is expected already canonical. | |
75 | * | |
11573ac0 KZ |
76 | * Anyway it's better to read huge mount table than canonicalize target |
77 | * paths. It means we use the filter only if --no-canonicalize enabled. | |
78 | * | |
f8416301 KZ |
79 | * It also means that we have to read mount table from kernel |
80 | * (non-writable mtab). | |
4709c9e6 | 81 | */ |
11573ac0 KZ |
82 | if (mnt_context_is_nocanonicalize(cxt) && |
83 | !mnt_context_mtab_writable(cxt) && *tgt == '/') | |
7deae03f KZ |
84 | rc = mnt_context_get_mtab_for_target(cxt, &mtab, tgt); |
85 | else | |
86 | rc = mnt_context_get_mtab(cxt, &mtab); | |
4709c9e6 | 87 | |
ea8f06f9 | 88 | if (rc) { |
83a78332 | 89 | DBG(CXT, ul_debugobj(cxt, "umount: failed to read mtab")); |
ea8f06f9 KZ |
90 | return rc; |
91 | } | |
a10d6ad6 | 92 | |
6506a866 | 93 | if (mnt_table_get_nents(mtab) == 0) { |
83a78332 | 94 | DBG(CXT, ul_debugobj(cxt, "umount: mtab empty")); |
6506a866 KZ |
95 | return 1; |
96 | } | |
97 | ||
cddd2eaa VD |
98 | ns_old = mnt_context_switch_target_ns(cxt); |
99 | if (!ns_old) | |
100 | return -MNT_ERR_NAMESPACE; | |
101 | ||
a10d6ad6 | 102 | try_loopdev: |
68164f6c | 103 | fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD); |
e39cbb76 | 104 | if (!fs && mnt_context_is_swapmatch(cxt)) { |
224f5b92 KZ |
105 | /* |
106 | * Maybe the option is source rather than target (sometimes | |
107 | * people use e.g. "umount /dev/sda1") | |
108 | */ | |
68164f6c | 109 | fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD); |
ea8f06f9 KZ |
110 | |
111 | if (fs) { | |
68164f6c | 112 | struct libmnt_fs *fs1 = mnt_table_find_target(mtab, |
ea8f06f9 KZ |
113 | mnt_fs_get_target(fs), |
114 | MNT_ITER_BACKWARD); | |
115 | if (!fs1) { | |
83a78332 | 116 | DBG(CXT, ul_debugobj(cxt, "mtab is broken?!?!")); |
6506a866 KZ |
117 | rc = -EINVAL; |
118 | goto err; | |
ea8f06f9 KZ |
119 | } |
120 | if (fs != fs1) { | |
121 | /* Something was stacked over `file' on the | |
122 | * same mount point. */ | |
83a78332 | 123 | DBG(CXT, ul_debugobj(cxt, |
ea8f06f9 KZ |
124 | "umount: %s: %s is mounted " |
125 | "over it on the same point", | |
126 | tgt, mnt_fs_get_source(fs1))); | |
6506a866 KZ |
127 | rc = -EINVAL; |
128 | goto err; | |
ea8f06f9 KZ |
129 | } |
130 | } | |
131 | } | |
132 | ||
224f5b92 | 133 | if (!fs && !loopdev && mnt_context_is_swapmatch(cxt)) { |
a10d6ad6 | 134 | /* |
224f5b92 | 135 | * Maybe the option is /path/file.img, try to convert to /dev/loopN |
a10d6ad6 KZ |
136 | */ |
137 | struct stat st; | |
138 | ||
6589a163 | 139 | if (mnt_stat_mountpoint(tgt, &st) == 0 && S_ISREG(st.st_mode)) { |
3f420a49 | 140 | int count; |
7deae03f | 141 | struct libmnt_cache *cache = mnt_context_get_cache(cxt); |
68c41a5f | 142 | const char *bf = cache ? mnt_resolve_path(tgt, cache) : tgt; |
3f420a49 | 143 | |
68c41a5f | 144 | count = loopdev_count_by_backing_file(bf, &loopdev); |
a10d6ad6 | 145 | if (count == 1) { |
83a78332 | 146 | DBG(CXT, ul_debugobj(cxt, |
6506a866 KZ |
147 | "umount: %s --> %s (retry)", tgt, loopdev)); |
148 | tgt = loopdev; | |
a10d6ad6 KZ |
149 | goto try_loopdev; |
150 | ||
151 | } else if (count > 1) | |
83a78332 | 152 | DBG(CXT, ul_debugobj(cxt, |
a10d6ad6 | 153 | "umount: warning: %s is associated " |
45683be5 | 154 | "with more than one loopdev", tgt)); |
a10d6ad6 KZ |
155 | } |
156 | } | |
157 | ||
a9717463 | 158 | *pfs = fs; |
9e01635e | 159 | free(loopdev); |
cddd2eaa VD |
160 | if (!mnt_context_switch_ns(cxt, ns_old)) |
161 | return -MNT_ERR_NAMESPACE; | |
6506a866 | 162 | |
83a78332 | 163 | DBG(CXT, ul_debugobj(cxt, "umount fs: %s", fs ? mnt_fs_get_target(fs) : |
7ba207e7 | 164 | "<not found>")); |
6506a866 KZ |
165 | return fs ? 0 : 1; |
166 | err: | |
9e01635e | 167 | free(loopdev); |
cddd2eaa VD |
168 | if (!mnt_context_switch_ns(cxt, ns_old)) |
169 | return -MNT_ERR_NAMESPACE; | |
6506a866 KZ |
170 | return rc; |
171 | } | |
172 | ||
a9717463 KZ |
173 | /** |
174 | * mnt_context_find_umount_fs: | |
175 | * @cxt: mount context | |
176 | * @tgt: mountpoint, device, ... | |
177 | * @pfs: returns point to filesystem | |
178 | * | |
179 | * Returns: 0 on success, <0 on error, 1 if target filesystem not found | |
180 | */ | |
181 | int mnt_context_find_umount_fs(struct libmnt_context *cxt, | |
182 | const char *tgt, | |
183 | struct libmnt_fs **pfs) | |
184 | { | |
185 | if (pfs) | |
186 | *pfs = NULL; | |
187 | ||
188 | if (!cxt || !tgt || !pfs) | |
189 | return -EINVAL; | |
190 | ||
191 | DBG(CXT, ul_debugobj(cxt, "umount: lookup FS for '%s'", tgt)); | |
192 | ||
193 | if (!*tgt) | |
194 | return 1; /* empty string is not an error */ | |
195 | ||
196 | /* In future this function should be extened to support for example | |
197 | * fsinfo() (or another cheap way kernel will support), for now the | |
198 | * default is expensive mountinfo/mtab. | |
199 | */ | |
200 | return __mtab_find_umount_fs(cxt, tgt, pfs); | |
201 | } | |
202 | ||
6a52473e KZ |
203 | /* Check if there is something important in the utab file. The parsed utab is |
204 | * stored in context->utab and deallocated by mnt_free_context(). | |
0f1cbe94 KZ |
205 | * |
206 | * This function exists to avoid (if possible) /proc/self/mountinfo usage, so | |
9e930041 | 207 | * don't use things like mnt_resolve_target(), mnt_context_get_mtab() etc here. |
0f1cbe94 | 208 | * See lookup_umount_fs() for more details. |
6a52473e KZ |
209 | */ |
210 | static int has_utab_entry(struct libmnt_context *cxt, const char *target) | |
211 | { | |
212 | struct libmnt_cache *cache = NULL; | |
213 | struct libmnt_fs *fs; | |
214 | struct libmnt_iter itr; | |
215 | char *cn = NULL; | |
7396bdd4 | 216 | int rc = 0; |
6a52473e KZ |
217 | |
218 | assert(cxt); | |
219 | ||
220 | if (!cxt->utab) { | |
221 | const char *path = mnt_get_utab_path(); | |
222 | ||
223 | if (!path || is_file_empty(path)) | |
224 | return 0; | |
225 | cxt->utab = mnt_new_table(); | |
226 | if (!cxt->utab) | |
227 | return 0; | |
228 | cxt->utab->fmt = MNT_FMT_UTAB; | |
229 | if (mnt_table_parse_file(cxt->utab, path)) | |
230 | return 0; | |
231 | } | |
232 | ||
233 | /* paths in utab are canonicalized */ | |
234 | cache = mnt_context_get_cache(cxt); | |
235 | cn = mnt_resolve_path(target, cache); | |
236 | mnt_reset_iter(&itr, MNT_ITER_BACKWARD); | |
237 | ||
238 | while (mnt_table_next_fs(cxt->utab, &itr, &fs) == 0) { | |
7396bdd4 KZ |
239 | if (mnt_fs_streq_target(fs, cn)) { |
240 | rc = 1; | |
241 | break; | |
242 | } | |
6a52473e | 243 | } |
7396bdd4 KZ |
244 | |
245 | if (!cache) | |
246 | free(cn); | |
7396bdd4 | 247 | return rc; |
6a52473e KZ |
248 | } |
249 | ||
4ce77c62 KZ |
250 | /* returns: 1 not found; <0 on error; 1 success */ |
251 | static int lookup_umount_fs_by_statfs(struct libmnt_context *cxt, const char *tgt) | |
6506a866 | 252 | { |
6a52473e | 253 | struct stat st; |
4ce77c62 | 254 | const char *type; |
6506a866 | 255 | |
4ce77c62 | 256 | DBG(CXT, ul_debugobj(cxt, " lookup by statfs")); |
6506a866 | 257 | |
6a52473e KZ |
258 | /* |
259 | * Let's try to avoid mountinfo usage at all to minimize performance | |
260 | * degradation. Don't forget that kernel has to compose *whole* | |
261 | * mountinfo about all mountpoints although we look for only one entry. | |
262 | * | |
263 | * All we need is fstype and to check if there is no userspace mount | |
264 | * options for the target (e.g. helper=udisks to call /sbin/umount.udisks). | |
265 | * | |
266 | * So, let's use statfs() if possible (it's bad idea for --lazy/--force | |
4fcb0f3f RS |
267 | * umounts as target is probably unreachable NFS, also for --detach-loop |
268 | * as this additionally needs to know the name of the loop device). | |
6a52473e | 269 | */ |
4ce77c62 KZ |
270 | if (mnt_context_is_restricted(cxt) |
271 | || *tgt != '/' | |
272 | || (cxt->flags & MNT_FL_HELPER) | |
273 | || mnt_context_mtab_writable(cxt) | |
274 | || mnt_context_is_force(cxt) | |
275 | || mnt_context_is_lazy(cxt) | |
276 | || mnt_context_is_nocanonicalize(cxt) | |
277 | || mnt_context_is_loopdel(cxt) | |
278 | || mnt_stat_mountpoint(tgt, &st) != 0 || !S_ISDIR(st.st_mode) | |
279 | || has_utab_entry(cxt, tgt)) | |
280 | return 1; /* not found */ | |
281 | ||
282 | type = mnt_fs_get_fstype(cxt->fs); | |
283 | ||
284 | DBG(CXT, ul_debugobj(cxt, " umount: disabling mtab")); | |
285 | mnt_context_disable_mtab(cxt, TRUE); | |
286 | ||
287 | if (!type) { | |
288 | struct statfs vfs; | |
289 | ||
7065cc0e FV |
290 | DBG(CXT, ul_debugobj(cxt, " trying fstatfs()")); |
291 | /* O_PATH avoids triggering automount points. */ | |
292 | int pathfd = open(tgt, O_PATH); | |
293 | if (pathfd >= 0 && fstatfs(pathfd, &vfs) == 0) { | |
4ce77c62 | 294 | type = mnt_statfs_get_fstype(&vfs); |
7065cc0e FV |
295 | close(pathfd); |
296 | } | |
6a52473e | 297 | if (type) { |
4ce77c62 KZ |
298 | int rc = mnt_fs_set_fstype(cxt->fs, type); |
299 | if (rc) | |
300 | return rc; | |
6a52473e KZ |
301 | } |
302 | } | |
4ce77c62 KZ |
303 | if (type) { |
304 | DBG(CXT, ul_debugobj(cxt, | |
305 | " mountinfo unnecessary [type=%s]", type)); | |
306 | return 0; | |
307 | } | |
308 | ||
309 | return 1; /* not found */ | |
310 | } | |
311 | ||
312 | /* returns: 1 not found; <0 on error; 1 success */ | |
313 | static int lookup_umount_fs_by_mountinfo(struct libmnt_context *cxt, const char *tgt) | |
314 | { | |
315 | struct libmnt_fs *fs = NULL; | |
316 | int rc; | |
317 | ||
318 | DBG(CXT, ul_debugobj(cxt, " lookup by mountinfo")); | |
6a52473e | 319 | |
a9717463 KZ |
320 | /* search */ |
321 | rc = __mtab_find_umount_fs(cxt, tgt, &fs); | |
4ce77c62 | 322 | if (rc != 0) |
6506a866 | 323 | return rc; |
6a52473e | 324 | |
a9717463 | 325 | /* apply result */ |
085f163b | 326 | if (fs != cxt->fs) { |
085f163b KZ |
327 | mnt_fs_set_source(cxt->fs, NULL); |
328 | mnt_fs_set_target(cxt->fs, NULL); | |
309139c7 | 329 | |
085f163b | 330 | if (!mnt_copy_fs(cxt->fs, fs)) { |
4ce77c62 | 331 | DBG(CXT, ul_debugobj(cxt, " failed to copy FS")); |
085f163b KZ |
332 | return -errno; |
333 | } | |
4ce77c62 | 334 | DBG(CXT, ul_debugobj(cxt, " mtab applied")); |
309139c7 | 335 | } |
ea8f06f9 KZ |
336 | |
337 | cxt->flags |= MNT_FL_TAB_APPLIED; | |
4ce77c62 KZ |
338 | return 0; |
339 | } | |
340 | ||
a9717463 KZ |
341 | /* This finction search for FS according to cxt->fs->target, |
342 | * apply result to cxt->fs and it's umount replacement to | |
343 | * mnt_context_apply_fstab(), use mnt_context_tab_applied() | |
344 | * to check result. | |
345 | * | |
346 | * The goal is to mimimize situations wehn we need to parse | |
347 | * /proc/self/mountinfo. | |
4ce77c62 KZ |
348 | */ |
349 | static int lookup_umount_fs(struct libmnt_context *cxt) | |
350 | { | |
351 | const char *tgt; | |
352 | int rc = 0; | |
353 | ||
354 | assert(cxt); | |
355 | assert(cxt->fs); | |
356 | ||
357 | DBG(CXT, ul_debugobj(cxt, "umount: lookup FS")); | |
358 | ||
359 | tgt = mnt_fs_get_target(cxt->fs); | |
360 | if (!tgt) { | |
361 | DBG(CXT, ul_debugobj(cxt, " undefined target")); | |
362 | return -EINVAL; | |
363 | } | |
364 | ||
365 | /* try get fs type by statfs() */ | |
366 | rc = lookup_umount_fs_by_statfs(cxt, tgt); | |
367 | if (rc <= 0) | |
368 | return rc; | |
369 | ||
370 | /* get complete fs from fs entry from mountinfo */ | |
371 | rc = lookup_umount_fs_by_mountinfo(cxt, tgt); | |
372 | if (rc <= 0) | |
373 | return rc; | |
374 | ||
375 | DBG(CXT, ul_debugobj(cxt, " cannot find '%s'", tgt)); | |
376 | return 0; /* this is correct! */ | |
ea8f06f9 KZ |
377 | } |
378 | ||
379 | /* check if @devname is loopdev and if the device is associated | |
380 | * with a source from @fstab_fs | |
ea8f06f9 | 381 | */ |
a10d6ad6 | 382 | static int is_associated_fs(const char *devname, struct libmnt_fs *fs) |
ea8f06f9 KZ |
383 | { |
384 | uintmax_t offset = 0; | |
ea927bde KZ |
385 | const char *src, *optstr; |
386 | char *val; | |
ea8f06f9 | 387 | size_t valsz; |
a10d6ad6 | 388 | int flags = 0; |
ea8f06f9 KZ |
389 | |
390 | /* check if it begins with /dev/loop */ | |
9af24334 | 391 | if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP) - 1)) |
ea8f06f9 KZ |
392 | return 0; |
393 | ||
394 | src = mnt_fs_get_srcpath(fs); | |
395 | if (!src) | |
396 | return 0; | |
397 | ||
d58b3157 | 398 | /* check for the offset option in @fs */ |
ea927bde | 399 | optstr = mnt_fs_get_user_options(fs); |
fd1eb7a7 KZ |
400 | |
401 | if (optstr && | |
a10d6ad6 KZ |
402 | mnt_optstr_get_option(optstr, "offset", &val, &valsz) == 0) { |
403 | flags |= LOOPDEV_FL_OFFSET; | |
ea8f06f9 | 404 | |
a10d6ad6 KZ |
405 | if (mnt_parse_offset(val, valsz, &offset) != 0) |
406 | return 0; | |
407 | } | |
408 | ||
74a4705a | 409 | return loopdev_is_used(devname, src, offset, 0, flags); |
ea8f06f9 KZ |
410 | } |
411 | ||
4b658e09 KZ |
412 | static int prepare_helper_from_options(struct libmnt_context *cxt, |
413 | const char *name) | |
414 | { | |
415 | char *suffix = NULL; | |
416 | const char *opts; | |
417 | size_t valsz; | |
fdaba3eb | 418 | int rc; |
4b658e09 | 419 | |
379e8439 | 420 | if (mnt_context_is_nohelpers(cxt)) |
4b658e09 KZ |
421 | return 0; |
422 | ||
423 | opts = mnt_fs_get_user_options(cxt->fs); | |
424 | if (!opts) | |
425 | return 0; | |
426 | ||
5dfc9843 | 427 | if (mnt_optstr_get_option(opts, name, &suffix, &valsz)) |
4b658e09 KZ |
428 | return 0; |
429 | ||
430 | suffix = strndup(suffix, valsz); | |
431 | if (!suffix) | |
432 | return -ENOMEM; | |
433 | ||
83a78332 | 434 | DBG(CXT, ul_debugobj(cxt, "umount: umount.%s %s requested", suffix, name)); |
4b658e09 | 435 | |
fdaba3eb KZ |
436 | rc = mnt_context_prepare_helper(cxt, "umount", suffix); |
437 | free(suffix); | |
438 | ||
439 | return rc; | |
4b658e09 KZ |
440 | } |
441 | ||
5fea669e RH |
442 | static int is_fuse_usermount(struct libmnt_context *cxt, int *errsv) |
443 | { | |
444 | struct libmnt_ns *ns_old; | |
445 | const char *type = mnt_fs_get_fstype(cxt->fs); | |
446 | const char *optstr; | |
df61216c | 447 | char *user_id = NULL; |
5fea669e RH |
448 | size_t sz; |
449 | uid_t uid; | |
df61216c | 450 | char uidstr[sizeof(stringify_value(ULONG_MAX))]; |
5fea669e RH |
451 | |
452 | *errsv = 0; | |
453 | ||
454 | if (!type) | |
455 | return 0; | |
456 | ||
457 | if (strcmp(type, "fuse") != 0 && | |
458 | strcmp(type, "fuseblk") != 0 && | |
459 | strncmp(type, "fuse.", 5) != 0 && | |
460 | strncmp(type, "fuseblk.", 8) != 0) | |
461 | return 0; | |
462 | ||
df61216c KZ |
463 | /* get user_id= from mount table */ |
464 | optstr = mnt_fs_get_fs_options(cxt->fs); | |
5fea669e RH |
465 | if (!optstr) |
466 | return 0; | |
467 | ||
df61216c | 468 | if (mnt_optstr_get_option(optstr, "user_id", &user_id, &sz) != 0) |
5fea669e RH |
469 | return 0; |
470 | ||
df61216c | 471 | if (sz == 0 || user_id == NULL) |
5fea669e RH |
472 | return 0; |
473 | ||
474 | /* get current user */ | |
475 | ns_old = mnt_context_switch_origin_ns(cxt); | |
476 | if (!ns_old) { | |
477 | *errsv = -MNT_ERR_NAMESPACE; | |
478 | return 0; | |
479 | } | |
480 | ||
481 | uid = getuid(); | |
5fea669e RH |
482 | |
483 | if (!mnt_context_switch_ns(cxt, ns_old)) { | |
484 | *errsv = -MNT_ERR_NAMESPACE; | |
485 | return 0; | |
486 | } | |
487 | ||
df61216c KZ |
488 | snprintf(uidstr, sizeof(uidstr), "%lu", (unsigned long) uid); |
489 | return strncmp(user_id, uidstr, sz) == 0; | |
5fea669e RH |
490 | } |
491 | ||
ea8f06f9 KZ |
492 | /* |
493 | * Note that cxt->fs contains relevant mtab entry! | |
494 | */ | |
68164f6c | 495 | static int evaluate_permissions(struct libmnt_context *cxt) |
ea8f06f9 | 496 | { |
68164f6c | 497 | struct libmnt_table *fstab; |
766af80b | 498 | unsigned long u_flags = 0; |
ea8f06f9 | 499 | const char *tgt, *src, *optstr; |
5fea669e | 500 | int rc = 0, ok = 0; |
68164f6c | 501 | struct libmnt_fs *fs; |
ea8f06f9 | 502 | |
1b56aae8 KZ |
503 | assert(cxt); |
504 | assert(cxt->fs); | |
505 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
506 | ||
ea8f06f9 KZ |
507 | if (!mnt_context_is_restricted(cxt)) |
508 | return 0; /* superuser mount */ | |
509 | ||
83a78332 | 510 | DBG(CXT, ul_debugobj(cxt, "umount: evaluating permissions")); |
dd369652 | 511 | |
c410f647 | 512 | if (!mnt_context_tab_applied(cxt)) { |
83a78332 | 513 | DBG(CXT, ul_debugobj(cxt, |
19f411cb | 514 | "cannot find %s in mtab and you are not root", |
ea8f06f9 KZ |
515 | mnt_fs_get_target(cxt->fs))); |
516 | goto eperm; | |
517 | } | |
518 | ||
4b658e09 KZ |
519 | if (cxt->user_mountflags & MNT_MS_UHELPER) { |
520 | /* on uhelper= mount option based helper */ | |
521 | rc = prepare_helper_from_options(cxt, "uhelper"); | |
522 | if (rc) | |
ea8f06f9 KZ |
523 | return rc; |
524 | if (cxt->helper) | |
525 | return 0; /* we'll call /sbin/umount.<uhelper> */ | |
526 | } | |
527 | ||
5fea669e RH |
528 | /* |
529 | * Check if this is a fuse mount for the current user, | |
530 | * if so then unmounting is allowed | |
531 | */ | |
532 | if (is_fuse_usermount(cxt, &rc)) { | |
533 | DBG(CXT, ul_debugobj(cxt, "fuse user mount, umount is allowed")); | |
534 | return 0; | |
535 | } | |
536 | if (rc) | |
537 | return rc; | |
538 | ||
ea8f06f9 | 539 | /* |
d58b3157 | 540 | * User mounts have to be in /etc/fstab |
ea8f06f9 KZ |
541 | */ |
542 | rc = mnt_context_get_fstab(cxt, &fstab); | |
543 | if (rc) | |
544 | return rc; | |
545 | ||
546 | tgt = mnt_fs_get_target(cxt->fs); | |
547 | src = mnt_fs_get_source(cxt->fs); | |
548 | ||
dd369652 KZ |
549 | if (mnt_fs_get_bindsrc(cxt->fs)) { |
550 | src = mnt_fs_get_bindsrc(cxt->fs); | |
83a78332 | 551 | DBG(CXT, ul_debugobj(cxt, |
dd369652 KZ |
552 | "umount: using bind source: %s", src)); |
553 | } | |
554 | ||
ea8f06f9 KZ |
555 | /* If fstab contains the two lines |
556 | * /dev/sda1 /mnt/zip auto user,noauto 0 0 | |
557 | * /dev/sda4 /mnt/zip auto user,noauto 0 0 | |
558 | * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail. | |
d58b3157 | 559 | * So, we must not look for the file, but for the pair (dev,file) in fstab. |
ea8f06f9 | 560 | */ |
68164f6c | 561 | fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD); |
ea8f06f9 KZ |
562 | if (!fs) { |
563 | /* | |
564 | * It's possible that there is /path/file.img in fstab and | |
d58b3157 | 565 | * /dev/loop0 in mtab -- then we have to check the relation |
ea8f06f9 KZ |
566 | * between loopdev and the file. |
567 | */ | |
68164f6c | 568 | fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD); |
ea8f06f9 | 569 | if (fs) { |
68c41a5f KZ |
570 | struct libmnt_cache *cache = mnt_context_get_cache(cxt); |
571 | const char *sp = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */ | |
572 | const char *dev = sp && cache ? mnt_resolve_path(sp, cache) : sp; | |
ea8f06f9 | 573 | |
a10d6ad6 | 574 | if (!dev || !is_associated_fs(dev, fs)) |
ea8f06f9 KZ |
575 | fs = NULL; |
576 | } | |
577 | if (!fs) { | |
83a78332 | 578 | DBG(CXT, ul_debugobj(cxt, |
ea8f06f9 KZ |
579 | "umount %s: mtab disagrees with fstab", |
580 | tgt)); | |
581 | goto eperm; | |
582 | } | |
583 | } | |
584 | ||
585 | /* | |
586 | * User mounting and unmounting is allowed only if fstab contains one | |
587 | * of the options `user', `users' or `owner' or `group'. | |
588 | * | |
589 | * The option `users' allows arbitrary users to mount and unmount - | |
590 | * this may be a security risk. | |
591 | * | |
592 | * The options `user', `owner' and `group' only allow unmounting by the | |
593 | * user that mounted (visible in mtab). | |
594 | */ | |
68164f6c | 595 | optstr = mnt_fs_get_user_options(fs); /* FSTAB mount options! */ |
ea8f06f9 KZ |
596 | if (!optstr) |
597 | goto eperm; | |
598 | ||
76a06ca4 KZ |
599 | if (mnt_optstr_get_flags(optstr, &u_flags, |
600 | mnt_get_builtin_optmap(MNT_USERSPACE_MAP))) | |
ea8f06f9 KZ |
601 | goto eperm; |
602 | ||
766af80b | 603 | if (u_flags & MNT_MS_USERS) { |
83a78332 | 604 | DBG(CXT, ul_debugobj(cxt, |
766af80b | 605 | "umount: promiscuous setting ('users') in fstab")); |
ea8f06f9 | 606 | return 0; |
766af80b | 607 | } |
ea8f06f9 | 608 | /* |
d58b3157 | 609 | * Check user=<username> setting from mtab if there is a user, owner or |
ea8f06f9 KZ |
610 | * group option in /etc/fstab |
611 | */ | |
07a67e68 | 612 | if (u_flags & (MNT_MS_USER | MNT_MS_OWNER | MNT_MS_GROUP)) { |
ea8f06f9 | 613 | |
1b504263 | 614 | char *curr_user; |
ea8f06f9 KZ |
615 | char *mtab_user = NULL; |
616 | size_t sz; | |
cddd2eaa | 617 | struct libmnt_ns *ns_old; |
ea8f06f9 | 618 | |
83a78332 | 619 | DBG(CXT, ul_debugobj(cxt, |
766af80b KZ |
620 | "umount: checking user=<username> from mtab")); |
621 | ||
cddd2eaa VD |
622 | ns_old = mnt_context_switch_origin_ns(cxt); |
623 | if (!ns_old) | |
624 | return -MNT_ERR_NAMESPACE; | |
625 | ||
766af80b KZ |
626 | curr_user = mnt_get_username(getuid()); |
627 | ||
cb400752 KZ |
628 | if (!mnt_context_switch_ns(cxt, ns_old)) { |
629 | free(curr_user); | |
cddd2eaa | 630 | return -MNT_ERR_NAMESPACE; |
cb400752 | 631 | } |
ea8f06f9 | 632 | if (!curr_user) { |
83a78332 | 633 | DBG(CXT, ul_debugobj(cxt, "umount %s: cannot " |
766af80b | 634 | "convert %d to username", tgt, getuid())); |
ea8f06f9 KZ |
635 | goto eperm; |
636 | } | |
637 | ||
638 | /* get options from mtab */ | |
68164f6c | 639 | optstr = mnt_fs_get_user_options(cxt->fs); |
5dfc9843 | 640 | if (optstr && !mnt_optstr_get_option(optstr, |
ea8f06f9 KZ |
641 | "user", &mtab_user, &sz) && sz) |
642 | ok = !strncmp(curr_user, mtab_user, sz); | |
2afcbe13 DR |
643 | |
644 | free(curr_user); | |
ea8f06f9 KZ |
645 | } |
646 | ||
647 | if (ok) { | |
83a78332 | 648 | DBG(CXT, ul_debugobj(cxt, "umount %s is allowed", tgt)); |
ea8f06f9 KZ |
649 | return 0; |
650 | } | |
651 | eperm: | |
83a78332 | 652 | DBG(CXT, ul_debugobj(cxt, "umount is not allowed for you")); |
ea8f06f9 KZ |
653 | return -EPERM; |
654 | } | |
655 | ||
68164f6c | 656 | static int exec_helper(struct libmnt_context *cxt) |
ea8f06f9 | 657 | { |
65ca7b5a VD |
658 | char *namespace = NULL; |
659 | struct libmnt_ns *ns_tgt = mnt_context_get_target_ns(cxt); | |
ea8f06f9 | 660 | int rc; |
7440665f | 661 | pid_t pid; |
ea8f06f9 KZ |
662 | |
663 | assert(cxt); | |
664 | assert(cxt->fs); | |
665 | assert(cxt->helper); | |
1b56aae8 | 666 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); |
97e23b5e | 667 | assert(cxt->helper_exec_status == 1); |
ea8f06f9 | 668 | |
28cdf9c6 KZ |
669 | if (mnt_context_is_fake(cxt)) { |
670 | DBG(CXT, ul_debugobj(cxt, "fake mode: does not execute helper")); | |
671 | cxt->helper_exec_status = rc = 0; | |
672 | return rc; | |
673 | } | |
674 | ||
65ca7b5a VD |
675 | if (ns_tgt->fd != -1 |
676 | && asprintf(&namespace, "/proc/%i/fd/%i", | |
677 | getpid(), ns_tgt->fd) == -1) { | |
678 | return -ENOMEM; | |
679 | } | |
680 | ||
ea8f06f9 KZ |
681 | DBG_FLUSH; |
682 | ||
7440665f VP |
683 | pid = fork(); |
684 | switch (pid) { | |
ea8f06f9 KZ |
685 | case 0: |
686 | { | |
65ca7b5a | 687 | const char *args[12], *type; |
ea8f06f9 KZ |
688 | int i = 0; |
689 | ||
690 | if (setgid(getgid()) < 0) | |
41d758f8 | 691 | _exit(EXIT_FAILURE); |
ea8f06f9 KZ |
692 | |
693 | if (setuid(getuid()) < 0) | |
41d758f8 | 694 | _exit(EXIT_FAILURE); |
ea8f06f9 | 695 | |
cddd2eaa VD |
696 | if (!mnt_context_switch_origin_ns(cxt)) |
697 | _exit(EXIT_FAILURE); | |
698 | ||
ea8f06f9 KZ |
699 | type = mnt_fs_get_fstype(cxt->fs); |
700 | ||
701 | args[i++] = cxt->helper; /* 1 */ | |
702 | args[i++] = mnt_fs_get_target(cxt->fs); /* 2 */ | |
703 | ||
379e8439 | 704 | if (mnt_context_is_nomtab(cxt)) |
ea8f06f9 | 705 | args[i++] = "-n"; /* 3 */ |
379e8439 | 706 | if (mnt_context_is_lazy(cxt)) |
ea8f06f9 | 707 | args[i++] = "-l"; /* 4 */ |
379e8439 | 708 | if (mnt_context_is_force(cxt)) |
ea8f06f9 | 709 | args[i++] = "-f"; /* 5 */ |
379e8439 | 710 | if (mnt_context_is_verbose(cxt)) |
ea8f06f9 | 711 | args[i++] = "-v"; /* 6 */ |
379e8439 | 712 | if (mnt_context_is_rdonly_umount(cxt)) |
ea8f06f9 | 713 | args[i++] = "-r"; /* 7 */ |
e4ea53de KZ |
714 | if (type |
715 | && strchr(type, '.') | |
716 | && !endswith(cxt->helper, type)) { | |
ea8f06f9 | 717 | args[i++] = "-t"; /* 8 */ |
ea927bde | 718 | args[i++] = type; /* 9 */ |
ea8f06f9 | 719 | } |
65ca7b5a VD |
720 | if (namespace) { |
721 | args[i++] = "-N"; /* 10 */ | |
722 | args[i++] = namespace; /* 11 */ | |
723 | } | |
ea8f06f9 | 724 | |
65ca7b5a | 725 | args[i] = NULL; /* 12 */ |
ea8f06f9 | 726 | for (i = 0; args[i]; i++) |
83a78332 | 727 | DBG(CXT, ul_debugobj(cxt, "argv[%d] = \"%s\"", |
ea8f06f9 | 728 | i, args[i])); |
ea8f06f9 KZ |
729 | DBG_FLUSH; |
730 | execv(cxt->helper, (char * const *) args); | |
41d758f8 | 731 | _exit(EXIT_FAILURE); |
ea8f06f9 KZ |
732 | } |
733 | default: | |
734 | { | |
735 | int st; | |
ea8f06f9 | 736 | |
fb7e4474 KZ |
737 | if (waitpid(pid, &st, 0) == (pid_t) -1) { |
738 | cxt->helper_status = -1; | |
739 | rc = -errno; | |
740 | } else { | |
741 | cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1; | |
742 | cxt->helper_exec_status = rc = 0; | |
743 | } | |
744 | DBG(CXT, ul_debugobj(cxt, "%s executed [status=%d, rc=%d%s]", | |
745 | cxt->helper, | |
746 | cxt->helper_status, rc, | |
747 | rc ? " waitpid failed" : "")); | |
ea8f06f9 KZ |
748 | break; |
749 | } | |
750 | ||
751 | case -1: | |
97e23b5e | 752 | cxt->helper_exec_status = rc = -errno; |
83a78332 | 753 | DBG(CXT, ul_debugobj(cxt, "fork() failed")); |
ea8f06f9 KZ |
754 | break; |
755 | } | |
756 | ||
757 | return rc; | |
758 | } | |
759 | ||
b70785bc KZ |
760 | /* |
761 | * mnt_context_helper_setopt() backend. | |
762 | * | |
ee314075 | 763 | * This function applies umount.type command line option (for example parsed |
b70785bc KZ |
764 | * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and |
765 | * then 1 is returned. | |
766 | * | |
767 | * Returns: negative number on error, 1 if @c is unknown option, 0 on success. | |
768 | */ | |
769 | int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg) | |
770 | { | |
771 | int rc = -EINVAL; | |
772 | ||
773 | assert(cxt); | |
774 | assert(cxt->action == MNT_ACT_UMOUNT); | |
775 | ||
776 | switch(c) { | |
777 | case 'n': | |
778 | rc = mnt_context_disable_mtab(cxt, TRUE); | |
779 | break; | |
780 | case 'l': | |
781 | rc = mnt_context_enable_lazy(cxt, TRUE); | |
782 | break; | |
783 | case 'f': | |
ff4c00f9 | 784 | rc = mnt_context_enable_force(cxt, TRUE); |
b70785bc KZ |
785 | break; |
786 | case 'v': | |
787 | rc = mnt_context_enable_verbose(cxt, TRUE); | |
788 | break; | |
789 | case 'r': | |
790 | rc = mnt_context_enable_rdonly_umount(cxt, TRUE); | |
791 | break; | |
792 | case 't': | |
793 | if (arg) | |
794 | rc = mnt_context_set_fstype(cxt, arg); | |
795 | break; | |
65ca7b5a VD |
796 | case 'N': |
797 | if (arg) | |
798 | rc = mnt_context_set_target_ns(cxt, arg); | |
799 | break; | |
b70785bc KZ |
800 | default: |
801 | return 1; | |
b70785bc KZ |
802 | } |
803 | ||
804 | return rc; | |
805 | } | |
806 | ||
d58b3157 | 807 | /* Check whether the kernel supports the UMOUNT_NOFOLLOW flag */ |
66bb8267 KZ |
808 | static int umount_nofollow_support(void) |
809 | { | |
810 | int res = umount2("", UMOUNT_UNUSED); | |
811 | if (res != -1 || errno != EINVAL) | |
812 | return 0; | |
813 | ||
814 | res = umount2("", UMOUNT_NOFOLLOW); | |
815 | if (res != -1 || errno != ENOENT) | |
816 | return 0; | |
817 | ||
818 | return 1; | |
819 | } | |
820 | ||
68164f6c | 821 | static int do_umount(struct libmnt_context *cxt) |
ea8f06f9 | 822 | { |
701b48cf | 823 | int rc = 0, flags = 0; |
ea8f06f9 | 824 | const char *src, *target; |
66bb8267 | 825 | char *tgtbuf = NULL; |
ea8f06f9 KZ |
826 | |
827 | assert(cxt); | |
828 | assert(cxt->fs); | |
1b56aae8 | 829 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); |
97e23b5e | 830 | assert(cxt->syscall_status == 1); |
ea8f06f9 KZ |
831 | |
832 | if (cxt->helper) | |
833 | return exec_helper(cxt); | |
834 | ||
835 | src = mnt_fs_get_srcpath(cxt->fs); | |
836 | target = mnt_fs_get_target(cxt->fs); | |
837 | ||
838 | if (!target) | |
839 | return -EINVAL; | |
840 | ||
83a78332 | 841 | DBG(CXT, ul_debugobj(cxt, "do umount")); |
66bb8267 | 842 | |
916a3f8d | 843 | if (mnt_context_is_restricted(cxt) && !mnt_context_is_fake(cxt)) { |
66bb8267 | 844 | /* |
45683be5 | 845 | * extra paranoia for non-root users |
66bb8267 KZ |
846 | * -- chdir to the parent of the mountpoint and use NOFOLLOW |
847 | * flag to avoid races and symlink attacks. | |
848 | */ | |
849 | if (umount_nofollow_support()) | |
850 | flags |= UMOUNT_NOFOLLOW; | |
851 | ||
852 | rc = mnt_chdir_to_parent(target, &tgtbuf); | |
853 | if (rc) | |
854 | return rc; | |
855 | target = tgtbuf; | |
856 | } | |
857 | ||
379e8439 | 858 | if (mnt_context_is_lazy(cxt)) |
701b48cf | 859 | flags |= MNT_DETACH; |
ea8f06f9 | 860 | |
5d9c7966 | 861 | if (mnt_context_is_force(cxt)) |
701b48cf | 862 | flags |= MNT_FORCE; |
ea8f06f9 | 863 | |
83a78332 | 864 | DBG(CXT, ul_debugobj(cxt, "umount(2) [target='%s', flags=0x%08x]%s", |
085f163b | 865 | target, flags, |
379e8439 | 866 | mnt_context_is_fake(cxt) ? " (FAKE)" : "")); |
701b48cf | 867 | |
379e8439 | 868 | if (mnt_context_is_fake(cxt)) |
085f163b KZ |
869 | rc = 0; |
870 | else { | |
871 | rc = flags ? umount2(target, flags) : umount(target); | |
872 | if (rc < 0) | |
873 | cxt->syscall_status = -errno; | |
874 | free(tgtbuf); | |
875 | } | |
66bb8267 | 876 | |
ea8f06f9 KZ |
877 | /* |
878 | * try remount read-only | |
879 | */ | |
379e8439 KZ |
880 | if (rc < 0 |
881 | && cxt->syscall_status == -EBUSY | |
882 | && mnt_context_is_rdonly_umount(cxt) | |
883 | && src) { | |
ea8f06f9 | 884 | |
98b1302e KZ |
885 | mnt_context_set_mflags(cxt, (cxt->mountflags | |
886 | MS_REMOUNT | MS_RDONLY)); | |
379e8439 | 887 | mnt_context_enable_loopdel(cxt, FALSE); |
98b1302e | 888 | |
83a78332 | 889 | DBG(CXT, ul_debugobj(cxt, |
26e42d81 | 890 | "umount(2) failed [errno=%d] -- trying to remount read-only", |
701b48cf | 891 | -cxt->syscall_status)); |
ea8f06f9 | 892 | |
66bb8267 | 893 | rc = mount(src, mnt_fs_get_target(cxt->fs), NULL, |
8d6de224 | 894 | MS_REMOUNT | MS_RDONLY, NULL); |
ea8f06f9 | 895 | if (rc < 0) { |
97e23b5e | 896 | cxt->syscall_status = -errno; |
83a78332 | 897 | DBG(CXT, ul_debugobj(cxt, |
701b48cf KZ |
898 | "read-only re-mount(2) failed [errno=%d]", |
899 | -cxt->syscall_status)); | |
900 | ||
5982583a | 901 | return -cxt->syscall_status; |
ea8f06f9 | 902 | } |
97e23b5e | 903 | cxt->syscall_status = 0; |
83a78332 | 904 | DBG(CXT, ul_debugobj(cxt, "read-only re-mount(2) success")); |
ea8f06f9 KZ |
905 | return 0; |
906 | } | |
907 | ||
908 | if (rc < 0) { | |
83a78332 | 909 | DBG(CXT, ul_debugobj(cxt, "umount(2) failed [errno=%d]", |
701b48cf | 910 | -cxt->syscall_status)); |
97e23b5e | 911 | return -cxt->syscall_status; |
ea8f06f9 | 912 | } |
701b48cf | 913 | |
21193a48 | 914 | cxt->syscall_status = 0; |
83a78332 | 915 | DBG(CXT, ul_debugobj(cxt, "umount(2) success")); |
ea8f06f9 KZ |
916 | return 0; |
917 | } | |
918 | ||
919 | /** | |
93760092 | 920 | * mnt_context_prepare_umount: |
ea8f06f9 KZ |
921 | * @cxt: mount context |
922 | * | |
93760092 | 923 | * Prepare context for umounting, unnecessary for mnt_context_umount(). |
ea8f06f9 KZ |
924 | * |
925 | * Returns: 0 on success, and negative number in case of error. | |
926 | */ | |
93760092 | 927 | int mnt_context_prepare_umount(struct libmnt_context *cxt) |
ea8f06f9 | 928 | { |
1d0cd73f | 929 | int rc; |
cddd2eaa | 930 | struct libmnt_ns *ns_old; |
ea8f06f9 | 931 | |
c70d9d76 | 932 | if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs)) |
ea8f06f9 | 933 | return -EINVAL; |
085f163b | 934 | if (!mnt_context_get_source(cxt) && !mnt_context_get_target(cxt)) |
ea8f06f9 | 935 | return -EINVAL; |
93760092 KZ |
936 | if (cxt->flags & MNT_FL_PREPARED) |
937 | return 0; | |
ea8f06f9 | 938 | |
cba392b6 KZ |
939 | assert(cxt->helper_exec_status == 1); |
940 | assert(cxt->syscall_status == 1); | |
941 | ||
ea8f06f9 KZ |
942 | free(cxt->helper); /* be paranoid */ |
943 | cxt->helper = NULL; | |
1d0cd73f KZ |
944 | cxt->action = MNT_ACT_UMOUNT; |
945 | ||
cddd2eaa VD |
946 | ns_old = mnt_context_switch_target_ns(cxt); |
947 | if (!ns_old) | |
948 | return -MNT_ERR_NAMESPACE; | |
949 | ||
ea8f06f9 | 950 | rc = lookup_umount_fs(cxt); |
1b56aae8 | 951 | if (!rc) |
68164f6c | 952 | rc = mnt_context_merge_mflags(cxt); |
ea8f06f9 KZ |
953 | if (!rc) |
954 | rc = evaluate_permissions(cxt); | |
4b658e09 KZ |
955 | |
956 | if (!rc && !cxt->helper) { | |
957 | ||
f19c952b KZ |
958 | if (cxt->user_mountflags & MNT_MS_HELPER) |
959 | /* on helper= mount option based helper */ | |
960 | rc = prepare_helper_from_options(cxt, "helper"); | |
4b658e09 KZ |
961 | |
962 | if (!rc && !cxt->helper) | |
963 | /* on fstype based helper */ | |
964 | rc = mnt_context_prepare_helper(cxt, "umount", NULL); | |
965 | } | |
966 | ||
475c30d0 KZ |
967 | if (!rc && (cxt->user_mountflags & MNT_MS_LOOP)) |
968 | /* loop option explicitly specified in mtab, detach this loop */ | |
969 | mnt_context_enable_loopdel(cxt, TRUE); | |
970 | ||
379e8439 | 971 | if (!rc && mnt_context_is_loopdel(cxt) && cxt->fs) { |
98b1302e KZ |
972 | const char *src = mnt_fs_get_srcpath(cxt->fs); |
973 | ||
974 | if (src && (!is_loopdev(src) || loopdev_is_autoclear(src))) | |
379e8439 | 975 | mnt_context_enable_loopdel(cxt, FALSE); |
98b1302e KZ |
976 | } |
977 | ||
1d0cd73f | 978 | if (rc) { |
83a78332 | 979 | DBG(CXT, ul_debugobj(cxt, "umount: preparing failed")); |
1d0cd73f | 980 | return rc; |
ea8f06f9 | 981 | } |
93760092 | 982 | cxt->flags |= MNT_FL_PREPARED; |
cddd2eaa VD |
983 | |
984 | if (!mnt_context_switch_ns(cxt, ns_old)) | |
985 | return -MNT_ERR_NAMESPACE; | |
986 | ||
93760092 KZ |
987 | return rc; |
988 | } | |
989 | ||
990 | /** | |
991 | * mnt_context_do_umount: | |
992 | * @cxt: mount context | |
993 | * | |
994 | * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type). | |
995 | * Unnecessary for mnt_context_umount(). | |
996 | * | |
997 | * See also mnt_context_disable_helpers(). | |
998 | * | |
5982583a | 999 | * WARNING: non-zero return code does not mean that umount(2) syscall or |
455fe9a0 | 1000 | * umount.type helper wasn't successfully called. |
5982583a KZ |
1001 | * |
1002 | * Check mnt_context_get_status() after error! | |
1003 | * | |
1004 | * Returns: 0 on success; | |
1005 | * >0 in case of umount(2) error (returns syscall errno), | |
1006 | * <0 in case of other errors. | |
93760092 KZ |
1007 | */ |
1008 | int mnt_context_do_umount(struct libmnt_context *cxt) | |
1009 | { | |
1010 | int rc; | |
cddd2eaa | 1011 | struct libmnt_ns *ns_old; |
93760092 KZ |
1012 | |
1013 | assert(cxt); | |
1014 | assert(cxt->fs); | |
1015 | assert(cxt->helper_exec_status == 1); | |
1016 | assert(cxt->syscall_status == 1); | |
1017 | assert((cxt->flags & MNT_FL_PREPARED)); | |
1018 | assert((cxt->action == MNT_ACT_UMOUNT)); | |
1019 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
ea8f06f9 | 1020 | |
cddd2eaa VD |
1021 | ns_old = mnt_context_switch_target_ns(cxt); |
1022 | if (!ns_old) | |
1023 | return -MNT_ERR_NAMESPACE; | |
1024 | ||
1d0cd73f KZ |
1025 | rc = do_umount(cxt); |
1026 | if (rc) | |
cddd2eaa | 1027 | goto end; |
ea8f06f9 | 1028 | |
379e8439 | 1029 | if (mnt_context_get_status(cxt) && !mnt_context_is_fake(cxt)) { |
ea8f06f9 | 1030 | /* |
98b1302e KZ |
1031 | * Umounted, do some post-umount operations |
1032 | * - remove loopdev | |
1033 | * - refresh in-memory mtab stuff if remount rather than | |
1034 | * umount has been performed | |
ea8f06f9 | 1035 | */ |
379e8439 | 1036 | if (mnt_context_is_loopdel(cxt) |
98b1302e KZ |
1037 | && !(cxt->mountflags & MS_REMOUNT)) |
1038 | rc = mnt_context_delete_loopdev(cxt); | |
1039 | ||
379e8439 | 1040 | if (!mnt_context_is_nomtab(cxt) |
98b1302e KZ |
1041 | && mnt_context_get_status(cxt) |
1042 | && !cxt->helper | |
379e8439 | 1043 | && mnt_context_is_rdonly_umount(cxt) |
98b1302e KZ |
1044 | && (cxt->mountflags & MS_REMOUNT)) { |
1045 | ||
1046 | /* use "remount" instead of "umount" in /etc/mtab */ | |
150e696d | 1047 | if (!rc && cxt->update && mnt_context_mtab_writable(cxt)) |
98b1302e KZ |
1048 | rc = mnt_update_set_fs(cxt->update, |
1049 | cxt->mountflags, NULL, cxt->fs); | |
1050 | } | |
ea8f06f9 | 1051 | } |
cddd2eaa VD |
1052 | end: |
1053 | if (!mnt_context_switch_ns(cxt, ns_old)) | |
1054 | return -MNT_ERR_NAMESPACE; | |
1055 | ||
93760092 KZ |
1056 | return rc; |
1057 | } | |
1058 | ||
1059 | /** | |
1060 | * mnt_context_finalize_umount: | |
1061 | * @cxt: context | |
1062 | * | |
1063 | * Mtab update, etc. Unnecessary for mnt_context_umount(), but should be called | |
1064 | * after mnt_context_do_umount(). See also mnt_context_set_syscall_status(). | |
1065 | * | |
1066 | * Returns: negative number on error, 0 on success. | |
1067 | */ | |
1068 | int mnt_context_finalize_umount(struct libmnt_context *cxt) | |
1069 | { | |
1070 | int rc; | |
1071 | ||
1072 | assert(cxt); | |
1073 | assert(cxt->fs); | |
1074 | assert((cxt->flags & MNT_FL_PREPARED)); | |
1075 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
1076 | ||
1077 | rc = mnt_context_prepare_update(cxt); | |
1078 | if (!rc) | |
f4d37838 | 1079 | rc = mnt_context_update_tabs(cxt); |
93760092 KZ |
1080 | return rc; |
1081 | } | |
1082 | ||
1083 | ||
1084 | /** | |
1085 | * mnt_context_umount: | |
1086 | * @cxt: umount context | |
1087 | * | |
1088 | * High-level, umounts filesystem by umount(2) or fork()+exec(/sbin/umount.type). | |
1089 | * | |
1090 | * This is similar to: | |
1091 | * | |
1092 | * mnt_context_prepare_umount(cxt); | |
1093 | * mnt_context_do_umount(cxt); | |
1094 | * mnt_context_finalize_umount(cxt); | |
1095 | * | |
1096 | * See also mnt_context_disable_helpers(). | |
1097 | * | |
5982583a | 1098 | * WARNING: non-zero return code does not mean that umount(2) syscall or |
455fe9a0 | 1099 | * umount.type helper wasn't successfully called. |
5982583a KZ |
1100 | * |
1101 | * Check mnt_context_get_status() after error! | |
1102 | * | |
1103 | * Returns: 0 on success; | |
1104 | * >0 in case of umount(2) error (returns syscall errno), | |
1105 | * <0 in case of other errors. | |
93760092 KZ |
1106 | */ |
1107 | int mnt_context_umount(struct libmnt_context *cxt) | |
1108 | { | |
1109 | int rc; | |
cddd2eaa | 1110 | struct libmnt_ns *ns_old; |
93760092 KZ |
1111 | |
1112 | assert(cxt); | |
1113 | assert(cxt->fs); | |
1114 | assert(cxt->helper_exec_status == 1); | |
1115 | assert(cxt->syscall_status == 1); | |
1116 | ||
83a78332 | 1117 | DBG(CXT, ul_debugobj(cxt, "umount: %s", mnt_context_get_target(cxt))); |
085f163b | 1118 | |
cddd2eaa VD |
1119 | ns_old = mnt_context_switch_target_ns(cxt); |
1120 | if (!ns_old) | |
1121 | return -MNT_ERR_NAMESPACE; | |
1122 | ||
93760092 KZ |
1123 | rc = mnt_context_prepare_umount(cxt); |
1124 | if (!rc) | |
1125 | rc = mnt_context_prepare_update(cxt); | |
1126 | if (!rc) | |
1127 | rc = mnt_context_do_umount(cxt); | |
1128 | if (!rc) | |
1129 | rc = mnt_context_update_tabs(cxt); | |
cddd2eaa VD |
1130 | |
1131 | if (!mnt_context_switch_ns(cxt, ns_old)) | |
1132 | return -MNT_ERR_NAMESPACE; | |
1133 | ||
93760092 | 1134 | return rc; |
ea8f06f9 | 1135 | } |
085f163b KZ |
1136 | |
1137 | ||
1138 | /** | |
1139 | * mnt_context_next_umount: | |
1140 | * @cxt: context | |
1141 | * @itr: iterator | |
1142 | * @fs: returns the current filesystem | |
1143 | * @mntrc: returns the return code from mnt_context_umount() | |
1144 | * @ignored: returns 1 for not matching | |
1145 | * | |
1146 | * This function tries to umount the next filesystem from mtab (as returned by | |
1147 | * mnt_context_get_mtab()). | |
1148 | * | |
1149 | * You can filter out filesystems by: | |
1150 | * mnt_context_set_options_pattern() to simulate umount -a -O pattern | |
1151 | * mnt_context_set_fstype_pattern() to simulate umount -a -t pattern | |
1152 | * | |
d58b3157 | 1153 | * If the filesystem is not mounted or does not match the defined criteria, |
085f163b KZ |
1154 | * then the function mnt_context_next_umount() returns zero, but the @ignored is |
1155 | * non-zero. Note that the root filesystem is always ignored. | |
1156 | * | |
1157 | * If umount(2) syscall or umount.type helper failed, then the | |
1158 | * mnt_context_next_umount() function returns zero, but the @mntrc is non-zero. | |
1159 | * Use also mnt_context_get_status() to check if the filesystem was | |
1160 | * successfully umounted. | |
1161 | * | |
1162 | * Returns: 0 on success, | |
1163 | * <0 in case of error (!= umount(2) errors) | |
1164 | * 1 at the end of the list. | |
1165 | */ | |
1166 | int mnt_context_next_umount(struct libmnt_context *cxt, | |
1167 | struct libmnt_iter *itr, | |
1168 | struct libmnt_fs **fs, | |
1169 | int *mntrc, | |
1170 | int *ignored) | |
1171 | { | |
1172 | struct libmnt_table *mtab; | |
1173 | const char *tgt; | |
1174 | int rc; | |
1175 | ||
1176 | if (ignored) | |
1177 | *ignored = 0; | |
1178 | if (mntrc) | |
1179 | *mntrc = 0; | |
1180 | ||
1181 | if (!cxt || !fs || !itr) | |
1182 | return -EINVAL; | |
1183 | ||
314f2522 | 1184 | rc = mnt_context_get_mtab(cxt, &mtab); |
085f163b KZ |
1185 | cxt->mtab = NULL; /* do not reset mtab */ |
1186 | mnt_reset_context(cxt); | |
085f163b | 1187 | |
314f2522 KZ |
1188 | if (rc) |
1189 | return rc; | |
1190 | ||
f958101d RF |
1191 | cxt->mtab = mtab; |
1192 | ||
085f163b KZ |
1193 | do { |
1194 | rc = mnt_table_next_fs(mtab, itr, fs); | |
1195 | if (rc != 0) | |
1196 | return rc; /* no more filesystems (or error) */ | |
1197 | ||
1198 | tgt = mnt_fs_get_target(*fs); | |
1199 | } while (!tgt); | |
1200 | ||
314c3358 KZ |
1201 | DBG(CXT, ul_debugobj(cxt, "next-umount: trying %s [fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]", tgt, |
1202 | mnt_fs_get_fstype(*fs), cxt->fstype_pattern, mnt_fs_get_options(*fs), cxt->optstr_pattern)); | |
085f163b | 1203 | |
d58b3157 | 1204 | /* ignore filesystems which don't match options patterns */ |
0b56c459 | 1205 | if ((cxt->fstype_pattern && !mnt_fs_match_fstype(*fs, |
085f163b KZ |
1206 | cxt->fstype_pattern)) || |
1207 | ||
d58b3157 | 1208 | /* ignore filesystems which don't match type patterns */ |
085f163b KZ |
1209 | (cxt->optstr_pattern && !mnt_fs_match_options(*fs, |
1210 | cxt->optstr_pattern))) { | |
1211 | if (ignored) | |
1212 | *ignored = 1; | |
314c3358 KZ |
1213 | |
1214 | DBG(CXT, ul_debugobj(cxt, "next-umount: not-match")); | |
085f163b KZ |
1215 | return 0; |
1216 | } | |
1217 | ||
1218 | rc = mnt_context_set_fs(cxt, *fs); | |
1219 | if (rc) | |
1220 | return rc; | |
1221 | rc = mnt_context_umount(cxt); | |
1222 | if (mntrc) | |
1223 | *mntrc = rc; | |
1224 | return 0; | |
1225 | } | |
ea848180 KZ |
1226 | |
1227 | ||
1228 | int mnt_context_get_umount_excode( | |
1229 | struct libmnt_context *cxt, | |
1230 | int rc, | |
1231 | char *buf, | |
1232 | size_t bufsz) | |
1233 | { | |
1234 | if (mnt_context_helper_executed(cxt)) | |
1235 | /* | |
1236 | * /sbin/umount.<type> called, return status | |
1237 | */ | |
1238 | return mnt_context_get_helper_status(cxt); | |
1239 | ||
1240 | if (rc == 0 && mnt_context_get_status(cxt) == 1) | |
1241 | /* | |
1242 | * Libmount success && syscall success. | |
1243 | */ | |
1244 | return MNT_EX_SUCCESS; | |
1245 | ||
1246 | if (!mnt_context_syscall_called(cxt)) { | |
1247 | /* | |
1248 | * libmount errors (extra library checks) | |
1249 | */ | |
1250 | if (rc == -EPERM && !mnt_context_tab_applied(cxt)) { | |
1251 | /* failed to evaluate permissions because not found | |
1252 | * relevant entry in mtab */ | |
1253 | if (buf) | |
1254 | snprintf(buf, bufsz, _("not mounted")); | |
1255 | return MNT_EX_USAGE; | |
042f62df RP |
1256 | } |
1257 | ||
1258 | if (rc == -MNT_ERR_LOCK) { | |
d369dc42 KZ |
1259 | if (buf) |
1260 | snprintf(buf, bufsz, _("locking failed")); | |
1261 | return MNT_EX_FILEIO; | |
042f62df RP |
1262 | } |
1263 | ||
1264 | if (rc == -MNT_ERR_NAMESPACE) { | |
8342e584 VD |
1265 | if (buf) |
1266 | snprintf(buf, bufsz, _("failed to switch namespace")); | |
1267 | return MNT_EX_SYSERR; | |
ea848180 KZ |
1268 | } |
1269 | return mnt_context_get_generic_excode(rc, buf, bufsz, | |
1270 | _("umount failed: %m")); | |
1271 | ||
042f62df | 1272 | } if (mnt_context_get_syscall_errno(cxt) == 0) { |
ea848180 KZ |
1273 | /* |
1274 | * umount(2) syscall success, but something else failed | |
1275 | * (probably error in mtab processing). | |
1276 | */ | |
d369dc42 KZ |
1277 | if (rc == -MNT_ERR_LOCK) { |
1278 | if (buf) | |
1279 | snprintf(buf, bufsz, _("filesystem was unmounted, but failed to update userspace mount table")); | |
1280 | return MNT_EX_FILEIO; | |
042f62df RP |
1281 | } |
1282 | ||
1283 | if (rc == -MNT_ERR_NAMESPACE) { | |
8342e584 VD |
1284 | if (buf) |
1285 | snprintf(buf, bufsz, _("filesystem was unmounted, but failed to switch namespace back")); | |
1286 | return MNT_EX_SYSERR; | |
d369dc42 | 1287 | |
042f62df RP |
1288 | } |
1289 | ||
1290 | if (rc < 0) | |
ea848180 KZ |
1291 | return mnt_context_get_generic_excode(rc, buf, bufsz, |
1292 | _("filesystem was unmounted, but any subsequent operation failed: %m")); | |
1293 | ||
1294 | return MNT_EX_SOFTWARE; /* internal error */ | |
1295 | } | |
1296 | ||
1297 | /* | |
1298 | * umount(2) errors | |
1299 | */ | |
1300 | if (buf) { | |
1301 | int syserr = mnt_context_get_syscall_errno(cxt); | |
1302 | ||
1303 | switch (syserr) { | |
1304 | case ENXIO: | |
1305 | snprintf(buf, bufsz, _("invalid block device")); /* ??? */ | |
1306 | break; | |
1307 | case EINVAL: | |
1308 | snprintf(buf, bufsz, _("not mounted")); | |
1309 | break; | |
1310 | case EIO: | |
1311 | snprintf(buf, bufsz, _("can't write superblock")); | |
1312 | break; | |
1313 | case EBUSY: | |
1314 | snprintf(buf, bufsz, _("target is busy")); | |
1315 | break; | |
1316 | case ENOENT: | |
1317 | snprintf(buf, bufsz, _("no mount point specified")); | |
1318 | break; | |
1319 | case EPERM: | |
1320 | snprintf(buf, bufsz, _("must be superuser to unmount")); | |
1321 | break; | |
1322 | case EACCES: | |
1323 | snprintf(buf, bufsz, _("block devices are not permitted on filesystem")); | |
1324 | break; | |
1325 | default: | |
1326 | return mnt_context_get_generic_excode(syserr, buf, bufsz,_("umount(2) system call failed: %m")); | |
1327 | } | |
1328 | } | |
1329 | return MNT_EX_FAIL; | |
1330 | } |