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