usrlib_exec_LTLIBRARIES = libmount.la
libmount_la_SOURCES = mountP.h version.c utils.c test.c init.c cache.c \
optstr.c optmap.c iter.c lock.c \
- fs.c tab.c tab_parse.c tab_update.c \
+ fs.c tab.c tab_parse.c tab_update.c tab_diff.c \
context.c context_mount.c context_umount.c \
$(mountinc_HEADERS) \
$(top_srcdir)/lib/at.c \
tests = test_version test_cache test_optstr test_lock \
- test_tab test_utils test_tab_update test_context
+ test_tab test_utils test_tab_update test_context \
+ test_tab_diff
tests: all $(tests)
test_%: %.c
extern const char *mnt_update_get_filename(struct libmnt_update *upd);
extern struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd);
+/* tab_diff.c */
+enum {
+ MNT_TABDIFF_MOUNT = 1,
+ MNT_TABDIFF_UMOUNT,
+ MNT_TABDIFF_MOVE,
+ MNT_TABDIFF_REMOUNT,
+ MNT_TABDIFF_PROPAGATION, /* not implemented yet (TODO) */
+};
+
+extern struct libmnt_tabdiff *mnt_new_tabdiff(void);
+extern void mnt_free_tabdiff(struct libmnt_tabdiff *df);
+
+extern int mnt_diff_tables(struct libmnt_tabdiff *df,
+ struct libmnt_table *old,
+ struct libmnt_table *new);
+
+extern int mnt_tabdiff_next_change(struct libmnt_tabdiff *df,
+ struct libmnt_iter *itr,
+ struct libmnt_fs **old_fs,
+ struct libmnt_fs **new_fs,
+ int *oper);
+
/* context.c */
/* mode for mount options from fstab */
mnt_update_is_ready;
mnt_update_set_fs;
mnt_update_table;
+ mnt_new_tabdiff;
+ mnt_free_tabdiff;
+ mnt_diff_tables;
+ mnt_tabdiff_next_change;
local:
*;
};
#define MNT_DEBUG_UPDATE (1 << 8)
#define MNT_DEBUG_UTILS (1 << 9)
#define MNT_DEBUG_CXT (1 << 10)
+#define MNT_DEBUG_DIFF (1 << 11)
#define MNT_DEBUG_ALL 0xFFFF
#ifdef CONFIG_LIBMOUNT_DEBUG
--- /dev/null
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: tabdiff
+ * @title: monitor mountinfo file
+ * @short_description: monitor changes in the list of the mounted filesystems
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "mountP.h"
+
+struct tabdiff_entry {
+ int oper; /* MNT_TABDIFF_* flags; */
+
+ struct libmnt_fs *old_fs; /* pointer to the old FS */
+ struct libmnt_fs *new_fs; /* pointer to the new FS */
+
+ struct list_head changes;
+};
+
+struct libmnt_tabdiff {
+ int nchanges; /* number of changes */
+
+ struct list_head changes; /* list with modified entries */
+ struct list_head unused; /* list with unuused entries */
+};
+
+/**
+ * mnt_new_tabdiff:
+ *
+ * Allocates a new table diff struct.
+ *
+ * Returns: new diff handler or NULL.
+ */
+struct libmnt_tabdiff *mnt_new_tabdiff(void)
+{
+ struct libmnt_tabdiff *df = calloc(1, sizeof(*df));
+
+ if (!df)
+ return NULL;
+
+ DBG(DIFF, mnt_debug_h(df, "alloc"));
+
+ INIT_LIST_HEAD(&df->changes);
+ INIT_LIST_HEAD(&df->unused);
+ return df;
+}
+
+static void free_tabdiff_entry(struct tabdiff_entry *de)
+{
+ if (!de)
+ return;
+ list_del(&de->changes);
+ free(de);
+}
+
+/**
+ * mnt_free_tabdiff:
+ * @df: tab diff
+ *
+ * Deallocates tab diff struct and all entries.
+ */
+void mnt_free_tabdiff(struct libmnt_tabdiff *df)
+{
+ if (!df)
+ return;
+
+ DBG(DIFF, mnt_debug_h(df, "free"));
+
+ while (!list_empty(&df->changes)) {
+ struct tabdiff_entry *de = list_entry(df->changes.next,
+ struct tabdiff_entry, changes);
+ free_tabdiff_entry(de);
+ }
+
+ free(df);
+}
+
+/**
+ * mnt_tabdiff_next_change:
+ * @df: tabdiff pointer
+ * @itr: iterator
+ * @old_fs: returns the old entry or NULL if new entry added
+ * @new_fs: returns the new entry or NULL if old entry removed
+ * @oper: MNT_TABDIFF_{MOVE,UMOUNT,REMOUNT,MOUNT} flags
+ *
+ * The options @old_fs, @new_fs and @oper are optional.
+ *
+ * Returns: 0 on success, negative number in case of error or 1 at end of list.
+ */
+int mnt_tabdiff_next_change(struct libmnt_tabdiff *df, struct libmnt_iter *itr,
+ struct libmnt_fs **old_fs, struct libmnt_fs **new_fs, int *oper)
+{
+ int rc = 1;
+ struct tabdiff_entry *de = NULL;
+
+ assert(df);
+ assert(df);
+
+ if (!df || !itr)
+ return -EINVAL;
+
+ if (!itr->head)
+ MNT_ITER_INIT(itr, &df->changes);
+ if (itr->p != itr->head) {
+ MNT_ITER_ITERATE(itr, de, struct tabdiff_entry, changes);
+ rc = 0;
+ }
+
+ if (old_fs)
+ *old_fs = de ? de->old_fs : NULL;
+ if (new_fs)
+ *new_fs = de ? de->new_fs : NULL;
+ if (oper)
+ *oper = de ? de->oper : 0;
+
+ return rc;
+}
+
+static int tabdiff_reset(struct libmnt_tabdiff *df)
+{
+ assert(df);
+
+ DBG(DIFF, mnt_debug_h(df, "reseting"));
+
+ /* zeroize all entries and move them to the list of unuused
+ */
+ while (!list_empty(&df->changes)) {
+ struct tabdiff_entry *de = list_entry(df->changes.next,
+ struct tabdiff_entry, changes);
+
+ list_del(&de->changes);
+ list_add_tail(&de->changes, &df->unused);
+
+ de->new_fs = de->old_fs = NULL;
+ de->oper = 0;
+ }
+
+ df->nchanges = 0;
+ return 0;
+}
+
+static int tabdiff_add_entry(struct libmnt_tabdiff *df, struct libmnt_fs *old,
+ struct libmnt_fs *new, int oper)
+{
+ struct tabdiff_entry *de;
+
+ assert(df);
+
+ DBG(DIFF, mnt_debug_h(df, "add change on %s",
+ mnt_fs_get_target(new ? new : old)));
+
+ if (!list_empty(&df->unused)) {
+ de = list_entry(df->unused.next, struct tabdiff_entry, changes);
+ list_del(&de->changes);
+ } else {
+ de = calloc(1, sizeof(*de));
+ if (!de)
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&de->changes);
+
+ de->old_fs = old;
+ de->new_fs = new;
+ de->oper = oper;
+
+ list_add_tail(&de->changes, &df->changes);
+ df->nchanges++;
+ return 0;
+}
+
+static struct tabdiff_entry *tabdiff_get_mount(struct libmnt_tabdiff *df,
+ const char *src,
+ int id)
+{
+ struct list_head *p;
+
+ assert(df);
+ assert(src);
+
+ list_for_each(p, &df->changes) {
+ struct tabdiff_entry *de;
+
+ de = list_entry(p, struct tabdiff_entry, changes);
+
+ if (de->oper == MNT_TABDIFF_MOUNT && de->new_fs &&
+ mnt_fs_get_id(de->new_fs) == id) {
+
+ const char *s = mnt_fs_get_source(de->new_fs);
+
+ if (s && strcmp(s, src) == 0)
+ return de;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * mnt_diff_tables:
+ * @df: diff handler
+ * @old: old table
+ * @new: new table
+ *
+ * Compares @old and @new, the result is stored in @df and accessible by
+ * mnt_tabdiff_next_change().
+ *
+ * Returns: number of changes, negative number in case of error.
+ */
+int mnt_diff_tables(struct libmnt_tabdiff *df, struct libmnt_table *old,
+ struct libmnt_table *new)
+{
+ struct libmnt_fs *fs;
+ struct libmnt_iter itr;
+ int no, nn;
+
+ if (!df || !old || !new)
+ return -EINVAL;
+
+ tabdiff_reset(df);
+
+ no = mnt_table_get_nents(old);
+ nn = mnt_table_get_nents(new);
+
+ if (!no && !nn) /* both tables are empty */
+ return 0;
+
+ DBG(DIFF, mnt_debug_h(df, "analyze new=%p (%d entries), "
+ "old=%p (%d entries)",
+ new, nn, old, no));
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ /* all mounted or umounted */
+ if (!no && nn) {
+ while(mnt_table_next_fs(new, &itr, &fs) == 0)
+ tabdiff_add_entry(df, NULL, fs, MNT_TABDIFF_MOUNT);
+ goto done;
+
+ } else if (no && !nn) {
+ while(mnt_table_next_fs(old, &itr, &fs) == 0)
+ tabdiff_add_entry(df, fs, NULL, MNT_TABDIFF_UMOUNT);
+ goto done;
+ }
+
+ /* search newly mounted or modified */
+ while(mnt_table_next_fs(new, &itr, &fs) == 0) {
+ struct libmnt_fs *o_fs;
+ const char *src = mnt_fs_get_source(fs),
+ *tgt = mnt_fs_get_target(fs);
+
+ o_fs = mnt_table_find_pair(old, src, tgt, MNT_ITER_FORWARD);
+ if (!o_fs)
+ /* 'fs' is not in the old table -- so newly mounted */
+ tabdiff_add_entry(df, NULL, fs, MNT_TABDIFF_MOUNT);
+ else {
+ /* is modified? */
+ const char *o1 = mnt_fs_get_options(o_fs),
+ *o2 = mnt_fs_get_options(fs);
+
+ if (o1 && o2 && strcmp(o1, o2))
+ tabdiff_add_entry(df, o_fs, fs, MNT_TABDIFF_REMOUNT);
+ }
+ }
+
+ /* search umounted or moved */
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+ while(mnt_table_next_fs(old, &itr, &fs) == 0) {
+ const char *src = mnt_fs_get_source(fs),
+ *tgt = mnt_fs_get_target(fs);
+
+ if (!mnt_table_find_pair(new, src, tgt, MNT_ITER_FORWARD)) {
+ struct tabdiff_entry *de;
+
+ de = tabdiff_get_mount(df, src, mnt_fs_get_id(fs));
+ if (de) {
+ de->oper = MNT_TABDIFF_MOVE;
+ de->old_fs = fs;
+ } else
+ tabdiff_add_entry(df, fs, NULL, MNT_TABDIFF_UMOUNT);
+ }
+ }
+done:
+ DBG(DIFF, mnt_debug_h(df, "%d changes detected", df->nchanges));
+ return df->nchanges;
+}
+
+#ifdef TEST_PROGRAM
+
+int test_diff(struct libmnt_test *ts, int argc, char *argv[])
+{
+ struct libmnt_table *tb_old = NULL, *tb_new = NULL;
+ struct libmnt_tabdiff *diff = NULL;
+ struct libmnt_iter *itr;
+ struct libmnt_fs *old, *new;
+ int rc = -1, change;
+
+ tb_old = mnt_new_table_from_file(argv[1]);
+ tb_new = mnt_new_table_from_file(argv[2]);
+ diff = mnt_new_tabdiff();
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+
+ if (!tb_old || !tb_new || !diff || !itr) {
+ warnx("failed to allocate resources");
+ goto done;
+ }
+
+ rc = mnt_diff_tables(diff, tb_old, tb_new);
+ if (rc < 0)
+ goto done;
+
+ while(mnt_tabdiff_next_change(diff, itr, &old, &new, &change) == 0) {
+
+ printf("%s on %s: ", mnt_fs_get_source(new ? new : old),
+ mnt_fs_get_target(new ? new : old));
+
+ switch(change) {
+ case MNT_TABDIFF_MOVE:
+ printf("MOVED to %s\n", mnt_fs_get_target(new));
+ break;
+ case MNT_TABDIFF_UMOUNT:
+ printf("UMOUNTED\n");
+ break;
+ case MNT_TABDIFF_REMOUNT:
+ printf("REMOUNTED from '%s' to '%s'\n",
+ mnt_fs_get_options(old),
+ mnt_fs_get_options(new));
+ break;
+ case MNT_TABDIFF_MOUNT:
+ printf("MOUNTED\n");
+ break;
+ default:
+ printf("unknown change!\n");
+ }
+ }
+
+ rc = 0;
+done:
+ mnt_free_table(tb_old);
+ mnt_free_table(tb_new);
+ mnt_free_tabdiff(diff);
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_test tss[] = {
+ { "--diff", test_diff, "<old> <new> prints change" },
+ { NULL }
+ };
+
+ return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */
TS_HELPER_LIBMOUNT_LOCK="$top_builddir/shlibs/mount/src/test_lock"
TS_HELPER_LIBMOUNT_UPDATE="$top_builddir/shlibs/mount/src/test_tab_update"
TS_HELPER_LIBMOUNT_CONTEXT="$top_builddir/shlibs/mount/src/test_context"
+TS_HELPER_LIBMOUNT_TABDIFF="$top_builddir/shlibs/mount/src/test_tab_diff"
# TODO: use partx
TS_HELPER_PARTITIONS="$top_builddir/shlibs/blkid/samples/partitions"
--- /dev/null
+/dev/mapper/kzak-home on /home/kzak: MOUNTED
--- /dev/null
+//foo.home/bar/ on /mnt/music: MOVED to /mnt/music
--- /dev/null
+/dev/mapper/kzak-home on /home/kzak: REMOUNTED from 'rw,noatime,barrier=1,data=ordered' to 'ro,noatime,barrier=1,data=ordered'
+//foo.home/bar/ on /mnt/sounds: REMOUNTED from 'rw,relatime,unc=\\foo.home\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344' to 'ro,relatime,unc=\\foo.home\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344'
--- /dev/null
+/dev/mapper/kzak-home on /home/kzak: UMOUNTED
--- /dev/null
+15 20 0:3 / /proc rw,relatime - proc /proc rw
+16 20 0:15 / /sys rw,relatime - sysfs /sys rw
+17 20 0:5 / /dev rw,relatime - devtmpfs udev rw,size=1983516k,nr_inodes=495879,mode=755
+18 17 0:10 / /dev/pts rw,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000
+19 17 0:16 / /dev/shm rw,relatime - tmpfs tmpfs rw
+20 1 8:4 / / rw,noatime - ext3 /dev/sda4 rw,errors=continue,user_xattr,acl,barrier=0,data=ordered
+21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755
+22 21 0:18 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
+23 21 0:19 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuset
+24 21 0:20 / /sys/fs/cgroup/ns rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,ns
+25 21 0:21 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpu
+26 21 0:22 / /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuacct
+27 21 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
+28 21 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices
+29 21 0:25 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer
+30 21 0:26 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,net_cls
+31 21 0:27 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio
+32 16 0:28 / /sys/kernel/security rw,relatime - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+33 17 0:29 / /dev/hugepages rw,relatime - autofs systemd-1 rw,fd=23,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+34 16 0:30 / /sys/kernel/debug rw,relatime - autofs systemd-1 rw,fd=24,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+35 15 0:31 / /proc/sys/fs/binfmt_misc rw,relatime - autofs systemd-1 rw,fd=25,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+36 17 0:32 / /dev/mqueue rw,relatime - autofs systemd-1 rw,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+37 15 0:14 / /proc/bus/usb rw,relatime - usbfs /proc/bus/usb rw
+38 33 0:33 / /dev/hugepages rw,relatime - hugetlbfs hugetlbfs rw
+39 36 0:12 / /dev/mqueue rw,relatime - mqueue mqueue rw
+40 20 8:6 / /boot rw,noatime - ext3 /dev/sda6 rw,errors=continue,barrier=0,data=ordered
+41 20 253:0 / /home/kzak rw,noatime - ext4 /dev/mapper/kzak-home rw,barrier=1,data=ordered
+42 35 0:34 / /proc/sys/fs/binfmt_misc rw,relatime - binfmt_misc none rw
+43 16 0:35 / /sys/fs/fuse/connections rw,relatime - fusectl fusectl rw
+44 41 0:36 / /home/kzak/.gvfs rw,nosuid,nodev,relatime - fuse.gvfs-fuse-daemon gvfs-fuse-daemon rw,user_id=500,group_id=500
+45 20 0:37 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs sunrpc rw
+47 20 0:38 / /mnt/music rw,relatime - cifs //foo.home/bar/ rw,unc=\\foo.home\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344
--- /dev/null
+15 20 0:3 / /proc rw,relatime - proc /proc rw
+16 20 0:15 / /sys rw,relatime - sysfs /sys rw
+17 20 0:5 / /dev rw,relatime - devtmpfs udev rw,size=1983516k,nr_inodes=495879,mode=755
+18 17 0:10 / /dev/pts rw,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000
+19 17 0:16 / /dev/shm rw,relatime - tmpfs tmpfs rw
+20 1 8:4 / / rw,noatime - ext3 /dev/sda4 rw,errors=continue,user_xattr,acl,barrier=0,data=ordered
+21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755
+22 21 0:18 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
+23 21 0:19 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuset
+24 21 0:20 / /sys/fs/cgroup/ns rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,ns
+25 21 0:21 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpu
+26 21 0:22 / /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuacct
+27 21 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
+28 21 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices
+29 21 0:25 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer
+30 21 0:26 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,net_cls
+31 21 0:27 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio
+32 16 0:28 / /sys/kernel/security rw,relatime - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+33 17 0:29 / /dev/hugepages rw,relatime - autofs systemd-1 rw,fd=23,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+34 16 0:30 / /sys/kernel/debug rw,relatime - autofs systemd-1 rw,fd=24,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+35 15 0:31 / /proc/sys/fs/binfmt_misc rw,relatime - autofs systemd-1 rw,fd=25,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+36 17 0:32 / /dev/mqueue rw,relatime - autofs systemd-1 rw,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+37 15 0:14 / /proc/bus/usb rw,relatime - usbfs /proc/bus/usb rw
+38 33 0:33 / /dev/hugepages rw,relatime - hugetlbfs hugetlbfs rw
+39 36 0:12 / /dev/mqueue rw,relatime - mqueue mqueue rw
+40 20 8:6 / /boot rw,noatime - ext3 /dev/sda6 rw,errors=continue,barrier=0,data=ordered
+41 20 253:0 / /home/kzak ro,noatime - ext4 /dev/mapper/kzak-home rw,barrier=1,data=ordered
+42 35 0:34 / /proc/sys/fs/binfmt_misc rw,relatime - binfmt_misc none rw
+43 16 0:35 / /sys/fs/fuse/connections rw,relatime - fusectl fusectl rw
+44 41 0:36 / /home/kzak/.gvfs rw,nosuid,nodev,relatime - fuse.gvfs-fuse-daemon gvfs-fuse-daemon rw,user_id=500,group_id=500
+45 20 0:37 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs sunrpc rw
+47 20 0:38 / /mnt/sounds rw,relatime - cifs //foo.home/bar/ ro,unc=\\foo.home\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344
--- /dev/null
+15 20 0:3 / /proc rw,relatime - proc /proc rw
+16 20 0:15 / /sys rw,relatime - sysfs /sys rw
+17 20 0:5 / /dev rw,relatime - devtmpfs udev rw,size=1983516k,nr_inodes=495879,mode=755
+18 17 0:10 / /dev/pts rw,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000
+19 17 0:16 / /dev/shm rw,relatime - tmpfs tmpfs rw
+20 1 8:4 / / rw,noatime - ext3 /dev/sda4 rw,errors=continue,user_xattr,acl,barrier=0,data=ordered
+21 16 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755
+22 21 0:18 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
+23 21 0:19 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuset
+24 21 0:20 / /sys/fs/cgroup/ns rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,ns
+25 21 0:21 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpu
+26 21 0:22 / /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuacct
+27 21 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
+28 21 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices
+29 21 0:25 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer
+30 21 0:26 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,net_cls
+31 21 0:27 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio
+32 16 0:28 / /sys/kernel/security rw,relatime - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+33 17 0:29 / /dev/hugepages rw,relatime - autofs systemd-1 rw,fd=23,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+34 16 0:30 / /sys/kernel/debug rw,relatime - autofs systemd-1 rw,fd=24,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+35 15 0:31 / /proc/sys/fs/binfmt_misc rw,relatime - autofs systemd-1 rw,fd=25,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+36 17 0:32 / /dev/mqueue rw,relatime - autofs systemd-1 rw,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
+37 15 0:14 / /proc/bus/usb rw,relatime - usbfs /proc/bus/usb rw
+38 33 0:33 / /dev/hugepages rw,relatime - hugetlbfs hugetlbfs rw
+39 36 0:12 / /dev/mqueue rw,relatime - mqueue mqueue rw
+40 20 8:6 / /boot rw,noatime - ext3 /dev/sda6 rw,errors=continue,barrier=0,data=ordered
+42 35 0:34 / /proc/sys/fs/binfmt_misc rw,relatime - binfmt_misc none rw
+43 16 0:35 / /sys/fs/fuse/connections rw,relatime - fusectl fusectl rw
+44 41 0:36 / /home/kzak/.gvfs rw,nosuid,nodev,relatime - fuse.gvfs-fuse-daemon gvfs-fuse-daemon rw,user_id=500,group_id=500
+45 20 0:37 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs sunrpc rw
+47 20 0:38 / /mnt/sounds rw,relatime - cifs //foo.home/bar/ rw,unc=\\foo.home\bar,username=kzak,domain=SRGROUP,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.111.1,posixpaths,serverino,acl,rsize=16384,wsize=57344
--- /dev/null
+#!/bin/bash
+
+# Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+
+TS_TOPDIR="$(dirname $0)/../.."
+TS_DESC="table diffs"
+
+. $TS_TOPDIR/functions.sh
+ts_init "$*"
+
+TESTPROG="$TS_HELPER_LIBMOUNT_TABDIFF"
+
+[ -x $TESTPROG ] || ts_skip "test not compiled"
+
+ts_init_subtest "mount"
+$TESTPROG --diff $TS_SELF/files/mountinfo_u $TS_SELF/files/mountinfo &> $TS_OUTPUT
+ts_finalize_subtest
+
+ts_init_subtest "umount"
+$TESTPROG --diff $TS_SELF/files/mountinfo $TS_SELF/files/mountinfo_u &> $TS_OUTPUT
+ts_finalize_subtest
+
+ts_init_subtest "remount"
+$TESTPROG --diff $TS_SELF/files/mountinfo $TS_SELF/files/mountinfo_re &> $TS_OUTPUT
+ts_finalize_subtest
+
+ts_init_subtest "move"
+$TESTPROG --diff $TS_SELF/files/mountinfo $TS_SELF/files/mountinfo_mv &> $TS_OUTPUT
+ts_finalize_subtest
+
+ts_finalize