(root)/
util-linux-2.39/
libmount/
src/
tab_diff.c
       1  /* SPDX-License-Identifier: LGPL-2.1-or-later */
       2  /*
       3   * This file is part of libmount from util-linux project.
       4   *
       5   * Copyright (C) 2011-2018 Karel Zak <kzak@redhat.com>
       6   *
       7   * libmount is free software; you can redistribute it and/or modify it
       8   * under the terms of the GNU Lesser General Public License as published by
       9   * the Free Software Foundation; either version 2.1 of the License, or
      10   * (at your option) any later version.
      11   */
      12  
      13  /**
      14   * SECTION: tabdiff
      15   * @title: Compare changes in mount tables
      16   * @short_description: compare changes in the list of the mounted filesystems
      17   */
      18  #include "mountP.h"
      19  
      20  struct tabdiff_entry {
      21  	int	oper;			/* MNT_TABDIFF_* flags; */
      22  
      23  	struct libmnt_fs *old_fs;	/* pointer to the old FS */
      24  	struct libmnt_fs *new_fs;	/* pointer to the new FS */
      25  
      26  	struct list_head changes;
      27  };
      28  
      29  struct libmnt_tabdiff {
      30  	int nchanges;			/* number of changes */
      31  
      32  	struct list_head changes;	/* list with modified entries */
      33  	struct list_head unused;	/* list with unused entries */
      34  };
      35  
      36  /**
      37   * mnt_new_tabdiff:
      38   *
      39   * Allocates a new table diff struct.
      40   *
      41   * Returns: new diff handler or NULL.
      42   */
      43  struct libmnt_tabdiff *mnt_new_tabdiff(void)
      44  {
      45  	struct libmnt_tabdiff *df = calloc(1, sizeof(*df));
      46  
      47  	if (!df)
      48  		return NULL;
      49  
      50  	DBG(DIFF, ul_debugobj(df, "alloc"));
      51  
      52  	INIT_LIST_HEAD(&df->changes);
      53  	INIT_LIST_HEAD(&df->unused);
      54  	return df;
      55  }
      56  
      57  static void free_tabdiff_entry(struct tabdiff_entry *de)
      58  {
      59  	if (!de)
      60  		return;
      61  	list_del(&de->changes);
      62  	mnt_unref_fs(de->new_fs);
      63  	mnt_unref_fs(de->old_fs);
      64  	free(de);
      65  }
      66  
      67  /**
      68   * mnt_free_tabdiff:
      69   * @df: tab diff
      70   *
      71   * Deallocates tab diff struct and all entries.
      72   */
      73  void mnt_free_tabdiff(struct libmnt_tabdiff *df)
      74  {
      75  	if (!df)
      76  		return;
      77  
      78  	DBG(DIFF, ul_debugobj(df, "free"));
      79  
      80  	while (!list_empty(&df->changes)) {
      81  		struct tabdiff_entry *de = list_entry(df->changes.next,
      82  			                  struct tabdiff_entry, changes);
      83  		free_tabdiff_entry(de);
      84  	}
      85  
      86  	free(df);
      87  }
      88  
      89  /**
      90   * mnt_tabdiff_next_change:
      91   * @df: tabdiff pointer
      92   * @itr: iterator
      93   * @old_fs: returns the old entry or NULL if new entry added
      94   * @new_fs: returns the new entry or NULL if old entry removed
      95   * @oper: MNT_TABDIFF_{MOVE,UMOUNT,REMOUNT,MOUNT} flags
      96   *
      97   * The options @old_fs, @new_fs and @oper are optional.
      98   *
      99   * Returns: 0 on success, negative number in case of error or 1 at the end of list.
     100   */
     101  int mnt_tabdiff_next_change(struct libmnt_tabdiff *df, struct libmnt_iter *itr,
     102  		struct libmnt_fs **old_fs, struct libmnt_fs **new_fs, int *oper)
     103  {
     104  	int rc = 1;
     105  	struct tabdiff_entry *de = NULL;
     106  
     107  	if (!df || !itr)
     108  		return -EINVAL;
     109  
     110  	if (!itr->head)
     111  		MNT_ITER_INIT(itr, &df->changes);
     112  	if (itr->p != itr->head) {
     113  		de = MNT_ITER_GET_ENTRY(itr, struct tabdiff_entry, changes);
     114  		MNT_ITER_ITERATE(itr);
     115  		rc = 0;
     116  	}
     117  
     118  	if (old_fs)
     119  		*old_fs = de ? de->old_fs : NULL;
     120  	if (new_fs)
     121  		*new_fs = de ? de->new_fs : NULL;
     122  	if (oper)
     123  		*oper = de ? de->oper : 0;
     124  
     125  	return rc;
     126  }
     127  
     128  static int tabdiff_reset(struct libmnt_tabdiff *df)
     129  {
     130  	assert(df);
     131  
     132  	DBG(DIFF, ul_debugobj(df, "resetting"));
     133  
     134  	/* zeroize all entries and move them to the list of unused
     135  	 */
     136  	while (!list_empty(&df->changes)) {
     137  		struct tabdiff_entry *de = list_entry(df->changes.next,
     138  			                  struct tabdiff_entry, changes);
     139  
     140  		list_del_init(&de->changes);
     141  		list_add_tail(&de->changes, &df->unused);
     142  
     143  		mnt_unref_fs(de->new_fs);
     144  		mnt_unref_fs(de->old_fs);
     145  
     146  		de->new_fs = de->old_fs = NULL;
     147  		de->oper = 0;
     148  	}
     149  
     150  	df->nchanges = 0;
     151  	return 0;
     152  }
     153  
     154  static int tabdiff_add_entry(struct libmnt_tabdiff *df, struct libmnt_fs *old,
     155  			     struct libmnt_fs *new, int oper)
     156  {
     157  	struct tabdiff_entry *de;
     158  
     159  	assert(df);
     160  
     161  	DBG(DIFF, ul_debugobj(df, "add change on %s",
     162  				mnt_fs_get_target(new ? new : old)));
     163  
     164  	if (!list_empty(&df->unused)) {
     165  		de = list_entry(df->unused.next, struct tabdiff_entry, changes);
     166  		list_del(&de->changes);
     167  	} else {
     168  		de = calloc(1, sizeof(*de));
     169  		if (!de)
     170  			return -ENOMEM;
     171  	}
     172  
     173  	INIT_LIST_HEAD(&de->changes);
     174  
     175  	mnt_ref_fs(new);
     176  	mnt_ref_fs(old);
     177  
     178  	mnt_unref_fs(de->new_fs);
     179  	mnt_unref_fs(de->old_fs);
     180  
     181  	de->old_fs = old;
     182  	de->new_fs = new;
     183  	de->oper = oper;
     184  
     185  	list_add_tail(&de->changes, &df->changes);
     186  	df->nchanges++;
     187  	return 0;
     188  }
     189  
     190  static struct tabdiff_entry *tabdiff_get_mount(struct libmnt_tabdiff *df,
     191  					       const char *src,
     192  					       int id)
     193  {
     194  	struct list_head *p;
     195  
     196  	assert(df);
     197  
     198  	list_for_each(p, &df->changes) {
     199  		struct tabdiff_entry *de;
     200  
     201  		de = list_entry(p, struct tabdiff_entry, changes);
     202  
     203  		if (de->oper == MNT_TABDIFF_MOUNT && de->new_fs &&
     204  		    mnt_fs_get_id(de->new_fs) == id) {
     205  
     206  			const char *s = mnt_fs_get_source(de->new_fs);
     207  
     208  			if (s == NULL && src == NULL)
     209  				return de;
     210  			if (s && src && strcmp(s, src) == 0)
     211  				return de;
     212  		}
     213  	}
     214  	return NULL;
     215  }
     216  
     217  /**
     218   * mnt_diff_tables:
     219   * @df: diff handler
     220   * @old_tab: old table
     221   * @new_tab: new table
     222   *
     223   * Compares @old_tab and @new_tab, the result is stored in @df and accessible by
     224   * mnt_tabdiff_next_change().
     225   *
     226   * Returns: number of changes, negative number in case of error.
     227   */
     228  int mnt_diff_tables(struct libmnt_tabdiff *df, struct libmnt_table *old_tab,
     229  		    struct libmnt_table *new_tab)
     230  {
     231  	struct libmnt_fs *fs;
     232  	struct libmnt_iter itr;
     233  	int no, nn;
     234  
     235  	if (!df || !old_tab || !new_tab)
     236  		return -EINVAL;
     237  
     238  	tabdiff_reset(df);
     239  
     240  	no = mnt_table_get_nents(old_tab);
     241  	nn = mnt_table_get_nents(new_tab);
     242  
     243  	if (!no && !nn)			/* both tables are empty */
     244  		return 0;
     245  
     246  	DBG(DIFF, ul_debugobj(df, "analyze new (%d entries), "
     247  				          "old (%d entries)",
     248  				nn, no));
     249  
     250  	mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     251  
     252  	/* all mounted or umounted */
     253  	if (!no && nn) {
     254  		while(mnt_table_next_fs(new_tab, &itr, &fs) == 0)
     255  			tabdiff_add_entry(df, NULL, fs, MNT_TABDIFF_MOUNT);
     256  		goto done;
     257  
     258  	} else if (no && !nn) {
     259  		while(mnt_table_next_fs(old_tab, &itr, &fs) == 0)
     260  			tabdiff_add_entry(df, fs, NULL, MNT_TABDIFF_UMOUNT);
     261  		goto done;
     262  	}
     263  
     264  	/* search newly mounted or modified */
     265  	while(mnt_table_next_fs(new_tab, &itr, &fs) == 0) {
     266  		struct libmnt_fs *o_fs;
     267  		const char *src = mnt_fs_get_source(fs),
     268  			   *tgt = mnt_fs_get_target(fs);
     269  
     270  		o_fs = mnt_table_find_pair(old_tab, src, tgt, MNT_ITER_FORWARD);
     271  		if (!o_fs)
     272  			/* 'fs' is not in the old table -- so newly mounted */
     273  			tabdiff_add_entry(df, NULL, fs, MNT_TABDIFF_MOUNT);
     274  		else {
     275  			/* is modified? */
     276  			const char *v1 = mnt_fs_get_vfs_options(o_fs),
     277  				   *v2 = mnt_fs_get_vfs_options(fs),
     278  				   *f1 = mnt_fs_get_fs_options(o_fs),
     279  				   *f2 = mnt_fs_get_fs_options(fs);
     280  
     281  			if ((v1 && v2 && strcmp(v1, v2) != 0) || (f1 && f2 && strcmp(f1, f2) != 0))
     282  				tabdiff_add_entry(df, o_fs, fs, MNT_TABDIFF_REMOUNT);
     283  		}
     284  	}
     285  
     286  	/* search umounted or moved */
     287  	mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     288  	while(mnt_table_next_fs(old_tab, &itr, &fs) == 0) {
     289  		const char *src = mnt_fs_get_source(fs),
     290  			   *tgt = mnt_fs_get_target(fs);
     291  
     292  		if (!mnt_table_find_pair(new_tab, src, tgt, MNT_ITER_FORWARD)) {
     293  			struct tabdiff_entry *de;
     294  
     295  			de = tabdiff_get_mount(df, src,	mnt_fs_get_id(fs));
     296  			if (de) {
     297  				mnt_ref_fs(fs);
     298  				mnt_unref_fs(de->old_fs);
     299  				de->oper = MNT_TABDIFF_MOVE;
     300  				de->old_fs = fs;
     301  			} else
     302  				tabdiff_add_entry(df, fs, NULL, MNT_TABDIFF_UMOUNT);
     303  		}
     304  	}
     305  done:
     306  	DBG(DIFF, ul_debugobj(df, "%d changes detected", df->nchanges));
     307  	return df->nchanges;
     308  }
     309  
     310  #ifdef TEST_PROGRAM
     311  
     312  static int test_diff(struct libmnt_test *ts, int argc, char *argv[])
     313  {
     314  	struct libmnt_table *tb_old, *tb_new;
     315  	struct libmnt_tabdiff *diff;
     316  	struct libmnt_iter *itr;
     317  	struct libmnt_fs *old, *new;
     318  	int rc = -1, change;
     319  
     320  	tb_old = mnt_new_table_from_file(argv[1]);
     321  	tb_new = mnt_new_table_from_file(argv[2]);
     322  	diff = mnt_new_tabdiff();
     323  	itr = mnt_new_iter(MNT_ITER_FORWARD);
     324  
     325  	if (!tb_old || !tb_new || !diff || !itr) {
     326  		warnx("failed to allocate resources");
     327  		goto done;
     328  	}
     329  
     330  	rc = mnt_diff_tables(diff, tb_old, tb_new);
     331  	if (rc < 0)
     332  		goto done;
     333  
     334  	while(mnt_tabdiff_next_change(diff, itr, &old, &new, &change) == 0) {
     335  
     336  		printf("%s on %s: ", mnt_fs_get_source(new ? new : old),
     337  				     mnt_fs_get_target(new ? new : old));
     338  
     339  		switch(change) {
     340  		case MNT_TABDIFF_MOVE:
     341  			printf("MOVED to %s\n", mnt_fs_get_target(new));
     342  			break;
     343  		case MNT_TABDIFF_UMOUNT:
     344  			printf("UMOUNTED\n");
     345  			break;
     346  		case MNT_TABDIFF_REMOUNT:
     347  			printf("REMOUNTED from '%s' to '%s'\n",
     348  					mnt_fs_get_options(old),
     349  					mnt_fs_get_options(new));
     350  			break;
     351  		case MNT_TABDIFF_MOUNT:
     352  			printf("MOUNTED\n");
     353  			break;
     354  		default:
     355  			printf("unknown change!\n");
     356  		}
     357  	}
     358  
     359  	rc = 0;
     360  done:
     361  	mnt_unref_table(tb_old);
     362  	mnt_unref_table(tb_new);
     363  	mnt_free_tabdiff(diff);
     364  	mnt_free_iter(itr);
     365  	return rc;
     366  }
     367  
     368  int main(int argc, char *argv[])
     369  {
     370  	struct libmnt_test tss[] = {
     371  		{ "--diff", test_diff, "<old> <new> prints change" },
     372  		{ NULL }
     373  	};
     374  
     375  	return mnt_run_test(tss, argc, argv);
     376  }
     377  
     378  #endif /* TEST_PROGRAM */