X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=libmount%2Fsrc%2Ftab.c;h=674dc67e5b17796f293b3dadb7a97d7ba85f1788;hb=911e694528618b52ce23e4e29a7a85ee9f4edf05;hp=77260ab96c35327aa61f172eeb8b4c90358125ef;hpb=115b801f390bf922aa1c4adc2559e535378d2eb0;p=thirdparty%2Futil-linux.git diff --git a/libmount/src/tab.c b/libmount/src/tab.c index 77260ab96c..674dc67e5b 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -1,8 +1,14 @@ + +/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* - * Copyright (C) 2008-2010 Karel Zak + * This file is part of libmount from util-linux project. + * + * Copyright (C) 2008-2018 Karel Zak * - * This file may be redistributed under the terms of the - * GNU Lesser General Public License. + * libmount is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. */ /** @@ -47,8 +53,9 @@ #include "strutils.h" #include "loopdev.h" #include "fileutils.h" +#include "canonicalize.h" -static int is_mountinfo(struct libmnt_table *tb) +int is_mountinfo(struct libmnt_table *tb) { struct libmnt_fs *fs; @@ -149,9 +156,9 @@ void mnt_unref_table(struct libmnt_table *tb) * @tb: tab pointer * * Deallocates the table. This function does not care about reference count. Don't - * use this function directly -- it's better to use use mnt_unref_table(). + * use this function directly -- it's better to use mnt_unref_table(). * - * The table entries (filesystems) are unrefrenced by mnt_reset_table() and + * The table entries (filesystems) are unreferenced by mnt_reset_table() and * cache by mnt_unref_cache(). */ void mnt_free_table(struct libmnt_table *tb) @@ -187,7 +194,6 @@ int mnt_table_get_nents(struct libmnt_table *tb) */ int mnt_table_is_empty(struct libmnt_table *tb) { - assert(tb); return tb == NULL || list_empty(&tb->ents) ? 1 : 0; } @@ -202,7 +208,6 @@ int mnt_table_is_empty(struct libmnt_table *tb) */ int mnt_table_set_userdata(struct libmnt_table *tb, void *data) { - assert(tb); if (!tb) return -EINVAL; @@ -218,7 +223,6 @@ int mnt_table_set_userdata(struct libmnt_table *tb, void *data) */ void *mnt_table_get_userdata(struct libmnt_table *tb) { - assert(tb); return tb ? tb->userdata : NULL; } @@ -244,14 +248,13 @@ void *mnt_table_get_userdata(struct libmnt_table *tb) * # this comments belongs to the first fs * LABEL=foo /mnt/foo auto defaults 1 2 * # this comments belongs to the second fs - * LABEL=bar /mnt/bar auto defaults 1 2 + * LABEL=bar /mnt/bar auto defaults 1 2 * # tailing comment * * */ void mnt_table_enable_comments(struct libmnt_table *tb, int enable) { - assert(tb); if (tb) tb->comms = enable; } @@ -276,7 +279,6 @@ int mnt_table_with_comments(struct libmnt_table *tb) */ const char *mnt_table_get_intro_comment(struct libmnt_table *tb) { - assert(tb); return tb ? tb->comm_intro : NULL; } @@ -291,19 +293,7 @@ const char *mnt_table_get_intro_comment(struct libmnt_table *tb) */ int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm) { - char *p = NULL; - - assert(tb); - if (!tb) - return -EINVAL; - if (comm) { - p = strdup(comm); - if (!p) - return -ENOMEM; - } - free(tb->comm_intro); - tb->comm_intro = p; - return 0; + return strdup_to_struct_member(tb, comm_intro, comm); } /** @@ -317,7 +307,6 @@ int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm) */ int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm) { - assert(tb); if (!tb) return -EINVAL; return append_string(&tb->comm_intro, comm); @@ -331,7 +320,6 @@ int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm) */ const char *mnt_table_get_trailing_comment(struct libmnt_table *tb) { - assert(tb); return tb ? tb->comm_tail : NULL; } @@ -346,19 +334,7 @@ const char *mnt_table_get_trailing_comment(struct libmnt_table *tb) */ int mnt_table_set_trailing_comment(struct libmnt_table *tb, const char *comm) { - char *p = NULL; - - assert(tb); - if (!tb) - return -EINVAL; - if (comm) { - p = strdup(comm); - if (!p) - return -ENOMEM; - } - free(tb->comm_tail); - tb->comm_tail = p; - return 0; + return strdup_to_struct_member(tb, comm_tail, comm); } /** @@ -372,7 +348,6 @@ int mnt_table_set_trailing_comment(struct libmnt_table *tb, const char *comm) */ int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm) { - assert(tb); if (!tb) return -EINVAL; return append_string(&tb->comm_tail, comm); @@ -390,7 +365,7 @@ int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm) * same cache between more threads -- currently the cache does not provide any * locking method. * - * This function increments cache refrence counter. It's recomented to use + * This function increments cache reference counter. It's recommended to use * mnt_unref_cache() after mnt_table_set_cache() if you want to keep the cache * referenced by @tb only. * @@ -400,7 +375,6 @@ int mnt_table_append_trailing_comment(struct libmnt_table *tb, const char *comm) */ int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc) { - assert(tb); if (!tb) return -EINVAL; @@ -418,10 +392,41 @@ int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc) */ struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb) { - assert(tb); return tb ? tb->cache : NULL; } +/** + * mnt_table_find_fs: + * @tb: tab pointer + * @fs: entry to look for + * + * Checks if @fs is part of table @tb. + * + * Returns: index of @fs in table, 0 if not found or negative number in case of error. + */ +int mnt_table_find_fs(struct libmnt_table *tb, struct libmnt_fs *fs) +{ + struct list_head *p; + int i = 0; + + if (!tb || !fs) + return -EINVAL; + + if (list_empty(&fs->ents)) + return 0; + + /* Let's use directly list rather than mnt_table_next_fs() as we + * compare list entry with fs only. + */ + list_for_each(p, &tb->ents) { + ++i; + if (list_entry(p, struct libmnt_fs, ents) == fs) + return i; + } + + return 0; +} + /** * mnt_table_add_fs: * @tb: tab pointer @@ -435,12 +440,12 @@ struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb) */ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) { - assert(tb); - assert(fs); - if (!tb || !fs) return -EINVAL; + if (!list_empty(&fs->ents)) + return -EBUSY; + mnt_ref_fs(fs); list_add_tail(&fs->ents, &tb->ents); tb->nents++; @@ -450,6 +455,95 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) return 0; } +static int __table_insert_fs( + struct libmnt_table *tb, int before, + struct libmnt_fs *pos, struct libmnt_fs *fs) +{ + struct list_head *head = pos ? &pos->ents : &tb->ents; + + if (before) + list_add(&fs->ents, head); + else + list_add_tail(&fs->ents, head); + + tb->nents++; + + DBG(TAB, ul_debugobj(tb, "insert entry: %s %s", + mnt_fs_get_source(fs), mnt_fs_get_target(fs))); + return 0; +} + +/** + * mnt_table_insert_fs: + * @tb: tab pointer + * @before: 1 to insert before pos, 0 to insert after pos + * @pos: entry to specify position or NULL + * @fs: new entry + * + * Adds a new entry to @tb before or after a specific table entry @pos. If the + * @pos is NULL than add the begin of the @tab if @before is 1; or to the tail + * of the @tb if @before is 0. + * + * This function inncrements reference to @fs. Don't forget to use + * mnt_unref_fs() after mnt_table_insert_fs() if you want to keep the @fs + * referenced by the table only. + + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_insert_fs(struct libmnt_table *tb, int before, + struct libmnt_fs *pos, struct libmnt_fs *fs) +{ + if (!tb || !fs) + return -EINVAL; + + if (!list_empty(&fs->ents)) + return -EBUSY; + + if (pos && mnt_table_find_fs(tb, pos) < 1) + return -ENOENT; + + mnt_ref_fs(fs); + return __table_insert_fs(tb, before, pos, fs); +} + +/** + * mnt_table_move_fs: + * @src: tab pointer of source table + * @dst: tab pointer of destination table + * @before: 1 to move before position, 0 to move after position + * @pos: entry to specify position or NULL + * @fs: entry to move + * + * Removes @fs from @src table and adds it before/after a specific entry @pos + * of @dst table. If the @pos is NULL than add the begin of the @dst if @before + * is 1; or to the tail of the @dst if @before is 0. + * + * The reference counter of @fs is not modified. + * + * Returns: 0 on success or negative number in case of error. + */ +int mnt_table_move_fs(struct libmnt_table *src, struct libmnt_table *dst, + int before, struct libmnt_fs *pos, struct libmnt_fs *fs) +{ + if (!src || !dst || !fs) + return -EINVAL; + + if (mnt_table_find_fs(src, fs) < 1) + return -ENOENT; + + if (pos && mnt_table_find_fs(dst, pos) < 1) + return -ENOENT; + + /* remove from source */ + list_del_init(&fs->ents); + src->nents--; + + /* insert to the destination */ + return __table_insert_fs(dst, before, pos, fs); +} + + /** * mnt_table_remove_fs: * @tb: tab pointer @@ -463,29 +557,41 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) */ int mnt_table_remove_fs(struct libmnt_table *tb, struct libmnt_fs *fs) { - assert(tb); - assert(fs); - - if (!tb || !fs) + if (!tb || !fs || mnt_table_find_fs(tb, fs) < 1) return -EINVAL; - list_del(&fs->ents); - INIT_LIST_HEAD(&fs->ents); /* otherwise FS still points to the list */ + list_del_init(&fs->ents); mnt_unref_fs(fs); tb->nents--; return 0; } +static inline struct libmnt_fs *get_parent_fs(struct libmnt_table *tb, struct libmnt_fs *fs) +{ + struct libmnt_iter itr; + struct libmnt_fs *x; + int parent_id = mnt_fs_get_parent_id(fs); + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + while (mnt_table_next_fs(tb, &itr, &x) == 0) { + if (mnt_fs_get_id(x) == parent_id) + return x; + } + + return NULL; +} + /** * mnt_table_get_root_fs: * @tb: mountinfo file (/proc/self/mountinfo) * @root: returns pointer to the root filesystem (/) * - * The function uses the parent ID from the mountinfo file to determine the root filesystem - * (the filesystem with the smallest ID). The function is designed mostly for - * applications where it is necessary to sort mountpoints by IDs to get the tree - * of the mountpoints (e.g. findmnt default output). + * The function uses the parent ID from the mountinfo file to determine the + * root filesystem (the filesystem with the smallest ID with parent ID missing + * in the table). The function is designed mostly for applications where it is + * necessary to sort mountpoints by IDs to get the tree of the mountpoints + * (e.g. findmnt default output). * * If you're not sure, then use * @@ -501,9 +607,6 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) struct libmnt_fs *fs; int root_id = 0; - assert(tb); - assert(root); - if (!tb || !root || !is_mountinfo(tb)) return -EINVAL; @@ -511,6 +614,7 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) *root = NULL; + /* get smallest possible ID from the table */ mnt_reset_iter(&itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { int id = mnt_fs_get_parent_id(fs); @@ -521,6 +625,15 @@ int mnt_table_get_root_fs(struct libmnt_table *tb, struct libmnt_fs **root) } } + /* go to the root node by "parent_id -> id" relation */ + while (*root) { + struct libmnt_fs *x = get_parent_fs(tb, *root); + if (!x || x == *root) + break; + DBG(TAB, ul_debugobj(tb, " messy mountinfo, walk to %s", mnt_fs_get_target(x))); + *root = x; + } + return *root ? 0 : -EINVAL; } @@ -612,10 +725,6 @@ int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct l { int rc = 1; - assert(tb); - assert(itr); - assert(fs); - if (!tb || !itr || !fs) return -EINVAL; *fs = NULL; @@ -639,9 +748,6 @@ int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct l */ int mnt_table_first_fs(struct libmnt_table *tb, struct libmnt_fs **fs) { - assert(tb); - assert(fs); - if (!tb || !fs) return -EINVAL; if (list_empty(&tb->ents)) @@ -659,9 +765,6 @@ int mnt_table_first_fs(struct libmnt_table *tb, struct libmnt_fs **fs) */ int mnt_table_last_fs(struct libmnt_table *tb, struct libmnt_fs **fs) { - assert(tb); - assert(fs); - if (!tb || !fs) return -EINVAL; if (list_empty(&tb->ents)) @@ -713,8 +816,6 @@ static int mnt_table_move_parent(struct libmnt_table *tb, int oldid, int newid) struct libmnt_iter itr; struct libmnt_fs *fs; - assert(tb); - if (!tb) return -EINVAL; if (list_empty(&tb->ents)) @@ -744,7 +845,7 @@ static int mnt_table_move_parent(struct libmnt_table *tb, int oldid, int newid) * backward mode iterator). * * @MNT_UNIQ_FORWARD: remove later mounted filesystems - * @MNT_UNIQ_KEEPTREE: keep parent->id relation ship stil valid + * @MNT_UNIQ_KEEPTREE: keep parent->id relationship still valid * * Returns: negative number in case of error, or 0 o success. */ @@ -757,9 +858,6 @@ int mnt_table_uniq_fs(struct libmnt_table *tb, int flags, struct libmnt_fs *fs; int direction = MNT_ITER_BACKWARD; - assert(tb); - assert(cmp); - if (!tb || !cmp) return -EINVAL; if (list_empty(&tb->ents)) @@ -812,10 +910,6 @@ int mnt_table_uniq_fs(struct libmnt_table *tb, int flags, */ 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; @@ -841,6 +935,7 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, int direction) { char *mnt; + struct stat st; if (!tb || !path || !*path) return NULL; @@ -849,6 +944,9 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, DBG(TAB, ul_debugobj(tb, "lookup MOUNTPOINT: '%s'", path)); + if (mnt_stat_mountpoint(path, &st)) + return NULL; + mnt = strdup(path); if (!mnt) return NULL; @@ -864,7 +962,7 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, } p = stripoff_last_component(mnt); - if (!p || !*p) + if (!p) break; } while (mnt && *(mnt + 1) != '\0'); @@ -880,8 +978,10 @@ struct libmnt_fs *mnt_table_find_mountpoint(struct libmnt_table *tb, * * Try to lookup an entry in the given tab, three iterations are possible, the first * with @path, the second with realpath(@path) and the third with realpath(@path) - * against realpath(fs->target). The 2nd and 3rd iterations are not performed - * when the @tb cache is not set (see mnt_table_set_cache()). + * against realpath(fs->target). The 2nd and 3rd iterations are not performed when + * the @tb cache is not set (see mnt_table_set_cache()). If + * mnt_cache_set_targets(cache, mtab) was called, the 3rd iteration skips any + * @fs->target found in @mtab (see mnt_resolve_target()). * * Returns: a tab entry or NULL. */ @@ -891,9 +991,6 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat struct libmnt_fs *fs = NULL; char *cn; - assert(tb); - assert(path); - if (!tb || !path || !*path) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) @@ -907,6 +1004,20 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat if (mnt_fs_streq_target(fs, path)) return fs; } + + /* try absolute path */ + if (is_relative_path(path) && (cn = absolute_path(path))) { + DBG(TAB, ul_debugobj(tb, "lookup absolute TARGET: '%s'", cn)); + mnt_reset_iter(&itr, direction); + while (mnt_table_next_fs(tb, &itr, &fs) == 0) { + if (mnt_fs_streq_target(fs, cn)) { + free(cn); + return fs; + } + } + free(cn); + } + if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) return NULL; @@ -919,7 +1030,7 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat return fs; } - /* non-canonicaled path in struct libmnt_table + /* non-canonical path in struct libmnt_table * -- note that mountpoint in /proc/self/mountinfo is already * canonicalized by the kernel */ @@ -933,7 +1044,7 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat || (*fs->target == '/' && *(fs->target + 1) == '\0')) continue; - p = mnt_resolve_path(fs->target, tb->cache); + p = mnt_resolve_target(fs->target, tb->cache); /* both canonicalized, strcmp() is fine here */ if (p && strcmp(cn, p) == 0) return fs; @@ -954,6 +1065,8 @@ struct libmnt_fs *mnt_table_find_target(struct libmnt_table *tb, const char *pat * The 2nd, 3rd and 4th iterations are not performed when the @tb cache is not * set (see mnt_table_set_cache()). * + * For btrfs returns tab entry for default id. + * * Note that NULL is a valid source path; it will be replaced with "none". The * "none" is used in /proc/{mounts,self/mountinfo} for pseudo filesystems. * @@ -967,7 +1080,6 @@ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *pa char *cn; const char *p; - assert(tb); if (!tb || !path || !*path) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) @@ -977,9 +1089,33 @@ struct libmnt_fs *mnt_table_find_srcpath(struct libmnt_table *tb, const char *pa /* native paths */ mnt_reset_iter(&itr, direction); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { - if (mnt_fs_streq_srcpath(fs, path)) + + if (mnt_fs_streq_srcpath(fs, path)) { +#ifdef HAVE_BTRFS_SUPPORT + if (fs->fstype && !strcmp(fs->fstype, "btrfs")) { + uint64_t default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs)); + char *val; + size_t len; + + if (default_id == UINT64_MAX) + DBG(TAB, ul_debug("not found btrfs volume setting")); + + else if (mnt_fs_get_option(fs, "subvolid", &val, &len) == 0) { + uint64_t subvol_id; + + if (mnt_parse_offset(val, len, &subvol_id)) { + DBG(TAB, ul_debugobj(tb, "failed to parse subvolid=")); + continue; + } + if (subvol_id != default_id) + continue; + } + } +#endif /* HAVE_BTRFS_SUPPORT */ return fs; + } if (mnt_fs_get_tag(fs, NULL, NULL) == 0) ntags++; } @@ -1074,10 +1210,6 @@ struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, struct libmnt_iter itr; struct libmnt_fs *fs = NULL; - assert(tb); - assert(tag); - assert(val); - if (!tb || !tag || !*tag || !val) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) @@ -1103,6 +1235,50 @@ struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag, return NULL; } +/** + * mnt_table_find_target_with_option: + * @tb: tab pointer + * @path: mountpoint directory + * @option: option name (e.g "subvol", "subvolid", ...) + * @val: option value or NULL + * @direction: MNT_ITER_{FORWARD,BACKWARD} + * + * Try to lookup an entry in the given tab that matches combination of @path + * and @option. In difference to mnt_table_find_target(), only @path iteration + * is done. No lookup by device name, no canonicalization. + * + * Returns: a tab entry or NULL. + * + * Since: 2.28 + */ +struct libmnt_fs *mnt_table_find_target_with_option( + struct libmnt_table *tb, const char *path, + const char *option, const char *val, int direction) +{ + struct libmnt_iter itr; + struct libmnt_fs *fs = NULL; + char *optval = NULL; + size_t optvalsz = 0, valsz = val ? strlen(val) : 0; + + if (!tb || !path || !*path || !option || !*option || !val) + return NULL; + if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) + return NULL; + + DBG(TAB, ul_debugobj(tb, "lookup TARGET: '%s' with OPTION %s %s", path, option, val)); + + /* look up by native @target with OPTION */ + mnt_reset_iter(&itr, direction); + while (mnt_table_next_fs(tb, &itr, &fs) == 0) { + if (mnt_fs_streq_target(fs, path) + && mnt_fs_get_option(fs, option, &optval, &optvalsz) == 0 + && (!val || (optvalsz == valsz + && strncmp(optval, val, optvalsz) == 0))) + return fs; + } + return NULL; +} + /** * mnt_table_find_source: * @tb: tab pointer @@ -1121,8 +1297,6 @@ struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb, struct libmnt_fs *fs; char *t = NULL, *v = NULL; - assert(tb); - if (!tb) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) @@ -1160,9 +1334,6 @@ struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb, const char *sourc struct libmnt_fs *fs = NULL; struct libmnt_iter itr; - assert(tb); - assert(target); - if (!tb || !target || !*target || !source || !*source) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) @@ -1198,8 +1369,6 @@ struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb, struct libmnt_fs *fs = NULL; struct libmnt_iter itr; - assert(tb); - if (!tb) return NULL; if (direction != MNT_ITER_FORWARD && direction != MNT_ITER_BACKWARD) @@ -1217,6 +1386,148 @@ struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb, return NULL; } +static char *remove_mountpoint_from_path(const char *path, const char *mnt) +{ + char *res; + const char *p; + size_t sz; + + sz = strlen(mnt); + p = sz > 1 ? path + sz : path; + + res = *p ? strdup(p) : strdup("/"); + DBG(UTILS, ul_debug("%s fs-root is %s", path, res)); + return res; +} + +#ifdef HAVE_BTRFS_SUPPORT +static int get_btrfs_fs_root(struct libmnt_table *tb, struct libmnt_fs *fs, char **root) +{ + char *vol = NULL, *p; + size_t sz, volsz = 0; + + DBG(BTRFS, ul_debug("lookup for btrfs FS root")); + *root = NULL; + + if (mnt_fs_get_option(fs, "subvolid", &vol, &volsz) == 0) { + char *target; + struct libmnt_fs *f; + char subvolidstr[sizeof(stringify_value(UINT64_MAX))]; + + DBG(BTRFS, ul_debug(" found subvolid=%s, checking", vol)); + + assert (volsz + 1 < sizeof(stringify_value(UINT64_MAX))); + memcpy(subvolidstr, vol, volsz); + subvolidstr[volsz] = '\0'; + + target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache); + if (!target) + goto err; + + DBG(BTRFS, ul_debug(" trying target=%s subvolid=%s", target, subvolidstr)); + f = mnt_table_find_target_with_option(tb, target, + "subvolid", subvolidstr, + MNT_ITER_BACKWARD); + if (!tb->cache) + free(target); + if (!f) + goto not_found; + + /* Instead of set of BACKREF queries constructing subvol path + * corresponding to a particular subvolid, use the one in + * mountinfo. Kernel keeps subvol path up to date. + */ + if (mnt_fs_get_option(f, "subvol", &vol, &volsz) != 0) + goto not_found; + + } else if (mnt_fs_get_option(fs, "subvol", &vol, &volsz) != 0) { + /* If fstab entry does not contain "subvol", we have to + * check, whether btrfs has default subvolume defined. + */ + uint64_t default_id; + char *target; + struct libmnt_fs *f; + char default_id_str[sizeof(stringify_value(UINT64_MAX))]; + + DBG(BTRFS, ul_debug(" subvolid/subvol not found, checking default")); + + default_id = btrfs_get_default_subvol_id(mnt_fs_get_target(fs)); + if (default_id == UINT64_MAX) + goto not_found; + + /* Volume has default subvolume. Check if it matches to + * the one in mountinfo. + * + * Only kernel >= 4.2 reports subvolid. On older + * kernels, there is no reasonable way to detect which + * subvolume was mounted. + */ + target = mnt_resolve_target(mnt_fs_get_target(fs), tb->cache); + if (!target) + goto err; + + snprintf(default_id_str, sizeof(default_id_str), "%llu", + (unsigned long long int) default_id); + + DBG(BTRFS, ul_debug(" trying target=%s default subvolid=%s", + target, default_id_str)); + + f = mnt_table_find_target_with_option(tb, target, + "subvolid", default_id_str, + MNT_ITER_BACKWARD); + if (!tb->cache) + free(target); + if (!f) + goto not_found; + + /* Instead of set of BACKREF queries constructing + * subvol path, use the one in mountinfo. Kernel does + * the evaluation for us. + */ + DBG(BTRFS, ul_debug("setting FS root: btrfs default subvolid = %s", + default_id_str)); + + if (mnt_fs_get_option(f, "subvol", &vol, &volsz) != 0) + goto not_found; + } + + DBG(BTRFS, ul_debug(" using subvol=%s", vol)); + 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'; + return 0; + +not_found: + DBG(BTRFS, ul_debug(" not found btrfs volume setting")); + return 1; +err: + DBG(BTRFS, ul_debug(" error on btrfs volume setting evaluation")); + return errno ? -errno : -1; +} +#endif /* HAVE_BTRFS_SUPPORT */ + +static const char *get_cifs_unc_subdir_path (const char *unc) +{ + /* + * 1 or more slash: %*[/] + * 1 or more non-slash: %*[^/] + * number of byte read: %n + */ + int share_end = 0; + int r = sscanf(unc, "%*[/]%*[^/]%*[/]%*[^/]%n", &share_end); + if (r == EOF || share_end == 0) + return NULL; + return unc + share_end; +} + /* * tb: /proc/self/mountinfo * fs: filesystem @@ -1226,6 +1537,8 @@ struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb, * * For btrfs subvolumes this function returns NULL, but @fsroot properly set. * + * If @tb is NULL then defaults to '/'. + * * Returns: entry from @tb that will be used as a source for @fs if the @fs is * bindmount. * @@ -1236,8 +1549,8 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, unsigned long mountflags, char **fsroot) { - char *root = NULL, *mnt = NULL; - const char *fstype; + char *root = NULL; + const char *mnt = NULL; struct libmnt_fs *src_fs = NULL; assert(fs); @@ -1245,8 +1558,6 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, DBG(TAB, ul_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; @@ -1254,10 +1565,14 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, DBG(TAB, ul_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 (src) { + struct libmnt_fs *f = mnt_table_find_mountpoint(tb, + src, MNT_ITER_BACKWARD); + if (f) + mnt = mnt_fs_get_target(f); + } if (mnt) - root = mnt_get_fs_root(src, mnt); + root = remove_mountpoint_from_path(src, mnt); if (xsrc && !tb->cache) { free(xsrc); @@ -1272,48 +1587,48 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb, 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 + /* It's possible that fstab_fs source is subdirectory on btrfs + * subvolume or another bind mount. For example: + * + * /dev/sdc /mnt/test btrfs subvol=/anydir + * /dev/sdc /mnt/test btrfs defaults + * /mnt/test/foo /mnt/test2 auto bind + * + * in this case, the root for /mnt/test2 will be /anydir/foo on + * /dev/sdc. It means we have to compose the final root from + * root and src_root. */ src_root = mnt_fs_get_root(src_fs); + + DBG(FS, ul_debugobj(fs, "source root: %s, source FS root: %s", root, src_root)); + 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; + if (strcmp(root, "/") == 0) { + free(root); + root = strdup(src_root); + if (!root) + goto err; + } else { + char *tmp; + if (asprintf(&tmp, "%s%s", src_root, root) < 0) + goto err; + free(root); + root = tmp; + } } } +#ifdef HAVE_BTRFS_SUPPORT /* * 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, ul_debug("setting FS root: btrfs subvol")); - - sz = volsz; - if (*vol != '/') - sz++; - root = malloc(sz + 1); - if (!root) + else if (tb && fs->fstype && + (!strcmp(fs->fstype, "btrfs") || !strcmp(fs->fstype, "auto"))) { + if (get_btrfs_fs_root(tb, fs, &root) < 0) goto err; - p = root; - if (*vol != '/') - *p++ = '/'; - memcpy(p, vol, volsz); - *(root + sz) = '\0'; } +#endif /* HAVE_BTRFS_SUPPORT */ + dflt: if (!root) { root = strdup("/"); @@ -1324,16 +1639,14 @@ dflt: DBG(TAB, ul_debug("FS root result: %s", root)); - free(mnt); return src_fs; err: free(root); - free(mnt); return NULL; } /** - * mnt_table_is_fs__mounted: + * mnt_table_is_fs_mounted: * @tb: /proc/self/mountinfo file * @fstab_fs: /etc/fstab entry * @@ -1358,16 +1671,15 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) struct libmnt_fs *fs; char *root = NULL; + char *src2 = NULL; const char *src = NULL, *tgt = NULL; char *xtgt = NULL; int rc = 0; dev_t devno = 0; - assert(tb); - assert(fstab_fs); - - DBG(FS, ul_debugobj(fstab_fs, "is FS mounted? [target=%s]", - mnt_fs_get_target(fstab_fs))); + DBG(FS, ul_debugobj(fstab_fs, "mnt_table_is_fs_mounted: target=%s, source=%s", + mnt_fs_get_target(fstab_fs), + mnt_fs_get_source(fstab_fs))); if (mnt_fs_is_swaparea(fstab_fs) || mnt_table_is_empty(tb)) { DBG(FS, ul_debugobj(fstab_fs, "- ignore (swap or no data)")); @@ -1379,12 +1691,22 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) struct libmnt_fs *rootfs; int flags = 0; - if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0) + if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0 || + mnt_fs_get_option(fstab_fs, "rbind", NULL, NULL) == 0) flags = MS_BIND; rootfs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root); - if (rootfs) + if (rootfs) { + const char *fstype = mnt_fs_get_fstype(rootfs); + src = mnt_fs_get_srcpath(rootfs); + if (fstype && strncmp(fstype, "nfs", 3) == 0 && root) { + /* NFS stores the root at the end of the source */ + src = src2 = strappend(src, root); + free(root); + root = NULL; + } + } } if (!src) @@ -1409,6 +1731,8 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) } mnt_reset_iter(&itr, MNT_ITER_FORWARD); + DBG(FS, ul_debugobj(fstab_fs, "mnt_table_is_fs_mounted: src=%s, tgt=%s, root=%s", src, tgt, root)); + while (mnt_table_next_fs(tb, &itr, &fs) == 0) { int eq = mnt_fs_streq_srcpath(fs, src); @@ -1423,28 +1747,42 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) uint64_t offset = 0; char *val; size_t len; - int flags; + int flags = 0; - if (!mnt_fs_is_kernel(fs) || - !mnt_fs_get_srcpath(fs) || + if (!mnt_fs_get_srcpath(fs) || !startswith(mnt_fs_get_srcpath(fs), "/dev/loop")) continue; /* does not look like loopdev */ - if (mnt_fs_get_option(fstab_fs, "offset", &val, &len) == 0 && - mnt_parse_offset(val, len, &offset)) { - DBG(FS, ul_debugobj(fstab_fs, "failed to parse offset=")); - continue; - } else + if (mnt_fs_get_option(fstab_fs, "offset", &val, &len) == 0) { + if (mnt_parse_offset(val, len, &offset)) { + DBG(FS, ul_debugobj(fstab_fs, "failed to parse offset=")); + continue; + } flags = LOOPDEV_FL_OFFSET; + } - if (loopdev_is_used(mnt_fs_get_srcpath(fs), src, offset, flags)) - break; + DBG(FS, ul_debugobj(fs, "checking for loop: src=%s", mnt_fs_get_srcpath(fs))); +#if __linux__ + if (!loopdev_is_used(mnt_fs_get_srcpath(fs), src, offset, 0, flags)) + continue; + + DBG(FS, ul_debugobj(fs, "used loop")); +#endif } if (root) { - const char *r = mnt_fs_get_root(fs); - if (!r || strcmp(r, root) != 0) - continue; + const char *fstype = mnt_fs_get_fstype(fs); + + if (fstype && strcmp(fstype, "cifs") == 0) { + const char *unc_subdir = get_cifs_unc_subdir_path(src); + const char *path_on_fs = mnt_fs_get_root(fs); + if (!unc_subdir || !path_on_fs || !streq_paths(unc_subdir, path_on_fs)) + continue; + } else { + const char *r = mnt_fs_get_root(fs); + if (!r || strcmp(r, root) != 0) + continue; + } } /* @@ -1468,6 +1806,7 @@ done: free(root); DBG(TAB, ul_debugobj(tb, "mnt_table_is_fs_mounted: %s [rc=%d]", src, rc)); + free(src2); return rc; } @@ -1481,7 +1820,7 @@ static int parser_errcb(struct libmnt_table *tb, const char *filename, int line) return 1; /* all errors are recoverable -- this is the default */ } -struct libmnt_table *create_table(const char *file, int comments) +static struct libmnt_table *create_table(const char *file, int comments) { struct libmnt_table *tb; @@ -1503,7 +1842,7 @@ err: return NULL; } -int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[]) +static int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs; @@ -1533,7 +1872,7 @@ done: return rc; } -int test_parse(struct libmnt_test *ts, int argc, char *argv[]) +static int test_parse(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_table *tb = NULL; struct libmnt_iter *itr = NULL; @@ -1569,7 +1908,52 @@ done: return rc; } -int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) +static int test_find_idx(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_table *tb; + struct libmnt_fs *fs = NULL; + struct libmnt_cache *mpc = NULL; + const char *file, *what; + int rc = -1; + + if (argc != 3) { + fprintf(stderr, "try --help\n"); + return -EINVAL; + } + + file = argv[1], what = argv[2]; + + tb = create_table(file, FALSE); + if (!tb) + goto done; + + /* create a cache for canonicalized paths */ + mpc = mnt_new_cache(); + if (!mpc) + goto done; + mnt_table_set_cache(tb, mpc); + mnt_unref_cache(mpc); + + fs = mnt_table_find_target(tb, what, MNT_ITER_BACKWARD); + + if (!fs) + fprintf(stderr, "%s: not found '%s'\n", file, what); + else { + int idx = mnt_table_find_fs(tb, fs); + + if (idx < 1) + fprintf(stderr, "%s: not found '%s' fs pointer", file, what); + else { + printf("%s index is %d\n", what, idx); + rc = 0; + } + } +done: + mnt_unref_table(tb); + return rc; +} + +static int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) { struct libmnt_table *tb; struct libmnt_fs *fs = NULL; @@ -1611,17 +1995,17 @@ done: return rc; } -int test_find_bw(struct libmnt_test *ts, int argc, char *argv[]) +static 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[]) +static 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[]) +static int test_find_pair(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs; @@ -1648,7 +2032,7 @@ done: return rc; } -int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) +static int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs; @@ -1681,9 +2065,14 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) struct libmnt_fs *fs; struct libmnt_iter *itr = NULL; struct libmnt_cache *mpc = NULL; - int rc; + int writable = 0; + const char *path = NULL; + + if (mnt_has_regular_mtab(&path, &writable) == 1 && writable == 0) + tb = mnt_new_table_from_file(path); + else + tb = mnt_new_table_from_file("/proc/self/mountinfo"); - tb = mnt_new_table_from_file("/proc/self/mountinfo"); if (!tb) { fprintf(stderr, "failed to parse mountinfo\n"); return -1; @@ -1703,7 +2092,7 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) mnt_table_set_cache(tb, mpc); mnt_unref_cache(mpc); - while(mnt_table_next_fs(fstab, itr, &fs) == 0) { + 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), @@ -1714,12 +2103,11 @@ static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) mnt_fs_get_target(fs)); } - rc = 0; done: mnt_unref_table(tb); mnt_unref_table(fstab); mnt_free_iter(itr); - return rc; + return 0; } /* returns 0 if @a and @b targets are the same */ @@ -1762,6 +2150,7 @@ done: return rc; } + int main(int argc, char *argv[]) { struct libmnt_test tss[] = { @@ -1770,9 +2159,10 @@ int main(int argc, char *argv[]) { "--find-backward", test_find_bw, " " }, { "--uniq-target", test_uniq, "" }, { "--find-pair", test_find_pair, " " }, + { "--find-fs", test_find_idx, " " }, { "--find-mountpoint", test_find_mountpoint, "" }, { "--copy-fs", test_copy_fs, " copy root FS from the file" }, - { "--is-mounted", test_is_mounted, " check what from are already mounted" }, + { "--is-mounted", test_is_mounted, " check what from fstab is already mounted" }, { NULL } };