13957Sth199096 /*
23957Sth199096 * CDDL HEADER START
33957Sth199096 *
43957Sth199096 * The contents of this file are subject to the terms of the
53957Sth199096 * Common Development and Distribution License (the "License").
63957Sth199096 * You may not use this file except in compliance with the License.
73957Sth199096 *
83957Sth199096 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93957Sth199096 * or http://www.opensolaris.org/os/licensing.
103957Sth199096 * See the License for the specific language governing permissions
113957Sth199096 * and limitations under the License.
123957Sth199096 *
133957Sth199096 * When distributing Covered Code, include this CDDL HEADER in each
143957Sth199096 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153957Sth199096 * If applicable, add the following below this CDDL HEADER, with the
163957Sth199096 * fields enclosed by brackets "[]" replaced with your own identifying
173957Sth199096 * information: Portions Copyright [yyyy] [name of copyright owner]
183957Sth199096 *
193957Sth199096 * CDDL HEADER END
203957Sth199096 */
213957Sth199096
223957Sth199096 /*
23*12679SPavel.Filipensky@Sun.COM * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
243957Sth199096 */
253957Sth199096
263957Sth199096 #include <sys/types.h>
273957Sth199096 #include <sys/types32.h>
283957Sth199096 #include <sys/param.h>
293957Sth199096 #include <sys/systm.h>
303957Sth199096 #include <rpc/types.h>
313957Sth199096 #include <sys/vfs.h>
323957Sth199096 #include <sys/siginfo.h>
333957Sth199096 #include <sys/proc.h> /* for exit() declaration */
343957Sth199096 #include <sys/kmem.h>
353957Sth199096 #include <sys/pathname.h>
363957Sth199096 #include <sys/debug.h>
373957Sth199096 #include <sys/vtrace.h>
383957Sth199096 #include <sys/cmn_err.h>
393957Sth199096 #include <sys/atomic.h>
404543Smarks #include <sys/policy.h>
413957Sth199096
423957Sth199096 #include <sharefs/sharefs.h>
433957Sth199096
443957Sth199096 /*
453957Sth199096 * A macro to avoid cut-and-paste errors on getting a string field
463957Sth199096 * from user-land.
473957Sth199096 */
483957Sth199096 #define SHARETAB_COPYIN(field) \
493957Sth199096 if (copyinstr(STRUCT_FGETP(u_sh, sh_##field), \
503957Sth199096 buf, \
513957Sth199096 bufsz + 1, /* Add one for extra NUL */ \
523957Sth199096 &len)) { \
533957Sth199096 error = EFAULT; \
543957Sth199096 goto cleanup; \
553957Sth199096 } \
563957Sth199096 /* \
573957Sth199096 * Need to remove 1 because copyinstr() counts the NUL. \
583957Sth199096 */ \
593957Sth199096 len--; \
603957Sth199096 sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP); \
613957Sth199096 bcopy(buf, sh->sh_##field, len); \
623957Sth199096 sh->sh_##field[len] = '\0'; \
633957Sth199096 shl.shl_##field = (int)len; \
643957Sth199096 sh->sh_size += shl.shl_##field; /* Debug counting */
653957Sth199096
663957Sth199096 #define SHARETAB_DELETE_FIELD(field) \
673957Sth199096 if (sh->sh_##field) { \
683957Sth199096 kmem_free(sh->sh_##field, \
693957Sth199096 shl ? shl->shl_##field + 1 : \
703957Sth199096 strlen(sh->sh_##field) + 1); \
713957Sth199096 }
723957Sth199096
733957Sth199096 sharetab_t *sharefs_sharetab = NULL; /* The incore sharetab. */
743957Sth199096 size_t sharetab_size;
753957Sth199096 uint_t sharetab_count;
763957Sth199096
773957Sth199096 krwlock_t sharetab_lock; /* lock to protect the cached sharetab */
783957Sth199096
793957Sth199096 krwlock_t sharefs_lock; /* lock to protect the vnode ops */
803957Sth199096
813957Sth199096 timestruc_t sharetab_mtime;
823957Sth199096 timestruc_t sharetab_snap_time;
833957Sth199096
843957Sth199096 uint_t sharetab_generation; /* Only increments and wraps! */
853957Sth199096
863957Sth199096 /*
873957Sth199096 * Take care of cleaning up a share.
883957Sth199096 * If passed in a length array, use it to determine how much
893957Sth199096 * space to clean up. Else, figure that out.
903957Sth199096 */
913957Sth199096 static void
sharefree(share_t * sh,sharefs_lens_t * shl)923957Sth199096 sharefree(share_t *sh, sharefs_lens_t *shl)
933957Sth199096 {
943957Sth199096 if (!sh)
953957Sth199096 return;
963957Sth199096
973957Sth199096 SHARETAB_DELETE_FIELD(path);
983957Sth199096 SHARETAB_DELETE_FIELD(res);
993957Sth199096 SHARETAB_DELETE_FIELD(fstype);
1003957Sth199096 SHARETAB_DELETE_FIELD(opts);
1013957Sth199096 SHARETAB_DELETE_FIELD(descr);
1023957Sth199096
1033957Sth199096 kmem_free(sh, sizeof (share_t));
1043957Sth199096 }
1053957Sth199096
1063957Sth199096 /*
1073957Sth199096 * If there is no error, then this function is responsible for
1083957Sth199096 * cleaning up the memory associated with the share argument.
1093957Sth199096 */
1103957Sth199096 static int
sharefs_remove(share_t * sh,sharefs_lens_t * shl)1113957Sth199096 sharefs_remove(share_t *sh, sharefs_lens_t *shl)
1123957Sth199096 {
1133957Sth199096 int iHash;
1143957Sth199096 sharetab_t *sht;
1153957Sth199096 share_t *s, *p;
1163957Sth199096 int iPath;
1173957Sth199096
1183957Sth199096 if (!sh)
1193957Sth199096 return (ENOENT);
1203957Sth199096
1213957Sth199096 rw_enter(&sharetab_lock, RW_WRITER);
1223957Sth199096 for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) {
1233957Sth199096 if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) {
1243957Sth199096 break;
1253957Sth199096 }
1263957Sth199096 }
1273957Sth199096
1283957Sth199096 /*
1293957Sth199096 * There does not exist a fstype in memory which
1303957Sth199096 * matches the share passed in.
1313957Sth199096 */
1323957Sth199096 if (!sht) {
1333957Sth199096 rw_exit(&sharetab_lock);
1343957Sth199096 return (ENOENT);
1353957Sth199096 }
1363957Sth199096
1373957Sth199096 iPath = shl ? shl->shl_path : strlen(sh->sh_path);
138*12679SPavel.Filipensky@Sun.COM iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
1393957Sth199096
1403957Sth199096 /*
1413957Sth199096 * Now walk down the hash table and find the entry to free!
1423957Sth199096 */
1433957Sth199096 for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
1444543Smarks s != NULL; s = s->sh_next) {
1453957Sth199096 /*
1463957Sth199096 * We need exact matches.
1473957Sth199096 */
1483957Sth199096 if (strcmp(sh->sh_path, s->sh_path) == 0 &&
1494543Smarks strlen(s->sh_path) == iPath) {
1503957Sth199096 if (p) {
1513957Sth199096 p->sh_next = s->sh_next;
1523957Sth199096 } else {
1533957Sth199096 sht->s_buckets[iHash].ssh_sh = s->sh_next;
1543957Sth199096 }
1553957Sth199096
1563957Sth199096 ASSERT(sht->s_buckets[iHash].ssh_count != 0);
1573957Sth199096 atomic_add_32(&sht->s_buckets[iHash].ssh_count, -1);
1583957Sth199096 atomic_add_32(&sht->s_count, -1);
1593957Sth199096 atomic_add_32(&sharetab_count, -1);
1603957Sth199096
1613957Sth199096 ASSERT(sharetab_size >= s->sh_size);
1623957Sth199096 sharetab_size -= s->sh_size;
1633957Sth199096
1643957Sth199096 gethrestime(&sharetab_mtime);
1653957Sth199096 atomic_add_32(&sharetab_generation, 1);
1663957Sth199096
1673957Sth199096 break;
1683957Sth199096 }
1693957Sth199096
1703957Sth199096 p = s;
1713957Sth199096 }
1723957Sth199096
1733957Sth199096 rw_exit(&sharetab_lock);
1743957Sth199096
1753957Sth199096 if (!s) {
1763957Sth199096 return (ENOENT);
1773957Sth199096 }
1783957Sth199096
1793957Sth199096 s->sh_next = NULL;
1803957Sth199096 sharefree(s, NULL);
1813957Sth199096
1823957Sth199096 /*
1833957Sth199096 * We need to free the share for the caller.
1843957Sth199096 */
1853957Sth199096 sharefree(sh, shl);
1863957Sth199096
1873957Sth199096 return (0);
1883957Sth199096 }
1893957Sth199096
1903957Sth199096 /*
1913957Sth199096 * The caller must have allocated memory for us to use.
1923957Sth199096 */
1933957Sth199096 static int
sharefs_add(share_t * sh,sharefs_lens_t * shl)1943957Sth199096 sharefs_add(share_t *sh, sharefs_lens_t *shl)
1953957Sth199096 {
1963957Sth199096 int iHash;
1973957Sth199096 sharetab_t *sht;
1983957Sth199096 share_t *s, *p;
1993957Sth199096 int iPath;
2003957Sth199096 int n;
2013957Sth199096
2023957Sth199096 if (!sh) {
2033957Sth199096 return (ENOENT);
2043957Sth199096 }
2053957Sth199096
2063957Sth199096 /*
2073957Sth199096 * We need to find the hash buckets for the fstype.
2083957Sth199096 */
2093957Sth199096 rw_enter(&sharetab_lock, RW_WRITER);
2103957Sth199096 for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) {
2113957Sth199096 if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) {
2123957Sth199096 break;
2133957Sth199096 }
2143957Sth199096 }
2153957Sth199096
2163957Sth199096 /*
2173957Sth199096 * Did not exist, so allocate one and add it to the
2183957Sth199096 * sharetab.
2193957Sth199096 */
2203957Sth199096 if (!sht) {
2213957Sth199096 sht = kmem_zalloc(sizeof (*sht), KM_SLEEP);
2223957Sth199096 n = strlen(sh->sh_fstype);
2233957Sth199096 sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP);
2243957Sth199096 (void) strncpy(sht->s_fstype, sh->sh_fstype, n);
2253957Sth199096
2263957Sth199096 sht->s_next = sharefs_sharetab;
2273957Sth199096 sharefs_sharetab = sht;
2283957Sth199096 }
2293957Sth199096
2303957Sth199096 /*
2313957Sth199096 * Now we need to find where we have to add the entry.
2323957Sth199096 */
233*12679SPavel.Filipensky@Sun.COM iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
2343957Sth199096
2353957Sth199096 iPath = shl ? shl->shl_path : strlen(sh->sh_path);
2363957Sth199096
2373957Sth199096 if (shl) {
2383957Sth199096 sh->sh_size = shl->shl_path + shl->shl_res +
2394543Smarks shl->shl_fstype + shl->shl_opts + shl->shl_descr;
2403957Sth199096 } else {
2413957Sth199096 sh->sh_size = strlen(sh->sh_path) +
2424543Smarks strlen(sh->sh_res) + strlen(sh->sh_fstype) +
2434543Smarks strlen(sh->sh_opts) + strlen(sh->sh_descr);
2443957Sth199096 }
2453957Sth199096
2463957Sth199096 /*
2473957Sth199096 * We need to account for field seperators and
2483957Sth199096 * the EOL.
2493957Sth199096 */
2503957Sth199096 sh->sh_size += 5;
2513957Sth199096
2523957Sth199096 /*
2533957Sth199096 * Now walk down the hash table and add the new entry!
2543957Sth199096 */
2553957Sth199096 for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
2564543Smarks s != NULL; s = s->sh_next) {
2573957Sth199096 /*
2583957Sth199096 * We need exact matches.
2593957Sth199096 *
2603957Sth199096 * We found a matching path. Either we have a
2613957Sth199096 * duplicate path in a share command or we are
2623957Sth199096 * being asked to replace an existing entry.
2633957Sth199096 */
2643957Sth199096 if (strcmp(sh->sh_path, s->sh_path) == 0 &&
2654543Smarks strlen(s->sh_path) == iPath) {
2663957Sth199096 if (p) {
2673957Sth199096 p->sh_next = sh;
2683957Sth199096 } else {
2693957Sth199096 sht->s_buckets[iHash].ssh_sh = sh;
2703957Sth199096 }
2713957Sth199096
2723957Sth199096 sh->sh_next = s->sh_next;
2733957Sth199096
2743957Sth199096 ASSERT(sharetab_size >= s->sh_size);
2753957Sth199096 sharetab_size -= s->sh_size;
2763957Sth199096 sharetab_size += sh->sh_size;
2773957Sth199096
2783957Sth199096 /*
2793957Sth199096 * Get rid of the old node.
2803957Sth199096 */
2813957Sth199096 sharefree(s, NULL);
2823957Sth199096
2833957Sth199096 gethrestime(&sharetab_mtime);
2843957Sth199096 atomic_add_32(&sharetab_generation, 1);
2853957Sth199096
2863957Sth199096 ASSERT(sht->s_buckets[iHash].ssh_count != 0);
2873957Sth199096 rw_exit(&sharetab_lock);
2883957Sth199096
2893957Sth199096 return (0);
2903957Sth199096 }
2913957Sth199096
2923957Sth199096 p = s;
2933957Sth199096 }
2943957Sth199096
2953957Sth199096 /*
2963957Sth199096 * Okay, we have gone through the entire hash chain and not
2973957Sth199096 * found a match. We just need to add this node.
2983957Sth199096 */
2993957Sth199096 sh->sh_next = sht->s_buckets[iHash].ssh_sh;
3003957Sth199096 sht->s_buckets[iHash].ssh_sh = sh;
3013957Sth199096 atomic_add_32(&sht->s_buckets[iHash].ssh_count, 1);
3023957Sth199096 atomic_add_32(&sht->s_count, 1);
3033957Sth199096 atomic_add_32(&sharetab_count, 1);
3043957Sth199096 sharetab_size += sh->sh_size;
3053957Sth199096
3063957Sth199096 gethrestime(&sharetab_mtime);
3073957Sth199096 atomic_add_32(&sharetab_generation, 1);
3083957Sth199096
3093957Sth199096 rw_exit(&sharetab_lock);
3103957Sth199096
3113957Sth199096 return (0);
3123957Sth199096 }
3133957Sth199096
3143957Sth199096 void
sharefs_sharetab_init(void)3153957Sth199096 sharefs_sharetab_init(void)
3163957Sth199096 {
3173957Sth199096 rw_init(&sharetab_lock, NULL, RW_DEFAULT, NULL);
3183957Sth199096 rw_init(&sharefs_lock, NULL, RW_DEFAULT, NULL);
3193957Sth199096
3203957Sth199096 sharetab_size = 0;
3213957Sth199096 sharetab_count = 0;
3223957Sth199096 sharetab_generation = 1;
3233957Sth199096
3243957Sth199096 gethrestime(&sharetab_mtime);
3253957Sth199096 gethrestime(&sharetab_snap_time);
3263957Sth199096 }
3273957Sth199096
3283957Sth199096 int
sharefs_impl(enum sharefs_sys_op opcode,share_t * sh_in,uint32_t iMaxLen)3294543Smarks sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
3303957Sth199096 {
3313957Sth199096 int error = 0;
3323957Sth199096 size_t len;
3333957Sth199096 size_t bufsz;
3343957Sth199096 share_t *sh;
3353957Sth199096
3363957Sth199096 sharefs_lens_t shl;
3373957Sth199096
3383957Sth199096 model_t model;
3393957Sth199096
3403957Sth199096 char *buf = NULL;
3413957Sth199096
3423957Sth199096 STRUCT_DECL(share, u_sh);
3433957Sth199096
3443957Sth199096 bufsz = iMaxLen;
3453957Sth199096
3463957Sth199096 /*
3473957Sth199096 * Before we do anything, lets make sure we have
3483957Sth199096 * a sharetab in memory if we need one.
3493957Sth199096 */
3503957Sth199096 rw_enter(&sharetab_lock, RW_READER);
3513957Sth199096 switch (opcode) {
3523957Sth199096 case (SHAREFS_REMOVE) :
3533957Sth199096 case (SHAREFS_REPLACE) :
3543957Sth199096 if (!sharefs_sharetab) {
3553957Sth199096 rw_exit(&sharetab_lock);
3563957Sth199096 return (set_errno(ENOENT));
3573957Sth199096 }
3583957Sth199096 break;
3593957Sth199096 case (SHAREFS_ADD) :
3603957Sth199096 default :
3613957Sth199096 break;
3623957Sth199096 }
3633957Sth199096 rw_exit(&sharetab_lock);
3643957Sth199096
3653957Sth199096 model = get_udatamodel();
3663957Sth199096
3673957Sth199096 /*
3683957Sth199096 * Initialize the data pointers.
3693957Sth199096 */
3703957Sth199096 STRUCT_INIT(u_sh, model);
3714543Smarks if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh))) {
3723957Sth199096 return (set_errno(EFAULT));
3733957Sth199096 }
3743957Sth199096
3753957Sth199096 /*
3763957Sth199096 * Get the share.
3773957Sth199096 */
3783957Sth199096 sh = kmem_zalloc(sizeof (share_t), KM_SLEEP);
3793957Sth199096
3803957Sth199096 /*
3813957Sth199096 * Get some storage for copying in the strings.
3823957Sth199096 */
3833957Sth199096 buf = kmem_zalloc(bufsz + 1, KM_SLEEP);
3843957Sth199096 bzero(&shl, sizeof (sharefs_lens_t));
3853957Sth199096
3863957Sth199096 /*
3873957Sth199096 * Only grab these two until we know what we want.
3883957Sth199096 */
3893957Sth199096 SHARETAB_COPYIN(path);
3903957Sth199096 SHARETAB_COPYIN(fstype);
3913957Sth199096
3923957Sth199096 switch (opcode) {
3933957Sth199096 case (SHAREFS_ADD) :
3943957Sth199096 case (SHAREFS_REPLACE) :
3953957Sth199096 SHARETAB_COPYIN(res);
3963957Sth199096 SHARETAB_COPYIN(opts);
3973957Sth199096 SHARETAB_COPYIN(descr);
3983957Sth199096
3993957Sth199096 error = sharefs_add(sh, &shl);
4003957Sth199096 break;
4013957Sth199096
4023957Sth199096 case (SHAREFS_REMOVE) :
4033957Sth199096
4043957Sth199096 error = sharefs_remove(sh, &shl);
4053957Sth199096 break;
4063957Sth199096
4073957Sth199096 default:
4083957Sth199096 error = EINVAL;
4093957Sth199096 break;
4103957Sth199096 }
4113957Sth199096
4123957Sth199096 cleanup:
4133957Sth199096
4143957Sth199096 /*
4153957Sth199096 * If there is no error, then we have stashed the structure
4163957Sth199096 * away in the sharetab hash table or have deleted it.
4173957Sth199096 *
4183957Sth199096 * Either way, the only reason to blow away the data is if
4193957Sth199096 * there was an error.
4203957Sth199096 */
4213957Sth199096 if (error != 0) {
4223957Sth199096 sharefree(sh, &shl);
4233957Sth199096 }
4243957Sth199096
4253957Sth199096 if (buf) {
4263957Sth199096 kmem_free(buf, bufsz + 1);
4273957Sth199096 }
4283957Sth199096
4293957Sth199096 return ((error != 0) ? set_errno(error) : 0);
4303957Sth199096 }
4314543Smarks
4324543Smarks int
sharefs(enum sharefs_sys_op opcode,share_t * sh_in,uint32_t iMaxLen)4334543Smarks sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
4344543Smarks {
4354543Smarks if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
4364543Smarks return (set_errno(EPERM));
4374543Smarks
4384543Smarks return (sharefs_impl(opcode, sh_in, iMaxLen));
4394543Smarks }
440