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