xref: /freebsd/sys/contrib/openzfs/lib/libzfs/libzfs_mnttab.c (revision efa8679e7f69c9cc225613827d9f75644cca5b3b)
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