1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * CDDL HEADER START
4 *
5 * The contents of this file are subject to the terms of the
6 * Common Development and Distribution License (the "License").
7 * You may not use this file except in compliance with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or https://opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23 /*
24 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
25 * Copyright 2019 Joyent, Inc.
26 * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
27 * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
28 * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
29 * Copyright (c) 2013 Martin Matuska. All rights reserved.
30 * Copyright (c) 2013 Steven Hartland. All rights reserved.
31 * Copyright 2017 Nexenta Systems, Inc.
32 * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
33 * Copyright 2017-2018 RackTop Systems.
34 * Copyright (c) 2019 Datto Inc.
35 * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
36 * Copyright (c) 2021 Matt Fiddaman
37 * Copyright (c) 2026, TrueNAS.
38 */
39
40 #include <sys/mntent.h>
41 #include <sys/mutex.h>
42 #include <libzfs.h>
43 #include "libzfs_impl.h"
44
45 typedef struct mnttab_node {
46 struct mnttab mtn_mt;
47 avl_node_t mtn_node;
48 } mnttab_node_t;
49
50 static mnttab_node_t *
mnttab_node_alloc(libzfs_handle_t * hdl,const char * special,const char * mountp,const char * mntopts)51 mnttab_node_alloc(libzfs_handle_t *hdl, const char *special,
52 const char *mountp, const char *mntopts)
53 {
54 mnttab_node_t *mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
55 mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
56 mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
57 mtn->mtn_mt.mnt_fstype = (char *)MNTTYPE_ZFS;
58 mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
59 return (mtn);
60 }
61
62 static void
mnttab_node_free(libzfs_handle_t * hdl,mnttab_node_t * mtn)63 mnttab_node_free(libzfs_handle_t *hdl, mnttab_node_t *mtn)
64 {
65 (void) hdl;
66 free(mtn->mtn_mt.mnt_special);
67 free(mtn->mtn_mt.mnt_mountp);
68 free(mtn->mtn_mt.mnt_mntopts);
69 free(mtn);
70 }
71
72 static int
mnttab_compare(const void * arg1,const void * arg2)73 mnttab_compare(const void *arg1, const void *arg2)
74 {
75 const mnttab_node_t *mtn1 = (const mnttab_node_t *)arg1;
76 const mnttab_node_t *mtn2 = (const mnttab_node_t *)arg2;
77 int rv;
78
79 rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
80
81 return (TREE_ISIGN(rv));
82 }
83
84 static void
mnttab_drop(libzfs_handle_t * hdl)85 mnttab_drop(libzfs_handle_t *hdl)
86 {
87 mnttab_node_t *mtn;
88 void *cookie = NULL;
89 while ((mtn = avl_destroy_nodes(&hdl->zh_mnttab, &cookie)) != NULL)
90 mnttab_node_free(hdl, mtn);
91 }
92
93 static int
mnttab_update(libzfs_handle_t * hdl)94 mnttab_update(libzfs_handle_t *hdl)
95 {
96 FILE *mnttab;
97 struct mnttab entry;
98
99 ASSERT(MUTEX_HELD(&hdl->zh_mnttab_lock));
100
101 if ((mnttab = fopen(MNTTAB, "re")) == NULL)
102 return (ENOENT);
103
104 while (getmntent(mnttab, &entry) == 0) {
105 mnttab_node_t *mtn;
106 avl_index_t where;
107
108 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
109 continue;
110
111 mtn = mnttab_node_alloc(hdl, entry.mnt_special,
112 entry.mnt_mountp, entry.mnt_mntopts);
113
114 /* Exclude duplicate mounts */
115 if (avl_find(&hdl->zh_mnttab, mtn, &where) != NULL) {
116 mnttab_node_free(hdl, mtn);
117 continue;
118 }
119
120 avl_add(&hdl->zh_mnttab, mtn);
121 }
122
123 (void) fclose(mnttab);
124 return (0);
125 }
126
127
128 void
libzfs_mnttab_init(libzfs_handle_t * hdl)129 libzfs_mnttab_init(libzfs_handle_t *hdl)
130 {
131 mutex_init(&hdl->zh_mnttab_lock, NULL, MUTEX_DEFAULT, NULL);
132 assert(avl_numnodes(&hdl->zh_mnttab) == 0);
133 avl_create(&hdl->zh_mnttab, mnttab_compare,
134 sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
135 hdl->zh_mnttab_cache_enabled = B_FALSE;
136 }
137
138 void
libzfs_mnttab_fini(libzfs_handle_t * hdl)139 libzfs_mnttab_fini(libzfs_handle_t *hdl)
140 {
141 mnttab_drop(hdl);
142 avl_destroy(&hdl->zh_mnttab);
143 (void) mutex_destroy(&hdl->zh_mnttab_lock);
144 }
145
146 void
libzfs_mnttab_cache(libzfs_handle_t * hdl,boolean_t enable)147 libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
148 {
149 mutex_enter(&hdl->zh_mnttab_lock);
150 hdl->zh_mnttab_cache_enabled = enable;
151 mutex_exit(&hdl->zh_mnttab_lock);
152 }
153
154 int
libzfs_mnttab_find(libzfs_handle_t * hdl,const char * fsname,struct mnttab * entry)155 libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
156 struct mnttab *entry)
157 {
158 mnttab_node_t find;
159 mnttab_node_t *mtn;
160 int ret = ENOENT;
161
162 mutex_enter(&hdl->zh_mnttab_lock);
163 if (!hdl->zh_mnttab_cache_enabled)
164 mnttab_drop(hdl);
165
166 if (avl_numnodes(&hdl->zh_mnttab) == 0) {
167 int error;
168
169 if ((error = mnttab_update(hdl)) != 0) {
170 mutex_exit(&hdl->zh_mnttab_lock);
171 return (error);
172 }
173 }
174
175 find.mtn_mt.mnt_special = (char *)fsname;
176 mtn = avl_find(&hdl->zh_mnttab, &find, NULL);
177 if (mtn) {
178 *entry = mtn->mtn_mt;
179 ret = 0;
180 }
181 mutex_exit(&hdl->zh_mnttab_lock);
182 return (ret);
183 }
184
185 void
libzfs_mnttab_add(libzfs_handle_t * hdl,const char * special,const char * mountp,const char * mntopts)186 libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
187 const char *mountp, const char *mntopts)
188 {
189 mnttab_node_t *mtn;
190
191 mutex_enter(&hdl->zh_mnttab_lock);
192 if (!hdl->zh_mnttab_cache_enabled) {
193 /* Don't bother; we're going to discard it anyway. */
194 mutex_exit(&hdl->zh_mnttab_lock);
195 return;
196 }
197
198 mtn = mnttab_node_alloc(hdl, special, mountp, mntopts);
199
200 /*
201 * Another thread may have already added this entry
202 * via mnttab_update. If so we should skip it.
203 */
204 if (avl_find(&hdl->zh_mnttab, mtn, NULL) != NULL)
205 mnttab_node_free(hdl, mtn);
206 else
207 avl_add(&hdl->zh_mnttab, mtn);
208
209 mutex_exit(&hdl->zh_mnttab_lock);
210 }
211
212 void
libzfs_mnttab_remove(libzfs_handle_t * hdl,const char * fsname)213 libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
214 {
215 mnttab_node_t find;
216 mnttab_node_t *ret;
217
218 mutex_enter(&hdl->zh_mnttab_lock);
219 if (!hdl->zh_mnttab_cache_enabled) {
220 /* Don't bother; we're going to discard it anyway. */
221 mutex_exit(&hdl->zh_mnttab_lock);
222 return;
223 }
224
225 find.mtn_mt.mnt_special = (char *)fsname;
226 if ((ret = avl_find(&hdl->zh_mnttab, (void *)&find, NULL)) != NULL) {
227 avl_remove(&hdl->zh_mnttab, ret);
228 mnttab_node_free(hdl, ret);
229 }
230 mutex_exit(&hdl->zh_mnttab_lock);
231 }
232