]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/tab.c
libmount: (docs) add reference to v2.33 and v2.34
[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;
312 return append_string(&tb->comm_intro, comm);
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;
353 return append_string(&tb->comm_tail, comm);
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 *
491 * This function inncrements reference to @fs. Don't forget to use
492 * mnt_unref_fs() after mnt_table_insert_fs() if you want to keep the @fs
493 * referenced by the table only.
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
KZ
592 * @tb: mountinfo file (/proc/self/mountinfo)
593 * @root: returns pointer to the root filesystem (/)
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
KZ
611 struct libmnt_iter itr;
612 struct libmnt_fs *fs;
26b4f9e4
KZ
613 int root_id = 0;
614
6c373810 615 if (!tb || !root || !is_mountinfo(tb))
59ae0ddd 616 return -EINVAL;
26b4f9e4 617
83a78332 618 DBG(TAB, ul_debugobj(tb, "lookup root fs"));
20aae9d3 619
2e358a90
TB
620 *root = NULL;
621
1a339790 622 /* get smallest possible ID from the table */
26b4f9e4 623 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
68164f6c 624 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
26b4f9e4 625 int id = mnt_fs_get_parent_id(fs);
26b4f9e4
KZ
626
627 if (!*root || id < root_id) {
628 *root = fs;
629 root_id = id;
630 }
631 }
632
1a339790
KZ
633 /* go to the root node by "parent_id -> id" relation */
634 while (*root) {
635 struct libmnt_fs *x = get_parent_fs(tb, *root);
636 if (!x || x == *root)
637 break;
638 DBG(TAB, ul_debugobj(tb, " messy mountinfo, walk to %s", mnt_fs_get_target(x)));
639 *root = x;
640 }
641
6c373810 642 return *root ? 0 : -EINVAL;
26b4f9e4
KZ
643}
644
645/**
68164f6c 646 * mnt_table_next_child_fs:
26b4f9e4 647 * @tb: mountinfo file (/proc/self/mountinfo)
3d735589 648 * @itr: iterator
26b4f9e4
KZ
649 * @parent: parental FS
650 * @chld: returns the next child filesystem
651 *
d58b3157 652 * Note that filesystems are returned in the order of mounting (according to
26b4f9e4
KZ
653 * IDs in /proc/self/mountinfo).
654 *
d58b3157 655 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
26b4f9e4 656 */
68164f6c
KZ
657int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
658 struct libmnt_fs *parent, struct libmnt_fs **chld)
26b4f9e4 659{
68164f6c 660 struct libmnt_fs *fs;
26b4f9e4
KZ
661 int parent_id, lastchld_id = 0, chld_id = 0;
662
6c373810 663 if (!tb || !itr || !parent || !is_mountinfo(tb))
59ae0ddd 664 return -EINVAL;
26b4f9e4 665
83a78332 666 DBG(TAB, ul_debugobj(tb, "lookup next child of '%s'",
3f31a959 667 mnt_fs_get_target(parent)));
20aae9d3 668
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) {
68164f6c 673 MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents);
26b4f9e4
KZ
674 lastchld_id = mnt_fs_get_id(fs);
675 }
676
677 *chld = NULL;
678
679 mnt_reset_iter(itr, MNT_ITER_FORWARD);
68164f6c 680 while(mnt_table_next_fs(tb, itr, &fs) == 0) {
26b4f9e4
KZ
681 int id;
682
683 if (mnt_fs_get_parent_id(fs) != parent_id)
684 continue;
685
686 id = mnt_fs_get_id(fs);
687
d58b3157 688 /* avoid an infinite loop. This only happens in rare cases
bf91904c
DR
689 * such as in early userspace when the rootfs is its own parent */
690 if (id == parent_id)
691 continue;
692
26b4f9e4
KZ
693 if ((!lastchld_id || id > lastchld_id) &&
694 (!*chld || id < chld_id)) {
695 *chld = fs;
696 chld_id = id;
697 }
698 }
699
6c373810 700 if (!*chld)
26b4f9e4
KZ
701 return 1; /* end of iterator */
702
703 /* set the iterator to the @chld for the next call */
68164f6c 704 mnt_table_set_iter(tb, itr, *chld);
26b4f9e4
KZ
705
706 return 0;
707}
708
6bd8b7a7 709/**
68164f6c 710 * mnt_table_next_fs:
6bd8b7a7
KZ
711 * @tb: tab pointer
712 * @itr: iterator
713 * @fs: returns the next tab entry
714 *
d58b3157 715 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
6bd8b7a7 716 *
0f32f1e2 717 * Example:
3d735589
KZ
718 * <informalexample>
719 * <programlisting>
68164f6c 720 * while(mnt_table_next_fs(tb, itr, &fs) == 0) {
6bd8b7a7
KZ
721 * const char *dir = mnt_fs_get_target(fs);
722 * printf("mount point: %s\n", dir);
723 * }
3d735589
KZ
724 * </programlisting>
725 * </informalexample>
0f32f1e2 726 *
d58b3157 727 * lists all mountpoints from fstab in reverse order.
6bd8b7a7 728 */
68164f6c 729int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs)
6bd8b7a7 730{
59ae0ddd 731 int rc = 1;
3fca8422 732
6bd8b7a7 733 if (!tb || !itr || !fs)
59ae0ddd 734 return -EINVAL;
0532ba1d
KZ
735 *fs = NULL;
736
6bd8b7a7
KZ
737 if (!itr->head)
738 MNT_ITER_INIT(itr, &tb->ents);
739 if (itr->p != itr->head) {
68164f6c 740 MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents);
3fca8422 741 rc = 0;
6bd8b7a7
KZ
742 }
743
3fca8422
KZ
744 return rc;
745}
746
686a6467
KZ
747/**
748 * mnt_table_first_fs:
749 * @tb: tab pointer
750 * @fs: returns the first tab entry
751 *
752 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
753 */
754int mnt_table_first_fs(struct libmnt_table *tb, struct libmnt_fs **fs)
755{
686a6467
KZ
756 if (!tb || !fs)
757 return -EINVAL;
758 if (list_empty(&tb->ents))
759 return 1;
760 *fs = list_first_entry(&tb->ents, struct libmnt_fs, ents);
761 return 0;
762}
763
764/**
765 * mnt_table_last_fs:
766 * @tb: tab pointer
767 * @fs: returns the last tab entry
768 *
769 * Returns: 0 on success, negative number in case of error or 1 at the end of list.
770 */
771int mnt_table_last_fs(struct libmnt_table *tb, struct libmnt_fs **fs)
772{
686a6467
KZ
773 if (!tb || !fs)
774 return -EINVAL;
775 if (list_empty(&tb->ents))
776 return 1;
777 *fs = list_last_entry(&tb->ents, struct libmnt_fs, ents);
778 return 0;
779}
780
3fca8422 781/**
68164f6c 782 * mnt_table_find_next_fs:
3fca8422
KZ
783 * @tb: table
784 * @itr: iterator
d58b3157 785 * @match_func: function returning 1 or 0
3d735589 786 * @userdata: extra data for match_func
3fca8422
KZ
787 * @fs: returns pointer to the next matching table entry
788 *
d58b3157 789 * This function allows searching in @tb.
3fca8422 790 *
59ae0ddd 791 * Returns: negative number in case of error, 1 at end of table or 0 o success.
3fca8422 792 */
68164f6c
KZ
793int mnt_table_find_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
794 int (*match_func)(struct libmnt_fs *, void *), void *userdata,
795 struct libmnt_fs **fs)
3fca8422
KZ
796{
797 if (!tb || !itr || !fs || !match_func)
59ae0ddd 798 return -EINVAL;
3fca8422 799
83a78332 800 DBG(TAB, ul_debugobj(tb, "lookup next fs"));
20aae9d3 801
3fca8422
KZ
802 if (!itr->head)
803 MNT_ITER_INIT(itr, &tb->ents);
804
805 do {
806 if (itr->p != itr->head)
68164f6c 807 MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents);
3fca8422
KZ
808 else
809 break; /* end */
810
3fca8422
KZ
811 if (match_func(*fs, userdata))
812 return 0;
813 } while(1);
814
26b4f9e4 815 *fs = NULL;
6bd8b7a7
KZ
816 return 1;
817}
818
51fffa7b
KZ
819static int mnt_table_move_parent(struct libmnt_table *tb, int oldid, int newid)
820{
821 struct libmnt_iter itr;
822 struct libmnt_fs *fs;
823
51fffa7b
KZ
824 if (!tb)
825 return -EINVAL;
826 if (list_empty(&tb->ents))
827 return 0;
828
83a78332 829 DBG(TAB, ul_debugobj(tb, "moving parent ID from %d -> %d", oldid, newid));
51fffa7b
KZ
830 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
831
832 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
833 if (fs->parent == oldid)
834 fs->parent = newid;
835 }
836 return 0;
837}
838
839/**
840 * mnt_table_uniq_fs:
841 * @tb: table
cbe92027 842 * @flags: MNT_UNIQ_*
51fffa7b
KZ
843 * @cmp: function to compare filesystems
844 *
845 * This function de-duplicate the @tb, but does not change order of the
846 * filesystems. The @cmp function has to return 0 if the filesystems are
847 * equal, otherwise non-zero.
848 *
849 * The default is to keep in the table later mounted filesystems (function uses
850 * backward mode iterator).
851 *
852 * @MNT_UNIQ_FORWARD: remove later mounted filesystems
9e930041 853 * @MNT_UNIQ_KEEPTREE: keep parent->id relationship still valid
51fffa7b
KZ
854 *
855 * Returns: negative number in case of error, or 0 o success.
856 */
857int mnt_table_uniq_fs(struct libmnt_table *tb, int flags,
858 int (*cmp)(struct libmnt_table *,
859 struct libmnt_fs *,
860 struct libmnt_fs *))
861{
862 struct libmnt_iter itr;
863 struct libmnt_fs *fs;
864 int direction = MNT_ITER_BACKWARD;
865
51fffa7b
KZ
866 if (!tb || !cmp)
867 return -EINVAL;
868 if (list_empty(&tb->ents))
869 return 0;
870
871 if (flags & MNT_UNIQ_FORWARD)
872 direction = MNT_ITER_FORWARD;
873
83a78332 874 DBG(TAB, ul_debugobj(tb, "de-duplicate"));
51fffa7b
KZ
875 mnt_reset_iter(&itr, direction);
876
877 if ((flags & MNT_UNIQ_KEEPTREE) && !is_mountinfo(tb))
878 flags &= ~MNT_UNIQ_KEEPTREE;
879
880 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
881 int want = 1;
882 struct libmnt_iter xtr;
883 struct libmnt_fs *x;
884
885 mnt_reset_iter(&xtr, direction);
886 while (want && mnt_table_next_fs(tb, &xtr, &x) == 0) {
887 if (fs == x)
888 break;
889 want = cmp(tb, x, fs) != 0;
890 }
891
892 if (!want) {
893 if (flags & MNT_UNIQ_KEEPTREE)
894 mnt_table_move_parent(tb, mnt_fs_get_id(fs),
895 mnt_fs_get_parent_id(fs));
896
83a78332 897 DBG(TAB, ul_debugobj(tb, "remove duplicate %s",
51fffa7b
KZ
898 mnt_fs_get_target(fs)));
899 mnt_table_remove_fs(tb, fs);
900 }
901 }
902
903 return 0;
904}
905
6bd8b7a7 906/**
68164f6c 907 * mnt_table_set_iter:
6bd8b7a7
KZ
908 * @tb: tab pointer
909 * @itr: iterator
910 * @fs: tab entry
911 *
912 * Sets @iter to the position of @fs in the file @tb.
913 *
59ae0ddd 914 * Returns: 0 on success, negative number in case of error.
6bd8b7a7 915 */
68164f6c 916int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs)
6bd8b7a7 917{
6bd8b7a7 918 if (!tb || !itr || !fs)
59ae0ddd 919 return -EINVAL;
6bd8b7a7 920
9d5eb4c4
TH
921 if (fs->tab != tb)
922 return -ENOENT;
923
6bd8b7a7
KZ
924 MNT_ITER_INIT(itr, &tb->ents);
925 itr->p = &fs->ents;
926
927 return 0;
928}
929
dcc15ce5
KZ
930/**
931 * mnt_table_find_mountpoint:
932 * @tb: tab pointer
933 * @path: directory
934 * @direction: MNT_ITER_{FORWARD,BACKWARD}
935 *
d58b3157 936 * Same as mnt_get_mountpoint(), except this function does not rely on
dcc15ce5
KZ
937 * st_dev numbers.
938 *
939 * Returns: a tab entry or NULL.
940 */
941struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb,
942 const char *path,
943 int direction)
944{
945 char *mnt;
80c31a0b 946 struct stat st;
dcc15ce5 947
7ba207e7 948 if (!tb || !path || !*path)
dcc15ce5 949 return NULL;
4cd271ad
KZ
950 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
951 return NULL;
dcc15ce5 952
83a78332 953 DBG(TAB, ul_debugobj(tb, "lookup MOUNTPOINT: '%s'", path));
dcc15ce5 954
80c31a0b
KZ
955 if (mnt_stat_mountpoint(path, &st))
956 return NULL;
957
dcc15ce5
KZ
958 mnt = strdup(path);
959 if (!mnt)
960 return NULL;
961
962 do {
963 char *p;
964 struct libmnt_fs *fs;
965
966 fs = mnt_table_find_target(tb, mnt, direction);
967 if (fs) {
968 free(mnt);
969 return fs;
970 }
971
972 p = stripoff_last_component(mnt);
41510d26 973 if (!p)
dcc15ce5
KZ
974 break;
975 } while (mnt && *(mnt + 1) != '\0');
976
977 free(mnt);
978 return mnt_table_find_target(tb, "/", direction);
979}
980
6bd8b7a7 981/**
68164f6c 982 * mnt_table_find_target:
6bd8b7a7
KZ
983 * @tb: tab pointer
984 * @path: mountpoint directory
985 * @direction: MNT_ITER_{FORWARD,BACKWARD}
986 *
d58b3157
OO
987 * Try to lookup an entry in the given tab, three iterations are possible, the first
988 * with @path, the second with realpath(@path) and the third with realpath(@path)
0382ba32
ER
989 * against realpath(fs->target). The 2nd and 3rd iterations are not performed when
990 * the @tb cache is not set (see mnt_table_set_cache()). If
991 * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any
992 * @fs->target found in @mtab (see mnt_resolve_target()).
6bd8b7a7 993 *
192c6aad 994 * Returns: a tab entry or NULL.
6bd8b7a7 995 */
68164f6c 996struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *path, int direction)
6bd8b7a7 997{
68164f6c
KZ
998 struct libmnt_iter itr;
999 struct libmnt_fs *fs = NULL;
6bd8b7a7
KZ
1000 char *cn;
1001
7ba207e7 1002 if (!tb || !path || !*path)
59ae0ddd 1003 return NULL;
4cd271ad
KZ
1004 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1005 return NULL;
59ae0ddd 1006
83a78332 1007 DBG(TAB, ul_debugobj(tb, "lookup TARGET: '%s'", path));
6bd8b7a7
KZ
1008
1009 /* native @target */
1010 mnt_reset_iter(&itr, direction);
de60d08e 1011 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
6699e742 1012 if (mnt_fs_streq_target(fs, path))
6bd8b7a7 1013 return fs;
de60d08e 1014 }
2238214d
KZ
1015
1016 /* try absolute path */
1017 if (is_relative_path(path) && (cn = absolute_path(path))) {
1018 DBG(TAB, ul_debugobj(tb, "lookup absolute TARGET: '%s'", cn));
1019 mnt_reset_iter(&itr, direction);
1020 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
1021 if (mnt_fs_streq_target(fs, cn)) {
1022 free(cn);
1023 return fs;
1024 }
1025 }
1026 free(cn);
1027 }
1028
6bd8b7a7
KZ
1029 if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
1030 return NULL;
1031
83a78332 1032 DBG(TAB, ul_debugobj(tb, "lookup canonical TARGET: '%s'", cn));
7ba207e7 1033
68164f6c 1034 /* canonicalized paths in struct libmnt_table */
6bd8b7a7 1035 mnt_reset_iter(&itr, direction);
68164f6c 1036 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
6699e742 1037 if (mnt_fs_streq_target(fs, cn))
6bd8b7a7
KZ
1038 return fs;
1039 }
1040
9e930041 1041 /* non-canonical path in struct libmnt_table
fa705b54 1042 * -- note that mountpoint in /proc/self/mountinfo is already
d58b3157 1043 * canonicalized by the kernel
fa705b54 1044 */
6bd8b7a7 1045 mnt_reset_iter(&itr, direction);
68164f6c 1046 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
6bd8b7a7 1047 char *p;
9dd75aa6 1048
fa705b54
KZ
1049 if (!fs->target
1050 || mnt_fs_is_swaparea(fs)
1051 || mnt_fs_is_kernel(fs)
1052 || (*fs->target == '/' && *(fs->target + 1) == '\0'))
6bd8b7a7 1053 continue;
9dd75aa6 1054
0382ba32 1055 p = mnt_resolve_target(fs->target, tb->cache);
6699e742
KZ
1056 /* both canonicalized, strcmp() is fine here */
1057 if (p && strcmp(cn, p) == 0)
6bd8b7a7
KZ
1058 return fs;
1059 }
1060 return NULL;
1061}
1062
1063/**
68164f6c 1064 * mnt_table_find_srcpath:
6bd8b7a7 1065 * @tb: tab pointer
f12aac6e 1066 * @path: source path (devname or dirname) or NULL
6bd8b7a7
KZ
1067 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1068 *
d58b3157
OO
1069 * Try to lookup an entry in the given tab, four iterations are possible, the first
1070 * with @path, the second with realpath(@path), the third with tags (LABEL, UUID, ..)
1071 * from @path and the fourth with realpath(@path) against realpath(entry->srcpath).
6bd8b7a7 1072 *
d58b3157 1073 * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not
68164f6c 1074 * set (see mnt_table_set_cache()).
6bd8b7a7 1075 *
443be113
PV
1076 * For btrfs returns tab entry for default id.
1077 *
d0ce7c07
DR
1078 * Note that NULL is a valid source path; it will be replaced with "none". The
1079 * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems.
f12aac6e 1080 *
192c6aad 1081 * Returns: a tab entry or NULL.
6bd8b7a7 1082 */
68164f6c 1083struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *path, int direction)
6bd8b7a7 1084{
68164f6c
KZ
1085 struct libmnt_iter itr;
1086 struct libmnt_fs *fs = NULL;
a2f17bb2 1087 int ntags = 0, nents;
6bd8b7a7
KZ
1088 char *cn;
1089 const char *p;
1090
7ba207e7 1091 if (!tb || !path || !*path)
dcc15ce5 1092 return NULL;
4cd271ad
KZ
1093 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1094 return NULL;
6bd8b7a7 1095
83a78332 1096 DBG(TAB, ul_debugobj(tb, "lookup SRCPATH: '%s'", path));
6bd8b7a7
KZ
1097
1098 /* native paths */
1099 mnt_reset_iter(&itr, direction);
443be113 1100
68164f6c 1101 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
443be113
PV
1102
1103 if (mnt_fs_streq_srcpath(fs, path)) {
1104#ifdef HAVE_BTRFS_SUPPORT
7bb9ee99 1105 if (fs->fstype && !strcmp(fs->fstype, "btrfs")) {
443be113 1106 uint64_t default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs));
443be113
PV
1107 char *val;
1108 size_t len;
1109
7bb9ee99 1110 if (default_id == UINT64_MAX)
443be113 1111 DBG(TAB, ul_debug("not found btrfs volume setting"));
443be113 1112
7bb9ee99
KZ
1113 else if (mnt_fs_get_option(fs, "subvolid", &val, &len) == 0) {
1114 uint64_t subvol_id;
1115
443be113
PV
1116 if (mnt_parse_offset(val, len, &subvol_id)) {
1117 DBG(TAB, ul_debugobj(tb, "failed to parse subvolid="));
1118 continue;
1119 }
1120 if (subvol_id != default_id)
1121 continue;
1122 }
1123 }
1124#endif /* HAVE_BTRFS_SUPPORT */
6bd8b7a7 1125 return fs;
443be113 1126 }
ab8c6e05
KZ
1127 if (mnt_fs_get_tag(fs, NULL, NULL) == 0)
1128 ntags++;
6bd8b7a7
KZ
1129 }
1130
f12aac6e 1131 if (!path || !tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
6bd8b7a7
KZ
1132 return NULL;
1133
83a78332 1134 DBG(TAB, ul_debugobj(tb, "lookup canonical SRCPATH: '%s'", cn));
7ba207e7 1135
a2f17bb2
KZ
1136 nents = mnt_table_get_nents(tb);
1137
68164f6c 1138 /* canonicalized paths in struct libmnt_table */
a2f17bb2 1139 if (ntags < nents) {
6bd8b7a7 1140 mnt_reset_iter(&itr, direction);
68164f6c 1141 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
ab8c6e05 1142 if (mnt_fs_streq_srcpath(fs, cn))
6bd8b7a7
KZ
1143 return fs;
1144 }
1145 }
1146
1147 /* evaluated tag */
3fca8422 1148 if (ntags) {
ba7232a1
KZ
1149 int rc = mnt_cache_read_tags(tb->cache, cn);
1150
6bd8b7a7 1151 mnt_reset_iter(&itr, direction);
6bd8b7a7 1152
ba7232a1 1153 if (rc == 0) {
3fca8422 1154 /* @path's TAGs are in the cache */
68164f6c 1155 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
3fca8422 1156 const char *t, *v;
6bd8b7a7 1157
3fca8422
KZ
1158 if (mnt_fs_get_tag(fs, &t, &v))
1159 continue;
1160
1161 if (mnt_cache_device_has_tag(tb->cache, cn, t, v))
1162 return fs;
1163 }
ba7232a1 1164 } else if (rc < 0 && errno == EACCES) {
d58b3157 1165 /* @path is inaccessible, try evaluating all TAGs in @tb
3fca8422 1166 * by udev symlinks -- this could be expensive on systems
d58b3157 1167 * with a huge fstab/mtab */
68164f6c 1168 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
3fca8422
KZ
1169 const char *t, *v, *x;
1170 if (mnt_fs_get_tag(fs, &t, &v))
1171 continue;
1172 x = mnt_resolve_tag(t, v, tb->cache);
6699e742
KZ
1173
1174 /* both canonicalized, strcmp() is fine here */
1175 if (x && strcmp(x, cn) == 0)
3fca8422
KZ
1176 return fs;
1177 }
6bd8b7a7
KZ
1178 }
1179 }
1180
68164f6c 1181 /* non-canonicalized paths in struct libmnt_table */
a2f17bb2 1182 if (ntags <= nents) {
6bd8b7a7 1183 mnt_reset_iter(&itr, direction);
68164f6c 1184 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
c70d9d76 1185 if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs))
9dd75aa6 1186 continue;
6bd8b7a7
KZ
1187 p = mnt_fs_get_srcpath(fs);
1188 if (p)
1189 p = mnt_resolve_path(p, tb->cache);
6699e742
KZ
1190
1191 /* both canonicalized, strcmp() is fine here */
1192 if (p && strcmp(p, cn) == 0)
6bd8b7a7
KZ
1193 return fs;
1194 }
1195 }
1196
1197 return NULL;
1198}
1199
1200
1201/**
68164f6c 1202 * mnt_table_find_tag:
6bd8b7a7
KZ
1203 * @tb: tab pointer
1204 * @tag: tag name (e.g "LABEL", "UUID", ...)
1205 * @val: tag value
1206 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1207 *
d58b3157 1208 * Try to lookup an entry in the given tab, the first attempt is to lookup by @tag and
6bd8b7a7 1209 * @val, for the second attempt the tag is evaluated (converted to the device
d58b3157 1210 * name) and mnt_table_find_srcpath() is performed. The second attempt is not
68164f6c 1211 * performed when @tb cache is not set (see mnt_table_set_cache()).
6bd8b7a7 1212
192c6aad 1213 * Returns: a tab entry or NULL.
6bd8b7a7 1214 */
68164f6c 1215struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag,
6bd8b7a7
KZ
1216 const char *val, int direction)
1217{
68164f6c
KZ
1218 struct libmnt_iter itr;
1219 struct libmnt_fs *fs = NULL;
6bd8b7a7 1220
7ba207e7 1221 if (!tb || !tag || !*tag || !val)
6bd8b7a7 1222 return NULL;
4cd271ad
KZ
1223 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1224 return NULL;
6bd8b7a7 1225
83a78332 1226 DBG(TAB, ul_debugobj(tb, "lookup by TAG: %s %s", tag, val));
6bd8b7a7
KZ
1227
1228 /* look up by TAG */
1229 mnt_reset_iter(&itr, direction);
68164f6c 1230 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
6bd8b7a7
KZ
1231 if (fs->tagname && fs->tagval &&
1232 strcmp(fs->tagname, tag) == 0 &&
1233 strcmp(fs->tagval, val) == 0)
1234 return fs;
1235 }
1236
1237 if (tb->cache) {
1238 /* look up by device name */
1239 char *cn = mnt_resolve_tag(tag, val, tb->cache);
1240 if (cn)
68164f6c 1241 return mnt_table_find_srcpath(tb, cn, direction);
6bd8b7a7
KZ
1242 }
1243 return NULL;
1244}
1245
2cd28fc8
SB
1246/**
1247 * mnt_table_find_target_with_option:
1248 * @tb: tab pointer
1249 * @path: mountpoint directory
1250 * @option: option name (e.g "subvol", "subvolid", ...)
1251 * @val: option value or NULL
1252 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1253 *
1254 * Try to lookup an entry in the given tab that matches combination of @path
1255 * and @option. In difference to mnt_table_find_target(), only @path iteration
1256 * is done. No lookup by device name, no canonicalization.
1257 *
1258 * Returns: a tab entry or NULL.
1259 *
1260 * Since: 2.28
1261 */
1262struct libmnt_fs *mnt_table_find_target_with_option(
1263 struct libmnt_table *tb, const char *path,
1264 const char *option, const char *val, int direction)
1265{
1266 struct libmnt_iter itr;
1267 struct libmnt_fs *fs = NULL;
1268 char *optval = NULL;
1269 size_t optvalsz = 0, valsz = val ? strlen(val) : 0;
1270
1271 if (!tb || !path || !*path || !option || !*option || !val)
1272 return NULL;
1273 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1274 return NULL;
1275
1276 DBG(TAB, ul_debugobj(tb, "lookup TARGET: '%s' with OPTION %s %s", path, option, val));
1277
1278 /* look up by native @target with OPTION */
1279 mnt_reset_iter(&itr, direction);
1280 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
1281 if (mnt_fs_streq_target(fs, path)
1282 && mnt_fs_get_option(fs, option, &optval, &optvalsz) == 0
1283 && (!val || (optvalsz == valsz
1284 && strncmp(optval, val, optvalsz) == 0)))
1285 return fs;
1286 }
1287 return NULL;
1288}
1289
6bd8b7a7 1290/**
68164f6c 1291 * mnt_table_find_source:
6bd8b7a7
KZ
1292 * @tb: tab pointer
1293 * @source: TAG or path
3d735589 1294 * @direction: MNT_ITER_{FORWARD,BACKWARD}
6bd8b7a7 1295 *
d58b3157
OO
1296 * This is a high-level API for mnt_table_find_{srcpath,tag}. You needn't care
1297 * about the @source format (device, LABEL, UUID, ...). This function parses
1298 * the @source and calls mnt_table_find_tag() or mnt_table_find_srcpath().
6bd8b7a7 1299 *
192c6aad 1300 * Returns: a tab entry or NULL.
6bd8b7a7 1301 */
f12aac6e
KZ
1302struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb,
1303 const char *source, int direction)
6bd8b7a7 1304{
2c6b25f0
KZ
1305 struct libmnt_fs *fs;
1306 char *t = NULL, *v = NULL;
6bd8b7a7 1307
f12aac6e 1308 if (!tb)
6bd8b7a7 1309 return NULL;
4cd271ad
KZ
1310 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1311 return NULL;
6bd8b7a7 1312
83a78332 1313 DBG(TAB, ul_debugobj(tb, "lookup SOURCE: '%s'", source));
6bd8b7a7 1314
2c6b25f0 1315 if (blkid_parse_tag_string(source, &t, &v) || !mnt_valid_tagname(t))
68164f6c 1316 fs = mnt_table_find_srcpath(tb, source, direction);
2c6b25f0
KZ
1317 else
1318 fs = mnt_table_find_tag(tb, t, v, direction);
1319
1320 free(t);
1321 free(v);
6bd8b7a7
KZ
1322
1323 return fs;
1324}
1325
059c696f 1326/**
68164f6c 1327 * mnt_table_find_pair
059c696f
KZ
1328 * @tb: tab pointer
1329 * @source: TAG or path
1330 * @target: mountpoint
1331 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1332 *
1333 * This function is implemented by mnt_fs_match_source() and
d58b3157 1334 * mnt_fs_match_target() functions. It means that this is more expensive than
68164f6c 1335 * others mnt_table_find_* function, because every @tab entry is fully evaluated.
059c696f
KZ
1336 *
1337 * Returns: a tab entry or NULL.
1338 */
68164f6c 1339struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *source,
f12aac6e 1340 const char *target, int direction)
059c696f 1341{
68164f6c
KZ
1342 struct libmnt_fs *fs = NULL;
1343 struct libmnt_iter itr;
059c696f 1344
7ba207e7 1345 if (!tb || !target || !*target || !source || !*source)
059c696f 1346 return NULL;
4cd271ad
KZ
1347 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1348 return NULL;
059c696f 1349
83a78332 1350 DBG(TAB, ul_debugobj(tb, "lookup SOURCE: %s TARGET: %s", source, target));
059c696f
KZ
1351
1352 mnt_reset_iter(&itr, direction);
68164f6c 1353 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
059c696f
KZ
1354
1355 if (mnt_fs_match_target(fs, target, tb->cache) &&
1356 mnt_fs_match_source(fs, source, tb->cache))
1357 return fs;
1358 }
1359
1360 return NULL;
1361}
1362
677ff053
KZ
1363/**
1364 * mnt_table_find_devno
1365 * @tb: /proc/self/mountinfo
1366 * @devno: device number
1367 * @direction: MNT_ITER_{FORWARD,BACKWARD}
1368 *
d58b3157 1369 * Note that zero could be a valid device number for the root pseudo filesystem (e.g.
677ff053
KZ
1370 * tmpfs).
1371 *
1372 * Returns: a tab entry or NULL.
1373 */
1374struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb,
1375 dev_t devno, int direction)
1376{
1377 struct libmnt_fs *fs = NULL;
1378 struct libmnt_iter itr;
1379
677ff053
KZ
1380 if (!tb)
1381 return NULL;
4cd271ad
KZ
1382 if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD)
1383 return NULL;
677ff053 1384
83a78332 1385 DBG(TAB, ul_debugobj(tb, "lookup DEVNO: %d", (int) devno));
677ff053
KZ
1386
1387 mnt_reset_iter(&itr, direction);
1388
1389 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
1390 if (mnt_fs_get_devno(fs) == devno)
1391 return fs;
1392 }
1393
1394 return NULL;
1395}
1396
cc06a01e
KZ
1397static char *remove_mountpoint_from_path(const char *path, const char *mnt)
1398{
1399 char *res;
1400 const char *p;
1401 size_t sz;
1402
1403 sz = strlen(mnt);
1404 p = sz > 1 ? path + sz : path;
1405
1406 res = *p ? strdup(p) : strdup("/");
1407 DBG(UTILS, ul_debug("%s fs-root is %s", path, res));
1408 return res;
1409}
1410
9aa3173b
KZ
1411#ifdef HAVE_BTRFS_SUPPORT
1412static int get_btrfs_fs_root(struct libmnt_table *tb, struct libmnt_fs *fs, char **root)
1413{
1414 char *vol = NULL, *p;
1415 size_t sz, volsz = 0;
1416
1417 DBG(BTRFS, ul_debug("lookup for btrfs FS root"));
1418 *root = NULL;
1419
1420 if (mnt_fs_get_option(fs, "subvolid", &vol, &volsz) == 0) {
1421 char *target;
1422 struct libmnt_fs *f;
1423 char subvolidstr[sizeof(stringify_value(UINT64_MAX))];
1424
1425 DBG(BTRFS, ul_debug(" found subvolid=%s, checking", vol));
1426
1427 assert (volsz + 1 < sizeof(stringify_value(UINT64_MAX)));
1428 memcpy(subvolidstr, vol, volsz);
1429 subvolidstr[volsz] = '\0';
1430
1431 target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache);
1432 if (!target)
1433 goto err;
1434
c59409b9 1435 DBG(BTRFS, ul_debug(" trying target=%s subvolid=%s", target, subvolidstr));
9aa3173b
KZ
1436 f = mnt_table_find_target_with_option(tb, target,
1437 "subvolid", subvolidstr,
1438 MNT_ITER_BACKWARD);
1439 if (!tb->cache)
1440 free(target);
1441 if (!f)
1442 goto not_found;
1443
1444 /* Instead of set of BACKREF queries constructing subvol path
1445 * corresponding to a particular subvolid, use the one in
1446 * mountinfo. Kernel keeps subvol path up to date.
1447 */
1448 if (mnt_fs_get_option(f, "subvol", &vol, &volsz) != 0)
1449 goto not_found;
1450
1451 } else if (mnt_fs_get_option(fs, "subvol", &vol, &volsz) != 0) {
1452 /* If fstab entry does not contain "subvol", we have to
1453 * check, whether btrfs has default subvolume defined.
1454 */
1455 uint64_t default_id;
1456 char *target;
1457 struct libmnt_fs *f;
1458 char default_id_str[sizeof(stringify_value(UINT64_MAX))];
1459
1460 DBG(BTRFS, ul_debug(" subvolid/subvol not found, checking default"));
1461
1462 default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs));
1463 if (default_id == UINT64_MAX)
1464 goto not_found;
1465
1466 /* Volume has default subvolume. Check if it matches to
1467 * the one in mountinfo.
1468 *
1469 * Only kernel >= 4.2 reports subvolid. On older
1470 * kernels, there is no reasonable way to detect which
1471 * subvolume was mounted.
1472 */
1473 target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache);
1474 if (!target)
1475 goto err;
1476
1477 snprintf(default_id_str, sizeof(default_id_str), "%llu",
1478 (unsigned long long int) default_id);
1479
c59409b9 1480 DBG(BTRFS, ul_debug(" trying target=%s default subvolid=%s",
9aa3173b
KZ
1481 target, default_id_str));
1482
1483 f = mnt_table_find_target_with_option(tb, target,
1484 "subvolid", default_id_str,
1485 MNT_ITER_BACKWARD);
1486 if (!tb->cache)
1487 free(target);
1488 if (!f)
1489 goto not_found;
1490
1491 /* Instead of set of BACKREF queries constructing
1492 * subvol path, use the one in mountinfo. Kernel does
1493 * the evaluation for us.
1494 */
1495 DBG(BTRFS, ul_debug("setting FS root: btrfs default subvolid = %s",
1496 default_id_str));
1497
1498 if (mnt_fs_get_option(f, "subvol", &vol, &volsz) != 0)
1499 goto not_found;
1500 }
1501
1502 DBG(BTRFS, ul_debug(" using subvol=%s", vol));
1503 sz = volsz;
1504 if (*vol != '/')
1505 sz++;
1506 *root = malloc(sz + 1);
1507 if (!*root)
1508 goto err;
1509 p = *root;
1510 if (*vol != '/')
1511 *p++ = '/';
1512 memcpy(p, vol, volsz);
1513 *(*root + sz) = '\0';
1514 return 0;
1515
1516not_found:
1517 DBG(BTRFS, ul_debug(" not found btrfs volume setting"));
1518 return 1;
1519err:
1520 DBG(BTRFS, ul_debug(" error on btrfs volume setting evaluation"));
1521 return errno ? -errno : -1;
1522}
1523#endif /* HAVE_BTRFS_SUPPORT */
1524
76d4fba2
AA
1525static const char *get_cifs_unc_subdir_path (const char *unc)
1526{
1527 /*
1528 * 1 or more slash: %*[/]
1529 * 1 or more non-slash: %*[^/]
1530 * number of byte read: %n
1531 */
1532 int share_end = 0;
1533 int r = sscanf(unc, "%*[/]%*[^/]%*[/]%*[^/]%n", &share_end);
1534 if (r == EOF || share_end == 0)
1535 return NULL;
1536 return unc + share_end;
1537}
1538
5c60a0ea 1539/*
7293e97a
KZ
1540 * tb: /proc/self/mountinfo
1541 * fs: filesystem
1542 * mountflags: MS_BIND or 0
d58b3157 1543 * fsroot: fs-root that will probably be used in the mountinfo file
5c60a0ea
KZ
1544 * for @fs after mount(2)
1545 *
1546 * For btrfs subvolumes this function returns NULL, but @fsroot properly set.
1547 *
e9cd2e2b
KZ
1548 * If @tb is NULL then defaults to '/'.
1549 *
5c60a0ea
KZ
1550 * Returns: entry from @tb that will be used as a source for @fs if the @fs is
1551 * bindmount.
7293e97a
KZ
1552 *
1553 * Don't export to library API!
5c60a0ea
KZ
1554 */
1555struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
1556 struct libmnt_fs *fs,
1557 unsigned long mountflags,
1558 char **fsroot)
1559{
cc06a01e
KZ
1560 char *root = NULL;
1561 const char *mnt = NULL;
5c60a0ea
KZ
1562 struct libmnt_fs *src_fs = NULL;
1563
5c60a0ea
KZ
1564 assert(fs);
1565 assert(fsroot);
1566
83a78332 1567 DBG(TAB, ul_debug("lookup fs-root for '%s'", mnt_fs_get_source(fs)));
5c60a0ea 1568
c4c66355 1569 if (tb && (mountflags & MS_BIND)) {
5c60a0ea 1570 const char *src, *src_root;
16b8db49 1571 char *xsrc = NULL;
5c60a0ea 1572
83a78332 1573 DBG(TAB, ul_debug("fs-root for bind"));
5c60a0ea 1574
16b8db49 1575 src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache);
cc06a01e 1576 if (src) {
624e147b 1577 struct libmnt_fs *f = mnt_table_find_mountpoint(tb,
cc06a01e 1578 src, MNT_ITER_BACKWARD);
624e147b
SK
1579 if (f)
1580 mnt = mnt_fs_get_target(f);
cc06a01e 1581 }
16b8db49 1582 if (mnt)
cc06a01e 1583 root = remove_mountpoint_from_path(src, mnt);
5c60a0ea 1584
16b8db49
KZ
1585 if (xsrc && !tb->cache) {
1586 free(xsrc);
1587 src = NULL;
1588 }
5c60a0ea
KZ
1589 if (!mnt)
1590 goto err;
1591
5c60a0ea
KZ
1592 src_fs = mnt_table_find_target(tb, mnt, MNT_ITER_BACKWARD);
1593 if (!src_fs) {
83a78332 1594 DBG(TAB, ul_debug("not found '%s' in mountinfo -- using default", mnt));
5c60a0ea
KZ
1595 goto dflt;
1596 }
1597
352740e8 1598 /* It's possible that fstab_fs source is subdirectory on btrfs
2cd28fc8 1599 * subvolume or another bind mount. For example:
352740e8
KZ
1600 *
1601 * /dev/sdc /mnt/test btrfs subvol=/anydir
2cd28fc8 1602 * /dev/sdc /mnt/test btrfs defaults
352740e8
KZ
1603 * /mnt/test/foo /mnt/test2 auto bind
1604 *
1605 * in this case, the root for /mnt/test2 will be /anydir/foo on
1606 * /dev/sdc. It means we have to compose the final root from
1607 * root and src_root.
5c60a0ea
KZ
1608 */
1609 src_root = mnt_fs_get_root(src_fs);
352740e8
KZ
1610
1611 DBG(FS, ul_debugobj(fs, "source root: %s, source FS root: %s", root, src_root));
1612
5c60a0ea 1613 if (src_root && !startswith(root, src_root)) {
352740e8
KZ
1614 if (strcmp(root, "/") == 0) {
1615 free(root);
1616 root = strdup(src_root);
1617 if (!root)
1618 goto err;
1619 } else {
1620 char *tmp;
1621 if (asprintf(&tmp, "%s%s", src_root, root) < 0)
1622 goto err;
1623 free(root);
1624 root = tmp;
1625 }
5c60a0ea
KZ
1626 }
1627 }
1628
5a971329 1629#ifdef HAVE_BTRFS_SUPPORT
5c60a0ea
KZ
1630 /*
1631 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
1632 */
0b5f75e4
RM
1633 else if (tb && fs->fstype &&
1634 (!strcmp(fs->fstype, "btrfs") || !strcmp(fs->fstype, "auto"))) {
9aa3173b 1635 if (get_btrfs_fs_root(tb, fs, &root) < 0)
5c60a0ea 1636 goto err;
5c60a0ea 1637 }
5a971329
KZ
1638#endif /* HAVE_BTRFS_SUPPORT */
1639
5c60a0ea
KZ
1640dflt:
1641 if (!root) {
1642 root = strdup("/");
1643 if (!root)
1644 goto err;
1645 }
1646 *fsroot = root;
1647
83a78332 1648 DBG(TAB, ul_debug("FS root result: %s", root));
5c60a0ea 1649
5c60a0ea
KZ
1650 return src_fs;
1651err:
1652 free(root);
5c60a0ea
KZ
1653 return NULL;
1654}
1655
1656/**
37290a53 1657 * mnt_table_is_fs_mounted:
5c60a0ea
KZ
1658 * @tb: /proc/self/mountinfo file
1659 * @fstab_fs: /etc/fstab entry
1660 *
975e14bd 1661 * Checks if the @fstab_fs entry is already in the @tb table. The "swap" is
d58b3157 1662 * ignored. This function explicitly compares the source, target and root of the
975e14bd
KZ
1663 * filesystems.
1664 *
1665 * Note that source and target are canonicalized only if a cache for @tb is
1666 * defined (see mnt_table_set_cache()). The target canonicalization may
d58b3157 1667 * trigger automount on autofs mountpoints!
975e14bd
KZ
1668 *
1669 * Don't use it if you want to know if a device is mounted, just use
d58b3157 1670 * mnt_table_find_source() on the device.
975e14bd
KZ
1671 *
1672 * This function is designed mostly for "mount -a".
5c60a0ea 1673 *
5c60a0ea
KZ
1674 * Returns: 0 or 1
1675 */
1676int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
1677{
449a7646
KZ
1678 struct libmnt_iter itr;
1679 struct libmnt_fs *fs;
1680
5c60a0ea 1681 char *root = NULL;
7966cbba 1682 char *src2 = NULL;
975e14bd
KZ
1683 const char *src = NULL, *tgt = NULL;
1684 char *xtgt = NULL;
7293e97a 1685 int rc = 0;
d828dfdf 1686 dev_t devno = 0;
5c60a0ea 1687
dc7c3d65 1688 DBG(FS, ul_debugobj(fstab_fs, "mnt_table_is_fs_mounted: target=%s, source=%s",
d58b9706
KZ
1689 mnt_fs_get_target(fstab_fs),
1690 mnt_fs_get_source(fstab_fs)));
ae978c4d 1691
a2f17bb2 1692 if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_is_empty(tb)) {
83a78332 1693 DBG(FS, ul_debugobj(fstab_fs, "- ignore (swap or no data)"));
5c60a0ea 1694 return 0;
ae978c4d 1695 }
5c60a0ea 1696
7293e97a
KZ
1697 if (is_mountinfo(tb)) {
1698 /* @tb is mountinfo, so we can try to use fs-roots */
c9f63764 1699 struct libmnt_fs *rootfs;
7293e97a 1700 int flags = 0;
5c60a0ea 1701
b5cc2323
KZ
1702 if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0 ||
1703 mnt_fs_get_option(fstab_fs, "rbind", NULL, NULL) == 0)
7293e97a
KZ
1704 flags = MS_BIND;
1705
c9f63764 1706 rootfs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root);
7966cbba
N
1707 if (rootfs) {
1708 const char *fstype = mnt_fs_get_fstype(rootfs);
1709
c9f63764 1710 src = mnt_fs_get_srcpath(rootfs);
7966cbba
N
1711 if (fstype && strncmp(fstype, "nfs", 3) == 0 && root) {
1712 /* NFS stores the root at the end of the source */
1713 src = src2 = strappend(src, root);
1714 free(root);
1715 root = NULL;
1716 }
1717 }
7293e97a
KZ
1718 }
1719
975e14bd 1720 if (!src)
14f66ad6 1721 src = mnt_fs_get_source(fstab_fs);
5c60a0ea 1722
975e14bd
KZ
1723 if (src && tb->cache && !mnt_fs_is_pseudofs(fstab_fs))
1724 src = mnt_resolve_spec(src, tb->cache);
1725
d828dfdf
KZ
1726 if (src && root) {
1727 struct stat st;
1728
1729 devno = mnt_fs_get_devno(fstab_fs);
1730 if (!devno && stat(src, &st) == 0 && S_ISBLK(st.st_mode))
1731 devno = st.st_rdev;
1732 }
1733
975e14bd 1734 tgt = mnt_fs_get_target(fstab_fs);
5c60a0ea 1735
ae978c4d 1736 if (!tgt || !src) {
83a78332 1737 DBG(FS, ul_debugobj(fstab_fs, "- ignore (no source/target)"));
449a7646 1738 goto done;
ae978c4d 1739 }
449a7646 1740 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
5c60a0ea 1741
dc7c3d65 1742 DBG(FS, ul_debugobj(fstab_fs, "mnt_table_is_fs_mounted: src=%s, tgt=%s, root=%s", src, tgt, root));
352740e8 1743
449a7646 1744 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
975e14bd 1745
d828dfdf
KZ
1746 int eq = mnt_fs_streq_srcpath(fs, src);
1747
1748 if (!eq && devno && mnt_fs_get_devno(fs) == devno)
1749 eq = 1;
1750
1751 if (!eq) {
b2cbe99f
KZ
1752 /* The source does not match. Maybe the source is a loop
1753 * device backing file.
1754 */
1755 uint64_t offset = 0;
1756 char *val;
1757 size_t len;
dc7c3d65 1758 int flags = 0;
b2cbe99f 1759
dc7c3d65 1760 if (!mnt_fs_get_srcpath(fs) ||
b2cbe99f
KZ
1761 !startswith(mnt_fs_get_srcpath(fs), "/dev/loop"))
1762 continue; /* does not look like loopdev */
1763
dc7c3d65
KZ
1764 if (mnt_fs_get_option(fstab_fs, "offset", &val, &len) == 0) {
1765 if (mnt_parse_offset(val, len, &offset)) {
1766 DBG(FS, ul_debugobj(fstab_fs, "failed to parse offset="));
1767 continue;
1768 }
b2cbe99f 1769 flags = LOOPDEV_FL_OFFSET;
dc7c3d65 1770 }
b2cbe99f 1771
d58b9706 1772 DBG(FS, ul_debugobj(fs, "checking for loop: src=%s", mnt_fs_get_srcpath(fs)));
e624bc55 1773#if __linux__
74a4705a 1774 if (!loopdev_is_used(mnt_fs_get_srcpath(fs), src, offset, 0, flags))
d58b9706
KZ
1775 continue;
1776
1777 DBG(FS, ul_debugobj(fs, "used loop"));
e624bc55 1778#endif
b2cbe99f
KZ
1779 }
1780
449a7646 1781 if (root) {
3e44e75d
KZ
1782 const char *fstype = mnt_fs_get_fstype(fs);
1783
1784 if (fstype && strcmp(fstype, "cifs") == 0) {
76d4fba2
AA
1785 const char *unc_subdir = get_cifs_unc_subdir_path(src);
1786 const char *path_on_fs = mnt_fs_get_root(fs);
1787 if (!unc_subdir || !path_on_fs || !streq_paths(unc_subdir, path_on_fs))
1788 continue;
1789 } else {
1790 const char *r = mnt_fs_get_root(fs);
1791 if (!r || strcmp(r, root) != 0)
1792 continue;
1793 }
449a7646 1794 }
5c60a0ea 1795
449a7646 1796 /*
d58b3157 1797 * Compare target, try to minimize the number of situations when we
449a7646
KZ
1798 * need to canonicalize the path to avoid readlink() on
1799 * mountpoints.
1800 */
1801 if (!xtgt) {
1802 if (mnt_fs_streq_target(fs, tgt))
5c60a0ea 1803 break;
449a7646
KZ
1804 if (tb->cache)
1805 xtgt = mnt_resolve_path(tgt, tb->cache);
5c60a0ea 1806 }
449a7646
KZ
1807 if (xtgt && mnt_fs_streq_target(fs, xtgt))
1808 break;
5c60a0ea
KZ
1809 }
1810
449a7646
KZ
1811 if (fs)
1812 rc = 1; /* success */
1813done:
5c60a0ea 1814 free(root);
b2cbe99f 1815
83a78332 1816 DBG(TAB, ul_debugobj(tb, "mnt_table_is_fs_mounted: %s [rc=%d]", src, rc));
7966cbba 1817 free(src2);
5c60a0ea
KZ
1818 return rc;
1819}
1820
6bd8b7a7 1821#ifdef TEST_PROGRAM
dcc15ce5 1822#include "pathnames.h"
fbb3eb85 1823
68164f6c 1824static int parser_errcb(struct libmnt_table *tb, const char *filename, int line)
fbb3eb85
KZ
1825{
1826 fprintf(stderr, "%s:%d: parse error\n", filename, line);
c3b0d5b3 1827
d58b3157 1828 return 1; /* all errors are recoverable -- this is the default */
fbb3eb85
KZ
1829}
1830
5fde1d9f 1831static struct libmnt_table *create_table(const char *file, int comments)
6bd8b7a7 1832{
68164f6c 1833 struct libmnt_table *tb;
6bd8b7a7
KZ
1834
1835 if (!file)
1836 return NULL;
68164f6c 1837 tb = mnt_new_table();
6bd8b7a7
KZ
1838 if (!tb)
1839 goto err;
fbb3eb85 1840
cb90e24e 1841 mnt_table_enable_comments(tb, comments);
68164f6c 1842 mnt_table_set_parser_errcb(tb, parser_errcb);
fbb3eb85 1843
68164f6c 1844 if (mnt_table_parse_file(tb, file) != 0)
6bd8b7a7 1845 goto err;
6bd8b7a7
KZ
1846 return tb;
1847err:
c3b0d5b3 1848 fprintf(stderr, "%s: parsing failed\n", file);
c9f1585e 1849 mnt_unref_table(tb);
6bd8b7a7
KZ
1850 return NULL;
1851}
1852
5fde1d9f 1853static int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[])
6d94f2dc 1854{
68164f6c
KZ
1855 struct libmnt_table *tb;
1856 struct libmnt_fs *fs;
059c696f 1857 int rc = -1;
6d94f2dc 1858
cb90e24e 1859 tb = create_table(argv[1], FALSE);
6d94f2dc
KZ
1860 if (!tb)
1861 return -1;
1862
68164f6c 1863 fs = mnt_table_find_target(tb, "/", MNT_ITER_FORWARD);
6d94f2dc 1864 if (!fs)
059c696f 1865 goto done;
6d94f2dc
KZ
1866
1867 printf("ORIGINAL:\n");
1868 mnt_fs_print_debug(fs, stdout);
1869
309139c7 1870 fs = mnt_copy_fs(NULL, fs);
6d94f2dc 1871 if (!fs)
059c696f 1872 goto done;
6d94f2dc
KZ
1873
1874 printf("COPY:\n");
1875 mnt_fs_print_debug(fs, stdout);
26d0c0ae 1876 mnt_unref_fs(fs);
059c696f
KZ
1877 rc = 0;
1878done:
c9f1585e 1879 mnt_unref_table(tb);
059c696f 1880 return rc;
6d94f2dc
KZ
1881}
1882
5fde1d9f 1883static int test_parse(struct libmnt_test *ts, int argc, char *argv[])
6bd8b7a7 1884{
68164f6c
KZ
1885 struct libmnt_table *tb = NULL;
1886 struct libmnt_iter *itr = NULL;
1887 struct libmnt_fs *fs;
059c696f 1888 int rc = -1;
cb90e24e
OO
1889 int parse_comments = FALSE;
1890
1891 if (argc == 3 && !strcmp(argv[2], "--comments"))
1892 parse_comments = TRUE;
6bd8b7a7 1893
cb90e24e 1894 tb = create_table(argv[1], parse_comments);
6bd8b7a7
KZ
1895 if (!tb)
1896 return -1;
1897
efe73c3e
KZ
1898 itr = mnt_new_iter(MNT_ITER_FORWARD);
1899 if (!itr)
059c696f 1900 goto done;
fbb3eb85 1901
cb90e24e
OO
1902 if (mnt_table_get_intro_comment(tb))
1903 fprintf(stdout, "Initial comment:\n\"%s\"\n",
1904 mnt_table_get_intro_comment(tb));
1905
68164f6c 1906 while(mnt_table_next_fs(tb, itr, &fs) == 0)
efe73c3e 1907 mnt_fs_print_debug(fs, stdout);
cb90e24e 1908
3035ba93 1909 if (mnt_table_get_trailing_comment(tb))
cb90e24e 1910 fprintf(stdout, "Trailing comment:\n\"%s\"\n",
3035ba93 1911 mnt_table_get_trailing_comment(tb));
059c696f
KZ
1912 rc = 0;
1913done:
efe73c3e 1914 mnt_free_iter(itr);
c9f1585e 1915 mnt_unref_table(tb);
059c696f 1916 return rc;
6bd8b7a7
KZ
1917}
1918
911e6945
KZ
1919static int test_find_idx(struct libmnt_test *ts, int argc, char *argv[])
1920{
1921 struct libmnt_table *tb;
1922 struct libmnt_fs *fs = NULL;
1923 struct libmnt_cache *mpc = NULL;
1924 const char *file, *what;
1925 int rc = -1;
1926
1927 if (argc != 3) {
1928 fprintf(stderr, "try --help\n");
1929 return -EINVAL;
1930 }
1931
1932 file = argv[1], what = argv[2];
1933
1934 tb = create_table(file, FALSE);
1935 if (!tb)
1936 goto done;
1937
1938 /* create a cache for canonicalized paths */
1939 mpc = mnt_new_cache();
1940 if (!mpc)
1941 goto done;
1942 mnt_table_set_cache(tb, mpc);
1943 mnt_unref_cache(mpc);
1944
1945 fs = mnt_table_find_target(tb, what, MNT_ITER_BACKWARD);
1946
1947 if (!fs)
1948 fprintf(stderr, "%s: not found '%s'\n", file, what);
1949 else {
1950 int idx = mnt_table_find_fs(tb, fs);
1951
1952 if (idx < 1)
1953 fprintf(stderr, "%s: not found '%s' fs pointer", file, what);
1954 else {
1955 printf("%s index is %d\n", what, idx);
1956 rc = 0;
1957 }
1958 }
1959done:
1960 mnt_unref_table(tb);
1961 return rc;
1962}
1963
5fde1d9f 1964static int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr)
6bd8b7a7 1965{
68164f6c
KZ
1966 struct libmnt_table *tb;
1967 struct libmnt_fs *fs = NULL;
1968 struct libmnt_cache *mpc = NULL;
6bd8b7a7 1969 const char *file, *find, *what;
059c696f 1970 int rc = -1;
6bd8b7a7
KZ
1971
1972 if (argc != 4) {
1973 fprintf(stderr, "try --help\n");
059c696f 1974 return -EINVAL;
6bd8b7a7
KZ
1975 }
1976
1977 file = argv[1], find = argv[2], what = argv[3];
1978
cb90e24e 1979 tb = create_table(file, FALSE);
6bd8b7a7 1980 if (!tb)
059c696f 1981 goto done;
6bd8b7a7
KZ
1982
1983 /* create a cache for canonicalized paths */
1984 mpc = mnt_new_cache();
1985 if (!mpc)
059c696f 1986 goto done;
68164f6c 1987 mnt_table_set_cache(tb, mpc);
0105691d 1988 mnt_unref_cache(mpc);
6bd8b7a7
KZ
1989
1990 if (strcasecmp(find, "source") == 0)
68164f6c 1991 fs = mnt_table_find_source(tb, what, dr);
6bd8b7a7 1992 else if (strcasecmp(find, "target") == 0)
68164f6c 1993 fs = mnt_table_find_target(tb, what, dr);
6bd8b7a7
KZ
1994
1995 if (!fs)
1996 fprintf(stderr, "%s: not found %s '%s'\n", file, find, what);
1997 else {
5e31c2c8 1998 mnt_fs_print_debug(fs, stdout);
059c696f 1999 rc = 0;
6bd8b7a7 2000 }
059c696f 2001done:
c9f1585e 2002 mnt_unref_table(tb);
059c696f 2003 return rc;
6bd8b7a7
KZ
2004}
2005
5fde1d9f 2006static int test_find_bw(struct libmnt_test *ts, int argc, char *argv[])
6bd8b7a7
KZ
2007{
2008 return test_find(ts, argc, argv, MNT_ITER_BACKWARD);
2009}
2010
5fde1d9f 2011static int test_find_fw(struct libmnt_test *ts, int argc, char *argv[])
6bd8b7a7
KZ
2012{
2013 return test_find(ts, argc, argv, MNT_ITER_FORWARD);
2014}
2015
5fde1d9f 2016static int test_find_pair(struct libmnt_test *ts, int argc, char *argv[])
059c696f 2017{
68164f6c
KZ
2018 struct libmnt_table *tb;
2019 struct libmnt_fs *fs;
975e14bd 2020 struct libmnt_cache *mpc = NULL;
059c696f
KZ
2021 int rc = -1;
2022
cb90e24e 2023 tb = create_table(argv[1], FALSE);
059c696f
KZ
2024 if (!tb)
2025 return -1;
975e14bd
KZ
2026 mpc = mnt_new_cache();
2027 if (!mpc)
2028 goto done;
2029 mnt_table_set_cache(tb, mpc);
0105691d 2030 mnt_unref_cache(mpc);
059c696f 2031
68164f6c 2032 fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD);
059c696f
KZ
2033 if (!fs)
2034 goto done;
2035
2036 mnt_fs_print_debug(fs, stdout);
2037 rc = 0;
2038done:
c9f1585e 2039 mnt_unref_table(tb);
059c696f
KZ
2040 return rc;
2041}
2042
5fde1d9f 2043static int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
dcc15ce5
KZ
2044{
2045 struct libmnt_table *tb;
2046 struct libmnt_fs *fs;
2047 struct libmnt_cache *mpc = NULL;
2048 int rc = -1;
2049
2050 tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
2051 if (!tb)
2052 return -1;
2053 mpc = mnt_new_cache();
2054 if (!mpc)
2055 goto done;
2056 mnt_table_set_cache(tb, mpc);
0105691d 2057 mnt_unref_cache(mpc);
dcc15ce5
KZ
2058
2059 fs = mnt_table_find_mountpoint(tb, argv[1], MNT_ITER_BACKWARD);
2060 if (!fs)
2061 goto done;
2062
2063 mnt_fs_print_debug(fs, stdout);
2064 rc = 0;
2065done:
c9f1585e 2066 mnt_unref_table(tb);
dcc15ce5
KZ
2067 return rc;
2068}
2069
5c60a0ea
KZ
2070static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[])
2071{
2072 struct libmnt_table *tb = NULL, *fstab = NULL;
2073 struct libmnt_fs *fs;
2074 struct libmnt_iter *itr = NULL;
975e14bd 2075 struct libmnt_cache *mpc = NULL;
58c87bd0 2076 int writable = 0;
dc7c3d65
KZ
2077 const char *path = NULL;
2078
2079 if (mnt_has_regular_mtab(&path, &writable) == 1 && writable == 0)
2080 tb = mnt_new_table_from_file(path);
2081 else
2082 tb = mnt_new_table_from_file("/proc/self/mountinfo");
5c60a0ea 2083
5c60a0ea
KZ
2084 if (!tb) {
2085 fprintf(stderr, "failed to parse mountinfo\n");
2086 return -1;
2087 }
2088
cb90e24e 2089 fstab = create_table(argv[1], FALSE);
5c60a0ea
KZ
2090 if (!fstab)
2091 goto done;
2092
2093 itr = mnt_new_iter(MNT_ITER_FORWARD);
2094 if (!itr)
2095 goto done;
2096
975e14bd
KZ
2097 mpc = mnt_new_cache();
2098 if (!mpc)
2099 goto done;
2100 mnt_table_set_cache(tb, mpc);
0105691d 2101 mnt_unref_cache(mpc);
975e14bd 2102
dc7c3d65 2103 while (mnt_table_next_fs(fstab, itr, &fs) == 0) {
5c60a0ea
KZ
2104 if (mnt_table_is_fs_mounted(tb, fs))
2105 printf("%s already mounted on %s\n",
2106 mnt_fs_get_source(fs),
2107 mnt_fs_get_target(fs));
2108 else
2109 printf("%s not mounted on %s\n",
2110 mnt_fs_get_source(fs),
2111 mnt_fs_get_target(fs));
2112 }
2113
5c60a0ea 2114done:
c9f1585e
KZ
2115 mnt_unref_table(tb);
2116 mnt_unref_table(fstab);
5c60a0ea 2117 mnt_free_iter(itr);
58c87bd0 2118 return 0;
5c60a0ea
KZ
2119}
2120
51fffa7b
KZ
2121/* returns 0 if @a and @b targets are the same */
2122static int test_uniq_cmp(struct libmnt_table *tb __attribute__((__unused__)),
2123 struct libmnt_fs *a,
2124 struct libmnt_fs *b)
2125{
2126 assert(a);
2127 assert(b);
2128
2129 return mnt_fs_streq_target(a, mnt_fs_get_target(b)) ? 0 : 1;
2130}
2131
2132static int test_uniq(struct libmnt_test *ts, int argc, char *argv[])
2133{
2134 struct libmnt_table *tb;
2135 int rc = -1;
2136
2137 if (argc != 2) {
2138 fprintf(stderr, "try --help\n");
2139 return -EINVAL;
2140 }
2141
2142 tb = create_table(argv[1], FALSE);
2143 if (!tb)
2144 goto done;
2145
2146 if (mnt_table_uniq_fs(tb, 0, test_uniq_cmp) == 0) {
2147 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
2148 struct libmnt_fs *fs;
2149 if (!itr)
2150 goto done;
2151 while (mnt_table_next_fs(tb, itr, &fs) == 0)
2152 mnt_fs_print_debug(fs, stdout);
2153 mnt_free_iter(itr);
2154 rc = 0;
2155 }
2156done:
2157 mnt_unref_table(tb);
2158 return rc;
2159}
2160
cc06a01e 2161
6bd8b7a7
KZ
2162int main(int argc, char *argv[])
2163{
68164f6c 2164 struct libmnt_test tss[] = {
cb90e24e 2165 { "--parse", test_parse, "<file> [--comments] parse and print tab" },
6bd8b7a7
KZ
2166 { "--find-forward", test_find_fw, "<file> <source|target> <string>" },
2167 { "--find-backward", test_find_bw, "<file> <source|target> <string>" },
51fffa7b 2168 { "--uniq-target", test_uniq, "<file>" },
059c696f 2169 { "--find-pair", test_find_pair, "<file> <source> <target>" },
911e6945 2170 { "--find-fs", test_find_idx, "<file> <target>" },
dcc15ce5 2171 { "--find-mountpoint", test_find_mountpoint, "<path>" },
6d94f2dc 2172 { "--copy-fs", test_copy_fs, "<file> copy root FS from the file" },
dc7c3d65 2173 { "--is-mounted", test_is_mounted, "<fstab> check what from fstab is already mounted" },
6bd8b7a7
KZ
2174 { NULL }
2175 };
2176
2177 return mnt_run_test(tss, argc, argv);
2178}
2179
2180#endif /* TEST_PROGRAM */