]> err.no Git - util-linux/commitdiff
libmount: add support for mtab managment
authorKarel Zak <kzak@redhat.com>
Thu, 5 Aug 2010 11:47:37 +0000 (13:47 +0200)
committerKarel Zak <kzak@redhat.com>
Mon, 3 Jan 2011 11:28:41 +0000 (12:28 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/mount/src/Makefile.am
shlibs/mount/src/mountP.h
shlibs/mount/src/mtab.c [new file with mode: 0644]
shlibs/mount/src/optstr.c

index 37a5177e6fab3a43bdbec5ee65817ba75efbc7e7..4576dd0cb5667f745143f5647afd71a25245b3ab 100644 (file)
@@ -11,7 +11,7 @@ nodist_mountinc_HEADERS = mount.h
 usrlib_exec_LTLIBRARIES = libmount.la
 libmount_la_SOURCES =  mountP.h version.c utils.c test.c init.c cache.c \
                        optstr.c optmap.c optent.c optls.c iter.c lock.c \
-                       fs.c tab.c tab_parse.c \
+                       fs.c tab.c tab_parse.c mtab.c \
                        $(mountinc_HEADERS) \
                        $(top_srcdir)/lib/at.c \
                        $(top_srcdir)/include/list.h \
@@ -48,7 +48,7 @@ uninstall-hook:
 
 
 tests = test_version test_cache test_optstr test_optls test_lock \
-                 test_tab test_utils
+                 test_tab test_utils test_mtab
 
 tests: all $(tests)
 test_%: %.c
index ea6a87c5c49dd110517bc7cbfb68c3fc25e47c94..bc584e3eeff2355c920cdceba9ab56d21afe3177 100644 (file)
@@ -168,14 +168,6 @@ struct _mnt_fs {
 #define MNT_FS_PSEUDO  (1 << 1) /* pseudo filesystem */
 #define MNT_FS_NET     (1 << 2) /* network filesystem */
 
-/*
- * File format
- */
-enum {
-       MNT_FMT_FSTAB = 1,              /* /etc/{fs,m}tab */
-       MNT_FMT_MOUNTINFO               /* /proc/#/mountinfo */
-};
-
 /*
  * mtab/fstab/mountinfo file
  */
diff --git a/shlibs/mount/src/mtab.c b/shlibs/mount/src/mtab.c
new file mode 100644 (file)
index 0000000..81b01f2
--- /dev/null
@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: mtab
+ * @title: mtab managment
+ * @short_description: userspace mount information management
+ *
+ * The libmount library allows to use more modes for mtab management:
+ *
+ * - /etc/mtab is regular file
+ *
+ *   then libmount manages the file in classical way (all mounts are
+ *   added to the file)
+ *
+ * - /etc/mtab is symlink
+ *
+ *   then libmount ignores mtab at all
+ *
+ * - /etc/mtab is symlink and /var/run/mount/ directory exists
+ *
+ *   then libmount stores userspace specific mount options to the
+ *   /var/run/mount/mountinfo file (the file format compatible to
+ *   /proc/self/mountinfo)
+ *
+ *
+ * The mtab is always updated in two steps. The first step is to prepare a new
+ * mtab entry -- mnt_mtab_prepare_update(), this step has to be done before
+ * mount(2) syscall. The second step is to update the mtab file --
+ * mnt_update_mtab(), this step should be done after mount(2) syscall.
+ *
+ * The mnt_update_mtab() behaviour is undefined if mnt_mtab_prepare_update() has
+ * not been used.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "c.h"
+#include "mountP.h"
+#include "mangle.h"
+#include "pathnames.h"
+
+
+
+/*
+ * mtab update description
+ */
+struct _mnt_mtab {
+       int             action;         /* MNT_ACT_{MOUNT,UMOUNT} */
+       int             mountflags;     /* MS_* flags */
+       char            *filename;      /* usually /etc/mtab or /var/run/mount/mountinfo */
+       char            *old_target;    /* for MS_MOVE */
+       int             format;         /* MNT_FMT_{MTAB,MOUNTINFO} */
+       int             nolock;         /* don't alloca private mnt_lock */
+       mnt_fs          *fs;            /* entry */
+       mnt_lock        *lc;            /* lock or NULL */
+};
+
+/**
+ * mnt_new_mtab:
+ * @action: MNT_ACT_{MOUNT,UMOUNT}
+ *
+ * Returns: newly allocated mtab description
+ */
+mnt_mtab *mnt_new_mtab(int action)
+{
+       mnt_mtab *mt;
+
+       mt = calloc(1, sizeof(struct _mnt_mtab));
+       if (!mt)
+               return NULL;
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: allocate\n", mt));
+
+       mt->action = action;
+       mt->fs = mnt_new_fs();
+       if (!mt->fs)
+               goto err;
+       return mt;
+err:
+       mnt_free_fs(mt->fs);
+       free(mt);
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab: failed to allocate handler\n"));
+
+       return NULL;
+}
+
+/**
+ * mnt_free_mtab:
+ * @mt: mtab
+ *
+ * Deallocates mnt_mtab handler.
+ */
+void mnt_free_mtab(mnt_mtab *mt)
+{
+       if (!mt)
+               return;
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: deallacate\n", mt));
+
+       mnt_free_lock(mt->lc);
+       free(mt->filename);
+       free(mt);
+}
+
+/**
+ * mnt_mtab_set_filename:
+ * @mt: mtab
+ * @filename: path to mtab (default is /etc/mtab or /var/run/mount/mountinfo)
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_filename(mnt_mtab *mt, const char *filename)
+{
+       char *p = NULL;
+       assert(mt);
+
+       if (!mt)
+               return -1;
+       if (filename) {
+               p = strdup(filename);
+               if (!p)
+                       return -1;
+       }
+       free(mt->filename);
+       mt->filename = p;
+       return 0;
+}
+
+/**
+ * mnt_mtab_set_action:
+ * @mt: mtab
+ * @action: MNT_ACT_{MOUNT,UMOUNT}
+ *
+ * Overwrites the previously defined action by mnt_new_mtab().
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_action(mnt_mtab *mt, int action)
+{
+       assert(mt);
+       if (!mt)
+               return -1;
+       mt->action = action;
+       return 0;
+}
+
+/**
+ * mnt_mtab_set_format:
+ * @mt: mtab
+ * @format: MNT_FMT_{MTAB,MOUNTINFO}
+ *
+ * Sets mtab file format, default is MNT_FMT_MTAB for paths that end with
+ * "mtab" and MNT_FMT_MOUNTINFO for paths that end with "mountinfo".
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_format(mnt_mtab *mt, int format)
+{
+       assert(mt);
+       if (!mt)
+               return -1;
+       mt->format = format;
+       return 0;
+}
+
+/**
+ * mnt_mtab_set_optstr:
+ * @mt: mtab
+ * @optstr: mount options that will be used for mount(2)
+ *
+ * Note that mnt_mtab_prepare_update() will remove options that does not belong
+ * to mtab.
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_optstr(mnt_mtab *mt, const char *optstr)
+{
+       if (!mt)
+               return -1;
+       return mnt_fs_set_optstr(mt->fs, optstr);
+}
+
+/**
+ * mnt_mtab_set_mountflags:
+ * @mt: mtab
+ * @flags: MS_{REMOUNT,MOVE}
+ *
+ * Sets mount flags for mount/umount action. The flags are also
+ * extracted from mount options by mnt_mtab_prepare_update().
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_mountflags(mnt_mtab *mt, int flags)
+{
+       assert(mt);
+       if (!mt)
+               return -1;
+       mt->mountflags = flags;
+       return 0;
+}
+
+/**
+ * mnt_mtab_get_lock:
+ * @mt: mtab
+ *
+ * This function should not be called before mnt_mtab_prepare_update(). The lock
+ * is initialized when mtab update is required only.
+ *
+ * Note that after mnt_mtab_disable_lock(mt, TRUE) or after mnt_free_mtab()
+ * the lock will be automaticaly deallocated.
+ *
+ * Returns: libmount lock handler or NULL if locking is disabled.
+ */
+mnt_lock *mnt_mtab_get_lock(mnt_mtab *mt)
+{
+       return mt ? mt->lc : NULL;
+}
+
+/**
+ * mnt_mtab_disable_lock:
+ * @mt: mtab
+ * @disable: TRUE/FALSE
+ *
+ * Enable or disable mtab locking, the locking is enabled by default.
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_disable_lock(mnt_mtab *mt, int disable)
+{
+       if (!mt)
+               return -1;
+       if (disable) {
+               mnt_free_lock(mt->lc);
+               mt->lc = NULL;
+       }
+       mt->nolock = disable;
+       return 0;
+}
+
+/**
+ * mnt_mtab_set_source:
+ * @mt: mtab
+ * @source: device or directory name
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_source(mnt_mtab *mt, const char *source)
+{
+       return mt ? mnt_fs_set_source(mt->fs, source) : -1;
+}
+
+/**
+ * mnt_mtab_set_target:
+ * @mt: mtab
+ * @target: mountpoint
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_target(mnt_mtab *mt, const char *target)
+{
+       return mt ? mnt_fs_set_target(mt->fs, target) : -1;
+}
+
+/**
+ * mnt_mtab_set_old_target:
+ * @mt: mtab
+ * @target: old mountpoint
+ *
+ * Sets the original target for the MS_MOVE operation.
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_old_target(mnt_mtab *mt, const char *target)
+{
+       if (!mt)
+               return -1;
+       mt->old_target = strdup(target);
+       if (!mt->old_target)
+               return -1;
+       return 0;
+}
+
+/**
+ * mnt_mtab_set_fstype:
+ * @mt: mtab
+ * @fstype: filesystem type (e.g. "ext3")
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_mtab_set_fstype(mnt_mtab *mt, const char *fstype)
+{
+       if (!mt)
+               return -1;
+       return mnt_fs_set_fstype(mt->fs, fstype);
+}
+
+
+/*
+ * The format is same as /proc/self/mountinfo, but it contains userspace
+ * mount options and some unncessary fields are ignored.
+ */
+static int fprintf_mountinfo_fs(FILE *f, mnt_fs *fs)
+{
+       char *root = NULL, *target = NULL, *optstr = NULL,
+            *fstype = NULL, *source = NULL;
+       int rc = -1;
+       dev_t devno;
+
+       assert(fs);
+       assert(f);
+
+       if (!fs || !f)
+               return -1;
+       devno = mnt_fs_get_devno(fs);
+       source = mangle(mnt_fs_get_source(fs));
+       root = mangle(mnt_fs_get_root(fs));
+       target = mangle(mnt_fs_get_target(fs));
+       fstype = mangle(mnt_fs_get_fstype(fs));
+       optstr = mangle(mnt_fs_get_optstr(fs));
+
+       if (!root || !target || !optstr)
+               goto done;
+
+       rc = fprintf(f, "%i %i %u:%u %s %s %s - %s %s %s\n",
+                       mnt_fs_get_id(fs),
+                       mnt_fs_get_parent_id(fs),
+                       major(devno), minor(devno),
+                       root,
+                       target,
+                       optstr,
+                       fstype ? fstype : "auto",
+                       source ? source : "none",
+                       "none");
+       rc = 0;
+done:
+       free(root);
+       free(target);
+       free(optstr);
+       free(fstype);
+       free(source);
+       return rc;
+}
+
+static int fprintf_mtab_fs(FILE *f, mnt_fs *fs)
+{
+       char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL;
+       int rc = -1;
+
+       assert(fs);
+       assert(f);
+
+       if (!fs || !f)
+               return -1;
+
+       m1 = mangle(mnt_fs_get_source(fs));
+       m2 = mangle(mnt_fs_get_target(fs));
+       m3 = mangle(mnt_fs_get_fstype(fs));
+       m4 = mangle(mnt_fs_get_optstr(fs));
+
+       if (!m1 || !m2 || !m3 || !m4)
+               goto done;
+
+       rc = fprintf(f, "%s %s %s %s %d %d\n",
+                               m1, m2, m3, m4,
+                               mnt_fs_get_freq(fs),
+                               mnt_fs_get_passno(fs));
+       rc = 0;
+done:
+       free(m1);
+       free(m2);
+       free(m3);
+       free(m4);
+
+       return rc;
+}
+
+static int update_file(const char *filename, int fmt, mnt_tab *tb)
+{
+       mnt_iter itr;
+       mnt_fs *fs;
+       FILE *f = NULL;
+       char tmpname[PATH_MAX];
+       struct stat st;
+       int fd;
+       int (*line_fn)(FILE *, mnt_fs *);
+
+       assert(tb);
+       if (!tb)
+               goto error;
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %s: update from tab %p\n", filename, tb));
+
+       if (snprintf(tmpname, sizeof(tmpname), "%s.tmp", filename)
+                                               >= sizeof(tmpname))
+               goto error;
+
+       f = fopen(tmpname, "w");
+       if (!f)
+               goto error;
+
+       line_fn = fmt == MNT_FMT_MTAB ? fprintf_mtab_fs : fprintf_mountinfo_fs;
+
+       mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+       while(mnt_tab_next_fs(tb, &itr, &fs) == 0)
+               line_fn(f, fs);
+
+       fd = fileno(f);
+
+       if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
+               goto error;
+
+       /* Copy uid/gid from the present file before renaming. */
+       if (stat(filename, &st) == 0) {
+               if (fchown(fd, st.st_uid, st.st_gid) < 0)
+                       goto error;
+       }
+
+       fclose(f);
+       f = NULL;
+
+       if (rename(tmpname, filename) < 0)
+               goto error;
+
+       return 0;
+error:
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %s: update from tab %p failed\n", filename, tb));
+       if (f)
+               fclose(f);
+       return -1;
+}
+
+static int set_fs_root(mnt_mtab *mt, mnt_fs *fs)
+{
+       char *root = NULL, *mnt = NULL;
+       const char *fstype;
+       char *optstr;
+       mnt_tab *tb = NULL;
+
+       if (mt->mountflags & MS_REMOUNT)
+               return 0;
+
+       optstr = (char *) mnt_fs_get_optstr(fs);
+       fstype = mnt_fs_get_fstype(fs);
+
+       /*
+        * bind-mount -- get fs-root and source device for the source filesystem
+        */
+       if (mt->mountflags & MS_BIND) {
+               const char *src, *src_root;
+               mnt_fs *src_fs;
+
+               src = mnt_fs_get_srcpath(fs);
+               if (!src)
+                       goto err;
+               mnt = mnt_get_mountpoint(src);
+               if (!mnt)
+                       goto err;
+               root = mnt_get_fs_root(src, mnt);
+
+               tb = mnt_new_tab_from_file(_PATH_PROC_MOUNTINFO);
+               if (!tb)
+                       goto dflt;
+               src_fs = mnt_tab_find_target(tb, mnt, MNT_ITER_BACKWARD);
+               if (!src_fs)
+                       goto dflt;
+
+               /* set device name and fs */
+               src = mnt_fs_get_srcpath(src_fs);
+               mnt_fs_set_source(fs, src);
+
+               mnt_fs_set_fstype(fs, mnt_fs_get_fstype(src_fs));
+
+               /* on btrfs the subvolume is used as fs-root in
+                * /proc/self/mountinfo, so we have get the original subvolume
+                * name from src_fs and prepend the subvolume name to the
+                * fs-root path
+                */
+               src_root = mnt_fs_get_root(src_fs);
+               if (src_root && !startswith(root, src_root)) {
+                       size_t sz = strlen(root) + strlen(src_root) + 1;
+                       char *tmp = malloc(sz);
+
+                       if (!tmp)
+                               goto err;
+                       snprintf(tmp, sz, "%s%s", src_root, root);
+                       free(root);
+                       root = tmp;
+               }
+       }
+
+       /*
+        * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
+        */
+       else if (fstype && !strcmp(fstype, "btrfs")) {
+               char *vol = NULL, *p;
+               size_t sz, volsz = 0;
+
+               if (mnt_optstr_get_option(optstr, "subvol", &vol, &volsz))
+                       goto dflt;
+
+               sz = volsz;
+               if (*vol != '/')
+                       sz++;
+               root = malloc(sz + 1);
+               if (!root)
+                       goto err;
+               p = root;
+               if (*vol != '/')
+                       *p++ = '/';
+               memcpy(p, vol, volsz);
+               *(root + sz) = '\0';
+       }
+
+dflt:
+       mnt_free_tab(tb);
+       if (!root)
+               root = strdup("/");
+       if (!root)
+               goto err;
+       fs->root = root;
+       free(mnt);
+       return 0;
+err:
+       free(root);
+       free(mnt);
+       return -1;
+}
+
+/**
+ * mnt_mtab_prepare_update:
+ * @mt: mtab
+ *
+ * Prepares internal data for mtab update:
+ * - set mtab filename if mnt_mtab_set_filename() was't called
+ * - set mtab file format if mnt_mtab_set_format() was't called
+ * - (bitwise) OR mountflags from mount options
+ * - for /var/run/mount/mountinfo:
+ *   * evaluate if mtab update is necessary
+ *   * set fs root and devname for bind mount and btrfs subvolumes
+ * - allocate mtab_lock if necessary
+ *
+ * This function has to be always called before mount(2). The mnt_update_mtab()
+ * should not be called is mnt_mtab_prepare_update() returns non-zero value.
+ *
+ * Returns: 0 on success, 1 if update is unncessary, -1 in case of error
+ */
+int mnt_mtab_prepare_update(mnt_mtab *mt)
+{
+       char *u = NULL;
+       const char *o = NULL;
+
+       assert(mt);
+
+       if (!mt)
+               return -1;
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: prepare update (target %s, source %s, optstr %s)\n",
+               mt, mnt_fs_get_target(mt->fs), mnt_fs_get_source(mt->fs),
+               mnt_fs_get_optstr(mt->fs)));
+
+       if (!mt->filename) {
+               const char *p = mnt_get_writable_mtab_path();
+               if (!p) {
+                       if (errno)
+                              goto err;        /* EACCES? */
+                       goto nothing;           /* no mtab */
+               }
+               mt->filename = strdup(p);
+               if (!mt->filename)
+                       goto err;
+       }
+       if (!mt->format)
+               mt->format = endswith(mt->filename, "mountinfo") ?
+                                       MNT_FMT_MOUNTINFO : MNT_FMT_MTAB;
+
+
+       /* TODO: cannonicalize source and target paths on mnt->fs */
+
+       o = mnt_fs_get_optstr(mt->fs);
+       if (o)
+               mt->mountflags |= mnt_optstr_get_mountflags(o);
+
+       /* umount */
+       if (mt->action == MNT_ACT_UMOUNT)
+               return 0;
+
+       /*
+        * A) classic /etc/mtab
+        */
+       if (mt->format != MNT_FMT_MOUNTINFO)
+               return 0;
+
+       /*
+        * B) /var/run/mount/mountinfo
+        */
+       if (mt->mountflags & MS_REMOUNT) {
+               /* remount */
+               if (mnt_split_optstr(o, &u, NULL, NULL, MNT_NOMTAB, 0))
+                       goto err;
+               if (mnt_fs_set_optstr(mt->fs, u))
+                       goto err;
+
+       } else {
+               if (!o)
+                       goto nothing;   /* no options */
+               if (mnt_split_optstr(o, &u, NULL, NULL, MNT_NOMTAB, 0))
+                       goto err;
+               if (!u)
+                       goto nothing;   /* no userpsace options */
+               if (set_fs_root(mt, mt->fs))
+                       goto err;
+               mnt_fs_set_optstr(mt->fs, u);
+       }
+
+       if (!mt->nolock && !mt->lc) {
+               mt->lc = mnt_new_lock(mt->filename, 0);
+               if (!mt->lc)
+                       goto err;
+       }
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: prepare update: success\n", mt));
+       free(u);
+       return 0;
+err:
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: prepare update: failed\n", mt));
+       free(u);
+       return -1;
+nothing:
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: prepare update: unnecessary\n", mt));
+       free(u);
+       return 1;
+}
+
+static int add_entry(mnt_mtab *mt)
+{
+       FILE *f;
+       int rc = -1;
+
+       assert(mt);
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: add entry\n", mt));
+       if (mt->lc)
+               mnt_lock_file(mt->lc);
+       f = fopen(mt->filename, "a+");
+       if (f) {
+               if (mt->format == MNT_FMT_MOUNTINFO)
+                       rc = fprintf_mountinfo_fs(f, mt->fs);
+               else
+                       rc = fprintf_mtab_fs(f, mt->fs);
+               fclose(f);
+       }
+       if (mt->lc)
+               mnt_unlock_file(mt->lc);
+       return rc;
+}
+
+static int remove_entry(mnt_mtab *mt)
+{
+       const char *target;
+       mnt_tab *tb = NULL;
+       mnt_fs *fs = NULL;
+       int rc = -1;
+
+       assert(mt);
+       assert(mt->filename);
+
+       target = mnt_fs_get_target(mt->fs);
+       assert(target);
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: remove entry (target %s)\n", mt, target));
+
+       if (mt->lc)
+               mnt_lock_file(mt->lc);
+       tb = mnt_new_tab_from_file(mt->filename);
+       if (!tb)
+               goto done;
+       fs = mnt_tab_find_target(tb, target, MNT_ITER_BACKWARD);
+       if (!fs) {
+               rc = 0; /* no error if the file does not contain the target */
+               goto done;
+       }
+       mnt_tab_remove_fs(tb, fs);
+
+       if (!update_file(mt->filename, mt->format, tb))
+               rc = 0;
+done:
+       if (mt->lc)
+               mnt_unlock_file(mt->lc);
+       mnt_free_tab(tb);
+       mnt_free_fs(fs);
+       return rc;
+}
+
+static int modify_target(mnt_mtab *mt)
+{
+       mnt_tab *tb = NULL;
+       mnt_fs *fs = NULL;
+       int rc = -1;
+
+       assert(mt);
+       assert(mt->old_target);
+       assert(mt->filename);
+       assert(mnt_fs_get_target(mt->fs));
+
+       if (!mt->old_target)
+               return -1;
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: modify target (%s->%s)\n", mt,
+               mt->old_target, mnt_fs_get_target(mt->fs)));
+
+       if (mt->lc)
+               mnt_lock_file(mt->lc);
+       tb = mnt_new_tab_from_file(mt->filename);
+       if (!tb)
+               goto done;
+       fs = mnt_tab_find_target(tb, mt->old_target, MNT_ITER_BACKWARD);
+       if (!fs) {
+               rc = 0; /* no error if the file does not contain the target */
+               goto done;
+       }
+
+       mnt_fs_set_target(fs, mnt_fs_get_target(mt->fs));
+
+       if (!update_file(mt->filename, mt->format, tb))
+               rc = 0;
+done:
+       if (mt->lc)
+               mnt_unlock_file(mt->lc);
+       mnt_free_tab(tb);
+       return rc;
+}
+
+static int modify_options(mnt_mtab *mt)
+{
+       mnt_tab *tb = NULL;
+       mnt_fs *fs = NULL, *rem_fs = NULL;
+       int rc = -1;
+       const char *target = mnt_fs_get_target(mt->fs);
+
+       assert(target);
+       assert(mt->filename);
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: modify options (target %s)\n", mt, target));
+
+       if (mt->lc)
+               mnt_lock_file(mt->lc);
+       tb = mnt_new_tab_from_file(mt->filename);
+       if (!tb)
+               goto done;
+       fs = mnt_tab_find_target(tb, target, MNT_ITER_BACKWARD);
+       if (!fs) {
+               rc = 0; /* no error if the file does not contain the target */
+               goto done;
+       }
+       if (mt->format == MNT_FMT_MOUNTINFO && !mnt_fs_get_optstr(mt->fs)) {
+               mnt_tab_remove_fs(tb, fs);
+               rem_fs = fs;
+       } else
+               mnt_fs_set_optstr(fs, mnt_fs_get_optstr(mt->fs));
+
+       if (!update_file(mt->filename, mt->format, tb))
+               rc = 0;
+done:
+       if (mt->lc)
+               mnt_unlock_file(mt->lc);
+       mnt_free_tab(tb);
+       mnt_free_fs(rem_fs);
+       return rc;
+}
+
+/**
+ * mnt_update_mtab:
+ * @mt: mtab
+ *
+ * Updates the mtab file. The behavior of this function is undefined if
+ * mnt_mtab_prepare_update() has not been called.
+ *
+ * Returns: 0 on success, -1 in case of error.
+ */
+int mnt_update_mtab(mnt_mtab *mt)
+{
+       assert(mt);
+       assert(mt->filename);
+       assert(mt->format);
+
+       if (!mt)
+               return -1;
+
+       DBG(DEBUG_MTAB, fprintf(stderr,
+               "libmount: mtab %p: update (target %s)\n", mt,
+               mnt_fs_get_target(mt->fs)));
+       /*
+        * umount
+        */
+       if (mt->action == MNT_ACT_UMOUNT)
+               return remove_entry(mt);
+       /*
+        * mount
+        */
+       if (mt->action == MNT_ACT_MOUNT) {
+               if (mt->mountflags & MS_REMOUNT)
+                       return modify_options(mt);
+
+               if (mt->mountflags & MS_MOVE)
+                       return modify_target(mt);
+
+               return add_entry(mt);   /* mount */
+       }
+       return -1;
+}
+
+#ifdef TEST_PROGRAM
+
+#include <errno.h>
+
+mnt_lock *lock;
+
+static void lock_fallback(void)
+{
+       if (lock)
+               mnt_unlock_file(lock);
+}
+
+static int update(mnt_mtab *mt)
+{
+       int rc;
+
+       /*
+        * Note that mount(2) syscal should be called *after*
+        * mnt_mtab_prepare_update() and *before* mnt_update_mtab()
+        */
+       rc = mnt_mtab_prepare_update(mt);
+       if (!rc) {
+               /* setup lock fallback */
+               lock = mnt_mtab_get_lock(mt);
+               atexit(lock_fallback);
+
+               return mnt_update_mtab(mt);
+       }
+       if (rc == 1) {
+               printf("mtab: update is not reuquired\n");
+               return 0;
+       }
+       fprintf(stderr, "mtab: failed to prepare update\n");
+       return -1;
+}
+
+int test_add(struct mtest *ts, int argc, char *argv[])
+{
+       mnt_mtab *mt;
+       int rc = -1;
+
+       if (argc < 5)
+               return -1;
+       mt = mnt_new_mtab(MNT_ACT_MOUNT);
+       if (!mt)
+               return -1;
+       mnt_mtab_set_source(mt, argv[1]);
+       mnt_mtab_set_target(mt, argv[2]);
+       mnt_mtab_set_fstype(mt, argv[3]);
+       mnt_mtab_set_optstr(mt, argv[4]);
+
+       rc = update(mt);
+
+       mnt_free_mtab(mt);
+       return rc;
+}
+
+int test_remove(struct mtest *ts, int argc, char *argv[])
+{
+       mnt_mtab *mt;
+       int rc = -1;
+
+       if (argc < 2)
+               return -1;
+       mt = mnt_new_mtab(MNT_ACT_UMOUNT);
+       if (!mt)
+               return -1;
+       mnt_mtab_set_target(mt, argv[1]);
+
+       rc = update(mt);
+
+       mnt_free_mtab(mt);
+       return rc;
+}
+
+int test_move(struct mtest *ts, int argc, char *argv[])
+{
+       mnt_mtab *mt;
+       int rc = -1;
+
+       if (argc < 3)
+               return -1;
+       mt = mnt_new_mtab(MNT_ACT_MOUNT);
+       if (!mt)
+               return -1;
+       mnt_mtab_set_mountflags(mt, MS_MOVE);
+       mnt_mtab_set_old_target(mt, argv[1]);
+       mnt_mtab_set_target(mt, argv[2]);
+
+       rc = update(mt);
+
+       mnt_free_mtab(mt);
+       return rc;
+}
+
+int test_remount(struct mtest *ts, int argc, char *argv[])
+{
+       mnt_mtab *mt;
+       int rc = -1;
+
+       if (argc < 3)
+               return -1;
+       mt = mnt_new_mtab(MNT_ACT_MOUNT);
+       if (!mt)
+               return -1;
+       mnt_mtab_set_mountflags(mt, MS_REMOUNT);
+       mnt_mtab_set_target(mt, argv[1]);
+       mnt_mtab_set_optstr(mt, argv[2]);
+
+       rc = update(mt);
+
+       mnt_free_mtab(mt);
+       return rc;
+}
+
+int main(int argc, char *argv[])
+{
+       struct mtest tss[] = {
+       { "--add",    test_add,     "<src> <target> <type> <options>  add line to mtab" },
+       { "--remove", test_remove,  "<target>                      MS_REMOUNT mtab change" },
+       { "--move",   test_move,    "<old_target>  <target>        MS_MOVE mtab change" },
+       { "--remount",test_remount, "<target>  <options>           MS_REMOUNT mtab change" },
+       { NULL }
+       };
+
+       return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
index c209784d72a6db85f711a09709b61cbb9be5f018..c2581be63a44a1c90dea91f417437e2522169f97 100644 (file)
@@ -452,7 +452,7 @@ int mnt_optstr_get_mountflags(const char *optstr)
        }
 
        DBG(DEBUG_OPTIONS, fprintf(stderr,
-               "libmount: optstr '%s': mountflags 0x%08x", optstr, flags));
+               "libmount: optstr '%s': mountflags 0x%08x\n", optstr, flags));
        return flags;
 }