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