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
73 sharetab_t *sharefs_sharetab = NULL; /* The incore sharetab. */
74 size_t sharetab_size;
75 uint_t sharetab_count;
76
77 krwlock_t sharetab_lock; /* lock to protect the cached sharetab */
78
79 krwlock_t sharefs_lock; /* lock to protect the vnode ops */
80
81 timestruc_t sharetab_mtime;
82 timestruc_t sharetab_snap_time;
83
84 uint_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 */
91 static void
sharefree(share_t * sh,sharefs_lens_t * shl)92 sharefree(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 */
110 static int
sharefs_remove(share_t * sh,sharefs_lens_t * shl)111 sharefs_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_dec_32(&sht->s_buckets[iHash].ssh_count);
158 atomic_dec_32(&sht->s_count);
159 atomic_dec_32(&sharetab_count);
160
161 ASSERT(sharetab_size >= s->sh_size);
162 sharetab_size -= s->sh_size;
163
164 gethrestime(&sharetab_mtime);
165 atomic_inc_32(&sharetab_generation);
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 */
193 static int
sharefs_add(share_t * sh,sharefs_lens_t * shl)194 sharefs_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_inc_32(&sharetab_generation);
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_inc_32(&sht->s_buckets[iHash].ssh_count);
302 atomic_inc_32(&sht->s_count);
303 atomic_inc_32(&sharetab_count);
304 sharetab_size += sh->sh_size;
305
306 gethrestime(&sharetab_mtime);
307 atomic_inc_32(&sharetab_generation);
308
309 rw_exit(&sharetab_lock);
310
311 return (0);
312 }
313
314 void
sharefs_sharetab_init(void)315 sharefs_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
328 int
sharefs_impl(enum sharefs_sys_op opcode,share_t * sh_in,uint32_t iMaxLen)329 sharefs_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
412 cleanup:
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
432 int
sharefs(enum sharefs_sys_op opcode,share_t * sh_in,uint32_t iMaxLen)433 sharefs(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