]>
Commit | Line | Data |
---|---|---|
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 | ||
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 */ | |
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 | */ | |
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 | ||
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 | ||
57 | static 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 | */ | |
73 | void 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 | */ |
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 | ||
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 | ||
127 | static 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 | ||
153 | static 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 | ||
189 | static 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 |
227 | int 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 | } | |
304 | done: | |
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 | 311 | static 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; | |
359 | done: | |
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 | ||
367 | int 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 */ |