/* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Kernel door client for LanMan share management. */ #include #include #include #include #include #include #include #include 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); } /* * Executes map and unmap command for shares. */ uint32_t smb_kshare_exec(door_handle_t dhdl, char *sharename, smb_execsub_info_t *subs, int exec_type) { door_arg_t arg; char *buf; int bufsz; unsigned int used; smb_dr_ctx_t *dec_ctx; smb_dr_ctx_t *enc_ctx; uint32_t rc; int opcode = SMB_SHROP_EXEC; bufsz = (2 * sizeof (int)) + strlen(sharename) + strlen(subs->e_winname) + strlen(subs->e_userdom) + strlen(subs->e_cli_netbiosname) + (2 * sizeof (smb_inaddr_t)) + sizeof (uid_t) + sizeof (smb_execsub_info_t); buf = kmem_alloc(bufsz, KM_SLEEP); enc_ctx = smb_dr_encode_start(buf, bufsz); smb_dr_put_uint32(enc_ctx, opcode); smb_dr_put_string(enc_ctx, sharename); smb_dr_put_string(enc_ctx, subs->e_winname); smb_dr_put_string(enc_ctx, subs->e_userdom); smb_dr_put_buf(enc_ctx, (uchar_t *)&subs->e_srv_ipaddr, sizeof (smb_inaddr_t)); smb_dr_put_buf(enc_ctx, (uchar_t *)&subs->e_cli_ipaddr, sizeof (smb_inaddr_t)); smb_dr_put_string(enc_ctx, subs->e_cli_netbiosname); smb_dr_put_int32(enc_ctx, subs->e_uid); smb_dr_put_int32(enc_ctx, exec_type); if (smb_dr_encode_finish(enc_ctx, &used) != 0) { kmem_free(buf, bufsz); return (NERR_InternalError); } arg.data_ptr = buf; arg.data_size = used; arg.desc_ptr = NULL; arg.desc_num = 0; arg.rbuf = buf; arg.rsize = bufsz; if (door_ki_upcall_limited(dhdl, &arg, NULL, SIZE_MAX, 0) != 0) { kmem_free(buf, 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(buf, bufsz); return (NERR_InternalError); } rc = smb_dr_get_uint32(dec_ctx); if (smb_dr_decode_finish(dec_ctx) != 0) rc = NERR_InternalError; kmem_free(buf, 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); }