// SPDX-License-Identifier: CDDL-1.0 /* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * https://opensource.org/license/CDDL-1.0. */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 Joyent, Inc. * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved. * Copyright (c) 2012 Pawel Jakub Dawidek . * Copyright (c) 2013 Martin Matuska. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright 2017 Nexenta Systems, Inc. * Copyright 2016 Igor Kozhukhov * Copyright 2017-2018 RackTop Systems. * Copyright (c) 2019 Datto Inc. * Copyright (c) 2019, loli10K * Copyright (c) 2021 Matt Fiddaman * Copyright (c) 2026, TrueNAS. */ #include #include #include #include "libzfs_impl.h" typedef struct mnttab_node { struct mnttab mtn_mt; avl_node_t mtn_node; } mnttab_node_t; static mnttab_node_t * mnttab_node_alloc(libzfs_handle_t *hdl, const char *special, const char *mountp, const char *mntopts) { mnttab_node_t *mtn = zfs_alloc(hdl, sizeof (mnttab_node_t)); mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special); mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp); mtn->mtn_mt.mnt_fstype = (char *)MNTTYPE_ZFS; mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts); return (mtn); } static void mnttab_node_free(libzfs_handle_t *hdl, mnttab_node_t *mtn) { (void) hdl; free(mtn->mtn_mt.mnt_special); free(mtn->mtn_mt.mnt_mountp); free(mtn->mtn_mt.mnt_mntopts); free(mtn); } static int mnttab_compare(const void *arg1, const void *arg2) { const mnttab_node_t *mtn1 = (const mnttab_node_t *)arg1; const mnttab_node_t *mtn2 = (const mnttab_node_t *)arg2; int rv; rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special); return (TREE_ISIGN(rv)); } static void mnttab_drop(libzfs_handle_t *hdl) { mnttab_node_t *mtn; void *cookie = NULL; while ((mtn = avl_destroy_nodes(&hdl->zh_mnttab, &cookie)) != NULL) mnttab_node_free(hdl, mtn); } static int mnttab_update(libzfs_handle_t *hdl) { FILE *mnttab; struct mnttab entry; ASSERT(MUTEX_HELD(&hdl->zh_mnttab_lock)); if ((mnttab = fopen(MNTTAB, "re")) == NULL) return (ENOENT); while (getmntent(mnttab, &entry) == 0) { mnttab_node_t *mtn; avl_index_t where; if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) continue; mtn = mnttab_node_alloc(hdl, entry.mnt_special, entry.mnt_mountp, entry.mnt_mntopts); /* Exclude duplicate mounts */ if (avl_find(&hdl->zh_mnttab, mtn, &where) != NULL) { mnttab_node_free(hdl, mtn); continue; } avl_add(&hdl->zh_mnttab, mtn); } (void) fclose(mnttab); return (0); } void libzfs_mnttab_init(libzfs_handle_t *hdl) { mutex_init(&hdl->zh_mnttab_lock, NULL, MUTEX_DEFAULT, NULL); assert(avl_numnodes(&hdl->zh_mnttab) == 0); avl_create(&hdl->zh_mnttab, mnttab_compare, sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node)); hdl->zh_mnttab_cache_enabled = B_FALSE; } void libzfs_mnttab_fini(libzfs_handle_t *hdl) { mnttab_drop(hdl); avl_destroy(&hdl->zh_mnttab); (void) mutex_destroy(&hdl->zh_mnttab_lock); } void libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable) { mutex_enter(&hdl->zh_mnttab_lock); hdl->zh_mnttab_cache_enabled = enable; mutex_exit(&hdl->zh_mnttab_lock); } int libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname, struct mnttab *entry) { mnttab_node_t find; mnttab_node_t *mtn; int ret = ENOENT; mutex_enter(&hdl->zh_mnttab_lock); if (!hdl->zh_mnttab_cache_enabled) mnttab_drop(hdl); if (avl_numnodes(&hdl->zh_mnttab) == 0) { int error; if ((error = mnttab_update(hdl)) != 0) { mutex_exit(&hdl->zh_mnttab_lock); return (error); } } find.mtn_mt.mnt_special = (char *)fsname; mtn = avl_find(&hdl->zh_mnttab, &find, NULL); if (mtn) { *entry = mtn->mtn_mt; ret = 0; } mutex_exit(&hdl->zh_mnttab_lock); return (ret); } void libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special, const char *mountp, const char *mntopts) { mnttab_node_t *mtn; mutex_enter(&hdl->zh_mnttab_lock); if (!hdl->zh_mnttab_cache_enabled) { /* Don't bother; we're going to discard it anyway. */ mutex_exit(&hdl->zh_mnttab_lock); return; } mtn = mnttab_node_alloc(hdl, special, mountp, mntopts); /* * Another thread may have already added this entry * via mnttab_update. If so we should skip it. */ if (avl_find(&hdl->zh_mnttab, mtn, NULL) != NULL) mnttab_node_free(hdl, mtn); else avl_add(&hdl->zh_mnttab, mtn); mutex_exit(&hdl->zh_mnttab_lock); } void libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname) { mnttab_node_t find; mnttab_node_t *ret; mutex_enter(&hdl->zh_mnttab_lock); if (!hdl->zh_mnttab_cache_enabled) { /* Don't bother; we're going to discard it anyway. */ mutex_exit(&hdl->zh_mnttab_lock); return; } find.mtn_mt.mnt_special = (char *)fsname; if ((ret = avl_find(&hdl->zh_mnttab, (void *)&find, NULL)) != NULL) { avl_remove(&hdl->zh_mnttab, ret); mnttab_node_free(hdl, ret); } mutex_exit(&hdl->zh_mnttab_lock); }