]>
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
);
119 mnt_table_reset_listmount(tb
);
128 * Increments reference counter.
130 void mnt_ref_table(struct libmnt_table
*tb
)
134 /*DBG(FS, ul_debugobj(tb, "ref=%d", tb->refcount));*/
142 * De-increments reference counter, on zero the @tb is automatically
143 * deallocated by mnt_free_table().
145 void mnt_unref_table(struct libmnt_table
*tb
)
149 /*DBG(FS, ul_debugobj(tb, "unref=%d", tb->refcount));*/
150 if (tb
->refcount
<= 0)
160 * Deallocates the table. This function does not care about reference count. Don't
161 * use this function directly -- it's better to use mnt_unref_table().
163 * The table entries (filesystems) are unreferenced by mnt_reset_table() and
164 * cache by mnt_unref_cache().
166 void mnt_free_table(struct libmnt_table
*tb
)
172 DBG(TAB
, ul_debugobj(tb
, "free [refcount=%d]", tb
->refcount
));
174 mnt_unref_cache(tb
->cache
);
175 free(tb
->comm_intro
);
181 mnt_unref_statmnt(tb
->stmnt
);
188 * mnt_table_get_nents:
189 * @tb: pointer to tab
191 * Returns: number of entries in table.
193 int mnt_table_get_nents(struct libmnt_table
*tb
)
195 return tb
? tb
->nents
: 0;
199 * mnt_table_is_empty:
200 * @tb: pointer to tab
202 * Returns: 1 if the table is without filesystems, or 0.
204 int mnt_table_is_empty(struct libmnt_table
*tb
)
206 return tb
== NULL
|| list_empty(&tb
->ents
) ? 1 : 0;
210 * mnt_table_set_userdata:
211 * @tb: pointer to tab
212 * @data: pointer to user data
214 * Sets pointer to the private user data.
216 * Returns: 0 on success or negative number in case of error.
218 int mnt_table_set_userdata(struct libmnt_table
*tb
, void *data
)
228 * mnt_table_get_userdata:
229 * @tb: pointer to tab
231 * Returns: pointer to user's data.
233 void *mnt_table_get_userdata(struct libmnt_table
*tb
)
235 return tb
? tb
->userdata
: NULL
;
239 * mnt_table_enable_comments:
240 * @tb: pointer to tab
241 * @enable: TRUE or FALSE
243 * Enables parsing of comments.
245 * The initial (intro) file comment is accessible by
246 * mnt_table_get_intro_comment(). The intro and the comment of the first fstab
247 * entry has to be separated by blank line. The filesystem comments are
248 * accessible by mnt_fs_get_comment(). The trailing fstab comment is accessible
249 * by mnt_table_get_trailing_comment().
257 * # this comments belongs to the first fs
258 * LABEL=foo /mnt/foo auto defaults 1 2
259 * # this comments belongs to the second fs
260 * LABEL=bar /mnt/bar auto defaults 1 2
265 void mnt_table_enable_comments(struct libmnt_table
*tb
, int enable
)
272 * mnt_table_with_comments:
273 * @tb: pointer to table
275 * Returns: 1 if comments parsing is enabled, or 0.
277 int mnt_table_with_comments(struct libmnt_table
*tb
)
280 return tb
? tb
->comms
: 0;
284 * mnt_table_get_intro_comment:
285 * @tb: pointer to tab
287 * Returns: initial comment in tb
289 const char *mnt_table_get_intro_comment(struct libmnt_table
*tb
)
291 return tb
? tb
->comm_intro
: NULL
;
295 * mnt_table_set_into_comment:
296 * @tb: pointer to tab
297 * @comm: comment or NULL
299 * Sets the initial comment in tb.
301 * Returns: 0 on success or negative number in case of error.
303 int mnt_table_set_intro_comment(struct libmnt_table
*tb
, const char *comm
)
305 return strdup_to_struct_member(tb
, comm_intro
, comm
);
309 * mnt_table_append_into_comment:
310 * @tb: pointer to tab
311 * @comm: comment of NULL
313 * Appends the initial comment in tb.
315 * Returns: 0 on success or negative number in case of error.
317 int mnt_table_append_intro_comment(struct libmnt_table
*tb
, const char *comm
)
321 return ul_strappend(&tb
->comm_intro
, comm
);
325 * mnt_table_get_trailing_comment:
326 * @tb: pointer to tab
328 * Returns: table trailing comment
330 const char *mnt_table_get_trailing_comment(struct libmnt_table
*tb
)
332 return tb
? tb
->comm_tail
: NULL
;
336 * mnt_table_set_trailing_comment
337 * @tb: pointer to tab
338 * @comm: comment string
340 * Sets the trailing comment in table.
342 * Returns: 0 on success or negative number in case of error.
344 int mnt_table_set_trailing_comment(struct libmnt_table
*tb
, const char *comm
)
346 return strdup_to_struct_member(tb
, comm_tail
, comm
);
350 * mnt_table_append_trailing_comment:
351 * @tb: pointer to tab
352 * @comm: comment of NULL
354 * Appends to the trailing table comment.
356 * Returns: 0 on success or negative number in case of error.
358 int mnt_table_append_trailing_comment(struct libmnt_table
*tb
, const char *comm
)
362 return ul_strappend(&tb
->comm_tail
, comm
);
366 * mnt_table_set_cache:
367 * @tb: pointer to tab
368 * @mpc: pointer to struct libmnt_cache instance
370 * Sets up a cache for canonicalized paths and evaluated tags (LABEL/UUID). The
371 * cache is recommended for mnt_table_find_*() functions.
373 * The cache could be shared between more tabs. Be careful when you share the
374 * same cache between more threads -- currently the cache does not provide any
377 * This function increments cache reference counter. It's recommended to use
378 * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache
379 * referenced by @tb only.
381 * See also mnt_new_cache().
383 * Returns: 0 on success or negative number in case of error.
385 int mnt_table_set_cache(struct libmnt_table
*tb
, struct libmnt_cache
*mpc
)
390 mnt_ref_cache(mpc
); /* new */
391 mnt_unref_cache(tb
->cache
); /* old */
397 * mnt_table_get_cache:
398 * @tb: pointer to tab
400 * Returns: pointer to struct libmnt_cache instance or NULL.
402 struct libmnt_cache
*mnt_table_get_cache(struct libmnt_table
*tb
)
404 return tb
? tb
->cache
: NULL
;
408 * mnt_table_refer_statmnt:
409 * @tb: pointer to tab
410 * @sm: statmount setting or NULL
412 * Add a reference to the statmount() setting in the table (see
413 * mnt_new_statmnt() function, etc.). This reference will automatically be
414 * used for any newly added filesystems in the @tb, eliminating the need for
415 * extra mnt_fs_refer_statmnt() calls for each filesystem.
417 * The reference is not removed by mnt_reset_table(), use NULL as @sm to
418 * remove the reference.
420 * Returns: 0 on success or negative number in case of error.
424 int mnt_table_refer_statmnt(struct libmnt_table
*tb
, struct libmnt_statmnt
*sm
)
431 mnt_unref_statmnt(tb
->stmnt
);
434 DBG(TAB
, ul_debugobj(tb
, "refer statmnt"));
443 * @fs: entry to look for
445 * Checks if @fs is part of table @tb.
447 * Returns: index of @fs in table, 0 if not found or negative number in case of error.
451 int mnt_table_find_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
459 if (list_empty(&fs
->ents
))
462 /* Let's use directly list rather than mnt_table_next_fs() as we
463 * compare list entry with fs only.
465 list_for_each(p
, &tb
->ents
) {
467 if (list_entry(p
, struct libmnt_fs
, ents
) == fs
)
479 * Adds a new entry to tab and increment @fs reference counter. Don't forget to
480 * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs
481 * referenced by the table only.
483 * Returns: 0 on success or negative number in case of error.
485 int mnt_table_add_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
494 list_add_tail(&fs
->ents
, &tb
->ents
);
498 DBG(TAB
, ul_debugobj(tb
, "add entry: %s %s",
499 mnt_fs_get_source(fs
), mnt_fs_get_target(fs
)));
501 mnt_fs_refer_statmnt(fs
, tb
->stmnt
);
506 static int __table_insert_fs(
507 struct libmnt_table
*tb
, int before
,
508 struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
511 list_add_tail(&fs
->ents
, &tb
->ents
);
513 list_add_tail(&fs
->ents
, &pos
->ents
);
515 list_add(&fs
->ents
, &pos
->ents
);
520 if (mnt_fs_get_uniq_id(fs
)) {
521 DBG(TAB
, ul_debugobj(tb
, "insert entry: %" PRIu64
, mnt_fs_get_uniq_id(fs
)));
523 DBG(TAB
, ul_debugobj(tb
, "insert entry: %s %s",
524 mnt_fs_get_source(fs
), mnt_fs_get_target(fs
)));
528 mnt_fs_refer_statmnt(fs
, tb
->stmnt
);
534 * mnt_table_insert_fs:
536 * @before: 1 to insert before pos, 0 to insert after pos
537 * @pos: entry to specify position or NULL
540 * Adds a new entry to @tb before or after a specific table entry @pos. If the
541 * @pos is NULL than add the begin of the @tab if @before is 1; or to the tail
542 * of the @tb if @before is 0.
544 * This function increments reference to @fs. Don't forget to use
545 * mnt_unref_fs() after mnt_table_insert_fs() if you want to keep the @fs
546 * referenced by the table only.
548 * Returns: 0 on success or negative number in case of error.
552 int mnt_table_insert_fs(struct libmnt_table
*tb
, int before
,
553 struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
561 if (pos
&& pos
->tab
!= tb
)
565 return __table_insert_fs(tb
, before
, pos
, fs
);
570 * @src: tab pointer of source table
571 * @dst: tab pointer of destination table
572 * @before: 1 to move before position, 0 to move after position
573 * @pos: entry to specify position or NULL
576 * Removes @fs from @src table and adds it before/after a specific entry @pos
577 * of @dst table. If the @pos is NULL than add the begin of the @dst if @before
578 * is 1; or to the tail of the @dst if @before is 0.
580 * The reference counter of @fs is not modified.
582 * Returns: 0 on success or negative number in case of error.
586 int mnt_table_move_fs(struct libmnt_table
*src
, struct libmnt_table
*dst
,
587 int before
, struct libmnt_fs
*pos
, struct libmnt_fs
*fs
)
589 if (!src
|| !dst
|| !fs
)
592 if (fs
->tab
!= src
|| (pos
&& pos
->tab
!= dst
))
595 /* remove from source */
596 list_del_init(&fs
->ents
);
599 /* insert to the destination */
600 return __table_insert_fs(dst
, before
, pos
, fs
);
605 * mnt_table_remove_fs:
609 * Removes the @fs from the table and de-increment reference counter of the @fs. The
610 * filesystem with zero reference counter will be deallocated. Don't forget to use
611 * mnt_ref_fs() before call mnt_table_remove_fs() if you want to use @fs later.
613 * Returns: 0 on success or negative number in case of error.
615 int mnt_table_remove_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
617 if (!tb
|| !fs
|| fs
->tab
!= tb
)
621 list_del_init(&fs
->ents
);
628 static inline struct libmnt_fs
*get_parent_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
630 struct libmnt_iter itr
;
632 int parent_id
= mnt_fs_get_parent_id(fs
);
634 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
635 while (mnt_table_next_fs(tb
, &itr
, &x
) == 0) {
636 if (mnt_fs_get_id(x
) == parent_id
)
644 * mnt_table_get_root_fs:
645 * @tb: mountinfo file (/proc/self/mountinfo)
646 * @root: NULL or returns pointer to the root filesystem (/)
648 * The function uses the parent ID from the mountinfo file to determine the
649 * root filesystem (the filesystem with the smallest ID with parent ID missing
650 * in the table). The function is designed mostly for applications where it is
651 * necessary to sort mountpoints by IDs to get the tree of the mountpoints
652 * (e.g. findmnt default output).
654 * If you're not sure, then use
656 * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD);
658 * this is more robust and usable for arbitrary tab files (including fstab).
660 * Returns: 0 on success or negative number in case of error.
662 int mnt_table_get_root_fs(struct libmnt_table
*tb
, struct libmnt_fs
**root
)
664 struct libmnt_iter itr
;
665 struct libmnt_fs
*fs
, *root_fs
= NULL
;
668 if (!tb
|| !is_mountinfo(tb
))
671 DBG(TAB
, ul_debugobj(tb
, "lookup root fs"));
673 /* get smallest possible ID from the table */
674 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
675 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
676 int id
= mnt_fs_get_parent_id(fs
);
678 if (!root_fs
|| id
< root_id
) {
684 /* go to the root node by "parent_id -> id" relation */
686 struct libmnt_fs
*x
= get_parent_fs(tb
, root_fs
);
687 if (!x
|| x
== root_fs
)
689 DBG(TAB
, ul_debugobj(tb
, " messy mountinfo, walk to %s", mnt_fs_get_target(x
)));
696 return root_fs
? 0 : -EINVAL
;
700 * mnt_table_next_child_fs:
701 * @tb: mountinfo file (/proc/self/mountinfo)
703 * @parent: parental FS
704 * @chld: NULL or returns the next child filesystem
706 * Since version 2.40, the filesystems are returned in the order specified by
707 * @itr. In the old versions the derection is always MNT_ITER_FORWARD.
709 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
711 int mnt_table_next_child_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
712 struct libmnt_fs
*parent
, struct libmnt_fs
**chld
)
714 struct libmnt_fs
*fs
, *chfs
= NULL
;
715 int parent_id
, lastchld_id
= 0, chld_id
= 0;
718 if (!tb
|| !itr
|| !parent
|| !is_mountinfo(tb
))
721 DBG(TAB
, ul_debugobj(tb
, "lookup next child of '%s'",
722 mnt_fs_get_target(parent
)));
723 parent_id
= mnt_fs_get_id(parent
);
724 direction
= mnt_iter_get_direction(itr
);
726 /* get ID of the previously returned child */
727 if (itr
->head
&& itr
->p
!= itr
->head
) {
728 fs
= MNT_ITER_GET_ENTRY(itr
, struct libmnt_fs
, ents
);
729 MNT_ITER_ITERATE(itr
);
730 lastchld_id
= mnt_fs_get_id(fs
);
733 mnt_reset_iter(itr
, direction
);
734 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
737 if (mnt_fs_get_parent_id(fs
) != parent_id
)
740 id
= mnt_fs_get_id(fs
);
742 /* avoid an infinite loop. This only happens in rare cases
743 * such as in early userspace when the rootfs is its own parent */
747 if (direction
== MNT_ITER_FORWARD
) {
748 /* return in the order of mounting */
749 if ((!lastchld_id
|| id
> lastchld_id
) &&
750 (!chfs
|| id
< chld_id
)) {
755 /* return last child first */
756 if ((!lastchld_id
|| id
< lastchld_id
) &&
757 (!chfs
|| id
> chld_id
)) {
767 return 1; /* end of iterator */
769 /* set the iterator to the @chfs for the next call */
770 mnt_table_set_iter(tb
, itr
, chfs
);
778 * @parent: pointer to parental FS
779 * @child: returns pointer to FS which over-mounting parent (optional)
781 * This function returns by @child the first filesystenm which is over-mounted
782 * on @parent. It means the mountpoint of @child is the same as for @parent and
783 * parent->id is the same as child->parent_id.
785 * Note that you need to call this function in loop until it returns 1 to get
786 * the highest filesystem.
791 * while (mnt_table_over_fs(tb, cur, &over) == 0) {
792 * printf("%s overmounted by %d\n", mnt_fs_get_target(cur), mnt_fs_get_id(over));
798 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
800 int mnt_table_over_fs(struct libmnt_table
*tb
, struct libmnt_fs
*parent
,
801 struct libmnt_fs
**child
)
803 struct libmnt_iter itr
;
804 struct libmnt_fs
*fs
= NULL
;
808 if (!tb
|| !parent
|| !is_mountinfo(tb
))
814 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
815 id
= mnt_fs_get_id(parent
);
816 tgt
= mnt_fs_get_target(parent
);
818 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
819 if (mnt_fs_get_parent_id(fs
) == id
&&
820 mnt_fs_streq_target(fs
, tgt
) == 1) {
827 return 1; /* nothing */
834 * @fs: NULL or returns the next tab entry
836 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
841 * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
842 * const char *dir = mnt_fs_get_target(fs);
843 * printf("mount point: %s\n", dir);
848 * lists all mountpoints from fstab in reverse order.
850 int mnt_table_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
**fs
)
858 #ifdef HAVE_STATMOUNT_API
859 if (mnt_table_want_listmount(tb
) &&
860 (list_empty(&tb
->ents
) || itr
->p
== itr
->head
)) {
861 struct list_head
*prev
= NULL
;
864 prev
= IS_ITER_FORWARD(itr
) ? itr
->p
->prev
: itr
->p
->next
;
865 rc
= mnt_table_next_lsmnt(tb
, itr
->direction
);
868 MNT_ITER_INIT(itr
, &tb
->ents
);
871 MNT_ITER_ITERATE(itr
);
876 MNT_ITER_INIT(itr
, &tb
->ents
);
877 if (itr
->p
!= itr
->head
) {
879 *fs
= MNT_ITER_GET_ENTRY(itr
, struct libmnt_fs
, ents
);
880 MNT_ITER_ITERATE(itr
);
888 * mnt_table_first_fs:
890 * @fs: NULL or returns the first tab entry
892 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
894 int mnt_table_first_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
898 if (list_empty(&tb
->ents
))
901 *fs
= list_first_entry(&tb
->ents
, struct libmnt_fs
, ents
);
908 * @fs: NULL or returns the last tab entry
910 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
912 int mnt_table_last_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
916 if (list_empty(&tb
->ents
))
919 *fs
= list_last_entry(&tb
->ents
, struct libmnt_fs
, ents
);
924 * mnt_table_find_next_fs:
927 * @match_func: function returning 1 or 0
928 * @userdata: extra data for match_func
929 * @fs: NULL or returns pointer to the next matching table entry
931 * This function allows searching in @tb.
933 * Returns: negative number in case of error, 1 at end of table or 0 o success.
935 int mnt_table_find_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
936 int (*match_func
)(struct libmnt_fs
*, void *), void *userdata
,
937 struct libmnt_fs
**fs
)
939 struct libmnt_fs
*re
= NULL
;
942 if (!tb
|| !itr
|| !match_func
)
945 DBG(TAB
, ul_debugobj(tb
, "lookup next fs"));
950 MNT_ITER_INIT(itr
, &tb
->ents
);
953 if (itr
->p
!= itr
->head
) {
954 re
= MNT_ITER_GET_ENTRY(itr
, struct libmnt_fs
, ents
);
955 MNT_ITER_ITERATE(itr
);
959 match
= match_func(re
, userdata
);
967 static int mnt_table_move_parent(struct libmnt_table
*tb
, int oldid
, int newid
)
969 struct libmnt_iter itr
;
970 struct libmnt_fs
*fs
;
974 if (list_empty(&tb
->ents
))
977 DBG(TAB
, ul_debugobj(tb
, "moving parent ID from %d -> %d", oldid
, newid
));
978 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
980 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
981 if (fs
->parent
== oldid
)
991 * @cmp: function to compare filesystems
993 * This function de-duplicate the @tb, but does not change order of the
994 * filesystems. The @cmp function has to return 0 if the filesystems are
995 * equal, otherwise non-zero.
997 * The default is to keep in the table later mounted filesystems (function uses
998 * backward mode iterator).
1000 * @MNT_UNIQ_FORWARD: remove later mounted filesystems
1001 * @MNT_UNIQ_KEEPTREE: keep parent->id relationship still valid
1003 * Returns: negative number in case of error, or 0 o success.
1005 int mnt_table_uniq_fs(struct libmnt_table
*tb
, int flags
,
1006 int (*cmp
)(struct libmnt_table
*,
1008 struct libmnt_fs
*))
1010 struct libmnt_iter itr
;
1011 struct libmnt_fs
*fs
;
1012 int direction
= MNT_ITER_BACKWARD
;
1016 if (list_empty(&tb
->ents
))
1019 if (flags
& MNT_UNIQ_FORWARD
)
1020 direction
= MNT_ITER_FORWARD
;
1022 DBG(TAB
, ul_debugobj(tb
, "de-duplicate"));
1023 mnt_reset_iter(&itr
, direction
);
1025 if ((flags
& MNT_UNIQ_KEEPTREE
) && !is_mountinfo(tb
))
1026 flags
&= ~MNT_UNIQ_KEEPTREE
;
1028 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1030 struct libmnt_iter xtr
;
1031 struct libmnt_fs
*x
;
1033 mnt_reset_iter(&xtr
, direction
);
1034 while (want
&& mnt_table_next_fs(tb
, &xtr
, &x
) == 0) {
1037 want
= cmp(tb
, x
, fs
) != 0;
1041 if (flags
& MNT_UNIQ_KEEPTREE
)
1042 mnt_table_move_parent(tb
, mnt_fs_get_id(fs
),
1043 mnt_fs_get_parent_id(fs
));
1045 DBG(TAB
, ul_debugobj(tb
, "remove duplicate %s",
1046 mnt_fs_get_target(fs
)));
1047 mnt_table_remove_fs(tb
, fs
);
1055 * mnt_table_set_iter:
1060 * Sets @iter to the position of @fs in the file @tb.
1062 * Returns: 0 on success, negative number in case of error.
1064 int mnt_table_set_iter(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
*fs
)
1066 if (!tb
|| !itr
|| !fs
)
1072 MNT_ITER_INIT(itr
, &tb
->ents
);
1079 * mnt_table_find_mountpoint:
1082 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1084 * Same as mnt_get_mountpoint(), except this function does not rely on
1087 * Returns: a tab entry or NULL.
1089 struct libmnt_fs
*mnt_table_find_mountpoint(struct libmnt_table
*tb
,
1095 if (!tb
|| !path
|| !*path
)
1097 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1100 DBG(TAB
, ul_debugobj(tb
, "lookup MOUNTPOINT: '%s'", path
));
1102 if (!mnt_is_path(path
))
1111 struct libmnt_fs
*fs
;
1113 fs
= mnt_table_find_target(tb
, mnt
, direction
);
1119 p
= stripoff_last_component(mnt
);
1122 } while (mnt
&& *(mnt
+ 1) != '\0');
1125 return mnt_table_find_target(tb
, "/", direction
);
1129 * mnt_table_find_target:
1131 * @path: mountpoint directory
1132 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1134 * Try to lookup an entry in the given tab, three iterations are possible, the first
1135 * with @path, the second with realpath(@path) and the third with realpath(@path)
1136 * against realpath(fs->target). The 2nd and 3rd iterations are not performed when
1137 * the @tb cache is not set (see mnt_table_set_cache()). If
1138 * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any
1139 * @fs->target found in @mtab (see mnt_resolve_target()).
1141 * Returns: a tab entry or NULL.
1143 struct libmnt_fs
*mnt_table_find_target(struct libmnt_table
*tb
, const char *path
, int direction
)
1145 struct libmnt_iter itr
;
1146 struct libmnt_fs
*fs
= NULL
;
1149 if (!tb
|| !path
|| !*path
)
1151 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1154 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s'", path
));
1156 /* native @target */
1157 mnt_reset_iter(&itr
, direction
);
1158 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1159 if (mnt_fs_streq_target(fs
, path
))
1163 /* try absolute path */
1164 if (is_relative_path(path
) && (cn
= absolute_path(path
))) {
1165 DBG(TAB
, ul_debugobj(tb
, "lookup absolute TARGET: '%s'", cn
));
1166 mnt_reset_iter(&itr
, direction
);
1167 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1168 if (mnt_fs_streq_target(fs
, cn
)) {
1176 if (!tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
1179 DBG(TAB
, ul_debugobj(tb
, "lookup canonical TARGET: '%s'", cn
));
1181 /* canonicalized paths in struct libmnt_table */
1182 mnt_reset_iter(&itr
, direction
);
1183 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1184 if (mnt_fs_streq_target(fs
, cn
))
1188 /* non-canonical path in struct libmnt_table
1189 * -- note that mountpoint in /proc/self/mountinfo is already
1190 * canonicalized by the kernel
1192 mnt_reset_iter(&itr
, direction
);
1193 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1197 || mnt_fs_is_swaparea(fs
)
1198 || mnt_fs_is_kernel(fs
)
1199 || (*fs
->target
== '/' && *(fs
->target
+ 1) == '\0'))
1202 p
= mnt_resolve_target(fs
->target
, tb
->cache
);
1203 /* both canonicalized, strcmp() is fine here */
1204 if (p
&& strcmp(cn
, p
) == 0)
1211 * mnt_table_find_srcpath:
1213 * @path: source path (devname or dirname) or NULL
1214 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1216 * Try to lookup an entry in the given tab, four iterations are possible, the first
1217 * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..)
1218 * from @path and the fourth with realpath(@path) against realpath(entry->srcpath).
1220 * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
1221 * set (see mnt_table_set_cache()).
1223 * For btrfs returns tab entry for default id.
1225 * Note that NULL is a valid source path; it will be replaced with "none". The
1226 * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
1228 * Returns: a tab entry or NULL.
1230 struct libmnt_fs
*mnt_table_find_srcpath(struct libmnt_table
*tb
, const char *path
, int direction
)
1232 struct libmnt_iter itr
;
1233 struct libmnt_fs
*fs
= NULL
;
1234 int ntags
= 0, nents
;
1238 if (!tb
|| !path
|| !*path
)
1240 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1243 DBG(TAB
, ul_debugobj(tb
, "lookup SRCPATH: '%s'", path
));
1246 mnt_reset_iter(&itr
, direction
);
1248 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1250 if (mnt_fs_streq_srcpath(fs
, path
)) {
1251 #ifdef HAVE_BTRFS_SUPPORT
1252 if (fs
->fstype
&& !strcmp(fs
->fstype
, "btrfs")) {
1253 uint64_t default_id
= btrfs_get_default_subvol_id(mnt_fs_get_target(fs
));
1257 if (default_id
== UINT64_MAX
)
1258 DBG(TAB
, ul_debug("not found btrfs volume setting"));
1260 else if (mnt_fs_get_option(fs
, "subvolid", &val
, &len
) == 0) {
1263 if (mnt_parse_offset(val
, len
, &subvol_id
)) {
1264 DBG(TAB
, ul_debugobj(tb
, "failed to parse subvolid="));
1267 if (subvol_id
!= default_id
)
1271 #endif /* HAVE_BTRFS_SUPPORT */
1274 if (mnt_fs_get_tag(fs
, NULL
, NULL
) == 0)
1278 if (!path
|| !tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
1281 DBG(TAB
, ul_debugobj(tb
, "lookup canonical SRCPATH: '%s'", cn
));
1283 nents
= mnt_table_get_nents(tb
);
1285 /* canonicalized paths in struct libmnt_table */
1286 if (ntags
< nents
) {
1287 mnt_reset_iter(&itr
, direction
);
1288 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1289 if (mnt_fs_streq_srcpath(fs
, cn
))
1296 int rc
= mnt_cache_read_tags(tb
->cache
, cn
);
1298 mnt_reset_iter(&itr
, direction
);
1301 /* @path's TAGs are in the cache */
1302 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1305 if (mnt_fs_get_tag(fs
, &t
, &v
))
1308 if (mnt_cache_device_has_tag(tb
->cache
, cn
, t
, v
))
1311 } else if (rc
< 0 && errno
== EACCES
) {
1312 /* @path is inaccessible, try evaluating all TAGs in @tb
1313 * by udev symlinks -- this could be expensive on systems
1314 * with a huge fstab/mtab */
1315 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1316 const char *t
, *v
, *x
;
1317 if (mnt_fs_get_tag(fs
, &t
, &v
))
1319 x
= mnt_resolve_tag(t
, v
, tb
->cache
);
1321 /* both canonicalized, strcmp() is fine here */
1322 if (x
&& strcmp(x
, cn
) == 0)
1328 /* non-canonicalized paths in struct libmnt_table */
1329 if (ntags
<= nents
) {
1330 mnt_reset_iter(&itr
, direction
);
1331 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1332 if (mnt_fs_is_netfs(fs
) || mnt_fs_is_pseudofs(fs
))
1334 p
= mnt_fs_get_srcpath(fs
);
1336 p
= mnt_resolve_path(p
, tb
->cache
);
1338 /* both canonicalized, strcmp() is fine here */
1339 if (p
&& strcmp(p
, cn
) == 0)
1349 * mnt_table_find_tag:
1351 * @tag: tag name (e.g "LABEL", "UUID", ...)
1353 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1355 * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and
1356 * @val, for the second attempt the tag is evaluated (converted to the device
1357 * name) and mnt_table_find_srcpath() is performed. The second attempt is not
1358 * performed when @tb cache is not set (see mnt_table_set_cache()).
1360 * Returns: a tab entry or NULL.
1362 struct libmnt_fs
*mnt_table_find_tag(struct libmnt_table
*tb
, const char *tag
,
1363 const char *val
, int direction
)
1365 struct libmnt_iter itr
;
1366 struct libmnt_fs
*fs
= NULL
;
1368 if (!tb
|| !tag
|| !*tag
|| !val
)
1370 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1373 DBG(TAB
, ul_debugobj(tb
, "lookup by TAG: %s %s", tag
, val
));
1375 /* look up by TAG */
1376 mnt_reset_iter(&itr
, direction
);
1377 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1378 if (fs
->tagname
&& fs
->tagval
&&
1379 strcmp(fs
->tagname
, tag
) == 0 &&
1380 strcmp(fs
->tagval
, val
) == 0)
1385 /* look up by device name */
1386 char *cn
= mnt_resolve_tag(tag
, val
, tb
->cache
);
1388 return mnt_table_find_srcpath(tb
, cn
, direction
);
1394 * mnt_table_find_target_with_option:
1396 * @path: mountpoint directory
1397 * @option: option name (e.g "subvol", "subvolid", ...)
1398 * @val: option value or NULL
1399 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1401 * Try to lookup an entry in the given tab that matches combination of @path
1402 * and @option. In difference to mnt_table_find_target(), only @path iteration
1403 * is done. No lookup by device name, no canonicalization.
1405 * Returns: a tab entry or NULL.
1409 struct libmnt_fs
*mnt_table_find_target_with_option(
1410 struct libmnt_table
*tb
, const char *path
,
1411 const char *option
, const char *val
, int direction
)
1413 struct libmnt_iter itr
;
1414 struct libmnt_fs
*fs
= NULL
;
1415 char *optval
= NULL
;
1416 size_t optvalsz
= 0, valsz
= val
? strlen(val
) : 0;
1418 if (!tb
|| !path
|| !*path
|| !option
|| !*option
|| !val
)
1420 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1423 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s' with OPTION %s %s", path
, option
, val
));
1425 /* look up by native @target with OPTION */
1426 mnt_reset_iter(&itr
, direction
);
1427 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1428 if (mnt_fs_streq_target(fs
, path
)
1429 && mnt_fs_get_option(fs
, option
, &optval
, &optvalsz
) == 0
1430 && (!val
|| (optvalsz
== valsz
1431 && strncmp(optval
, val
, optvalsz
) == 0)))
1438 * mnt_table_find_source:
1440 * @source: TAG or path
1441 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1443 * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care
1444 * about the @source format (device, LABEL, UUID, ...). This function parses
1445 * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath().
1447 * Returns: a tab entry or NULL.
1449 struct libmnt_fs
*mnt_table_find_source(struct libmnt_table
*tb
,
1450 const char *source
, int direction
)
1452 struct libmnt_fs
*fs
;
1453 char *t
= NULL
, *v
= NULL
;
1457 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1460 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: '%s'", source
));
1462 if (blkid_parse_tag_string(source
, &t
, &v
) || !mnt_valid_tagname(t
))
1463 fs
= mnt_table_find_srcpath(tb
, source
, direction
);
1465 fs
= mnt_table_find_tag(tb
, t
, v
, direction
);
1474 * mnt_table_find_pair
1476 * @source: TAG or path
1477 * @target: mountpoint
1478 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1480 * This function is implemented by mnt_fs_match_source() and
1481 * mnt_fs_match_target() functions. It means that this is more expensive than
1482 * others mnt_table_find_* function, because every @tab entry is fully evaluated.
1484 * Returns: a tab entry or NULL.
1486 struct libmnt_fs
*mnt_table_find_pair(struct libmnt_table
*tb
, const char *source
,
1487 const char *target
, int direction
)
1489 struct libmnt_fs
*fs
= NULL
;
1490 struct libmnt_iter itr
;
1492 if (!tb
|| !target
|| !*target
|| !source
|| !*source
)
1494 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1497 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: %s TARGET: %s", source
, target
));
1499 mnt_reset_iter(&itr
, direction
);
1500 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1502 if (mnt_fs_match_target(fs
, target
, tb
->cache
) &&
1503 mnt_fs_match_source(fs
, source
, tb
->cache
))
1511 * mnt_table_find_devno
1512 * @tb: /proc/self/mountinfo
1513 * @devno: device number
1514 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1516 * Note that zero could be a valid device number for the root pseudo filesystem (e.g.
1519 * Returns: a tab entry or NULL.
1521 struct libmnt_fs
*mnt_table_find_devno(struct libmnt_table
*tb
,
1522 dev_t devno
, int direction
)
1524 struct libmnt_fs
*fs
= NULL
;
1525 struct libmnt_iter itr
;
1529 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1532 DBG(TAB
, ul_debugobj(tb
, "lookup DEVNO: %d", (int) devno
));
1534 mnt_reset_iter(&itr
, direction
);
1536 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1537 if (mnt_fs_get_devno(fs
) == devno
)
1545 * mnt_table_find_id:
1547 * @id: classic mount ID
1549 * See also mnt_id_from_path().
1551 * Returns: a tab entry or NULL.
1555 struct libmnt_fs
*mnt_table_find_id(struct libmnt_table
*tb
, int id
)
1557 struct libmnt_fs
*fs
= NULL
;
1558 struct libmnt_iter itr
;
1563 DBG(TAB
, ul_debugobj(tb
, "lookup ID: %d", id
));
1564 mnt_reset_iter(&itr
, MNT_ITER_BACKWARD
);
1566 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1567 if (mnt_fs_get_id(fs
) == id
)
1575 * mnt_table_find_uniq_id:
1577 * @id: uniqie 64-bit mount ID
1579 * See also mnt_id_from_path().
1581 * Returns: a tab entry or NULL.
1585 struct libmnt_fs
*mnt_table_find_uniq_id(struct libmnt_table
*tb
, uint64_t id
)
1587 struct libmnt_fs
*fs
= NULL
;
1588 struct libmnt_iter itr
;
1593 DBG(TAB
, ul_debugobj(tb
, "lookup uniq-ID: %" PRIu64
, id
));
1594 mnt_reset_iter(&itr
, MNT_ITER_BACKWARD
);
1596 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1597 if (mnt_fs_get_uniq_id(fs
) == id
)
1605 static char *remove_mountpoint_from_path(const char *path
, const char *mnt
)
1612 p
= sz
> 1 ? path
+ sz
: path
;
1614 res
= *p
? strdup(p
) : strdup("/");
1615 DBG(UTILS
, ul_debug("%s fs-root is %s", path
, res
));
1619 #ifdef HAVE_BTRFS_SUPPORT
1620 static int get_btrfs_fs_root(struct libmnt_table
*tb
, struct libmnt_fs
*fs
, char **root
)
1622 char *vol
= NULL
, *p
;
1623 size_t sz
, volsz
= 0;
1625 DBG(BTRFS
, ul_debug("lookup for btrfs FS root"));
1628 if (mnt_fs_get_option(fs
, "subvolid", &vol
, &volsz
) == 0) {
1630 struct libmnt_fs
*f
;
1631 char subvolidstr
[sizeof(stringify_value(UINT64_MAX
))];
1633 DBG(BTRFS
, ul_debug(" found subvolid=%s, checking", vol
));
1635 assert (volsz
+ 1 < sizeof(stringify_value(UINT64_MAX
)));
1636 memcpy(subvolidstr
, vol
, volsz
);
1637 subvolidstr
[volsz
] = '\0';
1639 target
= mnt_resolve_target(mnt_fs_get_target(fs
), tb
->cache
);
1643 DBG(BTRFS
, ul_debug(" trying target=%s subvolid=%s", target
, subvolidstr
));
1644 f
= mnt_table_find_target_with_option(tb
, target
,
1645 "subvolid", subvolidstr
,
1652 /* Instead of set of BACKREF queries constructing subvol path
1653 * corresponding to a particular subvolid, use the one in
1654 * mountinfo. Kernel keeps subvol path up to date.
1656 if (mnt_fs_get_option(f
, "subvol", &vol
, &volsz
) != 0)
1659 } else if (mnt_fs_get_option(fs
, "subvol", &vol
, &volsz
) != 0) {
1660 /* If fstab entry does not contain "subvol", we have to
1661 * check, whether btrfs has default subvolume defined.
1663 uint64_t default_id
;
1665 struct libmnt_fs
*f
;
1666 char default_id_str
[sizeof(stringify_value(UINT64_MAX
))];
1668 DBG(BTRFS
, ul_debug(" subvolid/subvol not found, checking default"));
1670 default_id
= btrfs_get_default_subvol_id(mnt_fs_get_target(fs
));
1671 if (default_id
== UINT64_MAX
)
1674 /* Volume has default subvolume. Check if it matches to
1675 * the one in mountinfo.
1677 * Only kernel >= 4.2 reports subvolid. On older
1678 * kernels, there is no reasonable way to detect which
1679 * subvolume was mounted.
1681 target
= mnt_resolve_target(mnt_fs_get_target(fs
), tb
->cache
);
1685 snprintf(default_id_str
, sizeof(default_id_str
), "%llu",
1686 (unsigned long long int) default_id
);
1688 DBG(BTRFS
, ul_debug(" trying target=%s default subvolid=%s",
1689 target
, default_id_str
));
1691 f
= mnt_table_find_target_with_option(tb
, target
,
1692 "subvolid", default_id_str
,
1699 /* Instead of set of BACKREF queries constructing
1700 * subvol path, use the one in mountinfo. Kernel does
1701 * the evaluation for us.
1703 DBG(BTRFS
, ul_debug("setting FS root: btrfs default subvolid = %s",
1706 if (mnt_fs_get_option(f
, "subvol", &vol
, &volsz
) != 0)
1710 DBG(BTRFS
, ul_debug(" using subvol=%s", vol
));
1714 *root
= malloc(sz
+ 1);
1720 memcpy(p
, vol
, volsz
);
1721 *(*root
+ sz
) = '\0';
1725 DBG(BTRFS
, ul_debug(" not found btrfs volume setting"));
1728 DBG(BTRFS
, ul_debug(" error on btrfs volume setting evaluation"));
1729 return errno
? -errno
: -1;
1731 #endif /* HAVE_BTRFS_SUPPORT */
1733 static const char *get_cifs_unc_subdir_path (const char *unc
)
1736 * 1 or more slash: %*[/]
1737 * 1 or more non-slash: %*[^/]
1738 * number of byte read: %n
1741 int r
= sscanf(unc
, "%*[/]%*[^/]%*[/]%*[^/]%n", &share_end
);
1742 if (r
== EOF
|| share_end
== 0)
1744 return unc
+ share_end
;
1748 * tb: /proc/self/mountinfo
1750 * mountflags: MS_BIND or 0
1751 * fsroot: fs-root that will probably be used in the mountinfo file
1752 * for @fs after mount(2)
1754 * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
1756 * If @tb is NULL then defaults to '/'.
1758 * Returns: entry from @tb that will be used as a source for @fs if the @fs is
1761 * Don't export to library API!
1763 struct libmnt_fs
*mnt_table_get_fs_root(struct libmnt_table
*tb
,
1764 struct libmnt_fs
*fs
,
1765 unsigned long mountflags
,
1769 const char *mnt
= NULL
;
1770 struct libmnt_fs
*src_fs
= NULL
;
1775 DBG(TAB
, ul_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs
)));
1777 if (tb
&& (mountflags
& MS_BIND
)) {
1778 const char *src
, *src_root
;
1781 DBG(TAB
, ul_debug("fs-root for bind"));
1783 src
= xsrc
= mnt_resolve_spec(mnt_fs_get_source(fs
), tb
->cache
);
1785 struct libmnt_fs
*f
= mnt_table_find_mountpoint(tb
,
1786 src
, MNT_ITER_BACKWARD
);
1788 mnt
= mnt_fs_get_target(f
);
1791 root
= remove_mountpoint_from_path(src
, mnt
);
1793 if (xsrc
&& !tb
->cache
) {
1800 src_fs
= mnt_table_find_target(tb
, mnt
, MNT_ITER_BACKWARD
);
1802 DBG(TAB
, ul_debug("not found '%s' in mountinfo -- using default", mnt
));
1806 /* It's possible that fstab_fs source is subdirectory on btrfs
1807 * subvolume or another bind mount. For example:
1809 * /dev/sdc /mnt/test btrfs subvol=/anydir
1810 * /dev/sdc /mnt/test btrfs defaults
1811 * /mnt/test/foo /mnt/test2 auto bind
1813 * in this case, the root for /mnt/test2 will be /anydir/foo on
1814 * /dev/sdc. It means we have to compose the final root from
1815 * root and src_root.
1817 src_root
= mnt_fs_get_root(src_fs
);
1819 DBG(FS
, ul_debugobj(fs
, "source root: %s, source FS root: %s", root
, src_root
));
1821 if (src_root
&& root
&& !ul_startswith(root
, src_root
)) {
1822 if (strcmp(root
, "/") == 0) {
1824 root
= strdup(src_root
);
1829 if (asprintf(&tmp
, "%s%s", src_root
, root
) < 0)
1837 #ifdef HAVE_BTRFS_SUPPORT
1839 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
1841 else if (tb
&& fs
->fstype
&&
1842 (!strcmp(fs
->fstype
, "btrfs") || !strcmp(fs
->fstype
, "auto"))) {
1843 if (get_btrfs_fs_root(tb
, fs
, &root
) < 0)
1846 #endif /* HAVE_BTRFS_SUPPORT */
1856 DBG(TAB
, ul_debug("FS root result: %s", root
));
1865 int __mnt_table_is_fs_mounted(struct libmnt_table
*tb
, struct libmnt_fs
*fstab_fs
,
1866 const char *tgt_prefix
)
1868 struct libmnt_iter itr
;
1869 struct libmnt_fs
*fs
;
1873 const char *src
= NULL
, *tgt
= NULL
;
1874 char *xtgt
= NULL
, *tgt_buf
= NULL
;
1878 DBG(FS
, ul_debugobj(fstab_fs
, "mnt_table_is_fs_mounted: target=%s, source=%s",
1879 mnt_fs_get_target(fstab_fs
),
1880 mnt_fs_get_source(fstab_fs
)));
1882 if (mnt_fs_is_swaparea(fstab_fs
) || mnt_table_is_empty(tb
)) {
1883 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (swap or no data)"));
1887 if (is_mountinfo(tb
)) {
1888 /* @tb is mountinfo, so we can try to use fs-roots */
1889 struct libmnt_fs
*rootfs
;
1892 if (mnt_fs_get_option(fstab_fs
, "bind", NULL
, NULL
) == 0 ||
1893 mnt_fs_get_option(fstab_fs
, "rbind", NULL
, NULL
) == 0)
1896 rootfs
= mnt_table_get_fs_root(tb
, fstab_fs
, flags
, &root
);
1898 const char *fstype
= mnt_fs_get_fstype(rootfs
);
1900 src
= mnt_fs_get_srcpath(rootfs
);
1901 if (fstype
&& strncmp(fstype
, "nfs", 3) == 0 && root
) {
1902 /* NFS stores the root at the end of the source */
1903 src
= src2
= ul_strconcat(src
, root
);
1911 src
= mnt_fs_get_source(fstab_fs
);
1913 if (src
&& tb
->cache
&& !mnt_fs_is_pseudofs(fstab_fs
))
1914 src
= mnt_resolve_spec(src
, tb
->cache
);
1919 devno
= mnt_fs_get_devno(fstab_fs
);
1920 if (!devno
&& mnt_safe_stat(src
, &st
) == 0 && S_ISBLK(st
.st_mode
))
1924 tgt
= mnt_fs_get_target(fstab_fs
);
1927 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (no source/target)"));
1930 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
1932 DBG(FS
, ul_debugobj(fstab_fs
, "mnt_table_is_fs_mounted: src=%s, tgt=%s, root=%s", src
, tgt
, root
));
1934 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1936 int eq
= mnt_fs_streq_srcpath(fs
, src
);
1938 if (!eq
&& devno
&& mnt_fs_get_devno(fs
) == devno
)
1942 /* The source does not match. Maybe the source is a loop
1943 * device backing file.
1945 uint64_t offset
= 0;
1950 if (!mnt_fs_get_srcpath(fs
) ||
1951 !ul_startswith(mnt_fs_get_srcpath(fs
), "/dev/loop"))
1952 continue; /* does not look like loopdev */
1954 if (mnt_fs_get_option(fstab_fs
, "offset", &val
, &len
) == 0) {
1955 if (mnt_parse_offset(val
, len
, &offset
)) {
1956 DBG(FS
, ul_debugobj(fstab_fs
, "failed to parse offset="));
1959 flags
= LOOPDEV_FL_OFFSET
;
1962 DBG(FS
, ul_debugobj(fs
, "checking for loop: src=%s", mnt_fs_get_srcpath(fs
)));
1964 if (!loopdev_is_used(mnt_fs_get_srcpath(fs
), src
, offset
, 0, flags
))
1967 DBG(FS
, ul_debugobj(fs
, "used loop"));
1972 const char *fstype
= mnt_fs_get_fstype(fs
);
1974 if (fstype
&& (strcmp(fstype
, "cifs") == 0 ||
1975 strcmp(fstype
, "smb3") == 0)) {
1977 const char *sub
= get_cifs_unc_subdir_path(src
);
1978 const char *r
= mnt_fs_get_root(fs
);
1980 if (!sub
|| !r
|| (!streq_paths(sub
, r
) &&
1981 !streq_paths("/", r
)))
1984 const char *r
= mnt_fs_get_root(fs
);
1985 if (!r
|| strcmp(r
, root
) != 0)
1991 * Compare target, try to minimize the number of situations when we
1992 * need to canonicalize the path to avoid readlink() on
1997 const char *p
= *tgt
== '/' ? tgt
+ 1 : tgt
;
1999 tgt
= tgt_prefix
; /* target is '/' */
2001 if (asprintf(&tgt_buf
, "%s/%s", tgt_prefix
, p
) <= 0) {
2009 if (mnt_fs_streq_target(fs
, tgt
))
2012 xtgt
= mnt_resolve_path(tgt
, tb
->cache
);
2014 if (xtgt
&& mnt_fs_streq_target(fs
, xtgt
))
2019 rc
= 1; /* success */
2024 DBG(TAB
, ul_debugobj(tb
, "mnt_table_is_fs_mounted: %s [rc=%d]", src
, rc
));
2030 * mnt_table_is_fs_mounted:
2031 * @tb: /proc/self/mountinfo file
2032 * @fstab_fs: /etc/fstab entry
2034 * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
2035 * ignored. This function explicitly compares the source, target and root of the
2038 * Note that source and target are canonicalized only if a cache for @tb is
2039 * defined (see mnt_table_set_cache()). The target canonicalization may
2040 * trigger automount on autofs mountpoints!
2042 * Don't use it if you want to know if a device is mounted, just use
2043 * mnt_table_find_source() on the device.
2045 * This function is designed mostly for "mount -a".
2049 int mnt_table_is_fs_mounted(struct libmnt_table
*tb
, struct libmnt_fs
*fstab_fs
)
2051 return __mnt_table_is_fs_mounted(tb
, fstab_fs
, NULL
);
2056 #include "pathnames.h"
2059 static int parser_errcb(struct libmnt_table
*tb
__attribute__((unused
)),
2060 const char *filename
, int line
)
2062 fprintf(stderr
, "%s:%d: parse error\n", filename
, line
);
2064 return 1; /* all errors are recoverable -- this is the default */
2067 static struct libmnt_table
*create_table(const char *file
, int comments
)
2069 struct libmnt_table
*tb
;
2073 tb
= mnt_new_table();
2077 mnt_table_enable_comments(tb
, comments
);
2078 mnt_table_set_parser_errcb(tb
, parser_errcb
);
2080 if (mnt_table_parse_file(tb
, file
) != 0)
2084 fprintf(stderr
, "%s: parsing failed\n", file
);
2085 mnt_unref_table(tb
);
2089 static int test_copy_fs(struct libmnt_test
*ts
__attribute__((unused
)),
2090 int argc
, char *argv
[])
2092 struct libmnt_table
*tb
;
2093 struct libmnt_fs
*fs
;
2099 tb
= create_table(argv
[1], FALSE
);
2103 fs
= mnt_table_find_target(tb
, "/", MNT_ITER_FORWARD
);
2107 printf("ORIGINAL:\n");
2108 mnt_fs_print_debug(fs
, stdout
);
2110 fs
= mnt_copy_fs(NULL
, fs
);
2115 mnt_fs_print_debug(fs
, stdout
);
2119 mnt_unref_table(tb
);
2123 static int test_parse(struct libmnt_test
*ts
__attribute__((unused
)),
2124 int argc
, char *argv
[])
2126 struct libmnt_table
*tb
= NULL
;
2127 struct libmnt_iter
*itr
= NULL
;
2128 struct libmnt_fs
*fs
;
2130 int parse_comments
= FALSE
;
2132 if (argc
== 3 && !strcmp(argv
[2], "--comments"))
2133 parse_comments
= TRUE
;
2135 tb
= create_table(argv
[1], parse_comments
);
2139 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
2143 if (mnt_table_get_intro_comment(tb
))
2144 fprintf(stdout
, "Initial comment:\n\"%s\"\n",
2145 mnt_table_get_intro_comment(tb
));
2147 while(mnt_table_next_fs(tb
, itr
, &fs
) == 0)
2148 mnt_fs_print_debug(fs
, stdout
);
2150 if (mnt_table_get_trailing_comment(tb
))
2151 fprintf(stdout
, "Trailing comment:\n\"%s\"\n",
2152 mnt_table_get_trailing_comment(tb
));
2156 mnt_unref_table(tb
);
2160 static int test_find_idx(struct libmnt_test
*ts
__attribute__((unused
)),
2161 int argc
, char *argv
[])
2163 struct libmnt_table
*tb
;
2164 struct libmnt_fs
*fs
= NULL
;
2165 struct libmnt_cache
*mpc
= NULL
;
2166 const char *file
, *what
;
2170 fprintf(stderr
, "try --help\n");
2174 file
= argv
[1], what
= argv
[2];
2176 tb
= create_table(file
, FALSE
);
2180 /* create a cache for canonicalized paths */
2181 mpc
= mnt_new_cache();
2184 mnt_table_set_cache(tb
, mpc
);
2185 mnt_unref_cache(mpc
);
2187 fs
= mnt_table_find_target(tb
, what
, MNT_ITER_BACKWARD
);
2190 fprintf(stderr
, "%s: not found '%s'\n", file
, what
);
2192 int idx
= mnt_table_find_fs(tb
, fs
);
2195 fprintf(stderr
, "%s: not found '%s' fs pointer", file
, what
);
2197 printf("%s index is %d\n", what
, idx
);
2202 mnt_unref_table(tb
);
2206 static int test_find(struct libmnt_test
*ts
__attribute__((unused
)),
2207 int argc
, char *argv
[], int dr
)
2209 struct libmnt_table
*tb
;
2210 struct libmnt_fs
*fs
= NULL
;
2211 struct libmnt_cache
*mpc
= NULL
;
2212 const char *file
, *find
, *what
;
2216 fprintf(stderr
, "try --help\n");
2220 file
= argv
[1], find
= argv
[2], what
= argv
[3];
2222 tb
= create_table(file
, FALSE
);
2226 /* create a cache for canonicalized paths */
2227 mpc
= mnt_new_cache();
2230 mnt_table_set_cache(tb
, mpc
);
2231 mnt_unref_cache(mpc
);
2233 if (c_strcasecmp(find
, "source") == 0)
2234 fs
= mnt_table_find_source(tb
, what
, dr
);
2235 else if (c_strcasecmp(find
, "target") == 0)
2236 fs
= mnt_table_find_target(tb
, what
, dr
);
2239 fprintf(stderr
, "%s: not found %s '%s'\n", file
, find
, what
);
2241 mnt_fs_print_debug(fs
, stdout
);
2245 mnt_unref_table(tb
);
2249 static int test_find_bw(struct libmnt_test
*ts
__attribute__((unused
)),
2250 int argc
, char *argv
[])
2252 return test_find(ts
, argc
, argv
, MNT_ITER_BACKWARD
);
2255 static int test_find_fw(struct libmnt_test
*ts
__attribute__((unused
)),
2256 int argc
, char *argv
[])
2258 return test_find(ts
, argc
, argv
, MNT_ITER_FORWARD
);
2261 static int test_find_pair(struct libmnt_test
*ts
__attribute__((unused
)),
2262 int argc
, char *argv
[])
2264 struct libmnt_table
*tb
;
2265 struct libmnt_fs
*fs
;
2266 struct libmnt_cache
*mpc
= NULL
;
2272 tb
= create_table(argv
[1], FALSE
);
2275 mpc
= mnt_new_cache();
2278 mnt_table_set_cache(tb
, mpc
);
2279 mnt_unref_cache(mpc
);
2281 fs
= mnt_table_find_pair(tb
, argv
[2], argv
[3], MNT_ITER_FORWARD
);
2285 mnt_fs_print_debug(fs
, stdout
);
2288 mnt_unref_table(tb
);
2292 static int test_find_mountpoint(struct libmnt_test
*ts
__attribute__((unused
)),
2293 int argc
, char *argv
[])
2295 struct libmnt_table
*tb
;
2296 struct libmnt_fs
*fs
;
2297 struct libmnt_cache
*mpc
= NULL
;
2303 tb
= mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
);
2306 mpc
= mnt_new_cache();
2309 mnt_table_set_cache(tb
, mpc
);
2310 mnt_unref_cache(mpc
);
2312 fs
= mnt_table_find_mountpoint(tb
, argv
[1], MNT_ITER_BACKWARD
);
2316 mnt_fs_print_debug(fs
, stdout
);
2319 mnt_unref_table(tb
);
2323 static int test_is_mounted(struct libmnt_test
*ts
__attribute__((unused
)),
2324 int argc
, char *argv
[])
2326 struct libmnt_table
*tb
= NULL
, *fstab
= NULL
;
2327 struct libmnt_fs
*fs
;
2328 struct libmnt_iter
*itr
= NULL
;
2329 struct libmnt_cache
*mpc
= NULL
;
2334 tb
= mnt_new_table_from_file("/proc/self/mountinfo");
2336 fprintf(stderr
, "failed to parse mountinfo\n");
2340 fstab
= create_table(argv
[1], FALSE
);
2344 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
2348 mpc
= mnt_new_cache();
2351 mnt_table_set_cache(tb
, mpc
);
2352 mnt_unref_cache(mpc
);
2354 while (mnt_table_next_fs(fstab
, itr
, &fs
) == 0) {
2355 if (mnt_table_is_fs_mounted(tb
, fs
))
2356 printf("%s already mounted on %s\n",
2357 mnt_fs_get_source(fs
),
2358 mnt_fs_get_target(fs
));
2360 printf("%s not mounted on %s\n",
2361 mnt_fs_get_source(fs
),
2362 mnt_fs_get_target(fs
));
2366 mnt_unref_table(tb
);
2367 mnt_unref_table(fstab
);
2372 /* returns 0 if @a and @b targets are the same */
2373 static int test_uniq_cmp(struct libmnt_table
*tb
__attribute__((__unused__
)),
2374 struct libmnt_fs
*a
,
2375 struct libmnt_fs
*b
)
2380 return mnt_fs_streq_target(a
, mnt_fs_get_target(b
)) ? 0 : 1;
2383 static int test_uniq(struct libmnt_test
*ts
__attribute__((unused
)),
2384 int argc
, char *argv
[])
2386 struct libmnt_table
*tb
;
2390 fprintf(stderr
, "try --help\n");
2394 tb
= create_table(argv
[1], FALSE
);
2398 if (mnt_table_uniq_fs(tb
, 0, test_uniq_cmp
) == 0) {
2399 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_FORWARD
);
2400 struct libmnt_fs
*fs
;
2403 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0)
2404 mnt_fs_print_debug(fs
, stdout
);
2409 mnt_unref_table(tb
);
2414 int main(int argc
, char *argv
[])
2416 struct libmnt_test tss
[] = {
2417 { "--parse", test_parse
, "<file> [--comments] parse and print tab" },
2418 { "--find-forward", test_find_fw
, "<file> <source|target> <string>" },
2419 { "--find-backward", test_find_bw
, "<file> <source|target> <string>" },
2420 { "--uniq-target", test_uniq
, "<file>" },
2421 { "--find-pair", test_find_pair
, "<file> <source> <target>" },
2422 { "--find-fs", test_find_idx
, "<file> <target>" },
2423 { "--find-mountpoint", test_find_mountpoint
, "<path>" },
2424 { "--copy-fs", test_copy_fs
, "<file> copy root FS from the file" },
2425 { "--is-mounted", test_is_mounted
, "<fstab> check what from fstab is already mounted" },
2429 return mnt_run_test(tss
, argc
, argv
);
2432 #endif /* TEST_PROGRAM */