/* * Copyright (C) 2008-2010 Karel Zak * * This file may be redistributed under the terms of the * GNU Lesser General Public License. */ /** * SECTION: table * @title: Table of filesystems * @short_description: container for entries from fstab, mtab or mountinfo * * Note that mnt_table_find_* functions are mount(8) compatible. These functions * try to find an entry in more iterations where the first attempt is always * based on comparison with unmodified (non-canonicalized or un-evaluated) * paths or tags. For example fstab with two entries: * * * LABEL=foo /foo auto rw * /dev/foo /foo auto rw * * * * where both lines are used for the *same* device, then * * * mnt_table_find_source(tb, "/dev/foo", &fs); * * * will returns the second line, and * * * mnt_table_find_source(tb, "LABEL=foo", &fs); * * * will returns the first entry, and * * * mnt_table_find_source(tb, "UUID=anyuuid", &fs); * * * will returns the first entry (if UUID matches with the device). */ #include #include "mountP.h" #include "strutils.h" /** * mnt_new_table: * * The tab is a container for struct libmnt_fs entries that usually represents a fstab, * mtab or mountinfo file from your system. * * See also mnt_table_parse_file(). * * Returns: newly allocated tab struct. */ struct libmnt_table *mnt_new_table(void) { struct libmnt_table *tb = NULL; tb = calloc(1, sizeof(*tb)); if (!tb) return NULL; DBG(TAB, mnt_debug_h(tb, "alloc")); INIT_LIST_HEAD(&tb->ents); return tb; } /** * mnt_reset_table: * @tb: tab pointer * * Dealocates all entries (filesystems) from the table * * Returns: 0 on success or negative number in case of error. */ int mnt_reset_table(struct libmnt_table *tb) { if (!tb) return -EINVAL; DBG(TAB, mnt_debug_h(tb, "reset")); while (!list_empty(&tb->ents)) { struct libmnt_fs *fs = list_entry(tb->ents.next, struct libmnt_fs, ents); mnt_free_fs(fs); } tb->nents = 0; return 0; } /** * mnt_free_table: * @tb: tab pointer * * Deallocates tab struct and all entries. */ void mnt_free_table(struct libmnt_table *tb) { if (!tb) return; mnt_reset_table(tb); DBG(TAB, mnt_debug_h(tb, "free")); free(tb); } /** * mnt_table_get_nents: * @tb: pointer to tab * * Returns: number of valid entries in tab. */ int mnt_table_get_nents(struct libmnt_table *tb) { assert(tb); return tb ? tb->nents : 0; } /** * mnt_table_set_cache: * @tb: pointer to tab * @mpc: pointer to struct libmnt_cache instance * * Setups a cache for canonicalized paths and evaluated tags (LABEL/UUID). The * cache is recommended for mnt_table_find_*() functions. * * The cache could be shared between more tabs. Be careful when you share the * same cache between more threads -- currently the cache does not provide any * locking method. * * See also mnt_new_cache(). * * Returns: 0 on success or negative number in case of error. */ int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc) { assert(tb); if (!tb) return -EINVAL; tb->cache = mpc; return 0; } /** * mnt_table_get_cache: * @tb: pointer to tab * * Returns: pointer to struct libmnt_cache instance or NULL. */ struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb) { assert(tb); return tb ? tb->cache : NULL; } /** * mnt_table_add_fs: * @tb: tab pointer * @fs: new entry * * Adds a new entry to tab. * * Returns: 0 on success or negative number in case of error. */ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) { assert(tb); assert(fs); if (!tb || !fs) return -EINVAL; list_add_tail(&fs->ents, &tb->ents); DBG(TAB, mnt_debug_h(tb, "add entry: %s %s", mnt_fs_get_source(fs), mnt_fs_get_target(fs))); tb->nents++; return 0; } /** * mnt_table_remove_fs: * @tb: tab pointer * @fs: new entry * * Returns: 0 on success or negative number in case of error. */ int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs) { assert(tb); assert(fs); if (!tb || !fs) return -EINVAL; list_del(&fs->ents); tb->nents--; return 0; } /** * mnt_table_get_root_fs: * @tb: mountinfo file (/proc/self/mountinfo) * @root: returns pointer to the root filesystem (/) * * Returns: 0 on success or -1 case of error. */ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) { struct libmnt_iter itr; struct libmnt_fs *fs; int root_id = 0; assert(tb); assert(root); if (!tb || !root) return -EINVAL; DBG(TAB, mnt_debug_h(tb, "lookup root fs")); mnt_reset_iter(&itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { int id = mnt_fs_get_parent_id(fs); if (!id) break; /* @tab is not mountinfo file? */ if (!*root || id < root_id) { *root = fs; root_id = id; } } return root_id ? 0 : -EINVAL; } /** * mnt_table_next_child_fs: * @tb: mountinfo file (/proc/self/mountinfo) * @itr: iterator * @parent: parental FS * @chld: returns the next child filesystem * * Note that filesystems are returned in the order how was mounted (according to * IDs in /proc/self/mountinfo). * * Returns: 0 on success, negative number in case of error or 1 at end of list. */ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *parent, struct libmnt_fs **chld) { struct libmnt_fs *fs; int parent_id, lastchld_id = 0, chld_id = 0; if (!tb || !itr || !parent) return -EINVAL; DBG(TAB, mnt_debug_h(tb, "lookup next child of %s", mnt_fs_get_target(parent))); parent_id = mnt_fs_get_id(parent); if (!parent_id) return -EINVAL; /* get ID of the previously returned child */ if (itr->head && itr->p != itr->head) { MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents); lastchld_id = mnt_fs_get_id(fs); } *chld = NULL; mnt_reset_iter(itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, itr, &fs) == 0) { int id; if (mnt_fs_get_parent_id(fs) != parent_id) continue; id = mnt_fs_get_id(fs); if ((!lastchld_id || id > lastchld_id) && (!*chld || id < chld_id)) { *chld = fs; chld_id = id; } } if (!chld_id) return 1; /* end of iterator */ /* set the iterator to the @chld for the next call */ mnt_table_set_iter(tb, itr, *chld); return 0; } /** * mnt_table_next_fs: * @tb: tab pointer * @itr: iterator * @fs: returns the next tab entry * * Returns: 0 on success, negative number in case of error or 1 at end of list. * * Example: * * * while(mnt_table_next_fs(tb, itr, &fs) == 0) { * const char *dir = mnt_fs_get_target(fs); * printf("mount point: %s\n", dir); * } * mnt_free_table(fi); * * * * lists all mountpoints from fstab in backward order. */ int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs **fs) { int rc = 1; assert(tb); assert(itr); assert(fs); if (!tb || !itr || !fs) return -EINVAL; *fs = NULL; if (!itr->head) MNT_ITER_INIT(itr, &tb->ents); if (itr->p != itr->head) { MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents); rc = 0; } return rc; } /** * mnt_table_find_next_fs: * @tb: table * @itr: iterator * @match_func: function returns 1 or 0 * @userdata: extra data for match_func * @fs: returns pointer to the next matching table entry * * This function allows search in @tb. * * Returns: negative number in case of error, 1 at end of table or 0 o success. */ int mnt_table_find_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, int (*match_func)(struct libmnt_fs *, void *), void *userdata, struct libmnt_fs **fs) { if (!tb || !itr || !fs || !match_func) return -EINVAL; DBG(TAB, mnt_debug_h(tb, "lookup next fs")); if (!itr->head) MNT_ITER_INIT(itr, &tb->ents); do { if (itr->p != itr->head) MNT_ITER_ITERATE(itr, *fs, struct libmnt_fs, ents); else break; /* end */ if (match_func(*fs, userdata)) return 0; } while(1); *fs = NULL; return 1; } /** * mnt_table_set_iter: * @tb: tab pointer * @itr: iterator * @fs: tab entry * * Sets @iter to the position of @fs in the file @tb. * * Returns: 0 on success, negative number in case of error. */ int mnt_table_set_iter(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *fs) { assert(tb); assert(itr); assert(fs); if (!tb || !itr || !fs) return -EINVAL; MNT_ITER_INIT(itr, &tb->ents); itr->p = &fs->ents; return 0; } /** * mnt_table_find_target: * @tb: tab pointer * @path: mountpoint directory * @direction: MNT_ITER_{FORWARD,BACKWARD} * * Try to lookup an entry in given tab, possible are three iterations, first * with @path, second with realpath(@path) and third with realpath(@path) * against realpath(fs->target). The 2nd and 3rd iterations are not performed * when @tb cache is not set (see mnt_table_set_cache()). * * Returns: a tab entry or NULL. */ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *path, int direction) { struct libmnt_iter itr; struct libmnt_fs *fs = NULL; char *cn; assert(tb); assert(path); if (!tb || !path) return NULL; DBG(TAB, mnt_debug_h(tb, "lookup TARGET: %s", path)); /* native @target */ mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (fs->target && streq_except_trailing_slash(fs->target, path)) return fs; } if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) return NULL; /* canonicalized paths in struct libmnt_table */ mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (fs->target && strcmp(fs->target, cn) == 0) return fs; } /* non-canonicaled path in struct libmnt_table */ mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { char *p; if (!fs->target || mnt_fs_is_swaparea(fs) || (*fs->target == '/' && *(fs->target + 1) == '\0')) continue; p = mnt_resolve_path(fs->target, tb->cache); if (strcmp(cn, p) == 0) return fs; } return NULL; } /** * mnt_table_find_srcpath: * @tb: tab pointer * @path: source path (devname or dirname) or NULL * @direction: MNT_ITER_{FORWARD,BACKWARD} * * Try to lookup an entry in given tab, possible are four iterations, first * with @path, second with realpath(@path), third with tags (LABEL, UUID, ..) * from @path and fourth with realpath(@path) against realpath(entry->srcpath). * * The 2nd, 3rd and 4th iterations are not performed when @tb cache is not * set (see mnt_table_set_cache()). * * Note that valid source path is NULL; the libmount uses NULL instead of * "none". The "none" is used in /proc/{mounts,self/mountninfo} for pseudo * filesystems. * * Returns: a tab entry or NULL. */ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *path, int direction) { struct libmnt_iter itr; struct libmnt_fs *fs = NULL; int ntags = 0; char *cn; const char *p; assert(tb); DBG(TAB, mnt_debug_h(tb, "lookup srcpath: %s", path)); /* native paths */ mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *src = mnt_fs_get_source(fs); p = mnt_fs_get_srcpath(fs); if (path == NULL && src == NULL) return fs; /* source is "none" */ if (path && p && streq_except_trailing_slash(p, path)) return fs; if (!p && src) ntags++; /* mnt_fs_get_srcpath() returs nothing, it's TAG */ } if (!path || !tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) return NULL; /* canonicalized paths in struct libmnt_table */ if (ntags < mnt_table_get_nents(tb)) { mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { p = mnt_fs_get_srcpath(fs); if (p && streq_except_trailing_slash(p, cn)) return fs; } } /* evaluated tag */ if (ntags) { int rc = mnt_cache_read_tags(tb->cache, cn); mnt_reset_iter(&itr, direction); if (rc == 0) { /* @path's TAGs are in the cache */ while(mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *t, *v; if (mnt_fs_get_tag(fs, &t, &v)) continue; if (mnt_cache_device_has_tag(tb->cache, cn, t, v)) return fs; } } else if (rc < 0 && errno == EACCES) { /* @path is unaccessible, try evaluate all TAGs in @tb * by udev symlinks -- this could be expensive on systems * with huge fstab/mtab */ while(mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *t, *v, *x; if (mnt_fs_get_tag(fs, &t, &v)) continue; x = mnt_resolve_tag(t, v, tb->cache); if (x && streq_except_trailing_slash(x, cn)) return fs; } } } /* non-canonicalized paths in struct libmnt_table */ if (ntags <= mnt_table_get_nents(tb)) { mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs)) continue; p = mnt_fs_get_srcpath(fs); if (p) p = mnt_resolve_path(p, tb->cache); if (p && streq_except_trailing_slash(cn, p)) return fs; } } return NULL; } /** * mnt_table_find_tag: * @tb: tab pointer * @tag: tag name (e.g "LABEL", "UUID", ...) * @val: tag value * @direction: MNT_ITER_{FORWARD,BACKWARD} * * Try to lookup an entry in given tab, first attempt is lookup by @tag and * @val, for the second attempt the tag is evaluated (converted to the device * name) and mnt_table_find_srcpath() is preformed. The second attempt is not * performed when @tb cache is not set (see mnt_table_set_cache()). * Returns: a tab entry or NULL. */ struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, const char *val, int direction) { struct libmnt_iter itr; struct libmnt_fs *fs = NULL; assert(tb); assert(tag); assert(val); if (!tb || !tag || !val) return NULL; DBG(TAB, mnt_debug_h(tb, "lookup by TAG: %s %s", tag, val)); /* look up by TAG */ mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (fs->tagname && fs->tagval && strcmp(fs->tagname, tag) == 0 && strcmp(fs->tagval, val) == 0) return fs; } if (tb->cache) { /* look up by device name */ char *cn = mnt_resolve_tag(tag, val, tb->cache); if (cn) return mnt_table_find_srcpath(tb, cn, direction); } return NULL; } /** * mnt_table_find_source: * @tb: tab pointer * @source: TAG or path * @direction: MNT_ITER_{FORWARD,BACKWARD} * * This is high-level API for mnt_table_find_{srcpath,tag}. You needn't to care * about @source format (device, LABEL, UUID, ...). This function parses @source * and calls mnt_table_find_tag() or mnt_table_find_srcpath(). * * Returns: a tab entry or NULL. */ struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, const char *source, int direction) { struct libmnt_fs *fs = NULL; assert(tb); if (!tb) return NULL; DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s", source)); if (source && strchr(source, '=')) { char *tag, *val; if (blkid_parse_tag_string(source, &tag, &val) == 0) { fs = mnt_table_find_tag(tb, tag, val, direction); free(tag); free(val); } } else fs = mnt_table_find_srcpath(tb, source, direction); return fs; } /** * mnt_table_find_pair * @tb: tab pointer * @source: TAG or path * @target: mountpoint * @direction: MNT_ITER_{FORWARD,BACKWARD} * * This function is implemented by mnt_fs_match_source() and * mnt_fs_match_target() functions. It means that this is more expensive that * others mnt_table_find_* function, because every @tab entry is fully evaluated. * * Returns: a tab entry or NULL. */ struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *source, const char *target, int direction) { struct libmnt_fs *fs = NULL; struct libmnt_iter itr; assert(tb); assert(target); if (!tb || !target) return NULL; DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: %s TARGET: %s", source, target)); mnt_reset_iter(&itr, direction); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { if (mnt_fs_match_target(fs, target, tb->cache) && mnt_fs_match_source(fs, source, tb->cache)) return fs; } return NULL; } /* * @tb: /proc/self/mountinfo * @fs: filesystem * @mountflags: MS_BIND or 0 * @fsroot: fs-root that will be probably used in the mountinfo file * for @fs after mount(2) * * For btrfs subvolumes this function returns NULL, but @fsroot properly set. * * Returns: entry from @tb that will be used as a source for @fs if the @fs is * bindmount. */ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, struct libmnt_fs *fs, unsigned long mountflags, char **fsroot) { char *root = NULL, *mnt = NULL; const char *fstype; struct libmnt_fs *src_fs = NULL; assert(fs); assert(fsroot); DBG(TAB, mnt_debug("lookup fs-root for %s", mnt_fs_get_source(fs))); fstype = mnt_fs_get_fstype(fs); if (tb && (mountflags & MS_BIND)) { const char *src, *src_root; char *xsrc = NULL; DBG(TAB, mnt_debug("fs-root for bind")); src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache); if (src) mnt = mnt_get_mountpoint(src); if (mnt) root = mnt_get_fs_root(src, mnt); if (xsrc && !tb->cache) { free(xsrc); src = NULL; } if (!mnt) goto err; src_fs = mnt_table_find_target(tb, mnt, MNT_ITER_BACKWARD); if (!src_fs) { DBG(TAB, mnt_debug("not found '%s' in mountinfo -- using default", mnt)); goto dflt; } /* on btrfs the subvolume is used as fs-root in * /proc/self/mountinfo, so we have to get the original subvolume * name from src_fs and prepend the subvolume name to the * fs-root path */ src_root = mnt_fs_get_root(src_fs); if (src_root && !startswith(root, src_root)) { size_t sz = strlen(root) + strlen(src_root) + 1; char *tmp = malloc(sz); if (!tmp) goto err; snprintf(tmp, sz, "%s%s", src_root, root); free(root); root = tmp; } } /* * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path */ else if (fstype && !strcmp(fstype, "btrfs")) { char *vol = NULL, *p; size_t sz, volsz = 0; if (mnt_fs_get_option(fs, "subvol", &vol, &volsz)) goto dflt; DBG(TAB, mnt_debug("setting FS root: btrfs subvol")); sz = volsz; if (*vol != '/') sz++; root = malloc(sz + 1); if (!root) goto err; p = root; if (*vol != '/') *p++ = '/'; memcpy(p, vol, volsz); *(root + sz) = '\0'; } dflt: if (!root) { root = strdup("/"); if (!root) goto err; } *fsroot = root; DBG(TAB, mnt_debug("FS root result: %s", root)); free(mnt); return src_fs; err: free(root); free(mnt); return NULL; } /** * mnt_table_is_mounted: * @tb: /proc/self/mountinfo file * @fstab_fs: /etc/fstab entry * * Checks if the @fstab_fs entry is already in the @tb table. The "swap" * is ignored. * * TODO: check for loopdev (see mount/mount.c is_fstab_entry_mounted(). * * Returns: 0 or 1 */ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) { char *root = NULL; struct libmnt_fs *src_fs; const char *src; char *xsrc = NULL, *tgt; int flags = 0, rc = 0; assert(tb); assert(fstab_fs); if (mnt_fs_is_swaparea(fstab_fs)) return 0; if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0) flags = MS_BIND; src_fs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root); if (src_fs) src = mnt_fs_get_srcpath(src_fs); else if (mnt_fs_is_pseudofs(fstab_fs)) src = mnt_fs_get_source(fstab_fs); else src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fstab_fs), tb->cache); tgt = mnt_resolve_path(mnt_fs_get_target(fstab_fs), tb->cache); if (tgt && src && root) { struct libmnt_iter itr; struct libmnt_fs *fs; mnt_reset_iter(&itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *s = mnt_fs_get_srcpath(fs), *t = mnt_fs_get_target(fs), *r = mnt_fs_get_root(fs); /* * Note that kernel can add tailing slash to the * network filesystem source paths. */ if (t && s && r && streq_except_trailing_slash(t, tgt) && streq_except_trailing_slash(s, src) && strcmp(r, root) == 0) break; } if (fs) rc = 1; /* success */ } if (xsrc && !tb->cache) free(xsrc); if (!tb->cache) free(tgt); free(root); return rc; } #ifdef TEST_PROGRAM static int parser_errcb(struct libmnt_table *tb, const char *filename, int line) { fprintf(stderr, "%s:%d: parse error\n", filename, line); return 1; /* all errors are recoverable -- this is default */ } struct libmnt_table *create_table(const char *file) { struct libmnt_table *tb; if (!file) return NULL; tb = mnt_new_table(); if (!tb) goto err; mnt_table_set_parser_errcb(tb, parser_errcb); if (mnt_table_parse_file(tb, file) != 0) goto err; return tb; err: fprintf(stderr, "%s: parsing failed\n", file); mnt_free_table(tb); return NULL; } int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs; int rc = -1; tb = create_table(argv[1]); if (!tb) return -1; fs = mnt_table_find_target(tb, "/", MNT_ITER_FORWARD); if (!fs) goto done; printf("ORIGINAL:\n"); mnt_fs_print_debug(fs, stdout); fs = mnt_copy_fs(NULL, fs); if (!fs) goto done; printf("COPY:\n"); mnt_fs_print_debug(fs, stdout); mnt_free_fs(fs); rc = 0; done: mnt_free_table(tb); return rc; } int test_parse(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_table *tb = NULL; struct libmnt_iter *itr = NULL; struct libmnt_fs *fs; int rc = -1; tb = create_table(argv[1]); if (!tb) return -1; itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) goto done; while(mnt_table_next_fs(tb, itr, &fs) == 0) mnt_fs_print_debug(fs, stdout); rc = 0; done: mnt_free_iter(itr); mnt_free_table(tb); return rc; } int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) { struct libmnt_table *tb; struct libmnt_fs *fs = NULL; struct libmnt_cache *mpc = NULL; const char *file, *find, *what; int rc = -1; if (argc != 4) { fprintf(stderr, "try --help\n"); return -EINVAL; } file = argv[1], find = argv[2], what = argv[3]; tb = create_table(file); if (!tb) goto done; /* create a cache for canonicalized paths */ mpc = mnt_new_cache(); if (!mpc) goto done; mnt_table_set_cache(tb, mpc); if (strcasecmp(find, "source") == 0) fs = mnt_table_find_source(tb, what, dr); else if (strcasecmp(find, "target") == 0) fs = mnt_table_find_target(tb, what, dr); if (!fs) fprintf(stderr, "%s: not found %s '%s'\n", file, find, what); else { mnt_fs_print_debug(fs, stdout); rc = 0; } done: mnt_free_table(tb); mnt_free_cache(mpc); return rc; } int test_find_bw(struct libmnt_test *ts, int argc, char *argv[]) { return test_find(ts, argc, argv, MNT_ITER_BACKWARD); } int test_find_fw(struct libmnt_test *ts, int argc, char *argv[]) { return test_find(ts, argc, argv, MNT_ITER_FORWARD); } int test_find_pair(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs; int rc = -1; tb = create_table(argv[1]); if (!tb) return -1; fs = mnt_table_find_pair(tb, argv[2], argv[3], MNT_ITER_FORWARD); if (!fs) goto done; mnt_fs_print_debug(fs, stdout); rc = 0; done: mnt_free_table(tb); return rc; } static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_table *tb = NULL, *fstab = NULL; struct libmnt_fs *fs; struct libmnt_iter *itr = NULL; int rc; tb = mnt_new_table_from_file("/proc/self/mountinfo"); if (!tb) { fprintf(stderr, "failed to parse mountinfo\n"); return -1; } fstab = create_table(argv[1]); if (!fstab) goto done; itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) goto done; while(mnt_table_next_fs(fstab, itr, &fs) == 0) { if (mnt_table_is_fs_mounted(tb, fs)) printf("%s already mounted on %s\n", mnt_fs_get_source(fs), mnt_fs_get_target(fs)); else printf("%s not mounted on %s\n", mnt_fs_get_source(fs), mnt_fs_get_target(fs)); } rc = 0; done: mnt_free_table(tb); mnt_free_table(fstab); mnt_free_iter(itr); return rc; } int main(int argc, char *argv[]) { struct libmnt_test tss[] = { { "--parse", test_parse, " parse and print tab" }, { "--find-forward", test_find_fw, " " }, { "--find-backward", test_find_bw, " " }, { "--find-pair", test_find_pair, " " }, { "--copy-fs", test_copy_fs, " copy root FS from the file" }, { "--is-mounted", test_is_mounted, " check what from are already mounted" }, { NULL } }; return mnt_run_test(tss, argc, argv); } #endif /* TEST_PROGRAM */