]>
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"
50 #include "canonicalize.h"
52 int is_mountinfo(struct libmnt_table
*tb
)
59 fs
= list_first_entry(&tb
->ents
, struct libmnt_fs
, ents
);
60 if (fs
&& mnt_fs_is_kernel(fs
) && mnt_fs_get_root(fs
))
69 * The tab is a container for struct libmnt_fs entries that usually represents a fstab,
70 * mtab or mountinfo file from your system.
72 * See also mnt_table_parse_file().
74 * Returns: newly allocated tab struct.
76 struct libmnt_table
*mnt_new_table(void)
78 struct libmnt_table
*tb
= NULL
;
80 tb
= calloc(1, sizeof(*tb
));
84 DBG(TAB
, ul_debugobj(tb
, "alloc"));
86 INIT_LIST_HEAD(&tb
->ents
);
94 * Removes all entries (filesystems) from the table. The filesystems with zero
95 * reference count will be deallocated.
97 * Returns: 0 on success or negative number in case of error.
99 int mnt_reset_table(struct libmnt_table
*tb
)
104 DBG(TAB
, ul_debugobj(tb
, "reset"));
106 while (!list_empty(&tb
->ents
)) {
107 struct libmnt_fs
*fs
= list_entry(tb
->ents
.next
,
108 struct libmnt_fs
, ents
);
109 mnt_table_remove_fs(tb
, fs
);
120 * Increments reference counter.
122 void mnt_ref_table(struct libmnt_table
*tb
)
126 /*DBG(FS, ul_debugobj(tb, "ref=%d", tb->refcount));*/
134 * De-increments reference counter, on zero the @tb is automatically
135 * deallocated by mnt_free_table().
137 void mnt_unref_table(struct libmnt_table
*tb
)
141 /*DBG(FS, ul_debugobj(tb, "unref=%d", tb->refcount));*/
142 if (tb
->refcount
<= 0)
152 * Deallocates the table. This function does not care about reference count. Don't
153 * use this function directly -- it's better to use use mnt_unref_table().
155 * The table entries (filesystems) are unreferenced by mnt_reset_table() and
156 * cache by mnt_unref_cache().
158 void mnt_free_table(struct libmnt_table
*tb
)
164 DBG(TAB
, ul_debugobj(tb
, "free [refcount=%d]", tb
->refcount
));
166 mnt_unref_cache(tb
->cache
);
167 free(tb
->comm_intro
);
173 * mnt_table_get_nents:
174 * @tb: pointer to tab
176 * Returns: number of entries in table.
178 int mnt_table_get_nents(struct libmnt_table
*tb
)
180 return tb
? tb
->nents
: 0;
184 * mnt_table_is_empty:
185 * @tb: pointer to tab
187 * Returns: 1 if the table is without filesystems, or 0.
189 int mnt_table_is_empty(struct libmnt_table
*tb
)
191 return tb
== NULL
|| list_empty(&tb
->ents
) ? 1 : 0;
195 * mnt_table_set_userdata:
196 * @tb: pointer to tab
197 * @data: pointer to user data
199 * Sets pointer to the private user data.
201 * Returns: 0 on success or negative number in case of error.
203 int mnt_table_set_userdata(struct libmnt_table
*tb
, void *data
)
213 * mnt_table_get_userdata:
214 * @tb: pointer to tab
216 * Returns: pointer to user's data.
218 void *mnt_table_get_userdata(struct libmnt_table
*tb
)
220 return tb
? tb
->userdata
: NULL
;
224 * mnt_table_enable_comments:
225 * @tb: pointer to tab
226 * @enable: TRUE or FALSE
228 * Enables parsing of comments.
230 * The initial (intro) file comment is accessible by
231 * mnt_table_get_intro_comment(). The intro and the comment of the first fstab
232 * entry has to be separated by blank line. The filesystem comments are
233 * accessible by mnt_fs_get_comment(). The trailing fstab comment is accessible
234 * by mnt_table_get_trailing_comment().
242 * # this comments belongs to the first fs
243 * LABEL=foo /mnt/foo auto defaults 1 2
244 * # this comments belongs to the second fs
245 * LABEL=bar /mnt/bar auto defaults 1 2
250 void mnt_table_enable_comments(struct libmnt_table
*tb
, int enable
)
257 * mnt_table_with_comments:
258 * @tb: pointer to table
260 * Returns: 1 if comments parsing is enabled, or 0.
262 int mnt_table_with_comments(struct libmnt_table
*tb
)
265 return tb
? tb
->comms
: 0;
269 * mnt_table_get_intro_comment:
270 * @tb: pointer to tab
272 * Returns: initial comment in tb
274 const char *mnt_table_get_intro_comment(struct libmnt_table
*tb
)
276 return tb
? tb
->comm_intro
: NULL
;
280 * mnt_table_set_into_comment:
281 * @tb: pointer to tab
282 * @comm: comment or NULL
284 * Sets the initial comment in tb.
286 * Returns: 0 on success or negative number in case of error.
288 int mnt_table_set_intro_comment(struct libmnt_table
*tb
, const char *comm
)
290 return strdup_to_struct_member(tb
, comm_intro
, comm
);
294 * mnt_table_append_into_comment:
295 * @tb: pointer to tab
296 * @comm: comment of NULL
298 * Appends the initial comment in tb.
300 * Returns: 0 on success or negative number in case of error.
302 int mnt_table_append_intro_comment(struct libmnt_table
*tb
, const char *comm
)
306 return append_string(&tb
->comm_intro
, comm
);
310 * mnt_table_get_trailing_comment:
311 * @tb: pointer to tab
313 * Returns: table trailing comment
315 const char *mnt_table_get_trailing_comment(struct libmnt_table
*tb
)
317 return tb
? tb
->comm_tail
: NULL
;
321 * mnt_table_set_trailing_comment
322 * @tb: pointer to tab
323 * @comm: comment string
325 * Sets the trailing comment in table.
327 * Returns: 0 on success or negative number in case of error.
329 int mnt_table_set_trailing_comment(struct libmnt_table
*tb
, const char *comm
)
331 return strdup_to_struct_member(tb
, comm_tail
, comm
);
335 * mnt_table_append_trailing_comment:
336 * @tb: pointer to tab
337 * @comm: comment of NULL
339 * Appends to the trailing table comment.
341 * Returns: 0 on success or negative number in case of error.
343 int mnt_table_append_trailing_comment(struct libmnt_table
*tb
, const char *comm
)
347 return append_string(&tb
->comm_tail
, comm
);
351 * mnt_table_set_cache:
352 * @tb: pointer to tab
353 * @mpc: pointer to struct libmnt_cache instance
355 * Sets up a cache for canonicalized paths and evaluated tags (LABEL/UUID). The
356 * cache is recommended for mnt_table_find_*() functions.
358 * The cache could be shared between more tabs. Be careful when you share the
359 * same cache between more threads -- currently the cache does not provide any
362 * This function increments cache reference counter. It's recommended to use
363 * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache
364 * referenced by @tb only.
366 * See also mnt_new_cache().
368 * Returns: 0 on success or negative number in case of error.
370 int mnt_table_set_cache(struct libmnt_table
*tb
, struct libmnt_cache
*mpc
)
375 mnt_ref_cache(mpc
); /* new */
376 mnt_unref_cache(tb
->cache
); /* old */
382 * mnt_table_get_cache:
383 * @tb: pointer to tab
385 * Returns: pointer to struct libmnt_cache instance or NULL.
387 struct libmnt_cache
*mnt_table_get_cache(struct libmnt_table
*tb
)
389 return tb
? tb
->cache
: NULL
;
397 * Adds a new entry to tab and increment @fs reference counter. Don't forget to
398 * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs
399 * referenced by the table only.
401 * Returns: 0 on success or negative number in case of error.
403 int mnt_table_add_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
409 list_add_tail(&fs
->ents
, &tb
->ents
);
412 DBG(TAB
, ul_debugobj(tb
, "add entry: %s %s",
413 mnt_fs_get_source(fs
), mnt_fs_get_target(fs
)));
418 * mnt_table_remove_fs:
422 * Removes the @fs from the table and de-increment reference counter of the @fs. The
423 * filesystem with zero reference counter will be deallocated. Don't forget to use
424 * mnt_ref_fs() before call mnt_table_remove_fs() if you want to use @fs later.
426 * Returns: 0 on success or negative number in case of error.
428 int mnt_table_remove_fs(struct libmnt_table
*tb
, struct libmnt_fs
*fs
)
434 INIT_LIST_HEAD(&fs
->ents
); /* otherwise FS still points to the list */
442 * mnt_table_get_root_fs:
443 * @tb: mountinfo file (/proc/self/mountinfo)
444 * @root: returns pointer to the root filesystem (/)
446 * The function uses the parent ID from the mountinfo file to determine the root filesystem
447 * (the filesystem with the smallest ID). The function is designed mostly for
448 * applications where it is necessary to sort mountpoints by IDs to get the tree
449 * of the mountpoints (e.g. findmnt default output).
451 * If you're not sure, then use
453 * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD);
455 * this is more robust and usable for arbitrary tab files (including fstab).
457 * Returns: 0 on success or negative number in case of error.
459 int mnt_table_get_root_fs(struct libmnt_table
*tb
, struct libmnt_fs
**root
)
461 struct libmnt_iter itr
;
462 struct libmnt_fs
*fs
;
465 if (!tb
|| !root
|| !is_mountinfo(tb
))
468 DBG(TAB
, ul_debugobj(tb
, "lookup root fs"));
472 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
473 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
474 int id
= mnt_fs_get_parent_id(fs
);
476 if (!*root
|| id
< root_id
) {
482 return *root
? 0 : -EINVAL
;
486 * mnt_table_next_child_fs:
487 * @tb: mountinfo file (/proc/self/mountinfo)
489 * @parent: parental FS
490 * @chld: returns the next child filesystem
492 * Note that filesystems are returned in the order of mounting (according to
493 * IDs in /proc/self/mountinfo).
495 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
497 int mnt_table_next_child_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
498 struct libmnt_fs
*parent
, struct libmnt_fs
**chld
)
500 struct libmnt_fs
*fs
;
501 int parent_id
, lastchld_id
= 0, chld_id
= 0;
503 if (!tb
|| !itr
|| !parent
|| !is_mountinfo(tb
))
506 DBG(TAB
, ul_debugobj(tb
, "lookup next child of '%s'",
507 mnt_fs_get_target(parent
)));
509 parent_id
= mnt_fs_get_id(parent
);
511 /* get ID of the previously returned child */
512 if (itr
->head
&& itr
->p
!= itr
->head
) {
513 MNT_ITER_ITERATE(itr
, fs
, struct libmnt_fs
, ents
);
514 lastchld_id
= mnt_fs_get_id(fs
);
519 mnt_reset_iter(itr
, MNT_ITER_FORWARD
);
520 while(mnt_table_next_fs(tb
, itr
, &fs
) == 0) {
523 if (mnt_fs_get_parent_id(fs
) != parent_id
)
526 id
= mnt_fs_get_id(fs
);
528 /* avoid an infinite loop. This only happens in rare cases
529 * such as in early userspace when the rootfs is its own parent */
533 if ((!lastchld_id
|| id
> lastchld_id
) &&
534 (!*chld
|| id
< chld_id
)) {
541 return 1; /* end of iterator */
543 /* set the iterator to the @chld for the next call */
544 mnt_table_set_iter(tb
, itr
, *chld
);
553 * @fs: returns the next tab entry
555 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
560 * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
561 * const char *dir = mnt_fs_get_target(fs);
562 * printf("mount point: %s\n", dir);
567 * lists all mountpoints from fstab in reverse order.
569 int mnt_table_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
**fs
)
573 if (!tb
|| !itr
|| !fs
)
578 MNT_ITER_INIT(itr
, &tb
->ents
);
579 if (itr
->p
!= itr
->head
) {
580 MNT_ITER_ITERATE(itr
, *fs
, struct libmnt_fs
, ents
);
588 * mnt_table_first_fs:
590 * @fs: returns the first tab entry
592 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
594 int mnt_table_first_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
598 if (list_empty(&tb
->ents
))
600 *fs
= list_first_entry(&tb
->ents
, struct libmnt_fs
, ents
);
607 * @fs: returns the last tab entry
609 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
611 int mnt_table_last_fs(struct libmnt_table
*tb
, struct libmnt_fs
**fs
)
615 if (list_empty(&tb
->ents
))
617 *fs
= list_last_entry(&tb
->ents
, struct libmnt_fs
, ents
);
622 * mnt_table_find_next_fs:
625 * @match_func: function returning 1 or 0
626 * @userdata: extra data for match_func
627 * @fs: returns pointer to the next matching table entry
629 * This function allows searching in @tb.
631 * Returns: negative number in case of error, 1 at end of table or 0 o success.
633 int mnt_table_find_next_fs(struct libmnt_table
*tb
, struct libmnt_iter
*itr
,
634 int (*match_func
)(struct libmnt_fs
*, void *), void *userdata
,
635 struct libmnt_fs
**fs
)
637 if (!tb
|| !itr
|| !fs
|| !match_func
)
640 DBG(TAB
, ul_debugobj(tb
, "lookup next fs"));
643 MNT_ITER_INIT(itr
, &tb
->ents
);
646 if (itr
->p
!= itr
->head
)
647 MNT_ITER_ITERATE(itr
, *fs
, struct libmnt_fs
, ents
);
651 if (match_func(*fs
, userdata
))
659 static int mnt_table_move_parent(struct libmnt_table
*tb
, int oldid
, int newid
)
661 struct libmnt_iter itr
;
662 struct libmnt_fs
*fs
;
666 if (list_empty(&tb
->ents
))
669 DBG(TAB
, ul_debugobj(tb
, "moving parent ID from %d -> %d", oldid
, newid
));
670 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
672 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
673 if (fs
->parent
== oldid
)
683 * @cmp: function to compare filesystems
685 * This function de-duplicate the @tb, but does not change order of the
686 * filesystems. The @cmp function has to return 0 if the filesystems are
687 * equal, otherwise non-zero.
689 * The default is to keep in the table later mounted filesystems (function uses
690 * backward mode iterator).
692 * @MNT_UNIQ_FORWARD: remove later mounted filesystems
693 * @MNT_UNIQ_KEEPTREE: keep parent->id relationship still valid
695 * Returns: negative number in case of error, or 0 o success.
697 int mnt_table_uniq_fs(struct libmnt_table
*tb
, int flags
,
698 int (*cmp
)(struct libmnt_table
*,
702 struct libmnt_iter itr
;
703 struct libmnt_fs
*fs
;
704 int direction
= MNT_ITER_BACKWARD
;
708 if (list_empty(&tb
->ents
))
711 if (flags
& MNT_UNIQ_FORWARD
)
712 direction
= MNT_ITER_FORWARD
;
714 DBG(TAB
, ul_debugobj(tb
, "de-duplicate"));
715 mnt_reset_iter(&itr
, direction
);
717 if ((flags
& MNT_UNIQ_KEEPTREE
) && !is_mountinfo(tb
))
718 flags
&= ~MNT_UNIQ_KEEPTREE
;
720 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
722 struct libmnt_iter xtr
;
725 mnt_reset_iter(&xtr
, direction
);
726 while (want
&& mnt_table_next_fs(tb
, &xtr
, &x
) == 0) {
729 want
= cmp(tb
, x
, fs
) != 0;
733 if (flags
& MNT_UNIQ_KEEPTREE
)
734 mnt_table_move_parent(tb
, mnt_fs_get_id(fs
),
735 mnt_fs_get_parent_id(fs
));
737 DBG(TAB
, ul_debugobj(tb
, "remove duplicate %s",
738 mnt_fs_get_target(fs
)));
739 mnt_table_remove_fs(tb
, fs
);
747 * mnt_table_set_iter:
752 * Sets @iter to the position of @fs in the file @tb.
754 * Returns: 0 on success, negative number in case of error.
756 int mnt_table_set_iter(struct libmnt_table
*tb
, struct libmnt_iter
*itr
, struct libmnt_fs
*fs
)
758 if (!tb
|| !itr
|| !fs
)
761 MNT_ITER_INIT(itr
, &tb
->ents
);
768 * mnt_table_find_mountpoint:
771 * @direction: MNT_ITER_{FORWARD,BACKWARD}
773 * Same as mnt_get_mountpoint(), except this function does not rely on
776 * Returns: a tab entry or NULL.
778 struct libmnt_fs
*mnt_table_find_mountpoint(struct libmnt_table
*tb
,
784 if (!tb
|| !path
|| !*path
)
786 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
789 DBG(TAB
, ul_debugobj(tb
, "lookup MOUNTPOINT: '%s'", path
));
797 struct libmnt_fs
*fs
;
799 fs
= mnt_table_find_target(tb
, mnt
, direction
);
805 p
= stripoff_last_component(mnt
);
808 } while (mnt
&& *(mnt
+ 1) != '\0');
811 return mnt_table_find_target(tb
, "/", direction
);
815 * mnt_table_find_target:
817 * @path: mountpoint directory
818 * @direction: MNT_ITER_{FORWARD,BACKWARD}
820 * Try to lookup an entry in the given tab, three iterations are possible, the first
821 * with @path, the second with realpath(@path) and the third with realpath(@path)
822 * against realpath(fs->target). The 2nd and 3rd iterations are not performed when
823 * the @tb cache is not set (see mnt_table_set_cache()). If
824 * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any
825 * @fs->target found in @mtab (see mnt_resolve_target()).
827 * Returns: a tab entry or NULL.
829 struct libmnt_fs
*mnt_table_find_target(struct libmnt_table
*tb
, const char *path
, int direction
)
831 struct libmnt_iter itr
;
832 struct libmnt_fs
*fs
= NULL
;
835 if (!tb
|| !path
|| !*path
)
837 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
840 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s'", path
));
843 mnt_reset_iter(&itr
, direction
);
844 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
845 if (mnt_fs_streq_target(fs
, path
))
849 /* try absolute path */
850 if (is_relative_path(path
) && (cn
= absolute_path(path
))) {
851 DBG(TAB
, ul_debugobj(tb
, "lookup absolute TARGET: '%s'", cn
));
852 mnt_reset_iter(&itr
, direction
);
853 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
854 if (mnt_fs_streq_target(fs
, cn
)) {
862 if (!tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
865 DBG(TAB
, ul_debugobj(tb
, "lookup canonical TARGET: '%s'", cn
));
867 /* canonicalized paths in struct libmnt_table */
868 mnt_reset_iter(&itr
, direction
);
869 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
870 if (mnt_fs_streq_target(fs
, cn
))
874 /* non-canonical path in struct libmnt_table
875 * -- note that mountpoint in /proc/self/mountinfo is already
876 * canonicalized by the kernel
878 mnt_reset_iter(&itr
, direction
);
879 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
883 || mnt_fs_is_swaparea(fs
)
884 || mnt_fs_is_kernel(fs
)
885 || (*fs
->target
== '/' && *(fs
->target
+ 1) == '\0'))
888 p
= mnt_resolve_target(fs
->target
, tb
->cache
);
889 /* both canonicalized, strcmp() is fine here */
890 if (p
&& strcmp(cn
, p
) == 0)
897 * mnt_table_find_srcpath:
899 * @path: source path (devname or dirname) or NULL
900 * @direction: MNT_ITER_{FORWARD,BACKWARD}
902 * Try to lookup an entry in the given tab, four iterations are possible, the first
903 * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..)
904 * from @path and the fourth with realpath(@path) against realpath(entry->srcpath).
906 * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
907 * set (see mnt_table_set_cache()).
909 * Note that NULL is a valid source path; it will be replaced with "none". The
910 * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
912 * Returns: a tab entry or NULL.
914 struct libmnt_fs
*mnt_table_find_srcpath(struct libmnt_table
*tb
, const char *path
, int direction
)
916 struct libmnt_iter itr
;
917 struct libmnt_fs
*fs
= NULL
;
918 int ntags
= 0, nents
;
922 if (!tb
|| !path
|| !*path
)
924 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
927 DBG(TAB
, ul_debugobj(tb
, "lookup SRCPATH: '%s'", path
));
930 mnt_reset_iter(&itr
, direction
);
931 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
932 if (mnt_fs_streq_srcpath(fs
, path
))
934 if (mnt_fs_get_tag(fs
, NULL
, NULL
) == 0)
938 if (!path
|| !tb
->cache
|| !(cn
= mnt_resolve_path(path
, tb
->cache
)))
941 DBG(TAB
, ul_debugobj(tb
, "lookup canonical SRCPATH: '%s'", cn
));
943 nents
= mnt_table_get_nents(tb
);
945 /* canonicalized paths in struct libmnt_table */
947 mnt_reset_iter(&itr
, direction
);
948 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
949 if (mnt_fs_streq_srcpath(fs
, cn
))
956 int rc
= mnt_cache_read_tags(tb
->cache
, cn
);
958 mnt_reset_iter(&itr
, direction
);
961 /* @path's TAGs are in the cache */
962 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
965 if (mnt_fs_get_tag(fs
, &t
, &v
))
968 if (mnt_cache_device_has_tag(tb
->cache
, cn
, t
, v
))
971 } else if (rc
< 0 && errno
== EACCES
) {
972 /* @path is inaccessible, try evaluating all TAGs in @tb
973 * by udev symlinks -- this could be expensive on systems
974 * with a huge fstab/mtab */
975 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
976 const char *t
, *v
, *x
;
977 if (mnt_fs_get_tag(fs
, &t
, &v
))
979 x
= mnt_resolve_tag(t
, v
, tb
->cache
);
981 /* both canonicalized, strcmp() is fine here */
982 if (x
&& strcmp(x
, cn
) == 0)
988 /* non-canonicalized paths in struct libmnt_table */
989 if (ntags
<= nents
) {
990 mnt_reset_iter(&itr
, direction
);
991 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
992 if (mnt_fs_is_netfs(fs
) || mnt_fs_is_pseudofs(fs
))
994 p
= mnt_fs_get_srcpath(fs
);
996 p
= mnt_resolve_path(p
, tb
->cache
);
998 /* both canonicalized, strcmp() is fine here */
999 if (p
&& strcmp(p
, cn
) == 0)
1009 * mnt_table_find_tag:
1011 * @tag: tag name (e.g "LABEL", "UUID", ...)
1013 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1015 * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and
1016 * @val, for the second attempt the tag is evaluated (converted to the device
1017 * name) and mnt_table_find_srcpath() is performed. The second attempt is not
1018 * performed when @tb cache is not set (see mnt_table_set_cache()).
1020 * Returns: a tab entry or NULL.
1022 struct libmnt_fs
*mnt_table_find_tag(struct libmnt_table
*tb
, const char *tag
,
1023 const char *val
, int direction
)
1025 struct libmnt_iter itr
;
1026 struct libmnt_fs
*fs
= NULL
;
1028 if (!tb
|| !tag
|| !*tag
|| !val
)
1030 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1033 DBG(TAB
, ul_debugobj(tb
, "lookup by TAG: %s %s", tag
, val
));
1035 /* look up by TAG */
1036 mnt_reset_iter(&itr
, direction
);
1037 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1038 if (fs
->tagname
&& fs
->tagval
&&
1039 strcmp(fs
->tagname
, tag
) == 0 &&
1040 strcmp(fs
->tagval
, val
) == 0)
1045 /* look up by device name */
1046 char *cn
= mnt_resolve_tag(tag
, val
, tb
->cache
);
1048 return mnt_table_find_srcpath(tb
, cn
, direction
);
1054 * mnt_table_find_target_with_option:
1056 * @path: mountpoint directory
1057 * @option: option name (e.g "subvol", "subvolid", ...)
1058 * @val: option value or NULL
1059 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1061 * Try to lookup an entry in the given tab that matches combination of @path
1062 * and @option. In difference to mnt_table_find_target(), only @path iteration
1063 * is done. No lookup by device name, no canonicalization.
1065 * Returns: a tab entry or NULL.
1069 struct libmnt_fs
*mnt_table_find_target_with_option(
1070 struct libmnt_table
*tb
, const char *path
,
1071 const char *option
, const char *val
, int direction
)
1073 struct libmnt_iter itr
;
1074 struct libmnt_fs
*fs
= NULL
;
1075 char *optval
= NULL
;
1076 size_t optvalsz
= 0, valsz
= val
? strlen(val
) : 0;
1078 if (!tb
|| !path
|| !*path
|| !option
|| !*option
|| !val
)
1080 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1083 DBG(TAB
, ul_debugobj(tb
, "lookup TARGET: '%s' with OPTION %s %s", path
, option
, val
));
1085 /* look up by native @target with OPTION */
1086 mnt_reset_iter(&itr
, direction
);
1087 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1088 if (mnt_fs_streq_target(fs
, path
)
1089 && mnt_fs_get_option(fs
, option
, &optval
, &optvalsz
) == 0
1090 && (!val
|| (optvalsz
== valsz
1091 && strncmp(optval
, val
, optvalsz
) == 0)))
1098 * mnt_table_find_source:
1100 * @source: TAG or path
1101 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1103 * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care
1104 * about the @source format (device, LABEL, UUID, ...). This function parses
1105 * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath().
1107 * Returns: a tab entry or NULL.
1109 struct libmnt_fs
*mnt_table_find_source(struct libmnt_table
*tb
,
1110 const char *source
, int direction
)
1112 struct libmnt_fs
*fs
;
1113 char *t
= NULL
, *v
= NULL
;
1117 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1120 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: '%s'", source
));
1122 if (blkid_parse_tag_string(source
, &t
, &v
) || !mnt_valid_tagname(t
))
1123 fs
= mnt_table_find_srcpath(tb
, source
, direction
);
1125 fs
= mnt_table_find_tag(tb
, t
, v
, direction
);
1134 * mnt_table_find_pair
1136 * @source: TAG or path
1137 * @target: mountpoint
1138 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1140 * This function is implemented by mnt_fs_match_source() and
1141 * mnt_fs_match_target() functions. It means that this is more expensive than
1142 * others mnt_table_find_* function, because every @tab entry is fully evaluated.
1144 * Returns: a tab entry or NULL.
1146 struct libmnt_fs
*mnt_table_find_pair(struct libmnt_table
*tb
, const char *source
,
1147 const char *target
, int direction
)
1149 struct libmnt_fs
*fs
= NULL
;
1150 struct libmnt_iter itr
;
1152 if (!tb
|| !target
|| !*target
|| !source
|| !*source
)
1154 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1157 DBG(TAB
, ul_debugobj(tb
, "lookup SOURCE: %s TARGET: %s", source
, target
));
1159 mnt_reset_iter(&itr
, direction
);
1160 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1162 if (mnt_fs_match_target(fs
, target
, tb
->cache
) &&
1163 mnt_fs_match_source(fs
, source
, tb
->cache
))
1171 * mnt_table_find_devno
1172 * @tb: /proc/self/mountinfo
1173 * @devno: device number
1174 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1176 * Note that zero could be a valid device number for the root pseudo filesystem (e.g.
1179 * Returns: a tab entry or NULL.
1181 struct libmnt_fs
*mnt_table_find_devno(struct libmnt_table
*tb
,
1182 dev_t devno
, int direction
)
1184 struct libmnt_fs
*fs
= NULL
;
1185 struct libmnt_iter itr
;
1189 if (direction
!= MNT_ITER_FORWARD
&& direction
!= MNT_ITER_BACKWARD
)
1192 DBG(TAB
, ul_debugobj(tb
, "lookup DEVNO: %d", (int) devno
));
1194 mnt_reset_iter(&itr
, direction
);
1196 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1197 if (mnt_fs_get_devno(fs
) == devno
)
1204 static char *remove_mountpoint_from_path(const char *path
, const char *mnt
)
1211 p
= sz
> 1 ? path
+ sz
: path
;
1213 res
= *p
? strdup(p
) : strdup("/");
1214 DBG(UTILS
, ul_debug("%s fs-root is %s", path
, res
));
1218 #ifdef HAVE_BTRFS_SUPPORT
1219 static int get_btrfs_fs_root(struct libmnt_table
*tb
, struct libmnt_fs
*fs
, char **root
)
1221 char *vol
= NULL
, *p
;
1222 size_t sz
, volsz
= 0;
1224 DBG(BTRFS
, ul_debug("lookup for btrfs FS root"));
1227 if (mnt_fs_get_option(fs
, "subvolid", &vol
, &volsz
) == 0) {
1229 struct libmnt_fs
*f
;
1230 char subvolidstr
[sizeof(stringify_value(UINT64_MAX
))];
1232 DBG(BTRFS
, ul_debug(" found subvolid=%s, checking", vol
));
1234 assert (volsz
+ 1 < sizeof(stringify_value(UINT64_MAX
)));
1235 memcpy(subvolidstr
, vol
, volsz
);
1236 subvolidstr
[volsz
] = '\0';
1238 target
= mnt_resolve_target(mnt_fs_get_target(fs
), tb
->cache
);
1242 DBG(BTRFS
, ul_debug(" trying target=%s subvolid=%s", target
, subvolidstr
));
1243 f
= mnt_table_find_target_with_option(tb
, target
,
1244 "subvolid", subvolidstr
,
1251 /* Instead of set of BACKREF queries constructing subvol path
1252 * corresponding to a particular subvolid, use the one in
1253 * mountinfo. Kernel keeps subvol path up to date.
1255 if (mnt_fs_get_option(f
, "subvol", &vol
, &volsz
) != 0)
1258 } else if (mnt_fs_get_option(fs
, "subvol", &vol
, &volsz
) != 0) {
1259 /* If fstab entry does not contain "subvol", we have to
1260 * check, whether btrfs has default subvolume defined.
1262 uint64_t default_id
;
1264 struct libmnt_fs
*f
;
1265 char default_id_str
[sizeof(stringify_value(UINT64_MAX
))];
1267 DBG(BTRFS
, ul_debug(" subvolid/subvol not found, checking default"));
1269 default_id
= btrfs_get_default_subvol_id(mnt_fs_get_target(fs
));
1270 if (default_id
== UINT64_MAX
)
1273 /* Volume has default subvolume. Check if it matches to
1274 * the one in mountinfo.
1276 * Only kernel >= 4.2 reports subvolid. On older
1277 * kernels, there is no reasonable way to detect which
1278 * subvolume was mounted.
1280 target
= mnt_resolve_target(mnt_fs_get_target(fs
), tb
->cache
);
1284 snprintf(default_id_str
, sizeof(default_id_str
), "%llu",
1285 (unsigned long long int) default_id
);
1287 DBG(BTRFS
, ul_debug(" trying target=%s default subvolid=%s",
1288 target
, default_id_str
));
1290 f
= mnt_table_find_target_with_option(tb
, target
,
1291 "subvolid", default_id_str
,
1298 /* Instead of set of BACKREF queries constructing
1299 * subvol path, use the one in mountinfo. Kernel does
1300 * the evaluation for us.
1302 DBG(BTRFS
, ul_debug("setting FS root: btrfs default subvolid = %s",
1305 if (mnt_fs_get_option(f
, "subvol", &vol
, &volsz
) != 0)
1309 DBG(BTRFS
, ul_debug(" using subvol=%s", vol
));
1313 *root
= malloc(sz
+ 1);
1319 memcpy(p
, vol
, volsz
);
1320 *(*root
+ sz
) = '\0';
1324 DBG(BTRFS
, ul_debug(" not found btrfs volume setting"));
1327 DBG(BTRFS
, ul_debug(" error on btrfs volume setting evaluation"));
1328 return errno
? -errno
: -1;
1330 #endif /* HAVE_BTRFS_SUPPORT */
1333 * tb: /proc/self/mountinfo
1335 * mountflags: MS_BIND or 0
1336 * fsroot: fs-root that will probably be used in the mountinfo file
1337 * for @fs after mount(2)
1339 * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
1341 * If @tb is NULL then defaults to '/'.
1343 * Returns: entry from @tb that will be used as a source for @fs if the @fs is
1346 * Don't export to library API!
1348 struct libmnt_fs
*mnt_table_get_fs_root(struct libmnt_table
*tb
,
1349 struct libmnt_fs
*fs
,
1350 unsigned long mountflags
,
1354 const char *mnt
= NULL
;
1356 struct libmnt_fs
*src_fs
= NULL
;
1361 DBG(TAB
, ul_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs
)));
1363 fstype
= mnt_fs_get_fstype(fs
);
1365 if (tb
&& (mountflags
& MS_BIND
)) {
1366 const char *src
, *src_root
;
1369 DBG(TAB
, ul_debug("fs-root for bind"));
1371 src
= xsrc
= mnt_resolve_spec(mnt_fs_get_source(fs
), tb
->cache
);
1373 struct libmnt_fs
*f
= mnt_table_find_mountpoint(tb
,
1374 src
, MNT_ITER_BACKWARD
);
1376 mnt
= mnt_fs_get_target(f
);
1379 root
= remove_mountpoint_from_path(src
, mnt
);
1381 if (xsrc
&& !tb
->cache
) {
1388 src_fs
= mnt_table_find_target(tb
, mnt
, MNT_ITER_BACKWARD
);
1390 DBG(TAB
, ul_debug("not found '%s' in mountinfo -- using default", mnt
));
1394 /* It's possible that fstab_fs source is subdirectory on btrfs
1395 * subvolume or another bind mount. For example:
1397 * /dev/sdc /mnt/test btrfs subvol=/anydir
1398 * /dev/sdc /mnt/test btrfs defaults
1399 * /mnt/test/foo /mnt/test2 auto bind
1401 * in this case, the root for /mnt/test2 will be /anydir/foo on
1402 * /dev/sdc. It means we have to compose the final root from
1403 * root and src_root.
1405 src_root
= mnt_fs_get_root(src_fs
);
1407 DBG(FS
, ul_debugobj(fs
, "source root: %s, source FS root: %s", root
, src_root
));
1409 if (src_root
&& !startswith(root
, src_root
)) {
1410 if (strcmp(root
, "/") == 0) {
1412 root
= strdup(src_root
);
1417 if (asprintf(&tmp
, "%s%s", src_root
, root
) < 0)
1425 #ifdef HAVE_BTRFS_SUPPORT
1427 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
1429 else if (tb
&& fstype
&& (!strcmp(fstype
, "btrfs") || !strcmp(fstype
, "auto"))) {
1430 if (get_btrfs_fs_root(tb
, fs
, &root
) < 0)
1433 #endif /* HAVE_BTRFS_SUPPORT */
1443 DBG(TAB
, ul_debug("FS root result: %s", root
));
1452 * mnt_table_is_fs_mounted:
1453 * @tb: /proc/self/mountinfo file
1454 * @fstab_fs: /etc/fstab entry
1456 * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
1457 * ignored. This function explicitly compares the source, target and root of the
1460 * Note that source and target are canonicalized only if a cache for @tb is
1461 * defined (see mnt_table_set_cache()). The target canonicalization may
1462 * trigger automount on autofs mountpoints!
1464 * Don't use it if you want to know if a device is mounted, just use
1465 * mnt_table_find_source() on the device.
1467 * This function is designed mostly for "mount -a".
1471 int mnt_table_is_fs_mounted(struct libmnt_table
*tb
, struct libmnt_fs
*fstab_fs
)
1473 struct libmnt_iter itr
;
1474 struct libmnt_fs
*fs
;
1477 const char *src
= NULL
, *tgt
= NULL
;
1482 DBG(FS
, ul_debugobj(fstab_fs
, "mnt_table_is_fs_mounted: target=%s, source=%s",
1483 mnt_fs_get_target(fstab_fs
),
1484 mnt_fs_get_source(fstab_fs
)));
1486 if (mnt_fs_is_swaparea(fstab_fs
) || mnt_table_is_empty(tb
)) {
1487 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (swap or no data)"));
1491 if (is_mountinfo(tb
)) {
1492 /* @tb is mountinfo, so we can try to use fs-roots */
1493 struct libmnt_fs
*rootfs
;
1496 if (mnt_fs_get_option(fstab_fs
, "bind", NULL
, NULL
) == 0)
1499 rootfs
= mnt_table_get_fs_root(tb
, fstab_fs
, flags
, &root
);
1501 src
= mnt_fs_get_srcpath(rootfs
);
1505 src
= mnt_fs_get_source(fstab_fs
);
1507 if (src
&& tb
->cache
&& !mnt_fs_is_pseudofs(fstab_fs
))
1508 src
= mnt_resolve_spec(src
, tb
->cache
);
1513 devno
= mnt_fs_get_devno(fstab_fs
);
1514 if (!devno
&& stat(src
, &st
) == 0 && S_ISBLK(st
.st_mode
))
1518 tgt
= mnt_fs_get_target(fstab_fs
);
1521 DBG(FS
, ul_debugobj(fstab_fs
, "- ignore (no source/target)"));
1524 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
1526 DBG(FS
, ul_debugobj(fstab_fs
, "mnt_table_is_fs_mounted: src=%s, tgt=%s, root=%s", src
, tgt
, root
));
1528 while (mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
1530 int eq
= mnt_fs_streq_srcpath(fs
, src
);
1532 if (!eq
&& devno
&& mnt_fs_get_devno(fs
) == devno
)
1536 /* The source does not match. Maybe the source is a loop
1537 * device backing file.
1539 uint64_t offset
= 0;
1544 if (!mnt_fs_get_srcpath(fs
) ||
1545 !startswith(mnt_fs_get_srcpath(fs
), "/dev/loop"))
1546 continue; /* does not look like loopdev */
1548 if (mnt_fs_get_option(fstab_fs
, "offset", &val
, &len
) == 0) {
1549 if (mnt_parse_offset(val
, len
, &offset
)) {
1550 DBG(FS
, ul_debugobj(fstab_fs
, "failed to parse offset="));
1553 flags
= LOOPDEV_FL_OFFSET
;
1556 DBG(FS
, ul_debugobj(fs
, "checking for loop: src=%s", mnt_fs_get_srcpath(fs
)));
1558 if (!loopdev_is_used(mnt_fs_get_srcpath(fs
), src
, offset
, flags
))
1561 DBG(FS
, ul_debugobj(fs
, "used loop"));
1566 const char *r
= mnt_fs_get_root(fs
);
1567 if (!r
|| strcmp(r
, root
) != 0)
1572 * Compare target, try to minimize the number of situations when we
1573 * need to canonicalize the path to avoid readlink() on
1577 if (mnt_fs_streq_target(fs
, tgt
))
1580 xtgt
= mnt_resolve_path(tgt
, tb
->cache
);
1582 if (xtgt
&& mnt_fs_streq_target(fs
, xtgt
))
1587 rc
= 1; /* success */
1591 DBG(TAB
, ul_debugobj(tb
, "mnt_table_is_fs_mounted: %s [rc=%d]", src
, rc
));
1596 #include "pathnames.h"
1598 static int parser_errcb(struct libmnt_table
*tb
, const char *filename
, int line
)
1600 fprintf(stderr
, "%s:%d: parse error\n", filename
, line
);
1602 return 1; /* all errors are recoverable -- this is the default */
1605 static struct libmnt_table
*create_table(const char *file
, int comments
)
1607 struct libmnt_table
*tb
;
1611 tb
= mnt_new_table();
1615 mnt_table_enable_comments(tb
, comments
);
1616 mnt_table_set_parser_errcb(tb
, parser_errcb
);
1618 if (mnt_table_parse_file(tb
, file
) != 0)
1622 fprintf(stderr
, "%s: parsing failed\n", file
);
1623 mnt_unref_table(tb
);
1627 static int test_copy_fs(struct libmnt_test
*ts
, int argc
, char *argv
[])
1629 struct libmnt_table
*tb
;
1630 struct libmnt_fs
*fs
;
1633 tb
= create_table(argv
[1], FALSE
);
1637 fs
= mnt_table_find_target(tb
, "/", MNT_ITER_FORWARD
);
1641 printf("ORIGINAL:\n");
1642 mnt_fs_print_debug(fs
, stdout
);
1644 fs
= mnt_copy_fs(NULL
, fs
);
1649 mnt_fs_print_debug(fs
, stdout
);
1653 mnt_unref_table(tb
);
1657 static int test_parse(struct libmnt_test
*ts
, int argc
, char *argv
[])
1659 struct libmnt_table
*tb
= NULL
;
1660 struct libmnt_iter
*itr
= NULL
;
1661 struct libmnt_fs
*fs
;
1663 int parse_comments
= FALSE
;
1665 if (argc
== 3 && !strcmp(argv
[2], "--comments"))
1666 parse_comments
= TRUE
;
1668 tb
= create_table(argv
[1], parse_comments
);
1672 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
1676 if (mnt_table_get_intro_comment(tb
))
1677 fprintf(stdout
, "Initial comment:\n\"%s\"\n",
1678 mnt_table_get_intro_comment(tb
));
1680 while(mnt_table_next_fs(tb
, itr
, &fs
) == 0)
1681 mnt_fs_print_debug(fs
, stdout
);
1683 if (mnt_table_get_trailing_comment(tb
))
1684 fprintf(stdout
, "Trailing comment:\n\"%s\"\n",
1685 mnt_table_get_trailing_comment(tb
));
1689 mnt_unref_table(tb
);
1693 static int test_find(struct libmnt_test
*ts
, int argc
, char *argv
[], int dr
)
1695 struct libmnt_table
*tb
;
1696 struct libmnt_fs
*fs
= NULL
;
1697 struct libmnt_cache
*mpc
= NULL
;
1698 const char *file
, *find
, *what
;
1702 fprintf(stderr
, "try --help\n");
1706 file
= argv
[1], find
= argv
[2], what
= argv
[3];
1708 tb
= create_table(file
, FALSE
);
1712 /* create a cache for canonicalized paths */
1713 mpc
= mnt_new_cache();
1716 mnt_table_set_cache(tb
, mpc
);
1717 mnt_unref_cache(mpc
);
1719 if (strcasecmp(find
, "source") == 0)
1720 fs
= mnt_table_find_source(tb
, what
, dr
);
1721 else if (strcasecmp(find
, "target") == 0)
1722 fs
= mnt_table_find_target(tb
, what
, dr
);
1725 fprintf(stderr
, "%s: not found %s '%s'\n", file
, find
, what
);
1727 mnt_fs_print_debug(fs
, stdout
);
1731 mnt_unref_table(tb
);
1735 static int test_find_bw(struct libmnt_test
*ts
, int argc
, char *argv
[])
1737 return test_find(ts
, argc
, argv
, MNT_ITER_BACKWARD
);
1740 static int test_find_fw(struct libmnt_test
*ts
, int argc
, char *argv
[])
1742 return test_find(ts
, argc
, argv
, MNT_ITER_FORWARD
);
1745 static int test_find_pair(struct libmnt_test
*ts
, int argc
, char *argv
[])
1747 struct libmnt_table
*tb
;
1748 struct libmnt_fs
*fs
;
1749 struct libmnt_cache
*mpc
= NULL
;
1752 tb
= create_table(argv
[1], FALSE
);
1755 mpc
= mnt_new_cache();
1758 mnt_table_set_cache(tb
, mpc
);
1759 mnt_unref_cache(mpc
);
1761 fs
= mnt_table_find_pair(tb
, argv
[2], argv
[3], MNT_ITER_FORWARD
);
1765 mnt_fs_print_debug(fs
, stdout
);
1768 mnt_unref_table(tb
);
1772 static int test_find_mountpoint(struct libmnt_test
*ts
, int argc
, char *argv
[])
1774 struct libmnt_table
*tb
;
1775 struct libmnt_fs
*fs
;
1776 struct libmnt_cache
*mpc
= NULL
;
1779 tb
= mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
);
1782 mpc
= mnt_new_cache();
1785 mnt_table_set_cache(tb
, mpc
);
1786 mnt_unref_cache(mpc
);
1788 fs
= mnt_table_find_mountpoint(tb
, argv
[1], MNT_ITER_BACKWARD
);
1792 mnt_fs_print_debug(fs
, stdout
);
1795 mnt_unref_table(tb
);
1799 static int test_is_mounted(struct libmnt_test
*ts
, int argc
, char *argv
[])
1801 struct libmnt_table
*tb
= NULL
, *fstab
= NULL
;
1802 struct libmnt_fs
*fs
;
1803 struct libmnt_iter
*itr
= NULL
;
1804 struct libmnt_cache
*mpc
= NULL
;
1806 const char *path
= NULL
;
1808 if (mnt_has_regular_mtab(&path
, &writable
) == 1 && writable
== 0)
1809 tb
= mnt_new_table_from_file(path
);
1811 tb
= mnt_new_table_from_file("/proc/self/mountinfo");
1814 fprintf(stderr
, "failed to parse mountinfo\n");
1818 fstab
= create_table(argv
[1], FALSE
);
1822 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
1826 mpc
= mnt_new_cache();
1829 mnt_table_set_cache(tb
, mpc
);
1830 mnt_unref_cache(mpc
);
1832 while (mnt_table_next_fs(fstab
, itr
, &fs
) == 0) {
1833 if (mnt_table_is_fs_mounted(tb
, fs
))
1834 printf("%s already mounted on %s\n",
1835 mnt_fs_get_source(fs
),
1836 mnt_fs_get_target(fs
));
1838 printf("%s not mounted on %s\n",
1839 mnt_fs_get_source(fs
),
1840 mnt_fs_get_target(fs
));
1844 mnt_unref_table(tb
);
1845 mnt_unref_table(fstab
);
1850 /* returns 0 if @a and @b targets are the same */
1851 static int test_uniq_cmp(struct libmnt_table
*tb
__attribute__((__unused__
)),
1852 struct libmnt_fs
*a
,
1853 struct libmnt_fs
*b
)
1858 return mnt_fs_streq_target(a
, mnt_fs_get_target(b
)) ? 0 : 1;
1861 static int test_uniq(struct libmnt_test
*ts
, int argc
, char *argv
[])
1863 struct libmnt_table
*tb
;
1867 fprintf(stderr
, "try --help\n");
1871 tb
= create_table(argv
[1], FALSE
);
1875 if (mnt_table_uniq_fs(tb
, 0, test_uniq_cmp
) == 0) {
1876 struct libmnt_iter
*itr
= mnt_new_iter(MNT_ITER_FORWARD
);
1877 struct libmnt_fs
*fs
;
1880 while (mnt_table_next_fs(tb
, itr
, &fs
) == 0)
1881 mnt_fs_print_debug(fs
, stdout
);
1886 mnt_unref_table(tb
);
1891 int main(int argc
, char *argv
[])
1893 struct libmnt_test tss
[] = {
1894 { "--parse", test_parse
, "<file> [--comments] parse and print tab" },
1895 { "--find-forward", test_find_fw
, "<file> <source|target> <string>" },
1896 { "--find-backward", test_find_bw
, "<file> <source|target> <string>" },
1897 { "--uniq-target", test_uniq
, "<file>" },
1898 { "--find-pair", test_find_pair
, "<file> <source> <target>" },
1899 { "--find-mountpoint", test_find_mountpoint
, "<path>" },
1900 { "--copy-fs", test_copy_fs
, "<file> copy root FS from the file" },
1901 { "--is-mounted", test_is_mounted
, "<fstab> check what from fstab is already mounted" },
1905 return mnt_run_test(tss
, argc
, argv
);
1908 #endif /* TEST_PROGRAM */