From e86623f0425bb38569350037a1fb8bc389f1df59 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 26 Apr 2011 17:33:43 +0200 Subject: libmount: add mnt_tabdiff_* functions Signed-off-by: Karel Zak --- shlibs/mount/src/Makefile.am | 5 +- shlibs/mount/src/libmount.h.in | 22 +++ shlibs/mount/src/libmount.sym | 4 + shlibs/mount/src/mountP.h | 1 + shlibs/mount/src/tab_diff.c | 368 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 398 insertions(+), 2 deletions(-) create mode 100644 shlibs/mount/src/tab_diff.c (limited to 'shlibs') diff --git a/shlibs/mount/src/Makefile.am b/shlibs/mount/src/Makefile.am index 843f5abb1..56b0c207d 100644 --- a/shlibs/mount/src/Makefile.am +++ b/shlibs/mount/src/Makefile.am @@ -11,7 +11,7 @@ nodist_mountinc_HEADERS = libmount.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 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 \ @@ -56,7 +56,8 @@ uninstall-hook: 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 diff --git a/shlibs/mount/src/libmount.h.in b/shlibs/mount/src/libmount.h.in index 746555d48..3ea2f9255 100644 --- a/shlibs/mount/src/libmount.h.in +++ b/shlibs/mount/src/libmount.h.in @@ -320,6 +320,28 @@ extern int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly); 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 */ diff --git a/shlibs/mount/src/libmount.sym b/shlibs/mount/src/libmount.sym index 3e4d5cf7b..0b7c5606b 100644 --- a/shlibs/mount/src/libmount.sym +++ b/shlibs/mount/src/libmount.sym @@ -188,6 +188,10 @@ global: 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: *; }; diff --git a/shlibs/mount/src/mountP.h b/shlibs/mount/src/mountP.h index b4b47760d..ad271d08f 100644 --- a/shlibs/mount/src/mountP.h +++ b/shlibs/mount/src/mountP.h @@ -44,6 +44,7 @@ #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 diff --git a/shlibs/mount/src/tab_diff.c b/shlibs/mount/src/tab_diff.c new file mode 100644 index 000000000..12b2f7742 --- /dev/null +++ b/shlibs/mount/src/tab_diff.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2011 Karel Zak + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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, " prints change" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ -- cgit v1.2.3-55-g7522