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