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