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 void
libzfs_mnttab_init(libzfs_handle_t * hdl)85 libzfs_mnttab_init(libzfs_handle_t *hdl)
86 {
87 mutex_init(&hdl->zh_mnttab_lock, NULL, MUTEX_DEFAULT, NULL);
88 assert(avl_numnodes(&hdl->zh_mnttab) == 0);
89 avl_create(&hdl->zh_mnttab, mnttab_compare,
90 sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
91 }
92
93 void
libzfs_mnttab_fini(libzfs_handle_t * hdl)94 libzfs_mnttab_fini(libzfs_handle_t *hdl)
95 {
96 void *cookie = NULL;
97 mnttab_node_t *mtn;
98
99 while ((mtn = avl_destroy_nodes(&hdl->zh_mnttab, &cookie))
100 != NULL)
101 mnttab_node_free(hdl, mtn);
102
103 avl_destroy(&hdl->zh_mnttab);
104 (void) mutex_destroy(&hdl->zh_mnttab_lock);
105 }
106
107 void
libzfs_mnttab_cache(libzfs_handle_t * hdl,boolean_t enable)108 libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
109 {
110 /* This is a no-op to preserve ABI backward compatibility. */
111 (void) hdl, (void) enable;
112 }
113
114 static int
mnttab_update(libzfs_handle_t * hdl)115 mnttab_update(libzfs_handle_t *hdl)
116 {
117 FILE *mnttab;
118 struct mnttab entry;
119
120 ASSERT(MUTEX_HELD(&hdl->zh_mnttab_lock));
121
122 if ((mnttab = fopen(MNTTAB, "re")) == NULL)
123 return (ENOENT);
124
125 while (getmntent(mnttab, &entry) == 0) {
126 mnttab_node_t *mtn;
127 avl_index_t where;
128
129 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
130 continue;
131
132 mtn = mnttab_node_alloc(hdl, entry.mnt_special,
133 entry.mnt_mountp, entry.mnt_mntopts);
134
135 /* Exclude duplicate mounts */
136 if (avl_find(&hdl->zh_mnttab, mtn, &where) != NULL) {
137 mnttab_node_free(hdl, mtn);
138 continue;
139 }
140
141 avl_add(&hdl->zh_mnttab, mtn);
142 }
143
144 (void) fclose(mnttab);
145 return (0);
146 }
147
148 int
libzfs_mnttab_find(libzfs_handle_t * hdl,const char * fsname,struct mnttab * entry)149 libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
150 struct mnttab *entry)
151 {
152 mnttab_node_t find;
153 mnttab_node_t *mtn;
154 int ret = ENOENT;
155
156 mutex_enter(&hdl->zh_mnttab_lock);
157 if (avl_numnodes(&hdl->zh_mnttab) == 0) {
158 int error;
159
160 if ((error = mnttab_update(hdl)) != 0) {
161 mutex_exit(&hdl->zh_mnttab_lock);
162 return (error);
163 }
164 }
165
166 find.mtn_mt.mnt_special = (char *)fsname;
167 mtn = avl_find(&hdl->zh_mnttab, &find, NULL);
168 if (mtn) {
169 *entry = mtn->mtn_mt;
170 ret = 0;
171 }
172 mutex_exit(&hdl->zh_mnttab_lock);
173 return (ret);
174 }
175
176 void
libzfs_mnttab_add(libzfs_handle_t * hdl,const char * special,const char * mountp,const char * mntopts)177 libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
178 const char *mountp, const char *mntopts)
179 {
180 mnttab_node_t *mtn;
181
182 mutex_enter(&hdl->zh_mnttab_lock);
183
184 mtn = mnttab_node_alloc(hdl, special, mountp, mntopts);
185
186 /*
187 * Another thread may have already added this entry
188 * via mnttab_update. If so we should skip it.
189 */
190 if (avl_find(&hdl->zh_mnttab, mtn, NULL) != NULL)
191 mnttab_node_free(hdl, mtn);
192 else
193 avl_add(&hdl->zh_mnttab, mtn);
194
195 mutex_exit(&hdl->zh_mnttab_lock);
196 }
197
198 void
libzfs_mnttab_remove(libzfs_handle_t * hdl,const char * fsname)199 libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
200 {
201 mnttab_node_t find;
202 mnttab_node_t *ret;
203
204 mutex_enter(&hdl->zh_mnttab_lock);
205 find.mtn_mt.mnt_special = (char *)fsname;
206 if ((ret = avl_find(&hdl->zh_mnttab, (void *)&find, NULL)) != NULL) {
207 avl_remove(&hdl->zh_mnttab, ret);
208 mnttab_node_free(hdl, ret);
209 }
210 mutex_exit(&hdl->zh_mnttab_lock);
211 }
212