]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/tab.c
libmount: fix memory overflow [AddressSanitizer]
[thirdparty/util-linux.git] / libmount / src / tab.c
1 /*
2 * Copyright (C) 2008-2010 Karel Zak <kzak@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7
8 /**
9 * SECTION: table
10 * @title: Table of filesystems
11 * @short_description: container for entries from fstab, mtab or mountinfo
12 *
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:
17 * <informalexample>
18 * <programlisting>
19 * LABEL=foo /foo auto rw
20 * /dev/foo /foo auto rw
21 * </programlisting>
22 * </informalexample>
23 *
24 * where both lines are used for the *same* device, then
25 * <informalexample>
26 * <programlisting>
27 * mnt_table_find_source(tb, "/dev/foo", &fs);
28 * </programlisting>
29 * </informalexample>
30 * will returns the second line, and
31 * <informalexample>
32 * <programlisting>
33 * mnt_table_find_source(tb, "LABEL=foo", &fs);
34 * </programlisting>
35 * </informalexample>
36 * will returns the first entry, and
37 * <informalexample>
38 * <programlisting>
39 * mnt_table_find_source(tb, "UUID=anyuuid", &fs);
40 * </programlisting>
41 * </informalexample>
42 * will return the first entry (if UUID matches with the device).
43 */
44 #include <blkid.h>
45
46 #include "mountP.h"
47 #include "strutils.h"
48 #include "loopdev.h"
49 #include "fileutils.h"
50
51 static int is_mountinfo(struct libmnt_table *tb)
52 {
53 struct libmnt_fs *fs;
54
55 if (!tb)
56 return 0;
57
58 fs = list_first_entry(&tb->ents, struct libmnt_fs, ents);
59 if (fs && mnt_fs_is_kernel(fs) && mnt_fs_get_root(fs))
60 return 1;
61
62 return 0;
63 }
64
65 /**
66 * mnt_new_table:
67 *
68 * The tab is a container for struct libmnt_fs entries that usually represents a fstab,
69 * mtab or mountinfo file from your system.
70 *
71 * See also mnt_table_parse_file().
72 *
73 * Returns: newly allocated tab struct.
74 */
75 struct libmnt_table *mnt_new_table(void)
76 {
77 struct libmnt_table *tb = NULL;
78
79 tb = calloc(1, sizeof(*tb));
80 if (!tb)
81 return NULL;
82
83 DBG(TAB, ul_debugobj(tb, "alloc"));
84 tb->refcount = 1;
85 INIT_LIST_HEAD(&tb->ents);
86 return tb;
87 }
88
89 /**
90 * mnt_reset_table:
91 * @tb: tab pointer
92 *
93 * Removes all entries (filesystems) from the table. The filesystems with zero
94 * reference count will be deallocated.
95 *
96 * Returns: 0 on success or negative number in case of error.
97 */
98 int mnt_reset_table(struct libmnt_table *tb)
99 {
100 if (!tb)
101 return -EINVAL;
102
103 DBG(TAB, ul_debugobj(tb, "reset"));
104
105 while (!list_empty(&tb->ents)) {
106 struct libmnt_fs *fs = list_entry(tb->ents.next,
107 struct libmnt_fs, ents);
108 mnt_table_remove_fs(tb, fs);
109 }
110
111 tb->nents = 0;
112 return 0;
113 }
114
115 /**
116 * mnt_ref_table:
117 * @tb: table pointer
118 *
119 * Increments reference counter.
120 */
121 void mnt_ref_table(struct libmnt_table *tb)
122 {
123 if (tb) {
124 tb->refcount++;
125 /*DBG(FS, ul_debugobj(tb, "ref=%d", tb->refcount));*/
126 }
127 }
128
129 /**
130 * mnt_unref_table:
131 * @tb: table pointer
132 *
133 * De-increments reference counter, on zero the @tb is automatically
134 * deallocated by mnt_free_table().
135 */
136 void mnt_unref_table(struct libmnt_table *tb)
137 {
138 if (tb) {
139 tb->refcount--;
140 /*DBG(FS, ul_debugobj(tb, "unref=%d", tb->refcount));*/
141 if (tb->refcount <= 0)
142 mnt_free_table(tb);
143 }
144 }
145
146
147 /**
148 * mnt_free_table:
149 * @tb: tab pointer
150 *
151 * Deallocates the table. This function does not care about reference count. Don't
152 * use this function directly -- it's better to use use mnt_unref_table().
153 *
154 * The table entries (filesystems) are unrefrenced by mnt_reset_table() and
155 * cache by mnt_unref_cache().
156 */
157 void mnt_free_table(struct libmnt_table *tb)
158 {
159 if (!tb)
160 return;
161
162 mnt_reset_table(tb);
163 DBG(TAB, ul_debugobj(tb, "free [refcount=%d]", tb->refcount));
164
165 mnt_unref_cache(tb->cache);
166 free(tb->comm_intro);
167 free(tb->comm_tail);
168 free(tb);
169 }
170
171 /**
172 * mnt_table_get_nents:
173 * @tb: pointer to tab
174 *
175 * Returns: number of entries in table.
176 */
177 int mnt_table_get_nents(struct libmnt_table *tb)
178 {
179 return tb ? tb->nents : 0;
180 }
181
182 /**
183 * mnt_table_is_empty:
184 * @tb: pointer to tab
185 *
186 * Returns: 1 if the table is without filesystems, or 0.
187 */
188 int mnt_table_is_empty(struct libmnt_table *tb)
189 {
190 assert(tb);
191 return tb == NULL || list_empty(&tb->ents) ? 1 : 0;
192 }
193
194 /**
195 * mnt_table_set_userdata:
196 * @tb: pointer to tab
197 * @data: pointer to user data
198 *
199 * Sets pointer to the private user data.
200 *
201 * Returns: 0 on success or negative number in case of error.
202 */
203 int mnt_table_set_userdata(struct libmnt_table *tb, void *data)
204 {
205 assert(tb);
206 if (!tb)
207 return -EINVAL;
208
209 tb->userdata = data;
210 return 0;
211 }
212
213 /**
214 * mnt_table_get_userdata:
215 * @tb: pointer to tab
216 *
217 * Returns: pointer to user's data.
218 */
219 void *mnt_table_get_userdata(struct libmnt_table *tb)
220 {
221 assert(tb);
222 return tb ? tb->userdata : NULL;
223 }
224
225 /**
226 * mnt_table_enable_comments:
227 * @tb: pointer to tab
228 * @enable: TRUE or FALSE
229 *
230 * Enables parsing of comments.
231 *
232 * The initial (intro) file comment is accessible by
233 * mnt_table_get_intro_comment(). The intro and the comment of the first fstab
234 * entry has to be separated by blank line. The filesystem comments are
235 * accessible by mnt_fs_get_comment(). The trailing fstab comment is accessible
236 * by mnt_table_get_trailing_comment().
237 *
238 * <informalexample>
239 * <programlisting>
240 * #
241 * # Intro comment
242 * #
243 *
244 * # this comments belongs to the first fs
245 * LABEL=foo /mnt/foo auto defaults 1 2
246 * # this comments belongs to the second fs
247 * LABEL=bar /mnt/bar auto defaults 1 2
248 * # tailing comment
249 * </programlisting>
250 * </informalexample>
251 */
252 void mnt_table_enable_comments(struct libmnt_table *tb, int enable)
253 {
254 assert(tb);
255 if (tb)
256 tb->comms = enable;
257 }
258
259 /**
260 * mnt_table_with_comments:
261 * @tb: pointer to table
262 *
263 * Returns: 1 if comments parsing is enabled, or 0.
264 */
265 int mnt_table_with_comments(struct libmnt_table *tb)
266 {
267 assert(tb);
268 return tb ? tb->comms : 0;
269 }
270
271 /**
272 * mnt_table_get_intro_comment:
273 * @tb: pointer to tab
274 *
275 * Returns: initial comment in tb
276 */
277 const char *mnt_table_get_intro_comment(struct libmnt_table *tb)
278 {
279 assert(tb);
280 return tb ? tb->comm_intro : NULL;
281 }
282
283 /**
284 * mnt_table_set_into_comment:
285 * @tb: pointer to tab
286 * @comm: comment or NULL
287 *
288 * Sets the initial comment in tb.
289 *
290 * Returns: 0 on success or negative number in case of error.
291 */
292 int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm)
293 {
294 char *p = NULL;
295
296 assert(tb);
297 if (!tb)
298 return -EINVAL;
299 if (comm) {
300 p = strdup(comm);
301 if (!p)
302 return -ENOMEM;
303 }
304 free(tb->comm_intro);
305 tb->comm_intro = p;
306 return 0;
307 }
308
309 /**
310 * mnt_table_append_into_comment:
311 * @tb: pointer to tab
312 * @comm: comment of NULL
313 *
314 * Appends the initial comment in tb.
315 *
316 * Returns: 0 on success or negative number in case of error.
317 */
318 int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm)
319 {
320 assert(tb);
321 if (!tb)
322 return -EINVAL;
323 return append_string(&tb->comm_intro, comm);
324 }
325
326 /**
327 * mnt_table_get_trailing_comment:
328 * @tb: pointer to tab
329 *
330 * Returns: table trailing comment
331 */
332 const char *mnt_table_get_trailing_comment(struct libmnt_table *tb)
333 {
334 assert(tb);
335 return tb ? tb->comm_tail : NULL;
336 }
337
338 /**
339 * mnt_table_set_trailing_comment
340 * @tb: pointer to tab
341 * @comm: comment string
342 *
343 * Sets the trailing comment in table.
344 *
345 * Returns: 0 on success or negative number in case of error.
346 */
347 int mnt_table_set_trailing_comment(struct libmnt_table *tb, const char *comm)
348 {
349 char *p = NULL;
350
351 assert(tb);
352 if (!tb)
353 return -EINVAL;
354 if (comm) {
355 p = strdup(comm);
356 if (!p)
357 return -ENOMEM;
358 }
359 free(tb->comm_tail);
360 tb->comm_tail = p;
361 return 0;
362 }
363
364 /**
365 * mnt_table_append_trailing_comment:
366 * @tb: pointer to tab
367 * @comm: comment of NULL
368 *
369 * Appends to the trailing table comment.
370 *
371 * Returns: 0 on success or negative number in case of error.
372 */
373 int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm)
374 {
375 assert(tb);
376 if (!tb)
377 return -EINVAL;
378 return append_string(&tb->comm_tail, comm);
379 }
380
381 /**
382 * mnt_table_set_cache:
383 * @tb: pointer to tab
384 * @mpc: pointer to struct libmnt_cache instance
385 *
386 * Sets up a cache for canonicalized paths and evaluated tags (LABEL/UUID). The
387 * cache is recommended for mnt_table_find_*() functions.
388 *
389 * The cache could be shared between more tabs. Be careful when you share the
390 * same cache between more threads -- currently the cache does not provide any
391 * locking method.
392 *
393 * This function increments cache reference counter. It's recomented to use
394 * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache
395 * referenced by @tb only.
396 *
397 * See also mnt_new_cache().
398 *
399 * Returns: 0 on success or negative number in case of error.
400 */
401 int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc)
402 {
403 assert(tb);
404 if (!tb)
405 return -EINVAL;
406
407 mnt_ref_cache(mpc); /* new */
408 mnt_unref_cache(tb->cache); /* old */
409 tb->cache = mpc;
410 return 0;
411 }
412
413 /**
414 * mnt_table_get_cache:
415 * @tb: pointer to tab
416 *
417 * Returns: pointer to struct libmnt_cache instance or NULL.
418 */
419 struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb)
420 {
421 assert(tb);
422 return tb ? tb->cache : NULL;
423 }
424
425 /**
426 * mnt_table_add_fs:
427 * @tb: tab pointer
428 * @fs: new entry
429 *
430 * Adds a new entry to tab and increment @fs reference counter. Don't forget to
431 * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs
432 * referenced by the table only.
433 *
434 * Returns: 0 on success or negative number in case of error.
435 */
436 int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
437 {
438 assert(tb);
439 assert(fs);
440
441 if (!tb || !fs)
442 return -EINVAL;
443
444 mnt_ref_fs(fs);
445 list_add_tail(&fs->ents, &tb->ents);
446 tb->nents++;
447
448 DBG(TAB, ul_debugobj(tb, "add entry: %s %s",
449 mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
450 return 0;
451 }
452
453 /**
454 * mnt_table_remove_fs:
455 * @tb: tab pointer
456 * @fs: new entry
457 *
458 * Removes the @fs from the table and de-increment reference counter of the @fs. The
459 * filesystem with zero reference counter will be deallocated. Don't forget to use
460 * mnt_ref_fs() before call mnt_table_remove_fs() if you want to use @fs later.
461 *
462 * Returns: 0 on success or negative number in case of error.
463 */
464 int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
465 {
466 assert(tb);
467 assert(fs);
468
469 if (!tb || !fs)
470 return -EINVAL;
471
472 list_del(&fs->ents);
473 INIT_LIST_HEAD(&fs->ents); /* otherwise FS still points to the list */
474
475 mnt_unref_fs(fs);
476 tb->nents--;
477 return 0;
478 }
479
480 /**
481 * mnt_table_get_root_fs:
482 * @tb: mountinfo file (/proc/self/mountinfo)
483 * @root: returns pointer to the root filesystem (/)
484 *
485 * The function uses the parent ID from the mountinfo file to determine the root filesystem
486 * (the filesystem with the smallest ID). The function is designed mostly for
487 * applications where it is necessary to sort mountpoints by IDs to get the tree
488 * of the mountpoints (e.g. findmnt default output).
489 *
490 * If you're not sure, then use
491 *
492 * mnt_table_find_target(tb, "/", MNT_ITER_BACKWARD);
493 *
494 * this is more robust and usable for arbitrary tab files (including fstab).
495 *
496 * Returns: 0 on success or negative number in case of error.
497 */
498 int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root)
499 {
500 struct libmnt_iter itr;
501 struct libmnt_fs *fs;
502 int root_id = 0;
503
504 assert(tb);
505 assert(root);
506
507 if (!tb || !root || !is_mountinfo(tb))
508 return -EINVAL;
509
510 DBG(TAB, ul_debugobj(tb, "lookup root fs"));
511
512 *root = NULL;
513
514 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
515 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
516 int id = mnt_fs_get_parent_id(fs);
517
518 if (!*root || id < root_id) {
519 *root = fs;
520 root_id = id;
521 }
522 }
523
524 return *root ? 0 : -EINVAL;
525 }
526
527 /**
528 * mnt_table_next_child_fs:
529 * @tb: mountinfo file (/proc/self/mountinfo)
530 * @itr: iterator
531 * @parent: parental FS
532 * @chld: returns the next child filesystem
533 *
534 * Note that filesystems are returned in the order of mounting (according to
535 * IDs in /proc/self/mountinfo).
536 *
537 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
538 */
539 int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
540 struct libmnt_fs *parent, struct libmnt_fs **chld)
541 {
542 struct libmnt_fs *fs;
543 int parent_id, lastchld_id = 0, chld_id = 0;
544
545 if (!tb || !itr || !parent || !is_mountinfo(tb))
546 return -EINVAL;
547
548 DBG(TAB, ul_debugobj(tb, "lookup next child of '%s'",
549 mnt_fs_get_target(parent)));
550
551 parent_id = mnt_fs_get_id(parent);
552
553 /* get ID of the previously returned child */
554 if (itr->head && itr->p != itr->head) {
555 MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents);
556 lastchld_id = mnt_fs_get_id(fs);
557 }
558
559 *chld = NULL;
560
561 mnt_reset_iter(itr, MNT_ITER_FORWARD);
562 while(mnt_table_next_fs(tb, itr, &fs) == 0) {
563 int id;
564
565 if (mnt_fs_get_parent_id(fs) != parent_id)
566 continue;
567
568 id = mnt_fs_get_id(fs);
569
570 /* avoid an infinite loop. This only happens in rare cases
571 * such as in early userspace when the rootfs is its own parent */
572 if (id == parent_id)
573 continue;
574
575 if ((!lastchld_id || id > lastchld_id) &&
576 (!*chld || id < chld_id)) {
577 *chld = fs;
578 chld_id = id;
579 }
580 }
581
582 if (!*chld)
583 return 1; /* end of iterator */
584
585 /* set the iterator to the @chld for the next call */
586 mnt_table_set_iter(tb, itr, *chld);
587
588 return 0;
589 }
590
591 /**
592 * mnt_table_next_fs:
593 * @tb: tab pointer
594 * @itr: iterator
595 * @fs: returns the next tab entry
596 *
597 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
598 *
599 * Example:
600 * <informalexample>
601 * <programlisting>
602 * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
603 * const char *dir = mnt_fs_get_target(fs);
604 * printf("mount point: %s\n", dir);
605 * }
606 * </programlisting>
607 * </informalexample>
608 *
609 * lists all mountpoints from fstab in reverse order.
610 */
611 int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs)
612 {
613 int rc = 1;
614
615 assert(tb);
616 assert(itr);
617 assert(fs);
618
619 if (!tb || !itr || !fs)
620 return -EINVAL;
621 *fs = NULL;
622
623 if (!itr->head)
624 MNT_ITER_INIT(itr, &tb->ents);
625 if (itr->p != itr->head) {
626 MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents);
627 rc = 0;
628 }
629
630 return rc;
631 }
632
633 /**
634 * mnt_table_first_fs:
635 * @tb: tab pointer
636 * @fs: returns the first tab entry
637 *
638 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
639 */
640 int mnt_table_first_fs(struct libmnt_table *tb, struct libmnt_fs **fs)
641 {
642 assert(tb);
643 assert(fs);
644
645 if (!tb || !fs)
646 return -EINVAL;
647 if (list_empty(&tb->ents))
648 return 1;
649 *fs = list_first_entry(&tb->ents, struct libmnt_fs, ents);
650 return 0;
651 }
652
653 /**
654 * mnt_table_last_fs:
655 * @tb: tab pointer
656 * @fs: returns the last tab entry
657 *
658 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
659 */
660 int mnt_table_last_fs(struct libmnt_table *tb, struct libmnt_fs **fs)
661 {
662 assert(tb);
663 assert(fs);
664
665 if (!tb || !fs)
666 return -EINVAL;
667 if (list_empty(&tb->ents))
668 return 1;
669 *fs = list_last_entry(&tb->ents, struct libmnt_fs, ents);
670 return 0;
671 }
672
673 /**
674 * mnt_table_find_next_fs:
675 * @tb: table
676 * @itr: iterator
677 * @match_func: function returning 1 or 0
678 * @userdata: extra data for match_func
679 * @fs: returns pointer to the next matching table entry
680 *
681 * This function allows searching in @tb.
682 *
683 * Returns: negative number in case of error, 1 at end of table or 0 o success.
684 */
685 int mnt_table_find_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
686 int (*match_func)(struct libmnt_fs *, void *), void *userdata,
687 struct libmnt_fs **fs)
688 {
689 if (!tb || !itr || !fs || !match_func)
690 return -EINVAL;
691
692 DBG(TAB, ul_debugobj(tb, "lookup next fs"));
693
694 if (!itr->head)
695 MNT_ITER_INIT(itr, &tb->ents);
696
697 do {
698 if (itr->p != itr->head)
699 MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents);
700 else
701 break; /* end */
702
703 if (match_func(*fs, userdata))
704 return 0;
705 } while(1);
706
707 *fs = NULL;
708 return 1;
709 }
710
711 static int mnt_table_move_parent(struct libmnt_table *tb, int oldid, int newid)
712 {
713 struct libmnt_iter itr;
714 struct libmnt_fs *fs;
715
716 assert(tb);
717
718 if (!tb)
719 return -EINVAL;
720 if (list_empty(&tb->ents))
721 return 0;
722
723 DBG(TAB, ul_debugobj(tb, "moving parent ID from %d -> %d", oldid, newid));
724 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
725
726 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
727 if (fs->parent == oldid)
728 fs->parent = newid;
729 }
730 return 0;
731 }
732
733 /**
734 * mnt_table_uniq_fs:
735 * @tb: table
736 * @flags: MNT_UNIQ_*
737 * @cmp: function to compare filesystems
738 *
739 * This function de-duplicate the @tb, but does not change order of the
740 * filesystems. The @cmp function has to return 0 if the filesystems are
741 * equal, otherwise non-zero.
742 *
743 * The default is to keep in the table later mounted filesystems (function uses
744 * backward mode iterator).
745 *
746 * @MNT_UNIQ_FORWARD: remove later mounted filesystems
747 * @MNT_UNIQ_KEEPTREE: keep parent->id relation ship stil valid
748 *
749 * Returns: negative number in case of error, or 0 o success.
750 */
751 int mnt_table_uniq_fs(struct libmnt_table *tb, int flags,
752 int (*cmp)(struct libmnt_table *,
753 struct libmnt_fs *,
754 struct libmnt_fs *))
755 {
756 struct libmnt_iter itr;
757 struct libmnt_fs *fs;
758 int direction = MNT_ITER_BACKWARD;
759
760 assert(tb);
761 assert(cmp);
762
763 if (!tb || !cmp)
764 return -EINVAL;
765 if (list_empty(&tb->ents))
766 return 0;
767
768 if (flags & MNT_UNIQ_FORWARD)
769 direction = MNT_ITER_FORWARD;
770
771 DBG(TAB, ul_debugobj(tb, "de-duplicate"));
772 mnt_reset_iter(&itr, direction);
773
774 if ((flags & MNT_UNIQ_KEEPTREE) && !is_mountinfo(tb))
775 flags &= ~MNT_UNIQ_KEEPTREE;
776
777 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
778 int want = 1;
779 struct libmnt_iter xtr;
780 struct libmnt_fs *x;
781
782 mnt_reset_iter(&xtr, direction);
783 while (want && mnt_table_next_fs(tb, &xtr, &x) == 0) {
784 if (fs == x)
785 break;
786 want = cmp(tb, x, fs) != 0;
787 }
788
789 if (!want) {
790 if (flags & MNT_UNIQ_KEEPTREE)
791 mnt_table_move_parent(tb, mnt_fs_get_id(fs),
792 mnt_fs_get_parent_id(fs));
793
794 DBG(TAB, ul_debugobj(tb, "remove duplicate %s",
795 mnt_fs_get_target(fs)));
796 mnt_table_remove_fs(tb, fs);
797 }
798 }
799
800 return 0;
801 }
802
803 /**
804 * mnt_table_set_iter:
805 * @tb: tab pointer
806 * @itr: iterator
807 * @fs: tab entry
808 *
809 * Sets @iter to the position of @fs in the file @tb.
810 *
811 * Returns: 0 on success, negative number in case of error.
812 */
813 int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs)
814 {
815 assert(tb);
816 assert(itr);
817 assert(fs);
818
819 if (!tb || !itr || !fs)
820 return -EINVAL;
821
822 MNT_ITER_INIT(itr, &tb->ents);
823 itr->p = &fs->ents;
824
825 return 0;
826 }
827
828 /**
829 * mnt_table_find_mountpoint:
830 * @tb: tab pointer
831 * @path: directory
832 * @direction: MNT_ITER_{FORWARD,BACKWARD}
833 *
834 * Same as mnt_get_mountpoint(), except this function does not rely on
835 * st_dev numbers.
836 *
837 * Returns: a tab entry or NULL.
838 */
839 struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb,
840 const char *path,
841 int direction)
842 {
843 char *mnt;
844
845 if (!tb || !path || !*path)
846 return NULL;
847 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
848 return NULL;
849
850 DBG(TAB, ul_debugobj(tb, "lookup MOUNTPOINT: '%s'", path));
851
852 mnt = strdup(path);
853 if (!mnt)
854 return NULL;
855
856 do {
857 char *p;
858 struct libmnt_fs *fs;
859
860 fs = mnt_table_find_target(tb, mnt, direction);
861 if (fs) {
862 free(mnt);
863 return fs;
864 }
865
866 p = stripoff_last_component(mnt);
867 if (!p)
868 break;
869 } while (mnt && *(mnt + 1) != '\0');
870
871 free(mnt);
872 return mnt_table_find_target(tb, "/", direction);
873 }
874
875 /**
876 * mnt_table_find_target:
877 * @tb: tab pointer
878 * @path: mountpoint directory
879 * @direction: MNT_ITER_{FORWARD,BACKWARD}
880 *
881 * Try to lookup an entry in the given tab, three iterations are possible, the first
882 * with @path, the second with realpath(@path) and the third with realpath(@path)
883 * against realpath(fs->target). The 2nd and 3rd iterations are not performed when
884 * the @tb cache is not set (see mnt_table_set_cache()). If
885 * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any
886 * @fs->target found in @mtab (see mnt_resolve_target()).
887 *
888 * Returns: a tab entry or NULL.
889 */
890 struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *path, int direction)
891 {
892 struct libmnt_iter itr;
893 struct libmnt_fs *fs = NULL;
894 char *cn;
895
896 assert(tb);
897 assert(path);
898
899 if (!tb || !path || !*path)
900 return NULL;
901 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
902 return NULL;
903
904 DBG(TAB, ul_debugobj(tb, "lookup TARGET: '%s'", path));
905
906 /* native @target */
907 mnt_reset_iter(&itr, direction);
908 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
909 if (mnt_fs_streq_target(fs, path))
910 return fs;
911 }
912 if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
913 return NULL;
914
915 DBG(TAB, ul_debugobj(tb, "lookup canonical TARGET: '%s'", cn));
916
917 /* canonicalized paths in struct libmnt_table */
918 mnt_reset_iter(&itr, direction);
919 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
920 if (mnt_fs_streq_target(fs, cn))
921 return fs;
922 }
923
924 /* non-canonicaled path in struct libmnt_table
925 * -- note that mountpoint in /proc/self/mountinfo is already
926 * canonicalized by the kernel
927 */
928 mnt_reset_iter(&itr, direction);
929 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
930 char *p;
931
932 if (!fs->target
933 || mnt_fs_is_swaparea(fs)
934 || mnt_fs_is_kernel(fs)
935 || (*fs->target == '/' && *(fs->target + 1) == '\0'))
936 continue;
937
938 p = mnt_resolve_target(fs->target, tb->cache);
939 /* both canonicalized, strcmp() is fine here */
940 if (p && strcmp(cn, p) == 0)
941 return fs;
942 }
943 return NULL;
944 }
945
946 /**
947 * mnt_table_find_srcpath:
948 * @tb: tab pointer
949 * @path: source path (devname or dirname) or NULL
950 * @direction: MNT_ITER_{FORWARD,BACKWARD}
951 *
952 * Try to lookup an entry in the given tab, four iterations are possible, the first
953 * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..)
954 * from @path and the fourth with realpath(@path) against realpath(entry->srcpath).
955 *
956 * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
957 * set (see mnt_table_set_cache()).
958 *
959 * Note that NULL is a valid source path; it will be replaced with "none". The
960 * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
961 *
962 * Returns: a tab entry or NULL.
963 */
964 struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *path, int direction)
965 {
966 struct libmnt_iter itr;
967 struct libmnt_fs *fs = NULL;
968 int ntags = 0, nents;
969 char *cn;
970 const char *p;
971
972 assert(tb);
973 if (!tb || !path || !*path)
974 return NULL;
975 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
976 return NULL;
977
978 DBG(TAB, ul_debugobj(tb, "lookup SRCPATH: '%s'", path));
979
980 /* native paths */
981 mnt_reset_iter(&itr, direction);
982 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
983 if (mnt_fs_streq_srcpath(fs, path))
984 return fs;
985 if (mnt_fs_get_tag(fs, NULL, NULL) == 0)
986 ntags++;
987 }
988
989 if (!path || !tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
990 return NULL;
991
992 DBG(TAB, ul_debugobj(tb, "lookup canonical SRCPATH: '%s'", cn));
993
994 nents = mnt_table_get_nents(tb);
995
996 /* canonicalized paths in struct libmnt_table */
997 if (ntags < nents) {
998 mnt_reset_iter(&itr, direction);
999 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1000 if (mnt_fs_streq_srcpath(fs, cn))
1001 return fs;
1002 }
1003 }
1004
1005 /* evaluated tag */
1006 if (ntags) {
1007 int rc = mnt_cache_read_tags(tb->cache, cn);
1008
1009 mnt_reset_iter(&itr, direction);
1010
1011 if (rc == 0) {
1012 /* @path's TAGs are in the cache */
1013 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1014 const char *t, *v;
1015
1016 if (mnt_fs_get_tag(fs, &t, &v))
1017 continue;
1018
1019 if (mnt_cache_device_has_tag(tb->cache, cn, t, v))
1020 return fs;
1021 }
1022 } else if (rc < 0 && errno == EACCES) {
1023 /* @path is inaccessible, try evaluating all TAGs in @tb
1024 * by udev symlinks -- this could be expensive on systems
1025 * with a huge fstab/mtab */
1026 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1027 const char *t, *v, *x;
1028 if (mnt_fs_get_tag(fs, &t, &v))
1029 continue;
1030 x = mnt_resolve_tag(t, v, tb->cache);
1031
1032 /* both canonicalized, strcmp() is fine here */
1033 if (x && strcmp(x, cn) == 0)
1034 return fs;
1035 }
1036 }
1037 }
1038
1039 /* non-canonicalized paths in struct libmnt_table */
1040 if (ntags <= nents) {
1041 mnt_reset_iter(&itr, direction);
1042 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1043 if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs))
1044 continue;
1045 p = mnt_fs_get_srcpath(fs);
1046 if (p)
1047 p = mnt_resolve_path(p, tb->cache);
1048
1049 /* both canonicalized, strcmp() is fine here */
1050 if (p && strcmp(p, cn) == 0)
1051 return fs;
1052 }
1053 }
1054
1055 return NULL;
1056 }
1057
1058
1059 /**
1060 * mnt_table_find_tag:
1061 * @tb: tab pointer
1062 * @tag: tag name (e.g "LABEL", "UUID", ...)
1063 * @val: tag value
1064 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1065 *
1066 * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and
1067 * @val, for the second attempt the tag is evaluated (converted to the device
1068 * name) and mnt_table_find_srcpath() is performed. The second attempt is not
1069 * performed when @tb cache is not set (see mnt_table_set_cache()).
1070
1071 * Returns: a tab entry or NULL.
1072 */
1073 struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag,
1074 const char *val, int direction)
1075 {
1076 struct libmnt_iter itr;
1077 struct libmnt_fs *fs = NULL;
1078
1079 assert(tb);
1080 assert(tag);
1081 assert(val);
1082
1083 if (!tb || !tag || !*tag || !val)
1084 return NULL;
1085 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1086 return NULL;
1087
1088 DBG(TAB, ul_debugobj(tb, "lookup by TAG: %s %s", tag, val));
1089
1090 /* look up by TAG */
1091 mnt_reset_iter(&itr, direction);
1092 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1093 if (fs->tagname && fs->tagval &&
1094 strcmp(fs->tagname, tag) == 0 &&
1095 strcmp(fs->tagval, val) == 0)
1096 return fs;
1097 }
1098
1099 if (tb->cache) {
1100 /* look up by device name */
1101 char *cn = mnt_resolve_tag(tag, val, tb->cache);
1102 if (cn)
1103 return mnt_table_find_srcpath(tb, cn, direction);
1104 }
1105 return NULL;
1106 }
1107
1108 /**
1109 * mnt_table_find_source:
1110 * @tb: tab pointer
1111 * @source: TAG or path
1112 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1113 *
1114 * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care
1115 * about the @source format (device, LABEL, UUID, ...). This function parses
1116 * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath().
1117 *
1118 * Returns: a tab entry or NULL.
1119 */
1120 struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb,
1121 const char *source, int direction)
1122 {
1123 struct libmnt_fs *fs;
1124 char *t = NULL, *v = NULL;
1125
1126 assert(tb);
1127
1128 if (!tb)
1129 return NULL;
1130 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1131 return NULL;
1132
1133 DBG(TAB, ul_debugobj(tb, "lookup SOURCE: '%s'", source));
1134
1135 if (blkid_parse_tag_string(source, &t, &v) || !mnt_valid_tagname(t))
1136 fs = mnt_table_find_srcpath(tb, source, direction);
1137 else
1138 fs = mnt_table_find_tag(tb, t, v, direction);
1139
1140 free(t);
1141 free(v);
1142
1143 return fs;
1144 }
1145
1146 /**
1147 * mnt_table_find_pair
1148 * @tb: tab pointer
1149 * @source: TAG or path
1150 * @target: mountpoint
1151 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1152 *
1153 * This function is implemented by mnt_fs_match_source() and
1154 * mnt_fs_match_target() functions. It means that this is more expensive than
1155 * others mnt_table_find_* function, because every @tab entry is fully evaluated.
1156 *
1157 * Returns: a tab entry or NULL.
1158 */
1159 struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *source,
1160 const char *target, int direction)
1161 {
1162 struct libmnt_fs *fs = NULL;
1163 struct libmnt_iter itr;
1164
1165 assert(tb);
1166 assert(target);
1167
1168 if (!tb || !target || !*target || !source || !*source)
1169 return NULL;
1170 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1171 return NULL;
1172
1173 DBG(TAB, ul_debugobj(tb, "lookup SOURCE: %s TARGET: %s", source, target));
1174
1175 mnt_reset_iter(&itr, direction);
1176 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1177
1178 if (mnt_fs_match_target(fs, target, tb->cache) &&
1179 mnt_fs_match_source(fs, source, tb->cache))
1180 return fs;
1181 }
1182
1183 return NULL;
1184 }
1185
1186 /**
1187 * mnt_table_find_devno
1188 * @tb: /proc/self/mountinfo
1189 * @devno: device number
1190 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1191 *
1192 * Note that zero could be a valid device number for the root pseudo filesystem (e.g.
1193 * tmpfs).
1194 *
1195 * Returns: a tab entry or NULL.
1196 */
1197 struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb,
1198 dev_t devno, int direction)
1199 {
1200 struct libmnt_fs *fs = NULL;
1201 struct libmnt_iter itr;
1202
1203 assert(tb);
1204
1205 if (!tb)
1206 return NULL;
1207 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1208 return NULL;
1209
1210 DBG(TAB, ul_debugobj(tb, "lookup DEVNO: %d", (int) devno));
1211
1212 mnt_reset_iter(&itr, direction);
1213
1214 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1215 if (mnt_fs_get_devno(fs) == devno)
1216 return fs;
1217 }
1218
1219 return NULL;
1220 }
1221
1222 /*
1223 * tb: /proc/self/mountinfo
1224 * fs: filesystem
1225 * mountflags: MS_BIND or 0
1226 * fsroot: fs-root that will probably be used in the mountinfo file
1227 * for @fs after mount(2)
1228 *
1229 * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
1230 *
1231 * Returns: entry from @tb that will be used as a source for @fs if the @fs is
1232 * bindmount.
1233 *
1234 * Don't export to library API!
1235 */
1236 struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
1237 struct libmnt_fs *fs,
1238 unsigned long mountflags,
1239 char **fsroot)
1240 {
1241 char *root = NULL, *mnt = NULL;
1242 const char *fstype;
1243 struct libmnt_fs *src_fs = NULL;
1244
1245 assert(fs);
1246 assert(fsroot);
1247
1248 DBG(TAB, ul_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs)));
1249
1250 fstype = mnt_fs_get_fstype(fs);
1251
1252 if (tb && (mountflags & MS_BIND)) {
1253 const char *src, *src_root;
1254 char *xsrc = NULL;
1255
1256 DBG(TAB, ul_debug("fs-root for bind"));
1257
1258 src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache);
1259 if (src)
1260 mnt = mnt_get_mountpoint(src);
1261 if (mnt)
1262 root = mnt_get_fs_root(src, mnt);
1263
1264 if (xsrc && !tb->cache) {
1265 free(xsrc);
1266 src = NULL;
1267 }
1268 if (!mnt)
1269 goto err;
1270
1271 src_fs = mnt_table_find_target(tb, mnt, MNT_ITER_BACKWARD);
1272 if (!src_fs) {
1273 DBG(TAB, ul_debug("not found '%s' in mountinfo -- using default", mnt));
1274 goto dflt;
1275 }
1276
1277 /* on btrfs the subvolume is used as fs-root in
1278 * /proc/self/mountinfo, so we have to get the original subvolume
1279 * name from src_fs and prepend the subvolume name to the
1280 * fs-root path
1281 */
1282 src_root = mnt_fs_get_root(src_fs);
1283 if (src_root && !startswith(root, src_root)) {
1284 size_t sz = strlen(root) + strlen(src_root) + 1;
1285 char *tmp = malloc(sz);
1286
1287 if (!tmp)
1288 goto err;
1289 snprintf(tmp, sz, "%s%s", src_root, root);
1290 free(root);
1291 root = tmp;
1292 }
1293 }
1294
1295 /*
1296 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
1297 */
1298 else if (fstype && !strcmp(fstype, "btrfs")) {
1299 char *vol = NULL, *p;
1300 size_t sz, volsz = 0;
1301
1302 if (mnt_fs_get_option(fs, "subvol", &vol, &volsz))
1303 goto dflt;
1304
1305 DBG(TAB, ul_debug("setting FS root: btrfs subvol"));
1306
1307 sz = volsz;
1308 if (*vol != '/')
1309 sz++;
1310 root = malloc(sz + 1);
1311 if (!root)
1312 goto err;
1313 p = root;
1314 if (*vol != '/')
1315 *p++ = '/';
1316 memcpy(p, vol, volsz);
1317 *(root + sz) = '\0';
1318 }
1319 dflt:
1320 if (!root) {
1321 root = strdup("/");
1322 if (!root)
1323 goto err;
1324 }
1325 *fsroot = root;
1326
1327 DBG(TAB, ul_debug("FS root result: %s", root));
1328
1329 free(mnt);
1330 return src_fs;
1331 err:
1332 free(root);
1333 free(mnt);
1334 return NULL;
1335 }
1336
1337 /**
1338 * mnt_table_is_fs__mounted:
1339 * @tb: /proc/self/mountinfo file
1340 * @fstab_fs: /etc/fstab entry
1341 *
1342 * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
1343 * ignored. This function explicitly compares the source, target and root of the
1344 * filesystems.
1345 *
1346 * Note that source and target are canonicalized only if a cache for @tb is
1347 * defined (see mnt_table_set_cache()). The target canonicalization may
1348 * trigger automount on autofs mountpoints!
1349 *
1350 * Don't use it if you want to know if a device is mounted, just use
1351 * mnt_table_find_source() on the device.
1352 *
1353 * This function is designed mostly for "mount -a".
1354 *
1355 * Returns: 0 or 1
1356 */
1357 int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
1358 {
1359 struct libmnt_iter itr;
1360 struct libmnt_fs *fs;
1361
1362 char *root = NULL;
1363 const char *src = NULL, *tgt = NULL;
1364 char *xtgt = NULL;
1365 int rc = 0;
1366 dev_t devno = 0;
1367
1368 assert(tb);
1369 assert(fstab_fs);
1370
1371 DBG(FS, ul_debugobj(fstab_fs, "is FS mounted? [target=%s]",
1372 mnt_fs_get_target(fstab_fs)));
1373
1374 if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_is_empty(tb)) {
1375 DBG(FS, ul_debugobj(fstab_fs, "- ignore (swap or no data)"));
1376 return 0;
1377 }
1378
1379 if (is_mountinfo(tb)) {
1380 /* @tb is mountinfo, so we can try to use fs-roots */
1381 struct libmnt_fs *rootfs;
1382 int flags = 0;
1383
1384 if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0)
1385 flags = MS_BIND;
1386
1387 rootfs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root);
1388 if (rootfs)
1389 src = mnt_fs_get_srcpath(rootfs);
1390 }
1391
1392 if (!src)
1393 src = mnt_fs_get_source(fstab_fs);
1394
1395 if (src && tb->cache && !mnt_fs_is_pseudofs(fstab_fs))
1396 src = mnt_resolve_spec(src, tb->cache);
1397
1398 if (src && root) {
1399 struct stat st;
1400
1401 devno = mnt_fs_get_devno(fstab_fs);
1402 if (!devno && stat(src, &st) == 0 && S_ISBLK(st.st_mode))
1403 devno = st.st_rdev;
1404 }
1405
1406 tgt = mnt_fs_get_target(fstab_fs);
1407
1408 if (!tgt || !src) {
1409 DBG(FS, ul_debugobj(fstab_fs, "- ignore (no source/target)"));
1410 goto done;
1411 }
1412 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
1413
1414 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
1415
1416 int eq = mnt_fs_streq_srcpath(fs, src);
1417
1418 if (!eq && devno && mnt_fs_get_devno(fs) == devno)
1419 eq = 1;
1420
1421 if (!eq) {
1422 /* The source does not match. Maybe the source is a loop
1423 * device backing file.
1424 */
1425 uint64_t offset = 0;
1426 char *val;
1427 size_t len;
1428 int flags;
1429
1430 if (!mnt_fs_is_kernel(fs) ||
1431 !mnt_fs_get_srcpath(fs) ||
1432 !startswith(mnt_fs_get_srcpath(fs), "/dev/loop"))
1433 continue; /* does not look like loopdev */
1434
1435 if (mnt_fs_get_option(fstab_fs, "offset", &val, &len) == 0 &&
1436 mnt_parse_offset(val, len, &offset)) {
1437 DBG(FS, ul_debugobj(fstab_fs, "failed to parse offset="));
1438 continue;
1439 } else
1440 flags = LOOPDEV_FL_OFFSET;
1441
1442 #if __linux__
1443 if (loopdev_is_used(mnt_fs_get_srcpath(fs), src, offset, flags))
1444 break;
1445 #endif
1446 }
1447
1448 if (root) {
1449 const char *r = mnt_fs_get_root(fs);
1450 if (!r || strcmp(r, root) != 0)
1451 continue;
1452 }
1453
1454 /*
1455 * Compare target, try to minimize the number of situations when we
1456 * need to canonicalize the path to avoid readlink() on
1457 * mountpoints.
1458 */
1459 if (!xtgt) {
1460 if (mnt_fs_streq_target(fs, tgt))
1461 break;
1462 if (tb->cache)
1463 xtgt = mnt_resolve_path(tgt, tb->cache);
1464 }
1465 if (xtgt && mnt_fs_streq_target(fs, xtgt))
1466 break;
1467 }
1468
1469 if (fs)
1470 rc = 1; /* success */
1471 done:
1472 free(root);
1473
1474 DBG(TAB, ul_debugobj(tb, "mnt_table_is_fs_mounted: %s [rc=%d]", src, rc));
1475 return rc;
1476 }
1477
1478 #ifdef TEST_PROGRAM
1479 #include "pathnames.h"
1480
1481 static int parser_errcb(struct libmnt_table *tb, const char *filename, int line)
1482 {
1483 fprintf(stderr, "%s:%d: parse error\n", filename, line);
1484
1485 return 1; /* all errors are recoverable -- this is the default */
1486 }
1487
1488 struct libmnt_table *create_table(const char *file, int comments)
1489 {
1490 struct libmnt_table *tb;
1491
1492 if (!file)
1493 return NULL;
1494 tb = mnt_new_table();
1495 if (!tb)
1496 goto err;
1497
1498 mnt_table_enable_comments(tb, comments);
1499 mnt_table_set_parser_errcb(tb, parser_errcb);
1500
1501 if (mnt_table_parse_file(tb, file) != 0)
1502 goto err;
1503 return tb;
1504 err:
1505 fprintf(stderr, "%s: parsing failed\n", file);
1506 mnt_unref_table(tb);
1507 return NULL;
1508 }
1509
1510 int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[])
1511 {
1512 struct libmnt_table *tb;
1513 struct libmnt_fs *fs;
1514 int rc = -1;
1515
1516 tb = create_table(argv[1], FALSE);
1517 if (!tb)
1518 return -1;
1519
1520 fs = mnt_table_find_target(tb, "/", MNT_ITER_FORWARD);
1521 if (!fs)
1522 goto done;
1523
1524 printf("ORIGINAL:\n");
1525 mnt_fs_print_debug(fs, stdout);
1526
1527 fs = mnt_copy_fs(NULL, fs);
1528 if (!fs)
1529 goto done;
1530
1531 printf("COPY:\n");
1532 mnt_fs_print_debug(fs, stdout);
1533 mnt_unref_fs(fs);
1534 rc = 0;
1535 done:
1536 mnt_unref_table(tb);
1537 return rc;
1538 }
1539
1540 int test_parse(struct libmnt_test *ts, int argc, char *argv[])
1541 {
1542 struct libmnt_table *tb = NULL;
1543 struct libmnt_iter *itr = NULL;
1544 struct libmnt_fs *fs;
1545 int rc = -1;
1546 int parse_comments = FALSE;
1547
1548 if (argc == 3 && !strcmp(argv[2], "--comments"))
1549 parse_comments = TRUE;
1550
1551 tb = create_table(argv[1], parse_comments);
1552 if (!tb)
1553 return -1;
1554
1555 itr = mnt_new_iter(MNT_ITER_FORWARD);
1556 if (!itr)
1557 goto done;
1558
1559 if (mnt_table_get_intro_comment(tb))
1560 fprintf(stdout, "Initial comment:\n\"%s\"\n",
1561 mnt_table_get_intro_comment(tb));
1562
1563 while(mnt_table_next_fs(tb, itr, &fs) == 0)
1564 mnt_fs_print_debug(fs, stdout);
1565
1566 if (mnt_table_get_trailing_comment(tb))
1567 fprintf(stdout, "Trailing comment:\n\"%s\"\n",
1568 mnt_table_get_trailing_comment(tb));
1569 rc = 0;
1570 done:
1571 mnt_free_iter(itr);
1572 mnt_unref_table(tb);
1573 return rc;
1574 }
1575
1576 int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr)
1577 {
1578 struct libmnt_table *tb;
1579 struct libmnt_fs *fs = NULL;
1580 struct libmnt_cache *mpc = NULL;
1581 const char *file, *find, *what;
1582 int rc = -1;
1583
1584 if (argc != 4) {
1585 fprintf(stderr, "try --help\n");
1586 return -EINVAL;
1587 }
1588
1589 file = argv[1], find = argv[2], what = argv[3];
1590
1591 tb = create_table(file, FALSE);
1592 if (!tb)
1593 goto done;
1594
1595 /* create a cache for canonicalized paths */
1596 mpc = mnt_new_cache();
1597 if (!mpc)
1598 goto done;
1599 mnt_table_set_cache(tb, mpc);
1600 mnt_unref_cache(mpc);
1601
1602 if (strcasecmp(find, "source") == 0)
1603 fs = mnt_table_find_source(tb, what, dr);
1604 else if (strcasecmp(find, "target") == 0)
1605 fs = mnt_table_find_target(tb, what, dr);
1606
1607 if (!fs)
1608 fprintf(stderr, "%s: not found %s '%s'\n", file, find, what);
1609 else {
1610 mnt_fs_print_debug(fs, stdout);
1611 rc = 0;
1612 }
1613 done:
1614 mnt_unref_table(tb);
1615 return rc;
1616 }
1617
1618 int test_find_bw(struct libmnt_test *ts, int argc, char *argv[])
1619 {
1620 return test_find(ts, argc, argv, MNT_ITER_BACKWARD);
1621 }
1622
1623 int test_find_fw(struct libmnt_test *ts, int argc, char *argv[])
1624 {
1625 return test_find(ts, argc, argv, MNT_ITER_FORWARD);
1626 }
1627
1628 int test_find_pair(struct libmnt_test *ts, int argc, char *argv[])
1629 {
1630 struct libmnt_table *tb;
1631 struct libmnt_fs *fs;
1632 struct libmnt_cache *mpc = NULL;
1633 int rc = -1;
1634
1635 tb = create_table(argv[1], FALSE);
1636 if (!tb)
1637 return -1;
1638 mpc = mnt_new_cache();
1639 if (!mpc)
1640 goto done;
1641 mnt_table_set_cache(tb, mpc);
1642 mnt_unref_cache(mpc);
1643
1644 fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD);
1645 if (!fs)
1646 goto done;
1647
1648 mnt_fs_print_debug(fs, stdout);
1649 rc = 0;
1650 done:
1651 mnt_unref_table(tb);
1652 return rc;
1653 }
1654
1655 int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
1656 {
1657 struct libmnt_table *tb;
1658 struct libmnt_fs *fs;
1659 struct libmnt_cache *mpc = NULL;
1660 int rc = -1;
1661
1662 tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
1663 if (!tb)
1664 return -1;
1665 mpc = mnt_new_cache();
1666 if (!mpc)
1667 goto done;
1668 mnt_table_set_cache(tb, mpc);
1669 mnt_unref_cache(mpc);
1670
1671 fs = mnt_table_find_mountpoint(tb, argv[1], MNT_ITER_BACKWARD);
1672 if (!fs)
1673 goto done;
1674
1675 mnt_fs_print_debug(fs, stdout);
1676 rc = 0;
1677 done:
1678 mnt_unref_table(tb);
1679 return rc;
1680 }
1681
1682 static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[])
1683 {
1684 struct libmnt_table *tb = NULL, *fstab = NULL;
1685 struct libmnt_fs *fs;
1686 struct libmnt_iter *itr = NULL;
1687 struct libmnt_cache *mpc = NULL;
1688 int rc;
1689
1690 tb = mnt_new_table_from_file("/proc/self/mountinfo");
1691 if (!tb) {
1692 fprintf(stderr, "failed to parse mountinfo\n");
1693 return -1;
1694 }
1695
1696 fstab = create_table(argv[1], FALSE);
1697 if (!fstab)
1698 goto done;
1699
1700 itr = mnt_new_iter(MNT_ITER_FORWARD);
1701 if (!itr)
1702 goto done;
1703
1704 mpc = mnt_new_cache();
1705 if (!mpc)
1706 goto done;
1707 mnt_table_set_cache(tb, mpc);
1708 mnt_unref_cache(mpc);
1709
1710 while(mnt_table_next_fs(fstab, itr, &fs) == 0) {
1711 if (mnt_table_is_fs_mounted(tb, fs))
1712 printf("%s already mounted on %s\n",
1713 mnt_fs_get_source(fs),
1714 mnt_fs_get_target(fs));
1715 else
1716 printf("%s not mounted on %s\n",
1717 mnt_fs_get_source(fs),
1718 mnt_fs_get_target(fs));
1719 }
1720
1721 rc = 0;
1722 done:
1723 mnt_unref_table(tb);
1724 mnt_unref_table(fstab);
1725 mnt_free_iter(itr);
1726 return rc;
1727 }
1728
1729 /* returns 0 if @a and @b targets are the same */
1730 static int test_uniq_cmp(struct libmnt_table *tb __attribute__((__unused__)),
1731 struct libmnt_fs *a,
1732 struct libmnt_fs *b)
1733 {
1734 assert(a);
1735 assert(b);
1736
1737 return mnt_fs_streq_target(a, mnt_fs_get_target(b)) ? 0 : 1;
1738 }
1739
1740 static int test_uniq(struct libmnt_test *ts, int argc, char *argv[])
1741 {
1742 struct libmnt_table *tb;
1743 int rc = -1;
1744
1745 if (argc != 2) {
1746 fprintf(stderr, "try --help\n");
1747 return -EINVAL;
1748 }
1749
1750 tb = create_table(argv[1], FALSE);
1751 if (!tb)
1752 goto done;
1753
1754 if (mnt_table_uniq_fs(tb, 0, test_uniq_cmp) == 0) {
1755 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
1756 struct libmnt_fs *fs;
1757 if (!itr)
1758 goto done;
1759 while (mnt_table_next_fs(tb, itr, &fs) == 0)
1760 mnt_fs_print_debug(fs, stdout);
1761 mnt_free_iter(itr);
1762 rc = 0;
1763 }
1764 done:
1765 mnt_unref_table(tb);
1766 return rc;
1767 }
1768
1769 int main(int argc, char *argv[])
1770 {
1771 struct libmnt_test tss[] = {
1772 { "--parse", test_parse, "<file> [--comments] parse and print tab" },
1773 { "--find-forward", test_find_fw, "<file> <source|target> <string>" },
1774 { "--find-backward", test_find_bw, "<file> <source|target> <string>" },
1775 { "--uniq-target", test_uniq, "<file>" },
1776 { "--find-pair", test_find_pair, "<file> <source> <target>" },
1777 { "--find-mountpoint", test_find_mountpoint, "<path>" },
1778 { "--copy-fs", test_copy_fs, "<file> copy root FS from the file" },
1779 { "--is-mounted", test_is_mounted, "<fstab> check what from <file> are already mounted" },
1780 { NULL }
1781 };
1782
1783 return mnt_run_test(tss, argc, argv);
1784 }
1785
1786 #endif /* TEST_PROGRAM */