smb_kshare.c revision 7f667e74610492ddbce8ce60f52ece95d2401949
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Kernel door client for LanMan share management.
*/
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/door.h>
#include <smbsrv/lmerr.h>
#include <smbsrv/smb_share.h>
#include <smbsrv/smb_common_door.h>
#include <smbsrv/smbinfo.h>
static int smb_kshare_chk_dsrv_status(int, smb_dr_ctx_t *);
/*
* smb_kshare_init
*
* This function is not MultiThread safe. The caller has to make sure only one
* thread calls this function.
*/
door_handle_t
smb_kshare_init(int door_id)
{
return (door_ki_lookup(door_id));
}
/*
* smb_kshare_fini
*
* This function is not MultiThread safe. The caller has to make sure only one
* thread calls this function.
*/
void
smb_kshare_fini(door_handle_t dhdl)
{
if (dhdl)
door_ki_rele(dhdl);
}
uint32_t
smb_kshare_getinfo(door_handle_t dhdl, char *share_name, smb_share_t *si,
smb_inaddr_t *ipaddr)
{
door_arg_t arg;
char *buf;
unsigned int used;
smb_dr_ctx_t *dec_ctx;
smb_dr_ctx_t *enc_ctx;
uint32_t rc;
int opcode = SMB_SHROP_GETINFO;
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);
smb_dr_put_string(enc_ctx, share_name);
smb_dr_put_buf(enc_ctx, (uchar_t *)ipaddr, sizeof (smb_inaddr_t));
if (smb_dr_encode_finish(enc_ctx, &used) != 0) {
kmem_free(buf, SMB_SHARE_DSIZE);
return (NERR_InternalError);
}
arg.data_ptr = buf;
arg.data_size = used;
arg.desc_ptr = NULL;
arg.desc_num = 0;
arg.rbuf = buf;
arg.rsize = SMB_SHARE_DSIZE;
if (door_ki_upcall_limited(dhdl, &arg, NULL, SIZE_MAX, 0) != 0) {
kmem_free(buf, SMB_SHARE_DSIZE);
return (NERR_InternalError);
}
dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
kmem_free(buf, SMB_SHARE_DSIZE);
return (NERR_InternalError);
}
rc = smb_dr_get_uint32(dec_ctx);
smb_dr_get_share(dec_ctx, si);
if (smb_dr_decode_finish(dec_ctx) != 0)
rc = NERR_InternalError;
kmem_free(buf, SMB_SHARE_DSIZE);
return (rc);
}
uint32_t
smb_kshare_enum(door_handle_t dhdl, smb_enumshare_info_t *enuminfo)
{
door_arg_t arg;
char *door_buf;
int door_bufsz;
unsigned int used;
smb_dr_ctx_t *dec_ctx;
smb_dr_ctx_t *enc_ctx;
uint32_t rc;
int opcode = SMB_SHROP_ENUM;
enuminfo->es_ntotal = enuminfo->es_nsent = 0;
door_bufsz = enuminfo->es_bufsize + strlen(enuminfo->es_username)
+ sizeof (smb_enumshare_info_t);
door_buf = kmem_alloc(door_bufsz, KM_SLEEP);
enc_ctx = smb_dr_encode_start(door_buf, door_bufsz);
smb_dr_put_uint32(enc_ctx, opcode);
smb_dr_put_ushort(enc_ctx, enuminfo->es_bufsize);
smb_dr_put_string(enc_ctx, enuminfo->es_username);
if (smb_dr_encode_finish(enc_ctx, &used) != 0) {
kmem_free(door_buf, door_bufsz);
return (NERR_InternalError);
}
arg.data_ptr = door_buf;
arg.data_size = used;
arg.desc_ptr = NULL;
arg.desc_num = 0;
arg.rbuf = door_buf;
arg.rsize = door_bufsz;
if (door_ki_upcall_limited(dhdl, &arg, NULL, SIZE_MAX, 0) != 0) {
kmem_free(door_buf, door_bufsz);
return (NERR_InternalError);
}
dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
kmem_free(door_buf, door_bufsz);
return (NERR_InternalError);
}
rc = smb_dr_get_uint32(dec_ctx);
if (rc == NERR_Success) {
enuminfo->es_ntotal = smb_dr_get_ushort(dec_ctx);
enuminfo->es_nsent = smb_dr_get_ushort(dec_ctx);
enuminfo->es_datasize = smb_dr_get_ushort(dec_ctx);
(void) smb_dr_get_buf(dec_ctx,
(unsigned char *)enuminfo->es_buf,
enuminfo->es_bufsize);
}
if (smb_dr_decode_finish(dec_ctx) != 0)
rc = NERR_InternalError;
kmem_free(door_buf, door_bufsz);
return (rc);
}
/*
* This is a special interface that will be utilized by ZFS to cause
* a share to be added/removed
*
* arg is either a smb_share_t or share_name from userspace.
* It will need to be copied into the kernel. It is smb_share_t
* for add operations and share_name for delete operations.
*/
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);
if (error = xcopyin(arg, lmshare, sizeof (smb_share_t))) {
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);
if (error = copyinstr(arg, str, MAXPATHLEN, NULL)) {
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);
}
/*
* Return 0 upon success. Otherwise > 0
*/
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);
}