2
* Copyright (C) 2010 Karel Zak <kzak@redhat.com>
4
* This file may be redistributed under the terms of the
5
* GNU Lesser General Public License.
10
* @title: mtab managment
11
* @short_description: userspace mount information management.
13
* The struct libmnt_update provides abstraction to manage mount options in userspace independently on
14
* system configuration. This low-level API works on system with and without /etc/mtab. On
15
* systems without the regular /etc/mtab file are userspace mount options (e.g. user=)
16
* stored to the /run/mount/utab file.
18
* It's recommended to use high-level struct libmnt_context API.
23
#include <sys/types.h>
35
#include "pathnames.h"
37
struct libmnt_update {
41
unsigned long mountflags;
49
static int utab_new_entry(struct libmnt_fs *fs, unsigned long mountflags, struct libmnt_fs **ent);
50
static int set_fs_root(struct libmnt_fs *result, struct libmnt_fs *fs, unsigned long mountflags);
55
* Returns: newly allocated update handler
57
struct libmnt_update *mnt_new_update(void)
59
struct libmnt_update *upd;
61
upd = calloc(1, sizeof(*upd));
66
DBG(UPDATE, mnt_debug_h(upd, "allocate"));
74
* Deallocates struct libmnt_update handler.
76
void mnt_free_update(struct libmnt_update *upd)
81
DBG(UPDATE, mnt_debug_h(upd, "free"));
90
* Returns 0 on success, <0 in case of error.
92
int mnt_update_set_filename(struct libmnt_update *upd, const char *filename,
95
const char *path = NULL;
100
/* filename explicitly defined */
102
char *p = strdup(filename);
106
upd->userspace_only = userspace_only;
114
/* detect tab filename -- /etc/mtab or /run/mount/utab
116
mnt_has_regular_mtab(&path, &rw);
119
mnt_has_regular_utab(&path, &rw);
122
upd->userspace_only = TRUE;
124
upd->filename = strdup(path);
132
* mnt_update_get_filename:
135
* This function returns file name (e.g. /etc/mtab) if the update
136
* should be covered by mnt_lock, otherwise returne NULL.
138
* Returns: pointer to filename that will be updated or NULL in case of error.
140
const char *mnt_update_get_filename(struct libmnt_update *upd)
142
return upd && !upd->userspace_only ? upd->filename : NULL;
146
* mnt_update_is_ready:
147
* @upd: update handler
149
* Returns: 1 if entry described by @upd is successfully prepared and will be
150
* written to mtab/utab file.
152
int mnt_update_is_ready(struct libmnt_update *upd)
154
return upd ? upd->ready : FALSE;
159
* @upd: update handler
160
* @mountflags: MS_* flags
161
* @target: umount target, must be num for mount
162
* @fs: mount filesystem description, must be NULL for umount
164
* Returns: <0 in case on error, 0 on success, 1 if update is unnecessary.
166
int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
167
const char *target, struct libmnt_fs *fs)
172
assert(target || fs);
176
if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs)))
181
DBG(UPDATE, mnt_debug_h(upd,
182
"reseting FS [fs=0x%p, target=%s, flags=0x%08lx]",
183
fs, target, mountflags));
185
DBG(UPDATE, mnt_debug_h(upd, "FS template:"));
186
DBG(UPDATE, mnt_fs_print_debug(fs, stderr));
189
mnt_free_fs(upd->fs);
196
if (mountflags & MS_PROPAGATION)
199
upd->mountflags = mountflags;
201
rc = mnt_update_set_filename(upd, NULL, 0);
203
DBG(UPDATE, mnt_debug_h(upd, "no writable file available [rc=%d]", rc));
204
return rc; /* error or no file available (rc = 1) */
207
upd->target = strdup(target);
212
if (upd->userspace_only && !(mountflags & MS_MOVE)) {
213
int rc = utab_new_entry(fs, mountflags, &upd->fs);
217
upd->fs = mnt_copy_mtab_fs(fs);
225
DBG(UPDATE, mnt_debug_h(upd, "ready"));
234
* Returns: update filesystem entry or NULL
236
struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd)
238
return upd ? upd->fs : NULL;
242
* mnt_update_get_mflags:
245
* Returns: mount flags as was set by mnt_update_set_fs()
247
unsigned long mnt_update_get_mflags(struct libmnt_update *upd)
249
return upd ? upd->mountflags : 0;
253
* mnt_update_force_rdonly:
255
* @rdonly: is read-only?
257
* Returns: 0 on success and negative number in case of error.
259
int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly)
263
if (!upd || !upd->fs)
266
if (rdonly && (upd->mountflags & MS_RDONLY))
268
if (!rdonly && !(upd->mountflags & MS_RDONLY))
271
if (!upd->userspace_only) {
272
/* /etc/mtab -- we care about VFS options there */
273
const char *o = mnt_fs_get_vfs_options(upd->fs);
274
char *n = o ? strdup(o) : NULL;
277
mnt_optstr_remove_option(&n, rdonly ? "rw" : "ro");
278
if (!mnt_optstr_prepend_option(&n, rdonly ? "ro" : "rw", NULL))
279
rc = mnt_fs_set_vfs_options(upd->fs, n);
285
upd->mountflags &= ~MS_RDONLY;
287
upd->mountflags |= MS_RDONLY;
293
* Allocates (but does not write) utab entry for mount/remount. This function
294
* should be called *before* mount(2) syscall.
296
* Returns: 0 on success, negative number on error, 1 if utabs update is
299
static int utab_new_entry(struct libmnt_fs *fs, unsigned long mountflags, struct libmnt_fs **ent)
302
const char *o = NULL, *a = NULL;
307
assert(!(mountflags & MS_MOVE));
313
DBG(UPDATE, mnt_debug("prepare utab entry"));
315
o = mnt_fs_get_user_options(fs);
316
a = mnt_fs_get_attributes(fs);
319
/* remove non-mtab options */
320
rc = mnt_optstr_get_options(o, &u,
321
mnt_get_builtin_optmap(MNT_USERSPACE_MAP),
328
DBG(UPDATE, mnt_debug("utab entry unnecessary (no options)"));
332
/* allocate the entry */
333
*ent = mnt_copy_fs(NULL, fs);
339
rc = mnt_fs_set_user_options(*ent, u);
342
rc = mnt_fs_set_attributes(*ent, a);
346
if (!(mountflags & MS_REMOUNT)) {
347
rc = set_fs_root(*ent, fs, mountflags);
353
DBG(UPDATE, mnt_debug("utab entry OK"));
362
static int set_fs_root(struct libmnt_fs *result, struct libmnt_fs *fs, unsigned long mountflags)
364
char *root = NULL, *mnt = NULL;
366
struct libmnt_table *tb = NULL;
372
DBG(UPDATE, mnt_debug("setting FS root"));
374
fstype = mnt_fs_get_fstype(fs);
377
* bind-mount -- get fs-root and source device for the source filesystem
379
if (mountflags & MS_BIND) {
380
const char *src, *src_root;
381
struct libmnt_fs *src_fs;
383
DBG(UPDATE, mnt_debug("setting FS root: bind"));
385
src = mnt_fs_get_srcpath(fs);
387
rc = mnt_fs_set_bindsrc(result, src);
390
mnt = mnt_get_mountpoint(src);
396
root = mnt_get_fs_root(src, mnt);
398
tb = __mnt_new_table_from_file(_PATH_PROC_MOUNTINFO, MNT_FMT_MOUNTINFO);
400
DBG(UPDATE, mnt_debug("failed to parse mountinfo -- using default"));
403
src_fs = mnt_table_find_target(tb, mnt, MNT_ITER_BACKWARD);
405
DBG(UPDATE, mnt_debug("not found '%s' in mountinfo -- using default", mnt));
409
/* set device name and fs */
410
src = mnt_fs_get_srcpath(src_fs);
411
rc = mnt_fs_set_source(result, src);
415
mnt_fs_set_fstype(result, mnt_fs_get_fstype(src_fs));
417
/* on btrfs the subvolume is used as fs-root in
418
* /proc/self/mountinfo, so we have get the original subvolume
419
* name from src_fs and prepend the subvolume name to the
422
src_root = mnt_fs_get_root(src_fs);
423
if (src_root && !startswith(root, src_root)) {
424
size_t sz = strlen(root) + strlen(src_root) + 1;
425
char *tmp = malloc(sz);
429
snprintf(tmp, sz, "%s%s", src_root, root);
436
* btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
438
else if (fstype && !strcmp(fstype, "btrfs")) {
439
char *vol = NULL, *p;
440
size_t sz, volsz = 0;
442
if (mnt_fs_get_option(fs, "subvol", &vol, &volsz))
445
DBG(UPDATE, mnt_debug("setting FS root: btrfs subvol"));
450
root = malloc(sz + 1);
456
memcpy(p, vol, volsz);
468
DBG(UPDATE, mnt_debug("FS root result: %s", root));
478
/* mtab and fstab update -- returns zero on success
480
static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs)
483
char *m1, *m2, *m3, *m4;
489
o = mnt_fs_strdup_options(fs);
493
m1 = mangle(mnt_fs_get_source(fs));
494
m2 = mangle(mnt_fs_get_target(fs));
495
m3 = mangle(mnt_fs_get_fstype(fs));
498
if (m1 && m2 && m3 && m4) {
499
rc = fprintf(f, "%s %s %s %s %d %d\n",
502
mnt_fs_get_passno(fs));
517
static int fprintf_utab_fs(FILE *f, struct libmnt_fs *fs)
528
p = mangle(mnt_fs_get_source(fs));
530
rc = fprintf(f, "SRC=%s ", p);
534
p = mangle(mnt_fs_get_target(fs));
536
rc = fprintf(f, "TARGET=%s ", p);
541
p = mangle(mnt_fs_get_root(fs));
543
rc = fprintf(f, "ROOT=%s ", p);
548
p = mangle(mnt_fs_get_bindsrc(fs));
550
rc = fprintf(f, "BINDSRC=%s ", p);
555
p = mangle(mnt_fs_get_attributes(fs));
557
rc = fprintf(f, "ATTRS=%s ", p);
562
p = mangle(mnt_fs_get_user_options(fs));
564
rc = fprintf(f, "OPTS=%s", p);
569
rc = fprintf(f, "\n");
572
rc = 0; /* success */
576
static int update_table(struct libmnt_update *upd, struct libmnt_table *tb)
582
if (!tb || !upd->filename)
585
DBG(UPDATE, mnt_debug_h(upd, "%s: updating", upd->filename));
587
fd = mnt_open_uniq_filename(upd->filename, &uq);
589
return fd; /* error */
594
struct libmnt_iter itr;
595
struct libmnt_fs *fs;
598
mnt_reset_iter(&itr, MNT_ITER_FORWARD);
599
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
600
if (upd->userspace_only)
601
rc = fprintf_utab_fs(f, fs);
603
rc = fprintf_mtab_fs(f, fs);
605
DBG(UPDATE, mnt_debug_h(upd,
606
"%s: write entry failed: %m", uq));
611
if (fflush(f) != 0) {
613
DBG(UPDATE, mnt_debug_h(upd, "%s: fflush failed: %m", uq));
618
rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0;
620
if (!rc && stat(upd->filename, &st) == 0)
621
/* Copy uid/gid from the present file before renaming. */
622
rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0;
625
rc = rename(uq, upd->filename) ? -errno : 0;
632
unlink(uq); /* be paranoid */
637
static int utab_lock(struct libmnt_update *upd)
644
assert(upd->filename);
645
assert(upd->userspace_only);
647
if (asprintf(&lfile, "%s.lock", upd->filename) == -1)
650
DBG(UPDATE, mnt_debug("%s: locking", lfile));
652
sigemptyset(&upd->oldsigmask);
654
sigprocmask(SIG_BLOCK, &sigs, &upd->oldsigmask);
656
upd->utab_lock = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, S_IWUSR|
657
S_IRUSR|S_IRGRP|S_IROTH);
660
if (upd->utab_lock < 0) {
665
while (flock(upd->utab_lock, LOCK_EX) < 0) {
670
close(upd->utab_lock);
677
sigprocmask(SIG_SETMASK, &upd->oldsigmask, NULL);
681
static void utab_unlock(struct libmnt_update *upd)
684
assert(upd->userspace_only);
686
if (upd->utab_lock >= 0) {
687
DBG(UPDATE, mnt_debug("unlocking utab"));
688
close(upd->utab_lock);
690
sigprocmask(SIG_SETMASK, &upd->oldsigmask, NULL);
694
static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
696
struct libmnt_table *tb;
702
DBG(UPDATE, mnt_debug_h(upd, "%s: add entry", upd->filename));
704
if (upd->userspace_only)
707
rc = mnt_lock_file(lc);
711
tb = __mnt_new_table_from_file(upd->filename,
712
upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
714
struct libmnt_fs *fs = mnt_copy_fs(NULL, upd->fs);
718
mnt_table_add_fs(tb, fs);
719
rc = update_table(upd, tb);
723
if (upd->userspace_only)
732
static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
734
struct libmnt_table *tb;
740
DBG(UPDATE, mnt_debug_h(upd, "%s: remove entry", upd->filename));
742
if (upd->userspace_only)
745
rc = mnt_lock_file(lc);
749
tb = __mnt_new_table_from_file(upd->filename,
750
upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
752
struct libmnt_fs *rem = mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD);
754
mnt_table_remove_fs(tb, rem);
755
rc = update_table(upd, tb);
760
if (upd->userspace_only)
769
static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc)
771
struct libmnt_table *tb = NULL;
774
DBG(UPDATE, mnt_debug_h(upd, "%s: modify target", upd->filename));
776
if (upd->userspace_only)
779
rc = mnt_lock_file(lc);
783
tb = __mnt_new_table_from_file(upd->filename,
784
upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
786
struct libmnt_fs *cur = mnt_table_find_target(tb,
787
mnt_fs_get_srcpath(upd->fs), MNT_ITER_BACKWARD);
789
rc = mnt_fs_set_target(cur, mnt_fs_get_target(upd->fs));
791
rc = update_table(upd, tb);
795
if (upd->userspace_only)
804
static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc)
806
struct libmnt_table *tb = NULL;
808
struct libmnt_fs *fs;
813
DBG(UPDATE, mnt_debug_h(upd, "%s: modify options", upd->filename));
817
if (upd->userspace_only)
820
rc = mnt_lock_file(lc);
824
tb = __mnt_new_table_from_file(upd->filename,
825
upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
827
struct libmnt_fs *cur = mnt_table_find_target(tb,
828
mnt_fs_get_target(fs),
831
if (upd->userspace_only)
832
rc = mnt_fs_set_attributes(cur, mnt_fs_get_attributes(fs));
833
if (!rc && !upd->userspace_only)
834
rc = mnt_fs_set_vfs_options(cur, mnt_fs_get_vfs_options(fs));
835
if (!rc && !upd->userspace_only)
836
rc = mnt_fs_set_fs_options(cur, mnt_fs_get_fs_options(fs));
838
rc = mnt_fs_set_user_options(cur,
839
mnt_fs_get_user_options(fs));
841
rc = update_table(upd, tb);
845
if (upd->userspace_only)
859
* High-level API to update /etc/mtab (or private /run/mount/utab file).
861
* The @lc lock is optional and will be created if necessary. Note that
862
* the automatically created lock blocks all signals.
864
* See also mnt_lock_block_signals() and mnt_context_get_lock().
866
* Returns: 0 on success, negative number on error.
868
int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
870
struct libmnt_lock *lc0 = lc;
875
if (!upd->filename || !upd)
880
DBG(UPDATE, mnt_debug_h(upd, "%s: update tab", upd->filename));
882
DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr));
884
if (!lc && !upd->userspace_only) {
885
lc = mnt_new_lock(upd->filename, 0);
887
mnt_lock_block_signals(lc, TRUE);
890
if (!upd->fs && upd->target)
891
rc = update_remove_entry(upd, lc); /* umount */
892
else if (upd->mountflags & MS_MOVE)
893
rc = update_modify_target(upd, lc); /* move */
894
else if (upd->mountflags & MS_REMOUNT)
895
rc = update_modify_options(upd, lc); /* remount */
897
rc = update_add_entry(upd, lc); /* mount */
900
DBG(UPDATE, mnt_debug_h(upd, "%s: update tab: done [rc=%d]",
911
static int update(const char *target, struct libmnt_fs *fs, unsigned long mountflags)
914
struct libmnt_update *upd;
916
DBG(UPDATE, mnt_debug("update test"));
918
upd = mnt_new_update();
922
rc = mnt_update_set_fs(upd, mountflags, target, fs);
924
/* update is unnecessary */
929
fprintf(stderr, "failed to set FS\n");
933
/* [... here should be mount(2) call ...] */
935
rc = mnt_update_table(upd, NULL);
940
static int test_add(struct libmnt_test *ts, int argc, char *argv[])
942
struct libmnt_fs *fs = mnt_new_fs();
947
mnt_fs_set_source(fs, argv[1]);
948
mnt_fs_set_target(fs, argv[2]);
949
mnt_fs_set_fstype(fs, argv[3]);
950
mnt_fs_set_options(fs, argv[4]);
952
rc = update(NULL, fs, 0);
957
static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
961
return update(argv[1], NULL, 0);
964
static int test_move(struct libmnt_test *ts, int argc, char *argv[])
966
struct libmnt_fs *fs = mnt_new_fs();
971
mnt_fs_set_source(fs, argv[1]);
972
mnt_fs_set_target(fs, argv[2]);
974
rc = update(NULL, fs, MS_MOVE);
980
static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
982
struct libmnt_fs *fs = mnt_new_fs();
987
mnt_fs_set_target(fs, argv[1]);
988
mnt_fs_set_options(fs, argv[2]);
990
rc = update(NULL, fs, MS_REMOUNT);
995
int main(int argc, char *argv[])
997
struct libmnt_test tss[] = {
998
{ "--add", test_add, "<src> <target> <type> <options> add line to mtab" },
999
{ "--remove", test_remove, "<target> MS_REMOUNT mtab change" },
1000
{ "--move", test_move, "<old_target> <target> MS_MOVE mtab change" },
1001
{ "--remount",test_remount, "<target> <options> MS_REMOUNT mtab change" },
1005
return mnt_run_test(tss, argc, argv);
1008
#endif /* TEST_PROGRAM */