]>
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 | ||
8 | #include <stdio.h> | |
9 | #include <stdlib.h> | |
10 | #include <sys/types.h> | |
11 | #include <sys/stat.h> | |
12 | #include <unistd.h> | |
13 | #include <string.h> | |
14 | #include <errno.h> | |
15 | ||
16 | #include <sys/wait.h> | |
17 | #include <sys/mount.h> | |
18 | ||
19 | #include "c.h" | |
20 | #include "pathnames.h" | |
21 | #include "strutils.h" | |
22 | #include "mountP.h" | |
23 | ||
24 | static int lookup_umount_fs(mnt_context *cxt) | |
25 | { | |
26 | int rc; | |
27 | const char *tgt; | |
28 | mnt_tab *mtab; | |
29 | mnt_fs *fs; | |
30 | ||
1b56aae8 KZ |
31 | assert(cxt); |
32 | assert(cxt->fs); | |
33 | ||
21193a48 KZ |
34 | DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS")); |
35 | ||
ea8f06f9 KZ |
36 | tgt = mnt_fs_get_target(cxt->fs); |
37 | if (!tgt) { | |
38 | DBG(CXT, mnt_debug_h(cxt, "umount: undefined target")); | |
39 | return -EINVAL; | |
40 | } | |
41 | rc = mnt_context_get_mtab(cxt, &mtab); | |
42 | if (rc) { | |
43 | DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab")); | |
44 | return rc; | |
45 | } | |
46 | fs = mnt_tab_find_target(mtab, tgt, MNT_ITER_BACKWARD); | |
47 | if (!fs) { | |
48 | /* maybe the option is source rather than target (mountpoint) */ | |
49 | fs = mnt_tab_find_source(mtab, tgt, MNT_ITER_BACKWARD); | |
50 | ||
51 | if (fs) { | |
52 | mnt_fs *fs1 = mnt_tab_find_target(mtab, | |
53 | mnt_fs_get_target(fs), | |
54 | MNT_ITER_BACKWARD); | |
55 | if (!fs1) { | |
56 | DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!")); | |
57 | return -EINVAL; | |
58 | } | |
59 | if (fs != fs1) { | |
60 | /* Something was stacked over `file' on the | |
61 | * same mount point. */ | |
62 | DBG(CXT, mnt_debug_h(cxt, | |
63 | "umount: %s: %s is mounted " | |
64 | "over it on the same point", | |
65 | tgt, mnt_fs_get_source(fs1))); | |
66 | return -EINVAL; | |
67 | } | |
68 | } | |
69 | } | |
70 | ||
71 | if (!fs) { | |
72 | DBG(CXT, mnt_debug_h(cxt, "cannot found %s in mtab", tgt)); | |
73 | return 0; | |
74 | } | |
75 | ||
76 | /* copy from mtab/fstab to our FS description | |
77 | */ | |
78 | rc = mnt_fs_set_source(cxt->fs, mnt_fs_get_source(fs)); | |
79 | if (!rc) | |
80 | rc = mnt_fs_set_target(cxt->fs, mnt_fs_get_target(fs)); | |
81 | ||
82 | if (!rc && !mnt_fs_get_fstype(cxt->fs)) | |
83 | rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs)); | |
76a06ca4 KZ |
84 | |
85 | if (!rc) | |
86 | rc = mnt_fs_set_vfs_options(cxt->fs, mnt_fs_get_vfs_options(fs)); | |
ea8f06f9 | 87 | if (!rc) |
76a06ca4 KZ |
88 | rc = mnt_fs_set_fs_options(cxt->fs, mnt_fs_get_fs_options(fs)); |
89 | if (!rc) | |
90 | rc = mnt_fs_set_userspace_options(cxt->fs, mnt_fs_get_userspace_options(fs)); | |
91 | ||
dd369652 KZ |
92 | if (!rc && mnt_fs_get_bindsrc(fs)) |
93 | rc = mnt_fs_set_bindsrc(cxt->fs, mnt_fs_get_bindsrc(fs)); | |
ea8f06f9 | 94 | |
dd369652 | 95 | DBG(CXT, mnt_debug_h(cxt, "umount: mtab applied")); |
ea8f06f9 KZ |
96 | cxt->flags |= MNT_FL_TAB_APPLIED; |
97 | return rc; | |
98 | } | |
99 | ||
100 | /* check if @devname is loopdev and if the device is associated | |
101 | * with a source from @fstab_fs | |
102 | * | |
103 | * TODO : move this to loopdev.c | |
104 | */ | |
105 | static int mnt_loopdev_associated_fs(const char *devname, mnt_fs *fs) | |
106 | { | |
107 | uintmax_t offset = 0; | |
108 | const char *src; | |
109 | char *val, *optstr; | |
110 | size_t valsz; | |
111 | ||
112 | /* check if it begins with /dev/loop */ | |
113 | if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP))) | |
114 | return 0; | |
115 | ||
116 | src = mnt_fs_get_srcpath(fs); | |
117 | if (!src) | |
118 | return 0; | |
119 | ||
120 | /* check for offset option in @fs */ | |
76a06ca4 | 121 | optstr = (char *) mnt_fs_get_userspace_options(fs); |
ea8f06f9 KZ |
122 | if (optstr && !mnt_optstr_get_option(optstr, "offset=", &val, &valsz)) { |
123 | int rc; | |
124 | ||
125 | val = strndup(val, valsz); | |
126 | if (!val) | |
127 | return 0; | |
128 | rc = strtosize(val, &offset); | |
129 | free(val); | |
130 | if (rc) | |
131 | return 0; | |
132 | } | |
133 | ||
134 | /* TODO: | |
135 | * if (mnt_loopdev_associated_file(devname, src, offset)) | |
136 | * return 1; | |
137 | */ | |
138 | return 0; | |
139 | } | |
140 | ||
141 | /* | |
142 | * Note that cxt->fs contains relevant mtab entry! | |
143 | */ | |
144 | static int evaluate_permissions(mnt_context *cxt) | |
145 | { | |
146 | mnt_tab *fstab; | |
766af80b | 147 | unsigned long u_flags = 0; |
ea8f06f9 KZ |
148 | const char *tgt, *src, *optstr; |
149 | int rc, ok = 0; | |
150 | mnt_fs *fs; | |
151 | ||
1b56aae8 KZ |
152 | assert(cxt); |
153 | assert(cxt->fs); | |
154 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
155 | ||
ea8f06f9 KZ |
156 | if (!cxt || !cxt->fs) |
157 | return -EINVAL; | |
158 | ||
159 | if (!mnt_context_is_restricted(cxt)) | |
160 | return 0; /* superuser mount */ | |
161 | ||
dd369652 KZ |
162 | DBG(CXT, mnt_debug_h(cxt, "umount: evaluating permissions")); |
163 | ||
ea8f06f9 KZ |
164 | if (!(cxt->flags & MNT_FL_TAB_APPLIED)) { |
165 | DBG(CXT, mnt_debug_h(cxt, | |
166 | "cannot found %s in mtab and you are not root", | |
167 | mnt_fs_get_target(cxt->fs))); | |
168 | goto eperm; | |
169 | } | |
170 | ||
1b56aae8 KZ |
171 | if (!(cxt->flags & MNT_FL_NOHELPERS) && |
172 | (cxt->user_mountflags & MNT_MS_UHELPER)) { | |
ea8f06f9 | 173 | |
ea8f06f9 | 174 | char *suffix = NULL; |
76a06ca4 | 175 | char *o = (char *) mnt_fs_get_userspace_options(cxt->fs); |
ea8f06f9 KZ |
176 | size_t valsz; |
177 | ||
178 | rc = mnt_optstr_get_option(o, "uhelper", &suffix, &valsz); | |
179 | if (!rc) { | |
180 | suffix = strndup(suffix, valsz); | |
181 | if (!suffix) | |
182 | return -ENOMEM; | |
183 | rc = mnt_context_prepare_helper(cxt, "umount", suffix); | |
184 | } | |
185 | if (rc < 0) | |
186 | return rc; | |
187 | if (cxt->helper) | |
188 | return 0; /* we'll call /sbin/umount.<uhelper> */ | |
189 | } | |
190 | ||
191 | /* | |
192 | * User mounts has to be in /etc/fstab | |
193 | */ | |
194 | rc = mnt_context_get_fstab(cxt, &fstab); | |
195 | if (rc) | |
196 | return rc; | |
197 | ||
198 | tgt = mnt_fs_get_target(cxt->fs); | |
199 | src = mnt_fs_get_source(cxt->fs); | |
200 | ||
dd369652 KZ |
201 | if (mnt_fs_get_bindsrc(cxt->fs)) { |
202 | src = mnt_fs_get_bindsrc(cxt->fs); | |
203 | DBG(CXT, mnt_debug_h(cxt, | |
204 | "umount: using bind source: %s", src)); | |
205 | } | |
206 | ||
ea8f06f9 KZ |
207 | /* If fstab contains the two lines |
208 | * /dev/sda1 /mnt/zip auto user,noauto 0 0 | |
209 | * /dev/sda4 /mnt/zip auto user,noauto 0 0 | |
210 | * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail. | |
211 | * So, we must not look for file, but for the pair (dev,file) in fstab. | |
212 | */ | |
213 | fs = mnt_tab_find_pair(fstab, src, tgt, MNT_ITER_FORWARD); | |
214 | if (!fs) { | |
215 | /* | |
216 | * It's possible that there is /path/file.img in fstab and | |
217 | * /dev/loop0 in mtab -- then we have to check releation | |
218 | * between loopdev and the file. | |
219 | */ | |
220 | fs = mnt_tab_find_target(fstab, tgt, MNT_ITER_FORWARD); | |
221 | if (fs) { | |
222 | const char *dev = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */ | |
223 | ||
224 | if (!dev || !mnt_loopdev_associated_fs(dev, fs)) | |
225 | fs = NULL; | |
226 | } | |
227 | if (!fs) { | |
228 | DBG(CXT, mnt_debug_h(cxt, | |
229 | "umount %s: mtab disagrees with fstab", | |
230 | tgt)); | |
231 | goto eperm; | |
232 | } | |
233 | } | |
234 | ||
235 | /* | |
236 | * User mounting and unmounting is allowed only if fstab contains one | |
237 | * of the options `user', `users' or `owner' or `group'. | |
238 | * | |
239 | * The option `users' allows arbitrary users to mount and unmount - | |
240 | * this may be a security risk. | |
241 | * | |
242 | * The options `user', `owner' and `group' only allow unmounting by the | |
243 | * user that mounted (visible in mtab). | |
244 | */ | |
76a06ca4 | 245 | optstr = mnt_fs_get_userspace_options(fs); /* FSTAB mount options! */ |
ea8f06f9 KZ |
246 | if (!optstr) |
247 | goto eperm; | |
248 | ||
76a06ca4 KZ |
249 | if (mnt_optstr_get_flags(optstr, &u_flags, |
250 | mnt_get_builtin_optmap(MNT_USERSPACE_MAP))) | |
ea8f06f9 KZ |
251 | goto eperm; |
252 | ||
766af80b KZ |
253 | if (u_flags & MNT_MS_USERS) { |
254 | DBG(CXT, mnt_debug_h(cxt, | |
255 | "umount: promiscuous setting ('users') in fstab")); | |
ea8f06f9 | 256 | return 0; |
766af80b | 257 | } |
ea8f06f9 KZ |
258 | /* |
259 | * Check user=<username> setting from mtab if there is user, owner or | |
260 | * group option in /etc/fstab | |
261 | */ | |
262 | if ((u_flags & MNT_MS_USER) || (u_flags & MNT_MS_OWNER) || | |
263 | (u_flags & MNT_MS_GROUP)) { | |
264 | ||
766af80b | 265 | char *curr_user = NULL; |
ea8f06f9 KZ |
266 | char *mtab_user = NULL; |
267 | size_t sz; | |
268 | ||
766af80b KZ |
269 | DBG(CXT, mnt_debug_h(cxt, |
270 | "umount: checking user=<username> from mtab")); | |
271 | ||
272 | curr_user = mnt_get_username(getuid()); | |
273 | ||
ea8f06f9 KZ |
274 | if (!curr_user) { |
275 | DBG(CXT, mnt_debug_h(cxt, "umount %s: cannot " | |
766af80b | 276 | "convert %d to username", tgt, getuid())); |
ea8f06f9 KZ |
277 | goto eperm; |
278 | } | |
279 | ||
280 | /* get options from mtab */ | |
76a06ca4 | 281 | optstr = mnt_fs_get_userspace_options(cxt->fs); |
ea8f06f9 KZ |
282 | if (optstr && !mnt_optstr_get_option((char *) optstr, |
283 | "user", &mtab_user, &sz) && sz) | |
284 | ok = !strncmp(curr_user, mtab_user, sz); | |
285 | } | |
286 | ||
287 | if (ok) { | |
288 | DBG(CXT, mnt_debug_h(cxt, "umount %s is allowed", tgt)); | |
289 | return 0; | |
290 | } | |
291 | eperm: | |
292 | DBG(CXT, mnt_debug_h(cxt, "umount %s is not allowed for you", tgt)); | |
293 | return -EPERM; | |
294 | } | |
295 | ||
296 | static int exec_helper(mnt_context *cxt) | |
297 | { | |
298 | int rc; | |
299 | ||
300 | assert(cxt); | |
301 | assert(cxt->fs); | |
302 | assert(cxt->helper); | |
1b56aae8 | 303 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); |
97e23b5e | 304 | assert(cxt->helper_exec_status == 1); |
ea8f06f9 KZ |
305 | |
306 | DBG_FLUSH; | |
307 | ||
308 | switch (fork()) { | |
309 | case 0: | |
310 | { | |
311 | const char *args[10], *type; | |
312 | int i = 0; | |
313 | ||
314 | if (setgid(getgid()) < 0) | |
315 | exit(EXIT_FAILURE); | |
316 | ||
317 | if (setuid(getuid()) < 0) | |
318 | exit(EXIT_FAILURE); | |
319 | ||
320 | type = mnt_fs_get_fstype(cxt->fs); | |
321 | ||
322 | args[i++] = cxt->helper; /* 1 */ | |
323 | args[i++] = mnt_fs_get_target(cxt->fs); /* 2 */ | |
324 | ||
325 | if (cxt->flags & MNT_FL_NOMTAB) | |
326 | args[i++] = "-n"; /* 3 */ | |
327 | if (cxt->flags & MNT_FL_LAZY) | |
328 | args[i++] = "-l"; /* 4 */ | |
329 | if (cxt->flags & MNT_FL_FORCE) | |
330 | args[i++] = "-f"; /* 5 */ | |
331 | if (cxt->flags & MNT_FL_VERBOSE) | |
332 | args[i++] = "-v"; /* 6 */ | |
333 | if (cxt->flags & MNT_FL_RDONLY_UMOUNT) | |
334 | args[i++] = "-r"; /* 7 */ | |
335 | if (type && !endswith(cxt->helper, type)) { | |
336 | args[i++] = "-t"; /* 8 */ | |
337 | args[i++] = (char *) type; /* 9 */ | |
338 | } | |
339 | ||
340 | args[i] = NULL; /* 10 */ | |
341 | #ifdef CONFIG_LIBMOUNT_DEBUG | |
342 | i = 0; | |
343 | for (i = 0; args[i]; i++) | |
344 | DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"", | |
345 | i, args[i])); | |
346 | #endif | |
347 | DBG_FLUSH; | |
348 | execv(cxt->helper, (char * const *) args); | |
349 | exit(EXIT_FAILURE); | |
350 | } | |
351 | default: | |
352 | { | |
353 | int st; | |
354 | wait(&st); | |
355 | cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1; | |
356 | ||
357 | DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]", | |
358 | cxt->helper, cxt->helper_status)); | |
97e23b5e | 359 | cxt->helper_exec_status = rc = 0; |
ea8f06f9 KZ |
360 | break; |
361 | } | |
362 | ||
363 | case -1: | |
97e23b5e | 364 | cxt->helper_exec_status = rc = -errno; |
ea8f06f9 KZ |
365 | DBG(CXT, mnt_debug_h(cxt, "fork() failed")); |
366 | break; | |
367 | } | |
368 | ||
369 | return rc; | |
370 | } | |
371 | ||
372 | static int do_umount(mnt_context *cxt) | |
373 | { | |
374 | int rc = 0; | |
375 | const char *src, *target; | |
376 | ||
377 | assert(cxt); | |
378 | assert(cxt->fs); | |
1b56aae8 | 379 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); |
97e23b5e | 380 | assert(cxt->syscall_status == 1); |
ea8f06f9 KZ |
381 | |
382 | if (cxt->helper) | |
383 | return exec_helper(cxt); | |
384 | ||
385 | src = mnt_fs_get_srcpath(cxt->fs); | |
386 | target = mnt_fs_get_target(cxt->fs); | |
387 | ||
388 | if (!target) | |
389 | return -EINVAL; | |
390 | ||
391 | if (cxt->flags & MNT_FL_FAKE) | |
392 | return 0; | |
393 | ||
394 | if (cxt->flags & MNT_FL_LAZY) | |
395 | rc = umount2(target, MNT_DETACH); | |
396 | ||
397 | else if (cxt->flags & MNT_FL_FORCE) { | |
398 | rc = umount2(target, MNT_FORCE); | |
399 | ||
400 | if (rc < 0 && errno == ENOSYS) | |
401 | rc = umount(target); | |
402 | } else | |
403 | rc = umount(target); | |
404 | ||
405 | if (rc < 0) | |
97e23b5e | 406 | cxt->syscall_status = -errno; |
ea8f06f9 KZ |
407 | /* |
408 | * try remount read-only | |
409 | */ | |
97e23b5e | 410 | if (rc < 0 && cxt->syscall_status == -EBUSY && |
ea8f06f9 KZ |
411 | (cxt->flags & MNT_FL_RDONLY_UMOUNT) && src) { |
412 | ||
413 | cxt->mountflags |= MS_REMOUNT | MS_RDONLY; | |
414 | cxt->flags &= ~MNT_FL_LOOPDEL; | |
415 | DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d] -- " | |
416 | "tring remount read-only", | |
97e23b5e | 417 | -cxt->syscall_status)); |
ea8f06f9 KZ |
418 | |
419 | rc = mount(src, target, NULL, | |
420 | MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); | |
421 | if (rc < 0) { | |
97e23b5e | 422 | cxt->syscall_status = -errno; |
ea8f06f9 KZ |
423 | DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) failed " |
424 | "[errno=%d]", | |
97e23b5e KZ |
425 | -cxt->syscall_status)); |
426 | return cxt->syscall_status; | |
ea8f06f9 | 427 | } |
97e23b5e | 428 | cxt->syscall_status = 0; |
ea8f06f9 KZ |
429 | DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) success")); |
430 | return 0; | |
431 | } | |
432 | ||
433 | if (rc < 0) { | |
434 | DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d]", | |
97e23b5e KZ |
435 | -cxt->syscall_status)); |
436 | return -cxt->syscall_status; | |
ea8f06f9 | 437 | } |
21193a48 | 438 | cxt->syscall_status = 0; |
ea8f06f9 KZ |
439 | DBG(CXT, mnt_debug_h(cxt, "umount(2) success")); |
440 | return 0; | |
441 | } | |
442 | ||
443 | /** | |
1d0cd73f | 444 | * mnt_context_do_umount: |
ea8f06f9 KZ |
445 | * @cxt: mount context |
446 | * | |
0f32f1e2 | 447 | * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.type). |
ea8f06f9 | 448 | * |
1d0cd73f | 449 | * See also mnt_context_disable_helpers(). |
ea8f06f9 KZ |
450 | * |
451 | * Returns: 0 on success, and negative number in case of error. | |
452 | */ | |
1d0cd73f | 453 | int mnt_context_do_umount(mnt_context *cxt) |
ea8f06f9 | 454 | { |
1d0cd73f | 455 | int rc; |
ea8f06f9 | 456 | |
1d0cd73f | 457 | if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP)) |
ea8f06f9 | 458 | return -EINVAL; |
1d0cd73f | 459 | if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs)) |
ea8f06f9 KZ |
460 | return -EINVAL; |
461 | ||
462 | free(cxt->helper); /* be paranoid */ | |
463 | cxt->helper = NULL; | |
464 | ||
1d0cd73f KZ |
465 | cxt->action = MNT_ACT_UMOUNT; |
466 | ||
ea8f06f9 | 467 | rc = lookup_umount_fs(cxt); |
1b56aae8 KZ |
468 | if (!rc) |
469 | rc = mnt_context_merge_mountflags(cxt); | |
ea8f06f9 KZ |
470 | if (!rc) |
471 | rc = evaluate_permissions(cxt); | |
472 | if (!rc && !cxt->helper) | |
473 | rc = mnt_context_prepare_helper(cxt, "umount", NULL); | |
474 | /* TODO | |
475 | if ((cxt->flags & MNT_FL_LOOPDEL) && | |
476 | (!mnt_is_loopdev(src) || mnt_loopdev_is_autoclear(src))) | |
477 | cxt->flags &= ~MNT_FL_LOOPDEL; | |
478 | */ | |
479 | if (!rc) | |
1d0cd73f KZ |
480 | rc = mnt_context_prepare_update(cxt); |
481 | if (rc) { | |
482 | DBG(CXT, mnt_debug_h(cxt, "prepared umount failed")); | |
483 | return rc; | |
ea8f06f9 KZ |
484 | } |
485 | ||
1d0cd73f KZ |
486 | rc = do_umount(cxt); |
487 | if (rc) | |
488 | return rc; | |
ea8f06f9 KZ |
489 | /* TODO |
490 | if (cxt->flags & MNT_FL_LOOPDEL) | |
491 | rc = mnt_loopdev_clean(mnt_fs_get_source(cxt->fs)); | |
492 | */ | |
493 | if (cxt->flags & MNT_FL_NOMTAB) | |
494 | return rc; | |
495 | ||
496 | if ((cxt->flags & MNT_FL_RDONLY_UMOUNT) && | |
497 | (cxt->mountflags & (MS_RDONLY | MS_REMOUNT))) { | |
498 | /* | |
36bda5cb | 499 | * remount --> read-only mount |
ea8f06f9 | 500 | */ |
76a06ca4 | 501 | const char *o = mnt_fs_get_vfs_options(cxt->fs); |
1d0cd73f | 502 | char *n = o ? strdup(o) : NULL; |
ea8f06f9 | 503 | |
21193a48 KZ |
504 | DBG(CXT, mnt_debug_h(cxt, "fix remount-on-umount update")); |
505 | ||
1d0cd73f KZ |
506 | if (n) |
507 | mnt_optstr_remove_option(&n, "rw"); | |
508 | rc = mnt_optstr_prepend_option(&n, "ro", NULL); | |
ea8f06f9 | 509 | if (!rc) |
76a06ca4 | 510 | rc = mnt_fs_set_vfs_options(cxt->fs, n); |
1d0cd73f | 511 | |
36bda5cb | 512 | /* use "remount" instead of "umount" in /etc/mtab */ |
77417bc0 | 513 | if (!rc && cxt->update && cxt->mtab_writable) |
1d0cd73f KZ |
514 | rc = mnt_update_set_fs(cxt->update, |
515 | cxt->mountflags, NULL, cxt->fs); | |
ea8f06f9 | 516 | } |
ea8f06f9 | 517 | |
1d0cd73f | 518 | return rc ? : mnt_context_update_tabs(cxt); |
ea8f06f9 | 519 | } |