xref: /illumos-gate/usr/src/uts/common/fs/sharefs/sharetab.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Copyright 2018 Nexenta Systems, Inc.
28  * Copyright 2020 Joyent, Inc.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/types32.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <rpc/types.h>
36 #include <sys/vfs.h>
37 #include <sys/siginfo.h>
38 #include <sys/proc.h>		/* for exit() declaration */
39 #include <sys/kmem.h>
40 #include <sys/pathname.h>
41 #include <sys/debug.h>
42 #include <sys/vtrace.h>
43 #include <sys/cmn_err.h>
44 #include <sys/atomic.h>
45 #include <sys/policy.h>
46 
47 #include <sharefs/sharefs.h>
48 
49 /*
50  * A macro to avoid cut-and-paste errors on getting a string field
51  * from user-land.
52  */
53 #define	SHARETAB_COPYIN(field)						\
54 	if (copyinstr(STRUCT_FGETP(u_sh, sh_##field),			\
55 	    buf,							\
56 	    bufsz + 1,	/* Add one for extra NUL */			\
57 	    &len)) {							\
58 		error = EFAULT;						\
59 		goto cleanup;						\
60 	}								\
61 	/* Need to remove 1 because copyinstr() counts the NUL */	\
62 	len--;								\
63 	sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP);			\
64 	bcopy(buf, sh->sh_##field, len);				\
65 	sh->sh_##field[len] = '\0';					\
66 	shl.shl_##field = (int)len;					\
67 	sh->sh_size += shl.shl_##field;	/* Debug counting */
68 
69 #define	SHARETAB_DELETE_FIELD(field)					\
70 	if (sh->sh_##field != NULL) {					\
71 		kmem_free(sh->sh_##field,				\
72 		    shl ? shl->shl_##field + 1 :			\
73 		    strlen(sh->sh_##field) + 1);			\
74 	}
75 
76 static zone_key_t sharetab_zone_key;
77 
78 /*
79  * Take care of cleaning up a share.
80  * If passed in a length array, use it to determine how much
81  * space to clean up. Else, figure that out.
82  */
83 static void
84 sharefree(share_t *sh, sharefs_lens_t *shl)
85 {
86 	if (sh == NULL)
87 		return;
88 
89 	SHARETAB_DELETE_FIELD(path);
90 	SHARETAB_DELETE_FIELD(res);
91 	SHARETAB_DELETE_FIELD(fstype);
92 	SHARETAB_DELETE_FIELD(opts);
93 	SHARETAB_DELETE_FIELD(descr);
94 
95 	kmem_free(sh, sizeof (*sh));
96 }
97 
98 /*
99  * If there is no error, then this function is responsible for
100  * cleaning up the memory associated with the share argument.
101  */
102 static int
103 sharefs_remove(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
104 {
105 	int		iHash;
106 	sharetab_t	*sht;
107 	share_t		*s, *p;
108 	int		iPath;
109 
110 	if (!sh)
111 		return (ENOENT);
112 
113 	rw_enter(&sg->sharetab_lock, RW_WRITER);
114 	for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
115 		if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
116 			break;
117 	}
118 
119 	/*
120 	 * There does not exist a fstype in memory which
121 	 * matches the share passed in.
122 	 */
123 	if (sht == NULL) {
124 		rw_exit(&sg->sharetab_lock);
125 		return (ENOENT);
126 	}
127 
128 	iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
129 	iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
130 
131 	/*
132 	 * Now walk down the hash table and find the entry to free!
133 	 */
134 	for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
135 	    s != NULL; s = s->sh_next) {
136 		/*
137 		 * We need exact matches.
138 		 */
139 		if (strcmp(sh->sh_path, s->sh_path) == 0 &&
140 		    strlen(s->sh_path) == iPath) {
141 			if (p != NULL)
142 				p->sh_next = s->sh_next;
143 			else
144 				sht->s_buckets[iHash].ssh_sh = s->sh_next;
145 
146 			ASSERT(sht->s_buckets[iHash].ssh_count != 0);
147 			atomic_dec_32(&sht->s_buckets[iHash].ssh_count);
148 			atomic_dec_32(&sht->s_count);
149 			atomic_dec_32(&sg->sharetab_count);
150 
151 			ASSERT(sg->sharetab_size >= s->sh_size);
152 			sg->sharetab_size -= s->sh_size;
153 
154 			gethrestime(&sg->sharetab_mtime);
155 			atomic_inc_32(&sg->sharetab_generation);
156 
157 			break;
158 		}
159 
160 		p = s;
161 	}
162 
163 	rw_exit(&sg->sharetab_lock);
164 
165 	if (s == NULL)
166 		return (ENOENT);
167 
168 	s->sh_next = NULL;
169 	sharefree(s, NULL);
170 
171 	/* We need to free the share for the caller */
172 	sharefree(sh, shl);
173 
174 	return (0);
175 }
176 
177 /*
178  * The caller must have allocated memory for us to use.
179  */
180 static int
181 sharefs_add(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
182 {
183 	int		iHash;
184 	sharetab_t	*sht;
185 	share_t		*s, *p;
186 	int		iPath;
187 	int		n;
188 
189 	if (sh == NULL)
190 		return (ENOENT);
191 
192 	/* We need to find the hash buckets for the fstype */
193 	rw_enter(&sg->sharetab_lock, RW_WRITER);
194 	for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
195 		if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
196 			break;
197 	}
198 
199 	/* Did not exist, so allocate one and add it to the sharetab */
200 	if (sht == NULL) {
201 		sht = kmem_zalloc(sizeof (*sht), KM_SLEEP);
202 		n = strlen(sh->sh_fstype);
203 		sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP);
204 		(void) strncpy(sht->s_fstype, sh->sh_fstype, n);
205 
206 		sht->s_next = sg->sharefs_sharetab;
207 		sg->sharefs_sharetab = sht;
208 	}
209 
210 	/* Now we need to find where we have to add the entry */
211 	iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
212 	iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
213 
214 	if (shl) {
215 		sh->sh_size = shl->shl_path + shl->shl_res +
216 		    shl->shl_fstype + shl->shl_opts + shl->shl_descr;
217 	} else {
218 		sh->sh_size = strlen(sh->sh_path) +
219 		    strlen(sh->sh_res) + strlen(sh->sh_fstype) +
220 		    strlen(sh->sh_opts) + strlen(sh->sh_descr);
221 	}
222 
223 	/* We need to account for field separators and the EOL */
224 	sh->sh_size += 5;
225 
226 	/* Now walk down the hash table and add the new entry */
227 	for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
228 	    s != NULL; s = s->sh_next) {
229 		/*
230 		 * We need exact matches.
231 		 *
232 		 * We found a matching path. Either we have a
233 		 * duplicate path in a share command or we are
234 		 * being asked to replace an existing entry.
235 		 */
236 		if (strcmp(sh->sh_path, s->sh_path) == 0 &&
237 		    strlen(s->sh_path) == iPath) {
238 			if (p != NULL)
239 				p->sh_next = sh;
240 			else
241 				sht->s_buckets[iHash].ssh_sh = sh;
242 
243 			sh->sh_next = s->sh_next;
244 
245 			ASSERT(sg->sharetab_size >= s->sh_size);
246 			sg->sharetab_size -= s->sh_size;
247 			sg->sharetab_size += sh->sh_size;
248 
249 			/* Get rid of the old node */
250 			sharefree(s, NULL);
251 
252 			gethrestime(&sg->sharetab_mtime);
253 			atomic_inc_32(&sg->sharetab_generation);
254 
255 			ASSERT(sht->s_buckets[iHash].ssh_count != 0);
256 			rw_exit(&sg->sharetab_lock);
257 
258 			return (0);
259 		}
260 
261 		p = s;
262 	}
263 
264 	/*
265 	 * Okay, we have gone through the entire hash chain and not
266 	 * found a match. We just need to add this node.
267 	 */
268 	sh->sh_next = sht->s_buckets[iHash].ssh_sh;
269 	sht->s_buckets[iHash].ssh_sh = sh;
270 	atomic_inc_32(&sht->s_buckets[iHash].ssh_count);
271 	atomic_inc_32(&sht->s_count);
272 	atomic_inc_32(&sg->sharetab_count);
273 	sg->sharetab_size += sh->sh_size;
274 
275 	gethrestime(&sg->sharetab_mtime);
276 	atomic_inc_32(&sg->sharetab_generation);
277 
278 	rw_exit(&sg->sharetab_lock);
279 
280 	return (0);
281 }
282 
283 /* ARGSUSED */
284 static void *
285 sharetab_zone_init(zoneid_t zoneid)
286 {
287 	sharetab_globals_t *sg;
288 
289 	sg = kmem_zalloc(sizeof (*sg), KM_SLEEP);
290 
291 	rw_init(&sg->sharetab_lock, NULL, RW_DEFAULT, NULL);
292 	rw_init(&sg->sharefs_lock, NULL, RW_DEFAULT, NULL);
293 
294 	sg->sharetab_size = 0;
295 	sg->sharetab_count = 0;
296 	sg->sharetab_generation = 1;
297 
298 	gethrestime(&sg->sharetab_mtime);
299 	gethrestime(&sg->sharetab_snap_time);
300 
301 	return (sg);
302 }
303 
304 /* ARGSUSED */
305 static void
306 sharetab_zone_fini(zoneid_t zoneid, void *data)
307 {
308 	sharetab_globals_t *sg = data;
309 
310 	rw_destroy(&sg->sharefs_lock);
311 	rw_destroy(&sg->sharetab_lock);
312 
313 	/* ALL of the allocated things must be cleaned before we free sg. */
314 	while (sg->sharefs_sharetab != NULL) {
315 		int i;
316 		sharetab_t *freeing = sg->sharefs_sharetab;
317 
318 		sg->sharefs_sharetab = freeing->s_next;
319 		kmem_free(freeing->s_fstype, strlen(freeing->s_fstype) + 1);
320 		for (i = 0; i < PKP_HASH_SIZE; i++) {
321 			sharefs_hash_head_t *bucket;
322 
323 			bucket = &(freeing->s_buckets[i]);
324 			while (bucket->ssh_sh != NULL) {
325 				share_t *share = bucket->ssh_sh;
326 
327 				bucket->ssh_sh = share->sh_next;
328 				sharefree(share, NULL);
329 			}
330 		}
331 		kmem_free(freeing, sizeof (*freeing));
332 	}
333 
334 	kmem_free(sg, sizeof (*sg));
335 }
336 
337 void
338 sharefs_sharetab_init(void)
339 {
340 	zone_key_create(&sharetab_zone_key, sharetab_zone_init,
341 	    NULL, sharetab_zone_fini);
342 }
343 
344 sharetab_globals_t *
345 sharetab_get_globals(zone_t *zone)
346 {
347 	return (zone_getspecific(sharetab_zone_key, zone));
348 }
349 
350 int
351 sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
352 {
353 	int		error = 0;
354 	size_t		len;
355 	size_t		bufsz;
356 	share_t		*sh;
357 	sharefs_lens_t	shl;
358 	model_t		model;
359 	char		*buf = NULL;
360 	sharetab_globals_t *sg = sharetab_get_globals(curzone);
361 
362 	STRUCT_DECL(share, u_sh);
363 
364 	bufsz = iMaxLen;
365 
366 	/*
367 	 * Before we do anything, lets make sure we have
368 	 * a sharetab in memory if we need one.
369 	 */
370 	rw_enter(&sg->sharetab_lock, RW_READER);
371 	switch (opcode) {
372 	case SHAREFS_REMOVE:
373 	case SHAREFS_REPLACE:
374 		if (!sg->sharefs_sharetab) {
375 			rw_exit(&sg->sharetab_lock);
376 			return (set_errno(ENOENT));
377 		}
378 		break;
379 	case SHAREFS_ADD:
380 	default:
381 		break;
382 	}
383 	rw_exit(&sg->sharetab_lock);
384 
385 	model = get_udatamodel();
386 
387 	/*
388 	 * Initialize the data pointers.
389 	 */
390 	STRUCT_INIT(u_sh, model);
391 	if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh)))
392 		return (set_errno(EFAULT));
393 
394 	/* Get the share */
395 	sh = kmem_zalloc(sizeof (share_t), KM_SLEEP);
396 
397 	/* Get some storage for copying in the strings */
398 	buf = kmem_zalloc(bufsz + 1, KM_SLEEP);
399 	bzero(&shl, sizeof (sharefs_lens_t));
400 
401 	/* Only grab these two until we know what we want */
402 	SHARETAB_COPYIN(path);
403 	SHARETAB_COPYIN(fstype);
404 
405 	switch (opcode) {
406 	case SHAREFS_ADD:
407 	case SHAREFS_REPLACE:
408 		SHARETAB_COPYIN(res);
409 		SHARETAB_COPYIN(opts);
410 		SHARETAB_COPYIN(descr);
411 		error = sharefs_add(sg, sh, &shl);
412 		break;
413 	case SHAREFS_REMOVE:
414 		error = sharefs_remove(sg, sh, &shl);
415 		break;
416 	default:
417 		error = EINVAL;
418 		break;
419 	}
420 
421 cleanup:
422 	/*
423 	 * If there is no error, then we have stashed the structure
424 	 * away in the sharetab hash table or have deleted it.
425 	 *
426 	 * Either way, the only reason to blow away the data is if
427 	 * there was an error.
428 	 */
429 	if (error != 0)
430 		sharefree(sh, &shl);
431 
432 	if (buf != NULL)
433 		kmem_free(buf, bufsz + 1);
434 
435 	return (error != 0 ? set_errno(error) : 0);
436 }
437 
438 int
439 sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
440 {
441 	/*
442 	 * If we're in the global zone PRIV_SYS_CONFIG gives us the
443 	 * privileges needed to act on sharetab. However if we're in
444 	 * a non-global zone PRIV_SYS_CONFIG is not allowed. To work
445 	 * around this issue PRIV_SYS_NFS is used in this case.
446 	 *
447 	 * TODO: This basically overloads the definition/use of
448 	 * PRIV_SYS_NFS to work around the limitation of PRIV_SYS_CONFIG
449 	 * in a zone. Solaris 11 solved this by implementing a PRIV_SYS_SHARE
450 	 * we should do the same and replace the use of PRIV_SYS_NFS here and
451 	 * in zfs_secpolicy_share.
452 	 */
453 	if (INGLOBALZONE(curproc)) {
454 		if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
455 			return (set_errno(EPERM));
456 	} else {
457 		/* behave like zfs_secpolicy_share() */
458 		if (secpolicy_nfs(CRED()) != 0)
459 			return (set_errno(EPERM));
460 
461 	}
462 	return (sharefs_impl(opcode, sh_in, iMaxLen));
463 }
464