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