xref: /illumos-gate/usr/src/uts/common/fs/sharefs/sharetab.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/types32.h>
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <rpc/types.h>
34 #include <sys/vfs.h>
35 #include <sys/siginfo.h>
36 #include <sys/proc.h>		/* for exit() declaration */
37 #include <sys/kmem.h>
38 #include <sys/pathname.h>
39 #include <sys/debug.h>
40 #include <sys/vtrace.h>
41 #include <sys/cmn_err.h>
42 #include <sys/atomic.h>
43 #include <sys/policy.h>
44 
45 #include <sharefs/sharefs.h>
46 
47 /*
48  * A macro to avoid cut-and-paste errors on getting a string field
49  * from user-land.
50  */
51 #define	SHARETAB_COPYIN(field)						\
52 	if (copyinstr(STRUCT_FGETP(u_sh, sh_##field),			\
53 			buf,						\
54 			bufsz + 1,	/* Add one for extra NUL */	\
55 			&len)) {					\
56 		error = EFAULT;						\
57 		goto cleanup;						\
58 	}								\
59 	/*								\
60 	 * Need to remove 1 because copyinstr() counts the NUL.		\
61 	 */								\
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) {						\
71 		kmem_free(sh->sh_##field,				\
72 			shl ? shl->shl_##field + 1 :			\
73 			strlen(sh->sh_##field) + 1);			\
74 	}
75 
76 sharetab_t	*sharefs_sharetab = NULL;	/* The incore sharetab. */
77 size_t		sharetab_size;
78 uint_t		sharetab_count;
79 
80 krwlock_t	sharetab_lock;	/* lock to protect the cached sharetab */
81 
82 krwlock_t	sharefs_lock;	/* lock to protect the vnode ops */
83 
84 timestruc_t	sharetab_mtime;
85 timestruc_t	sharetab_snap_time;
86 
87 uint_t		sharetab_generation;	/* Only increments and wraps! */
88 
89 static uint_t	pkp_tab[SHARETAB_HASHES];
90 
91 /*
92  * Initialize table in pseudo-random fashion
93  * for use in Pearson's string hash algorithm.
94  *
95  * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
96  * http://www.acm.org/pubs/citations/journals/cacm/1990-33-6/p677-pearson
97  */
98 static void
99 init_pkp_tab(void)
100 {
101 	int	i;
102 	int	j;
103 	int	k = 7;
104 	uint_t	s;
105 
106 	for (i = 0; i < SHARETAB_HASHES; i++)
107 		pkp_tab[i] = i;
108 
109 	for (j = 0; j < 4; j++) {
110 		for (i = 0; i < SHARETAB_HASHES; i++) {
111 			s = pkp_tab[i];
112 			k = MOD2((k + s), SHARETAB_HASHES);
113 			pkp_tab[i] = pkp_tab[k];
114 			pkp_tab[k] = s;
115 		}
116 	}
117 }
118 
119 /*
120  * Take care of cleaning up a share.
121  * If passed in a length array, use it to determine how much
122  * space to clean up. Else, figure that out.
123  */
124 static void
125 sharefree(share_t *sh, sharefs_lens_t *shl)
126 {
127 	if (!sh)
128 		return;
129 
130 	SHARETAB_DELETE_FIELD(path);
131 	SHARETAB_DELETE_FIELD(res);
132 	SHARETAB_DELETE_FIELD(fstype);
133 	SHARETAB_DELETE_FIELD(opts);
134 	SHARETAB_DELETE_FIELD(descr);
135 
136 	kmem_free(sh, sizeof (share_t));
137 }
138 
139 /*
140  * If there is no error, then this function is responsible for
141  * cleaning up the memory associated with the share argument.
142  */
143 static int
144 sharefs_remove(share_t *sh, sharefs_lens_t *shl)
145 {
146 	int		iHash;
147 	sharetab_t	*sht;
148 	share_t		*s, *p;
149 	int		iPath;
150 
151 	if (!sh)
152 		return (ENOENT);
153 
154 	rw_enter(&sharetab_lock, RW_WRITER);
155 	for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) {
156 		if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) {
157 			break;
158 		}
159 	}
160 
161 	/*
162 	 * There does not exist a fstype in memory which
163 	 * matches the share passed in.
164 	 */
165 	if (!sht) {
166 		rw_exit(&sharetab_lock);
167 		return (ENOENT);
168 	}
169 
170 	iPath = shl ? shl->shl_path : strlen(sh->sh_path);
171 	SHARETAB_HASH_IT(iHash, sh->sh_path);
172 
173 	/*
174 	 * Now walk down the hash table and find the entry to free!
175 	 */
176 	for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
177 	    s != NULL; s = s->sh_next) {
178 		/*
179 		 * We need exact matches.
180 		 */
181 		if (strcmp(sh->sh_path, s->sh_path) == 0 &&
182 		    strlen(s->sh_path) == iPath) {
183 			if (p) {
184 				p->sh_next = s->sh_next;
185 			} else {
186 				sht->s_buckets[iHash].ssh_sh = s->sh_next;
187 			}
188 
189 			ASSERT(sht->s_buckets[iHash].ssh_count != 0);
190 			atomic_add_32(&sht->s_buckets[iHash].ssh_count, -1);
191 			atomic_add_32(&sht->s_count, -1);
192 			atomic_add_32(&sharetab_count, -1);
193 
194 			ASSERT(sharetab_size >= s->sh_size);
195 			sharetab_size -= s->sh_size;
196 
197 			gethrestime(&sharetab_mtime);
198 			atomic_add_32(&sharetab_generation, 1);
199 
200 			break;
201 		}
202 
203 		p = s;
204 	}
205 
206 	rw_exit(&sharetab_lock);
207 
208 	if (!s) {
209 		return (ENOENT);
210 	}
211 
212 	s->sh_next = NULL;
213 	sharefree(s, NULL);
214 
215 	/*
216 	 * We need to free the share for the caller.
217 	 */
218 	sharefree(sh, shl);
219 
220 	return (0);
221 }
222 
223 /*
224  * The caller must have allocated memory for us to use.
225  */
226 static int
227 sharefs_add(share_t *sh, sharefs_lens_t *shl)
228 {
229 	int		iHash;
230 	sharetab_t	*sht;
231 	share_t		*s, *p;
232 	int		iPath;
233 	int		n;
234 
235 	if (!sh) {
236 		return (ENOENT);
237 	}
238 
239 	/*
240 	 * We need to find the hash buckets for the fstype.
241 	 */
242 	rw_enter(&sharetab_lock, RW_WRITER);
243 	for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) {
244 		if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) {
245 			break;
246 		}
247 	}
248 
249 	/*
250 	 * Did not exist, so allocate one and add it to the
251 	 * sharetab.
252 	 */
253 	if (!sht) {
254 		sht = kmem_zalloc(sizeof (*sht), KM_SLEEP);
255 		n = strlen(sh->sh_fstype);
256 		sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP);
257 		(void) strncpy(sht->s_fstype, sh->sh_fstype, n);
258 
259 		sht->s_next = sharefs_sharetab;
260 		sharefs_sharetab = sht;
261 	}
262 
263 	/*
264 	 * Now we need to find where we have to add the entry.
265 	 */
266 	SHARETAB_HASH_IT(iHash, sh->sh_path);
267 
268 	iPath = shl ? shl->shl_path : strlen(sh->sh_path);
269 
270 	if (shl) {
271 		sh->sh_size = shl->shl_path + shl->shl_res +
272 		    shl->shl_fstype + shl->shl_opts + shl->shl_descr;
273 	} else {
274 		sh->sh_size = strlen(sh->sh_path) +
275 		    strlen(sh->sh_res) + strlen(sh->sh_fstype) +
276 		    strlen(sh->sh_opts) + strlen(sh->sh_descr);
277 	}
278 
279 	/*
280 	 * We need to account for field seperators and
281 	 * the EOL.
282 	 */
283 	sh->sh_size += 5;
284 
285 	/*
286 	 * Now walk down the hash table and add the new entry!
287 	 */
288 	for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
289 	    s != NULL; s = s->sh_next) {
290 		/*
291 		 * We need exact matches.
292 		 *
293 		 * We found a matching path. Either we have a
294 		 * duplicate path in a share command or we are
295 		 * being asked to replace an existing entry.
296 		 */
297 		if (strcmp(sh->sh_path, s->sh_path) == 0 &&
298 		    strlen(s->sh_path) == iPath) {
299 			if (p) {
300 				p->sh_next = sh;
301 			} else {
302 				sht->s_buckets[iHash].ssh_sh = sh;
303 			}
304 
305 			sh->sh_next = s->sh_next;
306 
307 			ASSERT(sharetab_size >= s->sh_size);
308 			sharetab_size -= s->sh_size;
309 			sharetab_size += sh->sh_size;
310 
311 			/*
312 			 * Get rid of the old node.
313 			 */
314 			sharefree(s, NULL);
315 
316 			gethrestime(&sharetab_mtime);
317 			atomic_add_32(&sharetab_generation, 1);
318 
319 			ASSERT(sht->s_buckets[iHash].ssh_count != 0);
320 			rw_exit(&sharetab_lock);
321 
322 			return (0);
323 		}
324 
325 		p = s;
326 	}
327 
328 	/*
329 	 * Okay, we have gone through the entire hash chain and not
330 	 * found a match. We just need to add this node.
331 	 */
332 	sh->sh_next = sht->s_buckets[iHash].ssh_sh;
333 	sht->s_buckets[iHash].ssh_sh = sh;
334 	atomic_add_32(&sht->s_buckets[iHash].ssh_count, 1);
335 	atomic_add_32(&sht->s_count, 1);
336 	atomic_add_32(&sharetab_count, 1);
337 	sharetab_size += sh->sh_size;
338 
339 	gethrestime(&sharetab_mtime);
340 	atomic_add_32(&sharetab_generation, 1);
341 
342 	rw_exit(&sharetab_lock);
343 
344 	return (0);
345 }
346 
347 void
348 sharefs_sharetab_init(void)
349 {
350 	init_pkp_tab();
351 
352 	rw_init(&sharetab_lock, NULL, RW_DEFAULT, NULL);
353 	rw_init(&sharefs_lock, NULL, RW_DEFAULT, NULL);
354 
355 	sharetab_size = 0;
356 	sharetab_count = 0;
357 	sharetab_generation = 1;
358 
359 	gethrestime(&sharetab_mtime);
360 	gethrestime(&sharetab_snap_time);
361 }
362 
363 int
364 sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
365 {
366 	int		error = 0;
367 	size_t		len;
368 	size_t		bufsz;
369 	share_t		*sh;
370 
371 	sharefs_lens_t	shl;
372 
373 	model_t		model;
374 
375 	char		*buf = NULL;
376 
377 	STRUCT_DECL(share, u_sh);
378 
379 	bufsz = iMaxLen;
380 
381 	/*
382 	 * Before we do anything, lets make sure we have
383 	 * a sharetab in memory if we need one.
384 	 */
385 	rw_enter(&sharetab_lock, RW_READER);
386 	switch (opcode) {
387 	case (SHAREFS_REMOVE) :
388 	case (SHAREFS_REPLACE) :
389 		if (!sharefs_sharetab) {
390 			rw_exit(&sharetab_lock);
391 			return (set_errno(ENOENT));
392 		}
393 		break;
394 	case (SHAREFS_ADD) :
395 	default :
396 		break;
397 	}
398 	rw_exit(&sharetab_lock);
399 
400 	model = get_udatamodel();
401 
402 	/*
403 	 * Initialize the data pointers.
404 	 */
405 	STRUCT_INIT(u_sh, model);
406 	if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh))) {
407 		return (set_errno(EFAULT));
408 	}
409 
410 	/*
411 	 * Get the share.
412 	 */
413 	sh = kmem_zalloc(sizeof (share_t), KM_SLEEP);
414 
415 	/*
416 	 * Get some storage for copying in the strings.
417 	 */
418 	buf = kmem_zalloc(bufsz + 1, KM_SLEEP);
419 	bzero(&shl, sizeof (sharefs_lens_t));
420 
421 	/*
422 	 * Only grab these two until we know what we want.
423 	 */
424 	SHARETAB_COPYIN(path);
425 	SHARETAB_COPYIN(fstype);
426 
427 	switch (opcode) {
428 	case (SHAREFS_ADD) :
429 	case (SHAREFS_REPLACE) :
430 		SHARETAB_COPYIN(res);
431 		SHARETAB_COPYIN(opts);
432 		SHARETAB_COPYIN(descr);
433 
434 		error = sharefs_add(sh, &shl);
435 		break;
436 
437 	case (SHAREFS_REMOVE) :
438 
439 		error = sharefs_remove(sh, &shl);
440 		break;
441 
442 	default:
443 		error = EINVAL;
444 		break;
445 	}
446 
447 cleanup:
448 
449 	/*
450 	 * If there is no error, then we have stashed the structure
451 	 * away in the sharetab hash table or have deleted it.
452 	 *
453 	 * Either way, the only reason to blow away the data is if
454 	 * there was an error.
455 	 */
456 	if (error != 0) {
457 		sharefree(sh, &shl);
458 	}
459 
460 	if (buf) {
461 		kmem_free(buf, bufsz + 1);
462 	}
463 
464 	return ((error != 0) ? set_errno(error) : 0);
465 }
466 
467 int
468 sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
469 {
470 	if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
471 		return (set_errno(EPERM));
472 
473 	return (sharefs_impl(opcode, sh_in, iMaxLen));
474 }
475