#include <smbsrv/smb_door.h>
#include <smbsrv/smb_ktypes.h>
#include <smbsrv/smb2_kproto.h>
#include <smbsrv/smb_kstat.h>
typedef struct smb_unshare {
list_node_t us_lnd;
char us_sharename[MAXNAMELEN];
} smb_unshare_t;
static kmem_cache_t *smb_kshare_cache_share;
static kmem_cache_t *smb_kshare_cache_unexport;
static int smb_kshare_cmp(const void *, const void *);
static void smb_kshare_hold(const void *);
static boolean_t smb_kshare_rele(const void *);
static void smb_kshare_destroy(void *);
static char *smb_kshare_oemname(const char *);
static int smb_kshare_is_special(const char *);
static int smb_kshare_is_admin(const char *);
static smb_kshare_t *smb_kshare_decode(nvlist_t *);
static uint32_t smb_kshare_decode_bool(nvlist_t *, const char *, uint32_t);
static void smb_kshare_unexport_thread(smb_thread_t *, void *);
static int smb_kshare_export(smb_server_t *, smb_kshare_t *);
static int smb_kshare_unexport(smb_server_t *, const char *);
static int smb_kshare_export_trans(smb_server_t *, char *, char *, char *);
static void smb_kshare_csc_flags(smb_kshare_t *, const char *);
static boolean_t smb_export_isready(smb_server_t *);
#ifdef _KERNEL
static int smb_kshare_chk_dsrv_status(int, smb_dr_ctx_t *);
#endif
static const smb_avl_nops_t smb_kshare_avlops = {
smb_kshare_cmp,
smb_kshare_hold,
smb_kshare_rele,
smb_kshare_destroy
};
#ifdef _KERNEL
door_handle_t
smb_kshare_door_init(int door_id)
{
return (door_ki_lookup(door_id));
}
void
smb_kshare_door_fini(door_handle_t dhdl)
{
if (dhdl)
door_ki_rele(dhdl);
}
int
smb_kshare_upcall(door_handle_t dhdl, void *arg, boolean_t add_share)
{
door_arg_t doorarg = { 0 };
char *buf = NULL;
char *str = NULL;
int error;
int rc;
unsigned int used;
smb_dr_ctx_t *dec_ctx;
smb_dr_ctx_t *enc_ctx;
smb_share_t *lmshare = NULL;
int opcode;
opcode = (add_share) ? SMB_SHROP_ADD : SMB_SHROP_DELETE;
buf = kmem_alloc(SMB_SHARE_DSIZE, KM_SLEEP);
enc_ctx = smb_dr_encode_start(buf, SMB_SHARE_DSIZE);
smb_dr_put_uint32(enc_ctx, opcode);
switch (opcode) {
case SMB_SHROP_ADD:
lmshare = kmem_alloc(sizeof (smb_share_t), KM_SLEEP);
error = xcopyin(arg, lmshare, sizeof (smb_share_t));
if (error != 0) {
kmem_free(lmshare, sizeof (smb_share_t));
kmem_free(buf, SMB_SHARE_DSIZE);
return (error);
}
smb_dr_put_share(enc_ctx, lmshare);
break;
case SMB_SHROP_DELETE:
str = kmem_alloc(MAXPATHLEN, KM_SLEEP);
error = copyinstr(arg, str, MAXPATHLEN, NULL);
if (error != 0) {
kmem_free(str, MAXPATHLEN);
kmem_free(buf, SMB_SHARE_DSIZE);
return (error);
}
smb_dr_put_string(enc_ctx, str);
kmem_free(str, MAXPATHLEN);
break;
}
if ((error = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
kmem_free(buf, SMB_SHARE_DSIZE);
if (lmshare)
kmem_free(lmshare, sizeof (smb_share_t));
return (NERR_InternalError);
}
doorarg.data_ptr = buf;
doorarg.data_size = used;
doorarg.rbuf = buf;
doorarg.rsize = SMB_SHARE_DSIZE;
error = door_ki_upcall_limited(dhdl, &doorarg, NULL, SIZE_MAX, 0);
if (error) {
kmem_free(buf, SMB_SHARE_DSIZE);
if (lmshare)
kmem_free(lmshare, sizeof (smb_share_t));
return (error);
}
dec_ctx = smb_dr_decode_start(doorarg.data_ptr, doorarg.data_size);
if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
kmem_free(buf, SMB_SHARE_DSIZE);
if (lmshare)
kmem_free(lmshare, sizeof (smb_share_t));
return (NERR_InternalError);
}
rc = smb_dr_get_uint32(dec_ctx);
if (opcode == SMB_SHROP_ADD)
smb_dr_get_share(dec_ctx, lmshare);
if (smb_dr_decode_finish(dec_ctx))
rc = NERR_InternalError;
kmem_free(buf, SMB_SHARE_DSIZE);
if (lmshare)
kmem_free(lmshare, sizeof (smb_share_t));
return ((rc == NERR_DuplicateShare && add_share) ? 0 : rc);
}
#endif
int
smb_kshare_exec(smb_server_t *sv, smb_shr_execinfo_t *execinfo)
{
int exec_rc = 0;
(void) smb_kdoor_upcall(sv, SMB_DR_SHR_EXEC,
execinfo, smb_shr_execinfo_xdr, &exec_rc, xdr_int);
return (exec_rc);
}
uint32_t
smb_kshare_hostaccess(smb_kshare_t *shr, smb_session_t *session)
{
smb_shr_hostaccess_query_t req;
smb_inaddr_t *ipaddr = &session->ipaddr;
uint32_t host_access = SMB_SHRF_ACC_OPEN;
uint32_t flag = SMB_SHRF_ACC_OPEN;
uint32_t access;
if (smb_inet_iszero(ipaddr))
return (ACE_ALL_PERMS);
if ((shr->shr_access_none == NULL || *shr->shr_access_none == '\0') &&
(shr->shr_access_ro == NULL || *shr->shr_access_ro == '\0') &&
(shr->shr_access_rw == NULL || *shr->shr_access_rw == '\0'))
return (ACE_ALL_PERMS);
if (shr->shr_access_none != NULL)
flag |= SMB_SHRF_ACC_NONE;
if (shr->shr_access_ro != NULL)
flag |= SMB_SHRF_ACC_RO;
if (shr->shr_access_rw != NULL)
flag |= SMB_SHRF_ACC_RW;
req.shq_none = shr->shr_access_none;
req.shq_ro = shr->shr_access_ro;
req.shq_rw = shr->shr_access_rw;
req.shq_flag = flag;
req.shq_ipaddr = *ipaddr;
(void) smb_kdoor_upcall(session->s_server, SMB_DR_SHR_HOSTACCESS,
&req, smb_shr_hostaccess_query_xdr, &host_access, xdr_uint32_t);
switch (host_access) {
case SMB_SHRF_ACC_RO:
access = ACE_ALL_PERMS & ~ACE_ALL_WRITE_PERMS;
break;
case SMB_SHRF_ACC_OPEN:
case SMB_SHRF_ACC_RW:
access = ACE_ALL_PERMS;
break;
case SMB_SHRF_ACC_NONE:
default:
access = 0;
}
return (access);
}
void
smb_export_start(smb_server_t *sv)
{
mutex_enter(&sv->sv_export.e_mutex);
if (sv->sv_export.e_ready) {
mutex_exit(&sv->sv_export.e_mutex);
return;
}
sv->sv_export.e_ready = B_TRUE;
mutex_exit(&sv->sv_export.e_mutex);
smb_avl_create(&sv->sv_export.e_share_avl, sizeof (smb_kshare_t),
offsetof(smb_kshare_t, shr_link), &smb_kshare_avlops);
(void) smb_kshare_export_trans(sv, "IPC$", "IPC$", "Remote IPC");
(void) smb_kshare_export_trans(sv, "c$", SMB_CVOL, "Default Share");
(void) smb_kshare_export_trans(sv, "vss$", SMB_VSS, "VSS");
}
void
smb_export_stop(smb_server_t *sv)
{
mutex_enter(&sv->sv_export.e_mutex);
if (!sv->sv_export.e_ready) {
mutex_exit(&sv->sv_export.e_mutex);
return;
}
sv->sv_export.e_ready = B_FALSE;
mutex_exit(&sv->sv_export.e_mutex);
smb_avl_destroy(&sv->sv_export.e_share_avl);
}
void
smb_kshare_g_init(void)
{
smb_kshare_cache_share = kmem_cache_create("smb_share_cache",
sizeof (smb_kshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
smb_kshare_cache_unexport = kmem_cache_create("smb_unexport_cache",
sizeof (smb_unshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
}
void
smb_kshare_init(smb_server_t *sv)
{
smb_slist_constructor(&sv->sv_export.e_unexport_list,
sizeof (smb_unshare_t), offsetof(smb_unshare_t, us_lnd));
}
int
smb_kshare_start(smb_server_t *sv)
{
smb_thread_init(&sv->sv_export.e_unexport_thread, "smb_kshare_unexport",
smb_kshare_unexport_thread, sv, smbsrv_base_pri, sv);
return (smb_thread_start(&sv->sv_export.e_unexport_thread));
}
void
smb_kshare_stop(smb_server_t *sv)
{
smb_thread_stop(&sv->sv_export.e_unexport_thread);
smb_thread_destroy(&sv->sv_export.e_unexport_thread);
}
void
smb_kshare_fini(smb_server_t *sv)
{
smb_unshare_t *ux;
while ((ux = list_head(&sv->sv_export.e_unexport_list.sl_list))
!= NULL) {
smb_slist_remove(&sv->sv_export.e_unexport_list, ux);
kmem_cache_free(smb_kshare_cache_unexport, ux);
}
smb_slist_destructor(&sv->sv_export.e_unexport_list);
}
void
smb_kshare_g_fini(void)
{
kmem_cache_destroy(smb_kshare_cache_unexport);
kmem_cache_destroy(smb_kshare_cache_share);
}
int
smb_kshare_export_list(smb_server_t *sv, smb_ioc_share_t *ioc)
{
nvlist_t *shrlist = NULL;
nvlist_t *share;
nvpair_t *nvp;
smb_kshare_t *shr;
char *shrname;
int rc;
if (!smb_export_isready(sv)) {
rc = ENOTACTIVE;
goto out;
}
if ((ioc->shrlen + offsetof(smb_ioc_share_t, shr)) > ioc->hdr.len) {
rc = EINVAL;
goto out;
}
rc = nvlist_unpack(ioc->shr, ioc->shrlen, &shrlist, KM_SLEEP);
if (rc != 0)
goto out;
for (nvp = nvlist_next_nvpair(shrlist, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(shrlist, nvp)) {
if (sv->sv_state != SMB_SERVER_STATE_RUNNING)
goto out;
if (nvpair_type(nvp) != DATA_TYPE_NVLIST)
continue;
shrname = nvpair_name(nvp);
ASSERT(shrname);
if ((rc = nvpair_value_nvlist(nvp, &share)) != 0) {
cmn_err(CE_WARN, "export[%s]: failed accessing",
shrname);
continue;
}
if ((shr = smb_kshare_decode(share)) == NULL) {
cmn_err(CE_WARN, "export[%s]: failed decoding",
shrname);
continue;
}
if ((rc = smb_kshare_export(sv, shr)) != 0) {
smb_kshare_destroy(shr);
continue;
}
}
rc = 0;
out:
nvlist_free(shrlist);
return (rc);
}
int
smb_kshare_unexport_list(smb_server_t *sv, smb_ioc_share_t *ioc)
{
smb_unshare_t *ux;
nvlist_t *shrlist = NULL;
nvpair_t *nvp;
boolean_t unexport = B_FALSE;
char *shrname;
int rc;
if ((ioc->shrlen + offsetof(smb_ioc_share_t, shr)) > ioc->hdr.len) {
rc = EINVAL;
goto out;
}
if ((rc = nvlist_unpack(ioc->shr, ioc->shrlen, &shrlist, 0)) != 0)
goto out;
for (nvp = nvlist_next_nvpair(shrlist, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(shrlist, nvp)) {
if (nvpair_type(nvp) != DATA_TYPE_NVLIST)
continue;
shrname = nvpair_name(nvp);
ASSERT(shrname);
if ((rc = smb_kshare_unexport(sv, shrname)) != 0)
continue;
ux = kmem_cache_alloc(smb_kshare_cache_unexport, KM_SLEEP);
(void) strlcpy(ux->us_sharename, shrname, MAXNAMELEN);
smb_slist_insert_tail(&sv->sv_export.e_unexport_list, ux);
unexport = B_TRUE;
}
if (unexport)
smb_thread_signal(&sv->sv_export.e_unexport_thread);
rc = 0;
out:
nvlist_free(shrlist);
return (rc);
}
int
smb_kshare_info(smb_server_t *sv, smb_ioc_shareinfo_t *ioc)
{
ioc->shortnames = sv->sv_cfg.skc_short_names;
return (0);
}
int
smb_kshare_access(smb_server_t *sv, smb_ioc_shareaccess_t *ioc)
{
smb_user_t *user = NULL;
smb_kshare_t *shr = NULL;
smb_node_t *shroot = NULL;
vnode_t *vp = NULL;
int rc = EACCES;
shr = smb_kshare_lookup(sv, ioc->shrname);
if (shr == NULL) {
rc = ENOENT;
goto out;
}
if ((shroot = shr->shr_root_node) == NULL) {
rc = 0;
goto out;
}
vp = shroot->vp;
user = smb_server_lookup_user(sv, ioc->session_id, ioc->user_id);
if (user == NULL) {
rc = EINVAL;
goto out;
}
ASSERT(user->u_cred != NULL);
rc = smb_vop_access(vp, VREAD, 0, NULL, user->u_cred);
if (rc != 0)
rc = smb_vop_access(vp, VWRITE, 0, NULL, user->u_cred);
out:
if (user != NULL)
smb_user_release(user);
if (shr != NULL)
smb_kshare_release(sv, shr);
return (rc);
}
void
smb_kshare_enum(smb_server_t *sv, smb_enumshare_info_t *esi)
{
smb_avl_t *share_avl;
smb_avl_cursor_t cursor;
smb_kshare_t *shr;
int remained;
uint16_t infolen = 0;
uint16_t cmntlen = 0;
uint16_t sharelen;
uint16_t clen;
uint32_t cmnt_offs;
smb_msgbuf_t info_mb;
smb_msgbuf_t cmnt_mb;
boolean_t autohome_added = B_FALSE;
if (!smb_export_isready(sv)) {
esi->es_ntotal = esi->es_nsent = 0;
esi->es_datasize = 0;
return;
}
esi->es_ntotal = esi->es_nsent = 0;
remained = esi->es_bufsize;
share_avl = &sv->sv_export.e_share_avl;
smb_avl_iterinit(share_avl, &cursor);
while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
if (shr->shr_oemname == NULL) {
smb_avl_release(share_avl, shr);
continue;
}
if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
if (esi->es_posix_uid == shr->shr_uid) {
autohome_added = B_TRUE;
} else {
smb_avl_release(share_avl, shr);
continue;
}
}
esi->es_ntotal++;
if (remained <= 0) {
smb_avl_release(share_avl, shr);
continue;
}
clen = strlen(shr->shr_cmnt) + 1;
sharelen = SHARE_INFO_1_SIZE + clen;
if (sharelen <= remained) {
infolen += SHARE_INFO_1_SIZE;
cmntlen += clen;
}
remained -= sharelen;
smb_avl_release(share_avl, shr);
}
esi->es_datasize = infolen + cmntlen;
smb_msgbuf_init(&info_mb, (uint8_t *)esi->es_buf, infolen, 0);
smb_msgbuf_init(&cmnt_mb, (uint8_t *)esi->es_buf + infolen, cmntlen, 0);
cmnt_offs = infolen;
smb_avl_iterinit(share_avl, &cursor);
autohome_added = B_FALSE;
while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
if (shr->shr_oemname == NULL) {
smb_avl_release(share_avl, shr);
continue;
}
if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
if (esi->es_posix_uid == shr->shr_uid) {
autohome_added = B_TRUE;
} else {
smb_avl_release(share_avl, shr);
continue;
}
}
if (smb_msgbuf_encode(&info_mb, "13c.wl",
shr->shr_oemname, shr->shr_type, cmnt_offs) < 0) {
smb_avl_release(share_avl, shr);
break;
}
if (smb_msgbuf_encode(&cmnt_mb, "s", shr->shr_cmnt) < 0) {
smb_avl_release(share_avl, shr);
break;
}
cmnt_offs += strlen(shr->shr_cmnt) + 1;
esi->es_nsent++;
smb_avl_release(share_avl, shr);
}
smb_msgbuf_term(&info_mb);
smb_msgbuf_term(&cmnt_mb);
}
smb_kshare_t *
smb_kshare_lookup(smb_server_t *sv, const char *shrname)
{
smb_kshare_t key;
smb_kshare_t *shr;
ASSERT(shrname);
if (!smb_export_isready(sv))
return (NULL);
key.shr_name = (char *)shrname;
shr = smb_avl_lookup(&sv->sv_export.e_share_avl, &key);
return (shr);
}
void
smb_kshare_release(smb_server_t *sv, smb_kshare_t *shr)
{
ASSERT(shr);
ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
smb_avl_release(&sv->sv_export.e_share_avl, shr);
}
static int
smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr)
{
smb_avl_t *share_avl;
smb_kshare_t *auto_shr;
smb_node_t *snode = NULL;
int rc = 0;
share_avl = &sv->sv_export.e_share_avl;
if (!STYPE_ISDSK(shr->shr_type)) {
if ((rc = smb_avl_add(share_avl, shr)) != 0) {
cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
shr->shr_name, rc);
}
return (rc);
}
if ((auto_shr = smb_avl_lookup(share_avl, shr)) != NULL) {
rc = EEXIST;
if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
mutex_enter(&auto_shr->shr_mutex);
auto_shr->shr_autocnt++;
mutex_exit(&auto_shr->shr_mutex);
rc = 0;
}
smb_avl_release(share_avl, auto_shr);
return (rc);
}
rc = smb_server_share_lookup(sv, shr->shr_path, &snode);
if (rc != 0) {
cmn_err(CE_WARN, "export[%s(%s)]: lookup failed (%d)",
shr->shr_name, shr->shr_path, rc);
return (rc);
}
shr->shr_root_node = snode;
if ((rc = smb_avl_add(share_avl, shr)) != 0) {
cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
shr->shr_name, rc);
shr->shr_root_node = NULL;
smb_node_release(snode);
return (rc);
}
if ((shr->shr_flags & SMB_SHRF_CA) != 0) {
rc = smb2_dh_new_ca_share(sv, shr);
if (rc != 0) {
mutex_enter(&shr->shr_mutex);
shr->shr_flags &= ~SMB_SHRF_CA;
mutex_exit(&shr->shr_mutex);
rc = 0;
}
}
return (rc);
}
static int
smb_kshare_unexport(smb_server_t *sv, const char *shrname)
{
smb_avl_t *share_avl;
smb_kshare_t key;
smb_kshare_t *shr;
boolean_t auto_unexport;
share_avl = &sv->sv_export.e_share_avl;
key.shr_name = (char *)shrname;
if ((shr = smb_avl_lookup(share_avl, &key)) == NULL)
return (ENOENT);
if ((shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
mutex_enter(&shr->shr_mutex);
shr->shr_autocnt--;
auto_unexport = (shr->shr_autocnt == 0);
mutex_exit(&shr->shr_mutex);
if (!auto_unexport) {
smb_avl_release(share_avl, shr);
return (0);
}
}
smb_avl_remove(share_avl, shr);
mutex_enter(&shr->shr_mutex);
shr->shr_flags |= SMB_SHRF_REMOVED;
mutex_exit(&shr->shr_mutex);
smb_avl_release(share_avl, shr);
return (0);
}
static int
smb_kshare_export_trans(smb_server_t *sv, char *name, char *path, char *cmnt)
{
smb_kshare_t *shr;
ASSERT(name);
ASSERT(path);
shr = kmem_cache_alloc(smb_kshare_cache_share, KM_SLEEP);
bzero(shr, sizeof (smb_kshare_t));
shr->shr_magic = SMB_SHARE_MAGIC;
shr->shr_refcnt = 1;
shr->shr_flags = SMB_SHRF_TRANS | smb_kshare_is_admin(name);
if (strcasecmp(name, "IPC$") == 0)
shr->shr_type = STYPE_IPC;
else
shr->shr_type = STYPE_DISKTREE;
shr->shr_type |= smb_kshare_is_special(name);
shr->shr_name = smb_mem_strdup(name);
if (path)
shr->shr_path = smb_mem_strdup(path);
if (cmnt)
shr->shr_cmnt = smb_mem_strdup(cmnt);
shr->shr_oemname = smb_kshare_oemname(name);
return (smb_kshare_export(sv, shr));
}
static smb_kshare_t *
smb_kshare_decode(nvlist_t *share)
{
smb_kshare_t tmp;
smb_kshare_t *shr;
nvlist_t *smb;
char *csc_name = NULL, *strbuf = NULL;
int rc;
ASSERT(share);
bzero(&tmp, sizeof (smb_kshare_t));
rc = nvlist_lookup_string(share, "name", &tmp.shr_name);
rc |= nvlist_lookup_string(share, "path", &tmp.shr_path);
(void) nvlist_lookup_string(share, "desc", &tmp.shr_cmnt);
ASSERT(tmp.shr_name && tmp.shr_path);
rc |= nvlist_lookup_nvlist(share, "smb", &smb);
if (rc != 0) {
cmn_err(CE_WARN, "kshare: failed looking up SMB properties"
" (%d)", rc);
return (NULL);
}
rc = nvlist_lookup_uint32(smb, "type", &tmp.shr_type);
if (rc != 0) {
cmn_err(CE_WARN, "kshare[%s]: failed getting the share type"
" (%d)", tmp.shr_name, rc);
return (NULL);
}
(void) nvlist_lookup_string(smb, SHOPT_AD_CONTAINER,
&tmp.shr_container);
(void) nvlist_lookup_string(smb, SHOPT_NONE, &tmp.shr_access_none);
(void) nvlist_lookup_string(smb, SHOPT_RO, &tmp.shr_access_ro);
(void) nvlist_lookup_string(smb, SHOPT_RW, &tmp.shr_access_rw);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_ABE, SMB_SHRF_ABE);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CATIA,
SMB_SHRF_CATIA);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_GUEST,
SMB_SHRF_GUEST_OK);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_DFSROOT,
SMB_SHRF_DFSROOT);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_QUOTAS,
SMB_SHRF_QUOTAS);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CA, SMB_SHRF_CA);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_FSO, SMB_SHRF_FSO);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_AUTOHOME,
SMB_SHRF_AUTOHOME);
if ((tmp.shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME) {
rc = nvlist_lookup_uint32(smb, "uid", &tmp.shr_uid);
rc |= nvlist_lookup_uint32(smb, "gid", &tmp.shr_gid);
if (rc != 0) {
cmn_err(CE_WARN, "kshare: failed looking up uid/gid"
" (%d)", rc);
return (NULL);
}
}
(void) nvlist_lookup_string(smb, SHOPT_ENCRYPT, &strbuf);
smb_cfg_set_require(strbuf, &tmp.shr_encrypt);
(void) nvlist_lookup_string(smb, SHOPT_CSC, &csc_name);
smb_kshare_csc_flags(&tmp, csc_name);
shr = kmem_cache_alloc(smb_kshare_cache_share, KM_SLEEP);
bzero(shr, sizeof (smb_kshare_t));
shr->shr_magic = SMB_SHARE_MAGIC;
shr->shr_refcnt = 1;
shr->shr_name = smb_mem_strdup(tmp.shr_name);
shr->shr_path = smb_mem_strdup(tmp.shr_path);
if (tmp.shr_cmnt)
shr->shr_cmnt = smb_mem_strdup(tmp.shr_cmnt);
if (tmp.shr_container)
shr->shr_container = smb_mem_strdup(tmp.shr_container);
if (tmp.shr_access_none)
shr->shr_access_none = smb_mem_strdup(tmp.shr_access_none);
if (tmp.shr_access_ro)
shr->shr_access_ro = smb_mem_strdup(tmp.shr_access_ro);
if (tmp.shr_access_rw)
shr->shr_access_rw = smb_mem_strdup(tmp.shr_access_rw);
shr->shr_oemname = smb_kshare_oemname(shr->shr_name);
shr->shr_flags = tmp.shr_flags | smb_kshare_is_admin(shr->shr_name);
shr->shr_type = tmp.shr_type | smb_kshare_is_special(shr->shr_name);
shr->shr_encrypt = tmp.shr_encrypt;
shr->shr_uid = tmp.shr_uid;
shr->shr_gid = tmp.shr_gid;
if ((shr->shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME)
shr->shr_autocnt = 1;
return (shr);
}
#if 0
static void
smb_kshare_log(smb_kshare_t *shr)
{
cmn_err(CE_NOTE, "Share info:");
cmn_err(CE_NOTE, "\tname: %s", (shr->shr_name) ? shr->shr_name : "");
cmn_err(CE_NOTE, "\tpath: %s", (shr->shr_path) ? shr->shr_path : "");
cmn_err(CE_NOTE, "\tcmnt: (%s)",
(shr->shr_cmnt) ? shr->shr_cmnt : "NULL");
cmn_err(CE_NOTE, "\toemname: (%s)",
(shr->shr_oemname) ? shr->shr_oemname : "NULL");
cmn_err(CE_NOTE, "\tflags: %X", shr->shr_flags);
cmn_err(CE_NOTE, "\ttype: %d", shr->shr_type);
}
#endif
static int
smb_kshare_cmp(const void *p1, const void *p2)
{
smb_kshare_t *shr1 = (smb_kshare_t *)p1;
smb_kshare_t *shr2 = (smb_kshare_t *)p2;
int rc;
ASSERT(shr1);
ASSERT(shr1->shr_name);
ASSERT(shr2);
ASSERT(shr2->shr_name);
rc = smb_strcasecmp(shr1->shr_name, shr2->shr_name, 0);
if (rc < 0)
return (-1);
if (rc > 0)
return (1);
return (0);
}
static void
smb_kshare_hold(const void *p)
{
smb_kshare_t *shr = (smb_kshare_t *)p;
ASSERT(shr);
ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
mutex_enter(&shr->shr_mutex);
shr->shr_refcnt++;
mutex_exit(&shr->shr_mutex);
}
static boolean_t
smb_kshare_rele(const void *p)
{
smb_kshare_t *shr = (smb_kshare_t *)p;
boolean_t destroy;
ASSERT(shr);
ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
mutex_enter(&shr->shr_mutex);
ASSERT(shr->shr_refcnt > 0);
shr->shr_refcnt--;
destroy = (shr->shr_refcnt == 0);
mutex_exit(&shr->shr_mutex);
return (destroy);
}
static void
smb_kshare_destroy(void *p)
{
smb_kshare_t *shr = (smb_kshare_t *)p;
ASSERT(shr);
ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
if (shr->shr_ca_dir != NULL)
smb_node_release(shr->shr_ca_dir);
if (shr->shr_root_node)
smb_node_release(shr->shr_root_node);
smb_mem_free(shr->shr_name);
smb_mem_free(shr->shr_path);
smb_mem_free(shr->shr_cmnt);
smb_mem_free(shr->shr_container);
smb_mem_free(shr->shr_oemname);
smb_mem_free(shr->shr_access_none);
smb_mem_free(shr->shr_access_ro);
smb_mem_free(shr->shr_access_rw);
kmem_cache_free(smb_kshare_cache_share, shr);
}
static char *
smb_kshare_oemname(const char *shrname)
{
smb_wchar_t *unibuf;
char *oem_name;
int length;
length = strlen(shrname) + 1;
oem_name = smb_mem_alloc(length);
unibuf = smb_mem_alloc(length * sizeof (smb_wchar_t));
(void) smb_mbstowcs(unibuf, shrname, length);
if (ucstooem(oem_name, unibuf, length, OEM_CPG_850) == 0)
(void) strcpy(oem_name, shrname);
smb_mem_free(unibuf);
if (strlen(oem_name) + 1 > SMB_SHARE_OEMNAME_MAX) {
smb_mem_free(oem_name);
return (NULL);
}
return (oem_name);
}
static int
smb_kshare_is_special(const char *sharename)
{
int len;
if (sharename == NULL)
return (0);
if ((len = strlen(sharename)) == 0)
return (0);
if (sharename[len - 1] == '$')
return (STYPE_SPECIAL);
return (0);
}
static int
smb_kshare_is_admin(const char *sharename)
{
if (sharename == NULL)
return (0);
if (strlen(sharename) == 2 &&
smb_isalpha(sharename[0]) && sharename[1] == '$') {
return (SMB_SHRF_ADMIN);
}
return (0);
}
static uint32_t
smb_kshare_decode_bool(nvlist_t *nvl, const char *propname, uint32_t flag)
{
char *boolp;
if (nvlist_lookup_string(nvl, propname, &boolp) == 0)
if (strcasecmp(boolp, "true") == 0)
return (flag);
return (0);
}
static void
smb_kshare_csc_flags(smb_kshare_t *shr, const char *value)
{
int i;
static struct {
char *value;
uint32_t flag;
} cscopt[] = {
{ "disabled", SMB_SHRF_CSC_DISABLED },
{ "manual", SMB_SHRF_CSC_MANUAL },
{ "auto", SMB_SHRF_CSC_AUTO },
{ "vdo", SMB_SHRF_CSC_VDO }
};
if (value == NULL)
return;
for (i = 0; i < (sizeof (cscopt) / sizeof (cscopt[0])); ++i) {
if (strcasecmp(value, cscopt[i].value) == 0) {
shr->shr_flags |= cscopt[i].flag;
break;
}
}
switch (shr->shr_flags & SMB_SHRF_CSC_MASK) {
case 0:
case SMB_SHRF_CSC_DISABLED:
case SMB_SHRF_CSC_MANUAL:
case SMB_SHRF_CSC_AUTO:
case SMB_SHRF_CSC_VDO:
break;
default:
cmn_err(CE_NOTE, "csc option conflict: 0x%08x",
shr->shr_flags & SMB_SHRF_CSC_MASK);
break;
}
}
static void
smb_kshare_unexport_thread(smb_thread_t *thread, void *arg)
{
smb_server_t *sv = arg;
smb_unshare_t *ux;
while (smb_thread_continue(thread)) {
while ((ux = list_head(&sv->sv_export.e_unexport_list.sl_list))
!= NULL) {
smb_slist_remove(&sv->sv_export.e_unexport_list, ux);
(void) smb_server_unshare(ux->us_sharename);
kmem_cache_free(smb_kshare_cache_unexport, ux);
}
}
}
static boolean_t
smb_export_isready(smb_server_t *sv)
{
boolean_t ready;
mutex_enter(&sv->sv_export.e_mutex);
ready = sv->sv_export.e_ready;
mutex_exit(&sv->sv_export.e_mutex);
return (ready);
}
#ifdef _KERNEL
static int
smb_kshare_chk_dsrv_status(int opcode, smb_dr_ctx_t *dec_ctx)
{
int status = smb_dr_get_int32(dec_ctx);
int err;
switch (status) {
case SMB_SHARE_DSUCCESS:
return (0);
case SMB_SHARE_DERROR:
err = smb_dr_get_uint32(dec_ctx);
cmn_err(CE_WARN, "%d: Encountered door server error %d",
opcode, err);
(void) smb_dr_decode_finish(dec_ctx);
return (err);
}
ASSERT(0);
return (EINVAL);
}
#endif