/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include /* for exit() declaration */ #include #include #include #include #include #include #include #include /* * A macro to avoid cut-and-paste errors on getting a string field * from user-land. */ #define SHARETAB_COPYIN(field) \ if (copyinstr(STRUCT_FGETP(u_sh, sh_##field), \ buf, \ bufsz + 1, /* Add one for extra NUL */ \ &len)) { \ error = EFAULT; \ goto cleanup; \ } \ /* \ * Need to remove 1 because copyinstr() counts the NUL. \ */ \ len--; \ sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP); \ bcopy(buf, sh->sh_##field, len); \ sh->sh_##field[len] = '\0'; \ shl.shl_##field = (int)len; \ sh->sh_size += shl.shl_##field; /* Debug counting */ #define SHARETAB_DELETE_FIELD(field) \ if (sh->sh_##field) { \ kmem_free(sh->sh_##field, \ shl ? shl->shl_##field + 1 : \ strlen(sh->sh_##field) + 1); \ } sharetab_t *sharefs_sharetab = NULL; /* The incore sharetab. */ size_t sharetab_size; uint_t sharetab_count; krwlock_t sharetab_lock; /* lock to protect the cached sharetab */ krwlock_t sharefs_lock; /* lock to protect the vnode ops */ timestruc_t sharetab_mtime; timestruc_t sharetab_snap_time; uint_t sharetab_generation; /* Only increments and wraps! */ static uint_t pkp_tab[SHARETAB_HASHES]; /* * Initialize table in pseudo-random fashion * for use in Pearson's string hash algorithm. * * See: Communications of the ACM, June 1990 Vol 33 pp 677-680 * http://www.acm.org/pubs/citations/journals/cacm/1990-33-6/p677-pearson */ static void init_pkp_tab(void) { int i; int j; int k = 7; uint_t s; for (i = 0; i < SHARETAB_HASHES; i++) pkp_tab[i] = i; for (j = 0; j < 4; j++) { for (i = 0; i < SHARETAB_HASHES; i++) { s = pkp_tab[i]; k = MOD2((k + s), SHARETAB_HASHES); pkp_tab[i] = pkp_tab[k]; pkp_tab[k] = s; } } } /* * Take care of cleaning up a share. * If passed in a length array, use it to determine how much * space to clean up. Else, figure that out. */ static void sharefree(share_t *sh, sharefs_lens_t *shl) { if (!sh) return; SHARETAB_DELETE_FIELD(path); SHARETAB_DELETE_FIELD(res); SHARETAB_DELETE_FIELD(fstype); SHARETAB_DELETE_FIELD(opts); SHARETAB_DELETE_FIELD(descr); kmem_free(sh, sizeof (share_t)); } /* * If there is no error, then this function is responsible for * cleaning up the memory associated with the share argument. */ static int sharefs_remove(share_t *sh, sharefs_lens_t *shl) { int iHash; sharetab_t *sht; share_t *s, *p; int iPath; if (!sh) return (ENOENT); rw_enter(&sharetab_lock, RW_WRITER); for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) { if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) { break; } } /* * There does not exist a fstype in memory which * matches the share passed in. */ if (!sht) { rw_exit(&sharetab_lock); return (ENOENT); } iPath = shl ? shl->shl_path : strlen(sh->sh_path); SHARETAB_HASH_IT(iHash, sh->sh_path); /* * Now walk down the hash table and find the entry to free! */ for (p = NULL, s = sht->s_buckets[iHash].ssh_sh; s != NULL; s = s->sh_next) { /* * We need exact matches. */ if (strcmp(sh->sh_path, s->sh_path) == 0 && strlen(s->sh_path) == iPath) { if (p) { p->sh_next = s->sh_next; } else { sht->s_buckets[iHash].ssh_sh = s->sh_next; } ASSERT(sht->s_buckets[iHash].ssh_count != 0); atomic_add_32(&sht->s_buckets[iHash].ssh_count, -1); atomic_add_32(&sht->s_count, -1); atomic_add_32(&sharetab_count, -1); ASSERT(sharetab_size >= s->sh_size); sharetab_size -= s->sh_size; gethrestime(&sharetab_mtime); atomic_add_32(&sharetab_generation, 1); break; } p = s; } rw_exit(&sharetab_lock); if (!s) { return (ENOENT); } s->sh_next = NULL; sharefree(s, NULL); /* * We need to free the share for the caller. */ sharefree(sh, shl); return (0); } /* * The caller must have allocated memory for us to use. */ static int sharefs_add(share_t *sh, sharefs_lens_t *shl) { int iHash; sharetab_t *sht; share_t *s, *p; int iPath; int n; if (!sh) { return (ENOENT); } /* * We need to find the hash buckets for the fstype. */ rw_enter(&sharetab_lock, RW_WRITER); for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) { if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) { break; } } /* * Did not exist, so allocate one and add it to the * sharetab. */ if (!sht) { sht = kmem_zalloc(sizeof (*sht), KM_SLEEP); n = strlen(sh->sh_fstype); sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP); (void) strncpy(sht->s_fstype, sh->sh_fstype, n); sht->s_next = sharefs_sharetab; sharefs_sharetab = sht; } /* * Now we need to find where we have to add the entry. */ SHARETAB_HASH_IT(iHash, sh->sh_path); iPath = shl ? shl->shl_path : strlen(sh->sh_path); if (shl) { sh->sh_size = shl->shl_path + shl->shl_res + shl->shl_fstype + shl->shl_opts + shl->shl_descr; } else { sh->sh_size = strlen(sh->sh_path) + strlen(sh->sh_res) + strlen(sh->sh_fstype) + strlen(sh->sh_opts) + strlen(sh->sh_descr); } /* * We need to account for field seperators and * the EOL. */ sh->sh_size += 5; /* * Now walk down the hash table and add the new entry! */ for (p = NULL, s = sht->s_buckets[iHash].ssh_sh; s != NULL; s = s->sh_next) { /* * We need exact matches. * * We found a matching path. Either we have a * duplicate path in a share command or we are * being asked to replace an existing entry. */ if (strcmp(sh->sh_path, s->sh_path) == 0 && strlen(s->sh_path) == iPath) { if (p) { p->sh_next = sh; } else { sht->s_buckets[iHash].ssh_sh = sh; } sh->sh_next = s->sh_next; ASSERT(sharetab_size >= s->sh_size); sharetab_size -= s->sh_size; sharetab_size += sh->sh_size; /* * Get rid of the old node. */ sharefree(s, NULL); gethrestime(&sharetab_mtime); atomic_add_32(&sharetab_generation, 1); ASSERT(sht->s_buckets[iHash].ssh_count != 0); rw_exit(&sharetab_lock); return (0); } p = s; } /* * Okay, we have gone through the entire hash chain and not * found a match. We just need to add this node. */ sh->sh_next = sht->s_buckets[iHash].ssh_sh; sht->s_buckets[iHash].ssh_sh = sh; atomic_add_32(&sht->s_buckets[iHash].ssh_count, 1); atomic_add_32(&sht->s_count, 1); atomic_add_32(&sharetab_count, 1); sharetab_size += sh->sh_size; gethrestime(&sharetab_mtime); atomic_add_32(&sharetab_generation, 1); rw_exit(&sharetab_lock); return (0); } void sharefs_sharetab_init(void) { init_pkp_tab(); rw_init(&sharetab_lock, NULL, RW_DEFAULT, NULL); rw_init(&sharefs_lock, NULL, RW_DEFAULT, NULL); sharetab_size = 0; sharetab_count = 0; sharetab_generation = 1; gethrestime(&sharetab_mtime); gethrestime(&sharetab_snap_time); } int sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen) { int error = 0; size_t len; size_t bufsz; share_t *sh; sharefs_lens_t shl; model_t model; char *buf = NULL; STRUCT_DECL(share, u_sh); bufsz = iMaxLen; /* * Before we do anything, lets make sure we have * a sharetab in memory if we need one. */ rw_enter(&sharetab_lock, RW_READER); switch (opcode) { case (SHAREFS_REMOVE) : case (SHAREFS_REPLACE) : if (!sharefs_sharetab) { rw_exit(&sharetab_lock); return (set_errno(ENOENT)); } break; case (SHAREFS_ADD) : default : break; } rw_exit(&sharetab_lock); model = get_udatamodel(); /* * Initialize the data pointers. */ STRUCT_INIT(u_sh, model); if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh))) { return (set_errno(EFAULT)); } /* * Get the share. */ sh = kmem_zalloc(sizeof (share_t), KM_SLEEP); /* * Get some storage for copying in the strings. */ buf = kmem_zalloc(bufsz + 1, KM_SLEEP); bzero(&shl, sizeof (sharefs_lens_t)); /* * Only grab these two until we know what we want. */ SHARETAB_COPYIN(path); SHARETAB_COPYIN(fstype); switch (opcode) { case (SHAREFS_ADD) : case (SHAREFS_REPLACE) : SHARETAB_COPYIN(res); SHARETAB_COPYIN(opts); SHARETAB_COPYIN(descr); error = sharefs_add(sh, &shl); break; case (SHAREFS_REMOVE) : error = sharefs_remove(sh, &shl); break; default: error = EINVAL; break; } cleanup: /* * If there is no error, then we have stashed the structure * away in the sharetab hash table or have deleted it. * * Either way, the only reason to blow away the data is if * there was an error. */ if (error != 0) { sharefree(sh, &shl); } if (buf) { kmem_free(buf, bufsz + 1); } return ((error != 0) ? set_errno(error) : 0); } int sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen) { if (secpolicy_sys_config(CRED(), B_FALSE) != 0) return (set_errno(EPERM)); return (sharefs_impl(opcode, sh_in, iMaxLen)); }