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