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
sharefree(share_t * sh,sharefs_lens_t * shl)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
sharefs_remove(sharetab_globals_t * sg,share_t * sh,sharefs_lens_t * shl)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
sharefs_add(sharetab_globals_t * sg,share_t * sh,sharefs_lens_t * shl)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 *
sharetab_zone_init(zoneid_t zoneid)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
sharetab_zone_fini(zoneid_t zoneid,void * data)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
sharefs_sharetab_init(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 *
sharetab_get_globals(zone_t * zone)345 sharetab_get_globals(zone_t *zone)
346 {
347 return (zone_getspecific(sharetab_zone_key, zone));
348 }
349
350 int
sharefs_impl(enum sharefs_sys_op opcode,share_t * sh_in,uint32_t iMaxLen)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
sharefs(enum sharefs_sys_op opcode,share_t * sh_in,uint32_t iMaxLen)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