2
* Copyright (C) 2008-2010 Karel Zak <kzak@redhat.com>
4
* This file may be redistributed under the terms of the
5
* GNU Lesser General Public License.
10
* @title: Table of filesystems
11
* @short_description: container for entries from fstab/mtab/mountinfo
14
* Note that mnt_table_find_* functions are mount(8) compatible. These functions
15
* try to found an entry in more iterations where the first attempt is always
16
* based on comparison with unmodified (non-canonicalized or un-evaluated)
17
* paths or tags. For example fstab with two entries:
20
* LABEL=foo /foo auto rw
21
* /dev/foo /foo auto rw
25
* where both lines are used for the *same* device, then
28
* mnt_table_find_source(tb, "/dev/foo", &fs);
31
* will returns the second line, and
34
* mnt_table_find_source(tb, "LABEL=foo", &fs);
37
* will returns the first entry, and
40
* mnt_table_find_source(tb, "UUID=anyuuid", &fs);
43
* will returns the first entry (if UUID matches with the device).
51
#include <sys/types.h>
63
* The tab is a container for struct libmnt_fs entries that usually represents a fstab,
64
* mtab or mountinfo file from your system.
66
* See also mnt_table_parse_file().
68
* Returns: newly allocated tab struct.
70
struct libmnt_table *mnt_new_table(void)
72
struct libmnt_table *tb = NULL;
74
tb = calloc(1, sizeof(*tb));
78
DBG(TAB, mnt_debug_h(tb, "alloc"));
80
INIT_LIST_HEAD(&tb->ents);
88
* Deallocates tab struct and all entries.
90
void mnt_free_table(struct libmnt_table *tb)
95
DBG(TAB, mnt_debug_h(tb, "free"));
97
while (!list_empty(&tb->ents)) {
98
struct libmnt_fs *fs = list_entry(tb->ents.next,
99
struct libmnt_fs, ents);
107
* mnt_table_get_nents:
108
* @tb: pointer to tab
110
* Returns: number of valid entries in tab.
112
int mnt_table_get_nents(struct libmnt_table *tb)
115
return tb ? tb->nents : 0;
119
* mnt_table_set_cache:
120
* @tb: pointer to tab
121
* @mpc: pointer to struct libmnt_cache instance
123
* Setups a cache for canonicalized paths and evaluated tags (LABEL/UUID). The
124
* cache is recommended for mnt_table_find_*() functions.
126
* The cache could be shared between more tabs. Be careful when you share the
127
* same cache between more threads -- currently the cache does not provide any
130
* See also mnt_new_cache().
132
* Returns: 0 on success or negative number in case of error.
134
int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc)
144
* mnt_table_get_cache:
145
* @tb: pointer to tab
147
* Returns: pointer to struct libmnt_cache instance or NULL.
149
struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb)
152
return tb ? tb->cache : NULL;
160
* Adds a new entry to tab.
162
* Returns: 0 on success or negative number in case of error.
164
int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
172
list_add_tail(&fs->ents, &tb->ents);
174
DBG(TAB, mnt_debug_h(tb, "add entry: %s %s",
175
mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
181
* mnt_table_remove_fs:
185
* Returns: 0 on success or negative number in case of error.
187
int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
200
* mnt_table_get_root_fs:
201
* @tb: mountinfo file (/proc/self/mountinfo)
202
* @root: returns pointer to the root filesystem (/)
204
* Returns: 0 on success or -1 case of error.
206
int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root)
208
struct libmnt_iter itr;
209
struct libmnt_fs *fs;
218
DBG(TAB, mnt_debug_h(tb, "lookup root fs"));
220
mnt_reset_iter(&itr, MNT_ITER_FORWARD);
221
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
222
int id = mnt_fs_get_parent_id(fs);
224
break; /* @tab is not mountinfo file? */
226
if (!*root || id < root_id) {
232
return root_id ? 0 : -EINVAL;
236
* mnt_table_next_child_fs:
237
* @tb: mountinfo file (/proc/self/mountinfo)
239
* @parent: parental FS
240
* @chld: returns the next child filesystem
242
* Note that filesystems are returned in the order how was mounted (according to
243
* IDs in /proc/self/mountinfo).
245
* Returns: 0 on success, negative number in case of error or 1 at end of list.
247
int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
248
struct libmnt_fs *parent, struct libmnt_fs **chld)
250
struct libmnt_fs *fs;
251
int parent_id, lastchld_id = 0, chld_id = 0;
253
if (!tb || !itr || !parent)
256
DBG(TAB, mnt_debug_h(tb, "lookup next child of %s",
257
mnt_fs_get_target(parent)));
259
parent_id = mnt_fs_get_id(parent);
263
/* get ID of the previously returned child */
264
if (itr->head && itr->p != itr->head) {
265
MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents);
266
lastchld_id = mnt_fs_get_id(fs);
271
mnt_reset_iter(itr, MNT_ITER_FORWARD);
272
while(mnt_table_next_fs(tb, itr, &fs) == 0) {
275
if (mnt_fs_get_parent_id(fs) != parent_id)
278
id = mnt_fs_get_id(fs);
280
if ((!lastchld_id || id > lastchld_id) &&
281
(!*chld || id < chld_id)) {
288
return 1; /* end of iterator */
290
/* set the iterator to the @chld for the next call */
291
mnt_table_set_iter(tb, itr, *chld);
300
* @fs: returns the next tab entry
302
* Returns: 0 on success, negative number in case of error or 1 at end of list.
307
* while(mnt_table_next_fs(tb, itr, &fs) == 0) {
308
* const char *dir = mnt_fs_get_target(fs);
309
* printf("mount point: %s\n", dir);
311
* mnt_free_table(fi);
315
* lists all mountpoints from fstab in backward order.
317
int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs)
325
if (!tb || !itr || !fs)
330
MNT_ITER_INIT(itr, &tb->ents);
331
if (itr->p != itr->head) {
332
MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents);
340
* mnt_table_find_next_fs:
343
* @match_func: function returns 1 or 0
344
* @userdata: extra data for match_func
345
* @fs: returns pointer to the next matching table entry
347
* This function allows search in @tb.
349
* Returns: negative number in case of error, 1 at end of table or 0 o success.
351
int mnt_table_find_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr,
352
int (*match_func)(struct libmnt_fs *, void *), void *userdata,
353
struct libmnt_fs **fs)
355
if (!tb || !itr || !fs || !match_func)
358
DBG(TAB, mnt_debug_h(tb, "lookup next fs"));
361
MNT_ITER_INIT(itr, &tb->ents);
364
if (itr->p != itr->head)
365
MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents);
369
if (match_func(*fs, userdata))
378
* mnt_table_set_iter:
383
* Sets @iter to the position of @fs in the file @tb.
385
* Returns: 0 on success, negative number in case of error.
387
int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs)
393
if (!tb || !itr || !fs)
396
MNT_ITER_INIT(itr, &tb->ents);
403
* mnt_table_find_target:
405
* @path: mountpoint directory
406
* @direction: MNT_ITER_{FORWARD,BACKWARD}
408
* Try to lookup an entry in given tab, possible are three iterations, first
409
* with @path, second with realpath(@path) and third with realpath(@path)
410
* against realpath(fs->target). The 2nd and 3rd iterations are not performed
411
* when @tb cache is not set (see mnt_table_set_cache()).
413
* Returns: a tab entry or NULL.
415
struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *path, int direction)
417
struct libmnt_iter itr;
418
struct libmnt_fs *fs = NULL;
427
DBG(TAB, mnt_debug_h(tb, "lookup TARGET: %s", path));
430
mnt_reset_iter(&itr, direction);
431
while(mnt_table_next_fs(tb, &itr, &fs) == 0)
432
if (fs->target && strcmp(fs->target, path) == 0)
435
if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
438
/* canonicalized paths in struct libmnt_table */
439
mnt_reset_iter(&itr, direction);
440
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
441
if (fs->target && strcmp(fs->target, cn) == 0)
445
/* non-canonicaled path in struct libmnt_table */
446
mnt_reset_iter(&itr, direction);
447
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
450
if (!fs->target || !(fs->flags & MNT_FS_SWAP) ||
451
(*fs->target == '/' && *(fs->target + 1) == '\0'))
454
p = mnt_resolve_path(fs->target, tb->cache);
455
if (strcmp(cn, p) == 0)
462
* mnt_table_find_srcpath:
464
* @path: source path (devname or dirname)
465
* @direction: MNT_ITER_{FORWARD,BACKWARD}
467
* Try to lookup an entry in given tab, possible are four iterations, first
468
* with @path, second with realpath(@path), third with tags (LABEL, UUID, ..)
469
* from @path and fourth with realpath(@path) against realpath(entry->srcpath).
471
* The 2nd, 3rd and 4th iterations are not performed when @tb cache is not
472
* set (see mnt_table_set_cache()).
474
* Returns: a tab entry or NULL.
476
struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *path, int direction)
478
struct libmnt_iter itr;
479
struct libmnt_fs *fs = NULL;
487
DBG(TAB, mnt_debug_h(tb, "lookup srcpath: %s", path));
490
mnt_reset_iter(&itr, direction);
491
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
492
p = mnt_fs_get_srcpath(fs);
493
if (p && strcmp(p, path) == 0)
496
/* mnt_fs_get_srcpath() returs nothing, it's TAG */
500
if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache)))
503
/* canonicalized paths in struct libmnt_table */
504
if (ntags < mnt_table_get_nents(tb)) {
505
mnt_reset_iter(&itr, direction);
506
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
507
p = mnt_fs_get_srcpath(fs);
508
if (p && strcmp(p, cn) == 0)
515
int rc = mnt_cache_read_tags(tb->cache, cn);
517
mnt_reset_iter(&itr, direction);
520
/* @path's TAGs are in the cache */
521
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
524
if (mnt_fs_get_tag(fs, &t, &v))
527
if (mnt_cache_device_has_tag(tb->cache, cn, t, v))
530
} else if (rc < 0 && errno == EACCES) {
531
/* @path is unaccessible, try evaluate all TAGs in @tb
532
* by udev symlinks -- this could be expensive on systems
533
* with huge fstab/mtab */
534
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
535
const char *t, *v, *x;
536
if (mnt_fs_get_tag(fs, &t, &v))
538
x = mnt_resolve_tag(t, v, tb->cache);
539
if (x && !strcmp(x, cn))
545
/* non-canonicalized paths in struct libmnt_table */
546
if (ntags <= mnt_table_get_nents(tb)) {
547
mnt_reset_iter(&itr, direction);
548
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
549
if (fs->flags & (MNT_FS_NET | MNT_FS_PSEUDO))
551
p = mnt_fs_get_srcpath(fs);
553
p = mnt_resolve_path(p, tb->cache);
554
if (p && strcmp(cn, p) == 0)
564
* mnt_table_find_tag:
566
* @tag: tag name (e.g "LABEL", "UUID", ...)
568
* @direction: MNT_ITER_{FORWARD,BACKWARD}
570
* Try to lookup an entry in given tab, first attempt is lookup by @tag and
571
* @val, for the second attempt the tag is evaluated (converted to the device
572
* name) and mnt_table_find_srcpath() is preformed. The second attempt is not
573
* performed when @tb cache is not set (see mnt_table_set_cache()).
575
* Returns: a tab entry or NULL.
577
struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag,
578
const char *val, int direction)
580
struct libmnt_iter itr;
581
struct libmnt_fs *fs = NULL;
587
if (!tb || !tag || !val)
590
DBG(TAB, mnt_debug_h(tb, "lookup by TAG: %s %s", tag, val));
593
mnt_reset_iter(&itr, direction);
594
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
595
if (fs->tagname && fs->tagval &&
596
strcmp(fs->tagname, tag) == 0 &&
597
strcmp(fs->tagval, val) == 0)
602
/* look up by device name */
603
char *cn = mnt_resolve_tag(tag, val, tb->cache);
605
return mnt_table_find_srcpath(tb, cn, direction);
611
* mnt_table_find_source:
613
* @source: TAG or path
614
* @direction: MNT_ITER_{FORWARD,BACKWARD}
616
* This is high-level API for mnt_table_find_{srcpath,tag}. You needn't to care
617
* about @source format (device, LABEL, UUID, ...). This function parses @source
618
* and calls mnt_table_find_tag() or mnt_table_find_srcpath().
620
* Returns: a tab entry or NULL.
622
struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, const char *source, int direction)
624
struct libmnt_fs *fs = NULL;
632
DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s", source));
634
if (strchr(source, '=')) {
637
if (blkid_parse_tag_string(source, &tag, &val) == 0) {
639
fs = mnt_table_find_tag(tb, tag, val, direction);
645
fs = mnt_table_find_srcpath(tb, source, direction);
651
* mnt_table_find_pair
653
* @source: TAG or path
654
* @target: mountpoint
655
* @direction: MNT_ITER_{FORWARD,BACKWARD}
657
* This function is implemented by mnt_fs_match_source() and
658
* mnt_fs_match_target() functions. It means that this is more expensive that
659
* others mnt_table_find_* function, because every @tab entry is fully evaluated.
661
* Returns: a tab entry or NULL.
663
struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *source,
664
const char *target, int direction)
666
struct libmnt_fs *fs = NULL;
667
struct libmnt_iter itr;
673
if (!tb || !source || !target)
676
DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s TARGET: %s", source, target));
678
mnt_reset_iter(&itr, direction);
679
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
681
if (mnt_fs_match_target(fs, target, tb->cache) &&
682
mnt_fs_match_source(fs, source, tb->cache))
691
static int parser_errcb(struct libmnt_table *tb, const char *filename, int line)
693
fprintf(stderr, "%s:%d: parse error\n", filename, line);
695
return 1; /* all errors are recoverable -- this is default */
698
struct libmnt_table *create_table(const char *file)
700
struct libmnt_table *tb;
704
tb = mnt_new_table();
708
mnt_table_set_parser_errcb(tb, parser_errcb);
710
if (mnt_table_parse_file(tb, file) != 0)
714
fprintf(stderr, "%s: parsing failed\n", file);
719
int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[])
721
struct libmnt_table *tb;
722
struct libmnt_fs *fs;
725
tb = create_table(argv[1]);
729
fs = mnt_table_find_target(tb, "/", MNT_ITER_FORWARD);
733
printf("ORIGINAL:\n");
734
mnt_fs_print_debug(fs, stdout);
736
fs = mnt_copy_fs(NULL, fs);
741
mnt_fs_print_debug(fs, stdout);
749
int test_parse(struct libmnt_test *ts, int argc, char *argv[])
751
struct libmnt_table *tb = NULL;
752
struct libmnt_iter *itr = NULL;
753
struct libmnt_fs *fs;
756
tb = create_table(argv[1]);
760
itr = mnt_new_iter(MNT_ITER_FORWARD);
764
while(mnt_table_next_fs(tb, itr, &fs) == 0)
765
mnt_fs_print_debug(fs, stdout);
773
int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr)
775
struct libmnt_table *tb;
776
struct libmnt_fs *fs = NULL;
777
struct libmnt_cache *mpc = NULL;
778
const char *file, *find, *what;
782
fprintf(stderr, "try --help\n");
786
file = argv[1], find = argv[2], what = argv[3];
788
tb = create_table(file);
792
/* create a cache for canonicalized paths */
793
mpc = mnt_new_cache();
796
mnt_table_set_cache(tb, mpc);
798
if (strcasecmp(find, "source") == 0)
799
fs = mnt_table_find_source(tb, what, dr);
800
else if (strcasecmp(find, "target") == 0)
801
fs = mnt_table_find_target(tb, what, dr);
804
fprintf(stderr, "%s: not found %s '%s'\n", file, find, what);
806
mnt_fs_print_debug(fs, stdout);
815
int test_find_bw(struct libmnt_test *ts, int argc, char *argv[])
817
return test_find(ts, argc, argv, MNT_ITER_BACKWARD);
820
int test_find_fw(struct libmnt_test *ts, int argc, char *argv[])
822
return test_find(ts, argc, argv, MNT_ITER_FORWARD);
825
int test_find_pair(struct libmnt_test *ts, int argc, char *argv[])
827
struct libmnt_table *tb;
828
struct libmnt_fs *fs;
831
tb = create_table(argv[1]);
835
fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD);
839
mnt_fs_print_debug(fs, stdout);
846
int main(int argc, char *argv[])
848
struct libmnt_test tss[] = {
849
{ "--parse", test_parse, "<file> parse and print tab" },
850
{ "--find-forward", test_find_fw, "<file> <source|target> <string>" },
851
{ "--find-backward", test_find_bw, "<file> <source|target> <string>" },
852
{ "--find-pair", test_find_pair, "<file> <source> <target>" },
853
{ "--copy-fs", test_copy_fs, "<file> copy root FS from the file" },
857
return mnt_run_test(tss, argc, argv);
860
#endif /* TEST_PROGRAM */