]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/tab.c
scriptreplay: fix uninitialized value [coverity scan]
[thirdparty/util-linux.git] / libmount / src / tab.c
1
2 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 /*
4 * This file is part of libmount from util-linux project.
5 *
6 * Copyright (C) 2008-2018 Karel Zak <kzak@redhat.com>
7 *
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.
12 */
13
14 /**
15 * SECTION: table
16 * @title: Table of filesystems
17 * @short_description: container for entries from fstab, mtab or mountinfo
18 *
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:
23 * <informalexample>
24 * <programlisting>
25 * LABEL=foo /foo auto rw
26 * /dev/foo /foo auto rw
27 * </programlisting>
28 * </informalexample>
29 *
30 * where both lines are used for the *same* device, then
31 * <informalexample>
32 * <programlisting>
33 * mnt_table_find_source(tb, "/dev/foo", &fs);
34 * </programlisting>
35 * </informalexample>
36 * will returns the second line, and
37 * <informalexample>
38 * <programlisting>
39 * mnt_table_find_source(tb, "LABEL=foo", &fs);
40 * </programlisting>
41 * </informalexample>
42 * will returns the first entry, and
43 * <informalexample>
44 * <programlisting>
45 * mnt_table_find_source(tb, "UUID=anyuuid", &fs);
46 * </programlisting>
47 * </informalexample>
48 * will return the first entry (if UUID matches with the device).
49 */
50 #include <blkid.h>
51
52 #include "mountP.h"
53 #include "strutils.h"
54 #include "loopdev.h"
55 #include "fileutils.h"
56 #include "canonicalize.h"
57
58 int is_mountinfo(struct libmnt_table *tb)
59 {
60 struct libmnt_fs *fs;
61
62 if (!tb)
63 return 0;
64
65 fs = list_first_entry(&tb->ents, struct libmnt_fs, ents);
66 if (fs && mnt_fs_is_kernel(fs) && mnt_fs_get_root(fs))
67 return 1;
68
69 return 0;
70 }
71
72 /**
73 * mnt_new_table:
74 *
75 * The tab is a container for struct libmnt_fs entries that usually represents a fstab,
76 * mtab or mountinfo file from your system.
77 *
78 * See also mnt_table_parse_file().
79 *
80 * Returns: newly allocated tab struct.
81 */
82 struct libmnt_table *mnt_new_table(void)
83 {
84 struct libmnt_table *tb = NULL;
85
86 tb = calloc(1, sizeof(*tb));
87 if (!tb)
88 return NULL;
89
90 DBG(TAB, ul_debugobj(tb, "alloc"));
91 tb->refcount = 1;
92 INIT_LIST_HEAD(&tb->ents);
93 return tb;
94 }
95
96 /**
97 * mnt_reset_table:
98 * @tb: tab pointer
99 *
100 * Removes all entries (filesystems) from the table. The filesystems with zero
101 * reference count will be deallocated.
102 *
103 * Returns: 0 on success or negative number in case of error.
104 */
105 int mnt_reset_table(struct libmnt_table *tb)
106 {
107 if (!tb)
108 return -EINVAL;
109
110 DBG(TAB, ul_debugobj(tb, "reset"));
111
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);
116 }
117
118 tb->nents = 0;
119 return 0;
120 }
121
122 /**
123 * mnt_ref_table:
124 * @tb: table pointer
125 *
126 * Increments reference counter.
127 */
128 void mnt_ref_table(struct libmnt_table *tb)
129 {
130 if (tb) {
131 tb->refcount++;
132 /*DBG(FS, ul_debugobj(tb, "ref=%d", tb->refcount));*/
133 }
134 }
135
136 /**
137 * mnt_unref_table:
138 * @tb: table pointer
139 *
140 * De-increments reference counter, on zero the @tb is automatically
141 * deallocated by mnt_free_table().
142 */
143 void mnt_unref_table(struct libmnt_table *tb)
144 {
145 if (tb) {
146 tb->refcount--;
147 /*DBG(FS, ul_debugobj(tb, "unref=%d", tb->refcount));*/
148 if (tb->refcount <= 0)
149 mnt_free_table(tb);
150 }
151 }
152
153
154 /**
155 * mnt_free_table:
156 * @tb: tab pointer
157 *
158 * Deallocates the table. This function does not care about reference count. Don't
159 * use this function directly -- it's better to use mnt_unref_table().
160 *
161 * The table entries (filesystems) are unreferenced by mnt_reset_table() and
162 * cache by mnt_unref_cache().
163 */
164 void mnt_free_table(struct libmnt_table *tb)
165 {
166 if (!tb)
167 return;
168
169 mnt_reset_table(tb);
170 DBG(TAB, ul_debugobj(tb, "free [refcount=%d]", tb->refcount));
171
172 mnt_unref_cache(tb->cache);
173 free(tb->comm_intro);
174 free(tb->comm_tail);
175 free(tb);
176 }
177
178 /**
179 * mnt_table_get_nents:
180 * @tb: pointer to tab
181 *
182 * Returns: number of entries in table.
183 */
184 int mnt_table_get_nents(struct libmnt_table *tb)
185 {
186 return tb ? tb->nents : 0;
187 }
188
189 /**
190 * mnt_table_is_empty:
191 * @tb: pointer to tab
192 *
193 * Returns: 1 if the table is without filesystems, or 0.
194 */
195 int mnt_table_is_empty(struct libmnt_table *tb)
196 {
197 return tb == NULL || list_empty(&tb->ents) ? 1 : 0;
198 }
199
200 /**
201 * mnt_table_set_userdata:
202 * @tb: pointer to tab
203 * @data: pointer to user data
204 *
205 * Sets pointer to the private user data.
206 *
207 * Returns: 0 on success or negative number in case of error.
208 */
209 int mnt_table_set_userdata(struct libmnt_table *tb, void *data)
210 {
211 if (!tb)
212 return -EINVAL;
213
214 tb->userdata = data;
215 return 0;
216 }
217
218 /**
219 * mnt_table_get_userdata:
220 * @tb: pointer to tab
221 *
222 * Returns: pointer to user's data.
223 */
224 void *mnt_table_get_userdata(struct libmnt_table *tb)
225 {
226 return tb ? tb->userdata : NULL;
227 }
228
229 /**
230 * mnt_table_enable_comments:
231 * @tb: pointer to tab
232 * @enable: TRUE or FALSE
233 *
234 * Enables parsing of comments.
235 *
236 * The initial (intro) file comment is accessible by
237 * mnt_table_get_intro_comment(). The intro and the comment of the first fstab
238 * entry has to be separated by blank line. The filesystem comments are
239 * accessible by mnt_fs_get_comment(). The trailing fstab comment is accessible
240 * by mnt_table_get_trailing_comment().
241 *
242 * <informalexample>
243 * <programlisting>
244 * #
245 * # Intro comment
246 * #
247 *
248 * # this comments belongs to the first fs
249 * LABEL=foo /mnt/foo auto defaults 1 2
250 * # this comments belongs to the second fs
251 * LABEL=bar /mnt/bar auto defaults 1 2
252 * # tailing comment
253 * </programlisting>
254 * </informalexample>
255 */
256 void mnt_table_enable_comments(struct libmnt_table *tb, int enable)
257 {
258 if (tb)
259 tb->comms = enable;
260 }
261
262 /**
263 * mnt_table_with_comments:
264 * @tb: pointer to table
265 *
266 * Returns: 1 if comments parsing is enabled, or 0.
267 */
268 int mnt_table_with_comments(struct libmnt_table *tb)
269 {
270 assert(tb);
271 return tb ? tb->comms : 0;
272 }
273
274 /**
275 * mnt_table_get_intro_comment:
276 * @tb: pointer to tab
277 *
278 * Returns: initial comment in tb
279 */
280 const char *mnt_table_get_intro_comment(struct libmnt_table *tb)
281 {
282 return tb ? tb->comm_intro : NULL;
283 }
284
285 /**
286 * mnt_table_set_into_comment:
287 * @tb: pointer to tab
288 * @comm: comment or NULL
289 *
290 * Sets the initial comment in tb.
291 *
292 * Returns: 0 on success or negative number in case of error.
293 */
294 int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm)
295 {
296 return strdup_to_struct_member(tb, comm_intro, comm);
297 }
298
299 /**
300 * mnt_table_append_into_comment:
301 * @tb: pointer to tab
302 * @comm: comment of NULL
303 *
304 * Appends the initial comment in tb.
305 *
306 * Returns: 0 on success or negative number in case of error.
307 */
308 int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm)
309 {
310 if (!tb)
311 return -EINVAL;
312 return strappend(&tb->comm_intro, comm);
313 }
314
315 /**
316 * mnt_table_get_trailing_comment:
317 * @tb: pointer to tab
318 *
319 * Returns: table trailing comment
320 */
321 const char *mnt_table_get_trailing_comment(struct libmnt_table *tb)
322 {
323 return tb ? tb->comm_tail : NULL;
324 }
325
326 /**
327 * mnt_table_set_trailing_comment
328 * @tb: pointer to tab
329 * @comm: comment string
330 *
331 * Sets the trailing comment in table.
332 *
333 * Returns: 0 on success or negative number in case of error.
334 */
335 int mnt_table_set_trailing_comment(struct libmnt_table *tb, const char *comm)
336 {
337 return strdup_to_struct_member(tb, comm_tail, comm);
338 }
339
340 /**
341 * mnt_table_append_trailing_comment:
342 * @tb: pointer to tab
343 * @comm: comment of NULL
344 *
345 * Appends to the trailing table comment.
346 *
347 * Returns: 0 on success or negative number in case of error.
348 */
349 int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm)
350 {
351 if (!tb)
352 return -EINVAL;
353 return strappend(&tb->comm_tail, comm);
354 }
355
356 /**
357 * mnt_table_set_cache:
358 * @tb: pointer to tab
359 * @mpc: pointer to struct libmnt_cache instance
360 *
361 * Sets up a cache for canonicalized paths and evaluated tags (LABEL/UUID). The
362 * cache is recommended for mnt_table_find_*() functions.
363 *
364 * The cache could be shared between more tabs. Be careful when you share the
365 * same cache between more threads -- currently the cache does not provide any
366 * locking method.
367 *
368 * This function increments cache reference counter. It's recommended to use
369 * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache
370 * referenced by @tb only.
371 *
372 * See also mnt_new_cache().
373 *
374 * Returns: 0 on success or negative number in case of error.
375 */
376 int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc)
377 {
378 if (!tb)
379 return -EINVAL;
380
381 mnt_ref_cache(mpc); /* new */
382 mnt_unref_cache(tb->cache); /* old */
383 tb->cache = mpc;
384 return 0;
385 }
386
387 /**
388 * mnt_table_get_cache:
389 * @tb: pointer to tab
390 *
391 * Returns: pointer to struct libmnt_cache instance or NULL.
392 */
393 struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb)
394 {
395 return tb ? tb->cache : NULL;
396 }
397
398 /**
399 * mnt_table_find_fs:
400 * @tb: tab pointer
401 * @fs: entry to look for
402 *
403 * Checks if @fs is part of table @tb.
404 *
405 * Returns: index of @fs in table, 0 if not found or negative number in case of error.
406 *
407 * Since: 2.34
408 */
409 int mnt_table_find_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
410 {
411 struct list_head *p;
412 int i = 0;
413
414 if (!tb || !fs)
415 return -EINVAL;
416
417 if (list_empty(&fs->ents))
418 return 0;
419
420 /* Let's use directly list rather than mnt_table_next_fs() as we
421 * compare list entry with fs only.
422 */
423 list_for_each(p, &tb->ents) {
424 ++i;
425 if (list_entry(p, struct libmnt_fs, ents) == fs)
426 return i;
427 }
428
429 return 0;
430 }
431
432 /**
433 * mnt_table_add_fs:
434 * @tb: tab pointer
435 * @fs: new entry
436 *
437 * Adds a new entry to tab and increment @fs reference counter. Don't forget to
438 * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs
439 * referenced by the table only.
440 *
441 * Returns: 0 on success or negative number in case of error.
442 */
443 int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
444 {
445 if (!tb || !fs)
446 return -EINVAL;
447
448 if (fs->tab)
449 return -EBUSY;
450
451 mnt_ref_fs(fs);
452 list_add_tail(&fs->ents, &tb->ents);
453 fs->tab = tb;
454 tb->nents++;
455
456 DBG(TAB, ul_debugobj(tb, "add entry: %s %s",
457 mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
458 return 0;
459 }
460
461 static int __table_insert_fs(
462 struct libmnt_table *tb, int before,
463 struct libmnt_fs *pos, struct libmnt_fs *fs)
464 {
465 struct list_head *head = pos ? &pos->ents : &tb->ents;
466
467 if (before)
468 list_add(&fs->ents, head);
469 else
470 list_add_tail(&fs->ents, head);
471
472 fs->tab = tb;
473 tb->nents++;
474
475 DBG(TAB, ul_debugobj(tb, "insert entry: %s %s",
476 mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
477 return 0;
478 }
479
480 /**
481 * mnt_table_insert_fs:
482 * @tb: tab pointer
483 * @before: 1 to insert before pos, 0 to insert after pos
484 * @pos: entry to specify position or NULL
485 * @fs: new entry
486 *
487 * Adds a new entry to @tb before or after a specific table entry @pos. If the
488 * @pos is NULL than add the begin of the @tab if @before is 1; or to the tail
489 * of the @tb if @before is 0.
490 *
491 * This function increments reference to @fs. Don't forget to use
492 * mnt_unref_fs() after mnt_table_insert_fs() if you want to keep the @fs
493 * referenced by the table only.
494 *
495 * Returns: 0 on success or negative number in case of error.
496 *
497 * Since: 2.34
498 */
499 int mnt_table_insert_fs(struct libmnt_table *tb, int before,
500 struct libmnt_fs *pos, struct libmnt_fs *fs)
501 {
502 if (!tb || !fs)
503 return -EINVAL;
504
505 if (fs->tab)
506 return -EBUSY;
507
508 if (pos && pos->tab != tb)
509 return -ENOENT;
510
511 mnt_ref_fs(fs);
512 return __table_insert_fs(tb, before, pos, fs);
513 }
514
515 /**
516 * mnt_table_move_fs:
517 * @src: tab pointer of source table
518 * @dst: tab pointer of destination table
519 * @before: 1 to move before position, 0 to move after position
520 * @pos: entry to specify position or NULL
521 * @fs: entry to move
522 *
523 * Removes @fs from @src table and adds it before/after a specific entry @pos
524 * of @dst table. If the @pos is NULL than add the begin of the @dst if @before
525 * is 1; or to the tail of the @dst if @before is 0.
526 *
527 * The reference counter of @fs is not modified.
528 *
529 * Returns: 0 on success or negative number in case of error.
530 *
531 * Since: 2.34
532 */
533 int mnt_table_move_fs(struct libmnt_table *src, struct libmnt_table *dst,
534 int before, struct libmnt_fs *pos, struct libmnt_fs *fs)
535 {
536 if (!src || !dst || !fs)
537 return -EINVAL;
538
539 if (fs->tab != src || (pos && pos->tab != dst))
540 return -ENOENT;
541
542 /* remove from source */
543 list_del_init(&fs->ents);
544 src->nents--;
545
546 /* insert to the destination */
547 return __table_insert_fs(dst, before, pos, fs);
548 }
549
550
551 /**
552 * mnt_table_remove_fs:
553 * @tb: tab pointer
554 * @fs: new entry
555 *
556 * Removes the @fs from the table and de-increment reference counter of the @fs. The
557 * filesystem with zero reference counter will be deallocated. Don't forget to use
558 * mnt_ref_fs() before call mnt_table_remove_fs() if you want to use @fs later.
559 *
560 * Returns: 0 on success or negative number in case of error.
561 */
562 int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
563 {
564 if (!tb || !fs || fs->tab != tb)
565 return -EINVAL;
566
567 fs->tab = NULL;
568 list_del_init(&fs->ents);
569
570 mnt_unref_fs(fs);
571 tb->nents--;
572 return 0;
573 }
574
575 static inline struct libmnt_fs *get_parent_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
576 {
577 struct libmnt_iter itr;
578 struct libmnt_fs *x;
579 int parent_id = mnt_fs_get_parent_id(fs);
580
581 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
582 while (mnt_table_next_fs(tb, &itr, &x) == 0) {
583 if (mnt_fs_get_id(x) == parent_id)
584 return x;
585 }
586
587 return NULL;
588 }
589
590 /**
591 * mnt_table_get_root_fs:
592 * @tb: mountinfo file (/proc/self/mountinfo)
593 * @root: NULL or returns pointer to the root filesystem (/)
594 *
595 * The function uses the parent ID from the mountinfo file to determine the
596 * root filesystem (the filesystem with the smallest ID with parent ID missing
597 * in the table). The function is designed mostly for applications where it is
598 * necessary to sort mountpoints by IDs to get the tree of the mountpoints
599 * (e.g. findmnt default output).
600 *
601 * If you're not sure, then use
602 *
603 * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD);
604 *
605 * this is more robust and usable for arbitrary tab files (including fstab).
606 *
607 * Returns: 0 on success or negative number in case of error.
608 */
609 int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root)
610 {
611 struct libmnt_iter itr;
612 struct libmnt_fs *fs, *root_fs = NULL;
613 int root_id = 0;
614
615 if (!tb || !is_mountinfo(tb))
616 return -EINVAL;
617
618 DBG(TAB, ul_debugobj(tb, "lookup root fs"));
619
620 /* get smallest possible ID from the table */
621 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
622 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
623 int id = mnt_fs_get_parent_id(fs);
624
625 if (!root_fs || id < root_id) {
626 root_fs = fs;
627 root_id = id;
628 }
629 }
630
631 /* go to the root node by "parent_id -> id" relation */
632 while (root_fs) {
633 struct libmnt_fs *x = get_parent_fs(tb, root_fs);
634 if (!x || x == root_fs)
635 break;
636 DBG(TAB, ul_debugobj(tb, " messy mountinfo, walk to %s", mnt_fs_get_target(x)));
637 root_fs = x;
638 }
639
640 if (root)
641 *root = root_fs;
642
643 return root_fs ? 0 : -EINVAL;
644 }
645
646 /**
647 * mnt_table_next_child_fs:
648 * @tb: mountinfo file (/proc/self/mountinfo)
649 * @itr: iterator
650 * @parent: parental FS
651 * @chld: NULL or returns the next child filesystem
652 *
653 * Since version 2.40, the filesystems are returned in the order specified by
654 * @itr. In the old versions the derection is always MNT_ITER_FORWARD.
655 *
656 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
657 */
658 int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
659 struct libmnt_fs *parent, struct libmnt_fs **chld)
660 {
661 struct libmnt_fs *fs, *chfs = NULL;
662 int parent_id, lastchld_id = 0, chld_id = 0;
663 int direction;
664
665 if (!tb || !itr || !parent || !is_mountinfo(tb))
666 return -EINVAL;
667
668 DBG(TAB, ul_debugobj(tb, "lookup next child of '%s'",
669 mnt_fs_get_target(parent)));
670 parent_id = mnt_fs_get_id(parent);
671 direction = mnt_iter_get_direction(itr);
672
673 /* get ID of the previously returned child */
674 if (itr->head && itr->p != itr->head) {
675 fs = MNT_ITER_GET_ENTRY(itr, struct libmnt_fs, ents);
676 MNT_ITER_ITERATE(itr);
677 lastchld_id = mnt_fs_get_id(fs);
678 }
679
680 mnt_reset_iter(itr, direction);
681 while (mnt_table_next_fs(tb, itr, &fs) == 0) {
682 int id;
683
684 if (mnt_fs_get_parent_id(fs) != parent_id)
685 continue;
686
687 id = mnt_fs_get_id(fs);
688
689 /* avoid an infinite loop. This only happens in rare cases
690 * such as in early userspace when the rootfs is its own parent */
691 if (id == parent_id)
692 continue;
693
694 if (direction == MNT_ITER_FORWARD) {
695 /* return in the order of mounting */
696 if ((!lastchld_id || id > lastchld_id) &&
697 (!chfs || id < chld_id)) {
698 chfs = fs;
699 chld_id = id;
700 }
701 } else {
702 /* return last child first */
703 if ((!lastchld_id || id < lastchld_id) &&
704 (!chfs || id > chld_id)) {
705 chfs = fs;
706 chld_id = id;
707 }
708 }
709 }
710
711 if (chld)
712 *chld = chfs;
713 if (!chfs)
714 return 1; /* end of iterator */
715
716 /* set the iterator to the @chfs for the next call */
717 mnt_table_set_iter(tb, itr, chfs);
718
719 return 0;
720 }
721
722 /**
723 * mnt_table_over_fs:
724 * @tb: tab pointer
725 * @parent: pointer to parental FS
726 * @child: returns pointer to FS which over-mounting parent (optional)
727 *
728 * This function returns by @child the first filesystenm which is over-mounted
729 * on @parent. It means the mountpoint of @child is the same as for @parent and
730 * parent->id is the same as child->parent_id.
731 *
732 * Note that you need to call this function in loop until it returns 1 to get
733 * the highest filesystem.
734 *
735 * Example:
736 * <informalexample>
737 * <programlisting>
738 * while (mnt_table_over_fs(tb, cur, &over) == 0) {
739 * printf("%s overmounted by %d\n", mnt_fs_get_target(cur), mnt_fs_get_id(over));
740 * cur = over;
741 * }
742 * </programlisting>
743 * </informalexample>
744 *
745 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
746 */
747 int mnt_table_over_fs(struct libmnt_table *tb, struct libmnt_fs *parent,
748 struct libmnt_fs **child)
749 {
750 struct libmnt_iter itr;
751 struct libmnt_fs *fs = NULL;
752 int id;
753 const char *tgt;
754
755 if (!tb || !parent || !is_mountinfo(tb))
756 return -EINVAL;
757
758 if (child)
759 *child = NULL;
760
761 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
762 id = mnt_fs_get_id(parent);
763 tgt = mnt_fs_get_target(parent);
764
765 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
766 if (mnt_fs_get_parent_id(fs) == id &&
767 mnt_fs_streq_target(fs, tgt) == 1) {
768 if (child)
769 *child = fs;
770 return 0;
771 }
772 }
773
774 return 1; /* nothing */
775 }
776
777 /**
778 * mnt_table_next_fs:
779 * @tb: tab pointer
780 * @itr: iterator
781 * @fs: NULL or returns the next tab entry
782 *
783 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
784 *
785 * Example:
786 * <informalexample>
787 * <programlisting>
788 * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
789 * const char *dir = mnt_fs_get_target(fs);
790 * printf("mount point: %s\n", dir);
791 * }
792 * </programlisting>
793 * </informalexample>
794 *
795 * lists all mountpoints from fstab in reverse order.
796 */
797 int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs)
798 {
799 int rc = 1;
800
801 if (!tb || !itr)
802 return -EINVAL;
803 if (fs)
804 *fs = NULL;
805
806 if (!itr->head)
807 MNT_ITER_INIT(itr, &tb->ents);
808 if (itr->p != itr->head) {
809 if (fs)
810 *fs = MNT_ITER_GET_ENTRY(itr, struct libmnt_fs, ents);
811 MNT_ITER_ITERATE(itr);
812 rc = 0;
813 }
814
815 return rc;
816 }
817
818 /**
819 * mnt_table_first_fs:
820 * @tb: tab pointer
821 * @fs: NULL or returns the first tab entry
822 *
823 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
824 */
825 int mnt_table_first_fs(struct libmnt_table *tb, struct libmnt_fs **fs)
826 {
827 if (!tb)
828 return -EINVAL;
829 if (list_empty(&tb->ents))
830 return 1;
831 if (fs)
832 *fs = list_first_entry(&tb->ents, struct libmnt_fs, ents);
833 return 0;
834 }
835
836 /**
837 * mnt_table_last_fs:
838 * @tb: tab pointer
839 * @fs: NULL or returns the last tab entry
840 *
841 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
842 */
843 int mnt_table_last_fs(struct libmnt_table *tb, struct libmnt_fs **fs)
844 {
845 if (!tb)
846 return -EINVAL;
847 if (list_empty(&tb->ents))
848 return 1;
849 if (fs)
850 *fs = list_last_entry(&tb->ents, struct libmnt_fs, ents);
851 return 0;
852 }
853
854 /**
855 * mnt_table_find_next_fs:
856 * @tb: table
857 * @itr: iterator
858 * @match_func: function returning 1 or 0
859 * @userdata: extra data for match_func
860 * @fs: NULL or returns pointer to the next matching table entry
861 *
862 * This function allows searching in @tb.
863 *
864 * Returns: negative number in case of error, 1 at end of table or 0 o success.
865 */
866 int mnt_table_find_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
867 int (*match_func)(struct libmnt_fs *, void *), void *userdata,
868 struct libmnt_fs **fs)
869 {
870 struct libmnt_fs *re = NULL;
871 int match = 0;
872
873 if (!tb || !itr || !match_func)
874 return -EINVAL;
875
876 DBG(TAB, ul_debugobj(tb, "lookup next fs"));
877
878 if (fs)
879 *fs = NULL;
880 if (!itr->head)
881 MNT_ITER_INIT(itr, &tb->ents);
882
883 while (!match) {
884 if (itr->p != itr->head) {
885 re = MNT_ITER_GET_ENTRY(itr, struct libmnt_fs, ents);
886 MNT_ITER_ITERATE(itr);
887 } else
888 return 1; /*end */
889
890 match = match_func(re, userdata);
891 }
892
893 if (fs)
894 *fs = re;
895 return 0;
896 }
897
898 static int mnt_table_move_parent(struct libmnt_table *tb, int oldid, int newid)
899 {
900 struct libmnt_iter itr;
901 struct libmnt_fs *fs;
902
903 if (!tb)
904 return -EINVAL;
905 if (list_empty(&tb->ents))
906 return 0;
907
908 DBG(TAB, ul_debugobj(tb, "moving parent ID from %d -> %d", oldid, newid));
909 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
910
911 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
912 if (fs->parent == oldid)
913 fs->parent = newid;
914 }
915 return 0;
916 }
917
918 /**
919 * mnt_table_uniq_fs:
920 * @tb: table
921 * @flags: MNT_UNIQ_*
922 * @cmp: function to compare filesystems
923 *
924 * This function de-duplicate the @tb, but does not change order of the
925 * filesystems. The @cmp function has to return 0 if the filesystems are
926 * equal, otherwise non-zero.
927 *
928 * The default is to keep in the table later mounted filesystems (function uses
929 * backward mode iterator).
930 *
931 * @MNT_UNIQ_FORWARD: remove later mounted filesystems
932 * @MNT_UNIQ_KEEPTREE: keep parent->id relationship still valid
933 *
934 * Returns: negative number in case of error, or 0 o success.
935 */
936 int mnt_table_uniq_fs(struct libmnt_table *tb, int flags,
937 int (*cmp)(struct libmnt_table *,
938 struct libmnt_fs *,
939 struct libmnt_fs *))
940 {
941 struct libmnt_iter itr;
942 struct libmnt_fs *fs;
943 int direction = MNT_ITER_BACKWARD;
944
945 if (!tb || !cmp)
946 return -EINVAL;
947 if (list_empty(&tb->ents))
948 return 0;
949
950 if (flags & MNT_UNIQ_FORWARD)
951 direction = MNT_ITER_FORWARD;
952
953 DBG(TAB, ul_debugobj(tb, "de-duplicate"));
954 mnt_reset_iter(&itr, direction);
955
956 if ((flags & MNT_UNIQ_KEEPTREE) && !is_mountinfo(tb))
957 flags &= ~MNT_UNIQ_KEEPTREE;
958
959 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
960 int want = 1;
961 struct libmnt_iter xtr;
962 struct libmnt_fs *x;
963
964 mnt_reset_iter(&xtr, direction);
965 while (want && mnt_table_next_fs(tb, &xtr, &x) == 0) {
966 if (fs == x)
967 break;
968 want = cmp(tb, x, fs) != 0;
969 }
970
971 if (!want) {
972 if (flags & MNT_UNIQ_KEEPTREE)
973 mnt_table_move_parent(tb, mnt_fs_get_id(fs),
974 mnt_fs_get_parent_id(fs));
975
976 DBG(TAB, ul_debugobj(tb, "remove duplicate %s",
977 mnt_fs_get_target(fs)));
978 mnt_table_remove_fs(tb, fs);
979 }
980 }
981
982 return 0;
983 }
984
985 /**
986 * mnt_table_set_iter:
987 * @tb: tab pointer
988 * @itr: iterator
989 * @fs: tab entry
990 *
991 * Sets @iter to the position of @fs in the file @tb.
992 *
993 * Returns: 0 on success, negative number in case of error.
994 */
995 int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs)
996 {
997 if (!tb || !itr || !fs)
998 return -EINVAL;
999
1000 if (fs->tab != tb)
1001 return -ENOENT;
1002
1003 MNT_ITER_INIT(itr, &tb->ents);
1004 itr->p = &fs->ents;
1005
1006 return 0;
1007 }
1008
1009 /**
1010 * mnt_table_find_mountpoint:
1011 * @tb: tab pointer
1012 * @path: directory
1013 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1014 *
1015 * Same as mnt_get_mountpoint(), except this function does not rely on
1016 * st_dev numbers.
1017 *
1018 * Returns: a tab entry or NULL.
1019 */
1020 struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb,
1021 const char *path,
1022 int direction)
1023 {
1024 char *mnt;
1025
1026 if (!tb || !path || !*path)
1027 return NULL;
1028 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1029 return NULL;
1030
1031 DBG(TAB, ul_debugobj(tb, "lookup MOUNTPOINT: '%s'", path));
1032
1033 if (!mnt_is_path(path))
1034 return NULL;
1035
1036 mnt = strdup(path);
1037 if (!mnt)
1038 return NULL;
1039
1040 do {
1041 char *p;
1042 struct libmnt_fs *fs;
1043
1044 fs = mnt_table_find_target(tb, mnt, direction);
1045 if (fs) {
1046 free(mnt);
1047 return fs;
1048 }
1049
1050 p = stripoff_last_component(mnt);
1051 if (!p)
1052 break;
1053 } while (mnt && *(mnt + 1) != '\0');
1054
1055 free(mnt);
1056 return mnt_table_find_target(tb, "/", direction);
1057 }
1058
1059 /**
1060 * mnt_table_find_target:
1061 * @tb: tab pointer
1062 * @path: mountpoint directory
1063 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1064 *
1065 * Try to lookup an entry in the given tab, three iterations are possible, the first
1066 * with @path, the second with realpath(@path) and the third with realpath(@path)
1067 * against realpath(fs->target). The 2nd and 3rd iterations are not performed when
1068 * the @tb cache is not set (see mnt_table_set_cache()). If
1069 * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any
1070 * @fs->target found in @mtab (see mnt_resolve_target()).
1071 *
1072 * Returns: a tab entry or NULL.
1073 */
1074 struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *path, int direction)
1075 {
1076 struct libmnt_iter itr;
1077 struct libmnt_fs *fs = NULL;
1078 char *cn;
1079
1080 if (!tb || !path || !*path)
1081 return NULL;
1082 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1083 return NULL;
1084
1085 DBG(TAB, ul_debugobj(tb, "lookup TARGET: '%s'", path));
1086
1087 /* native @target */
1088 mnt_reset_iter(&itr, direction);
1089 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1090 if (mnt_fs_streq_target(fs, path))
1091 return fs;
1092 }
1093
1094 /* try absolute path */
1095 if (is_relative_path(path) && (cn = absolute_path(path))) {
1096 DBG(TAB, ul_debugobj(tb, "lookup absolute TARGET: '%s'", cn));
1097 mnt_reset_iter(&itr, direction);
1098 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
1099 if (mnt_fs_streq_target(fs, cn)) {
1100 free(cn);
1101 return fs;
1102 }
1103 }
1104 free(cn);
1105 }
1106
1107 if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
1108 return NULL;
1109
1110 DBG(TAB, ul_debugobj(tb, "lookup canonical TARGET: '%s'", cn));
1111
1112 /* canonicalized paths in struct libmnt_table */
1113 mnt_reset_iter(&itr, direction);
1114 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1115 if (mnt_fs_streq_target(fs, cn))
1116 return fs;
1117 }
1118
1119 /* non-canonical path in struct libmnt_table
1120 * -- note that mountpoint in /proc/self/mountinfo is already
1121 * canonicalized by the kernel
1122 */
1123 mnt_reset_iter(&itr, direction);
1124 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1125 char *p;
1126
1127 if (!fs->target
1128 || mnt_fs_is_swaparea(fs)
1129 || mnt_fs_is_kernel(fs)
1130 || (*fs->target == '/' && *(fs->target + 1) == '\0'))
1131 continue;
1132
1133 p = mnt_resolve_target(fs->target, tb->cache);
1134 /* both canonicalized, strcmp() is fine here */
1135 if (p && strcmp(cn, p) == 0)
1136 return fs;
1137 }
1138 return NULL;
1139 }
1140
1141 /**
1142 * mnt_table_find_srcpath:
1143 * @tb: tab pointer
1144 * @path: source path (devname or dirname) or NULL
1145 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1146 *
1147 * Try to lookup an entry in the given tab, four iterations are possible, the first
1148 * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..)
1149 * from @path and the fourth with realpath(@path) against realpath(entry->srcpath).
1150 *
1151 * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
1152 * set (see mnt_table_set_cache()).
1153 *
1154 * For btrfs returns tab entry for default id.
1155 *
1156 * Note that NULL is a valid source path; it will be replaced with "none". The
1157 * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
1158 *
1159 * Returns: a tab entry or NULL.
1160 */
1161 struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *path, int direction)
1162 {
1163 struct libmnt_iter itr;
1164 struct libmnt_fs *fs = NULL;
1165 int ntags = 0, nents;
1166 char *cn;
1167 const char *p;
1168
1169 if (!tb || !path || !*path)
1170 return NULL;
1171 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1172 return NULL;
1173
1174 DBG(TAB, ul_debugobj(tb, "lookup SRCPATH: '%s'", path));
1175
1176 /* native paths */
1177 mnt_reset_iter(&itr, direction);
1178
1179 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1180
1181 if (mnt_fs_streq_srcpath(fs, path)) {
1182 #ifdef HAVE_BTRFS_SUPPORT
1183 if (fs->fstype && !strcmp(fs->fstype, "btrfs")) {
1184 uint64_t default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs));
1185 char *val;
1186 size_t len;
1187
1188 if (default_id == UINT64_MAX)
1189 DBG(TAB, ul_debug("not found btrfs volume setting"));
1190
1191 else if (mnt_fs_get_option(fs, "subvolid", &val, &len) == 0) {
1192 uint64_t subvol_id;
1193
1194 if (mnt_parse_offset(val, len, &subvol_id)) {
1195 DBG(TAB, ul_debugobj(tb, "failed to parse subvolid="));
1196 continue;
1197 }
1198 if (subvol_id != default_id)
1199 continue;
1200 }
1201 }
1202 #endif /* HAVE_BTRFS_SUPPORT */
1203 return fs;
1204 }
1205 if (mnt_fs_get_tag(fs, NULL, NULL) == 0)
1206 ntags++;
1207 }
1208
1209 if (!path || !tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
1210 return NULL;
1211
1212 DBG(TAB, ul_debugobj(tb, "lookup canonical SRCPATH: '%s'", cn));
1213
1214 nents = mnt_table_get_nents(tb);
1215
1216 /* canonicalized paths in struct libmnt_table */
1217 if (ntags < nents) {
1218 mnt_reset_iter(&itr, direction);
1219 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1220 if (mnt_fs_streq_srcpath(fs, cn))
1221 return fs;
1222 }
1223 }
1224
1225 /* evaluated tag */
1226 if (ntags) {
1227 int rc = mnt_cache_read_tags(tb->cache, cn);
1228
1229 mnt_reset_iter(&itr, direction);
1230
1231 if (rc == 0) {
1232 /* @path's TAGs are in the cache */
1233 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1234 const char *t, *v;
1235
1236 if (mnt_fs_get_tag(fs, &t, &v))
1237 continue;
1238
1239 if (mnt_cache_device_has_tag(tb->cache, cn, t, v))
1240 return fs;
1241 }
1242 } else if (rc < 0 && errno == EACCES) {
1243 /* @path is inaccessible, try evaluating all TAGs in @tb
1244 * by udev symlinks -- this could be expensive on systems
1245 * with a huge fstab/mtab */
1246 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1247 const char *t, *v, *x;
1248 if (mnt_fs_get_tag(fs, &t, &v))
1249 continue;
1250 x = mnt_resolve_tag(t, v, tb->cache);
1251
1252 /* both canonicalized, strcmp() is fine here */
1253 if (x && strcmp(x, cn) == 0)
1254 return fs;
1255 }
1256 }
1257 }
1258
1259 /* non-canonicalized paths in struct libmnt_table */
1260 if (ntags <= nents) {
1261 mnt_reset_iter(&itr, direction);
1262 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1263 if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs))
1264 continue;
1265 p = mnt_fs_get_srcpath(fs);
1266 if (p)
1267 p = mnt_resolve_path(p, tb->cache);
1268
1269 /* both canonicalized, strcmp() is fine here */
1270 if (p && strcmp(p, cn) == 0)
1271 return fs;
1272 }
1273 }
1274
1275 return NULL;
1276 }
1277
1278
1279 /**
1280 * mnt_table_find_tag:
1281 * @tb: tab pointer
1282 * @tag: tag name (e.g "LABEL", "UUID", ...)
1283 * @val: tag value
1284 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1285 *
1286 * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and
1287 * @val, for the second attempt the tag is evaluated (converted to the device
1288 * name) and mnt_table_find_srcpath() is performed. The second attempt is not
1289 * performed when @tb cache is not set (see mnt_table_set_cache()).
1290
1291 * Returns: a tab entry or NULL.
1292 */
1293 struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag,
1294 const char *val, int direction)
1295 {
1296 struct libmnt_iter itr;
1297 struct libmnt_fs *fs = NULL;
1298
1299 if (!tb || !tag || !*tag || !val)
1300 return NULL;
1301 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1302 return NULL;
1303
1304 DBG(TAB, ul_debugobj(tb, "lookup by TAG: %s %s", tag, val));
1305
1306 /* look up by TAG */
1307 mnt_reset_iter(&itr, direction);
1308 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1309 if (fs->tagname && fs->tagval &&
1310 strcmp(fs->tagname, tag) == 0 &&
1311 strcmp(fs->tagval, val) == 0)
1312 return fs;
1313 }
1314
1315 if (tb->cache) {
1316 /* look up by device name */
1317 char *cn = mnt_resolve_tag(tag, val, tb->cache);
1318 if (cn)
1319 return mnt_table_find_srcpath(tb, cn, direction);
1320 }
1321 return NULL;
1322 }
1323
1324 /**
1325 * mnt_table_find_target_with_option:
1326 * @tb: tab pointer
1327 * @path: mountpoint directory
1328 * @option: option name (e.g "subvol", "subvolid", ...)
1329 * @val: option value or NULL
1330 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1331 *
1332 * Try to lookup an entry in the given tab that matches combination of @path
1333 * and @option. In difference to mnt_table_find_target(), only @path iteration
1334 * is done. No lookup by device name, no canonicalization.
1335 *
1336 * Returns: a tab entry or NULL.
1337 *
1338 * Since: 2.28
1339 */
1340 struct libmnt_fs *mnt_table_find_target_with_option(
1341 struct libmnt_table *tb, const char *path,
1342 const char *option, const char *val, int direction)
1343 {
1344 struct libmnt_iter itr;
1345 struct libmnt_fs *fs = NULL;
1346 char *optval = NULL;
1347 size_t optvalsz = 0, valsz = val ? strlen(val) : 0;
1348
1349 if (!tb || !path || !*path || !option || !*option || !val)
1350 return NULL;
1351 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1352 return NULL;
1353
1354 DBG(TAB, ul_debugobj(tb, "lookup TARGET: '%s' with OPTION %s %s", path, option, val));
1355
1356 /* look up by native @target with OPTION */
1357 mnt_reset_iter(&itr, direction);
1358 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
1359 if (mnt_fs_streq_target(fs, path)
1360 && mnt_fs_get_option(fs, option, &optval, &optvalsz) == 0
1361 && (!val || (optvalsz == valsz
1362 && strncmp(optval, val, optvalsz) == 0)))
1363 return fs;
1364 }
1365 return NULL;
1366 }
1367
1368 /**
1369 * mnt_table_find_source:
1370 * @tb: tab pointer
1371 * @source: TAG or path
1372 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1373 *
1374 * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care
1375 * about the @source format (device, LABEL, UUID, ...). This function parses
1376 * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath().
1377 *
1378 * Returns: a tab entry or NULL.
1379 */
1380 struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb,
1381 const char *source, int direction)
1382 {
1383 struct libmnt_fs *fs;
1384 char *t = NULL, *v = NULL;
1385
1386 if (!tb)
1387 return NULL;
1388 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1389 return NULL;
1390
1391 DBG(TAB, ul_debugobj(tb, "lookup SOURCE: '%s'", source));
1392
1393 if (blkid_parse_tag_string(source, &t, &v) || !mnt_valid_tagname(t))
1394 fs = mnt_table_find_srcpath(tb, source, direction);
1395 else
1396 fs = mnt_table_find_tag(tb, t, v, direction);
1397
1398 free(t);
1399 free(v);
1400
1401 return fs;
1402 }
1403
1404 /**
1405 * mnt_table_find_pair
1406 * @tb: tab pointer
1407 * @source: TAG or path
1408 * @target: mountpoint
1409 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1410 *
1411 * This function is implemented by mnt_fs_match_source() and
1412 * mnt_fs_match_target() functions. It means that this is more expensive than
1413 * others mnt_table_find_* function, because every @tab entry is fully evaluated.
1414 *
1415 * Returns: a tab entry or NULL.
1416 */
1417 struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *source,
1418 const char *target, int direction)
1419 {
1420 struct libmnt_fs *fs = NULL;
1421 struct libmnt_iter itr;
1422
1423 if (!tb || !target || !*target || !source || !*source)
1424 return NULL;
1425 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1426 return NULL;
1427
1428 DBG(TAB, ul_debugobj(tb, "lookup SOURCE: %s TARGET: %s", source, target));
1429
1430 mnt_reset_iter(&itr, direction);
1431 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1432
1433 if (mnt_fs_match_target(fs, target, tb->cache) &&
1434 mnt_fs_match_source(fs, source, tb->cache))
1435 return fs;
1436 }
1437
1438 return NULL;
1439 }
1440
1441 /**
1442 * mnt_table_find_devno
1443 * @tb: /proc/self/mountinfo
1444 * @devno: device number
1445 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1446 *
1447 * Note that zero could be a valid device number for the root pseudo filesystem (e.g.
1448 * tmpfs).
1449 *
1450 * Returns: a tab entry or NULL.
1451 */
1452 struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb,
1453 dev_t devno, int direction)
1454 {
1455 struct libmnt_fs *fs = NULL;
1456 struct libmnt_iter itr;
1457
1458 if (!tb)
1459 return NULL;
1460 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1461 return NULL;
1462
1463 DBG(TAB, ul_debugobj(tb, "lookup DEVNO: %d", (int) devno));
1464
1465 mnt_reset_iter(&itr, direction);
1466
1467 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1468 if (mnt_fs_get_devno(fs) == devno)
1469 return fs;
1470 }
1471
1472 return NULL;
1473 }
1474
1475 static char *remove_mountpoint_from_path(const char *path, const char *mnt)
1476 {
1477 char *res;
1478 const char *p;
1479 size_t sz;
1480
1481 sz = strlen(mnt);
1482 p = sz > 1 ? path + sz : path;
1483
1484 res = *p ? strdup(p) : strdup("/");
1485 DBG(UTILS, ul_debug("%s fs-root is %s", path, res));
1486 return res;
1487 }
1488
1489 #ifdef HAVE_BTRFS_SUPPORT
1490 static int get_btrfs_fs_root(struct libmnt_table *tb, struct libmnt_fs *fs, char **root)
1491 {
1492 char *vol = NULL, *p;
1493 size_t sz, volsz = 0;
1494
1495 DBG(BTRFS, ul_debug("lookup for btrfs FS root"));
1496 *root = NULL;
1497
1498 if (mnt_fs_get_option(fs, "subvolid", &vol, &volsz) == 0) {
1499 char *target;
1500 struct libmnt_fs *f;
1501 char subvolidstr[sizeof(stringify_value(UINT64_MAX))];
1502
1503 DBG(BTRFS, ul_debug(" found subvolid=%s, checking", vol));
1504
1505 assert (volsz + 1 < sizeof(stringify_value(UINT64_MAX)));
1506 memcpy(subvolidstr, vol, volsz);
1507 subvolidstr[volsz] = '\0';
1508
1509 target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache);
1510 if (!target)
1511 goto err;
1512
1513 DBG(BTRFS, ul_debug(" trying target=%s subvolid=%s", target, subvolidstr));
1514 f = mnt_table_find_target_with_option(tb, target,
1515 "subvolid", subvolidstr,
1516 MNT_ITER_BACKWARD);
1517 if (!tb->cache)
1518 free(target);
1519 if (!f)
1520 goto not_found;
1521
1522 /* Instead of set of BACKREF queries constructing subvol path
1523 * corresponding to a particular subvolid, use the one in
1524 * mountinfo. Kernel keeps subvol path up to date.
1525 */
1526 if (mnt_fs_get_option(f, "subvol", &vol, &volsz) != 0)
1527 goto not_found;
1528
1529 } else if (mnt_fs_get_option(fs, "subvol", &vol, &volsz) != 0) {
1530 /* If fstab entry does not contain "subvol", we have to
1531 * check, whether btrfs has default subvolume defined.
1532 */
1533 uint64_t default_id;
1534 char *target;
1535 struct libmnt_fs *f;
1536 char default_id_str[sizeof(stringify_value(UINT64_MAX))];
1537
1538 DBG(BTRFS, ul_debug(" subvolid/subvol not found, checking default"));
1539
1540 default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs));
1541 if (default_id == UINT64_MAX)
1542 goto not_found;
1543
1544 /* Volume has default subvolume. Check if it matches to
1545 * the one in mountinfo.
1546 *
1547 * Only kernel >= 4.2 reports subvolid. On older
1548 * kernels, there is no reasonable way to detect which
1549 * subvolume was mounted.
1550 */
1551 target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache);
1552 if (!target)
1553 goto err;
1554
1555 snprintf(default_id_str, sizeof(default_id_str), "%llu",
1556 (unsigned long long int) default_id);
1557
1558 DBG(BTRFS, ul_debug(" trying target=%s default subvolid=%s",
1559 target, default_id_str));
1560
1561 f = mnt_table_find_target_with_option(tb, target,
1562 "subvolid", default_id_str,
1563 MNT_ITER_BACKWARD);
1564 if (!tb->cache)
1565 free(target);
1566 if (!f)
1567 goto not_found;
1568
1569 /* Instead of set of BACKREF queries constructing
1570 * subvol path, use the one in mountinfo. Kernel does
1571 * the evaluation for us.
1572 */
1573 DBG(BTRFS, ul_debug("setting FS root: btrfs default subvolid = %s",
1574 default_id_str));
1575
1576 if (mnt_fs_get_option(f, "subvol", &vol, &volsz) != 0)
1577 goto not_found;
1578 }
1579
1580 DBG(BTRFS, ul_debug(" using subvol=%s", vol));
1581 sz = volsz;
1582 if (*vol != '/')
1583 sz++;
1584 *root = malloc(sz + 1);
1585 if (!*root)
1586 goto err;
1587 p = *root;
1588 if (*vol != '/')
1589 *p++ = '/';
1590 memcpy(p, vol, volsz);
1591 *(*root + sz) = '\0';
1592 return 0;
1593
1594 not_found:
1595 DBG(BTRFS, ul_debug(" not found btrfs volume setting"));
1596 return 1;
1597 err:
1598 DBG(BTRFS, ul_debug(" error on btrfs volume setting evaluation"));
1599 return errno ? -errno : -1;
1600 }
1601 #endif /* HAVE_BTRFS_SUPPORT */
1602
1603 static const char *get_cifs_unc_subdir_path (const char *unc)
1604 {
1605 /*
1606 * 1 or more slash: %*[/]
1607 * 1 or more non-slash: %*[^/]
1608 * number of byte read: %n
1609 */
1610 int share_end = 0;
1611 int r = sscanf(unc, "%*[/]%*[^/]%*[/]%*[^/]%n", &share_end);
1612 if (r == EOF || share_end == 0)
1613 return NULL;
1614 return unc + share_end;
1615 }
1616
1617 /*
1618 * tb: /proc/self/mountinfo
1619 * fs: filesystem
1620 * mountflags: MS_BIND or 0
1621 * fsroot: fs-root that will probably be used in the mountinfo file
1622 * for @fs after mount(2)
1623 *
1624 * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
1625 *
1626 * If @tb is NULL then defaults to '/'.
1627 *
1628 * Returns: entry from @tb that will be used as a source for @fs if the @fs is
1629 * bindmount.
1630 *
1631 * Don't export to library API!
1632 */
1633 struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
1634 struct libmnt_fs *fs,
1635 unsigned long mountflags,
1636 char **fsroot)
1637 {
1638 char *root = NULL;
1639 const char *mnt = NULL;
1640 struct libmnt_fs *src_fs = NULL;
1641
1642 assert(fs);
1643 assert(fsroot);
1644
1645 DBG(TAB, ul_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs)));
1646
1647 if (tb && (mountflags & MS_BIND)) {
1648 const char *src, *src_root;
1649 char *xsrc = NULL;
1650
1651 DBG(TAB, ul_debug("fs-root for bind"));
1652
1653 src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache);
1654 if (src) {
1655 struct libmnt_fs *f = mnt_table_find_mountpoint(tb,
1656 src, MNT_ITER_BACKWARD);
1657 if (f)
1658 mnt = mnt_fs_get_target(f);
1659 }
1660 if (mnt)
1661 root = remove_mountpoint_from_path(src, mnt);
1662
1663 if (xsrc && !tb->cache) {
1664 free(xsrc);
1665 src = NULL;
1666 }
1667 if (!mnt)
1668 goto err;
1669
1670 src_fs = mnt_table_find_target(tb, mnt, MNT_ITER_BACKWARD);
1671 if (!src_fs) {
1672 DBG(TAB, ul_debug("not found '%s' in mountinfo -- using default", mnt));
1673 goto dflt;
1674 }
1675
1676 /* It's possible that fstab_fs source is subdirectory on btrfs
1677 * subvolume or another bind mount. For example:
1678 *
1679 * /dev/sdc /mnt/test btrfs subvol=/anydir
1680 * /dev/sdc /mnt/test btrfs defaults
1681 * /mnt/test/foo /mnt/test2 auto bind
1682 *
1683 * in this case, the root for /mnt/test2 will be /anydir/foo on
1684 * /dev/sdc. It means we have to compose the final root from
1685 * root and src_root.
1686 */
1687 src_root = mnt_fs_get_root(src_fs);
1688
1689 DBG(FS, ul_debugobj(fs, "source root: %s, source FS root: %s", root, src_root));
1690
1691 if (src_root && root && !startswith(root, src_root)) {
1692 if (strcmp(root, "/") == 0) {
1693 free(root);
1694 root = strdup(src_root);
1695 if (!root)
1696 goto err;
1697 } else {
1698 char *tmp;
1699 if (asprintf(&tmp, "%s%s", src_root, root) < 0)
1700 goto err;
1701 free(root);
1702 root = tmp;
1703 }
1704 }
1705 }
1706
1707 #ifdef HAVE_BTRFS_SUPPORT
1708 /*
1709 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
1710 */
1711 else if (tb && fs->fstype &&
1712 (!strcmp(fs->fstype, "btrfs") || !strcmp(fs->fstype, "auto"))) {
1713 if (get_btrfs_fs_root(tb, fs, &root) < 0)
1714 goto err;
1715 }
1716 #endif /* HAVE_BTRFS_SUPPORT */
1717
1718 dflt:
1719 if (!root) {
1720 root = strdup("/");
1721 if (!root)
1722 goto err;
1723 }
1724 *fsroot = root;
1725
1726 DBG(TAB, ul_debug("FS root result: %s", root));
1727
1728 return src_fs;
1729 err:
1730 free(root);
1731 return NULL;
1732 }
1733
1734
1735 int __mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs,
1736 const char *tgt_prefix)
1737 {
1738 struct libmnt_iter itr;
1739 struct libmnt_fs *fs;
1740
1741 char *root = NULL;
1742 char *src2 = NULL;
1743 const char *src = NULL, *tgt = NULL;
1744 char *xtgt = NULL, *tgt_buf = NULL;
1745 int rc = 0;
1746 dev_t devno = 0;
1747
1748 DBG(FS, ul_debugobj(fstab_fs, "mnt_table_is_fs_mounted: target=%s, source=%s",
1749 mnt_fs_get_target(fstab_fs),
1750 mnt_fs_get_source(fstab_fs)));
1751
1752 if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_is_empty(tb)) {
1753 DBG(FS, ul_debugobj(fstab_fs, "- ignore (swap or no data)"));
1754 return 0;
1755 }
1756
1757 if (is_mountinfo(tb)) {
1758 /* @tb is mountinfo, so we can try to use fs-roots */
1759 struct libmnt_fs *rootfs;
1760 int flags = 0;
1761
1762 if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0 ||
1763 mnt_fs_get_option(fstab_fs, "rbind", NULL, NULL) == 0)
1764 flags = MS_BIND;
1765
1766 rootfs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root);
1767 if (rootfs) {
1768 const char *fstype = mnt_fs_get_fstype(rootfs);
1769
1770 src = mnt_fs_get_srcpath(rootfs);
1771 if (fstype && strncmp(fstype, "nfs", 3) == 0 && root) {
1772 /* NFS stores the root at the end of the source */
1773 src = src2 = strconcat(src, root);
1774 free(root);
1775 root = NULL;
1776 }
1777 }
1778 }
1779
1780 if (!src)
1781 src = mnt_fs_get_source(fstab_fs);
1782
1783 if (src && tb->cache && !mnt_fs_is_pseudofs(fstab_fs))
1784 src = mnt_resolve_spec(src, tb->cache);
1785
1786 if (src && root) {
1787 struct stat st;
1788
1789 devno = mnt_fs_get_devno(fstab_fs);
1790 if (!devno && mnt_safe_stat(src, &st) == 0 && S_ISBLK(st.st_mode))
1791 devno = st.st_rdev;
1792 }
1793
1794 tgt = mnt_fs_get_target(fstab_fs);
1795
1796 if (!tgt || !src) {
1797 DBG(FS, ul_debugobj(fstab_fs, "- ignore (no source/target)"));
1798 goto done;
1799 }
1800 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
1801
1802 DBG(FS, ul_debugobj(fstab_fs, "mnt_table_is_fs_mounted: src=%s, tgt=%s, root=%s", src, tgt, root));
1803
1804 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
1805
1806 int eq = mnt_fs_streq_srcpath(fs, src);
1807
1808 if (!eq && devno && mnt_fs_get_devno(fs) == devno)
1809 eq = 1;
1810
1811 if (!eq) {
1812 /* The source does not match. Maybe the source is a loop
1813 * device backing file.
1814 */
1815 uint64_t offset = 0;
1816 char *val;
1817 size_t len;
1818 int flags = 0;
1819
1820 if (!mnt_fs_get_srcpath(fs) ||
1821 !startswith(mnt_fs_get_srcpath(fs), "/dev/loop"))
1822 continue; /* does not look like loopdev */
1823
1824 if (mnt_fs_get_option(fstab_fs, "offset", &val, &len) == 0) {
1825 if (mnt_parse_offset(val, len, &offset)) {
1826 DBG(FS, ul_debugobj(fstab_fs, "failed to parse offset="));
1827 continue;
1828 }
1829 flags = LOOPDEV_FL_OFFSET;
1830 }
1831
1832 DBG(FS, ul_debugobj(fs, "checking for loop: src=%s", mnt_fs_get_srcpath(fs)));
1833 #if __linux__
1834 if (!loopdev_is_used(mnt_fs_get_srcpath(fs), src, offset, 0, flags))
1835 continue;
1836
1837 DBG(FS, ul_debugobj(fs, "used loop"));
1838 #endif
1839 }
1840
1841 if (root) {
1842 const char *fstype = mnt_fs_get_fstype(fs);
1843
1844 if (fstype && (strcmp(fstype, "cifs") == 0 ||
1845 strcmp(fstype, "smb3") == 0)) {
1846
1847 const char *sub = get_cifs_unc_subdir_path(src);
1848 const char *r = mnt_fs_get_root(fs);
1849
1850 if (!sub || !r || (!streq_paths(sub, r) &&
1851 !streq_paths("/", r)))
1852 continue;
1853 } else {
1854 const char *r = mnt_fs_get_root(fs);
1855 if (!r || strcmp(r, root) != 0)
1856 continue;
1857 }
1858 }
1859
1860 /*
1861 * Compare target, try to minimize the number of situations when we
1862 * need to canonicalize the path to avoid readlink() on
1863 * mountpoints.
1864 */
1865 if (!xtgt) {
1866 if (tgt_prefix) {
1867 const char *p = *tgt == '/' ? tgt + 1 : tgt;
1868 if (!*p)
1869 tgt = tgt_prefix; /* target is '/' */
1870 else {
1871 if (asprintf(&tgt_buf, "%s/%s", tgt_prefix, p) <= 0) {
1872 rc = -ENOMEM;
1873 goto done;
1874 }
1875 tgt = tgt_buf;
1876 }
1877 }
1878
1879 if (mnt_fs_streq_target(fs, tgt))
1880 break;
1881 if (tb->cache)
1882 xtgt = mnt_resolve_path(tgt, tb->cache);
1883 }
1884 if (xtgt && mnt_fs_streq_target(fs, xtgt))
1885 break;
1886 }
1887
1888 if (fs)
1889 rc = 1; /* success */
1890 done:
1891 free(root);
1892 free(tgt_buf);
1893
1894 DBG(TAB, ul_debugobj(tb, "mnt_table_is_fs_mounted: %s [rc=%d]", src, rc));
1895 free(src2);
1896 return rc;
1897 }
1898
1899 /**
1900 * mnt_table_is_fs_mounted:
1901 * @tb: /proc/self/mountinfo file
1902 * @fstab_fs: /etc/fstab entry
1903 *
1904 * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
1905 * ignored. This function explicitly compares the source, target and root of the
1906 * filesystems.
1907 *
1908 * Note that source and target are canonicalized only if a cache for @tb is
1909 * defined (see mnt_table_set_cache()). The target canonicalization may
1910 * trigger automount on autofs mountpoints!
1911 *
1912 * Don't use it if you want to know if a device is mounted, just use
1913 * mnt_table_find_source() on the device.
1914 *
1915 * This function is designed mostly for "mount -a".
1916 *
1917 * Returns: 0 or 1
1918 */
1919 int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
1920 {
1921 return __mnt_table_is_fs_mounted(tb, fstab_fs, NULL);
1922 }
1923
1924
1925 #ifdef TEST_PROGRAM
1926 #include "pathnames.h"
1927
1928 static int parser_errcb(struct libmnt_table *tb __attribute__((unused)),
1929 const char *filename, int line)
1930 {
1931 fprintf(stderr, "%s:%d: parse error\n", filename, line);
1932
1933 return 1; /* all errors are recoverable -- this is the default */
1934 }
1935
1936 static struct libmnt_table *create_table(const char *file, int comments)
1937 {
1938 struct libmnt_table *tb;
1939
1940 if (!file)
1941 return NULL;
1942 tb = mnt_new_table();
1943 if (!tb)
1944 goto err;
1945
1946 mnt_table_enable_comments(tb, comments);
1947 mnt_table_set_parser_errcb(tb, parser_errcb);
1948
1949 if (mnt_table_parse_file(tb, file) != 0)
1950 goto err;
1951 return tb;
1952 err:
1953 fprintf(stderr, "%s: parsing failed\n", file);
1954 mnt_unref_table(tb);
1955 return NULL;
1956 }
1957
1958 static int test_copy_fs(struct libmnt_test *ts __attribute__((unused)),
1959 int argc, char *argv[])
1960 {
1961 struct libmnt_table *tb;
1962 struct libmnt_fs *fs;
1963 int rc = -1;
1964
1965 if (argc != 2)
1966 return -1;
1967
1968 tb = create_table(argv[1], FALSE);
1969 if (!tb)
1970 return -1;
1971
1972 fs = mnt_table_find_target(tb, "/", MNT_ITER_FORWARD);
1973 if (!fs)
1974 goto done;
1975
1976 printf("ORIGINAL:\n");
1977 mnt_fs_print_debug(fs, stdout);
1978
1979 fs = mnt_copy_fs(NULL, fs);
1980 if (!fs)
1981 goto done;
1982
1983 printf("COPY:\n");
1984 mnt_fs_print_debug(fs, stdout);
1985 mnt_unref_fs(fs);
1986 rc = 0;
1987 done:
1988 mnt_unref_table(tb);
1989 return rc;
1990 }
1991
1992 static int test_parse(struct libmnt_test *ts __attribute__((unused)),
1993 int argc, char *argv[])
1994 {
1995 struct libmnt_table *tb = NULL;
1996 struct libmnt_iter *itr = NULL;
1997 struct libmnt_fs *fs;
1998 int rc = -1;
1999 int parse_comments = FALSE;
2000
2001 if (argc == 3 && !strcmp(argv[2], "--comments"))
2002 parse_comments = TRUE;
2003
2004 tb = create_table(argv[1], parse_comments);
2005 if (!tb)
2006 return -1;
2007
2008 itr = mnt_new_iter(MNT_ITER_FORWARD);
2009 if (!itr)
2010 goto done;
2011
2012 if (mnt_table_get_intro_comment(tb))
2013 fprintf(stdout, "Initial comment:\n\"%s\"\n",
2014 mnt_table_get_intro_comment(tb));
2015
2016 while(mnt_table_next_fs(tb, itr, &fs) == 0)
2017 mnt_fs_print_debug(fs, stdout);
2018
2019 if (mnt_table_get_trailing_comment(tb))
2020 fprintf(stdout, "Trailing comment:\n\"%s\"\n",
2021 mnt_table_get_trailing_comment(tb));
2022 rc = 0;
2023 done:
2024 mnt_free_iter(itr);
2025 mnt_unref_table(tb);
2026 return rc;
2027 }
2028
2029 static int test_find_idx(struct libmnt_test *ts __attribute__((unused)),
2030 int argc, char *argv[])
2031 {
2032 struct libmnt_table *tb;
2033 struct libmnt_fs *fs = NULL;
2034 struct libmnt_cache *mpc = NULL;
2035 const char *file, *what;
2036 int rc = -1;
2037
2038 if (argc != 3) {
2039 fprintf(stderr, "try --help\n");
2040 return -EINVAL;
2041 }
2042
2043 file = argv[1], what = argv[2];
2044
2045 tb = create_table(file, FALSE);
2046 if (!tb)
2047 goto done;
2048
2049 /* create a cache for canonicalized paths */
2050 mpc = mnt_new_cache();
2051 if (!mpc)
2052 goto done;
2053 mnt_table_set_cache(tb, mpc);
2054 mnt_unref_cache(mpc);
2055
2056 fs = mnt_table_find_target(tb, what, MNT_ITER_BACKWARD);
2057
2058 if (!fs)
2059 fprintf(stderr, "%s: not found '%s'\n", file, what);
2060 else {
2061 int idx = mnt_table_find_fs(tb, fs);
2062
2063 if (idx < 1)
2064 fprintf(stderr, "%s: not found '%s' fs pointer", file, what);
2065 else {
2066 printf("%s index is %d\n", what, idx);
2067 rc = 0;
2068 }
2069 }
2070 done:
2071 mnt_unref_table(tb);
2072 return rc;
2073 }
2074
2075 static int test_find(struct libmnt_test *ts __attribute__((unused)),
2076 int argc, char *argv[], int dr)
2077 {
2078 struct libmnt_table *tb;
2079 struct libmnt_fs *fs = NULL;
2080 struct libmnt_cache *mpc = NULL;
2081 const char *file, *find, *what;
2082 int rc = -1;
2083
2084 if (argc != 4) {
2085 fprintf(stderr, "try --help\n");
2086 return -EINVAL;
2087 }
2088
2089 file = argv[1], find = argv[2], what = argv[3];
2090
2091 tb = create_table(file, FALSE);
2092 if (!tb)
2093 goto done;
2094
2095 /* create a cache for canonicalized paths */
2096 mpc = mnt_new_cache();
2097 if (!mpc)
2098 goto done;
2099 mnt_table_set_cache(tb, mpc);
2100 mnt_unref_cache(mpc);
2101
2102 if (strcasecmp(find, "source") == 0)
2103 fs = mnt_table_find_source(tb, what, dr);
2104 else if (strcasecmp(find, "target") == 0)
2105 fs = mnt_table_find_target(tb, what, dr);
2106
2107 if (!fs)
2108 fprintf(stderr, "%s: not found %s '%s'\n", file, find, what);
2109 else {
2110 mnt_fs_print_debug(fs, stdout);
2111 rc = 0;
2112 }
2113 done:
2114 mnt_unref_table(tb);
2115 return rc;
2116 }
2117
2118 static int test_find_bw(struct libmnt_test *ts __attribute__((unused)),
2119 int argc, char *argv[])
2120 {
2121 return test_find(ts, argc, argv, MNT_ITER_BACKWARD);
2122 }
2123
2124 static int test_find_fw(struct libmnt_test *ts __attribute__((unused)),
2125 int argc, char *argv[])
2126 {
2127 return test_find(ts, argc, argv, MNT_ITER_FORWARD);
2128 }
2129
2130 static int test_find_pair(struct libmnt_test *ts __attribute__((unused)),
2131 int argc, char *argv[])
2132 {
2133 struct libmnt_table *tb;
2134 struct libmnt_fs *fs;
2135 struct libmnt_cache *mpc = NULL;
2136 int rc = -1;
2137
2138 if (argc != 4)
2139 return -1;
2140
2141 tb = create_table(argv[1], FALSE);
2142 if (!tb)
2143 return -1;
2144 mpc = mnt_new_cache();
2145 if (!mpc)
2146 goto done;
2147 mnt_table_set_cache(tb, mpc);
2148 mnt_unref_cache(mpc);
2149
2150 fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD);
2151 if (!fs)
2152 goto done;
2153
2154 mnt_fs_print_debug(fs, stdout);
2155 rc = 0;
2156 done:
2157 mnt_unref_table(tb);
2158 return rc;
2159 }
2160
2161 static int test_find_mountpoint(struct libmnt_test *ts __attribute__((unused)),
2162 int argc, char *argv[])
2163 {
2164 struct libmnt_table *tb;
2165 struct libmnt_fs *fs;
2166 struct libmnt_cache *mpc = NULL;
2167 int rc = -1;
2168
2169 if (argc != 2)
2170 return -1;
2171
2172 tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
2173 if (!tb)
2174 return -1;
2175 mpc = mnt_new_cache();
2176 if (!mpc)
2177 goto done;
2178 mnt_table_set_cache(tb, mpc);
2179 mnt_unref_cache(mpc);
2180
2181 fs = mnt_table_find_mountpoint(tb, argv[1], MNT_ITER_BACKWARD);
2182 if (!fs)
2183 goto done;
2184
2185 mnt_fs_print_debug(fs, stdout);
2186 rc = 0;
2187 done:
2188 mnt_unref_table(tb);
2189 return rc;
2190 }
2191
2192 static int test_is_mounted(struct libmnt_test *ts __attribute__((unused)),
2193 int argc, char *argv[])
2194 {
2195 struct libmnt_table *tb = NULL, *fstab = NULL;
2196 struct libmnt_fs *fs;
2197 struct libmnt_iter *itr = NULL;
2198 struct libmnt_cache *mpc = NULL;
2199
2200 if (argc != 2)
2201 return -1;
2202
2203 tb = mnt_new_table_from_file("/proc/self/mountinfo");
2204 if (!tb) {
2205 fprintf(stderr, "failed to parse mountinfo\n");
2206 return -1;
2207 }
2208
2209 fstab = create_table(argv[1], FALSE);
2210 if (!fstab)
2211 goto done;
2212
2213 itr = mnt_new_iter(MNT_ITER_FORWARD);
2214 if (!itr)
2215 goto done;
2216
2217 mpc = mnt_new_cache();
2218 if (!mpc)
2219 goto done;
2220 mnt_table_set_cache(tb, mpc);
2221 mnt_unref_cache(mpc);
2222
2223 while (mnt_table_next_fs(fstab, itr, &fs) == 0) {
2224 if (mnt_table_is_fs_mounted(tb, fs))
2225 printf("%s already mounted on %s\n",
2226 mnt_fs_get_source(fs),
2227 mnt_fs_get_target(fs));
2228 else
2229 printf("%s not mounted on %s\n",
2230 mnt_fs_get_source(fs),
2231 mnt_fs_get_target(fs));
2232 }
2233
2234 done:
2235 mnt_unref_table(tb);
2236 mnt_unref_table(fstab);
2237 mnt_free_iter(itr);
2238 return 0;
2239 }
2240
2241 /* returns 0 if @a and @b targets are the same */
2242 static int test_uniq_cmp(struct libmnt_table *tb __attribute__((__unused__)),
2243 struct libmnt_fs *a,
2244 struct libmnt_fs *b)
2245 {
2246 assert(a);
2247 assert(b);
2248
2249 return mnt_fs_streq_target(a, mnt_fs_get_target(b)) ? 0 : 1;
2250 }
2251
2252 static int test_uniq(struct libmnt_test *ts __attribute__((unused)),
2253 int argc, char *argv[])
2254 {
2255 struct libmnt_table *tb;
2256 int rc = -1;
2257
2258 if (argc != 2) {
2259 fprintf(stderr, "try --help\n");
2260 return -EINVAL;
2261 }
2262
2263 tb = create_table(argv[1], FALSE);
2264 if (!tb)
2265 goto done;
2266
2267 if (mnt_table_uniq_fs(tb, 0, test_uniq_cmp) == 0) {
2268 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
2269 struct libmnt_fs *fs;
2270 if (!itr)
2271 goto done;
2272 while (mnt_table_next_fs(tb, itr, &fs) == 0)
2273 mnt_fs_print_debug(fs, stdout);
2274 mnt_free_iter(itr);
2275 rc = 0;
2276 }
2277 done:
2278 mnt_unref_table(tb);
2279 return rc;
2280 }
2281
2282
2283 int main(int argc, char *argv[])
2284 {
2285 struct libmnt_test tss[] = {
2286 { "--parse", test_parse, "<file> [--comments] parse and print tab" },
2287 { "--find-forward", test_find_fw, "<file> <source|target> <string>" },
2288 { "--find-backward", test_find_bw, "<file> <source|target> <string>" },
2289 { "--uniq-target", test_uniq, "<file>" },
2290 { "--find-pair", test_find_pair, "<file> <source> <target>" },
2291 { "--find-fs", test_find_idx, "<file> <target>" },
2292 { "--find-mountpoint", test_find_mountpoint, "<path>" },
2293 { "--copy-fs", test_copy_fs, "<file> copy root FS from the file" },
2294 { "--is-mounted", test_is_mounted, "<fstab> check what from fstab is already mounted" },
2295 { NULL }
2296 };
2297
2298 return mnt_run_test(tss, argc, argv);
2299 }
2300
2301 #endif /* TEST_PROGRAM */