]>
Commit | Line | Data |
---|---|---|
6bd8b7a7 | 1 | /* |
192c6aad | 2 | * Copyright (C) 2008-2010 Karel Zak <kzak@redhat.com> |
6bd8b7a7 KZ |
3 | * |
4 | * This file may be redistributed under the terms of the | |
5 | * GNU Lesser General Public License. | |
192c6aad KZ |
6 | */ |
7 | ||
8 | /** | |
63de90d4 | 9 | * SECTION: table |
0f32f1e2 | 10 | * @title: Table of filesystems |
63de90d4 | 11 | * @short_description: container for entries from fstab, mtab or mountinfo |
6bd8b7a7 | 12 | * |
68164f6c | 13 | * Note that mnt_table_find_* functions are mount(8) compatible. These functions |
d58b3157 | 14 | * try to find an entry in more iterations, where the first attempt is always |
192c6aad | 15 | * based on comparison with unmodified (non-canonicalized or un-evaluated) |
d58b3157 | 16 | * paths or tags. For example a fstab with two entries: |
3d735589 KZ |
17 | * <informalexample> |
18 | * <programlisting> | |
192c6aad KZ |
19 | * LABEL=foo /foo auto rw |
20 | * /dev/foo /foo auto rw | |
3d735589 KZ |
21 | * </programlisting> |
22 | * </informalexample> | |
6bd8b7a7 | 23 | * |
192c6aad | 24 | * where both lines are used for the *same* device, then |
3d735589 KZ |
25 | * <informalexample> |
26 | * <programlisting> | |
68164f6c | 27 | * mnt_table_find_source(tb, "/dev/foo", &fs); |
3d735589 KZ |
28 | * </programlisting> |
29 | * </informalexample> | |
192c6aad | 30 | * will returns the second line, and |
3d735589 KZ |
31 | * <informalexample> |
32 | * <programlisting> | |
68164f6c | 33 | * mnt_table_find_source(tb, "LABEL=foo", &fs); |
3d735589 KZ |
34 | * </programlisting> |
35 | * </informalexample> | |
192c6aad | 36 | * will returns the first entry, and |
3d735589 KZ |
37 | * <informalexample> |
38 | * <programlisting> | |
68164f6c | 39 | * mnt_table_find_source(tb, "UUID=anyuuid", &fs); |
3d735589 KZ |
40 | * </programlisting> |
41 | * </informalexample> | |
d58b3157 | 42 | * will return the first entry (if UUID matches with the device). |
6bd8b7a7 | 43 | */ |
8e368761 | 44 | #include <blkid.h> |
6bd8b7a7 | 45 | |
6bd8b7a7 | 46 | #include "mountP.h" |
b106d052 | 47 | #include "strutils.h" |
b2cbe99f | 48 | #include "loopdev.h" |
6bd8b7a7 KZ |
49 | |
50 | /** | |
68164f6c | 51 | * mnt_new_table: |
6bd8b7a7 | 52 | * |
68164f6c | 53 | * The tab is a container for struct libmnt_fs entries that usually represents a fstab, |
6bd8b7a7 KZ |
54 | * mtab or mountinfo file from your system. |
55 | * | |
68164f6c | 56 | * See also mnt_table_parse_file(). |
6bd8b7a7 | 57 | * |
192c6aad | 58 | * Returns: newly allocated tab struct. |
6bd8b7a7 | 59 | */ |
68164f6c | 60 | struct libmnt_table *mnt_new_table(void) |
6bd8b7a7 | 61 | { |
68164f6c | 62 | struct libmnt_table *tb = NULL; |
6bd8b7a7 | 63 | |
68164f6c | 64 | tb = calloc(1, sizeof(*tb)); |
6bd8b7a7 | 65 | if (!tb) |
9ed7507c KZ |
66 | return NULL; |
67 | ||
3f31a959 | 68 | DBG(TAB, mnt_debug_h(tb, "alloc")); |
6bd8b7a7 | 69 | |
6bd8b7a7 KZ |
70 | INIT_LIST_HEAD(&tb->ents); |
71 | return tb; | |
6bd8b7a7 KZ |
72 | } |
73 | ||
74 | /** | |
18486fbb | 75 | * mnt_reset_table: |
3d735589 | 76 | * @tb: tab pointer |
6bd8b7a7 | 77 | * |
d58b3157 | 78 | * Deallocates all entries (filesystems) from the table. |
18486fbb KZ |
79 | * |
80 | * Returns: 0 on success or negative number in case of error. | |
6bd8b7a7 | 81 | */ |
18486fbb | 82 | int mnt_reset_table(struct libmnt_table *tb) |
6bd8b7a7 KZ |
83 | { |
84 | if (!tb) | |
18486fbb | 85 | return -EINVAL; |
6bd8b7a7 | 86 | |
18486fbb | 87 | DBG(TAB, mnt_debug_h(tb, "reset")); |
3f31a959 | 88 | |
6bd8b7a7 | 89 | while (!list_empty(&tb->ents)) { |
68164f6c KZ |
90 | struct libmnt_fs *fs = list_entry(tb->ents.next, |
91 | struct libmnt_fs, ents); | |
6bd8b7a7 KZ |
92 | mnt_free_fs(fs); |
93 | } | |
94 | ||
0983b5f7 | 95 | tb->nents = 0; |
18486fbb KZ |
96 | return 0; |
97 | } | |
98 | ||
99 | /** | |
100 | * mnt_free_table: | |
101 | * @tb: tab pointer | |
102 | * | |
103 | * Deallocates tab struct and all entries. | |
104 | */ | |
105 | void mnt_free_table(struct libmnt_table *tb) | |
106 | { | |
107 | if (!tb) | |
108 | return; | |
109 | ||
110 | mnt_reset_table(tb); | |
111 | ||
112 | DBG(TAB, mnt_debug_h(tb, "free")); | |
cb90e24e OO |
113 | free(tb->comm_intro); |
114 | free(tb->comm_tail); | |
6bd8b7a7 KZ |
115 | free(tb); |
116 | } | |
117 | ||
118 | /** | |
68164f6c | 119 | * mnt_table_get_nents: |
6bd8b7a7 KZ |
120 | * @tb: pointer to tab |
121 | * | |
192c6aad | 122 | * Returns: number of valid entries in tab. |
6bd8b7a7 | 123 | */ |
68164f6c | 124 | int mnt_table_get_nents(struct libmnt_table *tb) |
6bd8b7a7 KZ |
125 | { |
126 | assert(tb); | |
127 | return tb ? tb->nents : 0; | |
128 | } | |
129 | ||
2bc04c11 KZ |
130 | /** |
131 | * mnt_table_set_userdata: | |
132 | * @tb: pointer to tab | |
133 | * @data: pointer to user data | |
134 | * | |
135 | * Sets pointer to the private user data. | |
136 | * | |
137 | * Returns: 0 on success or negative number in case of error. | |
138 | */ | |
139 | int mnt_table_set_userdata(struct libmnt_table *tb, void *data) | |
140 | { | |
141 | assert(tb); | |
142 | if (!tb) | |
143 | return -EINVAL; | |
144 | ||
145 | tb->userdata = data; | |
146 | return 0; | |
147 | } | |
148 | ||
149 | /** | |
150 | * mnt_table_get_userdata: | |
151 | * @tb: pointer to tab | |
152 | * | |
153 | * Returns: pointer to user's data. | |
154 | */ | |
155 | void *mnt_table_get_userdata(struct libmnt_table *tb) | |
156 | { | |
157 | assert(tb); | |
158 | return tb ? tb->userdata : NULL; | |
159 | } | |
160 | ||
cb90e24e OO |
161 | /** |
162 | * mnt_table_enable_comments: | |
163 | * @tb: pointer to tab | |
7b1333fa | 164 | * @enable: TRUE or FALSE |
cb90e24e OO |
165 | * |
166 | * Enables parsing of comments. | |
167 | * | |
168 | * The initial (intro) file comment is accessible by | |
169 | * mnt_table_get_intro_comment(). The intro and the comment of the first fstab | |
170 | * entry has to be separated by blank line. The filesystem comments are | |
d58b3157 | 171 | * accessible by mnt_fs_get_comment(). The trailing fstab comment is accessible |
3035ba93 | 172 | * by mnt_table_get_trailing_comment(). |
cb90e24e OO |
173 | * |
174 | * <informalexample> | |
175 | * <programlisting> | |
176 | * # | |
177 | * # Intro comment | |
178 | * # | |
179 | * | |
180 | * # this comments belongs to the first fs | |
181 | * LABEL=foo /mnt/foo auto defaults 1 2 | |
182 | * # this comments belongs to the second fs | |
183 | * LABEL=bar /mnt/bar auto defaults 1 2 | |
184 | * # tailing comment | |
185 | * </programlisting> | |
186 | * </informalexample> | |
187 | */ | |
188 | void mnt_table_enable_comments(struct libmnt_table *tb, int enable) | |
189 | { | |
190 | assert(tb); | |
191 | if (tb) | |
192 | tb->comms = enable; | |
193 | } | |
194 | ||
e670df45 KZ |
195 | /** |
196 | * mnt_table_with_comments: | |
197 | * @tb: pointer to table | |
198 | * | |
199 | * Returns: 1 if comments parsing is enabled, or 0. | |
200 | */ | |
201 | int mnt_table_with_comments(struct libmnt_table *tb) | |
202 | { | |
203 | assert(tb); | |
204 | return tb ? tb->comms : 0; | |
205 | } | |
206 | ||
cb90e24e OO |
207 | /** |
208 | * mnt_table_get_intro_comment: | |
209 | * @tb: pointer to tab | |
210 | * | |
211 | * Returns: initial comment in tb | |
212 | */ | |
213 | const char *mnt_table_get_intro_comment(struct libmnt_table *tb) | |
214 | { | |
215 | assert(tb); | |
216 | return tb ? tb->comm_intro : NULL; | |
217 | } | |
218 | ||
219 | /** | |
220 | * mnt_table_set_into_comment: | |
221 | * @tb: pointer to tab | |
222 | * @comm: comment or NULL | |
223 | * | |
d58b3157 | 224 | * Sets the initial comment in tb. |
cb90e24e OO |
225 | * |
226 | * Returns: 0 on success or negative number in case of error. | |
227 | */ | |
228 | int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm) | |
229 | { | |
230 | char *p = NULL; | |
231 | ||
232 | assert(tb); | |
233 | if (!tb) | |
234 | return -EINVAL; | |
235 | if (comm) { | |
236 | p = strdup(comm); | |
237 | if (!p) | |
238 | return -ENOMEM; | |
239 | } | |
240 | free(tb->comm_intro); | |
241 | tb->comm_intro = p; | |
242 | return 0; | |
243 | } | |
244 | ||
245 | /** | |
246 | * mnt_table_append_into_comment: | |
247 | * @tb: pointer to tab | |
248 | * @comm: comment of NULL | |
249 | * | |
250 | * Appends the initial comment in tb. | |
251 | * | |
252 | * Returns: 0 on success or negative number in case of error. | |
253 | */ | |
254 | int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm) | |
255 | { | |
256 | assert(tb); | |
257 | if (!tb) | |
258 | return -EINVAL; | |
259 | return append_string(&tb->comm_intro, comm); | |
260 | } | |
261 | ||
262 | /** | |
3035ba93 | 263 | * mnt_table_get_trailing_comment: |
cb90e24e OO |
264 | * @tb: pointer to tab |
265 | * | |
d58b3157 | 266 | * Returns: table trailing comment |
cb90e24e | 267 | */ |
3035ba93 | 268 | const char *mnt_table_get_trailing_comment(struct libmnt_table *tb) |
cb90e24e OO |
269 | { |
270 | assert(tb); | |
271 | return tb ? tb->comm_tail : NULL; | |
272 | } | |
273 | ||
274 | /** | |
3035ba93 | 275 | * mnt_table_set_trailing_comment |
cb90e24e | 276 | * @tb: pointer to tab |
7b1333fa | 277 | * @comm: comment string |
cb90e24e | 278 | * |
d58b3157 | 279 | * Sets the trailing comment in table. |
cb90e24e OO |
280 | * |
281 | * Returns: 0 on success or negative number in case of error. | |
282 | */ | |
3035ba93 | 283 | int mnt_table_set_trailing_comment(struct libmnt_table *tb, const char *comm) |
cb90e24e OO |
284 | { |
285 | char *p = NULL; | |
286 | ||
287 | assert(tb); | |
288 | if (!tb) | |
289 | return -EINVAL; | |
290 | if (comm) { | |
291 | p = strdup(comm); | |
292 | if (!p) | |
293 | return -ENOMEM; | |
294 | } | |
295 | free(tb->comm_tail); | |
296 | tb->comm_tail = p; | |
297 | return 0; | |
298 | } | |
299 | ||
300 | /** | |
3035ba93 | 301 | * mnt_table_append_trailing_comment: |
cb90e24e OO |
302 | * @tb: pointer to tab |
303 | * @comm: comment of NULL | |
304 | * | |
d58b3157 | 305 | * Appends to the trailing table comment. |
cb90e24e OO |
306 | * |
307 | * Returns: 0 on success or negative number in case of error. | |
308 | */ | |
3035ba93 | 309 | int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm) |
cb90e24e OO |
310 | { |
311 | assert(tb); | |
312 | if (!tb) | |
313 | return -EINVAL; | |
314 | return append_string(&tb->comm_tail, comm); | |
315 | } | |
316 | ||
6bd8b7a7 | 317 | /** |
68164f6c | 318 | * mnt_table_set_cache: |
6bd8b7a7 | 319 | * @tb: pointer to tab |
68164f6c | 320 | * @mpc: pointer to struct libmnt_cache instance |
6bd8b7a7 | 321 | * |
d58b3157 | 322 | * Sets up a cache for canonicalized paths and evaluated tags (LABEL/UUID). The |
68164f6c | 323 | * cache is recommended for mnt_table_find_*() functions. |
6bd8b7a7 KZ |
324 | * |
325 | * The cache could be shared between more tabs. Be careful when you share the | |
326 | * same cache between more threads -- currently the cache does not provide any | |
327 | * locking method. | |
328 | * | |
329 | * See also mnt_new_cache(). | |
330 | * | |
59ae0ddd | 331 | * Returns: 0 on success or negative number in case of error. |
6bd8b7a7 | 332 | */ |
68164f6c | 333 | int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc) |
6bd8b7a7 KZ |
334 | { |
335 | assert(tb); | |
336 | if (!tb) | |
59ae0ddd | 337 | return -EINVAL; |
6bd8b7a7 KZ |
338 | tb->cache = mpc; |
339 | return 0; | |
340 | } | |
341 | ||
342 | /** | |
68164f6c | 343 | * mnt_table_get_cache: |
6bd8b7a7 KZ |
344 | * @tb: pointer to tab |
345 | * | |
68164f6c | 346 | * Returns: pointer to struct libmnt_cache instance or NULL. |
6bd8b7a7 | 347 | */ |
68164f6c | 348 | struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb) |
6bd8b7a7 KZ |
349 | { |
350 | assert(tb); | |
351 | return tb ? tb->cache : NULL; | |
352 | } | |
353 | ||
6bd8b7a7 | 354 | /** |
68164f6c | 355 | * mnt_table_add_fs: |
6bd8b7a7 KZ |
356 | * @tb: tab pointer |
357 | * @fs: new entry | |
358 | * | |
359 | * Adds a new entry to tab. | |
360 | * | |
59ae0ddd | 361 | * Returns: 0 on success or negative number in case of error. |
6bd8b7a7 | 362 | */ |
68164f6c | 363 | int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) |
6bd8b7a7 KZ |
364 | { |
365 | assert(tb); | |
366 | assert(fs); | |
367 | ||
368 | if (!tb || !fs) | |
59ae0ddd | 369 | return -EINVAL; |
6bd8b7a7 KZ |
370 | |
371 | list_add_tail(&fs->ents, &tb->ents); | |
372 | ||
3f31a959 KZ |
373 | DBG(TAB, mnt_debug_h(tb, "add entry: %s %s", |
374 | mnt_fs_get_source(fs), mnt_fs_get_target(fs))); | |
911238af | 375 | tb->nents++; |
6bd8b7a7 KZ |
376 | return 0; |
377 | } | |
378 | ||
379 | /** | |
68164f6c | 380 | * mnt_table_remove_fs: |
6bd8b7a7 KZ |
381 | * @tb: tab pointer |
382 | * @fs: new entry | |
383 | * | |
59ae0ddd | 384 | * Returns: 0 on success or negative number in case of error. |
6bd8b7a7 | 385 | */ |
68164f6c | 386 | int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs) |
6bd8b7a7 KZ |
387 | { |
388 | assert(tb); | |
389 | assert(fs); | |
390 | ||
391 | if (!tb || !fs) | |
59ae0ddd | 392 | return -EINVAL; |
6bd8b7a7 | 393 | list_del(&fs->ents); |
911238af | 394 | tb->nents--; |
6bd8b7a7 KZ |
395 | return 0; |
396 | } | |
397 | ||
26b4f9e4 | 398 | /** |
68164f6c | 399 | * mnt_table_get_root_fs: |
26b4f9e4 KZ |
400 | * @tb: mountinfo file (/proc/self/mountinfo) |
401 | * @root: returns pointer to the root filesystem (/) | |
402 | * | |
d58b3157 | 403 | * The function uses the parent ID from the mountinfo file to determine the root filesystem |
c667aff9 | 404 | * (the filesystem with the smallest ID). The function is designed mostly for |
d58b3157 | 405 | * applications where it is necessary to sort mountpoints by IDs to get the tree |
c667aff9 KZ |
406 | * of the mountpoints (e.g. findmnt default output). |
407 | * | |
d58b3157 | 408 | * If you're not sure, then use |
c667aff9 KZ |
409 | * |
410 | * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD); | |
411 | * | |
d58b3157 | 412 | * this is more robust and usable for arbitrary tab files (including fstab). |
c667aff9 | 413 | * |
d58b3157 | 414 | * Returns: 0 on success or negative number in case of error. |
26b4f9e4 | 415 | */ |
68164f6c | 416 | int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) |
26b4f9e4 | 417 | { |
68164f6c KZ |
418 | struct libmnt_iter itr; |
419 | struct libmnt_fs *fs; | |
26b4f9e4 KZ |
420 | int root_id = 0; |
421 | ||
422 | assert(tb); | |
423 | assert(root); | |
424 | ||
425 | if (!tb || !root) | |
59ae0ddd | 426 | return -EINVAL; |
26b4f9e4 | 427 | |
0ccfc837 | 428 | DBG(TAB, mnt_debug_h(tb, "lookup root fs")); |
20aae9d3 | 429 | |
26b4f9e4 | 430 | mnt_reset_iter(&itr, MNT_ITER_FORWARD); |
68164f6c | 431 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
26b4f9e4 KZ |
432 | int id = mnt_fs_get_parent_id(fs); |
433 | if (!id) | |
d58b3157 | 434 | break; /* @tab is not a mountinfo file? */ |
26b4f9e4 KZ |
435 | |
436 | if (!*root || id < root_id) { | |
437 | *root = fs; | |
438 | root_id = id; | |
439 | } | |
440 | } | |
441 | ||
59ae0ddd | 442 | return root_id ? 0 : -EINVAL; |
26b4f9e4 KZ |
443 | } |
444 | ||
445 | /** | |
68164f6c | 446 | * mnt_table_next_child_fs: |
26b4f9e4 | 447 | * @tb: mountinfo file (/proc/self/mountinfo) |
3d735589 | 448 | * @itr: iterator |
26b4f9e4 KZ |
449 | * @parent: parental FS |
450 | * @chld: returns the next child filesystem | |
451 | * | |
d58b3157 | 452 | * Note that filesystems are returned in the order of mounting (according to |
26b4f9e4 KZ |
453 | * IDs in /proc/self/mountinfo). |
454 | * | |
d58b3157 | 455 | * Returns: 0 on success, negative number in case of error or 1 at the end of list. |
26b4f9e4 | 456 | */ |
68164f6c KZ |
457 | int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, |
458 | struct libmnt_fs *parent, struct libmnt_fs **chld) | |
26b4f9e4 | 459 | { |
68164f6c | 460 | struct libmnt_fs *fs; |
26b4f9e4 KZ |
461 | int parent_id, lastchld_id = 0, chld_id = 0; |
462 | ||
463 | if (!tb || !itr || !parent) | |
59ae0ddd | 464 | return -EINVAL; |
26b4f9e4 | 465 | |
7ba207e7 | 466 | DBG(TAB, mnt_debug_h(tb, "lookup next child of '%s'", |
3f31a959 | 467 | mnt_fs_get_target(parent))); |
20aae9d3 | 468 | |
26b4f9e4 KZ |
469 | parent_id = mnt_fs_get_id(parent); |
470 | if (!parent_id) | |
59ae0ddd | 471 | return -EINVAL; |
26b4f9e4 KZ |
472 | |
473 | /* get ID of the previously returned child */ | |
474 | if (itr->head && itr->p != itr->head) { | |
68164f6c | 475 | MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents); |
26b4f9e4 KZ |
476 | lastchld_id = mnt_fs_get_id(fs); |
477 | } | |
478 | ||
479 | *chld = NULL; | |
480 | ||
481 | mnt_reset_iter(itr, MNT_ITER_FORWARD); | |
68164f6c | 482 | while(mnt_table_next_fs(tb, itr, &fs) == 0) { |
26b4f9e4 KZ |
483 | int id; |
484 | ||
485 | if (mnt_fs_get_parent_id(fs) != parent_id) | |
486 | continue; | |
487 | ||
488 | id = mnt_fs_get_id(fs); | |
489 | ||
d58b3157 | 490 | /* avoid an infinite loop. This only happens in rare cases |
bf91904c DR |
491 | * such as in early userspace when the rootfs is its own parent */ |
492 | if (id == parent_id) | |
493 | continue; | |
494 | ||
26b4f9e4 KZ |
495 | if ((!lastchld_id || id > lastchld_id) && |
496 | (!*chld || id < chld_id)) { | |
497 | *chld = fs; | |
498 | chld_id = id; | |
499 | } | |
500 | } | |
501 | ||
502 | if (!chld_id) | |
503 | return 1; /* end of iterator */ | |
504 | ||
505 | /* set the iterator to the @chld for the next call */ | |
68164f6c | 506 | mnt_table_set_iter(tb, itr, *chld); |
26b4f9e4 KZ |
507 | |
508 | return 0; | |
509 | } | |
510 | ||
6bd8b7a7 | 511 | /** |
68164f6c | 512 | * mnt_table_next_fs: |
6bd8b7a7 KZ |
513 | * @tb: tab pointer |
514 | * @itr: iterator | |
515 | * @fs: returns the next tab entry | |
516 | * | |
d58b3157 | 517 | * Returns: 0 on success, negative number in case of error or 1 at the end of list. |
6bd8b7a7 | 518 | * |
0f32f1e2 | 519 | * Example: |
3d735589 KZ |
520 | * <informalexample> |
521 | * <programlisting> | |
68164f6c | 522 | * while(mnt_table_next_fs(tb, itr, &fs) == 0) { |
6bd8b7a7 KZ |
523 | * const char *dir = mnt_fs_get_target(fs); |
524 | * printf("mount point: %s\n", dir); | |
525 | * } | |
68164f6c | 526 | * mnt_free_table(fi); |
3d735589 KZ |
527 | * </programlisting> |
528 | * </informalexample> | |
0f32f1e2 | 529 | * |
d58b3157 | 530 | * lists all mountpoints from fstab in reverse order. |
6bd8b7a7 | 531 | */ |
68164f6c | 532 | int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs) |
6bd8b7a7 | 533 | { |
59ae0ddd | 534 | int rc = 1; |
3fca8422 | 535 | |
6bd8b7a7 KZ |
536 | assert(tb); |
537 | assert(itr); | |
538 | assert(fs); | |
539 | ||
540 | if (!tb || !itr || !fs) | |
59ae0ddd | 541 | return -EINVAL; |
0532ba1d KZ |
542 | *fs = NULL; |
543 | ||
6bd8b7a7 KZ |
544 | if (!itr->head) |
545 | MNT_ITER_INIT(itr, &tb->ents); | |
546 | if (itr->p != itr->head) { | |
68164f6c | 547 | MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents); |
3fca8422 | 548 | rc = 0; |
6bd8b7a7 KZ |
549 | } |
550 | ||
3fca8422 KZ |
551 | return rc; |
552 | } | |
553 | ||
554 | /** | |
68164f6c | 555 | * mnt_table_find_next_fs: |
3fca8422 KZ |
556 | * @tb: table |
557 | * @itr: iterator | |
d58b3157 | 558 | * @match_func: function returning 1 or 0 |
3d735589 | 559 | * @userdata: extra data for match_func |
3fca8422 KZ |
560 | * @fs: returns pointer to the next matching table entry |
561 | * | |
d58b3157 | 562 | * This function allows searching in @tb. |
3fca8422 | 563 | * |
59ae0ddd | 564 | * Returns: negative number in case of error, 1 at end of table or 0 o success. |
3fca8422 | 565 | */ |
68164f6c KZ |
566 | int mnt_table_find_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, |
567 | int (*match_func)(struct libmnt_fs *, void *), void *userdata, | |
568 | struct libmnt_fs **fs) | |
3fca8422 KZ |
569 | { |
570 | if (!tb || !itr || !fs || !match_func) | |
59ae0ddd | 571 | return -EINVAL; |
3fca8422 | 572 | |
0ccfc837 | 573 | DBG(TAB, mnt_debug_h(tb, "lookup next fs")); |
20aae9d3 | 574 | |
3fca8422 KZ |
575 | if (!itr->head) |
576 | MNT_ITER_INIT(itr, &tb->ents); | |
577 | ||
578 | do { | |
579 | if (itr->p != itr->head) | |
68164f6c | 580 | MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents); |
3fca8422 KZ |
581 | else |
582 | break; /* end */ | |
583 | ||
3fca8422 KZ |
584 | if (match_func(*fs, userdata)) |
585 | return 0; | |
586 | } while(1); | |
587 | ||
26b4f9e4 | 588 | *fs = NULL; |
6bd8b7a7 KZ |
589 | return 1; |
590 | } | |
591 | ||
592 | /** | |
68164f6c | 593 | * mnt_table_set_iter: |
6bd8b7a7 KZ |
594 | * @tb: tab pointer |
595 | * @itr: iterator | |
596 | * @fs: tab entry | |
597 | * | |
598 | * Sets @iter to the position of @fs in the file @tb. | |
599 | * | |
59ae0ddd | 600 | * Returns: 0 on success, negative number in case of error. |
6bd8b7a7 | 601 | */ |
68164f6c | 602 | int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs) |
6bd8b7a7 KZ |
603 | { |
604 | assert(tb); | |
605 | assert(itr); | |
606 | assert(fs); | |
607 | ||
608 | if (!tb || !itr || !fs) | |
59ae0ddd | 609 | return -EINVAL; |
6bd8b7a7 KZ |
610 | |
611 | MNT_ITER_INIT(itr, &tb->ents); | |
612 | itr->p = &fs->ents; | |
613 | ||
614 | return 0; | |
615 | } | |
616 | ||
dcc15ce5 KZ |
617 | /** |
618 | * mnt_table_find_mountpoint: | |
619 | * @tb: tab pointer | |
620 | * @path: directory | |
621 | * @direction: MNT_ITER_{FORWARD,BACKWARD} | |
622 | * | |
d58b3157 | 623 | * Same as mnt_get_mountpoint(), except this function does not rely on |
dcc15ce5 KZ |
624 | * st_dev numbers. |
625 | * | |
626 | * Returns: a tab entry or NULL. | |
627 | */ | |
628 | struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, | |
629 | const char *path, | |
630 | int direction) | |
631 | { | |
632 | char *mnt; | |
633 | ||
7ba207e7 | 634 | if (!tb || !path || !*path) |
dcc15ce5 | 635 | return NULL; |
4cd271ad KZ |
636 | if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) |
637 | return NULL; | |
dcc15ce5 | 638 | |
7ba207e7 | 639 | DBG(TAB, mnt_debug_h(tb, "lookup MOUNTPOINT: '%s'", path)); |
dcc15ce5 KZ |
640 | |
641 | mnt = strdup(path); | |
642 | if (!mnt) | |
643 | return NULL; | |
644 | ||
645 | do { | |
646 | char *p; | |
647 | struct libmnt_fs *fs; | |
648 | ||
649 | fs = mnt_table_find_target(tb, mnt, direction); | |
650 | if (fs) { | |
651 | free(mnt); | |
652 | return fs; | |
653 | } | |
654 | ||
655 | p = stripoff_last_component(mnt); | |
656 | if (!p || !*p) | |
657 | break; | |
658 | } while (mnt && *(mnt + 1) != '\0'); | |
659 | ||
660 | free(mnt); | |
661 | return mnt_table_find_target(tb, "/", direction); | |
662 | } | |
663 | ||
6bd8b7a7 | 664 | /** |
68164f6c | 665 | * mnt_table_find_target: |
6bd8b7a7 KZ |
666 | * @tb: tab pointer |
667 | * @path: mountpoint directory | |
668 | * @direction: MNT_ITER_{FORWARD,BACKWARD} | |
669 | * | |
d58b3157 OO |
670 | * Try to lookup an entry in the given tab, three iterations are possible, the first |
671 | * with @path, the second with realpath(@path) and the third with realpath(@path) | |
6bd8b7a7 | 672 | * against realpath(fs->target). The 2nd and 3rd iterations are not performed |
d58b3157 | 673 | * when the @tb cache is not set (see mnt_table_set_cache()). |
6bd8b7a7 | 674 | * |
192c6aad | 675 | * Returns: a tab entry or NULL. |
6bd8b7a7 | 676 | */ |
68164f6c | 677 | struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *path, int direction) |
6bd8b7a7 | 678 | { |
68164f6c KZ |
679 | struct libmnt_iter itr; |
680 | struct libmnt_fs *fs = NULL; | |
6bd8b7a7 KZ |
681 | char *cn; |
682 | ||
683 | assert(tb); | |
684 | assert(path); | |
685 | ||
7ba207e7 | 686 | if (!tb || !path || !*path) |
59ae0ddd | 687 | return NULL; |
4cd271ad KZ |
688 | if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) |
689 | return NULL; | |
59ae0ddd | 690 | |
7ba207e7 | 691 | DBG(TAB, mnt_debug_h(tb, "lookup TARGET: '%s'", path)); |
6bd8b7a7 KZ |
692 | |
693 | /* native @target */ | |
694 | mnt_reset_iter(&itr, direction); | |
de60d08e | 695 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
6699e742 | 696 | if (mnt_fs_streq_target(fs, path)) |
6bd8b7a7 | 697 | return fs; |
de60d08e | 698 | } |
6bd8b7a7 KZ |
699 | if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) |
700 | return NULL; | |
701 | ||
7ba207e7 KZ |
702 | DBG(TAB, mnt_debug_h(tb, "lookup canonical TARGET: '%s'", cn)); |
703 | ||
68164f6c | 704 | /* canonicalized paths in struct libmnt_table */ |
6bd8b7a7 | 705 | mnt_reset_iter(&itr, direction); |
68164f6c | 706 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
6699e742 | 707 | if (mnt_fs_streq_target(fs, cn)) |
6bd8b7a7 KZ |
708 | return fs; |
709 | } | |
710 | ||
fa705b54 KZ |
711 | /* non-canonicaled path in struct libmnt_table |
712 | * -- note that mountpoint in /proc/self/mountinfo is already | |
d58b3157 | 713 | * canonicalized by the kernel |
fa705b54 | 714 | */ |
6bd8b7a7 | 715 | mnt_reset_iter(&itr, direction); |
68164f6c | 716 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
6bd8b7a7 | 717 | char *p; |
9dd75aa6 | 718 | |
fa705b54 KZ |
719 | if (!fs->target |
720 | || mnt_fs_is_swaparea(fs) | |
721 | || mnt_fs_is_kernel(fs) | |
722 | || (*fs->target == '/' && *(fs->target + 1) == '\0')) | |
6bd8b7a7 | 723 | continue; |
9dd75aa6 | 724 | |
6bd8b7a7 | 725 | p = mnt_resolve_path(fs->target, tb->cache); |
6699e742 KZ |
726 | /* both canonicalized, strcmp() is fine here */ |
727 | if (p && strcmp(cn, p) == 0) | |
6bd8b7a7 KZ |
728 | return fs; |
729 | } | |
730 | return NULL; | |
731 | } | |
732 | ||
733 | /** | |
68164f6c | 734 | * mnt_table_find_srcpath: |
6bd8b7a7 | 735 | * @tb: tab pointer |
f12aac6e | 736 | * @path: source path (devname or dirname) or NULL |
6bd8b7a7 KZ |
737 | * @direction: MNT_ITER_{FORWARD,BACKWARD} |
738 | * | |
d58b3157 OO |
739 | * Try to lookup an entry in the given tab, four iterations are possible, the first |
740 | * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..) | |
741 | * from @path and the fourth with realpath(@path) against realpath(entry->srcpath). | |
6bd8b7a7 | 742 | * |
d58b3157 | 743 | * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not |
68164f6c | 744 | * set (see mnt_table_set_cache()). |
6bd8b7a7 | 745 | * |
d0ce7c07 DR |
746 | * Note that NULL is a valid source path; it will be replaced with "none". The |
747 | * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems. | |
f12aac6e | 748 | * |
192c6aad | 749 | * Returns: a tab entry or NULL. |
6bd8b7a7 | 750 | */ |
68164f6c | 751 | struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *path, int direction) |
6bd8b7a7 | 752 | { |
68164f6c KZ |
753 | struct libmnt_iter itr; |
754 | struct libmnt_fs *fs = NULL; | |
6bd8b7a7 KZ |
755 | int ntags = 0; |
756 | char *cn; | |
757 | const char *p; | |
758 | ||
759 | assert(tb); | |
7ba207e7 | 760 | if (!tb || !path || !*path) |
dcc15ce5 | 761 | return NULL; |
4cd271ad KZ |
762 | if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) |
763 | return NULL; | |
6bd8b7a7 | 764 | |
7ba207e7 | 765 | DBG(TAB, mnt_debug_h(tb, "lookup SRCPATH: '%s'", path)); |
6bd8b7a7 KZ |
766 | |
767 | /* native paths */ | |
768 | mnt_reset_iter(&itr, direction); | |
68164f6c | 769 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
ab8c6e05 | 770 | if (mnt_fs_streq_srcpath(fs, path)) |
6bd8b7a7 | 771 | return fs; |
ab8c6e05 KZ |
772 | if (mnt_fs_get_tag(fs, NULL, NULL) == 0) |
773 | ntags++; | |
6bd8b7a7 KZ |
774 | } |
775 | ||
f12aac6e | 776 | if (!path || !tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) |
6bd8b7a7 KZ |
777 | return NULL; |
778 | ||
7ba207e7 KZ |
779 | DBG(TAB, mnt_debug_h(tb, "lookup canonical SRCPATH: '%s'", cn)); |
780 | ||
68164f6c KZ |
781 | /* canonicalized paths in struct libmnt_table */ |
782 | if (ntags < mnt_table_get_nents(tb)) { | |
6bd8b7a7 | 783 | mnt_reset_iter(&itr, direction); |
68164f6c | 784 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
ab8c6e05 | 785 | if (mnt_fs_streq_srcpath(fs, cn)) |
6bd8b7a7 KZ |
786 | return fs; |
787 | } | |
788 | } | |
789 | ||
790 | /* evaluated tag */ | |
3fca8422 | 791 | if (ntags) { |
ba7232a1 KZ |
792 | int rc = mnt_cache_read_tags(tb->cache, cn); |
793 | ||
6bd8b7a7 | 794 | mnt_reset_iter(&itr, direction); |
6bd8b7a7 | 795 | |
ba7232a1 | 796 | if (rc == 0) { |
3fca8422 | 797 | /* @path's TAGs are in the cache */ |
68164f6c | 798 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
3fca8422 | 799 | const char *t, *v; |
6bd8b7a7 | 800 | |
3fca8422 KZ |
801 | if (mnt_fs_get_tag(fs, &t, &v)) |
802 | continue; | |
803 | ||
804 | if (mnt_cache_device_has_tag(tb->cache, cn, t, v)) | |
805 | return fs; | |
806 | } | |
ba7232a1 | 807 | } else if (rc < 0 && errno == EACCES) { |
d58b3157 | 808 | /* @path is inaccessible, try evaluating all TAGs in @tb |
3fca8422 | 809 | * by udev symlinks -- this could be expensive on systems |
d58b3157 | 810 | * with a huge fstab/mtab */ |
68164f6c | 811 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
3fca8422 KZ |
812 | const char *t, *v, *x; |
813 | if (mnt_fs_get_tag(fs, &t, &v)) | |
814 | continue; | |
815 | x = mnt_resolve_tag(t, v, tb->cache); | |
6699e742 KZ |
816 | |
817 | /* both canonicalized, strcmp() is fine here */ | |
818 | if (x && strcmp(x, cn) == 0) | |
3fca8422 KZ |
819 | return fs; |
820 | } | |
6bd8b7a7 KZ |
821 | } |
822 | } | |
823 | ||
68164f6c KZ |
824 | /* non-canonicalized paths in struct libmnt_table */ |
825 | if (ntags <= mnt_table_get_nents(tb)) { | |
6bd8b7a7 | 826 | mnt_reset_iter(&itr, direction); |
68164f6c | 827 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
c70d9d76 | 828 | if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs)) |
9dd75aa6 | 829 | continue; |
6bd8b7a7 KZ |
830 | p = mnt_fs_get_srcpath(fs); |
831 | if (p) | |
832 | p = mnt_resolve_path(p, tb->cache); | |
6699e742 KZ |
833 | |
834 | /* both canonicalized, strcmp() is fine here */ | |
835 | if (p && strcmp(p, cn) == 0) | |
6bd8b7a7 KZ |
836 | return fs; |
837 | } | |
838 | } | |
839 | ||
840 | return NULL; | |
841 | } | |
842 | ||
843 | ||
844 | /** | |
68164f6c | 845 | * mnt_table_find_tag: |
6bd8b7a7 KZ |
846 | * @tb: tab pointer |
847 | * @tag: tag name (e.g "LABEL", "UUID", ...) | |
848 | * @val: tag value | |
849 | * @direction: MNT_ITER_{FORWARD,BACKWARD} | |
850 | * | |
d58b3157 | 851 | * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and |
6bd8b7a7 | 852 | * @val, for the second attempt the tag is evaluated (converted to the device |
d58b3157 | 853 | * name) and mnt_table_find_srcpath() is performed. The second attempt is not |
68164f6c | 854 | * performed when @tb cache is not set (see mnt_table_set_cache()). |
6bd8b7a7 | 855 | |
192c6aad | 856 | * Returns: a tab entry or NULL. |
6bd8b7a7 | 857 | */ |
68164f6c | 858 | struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, |
6bd8b7a7 KZ |
859 | const char *val, int direction) |
860 | { | |
68164f6c KZ |
861 | struct libmnt_iter itr; |
862 | struct libmnt_fs *fs = NULL; | |
6bd8b7a7 KZ |
863 | |
864 | assert(tb); | |
865 | assert(tag); | |
866 | assert(val); | |
867 | ||
7ba207e7 | 868 | if (!tb || !tag || !*tag || !val) |
6bd8b7a7 | 869 | return NULL; |
4cd271ad KZ |
870 | if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) |
871 | return NULL; | |
6bd8b7a7 | 872 | |
3f31a959 | 873 | DBG(TAB, mnt_debug_h(tb, "lookup by TAG: %s %s", tag, val)); |
6bd8b7a7 KZ |
874 | |
875 | /* look up by TAG */ | |
876 | mnt_reset_iter(&itr, direction); | |
68164f6c | 877 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
6bd8b7a7 KZ |
878 | if (fs->tagname && fs->tagval && |
879 | strcmp(fs->tagname, tag) == 0 && | |
880 | strcmp(fs->tagval, val) == 0) | |
881 | return fs; | |
882 | } | |
883 | ||
884 | if (tb->cache) { | |
885 | /* look up by device name */ | |
886 | char *cn = mnt_resolve_tag(tag, val, tb->cache); | |
887 | if (cn) | |
68164f6c | 888 | return mnt_table_find_srcpath(tb, cn, direction); |
6bd8b7a7 KZ |
889 | } |
890 | return NULL; | |
891 | } | |
892 | ||
893 | /** | |
68164f6c | 894 | * mnt_table_find_source: |
6bd8b7a7 KZ |
895 | * @tb: tab pointer |
896 | * @source: TAG or path | |
3d735589 | 897 | * @direction: MNT_ITER_{FORWARD,BACKWARD} |
6bd8b7a7 | 898 | * |
d58b3157 OO |
899 | * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care |
900 | * about the @source format (device, LABEL, UUID, ...). This function parses | |
901 | * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath(). | |
6bd8b7a7 | 902 | * |
192c6aad | 903 | * Returns: a tab entry or NULL. |
6bd8b7a7 | 904 | */ |
f12aac6e KZ |
905 | struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, |
906 | const char *source, int direction) | |
6bd8b7a7 | 907 | { |
2c6b25f0 KZ |
908 | struct libmnt_fs *fs; |
909 | char *t = NULL, *v = NULL; | |
6bd8b7a7 KZ |
910 | |
911 | assert(tb); | |
6bd8b7a7 | 912 | |
f12aac6e | 913 | if (!tb) |
6bd8b7a7 | 914 | return NULL; |
4cd271ad KZ |
915 | if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) |
916 | return NULL; | |
6bd8b7a7 | 917 | |
7ba207e7 | 918 | DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: '%s'", source)); |
6bd8b7a7 | 919 | |
2c6b25f0 | 920 | if (blkid_parse_tag_string(source, &t, &v) || !mnt_valid_tagname(t)) |
68164f6c | 921 | fs = mnt_table_find_srcpath(tb, source, direction); |
2c6b25f0 KZ |
922 | else |
923 | fs = mnt_table_find_tag(tb, t, v, direction); | |
924 | ||
925 | free(t); | |
926 | free(v); | |
6bd8b7a7 KZ |
927 | |
928 | return fs; | |
929 | } | |
930 | ||
059c696f | 931 | /** |
68164f6c | 932 | * mnt_table_find_pair |
059c696f KZ |
933 | * @tb: tab pointer |
934 | * @source: TAG or path | |
935 | * @target: mountpoint | |
936 | * @direction: MNT_ITER_{FORWARD,BACKWARD} | |
937 | * | |
938 | * This function is implemented by mnt_fs_match_source() and | |
d58b3157 | 939 | * mnt_fs_match_target() functions. It means that this is more expensive than |
68164f6c | 940 | * others mnt_table_find_* function, because every @tab entry is fully evaluated. |
059c696f KZ |
941 | * |
942 | * Returns: a tab entry or NULL. | |
943 | */ | |
68164f6c | 944 | struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *source, |
f12aac6e | 945 | const char *target, int direction) |
059c696f | 946 | { |
68164f6c KZ |
947 | struct libmnt_fs *fs = NULL; |
948 | struct libmnt_iter itr; | |
059c696f KZ |
949 | |
950 | assert(tb); | |
059c696f KZ |
951 | assert(target); |
952 | ||
7ba207e7 | 953 | if (!tb || !target || !*target || !source || !*source) |
059c696f | 954 | return NULL; |
4cd271ad KZ |
955 | if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) |
956 | return NULL; | |
059c696f KZ |
957 | |
958 | DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s TARGET: %s", source, target)); | |
959 | ||
960 | mnt_reset_iter(&itr, direction); | |
68164f6c | 961 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { |
059c696f KZ |
962 | |
963 | if (mnt_fs_match_target(fs, target, tb->cache) && | |
964 | mnt_fs_match_source(fs, source, tb->cache)) | |
965 | return fs; | |
966 | } | |
967 | ||
968 | return NULL; | |
969 | } | |
970 | ||
677ff053 KZ |
971 | /** |
972 | * mnt_table_find_devno | |
973 | * @tb: /proc/self/mountinfo | |
974 | * @devno: device number | |
975 | * @direction: MNT_ITER_{FORWARD,BACKWARD} | |
976 | * | |
d58b3157 | 977 | * Note that zero could be a valid device number for the root pseudo filesystem (e.g. |
677ff053 KZ |
978 | * tmpfs). |
979 | * | |
980 | * Returns: a tab entry or NULL. | |
981 | */ | |
982 | struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb, | |
983 | dev_t devno, int direction) | |
984 | { | |
985 | struct libmnt_fs *fs = NULL; | |
986 | struct libmnt_iter itr; | |
987 | ||
988 | assert(tb); | |
989 | ||
990 | if (!tb) | |
991 | return NULL; | |
4cd271ad KZ |
992 | if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) |
993 | return NULL; | |
677ff053 KZ |
994 | |
995 | DBG(TAB, mnt_debug_h(tb, "lookup DEVNO: %d", (int) devno)); | |
996 | ||
997 | mnt_reset_iter(&itr, direction); | |
998 | ||
999 | while(mnt_table_next_fs(tb, &itr, &fs) == 0) { | |
1000 | if (mnt_fs_get_devno(fs) == devno) | |
1001 | return fs; | |
1002 | } | |
1003 | ||
1004 | return NULL; | |
1005 | } | |
1006 | ||
5c60a0ea | 1007 | /* |
7293e97a KZ |
1008 | * tb: /proc/self/mountinfo |
1009 | * fs: filesystem | |
1010 | * mountflags: MS_BIND or 0 | |
d58b3157 | 1011 | * fsroot: fs-root that will probably be used in the mountinfo file |
5c60a0ea KZ |
1012 | * for @fs after mount(2) |
1013 | * | |
1014 | * For btrfs subvolumes this function returns NULL, but @fsroot properly set. | |
1015 | * | |
1016 | * Returns: entry from @tb that will be used as a source for @fs if the @fs is | |
1017 | * bindmount. | |
7293e97a KZ |
1018 | * |
1019 | * Don't export to library API! | |
5c60a0ea KZ |
1020 | */ |
1021 | struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, | |
1022 | struct libmnt_fs *fs, | |
1023 | unsigned long mountflags, | |
1024 | char **fsroot) | |
1025 | { | |
1026 | char *root = NULL, *mnt = NULL; | |
1027 | const char *fstype; | |
1028 | struct libmnt_fs *src_fs = NULL; | |
1029 | ||
5c60a0ea KZ |
1030 | assert(fs); |
1031 | assert(fsroot); | |
1032 | ||
7ba207e7 | 1033 | DBG(TAB, mnt_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs))); |
5c60a0ea KZ |
1034 | |
1035 | fstype = mnt_fs_get_fstype(fs); | |
1036 | ||
c4c66355 | 1037 | if (tb && (mountflags & MS_BIND)) { |
5c60a0ea | 1038 | const char *src, *src_root; |
16b8db49 | 1039 | char *xsrc = NULL; |
5c60a0ea KZ |
1040 | |
1041 | DBG(TAB, mnt_debug("fs-root for bind")); | |
1042 | ||
16b8db49 KZ |
1043 | src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache); |
1044 | if (src) | |
1045 | mnt = mnt_get_mountpoint(src); | |
1046 | if (mnt) | |
1047 | root = mnt_get_fs_root(src, mnt); | |
5c60a0ea | 1048 | |
16b8db49 KZ |
1049 | if (xsrc && !tb->cache) { |
1050 | free(xsrc); | |
1051 | src = NULL; | |
1052 | } | |
5c60a0ea KZ |
1053 | if (!mnt) |
1054 | goto err; | |
1055 | ||
5c60a0ea KZ |
1056 | src_fs = mnt_table_find_target(tb, mnt, MNT_ITER_BACKWARD); |
1057 | if (!src_fs) { | |
1058 | DBG(TAB, mnt_debug("not found '%s' in mountinfo -- using default", mnt)); | |
1059 | goto dflt; | |
1060 | } | |
1061 | ||
1062 | /* on btrfs the subvolume is used as fs-root in | |
1063 | * /proc/self/mountinfo, so we have to get the original subvolume | |
1064 | * name from src_fs and prepend the subvolume name to the | |
1065 | * fs-root path | |
1066 | */ | |
1067 | src_root = mnt_fs_get_root(src_fs); | |
1068 | if (src_root && !startswith(root, src_root)) { | |
1069 | size_t sz = strlen(root) + strlen(src_root) + 1; | |
1070 | char *tmp = malloc(sz); | |
1071 | ||
1072 | if (!tmp) | |
1073 | goto err; | |
1074 | snprintf(tmp, sz, "%s%s", src_root, root); | |
1075 | free(root); | |
1076 | root = tmp; | |
1077 | } | |
1078 | } | |
1079 | ||
1080 | /* | |
1081 | * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path | |
1082 | */ | |
1083 | else if (fstype && !strcmp(fstype, "btrfs")) { | |
1084 | char *vol = NULL, *p; | |
1085 | size_t sz, volsz = 0; | |
1086 | ||
1087 | if (mnt_fs_get_option(fs, "subvol", &vol, &volsz)) | |
1088 | goto dflt; | |
1089 | ||
1090 | DBG(TAB, mnt_debug("setting FS root: btrfs subvol")); | |
1091 | ||
1092 | sz = volsz; | |
1093 | if (*vol != '/') | |
1094 | sz++; | |
1095 | root = malloc(sz + 1); | |
1096 | if (!root) | |
1097 | goto err; | |
1098 | p = root; | |
1099 | if (*vol != '/') | |
1100 | *p++ = '/'; | |
1101 | memcpy(p, vol, volsz); | |
1102 | *(root + sz) = '\0'; | |
1103 | } | |
1104 | dflt: | |
1105 | if (!root) { | |
1106 | root = strdup("/"); | |
1107 | if (!root) | |
1108 | goto err; | |
1109 | } | |
1110 | *fsroot = root; | |
1111 | ||
1112 | DBG(TAB, mnt_debug("FS root result: %s", root)); | |
1113 | ||
1114 | free(mnt); | |
1115 | return src_fs; | |
1116 | err: | |
1117 | free(root); | |
1118 | free(mnt); | |
1119 | return NULL; | |
1120 | } | |
1121 | ||
7293e97a KZ |
1122 | static int is_mountinfo(struct libmnt_table *tb) |
1123 | { | |
1124 | struct libmnt_fs *fs; | |
1125 | ||
1126 | if (!tb) | |
1127 | return 0; | |
1128 | ||
1129 | fs = list_first_entry(&tb->ents, struct libmnt_fs, ents); | |
1130 | if (fs && mnt_fs_is_kernel(fs) && mnt_fs_get_root(fs)) | |
1131 | return 1; | |
1132 | ||
1133 | return 0; | |
1134 | } | |
1135 | ||
5c60a0ea | 1136 | /** |
d58b3157 | 1137 | * mnt_table_is_fs__mounted: |
5c60a0ea KZ |
1138 | * @tb: /proc/self/mountinfo file |
1139 | * @fstab_fs: /etc/fstab entry | |
1140 | * | |
975e14bd | 1141 | * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is |
d58b3157 | 1142 | * ignored. This function explicitly compares the source, target and root of the |
975e14bd KZ |
1143 | * filesystems. |
1144 | * | |
1145 | * Note that source and target are canonicalized only if a cache for @tb is | |
1146 | * defined (see mnt_table_set_cache()). The target canonicalization may | |
d58b3157 | 1147 | * trigger automount on autofs mountpoints! |
975e14bd KZ |
1148 | * |
1149 | * Don't use it if you want to know if a device is mounted, just use | |
d58b3157 | 1150 | * mnt_table_find_source() on the device. |
975e14bd KZ |
1151 | * |
1152 | * This function is designed mostly for "mount -a". | |
5c60a0ea | 1153 | * |
5c60a0ea KZ |
1154 | * Returns: 0 or 1 |
1155 | */ | |
1156 | int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) | |
1157 | { | |
449a7646 KZ |
1158 | struct libmnt_iter itr; |
1159 | struct libmnt_fs *fs; | |
1160 | ||
5c60a0ea | 1161 | char *root = NULL; |
975e14bd KZ |
1162 | const char *src = NULL, *tgt = NULL; |
1163 | char *xtgt = NULL; | |
7293e97a | 1164 | int rc = 0; |
d828dfdf | 1165 | dev_t devno = 0; |
5c60a0ea KZ |
1166 | |
1167 | assert(tb); | |
1168 | assert(fstab_fs); | |
1169 | ||
ae978c4d KZ |
1170 | DBG(FS, mnt_debug_h(fstab_fs, "is FS mounted? [target=%s]", |
1171 | mnt_fs_get_target(fstab_fs))); | |
1172 | ||
1173 | if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_get_nents(tb) == 0) { | |
1174 | DBG(FS, mnt_debug_h(fstab_fs, "- ignore (swap or no data)")); | |
5c60a0ea | 1175 | return 0; |
ae978c4d | 1176 | } |
5c60a0ea | 1177 | |
7293e97a KZ |
1178 | if (is_mountinfo(tb)) { |
1179 | /* @tb is mountinfo, so we can try to use fs-roots */ | |
c9f63764 | 1180 | struct libmnt_fs *rootfs; |
7293e97a | 1181 | int flags = 0; |
5c60a0ea | 1182 | |
7293e97a KZ |
1183 | if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0) |
1184 | flags = MS_BIND; | |
1185 | ||
c9f63764 SK |
1186 | rootfs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root); |
1187 | if (rootfs) | |
1188 | src = mnt_fs_get_srcpath(rootfs); | |
7293e97a KZ |
1189 | } |
1190 | ||
975e14bd | 1191 | if (!src) |
14f66ad6 | 1192 | src = mnt_fs_get_source(fstab_fs); |
5c60a0ea | 1193 | |
975e14bd KZ |
1194 | if (src && tb->cache && !mnt_fs_is_pseudofs(fstab_fs)) |
1195 | src = mnt_resolve_spec(src, tb->cache); | |
1196 | ||
d828dfdf KZ |
1197 | if (src && root) { |
1198 | struct stat st; | |
1199 | ||
1200 | devno = mnt_fs_get_devno(fstab_fs); | |
1201 | if (!devno && stat(src, &st) == 0 && S_ISBLK(st.st_mode)) | |
1202 | devno = st.st_rdev; | |
1203 | } | |
1204 | ||
975e14bd | 1205 | tgt = mnt_fs_get_target(fstab_fs); |
5c60a0ea | 1206 | |
ae978c4d KZ |
1207 | if (!tgt || !src) { |
1208 | DBG(FS, mnt_debug_h(fstab_fs, "- ignore (no source/target)")); | |
449a7646 | 1209 | goto done; |
ae978c4d | 1210 | } |
449a7646 | 1211 | mnt_reset_iter(&itr, MNT_ITER_FORWARD); |
5c60a0ea | 1212 | |
449a7646 | 1213 | while (mnt_table_next_fs(tb, &itr, &fs) == 0) { |
975e14bd | 1214 | |
d828dfdf KZ |
1215 | int eq = mnt_fs_streq_srcpath(fs, src); |
1216 | ||
1217 | if (!eq && devno && mnt_fs_get_devno(fs) == devno) | |
1218 | eq = 1; | |
1219 | ||
1220 | if (!eq) { | |
b2cbe99f KZ |
1221 | /* The source does not match. Maybe the source is a loop |
1222 | * device backing file. | |
1223 | */ | |
1224 | uint64_t offset = 0; | |
1225 | char *val; | |
1226 | size_t len; | |
1227 | int flags; | |
1228 | ||
1229 | if (!mnt_fs_is_kernel(fs) || | |
1230 | !mnt_fs_get_srcpath(fs) || | |
1231 | !startswith(mnt_fs_get_srcpath(fs), "/dev/loop")) | |
1232 | continue; /* does not look like loopdev */ | |
1233 | ||
1234 | if (mnt_fs_get_option(fstab_fs, "offset", &val, &len) == 0 && | |
1235 | mnt_parse_offset(val, len, &offset)) { | |
1236 | DBG(FS, mnt_debug_h(fstab_fs, "failed to parse offset=")); | |
1237 | continue; | |
1238 | } else | |
1239 | flags = LOOPDEV_FL_OFFSET; | |
1240 | ||
1241 | if (loopdev_is_used(mnt_fs_get_srcpath(fs), src, offset, flags)) | |
1242 | break; | |
1243 | } | |
1244 | ||
449a7646 KZ |
1245 | if (root) { |
1246 | const char *r = mnt_fs_get_root(fs); | |
1247 | if (!r || strcmp(r, root) != 0) | |
975e14bd | 1248 | continue; |
449a7646 | 1249 | } |
5c60a0ea | 1250 | |
449a7646 | 1251 | /* |
d58b3157 | 1252 | * Compare target, try to minimize the number of situations when we |
449a7646 KZ |
1253 | * need to canonicalize the path to avoid readlink() on |
1254 | * mountpoints. | |
1255 | */ | |
1256 | if (!xtgt) { | |
1257 | if (mnt_fs_streq_target(fs, tgt)) | |
5c60a0ea | 1258 | break; |
449a7646 KZ |
1259 | if (tb->cache) |
1260 | xtgt = mnt_resolve_path(tgt, tb->cache); | |
5c60a0ea | 1261 | } |
449a7646 KZ |
1262 | if (xtgt && mnt_fs_streq_target(fs, xtgt)) |
1263 | break; | |
5c60a0ea KZ |
1264 | } |
1265 | ||
449a7646 KZ |
1266 | if (fs) |
1267 | rc = 1; /* success */ | |
1268 | done: | |
5c60a0ea | 1269 | free(root); |
b2cbe99f KZ |
1270 | |
1271 | DBG(TAB, mnt_debug_h(tb, "mnt_table_is_fs_mounted: %s [rc=%d]", src, rc)); | |
5c60a0ea KZ |
1272 | return rc; |
1273 | } | |
1274 | ||
6bd8b7a7 | 1275 | #ifdef TEST_PROGRAM |
dcc15ce5 | 1276 | #include "pathnames.h" |
fbb3eb85 | 1277 | |
68164f6c | 1278 | static int parser_errcb(struct libmnt_table *tb, const char *filename, int line) |
fbb3eb85 KZ |
1279 | { |
1280 | fprintf(stderr, "%s:%d: parse error\n", filename, line); | |
c3b0d5b3 | 1281 | |
d58b3157 | 1282 | return 1; /* all errors are recoverable -- this is the default */ |
fbb3eb85 KZ |
1283 | } |
1284 | ||
cb90e24e | 1285 | struct libmnt_table *create_table(const char *file, int comments) |
6bd8b7a7 | 1286 | { |
68164f6c | 1287 | struct libmnt_table *tb; |
6bd8b7a7 KZ |
1288 | |
1289 | if (!file) | |
1290 | return NULL; | |
68164f6c | 1291 | tb = mnt_new_table(); |
6bd8b7a7 KZ |
1292 | if (!tb) |
1293 | goto err; | |
fbb3eb85 | 1294 | |
cb90e24e | 1295 | mnt_table_enable_comments(tb, comments); |
68164f6c | 1296 | mnt_table_set_parser_errcb(tb, parser_errcb); |
fbb3eb85 | 1297 | |
68164f6c | 1298 | if (mnt_table_parse_file(tb, file) != 0) |
6bd8b7a7 | 1299 | goto err; |
6bd8b7a7 KZ |
1300 | return tb; |
1301 | err: | |
c3b0d5b3 | 1302 | fprintf(stderr, "%s: parsing failed\n", file); |
68164f6c | 1303 | mnt_free_table(tb); |
6bd8b7a7 KZ |
1304 | return NULL; |
1305 | } | |
1306 | ||
68164f6c | 1307 | int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[]) |
6d94f2dc | 1308 | { |
68164f6c KZ |
1309 | struct libmnt_table *tb; |
1310 | struct libmnt_fs *fs; | |
059c696f | 1311 | int rc = -1; |
6d94f2dc | 1312 | |
cb90e24e | 1313 | tb = create_table(argv[1], FALSE); |
6d94f2dc KZ |
1314 | if (!tb) |
1315 | return -1; | |
1316 | ||
68164f6c | 1317 | fs = mnt_table_find_target(tb, "/", MNT_ITER_FORWARD); |
6d94f2dc | 1318 | if (!fs) |
059c696f | 1319 | goto done; |
6d94f2dc KZ |
1320 | |
1321 | printf("ORIGINAL:\n"); | |
1322 | mnt_fs_print_debug(fs, stdout); | |
1323 | ||
309139c7 | 1324 | fs = mnt_copy_fs(NULL, fs); |
6d94f2dc | 1325 | if (!fs) |
059c696f | 1326 | goto done; |
6d94f2dc KZ |
1327 | |
1328 | printf("COPY:\n"); | |
1329 | mnt_fs_print_debug(fs, stdout); | |
1330 | mnt_free_fs(fs); | |
059c696f KZ |
1331 | rc = 0; |
1332 | done: | |
68164f6c | 1333 | mnt_free_table(tb); |
059c696f | 1334 | return rc; |
6d94f2dc KZ |
1335 | } |
1336 | ||
68164f6c | 1337 | int test_parse(struct libmnt_test *ts, int argc, char *argv[]) |
6bd8b7a7 | 1338 | { |
68164f6c KZ |
1339 | struct libmnt_table *tb = NULL; |
1340 | struct libmnt_iter *itr = NULL; | |
1341 | struct libmnt_fs *fs; | |
059c696f | 1342 | int rc = -1; |
cb90e24e OO |
1343 | int parse_comments = FALSE; |
1344 | ||
1345 | if (argc == 3 && !strcmp(argv[2], "--comments")) | |
1346 | parse_comments = TRUE; | |
6bd8b7a7 | 1347 | |
cb90e24e | 1348 | tb = create_table(argv[1], parse_comments); |
6bd8b7a7 KZ |
1349 | if (!tb) |
1350 | return -1; | |
1351 | ||
efe73c3e KZ |
1352 | itr = mnt_new_iter(MNT_ITER_FORWARD); |
1353 | if (!itr) | |
059c696f | 1354 | goto done; |
fbb3eb85 | 1355 | |
cb90e24e OO |
1356 | if (mnt_table_get_intro_comment(tb)) |
1357 | fprintf(stdout, "Initial comment:\n\"%s\"\n", | |
1358 | mnt_table_get_intro_comment(tb)); | |
1359 | ||
68164f6c | 1360 | while(mnt_table_next_fs(tb, itr, &fs) == 0) |
efe73c3e | 1361 | mnt_fs_print_debug(fs, stdout); |
cb90e24e | 1362 | |
3035ba93 | 1363 | if (mnt_table_get_trailing_comment(tb)) |
cb90e24e | 1364 | fprintf(stdout, "Trailing comment:\n\"%s\"\n", |
3035ba93 | 1365 | mnt_table_get_trailing_comment(tb)); |
059c696f KZ |
1366 | rc = 0; |
1367 | done: | |
efe73c3e | 1368 | mnt_free_iter(itr); |
68164f6c | 1369 | mnt_free_table(tb); |
059c696f | 1370 | return rc; |
6bd8b7a7 KZ |
1371 | } |
1372 | ||
68164f6c | 1373 | int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) |
6bd8b7a7 | 1374 | { |
68164f6c KZ |
1375 | struct libmnt_table *tb; |
1376 | struct libmnt_fs *fs = NULL; | |
1377 | struct libmnt_cache *mpc = NULL; | |
6bd8b7a7 | 1378 | const char *file, *find, *what; |
059c696f | 1379 | int rc = -1; |
6bd8b7a7 KZ |
1380 | |
1381 | if (argc != 4) { | |
1382 | fprintf(stderr, "try --help\n"); | |
059c696f | 1383 | return -EINVAL; |
6bd8b7a7 KZ |
1384 | } |
1385 | ||
1386 | file = argv[1], find = argv[2], what = argv[3]; | |
1387 | ||
cb90e24e | 1388 | tb = create_table(file, FALSE); |
6bd8b7a7 | 1389 | if (!tb) |
059c696f | 1390 | goto done; |
6bd8b7a7 KZ |
1391 | |
1392 | /* create a cache for canonicalized paths */ | |
1393 | mpc = mnt_new_cache(); | |
1394 | if (!mpc) | |
059c696f | 1395 | goto done; |
68164f6c | 1396 | mnt_table_set_cache(tb, mpc); |
6bd8b7a7 KZ |
1397 | |
1398 | if (strcasecmp(find, "source") == 0) | |
68164f6c | 1399 | fs = mnt_table_find_source(tb, what, dr); |
6bd8b7a7 | 1400 | else if (strcasecmp(find, "target") == 0) |
68164f6c | 1401 | fs = mnt_table_find_target(tb, what, dr); |
6bd8b7a7 KZ |
1402 | |
1403 | if (!fs) | |
1404 | fprintf(stderr, "%s: not found %s '%s'\n", file, find, what); | |
1405 | else { | |
5e31c2c8 | 1406 | mnt_fs_print_debug(fs, stdout); |
059c696f | 1407 | rc = 0; |
6bd8b7a7 | 1408 | } |
059c696f | 1409 | done: |
68164f6c | 1410 | mnt_free_table(tb); |
6bd8b7a7 | 1411 | mnt_free_cache(mpc); |
059c696f | 1412 | return rc; |
6bd8b7a7 KZ |
1413 | } |
1414 | ||
68164f6c | 1415 | int test_find_bw(struct libmnt_test *ts, int argc, char *argv[]) |
6bd8b7a7 KZ |
1416 | { |
1417 | return test_find(ts, argc, argv, MNT_ITER_BACKWARD); | |
1418 | } | |
1419 | ||
68164f6c | 1420 | int test_find_fw(struct libmnt_test *ts, int argc, char *argv[]) |
6bd8b7a7 KZ |
1421 | { |
1422 | return test_find(ts, argc, argv, MNT_ITER_FORWARD); | |
1423 | } | |
1424 | ||
68164f6c | 1425 | int test_find_pair(struct libmnt_test *ts, int argc, char *argv[]) |
059c696f | 1426 | { |
68164f6c KZ |
1427 | struct libmnt_table *tb; |
1428 | struct libmnt_fs *fs; | |
975e14bd | 1429 | struct libmnt_cache *mpc = NULL; |
059c696f KZ |
1430 | int rc = -1; |
1431 | ||
cb90e24e | 1432 | tb = create_table(argv[1], FALSE); |
059c696f KZ |
1433 | if (!tb) |
1434 | return -1; | |
975e14bd KZ |
1435 | mpc = mnt_new_cache(); |
1436 | if (!mpc) | |
1437 | goto done; | |
1438 | mnt_table_set_cache(tb, mpc); | |
059c696f | 1439 | |
68164f6c | 1440 | fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD); |
059c696f KZ |
1441 | if (!fs) |
1442 | goto done; | |
1443 | ||
1444 | mnt_fs_print_debug(fs, stdout); | |
1445 | rc = 0; | |
1446 | done: | |
68164f6c | 1447 | mnt_free_table(tb); |
975e14bd | 1448 | mnt_free_cache(mpc); |
059c696f KZ |
1449 | return rc; |
1450 | } | |
1451 | ||
dcc15ce5 KZ |
1452 | int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) |
1453 | { | |
1454 | struct libmnt_table *tb; | |
1455 | struct libmnt_fs *fs; | |
1456 | struct libmnt_cache *mpc = NULL; | |
1457 | int rc = -1; | |
1458 | ||
1459 | tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); | |
1460 | if (!tb) | |
1461 | return -1; | |
1462 | mpc = mnt_new_cache(); | |
1463 | if (!mpc) | |
1464 | goto done; | |
1465 | mnt_table_set_cache(tb, mpc); | |
1466 | ||
1467 | fs = mnt_table_find_mountpoint(tb, argv[1], MNT_ITER_BACKWARD); | |
1468 | if (!fs) | |
1469 | goto done; | |
1470 | ||
1471 | mnt_fs_print_debug(fs, stdout); | |
1472 | rc = 0; | |
1473 | done: | |
1474 | mnt_free_table(tb); | |
1475 | mnt_free_cache(mpc); | |
1476 | return rc; | |
1477 | } | |
1478 | ||
5c60a0ea KZ |
1479 | static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) |
1480 | { | |
1481 | struct libmnt_table *tb = NULL, *fstab = NULL; | |
1482 | struct libmnt_fs *fs; | |
1483 | struct libmnt_iter *itr = NULL; | |
975e14bd | 1484 | struct libmnt_cache *mpc = NULL; |
5c60a0ea KZ |
1485 | int rc; |
1486 | ||
1487 | tb = mnt_new_table_from_file("/proc/self/mountinfo"); | |
1488 | if (!tb) { | |
1489 | fprintf(stderr, "failed to parse mountinfo\n"); | |
1490 | return -1; | |
1491 | } | |
1492 | ||
cb90e24e | 1493 | fstab = create_table(argv[1], FALSE); |
5c60a0ea KZ |
1494 | if (!fstab) |
1495 | goto done; | |
1496 | ||
1497 | itr = mnt_new_iter(MNT_ITER_FORWARD); | |
1498 | if (!itr) | |
1499 | goto done; | |
1500 | ||
975e14bd KZ |
1501 | mpc = mnt_new_cache(); |
1502 | if (!mpc) | |
1503 | goto done; | |
1504 | mnt_table_set_cache(tb, mpc); | |
1505 | ||
5c60a0ea KZ |
1506 | while(mnt_table_next_fs(fstab, itr, &fs) == 0) { |
1507 | if (mnt_table_is_fs_mounted(tb, fs)) | |
1508 | printf("%s already mounted on %s\n", | |
1509 | mnt_fs_get_source(fs), | |
1510 | mnt_fs_get_target(fs)); | |
1511 | else | |
1512 | printf("%s not mounted on %s\n", | |
1513 | mnt_fs_get_source(fs), | |
1514 | mnt_fs_get_target(fs)); | |
1515 | } | |
1516 | ||
1517 | rc = 0; | |
1518 | done: | |
1519 | mnt_free_table(tb); | |
1520 | mnt_free_table(fstab); | |
975e14bd | 1521 | mnt_free_cache(mpc); |
5c60a0ea KZ |
1522 | mnt_free_iter(itr); |
1523 | return rc; | |
1524 | } | |
1525 | ||
6bd8b7a7 KZ |
1526 | int main(int argc, char *argv[]) |
1527 | { | |
68164f6c | 1528 | struct libmnt_test tss[] = { |
cb90e24e | 1529 | { "--parse", test_parse, "<file> [--comments] parse and print tab" }, |
6bd8b7a7 KZ |
1530 | { "--find-forward", test_find_fw, "<file> <source|target> <string>" }, |
1531 | { "--find-backward", test_find_bw, "<file> <source|target> <string>" }, | |
059c696f | 1532 | { "--find-pair", test_find_pair, "<file> <source> <target>" }, |
dcc15ce5 | 1533 | { "--find-mountpoint", test_find_mountpoint, "<path>" }, |
6d94f2dc | 1534 | { "--copy-fs", test_copy_fs, "<file> copy root FS from the file" }, |
5c60a0ea | 1535 | { "--is-mounted", test_is_mounted, "<fstab> check what from <file> are already mounted" }, |
6bd8b7a7 KZ |
1536 | { NULL } |
1537 | }; | |
1538 | ||
1539 | return mnt_run_test(tss, argc, argv); | |
1540 | } | |
1541 | ||
1542 | #endif /* TEST_PROGRAM */ |