]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/tab.c
674dc67e5b17796f293b3dadb7a97d7ba85f1788
2 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 * This file is part of libmount from util-linux project.
6 * Copyright (C) 2008-2018 Karel Zak <kzak@redhat.com>
8 * libmount is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
16 * @title: Table of filesystems
17 * @short_description: container for entries from fstab, mtab or mountinfo
19 * Note that mnt_table_find_* functions are mount(8) compatible. These functions
20 * try to find an entry in more iterations, where the first attempt is always
21 * based on comparison with unmodified (non-canonicalized or un-evaluated)
22 * paths or tags. For example a fstab with two entries:
25 * LABEL=foo /foo auto rw
26 * /dev/foo /foo auto rw
30 * where both lines are used for the *same* device, then
33 * mnt_table_find_source(tb, "/dev/foo", &fs);
36 * will returns the second line, and
39 * mnt_table_find_source(tb, "LABEL=foo", &fs);
42 * will returns the first entry, and
45 * mnt_table_find_source(tb, "UUID=anyuuid", &fs);
48 * will return the first entry (if UUID matches with the device).
55 #include "fileutils.h"
56 #include "canonicalize.h"
58 int is_mountinfo(struct libmnt_table
*tb
)
65 fs
= list_first_entry(&tb
->ents
, struct libmnt_fs
, ents
);
66 if (fs
&& mnt_fs_is_kernel(fs
) && mnt_fs_get_root(fs
))
75 * The tab is a container for struct libmnt_fs entries that usually represents a fstab,
76 * mtab or mountinfo file from your system.
78 * See also mnt_table_parse_file().
80 * Returns: newly allocated tab struct.
82 struct libmnt_table
*mnt_new_table(void)
84 struct libmnt_table
*tb
= NULL
;
86 tb
= calloc(1, sizeof(*tb
));
90 DBG(TAB
, ul_debugobj(tb
, "alloc"));
92 INIT_LIST_HEAD(&tb
->ents
);
100 * Removes all entries (filesystems) from the table. The filesystems with zero
101 * reference count will be deallocated.
103 * Returns: 0 on success or negative number in case of error.
105 int mnt_reset_table(struct libmnt_table
*tb
)
110 DBG(TAB
, ul_debugobj(tb
, "reset"));
112 while (!list_empty(&tb
->ents
)) {
113 struct libmnt_fs
*fs
= list_entry(tb
->ents
.next
,
114 struct libmnt_fs
, ents
);
115 mnt_table_remove_fs(tb
, fs
);
126 * Increments reference counter.
128 void mnt_ref_table(struct libmnt_table
*tb
)
132 /*DBG(FS, ul_debugobj(tb, "ref=%d", tb->refcount));*/
140 * De-increments reference counter, on zero the @tb is automatically
141 * deallocated by mnt_free_table().
143 void mnt_unref_table(struct libmnt_table
*tb
)
147 /*DBG(FS, ul_debugobj(tb, "unref=%d", tb->refcount));*/
148 if (tb
->refcount
<= 0)
158 * Deallocates the table. This function does not care about reference count. Don't
159 * use this function directly -- it's better to use mnt_unref_table().
161 * The table entries (filesystems) are unreferenced by mnt_reset_table() and
162 * cache by mnt_unref_cache().
164 void mnt_free_table(struct libmnt_table
*tb
)
170 DBG(TAB
, ul_debugobj(tb
, "free [refcount=%d]", tb
->refcount
));
172 mnt_unref_cache(tb
->cache
);
173 free(tb
->comm_intro
);
179 * mnt_table_get_nents:
180 * @tb: pointer to tab
182 * Returns: number of entries in table.
184 int mnt_table_get_nents(struct libmnt_table
*tb
)
186 return tb
? tb
->nents
: 0;
190 * mnt_table_is_empty:
191 * @tb: pointer to tab
193 * Returns: 1 if the table is without filesystems, or 0.
195 int mnt_table_is_empty(struct libmnt_table
*tb
)
197 return tb
== NULL
|| list_empty(&tb
->ents
) ? 1 : 0;
201 * mnt_table_set_userdata:
202 * @tb: pointer to tab
203 * @data: pointer to user data
205 * Sets pointer to the private user data.
207 * Returns: 0 on success or negative number in case of error.
209 int mnt_table_set_userdata(struct libmnt_table
*tb
, void *data
)
219 * mnt_table_get_userdata:
220 * @tb: pointer to tab
222 * Returns: pointer to user's data.
224 void *mnt_table_get_userdata(struct libmnt_table
*tb
)
226 return tb
? tb
->userdata
: NULL
;
230 * mnt_table_enable_comments:
231 * @tb: pointer to tab
232 * @enable: TRUE or FALSE
234 * Enables parsing of comments.
236 * The initial (intro) file comment is accessible by
237 * mnt_table_get_intro_comment(). The intro and the comment of the first fstab
238 * entry has to be separated by blank line. The filesystem comments are
239 * accessible by mnt_fs_get_comment(). The trailing fstab comment is accessible
240 * by mnt_table_get_trailing_comment().
248 * # this comments belongs to the first fs
249 * LABEL=foo /mnt/foo auto defaults 1 2
250 * # this comments belongs to the second fs
251 * LABEL=bar /mnt/bar auto defaults 1 2
256 void mnt_table_enable_comments(struct libmnt_table
*tb
, int enable
)
263 * mnt_table_with_comments:
264 * @tb: pointer to table
266 * Returns: 1 if comments parsing is enabled, or 0.
268 int mnt_table_with_comments(struct libmnt_table
*tb
)
271 return tb
? tb
->comms
: 0;
275 * mnt_table_get_intro_comment:
276 * @tb: pointer to tab
278 * Returns: initial comment in tb
280 const char *mnt_table_get_intro_comment(struct libmnt_table
*tb
)
282 return tb
? tb
->comm_intro
: NULL
;
286 * mnt_table_set_into_comment:
287 * @tb: pointer to tab
288 * @comm: comment or NULL
290 * Sets the initial comment in tb.
292 * Returns: 0 on success or negative number in case of error.
294 int mnt_table_set_intro_comment(struct libmnt_table
*tb
, const char *comm
)
296 return strdup_to_struct_member(tb
, comm_intro
, comm
);
300 * mnt_table_append_into_comment:
301 * @tb: pointer to tab
302 * @comm: comment of NULL
304 * Appends the initial comment in tb.
306 * Returns: 0 on success or negative number in case of error.
308 int mnt_table_append_intro_comment(struct libmnt_table
*tb
, const char *comm
)
312 return append_string(&tb
->comm_intro
, comm
);
316 * mnt_table_get_trailing_comment:
317 * @tb: pointer to tab
319 * Returns: table trailing comment
321 const char *mnt_table_get_trailing_comment(struct libmnt_table
*tb
)
323 return tb
? tb
->comm_tail
: NULL
;
327 * mnt_table_set_trailing_comment
328 * @tb: pointer to tab
329 * @comm: comment string
331 * Sets the trailing comment in table.
333 * Returns: 0 on success or negative number in case of error.
335 int mnt_table_set_trailing_comment(struct libmnt_table
*tb
, const char *comm
)
337 return strdup_to_struct_member(tb
, comm_tail
, comm
);
341 * mnt_table_append_trailing_comment:
342 * @tb: pointer to tab
343 * @comm: comment of NULL
345 * Appends to the trailing table comment.
347 * Returns: 0 on success or negative number in case of error.
349 int mnt_table_append_trailing_comment(struct libmnt_table
*tb
, const char *comm
)
353 return append_string(&tb
->comm_tail
, comm
);
357 * mnt_table_set_cache:
358 * @tb: pointer to tab
359 * @mpc: pointer to struct libmnt_cache instance
361 * Sets up a cache for canonicalized paths and evaluated tags (LABEL/UUID). The
362 * cache is recommended for mnt_table_find_*() functions.
364 * The cache could be shared between more tabs. Be careful when you share the
365 * same cache between more threads -- currently the cache does not provide any
368 * This function increments cache reference counter. It's recommended to use
369 * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache
370 * referenced by @tb only.
372 * See also mnt_new_cache().
374 * Returns: 0 on success or negative number in case of error.
376 int mnt_table_set_cache(struct libmnt_table
*tb
, struct libmnt_cache
*mpc
)
381 mnt_ref_cache(mpc
); /* new */
382 mnt_unref_cache(tb
->cache
); /* old */
388 * mnt_table_get_cache:
389 * @tb: pointer to tab
391 * Returns: pointer to struct libmnt_cache instance or NULL.
393 struct libmnt_cache
*mnt_table_get_cache(struct libmnt_table
*tb
)
395 return tb
? tb
->cache
: NULL
;
401 * @fs: entry to look for
403 * Checks if @fs is part of table @tb.
405 * Returns: index of @fs in table, 0 if not found or negative number in case of error.
407 int mnt_table_find_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
415 if (list_empty(&fs
->ents
))
418 /* Let's use directly list rather than mnt_table_next_fs() as we
419 * compare list entry with fs only.
421 list_for_each(p
, &tb
->ents
) {
423 if (list_entry(p
, struct libmnt_fs
, ents
) == fs
)
435 * Adds a new entry to tab and increment @fs reference counter. Don't forget to
436 * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs
437 * referenced by the table only.
439 * Returns: 0 on success or negative number in case of error.
441 int mnt_table_add_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
446 if (!list_empty(&fs
->ents
))
450 list_add_tail(&fs
->ents
, &tb
->ents
);
453 DBG(TAB
, ul_debugobj(tb
, "add entry: %s %s",
454 mnt_fs_get_source(fs
), mnt_fs_get_target(fs
)));
458 static int __table_insert_fs(
459 struct libmnt_table
*tb
, int before
,
460 struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
462 struct list_head
*head
= pos
? &pos
->ents
: &tb
->ents
;
465 list_add(&fs
->ents
, head
);
467 list_add_tail(&fs
->ents
, head
);
471 DBG(TAB
, ul_debugobj(tb
, "insert entry: %s %s",
472 mnt_fs_get_source(fs
), mnt_fs_get_target(fs
)));
477 * mnt_table_insert_fs:
479 * @before: 1 to insert before pos, 0 to insert after pos
480 * @pos: entry to specify position or NULL
483 * Adds a new entry to @tb before or after a specific table entry @pos. If the
484 * @pos is NULL than add the begin of the @tab if @before is 1; or to the tail
485 * of the @tb if @before is 0.
487 * This function inncrements reference to @fs. Don't forget to use
488 * mnt_unref_fs() after mnt_table_insert_fs() if you want to keep the @fs
489 * referenced by the table only.
492 * Returns: 0 on success or negative number in case of error.
494 int mnt_table_insert_fs(struct libmnt_table
*tb
, int before
,
495 struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
500 if (!list_empty(&fs
->ents
))
503 if (pos
&& mnt_table_find_fs(tb
, pos
) < 1)
507 return __table_insert_fs(tb
, before
, pos
, fs
);
512 * @src: tab pointer of source table
513 * @dst: tab pointer of destination table
514 * @before: 1 to move before position, 0 to move after position
515 * @pos: entry to specify position or NULL
518 * Removes @fs from @src table and adds it before/after a specific entry @pos
519 * of @dst table. If the @pos is NULL than add the begin of the @dst if @before
520 * is 1; or to the tail of the @dst if @before is 0.
522 * The reference counter of @fs is not modified.
524 * Returns: 0 on success or negative number in case of error.
526 int mnt_table_move_fs(struct libmnt_table
*src
, struct libmnt_table
*dst
,
527 int before
, struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
529 if (!src
|| !dst
|| !fs
)
532 if (mnt_table_find_fs(src
, fs
) < 1)
535 if (pos
&& mnt_table_find_fs(dst
, pos
) < 1)
538 /* remove from source */
539 list_del_init(&fs
->ents
);
542 /* insert to the destination */
543 return __table_insert_fs(dst
, before
, pos
, fs
);
548 * mnt_table_remove_fs:
552 * Removes the @fs from the table and de-increment reference counter of the @fs. The
553 * filesystem with zero reference counter will be deallocated. Don't forget to use
554 * mnt_ref_fs() before call mnt_table_remove_fs() if you want to use @fs later.
556 * Returns: 0 on success or negative number in case of error.
558 int mnt_table_remove_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
560 if (!tb
|| !fs
|| mnt_table_find_fs(tb
, fs
) < 1)
563 list_del_init(&fs
->ents
);
570 static inline struct libmnt_fs
*get_parent_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
572 struct libmnt_iter itr
;
574 int parent_id
= mnt_fs_get_parent_id(fs
);
576 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
577 while (mnt_table_next_fs(tb
, &itr
, &x
) == 0) {
578 if (mnt_fs_get_id(x
) == parent_id
)
586 * mnt_table_get_root_fs:
587 * @tb: mountinfo file (/proc/self/mountinfo)
588 * @root: returns pointer to the root filesystem (/)
590 * The function uses the parent ID from the mountinfo file to determine the
591 * root filesystem (the filesystem with the smallest ID with parent ID missing
592 * in the table). The function is designed mostly for applications where it is
593 * necessary to sort mountpoints by IDs to get the tree of the mountpoints
594 * (e.g. findmnt default output).
596 * If you're not sure, then use
598 * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD);
600 * this is more robust and usable for arbitrary tab files (including fstab).
602 * Returns: 0 on success or negative number in case of error.
604 int mnt_table_get_root_fs(struct libmnt_table
*tb
, struct libmnt_fs
**root
)
606 struct libmnt_iter itr
;
607 struct libmnt_fs
*fs
;
610 if (!tb
|| !root
|| !is_mountinfo(tb
))
613 DBG(TAB
, ul_debugobj(tb
, "lookup root fs"));
617 /* get smallest possible ID from the table */
618 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
619 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
620 int id
= mnt_fs_get_parent_id(fs
);
622 if (!*root
|| id
< root_id
) {
628 /* go to the root node by "parent_id -> id" relation */
630 struct libmnt_fs
*x
= get_parent_fs(tb
, *root
);
631 if (!x
|| x
== *root
)
633 DBG(TAB
, ul_debugobj(tb
, " messy mountinfo, walk to %s", mnt_fs_get_target(x
)));
637 return *root
? 0 : -EINVAL
;
641 * mnt_table_next_child_fs:
642 * @tb: mountinfo file (/proc/self/mountinfo)
644 * @parent: parental FS
645 * @chld: returns the next child filesystem
647 * Note that filesystems are returned in the order of mounting (according to
648 * IDs in /proc/self/mountinfo).
650 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
652 int mnt_table_next_child_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
653 struct libmnt_fs
*parent
, struct libmnt_fs
**chld
)
655 struct libmnt_fs
*fs
;
656 int parent_id
, lastchld_id
= 0, chld_id
= 0;
658 if (!tb
|| !itr
|| !parent
|| !is_mountinfo(tb
))
661 DBG(TAB
, ul_debugobj(tb
, "lookup next child of '%s'",
662 mnt_fs_get_target(parent
)));
664 parent_id
= mnt_fs_get_id(parent
);
666 /* get ID of the previously returned child */
667 if (itr
->head
&& itr
->p
!= itr
->head
) {
668 MNT_ITER_ITERATE(itr
, fs
, struct libmnt_fs
, ents
);
669 lastchld_id
= mnt_fs_get_id(fs
);
674 mnt_reset_iter(itr
, MNT_ITER_FORWARD
);
675 while(mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
678 if (mnt_fs_get_parent_id(fs
) != parent_id
)
681 id
= mnt_fs_get_id(fs
);
683 /* avoid an infinite loop. This only happens in rare cases
684 * such as in early userspace when the rootfs is its own parent */
688 if ((!lastchld_id
|| id
> lastchld_id
) &&
689 (!*chld
|| id
< chld_id
)) {
696 return 1; /* end of iterator */
698 /* set the iterator to the @chld for the next call */
699 mnt_table_set_iter(tb
, itr
, *chld
);
708 * @fs: returns the next tab entry
710 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
715 * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
716 * const char *dir = mnt_fs_get_target(fs);
717 * printf("mount point: %s\n", dir);
722 * lists all mountpoints from fstab in reverse order.
724 int mnt_table_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
**fs
)
728 if (!tb
|| !itr
|| !fs
)
733 MNT_ITER_INIT(itr
, &tb
->ents
);
734 if (itr
->p
!= itr
->head
) {
735 MNT_ITER_ITERATE(itr
, *fs
, struct libmnt_fs
, ents
);
743 * mnt_table_first_fs:
745 * @fs: returns the first tab entry
747 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
749 int mnt_table_first_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
753 if (list_empty(&tb
->ents
))
755 *fs
= list_first_entry(&tb
->ents
, struct libmnt_fs
, ents
);
762 * @fs: returns the last tab entry
764 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
766 int mnt_table_last_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
770 if (list_empty(&tb
->ents
))
772 *fs
= list_last_entry(&tb
->ents
, struct libmnt_fs
, ents
);
777 * mnt_table_find_next_fs:
780 * @match_func: function returning 1 or 0
781 * @userdata: extra data for match_func
782 * @fs: returns pointer to the next matching table entry
784 * This function allows searching in @tb.
786 * Returns: negative number in case of error, 1 at end of table or 0 o success.
788 int mnt_table_find_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
789 int (*match_func
)(struct libmnt_fs
*, void *), void *userdata
,
790 struct libmnt_fs
**fs
)
792 if (!tb
|| !itr
|| !fs
|| !match_func
)
795 DBG(TAB
, ul_debugobj(tb
, "lookup next fs"));
798 MNT_ITER_INIT(itr
, &tb
->ents
);
801 if (itr
->p
!= itr
->head
)
802 MNT_ITER_ITERATE(itr
, *fs
, struct libmnt_fs
, ents
);
806 if (match_func(*fs
, userdata
))
814 static int mnt_table_move_parent(struct libmnt_table
*tb
, int oldid
, int newid
)
816 struct libmnt_iter itr
;
817 struct libmnt_fs
*fs
;
821 if (list_empty(&tb
->ents
))
824 DBG(TAB
, ul_debugobj(tb
, "moving parent ID from %d -> %d", oldid
, newid
));
825 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
827 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
828 if (fs
->parent
== oldid
)
838 * @cmp: function to compare filesystems
840 * This function de-duplicate the @tb, but does not change order of the
841 * filesystems. The @cmp function has to return 0 if the filesystems are
842 * equal, otherwise non-zero.
844 * The default is to keep in the table later mounted filesystems (function uses
845 * backward mode iterator).
847 * @MNT_UNIQ_FORWARD: remove later mounted filesystems
848 * @MNT_UNIQ_KEEPTREE: keep parent->id relationship still valid
850 * Returns: negative number in case of error, or 0 o success.
852 int mnt_table_uniq_fs(struct libmnt_table
*tb
, int flags
,
853 int (*cmp
)(struct libmnt_table
*,
857 struct libmnt_iter itr
;
858 struct libmnt_fs
*fs
;
859 int direction
= MNT_ITER_BACKWARD
;
863 if (list_empty(&tb
->ents
))
866 if (flags
& MNT_UNIQ_FORWARD
)
867 direction
= MNT_ITER_FORWARD
;
869 DBG(TAB
, ul_debugobj(tb
, "de-duplicate"));
870 mnt_reset_iter(&itr
, direction
);
872 if ((flags
& MNT_UNIQ_KEEPTREE
) && !is_mountinfo(tb
))
873 flags
&= ~MNT_UNIQ_KEEPTREE
;
875 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
877 struct libmnt_iter xtr
;
880 mnt_reset_iter(&xtr
, direction
);
881 while (want
&& mnt_table_next_fs(tb
, &xtr
, &x
) == 0) {
884 want
= cmp(tb
, x
, fs
) != 0;
888 if (flags
& MNT_UNIQ_KEEPTREE
)
889 mnt_table_move_parent(tb
, mnt_fs_get_id(fs
),
890 mnt_fs_get_parent_id(fs
));
892 DBG(TAB
, ul_debugobj(tb
, "remove duplicate %s",
893 mnt_fs_get_target(fs
)));
894 mnt_table_remove_fs(tb
, fs
);
902 * mnt_table_set_iter:
907 * Sets @iter to the position of @fs in the file @tb.
909 * Returns: 0 on success, negative number in case of error.
911 int mnt_table_set_iter(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
*fs
)
913 if (!tb
|| !itr
|| !fs
)
916 MNT_ITER_INIT(itr
, &tb
->ents
);
923 * mnt_table_find_mountpoint:
926 * @direction: MNT_ITER_{FORWARD,BACKWARD}
928 * Same as mnt_get_mountpoint(), except this function does not rely on
931 * Returns: a tab entry or NULL.
933 struct libmnt_fs
*mnt_table_find_mountpoint(struct libmnt_table
*tb
,
940 if (!tb
|| !path
|| !*path
)
942 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
945 DBG(TAB
, ul_debugobj(tb
, "lookup MOUNTPOINT: '%s'", path
));
947 if (mnt_stat_mountpoint(path
, &st
))
956 struct libmnt_fs
*fs
;
958 fs
= mnt_table_find_target(tb
, mnt
, direction
);
964 p
= stripoff_last_component(mnt
);
967 } while (mnt
&& *(mnt
+ 1) != '\0');
970 return mnt_table_find_target(tb
, "/", direction
);
974 * mnt_table_find_target:
976 * @path: mountpoint directory
977 * @direction: MNT_ITER_{FORWARD,BACKWARD}
979 * Try to lookup an entry in the given tab, three iterations are possible, the first
980 * with @path, the second with realpath(@path) and the third with realpath(@path)
981 * against realpath(fs->target). The 2nd and 3rd iterations are not performed when
982 * the @tb cache is not set (see mnt_table_set_cache()). If
983 * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any
984 * @fs->target found in @mtab (see mnt_resolve_target()).
986 * Returns: a tab entry or NULL.
988 struct libmnt_fs
*mnt_table_find_target(struct libmnt_table
*tb
, const char *path
, int direction
)
990 struct libmnt_iter itr
;
991 struct libmnt_fs
*fs
= NULL
;
994 if (!tb
|| !path
|| !*path
)
996 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
999 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s'", path
));
1001 /* native @target */
1002 mnt_reset_iter(&itr
, direction
);
1003 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1004 if (mnt_fs_streq_target(fs
, path
))
1008 /* try absolute path */
1009 if (is_relative_path(path
) && (cn
= absolute_path(path
))) {
1010 DBG(TAB
, ul_debugobj(tb
, "lookup absolute TARGET: '%s'", cn
));
1011 mnt_reset_iter(&itr
, direction
);
1012 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1013 if (mnt_fs_streq_target(fs
, cn
)) {
1021 if (!tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
1024 DBG(TAB
, ul_debugobj(tb
, "lookup canonical TARGET: '%s'", cn
));
1026 /* canonicalized paths in struct libmnt_table */
1027 mnt_reset_iter(&itr
, direction
);
1028 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1029 if (mnt_fs_streq_target(fs
, cn
))
1033 /* non-canonical path in struct libmnt_table
1034 * -- note that mountpoint in /proc/self/mountinfo is already
1035 * canonicalized by the kernel
1037 mnt_reset_iter(&itr
, direction
);
1038 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1042 || mnt_fs_is_swaparea(fs
)
1043 || mnt_fs_is_kernel(fs
)
1044 || (*fs
->target
== '/' && *(fs
->target
+ 1) == '\0'))
1047 p
= mnt_resolve_target(fs
->target
, tb
->cache
);
1048 /* both canonicalized, strcmp() is fine here */
1049 if (p
&& strcmp(cn
, p
) == 0)
1056 * mnt_table_find_srcpath:
1058 * @path: source path (devname or dirname) or NULL
1059 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1061 * Try to lookup an entry in the given tab, four iterations are possible, the first
1062 * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..)
1063 * from @path and the fourth with realpath(@path) against realpath(entry->srcpath).
1065 * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
1066 * set (see mnt_table_set_cache()).
1068 * For btrfs returns tab entry for default id.
1070 * Note that NULL is a valid source path; it will be replaced with "none". The
1071 * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
1073 * Returns: a tab entry or NULL.
1075 struct libmnt_fs
*mnt_table_find_srcpath(struct libmnt_table
*tb
, const char *path
, int direction
)
1077 struct libmnt_iter itr
;
1078 struct libmnt_fs
*fs
= NULL
;
1079 int ntags
= 0, nents
;
1083 if (!tb
|| !path
|| !*path
)
1085 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1088 DBG(TAB
, ul_debugobj(tb
, "lookup SRCPATH: '%s'", path
));
1091 mnt_reset_iter(&itr
, direction
);
1093 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1095 if (mnt_fs_streq_srcpath(fs
, path
)) {
1096 #ifdef HAVE_BTRFS_SUPPORT
1097 if (fs
->fstype
&& !strcmp(fs
->fstype
, "btrfs")) {
1098 uint64_t default_id
= btrfs_get_default_subvol_id(mnt_fs_get_target(fs
));
1102 if (default_id
== UINT64_MAX
)
1103 DBG(TAB
, ul_debug("not found btrfs volume setting"));
1105 else if (mnt_fs_get_option(fs
, "subvolid", &val
, &len
) == 0) {
1108 if (mnt_parse_offset(val
, len
, &subvol_id
)) {
1109 DBG(TAB
, ul_debugobj(tb
, "failed to parse subvolid="));
1112 if (subvol_id
!= default_id
)
1116 #endif /* HAVE_BTRFS_SUPPORT */
1119 if (mnt_fs_get_tag(fs
, NULL
, NULL
) == 0)
1123 if (!path
|| !tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
1126 DBG(TAB
, ul_debugobj(tb
, "lookup canonical SRCPATH: '%s'", cn
));
1128 nents
= mnt_table_get_nents(tb
);
1130 /* canonicalized paths in struct libmnt_table */
1131 if (ntags
< nents
) {
1132 mnt_reset_iter(&itr
, direction
);
1133 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1134 if (mnt_fs_streq_srcpath(fs
, cn
))
1141 int rc
= mnt_cache_read_tags(tb
->cache
, cn
);
1143 mnt_reset_iter(&itr
, direction
);
1146 /* @path's TAGs are in the cache */
1147 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1150 if (mnt_fs_get_tag(fs
, &t
, &v
))
1153 if (mnt_cache_device_has_tag(tb
->cache
, cn
, t
, v
))
1156 } else if (rc
< 0 && errno
== EACCES
) {
1157 /* @path is inaccessible, try evaluating all TAGs in @tb
1158 * by udev symlinks -- this could be expensive on systems
1159 * with a huge fstab/mtab */
1160 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1161 const char *t
, *v
, *x
;
1162 if (mnt_fs_get_tag(fs
, &t
, &v
))
1164 x
= mnt_resolve_tag(t
, v
, tb
->cache
);
1166 /* both canonicalized, strcmp() is fine here */
1167 if (x
&& strcmp(x
, cn
) == 0)
1173 /* non-canonicalized paths in struct libmnt_table */
1174 if (ntags
<= nents
) {
1175 mnt_reset_iter(&itr
, direction
);
1176 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1177 if (mnt_fs_is_netfs(fs
) || mnt_fs_is_pseudofs(fs
))
1179 p
= mnt_fs_get_srcpath(fs
);
1181 p
= mnt_resolve_path(p
, tb
->cache
);
1183 /* both canonicalized, strcmp() is fine here */
1184 if (p
&& strcmp(p
, cn
) == 0)
1194 * mnt_table_find_tag:
1196 * @tag: tag name (e.g "LABEL", "UUID", ...)
1198 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1200 * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and
1201 * @val, for the second attempt the tag is evaluated (converted to the device
1202 * name) and mnt_table_find_srcpath() is performed. The second attempt is not
1203 * performed when @tb cache is not set (see mnt_table_set_cache()).
1205 * Returns: a tab entry or NULL.
1207 struct libmnt_fs
*mnt_table_find_tag(struct libmnt_table
*tb
, const char *tag
,
1208 const char *val
, int direction
)
1210 struct libmnt_iter itr
;
1211 struct libmnt_fs
*fs
= NULL
;
1213 if (!tb
|| !tag
|| !*tag
|| !val
)
1215 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1218 DBG(TAB
, ul_debugobj(tb
, "lookup by TAG: %s %s", tag
, val
));
1220 /* look up by TAG */
1221 mnt_reset_iter(&itr
, direction
);
1222 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1223 if (fs
->tagname
&& fs
->tagval
&&
1224 strcmp(fs
->tagname
, tag
) == 0 &&
1225 strcmp(fs
->tagval
, val
) == 0)
1230 /* look up by device name */
1231 char *cn
= mnt_resolve_tag(tag
, val
, tb
->cache
);
1233 return mnt_table_find_srcpath(tb
, cn
, direction
);
1239 * mnt_table_find_target_with_option:
1241 * @path: mountpoint directory
1242 * @option: option name (e.g "subvol", "subvolid", ...)
1243 * @val: option value or NULL
1244 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1246 * Try to lookup an entry in the given tab that matches combination of @path
1247 * and @option. In difference to mnt_table_find_target(), only @path iteration
1248 * is done. No lookup by device name, no canonicalization.
1250 * Returns: a tab entry or NULL.
1254 struct libmnt_fs
*mnt_table_find_target_with_option(
1255 struct libmnt_table
*tb
, const char *path
,
1256 const char *option
, const char *val
, int direction
)
1258 struct libmnt_iter itr
;
1259 struct libmnt_fs
*fs
= NULL
;
1260 char *optval
= NULL
;
1261 size_t optvalsz
= 0, valsz
= val
? strlen(val
) : 0;
1263 if (!tb
|| !path
|| !*path
|| !option
|| !*option
|| !val
)
1265 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1268 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s' with OPTION %s %s", path
, option
, val
));
1270 /* look up by native @target with OPTION */
1271 mnt_reset_iter(&itr
, direction
);
1272 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1273 if (mnt_fs_streq_target(fs
, path
)
1274 && mnt_fs_get_option(fs
, option
, &optval
, &optvalsz
) == 0
1275 && (!val
|| (optvalsz
== valsz
1276 && strncmp(optval
, val
, optvalsz
) == 0)))
1283 * mnt_table_find_source:
1285 * @source: TAG or path
1286 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1288 * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care
1289 * about the @source format (device, LABEL, UUID, ...). This function parses
1290 * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath().
1292 * Returns: a tab entry or NULL.
1294 struct libmnt_fs
*mnt_table_find_source(struct libmnt_table
*tb
,
1295 const char *source
, int direction
)
1297 struct libmnt_fs
*fs
;
1298 char *t
= NULL
, *v
= NULL
;
1302 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1305 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: '%s'", source
));
1307 if (blkid_parse_tag_string(source
, &t
, &v
) || !mnt_valid_tagname(t
))
1308 fs
= mnt_table_find_srcpath(tb
, source
, direction
);
1310 fs
= mnt_table_find_tag(tb
, t
, v
, direction
);
1319 * mnt_table_find_pair
1321 * @source: TAG or path
1322 * @target: mountpoint
1323 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1325 * This function is implemented by mnt_fs_match_source() and
1326 * mnt_fs_match_target() functions. It means that this is more expensive than
1327 * others mnt_table_find_* function, because every @tab entry is fully evaluated.
1329 * Returns: a tab entry or NULL.
1331 struct libmnt_fs
*mnt_table_find_pair(struct libmnt_table
*tb
, const char *source
,
1332 const char *target
, int direction
)
1334 struct libmnt_fs
*fs
= NULL
;
1335 struct libmnt_iter itr
;
1337 if (!tb
|| !target
|| !*target
|| !source
|| !*source
)
1339 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1342 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: %s TARGET: %s", source
, target
));
1344 mnt_reset_iter(&itr
, direction
);
1345 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1347 if (mnt_fs_match_target(fs
, target
, tb
->cache
) &&
1348 mnt_fs_match_source(fs
, source
, tb
->cache
))
1356 * mnt_table_find_devno
1357 * @tb: /proc/self/mountinfo
1358 * @devno: device number
1359 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1361 * Note that zero could be a valid device number for the root pseudo filesystem (e.g.
1364 * Returns: a tab entry or NULL.
1366 struct libmnt_fs
*mnt_table_find_devno(struct libmnt_table
*tb
,
1367 dev_t devno
, int direction
)
1369 struct libmnt_fs
*fs
= NULL
;
1370 struct libmnt_iter itr
;
1374 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1377 DBG(TAB
, ul_debugobj(tb
, "lookup DEVNO: %d", (int) devno
));
1379 mnt_reset_iter(&itr
, direction
);
1381 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1382 if (mnt_fs_get_devno(fs
) == devno
)
1389 static char *remove_mountpoint_from_path(const char *path
, const char *mnt
)
1396 p
= sz
> 1 ? path
+ sz
: path
;
1398 res
= *p
? strdup(p
) : strdup("/");
1399 DBG(UTILS
, ul_debug("%s fs-root is %s", path
, res
));
1403 #ifdef HAVE_BTRFS_SUPPORT
1404 static int get_btrfs_fs_root(struct libmnt_table
*tb
, struct libmnt_fs
*fs
, char **root
)
1406 char *vol
= NULL
, *p
;
1407 size_t sz
, volsz
= 0;
1409 DBG(BTRFS
, ul_debug("lookup for btrfs FS root"));
1412 if (mnt_fs_get_option(fs
, "subvolid", &vol
, &volsz
) == 0) {
1414 struct libmnt_fs
*f
;
1415 char subvolidstr
[sizeof(stringify_value(UINT64_MAX
))];
1417 DBG(BTRFS
, ul_debug(" found subvolid=%s, checking", vol
));
1419 assert (volsz
+ 1 < sizeof(stringify_value(UINT64_MAX
)));
1420 memcpy(subvolidstr
, vol
, volsz
);
1421 subvolidstr
[volsz
] = '\0';
1423 target
= mnt_resolve_target(mnt_fs_get_target(fs
), tb
->cache
);
1427 DBG(BTRFS
, ul_debug(" trying target=%s subvolid=%s", target
, subvolidstr
));
1428 f
= mnt_table_find_target_with_option(tb
, target
,
1429 "subvolid", subvolidstr
,
1436 /* Instead of set of BACKREF queries constructing subvol path
1437 * corresponding to a particular subvolid, use the one in
1438 * mountinfo. Kernel keeps subvol path up to date.
1440 if (mnt_fs_get_option(f
, "subvol", &vol
, &volsz
) != 0)
1443 } else if (mnt_fs_get_option(fs
, "subvol", &vol
, &volsz
) != 0) {
1444 /* If fstab entry does not contain "subvol", we have to
1445 * check, whether btrfs has default subvolume defined.
1447 uint64_t default_id
;
1449 struct libmnt_fs
*f
;
1450 char default_id_str
[sizeof(stringify_value(UINT64_MAX
))];
1452 DBG(BTRFS
, ul_debug(" subvolid/subvol not found, checking default"));
1454 default_id
= btrfs_get_default_subvol_id(mnt_fs_get_target(fs
));
1455 if (default_id
== UINT64_MAX
)
1458 /* Volume has default subvolume. Check if it matches to
1459 * the one in mountinfo.
1461 * Only kernel >= 4.2 reports subvolid. On older
1462 * kernels, there is no reasonable way to detect which
1463 * subvolume was mounted.
1465 target
= mnt_resolve_target(mnt_fs_get_target(fs
), tb
->cache
);
1469 snprintf(default_id_str
, sizeof(default_id_str
), "%llu",
1470 (unsigned long long int) default_id
);
1472 DBG(BTRFS
, ul_debug(" trying target=%s default subvolid=%s",
1473 target
, default_id_str
));
1475 f
= mnt_table_find_target_with_option(tb
, target
,
1476 "subvolid", default_id_str
,
1483 /* Instead of set of BACKREF queries constructing
1484 * subvol path, use the one in mountinfo. Kernel does
1485 * the evaluation for us.
1487 DBG(BTRFS
, ul_debug("setting FS root: btrfs default subvolid = %s",
1490 if (mnt_fs_get_option(f
, "subvol", &vol
, &volsz
) != 0)
1494 DBG(BTRFS
, ul_debug(" using subvol=%s", vol
));
1498 *root
= malloc(sz
+ 1);
1504 memcpy(p
, vol
, volsz
);
1505 *(*root
+ sz
) = '\0';
1509 DBG(BTRFS
, ul_debug(" not found btrfs volume setting"));
1512 DBG(BTRFS
, ul_debug(" error on btrfs volume setting evaluation"));
1513 return errno
? -errno
: -1;
1515 #endif /* HAVE_BTRFS_SUPPORT */
1517 static const char *get_cifs_unc_subdir_path (const char *unc
)
1520 * 1 or more slash: %*[/]
1521 * 1 or more non-slash: %*[^/]
1522 * number of byte read: %n
1525 int r
= sscanf(unc
, "%*[/]%*[^/]%*[/]%*[^/]%n", &share_end
);
1526 if (r
== EOF
|| share_end
== 0)
1528 return unc
+ share_end
;
1532 * tb: /proc/self/mountinfo
1534 * mountflags: MS_BIND or 0
1535 * fsroot: fs-root that will probably be used in the mountinfo file
1536 * for @fs after mount(2)
1538 * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
1540 * If @tb is NULL then defaults to '/'.
1542 * Returns: entry from @tb that will be used as a source for @fs if the @fs is
1545 * Don't export to library API!
1547 struct libmnt_fs
*mnt_table_get_fs_root(struct libmnt_table
*tb
,
1548 struct libmnt_fs
*fs
,
1549 unsigned long mountflags
,
1553 const char *mnt
= NULL
;
1554 struct libmnt_fs
*src_fs
= NULL
;
1559 DBG(TAB
, ul_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs
)));
1561 if (tb
&& (mountflags
& MS_BIND
)) {
1562 const char *src
, *src_root
;
1565 DBG(TAB
, ul_debug("fs-root for bind"));
1567 src
= xsrc
= mnt_resolve_spec(mnt_fs_get_source(fs
), tb
->cache
);
1569 struct libmnt_fs
*f
= mnt_table_find_mountpoint(tb
,
1570 src
, MNT_ITER_BACKWARD
);
1572 mnt
= mnt_fs_get_target(f
);
1575 root
= remove_mountpoint_from_path(src
, mnt
);
1577 if (xsrc
&& !tb
->cache
) {
1584 src_fs
= mnt_table_find_target(tb
, mnt
, MNT_ITER_BACKWARD
);
1586 DBG(TAB
, ul_debug("not found '%s' in mountinfo -- using default", mnt
));
1590 /* It's possible that fstab_fs source is subdirectory on btrfs
1591 * subvolume or another bind mount. For example:
1593 * /dev/sdc /mnt/test btrfs subvol=/anydir
1594 * /dev/sdc /mnt/test btrfs defaults
1595 * /mnt/test/foo /mnt/test2 auto bind
1597 * in this case, the root for /mnt/test2 will be /anydir/foo on
1598 * /dev/sdc. It means we have to compose the final root from
1599 * root and src_root.
1601 src_root
= mnt_fs_get_root(src_fs
);
1603 DBG(FS
, ul_debugobj(fs
, "source root: %s, source FS root: %s", root
, src_root
));
1605 if (src_root
&& !startswith(root
, src_root
)) {
1606 if (strcmp(root
, "/") == 0) {
1608 root
= strdup(src_root
);
1613 if (asprintf(&tmp
, "%s%s", src_root
, root
) < 0)
1621 #ifdef HAVE_BTRFS_SUPPORT
1623 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
1625 else if (tb
&& fs
->fstype
&&
1626 (!strcmp(fs
->fstype
, "btrfs") || !strcmp(fs
->fstype
, "auto"))) {
1627 if (get_btrfs_fs_root(tb
, fs
, &root
) < 0)
1630 #endif /* HAVE_BTRFS_SUPPORT */
1640 DBG(TAB
, ul_debug("FS root result: %s", root
));
1649 * mnt_table_is_fs_mounted:
1650 * @tb: /proc/self/mountinfo file
1651 * @fstab_fs: /etc/fstab entry
1653 * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
1654 * ignored. This function explicitly compares the source, target and root of the
1657 * Note that source and target are canonicalized only if a cache for @tb is
1658 * defined (see mnt_table_set_cache()). The target canonicalization may
1659 * trigger automount on autofs mountpoints!
1661 * Don't use it if you want to know if a device is mounted, just use
1662 * mnt_table_find_source() on the device.
1664 * This function is designed mostly for "mount -a".
1668 int mnt_table_is_fs_mounted(struct libmnt_table
*tb
, struct libmnt_fs
*fstab_fs
)
1670 struct libmnt_iter itr
;
1671 struct libmnt_fs
*fs
;
1675 const char *src
= NULL
, *tgt
= NULL
;
1680 DBG(FS
, ul_debugobj(fstab_fs
, "mnt_table_is_fs_mounted: target=%s, source=%s",
1681 mnt_fs_get_target(fstab_fs
),
1682 mnt_fs_get_source(fstab_fs
)));
1684 if (mnt_fs_is_swaparea(fstab_fs
) || mnt_table_is_empty(tb
)) {
1685 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (swap or no data)"));
1689 if (is_mountinfo(tb
)) {
1690 /* @tb is mountinfo, so we can try to use fs-roots */
1691 struct libmnt_fs
*rootfs
;
1694 if (mnt_fs_get_option(fstab_fs
, "bind", NULL
, NULL
) == 0 ||
1695 mnt_fs_get_option(fstab_fs
, "rbind", NULL
, NULL
) == 0)
1698 rootfs
= mnt_table_get_fs_root(tb
, fstab_fs
, flags
, &root
);
1700 const char *fstype
= mnt_fs_get_fstype(rootfs
);
1702 src
= mnt_fs_get_srcpath(rootfs
);
1703 if (fstype
&& strncmp(fstype
, "nfs", 3) == 0 && root
) {
1704 /* NFS stores the root at the end of the source */
1705 src
= src2
= strappend(src
, root
);
1713 src
= mnt_fs_get_source(fstab_fs
);
1715 if (src
&& tb
->cache
&& !mnt_fs_is_pseudofs(fstab_fs
))
1716 src
= mnt_resolve_spec(src
, tb
->cache
);
1721 devno
= mnt_fs_get_devno(fstab_fs
);
1722 if (!devno
&& stat(src
, &st
) == 0 && S_ISBLK(st
.st_mode
))
1726 tgt
= mnt_fs_get_target(fstab_fs
);
1729 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (no source/target)"));
1732 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
1734 DBG(FS
, ul_debugobj(fstab_fs
, "mnt_table_is_fs_mounted: src=%s, tgt=%s, root=%s", src
, tgt
, root
));
1736 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1738 int eq
= mnt_fs_streq_srcpath(fs
, src
);
1740 if (!eq
&& devno
&& mnt_fs_get_devno(fs
) == devno
)
1744 /* The source does not match. Maybe the source is a loop
1745 * device backing file.
1747 uint64_t offset
= 0;
1752 if (!mnt_fs_get_srcpath(fs
) ||
1753 !startswith(mnt_fs_get_srcpath(fs
), "/dev/loop"))
1754 continue; /* does not look like loopdev */
1756 if (mnt_fs_get_option(fstab_fs
, "offset", &val
, &len
) == 0) {
1757 if (mnt_parse_offset(val
, len
, &offset
)) {
1758 DBG(FS
, ul_debugobj(fstab_fs
, "failed to parse offset="));
1761 flags
= LOOPDEV_FL_OFFSET
;
1764 DBG(FS
, ul_debugobj(fs
, "checking for loop: src=%s", mnt_fs_get_srcpath(fs
)));
1766 if (!loopdev_is_used(mnt_fs_get_srcpath(fs
), src
, offset
, 0, flags
))
1769 DBG(FS
, ul_debugobj(fs
, "used loop"));
1774 const char *fstype
= mnt_fs_get_fstype(fs
);
1776 if (fstype
&& strcmp(fstype
, "cifs") == 0) {
1777 const char *unc_subdir
= get_cifs_unc_subdir_path(src
);
1778 const char *path_on_fs
= mnt_fs_get_root(fs
);
1779 if (!unc_subdir
|| !path_on_fs
|| !streq_paths(unc_subdir
, path_on_fs
))
1782 const char *r
= mnt_fs_get_root(fs
);
1783 if (!r
|| strcmp(r
, root
) != 0)
1789 * Compare target, try to minimize the number of situations when we
1790 * need to canonicalize the path to avoid readlink() on
1794 if (mnt_fs_streq_target(fs
, tgt
))
1797 xtgt
= mnt_resolve_path(tgt
, tb
->cache
);
1799 if (xtgt
&& mnt_fs_streq_target(fs
, xtgt
))
1804 rc
= 1; /* success */
1808 DBG(TAB
, ul_debugobj(tb
, "mnt_table_is_fs_mounted: %s [rc=%d]", src
, rc
));
1814 #include "pathnames.h"
1816 static int parser_errcb(struct libmnt_table
*tb
, const char *filename
, int line
)
1818 fprintf(stderr
, "%s:%d: parse error\n", filename
, line
);
1820 return 1; /* all errors are recoverable -- this is the default */
1823 static struct libmnt_table
*create_table(const char *file
, int comments
)
1825 struct libmnt_table
*tb
;
1829 tb
= mnt_new_table();
1833 mnt_table_enable_comments(tb
, comments
);
1834 mnt_table_set_parser_errcb(tb
, parser_errcb
);
1836 if (mnt_table_parse_file(tb
, file
) != 0)
1840 fprintf(stderr
, "%s: parsing failed\n", file
);
1841 mnt_unref_table(tb
);
1845 static int test_copy_fs(struct libmnt_test
*ts
, int argc
, char *argv
[])
1847 struct libmnt_table
*tb
;
1848 struct libmnt_fs
*fs
;
1851 tb
= create_table(argv
[1], FALSE
);
1855 fs
= mnt_table_find_target(tb
, "/", MNT_ITER_FORWARD
);
1859 printf("ORIGINAL:\n");
1860 mnt_fs_print_debug(fs
, stdout
);
1862 fs
= mnt_copy_fs(NULL
, fs
);
1867 mnt_fs_print_debug(fs
, stdout
);
1871 mnt_unref_table(tb
);
1875 static int test_parse(struct libmnt_test
*ts
, int argc
, char *argv
[])
1877 struct libmnt_table
*tb
= NULL
;
1878 struct libmnt_iter
*itr
= NULL
;
1879 struct libmnt_fs
*fs
;
1881 int parse_comments
= FALSE
;
1883 if (argc
== 3 && !strcmp(argv
[2], "--comments"))
1884 parse_comments
= TRUE
;
1886 tb
= create_table(argv
[1], parse_comments
);
1890 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
1894 if (mnt_table_get_intro_comment(tb
))
1895 fprintf(stdout
, "Initial comment:\n\"%s\"\n",
1896 mnt_table_get_intro_comment(tb
));
1898 while(mnt_table_next_fs(tb
, itr
, &fs
) == 0)
1899 mnt_fs_print_debug(fs
, stdout
);
1901 if (mnt_table_get_trailing_comment(tb
))
1902 fprintf(stdout
, "Trailing comment:\n\"%s\"\n",
1903 mnt_table_get_trailing_comment(tb
));
1907 mnt_unref_table(tb
);
1911 static int test_find_idx(struct libmnt_test
*ts
, int argc
, char *argv
[])
1913 struct libmnt_table
*tb
;
1914 struct libmnt_fs
*fs
= NULL
;
1915 struct libmnt_cache
*mpc
= NULL
;
1916 const char *file
, *what
;
1920 fprintf(stderr
, "try --help\n");
1924 file
= argv
[1], what
= argv
[2];
1926 tb
= create_table(file
, FALSE
);
1930 /* create a cache for canonicalized paths */
1931 mpc
= mnt_new_cache();
1934 mnt_table_set_cache(tb
, mpc
);
1935 mnt_unref_cache(mpc
);
1937 fs
= mnt_table_find_target(tb
, what
, MNT_ITER_BACKWARD
);
1940 fprintf(stderr
, "%s: not found '%s'\n", file
, what
);
1942 int idx
= mnt_table_find_fs(tb
, fs
);
1945 fprintf(stderr
, "%s: not found '%s' fs pointer", file
, what
);
1947 printf("%s index is %d\n", what
, idx
);
1952 mnt_unref_table(tb
);
1956 static int test_find(struct libmnt_test
*ts
, int argc
, char *argv
[], int dr
)
1958 struct libmnt_table
*tb
;
1959 struct libmnt_fs
*fs
= NULL
;
1960 struct libmnt_cache
*mpc
= NULL
;
1961 const char *file
, *find
, *what
;
1965 fprintf(stderr
, "try --help\n");
1969 file
= argv
[1], find
= argv
[2], what
= argv
[3];
1971 tb
= create_table(file
, FALSE
);
1975 /* create a cache for canonicalized paths */
1976 mpc
= mnt_new_cache();
1979 mnt_table_set_cache(tb
, mpc
);
1980 mnt_unref_cache(mpc
);
1982 if (strcasecmp(find
, "source") == 0)
1983 fs
= mnt_table_find_source(tb
, what
, dr
);
1984 else if (strcasecmp(find
, "target") == 0)
1985 fs
= mnt_table_find_target(tb
, what
, dr
);
1988 fprintf(stderr
, "%s: not found %s '%s'\n", file
, find
, what
);
1990 mnt_fs_print_debug(fs
, stdout
);
1994 mnt_unref_table(tb
);
1998 static int test_find_bw(struct libmnt_test
*ts
, int argc
, char *argv
[])
2000 return test_find(ts
, argc
, argv
, MNT_ITER_BACKWARD
);
2003 static int test_find_fw(struct libmnt_test
*ts
, int argc
, char *argv
[])
2005 return test_find(ts
, argc
, argv
, MNT_ITER_FORWARD
);
2008 static int test_find_pair(struct libmnt_test
*ts
, int argc
, char *argv
[])
2010 struct libmnt_table
*tb
;
2011 struct libmnt_fs
*fs
;
2012 struct libmnt_cache
*mpc
= NULL
;
2015 tb
= create_table(argv
[1], FALSE
);
2018 mpc
= mnt_new_cache();
2021 mnt_table_set_cache(tb
, mpc
);
2022 mnt_unref_cache(mpc
);
2024 fs
= mnt_table_find_pair(tb
, argv
[2], argv
[3], MNT_ITER_FORWARD
);
2028 mnt_fs_print_debug(fs
, stdout
);
2031 mnt_unref_table(tb
);
2035 static int test_find_mountpoint(struct libmnt_test
*ts
, int argc
, char *argv
[])
2037 struct libmnt_table
*tb
;
2038 struct libmnt_fs
*fs
;
2039 struct libmnt_cache
*mpc
= NULL
;
2042 tb
= mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
);
2045 mpc
= mnt_new_cache();
2048 mnt_table_set_cache(tb
, mpc
);
2049 mnt_unref_cache(mpc
);
2051 fs
= mnt_table_find_mountpoint(tb
, argv
[1], MNT_ITER_BACKWARD
);
2055 mnt_fs_print_debug(fs
, stdout
);
2058 mnt_unref_table(tb
);
2062 static int test_is_mounted(struct libmnt_test
*ts
, int argc
, char *argv
[])
2064 struct libmnt_table
*tb
= NULL
, *fstab
= NULL
;
2065 struct libmnt_fs
*fs
;
2066 struct libmnt_iter
*itr
= NULL
;
2067 struct libmnt_cache
*mpc
= NULL
;
2069 const char *path
= NULL
;
2071 if (mnt_has_regular_mtab(&path
, &writable
) == 1 && writable
== 0)
2072 tb
= mnt_new_table_from_file(path
);
2074 tb
= mnt_new_table_from_file("/proc/self/mountinfo");
2077 fprintf(stderr
, "failed to parse mountinfo\n");
2081 fstab
= create_table(argv
[1], FALSE
);
2085 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
2089 mpc
= mnt_new_cache();
2092 mnt_table_set_cache(tb
, mpc
);
2093 mnt_unref_cache(mpc
);
2095 while (mnt_table_next_fs(fstab
, itr
, &fs
) == 0) {
2096 if (mnt_table_is_fs_mounted(tb
, fs
))
2097 printf("%s already mounted on %s\n",
2098 mnt_fs_get_source(fs
),
2099 mnt_fs_get_target(fs
));
2101 printf("%s not mounted on %s\n",
2102 mnt_fs_get_source(fs
),
2103 mnt_fs_get_target(fs
));
2107 mnt_unref_table(tb
);
2108 mnt_unref_table(fstab
);
2113 /* returns 0 if @a and @b targets are the same */
2114 static int test_uniq_cmp(struct libmnt_table
*tb
__attribute__((__unused__
)),
2115 struct libmnt_fs
*a
,
2116 struct libmnt_fs
*b
)
2121 return mnt_fs_streq_target(a
, mnt_fs_get_target(b
)) ? 0 : 1;
2124 static int test_uniq(struct libmnt_test
*ts
, int argc
, char *argv
[])
2126 struct libmnt_table
*tb
;
2130 fprintf(stderr
, "try --help\n");
2134 tb
= create_table(argv
[1], FALSE
);
2138 if (mnt_table_uniq_fs(tb
, 0, test_uniq_cmp
) == 0) {
2139 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_FORWARD
);
2140 struct libmnt_fs
*fs
;
2143 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0)
2144 mnt_fs_print_debug(fs
, stdout
);
2149 mnt_unref_table(tb
);
2154 int main(int argc
, char *argv
[])
2156 struct libmnt_test tss
[] = {
2157 { "--parse", test_parse
, "<file> [--comments] parse and print tab" },
2158 { "--find-forward", test_find_fw
, "<file> <source|target> <string>" },
2159 { "--find-backward", test_find_bw
, "<file> <source|target> <string>" },
2160 { "--uniq-target", test_uniq
, "<file>" },
2161 { "--find-pair", test_find_pair
, "<file> <source> <target>" },
2162 { "--find-fs", test_find_idx
, "<file> <target>" },
2163 { "--find-mountpoint", test_find_mountpoint
, "<path>" },
2164 { "--copy-fs", test_copy_fs
, "<file> copy root FS from the file" },
2165 { "--is-mounted", test_is_mounted
, "<fstab> check what from fstab is already mounted" },
2169 return mnt_run_test(tss
, argc
, argv
);
2172 #endif /* TEST_PROGRAM */