]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/tab.c
2 * Copyright (C) 2008-2010 Karel Zak <kzak@redhat.com>
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
10 * @title: Table of filesystems
11 * @short_description: container for entries from fstab, mtab or mountinfo
13 * Note that mnt_table_find_* functions are mount(8) compatible. These functions
14 * try to find an entry in more iterations, where the first attempt is always
15 * based on comparison with unmodified (non-canonicalized or un-evaluated)
16 * paths or tags. For example a fstab with two entries:
19 * LABEL=foo /foo auto rw
20 * /dev/foo /foo auto rw
24 * where both lines are used for the *same* device, then
27 * mnt_table_find_source(tb, "/dev/foo", &fs);
30 * will returns the second line, and
33 * mnt_table_find_source(tb, "LABEL=foo", &fs);
36 * will returns the first entry, and
39 * mnt_table_find_source(tb, "UUID=anyuuid", &fs);
42 * will return the first entry (if UUID matches with the device).
49 #include "fileutils.h"
51 int is_mountinfo(struct libmnt_table
*tb
)
58 fs
= list_first_entry(&tb
->ents
, struct libmnt_fs
, ents
);
59 if (fs
&& mnt_fs_is_kernel(fs
) && mnt_fs_get_root(fs
))
68 * The tab is a container for struct libmnt_fs entries that usually represents a fstab,
69 * mtab or mountinfo file from your system.
71 * See also mnt_table_parse_file().
73 * Returns: newly allocated tab struct.
75 struct libmnt_table
*mnt_new_table(void)
77 struct libmnt_table
*tb
= NULL
;
79 tb
= calloc(1, sizeof(*tb
));
83 DBG(TAB
, ul_debugobj(tb
, "alloc"));
85 INIT_LIST_HEAD(&tb
->ents
);
93 * Removes all entries (filesystems) from the table. The filesystems with zero
94 * reference count will be deallocated.
96 * Returns: 0 on success or negative number in case of error.
98 int mnt_reset_table(struct libmnt_table
*tb
)
103 DBG(TAB
, ul_debugobj(tb
, "reset"));
105 while (!list_empty(&tb
->ents
)) {
106 struct libmnt_fs
*fs
= list_entry(tb
->ents
.next
,
107 struct libmnt_fs
, ents
);
108 mnt_table_remove_fs(tb
, fs
);
119 * Increments reference counter.
121 void mnt_ref_table(struct libmnt_table
*tb
)
125 /*DBG(FS, ul_debugobj(tb, "ref=%d", tb->refcount));*/
133 * De-increments reference counter, on zero the @tb is automatically
134 * deallocated by mnt_free_table().
136 void mnt_unref_table(struct libmnt_table
*tb
)
140 /*DBG(FS, ul_debugobj(tb, "unref=%d", tb->refcount));*/
141 if (tb
->refcount
<= 0)
151 * Deallocates the table. This function does not care about reference count. Don't
152 * use this function directly -- it's better to use use mnt_unref_table().
154 * The table entries (filesystems) are unrefrenced by mnt_reset_table() and
155 * cache by mnt_unref_cache().
157 void mnt_free_table(struct libmnt_table
*tb
)
163 DBG(TAB
, ul_debugobj(tb
, "free [refcount=%d]", tb
->refcount
));
165 mnt_unref_cache(tb
->cache
);
166 free(tb
->comm_intro
);
172 * mnt_table_get_nents:
173 * @tb: pointer to tab
175 * Returns: number of entries in table.
177 int mnt_table_get_nents(struct libmnt_table
*tb
)
179 return tb
? tb
->nents
: 0;
183 * mnt_table_is_empty:
184 * @tb: pointer to tab
186 * Returns: 1 if the table is without filesystems, or 0.
188 int mnt_table_is_empty(struct libmnt_table
*tb
)
190 return tb
== NULL
|| list_empty(&tb
->ents
) ? 1 : 0;
194 * mnt_table_set_userdata:
195 * @tb: pointer to tab
196 * @data: pointer to user data
198 * Sets pointer to the private user data.
200 * Returns: 0 on success or negative number in case of error.
202 int mnt_table_set_userdata(struct libmnt_table
*tb
, void *data
)
212 * mnt_table_get_userdata:
213 * @tb: pointer to tab
215 * Returns: pointer to user's data.
217 void *mnt_table_get_userdata(struct libmnt_table
*tb
)
219 return tb
? tb
->userdata
: NULL
;
223 * mnt_table_enable_comments:
224 * @tb: pointer to tab
225 * @enable: TRUE or FALSE
227 * Enables parsing of comments.
229 * The initial (intro) file comment is accessible by
230 * mnt_table_get_intro_comment(). The intro and the comment of the first fstab
231 * entry has to be separated by blank line. The filesystem comments are
232 * accessible by mnt_fs_get_comment(). The trailing fstab comment is accessible
233 * by mnt_table_get_trailing_comment().
241 * # this comments belongs to the first fs
242 * LABEL=foo /mnt/foo auto defaults 1 2
243 * # this comments belongs to the second fs
244 * LABEL=bar /mnt/bar auto defaults 1 2
249 void mnt_table_enable_comments(struct libmnt_table
*tb
, int enable
)
256 * mnt_table_with_comments:
257 * @tb: pointer to table
259 * Returns: 1 if comments parsing is enabled, or 0.
261 int mnt_table_with_comments(struct libmnt_table
*tb
)
264 return tb
? tb
->comms
: 0;
268 * mnt_table_get_intro_comment:
269 * @tb: pointer to tab
271 * Returns: initial comment in tb
273 const char *mnt_table_get_intro_comment(struct libmnt_table
*tb
)
275 return tb
? tb
->comm_intro
: NULL
;
279 * mnt_table_set_into_comment:
280 * @tb: pointer to tab
281 * @comm: comment or NULL
283 * Sets the initial comment in tb.
285 * Returns: 0 on success or negative number in case of error.
287 int mnt_table_set_intro_comment(struct libmnt_table
*tb
, const char *comm
)
298 free(tb
->comm_intro
);
304 * mnt_table_append_into_comment:
305 * @tb: pointer to tab
306 * @comm: comment of NULL
308 * Appends the initial comment in tb.
310 * Returns: 0 on success or negative number in case of error.
312 int mnt_table_append_intro_comment(struct libmnt_table
*tb
, const char *comm
)
316 return append_string(&tb
->comm_intro
, comm
);
320 * mnt_table_get_trailing_comment:
321 * @tb: pointer to tab
323 * Returns: table trailing comment
325 const char *mnt_table_get_trailing_comment(struct libmnt_table
*tb
)
327 return tb
? tb
->comm_tail
: NULL
;
331 * mnt_table_set_trailing_comment
332 * @tb: pointer to tab
333 * @comm: comment string
335 * Sets the trailing comment in table.
337 * Returns: 0 on success or negative number in case of error.
339 int mnt_table_set_trailing_comment(struct libmnt_table
*tb
, const char *comm
)
356 * mnt_table_append_trailing_comment:
357 * @tb: pointer to tab
358 * @comm: comment of NULL
360 * Appends to the trailing table comment.
362 * Returns: 0 on success or negative number in case of error.
364 int mnt_table_append_trailing_comment(struct libmnt_table
*tb
, const char *comm
)
368 return append_string(&tb
->comm_tail
, comm
);
372 * mnt_table_set_cache:
373 * @tb: pointer to tab
374 * @mpc: pointer to struct libmnt_cache instance
376 * Sets up a cache for canonicalized paths and evaluated tags (LABEL/UUID). The
377 * cache is recommended for mnt_table_find_*() functions.
379 * The cache could be shared between more tabs. Be careful when you share the
380 * same cache between more threads -- currently the cache does not provide any
383 * This function increments cache reference counter. It's recomented to use
384 * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache
385 * referenced by @tb only.
387 * See also mnt_new_cache().
389 * Returns: 0 on success or negative number in case of error.
391 int mnt_table_set_cache(struct libmnt_table
*tb
, struct libmnt_cache
*mpc
)
396 mnt_ref_cache(mpc
); /* new */
397 mnt_unref_cache(tb
->cache
); /* old */
403 * mnt_table_get_cache:
404 * @tb: pointer to tab
406 * Returns: pointer to struct libmnt_cache instance or NULL.
408 struct libmnt_cache
*mnt_table_get_cache(struct libmnt_table
*tb
)
410 return tb
? tb
->cache
: NULL
;
418 * Adds a new entry to tab and increment @fs reference counter. Don't forget to
419 * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs
420 * referenced by the table only.
422 * Returns: 0 on success or negative number in case of error.
424 int mnt_table_add_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
430 list_add_tail(&fs
->ents
, &tb
->ents
);
433 DBG(TAB
, ul_debugobj(tb
, "add entry: %s %s",
434 mnt_fs_get_source(fs
), mnt_fs_get_target(fs
)));
439 * mnt_table_remove_fs:
443 * Removes the @fs from the table and de-increment reference counter of the @fs. The
444 * filesystem with zero reference counter will be deallocated. Don't forget to use
445 * mnt_ref_fs() before call mnt_table_remove_fs() if you want to use @fs later.
447 * Returns: 0 on success or negative number in case of error.
449 int mnt_table_remove_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
455 INIT_LIST_HEAD(&fs
->ents
); /* otherwise FS still points to the list */
463 * mnt_table_get_root_fs:
464 * @tb: mountinfo file (/proc/self/mountinfo)
465 * @root: returns pointer to the root filesystem (/)
467 * The function uses the parent ID from the mountinfo file to determine the root filesystem
468 * (the filesystem with the smallest ID). The function is designed mostly for
469 * applications where it is necessary to sort mountpoints by IDs to get the tree
470 * of the mountpoints (e.g. findmnt default output).
472 * If you're not sure, then use
474 * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD);
476 * this is more robust and usable for arbitrary tab files (including fstab).
478 * Returns: 0 on success or negative number in case of error.
480 int mnt_table_get_root_fs(struct libmnt_table
*tb
, struct libmnt_fs
**root
)
482 struct libmnt_iter itr
;
483 struct libmnt_fs
*fs
;
486 if (!tb
|| !root
|| !is_mountinfo(tb
))
489 DBG(TAB
, ul_debugobj(tb
, "lookup root fs"));
493 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
494 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
495 int id
= mnt_fs_get_parent_id(fs
);
497 if (!*root
|| id
< root_id
) {
503 return *root
? 0 : -EINVAL
;
507 * mnt_table_next_child_fs:
508 * @tb: mountinfo file (/proc/self/mountinfo)
510 * @parent: parental FS
511 * @chld: returns the next child filesystem
513 * Note that filesystems are returned in the order of mounting (according to
514 * IDs in /proc/self/mountinfo).
516 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
518 int mnt_table_next_child_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
519 struct libmnt_fs
*parent
, struct libmnt_fs
**chld
)
521 struct libmnt_fs
*fs
;
522 int parent_id
, lastchld_id
= 0, chld_id
= 0;
524 if (!tb
|| !itr
|| !parent
|| !is_mountinfo(tb
))
527 DBG(TAB
, ul_debugobj(tb
, "lookup next child of '%s'",
528 mnt_fs_get_target(parent
)));
530 parent_id
= mnt_fs_get_id(parent
);
532 /* get ID of the previously returned child */
533 if (itr
->head
&& itr
->p
!= itr
->head
) {
534 MNT_ITER_ITERATE(itr
, fs
, struct libmnt_fs
, ents
);
535 lastchld_id
= mnt_fs_get_id(fs
);
540 mnt_reset_iter(itr
, MNT_ITER_FORWARD
);
541 while(mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
544 if (mnt_fs_get_parent_id(fs
) != parent_id
)
547 id
= mnt_fs_get_id(fs
);
549 /* avoid an infinite loop. This only happens in rare cases
550 * such as in early userspace when the rootfs is its own parent */
554 if ((!lastchld_id
|| id
> lastchld_id
) &&
555 (!*chld
|| id
< chld_id
)) {
562 return 1; /* end of iterator */
564 /* set the iterator to the @chld for the next call */
565 mnt_table_set_iter(tb
, itr
, *chld
);
574 * @fs: returns the next tab entry
576 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
581 * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
582 * const char *dir = mnt_fs_get_target(fs);
583 * printf("mount point: %s\n", dir);
588 * lists all mountpoints from fstab in reverse order.
590 int mnt_table_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
**fs
)
594 if (!tb
|| !itr
|| !fs
)
599 MNT_ITER_INIT(itr
, &tb
->ents
);
600 if (itr
->p
!= itr
->head
) {
601 MNT_ITER_ITERATE(itr
, *fs
, struct libmnt_fs
, ents
);
609 * mnt_table_first_fs:
611 * @fs: returns the first tab entry
613 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
615 int mnt_table_first_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
619 if (list_empty(&tb
->ents
))
621 *fs
= list_first_entry(&tb
->ents
, struct libmnt_fs
, ents
);
628 * @fs: returns the last tab entry
630 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
632 int mnt_table_last_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
636 if (list_empty(&tb
->ents
))
638 *fs
= list_last_entry(&tb
->ents
, struct libmnt_fs
, ents
);
643 * mnt_table_find_next_fs:
646 * @match_func: function returning 1 or 0
647 * @userdata: extra data for match_func
648 * @fs: returns pointer to the next matching table entry
650 * This function allows searching in @tb.
652 * Returns: negative number in case of error, 1 at end of table or 0 o success.
654 int mnt_table_find_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
655 int (*match_func
)(struct libmnt_fs
*, void *), void *userdata
,
656 struct libmnt_fs
**fs
)
658 if (!tb
|| !itr
|| !fs
|| !match_func
)
661 DBG(TAB
, ul_debugobj(tb
, "lookup next fs"));
664 MNT_ITER_INIT(itr
, &tb
->ents
);
667 if (itr
->p
!= itr
->head
)
668 MNT_ITER_ITERATE(itr
, *fs
, struct libmnt_fs
, ents
);
672 if (match_func(*fs
, userdata
))
680 static int mnt_table_move_parent(struct libmnt_table
*tb
, int oldid
, int newid
)
682 struct libmnt_iter itr
;
683 struct libmnt_fs
*fs
;
687 if (list_empty(&tb
->ents
))
690 DBG(TAB
, ul_debugobj(tb
, "moving parent ID from %d -> %d", oldid
, newid
));
691 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
693 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
694 if (fs
->parent
== oldid
)
704 * @cmp: function to compare filesystems
706 * This function de-duplicate the @tb, but does not change order of the
707 * filesystems. The @cmp function has to return 0 if the filesystems are
708 * equal, otherwise non-zero.
710 * The default is to keep in the table later mounted filesystems (function uses
711 * backward mode iterator).
713 * @MNT_UNIQ_FORWARD: remove later mounted filesystems
714 * @MNT_UNIQ_KEEPTREE: keep parent->id relation ship stil valid
716 * Returns: negative number in case of error, or 0 o success.
718 int mnt_table_uniq_fs(struct libmnt_table
*tb
, int flags
,
719 int (*cmp
)(struct libmnt_table
*,
723 struct libmnt_iter itr
;
724 struct libmnt_fs
*fs
;
725 int direction
= MNT_ITER_BACKWARD
;
729 if (list_empty(&tb
->ents
))
732 if (flags
& MNT_UNIQ_FORWARD
)
733 direction
= MNT_ITER_FORWARD
;
735 DBG(TAB
, ul_debugobj(tb
, "de-duplicate"));
736 mnt_reset_iter(&itr
, direction
);
738 if ((flags
& MNT_UNIQ_KEEPTREE
) && !is_mountinfo(tb
))
739 flags
&= ~MNT_UNIQ_KEEPTREE
;
741 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
743 struct libmnt_iter xtr
;
746 mnt_reset_iter(&xtr
, direction
);
747 while (want
&& mnt_table_next_fs(tb
, &xtr
, &x
) == 0) {
750 want
= cmp(tb
, x
, fs
) != 0;
754 if (flags
& MNT_UNIQ_KEEPTREE
)
755 mnt_table_move_parent(tb
, mnt_fs_get_id(fs
),
756 mnt_fs_get_parent_id(fs
));
758 DBG(TAB
, ul_debugobj(tb
, "remove duplicate %s",
759 mnt_fs_get_target(fs
)));
760 mnt_table_remove_fs(tb
, fs
);
768 * mnt_table_set_iter:
773 * Sets @iter to the position of @fs in the file @tb.
775 * Returns: 0 on success, negative number in case of error.
777 int mnt_table_set_iter(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
*fs
)
779 if (!tb
|| !itr
|| !fs
)
782 MNT_ITER_INIT(itr
, &tb
->ents
);
789 * mnt_table_find_mountpoint:
792 * @direction: MNT_ITER_{FORWARD,BACKWARD}
794 * Same as mnt_get_mountpoint(), except this function does not rely on
797 * Returns: a tab entry or NULL.
799 struct libmnt_fs
*mnt_table_find_mountpoint(struct libmnt_table
*tb
,
805 if (!tb
|| !path
|| !*path
)
807 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
810 DBG(TAB
, ul_debugobj(tb
, "lookup MOUNTPOINT: '%s'", path
));
818 struct libmnt_fs
*fs
;
820 fs
= mnt_table_find_target(tb
, mnt
, direction
);
826 p
= stripoff_last_component(mnt
);
829 } while (mnt
&& *(mnt
+ 1) != '\0');
832 return mnt_table_find_target(tb
, "/", direction
);
836 * mnt_table_find_target:
838 * @path: mountpoint directory
839 * @direction: MNT_ITER_{FORWARD,BACKWARD}
841 * Try to lookup an entry in the given tab, three iterations are possible, the first
842 * with @path, the second with realpath(@path) and the third with realpath(@path)
843 * against realpath(fs->target). The 2nd and 3rd iterations are not performed when
844 * the @tb cache is not set (see mnt_table_set_cache()). If
845 * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any
846 * @fs->target found in @mtab (see mnt_resolve_target()).
848 * Returns: a tab entry or NULL.
850 struct libmnt_fs
*mnt_table_find_target(struct libmnt_table
*tb
, const char *path
, int direction
)
852 struct libmnt_iter itr
;
853 struct libmnt_fs
*fs
= NULL
;
856 if (!tb
|| !path
|| !*path
)
858 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
861 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s'", path
));
864 mnt_reset_iter(&itr
, direction
);
865 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
866 if (mnt_fs_streq_target(fs
, path
))
869 if (!tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
872 DBG(TAB
, ul_debugobj(tb
, "lookup canonical TARGET: '%s'", cn
));
874 /* canonicalized paths in struct libmnt_table */
875 mnt_reset_iter(&itr
, direction
);
876 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
877 if (mnt_fs_streq_target(fs
, cn
))
881 /* non-canonicaled path in struct libmnt_table
882 * -- note that mountpoint in /proc/self/mountinfo is already
883 * canonicalized by the kernel
885 mnt_reset_iter(&itr
, direction
);
886 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
890 || mnt_fs_is_swaparea(fs
)
891 || mnt_fs_is_kernel(fs
)
892 || (*fs
->target
== '/' && *(fs
->target
+ 1) == '\0'))
895 p
= mnt_resolve_target(fs
->target
, tb
->cache
);
896 /* both canonicalized, strcmp() is fine here */
897 if (p
&& strcmp(cn
, p
) == 0)
904 * mnt_table_find_srcpath:
906 * @path: source path (devname or dirname) or NULL
907 * @direction: MNT_ITER_{FORWARD,BACKWARD}
909 * Try to lookup an entry in the given tab, four iterations are possible, the first
910 * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..)
911 * from @path and the fourth with realpath(@path) against realpath(entry->srcpath).
913 * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
914 * set (see mnt_table_set_cache()).
916 * Note that NULL is a valid source path; it will be replaced with "none". The
917 * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
919 * Returns: a tab entry or NULL.
921 struct libmnt_fs
*mnt_table_find_srcpath(struct libmnt_table
*tb
, const char *path
, int direction
)
923 struct libmnt_iter itr
;
924 struct libmnt_fs
*fs
= NULL
;
925 int ntags
= 0, nents
;
929 if (!tb
|| !path
|| !*path
)
931 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
934 DBG(TAB
, ul_debugobj(tb
, "lookup SRCPATH: '%s'", path
));
937 mnt_reset_iter(&itr
, direction
);
938 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
939 if (mnt_fs_streq_srcpath(fs
, path
))
941 if (mnt_fs_get_tag(fs
, NULL
, NULL
) == 0)
945 if (!path
|| !tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
948 DBG(TAB
, ul_debugobj(tb
, "lookup canonical SRCPATH: '%s'", cn
));
950 nents
= mnt_table_get_nents(tb
);
952 /* canonicalized paths in struct libmnt_table */
954 mnt_reset_iter(&itr
, direction
);
955 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
956 if (mnt_fs_streq_srcpath(fs
, cn
))
963 int rc
= mnt_cache_read_tags(tb
->cache
, cn
);
965 mnt_reset_iter(&itr
, direction
);
968 /* @path's TAGs are in the cache */
969 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
972 if (mnt_fs_get_tag(fs
, &t
, &v
))
975 if (mnt_cache_device_has_tag(tb
->cache
, cn
, t
, v
))
978 } else if (rc
< 0 && errno
== EACCES
) {
979 /* @path is inaccessible, try evaluating all TAGs in @tb
980 * by udev symlinks -- this could be expensive on systems
981 * with a huge fstab/mtab */
982 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
983 const char *t
, *v
, *x
;
984 if (mnt_fs_get_tag(fs
, &t
, &v
))
986 x
= mnt_resolve_tag(t
, v
, tb
->cache
);
988 /* both canonicalized, strcmp() is fine here */
989 if (x
&& strcmp(x
, cn
) == 0)
995 /* non-canonicalized paths in struct libmnt_table */
996 if (ntags
<= nents
) {
997 mnt_reset_iter(&itr
, direction
);
998 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
999 if (mnt_fs_is_netfs(fs
) || mnt_fs_is_pseudofs(fs
))
1001 p
= mnt_fs_get_srcpath(fs
);
1003 p
= mnt_resolve_path(p
, tb
->cache
);
1005 /* both canonicalized, strcmp() is fine here */
1006 if (p
&& strcmp(p
, cn
) == 0)
1016 * mnt_table_find_tag:
1018 * @tag: tag name (e.g "LABEL", "UUID", ...)
1020 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1022 * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and
1023 * @val, for the second attempt the tag is evaluated (converted to the device
1024 * name) and mnt_table_find_srcpath() is performed. The second attempt is not
1025 * performed when @tb cache is not set (see mnt_table_set_cache()).
1027 * Returns: a tab entry or NULL.
1029 struct libmnt_fs
*mnt_table_find_tag(struct libmnt_table
*tb
, const char *tag
,
1030 const char *val
, int direction
)
1032 struct libmnt_iter itr
;
1033 struct libmnt_fs
*fs
= NULL
;
1035 if (!tb
|| !tag
|| !*tag
|| !val
)
1037 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1040 DBG(TAB
, ul_debugobj(tb
, "lookup by TAG: %s %s", tag
, val
));
1042 /* look up by TAG */
1043 mnt_reset_iter(&itr
, direction
);
1044 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1045 if (fs
->tagname
&& fs
->tagval
&&
1046 strcmp(fs
->tagname
, tag
) == 0 &&
1047 strcmp(fs
->tagval
, val
) == 0)
1052 /* look up by device name */
1053 char *cn
= mnt_resolve_tag(tag
, val
, tb
->cache
);
1055 return mnt_table_find_srcpath(tb
, cn
, direction
);
1061 * mnt_table_find_source:
1063 * @source: TAG or path
1064 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1066 * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care
1067 * about the @source format (device, LABEL, UUID, ...). This function parses
1068 * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath().
1070 * Returns: a tab entry or NULL.
1072 struct libmnt_fs
*mnt_table_find_source(struct libmnt_table
*tb
,
1073 const char *source
, int direction
)
1075 struct libmnt_fs
*fs
;
1076 char *t
= NULL
, *v
= NULL
;
1080 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1083 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: '%s'", source
));
1085 if (blkid_parse_tag_string(source
, &t
, &v
) || !mnt_valid_tagname(t
))
1086 fs
= mnt_table_find_srcpath(tb
, source
, direction
);
1088 fs
= mnt_table_find_tag(tb
, t
, v
, direction
);
1097 * mnt_table_find_pair
1099 * @source: TAG or path
1100 * @target: mountpoint
1101 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1103 * This function is implemented by mnt_fs_match_source() and
1104 * mnt_fs_match_target() functions. It means that this is more expensive than
1105 * others mnt_table_find_* function, because every @tab entry is fully evaluated.
1107 * Returns: a tab entry or NULL.
1109 struct libmnt_fs
*mnt_table_find_pair(struct libmnt_table
*tb
, const char *source
,
1110 const char *target
, int direction
)
1112 struct libmnt_fs
*fs
= NULL
;
1113 struct libmnt_iter itr
;
1115 if (!tb
|| !target
|| !*target
|| !source
|| !*source
)
1117 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1120 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: %s TARGET: %s", source
, target
));
1122 mnt_reset_iter(&itr
, direction
);
1123 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1125 if (mnt_fs_match_target(fs
, target
, tb
->cache
) &&
1126 mnt_fs_match_source(fs
, source
, tb
->cache
))
1134 * mnt_table_find_devno
1135 * @tb: /proc/self/mountinfo
1136 * @devno: device number
1137 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1139 * Note that zero could be a valid device number for the root pseudo filesystem (e.g.
1142 * Returns: a tab entry or NULL.
1144 struct libmnt_fs
*mnt_table_find_devno(struct libmnt_table
*tb
,
1145 dev_t devno
, int direction
)
1147 struct libmnt_fs
*fs
= NULL
;
1148 struct libmnt_iter itr
;
1154 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1157 DBG(TAB
, ul_debugobj(tb
, "lookup DEVNO: %d", (int) devno
));
1159 mnt_reset_iter(&itr
, direction
);
1161 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1162 if (mnt_fs_get_devno(fs
) == devno
)
1170 * tb: /proc/self/mountinfo
1172 * mountflags: MS_BIND or 0
1173 * fsroot: fs-root that will probably be used in the mountinfo file
1174 * for @fs after mount(2)
1176 * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
1178 * Returns: entry from @tb that will be used as a source for @fs if the @fs is
1181 * Don't export to library API!
1183 struct libmnt_fs
*mnt_table_get_fs_root(struct libmnt_table
*tb
,
1184 struct libmnt_fs
*fs
,
1185 unsigned long mountflags
,
1188 char *root
= NULL
, *mnt
= NULL
;
1190 struct libmnt_fs
*src_fs
= NULL
;
1195 DBG(TAB
, ul_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs
)));
1197 fstype
= mnt_fs_get_fstype(fs
);
1199 if (tb
&& (mountflags
& MS_BIND
)) {
1200 const char *src
, *src_root
;
1203 DBG(TAB
, ul_debug("fs-root for bind"));
1205 src
= xsrc
= mnt_resolve_spec(mnt_fs_get_source(fs
), tb
->cache
);
1207 mnt
= mnt_get_mountpoint(src
);
1209 root
= mnt_get_fs_root(src
, mnt
);
1211 if (xsrc
&& !tb
->cache
) {
1218 src_fs
= mnt_table_find_target(tb
, mnt
, MNT_ITER_BACKWARD
);
1220 DBG(TAB
, ul_debug("not found '%s' in mountinfo -- using default", mnt
));
1224 /* on btrfs the subvolume is used as fs-root in
1225 * /proc/self/mountinfo, so we have to get the original subvolume
1226 * name from src_fs and prepend the subvolume name to the
1229 src_root
= mnt_fs_get_root(src_fs
);
1230 if (src_root
&& !startswith(root
, src_root
)) {
1231 size_t sz
= strlen(root
) + strlen(src_root
) + 1;
1232 char *tmp
= malloc(sz
);
1236 snprintf(tmp
, sz
, "%s%s", src_root
, root
);
1243 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
1245 else if (fstype
&& !strcmp(fstype
, "btrfs")) {
1246 char *vol
= NULL
, *p
;
1247 size_t sz
, volsz
= 0;
1249 if (mnt_fs_get_option(fs
, "subvol", &vol
, &volsz
))
1252 DBG(TAB
, ul_debug("setting FS root: btrfs subvol"));
1257 root
= malloc(sz
+ 1);
1263 memcpy(p
, vol
, volsz
);
1264 *(root
+ sz
) = '\0';
1274 DBG(TAB
, ul_debug("FS root result: %s", root
));
1285 * mnt_table_is_fs_mounted:
1286 * @tb: /proc/self/mountinfo file
1287 * @fstab_fs: /etc/fstab entry
1289 * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
1290 * ignored. This function explicitly compares the source, target and root of the
1293 * Note that source and target are canonicalized only if a cache for @tb is
1294 * defined (see mnt_table_set_cache()). The target canonicalization may
1295 * trigger automount on autofs mountpoints!
1297 * Don't use it if you want to know if a device is mounted, just use
1298 * mnt_table_find_source() on the device.
1300 * This function is designed mostly for "mount -a".
1304 int mnt_table_is_fs_mounted(struct libmnt_table
*tb
, struct libmnt_fs
*fstab_fs
)
1306 struct libmnt_iter itr
;
1307 struct libmnt_fs
*fs
;
1310 const char *src
= NULL
, *tgt
= NULL
;
1315 DBG(FS
, ul_debugobj(fstab_fs
, "is FS mounted? [target=%s]",
1316 mnt_fs_get_target(fstab_fs
)));
1318 if (mnt_fs_is_swaparea(fstab_fs
) || mnt_table_is_empty(tb
)) {
1319 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (swap or no data)"));
1323 if (is_mountinfo(tb
)) {
1324 /* @tb is mountinfo, so we can try to use fs-roots */
1325 struct libmnt_fs
*rootfs
;
1328 if (mnt_fs_get_option(fstab_fs
, "bind", NULL
, NULL
) == 0)
1331 rootfs
= mnt_table_get_fs_root(tb
, fstab_fs
, flags
, &root
);
1333 src
= mnt_fs_get_srcpath(rootfs
);
1337 src
= mnt_fs_get_source(fstab_fs
);
1339 if (src
&& tb
->cache
&& !mnt_fs_is_pseudofs(fstab_fs
))
1340 src
= mnt_resolve_spec(src
, tb
->cache
);
1345 devno
= mnt_fs_get_devno(fstab_fs
);
1346 if (!devno
&& stat(src
, &st
) == 0 && S_ISBLK(st
.st_mode
))
1350 tgt
= mnt_fs_get_target(fstab_fs
);
1353 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (no source/target)"));
1356 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
1358 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1360 int eq
= mnt_fs_streq_srcpath(fs
, src
);
1362 if (!eq
&& devno
&& mnt_fs_get_devno(fs
) == devno
)
1366 /* The source does not match. Maybe the source is a loop
1367 * device backing file.
1369 uint64_t offset
= 0;
1374 if (!mnt_fs_is_kernel(fs
) ||
1375 !mnt_fs_get_srcpath(fs
) ||
1376 !startswith(mnt_fs_get_srcpath(fs
), "/dev/loop"))
1377 continue; /* does not look like loopdev */
1379 if (mnt_fs_get_option(fstab_fs
, "offset", &val
, &len
) == 0 &&
1380 mnt_parse_offset(val
, len
, &offset
)) {
1381 DBG(FS
, ul_debugobj(fstab_fs
, "failed to parse offset="));
1384 flags
= LOOPDEV_FL_OFFSET
;
1387 if (loopdev_is_used(mnt_fs_get_srcpath(fs
), src
, offset
, flags
))
1393 const char *r
= mnt_fs_get_root(fs
);
1394 if (!r
|| strcmp(r
, root
) != 0)
1399 * Compare target, try to minimize the number of situations when we
1400 * need to canonicalize the path to avoid readlink() on
1404 if (mnt_fs_streq_target(fs
, tgt
))
1407 xtgt
= mnt_resolve_path(tgt
, tb
->cache
);
1409 if (xtgt
&& mnt_fs_streq_target(fs
, xtgt
))
1414 rc
= 1; /* success */
1418 DBG(TAB
, ul_debugobj(tb
, "mnt_table_is_fs_mounted: %s [rc=%d]", src
, rc
));
1423 #include "pathnames.h"
1425 static int parser_errcb(struct libmnt_table
*tb
, const char *filename
, int line
)
1427 fprintf(stderr
, "%s:%d: parse error\n", filename
, line
);
1429 return 1; /* all errors are recoverable -- this is the default */
1432 struct libmnt_table
*create_table(const char *file
, int comments
)
1434 struct libmnt_table
*tb
;
1438 tb
= mnt_new_table();
1442 mnt_table_enable_comments(tb
, comments
);
1443 mnt_table_set_parser_errcb(tb
, parser_errcb
);
1445 if (mnt_table_parse_file(tb
, file
) != 0)
1449 fprintf(stderr
, "%s: parsing failed\n", file
);
1450 mnt_unref_table(tb
);
1454 int test_copy_fs(struct libmnt_test
*ts
, int argc
, char *argv
[])
1456 struct libmnt_table
*tb
;
1457 struct libmnt_fs
*fs
;
1460 tb
= create_table(argv
[1], FALSE
);
1464 fs
= mnt_table_find_target(tb
, "/", MNT_ITER_FORWARD
);
1468 printf("ORIGINAL:\n");
1469 mnt_fs_print_debug(fs
, stdout
);
1471 fs
= mnt_copy_fs(NULL
, fs
);
1476 mnt_fs_print_debug(fs
, stdout
);
1480 mnt_unref_table(tb
);
1484 int test_parse(struct libmnt_test
*ts
, int argc
, char *argv
[])
1486 struct libmnt_table
*tb
= NULL
;
1487 struct libmnt_iter
*itr
= NULL
;
1488 struct libmnt_fs
*fs
;
1490 int parse_comments
= FALSE
;
1492 if (argc
== 3 && !strcmp(argv
[2], "--comments"))
1493 parse_comments
= TRUE
;
1495 tb
= create_table(argv
[1], parse_comments
);
1499 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
1503 if (mnt_table_get_intro_comment(tb
))
1504 fprintf(stdout
, "Initial comment:\n\"%s\"\n",
1505 mnt_table_get_intro_comment(tb
));
1507 while(mnt_table_next_fs(tb
, itr
, &fs
) == 0)
1508 mnt_fs_print_debug(fs
, stdout
);
1510 if (mnt_table_get_trailing_comment(tb
))
1511 fprintf(stdout
, "Trailing comment:\n\"%s\"\n",
1512 mnt_table_get_trailing_comment(tb
));
1516 mnt_unref_table(tb
);
1520 int test_find(struct libmnt_test
*ts
, int argc
, char *argv
[], int dr
)
1522 struct libmnt_table
*tb
;
1523 struct libmnt_fs
*fs
= NULL
;
1524 struct libmnt_cache
*mpc
= NULL
;
1525 const char *file
, *find
, *what
;
1529 fprintf(stderr
, "try --help\n");
1533 file
= argv
[1], find
= argv
[2], what
= argv
[3];
1535 tb
= create_table(file
, FALSE
);
1539 /* create a cache for canonicalized paths */
1540 mpc
= mnt_new_cache();
1543 mnt_table_set_cache(tb
, mpc
);
1544 mnt_unref_cache(mpc
);
1546 if (strcasecmp(find
, "source") == 0)
1547 fs
= mnt_table_find_source(tb
, what
, dr
);
1548 else if (strcasecmp(find
, "target") == 0)
1549 fs
= mnt_table_find_target(tb
, what
, dr
);
1552 fprintf(stderr
, "%s: not found %s '%s'\n", file
, find
, what
);
1554 mnt_fs_print_debug(fs
, stdout
);
1558 mnt_unref_table(tb
);
1562 int test_find_bw(struct libmnt_test
*ts
, int argc
, char *argv
[])
1564 return test_find(ts
, argc
, argv
, MNT_ITER_BACKWARD
);
1567 int test_find_fw(struct libmnt_test
*ts
, int argc
, char *argv
[])
1569 return test_find(ts
, argc
, argv
, MNT_ITER_FORWARD
);
1572 int test_find_pair(struct libmnt_test
*ts
, int argc
, char *argv
[])
1574 struct libmnt_table
*tb
;
1575 struct libmnt_fs
*fs
;
1576 struct libmnt_cache
*mpc
= NULL
;
1579 tb
= create_table(argv
[1], FALSE
);
1582 mpc
= mnt_new_cache();
1585 mnt_table_set_cache(tb
, mpc
);
1586 mnt_unref_cache(mpc
);
1588 fs
= mnt_table_find_pair(tb
, argv
[2], argv
[3], MNT_ITER_FORWARD
);
1592 mnt_fs_print_debug(fs
, stdout
);
1595 mnt_unref_table(tb
);
1599 int test_find_mountpoint(struct libmnt_test
*ts
, int argc
, char *argv
[])
1601 struct libmnt_table
*tb
;
1602 struct libmnt_fs
*fs
;
1603 struct libmnt_cache
*mpc
= NULL
;
1606 tb
= mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
);
1609 mpc
= mnt_new_cache();
1612 mnt_table_set_cache(tb
, mpc
);
1613 mnt_unref_cache(mpc
);
1615 fs
= mnt_table_find_mountpoint(tb
, argv
[1], MNT_ITER_BACKWARD
);
1619 mnt_fs_print_debug(fs
, stdout
);
1622 mnt_unref_table(tb
);
1626 static int test_is_mounted(struct libmnt_test
*ts
, int argc
, char *argv
[])
1628 struct libmnt_table
*tb
= NULL
, *fstab
= NULL
;
1629 struct libmnt_fs
*fs
;
1630 struct libmnt_iter
*itr
= NULL
;
1631 struct libmnt_cache
*mpc
= NULL
;
1634 tb
= mnt_new_table_from_file("/proc/self/mountinfo");
1636 fprintf(stderr
, "failed to parse mountinfo\n");
1640 fstab
= create_table(argv
[1], FALSE
);
1644 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
1648 mpc
= mnt_new_cache();
1651 mnt_table_set_cache(tb
, mpc
);
1652 mnt_unref_cache(mpc
);
1654 while(mnt_table_next_fs(fstab
, itr
, &fs
) == 0) {
1655 if (mnt_table_is_fs_mounted(tb
, fs
))
1656 printf("%s already mounted on %s\n",
1657 mnt_fs_get_source(fs
),
1658 mnt_fs_get_target(fs
));
1660 printf("%s not mounted on %s\n",
1661 mnt_fs_get_source(fs
),
1662 mnt_fs_get_target(fs
));
1667 mnt_unref_table(tb
);
1668 mnt_unref_table(fstab
);
1673 /* returns 0 if @a and @b targets are the same */
1674 static int test_uniq_cmp(struct libmnt_table
*tb
__attribute__((__unused__
)),
1675 struct libmnt_fs
*a
,
1676 struct libmnt_fs
*b
)
1681 return mnt_fs_streq_target(a
, mnt_fs_get_target(b
)) ? 0 : 1;
1684 static int test_uniq(struct libmnt_test
*ts
, int argc
, char *argv
[])
1686 struct libmnt_table
*tb
;
1690 fprintf(stderr
, "try --help\n");
1694 tb
= create_table(argv
[1], FALSE
);
1698 if (mnt_table_uniq_fs(tb
, 0, test_uniq_cmp
) == 0) {
1699 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_FORWARD
);
1700 struct libmnt_fs
*fs
;
1703 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0)
1704 mnt_fs_print_debug(fs
, stdout
);
1709 mnt_unref_table(tb
);
1713 int main(int argc
, char *argv
[])
1715 struct libmnt_test tss
[] = {
1716 { "--parse", test_parse
, "<file> [--comments] parse and print tab" },
1717 { "--find-forward", test_find_fw
, "<file> <source|target> <string>" },
1718 { "--find-backward", test_find_bw
, "<file> <source|target> <string>" },
1719 { "--uniq-target", test_uniq
, "<file>" },
1720 { "--find-pair", test_find_pair
, "<file> <source> <target>" },
1721 { "--find-mountpoint", test_find_mountpoint
, "<path>" },
1722 { "--copy-fs", test_copy_fs
, "<file> copy root FS from the file" },
1723 { "--is-mounted", test_is_mounted
, "<fstab> check what from <file> are already mounted" },
1727 return mnt_run_test(tss
, argc
, argv
);
1730 #endif /* TEST_PROGRAM */