#include <sys/types.h>
#include <sys/types32.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <rpc/types.h>
#include <sys/vfs.h>
#include <sys/siginfo.h>
#include <sys/proc.h>
#include <sys/kmem.h>
#include <sys/pathname.h>
#include <sys/debug.h>
#include <sys/vtrace.h>
#include <sys/cmn_err.h>
#include <sys/atomic.h>
#include <sys/policy.h>
#include <sharefs/sharefs.h>
#define SHARETAB_COPYIN(field) \
if (copyinstr(STRUCT_FGETP(u_sh, sh_##field), \
buf, \
bufsz + 1, \
&len)) { \
error = EFAULT; \
goto cleanup; \
} \
\
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;
#define SHARETAB_DELETE_FIELD(field) \
if (sh->sh_##field != NULL) { \
kmem_free(sh->sh_##field, \
shl ? shl->shl_##field + 1 : \
strlen(sh->sh_##field) + 1); \
}
static zone_key_t sharetab_zone_key;
static void
sharefree(share_t *sh, sharefs_lens_t *shl)
{
if (sh == NULL)
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 (*sh));
}
static int
sharefs_remove(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
{
int iHash;
sharetab_t *sht;
share_t *s, *p;
int iPath;
if (!sh)
return (ENOENT);
rw_enter(&sg->sharetab_lock, RW_WRITER);
for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
break;
}
if (sht == NULL) {
rw_exit(&sg->sharetab_lock);
return (ENOENT);
}
iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
s != NULL; s = s->sh_next) {
if (strcmp(sh->sh_path, s->sh_path) == 0 &&
strlen(s->sh_path) == iPath) {
if (p != NULL)
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_dec_32(&sht->s_buckets[iHash].ssh_count);
atomic_dec_32(&sht->s_count);
atomic_dec_32(&sg->sharetab_count);
ASSERT(sg->sharetab_size >= s->sh_size);
sg->sharetab_size -= s->sh_size;
gethrestime(&sg->sharetab_mtime);
atomic_inc_32(&sg->sharetab_generation);
break;
}
p = s;
}
rw_exit(&sg->sharetab_lock);
if (s == NULL)
return (ENOENT);
s->sh_next = NULL;
sharefree(s, NULL);
sharefree(sh, shl);
return (0);
}
static int
sharefs_add(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
{
int iHash;
sharetab_t *sht;
share_t *s, *p;
int iPath;
int n;
if (sh == NULL)
return (ENOENT);
rw_enter(&sg->sharetab_lock, RW_WRITER);
for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
break;
}
if (sht == NULL) {
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 = sg->sharefs_sharetab;
sg->sharefs_sharetab = sht;
}
iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
iHash = pkp_tab_hash(sh->sh_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);
}
sh->sh_size += 5;
for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
s != NULL; s = s->sh_next) {
if (strcmp(sh->sh_path, s->sh_path) == 0 &&
strlen(s->sh_path) == iPath) {
if (p != NULL)
p->sh_next = sh;
else
sht->s_buckets[iHash].ssh_sh = sh;
sh->sh_next = s->sh_next;
ASSERT(sg->sharetab_size >= s->sh_size);
sg->sharetab_size -= s->sh_size;
sg->sharetab_size += sh->sh_size;
sharefree(s, NULL);
gethrestime(&sg->sharetab_mtime);
atomic_inc_32(&sg->sharetab_generation);
ASSERT(sht->s_buckets[iHash].ssh_count != 0);
rw_exit(&sg->sharetab_lock);
return (0);
}
p = s;
}
sh->sh_next = sht->s_buckets[iHash].ssh_sh;
sht->s_buckets[iHash].ssh_sh = sh;
atomic_inc_32(&sht->s_buckets[iHash].ssh_count);
atomic_inc_32(&sht->s_count);
atomic_inc_32(&sg->sharetab_count);
sg->sharetab_size += sh->sh_size;
gethrestime(&sg->sharetab_mtime);
atomic_inc_32(&sg->sharetab_generation);
rw_exit(&sg->sharetab_lock);
return (0);
}
static void *
sharetab_zone_init(zoneid_t zoneid)
{
sharetab_globals_t *sg;
sg = kmem_zalloc(sizeof (*sg), KM_SLEEP);
rw_init(&sg->sharetab_lock, NULL, RW_DEFAULT, NULL);
rw_init(&sg->sharefs_lock, NULL, RW_DEFAULT, NULL);
sg->sharetab_size = 0;
sg->sharetab_count = 0;
sg->sharetab_generation = 1;
gethrestime(&sg->sharetab_mtime);
gethrestime(&sg->sharetab_snap_time);
return (sg);
}
static void
sharetab_zone_fini(zoneid_t zoneid, void *data)
{
sharetab_globals_t *sg = data;
rw_destroy(&sg->sharefs_lock);
rw_destroy(&sg->sharetab_lock);
while (sg->sharefs_sharetab != NULL) {
int i;
sharetab_t *freeing = sg->sharefs_sharetab;
sg->sharefs_sharetab = freeing->s_next;
kmem_free(freeing->s_fstype, strlen(freeing->s_fstype) + 1);
for (i = 0; i < PKP_HASH_SIZE; i++) {
sharefs_hash_head_t *bucket;
bucket = &(freeing->s_buckets[i]);
while (bucket->ssh_sh != NULL) {
share_t *share = bucket->ssh_sh;
bucket->ssh_sh = share->sh_next;
sharefree(share, NULL);
}
}
kmem_free(freeing, sizeof (*freeing));
}
kmem_free(sg, sizeof (*sg));
}
void
sharefs_sharetab_init(void)
{
zone_key_create(&sharetab_zone_key, sharetab_zone_init,
NULL, sharetab_zone_fini);
}
sharetab_globals_t *
sharetab_get_globals(zone_t *zone)
{
return (zone_getspecific(sharetab_zone_key, zone));
}
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;
sharetab_globals_t *sg = sharetab_get_globals(curzone);
STRUCT_DECL(share, u_sh);
bufsz = iMaxLen;
rw_enter(&sg->sharetab_lock, RW_READER);
switch (opcode) {
case SHAREFS_REMOVE:
case SHAREFS_REPLACE:
if (!sg->sharefs_sharetab) {
rw_exit(&sg->sharetab_lock);
return (set_errno(ENOENT));
}
break;
case SHAREFS_ADD:
default:
break;
}
rw_exit(&sg->sharetab_lock);
model = get_udatamodel();
STRUCT_INIT(u_sh, model);
if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh)))
return (set_errno(EFAULT));
sh = kmem_zalloc(sizeof (share_t), KM_SLEEP);
buf = kmem_zalloc(bufsz + 1, KM_SLEEP);
bzero(&shl, sizeof (sharefs_lens_t));
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(sg, sh, &shl);
break;
case SHAREFS_REMOVE:
error = sharefs_remove(sg, sh, &shl);
break;
default:
error = EINVAL;
break;
}
cleanup:
if (error != 0)
sharefree(sh, &shl);
if (buf != NULL)
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 (INGLOBALZONE(curproc)) {
if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
return (set_errno(EPERM));
} else {
if (secpolicy_nfs(CRED()) != 0)
return (set_errno(EPERM));
}
return (sharefs_impl(opcode, sh_in, iMaxLen));
}