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