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