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