diff options
Diffstat (limited to 'usr/src/uts')
77 files changed, 9556 insertions, 4775 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 8b236155fb..1ddfacb0cd 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1301,11 +1301,13 @@ VSCAN_OBJS += vscan_drv.o vscan_svc.o vscan_door.o NSMB_OBJS += smb_conn.o smb_dev.o smb_iod.o smb_pass.o \ smb_rq.o smb_sign.o smb_smb.o smb_subrs.o \ smb_time.o smb_tran.o smb_trantcp.o smb_usr.o \ - subr_mchain.o + smb2_rq.o smb2_sign.o smb2_smb.o subr_mchain.o \ + nsmb_sign_kcf.o SMBFS_COMMON_OBJS += smbfs_ntacl.o SMBFS_OBJS += smbfs_vfsops.o smbfs_vnops.o smbfs_node.o \ smbfs_acl.o smbfs_client.o smbfs_smb.o \ + smbfs_smb1.o smbfs_smb2.o \ smbfs_subr.o smbfs_subr2.o \ smbfs_rwlock.o smbfs_xattr.o \ $(SMBFS_COMMON_OBJS) diff --git a/usr/src/uts/common/fs/fs_subr.h b/usr/src/uts/common/fs/fs_subr.h index d25bf0213f..877dc36f9c 100644 --- a/usr/src/uts/common/fs/fs_subr.h +++ b/usr/src/uts/common/fs/fs_subr.h @@ -49,7 +49,7 @@ extern "C" { * Utilities shared among file system implementations. */ -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) extern int fs_nosys(); extern int fs_inval(); diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.apple b/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.apple new file mode 100644 index 0000000000..a42fec94d8 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.apple @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2001 - 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.apple.descrip b/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.apple.descrip new file mode 100644 index 0000000000..96b9771514 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.apple.descrip @@ -0,0 +1 @@ +PORTIONS OF NSMB DRIVER IN SMB CLIENT diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.boris_popov b/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.boris_popov new file mode 100644 index 0000000000..583923c355 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.boris_popov @@ -0,0 +1,29 @@ + Copyright (c) 2000, 2001 Boris Popov + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by Boris Popov. + 4. Neither the name of the author nor the names of any co-contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.boris_popov.descrip b/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.boris_popov.descrip new file mode 100644 index 0000000000..96b9771514 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/THIRDPARTYLICENSE.boris_popov.descrip @@ -0,0 +1 @@ +PORTIONS OF NSMB DRIVER IN SMB CLIENT diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/nsmb_sign_kcf.c b/usr/src/uts/common/fs/smbclnt/netsmb/nsmb_sign_kcf.c new file mode 100644 index 0000000000..2be033a8fc --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/nsmb_sign_kcf.c @@ -0,0 +1,180 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Helper functions for SMB signing using the + * Kernel Cryptographic Framework (KCF) + * + * There are two implementations of these functions: + * This one (for kernel) and another for user space: + * See: lib/smbclnt/libfknsmb/common/fksmb_sign_pkcs.c + */ + +#include <sys/types.h> +#include <sys/kmem.h> +#include <sys/sunddi.h> +#include <sys/crypto/api.h> +#include <netsmb/smb_signing.h> + +/* + * SMB1 signing helpers: + * (getmech, init, update, final) + */ + +int +smb_md5_getmech(smb_sign_mech_t *mech) +{ + crypto_mech_type_t t; + + t = crypto_mech2id(SUN_CKM_MD5); + if (t == CRYPTO_MECH_INVALID) + return (-1); + mech->cm_type = t; + return (0); +} + +/* + * Start the KCF session, load the key + */ +int +smb_md5_init(smb_sign_ctx_t *ctxp, smb_sign_mech_t *mech) +{ + int rv; + + rv = crypto_digest_init(mech, ctxp, NULL); + + return (rv == CRYPTO_SUCCESS ? 0 : -1); +} + +/* + * Digest one segment + */ +int +smb_md5_update(smb_sign_ctx_t ctx, void *buf, size_t len) +{ + crypto_data_t data; + int rv; + + bzero(&data, sizeof (data)); + data.cd_format = CRYPTO_DATA_RAW; + data.cd_length = len; + data.cd_raw.iov_base = buf; + data.cd_raw.iov_len = len; + + rv = crypto_digest_update(ctx, &data, 0); + + return (rv == CRYPTO_SUCCESS ? 0 : -1); +} + +/* + * Get the final digest. + */ +int +smb_md5_final(smb_sign_ctx_t ctx, uint8_t *digest16) +{ + crypto_data_t out; + int rv; + + bzero(&out, sizeof (out)); + out.cd_format = CRYPTO_DATA_RAW; + out.cd_length = MD5_DIGEST_LENGTH; + out.cd_raw.iov_len = MD5_DIGEST_LENGTH; + out.cd_raw.iov_base = (void *)digest16; + + rv = crypto_digest_final(ctx, &out, 0); + + return (rv == CRYPTO_SUCCESS ? 0 : -1); +} + +/* + * SMB2 signing helpers: + * (getmech, init, update, final) + */ + +int +smb2_hmac_getmech(smb_sign_mech_t *mech) +{ + crypto_mech_type_t t; + + t = crypto_mech2id(SUN_CKM_SHA256_HMAC); + if (t == CRYPTO_MECH_INVALID) + return (-1); + mech->cm_type = t; + return (0); +} + +/* + * Start the KCF session, load the key + */ +int +smb2_hmac_init(smb_sign_ctx_t *ctxp, smb_sign_mech_t *mech, + uint8_t *key, size_t key_len) +{ + crypto_key_t ckey; + int rv; + + bzero(&ckey, sizeof (ckey)); + ckey.ck_format = CRYPTO_KEY_RAW; + ckey.ck_data = key; + ckey.ck_length = key_len * 8; /* in bits */ + + rv = crypto_mac_init(mech, &ckey, NULL, ctxp, NULL); + + return (rv == CRYPTO_SUCCESS ? 0 : -1); +} + +/* + * Digest one segment + */ +int +smb2_hmac_update(smb_sign_ctx_t ctx, uint8_t *in, size_t len) +{ + crypto_data_t data; + int rv; + + bzero(&data, sizeof (data)); + data.cd_format = CRYPTO_DATA_RAW; + data.cd_length = len; + data.cd_raw.iov_base = (void *)in; + data.cd_raw.iov_len = len; + + rv = crypto_mac_update(ctx, &data, 0); + + return (rv == CRYPTO_SUCCESS ? 0 : -1); +} + +/* + * Note, the SMB2 signature is the first 16 bytes of the + * 32-byte SHA256 HMAC digest. + */ +int +smb2_hmac_final(smb_sign_ctx_t ctx, uint8_t *digest16) +{ + uint8_t full_digest[SHA256_DIGEST_LENGTH]; + crypto_data_t out; + int rv; + + bzero(&out, sizeof (out)); + out.cd_format = CRYPTO_DATA_RAW; + out.cd_length = SHA256_DIGEST_LENGTH; + out.cd_raw.iov_len = SHA256_DIGEST_LENGTH; + out.cd_raw.iov_base = (void *)full_digest; + + rv = crypto_mac_final(ctx, &out, 0); + if (rv == CRYPTO_SUCCESS) + bcopy(full_digest, digest16, 16); + + return (rv == CRYPTO_SUCCESS ? 0 : -1); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in b/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in index 993bdd61a8..a78b08bba0 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in +++ b/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in @@ -20,7 +20,7 @@ \ \ -\ Copyright 2011 Nexenta Systems, Inc. All rights reserved. +\ Copyright 2018 Nexenta Systems, Inc. All rights reserved. \ Copyright 2009 Sun Microsystems, Inc. All rights reserved. \ Use is subject to license terms. \ @@ -52,8 +52,10 @@ smbioc_ssn_ident id_user smbioc_ossn - ssn_vopt ssn_owner + ssn_vopt + ssn_minver + ssn_maxver ssn_id ssn_srvname @@ -68,77 +70,30 @@ smbioc_tcon tc_opt tc_sh -smb_sopt - sv_proto - sv_sm - sv_tz - sv_maxmux - sv_maxvcs - sv_rawmode - sv_maxtx - sv_maxraw - sv_skey - sv_caps - -smb_iods - is_tran_fd - is_vcflags - is_hflags - is_hflags2 - is_smbuid - is_next_mid - is_txmax - is_rwmax - is_rxmax - is_wxmax - is_ssn_key - is_next_seq - is_u_maclen - is_u_mackey - smbioc_ssn_work - wk_iods - wk_sopt wk_out_state - -smbioc_rq SIZEOF_SMBIOC_RQ - ioc_cmd - ioc_errclass IOC_RQ_ERRCLASS - ioc_serror IOC_RQ_SERROR - ioc_error IOC_RQ_ERROR - ioc_tbufsz - ioc_rbufsz - _ioc_tbuf - _ioc_rbuf - -smbioc_t2rq SIZEOF_SMBIOC_T2RQ - ioc_setup - ioc_setupcnt - ioc_name IOC_T2_NAME - ioc_tparamcnt - ioc_tdatacnt - ioc_rparamcnt - ioc_rdatacnt - ioc_errclass IOC_T2_ERRCLASS - ioc_serror IOC_T2_SERROR - ioc_error IOC_T2_ERROR - ioc_rpflags2 - _ioc_tparam - _ioc_tdata - _ioc_rparam - _ioc_rdata - -smbioc_flags SIZEOF_SMBIOC_FLAGS - ioc_level - ioc_mask - ioc_flags + wk_u_ssnkey_len + wk_u_ssnkey_buf + wk_u_auth_rlen + wk_u_auth_wlen + wk_u_auth_rbuf + wk_u_auth_wbuf + wk_cl_guid smbioc_rw SIZEOF_SMBIOC_RW - ioc_fh ioc_cnt + ioc_flags _ioc_offset _ioc_base +smbioc_xnp SIZEOF_SMBIOC_XNP + ioc_tdlen + ioc_rdlen + ioc_more + ioc_pad1 + _ioc_tdata + _ioc_rdata + smbioc_ntcreate SIZEOF_NTCREATE ioc_req_acc ioc_efattr diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb2_rq.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb2_rq.c new file mode 100644 index 0000000000..44041ad975 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb2_rq.c @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/kmem.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <sys/sunddi.h> +#include <sys/cmn_err.h> +#include <sys/atomic.h> +#include <sys/sdt.h> + +#include <netsmb/smb_osdep.h> + +#include <netsmb/smb.h> +#include <netsmb/smb2.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_tran.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb2_rq.h> + +static const uint8_t SMB2_SIGNATURE[4] = SMB2_PROTOCOL_ID; + +static int smb2_rq_enqueue(struct smb_rq *rqp); +static int smb2_rq_reply(struct smb_rq *rqp); + +/* + * Given a request with it's body already composed, + * rewind to the start and fill in the SMB2 header. + * This is called when the request is enqueued, + * so we have the final message ID etc. + */ +void +smb2_rq_fillhdr(struct smb_rq *rqp) +{ + struct mbchain mbtmp, *mbp = &mbtmp; + uint16_t creditcharge, creditrequest; + size_t len; + mblk_t *m; + + ASSERT((rqp->sr2_nextcmd & 7) == 0); + if (rqp->sr2_nextcmd != 0) { + len = msgdsize(rqp->sr_rq.mb_top); + ASSERT((len & 7) == 0); + } + + /* + * When sending negotiate, we don't technically know yet + * if the server handles SMB 2.1 or later and credits. + * Negotiate is supposed to set these to zero. + */ + if (rqp->sr2_command == SMB2_NEGOTIATE) { + creditcharge = creditrequest = 0; + } else { + creditcharge = rqp->sr2_creditcharge; + creditrequest = rqp->sr2_creditsrequested; + } + + /* + * Fill in the SMB2 header using a dup of the first mblk, + * which points at the same data but has its own wptr, + * so we can rewind without trashing the message. + */ + m = dupb(rqp->sr_rq.mb_top); + m->b_wptr = m->b_rptr; /* rewind */ + mb_initm(mbp, m); + + mb_put_mem(mbp, SMB2_SIGNATURE, 4, MB_MSYSTEM); + mb_put_uint16le(mbp, SMB2_HDR_SIZE); /* Struct Size */ + mb_put_uint16le(mbp, creditcharge); + mb_put_uint32le(mbp, 0); /* Status */ + mb_put_uint16le(mbp, rqp->sr2_command); + mb_put_uint16le(mbp, creditrequest); + mb_put_uint32le(mbp, rqp->sr2_rqflags); + mb_put_uint32le(mbp, rqp->sr2_nextcmd); + mb_put_uint64le(mbp, rqp->sr2_messageid); + + mb_put_uint32le(mbp, rqp->sr_pid); /* Process ID */ + mb_put_uint32le(mbp, rqp->sr2_rqtreeid); /* Tree ID */ + mb_put_uint64le(mbp, rqp->sr2_rqsessionid); /* Session ID */ + /* The MAC signature is filled in by smb2_vc_sign() */ + + /* This will free the mblk from dupb. */ + mb_done(mbp); +} + +int +smb2_rq_simple(struct smb_rq *rqp) +{ + return (smb2_rq_simple_timed(rqp, smb2_timo_default)); +} + +/* + * Simple request-reply exchange + */ +int +smb2_rq_simple_timed(struct smb_rq *rqp, int timeout) +{ + int error; + + rqp->sr_flags &= ~SMBR_RESTART; + rqp->sr_timo = timeout; /* in seconds */ + rqp->sr_state = SMBRQ_NOTSENT; + + error = smb2_rq_enqueue(rqp); + if (error == 0) + error = smb2_rq_reply(rqp); + + return (error); +} + + +static int +smb2_rq_enqueue(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct smb_share *ssp = rqp->sr_share; + int error = 0; + + ASSERT((vcp->vc_flags & SMBV_SMB2) != 0); + + /* + * Normal requests may initiate a reconnect, + * and/or wait for state changes to finish. + * Some requests set the NORECONNECT flag + * to avoid all that (i.e. tree discon) + */ + if (rqp->sr_flags & SMBR_NORECONNECT) { + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBSDEBUG("bad vc_state=%d\n", vcp->vc_state); + return (ENOTCONN); + } + if (ssp != NULL && + ((ssp->ss_flags & SMBS_CONNECTED) == 0)) + return (ENOTCONN); + goto ok_out; + } + + /* + * If we're not connected, initiate a reconnect + * and/or wait for an existing one to finish. + */ + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + error = smb_iod_reconnect(vcp); + if (error != 0) + return (error); + } + + /* + * If this request has a "share" object + * that needs a tree connect, do it now. + */ + if (ssp != NULL && (ssp->ss_flags & SMBS_CONNECTED) == 0) { + error = smb_share_tcon(ssp, rqp->sr_cred); + if (error) + return (error); + } + + /* + * We now know what UID + TID to use. + * Store them in the request. + */ +ok_out: + rqp->sr2_rqsessionid = vcp->vc2_session_id; + rqp->sr2_rqtreeid = ssp ? ssp->ss2_tree_id : SMB2_TID_UNKNOWN; + error = smb2_iod_addrq(rqp); + + return (error); +} + +/* + * Used by the IOD thread during connection setup, + * and for smb2_echo after network timeouts. Note that + * unlike smb2_rq_simple, callers must check sr_error. + */ +int +smb2_rq_internal(struct smb_rq *rqp, int timeout) +{ + struct smb_vc *vcp = rqp->sr_vc; + int error; + + ASSERT((vcp->vc_flags & SMBV_SMB2) != 0); + + rqp->sr_flags &= ~SMBR_RESTART; + rqp->sr_timo = timeout; /* in seconds */ + rqp->sr_state = SMBRQ_NOTSENT; + + /* + * In-line smb2_rq_enqueue(rqp) here, as we don't want it + * trying to reconnect etc. for an internal request. + */ + rqp->sr2_rqsessionid = vcp->vc2_session_id; + rqp->sr2_rqtreeid = SMB2_TID_UNKNOWN; + rqp->sr_flags |= SMBR_INTERNAL; + error = smb2_iod_addrq(rqp); + if (error != 0) + return (error); + + /* + * In-line a variant of smb2_rq_reply(rqp) here as we may + * need to do custom parsing for SMB1-to-SMB2 negotiate. + */ + if (rqp->sr_timo == SMBNOREPLYWAIT) { + smb_iod_removerq(rqp); + return (0); + } + + error = smb_iod_waitrq_int(rqp); + if (error) + return (error); + + /* + * If the request was signed, validate the + * signature on the response. + */ + if (rqp->sr2_rqflags & SMB2_FLAGS_SIGNED) { + error = smb2_rq_verify(rqp); + if (error) + return (error); + } + + /* + * Parse the SMB2 header. + */ + error = smb2_rq_parsehdr(rqp); + + /* + * Skip the error translation smb2_rq_reply does. + * Callers of this expect "raw" NT status. + */ + + return (error); +} + +/* + * Wait for a reply to this request, then parse it. + */ +static int +smb2_rq_reply(struct smb_rq *rqp) +{ + int error; + + if (rqp->sr_timo == SMBNOREPLYWAIT) { + smb_iod_removerq(rqp); + return (0); + } + + error = smb_iod_waitrq(rqp); + if (error) + return (error); + + /* + * If the request was signed, validate the + * signature on the response. + */ + if (rqp->sr2_rqflags & SMB2_FLAGS_SIGNED) { + error = smb2_rq_verify(rqp); + if (error) + return (error); + } + + /* + * Parse the SMB2 header + */ + error = smb2_rq_parsehdr(rqp); + if (error != 0) + return (error); + + if (rqp->sr_error != 0) { + error = smb_maperr32(rqp->sr_error); + } + + if (error != 0) { + /* + * Do a special check for STATUS_BUFFER_OVERFLOW; + * it's not an error. + */ + if (rqp->sr_error == NT_STATUS_BUFFER_OVERFLOW) { + /* + * Don't report it as an error to our caller; + * they can look at rqp->sr_error if they + * need to know whether we got a + * STATUS_BUFFER_OVERFLOW. + */ + rqp->sr_flags |= SMBR_MOREDATA; + error = 0; + } + } else { + rqp->sr_flags &= ~SMBR_MOREDATA; + } + + return (error); +} + +/* + * Parse the SMB 2+ Header + */ +int +smb2_rq_parsehdr(struct smb_rq *rqp) +{ + struct mdchain *mdp = &rqp->sr_rp; + uint32_t protocol_id; + uint16_t length = 0; + uint16_t credit_charge; + uint16_t command; + uint64_t message_id = 0; + int error = 0; + + /* Get Protocol ID */ + md_get_uint32le(mdp, &protocol_id); + + /* Get/Check structure size is 64 */ + md_get_uint16le(mdp, &length); + if (length != 64) + return (EBADRPC); + + md_get_uint16le(mdp, &credit_charge); + md_get_uint32le(mdp, &rqp->sr_error); + md_get_uint16le(mdp, &command); + md_get_uint16le(mdp, &rqp->sr2_rspcreditsgranted); + md_get_uint32le(mdp, &rqp->sr2_rspflags); + md_get_uint32le(mdp, &rqp->sr2_rspnextcmd); + md_get_uint64le(mdp, &message_id); + + if ((rqp->sr2_rspflags & SMB2_FLAGS_ASYNC_COMMAND) == 0) { + /* + * Sync Header + */ + + /* Get Process ID */ + md_get_uint32le(mdp, &rqp->sr2_rsppid); + + /* Get Tree ID */ + md_get_uint32le(mdp, &rqp->sr2_rsptreeid); + } else { + /* + * Async Header + */ + + /* Get Async ID */ + md_get_uint64le(mdp, &rqp->sr2_rspasyncid); + } + + /* Get Session ID */ + error = md_get_uint64le(mdp, &rqp->sr2_rspsessionid); + if (error) + return (error); + + /* Skip MAC Signature */ + error = md_get_mem(mdp, NULL, 16, MB_MSYSTEM); + + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb2_rq.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb2_rq.h new file mode 100644 index 0000000000..812c679b2f --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb2_rq.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 - 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _NETSMB_SMB2_RQ_H_ +#define _NETSMB_SMB2_RQ_H_ + +#include <sys/types.h> + +/* + * Note: Pad all structures to 8 byte boundaries + */ + +int smb2_rq_parsehdr(struct smb_rq *rqp); +void smb2_rq_fillhdr(struct smb_rq *rqp); + +int smb2_rq_simple(struct smb_rq *rqp); +int smb2_rq_simple_timed(struct smb_rq *rqp, int timeout); +int smb2_rq_internal(struct smb_rq *rqp, int timeout); + +#endif /* _NETSMB_SMB2_RQ_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb2_sign.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb2_sign.c new file mode 100644 index 0000000000..46bf28c370 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb2_sign.c @@ -0,0 +1,253 @@ +/* + * 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Support for SMB2 "signing" (message integrity) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/md4.h> +#include <sys/md5.h> +#include <sys/des.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/stream.h> +#include <sys/strsun.h> +#include <sys/sdt.h> + +#include <netsmb/smb_osdep.h> +#include <netsmb/smb2.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_signing.h> + +#define SMB2_SIG_OFF 48 +#define SMB2_SIG_LEN 16 + +/* + * smb2_sign_init + * + * Get the mechanism info and initilize SMB2 signing. + */ +int +smb2_sign_init(smb_vc_t *vcp) +{ + uint_t copysize; + int rc; + + ASSERT(vcp->vc_ssnkey != NULL); + ASSERT(vcp->vc_mackey == NULL); + + rc = smb2_hmac_getmech(&vcp->vc_signmech); + if (rc != 0) { + cmn_err(CE_NOTE, "smb2 can't get signing mechanism"); + return (EAUTH); + } + + /* + * Convert the session key to the MAC key. + * + * For SMB2, the signing key is just the first 16 bytes + * of the session key (truncated or padded with zeros). + * [MS-SMB2] 3.2.5.3.1 + * + * SMB3 would do KDF here. + */ + vcp->vc_mackeylen = SMB2_SIG_LEN; + vcp->vc_mackey = kmem_zalloc(vcp->vc_mackeylen, KM_SLEEP); + copysize = vcp->vc_ssnkeylen; + if (copysize > vcp->vc_mackeylen) + copysize = vcp->vc_mackeylen; + bcopy(vcp->vc_ssnkey, vcp->vc_mackey, copysize); + + return (0); +} + + +/* + * Compute MAC signature of packet data, using the stored MAC key. + * + * The signature is in the last 16 bytes of the SMB2 header. + * The signature algorighm is to compute HMAC SHA256 over the + * entire command, with the signature field set to zeros. + * + * See similar code for the server side: + * uts/common/fs/smbsrv/smb2_signing.c : smb2_sign_calc + */ +static int +smb2_compute_MAC(struct smb_vc *vcp, mblk_t *mp, uchar_t *signature) +{ + uint8_t tmp_hdr[SMB2_HDR_SIZE]; + smb_sign_ctx_t ctx = 0; + mblk_t *m = mp; + int size; + int rc; + + if (vcp->vc_mackey == NULL) + return (-1); + + rc = smb2_hmac_init(&ctx, &vcp->vc_signmech, + vcp->vc_mackey, vcp->vc_mackeylen); + if (rc != 0) + return (rc); + + /* Our caller should ensure mp has a contiguous header */ + ASSERT(m != NULL); + ASSERT(MBLKL(m) >= SMB2_HDRLEN); + + /* + * Copy of the SMB2 header, zero out the signature, and digest. + */ + size = SMB2_HDRLEN; + bcopy(m->b_rptr, tmp_hdr, size); + bzero(tmp_hdr + SMB2_SIG_OFF, SMB2_SIG_LEN); + rc = smb2_hmac_update(ctx, tmp_hdr, size); + if (rc != 0) + return (rc); + + /* + * Digest the rest of the SMB2 header packet, starting at + * the data just after the SMB2 header. + */ + size = MBLKL(m) - SMB2_HDRLEN; + rc = smb2_hmac_update(ctx, m->b_rptr + SMB2_HDRLEN, size); + if (rc != 0) + return (rc); + m = m->b_cont; + + /* Digest rest of the SMB2 message. */ + while (m != NULL) { + size = MBLKL(m); + if (size > 0) { + rc = smb2_hmac_update(ctx, m->b_rptr, size); + if (rc != 0) + return (rc); + } + m = m->b_cont; + } + rc = smb2_hmac_final(ctx, signature); + + return (rc); +} + +/* + * Sign a request with HMAC-MD5. + */ +void +smb2_rq_sign(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + mblk_t *mp = rqp->sr_rq.mb_top; + uint8_t *sigloc; + int rc; + + /* + * smb_rq_new() ensures this, + * but just in case.. + */ + ASSERT(MBLKL(mp) >= SMB2_HDRLEN); + sigloc = mp->b_rptr + SMB2_SIG_OFF; + + if (vcp->vc_mackey == NULL) + return; + + /* + * This will compute the MAC and store it + * directly into the message at sigloc. + */ + rc = smb2_compute_MAC(vcp, mp, sigloc); + if (rc != 0) { + SMBSDEBUG("Crypto error %d", rc); + bzero(sigloc, SMB2_SIG_LEN); + } +} + +/* + * Verify reply signature. + */ +int +smb2_rq_verify(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + mblk_t *mp = rqp->sr_rp.md_top; + uint8_t sigbuf[SMB2_SIG_LEN]; + uint8_t *sigloc; + int rc; + + /* + * Note vc_mackey and vc_mackeylen gets filled in by + * smb_usr_iod_work as the connection comes in. + */ + if (vcp->vc_mackey == NULL) { + SMBSDEBUG("no mac key\n"); + return (0); + } + + /* + * Let caller deal with empty reply or short messages by + * returning zero. Caller will fail later, in parsing. + */ + if (mp == NULL) { + SMBSDEBUG("empty reply\n"); + return (0); + } + + /* smb2_iod_process ensures this */ + ASSERT(MBLKL(mp) >= SMB2_HDRLEN); + sigloc = mp->b_rptr + SMB2_SIG_OFF; + + /* + * Compute the expected signature in sigbuf. + */ + rc = smb2_compute_MAC(vcp, mp, sigbuf); + if (rc != 0) { + SMBSDEBUG("Crypto error %d", rc); + /* + * If we can't compute a MAC, then there's + * no point trying other seqno values. + */ + return (EBADRPC); + } + + /* + * Compare the computed signature with the + * one found in the message (at sigloc) + */ + if (bcmp(sigbuf, sigloc, SMB2_SIG_LEN) == 0) + return (0); + + SMBERROR("BAD signature, Server=%s MID=0x%llx\n", + vcp->vc_srvname, (long long)rqp->sr2_messageid); + + return (EBADRPC); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb2_smb.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb2_smb.c new file mode 100644 index 0000000000..c3df18faa9 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb2_smb.c @@ -0,0 +1,1325 @@ +/* + * Copyright (c) 2011 - 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kmem.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/random.h> +#include <sys/note.h> +#include <sys/errno.h> +#include <sys/cmn_err.h> + +#include <smb/ntaccess.h> +#include <smb/winioctl.h> + +#include <netsmb/smb_osdep.h> + +#include <netsmb/smb.h> +#include <netsmb/smb2.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_tran.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb2_rq.h> + +#define NDIALECTS 1 +static const uint16_t smb2_dialects[1] = { + SMB2_DIALECT_0210 +}; + +/* Optional capabilities we advertise (none yet). */ +uint32_t smb2_clnt_caps = 0; + +/* How many credits to ask for during ssn. setup. */ +uint16_t smb2_ss_req_credits = 64; + +/* + * Default timeout values, all in seconds. + * Make these tunable (only via mdb for now). + */ +int smb2_timo_notice = 15; +int smb2_timo_default = 30; +int smb2_timo_logon = 45; +int smb2_timo_open = 45; +int smb2_timo_read = 45; +int smb2_timo_write = 60; +int smb2_timo_append = 90; + +/* + * This is a special handler for the odd SMB1-to-SMB2 negotiate + * response, where an SMB1 request gets an SMB2 response. + * + * Unlike most parse functions here, this needs to parse both + * the SMB2 header and the nego. response body. Note that + * the only "SMB2" dialect our SMB1 negotiate offered was + * { SMB_DIALECT_SMB2_FF, "SMB 2.???"} so the only valid + * SMB2 dialect we should get is: SMB2_DIALECT_02ff + */ +int +smb2_parse_smb1nego_resp(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct smb_sopt *sp = &vcp->vc_sopt; + struct mdchain *mdp; + uint16_t length = 0; + int error; + + /* Get pointer to response data */ + smb_rq_getreply(rqp, &mdp); + + error = smb2_rq_parsehdr(rqp); + if (error != 0) + return (error); + + /* + * Parse SMB 2/3 Negotiate Response + * We are already pointing to begining of Response data + */ + + /* Check structure size is 65 */ + md_get_uint16le(mdp, &length); + if (length != 65) + return (EBADRPC); + + /* Get Security Mode */ + md_get_uint16le(mdp, &sp->sv2_security_mode); + + /* Get Dialect. */ + error = md_get_uint16le(mdp, &sp->sv2_dialect); + if (error != 0) + return (error); + + /* What dialect did we get? */ + if (sp->sv2_dialect != SMB2_DIALECT_02ff) { + SMBERROR("Unknown dialect 0x%x\n", sp->sv2_dialect); + return (EINVAL); + } + /* Set our (internal) SMB1 dialect also. */ + sp->sv_proto = SMB_DIALECT_SMB2_FF; + + /* + * This request did not go through smb2_iod_addrq and + * smb2_iod_process() so the SMB2 message ID state is + * behind what we need it to be. Fix that. + */ + vcp->vc2_next_message_id = 1; + vcp->vc2_limit_message_id = 2; + + /* + * Skip parsing the rest. We'll get a normal + * SMB2 negotiate next and do negotiate then. + */ + return (0); +} + +int +smb2_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) +{ + smb_sopt_t *sp = &vcp->vc_sopt; + smbioc_ssn_work_t *wk = &vcp->vc_work; + struct smb_rq *rqp = NULL; + struct mbchain *mbp = NULL; + struct mdchain *mdp = NULL; + uint16_t ndialects = NDIALECTS; + boolean_t will_sign = B_FALSE; + uint16_t length = 0; + uint16_t security_mode; + uint16_t sec_buf_off; + uint16_t sec_buf_len; + int err, i; + + /* + * Compute security mode + */ + if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) { + security_mode = SMB2_NEGOTIATE_SIGNING_REQUIRED; + } else { + security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; + } + + err = smb_rq_alloc(VCTOCP(vcp), SMB2_NEGOTIATE, scred, &rqp); + if (err) + return (err); + + /* + * Build the SMB2 negotiate request. + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 36); /* Struct Size */ + mb_put_uint16le(mbp, ndialects); /* Dialect Count */ + mb_put_uint16le(mbp, security_mode); + mb_put_uint16le(mbp, 0); /* Reserved */ + mb_put_uint32le(mbp, smb2_clnt_caps); + mb_put_mem(mbp, vcp->vc_cl_guid, 16, MB_MSYSTEM); + mb_put_uint64le(mbp, 0); /* Start Time */ + for (i = 0; i < ndialects; i++) { /* Dialects */ + mb_put_uint16le(mbp, smb2_dialects[i]); + } + + /* + * Do the OTW call. + */ + err = smb2_rq_internal(rqp, smb2_timo_default); + if (err) { + goto errout; + } + /* Should only get status success. */ + if (rqp->sr_error != NT_STATUS_SUCCESS) { + err = ENOTSUP; + goto errout; + } + + /* + * Decode the negotiate response + */ + smb_rq_getreply(rqp, &mdp); + + md_get_uint16le(mdp, &length); /* Struct size */ + if (length != 65) { + err = EBADRPC; + goto errout; + } + + md_get_uint16le(mdp, &sp->sv2_security_mode); + md_get_uint16le(mdp, &sp->sv2_dialect); + md_get_uint16le(mdp, NULL); /* reserved */ + md_get_mem(mdp, sp->sv2_guid, 16, MB_MSYSTEM); + md_get_uint32le(mdp, &sp->sv2_capabilities); + md_get_uint32le(mdp, &sp->sv2_maxtransact); + md_get_uint32le(mdp, &sp->sv2_maxread); + md_get_uint32le(mdp, &sp->sv2_maxwrite); + md_get_uint64le(mdp, NULL); /* curr_time */ + md_get_uint64le(mdp, NULL); /* boot_time */ + + /* Get Security Blob offset and length */ + md_get_uint16le(mdp, &sec_buf_off); + err = md_get_uint16le(mdp, &sec_buf_len); + if (err != 0) + goto errout; + md_get_uint32le(mdp, NULL); /* reserved */ + + /* + * Security buffer offset is from the beginning of SMB 2 Header + * Calculate how much further we have to go to get to it. + * Current offset is: SMB2_HDRLEN + 64 + */ + if (sec_buf_len != 0) { + int skip = (int)sec_buf_off - (SMB2_HDRLEN + 64); + if (skip < 0) { + err = EBADRPC; + goto errout; + } + if (skip > 0) { + md_get_mem(mdp, NULL, skip, MB_MSYSTEM); + } + + /* + * Copy the security blob out to user space. + * Buffer addr,size in vc_auth_rbuf,rlen + */ + if (wk->wk_u_auth_rlen < sec_buf_len) { + SMBSDEBUG("vc_auth_rbuf too small"); + /* Give caller required size. */ + wk->wk_u_auth_rlen = sec_buf_len; + err = EMSGSIZE; + goto errout; + } + wk->wk_u_auth_rlen = sec_buf_len; + err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr, + sec_buf_len, MB_MUSER); + if (err) { + goto errout; + } + } + + /* + * Decoded everything. Now decisions. + */ + + /* + * Turn on signing if either Server or client requires it, + * except: anonymous sessions can't sign. + */ + if ((sp->sv2_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) || + (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED)) + will_sign = B_TRUE; + if (vcp->vc_vopt & SMBVOPT_ANONYMOUS) + will_sign = B_FALSE; + SMBSDEBUG("Security signatures: %d", (int)will_sign); + if (will_sign) + vcp->vc_flags |= SMBV_SIGNING; + + /* + * ToDo - too many places are looking at sv_caps, so for now + * set the SMB1 capabilities too. Later we should use the + * sv2_capabilities for SMB 2+. + */ + sp->sv_caps = (SMB_CAP_UNICODE | + SMB_CAP_LARGE_FILES | + SMB_CAP_STATUS32 | + SMB_CAP_LARGE_READX | + SMB_CAP_LARGE_WRITEX | + SMB_CAP_EXT_SECURITY); + if (sp->sv2_capabilities & SMB2_CAP_DFS) + sp->sv_caps |= SMB_CAP_DFS; + + /* + * A few sanity checks on what we received, + * becuse we will send these in ssnsetup. + * + * Maximum outstanding requests (we care), + * and Max. VCs (we only use one). Also, + * MaxBufferSize lower limit per spec. + */ + if (sp->sv2_maxread < 0x8000) { + SMBSDEBUG("maxread too small\n"); + err = ENOTSUP; + goto errout; + } + if (sp->sv2_maxwrite < 0x8000) { + SMBSDEBUG("maxwrite too small\n"); + err = ENOTSUP; + goto errout; + } + if (sp->sv2_maxtransact < 0x4000) { + SMBSDEBUG("maxtransact too small\n"); + err = ENOTSUP; + goto errout; + } + + /* Here too, fill SMB1 fields */ + vcp->vc_rxmax = sp->sv2_maxread; + vcp->vc_wxmax = sp->sv2_maxwrite; + vcp->vc_txmax = sp->sv2_maxtransact; + + smb_rq_done(rqp); + return (0); + +errout: + smb_rq_done(rqp); + if (err == 0) + err = EBADRPC; + return (err); +} + +int +smb2_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) +{ + // smb_sopt_t *sv = &vcp->vc_sopt; + smbioc_ssn_work_t *wk = &vcp->vc_work; + struct smb_rq *rqp = NULL; + struct mbchain *mbp = NULL; + struct mdchain *mdp = NULL; + char *sb; + int err, ret; + uint16_t sblen; + uint16_t length = 0; + uint16_t session_flags; + uint16_t sec_buf_off; + uint16_t sec_buf_len; + uint8_t security_mode; + + /* + * Compute security mode + */ + if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) { + security_mode = SMB2_NEGOTIATE_SIGNING_REQUIRED; + } else { + security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; + } + + sb = wk->wk_u_auth_wbuf.lp_ptr; + sblen = (uint16_t)wk->wk_u_auth_wlen; + + err = smb_rq_alloc(VCTOCP(vcp), SMB2_SESSION_SETUP, scred, &rqp); + if (err != 0) { + ret = err; + goto out; + } + + /* + * Always ask for some credits. The server usually will + * only grant these credits once we've authenticated. + */ + rqp->sr2_creditsrequested = smb2_ss_req_credits; + + /* + * Build the SMB Session Setup request. + */ + smb_rq_getrequest(rqp, &mbp); + + mb_put_uint16le(mbp, 25); /* Struct size */ + mb_put_uint8(mbp, 0); /* VcNumber */ + mb_put_uint8(mbp, security_mode); + mb_put_uint32le(mbp, smb2_clnt_caps); /* Capabilities */ + mb_put_uint32le(mbp, 0); /* Channel - always 0 */ + + /* + * Security buffer offset and length. Normally would use + * ptr = mb_reserve() and fill in later, but since only a + * small amount of fixed-size stuff follows (12 bytes) + * we can just compute the offset now. + */ + mb_put_uint16le(mbp, mbp->mb_count + 12); + mb_put_uint16le(mbp, sblen); + mb_put_uint64le(mbp, vcp->vc2_prev_session_id); + err = mb_put_mem(mbp, sb, sblen, MB_MUSER); + if (err != 0) { + ret = err; + goto out; + } + + /* + * Run the request. The return value here should be the + * return from this function, unless we fail decoding. + * Note: NT_STATUS_MORE_PROCESSING_REQUIRED is OK, and + * the caller expects EINPROGRESS for that case. + */ + ret = smb2_rq_internal(rqp, smb2_timo_logon); + if (ret != 0) + goto out; + switch (rqp->sr_error) { + case NT_STATUS_SUCCESS: + break; + case NT_STATUS_MORE_PROCESSING_REQUIRED: + /* Keep going, but return... */ + ret = EINPROGRESS; + break; + default: + ret = EAUTH; + goto out; + } + + /* + * After the first Session Setup Response, + * save the session ID. + */ + if (vcp->vc2_session_id == 0) + vcp->vc2_session_id = rqp->sr2_rspsessionid; + + /* + * Decode the session setup response + */ + smb_rq_getreply(rqp, &mdp); + + md_get_uint16le(mdp, &length); /* Struct size */ + if (length != 9) { + ret = EBADRPC; + goto out; + } + + md_get_uint16le(mdp, &session_flags); + md_get_uint16le(mdp, &sec_buf_off); + err = md_get_uint16le(mdp, &sec_buf_len); + if (err != 0) { + ret = err; + goto out; + } + + /* + * Security buffer offset is from the beginning of SMB 2 Header + * Calculate how much further we have to go to get to it. + * Current offset is: SMB2_HDRLEN + 8 + */ + if (sec_buf_len != 0) { + int skip = (int)sec_buf_off - (SMB2_HDRLEN + 8); + if (skip < 0) { + ret = EBADRPC; + goto out; + } + if (skip > 0) { + md_get_mem(mdp, NULL, skip, MB_MSYSTEM); + } + + /* + * Copy the security blob out to user space. + * Buffer addr,size in vc_auth_rbuf,rlen + */ + if (wk->wk_u_auth_rlen < sec_buf_len) { + SMBSDEBUG("vc_auth_rbuf too small"); + /* Give caller required size. */ + wk->wk_u_auth_rlen = sec_buf_len; + ret = EMSGSIZE; + goto out; + } + wk->wk_u_auth_rlen = sec_buf_len; + err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr, + sec_buf_len, MB_MUSER); + if (err != 0) { + ret = err; + goto out; + } + } + +out: + if (err != 0 && err != EINPROGRESS) { + /* Session ID no longer valid. */ + vcp->vc2_session_id = 0; + } + if (rqp) + smb_rq_done(rqp); + + return (ret); +} + +int +smb2_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + if (vcp->vc2_session_id == 0) + return (0); + + error = smb_rq_alloc(VCTOCP(vcp), SMB2_LOGOFF, scred, &rqp); + if (error) + return (error); + + /* + * Fill in Logoff part + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 4); /* Struct size */ + mb_put_uint16le(mbp, 0); /* Reserved */ + + /* + * Run this with a relatively short timeout. (5 sec.) + * We don't really care about the result here. + * Also, don't reconnect for this, of course! + */ + rqp->sr_flags |= SMBR_NORECONNECT; + error = smb2_rq_internal(rqp, 5); + smb_rq_done(rqp); + return (error); +} + +int +smb2_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) +{ + struct smb_vc *vcp; + struct smb_rq *rqp = NULL; + struct mbchain *mbp; + struct mdchain *mdp; + char *unc_name = NULL; + int error, unc_len; + uint16_t plen, *plenp; + uint16_t options = 0; + uint_t cnt0; + uint32_t net_stype; + uint16_t structure_size = 0; + uint8_t smb2stype; + + vcp = SSTOVC(ssp); + + /* + * Make this a "VC-level" request, so it will have + * rqp->sr_share == NULL, and smb_iod_sendrq() + * will send it with TID = SMB_TID_UNKNOWN + * + * This also serves to bypass the wait for + * share state changes, which this call is + * trying to carry out. + */ + error = smb_rq_alloc(VCTOCP(vcp), SMB2_TREE_CONNECT, scred, &rqp); + if (error) + return (error); + + /* + * Build the UNC name, i.e. "//server/share" + * but with backslashes of course. + * size math: three slashes, one null. + */ + unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name); + unc_name = kmem_alloc(unc_len, KM_SLEEP); + (void) snprintf(unc_name, unc_len, "\\\\%s\\%s", + vcp->vc_srvname, ssp->ss_name); + SMBSDEBUG("unc_name: \"%s\"", unc_name); + + /* + * Build the request. + */ + mbp = &rqp->sr_rq; + + mb_put_uint16le(mbp, 9); /* Struct size */ + mb_put_uint16le(mbp, 0); /* Reserved */ + mb_put_uint16le(mbp, 72); /* Path Offset */ + + /* + * Fill in path length after we put the string, so we know + * the length after conversion from UTF-8 to UCS-2. + */ + plenp = mb_reserve(mbp, 2); + cnt0 = mbp->mb_count; + + /* UNC resource name (without the null) */ + error = smb_put_dmem(mbp, vcp, unc_name, unc_len - 1, + SMB_CS_NONE, NULL); + if (error) + goto out; + + /* Now go back and fill in the path length. */ + plen = (uint16_t)(mbp->mb_count - cnt0); + *plenp = htoles(plen); + + /* + * Run the request. + * + * Using NOINTR_RECV because we don't want to risk + * missing a successful tree connect response, + * which would "leak" Tree IDs. + */ + rqp->sr_flags |= SMBR_NOINTR_RECV; + error = smb2_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) { + /* + * If we get the server name wrong, i.e. due to + * mis-configured name services, this will be + * NT_STATUS_DUPLICATE_NAME. Log this error. + */ + SMBERROR("(%s) failed, status=0x%x", + unc_name, rqp->sr_error); + goto out; + } + + /* + * Parse the tree connect response + */ + smb_rq_getreply(rqp, &mdp); + + /* Check structure size is 16 */ + md_get_uint16le(mdp, &structure_size); + if (structure_size != 16) { + error = EBADRPC; + goto out; + } + + md_get_uint8(mdp, &smb2stype); + md_get_uint8(mdp, NULL); /* reserved */ + md_get_uint32le(mdp, &ssp->ss2_share_flags); + md_get_uint32le(mdp, &ssp->ss2_share_caps); + error = md_get_uint32le(mdp, NULL); /* maxAccessRights */ + if (error) + goto out; + + /* + * Convert SMB2 share type to NetShareEnum share type + */ + switch (smb2stype) { + case SMB2_SHARE_TYPE_DISK: + net_stype = STYPE_DISKTREE; + break; + case SMB2_SHARE_TYPE_PIPE: + net_stype = STYPE_IPC; + break; + case SMB2_SHARE_TYPE_PRINT: + net_stype = STYPE_PRINTQ; + break; + default: + net_stype = STYPE_UNKNOWN; + break; + } + ssp->ss_type = net_stype; + + /* + * Map SMB 2/3 capabilities to SMB 1 options, + * for common code that looks there. + */ + if (ssp->ss2_share_caps & SMB2_SHARE_CAP_DFS) + options |= SMB_SHARE_IS_IN_DFS; + + /* Update share state */ + SMB_SS_LOCK(ssp); + ssp->ss2_tree_id = rqp->sr2_rsptreeid; + ssp->ss_vcgenid = vcp->vc_genid; + ssp->ss_options = options; + ssp->ss_flags |= SMBS_CONNECTED; + SMB_SS_UNLOCK(ssp); + +out: + if (unc_name) + kmem_free(unc_name, unc_len); + smb_rq_done(rqp); + return (error); +} + +int +smb2_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) +{ + struct smb_vc *vcp; + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + if (ssp->ss2_tree_id == SMB2_TID_UNKNOWN) + return (0); + + /* + * Build this as a "VC-level" request, so it will + * avoid testing the _GONE flag on the share, + * which has already been set at this point. + * Add the share pointer "by hand" below, so + * smb_iod_sendrq will plug in the TID. + */ + vcp = SSTOVC(ssp); + error = smb_rq_alloc(VCTOCP(vcp), SMB2_TREE_DISCONNECT, scred, &rqp); + if (error) + return (error); + rqp->sr_share = ssp; /* See "by hand" above. */ + + /* + * Fill in SMB2 Tree Disconnect part + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 4); /* Struct size */ + mb_put_uint16le(mbp, 0); /* Reserved */ + + /* + * Run this with a relatively short timeout. (5 sec.) + * We don't really care about the result here, but we + * do need to make sure we send this out, or we could + * "leak" active tree IDs on interrupt or timeout. + * The NOINTR_SEND flag makes this request immune to + * interrupt or timeout until the send is done. + * Also, don't reconnect for this, of course! + */ + rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT); + error = smb2_rq_simple_timed(rqp, 5); + + smb_rq_done(rqp); + + /* Whether we get an error or not... */ + ssp->ss2_tree_id = SMB2_TID_UNKNOWN; + + return (error); +} + +/* + * Put the name, first skipping a leading slash. + */ +static int +put_name_skip_slash(struct mbchain *mbp, struct mbchain *name_mbp) +{ + mblk_t *m; + + if (name_mbp == NULL) + return (0); + m = name_mbp->mb_top; + if (m == NULL) + return (0); + + /* Use a dup of the message to leave the passed one untouched. */ + m = dupmsg(m); + if (m == NULL) + return (ENOSR); + + if (MBLKL(m) >= 2 && + m->b_rptr[0] == '\\' && + m->b_rptr[1] == '\0') + m->b_rptr += 2; + + return (mb_put_mbuf(mbp, m)); +} + +/* + * Modern create/open of file or directory. + * + * The passed name is a full path relative to the share root. + * Callers prepare paths with a leading slash (backslash) + * because that's what SMB1 expected. SMB2 does not allow the + * leading slash here. To make life simpler for callers skip a + * leading slash here. That allows callers use use common logic + * for building paths without needing to know if the connection + * is using SMB1 or SMB2 (just build paths with a leading slash). + */ +int +smb2_smb_ntcreate( + struct smb_share *ssp, + struct mbchain *name_mb, + struct mbchain *cctx_in, + struct mdchain *cctx_out, + uint32_t cr_flags, /* create flags */ + uint32_t req_acc, /* requested access */ + uint32_t efa, /* ext. file attrs (DOS attr +) */ + uint32_t share_acc, + uint32_t open_disp, /* open disposition */ + uint32_t createopt, /* NTCREATEX_OPTIONS_ */ + uint32_t impersonate, /* NTCREATEX_IMPERSONATION_... */ + struct smb_cred *scrp, + smb2fid_t *fidp, /* returned FID */ + uint32_t *cr_act_p, /* optional create action */ + struct smbfattr *fap) /* optional attributes */ +{ + struct smbfattr fa; + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + uint16_t *name_offp; + uint16_t *name_lenp; + uint32_t *cctx_offp; + uint32_t *cctx_lenp; + uint32_t rcc_off, rcc_len; + smb2fid_t smb2_fid; + uint64_t llongint; + uint32_t longint, createact; + uint_t off, len; + int error; + uint16_t StructSize = 57; // [MS-SMB2] + + bzero(&fa, sizeof (fa)); + + error = smb_rq_alloc(SSTOCP(ssp), SMB2_CREATE, scrp, &rqp); + if (error) + return (error); + + /* + * Todo: Assemble creat contexts (if needed) + * into an mbchain. + */ + + /* + * Build the SMB 2/3 Create Request + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, StructSize); + mb_put_uint8(mbp, 0); /* Security flags */ + mb_put_uint8(mbp, SMB2_OPLOCK_LEVEL_NONE); /* Oplock level */ + mb_put_uint32le(mbp, impersonate); /* Impersonation Level */ + mb_put_uint64le(mbp, cr_flags); + mb_put_uint64le(mbp, 0); /* Reserved */ + mb_put_uint32le(mbp, req_acc); + mb_put_uint32le(mbp, efa); /* File attributes */ + mb_put_uint32le(mbp, share_acc); /* Share access */ + mb_put_uint32le(mbp, open_disp); /* Create disposition */ + mb_put_uint32le(mbp, createopt); /* Create options */ + + name_offp = mb_reserve(mbp, 2); /* Name offset */ + name_lenp = mb_reserve(mbp, 2); /* Name len */ + + cctx_offp = mb_reserve(mbp, 4); /* Context offset */ + cctx_lenp = mb_reserve(mbp, 4); /* Context len */ + + /* + * Put the file name, which is provided in an mbchain. + * If there's a leading slash, skip it (see above). + */ + off = mbp->mb_count; + *name_offp = htoles((uint16_t)off); + error = put_name_skip_slash(mbp, name_mb); + if (error) + goto out; + len = mbp->mb_count - off; + *name_lenp = htoles((uint16_t)len); + + /* + * Now the create contexts (if provided) + */ + if (cctx_in != NULL) { + off = mbp->mb_count; + *cctx_offp = htolel((uint32_t)off); + mb_put_mbchain(mbp, cctx_in); + len = mbp->mb_count - off; + *cctx_lenp = htolel((uint32_t)len); + } else { + *cctx_offp = 0; + *cctx_lenp = 0; + } + + /* + * If we didn't put any variable-sized data, we'll have + * put exactly 56 bytes of data, and we need to pad out + * this request to the 57 bytes StructSize indicated. + */ + if (mbp->mb_count < (StructSize + SMB2_HDRLEN)) + mb_put_uint8(mbp, 0); + + /* + * Don't want to risk missing a successful + * open response, or we could "leak" FIDs. + */ + rqp->sr_flags |= SMBR_NOINTR_RECV; + error = smb2_rq_simple_timed(rqp, smb2_timo_open); + if (error) + goto out; + + /* + * Parse SMB 2/3 Create Response + */ + smb_rq_getreply(rqp, &mdp); + + /* Check structure size is 89 */ + error = md_get_uint16le(mdp, &StructSize); + if (StructSize != 89) { + error = EBADRPC; + goto out; + } + + md_get_uint8(mdp, NULL); /* oplock lvl granted */ + md_get_uint8(mdp, NULL); /* mbz */ + md_get_uint32le(mdp, &createact); /* create_action */ + md_get_uint64le(mdp, &llongint); /* creation time */ + smb_time_NT2local(llongint, &fa.fa_createtime); + md_get_uint64le(mdp, &llongint); /* access time */ + smb_time_NT2local(llongint, &fa.fa_atime); + md_get_uint64le(mdp, &llongint); /* write time */ + smb_time_NT2local(llongint, &fa.fa_mtime); + md_get_uint64le(mdp, &llongint); /* change time */ + smb_time_NT2local(llongint, &fa.fa_ctime); + md_get_uint64le(mdp, &llongint); /* allocation size */ + fa.fa_allocsz = llongint; + md_get_uint64le(mdp, &llongint); /* EOF position */ + fa.fa_size = llongint; + md_get_uint32le(mdp, &longint); /* attributes */ + fa.fa_attr = longint; + md_get_uint32le(mdp, NULL); /* reserved */ + + /* Get SMB 2/3 File ID and create user fid to return */ + md_get_uint64le(mdp, &smb2_fid.fid_persistent); + error = md_get_uint64le(mdp, &smb2_fid.fid_volatile); + if (error) + goto out; + + /* Get Context Offset */ + error = md_get_uint32le(mdp, &rcc_off); + if (error) + goto out; + /* Get Context Length */ + error = md_get_uint32le(mdp, &rcc_len); + if (error) + goto out; + + /* + * If the caller wants the returned create contexts, parse. + * Context offset is from the beginning of SMB 2/3 Header + * Calculate how much further we have to go to get to it. + * Current offset is: SMB2_HDRLEN + 88 + */ + if (rcc_len != 0) { + int skip = (int)rcc_off - (SMB2_HDRLEN + 88); + if (skip < 0) { + error = EBADRPC; + goto out; + } + if (skip > 0) { + md_get_mem(mdp, NULL, skip, MB_MSYSTEM); + } + if (cctx_out != NULL) { + mblk_t *m = NULL; + error = md_get_mbuf(mdp, rcc_len, &m); + if (error) + goto out; + md_initm(cctx_out, m); + } + } + +out: + smb_rq_done(rqp); + if (error) + return (error); + + *fidp = smb2_fid; + if (cr_act_p) + *cr_act_p = createact; + if (fap) + *fap = fa; /* struct copy */ + + return (0); +} + +int +smb2_smb_close(struct smb_share *ssp, smb2fid_t *fid, struct smb_cred *scrp) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + error = smb_rq_alloc(SSTOCP(ssp), SMB2_CLOSE, scrp, &rqp); + if (error) + return (error); + + /* + * Build the SMB 2/3 Close Request + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 24); /* Struct size */ + mb_put_uint16le(mbp, 0); /* Flags */ + mb_put_uint32le(mbp, 0); /* Reserved */ + + mb_put_uint64le(mbp, fid->fid_persistent); + mb_put_uint64le(mbp, fid->fid_volatile); + + /* Make sure we send, but only if already connected */ + rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT); + error = smb2_rq_simple(rqp); + smb_rq_done(rqp); + return (error); +} + +int +smb2_smb_ioctl( + struct smb_share *ssp, + smb2fid_t *fid, + struct mbchain *data_in, + struct mdchain *data_out, + uint32_t *data_out_sz, /* max / returned */ + uint32_t ctl_code, + struct smb_cred *scrp) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + uint32_t *data_in_offp; + uint32_t *data_in_lenp; + uint32_t data_out_off; + uint32_t data_out_len; + uint16_t length = 0; + uint_t off, len; + int error; + + error = smb_rq_alloc(SSTOCP(ssp), SMB2_IOCTL, scrp, &rqp); + if (error) + return (error); + + /* + * Build the SMB 2 IOCTL Request + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 57); /* Struct size */ + mb_put_uint16le(mbp, 0); /* Reserved */ + mb_put_uint32le(mbp, ctl_code); + + mb_put_uint64le(mbp, fid->fid_persistent); + mb_put_uint64le(mbp, fid->fid_volatile); + + data_in_offp = mb_reserve(mbp, 4); + data_in_lenp = mb_reserve(mbp, 4); + mb_put_uint32le(mbp, 0); /* Max input resp */ + + mb_put_uint32le(mbp, 0); /* Output offset */ + mb_put_uint32le(mbp, 0); /* Output count */ + mb_put_uint32le(mbp, *data_out_sz); + + mb_put_uint32le(mbp, SMB2_IOCTL_IS_FSCTL); /* Flags */ + mb_put_uint32le(mbp, 0); /* Reserved2 */ + + /* + * Now data_in (if provided) + */ + if (data_in != NULL) { + off = mbp->mb_count; + *data_in_offp = htolel((uint32_t)off); + mb_put_mbchain(mbp, data_in); + len = mbp->mb_count - off; + *data_in_lenp = htolel((uint32_t)len); + } else { + *data_in_offp = 0; + *data_in_lenp = 0; + } + + /* + * Run the request + */ + error = smb2_rq_simple_timed(rqp, smb2_timo_default); + if (error) + goto out; + + /* + * Parse SMB 2 Ioctl Response + */ + smb_rq_getreply(rqp, &mdp); + + /* Check structure size is 49 */ + md_get_uint16le(mdp, &length); + if (length != 49) { + error = EBADRPC; + goto out; + } + md_get_uint16le(mdp, NULL); /* reserved */ + md_get_uint32le(mdp, NULL); /* Get CtlCode */ + md_get_uint64le(mdp, NULL); /* fid_persistent */ + md_get_uint64le(mdp, NULL); /* fid_volatile */ + md_get_uint32le(mdp, NULL); /* Get Input offset */ + md_get_uint32le(mdp, NULL); /* Get Input count */ + + error = md_get_uint32le(mdp, &data_out_off); + if (error) + goto out; + error = md_get_uint32le(mdp, &data_out_len); + if (error) + goto out; + + md_get_uint32le(mdp, NULL); /* Flags */ + md_get_uint32le(mdp, NULL); /* reserved */ + + /* + * If the caller wants the ioctl output data, parse. + * Current offset is: SMB2_HDRLEN + 48 + * Always return the received length. + */ + *data_out_sz = data_out_len; + if (data_out_len != 0) { + int skip = (int)data_out_off - (SMB2_HDRLEN + 48); + if (skip < 0) { + error = EBADRPC; + goto out; + } + if (skip > 0) { + md_get_mem(mdp, NULL, skip, MB_MSYSTEM); + } + if (data_out != NULL) { + mblk_t *m = NULL; + error = md_get_mbuf(mdp, data_out_len, &m); + if (error) + goto out; + md_initm(data_out, m); + } + } + +out: + smb_rq_done(rqp); + + return (error); +} + +int +smb2_smb_read(smb_fh_t *fhp, uint32_t *lenp, + uio_t *uiop, smb_cred_t *scred, int timo) +{ + struct smb_share *ssp = FHTOSS(fhp); + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + int error; + uint64_t off64 = uiop->uio_loffset; + uint32_t rlen; + uint16_t length = 0; + uint8_t data_offset; + + error = smb_rq_alloc(SSTOCP(ssp), SMB2_READ, scred, &rqp); + if (error) + return (error); + + /* + * Build the SMB 2 Read Request + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 49); /* Struct size */ + mb_put_uint16le(mbp, 0); /* Padding and Reserved */ + + mb_put_uint32le(mbp, *lenp); /* Length of read */ + mb_put_uint64le(mbp, off64); /* Offset */ + + mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent); + mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile); + + mb_put_uint32le(mbp, 1); /* MinCount */ + /* (only indicates blocking) */ + + mb_put_uint32le(mbp, 0); /* Channel */ + mb_put_uint32le(mbp, 0); /* Remaining */ + mb_put_uint32le(mbp, 0); /* Channel offset/len */ + mb_put_uint8(mbp, 0); /* data "blob" (pad) */ + + if (timo == 0) + timo = smb2_timo_read; + error = smb2_rq_simple_timed(rqp, timo); + if (error) + goto out; + + /* + * Parse SMB 2 Read Response + */ + smb_rq_getreply(rqp, &mdp); + + /* Check structure size is 17 */ + md_get_uint16le(mdp, &length); + if (length != 17) { + error = EBADRPC; + goto out; + } + md_get_uint8(mdp, &data_offset); + md_get_uint8(mdp, NULL); /* reserved */ + + /* Get Data Length read */ + error = md_get_uint32le(mdp, &rlen); + if (error) + goto out; + + md_get_uint32le(mdp, NULL); /* Data Remaining (always 0) */ + md_get_uint32le(mdp, NULL); /* Get Reserved2 (always 0) */ + + /* + * Data offset is from the beginning of SMB 2/3 Header + * Calculate how much further we have to go to get to it. + */ + if (data_offset < (SMB2_HDRLEN + 16)) { + error = EBADRPC; + goto out; + } + if (data_offset > (SMB2_HDRLEN + 16)) { + int skip = data_offset - (SMB2_HDRLEN + 16); + md_get_mem(mdp, NULL, skip, MB_MSYSTEM); + } + + /* + * Get the data + */ + if (rlen == 0) { + *lenp = rlen; + goto out; + } + /* paranoid */ + if (rlen > *lenp) { + SMBSDEBUG("bad server! rlen %d, len %d\n", + rlen, *lenp); + rlen = *lenp; + } + + error = md_get_uio(mdp, uiop, rlen); + if (error) + goto out; + + /* Success */ + *lenp = rlen; + +out: + smb_rq_done(rqp); + return (error); +} + +int +smb2_smb_write(smb_fh_t *fhp, uint32_t *lenp, + uio_t *uiop, smb_cred_t *scred, int timo) +{ + struct smb_share *ssp = FHTOSS(fhp); + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + int error; + uint64_t off64 = uiop->uio_loffset; + uint32_t rlen; + uint16_t data_offset; + uint16_t length = 0; + + error = smb_rq_alloc(SSTOCP(ssp), SMB2_WRITE, scred, &rqp); + if (error) + return (error); + + /* + * Build the SMB 2 Write Request + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 49); /* Struct size */ + data_offset = SMB2_HDRLEN + 48; + mb_put_uint16le(mbp, data_offset); /* Data Offset */ + mb_put_uint32le(mbp, *lenp); /* Length of write */ + mb_put_uint64le(mbp, off64); /* Offset */ + + mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent); + mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile); + + mb_put_uint32le(mbp, 0); /* Channel */ + mb_put_uint32le(mbp, 0); /* Remaining */ + mb_put_uint32le(mbp, 0); /* Channel offset/len */ + mb_put_uint32le(mbp, 0); /* Write flags */ + + error = mb_put_uio(mbp, uiop, *lenp); + if (error) + goto out; + + if (timo == 0) + timo = smb2_timo_write; + error = smb2_rq_simple_timed(rqp, timo); + if (error) + goto out; + + /* + * Parse SMB 2/3 Write Response + */ + smb_rq_getreply(rqp, &mdp); + + /* Check structure size is 17 */ + md_get_uint16le(mdp, &length); + if (length != 17) { + error = EBADRPC; + goto out; + } + + md_get_uint16le(mdp, NULL); /* Get Reserved */ + + /* Get Data Length written */ + error = md_get_uint32le(mdp, &rlen); + if (error) + goto out; + + /* Get Data Remaining (always 0) */ + md_get_uint32le(mdp, NULL); + + /* Get Reserved2 (always 0) */ + md_get_uint32le(mdp, NULL); + + /* Success */ + *lenp = rlen; + +out: + smb_rq_done(rqp); + return (error); +} + +/* + * Note: the IOD calls this, so this request must not wait for + * connection state changes, etc. (uses smb2_rq_internal) + */ +int +smb2_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + error = smb_rq_alloc(VCTOCP(vcp), SMB2_ECHO, scred, &rqp); + if (error) + return (error); + + /* + * Build the SMB 2 Echo Request + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 4); /* Struct size */ + mb_put_uint16le(mbp, 0); /* Reserved */ + + rqp->sr_flags |= SMBR_NORECONNECT; + error = smb2_rq_internal(rqp, timo); + + smb_rq_done(rqp); + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c index d1e7efd60a..398be59709 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c @@ -34,6 +34,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -61,6 +63,7 @@ #include <netsmb/smb_osdep.h> #include <netsmb/smb.h> +#include <netsmb/smb2.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_subr.h> #include <netsmb/smb_tran.h> @@ -80,6 +83,9 @@ static void smb_vc_gone(struct smb_connobj *cp); static void smb_share_free(struct smb_connobj *cp); static void smb_share_gone(struct smb_connobj *cp); +static void smb_fh_free(struct smb_connobj *cp); +static void smb_fh_gone(struct smb_connobj *cp); + int smb_sm_init(void) { @@ -105,7 +111,7 @@ void smb_sm_done(void) { /* - * XXX Q4BP why are we not iterating on smb_vclist here? + * Why are we not iterating on smb_vclist here? * Because the caller has just called smb_sm_idle() to * make sure we have no VCs before calling this. */ @@ -181,6 +187,16 @@ smb_co_rele(struct smb_connobj *co) int old_flags; SMB_CO_LOCK(co); + + /* + * When VC usecount goes from 2 to 1, signal the iod_idle CV. + * It's unfortunate to have object type-specific logic here, + * but it's hard to do this anywhere else. + */ + if (co->co_level == SMBL_VC && co->co_usecount == 2) { + smb_vc_t *vcp = CPTOVC(co); + cv_signal(&vcp->iod_idle); + } if (co->co_usecount > 1) { co->co_usecount--; SMB_CO_UNLOCK(co); @@ -365,10 +381,12 @@ smb_vc_free(struct smb_connobj *cp) if (vcp->vc_mackey != NULL) kmem_free(vcp->vc_mackey, vcp->vc_mackeylen); + if (vcp->vc_ssnkey != NULL) + kmem_free(vcp->vc_ssnkey, vcp->vc_ssnkeylen); + cv_destroy(&vcp->iod_muxwait); cv_destroy(&vcp->iod_idle); rw_destroy(&vcp->iod_rqlock); - sema_destroy(&vcp->vc_sendlock); cv_destroy(&vcp->vc_statechg); smb_co_done(VCTOCP(vcp)); kmem_free(vcp, sizeof (*vcp)); @@ -392,14 +410,15 @@ smb_vc_create(smbioc_ossn_t *ossn, smb_cred_t *scred, smb_vc_t **vcpp) vcp->vc_co.co_gone = smb_vc_gone; cv_init(&vcp->vc_statechg, objtype, CV_DRIVER, NULL); - sema_init(&vcp->vc_sendlock, 1, objtype, SEMA_DRIVER, NULL); rw_init(&vcp->iod_rqlock, objtype, RW_DRIVER, NULL); cv_init(&vcp->iod_idle, objtype, CV_DRIVER, NULL); + cv_init(&vcp->iod_muxwait, objtype, CV_DRIVER, NULL); /* Expanded TAILQ_HEAD_INITIALIZER */ vcp->iod_rqlist.tqh_last = &vcp->iod_rqlist.tqh_first; - vcp->vc_state = SMBIOD_ST_IDLE; + /* A brand new VC should connect. */ + vcp->vc_state = SMBIOD_ST_RECONNECT; /* * These identify the connection. @@ -612,10 +631,14 @@ smb_share_gone(struct smb_connobj *cp) { struct smb_cred scred; struct smb_share *ssp = CPTOSS(cp); + smb_vc_t *vcp = SSTOVC(ssp); smb_credinit(&scred, NULL); smb_iod_shutdown_share(ssp); - (void) smb_smb_treedisconnect(ssp, &scred); + if (vcp->vc_flags & SMBV_SMB2) + (void) smb2_smb_treedisconnect(ssp, &scred); + else + (void) smb_smb_treedisconnect(ssp, &scred); smb_credrele(&scred); } @@ -655,6 +678,7 @@ smb_share_create(smbioc_tcon_t *tcon, struct smb_vc *vcp, cv_init(&ssp->ss_conn_done, objtype, CV_DRIVER, NULL); ssp->ss_tid = SMB_TID_UNKNOWN; + ssp->ss2_tree_id = SMB2_TID_UNKNOWN; bcopy(&tcon->tc_sh, &ssp->ss_ioc, sizeof (smbioc_oshare_t)); @@ -770,6 +794,7 @@ smb_share_invalidate(struct smb_share *ssp) int smb_share_tcon(smb_share_t *ssp, smb_cred_t *scred) { + smb_vc_t *vcp = SSTOVC(ssp); clock_t tmo; int error; @@ -813,7 +838,10 @@ smb_share_tcon(smb_share_t *ssp, smb_cred_t *scred) * and ss_flags |= SMBS_CONNECTED; */ SMB_SS_UNLOCK(ssp); - error = smb_smb_treeconnect(ssp, scred); + if (vcp->vc_flags & SMBV_SMB2) + error = smb2_smb_treeconnect(ssp, scred); + else + error = smb_smb_treeconnect(ssp, scred); SMB_SS_LOCK(ssp); ssp->ss_flags &= ~SMBS_RECONNECTING; @@ -829,6 +857,114 @@ out: } /* + * File handle level functions + */ + +void +smb_fh_hold(struct smb_fh *fhp) +{ + smb_co_hold(FHTOCP(fhp)); +} + +void +smb_fh_rele(struct smb_fh *fhp) +{ + smb_co_rele(FHTOCP(fhp)); +} + +void +smb_fh_close(struct smb_fh *fhp) +{ + smb_co_kill(FHTOCP(fhp)); +} + +/* + * Normally called via smb_fh_rele() + * after co_usecount drops to zero. + * Also called via: smb_fh_kill() + */ +static void +smb_fh_gone(struct smb_connobj *cp) +{ + struct smb_cred scred; + struct smb_fh *fhp = CPTOFH(cp); + smb_share_t *ssp = FHTOSS(fhp); + int err; + + if ((fhp->fh_flags & SMBFH_VALID) == 0) + return; + + /* + * We have no durable handles (yet) so if there has been a + * reconnect, don't bother to close this handle. + */ + if (fhp->fh_vcgenid != ssp->ss_vcgenid) + return; + + smb_credinit(&scred, NULL); + err = smb_smb_close(ssp, fhp, &scred); + smb_credrele(&scred); + if (err) { + SMBSDEBUG("close err=%d\n", err); + } +} + +/* + * Normally called via smb_fh_rele() + * after co_usecount drops to zero. + */ +static void +smb_fh_free(struct smb_connobj *cp) +{ + struct smb_fh *fhp = CPTOFH(cp); + + smb_co_done(FHTOCP(fhp)); + kmem_free(fhp, sizeof (*fhp)); +} + +/* + * Allocate fh structure and attach it to the given share. + * Share expected to be locked on entry. + */ +/*ARGSUSED*/ +int +smb_fh_create(smb_share_t *ssp, struct smb_fh **fhpp) +{ + static char objtype[] = "smb_fh"; + struct smb_fh *fhp; + + fhp = kmem_zalloc(sizeof (struct smb_fh), KM_SLEEP); + smb_co_init(FHTOCP(fhp), SMBL_FH, objtype); + fhp->fh_co.co_free = smb_fh_free; + fhp->fh_co.co_gone = smb_fh_gone; + + SMB_SS_LOCK(ssp); + if ((ssp->ss_flags & SMBS_GONE) != 0) { + SMB_SS_UNLOCK(ssp); + smb_fh_free(FHTOCP(fhp)); + return (ENOTCONN); + } + + smb_co_addchild(SSTOCP(ssp), FHTOCP(fhp)); + *fhpp = fhp; + SMB_SS_UNLOCK(ssp); + + return (0); +} + +void +smb_fh_opened(struct smb_fh *fhp) +{ + smb_share_t *ssp = FHTOSS(fhp); + + SMB_FH_LOCK(fhp); + fhp->fh_vcgenid = ssp->ss_vcgenid; + fhp->fh_flags |= SMBFH_VALID; + SMB_FH_UNLOCK(fhp); +} + + +/* * Solaris zones support */ /*ARGSUSED*/ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h index 42dfd687f9..d0a8a1dca0 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h @@ -33,9 +33,11 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Portions Copyright (C) 2001 - 2013 Apple Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMB_CONN_H @@ -46,6 +48,7 @@ #include <sys/queue.h> /* for SLIST below */ #include <sys/uio.h> #include <netsmb/smb_dev.h> +#include "smb_signing.h" /* * Credentials of user/process for processing in the connection procedures @@ -61,14 +64,14 @@ typedef struct smb_cred { /* * Bits in vc_flags (a.k.a. vc_co.co_flags) - * Many of these were duplicates of SMBVOPT_ flags - * and we now keep those too instead of merging - * them into vc_flags. + * Note: SMBO_GONE is also in vc_flags */ - -#define SMBV_WIN95 0x0010 /* used to apply bugfixes for this OS */ -#define SMBV_NT4 0x0020 /* used when NT4 issues invalid resp */ #define SMBV_UNICODE 0x0040 /* conn configured to use Unicode */ +#define SMBV_EXT_SEC 0x0080 /* conn to use extended security */ +#define SMBV_SIGNING 0x0100 /* negotiated signing */ +#define SMBV_SMB2 0x0200 /* VC using SMB 2 or 3 */ +#define SMBV_HAS_FILEIDS 0x0400 /* Use File IDs for hash and inode numbers */ +#define SMBV_NO_WRITE_THRU 0x0800 /* Can't use ... */ /* * Note: the common "obj" level uses this GONE flag by @@ -89,6 +92,16 @@ typedef struct smb_cred { */ #define SMBS_GONE SMBO_GONE +/* + * bits in smb_fh fh_flags (a.k.a. ss_co.co_flags) + */ +#define SMBFH_VALID 0x0002 /* FID is valid */ +/* + * Note: the common "obj" level uses this GONE flag by + * the name SMBO_GONE. Keep this alias as a reminder. + */ +#define SMBFH_GONE SMBO_GONE + struct smb_rq; /* This declares struct smb_rqhead */ TAILQ_HEAD(smb_rqhead, smb_rq); @@ -141,9 +154,57 @@ typedef struct smb_connobj smb_connobj_t; /* * "Level" in the connection object hierarchy */ -#define SMBL_SM 0 -#define SMBL_VC 1 -#define SMBL_SHARE 2 +enum smbco_level { + SMBL_SM = 0, + SMBL_VC = 1, + SMBL_SHARE = 2, + SMBL_FH = 3 +}; + +/* + * SMB1 Negotiated protocol parameters + */ +struct smb_sopt { + int16_t sv_proto; /* protocol dialect */ + uchar_t sv_sm; /* security mode */ + int16_t sv_tz; /* offset in min relative to UTC */ + uint16_t sv_maxmux; /* max number of outstanding rq's */ + uint16_t sv_maxvcs; /* max number of VCs */ + uint16_t sv_rawmode; + uint32_t sv_maxtx; /* maximum transmit buf size */ + uint32_t sv_maxraw; /* maximum raw-buffer size */ + uint32_t sv_skey; /* session key */ + uint32_t sv_caps; /* capabilites SMB_CAP_ */ + + /* SMB2+ fields */ + uint32_t sv2_sessflags; /* final session setup reply flags */ + uint16_t sv2_dialect; /* dialect (non zero for SMB 2/3 */ + uint32_t sv2_capabilities; /* capabilities */ + uint32_t sv2_maxtransact; /* max transact size */ + uint32_t sv2_maxread; /* max read size */ + uint32_t sv2_maxwrite; /* max write size */ + uint8_t sv2_guid[16]; /* GUID */ + uint16_t sv2_security_mode; /* security mode */ +}; +typedef struct smb_sopt smb_sopt_t; + +/* + * SMB1 I/O Deamon state + */ +struct smb_iods { + uint8_t is_hflags; /* SMB header flags */ + uint16_t is_hflags2; /* SMB header flags2 */ + uint16_t is_smbuid; /* SMB header UID */ + uint16_t is_next_mid; /* SMB header MID */ + uint32_t is_txmax; /* max tx/rx packet size */ + uint32_t is_rwmax; /* max read/write data size */ + uint32_t is_rxmax; /* max readx data size */ + uint32_t is_wxmax; /* max writex data size */ + /* Signing state */ + uint32_t is_next_seq; /* my next sequence number */ + +}; +typedef struct smb_iods smb_iods_t; /* * Virtual Circuit to a server (really connection + session). @@ -160,20 +221,35 @@ typedef struct smb_vc { uid_t vc_owner; /* Unix owner */ int vc_genid; /* "generation" ID */ - int vc_mackeylen; /* length of MAC key */ - uint8_t *vc_mackey; /* MAC key */ + int vc_mackeylen; /* MAC key length */ + int vc_ssnkeylen; /* session key length */ + uint8_t *vc_mackey; /* MAC key buffer */ + uint8_t *vc_ssnkey; /* session key buffer */ + smb_sign_mech_t vc_signmech; - ksema_t vc_sendlock; struct smb_tran_desc *vc_tdesc; /* transport ops. vector */ void *vc_tdata; /* transport control block */ - kcondvar_t iod_idle; /* IOD thread idle CV */ + /* SMB2+ fields */ + uint64_t vc2_oldest_message_id; + uint64_t vc2_next_message_id; + uint64_t vc2_limit_message_id; + uint64_t vc2_session_id; /* session id */ + uint64_t vc2_prev_session_id; /* for reconnect */ + uint32_t vc2_lease_key; /* lease key gen */ + + kcondvar_t iod_idle; /* IOD thread idle CV */ krwlock_t iod_rqlock; /* iod_rqlist */ - struct smb_rqhead iod_rqlist; /* list of outstanding reqs */ - struct _kthread *iod_thr; /* the IOD (reader) thread */ + struct smb_rqhead iod_rqlist; /* list of active reqs */ + struct _kthread *iod_thr; /* the IOD (reader) thread */ int iod_flags; /* see SMBIOD_* below */ - int iod_newrq; /* send needed (iod_rqlock) */ - int iod_muxfull; /* maxmux limit reached */ + uint_t iod_muxcnt; /* num. active requests */ + uint_t iod_muxwant; /* waiting to be active */ + kcondvar_t iod_muxwait; + boolean_t iod_noresp; /* Logged "not responding" */ + + smb_iods_t vc_iods; + smb_sopt_t vc_sopt; /* This is copied in/out when IOD enters/returns */ smbioc_ssn_work_t vc_work; @@ -187,33 +263,40 @@ typedef struct smb_vc { /* defines for members in vc_ssn */ #define vc_owner vc_ssn.ssn_owner +#define vc_vopt vc_ssn.ssn_vopt +#define vc_minver vc_ssn.ssn_minver +#define vc_maxver vc_ssn.ssn_maxver #define vc_srvname vc_ssn.ssn_srvname #define vc_srvaddr vc_ssn.ssn_id.id_srvaddr #define vc_domain vc_ssn.ssn_id.id_domain #define vc_username vc_ssn.ssn_id.id_user -#define vc_vopt vc_ssn.ssn_vopt /* defines for members in vc_work */ -#define vc_sopt vc_work.wk_sopt -#define vc_maxmux vc_work.wk_sopt.sv_maxmux -#define vc_tran_fd vc_work.wk_iods.is_tran_fd -#define vc_hflags vc_work.wk_iods.is_hflags -#define vc_hflags2 vc_work.wk_iods.is_hflags2 -#define vc_smbuid vc_work.wk_iods.is_smbuid -#define vc_next_mid vc_work.wk_iods.is_next_mid -#define vc_txmax vc_work.wk_iods.is_txmax -#define vc_rwmax vc_work.wk_iods.is_rwmax -#define vc_rxmax vc_work.wk_iods.is_rxmax -#define vc_wxmax vc_work.wk_iods.is_wxmax -#define vc_ssn_key vc_work.wk_iods.is_ssn_key -#define vc_next_seq vc_work.wk_iods.is_next_seq -#define vc_u_mackey vc_work.wk_iods.is_u_mackey -#define vc_u_maclen vc_work.wk_iods.is_u_maclen +#define vc_cl_guid vc_work.wk_cl_guid + +/* defines for members in vc_sopt ? */ +#define vc_maxmux vc_sopt.sv_maxmux + +/* defines for members in vc_iods */ +#define vc_hflags vc_iods.is_hflags +#define vc_hflags2 vc_iods.is_hflags2 +#define vc_smbuid vc_iods.is_smbuid +#define vc_next_mid vc_iods.is_next_mid +#define vc_txmax vc_iods.is_txmax +#define vc_rwmax vc_iods.is_rwmax +#define vc_rxmax vc_iods.is_rxmax +#define vc_wxmax vc_iods.is_wxmax +#define vc_next_seq vc_iods.is_next_seq #define SMB_VC_LOCK(vcp) mutex_enter(&(vcp)->vc_lock) #define SMB_VC_UNLOCK(vcp) mutex_exit(&(vcp)->vc_lock) -#define SMB_UNICODE_STRINGS(vcp) ((vcp)->vc_hflags2 & SMB_FLAGS2_UNICODE) +#define CPTOVC(cp) ((struct smb_vc *)((void *)(cp))) +#define VCTOCP(vcp) (&(vcp)->vc_co) + +#define SMB_UNICODE_STRINGS(vcp) \ + (((vcp)->vc_flags & SMBV_SMB2) != 0 || \ + ((vcp)->vc_hflags2 & SMB_FLAGS2_UNICODE) != 0) /* Bits in iod_flags */ #define SMBIOD_RUNNING 0x0001 @@ -231,6 +314,9 @@ typedef struct smb_share { int ss_vcgenid; /* check VC generation ID */ uint16_t ss_tid; /* TID */ uint16_t ss_options; /* option support bits */ + uint32_t ss2_tree_id; + uint32_t ss2_share_flags; + uint32_t ss2_share_caps; smbioc_oshare_t ss_ioc; } smb_share_t; @@ -245,27 +331,47 @@ typedef struct smb_share { #define SMB_SS_LOCK(ssp) mutex_enter(&(ssp)->ss_lock) #define SMB_SS_UNLOCK(ssp) mutex_exit(&(ssp)->ss_lock) -#define CPTOVC(cp) ((struct smb_vc *)((void *)(cp))) -#define VCTOCP(vcp) (&(vcp)->vc_co) - #define CPTOSS(cp) ((struct smb_share *)((void *)(cp))) -#define SSTOVC(ssp) CPTOVC(((ssp)->ss_co.co_parent)) #define SSTOCP(ssp) (&(ssp)->ss_co) +#define SSTOVC(ssp) CPTOVC(((ssp)->ss_co.co_parent)) + +typedef struct smb2fid { + uint64_t fid_persistent; + uint64_t fid_volatile; +} smb2fid_t; + +/* + * smb_fh struct describes an open file handle under some share. + */ +typedef struct smb_fh { + struct smb_connobj fh_co; /* keep first! See CPTOSS */ + int fh_vcgenid; /* check VC generation ID */ + uint32_t fh_rights; /* granted access */ + smb2fid_t fh_fid2; + uint16_t fh_fid1; +} smb_fh_t; + +#define fh_lock fh_co.co_lock +#define fh_flags fh_co.co_flags + +#define SMB_FH_LOCK(fhp) mutex_enter(&(fhp)->fh_lock) +#define SMB_FH_UNLOCK(fhp) mutex_exit(&(fhp)->fh_lock) + +#define CPTOFH(cp) ((struct smb_fh *)((void *)(cp))) +#define FHTOCP(fhp) (&(fhp)->fh_co) +#define FHTOSS(fhp) CPTOSS(((fhp)->fh_co.co_parent)) /* * Call-back operations vector, so the netsmb module * can notify smbfs about events affecting mounts. * Installed in netsmb after smbfs loads. + * Note: smbfs only uses the fscb_discon hook. */ typedef struct smb_fscb { /* Called when the VC has disconnected. */ void (*fscb_disconn)(smb_share_t *); /* Called when the VC has reconnected. */ void (*fscb_connect)(smb_share_t *); - /* Called when the server becomes unresponsive. */ - void (*fscb_down)(smb_share_t *); - /* Called when the server is responding again. */ - void (*fscb_up)(smb_share_t *); } smb_fscb_t; /* Install the above vector, or pass NULL to clear it. */ void smb_fscb_set(smb_fscb_t *); @@ -278,14 +384,14 @@ typedef struct smb_dev { kmutex_t sd_lock; struct smb_vc *sd_vc; /* Reference to VC */ struct smb_share *sd_share; /* Reference to share if any */ + struct smb_fh *sd_fh; /* Reference to FH, if any */ int sd_level; /* SMBL_VC, ... */ int sd_vcgenid; /* Generation of share or VC */ int sd_poll; /* Future use */ int sd_flags; /* State of connection */ -#define NSMBFL_OPEN 0x0001 -#define NSMBFL_IOD 0x0002 -#define NSMBFL_IOCTL 0x0004 - int sd_smbfid; /* library read/write */ +#define NSMBFL_OPEN 0x0001 /* Device minor is open */ +#define NSMBFL_IOD 0x0004 /* Open by IOD */ +#define NSMBFL_IOCTL 0x0010 /* Serialize ioctl calls */ zoneid_t zoneid; /* Zone id */ } smb_dev_t; @@ -300,6 +406,8 @@ int smb_dev2share(int fd, struct smb_share **sspp); /* * smb_usr.c */ +int smb_usr_ioctl(smb_dev_t *, int, intptr_t, int, cred_t *); + int smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags); int smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags); int smb_usr_dup_dev(smb_dev_t *sdp, intptr_t arg, int flags); @@ -319,7 +427,10 @@ int smb_usr_get_tree(smb_dev_t *, int, intptr_t, int, cred_t *); int smb_usr_drop_tree(smb_dev_t *sdp, int cmd); int smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr); -int smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags); +int smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, + cred_t *cr); + +int smb_pkey_ioctl(int, intptr_t, int, cred_t *); /* @@ -327,18 +438,23 @@ int smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags); */ int smb_iod_create(smb_vc_t *vcp); int smb_iod_destroy(smb_vc_t *vcp); -int smb_iod_connect(smb_vc_t *vcp); void smb_iod_disconnect(smb_vc_t *vcp); -int smb_iod_addrq(struct smb_rq *rqp); -int smb_iod_multirq(struct smb_rq *rqp); +int smb2_iod_addrq(struct smb_rq *rqp); +int smb1_iod_addrq(struct smb_rq *rqp); +int smb1_iod_multirq(struct smb_rq *rqp); int smb_iod_waitrq(struct smb_rq *rqp); +int smb_iod_waitrq_int(struct smb_rq *rqp); void smb_iod_removerq(struct smb_rq *rqp); +int smb_iod_sendrecv(struct smb_rq *, int); void smb_iod_shutdown_share(smb_share_t *ssp); void smb_iod_sendall(smb_vc_t *); -int smb_iod_recvall(smb_vc_t *); +int smb_iod_recvall(smb_vc_t *, boolean_t); -int smb_iod_vc_work(smb_vc_t *, cred_t *); +int nsmb_iod_connect(smb_vc_t *vcp, cred_t *cr); +int nsmb_iod_negotiate(smb_vc_t *vcp, cred_t *cr); +int nsmb_iod_ssnsetup(smb_vc_t *vcp, cred_t *cr); +int smb_iod_vc_work(smb_vc_t *, int, cred_t *); int smb_iod_vc_idle(smb_vc_t *); int smb_iod_vc_rcfail(smb_vc_t *); int smb_iod_reconnect(smb_vc_t *); @@ -381,4 +497,13 @@ void smb_share_kill(smb_share_t *ssp); void smb_share_invalidate(smb_share_t *ssp); int smb_share_tcon(smb_share_t *, smb_cred_t *); +/* + * File handle level functions + */ +int smb_fh_create(smb_share_t *ssp, struct smb_fh **fhpp); +void smb_fh_opened(struct smb_fh *fhp); +void smb_fh_close(struct smb_fh *fhp); +void smb_fh_hold(struct smb_fh *fhp); +void smb_fh_rele(struct smb_fh *fhp); + #endif /* _SMB_CONN_H */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c index 489a1756ec..3f00ec24ed 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c @@ -31,9 +31,10 @@ */ /* - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/types.h> @@ -61,35 +62,42 @@ #include <sys/modctl.h> #include <sys/devops.h> #include <sys/thread.h> -#include <sys/types.h> +#include <sys/socket.h> #include <sys/zone.h> #include <netsmb/smb_osdep.h> #include <netsmb/mchain.h> /* for "htoles()" */ #include <netsmb/smb.h> +#include <netsmb/smb2.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_subr.h> #include <netsmb/smb_dev.h> #include <netsmb/smb_pass.h> +#ifndef _KERNEL +#include <libfknsmb.h> + +#define _init(v) nsmb_drv_init(v) +#define _fini(v) nsmb_drv_fini(v) + +#endif /* _KERNEL */ + #define NSMB_MIN_MINOR 1 #define NSMB_MAX_MINOR L_MAXMIN32 /* for version checks */ const uint32_t nsmb_version = NSMB_VERSION; +/* for smb_nbst_create() */ +dev_t nsmb_dev_tcp = NODEV; +dev_t nsmb_dev_tcp6 = NODEV; + static void *statep; static major_t nsmb_major; static minor_t last_minor = NSMB_MIN_MINOR; -static dev_info_t *nsmb_dip; static kmutex_t dev_lck; -/* Zone support */ -zone_key_t nsmb_zone_key; -extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data); -extern void nsmb_zone_destroy(zoneid_t zoneid, void *data); - /* * cb_ops device operations. */ @@ -99,6 +107,15 @@ static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp); static int nsmb_close2(smb_dev_t *sdp, cred_t *cr); +#ifdef _KERNEL + +static dev_info_t *nsmb_dip; + +/* Zone support */ +zone_key_t nsmb_zone_key; +extern void nsmb_zone_shutdown(zoneid_t zoneid, void *data); +extern void nsmb_zone_destroy(zoneid_t zoneid, void *data); + /* smbfs cb_ops */ static struct cb_ops nsmb_cbops = { nsmb_open, /* open */ @@ -160,10 +177,14 @@ static struct modlinkage nsmb_modlinkage = { NULL }; +#endif /* _KERNEL */ + int _init(void) { +#ifdef _KERNEL int error; +#endif /* _KERNEL */ (void) ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1); @@ -176,12 +197,7 @@ _init(void) /* Initialize password Key chain DB. */ smb_pkey_init(); - /* Time conversion stuff. */ - smb_time_init(); - - /* Initialize crypto mechanisms. */ - smb_crypto_mech_init(); - +#ifdef _KERNEL zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown, nsmb_zone_destroy); @@ -200,6 +216,14 @@ _init(void) return (error); } +#else /* _KERNEL */ + streams_msg_init(); + /* No attach, so need to set major. */ + nsmb_major = 1; + /* And these, for smb_nbst_create() */ + nsmb_dev_tcp = AF_INET; + nsmb_dev_tcp6 = AF_INET6; +#endif /* _KERNEL */ return (0); } @@ -218,6 +242,7 @@ _fini(void) if ((status = smb_pkey_idle()) != 0) return (status); +#ifdef _KERNEL /* * Remove the module. Do this before destroying things, * to prevent new entrances while we're destorying. @@ -227,9 +252,7 @@ _fini(void) } (void) zone_key_delete(nsmb_zone_key); - - /* Time conversion stuff. */ - smb_time_fini(); +#endif /* _KERNEL */ /* Destroy password Key chain DB. */ smb_pkey_fini(); @@ -242,6 +265,8 @@ _fini(void) return (status); } +#ifdef _KERNEL + int _info(struct modinfo *modinfop) { @@ -270,6 +295,7 @@ nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { + major_t tmaj; if (cmd != DDI_ATTACH) return (DDI_FAILURE); @@ -294,6 +320,20 @@ nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) */ nsmb_major = ddi_name_to_major(NSMB_NAME); + /* + * We also need major numbers for t_kopen + */ + tmaj = ddi_name_to_major("tcp"); + if (tmaj == DDI_MAJOR_T_NONE) + cmn_err(CE_NOTE, "no tcp major?"); + else + nsmb_dev_tcp = makedevice(tmaj, 0); + tmaj = ddi_name_to_major("tcp6"); + if (tmaj == DDI_MAJOR_T_NONE) + cmn_err(CE_NOTE, "no tcp6 major?"); + else + nsmb_dev_tcp6 = makedevice(tmaj, 0); + nsmb_dip = dip; ddi_report_dev(dip); return (DDI_SUCCESS); @@ -315,6 +355,65 @@ nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) return (DDI_SUCCESS); } +#else /* _KERNEL */ + +/* + * Wrappers for libfknsmb: ioctl, open, close, load + */ + +/*ARGSUSED*/ +int +nsmb_drv_ioctl(dev32_t dev32, int cmd, intptr_t arg, int flags) +{ + dev_t dev = expldev(dev32); + cred_t *cr = CRED(); + int err; + + err = nsmb_ioctl(dev, cmd, arg, flags, cr, NULL); + return (err); +} + +/*ARGSUSED*/ +int +nsmb_drv_open(dev32_t *dev32p, int flags, int otyp) +{ + dev_t dev = expldev(*dev32p); + int err; + + err = nsmb_open(&dev, flags, otyp, CRED()); + if (err == 0) { + /* + * We have NSMB_MAX_MINOR == L_MAXMIN32 + * therefore cmpldev never fails. + */ + VERIFY(cmpldev(dev32p, dev) != 0); + } + return (err); +} + +/*ARGSUSED*/ +int +nsmb_drv_close(dev32_t dev32, int flags, int otyp) +{ + dev_t dev = expldev(dev32); + int err; + + err = nsmb_close(dev, flags, otyp, CRED()); + return (err); +} + +/* + * This function intentionally does nothing. It's used only to + * force libfknsmb to load at program start so one can set + * breakpoints etc. without debugger "force load" tricks. + */ +void +nsmb_drv_load(void) +{ +} + +#endif /* _KERNEL */ + /*ARGSUSED*/ static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, /* model.h */ @@ -325,7 +424,7 @@ nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, /* model.h */ sdp = ddi_get_soft_state(statep, getminor(dev)); if (sdp == NULL) { - return (DDI_FAILURE); + return (EBADF); } if ((sdp->sd_flags & NSMBFL_OPEN) == 0) { return (EBADF); @@ -346,107 +445,7 @@ nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, /* model.h */ * check the zone status here on every ioctl call. */ - /* - * Serialize ioctl calls. The smb_usr_... functions - * don't expect concurrent calls on a given sdp. - */ - mutex_enter(&sdp->sd_lock); - if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) { - mutex_exit(&sdp->sd_lock); - return (EBUSY); - } - sdp->sd_flags |= NSMBFL_IOCTL; - mutex_exit(&sdp->sd_lock); - - err = 0; - switch (cmd) { - case SMBIOC_GETVERS: - (void) ddi_copyout(&nsmb_version, (void *)arg, - sizeof (nsmb_version), flags); - break; - - case SMBIOC_FLAGS2: - err = smb_usr_get_flags2(sdp, arg, flags); - break; - - case SMBIOC_GETSSNKEY: - err = smb_usr_get_ssnkey(sdp, arg, flags); - break; - - case SMBIOC_DUP_DEV: - err = smb_usr_dup_dev(sdp, arg, flags); - break; - - case SMBIOC_REQUEST: - err = smb_usr_simplerq(sdp, arg, flags, cr); - break; - - case SMBIOC_T2RQ: - err = smb_usr_t2request(sdp, arg, flags, cr); - break; - - case SMBIOC_READ: - case SMBIOC_WRITE: - err = smb_usr_rw(sdp, cmd, arg, flags, cr); - break; - - case SMBIOC_NTCREATE: - err = smb_usr_ntcreate(sdp, arg, flags, cr); - break; - - case SMBIOC_PRINTJOB: - err = smb_usr_printjob(sdp, arg, flags, cr); - break; - - case SMBIOC_CLOSEFH: - err = smb_usr_closefh(sdp, cr); - break; - - case SMBIOC_SSN_CREATE: - case SMBIOC_SSN_FIND: - err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr); - break; - - case SMBIOC_SSN_KILL: - case SMBIOC_SSN_RELE: - err = smb_usr_drop_ssn(sdp, cmd); - break; - - case SMBIOC_TREE_CONNECT: - case SMBIOC_TREE_FIND: - err = smb_usr_get_tree(sdp, cmd, arg, flags, cr); - break; - - case SMBIOC_TREE_KILL: - case SMBIOC_TREE_RELE: - err = smb_usr_drop_tree(sdp, cmd); - break; - - case SMBIOC_IOD_WORK: - err = smb_usr_iod_work(sdp, arg, flags, cr); - break; - - case SMBIOC_IOD_IDLE: - case SMBIOC_IOD_RCFAIL: - err = smb_usr_iod_ioctl(sdp, cmd, arg, flags); - break; - - case SMBIOC_PK_ADD: - case SMBIOC_PK_DEL: - case SMBIOC_PK_CHK: - case SMBIOC_PK_DEL_OWNER: - case SMBIOC_PK_DEL_EVERYONE: - err = smb_pkey_ioctl(cmd, arg, flags, cr); - break; - - default: - err = ENOTTY; - break; - } - - mutex_enter(&sdp->sd_lock); - sdp->sd_flags &= ~NSMBFL_IOCTL; - mutex_exit(&sdp->sd_lock); + err = smb_usr_ioctl(sdp, cmd, arg, flags, cr); return (err); } @@ -491,7 +490,6 @@ found: *dev = makedevice(nsmb_major, m); mutex_exit(&dev_lck); - sdp->sd_smbfid = -1; sdp->sd_flags |= NSMBFL_OPEN; sdp->zoneid = crgetzoneid(cr); mutex_init(&sdp->sd_lock, NULL, MUTEX_DRIVER, NULL); @@ -529,14 +527,17 @@ nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr) return (err); } +/*ARGSUSED*/ static int nsmb_close2(smb_dev_t *sdp, cred_t *cr) { struct smb_vc *vcp; struct smb_share *ssp; + struct smb_fh *fhp; - if (sdp->sd_smbfid != -1) - (void) smb_usr_closefh(sdp, cr); + fhp = sdp->sd_fh; + if (fhp != NULL) + smb_fh_rele(fhp); ssp = sdp->sd_share; if (ssp != NULL) @@ -566,8 +567,10 @@ nsmb_close2(smb_dev_t *sdp, cred_t *cr) int smb_usr_dup_dev(smb_dev_t *sdp, intptr_t arg, int flags) { +#ifdef _KERNEL file_t *fp = NULL; vnode_t *vp; +#endif /* _KERNEL */ smb_dev_t *from_sdp; dev_t dev; int32_t ufd; @@ -582,16 +585,24 @@ smb_usr_dup_dev(smb_dev_t *sdp, intptr_t arg, int flags) */ if (ddi_copyin((void *) arg, &ufd, sizeof (ufd), flags)) return (EFAULT); +#ifdef _KERNEL if ((fp = getf(ufd)) == NULL) return (EBADF); /* rele fp below */ vp = fp->f_vnode; dev = vp->v_rdev; +#else /* _KERNEL */ + /* + * No getf(ufd) -- ufd is really a dev32_t + */ + dev = expldev((dev32_t)ufd); +#endif /* _KERNEL */ if (dev == 0 || dev == NODEV || getmajor(dev) != nsmb_major) { err = EINVAL; goto out; } + from_sdp = ddi_get_soft_state(statep, getminor(dev)); if (from_sdp == NULL) { err = EINVAL; @@ -609,8 +620,10 @@ smb_usr_dup_dev(smb_dev_t *sdp, intptr_t arg, int flags) err = 0; out: +#ifdef _KERNEL if (fp) releasef(ufd); +#endif /* _KERNEL */ return (err); } @@ -621,19 +634,27 @@ out: int smb_dev2share(int fd, struct smb_share **sspp) { +#ifdef _KERNEL file_t *fp = NULL; vnode_t *vp; +#endif /* _KERNEL */ smb_dev_t *sdp; smb_share_t *ssp; dev_t dev; int err; +#ifdef _KERNEL if ((fp = getf(fd)) == NULL) return (EBADF); /* rele fp below */ - vp = fp->f_vnode; dev = vp->v_rdev; +#else /* _KERNEL */ + /* + * No getf(ufd) -- fd is really a dev32_t + */ + dev = expldev((dev32_t)fd); +#endif /* _KERNEL */ if (dev == 0 || dev == NODEV || getmajor(dev) != nsmb_major) { err = EINVAL; @@ -660,7 +681,9 @@ smb_dev2share(int fd, struct smb_share **sspp) err = 0; out: +#ifdef _KERNEL if (fp) releasef(fd); +#endif /* _KERNEL */ return (err); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c index db82fa0958..48c8ef591d 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c @@ -35,6 +35,9 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Portions Copyright (C) 2001 - 2013 Apple Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifdef DEBUG @@ -67,13 +70,26 @@ #include <netsmb/smb_osdep.h> #include <netsmb/smb.h> +#include <netsmb/smb2.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_rq.h> +#include <netsmb/smb2_rq.h> #include <netsmb/smb_subr.h> #include <netsmb/smb_tran.h> #include <netsmb/smb_trantcp.h> -int smb_iod_send_echo(smb_vc_t *); +/* + * SMB messages are up to 64K. Let's leave room for two. + * If we negotiate up to SMB2, increase these. XXX todo + */ +static int smb_tcpsndbuf = 0x20000; +static int smb_tcprcvbuf = 0x20000; +static int smb_connect_timeout = 10; /* seconds */ + +static int smb1_iod_process(smb_vc_t *, mblk_t *); +static int smb2_iod_process(smb_vc_t *, mblk_t *); +static int smb_iod_send_echo(smb_vc_t *, cred_t *cr); +static int smb_iod_logoff(struct smb_vc *vcp, cred_t *cr); /* * This is set/cleared when smbfs loads/unloads @@ -93,7 +109,10 @@ smb_iod_share_disconnected(smb_share_t *ssp) smb_share_invalidate(ssp); - /* smbfs_dead() */ + /* + * This is the only fscb hook smbfs currently uses. + * Replaces smbfs_dead() from Darwin. + */ if (fscb && fscb->fscb_disconn) { fscb->fscb_disconn(ssp); } @@ -142,19 +161,22 @@ smb_iod_invrq(struct smb_vc *vcp) /* * Invalidate all outstanding requests for this connection + * Also wakeup iod_muxwant waiters. */ rw_enter(&vcp->iod_rqlock, RW_READER); TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { smb_iod_rqprocessed(rqp, ENOTCONN, SMBR_RESTART); } rw_exit(&vcp->iod_rqlock); + cv_broadcast(&vcp->iod_muxwait); } /* - * Called by smb_vc_rele, smb_vc_kill, and by the driver - * close entry point if the IOD closes its dev handle. + * Called by smb_vc_rele/smb_vc_kill on last ref, and by + * the driver close function if the IOD closes its minor. + * In those cases, the caller should be the IOD thread. * - * Forcibly kill the connection and IOD. + * Forcibly kill the connection. */ void smb_iod_disconnect(struct smb_vc *vcp) @@ -170,139 +192,209 @@ smb_iod_disconnect(struct smb_vc *vcp) } SMB_VC_UNLOCK(vcp); - /* - * Let's be safe here and avoid doing any - * call across the network while trying to - * shut things down. If we just disconnect, - * the server will take care of the logoff. - */ SMB_TRAN_DISCONNECT(vcp); } /* * Send one request. * + * SMB1 only + * * Called by _addrq (for internal requests) * and _sendall (via _addrq, _multirq, _waitrq) + * Errors are reported via the smb_rq, using: + * smb_iod_rqprocessed(rqp, ...) */ -static int -smb_iod_sendrq(struct smb_rq *rqp) +static void +smb1_iod_sendrq(struct smb_rq *rqp) { struct smb_vc *vcp = rqp->sr_vc; mblk_t *m; int error; ASSERT(vcp); - ASSERT(SEMA_HELD(&vcp->vc_sendlock)); - ASSERT(RW_READ_HELD(&vcp->iod_rqlock)); + ASSERT(RW_WRITE_HELD(&vcp->iod_rqlock)); + ASSERT((vcp->vc_flags & SMBV_SMB2) == 0); /* - * Note: Anything special for SMBR_INTERNAL here? + * Internal requests are allowed in any state; + * otherwise should be active. */ - if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + if ((rqp->sr_flags & SMBR_INTERNAL) == 0 && + vcp->vc_state != SMBIOD_ST_VCACTIVE) { SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); - return (ENOTCONN); + smb_iod_rqprocessed(rqp, ENOTCONN, SMBR_RESTART); + return; } - /* - * On the first send, set the MID and (maybe) - * the signing sequence numbers. The increments - * here are serialized by vc_sendlock + * Overwrite the SMB header with the assigned MID and + * (if we're signing) sign it. */ - if (rqp->sr_sendcnt == 0) { + smb_rq_fillhdr(rqp); + if (rqp->sr_rqflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { + smb_rq_sign(rqp); + } - rqp->sr_mid = vcp->vc_next_mid++; + /* + * The transport send consumes the message and we'd + * prefer to keep a copy, so dupmsg() before sending. + */ + m = dupmsg(rqp->sr_rq.mb_top); + if (m == NULL) { + error = ENOBUFS; + goto fatal; + } - if (rqp->sr_rqflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { - /* - * We're signing requests and verifying - * signatures on responses. Set the - * sequence numbers of the request and - * response here, used in smb_rq_verify. - */ - rqp->sr_seqno = vcp->vc_next_seq++; - rqp->sr_rseqno = vcp->vc_next_seq++; - } +#ifdef DTRACE_PROBE2 + DTRACE_PROBE2(iod_sendrq, + (smb_rq_t *), rqp, (mblk_t *), m); +#endif - /* Fill in UID, TID, MID, etc. */ - smb_rq_fillhdr(rqp); + error = SMB_TRAN_SEND(vcp, m); + m = 0; /* consumed by SEND */ - /* - * Sign the message now that we're finally done - * filling in the SMB header fields, etc. - */ - if (rqp->sr_rqflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { - smb_rq_sign(rqp); - } + rqp->sr_lerror = error; + if (error == 0) { + SMBRQ_LOCK(rqp); + rqp->sr_flags |= SMBR_SENT; + rqp->sr_state = SMBRQ_SENT; + SMBRQ_UNLOCK(rqp); + return; } - if (rqp->sr_sendcnt++ >= 60/SMBSBTIMO) { /* one minute */ - smb_iod_rqprocessed(rqp, rqp->sr_lerror, SMBR_RESTART); + /* + * Transport send returned an error. + * Was it a fatal one? + */ + if (SMB_TRAN_FATAL(vcp, error)) { /* - * If all attempts to send a request failed, then - * something is seriously hosed. + * No further attempts should be made */ - return (ENOTCONN); + fatal: + SMBSDEBUG("TRAN_SEND returned fatal error %d\n", error); + smb_iod_rqprocessed(rqp, error, SMBR_RESTART); + return; } +} + +/* + * Send one request. + * + * SMB2 only + * + * Called by _addrq (for internal requests) + * and _sendall (via _addrq, _multirq, _waitrq) + * Errors are reported via the smb_rq, using: + * smb_iod_rqprocessed(rqp, ...) + */ +static void +smb2_iod_sendrq(struct smb_rq *rqp) +{ + struct smb_rq *c_rqp; /* compound */ + struct smb_vc *vcp = rqp->sr_vc; + mblk_t *top_m; + mblk_t *cur_m; + int error; + + ASSERT(vcp); + ASSERT(RW_WRITE_HELD(&vcp->iod_rqlock)); + ASSERT((vcp->vc_flags & SMBV_SMB2) != 0); /* - * Replaced m_copym() with Solaris copymsg() which does the same - * work when we want to do a M_COPYALL. - * m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, 0); + * Internal requests are allowed in any state; + * otherwise should be active. */ - m = copymsg(rqp->sr_rq.mb_top); + if ((rqp->sr_flags & SMBR_INTERNAL) == 0 && + vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + smb_iod_rqprocessed(rqp, ENOTCONN, SMBR_RESTART); + return; + } -#ifdef DTRACE_PROBE - DTRACE_PROBE2(smb_iod_sendrq, - (smb_rq_t *), rqp, (mblk_t *), m); -#else - SMBIODEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0); -#endif - m_dumpm(m); + /* + * Overwrite the SMB header with the assigned MID and + * (if we're signing) sign it. If there are compounded + * requests after the top one, do those too. + */ + smb2_rq_fillhdr(rqp); + if (rqp->sr2_rqflags & SMB2_FLAGS_SIGNED) { + smb2_rq_sign(rqp); + } + c_rqp = rqp->sr2_compound_next; + while (c_rqp != NULL) { + smb2_rq_fillhdr(c_rqp); + if (c_rqp->sr2_rqflags & SMB2_FLAGS_SIGNED) { + smb2_rq_sign(c_rqp); + } + c_rqp = c_rqp->sr2_compound_next; + } - if (m != NULL) { - error = SMB_TRAN_SEND(vcp, m); - m = 0; /* consumed by SEND */ - } else + /* + * The transport send consumes the message and we'd + * prefer to keep a copy, so dupmsg() before sending. + * We also need this to build the compound message + * that we'll actually send. The message offset at + * the start of each compounded message should be + * eight-byte aligned. The caller preparing the + * compounded request has to take care of that + * before we get here and sign messages etc. + */ + top_m = dupmsg(rqp->sr_rq.mb_top); + if (top_m == NULL) { error = ENOBUFS; + goto fatal; + } + c_rqp = rqp->sr2_compound_next; + while (c_rqp != NULL) { + size_t len = msgdsize(top_m); + ASSERT((len & 7) == 0); + cur_m = dupmsg(c_rqp->sr_rq.mb_top); + if (cur_m == NULL) { + freemsg(top_m); + error = ENOBUFS; + goto fatal; + } + linkb(top_m, cur_m); + } + + DTRACE_PROBE2(iod_sendrq, + (smb_rq_t *), rqp, (mblk_t *), top_m); + + error = SMB_TRAN_SEND(vcp, top_m); + top_m = 0; /* consumed by SEND */ rqp->sr_lerror = error; if (error == 0) { SMBRQ_LOCK(rqp); rqp->sr_flags |= SMBR_SENT; rqp->sr_state = SMBRQ_SENT; - if (rqp->sr_flags & SMBR_SENDWAIT) - cv_broadcast(&rqp->sr_cond); SMBRQ_UNLOCK(rqp); - return (0); + return; } /* - * Check for fatal errors + * Transport send returned an error. + * Was it a fatal one? */ if (SMB_TRAN_FATAL(vcp, error)) { /* * No further attempts should be made */ + fatal: SMBSDEBUG("TRAN_SEND returned fatal error %d\n", error); - return (ENOTCONN); + smb_iod_rqprocessed(rqp, error, SMBR_RESTART); + return; } - if (error) - SMBSDEBUG("TRAN_SEND returned non-fatal error %d\n", error); - -#ifdef APPLE - /* If proc waiting on rqp was signaled... */ - if (smb_rq_intr(rqp)) - smb_iod_rqprocessed(rqp, EINTR, 0); -#endif - - return (0); } +/* + * Receive one NetBIOS (or NBT over TCP) message. If none have arrived, + * wait up to SMB_NBTIMO (15 sec.) for one to arrive, and then if still + * none have arrived, return ETIME. + */ static int -smb_iod_recv1(struct smb_vc *vcp, mblk_t **mpp) +smb_iod_recvmsg(struct smb_vc *vcp, mblk_t **mpp) { mblk_t *m; - uchar_t *hp; int error; top: @@ -312,58 +404,50 @@ top: goto top; if (error) return (error); - ASSERT(m); + ASSERT(m != NULL); - m = m_pullup(m, SMB_HDRLEN); + m = m_pullup(m, 4); if (m == NULL) { return (ENOSR); } - /* - * Check the SMB header - */ - hp = mtod(m, uchar_t *); - if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) { - m_freem(m); - return (EPROTO); - } - *mpp = m; return (0); } /* + * How long should we keep around an unused VC (connection)? + * There's usually a good chance connections will be reused, + * so the default is to keep such connections for 5 min. + */ +#ifdef DEBUG +int smb_iod_idle_keep_time = 60; /* seconds */ +#else +int smb_iod_idle_keep_time = 300; /* seconds */ +#endif + +/* * Process incoming packets * - * This is the "reader" loop, run by the IOD thread - * while in state SMBIOD_ST_VCACTIVE. The loop now - * simply blocks in the socket recv until either a - * message arrives, or a disconnect. + * This is the "reader" loop, run by the IOD thread. Normally we're in + * state SMBIOD_ST_VCACTIVE here, but during reconnect we're called in + * other states with poll==TRUE * - * Any non-zero error means the IOD should terminate. + * A non-zero error return here causes the IOD work loop to terminate. */ int -smb_iod_recvall(struct smb_vc *vcp) +smb_iod_recvall(struct smb_vc *vcp, boolean_t poll) { - struct smb_rq *rqp; mblk_t *m; - uchar_t *hp; - ushort_t mid; int error = 0; - int etime_count = 0; /* for "server not responding", etc. */ + int etime_idle = 0; /* How many 15 sec. "ticks" idle. */ + int etime_count = 0; /* ... and when we have requests. */ for (;;) { /* * Check whether someone "killed" this VC, * or is asking the IOD to terminate. */ - - if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { - SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); - error = 0; - break; - } - if (vcp->iod_flags & SMBIOD_SHUTDOWN) { SMBIODEBUG("SHUTDOWN set\n"); /* This IOD thread will terminate. */ @@ -376,52 +460,88 @@ smb_iod_recvall(struct smb_vc *vcp) } m = NULL; - error = smb_iod_recv1(vcp, &m); + error = smb_iod_recvmsg(vcp, &m); + + /* + * Internal requests (reconnecting) call this in a loop + * (with poll==TRUE) until the request completes. + */ + if (error == ETIME && poll) + break; if (error == ETIME && vcp->iod_rqlist.tqh_first != NULL) { + /* - * Nothing received for 15 seconds and - * we have requests in the queue. + * Nothing received and requests waiting. + * Increment etime_count. If we were idle, + * skip the 1st tick, because we started + * waiting before there were any requests. */ - etime_count++; + if (etime_idle != 0) { + etime_idle = 0; + } else if (etime_count < INT16_MAX) { + etime_count++; + } /* - * Once, at 15 sec. notify callbacks - * and print the warning message. + * ETIME and requests in the queue. + * The first time (at 15 sec.) + * Log an error (just once). */ - if (etime_count == 1) { - /* Was: smb_iod_notify_down(vcp); */ - if (fscb && fscb->fscb_down) - smb_vc_walkshares(vcp, - fscb->fscb_down); + if (etime_count > 0 && + vcp->iod_noresp == B_FALSE) { + vcp->iod_noresp = B_TRUE; zprintf(vcp->vc_zoneid, "SMB server %s not responding\n", vcp->vc_srvname); } - /* - * At 30 sec. try sending an echo, and then - * once a minute thereafter. + * At 30 sec. try sending an echo, which + * should cause some response. */ - if ((etime_count & 3) == 2) { - (void) smb_iod_send_echo(vcp); + if (etime_count == 2) { + SMBIODEBUG("send echo\n"); + (void) smb_iod_send_echo(vcp, CRED()); + } + /* + * At 45 sec. give up on the connection + * and try to reconnect. + */ + if (etime_count == 3) { + SMB_VC_LOCK(vcp); + smb_iod_newstate(vcp, SMBIOD_ST_RECONNECT); + SMB_VC_UNLOCK(vcp); + SMB_TRAN_DISCONNECT(vcp); + break; } - continue; - } /* ETIME && requests in queue */ + } /* ETIME and requests in the queue */ if (error == ETIME) { /* - * If the IOD thread holds the last reference - * to this VC, let the IOD thread terminate. + * Nothing received and no active requests. + * + * If we've received nothing from the server for + * smb_iod_idle_keep_time seconds, and the IOD + * thread holds the last reference to this VC, + * move to state IDLE and drop the TCP session. + * The IDLE handler will destroy the VC unless + * vc_state goes to RECONNECT before then. */ - if (vcp->vc_co.co_usecount > 1) + etime_count = 0; + if (etime_idle < INT16_MAX) + etime_idle++; + if ((etime_idle * SMB_NBTIMO) < + smb_iod_idle_keep_time) continue; SMB_VC_LOCK(vcp); if (vcp->vc_co.co_usecount == 1) { - smb_iod_newstate(vcp, SMBIOD_ST_DEAD); + smb_iod_newstate(vcp, SMBIOD_ST_IDLE); SMB_VC_UNLOCK(vcp); + SMBIODEBUG("logoff & disconnect\n"); + (void) smb_iod_logoff(vcp, CRED()); + SMB_TRAN_DISCONNECT(vcp); error = 0; break; } @@ -431,91 +551,327 @@ smb_iod_recvall(struct smb_vc *vcp) if (error) { /* - * The recv. above returned some error - * we can't continue from i.e. ENOTCONN. - * It's dangerous to continue here. - * (possible infinite loop!) - * - * If we have requests enqueued, next - * state is reconnecting, else idle. + * The recv above returned an error indicating + * that our TCP session is no longer usable. + * Disconnect the session and get ready to + * reconnect. If we have pending requests, + * move to state reconnect immediately; + * otherwise move to state IDLE until a + * request is issued on this VC. */ - int state; SMB_VC_LOCK(vcp); - state = (vcp->iod_rqlist.tqh_first != NULL) ? - SMBIOD_ST_RECONNECT : SMBIOD_ST_IDLE; - smb_iod_newstate(vcp, state); + if (vcp->iod_rqlist.tqh_first != NULL) + smb_iod_newstate(vcp, SMBIOD_ST_RECONNECT); + else + smb_iod_newstate(vcp, SMBIOD_ST_IDLE); cv_broadcast(&vcp->vc_statechg); SMB_VC_UNLOCK(vcp); - error = 0; + SMB_TRAN_DISCONNECT(vcp); break; } /* * Received something. Yea! */ - if (etime_count) { - etime_count = 0; + etime_count = 0; + etime_idle = 0; + /* + * If we just completed a reconnect after logging + * "SMB server %s not responding" then log OK now. + */ + if (vcp->iod_noresp) { + vcp->iod_noresp = B_FALSE; zprintf(vcp->vc_zoneid, "SMB server %s OK\n", vcp->vc_srvname); + } - /* Was: smb_iod_notify_up(vcp); */ - if (fscb && fscb->fscb_up) - smb_vc_walkshares(vcp, fscb->fscb_up); + if ((vcp->vc_flags & SMBV_SMB2) != 0) { + error = smb2_iod_process(vcp, m); + } else { + error = smb1_iod_process(vcp, m); } /* - * Have an SMB packet. The SMB header was - * checked in smb_iod_recv1(). - * Find the request... + * Reconnect calls this in a loop with poll=TRUE + * We've received a response, so break now. */ - hp = mtod(m, uchar_t *); - /*LINTED*/ - mid = letohs(SMB_HDRMID(hp)); - SMBIODEBUG("mid %04x\n", (uint_t)mid); + if (poll) { + error = 0; + break; + } + } - rw_enter(&vcp->iod_rqlock, RW_READER); - TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { + return (error); +} - if (rqp->sr_mid != mid) - continue; +/* + * Have what should be an SMB1 reply. Check and parse the header, + * then use the message ID to find the request this belongs to and + * post it on that request. + * + * Returns an error if the reader should give up. + * To be safe, error if we read garbage. + */ +static int +smb1_iod_process(smb_vc_t *vcp, mblk_t *m) +{ + struct mdchain md; + struct smb_rq *rqp; + uint8_t cmd, sig[4]; + uint16_t mid; + int err, skip; + + m = m_pullup(m, SMB_HDRLEN); + if (m == NULL) + return (ENOMEM); + + /* + * Note: Intentionally do NOT md_done(&md) + * because that would free the message and + * we just want to peek here. + */ + md_initm(&md, m); - DTRACE_PROBE2(smb_iod_recvrq, - (smb_rq_t *), rqp, (mblk_t *), m); - m_dumpm(m); + /* + * Check the SMB header version and get the MID. + * + * The header version should be SMB1 except when we're + * doing SMB1-to-SMB2 negotiation, in which case we may + * see an SMB2 header with message ID=0 (only allowed in + * vc_state == SMBIOD_ST_CONNECTED -- negotiationg). + */ + err = md_get_mem(&md, sig, 4, MB_MSYSTEM); + if (err) + return (err); + if (sig[1] != 'S' || sig[2] != 'M' || sig[3] != 'B') { + goto bad_hdr; + } + switch (sig[0]) { + case SMB_HDR_V1: /* SMB1 */ + md_get_uint8(&md, &cmd); + /* Skip to and get the MID. At offset 5 now. */ + skip = SMB_HDR_OFF_MID - 5; + md_get_mem(&md, NULL, skip, MB_MSYSTEM); + err = md_get_uint16le(&md, &mid); + if (err) + return (err); + break; + case SMB_HDR_V2: /* SMB2+ */ + if (vcp->vc_state == SMBIOD_ST_CONNECTED) { + /* + * No need to look, can only be + * MID=0, cmd=negotiate + */ + cmd = SMB_COM_NEGOTIATE; + mid = 0; + break; + } + /* FALLTHROUGH */ + bad_hdr: + default: + SMBIODEBUG("Bad SMB hdr\n"); + m_freem(m); + return (EPROTO); + } - SMBRQ_LOCK(rqp); - if (rqp->sr_rp.md_top == NULL) { - md_initm(&rqp->sr_rp, m); + /* + * Find the reqeuest and post the reply + */ + rw_enter(&vcp->iod_rqlock, RW_READER); + TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { + + if (rqp->sr_mid != mid) + continue; + + DTRACE_PROBE2(iod_post_reply, + (smb_rq_t *), rqp, (mblk_t *), m); + m_dumpm(m); + + SMBRQ_LOCK(rqp); + if (rqp->sr_rp.md_top == NULL) { + md_initm(&rqp->sr_rp, m); + } else { + if (rqp->sr_flags & SMBR_MULTIPACKET) { + md_append_record(&rqp->sr_rp, m); } else { - if (rqp->sr_flags & SMBR_MULTIPACKET) { - md_append_record(&rqp->sr_rp, m); - } else { - SMBRQ_UNLOCK(rqp); - SMBSDEBUG("duplicate response %d " - "(ignored)\n", mid); - break; - } + SMBRQ_UNLOCK(rqp); + rqp = NULL; + break; } - smb_iod_rqprocessed_LH(rqp, 0, 0); - SMBRQ_UNLOCK(rqp); - break; } + smb_iod_rqprocessed_LH(rqp, 0, 0); + SMBRQ_UNLOCK(rqp); + break; + } + rw_exit(&vcp->iod_rqlock); + + if (rqp == NULL) { + if (cmd != SMB_COM_ECHO) { + SMBSDEBUG("drop resp: MID 0x%04x\n", (uint_t)mid); + } + m_freem(m); + /* + * Keep going. It's possible this reply came + * after the request timed out and went away. + */ + } + return (0); +} + +/* + * Have what should be an SMB2 reply. Check and parse the header, + * then use the message ID to find the request this belongs to and + * post it on that request. + * + * We also want to apply any credit grant in this reply now, + * rather than waiting for the owner to wake up. + */ +static int +smb2_iod_process(smb_vc_t *vcp, mblk_t *m) +{ + struct mdchain md; + struct smb_rq *rqp; + uint8_t sig[4]; + mblk_t *next_m = NULL; + uint64_t message_id, async_id; + uint32_t flags, next_cmd_off, status; + uint16_t command, credits_granted; + int err; - if (rqp == NULL) { - int cmd = SMB_HDRCMD(hp); +top: + m = m_pullup(m, SMB2_HDRLEN); + if (m == NULL) + return (ENOMEM); - if (cmd != SMB_COM_ECHO) - SMBSDEBUG("drop resp: mid %d, cmd %d\n", - (uint_t)mid, cmd); -/* smb_printrqlist(vcp); */ + /* + * Note: Intentionally do NOT md_done(&md) + * because that would free the message and + * we just want to peek here. + */ + md_initm(&md, m); + + /* + * Check the SMB header. Must be SMB2 + * (and later, could be SMB3 encrypted) + */ + err = md_get_mem(&md, sig, 4, MB_MSYSTEM); + if (err) + return (err); + if (sig[1] != 'S' || sig[2] != 'M' || sig[3] != 'B') { + goto bad_hdr; + } + switch (sig[0]) { + case SMB_HDR_V2: + break; + case SMB_HDR_V3E: + /* + * Todo: If encryption enabled, decrypt the message + * and restart processing on the cleartext. + */ + /* FALLTHROUGH */ + bad_hdr: + default: + SMBIODEBUG("Bad SMB2 hdr\n"); + m_freem(m); + return (EPROTO); + } + + /* + * Parse the rest of the SMB2 header, + * skipping what we don't need. + */ + md_get_uint32le(&md, NULL); /* length, credit_charge */ + md_get_uint32le(&md, &status); + md_get_uint16le(&md, &command); + md_get_uint16le(&md, &credits_granted); + md_get_uint32le(&md, &flags); + md_get_uint32le(&md, &next_cmd_off); + md_get_uint64le(&md, &message_id); + if (flags & SMB2_FLAGS_ASYNC_COMMAND) { + md_get_uint64le(&md, &async_id); + } else { + /* PID, TID (not needed) */ + async_id = 0; + } + + /* + * If this is a compound reply, split it. + * Next must be 8-byte aligned. + */ + if (next_cmd_off != 0) { + if ((next_cmd_off & 7) != 0) + SMBIODEBUG("Misaligned next cmd\n"); + else + next_m = m_split(m, next_cmd_off, 1); + } + + /* + * Apply the credit grant + */ + rw_enter(&vcp->iod_rqlock, RW_WRITER); + vcp->vc2_limit_message_id += credits_granted; + + /* + * Find the reqeuest and post the reply + */ + rw_downgrade(&vcp->iod_rqlock); + TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { + + if (rqp->sr2_messageid != message_id) + continue; + + DTRACE_PROBE2(iod_post_reply, + (smb_rq_t *), rqp, (mblk_t *), m); + m_dumpm(m); + + /* + * If this is an interim response, just save the + * async ID but don't wakup the request. + * Don't need SMBRQ_LOCK for this. + */ + if (status == NT_STATUS_PENDING && async_id != 0) { + rqp->sr2_rspasyncid = async_id; m_freem(m); + break; } - rw_exit(&vcp->iod_rqlock); + SMBRQ_LOCK(rqp); + if (rqp->sr_rp.md_top == NULL) { + md_initm(&rqp->sr_rp, m); + } else { + SMBRQ_UNLOCK(rqp); + rqp = NULL; + break; + } + smb_iod_rqprocessed_LH(rqp, 0, 0); + SMBRQ_UNLOCK(rqp); + break; } + rw_exit(&vcp->iod_rqlock); - return (error); + if (rqp == NULL) { + if (command != SMB2_ECHO) { + SMBSDEBUG("drop resp: MID %lld\n", + (long long)message_id); + } + m_freem(m); + /* + * Keep going. It's possible this reply came + * after the request timed out and went away. + */ + } + + /* + * If we split a compound reply, continue with the + * next part of the compound. + */ + if (next_m != NULL) { + m = next_m; + goto top; + } + + return (0); } /* @@ -529,126 +885,256 @@ smb_iod_recvall(struct smb_vc *vcp) * The smb_smb_echo call uses SMBR_INTERNAL * to avoid calling smb_iod_sendall(). */ -int -smb_iod_send_echo(smb_vc_t *vcp) +static int +smb_iod_send_echo(smb_vc_t *vcp, cred_t *cr) { smb_cred_t scred; - int err; + int err, tmo = SMBNOREPLYWAIT; - smb_credinit(&scred, NULL); - err = smb_smb_echo(vcp, &scred, SMBNOREPLYWAIT); + ASSERT(vcp->iod_thr == curthread); + + smb_credinit(&scred, cr); + if ((vcp->vc_flags & SMBV_SMB2) != 0) { + err = smb2_smb_echo(vcp, &scred, tmo); + } else { + err = smb_smb_echo(vcp, &scred, tmo); + } smb_credrele(&scred); return (err); } /* - * The IOD thread is now just a "reader", - * so no more smb_iod_request(). Yea! + * Helper for smb1_iod_addrq, smb2_iod_addrq + * Returns zero if interrupted, else 1. */ +static int +smb_iod_muxwait(smb_vc_t *vcp, boolean_t sig_ok) +{ + int rc; + + SMB_VC_LOCK(vcp); + vcp->iod_muxwant++; + if (sig_ok) { + rc = cv_wait_sig(&vcp->iod_muxwait, &vcp->vc_lock); + } else { + cv_wait(&vcp->iod_muxwait, &vcp->vc_lock); + rc = 1; + } + vcp->iod_muxwant--; + SMB_VC_UNLOCK(vcp); + + return (rc); +} /* - * Place request in the queue, and send it now if possible. + * Place request in the queue, and send it. * Called with no locks held. + * + * Called for SMB1 only + * + * The logic for how we limit active requests differs between + * SMB1 and SMB2. With SMB1 it's a simple counter ioc_muxcnt. */ int -smb_iod_addrq(struct smb_rq *rqp) +smb1_iod_addrq(struct smb_rq *rqp) { struct smb_vc *vcp = rqp->sr_vc; - int error, save_newrq; + uint16_t need; + boolean_t sig_ok = + (rqp->sr_flags & SMBR_NOINTR_SEND) == 0; ASSERT(rqp->sr_cred); + ASSERT((vcp->vc_flags & SMBV_SMB2) == 0); + rqp->sr_owner = curthread; + + rw_enter(&vcp->iod_rqlock, RW_WRITER); + +recheck: /* - * State should be correct after the check in - * smb_rq_enqueue(), but we dropped locks... + * Internal requests can be added in any state, + * but normal requests only in state active. */ - if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + if ((rqp->sr_flags & SMBR_INTERNAL) == 0 && + vcp->vc_state != SMBIOD_ST_VCACTIVE) { SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + rw_exit(&vcp->iod_rqlock); return (ENOTCONN); } /* - * Requests from the IOD itself are marked _INTERNAL, - * and get some special treatment to avoid blocking - * the reader thread (so we don't deadlock). - * The request is not yet on the queue, so we can - * modify it's state here without locks. - * Only thing using this now is ECHO. + * If we're at the limit of active requests, block until + * enough requests complete so we can make ours active. + * Wakeup in smb_iod_removerq(). + * + * Normal callers leave one slot free, so internal + * callers can have the last slot if needed. */ - rqp->sr_owner = curthread; - if (rqp->sr_owner == vcp->iod_thr) { - rqp->sr_flags |= SMBR_INTERNAL; - - /* - * This is a request from the IOD thread. - * Always send directly from this thread. - * Note lock order: iod_rqlist, vc_sendlock - */ + need = 1; + if ((rqp->sr_flags & SMBR_INTERNAL) == 0) + need++; + if ((vcp->iod_muxcnt + need) > vcp->vc_maxmux) { + rw_exit(&vcp->iod_rqlock); + if (rqp->sr_flags & SMBR_INTERNAL) + return (EBUSY); + if (smb_iod_muxwait(vcp, sig_ok) == 0) + return (EINTR); rw_enter(&vcp->iod_rqlock, RW_WRITER); - TAILQ_INSERT_HEAD(&vcp->iod_rqlist, rqp, sr_link); - rw_downgrade(&vcp->iod_rqlock); + goto recheck; + } - /* - * Note: iod_sendrq expects vc_sendlock, - * so take that here, but carefully: - * Never block the IOD thread here. - */ - if (sema_tryp(&vcp->vc_sendlock) == 0) { - SMBIODEBUG("sendlock busy\n"); - error = EAGAIN; - } else { - /* Have vc_sendlock */ - error = smb_iod_sendrq(rqp); - sema_v(&vcp->vc_sendlock); - } + /* + * Add this request to the active list and send it. + * For SMB2 we may have a sequence of compounded + * requests, in which case we must add them all. + * They're sent as a compound in smb2_iod_sendrq. + */ + rqp->sr_mid = vcp->vc_next_mid++; + /* If signing, set the signing sequence numbers. */ + if (vcp->vc_mackey != NULL && (rqp->sr_rqflags2 & + SMB_FLAGS2_SECURITY_SIGNATURE) != 0) { + rqp->sr_seqno = vcp->vc_next_seq++; + rqp->sr_rseqno = vcp->vc_next_seq++; + } + vcp->iod_muxcnt++; + TAILQ_INSERT_TAIL(&vcp->iod_rqlist, rqp, sr_link); + smb1_iod_sendrq(rqp); - rw_exit(&vcp->iod_rqlock); + rw_exit(&vcp->iod_rqlock); + return (0); +} - /* - * In the non-error case, _removerq - * is done by either smb_rq_reply - * or smb_iod_waitrq. - */ - if (error) - smb_iod_removerq(rqp); +/* + * Place request in the queue, and send it. + * Called with no locks held. + * + * Called for SMB2 only. + * + * With SMB2 we have a range of valid message IDs, and we may + * only send requests when we can assign a message ID within + * the valid range. We may need to wait here for some active + * request to finish (and update vc2_limit_message_id) before + * we can get message IDs for our new request(s). Another + * difference is that the request sequence we're waiting to + * add here may require multipe message IDs, either due to + * either compounding or multi-credit requests. Therefore + * we need to wait for the availibility of how ever many + * message IDs are required by our request sequence. + */ +int +smb2_iod_addrq(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct smb_rq *c_rqp; /* compound req */ + uint16_t charge; + boolean_t sig_ok = + (rqp->sr_flags & SMBR_NOINTR_SEND) == 0; - return (error); + ASSERT(rqp->sr_cred != NULL); + ASSERT((vcp->vc_flags & SMBV_SMB2) != 0); + + /* + * Figure out the credit charges + * No multi-credit messages yet. + */ + rqp->sr2_totalcreditcharge = rqp->sr2_creditcharge; + c_rqp = rqp->sr2_compound_next; + while (c_rqp != NULL) { + rqp->sr2_totalcreditcharge += c_rqp->sr2_creditcharge; + c_rqp = c_rqp->sr2_compound_next; } + /* + * Internal request must not be compounded + * and should use exactly one credit. + */ + if (rqp->sr_flags & SMBR_INTERNAL) { + if (rqp->sr2_compound_next != NULL) { + ASSERT(0); + return (EINVAL); + } + } + + rqp->sr_owner = curthread; + rw_enter(&vcp->iod_rqlock, RW_WRITER); - TAILQ_INSERT_TAIL(&vcp->iod_rqlist, rqp, sr_link); - /* iod_rqlock/WRITER protects iod_newrq */ - save_newrq = vcp->iod_newrq; - vcp->iod_newrq++; +recheck: + /* + * Internal requests can be added in any state, + * but normal requests only in state active. + */ + if ((rqp->sr_flags & SMBR_INTERNAL) == 0 && + vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + rw_exit(&vcp->iod_rqlock); + return (ENOTCONN); + } - rw_exit(&vcp->iod_rqlock); + /* + * If we're at the limit of active requests, block until + * enough requests complete so we can make ours active. + * Wakeup in smb_iod_removerq(). + * + * Normal callers leave one slot free, so internal + * callers can have the last slot if needed. + */ + charge = rqp->sr2_totalcreditcharge; + if ((rqp->sr_flags & SMBR_INTERNAL) == 0) + charge++; + if ((vcp->vc2_next_message_id + charge) > + vcp->vc2_limit_message_id) { + rw_exit(&vcp->iod_rqlock); + if (rqp->sr_flags & SMBR_INTERNAL) + return (EBUSY); + if (smb_iod_muxwait(vcp, sig_ok) == 0) + return (EINTR); + rw_enter(&vcp->iod_rqlock, RW_WRITER); + goto recheck; + } /* - * Now send any requests that need to be sent, - * including the one we just put on the list. - * Only the thread that found iod_newrq==0 - * needs to run the send loop. + * Add this request to the active list and send it. + * For SMB2 we may have a sequence of compounded + * requests, in which case we must add them all. + * They're sent as a compound in smb2_iod_sendrq. */ - if (save_newrq == 0) - smb_iod_sendall(vcp); + rqp->sr2_messageid = vcp->vc2_next_message_id; + vcp->vc2_next_message_id += rqp->sr2_creditcharge; + TAILQ_INSERT_TAIL(&vcp->iod_rqlist, rqp, sr_link); + + c_rqp = rqp->sr2_compound_next; + while (c_rqp != NULL) { + c_rqp->sr2_messageid = vcp->vc2_next_message_id; + vcp->vc2_next_message_id += c_rqp->sr2_creditcharge; + TAILQ_INSERT_TAIL(&vcp->iod_rqlist, c_rqp, sr_link); + c_rqp = c_rqp->sr2_compound_next; + } + smb2_iod_sendrq(rqp); + + rw_exit(&vcp->iod_rqlock); return (0); } /* * Mark an SMBR_MULTIPACKET request as * needing another send. Similar to the - * "normal" part of smb_iod_addrq. + * "normal" part of smb1_iod_addrq. + * Only used by SMB1 */ int -smb_iod_multirq(struct smb_rq *rqp) +smb1_iod_multirq(struct smb_rq *rqp) { struct smb_vc *vcp = rqp->sr_vc; - int save_newrq; ASSERT(rqp->sr_flags & SMBR_MULTIPACKET); + if (vcp->vc_flags & SMBV_SMB2) { + ASSERT("!SMB2?"); + return (EINVAL); + } + if (rqp->sr_flags & SMBR_INTERNAL) return (EINVAL); @@ -661,32 +1147,36 @@ smb_iod_multirq(struct smb_rq *rqp) /* Already on iod_rqlist, just reset state. */ rqp->sr_state = SMBRQ_NOTSENT; - - /* iod_rqlock/WRITER protects iod_newrq */ - save_newrq = vcp->iod_newrq; - vcp->iod_newrq++; + smb1_iod_sendrq(rqp); rw_exit(&vcp->iod_rqlock); - /* - * Now send any requests that need to be sent, - * including the one we just marked NOTSENT. - * Only the thread that found iod_newrq==0 - * needs to run the send loop. - */ - if (save_newrq == 0) - smb_iod_sendall(vcp); - return (0); } - +/* + * Remove a request from the active list, and + * wake up requests waiting to go active. + * + * Shared by SMB1 + SMB2 + * + * The logic for how we limit active requests differs between + * SMB1 and SMB2. With SMB1 it's a simple counter ioc_muxcnt. + * With SMB2 we have a range of valid message IDs, and when we + * retire the oldest request we need to keep track of what is + * now the oldest message ID. In both cases, after we take a + * request out of the list here, we should be able to wake up + * a request waiting to get in the active list. + */ void smb_iod_removerq(struct smb_rq *rqp) { + struct smb_rq *rqp2; struct smb_vc *vcp = rqp->sr_vc; + boolean_t was_head = B_FALSE; rw_enter(&vcp->iod_rqlock, RW_WRITER); + #ifdef QUEUEDEBUG /* * Make sure we have not already removed it. @@ -695,30 +1185,47 @@ smb_iod_removerq(struct smb_rq *rqp) */ ASSERT(rqp->sr_link.tqe_next != (void *)1L); #endif + + if (TAILQ_FIRST(&vcp->iod_rqlist) == rqp) + was_head = B_TRUE; TAILQ_REMOVE(&vcp->iod_rqlist, rqp, sr_link); - rw_exit(&vcp->iod_rqlock); -} + if (vcp->vc_flags & SMBV_SMB2) { + rqp2 = TAILQ_FIRST(&vcp->iod_rqlist); + if (was_head && rqp2 != NULL) { + /* Do we still need this? */ + vcp->vc2_oldest_message_id = + rqp2->sr2_messageid; + } + } else { + ASSERT(vcp->iod_muxcnt > 0); + vcp->iod_muxcnt--; + } + rw_exit(&vcp->iod_rqlock); + /* + * If there are requests waiting for "mux" slots, + * wake one. + */ + SMB_VC_LOCK(vcp); + if (vcp->iod_muxwant != 0) + cv_signal(&vcp->iod_muxwait); + SMB_VC_UNLOCK(vcp); +} /* * Wait for a request to complete. - * - * For normal requests, we need to deal with - * ioc_muxcnt dropping below vc_maxmux by - * making arrangements to send more... */ int smb_iod_waitrq(struct smb_rq *rqp) { struct smb_vc *vcp = rqp->sr_vc; clock_t tr, tmo1, tmo2; - int error, rc; + int error; if (rqp->sr_flags & SMBR_INTERNAL) { - ASSERT((rqp->sr_flags & SMBR_MULTIPACKET) == 0); - smb_iod_removerq(rqp); - return (EAGAIN); + /* XXX - Do we ever take this path now? */ + return (smb_iod_waitrq_int(rqp)); } /* @@ -730,35 +1237,6 @@ smb_iod_waitrq(struct smb_rq *rqp) SMBRQ_LOCK(rqp); /* - * First, wait for the request to be sent. Normally the send - * has already happened by the time we get here. However, if - * we have more than maxmux entries in the request list, our - * request may not be sent until other requests complete. - * The wait in this case is due to local I/O demands, so - * we don't want the server response timeout to apply. - * - * If a request is allowed to interrupt this wait, then the - * request is cancelled and never sent OTW. Some kinds of - * requests should never be cancelled (i.e. close) and those - * are marked SMBR_NOINTR_SEND so they either go eventually, - * or a connection close will terminate them with ENOTCONN. - */ - while (rqp->sr_state == SMBRQ_NOTSENT) { - rqp->sr_flags |= SMBR_SENDWAIT; - if (rqp->sr_flags & SMBR_NOINTR_SEND) { - cv_wait(&rqp->sr_cond, &rqp->sr_lock); - rc = 1; - } else - rc = cv_wait_sig(&rqp->sr_cond, &rqp->sr_lock); - rqp->sr_flags &= ~SMBR_SENDWAIT; - if (rc == 0) { - SMBIODEBUG("EINTR in sendwait, rqp=%p\n", rqp); - error = EINTR; - goto out; - } - } - - /* * The request has been sent. Now wait for the response, * with the timeout specified for this request. * Compute all the deadlines now, so we effectively @@ -791,17 +1269,8 @@ smb_iod_waitrq(struct smb_rq *rqp) goto out; } if (tr < 0) { -#ifdef DTRACE_PROBE DTRACE_PROBE1(smb_iod_waitrq1, (smb_rq_t *), rqp); -#endif -#ifdef NOT_YET - /* Want this to go ONLY to the user. */ - uprintf("SMB server %s has not responded" - " to request %d after %d seconds..." - " (still waiting).\n", vcp->vc_srvname, - rqp->sr_mid, smb_timo_notice); -#endif } } @@ -820,17 +1289,8 @@ smb_iod_waitrq(struct smb_rq *rqp) goto out; } if (tr < 0) { -#ifdef DTRACE_PROBE DTRACE_PROBE1(smb_iod_waitrq2, (smb_rq_t *), rqp); -#endif -#ifdef NOT_YET - /* Want this to go ONLY to the user. */ - uprintf("SMB server %s has not responded" - " to request %d after %d seconds..." - " (giving up).\n", vcp->vc_srvname, - rqp->sr_mid, rqp->sr_timo); -#endif error = ETIME; goto out; } @@ -849,13 +1309,34 @@ out: if ((rqp->sr_flags & SMBR_MULTIPACKET) == 0) smb_iod_removerq(rqp); - /* - * Some request has been completed. - * If we reached the mux limit, - * re-run the send loop... - */ - if (vcp->iod_muxfull) - smb_iod_sendall(vcp); + return (error); +} + +/* + * Internal variant of smb_iod_waitrq(), for use in + * requests run by the IOD (reader) thread itself. + * Block only long enough to receive one reply. + */ +int +smb_iod_waitrq_int(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + int timeleft = rqp->sr_timo; + int error; + + ASSERT((rqp->sr_flags & SMBR_MULTIPACKET) == 0); +again: + error = smb_iod_recvall(vcp, B_TRUE); + if (error == ETIME) { + /* We waited SMB_NBTIMO sec. */ + timeleft -= SMB_NBTIMO; + if (timeleft > 0) + goto again; + } + + smb_iod_removerq(rqp); + if (rqp->sr_state != SMBRQ_NOTIFIED) + error = ETIME; return (error); } @@ -885,79 +1366,298 @@ smb_iod_shutdown_share(struct smb_share *ssp) } /* - * Send all requests that need sending. - * Called from _addrq, _multirq, _waitrq + * Ioctl functions called by the user-level I/O Deamon (IOD) + * to bring up and service a connection to some SMB server. */ -void -smb_iod_sendall(smb_vc_t *vcp) + +/* + * Handle ioctl SMBIOC_IOD_CONNECT + */ +int +nsmb_iod_connect(struct smb_vc *vcp, cred_t *cr) { - struct smb_rq *rqp; - int error, muxcnt; + int err, val; + + ASSERT(vcp->iod_thr == curthread); + + if (vcp->vc_state != SMBIOD_ST_RECONNECT) { + cmn_err(CE_NOTE, "iod_connect: bad state %d", vcp->vc_state); + return (EINVAL); + } /* - * Clear "newrq" to make sure threads adding - * new requests will run this function again. + * Putting a TLI endpoint back in the right state for a new + * connection is a bit tricky. In theory, this could be: + * SMB_TRAN_DISCONNECT(vcp); + * SMB_TRAN_UNBIND(vcp); + * but that method often results in TOUTSTATE errors. + * It's easier to just close it and open a new endpoint. */ - rw_enter(&vcp->iod_rqlock, RW_WRITER); - vcp->iod_newrq = 0; + SMB_VC_LOCK(vcp); + if (vcp->vc_tdata) + SMB_TRAN_DONE(vcp); + err = SMB_TRAN_CREATE(vcp, cr); + SMB_VC_UNLOCK(vcp); + if (err != 0) + return (err); /* - * We only read iod_rqlist, so downgrade rwlock. - * This allows the IOD to handle responses while - * some requesting thread may be blocked in send. + * Set various options on this endpoint. + * Keep going in spite of errors. */ - rw_downgrade(&vcp->iod_rqlock); + val = smb_tcpsndbuf; + err = SMB_TRAN_SETPARAM(vcp, SMBTP_SNDBUF, &val); + if (err != 0) { + cmn_err(CE_NOTE, "iod_connect: setopt SNDBUF, err=%d", err); + } + val = smb_tcprcvbuf; + err = SMB_TRAN_SETPARAM(vcp, SMBTP_RCVBUF, &val); + if (err != 0) { + cmn_err(CE_NOTE, "iod_connect: setopt RCVBUF, err=%d", err); + } + val = 1; + err = SMB_TRAN_SETPARAM(vcp, SMBTP_KEEPALIVE, &val); + if (err != 0) { + cmn_err(CE_NOTE, "iod_connect: setopt KEEPALIVE, err=%d", err); + } + val = 1; + err = SMB_TRAN_SETPARAM(vcp, SMBTP_TCP_NODELAY, &val); + if (err != 0) { + cmn_err(CE_NOTE, "iod_connect: setopt TCP_NODELAY, err=%d", err); + } + val = smb_connect_timeout * 1000; + err = SMB_TRAN_SETPARAM(vcp, SMBTP_TCP_CON_TMO, &val); + if (err != 0) { + cmn_err(CE_NOTE, "iod_connect: setopt TCP con tmo, err=%d", err); + } /* - * Serialize to prevent multiple senders. - * Note lock order: iod_rqlock, vc_sendlock + * Bind and connect */ - sema_p(&vcp->vc_sendlock); + err = SMB_TRAN_BIND(vcp, NULL); + if (err != 0) { + cmn_err(CE_NOTE, "iod_connect: t_kbind: err=%d", err); + /* Continue on and try connect. */ + } + err = SMB_TRAN_CONNECT(vcp, &vcp->vc_srvaddr.sa); + /* + * No cmn_err here, as connect failures are normal, i.e. + * when a server has multiple addresses and only some are + * routed for us. (libsmbfs tries them all) + */ + if (err == 0) { + SMB_VC_LOCK(vcp); + smb_iod_newstate(vcp, SMBIOD_ST_CONNECTED); + SMB_VC_UNLOCK(vcp); + } /* else stay in state reconnect */ + + return (err); +} + +/* + * Handle ioctl SMBIOC_IOD_NEGOTIATE + * Do the whole SMB1/SMB2 negotiate + * + * This is where we send our first request to the server. + * If this is the first time we're talking to this server, + * (meaning not a reconnect) then we don't know whether + * the server supports SMB2, so we need to use the weird + * SMB1-to-SMB2 negotiation. That's where we send an SMB1 + * negotiate including dialect "SMB 2.???" and if the + * server supports SMB2 we get an SMB2 reply -- Yes, an + * SMB2 reply to an SMB1 request. A strange protocol... + * + * If on the other hand we already know the server supports + * SMB2 (because this is a reconnect) or if the client side + * has disabled SMB1 entirely, we'll skip the SMB1 part. + */ +int +nsmb_iod_negotiate(struct smb_vc *vcp, cred_t *cr) +{ + struct smb_sopt *sv = &vcp->vc_sopt; + smb_cred_t scred; + int err = 0; + + ASSERT(vcp->iod_thr == curthread); + + smb_credinit(&scred, cr); + + if (vcp->vc_state != SMBIOD_ST_CONNECTED) { + cmn_err(CE_NOTE, "iod_negotiate: bad state %d", vcp->vc_state); + err = EINVAL; + goto out; + } + + if (vcp->vc_maxver == 0 || vcp->vc_minver > vcp->vc_maxver) { + err = EINVAL; + goto out; + } /* - * Walk the list of requests and send when possible. - * We avoid having more than vc_maxmux requests - * outstanding to the server by traversing only - * vc_maxmux entries into this list. Simple! + * (Re)init negotiated values */ - ASSERT(vcp->vc_maxmux > 0); - error = muxcnt = 0; - TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { + bzero(sv, sizeof (*sv)); + vcp->vc2_next_message_id = 0; + vcp->vc2_limit_message_id = 1; + vcp->vc2_session_id = 0; + vcp->vc_next_seq = 0; - if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { - error = ENOTCONN; /* stop everything! */ - break; - } + /* + * If this was reconnect, get rid of the old MAC key + * and session key. + */ + SMB_VC_LOCK(vcp); + if (vcp->vc_mackey != NULL) { + kmem_free(vcp->vc_mackey, vcp->vc_mackeylen); + vcp->vc_mackey = NULL; + vcp->vc_mackeylen = 0; + } + if (vcp->vc_ssnkey != NULL) { + kmem_free(vcp->vc_ssnkey, vcp->vc_ssnkeylen); + vcp->vc_ssnkey = NULL; + vcp->vc_ssnkeylen = 0; + } + SMB_VC_UNLOCK(vcp); - if (rqp->sr_state == SMBRQ_NOTSENT) { - error = smb_iod_sendrq(rqp); - if (error) - break; + /* + * If this is not an SMB2 reconect (SMBV_SMB2 not set), + * and if SMB1 is enabled, do SMB1 neogotiate. Then + * if either SMB1-to-SMB2 negotiate tells us we should + * switch to SMB2, or the local configuration has + * disabled SMB1, set the SMBV_SMB2 flag. + * + * Note that vc_maxver is handled in smb_smb_negotiate + * so we never get sv_proto == SMB_DIALECT_SMB2_FF when + * the local configuration disables SMB2, and therefore + * we won't set the SMBV_SMB2 flag. + */ + if ((vcp->vc_flags & SMBV_SMB2) == 0) { + if (vcp->vc_minver < SMB2_DIALECT_BASE) { + /* + * SMB1 is enabled + */ + err = smb_smb_negotiate(vcp, &scred); + if (err != 0) + goto out; } - - if (++muxcnt == vcp->vc_maxmux) { - SMBIODEBUG("muxcnt == vc_maxmux\n"); - break; + /* + * If SMB1-to-SMB2 negotiate told us we should + * switch to SMB2, or if the local configuration + * disables SMB1, set the SMB2 flag. + */ + if (sv->sv_proto == SMB_DIALECT_SMB2_FF || + vcp->vc_minver >= SMB2_DIALECT_BASE) { + /* + * Switch this VC to SMB2. + */ + SMB_VC_LOCK(vcp); + vcp->vc_flags |= SMBV_SMB2; + SMB_VC_UNLOCK(vcp); } + } + /* + * If this is an SMB2 reconnect (SMBV_SMB2 was set before this + * function was called), or SMB1-to-SMB2 negotiate indicated + * we should switch to SMB2, or we have SMB1 disabled (both + * cases set SMBV_SMB2 above), then do SMB2 negotiate. + */ + if ((vcp->vc_flags & SMBV_SMB2) != 0) { + err = smb2_smb_negotiate(vcp, &scred); } +out: + if (err == 0) { + SMB_VC_LOCK(vcp); + smb_iod_newstate(vcp, SMBIOD_ST_NEGOTIATED); + SMB_VC_UNLOCK(vcp); + } /* - * If we have vc_maxmux requests outstanding, - * arrange for _waitrq to call _sendall as - * requests are completed. + * (else) leave state as it was. + * User-level will either close this handle (if connecting + * for the first time) or call rcfail and then try again. */ - vcp->iod_muxfull = - (muxcnt < vcp->vc_maxmux) ? 0 : 1; - sema_v(&vcp->vc_sendlock); - rw_exit(&vcp->iod_rqlock); + smb_credrele(&scred); + + return (err); } +/* + * Handle ioctl SMBIOC_IOD_SSNSETUP + * Do either SMB1 or SMB2 session setup (one call/reply) + */ int -smb_iod_vc_work(struct smb_vc *vcp, cred_t *cr) +nsmb_iod_ssnsetup(struct smb_vc *vcp, cred_t *cr) { - struct file *fp = NULL; + smb_cred_t scred; + int err; + + ASSERT(vcp->iod_thr == curthread); + + switch (vcp->vc_state) { + case SMBIOD_ST_NEGOTIATED: + case SMBIOD_ST_AUTHCONT: + break; + default: + return (EINVAL); + } + + smb_credinit(&scred, cr); + if (vcp->vc_flags & SMBV_SMB2) + err = smb2_smb_ssnsetup(vcp, &scred); + else + err = smb_smb_ssnsetup(vcp, &scred); + smb_credrele(&scred); + + SMB_VC_LOCK(vcp); + switch (err) { + case 0: + smb_iod_newstate(vcp, SMBIOD_ST_AUTHOK); + break; + case EINPROGRESS: /* MORE_PROCESSING_REQUIRED */ + smb_iod_newstate(vcp, SMBIOD_ST_AUTHCONT); + break; + default: + smb_iod_newstate(vcp, SMBIOD_ST_AUTHFAIL); + break; + } + SMB_VC_UNLOCK(vcp); + + return (err); +} + +static int +smb_iod_logoff(struct smb_vc *vcp, cred_t *cr) +{ + smb_cred_t scred; + int err; + + ASSERT(vcp->iod_thr == curthread); + + smb_credinit(&scred, cr); + if (vcp->vc_flags & SMBV_SMB2) + err = smb2_smb_logoff(vcp, &scred); + else + err = smb_smb_logoff(vcp, &scred); + smb_credrele(&scred); + + return (err); +} + +/* + * Handle ioctl SMBIOC_IOD_WORK + * + * The smbiod agent calls this after authentication to become + * the reader for this session, so long as that's possible. + * This should only return non-zero if we want that agent to + * give up on this VC permanently. + */ +/* ARGSUSED */ +int +smb_iod_vc_work(struct smb_vc *vcp, int flags, cred_t *cr) +{ + smbioc_ssn_work_t *wk = &vcp->vc_work; int err = 0; /* @@ -967,19 +1667,57 @@ smb_iod_vc_work(struct smb_vc *vcp, cred_t *cr) ASSERT(vcp->iod_thr == curthread); /* - * Get the network transport file pointer, - * and "loan" it to our transport module. + * Should be in state... */ - if ((fp = getf(vcp->vc_tran_fd)) == NULL) { - err = EBADF; - goto out; + if (vcp->vc_state != SMBIOD_ST_AUTHOK) { + cmn_err(CE_NOTE, "iod_vc_work: bad state %d", vcp->vc_state); + return (EINVAL); } - if ((err = SMB_TRAN_LOAN_FP(vcp, fp, cr)) != 0) - goto out; /* - * In case of reconnect, tell any enqueued requests - * then can GO! + * Update the session key and initialize SMB signing. + * + * This implementation does not use multiple SMB sessions per + * TCP connection (where only the first session key is used) + * so we always have a new session key here. Sanity check the + * length from user space. Normally 16 or 32. + */ + if (wk->wk_u_ssnkey_len > 1024) { + cmn_err(CE_NOTE, "iod_vc_work: ssn key too long"); + return (EINVAL); + } + + ASSERT(vcp->vc_ssnkey == NULL); + SMB_VC_LOCK(vcp); + if (wk->wk_u_ssnkey_len != 0 && + wk->wk_u_ssnkey_buf.lp_ptr != NULL) { + vcp->vc_ssnkeylen = wk->wk_u_ssnkey_len; + vcp->vc_ssnkey = kmem_alloc(vcp->vc_ssnkeylen, KM_SLEEP); + if (ddi_copyin(wk->wk_u_ssnkey_buf.lp_ptr, + vcp->vc_ssnkey, vcp->vc_ssnkeylen, flags) != 0) { + err = EFAULT; + } + } + SMB_VC_UNLOCK(vcp); + if (err) + return (err); + + /* + * If we have a session key, derive the MAC key for SMB signing. + * If this was a NULL session, we might have no session key. + */ + ASSERT(vcp->vc_mackey == NULL); + if (vcp->vc_ssnkey != NULL) { + if (vcp->vc_flags & SMBV_SMB2) + err = smb2_sign_init(vcp); + else + err = smb_sign_init(vcp); + if (err != 0) + return (err); + } + + /* + * Tell any enqueued requests they can start. */ SMB_VC_LOCK(vcp); vcp->vc_genid++; /* possibly new connection */ @@ -998,9 +1736,11 @@ smb_iod_vc_work(struct smb_vc *vcp, cred_t *cr) smb_vc_walkshares(vcp, fscb->fscb_connect); /* - * Run the "reader" loop. + * Run the "reader" loop. An error return here is normal + * (i.e. when we need to reconnect) so ignore errors. + * Note: This call updates the vc_state. */ - err = smb_iod_recvall(vcp); + (void) smb_iod_recvall(vcp, B_FALSE); /* * The reader loop returned, so we must have a @@ -1021,26 +1761,24 @@ smb_iod_vc_work(struct smb_vc *vcp, cred_t *cr) */ smb_iod_invrq(vcp); -out: - /* Recall the file descriptor loan. */ - (void) SMB_TRAN_LOAN_FP(vcp, NULL, cr); - if (fp != NULL) { - releasef(vcp->vc_tran_fd); - } - return (err); } /* - * Wait around for someone to ask to use this VC. - * If the VC has only the IOD reference, then - * wait only a minute or so, then drop it. + * Handle ioctl SMBIOC_IOD_IDLE + * + * Wait around for someone to ask to use this VC again after the + * TCP session has closed. When one of the connected trees adds a + * request, smb_iod_reconnect will set vc_state to RECONNECT and + * wake this cv_wait. When a VC ref. goes away in smb_vc_rele, + * that also signals this wait so we can re-check whether we + * now hold the last ref. on this VC (and can destroy it). */ int smb_iod_vc_idle(struct smb_vc *vcp) { - clock_t tr, delta = SEC_TO_TICK(15); int err = 0; + boolean_t destroy = B_FALSE; /* * This is called by the one-and-only @@ -1048,30 +1786,50 @@ smb_iod_vc_idle(struct smb_vc *vcp) */ ASSERT(vcp->iod_thr == curthread); + /* + * Should be in state... + */ + if (vcp->vc_state != SMBIOD_ST_IDLE && + vcp->vc_state != SMBIOD_ST_RECONNECT) { + cmn_err(CE_NOTE, "iod_vc_idle: bad state %d", vcp->vc_state); + return (EINVAL); + } + SMB_VC_LOCK(vcp); - while (vcp->vc_state == SMBIOD_ST_IDLE) { - tr = cv_reltimedwait_sig(&vcp->iod_idle, &vcp->vc_lock, - delta, TR_CLOCK_TICK); - if (tr == 0) { + + while (vcp->vc_state == SMBIOD_ST_IDLE && + vcp->vc_co.co_usecount > 1) { + if (cv_wait_sig(&vcp->iod_idle, &vcp->vc_lock) == 0) { err = EINTR; break; } - if (tr < 0) { - /* timeout */ - if (vcp->vc_co.co_usecount == 1) { - /* Let this IOD terminate. */ - smb_iod_newstate(vcp, SMBIOD_ST_DEAD); - /* nobody to cv_broadcast */ - break; - } - } } + if (vcp->vc_state == SMBIOD_ST_IDLE && + vcp->vc_co.co_usecount == 1) { + /* + * We were woken because we now have the last ref. + * Arrange for this VC to be destroyed now. + * Set the "GONE" flag while holding the lock, + * to prevent a race with new references. + * The destroy happens after unlock. + */ + vcp->vc_flags |= SMBV_GONE; + destroy = B_TRUE; + } + SMB_VC_UNLOCK(vcp); + if (destroy) { + /* This sets vc_state = DEAD */ + smb_iod_disconnect(vcp); + } + return (err); } /* + * Handle ioctl SMBIOC_IOD_RCFAIL + * * After a failed reconnect attempt, smbiod will * call this to make current requests error out. */ @@ -1086,10 +1844,6 @@ smb_iod_vc_rcfail(struct smb_vc *vcp) * IOD thread for this VC. */ ASSERT(vcp->iod_thr == curthread); - - if (vcp->vc_state != SMBIOD_ST_RECONNECT) - return (EINVAL); - SMB_VC_LOCK(vcp); smb_iod_newstate(vcp, SMBIOD_ST_RCFAILED); @@ -1105,8 +1859,18 @@ smb_iod_vc_rcfail(struct smb_vc *vcp) if (tr == 0) err = EINTR; - smb_iod_newstate(vcp, SMBIOD_ST_IDLE); - cv_broadcast(&vcp->vc_statechg); + /* + * Normally we'll switch to state IDLE here. However, + * if something called smb_iod_reconnect() while we were + * waiting above, we'll be in in state reconnect already. + * In that case, keep state RECONNECT, so we essentially + * skip transition through state IDLE that would normally + * happen next. + */ + if (vcp->vc_state != SMBIOD_ST_RECONNECT) { + smb_iod_newstate(vcp, SMBIOD_ST_IDLE); + cv_broadcast(&vcp->vc_statechg); + } SMB_VC_UNLOCK(vcp); @@ -1127,11 +1891,17 @@ again: switch (vcp->vc_state) { case SMBIOD_ST_IDLE: + /* Tell the IOD thread it's no longer IDLE. */ smb_iod_newstate(vcp, SMBIOD_ST_RECONNECT); cv_signal(&vcp->iod_idle); /* FALLTHROUGH */ case SMBIOD_ST_RECONNECT: + case SMBIOD_ST_CONNECTED: + case SMBIOD_ST_NEGOTIATED: + case SMBIOD_ST_AUTHCONT: + case SMBIOD_ST_AUTHOK: + /* Wait for the VC state to become ACTIVE. */ rv = cv_wait_sig(&vcp->vc_statechg, &vcp->vc_lock); if (rv == 0) { err = EINTR; @@ -1143,6 +1913,7 @@ again: err = 0; /* success! */ break; + case SMBIOD_ST_AUTHFAIL: case SMBIOD_ST_RCFAILED: case SMBIOD_ST_DEAD: default: diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h index 712acb6f3b..aef9e70e27 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h @@ -1,16 +1,35 @@ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2001 - 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ /* * Code corresponding to smb_apple.h - * XXX: Could merge this into smb_subr.h - * as long as that doesn't break smbfs */ #ifndef _NETSMB_SMB_OSDEP_H_ #define _NETSMB_SMB_OSDEP_H_ #ifndef PRIVSYM -#define PRIVSYM +#define PRIVSYM #endif #ifndef min @@ -65,8 +84,14 @@ typedef uint32_t u_int32_t; typedef uint16_t u_int16_t; typedef uint8_t u_int8_t; -typedef const char * c_caddr_t; -typedef uint64_t user_addr_t; +typedef const char *c_caddr_t; +typedef uint64_t user_addr_t; +typedef ssize_t user_ssize_t; +typedef size_t user_size_t; + +#ifdef _FAKE_KERNEL +#define ddi_get_cred() CRED() +#endif /* * Time related calls. @@ -75,7 +100,7 @@ typedef uint64_t user_addr_t; /* BEGIN CSTYLED */ #define timespeccmp(tvp, uvp, cmp) \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \ - ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \ + ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \ ((tvp)->tv_sec cmp (uvp)->tv_sec)) /* END CSTYLED */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h index f4ebf9a573..4a44e36e89 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h @@ -22,6 +22,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMB_PASS_H @@ -44,14 +46,13 @@ typedef struct smb_passid { zoneid_t zoneid; /* Future Use */ char *srvdom; /* Windows Domain (or server) */ char *username; /* Windows User name */ - uchar_t lmhash[SMBIOC_HASH_SZ]; - uchar_t nthash[SMBIOC_HASH_SZ]; + uchar_t lmhash[SMBIOC_HASH_SZ]; + uchar_t nthash[SMBIOC_HASH_SZ]; } smb_passid_t; /* Called from smb_dev.c */ void smb_pkey_init(void); void smb_pkey_fini(void); int smb_pkey_idle(void); -int smb_pkey_ioctl(int, intptr_t, int, cred_t *); #endif /* _SMB_PASS_H */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c index 997b7318dd..1c62850599 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c @@ -34,6 +34,8 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Portions Copyright (C) 2001 - 2013 Apple Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -51,10 +53,12 @@ #include <netsmb/smb_osdep.h> #include <netsmb/smb.h> +#include <netsmb/smb2.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_subr.h> #include <netsmb/smb_tran.h> #include <netsmb/smb_rq.h> +#include <netsmb/smb2_rq.h> /* * How long to wait before restarting a request (after reconnect) @@ -69,9 +73,8 @@ static int smb_rq_reply(struct smb_rq *rqp); +static int smb_rq_parsehdr(struct smb_rq *rqp); static int smb_rq_enqueue(struct smb_rq *rqp); -static int smb_rq_getenv(struct smb_connobj *layer, - struct smb_vc **vcpp, struct smb_share **sspp); static int smb_rq_new(struct smb_rq *rqp, uchar_t cmd); static int smb_t2_reply(struct smb_t2rq *t2p); static int smb_nt_reply(struct smb_ntrq *ntp); @@ -147,7 +150,6 @@ smb_rq_init(struct smb_rq *rqp, struct smb_connobj *co, uchar_t cmd, rqp->sr_rexmit = SMBMAXRESTARTS; rqp->sr_cred = scred; /* Note: ref hold done by caller. */ - rqp->sr_pid = (uint16_t)ddi_get_pid(); error = smb_rq_new(rqp, cmd); return (error); @@ -163,7 +165,6 @@ smb_rq_new(struct smb_rq *rqp, uchar_t cmd) ASSERT(rqp != NULL); rqp->sr_sendcnt = 0; - rqp->sr_cmd = cmd; mb_done(mbp); md_done(&rqp->sr_rp); @@ -171,18 +172,42 @@ smb_rq_new(struct smb_rq *rqp, uchar_t cmd) if (error) return (error); - /* - * Is this the right place to save the flags? - */ - rqp->sr_rqflags = vcp->vc_hflags; - rqp->sr_rqflags2 = vcp->vc_hflags2; + if (vcp->vc_flags & SMBV_SMB2) { + /* + * SMB2 request initialization + */ + rqp->sr2_command = cmd; + rqp->sr2_creditcharge = 1; + rqp->sr2_creditsrequested = 1; + rqp->sr_pid = 0xFEFF; /* Made up, just like Windows */ + rqp->sr2_rqflags = 0; + if ((vcp->vc_flags & SMBV_SIGNING) != 0 && + vcp->vc_mackey != NULL) { + rqp->sr2_rqflags |= SMB2_FLAGS_SIGNED; + } - /* - * The SMB header is filled in later by - * smb_rq_fillhdr (see below) - * Just reserve space here. - */ - mb_put_mem(mbp, NULL, SMB_HDRLEN, MB_MZERO); + /* + * The SMB2 header is filled in later by + * smb2_rq_fillhdr (see smb2_rq.c) + * Just reserve space here. + */ + mb_put_mem(mbp, NULL, SMB2_HDRLEN, MB_MZERO); + } else { + /* + * SMB1 request initialization + */ + rqp->sr_cmd = cmd; + rqp->sr_pid = (uint32_t)ddi_get_pid(); + rqp->sr_rqflags = vcp->vc_hflags; + rqp->sr_rqflags2 = vcp->vc_hflags2; + + /* + * The SMB header is filled in later by + * smb_rq_fillhdr (see below) + * Just reserve space here. + */ + mb_put_mem(mbp, NULL, SMB_HDRLEN, MB_MZERO); + } return (0); } @@ -190,7 +215,7 @@ smb_rq_new(struct smb_rq *rqp, uchar_t cmd) /* * Given a request with it's body already composed, * rewind to the start and fill in the SMB header. - * This is called after the request is enqueued, + * This is called when the request is enqueued, * so we have the final MID, seq num. etc. */ void @@ -217,7 +242,7 @@ smb_rq_fillhdr(struct smb_rq *rqp) mb_put_mem(mbp, NULL, 8, MB_MZERO); /* MAC sig. (later) */ mb_put_uint16le(mbp, 0); /* reserved */ mb_put_uint16le(mbp, rqp->sr_rqtid); - mb_put_uint16le(mbp, rqp->sr_pid); + mb_put_uint16le(mbp, (uint16_t)rqp->sr_pid); mb_put_uint16le(mbp, rqp->sr_rquid); mb_put_uint16le(mbp, rqp->sr_mid); @@ -281,6 +306,8 @@ smb_rq_enqueue(struct smb_rq *rqp) struct smb_share *ssp = rqp->sr_share; int error = 0; + ASSERT((vcp->vc_flags & SMBV_SMB2) == 0); + /* * Normal requests may initiate a reconnect, * and/or wait for state changes to finish. @@ -325,7 +352,71 @@ smb_rq_enqueue(struct smb_rq *rqp) ok_out: rqp->sr_rquid = vcp->vc_smbuid; rqp->sr_rqtid = ssp ? ssp->ss_tid : SMB_TID_UNKNOWN; - error = smb_iod_addrq(rqp); + error = smb1_iod_addrq(rqp); + + return (error); +} + +/* + * Used by the IOD thread during connection setup, + * and for smb_echo after network timeouts. Note that + * unlike smb_rq_simple, callers must check sr_error. + */ +int +smb_rq_internal(struct smb_rq *rqp, int timeout) +{ + struct smb_vc *vcp = rqp->sr_vc; + int error; + + ASSERT((vcp->vc_flags & SMBV_SMB2) == 0); + + rqp->sr_flags &= ~SMBR_RESTART; + rqp->sr_timo = timeout; /* in seconds */ + rqp->sr_state = SMBRQ_NOTSENT; + + /* + * In-line smb_rq_enqueue(rqp) here, as we don't want it + * trying to reconnect etc. for an internal request. + */ + rqp->sr_rquid = vcp->vc_smbuid; + rqp->sr_rqtid = SMB_TID_UNKNOWN; + rqp->sr_flags |= SMBR_INTERNAL; + error = smb1_iod_addrq(rqp); + if (error != 0) + return (error); + + /* + * In-line a variant of smb_rq_reply(rqp) here as we may + * need to do custom parsing for SMB1-to-SMB2 negotiate. + */ + if (rqp->sr_timo == SMBNOREPLYWAIT) { + smb_iod_removerq(rqp); + return (0); + } + + error = smb_iod_waitrq_int(rqp); + if (error) + return (error); + + /* + * If the request was signed, validate the + * signature on the response. + */ + if (rqp->sr_rqflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { + error = smb_rq_verify(rqp); + if (error) + return (error); + } + + /* + * Parse the SMB header. + */ + error = smb_rq_parsehdr(rqp); + + /* + * Skip the error translation smb_rq_reply does. + * Callers of this expect "raw" NT status. + */ return (error); } @@ -398,15 +489,6 @@ smb_rq_bend(struct smb_rq *rqp) } int -smb_rq_intr(struct smb_rq *rqp) -{ - if (rqp->sr_flags & SMBR_INTR) - return (EINTR); - - return (0); -} - -static int smb_rq_getenv(struct smb_connobj *co, struct smb_vc **vcpp, struct smb_share **sspp) { @@ -457,14 +539,12 @@ out: } /* - * Wait for reply on the request + * Wait for a reply to this request, then parse it. */ static int smb_rq_reply(struct smb_rq *rqp) { - struct mdchain *mdp = &rqp->sr_rp; - u_int8_t tb; - int error, rperror = 0; + int error; if (rqp->sr_timo == SMBNOREPLYWAIT) { smb_iod_removerq(rqp); @@ -488,14 +568,23 @@ smb_rq_reply(struct smb_rq *rqp) /* * Parse the SMB header */ - error = md_get_uint32le(mdp, NULL); - if (error) + error = smb_rq_parsehdr(rqp); + if (error != 0) return (error); - error = md_get_uint8(mdp, &tb); - error = md_get_uint32le(mdp, &rqp->sr_error); - error = md_get_uint8(mdp, &rqp->sr_rpflags); - error = md_get_uint16le(mdp, &rqp->sr_rpflags2); - if (rqp->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS) { + + if (rqp->sr_error != 0) { + if (rqp->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS) { + error = smb_maperr32(rqp->sr_error); + } else { + uint8_t errClass = rqp->sr_error & 0xff; + uint16_t errCode = rqp->sr_error >> 16; + /* Convert to NT status */ + rqp->sr_error = smb_doserr2status(errClass, errCode); + error = smb_maperror(errClass, errCode); + } + } + + if (error != 0) { /* * Do a special check for STATUS_BUFFER_OVERFLOW; * it's not an error. @@ -506,34 +595,65 @@ smb_rq_reply(struct smb_rq *rqp) * they can look at rqp->sr_error if they * need to know whether we got a * STATUS_BUFFER_OVERFLOW. - * XXX - should we do that for all errors - * where (error & 0xC0000000) is 0x80000000, - * i.e. all warnings? */ - rperror = 0; - } else - rperror = smb_maperr32(rqp->sr_error); + rqp->sr_flags |= SMBR_MOREDATA; + error = 0; + } } else { - rqp->sr_errclass = rqp->sr_error & 0xff; - rqp->sr_serror = rqp->sr_error >> 16; - rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror); - } - if (rperror == EMOREDATA) { - rperror = E2BIG; - rqp->sr_flags |= SMBR_MOREDATA; - } else rqp->sr_flags &= ~SMBR_MOREDATA; + } + + return (error); +} + +/* + * Parse the SMB header + */ +static int +smb_rq_parsehdr(struct smb_rq *rqp) +{ + struct mdchain mdp_save; + struct mdchain *mdp = &rqp->sr_rp; + u_int8_t tb, sig[4]; + int error; - error = md_get_uint32le(mdp, NULL); - error = md_get_uint32le(mdp, NULL); - error = md_get_uint32le(mdp, NULL); + /* + * Parse the signature. The reader already checked that + * the signature is valid. Here we just have to check + * for SMB1-to-SMB2 negotiate. Caller handles an EPROTO + * as a signal that we got an SMB2 reply. If we return + * EPROTO, rewind the mdchain back where it was. + */ + mdp_save = *mdp; + error = md_get_mem(mdp, sig, 4, MB_MSYSTEM); + if (error) + return (error); + if (sig[0] != SMB_HDR_V1) { + if (rqp->sr_cmd == SMB_COM_NEGOTIATE) { + *mdp = mdp_save; + return (EPROTO); + } + return (EBADRPC); + } + + /* Check cmd */ + error = md_get_uint8(mdp, &tb); + if (tb != rqp->sr_cmd) + return (EBADRPC); + + md_get_uint32le(mdp, &rqp->sr_error); + md_get_uint8(mdp, &rqp->sr_rpflags); + md_get_uint16le(mdp, &rqp->sr_rpflags2); - error = md_get_uint16le(mdp, &rqp->sr_rptid); - error = md_get_uint16le(mdp, &rqp->sr_rppid); - error = md_get_uint16le(mdp, &rqp->sr_rpuid); + /* Skip: pid-high(2), MAC sig(8), reserved(2) */ + md_get_mem(mdp, NULL, 12, MB_MSYSTEM); + + md_get_uint16le(mdp, &rqp->sr_rptid); + md_get_uint16le(mdp, &rqp->sr_rppid); + md_get_uint16le(mdp, &rqp->sr_rpuid); error = md_get_uint16le(mdp, &rqp->sr_rpmid); - return ((error) ? error : rperror); + return (error); } @@ -1134,7 +1254,7 @@ smb_t2_request_int(struct smb_t2rq *t2p) mb_put_mbuf(mbp, m); } smb_rq_bend(rqp); - error = smb_iod_multirq(rqp); + error = smb1_iod_multirq(rqp); if (error) goto bad; } /* while left params or data */ @@ -1345,7 +1465,7 @@ smb_nt_request_int(struct smb_ntrq *ntp) mb_put_mbuf(mbp, m); } smb_rq_bend(rqp); - error = smb_iod_multirq(rqp); + error = smb1_iod_multirq(rqp); if (error) goto bad; } /* while left params or data */ @@ -1438,3 +1558,83 @@ smb_nt_request(struct smb_ntrq *ntp) } return (error); } + +/* + * Run an SMB transact named pipe. + * Note: send_mb is consumed. + */ +int +smb_t2_xnp(struct smb_share *ssp, uint16_t fid, + struct mbchain *send_mb, struct mdchain *recv_md, + uint32_t *data_out_sz, /* max / returned */ + uint32_t *more, struct smb_cred *scrp) +{ + struct smb_t2rq *t2p = NULL; + mblk_t *m; + uint16_t setup[2]; + int err; + + setup[0] = TRANS_TRANSACT_NAMED_PIPE; + setup[1] = fid; + + t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP); + err = smb_t2_init(t2p, SSTOCP(ssp), setup, 2, scrp); + if (err) { + *data_out_sz = 0; + goto out; + } + + t2p->t2_setupcount = 2; + t2p->t2_setupdata = setup; + + t2p->t_name = "\\PIPE\\"; + t2p->t_name_len = 6; + + t2p->t2_maxscount = 0; + t2p->t2_maxpcount = 0; + t2p->t2_maxdcount = (uint16_t)*data_out_sz; + + /* Transmit parameters (none) */ + + /* + * Transmit data + * + * Copy the mb, and clear the source so we + * don't end up with a double free. + */ + t2p->t2_tdata = *send_mb; + bzero(send_mb, sizeof (*send_mb)); + + /* + * Run the request + */ + err = smb_t2_request(t2p); + + /* No returned parameters. */ + + if (err == 0 && (m = t2p->t2_rdata.md_top) != NULL) { + /* + * Received data + * + * Copy the mdchain, and clear the source so we + * don't end up with a double free. + */ + *data_out_sz = msgdsize(m); + md_initm(recv_md, m); + t2p->t2_rdata.md_top = NULL; + } else { + *data_out_sz = 0; + } + + if (t2p->t2_sr_error == NT_STATUS_BUFFER_OVERFLOW) + *more = 1; + +out: + if (t2p != NULL) { + /* Note: t2p->t_name no longer allocated */ + smb_t2_done(t2p); + kmem_free(t2p, sizeof (*t2p)); + } + + return (err); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h index 0184022a65..7c3106612c 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h @@ -34,6 +34,8 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Portions Copyright (C) 2001 - 2012 Apple Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _NETSMB_SMB_RQ_H_ @@ -54,8 +56,12 @@ #define SMBR_NOINTR_RECV 0x0200 /* no interrupt in recv wait */ #define SMBR_SENDWAIT 0x0400 /* waiting for send to complete */ #define SMBR_NORECONNECT 0x0800 /* do not reconnect for this */ -/* SMBR_VCREF 0x4000 * took vc reference (obsolete) */ +/* SMBR_VCREF 0x4000 * took vc reference (obsolete) */ #define SMBR_MOREDATA 0x8000 /* our buffer was too small */ +#define SMBR_COMPOUND_RQ 0x10000 /* SMB 2/3 compound request */ +#define SMBR_ASYNC 0x20000 /* got async response */ +#define SMBR_RECONNECTED 0x40000 /* reconnected during request */ + #define SMBT2_ALLSENT 0x0001 /* all data and params are sent */ #define SMBT2_ALLRECV 0x0002 /* all data and params are received */ @@ -64,7 +70,7 @@ #define SMBT2_NORESTART 0x0010 #define SMBT2_MOREDATA 0x8000 /* our buffer was too small */ -#define SMBRQ_LOCK(rqp) mutex_enter(&(rqp)->sr_lock) +#define SMBRQ_LOCK(rqp) mutex_enter(&(rqp)->sr_lock) #define SMBRQ_UNLOCK(rqp) mutex_exit(&(rqp)->sr_lock) enum smbrq_state { @@ -83,7 +89,7 @@ struct smb_rq { enum smbrq_state sr_state; struct smb_vc *sr_vc; struct smb_share *sr_share; - struct _kthread *sr_owner; + struct _kthread *sr_owner; uint32_t sr_seqno; /* Seq. no. of request */ uint32_t sr_rseqno; /* Seq. no. of reply */ struct mbchain sr_rq; @@ -91,11 +97,24 @@ struct smb_rq { uint8_t sr_rqflags; uint16_t sr_rqflags2; uint16_t sr_rqtid; - uint16_t sr_pid; + uint32_t sr_pid; uint16_t sr_rquid; uint16_t sr_mid; uchar_t *sr_wcount; uchar_t *sr_bcount; + + /* SMB 2/3 request fields */ + struct smb_rq *sr2_compound_next; + uint16_t sr2_command; + uint16_t sr2_totalcreditcharge; + uint16_t sr2_creditcharge; + uint16_t sr2_creditsrequested; + uint32_t sr2_rqflags; + uint32_t sr2_nextcmd; + uint64_t sr2_messageid; /* local copy of message id */ + uint64_t sr2_rqsessionid; + uint32_t sr2_rqtreeid; + struct mdchain sr_rp; int sr_rpgen; int sr_rplast; @@ -105,7 +124,7 @@ struct smb_rq { int sr_timo; int sr_rexmit; /* how many more retries. dflt 0 */ int sr_sendcnt; - struct timespec sr_timesent; + struct timespec sr_timesent; int sr_lerror; uint8_t sr_errclass; uint16_t sr_serror; @@ -116,6 +135,15 @@ struct smb_rq { uint16_t sr_rppid; uint16_t sr_rpuid; uint16_t sr_rpmid; + + /* SMB2 response fields */ + uint16_t sr2_rspcreditsgranted; + uint32_t sr2_rspflags; + uint32_t sr2_rspnextcmd; + uint32_t sr2_rsppid; + uint32_t sr2_rsptreeid; + uint64_t sr2_rspasyncid; + uint64_t sr2_rspsessionid; }; typedef struct smb_rq smb_rq_t; @@ -124,7 +152,7 @@ struct smb_t2rq { kcondvar_t t2_cond; uint16_t t2_setupcount; uint16_t *t2_setupdata; - uint16_t t2_setup[SMBIOC_T2RQ_MAXSETUP]; + uint16_t t2_setup[4]; uint8_t t2_maxscount; /* max setup words to return */ uint16_t t2_maxpcount; /* max param bytes to return */ uint16_t t2_maxdcount; /* max data bytes to return */ @@ -184,15 +212,19 @@ int smb_rq_alloc(struct smb_connobj *layer, uchar_t cmd, struct smb_cred *scred, struct smb_rq **rqpp); int smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, uchar_t cmd, struct smb_cred *scred); +int smb_rq_getenv(struct smb_connobj *layer, + struct smb_vc **vcpp, struct smb_share **sspp); void smb_rq_fillhdr(struct smb_rq *rqp); void smb_rq_wstart(struct smb_rq *rqp); void smb_rq_wend(struct smb_rq *rqp); void smb_rq_bstart(struct smb_rq *rqp); void smb_rq_bend(struct smb_rq *rqp); -int smb_rq_intr(struct smb_rq *rqp); int smb_rq_simple(struct smb_rq *rqp); int smb_rq_simple_timed(struct smb_rq *rqp, int timeout); +int smb_rq_internal(struct smb_rq *rqp, int timeout); + +int smb2_parse_smb1nego_resp(struct smb_rq *rqp); int smb_t2_alloc(struct smb_connobj *layer, ushort_t setup, struct smb_cred *scred, struct smb_t2rq **rqpp); @@ -201,6 +233,10 @@ int smb_t2_init(struct smb_t2rq *rqp, struct smb_connobj *layer, void smb_t2_done(struct smb_t2rq *t2p); int smb_t2_request(struct smb_t2rq *t2p); +int smb_t2_xnp(struct smb_share *ssp, uint16_t fid, + struct mbchain *send_mb, struct mdchain *recv_md, + uint32_t *data_out_sz, uint32_t *more, struct smb_cred *scrp); + int smb_nt_alloc(struct smb_connobj *layer, ushort_t fn, struct smb_cred *scred, struct smb_ntrq **rqpp); int smb_nt_init(struct smb_ntrq *rqp, struct smb_connobj *layer, diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c index cf06e75767..bec61e4392 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -37,8 +38,6 @@ #include <sys/md5.h> #include <sys/des.h> #include <sys/kmem.h> -#include <sys/crypto/api.h> -#include <sys/crypto/common.h> #include <sys/cmn_err.h> #include <sys/stream.h> #include <sys/strsun.h> @@ -50,6 +49,7 @@ #include <netsmb/smb_subr.h> #include <netsmb/smb_dev.h> #include <netsmb/smb_rq.h> +#include <netsmb/smb_signing.h> #ifdef DEBUG /* @@ -59,15 +59,37 @@ int nsmb_signing_fudge = 0; #endif -/* Mechanism definitions */ -static crypto_mechanism_t crypto_mech_md5 = { CRYPTO_MECH_INVALID }; - -void -smb_crypto_mech_init(void) +/* + * This is called just after session setup completes, + * at the top of smb_iod_vc_work(). Initialize signing. + */ +int +smb_sign_init(smb_vc_t *vcp) { - crypto_mech_md5.cm_type = crypto_mech2id(SUN_CKM_MD5); -} + int rc; + ASSERT(vcp->vc_ssnkey != NULL); + ASSERT(vcp->vc_mackey == NULL); + + rc = smb_md5_getmech(&vcp->vc_signmech); + if (rc != 0) { + cmn_err(CE_NOTE, "smb can't get signing mechanism"); + return (EAUTH); + } + + /* + * Convert the session key to the MAC key. + * SMB1 uses the whole session key. + */ + vcp->vc_mackeylen = vcp->vc_ssnkeylen; + vcp->vc_mackey = kmem_zalloc(vcp->vc_mackeylen, KM_SLEEP); + bcopy(vcp->vc_ssnkey, vcp->vc_mackey, vcp->vc_mackeylen); + + /* The initial sequence number is two. */ + vcp->vc_next_seq = 2; + + return (0); +} #define SMBSIGLEN 8 /* SMB signature length */ @@ -83,12 +105,12 @@ static int smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp, uint32_t seqno, uchar_t *signature) { - crypto_context_t crypto_ctx; - crypto_data_t key; - crypto_data_t data; - crypto_data_t digest; - uchar_t mac[16]; - int status; + uchar_t digest[MD5_DIGEST_LENGTH]; + smb_sign_ctx_t ctx = 0; + mblk_t *m = mp; + int size; + int rc; + /* * This union is a little bit of trickery to: * (1) get the sequence number int aligned, and @@ -109,78 +131,64 @@ smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp, } s; } smbhdr; - ASSERT(mp != NULL); - ASSERT(MBLKL(mp) >= SMB_HDRLEN); - ASSERT(vcp->vc_mackey != NULL); + if (vcp->vc_mackey == NULL) + return (-1); + + if ((rc = smb_md5_init(&ctx, &vcp->vc_signmech)) != 0) + return (rc); + + /* Digest the MAC Key */ + rc = smb_md5_update(ctx, vcp->vc_mackey, vcp->vc_mackeylen); + if (rc != 0) + return (rc); + + /* Our caller should ensure mp has a contiguous header */ + ASSERT(m != NULL); + ASSERT(MBLKL(m) >= SMB_HDRLEN); /* - * Make an aligned copy of the SMB header - * and fill in the sequence number. + * Make an aligned copy of the SMB header, + * fill in the sequence number, and digest. */ - bcopy(mp->b_rptr, smbhdr.r.raw, SMB_HDRLEN); + size = SMB_HDRLEN; + bcopy(m->b_rptr, smbhdr.r.raw, size); smbhdr.s.sig[0] = htolel(seqno); smbhdr.s.sig[1] = 0; + rc = smb_md5_update(ctx, &smbhdr.r.raw, size); + if (rc != 0) + return (rc); + /* - * Compute the MAC: MD5(concat(Key, message)) + * Digest the rest of the SMB header packet, starting at + * the data just after the SMB header. */ - if (crypto_mech_md5.cm_type == CRYPTO_MECH_INVALID) { - SMBSDEBUG("crypto_mech_md5 invalid\n"); - return (CRYPTO_MECHANISM_INVALID); - } - status = crypto_digest_init(&crypto_mech_md5, &crypto_ctx, 0); - if (status != CRYPTO_SUCCESS) - return (status); - - /* Digest the MAC Key */ - key.cd_format = CRYPTO_DATA_RAW; - key.cd_offset = 0; - key.cd_length = vcp->vc_mackeylen; - key.cd_miscdata = 0; - key.cd_raw.iov_base = (char *)vcp->vc_mackey; - key.cd_raw.iov_len = vcp->vc_mackeylen; - status = crypto_digest_update(crypto_ctx, &key, 0); - if (status != CRYPTO_SUCCESS) - return (status); - - /* Digest the (copied) SMB header */ - data.cd_format = CRYPTO_DATA_RAW; - data.cd_offset = 0; - data.cd_length = SMB_HDRLEN; - data.cd_miscdata = 0; - data.cd_raw.iov_base = (char *)smbhdr.r.raw; - data.cd_raw.iov_len = SMB_HDRLEN; - status = crypto_digest_update(crypto_ctx, &data, 0); - if (status != CRYPTO_SUCCESS) - return (status); + size = MBLKL(m) - SMB_HDRLEN; + rc = smb_md5_update(ctx, m->b_rptr + SMB_HDRLEN, size); + if (rc != 0) + return (rc); + m = m->b_cont; /* Digest rest of the SMB message. */ - data.cd_format = CRYPTO_DATA_MBLK; - data.cd_offset = SMB_HDRLEN; - data.cd_length = msgdsize(mp) - SMB_HDRLEN; - data.cd_miscdata = 0; - data.cd_mp = mp; - status = crypto_digest_update(crypto_ctx, &data, 0); - if (status != CRYPTO_SUCCESS) - return (status); - - /* Final */ - digest.cd_format = CRYPTO_DATA_RAW; - digest.cd_offset = 0; - digest.cd_length = sizeof (mac); - digest.cd_miscdata = 0; - digest.cd_raw.iov_base = (char *)mac; - digest.cd_raw.iov_len = sizeof (mac); - status = crypto_digest_final(crypto_ctx, &digest, 0); - if (status != CRYPTO_SUCCESS) - return (status); + while (m != NULL) { + size = MBLKL(m); + if (size > 0) { + rc = smb_md5_update(ctx, m->b_rptr, size); + if (rc != 0) + return (rc); + } + m = m->b_cont; + } + rc = smb_md5_final(ctx, digest); + if (rc != 0) + return (rc); /* * Finally, store the signature. * (first 8 bytes of the mac) */ - if (signature) - bcopy(mac, signature, SMBSIGLEN); + if (signature != NULL) + bcopy(digest, signature, SMBSIGLEN); return (0); } @@ -197,13 +205,10 @@ smb_rq_sign(struct smb_rq *rqp) int status; /* - * Our mblk allocation ensures this, - * but just in case... + * smb_rq_new() ensures this, + * but just in case.. */ - if (MBLKL(mp) < SMB_HDRLEN) { - if (!pullupmsg(mp, SMB_HDRLEN)) - return; - } + ASSERT(MBLKL(mp) >= SMB_HDRLEN); sigloc = mp->b_rptr + SMBSIGOFF; if (vcp->vc_mackey == NULL) { @@ -221,7 +226,7 @@ smb_rq_sign(struct smb_rq *rqp) * directly into the message at sigloc. */ status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc); - if (status != CRYPTO_SUCCESS) { + if (status != 0) { SMBSDEBUG("Crypto error %d", status); bzero(sigloc, SMBSIGLEN); } @@ -256,10 +261,8 @@ smb_rq_verify(struct smb_rq *rqp) SMBSDEBUG("empty reply\n"); return (0); } - if (MBLKL(mp) < SMB_HDRLEN) { - if (!pullupmsg(mp, SMB_HDRLEN)) - return (0); - } + + ASSERT(MBLKL(mp) >= SMB_HDRLEN); sigloc = mp->b_rptr + SMBSIGOFF; /* @@ -267,7 +270,7 @@ smb_rq_verify(struct smb_rq *rqp) */ rsn = rqp->sr_rseqno; status = smb_compute_MAC(vcp, mp, rsn, sigbuf); - if (status != CRYPTO_SUCCESS) { + if (status != 0) { SMBSDEBUG("Crypto error %d", status); /* * If we can't compute a MAC, then there's diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_signing.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_signing.h new file mode 100644 index 0000000000..e1799e3383 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_signing.h @@ -0,0 +1,73 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _SMB_SIGNING_H_ +#define _SMB_SIGNING_H_ + +/* + * SMB signing routines used in {smb,smb2}_sign.c + * Two implementations of these (kernel/user) in: + * uts/common/fs/smbclnt/netsmb/smb_sign_kcf.c + * lib/smbclnt/libfknsmb/common/fksmb_sign_pkcs.c + */ + +#ifdef _KERNEL +#include <sys/crypto/api.h> +#else +#include <security/cryptoki.h> +#include <security/pkcs11.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define MD5_DIGEST_LENGTH 16 /* MD5 digest length in bytes */ +#define SHA256_DIGEST_LENGTH 32 /* SHA256 digest length in bytes */ +#define SMB2_SIG_SIZE 16 + +#ifdef _KERNEL +/* KCF variant */ +typedef crypto_mechanism_t smb_sign_mech_t; +typedef crypto_context_t smb_sign_ctx_t; +#else /* _KERNEL */ +/* PKCS11 variant */ +typedef CK_MECHANISM smb_sign_mech_t; +typedef CK_SESSION_HANDLE smb_sign_ctx_t; +#endif /* _KERNEL */ + +/* + * SMB signing routines used in smb_signing.c + */ + +int smb_md5_getmech(smb_sign_mech_t *); +int smb_md5_init(smb_sign_ctx_t *, smb_sign_mech_t *); +int smb_md5_update(smb_sign_ctx_t, void *, size_t); +int smb_md5_final(smb_sign_ctx_t, uint8_t *); + +/* + * SMB2 signing routines used in smb2_signing.c + */ + +int smb2_hmac_getmech(smb_sign_mech_t *); +int smb2_hmac_init(smb_sign_ctx_t *, smb_sign_mech_t *, uint8_t *, size_t); +int smb2_hmac_update(smb_sign_ctx_t, uint8_t *, size_t); +int smb2_hmac_final(smb_sign_ctx_t, uint8_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SMB_SIGNING_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c index 6016f5061a..566f131316 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c @@ -33,8 +33,9 @@ */ /* - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Portions Copyright (C) 2001 - 2014 Apple Inc. All rights reserved. * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -49,6 +50,7 @@ #include <sys/uio.h> #include <sys/random.h> #include <sys/note.h> +#include <sys/errno.h> #include <sys/cmn_err.h> #include <netsmb/smb_osdep.h> @@ -61,14 +63,25 @@ #define STYPE_LEN 8 /* share type strings */ -/* - * Largest size to use with LARGE_READ/LARGE_WRITE. - * Specs say up to 64k data bytes, but Windows traffic - * uses 60k... no doubt for some good reason. - * (Probably to keep 4k block alignment.) - * XXX: Move to smb.h maybe? - */ -#define SMB_MAX_LARGE_RW_SIZE (60*1024) +struct smb_dialect { + int d_id; + const char *d_name; +}; + +static struct smb_dialect smb_dialects[3] = { + {SMB_DIALECT_NTLM0_12, "NT LANMAN 1.0"}, + {SMB_DIALECT_NTLM0_12, "NT LM 0.12"}, +#define NDIALECT_SMB1 2 + {SMB_DIALECT_SMB2_FF, "SMB 2.???"}, +#define NDIALECT_SMB2 3 +}; + +static const uint32_t smb_clnt_caps_mask = + SMB_CAP_UNICODE | + SMB_CAP_LARGE_FILES | + SMB_CAP_NT_SMBS | + SMB_CAP_STATUS32 | + SMB_CAP_EXT_SECURITY; /* * Default timeout values, all in seconds. @@ -76,20 +89,526 @@ */ int smb_timo_notice = 15; int smb_timo_default = 30; /* was SMB_DEFRQTIMO */ +int smb_timo_logon = 45; int smb_timo_open = 45; int smb_timo_read = 45; int smb_timo_write = 60; /* was SMBWRTTIMO */ int smb_timo_append = 90; -static int smb_smb_read(struct smb_share *ssp, uint16_t fid, - uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); -static int smb_smb_write(struct smb_share *ssp, uint16_t fid, - uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); +int +smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) +{ + smb_sopt_t *sv = &vcp->vc_sopt; + smbioc_ssn_work_t *wk = &vcp->vc_work; + struct smb_rq *rqp = NULL; + struct mbchain *mbp = NULL; + struct mdchain *mdp = NULL; + struct smb_dialect *dp; + int err, sblen, tlen; + uint8_t wc, eklen; + uint16_t dindex, bc; + uint16_t ndialects; + boolean_t will_sign = B_FALSE; + + /* + * Initialize: vc_hflags and vc_hflags2. + * Note: vcp->vc_hflags* are copied into the + * (per request) rqp->rq_hflags* by smb_rq_init. + * + * Like Windows, set FLAGS2_UNICODE in our first request, + * even though technically we don't yet know whether the + * server supports Unicode. Will clear this flag below + * if we find out it doesn't. Need to do this because + * some servers reject all non-Unicode requests. + */ + vcp->vc_hflags = + SMB_FLAGS_CASELESS | + SMB_FLAGS_CANONICAL_PATHNAMES; + vcp->vc_hflags2 = + SMB_FLAGS2_KNOWS_LONG_NAMES | + SMB_FLAGS2_KNOWS_EAS | + SMB_FLAGS2_IS_LONG_NAME | + SMB_FLAGS2_EXT_SEC | + SMB_FLAGS2_ERR_STATUS | + SMB_FLAGS2_UNICODE; + + /* + * The initial UID needs to be zero, + */ + vcp->vc_smbuid = 0; + + /* + * (Re)init negotiated values + */ + bzero(sv, sizeof (*sv)); + sv->sv_maxmux = 1; + sv->sv_maxvcs = 1; + sv->sv_maxtx = 1024; + + /* + * Should we offer the magic SMB2 dialect? + */ + if (vcp->vc_ssn.ssn_maxver >= SMB2_DIALECT_BASE) + ndialects = NDIALECT_SMB2; + else + ndialects = NDIALECT_SMB1; + + err = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp); + if (err) + return (err); + + /* + * Build the SMB request. + */ + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + for (dindex = 0; dindex < ndialects; dindex++) { + dp = &smb_dialects[dindex]; + mb_put_uint8(mbp, SMB_DT_DIALECT); + tlen = strlen(dp->d_name) + 1; + mb_put_mem(mbp, dp->d_name, tlen, MB_MSYSTEM); + } + smb_rq_bend(rqp); + + /* + * Do the OTW call. + */ + err = smb_rq_internal(rqp, smb_timo_default); + /* + * If it's an SMB1-to-SMB2 negotiate response, + * call the special handler and then skip the + * whole rest of this function. + */ + if (err == EPROTO) { + err = smb2_parse_smb1nego_resp(rqp); + smb_rq_done(rqp); + return (err); + } + if (err) { + SMBSDEBUG("smb_rq_internal, err %d", err); + goto errout; + } + /* Should only get status success. */ + if (rqp->sr_error != NT_STATUS_SUCCESS) { + err = ENOTSUP; + goto errout; + } + + /* + * Decode the response + * + * Comments to right show names as described in + * The Microsoft SMB Protocol spec. [MS-SMB] + * section 2.2.3 + */ + smb_rq_getreply(rqp, &mdp); + (void) md_get_uint8(mdp, &wc); + err = md_get_uint16le(mdp, &dindex); + if (err != 0) + goto errout; + if (dindex >= ndialects) { + SMBERROR("Invalid dialect index from server: %s\n", + vcp->vc_srvname); + err = EBADRPC; + goto errout; + } + dp = smb_dialects + dindex; + sv->sv_proto = dp->d_id; + SMBSDEBUG("Dialect %s", dp->d_name); + if (dp->d_id < SMB_DIALECT_NTLM0_12) { + SMBSDEBUG("old dialect %s", dp->d_name); + goto errout; + } + if (wc != 17) { + SMBSDEBUG("bad wc %d", (int)wc); + goto errout; + } + md_get_uint8(mdp, &sv->sv_sm); /* SecurityMode */ + md_get_uint16le(mdp, &sv->sv_maxmux); /* MaxMpxCount */ + md_get_uint16le(mdp, &sv->sv_maxvcs); /* MaxCountVCs */ + md_get_uint32le(mdp, &sv->sv_maxtx); /* MaxBufferSize */ + md_get_uint32le(mdp, &sv->sv_maxraw); /* MaxRawSize */ + md_get_uint32le(mdp, &sv->sv_skey); /* SessionKey */ + md_get_uint32le(mdp, &sv->sv_caps); /* Capabilities */ + md_get_mem(mdp, NULL, 8, MB_MSYSTEM); /* SystemTime(s) */ + md_get_uint16le(mdp, (uint16_t *)&sv->sv_tz); + md_get_uint8(mdp, &eklen); /* EncryptionKeyLength */ + err = md_get_uint16le(mdp, &bc); /* ByteCount */ + if (err) + goto errout; + + /* BEGIN CSTYLED */ + /* + * Will we do SMB signing? Or block the connection? + * The table below describes this logic. References: + * [Windows Server Protocols: MS-SMB, sec. 3.2.4.2.3] + * http://msdn.microsoft.com/en-us/library/cc212511.aspx + * http://msdn.microsoft.com/en-us/library/cc212929.aspx + * + * Srv/Cli | Required | Enabled | If Required | Disabled + * ------------+----------+------------+-------------+----------- + * Required | Signed | Signed | Signed | Blocked [1] + * ------------+----------+------------+-------------+----------- + * Enabled | Signed | Signed | Not Signed | Not Signed + * ------------+----------+------------+-------------+----------- + * If Required | Signed | Not Signed | Not Signed | Not Signed + * ------------+----------+------------+-------------+----------- + * Disabled | Blocked | Not Signed | Not Signed | Not Signed + * + * [1] Like Windows 2003 and later, we don't really implement + * the "Disabled" setting. Instead we implement "If Required", + * so we always sign if the server requires signing. + */ + /* END CSTYLED */ + + if (sv->sv_sm & SMB_SM_SIGS_REQUIRE) { + /* + * Server requires signing. We will sign, + * even if local setting is "disabled". + */ + will_sign = B_TRUE; + } else if (sv->sv_sm & SMB_SM_SIGS) { + /* + * Server enables signing (client's option). + * If enabled locally, do signing. + */ + if (vcp->vc_vopt & SMBVOPT_SIGNING_ENABLED) + will_sign = B_TRUE; + /* else not signing. */ + } else { + /* + * Server does not support signing. + * If we "require" it, bail now. + */ + if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) { + SMBERROR("Client requires signing " + "but server has it disabled."); + err = EBADRPC; + goto errout; + } + } + + /* + * Anonymous sessions can't sign. + */ + if (vcp->vc_vopt & SMBVOPT_ANONYMOUS) { + will_sign = B_FALSE; + } + + SMBSDEBUG("Security signatures: %d", (int)will_sign); + if (will_sign) { + vcp->vc_flags |= SMBV_SIGNING; + vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE; + + /* + * MS-SMB 2.2.4.5 says that when SMB signing is enabled, + * we should NOT use "large read/write" even though the + * server might offer those capabilities. + */ + sv->sv_caps &= ~(SMB_CAP_LARGE_READX | SMB_CAP_LARGE_WRITEX); + } + + /* See comment above re. FLAGS2_UNICODE */ + if ((sv->sv_caps & SMB_CAP_UNICODE) != 0) + vcp->vc_flags |= SMBV_UNICODE; + else + vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE; + + if ((sv->sv_caps & SMB_CAP_STATUS32) == 0) { + /* They don't do NT error codes. */ + vcp->vc_hflags2 &= ~SMB_FLAGS2_ERR_STATUS; + } + + /* + * Warn if they don't support SMB_CAP_NT_SMBS + * (We'll try to use NtCreate anyway) + */ + if ((sv->sv_caps & SMB_CAP_NT_SMBS) == 0) { + cmn_err(CE_NOTE, "%s does not support SMB_CAP_NT_SMBS", + vcp->vc_srvname); + } + + /* + * The rest of the message varies depending on + * whether we've negotiated "extended security". + * + * With extended security, we have: + * Server_GUID (length 16) + * Security_BLOB + * Otherwise we have: + * EncryptionKey (length is eklen) + * PrimaryDomain + */ + if (sv->sv_caps & SMB_CAP_EXT_SECURITY) { + SMBSDEBUG("Ext.Security: yes"); + + /* + * Skip the server GUID. + */ + err = md_get_mem(mdp, NULL, SMB_GUIDLEN, MB_MSYSTEM); + if (err) + goto errout; + /* + * Remainder is the security blob. + * Note: eklen "must be ignored" [MS-SMB] + */ + sblen = (int)bc - SMB_GUIDLEN; + if (sblen < 0) + goto errout; + /* Security blob (hint) is next */ + } else { + SMBSDEBUG("Ext.Security: no"); + err = ENOTSUP; + goto errout; + } + + /* + * Copy the security blob out to user space. + * Buffer addr,size in vc_auth_rbuf,rlen + */ + if (wk->wk_u_auth_rlen < sblen) { + SMBSDEBUG("vc_auth_rbuf too small"); + /* Give caller required size. */ + wk->wk_u_auth_rlen = sblen; + err = EMSGSIZE; + goto errout; + } + wk->wk_u_auth_rlen = sblen; + err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr, sblen, MB_MUSER); + if (err) + goto errout; + + /* + * A few sanity checks on what we received, + * becuse we will send these in ssnsetup. + * + * Maximum outstanding requests (we care), + * and Max. VCs (we only use one). Also, + * MaxBufferSize lower limit per spec. + */ + if (sv->sv_maxmux < 1) + sv->sv_maxmux = 1; + if (sv->sv_maxvcs < 1) + sv->sv_maxvcs = 1; + if (sv->sv_maxtx < 1024) + sv->sv_maxtx = 1024; + + /* + * Maximum transfer size. + * Sanity checks: + * + * Let's be conservative about an upper limit here. + * Win2k uses 16644 (and others) so 32k should be a + * reasonable sanity limit for this value. + * + * Note that this limit does NOT affect READX/WRITEX + * with CAP_LARGE_..., which we nearly always use. + */ + vcp->vc_txmax = sv->sv_maxtx; + if (vcp->vc_txmax > 0x8000) + vcp->vc_txmax = 0x8000; + + /* + * Max read/write sizes, WITHOUT overhead. + * This is just the payload size, so we must + * leave room for the SMB headers, etc. + * This is just the ct_txmax value, but + * reduced and rounded down. Tricky bit: + * + * Servers typically give us a value that's + * some nice "round" number, i.e 0x4000 plus + * some overhead, i.e. Win2k: 16644==0x4104 + * Subtract for the SMB header (32) and the + * SMB command word and byte vectors (34?), + * then round down to a 512 byte multiple. + */ + tlen = vcp->vc_txmax - 68; + tlen &= 0xFE00; + + vcp->vc_rwmax = tlen; + vcp->vc_rxmax = tlen; + vcp->vc_wxmax = tlen; + + /* + * Most of the "capability" bits we offer in session setup + * are just copied from those offered by the server. + */ + sv->sv_caps &= smb_clnt_caps_mask; + + smb_rq_done(rqp); + return (0); + +errout: + smb_rq_done(rqp); + if (err == 0) + err = EBADRPC; + return (err); +} + +static const char NativeOS[] = "illumos"; +static const char LanMan[] = "NETSMB"; + +int +smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) +{ + smb_sopt_t *sv = &vcp->vc_sopt; + smbioc_ssn_work_t *wk = &vcp->vc_work; + struct smb_rq *rqp = NULL; + struct mbchain *mbp = NULL; + struct mdchain *mdp = NULL; + char *sb; + int err, ret; + uint32_t caps; + uint16_t action, bc, sblen; + uint8_t wc; + + caps = sv->sv_caps; + sb = wk->wk_u_auth_wbuf.lp_ptr; + sblen = (uint16_t)wk->wk_u_auth_wlen; + + err = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, + scred, &rqp); + if (err != 0) { + ret = err; + goto out; + } + + /* + * Build the SMB Session Setup request. + * Always extended security form. + */ + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, 0xff); /* 0: AndXCommand */ + mb_put_uint16le(mbp, 0); /* 1: AndXOffset */ + mb_put_uint16le(mbp, sv->sv_maxtx); /* 2: MaxBufferSize */ + mb_put_uint16le(mbp, sv->sv_maxmux); /* 3: MaxMpxCount */ + mb_put_uint16le(mbp, 1); /* 4: VcNumber */ + mb_put_uint32le(mbp, sv->sv_skey); /* 5,6: Session Key */ + mb_put_uint16le(mbp, sblen); /* 7: Sec. Blob Len */ + mb_put_uint32le(mbp, 0); /* 8,9: reserved */ + mb_put_uint32le(mbp, caps); /* 10,11: Capabilities */ + smb_rq_wend(rqp); /* 12: Byte Count */ + smb_rq_bstart(rqp); + err = mb_put_mem(mbp, sb, sblen, MB_MUSER); + if (err != 0) { + ret = err; + goto out; + } + (void) smb_put_dstring(mbp, vcp, NativeOS, SMB_CS_NONE); + (void) smb_put_dstring(mbp, vcp, LanMan, SMB_CS_NONE); + smb_rq_bend(rqp); + + /* + * Run the request. The return value here should be the + * return from this function, unless we fail decoding. + * Note: NT_STATUS_MORE_PROCESSING_REQUIRED is OK, and + * the caller expects EINPROGRESS for that case. + */ + ret = smb_rq_internal(rqp, smb_timo_logon); + if (ret != 0) + goto out; + switch (rqp->sr_error) { + case NT_STATUS_SUCCESS: + break; + case NT_STATUS_MORE_PROCESSING_REQUIRED: + /* Keep going, but return... */ + ret = EINPROGRESS; + break; + default: + ret = EAUTH; + goto out; + } + + if (vcp->vc_smbuid == 0) + vcp->vc_smbuid = rqp->sr_rpuid; + + /* + * Parse the reply + */ + smb_rq_getreply(rqp, &mdp); + + err = md_get_uint8(mdp, &wc); + if (err != 0) + wc = 0; + if (wc != 4) { + ret = EBADRPC; + goto out; + } + md_get_uint16le(mdp, NULL); /* secondary cmd */ + md_get_uint16le(mdp, NULL); /* andxoffset */ + md_get_uint16le(mdp, &action); /* action XXX */ + md_get_uint16le(mdp, &sblen); /* sec. blob len */ + md_get_uint16le(mdp, &bc); /* byte count */ + /* + * Get the security blob, after + * sanity-checking the length. + */ + if (sblen == 0 || sblen > bc) { + ret = EBADRPC; + goto out; + } + if (sblen > wk->wk_u_auth_rlen) { + ret = EBADRPC; + goto out; + } + sb = wk->wk_u_auth_rbuf.lp_ptr; + err = md_get_mem(mdp, sb, sblen, MB_MUSER); + if (err) { + ret = EBADRPC; + goto out; + } + + /* + * Native OS, LANMGR, & Domain follow here. + * We don't need them and don't parse them. + */ + +out: + if (err != 0 && err != EINPROGRESS) { + /* UID no longer valid. */ + vcp->vc_smbuid = 0; + } + if (rqp) + smb_rq_done(rqp); + + return (ret); +} + +int +smb_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + if (vcp->vc_smbuid == SMB_UID_UNKNOWN) + return (0); + + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp); + if (error) + return (error); + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); -static int smb_smb_readx(struct smb_share *ssp, uint16_t fid, - uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); -static int smb_smb_writex(struct smb_share *ssp, uint16_t fid, - uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); + /* + * Run this with a relatively short timeout. (5 sec.) + * We don't really care about the result here. + * Also, don't reconnect for this, of course! + */ + rqp->sr_flags |= SMBR_NORECONNECT; + error = smb_rq_internal(rqp, 5); + smb_rq_done(rqp); + return (error); +} /* * Get the string representation of a share "use" type, @@ -352,7 +871,7 @@ smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) * Modern create/open of file or directory. */ int -smb_smb_ntcreate( +smb1_smb_ntcreate( struct smb_share *ssp, struct mbchain *name_mb, uint32_t cr_flags, /* create flags */ @@ -475,7 +994,7 @@ done: } int -smb_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime, +smb1_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime, struct smb_cred *scrp) { struct smb_rq rq, *rqp = &rq; @@ -600,101 +1119,11 @@ smb_smb_close_prjob(struct smb_share *ssp, uint16_t fid, return (error); } -/* - * Common function for read/write with UIO. - * Called by netsmb smb_usr_rw, - * smbfs_readvnode, smbfs_writevnode - */ int -smb_rwuio(struct smb_share *ssp, uint16_t fid, uio_rw_t rw, - uio_t *uiop, smb_cred_t *scred, int timo) -{ - struct smb_vc *vcp = SSTOVC(ssp); - ssize_t save_resid; - uint32_t len, rlen, maxlen; - int error = 0; - int (*iofun)(struct smb_share *, uint16_t, uint32_t *, - uio_t *, smb_cred_t *, int); - - /* - * Determine which function to use, - * and the transfer size per call. - */ - if (SMB_DIALECT(vcp) >= SMB_DIALECT_NTLM0_12) { - /* - * Using NT LM 0.12, so readx, writex. - * Make sure we can represent the offset. - */ - if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0 && - (uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX) - return (EFBIG); - - if (rw == UIO_READ) { - iofun = smb_smb_readx; - if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) - maxlen = SMB_MAX_LARGE_RW_SIZE; - else - maxlen = vcp->vc_rxmax; - } else { /* UIO_WRITE */ - iofun = smb_smb_writex; - if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) - maxlen = SMB_MAX_LARGE_RW_SIZE; - else - maxlen = vcp->vc_wxmax; - } - } else { - /* - * Using the old SMB_READ and SMB_WRITE so - * we're limited to 32-bit offsets, etc. - * XXX: Someday, punt the old dialects. - */ - if ((uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX) - return (EFBIG); - - if (rw == UIO_READ) { - iofun = smb_smb_read; - maxlen = vcp->vc_rxmax; - } else { /* UIO_WRITE */ - iofun = smb_smb_write; - maxlen = vcp->vc_wxmax; - } - } - - save_resid = uiop->uio_resid; - while (uiop->uio_resid > 0) { - /* Lint: uio_resid may be 64-bits */ - rlen = len = (uint32_t)min(maxlen, uiop->uio_resid); - error = (*iofun)(ssp, fid, &rlen, uiop, scred, timo); - - /* - * Note: the iofun called uio_update, so - * not doing that here as one might expect. - * - * Quit the loop either on error, or if we - * transferred less then requested. - */ - if (error || (rlen < len)) - break; - - timo = 0; /* only first I/O should wait */ - } - if (error && (save_resid != uiop->uio_resid)) { - /* - * Stopped on an error after having - * successfully transferred data. - * Suppress this error. - */ - SMBSDEBUG("error %d suppressed\n", error); - error = 0; - } - - return (error); -} - -static int -smb_smb_readx(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, +smb_smb_readx(smb_fh_t *fhp, uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo) { + struct smb_share *ssp = FHTOSS(fhp); struct smb_rq *rqp; struct mbchain *mbp; struct mdchain *mdp; @@ -716,7 +1145,7 @@ smb_smb_readx(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, mb_put_uint8(mbp, 0xff); /* no secondary command */ mb_put_uint8(mbp, 0); /* MBZ */ mb_put_uint16le(mbp, 0); /* offset to secondary */ - mb_put_uint16le(mbp, fid); + mb_put_uint16le(mbp, fhp->fh_fid1); mb_put_uint32le(mbp, offlo); /* offset (low part) */ mb_put_uint16le(mbp, lenlo); /* MaxCount */ mb_put_uint16le(mbp, 1); /* MinCount */ @@ -786,10 +1215,11 @@ out: return (error); } -static int -smb_smb_writex(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, +int +smb_smb_writex(smb_fh_t *fhp, uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo) { + struct smb_share *ssp = FHTOSS(fhp); struct smb_rq *rqp; struct mbchain *mbp; struct mdchain *mdp; @@ -811,7 +1241,7 @@ smb_smb_writex(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, mb_put_uint8(mbp, 0xff); /* no secondary command */ mb_put_uint8(mbp, 0); /* MBZ */ mb_put_uint16le(mbp, 0); /* offset to secondary */ - mb_put_uint16le(mbp, fid); + mb_put_uint16le(mbp, fhp->fh_fid1); mb_put_uint32le(mbp, offlo); /* offset (low part) */ mb_put_uint32le(mbp, 0); /* MBZ (timeout) */ mb_put_uint16le(mbp, 0); /* !write-thru */ @@ -859,148 +1289,13 @@ out: return (error); } -static int -smb_smb_read(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, - uio_t *uiop, smb_cred_t *scred, int timo) -{ - struct smb_rq *rqp; - struct mbchain *mbp; - struct mdchain *mdp; - int error; - uint32_t off32; - uint16_t bc, cnt, dlen, rcnt, todo; - uint8_t wc; - - ASSERT(uiop->uio_loffset <= UINT32_MAX); - off32 = (uint32_t)uiop->uio_loffset; - ASSERT(*lenp <= UINT16_MAX); - cnt = (uint16_t)*lenp; - /* This next is an "estimate" of planned reads. */ - todo = (uint16_t)min(uiop->uio_resid, UINT16_MAX); - - error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, fid); - mb_put_uint16le(mbp, cnt); - mb_put_uint32le(mbp, off32); - mb_put_uint16le(mbp, todo); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - smb_rq_bend(rqp); - - if (timo == 0) - timo = smb_timo_read; - error = smb_rq_simple_timed(rqp, timo); - if (error) - goto out; - smb_rq_getreply(rqp, &mdp); - error = md_get_uint8(mdp, &wc); - if (error) - goto out; - if (wc != 5) { - error = EBADRPC; - goto out; - } - md_get_uint16le(mdp, &rcnt); /* ret. count */ - md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); /* res. */ - md_get_uint16le(mdp, &bc); /* byte count */ - md_get_uint8(mdp, NULL); /* buffer format */ - error = md_get_uint16le(mdp, &dlen); /* data len */ - if (error) - goto out; - if (dlen < rcnt) { - SMBSDEBUG("oops: dlen=%d rcnt=%d\n", - (int)dlen, (int)rcnt); - rcnt = dlen; - } - if (rcnt == 0) { - *lenp = 0; - goto out; - } - /* paranoid */ - if (rcnt > cnt) { - SMBSDEBUG("bad server! rcnt %d, cnt %d\n", - (int)rcnt, (int)cnt); - rcnt = cnt; - } - error = md_get_uio(mdp, uiop, (int)rcnt); - if (error) - goto out; - - /* success */ - *lenp = (int)rcnt; - -out: - smb_rq_done(rqp); - return (error); -} - -static int -smb_smb_write(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, - uio_t *uiop, smb_cred_t *scred, int timo) -{ - struct smb_rq *rqp; - struct mbchain *mbp; - struct mdchain *mdp; - int error; - uint32_t off32; - uint16_t cnt, rcnt, todo; - uint8_t wc; - - ASSERT(uiop->uio_loffset <= UINT32_MAX); - off32 = (uint32_t)uiop->uio_loffset; - ASSERT(*lenp <= UINT16_MAX); - cnt = (uint16_t)*lenp; - /* This next is an "estimate" of planned writes. */ - todo = (uint16_t)min(uiop->uio_resid, UINT16_MAX); - - error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, fid); - mb_put_uint16le(mbp, cnt); - mb_put_uint32le(mbp, off32); - mb_put_uint16le(mbp, todo); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_DATA); - mb_put_uint16le(mbp, cnt); - - error = mb_put_uio(mbp, uiop, *lenp); - if (error) - goto out; - smb_rq_bend(rqp); - if (timo == 0) - timo = smb_timo_write; - error = smb_rq_simple_timed(rqp, timo); - if (error) - goto out; - smb_rq_getreply(rqp, &mdp); - error = md_get_uint8(mdp, &wc); - if (error) - goto out; - if (wc != 1) { - error = EBADRPC; - goto out; - } - error = md_get_uint16le(mdp, &rcnt); - if (error) - goto out; - *lenp = rcnt; - -out: - smb_rq_done(rqp); - return (error); -} - static u_int32_t smbechoes = 0; +/* + * Note: the IOD calls this, so this request must not wait for + * connection state changes, etc. (uses smb_rq_internal) + */ int smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo) { @@ -1018,13 +1313,8 @@ smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo) smb_rq_bstart(rqp); mb_put_uint32le(mbp, atomic_inc_32_nv(&smbechoes)); smb_rq_bend(rqp); - /* - * Note: the IOD calls this, so - * this request must not wait for - * connection state changes, etc. - */ rqp->sr_flags |= SMBR_NORECONNECT; - error = smb_rq_simple_timed(rqp, timo); + error = smb_rq_internal(rqp, timo); SMBSDEBUG("%d\n", error); smb_rq_done(rqp); return (error); diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h index df0f28ec05..b780f9b21b 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h @@ -33,8 +33,8 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _NETSMB_SMB_SUBR_H_ @@ -43,8 +43,16 @@ #include <sys/cmn_err.h> #include <sys/lock.h> #include <sys/note.h> +#include <netsmb/mchain.h> #include <netsmb/smb_conn.h> +/* + * Possible lock commands + */ +#define SMB_LOCK_EXCL 0 +#define SMB_LOCK_SHARED 1 +#define SMB_LOCK_RELEASE 2 + struct msgb; /* avoiding sys/stream.h here */ /* Helper function for SMBERROR */ @@ -117,86 +125,131 @@ extern int smb_timo_open; extern int smb_timo_read; extern int smb_timo_write; extern int smb_timo_append; +extern dev_t nsmb_dev_tcp; +extern dev_t nsmb_dev_tcp6; -#define EMOREDATA (0x7fff) +/* + * Tunable timeout values. See: smb2_smb.c + */ +extern int smb2_timo_notice; +extern int smb2_timo_default; +extern int smb2_timo_open; +extern int smb2_timo_read; +extern int smb2_timo_write; +extern int smb2_timo_append; void smb_credinit(struct smb_cred *scred, cred_t *cr); void smb_credrele(struct smb_cred *scred); -void smb_oldlm_hash(const char *apwd, uchar_t *hash); -void smb_ntlmv1hash(const char *apwd, uchar_t *hash); -void smb_ntlmv2hash(const uchar_t *v1hash, const char *user, - const char *destination, uchar_t *v2hash); - -int smb_lmresponse(const uchar_t *hash, const uchar_t *C8, uchar_t *RN); -int smb_ntlmv2response(const uchar_t *hash, const uchar_t *C8, - const uchar_t *blob, size_t bloblen, uchar_t **RN, size_t *RNlen); int smb_maperror(int eclass, int eno); int smb_maperr32(uint32_t eno); +uint_t smb_doserr2status(int, int); +int smb_get_dstring(struct mdchain *mdc, struct smb_vc *vcp, + char *outbuf, size_t *outlen, int inlen); int smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src, int len, int caseopt, int *lenp); int smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, const char *src, int caseopt); int smb_put_string(struct smb_rq *rqp, const char *src); int smb_put_asunistring(struct smb_rq *rqp, const char *src); -int smb_checksmp(void); + +int smb_smb_ntcreate(struct smb_share *ssp, struct mbchain *name_mb, + uint32_t crflag, uint32_t req_acc, uint32_t efa, uint32_t sh_acc, + uint32_t disp, uint32_t createopt, uint32_t impersonate, + struct smb_cred *scrp, smb_fh_t *fhp, + uint32_t *cr_act_p, struct smbfattr *fap); + +int smb_smb_close(struct smb_share *ssp, smb_fh_t *fhp, + struct smb_cred *scrp); + +int smb_rwuio(smb_fh_t *fhp, uio_rw_t rw, + uio_t *uiop, smb_cred_t *scred, int timo); int smb_cmp_sockaddr(struct sockaddr *, struct sockaddr *); struct sockaddr *smb_dup_sockaddr(struct sockaddr *sa); void smb_free_sockaddr(struct sockaddr *sa); -int smb_toupper(const char *, char *, size_t); +int smb_sign_init(struct smb_vc *); void smb_rq_sign(struct smb_rq *); int smb_rq_verify(struct smb_rq *); int smb_calcv2mackey(struct smb_vc *, const uchar_t *, const uchar_t *, size_t); int smb_calcmackey(struct smb_vc *, const uchar_t *, const uchar_t *, size_t); -void smb_crypto_mech_init(void); +int smb2_sign_init(struct smb_vc *); +void smb2_rq_sign(struct smb_rq *); +int smb2_rq_verify(struct smb_rq *); /* * SMB protocol level functions */ +int smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred); int smb_smb_echo(smb_vc_t *vcp, smb_cred_t *scred, int timo); int smb_smb_treeconnect(smb_share_t *ssp, smb_cred_t *scred); int smb_smb_treedisconnect(smb_share_t *ssp, smb_cred_t *scred); -int -smb_smb_ntcreate(struct smb_share *ssp, struct mbchain *name_mb, +int smb1_smb_ntcreate(struct smb_share *ssp, struct mbchain *name_mb, uint32_t crflag, uint32_t req_acc, uint32_t efa, uint32_t sh_acc, uint32_t disp, uint32_t createopt, uint32_t impersonate, struct smb_cred *scrp, uint16_t *fidp, uint32_t *cr_act_p, struct smbfattr *fap); -int smb_smb_close(struct smb_share *ssp, uint16_t fid, +int smb1_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime, struct smb_cred *scrp); -int -smb_smb_open_prjob(struct smb_share *ssp, char *title, +int smb_smb_open_prjob(struct smb_share *ssp, char *title, uint16_t setuplen, uint16_t mode, struct smb_cred *scrp, uint16_t *fidp); int smb_smb_close_prjob(struct smb_share *ssp, uint16_t fid, struct smb_cred *scrp); -int smb_rwuio(smb_share_t *ssp, uint16_t fid, uio_rw_t rw, +int smb_smb_readx(smb_fh_t *fhp, uint32_t *lenp, + uio_t *uiop, smb_cred_t *scred, int timo); +int smb_smb_writex(smb_fh_t *fhp, uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo); /* - * time conversions + * SMB2 protocol level functions */ +int smb2_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred); +int smb2_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred); +int smb2_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred); +int smb2_smb_echo(smb_vc_t *vcp, smb_cred_t *scred, int timo); +int smb2_smb_treeconnect(smb_share_t *ssp, smb_cred_t *scred); +int smb2_smb_treedisconnect(smb_share_t *ssp, smb_cred_t *scred); + +int +smb2_smb_ntcreate(struct smb_share *ssp, struct mbchain *name_mb, + struct mbchain *cctx_in, struct mdchain *cctx_out, + uint32_t crflag, uint32_t req_acc, uint32_t efa, uint32_t sh_acc, + uint32_t disp, uint32_t createopt, uint32_t impersonate, + struct smb_cred *scrp, smb2fid_t *fidp, + uint32_t *cr_act_p, struct smbfattr *fap); + +int smb2_smb_close(struct smb_share *ssp, smb2fid_t *fid, + struct smb_cred *scrp); + +int smb2_smb_ioctl(struct smb_share *ssp, smb2fid_t *fid, + struct mbchain *data_in, struct mdchain *data_out, + uint32_t *data_out_sz, /* max / returned */ + uint32_t ctl_code, struct smb_cred *scrp); + +int smb2_smb_read(smb_fh_t *fhp, uint32_t *lenp, + uio_t *uiop, smb_cred_t *scred, int timo); +int smb2_smb_write(smb_fh_t *fhp, uint32_t *lenp, + uio_t *uiop, smb_cred_t *scred, int timo); -void smb_time_init(void); -void smb_time_fini(void); +/* + * time conversions + */ void smb_time_local2server(struct timespec *tsp, int tzoff, long *seconds); void smb_time_server2local(ulong_t seconds, int tzoff, struct timespec *tsp); void smb_time_NT2local(uint64_t nsec, struct timespec *tsp); void smb_time_local2NT(struct timespec *tsp, uint64_t *nsec); -void smb_time_unix2dos(struct timespec *tsp, int tzoff, uint16_t *ddp, - uint16_t *dtp, uint8_t *dhp); -void smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff, - struct timespec *tsp); #endif /* !_NETSMB_SMB_SUBR_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c index e29a96631a..bf1e605187 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c @@ -34,6 +34,8 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Portions Copyright (C) 2001 - 2013 Apple Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -57,50 +59,19 @@ #include <netsmb/smb_rq.h> #include <netsmb/smb_subr.h> -/* - * XXX:This conversion might not be fully MS-Compatible - * for calculating hashes. The output length may differ - * for some locales and needs to be handled from where - * the call is made. - */ -int -smb_toupper(const char *inbuf, char *outbuf, size_t outlen) -{ - int err = 0; - size_t inlen, inrem, outrem; - - inrem = inlen = strlen(inbuf); - outrem = outlen; - (void) u8_textprep_str((char *)inbuf, &inrem, outbuf, &outrem, - U8_TEXTPREP_TOUPPER, U8_UNICODE_LATEST, &err); - /* inrem, outrem are bytes unused, remaining */ - if (inrem) { - SMBSDEBUG("input %d remains: %s\n", (int)inrem, inbuf); - inlen -= inrem; - } - if (outrem) { - outlen -= outrem; - outbuf[outlen] = '\0'; - } - if (outlen > inlen) { - SMBSDEBUG("outlen > inlen! (%d > %d)\n", - (int)outlen, (int)inlen); - /* Truncate to inlen here? */ - } - - return (err); -} - void smb_credinit(struct smb_cred *scred, cred_t *cr) { /* cr arg is optional */ if (cr == NULL) cr = ddi_get_cred(); +#ifdef _KERNEL if (is_system_labeled()) { cr = crdup(cr); (void) setpflags(NET_MAC_AWARE, 1, cr); - } else { + } else +#endif + { crhold(cr); } scred->scr_cred = cr; @@ -115,6 +86,14 @@ smb_credrele(struct smb_cred *scred) } } +#ifndef _KERNEL +/* ARGSUSED */ +void +smb_debugmsg(const char *func, char *msg) +{ +} +#endif /* _KERNEL */ + /* * Helper for the SMBERROR macro, etc. * This is also a good place for a breakpoint @@ -137,6 +116,9 @@ smb_errmsg(int cel, const char *func_name, const char *fmt, ...) DTRACE_PROBE2(debugmsg2, (char *), func_name, (char *), buf); +#ifndef _KERNEL + smb_debugmsg(func_name, buf); +#endif } else { /* * This is one of our xxxERROR macros. @@ -190,6 +172,9 @@ m_dumpm(mblk_t *m) #ifndef ETIME #define ETIME ETIMEDOUT #endif +#ifndef EMOREDATA +#define EMOREDATA (0x7fff) +#endif /* * Log any un-handled NT or DOS errors we encounter. @@ -219,7 +204,9 @@ static const nt2errno_t nt2errno[] = { {NT_STATUS_ACCOUNT_RESTRICTION, EACCES}, {NT_STATUS_ADDRESS_ALREADY_EXISTS, EADDRINUSE}, {NT_STATUS_BAD_NETWORK_NAME, ENOENT}, - {NT_STATUS_BUFFER_TOO_SMALL, EMOREDATA}, + {NT_STATUS_BAD_NETWORK_PATH, ENOENT}, + {NT_STATUS_BUFFER_TOO_SMALL, E2BIG}, + {NT_STATUS_CANCELLED, ECANCELED}, {NT_STATUS_CANNOT_DELETE, EACCES}, {NT_STATUS_CONFLICTING_ADDRESSES, EADDRINUSE}, {NT_STATUS_CONNECTION_ABORTED, ECONNABORTED}, @@ -233,59 +220,87 @@ static const nt2errno_t nt2errno[] = { {NT_STATUS_DISK_FULL, ENOSPC}, {NT_STATUS_DLL_NOT_FOUND, ELIBACC}, {NT_STATUS_DUPLICATE_NAME, EINVAL}, + {NT_STATUS_EAS_NOT_SUPPORTED, ENOTSUP}, + {NT_STATUS_EA_TOO_LARGE, E2BIG}, {NT_STATUS_END_OF_FILE, ENODATA}, + {NT_STATUS_FILE_CLOSED, EBADF}, + {NT_STATUS_FILE_DELETED, ENOENT}, + {NT_STATUS_FILE_INVALID, EIO}, {NT_STATUS_FILE_IS_A_DIRECTORY, EISDIR}, {NT_STATUS_FILE_LOCK_CONFLICT, EAGAIN}, + {NT_STATUS_FILE_RENAMED, ENOENT}, {NT_STATUS_FLOAT_INEXACT_RESULT, ERANGE}, {NT_STATUS_FLOAT_OVERFLOW, ERANGE}, {NT_STATUS_FLOAT_UNDERFLOW, ERANGE}, {NT_STATUS_HOST_UNREACHABLE, EHOSTUNREACH}, - {NT_STATUS_ILL_FORMED_PASSWORD, EACCES}, + {NT_STATUS_ILL_FORMED_PASSWORD, EAUTH}, + {NT_STATUS_INFO_LENGTH_MISMATCH, EINVAL}, + {NT_STATUS_INSUFFICIENT_RESOURCES, EAGAIN}, + {NT_STATUS_INSUFF_SERVER_RESOURCES, EAGAIN}, {NT_STATUS_INTEGER_OVERFLOW, ERANGE}, - {NT_STATUS_INVALID_ACCOUNT_NAME, EACCES}, + {NT_STATUS_INVALID_ACCOUNT_NAME, EAUTH}, + {NT_STATUS_INVALID_BUFFER_SIZE, EIO}, + {NT_STATUS_INVALID_DEVICE_REQUEST, EINVAL}, {NT_STATUS_INVALID_HANDLE, EBADF}, + {NT_STATUS_INVALID_INFO_CLASS, EINVAL}, {NT_STATUS_INVALID_LEVEL, ENOTSUP}, - {NT_STATUS_INVALID_LOGON_HOURS, EACCES}, + {NT_STATUS_INVALID_LOCK_SEQUENCE, EINVAL}, + {NT_STATUS_INVALID_LOGON_HOURS, EAUTH}, {NT_STATUS_INVALID_OWNER, EINVAL}, {NT_STATUS_INVALID_PARAMETER, EINVAL}, {NT_STATUS_INVALID_PIPE_STATE, EPIPE}, {NT_STATUS_INVALID_PRIMARY_GROUP, EINVAL}, {NT_STATUS_INVALID_WORKSTATION, EACCES}, {NT_STATUS_IN_PAGE_ERROR, EFAULT}, + {NT_STATUS_IO_DEVICE_ERROR, EIO}, {NT_STATUS_IO_TIMEOUT, ETIMEDOUT}, - {NT_STATUS_IP_ADDRESS_CONFLICT1, ENOTUNIQ}, - {NT_STATUS_IP_ADDRESS_CONFLICT2, ENOTUNIQ}, + {NT_STATUS_IP_ADDRESS_CONFLICT1, EADDRINUSE}, + {NT_STATUS_IP_ADDRESS_CONFLICT2, EADDRINUSE}, {NT_STATUS_LICENSE_QUOTA_EXCEEDED, EDQUOT}, {NT_STATUS_LOCK_NOT_GRANTED, EAGAIN}, - {NT_STATUS_LOGIN_TIME_RESTRICTION, EACCES}, - {NT_STATUS_LOGON_FAILURE, EACCES}, + {NT_STATUS_LOGIN_TIME_RESTRICTION, EAUTH}, + {NT_STATUS_LOGON_FAILURE, EAUTH}, + {NT_STATUS_LOGON_TYPE_NOT_GRANTED, EAUTH}, {NT_STATUS_MEDIA_WRITE_PROTECTED, EROFS}, {NT_STATUS_MEMORY_NOT_ALLOCATED, EFAULT}, + {NT_STATUS_MORE_PROCESSING_REQUIRED, EINPROGRESS}, {NT_STATUS_NAME_TOO_LONG, ENAMETOOLONG}, {NT_STATUS_NETWORK_ACCESS_DENIED, EACCES}, {NT_STATUS_NETWORK_BUSY, EBUSY}, + {NT_STATUS_NETWORK_NAME_DELETED, ENOENT}, {NT_STATUS_NETWORK_UNREACHABLE, ENETUNREACH}, {NT_STATUS_NET_WRITE_FAULT, ECOMM}, + {NT_STATUS_NONEXISTENT_EA_ENTRY, ENOENT}, {NT_STATUS_NONEXISTENT_SECTOR, ESPIPE}, {NT_STATUS_NONE_MAPPED, EINVAL}, {NT_STATUS_NOT_A_DIRECTORY, ENOTDIR}, + {NT_STATUS_NOT_FOUND, ENOENT}, {NT_STATUS_NOT_IMPLEMENTED, ENOTSUP}, + {NT_STATUS_NOT_LOCKED, ENOLCK}, {NT_STATUS_NOT_MAPPED_VIEW, EINVAL}, {NT_STATUS_NOT_SUPPORTED, ENOTSUP}, + {NT_STATUS_NO_EAS_ON_FILE, ENOENT}, + {NT_STATUS_NO_LOGON_SERVERS, EAUTH}, {NT_STATUS_NO_MEDIA, ENOMEDIUM}, {NT_STATUS_NO_MEDIA_IN_DEVICE, ENOMEDIUM}, {NT_STATUS_NO_MEMORY, ENOMEM}, {NT_STATUS_NO_SUCH_DEVICE, ENODEV}, {NT_STATUS_NO_SUCH_FILE, ENOENT}, + {NT_STATUS_NO_SUCH_LOGON_SESSION, EAUTH}, + {NT_STATUS_NO_SUCH_USER, EAUTH}, + {NT_STATUS_NO_TRUST_LSA_SECRET, EAUTH}, + {NT_STATUS_NO_TRUST_SAM_ACCOUNT, EAUTH}, {NT_STATUS_OBJECT_NAME_COLLISION, EEXIST}, {NT_STATUS_OBJECT_NAME_INVALID, EINVAL}, {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT}, {NT_STATUS_OBJECT_PATH_INVALID, ENOTDIR}, {NT_STATUS_OBJECT_PATH_NOT_FOUND, ENOENT}, + {NT_STATUS_OBJECT_PATH_SYNTAX_BAD, EINVAL}, + {NT_STATUS_OBJECT_TYPE_MISMATCH, EBADF}, {NT_STATUS_PAGEFILE_QUOTA, EDQUOT}, - {NT_STATUS_PASSWORD_EXPIRED, EACCES}, - {NT_STATUS_PASSWORD_MUST_CHANGE, EACCES}, - {NT_STATUS_PASSWORD_RESTRICTION, EACCES}, + {NT_STATUS_PASSWORD_EXPIRED, EAUTH}, + {NT_STATUS_PASSWORD_MUST_CHANGE, EAUTH}, + {NT_STATUS_PASSWORD_RESTRICTION, EAUTH}, {NT_STATUS_PATH_NOT_COVERED, ENOENT}, {NT_STATUS_PIPE_BROKEN, EPIPE}, {NT_STATUS_PIPE_BUSY, EPIPE}, @@ -293,11 +308,12 @@ static const nt2errno_t nt2errno[] = { {NT_STATUS_PIPE_DISCONNECTED, EPIPE}, {NT_STATUS_PIPE_NOT_AVAILABLE, EBUSY}, {NT_STATUS_PORT_CONNECTION_REFUSED, ECONNREFUSED}, + {NT_STATUS_PORT_DISCONNECTED, EBADF}, {NT_STATUS_PORT_MESSAGE_TOO_LONG, EMSGSIZE}, {NT_STATUS_PORT_UNREACHABLE, EHOSTUNREACH}, {NT_STATUS_PROTOCOL_UNREACHABLE, ENOPROTOOPT}, {NT_STATUS_QUOTA_EXCEEDED, EDQUOT}, - {NT_STATUS_RANGE_NOT_LOCKED, EIO}, + {NT_STATUS_RANGE_NOT_LOCKED, EAGAIN}, /* like F_SETLK */ {NT_STATUS_REGISTRY_QUOTA_LIMIT, EDQUOT}, {NT_STATUS_REMOTE_DISCONNECT, ESHUTDOWN}, {NT_STATUS_REMOTE_NOT_LISTENING, ECONNREFUSED}, @@ -307,9 +323,11 @@ static const nt2errno_t nt2errno[] = { {NT_STATUS_TIMER_NOT_CANCELED, ETIME}, {NT_STATUS_TOO_MANY_LINKS, EMLINK}, {NT_STATUS_TOO_MANY_OPENED_FILES, EMFILE}, + {NT_STATUS_TRUSTED_DOMAIN_FAILURE, EAUTH}, + {NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE, EAUTH}, {NT_STATUS_UNABLE_TO_FREE_VM, EADDRINUSE}, {NT_STATUS_UNSUCCESSFUL, EINVAL}, - {NT_STATUS_WRONG_PASSWORD, EACCES}, + {NT_STATUS_WRONG_PASSWORD, EAUTH}, {0, 0} }; @@ -593,14 +611,6 @@ static const nt2doserr_t nt2doserr[] = { {ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, {ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, - {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, - {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, - {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, - {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, - {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, - {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, - {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, - {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, {ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, @@ -868,6 +878,19 @@ smb_maperr32(uint32_t nterr) return (EIO); } +uint_t +smb_doserr2status(int dclass, int derr) +{ + const nt2doserr_t *nt2d; + + if (dclass == 0 && derr == 0) + return (0); + + for (nt2d = nt2doserr; nt2d->nterr; nt2d++) + if (nt2d->dclass == dclass && nt2d->derr == derr) + return (nt2d->nterr); + return (NT_STATUS_UNSUCCESSFUL); +} int smb_maperror(int eclass, int eno) @@ -996,13 +1019,90 @@ smb_maperror(int eclass, int eno) return (EIO); } -#if defined(NOICONVSUPPORT) || defined(lint) -extern int iconv_conv(void *handle, const char **inbuf, - size_t *inbytesleft, char **outbuf, size_t *outbytesleft); -#endif - #define SMALL_CONV 256 +/* + * Decode an SMB OTW string (Unicode or OEM chars) + * converting to UTF-8 in the output buffer. + * outlen is in/out (max size on input) + * insize is the wire size (2 * chars if unicode) + * The output string is null terminated. + * Output length does not include the null. + */ +int +smb_get_dstring(struct mdchain *mdc, struct smb_vc *vcp, + char *outbuf, size_t *outlen, int insize) +{ + uint16_t convbuf[SMALL_CONV]; + uint16_t *cbuf; + size_t cbufalloc, inlen, outsize; + int error; + + if (insize <= 0) + return (0); + /* Note: inlen is UTF-16 symbols. */ + inlen = insize / 2; + + if (*outlen < 2) + return (EINVAL); + outsize = *outlen - 1; /* room for null */ + + /* + * Get a buffer for the conversion and fill it. + * Use stack buffer if the string is + * small enough, else allocate. + */ + if (insize < sizeof (convbuf)) { + cbufalloc = 0; + cbuf = convbuf; + } else { + cbufalloc = insize + 2; + cbuf = kmem_alloc(cbufalloc, KM_SLEEP); + } + error = md_get_mem(mdc, cbuf, insize, MB_MSYSTEM); + if (error != 0) + goto out; + cbuf[inlen] = 0; + + /* + * Handle the easy case (non-unicode). + * XXX: Technically, we should convert + * the string to OEM codeset first... + * Modern servers all use Unicode, so + * this is good enough. + */ + if (SMB_UNICODE_STRINGS(vcp) == 0) { + *outlen = strlcpy(outbuf, (char *)cbuf, outsize); + if (*outlen > outsize) { + *outlen = outsize; + error = E2BIG; + } + } else { + /* + * Convert from UTF-16 to UTF-8 + */ + error = uconv_u16tou8(cbuf, &inlen, + (uchar_t *)outbuf, outlen, + UCONV_IN_LITTLE_ENDIAN); + if (error == 0) { + outbuf[*outlen] = '\0'; + } + } + + ASSERT(*outlen == strlen(outbuf)); + +out: + if (cbufalloc != 0) + kmem_free(cbuf, cbufalloc); + + return (error); +} + +/* + * It's surprising that this function does utf8-ucs2 conversion. + * One would expect only smb_put_dstring to do that. + * Fixing that will require changing a bunch of callers. XXX + */ /*ARGSUSED*/ int smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src, @@ -1080,3 +1180,130 @@ smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, const char *src, return (error); } +int +smb_smb_ntcreate(struct smb_share *ssp, struct mbchain *name_mb, + uint32_t crflag, uint32_t req_acc, uint32_t efa, uint32_t sh_acc, + uint32_t disp, uint32_t createopt, uint32_t impersonate, + struct smb_cred *scrp, smb_fh_t *fhp, + uint32_t *cr_act_p, struct smbfattr *fap) +{ + int err; + + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + err = smb2_smb_ntcreate(ssp, name_mb, NULL, NULL, + crflag, req_acc, efa, sh_acc, disp, createopt, + impersonate, scrp, &fhp->fh_fid2, cr_act_p, fap); + } else { + err = smb1_smb_ntcreate(ssp, name_mb, crflag, req_acc, + efa, sh_acc, disp, createopt, impersonate, scrp, + &fhp->fh_fid1, cr_act_p, fap); + } + return (err); +} + +int +smb_smb_close(struct smb_share *ssp, smb_fh_t *fhp, + struct smb_cred *scrp) +{ + int err; + + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + err = smb2_smb_close(ssp, &fhp->fh_fid2, scrp); + } else { + err = smb1_smb_close(ssp, fhp->fh_fid1, NULL, scrp); + } + + return (err); +} + +/* + * Largest size to use with LARGE_READ/LARGE_WRITE. + * Specs say up to 64k data bytes, but Windows traffic + * uses 60k... no doubt for some good reason. + * (Probably to keep 4k block alignment.) + */ +uint32_t smb1_large_io_max = (60*1024); + +/* + * Common function for read/write with UIO. + * Called by netsmb smb_usr_rw, + * smbfs_readvnode, smbfs_writevnode + */ +int +smb_rwuio(smb_fh_t *fhp, uio_rw_t rw, + uio_t *uiop, smb_cred_t *scred, int timo) +{ + struct smb_share *ssp = FHTOSS(fhp); + struct smb_vc *vcp = SSTOVC(ssp); + ssize_t save_resid; + uint32_t len, rlen, maxlen; + int error = 0; + int (*iofun)(smb_fh_t *, uint32_t *, + uio_t *, smb_cred_t *, int); + + /* After reconnect, the fid is invalid. */ + if (fhp->fh_vcgenid != ssp->ss_vcgenid) + return (ESTALE); + + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + if (rw == UIO_READ) { + iofun = smb2_smb_read; + maxlen = vcp->vc_sopt.sv2_maxread; + } else { /* UIO_WRITE */ + iofun = smb2_smb_write; + maxlen = vcp->vc_sopt.sv2_maxwrite; + } + } else { + /* + * Using NT LM 0.12, so readx, writex. + * Make sure we can represent the offset. + */ + if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0 && + (uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX) + return (EFBIG); + + if (rw == UIO_READ) { + iofun = smb_smb_readx; + if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) + maxlen = smb1_large_io_max; + else + maxlen = vcp->vc_rxmax; + } else { /* UIO_WRITE */ + iofun = smb_smb_writex; + if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) + maxlen = smb1_large_io_max; + else + maxlen = vcp->vc_wxmax; + } + } + + save_resid = uiop->uio_resid; + while (uiop->uio_resid > 0) { + /* Lint: uio_resid may be 64-bits */ + rlen = len = (uint32_t)min(maxlen, uiop->uio_resid); + error = (*iofun)(fhp, &rlen, uiop, scred, timo); + + /* + * Note: the iofun called uio_update, so + * not doing that here as one might expect. + * + * Quit the loop either on error, or if we + * transferred less then requested. + */ + if (error || (rlen < len)) + break; + + timo = 0; /* only first I/O should wait */ + } + if (error && (save_resid != uiop->uio_resid)) { + /* + * Stopped on an error after having + * successfully transferred data. + * Suppress this error. + */ + SMBSDEBUG("error %d suppressed\n", error); + error = 0; + } + + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_time.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_time.c index e984ded911..0116d6ea6e 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_time.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_time.c @@ -35,6 +35,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -55,61 +57,6 @@ #include <netsmb/smb_subr.h> /* - * Time & date conversion routines taken from msdosfs. Although leap - * year calculation is bogus, it's sufficient before 2100 :) - */ -/* - * This is the format of the contents of the deTime field in the direntry - * structure. - * We don't use bitfields because we don't know how compilers for - * arbitrary machines will lay them out. - */ -#define DT_2SECONDS_MASK 0x1F /* seconds divided by 2 */ -#define DT_2SECONDS_SHIFT 0 -#define DT_MINUTES_MASK 0x7E0 /* minutes */ -#define DT_MINUTES_SHIFT 5 -#define DT_HOURS_MASK 0xF800 /* hours */ -#define DT_HOURS_SHIFT 11 - -/* - * This is the format of the contents of the deDate field in the direntry - * structure. - */ -#define DD_DAY_MASK 0x1F /* day of month */ -#define DD_DAY_SHIFT 0 -#define DD_MONTH_MASK 0x1E0 /* month */ -#define DD_MONTH_SHIFT 5 -#define DD_YEAR_MASK 0xFE00 /* year - 1980 */ -#define DD_YEAR_SHIFT 9 -/* - * Total number of days that have passed for each month in a regular year. - */ -static ushort_t regyear[] = { - 31, 59, 90, 120, 151, 181, - 212, 243, 273, 304, 334, 365 -}; - -/* - * Total number of days that have passed for each month in a leap year. - */ -static ushort_t leapyear[] = { - 31, 60, 91, 121, 152, 182, - 213, 244, 274, 305, 335, 366 -}; - -/* - * Variables used to remember parts of the last time conversion. Maybe we - * can avoid a full conversion. - */ -static ulong_t lasttime; -static ulong_t lastday; -static ushort_t lastddate; -static ushort_t lastdtime; - -/* Lock for the lastxxx variables */ -static kmutex_t lastdt_lock; - -/* * Number of seconds between 1970 and 1601 year * (134774 days) */ @@ -193,161 +140,3 @@ smb_time_server2local(ulong_t seconds, int tzoff, struct timespec *tsp) tsp->tv_sec = seconds + tzoff * 60; tsp->tv_nsec = 0; } - -/* - * Time conversions to/from DOS format, for old dialects. - */ - -void -smb_time_unix2dos(struct timespec *tsp, int tzoff, u_int16_t *ddp, - u_int16_t *dtp, u_int8_t *dhp) -{ - long t; - ulong_t days, year, month, inc; - ushort_t *months; - - mutex_enter(&lastdt_lock); - - /* - * If the time from the last conversion is the same as now, then - * skip the computations and use the saved result. - */ - smb_time_local2server(tsp, tzoff, &t); - t &= ~1; - if (lasttime != t) { - lasttime = t; - if (t < 0) { - /* - * This is before 1970, so it's before 1980, - * and can't be represented as a DOS time. - * Just represent it as the DOS epoch. - */ - lastdtime = 0; - lastddate = (1 << DD_DAY_SHIFT) - + (1 << DD_MONTH_SHIFT) - + ((1980 - 1980) << DD_YEAR_SHIFT); - } else { - lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) - + (((t / 60) % 60) << DT_MINUTES_SHIFT) - + (((t / 3600) % 24) << DT_HOURS_SHIFT); - - /* - * If the number of days since 1970 is the same as - * the last time we did the computation then skip - * all this leap year and month stuff. - */ - days = t / (24 * 60 * 60); - if (days != lastday) { - lastday = days; - for (year = 1970; ; year++) { - /* - * XXX - works in 2000, but won't - * work in 2100. - */ - inc = year & 0x03 ? 365 : 366; - if (days < inc) - break; - days -= inc; - } - /* - * XXX - works in 2000, but won't work in 2100. - */ - months = year & 0x03 ? regyear : leapyear; - for (month = 0; days >= months[month]; month++) - ; - if (month > 0) - days -= months[month - 1]; - lastddate = ((days + 1) << DD_DAY_SHIFT) - + ((month + 1) << DD_MONTH_SHIFT); - /* - * Remember DOS's idea of time is relative - * to 1980, but UN*X's is relative to 1970. - * If somehow we get a time before 1980 then - * don't give totally crazy results. - */ - if (year > 1980) - lastddate += (year - 1980) << - DD_YEAR_SHIFT; - } - } - } - if (dhp) - *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; - - *ddp = lastddate; - *dtp = lastdtime; - - mutex_exit(&lastdt_lock); -} - -/* - * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that - * interval there were 8 regular years and 2 leap years. - */ -#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) - -static ushort_t lastdosdate; -static ulong_t lastseconds; - -void -smb_dos2unixtime(uint_t dd, uint_t dt, uint_t dh, int tzoff, - struct timespec *tsp) -{ - ulong_t seconds; - ulong_t month; - ulong_t year; - ulong_t days; - ushort_t *months; - - if (dd == 0) { - tsp->tv_sec = 0; - tsp->tv_nsec = 0; - return; - } - seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) - + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 - + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 - + dh / 100; - - /* - * If the year, month, and day from the last conversion are the - * same then use the saved value. - */ - mutex_enter(&lastdt_lock); - if (lastdosdate != dd) { - lastdosdate = (ushort_t)dd; - days = 0; - year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; - days = year * 365; - days += year / 4 + 1; /* add in leap days */ - /* - * XXX - works in 2000, but won't work in 2100. - */ - if ((year & 0x03) == 0) - days--; /* if year is a leap year */ - months = year & 0x03 ? regyear : leapyear; - month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; - if (month < 1 || month > 12) { - month = 1; - } - if (month > 1) - days += months[month - 2]; - days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; - lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; - } - smb_time_server2local(seconds + lastseconds, tzoff, tsp); - tsp->tv_nsec = (dh % 100) * 10000000; - mutex_exit(&lastdt_lock); -} - -void -smb_time_init(void) -{ - mutex_init(&lastdt_lock, NULL, MUTEX_DEFAULT, NULL); -} - -void -smb_time_fini(void) -{ - mutex_destroy(&lastdt_lock); -} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h index f954056a79..1060139491 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h @@ -35,6 +35,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _NETSMB_SMB_TRAN_H_ @@ -42,6 +44,9 @@ #include <sys/socket.h> #include <sys/stream.h> +#ifndef _KERNEL +struct file; +#endif /* * Known transports @@ -49,11 +54,14 @@ #define SMBT_NBTCP 1 /* - * Transport parameters + * Transport parameters, for tr_getparam/tr_setparam */ -#define SMBTP_SNDSZ 1 /* R - int */ -#define SMBTP_RCVSZ 2 /* R - int */ -#define SMBTP_TIMEOUT 3 /* RW - struct timespec */ +#define SMBTP_TCP_NODELAY 0x01 /* RW - int */ +#define SMBTP_TCP_CON_TMO 0x13 /* RW - int */ +#define SMBTP_KEEPALIVE SO_KEEPALIVE /* RW - int */ +#define SMBTP_SNDBUF SO_SNDBUF /* RW - int */ +#define SMBTP_RCVBUF SO_RCVBUF /* RW - int */ +#define SMBTP_RCVTIMEO SO_RCVTIMEO /* RW - int? */ struct smb_tran_ops; @@ -62,12 +70,12 @@ struct smb_tran_desc { int (*tr_create)(struct smb_vc *vcp, cred_t *cr); int (*tr_done)(struct smb_vc *vcp); int (*tr_bind)(struct smb_vc *vcp, struct sockaddr *sap); + int (*tr_unbind)(struct smb_vc *vcp); int (*tr_connect)(struct smb_vc *vcp, struct sockaddr *sap); int (*tr_disconnect)(struct smb_vc *vcp); int (*tr_send)(struct smb_vc *vcp, mblk_t *m); int (*tr_recv)(struct smb_vc *vcp, mblk_t **mpp); int (*tr_poll)(struct smb_vc *vcp, int ticks); - int (*tr_loan_fp)(struct smb_vc *, struct file *, cred_t *cr); int (*tr_getparam)(struct smb_vc *vcp, int param, void *data); int (*tr_setparam)(struct smb_vc *vcp, int param, void *data); int (*tr_fatal)(struct smb_vc *vcp, int error); @@ -78,12 +86,12 @@ typedef struct smb_tran_desc smb_tran_desc_t; #define SMB_TRAN_CREATE(vcp, cr) (vcp)->vc_tdesc->tr_create(vcp, cr) #define SMB_TRAN_DONE(vcp) (vcp)->vc_tdesc->tr_done(vcp) #define SMB_TRAN_BIND(vcp, sap) (vcp)->vc_tdesc->tr_bind(vcp, sap) +#define SMB_TRAN_UNBIND(vcp) (vcp)->vc_tdesc->tr_unbind(vcp) #define SMB_TRAN_CONNECT(vcp, sap) (vcp)->vc_tdesc->tr_connect(vcp, sap) #define SMB_TRAN_DISCONNECT(vcp) (vcp)->vc_tdesc->tr_disconnect(vcp) #define SMB_TRAN_SEND(vcp, m) (vcp)->vc_tdesc->tr_send(vcp, m) #define SMB_TRAN_RECV(vcp, m) (vcp)->vc_tdesc->tr_recv(vcp, m) #define SMB_TRAN_POLL(vcp, t) (vcp)->vc_tdesc->tr_poll(vcp, t) -#define SMB_TRAN_LOAN_FP(vcp, f, cr) (vcp)->vc_tdesc->tr_loan_fp(vcp, f, cr) #define SMB_TRAN_GETPARAM(vcp, par, data) \ (vcp)->vc_tdesc->tr_getparam(vcp, par, data) #define SMB_TRAN_SETPARAM(vcp, par, data) \ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c index 4f101a4f5f..849e91637e 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c @@ -34,7 +34,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -73,13 +74,6 @@ #include <netsmb/smb_tran.h> #include <netsmb/smb_trantcp.h> -/* - * SMB messages are up to 64K. - * Let's leave room for two. - */ -static int smb_tcpsndbuf = 0x20000; -static int smb_tcprcvbuf = 0x20000; - static int nb_disconnect(struct nbpcb *nbp); @@ -163,7 +157,7 @@ nb_getmsg_mlen(struct nbpcb *nbp, mblk_t **mpp, size_t mlen) case T_DISCON_IND: /* Peer disconnected. */ NBDEBUG("T_DISCON_IND: reason=%d", - pptr->discon_ind.DISCON_reason); + (int)pptr->discon_ind.DISCON_reason); goto discon; case T_ORDREL_IND: /* Peer disconnecting. */ @@ -176,11 +170,11 @@ nb_getmsg_mlen(struct nbpcb *nbp, mblk_t **mpp, size_t mlen) goto discon; default: NBDEBUG("T_OK_ACK/prim=%d", - pptr->ok_ack.CORRECT_prim); + (int)pptr->ok_ack.CORRECT_prim); goto discon; } default: - NBDEBUG("M_PROTO/type=%d", pptr->type); + NBDEBUG("M_PROTO/type=%d", (int)pptr->type); goto discon; } break; /* M_PROTO, M_PCPROTO */ @@ -485,22 +479,45 @@ out: * This is called only by the thread creating this endpoint, * so we're single-threaded here. */ -/*ARGSUSED*/ static int smb_nbst_create(struct smb_vc *vcp, cred_t *cr) { - struct nbpcb *nbp; + TIUSER *tiptr = NULL; + struct nbpcb *nbp = NULL; + dev_t dev; + int rc; + ushort_t fmode; + + switch (vcp->vc_srvaddr.sa.sa_family) { + case AF_INET: + dev = nsmb_dev_tcp; + break; + case AF_INET6: + dev = nsmb_dev_tcp6; + break; + default: + return (EAFNOSUPPORT); + } + + fmode = FREAD|FWRITE; + rc = t_kopen(NULL, dev, fmode, &tiptr, cr); + if (rc != 0) { + cmn_err(CE_NOTE, "t_kopen failed, rc=%d", rc); + return (rc); + } + ASSERT(tiptr != NULL); nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP); nbp->nbp_timo.tv_sec = SMB_NBTIMO; - nbp->nbp_state = NBST_CLOSED; /* really IDLE */ + nbp->nbp_state = NBST_IDLE; nbp->nbp_vc = vcp; - nbp->nbp_sndbuf = smb_tcpsndbuf; - nbp->nbp_rcvbuf = smb_tcprcvbuf; + nbp->nbp_tiptr = tiptr; + nbp->nbp_fmode = fmode; nbp->nbp_cred = cr; crhold(cr); mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL); + vcp->vc_tdata = nbp; return (0); @@ -541,111 +558,74 @@ smb_nbst_done(struct smb_vc *vcp) return (0); } -/* - * Loan a transport file pointer (from user space) to this - * IOD endpoint. There should be no other thread using this - * endpoint when we do this, but lock for consistency. - */ static int -nb_loan_fp(struct nbpcb *nbp, struct file *fp, cred_t *cr) +smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap) { - TIUSER *tiptr; + struct nbpcb *nbp = vcp->vc_tdata; + TIUSER *tiptr = nbp->nbp_tiptr; int err; - err = t_kopen(fp, 0, 0, &tiptr, cr); - if (err != 0) - return (err); + /* Only default bind supported. */ + if (sap != NULL) + return (ENOTSUP); - mutex_enter(&nbp->nbp_lock); - - nbp->nbp_tiptr = tiptr; - nbp->nbp_fmode = tiptr->fp->f_flag; - nbp->nbp_flags |= NBF_CONNECTED; - nbp->nbp_state = NBST_SESSION; - - mutex_exit(&nbp->nbp_lock); + err = t_kbind(tiptr, NULL, NULL); - return (0); + return (err); } -/* - * Take back the transport file pointer we previously loaned. - * It's possible there may be another thread in here, so let - * others get out of the way before we pull the rug out. - * - * Some notes about the locking here: The higher-level IOD code - * serializes activity such that at most one reader and writer - * thread can be active in this code (and possibly both). - * Keeping nbp_lock held during the activities of these two - * threads would lead to the possibility of nbp_lock being - * held by a blocked thread, so this instead sets one of the - * flags (NBF_SENDLOCK | NBF_RECVLOCK) when a sender or a - * receiver is active (respectively). Lastly, tear-down is - * the only tricky bit (here) where we must wait for any of - * these activities to get out of current calls so they will - * notice that we've turned off the NBF_CONNECTED flag. - */ -static void -nb_unloan_fp(struct nbpcb *nbp) +static int +smb_nbst_unbind(struct smb_vc *vcp) { + struct nbpcb *nbp = vcp->vc_tdata; + TIUSER *tiptr = nbp->nbp_tiptr; + int err; - mutex_enter(&nbp->nbp_lock); - - nbp->nbp_flags &= ~NBF_CONNECTED; - while (nbp->nbp_flags & (NBF_SENDLOCK | NBF_RECVLOCK)) { - nbp->nbp_flags |= NBF_LOCKWAIT; - cv_wait(&nbp->nbp_cv, &nbp->nbp_lock); - } - if (nbp->nbp_frag != NULL) { - freemsg(nbp->nbp_frag); - nbp->nbp_frag = NULL; - } - if (nbp->nbp_tiptr != NULL) { - (void) t_kclose(nbp->nbp_tiptr, 0); - nbp->nbp_tiptr = NULL; - } - nbp->nbp_state = NBST_CLOSED; + err = t_kunbind(tiptr); - mutex_exit(&nbp->nbp_lock); + return (err); } static int -smb_nbst_loan_fp(struct smb_vc *vcp, struct file *fp, cred_t *cr) +smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap) { - struct nbpcb *nbp = vcp->vc_tdata; - int error = 0; + struct t_call call; + struct nbpcb *nbp = vcp->vc_tdata; + TIUSER *tiptr = nbp->nbp_tiptr; + int alen, err; + + /* Need the address length */ + switch (sap->sa_family) { + case AF_INET: + alen = sizeof (struct sockaddr_in); + break; + case AF_INET6: + alen = sizeof (struct sockaddr_in6); + break; + default: + return (EAFNOSUPPORT); + } - /* - * Un-loan the existing one, if any. - */ - (void) nb_disconnect(nbp); - nb_unloan_fp(nbp); + /* sockaddr goes in the "addr" netbuf */ + bzero(&call, sizeof (call)); + call.addr.buf = (char *)sap; + call.addr.len = alen; + call.addr.maxlen = alen; - /* - * Loan the new one passed in. - */ - if (fp != NULL) { - error = nb_loan_fp(nbp, fp, cr); - } + err = t_kconnect(tiptr, &call, NULL); + if (err != 0) + return (err); - return (error); -} + mutex_enter(&nbp->nbp_lock); -/*ARGSUSED*/ -static int -smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap) -{ - return (ENOTSUP); -} + nbp->nbp_flags |= NBF_CONNECTED; + nbp->nbp_state = NBST_SESSION; -/*ARGSUSED*/ -static int -smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap) -{ - return (ENOTSUP); + mutex_exit(&nbp->nbp_lock); + + return (0); } -/*ARGSUSED*/ static int smb_nbst_disconnect(struct smb_vc *vcp) { @@ -832,42 +812,78 @@ smb_nbst_poll(struct smb_vc *vcp, int ticks) return (ENOTSUP); } +/*ARGSUSED*/ static int smb_nbst_getparam(struct smb_vc *vcp, int param, void *data) { + return (EINVAL); +} + +static int +smb_nbst_setparam(struct smb_vc *vcp, int param, void *data) +{ + struct t_optmgmt oreq, ores; + struct { + struct T_opthdr oh; + int ival; + } opts; struct nbpcb *nbp = vcp->vc_tdata; + int level, name, err; switch (param) { - case SMBTP_SNDSZ: - *(int *)data = nbp->nbp_sndbuf; + case SMBTP_TCP_NODELAY: + level = IPPROTO_TCP; + name = TCP_NODELAY; break; - case SMBTP_RCVSZ: - *(int *)data = nbp->nbp_rcvbuf; - break; - case SMBTP_TIMEOUT: - *(struct timespec *)data = nbp->nbp_timo; - break; -#ifdef SMBTP_SELECTID - case SMBTP_SELECTID: - *(void **)data = nbp->nbp_selectid; + + case SMBTP_TCP_CON_TMO: /* int mSec */ + level = IPPROTO_TCP; + name = TCP_CONN_ABORT_THRESHOLD; break; -#endif -#ifdef SMBTP_UPCALL - case SMBTP_UPCALL: - *(void **)data = nbp->nbp_upcall; + + case SMBTP_KEEPALIVE: // SO_KEEPALIVE + case SMBTP_SNDBUF: // SO_SNDBUF + case SMBTP_RCVBUF: // SO_RCVBUF + case SMBTP_RCVTIMEO: // SO_RCVTIMEO + level = SOL_SOCKET; + name = param; break; -#endif + default: return (EINVAL); } - return (0); -} -/*ARGSUSED*/ -static int -smb_nbst_setparam(struct smb_vc *vcp, int param, void *data) -{ - return (EINVAL); + /* opt header */ + opts.oh.len = sizeof (opts); + opts.oh.level = level; + opts.oh.name = name; + opts.oh.status = 0; + opts.ival = *(int *)data; + + oreq.flags = T_NEGOTIATE; + oreq.opt.buf = (void *)&opts; + oreq.opt.len = sizeof (opts); + oreq.opt.maxlen = oreq.opt.len; + + ores.flags = 0; + ores.opt.buf = NULL; + ores.opt.len = 0; + ores.opt.maxlen = 0; + + err = t_koptmgmt(nbp->nbp_tiptr, &oreq, &ores); + if (err != 0) { + cmn_err(CE_NOTE, "t_opgmgnt, err = %d", err); + return (EPROTO); + } + + if ((ores.flags & T_SUCCESS) == 0) { + cmn_err(CE_NOTE, "smb_nbst_setparam: " + "flags 0x%x, status 0x%x", + (int)ores.flags, (int)opts.oh.status); + return (EPROTO); + } + + return (0); } /* @@ -893,12 +909,12 @@ struct smb_tran_desc smb_tran_nbtcp_desc = { smb_nbst_create, smb_nbst_done, smb_nbst_bind, + smb_nbst_unbind, smb_nbst_connect, smb_nbst_disconnect, smb_nbst_send, smb_nbst_recv, smb_nbst_poll, - smb_nbst_loan_fp, smb_nbst_getparam, smb_nbst_setparam, smb_nbst_fatal, diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h index f810a538cd..3c5e86639e 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h @@ -32,13 +32,14 @@ * $Id: smb_trantcp.h,v 1.8 2004/08/03 23:50:01 lindak Exp $ */ /* - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _NETSMB_SMB_TRANTCP_H_ #define _NETSMB_SMB_TRANTCP_H_ enum nbstate { NBST_CLOSED, + NBST_IDLE, NBST_RQSENT, NBST_SESSION, NBST_RETARGET, diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c index fb587cd79e..d83c3a086c 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c @@ -33,9 +33,10 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -52,6 +53,7 @@ #include <netsmb/smb_osdep.h> +#include <smb/winioctl.h> #include <netsmb/smb.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_rq.h> @@ -61,28 +63,6 @@ static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg); /* - * Ioctl function for SMBIOC_FLAGS2 - */ -int -smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags) -{ - struct smb_vc *vcp = NULL; - - /* This ioctl requires a session. */ - if ((vcp = sdp->sd_vc) == NULL) - return (ENOTCONN); - - /* - * Return the flags2 value. - */ - if (ddi_copyout(&vcp->vc_hflags2, (void *)arg, - sizeof (u_int16_t), flags)) - return (EFAULT); - - return (0); -} - -/* * Ioctl function for SMBIOC_GETSSNKEY * Size copied out is SMBIOC_HASH_SZ. * @@ -105,7 +85,10 @@ smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags) /* * Return the session key. */ - if (ddi_copyout(vcp->vc_ssn_key, (void *)arg, + if (vcp->vc_ssnkey == NULL || + vcp->vc_ssnkeylen < SMBIOC_HASH_SZ) + return (EINVAL); + if (ddi_copyout(vcp->vc_ssnkey, (void *)arg, SMBIOC_HASH_SZ, flags)) return (EFAULT); @@ -113,224 +96,90 @@ smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags) } /* - * Ioctl function for SMBIOC_REQUEST + * Ioctl function for SMBIOC_XACTNP (transact named pipe) */ int -smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) +smb_usr_xnp(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) { struct smb_cred scred; struct smb_share *ssp; - smbioc_rq_t *ioc = NULL; - struct smb_rq *rqp = NULL; - struct mbchain *mbp; - struct mdchain *mdp; - uint32_t rsz; + struct smb_fh *fhp; + smbioc_xnp_t *ioc = NULL; + struct mbchain send_mb; + struct mdchain recv_md; + uint32_t rdlen; int err, mbseg; - /* This ioctl requires a share. */ - if ((ssp = sdp->sd_share) == NULL) - return (ENOTCONN); - - smb_credinit(&scred, cr); - ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); - if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { - err = EFAULT; - goto out; - } - - /* See ddi_copyin, ddi_copyout */ - mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER; - - /* - * Lots of SMB commands could be safe, but - * these are the only ones used by libsmbfs. - */ - switch (ioc->ioc_cmd) { - /* These are OK */ - case SMB_COM_CLOSE: - case SMB_COM_FLUSH: - case SMB_COM_NT_CREATE_ANDX: - case SMB_COM_OPEN_PRINT_FILE: - case SMB_COM_CLOSE_PRINT_FILE: - break; - - default: - err = EPERM; - goto out; - } - - err = smb_rq_alloc(SSTOCP(ssp), ioc->ioc_cmd, &scred, &rqp); - if (err) - goto out; - - mbp = &rqp->sr_rq; - err = mb_put_mem(mbp, ioc->ioc_tbuf, ioc->ioc_tbufsz, mbseg); - - err = smb_rq_simple(rqp); - if (err == 0) { - /* - * This may have been an open, so save the - * generation ID of the share, which we - * check before trying read or write. - */ - sdp->sd_vcgenid = ssp->ss_vcgenid; - - /* - * Have reply data. to copyout. - * SMB header already parsed. - */ - mdp = &rqp->sr_rp; - rsz = msgdsize(mdp->md_top) - SMB_HDRLEN; - if (ioc->ioc_rbufsz < rsz) { - err = EOVERFLOW; - goto out; - } - ioc->ioc_rbufsz = rsz; - err = md_get_mem(mdp, ioc->ioc_rbuf, rsz, mbseg); - if (err) - goto out; - - } - - ioc->ioc_errclass = rqp->sr_errclass; - ioc->ioc_serror = rqp->sr_serror; - ioc->ioc_error = rqp->sr_error; - (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); - -out: - if (rqp != NULL) - smb_rq_done(rqp); /* free rqp */ - kmem_free(ioc, sizeof (*ioc)); - smb_credrele(&scred); - - return (err); - -} + /* This ioctl requires a file handle. */ + if ((fhp = sdp->sd_fh) == NULL) + return (EINVAL); + ssp = FHTOSS(fhp); -/* - * Ioctl function for SMBIOC_T2RQ - */ -int -smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) -{ - struct smb_cred scred; - struct smb_share *ssp; - smbioc_t2rq_t *ioc = NULL; - struct smb_t2rq *t2p = NULL; - struct mdchain *mdp; - int err, len, mbseg; + /* After reconnect, force close+reopen */ + if (fhp->fh_vcgenid != ssp->ss_vcgenid) + return (ESTALE); - /* This ioctl requires a share. */ - if ((ssp = sdp->sd_share) == NULL) - return (ENOTCONN); + bzero(&send_mb, sizeof (send_mb)); + bzero(&recv_md, sizeof (recv_md)); - smb_credinit(&scred, cr); ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { err = EFAULT; goto out; } - /* See ddi_copyin, ddi_copyout */ + /* + * Copyin the send data, into an mbchain, + * save output buffer size. + */ mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER; - - if (ioc->ioc_setupcnt > SMBIOC_T2RQ_MAXSETUP) { - err = EINVAL; + err = smb_cpdatain(&send_mb, ioc->ioc_tdlen, ioc->ioc_tdata, mbseg); + if (err) goto out; - } + rdlen = ioc->ioc_rdlen; /* - * Fill in the FID for libsmbfs transact named pipe. + * Run the SMB2 ioctl or SMB1 trans2 */ - if (ioc->ioc_setupcnt > 1 && ioc->ioc_setup[1] == 0xFFFF) { - if (sdp->sd_vcgenid != ssp->ss_vcgenid) { - err = ESTALE; - goto out; - } - ioc->ioc_setup[1] = (uint16_t)sdp->sd_smbfid; - } - - t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP); - err = smb_t2_init(t2p, SSTOCP(ssp), - ioc->ioc_setup, ioc->ioc_setupcnt, &scred); - if (err) - goto out; - t2p->t2_setupcount = ioc->ioc_setupcnt; - t2p->t2_setupdata = ioc->ioc_setup; - - /* This ioc member is a fixed-size array. */ - if (ioc->ioc_name[0]) { - /* Get the name length - carefully! */ - ioc->ioc_name[SMBIOC_T2RQ_MAXNAME-1] = '\0'; - t2p->t_name_len = strlen(ioc->ioc_name); - t2p->t_name = ioc->ioc_name; + smb_credinit(&scred, cr); + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + err = smb2_smb_ioctl(ssp, &fhp->fh_fid2, + &send_mb, &recv_md, &rdlen, + FSCTL_PIPE_TRANSCEIVE, &scred); + } else { + err = smb_t2_xnp(ssp, fhp->fh_fid1, + &send_mb, &recv_md, &rdlen, + &ioc->ioc_more, &scred); } - t2p->t2_maxscount = 0; - t2p->t2_maxpcount = ioc->ioc_rparamcnt; - t2p->t2_maxdcount = ioc->ioc_rdatacnt; - - /* Transmit parameters */ - err = smb_cpdatain(&t2p->t2_tparam, - ioc->ioc_tparamcnt, ioc->ioc_tparam, mbseg); - if (err) - goto out; - - /* Transmit data */ - err = smb_cpdatain(&t2p->t2_tdata, - ioc->ioc_tdatacnt, ioc->ioc_tdata, mbseg); - if (err) - goto out; - - err = smb_t2_request(t2p); - - /* Copyout returned parameters. */ - mdp = &t2p->t2_rparam; - if (err == 0 && mdp->md_top != NULL) { - /* User's buffer large enough? */ - len = m_fixhdr(mdp->md_top); - if (len > ioc->ioc_rparamcnt) { - err = EMSGSIZE; - goto out; - } - ioc->ioc_rparamcnt = (ushort_t)len; - err = md_get_mem(mdp, ioc->ioc_rparam, len, mbseg); - if (err) - goto out; - } else - ioc->ioc_rparamcnt = 0; + smb_credrele(&scred); /* Copyout returned data. */ - mdp = &t2p->t2_rdata; - if (err == 0 && mdp->md_top != NULL) { - /* User's buffer large enough? */ - len = m_fixhdr(mdp->md_top); - if (len > ioc->ioc_rdatacnt) { + if (err == 0 && recv_md.md_top != NULL) { + /* User's buffer large enough for copyout? */ + size_t len = m_fixhdr(recv_md.md_top); + if (len > ioc->ioc_rdlen) { err = EMSGSIZE; goto out; } - ioc->ioc_rdatacnt = (ushort_t)len; - err = md_get_mem(mdp, ioc->ioc_rdata, len, mbseg); + err = md_get_mem(&recv_md, ioc->ioc_rdata, len, mbseg); if (err) goto out; } else - ioc->ioc_rdatacnt = 0; + ioc->ioc_rdlen = 0; - ioc->ioc_errclass = t2p->t2_sr_errclass; - ioc->ioc_serror = t2p->t2_sr_serror; - ioc->ioc_error = t2p->t2_sr_error; - ioc->ioc_rpflags2 = t2p->t2_sr_rpflags2; + /* Tell caller received length */ + if (rdlen <= ioc->ioc_rdlen) { + /* Normal case */ + ioc->ioc_rdlen = rdlen; + } else { + /* Buffer overlow. Leave ioc_rdlen */ + ioc->ioc_more = 1; + } (void) ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); - out: - if (t2p != NULL) { - /* Note: t2p->t_name no longer allocated */ - smb_t2_done(t2p); - kmem_free(t2p, sizeof (*t2p)); - } kmem_free(ioc, sizeof (*ioc)); - smb_credrele(&scred); return (err); } @@ -358,22 +207,22 @@ smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) { struct smb_cred scred; struct smb_share *ssp; + struct smb_fh *fhp; smbioc_rw_t *ioc = NULL; struct iovec aiov[1]; struct uio auio; - uint16_t fh; int err; uio_rw_t rw; - /* This ioctl requires a share. */ - if ((ssp = sdp->sd_share) == NULL) - return (ENOTCONN); + /* This ioctl requires a file handle. */ + if ((fhp = sdp->sd_fh) == NULL) + return (EINVAL); + ssp = FHTOSS(fhp); /* After reconnect, force close+reopen */ - if (sdp->sd_vcgenid != ssp->ss_vcgenid) + if (fhp->fh_vcgenid != ssp->ss_vcgenid) return (ESTALE); - smb_credinit(&scred, cr); ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { err = EFAULT; @@ -392,15 +241,6 @@ smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) goto out; } - /* - * If caller passes -1 in ioc_fh, then - * use the FID from SMBIOC_NTCREATE. - */ - if (ioc->ioc_fh == -1) - fh = (uint16_t)sdp->sd_smbfid; - else - fh = (uint16_t)ioc->ioc_fh; - aiov[0].iov_base = ioc->ioc_base; aiov[0].iov_len = (size_t)ioc->ioc_cnt; @@ -412,7 +252,9 @@ smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) auio.uio_fmode = 0; auio.uio_resid = (size_t)ioc->ioc_cnt; - err = smb_rwuio(ssp, fh, rw, &auio, &scred, 0); + smb_credinit(&scred, cr); + err = smb_rwuio(fhp, rw, &auio, &scred, 0); + smb_credrele(&scred); /* * On return ioc_cnt holds the @@ -424,7 +266,6 @@ smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) out: kmem_free(ioc, sizeof (*ioc)); - smb_credrele(&scred); return (err); } @@ -439,20 +280,20 @@ smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) struct smb_cred scred; struct mbchain name_mb; struct smb_share *ssp; + struct smb_fh *fhp = NULL; smbioc_ntcreate_t *ioc = NULL; - uint16_t fid; int err, nmlen; + mb_init(&name_mb); + /* This ioctl requires a share. */ if ((ssp = sdp->sd_share) == NULL) return (ENOTCONN); - /* Must not be already open. */ - if (sdp->sd_smbfid != -1) + /* Must not already have a file handle. */ + if (sdp->sd_fh != NULL) return (EINVAL); - mb_init(&name_mb); - smb_credinit(&scred, cr); ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { err = EFAULT; @@ -468,7 +309,14 @@ smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) if (err != 0) goto out; - /* Do the OtW open, save the FID. */ + err = smb_fh_create(ssp, &fhp); + if (err != 0) + goto out; + + /* + * Do the OtW open, save the FID. + */ + smb_credinit(&scred, cr); err = smb_smb_ntcreate(ssp, &name_mb, 0, /* create flags */ ioc->ioc_req_acc, @@ -478,18 +326,22 @@ smb_usr_ntcreate(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) ioc->ioc_creat_opts, NTCREATEX_IMPERSONATION_IMPERSONATION, &scred, - &fid, + fhp, NULL, NULL); + smb_credrele(&scred); if (err != 0) goto out; - sdp->sd_smbfid = fid; - sdp->sd_vcgenid = ssp->ss_vcgenid; + fhp->fh_rights = ioc->ioc_req_acc; + smb_fh_opened(fhp); + sdp->sd_fh = fhp; + fhp = NULL; out: + if (fhp != NULL) + smb_fh_rele(fhp); kmem_free(ioc, sizeof (*ioc)); - smb_credrele(&scred); mb_done(&name_mb); return (err); @@ -502,11 +354,17 @@ out: int smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) { + static const char invalid_chars[] = SMB_FILENAME_INVALID_CHARS; struct smb_cred scred; + struct mbchain name_mb; struct smb_share *ssp; + struct smb_fh *fhp = NULL; smbioc_printjob_t *ioc = NULL; - uint16_t fid; - int err; + int err, cklen, nmlen; + uint32_t access = SA_RIGHT_FILE_WRITE_DATA | + SA_RIGHT_FILE_READ_ATTRIBUTES; + + mb_init(&name_mb); /* This ioctl requires a share. */ if ((ssp = sdp->sd_share) == NULL) @@ -516,8 +374,8 @@ smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) if (ssp->ss_type != STYPE_PRINTQ) return (EINVAL); - /* Must not be already open. */ - if (sdp->sd_smbfid != -1) + /* Must not already have a file handle. */ + if (sdp->sd_fh != NULL) return (EINVAL); smb_credinit(&scred, cr); @@ -526,21 +384,68 @@ smb_usr_printjob(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) err = EFAULT; goto out; } + + /* + * Use the print job title as the file name to open, but + * check for invalid characters first. See the notes in + * libsmbfs/smb/print.c about job name sanitizing. + */ ioc->ioc_title[SMBIOC_MAX_NAME-1] = '\0'; + nmlen = strnlen(ioc->ioc_title, SMBIOC_MAX_NAME-1); + cklen = strcspn(ioc->ioc_title, invalid_chars); + if (cklen < nmlen) { + err = EINVAL; + goto out; + } - /* Do the OtW open, save the FID. */ - err = smb_smb_open_prjob(ssp, ioc->ioc_title, - ioc->ioc_setuplen, ioc->ioc_prmode, - &scred, &fid); + /* Build name_mb */ + err = smb_put_dmem(&name_mb, SSTOVC(ssp), + ioc->ioc_title, nmlen, + SMB_CS_NONE, NULL); if (err != 0) goto out; - sdp->sd_smbfid = fid; - sdp->sd_vcgenid = ssp->ss_vcgenid; + err = smb_fh_create(ssp, &fhp); + if (err != 0) + goto out; + + /* + * Do the OtW open, save the FID. + */ + smb_credinit(&scred, cr); + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + err = smb2_smb_ntcreate(ssp, &name_mb, + NULL, NULL, /* cctx in, out */ + 0, /* create flags */ + access, + SMB_EFA_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE, + NTCREATEX_IMPERSONATION_IMPERSONATION, + &scred, + &fhp->fh_fid2, + NULL, + NULL); + } else { + err = smb_smb_open_prjob(ssp, ioc->ioc_title, + ioc->ioc_setuplen, ioc->ioc_prmode, + &scred, &fhp->fh_fid1); + } + smb_credrele(&scred); + if (err != 0) + goto out; + + fhp->fh_rights = access; + smb_fh_opened(fhp); + sdp->sd_fh = fhp; + fhp = NULL; out: + if (fhp != NULL) + smb_fh_rele(fhp); kmem_free(ioc, sizeof (*ioc)); - smb_credrele(&scred); + mb_done(&name_mb); return (err); } @@ -549,31 +454,21 @@ out: * Helper for nsmb_ioctl case * SMBIOC_CLOSEFH */ +/*ARGSUSED*/ int smb_usr_closefh(smb_dev_t *sdp, cred_t *cr) { - struct smb_cred scred; - struct smb_share *ssp; - uint16_t fid; - int err; + struct smb_fh *fhp; - /* This ioctl requires a share. */ - if ((ssp = sdp->sd_share) == NULL) - return (ENOTCONN); + /* This ioctl requires a file handle. */ + if ((fhp = sdp->sd_fh) == NULL) + return (EINVAL); + sdp->sd_fh = NULL; - if (sdp->sd_smbfid == -1) - return (0); - fid = (uint16_t)sdp->sd_smbfid; - sdp->sd_smbfid = -1; + smb_fh_close(fhp); + smb_fh_rele(fhp); - smb_credinit(&scred, cr); - if (ssp->ss_type == STYPE_PRINTQ) - err = smb_smb_close_prjob(ssp, fid, &scred); - else - err = smb_smb_close(ssp, fid, NULL, &scred); - smb_credrele(&scred); - - return (err); + return (0); } /* @@ -825,24 +720,22 @@ smb_usr_drop_tree(smb_dev_t *sdp, int cmd) return (0); } - /* - * Ioctl function: SMBIOC_IOD_WORK - * - * Become the reader (IOD) thread, until either the connection is - * reset by the server, or until the connection is idle longer than - * some max time. (max idle time not yet implemented) + * Ioctl handler for all SMBIOC_IOD_... */ int -smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) +smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) { - struct smb_vc *vcp = NULL; + struct smb_vc *vcp; int err = 0; - /* Must have a valid session. */ + /* Must be the IOD. */ + if ((sdp->sd_flags & NSMBFL_IOD) == 0) + return (EINVAL); + /* Must have a VC and no share. */ if ((vcp = sdp->sd_vc) == NULL) return (EINVAL); - if (vcp->vc_flags & SMBV_GONE) + if (sdp->sd_share != NULL) return (EINVAL); /* @@ -859,39 +752,53 @@ smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) return (err); /* - * Copy the "work" state, etc. into the VC - * The MAC key is copied separately. + * Copy the "work" state, etc. into the VC, + * and back to the caller on the way out. + * Clear the "out only" part. */ if (ddi_copyin((void *)arg, &vcp->vc_work, sizeof (smbioc_ssn_work_t), flags)) { err = EFAULT; goto out; } - if (vcp->vc_u_maclen) { - vcp->vc_mackeylen = vcp->vc_u_maclen; - vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP); - if (ddi_copyin(vcp->vc_u_mackey.lp_ptr, vcp->vc_mackey, - vcp->vc_mackeylen, flags)) { - err = EFAULT; - goto out; - } - } + vcp->vc_work.wk_out_state = 0; - err = smb_iod_vc_work(vcp, cr); + switch (cmd) { - /* Caller wants state here. */ - vcp->vc_work.wk_out_state = vcp->vc_state; + case SMBIOC_IOD_CONNECT: + err = nsmb_iod_connect(vcp, cr); + break; - (void) ddi_copyout(&vcp->vc_work, (void *)arg, - sizeof (smbioc_ssn_work_t), flags); + case SMBIOC_IOD_NEGOTIATE: + err = nsmb_iod_negotiate(vcp, cr); + break; -out: - if (vcp->vc_mackey) { - kmem_free(vcp->vc_mackey, vcp->vc_mackeylen); - vcp->vc_mackey = NULL; - vcp->vc_mackeylen = 0; + case SMBIOC_IOD_SSNSETUP: + err = nsmb_iod_ssnsetup(vcp, cr); + break; + + case SMBIOC_IOD_WORK: + err = smb_iod_vc_work(vcp, flags, cr); + break; + + case SMBIOC_IOD_IDLE: + err = smb_iod_vc_idle(vcp); + break; + + case SMBIOC_IOD_RCFAIL: + err = smb_iod_vc_rcfail(vcp); + break; + + default: + err = ENOTTY; + break; } +out: + vcp->vc_work.wk_out_state = vcp->vc_state; + (void) ddi_copyout(&vcp->vc_work, (void *)arg, + sizeof (smbioc_ssn_work_t), flags); + /* * The IOD thread is leaving the driver. Clear iod_thr, * and wake up anybody waiting for us to quit. @@ -904,67 +811,104 @@ out: return (err); } -/* - * Ioctl functions: SMBIOC_IOD_IDLE, SMBIOC_IOD_RCFAIL - * - * Wait for user-level requests to be enqueued on this session, - * and then return to the user-space helper, which will then - * initiate a reconnect, etc. - */ int -smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags) +smb_usr_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) { - struct smb_vc *vcp = NULL; - int err = 0; - - /* Must have a valid session. */ - if ((vcp = sdp->sd_vc) == NULL) - return (EINVAL); - if (vcp->vc_flags & SMBV_GONE) - return (EINVAL); + int err; /* - * Is there already an IOD for this VC? - * (Should never happen.) + * Serialize ioctl calls. The smb_usr_... functions + * don't expect concurrent calls on a given sdp. */ - SMB_VC_LOCK(vcp); - if (vcp->iod_thr == NULL) - vcp->iod_thr = curthread; - else - err = EEXIST; - SMB_VC_UNLOCK(vcp); - if (err) - return (err); - - /* nothing to copyin */ + mutex_enter(&sdp->sd_lock); + if ((sdp->sd_flags & NSMBFL_IOCTL) != 0) { + mutex_exit(&sdp->sd_lock); + return (EBUSY); + } + sdp->sd_flags |= NSMBFL_IOCTL; + mutex_exit(&sdp->sd_lock); + err = 0; switch (cmd) { - case SMBIOC_IOD_IDLE: - err = smb_iod_vc_idle(vcp); + case SMBIOC_GETVERS: + (void) ddi_copyout(&nsmb_version, (void *)arg, + sizeof (nsmb_version), flags); + break; + + case SMBIOC_GETSSNKEY: + err = smb_usr_get_ssnkey(sdp, arg, flags); + break; + + case SMBIOC_DUP_DEV: + err = smb_usr_dup_dev(sdp, arg, flags); + break; + + case SMBIOC_XACTNP: + err = smb_usr_xnp(sdp, arg, flags, cr); break; + case SMBIOC_READ: + case SMBIOC_WRITE: + err = smb_usr_rw(sdp, cmd, arg, flags, cr); + break; + + case SMBIOC_NTCREATE: + err = smb_usr_ntcreate(sdp, arg, flags, cr); + break; + + case SMBIOC_PRINTJOB: + err = smb_usr_printjob(sdp, arg, flags, cr); + break; + + case SMBIOC_CLOSEFH: + err = smb_usr_closefh(sdp, cr); + break; + + case SMBIOC_SSN_CREATE: + case SMBIOC_SSN_FIND: + err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr); + break; + + case SMBIOC_SSN_KILL: + case SMBIOC_SSN_RELE: + err = smb_usr_drop_ssn(sdp, cmd); + break; + + case SMBIOC_TREE_CONNECT: + case SMBIOC_TREE_FIND: + err = smb_usr_get_tree(sdp, cmd, arg, flags, cr); + break; + + case SMBIOC_TREE_KILL: + case SMBIOC_TREE_RELE: + err = smb_usr_drop_tree(sdp, cmd); + break; + + case SMBIOC_IOD_CONNECT: + case SMBIOC_IOD_NEGOTIATE: + case SMBIOC_IOD_SSNSETUP: + case SMBIOC_IOD_WORK: + case SMBIOC_IOD_IDLE: case SMBIOC_IOD_RCFAIL: - err = smb_iod_vc_rcfail(vcp); + err = smb_usr_iod_ioctl(sdp, cmd, arg, flags, cr); + break; + + case SMBIOC_PK_ADD: + case SMBIOC_PK_DEL: + case SMBIOC_PK_CHK: + case SMBIOC_PK_DEL_OWNER: + case SMBIOC_PK_DEL_EVERYONE: + err = smb_pkey_ioctl(cmd, arg, flags, cr); break; default: err = ENOTTY; - goto out; + break; } - /* Both of these ioctls copy out the new state. */ - (void) ddi_copyout(&vcp->vc_state, (void *)arg, - sizeof (int), flags); - -out: - /* - * The IOD thread is leaving the driver. Clear iod_thr, - * and wake up anybody waiting for us to quit. - */ - SMB_VC_LOCK(vcp); - vcp->iod_thr = NULL; - cv_broadcast(&vcp->vc_statechg); - SMB_VC_UNLOCK(vcp); + mutex_enter(&sdp->sd_lock); + sdp->sd_flags &= ~NSMBFL_IOCTL; + mutex_exit(&sdp->sd_lock); return (err); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c index 47b2783c58..b7f6bd8570 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c @@ -33,9 +33,10 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -63,9 +64,9 @@ * m_data ... (m_data + m_len) * In Unix STREAMS, the mblk payload is: * b_rptr ... b_wptr - * + * * Here are some handy conversion notes: - * + * * struct mbuf struct mblk * m->m_next m->b_cont * m->m_nextpkt m->b_next @@ -75,7 +76,7 @@ * &m->m_dat[MLEN] m->b_datap->db_lim * M_TRAILINGSPACE(m) MBLKTAIL(m) * m_freem(m) freemsg(m) - * + * * Note that mbufs chains also have a special "packet" header, * which has the length of the whole message. In STREAMS one * typically just calls msgdsize(m) to get that. @@ -113,6 +114,9 @@ */ #define MLEN 4096 +#if (MLEN < SMB2_HDRLEN) +#error "MLEN can't fit a contiguous SMB2 header" +#endif /* * Some UIO routines. @@ -420,6 +424,22 @@ mb_put_padbyte(struct mbchain *mbp) return (0); } +/* + * Adds padding to 8 byte boundary + */ +int +mb_put_align8(struct mbchain *mbp) +{ + static const char zeros[8] = { 0 }; + int pad_len = 0; + + if ((mbp->mb_count % 8) != 0) { + pad_len = 8 - (mbp->mb_count % 8); + MB_PUT_INLINE(mbp, zeros, pad_len); + } + return (0); +} + int mb_put_uint8(struct mbchain *mbp, u_int8_t x) { @@ -537,6 +557,7 @@ mb_put_mem(struct mbchain *mbp, const void *vsrc, int size, int type) /* * Append an mblk to the chain. + * Note: The mblk_t *m is consumed. */ int mb_put_mbuf(struct mbchain *mbp, mblk_t *m) @@ -576,6 +597,29 @@ mb_put_mbuf(struct mbchain *mbp, mblk_t *m) } /* + * Put an mbchain into another mbchain + * Leave sub_mbp untouched. + */ +int +mb_put_mbchain(struct mbchain *mbp, struct mbchain *sub_mbp) +{ + mblk_t *m; + + if (sub_mbp == NULL) + return (0); + + m = sub_mbp->mb_top; + if (m == NULL) + return (0); + + m = dupmsg(m); + if (m == NULL) + return (ENOSR); + + return (mb_put_mbuf(mbp, m)); +} + +/* * copies a uio scatter/gather list to an mbuf chain. */ int @@ -875,6 +919,7 @@ md_get_mem(struct mdchain *mdp, void *vdst, int size, int type) /* * Get the next SIZE bytes as a separate mblk. + * Advances position in mdp by SIZE. */ int md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret) @@ -898,6 +943,7 @@ md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret) rm = m_copym(m, off, size, M_WAITOK); if (rm == NULL) return (EBADRPC); + (void) md_get_mem(mdp, NULL, size, MB_MSYSTEM); *ret = rm; return (0); diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c index c5af3391c6..1e16e95d18 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c @@ -22,6 +22,8 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -69,12 +71,12 @@ static int smbfs_getsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr) { struct smb_cred scred; - int error, cerror; smbmntinfo_t *smi; smbnode_t *np; - u_int16_t fid = SMB_FID_UNUSED; + smb_fh_t *fid = NULL; uint32_t sdlen = SMALL_SD_SIZE; uint32_t rights = STD_RIGHT_READ_CONTROL_ACCESS; + int error; if (selector & SACL_SECURITY_INFORMATION) rights |= SEC_RIGHT_SYSTEM_SECURITY; @@ -82,9 +84,6 @@ smbfs_getsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr) np = VTOSMB(vp); smi = VTOSMI(vp); - /* Shared lock for (possible) n_fid use. */ - if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) - return (EINTR); smb_credinit(&scred, cr); error = smbfs_smb_tmpopen(np, rights, &scred, &fid); @@ -95,8 +94,8 @@ again: /* * This does the OTW Get */ - error = smbfs_smb_getsec_m(smi->smi_share, fid, - &scred, selector, mp, &sdlen); + error = smbfs_smb_getsec(smi->smi_share, fid, + selector, mp, &sdlen, &scred); /* * Server may give us an error indicating that we * need a larger data buffer to receive the SD, @@ -115,14 +114,10 @@ again: sdlen <= MAX_RAW_SD_SIZE) goto again; - cerror = smbfs_smb_tmpclose(np, fid, &scred); - if (cerror) - SMBVDEBUG("error %d closing file %s\n", - cerror, np->n_rpath); + smbfs_smb_tmpclose(np, fid); out: smb_credrele(&scred); - smbfs_rw_exit(&np->r_lkserlock); return (error); } @@ -139,11 +134,11 @@ static int smbfs_setsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr) { struct smb_cred scred; - int error, cerror; smbmntinfo_t *smi; smbnode_t *np; uint32_t rights; - u_int16_t fid = SMB_FID_UNUSED; + smb_fh_t *fid = NULL; + int error; np = VTOSMB(vp); smi = VTOSMI(vp); @@ -164,9 +159,6 @@ smbfs_setsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr) if (selector & SACL_SECURITY_INFORMATION) rights |= SEC_RIGHT_SYSTEM_SECURITY; - /* Shared lock for (possible) n_fid use. */ - if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) - return (EINTR); smb_credinit(&scred, cr); error = smbfs_smb_tmpopen(np, rights, &scred, &fid); @@ -185,17 +177,13 @@ smbfs_setsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr) /* * This does the OTW Set */ - error = smbfs_smb_setsec_m(smi->smi_share, fid, - &scred, selector, mp); + error = smbfs_smb_setsec(smi->smi_share, fid, + selector, mp, &scred); - cerror = smbfs_smb_tmpclose(np, fid, &scred); - if (cerror) - SMBVDEBUG("error %d closing file %s\n", - cerror, np->n_rpath); + smbfs_smb_tmpclose(np, fid); out: smb_credrele(&scred); - smbfs_rw_exit(&np->r_lkserlock); return (error); } diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c index 44319e6682..41a6f41a2b 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c @@ -24,6 +24,8 @@ * * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. * All rights reserved. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -60,6 +62,7 @@ #include <smbfs/smbfs_node.h> #include <smbfs/smbfs_subr.h> +#ifdef _KERNEL #include <vm/hat.h> #include <vm/as.h> #include <vm/page.h> @@ -67,6 +70,7 @@ #include <vm/seg.h> #include <vm/seg_map.h> #include <vm/seg_vn.h> +#endif // _KERNEL #define ATTRCACHE_VALID(vp) (gethrtime() < VTOSMB(vp)->r_attrtime) @@ -394,11 +398,23 @@ smbfs_getattr_cache(vnode_t *vp, struct smbfattr *fap) static int smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr) { - struct smbnode *np; struct smb_cred scred; + smbnode_t *np = VTOSMB(vp); + smb_share_t *ssp = np->n_mount->smi_share; + smb_fh_t *fhp = NULL; int error; - np = VTOSMB(vp); + bzero(fap, sizeof (*fap)); + + /* + * Special case the XATTR directory here (all fake). + * OK to leave a,c,m times zero (expected). + */ + if (vp->v_flag & V_XATTRDIR) { + fap->fa_attr = SMB_FA_DIR; + fap->fa_size = DEV_BSIZE; + return (0); + } /* * Here NFS uses the ACL RPC (if smi_flags & SMI_ACL) @@ -412,8 +428,28 @@ smbfs_getattr_otw(vnode_t *vp, struct smbfattr *fap, cred_t *cr) return (EINTR); smb_credinit(&scred, cr); - bzero(fap, sizeof (*fap)); - error = smbfs_smb_getfattr(np, fap, &scred); +// Does the attr. open code path work for streams? +// Trying that, and if it doesn't work enable this. +#if 0 // XXX + /* + * Extended attribute files + */ + if (np->n_flag & N_XATTR) { + error = smbfs_xa_getfattr(np, fap, scrp); + goto out; + } +#endif // XXX + + if (np->n_fidrefs > 0 && + (fhp = np->n_fid) != NULL && + (fhp->fh_vcgenid == ssp->ss_vcgenid)) { + /* Use the FID we have. */ + error = smbfs_smb_getfattr(np, fhp, fap, &scred); + + } else { + /* This will do an attr open */ + error = smbfs_smb_getpattr(np, fap, &scred); + } smb_credrele(&scred); smbfs_rw_exit(&np->r_lkserlock); @@ -816,9 +852,8 @@ static void smbfs_cb_nop(smb_share_t *ss) smb_fscb_t smbfs_cb = { .fscb_disconn = smbfs_dead, - .fscb_connect = smbfs_cb_nop, - .fscb_down = smbfs_cb_nop, - .fscb_up = smbfs_cb_nop }; + .fscb_connect = smbfs_cb_nop +}; #endif /* NEED_SMBFS_CALLBACKS */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c index 5bbbae860e..163a5a4504 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c @@ -35,11 +35,14 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/cred.h> +#include <sys/errno.h> #include <sys/time.h> #include <sys/vfs.h> #include <sys/vnode.h> diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h index 2fe7476814..62d47e9b20 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h @@ -33,9 +33,10 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _FS_SMBFS_NODE_H_ @@ -221,10 +222,8 @@ typedef struct smbnode { struct smbfs_fctx *n_dirseq; /* ff context */ int n_dirofs; /* last ff offset */ int n_fidrefs; - uint16_t n_fid; /* file handle */ + smb_fh_t *n_fid; /* file handle */ enum vtype n_ovtype; /* vnode type opened */ - uint32_t n_rights; /* granted rights */ - int n_vcgenid; /* gereration no. (reconnect) */ /* * Misc. bookkeeping diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c index db920f21b8..9a44867521 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c @@ -28,18 +28,18 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $Id: smbfs_smb.c,v 1.73.38.1 2005/05/27 02:35:28 lindak Exp $ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> #include <sys/systm.h> +#include <sys/inttypes.h> #include <sys/time.h> #include <sys/vnode.h> #include <sys/sunddi.h> @@ -48,6 +48,7 @@ #include <netsmb/smb_osdep.h> #include <netsmb/smb.h> +#include <netsmb/smb2.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_subr.h> #include <netsmb/smb_rq.h> @@ -62,375 +63,70 @@ */ const uint64_t NT1980 = 11960035200ULL*10000000ULL; -/* - * Local functions. - * Not static, to aid debugging. - */ - -int smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen, - struct smbfattr *fap, struct smb_cred *scrp); -int smbfs_smb_trans2_query(struct smbnode *np, struct smbfattr *fap, - struct smb_cred *scrp, uint16_t infolevel); - -int smbfs_smb_statfsLM1(struct smb_share *ssp, - statvfs64_t *sbp, struct smb_cred *scrp); -int smbfs_smb_statfsLM2(struct smb_share *ssp, - statvfs64_t *sbp, struct smb_cred *scrp); - -int smbfs_smb_setfattrNT(struct smbnode *np, int fid, - uint32_t attr, struct timespec *mtime, struct timespec *atime, - struct smb_cred *scrp); - -int smbfs_smb_setftime1(struct smbnode *np, uint16_t fid, - struct timespec *mtime, struct timespec *atime, - struct smb_cred *scrp); - -int smbfs_smb_setpattr1(struct smbnode *np, - const char *name, int len, uint32_t attr, - struct timespec *mtime, struct smb_cred *scrp); - - -/* - * Todo: locking over-the-wire - */ -#ifdef APPLE - -static int -smbfs_smb_lockandx(struct smbnode *np, int op, uint32_t pid, - offset_t start, uint64_t len, int largelock, - struct smb_cred *scrp, uint32_t timeout) -{ - struct smb_share *ssp = np->n_mount->smi_share; - struct smb_rq rq, *rqp = &rq; - struct mbchain *mbp; - uint8_t ltype = 0; - int error; - - /* Shared lock for n_fid use below. */ - ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); - - /* After reconnect, n_fid is invalid */ - if (np->n_vcgenid != ssp->ss_vcgenid) - return (ESTALE); - - if (op == SMB_LOCK_SHARED) - ltype |= SMB_LOCKING_ANDX_SHARED_LOCK; - /* XXX: if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES)? */ - if (largelock) - ltype |= SMB_LOCKING_ANDX_LARGE_FILES; - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint8(mbp, 0xff); /* secondary command */ - mb_put_uint8(mbp, 0); /* MBZ */ - mb_put_uint16le(mbp, 0); - mb_put_uint16le(mbp, np->n_fid); - mb_put_uint8(mbp, ltype); /* locktype */ - mb_put_uint8(mbp, 0); /* oplocklevel - 0 seems is NO_OPLOCK */ - mb_put_uint32le(mbp, timeout); /* 0 nowait, -1 infinite wait */ - mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0); - mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint16le(mbp, pid); - if (!largelock) { - mb_put_uint32le(mbp, start); - mb_put_uint32le(mbp, len); - } else { - mb_put_uint16le(mbp, 0); /* pad */ - mb_put_uint32le(mbp, start >> 32); /* OffsetHigh */ - mb_put_uint32le(mbp, start & 0xffffffff); /* OffsetLow */ - mb_put_uint32le(mbp, len >> 32); /* LengthHigh */ - mb_put_uint32le(mbp, len & 0xffffffff); /* LengthLow */ - } - smb_rq_bend(rqp); - /* - * Don't want to risk missing a successful - * unlock send or lock response, or we could - * lose track of an outstanding lock. - */ - if (op == SMB_LOCK_RELEASE) - rqp->sr_flags |= SMBR_NOINTR_SEND; - else - rqp->sr_flags |= SMBR_NOINTR_RECV; - - error = smb_rq_simple(rqp); - smb_rq_done(rqp); - return (error); -} - -int -smbfs_smb_lock(struct smbnode *np, int op, caddr_t id, - offset_t start, uint64_t len, int largelock, - struct smb_cred *scrp, uint32_t timeout) -{ - struct smb_share *ssp = np->n_mount->smi_share; - - if (SMB_DIALECT(SSTOVC(ssp)) < SMB_DIALECT_LANMAN1_0) - /* - * TODO: use LOCK_BYTE_RANGE here. - */ - return (EINVAL); - - /* - * XXX: compute largelock via: - * (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES)? - */ - return (smbfs_smb_lockandx(np, op, (uint32_t)id, start, len, - largelock, scrp, timeout)); -} - -#endif /* APPLE */ /* - * Helper for smbfs_getattr - * Something like nfs_getattr_otw + * Helper for smbfs_getattr_otw + * used when we have an open FID */ int smbfs_smb_getfattr( struct smbnode *np, + smb_fh_t *fhp, struct smbfattr *fap, struct smb_cred *scrp) { + struct smb_share *ssp = np->n_mount->smi_share; int error; - /* - * This lock is necessary for FID-based calls. - * Lock may be writer (via open) or reader. - */ - ASSERT(np->r_lkserlock.count != 0); - - /* - * Extended attribute directory or file. - */ - if (np->n_flag & N_XATTR) { - error = smbfs_xa_getfattr(np, fap, scrp); - return (error); + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_qfileinfo(ssp, &fhp->fh_fid2, fap, scrp); + } else { + error = smbfs_smb1_trans2_query(np, fhp->fh_fid1, fap, scrp); } - error = smbfs_smb_trans2_query(np, fap, scrp, 0); - if (error != EINVAL) - return (error); - - /* fallback */ - error = smbfs_smb_query_info(np, NULL, 0, fap, scrp); - return (error); } /* - * Common function for QueryFileInfo, QueryPathInfo. + * Helper for smbfs_getattr_otw + * used when we don't have an open FID + * + * For SMB1 we can just use the path form of trans2 query. + * For SMB2 we need to do an attribute-only open. + * See smbfs_smb2_getpattr() */ int -smbfs_smb_trans2_query(struct smbnode *np, struct smbfattr *fap, - struct smb_cred *scrp, uint16_t infolevel) +smbfs_smb_getpattr( + struct smbnode *np, + struct smbfattr *fap, + struct smb_cred *scrp) { struct smb_share *ssp = np->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); - struct smb_t2rq *t2p; - int error, svtz, timesok = 1; - struct mbchain *mbp; - struct mdchain *mdp; - uint16_t cmd, date, time, wattr; - uint64_t llongint, lsize; - uint32_t size, dattr; - - /* - * Shared lock for n_fid use below. - * See smbfs_smb_getfattr() - */ - ASSERT(np->r_lkserlock.count != 0); - - /* - * If we have a valid open FID, use it. - */ - if ((np->n_fidrefs > 0) && - (np->n_fid != SMB_FID_UNUSED) && - (np->n_vcgenid == ssp->ss_vcgenid)) - cmd = SMB_TRANS2_QUERY_FILE_INFORMATION; - else - cmd = SMB_TRANS2_QUERY_PATH_INFORMATION; - -top: - error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p); - if (error) - return (error); - mbp = &t2p->t2_tparam; - mb_init(mbp); - if (!infolevel) { - if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) - infolevel = SMB_QFILEINFO_STANDARD; - else - infolevel = SMB_QFILEINFO_ALL_INFO; - } - - if (cmd == SMB_TRANS2_QUERY_FILE_INFORMATION) - mb_put_uint16le(mbp, np->n_fid); - - mb_put_uint16le(mbp, infolevel); - - if (cmd == SMB_TRANS2_QUERY_PATH_INFORMATION) { - mb_put_uint32le(mbp, 0); - /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */ - error = smbfs_fullpath(mbp, vcp, np, NULL, 0, '\\'); - if (error) { - smb_t2_done(t2p); - return (error); - } - } + int error; - t2p->t2_maxpcount = 2; - t2p->t2_maxdcount = vcp->vc_txmax; - error = smb_t2_request(t2p); - if (error) { - smb_t2_done(t2p); - /* Invalid info level? Try fallback. */ - if (error == EINVAL && - infolevel == SMB_QFILEINFO_ALL_INFO) { - infolevel = SMB_QFILEINFO_STANDARD; - goto top; - } - return (error); + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_getpattr(np, fap, scrp); + } else { + uint16_t fid = SMB_FID_UNUSED; + error = smbfs_smb1_trans2_query(np, fid, fap, scrp); } - mdp = &t2p->t2_rdata; - svtz = vcp->vc_sopt.sv_tz; - switch (infolevel) { - case SMB_QFILEINFO_STANDARD: - md_get_uint16le(mdp, &date); - md_get_uint16le(mdp, &time); /* creation time */ - smb_dos2unixtime(date, time, 0, svtz, &fap->fa_createtime); - md_get_uint16le(mdp, &date); - md_get_uint16le(mdp, &time); /* access time */ - smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime); - md_get_uint16le(mdp, &date); - md_get_uint16le(mdp, &time); /* modify time */ - smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime); - md_get_uint32le(mdp, &size); /* EOF position */ - fap->fa_size = size; - md_get_uint32le(mdp, &size); /* allocation size */ - fap->fa_allocsz = size; - error = md_get_uint16le(mdp, &wattr); - fap->fa_attr = wattr; - timesok = 1; - break; - case SMB_QFILEINFO_ALL_INFO: - timesok = 0; - /* creation time */ - md_get_uint64le(mdp, &llongint); - if (llongint) - timesok++; - smb_time_NT2local(llongint, &fap->fa_createtime); - - /* last access time */ - md_get_uint64le(mdp, &llongint); - if (llongint) - timesok++; - smb_time_NT2local(llongint, &fap->fa_atime); - - /* last write time */ - md_get_uint64le(mdp, &llongint); - if (llongint) - timesok++; - smb_time_NT2local(llongint, &fap->fa_mtime); - - /* last change time */ - md_get_uint64le(mdp, &llongint); - if (llongint) - timesok++; - smb_time_NT2local(llongint, &fap->fa_ctime); - - /* attributes */ - md_get_uint32le(mdp, &dattr); - fap->fa_attr = dattr; - /* - * 4-Byte alignment - discard - * Specs don't talk about this. - */ - md_get_uint32le(mdp, NULL); - /* allocation size */ - md_get_uint64le(mdp, &lsize); - fap->fa_allocsz = lsize; - /* File size */ - error = md_get_uint64le(mdp, &lsize); - fap->fa_size = lsize; - break; - default: - SMBVDEBUG("unexpected info level %d\n", infolevel); - error = EINVAL; - } - smb_t2_done(t2p); - /* - * if all times are zero (observed with FAT on NT4SP6) - * then fall back to older info level - */ - if (!timesok) { - if (infolevel == SMB_QFILEINFO_ALL_INFO) { - infolevel = SMB_QFILEINFO_STANDARD; - goto top; - } - error = EINVAL; - } return (error); } /* - * Support functions for _qstreaminfo - * Moved to smbfs_xattr.c + * Get and parse FileFsAttributeInformation */ - int smbfs_smb_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *fsa, struct smb_cred *scrp) { - struct smb_t2rq *t2p; - struct mbchain *mbp; - struct mdchain *mdp; int error; - uint32_t nlen; - - error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION, - scrp, &t2p); - if (error) - return (error); - mbp = &t2p->t2_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, SMB_QFS_ATTRIBUTE_INFO); - t2p->t2_maxpcount = 4; - t2p->t2_maxdcount = 4 * 3 + 512; - error = smb_t2_request(t2p); - if (error) - goto out; - - mdp = &t2p->t2_rdata; - md_get_uint32le(mdp, &fsa->fsa_aflags); - md_get_uint32le(mdp, &fsa->fsa_maxname); - error = md_get_uint32le(mdp, &nlen); /* fs name length */ - if (error) - goto out; - /* - * Get the FS type name. - */ - bzero(fsa->fsa_tname, FSTYPSZ); - if (SMB_UNICODE_STRINGS(SSTOVC(ssp))) { - uint16_t tmpbuf[FSTYPSZ]; - size_t tmplen, outlen; - - if (nlen > sizeof (tmpbuf)) - nlen = sizeof (tmpbuf); - error = md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM); - tmplen = nlen / 2; /* UCS-2 chars */ - outlen = FSTYPSZ - 1; - (void) uconv_u16tou8(tmpbuf, &tmplen, - (uchar_t *)fsa->fsa_tname, &outlen, - UCONV_IN_LITTLE_ENDIAN); + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_qfsattr(ssp, fsa, scrp); } else { - if (nlen > (FSTYPSZ - 1)) - nlen = FSTYPSZ - 1; - error = md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM); + error = smbfs_smb1_qfsattr(ssp, fsa, scrp); } /* @@ -442,652 +138,113 @@ smbfs_smb_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *fsa, SMB_SS_UNLOCK(ssp); } -out: - smb_t2_done(t2p); - return (0); + return (error); } int smbfs_smb_statfs(struct smb_share *ssp, statvfs64_t *sbp, struct smb_cred *scp) { - int error; - - if (SMB_DIALECT(SSTOVC(ssp)) >= SMB_DIALECT_LANMAN2_0) - error = smbfs_smb_statfsLM2(ssp, sbp, scp); - else - error = smbfs_smb_statfsLM1(ssp, sbp, scp); - - return (error); -} - -int -smbfs_smb_statfsLM2(struct smb_share *ssp, statvfs64_t *sbp, - struct smb_cred *scrp) -{ - struct smb_t2rq *t2p; - struct mbchain *mbp; - struct mdchain *mdp; - uint16_t bsize; - uint32_t units, bpu, funits; - uint64_t s, t, f; - int error; - - error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION, - scrp, &t2p); - if (error) - return (error); - mbp = &t2p->t2_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, SMB_QFS_ALLOCATION); - t2p->t2_maxpcount = 4; - t2p->t2_maxdcount = 4 * 4 + 2; - error = smb_t2_request(t2p); - if (error) - goto out; - - mdp = &t2p->t2_rdata; - md_get_uint32le(mdp, NULL); /* fs id */ - md_get_uint32le(mdp, &bpu); - md_get_uint32le(mdp, &units); - md_get_uint32le(mdp, &funits); - error = md_get_uint16le(mdp, &bsize); - if (error) - goto out; - s = bsize; - s *= bpu; - t = units; - f = funits; - /* - * Don't allow over-large blocksizes as they determine - * Finder List-view size granularities. On the other - * hand, we mustn't let the block count overflow the - * 31 bits available. - */ - while (s > 16 * 1024) { - if (t > LONG_MAX) - break; - s /= 2; - t *= 2; - f *= 2; - } - while (t > LONG_MAX) { - t /= 2; - f /= 2; - s *= 2; - } - sbp->f_bsize = (ulong_t)s; /* file system block size */ - sbp->f_blocks = t; /* total data blocks in file system */ - sbp->f_bfree = f; /* free blocks in fs */ - sbp->f_bavail = f; /* free blocks avail to non-superuser */ - sbp->f_files = (-1); /* total file nodes in file system */ - sbp->f_ffree = (-1); /* free file nodes in fs */ - -out: - smb_t2_done(t2p); - return (0); -} - -int -smbfs_smb_statfsLM1(struct smb_share *ssp, statvfs64_t *sbp, - struct smb_cred *scrp) -{ - struct smb_rq rq, *rqp = &rq; - struct mdchain *mdp; - uint16_t units, bpu, bsize, funits; - uint64_t s, t, f; - int error; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION_DISK, - scrp); - if (error) - return (error); - smb_rq_wstart(rqp); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - if (error) - goto out; - - smb_rq_getreply(rqp, &mdp); - md_get_uint16le(mdp, &units); - md_get_uint16le(mdp, &bpu); - md_get_uint16le(mdp, &bsize); - error = md_get_uint16le(mdp, &funits); - if (error) - goto out; - s = bsize; - s *= bpu; - t = units; - f = funits; - /* - * Don't allow over-large blocksizes as they determine - * Finder List-view size granularities. On the other - * hand, we mustn't let the block count overflow the - * 31 bits available. - */ - while (s > 16 * 1024) { - if (t > LONG_MAX) - break; - s /= 2; - t *= 2; - f *= 2; - } - while (t > LONG_MAX) { - t /= 2; - f /= 2; - s *= 2; - } - sbp->f_bsize = (ulong_t)s; /* file system block size */ - sbp->f_blocks = t; /* total data blocks in file system */ - sbp->f_bfree = f; /* free blocks in fs */ - sbp->f_bavail = f; /* free blocks avail to non-superuser */ - sbp->f_files = (-1); /* total file nodes in file system */ - sbp->f_ffree = (-1); /* free file nodes in fs */ - -out: - smb_rq_done(rqp); - return (0); -} - -int -smbfs_smb_seteof(struct smb_share *ssp, uint16_t fid, uint64_t newsize, - struct smb_cred *scrp) -{ - struct smb_t2rq *t2p; - struct smb_vc *vcp = SSTOVC(ssp); - struct mbchain *mbp; - int error; - - error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION, - scrp, &t2p); - if (error) - return (error); - mbp = &t2p->t2_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, fid); - if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) - mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFORMATION); - else - mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFO); - mb_put_uint16le(mbp, 0); /* pad */ - mbp = &t2p->t2_tdata; - mb_init(mbp); - mb_put_uint64le(mbp, newsize); - t2p->t2_maxpcount = 2; - t2p->t2_maxdcount = 0; - error = smb_t2_request(t2p); - smb_t2_done(t2p); - return (error); -} - -int -smbfs_smb_setdisp(struct smbnode *np, - uint16_t fid, uint8_t newdisp, - struct smb_cred *scrp) -{ - struct smb_t2rq *t2p; - struct smb_share *ssp = np->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); - struct mbchain *mbp; - int error; - - error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION, - scrp, &t2p); - if (error) - return (error); - mbp = &t2p->t2_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, fid); - if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) - mb_put_uint16le(mbp, SMB_SFILEINFO_DISPOSITION_INFORMATION); - else - mb_put_uint16le(mbp, SMB_SFILEINFO_DISPOSITION_INFO); - mb_put_uint16le(mbp, 0); /* pad */ - mbp = &t2p->t2_tdata; - mb_init(mbp); - mb_put_uint8(mbp, newdisp); - t2p->t2_maxpcount = 2; - t2p->t2_maxdcount = 0; - error = smb_t2_request(t2p); - smb_t2_done(t2p); - return (error); -} - -/* - * On SMB1, the trans2 rename only allows a rename where the - * source and target are in the same directory. If you give - * the server any separators, you get "status not supported". - */ - -/*ARGSUSED*/ -int -smbfs_smb_t2rename(struct smbnode *np, - const char *tname, int tnlen, struct smb_cred *scrp, - uint16_t fid, int overwrite) -{ - struct smb_t2rq *t2p; - struct smb_share *ssp = np->n_mount->smi_share; + struct smb_fs_size_info info; struct smb_vc *vcp = SSTOVC(ssp); - struct mbchain *mbp; - int32_t *ucslenp; + uint32_t bps, spu; int error; - if (!(vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)) - return (ENOTSUP); - error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION, - scrp, &t2p); + if (vcp->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_statfs(ssp, &info, scp); + } else { + error = smbfs_smb1_statfs(ssp, &info, scp); + } if (error) return (error); - mbp = &t2p->t2_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, fid); - mb_put_uint16le(mbp, SMB_SFILEINFO_RENAME_INFORMATION); - mb_put_uint16le(mbp, 0); /* reserved, nowadays */ - - mbp = &t2p->t2_tdata; - mb_init(mbp); - mb_put_uint32le(mbp, overwrite); /* one or zero */ - mb_put_uint32le(mbp, 0); /* obsolete target dir fid */ + /* A bit of paranoia. */ + bps = info.bytes_per_sect; + if (bps < DEV_BSIZE) + bps = DEV_BSIZE; + spu = info.sect_per_unit; + if (spu == 0) + spu = 1; - ucslenp = (int32_t *)mb_reserve(mbp, sizeof (int32_t)); - mbp->mb_count = 0; - error = smb_put_dmem(mbp, vcp, tname, tnlen, SMB_CS_NONE, NULL); - if (error) - goto out; - *ucslenp = htolel(mbp->mb_count); + /* preferred file system block size */ + sbp->f_bsize = bps * spu; - t2p->t2_maxpcount = 2; - t2p->t2_maxdcount = 0; - error = smb_t2_request(t2p); -out: - smb_t2_done(t2p); - return (error); -} + /* file system block size ("fragment size") */ + sbp->f_frsize = bps; -int -smbfs_smb_flush(struct smbnode *np, struct smb_cred *scrp) -{ - struct smb_share *ssp = np->n_mount->smi_share; - struct smb_rq rq, *rqp = &rq; - struct mbchain *mbp; - int error; + /* total blocks of f_frsize */ + sbp->f_blocks = info.total_units * spu; - /* Shared lock for n_fid use below. */ - ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + /* free blocks of f_frsize */ + sbp->f_bfree = info.actual_avail * spu; - if (!(np->n_flag & NFLUSHWIRE)) - return (0); - if (np->n_fidrefs == 0) - return (0); /* not open */ + /* free blocks avail to non-superuser */ + sbp->f_bavail = info.caller_avail * spu; - /* After reconnect, n_fid is invalid */ - if (np->n_vcgenid != ssp->ss_vcgenid) - return (ESTALE); + sbp->f_files = (-1); /* total file nodes in file system */ + sbp->f_ffree = (-1); /* free file nodes in fs */ - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_FLUSH, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, np->n_fid); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - smb_rq_done(rqp); - if (!error) { - mutex_enter(&np->r_statelock); - np->n_flag &= ~NFLUSHWIRE; - mutex_exit(&np->r_statelock); - } return (error); } int -smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize, - struct smb_cred *scrp) +smbfs_smb_setdisp(struct smb_share *ssp, smb_fh_t *fhp, + uint8_t disp, struct smb_cred *scrp) { - struct smb_share *ssp = np->n_mount->smi_share; - struct smb_rq rq, *rqp = &rq; - struct mbchain *mbp; - int error; + int err; - if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { - /* - * This call knows about 64-bit offsets. - */ - error = smbfs_smb_seteof(ssp, fid, newsize, scrp); - if (!error) { - mutex_enter(&np->r_statelock); - np->n_flag |= (NFLUSHWIRE | NATTRCHANGED); - mutex_exit(&np->r_statelock); - return (0); - } + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + err = smbfs_smb2_setdisp(ssp, &fhp->fh_fid2, disp, scrp); + } else { + err = smbfs_smb1_setdisp(ssp, fhp->fh_fid1, disp, scrp); } - /* - * OK, so fallback to SMB_COM_WRITE, but note: - * it only supports 32-bit file offsets. - */ - if (newsize > UINT32_MAX) - return (EFBIG); - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_WRITE, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, fid); - mb_put_uint16le(mbp, 0); - mb_put_uint32le(mbp, newsize); - mb_put_uint16le(mbp, 0); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_DATA); - mb_put_uint16le(mbp, 0); - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - smb_rq_done(rqp); - mutex_enter(&np->r_statelock); - np->n_flag |= (NFLUSHWIRE | NATTRCHANGED); - mutex_exit(&np->r_statelock); - return (error); + return (err); } -/* - * Old method for getting file attributes. - */ int -smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen, - struct smbfattr *fap, struct smb_cred *scrp) +smbfs_smb_setfsize(struct smb_share *ssp, smb_fh_t *fhp, + uint64_t size, struct smb_cred *scrp) { - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = np->n_mount->smi_share; - struct mbchain *mbp; - struct mdchain *mdp; - uint8_t wc; int error; - uint16_t wattr; - uint32_t longint; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - - error = smbfs_fullpath(mbp, SSTOVC(ssp), np, - name, nmlen, '\\'); - if (error) - goto out; - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - if (error) - goto out; - smb_rq_getreply(rqp, &mdp); - error = md_get_uint8(mdp, &wc); - if (error) - goto out; - if (wc != 10) { - error = EBADRPC; - goto out; - } - md_get_uint16le(mdp, &wattr); - fap->fa_attr = wattr; - /* - * Be careful using the time returned here, as - * with FAT on NT4SP6, at least, the time returned is low - * 32 bits of 100s of nanoseconds (since 1601) so it rolls - * over about every seven minutes! - */ - md_get_uint32le(mdp, &longint); /* specs: secs since 1970 */ - smb_time_server2local(longint, - SSTOVC(ssp)->vc_sopt.sv_tz, &fap->fa_mtime); - error = md_get_uint32le(mdp, &longint); - fap->fa_size = longint; - -out: - smb_rq_done(rqp); - return (error); -} - -/* - * Set DOS file attributes. mtime should be NULL for dialects above lm10 - */ -int -smbfs_smb_setpattr1(struct smbnode *np, const char *name, int len, - uint32_t attr, struct timespec *mtime, - struct smb_cred *scrp) -{ - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = np->n_mount->smi_share; - struct mbchain *mbp; - long time; - int error, svtz; - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scrp); - if (error) - return (error); - svtz = SSTOVC(ssp)->vc_sopt.sv_tz; - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, (uint16_t)attr); - if (mtime) { - smb_time_local2server(mtime, svtz, &time); - } else - time = 0; - mb_put_uint32le(mbp, time); /* mtime */ - mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - - error = smbfs_fullpath(mbp, SSTOVC(ssp), np, name, len, '\\'); - if (error) - goto out; - mb_put_uint8(mbp, SMB_DT_ASCII); - if (SMB_UNICODE_STRINGS(SSTOVC(ssp))) { - mb_put_padbyte(mbp); - mb_put_uint8(mbp, 0); /* 1st byte NULL Unicode char */ + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_seteof(ssp, &fhp->fh_fid2, size, scrp); + } else { + error = smbfs_smb1_seteof(ssp, fhp->fh_fid1, size, scrp); } - mb_put_uint8(mbp, 0); - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - -out: - smb_rq_done(rqp); - return (error); -} - -int -smbfs_smb_hideit(struct smbnode *np, const char *name, int len, - struct smb_cred *scrp) -{ - struct smbfattr fa; - int error; - uint32_t attr; - error = smbfs_smb_query_info(np, name, len, &fa, scrp); - attr = fa.fa_attr; - if (!error && !(attr & SMB_FA_HIDDEN)) { - attr |= SMB_FA_HIDDEN; - error = smbfs_smb_setpattr1(np, name, len, attr, NULL, scrp); - } return (error); } -int -smbfs_smb_unhideit(struct smbnode *np, const char *name, int len, - struct smb_cred *scrp) -{ - struct smbfattr fa; - uint32_t attr; - int error; - - error = smbfs_smb_query_info(np, name, len, &fa, scrp); - attr = fa.fa_attr; - if (!error && (attr & SMB_FA_HIDDEN)) { - attr &= ~SMB_FA_HIDDEN; - error = smbfs_smb_setpattr1(np, name, len, attr, NULL, scrp); - } - return (error); -} - /* * Set file attributes (optionally: DOS attr, atime, mtime) - * either by open FID or by path name (FID == -1). + * Always have an open FID with set attr rights. */ int smbfs_smb_setfattr( - struct smbnode *np, - int fid, + struct smb_share *ssp, + smb_fh_t *fhp, uint32_t attr, struct timespec *mtime, struct timespec *atime, struct smb_cred *scrp) { - struct smb_share *ssp = np->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); + struct mbchain mb_info; + struct mbchain *mbp = &mb_info; + uint64_t tm; int error; /* - * Normally can use the trans2 call. - */ - if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { - error = smbfs_smb_setfattrNT(np, fid, - attr, mtime, atime, scrp); - return (error); - } - - /* - * Fall-back for older protocols. + * Build a struct FILE_BASIC_INFORMATION in mbp + * LARGE_INTEGER CreationTime; + * LARGE_INTEGER LastAccessTime; + * LARGE_INTEGER LastWriteTime; + * LARGE_INTEGER ChangeTime; + * ULONG FileAttributes; + * Zero in times means "no change". */ - if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) { - error = smbfs_smb_setftime1(np, fid, - mtime, atime, scrp); - return (error); - } - error = smbfs_smb_setpattr1(np, NULL, 0, - attr, mtime, scrp); - return (error); -} - -/* - * Set file atime and mtime. Isn't supported by core dialect. - */ -int -smbfs_smb_setftime1( - struct smbnode *np, - uint16_t fid, - struct timespec *mtime, - struct timespec *atime, - struct smb_cred *scrp) -{ - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = np->n_mount->smi_share; - struct mbchain *mbp; - uint16_t date, time; - int error, tzoff; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scrp); - if (error) - return (error); - - tzoff = SSTOVC(ssp)->vc_sopt.sv_tz; - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, fid); - mb_put_uint32le(mbp, 0); /* creation time */ - - if (atime) - smb_time_unix2dos(atime, tzoff, &date, &time, NULL); - else - time = date = 0; - mb_put_uint16le(mbp, date); - mb_put_uint16le(mbp, time); - if (mtime) - smb_time_unix2dos(mtime, tzoff, &date, &time, NULL); - else - time = date = 0; - mb_put_uint16le(mbp, date); - mb_put_uint16le(mbp, time); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - SMBVDEBUG("%d\n", error); - smb_rq_done(rqp); - return (error); -} - -/* - * Set DOS file attributes, either via open FID or by path name. - * Looks like this call can be used only if CAP_NT_SMBS bit is on. - * - * When setting via path (fid == -1): - * *BASIC_INFO works with Samba, but Win2K servers say it is an - * invalid information level on a SET_PATH_INFO. Note Win2K does - * support *BASIC_INFO on a SET_FILE_INFO, and they support the - * equivalent *BASIC_INFORMATION on SET_PATH_INFO. Go figure. - */ -int -smbfs_smb_setfattrNT( - struct smbnode *np, - int fid, /* if fid == -1, set by path */ - uint32_t attr, - struct timespec *mtime, - struct timespec *atime, - struct smb_cred *scrp) -{ - struct smb_t2rq *t2p; - struct smb_share *ssp = np->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); - struct mbchain *mbp; - uint64_t tm; - int error; - uint16_t cmd, level; - - if (fid == -1) { - cmd = SMB_TRANS2_SET_PATH_INFORMATION; - } else { - if (fid > UINT16_MAX) - return (EINVAL); - cmd = SMB_TRANS2_SET_FILE_INFORMATION; - } - if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) - level = SMB_SFILEINFO_BASIC_INFORMATION; - else - level = SMB_SFILEINFO_BASIC_INFO; - - error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p); - if (error) - return (error); - - mbp = &t2p->t2_tparam; - mb_init(mbp); - - if (cmd == SMB_TRANS2_SET_FILE_INFORMATION) - mb_put_uint16le(mbp, fid); - - mb_put_uint16le(mbp, level); - mb_put_uint32le(mbp, 0); /* MBZ */ - - if (cmd == SMB_TRANS2_SET_PATH_INFORMATION) { - error = smbfs_fullpath(mbp, vcp, np, NULL, 0, '\\'); - if (error != 0) - goto out; - } - - /* FAT file systems don't support dates earlier than 1980. */ - - mbp = &t2p->t2_tdata; mb_init(mbp); mb_put_uint64le(mbp, 0); /* creation time */ if (atime) { @@ -1097,7 +254,7 @@ smbfs_smb_setfattrNT( tm = NT1980; } else tm = 0; - mb_put_uint64le(mbp, tm); /* access time */ + mb_put_uint64le(mbp, tm); /* last access time */ if (mtime) { smb_time_local2NT(mtime, &tm); if (tm != 0 && (ssp->ss_flags & SMBS_FST_FAT) && @@ -1106,19 +263,35 @@ smbfs_smb_setfattrNT( } else tm = 0; mb_put_uint64le(mbp, tm); /* last write time */ - mb_put_uint64le(mbp, 0); /* ctime (no change) */ + mb_put_uint64le(mbp, 0); /* change time */ mb_put_uint32le(mbp, attr); - mb_put_uint32le(mbp, 0); /* padding */ - t2p->t2_maxpcount = 2; - t2p->t2_maxdcount = 0; - error = smb_t2_request(t2p); -out: - smb_t2_done(t2p); + + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_setfattr(ssp, &fhp->fh_fid2, mbp, scrp); + } else { + error = smbfs_smb1_setfattr(ssp, fhp->fh_fid1, mbp, scrp); + } + + return (error); +} + +int +smbfs_smb_flush(struct smb_share *ssp, smb_fh_t *fhp, + struct smb_cred *scrp) +{ + int error; + + if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_flush(ssp, &fhp->fh_fid2, scrp); + } else { + error = smbfs_smb1_flush(ssp, fhp->fh_fid1, scrp); + } return (error); } /* * Modern create/open of file or directory. + * On success, fills in fhp->fh_fid* and fhp->fh_rights */ int smbfs_smb_ntcreatex( @@ -1132,7 +305,7 @@ smbfs_smb_ntcreatex( uint32_t disp, /* open disposition */ uint32_t createopt, /* NTCREATEX_OPTIONS_ */ struct smb_cred *scrp, - uint16_t *fidp, /* returned FID */ + smb_fh_t *fhp, /* pre-made file handle to fill in */ uint32_t *cr_act_p, /* optional returned create action */ struct smbfattr *fap) /* optional returned attributes */ { @@ -1153,7 +326,7 @@ smbfs_smb_ntcreatex( 0, /* NTCREATEX_FLAGS... */ req_acc, efa, share_acc, disp, createopt, NTCREATEX_IMPERSONATION_IMPERSONATION, - scrp, fidp, cr_act_p, fap); + scrp, fhp, cr_act_p, fap); out: mb_done(&name_mb); @@ -1161,233 +334,68 @@ out: return (err); } -static uint32_t -smb_mode2rights(int mode) -{ - mode = mode & SMB_AM_OPENMODE; - uint32_t rights = - STD_RIGHT_SYNCHRONIZE_ACCESS | - STD_RIGHT_READ_CONTROL_ACCESS; - - if ((mode == SMB_AM_OPENREAD) || - (mode == SMB_AM_OPENRW)) { - rights |= - SA_RIGHT_FILE_READ_ATTRIBUTES | - SA_RIGHT_FILE_READ_DATA; - } - - if ((mode == SMB_AM_OPENWRITE) || - (mode == SMB_AM_OPENRW)) { - rights |= - SA_RIGHT_FILE_WRITE_ATTRIBUTES | - SA_RIGHT_FILE_APPEND_DATA | - SA_RIGHT_FILE_WRITE_DATA; - } - - if (mode == SMB_AM_OPENEXEC) { - rights |= - SA_RIGHT_FILE_READ_ATTRIBUTES | - SA_RIGHT_FILE_EXECUTE; - } - - return (rights); -} - -static int -smb_rights2mode(uint32_t rights) -{ - int accmode = SMB_AM_OPENEXEC; /* our fallback */ - - if (rights & (SA_RIGHT_FILE_APPEND_DATA | SA_RIGHT_FILE_DELETE_CHILD | - SA_RIGHT_FILE_WRITE_EA | SA_RIGHT_FILE_WRITE_ATTRIBUTES | - SA_RIGHT_FILE_WRITE_DATA | STD_RIGHT_WRITE_OWNER_ACCESS | - STD_RIGHT_DELETE_ACCESS | STD_RIGHT_WRITE_DAC_ACCESS)) - accmode = SMB_AM_OPENWRITE; - if (rights & (SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_READ_ATTRIBUTES | - SA_RIGHT_FILE_READ_EA | STD_RIGHT_READ_CONTROL_ACCESS)) - accmode = (accmode == SMB_AM_OPENEXEC) ? SMB_AM_OPENREAD - : SMB_AM_OPENRW; - return (accmode); -} - -static int -smbfs_smb_oldopen( - struct smbnode *np, - const char *name, - int nmlen, - int xattr, - int accmode, - struct smb_cred *scrp, - uint16_t *fidp, - uint16_t *granted_mode_p, - smbfattr_t *fap) -{ - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = np->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); - struct mbchain *mbp; - struct mdchain *mdp; - struct smbfattr fa; - uint8_t wc; - uint16_t wattr; - uint32_t longint; - int error; - - bzero(&fa, sizeof (fa)); - - /* - * XXX: move to callers... - * - * Use DENYNONE to give unixy semantics of permitting - * everything not forbidden by permissions. Ie denial - * is up to server with clients/openers needing to use - * advisory locks for further control. - */ - accmode |= SMB_SM_DENYNONE; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, accmode); - mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_RDONLY | - SMB_FA_DIR); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - - error = smbfs_fullpath(mbp, vcp, np, name, nmlen, - xattr ? ':' : '\\'); - if (error) - goto done; - smb_rq_bend(rqp); - /* - * Don't want to risk missing a successful - * open response, or we could "leak" FIDs. - */ - rqp->sr_flags |= SMBR_NOINTR_RECV; - error = smb_rq_simple_timed(rqp, smb_timo_open); - if (error) - goto done; - smb_rq_getreply(rqp, &mdp); - /* - * 8/2002 a DAVE server returned wc of 15 so we ignore that. - * (the actual packet length and data was correct) - */ - error = md_get_uint8(mdp, &wc); - if (error) - goto done; - if (wc != 7 && wc != 15) { - error = EBADRPC; - goto done; - } - md_get_uint16le(mdp, fidp); - md_get_uint16le(mdp, &wattr); - fa.fa_attr = wattr; - /* - * Be careful using the time returned here, as - * with FAT on NT4SP6, at least, the time returned is low - * 32 bits of 100s of nanoseconds (since 1601) so it rolls - * over about every seven minutes! - */ - md_get_uint32le(mdp, &longint); /* specs: secs since 1970 */ - smb_time_server2local(longint, vcp->vc_sopt.sv_tz, &fa.fa_mtime); - md_get_uint32le(mdp, &longint); - fa.fa_size = longint; - error = md_get_uint16le(mdp, granted_mode_p); - -done: - smb_rq_done(rqp); - if (error) - return (error); - - if (fap) - *fap = fa; /* struct copy */ - - return (0); -} - +/* + * Get a file handle with (at least) the specified rights. + * + * We'll try to borrow the node ->n_fid if we can. When we + * borrow n_fid, just take a hold on the smb_fh_t, and don't + * bump n_fidrefs as that tracks VFS-level opens. Similarly + * in _tmpclose we just release the smb_fh_t, not n_fidrefs. + */ int smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, struct smb_cred *scrp, - uint16_t *fidp) + smb_fh_t **fhpp) { struct smb_share *ssp = np->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); - int accmode, error; - - /* Shared lock for n_fid use below. */ - ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + smb_fh_t *fhp = NULL; + int error; /* Can we re-use n_fid? or must we open anew? */ mutex_enter(&np->r_statelock); if (np->n_fidrefs > 0 && - np->n_vcgenid == ssp->ss_vcgenid && - (rights & np->n_rights) == rights) { - np->n_fidrefs++; - *fidp = np->n_fid; + (fhp = np->n_fid) != NULL && + fhp->fh_vcgenid == ssp->ss_vcgenid && + (fhp->fh_rights & rights) == rights) { + smb_fh_hold(fhp); + *fhpp = fhp; mutex_exit(&np->r_statelock); return (0); } mutex_exit(&np->r_statelock); + error = smb_fh_create(ssp, &fhp); + if (error != 0) + goto out; + /* re-open an existing file. */ - if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { - error = smbfs_smb_ntcreatex(np, - NULL, 0, 0, /* name nmlen xattr */ - rights, SMB_EFA_NORMAL, - NTCREATEX_SHARE_ACCESS_ALL, - NTCREATEX_DISP_OPEN, - 0, /* create options */ - scrp, fidp, - NULL, NULL); /* cr_act_p fa_p */ - return (error); - } + error = smbfs_smb_ntcreatex(np, + NULL, 0, 0, /* name nmlen xattr */ + rights, SMB_EFA_NORMAL, + NTCREATEX_SHARE_ACCESS_ALL, + NTCREATEX_DISP_OPEN, + 0, /* create options */ + scrp, fhp, + NULL, NULL); /* cr_act_p fa_p */ + if (error != 0) + goto out; + + fhp->fh_rights = rights; + smb_fh_opened(fhp); + *fhpp = fhp; + fhp = NULL; - accmode = smb_rights2mode(rights); - error = smbfs_smb_oldopen(np, - NULL, 0, 0, /* name nmlen xattr */ - accmode, scrp, - fidp, - NULL, /* granted mode p */ - NULL); /* fa p */ +out: + if (fhp != NULL) + smb_fh_rele(fhp); return (error); } -int -smbfs_smb_tmpclose(struct smbnode *np, uint16_t fid, struct smb_cred *scrp) +/* ARGSUSED */ +void +smbfs_smb_tmpclose(struct smbnode *np, smb_fh_t *fhp) { - struct smb_share *ssp = np->n_mount->smi_share; - int error = 0; - uint16_t oldfid = SMB_FID_UNUSED; - - /* Shared lock for n_fid use below. */ - ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); - - mutex_enter(&np->r_statelock); - if (fid == np->n_fid) { - ASSERT(np->n_fidrefs > 0); - if (--np->n_fidrefs == 0) { - /* - * Don't expect to find the last reference - * here in tmpclose. Hard to deal with as - * we don't have r_lkserlock exclusive. - * Will close oldfid below. - */ - oldfid = np->n_fid; - np->n_fid = SMB_FID_UNUSED; - } - } else { - /* Will close the passed fid. */ - oldfid = fid; - } - mutex_exit(&np->r_statelock); - - if (oldfid != SMB_FID_UNUSED) - error = smbfs_smb_close(ssp, oldfid, NULL, scrp); - - return (error); + smb_fh_rele(fhp); } int @@ -1398,124 +406,47 @@ smbfs_smb_open( int xattr, uint32_t rights, struct smb_cred *scrp, - uint16_t *fidp, - uint32_t *rightsp, + smb_fh_t **fhpp, smbfattr_t *fap) { struct smb_share *ssp = np->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); - int accmode, error; - uint16_t grantedmode; - - /* open an existing file */ - if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { - error = smbfs_smb_ntcreatex(np, - name, nmlen, xattr, - rights, SMB_EFA_NORMAL, - NTCREATEX_SHARE_ACCESS_ALL, - NTCREATEX_DISP_OPEN, - 0, /* create options */ - scrp, fidp, - NULL, fap); /* cr_act_p fa_p */ - if (error != 0) - return (error); - *rightsp = rights; - return (0); - } + // struct smb_vc *vcp = SSTOVC(ssp); + smb_fh_t *fhp = NULL; + int error; - accmode = smb_rights2mode(rights); - error = smbfs_smb_oldopen(np, - name, nmlen, xattr, accmode, scrp, - fidp, &grantedmode, fap); + error = smb_fh_create(ssp, &fhp); if (error != 0) - return (error); - *rightsp = smb_mode2rights(grantedmode); - (void) smbfs_smb_getfattr(np, fap, scrp); + goto out; - return (0); -} + /* open an existing file */ + error = smbfs_smb_ntcreatex(np, + name, nmlen, xattr, + rights, SMB_EFA_NORMAL, + NTCREATEX_SHARE_ACCESS_ALL, + NTCREATEX_DISP_OPEN, + 0, /* create options */ + scrp, fhp, NULL, fap); + if (error != 0) + goto out; -int -smbfs_smb_close(struct smb_share *ssp, uint16_t fid, - struct timespec *mtime, struct smb_cred *scrp) -{ - int error; + fhp->fh_rights = rights; + smb_fh_opened(fhp); + *fhpp = fhp; + fhp = NULL; - error = smb_smb_close(ssp, fid, mtime, scrp); +out: + if (fhp != NULL) + smb_fh_rele(fhp); - /* - * ENOTCONN isn't interesting - if the connection is closed, - * so are all our FIDs - and EIO is also not interesting, - * as it means a forced unmount was done. (was ENXIO) - * Also ETIME, which means we sent the request but gave up - * waiting before the response came back. - * - * Don't clog up the system log with warnings about these - * uninteresting failures on closes. - */ - switch (error) { - case ENOTCONN: - case ENXIO: - case EIO: - case ETIME: - error = 0; - } - return (error); + return (0); } -static int -smbfs_smb_oldcreate(struct smbnode *dnp, const char *name, int nmlen, - int xattr, struct smb_cred *scrp, uint16_t *fidp) +void +smbfs_smb_close(smb_fh_t *fhp) { - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = dnp->n_mount->smi_share; - struct mbchain *mbp; - struct mdchain *mdp; - struct timespec ctime; - uint8_t wc; - long tm; - int error; - uint16_t attr = SMB_FA_ARCHIVE; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - if (name && *name == '.') - attr |= SMB_FA_HIDDEN; - mb_put_uint16le(mbp, attr); /* attributes */ - gethrestime(&ctime); - smb_time_local2server(&ctime, SSTOVC(ssp)->vc_sopt.sv_tz, &tm); - mb_put_uint32le(mbp, tm); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, nmlen, - xattr ? ':' : '\\'); - if (error) - goto out; - smb_rq_bend(rqp); - /* - * Don't want to risk missing a successful - * open response, or we could "leak" FIDs. - */ - rqp->sr_flags |= SMBR_NOINTR_RECV; - error = smb_rq_simple_timed(rqp, smb_timo_open); - if (error) - goto out; - - smb_rq_getreply(rqp, &mdp); - md_get_uint8(mdp, &wc); - if (wc != 1) { - error = EBADRPC; - goto out; - } - error = md_get_uint16le(mdp, fidp); -out: - smb_rq_done(rqp); - return (error); + smb_fh_close(fhp); + smb_fh_rele(fhp); } int @@ -1526,759 +457,127 @@ smbfs_smb_create( int xattr, uint32_t disp, struct smb_cred *scrp, - uint16_t *fidp) + smb_fh_t **fhpp) { struct smb_share *ssp = dnp->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); + // struct smb_vc *vcp = SSTOVC(ssp); + smb_fh_t *fhp = NULL; uint32_t efa, rights; int error; + error = smb_fh_create(ssp, &fhp); + if (error != 0) + goto out; + /* * At present the only access we might need is to WRITE data, * and that only if we are creating a "symlink". When/if the * access needed gets more complex it should made a parameter * and be set upstream. */ - if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { - rights = SA_RIGHT_FILE_WRITE_DATA; - efa = SMB_EFA_NORMAL; - if (!xattr && name && *name == '.') - efa = SMB_EFA_HIDDEN; - error = smbfs_smb_ntcreatex(dnp, - name, nmlen, xattr, rights, efa, - NTCREATEX_SHARE_ACCESS_ALL, - disp, /* != NTCREATEX_DISP_OPEN */ - NTCREATEX_OPTIONS_NON_DIRECTORY_FILE, - scrp, fidp, NULL, NULL); /* cr_act_p fa_p */ - return (error); - } + rights = SA_RIGHT_FILE_WRITE_DATA; + efa = SMB_EFA_NORMAL; + if (!xattr && name && *name == '.') + efa = SMB_EFA_HIDDEN; + error = smbfs_smb_ntcreatex(dnp, + name, nmlen, xattr, rights, efa, + NTCREATEX_SHARE_ACCESS_ALL, + disp, /* != NTCREATEX_DISP_OPEN */ + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE, + scrp, fhp, NULL, NULL); + if (error != 0) + goto out; - error = smbfs_smb_oldcreate(dnp, name, nmlen, xattr, scrp, fidp); - return (error); -} + fhp->fh_rights = rights; + smb_fh_opened(fhp); + *fhpp = fhp; + fhp = NULL; -int -smbfs_smb_delete(struct smbnode *np, struct smb_cred *scrp, const char *name, - int nmlen, int xattr) -{ - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = np->n_mount->smi_share; - struct mbchain *mbp; - int error; +out: + if (fhp != NULL) + smb_fh_rele(fhp); - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - error = smbfs_fullpath(mbp, SSTOVC(ssp), np, name, nmlen, - xattr ? ':' : '\\'); - if (!error) { - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - } - smb_rq_done(rqp); return (error); } int -smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp, - const char *tname, int tnmlen, struct smb_cred *scrp) +smbfs_smb_rename(struct smbnode *sdnp, struct smbnode *np, + struct smbnode *tdnp, const char *tname, int tnlen, + smb_fh_t *fhp, struct smb_cred *scrp) { - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = src->n_mount->smi_share; - struct mbchain *mbp; - int error; - uint16_t fa; - char sep; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - /* freebsd bug: Let directories be renamed - Win98 requires DIR bit */ - fa = (SMBTOV(src)->v_type == VDIR) ? SMB_FA_DIR : 0; - fa |= SMB_FA_SYSTEM | SMB_FA_HIDDEN; - mb_put_uint16le(mbp, fa); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); + struct smb_share *ssp = np->n_mount->smi_share; + struct smb_vc *vcp = SSTOVC(ssp); + int err; - /* - * When we're not adding any component name, the - * passed sep is ignored, so just pass sep=0. - */ - mb_put_uint8(mbp, SMB_DT_ASCII); - error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0, 0); - if (error) - goto out; + if (vcp->vc_flags & SMBV_SMB2) { + err = smbfs_smb2_rename(np, tdnp, tname, tnlen, 0, + &fhp->fh_fid2, scrp); + return (err); + } /* - * After XATTR directories, separator is ":" + * SMB1 -- Want to use _t2rename if we can + * (rename in same dir and cap pass-through) + * Most SMB1 servers have cap pass-through. */ - sep = (src->n_flag & N_XATTR) ? ':' : '\\'; - mb_put_uint8(mbp, SMB_DT_ASCII); - error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen, sep); - if (error) - goto out; - - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); -out: - smb_rq_done(rqp); - return (error); -} - -int -smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp, - const char *tname, int tnmlen, uint16_t flags, struct smb_cred *scrp) -{ - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = src->n_mount->smi_share; - struct mbchain *mbp; - int error; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_MOVE, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, SMB_TID_UNKNOWN); - mb_put_uint16le(mbp, 0x20); /* delete target file */ - mb_put_uint16le(mbp, flags); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - - error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0, '\\'); - if (error) - goto out; - mb_put_uint8(mbp, SMB_DT_ASCII); - error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen, '\\'); - if (error) - goto out; - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - -out: - smb_rq_done(rqp); - return (error); -} - -static int -smbfs_smb_oldmkdir(struct smbnode *dnp, const char *name, int len, - struct smb_cred *scrp) -{ - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = dnp->n_mount->smi_share; - struct mbchain *mbp; - int error; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, len, '\\'); - if (!error) { - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); + if (sdnp == tdnp && + (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) != 0) { + err = smbfs_smb1_t2rename(np, tname, tnlen, fhp->fh_fid1, scrp); + } else { + err = smbfs_smb1_oldrename(np, tdnp, tname, tnlen, scrp); } - smb_rq_done(rqp); - return (error); + + return (err); } int smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int nmlen, struct smb_cred *scrp) { + smb_fh_t tmp_fh; struct smb_share *ssp = dnp->n_mount->smi_share; - struct smb_vc *vcp = SSTOVC(ssp); - uint32_t rights; - uint16_t fid; + uint32_t efa, rights; int error; /* + * Using a faked-up handle here to avoid the work of + * creating and destroying a real "conn obj". + */ + bzero(&tmp_fh, sizeof (tmp_fh)); + + /* * We ask for SA_RIGHT_FILE_READ_DATA not because we need it, but * just to be asking for something. The rights==0 case could * easily be broken on some old or unusual servers. */ - if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { - rights = SA_RIGHT_FILE_READ_DATA; - error = smbfs_smb_ntcreatex(dnp, - name, nmlen, 0, /* xattr */ - rights, SMB_EFA_DIRECTORY, - NTCREATEX_SHARE_ACCESS_ALL, - NTCREATEX_DISP_CREATE, - NTCREATEX_OPTIONS_DIRECTORY, - scrp, &fid, NULL, NULL); /* cr_act_p fa_p */ - if (error) - return (error); - (void) smbfs_smb_close(ssp, fid, NULL, scrp); - return (0); + rights = SA_RIGHT_FILE_READ_DATA; + efa = SMB_EFA_NORMAL; + if (name && *name == '.') + efa |= SMB_EFA_HIDDEN; + error = smbfs_smb_ntcreatex(dnp, + name, nmlen, 0, /* xattr */ + rights, SMB_EFA_DIRECTORY, + NTCREATEX_SHARE_ACCESS_ALL, + NTCREATEX_DISP_CREATE, + NTCREATEX_OPTIONS_DIRECTORY, + scrp, &tmp_fh, NULL, NULL); + if (error == 0) { + (void) smb_smb_close(ssp, &tmp_fh, scrp); } - error = smbfs_smb_oldmkdir(dnp, name, nmlen, scrp); - return (error); -} - -int -smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scrp) -{ - struct smb_rq rq, *rqp = &rq; - struct smb_share *ssp = np->n_mount->smi_share; - struct mbchain *mbp; - int error; - - error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scrp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0, '\\'); - if (!error) { - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - } - smb_rq_done(rqp); return (error); } -static int -smbfs_smb_search(struct smbfs_fctx *ctx) -{ - struct smb_vc *vcp = SSTOVC(ctx->f_ssp); - struct smb_rq *rqp; - struct mbchain *mbp; - struct mdchain *mdp; - uint8_t wc, bt; - uint16_t ec, dlen, bc; - int maxent, error, iseof = 0; - - maxent = min(ctx->f_left, - (vcp->vc_txmax - SMB_HDRLEN - 2*2) / SMB_DENTRYLEN); - if (ctx->f_rq) { - smb_rq_done(ctx->f_rq); - ctx->f_rq = NULL; - } - error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, - ctx->f_scred, &rqp); - if (error) - return (error); - ctx->f_rq = rqp; - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, maxent); /* max entries to return */ - mb_put_uint16le(mbp, ctx->f_attrmask); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); /* buffer format */ - if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { - error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, - ctx->f_wildcard, ctx->f_wclen, '\\'); - if (error) - return (error); - mb_put_uint8(mbp, SMB_DT_VARIABLE); - mb_put_uint16le(mbp, 0); /* context length */ - ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; - } else { - if (SMB_UNICODE_STRINGS(vcp)) { - mb_put_padbyte(mbp); - mb_put_uint8(mbp, 0); - } - mb_put_uint8(mbp, 0); - mb_put_uint8(mbp, SMB_DT_VARIABLE); - mb_put_uint16le(mbp, SMB_SKEYLEN); - mb_put_mem(mbp, (char *)ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM); - } - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); - if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnofiles) { - error = 0; - iseof = 1; - ctx->f_flags |= SMBFS_RDD_EOF; - } else if (error) - return (error); - smb_rq_getreply(rqp, &mdp); - error = md_get_uint8(mdp, &wc); - if (error) - return (error); - if (wc != 1) - return (iseof ? ENOENT : EBADRPC); - md_get_uint16le(mdp, &ec); - md_get_uint16le(mdp, &bc); - md_get_uint8(mdp, &bt); - error = md_get_uint16le(mdp, &dlen); - if (error) - return (error); - if (ec == 0) - return (ENOENT); - ctx->f_ecnt = ec; - if (bc < 3) - return (EBADRPC); - bc -= 3; - if (bt != SMB_DT_VARIABLE) - return (EBADRPC); - if (dlen != bc || dlen % SMB_DENTRYLEN != 0) - return (EBADRPC); - return (0); -} - - -/*ARGSUSED*/ -static int -smbfs_smb_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp, - const char *wildcard, int wclen, uint16_t attr) -{ - - ctx->f_type = ft_LM1; - ctx->f_attrmask = attr; - if (wildcard) { - if (wclen == 1 && wildcard[0] == '*') { - ctx->f_wildcard = "*.*"; - ctx->f_wclen = 3; - } else { - ctx->f_wildcard = wildcard; - ctx->f_wclen = wclen; - } - } else { - ctx->f_wildcard = NULL; - ctx->f_wclen = 0; - } - ctx->f_name = (char *)ctx->f_fname; - ctx->f_namesz = 0; - return (0); -} - -static int -smbfs_smb_findnextLM1(struct smbfs_fctx *ctx, uint16_t limit) -{ - struct mdchain *mdp; - struct smb_rq *rqp; - char *cp; - uint8_t battr; - uint16_t date, time; - uint32_t size; - int error; - struct timespec ts; - - if (ctx->f_ecnt == 0) { - if (ctx->f_flags & SMBFS_RDD_EOF) - return (ENOENT); - ctx->f_left = ctx->f_limit = limit; - gethrestime(&ts); - error = smbfs_smb_search(ctx); - if (error) - return (error); - } - rqp = ctx->f_rq; - smb_rq_getreply(rqp, &mdp); - md_get_mem(mdp, (char *)ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM); - md_get_uint8(mdp, &battr); - md_get_uint16le(mdp, &time); - md_get_uint16le(mdp, &date); - md_get_uint32le(mdp, &size); - cp = ctx->f_name; - error = md_get_mem(mdp, cp, sizeof (ctx->f_fname), MB_MSYSTEM); - cp[sizeof (ctx->f_fname) - 1] = 0; - cp += strlen(cp) - 1; - while (*cp == ' ' && cp >= ctx->f_name) - *cp-- = 0; - ctx->f_attr.fa_attr = battr; - smb_dos2unixtime(date, time, 0, rqp->sr_vc->vc_sopt.sv_tz, - &ctx->f_attr.fa_mtime); - ctx->f_attr.fa_size = size; - ctx->f_nmlen = strlen(ctx->f_name); - ctx->f_ecnt--; - ctx->f_left--; - return (0); -} - -static int -smbfs_smb_findcloseLM1(struct smbfs_fctx *ctx) -{ - if (ctx->f_rq) - smb_rq_done(ctx->f_rq); - return (0); -} - /* - * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect + * Protocol-level directory open */ -static int -smbfs_smb_trans2find2(struct smbfs_fctx *ctx) -{ - struct smb_t2rq *t2p; - struct smb_vc *vcp = SSTOVC(ctx->f_ssp); - struct mbchain *mbp; - struct mdchain *mdp; - uint16_t ecnt, eos, lno, flags; - int error; - - if (ctx->f_t2) { - smb_t2_done(ctx->f_t2); - ctx->f_t2 = NULL; - } - flags = FIND2_RETURN_RESUME_KEYS | FIND2_CLOSE_ON_EOS; - if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) { - flags |= FIND2_CLOSE_AFTER_REQUEST; - ctx->f_flags |= SMBFS_RDD_NOCLOSE; - } - if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { - error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2, - ctx->f_scred, &t2p); - if (error) - return (error); - ctx->f_t2 = t2p; - mbp = &t2p->t2_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, ctx->f_attrmask); - mb_put_uint16le(mbp, ctx->f_limit); - mb_put_uint16le(mbp, flags); - mb_put_uint16le(mbp, ctx->f_infolevel); - mb_put_uint32le(mbp, 0); - error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, - ctx->f_wildcard, ctx->f_wclen, '\\'); - if (error) - return (error); - } else { - error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2, - ctx->f_scred, &t2p); - if (error) - return (error); - ctx->f_t2 = t2p; - mbp = &t2p->t2_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, ctx->f_Sid); - mb_put_uint16le(mbp, ctx->f_limit); - mb_put_uint16le(mbp, ctx->f_infolevel); - /* Send whatever resume key we received... */ - mb_put_uint32le(mbp, ctx->f_rkey); - mb_put_uint16le(mbp, flags); - /* ... and the resume name if we have one. */ - if (ctx->f_rname) { - /* resume file name */ - mb_put_mem(mbp, ctx->f_rname, ctx->f_rnamelen, - MB_MSYSTEM); - } - /* Add trailing null - 1 byte if ASCII, 2 if Unicode */ - if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) - mb_put_uint8(mbp, 0); /* 1st byte NULL Unicode char */ - mb_put_uint8(mbp, 0); - } - t2p->t2_maxpcount = 5 * 2; - t2p->t2_maxdcount = 0xF000; /* 64K less some overhead */ - error = smb_t2_request(t2p); - if (error) - return (error); - - /* - * This is the "resume name" we just sent. - * We want the new one (if any) that may be - * found in the response we just received and - * will now begin parsing. Free the old one - * now so we'll know if we found a new one. - */ - if (ctx->f_rname) { - kmem_free(ctx->f_rname, ctx->f_rnamelen); - ctx->f_rname = NULL; - ctx->f_rnamelen = 0; - } - - mdp = &t2p->t2_rparam; - if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { - if ((error = md_get_uint16le(mdp, &ctx->f_Sid)) != 0) - goto nodata; - ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; - } - md_get_uint16le(mdp, &ecnt); /* entry count */ - md_get_uint16le(mdp, &eos); /* end of search */ - md_get_uint16le(mdp, NULL); /* EA err. off. */ - error = md_get_uint16le(mdp, &lno); /* last name off. */ - if (error != 0) - goto nodata; - - /* - * The "end of search" flag from an XP server sometimes - * comes back zero when the prior find_next returned exactly - * the number of entries requested. in which case we'd try again - * but the search has in fact been closed so an EBADF results. - * our circumvention is to check here for a zero entry count. - */ - ctx->f_ecnt = ecnt; - if (eos || ctx->f_ecnt == 0) - ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE; - if (ctx->f_ecnt == 0) - return (ENOENT); - - /* Last Name Off (LNO) is the entry with the resume name. */ - ctx->f_rnameofs = lno; - ctx->f_eofs = 0; - return (0); - -nodata: - /* - * Failed parsing the FindFirst or FindNext response. - * Force this directory listing closed, otherwise the - * calling process may hang in an infinite loop. - */ - ctx->f_ecnt = 0; /* Force closed. */ - ctx->f_flags |= SMBFS_RDD_EOF; - return (EIO); -} - -static int -smbfs_smb_findclose2(struct smbfs_fctx *ctx) -{ - struct smb_rq rq, *rqp = &rq; - struct mbchain *mbp; - int error; - - error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, - ctx->f_scred); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - mb_put_uint16le(mbp, ctx->f_Sid); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - smb_rq_bend(rqp); - /* Ditto comments at _smb_close */ - rqp->sr_flags |= SMBR_NOINTR_SEND; - error = smb_rq_simple(rqp); - smb_rq_done(rqp); - return (error); -} - -/*ARGSUSED*/ -static int -smbfs_smb_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp, - const char *wildcard, int wclen, uint16_t attr) -{ - - ctx->f_type = ft_LM2; - ctx->f_namesz = SMB_MAXFNAMELEN + 1; - if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) - ctx->f_namesz *= 2; - ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP); - ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) - < SMB_DIALECT_NTLM0_12 ? SMB_FIND_STANDARD : - SMB_FIND_BOTH_DIRECTORY_INFO; - ctx->f_attrmask = attr; - ctx->f_wildcard = wildcard; - ctx->f_wclen = wclen; - return (0); -} - -static int -smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit) -{ - struct mdchain *mdp; - struct smb_t2rq *t2p; - char *cp; - uint8_t tb; - uint16_t date, time, wattr; - uint32_t size, next, dattr, resumekey = 0; - uint64_t llongint; - int error, svtz, cnt, fxsz, nmlen, recsz; - struct timespec ts; - - if (ctx->f_ecnt == 0) { - if (ctx->f_flags & SMBFS_RDD_EOF) - return (ENOENT); - ctx->f_left = ctx->f_limit = limit; - gethrestime(&ts); - error = smbfs_smb_trans2find2(ctx); - if (error) - return (error); - ctx->f_otws++; - } - t2p = ctx->f_t2; - mdp = &t2p->t2_rdata; - svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz; - switch (ctx->f_infolevel) { - case SMB_FIND_STANDARD: - next = 0; - fxsz = 0; - md_get_uint16le(mdp, &date); - md_get_uint16le(mdp, &time); /* creation time */ - smb_dos2unixtime(date, time, 0, svtz, - &ctx->f_attr.fa_createtime); - md_get_uint16le(mdp, &date); - md_get_uint16le(mdp, &time); /* access time */ - smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime); - md_get_uint16le(mdp, &date); - md_get_uint16le(mdp, &time); /* modify time */ - smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime); - md_get_uint32le(mdp, &size); - ctx->f_attr.fa_size = size; - md_get_uint32le(mdp, &size); /* allocation size */ - ctx->f_attr.fa_allocsz = size; - md_get_uint16le(mdp, &wattr); - ctx->f_attr.fa_attr = wattr; - error = md_get_uint8(mdp, &tb); - if (error) - goto nodata; - size = nmlen = tb; - fxsz = 23; - recsz = next = 24 + nmlen; /* docs misses zero byte @end */ - break; - case SMB_FIND_DIRECTORY_INFO: - case SMB_FIND_BOTH_DIRECTORY_INFO: - md_get_uint32le(mdp, &next); - md_get_uint32le(mdp, &resumekey); /* file index (resume key) */ - md_get_uint64le(mdp, &llongint); /* creation time */ - smb_time_NT2local(llongint, &ctx->f_attr.fa_createtime); - md_get_uint64le(mdp, &llongint); - smb_time_NT2local(llongint, &ctx->f_attr.fa_atime); - md_get_uint64le(mdp, &llongint); - smb_time_NT2local(llongint, &ctx->f_attr.fa_mtime); - md_get_uint64le(mdp, &llongint); - smb_time_NT2local(llongint, &ctx->f_attr.fa_ctime); - md_get_uint64le(mdp, &llongint); /* file size */ - ctx->f_attr.fa_size = llongint; - md_get_uint64le(mdp, &llongint); /* alloc. size */ - ctx->f_attr.fa_allocsz = llongint; - md_get_uint32le(mdp, &dattr); /* ext. file attributes */ - ctx->f_attr.fa_attr = dattr; - error = md_get_uint32le(mdp, &size); /* name len */ - if (error) - goto nodata; - fxsz = 64; /* size ofinfo up to filename */ - if (ctx->f_infolevel == SMB_FIND_BOTH_DIRECTORY_INFO) { - /* - * Skip EaSize(4 bytes), a byte of ShortNameLength, - * a reserved byte, and ShortName(8.3 means 24 bytes, - * as Leach defined it to always be Unicode) - */ - error = md_get_mem(mdp, NULL, 30, MB_MSYSTEM); - if (error) - goto nodata; - fxsz += 30; - } - recsz = next ? next : fxsz + size; - break; - default: - SMBVDEBUG("unexpected info level %d\n", ctx->f_infolevel); - return (EINVAL); - } - - if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) - nmlen = min(size, SMB_MAXFNAMELEN * 2); - else - nmlen = min(size, SMB_MAXFNAMELEN); - - /* Allocated f_name in findopen */ - ASSERT(nmlen < ctx->f_namesz); - cp = ctx->f_name; - - error = md_get_mem(mdp, cp, nmlen, MB_MSYSTEM); - if (error) - goto nodata; - if (next) { - /* How much data to skip? */ - cnt = next - nmlen - fxsz; - if (cnt < 0) { - SMBVDEBUG("out of sync\n"); - goto nodata; - } - if (cnt > 0) - md_get_mem(mdp, NULL, cnt, MB_MSYSTEM); - } - /* Don't count any trailing null in the name. */ - if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { - if (nmlen > 1 && cp[nmlen - 1] == 0 && cp[nmlen - 2] == 0) - nmlen -= 2; - } else { - if (nmlen && cp[nmlen - 1] == 0) - nmlen--; - } - if (nmlen == 0) - goto nodata; - - /* - * On a find-next we expect that the server will: - * 1) if the continue bit is set, use the server's offset, - * 2) else if the resume key is non-zero, use that offset, - * 3) else if the resume name is set, use that offset, - * 4) else use the server's idea of current offset. - * - * We always set the resume key flag. If the server returns - * a resume key then we should always send it back to them. - */ - ctx->f_rkey = resumekey; - - next = ctx->f_eofs + recsz; - if (ctx->f_rnameofs && - ctx->f_rnameofs >= ctx->f_eofs && - ctx->f_rnameofs < (int)next) { - /* - * This entry is the "resume name". - * Save it for the next request. - */ - if (ctx->f_rnamelen != nmlen) { - if (ctx->f_rname) - kmem_free(ctx->f_rname, ctx->f_rnamelen); - ctx->f_rname = kmem_alloc(nmlen, KM_SLEEP); - ctx->f_rnamelen = nmlen; - } - bcopy(ctx->f_name, ctx->f_rname, nmlen); - } - ctx->f_nmlen = nmlen; - ctx->f_eofs = next; - ctx->f_ecnt--; - ctx->f_left--; - - smbfs_fname_tolocal(ctx); - return (0); - -nodata: - /* - * Something bad has happened and we ran out of data - * before we could parse all f_ecnt entries expected. - * Force this directory listing closed, otherwise the - * calling process may hang in an infinite loop. - */ - SMBVDEBUG("ran out of data\n"); - ctx->f_ecnt = 0; /* Force closed. */ - ctx->f_flags |= SMBFS_RDD_EOF; - return (EIO); -} - -static int -smbfs_smb_findcloseLM2(struct smbfs_fctx *ctx) -{ - int error = 0; - if (ctx->f_name) - kmem_free(ctx->f_name, ctx->f_namesz); - if (ctx->f_t2) - smb_t2_done(ctx->f_t2); - /* - * If SMBFS_RDD_FINDFIRST is still set, we were opened - * but never saw a findfirst, so we don't have any - * search handle to close. - */ - if ((ctx->f_flags & (SMBFS_RDD_FINDFIRST | SMBFS_RDD_NOCLOSE)) == 0) - error = smbfs_smb_findclose2(ctx); - return (error); -} - int smbfs_smb_findopen(struct smbnode *dnp, const char *wild, int wlen, int attr, struct smb_cred *scrp, struct smbfs_fctx **ctxpp) { + struct smb_share *ssp = dnp->n_mount->smi_share; + struct smb_vc *vcp = SSTOVC(ssp); struct smbfs_fctx *ctx; int error; @@ -2287,50 +586,56 @@ smbfs_smb_findopen(struct smbnode *dnp, const char *wild, int wlen, ctx->f_flags = SMBFS_RDD_FINDFIRST; ctx->f_dnp = dnp; ctx->f_scred = scrp; - ctx->f_ssp = dnp->n_mount->smi_share; + ctx->f_ssp = ssp; if (dnp->n_flag & N_XATTR) { error = smbfs_xa_findopen(ctx, dnp, wild, wlen); goto out; } - if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0) { - error = smbfs_smb_findopenLM1(ctx, dnp, wild, wlen, attr); + if (vcp->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_findopen(ctx, dnp, wild, wlen, attr); } else { error = smbfs_smb_findopenLM2(ctx, dnp, wild, wlen, attr); } out: - if (error) - (void) smbfs_smb_findclose(ctx, scrp); - else + ctx->f_scred = NULL; + if (error) { + kmem_free(ctx, sizeof (*ctx)); + } else { *ctxpp = ctx; + } + return (error); } int smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scrp) { - int error; + int error = 0; + uint16_t lim; /* * Note: "limit" (maxcount) needs to fit in a short! */ if (limit > 0xffff) limit = 0xffff; + lim = (uint16_t)limit; ctx->f_scred = scrp; for (;;) { bzero(&ctx->f_attr, sizeof (ctx->f_attr)); switch (ctx->f_type) { - case ft_LM1: - error = smbfs_smb_findnextLM1(ctx, (uint16_t)limit); + + case ft_SMB2: + error = smbfs_smb2_findnext(ctx, lim); break; case ft_LM2: - error = smbfs_smb_findnextLM2(ctx, (uint16_t)limit); + error = smbfs_smb_findnextLM2(ctx, lim); break; case ft_XA: - error = smbfs_xa_findnext(ctx, (uint16_t)limit); + error = smbfs_xa_findnext(ctx, lim); break; default: ASSERT(0); @@ -2338,7 +643,7 @@ smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scrp) break; } if (error) - return (error); + break; /* * Skip "." or ".." - easy now that ctx->f_name * has already been converted to utf-8 format. @@ -2349,14 +654,18 @@ smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scrp) continue; break; } + ctx->f_scred = NULL; + if (error != 0) + return (error); - /* - * Moved the smbfs_fname_tolocal(ctx) call into - * the ..._findnext functions above. - */ + ctx->f_inum = smbfs_getino(ctx->f_dnp, + ctx->f_name, ctx->f_nmlen); - ctx->f_inum = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen); - return (0); +#ifdef DEBUG + SMBVDEBUG("findnext: (%s)\n", ctx->f_name); +#endif + + return (error); } @@ -2367,8 +676,8 @@ smbfs_smb_findclose(struct smbfs_fctx *ctx, struct smb_cred *scrp) ctx->f_scred = scrp; switch (ctx->f_type) { - case ft_LM1: - error = smbfs_smb_findcloseLM1(ctx); + case ft_SMB2: + error = smbfs_smb2_findclose(ctx); break; case ft_LM2: error = smbfs_smb_findcloseLM2(ctx); @@ -2376,7 +685,11 @@ smbfs_smb_findclose(struct smbfs_fctx *ctx, struct smb_cred *scrp) case ft_XA: error = smbfs_xa_findclose(ctx); break; + default: + error = ENOSYS; + break; } + ctx->f_scred = NULL; if (ctx->f_rname) kmem_free(ctx->f_rname, ctx->f_rnamelen); if (ctx->f_firstnm) @@ -2416,25 +729,12 @@ smbfs_smb_lookup(struct smbnode *dnp, const char **namep, int *nmlenp, } /* - * XXX: Should use _qpathinfo here instead. - * (if SMB_CAP_NT_SMBS) - */ - - /* * Shared lock for n_fid use (smb_flush). */ intr = dnp->n_mount->smi_flags & SMI_INT; if (smbfs_rw_enter_sig(&dnp->r_lkserlock, RW_READER, intr)) return (EINTR); - /* - * This hides a server bug observable in Win98: - * size changes may not show until a CLOSE or a FLUSH op - * XXX: Make this conditional on !NTSMBs - */ - error = smbfs_smb_flush(dnp, scrp); - if (error) - goto out; error = smbfs_smb_findopen(dnp, name, nmlen, SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scrp, &ctx); if (error) @@ -2468,148 +768,49 @@ out: * which the caller should free. */ int -smbfs_smb_getsec_m(struct smb_share *ssp, uint16_t fid, - struct smb_cred *scrp, uint32_t selector, - mblk_t **res, uint32_t *reslen) +smbfs_smb_getsec(struct smb_share *ssp, smb_fh_t *fhp, + uint32_t selector, mblk_t **res, uint32_t *reslen, + struct smb_cred *scrp) { - struct smb_ntrq *ntp; - struct mbchain *mbp; - struct mdchain *mdp; + struct smb_vc *vcp = SSTOVC(ssp); int error, len; - error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_QUERY_SECURITY_DESC, - scrp, &ntp); - if (error) - return (error); - - /* Parameters part */ - mbp = &ntp->nt_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, fid); - mb_put_uint16le(mbp, 0); /* reserved */ - mb_put_uint32le(mbp, selector); - /* Data part (none) */ - - /* Max. returned parameters and data. */ - ntp->nt_maxpcount = 4; - ntp->nt_maxdcount = *reslen; - - error = smb_nt_request(ntp); - if (error && !(ntp->nt_flags & SMBT2_MOREDATA)) - goto done; *res = NULL; - /* - * if there's more data than we said we could receive, here - * is where we pick up the length of it - */ - mdp = &ntp->nt_rparam; - md_get_uint32le(mdp, reslen); - if (error) - goto done; + if (vcp->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_getsec(ssp, &fhp->fh_fid2, + selector, res, reslen, scrp); + } else { + error = smbfs_smb1_getsec(ssp, fhp->fh_fid1, + selector, res, reslen, scrp); + } /* * get the data part. */ - mdp = &ntp->nt_rdata; - if (mdp->md_top == NULL) { - SMBVDEBUG("null md_top? fid 0x%x\n", fid); + if (*res == NULL) { error = EBADRPC; goto done; } /* - * The returned parameter SD_length should match - * the length of the returned data. Unfortunately, - * we have to work around server bugs here. - */ - len = m_fixhdr(mdp->md_top); - if (len != *reslen) { - SMBVDEBUG("len %d *reslen %d fid 0x%x\n", - len, *reslen, fid); - } - - /* - * Actual data provided is < returned SD_length. - * - * The following "if (len < *reslen)" handles a Windows bug - * observed when the underlying filesystem is FAT32. In that - * case a 32 byte security descriptor comes back (S-1-1-0, ie - * "Everyone") but the Parameter Block claims 44 is the length - * of the security descriptor. (The Data Block length - * claimed is 32. This server bug was reported against NT - * first and I've personally observed it with W2K. + * If message length is < returned SD_length, + * correct *reslen (reduce it). It greater, + * just ignore the extra data. */ + len = m_fixhdr(*res); if (len < *reslen) *reslen = len; - /* - * Actual data provided is > returned SD_length. - * (Seen on StorageTek NAS 5320, s/w ver. 4.21 M0) - * Narrow work-around for returned SD_length==0. - */ - if (len > *reslen) { - /* - * Increase *reslen, but carefully. - */ - if (*reslen == 0 && len <= ntp->nt_maxdcount) - *reslen = len; - } - error = md_get_mbuf(mdp, len, res); - done: if (error == 0 && *res == NULL) { ASSERT(*res); error = EBADRPC; } - smb_nt_done(ntp); return (error); } -#ifdef APPLE -/* - * Wrapper for _getsd() compatible with darwin code. - */ -int -smbfs_smb_getsec(struct smb_share *ssp, uint16_t fid, struct smb_cred *scrp, - uint32_t selector, struct ntsecdesc **res) -{ - int error; - uint32_t len, olen; - struct mdchain *mdp, md_store; - struct mbuf *m; - - bzero(mdp, sizeof (*mdp)); - len = 500; /* "overlarge" values => server errors */ -again: - olen = len; - error = smbfs_smb_getsec_m(ssp, fid, scrp, selector, &m, &len); - /* - * Server may give us an error indicating that we - * need a larger data buffer to receive the SD, - * and the size we'll need. Use the given size, - * but only after a sanity check. - * - * XXX: Check for specific error values here? - * XXX: also ... && len <= MAX_RAW_SD_SIZE - */ - if (error && len > olen) - goto again; - - if (error) - return (error); - - mdp = &md_store; - md_initm(mdp, m); - MALLOC(*res, struct ntsecdesc *, len, M_TEMP, M_WAITOK); - error = md_get_mem(mdp, (caddr_t)*res, len, MB_MSYSTEM); - md_done(mdp); - - return (error); -} -#endif /* APPLE */ - /* * OTW function to Set a security descriptor (SD). * Caller data are carried in an mbchain_t. @@ -2617,108 +818,21 @@ again: * Note: This normally consumes mbp->mb_top, and clears * that pointer when it does. */ -int smbfs_smb_setsec_m(struct smb_share *ssp, uint16_t fid, - struct smb_cred *scrp, uint32_t selector, mblk_t **mp) -{ - struct smb_ntrq *ntp; - struct mbchain *mbp; - int error; - - error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_SET_SECURITY_DESC, - scrp, &ntp); - if (error) - return (error); - - /* Parameters part */ - mbp = &ntp->nt_tparam; - mb_init(mbp); - mb_put_uint16le(mbp, fid); - mb_put_uint16le(mbp, 0); /* reserved */ - mb_put_uint32le(mbp, selector); - - /* Data part */ - mbp = &ntp->nt_tdata; - mb_initm(mbp, *mp); - *mp = NULL; /* consumed */ - - /* No returned parameters or data. */ - ntp->nt_maxpcount = 0; - ntp->nt_maxdcount = 0; - - error = smb_nt_request(ntp); - smb_nt_done(ntp); - - return (error); -} - -#ifdef APPLE -/* - * This function builds the SD given the various parts. - */ int -smbfs_smb_setsec(struct smb_share *ssp, uint16_t fid, struct smb_cred *scrp, - uint32_t selector, uint16_t flags, struct ntsid *owner, - struct ntsid *group, struct ntacl *sacl, struct ntacl *dacl) +smbfs_smb_setsec(struct smb_share *ssp, smb_fh_t *fhp, + uint32_t selector, mblk_t **mp, + struct smb_cred *scrp) { - struct mbchain *mbp, mb_store; - struct ntsecdesc ntsd; - int error, off; + struct smb_vc *vcp = SSTOVC(ssp); + int error; - /* - * Build the SD as its own mbuf chain and pass it to - * smbfs_smb_setsec_m() - */ - mbp = &mb_store; - mb_init(mbp); - bzero(&ntsd, sizeof (ntsd)); - wset_sdrevision(&ntsd); - /* - * A note about flags ("SECURITY_DESCRIPTOR_CONTROL" in MSDN) - * We set here only those bits we can be sure must be set. The rest - * are up to the caller. In particular, the caller may intentionally - * set an acl PRESENT bit while giving us a null pointer for the - * acl - that sets a null acl, giving access to everyone. Note also - * that the AUTO_INHERITED bits should probably always be set unless - * the server is NT. - */ - flags |= SD_SELF_RELATIVE; - off = sizeof (ntsd); - if (owner) { - wset_sdowneroff(&ntsd, off); - off += sidlen(owner); - } - if (group) { - wset_sdgroupoff(&ntsd, off); - off += sidlen(group); - } - if (sacl) { - flags |= SD_SACL_PRESENT; - wset_sdsacloff(&ntsd, off); - off += acllen(sacl); - } - if (dacl) { - flags |= SD_DACL_PRESENT; - wset_sddacloff(&ntsd, off); + if (vcp->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_setsec(ssp, &fhp->fh_fid2, + selector, mp, scrp); + } else { + error = smbfs_smb1_setsec(ssp, fhp->fh_fid1, + selector, mp, scrp); } - wset_sdflags(&ntsd, flags); - mb_put_mem(mbp, (caddr_t)&ntsd, sizeof (ntsd), MB_MSYSTEM); - if (owner) - mb_put_mem(mbp, (caddr_t)owner, sidlen(owner), MB_MSYSTEM); - if (group) - mb_put_mem(mbp, (caddr_t)group, sidlen(group), MB_MSYSTEM); - if (sacl) - mb_put_mem(mbp, (caddr_t)sacl, acllen(sacl), MB_MSYSTEM); - if (dacl) - mb_put_mem(mbp, (caddr_t)dacl, acllen(dacl), MB_MSYSTEM); - - /* - * Just pass the mbuf to _setsec_m - * It will clear mb_top if consumed. - */ - error = smbfs_smb_setsec_m(ssp, fid, scrp, selector, &mbp->mb_top); - mb_done(mbp); return (error); } - -#endif /* APPLE */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb1.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb1.c new file mode 100644 index 0000000000..6c00816133 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb1.c @@ -0,0 +1,926 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/inttypes.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/sunddi.h> +#include <sys/cmn_err.h> + +#include <netsmb/smb_osdep.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_rq.h> + +#include <smbfs/smbfs.h> +#include <smbfs/smbfs_node.h> +#include <smbfs/smbfs_subr.h> + + +/* + * Todo: locking over-the-wire + */ +#if 0 // todo + +int +smbfs_smb1_lockandx(struct smbnode *np, int op, uint32_t pid, + offset_t start, uint64_t len, int largelock, + struct smb_cred *scrp, uint32_t timeout) +{ + struct smb_share *ssp = np->n_mount->smi_share; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + uint8_t ltype = 0; + int error; + + /* Shared lock for n_fid use below. */ + ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + + /* After reconnect, n_fid is invalid */ + if (np->n_vcgenid != ssp->ss_vcgenid) + return (ESTALE); + + if (op == SMB_LOCK_SHARED) + ltype |= SMB_LOCKING_ANDX_SHARED_LOCK; + /* XXX: if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES)? */ + if (largelock) + ltype |= SMB_LOCKING_ANDX_LARGE_FILES; + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scrp); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); /* secondary command */ + mb_put_uint8(mbp, 0); /* MBZ */ + mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, np->n_fid); + mb_put_uint8(mbp, ltype); /* locktype */ + mb_put_uint8(mbp, 0); /* oplocklevel - 0 seems is NO_OPLOCK */ + mb_put_uint32le(mbp, timeout); /* 0 nowait, -1 infinite wait */ + mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0); + mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint16le(mbp, pid); + if (!largelock) { + mb_put_uint32le(mbp, start); + mb_put_uint32le(mbp, len); + } else { + mb_put_uint16le(mbp, 0); /* pad */ + mb_put_uint32le(mbp, start >> 32); /* OffsetHigh */ + mb_put_uint32le(mbp, start & 0xffffffff); /* OffsetLow */ + mb_put_uint32le(mbp, len >> 32); /* LengthHigh */ + mb_put_uint32le(mbp, len & 0xffffffff); /* LengthLow */ + } + smb_rq_bend(rqp); + /* + * Don't want to risk missing a successful + * unlock send or lock response, or we could + * lose track of an outstanding lock. + */ + if (op == SMB_LOCK_RELEASE) + rqp->sr_flags |= SMBR_NOINTR_SEND; + else + rqp->sr_flags |= SMBR_NOINTR_RECV; + + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return (error); +} + +#endif // todo + +/* + * Common function for QueryFileInfo, QueryPathInfo. + */ +int +smbfs_smb1_trans2_query(struct smbnode *np, uint16_t fid, + struct smbfattr *fap, struct smb_cred *scrp) +{ + struct smb_share *ssp = np->n_mount->smi_share; + struct smb_vc *vcp = SSTOVC(ssp); + struct smb_t2rq *t2p; + struct mbchain *mbp; + struct mdchain *mdp; + uint16_t cmd; + uint16_t infolevel = SMB_QFILEINFO_ALL_INFO; + int error; + + /* + * If we have a valid open FID, use it. + */ + if (fid != SMB_FID_UNUSED) + cmd = SMB_TRANS2_QUERY_FILE_INFORMATION; + else + cmd = SMB_TRANS2_QUERY_PATH_INFORMATION; + + error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p); + if (error) + return (error); + mbp = &t2p->t2_tparam; + mb_init(mbp); + + if (cmd == SMB_TRANS2_QUERY_FILE_INFORMATION) + mb_put_uint16le(mbp, fid); + + mb_put_uint16le(mbp, infolevel); + + if (cmd == SMB_TRANS2_QUERY_PATH_INFORMATION) { + mb_put_uint32le(mbp, 0); + /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */ + error = smbfs_fullpath(mbp, vcp, np, NULL, 0, '\\'); + if (error) + goto out; + } + + t2p->t2_maxpcount = 2; + t2p->t2_maxdcount = vcp->vc_txmax; + error = smb_t2_request(t2p); + if (error) + goto out; + + /* + * Parse the SMB_QFILEINFO_ALL_INFO + */ + mdp = &t2p->t2_rdata; + error = smbfs_decode_file_all_info(ssp, mdp, fap); + +out: + smb_t2_done(t2p); + + return (error); +} + +/* + * Get some FS information + */ +static int +smbfs_smb1_query_fs_info(struct smb_share *ssp, struct mdchain *info_mdp, + uint16_t level, struct smb_cred *scrp) +{ + struct smb_t2rq *t2p; + struct mbchain *mbp; + struct mdchain *mdp; + int error; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION, + scrp, &t2p); + if (error) + return (error); + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, level); + t2p->t2_maxpcount = 4; + t2p->t2_maxdcount = 1024; + error = smb_t2_request(t2p); + if (error) + goto out; + + mdp = &t2p->t2_rdata; + *info_mdp = *mdp; + bzero(mdp, sizeof (*mdp)); + +out: + smb_t2_done(t2p); + return (error); +} + +/* + * Get FILE_FS_ATTRIBUTE_INFORMATION + */ +int +smbfs_smb1_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *fsa, + struct smb_cred *scrp) +{ + struct mdchain info_mdc, *mdp = &info_mdc; + int error; + + bzero(mdp, sizeof (*mdp)); + + error = smbfs_smb1_query_fs_info(ssp, mdp, + SMB_QFS_ATTRIBUTE_INFO, scrp); + if (error) + goto out; + error = smbfs_decode_fs_attr_info(ssp, mdp, fsa); + +out: + md_done(mdp); + + return (error); +} + +/* + * Get FileFsFullSizeInformation and + * parse into *info + */ +int +smbfs_smb1_statfs(struct smb_share *ssp, + struct smb_fs_size_info *info, + struct smb_cred *scrp) +{ + struct mdchain info_mdc, *mdp = &info_mdc; + struct smb_vc *vcp = SSTOVC(ssp); + uint16_t level; + int error; + + bzero(mdp, sizeof (*mdp)); + + if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) + level = SMB_QFS_FULL_SIZE_INFORMATION; + else + level = SMB_QFS_SIZE_INFO; + error = smbfs_smb1_query_fs_info(ssp, mdp, level, scrp); + if (error) + goto out; + + md_get_uint64le(mdp, &info->total_units); + md_get_uint64le(mdp, &info->caller_avail); + if (level == SMB_QFS_FULL_SIZE_INFORMATION) + md_get_uint64le(mdp, &info->actual_avail); + else + info->actual_avail = info->caller_avail; + + md_get_uint32le(mdp, &info->sect_per_unit); + error = md_get_uint32le(mdp, &info->bytes_per_sect); + +out: + md_done(mdp); + + return (error); +} + +int +smbfs_smb1_flush(struct smb_share *ssp, uint16_t fid, struct smb_cred *scrp) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_FLUSH, scrp); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, fid); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return (error); +} + +/* + * Set file info via an open handle. + * Caller provides payload, info level. + */ +static int +smbfs_smb1_setinfo_file(struct smb_share *ssp, uint16_t fid, + struct mbchain *info_mbp, uint16_t level, struct smb_cred *scrp) +{ + struct smb_t2rq *t2p = NULL; + struct mbchain *mbp; + uint16_t cmd = SMB_TRANS2_SET_FILE_INFORMATION; + int error; + + ASSERT(fid != SMB_FID_UNUSED); + + error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p); + if (error) + return (error); + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, fid); + mb_put_uint16le(mbp, level); + mb_put_uint16le(mbp, 0); /* pad */ + + /* put the payload */ + mbp = &t2p->t2_tdata; + mb_init(mbp); + error = mb_put_mbchain(mbp, info_mbp); + if (error) + goto out; + + t2p->t2_maxpcount = 2; + t2p->t2_maxdcount = 0; + error = smb_t2_request(t2p); + +out: + smb_t2_done(t2p); + + return (error); +} + +int +smbfs_smb1_seteof(struct smb_share *ssp, uint16_t fid, + uint64_t newsize, struct smb_cred *scrp) +{ + struct mbchain data_mb, *mbp = &data_mb; + uint16_t level; + int error; + + if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) + level = SMB_SFILEINFO_END_OF_FILE_INFORMATION; + else + level = SMB_SFILEINFO_END_OF_FILE_INFO; + + mb_init(mbp); + error = mb_put_uint64le(mbp, newsize); + if (error) + goto out; + error = smbfs_smb1_setinfo_file(ssp, fid, mbp, level, scrp); + +out: + mb_done(mbp); + return (error); +} + +int +smbfs_smb1_setdisp(struct smb_share *ssp, uint16_t fid, + uint8_t newdisp, struct smb_cred *scrp) +{ + struct mbchain data_mb, *mbp = &data_mb; + uint16_t level; + int error; + + if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) + level = SMB_SFILEINFO_DISPOSITION_INFORMATION; + else + level = SMB_SFILEINFO_DISPOSITION_INFO; + + mb_init(mbp); + error = mb_put_uint8(mbp, newdisp); + if (error) + goto out; + error = smbfs_smb1_setinfo_file(ssp, fid, mbp, level, scrp); + +out: + mb_done(mbp); + + return (error); +} + +/* + * Set FileBasicInformation on an open handle + * Caller builds the mbchain. + * Always have a FID here. + */ +int +smbfs_smb1_setfattr(struct smb_share *ssp, uint16_t fid, + struct mbchain *mbp, struct smb_cred *scrp) +{ + uint16_t level; + int error; + + if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) + level = SMB_SFILEINFO_BASIC_INFORMATION; + else + level = SMB_SFILEINFO_BASIC_INFO; + error = smbfs_smb1_setinfo_file(ssp, fid, mbp, level, scrp); + + return (error); +} + +/* + * On SMB1, the trans2 rename only allows a rename where the + * source and target are in the same directory. If you give + * the server any separators, you get "status not supported". + * + * Why bother using this instead of smbfs_smb1_oldrename? + * Because it works with an open file, and some servers don't + * allow oldrename of a file that's currently open. We call + * this when deleting an open file in smbfsremove(), where + * the rename is always in the same directory. + */ +/*ARGSUSED*/ +int +smbfs_smb1_t2rename(struct smbnode *np, + const char *tname, int tnlen, + uint16_t fid, struct smb_cred *scrp) +{ + struct smb_share *ssp = np->n_mount->smi_share; + struct mbchain data_mb, *mbp = &data_mb; + struct smb_vc *vcp = SSTOVC(ssp); + uint32_t *name_lenp; + uint16_t level = SMB_SFILEINFO_RENAME_INFORMATION; + int base, len; + int error; + + mb_init(mbp); + mb_put_uint32le(mbp, 0); /* don't overwrite */ + mb_put_uint32le(mbp, 0); /* obsolete target dir fid */ + name_lenp = mb_reserve(mbp, 4); /* name len */ + + /* New name */ + base = mbp->mb_count; + error = smb_put_dmem(mbp, vcp, tname, tnlen, SMB_CS_NONE, NULL); + if (error) + goto out; + len = mbp->mb_count - base; + *name_lenp = htolel(len); + + error = smbfs_smb1_setinfo_file(ssp, fid, mbp, level, scrp); + +out: + mb_done(mbp); + return (error); +} + +/* + * Do an SMB1 (old style) rename using a full dest. path. + * This is used when renaming to a different directory, + * because the (preferred) t2rename can't do that. + */ +int +smbfs_smb1_oldrename(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, struct smb_cred *scrp) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = src->n_mount->smi_share; + struct mbchain *mbp; + int error; + uint16_t fa; + char sep; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scrp); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + /* freebsd bug: Let directories be renamed - Win98 requires DIR bit */ + fa = (SMBTOV(src)->v_type == VDIR) ? SMB_FA_DIR : 0; + fa |= SMB_FA_SYSTEM | SMB_FA_HIDDEN; + mb_put_uint16le(mbp, fa); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + + /* + * When we're not adding any component name, the + * passed sep is ignored, so just pass sep=0. + */ + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0, 0); + if (error) + goto out; + + /* + * After XATTR directories, separator is ":" + */ + sep = (src->n_flag & N_XATTR) ? ':' : '\\'; + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen, sep); + if (error) + goto out; + + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); +out: + smb_rq_done(rqp); + return (error); +} + + +/* + * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect + */ +static int +smbfs_smb1_trans2find2(struct smbfs_fctx *ctx) +{ + struct smb_t2rq *t2p; + struct smb_vc *vcp = SSTOVC(ctx->f_ssp); + struct mbchain *mbp; + struct mdchain *mdp; + uint16_t ecnt, eos, lno, flags; + uint16_t amask, limit; + int error; + + /* smbfs_smb_findnextLM2 sets this */ + limit = ctx->f_limit; + amask = (uint16_t)ctx->f_attrmask; + + if (ctx->f_t2) { + smb_t2_done(ctx->f_t2); + ctx->f_t2 = NULL; + } + flags = FIND2_RETURN_RESUME_KEYS | FIND2_CLOSE_ON_EOS; + if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) { + flags |= FIND2_CLOSE_AFTER_REQUEST; + ctx->f_flags |= SMBFS_RDD_NOCLOSE; + } + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2, + ctx->f_scred, &t2p); + if (error) + return (error); + ctx->f_t2 = t2p; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, amask); + mb_put_uint16le(mbp, limit); + mb_put_uint16le(mbp, flags); + mb_put_uint16le(mbp, ctx->f_infolevel); + mb_put_uint32le(mbp, 0); + error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, + ctx->f_wildcard, ctx->f_wclen, '\\'); + if (error) + return (error); + } else { + error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2, + ctx->f_scred, &t2p); + if (error) + return (error); + ctx->f_t2 = t2p; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, ctx->f_Sid); + mb_put_uint16le(mbp, limit); + mb_put_uint16le(mbp, ctx->f_infolevel); + /* Send whatever resume key we received... */ + mb_put_uint32le(mbp, ctx->f_rkey); + mb_put_uint16le(mbp, flags); + /* ... and the resume name if we have one. */ + if (ctx->f_rname) { + /* resume file name */ + mb_put_mem(mbp, ctx->f_rname, ctx->f_rnamelen, + MB_MSYSTEM); + } + /* Add trailing null - 1 byte if ASCII, 2 if Unicode */ + if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) + mb_put_uint8(mbp, 0); /* 1st byte NULL Unicode char */ + mb_put_uint8(mbp, 0); + } + t2p->t2_maxpcount = 5 * 2; + t2p->t2_maxdcount = 0xF000; /* 64K less some overhead */ + error = smb_t2_request(t2p); + if (error) + return (error); + + /* + * This is the "resume name" we just sent. + * We want the new one (if any) that may be + * found in the response we just received and + * will now begin parsing. Free the old one + * now so we'll know if we found a new one. + */ + if (ctx->f_rname) { + kmem_free(ctx->f_rname, ctx->f_rnamelen); + ctx->f_rname = NULL; + ctx->f_rnamelen = 0; + } + + mdp = &t2p->t2_rparam; + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + if ((error = md_get_uint16le(mdp, &ctx->f_Sid)) != 0) + goto nodata; + ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; + } + md_get_uint16le(mdp, &ecnt); /* entry count */ + md_get_uint16le(mdp, &eos); /* end of search */ + md_get_uint16le(mdp, NULL); /* EA err. off. */ + error = md_get_uint16le(mdp, &lno); /* last name off. */ + if (error != 0) + goto nodata; + + /* + * The "end of search" flag from an XP server sometimes + * comes back zero when the prior find_next returned exactly + * the number of entries requested. in which case we'd try again + * but the search has in fact been closed so an EBADF results. + * our circumvention is to check here for a zero entry count. + */ + ctx->f_ecnt = ecnt; + if (eos || ctx->f_ecnt == 0) + ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE; + if (ctx->f_ecnt == 0) + return (ENOENT); + + /* Last Name Off (LNO) is the entry with the resume name. */ + ctx->f_rnameofs = lno; + ctx->f_eofs = 0; + + /* + * Have data. Put the payload in ctx->f_mdchain + * Note struct assignments here. + */ + mdp = &t2p->t2_rdata; + md_done(&ctx->f_mdchain); + ctx->f_mdchain = *mdp; + ctx->f_left = m_fixhdr(mdp->md_top); + bzero(mdp, sizeof (*mdp)); + + return (0); + +nodata: + /* + * Failed parsing the FindFirst or FindNext response. + * Force this directory listing closed, otherwise the + * calling process may hang in an infinite loop. + */ + ctx->f_ecnt = 0; /* Force closed. */ + ctx->f_flags |= SMBFS_RDD_EOF; + return (EIO); +} + +static int +smbfs_smb1_findclose2(struct smbfs_fctx *ctx) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + int error; + + error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, + ctx->f_scred); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, ctx->f_Sid); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + /* Ditto comments at _smb_close */ + rqp->sr_flags |= SMBR_NOINTR_SEND; + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + return (error); +} + +/*ARGSUSED*/ +int +smbfs_smb_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen, uint32_t attr) +{ + + ctx->f_type = ft_LM2; + ctx->f_namesz = SMB_MAXFNAMELEN + 1; + ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP); + ctx->f_infolevel = SMB_FIND_FULL_DIRECTORY_INFO; + ctx->f_attrmask = attr; + ctx->f_wildcard = wildcard; + ctx->f_wclen = wclen; + return (0); +} + +int +smbfs_smb_findcloseLM2(struct smbfs_fctx *ctx) +{ + int error = 0; + if (ctx->f_name) + kmem_free(ctx->f_name, ctx->f_namesz); + if (ctx->f_t2) + smb_t2_done(ctx->f_t2); + md_done(&ctx->f_mdchain); + + /* + * If SMBFS_RDD_FINDFIRST is still set, we were opened + * but never saw a findfirst, so we don't have any + * search handle to close. + */ + if ((ctx->f_flags & (SMBFS_RDD_FINDFIRST | SMBFS_RDD_NOCLOSE)) == 0) + error = smbfs_smb1_findclose2(ctx); + return (error); +} + +/* + * Get a buffer of directory entries (if we don't already have + * some remaining in the current buffer) then decode one. + */ +int +smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit) +{ + int error; + + /* + * If we've scanned to the end of the current buffer + * try to read anohther buffer of dir entries. + * Treat anything less than 8 bytes as an "empty" + * buffer to ensure we can read something. + * (There may be up to 8 bytes of padding.) + */ + if ((ctx->f_eofs + 8) > ctx->f_left) { + /* Scanned the whole buffer. */ + if (ctx->f_flags & SMBFS_RDD_EOF) + return (ENOENT); + ctx->f_limit = limit; + error = smbfs_smb1_trans2find2(ctx); + if (error) + return (error); + ctx->f_otws++; + } + + /* + * Decode one entry, advance f_eofs + */ + error = smbfs_decode_dirent(ctx); + + return (error); +} + +/* + * Helper for smbfs_xa_get_streaminfo + * Query stream info + */ +int +smbfs_smb1_get_streaminfo(smbnode_t *np, struct mdchain *mdp, + struct smb_cred *scrp) +{ + smb_share_t *ssp = np->n_mount->smi_share; + struct smb_vc *vcp = SSTOVC(ssp); + struct smb_t2rq *t2p = NULL; + struct mbchain *mbp; + mblk_t *m; + uint16_t cmd = SMB_TRANS2_QUERY_PATH_INFORMATION; + int error; + + error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p); + if (error) + return (error); + + mbp = &t2p->t2_tparam; + (void) mb_init(mbp); + (void) mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO); + (void) mb_put_uint32le(mbp, 0); + error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, 0); + if (error) + goto out; + + t2p->t2_maxpcount = 2; + t2p->t2_maxdcount = INT16_MAX; + error = smb_t2_request(t2p); + if (error) { + if (t2p->t2_sr_error == NT_STATUS_INVALID_PARAMETER) + error = ENOTSUP; + goto out; + } + + /* + * Have data. Move it to *mdp + */ + m = t2p->t2_rdata.md_top; + if (m == NULL) { + error = EBADRPC; + goto out; + } + t2p->t2_rdata.md_top = NULL; + md_initm(mdp, m); + +out: + smb_t2_done(t2p); + return (error); +} + +/* + * OTW function to Get a security descriptor (SD). + * + * The *reslen param is bufsize(in) / length(out) + * Note: On success, this fills in mdp->md_top, + * which the caller should free. + */ +int +smbfs_smb1_getsec(struct smb_share *ssp, uint16_t fid, + uint32_t selector, mblk_t **res, uint32_t *reslen, + struct smb_cred *scrp) +{ + struct smb_ntrq *ntp; + struct mbchain *mbp; + struct mdchain *mdp; + uint32_t dlen; + int error; + + *res = NULL; + + error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_QUERY_SECURITY_DESC, + scrp, &ntp); + if (error) + return (error); + + /* Parameters part */ + mbp = &ntp->nt_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, fid); + mb_put_uint16le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, selector); + /* Data part (none) */ + + /* Max. returned parameters and data. */ + ntp->nt_maxpcount = 4; + ntp->nt_maxdcount = *reslen; // out buf size + + error = smb_nt_request(ntp); + if (error && !(ntp->nt_flags & SMBT2_MOREDATA)) + goto done; + + /* Get data len */ + mdp = &ntp->nt_rparam; + error = md_get_uint32le(mdp, &dlen); + if (error) + goto done; + + /* + * if there's more data than we said we could receive, + * here is where we pick up the length of it + */ + *reslen = dlen; + if (dlen == 0) { + error = EBADRPC; + goto done; + } + + /* + * get the SD data part. + */ + mdp = &ntp->nt_rdata; + error = md_get_mbuf(mdp, dlen, res); + +done: + if (error == 0 && *res == NULL) { + ASSERT(*res); + error = EBADRPC; + } + + smb_nt_done(ntp); + return (error); +} + + +/* + * OTW function to Set a security descriptor (SD). + * Caller data are carried in an mbchain_t. + * + * Note: This normally consumes mbp->mb_top, and clears + * that pointer when it does. + */ +int +smbfs_smb1_setsec(struct smb_share *ssp, uint16_t fid, + uint32_t selector, mblk_t **mp, struct smb_cred *scrp) +{ + struct smb_ntrq *ntp; + struct mbchain *mbp; + int error; + + error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_SET_SECURITY_DESC, + scrp, &ntp); + if (error) + return (error); + + /* Parameters part */ + mbp = &ntp->nt_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, fid); + mb_put_uint16le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, selector); + + /* Data part */ + mbp = &ntp->nt_tdata; + mb_initm(mbp, *mp); + *mp = NULL; /* consumed */ + + /* No returned parameters or data. */ + ntp->nt_maxpcount = 0; + ntp->nt_maxdcount = 0; + + error = smb_nt_request(ntp); + smb_nt_done(ntp); + + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb2.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb2.c new file mode 100644 index 0000000000..80afc327c3 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb2.c @@ -0,0 +1,896 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 2011 - 2013 Apple Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/inttypes.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/sunddi.h> +#include <sys/cmn_err.h> + +#include <netsmb/smb_osdep.h> + +#include <netsmb/smb.h> +#include <netsmb/smb2.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb2_rq.h> + +#include <smbfs/smbfs.h> +#include <smbfs/smbfs_node.h> +#include <smbfs/smbfs_subr.h> + + +/* + * Todo: locking over-the-wire + */ +#if 0 // todo + +int +smbfs_smb2_locking(struct smbnode *np, int op, uint32_t pid, + offset_t start, uint64_t len, int largelock, + struct smb_cred *scrp, uint32_t timeout) +{ + return (ENOTSUP); +} + +#endif // todo + +/* + * Helper for smbfs_getattr_otw + * used when we don't have an open FID + * + * For SMB2 we need to do an attribute-only open. The + * data returned by open gets us everything we need, so + * just close the handle and we're done. + */ +int +smbfs_smb2_getpattr( + struct smbnode *np, + struct smbfattr *fap, + struct smb_cred *scrp) +{ + smb_fh_t tmp_fh; + struct smb_share *ssp = np->n_mount->smi_share; + uint32_t rights = (STD_RIGHT_READ_CONTROL_ACCESS | + SA_RIGHT_FILE_READ_ATTRIBUTES); + int error; + + bzero(&tmp_fh, sizeof (tmp_fh)); + error = smbfs_smb_ntcreatex(np, + NULL, 0, 0, /* name nmlen xattr */ + rights, SMB_EFA_NORMAL, + NTCREATEX_SHARE_ACCESS_ALL, + NTCREATEX_DISP_OPEN, + 0, /* create options */ + scrp, &tmp_fh, + NULL, fap); + if (error == 0) { + (void) smb_smb_close(ssp, &tmp_fh, scrp); + } + + return (error); +} + +/* + * Common SMB2 query file info + */ +static int +smbfs_smb2_query_info(struct smb_share *ssp, smb2fid_t *fid, + struct mdchain *info_mdp, uint32_t *iolen, + uint8_t type, uint8_t level, uint32_t addl_info, + struct smb_cred *scrp) +{ + struct smb_rq *rqp = NULL; + struct mbchain *mbp; + struct mdchain *mdp; + uint32_t dlen = 0; + uint16_t doff = 0; + uint16_t ssize = 0; + int error; + + error = smb_rq_alloc(SSTOCP(ssp), SMB2_QUERY_INFO, scrp, &rqp); + if (error) + goto out; + + /* + * Build the SMB 2 Query Info req. + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 41); // struct size + mb_put_uint8(mbp, type); + mb_put_uint8(mbp, level); + mb_put_uint32le(mbp, *iolen); // out buf len + mb_put_uint16le(mbp, 0); // in buf off + mb_put_uint16le(mbp, 0); // reserved + mb_put_uint32le(mbp, 0); // in buf len + mb_put_uint32le(mbp, addl_info); + mb_put_uint32le(mbp, 0); // flags + mb_put_uint64le(mbp, fid->fid_persistent); + mb_put_uint64le(mbp, fid->fid_volatile); + + error = smb2_rq_simple(rqp); + if (error) { + if (rqp->sr_error == NT_STATUS_INVALID_PARAMETER) + error = ENOTSUP; + goto out; + } + + /* + * Parse SMB 2 Query Info response + */ + smb_rq_getreply(rqp, &mdp); + + /* Check structure size is 9 */ + md_get_uint16le(mdp, &ssize); + if (ssize != 9) { + error = EBADRPC; + goto out; + } + + /* Get data off, len */ + md_get_uint16le(mdp, &doff); + md_get_uint32le(mdp, &dlen); + *iolen = dlen; + + /* + * Skip ahead to the payload, as needed. + * Current offset is SMB2_HDRLEN + 8. + */ + if (dlen != 0) { + mblk_t *m = NULL; + int skip = (int)doff - (SMB2_HDRLEN + 8); + if (skip < 0) { + error = EBADRPC; + goto out; + } + if (skip > 0) { + md_get_mem(mdp, NULL, skip, MB_MSYSTEM); + } + error = md_get_mbuf(mdp, dlen, &m); + if (error) + goto out; + md_initm(info_mdp, m); + } + +out: + smb_rq_done(rqp); + + return (error); +} + + +/* + * Get FileAllInformation for an open file + * and parse into *fap + */ +int +smbfs_smb2_qfileinfo(struct smb_share *ssp, smb2fid_t *fid, + struct smbfattr *fap, struct smb_cred *scrp) +{ + struct mdchain info_mdc, *mdp = &info_mdc; + uint32_t iolen = 1024; + int error; + + bzero(mdp, sizeof (*mdp)); + + error = smbfs_smb2_query_info(ssp, fid, mdp, &iolen, + SMB2_0_INFO_FILE, FileAllInformation, 0, scrp); + if (error) + goto out; + + error = smbfs_decode_file_all_info(ssp, mdp, fap); + +out: + md_done(mdp); + + return (error); +} + +/* + * Get some SMB2_0_INFO_FILESYSTEM info + * + * Note: This can be called during mount. We don't have any + * smbfs_node_t or pathname, so do our own attr. open on + * the root of the share to get a handle for this request. + */ +static int +smbfs_smb2_query_fs_info(struct smb_share *ssp, struct mdchain *mdp, + uint8_t level, struct smb_cred *scrp) +{ + smb2fid_t fid; + uint32_t iolen = 1024; + boolean_t opened = B_FALSE; + int error; + + /* + * Need a FID for smb2, and this is called during mount + * so "go behind" the usual open/close functions. + */ + error = smb2_smb_ntcreate( + ssp, NULL, // name + NULL, NULL, // create ctx in, out + 0, /* NTCREATEX_FLAGS... */ + SA_RIGHT_FILE_READ_ATTRIBUTES, + SMB_EFA_NORMAL, + NTCREATEX_SHARE_ACCESS_ALL, + NTCREATEX_DISP_OPEN, + 0, /* create options */ + NTCREATEX_IMPERSONATION_IMPERSONATION, + scrp, &fid, NULL, NULL); + if (error != 0) + goto out; + opened = B_TRUE; + + error = smbfs_smb2_query_info(ssp, &fid, mdp, &iolen, + SMB2_0_INFO_FILESYSTEM, level, 0, scrp); + +out: + if (opened) + (void) smb2_smb_close(ssp, &fid, scrp); + + return (error); +} + +/* + * Get FileFsAttributeInformation and + * parse into *info + */ +int +smbfs_smb2_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *info, + struct smb_cred *scrp) +{ + struct mdchain info_mdc, *mdp = &info_mdc; + int error; + + bzero(mdp, sizeof (*mdp)); + + error = smbfs_smb2_query_fs_info(ssp, mdp, + FileFsAttributeInformation, scrp); + if (error) + goto out; + error = smbfs_decode_fs_attr_info(ssp, mdp, info); + +out: + md_done(mdp); + + return (error); +} + +/* + * Get FileFsFullSizeInformation and + * parse into *info + */ +int +smbfs_smb2_statfs(struct smb_share *ssp, + struct smb_fs_size_info *info, + struct smb_cred *scrp) +{ + struct mdchain info_mdc, *mdp = &info_mdc; + int error; + + bzero(mdp, sizeof (*mdp)); + + error = smbfs_smb2_query_fs_info(ssp, mdp, + FileFsFullSizeInformation, scrp); + if (error) + goto out; + + md_get_uint64le(mdp, &info->total_units); + md_get_uint64le(mdp, &info->caller_avail); + md_get_uint64le(mdp, &info->actual_avail); + + md_get_uint32le(mdp, &info->sect_per_unit); + error = md_get_uint32le(mdp, &info->bytes_per_sect); + +out: + md_done(mdp); + + return (error); +} + +int +smbfs_smb2_flush(struct smb_share *ssp, smb2fid_t *fid, + struct smb_cred *scrp) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + error = smb_rq_alloc(SSTOCP(ssp), SMB2_FLUSH, scrp, &rqp); + if (error) + return (error); + + /* + * Build the SMB 2 Flush Request + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 24); /* struct size */ + mb_put_uint16le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, 0); /* reserved */ + + mb_put_uint64le(mbp, fid->fid_persistent); + mb_put_uint64le(mbp, fid->fid_volatile); + + rqp->sr_flags |= SMBR_NORECONNECT; + error = smb2_rq_simple(rqp); + smb_rq_done(rqp); + + return (error); +} + +/* + * Set file info via an open handle. + * Caller provides payload, info level. + */ +static int +smbfs_smb2_set_info(struct smb_share *ssp, smb2fid_t *fid, + struct mbchain *info_mbp, uint8_t type, uint8_t level, + uint32_t addl_info, struct smb_cred *scrp) +{ + struct smb_rq *rqp = NULL; + struct mbchain *mbp; + uint32_t *buffer_lenp; + int base, len; + int error; + + error = smb_rq_alloc(SSTOCP(ssp), SMB2_SET_INFO, scrp, &rqp); + if (error) + goto out; + + /* + * Build the SMB 2 Set Info req. + */ + smb_rq_getrequest(rqp, &mbp); + mb_put_uint16le(mbp, 33); // struct size + mb_put_uint8(mbp, type); + mb_put_uint8(mbp, level); + buffer_lenp = mb_reserve(mbp, sizeof (uint32_t)); + mb_put_uint16le(mbp, SMB2_HDRLEN + 32); // Buffer Offset + mb_put_uint16le(mbp, 0); // Reserved + mb_put_uint32le(mbp, addl_info); // Additional Info + + mb_put_uint64le(mbp, fid->fid_persistent); + mb_put_uint64le(mbp, fid->fid_volatile); + + /* + * Now the payload + */ + base = mbp->mb_count; + error = mb_put_mbchain(mbp, info_mbp); + if (error) + goto out; + len = mbp->mb_count - base; + *buffer_lenp = htolel(len); + if (error) + goto out; + + /* + * Run the request. + * Don't care about the (empty) reply. + */ + error = smb2_rq_simple(rqp); + +out: + smb_rq_done(rqp); + + return (error); +} + +int +smbfs_smb2_seteof(struct smb_share *ssp, smb2fid_t *fid, + uint64_t newsize, struct smb_cred *scrp) +{ + struct mbchain data_mb, *mbp = &data_mb; + uint8_t level = FileEndOfFileInformation; + int error; + + mb_init(mbp); + mb_put_uint64le(mbp, newsize); + error = smbfs_smb2_set_info(ssp, fid, mbp, + SMB2_0_INFO_FILE, level, 0, scrp); + mb_done(mbp); + + return (error); +} + +int +smbfs_smb2_setdisp(struct smb_share *ssp, smb2fid_t *fid, + uint8_t newdisp, struct smb_cred *scrp) +{ + struct mbchain data_mb, *mbp = &data_mb; + uint8_t level = FileDispositionInformation; + int error; + + mb_init(mbp); + mb_put_uint8(mbp, newdisp); + error = smbfs_smb2_set_info(ssp, fid, mbp, + SMB2_0_INFO_FILE, level, 0, scrp); + mb_done(mbp); + + return (error); +} + +/* + * Set FileBasicInformation on an open handle + * Caller builds the mbchain. + */ +int +smbfs_smb2_setfattr(struct smb_share *ssp, smb2fid_t *fid, + struct mbchain *mbp, struct smb_cred *scrp) +{ + uint8_t level = FileBasicInformation; + int error; + + error = smbfs_smb2_set_info(ssp, fid, mbp, + SMB2_0_INFO_FILE, level, 0, scrp); + return (error); +} + +/* + * Build a FileRenameInformation and call setinfo + */ +int +smbfs_smb2_rename(struct smbnode *np, struct smbnode *tdnp, + const char *tname, int tnlen, int overwrite, + smb2fid_t *fid, struct smb_cred *scrp) +{ + struct smb_share *ssp = np->n_mount->smi_share; + struct mbchain data_mb, *mbp = &data_mb; + uint32_t *name_lenp; + uint8_t level = FileRenameInformation; + int base, len; + int error; + + mb_init(mbp); + + mb_put_uint32le(mbp, (overwrite & 1)); + mb_put_uint32le(mbp, 0); // reserved + mb_put_uint64le(mbp, 0); // Root Dir + name_lenp = mb_reserve(mbp, 4); + + /* Target name (full path) */ + base = mbp->mb_count; + if (tnlen > 0) { + error = smbfs_fullpath(mbp, SSTOVC(ssp), + tdnp, tname, tnlen, '\\'); + if (error) + goto out; + } + len = mbp->mb_count - base; + *name_lenp = htolel(len); + + error = smbfs_smb2_set_info(ssp, fid, mbp, + SMB2_0_INFO_FILE, level, 0, scrp); + +out: + mb_done(mbp); + + return (error); +} + +/* + * Later servers have maxtransact at a megabyte or more, + * but we don't want to buffer up that much data, so use + * the lesser of that or 64k. + */ +#define SMBFS_QDIR_MAX_BUF (1<<16) + +/* + * SMB2 query directory + */ +static int +smbfs_smb2_qdir(struct smbfs_fctx *ctx) +{ + smb_fh_t *fhp = ctx->f_fhp; + smb_share_t *ssp = ctx->f_ssp; + smb_vc_t *vcp = SSTOVC(ssp); + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + uint16_t *name_lenp; + uint8_t level, flags; + uint16_t ssize = 0; + uint16_t obuf_off = 0; + uint32_t obuf_len = 0; + uint32_t obuf_req; + int error; + + level = (uint8_t)ctx->f_infolevel; + flags = 0; + if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) + flags |= SMB2_QDIR_FLAG_SINGLE; + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) + ctx->f_rkey = 0; + else + flags |= SMB2_QDIR_FLAG_INDEX; + + obuf_req = SMBFS_QDIR_MAX_BUF; + if (obuf_req > vcp->vc_sopt.sv2_maxtransact) + obuf_req = vcp->vc_sopt.sv2_maxtransact; + + if (ctx->f_rq) { + smb_rq_done(ctx->f_rq); + ctx->f_rq = NULL; + } + error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB2_QUERY_DIRECTORY, + ctx->f_scred, &rqp); + if (error) + return (error); + ctx->f_rq = rqp; + + /* + * Build an SMB2 Query Dir req. + */ + smb_rq_getrequest(rqp, &mbp); + + mb_put_uint16le(mbp, 33); /* Struct size */ + mb_put_uint8(mbp, level); + mb_put_uint8(mbp, flags); + mb_put_uint32le(mbp, ctx->f_rkey); /* FileIndex */ + + mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent); + mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile); + + mb_put_uint16le(mbp, 96); + name_lenp = mb_reserve(mbp, sizeof (uint16_t)); /* FileNameLen */ + mb_put_uint32le(mbp, obuf_req); /* Output Buf Len */ + + /* Add in the name if any */ + if (ctx->f_wclen > 0) { + int base, len; + + /* Put the match pattern. */ + base = mbp->mb_count; + error = smb_put_dmem(mbp, vcp, + ctx->f_wildcard, ctx->f_wclen, + SMB_CS_NONE, NULL); + if (error) + return (error); + + /* Update the FileNameLen */ + len = mbp->mb_count - base; + *name_lenp = htoles(len); + } else { + /* Empty string */ + mb_put_uint16le(mbp, 0); + *name_lenp = 0; + } + + error = smb2_rq_simple(rqp); + if (error != 0) + goto out; + + /* + * Parse the SMB2 Query Dir response + */ + smb_rq_getreply(rqp, &mdp); + + /* Check structure size is 9 */ + md_get_uint16le(mdp, &ssize); + if (ssize != 9) { + error = EBADRPC; + goto out; + } + + /* Get output buffer offset, length */ + md_get_uint16le(mdp, &obuf_off); + md_get_uint32le(mdp, &obuf_len); + + /* + * After read at EOF we'll have just one word: + * NextEntryOffset == 0 Allow some padding. + */ + if (obuf_len < 8) { + error = ENOENT; + goto out; + } + + /* + * If this reply is shorter than requested by 1k + * or more, we must have reached EOF. + */ + if ((obuf_len + 1024) < obuf_req) + ctx->f_flags |= SMBFS_RDD_EOF; + + /* + * Have data. Put the payload in ctx->f_mdchain + * Current offset is SMB2_HDRLEN + 8. + */ + { + mblk_t *m = NULL; + int skip = (int)obuf_off - (SMB2_HDRLEN + 8); + if (skip < 0) { + error = EBADRPC; + goto out; + } + if (skip > 0) { + md_get_mem(mdp, NULL, skip, MB_MSYSTEM); + } + error = md_get_mbuf(mdp, obuf_len, &m); + if (error) + goto out; + md_done(&ctx->f_mdchain); + md_initm(&ctx->f_mdchain, m); + } + + /* + * SMB2 Query Directory does not provie an EntryCount. + * Instead, we'll advance f_eofs (entry offset) + * through the range [0..f_left] + */ + ctx->f_left = obuf_len; + ctx->f_eofs = 0; + return (0); + +out: + if (error != 0) { + /* + * Failed parsing the FindFirst or FindNext response. + * Force this directory listing closed, otherwise the + * calling process may hang in an infinite loop. + */ + ctx->f_left = 0; + ctx->f_eofs = 0; + ctx->f_flags |= SMBFS_RDD_EOF; + } + + return (error); +} + +int +smbfs_smb2_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen, uint32_t attr) +{ + smb_fh_t *fhp = NULL; + uint32_t rights = + STD_RIGHT_READ_CONTROL_ACCESS | + SA_RIGHT_FILE_READ_ATTRIBUTES | + SA_RIGHT_FILE_READ_DATA; + int error; + + /* + * Set f_type no matter what, so cleanup will call + * smbfs_smb2_findclose, error or not. + */ + ctx->f_type = ft_SMB2; + ASSERT(ctx->f_dnp == dnp); + + /* + * Get a file handle on the directory + */ + error = smb_fh_create(ctx->f_ssp, &fhp); + if (error != 0) + goto errout; + + error = smbfs_smb_ntcreatex(dnp, + NULL, 0, 0, /* name nmlen xattr */ + rights, SMB_EFA_NORMAL, + NTCREATEX_SHARE_ACCESS_ALL, + NTCREATEX_DISP_OPEN, + 0, /* create options */ + ctx->f_scred, fhp, + NULL, NULL); /* cr_act_p fa_p */ + if (error != 0) + goto errout; + + fhp->fh_rights = rights; + smb_fh_opened(fhp); + ctx->f_fhp = fhp; + + ctx->f_namesz = SMB_MAXFNAMELEN + 1; + ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP); + ctx->f_infolevel = FileFullDirectoryInformation; + ctx->f_attrmask = attr; + ctx->f_wildcard = wildcard; + ctx->f_wclen = wclen; + + return (0); + +errout: + if (fhp != NULL) + smb_fh_rele(fhp); + return (error); +} + +int +smbfs_smb2_findclose(struct smbfs_fctx *ctx) +{ + smb_fh_t *fhp = NULL; + + if ((fhp = ctx->f_fhp) != NULL) { + ctx->f_fhp = NULL; + smb_fh_rele(fhp); + } + if (ctx->f_name) + kmem_free(ctx->f_name, ctx->f_namesz); + if (ctx->f_rq) + smb_rq_done(ctx->f_rq); + md_done(&ctx->f_mdchain); + + return (0); +} + +/* + * Get a buffer of directory entries (if we don't already have + * some remaining in the current buffer) then decode one. + */ +int +smbfs_smb2_findnext(struct smbfs_fctx *ctx, uint16_t limit) +{ + int error; + + /* + * If we've scanned to the end of the current buffer + * try to read anohther buffer of dir entries. + * Treat anything less than 8 bytes as an "empty" + * buffer to ensure we can read something. + * (There may be up to 8 bytes of padding.) + */ + if ((ctx->f_eofs + 8) > ctx->f_left) { + /* Scanned the whole buffer. */ + if (ctx->f_flags & SMBFS_RDD_EOF) + return (ENOENT); + ctx->f_limit = limit; + error = smbfs_smb2_qdir(ctx); + if (error) + return (error); + ctx->f_otws++; + } + + /* + * Decode one entry + */ + error = smbfs_decode_dirent(ctx); + + return (error); +} + + +/* + * Helper for smbfs_xa_get_streaminfo + * Query stream info + */ +int +smbfs_smb2_get_streaminfo(smbnode_t *np, struct mdchain *mdp, + struct smb_cred *scrp) +{ + smb_share_t *ssp = np->n_mount->smi_share; + smb_fh_t *fhp = NULL; + uint32_t rights = + STD_RIGHT_READ_CONTROL_ACCESS | + SA_RIGHT_FILE_READ_ATTRIBUTES; + uint32_t iolen = INT16_MAX; + int error; + + /* + * Get a file handle on the object + * with read attr. rights. + */ + error = smb_fh_create(ssp, &fhp); + if (error != 0) + goto out; + error = smbfs_smb_ntcreatex(np, + NULL, 0, 0, /* name nmlen xattr */ + rights, SMB_EFA_NORMAL, + NTCREATEX_SHARE_ACCESS_ALL, + NTCREATEX_DISP_OPEN, + 0, /* create options */ + scrp, fhp, NULL, NULL); + if (error != 0) + goto out; + + smb_fh_opened(fhp); + + /* + * Query stream info + */ + error = smbfs_smb2_query_info(ssp, &fhp->fh_fid2, mdp, &iolen, + SMB2_0_INFO_FILE, FileStreamInformation, 0, scrp); + +out: + if (fhp != NULL) + smb_fh_rele(fhp); + return (error); +} + + +/* + * OTW function to Get a security descriptor (SD). + * + * The *reslen param is bufsize(in) / length(out) + * Note: On success, this fills in mdp->md_top, + * which the caller should free. + */ +int +smbfs_smb2_getsec(struct smb_share *ssp, smb2fid_t *fid, + uint32_t selector, mblk_t **res, uint32_t *reslen, + struct smb_cred *scrp) +{ + struct mdchain info_mdc, *mdp = &info_mdc; + int error; + + bzero(mdp, sizeof (*mdp)); + + error = smbfs_smb2_query_info(ssp, fid, mdp, reslen, + SMB2_0_INFO_SECURITY, 0, selector, scrp); + if (error) + goto out; + + if (mdp->md_top == NULL) { + error = EBADRPC; + goto out; + } + *res = mdp->md_top; + mdp->md_top = NULL; + +out: + md_done(mdp); + return (error); +} + + +/* + * OTW function to Set a security descriptor (SD). + * Caller data are carried in an mbchain_t. + * + * Note: This normally consumes mbp->mb_top, and clears + * that pointer when it does. + */ +int +smbfs_smb2_setsec(struct smb_share *ssp, smb2fid_t *fid, + uint32_t selector, mblk_t **mp, struct smb_cred *scrp) +{ + struct mbchain info_mbp, *mbp = &info_mbp; + int error; + + ASSERT(*mp != NULL); + mb_initm(mbp, *mp); + *mp = NULL; /* consumed */ + + error = smbfs_smb2_set_info(ssp, fid, mbp, + SMB2_0_INFO_SECURITY, 0, selector, scrp); + + mb_done(mbp); + + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c index 71d49c49b4..0b5eb411f1 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c @@ -33,9 +33,10 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -47,6 +48,7 @@ #include <netsmb/smb_osdep.h> #include <netsmb/smb.h> +#include <netsmb/smb2.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_subr.h> #include <netsmb/smb_rq.h> @@ -73,10 +75,11 @@ smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0; int error; - if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) - caseopt |= SMB_CS_UPPER; - - if (unicode) { + /* + * SMB1 may need an alignment pad before (not SMB2) + */ + if (((vcp)->vc_flags & SMBV_SMB2) == 0 && + ((vcp)->vc_hflags2 & SMB_FLAGS2_UNICODE) != 0) { error = mb_put_padbyte(mbp); if (error) return (error); @@ -122,11 +125,14 @@ smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, if (error) return (error); } - /* Put NULL termination. */ - if (unicode) - error = mb_put_uint16le(mbp, 0); - else - error = mb_put_uint8(mbp, 0); + + /* SMB1 wants NULL termination. */ + if (((vcp)->vc_flags & SMBV_SMB2) == 0) { + if (unicode) + error = mb_put_uint16le(mbp, 0); + else + error = mb_put_uint8(mbp, 0); + } return (error); } @@ -183,3 +189,271 @@ errout: */ (void) strlcpy(ctx->f_name, "?", ctx->f_namesz); } + +/* + * Decode a directory entry from OtW form into ctx->f_attr + * + * Caller already put some (wire-format) directory entries + * into ctx->f_mdchain and we expect to find one. + * + * Advancing correctly through the buffer can be tricky if one + * tries to add up the size of an entry as you go (which is how + * the darwin code this is derived from did it). The easiest way + * to correctly advance the position is to get a whole dirent + * into another mdchain (entry_mdc) based on NextEntryOffset, + * and then scan the data from that mdchain. On the last entry, + * we don't know the entire length, so just scan directly from + * what remains of the multi-entry buffer instead of trying to + * figure out the length to copy into a separate mdchain. + */ +int +smbfs_decode_dirent(struct smbfs_fctx *ctx) +{ + struct mdchain entry_mdc; + struct mdchain *mdp = &ctx->f_mdchain; + size_t nmlen; + uint64_t llongint; + uint32_t nmsize, dattr; + uint32_t nextoff = 0; + int error; + + /* In case we error out... */ + ctx->f_nmlen = 0; + ctx->f_rkey = (uint32_t)-1; + bzero(&entry_mdc, sizeof (entry_mdc)); + + /* + * Setup mdp to point to an mbchain holding + * what should be a single directory entry. + */ + error = md_get_uint32le(mdp, &nextoff); + if (error != 0) + goto errout; + if (nextoff >= 4) { + /* + * More entries follow. Make a new mbchain + * holding just this one entry, then advance. + */ + mblk_t *m = NULL; + error = md_get_mbuf(mdp, nextoff - 4, &m); + if (error != 0) + goto errout; + md_initm(&entry_mdc, m); + mdp = &entry_mdc; + ctx->f_eofs += nextoff; + } else { + /* Scan directly from ctx->f_mdchain */ + ctx->f_eofs = ctx->f_left; + } + + /* + * Decode the fixed-size parts + */ + switch (ctx->f_infolevel) { + case FileFullDirectoryInformation: + case SMB_FIND_FULL_DIRECTORY_INFO: + md_get_uint32le(mdp, &ctx->f_rkey); /* resume key (idx) */ + md_get_uint64le(mdp, &llongint); /* creation time */ + smb_time_NT2local(llongint, &ctx->f_attr.fa_createtime); + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, &ctx->f_attr.fa_atime); + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, &ctx->f_attr.fa_mtime); + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, &ctx->f_attr.fa_ctime); + md_get_uint64le(mdp, &llongint); /* file size */ + ctx->f_attr.fa_size = llongint; + md_get_uint64le(mdp, &llongint); /* alloc. size */ + ctx->f_attr.fa_allocsz = llongint; + md_get_uint32le(mdp, &dattr); /* ext. file attributes */ + ctx->f_attr.fa_attr = dattr; + error = md_get_uint32le(mdp, &nmsize); /* name size (otw) */ + if (error) + goto errout; + md_get_uint32le(mdp, NULL); /* Ea size */ + break; + + case FileStreamInformation: + error = md_get_uint32le(mdp, &nmsize); /* name size (otw) */ + md_get_uint64le(mdp, &llongint); /* file size */ + ctx->f_attr.fa_size = llongint; + md_get_uint64le(mdp, &llongint); /* alloc. size */ + ctx->f_attr.fa_allocsz = llongint; + /* + * Stream names start with a ':' that we want to skip. + * This is the easiest place to take care of that. + * Always unicode here. + */ + if (nmsize >= 2) { + struct mdchain save_mdc; + uint16_t wch; + save_mdc = *mdp; + md_get_uint16le(mdp, &wch); + if (wch == ':') { + /* OK, we skipped the ':' */ + nmsize -= 2; + } else { + SMBVDEBUG("No leading : in stream?\n"); + /* restore position */ + *mdp = save_mdc; + } + } + break; + + default: + SMBVDEBUG("unexpected info level %d\n", ctx->f_infolevel); + error = EINVAL; + goto errout; + } + + /* + * Get the filename, and convert to utf-8 + * Allocated f_name in findopen + */ + nmlen = ctx->f_namesz; + error = smb_get_dstring(mdp, SSTOVC(ctx->f_ssp), + ctx->f_name, &nmlen, nmsize); + if (error != 0) + goto errout; + ctx->f_nmlen = (int)nmlen; + md_done(&entry_mdc); + return (0); + +errout: + /* + * Something bad has happened and we ran out of data + * before we could parse all f_ecnt entries expected. + * Give up on the current buffer. + */ + SMBVDEBUG("ran out of data\n"); + ctx->f_eofs = ctx->f_left; + md_done(&entry_mdc); + return (error); +} + +/* + * Decode FileAllInformation + * + * The data is a concatenation of: + * FileBasicInformation + * FileStandardInformation + * FileInternalInformation + * FileEaInformation + * FilePositionInformation + * FileModeInformation + * FileAlignmentInformation + * FileNameInformation + */ +/*ARGSUSED*/ +int +smbfs_decode_file_all_info(struct smb_share *ssp, + struct mdchain *mdp, struct smbfattr *fap) +{ + uint64_t llongint, lsize; + uint32_t dattr; + int error; + + /* + * This part is: FileBasicInformation + */ + + /* creation time */ + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, &fap->fa_createtime); + + /* last access time */ + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, &fap->fa_atime); + + /* last write time */ + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, &fap->fa_mtime); + + /* last change time */ + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, &fap->fa_ctime); + + /* attributes */ + md_get_uint32le(mdp, &dattr); + fap->fa_attr = dattr; + + /* reserved */ + md_get_uint32le(mdp, NULL); + + /* + * This part is: FileStandardInformation + */ + + /* allocation size */ + md_get_uint64le(mdp, &lsize); + fap->fa_allocsz = lsize; + + /* File size */ + error = md_get_uint64le(mdp, &lsize); + fap->fa_size = lsize; + + /* + * There's more after this but we don't need it: + * Remainder of FileStandardInformation + * NumLlinks, DeletOnClose, IsDir, reserved. + * Then: + * FileInternalInformation + * FileEaInformation + * FilePositionInformation + * FileModeInformation + * FileAlignmentInformation + * FileNameInformation + */ + + return (error); +} + +/* + * Decode FileFsAttributeInformation + * + * ULONG FileSystemAttributes; + * LONG MaximumComponentNameLength; + * ULONG FileSystemNameLength; + * WCHAR FileSystemName[1]; + */ +int +smbfs_decode_fs_attr_info(struct smb_share *ssp, + struct mdchain *mdp, struct smb_fs_attr_info *fsa) +{ + struct smb_vc *vcp = SSTOVC(ssp); + uint32_t nlen; + int error; + + md_get_uint32le(mdp, &fsa->fsa_aflags); + md_get_uint32le(mdp, &fsa->fsa_maxname); + error = md_get_uint32le(mdp, &nlen); /* fs name length */ + if (error) + goto out; + + /* + * Get the FS type name. + */ + bzero(fsa->fsa_tname, FSTYPSZ); + if (SMB_UNICODE_STRINGS(vcp)) { + uint16_t tmpbuf[FSTYPSZ]; + size_t tmplen, outlen; + + if (nlen > sizeof (tmpbuf)) + nlen = sizeof (tmpbuf); + error = md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM); + if (error != 0) + goto out; + tmplen = nlen / 2; /* UCS-2 chars */ + outlen = FSTYPSZ - 1; + error = uconv_u16tou8(tmpbuf, &tmplen, + (uchar_t *)fsa->fsa_tname, &outlen, + UCONV_IN_LITTLE_ENDIAN); + } else { + if (nlen > (FSTYPSZ - 1)) + nlen = FSTYPSZ - 1; + error = md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM); + } + +out: + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h index fc7a4ffa26..f8d708b5a3 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h @@ -33,9 +33,10 @@ */ /* - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _FS_SMBFS_SMBFS_SUBR_H_ @@ -85,6 +86,7 @@ struct timespec; typedef enum { ft_LM1 = 1, ft_LM2, + ft_SMB2, ft_XA } smbfs_fctx_type_t; @@ -120,31 +122,33 @@ struct smbfs_fctx { /* * Internal variables */ + uint16_t f_infolevel; uint16_t f_limit; /* maximum number of entries */ - uint16_t f_attrmask; /* SMB_FA_ */ + uint32_t f_attrmask; /* SMB_FA_ */ int f_wclen; const char *f_wildcard; struct smbnode *f_dnp; struct smb_cred *f_scred; struct smb_share *f_ssp; + struct smb_fh *f_fhp; union { struct smb_rq *uf_rq; struct smb_t2rq *uf_t2; - } f_urq; + } f_urq; // XXX remove and use... + struct mdchain f_mdchain; int f_left; /* entries left */ int f_ecnt; /* entries left in current response */ int f_eofs; /* entry offset in data block */ uchar_t f_skey[SMB_SKEYLEN]; /* server side search context */ uchar_t f_fname[8 + 1 + 3 + 1]; /* for 8.3 filenames */ uint16_t f_Sid; /* Search handle (like a FID) */ - uint16_t f_infolevel; int f_rnamelen; char *f_rname; /* resume name */ int f_rnameofs; int f_otws; /* # over-the-wire ops so far */ char *f_firstnm; /* first filename we got back */ int f_firstnmlen; - int f_rkey; /* resume key */ + uint32_t f_rkey; /* resume key */ }; typedef struct smbfs_fctx smbfs_fctx_t; @@ -152,73 +156,163 @@ typedef struct smbfs_fctx smbfs_fctx_t; #define f_t2 f_urq.uf_t2 /* - * smb level (smbfs_smb.c) + * Internal form of FileFsFullSizeInformation + * [MS-FSCC] 2.5.4 FileFsFullSizeInformation + * for the _statfs functions. + */ +struct smb_fs_size_info { + uint64_t total_units; + uint64_t caller_avail; + uint64_t actual_avail; + uint32_t sect_per_unit; + uint32_t bytes_per_sect; +}; + +/* + * smb common functions (smbfs_smbx.c) */ int smbfs_smb_lock(struct smbnode *np, int op, caddr_t id, offset_t start, uint64_t len, int largelock, struct smb_cred *scrp, uint32_t timeout); +int smbfs_smb_getfattr(struct smbnode *np, smb_fh_t *fhp, + struct smbfattr *fap, struct smb_cred *scrp); +int smbfs_smb_getpattr(struct smbnode *np, struct smbfattr *fap, + struct smb_cred *scrp); int smbfs_smb_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *, struct smb_cred *scrp); int smbfs_smb_statfs(struct smb_share *ssp, statvfs64_t *sbp, struct smb_cred *scrp); - -int smbfs_smb_setdisp(struct smbnode *np, uint16_t fid, uint8_t newdisp, - struct smb_cred *scrp); -int smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize, +int smbfs_smb_setdisp(struct smb_share *ssp, smb_fh_t *fid, uint8_t newdisp, struct smb_cred *scrp); - -int smbfs_smb_getfattr(struct smbnode *np, struct smbfattr *fap, +int smbfs_smb_setfsize(struct smb_share *ssp, smb_fh_t *fid, uint64_t newsize, struct smb_cred *scrp); - -int smbfs_smb_setfattr(struct smbnode *np, int fid, +int smbfs_smb_setfattr(struct smb_share *ssp, smb_fh_t *fid, uint32_t attr, struct timespec *mtime, struct timespec *atime, struct smb_cred *scrp); +int smbfs_smb_flush(struct smb_share *ssp, smb_fh_t *fid, + struct smb_cred *scrp); + +int smbfs_smb_ntcreatex( + struct smbnode *np, const char *name, int nmlen, int xattr, + uint32_t req_acc, uint32_t efa, uint32_t share_acc, + uint32_t disp, uint32_t createopt, struct smb_cred *scrp, + smb_fh_t *fhpp, uint32_t *cr_act_p, struct smbfattr *fap); +int smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, + struct smb_cred *scrp, smb_fh_t **fidpp); +void smbfs_smb_tmpclose(struct smbnode *ssp, smb_fh_t *fid); int smbfs_smb_open(struct smbnode *np, const char *name, int nmlen, int xattr, uint32_t rights, struct smb_cred *scrp, - uint16_t *fidp, uint32_t *rightsp, struct smbfattr *fap); -int smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, - struct smb_cred *scrp, uint16_t *fidp); -int smbfs_smb_close(struct smb_share *ssp, uint16_t fid, - struct timespec *mtime, struct smb_cred *scrp); -int smbfs_smb_tmpclose(struct smbnode *ssp, uint16_t fid, - struct smb_cred *scrp); + smb_fh_t **fidpp, struct smbfattr *fap); +void smbfs_smb_close(smb_fh_t *fid); int smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen, - int xattr, uint32_t disp, struct smb_cred *scrp, uint16_t *fidp); -int smbfs_smb_delete(struct smbnode *np, struct smb_cred *scrp, - const char *name, int len, int xattr); -int smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp, - const char *tname, int tnmlen, struct smb_cred *scrp); -int smbfs_smb_t2rename(struct smbnode *np, const char *tname, int tnmlen, - struct smb_cred *scrp, uint16_t fid, int replace); -int smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp, - const char *tname, int tnmlen, uint16_t flags, struct smb_cred *scrp); + int xattr, uint32_t disp, struct smb_cred *scrp, smb_fh_t **fidpp); +int smbfs_smb_rename(struct smbnode *sdnp, struct smbnode *src, + struct smbnode *tdnp, const char *tname, int tnmlen, + smb_fh_t *fid, struct smb_cred *scrp); int smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len, struct smb_cred *scrp); -int smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scrp); + int smbfs_smb_findopen(struct smbnode *dnp, const char *wildcard, int wclen, int attr, struct smb_cred *scrp, struct smbfs_fctx **ctxpp); int smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scrp); int smbfs_smb_findclose(struct smbfs_fctx *ctx, struct smb_cred *scrp); -int smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, - struct smbnode *dnp, const char *name, int nmlen, uint8_t sep); + int smbfs_smb_lookup(struct smbnode *dnp, const char **namep, int *nmlenp, struct smbfattr *fap, struct smb_cred *scrp); -int smbfs_smb_hideit(struct smbnode *np, const char *name, int len, - struct smb_cred *scrp); -int smbfs_smb_unhideit(struct smbnode *np, const char *name, int len, - struct smb_cred *scrp); -int smbfs_smb_flush(struct smbnode *np, struct smb_cred *scrp); -int smbfs_0extend(vnode_t *vp, uint16_t fid, len_t from, len_t to, - struct smb_cred *scredp, int timo); /* get/set security descriptor */ -int smbfs_smb_getsec_m(struct smb_share *ssp, uint16_t fid, - struct smb_cred *scrp, uint32_t selector, - mblk_t **res, uint32_t *reslen); -int smbfs_smb_setsec_m(struct smb_share *ssp, uint16_t fid, - struct smb_cred *scrp, uint32_t selector, mblk_t **mp); +int smbfs_smb_getsec(struct smb_share *ssp, smb_fh_t *fid, + uint32_t selector, mblk_t **res, uint32_t *reslen, + struct smb_cred *scrp); +int smbfs_smb_setsec(struct smb_share *ssp, smb_fh_t *fid, + uint32_t selector, mblk_t **mp, struct smb_cred *scrp); + +/* + * SMB1 functions + */ +int smbfs_smb1_trans2_query(struct smbnode *np, uint16_t fid, + struct smbfattr *fap, struct smb_cred *scrp); +int smbfs_smb1_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *, + struct smb_cred *scrp); +int smbfs_smb1_statfs(struct smb_share *ssp, + struct smb_fs_size_info *info, struct smb_cred *scrp); +int smbfs_smb1_seteof(struct smb_share *ssp, uint16_t fid, uint64_t newsize, + struct smb_cred *scrp); +int smbfs_smb1_setdisp(struct smb_share *ssp, uint16_t fid, uint8_t newdisp, + struct smb_cred *scrp); +int smbfs_smb1_setfattr(struct smb_share *ssp, uint16_t fid, + struct mbchain *, struct smb_cred *); +int smbfs_smb1_flush(struct smb_share *ssp, uint16_t fid, + struct smb_cred *scrp); +int smbfs_smb1_t2rename(struct smbnode *np, const char *tname, int tnmlen, + uint16_t fid, struct smb_cred *scrp); +int smbfs_smb1_oldrename(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, struct smb_cred *scrp); + +int smbfs_smb_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen, uint32_t attr); +int smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit); +int smbfs_smb_findcloseLM2(struct smbfs_fctx *ctx); +int smbfs_smb1_get_streaminfo(smbnode_t *np, struct mdchain *mdp, + struct smb_cred *scrp); + +int smbfs_smb1_getsec(struct smb_share *ssp, uint16_t fid, + uint32_t selector, mblk_t **res, uint32_t *reslen, + struct smb_cred *scrp); +int smbfs_smb1_setsec(struct smb_share *ssp, uint16_t fid, + uint32_t selector, mblk_t **mp, + struct smb_cred *scrp); + +/* + * SMB2 functions + */ + +int smbfs_smb2_getpattr(struct smbnode *np, struct smbfattr *fap, + struct smb_cred *scrp); +int smbfs_smb2_qfileinfo(struct smb_share *ssp, smb2fid_t *fid, + struct smbfattr *fap, struct smb_cred *scrp); +int smbfs_smb2_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *, + struct smb_cred *scrp); +int smbfs_smb2_statfs(struct smb_share *ssp, + struct smb_fs_size_info *info, struct smb_cred *scrp); +int smbfs_smb2_seteof(struct smb_share *ssp, smb2fid_t *fid, uint64_t newsize, + struct smb_cred *scrp); +int smbfs_smb2_setdisp(struct smb_share *ssp, smb2fid_t *fid, uint8_t newdisp, + struct smb_cred *scrp); +int smbfs_smb2_flush(struct smb_share *ssp, smb2fid_t *fid, + struct smb_cred *scrp); +int smbfs_smb2_setfattr(struct smb_share *ssp, smb2fid_t *fid, + struct mbchain *, struct smb_cred *); +int smbfs_smb2_rename(struct smbnode *np, struct smbnode *tdnp, + const char *tname, int tnlen, int overwrite, + smb2fid_t *fid, struct smb_cred *scrp); + +int smbfs_smb2_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen, uint32_t attr); +int smbfs_smb2_findnext(struct smbfs_fctx *ctx, uint16_t limit); +int smbfs_smb2_findclose(struct smbfs_fctx *ctx); +int smbfs_smb2_get_streaminfo(smbnode_t *np, struct mdchain *mdp, + struct smb_cred *scrp); + +int smbfs_smb2_getsec(struct smb_share *ssp, smb2fid_t *fid, + uint32_t selector, mblk_t **res, uint32_t *reslen, + struct smb_cred *scrp); +int smbfs_smb2_setsec(struct smb_share *ssp, smb2fid_t *fid, + uint32_t selector, mblk_t **mp, + struct smb_cred *scrp); + + +/* smbfs_subr.c */ + +int smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, + struct smbnode *dnp, const char *name, int nmlen, uint8_t sep); +int smbfs_decode_dirent(struct smbfs_fctx *ctx); +int smbfs_decode_file_all_info(struct smb_share *ssp, + struct mdchain *mdp, struct smbfattr *fap); +int smbfs_decode_fs_attr_info(struct smb_share *ssp, + struct mdchain *mdp, struct smb_fs_attr_info *fsa); /* * VFS-level init, fini stuff diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c index 798c26a09b..768664b610 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c @@ -27,6 +27,7 @@ */ /* * Copyright (c) 2017 by Delphix. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -39,7 +40,9 @@ #include <sys/systm.h> #include <sys/time.h> #include <sys/vnode.h> +#include <sys/atomic.h> #include <sys/bitmap.h> +#include <sys/buf.h> #include <sys/dnlc.h> #include <sys/kmem.h> #include <sys/sunddi.h> @@ -430,7 +433,7 @@ start: np->r_vnode = vp; np->n_mount = mi; - np->n_fid = SMB_FID_UNUSED; + np->n_fid = NULL; np->n_uid = mi->smi_uid; np->n_gid = mi->smi_gid; /* Leave attributes "stale." */ @@ -1231,7 +1234,7 @@ smbfs_subrinit(void) nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) / sizeof (struct smbnode)); if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) { - zcmn_err(GLOBAL_ZONEID, CE_NOTE, + cmn_err(CE_NOTE, "setting nsmbnode to max value of %ld", nsmbnode_max); nsmbnode = nsmbnode_max; } @@ -1249,7 +1252,7 @@ smbfs_subrinit(void) * Assign unique major number for all smbfs mounts */ if ((smbfs_major = getudev()) == -1) { - zcmn_err(GLOBAL_ZONEID, CE_WARN, + cmn_err(CE_WARN, "smbfs: init: can't get unique device number"); smbfs_major = 0; } diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c index 0122d52115..7dad8d2a70 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c @@ -34,9 +34,9 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. * Copyright 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ #include <sys/systm.h> @@ -76,6 +76,17 @@ #include <smbfs/smbfs_node.h> #include <smbfs/smbfs_subr.h> +#ifndef _KERNEL + +#include <libfksmbfs.h> + +#define STRUCT_DECL(s, a) struct s a +#define STRUCT_FGET(handle, field) ((handle).field) +#define _init(v) fksmbfs_init(v) +#define _fini(v) fksmbfs_fini(v) + +#endif /* !_KERNEL */ + /* * Should smbfs mount enable "-o acl" by default? There are good * arguments for both. The most common use case is individual users @@ -98,7 +109,10 @@ int smbfs_tq_nthread = 1; */ int smbfsinit(int fstyp, char *name); void smbfsfini(); + +#ifdef _KERNEL static int smbfs_mount_label_policy(vfs_t *, void *, int, cred_t *); +#endif /* _KERNEL */ /* * SMBFS Mount options table for MS_OPTIONSTR @@ -123,7 +137,11 @@ static mntopt_t mntopts[] = { { MNTOPT_ACL, acl_cancel, NULL, 0, 0 }, { MNTOPT_NOACL, noacl_cancel, NULL, 0, 0 }, { MNTOPT_XATTR, xattr_cancel, NULL, MO_DEFAULT, 0 }, - { MNTOPT_NOXATTR, noxattr_cancel, NULL, 0, 0 } + { MNTOPT_NOXATTR, noxattr_cancel, NULL, 0, 0 }, +#ifndef _KERNEL + /* See vfs_optionisset MNTOPT_NOAC below. */ + { MNTOPT_NOAC, NULL, NULL, 0, 0 }, +#endif /* !_KERNEL */ }; static mntopts_t smbfs_mntopts = { @@ -141,6 +159,7 @@ static vfsdef_t vfw = { &smbfs_mntopts /* mount options table prototype */ }; +#ifdef _KERNEL static struct modlfs modlfs = { &mod_fsops, "SMBFS filesystem", @@ -150,6 +169,7 @@ static struct modlfs modlfs = { static struct modlinkage modlinkage = { MODREV_1, (void *)&modlfs, NULL }; +#endif /* _KERNEL */ /* * Mutex to protect the following variables: @@ -222,7 +242,12 @@ _init(void) return (error); } +#ifdef _KERNEL error = mod_install((struct modlinkage *)&modlinkage); +#else /* _KERNEL */ + error = fake_installfs(&vfw); +#endif /* _KERNEL */ + return (error); } @@ -244,7 +269,11 @@ _fini(void) if (smbfs_mountcount) return (EBUSY); +#ifdef _KERNEL error = mod_remove(&modlinkage); +#else /* _KERNEL */ + error = fake_removefs(&vfw); +#endif /* _KERNEL */ if (error) return (error); @@ -267,17 +296,19 @@ _fini(void) /* * Return information about the module */ +#ifdef _KERNEL int _info(struct modinfo *modinfop) { return (mod_info((struct modlinkage *)&modlinkage, modinfop)); } +#endif /* _KERNEL */ /* * Initialize the vfs structure */ -int smbfsfstyp; +int smbfs_fstyp; vfsops_t *smbfs_vfsops = NULL; static const fs_operation_def_t smbfs_vfsops_template[] = { @@ -292,6 +323,10 @@ static const fs_operation_def_t smbfs_vfsops_template[] = { { NULL, NULL } }; +/* + * This is the VFS switch initialization routine, normally called + * via vfssw[x].vsw_init by vfsinit() or mod_install + */ int smbfsinit(int fstyp, char *name) { @@ -299,7 +334,7 @@ smbfsinit(int fstyp, char *name) error = vfs_setfsops(fstyp, smbfs_vfsops_template, &smbfs_vfsops); if (error != 0) { - zcmn_err(GLOBAL_ZONEID, CE_WARN, + cmn_err(CE_WARN, "smbfsinit: bad vfs ops template"); return (error); } @@ -307,12 +342,12 @@ smbfsinit(int fstyp, char *name) error = vn_make_ops(name, smbfs_vnodeops_template, &smbfs_vnodeops); if (error != 0) { (void) vfs_freevfsops_by_type(fstyp); - zcmn_err(GLOBAL_ZONEID, CE_WARN, + cmn_err(CE_WARN, "smbfsinit: bad vnode ops template"); return (error); } - smbfsfstyp = fstyp; + smbfs_fstyp = fstyp; return (0); } @@ -321,7 +356,7 @@ void smbfsfini() { if (smbfs_vfsops) { - (void) vfs_freevfsops_by_type(smbfsfstyp); + (void) vfs_freevfsops_by_type(smbfs_fstyp); smbfs_vfsops = NULL; } if (smbfs_vnodeops) { @@ -336,8 +371,10 @@ smbfs_free_smi(smbmntinfo_t *smi) if (smi == NULL) return; +#ifdef _KERNEL if (smi->smi_zone_ref.zref_zone != NULL) zone_rele_ref(&smi->smi_zone_ref, ZONE_REF_SMBFS); +#endif /* _KERNEL */ if (smi->smi_share != NULL) smb_share_rele(smi->smi_share); @@ -364,16 +401,21 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) dev_t smbfs_dev; int version; int devfd; - zone_t *zone = curproc->p_zone; + zone_t *zone = curzone; +#ifdef _KERNEL zone_t *mntzone = NULL; +#else /* _KERNEL */ + short minclsyspri = MINCLSYSPRI; +#endif /* _KERNEL */ smb_share_t *ssp = NULL; smb_cred_t scred; int flags, sec; - STRUCT_DECL(smbfs_args, args); /* smbfs mount arguments */ +#ifdef _KERNEL if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) return (error); +#endif /* _KERNEL */ if (mvp->v_type != VDIR) return (ENOTDIR); @@ -384,11 +426,17 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) * uap->datalen might be different from sizeof (args) * in a compatible situation. */ +#ifdef _KERNEL STRUCT_INIT(args, get_udatamodel()); bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)); if (copyin(data, STRUCT_BUF(args), MIN(uap->datalen, SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE)))) return (EFAULT); +#else /* _KERNEL */ + bzero(&args, sizeof (args)); + if (copyin(data, &args, MIN(uap->datalen, sizeof (args)))) + return (EFAULT); +#endif /* _KERNEL */ /* * Check mount program version @@ -439,6 +487,7 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) * See: ssp, smi, rtnp, mntzone */ +#ifdef _KERNEL /* * Determine the zone we're being mounted into. */ @@ -482,6 +531,7 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); } } +#endif /* _KERNEL */ /* Prevent unload. */ atomic_inc_32(&smbfs_mountcount); @@ -504,6 +554,7 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) smi->smi_share = ssp; ssp = NULL; +#ifdef _KERNEL /* * Convert the anonymous zone hold acquired via zone_hold() above * into a zone reference. @@ -512,15 +563,22 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) zone_hold_ref(mntzone, &smi->smi_zone_ref, ZONE_REF_SMBFS); zone_rele(mntzone); mntzone = NULL; +#else /* _KERNEL */ + smi->smi_zone_ref.zref_zone = curzone; +#endif /* _KERNEL */ /* * Initialize option defaults */ - smi->smi_flags = SMI_LLOCK; smi->smi_acregmin = SEC2HR(SMBFS_ACREGMIN); smi->smi_acregmax = SEC2HR(SMBFS_ACREGMAX); smi->smi_acdirmin = SEC2HR(SMBFS_ACDIRMIN); smi->smi_acdirmax = SEC2HR(SMBFS_ACDIRMAX); + smi->smi_flags = SMI_LLOCK; +#ifndef _KERNEL + /* Always direct IO with fakekernel */ + smi->smi_flags |= SMI_DIRECTIO; +#endif /* _KERNEL */ /* * All "generic" mount options have already been @@ -541,10 +599,30 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) * starting with args.flags (SMBFS_MF_xxx) */ flags = STRUCT_FGET(args, flags); - smi->smi_uid = STRUCT_FGET(args, uid); - smi->smi_gid = STRUCT_FGET(args, gid); smi->smi_fmode = STRUCT_FGET(args, file_mode) & 0777; smi->smi_dmode = STRUCT_FGET(args, dir_mode) & 0777; +#ifdef _KERNEL + smi->smi_uid = STRUCT_FGET(args, uid); + smi->smi_gid = STRUCT_FGET(args, gid); +#else /* _KERNEL */ + /* + * Need uid/gid to match our fake cred we'll fail in + * smbfs_access_rwx later. + */ + smi->smi_uid = crgetuid(cr); + smi->smi_gid = crgetgid(cr); + + /* + * Our user-level do_mount() passes the mount options sting + * as-is, where the real mount program would convert some + * of those options to bits set in smbfs_args.flags. + * To avoid replicating all that conversion code, this + * uses the generic vfs option support to handle those + * option flag bits we need, i.e.: "noac" + */ + if (vfs_optionisset(vfsp, MNTOPT_NOAC, NULL)) + flags |= SMBFS_MF_NOAC; +#endif /* _KERNEL */ /* * Hande the SMBFS_MF_xxx flags. @@ -615,9 +693,9 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) mutex_exit(&smbfs_minor_lock); vfsp->vfs_dev = smbfs_dev; - vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfsfstyp); + vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfs_fstyp); vfsp->vfs_data = (caddr_t)smi; - vfsp->vfs_fstype = smbfsfstyp; + vfsp->vfs_fstype = smbfs_fstyp; vfsp->vfs_bsize = MAXBSIZE; vfsp->vfs_bcount = 0; @@ -657,6 +735,7 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) */ return (0); +#ifdef _KERNEL errout: vfsp->vfs_data = NULL; if (smi != NULL) @@ -669,6 +748,7 @@ errout: smb_share_rele(ssp); return (error); +#endif /* _KERNEL */ } /* @@ -682,8 +762,10 @@ smbfs_unmount(vfs_t *vfsp, int flag, cred_t *cr) smi = VFTOSMI(vfsp); +#ifdef _KERNEL if (secpolicy_fs_unmount(cr, vfsp) != 0) return (EPERM); +#endif /* _KERNEL */ if ((flag & MS_FORCE) == 0) { smbfs_rflush(vfsp, cr); @@ -976,6 +1058,7 @@ smbfs_freevfs(vfs_t *vfsp) atomic_dec_32(&smbfs_mountcount); } +#ifdef _KERNEL /* * smbfs_mount_label_policy: * Determine whether the mount is allowed according to MAC check, @@ -1066,3 +1149,4 @@ out: label_rele(zlabel); return (retv); } +#endif /* _KERNEL */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c index 23c9f8f15d..3fca806155 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c @@ -34,6 +34,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -60,8 +61,9 @@ #include <sys/sdt.h> #include <sys/taskq_impl.h> #include <sys/zone.h> -#include <sys/vmsystm.h> +#ifdef _KERNEL +#include <sys/vmsystm.h> // for desfree #include <vm/hat.h> #include <vm/as.h> #include <vm/page.h> @@ -70,6 +72,7 @@ #include <vm/seg_map.h> #include <vm/seg_kpm.h> #include <vm/seg_vn.h> +#endif // _KERNEL #include <netsmb/smb_osdep.h> #include <netsmb/smb.h> @@ -83,6 +86,10 @@ #include <sys/fs/smbfs_ioctl.h> #include <fs/fs_subr.h> +#ifndef MAXOFF32_T +#define MAXOFF32_T 0x7fffffff +#endif + /* * We assign directory offsets like the NFS client, where the * offset increments by _one_ after each directory entry. @@ -137,18 +144,15 @@ static int smbfssetattr(vnode_t *, struct vattr *, int, cred_t *); static int smbfs_accessx(void *, int, cred_t *); static int smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, caller_context_t *); +static int smbfsflush(smbnode_t *, struct smb_cred *); static void smbfs_rele_fid(smbnode_t *, struct smb_cred *); static uint32_t xvattr_to_dosattr(smbnode_t *, struct vattr *); -static int smbfs_rdwrlbn(vnode_t *, page_t *, u_offset_t, size_t, int, - cred_t *); -static int smbfs_bio(struct buf *, int, cred_t *); -static int smbfs_writenp(smbnode_t *np, caddr_t base, int tcount, - struct uio *uiop, int pgcreated); - static int smbfs_fsync(vnode_t *, int, cred_t *, caller_context_t *); + static int smbfs_putpage(vnode_t *, offset_t, size_t, int, cred_t *, caller_context_t *); +#ifdef _KERNEL static int smbfs_getapage(vnode_t *, u_offset_t, size_t, uint_t *, page_t *[], size_t, struct seg *, caddr_t, enum seg_rw, cred_t *); @@ -156,6 +160,13 @@ static int smbfs_putapage(vnode_t *, page_t *, u_offset_t *, size_t *, int, cred_t *); static void smbfs_delmap_async(void *); +static int smbfs_rdwrlbn(vnode_t *, page_t *, u_offset_t, size_t, int, + cred_t *); +static int smbfs_bio(struct buf *, int, cred_t *); +static int smbfs_writenp(smbnode_t *np, caddr_t base, int tcount, + struct uio *uiop, int pgcreated); +#endif // _KERNEL + /* * Error flags used to pass information about certain special errors * which need to be handled specially. @@ -191,14 +202,13 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) smbnode_t *np; vnode_t *vp; smbfattr_t fa; - u_int32_t rights, rightsrcvd; - u_int16_t fid, oldfid; - int oldgenid; + smb_fh_t *fid = NULL; + smb_fh_t *oldfid; + uint32_t rights; struct smb_cred scred; smbmntinfo_t *smi; smb_share_t *ssp; cred_t *oldcr; - int tmperror; int error = 0; vp = *vpp; @@ -270,14 +280,15 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) * check whether the rights are sufficient for FID reuse. */ if (np->n_fidrefs > 0 && - np->n_vcgenid == ssp->ss_vcgenid) { + (fid = np->n_fid) != NULL && + fid->fh_vcgenid == ssp->ss_vcgenid) { int upgrade = 0; if ((flag & FWRITE) && - !(np->n_rights & SA_RIGHT_FILE_WRITE_DATA)) + !(fid->fh_rights & SA_RIGHT_FILE_WRITE_DATA)) upgrade = 1; if ((flag & FREAD) && - !(np->n_rights & SA_RIGHT_FILE_READ_DATA)) + !(fid->fh_rights & SA_RIGHT_FILE_READ_DATA)) upgrade = 1; if (!upgrade) { /* @@ -286,8 +297,9 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) np->n_fidrefs++; goto have_fid; } + fid = NULL; } - rights = np->n_fidrefs ? np->n_rights : 0; + rights = (fid != NULL) ? fid->fh_rights : 0; /* * we always ask for READ_CONTROL so we can always get the @@ -306,7 +318,7 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) error = smbfs_smb_open(np, NULL, 0, 0, /* name nmlen xattr */ rights, &scred, - &fid, &rightsrcvd, &fa); + &fid, &fa); if (error) goto out; smbfs_attrcache_fa(vp, &fa); @@ -315,24 +327,10 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) * We have a new FID and access rights. */ oldfid = np->n_fid; - oldgenid = np->n_vcgenid; np->n_fid = fid; - np->n_vcgenid = ssp->ss_vcgenid; - np->n_rights = rightsrcvd; np->n_fidrefs++; - if (np->n_fidrefs > 1 && - oldgenid == ssp->ss_vcgenid) { - /* - * We already had it open (presumably because - * it was open with insufficient rights.) - * Close old wire-open. - */ - tmperror = smbfs_smb_close(ssp, - oldfid, NULL, &scred); - if (tmperror) - SMBVDEBUG("error %d closing %s\n", - tmperror, np->n_rpath); - } + if (oldfid != NULL) + smb_fh_rele(oldfid); /* * This thread did the open. @@ -478,13 +476,11 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, static void smbfs_rele_fid(smbnode_t *np, struct smb_cred *scred) { - smb_share_t *ssp; cred_t *oldcr; struct smbfs_fctx *fctx; int error; - uint16_t ofid; + smb_fh_t *ofid; - ssp = np->n_mount->smi_share; error = 0; /* Make sure we serialize for n_dirseq use. */ @@ -513,13 +509,9 @@ smbfs_rele_fid(smbnode_t *np, struct smb_cred *scred) ASSERT(np->n_fidrefs > 0); if (--np->n_fidrefs) return; - if ((ofid = np->n_fid) != SMB_FID_UNUSED) { - np->n_fid = SMB_FID_UNUSED; - /* After reconnect, n_fid is invalid */ - if (np->n_vcgenid == ssp->ss_vcgenid) { - error = smbfs_smb_close( - ssp, ofid, NULL, scred); - } + if ((ofid = np->n_fid) != NULL) { + np->n_fid = NULL; + smb_fh_rele(ofid); } break; @@ -557,20 +549,12 @@ smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, struct vattr va; smbnode_t *np; smbmntinfo_t *smi; - smb_share_t *ssp; offset_t endoff; ssize_t past_eof; int error; - caddr_t base; - u_offset_t off; - size_t n; - int on; - uint_t flags; - np = VTOSMB(vp); smi = VTOSMI(vp); - ssp = smi->smi_share; if (curproc->p_zone != smi->smi_zone_ref.zref_zone) return (EIO); @@ -632,12 +616,8 @@ smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, return (EINTR); smb_credinit(&scred, cr); - /* After reconnect, n_fid is invalid */ - if (np->n_vcgenid != ssp->ss_vcgenid) - error = ESTALE; - else - error = smb_rwuio(ssp, np->n_fid, UIO_READ, - uiop, &scred, smb_timo_read); + error = smb_rwuio(np->n_fid, UIO_READ, + uiop, &scred, smb_timo_read); smb_credrele(&scred); smbfs_rw_exit(&np->r_lkserlock); @@ -648,8 +628,15 @@ smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, return (error); } +#ifdef _KERNEL /* (else) Do I/O through segmap. */ do { + caddr_t base; + u_offset_t off; + size_t n; + int on; + uint_t flags; + off = uiop->uio_loffset & MAXBMASK; /* mapping offset */ on = uiop->uio_loffset & MAXBOFFSET; /* Relative offset */ n = MIN(MAXBSIZE - on, uiop->uio_resid); @@ -698,6 +685,9 @@ smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, } } } while (!error && uiop->uio_resid > 0); +#else // _KERNEL + error = ENOSYS; +#endif // _KERNEL /* undo adjustment of resid */ uiop->uio_resid += past_eof; @@ -715,22 +705,17 @@ smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, struct vattr va; smbnode_t *np; smbmntinfo_t *smi; - smb_share_t *ssp; offset_t endoff, limit; ssize_t past_limit; int error, timo; - caddr_t base; - u_offset_t off; - size_t n; - int on; - uint_t flags; u_offset_t last_off; size_t last_resid; +#ifdef _KERNEL uint_t bsize; +#endif np = VTOSMB(vp); smi = VTOSMI(vp); - ssp = smi->smi_share; if (curproc->p_zone != smi->smi_zone_ref.zref_zone) return (EIO); @@ -789,12 +774,14 @@ smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T) limit = MAXOFFSET_T; if (uiop->uio_loffset >= limit) { +#ifdef _KERNEL proc_t *p = ttoproc(curthread); mutex_enter(&p->p_lock); (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls, p, RCA_UNSAFE_SIGINFO); mutex_exit(&p->p_lock); +#endif // _KERNEL return (EFBIG); } if (endoff > limit) { @@ -813,7 +800,9 @@ smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, np->r_mapcnt == 0 && np->r_inmap == 0 && !vn_has_cached_data(vp))) { +#ifdef _KERNEL smbfs_fwrite: +#endif // _KERNEL if (np->r_flags & RSTALE) { last_resid = uiop->uio_resid; last_off = uiop->uio_loffset; @@ -837,12 +826,8 @@ smbfs_fwrite: return (EINTR); smb_credinit(&scred, cr); - /* After reconnect, n_fid is invalid */ - if (np->n_vcgenid != ssp->ss_vcgenid) - error = ESTALE; - else - error = smb_rwuio(ssp, np->n_fid, UIO_WRITE, - uiop, &scred, timo); + error = smb_rwuio(np->n_fid, UIO_WRITE, + uiop, &scred, timo); if (error == 0) { mutex_enter(&np->r_statelock); @@ -852,7 +837,7 @@ smbfs_fwrite: mutex_exit(&np->r_statelock); if (ioflag & (FSYNC | FDSYNC)) { /* Don't error the I/O if this fails. */ - (void) smbfs_smb_flush(np, &scred); + (void) smbfsflush(np, &scred); } } @@ -865,10 +850,17 @@ smbfs_fwrite: return (error); } +#ifdef _KERNEL /* (else) Do I/O through segmap. */ bsize = vp->v_vfsp->vfs_bsize; do { + caddr_t base; + u_offset_t off; + size_t n; + int on; + uint_t flags; + off = uiop->uio_loffset & MAXBMASK; /* mapping offset */ on = uiop->uio_loffset & MAXBOFFSET; /* Relative offset */ n = MIN(MAXBSIZE - on, uiop->uio_resid); @@ -993,6 +985,11 @@ smbfs_fwrite: goto smbfs_fwrite; } } while (!error && uiop->uio_resid > 0); +#else // _KERNEL + last_resid = uiop->uio_resid; + last_off = uiop->uio_loffset; + error = ENOSYS; +#endif // _KERNEL bottom: /* undo adjustment of resid */ @@ -1006,6 +1003,8 @@ bottom: return (error); } +#ifdef _KERNEL + /* * Like nfs_client.c: writerp() * @@ -1245,7 +1244,6 @@ smbfs_bio(struct buf *bp, int sync, cred_t *cr) struct smb_cred scred; smbnode_t *np = VTOSMB(bp->b_vp); smbmntinfo_t *smi = np->n_mount; - smb_share_t *ssp = smi->smi_share; offset_t offset; offset_t endoff; size_t count; @@ -1301,12 +1299,8 @@ smbfs_bio(struct buf *bp, int sync, cred_t *cr) if (bp->b_flags & B_READ) { - /* After reconnect, n_fid is invalid */ - if (np->n_vcgenid != ssp->ss_vcgenid) - error = ESTALE; - else - error = smb_rwuio(ssp, np->n_fid, UIO_READ, - &auio, &scred, smb_timo_read); + error = smb_rwuio(np->n_fid, UIO_READ, + &auio, &scred, smb_timo_read); /* Like NFS, only set b_error here. */ bp->b_error = error; @@ -1320,12 +1314,8 @@ smbfs_bio(struct buf *bp, int sync, cred_t *cr) } } else { - /* After reconnect, n_fid is invalid */ - if (np->n_vcgenid != ssp->ss_vcgenid) - error = ESTALE; - else - error = smb_rwuio(ssp, np->n_fid, UIO_WRITE, - &auio, &scred, smb_timo_write); + error = smb_rwuio(np->n_fid, UIO_WRITE, + &auio, &scred, smb_timo_write); /* Like NFS, only set b_error here. */ bp->b_error = error; @@ -1334,7 +1324,7 @@ smbfs_bio(struct buf *bp, int sync, cred_t *cr) if (!error && auio.uio_resid != 0) error = EIO; if (!error && sync) { - (void) smbfs_smb_flush(np, &scred); + (void) smbfsflush(np, &scred); } } @@ -1361,6 +1351,7 @@ smbfs_bio(struct buf *bp, int sync, cred_t *cr) return (error); } +#endif // _KERNEL /* * Here NFS has: nfs3write, nfs3read @@ -1588,12 +1579,12 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) { int error = 0; smbnode_t *np = VTOSMB(vp); + smbmntinfo_t *smi = np->n_mount; uint_t mask = vap->va_mask; struct timespec *mtime, *atime; struct smb_cred scred; - int cerror, modified = 0; - unsigned short fid; - int have_fid = 0; + int modified = 0; + smb_fh_t *fid = NULL; uint32_t rights = 0; uint32_t dosattr = 0; @@ -1645,9 +1636,6 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) * with a partially complete request. */ - /* Shared lock for (possible) n_fid use. */ - if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) - return (EINTR); smb_credinit(&scred, cr); /* @@ -1685,7 +1673,7 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) error, np->n_rpath); goto out; } - have_fid = 1; + ASSERT(fid != NULL); } /* @@ -1704,8 +1692,9 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) /* * Set the file size to vap->va_size. */ - ASSERT(have_fid); - error = smbfs_smb_setfsize(np, fid, vap->va_size, &scred); + ASSERT(fid != NULL); + error = smbfs_smb_setfsize(smi->smi_share, fid, + vap->va_size, &scred); if (error) { SMBVDEBUG("setsize error %d file %s\n", error, np->n_rpath); @@ -1717,6 +1706,7 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) */ mutex_enter(&np->r_statelock); np->r_size = vap->va_size; + np->n_flag |= (NFLUSHWIRE | NATTRCHANGED); mutex_exit(&np->r_statelock); modified = 1; } @@ -1733,8 +1723,8 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) /* * Always use the handle-based set attr call now. */ - ASSERT(have_fid); - error = smbfs_smb_setfattr(np, fid, + ASSERT(fid != NULL); + error = smbfs_smb_setfattr(smi->smi_share, fid, dosattr, mtime, atime, &scred); if (error) { SMBVDEBUG("set times error %d file %s\n", @@ -1745,15 +1735,10 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) } out: - if (have_fid) { - cerror = smbfs_smb_tmpclose(np, fid, &scred); - if (cerror) - SMBVDEBUG("error %d closing %s\n", - cerror, np->n_rpath); - } + if (fid != NULL) + smbfs_smb_tmpclose(np, fid); smb_credrele(&scred); - smbfs_rw_exit(&np->r_lkserlock); if (modified) { /* @@ -2022,7 +2007,7 @@ smbfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) return (EINTR); smb_credinit(&scred, cr); - error = smbfs_smb_flush(np, &scred); + error = smbfsflush(np, &scred); smb_credrele(&scred); smbfs_rw_exit(&np->r_lkserlock); @@ -2030,6 +2015,37 @@ smbfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) return (error); } +static int +smbfsflush(smbnode_t *np, struct smb_cred *scrp) +{ + struct smb_share *ssp = np->n_mount->smi_share; + smb_fh_t *fhp; + int error; + + /* Shared lock for n_fid use below. */ + ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + + if (!(np->n_flag & NFLUSHWIRE)) + return (0); + if (np->n_fidrefs == 0) + return (0); /* not open */ + if ((fhp = np->n_fid) == NULL) + return (0); + + /* After reconnect, n_fid is invalid */ + if (fhp->fh_vcgenid != ssp->ss_vcgenid) + return (ESTALE); + + error = smbfs_smb_flush(ssp, fhp, scrp); + + if (!error) { + mutex_enter(&np->r_statelock); + np->n_flag &= ~NFLUSHWIRE; + mutex_exit(&np->r_statelock); + } + return (error); +} + /* * Last reference to vnode went away. */ @@ -2119,8 +2135,8 @@ smbfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) case VREG: if (np->n_fidrefs == 0) break; - SMBVDEBUG("open file: refs %d id 0x%x path %s\n", - np->n_fidrefs, np->n_fid, np->n_rpath); + SMBVDEBUG("open file: refs %d path %s\n", + np->n_fidrefs, np->n_rpath); /* Force last close. */ np->n_fidrefs = 1; smbfs_rele_fid(np, &scred); @@ -2198,6 +2214,14 @@ smbfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, smbfs_rw_exit(&dnp->r_rwlock); + /* + * If the caller passes an invalid name here, we'll have + * error == EINVAL but want to return ENOENT. This is + * common with things like "ls foo*" with no matches. + */ + if (error == EINVAL) + error = ENOENT; + return (error); } @@ -2225,14 +2249,7 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, ASSERT(curproc->p_zone == smi->smi_zone_ref.zref_zone); -#ifdef NOT_YET - vcp = SSTOVC(smi->smi_share); - - /* XXX: Should compute this once and store it in smbmntinfo_t */ - supplen = (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) ? 255 : 12; -#else supplen = 255; -#endif /* * RWlock must be held, either reader or writer. @@ -2519,7 +2536,6 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, vsecattr_t *vsecp) { int error; - int cerror; vfs_t *vfsp; vnode_t *vp; smbnode_t *np; @@ -2531,7 +2547,7 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, const char *name = (const char *)nm; int nmlen = strlen(nm); uint32_t disp; - uint16_t fid; + smb_fh_t *fid = NULL; int xattr; vfsp = dvp->v_vfsp; @@ -2693,11 +2709,7 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, * Should use the fid to get/set the size * while we have it opened here. See above. */ - - cerror = smbfs_smb_close(smi->smi_share, fid, NULL, &scred); - if (cerror) - SMBVDEBUG("error %d closing %s\\%s\n", - cerror, dnp->n_rpath, name); + smbfs_smb_close(fid); /* * In the open case, the name may differ a little @@ -2766,14 +2778,30 @@ smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, /* Lookup the file to remove. */ error = smbfslookup(dvp, nm, &vp, cr, 0, ct); - if (error == 0) { - /* - * Do the real remove work - */ - error = smbfsremove(dvp, vp, &scred, flags); - VN_RELE(vp); + if (error != 0) + goto out; + + /* Don't allow unlink of a directory. */ + if (vp->v_type == VDIR) { + error = EPERM; + goto out; } + /* + * Do the real remove work + */ + error = smbfsremove(dvp, vp, &scred, flags); + if (error != 0) + goto out; + +#ifdef SMBFS_VNEVENT + vnevent_remove(vp, dvp, nm, ct); +#endif + +out: + if (vp != NULL) + VN_RELE(vp); + smb_credrele(&scred); smbfs_rw_exit(&dnp->r_rwlock); @@ -2808,11 +2836,11 @@ smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred, { smbnode_t *dnp = VTOSMB(dvp); smbnode_t *np = VTOSMB(vp); + smbmntinfo_t *smi = np->n_mount; char *tmpname = NULL; int tnlen; int error; - unsigned short fid; - boolean_t have_fid = B_FALSE; + smb_fh_t *fid = NULL; boolean_t renamed = B_FALSE; /* @@ -2820,10 +2848,6 @@ smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred, */ ASSERT(dnp->r_rwlock.owner == curthread); - /* Never allow link/unlink directories on SMB. */ - if (vp->v_type == VDIR) - return (EPERM); - /* * We need to flush any dirty pages which happen to * be hanging around before removing the file. This @@ -2842,10 +2866,6 @@ smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred, } } - /* Shared lock for n_fid use in smbfs_smb_setdisp etc. */ - if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) - return (EINTR); - /* * Get a file handle with delete access. * Close this FID before return. @@ -2857,16 +2877,18 @@ smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred, error, np->n_rpath); goto out; } - have_fid = B_TRUE; + ASSERT(fid != NULL); /* * If we have the file open, try to rename it to a temporary name. * If we can't rename, continue on and try setting DoC anyway. + * Unnecessary for directories. */ - if ((vp->v_count > 1) && (np->n_fidrefs > 0)) { + if (vp->v_type != VDIR && vp->v_count > 1 && np->n_fidrefs > 0) { tmpname = kmem_alloc(MAXNAMELEN, KM_SLEEP); tnlen = smbfs_newname(tmpname, MAXNAMELEN); - error = smbfs_smb_t2rename(np, tmpname, tnlen, scred, fid, 0); + error = smbfs_smb_rename(dnp, np, dnp, tmpname, tnlen, + fid, scred); if (error != 0) { SMBVDEBUG("error %d renaming %s -> %s\n", error, np->n_rpath, tmpname); @@ -2880,7 +2902,7 @@ smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred, * Mark the file as delete-on-close. If we can't, * undo what we did and err out. */ - error = smbfs_smb_setdisp(np, fid, 1, scred); + error = smbfs_smb_setdisp(smi->smi_share, fid, 1, scred); if (error != 0) { SMBVDEBUG("error %d setting DoC on %s\n", error, np->n_rpath); @@ -2897,8 +2919,8 @@ smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred, oldname = np->n_rpath + (dnp->n_rplen + 1); oldnlen = np->n_rplen - (dnp->n_rplen + 1); - err2 = smbfs_smb_t2rename(np, oldname, oldnlen, - scred, fid, 0); + err2 = smbfs_smb_rename(dnp, np, dnp, oldname, oldnlen, + fid, scred); SMBVDEBUG("error %d un-renaming %s -> %s\n", err2, tmpname, np->n_rpath); } @@ -2906,19 +2928,14 @@ smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred, goto out; } /* Done! */ + smbfs_attrcache_remove(np); smbfs_attrcache_prune(np); -#ifdef SMBFS_VNEVENT - vnevent_remove(vp, dvp, nm, ct); -#endif - out: if (tmpname != NULL) kmem_free(tmpname, MAXNAMELEN); - - if (have_fid) - (void) smbfs_smb_tmpclose(np, fid, scred); - smbfs_rw_exit(&np->r_lkserlock); + if (fid != NULL) + smbfs_smb_tmpclose(np, fid); if (error == 0) { /* Keep lookup from finding this node anymore. */ @@ -3050,6 +3067,7 @@ smbfsrename(vnode_t *odvp, vnode_t *ovp, vnode_t *ndvp, char *nnm, vnode_t *nvp = NULL; int error; int nvp_locked = 0; + smb_fh_t *fid = NULL; /* Things our caller should have checked. */ ASSERT(curproc->p_zone == VTOSMI(odvp)->smi_zone_ref.zref_zone); @@ -3130,8 +3148,23 @@ smbfsrename(vnode_t *odvp, vnode_t *ovp, vnode_t *ndvp, char *nnm, nvp = NULL; } /* nvp */ + /* + * Get a file handle with delete access. + * Close this FID before return. + */ + error = smbfs_smb_tmpopen(onp, STD_RIGHT_DELETE_ACCESS, + scred, &fid); + if (error) { + SMBVDEBUG("error %d opening %s\n", + error, onp->n_rpath); + goto out; + } + smbfs_attrcache_remove(onp); - error = smbfs_smb_rename(onp, ndnp, nnm, strlen(nnm), scred); + error = smbfs_smb_rename(odnp, onp, ndnp, nnm, strlen(nnm), + fid, scred); + + smbfs_smb_tmpclose(onp, fid); /* * If the old name should no longer exist, @@ -3172,7 +3205,7 @@ smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, struct smbfattr fattr; const char *name = (const char *) nm; int nmlen = strlen(name); - int error, hiderr; + int error; if (curproc->p_zone != smi->smi_zone_ref.zref_zone) return (EPERM); @@ -3213,10 +3246,6 @@ smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, if (error) goto out; - if (name[0] == '.') - if ((hiderr = smbfs_smb_hideit(VTOSMB(vp), NULL, 0, &scred))) - SMBVDEBUG("hide failure %d\n", hiderr); - /* Success! */ *vpp = vp; error = 0; @@ -3240,12 +3269,12 @@ static int smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, caller_context_t *ct, int flags) { + struct smb_cred scred; vnode_t *vp = NULL; int vp_locked = 0; struct smbmntinfo *smi = VTOSMI(dvp); struct smbnode *dnp = VTOSMB(dvp); struct smbnode *np; - struct smb_cred scred; int error; if (curproc->p_zone != smi->smi_zone_ref.zref_zone) @@ -3254,17 +3283,16 @@ smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED) return (EIO); - if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp))) - return (EINTR); - smb_credinit(&scred, cr); - /* - * Require w/x access in the containing directory. - * Server handles all other access checks. + * Verify access to the dirctory. */ - error = smbfs_access(dvp, VEXEC|VWRITE, 0, cr, ct); + error = smbfs_access(dvp, VWRITE|VEXEC, 0, cr, ct); if (error) - goto out; + return (error); + + if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp))) + return (EINTR); + smb_credinit(&scred, cr); /* * First lookup the entry to be removed. @@ -3297,23 +3325,17 @@ smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, goto out; } - smbfs_attrcache_remove(np); - error = smbfs_smb_rmdir(np, &scred); - /* - * Similar to smbfs_remove + * Do the real rmdir work */ - switch (error) { - case 0: - case ENOENT: - case ENOTDIR: - smbfs_attrcache_prune(np); - break; - } - + error = smbfsremove(dvp, vp, &scred, flags); if (error) goto out; +#ifdef SMBFS_VNEVENT + vnevent_rmdir(vp, dvp, nm, ct); +#endif + mutex_enter(&np->r_statelock); dnp->n_flag |= NMODIFIED; mutex_exit(&np->r_statelock); @@ -3692,6 +3714,8 @@ smbfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) /* mmap support ******************************************************** */ +#ifdef _KERNEL + #ifdef DEBUG static int smbfs_lostpage = 0; /* number of times we lost original page */ #endif @@ -4011,6 +4035,8 @@ again: * No read-ahead in smbfs yet. */ +#endif // _KERNEL + /* * Flags are composed of {B_INVAL, B_FREE, B_DONTNEED, B_FORCE} * If len == 0, do from off to EOF. @@ -4026,6 +4052,7 @@ static int smbfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, caller_context_t *ct) { +#ifdef _KERNEL smbnode_t *np; smbmntinfo_t *smi; page_t *pp; @@ -4157,8 +4184,14 @@ smbfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, } return (error); + +#else // _KERNEL + return (ENOSYS); +#endif // _KERNEL } +#ifdef _KERNEL + /* * Write out a single page, possibly klustering adjacent dirty pages. * @@ -4331,6 +4364,9 @@ smbfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, size_t *lenp, return (error); } +#endif // _KERNEL + + /* * NFS has this in nfs_client.c (shared by v2,v3,...) * We have it here so smbfs_putapage can be file scope. @@ -4355,8 +4391,10 @@ smbfs_invalidate_pages(vnode_t *vp, u_offset_t off, cred_t *cr) /* Here NFSv3 has np->r_truncaddr = off; */ mutex_exit(&np->r_statelock); +#ifdef _KERNEL (void) pvn_vplist_dirty(vp, off, smbfs_putapage, B_INVAL | B_TRUNC, cr); +#endif // _KERNEL mutex_enter(&np->r_statelock); np->r_flags &= ~RTRUNCATE; @@ -4364,6 +4402,8 @@ smbfs_invalidate_pages(vnode_t *vp, u_offset_t off, cred_t *cr) mutex_exit(&np->r_statelock); } +#ifdef _KERNEL + /* Like nfs3_map */ /* ARGSUSED */ @@ -4659,6 +4699,8 @@ smbfs_delmap_async(void *varg) /* No smbfs_pageio() or smbfs_dispose() ops. */ +#endif // _KERNEL + /* misc. ******************************************************** */ @@ -4961,11 +5003,13 @@ const fs_operation_def_t smbfs_vnodeops_template[] = { VOPNAME_FRLOCK, { .vop_frlock = smbfs_frlock }, VOPNAME_SPACE, { .vop_space = smbfs_space }, VOPNAME_REALVP, { .vop_realvp = smbfs_realvp }, +#ifdef _KERNEL VOPNAME_GETPAGE, { .vop_getpage = smbfs_getpage }, VOPNAME_PUTPAGE, { .vop_putpage = smbfs_putpage }, VOPNAME_MAP, { .vop_map = smbfs_map }, VOPNAME_ADDMAP, { .vop_addmap = smbfs_addmap }, VOPNAME_DELMAP, { .vop_delmap = smbfs_delmap }, +#endif // _KERNEL VOPNAME_PATHCONF, { .vop_pathconf = smbfs_pathconf }, VOPNAME_SETSECATTR, { .vop_setsecattr = smbfs_setsecattr }, VOPNAME_GETSECATTR, { .vop_getsecattr = smbfs_getsecattr }, diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c index 75241cc1f0..323b8c8d10 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -29,6 +30,7 @@ */ #include <sys/systm.h> +#include <sys/inttypes.h> #include <sys/cred.h> #include <sys/vnode.h> #include <sys/vfs.h> @@ -43,7 +45,9 @@ #include <sys/u8_textprep.h> #include <netsmb/smb_osdep.h> + #include <netsmb/smb.h> +#include <netsmb/smb2.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_subr.h> #include <netsmb/smb_rq.h> @@ -289,32 +293,20 @@ smbfs_xa_getfattr(struct smbnode *xnp, struct smbfattr *fap, } /* - * Fetch the entire attribute list here in findopen. - * Will parse the results in findnext. + * Actually go OtW to get the list of "streams". * * This is called on the XATTR directory, so we * have to get the (real) parent object first. */ -/* ARGSUSED */ -int -smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp, - const char *wildcard, int wclen) +static int +smbfs_xa_get_streaminfo(struct smbfs_fctx *ctx) { vnode_t *pvp; /* parent */ smbnode_t *pnp; - struct smb_t2rq *t2p; - struct smb_vc *vcp = SSTOVC(ctx->f_ssp); - struct mbchain *mbp; + smbnode_t *dnp = ctx->f_dnp; + struct mdchain *mdp; int error; - ASSERT(dnp->n_flag & N_XATTR); - - ctx->f_type = ft_XA; - ctx->f_namesz = SMB_MAXFNAMELEN + 1; - if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) - ctx->f_namesz *= 2; - ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP); - error = smbfs_xa_parent(SMBTOV(dnp), &pvp); if (error) return (error); @@ -322,40 +314,31 @@ smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp, /* Note: pvp has a VN_HOLD */ pnp = VTOSMB(pvp); - if (ctx->f_t2) { - smb_t2_done(ctx->f_t2); - ctx->f_t2 = NULL; - } + /* + * Get stream info into f_mdchain + */ + mdp = &ctx->f_mdchain; + md_done(mdp); - error = smb_t2_alloc(SSTOCP(ctx->f_ssp), - SMB_TRANS2_QUERY_PATH_INFORMATION, - ctx->f_scred, &t2p); + if (SSTOVC(ctx->f_ssp)->vc_flags & SMBV_SMB2) { + error = smbfs_smb2_get_streaminfo(pnp, mdp, ctx->f_scred); + } else { + error = smbfs_smb1_get_streaminfo(pnp, mdp, ctx->f_scred); + } if (error) goto out; - ctx->f_t2 = t2p; - mbp = &t2p->t2_tparam; - (void) mb_init(mbp); - (void) mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO); - (void) mb_put_uint32le(mbp, 0); - error = smbfs_fullpath(mbp, vcp, pnp, NULL, NULL, 0); - if (error) - goto out; - t2p->t2_maxpcount = 2; - t2p->t2_maxdcount = INT16_MAX; - error = smb_t2_request(t2p); - if (error) { - if (t2p->t2_sr_error == NT_STATUS_INVALID_PARAMETER) - error = ENOTSUP; - } /* - * No returned parameters to parse. - * Returned data are in t2_rdata, - * which we'll parse in _findnext. - * However, save the wildcard. + * Have stream info in ctx->f_mdchain + * Initialize buffer length, position. */ - ctx->f_wildcard = wildcard; - ctx->f_wclen = wclen; + ctx->f_left = m_fixhdr(mdp->md_top); + ctx->f_eofs = 0; + + /* + * After one successful call, we're at EOF. + */ + ctx->f_flags |= SMBFS_RDD_EOF; out: VN_RELE(pvp); @@ -363,83 +346,62 @@ out: } /* - * Get the next name in an XATTR directory into f_name + * Get a buffer of directory entries (if we don't already have + * some remaining in the current buffer) then decode one. */ -/* ARGSUSED */ int -smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit) +smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp, + const char *wildcard, int wclen) { - struct mdchain *mdp; - struct smb_t2rq *t2p; - uint32_t size, next; - uint64_t llongint; - int error, skip, used, nmlen; - t2p = ctx->f_t2; - mdp = &t2p->t2_rdata; + ASSERT(dnp->n_flag & N_XATTR); - if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) { - ASSERT(ctx->f_wildcard); - SMBVDEBUG("wildcard: %s\n", ctx->f_wildcard); - } + ctx->f_type = ft_XA; + ctx->f_namesz = SMB_MAXFNAMELEN + 1; + ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP); + ctx->f_infolevel = FileStreamInformation; + ctx->f_wildcard = wildcard; + ctx->f_wclen = wclen; -again: - if (ctx->f_flags & SMBFS_RDD_EOF) - return (ENOENT); - - /* Parse FILE_STREAM_INFORMATION */ - if ((error = md_get_uint32le(mdp, &next)) != 0) /* offset to */ - return (ENOENT); - if ((error = md_get_uint32le(mdp, &size)) != 0) /* name len */ - return (ENOENT); - (void) md_get_uint64le(mdp, &llongint); /* file size */ - ctx->f_attr.fa_size = llongint; - (void) md_get_uint64le(mdp, NULL); /* alloc. size */ - used = 4 + 4 + 8 + 8; /* how much we consumed */ + return (0); +} - /* - * Copy the string, but skip the first char (":") - * Watch out for zero-length strings here. - */ - if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { - if (size >= 2) { - size -= 2; used += 2; - (void) md_get_uint16le(mdp, NULL); - } - nmlen = min(size, SMB_MAXFNAMELEN * 2); - } else { - if (size >= 1) { - size -= 1; used += 1; - (void) md_get_uint8(mdp, NULL); - } - nmlen = min(size, SMB_MAXFNAMELEN); - } - ASSERT(nmlen < ctx->f_namesz); - ctx->f_nmlen = nmlen; - error = md_get_mem(mdp, ctx->f_name, nmlen, MB_MSYSTEM); - if (error) - return (error); - used += nmlen; +/* + * Get the next name in an XATTR directory + */ +/* ARGSUSED */ +int +smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit) +{ + int error; /* - * Convert UCS-2 to UTF-8 + * If we've scanned to the end of the current buffer + * try to read anohther buffer of dir entries. + * Treat anything less than 8 bytes as an "empty" + * buffer to ensure we can read something. + * (There may be up to 8 bytes of padding.) */ - smbfs_fname_tolocal(ctx); - if (nmlen) - SMBVDEBUG("name: %s\n", ctx->f_name); - else - SMBVDEBUG("null name!\n"); +again: + if ((ctx->f_eofs + 8) > ctx->f_left) { + /* Scanned the whole buffer. */ + if (ctx->f_flags & SMBFS_RDD_EOF) + return (ENOENT); + ctx->f_limit = limit; + error = smbfs_xa_get_streaminfo(ctx); + if (error) + return (error); + ctx->f_otws++; + } /* - * Skip padding until next offset + * Decode one entry, advance f_eofs */ - if (next > used) { - skip = next - used; - (void) md_get_mem(mdp, NULL, skip, MB_MSYSTEM); - } - if (next == 0) - ctx->f_flags |= SMBFS_RDD_EOF; + error = smbfs_decode_dirent(ctx); + if (error) + return (error); + SMBVDEBUG("name: %s\n", ctx->f_name); /* * Chop off the trailing ":$DATA" @@ -462,8 +424,10 @@ again: goto again; /* - * If this is a lookup of a specific name, - * skip past any non-matching names. + * When called by lookup, we'll have the "single" flag, + * and a name with no wildcards. We need to filter here + * because smbfs_xa_get_streaminfo() gets ALL the names + * (not just those matching our pattern). */ if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) { if (ctx->f_wclen != ctx->f_nmlen) @@ -488,8 +452,6 @@ smbfs_xa_findclose(struct smbfs_fctx *ctx) if (ctx->f_name) kmem_free(ctx->f_name, ctx->f_namesz); - if (ctx->f_t2) - smb_t2_done(ctx->f_t2); return (0); } diff --git a/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c b/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c index f4a9f9a948..4ed5cba79c 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_ioctl.c @@ -22,7 +22,7 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -31,7 +31,7 @@ */ #include <smbsrv/smb2_kproto.h> -#include <smbsrv/winioctl.h> +#include <smb/winioctl.h> struct smb2_ioctbl_ent { uint32_t te_code; diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c index ffe230f888..6176a8b002 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c +++ b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> @@ -30,7 +30,7 @@ #include <smbsrv/string.h> #include <smbsrv/nmpipes.h> #include <smbsrv/mailslot.h> -#include <smbsrv/winioctl.h> +#include <smb/winioctl.h> /* * count of bytes in server response packet @@ -943,7 +943,7 @@ smb_trans_net_share_enum(struct smb_request *sr, struct smb_xa *xa) data_off, /* Data offset from header start */ data_disp, /* Data displacement */ n_setup, /* suwcnt */ - &xa->rep_setup_mb, /* setup[] */ + &xa->rep_setup_mb, /* setup[] */ tot_packet_bytes, /* Total data bytes */ param_pad, &xa->rep_param_mb, diff --git a/usr/src/uts/common/fs/smbsrv/smb_dfs.c b/usr/src/uts/common/fs/smbsrv/smb_dfs.c index 495965859b..03c565e9e4 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_dfs.c +++ b/usr/src/uts/common/fs/smbsrv/smb_dfs.c @@ -22,13 +22,13 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_dfs.h> #include <smbsrv/smb_door.h> -#include <smbsrv/winioctl.h> +#include <smb/winioctl.h> /* * Get Referral response header flags diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c index c0ab285bd5..f355e314bc 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c @@ -20,11 +20,11 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> -#include <smbsrv/winioctl.h> +#include <smb/winioctl.h> static uint32_t smb_nt_trans_ioctl_noop(smb_request_t *, smb_xa_t *); diff --git a/usr/src/uts/common/fs/smbsrv/smb_opipe.c b/usr/src/uts/common/fs/smbsrv/smb_opipe.c index 273a2f7297..c08cba0ac6 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_opipe.c +++ b/usr/src/uts/common/fs/smbsrv/smb_opipe.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* @@ -35,7 +35,7 @@ #include <sys/filio.h> #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_xdr.h> -#include <smbsrv/winioctl.h> +#include <smb/winioctl.h> static uint32_t smb_opipe_transceive(smb_request_t *, smb_fsctl_t *); diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c index df874ffe1d..918bf78727 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_dfs.c @@ -22,11 +22,11 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> -#include <smbsrv/winioctl.h> +#include <smb/winioctl.h> /* * [MS-CIFS] diff --git a/usr/src/uts/common/fs/smbsrv/smb_vss.c b/usr/src/uts/common/fs/smbsrv/smb_vss.c index 211bc467a8..72657ae3be 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_vss.c +++ b/usr/src/uts/common/fs/smbsrv/smb_vss.c @@ -41,7 +41,7 @@ #include <smbsrv/smb_kproto.h> #include <smbsrv/string.h> -#include <smbsrv/winioctl.h> +#include <smb/winioctl.h> #include <smbsrv/smb_door.h> /* Size of the token on the wire due to encoding */ diff --git a/usr/src/uts/common/io/aac/aac.c b/usr/src/uts/common/io/aac/aac.c index 36c48d96e6..f9d3672963 100644 --- a/usr/src/uts/common/io/aac/aac.c +++ b/usr/src/uts/common/io/aac/aac.c @@ -255,8 +255,8 @@ static int aac_add_intrs(struct aac_softstate *); static void aac_remove_intrs(struct aac_softstate *); static int aac_enable_intrs(struct aac_softstate *); static int aac_disable_intrs(struct aac_softstate *); -static uint_t aac_intr_old(caddr_t); -static uint_t aac_intr_new(caddr_t); +static uint_t aac_intr_old(caddr_t, caddr_t); +static uint_t aac_intr_new(caddr_t, caddr_t); static uint_t aac_softintr(caddr_t); /* @@ -1416,7 +1416,7 @@ aac_process_intr_new(struct aac_softstate *softs) } static uint_t -aac_intr_new(caddr_t arg) +aac_intr_new(caddr_t arg, caddr_t arg1 __unused) { struct aac_softstate *softs = (void *)arg; uint_t rval; @@ -1555,7 +1555,7 @@ aac_process_intr_old(struct aac_softstate *softs) } static uint_t -aac_intr_old(caddr_t arg) +aac_intr_old(caddr_t arg, caddr_t arg1 __unused) { struct aac_softstate *softs = (void *)arg; int rval; @@ -1674,7 +1674,7 @@ aac_add_intrs(struct aac_softstate *softs) ddi_intr_handler_t *aac_intr; actual = softs->intr_cnt; - aac_intr = (ddi_intr_handler_t *)((softs->flags & AAC_FLAGS_NEW_COMM) ? + aac_intr = ((softs->flags & AAC_FLAGS_NEW_COMM) ? aac_intr_new : aac_intr_old); /* Call ddi_intr_add_handler() */ diff --git a/usr/src/uts/common/netsmb/mchain.h b/usr/src/uts/common/netsmb/mchain.h index c5c8512fd7..156671999b 100644 --- a/usr/src/uts/common/netsmb/mchain.h +++ b/usr/src/uts/common/netsmb/mchain.h @@ -35,6 +35,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _MCHAIN_H_ @@ -70,10 +72,10 @@ #else /* (BYTE_ORDER == LITTLE_ENDIAN) */ /* little-endian values on big-endian (swap) */ -#define letohs(x) BSWAP_16(x) -#define htoles(x) BSWAP_16(x) -#define letohl(x) BSWAP_32(x) -#define htolel(x) BSWAP_32(x) +#define letohs(x) BSWAP_16(x) +#define htoles(x) BSWAP_16(x) +#define letohl(x) BSWAP_32(x) +#define htolel(x) BSWAP_32(x) #define letohq(x) BSWAP_64(x) #define htoleq(x) BSWAP_64(x) @@ -93,7 +95,7 @@ * wrappers for streams functions. See: subr_mchain.c */ -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) /* * BSD-style mbuf "shim" for kernel code. Note, this @@ -103,6 +105,7 @@ */ #include <sys/stream.h> /* mblk_t */ +#include <sys/strsun.h> /* MBLKL */ typedef mblk_t mbuf_t; /* BEGIN CSTYLED */ @@ -112,9 +115,9 @@ typedef mblk_t mbuf_t; * m_data ... (m_data + m_len) * In Unix STREAMS, the mblk payload is: * b_rptr ... b_wptr - * + * * Here are some handy conversion notes: - * + * * struct mbuf struct mblk * m->m_next m->b_cont * m->m_nextpkt m->b_next @@ -124,7 +127,7 @@ typedef mblk_t mbuf_t; * &m->m_dat[MLEN] m->b_datap->db_lim * M_TRAILINGSPACE(m) MBLKTAIL(m) * m_freem(m) freemsg(m) - * + * * Note that mbufs chains also have a special "packet" header, * which has the length of the whole message. In STREAMS one * typically just calls msgdsize(m) to get that. @@ -177,7 +180,7 @@ void m_freem(mbuf_t *); #define MB_MZERO 3 /* bzero(), mb_put_mem only */ #define MB_MCUSTOM 4 /* use an user defined function */ -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) struct mbchain { mblk_t *mb_top; @@ -224,6 +227,7 @@ void mb_initm(mbchain_t *, mbuf_t *); void mb_done(mbchain_t *); void *mb_reserve(mbchain_t *, int size); +int mb_put_align8(mbchain_t *mbp); int mb_put_padbyte(mbchain_t *mbp); int mb_put_uint8(mbchain_t *, uint8_t); int mb_put_uint16be(mbchain_t *, uint16_t); @@ -234,6 +238,7 @@ int mb_put_uint64be(mbchain_t *, uint64_t); int mb_put_uint64le(mbchain_t *, uint64_t); int mb_put_mem(mbchain_t *, const void *, int, int); int mb_put_mbuf(mbchain_t *, mbuf_t *); +int mb_put_mbchain(mbchain_t *, mbchain_t *); int md_init(mdchain_t *mdp); void md_initm(mdchain_t *mbp, mbuf_t *m); @@ -248,5 +253,7 @@ int md_get_uint64be(mdchain_t *, uint64_t *); int md_get_uint64le(mdchain_t *, uint64_t *); int md_get_mem(mdchain_t *, void *, int, int); int md_get_mbuf(mdchain_t *, int, mbuf_t **); +int md_seek(mdchain_t *, uint32_t); +uint32_t md_tell(mdchain_t *); #endif /* !_MCHAIN_H_ */ diff --git a/usr/src/uts/common/netsmb/smb.h b/usr/src/uts/common/netsmb/smb.h index e3bfc5144c..b57be5bbfe 100644 --- a/usr/src/uts/common/netsmb/smb.h +++ b/usr/src/uts/common/netsmb/smb.h @@ -40,6 +40,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _NETSMB_SMB_H_ @@ -66,8 +67,8 @@ enum smb_dialects { SMB_DIALECT_LANMAN1_0, /* MICROSOFT NETWORKS 3.0, LANMAN1.0 */ SMB_DIALECT_LANMAN2_0, /* LM1.2X002, DOS LM1.2X002, Samba */ SMB_DIALECT_LANMAN2_1, /* DOS LANMAN2.1, LANMAN2.1 */ - SMB_DIALECT_NTLM0_12 /* NT LM 0.12, Windows for Workgroups */ - /* 3.1a, * NT LANMAN 1.0 */ + SMB_DIALECT_NTLM0_12, /* NT LM 0.12, etc. */ + SMB_DIALECT_SMB2_FF /* SMB1 negotiate to SMB2 */ }; /* @@ -82,11 +83,18 @@ enum smb_dialects { /* * SMB header */ + #define SMB_SIGNATURE "\xFFSMB" #define SMB_SIGLEN 4 #define SMB_HDRCMD(p) (*((uchar_t *)(p) + SMB_SIGLEN)) #define SMB_HDRMID(p) (*(ushort_t *)((uchar_t *)(p) + 30)) +#define SMB_HDR_OFF_MID 30 #define SMB_HDRLEN 32 + +#define SMB_HDR_V1 0xFF +#define SMB_HDR_V2 0xFE +#define SMB_HDR_V3E 0xFD /* SMB3 encrypted */ + /* * bits in the smb_flags field */ @@ -151,6 +159,25 @@ enum smb_dialects { #define SMB_CAP_COMPRESSED_DATA 0x40000000 #define SMB_CAP_EXT_SECURITY 0x80000000 +/* SMB_COM_TREE_CONNECT_ANDX flags. See [MS-SMB] for a complete description. */ +#define TREE_CONNECT_ANDX_DISCONNECT_TID 0x0001 +#define TREE_CONNECT_ANDX_EXTENDED_SIGNATURES 0x0004 +#define TREE_CONNECT_ANDX_EXTENDED_RESPONSE 0x0008 + +/* + * SMB_COM_TREE_CONNECT_ANDX optional support flags. See [MS-SMB] for a + * complete description. + */ +#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* supports SearchAttributes */ +#define SMB_SHARE_IS_IN_DFS 0x0002 /* share is managed by DFS */ +#define SMB_CSC_MASK 0x000C /* Offline-caching bits. */ +#define SMB_UNIQUE_FILE_NAME 0x0010 /* Long file names only */ +#define SMB_EXTENDED_SIGNATURES 0x0020 /* Signing key protection. */ +/* See [MS-SMB] for a complete description of SMB_CSC_MASK bits. */ +#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 +#define SMB_CSC_CACHE_AUTO_REINT 0x0004 +#define SMB_CSC_CACHE_VDO 0x0008 + /* * File attributes */ @@ -372,6 +399,7 @@ enum smb_dialects { #define SMB_QFS_DEVICE_INFO 0x104 #define SMB_QFS_ATTRIBUTE_INFO 0x105 #define SMB_QFS_UNIX_INFO 0x200 +#define SMB_QFS_POSIX_WHOAMI 0x202 #define SMB_QFS_MAC_FS_INFO 0x301 #define SMB_QFS_VOLUME_INFORMATION 1001 #define SMB_QFS_SIZE_INFORMATION 1003 @@ -381,6 +409,11 @@ enum smb_dialects { #define SMB_QFS_FULL_SIZE_INFORMATION 1007 #define SMB_QFS_OBJECTID_INFORMATION 1008 +/* + * NT Notify Change Compeletion Filter + * NT Notify Actions + * (We don't use these.) + */ /* * SMB_QFS_ATTRIBUTE_INFO bits. @@ -403,6 +436,7 @@ enum smb_dialects { #define FILE_SUPPORTS_OBJECT_IDS 0x00010000 #define FILE_SUPPORTS_ENCRYPTION 0x00020000 #define FILE_NAMED_STREAMS 0x00040000 +#define FILE_READ_ONLY_VOLUME 0x00080000 /* * SMB_TRANS2_QUERY_PATH levels @@ -424,9 +458,12 @@ enum smb_dialects { #define SMB_QFILEINFO_COMPRESSION_INFO 0x10b #define SMB_QFILEINFO_UNIX_BASIC 0x200 #define SMB_QFILEINFO_UNIX_LINK 0x201 +#define SMB_QFILEINFO_POSIX_ACL 0x204 +#define SMB_QFILEINFO_UNIX_INFO2 0x20B #define SMB_QFILEINFO_MAC_DT_GET_APPL 0x306 #define SMB_QFILEINFO_MAC_DT_GET_ICON 0x307 #define SMB_QFILEINFO_MAC_DT_GET_ICON_INFO 0x308 +#define SMB_QFILEINFO_MAC_SPOTLIGHT 0x310 #define SMB_QFILEINFO_BASIC_INFORMATION 1004 #define SMB_QFILEINFO_STANDARD_INFORMATION 1005 #define SMB_QFILEINFO_INTERNAL_INFORMATION 1006 @@ -454,6 +491,9 @@ enum smb_dialects { #define SMB_FIND_NAME_INFO 0x103 #define SMB_FIND_BOTH_DIRECTORY_INFO 0x104 #define SMB_FIND_UNIX_INFO 0x200 +/* Transact 2 Find First levels */ +#define SMB_FIND_FILE_UNIX 0x202 +#define SMB_FIND_FILE_UNIX_INFO2 0x20B /* UNIX File Info2 */ /* * Selectors for NT_TRANSACT_QUERY_SECURITY_DESC and @@ -707,6 +747,9 @@ typedef struct ntsid ntsid_t; #define SMB_SFILEINFO_UNIX_BASIC 0x200 #define SMB_SFILEINFO_UNIX_LINK 0x201 #define SMB_SFILEINFO_UNIX_HLINK 0x203 +#define SMB_SFILEINFO_POSIX_ACL 0x204 +#define SMB_SFILEINFO_POSIX_UNLINK 0x20A +#define SMB_SFILEINFO_UNIX_INFO2 0x20B #define SMB_SFILEINFO_DIRECTORY_INFORMATION 1001 #define SMB_SFILEINFO_FULL_DIRECTORY_INFORMATION 1002 #define SMB_SFILEINFO_BOTH_DIRECTORY_INFORMATION 1003 @@ -816,4 +859,19 @@ typedef struct ntlmv2_namehdr ntlmv2_namehdr_t; #define STYPE_TEMPORARY 0x40000000 #define STYPE_HIDDEN 0x80000000 +/* + * Characters that are not allowed in an SMB file name component. + * From MSDN: Naming Files, Paths, ... + * < (less than) + * > (greater than) + * : (colon) + * " (double quote) + * / (forward slash) + * \ (backslash) + * | (vertical bar or pipe) + * ? (question mark) + * * (asterisk) + */ +#define SMB_FILENAME_INVALID_CHARS "<>:\"/\\|?*" + #endif /* _NETSMB_SMB_H_ */ diff --git a/usr/src/uts/common/netsmb/smb2.h b/usr/src/uts/common/netsmb/smb2.h new file mode 100644 index 0000000000..abae5e8063 --- /dev/null +++ b/usr/src/uts/common/netsmb/smb2.h @@ -0,0 +1,465 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _NETSMB_SMB2_H +#define _NETSMB_SMB2_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define SMB2_PROTOCOL_ID { 0xFE, 'S', 'M', 'B' } +#define SMB2_HDR_SIZE 64 +#define SMB2_HDRLEN SMB2_HDR_SIZE + +/* + * SMB2 header command codes. + * These are uint16_t on the wire. + */ +typedef enum { + SMB2_NEGOTIATE = 0, + SMB2_SESSION_SETUP, + SMB2_LOGOFF, + SMB2_TREE_CONNECT, + SMB2_TREE_DISCONNECT, + SMB2_CREATE, + SMB2_CLOSE, + SMB2_FLUSH, + SMB2_READ, + SMB2_WRITE, + SMB2_LOCK, + SMB2_IOCTL, + SMB2_CANCEL, + SMB2_ECHO, + SMB2_QUERY_DIRECTORY, + SMB2_CHANGE_NOTIFY, + SMB2_QUERY_INFO, + SMB2_SET_INFO, + SMB2_OPLOCK_BREAK, + /* + * The above (oplock break) is the last real SMB2 op-code. + * We use one more slot to represent invalid commands, and + * the final enum value is used for array sizes. Keep last! + */ + SMB2_INVALID_CMD, + SMB2__NCMDS +} SMB2_cmd_code; + +/* + * SMB2 header flags. + */ + +/* + * SERVER_TO_REDIR + * When set, indicates the message is a response rather than + * a request. This MUST be set on responses sent from the + * server to the client, and MUST NOT be set on requests + * sent from the client to the server. + */ +#define SMB2_FLAGS_SERVER_TO_REDIR 0x00000001 + +/* + * ASYNC_COMMAND + * When set, indicates that this is an ASYNC SMB2 header. + * Always set for headers of the form described in this + * section. + */ +#define SMB2_FLAGS_ASYNC_COMMAND 0x00000002 + +/* + * RELATED_OPERATIONS + * When set in an SMB2 request, indicates that this request + * is a related operation in a compounded request chain. + * [MS-SMB2 sec. 3.2.4.1.4] + * + * When set in an SMB2 compound response, indicates that + * the request corresponding to this response was part of a + * related operation in a compounded request chain. + * [MS-SMB2 sec. 3.3.5.2.7.2] + */ +#define SMB2_FLAGS_RELATED_OPERATIONS 0x00000004 + +/* + * SIGNED + * When set, indicates that this packet has been signed. + * [MS-SMB2 3.1.5.1] + */ +#define SMB2_FLAGS_SIGNED 0x00000008 + +/* + * [MS-SMB2] 3.2.5.3.1 The SessionKey MUST be set to the + * first 16 bytes of the cryptographic key from GSSAPI. + * (Padded with zeros if the GSSAPI key is shorter.) + */ +#define SMB2_SESSION_KEY_LEN 16 + +/* + * DFS_OPERATIONS + * When set, indicates that this command is a Distributed + * File System (DFS) operation. [MS-SMB2 3.3.5.9] + */ +#define SMB2_FLAGS_DFS_OPERATIONS 0x10000000 + +/* + * REPLAY_OPERATION + * This flag is only valid for the SMB 3.0 dialect. When set, + * it indicates that this command is a replay operation. + * The client MUST ignore this bit on receipt. + */ +#define SMB2_FLAGS_REPLAY_OPERATION 0x20000000 + +/* + * SMB2 Netgotiate [MS-SMB2 2.2.3] + */ + +#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x01 +#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x02 + +#define SMB2_CAP_DFS 0x00000001 + +/* Added with SMB2.1 */ +#define SMB2_CAP_DFS 0x00000001 +#define SMB2_CAP_LEASING 0x00000002 +/* + * LARGE_MTU: + * When set, indicates that the client supports multi-credit operations. + */ +#define SMB2_CAP_LARGE_MTU 0x00000004 + +/* Added with SMB3.0 */ +#define SMB2_CAP_MULTI_CHANNEL 0x00000008 +#define SMB2_CAP_PERSISTENT_HANDLES 0x00000010 +#define SMB2_CAP_DIRECTORY_LEASING 0x00000020 +#define SMB2_CAP_ENCRYPTION 0x00000040 + +/* SMB2 session flags */ +#define SMB2_SESSION_FLAG_IS_GUEST 0x0001 +#define SMB2_SESSION_FLAG_IS_NULL 0x0002 +#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004 + +/* + * SMB2 Tree connect, disconnect + */ + +/* SMB2 sharetype flags */ +#define SMB2_SHARE_TYPE_DISK 0x1 +#define SMB2_SHARE_TYPE_PIPE 0x2 +#define SMB2_SHARE_TYPE_PRINT 0x3 + +/* SMB2 share flags */ +#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 +#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 +#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 +#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 +#define SMB2_SHAREFLAG_DFS 0x00000001 +#define SMB2_SHAREFLAG_DFS_ROOT 0x00000002 +#define SMB2_SHAREFLAG_RESTRICT_EXCLUSIVE_OPENS 0x00000100 +#define SMB2_SHAREFLAG_FORCE_SHARED_DELETE 0x00000200 +#define SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING 0x00000400 +#define SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 +#define SMB2_SHAREFLAG_FORCE_LEVELII_OPLOCK 0x00001000 +/* SMB 3.0 */ +#define SMB2_SHAREFLAG_ENABLE_HASH_V1 0x00002000 +#define SMB2_SHAREFLAG_ENABLE_HASH_V2 0x00004000 +#define SMB2_SHAREFLAG_ENCRYPT_DATA 0x00008000 + +/* SMB2 share capabilities */ +#define SMB2_SHARE_CAP_DFS 0x00000008 +/* SMB 3.0 */ +#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY 0x00000010 +#define SMB2_SHARE_CAP_SCALEOUT 0x00000020 +#define SMB2_SHARE_CAP_CLUSTER 0x00000040 + +/* + * SMB2 Create (open) + */ + +/* SMB2 requested oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF + +/* SMB2 impersonation levels */ +#define SMB2_IMPERSONATION_ANONYMOUS 0x00 +#define SMB2_IMPERSONATION_IDENTIFICATION 0x01 +#define SMB2_IMPERSONATION_IMPERSONATION 0x02 +#define SMB2_IMPERSONATION_DELEGATE 0x03 + +/* + * Note: ShareAccess, CreateDispositon, CreateOptions, + * all use the same definitions as SMB1 (from MS-FSA). + * Ditto FileAccess flags (as with ACLs) + */ + +/* SMB2 Create Context tags */ + +#define SMB2_CREATE_EA_BUFFER 0x45787441 /* ("ExtA") */ +/* + * The data contains the extended attributes + * that MUST be stored on the created file. + * This value MUST NOT be set for named + * pipes and print files. + */ + +#define SMB2_CREATE_SD_BUFFER 0x53656344 /* ("SecD") */ +/* + * The data contains a security descriptor that + * MUST be stored on the created file. + * This value MUST NOT be set for named + * pipes and print files. + */ + +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST 0x44486e51 /* ("DHnQ") */ +/* The client is requesting the open to be durable */ + +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT 0x44486e43 /* ("DHnC") */ +/* + * The client is requesting to reconnect to a + * durable open after being disconnected + */ + +#define SMB2_CREATE_ALLOCATION_SIZE 0x416c5369 /* ("AISi") */ +/* + * The data contains the required allocation + * size of the newly created file. + */ + +#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS 0x4d784163 /* ("MxAc") */ +/* + * The client is requesting that the server + * return maximal access information. + */ + +#define SMB2_CREATE_TIMEWARP_TOKEN 0x54577270 /* ("TWrp") */ +/* + * The client is requesting that the server + * open an earlier version of the file identified + * by the provided time stamp. + */ + +#define SMB2_CREATE_QUERY_ON_DISK_ID 0x51466964 /* ("QFid") */ +/* + * The client is requesting that the server return a 32-byte + * opaque BLOB that uniquely identifies the file being opened + * on disk. No data is passed to the server by the client. + */ + +#define SMB2_CREATE_REQUEST_LEASE 0x52714c73 /* ("RqLs") */ +/* + * The client is requesting that the server return a lease. + * This value is only supported for the SMB 2.1 and 3.0 dialects. + */ + +/* SMB2 create request lease */ +#define SMB2_LEASE_NONE 0x00 +#define SMB2_LEASE_READ_CACHING 0x01 +#define SMB2_LEASE_HANDLE_CACHING 0x02 +#define SMB2_LEASE_WRITE_CACHING 0x04 + +/* SMB2 lease break notification flags */ +#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED 0x01 + +/* + * SMB2 Close + */ +#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB 0x0001 + +/* + * SMB2 Write + */ +#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 + +/* + * SMB2 Lock Request + */ + +/* SMB2 lock flags */ + +/* + * SMB2_LOCKFLAG_SHARED_LOCK + * The range MUST be locked shared, allowing other opens + * to read from or take a shared lock on the range. All opens + * MUST NOT be allowed to write within the range. Other + * locks can be requested and taken on this range. + */ +#define SMB2_LOCKFLAG_SHARED_LOCK 0x00000001 + +/* + * SMB2_LOCKFLAG_EXCLUSIVE_LOCK + * The range MUST be locked exclusive, not allowing other + * opens to read, write, or lock within the range. + */ +#define SMB2_LOCKFLAG_EXCLUSIVE_LOCK 0x00000002 + +/* + * SMB2_LOCKFLAG_UNLOCK + * The range MUST be unlocked from a previous lock taken + * on this range. The unlock range MUST be identical to the + * lock range. Sub-ranges cannot be unlocked. + */ +#define SMB2_LOCKFLAG_UNLOCK 0x00000004 + +/* + * SMB2_LOCKFLAG_FAIL_IMMEDIATELY + * The lock operation MUST fail immediately if it conflicts + * with an existing lock, instead of waiting for the range to + * become available. This can be OR'ed with either of + * shared_lock, exclusive_lock (nothing else). + */ +#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x00000010 + +/* + * SMB2 Ioctl Request + */ +#define SMB2_IOCTL_IS_FSCTL 0x00000001 + + +/* + * SMB2 Query Directory + */ + +/* + * SMB2 query directory info levels + * Same as SMB1 (see ntifs.h) + */ + +/* + * SMB2 Query Directory Flags + * (our own names for these - spec. used poor names) + */ +#define SMB2_QDIR_FLAG_RESTART 0x01 /* SMB2_RESTART_SCANS */ +#define SMB2_QDIR_FLAG_SINGLE 0x02 /* SMB2_RETURN_SINGLE_ENTRY */ +#define SMB2_QDIR_FLAG_INDEX 0x04 /* SMB2_INDEX_SPECIFIED */ +#define SMB2_QDIR_FLAG_REOPEN 0x10 /* SMB2_REOPEN */ + +/* + * SMB2 Query Info Request + */ + +/* info type */ +#define SMB2_0_INFO_FILE 0x01 +/* The file information is requested. */ +#define SMB2_0_INFO_FILESYSTEM 0x02 +/* The underlying object store information is requested. */ +#define SMB2_0_INFO_SECURITY 0x03 +/* The security information is requested. */ +#define SMB2_0_INFO_QUOTA 0x04 +/* The underlying object store quota information is requested. */ + +/* + * MS-FSCC 2.5 FileSystem Information Classes. + * Also see MSDN for ZwQueryVolumeInformationFile. + */ +typedef enum _FS_INFORMATION_CLASS +{ + FileFsVolumeInformation = 1, /* Query */ + FileFsLabelInformation = 2, /* Set */ + FileFsSizeInformation = 3, /* Query */ + FileFsDeviceInformation = 4, /* Query */ + FileFsAttributeInformation = 5, /* Query */ + FileFsControlInformation = 6, /* Query, Set */ + FileFsFullSizeInformation = 7, /* Query */ + FileFsObjectIdInformation = 8, /* Query, Set */ + FileFsDriverPathInformation = 9 /* Query */ +} FS_INFORMATION_CLASS; + +/* + * MS-FSCC 2.4 File Information Classes + */ +typedef enum _FILE_INFORMATION_CLASS +{ + FileDirectoryInformation = 1, + FileFullDirectoryInformation = 2, + FileBothDirectoryInformation = 3, + FileBasicInformation = 4, + FileStandardInformation = 5, + FileInternalInformation = 6, + FileEaInformation = 7, + FileAccessInformation = 8, + FileNameInformation = 9, + FileRenameInformation = 10, + FileLinkInformation = 11, + FileNamesInformation = 12, + FileDispositionInformation = 13, + FilePositionInformation = 14, + FileFullEaInformation = 15, + FileModeInformation = 16, + FileAlignmentInformation = 17, + FileAllInformation = 18, + FileAllocationInformation = 19, + FileEndOfFileInformation = 20, + FileAlternateNameInformation = 21, + FileStreamInformation = 22, + FilePipeInformation = 23, + FilePipeLocalInformation = 24, + FilePipeRemoteInformation = 25, + FileMailslotQueryInformation = 26, + FileMailslotSetInformation = 27, + FileCompressionInformation = 28, + FileObjectIdInformation = 29, + FileMoveClusterInformation = 31, + FileQuotaInformation = 32, + FileReparsePointInformation = 33, + FileNetworkOpenInformation = 34, + FileAttributeTagInformation = 35, + FileTrackingInformation = 36, + FileIdBothDirectoryInformation = 37, + FileIdFullDirectoryInformation = 38, + FileValidDataLengthInformation = 39, + FileShortNameInformation = 40, + FileSfioReserveInformation = 44, + FileSfioVolumeInformation = 45, + FileHardLinkInformation = 46, + FileNormalizedNameInformation = 48, + FileIdGlobalTxDirectoryInformation = 50, + FileStandardLinkInformation = 54 +} FILE_INFORMATION_CLASS; + +/* + * SMB2 Change Nofity Request + */ +#define SMB2_WATCH_TREE 0x00000001 + +/* + * After here, added stuff from darwin + */ +#define SMB2_TID_UNKNOWN 0 +#define SMB2_FID_UNUSED 0xffffffffffffffff + +/* smb2_durable_handle flags */ +typedef enum _SMB2_DURABLE_HANDLE_FLAGS +{ + SMB2_DURABLE_HANDLE_REQUEST = 0x0001, + SMB2_DURABLE_HANDLE_RECONNECT = 0x0002, + SMB2_DURABLE_HANDLE_GRANTED = 0x0004, + SMB2_LEASE_GRANTED = 0x0008 +} _SMB2_DURABLE_HANDLE_FLAGS; + +struct smb2_durable_handle { + uint64_t fid; /* SMBFID to reconnect in durable handle reconnect */ + uint64_t flags; + uint64_t lease_key_hi; /* atomic increment number */ + uint64_t lease_key_low; /* node hash value */ + uint32_t lease_state; + uint32_t pad; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _NETSMB_SMB2_H */ diff --git a/usr/src/uts/common/netsmb/smb_dev.h b/usr/src/uts/common/netsmb/smb_dev.h index d2e7690062..817d214b3e 100644 --- a/usr/src/uts/common/netsmb/smb_dev.h +++ b/usr/src/uts/common/netsmb/smb_dev.h @@ -33,9 +33,10 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _NETSMB_DEV_H_ @@ -71,9 +72,9 @@ * associated structures change in ways that would * make them incompatible with an old driver. */ -#define NSMB_VERMAJ 1 -#define NSMB_VERMIN 4000 -#define NSMB_VERSION (NSMB_VERMAJ * 100000 + NSMB_VERMIN) +#define NSMB_VERMAJ 2 +#define NSMB_VERMIN 0x100 +#define NSMB_VERSION ((NSMB_VERMAJ << 16) | NSMB_VERMIN) /* * Some errno values we need to expose to the library. @@ -84,7 +85,7 @@ * EAUTH is used for CIFS authentication errors. */ #ifndef EBADRPC -#define EBADRPC 113 +#define EBADRPC 113 #endif #ifndef EAUTH #define EAUTH 114 @@ -111,13 +112,21 @@ #define SMBVOPT_PRIVATE 0x0002 /* connection should be private */ #define SMBVOPT_SINGLESHARE 0x0004 /* keep only one share at this VC */ #define SMBVOPT_PERMANENT 0x0010 /* object will keep last reference */ -#define SMBVOPT_EXT_SEC 0x0020 /* extended security negotiation */ -#define SMBVOPT_USE_KEYCHAIN 0x0040 /* get p/w from keychain */ -#define SMBVOPT_KC_DOMAIN 0x0080 /* keychain lookup uses domain */ +#define SMBVOPT_ANONYMOUS 0x0020 /* using a NULL session */ + +#define SMBVOPT_SIGNING_ENABLED 0x10000 /* sign if server agrees */ +#define SMBVOPT_SIGNING_REQUIRED 0x20000 /* signing required */ +#define SMBVOPT_SIGNING_MASK 0x30000 /* all signing bits */ -#define SMBVOPT_SIGNING_ENABLED 0x0100 /* sign if server agrees */ -#define SMBVOPT_SIGNING_REQUIRED 0x0200 /* signing required */ -#define SMBVOPT_SIGNING_MASK 0x0300 /* all signing bits */ +#define SMB2_DIALECT_BASE 0x0200 +#define SMB2_DIALECT_0202 0x0202 +#define SMB2_DIALECT_02ff 0x02ff +#define SMB2_DIALECT_0210 0x0210 +#define SMB2_DIALECT_0300 0x0300 +#define SMB2_DIALECT_0302 0x0302 + +/* Maximum supported dialect (for ssn_maxver) */ +#define SMB2_DIALECT_MAX SMB2_DIALECT_0210 /* * Option flags in smbioc_oshare.ioc_opt @@ -137,13 +146,18 @@ /* * network IO daemon states - * really connection states. */ enum smbiod_state { - SMBIOD_ST_IDLE = 0, /* no user requests enqueued yet */ - SMBIOD_ST_RECONNECT, /* a [re]connect attempt is in progress */ + SMBIOD_ST_UNINIT = 0, /* uninitialized */ + SMBIOD_ST_RECONNECT, /* a [re]connect attempt requested */ SMBIOD_ST_RCFAILED, /* a reconnect attempt has failed */ - SMBIOD_ST_VCACTIVE, /* session established */ + SMBIOD_ST_CONNECTED, /* Transport (TCP) connected */ + SMBIOD_ST_NEGOTIATED, /* Negotiated SMB/SMB2+ */ + SMBIOD_ST_AUTHCONT, /* Session setup continuing */ + SMBIOD_ST_AUTHFAIL, /* Session setup failed */ + SMBIOD_ST_AUTHOK, /* Session setup success */ + SMBIOD_ST_VCACTIVE, /* iod_work running */ + SMBIOD_ST_IDLE, /* no trees, will go DEAD */ SMBIOD_ST_DEAD /* connection gone, no IOD */ }; @@ -204,8 +218,10 @@ typedef struct smbioc_ssn_ident smbioc_ssn_ident_t; * Structure used with SMBIOC_SSN_FIND, _CREATE */ struct smbioc_ossn { - uint32_t ssn_vopt; /* i.e. SMBVOPT_CREATE */ uint32_t ssn_owner; /* Unix owner (UID) */ + uint32_t ssn_vopt; /* i.e. SMBVOPT_CREATE */ + uint16_t ssn_minver; /* Min SMB version. */ + uint16_t ssn_maxver; /* Max SMB version. */ smbioc_ssn_ident_t ssn_id; char ssn_srvname[SMBIOC_MAX_NAME]; }; @@ -232,55 +248,19 @@ typedef struct smbioc_tcon { smbioc_oshare_t tc_sh; } smbioc_tcon_t; - -/* - * Negotiated protocol parameters - */ -struct smb_sopt { - int16_t sv_proto; /* protocol dialect */ - uchar_t sv_sm; /* security mode */ - int16_t sv_tz; /* offset in min relative to UTC */ - uint16_t sv_maxmux; /* max number of outstanding rq's */ - uint16_t sv_maxvcs; /* max number of VCs */ - uint16_t sv_rawmode; - uint32_t sv_maxtx; /* maximum transmit buf size */ - uint32_t sv_maxraw; /* maximum raw-buffer size */ - uint32_t sv_skey; /* session key */ - uint32_t sv_caps; /* capabilites SMB_CAP_ */ -}; -typedef struct smb_sopt smb_sopt_t; - -/* - * State carried in/out of the driver by the IOD thread. - * Inside the driver, these are members of the "VC" object. - */ -struct smb_iods { - int32_t is_tran_fd; /* transport FD */ - uint32_t is_vcflags; /* SMBV_... */ - uint8_t is_hflags; /* SMB header flags */ - uint16_t is_hflags2; /* SMB header flags2 */ - uint16_t is_smbuid; /* SMB header UID */ - uint16_t is_next_mid; /* SMB header MID */ - uint32_t is_txmax; /* max tx/rx packet size */ - uint32_t is_rwmax; /* max read/write data size */ - uint32_t is_rxmax; /* max readx data size */ - uint32_t is_wxmax; /* max writex data size */ - uint8_t is_ssn_key[SMBIOC_HASH_SZ]; /* session key */ - /* Signing state */ - uint32_t is_next_seq; /* my next sequence number */ - uint32_t is_u_maclen; /* MAC key length */ - lptr_t is_u_mackey; /* user-space ptr! */ -}; -typedef struct smb_iods smb_iods_t; - /* * This is the operational state information passed * in and out of the driver for SMBIOC_SSN_WORK */ struct smbioc_ssn_work { - smb_iods_t wk_iods; - smb_sopt_t wk_sopt; - int wk_out_state; + uint32_t wk_out_state; /* out-only */ + uint32_t wk_u_ssnkey_len; /* ssn key length */ + lptr_t wk_u_ssnkey_buf; /* user-space ptr! */ + uint32_t wk_u_auth_rlen; /* recv auth tok len */ + uint32_t wk_u_auth_wlen; /* send auth tok len */ + lptr_t wk_u_auth_rbuf; /* recv auth tok buf */ + lptr_t wk_u_auth_wbuf; /* send auth tok buf */ + uint8_t wk_cl_guid[16]; /* client GUID */ }; typedef struct smbioc_ssn_work smbioc_ssn_work_t; @@ -288,66 +268,27 @@ typedef struct smbioc_ssn_work smbioc_ssn_work_t; * User-level SMB requests */ -/* - * SMBIOC_REQUEST (simple SMB request) - */ -typedef struct smbioc_rq { - uchar_t ioc_cmd; - uint8_t ioc_errclass; - uint16_t ioc_serror; - uint32_t ioc_error; - uint32_t ioc_tbufsz; /* transmit */ - uint32_t ioc_rbufsz; /* receive */ - lptr_t _ioc_tbuf; - lptr_t _ioc_rbuf; -} smbioc_rq_t; -#define ioc_tbuf _ioc_tbuf.lp_ptr -#define ioc_rbuf _ioc_rbuf.lp_ptr - - -#define SMBIOC_T2RQ_MAXSETUP 4 -#define SMBIOC_T2RQ_MAXNAME 128 - -typedef struct smbioc_t2rq { - uint16_t ioc_setup[SMBIOC_T2RQ_MAXSETUP]; - int32_t ioc_setupcnt; - char ioc_name[SMBIOC_T2RQ_MAXNAME]; - ushort_t ioc_tparamcnt; - ushort_t ioc_tdatacnt; - ushort_t ioc_rparamcnt; - ushort_t ioc_rdatacnt; - uint8_t ioc__pad1; - uint8_t ioc_errclass; - uint16_t ioc_serror; - uint32_t ioc_error; - uint16_t ioc_rpflags2; - uint16_t ioc__pad2; - lptr_t _ioc_tparam; - lptr_t _ioc_tdata; - lptr_t _ioc_rparam; - lptr_t _ioc_rdata; -} smbioc_t2rq_t; -#define ioc_tparam _ioc_tparam.lp_ptr -#define ioc_tdata _ioc_tdata.lp_ptr -#define ioc_rparam _ioc_rparam.lp_ptr -#define ioc_rdata _ioc_rdata.lp_ptr - - -typedef struct smbioc_flags { - int32_t ioc_level; /* 0 - session, 1 - share */ - int32_t ioc_flags; - int32_t ioc_mask; -} smbioc_flags_t; - typedef struct smbioc_rw { - int32_t ioc_fh; uint32_t ioc_cnt; + uint32_t ioc_flags; lloff_t _ioc_offset; lptr_t _ioc_base; } smbioc_rw_t; #define ioc_offset _ioc_offset._f #define ioc_base _ioc_base.lp_ptr +/* Transact on named pipe (send/recv) */ +typedef struct smbioc_xnp { + uint32_t ioc_tdlen; /* transmit len */ + uint32_t ioc_rdlen; /* recv maxlen */ + uint32_t ioc_more; /* more data to read */ + uint32_t ioc_pad1; + lptr_t _ioc_tdata; + lptr_t _ioc_rdata; +} smbioc_xnp_t; +#define ioc_tdata _ioc_tdata.lp_ptr +#define ioc_rdata _ioc_rdata.lp_ptr + typedef struct smbioc_ntcreate { uint32_t ioc_req_acc; uint32_t ioc_efattr; @@ -383,18 +324,16 @@ typedef struct smbioc_pk { * Keep GETVERS first and use it to verify * driver compatibility with the library. */ -#define SMBIOC_BASE ((('n' << 8) | 's') << 8) +#define SMBIOC_BASE ((('n' << 8) | 's') << 8) typedef enum nsmb_ioc { SMBIOC_GETVERS = SMBIOC_BASE, /* keep first */ - SMBIOC_FLAGS2, /* get hflags2 */ + SMBIOC_FLAGS2, /* obsolete */ SMBIOC_GETSSNKEY, /* get SMB session key */ SMBIOC_DUP_DEV, /* duplicate dev handle */ - SMBIOC_REQUEST, /* simple request */ - SMBIOC_T2RQ, /* trans2 request */ - SMBIOC_READ, /* read (pipe) */ SMBIOC_WRITE, /* write (pipe) */ + SMBIOC_XACTNP, /* "transact" (pipe) */ SMBIOC_NTCREATE, /* open or create */ SMBIOC_PRINTJOB, /* open print job */ SMBIOC_CLOSEFH, /* from ntcreate or printjob */ @@ -409,9 +348,12 @@ typedef enum nsmb_ioc { SMBIOC_TREE_KILL, SMBIOC_TREE_RELE, + SMBIOC_IOD_CONNECT, /* Setup connection */ + SMBIOC_IOD_NEGOTIATE, /* SMB/SMB2 negotiate */ + SMBIOC_IOD_SSNSETUP, /* SMB/SMB2 session setup */ SMBIOC_IOD_WORK, /* work on session requests */ SMBIOC_IOD_IDLE, /* wait for requests on this session */ - SMBIOC_IOD_RCFAIL, /* notify that reconnect failed */ + SMBIOC_IOD_RCFAIL, /* tell driver reconnect failed */ /* Password Keychain (PK) support. */ SMBIOC_PK_ADD, /* Add/Modify a password entry */ diff --git a/usr/src/uts/common/smb/Makefile b/usr/src/uts/common/smb/Makefile index 74ac618565..afa0837b2b 100644 --- a/usr/src/uts/common/smb/Makefile +++ b/usr/src/uts/common/smb/Makefile @@ -21,7 +21,7 @@ # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2018 Nexenta Systems, Inc. All rights reserved. # include ../../../Makefile.master @@ -29,8 +29,10 @@ include ../../../Makefile.master HDRS= \ doserror.h \ lmerr.h \ + ntaccess.h \ nterror.h \ ntstatus.h \ + winioctl.h \ wintypes.h diff --git a/usr/src/uts/common/smbsrv/ntaccess.h b/usr/src/uts/common/smb/ntaccess.h index 114150baa9..77d48b48ad 100644 --- a/usr/src/uts/common/smbsrv/ntaccess.h +++ b/usr/src/uts/common/smb/ntaccess.h @@ -21,13 +21,13 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_NTACCESS_H #define _SMBSRV_NTACCESS_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * This file defines the NT compatible access control masks and values. * An access mask as a 32-bit value arranged as shown below. diff --git a/usr/src/uts/common/smbsrv/winioctl.h b/usr/src/uts/common/smb/winioctl.h index bbde2e4a6f..a18d7853ce 100644 --- a/usr/src/uts/common/smbsrv/winioctl.h +++ b/usr/src/uts/common/smb/winioctl.h @@ -22,10 +22,10 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ -#ifndef _SMBSRV_WINIOCTL_H -#define _SMBSRV_WINIOCTL_H +#ifndef _SMB_WINIOCTL_H +#define _SMB_WINIOCTL_H /* * Standard Windows NT IOCTL/FSCTL definitions (derived from the VC++ @@ -544,4 +544,4 @@ extern "C" { } #endif -#endif /* _SMBSRV_WINIOCTL_H */ +#endif /* _SMB_WINIOCTL_H */ diff --git a/usr/src/uts/common/smbsrv/Makefile b/usr/src/uts/common/smbsrv/Makefile index 4664c09cfb..a80be7497f 100644 --- a/usr/src/uts/common/smbsrv/Makefile +++ b/usr/src/uts/common/smbsrv/Makefile @@ -20,7 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# Copyright 2018 Nexenta Systems, Inc. All rights reserved. # include ../../../Makefile.master @@ -36,7 +36,6 @@ HDRS= alloc.h \ netbios.h \ netrauth.h \ nmpipes.h \ - ntaccess.h \ ntifs.h \ ntlocale.h \ smb_sid.h \ @@ -61,7 +60,6 @@ HDRS= alloc.h \ smb2_kproto.h \ string.h \ svrapi.h \ - winioctl.h \ winsvc.h NDLHDRS= dssetup.ndl \ diff --git a/usr/src/uts/common/smbsrv/smb.h b/usr/src/uts/common/smbsrv/smb.h index a4986bea78..65e2708569 100644 --- a/usr/src/uts/common/smbsrv/smb.h +++ b/usr/src/uts/common/smbsrv/smb.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_SMB_H @@ -39,7 +39,7 @@ #include <smb/nterror.h> #include <smb/lmerr.h> #include <smb/doserror.h> -#include <smbsrv/ntaccess.h> +#include <smb/ntaccess.h> /* * Macintosh Extensions for CIFS diff --git a/usr/src/uts/common/sys/conf.h b/usr/src/uts/common/sys/conf.h index 4bf3d5c7e3..148104f83a 100644 --- a/usr/src/uts/common/sys/conf.h +++ b/usr/src/uts/common/sys/conf.h @@ -41,11 +41,11 @@ extern "C" { #endif -#define FMNAMESZ 8 /* used by struct fmodsw */ +#define FMNAMESZ 8 /* used by struct fmodsw */ #if !defined(_XPG4_2) || defined(__EXTENSIONS__) -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) /* * XXX Given that drivers need to include this file, @@ -53,10 +53,14 @@ extern "C" { * it legitimizes (aka provides prototypes for) * all sorts of functions that aren't in the DKI/SunDDI */ +#include <sys/types.h> #include <sys/systm.h> + +#endif /* _KERNEL || _FAKE_KERNEL */ +#ifdef _KERNEL + #include <sys/devops.h> #include <sys/model.h> -#include <sys/types.h> #include <sys/buf.h> #include <sys/cred.h> #include <sys/uio.h> diff --git a/usr/src/uts/common/sys/debug.h b/usr/src/uts/common/sys/debug.h index e4a959205a..9c91905af2 100644 --- a/usr/src/uts/common/sys/debug.h +++ b/usr/src/uts/common/sys/debug.h @@ -138,12 +138,12 @@ _NOTE(CONSTCOND) } while (0) #define __CTASSERT(x, y) \ typedef char __compile_time_assertion__ ## y [(x) ? 1 : -1] __unused -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) extern void abort_sequence_enter(char *); extern void debug_enter(char *); -#endif /* _KERNEL */ +#endif /* _KERNEL || _FAKE_KERNEL */ #if defined(DEBUG) && !defined(__sun) /* CSTYLED */ diff --git a/usr/src/uts/common/sys/dirent.h b/usr/src/uts/common/sys/dirent.h index 114fcf6e30..c079fb983a 100644 --- a/usr/src/uts/common/sys/dirent.h +++ b/usr/src/uts/common/sys/dirent.h @@ -76,7 +76,7 @@ typedef struct dirent64 { #endif /* _LARGEFILE64_SOURCE */ #if !defined(__XOPEN_OR_POSIX) || defined(__EXTENSIONS__) -#if defined(_KERNEL) +#if defined(_KERNEL) || defined(_FAKE_KERNEL) #define DIRENT64_RECLEN(namelen) \ ((offsetof(dirent64_t, d_name[0]) + 1 + (namelen) + 7) & ~ 7) #define DIRENT64_NAMELEN(reclen) \ diff --git a/usr/src/uts/common/sys/dnlc.h b/usr/src/uts/common/sys/dnlc.h index bf947659d0..4a5c20d6d0 100644 --- a/usr/src/uts/common/sys/dnlc.h +++ b/usr/src/uts/common/sys/dnlc.h @@ -76,7 +76,7 @@ extern "C" { * storing full names, then we are ok. The space savings are worth it. */ typedef struct ncache { - struct ncache *hash_next; /* hash chain, MUST BE FIRST */ + struct ncache *hash_next; /* hash chain, MUST BE FIRST */ struct ncache *hash_prev; struct vnode *vp; /* vnode the name refers to */ struct vnode *dp; /* vnode of parent of name */ @@ -169,7 +169,7 @@ struct nc_stats { (namlen) = Xcp - (name); \ } -#if defined(_KERNEL) +#if defined(_KERNEL) || defined(_FAKE_KERNEL) #include <sys/vfs.h> #include <sys/vnode.h> @@ -205,7 +205,7 @@ void dnlc_reduce_cache(void *); */ typedef struct dcfree { uint64_t df_handle; /* fs supplied handle */ - struct dcfree *df_next; /* link to next free entry in bucket */ + struct dcfree *df_next; /* link to next free entry in bucket */ uint_t df_len; /* length of free entry */ } dcfree_t; diff --git a/usr/src/uts/common/sys/file.h b/usr/src/uts/common/sys/file.h index eb1328f38d..b0914cfb45 100644 --- a/usr/src/uts/common/sys/file.h +++ b/usr/src/uts/common/sys/file.h @@ -119,7 +119,7 @@ typedef struct fpollinfo { #define FCLOEXEC 0x800000 /* O_CLOEXEC = 0x800000 */ -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) /* * Fake flags for driver ioctl calls to inform them of the originating @@ -183,7 +183,7 @@ typedef struct fpollinfo { extern int flock(int, int); #endif -#if defined(_KERNEL) +#if defined(_KERNEL) || defined(_FAKE_KERNEL) /* * Routines dealing with user per-open file flags and @@ -192,15 +192,16 @@ extern int flock(int, int); struct proc; /* forward reference for function prototype */ struct vnodeops; struct vattr; +struct uf_info; extern file_t *getf(int); extern file_t *getf_gen(int, uf_entry_gen_t *); extern void releasef(int); -extern void areleasef(int, uf_info_t *); +extern void areleasef(int, struct uf_info *); #ifndef _BOOT -extern void closeall(uf_info_t *); +extern void closeall(struct uf_info *); #endif -extern void flist_fork(uf_info_t *, uf_info_t *); +extern void flist_fork(struct uf_info *, struct uf_info *); extern int closef(file_t *); extern int closeandsetf(int, file_t *); extern int ufalloc_file(int, file_t *); @@ -217,8 +218,8 @@ extern void f_setfd(int, char); extern int f_getfl(int, int *); extern int f_badfd(int, int *, int); extern int fassign(struct vnode **, int, int *); -extern void fcnt_add(uf_info_t *, int); -extern void close_exec(uf_info_t *); +extern void fcnt_add(struct uf_info *, int); +extern void close_exec(struct uf_info *); extern void clear_stale_fd(void); extern void clear_active_fd(int); extern void set_active_fd(int); diff --git a/usr/src/uts/common/sys/model.h b/usr/src/uts/common/sys/model.h index fab96bbe00..0569c086f5 100644 --- a/usr/src/uts/common/sys/model.h +++ b/usr/src/uts/common/sys/model.h @@ -37,7 +37,7 @@ extern "C" { #include <sys/isa_defs.h> -#if defined(_KERNEL) || defined(_KMEMUSER) +#if defined(_KERNEL) || defined(_FAKE_KERNEL) || defined(_KMEMUSER) /* * These bits are used in various places to specify the data model diff --git a/usr/src/uts/common/sys/modhash.h b/usr/src/uts/common/sys/modhash.h index 68d1c4dedd..7f7103ecb9 100644 --- a/usr/src/uts/common/sys/modhash.h +++ b/usr/src/uts/common/sys/modhash.h @@ -34,7 +34,7 @@ extern "C" { #endif -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) #include <sys/types.h> diff --git a/usr/src/uts/common/sys/poll.h b/usr/src/uts/common/sys/poll.h index 4434a8551a..558edbfba9 100644 --- a/usr/src/uts/common/sys/poll.h +++ b/usr/src/uts/common/sys/poll.h @@ -96,7 +96,7 @@ typedef unsigned long nfds_t; #endif /* _KERNEL */ -#if defined(_KERNEL) || defined(_KMEMUSER) +#if defined(_KERNEL) || defined(_FAKE_KERNEL) || defined(_KMEMUSER) #include <sys/thread.h> diff --git a/usr/src/uts/common/sys/share.h b/usr/src/uts/common/sys/share.h index 4de5f5ce65..1a8aead065 100644 --- a/usr/src/uts/common/sys/share.h +++ b/usr/src/uts/common/sys/share.h @@ -63,7 +63,7 @@ struct shrlocklist { struct shrlocklist *next; }; -#if defined(_KERNEL) +#if defined(_KERNEL) || defined(_FAKE_KERNEL) struct flock64; extern int add_share(struct vnode *, struct shrlock *); diff --git a/usr/src/uts/common/sys/signal.h b/usr/src/uts/common/sys/signal.h index 1818665b45..b12dff6034 100644 --- a/usr/src/uts/common/sys/signal.h +++ b/usr/src/uts/common/sys/signal.h @@ -246,7 +246,7 @@ struct sigstack { #include <sys/ucontext.h> #endif /* defined(_XPG4_2) */ -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) #include <sys/t_lock.h> extern const k_sigset_t nullsmask; /* a null signal mask */ diff --git a/usr/src/uts/common/sys/stream.h b/usr/src/uts/common/sys/stream.h index efcd2a9194..7488d3dee8 100644 --- a/usr/src/uts/common/sys/stream.h +++ b/usr/src/uts/common/sys/stream.h @@ -36,7 +36,7 @@ * For source compatibility */ #include <sys/isa_defs.h> -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) #include <sys/kmem.h> #include <sys/uio.h> #endif @@ -641,7 +641,7 @@ struct stroptions { #define SO_MAXBLK 0x100000 /* set maximum message block size */ #define SO_TAIL 0x200000 /* set the extra allocated space */ -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) /* * Structure for rw (read/write) procedure calls. A pointer * to a struiod_t is passed as a parameter to the rwnext() call. @@ -766,7 +766,7 @@ typedef struct cmdblk { */ #define bpsize(bp) ((unsigned int)(bp->b_datap->db_lim - bp->b_datap->db_base)) -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) /* * For two-byte M_ERROR messages: indication that a side does not have an error diff --git a/usr/src/uts/common/sys/strsubr.h b/usr/src/uts/common/sys/strsubr.h index 0f29dd3675..f3bc1ed407 100644 --- a/usr/src/uts/common/sys/strsubr.h +++ b/usr/src/uts/common/sys/strsubr.h @@ -40,6 +40,7 @@ */ #include <sys/stream.h> #include <sys/stropts.h> +#include <sys/vnode.h> #include <sys/kstat.h> #include <sys/uio.h> #include <sys/proc.h> @@ -115,7 +116,7 @@ extern "C" { /* * Function types for the parameterized stream head. * The msgfunc_t takes the parameters: - * msgfunc(vnode_t *vp, mblk_t *mp, strwakeup_t *wakeups, + * msgfunc(vnode_t *vp, mblk_t *mp, strwakeup_t *wakeups, * strsigset_t *firstmsgsigs, strsigset_t *allmsgsigs, * strpollset_t *pollwakeups); * It returns an optional message to be processed by the stream head. @@ -130,7 +131,7 @@ typedef short strpollset_t; typedef uintptr_t callbparams_id_t; typedef mblk_t *(*msgfunc_t)(vnode_t *, mblk_t *, strwakeup_t *, strsigset_t *, strsigset_t *, strpollset_t *); -typedef int (*errfunc_t)(vnode_t *, int, int *); +typedef int (*errfunc_t)(vnode_t *, int, int *); /* * Per stream sd_lock in putnext may be replaced by per cpu stream_putlocks @@ -276,7 +277,7 @@ typedef struct stdata { /* 0x00020000 unused */ /* 0x00040000 unused */ #define STRTOSTOP 0x00080000 /* block background writes */ -#define STRCMDWAIT 0x00100000 /* someone is doing an _I_CMD */ +#define STRCMDWAIT 0x00100000 /* someone is doing an _I_CMD */ /* 0x00200000 unused */ #define STRMOUNT 0x00400000 /* stream is mounted */ #define STRNOTATMARK 0x00800000 /* Not at mark (when empty read q) */ @@ -410,7 +411,7 @@ typedef struct stdata { * * The new way is: * - * mutex_enter(SQLOCK(sq)); + * mutex_enter(SQLOCK(sq)); * count = sq->sq_count; * SQ_PUTLOCKS_ENTER(sq); * SUM_SQ_PUTCOUNTS(sq, count); @@ -459,8 +460,8 @@ struct syncq { */ uint16_t sq_type; /* type (concurrency) of syncq */ uint16_t sq_rmqcount; /* # threads inside removeq() */ - kcondvar_t sq_wait; /* block on this sync queue */ - kcondvar_t sq_exitwait; /* waiting for thread to leave the */ + kcondvar_t sq_wait; /* block on this sync queue */ + kcondvar_t sq_exitwait; /* waiting for thread to leave the */ /* inner perimeter */ /* * Handling synchronous callbacks such as qtimeout and qbufcall @@ -1024,7 +1025,7 @@ typedef struct str_stack str_stack_t; /* * Copy modes for tty and I_STR ioctls */ -#define U_TO_K 01 /* User to Kernel */ +#define U_TO_K 01 /* User to Kernel */ #define K_TO_K 02 /* Kernel to Kernel */ /* @@ -1077,7 +1078,7 @@ typedef struct str_stack str_stack_t; #define STRUNLOCKMATES(X) mutex_exit(&((X)->sd_lock)); \ mutex_exit(&(((X)->sd_mate)->sd_lock)) -#ifdef _KERNEL +#if defined(_KERNEL) || defined(_FAKE_KERNEL) extern void strinit(void); extern int strdoioctl(struct stdata *, struct strioctl *, int, int, diff --git a/usr/src/uts/common/sys/t_kuser.h b/usr/src/uts/common/sys/t_kuser.h index 7aac9d94d2..b3c4da657c 100644 --- a/usr/src/uts/common/sys/t_kuser.h +++ b/usr/src/uts/common/sys/t_kuser.h @@ -98,6 +98,7 @@ extern int t_kconnect(TIUSER *, struct t_call *, struct t_call *); extern int t_kfree(TIUSER *, char *, int); extern int t_kgetstate(TIUSER *, int *); extern int t_kopen(struct file *, dev_t, int, TIUSER **, struct cred *); +extern int t_koptmgmt(TIUSER *, struct t_optmgmt *, struct t_optmgmt *); extern int t_krcvudata(TIUSER *, struct t_kunitdata *, int *, int *); extern int t_ksndudata(TIUSER *, struct t_kunitdata *, frtn_t *); extern int t_kspoll(TIUSER *, int, int, int *); diff --git a/usr/src/uts/intel/nsmb/ioc_check.ref b/usr/src/uts/intel/nsmb/ioc_check.ref index 9d670f24c3..7476bc696d 100644 --- a/usr/src/uts/intel/nsmb/ioc_check.ref +++ b/usr/src/uts/intel/nsmb/ioc_check.ref @@ -3,10 +3,12 @@ #define ID_DOMAIN_INCR 0x1 #define ID_USER 0x120 #define ID_USER_INCR 0x1 -#define SSN_VOPT 0x0 -#define SSN_OWNER 0x4 -#define SSN_ID 0x8 -#define SSN_SRVNAME 0x228 +#define SSN_OWNER 0x0 +#define SSN_VOPT 0x4 +#define SSN_MINVER 0x8 +#define SSN_MAXVER 0xa +#define SSN_ID 0xc +#define SSN_SRVNAME 0x22c #define SSN_SRVNAME_INCR 0x1 #define SH_USE 0x0 #define SH_TYPE 0x4 @@ -17,70 +19,27 @@ #define TC_FLAGS 0x0 #define TC_OPT 0x4 #define TC_SH 0x8 -#define SV_PROTO 0x0 -#define SV_SM 0x2 -#define SV_TZ 0x4 -#define SV_MAXMUX 0x6 -#define SV_MAXVCS 0x8 -#define SV_RAWMODE 0xa -#define SV_MAXTX 0xc -#define SV_MAXRAW 0x10 -#define SV_SKEY 0x14 -#define SV_CAPS 0x18 -#define IS_TRAN_FD 0x0 -#define IS_VCFLAGS 0x4 -#define IS_HFLAGS 0x8 -#define IS_HFLAGS2 0xa -#define IS_SMBUID 0xc -#define IS_NEXT_MID 0xe -#define IS_TXMAX 0x10 -#define IS_RWMAX 0x14 -#define IS_RXMAX 0x18 -#define IS_WXMAX 0x1c -#define IS_SSN_KEY 0x20 -#define IS_SSN_KEY_INCR 0x1 -#define IS_NEXT_SEQ 0x30 -#define IS_U_MACLEN 0x34 -#define IS_U_MACKEY 0x38 -#define WK_IODS 0x0 -#define WK_SOPT 0x40 -#define WK_OUT_STATE 0x5c -#define SIZEOF_SMBIOC_RQ 0x20 -#define IOC_CMD 0x0 -#define IOC_RQ_ERRCLASS 0x1 -#define IOC_RQ_SERROR 0x2 -#define IOC_RQ_ERROR 0x4 -#define IOC_TBUFSZ 0x8 -#define IOC_RBUFSZ 0xc -#define _IOC_TBUF 0x10 -#define _IOC_RBUF 0x18 -#define SIZEOF_SMBIOC_T2RQ 0xc0 -#define IOC_SETUP 0x0 -#define IOC_SETUP_INCR 0x2 -#define IOC_SETUPCNT 0x8 -#define IOC_T2_NAME 0xc -#define IOC_T2_NAME_INCR 0x1 -#define IOC_TPARAMCNT 0x8c -#define IOC_TDATACNT 0x8e -#define IOC_RPARAMCNT 0x90 -#define IOC_RDATACNT 0x92 -#define IOC_T2_ERRCLASS 0x95 -#define IOC_T2_SERROR 0x96 -#define IOC_T2_ERROR 0x98 -#define IOC_RPFLAGS2 0x9c -#define _IOC_TPARAM 0xa0 -#define _IOC_TDATA 0xa8 -#define _IOC_RPARAM 0xb0 -#define _IOC_RDATA 0xb8 -#define SIZEOF_SMBIOC_FLAGS 0xc -#define IOC_LEVEL 0x0 -#define IOC_MASK 0x8 -#define IOC_FLAGS 0x4 +#define WK_OUT_STATE 0x0 +#define WK_U_SSNKEY_LEN 0x4 +#define WK_U_SSNKEY_BUF 0x8 +#define WK_U_AUTH_RLEN 0x10 +#define WK_U_AUTH_WLEN 0x14 +#define WK_U_AUTH_RBUF 0x18 +#define WK_U_AUTH_WBUF 0x20 +#define WK_CL_GUID 0x28 +#define WK_CL_GUID_INCR 0x1 #define SIZEOF_SMBIOC_RW 0x18 -#define IOC_FH 0x0 -#define IOC_CNT 0x4 +#define IOC_CNT 0x0 +#define IOC_FLAGS 0x4 #define _IOC_OFFSET 0x8 #define _IOC_BASE 0x10 +#define SIZEOF_SMBIOC_XNP 0x20 +#define IOC_TDLEN 0x0 +#define IOC_RDLEN 0x4 +#define IOC_MORE 0x8 +#define IOC_PAD1 0xc +#define _IOC_TDATA 0x10 +#define _IOC_RDATA 0x18 #define SIZEOF_NTCREATE 0x114 #define IOC_REQ_ACC 0x0 #define IOC_EFATTR 0x4 diff --git a/usr/src/uts/intel/pcbe/core_pcbe.c b/usr/src/uts/intel/pcbe/core_pcbe.c index 7424e2526b..ad92c2f62f 100644 --- a/usr/src/uts/intel/pcbe/core_pcbe.c +++ b/usr/src/uts/intel/pcbe/core_pcbe.c @@ -994,7 +994,8 @@ core_pcbe_event_coverage(char *event) } else { if (find_generic_events(event, cmn_generic_events) != NULL) { bitmap |= BITMASK_XBITS(num_gpc); - } if (find_generic_events(event, generic_events_pic0) != NULL) { + } else if (find_generic_events(event, + generic_events_pic0) != NULL) { bitmap |= 1ULL; } else if (find_gpcevent_core_uarch(event, cmn_gpc_events_core_uarch) != NULL) { diff --git a/usr/src/uts/sparc/nsmb/ioc_check.ref b/usr/src/uts/sparc/nsmb/ioc_check.ref index 9d670f24c3..7476bc696d 100644 --- a/usr/src/uts/sparc/nsmb/ioc_check.ref +++ b/usr/src/uts/sparc/nsmb/ioc_check.ref @@ -3,10 +3,12 @@ #define ID_DOMAIN_INCR 0x1 #define ID_USER 0x120 #define ID_USER_INCR 0x1 -#define SSN_VOPT 0x0 -#define SSN_OWNER 0x4 -#define SSN_ID 0x8 -#define SSN_SRVNAME 0x228 +#define SSN_OWNER 0x0 +#define SSN_VOPT 0x4 +#define SSN_MINVER 0x8 +#define SSN_MAXVER 0xa +#define SSN_ID 0xc +#define SSN_SRVNAME 0x22c #define SSN_SRVNAME_INCR 0x1 #define SH_USE 0x0 #define SH_TYPE 0x4 @@ -17,70 +19,27 @@ #define TC_FLAGS 0x0 #define TC_OPT 0x4 #define TC_SH 0x8 -#define SV_PROTO 0x0 -#define SV_SM 0x2 -#define SV_TZ 0x4 -#define SV_MAXMUX 0x6 -#define SV_MAXVCS 0x8 -#define SV_RAWMODE 0xa -#define SV_MAXTX 0xc -#define SV_MAXRAW 0x10 -#define SV_SKEY 0x14 -#define SV_CAPS 0x18 -#define IS_TRAN_FD 0x0 -#define IS_VCFLAGS 0x4 -#define IS_HFLAGS 0x8 -#define IS_HFLAGS2 0xa -#define IS_SMBUID 0xc -#define IS_NEXT_MID 0xe -#define IS_TXMAX 0x10 -#define IS_RWMAX 0x14 -#define IS_RXMAX 0x18 -#define IS_WXMAX 0x1c -#define IS_SSN_KEY 0x20 -#define IS_SSN_KEY_INCR 0x1 -#define IS_NEXT_SEQ 0x30 -#define IS_U_MACLEN 0x34 -#define IS_U_MACKEY 0x38 -#define WK_IODS 0x0 -#define WK_SOPT 0x40 -#define WK_OUT_STATE 0x5c -#define SIZEOF_SMBIOC_RQ 0x20 -#define IOC_CMD 0x0 -#define IOC_RQ_ERRCLASS 0x1 -#define IOC_RQ_SERROR 0x2 -#define IOC_RQ_ERROR 0x4 -#define IOC_TBUFSZ 0x8 -#define IOC_RBUFSZ 0xc -#define _IOC_TBUF 0x10 -#define _IOC_RBUF 0x18 -#define SIZEOF_SMBIOC_T2RQ 0xc0 -#define IOC_SETUP 0x0 -#define IOC_SETUP_INCR 0x2 -#define IOC_SETUPCNT 0x8 -#define IOC_T2_NAME 0xc -#define IOC_T2_NAME_INCR 0x1 -#define IOC_TPARAMCNT 0x8c -#define IOC_TDATACNT 0x8e -#define IOC_RPARAMCNT 0x90 -#define IOC_RDATACNT 0x92 -#define IOC_T2_ERRCLASS 0x95 -#define IOC_T2_SERROR 0x96 -#define IOC_T2_ERROR 0x98 -#define IOC_RPFLAGS2 0x9c -#define _IOC_TPARAM 0xa0 -#define _IOC_TDATA 0xa8 -#define _IOC_RPARAM 0xb0 -#define _IOC_RDATA 0xb8 -#define SIZEOF_SMBIOC_FLAGS 0xc -#define IOC_LEVEL 0x0 -#define IOC_MASK 0x8 -#define IOC_FLAGS 0x4 +#define WK_OUT_STATE 0x0 +#define WK_U_SSNKEY_LEN 0x4 +#define WK_U_SSNKEY_BUF 0x8 +#define WK_U_AUTH_RLEN 0x10 +#define WK_U_AUTH_WLEN 0x14 +#define WK_U_AUTH_RBUF 0x18 +#define WK_U_AUTH_WBUF 0x20 +#define WK_CL_GUID 0x28 +#define WK_CL_GUID_INCR 0x1 #define SIZEOF_SMBIOC_RW 0x18 -#define IOC_FH 0x0 -#define IOC_CNT 0x4 +#define IOC_CNT 0x0 +#define IOC_FLAGS 0x4 #define _IOC_OFFSET 0x8 #define _IOC_BASE 0x10 +#define SIZEOF_SMBIOC_XNP 0x20 +#define IOC_TDLEN 0x0 +#define IOC_RDLEN 0x4 +#define IOC_MORE 0x8 +#define IOC_PAD1 0xc +#define _IOC_TDATA 0x10 +#define _IOC_RDATA 0x18 #define SIZEOF_NTCREATE 0x114 #define IOC_REQ_ACC 0x0 #define IOC_EFATTR 0x4 |