]>
Commit | Line | Data |
---|---|---|
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 | ||
15 | struct 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 | ||
24 | struct 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 | */ | |
38 | struct 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 | ||
52 | static 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 | */ | |
68 | void 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 | */ |
96 | int 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 | ||
125 | static 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 | ||
151 | static 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 | ||
187 | static 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 |
225 | int 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 | } | |
302 | done: | |
303 | DBG(DIFF, mnt_debug_h(df, "%d changes detected", df->nchanges)); | |
304 | return df->nchanges; | |
305 | } | |
306 | ||
307 | #ifdef TEST_PROGRAM | |
308 | ||
309 | int 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; | |
357 | done: | |
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 | ||
365 | int 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 */ |