]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/tab.c
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 strappend(&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 strappend(&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.
409 int mnt_table_find_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
417 if (list_empty(&fs
->ents
))
420 /* Let's use directly list rather than mnt_table_next_fs() as we
421 * compare list entry with fs only.
423 list_for_each(p
, &tb
->ents
) {
425 if (list_entry(p
, struct libmnt_fs
, ents
) == fs
)
437 * Adds a new entry to tab and increment @fs reference counter. Don't forget to
438 * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs
439 * referenced by the table only.
441 * Returns: 0 on success or negative number in case of error.
443 int mnt_table_add_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
452 list_add_tail(&fs
->ents
, &tb
->ents
);
456 DBG(TAB
, ul_debugobj(tb
, "add entry: %s %s",
457 mnt_fs_get_source(fs
), mnt_fs_get_target(fs
)));
461 static int __table_insert_fs(
462 struct libmnt_table
*tb
, int before
,
463 struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
465 struct list_head
*head
= pos
? &pos
->ents
: &tb
->ents
;
468 list_add(&fs
->ents
, head
);
470 list_add_tail(&fs
->ents
, head
);
475 DBG(TAB
, ul_debugobj(tb
, "insert entry: %s %s",
476 mnt_fs_get_source(fs
), mnt_fs_get_target(fs
)));
481 * mnt_table_insert_fs:
483 * @before: 1 to insert before pos, 0 to insert after pos
484 * @pos: entry to specify position or NULL
487 * Adds a new entry to @tb before or after a specific table entry @pos. If the
488 * @pos is NULL than add the begin of the @tab if @before is 1; or to the tail
489 * of the @tb if @before is 0.
491 * This function increments reference to @fs. Don't forget to use
492 * mnt_unref_fs() after mnt_table_insert_fs() if you want to keep the @fs
493 * referenced by the table only.
495 * Returns: 0 on success or negative number in case of error.
499 int mnt_table_insert_fs(struct libmnt_table
*tb
, int before
,
500 struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
508 if (pos
&& pos
->tab
!= tb
)
512 return __table_insert_fs(tb
, before
, pos
, fs
);
517 * @src: tab pointer of source table
518 * @dst: tab pointer of destination table
519 * @before: 1 to move before position, 0 to move after position
520 * @pos: entry to specify position or NULL
523 * Removes @fs from @src table and adds it before/after a specific entry @pos
524 * of @dst table. If the @pos is NULL than add the begin of the @dst if @before
525 * is 1; or to the tail of the @dst if @before is 0.
527 * The reference counter of @fs is not modified.
529 * Returns: 0 on success or negative number in case of error.
533 int mnt_table_move_fs(struct libmnt_table
*src
, struct libmnt_table
*dst
,
534 int before
, struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
536 if (!src
|| !dst
|| !fs
)
539 if (fs
->tab
!= src
|| (pos
&& pos
->tab
!= dst
))
542 /* remove from source */
543 list_del_init(&fs
->ents
);
546 /* insert to the destination */
547 return __table_insert_fs(dst
, before
, pos
, fs
);
552 * mnt_table_remove_fs:
556 * Removes the @fs from the table and de-increment reference counter of the @fs. The
557 * filesystem with zero reference counter will be deallocated. Don't forget to use
558 * mnt_ref_fs() before call mnt_table_remove_fs() if you want to use @fs later.
560 * Returns: 0 on success or negative number in case of error.
562 int mnt_table_remove_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
564 if (!tb
|| !fs
|| fs
->tab
!= tb
)
568 list_del_init(&fs
->ents
);
575 static inline struct libmnt_fs
*get_parent_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
577 struct libmnt_iter itr
;
579 int parent_id
= mnt_fs_get_parent_id(fs
);
581 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
582 while (mnt_table_next_fs(tb
, &itr
, &x
) == 0) {
583 if (mnt_fs_get_id(x
) == parent_id
)
591 * mnt_table_get_root_fs:
592 * @tb: mountinfo file (/proc/self/mountinfo)
593 * @root: NULL or returns pointer to the root filesystem (/)
595 * The function uses the parent ID from the mountinfo file to determine the
596 * root filesystem (the filesystem with the smallest ID with parent ID missing
597 * in the table). The function is designed mostly for applications where it is
598 * necessary to sort mountpoints by IDs to get the tree of the mountpoints
599 * (e.g. findmnt default output).
601 * If you're not sure, then use
603 * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD);
605 * this is more robust and usable for arbitrary tab files (including fstab).
607 * Returns: 0 on success or negative number in case of error.
609 int mnt_table_get_root_fs(struct libmnt_table
*tb
, struct libmnt_fs
**root
)
611 struct libmnt_iter itr
;
612 struct libmnt_fs
*fs
, *root_fs
= NULL
;
615 if (!tb
|| !is_mountinfo(tb
))
618 DBG(TAB
, ul_debugobj(tb
, "lookup root fs"));
620 /* get smallest possible ID from the table */
621 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
622 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
623 int id
= mnt_fs_get_parent_id(fs
);
625 if (!root_fs
|| id
< root_id
) {
631 /* go to the root node by "parent_id -> id" relation */
633 struct libmnt_fs
*x
= get_parent_fs(tb
, root_fs
);
634 if (!x
|| x
== root_fs
)
636 DBG(TAB
, ul_debugobj(tb
, " messy mountinfo, walk to %s", mnt_fs_get_target(x
)));
643 return root_fs
? 0 : -EINVAL
;
647 * mnt_table_next_child_fs:
648 * @tb: mountinfo file (/proc/self/mountinfo)
650 * @parent: parental FS
651 * @chld: NULL or returns the next child filesystem
653 * Since version 2.40, the filesystems are returned in the order specified by
654 * @itr. In the old versions the derection is always MNT_ITER_FORWARD.
656 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
658 int mnt_table_next_child_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
659 struct libmnt_fs
*parent
, struct libmnt_fs
**chld
)
661 struct libmnt_fs
*fs
, *chfs
= NULL
;
662 int parent_id
, lastchld_id
= 0, chld_id
= 0;
665 if (!tb
|| !itr
|| !parent
|| !is_mountinfo(tb
))
668 DBG(TAB
, ul_debugobj(tb
, "lookup next child of '%s'",
669 mnt_fs_get_target(parent
)));
670 parent_id
= mnt_fs_get_id(parent
);
671 direction
= mnt_iter_get_direction(itr
);
673 /* get ID of the previously returned child */
674 if (itr
->head
&& itr
->p
!= itr
->head
) {
675 fs
= MNT_ITER_GET_ENTRY(itr
, struct libmnt_fs
, ents
);
676 MNT_ITER_ITERATE(itr
);
677 lastchld_id
= mnt_fs_get_id(fs
);
680 mnt_reset_iter(itr
, direction
);
681 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
684 if (mnt_fs_get_parent_id(fs
) != parent_id
)
687 id
= mnt_fs_get_id(fs
);
689 /* avoid an infinite loop. This only happens in rare cases
690 * such as in early userspace when the rootfs is its own parent */
694 if (direction
== MNT_ITER_FORWARD
) {
695 /* return in the order of mounting */
696 if ((!lastchld_id
|| id
> lastchld_id
) &&
697 (!chfs
|| id
< chld_id
)) {
702 /* return last child first */
703 if ((!lastchld_id
|| id
< lastchld_id
) &&
704 (!chfs
|| id
> chld_id
)) {
714 return 1; /* end of iterator */
716 /* set the iterator to the @chfs for the next call */
717 mnt_table_set_iter(tb
, itr
, chfs
);
725 * @parent: pointer to parental FS
726 * @child: returns pointer to FS which over-mounting parent (optional)
728 * This function returns by @child the first filesystenm which is over-mounted
729 * on @parent. It means the mountpoint of @child is the same as for @parent and
730 * parent->id is the same as child->parent_id.
732 * Note that you need to call this function in loop until it returns 1 to get
733 * the highest filesystem.
738 * while (mnt_table_over_fs(tb, cur, &over) == 0) {
739 * printf("%s overmounted by %d\n", mnt_fs_get_target(cur), mnt_fs_get_id(over));
745 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
747 int mnt_table_over_fs(struct libmnt_table
*tb
, struct libmnt_fs
*parent
,
748 struct libmnt_fs
**child
)
750 struct libmnt_iter itr
;
751 struct libmnt_fs
*fs
= NULL
;
755 if (!tb
|| !parent
|| !is_mountinfo(tb
))
761 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
762 id
= mnt_fs_get_id(parent
);
763 tgt
= mnt_fs_get_target(parent
);
765 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
766 if (mnt_fs_get_parent_id(fs
) == id
&&
767 mnt_fs_streq_target(fs
, tgt
) == 1) {
774 return 1; /* nothing */
781 * @fs: NULL or returns the next tab entry
783 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
788 * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
789 * const char *dir = mnt_fs_get_target(fs);
790 * printf("mount point: %s\n", dir);
795 * lists all mountpoints from fstab in reverse order.
797 int mnt_table_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
**fs
)
807 MNT_ITER_INIT(itr
, &tb
->ents
);
808 if (itr
->p
!= itr
->head
) {
810 *fs
= MNT_ITER_GET_ENTRY(itr
, struct libmnt_fs
, ents
);
811 MNT_ITER_ITERATE(itr
);
819 * mnt_table_first_fs:
821 * @fs: NULL or returns the first tab entry
823 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
825 int mnt_table_first_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
829 if (list_empty(&tb
->ents
))
832 *fs
= list_first_entry(&tb
->ents
, struct libmnt_fs
, ents
);
839 * @fs: NULL or returns the last tab entry
841 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
843 int mnt_table_last_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
847 if (list_empty(&tb
->ents
))
850 *fs
= list_last_entry(&tb
->ents
, struct libmnt_fs
, ents
);
855 * mnt_table_find_next_fs:
858 * @match_func: function returning 1 or 0
859 * @userdata: extra data for match_func
860 * @fs: NULL or returns pointer to the next matching table entry
862 * This function allows searching in @tb.
864 * Returns: negative number in case of error, 1 at end of table or 0 o success.
866 int mnt_table_find_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
867 int (*match_func
)(struct libmnt_fs
*, void *), void *userdata
,
868 struct libmnt_fs
**fs
)
870 struct libmnt_fs
*re
= NULL
;
873 if (!tb
|| !itr
|| !match_func
)
876 DBG(TAB
, ul_debugobj(tb
, "lookup next fs"));
881 MNT_ITER_INIT(itr
, &tb
->ents
);
884 if (itr
->p
!= itr
->head
) {
885 re
= MNT_ITER_GET_ENTRY(itr
, struct libmnt_fs
, ents
);
886 MNT_ITER_ITERATE(itr
);
890 match
= match_func(re
, userdata
);
898 static int mnt_table_move_parent(struct libmnt_table
*tb
, int oldid
, int newid
)
900 struct libmnt_iter itr
;
901 struct libmnt_fs
*fs
;
905 if (list_empty(&tb
->ents
))
908 DBG(TAB
, ul_debugobj(tb
, "moving parent ID from %d -> %d", oldid
, newid
));
909 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
911 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
912 if (fs
->parent
== oldid
)
922 * @cmp: function to compare filesystems
924 * This function de-duplicate the @tb, but does not change order of the
925 * filesystems. The @cmp function has to return 0 if the filesystems are
926 * equal, otherwise non-zero.
928 * The default is to keep in the table later mounted filesystems (function uses
929 * backward mode iterator).
931 * @MNT_UNIQ_FORWARD: remove later mounted filesystems
932 * @MNT_UNIQ_KEEPTREE: keep parent->id relationship still valid
934 * Returns: negative number in case of error, or 0 o success.
936 int mnt_table_uniq_fs(struct libmnt_table
*tb
, int flags
,
937 int (*cmp
)(struct libmnt_table
*,
941 struct libmnt_iter itr
;
942 struct libmnt_fs
*fs
;
943 int direction
= MNT_ITER_BACKWARD
;
947 if (list_empty(&tb
->ents
))
950 if (flags
& MNT_UNIQ_FORWARD
)
951 direction
= MNT_ITER_FORWARD
;
953 DBG(TAB
, ul_debugobj(tb
, "de-duplicate"));
954 mnt_reset_iter(&itr
, direction
);
956 if ((flags
& MNT_UNIQ_KEEPTREE
) && !is_mountinfo(tb
))
957 flags
&= ~MNT_UNIQ_KEEPTREE
;
959 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
961 struct libmnt_iter xtr
;
964 mnt_reset_iter(&xtr
, direction
);
965 while (want
&& mnt_table_next_fs(tb
, &xtr
, &x
) == 0) {
968 want
= cmp(tb
, x
, fs
) != 0;
972 if (flags
& MNT_UNIQ_KEEPTREE
)
973 mnt_table_move_parent(tb
, mnt_fs_get_id(fs
),
974 mnt_fs_get_parent_id(fs
));
976 DBG(TAB
, ul_debugobj(tb
, "remove duplicate %s",
977 mnt_fs_get_target(fs
)));
978 mnt_table_remove_fs(tb
, fs
);
986 * mnt_table_set_iter:
991 * Sets @iter to the position of @fs in the file @tb.
993 * Returns: 0 on success, negative number in case of error.
995 int mnt_table_set_iter(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
*fs
)
997 if (!tb
|| !itr
|| !fs
)
1003 MNT_ITER_INIT(itr
, &tb
->ents
);
1010 * mnt_table_find_mountpoint:
1013 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1015 * Same as mnt_get_mountpoint(), except this function does not rely on
1018 * Returns: a tab entry or NULL.
1020 struct libmnt_fs
*mnt_table_find_mountpoint(struct libmnt_table
*tb
,
1026 if (!tb
|| !path
|| !*path
)
1028 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1031 DBG(TAB
, ul_debugobj(tb
, "lookup MOUNTPOINT: '%s'", path
));
1033 if (!mnt_is_path(path
))
1042 struct libmnt_fs
*fs
;
1044 fs
= mnt_table_find_target(tb
, mnt
, direction
);
1050 p
= stripoff_last_component(mnt
);
1053 } while (mnt
&& *(mnt
+ 1) != '\0');
1056 return mnt_table_find_target(tb
, "/", direction
);
1060 * mnt_table_find_target:
1062 * @path: mountpoint directory
1063 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1065 * Try to lookup an entry in the given tab, three iterations are possible, the first
1066 * with @path, the second with realpath(@path) and the third with realpath(@path)
1067 * against realpath(fs->target). The 2nd and 3rd iterations are not performed when
1068 * the @tb cache is not set (see mnt_table_set_cache()). If
1069 * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any
1070 * @fs->target found in @mtab (see mnt_resolve_target()).
1072 * Returns: a tab entry or NULL.
1074 struct libmnt_fs
*mnt_table_find_target(struct libmnt_table
*tb
, const char *path
, int direction
)
1076 struct libmnt_iter itr
;
1077 struct libmnt_fs
*fs
= NULL
;
1080 if (!tb
|| !path
|| !*path
)
1082 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1085 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s'", path
));
1087 /* native @target */
1088 mnt_reset_iter(&itr
, direction
);
1089 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1090 if (mnt_fs_streq_target(fs
, path
))
1094 /* try absolute path */
1095 if (is_relative_path(path
) && (cn
= absolute_path(path
))) {
1096 DBG(TAB
, ul_debugobj(tb
, "lookup absolute TARGET: '%s'", cn
));
1097 mnt_reset_iter(&itr
, direction
);
1098 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1099 if (mnt_fs_streq_target(fs
, cn
)) {
1107 if (!tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
1110 DBG(TAB
, ul_debugobj(tb
, "lookup canonical TARGET: '%s'", cn
));
1112 /* canonicalized paths in struct libmnt_table */
1113 mnt_reset_iter(&itr
, direction
);
1114 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1115 if (mnt_fs_streq_target(fs
, cn
))
1119 /* non-canonical path in struct libmnt_table
1120 * -- note that mountpoint in /proc/self/mountinfo is already
1121 * canonicalized by the kernel
1123 mnt_reset_iter(&itr
, direction
);
1124 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1128 || mnt_fs_is_swaparea(fs
)
1129 || mnt_fs_is_kernel(fs
)
1130 || (*fs
->target
== '/' && *(fs
->target
+ 1) == '\0'))
1133 p
= mnt_resolve_target(fs
->target
, tb
->cache
);
1134 /* both canonicalized, strcmp() is fine here */
1135 if (p
&& strcmp(cn
, p
) == 0)
1142 * mnt_table_find_srcpath:
1144 * @path: source path (devname or dirname) or NULL
1145 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1147 * Try to lookup an entry in the given tab, four iterations are possible, the first
1148 * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..)
1149 * from @path and the fourth with realpath(@path) against realpath(entry->srcpath).
1151 * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
1152 * set (see mnt_table_set_cache()).
1154 * For btrfs returns tab entry for default id.
1156 * Note that NULL is a valid source path; it will be replaced with "none". The
1157 * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
1159 * Returns: a tab entry or NULL.
1161 struct libmnt_fs
*mnt_table_find_srcpath(struct libmnt_table
*tb
, const char *path
, int direction
)
1163 struct libmnt_iter itr
;
1164 struct libmnt_fs
*fs
= NULL
;
1165 int ntags
= 0, nents
;
1169 if (!tb
|| !path
|| !*path
)
1171 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1174 DBG(TAB
, ul_debugobj(tb
, "lookup SRCPATH: '%s'", path
));
1177 mnt_reset_iter(&itr
, direction
);
1179 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1181 if (mnt_fs_streq_srcpath(fs
, path
)) {
1182 #ifdef HAVE_BTRFS_SUPPORT
1183 if (fs
->fstype
&& !strcmp(fs
->fstype
, "btrfs")) {
1184 uint64_t default_id
= btrfs_get_default_subvol_id(mnt_fs_get_target(fs
));
1188 if (default_id
== UINT64_MAX
)
1189 DBG(TAB
, ul_debug("not found btrfs volume setting"));
1191 else if (mnt_fs_get_option(fs
, "subvolid", &val
, &len
) == 0) {
1194 if (mnt_parse_offset(val
, len
, &subvol_id
)) {
1195 DBG(TAB
, ul_debugobj(tb
, "failed to parse subvolid="));
1198 if (subvol_id
!= default_id
)
1202 #endif /* HAVE_BTRFS_SUPPORT */
1205 if (mnt_fs_get_tag(fs
, NULL
, NULL
) == 0)
1209 if (!path
|| !tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
1212 DBG(TAB
, ul_debugobj(tb
, "lookup canonical SRCPATH: '%s'", cn
));
1214 nents
= mnt_table_get_nents(tb
);
1216 /* canonicalized paths in struct libmnt_table */
1217 if (ntags
< nents
) {
1218 mnt_reset_iter(&itr
, direction
);
1219 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1220 if (mnt_fs_streq_srcpath(fs
, cn
))
1227 int rc
= mnt_cache_read_tags(tb
->cache
, cn
);
1229 mnt_reset_iter(&itr
, direction
);
1232 /* @path's TAGs are in the cache */
1233 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1236 if (mnt_fs_get_tag(fs
, &t
, &v
))
1239 if (mnt_cache_device_has_tag(tb
->cache
, cn
, t
, v
))
1242 } else if (rc
< 0 && errno
== EACCES
) {
1243 /* @path is inaccessible, try evaluating all TAGs in @tb
1244 * by udev symlinks -- this could be expensive on systems
1245 * with a huge fstab/mtab */
1246 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1247 const char *t
, *v
, *x
;
1248 if (mnt_fs_get_tag(fs
, &t
, &v
))
1250 x
= mnt_resolve_tag(t
, v
, tb
->cache
);
1252 /* both canonicalized, strcmp() is fine here */
1253 if (x
&& strcmp(x
, cn
) == 0)
1259 /* non-canonicalized paths in struct libmnt_table */
1260 if (ntags
<= nents
) {
1261 mnt_reset_iter(&itr
, direction
);
1262 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1263 if (mnt_fs_is_netfs(fs
) || mnt_fs_is_pseudofs(fs
))
1265 p
= mnt_fs_get_srcpath(fs
);
1267 p
= mnt_resolve_path(p
, tb
->cache
);
1269 /* both canonicalized, strcmp() is fine here */
1270 if (p
&& strcmp(p
, cn
) == 0)
1280 * mnt_table_find_tag:
1282 * @tag: tag name (e.g "LABEL", "UUID", ...)
1284 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1286 * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and
1287 * @val, for the second attempt the tag is evaluated (converted to the device
1288 * name) and mnt_table_find_srcpath() is performed. The second attempt is not
1289 * performed when @tb cache is not set (see mnt_table_set_cache()).
1291 * Returns: a tab entry or NULL.
1293 struct libmnt_fs
*mnt_table_find_tag(struct libmnt_table
*tb
, const char *tag
,
1294 const char *val
, int direction
)
1296 struct libmnt_iter itr
;
1297 struct libmnt_fs
*fs
= NULL
;
1299 if (!tb
|| !tag
|| !*tag
|| !val
)
1301 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1304 DBG(TAB
, ul_debugobj(tb
, "lookup by TAG: %s %s", tag
, val
));
1306 /* look up by TAG */
1307 mnt_reset_iter(&itr
, direction
);
1308 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1309 if (fs
->tagname
&& fs
->tagval
&&
1310 strcmp(fs
->tagname
, tag
) == 0 &&
1311 strcmp(fs
->tagval
, val
) == 0)
1316 /* look up by device name */
1317 char *cn
= mnt_resolve_tag(tag
, val
, tb
->cache
);
1319 return mnt_table_find_srcpath(tb
, cn
, direction
);
1325 * mnt_table_find_target_with_option:
1327 * @path: mountpoint directory
1328 * @option: option name (e.g "subvol", "subvolid", ...)
1329 * @val: option value or NULL
1330 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1332 * Try to lookup an entry in the given tab that matches combination of @path
1333 * and @option. In difference to mnt_table_find_target(), only @path iteration
1334 * is done. No lookup by device name, no canonicalization.
1336 * Returns: a tab entry or NULL.
1340 struct libmnt_fs
*mnt_table_find_target_with_option(
1341 struct libmnt_table
*tb
, const char *path
,
1342 const char *option
, const char *val
, int direction
)
1344 struct libmnt_iter itr
;
1345 struct libmnt_fs
*fs
= NULL
;
1346 char *optval
= NULL
;
1347 size_t optvalsz
= 0, valsz
= val
? strlen(val
) : 0;
1349 if (!tb
|| !path
|| !*path
|| !option
|| !*option
|| !val
)
1351 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1354 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s' with OPTION %s %s", path
, option
, val
));
1356 /* look up by native @target with OPTION */
1357 mnt_reset_iter(&itr
, direction
);
1358 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1359 if (mnt_fs_streq_target(fs
, path
)
1360 && mnt_fs_get_option(fs
, option
, &optval
, &optvalsz
) == 0
1361 && (!val
|| (optvalsz
== valsz
1362 && strncmp(optval
, val
, optvalsz
) == 0)))
1369 * mnt_table_find_source:
1371 * @source: TAG or path
1372 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1374 * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care
1375 * about the @source format (device, LABEL, UUID, ...). This function parses
1376 * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath().
1378 * Returns: a tab entry or NULL.
1380 struct libmnt_fs
*mnt_table_find_source(struct libmnt_table
*tb
,
1381 const char *source
, int direction
)
1383 struct libmnt_fs
*fs
;
1384 char *t
= NULL
, *v
= NULL
;
1388 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1391 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: '%s'", source
));
1393 if (blkid_parse_tag_string(source
, &t
, &v
) || !mnt_valid_tagname(t
))
1394 fs
= mnt_table_find_srcpath(tb
, source
, direction
);
1396 fs
= mnt_table_find_tag(tb
, t
, v
, direction
);
1405 * mnt_table_find_pair
1407 * @source: TAG or path
1408 * @target: mountpoint
1409 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1411 * This function is implemented by mnt_fs_match_source() and
1412 * mnt_fs_match_target() functions. It means that this is more expensive than
1413 * others mnt_table_find_* function, because every @tab entry is fully evaluated.
1415 * Returns: a tab entry or NULL.
1417 struct libmnt_fs
*mnt_table_find_pair(struct libmnt_table
*tb
, const char *source
,
1418 const char *target
, int direction
)
1420 struct libmnt_fs
*fs
= NULL
;
1421 struct libmnt_iter itr
;
1423 if (!tb
|| !target
|| !*target
|| !source
|| !*source
)
1425 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1428 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: %s TARGET: %s", source
, target
));
1430 mnt_reset_iter(&itr
, direction
);
1431 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1433 if (mnt_fs_match_target(fs
, target
, tb
->cache
) &&
1434 mnt_fs_match_source(fs
, source
, tb
->cache
))
1442 * mnt_table_find_devno
1443 * @tb: /proc/self/mountinfo
1444 * @devno: device number
1445 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1447 * Note that zero could be a valid device number for the root pseudo filesystem (e.g.
1450 * Returns: a tab entry or NULL.
1452 struct libmnt_fs
*mnt_table_find_devno(struct libmnt_table
*tb
,
1453 dev_t devno
, int direction
)
1455 struct libmnt_fs
*fs
= NULL
;
1456 struct libmnt_iter itr
;
1460 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1463 DBG(TAB
, ul_debugobj(tb
, "lookup DEVNO: %d", (int) devno
));
1465 mnt_reset_iter(&itr
, direction
);
1467 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1468 if (mnt_fs_get_devno(fs
) == devno
)
1475 static char *remove_mountpoint_from_path(const char *path
, const char *mnt
)
1482 p
= sz
> 1 ? path
+ sz
: path
;
1484 res
= *p
? strdup(p
) : strdup("/");
1485 DBG(UTILS
, ul_debug("%s fs-root is %s", path
, res
));
1489 #ifdef HAVE_BTRFS_SUPPORT
1490 static int get_btrfs_fs_root(struct libmnt_table
*tb
, struct libmnt_fs
*fs
, char **root
)
1492 char *vol
= NULL
, *p
;
1493 size_t sz
, volsz
= 0;
1495 DBG(BTRFS
, ul_debug("lookup for btrfs FS root"));
1498 if (mnt_fs_get_option(fs
, "subvolid", &vol
, &volsz
) == 0) {
1500 struct libmnt_fs
*f
;
1501 char subvolidstr
[sizeof(stringify_value(UINT64_MAX
))];
1503 DBG(BTRFS
, ul_debug(" found subvolid=%s, checking", vol
));
1505 assert (volsz
+ 1 < sizeof(stringify_value(UINT64_MAX
)));
1506 memcpy(subvolidstr
, vol
, volsz
);
1507 subvolidstr
[volsz
] = '\0';
1509 target
= mnt_resolve_target(mnt_fs_get_target(fs
), tb
->cache
);
1513 DBG(BTRFS
, ul_debug(" trying target=%s subvolid=%s", target
, subvolidstr
));
1514 f
= mnt_table_find_target_with_option(tb
, target
,
1515 "subvolid", subvolidstr
,
1522 /* Instead of set of BACKREF queries constructing subvol path
1523 * corresponding to a particular subvolid, use the one in
1524 * mountinfo. Kernel keeps subvol path up to date.
1526 if (mnt_fs_get_option(f
, "subvol", &vol
, &volsz
) != 0)
1529 } else if (mnt_fs_get_option(fs
, "subvol", &vol
, &volsz
) != 0) {
1530 /* If fstab entry does not contain "subvol", we have to
1531 * check, whether btrfs has default subvolume defined.
1533 uint64_t default_id
;
1535 struct libmnt_fs
*f
;
1536 char default_id_str
[sizeof(stringify_value(UINT64_MAX
))];
1538 DBG(BTRFS
, ul_debug(" subvolid/subvol not found, checking default"));
1540 default_id
= btrfs_get_default_subvol_id(mnt_fs_get_target(fs
));
1541 if (default_id
== UINT64_MAX
)
1544 /* Volume has default subvolume. Check if it matches to
1545 * the one in mountinfo.
1547 * Only kernel >= 4.2 reports subvolid. On older
1548 * kernels, there is no reasonable way to detect which
1549 * subvolume was mounted.
1551 target
= mnt_resolve_target(mnt_fs_get_target(fs
), tb
->cache
);
1555 snprintf(default_id_str
, sizeof(default_id_str
), "%llu",
1556 (unsigned long long int) default_id
);
1558 DBG(BTRFS
, ul_debug(" trying target=%s default subvolid=%s",
1559 target
, default_id_str
));
1561 f
= mnt_table_find_target_with_option(tb
, target
,
1562 "subvolid", default_id_str
,
1569 /* Instead of set of BACKREF queries constructing
1570 * subvol path, use the one in mountinfo. Kernel does
1571 * the evaluation for us.
1573 DBG(BTRFS
, ul_debug("setting FS root: btrfs default subvolid = %s",
1576 if (mnt_fs_get_option(f
, "subvol", &vol
, &volsz
) != 0)
1580 DBG(BTRFS
, ul_debug(" using subvol=%s", vol
));
1584 *root
= malloc(sz
+ 1);
1590 memcpy(p
, vol
, volsz
);
1591 *(*root
+ sz
) = '\0';
1595 DBG(BTRFS
, ul_debug(" not found btrfs volume setting"));
1598 DBG(BTRFS
, ul_debug(" error on btrfs volume setting evaluation"));
1599 return errno
? -errno
: -1;
1601 #endif /* HAVE_BTRFS_SUPPORT */
1603 static const char *get_cifs_unc_subdir_path (const char *unc
)
1606 * 1 or more slash: %*[/]
1607 * 1 or more non-slash: %*[^/]
1608 * number of byte read: %n
1611 int r
= sscanf(unc
, "%*[/]%*[^/]%*[/]%*[^/]%n", &share_end
);
1612 if (r
== EOF
|| share_end
== 0)
1614 return unc
+ share_end
;
1618 * tb: /proc/self/mountinfo
1620 * mountflags: MS_BIND or 0
1621 * fsroot: fs-root that will probably be used in the mountinfo file
1622 * for @fs after mount(2)
1624 * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
1626 * If @tb is NULL then defaults to '/'.
1628 * Returns: entry from @tb that will be used as a source for @fs if the @fs is
1631 * Don't export to library API!
1633 struct libmnt_fs
*mnt_table_get_fs_root(struct libmnt_table
*tb
,
1634 struct libmnt_fs
*fs
,
1635 unsigned long mountflags
,
1639 const char *mnt
= NULL
;
1640 struct libmnt_fs
*src_fs
= NULL
;
1645 DBG(TAB
, ul_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs
)));
1647 if (tb
&& (mountflags
& MS_BIND
)) {
1648 const char *src
, *src_root
;
1651 DBG(TAB
, ul_debug("fs-root for bind"));
1653 src
= xsrc
= mnt_resolve_spec(mnt_fs_get_source(fs
), tb
->cache
);
1655 struct libmnt_fs
*f
= mnt_table_find_mountpoint(tb
,
1656 src
, MNT_ITER_BACKWARD
);
1658 mnt
= mnt_fs_get_target(f
);
1661 root
= remove_mountpoint_from_path(src
, mnt
);
1663 if (xsrc
&& !tb
->cache
) {
1670 src_fs
= mnt_table_find_target(tb
, mnt
, MNT_ITER_BACKWARD
);
1672 DBG(TAB
, ul_debug("not found '%s' in mountinfo -- using default", mnt
));
1676 /* It's possible that fstab_fs source is subdirectory on btrfs
1677 * subvolume or another bind mount. For example:
1679 * /dev/sdc /mnt/test btrfs subvol=/anydir
1680 * /dev/sdc /mnt/test btrfs defaults
1681 * /mnt/test/foo /mnt/test2 auto bind
1683 * in this case, the root for /mnt/test2 will be /anydir/foo on
1684 * /dev/sdc. It means we have to compose the final root from
1685 * root and src_root.
1687 src_root
= mnt_fs_get_root(src_fs
);
1689 DBG(FS
, ul_debugobj(fs
, "source root: %s, source FS root: %s", root
, src_root
));
1691 if (src_root
&& root
&& !startswith(root
, src_root
)) {
1692 if (strcmp(root
, "/") == 0) {
1694 root
= strdup(src_root
);
1699 if (asprintf(&tmp
, "%s%s", src_root
, root
) < 0)
1707 #ifdef HAVE_BTRFS_SUPPORT
1709 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
1711 else if (tb
&& fs
->fstype
&&
1712 (!strcmp(fs
->fstype
, "btrfs") || !strcmp(fs
->fstype
, "auto"))) {
1713 if (get_btrfs_fs_root(tb
, fs
, &root
) < 0)
1716 #endif /* HAVE_BTRFS_SUPPORT */
1726 DBG(TAB
, ul_debug("FS root result: %s", root
));
1735 int __mnt_table_is_fs_mounted(struct libmnt_table
*tb
, struct libmnt_fs
*fstab_fs
,
1736 const char *tgt_prefix
)
1738 struct libmnt_iter itr
;
1739 struct libmnt_fs
*fs
;
1743 const char *src
= NULL
, *tgt
= NULL
;
1744 char *xtgt
= NULL
, *tgt_buf
= NULL
;
1748 DBG(FS
, ul_debugobj(fstab_fs
, "mnt_table_is_fs_mounted: target=%s, source=%s",
1749 mnt_fs_get_target(fstab_fs
),
1750 mnt_fs_get_source(fstab_fs
)));
1752 if (mnt_fs_is_swaparea(fstab_fs
) || mnt_table_is_empty(tb
)) {
1753 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (swap or no data)"));
1757 if (is_mountinfo(tb
)) {
1758 /* @tb is mountinfo, so we can try to use fs-roots */
1759 struct libmnt_fs
*rootfs
;
1762 if (mnt_fs_get_option(fstab_fs
, "bind", NULL
, NULL
) == 0 ||
1763 mnt_fs_get_option(fstab_fs
, "rbind", NULL
, NULL
) == 0)
1766 rootfs
= mnt_table_get_fs_root(tb
, fstab_fs
, flags
, &root
);
1768 const char *fstype
= mnt_fs_get_fstype(rootfs
);
1770 src
= mnt_fs_get_srcpath(rootfs
);
1771 if (fstype
&& strncmp(fstype
, "nfs", 3) == 0 && root
) {
1772 /* NFS stores the root at the end of the source */
1773 src
= src2
= strconcat(src
, root
);
1781 src
= mnt_fs_get_source(fstab_fs
);
1783 if (src
&& tb
->cache
&& !mnt_fs_is_pseudofs(fstab_fs
))
1784 src
= mnt_resolve_spec(src
, tb
->cache
);
1789 devno
= mnt_fs_get_devno(fstab_fs
);
1790 if (!devno
&& mnt_safe_stat(src
, &st
) == 0 && S_ISBLK(st
.st_mode
))
1794 tgt
= mnt_fs_get_target(fstab_fs
);
1797 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (no source/target)"));
1800 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
1802 DBG(FS
, ul_debugobj(fstab_fs
, "mnt_table_is_fs_mounted: src=%s, tgt=%s, root=%s", src
, tgt
, root
));
1804 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1806 int eq
= mnt_fs_streq_srcpath(fs
, src
);
1808 if (!eq
&& devno
&& mnt_fs_get_devno(fs
) == devno
)
1812 /* The source does not match. Maybe the source is a loop
1813 * device backing file.
1815 uint64_t offset
= 0;
1820 if (!mnt_fs_get_srcpath(fs
) ||
1821 !startswith(mnt_fs_get_srcpath(fs
), "/dev/loop"))
1822 continue; /* does not look like loopdev */
1824 if (mnt_fs_get_option(fstab_fs
, "offset", &val
, &len
) == 0) {
1825 if (mnt_parse_offset(val
, len
, &offset
)) {
1826 DBG(FS
, ul_debugobj(fstab_fs
, "failed to parse offset="));
1829 flags
= LOOPDEV_FL_OFFSET
;
1832 DBG(FS
, ul_debugobj(fs
, "checking for loop: src=%s", mnt_fs_get_srcpath(fs
)));
1834 if (!loopdev_is_used(mnt_fs_get_srcpath(fs
), src
, offset
, 0, flags
))
1837 DBG(FS
, ul_debugobj(fs
, "used loop"));
1842 const char *fstype
= mnt_fs_get_fstype(fs
);
1844 if (fstype
&& (strcmp(fstype
, "cifs") == 0 ||
1845 strcmp(fstype
, "smb3") == 0)) {
1847 const char *sub
= get_cifs_unc_subdir_path(src
);
1848 const char *r
= mnt_fs_get_root(fs
);
1850 if (!sub
|| !r
|| (!streq_paths(sub
, r
) &&
1851 !streq_paths("/", r
)))
1854 const char *r
= mnt_fs_get_root(fs
);
1855 if (!r
|| strcmp(r
, root
) != 0)
1861 * Compare target, try to minimize the number of situations when we
1862 * need to canonicalize the path to avoid readlink() on
1867 const char *p
= *tgt
== '/' ? tgt
+ 1 : tgt
;
1869 tgt
= tgt_prefix
; /* target is '/' */
1871 if (asprintf(&tgt_buf
, "%s/%s", tgt_prefix
, p
) <= 0) {
1879 if (mnt_fs_streq_target(fs
, tgt
))
1882 xtgt
= mnt_resolve_path(tgt
, tb
->cache
);
1884 if (xtgt
&& mnt_fs_streq_target(fs
, xtgt
))
1889 rc
= 1; /* success */
1894 DBG(TAB
, ul_debugobj(tb
, "mnt_table_is_fs_mounted: %s [rc=%d]", src
, rc
));
1900 * mnt_table_is_fs_mounted:
1901 * @tb: /proc/self/mountinfo file
1902 * @fstab_fs: /etc/fstab entry
1904 * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
1905 * ignored. This function explicitly compares the source, target and root of the
1908 * Note that source and target are canonicalized only if a cache for @tb is
1909 * defined (see mnt_table_set_cache()). The target canonicalization may
1910 * trigger automount on autofs mountpoints!
1912 * Don't use it if you want to know if a device is mounted, just use
1913 * mnt_table_find_source() on the device.
1915 * This function is designed mostly for "mount -a".
1919 int mnt_table_is_fs_mounted(struct libmnt_table
*tb
, struct libmnt_fs
*fstab_fs
)
1921 return __mnt_table_is_fs_mounted(tb
, fstab_fs
, NULL
);
1926 #include "pathnames.h"
1928 static int parser_errcb(struct libmnt_table
*tb
__attribute__((unused
)),
1929 const char *filename
, int line
)
1931 fprintf(stderr
, "%s:%d: parse error\n", filename
, line
);
1933 return 1; /* all errors are recoverable -- this is the default */
1936 static struct libmnt_table
*create_table(const char *file
, int comments
)
1938 struct libmnt_table
*tb
;
1942 tb
= mnt_new_table();
1946 mnt_table_enable_comments(tb
, comments
);
1947 mnt_table_set_parser_errcb(tb
, parser_errcb
);
1949 if (mnt_table_parse_file(tb
, file
) != 0)
1953 fprintf(stderr
, "%s: parsing failed\n", file
);
1954 mnt_unref_table(tb
);
1958 static int test_copy_fs(struct libmnt_test
*ts
__attribute__((unused
)),
1959 int argc
, char *argv
[])
1961 struct libmnt_table
*tb
;
1962 struct libmnt_fs
*fs
;
1968 tb
= create_table(argv
[1], FALSE
);
1972 fs
= mnt_table_find_target(tb
, "/", MNT_ITER_FORWARD
);
1976 printf("ORIGINAL:\n");
1977 mnt_fs_print_debug(fs
, stdout
);
1979 fs
= mnt_copy_fs(NULL
, fs
);
1984 mnt_fs_print_debug(fs
, stdout
);
1988 mnt_unref_table(tb
);
1992 static int test_parse(struct libmnt_test
*ts
__attribute__((unused
)),
1993 int argc
, char *argv
[])
1995 struct libmnt_table
*tb
= NULL
;
1996 struct libmnt_iter
*itr
= NULL
;
1997 struct libmnt_fs
*fs
;
1999 int parse_comments
= FALSE
;
2001 if (argc
== 3 && !strcmp(argv
[2], "--comments"))
2002 parse_comments
= TRUE
;
2004 tb
= create_table(argv
[1], parse_comments
);
2008 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
2012 if (mnt_table_get_intro_comment(tb
))
2013 fprintf(stdout
, "Initial comment:\n\"%s\"\n",
2014 mnt_table_get_intro_comment(tb
));
2016 while(mnt_table_next_fs(tb
, itr
, &fs
) == 0)
2017 mnt_fs_print_debug(fs
, stdout
);
2019 if (mnt_table_get_trailing_comment(tb
))
2020 fprintf(stdout
, "Trailing comment:\n\"%s\"\n",
2021 mnt_table_get_trailing_comment(tb
));
2025 mnt_unref_table(tb
);
2029 static int test_find_idx(struct libmnt_test
*ts
__attribute__((unused
)),
2030 int argc
, char *argv
[])
2032 struct libmnt_table
*tb
;
2033 struct libmnt_fs
*fs
= NULL
;
2034 struct libmnt_cache
*mpc
= NULL
;
2035 const char *file
, *what
;
2039 fprintf(stderr
, "try --help\n");
2043 file
= argv
[1], what
= argv
[2];
2045 tb
= create_table(file
, FALSE
);
2049 /* create a cache for canonicalized paths */
2050 mpc
= mnt_new_cache();
2053 mnt_table_set_cache(tb
, mpc
);
2054 mnt_unref_cache(mpc
);
2056 fs
= mnt_table_find_target(tb
, what
, MNT_ITER_BACKWARD
);
2059 fprintf(stderr
, "%s: not found '%s'\n", file
, what
);
2061 int idx
= mnt_table_find_fs(tb
, fs
);
2064 fprintf(stderr
, "%s: not found '%s' fs pointer", file
, what
);
2066 printf("%s index is %d\n", what
, idx
);
2071 mnt_unref_table(tb
);
2075 static int test_find(struct libmnt_test
*ts
__attribute__((unused
)),
2076 int argc
, char *argv
[], int dr
)
2078 struct libmnt_table
*tb
;
2079 struct libmnt_fs
*fs
= NULL
;
2080 struct libmnt_cache
*mpc
= NULL
;
2081 const char *file
, *find
, *what
;
2085 fprintf(stderr
, "try --help\n");
2089 file
= argv
[1], find
= argv
[2], what
= argv
[3];
2091 tb
= create_table(file
, FALSE
);
2095 /* create a cache for canonicalized paths */
2096 mpc
= mnt_new_cache();
2099 mnt_table_set_cache(tb
, mpc
);
2100 mnt_unref_cache(mpc
);
2102 if (strcasecmp(find
, "source") == 0)
2103 fs
= mnt_table_find_source(tb
, what
, dr
);
2104 else if (strcasecmp(find
, "target") == 0)
2105 fs
= mnt_table_find_target(tb
, what
, dr
);
2108 fprintf(stderr
, "%s: not found %s '%s'\n", file
, find
, what
);
2110 mnt_fs_print_debug(fs
, stdout
);
2114 mnt_unref_table(tb
);
2118 static int test_find_bw(struct libmnt_test
*ts
__attribute__((unused
)),
2119 int argc
, char *argv
[])
2121 return test_find(ts
, argc
, argv
, MNT_ITER_BACKWARD
);
2124 static int test_find_fw(struct libmnt_test
*ts
__attribute__((unused
)),
2125 int argc
, char *argv
[])
2127 return test_find(ts
, argc
, argv
, MNT_ITER_FORWARD
);
2130 static int test_find_pair(struct libmnt_test
*ts
__attribute__((unused
)),
2131 int argc
, char *argv
[])
2133 struct libmnt_table
*tb
;
2134 struct libmnt_fs
*fs
;
2135 struct libmnt_cache
*mpc
= NULL
;
2141 tb
= create_table(argv
[1], FALSE
);
2144 mpc
= mnt_new_cache();
2147 mnt_table_set_cache(tb
, mpc
);
2148 mnt_unref_cache(mpc
);
2150 fs
= mnt_table_find_pair(tb
, argv
[2], argv
[3], MNT_ITER_FORWARD
);
2154 mnt_fs_print_debug(fs
, stdout
);
2157 mnt_unref_table(tb
);
2161 static int test_find_mountpoint(struct libmnt_test
*ts
__attribute__((unused
)),
2162 int argc
, char *argv
[])
2164 struct libmnt_table
*tb
;
2165 struct libmnt_fs
*fs
;
2166 struct libmnt_cache
*mpc
= NULL
;
2172 tb
= mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
);
2175 mpc
= mnt_new_cache();
2178 mnt_table_set_cache(tb
, mpc
);
2179 mnt_unref_cache(mpc
);
2181 fs
= mnt_table_find_mountpoint(tb
, argv
[1], MNT_ITER_BACKWARD
);
2185 mnt_fs_print_debug(fs
, stdout
);
2188 mnt_unref_table(tb
);
2192 static int test_is_mounted(struct libmnt_test
*ts
__attribute__((unused
)),
2193 int argc
, char *argv
[])
2195 struct libmnt_table
*tb
= NULL
, *fstab
= NULL
;
2196 struct libmnt_fs
*fs
;
2197 struct libmnt_iter
*itr
= NULL
;
2198 struct libmnt_cache
*mpc
= NULL
;
2203 tb
= mnt_new_table_from_file("/proc/self/mountinfo");
2205 fprintf(stderr
, "failed to parse mountinfo\n");
2209 fstab
= create_table(argv
[1], FALSE
);
2213 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
2217 mpc
= mnt_new_cache();
2220 mnt_table_set_cache(tb
, mpc
);
2221 mnt_unref_cache(mpc
);
2223 while (mnt_table_next_fs(fstab
, itr
, &fs
) == 0) {
2224 if (mnt_table_is_fs_mounted(tb
, fs
))
2225 printf("%s already mounted on %s\n",
2226 mnt_fs_get_source(fs
),
2227 mnt_fs_get_target(fs
));
2229 printf("%s not mounted on %s\n",
2230 mnt_fs_get_source(fs
),
2231 mnt_fs_get_target(fs
));
2235 mnt_unref_table(tb
);
2236 mnt_unref_table(fstab
);
2241 /* returns 0 if @a and @b targets are the same */
2242 static int test_uniq_cmp(struct libmnt_table
*tb
__attribute__((__unused__
)),
2243 struct libmnt_fs
*a
,
2244 struct libmnt_fs
*b
)
2249 return mnt_fs_streq_target(a
, mnt_fs_get_target(b
)) ? 0 : 1;
2252 static int test_uniq(struct libmnt_test
*ts
__attribute__((unused
)),
2253 int argc
, char *argv
[])
2255 struct libmnt_table
*tb
;
2259 fprintf(stderr
, "try --help\n");
2263 tb
= create_table(argv
[1], FALSE
);
2267 if (mnt_table_uniq_fs(tb
, 0, test_uniq_cmp
) == 0) {
2268 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_FORWARD
);
2269 struct libmnt_fs
*fs
;
2272 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0)
2273 mnt_fs_print_debug(fs
, stdout
);
2278 mnt_unref_table(tb
);
2283 int main(int argc
, char *argv
[])
2285 struct libmnt_test tss
[] = {
2286 { "--parse", test_parse
, "<file> [--comments] parse and print tab" },
2287 { "--find-forward", test_find_fw
, "<file> <source|target> <string>" },
2288 { "--find-backward", test_find_bw
, "<file> <source|target> <string>" },
2289 { "--uniq-target", test_uniq
, "<file>" },
2290 { "--find-pair", test_find_pair
, "<file> <source> <target>" },
2291 { "--find-fs", test_find_idx
, "<file> <target>" },
2292 { "--find-mountpoint", test_find_mountpoint
, "<path>" },
2293 { "--copy-fs", test_copy_fs
, "<file> copy root FS from the file" },
2294 { "--is-mounted", test_is_mounted
, "<fstab> check what from fstab is already mounted" },
2298 return mnt_run_test(tss
, argc
, argv
);
2301 #endif /* TEST_PROGRAM */