diff options
author | thurlow <none@none> | 2008-02-13 19:51:22 -0800 |
---|---|---|
committer | thurlow <none@none> | 2008-02-13 19:51:22 -0800 |
commit | 4bff34e37def8a90f9194d81bc345c52ba20086a (patch) | |
tree | 7bf2710d9da099e3b07fea38e12788bfd565f3c5 /usr/src/uts | |
parent | a916d99c7b27a531bf37c57f83b0b74120fd05bb (diff) | |
download | illumos-joyent-4bff34e37def8a90f9194d81bc345c52ba20086a.tar.gz |
PSARC 2005/695 CIFS Client on Solaris
PSARC 2007/303 pam_smb_login
PSARC 2008/073 CIFS Client on Solaris - Updates
6651904 CIFS Client - PSARC 2005/695
Diffstat (limited to 'usr/src/uts')
63 files changed, 26004 insertions, 11 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index c732c44f3a..7c26fb1f1a 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1043,6 +1043,14 @@ UFS_OBJS += ufs_alloc.o ufs_bmap.o ufs_dir.o ufs_xattr.o \ lufs_log.o lufs_map.o lufs_top.o lufs_debug.o VSCAN_OBJS += vscan_drv.o vscan_svc.o vscan_door.o +NSMB_OBJS += smb_conn.o smb_crypt.o smb_dev.o smb_iod.o \ + smb_rq.o smb_smb.o smb_tran.o smb_trantcp.o \ + smb_usr.o smb_subrs.o subr_mchain.o smb_pass.o + +SMBFS_OBJS += smbfs_vfsops.o smbfs_vnops.o smbfs_node.o \ + smbfs_client.o smbfs_io.o smbfs_smb.o \ + smbfs_subr.o smbfs_subr2.o smbfs_rwlock.o + # # LVM modules # diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index 208b098b53..8a5ebdfc87 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -262,6 +262,14 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/sharefs/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/smbclnt/netsmb/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/smbclnt/smbfs/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/sockfs/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1239,6 +1247,18 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/proc/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/sharefs/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +NSMBLINT = -erroff=E_FUNC_RET_ALWAYS_IGNOR2 -erroff=E_FUNC_RET_MAYBE_IGNORED2 + +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/smbclnt/netsmb/%.c + @($(LHEAD) $(LINT.c) $(NSMBLINT) $< $(LTAIL)) + +$(LINTS_DIR)/smbfs_smb.ln: $(UTSBASE)/common/fs/smbclnt/smbfs/smbfs_smb.c + @($(LHEAD) $(LINT.c) $(NSMBLINT) \ + $(UTSBASE)/common/fs/smbclnt/smbfs/smbfs_smb.c $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/smbclnt/smbfs/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/fs/sockfs/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/COPYRIGHT b/usr/src/uts/common/fs/smbclnt/netsmb/COPYRIGHT new file mode 100644 index 0000000000..2e2fa13fc4 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/COPYRIGHT @@ -0,0 +1,31 @@ +#ident "%Z%%M% %I% %E% SMI" + + 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/CREDITS b/usr/src/uts/common/fs/smbclnt/netsmb/CREDITS new file mode 100644 index 0000000000..31dbb784d9 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/CREDITS @@ -0,0 +1,11 @@ +#ident "%Z%%M% %I% %E% SMI" + + In the development process next sources were used: + +Various documents from the Microsoft ftp site. +HTML docs published by Thursby Software. + +Special thanks to the Samba team for permission to use their source +code as reference. + +Author - Boris Popov <bp@butya.kz>, <bp@freebsd.org> diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/README b/usr/src/uts/common/fs/smbclnt/netsmb/README new file mode 100644 index 0000000000..c90f56cf7d --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/README @@ -0,0 +1,49 @@ + + + SMB/CIFS protocol and SMB/CIFS file system implementation + for FreeBSD, version 1.4. + + This is native SMB/CIFS filesystem (smbfs for short) for FreeBSD. +It is a complete, kernel side implementation of SMB requester and filesystem. + + Supportted platforms: + FreeBSD 4.X + + FreeBSD-current kernel module is included in the base source + tree. + + I'm would be very grateful for any feedback, bug reports etc. + + Supported SMB servers: + Samba + Windows 95/98/ME/2000/NT4.0 (SPs 4, 5, 6) + IBM LanManager + NetApp + + An updated versions of this package can be retrieved from ftp server: + + ftp://ftp.butya.kz/pub/smbfs/smbfs.tar.gz + +Author: Boris Popov <bp@freebsd.org> + +================================================================ + +Additional notes from Sun Microsystems: + +This code (the OpenSolaris CIFS client) is derived from the +Apple Darwin project, smb-217.2 code drop. See the file +COPYRIGHT for copyright and redistribution terms. + +The Darwin code was reorganized as follows for Solaris: + +Darwin location: Solaris location: (usr/src/...) +kernel/netsmb uts/common/netsmb (exported headers) +kernel/netsmb uts/common/fs/smbclnt/netsmb +kernel/fs/smbfs uts/common/fs/smbclnt/smbfs +include/netsmb lib/libsmb/netsmb +lib/smb lib/libsmb/smb +mount_smbfs cmd/fs.d/smbclnt/mount +smbutil cmd/fs.d/smbclnt/smbutil +idl_compiler cmd/idlgen + +[ ident "%Z%%M% %I% %E% SMI" ] diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/nsmb.conf b/usr/src/uts/common/fs/smbclnt/netsmb/nsmb.conf new file mode 100644 index 0000000000..91cbaae871 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/nsmb.conf @@ -0,0 +1,28 @@ +# +# 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 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +name="nsmb" parent="pseudo"; + diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in b/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in new file mode 100644 index 0000000000..6c39fa5a22 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in @@ -0,0 +1,134 @@ +\ +\ 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 2007 Sun Microsystems, Inc. All rights reserved. +\ Use is subject to license terms. +\ + +#pragma ident "%Z%%M% %I% %E% SMI" + +\ +\ offsets.in: input file for the ctfstabs program, +\ used to generate ioc_check.h - which verifies +\ invariance of our ioctl data structures across +\ 32-bit and 64-bit ABIs. + +#ifndef _GENASSYM +#define _GENASSYM +#endif + +#include <sys/types.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/socket_impl.h> +#include <netsmb/smb.h> +#include <netsmb/netbios.h> +#include <netsmb/smb_dev.h> + +sockaddr_any SIZEOF_SOCKADDR_ANY + +sockaddr_in SIZEOF_SOCKADDR_IN + +sockaddr_nb SIZEOF_SOCKADDR_NB + +smbioc_ossn SIZEOF_SMBIOC_OSSN + ioc_server + ioc_local + ioc_localcs + ioc_servercs + ioc_srvname + ioc_user + ioc_workgroup + ioc_password IOC_SSN_PASSWD + ioc_opt IOC_SSN_OPT + ioc_timeout + ioc_retrycount + ioc_owner IOC_SSN_OWNER + ioc_group IOC_SSN_GROUP + ioc_mode IOC_SSN_MODE + ioc_rights IOC_SSN_RIGHTS + ioc_intoklen + ioc_outtoklen + _ioc_intok + _ioc_outtok + +smbioc_oshare SIZEOF_SMBIOC_OSHARE + ioc_share + ioc_password IOC_SH_PASSWD + ioc_opt IOC_SH_OPT + ioc_stype + ioc_owner IOC_SH_OWNER + ioc_group IOC_SH_GROUP + ioc_mode IOC_SH_MODE + ioc_rights IOC_SH_RIGHTS + +smbioc_rq SIZEOF_SMBIOC_RQ + ioc_cmd + ioc_twc + ioc_tbc + ioc_rpbufsz + ioc_rwc + ioc_rbc + ioc_errclass IOC_RQ_ERRCLASS + ioc_serror IOC_RQ_SERROR + ioc_error IOC_RQ_ERROR + _ioc_twords + _ioc_tbytes + _ioc_rpbuf + +smbioc_t2rq SIZEOF_SMBIOC_T2RQ + ioc_setup + ioc_setupcnt + ioc_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 + +smbioc_lookup SIZEOF_SMBIOC_LOOKUP + ioc_level IOC_LOOK_LEVEL + ioc_flags IOC_LOOK_FLAGS + ioc_sh + ioc_ssn + +smbioc_rw SIZEOF_SMBIOC_RW + ioc_fh + ioc_cnt + _ioc_offset + _ioc_base + +smbioc_pk SIZEOF_SMBIOC_PK + pk_dom + pk_usr + pk_pass diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c new file mode 100644 index 0000000000..b0b50b80e1 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c @@ -0,0 +1,1294 @@ +/* + * 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. + * + * $Id: smb_conn.c,v 1.27.166.1 2005/05/27 02:35:29 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Connection engine. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kmem.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/vnode.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/socketvar.h> +#include <sys/cred.h> +#include <sys/cred_impl.h> +#include <netinet/in.h> +#include <inet/ip.h> +#include <inet/ip6.h> +#include <sys/cmn_err.h> +#include <sys/thread.h> +#include <sys/atomic.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#include <sys/smb_iconv.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_tran.h> +#include <netsmb/smb_pass.h> + +static struct smb_connobj smb_vclist; +static uint_t smb_vcnext = 0; /* next unique id for VC */ + +void smb_co_init(struct smb_connobj *cp, int level, char *objname); +void smb_co_done(struct smb_connobj *cp); +void smb_co_hold(struct smb_connobj *cp); +void smb_co_rele(struct smb_connobj *cp); +void smb_co_kill(struct smb_connobj *cp); + +#ifdef APPLE +static void smb_sm_lockvclist(void); +static void smb_sm_unlockvclist(void); +#endif + +static void smb_vc_free(struct smb_connobj *cp); +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); + +/* smb_dup_sockaddr moved to smb_tran.c */ + +int +smb_sm_init(void) +{ + smb_co_init(&smb_vclist, SMBL_SM, "smbsm"); + return (0); +} + +int +smb_sm_idle(void) +{ + int error = 0; + SMB_CO_LOCK(&smb_vclist); + if (smb_vclist.co_usecount > 1) { + SMBSDEBUG("%d connections still active\n", + smb_vclist.co_usecount - 1); + error = EBUSY; + } + SMB_CO_UNLOCK(&smb_vclist); + return (error); +} + +void +smb_sm_done(void) +{ + /* + * XXX Q4BP 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. + */ + smb_co_done(&smb_vclist); +} + +/* + * Find a VC identified by the info in vcspec, + * and return it with a "hold", but not locked. + */ +/*ARGSUSED*/ +static int +smb_sm_lookupvc( + struct smb_vcspec *vcspec, + struct smb_cred *scred, + struct smb_vc **vcpp) +{ + struct smb_connobj *co; + struct smb_vc *vcp; + zoneid_t zoneid = getzoneid(); + + ASSERT(MUTEX_HELD(&smb_vclist.co_lock)); + + /* var, head, next_field */ + SLIST_FOREACH(co, &smb_vclist.co_children, co_next) { + vcp = CPTOVC(co); + + /* + * Some things we can check without + * holding the lock (those that are + * set at creation and never change). + */ + + /* VCs in other zones are invisibile. */ + if (vcp->vc_zoneid != zoneid) + continue; + + /* Also segregate by owner. */ + if (vcp->vc_uid != vcspec->owner) + continue; + + /* XXX: we ignore the group. Remove vc_gid? */ + + /* server */ + if (smb_cmp_sockaddr(vcp->vc_paddr, vcspec->sap)) + continue; + + /* domain+user */ + if (strcmp(vcp->vc_domain, vcspec->domain)) + continue; + if (strcmp(vcp->vc_username, vcspec->username)) + continue; + + SMB_VC_LOCK(vcp); + + /* No new references allowed when _GONE is set */ + if (vcp->vc_flags & SMBV_GONE) + goto unlock_continue; + + if (vcp->vc_vopt & SMBVOPT_PRIVATE) + goto unlock_continue; + + found: + /* + * Success! (Found one we can use) + * Return with it held, unlocked. + * In-line smb_vc_hold here. + */ + co->co_usecount++; + SMB_VC_UNLOCK(vcp); + *vcpp = vcp; + return (0); + + unlock_continue: + SMB_VC_UNLOCK(vcp); + /* keep looking. */ + } + + return (ENOENT); +} + + +int +smb_sm_negotiate( + struct smb_vcspec *vcspec, + struct smb_cred *scred, + struct smb_vc **vcpp) +{ + struct smb_vc *vcp; + clock_t tmo; + int created, error; + +top: + *vcpp = vcp = NULL; + + SMB_CO_LOCK(&smb_vclist); + error = smb_sm_lookupvc(vcspec, scred, &vcp); + if (error) { + /* The VC was not found. Create? */ + if ((vcspec->optflags & SMBVOPT_CREATE) == 0) { + SMB_CO_UNLOCK(&smb_vclist); + return (error); + } + error = smb_vc_create(vcspec, scred, &vcp); + if (error) { + /* Could not create? Unusual. */ + SMB_CO_UNLOCK(&smb_vclist); + return (error); + } + /* Note: co_usecount == 1 */ + created = 1; + } else + created = 0; + SMB_CO_UNLOCK(&smb_vclist); + + if (created) { + /* + * We have a NEW VC, held, but not locked. + */ + + SMBIODEBUG("vc_state=%d\n", vcp->vc_state); + switch (vcp->vc_state) { + + case SMBIOD_ST_NOTCONN: + (void) smb_vc_setup(vcspec, scred, vcp, 0); + vcp->vc_genid++; + /* XXX: Save credentials of caller here? */ + vcp->vc_state = SMBIOD_ST_RECONNECT; + /* FALLTHROUGH */ + + case SMBIOD_ST_RECONNECT: + error = smb_iod_connect(vcp); + if (error) + break; + vcp->vc_state = SMBIOD_ST_TRANACTIVE; + /* FALLTHROUGH */ + + case SMBIOD_ST_TRANACTIVE: + /* XXX: Just pass vcspec instead? */ + vcp->vc_intok = vcspec->tok; + vcp->vc_intoklen = vcspec->toklen; + error = smb_smb_negotiate(vcp, &vcp->vc_scred); + vcp->vc_intok = NULL; + vcp->vc_intoklen = 0; + if (error) + break; + vcp->vc_state = SMBIOD_ST_NEGOACTIVE; + /* FALLTHROUGH */ + + case SMBIOD_ST_NEGOACTIVE: + case SMBIOD_ST_SSNSETUP: + case SMBIOD_ST_VCACTIVE: + /* We can (re)use this VC. */ + error = 0; + break; + + default: + error = EINVAL; + break; + } + + SMB_VC_LOCK(vcp); + cv_broadcast(&vcp->vc_statechg); + SMB_VC_UNLOCK(vcp); + + } else { + /* + * Found an existing VC. Reuse it, but first, + * wait for authentication to finish, etc. + * Note: We hold a reference on the VC. + */ + error = 0; + SMB_VC_LOCK(vcp); + while (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + tmo = lbolt + SEC_TO_TICK(5); + tmo = cv_timedwait_sig(&vcp->vc_statechg, + &vcp->vc_lock, tmo); + if (tmo == 0) { + error = EINTR; + break; + } + if (vcp->vc_flags & SMBV_GONE) + break; + } + SMB_VC_UNLOCK(vcp); + + /* Interrupted? */ + if (error) + goto out; + + /* + * The other guy failed authentication, + * or otherwise gave up on this VC. + * Drop reference, start over. + */ + if (vcp->vc_flags & SMBV_GONE) { + smb_vc_rele(vcp); + goto top; + } + + ASSERT(vcp->vc_state == SMBIOD_ST_VCACTIVE); + /* Success! */ + } + +out: + if (error) { + /* + * Undo the hold from lookupvc, + * or destroy if from vc_create. + */ + smb_vc_rele(vcp); + } else { + /* Return it held. */ + *vcpp = vcp; + } + + return (error); +} + + +int +smb_sm_ssnsetup( + struct smb_vcspec *vcspec, + struct smb_cred *scred, + struct smb_vc *vcp) +{ + int error; + + /* + * We have a VC, held, but not locked. + * + * Code from smb_iod_ssnsetup, + * with lots of rework. + */ + + SMBIODEBUG("vc_state=%d\n", vcp->vc_state); + switch (vcp->vc_state) { + + case SMBIOD_ST_NEGOACTIVE: + /* + * This is the state we normally find. + * Calling _setup AGAIN to update the + * flags, security info, etc. + */ + error = smb_vc_setup(vcspec, scred, vcp, 1); + if (error) + break; + vcp->vc_state = SMBIOD_ST_SSNSETUP; + /* FALLTHROUGH */ + + case SMBIOD_ST_SSNSETUP: + /* XXX: Just pass vcspec instead? */ + vcp->vc_intok = vcspec->tok; + vcp->vc_intoklen = vcspec->toklen; + error = smb_smb_ssnsetup(vcp, &vcp->vc_scred); + vcp->vc_intok = NULL; + vcp->vc_intoklen = 0; + if (error) + break; + /* OK, start the reader thread... */ + error = smb_iod_create(vcp); + if (error) + break; + vcp->vc_state = SMBIOD_ST_VCACTIVE; + /* FALLTHROUGH */ + + case SMBIOD_ST_VCACTIVE: + /* We can (re)use this VC. */ + error = 0; + break; + + default: + error = EINVAL; + break; + } + + SMB_VC_LOCK(vcp); + cv_broadcast(&vcp->vc_statechg); + SMB_VC_UNLOCK(vcp); + + return (error); +} + +int +smb_sm_tcon( + struct smb_sharespec *shspec, + struct smb_cred *scred, + struct smb_vc *vcp, + struct smb_share **sspp) +{ + struct smb_share *ssp; + int error; + + *sspp = ssp = NULL; + + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + /* + * The wait for vc_state in smb_sm_negotiate + * _should_ get us a VC in the right state. + */ + SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + return (ENOTCONN); + } + + SMB_VC_LOCK(vcp); + error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); + if (error) { + /* The share was not found. Create? */ + if ((shspec->optflags & SMBVOPT_CREATE) == 0) { + SMB_VC_UNLOCK(vcp); + return (error); + } + error = smb_share_create(vcp, shspec, scred, &ssp); + if (error) { + /* Could not create? Unusual. */ + SMB_VC_UNLOCK(vcp); + return (error); + } + /* Note: co_usecount == 1 */ + } + SMB_VC_UNLOCK(vcp); + + /* + * We have a share, held, but not locked. + * Make it connected... + */ + SMB_SS_LOCK(ssp); + if (!smb_share_valid(ssp)) + error = smb_share_tcon(ssp); + SMB_SS_UNLOCK(ssp); + + if (error) { + /* + * Undo hold from lookupshare, + * or destroy if from _create. + */ + smb_share_rele(ssp); + } else { + /* Return it held. */ + *sspp = ssp; + } + + return (error); +} + +/* + * Common code for connection object + */ +/*ARGSUSED*/ +void +smb_co_init(struct smb_connobj *cp, int level, char *objname) +{ + + mutex_init(&cp->co_lock, objname, MUTEX_DRIVER, NULL); + + cp->co_level = level; + cp->co_usecount = 1; + SLIST_INIT(&cp->co_children); +} + +/* + * Called just before free of an object + * of which smb_connobj is a part, i.e. + * _vc_free, _share_free, also sm_done. + */ +void +smb_co_done(struct smb_connobj *cp) +{ + ASSERT(SLIST_EMPTY(&cp->co_children)); + mutex_destroy(&cp->co_lock); +} + +static void +smb_co_addchild( + struct smb_connobj *parent, + struct smb_connobj *child) +{ + + /* + * Set the child's pointer to the parent. + * No references yet, so no need to lock. + */ + ASSERT(child->co_usecount == 1); + child->co_parent = parent; + + /* + * Add the child to the parent's list of + * children, and in-line smb_co_hold + */ + ASSERT(MUTEX_HELD(&parent->co_lock)); + parent->co_usecount++; + SLIST_INSERT_HEAD(&parent->co_children, child, co_next); +} + +void +smb_co_hold(struct smb_connobj *cp) +{ + SMB_CO_LOCK(cp); + cp->co_usecount++; + SMB_CO_UNLOCK(cp); +} + +/* + * Called via smb_vc_rele, smb_share_rele + */ +void +smb_co_rele(struct smb_connobj *co) +{ + struct smb_connobj *parent; + int old_flags; + + SMB_CO_LOCK(co); + if (co->co_usecount > 1) { + co->co_usecount--; + SMB_CO_UNLOCK(co); + return; + } + ASSERT(co->co_usecount == 1); + co->co_usecount = 0; + + /* + * This list of children should be empty now. + * Check this while we're still linked, so + * we have a better chance of debugging. + */ + ASSERT(SLIST_EMPTY(&co->co_children)); + + /* + * OK, this element is going away. + * + * We need to drop the lock on this CO so we can take the + * parent CO lock. The _GONE flag prevents this CO from + * getting new references before we can unlink it from the + * parent list. + * + * The _GONE flag is also used to ensure that the co_gone + * function is called only once. Note that smb_co_kill may + * do this before we get here. If we find that the _GONE + * flag was not already set, then call the co_gone hook + * (smb_share_gone, smb_vc_gone) which will disconnect + * the share or the VC, respectively. + * + * Note the old: smb_co_gone(co, scred); + * is now in-line here. + */ + old_flags = co->co_flags; + co->co_flags |= SMBO_GONE; + SMB_CO_UNLOCK(co); + + if ((old_flags & SMBO_GONE) == 0 && co->co_gone) + co->co_gone(co); + + /* + * If we have a parent (only smb_vclist does not) + * then unlink from parent's list of children. + * We have the only reference to the child. + */ + parent = co->co_parent; + if (parent) { + SMB_CO_LOCK(parent); + ASSERT(SLIST_FIRST(&parent->co_children)); + if (SLIST_FIRST(&parent->co_children)) { + SLIST_REMOVE(&parent->co_children, co, + smb_connobj, co_next); + } + SMB_CO_UNLOCK(parent); + } + + /* + * Now it's safe to free the CO + */ + if (co->co_free) { + co->co_free(co); + } + + /* + * Finally, if the CO had a parent, decrement + * the parent's hold count for the lost child. + */ + if (parent) { + /* + * Recursive call here (easier for debugging). + * Can only go two levels. + */ + smb_co_rele(parent); + } +} + +/* + * Do just the first part of what co_gone does, + * i.e. tree disconnect, or disconnect a VC. + * This is used to forcibly close things. + */ +void +smb_co_kill(struct smb_connobj *co) +{ + int old_flags; + + SMB_CO_LOCK(co); + old_flags = co->co_flags; + co->co_flags |= SMBO_GONE; + SMB_CO_UNLOCK(co); + + /* + * Do the same "call only once" logic here as in + * smb_co_rele, though it's probably not possible + * for this to be called after smb_co_rele. + */ + if ((old_flags & SMBO_GONE) == 0 && co->co_gone) + co->co_gone(co); + + /* XXX: Walk list of children and kill those too? */ +} + + +/* + * Session implementation + */ + +/* + * This sets the fields that are allowed to change + * when doing a reconnect. Many others are set in + * smb_vc_create and never change afterwards. + * Don't want domain or user to change here. + */ +int +smb_vc_setup(struct smb_vcspec *vcspec, struct smb_cred *scred, + struct smb_vc *vcp, int is_ss) +{ + int error, minauth; + + /* Just save all the SMBVOPT_ options. */ + vcp->vc_vopt = vcspec->optflags; + + /* Cleared if nego response shows antique server! */ + vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; + + /* XXX: Odd place for this. */ + if (vcspec->optflags & SMBVOPT_EXT_SEC) + vcp->vc_hflags2 |= SMB_FLAGS2_EXT_SEC; + + if (is_ss) { + /* Called from smb_sm_ssnsetup */ + + if (vcspec->optflags & SMBVOPT_USE_KEYCHAIN) { + /* + * Get p/w hashes from the keychain. + * The password in vcspec->pass is + * fiction, so don't store it. + */ + error = smb_pkey_getpwh(vcp, scred->vc_ucred); + return (error); + } + + /* + * Note: this can be called more than once + * for a given vcp, so free the old strings. + */ + SMB_STRFREE(vcp->vc_pass); + + /* + * Don't store the cleartext password + * unless the minauth value was changed + * to allow use of cleartext passwords. + * (By default, this is not allowed.) + */ + minauth = vcspec->optflags & SMBVOPT_MINAUTH; + if (minauth == SMBVOPT_MINAUTH_NONE) + vcp->vc_pass = smb_strdup(vcspec->pass); + + /* Compute LM and NTLM hashes. */ + smb_oldlm_hash(vcspec->pass, vcp->vc_lmhash); + smb_ntlmv1hash(vcspec->pass, vcp->vc_nthash); + } + + /* Success! */ + error = 0; + return (error); +} + +/*ARGSUSED*/ +int +smb_vc_create(struct smb_vcspec *vcspec, + struct smb_cred *scred, struct smb_vc **vcpp) +{ + static char objtype[] = "smb_vc"; + struct smb_vc *vcp; + int error = 0; + + ASSERT(MUTEX_HELD(&smb_vclist.co_lock)); + + /* + * Checks for valid uid/gid are now in + * smb_usr_ioc2vcspec, so at this point + * we know the user has right to create + * with the uid/gid in the vcspec. + */ + + vcp = kmem_zalloc(sizeof (struct smb_vc), KM_SLEEP); + + smb_co_init(VCTOCP(vcp), SMBL_VC, objtype); + vcp->vc_co.co_free = smb_vc_free; + 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_exit, objtype, CV_DRIVER, NULL); + + vcp->vc_number = atomic_inc_uint_nv(&smb_vcnext); + vcp->vc_state = SMBIOD_ST_NOTCONN; + vcp->vc_timo = SMB_DEFRQTIMO; + /* + * I think SMB_UID_UNKNOWN is not the correct + * initial value for vc_smbuid. See the long + * comment in smb_iod_sendrq() + */ + vcp->vc_smbuid = SMB_UID_UNKNOWN; /* XXX should be zero */ + vcp->vc_tdesc = &smb_tran_nbtcp_desc; + + /* + * These identify the connection. + */ + vcp->vc_zoneid = getzoneid(); + vcp->vc_uid = vcspec->owner; + vcp->vc_grp = vcspec->group; + vcp->vc_mode = vcspec->rights & SMBM_MASK; + + vcp->vc_domain = smb_strdup(vcspec->domain); + vcp->vc_username = smb_strdup(vcspec->username); + vcp->vc_srvname = smb_strdup(vcspec->srvname); + vcp->vc_paddr = smb_dup_sockaddr(vcspec->sap); + vcp->vc_laddr = smb_dup_sockaddr(vcspec->lap); + +#ifdef NOICONVSUPPORT + /* + * REVISIT + */ + error = iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower); + if (error) + goto errout; + + error = iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper); + if (error) + goto errout; + + if (vcspec->servercs[0]) { + + error = iconv_open(vcspec->servercs, vcspec->localcs, + &vcp->vc_toserver); + if (error) + goto errout; + + error = iconv_open(vcspec->localcs, vcspec->servercs, + &vcp->vc_tolocal); + if (error) + goto errout; + } +#endif /* NOICONVSUPPORT */ + + /* This fills in vcp->vc_tdata */ + if ((error = SMB_TRAN_CREATE(vcp, curproc)) != 0) + goto errout; + + /* Success! */ + smb_co_addchild(&smb_vclist, VCTOCP(vcp)); + *vcpp = vcp; + return (0); + +errout: + /* + * This will destroy the new vc. + * See: smb_vc_free + */ + smb_vc_rele(vcp); + return (error); +} + +void +smb_vc_hold(struct smb_vc *vcp) +{ + smb_co_hold(VCTOCP(vcp)); +} + +void +smb_vc_rele(struct smb_vc *vcp) +{ + smb_co_rele(VCTOCP(vcp)); +} + +void +smb_vc_kill(struct smb_vc *vcp) +{ + smb_co_kill(VCTOCP(vcp)); +} + +/* + * Normally called via smb_vc_rele() + * after co_usecount drops to zero. + * Also called via: smb_vc_kill() + * + * Shutdown the VC to this server, + * invalidate shares linked with it. + */ +/*ARGSUSED*/ +static void +smb_vc_gone(struct smb_connobj *cp) +{ + struct smb_vc *vcp = CPTOVC(cp); + + /* + * Was smb_vc_disconnect(vcp); + */ + smb_iod_disconnect(vcp); + + /* Note: smb_iod_destroy in vc_free */ +} + +static void +smb_vc_free(struct smb_connobj *cp) +{ + struct smb_vc *vcp = CPTOVC(cp); + + /* + * The VC has no more references, so + * no locks should be needed here. + * Make sure the IOD is gone. + */ + smb_iod_destroy(vcp); + + if (vcp->vc_tdata) + SMB_TRAN_DONE(vcp, curproc); + + SMB_STRFREE(vcp->vc_username); + SMB_STRFREE(vcp->vc_srvname); + SMB_STRFREE(vcp->vc_pass); + SMB_STRFREE(vcp->vc_domain); + if (vcp->vc_paddr) { + smb_free_sockaddr(vcp->vc_paddr); + vcp->vc_paddr = NULL; + } + if (vcp->vc_laddr) { + smb_free_sockaddr(vcp->vc_laddr); + vcp->vc_laddr = NULL; + } + +/* + * We are not using the iconv routines here. So commenting them for now. + * REVISIT. + */ +#ifdef NOTYETDEFINED + if (vcp->vc_tolower) + iconv_close(vcp->vc_tolower); + if (vcp->vc_toupper) + iconv_close(vcp->vc_toupper); + if (vcp->vc_tolocal) + iconv_close(vcp->vc_tolocal); + if (vcp->vc_toserver) + iconv_close(vcp->vc_toserver); +#endif + if (vcp->vc_intok) + kmem_free(vcp->vc_intok, vcp->vc_intoklen); + if (vcp->vc_outtok) + kmem_free(vcp->vc_outtok, vcp->vc_outtoklen); + if (vcp->vc_negtok) + kmem_free(vcp->vc_negtok, vcp->vc_negtoklen); + + cv_destroy(&vcp->iod_exit); + 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)); +} + + +/* + * Lookup share in the given VC. Share referenced and locked on return. + * VC expected to be locked on entry and will be left locked on exit. + */ +/*ARGSUSED*/ +int +smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp) +{ + struct smb_connobj *co; + struct smb_share *ssp = NULL; + + ASSERT(MUTEX_HELD(&vcp->vc_lock)); + + *sspp = NULL; + + /* var, head, next_field */ + SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { + ssp = CPTOSS(co); + + /* No new refs if _GONE is set. */ + if (ssp->ss_flags & SMBS_GONE) + continue; + + /* This has a hold, so no need to lock it. */ + if (strcmp(ssp->ss_name, shspec->name) == 0) + goto found; + } + return (ENOENT); + +found: + /* Return it with a hold. */ + smb_share_hold(ssp); + *sspp = ssp; + return (0); +} + + +static char smb_emptypass[] = ""; + +const char * +smb_vc_getpass(struct smb_vc *vcp) +{ + if (vcp->vc_pass) + return (vcp->vc_pass); + return (smb_emptypass); +} + +uint16_t +smb_vc_nextmid(struct smb_vc *vcp) +{ + uint16_t r; + + r = atomic_inc_16_nv(&vcp->vc_mid); + return (r); +} + +/* + * Get a pointer to the IP address suitable for passing to Trusted + * Extensions find_tpc() routine. Used by smbfs_mount_label_policy(). + * Compare this code to nfs_mount_label_policy() if problems arise. + * Without support for direct CIFS-over-TCP, we should always see + * an AF_NETBIOS sockaddr here. + */ +void * +smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers) +{ + switch (vcp->vc_paddr->sa_family) { + case AF_NETBIOS: { + struct sockaddr_nb *snb; + + *ipvers = IPV4_VERSION; + /*LINTED*/ + snb = (struct sockaddr_nb *)vcp->vc_paddr; + return ((void *)&snb->snb_ipaddr); + } + case AF_INET: { + struct sockaddr_in *sin; + + *ipvers = IPV4_VERSION; + /*LINTED*/ + sin = (struct sockaddr_in *)vcp->vc_paddr; + return ((void *)&sin->sin_addr); + } + case AF_INET6: { + struct sockaddr_in6 *sin6; + + *ipvers = IPV6_VERSION; + /*LINTED*/ + sin6 = (struct sockaddr_in6 *)vcp->vc_paddr; + return ((void *)&sin6->sin6_addr); + } + default: + SMBSDEBUG("invalid address family %d\n", + vcp->vc_paddr->sa_family); + *ipvers = 0; + return (NULL); + } +} + +/* + * Share implementation + */ +/* + * Allocate share structure and attach it to the given VC + * Connection expected to be locked on entry. Share will be returned + * in locked state. + */ +/*ARGSUSED*/ +int +smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp) +{ + static char objtype[] = "smb_ss"; + struct smb_share *ssp; + + ASSERT(MUTEX_HELD(&vcp->vc_lock)); + + ssp = kmem_zalloc(sizeof (struct smb_share), KM_SLEEP); + smb_co_init(SSTOCP(ssp), SMBL_SHARE, objtype); + ssp->ss_co.co_free = smb_share_free; + ssp->ss_co.co_gone = smb_share_gone; + + ssp->ss_name = smb_strdup(shspec->name); + ssp->ss_mount = NULL; + if (shspec->pass && shspec->pass[0]) + ssp->ss_pass = smb_strdup(shspec->pass); + ssp->ss_type = shspec->stype; + ssp->ss_tid = SMB_TID_UNKNOWN; + ssp->ss_mode = shspec->rights & SMBM_MASK; + ssp->ss_fsname = NULL; + smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); + *sspp = ssp; + + return (0); +} + +/* + * Normally called via smb_share_rele() + * after co_usecount drops to zero. + */ +static void +smb_share_free(struct smb_connobj *cp) +{ + struct smb_share *ssp = CPTOSS(cp); + + SMB_STRFREE(ssp->ss_name); + SMB_STRFREE(ssp->ss_pass); + SMB_STRFREE(ssp->ss_fsname); + smb_co_done(SSTOCP(ssp)); + kmem_free(ssp, sizeof (*ssp)); +} + +/* + * Normally called via smb_share_rele() + * after co_usecount drops to zero. + * Also called via: smb_share_kill() + */ +static void +smb_share_gone(struct smb_connobj *cp) +{ + struct smb_cred scred; + struct smb_share *ssp = CPTOSS(cp); + + smb_credinit(&scred, curproc, NULL); + smb_iod_shutdown_share(ssp); + smb_smb_treedisconnect(ssp, &scred); + smb_credrele(&scred); +} + +void +smb_share_hold(struct smb_share *ssp) +{ + smb_co_hold(SSTOCP(ssp)); +} + +void +smb_share_rele(struct smb_share *ssp) +{ + smb_co_rele(SSTOCP(ssp)); +} + +void +smb_share_kill(struct smb_share *ssp) +{ + smb_co_kill(SSTOCP(ssp)); +} + + +void +smb_share_invalidate(struct smb_share *ssp) +{ + ssp->ss_tid = SMB_TID_UNKNOWN; +} + +/* + * Returns NON-zero if the share is valid. + * Called with the share locked. + */ +int +smb_share_valid(struct smb_share *ssp) +{ + struct smb_vc *vcp = SSTOVC(ssp); + + ASSERT(MUTEX_HELD(&ssp->ss_lock)); + + if ((ssp->ss_flags & SMBS_CONNECTED) == 0) + return (0); + + if (ssp->ss_tid == SMB_TID_UNKNOWN) { + SMBIODEBUG("found TID unknown\n"); + ssp->ss_flags &= ~SMBS_CONNECTED; + } + + if (ssp->ss_vcgenid != vcp->vc_genid) { + SMBIODEBUG("wrong genid\n"); + ssp->ss_flags &= ~SMBS_CONNECTED; + } + + return (ssp->ss_flags & SMBS_CONNECTED); +} + +/* + * Connect (or reconnect) a share object. + * Called with the share locked. + */ +int +smb_share_tcon(struct smb_share *ssp) +{ + struct smb_vc *vcp = SSTOVC(ssp); + clock_t tmo; + int error; + + ASSERT(MUTEX_HELD(&ssp->ss_lock)); + + if (ssp->ss_flags & SMBS_CONNECTED) { + SMBIODEBUG("alread connected?"); + return (0); + } + + /* + * Wait for completion of any state changes + * that might be underway. + */ + while (ssp->ss_flags & SMBS_RECONNECTING) { + ssp->ss_conn_waiters++; + tmo = cv_wait_sig(&ssp->ss_conn_done, &ssp->ss_lock); + ssp->ss_conn_waiters--; + if (tmo == 0) { + /* Interrupt! */ + return (EINTR); + } + } + + /* Did someone else do it for us? */ + if (ssp->ss_flags & SMBS_CONNECTED) + return (0); + + /* + * OK, we'll do the work. + */ + ssp->ss_flags |= SMBS_RECONNECTING; + + /* Drop the lock while doing the call. */ + SMB_SS_UNLOCK(ssp); + error = smb_smb_treeconnect(ssp, &vcp->vc_scred); + SMB_SS_LOCK(ssp); + + if (!error) + ssp->ss_flags |= SMBS_CONNECTED; + ssp->ss_flags &= ~SMBS_RECONNECTING; + + /* They can all go ahead! */ + if (ssp->ss_conn_waiters) + cv_broadcast(&ssp->ss_conn_done); + + return (error); +} + +const char * +smb_share_getpass(struct smb_share *ssp) +{ + struct smb_vc *vcp; + + if (ssp->ss_pass) + return (ssp->ss_pass); + vcp = SSTOVC(ssp); + if (vcp->vc_pass) + return (vcp->vc_pass); + return (smb_emptypass); +} + +int +smb_share_count(void) +{ + struct smb_connobj *covc, *coss; + struct smb_vc *vcp; + zoneid_t zoneid = getzoneid(); + int nshares = 0; + + SMB_CO_LOCK(&smb_vclist); + SLIST_FOREACH(covc, &smb_vclist.co_children, co_next) { + vcp = CPTOVC(covc); + + /* VCs in other zones are invisibile. */ + if (vcp->vc_zoneid != zoneid) + continue; + + SMB_VC_LOCK(vcp); + + /* var, head, next_field */ + SLIST_FOREACH(coss, &(VCTOCP(vcp)->co_children), co_next) { + nshares++; + } + + SMB_VC_UNLOCK(vcp); + } + SMB_CO_UNLOCK(&smb_vclist); + + return (nshares); +} + +/* + * Solaris zones support + */ +/*ARGSUSED*/ +void +lingering_vc(struct smb_vc *vc) +{ + /* good place for a breakpoint */ + DEBUG_ENTER("lingering VC"); +} + +/* + * On zone shutdown, kill any IOD threads still running in this zone. + */ +/* ARGSUSED */ +void +nsmb_zone_shutdown(zoneid_t zoneid, void *data) +{ + struct smb_connobj *co; + struct smb_vc *vcp; + + SMB_CO_LOCK(&smb_vclist); + SLIST_FOREACH(co, &smb_vclist.co_children, co_next) { + vcp = CPTOVC(co); + + if (vcp->vc_zoneid != zoneid) + continue; + + /* + * This will close the connection, and + * cause the IOD thread to terminate. + */ + smb_vc_kill(vcp); + } + SMB_CO_UNLOCK(&smb_vclist); +} + +/* + * On zone destroy, kill any IOD threads and free all resources they used. + */ +/* ARGSUSED */ +void +nsmb_zone_destroy(zoneid_t zoneid, void *data) +{ + struct smb_connobj *co; + struct smb_vc *vcp; + + /* + * We will repeat what should have already happened + * in zone_shutdown to make things go away. + * + * There should have been an smb_vc_rele call + * by now for all VCs in the zone. If not, + * there's probably more we needed to do in + * the shutdown call. + */ + + SMB_CO_LOCK(&smb_vclist); + + if (smb_vclist.co_usecount > 1) { + SMBERROR("%d connections still active\n", + smb_vclist.co_usecount - 1); + } + + /* var, head, next_field */ + SLIST_FOREACH(co, &smb_vclist.co_children, co_next) { + vcp = CPTOVC(co); + + if (vcp->vc_zoneid != zoneid) + continue; + + /* Debugging */ + lingering_vc(vcp); + } + + SMB_CO_UNLOCK(&smb_vclist); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h new file mode 100644 index 0000000000..8211c01d1b --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h @@ -0,0 +1,481 @@ +/* + * 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. + * + * $Id: smb_conn.h,v 1.32.42.1 2005/05/27 02:35:29 lindak Exp $ + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_CONN_H +#define _SMB_CONN_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/t_lock.h> +#include <sys/queue.h> /* for SLIST below */ +#include <sys/uio.h> +#include <netsmb/smb_dev.h> + +#ifndef _KERNEL +#error "Not _KERNEL?" +#endif + +/* + * Credentials of user/process for processing in the connection procedures + */ +typedef struct smb_cred { + pid_t vc_pid; + cred_t *vc_ucred; +} smb_cred_t; + +/* + * Common object flags + */ +#define SMBO_GONE 0x1000000 + +/* + * 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. + */ + +#define SMBV_LONGNAMES 0x0004 /* conn configured to use long names */ +#define SMBV_ENCRYPT 0x0008 /* server demands encrypted password */ +#define SMBV_WIN95 0x0010 /* used to apply bugfixes for this OS */ +#define SMBV_NT4 0x0020 /* used when NT4 issues invalid resp */ +#define SMBV_RECONNECTING 0x0040 /* conn in process of reconnection */ +/* 0x0200 unused - was SMBV_FAILED */ +#define SMBV_UNICODE 0x0400 /* conn configured to use Unicode */ +#define SMBV_EXT_SEC 0x0800 /* conn to use extended security */ + +/* + * Note: the common "obj" level uses this GONE flag by + * the name SMBO_GONE. Keep this alias as a reminder. + */ +#define SMBV_GONE SMBO_GONE + +/* + * bits in smb_share ss_flags (a.k.a. ss_co.co_flags) + */ +#define SMBS_RECONNECTING 0x0002 +#define SMBS_CONNECTED 0x0004 +#define SMBS_TCON_WAIT 0x0008 +#define SMBS_1980 0x0010 +/* + * ^ This partition can't handle dates before 1980. It's probably a FAT + * partition but could be some other ancient FS type + */ +#define SMBS_RESUMEKEYS 0x0010 /* must use resume keys */ +/* + * Note: the common "obj" level uses this GONE flag by + * the name SMBO_GONE. Keep this alias as a reminder. + */ +#define SMBS_GONE SMBO_GONE + +/* + * Negotiated protocol parameters + */ +struct smb_sopt { + int sv_proto; + int16_t sv_tz; /* offset in min relative to UTC */ + uint32_t sv_maxtx; /* maximum transmit buf size */ + uchar_t sv_sm; /* security mode */ + 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_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; + +/* + * network IO daemon states + * really connection states. + */ +enum smbiod_state { + SMBIOD_ST_NOTCONN, /* no connect request was made */ + SMBIOD_ST_RECONNECT, /* a [re]connect attempt is in progress */ + SMBIOD_ST_TRANACTIVE, /* transport level is up */ + SMBIOD_ST_NEGOACTIVE, /* completed negotiation */ + SMBIOD_ST_SSNSETUP, /* started (a) session setup */ + SMBIOD_ST_VCACTIVE, /* session established */ + SMBIOD_ST_DEAD /* connection broken, transport is down */ +}; + + +/* + * Info structures + */ +#define SMB_INFO_NONE 0 +#define SMB_INFO_VC 2 +#define SMB_INFO_SHARE 3 + +struct smb_vc_info { + int itype; + int usecount; + uid_t uid; /* user id of connection */ + gid_t gid; /* group of connection */ + mode_t mode; /* access mode */ + int flags; + enum smbiod_state iodstate; + struct smb_sopt sopt; + char srvname[SMB_MAXSRVNAMELEN+1]; + char vcname[128]; +}; +typedef struct smb_vc_info smb_vc_info_t; + +struct smb_share_info { + int itype; + int usecount; + ushort_t tid; /* TID */ + int type; /* share type */ + uid_t uid; /* user id of connection */ + gid_t gid; /* group of connection */ + mode_t mode; /* access mode */ + int flags; + char sname[128]; +}; +typedef struct smb_share_info smb_share_info_t; + +struct smb_rq; +/* This declares struct smb_rqhead */ +TAILQ_HEAD(smb_rqhead, smb_rq); + +#define SMB_NBTIMO 15 +#define SMB_DEFRQTIMO 30 /* 30 for oplock revoke/writeback */ +#define SMBWRTTIMO 60 +#define SMBSSNSETUPTIMO 60 +#define SMBNOREPLYWAIT (0) + +#define SMB_DIALECT(vcp) ((vcp)->vc_sopt.sv_proto) + +/* + * Connection object + */ + +#define SMB_CO_LOCK(cp) mutex_enter(&(cp)->co_lock) +#define SMB_CO_UNLOCK(cp) mutex_exit(&(cp)->co_lock) + +/* + * Common part of smb_vc, smb_share + * Locking: co_lock protects most + * fields in this struct, except + * as noted below: + */ +struct smb_connobj { + kmutex_t co_lock; + int co_level; /* SMBL_ */ + int co_flags; + int co_usecount; + + /* Note: must lock co_parent before child. */ + struct smb_connobj *co_parent; + + /* this.co_lock protects the co_children list */ + SLIST_HEAD(, smb_connobj) co_children; + + /* + * Linkage in parent's list of children. + * Must hold parent.co_lock to traverse. + */ + SLIST_ENTRY(smb_connobj) co_next; + + /* These two are set only at creation. */ + void (*co_gone)(struct smb_connobj *); + void (*co_free)(struct smb_connobj *); +}; +typedef struct smb_connobj smb_connobj_t; + +/* + * Virtual Circuit (session) to a server. + * This is the most (over)complicated part of SMB protocol. + * For the user security level (usl), each session with different remote + * user name has its own VC. + * It is unclear however, should share security level (ssl) allow additional + * VCs, because user name is not used and can be the same. On other hand, + * multiple VCs allows us to create separate sessions to server on a per + * user basis. + */ + +typedef struct smb_vc { + struct smb_connobj vc_co; + enum smbiod_state vc_state; + kcondvar_t vc_statechg; + ksema_t vc_sendlock; + + zoneid_t vc_zoneid; + char *vc_srvname; + struct sockaddr *vc_paddr; /* server addr */ + struct sockaddr *vc_laddr; /* local addr, if any */ + char *vc_domain; /* domain that defines username */ + char *vc_username; + char *vc_pass; /* password for usl case */ + uchar_t vc_lmhash[SMB_PWH_MAX]; + uchar_t vc_nthash[SMB_PWH_MAX]; + + uint_t vc_timo; /* default request timeout */ + int vc_maxvcs; /* maximum number of VC per conn */ + + void *vc_tolower; /* local charset */ + void *vc_toupper; /* local charset */ + void *vc_toserver; /* local charset to server one */ + void *vc_tolocal; /* server charset to local one */ + int vc_number; /* number of this VC from client side */ + int vc_genid; /* "generation ID" of this VC */ + uid_t vc_uid; /* user id of connection */ + gid_t vc_grp; /* group of connection */ + mode_t vc_mode; /* access mode */ + uint16_t vc_smbuid; /* auth. session ID from server */ + + uint8_t vc_hflags; /* or'ed with flags in the smb header */ + uint16_t vc_hflags2; /* or'ed with flags in the smb header */ + void *vc_tdata; /* transport control block */ + struct smb_tran_desc *vc_tdesc; + int vc_chlen; /* actual challenge length */ + uchar_t vc_challenge[SMB_MAXCHALLENGELEN]; + uint16_t vc_mid; /* multiplex id */ + int vc_vopt; /* local options SMBVOPT_ */ + struct smb_sopt vc_sopt; /* server options */ + struct smb_cred vc_scred; /* used in reconnect procedure */ + int vc_txmax; /* max tx/rx packet size */ + int vc_rxmax; /* max readx data size */ + int vc_wxmax; /* max writex data size */ + + /* Authentication tokens */ + size_t vc_intoklen; + caddr_t vc_intok; + size_t vc_outtoklen; + caddr_t vc_outtok; + size_t vc_negtoklen; + caddr_t vc_negtok; + + /* + * These members used to be in struct smbiod, + * which has been eliminated. + */ + krwlock_t iod_rqlock; /* iod_rqlist */ + struct smb_rqhead iod_rqlist; /* list of outstanding reqs */ + struct _kthread *iod_thr; /* the IOD (reader) thread */ + kcondvar_t iod_exit; /* IOD thread termination */ + int iod_flags; /* see SMBIOD_* below */ + int iod_newrq; /* send needed (iod_rqlock) */ + int iod_muxfull; /* maxmux limit reached */ + uint_t iod_rqwaiting; /* count of waiting requests */ +} smb_vc_t; + +#define vc_lock vc_co.co_lock +#define vc_flags vc_co.co_flags +#define vc_maxmux vc_sopt.sv_maxmux + +#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) + +/* Bits in iod_flags */ +#define SMBIOD_RUNNING 0x0001 +#define SMBIOD_SHUTDOWN 0x0002 + +/* + * smb_share structure describes connection to the given SMB share (tree). + * Connection to share is always built on top of the VC. + */ + +typedef struct smb_share { + struct smb_connobj ss_co; + kcondvar_t ss_conn_done; /* wait for reconnect */ + int ss_conn_waiters; + char *ss_name; + char *ss_pass; /* share password, can be null */ + char *ss_fsname; + void *ss_mount; /* used for smb up/down */ + uint16_t ss_tid; /* TID */ + int ss_type; /* share type */ + mode_t ss_mode; /* access mode */ + int ss_vcgenid; /* check VC generation ID */ + uint32_t ss_maxfilenamelen; + int ss_sopt; /* local options SMBSOPT_ */ +} smb_share_t; + +#define ss_lock ss_co.co_lock +#define ss_flags ss_co.co_flags + +#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 *)(cp)) +#define VCTOCP(vcp) (&(vcp)->vc_co) + +#define CPTOSS(cp) ((struct smb_share *)(cp)) +#define SSTOVC(ssp) CPTOVC(((ssp)->ss_co.co_parent)) +#define SSTOCP(ssp) (&(ssp)->ss_co) + +/* + * This is used internally to pass all the info about + * some VC that an ioctl caller is looking for. + */ +struct smb_vcspec { + char *srvname; + struct sockaddr *sap; + struct sockaddr *lap; + int optflags; + char *domain; + char *username; + char *pass; + uid_t owner; + gid_t group; + mode_t mode; + mode_t rights; + char *localcs; + char *servercs; + size_t toklen; + caddr_t tok; +}; +typedef struct smb_vcspec smb_vcspec_t; + +/* + * This is used internally to pass all the info about + * some share that an ioctl caller is looking for. + */ +struct smb_sharespec { + char *name; + char *pass; + mode_t mode; + mode_t rights; + uid_t owner; + gid_t group; + int stype; + int optflags; +}; +typedef struct smb_sharespec smb_sharespec_t; + + +/* + * Call-back operations vector, so the netsmb module + * can notify smbfs about events affecting mounts. + * Installed in netsmb after smbfs loads. + */ +/* #define NEED_SMBFS_CALLBACKS 1 */ +#ifdef NEED_SMBFS_CALLBACKS +typedef struct smb_fscb { + void (*fscb_dead)(smb_share_t *); + void (*fscb_down)(smb_share_t *); + void (*fscb_up)(smb_share_t *); +} smb_fscb_t; +/* Install the above vector, or pass NULL to clear it. */ +int smb_fscb_set(smb_fscb_t *); +#endif /* NEED_SMBFS_CALLBACKS */ + +/* + * IOD functions + */ +int smb_iod_create(struct smb_vc *vcp); +int smb_iod_destroy(struct smb_vc *vcp); +int smb_iod_connect(struct smb_vc *vcp); +int smb_iod_disconnect(struct smb_vc *vcp); +int smb_iod_addrq(struct smb_rq *rqp); +int smb_iod_multirq(struct smb_rq *rqp); +int smb_iod_waitrq(struct smb_rq *rqp); +int smb_iod_removerq(struct smb_rq *rqp); +void smb_iod_shutdown_share(struct smb_share *ssp); +void smb_iod_notify_down(struct smb_vc *vcp); +void smb_iod_notify_up(struct smb_vc *vcp); + +/* + * Session level functions + */ +int smb_sm_init(void); +int smb_sm_idle(void); +void smb_sm_done(void); + +int smb_sm_negotiate(struct smb_vcspec *vcspec, + struct smb_cred *scred, struct smb_vc **vcpp); +int smb_sm_ssnsetup(struct smb_vcspec *vcspec, + struct smb_cred *scred, struct smb_vc *vcp); +int smb_sm_tcon(struct smb_sharespec *shspec, struct smb_cred *scred, + struct smb_vc *vcp, struct smb_share **sspp); + +/* + * VC level functions + */ +int smb_vc_setup(struct smb_vcspec *vcspec, struct smb_cred *scred, + struct smb_vc *vcp, int is_ss); +int smb_vc_create(struct smb_vcspec *vcspec, + struct smb_cred *scred, struct smb_vc **vcpp); +int smb_vc_negotiate(struct smb_vc *vcp, struct smb_cred *scred); +int smb_vc_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred); +void smb_vc_hold(struct smb_vc *vcp); +void smb_vc_rele(struct smb_vc *vcp); +void smb_vc_kill(struct smb_vc *vcp); +int smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp); +const char *smb_vc_getpass(struct smb_vc *vcp); +uint16_t smb_vc_nextmid(struct smb_vc *vcp); +void *smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers); + +/* + * share level functions + */ +int smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, + struct smb_cred *scred, struct smb_share **sspp); + +void smb_share_hold(struct smb_share *ssp); +void smb_share_rele(struct smb_share *ssp); +void smb_share_kill(struct smb_share *ssp); + +void smb_share_invalidate(struct smb_share *ssp); +int smb_share_tcon(struct smb_share *ssp); +int smb_share_valid(struct smb_share *ssp); +const char *smb_share_getpass(struct smb_share *ssp); +int smb_share_count(void); + +/* + * 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_ssnclose(struct smb_vc *vcp, struct smb_cred *scred); +int smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred); +int smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred); +int smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo); +#ifdef APPLE +int smb_smb_checkdir(struct smb_share *ssp, void *dnp, + char *name, int nmlen, struct smb_cred *scred); +#endif +int smb_rwuio(struct smb_share *ssp, uint16_t fid, uio_rw_t rw, + uio_t *uiop, struct smb_cred *scred, int timo); + +#endif /* _SMB_CONN_H */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c new file mode 100644 index 0000000000..64057080b9 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c @@ -0,0 +1,288 @@ +/* + * 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. + * + * $Id: smb_crypt.c,v 1.13 2005/01/26 23:50:50 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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/crypto/api.h> +#include <sys/crypto/common.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_dev.h> + +static uchar_t N8[] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + +static void +smb_E(const uchar_t *key, uchar_t *data, uchar_t *dest) +{ + int rv; + uchar_t kk[8]; + crypto_mechanism_t mech; + crypto_data_t d1, d2; + crypto_key_t keyt; + + + bzero(&d1, sizeof (crypto_data_t)); + bzero(&d2, sizeof (crypto_data_t)); + /* + * 'Key' here is the username - 7-bytes. Convert that to + * to a 8-byte string. + */ + kk[0] = key[0] & 0xfe; + kk[1] = key[0] << 7 | (key[1] >> 1 & 0xfe); + kk[2] = key[1] << 6 | (key[2] >> 2 & 0xfe); + kk[3] = key[2] << 5 | (key[3] >> 3 & 0xfe); + kk[4] = key[3] << 4 | (key[4] >> 4 & 0xfe); + kk[5] = key[4] << 3 | (key[5] >> 5 & 0xfe); + kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe); + kk[7] = key[6] << 1; + + keyt.ck_format = CRYPTO_KEY_RAW; + keyt.ck_length = 8 * 8; + keyt.ck_data = (void *)kk; + + d1.cd_format = CRYPTO_DATA_RAW; + d1.cd_length = 8; + d1.cd_offset = 0; + d1.cd_raw.iov_len = 8; + d1.cd_raw.iov_base = (void *)data; + + d2.cd_format = CRYPTO_DATA_RAW; + d2.cd_length = 8; + d2.cd_offset = 0; + d2.cd_raw.iov_len = 8; + d2.cd_raw.iov_base = (void *)dest; + + mech.cm_type = crypto_mech2id(SUN_CKM_DES_ECB); + if (mech.cm_type == CRYPTO_MECH_INVALID) + cmn_err(CE_NOTE, "Invalid algorithm\n"); + mech.cm_param = NULL; + mech.cm_param_len = 0; + + rv = crypto_encrypt(&mech, &d1, &keyt, NULL, &d2, NULL); + if (rv != CRYPTO_SUCCESS) + SMBSDEBUG("crypto_encrypt failed.\n"); +} + +/* + * Compute the LM hash, which is used to compute the LM response. + */ +void +smb_oldlm_hash(const char *apwd, uchar_t *lmhash) +{ + uchar_t P14[14+1]; + + /* Convert apwd to upper case, zero extend. */ + bzero(P14, sizeof (P14)); + smb_toupper(apwd, (char *)P14, 14); + + /* + * lmhash = concat(Ex(P14, N8), zeros(5)); + */ + bzero(lmhash, 21); + smb_E(P14, N8, lmhash); + smb_E(P14 + 7, N8, lmhash + 8); +} + +/* + * Compute an LM or NTLM response given the LM or NTLM hash and a + * challenge. Note: This now replaces smb_ntlmresponse which + * used to compute a different hash and then do the same + * response computation as found here. Now that the hash + * is computed by the caller, this is used for both. + */ +int +smb_lmresponse(const uchar_t *hash, uchar_t *C8, uchar_t *RN) +{ + + smb_E(hash, C8, RN); + smb_E(hash + 7, C8, RN + 8); + smb_E(hash + 14, C8, RN + 16); + + return (0); +} + +/* + * Compute the NTLMv1 hash, which is used to compute both NTLMv1 and + * NTLMv2 responses. + */ +void +smb_ntlmv1hash(const char *apwd, uchar_t *v1hash) +{ + u_int16_t *unipwd; + MD4_CTX *ctxp; + size_t alen, unilen; + + alen = strlen(apwd); + unipwd = kmem_alloc(alen * 2, KM_SLEEP); + /* + * v1hash = concat(MD4(U(apwd)), zeros(5)); + */ + unilen = smb_strtouni(unipwd, apwd, alen, UCONV_IGNORE_NULL); + + ctxp = kmem_alloc(sizeof (MD4_CTX), KM_SLEEP); + MD4Init(ctxp); + MD4Update(ctxp, unipwd, unilen); + bzero(v1hash, 21); + MD4Final(v1hash, ctxp); + + kmem_free(ctxp, sizeof (MD4_CTX)); + kmem_free(unipwd, alen * 2); +} + +/* + * Note: smb_ntlmresponse() is gone. + * Use: smb_lmresponse() instead. + */ + +static void +HMACT64(const uchar_t *key, size_t key_len, const uchar_t *data, + size_t data_len, uchar_t *digest) +{ + MD5_CTX context; + uchar_t k_ipad[64]; /* inner padding - key XORd with ipad */ + uchar_t k_opad[64]; /* outer padding - key XORd with opad */ + int i; + + /* if key is longer than 64 bytes use only the first 64 bytes */ + if (key_len > 64) + key_len = 64; + + /* + * The HMAC-MD5 (and HMACT64) transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, data)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and data is the data being protected. + */ + + /* start out by storing key in pads */ + bzero(k_ipad, sizeof (k_ipad)); + bzero(k_opad, sizeof (k_opad)); + bcopy(key, k_ipad, key_len); + bcopy(key, k_opad, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* + * perform inner MD5 + */ + MD5Init(&context); /* init context for 1st pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + MD5Update(&context, data, data_len); /* then data of datagram */ + MD5Final(digest, &context); /* finish up 1st pass */ + + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st hash */ + MD5Final(digest, &context); /* finish up 2nd pass */ +} + +/* + * Compute an NTLMv2 response given the 21 byte NTLM(v1) hash, + * the user name, the destination workgroup/domain name, + * a challenge, and the blob. + */ +int +smb_ntlmv2response(const uchar_t *v1hash, const uchar_t *user, + const uchar_t *destination, uchar_t *C8, const uchar_t *blob, + size_t bloblen, uchar_t **RN, size_t *RNlen) +{ + u_int16_t *uniuser, *unidest; + size_t uniuserlen, unidestlen; + uchar_t v2hash[16]; + size_t len; + size_t datalen; + uchar_t *data, *data1; + size_t v2resplen; + uchar_t *v2resp; + + /* + * v2hash=HMACT64(v1hash, 16, concat(upcase(user), upcase(destination)) + * We assume that user and destination are supplied to us as + * upper-case UTF-8. + */ + len = strlen((char *)user); + uniuser = kmem_alloc(len * sizeof (u_int16_t) + 1, KM_SLEEP); + uniuserlen = smb_strtouni(uniuser, (char *)user, len, + UCONV_IGNORE_NULL); + len = strlen((char *)destination); + unidest = kmem_alloc(len * sizeof (u_int16_t) + 1, KM_SLEEP); + unidestlen = smb_strtouni(unidest, (char *)destination, len, + UCONV_IGNORE_NULL); + datalen = uniuserlen + unidestlen; + data = kmem_alloc(datalen, KM_SLEEP); + bcopy(uniuser, data, uniuserlen); + bcopy(unidest, data + uniuserlen, unidestlen); + kmem_free(uniuser, strlen((char *)user) * sizeof (u_int16_t) + 1); + kmem_free(unidest, len * sizeof (u_int16_t) + 1); + HMACT64(v1hash, 16, data, datalen, v2hash); + kmem_free(data, datalen); + + datalen = 8 + bloblen; + data1 = kmem_alloc(datalen, KM_SLEEP); + bcopy(C8, data1, 8); + bcopy(blob, data1 + 8, bloblen); + v2resplen = 16 + bloblen; + v2resp = kmem_alloc(v2resplen, KM_SLEEP); + HMACT64(v2hash, 16, data1, datalen, v2resp); + kmem_free(data1, datalen); + bcopy(blob, v2resp + 16, bloblen); + *RN = v2resp; + *RNlen = v2resplen; + return (0); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c new file mode 100644 index 0000000000..6e40480724 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c @@ -0,0 +1,938 @@ +/* + * 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. + * + * $Id: smb_dev.c,v 1.21 2004/12/13 00:25:18 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/sysmacros.h> +#include <sys/uio.h> +#include <sys/buf.h> +#include <sys/modctl.h> +#include <sys/open.h> +#include <sys/file.h> +#include <sys/kmem.h> +#include <sys/conf.h> +#include <sys/cmn_err.h> +#include <sys/stat.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sunldi.h> +#include <sys/policy.h> +#include <sys/zone.h> +#include <sys/pathname.h> +#include <sys/mount.h> +#include <sys/sdt.h> +#include <fs/fs_subr.h> +#include <sys/modctl.h> +#include <sys/devops.h> +#include <sys/thread.h> +#include <sys/mkdev.h> +#include <sys/types.h> +#include <sys/zone.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/mchain.h> /* for "htoles()" */ + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> +#include <netsmb/smb_pass.h> + +/* for version checks */ +const uint32_t nsmb_version = NSMB_VERSION; + +/* + * Userland code loops through minor #s 0 to 1023, looking for one which opens. + * Intially we create minor 0 and leave it for anyone. Minor zero will never + * actually get used - opening triggers creation of another (but private) minor, + * which userland code will get to and mark busy. + */ +#define SMBMINORS 1024 +static void *statep; +static major_t nsmb_major; +static minor_t nsmb_minor = 1; + +#define NSMB_MAX_MINOR (1 << 8) +#define NSMB_MIN_MINOR (NSMB_MAX_MINOR + 1) + +#define ILP32 1 +#define LP64 2 + +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. + */ +static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp); +static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp); +static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, + cred_t *credp, int *rvalp); +/* smbfs cb_ops */ +static struct cb_ops nsmb_cbops = { + nsmb_open, /* open */ + nsmb_close, /* close */ + nodev, /* strategy */ + nodev, /* print */ + nodev, /* dump */ + nodev, /* read */ + nodev, /* write */ + nsmb_ioctl, /* ioctl */ + nodev, /* devmap */ + nodev, /* mmap */ + nodev, /* segmap */ + nochpoll, /* poll */ + ddi_prop_op, /* prop_op */ + NULL, /* stream */ + D_MP, /* cb_flag */ + CB_REV, /* rev */ + nodev, /* int (*cb_aread)() */ + nodev /* int (*cb_awrite)() */ +}; + +/* + * Device options + */ +static int nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); +static int nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); +static int nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, + void *arg, void **result); + +static struct dev_ops nsmb_ops = { + DEVO_REV, /* devo_rev, */ + 0, /* refcnt */ + nsmb_getinfo, /* info */ + nulldev, /* identify */ + nulldev, /* probe */ + nsmb_attach, /* attach */ + nsmb_detach, /* detach */ + nodev, /* reset */ + &nsmb_cbops, /* driver ops - devctl interfaces */ + NULL, /* bus operations */ + NULL /* power */ +}; + +/* + * Module linkage information. + */ + +static struct modldrv nsmb_modldrv = { + &mod_driverops, /* Driver module */ + "SMBFS network driver v" NSMB_VER_STR, + &nsmb_ops /* Driver ops */ +}; + +static struct modlinkage nsmb_modlinkage = { + MODREV_1, + (void *)&nsmb_modldrv, + NULL +}; + +int +_init(void) +{ + int error; + + ddi_soft_state_init(&statep, sizeof (smb_dev_t), 1); + + /* Can initialize some mutexes also. */ + mutex_init(&dev_lck, NULL, MUTEX_DRIVER, NULL); + /* + * Create a major name and number. + */ + nsmb_major = ddi_name_to_major(NSMB_NAME); + nsmb_minor = 0; + + /* Connection data structures. */ + (void) smb_sm_init(); + + /* Initialize password Key chain DB. */ + smb_pkey_init(); + + zone_key_create(&nsmb_zone_key, NULL, nsmb_zone_shutdown, + nsmb_zone_destroy); + + /* + * Install the module. Do this after other init, + * to prevent entrances before we're ready. + */ + if ((error = mod_install((&nsmb_modlinkage))) != 0) { + + /* Same as 2nd half of _fini */ + (void) zone_key_delete(nsmb_zone_key); + smb_pkey_fini(); + smb_sm_done(); + mutex_destroy(&dev_lck); + ddi_soft_state_fini(&statep); + + return (error); + } + + return (0); +} + +int +_fini(void) +{ + int status; + + /* + * Prevent unload if we have active VCs + * or stored passwords + */ + if ((status = smb_sm_idle()) != 0) + return (status); + if ((status = smb_pkey_idle()) != 0) + return (status); + + /* + * Remove the module. Do this before destroying things, + * to prevent new entrances while we're destorying. + */ + if ((status = mod_remove(&nsmb_modlinkage)) != 0) { + return (status); + } + + (void) zone_key_delete(nsmb_zone_key); + + /* Destroy password Key chain DB. */ + smb_pkey_fini(); + + smb_sm_done(); + + mutex_destroy(&dev_lck); + ddi_soft_state_fini(&statep); + + return (status); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&nsmb_modlinkage, modinfop)); +} + +/*ARGSUSED*/ +static int +nsmb_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) +{ + int ret = DDI_SUCCESS; + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + *result = 0; + break; + case DDI_INFO_DEVT2INSTANCE: + *result = 0; + break; + default: + ret = DDI_FAILURE; + } + return (ret); +} + +static int +nsmb_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + smb_dev_t *sdp; + + if (cmd != DDI_ATTACH) + return (DDI_FAILURE); + /* + * only one instance - but we clone using the open routine + */ + if (ddi_get_instance(dip) > 0) + return (DDI_FAILURE); + + mutex_enter(&dev_lck); + + /* + * This is the Zero'th minor device which is created. + */ + if (ddi_soft_state_zalloc(statep, 0) == DDI_FAILURE) { + cmn_err(CE_WARN, "nsmb_attach: soft state alloc"); + goto attach_failed; + } + if (ddi_create_minor_node(dip, "nsmb", S_IFCHR, 0, DDI_PSEUDO, + NULL) == DDI_FAILURE) { + cmn_err(CE_WARN, "nsmb_attach: create minor"); + goto attach_failed; + } + if ((sdp = ddi_get_soft_state(statep, 0)) == NULL) { + cmn_err(CE_WARN, "nsmb_attach: get soft state"); + ddi_remove_minor_node(dip, NULL); + goto attach_failed; + } + + /* + * Need to see if this field is required. + * REVISIT + */ + sdp->smb_dip = dip; + sdp->sd_seq = 0; + sdp->sd_opened = 1; + + mutex_exit(&dev_lck); + ddi_report_dev(dip); + return (DDI_SUCCESS); + +attach_failed: + ddi_soft_state_free(statep, 0); + mutex_exit(&dev_lck); + return (DDI_FAILURE); +} + +/*ARGSUSED*/ +static int +nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + + if (cmd != DDI_DETACH) + return (DDI_FAILURE); + if (ddi_get_instance(dip) > 0) + return (DDI_FAILURE); + + ddi_soft_state_free(statep, 0); + ddi_remove_minor_node(dip, NULL); + + return (DDI_SUCCESS); +} + +/*ARGSUSED*/ +static int +nsmb_ioctl(dev_t dev, + int cmd, + intptr_t arg, + int mode, + cred_t *credp, + int *rvalp) +{ + smb_dev_t *sdp; + struct smb_vc *vcp = NULL; + struct smb_share *ssp = NULL; + struct smb_cred scred; + int err, error; + uid_t uid; + + /* Free any+all of these at end of switch. */ + smbioc_lookup_t *sioc = NULL; + smbioc_rq_t *srq = NULL; + smbioc_rw_t *rwrq = NULL; + smbioc_t2rq_t *strq = NULL; + smbioc_pk_t *pk = NULL; + + sdp = ddi_get_soft_state(statep, getminor(dev)); + if (sdp == NULL) { + return (DDI_FAILURE); + } + if ((sdp->sd_flags & NSMBFL_OPEN) == 0) { + return (EBADF); + } + + /* + * Dont give access if the zone id is not as the same as we + * set in the nsmb_open or dont belong to the global zone. + * Check if the user belongs to this zone.. + */ + if (sdp->zoneid != getzoneid()) + return (EIO); + if (cmd != SMBIOC_TDIS && + zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) + return (EIO); + + + error = 0; + smb_credinit(&scred, curproc, credp); + switch (cmd) { + case SMBIOC_GETVERS: + ddi_copyout(&nsmb_version, (void *)arg, + sizeof (nsmb_version), mode); + break; + + case SMBIOC_REQUEST: + if (sdp->sd_share == NULL) { + error = ENOTCONN; + break; + } + srq = kmem_alloc(sizeof (*srq), KM_SLEEP); + if (ddi_copyin((void *) arg, srq, + sizeof (*srq), mode)) { + error = EFAULT; + break; + } + error = smb_usr_simplerequest(sdp->sd_share, + srq, &scred); + ddi_copyout(srq, (void *)arg, + SMBIOC_RQ_COPYOUT_SIZE, mode); + break; + + case SMBIOC_T2RQ: + if (sdp->sd_share == NULL) { + error = ENOTCONN; + break; + } + strq = kmem_alloc(sizeof (*strq), KM_SLEEP); + if (ddi_copyin((void *)arg, strq, + sizeof (*strq), mode)) { + error = EFAULT; + break; + } + error = smb_usr_t2request(sdp->sd_share, strq, &scred); + ddi_copyout(strq, (void *)arg, + SMBIOC_T2RQ_COPYOUT_SIZE, mode); + break; + + case SMBIOC_READ: + case SMBIOC_WRITE: + if ((ssp = sdp->sd_share) == NULL) { + error = ENOTCONN; + break; + } + rwrq = kmem_alloc(sizeof (*rwrq), KM_SLEEP); + if (ddi_copyin((void *)arg, rwrq, + sizeof (*rwrq), mode)) { + error = EFAULT; + break; + } + error = smb_usr_rw(ssp, rwrq, cmd, &scred); + ddi_copyout(rwrq, (void *)arg, + SMBIOC_RW_COPYOUT_SIZE, mode); + break; + + case SMBIOC_NEGOTIATE: + /* Should be no VC (and no share) */ + if (sdp->sd_vc || sdp->sd_share) { + error = EISCONN; + break; + } + sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP); + if (ddi_copyin((void *)arg, sioc, + sizeof (*sioc), mode)) { + error = EFAULT; + break; + } + vcp = NULL; + ssp = NULL; + error = smb_usr_negotiate(sioc, &scred, &vcp); + if (error) + break; + if (vcp) { + /* + * The VC has a hold from _negotiate + * which we keep until nsmb_close(). + */ + sdp->sd_level = SMBL_VC; + sdp->sd_vc = vcp; + /* + * If we just created this VC, and + * this minor is doing the setup, + * keep track of that fact here. + */ + if (vcp->vc_state < SMBIOD_ST_VCACTIVE) + sdp->sd_flags |= NSMBFL_NEWVC; + + } + /* + * Copyout the "out token" (security blob). + * + * This code used to be near the end of + * smb_usr_negotiate(). Moved the copyout + * calls here so we know the "mode" + */ + if (vcp->vc_outtok) { + /* + * Note: will copyout sioc below + * including sioc.vc_outtoklen, + * so we no longer put the length + * at the start of the outtok data. + */ + sioc->ioc_ssn.ioc_outtoklen = + vcp->vc_outtoklen; + err = ddi_copyout( + vcp->vc_outtok, + sioc->ioc_ssn.ioc_outtok, + vcp->vc_outtoklen, mode); + if (err) { + error = EFAULT; + break; + } + /* + * Save this blob in vc_negtok. + * We need it in case we have to + * reconnect. + * + * Set vc_negtok = vc_outtok + * but free vc_negtok first. + */ + if (vcp->vc_negtok) { + kmem_free( + vcp->vc_negtok, + vcp->vc_negtoklen); + vcp->vc_negtok = NULL; + vcp->vc_negtoklen = 0; + } + vcp->vc_negtok = vcp->vc_outtok; + vcp->vc_negtoklen = vcp->vc_outtoklen; + vcp->vc_outtok = NULL; + vcp->vc_outtoklen = 0; + } + /* + * Added copyout here of (almost) + * the whole struct, even though + * the lib only needs _outtoklen. + * We may put other things in this + * struct that user-land needs. + */ + err = ddi_copyout(sioc, (void *)arg, + SMBIOC_LOOK_COPYOUT_SIZE, mode); + if (err) + error = EFAULT; + break; + + case SMBIOC_SSNSETUP: + /* Must have a VC, but no share. */ + if (sdp->sd_share) { + error = EISCONN; + break; + } + if (!sdp->sd_vc) { + error = ENOTCONN; + break; + } + sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP); + if (ddi_copyin((void *)arg, sioc, + sizeof (*sioc), mode)) { + error = EFAULT; + break; + } + vcp = sdp->sd_vc; + ssp = NULL; + error = smb_usr_ssnsetup(sioc, &scred, vcp); + if (error) + break; + /* + * If this minor has finished ssn setup, + * turn off the NEWVC flag, otherwise we + * will kill this VC when we close. + */ + if (vcp->vc_state == SMBIOD_ST_VCACTIVE) + sdp->sd_flags &= ~NSMBFL_NEWVC; + /* + * Copyout the "out token" (security blob). + * + * This code used to be near the end of + * smb_usr_ssnsetup(). Moved the copyout + * calls here so we know the "mode" + */ + if (vcp->vc_outtok) { + /* + * Note: will copyout sioc below + * including sioc.vc_outtoklen, + * so we no longer put the length + * at the start of the outtok data. + */ + sioc->ioc_ssn.ioc_outtoklen = + vcp->vc_outtoklen; + err = ddi_copyout( + vcp->vc_outtok, + sioc->ioc_ssn.ioc_outtok, + vcp->vc_outtoklen, mode); + if (err) { + error = EFAULT; + break; + } + /* + * Done with vc_outtok. Similar, + * but NOT the same as after the + * smb_usr_negotiate call above. + */ + kmem_free( + vcp->vc_outtok, + vcp->vc_outtoklen); + vcp->vc_outtok = NULL; + vcp->vc_outtoklen = 0; + } + /* Added copyout here... (see above) */ + err = ddi_copyout(sioc, (void *)arg, + SMBIOC_LOOK_COPYOUT_SIZE, mode); + if (err) + error = EFAULT; + break; + + case SMBIOC_TCON: + /* Must have a VC, but no share. */ + if (sdp->sd_share) { + error = EISCONN; + break; + } + if (!sdp->sd_vc) { + error = ENOTCONN; + break; + } + sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP); + if (ddi_copyin((void *)arg, sioc, + sizeof (*sioc), mode)) { + error = EFAULT; + break; + } + vcp = sdp->sd_vc; + ssp = NULL; + error = smb_usr_tcon(sioc, &scred, vcp, &ssp); + if (error) + break; + if (ssp) { + /* + * The share has a hold from _tcon + * which we keep until nsmb_close() + * or the SMBIOC_TDIS below. + */ + sdp->sd_share = ssp; + sdp->sd_level = SMBL_SHARE; + } + /* No need for copyout here. */ + break; + + case SMBIOC_TDIS: + if (sdp->sd_share == NULL) { + error = ENOTCONN; + break; + } + smb_share_rele(sdp->sd_share); + sdp->sd_share = NULL; + sdp->sd_level = SMBL_VC; + break; + case SMBIOC_FLAGS2: + if (sdp->sd_share == NULL) { + error = ENOTCONN; + break; + } + if (!sdp->sd_vc) { + error = ENOTCONN; + break; + } + vcp = sdp->sd_vc; + /* + * Return the flags2 value. + */ + ddi_copyout(&vcp->vc_hflags2, (void *)arg, + sizeof (u_int16_t), mode); + break; + + case SMBIOC_PK_ADD: + pk = kmem_alloc(sizeof (*pk), KM_SLEEP); + if (ddi_copyin((void *)arg, pk, + sizeof (*pk), mode)) { + error = EFAULT; + break; + } + error = smb_pkey_add(pk, credp); + break; + + case SMBIOC_PK_DEL: + pk = kmem_alloc(sizeof (*pk), KM_SLEEP); + if (ddi_copyin((void *)arg, pk, + sizeof (*pk), mode)) { + error = EFAULT; + break; + } + error = smb_pkey_del(pk, credp); + break; + + case SMBIOC_PK_CHK: + pk = kmem_alloc(sizeof (*pk), KM_SLEEP); + if (ddi_copyin((void *)arg, pk, + sizeof (*pk), mode)) { + error = EFAULT; + break; + } + error = smb_pkey_check(pk, credp); + /* + * Note: Intentionally DO NOT copyout + * the pasword here. It can only be + * retrieved by internal calls. This + * ioctl only tells the caller if the + * keychain entry exists. + */ + break; + + case SMBIOC_PK_DEL_OWNER: + uid = crgetruid(credp); + error = smb_pkey_deluid(uid, credp); + break; + + case SMBIOC_PK_DEL_EVERYONE: + uid = (uid_t)-1; + error = smb_pkey_deluid(uid, credp); + break; + + default: + error = ENODEV; + } + + /* + * Let's just do all the kmem_free stuff HERE, + * instead of at every switch break. + */ + + /* SMBIOC_REQUEST */ + if (srq) + kmem_free(srq, sizeof (*srq)); + + /* SMBIOC_T2RQ */ + if (strq) + kmem_free(strq, sizeof (*strq)); + + /* SMBIOC_READ */ + /* SMBIOC_WRITE */ + if (rwrq) + kmem_free(rwrq, sizeof (*rwrq)); + + /* SMBIOC_NEGOTIATE */ + /* SMBIOC_SSNSETUP */ + /* SMBIOC_TCON */ + if (sioc) { + /* + * This data structure may contain + * cleartext passwords, so zap it. + */ + bzero(sioc, sizeof (*sioc)); + kmem_free(sioc, sizeof (*sioc)); + } + + /* SMBIOC_PK_... */ + if (pk) { + /* + * This data structure may contain + * cleartext passwords, so zap it. + */ + bzero(pk, sizeof (*pk)); + kmem_free(pk, sizeof (*pk)); + } + + smb_credrele(&scred); + + return (error); +} + +/*ARGSUSED*/ +static int +nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr) +{ + major_t new_major; + smb_dev_t *sdp, *sdv; + + mutex_enter(&dev_lck); + for (; ; ) { + minor_t start = nsmb_minor; + do { + if (nsmb_minor >= MAXMIN32) { + if (nsmb_major == getmajor(*dev)) + nsmb_minor = NSMB_MIN_MINOR; + else + nsmb_minor = 0; + } else { + nsmb_minor++; + } + sdv = ddi_get_soft_state(statep, nsmb_minor); + } while ((sdv != NULL) && (nsmb_minor != start)); + if (nsmb_minor == start) { + /* + * The condition we need to solve here is all the + * MAXMIN32(~262000) minors numbers are reached. We + * need to create a new major number. + * zfs uses getudev() to create a new major number. + */ + if ((new_major = getudev()) == (major_t)-1) { + cmn_err(CE_WARN, + "nsmb: Can't get unique major " + "device number."); + mutex_exit(&dev_lck); + return (-1); + } + nsmb_major = new_major; + nsmb_minor = 0; + } else { + break; + } + } + + /* + * This is called by mount or open call. + * The open() routine is passed a pointer to a device number so + * that the driver can change the minor number. This allows + * drivers to dynamically create minor instances of the dev- + * ice. An example of this might be a pseudo-terminal driver + * that creates a new pseudo-terminal whenever it is opened. + * A driver that chooses the minor number dynamically, normally + * creates only one minor device node in attach(9E) with + * ddi_create_minor_node(9F) then changes the minor number com- + * ponent of *devp using makedevice(9F) and getmajor(9F) The + * driver needs to keep track of available minor numbers inter- + * nally. + * Stuff the structure smb_dev. + * return. + */ + + if (ddi_soft_state_zalloc(statep, nsmb_minor) == DDI_FAILURE) { + mutex_exit(&dev_lck); + return (ENXIO); + } + if ((sdp = ddi_get_soft_state(statep, nsmb_minor)) == NULL) { + mutex_exit(&dev_lck); + return (ENXIO); + } + + sdp->sd_opened = 1; + sdp->sd_seq = nsmb_minor; + sdp->smb_cred = cr; + sdp->sd_flags |= NSMBFL_OPEN; + sdp->zoneid = crgetzoneid(cr); + mutex_exit(&dev_lck); + + *dev = makedevice(nsmb_major, nsmb_minor); + + return (0); +} + +/*ARGSUSED*/ +static int +nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr) +{ + struct smb_vc *vcp; + struct smb_share *ssp; + struct smb_cred scred; + minor_t inst = getminor(dev); + smb_dev_t *sdp; + + mutex_enter(&dev_lck); + /* + * 1. Check the validity of the minor number. + * 2. Release any shares/vc associated with the connection. + * 3. Can close the minor number. + * 4. Deallocate any resources allocated in open() call. + */ + smb_credinit(&scred, curproc, cr); + + sdp = ddi_get_soft_state(statep, inst); + + /* + * time to call ddi_get_soft_state() + */ + ssp = sdp->sd_share; + if (ssp != NULL) + smb_share_rele(ssp); + vcp = sdp->sd_vc; + if (vcp != NULL) { + /* + * If this dev minor was doing session setup + * and failed to authenticate (or whatever) + * then we need to "kill" the VC here so any + * other threads waiting for the VC setup to + * finish will drop their references. + */ + if (sdp->sd_flags & NSMBFL_NEWVC) + smb_vc_kill(vcp); + smb_vc_rele(vcp); + } + smb_credrele(&scred); + + /* + * Free the instance + */ + ddi_soft_state_free(statep, inst); + mutex_exit(&dev_lck); + return (0); +} + +int +smb_dev2share(int fd, struct smb_share **sspp) +{ + register vnode_t *vp; + smb_dev_t *sdp; + struct smb_share *ssp; + dev_t dev; + file_t *fp; + + if ((fp = getf(fd)) == NULL) + return (set_errno(EBADF)); + vp = fp->f_vnode; + dev = vp->v_rdev; + if (dev == NULL) { + releasef(fd); + return (EBADF); + } + sdp = ddi_get_soft_state(statep, getminor(dev)); + if (sdp == NULL) { + releasef(fd); + return (DDI_FAILURE); + } + ssp = sdp->sd_share; + if (ssp == NULL) { + releasef(fd); + return (ENOTCONN); + } + /* + * The share is already locked and referenced by the TCON ioctl + * We NULL to hand off share to caller (mount) + * This allows further ioctls against connection, for instance + * another tree connect and mount, in the automounter case + * + * We're effectively giving our reference to the mount. + * + * XXX: I'm not sure I like this. I'd rather see the ioctl + * caller do something explicit to give up this reference, + * (i.e. SMBIOC_TDIS above) and increment the hold here. + */ + sdp->sd_share = NULL; + releasef(fd); + *sspp = ssp; + return (0); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c new file mode 100644 index 0000000000..69d3fe25c4 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c @@ -0,0 +1,1372 @@ +/* + * 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. + * + * $Id: smb_iod.c,v 1.32 2005/02/12 00:17:09 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef DEBUG +/* See sys/queue.h */ +#define QUEUEDEBUG 1 +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/atomic.h> +#include <sys/proc.h> +#include <sys/thread.h> +#include <sys/kmem.h> +#include <sys/unistd.h> +#include <sys/mount.h> +#include <sys/vnode.h> +#include <sys/types.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/stream.h> +#include <sys/strsun.h> +#include <sys/time.h> +#include <sys/class.h> +#include <sys/disp.h> +#include <sys/cmn_err.h> +#include <sys/zone.h> +#include <sys/sdt.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_tran.h> +#include <netsmb/smb_trantcp.h> + +#ifdef NEED_SMBFS_CALLBACKS +/* + * This is set/cleared when smbfs loads/unloads + * No locks should be necessary, because smbfs + * can't unload until all the mounts are gone. + */ +static smb_fscb_t *fscb; +int +smb_fscb_set(smb_fscb_t *cb) +{ + fscb = cb; + return (0); +} +#endif /* NEED_SMBFS_CALLBACKS */ + +static void smb_iod_sendall(struct smb_vc *); +static void smb_iod_recvall(struct smb_vc *); +static void smb_iod_main(struct smb_vc *); + + +#define SMBIOD_SLEEP_TIMO 2 +#define SMBIOD_PING_TIMO 60 /* seconds */ + +/* + * After this many seconds we want an unresponded-to request to trigger + * some sort of UE (dialogue). If the connection hasn't responded at all + * in this many seconds then the dialogue is of the "connection isn't + * responding would you like to force unmount" variety. If the connection + * has been responding (to other requests that is) then we need a dialogue + * of the "operation is still pending do you want to cancel it" variety. + * At present this latter dialogue does not exist so we have no UE and + * just keep waiting for the slow operation. + */ +#define SMBUETIMEOUT 8 /* seconds */ + + +/* Lock Held version of the next function. */ +static inline void +smb_iod_rqprocessed_LH( + struct smb_rq *rqp, + int error, + int flags) +{ + rqp->sr_flags |= flags; + rqp->sr_lerror = error; + rqp->sr_rpgen++; + rqp->sr_state = SMBRQ_NOTIFIED; + cv_broadcast(&rqp->sr_cond); +} + +static void +smb_iod_rqprocessed( + struct smb_rq *rqp, + int error, + int flags) +{ + + SMBRQ_LOCK(rqp); + smb_iod_rqprocessed_LH(rqp, error, flags); + SMBRQ_UNLOCK(rqp); +} + +static void +smb_iod_invrq(struct smb_vc *vcp) +{ + struct smb_rq *rqp; + + /* + * Invalidate all outstanding requests for this connection + */ + 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); +} + +#ifdef SMBTP_UPCALL +static void +smb_iod_sockwakeup(struct smb_vc *vcp) +{ + /* note: called from socket upcall... */ +} +#endif + +/* + * Called after we fail to send or recv. + * Called with no locks held. + */ +static void +smb_iod_dead(struct smb_vc *vcp) +{ + + SMB_VC_LOCK(vcp); + vcp->vc_state = SMBIOD_ST_DEAD; + cv_broadcast(&vcp->vc_statechg); + +#ifdef NEED_SMBFS_CALLBACKS + if (fscb != NULL) { + struct smb_connobj *co; + /* + * Walk the share list, notify... + * Was: smbfs_dead(...share->ss_mount); + * XXX: Ok to hold vc_lock here? + * XXX: More to do here? + */ + SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { + /* smbfs_dead() */ + fscb->fscb_dead(CPTOSS(co)); + } + } +#endif /* NEED_SMBFS_CALLBACKS */ + + SMB_VC_UNLOCK(vcp); + + smb_iod_invrq(vcp); +} + +int +smb_iod_connect(struct smb_vc *vcp) +{ + struct proc *p = curproc; + int error; + + if (vcp->vc_state != SMBIOD_ST_RECONNECT) + return (EINVAL); + + if (vcp->vc_laddr) { + error = SMB_TRAN_BIND(vcp, vcp->vc_laddr, p); + if (error) + goto errout; + } + +#ifdef SMBTP_SELECTID + SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, vcp); +#endif +#ifdef SMBTP_UPCALL + SMB_TRAN_SETPARAM(vcp, SMBTP_UPCALL, (void *)smb_iod_sockwakeup); +#endif + + error = SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, p); + if (error) { + SMBIODEBUG("connection to %s error %d\n", + vcp->vc_srvname, error); + goto errout; + } + + /* Success! */ + return (0); + +errout: + + return (error); +} + +/* + * Called by smb_vc_rele, smb_vc_kill + * Make the connection go away, and + * the IOD (reader) thread too! + */ +int +smb_iod_disconnect(struct smb_vc *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. + */ +#if 0 + if (vcp->vc_state == SMBIOD_ST_VCACTIVE) { + smb_smb_ssnclose(vcp, &vcp->vc_scred); + vcp->vc_state = SMBIOD_ST_TRANACTIVE; + } + vcp->vc_smbuid = SMB_UID_UNKNOWN; +#endif + + /* + * Used to call smb_iod_closetran here, + * which did both disconnect and close. + * We now do the close in smb_vc_free, + * so we always have a valid vc_tdata. + * Now just send the disconnect here. + * Extra disconnect calls are ignored. + */ + SMB_TRAN_DISCONNECT(vcp, curproc); + + /* + * If we have an IOD, let it handle the + * state change when it receives the ACK + * from the disconnect we just sent. + * Otherwise set the state here, i.e. + * after failing session setup. + */ + SMB_VC_LOCK(vcp); + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + vcp->vc_state = SMBIOD_ST_DEAD; + cv_broadcast(&vcp->vc_statechg); + } + SMB_VC_UNLOCK(vcp); + + return (0); +} + +/* + * Send one request. + * + * Called by _addrq (for internal requests) + * and by _sendall (via _addrq, _waitrq) + */ +static int +smb_iod_sendrq(struct smb_rq *rqp) +{ + struct proc *p = curproc; + struct smb_vc *vcp = rqp->sr_vc; + struct smb_share *ssp = rqp->sr_share; + mblk_t *m; + int error; + + ASSERT(vcp); + ASSERT(SEMA_HELD(&vcp->vc_sendlock)); + ASSERT(RW_READ_HELD(&vcp->iod_rqlock)); + + /* + * Note: requests with sr_flags & SMBR_INTERNAL + * need to pass here with these states: + * SMBIOD_ST_TRANACTIVE: smb_negotiate + * SMBIOD_ST_NEGOACTIVE: smb_ssnsetup + */ + SMBIODEBUG("vc_state = %d\n", vcp->vc_state); + switch (vcp->vc_state) { + case SMBIOD_ST_NOTCONN: + smb_iod_rqprocessed(rqp, ENOTCONN, 0); + return (0); + case SMBIOD_ST_DEAD: + /* This is what keeps the iod itself from sending more */ + smb_iod_rqprocessed(rqp, ENOTCONN, 0); + return (0); + case SMBIOD_ST_RECONNECT: + return (0); + default: + break; + } + + if (rqp->sr_sendcnt == 0) { + + *rqp->sr_rquid = htoles(vcp->vc_smbuid); + + /* + * XXX: Odd place for all this... + * Would expect these values in vc_smbuid + * and/or the request before we get here. + * I think most of this mess is due to having + * the initial UID set to SMB_UID_UKNOWN when + * it should have been initialized to zero! + * REVIST this later. XXX -gwr + * + * This is checking for the case where + * "vc_smbuid" was set to 0 in "smb_smb_ssnsetup()"; + * that happens for requests that occur + * after that's done but before we get back the final + * session setup reply, where the latter is what + * gives us the UID. (There can be an arbitrary # of + * session setup packet exchanges to complete + * "extended security" authentication.) + * + * However, if the server gave us a UID of 0 in a + * Session Setup andX reply, and we then do a + * Tree Connect andX and get back a TID, we should + * use that TID, not 0, in subsequent references to + * that tree (e.g., in NetShareEnum RAP requests). + * + * So, for now, we forcibly zero out the TID only if we're + * doing extended security, as that's the only time + * that "vc_smbuid" should be explicitly zeroed. + * + * note we must and do use SMB_TID_UNKNOWN for SMB_COM_ECHO + */ + if (!vcp->vc_smbuid && + (vcp->vc_hflags2 & SMB_FLAGS2_EXT_SEC)) + *rqp->sr_rqtid = htoles(0); + else + *rqp->sr_rqtid = + htoles(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN); + mb_fixhdr(&rqp->sr_rq); + } + if (rqp->sr_sendcnt++ >= 60/SMBSBTIMO) { /* one minute */ + smb_iod_rqprocessed(rqp, rqp->sr_lerror, SMBR_RESTART); + /* + * If all attempts to send a request failed, then + * something is seriously hosed. + */ + return (ENOTCONN); + } + + /* + * 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); + */ + m = copymsg(rqp->sr_rq.mb_top); + +#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); + + error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, p) : ENOBUFS; + m = 0; /* consumed by SEND */ + 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); + } + /* + * Check for fatal errors + */ + if (SMB_TRAN_FATAL(vcp, error)) { + /* + * No further attempts should be made + */ + SMBSDEBUG("TRAN_SEND returned fatal error %d\n", error); + return (ENOTCONN); + } + 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); +} + +static int +smb_iod_recv1(struct smb_vc *vcp, mblk_t **mpp) +{ + struct proc *p = curproc; + mblk_t *m; + uchar_t *hp; + int error; + +top: + m = NULL; + error = SMB_TRAN_RECV(vcp, &m, p); + if (error == EAGAIN) + goto top; + if (error) + return (error); + ASSERT(m); + + m = m_pullup(m, SMB_HDRLEN); + 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); +} + +/* + * 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. + */ +static void +smb_iod_recvall(struct smb_vc *vcp) +{ + struct smb_rq *rqp; + mblk_t *m; + uchar_t *hp; + ushort_t mid; + int error; + int etime_count = 0; /* for "server not responding", etc. */ + + for (;;) { + + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + error = EIO; + break; + } + + if (vcp->iod_flags & SMBIOD_SHUTDOWN) { + SMBIODEBUG("SHUTDOWN set\n"); + error = EIO; + break; + } + + m = NULL; + error = smb_iod_recv1(vcp, &m); + + if ((error == ETIME) && vcp->iod_rqwaiting) { + /* + * Nothing received for 15 seconds, + * and we have requests waiting. + */ + etime_count++; + + /* + * Once, at 15 sec. notify callbacks + * and print the warning message. + */ + if (etime_count == 1) { + smb_iod_notify_down(vcp); + 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. It's tricky to + * do a send from the IOD thread because + * we don't want to block here. + * + * Using tmo=SMBNOREPLYWAIT in the request + * so smb_rq_reply will skip smb_iod_waitrq. + * The smb_smb_echo call uses SMBR_INTERNAL + * to avoid calling smb_iod_sendall(). + */ + if ((etime_count & 3) == 2) { + smb_smb_echo(vcp, &vcp->vc_scred, + SMBNOREPLYWAIT); + } + + continue; + } /* ETIME && iod_rqwaiting */ + + if (error == ETIME) { + /* + * If the IOD thread holds the last reference + * to this VC, disconnect, release, terminate. + * Usually can avoid the lock/unlock here. + * Note, in-line: _vc_kill ... _vc_gone + */ + if (vcp->vc_co.co_usecount > 1) + continue; + SMB_VC_LOCK(vcp); + if (vcp->vc_co.co_usecount == 1 && + (vcp->vc_flags & SMBV_GONE) == 0) { + vcp->vc_flags |= SMBV_GONE; + SMB_VC_UNLOCK(vcp); + smb_iod_disconnect(vcp); + continue; /* wait for ACK */ + } + SMB_VC_UNLOCK(vcp); + continue; + } /* error == ETIME */ + + if (error) { + /* + * It's dangerous to continue here. + * (possible infinite loop!) + */ + break; + } + + /* + * Received something. Yea! + */ + if (etime_count) { + etime_count = 0; + + zprintf(vcp->vc_zoneid, "SMB server %s OK\n", + vcp->vc_srvname); + + smb_iod_notify_up(vcp); + } + + /* + * Have an SMB packet. The SMB header was + * checked in smb_iod_recv1(). + * Find the request... + */ + hp = mtod(m, uchar_t *); + /*LINTED*/ + mid = SMB_HDRMID(hp); + SMBIODEBUG("mid %04x\n", (uint_t)mid); + + rw_enter(&vcp->iod_rqlock, RW_READER); + TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { + + if (rqp->sr_mid != mid) + continue; + + DTRACE_PROBE2(smb_iod_recvrq, + (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 { + SMBRQ_UNLOCK(rqp); + SMBSDEBUG("duplicate response %d " + "(ignored)\n", mid); + break; + } + } + smb_iod_rqprocessed_LH(rqp, 0, 0); + SMBRQ_UNLOCK(rqp); + break; + } + + if (rqp == NULL) { + int cmd = SMB_HDRCMD(hp); + + if (cmd != SMB_COM_ECHO) + SMBSDEBUG("drop resp: mid %d, cmd %d\n", + (uint_t)mid, cmd); +/* smb_printrqlist(vcp); */ + m_freem(m); + } + rw_exit(&vcp->iod_rqlock); + + } +#ifdef APPLE + /* + * check for interrupts + * On Solaris, handle in smb_iod_waitrq + */ + rw_enter(&vcp->iod_rqlock, RW_READER); + TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { + if (smb_sigintr(rqp->sr_cred->scr_vfsctx)) + smb_iod_rqprocessed(rqp, EINTR, 0); + } + rw_exit(&vcp->iod_rqlock); +#endif +} + +/* + * Looks like we don't need these callbacks, + * but keep the code for now (for Apple). + */ +/*ARGSUSED*/ +void +smb_iod_notify_down(struct smb_vc *vcp) +{ +#ifdef NEED_SMBFS_CALLBACKS + struct smb_connobj *co; + + if (fscb == NULL) + return; + + /* + * Walk the share list, notify... + * Was: smbfs_down(...share->ss_mount); + * XXX: Ok to hold vc_lock here? + */ + SMB_VC_LOCK(vcp); + SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { + /* smbfs_down() */ + fscb->fscb_down(CPTOSS(co)); + } + SMB_VC_UNLOCK(vcp); +#endif /* NEED_SMBFS_CALLBACKS */ +} + +/*ARGSUSED*/ +void +smb_iod_notify_up(struct smb_vc *vcp) +{ +#ifdef NEED_SMBFS_CALLBACKS + struct smb_connobj *co; + + if (fscb == NULL) + return; + + /* + * Walk the share list, notify... + * Was: smbfs_up(...share->ss_mount); + * XXX: Ok to hold vc_lock here? + */ + SMB_VC_LOCK(vcp); + SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { + /* smbfs_up() */ + fscb->fscb_up(CPTOSS(co)); + } + SMB_VC_UNLOCK(vcp); +#endif /* NEED_SMBFS_CALLBACKS */ +} + +/* + * The IOD thread is now just a "reader", + * so no more smb_iod_request(). Yea! + */ + +/* + * Place request in the queue, and send it now if possible. + * Called with no locks held. + */ +int +smb_iod_addrq(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + int error, save_newrq; + + SMBIODEBUG("entry, mid=%d\n", rqp->sr_mid); + + ASSERT(rqp->sr_cred); + + /* This helps a little with debugging. */ + rqp->sr_owner = curthread; + + if (rqp->sr_flags & SMBR_INTERNAL) { + /* + * This is some kind of internal request, + * i.e. negotiate, session setup, echo... + * Allow vc_state < SMBIOD_ST_VCACTIVE, and + * always send directly from this thread. + * May be called by the IOD thread (echo). + * Note lock order: iod_rqlist, vc_sendlock + */ + rw_enter(&vcp->iod_rqlock, RW_WRITER); + TAILQ_INSERT_HEAD(&vcp->iod_rqlist, rqp, sr_link); + rw_downgrade(&vcp->iod_rqlock); + + /* + * Note: iod_sendrq expects vc_sendlock, + * so take that here, but carefully: + * Never block the IOD thread here. + */ + if (curthread == vcp->iod_thr) { + 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); + } + } else { + sema_p(&vcp->vc_sendlock); + error = smb_iod_sendrq(rqp); + sema_v(&vcp->vc_sendlock); + } + + rw_exit(&vcp->iod_rqlock); + if (error) + smb_iod_removerq(rqp); + + return (error); + } + + /* + * Normal request from the driver or smbfs. + * State should be correct after the check in + * smb_rq_enqueue(), but we dropped locks... + */ + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + return (ENOTCONN); + } + + 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++; + + rw_exit(&vcp->iod_rqlock); + + /* + * 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. + */ + if (save_newrq == 0) + smb_iod_sendall(vcp); + + return (0); +} + +/* + * Mark an SMBR_MULTIPACKET request as + * needing another send. Similar to the + * "normal" part of smb_iod_addrq. + */ +int +smb_iod_multirq(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + int save_newrq; + + ASSERT(rqp->sr_flags & SMBR_MULTIPACKET); + + if (rqp->sr_flags & SMBR_INTERNAL) + return (EINVAL); + + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + return (ENOTCONN); + } + + rw_enter(&vcp->iod_rqlock, RW_WRITER); + + /* 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++; + + 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); +} + + +int +smb_iod_removerq(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + + SMBIODEBUG("entry, mid=%d\n", rqp->sr_mid); + + rw_enter(&vcp->iod_rqlock, RW_WRITER); +#ifdef QUEUEDEBUG + /* + * Make sure we have not already removed it. + * See sys/queue.h QUEUEDEBUG_TAILQ_POSTREMOVE + * XXX: Don't like the constant 1 here... + */ + ASSERT(rqp->sr_link.tqe_next != (void *)1L); +#endif + TAILQ_REMOVE(&vcp->iod_rqlist, rqp, sr_link); + rw_exit(&vcp->iod_rqlock); + + return (0); +} + + +/* + * Internal version of smb_iod_waitrq. + * + * This is used when there is no reader thread, + * so we have to do the recv here. The request + * must have the SMBR_INTERNAL flag set. + */ +static int +smb_iod_waitrq_internal(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + mblk_t *m; + uchar_t *hp; + int error; + uint16_t mid; + uint8_t cmd; + + /* Make sure it's an internal request. */ + if ((rqp->sr_flags & SMBR_INTERNAL) == 0) { + SMBIODEBUG("not internal\n"); + return (EINVAL); + } + + /* Only simple requests allowed. */ + if (rqp->sr_flags & SMBR_MULTIPACKET) { + SMBIODEBUG("multipacket\n"); + return (EINVAL); + } + + /* Should not already have a response. */ + if (rqp->sr_rp.md_top) { + DEBUG_ENTER("smb_iod_waitrq again?\n"); + return (0); + } + + /* + * The message recv loop. Terminates when we + * receive the message we're looking for. + * Drop others, with complaints. + * Scaled-down version of smb_iod_recvall + */ + for (;;) { + m = NULL; + error = smb_iod_recv1(vcp, &m); + if (error) { + /* + * It's dangerous to continue here. + * (possible infinite loop!) + */ +#if 0 + if (SMB_TRAN_FATAL(vcp, error)) { + return (error); + } + continue; +#endif + return (error); + } + + hp = mtod(m, uchar_t *); + cmd = SMB_HDRCMD(hp); + /*LINTED*/ + mid = SMB_HDRMID(hp); + + SMBIODEBUG("cmd 0x%02x mid %04x\n", + (uint_t)cmd, (uint_t)mid); + m_dumpm(m); + + /* + * Normally, the MID will match. + * For internal requests, also + * match on the cmd to be safe. + */ + if (mid == rqp->sr_mid) + break; + if (cmd == rqp->sr_cmd) { + SMBIODEBUG("cmd match but not mid!\n"); + break; + } + + SMBIODEBUG("drop nomatch\n"); + m_freem(m); + } + + /* + * Have the response we were waiting for. + * Simplified version of the code from + * smb_iod_recvall + */ + SMBRQ_LOCK(rqp); + if (rqp->sr_rp.md_top == NULL) { + md_initm(&rqp->sr_rp, m); + } else { + SMBIODEBUG("drop duplicate\n"); + m_freem(m); + } + SMBRQ_UNLOCK(rqp); + + return (0); +} + + +/* + * Wait for a request to complete. + * + * For internal requests, see smb_iod_waitrq_internal. + * 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; + + SMBIODEBUG("entry, cmd=0x%02x mid=0x%04x\n", + (uint_t)rqp->sr_cmd, (uint_t)rqp->sr_mid); + + if (rqp->sr_flags & SMBR_INTERNAL) { + ASSERT((rqp->sr_flags & SMBR_MULTIPACKET) == 0); + error = smb_iod_waitrq_internal(rqp); + smb_iod_removerq(rqp); + return (error); + } + + /* + * Make sure this is NOT the IOD thread, + * or the wait below will always timeout. + */ + ASSERT(curthread != vcp->iod_thr); + + atomic_inc_uint(&vcp->iod_rqwaiting); + 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, mid=%u\n", rqp->sr_mid); + 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 + * start the timer(s) after the request is sent. + */ + if (smb_timo_notice && (smb_timo_notice < rqp->sr_timo)) + tmo1 = lbolt + SEC_TO_TICK(smb_timo_notice); + else + tmo1 = 0; + tmo2 = lbolt + SEC_TO_TICK(rqp->sr_timo); + + /* + * As above, we don't want to allow interrupt for some + * requests like open, because we could miss a succesful + * response and therefore "leak" a FID. Such requests + * are marked SMBR_NOINTR_RECV to prevent that. + * + * If "slow server" warnings are enabled, wait first + * for the "notice" timeout, and warn if expired. + */ + if (tmo1 && rqp->sr_rpgen == rqp->sr_rplast) { + if (rqp->sr_flags & SMBR_NOINTR_RECV) + tr = cv_timedwait(&rqp->sr_cond, + &rqp->sr_lock, tmo1); + else + tr = cv_timedwait_sig(&rqp->sr_cond, + &rqp->sr_lock, tmo1); + if (tr == 0) { + error = EINTR; + 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 + } + } + + /* + * Keep waiting until tmo2 is expired. + */ + while (rqp->sr_rpgen == rqp->sr_rplast) { + if (rqp->sr_flags & SMBR_NOINTR_RECV) + tr = cv_timedwait(&rqp->sr_cond, + &rqp->sr_lock, tmo2); + else + tr = cv_timedwait_sig(&rqp->sr_cond, + &rqp->sr_lock, tmo2); + if (tr == 0) { + error = EINTR; + 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; + } + /* got wakeup */ + } + error = rqp->sr_lerror; + rqp->sr_rplast++; + +out: + SMBRQ_UNLOCK(rqp); + atomic_dec_uint(&vcp->iod_rqwaiting); + + /* + * MULTIPACKET request must stay in the list. + * They may need additional responses. + */ + 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); +} + +/* + * Shutdown all outstanding I/O requests on the specified share with + * ENXIO; used when unmounting a share. (There shouldn't be any for a + * non-forced unmount; if this is a forced unmount, we have to shutdown + * the requests as part of the unmount process.) + */ +void +smb_iod_shutdown_share(struct smb_share *ssp) +{ + struct smb_vc *vcp = SSTOVC(ssp); + struct smb_rq *rqp; + + /* + * Loop through the list of requests and shutdown the ones + * that are for the specified share. + */ + rw_enter(&vcp->iod_rqlock, RW_READER); + TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { + if (rqp->sr_state != SMBRQ_NOTIFIED && rqp->sr_share == ssp) + smb_iod_rqprocessed(rqp, EIO, 0); + } + rw_exit(&vcp->iod_rqlock); +} + +/* + * Send all requests that need sending. + * Called from _addrq, _multirq, _waitrq + */ +static void +smb_iod_sendall(struct smb_vc *vcp) +{ + struct smb_rq *rqp; + int error, save_newrq, muxcnt; + + /* + * Clear "newrq" to make sure threads adding + * new requests will run this function again. + */ + rw_enter(&vcp->iod_rqlock, RW_WRITER); + save_newrq = vcp->iod_newrq; + vcp->iod_newrq = 0; + + /* + * We only read iod_rqlist, so downgrade rwlock. + * This allows the IOD to handle responses while + * some requesting thread may be blocked in send. + */ + rw_downgrade(&vcp->iod_rqlock); + + /* Expect to find about this many requests. */ + SMBIODEBUG("top, save_newrq=%d\n", save_newrq); + + /* + * Serialize to prevent multiple senders. + * Note lock order: iod_rqlock, vc_sendlock + */ + sema_p(&vcp->vc_sendlock); + + /* + * 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! + */ + ASSERT(vcp->vc_maxmux > 0); + error = muxcnt = 0; + TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { + + if (vcp->vc_state == SMBIOD_ST_DEAD) { + error = ENOTCONN; /* stop everything! */ + break; + } + + if (rqp->sr_state == SMBRQ_NOTSENT) { + error = smb_iod_sendrq(rqp); + if (error) + break; + } + + if (++muxcnt == vcp->vc_maxmux) { + SMBIODEBUG("muxcnt == vc_maxmux\n"); + break; + } + + } + + /* + * If we have vc_maxmux requests outstanding, + * arrange for _waitrq to call _sendall as + * requests are completed. + */ + vcp->iod_muxfull = + (muxcnt < vcp->vc_maxmux) ? 0 : 1; + + sema_v(&vcp->vc_sendlock); + rw_exit(&vcp->iod_rqlock); + + if (error == ENOTCONN) + smb_iod_dead(vcp); + +} + + +/* + * "main" function for smbiod daemon thread + */ +void +smb_iod_main(struct smb_vc *vcp) +{ + kthread_t *thr = curthread; + + SMBIODEBUG("entry\n"); + + SMBIODEBUG("Running, thr=0x%p\n", thr); + + /* + * Prevent race with thread that created us. + * After we get this lock iod_thr is set. + */ + SMB_VC_LOCK(vcp); + ASSERT(thr == vcp->iod_thr); + + /* Redundant with iod_thr, but may help debugging. */ + vcp->iod_flags |= SMBIOD_RUNNING; + SMB_VC_UNLOCK(vcp); + + /* + * OK, this is a new reader thread. + * In case of reconnect, tell any + * old requests they can restart. + */ + smb_iod_invrq(vcp); + + /* + * Run the "reader" loop. + */ + smb_iod_recvall(vcp); + + /* + * The reader loop function returns only when + * there's been a fatal error on the connection. + */ + smb_iod_dead(vcp); + + /* + * The reader thread is going away. Clear iod_thr, + * and wake up anybody waiting for us to quit. + */ + SMB_VC_LOCK(vcp); + vcp->iod_flags &= ~SMBIOD_RUNNING; + vcp->iod_thr = NULL; + cv_broadcast(&vcp->iod_exit); + SMB_VC_UNLOCK(vcp); + + /* + * This hold was taken in smb_iod_create() + * when this thread was created. + */ + smb_vc_rele(vcp); + + SMBIODEBUG("Exiting, p=0x%p\n", curproc); + zthread_exit(); +} + +/* + * Create the reader thread. + * + * This happens when we are just about to + * enter vc_state = SMBIOD_ST_VCACTIVE; + * See smb_sm_ssnsetup() + */ +int +smb_iod_create(struct smb_vc *vcp) +{ + kthread_t *thr = NULL; + int error; + + /* + * Take a hold on the VC for the IOD thread. + * This hold will be released when the IOD + * thread terminates. (or on error below) + */ + smb_vc_hold(vcp); + + SMB_VC_LOCK(vcp); + + if (vcp->iod_thr != NULL) { + SMBIODEBUG("aready have an IOD?"); + error = EIO; + goto out; + } + + /* + * Darwin code used: IOCreateThread(...) + * In Solaris, we use... + */ + thr = zthread_create( + NULL, /* stack */ + 0, /* stack size (default) */ + smb_iod_main, /* entry func... */ + vcp, /* ... and arg */ + 0, /* len (of what?) */ + minclsyspri); /* priority */ + if (thr == NULL) { + SMBERROR("can't start smbiod\n"); + error = ENOMEM; + goto out; + } + + /* Success! */ + error = 0; + vcp->iod_thr = thr; + +out: + SMB_VC_UNLOCK(vcp); + + if (error) + smb_vc_rele(vcp); + + return (error); +} + +/* + * Called from smb_vc_free to do any + * cleanup of our IOD (reader) thread. + */ +int +smb_iod_destroy(struct smb_vc *vcp) +{ + clock_t tmo; + + /* + * Let's try to make sure the IOD thread + * goes away, by waiting for it to exit. + * Normally, it's gone by now. + * + * Only wait for a second, because we're in the + * teardown path and don't want to get stuck here. + * Should not take long, or things are hosed... + */ + SMB_VC_LOCK(vcp); + if (vcp->iod_thr) { + vcp->iod_flags |= SMBIOD_SHUTDOWN; + tmo = lbolt + hz; + tmo = cv_timedwait(&vcp->iod_exit, &vcp->vc_lock, tmo); + if (tmo == -1) { + SMBERROR("IOD thread for %s did not exit?\n", + vcp->vc_srvname); + } + } + if (vcp->iod_thr) { + /* This should not happen. */ + SMBIODEBUG("IOD thread did not exit!\n"); + /* Try harder? */ + tsignal(vcp->iod_thr, SIGKILL); + } + SMB_VC_UNLOCK(vcp); + + return (0); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h new file mode 100644 index 0000000000..712acb6f3b --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_osdep.h @@ -0,0 +1,102 @@ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 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 +#endif + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#define CAST_DOWN(type, addr) (((type)((uintptr_t)(addr)))) +#define USER_ADDR_NULL ((user_addr_t)0) +#define CAST_USER_ADDR_T(a_ptr) ((user_addr_t)(a_ptr)) + +/* + * flags to (BSD) malloc + */ +#define M_WAITOK 0x0000 +#define M_NOWAIT 0x0001 +#define M_ZERO 0x0004 /* bzero the allocation */ + +/* Iconv stuff */ + +/* + * Some UTF Related stuff. Will be deleting this once compiled and using + * ienup's code. + */ +/* + * UTF-8 encode/decode flags + */ +#define UTF_REVERSE_ENDIAN 0x01 /* reverse UCS-2 byte order */ +#define UTF_NO_NULL_TERM 0x02 /* do not add null termination */ +#define UTF_DECOMPOSED 0x04 /* generate fully decomposed UCS-2 */ +#define UTF_PRECOMPOSED 0x08 /* generate precomposed UCS-2 */ + +/* + * These are actually included in sunddi.h. I am getting compilation + * errors right now. Adding the induvidual defines here again from sunddi.h + * Unicode encoding conversion functions and their macros. + */ +#define UCONV_IN_BIG_ENDIAN 0x0001 +#define UCONV_OUT_BIG_ENDIAN 0x0002 +#define UCONV_IN_SYSTEM_ENDIAN 0x0004 +#define UCONV_OUT_SYSTEM_ENDIAN 0x0008 +#define UCONV_IN_LITTLE_ENDIAN 0x0010 +#define UCONV_OUT_LITTLE_ENDIAN 0x0020 +#define UCONV_IGNORE_NULL 0x0040 +#define UCONV_IN_ACCEPT_BOM 0x0080 +#define UCONV_OUT_EMIT_BOM 0x0100 + +extern int uconv_u8tou16(const uchar_t *, size_t *, uint16_t *, size_t *, int); + +/* Legacy type names for Solaris. */ +typedef uint64_t u_int64_t; +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; + +/* + * Time related calls. + */ + +/* BEGIN CSTYLED */ +#define timespeccmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) +/* END CSTYLED */ + +#define timespecadd(vvp, uvp) \ + { \ + (vvp)->tv_sec += (uvp)->tv_sec; \ + (vvp)->tv_nsec += (uvp)->tv_nsec; \ + if ((vvp)->tv_nsec >= 1000000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_nsec -= 1000000000; \ + } \ + } + +#define timespecsub(vvp, uvp) \ + { \ + (vvp)->tv_sec -= (uvp)->tv_sec; \ + (vvp)->tv_nsec -= (uvp)->tv_nsec; \ + if ((vvp)->tv_nsec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_nsec += 1000000000; \ + } \ + } + +#endif /* _NETSMB_SMB_OSDEP_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c new file mode 100644 index 0000000000..c78c0e61b1 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c @@ -0,0 +1,391 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Password Keychain storage mechanism. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/sysmacros.h> +#include <sys/uio.h> +#include <sys/buf.h> +#include <sys/modctl.h> +#include <sys/open.h> +#include <sys/file.h> +#include <sys/kmem.h> +#include <sys/conf.h> +#include <sys/cmn_err.h> +#include <sys/stat.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/sunldi.h> +#include <sys/policy.h> +#include <sys/zone.h> +#include <sys/pathname.h> +#include <sys/mount.h> +#include <sys/sdt.h> +#include <fs/fs_subr.h> +#include <sys/devops.h> +#include <sys/thread.h> +#include <sys/mkdev.h> +#include <sys/avl.h> +#include <sys/avl_impl.h> + +#include <netsmb/smb_osdep.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> +#include <netsmb/smb_pass.h> + +/* + * The smb_ptd is a cache of Uid's, User names, passwords and domain names. + * It will be used for storing the password information for a user and will + * be used to for connections without entering the pasword again if its + * already keyed in by the user. Its a kind of Key-Chain mechanism + * implemented by Apple folks. + */ + +/* + * Information stored in the nodes: + * UID: Uid of the person who initiated the login request. + * ZoneID: ZoneID of the zone from where the login request is initiated. + * Username: Username in the CIFS server. + * Srvdom: Domain name/ Server name of the CIFS server. + * Password: Password of the user. + * For more information, see smb_pass.h and sys/avl.h + */ + +/* + * Information retrieved from the node. + * Node/password information can only be retrived with a call + * to smb_pkey_getpw(). Password never gets copied to the userspace. + * It will be copied to the Kernel data structure smbioc_ossn->ioc_password + * when needed for doing the "Session Setup". All other calls will return + * either a success or a failure. + */ + +avl_tree_t smb_ptd; /* AVL password tree descriptor */ +unsigned int smb_list_len = 0; /* No. of elements in the tree. */ +kmutex_t smb_ptd_lock; /* Mutex lock for controlled access */ + +/* + * This routine is called by AVL tree calls when they want to find a + * node, find the next position in the tree to add or for deletion. + * Compare nodes from the tree to find the actual node based on + * uid/zoneid/username/domainname. + */ +int +smb_pkey_cmp(const void *a, const void *b) +{ + const smb_passid_t *pa = (smb_passid_t *)a; + const smb_passid_t *pb = (smb_passid_t *)b; + int duser, dsrv; + + ASSERT(MUTEX_HELD(&smb_ptd_lock)); + + /* + * The nodes are added sorted on the uid/zoneid/domainname/username + * We will do this: + * Compare uid's. The owner who stored the node gets access. + * Then zoneid to check if the access is from the same zone. + * Compare usernames. + * If the above are same, then compare domain/server names. + */ + if (pa->uid < pb->uid) + return (-1); + if (pa->uid > pb->uid) + return (+1); + if (pa->zoneid < pb->zoneid) + return (-1); + if (pa->zoneid > pb->zoneid) + return (+1); + dsrv = strcasecmp(pa->srvdom, pb->srvdom); + if (dsrv < 0) + return (-1); + if (dsrv > 0) + return (+1); + duser = strcasecmp(pa->username, pb->username); + if (duser < 0) + return (-1); + if (duser > 0) + return (+1); + return (0); +} + +/* + * Initialization of the code that deals with uid and passwords. + */ +void +smb_pkey_init() +{ + avl_create(&smb_ptd, + smb_pkey_cmp, + sizeof (smb_passid_t), + offsetof(smb_passid_t, + cpnode)); + mutex_init(&smb_ptd_lock, NULL, MUTEX_DEFAULT, NULL); +} + +/* + * Destroy the full AVL tree. + */ +void +smb_pkey_fini() +{ + smb_pkey_deluid((uid_t)-1, CRED()); + avl_destroy(&smb_ptd); + mutex_destroy(&smb_ptd_lock); +} + +/* + * Driver unload calls this to ask if we + * have any stored passwords + */ +int +smb_pkey_idle() +{ + int n; + + mutex_enter(&smb_ptd_lock); + n = avl_numnodes(&smb_ptd); + mutex_exit(&smb_ptd_lock); + + return ((n) ? EBUSY : 0); +} + +int +smb_node_delete(smb_passid_t *tmp) +{ + ASSERT(MUTEX_HELD(&smb_ptd_lock)); + avl_remove(&smb_ptd, tmp); + smb_strfree(tmp->srvdom); + smb_strfree(tmp->username); + kmem_free(tmp, sizeof (*tmp)); + return (0); +} + + +/* + * Remove a node from the AVL tree identified by cpid. + */ +int +smb_pkey_del(smbioc_pk_t *pk, cred_t *cr) +{ + avl_index_t where; + smb_passid_t buf, *cpid, *tmp; + uid_t uid; + + tmp = &buf; + uid = pk->pk_uid; + if (uid == (uid_t)-1) + uid = crgetruid(cr); + else { + if (secpolicy_smbfs_login(cr, uid)) + return (EPERM); + } + tmp->uid = uid; + tmp->zoneid = getzoneid(); + tmp->srvdom = pk->pk_dom; + tmp->username = pk->pk_usr; + + mutex_enter(&smb_ptd_lock); + if ((cpid = (smb_passid_t *)avl_find(&smb_ptd, + tmp, &where)) != NULL) { + smb_node_delete(cpid); + } + mutex_exit(&smb_ptd_lock); + + return (0); +} + +/* + * Delete the entries owned by a particular user + * based on uid. We go through all the nodes and + * delete the nodes whereever the uid matches. + * + * Also implements "delete all" when uid == -1. + * + * You must have privilege to use any uid other + * than your real uid. + */ +int +smb_pkey_deluid(uid_t ioc_uid, cred_t *cr) +{ + smb_passid_t *cpid, *tmp; + + if (secpolicy_smbfs_login(cr, ioc_uid)) + return (EPERM); + + mutex_enter(&smb_ptd_lock); + for (tmp = avl_first(&smb_ptd); tmp != NULL; + tmp = cpid) { + cpid = AVL_NEXT(&smb_ptd, tmp); + if (ioc_uid == (uid_t)-1 || + ioc_uid == tmp->uid) { + /* + * Delete the node. + */ + smb_node_delete(tmp); + } + } + mutex_exit(&smb_ptd_lock); + + return (0); +} + +/* + * Add entry or modify existing. + * Check for existing entry.. + * If present, delete. + * Now, add the new entry. + */ +int +smb_pkey_add(smbioc_pk_t *pk, cred_t *cr) +{ + avl_tree_t *t = &smb_ptd; + avl_index_t where; + smb_passid_t *tmp, *cpid; + int ret; + uid_t uid; + + uid = pk->pk_uid; + if (uid == (uid_t)-1) + uid = crgetruid(cr); + else { + if (secpolicy_smbfs_login(cr, uid)) + return (EPERM); + } + cpid = kmem_zalloc(sizeof (smb_passid_t), KM_SLEEP); + cpid->uid = uid; + cpid->zoneid = getzoneid(); + cpid->srvdom = smb_strdup(pk->pk_dom); + cpid->username = smb_strdup(pk->pk_usr); + smb_oldlm_hash(pk->pk_pass, cpid->lmhash); + smb_ntlmv1hash(pk->pk_pass, cpid->nthash); + + /* + * XXX: Instead of calling smb_pkey_check here, + * should call avl_find directly, and hold the + * lock across: avl_find, avl_remove, avl_insert. + */ + + /* If it already exists, delete it. */ + ret = smb_pkey_check(pk, cr); + if (ret == 0) { + smb_pkey_del(pk, cr); + } + + mutex_enter(&smb_ptd_lock); + tmp = (smb_passid_t *)avl_find(t, cpid, &where); + if (tmp == NULL) { + avl_insert(t, cpid, where); + } else { + smb_strfree(cpid->srvdom); + smb_strfree(cpid->username); + kmem_free(cpid, sizeof (smb_passid_t)); + } + mutex_exit(&smb_ptd_lock); + + return (0); +} + +/* + * Determine if a node with uid,zoneid, uname & dname exists in the tree + * given the information. Does NOT return the stored password. + */ +int +smb_pkey_check(smbioc_pk_t *pk, cred_t *cr) +{ + avl_tree_t *t = &smb_ptd; + avl_index_t where; + smb_passid_t *tmp, *cpid; + int error = ENOENT; + uid_t uid; + + uid = pk->pk_uid; + if (uid == (uid_t)-1) + uid = crgetruid(cr); + else { + if (secpolicy_smbfs_login(cr, uid)) + return (EPERM); + } + cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP); + cpid->uid = uid; + cpid->zoneid = getzoneid(); + cpid->srvdom = pk->pk_dom; + cpid->username = pk->pk_usr; + + mutex_enter(&smb_ptd_lock); + tmp = (smb_passid_t *)avl_find(t, cpid, &where); + if (tmp != NULL) + error = 0; + mutex_exit(&smb_ptd_lock); + + kmem_free(cpid, sizeof (smb_passid_t)); + return (error); +} + +/* + * Interface function between the keychain mechanism and SMB password + * handling during Session Setup. Internal form of smb_pkey_check(). + * Copies the password hashes into the VC. + */ +int +smb_pkey_getpwh(struct smb_vc *vcp, cred_t *cr) +{ + avl_tree_t *t = &smb_ptd; + avl_index_t where; + smb_passid_t *tmp, *cpid; + int error = ENOENT; + + cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP); + cpid->uid = crgetruid(cr); + cpid->zoneid = getzoneid(); + cpid->username = vcp->vc_username; + + if (vcp->vc_vopt & SMBVOPT_KC_DOMAIN) + cpid->srvdom = vcp->vc_domain; + else + cpid->srvdom = vcp->vc_srvname; + + mutex_enter(&smb_ptd_lock); + tmp = (smb_passid_t *)avl_find(t, cpid, &where); + if (tmp != NULL) { + bcopy(tmp->lmhash, vcp->vc_lmhash, SMB_PWH_MAX); + bcopy(tmp->nthash, vcp->vc_nthash, SMB_PWH_MAX); + error = 0; + } + mutex_exit(&smb_ptd_lock); + + kmem_free(cpid, sizeof (smb_passid_t)); + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h new file mode 100644 index 0000000000..d4147798ba --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h @@ -0,0 +1,66 @@ +/* + * 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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMB_PASS_H +#define _SMB_PASS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Password keychains interface + */ + +#include <sys/avl.h> +#include <netsmb/smb_dev.h> + +/* + * Here just so our mdb module can use it. + * Otherwise could be private to smb_pass.c + */ +typedef struct smb_passid { + avl_node_t cpnode; /* Next Node information */ + uid_t uid; /* User id */ + zoneid_t zoneid; /* Future Use */ + char *srvdom; /* Windows Domain (or server) */ + char *username; /* Windows User name */ + uchar_t lmhash[SMB_PWH_MAX]; + uchar_t nthash[SMB_PWH_MAX]; +} 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_add(smbioc_pk_t *pk, cred_t *cr); +int smb_pkey_del(smbioc_pk_t *pk, cred_t *cr); +int smb_pkey_check(smbioc_pk_t *pk, cred_t *cr); +int smb_pkey_deluid(uid_t uid, cred_t *cr); + +/* Called from smb_usr.c */ +struct smb_vc; +int smb_pkey_getpwh(struct smb_vc *vcp, cred_t *cr); + +#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 new file mode 100644 index 0000000000..1115e6fc69 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c @@ -0,0 +1,1408 @@ +/* + * 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. + * + * $Id: smb_rq.c,v 1.29 2005/02/11 01:44:17 lindak Exp $ + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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/mount.h> +#include <sys/cmn_err.h> +#include <sys/sdt.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_tran.h> +#include <netsmb/smb_rq.h> + +static int smb_rq_reply(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); + + + +int +smb_rq_alloc(struct smb_connobj *layer, uchar_t cmd, struct smb_cred *scred, + struct smb_rq **rqpp) +{ + struct smb_rq *rqp; + int error; + + rqp = (struct smb_rq *)kmem_alloc(sizeof (struct smb_rq), KM_SLEEP); + if (rqp == NULL) + return (ENOMEM); + error = smb_rq_init(rqp, layer, cmd, scred); + if (error) { + smb_rq_done(rqp); + return (error); + } + rqp->sr_flags |= SMBR_ALLOCED; + *rqpp = rqp; + return (0); +} + + +int +smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, uchar_t cmd, + struct smb_cred *scred) +{ + int error; + + bzero(rqp, sizeof (*rqp)); + mutex_init(&rqp->sr_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&rqp->sr_cond, NULL, CV_DEFAULT, NULL); + + error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share); + if (error) + return (error); + + rqp->sr_rexmit = SMBMAXRESTARTS; + rqp->sr_cred = scred; /* XXX no ref hold */ + rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc); + error = smb_rq_new(rqp, cmd); + if (!error) { + rqp->sr_flags |= SMBR_VCREF; + smb_vc_hold(rqp->sr_vc); + } + return (error); +} + +static int +smb_rq_new(struct smb_rq *rqp, uchar_t cmd) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct mbchain *mbp = &rqp->sr_rq; + int error; + static char tzero[12]; + caddr_t ptr; + pid_t pid; + + ASSERT(rqp != NULL); + ASSERT(rqp->sr_cred != NULL); + pid = rqp->sr_cred->vc_pid; + rqp->sr_sendcnt = 0; + rqp->sr_cmd = cmd; + mb_done(mbp); + md_done(&rqp->sr_rp); + error = mb_init(mbp); + if (error) + return (error); + mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM); + mb_put_uint8(mbp, cmd); + mb_put_uint32le(mbp, 0); + mb_put_uint8(mbp, vcp->vc_hflags); + if (cmd == SMB_COM_TRANSACTION || cmd == SMB_COM_TRANSACTION_SECONDARY) + mb_put_uint16le(mbp, (vcp->vc_hflags2 & ~SMB_FLAGS2_UNICODE)); + else + mb_put_uint16le(mbp, vcp->vc_hflags2); + mb_put_mem(mbp, tzero, 12, MB_MSYSTEM); + ptr = mb_reserve(mbp, sizeof (u_int16_t)); + /*LINTED*/ + ASSERT(ptr == (caddr_t)((u_int16_t *)ptr)); + /*LINTED*/ + rqp->sr_rqtid = (u_int16_t *)ptr; + mb_put_uint16le(mbp, (u_int16_t)(pid)); + ptr = mb_reserve(mbp, sizeof (u_int16_t)); + /*LINTED*/ + ASSERT(ptr == (caddr_t)((u_int16_t *)ptr)); + /*LINTED*/ + rqp->sr_rquid = (u_int16_t *)ptr; + mb_put_uint16le(mbp, rqp->sr_mid); + return (0); +} + +void +smb_rq_done(struct smb_rq *rqp) +{ + /* No locks. Last ref. here. */ + if (rqp->sr_flags & SMBR_VCREF) { + rqp->sr_flags &= ~SMBR_VCREF; + smb_vc_rele(rqp->sr_vc); + } + mb_done(&rqp->sr_rq); + md_done(&rqp->sr_rp); + mutex_destroy(&rqp->sr_lock); + cv_destroy(&rqp->sr_cond); + if (rqp->sr_flags & SMBR_ALLOCED) + kmem_free(rqp, sizeof (*rqp)); +} + +/* + * Simple request-reply exchange + */ +int +smb_rq_simple_timed(struct smb_rq *rqp, int timeout) +{ + int error = EINVAL; + + for (; ; ) { + /* + * Don't send any new requests if force unmount is underway. + * This check was moved into smb_rq_enqueue. + */ + rqp->sr_flags &= ~SMBR_RESTART; + rqp->sr_timo = timeout; /* in seconds */ + rqp->sr_state = SMBRQ_NOTSENT; + error = smb_rq_enqueue(rqp); + if (error) { + break; + } + error = smb_rq_reply(rqp); + if (!error) + break; + if ((rqp->sr_flags & (SMBR_RESTART | SMBR_NORESTART)) != + SMBR_RESTART) + break; + if (rqp->sr_rexmit <= 0) + break; + SMBRQ_LOCK(rqp); + if (rqp->sr_share && rqp->sr_share->ss_mount) { + cv_timedwait(&rqp->sr_cond, &(rqp)->sr_lock, + lbolt + (hz * SMB_RCNDELAY)); + + } else { + delay(lbolt + (hz * SMB_RCNDELAY)); + } + SMBRQ_UNLOCK(rqp); + rqp->sr_rexmit--; +#ifdef XXX + timeout *= 2; +#endif + } + return (error); +} + + +int +smb_rq_simple(struct smb_rq *rqp) +{ + return (smb_rq_simple_timed(rqp, smb_timo_default)); +} + +static int +smb_rq_enqueue(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + struct smb_share *ssp = rqp->sr_share; + int error = 0; + + /* + * Unfortunate special case needed for + * tree disconnect, which needs sr_share + * but should skip the reconnect check. + */ + if (rqp->sr_cmd == SMB_COM_TREE_DISCONNECT) + ssp = NULL; + + /* + * If this is an "internal" request, bypass any + * wait for connection state changes, etc. + * This request is making those changes. + */ + if (rqp->sr_flags & SMBR_INTERNAL) { + ASSERT(ssp == NULL); + goto just_doit; + } + + /* + * Wait for VC reconnect to finish... + * XXX: Deal with reconnect later. + * Just bail out for now. + * + * MacOS might check vfs_isforce() here. + */ + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBSDEBUG("bad vc_state=%d\n", vcp->vc_state); + return (ENOTCONN); + } + + /* + * If this request has a "share" object: + * 1: Deny access if share is _GONE (unmounted) + * 2: Wait for state changes in that object, + * Initiate share (re)connect if needed. + * XXX: Not really doing 2 yet. + */ + if (ssp) { + if (ssp->ss_flags & SMBS_GONE) + return (ENOTCONN); + SMB_SS_LOCK(ssp); + if (!smb_share_valid(ssp)) { + error = smb_share_tcon(ssp); + } + SMB_SS_UNLOCK(ssp); + } + + if (!error) { + just_doit: + error = smb_iod_addrq(rqp); + } + + return (error); +} + +/* + * Mark location of the word count, which is filled in later by + * smb_rw_wend(). Also initialize the counter that it uses + * to figure out what value to fill in. + * + * Note that the word count happens to be 8-bit. + */ +void +smb_rq_wstart(struct smb_rq *rqp) +{ + rqp->sr_wcount = mb_reserve(&rqp->sr_rq, sizeof (uint8_t)); + rqp->sr_rq.mb_count = 0; +} + +void +smb_rq_wend(struct smb_rq *rqp) +{ + uint_t wcnt; + + if (rqp->sr_wcount == NULL) { + SMBSDEBUG("no wcount\n"); + return; + } + wcnt = rqp->sr_rq.mb_count; + if (wcnt > 0x1ff) + SMBSDEBUG("word count too large (%d)\n", wcnt); + if (wcnt & 1) + SMBSDEBUG("odd word count\n"); + /* Fill in the word count (8-bits) */ + *rqp->sr_wcount = (wcnt >> 1); +} + +/* + * Mark location of the byte count, which is filled in later by + * smb_rw_bend(). Also initialize the counter that it uses + * to figure out what value to fill in. + * + * Note that the byte count happens to be 16-bit. + */ +void +smb_rq_bstart(struct smb_rq *rqp) +{ + rqp->sr_bcount = mb_reserve(&rqp->sr_rq, sizeof (uint16_t)); + rqp->sr_rq.mb_count = 0; +} + +void +smb_rq_bend(struct smb_rq *rqp) +{ + uint_t bcnt; + + if (rqp->sr_bcount == NULL) { + SMBSDEBUG("no bcount\n"); + return; + } + bcnt = rqp->sr_rq.mb_count; + if (bcnt > 0xffff) + SMBSDEBUG("byte count too large (%d)\n", bcnt); + /* + * Fill in the byte count (16-bits) + * The pointer is char * type due to + * typical off-by-one alignment. + */ + rqp->sr_bcount[0] = bcnt & 0xFF; + rqp->sr_bcount[1] = (bcnt >> 8); +} + +int +smb_rq_intr(struct smb_rq *rqp) +{ + if (rqp->sr_flags & SMBR_INTR) + return (EINTR); + + return (0); +#ifdef APPLE + return (smb_sigintr(rqp->sr_cred->scr_vfsctx)); +#endif +} + +int +smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp) +{ + *mbpp = &rqp->sr_rq; + return (0); +} + +int +smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp) +{ + *mbpp = &rqp->sr_rp; + return (0); +} + +static int +smb_rq_getenv(struct smb_connobj *co, + struct smb_vc **vcpp, struct smb_share **sspp) +{ + struct smb_vc *vcp = NULL; + struct smb_share *ssp = NULL; + int error = 0; + + if (co->co_flags & SMBO_GONE) { + SMBSDEBUG("zombie CO\n"); + error = EINVAL; + goto out; + } + + switch (co->co_level) { + case SMBL_VC: + vcp = CPTOVC(co); + if (co->co_parent == NULL) { + SMBSDEBUG("zombie VC %s\n", vcp->vc_srvname); + error = EINVAL; + break; + } + break; + + case SMBL_SHARE: + ssp = CPTOSS(co); + if (co->co_parent == NULL) { + SMBSDEBUG("zombie share %s\n", ssp->ss_name); + error = EINVAL; + break; + } + error = smb_rq_getenv(co->co_parent, &vcp, NULL); + break; + default: + SMBSDEBUG("invalid level %d passed\n", co->co_level); + error = EINVAL; + } + +out: + if (!error) { + if (vcpp) + *vcpp = vcp; + if (sspp) + *sspp = ssp; + } + + return (error); +} + +/* + * Wait for reply on the request + */ +static int +smb_rq_reply(struct smb_rq *rqp) +{ + struct mdchain *mdp = &rqp->sr_rp; + u_int32_t tdw; + u_int8_t tb; + int error, rperror = 0; + + if (rqp->sr_timo == SMBNOREPLYWAIT) + return (smb_iod_removerq(rqp)); + + error = smb_iod_waitrq(rqp); + if (error) + return (error); + error = md_get_uint32(mdp, &tdw); + if (error) + 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) { + /* + * 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. + * 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); + } 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; + + error = md_get_uint32(mdp, &tdw); + error = md_get_uint32(mdp, &tdw); + error = md_get_uint32(mdp, &tdw); + + error = md_get_uint16le(mdp, &rqp->sr_rptid); + error = md_get_uint16le(mdp, &rqp->sr_rppid); + error = md_get_uint16le(mdp, &rqp->sr_rpuid); + error = md_get_uint16le(mdp, &rqp->sr_rpmid); + + SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n", + rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid, + rqp->sr_errclass, rqp->sr_serror); + + return ((error) ? error : rperror); +} + + +#define ALIGN4(a) (((a) + 3) & ~3) + +/* + * TRANS2 request implementation + * TRANS implementation is in the "t2" routines + * NT_TRANSACTION implementation is the separate "nt" stuff + */ +int +smb_t2_alloc(struct smb_connobj *layer, ushort_t setup, struct smb_cred *scred, + struct smb_t2rq **t2pp) +{ + struct smb_t2rq *t2p; + int error; + + t2p = (struct smb_t2rq *)kmem_alloc(sizeof (*t2p), KM_SLEEP); + if (t2p == NULL) + return (ENOMEM); + error = smb_t2_init(t2p, layer, &setup, 1, scred); + mutex_init(&t2p->t2_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&t2p->t2_cond, NULL, CV_DEFAULT, NULL); + t2p->t2_flags |= SMBT2_ALLOCED; + if (error) { + smb_t2_done(t2p); + return (error); + } + *t2pp = t2p; + return (0); +} + +int +smb_nt_alloc(struct smb_connobj *layer, ushort_t fn, struct smb_cred *scred, + struct smb_ntrq **ntpp) +{ + struct smb_ntrq *ntp; + int error; + + ntp = (struct smb_ntrq *)kmem_alloc(sizeof (*ntp), KM_SLEEP); + if (ntp == NULL) + return (ENOMEM); + error = smb_nt_init(ntp, layer, fn, scred); + mutex_init(&ntp->nt_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&ntp->nt_cond, NULL, CV_DEFAULT, NULL); + ntp->nt_flags |= SMBT2_ALLOCED; + if (error) { + smb_nt_done(ntp); + return (error); + } + *ntpp = ntp; + return (0); +} + +int +smb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, ushort_t *setup, + int setupcnt, struct smb_cred *scred) +{ + int i; + int error; + + bzero(t2p, sizeof (*t2p)); + t2p->t2_source = source; + t2p->t2_setupcount = (u_int16_t)setupcnt; + t2p->t2_setupdata = t2p->t2_setup; + for (i = 0; i < setupcnt; i++) + t2p->t2_setup[i] = setup[i]; + t2p->t2_fid = 0xffff; + t2p->t2_cred = scred; + t2p->t2_share = (source->co_level == SMBL_SHARE ? + CPTOSS(source) : NULL); /* for smb up/down */ + error = smb_rq_getenv(source, &t2p->t2_vc, NULL); + if (error) + return (error); + return (0); +} + +int +smb_nt_init(struct smb_ntrq *ntp, struct smb_connobj *source, ushort_t fn, + struct smb_cred *scred) +{ + int error; + + bzero(ntp, sizeof (*ntp)); + ntp->nt_source = source; + ntp->nt_function = fn; + ntp->nt_cred = scred; + ntp->nt_share = (source->co_level == SMBL_SHARE ? + CPTOSS(source) : NULL); /* for smb up/down */ + error = smb_rq_getenv(source, &ntp->nt_vc, NULL); + if (error) + return (error); + return (0); +} + +void +smb_t2_done(struct smb_t2rq *t2p) +{ + mb_done(&t2p->t2_tparam); + mb_done(&t2p->t2_tdata); + md_done(&t2p->t2_rparam); + md_done(&t2p->t2_rdata); + mutex_destroy(&t2p->t2_lock); + cv_destroy(&t2p->t2_cond); +#ifdef NOTYETRESOLVED + if (t2p->t2_flags & SMBT2_ALLOCED) + kmem_free(t2p, sizeof (*t2p)); +#endif + if (t2p) { + kmem_free(t2p, sizeof (*t2p)); + } +} + +u_int32_t +smb_t2_err(struct smb_t2rq *t2p) +{ + /* mask off "severity" and the "component" bit */ + return (t2p->t2_sr_error & ~(0xe0000000)); +} + +void +smb_nt_done(struct smb_ntrq *ntp) +{ + mb_done(&ntp->nt_tsetup); + mb_done(&ntp->nt_tparam); + mb_done(&ntp->nt_tdata); + md_done(&ntp->nt_rparam); + md_done(&ntp->nt_rdata); + cv_destroy(&ntp->nt_cond); + mutex_destroy(&ntp->nt_lock); + if (ntp->nt_flags & SMBT2_ALLOCED) + kmem_free(ntp, sizeof (*ntp)); +} + +/* + * Extract data [offset,count] from mtop and add to mdp. + */ +static int +smb_t2_placedata(mblk_t *mtop, u_int16_t offset, u_int16_t count, + struct mdchain *mdp) +{ + mblk_t *n; + + n = m_copym(mtop, offset, count, M_WAITOK); + if (n == NULL) + return (EBADRPC); + + if (mdp->md_top == NULL) { + md_initm(mdp, n); + } else + m_cat(mdp->md_top, n); + + return (0); +} + +static int +smb_t2_reply(struct smb_t2rq *t2p) +{ + struct mdchain *mdp; + struct smb_rq *rqp = t2p->t2_rq; + int error, error2, totpgot, totdgot; + u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp; + u_int16_t tmp, bc, dcount; + u_int8_t wc; + + t2p->t2_flags &= ~SMBT2_MOREDATA; + + error = smb_rq_reply(rqp); + if (rqp->sr_flags & SMBR_MOREDATA) + t2p->t2_flags |= SMBT2_MOREDATA; + t2p->t2_sr_errclass = rqp->sr_errclass; + t2p->t2_sr_serror = rqp->sr_serror; + t2p->t2_sr_error = rqp->sr_error; + t2p->t2_sr_rpflags2 = rqp->sr_rpflags2; + if (error && !(rqp->sr_flags & SMBR_MOREDATA)) + return (error); + /* + * Now we have to get all subseqent responses, if any. + * The CIFS specification says that they can be misordered, + * which is weird. + * TODO: timo + */ + totpgot = totdgot = 0; + totpcount = totdcount = 0xffff; + mdp = &rqp->sr_rp; + for (;;) { + DTRACE_PROBE2(smb_trans_reply, + (smb_rq_t *), rqp, (mblk_t *), mdp->md_top); + m_dumpm(mdp->md_top); + + if ((error2 = md_get_uint8(mdp, &wc)) != 0) + break; + if (wc < 10) { + error2 = ENOENT; + break; + } + if ((error2 = md_get_uint16le(mdp, &tmp)) != 0) + break; + if (totpcount > tmp) + totpcount = tmp; + if ((error2 = md_get_uint16le(mdp, &tmp)) != 0) + break; + if (totdcount > tmp) + totdcount = tmp; + if ((error2 = md_get_uint16le(mdp, &tmp)) != 0 || /* reserved */ + (error2 = md_get_uint16le(mdp, &pcount)) != 0 || + (error2 = md_get_uint16le(mdp, &poff)) != 0 || + (error2 = md_get_uint16le(mdp, &pdisp)) != 0) + break; + if (pcount != 0 && pdisp != totpgot) { + SMBSDEBUG("Can't handle misordered parameters %d:%d\n", + pdisp, totpgot); + error2 = EINVAL; + break; + } + if ((error2 = md_get_uint16le(mdp, &dcount)) != 0 || + (error2 = md_get_uint16le(mdp, &doff)) != 0 || + (error2 = md_get_uint16le(mdp, &ddisp)) != 0) + break; + if (dcount != 0 && ddisp != totdgot) { + SMBSDEBUG("Can't handle misordered data: dcount %d\n", + dcount); + error2 = EINVAL; + break; + } + + /* XXX: Skip setup words? We don't save them? */ + md_get_uint8(mdp, &wc); /* SetupCount */ + md_get_uint8(mdp, NULL); /* Reserved2 */ + tmp = wc; + while (tmp--) + md_get_uint16(mdp, NULL); + + if ((error2 = md_get_uint16le(mdp, &bc)) != 0) + break; + + /* + * There are pad bytes here, and the poff value + * indicates where the next data are found. + * No need to guess at the padding size. + */ + if (pcount) { + error2 = smb_t2_placedata(mdp->md_top, poff, + pcount, &t2p->t2_rparam); + if (error2) + break; + } + totpgot += pcount; + + if (dcount) { + error2 = smb_t2_placedata(mdp->md_top, doff, + dcount, &t2p->t2_rdata); + if (error2) + break; + } + totdgot += dcount; + + if (totpgot >= totpcount && totdgot >= totdcount) { + error2 = 0; + t2p->t2_flags |= SMBT2_ALLRECV; + break; + } + /* + * We're done with this reply, look for the next one. + */ + SMBRQ_LOCK(rqp); + md_next_record(&rqp->sr_rp); + SMBRQ_UNLOCK(rqp); + error2 = smb_rq_reply(rqp); + if (rqp->sr_flags & SMBR_MOREDATA) + t2p->t2_flags |= SMBT2_MOREDATA; + if (!error2) + continue; + t2p->t2_sr_errclass = rqp->sr_errclass; + t2p->t2_sr_serror = rqp->sr_serror; + t2p->t2_sr_error = rqp->sr_error; + t2p->t2_sr_rpflags2 = rqp->sr_rpflags2; + error = error2; + if (!(rqp->sr_flags & SMBR_MOREDATA)) + break; + } + return (error ? error : error2); +} + +static int +smb_nt_reply(struct smb_ntrq *ntp) +{ + struct mdchain *mdp; + struct smb_rq *rqp = ntp->nt_rq; + int error, error2; + u_int32_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp; + u_int32_t tmp, dcount, totpgot, totdgot; + u_int16_t bc; + u_int8_t wc; + + ntp->nt_flags &= ~SMBT2_MOREDATA; + + error = smb_rq_reply(rqp); + if (rqp->sr_flags & SMBR_MOREDATA) + ntp->nt_flags |= SMBT2_MOREDATA; + ntp->nt_sr_error = rqp->sr_error; + ntp->nt_sr_rpflags2 = rqp->sr_rpflags2; + if (error && !(rqp->sr_flags & SMBR_MOREDATA)) + return (error); + /* + * Now we have to get all subseqent responses. The CIFS specification + * says that they can be misordered which is weird. + * TODO: timo + */ + totpgot = totdgot = 0; + totpcount = totdcount = 0xffffffff; + mdp = &rqp->sr_rp; + for (;;) { + DTRACE_PROBE2(smb_trans_reply, + (smb_rq_t *), rqp, (mblk_t *), mdp->md_top); + m_dumpm(mdp->md_top); + + if ((error2 = md_get_uint8(mdp, &wc)) != 0) + break; + if (wc < 18) { + error2 = ENOENT; + break; + } + md_get_mem(mdp, NULL, 3, MB_MSYSTEM); /* reserved */ + if ((error2 = md_get_uint32le(mdp, &tmp)) != 0) + break; + if (totpcount > tmp) + totpcount = tmp; + if ((error2 = md_get_uint32le(mdp, &tmp)) != 0) + break; + if (totdcount > tmp) + totdcount = tmp; + if ((error2 = md_get_uint32le(mdp, &pcount)) != 0 || + (error2 = md_get_uint32le(mdp, &poff)) != 0 || + (error2 = md_get_uint32le(mdp, &pdisp)) != 0) + break; + if (pcount != 0 && pdisp != totpgot) { + SMBSDEBUG("Can't handle misordered parameters %d:%d\n", + pdisp, totpgot); + error2 = EINVAL; + break; + } + if ((error2 = md_get_uint32le(mdp, &dcount)) != 0 || + (error2 = md_get_uint32le(mdp, &doff)) != 0 || + (error2 = md_get_uint32le(mdp, &ddisp)) != 0) + break; + if (dcount != 0 && ddisp != totdgot) { + SMBSDEBUG("Can't handle misordered data: dcount %d\n", + dcount); + error2 = EINVAL; + break; + } + + /* XXX: Skip setup words? We don't save them? */ + md_get_uint8(mdp, &wc); /* SetupCount */ + tmp = wc; + while (tmp--) + md_get_uint16(mdp, NULL); + + if ((error2 = md_get_uint16le(mdp, &bc)) != 0) + break; + + /* + * There are pad bytes here, and the poff value + * indicates where the next data are found. + * No need to guess at the padding size. + */ + if (pcount) { + error2 = smb_t2_placedata(mdp->md_top, poff, pcount, + &ntp->nt_rparam); + if (error2) + break; + } + totpgot += pcount; + + if (dcount) { + error2 = smb_t2_placedata(mdp->md_top, doff, dcount, + &ntp->nt_rdata); + if (error2) + break; + } + totdgot += dcount; + + if (totpgot >= totpcount && totdgot >= totdcount) { + error2 = 0; + ntp->nt_flags |= SMBT2_ALLRECV; + break; + } + /* + * We're done with this reply, look for the next one. + */ + SMBRQ_LOCK(rqp); + md_next_record(&rqp->sr_rp); + SMBRQ_UNLOCK(rqp); + error2 = smb_rq_reply(rqp); + if (rqp->sr_flags & SMBR_MOREDATA) + ntp->nt_flags |= SMBT2_MOREDATA; + if (!error2) + continue; + ntp->nt_sr_error = rqp->sr_error; + ntp->nt_sr_rpflags2 = rqp->sr_rpflags2; + error = error2; + if (!(rqp->sr_flags & SMBR_MOREDATA)) + break; + } + return (error ? error : error2); +} + +int md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret); +int mb_put_mbuf(struct mbchain *mbp, mblk_t *m); + +/* + * Perform a full round of TRANS2 request + */ +static int +smb_t2_request_int(struct smb_t2rq *t2p) +{ + struct smb_vc *vcp = t2p->t2_vc; + struct smb_cred *scred = t2p->t2_cred; + struct mbchain *mbp; + struct mdchain *mdp, mbparam, mbdata; + mblk_t *m; + struct smb_rq *rqp; + int totpcount, leftpcount, totdcount, leftdcount, len, txmax, i; + int error, doff, poff, txdcount, txpcount, nmlen; + + m = t2p->t2_tparam.mb_top; + if (m) { + md_initm(&mbparam, m); /* do not free it! */ + totpcount = m_fixhdr(m); + if (totpcount > 0xffff) /* maxvalue for ushort_t */ + return (EINVAL); + } else + totpcount = 0; + m = t2p->t2_tdata.mb_top; + if (m) { + md_initm(&mbdata, m); /* do not free it! */ + totdcount = m_fixhdr(m); + if (totdcount > 0xffff) + return (EINVAL); + } else + totdcount = 0; + leftdcount = totdcount; + leftpcount = totpcount; + txmax = vcp->vc_txmax; + error = smb_rq_alloc(t2p->t2_source, t2p->t_name[0] ? + SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, scred, &rqp); + if (error) + return (error); + rqp->sr_timo = smb_timo_default; + rqp->sr_flags |= SMBR_MULTIPACKET; + t2p->t2_rq = rqp; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, totpcount); + mb_put_uint16le(mbp, totdcount); + mb_put_uint16le(mbp, t2p->t2_maxpcount); + mb_put_uint16le(mbp, t2p->t2_maxdcount); + mb_put_uint8(mbp, t2p->t2_maxscount); + mb_put_uint8(mbp, 0); /* reserved */ + mb_put_uint16le(mbp, 0); /* flags */ + mb_put_uint32le(mbp, 0); /* Timeout */ + mb_put_uint16le(mbp, 0); /* reserved 2 */ + len = mb_fixhdr(mbp); + + /* + * now we have known packet size as + * ALIGN4(len + 5 * 2 + setupcount * 2 + 2 + strlen(name) + 1), + * and need to decide which parts should go into the first request + */ + nmlen = t2p->t_name ? strlen(t2p->t_name) : 0; + len = ALIGN4(len + 5 * 2 + t2p->t2_setupcount * 2 + 2 + nmlen + 1); + if (len + leftpcount > txmax) { + txpcount = min(leftpcount, txmax - len); + poff = len; + txdcount = 0; + doff = 0; + } else { + txpcount = leftpcount; + poff = txpcount ? len : 0; + /* + * Other client traffic seems to "ALIGN2" here. The extra + * 2 byte pad we use has no observed downside and may be + * required for some old servers(?) + */ + len = ALIGN4(len + txpcount); + txdcount = min(leftdcount, txmax - len); + doff = txdcount ? len : 0; + } + leftpcount -= txpcount; + leftdcount -= txdcount; + mb_put_uint16le(mbp, txpcount); + mb_put_uint16le(mbp, poff); + mb_put_uint16le(mbp, txdcount); + mb_put_uint16le(mbp, doff); + mb_put_uint8(mbp, t2p->t2_setupcount); + mb_put_uint8(mbp, 0); + for (i = 0; i < t2p->t2_setupcount; i++) { + mb_put_uint16le(mbp, t2p->t2_setupdata[i]); + } + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + /* TDUNICODE */ + if (t2p->t_name) + mb_put_mem(mbp, t2p->t_name, nmlen, MB_MSYSTEM); + mb_put_uint8(mbp, 0); /* terminating zero */ + len = mb_fixhdr(mbp); + if (txpcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbparam, txpcount, &m); + SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + } + len = mb_fixhdr(mbp); + if (txdcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbdata, txdcount, &m); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + } + smb_rq_bend(rqp); /* incredible, but thats it... */ + error = smb_rq_enqueue(rqp); + if (error) + goto freerq; + if (leftpcount || leftdcount) { + error = smb_rq_reply(rqp); + if (error) + goto bad; + /* + * this is an interim response, ignore it. + */ + SMBRQ_LOCK(rqp); + md_next_record(&rqp->sr_rp); + SMBRQ_UNLOCK(rqp); + } + while (leftpcount || leftdcount) { + error = smb_rq_new(rqp, t2p->t_name ? + SMB_COM_TRANSACTION_SECONDARY : + SMB_COM_TRANSACTION2_SECONDARY); + if (error) + goto bad; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, totpcount); + mb_put_uint16le(mbp, totdcount); + len = mb_fixhdr(mbp); + /* + * now we have known packet size as + * ALIGN4(len + 7 * 2 + 2) for T2 request, and -2 for T one, + * and need to decide which parts should go into request + */ + len = ALIGN4(len + 6 * 2 + 2); + if (t2p->t_name == NULL) + len += 2; + if (len + leftpcount > txmax) { + txpcount = min(leftpcount, txmax - len); + poff = len; + txdcount = 0; + doff = 0; + } else { + txpcount = leftpcount; + poff = txpcount ? len : 0; + len = ALIGN4(len + txpcount); + txdcount = min(leftdcount, txmax - len); + doff = txdcount ? len : 0; + } + mb_put_uint16le(mbp, txpcount); + mb_put_uint16le(mbp, poff); + mb_put_uint16le(mbp, totpcount - leftpcount); + mb_put_uint16le(mbp, txdcount); + mb_put_uint16le(mbp, doff); + mb_put_uint16le(mbp, totdcount - leftdcount); + leftpcount -= txpcount; + leftdcount -= txdcount; + if (t2p->t_name == NULL) + mb_put_uint16le(mbp, t2p->t2_fid); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, 0); /* name */ + len = mb_fixhdr(mbp); + if (txpcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbparam, txpcount, &m); + if (error) + goto bad; + mb_put_mbuf(mbp, m); + } + len = mb_fixhdr(mbp); + if (txdcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbdata, txdcount, &m); + if (error) + goto bad; + mb_put_mbuf(mbp, m); + } + smb_rq_bend(rqp); + error = smb_iod_multirq(rqp); + if (error) + goto bad; + } /* while left params or data */ + error = smb_t2_reply(t2p); + if (error && !(t2p->t2_flags & SMBT2_MOREDATA)) + goto bad; + mdp = &t2p->t2_rdata; + if (mdp->md_top) { + m_fixhdr(mdp->md_top); + md_initm(mdp, mdp->md_top); + } + mdp = &t2p->t2_rparam; + if (mdp->md_top) { + m_fixhdr(mdp->md_top); + md_initm(mdp, mdp->md_top); + } +bad: + smb_iod_removerq(rqp); +freerq: + if (error && !(t2p->t2_flags & SMBT2_MOREDATA)) { + if (rqp->sr_flags & SMBR_RESTART) + t2p->t2_flags |= SMBT2_RESTART; + md_done(&t2p->t2_rparam); + md_done(&t2p->t2_rdata); + } + smb_rq_done(rqp); + return (error); +} + + +/* + * Perform a full round of NT_TRANSACTION request + */ +static int +smb_nt_request_int(struct smb_ntrq *ntp) +{ + struct smb_vc *vcp = ntp->nt_vc; + struct smb_cred *scred = ntp->nt_cred; + struct mbchain *mbp; + struct mdchain *mdp, mbsetup, mbparam, mbdata; + mblk_t *m; + struct smb_rq *rqp; + int totpcount, leftpcount, totdcount, leftdcount, len, txmax; + int error, doff, poff, txdcount, txpcount; + int totscount; + + m = ntp->nt_tsetup.mb_top; + if (m) { + md_initm(&mbsetup, m); /* do not free it! */ + totscount = m_fixhdr(m); + if (totscount > 2 * 0xff) + return (EINVAL); + } else + totscount = 0; + m = ntp->nt_tparam.mb_top; + if (m) { + md_initm(&mbparam, m); /* do not free it! */ + totpcount = m_fixhdr(m); + if (totpcount > 0x7fffffff) + return (EINVAL); + } else + totpcount = 0; + m = ntp->nt_tdata.mb_top; + if (m) { + md_initm(&mbdata, m); /* do not free it! */ + totdcount = m_fixhdr(m); + if (totdcount > 0x7fffffff) + return (EINVAL); + } else + totdcount = 0; + leftdcount = totdcount; + leftpcount = totpcount; + txmax = vcp->vc_txmax; + error = smb_rq_alloc(ntp->nt_source, SMB_COM_NT_TRANSACT, scred, &rqp); + if (error) + return (error); + rqp->sr_timo = smb_timo_default; + rqp->sr_flags |= SMBR_MULTIPACKET; + ntp->nt_rq = rqp; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint8(mbp, ntp->nt_maxscount); + mb_put_uint16le(mbp, 0); /* reserved (flags?) */ + mb_put_uint32le(mbp, totpcount); + mb_put_uint32le(mbp, totdcount); + mb_put_uint32le(mbp, ntp->nt_maxpcount); + mb_put_uint32le(mbp, ntp->nt_maxdcount); + len = mb_fixhdr(mbp); + /* + * now we have known packet size as + * ALIGN4(len + 4 * 4 + 1 + 2 + ((totscount+1)&~1) + 2), + * and need to decide which parts should go into the first request + */ + len = ALIGN4(len + 4 * 4 + 1 + 2 + ((totscount+1)&~1) + 2); + if (len + leftpcount > txmax) { + txpcount = min(leftpcount, txmax - len); + poff = len; + txdcount = 0; + doff = 0; + } else { + txpcount = leftpcount; + poff = txpcount ? len : 0; + len = ALIGN4(len + txpcount); + txdcount = min(leftdcount, txmax - len); + doff = txdcount ? len : 0; + } + leftpcount -= txpcount; + leftdcount -= txdcount; + mb_put_uint32le(mbp, txpcount); + mb_put_uint32le(mbp, poff); + mb_put_uint32le(mbp, txdcount); + mb_put_uint32le(mbp, doff); + mb_put_uint8(mbp, (totscount+1)/2); + mb_put_uint16le(mbp, ntp->nt_function); + if (totscount) { + error = md_get_mbuf(&mbsetup, totscount, &m); + SMBSDEBUG("%d:%d:%d\n", error, totscount, txmax); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + if (totscount & 1) + mb_put_uint8(mbp, 0); /* setup is in words */ + } + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + len = mb_fixhdr(mbp); + if (txpcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbparam, txpcount, &m); + SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + } + len = mb_fixhdr(mbp); + if (txdcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbdata, txdcount, &m); + if (error) + goto freerq; + mb_put_mbuf(mbp, m); + } + smb_rq_bend(rqp); /* incredible, but thats it... */ + error = smb_rq_enqueue(rqp); + if (error) + goto freerq; + if (leftpcount || leftdcount) { + error = smb_rq_reply(rqp); + if (error) + goto bad; + /* + * this is an interim response, ignore it. + */ + SMBRQ_LOCK(rqp); + md_next_record(&rqp->sr_rp); + SMBRQ_UNLOCK(rqp); + } + while (leftpcount || leftdcount) { + error = smb_rq_new(rqp, SMB_COM_NT_TRANSACT_SECONDARY); + if (error) + goto bad; + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_mem(mbp, NULL, 3, MB_MZERO); + mb_put_uint32le(mbp, totpcount); + mb_put_uint32le(mbp, totdcount); + len = mb_fixhdr(mbp); + /* + * now we have known packet size as + * ALIGN4(len + 6 * 4 + 2) + * and need to decide which parts should go into request + */ + len = ALIGN4(len + 6 * 4 + 2); + if (len + leftpcount > txmax) { + txpcount = min(leftpcount, txmax - len); + poff = len; + txdcount = 0; + doff = 0; + } else { + txpcount = leftpcount; + poff = txpcount ? len : 0; + len = ALIGN4(len + txpcount); + txdcount = min(leftdcount, txmax - len); + doff = txdcount ? len : 0; + } + mb_put_uint32le(mbp, txpcount); + mb_put_uint32le(mbp, poff); + mb_put_uint32le(mbp, totpcount - leftpcount); + mb_put_uint32le(mbp, txdcount); + mb_put_uint32le(mbp, doff); + mb_put_uint32le(mbp, totdcount - leftdcount); + leftpcount -= txpcount; + leftdcount -= txdcount; + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + len = mb_fixhdr(mbp); + if (txpcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbparam, txpcount, &m); + if (error) + goto bad; + mb_put_mbuf(mbp, m); + } + len = mb_fixhdr(mbp); + if (txdcount) { + mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO); + error = md_get_mbuf(&mbdata, txdcount, &m); + if (error) + goto bad; + mb_put_mbuf(mbp, m); + } + smb_rq_bend(rqp); + error = smb_iod_multirq(rqp); + if (error) + goto bad; + } /* while left params or data */ + error = smb_nt_reply(ntp); + if (error && !(ntp->nt_flags & SMBT2_MOREDATA)) + goto bad; + mdp = &ntp->nt_rdata; + if (mdp->md_top) { + m_fixhdr(mdp->md_top); + md_initm(mdp, mdp->md_top); + } + mdp = &ntp->nt_rparam; + if (mdp->md_top) { + m_fixhdr(mdp->md_top); + md_initm(mdp, mdp->md_top); + } +bad: + smb_iod_removerq(rqp); +freerq: + if (error && !(ntp->nt_flags & SMBT2_MOREDATA)) { + if (rqp->sr_flags & SMBR_RESTART) + ntp->nt_flags |= SMBT2_RESTART; + md_done(&ntp->nt_rparam); + md_done(&ntp->nt_rdata); + } + smb_rq_done(rqp); + return (error); +} + +int +smb_t2_request(struct smb_t2rq *t2p) +{ + int error = EINVAL, i; + + for (i = 0; ; ) { + /* + * Don't send any new requests if force unmount is underway. + * This check was moved into smb_rq_enqueue, called by + * smb_t2_request_int() + */ + t2p->t2_flags &= ~SMBT2_RESTART; + error = smb_t2_request_int(t2p); + if (!error) + break; + if ((t2p->t2_flags & (SMBT2_RESTART | SMBT2_NORESTART)) != + SMBT2_RESTART) + break; + if (++i > SMBMAXRESTARTS) + break; + mutex_enter(&(t2p)->t2_lock); + if (t2p->t2_share && t2p->t2_share->ss_mount) { + cv_timedwait(&t2p->t2_cond, &(t2p)->t2_lock, + lbolt + (hz * SMB_RCNDELAY)); + } else { + delay(lbolt + (hz * SMB_RCNDELAY)); + } + mutex_exit(&(t2p)->t2_lock); + } + return (error); +} + + +int +smb_nt_request(struct smb_ntrq *ntp) +{ + int error = EINVAL, i; + + for (i = 0; ; ) { + /* + * Don't send any new requests if force unmount is underway. + * This check was moved into smb_rq_enqueue, called by + * smb_nt_request_int() + */ + ntp->nt_flags &= ~SMBT2_RESTART; + error = smb_nt_request_int(ntp); + if (!error) + break; + if ((ntp->nt_flags & (SMBT2_RESTART | SMBT2_NORESTART)) != + SMBT2_RESTART) + break; + if (++i > SMBMAXRESTARTS) + break; + mutex_enter(&(ntp)->nt_lock); + if (ntp->nt_share && ntp->nt_share->ss_mount) { + cv_timedwait(&ntp->nt_cond, &(ntp)->nt_lock, + lbolt + (hz * SMB_RCNDELAY)); + + } else { + delay(lbolt + (hz * SMB_RCNDELAY)); + } + mutex_exit(&(ntp)->nt_lock); + } + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h new file mode 100644 index 0000000000..2e90252405 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h @@ -0,0 +1,199 @@ +/* + * 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. + * + * $Id: smb_rq.h,v 1.9 2005/01/22 22:20:58 lindak Exp $ + */ +#ifndef _NETSMB_SMB_RQ_H_ +#define _NETSMB_SMB_RQ_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <netsmb/mchain.h> +#include <sys/queue.h> + +#define SMBR_ALLOCED 0x0001 /* structure was malloced */ +#define SMBR_SENT 0x0002 /* request successfully transmitted */ +#define SMBR_REXMIT 0x0004 /* request should be retransmitted */ +#define SMBR_INTR 0x0008 /* request interrupted */ +#define SMBR_RESTART 0x0010 /* req should be repeated if possible */ +#define SMBR_NORESTART 0x0020 /* request is not restartable */ +#define SMBR_MULTIPACKET 0x0040 /* multiple pkts can be sent/received */ +#define SMBR_INTERNAL 0x0080 /* request is internal to netsmb */ +#define SMBR_NOINTR_SEND 0x0100 /* no interrupt in send wait */ +#define SMBR_NOINTR_RECV 0x0200 /* no interrupt in recv wait */ +#define SMBR_SENDWAIT 0x0400 /* waiting for send to complete */ +#define SMBR_VCREF 0x4000 /* took vc reference */ +#define SMBR_MOREDATA 0x8000 /* our buffer was too small */ + +#define SMBT2_ALLSENT 0x0001 /* all data and params are sent */ +#define SMBT2_ALLRECV 0x0002 /* all data and params are received */ +#define SMBT2_ALLOCED 0x0004 +#define SMBT2_RESTART 0x0008 +#define SMBT2_NORESTART 0x0010 +#define SMBT2_MOREDATA 0x8000 /* our buffer was too small */ + +#define SMBRQ_LOCK(rqp) mutex_enter(&(rqp)->sr_lock) +#define SMBRQ_UNLOCK(rqp) mutex_exit(&(rqp)->sr_lock) + + +enum smbrq_state { + SMBRQ_NOTSENT, /* rq have data to send */ + SMBRQ_SENT, /* send procedure completed */ + SMBRQ_REPLYRECEIVED, + SMBRQ_NOTIFIED /* owner notified about completion */ +}; + +struct smb_vc; + +struct smb_rq { + TAILQ_ENTRY(smb_rq) sr_link; + kmutex_t sr_lock; + kcondvar_t sr_cond; + enum smbrq_state sr_state; + struct smb_vc *sr_vc; + struct smb_share *sr_share; + struct _kthread *sr_owner; + ushort_t sr_mid; + struct mbchain sr_rq; + uchar_t sr_cmd; + uint8_t sr_rqflags; + uint16_t sr_rqflags2; + uchar_t *sr_wcount; + uchar_t *sr_bcount; + struct mdchain sr_rp; + int sr_rpgen; + int sr_rplast; + int sr_flags; /* SMBR_* */ + int sr_rpsize; + struct smb_cred *sr_cred; + int sr_timo; + int sr_rexmit; /* how many more retries. dflt 0 */ + int sr_sendcnt; + struct timespec sr_timesent; + int sr_lerror; + uint16_t *sr_rqtid; + uint16_t *sr_rquid; + uint8_t sr_errclass; + uint16_t sr_serror; + uint32_t sr_error; + uint8_t sr_rpflags; + uint16_t sr_rpflags2; + uint16_t sr_rptid; + uint16_t sr_rppid; + uint16_t sr_rpuid; + uint16_t sr_rpmid; +}; +typedef struct smb_rq smb_rq_t; + +struct smb_t2rq { + kmutex_t t2_lock; + kcondvar_t t2_cond; + uint16_t t2_setupcount; + uint16_t *t2_setupdata; + uint16_t t2_setup[SMB_MAXSETUPWORDS]; + 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 */ + uint16_t t2_fid; /* for T2 request */ + char t_name[128]; /* for T, should be zero for T2 */ + int t2_flags; /* SMBT2_ */ + struct mbchain t2_tparam; /* parameters to transmit */ + struct mbchain t2_tdata; /* data to transmit */ + struct mdchain t2_rparam; /* received paramters */ + struct mdchain t2_rdata; /* received data */ + struct smb_cred *t2_cred; + struct smb_connobj *t2_source; + struct smb_rq *t2_rq; + struct smb_vc *t2_vc; + struct smb_share *t2_share; /* for smb up/down */ + /* unmapped windows error detail */ + uint8_t t2_sr_errclass; + uint16_t t2_sr_serror; + uint32_t t2_sr_error; + uint16_t t2_sr_rpflags2; +}; +typedef struct smb_t2rq smb_t2rq_t; + +struct smb_ntrq { + kmutex_t nt_lock; + kcondvar_t nt_cond; + uint16_t nt_function; + uint8_t nt_maxscount; /* max setup words to return */ + uint32_t nt_maxpcount; /* max param bytes to return */ + uint32_t nt_maxdcount; /* max data bytes to return */ + int nt_flags; /* SMBT2_ */ + struct mbchain nt_tsetup; /* setup to transmit */ + struct mbchain nt_tparam; /* parameters to transmit */ + struct mbchain nt_tdata; /* data to transmit */ + struct mdchain nt_rparam; /* received paramters */ + struct mdchain nt_rdata; /* received data */ + struct smb_cred *nt_cred; + struct smb_connobj *nt_source; + struct smb_rq *nt_rq; + struct smb_vc *nt_vc; + struct smb_share *nt_share; /* for smb up/down */ + /* unmapped windows error details */ + uint32_t nt_sr_error; + uint16_t nt_sr_rpflags2; +}; +typedef struct smb_ntrq smb_ntrq_t; + +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); +void smb_rq_done(struct smb_rq *rqp); +int smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp); +int smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp); +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_t2_alloc(struct smb_connobj *layer, ushort_t setup, + struct smb_cred *scred, struct smb_t2rq **rqpp); +int smb_t2_init(struct smb_t2rq *rqp, struct smb_connobj *layer, + ushort_t *setup, int setupcnt, struct smb_cred *scred); +void smb_t2_done(struct smb_t2rq *t2p); +int smb_t2_request(struct smb_t2rq *t2p); +uint32_t smb_t2_err(struct smb_t2rq *t2p); + +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, + ushort_t fn, struct smb_cred *scred); +void smb_nt_done(struct smb_ntrq *ntp); +int smb_nt_request(struct smb_ntrq *ntp); + +#endif /* _NETSMB_SMB_RQ_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 new file mode 100644 index 0000000000..5f54b7c38f --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c @@ -0,0 +1,1783 @@ +/* + * 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. + * + * $Id: smb_smb.c,v 1.35.100.2 2005/06/02 00:55:39 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * various SMB requests. Most of the routines merely packs data into mbufs. + */ +#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/cmn_err.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#include <sys/utfconv.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_tran.h> + +/* + * 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) + +/* + * Default timeout values, all in seconds. + * Make these tunable (only via mdb for now). + */ +int smb_timo_notice = 15; +int smb_timo_default = 30; /* was SMB_DEFRQTIMO */ +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, u_int16_t fid, + int *len, int *rresid, uio_t *uiop, struct smb_cred *scred, int timo); +static int smb_smb_write(struct smb_share *ssp, u_int16_t fid, + int *len, int *rresid, uio_t *uiop, struct smb_cred *scred, int timo); + +struct smb_dialect { + int d_id; + const char *d_name; +}; + +smb_unichar smb_unieol = 0; + +static struct smb_dialect smb_dialects[] = { + {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"}, + {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"}, + {SMB_DIALECT_LANMAN2_0, "LM1.2X002"}, + {SMB_DIALECT_LANMAN2_1, "LANMAN2.1"}, + {SMB_DIALECT_NTLM0_12, "NT LM 0.12"}, + {-1, NULL} +}; + +#define SMB_DIALECT_MAX \ + (sizeof (smb_dialects) / sizeof (struct smb_dialect) - 2) + +/* + * Number of seconds between 1970 and 1601 year + */ +const u_int64_t DIFF1970TO1601 = 11644473600ULL; + +void +smb_time_local2server(struct timespec *tsp, int tzoff, long *seconds) +{ + /* + * XXX - what if we connected to the server when it was in + * daylight savings/summer time and we've subsequently switched + * to standard time, or vice versa, so that the time zone + * offset we got from the server is now wrong? + */ + *seconds = tsp->tv_sec - tzoff * 60; + /* - tz.tz_minuteswest * 60 - (wall_cmos_clock ? adjkerntz : 0) */ +} + +void +smb_time_server2local(ulong_t seconds, int tzoff, struct timespec *tsp) +{ + /* + * XXX - what if we connected to the server when it was in + * daylight savings/summer time and we've subsequently switched + * to standard time, or vice versa, so that the time zone + * offset we got from the server is now wrong? + */ + tsp->tv_sec = seconds + tzoff * 60; + /* + tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); */ + tsp->tv_nsec = 0; +} + +/* + * Time from server comes as UTC, so no need to use tz + */ +/*ARGSUSED*/ +void +smb_time_NT2local(u_int64_t nsec, int tzoff, struct timespec *tsp) +{ + smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp); +} + +/*ARGSUSED*/ +void +smb_time_local2NT(struct timespec *tsp, int tzoff, u_int64_t *nsec) +{ + long seconds; + + smb_time_local2server(tsp, 0, &seconds); + *nsec = (((u_int64_t)(seconds) & ~1) + DIFF1970TO1601) * + (u_int64_t)10000000; +} + +#if defined(NOICONVSUPPORT) || defined(lint) +extern int iconv_open(const char *to, const char *from, void **handle); +extern int iconv_close(void *handle); +#endif + +int +smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_dialect *dp; + struct smb_sopt *sp = NULL; + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc, stime[8], sblen; + u_int16_t dindex, tw, tw1, swlen, bc; + int error; + int unicode = 0; + char *servercs; + void *servercshandle = NULL; + void *localcshandle = NULL; + u_int16_t toklen; + + vcp->vc_hflags = SMB_FLAGS_CASELESS; /* XXX on Unix? */ + /* + * Make sure SMB_FLAGS2_UNICODE is "off" so mb_put_dstring + * marshalls the dialect strings in plain ascii. + */ + vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE; + vcp->vc_hflags2 |= SMB_FLAGS2_ERR_STATUS; + + SMB_VC_LOCK(vcp); + vcp->vc_flags &= ~(SMBV_ENCRYPT); + SMB_VC_UNLOCK(vcp); + + sp = &vcp->vc_sopt; + bzero(sp, sizeof (struct smb_sopt)); + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + for (dp = smb_dialects; dp->d_id != -1; dp++) { + mb_put_uint8(mbp, SMB_DT_DIALECT); + smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE); + } + smb_rq_bend(rqp); + + /* + * This request should not wait for + * connection state changes, etc. + */ + rqp->sr_flags |= SMBR_INTERNAL; + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) + goto bad; + + smb_rq_getreply(rqp, &mdp); + do { + error = md_get_uint8(mdp, &wc); + if (error) + break; + error = md_get_uint16le(mdp, &dindex); + if (error) + break; + error = EBADRPC; + if (dindex > SMB_DIALECT_MAX) { + SMBERROR( + "Don't know how to talk with server %s (%d)\n", + vcp->vc_srvname, dindex); + break; + } + dp = smb_dialects + dindex; + if (dindex < SMB_DIALECT_MAX) { + SMBERROR( + "Server %s negotiated old dialect (%s)\n", + vcp->vc_srvname, dp->d_name); + } + sp->sv_proto = dp->d_id; + SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc); + if (dp->d_id >= SMB_DIALECT_NTLM0_12) { + if (wc != 17) + break; + md_get_uint8(mdp, &sp->sv_sm); + md_get_uint16le(mdp, &sp->sv_maxmux); + md_get_uint16le(mdp, &sp->sv_maxvcs); + md_get_uint32le(mdp, &sp->sv_maxtx); + md_get_uint32le(mdp, &sp->sv_maxraw); + md_get_uint32le(mdp, &sp->sv_skey); + md_get_uint32le(mdp, &sp->sv_caps); + md_get_mem(mdp, (char *)stime, 8, MB_MSYSTEM); + md_get_uint16le(mdp, (u_int16_t *)&sp->sv_tz); + md_get_uint8(mdp, &sblen); + error = md_get_uint16le(mdp, &bc); + if (error) + break; + if (sp->sv_sm & SMB_SM_SIGS_REQUIRE) + SMBERROR("server configuration requires " + "packet signing, which we dont support: " + "sp->sv_sm %d\n", sp->sv_sm); + if (sp->sv_caps & SMB_CAP_UNICODE) { + SMB_VC_LOCK(vcp); + vcp->vc_flags |= SMBV_UNICODE; + SMB_VC_UNLOCK(vcp); + unicode = 1; + } + if (!(sp->sv_caps & SMB_CAP_STATUS32)) { + /* + * They don't do NT error codes. + * + * If we send requests with + * SMB_FLAGS2_ERR_STATUS set in + * Flags2, Windows 98, at least, + * appears to send replies with that + * bit set even though it sends back + * DOS error codes. (They probably + * just use the request header as + * a template for the reply header, + * and don't bother clearing that bit.) + * + * Therefore, we clear that bit in + * our vc_hflags2 field. + */ + vcp->vc_hflags2 &= ~SMB_FLAGS2_ERR_STATUS; + } + if (dp->d_id == SMB_DIALECT_NTLM0_12 && + sp->sv_maxtx < 4096 && + (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) { + SMB_VC_LOCK(vcp); + vcp->vc_flags |= SMBV_WIN95; + SMB_VC_UNLOCK(vcp); + SMBSDEBUG("Win95 detected\n"); + } + + /* + * 3 cases here: + * + * 1) Extended security. + * Read bc bytes below for security blob. + * Note that we DON'T put the Caps flag in outtok. + * outtoklen = bc + * + * 2) No extended security, have challenge data and + * possibly a domain name (which might be zero + * bytes long, meaning "missing"). + * Copy challenge stuff to vcp->vc_ch (sblen bytes), + * then copy Cap flags and domain name (bc-sblen + * bytes) to outtok. + * outtoklen = bc-sblen+4, where the 4 is for the + * Caps flag. + * + * 3) No extended security, no challenge data, just + * possibly a domain name. + * Copy Capsflags and domain name (bc) to outtok. + * outtoklen = bc+4, where 4 is for the Caps flag + */ + + /* + * Sanity check: make sure the challenge length + * isn't bigger than the byte count. + */ + if (sblen > bc) { + error = EBADRPC; + break; + } + toklen = bc; + + if (sblen && sblen <= SMB_MAXCHALLENGELEN && + sp->sv_sm & SMB_SM_ENCRYPT) { + error = md_get_mem(mdp, + (char *)vcp->vc_challenge, + sblen, MB_MSYSTEM); + if (error) + break; + vcp->vc_chlen = sblen; + toklen -= sblen; + + SMB_VC_LOCK(vcp); + vcp->vc_flags |= SMBV_ENCRYPT; + SMB_VC_UNLOCK(vcp); + } + + /* + * For servers that don't support unicode + * there are 2 things we could do: + * 1) Pass the server Caps flags up to the + * user level so the logic up there will + * know whether the domain name is unicode + * (this is what I did). + * 2) Try to convert the non-unicode string + * to unicode. This doubles the length of + * the outtok buffer and would be guessing that + * the string was single-byte ascii, and that + * might be wrong. Why ask for trouble? + */ + + /* Warning: NetApp may omit the GUID */ + + if (!(sp->sv_caps & SMB_CAP_EXT_SECURITY)) { + /* + * No extended security. + * Stick domain name, if present, + * and caps in outtok. + */ + toklen = toklen + 4; /* space for Caps flags */ + vcp->vc_outtoklen = toklen; + vcp->vc_outtok = kmem_alloc(toklen, KM_SLEEP); + /* first store server capability bits */ + /*LINTED*/ + ASSERT(vcp->vc_outtok == + (caddr_t)(((u_int32_t *)vcp->vc_outtok))); + /*LINTED*/ + *(u_int32_t *)(vcp->vc_outtok) = sp->sv_caps; + + /* + * Then store the domain name if present; + * be sure to subtract 4 from the length + * for the Caps flag. + */ + if (toklen > 4) { + error = md_get_mem(mdp, + vcp->vc_outtok+4, toklen-4, + MB_MSYSTEM); + } + } else { + /* + * Extended security. + * Stick the rest of the buffer in outtok. + */ + vcp->vc_outtoklen = toklen; + vcp->vc_outtok = kmem_alloc(toklen, KM_SLEEP); + error = md_get_mem(mdp, vcp->vc_outtok, toklen, + MB_MSYSTEM); + } + break; + } + vcp->vc_hflags2 &= ~(SMB_FLAGS2_EXT_SEC|SMB_FLAGS2_DFS| + SMB_FLAGS2_ERR_STATUS|SMB_FLAGS2_UNICODE); + if (dp->d_id > SMB_DIALECT_CORE) { + md_get_uint16le(mdp, &tw); + sp->sv_sm = (uchar_t)tw; + md_get_uint16le(mdp, &tw); + sp->sv_maxtx = tw; + md_get_uint16le(mdp, &sp->sv_maxmux); + md_get_uint16le(mdp, &sp->sv_maxvcs); + md_get_uint16le(mdp, &tw); /* rawmode */ + md_get_uint32le(mdp, &sp->sv_skey); + if (wc == 13) { /* >= LANMAN1 */ + md_get_uint16(mdp, &tw); /* time */ + md_get_uint16(mdp, &tw1); /* date */ + md_get_uint16le(mdp, (u_int16_t *)&sp->sv_tz); + md_get_uint16le(mdp, &swlen); + if (swlen > SMB_MAXCHALLENGELEN) + break; + md_get_uint16(mdp, NULL); /* mbz */ + if (md_get_uint16le(mdp, &bc) != 0) + break; + if (bc < swlen) + break; + if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) { + error = md_get_mem(mdp, + (char *)vcp->vc_challenge, + swlen, MB_MSYSTEM); + if (error) + break; + vcp->vc_chlen = swlen; + + SMB_VC_LOCK(vcp); + vcp->vc_flags |= SMBV_ENCRYPT; + SMB_VC_UNLOCK(vcp); + } + } + } else { /* an old CORE protocol */ + vcp->vc_hflags2 &= ~SMB_FLAGS2_KNOWS_LONG_NAMES; + sp->sv_maxmux = 1; + } + error = 0; + /*LINTED*/ + } while (0); + if (error == 0) { + uint32_t x; + + /* + * Maximum outstanding requests. + */ + if (vcp->vc_maxmux < 1) + vcp->vc_maxmux = 1; + + /* + * Max VCs between server and client. + * We only use one. + */ + vcp->vc_maxvcs = sp->sv_maxvcs; + if (vcp->vc_maxvcs < 1) + vcp->vc_maxvcs = 1; + + /* + * Maximum transfer size. + * Sanity checks: + * + * Spec. says lower limit is 1024. OK. + * + * 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_xxx, which we nearly always use. + */ + vcp->vc_txmax = sp->sv_maxtx; + if (vcp->vc_txmax < 1024) + vcp->vc_txmax = 1024; + 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. + * + * With CAP_LARGE_xxx, always use 60k. + * Otherwise use the vc_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. + */ + x = (vcp->vc_txmax - 68) & 0xFE00; + if (sp->sv_caps & SMB_CAP_LARGE_READX) + vcp->vc_rxmax = SMB_MAX_LARGE_RW_SIZE; + else + vcp->vc_rxmax = x; + if (sp->sv_caps & SMB_CAP_LARGE_WRITEX) + vcp->vc_wxmax = SMB_MAX_LARGE_RW_SIZE; + else + vcp->vc_wxmax = x; + + SMBSDEBUG("TZ = %d\n", sp->sv_tz); + SMBSDEBUG("CAPS = %x\n", sp->sv_caps); + + SMBSDEBUG("maxmux = %d\n", vcp->vc_maxmux); + SMBSDEBUG("maxvcs = %d\n", vcp->vc_maxvcs); + SMBSDEBUG("txmax = %d\n", vcp->vc_txmax); + SMBSDEBUG("rxmax = %d\n", vcp->vc_rxmax); + SMBSDEBUG("wxmax = %d\n", vcp->vc_wxmax); + } + + /* + * If the server supports Unicode, set up to use Unicode + * when talking to them. Othewise, use code page 437. + */ + if (unicode) + servercs = "ucs-2"; + else { + /* + * todo: if we can't determine the server's encoding, we + * need to try a best-guess here. + */ + servercs = "cp437"; + } +#if defined(NOICONVSUPPORT) || defined(lint) + /* + * REVISIT + */ + error = iconv_open(servercs, "utf-8", &servercshandle); + if (error != 0) + goto bad; + error = iconv_open("utf-8", servercs, &localcshandle); + if (error != 0) { + iconv_close(servercshandle); + goto bad; + } + if (vcp->vc_toserver) + iconv_close(vcp->vc_toserver); + if (vcp->vc_tolocal) + iconv_close(vcp->vc_tolocal); + vcp->vc_toserver = servercshandle; + vcp->vc_tolocal = localcshandle; +#endif + if (unicode) + vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE; +bad: + smb_rq_done(rqp); + return (error); +} + +static void +get_ascii_password(struct smb_vc *vcp, int upper, char *pbuf) +{ + const char *pw = smb_vc_getpass(vcp); + if (upper) + smb_toupper(pw, pbuf, SMB_MAXPASSWORDLEN); + else + strncpy(pbuf, pw, SMB_MAXPASSWORDLEN); + pbuf[SMB_MAXPASSWORDLEN] = '\0'; +} + +#ifdef APPLE +static void +get_unicode_password(struct smb_vc *vcp, char *pbuf) +{ + strncpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN); + pbuf[SMB_MAXPASSWORDLEN] = '\0'; +} +#endif + +/*ARGSUSED*/ +static uchar_t * +add_name_to_blob(uchar_t *blobnames, struct smb_vc *vcp, const uchar_t *name, + size_t namelen, int nametype, int uppercase) +{ + struct ntlmv2_namehdr namehdr; + char *namebuf; + u_int16_t *uninamebuf; + size_t uninamelen; + + if (name != NULL) { + uninamebuf = kmem_alloc(2 * namelen, KM_SLEEP); + if (uppercase) { + namebuf = kmem_alloc(namelen + 1, KM_SLEEP); + smb_toupper((const char *)name, namebuf, namelen); + namebuf[namelen] = '\0'; + uninamelen = smb_strtouni(uninamebuf, namebuf, namelen, + UCONV_IGNORE_NULL); + kmem_free(namebuf, namelen + 1); + } else { + uninamelen = smb_strtouni(uninamebuf, (char *)name, + namelen, UCONV_IGNORE_NULL); + } + } else { + uninamelen = 0; + uninamebuf = NULL; + } + namehdr.type = htoles(nametype); + namehdr.len = htoles(uninamelen); + bcopy(&namehdr, blobnames, sizeof (namehdr)); + blobnames += sizeof (namehdr); + if (uninamebuf != NULL) { + bcopy(uninamebuf, blobnames, uninamelen); + blobnames += uninamelen; + kmem_free(uninamebuf, namelen * 2); + } + return (blobnames); +} + +static uchar_t * +make_ntlmv2_blob(struct smb_vc *vcp, u_int64_t client_nonce, size_t *bloblen) +{ + uchar_t *blob; + size_t blobsize; + size_t domainlen, srvlen; + struct ntlmv2_blobhdr *blobhdr; + struct timespec now; + u_int64_t timestamp; + uchar_t *blobnames; + ptrdiff_t diff; + + /* + * XXX - the information at + * + * http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response + * + * says that the "target information" comes from the Type 2 message, + * but, as we're not doing NTLMSSP, we don't have that. + * + * Should we use the names from the NegProt response? Can we trust + * the NegProt response? (I've seen captures where the primary + * domain name has an extra byte in front of it.) + * + * For now, we don't trust it - we use vcp->vc_domain and + * vcp->vc_srvname, instead. We upper-case them and convert + * them to Unicode, as that's what's supposed to be in the blob. + */ + domainlen = strlen(vcp->vc_domain); + srvlen = strlen(vcp->vc_srvname); + blobsize = sizeof (struct ntlmv2_blobhdr) + + 3*sizeof (struct ntlmv2_namehdr) + 4 + 2*domainlen + 2*srvlen; + blob = kmem_zalloc(blobsize, KM_SLEEP); + /*LINTED*/ + ASSERT(blob == (uchar_t *)((struct ntlmv2_blobhdr *)blob)); + /*LINTED*/ + blobhdr = (struct ntlmv2_blobhdr *)blob; + blobhdr->header = htolel(0x00000101); + gethrestime(&now); + smb_time_local2NT(&now, 0, ×tamp); + blobhdr->timestamp = htoleq(timestamp); + blobhdr->client_nonce = client_nonce; + blobnames = blob + sizeof (struct ntlmv2_blobhdr); + blobnames = add_name_to_blob(blobnames, vcp, (uchar_t *)vcp->vc_domain, + domainlen, NAMETYPE_DOMAIN_NB, 1); + blobnames = add_name_to_blob(blobnames, vcp, (uchar_t *)vcp->vc_srvname, + srvlen, NAMETYPE_MACHINE_NB, 1); + blobnames = add_name_to_blob(blobnames, vcp, NULL, 0, NAMETYPE_EOL, 0); + diff = (intptr_t)blobnames - (intptr_t)blob; + ASSERT(diff == (ptrdiff_t)((size_t)diff)); + *bloblen = (size_t)diff; + return (blob); +} + +/* + * See radar 4134676. This define helps us avoid how a certain old server + * grants limited Guest access when we try NTLMv2, but works fine with NTLM. + * The fingerprint we are looking for here is DOS error codes and no-Unicode. + * Note XP grants Guest access but uses Unicode and NT error codes. + */ +#define smb_antique(rqp) (!((rqp)->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS) && \ + !((rqp)->sr_rpflags2 & SMB_FLAGS2_UNICODE)) + +/* + * When not doing Kerberos, we can try, in order: + * + * NTLMv2 + * NTLM with the ASCII password not upper-cased + * NTLM with the ASCII password upper-cased + * + * if the server supports encrypted passwords, or + * + * plain-text with the ASCII password not upper-cased + * plain-text with the ASCII password upper-cased + * + * if it doesn't. + */ +#define STATE_NTLMV2 0 +#define STATE_NOUCPW 1 +#define STATE_UCPW 2 + +int +smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc; + int minauth; + smb_uniptr unipp = NULL, ntencpass = NULL; + char *pp = NULL, *up = NULL, *ucup = NULL, *ucdp = NULL; + char *pbuf = NULL; + char *encpass = NULL; + int error = 0; + size_t plen = 0, uniplen = 0, uniplen2 = 0, tmplen; + size_t ucup_sl = 0, ucdp_sl = 0; + int state; + size_t ntlmv2_bloblen; + uchar_t *ntlmv2_blob; + u_int64_t client_nonce; + u_int32_t caps; + u_int16_t bl; /* BLOB length */ + u_int16_t saveflags2 = vcp->vc_hflags2; + void * savetoserver = vcp->vc_toserver; + u_int16_t action; + int declinedguest = 0; + static const char NativeOS[] = "Solaris"; + static const char LanMan[] = "NETSMB"; + /* + * Most of the "capability" bits we offer should be copied + * from those offered by the server, with a mask applied. + * This is the mask of capabilies copied from the server. + * Some others get special handling below. + */ + static const uint32_t caps_mask = + SMB_CAP_UNICODE | + SMB_CAP_LARGE_FILES | + SMB_CAP_NT_SMBS | + SMB_CAP_STATUS32 | + SMB_CAP_LARGE_READX | + SMB_CAP_LARGE_WRITEX; + + caps = vcp->vc_sopt.sv_caps & caps_mask; + + /* No unicode unless server supports and encryption on */ + if (!((vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) && + (vcp->vc_flags & SMBV_UNICODE))) { + vcp->vc_hflags2 &= 0xffff - SMB_FLAGS2_UNICODE; + vcp->vc_toserver = 0; + } + + minauth = vcp->vc_vopt & SMBVOPT_MINAUTH; + if (vcp->vc_intok) { + if (vcp->vc_intoklen > 65536 || + !(vcp->vc_hflags2 & SMB_FLAGS2_EXT_SEC) || + SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) { + error = EINVAL; + goto ssn_exit; + } + vcp->vc_smbuid = 0; + } + + /* + * Try only plain text passwords. + */ + if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { + state = STATE_NTLMV2; /* try NTLMv2 first */ + } else { + state = STATE_NOUCPW; /* try plain-text mixed-case first */ + } +again: + + if (!vcp->vc_intok) + vcp->vc_smbuid = SMB_UID_UNKNOWN; + + if (!vcp->vc_intok) { + /* + * We're not doing extended security, which, for + * now, means we're not doing Kerberos. + * Fail if the minimum authentication level is + * Kerberos. + */ + if (minauth >= SMBVOPT_MINAUTH_KERBEROS) { + error = EAUTH; + goto ssn_exit; + } + if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { + /* + * Server wants encrypted passwords. + */ + if (state > STATE_NTLMV2) { + /* + * We tried NTLMv2 in STATE_NTLMV2. + * Shall we allow fallback? (to NTLM) + */ + if (minauth >= SMBVOPT_MINAUTH_NTLMV2) { + error = EAUTH; + goto ssn_exit; + } + } + if (state > STATE_NOUCPW) { + /* + * We tried NTLM in STATE_NOUCPW. + * No need to try it again. + */ + error = EAUTH; + goto ssn_exit; + } + } else { + /* + * Plain-text passwords. + * Fail if the minimum authentication level is + * LM or better. + */ + if (minauth > SMBVOPT_MINAUTH_NTLM) { + error = EAUTH; + goto ssn_exit; + } + } + } + + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, + scred, &rqp); + if (error) + goto ssn_exit; + + /* + * Domain name must be upper-case, as that's what's used + * when computing LMv2 and NTLMv2 responses - and, for NTLMv2, + * the domain name in the request has to be upper-cased as well. + * (That appears not to be the case for the user name. Go + * figure.) + * + * don't need to uppercase domain string. It's already uppercase UTF-8. + */ + + ucdp_sl = strlen(vcp->vc_domain); + ucdp = kmem_zalloc(ucdp_sl + 1, KM_SLEEP); + memcpy(ucdp, vcp->vc_domain, ucdp_sl + 1); + + if (vcp->vc_intok) { + caps |= SMB_CAP_EXT_SECURITY; + } else if (!(vcp->vc_sopt.sv_sm & SMB_SM_USER)) { + /* + * In the share security mode password will be used + * only in the tree authentication + */ + pp = ""; + plen = 1; + unipp = &smb_unieol; + uniplen = sizeof (smb_unieol); + } else { + pbuf = kmem_alloc(SMB_MAXPASSWORDLEN + 1, KM_SLEEP); + if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { + if (state == STATE_NTLMV2) { + /* + * Compute the LMv2 and NTLMv2 responses, + * derived from the challenge, the user name, + * the domain/workgroup into which we're + * logging, and the Unicode password. + */ + + /* + * Construct the client nonce by getting + * a bunch of random data. + */ + (void) random_get_pseudo_bytes((void *) + &client_nonce, sizeof (client_nonce)); + + /* + * Convert the user name to upper-case, as + * that's what's used when computing LMv2 + * and NTLMv2 responses. + */ + ucup_sl = strlen(vcp->vc_username); + ucup = kmem_alloc(ucup_sl + 1, KM_SLEEP); + smb_toupper((const char *)vcp->vc_username, + ucup, ucup_sl); + ucup[ucup_sl] = '\0'; + + /* + * Compute the LMv2 response, derived + * from the server challenge, the + * user name, the domain/workgroup + * into which we're logging, the + * client nonce, and the NT hash. + */ + smb_ntlmv2response(vcp->vc_nthash, + (uchar_t *)ucup, (uchar_t *)ucdp, + vcp->vc_challenge, + (uchar_t *)&client_nonce, 8, + (uchar_t **)&encpass, &plen); + pp = encpass; + + /* + * Construct the blob. + */ + ntlmv2_blob = make_ntlmv2_blob(vcp, + client_nonce, &ntlmv2_bloblen); + + /* + * Compute the NTLMv2 response, derived + * from the server challenge, the + * user name, the domain/workgroup + * into which we're logging, the + * blob, and the NT hash. + */ + smb_ntlmv2response(vcp->vc_nthash, + (uchar_t *)ucup, (uchar_t *)ucdp, + vcp->vc_challenge, + ntlmv2_blob, ntlmv2_bloblen, + (uchar_t **)&ntencpass, &uniplen); + uniplen2 = uniplen; + unipp = ntencpass; + tmplen = plen; + + kmem_free(ucup, ucup_sl + 1); + kmem_free((char *)ntlmv2_blob, + sizeof (struct ntlmv2_blobhdr) + + 3 * sizeof (struct ntlmv2_namehdr) + + 4 + + 2 * strlen(vcp->vc_domain) + + 2 * strlen(vcp->vc_srvname)); + } else { + plen = 24; + encpass = kmem_zalloc(plen, KM_SLEEP); + /* + * Compute the LM response, derived + * from the challenge and the ASCII + * password. + */ + if (minauth < SMBVOPT_MINAUTH_NTLM) { + smb_lmresponse(vcp->vc_lmhash, + vcp->vc_challenge, + (uchar_t *)encpass); + } + pp = encpass; + + /* + * Compute the NTLM response, derived from + * the challenge and the NT hash. + */ + uniplen = 24; + uniplen2 = uniplen; + ntencpass = kmem_alloc(uniplen, KM_SLEEP); + smb_lmresponse(vcp->vc_nthash, + vcp->vc_challenge, + (uchar_t *)ntencpass); + unipp = ntencpass; + } + } else { + /* + * We try w/o uppercasing first so Samba mixed case + * passwords work. If that fails, we come back and + * try uppercasing to satisfy OS/2 and Windows for + * Workgroups. + */ + get_ascii_password(vcp, (state == STATE_UCPW), pbuf); + plen = strlen(pbuf) + 1; + pp = pbuf; + uniplen = plen * 2; + uniplen2 = uniplen; + ntencpass = kmem_alloc(uniplen, KM_SLEEP); + (void) smb_strtouni(ntencpass, smb_vc_getpass(vcp), + 0, 0); + plen--; + /* + * The uniplen is zeroed because Samba cannot deal + * with this 2nd cleartext password. This Samba + * "bug" is actually a workaround for problems in + * Microsoft clients. + */ + uniplen = 0; /* -= 2 */ + unipp = ntencpass; + } + } + smb_rq_wstart(rqp); + mbp = &rqp->sr_rq; + up = vcp->vc_username; + /* + * If userid is null we are attempting anonymous browse login + * so passwords must be zero length. + */ + if (*up == '\0') { + plen = uniplen = 0; + } + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx); + mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux); + mb_put_uint16le(mbp, vcp->vc_number); + mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey); + if ((SMB_DIALECT(vcp)) < SMB_DIALECT_NTLM0_12) { + mb_put_uint16le(mbp, plen); + mb_put_uint32le(mbp, 0); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, pp, plen, MB_MSYSTEM); + smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* user */ + smb_put_dstring(mbp, vcp, ucdp, SMB_CS_NONE); /* domain */ + } else { + if (vcp->vc_intok) { + mb_put_uint16le(mbp, vcp->vc_intoklen); + mb_put_uint32le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, caps); /* my caps */ + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, vcp->vc_intok, vcp->vc_intoklen, + MB_MSYSTEM); /* security blob */ + } else { + mb_put_uint16le(mbp, plen); + mb_put_uint16le(mbp, uniplen); + mb_put_uint32le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, caps); /* my caps */ + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_mem(mbp, pp, plen, MB_MSYSTEM); /* password */ + mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM); + smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* user */ + smb_put_dstring(mbp, vcp, ucdp, SMB_CS_NONE); /* dom */ + } + } + smb_put_dstring(mbp, vcp, NativeOS, SMB_CS_NONE); /* OS */ + smb_put_dstring(mbp, vcp, LanMan, SMB_CS_NONE); /* LAN Mgr */ + smb_rq_bend(rqp); + if (ntencpass) { + kmem_free(ntencpass, uniplen2); + ntencpass = NULL; + } + if (encpass) { + kmem_free(encpass, 24); + encpass = NULL; + } + if (ucdp) { + kmem_free(ucdp, ucdp_sl + 1); + ucdp = NULL; + } + + /* + * This request should not wait for + * connection state changes, etc. + */ + rqp->sr_flags |= SMBR_INTERNAL; + error = smb_rq_simple_timed(rqp, SMBSSNSETUPTIMO); + SMBSDEBUG("%d\n", error); + if (error) { + if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess) + error = EAUTH; + if (!(rqp->sr_errclass == ERRDOS && + rqp->sr_serror == ERRmoredata)) + goto bad; + } + vcp->vc_smbuid = rqp->sr_rpuid; + smb_rq_getreply(rqp, &mdp); + do { + error = md_get_uint8(mdp, &wc); + if (error) + break; + error = EBADRPC; + if (vcp->vc_intok) { + if (wc != 4) + break; + } else if (wc != 3) + break; + md_get_uint8(mdp, NULL); /* secondary cmd */ + md_get_uint8(mdp, NULL); /* mbz */ + md_get_uint16le(mdp, NULL); /* andxoffset */ + md_get_uint16le(mdp, &action); /* action */ + if (vcp->vc_intok) + md_get_uint16le(mdp, &bl); /* ext security */ + md_get_uint16le(mdp, NULL); /* byte count */ + if (vcp->vc_intok) { + vcp->vc_outtoklen = bl; + vcp->vc_outtok = kmem_alloc(bl, KM_SLEEP); + error = md_get_mem(mdp, vcp->vc_outtok, bl, MB_MSYSTEM); + if (error) + break; + } + /* server OS, LANMGR, & Domain here */ + error = 0; + /*LINTED*/ + } while (0); +bad: + if (encpass) { + kmem_free(encpass, tmplen); + encpass = NULL; + } + if (pbuf) { + kmem_free(pbuf, SMB_MAXPASSWORDLEN + 1); + pbuf = NULL; + } + if (vcp->vc_sopt.sv_sm & SMB_SM_USER && !vcp->vc_intok && + (error || (*up != '\0' && action & SMB_ACT_GUEST && + state == STATE_NTLMV2 && smb_antique(rqp)))) { + /* + * We're doing user-level authentication (so we are actually + * sending authentication stuff over the wire), and we're + * not doing extended security, and the stuff we tried + * failed (or we we're trying to login a real user but + * got granted guest access instead.) + */ + if (!error) + declinedguest = 1; + /* + * Should we try the next type of authentication? + */ + if (state < STATE_UCPW) { + /* + * Yes, we still have more to try. + */ + state++; + smb_rq_done(rqp); + goto again; + } + } + smb_rq_done(rqp); + +ssn_exit: + if (error && declinedguest) + SMBERROR("we declined ntlmv2 guest access. errno will be %d\n", + error); + /* Restore things we changed and return */ + vcp->vc_hflags2 = saveflags2; + vcp->vc_toserver = savetoserver; + return (error); +} + +int +smb_smb_ssnclose(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); + /* + * Run this with a relatively short timeout. + * We don't really care about the result, + * as we're just trying to play nice and + * "say goodbye" before we hangup. + * XXX: Add SMBLOGOFFTIMO somewhere? + */ + error = smb_rq_simple_timed(rqp, 5); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + return (error); +} + +static char smb_any_share[] = "?????"; + +static char * +smb_share_typename(int stype) +{ + char *pp; + + switch (stype) { + case STYPE_DISKTREE: + pp = "A:"; + break; + case STYPE_PRINTQ: + pp = smb_any_share; /* can't use LPT: here... */ + break; + case STYPE_DEVICE: + pp = "COMM"; + break; + case STYPE_IPC: + pp = "IPC"; + break; + default: + pp = smb_any_share; + break; + } + return (pp); +} + +int +smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) +{ + struct smb_vc *vcp; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + char *pp, *pbuf, *encpass; + const char *pw; + uchar_t hash[SMB_PWH_MAX]; + int error, plen, caseopt; + int upper = 0; + +again: + 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. + * + * No longer need to set ssp->ss_tid + * here, but it's harmless enough. + */ + ssp->ss_tid = SMB_TID_UNKNOWN; + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX, + scred, &rqp); + if (error) + return (error); + caseopt = SMB_CS_NONE; + if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { + plen = 1; + pp = ""; + pbuf = NULL; + encpass = NULL; + } else { + pbuf = kmem_alloc(SMB_MAXPASSWORDLEN + 1, KM_SLEEP); + encpass = kmem_alloc(24, KM_SLEEP); + pw = smb_share_getpass(ssp); + /* + * We try w/o uppercasing first so Samba mixed case + * passwords work. If that fails we come back and try + * uppercasing to satisfy OS/2 and Windows for Workgroups. + */ + if (upper++) { + smb_toupper(pw, pbuf, SMB_MAXPASSWORDLEN); + smb_oldlm_hash(pw, hash); + } else { + strncpy(pbuf, pw, SMB_MAXPASSWORDLEN); + smb_ntlmv1hash(pw, hash); + } + pbuf[SMB_MAXPASSWORDLEN] = '\0'; + +#ifdef NOICONVSUPPORT + /* + * We need to convert here to the server codeset. + * Initially we will send the same stuff and see what happens + * witout the conversion. REVISIT. + */ + iconv_convstr(vcp->vc_toserver, pbuf, pbuf, SMB_MAXPASSWORDLEN); +#endif + if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { + plen = 24; + smb_lmresponse(hash, + vcp->vc_challenge, + (uchar_t *)encpass); + pp = encpass; + } else { + plen = strlen(pbuf) + 1; + pp = pbuf; + } + } + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); + mb_put_uint8(mbp, 0); + mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, 0); /* Flags */ + mb_put_uint16le(mbp, plen); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + error = mb_put_mem(mbp, pp, plen, MB_MSYSTEM); + if (error) { + SMBSDEBUG("error %d from mb_put_mem for pp\n", error); + goto bad; + } + smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt, NULL); + pp = vcp->vc_srvname; + error = smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt, NULL); + if (error) { + SMBSDEBUG("error %d from smb_put_dmem for srvname\n", error); + goto bad; + } + smb_put_dmem(mbp, vcp, "\\", 1, caseopt, NULL); + pp = ssp->ss_name; + error = smb_put_dstring(mbp, vcp, pp, caseopt); + if (error) { + SMBSDEBUG("error %d from smb_put_dstring for ss_name\n", error); + goto bad; + } + /* The type name is always ASCII */ + pp = smb_share_typename(ssp->ss_type); + error = mb_put_mem(mbp, pp, strlen(pp) + 1, MB_MSYSTEM); + if (error) { + SMBSDEBUG("error %d from mb_put_mem for ss_type\n", error); + goto bad; + } + smb_rq_bend(rqp); + /* + * Don't want to risk missing a successful + * tree connect response. + */ + rqp->sr_flags |= SMBR_NOINTR_RECV; + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + if (error) + goto bad; + + /* Success! */ + SMB_SS_LOCK(ssp); + ssp->ss_tid = rqp->sr_rptid; + ssp->ss_vcgenid = vcp->vc_genid; + ssp->ss_flags |= SMBS_CONNECTED; + SMB_SS_UNLOCK(ssp); + +bad: + if (encpass) + kmem_free(encpass, 24); + if (pbuf) + kmem_free(pbuf, SMB_MAXPASSWORDLEN + 1); + smb_rq_done(rqp); + if (error && upper == 1) + goto again; + return (error); +} + +int +smb_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->ss_tid == SMB_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), SMB_COM_TREE_DISCONNECT, scred, &rqp); + if (error) + return (error); + rqp->sr_share = ssp; /* by hand */ + mbp = &rqp->sr_rq; +#ifdef lint + mbp = mbp; +#endif + smb_rq_wstart(rqp); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + + /* + * 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. + */ + rqp->sr_flags |= SMBR_NOINTR_SEND; + error = smb_rq_simple_timed(rqp, 5); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + ssp->ss_tid = SMB_TID_UNKNOWN; + return (error); +} + +static int +smb_smb_readx(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, + uio_t *uiop, struct smb_cred *scred, int timo) +{ + struct smb_vc *vcp = SSTOVC(ssp); + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int8_t wc; + int error; + u_int16_t residhi, residlo, off, doff; + u_int32_t resid; + + if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) == 0) { + /* Fall back to the old cmd. */ + return (smb_smb_read(ssp, fid, len, rresid, uiop, + scred, timo)); + } + if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0) { + /* Have ReadX but not large files? */ + if ((uiop->uio_loffset + *len) > UINT32_MAX) + return (EFBIG); + } + *len = min(*len, vcp->vc_rxmax); + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); /* no secondary command */ + mb_put_uint8(mbp, 0); /* MBZ */ + mb_put_uint16le(mbp, 0); /* offset to secondary */ + mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint32le(mbp, (u_int32_t)(uiop->uio_offset)); + mb_put_uint16le(mbp, (u_int16_t)*len); /* MaxCount */ + mb_put_uint16le(mbp, (u_int16_t)*len); /* MinCount */ + /* (only indicates blocking) */ + mb_put_uint32le(mbp, (unsigned)*len >> 16); /* MaxCountHigh */ + mb_put_uint16le(mbp, (u_int16_t)*len); /* Remaining ("obsolete") */ + mb_put_uint32le(mbp, (u_int32_t)((uiop->uio_loffset) >> 32)); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + do { + if (timo == 0) + timo = smb_timo_read; + error = smb_rq_simple_timed(rqp, timo); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + off = SMB_HDRLEN; + md_get_uint8(mdp, &wc); + off++; + if (wc != 12) { + error = EBADRPC; + break; + } + md_get_uint8(mdp, NULL); + off++; + md_get_uint8(mdp, NULL); + off++; + md_get_uint16le(mdp, NULL); + off += 2; + md_get_uint16le(mdp, NULL); + off += 2; + md_get_uint16le(mdp, NULL); /* data compaction mode */ + off += 2; + md_get_uint16le(mdp, NULL); + off += 2; + md_get_uint16le(mdp, &residlo); + off += 2; + md_get_uint16le(mdp, &doff); /* data offset */ + off += 2; + md_get_uint16le(mdp, &residhi); + off += 2; + resid = (residhi << 16) | residlo; + md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); + off += 4*2; + md_get_uint16le(mdp, NULL); /* ByteCount */ + off += 2; + if (doff > off) /* pad byte(s)? */ + md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM); + if (resid == 0) { + *rresid = resid; + break; + } + error = md_get_uio(mdp, uiop, resid); + if (error) + break; + *rresid = resid; + /*LINTED*/ + } while (0); + smb_rq_done(rqp); + return (error); +} + +static int +smb_smb_writex(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, + uio_t *uiop, struct smb_cred *scred, int timo) +{ + struct smb_vc *vcp = SSTOVC(ssp); + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + int error; + u_int8_t wc; + u_int16_t resid; + + if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) == 0) { + /* Fall back to the old cmd. */ + return (smb_smb_write(ssp, fid, len, rresid, uiop, + scred, timo)); + } + if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0) { + /* Have WriteX but not large files? */ + if ((uiop->uio_loffset + *len) > UINT32_MAX) + return (EFBIG); + } + *len = min(*len, vcp->vc_wxmax); + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_uint8(mbp, 0xff); /* no secondary command */ + mb_put_uint8(mbp, 0); /* MBZ */ + mb_put_uint16le(mbp, 0); /* offset to secondary */ + mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint32le(mbp, (u_int32_t)(uiop->uio_offset)); + mb_put_uint32le(mbp, 0); /* MBZ (timeout) */ + mb_put_uint16le(mbp, 0); /* !write-thru */ + mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, (u_int16_t)((unsigned)*len >> 16)); + mb_put_uint16le(mbp, (u_int16_t)*len); + mb_put_uint16le(mbp, 64); /* data offset from header start */ + mb_put_uint32le(mbp, (u_int32_t)((uiop->uio_loffset) >> 32)); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + do { + mb_put_uint8(mbp, 0xee); /* mimic xp pad byte! */ + error = mb_put_uio(mbp, uiop, *len); + if (error) + break; + smb_rq_bend(rqp); + if (timo == 0) + timo = smb_timo_write; + error = smb_rq_simple_timed(rqp, timo); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 6) { + error = EBADRPC; + break; + } + md_get_uint8(mdp, NULL); + md_get_uint8(mdp, NULL); + md_get_uint16le(mdp, NULL); + md_get_uint16le(mdp, &resid); /* actually is # written */ + *rresid = resid; + /* + * if LARGE_WRITEX then there's one more bit of # written + */ + if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)) { + md_get_uint16le(mdp, NULL); + md_get_uint16le(mdp, &resid); + *rresid |= (int)(resid & 1) << 16; + } + /*LINTED*/ + } while (0); + + smb_rq_done(rqp); + return (error); +} + +static int +smb_smb_read(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, + uio_t *uiop, struct smb_cred *scred, int timo) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t resid, bc; + u_int8_t wc; + int error, rlen; + + /* This cmd is limited to 32-bit offsets. */ + if ((uiop->uio_loffset + *len) > UINT32_MAX) + return (EFBIG); + *len = rlen = min(*len, SSTOVC(ssp)->vc_rxmax); + + 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_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint16le(mbp, (u_int16_t)rlen); + mb_put_uint32le(mbp, (u_int32_t)uiop->uio_offset); + mb_put_uint16le(mbp, (u_int16_t)min(uiop->uio_resid, 0xffff)); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + do { + if (timo == 0) + timo = smb_timo_read; + error = smb_rq_simple_timed(rqp, timo); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 5) { + error = EBADRPC; + break; + } + md_get_uint16le(mdp, &resid); + md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); + md_get_uint16le(mdp, &bc); + md_get_uint8(mdp, NULL); /* ignore buffer type */ + md_get_uint16le(mdp, &resid); + if (resid == 0) { + *rresid = resid; + break; + } + error = md_get_uio(mdp, uiop, resid); + if (error) + break; + *rresid = resid; + /*LINTED*/ + } while (0); + smb_rq_done(rqp); + return (error); +} + +static int +smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid, + uio_t *uiop, struct smb_cred *scred, int timo) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + struct mdchain *mdp; + u_int16_t resid; + u_int8_t wc; + int error; + + /* This cmd is limited to 32-bit offsets. */ + if ((uiop->uio_loffset + *len) > UINT32_MAX) + return (EFBIG); + *len = resid = min(*len, SSTOVC(ssp)->vc_wxmax); + + 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_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint16le(mbp, resid); + mb_put_uint32le(mbp, (u_int32_t)uiop->uio_offset); + mb_put_uint16le(mbp, (u_int16_t)min(uiop->uio_resid, 0xffff)); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + mb_put_uint8(mbp, SMB_DT_DATA); + mb_put_uint16le(mbp, resid); + do { + error = mb_put_uio(mbp, uiop, resid); + if (error) + break; + smb_rq_bend(rqp); + if (timo == 0) + timo = smb_timo_write; + error = smb_rq_simple_timed(rqp, timo); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 1) { + error = EBADRPC; + break; + } + md_get_uint16le(mdp, &resid); + *rresid = resid; + /*LINTED*/ + } while (0); + smb_rq_done(rqp); + 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, u_int16_t fid, uio_rw_t rw, + uio_t *uiop, struct smb_cred *scred, int timo) +{ + ssize_t old_resid, tsize; + offset_t old_offset; + int len, resid; + int error = 0; + + old_offset = uiop->uio_loffset; + old_resid = tsize = uiop->uio_resid; + + while (tsize > 0) { + /* Lint: tsize may be 64-bits */ + len = SMB_MAX_LARGE_RW_SIZE; + if (len > tsize) + len = (int)tsize; + + if (rw == UIO_READ) + error = smb_smb_readx(ssp, fid, &len, &resid, uiop, + scred, timo); + else + error = smb_smb_writex(ssp, fid, &len, &resid, uiop, + scred, timo); + if (error) + break; + + if (resid < len) { + error = EIO; + break; + } + + tsize -= resid; + timo = 0; /* only first write is special */ + } + + if (error) { + /* + * Errors can happen in copyin/copyout, the rpc, etc. so + * they imply resid is unreliable. The only safe thing is + * to pretend zero bytes made it. We needn't restore the + * iovs because callers don't depend on them in error + * paths - uio_resid and uio_offset are what matter. + */ + uiop->uio_loffset = old_offset; + uiop->uio_resid = old_resid; + } + + return (error); +} + + +static u_int32_t smbechoes = 0; + +int +smb_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), SMB_COM_ECHO, scred, &rqp); + if (error) + return (error); + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + mb_put_uint16le(mbp, 1); /* echo count */ + smb_rq_wend(rqp); + 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_INTERNAL; + error = smb_rq_simple_timed(rqp, timo); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + return (error); +} + +#ifdef APPLE +int +smb_smb_checkdir(struct smb_share *ssp, void *dnp, char *name, + int nmlen, struct smb_cred *scred) +{ + struct smb_rq *rqp; + struct mbchain *mbp; + int error; + + error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CHECK_DIRECTORY, scred, &rqp); + 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); + /* + * All we need to do is marshall the path: "\\" + * (the root of the share) into this request. + * We essentially in-line smbfs_fullpath() here, + * except no mb_put_padbyte (already aligned). + */ + smb_put_dstring(mbp, SSTOVC(ssp), "\\", SMB_CS_NONE); + smb_rq_bend(rqp); + + error = smb_rq_simple(rqp); + SMBSDEBUG("%d\n", error); + smb_rq_done(rqp); + + return (error); +} +#endif /* APPLE */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h new file mode 100644 index 0000000000..eb5895f702 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h @@ -0,0 +1,149 @@ +/* + * 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. + * + * $Id: smb_subr.h,v 1.13 2004/09/14 22:59:08 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _NETSMB_SMB_SUBR_H_ +#define _NETSMB_SMB_SUBR_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/cmn_err.h> +#include <sys/lock.h> +#include <sys/note.h> + +/* Helper function for SMBERROR */ +/*PRINTFLIKE3*/ +extern void smb_errmsg(int, const char *, const char *, ...) + __KPRINTFLIKE(3); +void m_dumpm(mblk_t *m); + +/* + * Let's use C99 standard variadic macros! + * Also the C99 __func__ (function name) feature. + */ +#define SMBERROR(...) \ + smb_errmsg(CE_NOTE, __func__, __VA_ARGS__) +#define SMBPANIC(...) \ + smb_errmsg(CE_PANIC, __func__, __VA_ARGS__) +#define SMBSDEBUG(...) \ + smb_errmsg(CE_CONT, __func__, __VA_ARGS__) +#define SMBIODEBUG(...) \ + smb_errmsg(CE_CONT, __func__, __VA_ARGS__) +#define NBDEBUG(...) \ + smb_errmsg(CE_CONT, __func__, __VA_ARGS__) + +#if defined(DEBUG) || defined(lint) + +#define DEBUG_ENTER(str) debug_enter(str) + +#else /* DEBUG or lint */ + +#define DEBUG_ENTER(str) ((void)0) + +#endif /* DEBUG or lint */ + +#define SMB_SIGMASK \ + (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \ + sigmask(SIGHUP)|sigmask(SIGQUIT)) + +#define SMB_STRFREE(p) do { \ + if (p) \ + smb_strfree(p); \ + _NOTE(CONSTCOND) \ +} while (0) + +typedef uint16_t smb_unichar; +typedef smb_unichar *smb_uniptr; + +extern smb_unichar smb_unieol; + +struct mbchain; +struct smb_rq; +struct smb_vc; + +/* + * Tunable timeout values. See: smb_smb.c + */ +extern int smb_timo_notice; +extern int smb_timo_default; +extern int smb_timo_open; +extern int smb_timo_read; +extern int smb_timo_write; +extern int smb_timo_append; + +#define EMOREDATA (0x7fff) + +#ifdef APPLE +void smb_scred_init(struct smb_cred *scred, vfs_context_t vfsctx); +int smb_sigintr(vfs_context_t); +#endif +void smb_credinit(struct smb_cred *scred, struct proc *p, cred_t *cr); +void smb_credrele(struct smb_cred *scred); +char *smb_strdup(const char *s); +void *smb_memdup(const void *umem, int len); +char *smb_strdupin(char *s, int maxlen); +void *smb_memdupin(void *umem, int len); +size_t smb_strtouni(uint16_t *dst, const char *src, size_t inlen, int flags); +void smb_strfree(char *s); +void smb_memfree(void *s); +void *smb_zmalloc(unsigned long size); + +void smb_oldlm_hash(const char *apwd, uchar_t *hash); +void smb_ntlmv1hash(const char *apwd, uchar_t *hash); + +int smb_lmresponse(const uchar_t *hash, uchar_t *C8, uchar_t *RN); +int smb_ntlmresponse(const uchar_t *hash, uchar_t *C8, uchar_t *RN); +int smb_ntlmv2response(const uchar_t *hash, const uchar_t *user, + const uchar_t *destination, uchar_t *C8, const uchar_t *blob, + size_t bloblen, uchar_t **RN, size_t *RNlen); +int smb_maperror(int eclass, int eno); +uint32_t smb_maperr32(uint32_t eno); +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_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); + +#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 new file mode 100644 index 0000000000..41341a0308 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c @@ -0,0 +1,1179 @@ +/* + * 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. + * + * $Id: smb_subr.c,v 1.27.108.1 2005/06/02 00:55:39 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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/isa_defs.h> +#include <sys/stream.h> +#include <sys/strsun.h> +#include <sys/sunddi.h> +#include <sys/cmn_err.h> +#include <sys/sdt.h> +#include <sys/priv.h> +#include <sys/u8_textprep.h> + +#include <netsmb/smb_osdep.h> +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#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, struct proc *p, cred_t *icr) +{ + scred->vc_pid = p->p_pidp->pid_id; + if (!icr) + icr = p->p_cred; + if (is_system_labeled()) { + icr = crdup(icr); + (void) setpflags(NET_MAC_AWARE, 1, icr); + } else { + crhold(icr); + } + scred->vc_ucred = icr; +} + +void +smb_credrele(struct smb_cred *scred) +{ + crfree(scred->vc_ucred); + scred->vc_ucred = NULL; +} + +#ifdef APPLE +/*ARGSUSED*/ +int +smb_sigintr(vfs_context_t vfsctx) +{ + /* + * I cannot find something to match vfs_context_issignal. + * It calls proc_pendingsignals() in Darwin code. + */ + if (vfsctx && vfs_context_issignal(vfsctx, SMB_SIGMASK)) + return (EINTR); + return (0); +} +#endif + +char * +smb_strdup(const char *s) +{ + char *p; + int len; + + len = s ? strlen(s) + 1 : 1; + p = kmem_alloc(len, KM_SLEEP); + if (s) + bcopy(s, p, len); + else + *p = 0; + return (p); +} + +/* + * duplicate string from a user space. + */ +char * +smb_strdupin(char *s, int maxlen) +{ + char *p, bt; + int len = 0; + + for (p = s; ; p++) { + if (copyin(p, &bt, 1)) + return (NULL); + len++; + if (maxlen && len > maxlen) + return (NULL); + if (bt == 0) + break; + } + p = kmem_alloc(len, KM_SLEEP); + copyin(s, p, len); + return (p); +} + +/* + * duplicate memory block from a user space. + */ +void * +smb_memdupin(void *umem, int len) +{ + char *p; + + if (len > 32 * 1024) + return (NULL); + p = kmem_alloc(len, KM_SLEEP); + if (copyin(umem, p, len) == 0) + return (p); + kmem_free(p, len); + return (NULL); +} + +/* + * duplicate memory block in the kernel space. + */ +void * +smb_memdup(const void *umem, int len) +{ + char *p; + + if (len > 32 * 1024) + return (NULL); + p = kmem_alloc(len, KM_SLEEP); + if (p == NULL) + return (NULL); + bcopy(umem, p, len); + return (p); +} + +void +smb_strfree(char *s) +{ + kmem_free(s, strlen(s) + 1); +} + +void +smb_memfree(void *s) +{ + kmem_free(s, strlen(s)); +} + +void * +smb_zmalloc(unsigned long size) +{ + void *p = kmem_zalloc(size, KM_SLEEP); + return (p); +} + +size_t +smb_strtouni(u_int16_t *dst, const char *src, size_t inlen, int flags) +{ + size_t outlen = 0; + + if (!inlen) + inlen = strlen(src); + + /* Force output format to little-endian. */ + flags &= ~UCONV_OUT_BIG_ENDIAN; + flags |= UCONV_OUT_LITTLE_ENDIAN; + + outlen = inlen * 2; + if (uconv_u8tou16((uchar_t *)src, &inlen, dst, &outlen, flags) != 0) { + outlen = 0; + } + return (outlen * 2); +} + +/* + * Helper for the SMBERROR macro, etc. + * This is also a good place for a breakpoint + * or a dtrace probe, i.e. fbt:nsmb:smb_errmsg + */ +void +smb_errmsg(int cel, const char *func_name, const char *fmt, ...) +{ + va_list adx; + char buf[100]; + + va_start(adx, fmt); + if (cel == CE_CONT) { + /* + * This is one of our xxxDEBUG macros. + * Don't bother to log these, but just + * fire a dtrace probe with the message. + */ + vsnprintf(buf, sizeof (buf), fmt, adx); + DTRACE_PROBE2(debugmsg2, + (char *), func_name, + (char *), buf); + } else { + /* + * This is one of our xxxERROR macros. + * Add a prefix to the fmt string, + * then let vcmn_err do the args. + */ + snprintf(buf, sizeof (buf), "?%s: %s", func_name, fmt); + DTRACE_PROBE3(debugmsg3, + (char *), func_name, + (char *), buf, + va_list, adx); + vcmn_err(cel, buf, adx); + } + va_end(adx); +} + +#if 1 /* def SMB_SOCKETDATA_DEBUG */ +void +m_dumpm(mblk_t *m) +{ + int len, seg; + + len = msgdsize(m); + DTRACE_PROBE2(dsize, int, len, (mblk_t *), m); + + for (seg = 0; m; seg++) { + DTRACE_PROBE2(mblk, int, seg, (mblk_t *), m); + m = m->b_cont; + } +} +#endif + +/* all these need review XXX */ +#ifndef EPROTO +#define EPROTO ECONNABORTED +#endif +#ifndef ELIBACC +#define ELIBACC ENOENT +#endif +#ifndef ENODATA +#define ENODATA EINVAL +#endif +#ifndef ENOTUNIQ +#define ENOTUNIQ EADDRINUSE +#endif +#ifndef ECOMM +#define ECOMM EIO +#endif +#ifndef ENOMEDIUM +#define ENOMEDIUM EIO +#endif +#ifndef ETIME +#define ETIME ETIMEDOUT +#endif + +static struct { + unsigned nterr; + unsigned errno; +} nt2errno[] = { + {NT_STATUS_ACCESS_DENIED, EACCES}, + {NT_STATUS_ACCESS_VIOLATION, EACCES}, + {NT_STATUS_ACCOUNT_DISABLED, EACCES}, + {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_CANNOT_DELETE, EACCES}, + {NT_STATUS_CONFLICTING_ADDRESSES, EADDRINUSE}, + {NT_STATUS_CONNECTION_ABORTED, ECONNABORTED}, + {NT_STATUS_CONNECTION_DISCONNECTED, ECONNABORTED}, + {NT_STATUS_CONNECTION_REFUSED, ECONNREFUSED}, + {NT_STATUS_CONNECTION_RESET, ENETRESET}, + {NT_STATUS_DEVICE_DOES_NOT_EXIST, ENODEV}, + {NT_STATUS_DEVICE_PROTOCOL_ERROR, EPROTO}, + {NT_STATUS_DIRECTORY_NOT_EMPTY, ENOTEMPTY}, + {NT_STATUS_DISK_FULL, ENOSPC}, + {NT_STATUS_DLL_NOT_FOUND, ELIBACC}, + {NT_STATUS_END_OF_FILE, ENODATA}, + {NT_STATUS_FILE_IS_A_DIRECTORY, EISDIR}, + {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_INTEGER_OVERFLOW, ERANGE}, + {NT_STATUS_INVALID_HANDLE, EBADF}, + {NT_STATUS_INVALID_LOGON_HOURS, EACCES}, + {NT_STATUS_INVALID_PARAMETER, EINVAL}, + {NT_STATUS_INVALID_PIPE_STATE, EPIPE}, + {NT_STATUS_INVALID_WORKSTATION, EACCES}, + {NT_STATUS_IN_PAGE_ERROR, EFAULT}, + {NT_STATUS_IO_TIMEOUT, ETIMEDOUT}, + {NT_STATUS_IP_ADDRESS_CONFLICT1, ENOTUNIQ}, + {NT_STATUS_IP_ADDRESS_CONFLICT2, ENOTUNIQ}, + {NT_STATUS_LICENSE_QUOTA_EXCEEDED, EDQUOT}, + {NT_STATUS_LOGON_FAILURE, EACCES}, + {NT_STATUS_MEDIA_WRITE_PROTECTED, EROFS}, + {NT_STATUS_MEMORY_NOT_ALLOCATED, EFAULT}, + {NT_STATUS_NAME_TOO_LONG, ENAMETOOLONG}, + {NT_STATUS_NETWORK_ACCESS_DENIED, EACCES}, + {NT_STATUS_NETWORK_BUSY, EBUSY}, + {NT_STATUS_NETWORK_UNREACHABLE, ENETUNREACH}, + {NT_STATUS_NET_WRITE_FAULT, ECOMM}, + {NT_STATUS_NONEXISTENT_SECTOR, ESPIPE}, + {NT_STATUS_NOT_A_DIRECTORY, ENOTDIR}, + {NT_STATUS_NOT_IMPLEMENTED, ENOSYS}, + {NT_STATUS_NOT_MAPPED_VIEW, EINVAL}, + {NT_STATUS_NOT_SUPPORTED, ENOSYS}, + {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_OBJECT_NAME_COLLISION, EEXIST}, + {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT}, + {NT_STATUS_OBJECT_PATH_INVALID, ENOTDIR}, + {NT_STATUS_PAGEFILE_QUOTA, EDQUOT}, + {NT_STATUS_PASSWORD_EXPIRED, EACCES}, + {NT_STATUS_PASSWORD_RESTRICTION, EACCES}, + {NT_STATUS_PATH_NOT_COVERED, ENOENT}, + {NT_STATUS_PIPE_BROKEN, EPIPE}, + {NT_STATUS_PIPE_BUSY, EPIPE}, + {NT_STATUS_PIPE_CONNECTED, EISCONN}, + {NT_STATUS_PIPE_DISCONNECTED, EPIPE}, + {NT_STATUS_PIPE_NOT_AVAILABLE, ENOSYS}, + {NT_STATUS_PORT_CONNECTION_REFUSED, ECONNREFUSED}, + {NT_STATUS_PORT_MESSAGE_TOO_LONG, EMSGSIZE}, + {NT_STATUS_PORT_UNREACHABLE, EHOSTUNREACH}, + {NT_STATUS_PROTOCOL_UNREACHABLE, ENOPROTOOPT}, + {NT_STATUS_QUOTA_EXCEEDED, EDQUOT}, + {NT_STATUS_REGISTRY_QUOTA_LIMIT, EDQUOT}, + {NT_STATUS_REMOTE_DISCONNECT, ESHUTDOWN}, + {NT_STATUS_REMOTE_NOT_LISTENING, ECONNREFUSED}, + {NT_STATUS_REQUEST_NOT_ACCEPTED, EACCES}, + {NT_STATUS_RETRY, EAGAIN}, + {NT_STATUS_SHARING_VIOLATION, EBUSY}, + {NT_STATUS_TIMER_NOT_CANCELED, ETIME}, + {NT_STATUS_TOO_MANY_LINKS, EMLINK}, + {NT_STATUS_TOO_MANY_OPENED_FILES, EMFILE}, + {NT_STATUS_UNABLE_TO_FREE_VM, EADDRINUSE}, + {NT_STATUS_UNSUCCESSFUL, EINVAL}, + {NT_STATUS_WRONG_PASSWORD, EACCES}, + {0, 0} +}; + +static struct { + unsigned dclass; + unsigned derr; + unsigned nterr; +} nt2doserr[] = { + {ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, + {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_INFO_CLASS}, + {ERRDOS, ERRbadlength, NT_STATUS_INFO_LENGTH_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, + {ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, + {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, + {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_CID}, + {ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER}, + {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, + {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, + {ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, + {ERRDOS, ERRhandleeof, NT_STATUS_END_OF_FILE}, + {ERRDOS, ERRwrongdisk, NT_STATUS_WRONG_VOLUME}, + {ERRDOS, ERRnotready, NT_STATUS_NO_MEDIA_IN_DEVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, + {ERRDOS, ERRsectornotfound, NT_STATUS_NONEXISTENT_SECTOR}, + {ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, + {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, + {ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, + {ERRDOS, ERRinvalidparam, NT_STATUS_UNABLE_TO_FREE_VM}, + {ERRDOS, ERRinvalidparam, NT_STATUS_UNABLE_TO_DELETE_SECTION}, + {ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, + {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, + {ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, + {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, + {ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, + {ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, + {ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, + {ERRDOS, 158, NT_STATUS_NOT_LOCKED}, + {ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, + {ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, + {ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, + {ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_MIX}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, + {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, + {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, + {ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, + {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, + {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, + {ERRDOS, ERRcrc, NT_STATUS_DATA_ERROR}, + {ERRDOS, ERRcrc, NT_STATUS_CRC_ERROR}, + {ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, + {ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, + {ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, + {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, + {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PAGE_PROTECTION}, + {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, + {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {ERRDOS, ERRinvalidparam, NT_STATUS_PORT_ALREADY_SET}, + {ERRDOS, ERRinvalidparam, NT_STATUS_SECTION_NOT_IMAGE}, + {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, + {ERRDOS, ERRinvalidparam, NT_STATUS_BAD_WORKING_SET_LIMIT}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INCOMPATIBLE_FILE_MAP}, + {ERRDOS, ERRinvalidparam, NT_STATUS_SECTION_PROTECTION}, + {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED}, + {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, + {ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, + {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, + {ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, + {ERRDOS, ERRnoaccess, NT_STATUS_DELETE_PENDING}, + {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, + {ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, + {ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, + {ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, + {ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, + {ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, + {ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, + {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, + {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, + {ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, + {ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS}, + {ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION}, + {ERRSRV, 2242, NT_STATUS_PASSWORD_EXPIRED}, + {ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED}, + {ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, + {ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, + {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, + {ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, + {ERRDOS, 112, NT_STATUS_DISK_FULL}, + {ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, + {ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, + {ERRDOS, ERRtoomanynames, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, + {ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, + {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, + {ERRDOS, ERRoutofmem, NT_STATUS_SECTION_NOT_EXTENDED}, + {ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, + {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, + {ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, + {ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, + {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, + {ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, + {ERRDOS, ERRcrc, NT_STATUS_DEVICE_DATA_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, + {ERRDOS, ERRnotready, NT_STATUS_DEVICE_POWER_FAILURE}, + {ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, + {ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, + {ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, + {ERRDOS, ERRwriteprotect, NT_STATUS_MEDIA_WRITE_PROTECTED}, + {ERRDOS, ERRnotready, NT_STATUS_DEVICE_NOT_READY}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, + {ERRDOS, ERRinvalidparam, NT_STATUS_BAD_MASTER_BOOT_RECORD}, + {ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, + {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, + {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, + {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, + {ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, + {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, + {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, + {ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, + {ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, + {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, + {ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, + {ERRDOS, ERRhandleeof, NT_STATUS_FILE_FORCED_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, + {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, + {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, + {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, + {ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, + {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, + {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, + {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, + {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, + {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, + {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, + {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, + {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, + {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, + {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, + {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, + {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, + {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, + {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_1}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_2}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_3}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_4}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_5}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_6}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_7}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_8}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_9}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_10}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_11}, + {ERRDOS, ERRinvalidparam, NT_STATUS_INVALID_PARAMETER_12}, + {ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, + {ERRDOS, 203, NT_STATUS_VARIABLE_NOT_FOUND}, + {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, + {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, + {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, + {ERRDOS, 2401, NT_STATUS_FILES_OPEN}, + {ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, + {ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, + {ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, + {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}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, + {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, + {ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, + {ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, + {ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, + {ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, + {ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, + {ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, + {ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, + {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, + {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, + {ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, + {ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, + {ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, + {ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, + {ERRDOS, 59, NT_STATUS_LINK_FAILED}, + {ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, + {ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, + {ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, + {ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, + {ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, + {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, + {ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, + {ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, + {ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, + {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, + {ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, + {ERRHRD, ERRgeneral, NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT}, + {ERRHRD, ERRgeneral, NT_STATUS_16F}, + {ERRHRD, ERRgeneral, NT_STATUS_170}, + {ERRHRD, ERRgeneral, NT_STATUS_171}, + {ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, + {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS_179}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, + {ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, + {ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {ERRDOS, ERRinvalidparam, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, + {ERRDOS, ERRbadcmd, NT_STATUS_INVALID_DEVICE_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, + {ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, + {ERRDOS, ERRwriteprotect, NT_STATUS_TOO_LATE}, + {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, + {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, + {ERRDOS, ERRinvgroup, NT_STATUS_NETLOGON_NOT_STARTED}, + {ERRSRV, 2239, NT_STATUS_ACCOUNT_EXPIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, + {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, + {ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, + {ERRDOS, ERRnoaccess, + NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, + {ERRDOS, ERRnoaccess, + NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, + {ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, + {ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, + {ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, + {ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, + {ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, + {ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, + {ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, + {ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, + {ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, + {ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, + {ERRSRV, 2242, NT_STATUS_PASSWORD_MUST_CHANGE}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, + {ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, + {ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, + {ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, + {ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, + {ERRHRD, ERRgeneral, NT_STATUS_RETRY}, + {ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, + {ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, + {ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, + {ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, + {ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, + {ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, + {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, + {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, + {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, + {ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, + {ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, + {ERRHRD, ERRgeneral, 0x000024a}, + {ERRHRD, ERRgeneral, 0x000024b}, + {ERRHRD, ERRgeneral, 0x000024c}, + {ERRHRD, ERRgeneral, 0x000024d}, + {ERRHRD, ERRgeneral, 0x000024e}, + {ERRHRD, ERRgeneral, 0x000024f}, + {ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, + {ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, + {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, + {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, + {ERRSRV, ERRbadtype, NT_STATUS_PATH_NOT_COVERED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, + {ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, + {ERRHRD, ERRgeneral, 0x000025d}, + {ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, + {ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, + {ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, + {ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LINKS}, + {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, + {ERRDOS, ERRnotready, NT_STATUS_VOLUME_DISMOUNTED}, + {ERRDOS, 161, NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT}, + {ERRDOS, ERRnoaccess, NT_STATUS_ENCRYPTION_FAILED}, + {ERRDOS, ERRnoaccess, NT_STATUS_DECRYPTION_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_RANGE_NOT_FOUND}, + {ERRDOS, ERRnoaccess, NT_STATUS_NO_RECOVERY_POLICY}, + {ERRDOS, ERRnoaccess, NT_STATUS_NO_EFS}, + {ERRDOS, ERRnoaccess, NT_STATUS_WRONG_EFS}, + {ERRDOS, ERRnoaccess, NT_STATUS_NO_USER_KEYS}, + {ERRDOS, ERRbadfunc, NT_STATUS_VOLUME_NOT_UPGRADED}, +}; + +u_int32_t +smb_maperr32(u_int32_t eno) +{ + int i; + unsigned orig = eno; + + /* + * Hi two bits are "severity". Ignore "success" (0) and + * "informational" (1) values. + */ + if (!(eno & 0x80000000)) + return (0); + /* mask off "severity" and the "component" bit */ + eno &= ~(0xe0000000); + + /* first try direct map to unix */ + for (i = 0; nt2errno[i].errno; i++) + if (nt2errno[i].nterr == eno) + return (nt2errno[i].errno); + SMBERROR("no direct map for 32 bit server error (0x%x)\n", orig); + + /* ok, then try mapping to dos to unix */ + for (i = 0; nt2doserr[i].derr; i++) + if (nt2doserr[i].nterr == eno) + return (smb_maperror(nt2doserr[i].dclass, + nt2doserr[i].derr)); + return (smb_maperror(ERRHRD, ERRgeneral)); +} + + +int +smb_maperror(int eclass, int eno) +{ + if (eclass == 0 && eno == 0) + return (0); + switch (eclass) { + case ERRDOS: + switch (eno) { + case ERRbadfunc: + case ERRbadenv: + case ERRbadformat: + case ERRremcd: + case ERRrmuns: + return (EINVAL); + case ERRbadfile: + case ERRbadpath: + case ERRnoipc: + case ERRnosuchshare: + return (ENOENT); + case ERRnofids: + return (EMFILE); + case ERRnoaccess: + /* + * XXX CSM Reported on samba-technical 12/7/2002 + * + * There is a case for which server(s) return + * ERRnoaccess but should return ERRdiskfull: When + * the offset for a write is exactly the server + * file size limit then Samba (at least) thinks + * the reason for zero bytes having been written + * must have been "access denied" from the local + * filesystem. This cannot be easily worked + * around since the server behaviour is + * indistinguishable from actual access denied. + * An incomplete workaround: attempt a 2 byte write + * from "offset-1". (That may require reading at + * offset-1 first.) The flaw is that reading or + * writing at offset-1 could cause an + * unrelated error (due to a byte range lock + * for instance) and we can't presume the + * order servers check errors in. + */ + case ERRbadaccess: + return (EACCES); + case ERRbadshare: + return (EBUSY); + case ERRbadfid: + return (EBADF); + case ERRbadmcb: + return (EIO); + case ERRnomem: + return (ENOMEM); /* actually remote no mem... */ + case ERRbadmem: + return (EFAULT); + case ERRbaddata: + return (E2BIG); + case ERRbaddrive: + case ERRnotready: /* nt */ + return (ENXIO); + case ERRdiffdevice: + return (EXDEV); + case ERRnofiles: + return (0); /* eeof ? */ + case ERRlock: + return (EDEADLK); + case ERRfilexists: + return (EEXIST); + case ERRinvalidname: /* samba maps as noent */ + return (ENOENT); + case ERRdirnotempty: /* samba */ + return (ENOTEMPTY); + case ERRnotlocked: + return (0); /* 0 since bsd unlocks on any close */ + case ERRrename: + return (EEXIST); + case ERRmoredata: + return (EMOREDATA); + } + break; + case ERRSRV: + switch (eno) { + case ERRerror: + return (EINVAL); + case ERRbadpw: + return (EAUTH); + case ERRaccess: + case ERRbaduid: + return (EACCES); + case ERRinvnid: + return (ENETRESET); + case ERRinvnetname: + SMBERROR("NetBIOS name is invalid: %d\n", + ERRinvnetname); + return (EAUTH); + case ERRbadtype: /* reserved and returned */ + return (EIO); + case ERRacctexpired: /* NT: account exists but disabled */ + return (EPERM); + } + break; + case ERRHRD: + switch (eno) { + case ERRnowrite: + return (EROFS); + case ERRbadunit: + return (ENODEV); + case ERRbadreq: + return (EBADRPC); + case ERRbadshare: + return (ETXTBSY); + case ERRlock: + return (EDEADLK); + case ERRdiskfull: + return (EFBIG); + case ERRnotready: + case ERRbadcmd: + case ERRdata: + case ERRgeneral: + return (EIO); + default: + SMBERROR("Unmapped DOS error %d:%d\n", eclass, eno); + return (EIO); + } + } + SMBERROR("Unmapped DOS error %d:%d\n", eclass, eno); + return (EBADRPC); +} + +#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 + +/*ARGSUSED*/ +int +smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src, + int size, int caseopt, int *lenp) +{ + uint16_t convbuf[SMALL_CONV]; + uint16_t *cbuf; + size_t cbufalloc, inlen, outlen; + int error; + + if (size <= 0) + return (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) { + error = mb_put_mem(mbp, src, size, MB_MSYSTEM); + if (!error && lenp) + *lenp += size; + return (error); + } + + /* + * Convert to UCS-2 (really UTF-16). + * Use stack buffer if the string is + * small enough, else allocate. + */ + if (size <= SMALL_CONV) { + cbufalloc = 0; + outlen = SMALL_CONV; + cbuf = convbuf; + } else { + outlen = size; /* in utf-16 characters */ + cbufalloc = outlen * 2; + cbuf = kmem_alloc(cbufalloc, KM_SLEEP); + } + + inlen = size; + error = uconv_u8tou16((uchar_t *)src, &inlen, cbuf, &outlen, + UCONV_OUT_LITTLE_ENDIAN | UCONV_IGNORE_NULL); + outlen *= 2; /* convert to bytes */ + + if (!error) { + (void) mb_put_padbyte(mbp); /* align */ + error = mb_put_mem(mbp, (char *)cbuf, outlen, MB_MSYSTEM); + } + if (!error && lenp) + *lenp += outlen; + + if (cbufalloc) + kmem_free(cbuf, cbufalloc); + + return (error); +} + +int +smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, const char *src, + int caseopt) +{ + int error, len; + + /* + * Let smb_put_dmem put both the string + * and the terminating null. + */ + len = strlen(src) + 1; + error = smb_put_dmem(mbp, vcp, src, len, caseopt, NULL); + if (error) + return (error); + + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c new file mode 100644 index 0000000000..7a646b48c7 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.c @@ -0,0 +1,138 @@ +/* + * 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. + * + * Selected code from smb_conn.c + */ + +#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Helper functions for smb_trantcp.c + * (and maybe future transports) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> + +#include <netinet/in.h> +#include <netinet/tcp.h> + +/* Like smb_dev.h, this knows about all our sockaddr formats. */ +#include <netsmb/netbios.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_tran.h> + +/* + * Return the length of a sockaddr structure. + * Only needs to handle the address formats + * used by smb_dup_sockaddr. + */ +static size_t +SA_LEN(struct sockaddr *sa) +{ + size_t len; + + switch (sa->sa_family) { + case AF_INET: + len = sizeof (struct sockaddr_in); + break; + case AF_INET6: + len = sizeof (struct sockaddr_in6); + break; + case AF_NETBIOS: + len = sizeof (struct sockaddr_nb); + break; + default: + SMBSDEBUG("invalid address family %d\n", sa->sa_family); + len = sizeof (struct sockaddr); + break; + } + + return (len); +} + +/* + * Compare two sockaddr contents + * Return zero if identical. + */ +int +smb_cmp_sockaddr(struct sockaddr *a1, struct sockaddr *a2) +{ + size_t l1, l2; + + l1 = SA_LEN(a1); + l2 = SA_LEN(a2); + + if (l1 != l2) + return (-1); + + return (bcmp(a1, a2, l1)); +} + +/* + * Copy a socket address of varying size. + */ +struct sockaddr * +smb_dup_sockaddr(struct sockaddr *sa) +{ + struct sockaddr *sa2; + size_t len; + + /* Get the length (varies per family) */ + len = SA_LEN(sa); + + sa2 = kmem_alloc(len, KM_SLEEP); + if (sa2) + bcopy(sa, sa2, len); + + return (sa2); +} + +void +smb_free_sockaddr(struct sockaddr *sa) +{ + size_t len; + + /* Get the length (varies per family) */ + len = SA_LEN(sa); + + kmem_free(sa, len); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h new file mode 100644 index 0000000000..d82e3d862b --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h @@ -0,0 +1,99 @@ +/* + * 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. + * + * $Id: smb_tran.h,v 1.2 2001/12/21 02:41:30 conrad Exp $ + */ + +#ifndef _NETSMB_SMB_TRAN_H_ +#define _NETSMB_SMB_TRAN_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/socket.h> + +/* + * Known transports + */ +#define SMBT_NBTCP 1 + +/* + * Transport parameters + */ +#define SMBTP_SNDSZ 1 /* R - int */ +#define SMBTP_RCVSZ 2 /* R - int */ +#define SMBTP_TIMEOUT 3 /* RW - struct timespec */ +#ifndef __sun +#define SMBTP_SELECTID 4 /* RW - (void *) */ +#define SMBTP_UPCALL 5 /* RW - (* void)(void *) */ +#endif + +struct smb_tran_ops; + +struct smb_tran_desc { + sa_family_t tr_type; + int (*tr_create)(struct smb_vc *vcp, struct proc *p); + int (*tr_done)(struct smb_vc *vcp, struct proc *p); + int (*tr_bind)(struct smb_vc *vcp, struct sockaddr *sap, + struct proc *p); + int (*tr_connect)(struct smb_vc *vcp, struct sockaddr *sap, + struct proc *p); + int (*tr_disconnect)(struct smb_vc *vcp, struct proc *p); + int (*tr_send)(struct smb_vc *vcp, mblk_t *m0, struct proc *p); + int (*tr_recv)(struct smb_vc *vcp, mblk_t **mpp, struct proc *p); + int (*tr_poll)(struct smb_vc *vcp, int ticks, struct proc *p); + 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); +#ifdef notyet + int (*tr_cmpaddr)(void *addr1, void *addr2); +#endif + LIST_ENTRY(smb_tran_desc) tr_link; +}; +typedef struct smb_tran_desc smb_tran_desc_t; + +#define SMB_TRAN_CREATE(vcp, p) (vcp)->vc_tdesc->tr_create(vcp, p) +#define SMB_TRAN_DONE(vcp, p) (vcp)->vc_tdesc->tr_done(vcp, p) +#define SMB_TRAN_BIND(vcp, sap, p) (vcp)->vc_tdesc->tr_bind(vcp, sap, p) +#define SMB_TRAN_CONNECT(vcp, sap, p) (vcp)->vc_tdesc->tr_connect(vcp, sap, p) +#define SMB_TRAN_DISCONNECT(vcp, p) (vcp)->vc_tdesc->tr_disconnect(vcp, p) +#define SMB_TRAN_SEND(vcp, m0, p) (vcp)->vc_tdesc->tr_send(vcp, m0, p) +#define SMB_TRAN_RECV(vcp, m, p) (vcp)->vc_tdesc->tr_recv(vcp, m, p) +#define SMB_TRAN_POLL(vcp, t, p) (vcp)->vc_tdesc->tr_poll(vcp, t, p) +#define SMB_TRAN_GETPARAM(vcp, par, data) \ + (vcp)->vc_tdesc->tr_getparam(vcp, par, data) +#define SMB_TRAN_SETPARAM(vcp, par, data) \ + (vcp)->vc_tdesc->tr_setparam(vcp, par, data) +#define SMB_TRAN_FATAL(vcp, error) (vcp)->vc_tdesc->tr_fatal(vcp, error) + +/* Ops vectors for each transport. */ +extern struct smb_tran_desc smb_tran_nbtcp_desc; + +#endif /* _NETSMB_SMB_TRAN_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c new file mode 100644 index 0000000000..4543c65935 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c @@ -0,0 +1,1221 @@ +/* + * 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. + * + * $Id: smb_trantcp.c,v 1.39 2005/03/02 01:27:44 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/autoconf.h> +#include <sys/sysmacros.h> +#include <sys/sunddi.h> +#include <sys/kmem.h> +#include <sys/proc.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/poll.h> +#include <sys/stream.h> +#include <sys/strsubr.h> +#include <sys/strsun.h> +#include <sys/stropts.h> +#include <sys/cmn_err.h> +#include <sys/tihdr.h> +#include <sys/tiuser.h> +#include <sys/t_kuser.h> +#include <sys/priv.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/tcp.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/mchain.h> +#include <netsmb/netbios.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#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 dev_t smb_tcp_dev; + +static int nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp, + uint8_t *rpcodep, struct proc *p); +static int nb_disconnect(struct nbpcb *nbp); + +static int +nb_wait_ack(TIUSER *tiptr, t_scalar_t ack_prim, int fmode) +{ + int msgsz; + union T_primitives *pptr; + mblk_t *bp; + ptrdiff_t diff; + int error; + + /* + * wait for ack + */ + bp = NULL; + if ((error = tli_recv(tiptr, &bp, fmode)) != 0) + return (error); + + /*LINTED*/ + diff = MBLKL(bp); + ASSERT(diff == (ptrdiff_t)((int)diff)); + msgsz = (int)diff; + + if (msgsz < sizeof (int)) { + freemsg(bp); + return (EPROTO); + } + + /*LINTED*/ + pptr = (union T_primitives *)bp->b_rptr; + if (pptr->type == ack_prim) + error = 0; /* Success */ + else if (pptr->type == T_ERROR_ACK) { + if (pptr->error_ack.TLI_error == TSYSERR) + error = pptr->error_ack.UNIX_error; + else + error = t_tlitosyserr(pptr->error_ack.TLI_error); + } else + error = EPROTO; + + freemsg(bp); + return (error); +} + +/* + * Internal set sockopt for int-sized options. + * Is there a common Solaris function for this? + * Code from uts/common/rpc/clnt_cots.c + */ +static int +nb_setsockopt_int(TIUSER *tiptr, int level, int name, int val) +{ + int fmode; + mblk_t *mp; + struct opthdr *opt; + struct T_optmgmt_req *tor; + int *valp; + int error, mlen; + + mlen = (sizeof (struct T_optmgmt_req) + + sizeof (struct opthdr) + sizeof (int)); + if (!(mp = allocb_wait(mlen, BPRI_LO, STR_NOSIG, &error))) + return (error); + + mp->b_datap->db_type = M_PROTO; + /*LINTED*/ + tor = (struct T_optmgmt_req *)mp->b_wptr; + tor->PRIM_type = T_SVR4_OPTMGMT_REQ; + tor->MGMT_flags = T_NEGOTIATE; + tor->OPT_length = sizeof (struct opthdr) + sizeof (int); + tor->OPT_offset = sizeof (struct T_optmgmt_req); + mp->b_wptr += sizeof (struct T_optmgmt_req); + + /*LINTED*/ + opt = (struct opthdr *)mp->b_wptr; + opt->level = level; + opt->name = name; + opt->len = sizeof (int); + mp->b_wptr += sizeof (struct opthdr); + + /* LINTED */ + valp = (int *)mp->b_wptr; + *valp = val; + mp->b_wptr += sizeof (int); + + fmode = tiptr->fp->f_flag; + if ((error = tli_send(tiptr, mp, fmode)) != 0) + return (error); + + fmode = 0; /* need to block */ + error = nb_wait_ack(tiptr, T_OPTMGMT_ACK, fmode); + return (error); +} + +/* + * Get mblks into *mpp until the data length is at least mlen. + * Note that *mpp may already contain a fragment. + * + * If we ever have to wait more than 15 sec. to read a message, + * return ETIME. (Caller will declare the VD dead.) + */ +static int +nb_getmsg_mlen(struct nbpcb *nbp, mblk_t **mpp, size_t mlen) +{ + mblk_t *im, *tm; + union T_primitives *pptr; + size_t dlen; + int events, fmode, timo, waitflg; + int error = 0; + + /* + * Get the first message (fragment) if + * we don't already have a left-over. + */ + dlen = msgdsize(*mpp); /* *mpp==null is OK */ + while (dlen < mlen) { + + /* + * I think we still want this to return ETIME + * if nothing arrives for SMB_NBTIMO (15) sec. + * so we can report "server not responding". + * We _could_ just block here now that our + * IOD is just a reader. + */ +#if 1 + /* Wait with timeout... */ + events = 0; + waitflg = READWAIT; + timo = SEC_TO_TICK(SMB_NBTIMO); + error = t_kspoll(nbp->nbp_tiptr, timo, waitflg, &events); + if (!error && !events) + error = ETIME; + if (error) + break; + /* file mode for recv is: */ + fmode = FNDELAY; /* non-blocking */ +#else + fmode = 0; /* normal (blocking) */ +#endif + + /* Get some more... */ + tm = NULL; + error = tli_recv(nbp->nbp_tiptr, &tm, fmode); + if (error == EAGAIN) + continue; + if (error) + break; + + /* + * Normally get M_DATA messages here, + * but have to check for other types. + */ + switch (tm->b_datap->db_type) { + case M_DATA: + break; + case M_PROTO: + case M_PCPROTO: + /*LINTED*/ + pptr = (union T_primitives *)tm->b_rptr; + switch (pptr->type) { + case T_DATA_IND: + /* remove 1st mblk, keep the rest. */ + im = tm->b_cont; + tm->b_cont = NULL; + freeb(tm); + tm = im; + break; + case T_DISCON_IND: + /* Peer disconnected. */ + NBDEBUG("T_DISCON_IND: reason=%d", + pptr->discon_ind.DISCON_reason); + goto discon; + case T_ORDREL_IND: + /* Peer disconnecting. */ + NBDEBUG("T_ORDREL_IND"); + goto discon; + case T_OK_ACK: + switch (pptr->ok_ack.CORRECT_prim) { + case T_DISCON_REQ: + NBDEBUG("T_OK_ACK/T_DISCON_REQ"); + goto discon; + default: + NBDEBUG("T_OK_ACK/prim=%d", + pptr->ok_ack.CORRECT_prim); + goto discon; + } + default: + NBDEBUG("M_PROTO/type=%d", pptr->type); + goto discon; + } + break; /* M_PROTO, M_PCPROTO */ + + default: + NBDEBUG("unexpected msg type=%d", + tm->b_datap->db_type); + /*FALLTHROUGH*/ +discon: + /* + * The connection is no longer usable. + * Drop this message and disconnect. + * + * Note: nb_disconnect only does t_snddis + * on the first call, but does important + * cleanup and state change on any call. + */ + freemsg(tm); + nb_disconnect(nbp); + return (ENOTCONN); + } + + /* + * If we have a data message, append it to + * the previous chunk(s) and update dlen + */ + if (!tm) + continue; + if (*mpp == NULL) { + *mpp = tm; + } else { + /* Append */ + for (im = *mpp; im->b_cont; im = im->b_cont) + ; + im->b_cont = tm; + } + dlen += msgdsize(tm); + } + + return (error); +} + +/* + * Send a T_DISCON_REQ (disconnect) + */ +static int +nb_snddis(TIUSER *tiptr) +{ + mblk_t *mp; + struct T_discon_req *dreq; + int error, fmode, mlen; + + mlen = sizeof (struct T_discon_req); + if (!(mp = allocb_wait(mlen, BPRI_LO, STR_NOSIG, &error))) + return (error); + + mp->b_datap->db_type = M_PROTO; + /*LINTED*/ + dreq = (struct T_discon_req *)mp->b_wptr; + dreq->PRIM_type = T_DISCON_REQ; + dreq->SEQ_number = -1; + mp->b_wptr += sizeof (struct T_discon_req); + + fmode = tiptr->fp->f_flag; + if ((error = tli_send(tiptr, mp, fmode)) != 0) + return (error); + +#if 0 /* Now letting the IOD recv this. */ + fmode = 0; /* need to block */ + error = nb_wait_ack(tiptr, T_OK_ACK, fmode); +#endif + return (error); +} + +#ifdef APPLE +static int +nb_intr(struct nbpcb *nbp, struct proc *p) +{ + return (0); +} +#endif + +/* + * Stuff the NetBIOS header into space already prepended. + */ +static int +nb_sethdr(mblk_t *m, uint8_t type, uint32_t len) +{ + uint32_t *p; + + len &= 0x1FFFF; + len |= (type << 24); + + /*LINTED*/ + p = (uint32_t *)m->b_rptr; + *p = htonl(len); + return (0); +} + +/* + * Note: Moved name encoding into here. + */ +static int +nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb) +{ + int i, len; + uchar_t ch, *p; + + /* + * Do the NetBIOS "first-level encoding" here. + * (RFC1002 explains this wierdness...) + * See similar code in smbfs library: + * lib/libsmbfs/smb/nb_name.c + * + * Here is what we marshall: + * uint8_t NAME_LENGTH (always 32) + * uint8_t ENCODED_NAME[32] + * uint8_t SCOPE_LENGTH + * XXX Scope should follow here, then another null, + * if and when we support NetBIOS scopes. + */ + len = 1 + (2 * NB_NAMELEN) + 1; + + p = mb_reserve(mbp, len); + if (!p) + return (ENOSR); + + /* NAME_LENGTH */ + *p++ = (2 * NB_NAMELEN); + + /* ENCODED_NAME */ + for (i = 0; i < NB_NAMELEN; i++) { + ch = (uchar_t)snb->snb_name[i]; + *p++ = 'A' + ((ch >> 4) & 0xF); + *p++ = 'A' + ((ch) & 0xF); + } + + /* SCOPE_LENGTH */ + *p++ = 0; + + return (0); +} + +static int +nb_tcpopen(struct nbpcb *nbp, struct proc *p) +{ + TIUSER *tiptr; + int err, oflags = FREAD|FWRITE; + cred_t *cr = p->p_cred; + + if (!smb_tcp_dev) { + smb_tcp_dev = makedevice( + clone_major, ddi_name_to_major("tcp")); + } + + /* + * This magic arranges for our network endpoint + * to have the right "label" for operation in a + * "trusted extensions" environment. + */ + if (is_system_labeled()) { + cr = crdup(cr); + (void) setpflags(NET_MAC_AWARE, 1, cr); + } else { + crhold(cr); + } + err = t_kopen(NULL, smb_tcp_dev, oflags, &tiptr, cr); + crfree(cr); + if (err) + return (err); + + /* Note: I_PUSH "timod" is done by t_kopen */ + + /* Save the TPI handle we use everywhere. */ + nbp->nbp_tiptr = tiptr; + + /* + * Internal ktli calls need the "fmode" flags + * from the t_kopen call. XXX: Not sure if the + * flags have the right bits set, or if we + * always want the same block/non-block flags. + * XXX: Look into this... + */ + nbp->nbp_fmode = tiptr->fp->f_flag; + return (0); +} + +/*ARGSUSED*/ +static int +nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct proc *p) +{ + int error; + TIUSER *tiptr = NULL; + struct t_call call; + + tiptr = nbp->nbp_tiptr; + if (tiptr == NULL) + return (EBADF); + if (nbp->nbp_flags & NBF_CONNECTED) + return (EISCONN); + + /* + * Set various socket/TCP options. + * Failures here are not fatal - + * just log a complaint. + * + * We don't need these two: + * SO_RCVTIMEO, SO_SNDTIMEO + */ + + error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_SNDBUF, + nbp->nbp_sndbuf); + if (error) + NBDEBUG("nb_connect_in: set SO_SNDBUF"); + + error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_RCVBUF, + nbp->nbp_rcvbuf); + if (error) + NBDEBUG("nb_connect_in: set SO_RCVBUF"); + + error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_KEEPALIVE, 1); + if (error) + NBDEBUG("nb_connect_in: set SO_KEEPALIVE"); + + error = nb_setsockopt_int(tiptr, IPPROTO_TCP, TCP_NODELAY, 1); + if (error) + NBDEBUG("nb_connect_in: set TCP_NODELAY"); + + /* Do local bind (any address) */ + if ((error = t_kbind(tiptr, NULL, NULL)) != 0) { + NBDEBUG("nb_connect_in: bind local"); + return (error); + } + + /* + * Setup (snd)call address (connect to). + * Just pass NULL for the (rcv)call. + */ + bzero(&call, sizeof (call)); + call.addr.len = sizeof (*to); + call.addr.buf = (char *)to; + /* call.opt - none */ + /* call.udata -- XXX: Should put NB session req here! */ + + /* Send the connect, wait... */ + error = t_kconnect(tiptr, &call, NULL); + if (error) { + NBDEBUG("nb_connect_in: connect %d error", error); + /* + * XXX: t_kconnect returning EPROTO here instead of ETIMEDOUT + * here. Temporarily return ETIMEDOUT error if we get EPROTO. + */ + if (error == EPROTO) + error = ETIMEDOUT; + } else { + mutex_enter(&nbp->nbp_lock); + nbp->nbp_flags |= NBF_CONNECTED; + mutex_exit(&nbp->nbp_lock); + } + + return (error); +} + +static int +nbssn_rq_request(struct nbpcb *nbp, struct proc *p) +{ + struct mbchain mb, *mbp = &mb; + struct mdchain md, *mdp = &md; + mblk_t *m0; + struct sockaddr_in sin; + ushort_t port; + uint8_t rpcode; + int error, rplen; + + error = mb_init(mbp); + if (error) + return (error); + + /* + * Put a zero for the 4-byte NetBIOS header, + * then let nb_sethdr() overwrite it. + */ + mb_put_uint32le(mbp, 0); + nb_put_name(mbp, nbp->nbp_paddr); + nb_put_name(mbp, nbp->nbp_laddr); + nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4); + + m0 = mb_detach(mbp); + error = tli_send(nbp->nbp_tiptr, m0, nbp->nbp_fmode); + m0 = NULL; /* Note: _always_ consumed by tli_send */ + mb_done(mbp); + if (error) + return (error); + + nbp->nbp_state = NBST_RQSENT; + error = nbssn_recv(nbp, &m0, &rplen, &rpcode, p); + if (error == EWOULDBLOCK) { /* Timeout */ + NBDEBUG("initial request timeout\n"); + return (ETIMEDOUT); + } + if (error) { + NBDEBUG("recv() error %d\n", error); + return (error); + } + /* + * Process NETBIOS reply + */ + if (m0) + md_initm(mdp, m0); + + error = 0; + if (rpcode == NB_SSN_POSRESP) { + mutex_enter(&nbp->nbp_lock); + nbp->nbp_state = NBST_SESSION; + mutex_exit(&nbp->nbp_lock); + goto out; + } + if (rpcode != NB_SSN_RTGRESP) { + error = ECONNABORTED; + goto out; + } + if (rplen != 6) { + error = ECONNABORTED; + goto out; + } + md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM); + md_get_uint16(mdp, &port); + sin.sin_port = port; + nbp->nbp_state = NBST_RETARGET; + nb_disconnect(nbp); + error = nb_connect_in(nbp, &sin, p); + if (!error) + error = nbssn_rq_request(nbp, p); + if (error) { + nb_disconnect(nbp); + } + +out: + if (m0) + md_done(mdp); + return (error); +} + +/* + * Wait for up to 15 sec. for the next packet. + * Often return ETIME and do nothing else. + * When a packet header is available, check + * the header and get the length, but don't + * consume it. No side effects here except + * for the pullupmsg call. + */ +static int +nbssn_peekhdr(struct nbpcb *nbp, size_t *lenp, uint8_t *rpcodep) +{ + uint32_t len, *hdr; + int error; + + /* + * Get the first message (fragment) if + * we don't already have a left-over. + */ + error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, sizeof (len)); + if (error) + return (error); + + if (!pullupmsg(nbp->nbp_frag, sizeof (len))) + return (ENOSR); + + /* + * Check the NetBIOS header. + * (NOT consumed here) + */ + /*LINTED*/ + hdr = (uint32_t *)nbp->nbp_frag->b_rptr; + + len = ntohl(*hdr); + if ((len >> 16) & 0xFE) { + NBDEBUG("bad nb header received 0x%x (MBZ flag set)\n", len); + return (EPIPE); + } + *rpcodep = (len >> 24) & 0xFF; + switch (*rpcodep) { + case NB_SSN_MESSAGE: + case NB_SSN_REQUEST: + case NB_SSN_POSRESP: + case NB_SSN_NEGRESP: + case NB_SSN_RTGRESP: + case NB_SSN_KEEPALIVE: + break; + default: + NBDEBUG("bad nb header received 0x%x (bogus type)\n", len); + return (EPIPE); + } + len &= 0x1ffff; + if (len > SMB_MAXPKTLEN) { + NBDEBUG("packet too long (%d)\n", len); + return (EFBIG); + } + *lenp = len; + return (0); +} + +/* + * Receive a NetBIOS message. This may block to wait for the entire + * message to arrive. The caller knows there is (or should be) a + * message to be read. When we receive and drop a keepalive or + * zero-length message, return EAGAIN so the caller knows that + * something was received. This avoids false triggering of the + * "server not responding" state machine. + */ +/*ARGSUSED*/ +static int +nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp, + uint8_t *rpcodep, struct proc *p) +{ + TIUSER *tiptr = nbp->nbp_tiptr; + mblk_t *m0; + uint8_t rpcode; + int error; + size_t rlen, len; + + /* We should be the only reader. */ + ASSERT(nbp->nbp_flags & NBF_RECVLOCK); + + if (tiptr == NULL) + return (EBADF); + if (mpp) { + if (*mpp) { + NBDEBUG("*mpp not 0 - leak?"); + } + *mpp = NULL; + } + m0 = NULL; + + /* + * Get the NetBIOS header (not consumed yet) + */ + error = nbssn_peekhdr(nbp, &len, &rpcode); + if (error) { + if (error != ETIME) + NBDEBUG("peekhdr, error=%d\n", error); + return (error); + } + NBDEBUG("Have pkt, type=0x%x len=0x%x\n", + (int)rpcode, (int)len); + + /* + * Block here waiting for the whole packet to arrive. + * If we get a timeout, return without side effects. + * The data length we wait for here includes both the + * NetBIOS header and the payload. + */ + error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, len + 4); + if (error) { + NBDEBUG("getmsg(body), error=%d\n", error); + return (error); + } + + /* + * We now have an entire NetBIOS message. + * Trim off the NetBIOS header and consume it. + * Note: _peekhdr has done pullupmsg for us, + * so we know it's safe to advance b_rptr. + */ + m0 = nbp->nbp_frag; + m0->b_rptr += 4; + + /* + * There may be more data after the message + * we're about to return, in which case we + * split it and leave the remainder. + */ + rlen = msgdsize(m0); + ASSERT(rlen >= len); + nbp->nbp_frag = NULL; + if (rlen > len) + nbp->nbp_frag = m_split(m0, len, 1); + + if (nbp->nbp_state != NBST_SESSION) { + /* + * No session is established. + * Return whatever packet we got. + */ + goto out; + } + + /* + * A session is established; the only packets + * we should see are session message and + * keep-alive packets. Drop anything else. + */ + switch (rpcode) { + + case NB_SSN_KEEPALIVE: + /* + * It's a keepalive. Discard any data in it + * (there's not supposed to be any, but that + * doesn't mean some server won't send some) + */ + if (len) + NBDEBUG("Keepalive with data %d\n", (int)len); + error = EAGAIN; + break; + + case NB_SSN_MESSAGE: + /* + * Session message. Does it have any data? + */ + if (len == 0) { + /* + * No data - treat as keepalive (drop). + */ + error = EAGAIN; + break; + } + /* + * Yes, has data. Return it. + */ + error = 0; + break; + + default: + /* + * Drop anything else. + */ + NBDEBUG("non-session packet %x\n", rpcode); + error = EAGAIN; + break; + } + +out: + if (error) { + if (m0) + m_freem(m0); + return (error); + } + if (mpp) + *mpp = m0; + else + m_freem(m0); + *lenp = (int)len; + *rpcodep = rpcode; + return (0); +} + +/* + * SMB transport interface + */ +static int +smb_nbst_create(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp; + int error; + + nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP); + + /* + * We don't keep reference counts or otherwise + * prevent nbp->nbp_tiptr from going away, so + * do the TLI open here and keep it until the + * last ref calls smb_nbst_done. + * This does t_kopen (open endpoint) + */ + error = nb_tcpopen(nbp, p); + if (error) { + kmem_free(nbp, sizeof (*nbp)); + return (error); + } + + nbp->nbp_timo.tv_sec = SMB_NBTIMO; + nbp->nbp_state = NBST_CLOSED; /* really IDLE */ + nbp->nbp_vc = vcp; + nbp->nbp_sndbuf = smb_tcpsndbuf; + nbp->nbp_rcvbuf = smb_tcprcvbuf; + mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL); + vcp->vc_tdata = nbp; + return (0); +} + +/*ARGSUSED*/ +static int +smb_nbst_done(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + if (nbp == NULL) + return (ENOTCONN); + vcp->vc_tdata = NULL; + + /* + * Don't really need to disconnect here, + * because the close following will do it. + * But it's harmless. + */ + if (nbp->nbp_flags & NBF_CONNECTED) + nb_disconnect(nbp); + if (nbp->nbp_tiptr) + t_kclose(nbp->nbp_tiptr, 1); + if (nbp->nbp_laddr) + smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr); + if (nbp->nbp_paddr) + smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr); + mutex_destroy(&nbp->nbp_lock); + kmem_free(nbp, sizeof (*nbp)); + return (0); +} + +/*ARGSUSED*/ +static int +smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + struct sockaddr_nb *snb; + int error; + + NBDEBUG("\n"); + error = EINVAL; + + if (nbp->nbp_flags & NBF_LOCADDR) + goto out; + + /* + * Null name is an "anonymous" (NULL) bind request. + * (Let the transport pick a local name.) + * This transport does not support NULL bind. + */ + if (sap == NULL) + goto out; + + /*LINTED*/ + snb = (struct sockaddr_nb *)smb_dup_sockaddr(sap); + if (snb == NULL) { + error = ENOMEM; + goto out; + } + mutex_enter(&nbp->nbp_lock); + nbp->nbp_laddr = snb; + nbp->nbp_flags |= NBF_LOCADDR; + mutex_exit(&nbp->nbp_lock); + error = 0; + +out: + return (error); +} + +static int +smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + struct sockaddr_in sin; + struct sockaddr_nb *snb; + struct timespec ts1, ts2; + int error; + + NBDEBUG("\n"); + if (nbp->nbp_tiptr == NULL) + return (EBADF); + if (nbp->nbp_laddr == NULL) + return (EINVAL); + + /* + * Note: nbssn_rq_request() will call nbssn_recv(), + * so set the RECVLOCK flag here. Otherwise we'll + * hit an ASSERT for this flag in nbssn_recv(). + */ + mutex_enter(&nbp->nbp_lock); + if (nbp->nbp_flags & NBF_RECVLOCK) { + NBDEBUG("attempt to reenter session layer!\n"); + mutex_exit(&nbp->nbp_lock); + return (EWOULDBLOCK); + } + nbp->nbp_flags |= NBF_RECVLOCK; + mutex_exit(&nbp->nbp_lock); + + /*LINTED*/ + snb = (struct sockaddr_nb *)smb_dup_sockaddr(sap); + if (snb == NULL) { + error = ENOMEM; + goto out; + } + if (nbp->nbp_paddr) + smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr); + nbp->nbp_paddr = snb; + + /* Setup the remote IP address. */ + bzero(&sin, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(SMB_TCP_PORT); + sin.sin_addr.s_addr = snb->snb_ipaddr; + + /* + * For our general timeout we use the greater of + * the default (15 sec) and 4 times the time it + * took for the first round trip. We used to use + * just the latter, but sometimes if the first + * round trip is very fast the subsequent 4 sec + * timeouts are simply too short. + */ + gethrestime(&ts1); + error = nb_connect_in(nbp, &sin, p); + if (error) + goto out; + gethrestime(&ts2); + timespecsub(&ts2, &ts1); + timespecadd(&ts2, &ts2); + timespecadd(&ts2, &ts2); /* * 4 */ + /*CSTYLED*/ + if (timespeccmp(&ts2, (&(nbp->nbp_timo)), >)) + nbp->nbp_timo = ts2; + error = nbssn_rq_request(nbp, p); + if (error) + nb_disconnect(nbp); +out: + mutex_enter(&nbp->nbp_lock); + nbp->nbp_flags &= ~NBF_RECVLOCK; + mutex_exit(&nbp->nbp_lock); + + return (error); +} + +/*ARGSUSED*/ +static int +smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + if (nbp == NULL) + return (ENOTCONN); + + return (nb_disconnect(nbp)); +} + +static int +nb_disconnect(struct nbpcb *nbp) +{ + TIUSER *tiptr; + int save_flags; + + tiptr = nbp->nbp_tiptr; + if (tiptr == NULL) + return (EBADF); + + mutex_enter(&nbp->nbp_lock); + save_flags = nbp->nbp_flags; + nbp->nbp_flags &= ~NBF_CONNECTED; + if (nbp->nbp_frag) { + freemsg(nbp->nbp_frag); + nbp->nbp_frag = NULL; + } + mutex_exit(&nbp->nbp_lock); + + if (save_flags & NBF_CONNECTED) + nb_snddis(tiptr); + + if (nbp->nbp_state != NBST_RETARGET) { + nbp->nbp_state = NBST_CLOSED; /* really IDLE */ + } + return (0); +} + +/* + * Always consume the message. + * (On error too!) + */ +/*ARGSUSED*/ +static int +smb_nbst_send(struct smb_vc *vcp, mblk_t *m, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + ptrdiff_t diff; + uint32_t mlen; + int error; + + if (nbp == NULL || nbp->nbp_tiptr == NULL) { + error = EBADF; + goto errout; + } + + /* + * Get the message length, which + * does NOT include the NetBIOS header + */ + mlen = msgdsize(m); + + /* + * Normally, mb_init() will have left space + * for us to prepend the NetBIOS header in + * the data block of the first mblk. + * However, we have to check in case other + * code did not leave this space, or if the + * message is from dupmsg (db_ref > 1) + * + * If don't find room in the first data block, + * we have to allocb a new message and link it + * on the front of the chain. We try not to + * do this becuase it's less efficient. Also, + * some network drivers will apparently send + * each mblk in the chain as separate frames. + * (That's arguably a driver bug.) + */ + + /* LINTED */ + diff = MBLKHEAD(m); + if (diff == 4 && DB_REF(m) == 1) { + /* We can use the first dblk. */ + m->b_rptr -= 4; + } else { + /* Link a new mblk on the head. */ + mblk_t *m0; + + /* M_PREPEND */ + m0 = allocb_wait(4, BPRI_LO, STR_NOSIG, &error); + if (!m0) + goto errout; + + m0->b_wptr += 4; + m0->b_cont = m; + m = m0; + } + + nb_sethdr(m, NB_SSN_MESSAGE, mlen); + error = tli_send(nbp->nbp_tiptr, m, 0); + return (error); + +errout: + if (m) + m_freem(m); + return (error); +} + + +static int +smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp, struct proc *p) +{ + struct nbpcb *nbp = vcp->vc_tdata; + uint8_t rpcode; + int error, rplen; + + mutex_enter(&nbp->nbp_lock); + if (nbp->nbp_flags & NBF_RECVLOCK) { + NBDEBUG("attempt to reenter session layer!\n"); + mutex_exit(&nbp->nbp_lock); + return (EWOULDBLOCK); + } + nbp->nbp_flags |= NBF_RECVLOCK; + mutex_exit(&nbp->nbp_lock); + error = nbssn_recv(nbp, mpp, &rplen, &rpcode, p); + mutex_enter(&nbp->nbp_lock); + nbp->nbp_flags &= ~NBF_RECVLOCK; + mutex_exit(&nbp->nbp_lock); + return (error); +} + +/* + * Wait for up to "ticks" clock ticks for input on vcp. + * Returns zero if input is available, otherwise ETIME + * indicating time expired, or other error codes. + */ +/*ARGSUSED*/ +static int +smb_nbst_poll(struct smb_vc *vcp, int ticks, struct proc *p) +{ + int error; + int events = 0; + int waitflg = READWAIT; + struct nbpcb *nbp = vcp->vc_tdata; + + error = t_kspoll(nbp->nbp_tiptr, ticks, waitflg, &events); + if (!error && !events) + error = ETIME; + + return (error); +} + +static int +smb_nbst_getparam(struct smb_vc *vcp, int param, void *data) +{ + struct nbpcb *nbp = vcp->vc_tdata; + + switch (param) { + case SMBTP_SNDSZ: + *(int *)data = nbp->nbp_sndbuf; + 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; + break; +#endif +#ifdef SMBTP_UPCALL + case SMBTP_UPCALL: + *(void **)data = nbp->nbp_upcall; + break; +#endif + default: + return (EINVAL); + } + return (0); +} + +/*ARGSUSED*/ +static int +smb_nbst_setparam(struct smb_vc *vcp, int param, void *data) +{ + return (EINVAL); +} + +/* + * Check for fatal errors + */ +/*ARGSUSED*/ +static int +smb_nbst_fatal(struct smb_vc *vcp, int error) +{ + switch (error) { + case ENOTCONN: + case ENETRESET: + case ECONNABORTED: + case EPIPE: + return (1); + } + return (0); +} + + +struct smb_tran_desc smb_tran_nbtcp_desc = { + SMBT_NBTCP, + smb_nbst_create, + smb_nbst_done, + smb_nbst_bind, + smb_nbst_connect, + smb_nbst_disconnect, + smb_nbst_send, + smb_nbst_recv, + smb_nbst_poll, + smb_nbst_getparam, + smb_nbst_setparam, + smb_nbst_fatal, + {NULL, NULL} +}; diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h new file mode 100644 index 0000000000..80ebdc8dd9 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.h @@ -0,0 +1,91 @@ +/* + * 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. + * + * $Id: smb_trantcp.h,v 1.8 2004/08/03 23:50:01 lindak Exp $ + */ +#ifndef _NETSMB_SMB_TRANTCP_H_ +#define _NETSMB_SMB_TRANTCP_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +enum nbstate { + NBST_CLOSED, + NBST_RQSENT, + NBST_SESSION, + NBST_RETARGET, + NBST_REFUSED +}; + + +/* + * socket specific data + */ +struct nbpcb { + struct smb_vc *nbp_vc; + struct tiuser *nbp_tiptr; /* KTLI transport handle... */ + ushort_t nbp_fmode; + mblk_t *nbp_frag; /* left-over from last recv */ + + struct sockaddr_nb *nbp_laddr; /* local address */ + struct sockaddr_nb *nbp_paddr; /* peer address */ + + int nbp_flags; +#define NBF_LOCADDR 0x0001 /* has local addr */ +#define NBF_CONNECTED 0x0002 +#define NBF_RECVLOCK 0x0004 +#define NBF_UPCALLED 0x0010 + + enum nbstate nbp_state; + struct timespec nbp_timo; + int nbp_sndbuf; + int nbp_rcvbuf; + void *nbp_selectid; + kmutex_t nbp_lock; +}; +typedef struct nbpcb nbpcb_t; + +/* + * Nominal space allocated per a NETBIOS socket. + */ +#define NB_SNDQ (10 * 1024) +#define NB_RCVQ (20 * 1024) + +/* + * TCP slowstart presents a problem in conjunction with large + * reads. To ensure a steady stream of ACKs while reading using + * large transaction sizes, we call soreceive() with a smaller + * buffer size. See nbssn_recv(). + */ +#define NB_SORECEIVE_CHUNK (8 * 1024) + +#define SMBSBTIMO 15 /* seconds for sockbuf timeouts */ + +#endif /* !_NETSMB_SMB_TRANTCP_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c new file mode 100644 index 0000000000..e8af12ba20 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c @@ -0,0 +1,513 @@ +/* + * 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. + * + * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $ + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/kmem.h> +#include <sys/systm.h> +#include <sys/policy.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/cmn_err.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#include <sys/smb_iconv.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_rq.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> + +/* + * helpers for nsmb device. Can be moved to the smb_dev.c file. + */ +static void smb_usr_vcspec_free(struct smb_vcspec *spec); + +/* + * Moved the access checks here, just becuase + * this was a more convenient place to do it + * than in every function calling this. + */ +static int +smb_usr_ioc2vcspec(struct smbioc_ossn *dp, struct smb_vcspec *spec) +{ + cred_t *cr = CRED(); + uid_t realuid; + + /* + * Only superuser can specify a UID or GID. + */ + realuid = crgetruid(cr); + if (dp->ioc_owner == SMBM_ANY_OWNER) + spec->owner = realuid; + else { + /* + * Do we have the privilege to create with the + * specified uid? (does uid == cr->cr_uid, etc.) + * MacOS would want suser(), or similar here. + */ + if (secpolicy_vnode_owner(cr, dp->ioc_owner)) + return (EPERM); + spec->owner = dp->ioc_owner; + } + if (dp->ioc_group == SMBM_ANY_GROUP) + spec->group = crgetgid(cr); + else { + /* + * Do we have the privilege to create with the + * specified gid? (one of our groups?) + */ + if (groupmember(dp->ioc_group, cr) || + secpolicy_vnode_create_gid(cr) == 0) + spec->group = dp->ioc_group; + else + return (EPERM); + } + + /* + * Valid codesets? XXX + */ + if (dp->ioc_localcs[0] == 0) { + spec->localcs = "ISO8859-1"; +#ifdef NOTYETRESOLVED + SMBERROR("no local charset ? dp->ioc_localcs[0]: %d\n", + dp->ioc_localcs[0]); + return (EINVAL); +#endif + } else + spec->localcs = spec->localcs; + + /* + * Check for valid sa_family. + * XXX: Just NetBIOS for now. + */ + if (dp->ioc_server.sa.sa_family != AF_NETBIOS) + return (EINVAL); + spec->sap = &dp->ioc_server.sa; + + if (dp->ioc_local.sa.sa_family) { + /* If specified, local AF must be the same. */ + if (dp->ioc_local.sa.sa_family != + dp->ioc_server.sa.sa_family) + return (EINVAL); + spec->lap = &dp->ioc_local.sa; + } + + if (dp->ioc_intok) { + spec->tok = smb_memdupin(dp->ioc_intok, dp->ioc_intoklen); + if (spec->tok == NULL) + return (EFAULT); + spec->toklen = dp->ioc_intoklen; + } + + spec->srvname = dp->ioc_srvname; + spec->pass = dp->ioc_password; + spec->domain = dp->ioc_workgroup; + spec->username = dp->ioc_user; + spec->mode = dp->ioc_mode; + spec->rights = dp->ioc_rights; + spec->servercs = dp->ioc_servercs; + spec->optflags = dp->ioc_opt; + + return (0); +} + +static void +smb_usr_shspec_free(struct smb_sharespec *sspec) +{ + kmem_free(sspec, sizeof (struct smb_sharespec)); +} + +static void +smb_usr_vcspec_free(struct smb_vcspec *spec) +{ + + if (spec->tok) { + kmem_free(spec->tok, spec->toklen); + } + kmem_free(spec, sizeof (*spec)); +} + +static int +smb_usr_ioc2sharespec(struct smbioc_oshare *dp, struct smb_sharespec *spec) +{ + bzero(spec, sizeof (*spec)); + spec->name = dp->ioc_share; + spec->pass = dp->ioc_password; + spec->mode = dp->ioc_mode; + spec->rights = dp->ioc_rights; + spec->owner = dp->ioc_owner; + spec->group = dp->ioc_group; + spec->stype = dp->ioc_stype; + spec->optflags = dp->ioc_opt; + return (0); +} + +int +smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc **vcpp) +{ + struct smb_vc *vcp = NULL; + struct smb_vcspec *vspec = NULL; + struct smb_sharespec *sspecp = NULL; + int error = 0; + + if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE) + return (EINVAL); + vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP); + error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec); + if (error) + return (error); + if (dp->ioc_flags & SMBLK_CREATE) + vspec->optflags |= SMBVOPT_CREATE; + if (dp->ioc_level >= SMBL_SHARE) { + sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP); + error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp); + if (error) + goto out; + } + error = smb_sm_negotiate(vspec, scred, &vcp); + if (error == 0) { + *vcpp = vcp; + /* + * Used to copyout ioc_outtok, outtoklen here, + * but that's now in smb_dev. (our caller) + * + * If this call asked for extended security and + * the server does not support it, clear the + * flag so the caller knows this. + * + * XXX: Should just add sv_caps to ioc_ssn, + * set the new sv_caps field here, and let + * let the copyout of ioc_ssn handle it. + */ + if (!(vcp->vc_sopt.sv_caps & SMB_CAP_EXT_SECURITY) && + (dp->ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC)) { + dp->ioc_ssn.ioc_opt &= ~SMBVOPT_EXT_SEC; + SMBSDEBUG("turned off extended security"); + } + } +out: + smb_usr_vcspec_free(vspec); + smb_usr_shspec_free(sspecp); + return (error); +} + +int +smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc *vcp) +{ + struct smb_vcspec *vspec = NULL; + int error; + + if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE) + return (EINVAL); + + vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP); + error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec); + if (error) + goto out; + + error = smb_sm_ssnsetup(vspec, scred, vcp); + /* + * Moved the copyout of ioc_outtok to + * smb_dev.c (our caller) + */ + +out: + smb_usr_vcspec_free(vspec); + return (error); +} + + +int +smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc *vcp, struct smb_share **sspp) +{ + struct smb_sharespec *sspecp = NULL; + int error; + + if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE) + return (EINVAL); + + if (dp->ioc_level >= SMBL_SHARE) { + sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP); + error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp); + if (error) + goto out; + } + error = smb_sm_tcon(sspecp, scred, vcp, sspp); + +out: + if (sspecp) + smb_usr_shspec_free(sspecp); + + return (error); +} + +/* + * Connect to the resource specified by smbioc_ossn structure. + * It may either find an existing connection or try to establish a new one. + * If no errors occured smb_vc returned locked and referenced. + */ + +int +smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp, + struct smb_cred *scred) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + struct mdchain *mdp; + char *p; + size_t wc2; + u_int8_t wc; + u_int16_t bc; + int error; + + switch (dp->ioc_cmd) { + case SMB_COM_TRANSACTION2: + case SMB_COM_TRANSACTION2_SECONDARY: + case SMB_COM_CLOSE_AND_TREE_DISC: + case SMB_COM_TREE_CONNECT: + case SMB_COM_TREE_DISCONNECT: + case SMB_COM_NEGOTIATE: + case SMB_COM_SESSION_SETUP_ANDX: + case SMB_COM_LOGOFF_ANDX: + case SMB_COM_TREE_CONNECT_ANDX: + return (EPERM); + } + error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred); + if (error) + return (error); + mbp = &rqp->sr_rq; + smb_rq_wstart(rqp); + error = mb_put_mem(mbp, dp->ioc_twords, + dp->ioc_twc * 2, MB_MUSER); + if (error) + goto bad; + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + error = mb_put_mem(mbp, dp->ioc_tbytes, + dp->ioc_tbc, MB_MUSER); + if (error) + goto bad; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + goto bad; + mdp = &rqp->sr_rp; + md_get_uint8(mdp, &wc); + dp->ioc_rwc = wc; + wc2 = wc * 2; + if (wc2 > dp->ioc_rpbufsz) { + error = EBADRPC; + goto bad; + } + error = md_get_mem(mdp, dp->ioc_rpbuf, wc2, MB_MUSER); + if (error) + goto bad; + md_get_uint16le(mdp, &bc); + if ((wc2 + bc) > dp->ioc_rpbufsz) { + error = EBADRPC; + goto bad; + } + dp->ioc_rbc = bc; + p = dp->ioc_rpbuf; + error = md_get_mem(mdp, p + wc2, bc, MB_MUSER); +bad: + dp->ioc_errclass = rqp->sr_errclass; + dp->ioc_serror = rqp->sr_serror; + dp->ioc_error = rqp->sr_error; + smb_rq_done(rqp); + return (error); + +} + +static int +smb_cpdatain(struct mbchain *mbp, int len, char *data) +{ + int error; + + if (len == 0) + return (0); + error = mb_init(mbp); + if (error) + return (error); + return (mb_put_mem(mbp, data, len, MB_MUSER)); +} + +int +smb_usr_t2request(struct smb_share *ssp, smbioc_t2rq_t *dp, + struct smb_cred *scred) +{ + struct smb_t2rq t2, *t2p = &t2; + struct mdchain *mdp; + int error, len; + + if (dp->ioc_setupcnt > SMB_MAXSETUPWORDS) + return (EINVAL); + + t2p = (struct smb_t2rq *)kmem_alloc(sizeof (struct smb_t2rq), KM_SLEEP); + if (t2p == NULL) + return (ENOMEM); + error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup, dp->ioc_setupcnt, + scred); + if (error) + return (error); + len = t2p->t2_setupcount = dp->ioc_setupcnt; + if (len > 1) + t2p->t2_setupdata = dp->ioc_setup; + if (dp->ioc_name) { + bcopy(dp->ioc_name, t2p->t_name, 128); + if (t2p->t_name == NULL) { + error = ENOMEM; + goto bad; + } + } + t2p->t2_maxscount = 0; + t2p->t2_maxpcount = dp->ioc_rparamcnt; + t2p->t2_maxdcount = dp->ioc_rdatacnt; + error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt, + dp->ioc_tparam); + if (error) + goto bad; + error = smb_cpdatain(&t2p->t2_tdata, + dp->ioc_tdatacnt, dp->ioc_tdata); + if (error) + goto bad; + error = smb_t2_request(t2p); + dp->ioc_errclass = t2p->t2_sr_errclass; + dp->ioc_serror = t2p->t2_sr_serror; + dp->ioc_error = t2p->t2_sr_error; + dp->ioc_rpflags2 = t2p->t2_sr_rpflags2; + if (error) + goto bad; + mdp = &t2p->t2_rparam; + if (mdp->md_top) { + mblk_t *m = mdp->md_top; +#ifdef lint + m = m; +#endif + len = m_fixhdr(mdp->md_top); + if (len > dp->ioc_rparamcnt) { + error = EMSGSIZE; + goto bad; + } + dp->ioc_rparamcnt = (ushort_t)len; + error = md_get_mem(mdp, dp->ioc_rparam, + len, MB_MUSER); + if (error) { + goto bad; + } + } else + dp->ioc_rparamcnt = 0; + mdp = &t2p->t2_rdata; + if (mdp->md_top) { + mblk_t *m = mdp->md_top; +#ifdef lint + m = m; +#endif + len = m_fixhdr(mdp->md_top); + if (len > dp->ioc_rdatacnt) { + error = EMSGSIZE; + goto bad; + } + dp->ioc_rdatacnt = (ushort_t)len; + error = md_get_mem(mdp, dp->ioc_rdata, + len, MB_MUSER); + if (error) { + goto bad; + } + } else + dp->ioc_rdatacnt = 0; +bad: + smb_t2_done(t2p); + return (error); +} + +/* + * Helper for nsmb_ioctl cases + * SMBIOC_READ, SMBIOC_WRITE + */ +int +smb_usr_rw(struct smb_share *ssp, smbioc_rw_t *rwrq, + int cmd, struct smb_cred *scred) +{ + struct iovec aiov[1]; + struct uio auio; + u_int16_t fh; + int error; + uio_rw_t rw; + + switch (cmd) { + case SMBIOC_READ: + rw = UIO_READ; + break; + case SMBIOC_WRITE: + rw = UIO_WRITE; + break; + default: + return (ENODEV); + } + + fh = htoles(rwrq->ioc_fh); + + aiov[0].iov_base = rwrq->ioc_base; + aiov[0].iov_len = (size_t)rwrq->ioc_cnt; + + auio.uio_iov = aiov; + auio.uio_iovcnt = 1; + auio.uio_loffset = rwrq->ioc_offset; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_fmode = 0; + auio.uio_resid = (size_t)rwrq->ioc_cnt; + + error = smb_rwuio(ssp, fh, rw, &auio, scred, 0); + + /* + * On return ioc_cnt holds the + * number of bytes transferred. + */ + rwrq->ioc_cnt -= auio.uio_resid; + + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c new file mode 100644 index 0000000000..8ead47d2a5 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c @@ -0,0 +1,990 @@ +/* + * 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. + * + * $FreeBSD: src/sys/kern/subr_mchain.c,v 1.1 2001/02/24 15:44:29 bp Exp $ + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/uio.h> +#include <sys/types.h> +#include <sys/stream.h> +#include <sys/strsun.h> +#include <sys/strsubr.h> +#include <sys/cmn_err.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/mchain.h> + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> + +/* BEGIN CSTYLED */ +/* + * BSD-style mbufs, vs SysV-style mblks: + * One big difference: the mbuf payload is: + * 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 + * m->m_data m->b_rptr + * m->m_len MBLKL(m) + * m->m_dat[] m->b_datap->db_base + * &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. + */ +/* END CSTYLED */ + + +/* + * + * MODULE_VERSION(libmchain, 1); + */ + +#ifdef __GNUC__ +#define MBERROR(format, args...) printf("%s(%d): "format, \ + __FUNCTION__, __LINE__, ## args) +#define MBPANIC(format, args...) printf("%s(%d): "format, \ + __FUNCTION__, __LINE__, ## args) +#else +#define MBERROR(...) \ + smb_errmsg(CE_NOTE, __func__, __VA_ARGS__) +#define MBPANIC(...) \ + smb_errmsg(CE_PANIC, __func__, __VA_ARGS__) +#endif + +/* + * MLEN: The smallest mblk we'll allocate. + * + * There's more to MLEN than you might think. + * Some ethernet drivers may send each mblk as a + * separate frame, so we want MLEN at least 1K. + * We could have used 1K here, but that might + * hurt transports that support larger frames. + * 4K fits nicely in 3 Ethernet frames (3 * 1500) + * leaving about 500 bytes for protocol headers. + * + * XXX: Would Ethernet drivers be happier + * (more efficient) if we used 1K here? + */ +#define MLEN 4096 + + +/* + * Some UIO routines. + * Taken from Darwin Sourcecs. + */ + +/* + * uio_isuserspace - return non zero value if the address space + * flag is for a user address space (could be 32 or 64 bit). + */ +int +uio_isuserspace(uio_t *a_uio) +{ + if (a_uio->uio_segflg == UIO_USERSPACE) { + return (1); + } + return (0); +} + +/* + * uio_curriovbase - return the base address of the current iovec associated + * with the given uio_t. May return 0. + */ +caddr_t +uio_curriovbase(uio_t *a_uio) +{ + if (a_uio->uio_iovcnt < 1) { + return (0); + } + return ((caddr_t)((uintptr_t)a_uio->uio_iov->iov_base)); +} + +/* + * uio_curriovlen - return the length value of the current iovec associated + * with the given uio_t. + */ +size_t +uio_curriovlen(uio_t *a_uio) +{ + if (a_uio->uio_iovcnt < 1) { + return (0); + } + return ((size_t)a_uio->uio_iov->iov_len); +} + + +/* + * uio_update - update the given uio_t for a_count of completed IO. + * This call decrements the current iovec length and residual IO value + * and increments the current iovec base address and offset value. + * If the current iovec length is 0 then advance to the next + * iovec (if any). + * If the a_count passed in is 0, than only do the advancement + * over any 0 length iovec's. + */ +void +uio_update(uio_t *a_uio, size_t a_count) +{ + if (a_uio->uio_iovcnt < 1) { + return; + } + + /* + * if a_count == 0, then we are asking to skip over + * any empty iovs + */ + if (a_count) { + if (a_count > a_uio->uio_iov->iov_len) { + a_uio->uio_iov->iov_base += a_uio->uio_iov->iov_len; + a_uio->uio_iov->iov_len = 0; + } else { + a_uio->uio_iov->iov_base += a_count; + a_uio->uio_iov->iov_len -= a_count; + } + if (a_uio->uio_resid < 0) { + a_uio->uio_resid = 0; + } + if (a_count > (size_t)a_uio->uio_resid) { + a_uio->uio_offset += a_uio->uio_resid; + a_uio->uio_resid = 0; + } else { + a_uio->uio_offset += a_count; + a_uio->uio_resid -= a_count; + } + } + /* + * advance to next iovec if current one is totally consumed + */ + while (a_uio->uio_iovcnt > 0 && a_uio->uio_iov->iov_len == 0) { + a_uio->uio_iovcnt--; + if (a_uio->uio_iovcnt > 0) { + a_uio->uio_iov++; + } + } +} + + +/*ARGSUSED*/ +mblk_t * +m_getblk(int size, int type) +{ + mblk_t *mblk; + int error; + + /* Make size at least MLEN. */ + if (size < MLEN) + size = MLEN; + mblk = allocb_wait(size, BPRI_LO, STR_NOSIG, &error); + ASSERT(mblk); + return (mblk); +} + +void +mb_done(struct mbchain *mbp) +{ + if (mbp->mb_top) { + freemsg(mbp->mb_top); + mbp->mb_top = NULL; + } + /* Avoid dangling references */ + mbp->mb_cur = NULL; +} + +unsigned int +m_length(mblk_t *mblk) +{ + uint64_t diff; + + diff = (uintptr_t)mblk->b_datap->db_lim - + (uintptr_t)mblk->b_datap->db_base; + ASSERT(diff == (uint64_t)((unsigned int)diff)); + return ((unsigned int)diff); +} + +void +mb_initm(struct mbchain *mbp, mblk_t *m) +{ + bzero(mbp, sizeof (*mbp)); + mbp->mb_top = mbp->mb_cur = m; +} + + +int +mb_init(struct mbchain *mbp) +{ + mblk_t *mblk; + + mblk = m_getblk(MLEN, 1); + if (mblk == NULL) { + return (ENOSR); + } + + /* + * Leave room in this first mblk so we can + * prepend a 4-byte NetBIOS header. + * See smb_nbst_send() + */ + mblk->b_wptr += 4; + mblk->b_rptr = mblk->b_wptr; + + mb_initm(mbp, mblk); + return (0); +} + + +/* + * mb_detach() function returns the value of mbp->mb_top field + * and sets its * value to NULL. + */ + +mblk_t * +mb_detach(struct mbchain *mbp) +{ + mblk_t *m; + + m = mbp->mb_top; + mbp->mb_top = mbp->mb_cur = NULL; + return (m); +} + +/* + * Returns the length of the mblk_t data. + * + */ +int +m_fixhdr(mblk_t *m0) +{ + size_t dsz; + + dsz = msgdsize(m0); + return ((int)dsz); +} + +/* + * BSD code set the message header length here, and + * returned the length. We don't have that field, so + * just return the message length. + */ +int +mb_fixhdr(struct mbchain *mbp) +{ + return (m_fixhdr(mbp->mb_top)); +} + + +/* + * Check if object of size 'size' fit to the current position and + * allocate new mbuf if not. Advance pointers and increase len. of mbuf(s). + * Return pointer to the object placeholder or NULL if any error occured. + * Note: size should be <= MLEN + */ +void * +mb_reserve(struct mbchain *mbp, int size) +{ + mblk_t *m, *mn; + void *bpos; + + m = mbp->mb_cur; + /* + * If the requested size is more than the space left. + * Allocate and appenad a new mblk. + */ + /*LINTED*/ + if (MBLKTAIL(m) < size) { + mn = m_getblk(size, 1); + if (mn == NULL) + return (NULL); + mbp->mb_cur = m->b_cont = mn; + m = mn; + } + /* + * If 'size' bytes fits into the buffer, then + * 1. increment the write pointer to the size. + * 2. return the position from where the memory is reserved. + */ + bpos = m->b_wptr; + m->b_wptr += size; + mbp->mb_count += size; + return (bpos); +} + +/* + * All mb_put_*() functions perform an actual copy of the data into mbuf + * chain. Functions which have le or be suffixes will perform conversion to + * the little- or big-endian data formats. + * XXX: Assumes total data length in previous mblks is EVEN. + * XXX: Might need to compute the offset from mb_top instead. + */ +int +mb_put_padbyte(struct mbchain *mbp) +{ + caddr_t dst; + char x = 0; + + dst = (caddr_t)mbp->mb_cur->b_wptr; + + /* only add padding if address is odd */ + if ((long)dst & 1) + return (mb_put_mem(mbp, (caddr_t)&x, 1, MB_MSYSTEM)); + else + return (0); +} + +int +mb_put_uint8(struct mbchain *mbp, u_int8_t x) +{ + return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); +} + +int +mb_put_uint16be(struct mbchain *mbp, u_int16_t x) +{ + x = htobes(x); + return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); +} + +int +mb_put_uint16le(struct mbchain *mbp, u_int16_t x) +{ + x = htoles(x); + return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); +} + +int +mb_put_uint32be(struct mbchain *mbp, u_int32_t x) +{ + x = htobel(x); + return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); +} + +int +mb_put_uint32le(struct mbchain *mbp, u_int32_t x) +{ + x = htolel(x); + return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); +} + +int +mb_put_uint64be(struct mbchain *mbp, u_int64_t x) +{ + x = htobeq(x); + return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); +} + +int +mb_put_uint64le(struct mbchain *mbp, u_int64_t x) +{ + x = htoleq(x); + return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); +} + +/* + * mb_put_mem() function copies size bytes of data specified by the source + * argument to an mbuf chain. The type argument specifies the method used + * to perform a copy + */ +int +mb_put_mem(struct mbchain *mbp, c_caddr_t source, int size, int type) +{ + mblk_t *m, *n; + caddr_t dst; + c_caddr_t src; + int cplen, error, mleft, count; + uint64_t diff; + + m = mbp->mb_cur; + + /*LINTED*/ + diff = MBLKTAIL(m); + ASSERT(diff == (uint64_t)((int)diff)); + mleft = (int)diff; + + while (size > 0) { + if (mleft == 0) { + if (m->b_cont == NULL) { + /* + * Changed m_getm() to m_getblk() + * with the requested size, so we + * don't need m_getm() anymore. + */ + n = m_getblk(size, 1); + if (n == NULL) + return (ENOBUFS); + m->b_cont = n; + } + m = m->b_cont; + /*LINTED*/ + diff = MBLKTAIL(m); + ASSERT(diff == (uint64_t)((int)diff)); + mleft = (int)diff; + continue; + } + cplen = mleft > size ? size : mleft; + dst = (caddr_t)m->b_wptr; + switch (type) { + case MB_MINLINE: + for (src = source, count = cplen; count; count--) + *dst++ = *src++; + break; + case MB_MSYSTEM: + /* + * Try copying the raw bytes instead of using bcopy() + */ + bcopy(source, dst, cplen); + break; + case MB_MUSER: + error = copyin((void *)source, dst, cplen); + if (error) + return (error); + break; + case MB_MZERO: + bzero(dst, cplen); + break; + } + size -= cplen; + source += cplen; + mleft -= cplen; + m->b_wptr += cplen; + mbp->mb_count += cplen; + } + mbp->mb_cur = m; + return (0); +} + +/* + * Append an mblk to the chain. + */ +int +mb_put_mbuf(struct mbchain *mbp, mblk_t *m) +{ + mblk_t *mb; + + /* See: linkb(9f) */ + for (mb = mbp->mb_cur; mb->b_cont; mb = mb->b_cont) + ; + mb->b_cont = m; + mbp->mb_cur = m; + mbp->mb_count += msgdsize(m); + + return (0); +} + +/* + * copies a uio scatter/gather list to an mbuf chain. + */ +int +mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size) +{ + int left; + int mtype, error; + + mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM); + + while (size > 0 && uiop->uio_resid) { + if (uiop->uio_iovcnt <= 0 || uio_curriovbase(uiop) == + USER_ADDR_NULL) + return (EFBIG); + left = uio_curriovlen(uiop); + if (left > size) + left = size; + error = mb_put_mem(mbp, CAST_DOWN(caddr_t, + uio_curriovbase(uiop)), left, mtype); + if (error) + return (error); + uio_update(uiop, left); + size -= left; + } + return (0); +} + +/* + * Routines for fetching data from an mbuf chain + */ +int +md_init(struct mdchain *mdp) +{ + mblk_t *m; + + m = m_getblk(MLEN, 1); + if (m == NULL) + return (ENOBUFS); + md_initm(mdp, m); + return (0); +} + +void +md_initm(struct mdchain *mdp, mblk_t *m) +{ + bzero(mdp, sizeof (*mdp)); + mdp->md_top = mdp->md_cur = m; + mdp->md_pos = m->b_rptr; +} + +void +md_done(struct mdchain *mdp) +{ + mblk_t *m; + + /* + * Deal with the fact that we can error out of + * smb_t2_reply or smb_nt_reply without using up + * all the "records" added by md_append_record(). + */ + while ((m = mdp->md_top) != NULL) { + mdp->md_top = m->b_next; + m->b_next = NULL; + freemsg(m); + } + /* Avoid dangling references */ + mdp->md_cur = NULL; + mdp->md_pos = NULL; +} + +/* + * Append a new message (separate mbuf chain). + * It is caller responsibility to prevent + * multiple calls to fetch/record routines. + * XXX: Note (mis)use of mblk->b_next here. + */ +void +md_append_record(struct mdchain *mdp, mblk_t *top) +{ + mblk_t *m; + + top->b_next = NULL; + if (mdp->md_top == NULL) { + md_initm(mdp, top); + return; + } + m = mdp->md_top; + /* Get to last message (not b_cont chain) */ + while (m->b_next) + m = m->b_next; + m->b_next = top; +} + +/* + * Advance mdp->md_top to the next message. + * XXX: Note (mis)use of mblk->b_next here. + */ +int +md_next_record(struct mdchain *mdp) +{ + mblk_t *m; + + if (mdp->md_top == NULL) + return (ENOENT); + /* Get to next message (not b_cont chain) */ + m = mdp->md_top->b_next; + mdp->md_top->b_next = NULL; + md_done(mdp); + if (m == NULL) + return (ENOENT); + md_initm(mdp, m); + return (0); +} + +int +md_get_uint8(struct mdchain *mdp, u_int8_t *x) +{ + return (md_get_mem(mdp, (char *)x, 1, MB_MINLINE)); +} + +int +md_get_uint16(struct mdchain *mdp, u_int16_t *x) +{ + return (md_get_mem(mdp, (char *)x, 2, MB_MINLINE)); +} + +int +md_get_uint16le(struct mdchain *mdp, u_int16_t *x) +{ + u_int16_t v; + int error = md_get_uint16(mdp, &v); + + if (x) + *x = letohs(v); + return (error); +} + +int +md_get_uint16be(struct mdchain *mdp, u_int16_t *x) { + u_int16_t v; + int error = md_get_uint16(mdp, &v); + + if (x) + *x = betohs(v); + return (error); +} + +int +md_get_uint32(struct mdchain *mdp, u_int32_t *x) +{ + return (md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE)); +} + +int +md_get_uint32be(struct mdchain *mdp, u_int32_t *x) +{ + u_int32_t v; + int error; + + error = md_get_uint32(mdp, &v); + if (x) + *x = betohl(v); + return (error); +} + +int +md_get_uint32le(struct mdchain *mdp, u_int32_t *x) +{ + u_int32_t v; + int error; + + error = md_get_uint32(mdp, &v); + if (x) + *x = letohl(v); + return (error); +} + +int +md_get_uint64(struct mdchain *mdp, u_int64_t *x) +{ + return (md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE)); +} + +int +md_get_uint64be(struct mdchain *mdp, u_int64_t *x) +{ + u_int64_t v; + int error; + + error = md_get_uint64(mdp, &v); + if (x) + *x = betohq(v); + return (error); +} + +int +md_get_uint64le(struct mdchain *mdp, u_int64_t *x) +{ + u_int64_t v; + int error; + + error = md_get_uint64(mdp, &v); + if (x) + *x = letohq(v); + return (error); +} + +int +md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type) +{ + mblk_t *m = mdp->md_cur; + int error; + int count; + unsigned char *s; + uint64_t diff; + + while (size > 0) { + if (m == NULL) { + SMBSDEBUG("incomplete copy\n"); + return (EBADRPC); + } + + /* + * Offset in the current MBUF. + */ + s = mdp->md_pos; + ASSERT((m->b_rptr <= s) && (s <= m->b_wptr)); + + /* Data remaining. */ + diff = (uintptr_t)m->b_wptr - (uintptr_t)s; + ASSERT(diff == (uint64_t)((int)diff)); + count = (int)diff; + + /* + * Check if the no. of bytes remaining is less than + * the bytes requested. + */ + if (count == 0) { + m = m->b_cont; + if (m) { + mdp->md_cur = m; + mdp->md_pos = s = m->b_rptr; + } + continue; + } + if (count > size) + count = size; + size -= count; + mdp->md_pos += count; + if (target == NULL) + continue; + switch (type) { + case MB_MUSER: + error = copyout(s, (void *)target, count); + if (error) + return (error); + break; + case MB_MSYSTEM: + bcopy(s, target, count); + break; + case MB_MINLINE: + while (count--) + *target++ = *s++; + continue; + } + target += count; + } + return (0); +} + +/* + * Get the next SIZE bytes as a separate mblk. + */ +int +md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret) +{ + mblk_t *m, *rm; + + unsigned char *s; + uint64_t diff; + int off; + + /* + * Offset in the current MBUF. + */ + m = mdp->md_cur; + s = mdp->md_pos; + ASSERT((m->b_rptr <= s) && (s <= m->b_wptr)); + diff = (uintptr_t)s - (uintptr_t)m->b_rptr; + ASSERT(diff == (uint64_t)((int)diff)); + off = (int)diff; + + rm = m_copym(m, off, size, M_WAITOK); + if (rm == NULL) + return (EBADRPC); + + *ret = rm; + return (0); +} + +int +md_get_uio(struct mdchain *mdp, uio_t *uiop, int size) +{ + size_t left; + int mtype, error; + + mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM); + while (size > 0 && uiop->uio_resid) { + if (uiop->uio_iovcnt <= 0 || + uio_curriovbase(uiop) == USER_ADDR_NULL) + return (EFBIG); + left = uio_curriovlen(uiop); + if (left > size) + left = size; + error = md_get_mem(mdp, CAST_DOWN(caddr_t, + uio_curriovbase(uiop)), left, mtype); + if (error) + return (error); + uio_update(uiop, left); + size -= left; + } + return (0); +} + +/* + * Additions for Solaris + */ + +/* + * concatenate mblk chain n to m. + * go till end of data in m. + * then add the link of b_cont to n. + * See: linkb(9f) + */ + +void m_cat( + mblk_t *m, + mblk_t *n) +{ + if (!n) + return; + while (m->b_cont) { + m = m->b_cont; + } + m->b_cont = n; +} + +/*ARGSUSED*/ +mblk_t * +m_copym(mblk_t *m, int off, int len, int wait) +{ + mblk_t *n; + size_t dsz; + ssize_t adj; + + dsz = msgdsize(m); + if (len == M_COPYALL) { + if (off > dsz) + return (0); + } else { + if ((off + len) > dsz) + return (0); + } + + if ((n = dupmsg(m)) == NULL) + return (0); + + /* trim from head */ + adj = off; + if (!adjmsg(n, adj)) { + freemsg(n); + return (0); + } + + /* trim from tail */ + if (len != M_COPYALL) { + dsz = msgdsize(n); + ASSERT(len <= dsz); + if (len < dsz) { + adj = (ssize_t)len - (ssize_t)dsz; + ASSERT(adj < 0); + adjmsg(n, adj); + } + } + + return (n); +} + +/* + * Get "rqlen" contiguous bytes into the first mblk of a chain. + */ +mblk_t * +m_pullup( + mblk_t *m, + int rqlen) +{ + ptrdiff_t diff; + + /*LINTED*/ + diff = MBLKL(m); + ASSERT(diff == (ptrdiff_t)((int)diff)); + if ((int)diff < rqlen) { + /* This should be rare. */ + if (!pullupmsg(m, rqlen)) { + SMBSDEBUG("pullupmsg failed!\n"); + freemsg(m); + return (NULL); + } + } + return (m); +} + + +/* + * m_split : split the mblk from the offset(len0) to the end. + * Partition an mbuf chain in two pieces, returning the tail -- + * all but the first len0 bytes. In case of failure, it returns NULL and + * attempts to restore the chain to its original state. + * Similar to dupmsg() + adjmsg() on Solaris. + */ +/*ARGSUSED*/ +mblk_t * +m_split( + mblk_t *m0, + int len0, + int wait) +{ + mblk_t *m, *n; + int mbl, len = len0; + ptrdiff_t diff; + +#if 0 /* If life were simple, this would be: */ + for (m = m0; m && len > MBLKL(m); m = m->b_cont) + len -= MBLKL(m); +#else /* but with LP64 and picky lint we have: */ + for (m = m0; m; m = m->b_cont) { + /*LINTED*/ + diff = MBLKL(m); + ASSERT(diff == (ptrdiff_t)((int)diff)); + mbl = (int)diff; + if (len <= mbl) + break; + len -= mbl; + } +#endif + + if (m == 0) + return (0); + + /* This is the one to split (dupb, adjust) */ + if ((n = dupb(m)) == 0) + return (0); + + /*LINTED*/ + ASSERT(len <= MBLKL(m)); + + m->b_wptr = m->b_rptr + len; + n->b_rptr += len; + + /* Move any b_cont (tail) to the new head. */ + n->b_cont = m->b_cont; + m->b_cont = NULL; + + return (n); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h new file mode 100644 index 0000000000..0a2e78c103 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h @@ -0,0 +1,136 @@ +/* + * 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. + * + * $Id: smbfs.h,v 1.30.100.1 2005/05/27 02:35:28 lindak Exp $ + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBFS_SMBFS_H +#define _SMBFS_SMBFS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * FS-specific VFS structures for smbfs. + * (per-mount stuff, etc.) + * + * This file used to have mount args stuff, + * but that's now in sys/fs/smbfs_mount.h + */ + +#include <sys/list.h> +#include <sys/vfs.h> +#include <sys/fs/smbfs_mount.h> + + +/* + * SM_MAX_STATFSTIME is the maximum time to cache statvfs data. Since this + * should be a fast call on the server, the time the data cached is short. + * That lets the cache handle bursts of statvfs() requests without generating + * lots of network traffic. + */ +#define SM_MAX_STATFSTIME 2 + +/* Mask values for smbmount structure sm_status field */ +#define SM_STATUS_STATFS_BUSY 0x00000001 /* statvfs is in progress */ +#define SM_STATUS_STATFS_WANT 0x00000002 /* statvfs wakeup is wanted */ +#define SM_STATUS_TIMEO 0x00000004 /* this mount is not responding */ +#define SM_STATUS_DEAD 0x00000010 /* connection gone - unmount this */ + +extern const struct fs_operation_def smbfs_vnodeops_template[]; +extern struct vnodeops *smbfs_vnodeops; + +struct smbnode; +struct smb_share; + +/* + * The values for smi_flags. + */ +#define SMI_INT 0x01 /* interrupts allowed */ +#define SMI_DEAD 0x02 /* zone shutting down */ +#define SMI_LLOCK 0x80 /* local locking only */ + +/* + * Corresponds to Darwin: struct smbmount + */ +typedef struct smbmntinfo { + struct vfs *smi_vfsp; /* mount back pointer to vfs */ + struct smbnode *smi_root; /* the root node */ + struct smb_share *smi_share; /* netsmb SMB share conn data */ + kmutex_t smi_lock; /* mutex for flags, etc. */ + uint32_t smi_flags; /* NFS-derived flag bits */ + uint32_t smi_fsattr; /* acls & streams opts */ + uint32_t smi_status; /* status bits for this mount */ + hrtime_t smi_statfstime; /* sm_statvfsbuf cache time */ + statvfs64_t smi_statvfsbuf; /* cached statvfs data */ + kcondvar_t smi_statvfs_cv; + + /* + * Kstat statistics + */ + struct kstat *smi_io_kstats; + struct kstat *smi_ro_kstats; + + /* + * Zones support. + */ + struct zone *smi_zone; /* Zone mounted in */ + list_node_t smi_zone_node; /* Link to per-zone smi list */ + /* Lock for the list is: smi_globals_t -> smg_lock */ + + /* + * Copy of the args from mount. + */ + struct smbfs_args smi_args; +} smbmntinfo_t; + +typedef struct smbfattr { + int fa_attr; + len_t fa_size; + struct timespec fa_atime; + struct timespec fa_ctime; + struct timespec fa_mtime; + ino64_t fa_ino; + struct timespec fa_reqtime; +} smbfattr_t; + +/* + * vnode pointer to mount info + */ +#define VTOSMI(vp) ((smbmntinfo_t *)(((vp)->v_vfsp)->vfs_data)) +#define VFTOSMI(vfsp) ((smbmntinfo_t *)((vfsp)->vfs_data)) +#define SMBINTR(vp) (VTOSMI(vp)->smi_flags & SMI_INT) + +#endif /* _SMBFS_SMBFS_H */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c new file mode 100644 index 0000000000..368d5e2487 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c @@ -0,0 +1,288 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/thread.h> +#include <sys/t_lock.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/vfs.h> +#include <sys/errno.h> +#include <sys/buf.h> +#include <sys/stat.h> +#include <sys/cred.h> +#include <sys/kmem.h> +#include <sys/debug.h> +#include <sys/dnlc.h> +#include <sys/vmsystm.h> +#include <sys/flock.h> +#include <sys/share.h> +#include <sys/cmn_err.h> +#include <sys/tiuser.h> +#include <sys/sysmacros.h> +#include <sys/callb.h> +#include <sys/acl.h> +#include <sys/kstat.h> +#include <sys/signal.h> +#include <sys/list.h> +#include <sys/zone.h> + +#include <netsmb/smb_conn.h> + +#include <smbfs/smbfs.h> +#include <smbfs/smbfs_node.h> +#include <smbfs/smbfs_subr.h> + +#include <vm/hat.h> +#include <vm/as.h> +#include <vm/page.h> +#include <vm/pvn.h> +#include <vm/seg.h> +#include <vm/seg_map.h> +#include <vm/seg_vn.h> + +/* + * The following code provide zone support in order to perform an action + * for each smbfs mount in a zone. This is also where we would add + * per-zone globals and kernel threads for the smbfs module (since + * they must be terminated by the shutdown callback). + */ + +struct smi_globals { + kmutex_t smg_lock; /* lock protecting smg_list */ + list_t smg_list; /* list of SMBFS mounts in zone */ + boolean_t smg_destructor_called; +}; +typedef struct smi_globals smi_globals_t; + +static zone_key_t smi_list_key; + +/* ARGSUSED */ +static void * +smbfs_zone_init(zoneid_t zoneid) +{ + smi_globals_t *smg; + + smg = kmem_alloc(sizeof (*smg), KM_SLEEP); + mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&smg->smg_list, sizeof (smbmntinfo_t), + offsetof(smbmntinfo_t, smi_zone_node)); + smg->smg_destructor_called = B_FALSE; + return (smg); +} + +/* + * Callback routine to tell all SMBFS mounts in the zone to stop creating new + * threads. Existing threads should exit. + */ +/* ARGSUSED */ +static void +smbfs_zone_shutdown(zoneid_t zoneid, void *data) +{ + smi_globals_t *smg = data; + smbmntinfo_t *smi; + + ASSERT(smg != NULL); +again: + mutex_enter(&smg->smg_lock); + for (smi = list_head(&smg->smg_list); smi != NULL; + smi = list_next(&smg->smg_list, smi)) { + + /* + * If we've done the shutdown work for this FS, skip. + * Once we go off the end of the list, we're done. + */ + if (smi->smi_flags & SMI_DEAD) + continue; + + /* + * We will do work, so not done. Get a hold on the FS. + */ + VFS_HOLD(smi->smi_vfsp); + + /* + * purge the DNLC for this filesystem + */ + (void) dnlc_purge_vfsp(smi->smi_vfsp, 0); + + mutex_enter(&smi->smi_lock); + smi->smi_flags |= SMI_DEAD; + mutex_exit(&smi->smi_lock); + + /* + * Drop lock and release FS, which may change list, then repeat. + * We're done when every mi has been done or the list is empty. + */ + mutex_exit(&smg->smg_lock); + VFS_RELE(smi->smi_vfsp); + goto again; + } + mutex_exit(&smg->smg_lock); +} + +static void +smbfs_zone_free_globals(smi_globals_t *smg) +{ + list_destroy(&smg->smg_list); /* makes sure the list is empty */ + mutex_destroy(&smg->smg_lock); + kmem_free(smg, sizeof (*smg)); + +} + +/* ARGSUSED */ +static void +smbfs_zone_destroy(zoneid_t zoneid, void *data) +{ + smi_globals_t *smg = data; + + ASSERT(smg != NULL); + mutex_enter(&smg->smg_lock); + if (list_head(&smg->smg_list) != NULL) { + /* Still waiting for VFS_FREEVFS() */ + smg->smg_destructor_called = B_TRUE; + mutex_exit(&smg->smg_lock); + return; + } + smbfs_zone_free_globals(smg); +} + +/* + * Add an SMBFS mount to the per-zone list of SMBFS mounts. + */ +void +smbfs_zonelist_add(smbmntinfo_t *smi) +{ + smi_globals_t *smg; + + smg = zone_getspecific(smi_list_key, smi->smi_zone); + mutex_enter(&smg->smg_lock); + list_insert_head(&smg->smg_list, smi); + mutex_exit(&smg->smg_lock); +} + +/* + * Remove an SMBFS mount from the per-zone list of SMBFS mounts. + */ +void +smbfs_zonelist_remove(smbmntinfo_t *smi) +{ + smi_globals_t *smg; + + smg = zone_getspecific(smi_list_key, smi->smi_zone); + mutex_enter(&smg->smg_lock); + list_remove(&smg->smg_list, smi); + /* + * We can be called asynchronously by VFS_FREEVFS() after the zone + * shutdown/destroy callbacks have executed; if so, clean up the zone's + * smi_globals. + */ + if (list_head(&smg->smg_list) == NULL && + smg->smg_destructor_called == B_TRUE) { + smbfs_zone_free_globals(smg); + return; + } + mutex_exit(&smg->smg_lock); +} + + +#ifdef NEED_SMBFS_CALLBACKS +/* + * Call-back hooks for netsmb, in case we want them. + * Apple's VFS wants them. We may not need them. + * + * I thought I could use the "dead" callback from netsmb + * to set the SMI_DEAD flag, but that looks like it will + * interfere with the zone shutdown mechanisms. + */ +static void smbfs_dead(smb_share_t *ssp) +{ +#if 0 /* see above */ + smbmntinfo_t *smi = ssp->ss_mount; + if (smi) { + mutex_enter(&smi->smi_lock); + smi->smi_flags |= SMI_DEAD; + mutex_exit(&smi->smi_lock); + } +#endif +} + +static void smbfs_down(smb_share_t *ss) +{ + /* no-op */ +} + +static void smbfs_up(smb_share_t *ss) +{ + /* no-op */ +} + +smb_fscb_t smbfs_cb = { + .fscb_dead = smbfs_dead, + .fscb_down = smbfs_down, + .fscb_up = smbfs_up }; + +#endif /* NEED_SMBFS_CALLBACKS */ + +/* + * SMBFS Client initialization routine. This routine should only be called + * once. It performs the following tasks: + * - Initalize all global locks + * - Call sub-initialization routines (localize access to variables) + */ +int +smbfs_clntinit(void) +{ + int error; + + error = smbfs_subrinit(); + if (error) + return (error); + zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown, + smbfs_zone_destroy); +#ifdef NEED_SMBFS_CALLBACKS + smb_fscb_set(&smbfs_cb); +#endif /* NEED_SMBFS_CALLBACKS */ + return (0); +} + +/* + * This routine is called when the modunload is called. This will cleanup + * the previously allocated/initialized nodes. + */ +void +smbfs_clntfini(void) +{ +#ifdef NEED_SMBFS_CALLBACKS + smb_fscb_set(NULL); +#endif /* NEED_SMBFS_CALLBACKS */ + (void) zone_key_delete(smi_list_key); + smbfs_subrfini(); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_io.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_io.c new file mode 100644 index 0000000000..b813026f5e --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_io.c @@ -0,0 +1,206 @@ +/* + * 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. + * + * $Id: smbfs_io.c,v 1.41.38.1 2005/05/27 02:35:28 lindak Exp $ + * + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/mount.h> +#include <sys/dirent.h> +#include <sys/syslog.h> +#include <sys/file.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> + +#include <smbfs/smbfs.h> +#include <smbfs/smbfs_node.h> +#include <smbfs/smbfs_subr.h> + +/* XXX: This file should go away, after more work in _vnops.c */ + +int +smbfs_readvnode(vnode_t *vp, uio_t *uiop, cred_t *cr, + struct vattr *vap) +{ + smbmntinfo_t *smp = VTOSMI(vp); + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + int error; + int requestsize; + size_t remainder; + + /* shared lock for n_fid use in smb_rwuio */ + ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + + if (vp->v_type != VREG) { + SMBVDEBUG("only VREG supported\n"); + return (EIO); + } + if (uiop->uio_resid == 0) + return (0); + if (uiop->uio_loffset < 0) + return (EINVAL); +#ifdef NOT_YET + if ((uiop->uio_loffset + uiop->uio_resid) > smp->nm_maxfilesize) + return (EFBIG); +#endif + + smb_credinit(&scred, curproc, cr); + + /* XXX: Update n_size maybe? */ + (void) smbfs_smb_flush(np, &scred); + + if (uiop->uio_loffset >= vap->va_size) { + /* if offset is beyond EOF, read nothing */ + error = 0; + goto out; + } + + /* pin requestsize to EOF */ + requestsize = min(uiop->uio_resid, + (vap->va_size - uiop->uio_loffset)); + + /* subtract requestSize from uio_resid and save remainder */ + remainder = uiop->uio_resid - requestsize; + + /* adjust size of read */ + uiop->uio_resid = requestsize; + + error = smb_rwuio(smp->smi_share, np->n_fid, UIO_READ, uiop, + &scred, smb_timo_read); + + /* set remaining uio_resid */ + uiop->uio_resid = uiop->uio_resid + remainder; + +out: + smb_credrele(&scred); + + return (error); +} + +int +smbfs_writevnode(vnode_t *vp, uio_t *uiop, + cred_t *cr, int ioflag, int timo) +{ + smbmntinfo_t *smp = VTOSMI(vp); + struct smbnode *np = VTOSMB(vp); + struct smb_cred scred; + int error = 0; + + /* shared lock for n_fid use in smb_rwuio */ + ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + + if (vp->v_type != VREG) { + SMBVDEBUG("only VREG supported\n"); + return (EIO); + } + SMBVDEBUG("ofs=%lld,resid=%d\n", uiop->uio_loffset, + (int)uiop->uio_resid); + if (uiop->uio_loffset < 0) + return (EINVAL); +#ifdef NOT_YET + if (uiop->uio_loffset + uiop->uio_resid > smp->nm_maxfilesize) + return (EFBIG); +#endif + if (ioflag & (FAPPEND | FSYNC)) { + if (np->n_flag & NMODIFIED) { + smbfs_attr_cacheremove(np); + /* XXX: smbfs_vinvalbuf? */ + } + if (ioflag & FAPPEND) { + struct vattr vattr; + /* + * File size can be changed by another client + */ + error = smbfsgetattr(vp, &vattr, cr); + if (error) + return (error); + mutex_enter(&np->r_statelock); + uiop->uio_loffset = np->n_size; + mutex_exit(&np->r_statelock); + } + } + if (uiop->uio_resid == 0) + return (0); + + smb_credinit(&scred, curproc, cr); + + /* + * Darwin had code here to zero-extend using + * smb_write requests. Not needed. + * + * Use a longer timeout when appending. + * This ignores the passed-in timo value, + * but that was just a constant anyway. + * XXX: remove passed in timo arg later. + */ + timo = smb_timo_write; + if ((uiop->uio_loffset + uiop->uio_resid) > np->n_size) + timo = smb_timo_append; + error = smb_rwuio(smp->smi_share, np->n_fid, UIO_WRITE, uiop, + &scred, timo); + + mutex_enter(&np->r_statelock); + np->n_flag |= (NFLUSHWIRE | NATTRCHANGED); + mutex_exit(&np->r_statelock); + + smb_credrele(&scred); + + SMBVDEBUG("after: ofs=%lld,resid=%d\n", uiop->uio_loffset, + (int)uiop->uio_resid); + if (!error) { + mutex_enter(&np->r_statelock); + if (uiop->uio_loffset > (offset_t)np->n_size) + np->n_size = (len_t)uiop->uio_loffset; + mutex_exit(&np->r_statelock); + } + return (error); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c new file mode 100644 index 0000000000..6a2b184f47 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c @@ -0,0 +1,489 @@ +/* + * 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. + * + * $Id: smbfs_node.c,v 1.54.52.1 2005/05/27 02:35:28 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/cred.h> +#include <sys/vfs.h> +#include <sys/vnode.h> +#include <sys/kmem.h> +#include <sys/stat.h> +#include <sys/atomic.h> +#include <sys/cmn_err.h> +#include <sys/sysmacros.h> +#include <sys/bitmap.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> + +#include <smbfs/smbfs.h> +#include <smbfs/smbfs_node.h> +#include <smbfs/smbfs_subr.h> + +#if defined(DEBUG) || defined(lint) +#define SMBFS_NAME_DEBUG +#endif + +/* + * Lack of inode numbers leads us to the problem of generating them. + * Partially this problem can be solved by having a dir/file cache + * with inode numbers generated from the incremented by one counter. + * However this way will require too much kernel memory, gives all + * sorts of locking and consistency problems, not to mentinon counter + * overflows. So, I'm decided to use a hash function to generate + * pseudo random (and [often?] unique) inode numbers. + */ + +/* Magic constants for name hashing. */ +#define FNV_32_PRIME ((uint32_t)0x01000193UL) +#define FNV1_32_INIT ((uint32_t)33554467UL) + +uint32_t +smbfs_hash3(uint32_t ival, const char *name, int nmlen) +{ + uint32_t v; + + for (v = ival; nmlen; name++, nmlen--) { + v *= FNV_32_PRIME; + v ^= (uint32_t)*name; + } + return (v); +} + +uint32_t +smbfs_hash(const char *name, int nmlen) +{ + uint32_t v; + + v = smbfs_hash3(FNV1_32_INIT, name, nmlen); + return (v); +} + +/* + * This is basically a hash of the full path name, but + * computed without having the full path contiguously. + * The path building logic needs to match what + * smbfs_fullpath does. + * + * Note that smbfs_make_node computes inode numbers by + * calling smbfs_hash on the full path name. This will + * compute the same result given the directory path and + * the last component separately. + */ +uint32_t +smbfs_getino(struct smbnode *dnp, const char *name, int nmlen) +{ + uint32_t ino; + + /* Start with directory hash */ + ino = (uint32_t)dnp->n_ino; + + /* + * If not the root, hash a slash. + */ + if (dnp->n_rplen > 1) + ino = smbfs_hash3(ino, "\\", 1); + + /* Now hash this component. */ + ino = smbfs_hash3(ino, name, nmlen); + + return (ino); +} + +#define CHAR_FC '\374' /* 0xFC */ +#define CHAR_FE '\376' /* 0xFE */ +char * +smbfs_name_alloc(const char *name, int nmlen) +{ + char *cp; + size_t alen; + +#ifdef SMBFS_NAME_DEBUG + /* + * Note: The passed length is strlen(name), + * and does NOT include the terminating nul. + * Allocated space holds: (in order) + * (int)strlen + * char 0xFC (1st marker) + * copy of string + * terminating null + * char 0xFE (2nd marker) + */ + alen = sizeof (int) + 1 + nmlen + 1 + 1; + cp = kmem_alloc(alen, KM_SLEEP); + /*LINTED*/ + *(int *)cp = nmlen; + cp += sizeof (int); + cp[0] = CHAR_FC; + cp++; + bcopy(name, cp, nmlen); + cp[nmlen] = 0; + cp[nmlen + 1] = CHAR_FE; +#else + alen = nmlen + 1; /* Passed length does NOT include the nul. */ + cp = kmem_alloc(alen, KM_SLEEP); + bcopy(name, cp, nmlen); + cp[nmlen] = 0; +#endif + return (cp); +} + +/* + * Note: Passed length does NOT include the nul, + * the same as with smbfs_name_alloc(). + */ +void +smbfs_name_free(const char *name, int nmlen) +{ + size_t alen; +#ifdef SMBFS_NAME_DEBUG + int lnmlen; + char *cp; + + /* + * See comment in smbfs_name_alloc + * about the layout of this memory. + */ + alen = sizeof (int) + 1 + nmlen + 1 + 1; + cp = (char *)name; + cp--; + if (*cp != CHAR_FC) { + debug_enter("smbfs_name_free: name[-1] != 0xFC"); + } + cp -= sizeof (int); + /*LINTED*/ + lnmlen = *(int *)cp; + if (lnmlen != nmlen) { + debug_enter("smbfs_name_free: name[-5] != nmlen"); + } + if (name[nmlen + 1] != CHAR_FE) { + debug_enter("smbfs_name_free: name[nmlen+1] != 0xFE"); + } + kmem_free(cp, alen); +#else + alen = nmlen + 1; + kmem_free((char *)name, alen); +#endif +} + +/* + * smbfs_nget() + * + * NOTES: + * + * It would be nice to be able to pass in a flag when the caller is sure + * that the node does not exist and should just be allocated. + */ +int +smbfs_nget(vnode_t *dvp, const char *name, int nmlen, + struct smbfattr *fap, vnode_t **vpp) +{ + struct smbnode *dnp = VTOSMB(dvp); + vnode_t *vp; + + *vpp = NULL; + + /* Don't expect "." or ".." here anymore. */ + if ((nmlen == 1 && name[0] == '.') || + (nmlen == 2 && name[0] == '.' && name[1] == '.')) { + DEBUG_ENTER("smbfs_nget: name is '.' or '..'"); + return (EINVAL); + } + + /* The real work is in this call... */ + vp = smbfs_make_node(dvp->v_vfsp, + dnp->n_rpath, dnp->n_rplen, + name, nmlen, fap); + + /* + * We always have a vp now, because + * smbfs_make_node / make_smbnode + * calls kmem_alloc with KM_SLEEP. + */ + ASSERT(vp); + +#ifdef NOT_YET + /* update the attr_cache info if the file is clean */ + if (fap && !(VTOSMB(vp)->n_flag & NFLUSHWIRE)) + smbfs_attr_cacheenter(vp, fap); + if (dvp && makeentry) { + /* add entry to DNLC */ + cache_enter(dvp, vp, &cn); + } +#endif /* NOT_YET */ + + /* BSD symlink hack removed (smb_symmagic) */ + +#ifdef NOT_YET + smbfs_attr_cacheenter(vp, fap); /* update the attr_cache info */ +#endif /* NOT_YET */ + + *vpp = vp; + + return (0); +} + +/* + * routines to maintain vnode attributes cache + * smbfs_attr_cacheenter: unpack np.i to vnode_vattr structure + * + * Note that some SMB servers do not exhibit POSIX behaviour + * with regard to the mtime on directories. To work around + * this, we never allow the mtime on a directory to go backwards, + * and bump it forwards elsewhere to simulate the correct + * behaviour. + */ +void +smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap) +{ + struct smbnode *np = VTOSMB(vp); + int vtype; + struct timespec ts; + + mutex_enter(&np->r_statelock); + + vtype = vp->v_type; + if (vtype == VREG) { + if (np->n_size != fap->fa_size) { + /* + * Had Darwin ubc_sync_range call here, + * invalidating the truncated range. + * XXX: Solaris equivalent? + */ + SMBVDEBUG("Update size?\n"); + } + np->n_size = fap->fa_size; + } else if (vtype == VDIR) { + np->n_size = 16384; /* XXX should be a better way ... */ + /* + * Don't allow mtime to go backwards. + * Yes this has its flaws. Better ideas are welcome! + */ + /*CSTYLED*/ + if (timespeccmp(&fap->fa_mtime, &np->n_mtime, <)) + fap->fa_mtime = np->n_mtime; + } else if (vtype != VLNK) + goto out; + + np->n_atime = fap->fa_atime; + np->n_ctime = fap->fa_ctime; + np->n_mtime = fap->fa_mtime; + np->n_dosattr = fap->fa_attr; + + np->n_flag &= ~NATTRCHANGED; + gethrestime(&ts); + np->n_attrage = ts.tv_sec; + +out: + mutex_exit(&np->r_statelock); +} + +int +smbfs_attr_cachelookup(vnode_t *vp, struct vattr *vap) +{ + struct smbnode *np = VTOSMB(vp); + struct smbmntinfo *smi = VTOSMI(vp); + time_t attrtimeo; + struct timespec ts, *stime; + mode_t type; + + /* + * Determine attrtimeo. It will be something between SMB_MINATTRTIMO and + * SMB_MAXATTRTIMO where recently modified files have a short timeout + * and files that haven't been modified in a long time have a long + * timeout. This is the same algorithm used by NFS. + */ + gethrestime(&ts); + stime = &np->r_mtime; + attrtimeo = (ts.tv_sec - stime->tv_sec) / 10; + if (attrtimeo < SMB_MINATTRTIMO) { + attrtimeo = SMB_MINATTRTIMO; + } else if (attrtimeo > SMB_MAXATTRTIMO) + attrtimeo = SMB_MAXATTRTIMO; + /* has too much time passed? */ + stime = (struct timespec *)&np->r_attrtime; + if ((ts.tv_sec - stime->tv_sec) > attrtimeo) + return (ENOENT); + + if (!vap) + return (0); + + switch (vp->v_type) { + case VREG: + type = S_IFREG; + break; + case VLNK: + type = S_IFLNK; + break; + case VDIR: + type = S_IFDIR; + break; + default: + SMBSDEBUG("unknown vnode_vtype %d\n", vp->v_type); + return (EINVAL); + } + + mutex_enter(&np->r_statelock); + + if (!(np->n_flag & NGOTIDS)) { + np->n_mode = type; +#ifdef APPLE + if (smi->smi_fsattr & FILE_PERSISTENT_ACLS) { + /* XXX: Can this block? Drop r_statelock? */ + if (!smbfs_getids(np, scredp)) { + np->n_flag |= NGOTIDS; + np->n_mode |= ACCESSPERMS; /* 0777 */ + } + } +#endif /* APPLE */ + if (!(np->n_flag & NGOTIDS)) { + np->n_flag |= NGOTIDS; + np->n_uid = smi->smi_args.uid; + np->n_gid = smi->smi_args.gid; + } + } + + if (vap->va_mask & AT_TYPE) + vap->va_type = vp->v_type; + if (vap->va_mask & AT_MODE) { + np->n_mode = 0; + if (vp->v_type == VDIR) + np->n_mode |= smi->smi_args.dir_mode; + else /* symlink and regular file */ + np->n_mode |= smi->smi_args.file_mode; + vap->va_mode = np->n_mode; + } + if (vap->va_mask & AT_SIZE) + vap->va_size = np->n_size; + if (vap->va_mask & AT_NODEID) + vap->va_nodeid = np->n_ino; + if (vap->va_mask & AT_ATIME) + vap->va_atime = np->n_atime; + if (vap->va_mask & AT_CTIME) + vap->va_ctime = np->n_ctime; + if (vap->va_mask & AT_MTIME) + vap->va_mtime = np->n_mtime; + vap->va_nlink = 1; + vap->va_uid = np->n_uid; + vap->va_gid = np->n_gid; + vap->va_fsid = vp->v_vfsp->vfs_dev; + vap->va_rdev = 0; + vap->va_blksize = MAXBSIZE; + vap->va_nblocks = (fsblkcnt64_t)btod(np->n_size); + vap->va_seq = 0; + + mutex_exit(&np->r_statelock); + + return (0); +} + +/* + * Some SMB servers don't exhibit POSIX behaviour with regard to + * updating the directory mtime when the directory's contents + * change. + * + * We force the issue here by updating our cached copy of the mtime + * whenever we perform such an action ourselves, and then mark the + * cache invalid. Subsequently when the invalidated cache entry is + * updated, we disallow an update that would move the mtime backwards. + * + * This preserves correct or near-correct behaviour with a + * compliant server, and gives near-correct behaviour with + * a non-compliant server in the most common case (we are the + * only client changing the directory). + * + * There are also complications if a server's time is ahead + * of our own. We must 'touch' a directory when it is first + * created, to ensure that the timestamp starts out sane, + * however it may have a timestamp well ahead of the 'touch' + * point which will be returned and cached the first time the + * directory's attributes are fetched. Subsequently, the + * directory's mtime will not appear to us to change at all + * until our local time catches up to the server. + * + * Thus, any time a directory is 'touched', the saved timestamp + * must advance at least far enough forwards to be visible to + * the stat(2) interface. + * + * XXX note that better behaviour with non-compliant servers + * could be obtained by bumping the mtime forwards when + * an update for an invalidated entry returns a nonsensical + * mtime. + */ + +void +smbfs_attr_touchdir(struct smbnode *dnp) +{ + struct timespec ts, ta; + + mutex_enter(&dnp->r_statelock); + + /* + * XXX - not sure about this... + * Creep the saved time forwards far enough that + * layers above the kernel will notice. + */ + ta.tv_sec = 1; + ta.tv_nsec = 0; + timespecadd(&dnp->n_mtime, &ta); + /* + * If the current time is later than the updated + * saved time, apply it instead. + */ + gethrestime(&ts); + /*CSTYLED*/ + if (timespeccmp(&dnp->n_mtime, &ts, <)) + dnp->n_mtime = ts; + /* + * Invalidate the cache, so that we go to the wire + * to check that the server doesn't have a better + * timestamp next time we care. + */ + smbfs_attr_cacheremove(dnp); + mutex_exit(&dnp->r_statelock); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h new file mode 100644 index 0000000000..e05a0b4a5d --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h @@ -0,0 +1,301 @@ +/* + * 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. + * + * $Id: smbfs_node.h,v 1.31.52.1 2005/05/27 02:35:28 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _FS_SMBFS_NODE_H_ +#define _FS_SMBFS_NODE_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Much code copied into here from Sun NFS. + */ + +#include <sys/avl.h> +#include <sys/list.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct rddir_cache { + lloff_t _cookie; /* cookie used to find this cache entry */ + lloff_t _ncookie; /* cookie used to find the next cache entry */ + char *entries; /* buffer containing dirent entries */ + int eof; /* EOF reached after this request */ + int entlen; /* size of dirent entries in buf */ + int buflen; /* size of the buffer used to store entries */ + int flags; /* control flags, see below */ + kcondvar_t cv; /* cv for blocking */ + int error; /* error from RPC operation */ + kmutex_t lock; + uint_t count; /* reference count */ + avl_node_t tree; /* AVL tree links */ +} rddir_cache; + +#define smbfs_cookie _cookie._p._l +#define smbfs_ncookie _ncookie._p._l +#define smbfs3_cookie _cookie._f +#define smbfs3_ncookie _ncookie._f + +#define RDDIR 0x1 /* readdir operation in progress */ +#define RDDIRWAIT 0x2 /* waiting on readdir in progress */ +#define RDDIRREQ 0x4 /* a new readdir is required */ +#define RDDIRCACHED 0x8 /* entry is in the cache */ + +#define HAVE_RDDIR_CACHE(rp) (avl_numnodes(&(rp)->r_dir) > 0) + +/* + * A homegrown reader/writer lock implementation. It addresses + * two requirements not addressed by the system primitives. They + * are that the `enter" operation is optionally interruptible and + * that that they can be re`enter'ed by writers without deadlock. + */ +typedef struct smbfs_rwlock { + int count; + int waiters; + kthread_t *owner; + kmutex_t lock; + kcondvar_t cv; +} smbfs_rwlock_t; + +/* + * The format of the hash bucket used to lookup smbnodes from a file handle. + */ +typedef struct rhashq { + struct smbnode *r_hashf; + struct smbnode *r_hashb; + krwlock_t r_lock; +} rhashq_t; + +/* + * Remote file information structure. + * + * The smbnode is the "inode" for remote files. It contains all the + * information necessary to handle remote file on the client side. + * + * Note on file sizes: we keep two file sizes in the smbnode: the size + * according to the client (r_size) and the size according to the server + * (r_attr.va_size). They can differ because we modify r_size during a + * write system call (smbfs_rdwr), before the write request goes over the + * wire (before the file is actually modified on the server). If an OTW + * request occurs before the cached data is written to the server the file + * size returned from the server (r_attr.va_size) may not match r_size. + * r_size is the one we use, in general. r_attr.va_size is only used to + * determine whether or not our cached data is valid. + * + * Each smbnode has 3 locks associated with it (not including the smbnode + * hash table and free list locks): + * + * r_rwlock: Serializes smbfs_write and smbfs_setattr requests + * and allows smbfs_read requests to proceed in parallel. + * Serializes reads/updates to directories. + * + * r_lkserlock: Serializes lock requests with map, write, and + * readahead operations. + * + * r_statelock: Protects all fields in the smbnode except for + * those listed below. This lock is intented + * to be held for relatively short periods of + * time (not accross entire putpage operations, + * for example). + * + * The following members are protected by the mutex rpfreelist_lock: + * r_freef + * r_freeb + * + * The following members are protected by the hash bucket rwlock: + * r_hashf + * r_hashb + * + * Note: r_modaddr is only accessed when the r_statelock mutex is held. + * Its value is also controlled via r_rwlock. It is assumed that + * there will be only 1 writer active at a time, so it safe to + * set r_modaddr and release r_statelock as long as the r_rwlock + * writer lock is held. + * + * 64-bit offsets: the code formerly assumed that atomic reads of + * r_size were safe and reliable; on 32-bit architectures, this is + * not true since an intervening bus cycle from another processor + * could update half of the size field. The r_statelock must now + * be held whenever any kind of access of r_size is made. + * + * Lock ordering: + * r_rwlock > r_lkserlock > r_statelock + */ +struct exportinfo; /* defined in smbfs/export.h */ +struct failinfo; /* defined in smbfs/smbfs_clnt.h */ +struct mntinfo; /* defined in smbfs/smbfs_clnt.h */ + +#ifdef _KERNEL +/* Bits for smbnode.n_flag */ +#define NFLUSHINPROG 0x00001 +#define NFLUSHWANT 0x00002 /* they should gone ... */ +#define NMODIFIED 0x00004 /* bogus, until async IO implemented */ +#define NREFPARENT 0x00010 /* node holds parent from recycling */ +#define NGOTIDS 0x00020 +#define NRDIRSERIAL 0x00080 /* serialize readdir operation */ +#define NISMAPPED 0x00800 +#define NFLUSHWIRE 0x01000 +#define NATTRCHANGED 0x02000 /* use smbfs_attr_cacheremove at close */ +#define NALLOC 0x04000 /* being created */ +#define NWALLOC 0x08000 /* awaiting creation */ + +typedef struct smbnode { + /* from Sun NFS struct rnode (XXX: cleanup needed) */ + /* the hash fields must be first to match the rhashq_t */ + /* Lock for the hash queue is: np->r_hashq->r_lock */ + struct smbnode *r_hashf; /* hash queue forward pointer */ + struct smbnode *r_hashb; /* hash queue back pointer */ + /* Lock for the free list is: smbfreelist_lock */ + struct smbnode *r_freef; /* free list forward pointer */ + struct smbnode *r_freeb; /* free list back pointer */ + rhashq_t *r_hashq; /* pointer to the hash bucket */ + vnode_t *r_vnode; /* vnode for remote file */ + smbfs_rwlock_t r_rwlock; /* serializes write/setattr requests */ + smbfs_rwlock_t r_lkserlock; /* serialize lock with other ops */ + kmutex_t r_statelock; /* protects (most of) smbnode fields */ + u_offset_t r_nextr; /* next byte read offset (read-ahead) */ + cred_t *r_cred; /* current credentials */ + len_t r_size; /* client's view of file size */ + struct vattr r_attr; /* cached vnode attributes */ + hrtime_t r_attrtime; /* time attributes become invalid */ + long r_mapcnt; /* count of mmapped pages */ + uint_t r_count; /* # of refs not reflect in v_count */ + uint_t r_awcount; /* # of outstanding async write */ + uint_t r_gcount; /* getattrs waiting to flush pages */ + ushort_t r_flags; /* flags, see below */ + short r_error; /* async write error */ + kcondvar_t r_cv; /* condvar for blocked threads */ + avl_tree_t r_dir; /* cache of readdir responses */ + rddir_cache *r_direof; /* pointer to the EOF entry */ + kthread_t *r_serial; /* id of purging thread */ + list_t r_indelmap; /* list of delmap callers */ + /* + * Members derived from Darwin struct smbnode. + * Note: n_parent node pointer removed because it + * caused unwanted "holds" on nodes in our cache. + * Now keeping just the full remote path instead, + * in server form, relative to the share root. + */ + char *n_rpath; + int n_rplen; + uint32_t n_flag; + smbmntinfo_t *n_mount; + ino64_t n_ino; + /* Lock for the next 7 is r_lkserlock */ + int n_dirrefs; + struct smbfs_fctx *n_dirseq; /* ff context */ + long n_dirofs; /* last ff offset */ + long n_direof; /* End of dir. offset. */ + int n_fidrefs; + uint16_t n_fid; /* file handle */ + uint32_t n_rights; /* granted rights */ + /* Lock for the rest is r_statelock */ + uid_t n_uid; + gid_t n_gid; + mode_t n_mode; + timestruc_t r_atime; + timestruc_t r_ctime; + timestruc_t r_mtime; + int n_dosattr; + /* + * XXX: Maybe use this instead: + * #define n_atime r_attr.va_atime + * etc. + */ +#define n_size r_size +#define n_atime r_atime +#define n_ctime r_ctime +#define n_mtime r_mtime +#define n_attrage r_attrtime +} smbnode_t; +#endif /* _KERNEL */ + +/* + * Flags + */ +#define RREADDIRPLUS 0x1 /* issue a READDIRPLUS instead of READDIR */ +#define RDIRTY 0x2 /* dirty pages from write operation */ +#define RSTALE 0x4 /* file handle is stale */ +#define RMODINPROGRESS 0x8 /* page modification happening */ +#define RTRUNCATE 0x10 /* truncating, don't commit */ +#define RHAVEVERF 0x20 /* have a write verifier to compare against */ +#define RCOMMIT 0x40 /* commit in progress */ +#define RCOMMITWAIT 0x80 /* someone is waiting to do a commit */ +#define RHASHED 0x100 /* smbnode is in hash queues */ +#define ROUTOFSPACE 0x200 /* an out of space error has happened */ +#define RDIRECTIO 0x400 /* bypass the buffer cache */ +#define RLOOKUP 0x800 /* a lookup has been performed */ +#define RWRITEATTR 0x1000 /* attributes came from WRITE */ +#define RINDNLCPURGE 0x2000 /* in the process of purging DNLC references */ +#define RDELMAPLIST 0x4000 /* delmap callers tracking for as callback */ + +/* + * Convert between vnode and smbnode + */ +#define VTOSMB(vp) ((smbnode_t *)((vp)->v_data)) +#define SMBTOV(np) ((np)->r_vnode) + +/* Attribute cache timeouts in seconds */ +#define SMB_MINATTRTIMO 2 +#define SMB_MAXATTRTIMO 30 + +/* + * Function definitions. + */ +struct smb_cred; +int smbfs_nget(vnode_t *dvp, const char *name, int nmlen, + struct smbfattr *fap, vnode_t **vpp); +void smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap); +int smbfs_attr_cachelookup(vnode_t *vp, struct vattr *va); +void smbfs_attr_touchdir(struct smbnode *dnp); +char *smbfs_name_alloc(const char *name, int nmlen); +void smbfs_name_free(const char *name, int nmlen); +uint32_t smbfs_hash(const char *name, int nmlen); +uint32_t smbfs_hash3(uint32_t ival, const char *name, int nmlen); +uint32_t smbfs_getino(struct smbnode *dnp, const char *name, int nmlen); +int smb_check_table(struct vfs *vfsp, smbnode_t *srp); + +#define smbfs_attr_cacheremove(np) (np)->n_attrage = 0 + +#ifdef __cplusplus +} +#endif + +#endif /* _FS_SMBFS_NODE_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_rwlock.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_rwlock.c new file mode 100644 index 0000000000..d9f4ae70aa --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_rwlock.c @@ -0,0 +1,252 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * A homegrown reader/writer lock implementation. It addresses + * two requirements not addressed by the system primitives. They + * are that the `enter" operation is optionally interruptible and + * that that they can be re`enter'ed by writers without deadlock. + * + * All of this was borrowed from NFS. + * See: uts/common/fs/nfs/nfs_subr.c + * + * XXX: Could we make this serve our needs instead? + * See: uts/common/os/rwstlock.c + * (and then use it for NFS too) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/vnode.h> + +#include <smbfs/smbfs.h> +#include <smbfs/smbfs_node.h> +#include <smbfs/smbfs_subr.h> + + +/* + * Only can return non-zero if intr != 0. + */ +int +smbfs_rw_enter_sig(smbfs_rwlock_t *l, krw_t rw, int intr) +{ + + mutex_enter(&l->lock); + + /* + * If this is a nested enter, then allow it. There + * must be as many exits as enters through. + */ + if (l->owner == curthread) { + /* lock is held for writing by current thread */ + ASSERT(rw == RW_READER || rw == RW_WRITER); + l->count--; + } else if (rw == RW_READER) { + /* + * While there is a writer active or writers waiting, + * then wait for them to finish up and move on. Then, + * increment the count to indicate that a reader is + * active. + */ + while (l->count < 0 || l->waiters > 0) { + if (intr) { + klwp_t *lwp = ttolwp(curthread); + + if (lwp != NULL) + lwp->lwp_nostop++; + if (!cv_wait_sig(&l->cv, &l->lock)) { + if (lwp != NULL) + lwp->lwp_nostop--; + mutex_exit(&l->lock); + return (EINTR); + } + if (lwp != NULL) + lwp->lwp_nostop--; + } else + cv_wait(&l->cv, &l->lock); + } + ASSERT(l->count < INT_MAX); +#ifdef SMBDEBUG + if ((l->count % 10000) == 9999) + cmn_err(CE_WARN, "smbfs_rw_enter_sig: count %d on" + "rwlock @ %p\n", l->count, (void *)&l); +#endif + l->count++; + } else { + ASSERT(rw == RW_WRITER); + /* + * While there are readers active or a writer + * active, then wait for all of the readers + * to finish or for the writer to finish. + * Then, set the owner field to curthread and + * decrement count to indicate that a writer + * is active. + */ + while (l->count > 0 || l->owner != NULL) { + l->waiters++; + if (intr) { + klwp_t *lwp = ttolwp(curthread); + + if (lwp != NULL) + lwp->lwp_nostop++; + if (!cv_wait_sig(&l->cv, &l->lock)) { + if (lwp != NULL) + lwp->lwp_nostop--; + l->waiters--; + cv_broadcast(&l->cv); + mutex_exit(&l->lock); + return (EINTR); + } + if (lwp != NULL) + lwp->lwp_nostop--; + } else + cv_wait(&l->cv, &l->lock); + l->waiters--; + } + l->owner = curthread; + l->count--; + } + + mutex_exit(&l->lock); + + return (0); +} + +/* + * If the lock is available, obtain it and return non-zero. If there is + * already a conflicting lock, return 0 immediately. + */ + +int +smbfs_rw_tryenter(smbfs_rwlock_t *l, krw_t rw) +{ + mutex_enter(&l->lock); + + /* + * If this is a nested enter, then allow it. There + * must be as many exits as enters through. + */ + if (l->owner == curthread) { + /* lock is held for writing by current thread */ + ASSERT(rw == RW_READER || rw == RW_WRITER); + l->count--; + } else if (rw == RW_READER) { + /* + * If there is a writer active or writers waiting, deny the + * lock. Otherwise, bump the count of readers. + */ + if (l->count < 0 || l->waiters > 0) { + mutex_exit(&l->lock); + return (0); + } + l->count++; + } else { + ASSERT(rw == RW_WRITER); + /* + * If there are readers active or a writer active, deny the + * lock. Otherwise, set the owner field to curthread and + * decrement count to indicate that a writer is active. + */ + if (l->count > 0 || l->owner != NULL) { + mutex_exit(&l->lock); + return (0); + } + l->owner = curthread; + l->count--; + } + + mutex_exit(&l->lock); + + return (1); +} + +void +smbfs_rw_exit(smbfs_rwlock_t *l) +{ + + mutex_enter(&l->lock); + /* + * If this is releasing a writer lock, then increment count to + * indicate that there is one less writer active. If this was + * the last of possibly nested writer locks, then clear the owner + * field as well to indicate that there is no writer active + * and wakeup any possible waiting writers or readers. + * + * If releasing a reader lock, then just decrement count to + * indicate that there is one less reader active. If this was + * the last active reader and there are writer(s) waiting, + * then wake up the first. + */ + if (l->owner != NULL) { + ASSERT(l->owner == curthread); + l->count++; + if (l->count == 0) { + l->owner = NULL; + cv_broadcast(&l->cv); + } + } else { + ASSERT(l->count > 0); + l->count--; + if (l->count == 0 && l->waiters > 0) + cv_broadcast(&l->cv); + } + mutex_exit(&l->lock); +} + +int +smbfs_rw_lock_held(smbfs_rwlock_t *l, krw_t rw) +{ + + if (rw == RW_READER) + return (l->count > 0); + ASSERT(rw == RW_WRITER); + return (l->count < 0); +} + +/* ARGSUSED */ +void +smbfs_rw_init(smbfs_rwlock_t *l, char *name, krw_type_t type, void *arg) +{ + + l->count = 0; + l->waiters = 0; + l->owner = NULL; + mutex_init(&l->lock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&l->cv, NULL, CV_DEFAULT, NULL); +} + +void +smbfs_rw_destroy(smbfs_rwlock_t *l) +{ + + mutex_destroy(&l->lock); + cv_destroy(&l->cv); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c new file mode 100644 index 0000000000..789bfdfaff --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c @@ -0,0 +1,3147 @@ +/* + * 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. + * + * $Id: smbfs_smb.c,v 1.73.38.1 2005/05/27 02:35:28 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/cmn_err.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#include <sys/utfconv.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#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> + +/* + * Local functions. + * Not static, to aid debugging. + */ + +int smbfs_smb_qfileinfo(struct smbnode *np, struct smbfattr *fap, + struct smb_cred *scrp, short infolevel); +int smbfs_smb_qpathinfo(struct smbnode *np, struct smbfattr *fap, + struct smb_cred *scrp, short infolevel); +int smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen, + struct smbfattr *fap, struct smb_cred *scrp); + +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_setftime1(struct smbnode *np, uint16_t fid, + struct timespec *mtime, struct timespec *atime, + struct smb_cred *scrp); +int smbfs_smb_setfattrNT(struct smbnode *np, uint16_t fid, + uint32_t attr, 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); +int smbfs_smb_setpattr2(struct smbnode *np, uint32_t attr, + struct timespec *mtime, struct timespec *atime, + struct smb_cred *scrp); +int smbfs_smb_setpattrNT(struct smbnode *np, uint32_t attr, + struct timespec *mtime, struct timespec *atime, + 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)); + + if (op == SMB_LOCK_SHARED) + ltype |= SMB_LOCKING_ANDX_SHARED_LOCK; + 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_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + 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); + else + 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 + */ +int +smbfs_smb_getfattr( + struct smbnode *np, + struct smbfattr *fap, + struct smb_cred *scrp) +{ + int error; + + /* + * This lock is really only necessary for qfileinfo, + * but hopefully we use that most of the time. + * Lock may be writer (via open) or reader. + */ + ASSERT(np->r_lkserlock.count != 0); + + if (np->n_fidrefs) + error = smbfs_smb_qfileinfo(np, fap, scrp, 0); + else + error = smbfs_smb_qpathinfo(np, fap, scrp, 0); + + if (error == EINVAL) { + /* fallback */ + error = smbfs_smb_query_info(np, NULL, 0, fap, scrp); + } + +#if 0 /* Moved this part to caller. */ + if (!error && fap->fa_mtime.tv_sec == 0) + smbfs_attr_touchdir(dnp); +#endif + + return (error); +} + + +/* + * Nearly identical to smbfs_smb_qfileinfo (below). + * Please keep them in sync. + */ +int +smbfs_smb_qpathinfo(struct smbnode *np, struct smbfattr *fap, + struct smb_cred *scrp, short infolevel) +{ + 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 date, time, wattr; + uint64_t llongint, lsize; + uint32_t size, dattr; + +top: + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_PATH_INFORMATION, + 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; + } + mb_put_uint16le(mbp, infolevel); + mb_put_uint32le(mbp, 0); + /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */ + error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\'); + if (error) { + smb_t2_done(t2p); + return (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); + } + mdp = &t2p->t2_rdata; + svtz = vcp->vc_sopt.sv_tz; + switch (infolevel) { + case SMB_QFILEINFO_STANDARD: + timesok = 0; + md_get_uint16le(mdp, NULL); + md_get_uint16le(mdp, NULL); /* creation time */ + md_get_uint16le(mdp, &date); + md_get_uint16le(mdp, &time); /* access time */ + if (date || time) { + timesok++; + smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime); + } + md_get_uint16le(mdp, &date); + md_get_uint16le(mdp, &time); /* modify time */ + if (date || time) { + timesok++; + smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime); + } + md_get_uint32le(mdp, &size); + fap->fa_size = size; + md_get_uint32(mdp, NULL); /* allocation size */ + md_get_uint16le(mdp, &wattr); + fap->fa_attr = wattr; + break; + case SMB_QFILEINFO_ALL_INFO: + timesok = 0; + /* creation time (discard) */ + md_get_uint64(mdp, NULL); + /* last access time */ + md_get_uint64le(mdp, &llongint); + if (llongint) { + timesok++; + smb_time_NT2local(llongint, svtz, &fap->fa_atime); + } + /* last write time */ + md_get_uint64le(mdp, &llongint); + if (llongint) { + timesok++; + smb_time_NT2local(llongint, svtz, &fap->fa_mtime); + } + /* last change time */ + md_get_uint64le(mdp, &llongint); + if (llongint) { + timesok++; + smb_time_NT2local(llongint, svtz, &fap->fa_ctime); + } + /* attributes */ + md_get_uint32le(mdp, &dattr); + fap->fa_attr = dattr; + /* + * 4-Byte alignment - discard + * Specs doesn't talk about this. + */ + md_get_uint32le(mdp, NULL); + /* allocation size (discard) */ + md_get_uint64le(mdp, NULL); + /* File size */ + 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); +} + +/* + * Nearly identical to smbfs_smb_qpathinfo (above). + * Please keep them in sync. + */ +int +smbfs_smb_qfileinfo(struct smbnode *np, struct smbfattr *fap, + struct smb_cred *scrp, short infolevel) +{ + 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 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 (np->n_fid == SMB_FID_UNUSED) + return (EBADF); + +top: + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FILE_INFORMATION, + 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; + } + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, infolevel); + 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); + } + mdp = &t2p->t2_rdata; + svtz = vcp->vc_sopt.sv_tz; + switch (infolevel) { + case SMB_QFILEINFO_STANDARD: + timesok = 0; + md_get_uint16le(mdp, NULL); + md_get_uint16le(mdp, NULL); /* creation time */ + md_get_uint16le(mdp, &date); + md_get_uint16le(mdp, &time); /* access time */ + if (date || time) { + timesok++; + smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime); + } + md_get_uint16le(mdp, &date); + md_get_uint16le(mdp, &time); /* modify time */ + if (date || time) { + timesok++; + smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime); + } + md_get_uint32le(mdp, &size); + fap->fa_size = size; + md_get_uint32(mdp, NULL); /* allocation size */ + md_get_uint16le(mdp, &wattr); + fap->fa_attr = wattr; + break; + case SMB_QFILEINFO_ALL_INFO: + timesok = 0; + /* creation time (discard) */ + md_get_uint64(mdp, NULL); + /* last access time */ + md_get_uint64le(mdp, &llongint); + if (llongint) { + timesok++; + smb_time_NT2local(llongint, svtz, &fap->fa_atime); + } + /* last write time */ + md_get_uint64le(mdp, &llongint); + if (llongint) { + timesok++; + smb_time_NT2local(llongint, svtz, &fap->fa_mtime); + } + /* last change time */ + md_get_uint64le(mdp, &llongint); + if (llongint) { + timesok++; + smb_time_NT2local(llongint, svtz, &fap->fa_ctime); + } + /* attributes */ + md_get_uint32le(mdp, &dattr); + fap->fa_attr = dattr; + /* + * 4-Byte alignment - discard + * Specs doesn't talk about this. + */ + md_get_uint32le(mdp, NULL); + /* allocation size (discard) */ + md_get_uint64le(mdp, NULL); + /* File size */ + 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 + * Todo: show NT file streams as + * Solaris named attributes. + */ +#ifdef APPLE + +static char * +sfm2xattr(char *sfm) +{ + if (!strncasecmp(sfm, SFM_RESOURCEFORK_NAME, + sizeof (SFM_RESOURCEFORK_NAME))) + return (XATTR_RESOURCEFORK_NAME); + if (!strncasecmp(sfm, SFM_FINDERINFO_NAME, + sizeof (SFM_FINDERINFO_NAME))) + return (XATTR_FINDERINFO_NAME); + return (NULL); +} + +static int +smbfs_smb_undollardata(struct smbnode *np, struct smbfs_fctx *ctx) +{ + char *cp; + int len = strlen(SMB_DATASTREAM); + + if (!ctx->f_name) /* sanity check */ + goto bad; + if (ctx->f_nmlen < len + 1) /* "::$DATA" at a minimum */ + goto bad; + if (*ctx->f_name != ':') /* leading colon - "always" */ + goto bad; + cp = &ctx->f_name[ctx->f_nmlen - len]; /* point to 2nd colon */ + if (bcmp(cp, SMB_DATASTREAM, len)) + goto bad; + if (ctx->f_nmlen == len + 1) /* merely the data fork? */ + return (0); /* skip it */ + /* + * XXX here we should be calling KPI to validate the stream name + */ + if (ctx->f_nmlen >= 18 && + !(bcmp(ctx->f_name, ":com.apple.system.", 18) == 0)) + return (0); /* skip protected system attrs */ + if (ctx->f_nmlen - len > XATTR_MAXNAMELEN + 1) + goto bad; /* mustnt return more than 128 bytes */ + /* + * Un-count a colon and the $DATA, then the + * 2nd colon is replaced by a terminating null. + */ + ctx->f_nmlen -= len; + *cp = '\0'; + return (1); +bad: + SMBSDEBUG("file \"%.*s\" has bad stream \"%.*s\"\n", + np->n_nmlen, np->n_name, ctx->f_nmlen, ctx->f_name); + return (0); /* skip it */ +} + +PRIVSYM int +smbfs_smb_qstreaminfo(struct smbnode *np, struct smb_cred *scrp, + uio_t uio, size_t *sizep) +{ + struct smb_share *ssp = np->n_mount->smi_share; + struct smb_vc *vcp = SSTOVC(ssp); + struct smb_t2rq *t2p; + int error; + struct mbchain *mbp; + struct mdchain *mdp; + uint32_t next, nlen, used; + struct smbfs_fctx ctx; + + *sizep = 0; + ctx.f_ssp = ssp; + ctx.f_name = NULL; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_PATH_INFORMATION, + scrp, &t2p); + if (error) + return (error); + mbp = &t2p->t2_tparam; + mb_init(mbp); + /* + * SMB_QFILEINFO_STREAM_INFORMATION is an option to consider + * here. Samba declined to support the older info level with + * a comment claiming doing so caused a BSOD. + */ + mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO); + mb_put_uint32le(mbp, 0); + /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */ + error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\'); + if (error) + goto out; + t2p->t2_maxpcount = 2; + t2p->t2_maxdcount = vcp->vc_txmax; + error = smb_t2_request(t2p); + if (error) { + if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER) + error = ENOTSUP; + goto out; + } + mdp = &t2p->t2_rdata; + /* + * On a directory Windows is likely to return a zero data count. + * Check for that now to avoid EBADRPC from md_get_uint32le + */ + if (mdp->md_cur == NULL) + goto out; + do { + if ((error = md_get_uint32le(mdp, &next))) + goto out; + if ((error = md_get_uint32le(mdp, &nlen))) /* name length */ + goto out; + if ((error = md_get_uint64le(mdp, NULL))) /* stream size */ + goto out; + if ((error = md_get_uint64le(mdp, NULL))) /* allocated size */ + goto out; + /* + * Sanity check to limit DoS or buffer overrun attempts. + * The arbitrary 16384 is sufficient for all legit packets. + */ + if (nlen > 16384) { + SMBVDEBUG("huge name length in packet!\n"); + error = EBADRPC; + goto out; + } + ctx.f_name = kmem_zalloc(nlen, KM_SLEEP); + ctx.f_namesz = nlen; + if ((error = md_get_mem(mdp, ctx.f_name, nlen, MB_MSYSTEM))) + goto out; + /* + * skip pad bytes and/or tail of overlong name + */ + used = 4 + 4 + 8 + 8 + nlen; + if (next && next > used) { + if (next - used > 16384) { + SMBVDEBUG("huge offset in packet!\n"); + error = EBADRPC; + goto out; + } + md_get_mem(mdp, NULL, next - used, MB_MSYSTEM); + } + /* ignore a trailing null, not that we expect them */ + if (SMB_UNICODE_STRINGS(vcp)) { + if (nlen > 1 && !ctx.f_name[nlen - 1] && + !ctx.f_name[nlen - 2]) + nlen -= 2; + } else { + if (nlen && !ctx.f_name[nlen - 1]) + nlen -= 1; + } + ctx.f_nmlen = nlen; + smbfs_fname_tolocal(&ctx); /* converts from UCS2LE */ + /* + * We should now have a name in the form + * : <foo> :$DATA + * Where <foo> is UTF-8 w/o null termination + * If it isn't in that form we want to LOG it and skip it. + * Note we want to skip w/o logging the "data fork" entry, + * which is simply ::$DATA + * Otherwise we want to uiomove out <foo> with a null added. + */ + if (smbfs_smb_undollardata(np, &ctx)) { + char *s; + + /* the "+ 1" skips over the leading colon */ + s = sfm2xattr(ctx.f_name + 1); +#ifndef DUAL_EAS /* XXX */ + /* + * In Tiger Carbon still accesses dot-underscore files directly, so... + * For Tiger we preserve the SFM/Thursby AFP_* stream names rather + * than mapping them to com.apple.*. This means our copy engines + * will preserve SFM/Thursby resource-fork and finder-info. + */ + s = NULL; +#endif + if (s) + ctx.f_nmlen = strlen(s) + 1; + else + s = ctx.f_name + 1; + if (uio) + uiomove(s, ctx.f_nmlen, uio); + else + *sizep += ctx.f_nmlen; + } + kmem_free(ctx.f_name, ctx.f_namesz); + ctx.f_name = NULL; + } while (next && !error); +out: + if (ctx.f_name) + kmem_free(ctx.f_name, ctx.f_namesz); + smb_t2_done(t2p); + return (error); +} + +#endif /* APPLE */ + +int +smbfs_smb_qfsattr(struct smb_share *ssp, uint32_t *attrp, + struct smb_cred *scrp) +{ + struct smb_t2rq *t2p; + struct mbchain *mbp; + struct mdchain *mdp; + uint32_t nlen; + int error; + char *fs_name; /* will malloc whatever the size is */ + struct smbfs_fctx ctx; + + 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) { + smb_t2_done(t2p); + return (error); + } + mdp = &t2p->t2_rdata; + md_get_uint32le(mdp, attrp); + md_get_uint32le(mdp, &ssp->ss_maxfilenamelen); + md_get_uint32le(mdp, &nlen); /* fs name length */ + if (ssp->ss_fsname == NULL && nlen) { + ctx.f_ssp = ssp; + ctx.f_name = kmem_alloc(nlen, KM_SLEEP); + ctx.f_namesz = nlen; + md_get_mem(mdp, ctx.f_name, nlen, MB_MSYSTEM); + ctx.f_nmlen = nlen; + smbfs_fname_tolocal(&ctx); + fs_name = kmem_alloc(ctx.f_nmlen+1, KM_SLEEP); + bcopy(ctx.f_name, fs_name, ctx.f_nmlen); + fs_name[ctx.f_nmlen] = '\0'; + ssp->ss_fsname = fs_name; + kmem_free(ctx.f_name, ctx.f_namesz); + /* + * If fs_name isn't NTFS they probably require resume keys. + * This is another example of the client trying to fix a server + * bug. This code uses the logic created by PR-3983209. See + * long block comment in smbfs_smb_findnextLM2. + */ + if (strcmp(fs_name, "NTFS")) { + SMB_SS_LOCK(ssp); + ssp->ss_flags |= SMBS_RESUMEKEYS; + SMB_SS_UNLOCK(ssp); + } + SMBVDEBUG("(fyi) share '%s', attr 0x%x, maxfilename %d\n", + ssp->ss_fsname, *attrp, ssp->ss_maxfilenamelen); + } + smb_t2_done(t2p); + return (0); +} + +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) { + smb_t2_done(t2p); + return (error); + } + mdp = &t2p->t2_rdata; + md_get_uint32(mdp, NULL); /* fs id */ + md_get_uint32le(mdp, &bpu); + md_get_uint32le(mdp, &units); + md_get_uint32le(mdp, &funits); + md_get_uint16le(mdp, &bsize); + 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 */ + 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) { + smb_rq_done(rqp); + return (error); + } + smb_rq_getreply(rqp, &mdp); + md_get_uint16le(mdp, &units); + md_get_uint16le(mdp, &bpu); + md_get_uint16le(mdp, &bsize); + md_get_uint16le(mdp, &funits); + 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 */ + 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_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + 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_uint32le(mbp, 0); /* XXX should be 16 not 32(?) */ + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_uint64le(mbp, newsize); + mb_put_uint32le(mbp, 0); /* padding */ + mb_put_uint16le(mbp, 0); + t2p->t2_maxpcount = 2; + t2p->t2_maxdcount = 0; + error = smb_t2_request(t2p); + smb_t2_done(t2p); + return (error); +} + +/*ARGSUSED*/ +int +smbfs_smb_t2rename(struct smbnode *np, struct smbnode *tdnp, + const char *tname, int tnmlen, struct smb_cred *scrp, int overwrite) +{ + struct smb_t2rq *t2p; + struct smb_share *ssp = np->n_mount->smi_share; + struct smb_vc *vcp = SSTOVC(ssp); + struct mbchain *mbp; + int32_t *ucslenp; + int error, cerror; + uint16_t fid = 0; + + /* Shared lock for n_fid use below. */ + ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + + 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 (error) + return (error); + if (tdnp) { + error = smbfs_smb_tmpopen(tdnp, SA_RIGHT_FILE_READ_DATA, scrp, + &fid); + if (error) + goto exit; + } + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + 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); + mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); /* base for tname */ + mb_put_uint16le(mbp, 0); /* part of a 32bit fid? */ + ucslenp = (int32_t *)mb_reserve(mbp, sizeof (int32_t)); + mbp->mb_count = 0; + error = smb_put_dstring(mbp, vcp, tname, SMB_CS_NONE); + if (error) + goto exit; + mbp->mb_count--; /* don't count the null */ + *ucslenp = htolel(mbp->mb_count); + t2p->t2_maxpcount = 2; + t2p->t2_maxdcount = 0; + error = smb_t2_request(t2p); +exit: + if (fid) { + cerror = smbfs_smb_tmpclose(tdnp, fid, scrp); + if (cerror) + SMBERROR("error %d closing fid %d\n", cerror, fid); + } + smb_t2_done(t2p); + return (error); +} + +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; + + /* 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->r_count == 0) + return (0); /* not open */ + if (np->r_vnode->v_type != VREG) + return (0); /* not a file */ + + 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_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + 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) +{ + struct smb_share *ssp = np->n_mount->smi_share; + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + int error; + + /* + * 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 we have SMB_CAP_LARGE_FILES, the above + * should have worked. XXX: Don't fallback? + * XXX: Or fallback on specific errors? + */ + if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) { + SMBVDEBUG("Have CAP_LARGE but _seteof error=%d\n", error); + } + + /* + * 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_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + 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); +} + +int +smbfs_smb_query_info(struct smbnode *np, const char *name, int nmlen, + struct smbfattr *fap, 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); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, + name, &nmlen, '\\'); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + break; + smb_rq_getreply(rqp, &mdp); + if (md_get_uint8(mdp, &wc) != 0 || wc != 10) { + error = EBADRPC; + break; + } + 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 */ + if (longint) /* avoid bogus zero returns */ + smb_time_server2local(longint, + SSTOVC(ssp)->vc_sopt.sv_tz, &fap->fa_mtime); + md_get_uint32le(mdp, &longint); + fap->fa_size = longint; + /*LINTED*/ + } while (0); + smb_rq_done(rqp); + return (error); +} + +int +smbfs_smb_setpattr(struct smbnode *np, 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); + int error; + + /* + * This is the logic that was in smbfs_vnops.c + */ + if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) && + (vcp->vc_flags & SMBV_NT4) == 0) { + /* + * NT4 doesn't understand "NT" style SMBs; + * for NT4 we use the old SET_PATH_INFO + * XXX Actually, I think the issue is that + * NT requires an open handle for this. + */ + error = smbfs_smb_setpattrNT(np, + attr, mtime, atime, scrp); + if (error != EBADRPC) + return (error); + + /* NT4 response, remember */ + SMB_VC_LOCK(vcp); + vcp->vc_flags |= SMBV_NT4; + SMB_VC_UNLOCK(vcp); + } + + if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) { + error = smbfs_smb_setpattr2(np, + attr, mtime, atime, scrp); + } else { + error = smbfs_smb_setpattr1(np, NULL, 0, + attr, mtime, scrp); + } + + 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); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), np, name, &len, '\\'); + if (error) + break; + 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 */ + } + mb_put_uint8(mbp, 0); + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + if (error) + break; + /*LINTED*/ + } while (0); + 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); +} + +/* + * Note, win95 doesn't support this call. + */ +int +smbfs_smb_setpattr2(struct smbnode *np, 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; + uint16_t date, time; + int error, tzoff; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION, + scrp, &t2p); + if (error) + return (error); + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_uint16le(mbp, SMB_SFILEINFO_STANDARD); + mb_put_uint32le(mbp, 0); /* MBZ */ + /* mb_put_uint8(mbp, SMB_DT_ASCII); specs incorrect */ + error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\'); + if (error) { + smb_t2_done(t2p); + return (error); + } + tzoff = vcp->vc_sopt.sv_tz; + mbp = &t2p->t2_tdata; + mb_init(mbp); + 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); + mb_put_uint32le(mbp, 0); /* file size */ + mb_put_uint32le(mbp, 0); /* allocation unit size */ + mb_put_uint16le(mbp, attr); /* DOS attr */ + mb_put_uint32le(mbp, 0); /* EA size */ + t2p->t2_maxpcount = 5 * 2; + t2p->t2_maxdcount = vcp->vc_txmax; + error = smb_t2_request(t2p); + smb_t2_done(t2p); + return (error); +} + +/* + * *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_setpattrNT(struct smbnode *np, 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, tzoff; + /* 64 bit value for Jan 1 1980 */ + PRIVSYM uint64_t DIFF1980TO1601 = 11960035200ULL*10000000ULL; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION, + scrp, &t2p); + if (error) + return (error); + mbp = &t2p->t2_tparam; + mb_init(mbp); + if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) + mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION); + else + mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFO); + mb_put_uint32le(mbp, 0); /* MBZ */ + /* mb_put_uint8(mbp, SMB_DT_ASCII); specs incorrect */ + error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, '\\'); + if (error) { + smb_t2_done(t2p); + return (error); + } + tzoff = vcp->vc_sopt.sv_tz; + + /* do we know it won't support dates < 1980? */ + if (!(ssp->ss_flags & SMBS_1980)) { + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_uint64le(mbp, 0); /* creation time */ + if (atime) { + smb_time_local2NT(atime, tzoff, &tm); + } else + tm = 0; + mb_put_uint64le(mbp, tm); /* access time */ + if (mtime) { + smb_time_local2NT(mtime, tzoff, &tm); + } else + tm = 0; + mb_put_uint64le(mbp, tm); /* last write time */ + mb_put_uint64le(mbp, tm); /* change time */ + mb_put_uint32le(mbp, attr); /* attr */ + mb_put_uint32le(mbp, 0); /* undocumented padding */ + t2p->t2_maxpcount = 24; + t2p->t2_maxdcount = 56; + error = smb_t2_request(t2p); + } + /* + * "invalid argument" error probably means it's a + * FAT drive that doesn't accept dates earlier + * than 1980, so adjust dates and retry. If the + * 1980 flag is on we fell thru the if {} above + */ + if ((ssp->ss_flags & SMBS_1980) || (error == EINVAL)) { + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_uint64le(mbp, 0); /* creation time */ + if (atime) { + smb_time_local2NT(atime, tzoff, &tm); + if (tm < DIFF1980TO1601) + tm = DIFF1980TO1601; + } else + tm = 0; + mb_put_uint64le(mbp, tm); /* access time */ + if (mtime) { + smb_time_local2NT(mtime, tzoff, &tm); + if (tm < DIFF1980TO1601) + tm = DIFF1980TO1601; + } else + tm = 0; + mb_put_uint64le(mbp, tm); /* last write time */ + mb_put_uint64le(mbp, tm); /* change time */ + mb_put_uint32le(mbp, attr); /* attr */ + mb_put_uint32le(mbp, 0); /* undocumented padding */ + t2p->t2_maxpcount = 24; + t2p->t2_maxdcount = 56; + error = smb_t2_request(t2p); + + /* if this worked set flag to do the right thing next time */ + if (!(error)) { + SMB_SS_LOCK(ssp); + ssp->ss_flags |= SMBS_1980; + SMB_SS_UNLOCK(ssp); + } + } + smb_t2_done(t2p); + return (error); +} + +int +smbfs_smb_setfattr(struct smbnode *np, uint16_t fid, + 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); + int error; + + /* + * This is the logic that was in smbfs_vnops.c + * Might not be quite right for older dialects. + * (XXX: What about the DOS attributes?) + */ + if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) + error = smbfs_smb_setfattrNT(np, fid, + np->n_dosattr, mtime, atime, scrp); + else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) + error = smbfs_smb_setftime1(np, fid, + mtime, atime, scrp); + else + 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_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + 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. + * Looks like this call can be used only if CAP_NT_SMBS bit is on. + */ +int +smbfs_smb_setfattrNT(struct smbnode *np, uint16_t fid, + 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, svtz; + + error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION, + scrp, &t2p); + if (error) + return (error); + svtz = SSTOVC(ssp)->vc_sopt.sv_tz; + mbp = &t2p->t2_tparam; + mb_init(mbp); + mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) + mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION); + else + mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFO); + mb_put_uint32le(mbp, 0); /* XXX should be 16 not 32(?) */ + mbp = &t2p->t2_tdata; + mb_init(mbp); + mb_put_uint64le(mbp, 0); /* creation time */ + if (atime) { + smb_time_local2NT(atime, svtz, &tm); + } else + tm = 0; + mb_put_uint64le(mbp, tm); /* access time */ + if (mtime) { + smb_time_local2NT(mtime, svtz, &tm); + } else + tm = 0; + mb_put_uint64le(mbp, tm); /* last write time */ + mb_put_uint64le(mbp, tm); /* 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); + smb_t2_done(t2p); + return (error); +} + +/* + * Modern create/open of file or directory. + * + * If disp is ..._DISP_OPEN, or ...DISP_OPEN_IF, or... + * then this is an open attempt, and: + * If xattr then name is the stream to be opened at np, + * Else np should be opened. + * ...we won't touch *fidp, + * ...we will set or clear *attrcacheupdated. + * Else this is a creation attempt, and: + * If xattr then name is the stream to create at np, + * Else name is the thing to create under directory np. + * ...we will return *fidp, + * ...we won't touch *attrcacheupdated. + * + * Note, We use: disp = ...OPEN_IF, ...OVERWRITE_IF, etc. + * now too, which may or may not create a new object. + */ +int +smbfs_smb_ntcreatex(struct smbnode *np, uint32_t rights, + struct smb_cred *scrp, enum vtype vt, + int *attrcacheupdated, uint16_t *fidp, + const char *name, int nmlen, uint32_t disp, int xattr, + len_t *sizep, uint32_t *rightsp) +{ + 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 fap; + uint8_t wc; + uint32_t longint, createact, createopt, efa; + uint64_t llongint; + int error; + uint16_t fid, *namelenp; + + /* + * Set the File attributes and Create options. + * WinXP uses EFA_NORMAL in all of these cases. + */ + createopt = (vt == VDIR) ? + NTCREATEX_OPTIONS_DIRECTORY : + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + efa = SMB_EFA_NORMAL; + if (disp != NTCREATEX_DISP_OPEN && !xattr) { + if (name && *name == '.') + efa = SMB_EFA_HIDDEN; + } + + gethrestime(&fap.fa_reqtime); + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_NT_CREATE_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); /* offset to next command (none) */ + mb_put_uint8(mbp, 0); /* MBZ */ + namelenp = (uint16_t *)mb_reserve(mbp, sizeof (uint16_t)); + /* + * XP to a W2K Server does not use NTCREATEX_FLAGS_OPEN_DIRECTORY + * for creating nor for opening a directory. Samba ignores the bit. + */ +#if 0 /* causes sharing violation when making dir on W2K! */ + mb_put_uint32le(mbp, vt == VDIR ? NTCREATEX_FLAGS_OPEN_DIRECTORY : 0); +#else + mb_put_uint32le(mbp, 0); /* NTCREATEX_FLAGS_* */ +#endif + mb_put_uint32le(mbp, 0); /* FID - basis for path if not root */ + mb_put_uint32le(mbp, rights); + mb_put_uint64le(mbp, 0); /* "initial allocation size" */ + mb_put_uint32le(mbp, efa); + mb_put_uint32le(mbp, NTCREATEX_SHARE_ACCESS_ALL); + mb_put_uint32le(mbp, disp); + mb_put_uint32le(mbp, createopt); + mb_put_uint32le(mbp, NTCREATEX_IMPERSONATION_IMPERSONATION); /* (?) */ + mb_put_uint8(mbp, 0); /* security flags (?) */ + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + do { + if (name == NULL) + nmlen = 0; + error = smbfs_fullpath(mbp, vcp, np, name, &nmlen, + xattr ? ':' : '\\'); + if (error) + break; + *namelenp = htoles(nmlen); /* includes null */ + 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) + break; + smb_rq_getreply(rqp, &mdp); + /* + * spec says 26 for word count, but 34 words are defined + * and observed from win2000 + */ + if (md_get_uint8(mdp, &wc) != 0 || + (wc != 26 && wc != 34 && wc != 42)) { + error = EBADRPC; + break; + } + md_get_uint8(mdp, NULL); /* secondary cmd */ + md_get_uint8(mdp, NULL); /* mbz */ + md_get_uint16le(mdp, NULL); /* andxoffset */ + md_get_uint8(mdp, NULL); /* oplock lvl granted */ + md_get_uint16(mdp, &fid); /* yes, leaving it LE */ + md_get_uint32le(mdp, &createact); /* create_action */ + md_get_uint64le(mdp, &llongint); /* creation time */ + md_get_uint64le(mdp, &llongint); /* access time */ + if (llongint) /* avoid bogus 0 time (on FAT roots) */ + smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz, + &fap.fa_atime); + md_get_uint64le(mdp, &llongint); /* write time */ + if (llongint) /* avoid bogus 0 time (on FAT roots) */ + smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz, + &fap.fa_mtime); + md_get_uint64le(mdp, &llongint); /* change time */ + if (llongint) /* avoid bogus 0 time (on FAT roots) */ + smb_time_NT2local(llongint, vcp->vc_sopt.sv_tz, + &fap.fa_ctime); + md_get_uint32le(mdp, &longint); /* attributes */ + fap.fa_attr = longint; + md_get_uint64le(mdp, NULL); /* allocation size */ + md_get_uint64le(mdp, &llongint); /* EOF */ + fap.fa_size = llongint; + if (sizep) + *sizep = fap.fa_size; + md_get_uint16le(mdp, NULL); /* file type */ + md_get_uint16le(mdp, NULL); /* device state */ + md_get_uint8(mdp, NULL); /* directory (boolean) */ + /*LINTED*/ + } while (0); + smb_rq_done(rqp); + if (error) + return (error); + if (fidp) + *fidp = fid; + if (rightsp) + *rightsp = rights; + /* + * Is it possible that we have cached attributes? + * Assume "not cached" if we created the object. + */ + if (createact == NTCREATEX_ACTION_CREATED || xattr) + goto uncached; + if (attrcacheupdated) + *attrcacheupdated = 0; + /* + * Update the cached attributes if they are still valid + * in the cache and if nothing has changed. + */ + if (np->r_vnode == NULL) + goto uncached; + if (smbfs_attr_cachelookup(np->r_vnode, NULL) != 0) + goto uncached; /* the cached attributes are not valid */ + if (fap.fa_size != np->n_size) + goto uncached; /* the size is different */ + if (fap.fa_attr != np->n_dosattr) + goto uncached; /* the attrs are different */ + /* + * fap.fa_mtime is in two second increments while np->n_mtime + * may be in one second increments, so comparing the times is + * somewhat sloppy. + * + * XXX: true fap.fa_mtime resolution must depend upon server's + * local filesystem and is thus indeterminate... XXX ...TBD how that + * affects this code... note wire resolution here is 100ns versus + * 1sec down in smbfs_smb_oldopen(SMB_COM_OPEN) + */ + if (fap.fa_mtime.tv_sec != np->n_mtime.tv_sec && + fap.fa_mtime.tv_sec != np->n_mtime.tv_sec - 1 && + fap.fa_mtime.tv_sec != np->n_mtime.tv_sec + 1) + goto uncached; /* the mod time is different */ + + fap.fa_mtime.tv_sec = np->n_mtime.tv_sec; /* keep higher res time */ + smbfs_attr_cacheenter(np->r_vnode, &fap); + if (attrcacheupdated) + *attrcacheupdated = 1; +uncached: + return (0); +} + +static uint32_t +smb_mode2rights(int mode) +{ + mode = mode & SMB_AM_OPENMODE; + + switch (mode) { + case SMB_AM_OPENREAD: + return (GENERIC_RIGHT_READ_ACCESS); + case SMB_AM_OPENWRITE: + return (GENERIC_RIGHT_WRITE_ACCESS); + case SMB_AM_OPENRW: + return (GENERIC_RIGHT_ALL_ACCESS); + case SMB_AM_OPENEXEC: + return (GENERIC_RIGHT_EXECUTE_ACCESS); + } + return (0); +} + +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 | + GENERIC_RIGHT_ALL_ACCESS | GENERIC_RIGHT_WRITE_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 | + GENERIC_RIGHT_ALL_ACCESS | GENERIC_RIGHT_READ_ACCESS)) + accmode = (accmode == SMB_AM_OPENEXEC) ? SMB_AM_OPENREAD + : SMB_AM_OPENRW; + return (accmode); +} + +static int +smbfs_smb_oldopen(struct smbnode *np, int accmode, struct smb_cred *scrp, + int *attrcacheupdated, uint16_t *fidp, const char *name, + int nmlen, int xattr, len_t *sizep, uint32_t *rightsp) +{ + 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 fap; + uint8_t wc; + uint16_t fid, wattr, grantedmode; + uint32_t longint; + int error; + + /* + * 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; + + gethrestime(&fap.fa_reqtime); + 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); + do { + error = smbfs_fullpath(mbp, vcp, np, name, &nmlen, + xattr ? ':' : '\\'); + if (error) + break; + 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) + break; + 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) + */ + if (md_get_uint8(mdp, &wc) != 0 || (wc != 7 && wc != 15)) { + error = EBADRPC; + break; + } + md_get_uint16(mdp, &fid); /* yes, we leave it LE */ + 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 */ + if (longint) /* avoid bogus zero returns */ + smb_time_server2local(longint, vcp->vc_sopt.sv_tz, + &fap.fa_mtime); + md_get_uint32le(mdp, &longint); + fap.fa_size = longint; + if (sizep) + *sizep = fap.fa_size; + md_get_uint16le(mdp, &grantedmode); + /*LINTED*/ + } while (0); + smb_rq_done(rqp); + if (error) + return (error); + if (fidp) + *fidp = fid; + if (xattr) + goto uncached; + if (rightsp) + *rightsp = smb_mode2rights(grantedmode); + if (attrcacheupdated) + *attrcacheupdated = 0; + /* + * Update the cached attributes if they are still valid + * in the cache and if nothing has changed. + * Note that this won't ever update if the file size is + * greater than the 32-bits returned by SMB_COM_OPEN. + * For 64-bit file sizes, SMB_COM_NT_CREATE_ANDX must + * be used instead of SMB_COM_OPEN. + */ + if (np->r_vnode == NULL) + goto uncached; + if (smbfs_attr_cachelookup(np->r_vnode, NULL) != 0) + goto uncached; /* the cached attributes are not valid */ + if (fap.fa_size != np->n_size) + goto uncached; /* the size is different */ + if (fap.fa_attr != np->n_dosattr) + goto uncached; /* the attrs are different */ + /* + * fap.fa_mtime is in two second increments while np->n_mtime + * may be in one second increments, so comparing the times is + * somewhat sloppy. + */ + if (fap.fa_mtime.tv_sec != np->n_mtime.tv_sec && + fap.fa_mtime.tv_sec != np->n_mtime.tv_sec - 1 && + fap.fa_mtime.tv_sec != np->n_mtime.tv_sec + 1) + goto uncached; /* the mod time is different */ + + fap.fa_mtime.tv_sec = np->n_mtime.tv_sec; /* keep higher res time */ + smbfs_attr_cacheenter(np->r_vnode, &fap); + if (attrcacheupdated) + *attrcacheupdated = 1; +uncached: + return (0); +} + +int +smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, struct smb_cred *scrp, + uint16_t *fidp) +{ + struct smb_vc *vcp = SSTOVC(np->n_mount->smi_share); + enum vtype vt = VREG; + int error; + + /* Shared lock for n_fid use below. */ + ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + + mutex_enter(&np->r_statelock); + if (np->n_fidrefs && (rights & np->n_rights) == rights) { + np->n_fidrefs++; + *fidp = np->n_fid; + mutex_exit(&np->r_statelock); + return (0); + } + mutex_exit(&np->r_statelock); + + if (!(vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) { + int mode = smb_rights2mode(rights); + error = smbfs_smb_oldopen(np, mode, scrp, + NULL, fidp, NULL, 0, 0, NULL, NULL); + } else { + if (SMBTOV(np)) + vt = SMBTOV(np)->v_type; + error = smbfs_smb_ntcreatex(np, rights, scrp, vt, + NULL, fidp, NULL, 0, NTCREATEX_DISP_OPEN, 0, + NULL, NULL); + } + + if (*fidp == np->n_fid) { + /* + * Oh no, the server gave us the same FID again? + * This will cause us to close np->n_fid early! + */ + SMBVDEBUG("duplicate fid: 0x%x\n", *fidp); + } + + return (error); +} + +int +smbfs_smb_tmpclose(struct smbnode *np, uint16_t fid, struct smb_cred *scrp) +{ + 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); +} + +int +smbfs_smb_open(struct smbnode *np, uint32_t rights, struct smb_cred *scrp, + int *attrcacheupdated, uint16_t *fidp, const char *name, + int nmlen, int xattr, len_t *sizep, uint32_t *rightsp) +{ + int error; + struct smb_share *ssp = np->n_mount->smi_share; + struct smb_vc *vcp = SSTOVC(ssp); + enum vtype vt = VREG; + + if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { + if (SMBTOV(np)) + vt = SMBTOV(np)->v_type; + error = smbfs_smb_ntcreatex(np, rights, scrp, vt, + attrcacheupdated, fidp, name, nmlen, + NTCREATEX_DISP_OPEN, xattr, sizep, rightsp); + } else { + error = smbfs_smb_oldopen(np, smb_rights2mode(rights), scrp, + attrcacheupdated, fidp, name, nmlen, xattr, sizep, rightsp); + } +#if 0 /* let caller do this */ + if (!error && !name) + np->n_fidrefs++; +#endif + return (error); +} + +int +smbfs_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime, + struct smb_cred *scrp) +{ + struct smb_rq rq, *rqp = &rq; + struct mbchain *mbp; + long time; + int error; + + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scrp); + if (error) + return (error); + smb_rq_getrequest(rqp, &mbp); + smb_rq_wstart(rqp); + mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + if (mtime) { + smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &time); + } else + time = 0; + mb_put_uint32le(mbp, time); + smb_rq_wend(rqp); + smb_rq_bstart(rqp); + smb_rq_bend(rqp); + + /* + * We don't really care about the result here, but we + * do need to make sure we send this out, or we could + * "leak" open file handles on interrupt or timeout. + * The NOINTR_SEND flag makes this request immune to + * interrupt or timeout until the send is done. + */ + rqp->sr_flags |= SMBR_NOINTR_SEND; + error = smb_rq_simple(rqp); + smb_rq_done(rqp); + /* + * 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); +} + +static int +smbfs_smb_oldcreate(struct smbnode *dnp, const char *name, int nmlen, + struct smb_cred *scrp, uint16_t *fidp, int xattr) +{ + 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) { + 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) { + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc == 1) + md_get_uint16(mdp, fidp); + else + error = EBADRPC; + } + } + smb_rq_done(rqp); + return (error); +} + +int +smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen, + struct smb_cred *scrp, uint16_t *fidp, uint32_t disp, int xattr) +{ + struct smb_vc *vcp = SSTOVC(dnp->n_mount->smi_share); + + /* + * 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) { + return (smbfs_smb_ntcreatex(dnp, SA_RIGHT_FILE_WRITE_DATA, + scrp, VREG, NULL, fidp, name, nmlen, disp, xattr, + NULL, NULL)); + } else + return (smbfs_smb_oldcreate(dnp, name, nmlen, scrp, fidp, + xattr)); +} + +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; + + 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) +{ + struct smb_rq rq, *rqp = &rq; + struct smb_share *ssp = src->n_mount->smi_share; + struct mbchain *mbp; + int error; + uint16_t fa; + + 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); + mb_put_uint8(mbp, SMB_DT_ASCII); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, '\\'); + if (error) + break; + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen, + '\\'); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + /*LINTED*/ + } while (0); + 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); + do { + error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, NULL, '\\'); + if (error) + break; + mb_put_uint8(mbp, SMB_DT_ASCII); + error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, &tnmlen, + '\\'); + if (error) + break; + smb_rq_bend(rqp); + error = smb_rq_simple(rqp); + /*LINTED*/ + } while (0); + 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); + } + smb_rq_done(rqp); + return (error); +} + +int +smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len, + struct smb_cred *scrp) +{ + struct smb_share *ssp = dnp->n_mount->smi_share; + uint16_t fid; + int error; + + /* + * 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 (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { + error = smbfs_smb_ntcreatex(dnp, SA_RIGHT_FILE_READ_DATA, + scrp, VDIR, NULL, &fid, name, len, + NTCREATEX_DISP_CREATE, 0, NULL, NULL); + if (error) + return (error); + error = smbfs_smb_close(ssp, fid, NULL, scrp); + if (error) + SMBERROR("error %d closing fid %d\n", error, fid); + return (0); + } else + return (smbfs_smb_oldmkdir(dnp, name, len, scrp)); +} + +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, NULL, '\\'); + 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 len, 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) { + len = ctx->f_wclen; + error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, + &len, '\\'); + 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); + md_get_uint8(mdp, &wc); + if (wc != 1) + return (iseof ? ENOENT : EBADRPC); + md_get_uint16le(mdp, &ec); + if (ec == 0) + return (ENOENT); + ctx->f_ecnt = ec; + md_get_uint16le(mdp, &bc); + if (bc < 3) + return (EBADRPC); + bc -= 3; + md_get_uint8(mdp, &bt); + if (bt != SMB_DT_VARIABLE) + return (EBADRPC); + md_get_uint16le(mdp, &dlen); + 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, struct smb_cred *scrp) +{ + /* #pragma unused(dnp, scrp) */ + 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); + ctx->f_attr.fa_reqtime = ts; + } + 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; + 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 + */ +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 tw, flags; + int len, error; + + if (ctx->f_t2) { + smb_t2_done(ctx->f_t2); + ctx->f_t2 = NULL; + } + ctx->f_flags &= ~SMBFS_RDD_GOTRNAME; + 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); + /* mb_put_uint8(mbp, SMB_DT_ASCII); specs? hah! */ + len = ctx->f_wclen; + error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, + &len, '\\'); + 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_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, ctx->f_limit); + mb_put_uint16le(mbp, ctx->f_infolevel); + if (ctx->f_ssp->ss_flags & SMBS_RESUMEKEYS) { + mb_put_uint32le(mbp, ctx->f_rkey); + } else + mb_put_uint32le(mbp, 0); + mb_put_uint16le(mbp, flags); + 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); +#if 0 + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 200 * 1000 * 1000; /* 200ms */ + if (vcp->vc_flags & SMBC_WIN95) { + /* + * some implementations suggests to sleep here + * for 200ms, due to the bug in the Win95. + * I've didn't notice any problem, but put code + * for it. + */ + msleep(&flags, 0, PVFS, "fix95", &ts); + } +#endif + } + t2p->t2_maxpcount = 5 * 2; + t2p->t2_maxdcount = vcp->vc_txmax; + error = smb_t2_request(t2p); + if (error) + return (error); + mdp = &t2p->t2_rparam; + if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { + if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0) + return (error); + ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; + } + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return (error); + ctx->f_ecnt = tw; /* search count - # entries returned */ + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return (error); + /* + * tw now is the "end of search" flag. against an XP server tw + * 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 search count. + */ + if (tw || ctx->f_ecnt == 0) + ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE; + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return (error); + if ((error = md_get_uint16le(mdp, &tw)) != 0) + return (error); + if (ctx->f_ecnt == 0) + return (ENOENT); + ctx->f_rnameofs = tw; + mdp = &t2p->t2_rdata; + if (mdp->md_top == NULL) { + SMBVDEBUG("ecnt = %d, but data is NULL\n", ctx->f_ecnt); + return (ENOENT); + } +#ifdef APPLE + if (mdp->md_top->m_len == 0) { + printf("bug: ecnt = %d, but m_len = 0 and m_next = %p " + "(please report)\n", ctx->f_ecnt, mbp->mb_top->m_next); + return (ENOENT); + } +#endif + ctx->f_eofs = 0; + return (0); +} + +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_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM); + 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, struct smb_cred *scrp) +{ + ctx->f_namesz = SMB_MAXFNAMELEN; + if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) + ctx->f_namesz *= 2; + ctx->f_name = kmem_zalloc(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, otw; + struct timespec ts; + +again: + otw = 0; /* nothing sent Over The Wire (yet) */ + 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_attr.fa_reqtime = ts; + ctx->f_otws++; + otw = 1; + } + 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 */ + 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_uint32(mdp, NULL); /* allocation size */ + md_get_uint16le(mdp, &wattr); + ctx->f_attr.fa_attr = wattr; + md_get_uint8(mdp, &tb); + 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_uint64(mdp, NULL); /* creation time */ + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_atime); + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_mtime); + md_get_uint64le(mdp, &llongint); + smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_ctime); + md_get_uint64le(mdp, &llongint); /* file size */ + ctx->f_attr.fa_size = llongint; + md_get_uint64(mdp, NULL); /* real size (should use) */ + /* freebsd bug: fa_attr endian bug */ + md_get_uint32le(mdp, &dattr); /* extended file attributes */ + ctx->f_attr.fa_attr = dattr; + md_get_uint32le(mdp, &size); /* name len */ + 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) + */ + md_get_mem(mdp, NULL, 30, MB_MSYSTEM); + 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); + if (ctx->f_name) + kmem_free(ctx->f_name, ctx->f_namesz); + cp = ctx->f_name = kmem_alloc(nmlen, KM_SLEEP); + ctx->f_namesz = nmlen; + error = md_get_mem(mdp, cp, nmlen, MB_MSYSTEM); + if (error) + return (error); + if (next) { + cnt = next - nmlen - fxsz; + if (cnt > 0) + md_get_mem(mdp, NULL, cnt, MB_MSYSTEM); + else if (cnt < 0) { + SMBVDEBUG("out of sync\n"); + return (EBADRPC); + } + } + /* 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) + return (EBADRPC); + + /* + * Ref radar 3983209. On a find-next we expect a server will + * 1) if the continue bit is set, use the server's idea of current loc, + * 2) else if the resume key is non-zero, use that location, + * 3) else if the resume name is set, use that location, + * 4) else use the server's idea of current location. + * + * Current NetApps don't do that. If we send no continue bit, a zero + * resume key, and a resume name, the NetApp ignores the resume name + * and acts on the (zero) resume key, sending back the start of the + * directory again. Panther doesn't expose the netapp bug; Panther used + * the continue bit, but that was changed for 2866172. Win2000 as a + * client also relies upon the resume name, but they request a very + * large number of files, so the bug would be seen only with very + * large directories. + * + * Our fix is to notice if the second OTW op (the first find-next) + * returns, in the first filename, the same filename we got back + * at the start of the first OTW (the find-first). In that case + * we've detected the server bug and set SMBS_RESUMEKEYS, causing us + * to send non-zero resume keys henceforth. + * + * Caveat: if there's a netapp so old it doesn't negotiate NTLM 0.12 + * then we get no resume keys so f_rkey stays zero and this "fix" + * changes nothing. + * + * Note due to a similar problem (4051871) we also set SMBS_RESUMEKEYS + * for FAT volumes, at mount time. + */ + if (otw && !(ctx->f_ssp->ss_flags & SMBS_RESUMEKEYS)) { + if (ctx->f_otws == 1) { + ctx->f_firstnmlen = nmlen; + ctx->f_firstnm = kmem_alloc(nmlen, KM_SLEEP); + bcopy(ctx->f_name, ctx->f_firstnm, nmlen); + } else if (ctx->f_otws == 2 && nmlen == ctx->f_firstnmlen && + !(bcmp(ctx->f_name, ctx->f_firstnm, nmlen) == 0)) { + struct smb_share *ssp = ctx->f_ssp; + SMBERROR( + "server resume_name bug seen; using resume keys\n"); + SMB_SS_LOCK(ssp); + ssp->ss_flags |= SMBS_RESUMEKEYS; + SMB_SS_UNLOCK(ssp); + ctx->f_ecnt = 0; + goto again; /* must redo last otw op! */ + } + } + ctx->f_rkey = resumekey; + + next = ctx->f_eofs + recsz; + if (ctx->f_rnameofs && + (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 && + (ctx->f_rnameofs >= ctx->f_eofs && + ctx->f_rnameofs < (int)next)) { + /* + * Server needs a resume filename. + */ + 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_flags |= SMBFS_RDD_GOTRNAME; + } + ctx->f_nmlen = nmlen; + ctx->f_eofs = next; + ctx->f_ecnt--; + ctx->f_left--; + return (0); +} + +static int +smbfs_smb_findcloseLM2(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); + if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0) + smbfs_smb_findclose2(ctx); + return (0); +} + +int +smbfs_smb_findopen(struct smbnode *dnp, const char *wildcard, int wclen, + int attr, struct smb_cred *scrp, + struct smbfs_fctx **ctxpp) +{ + struct smbfs_fctx *ctx; + int error; + + ctx = kmem_alloc(sizeof (*ctx), KM_SLEEP); + if (ctx == NULL) + return (ENOMEM); + bzero(ctx, sizeof (*ctx)); + if (dnp->n_mount->smi_share) { + ctx->f_ssp = dnp->n_mount->smi_share; + } + ctx->f_dnp = dnp; + ctx->f_flags = SMBFS_RDD_FINDFIRST; + ctx->f_scred = scrp; + if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 || + (dnp->n_mount->smi_args.flags & SMBFS_MOUNT_NO_LONG)) { + ctx->f_flags |= SMBFS_RDD_USESEARCH; + error = smbfs_smb_findopenLM1(ctx, dnp, wildcard, wclen, + attr, scrp); + } else + error = smbfs_smb_findopenLM2(ctx, dnp, wildcard, wclen, + attr, scrp); + if (error) + smbfs_smb_findclose(ctx, scrp); + else + *ctxpp = ctx; + return (error); +} + +int +smbfs_smb_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scrp) +{ + int error; + + /* + * Note: "limit" (maxcount) needs to fit in a short! + * + * smb_lookup always uses 1, which is OK (no wildcards). + * Otherwise, this is smbfs_readdir, and we want to force + * limit to be in the range 3 to 1000. The low limit (3) + * is so we can always give the caller one "real" entry + * (something other than "." or "..") The high limit is + * just tuning. WinNT used 512, Win2k 1366. We use 1000. + * + * XXX: Move the [skip . ..] gunk to our caller (readdir). + */ + if ((ctx->f_flags & SMBFS_RDD_FINDSINGLE) == 0) { + if (limit < 3) + limit = 3; + if (limit > 1000) + limit = 1000; + } + + ctx->f_scred = scrp; + for (;;) { + if (ctx->f_flags & SMBFS_RDD_USESEARCH) { + error = smbfs_smb_findnextLM1(ctx, (uint16_t)limit); + } else + error = smbfs_smb_findnextLM2(ctx, (uint16_t)limit); + if (error) + return (error); + if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { + /*LINTED*/ + uint16_t *up = (uint16_t *)ctx->f_name; + + /* Do comparisons on UCS-2LE characters */ + if ((ctx->f_nmlen == 2 && up[0] == htoles('.')) || + (ctx->f_nmlen == 4 && up[0] == htoles('.') && + up[1] == htoles('.'))) + continue; + } else { + if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') || + (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' && + ctx->f_name[1] == '.')) + continue; + } + break; + } + smbfs_fname_tolocal(ctx); + ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name, + ctx->f_nmlen); + return (0); +} + + +int +smbfs_smb_findclose(struct smbfs_fctx *ctx, struct smb_cred *scrp) +{ + ctx->f_scred = scrp; + if (ctx->f_flags & SMBFS_RDD_USESEARCH) { + smbfs_smb_findcloseLM1(ctx); + } else + smbfs_smb_findcloseLM2(ctx); + if (ctx->f_rname) + kmem_free(ctx->f_rname, ctx->f_rnamelen); + if (ctx->f_firstnm) + kmem_free(ctx->f_firstnm, ctx->f_firstnmlen); + kmem_free(ctx, sizeof (*ctx)); + return (0); +} + + +int +smbfs_smb_lookup(struct smbnode *dnp, const char **namep, int *nmlenp, + struct smbfattr *fap, struct smb_cred *scrp) +{ + struct smbfs_fctx *ctx; + int error, intr; + const char *name = (namep ? *namep : NULL); + int nmlen = (nmlenp ? *nmlenp : 0); + + /* This is no longer called with a null dnp */ + ASSERT(dnp); + + /* + * Should not get here with "" anymore. + */ + if (!name || !nmlen) { + DEBUG_ENTER("smbfs_smb_lookup: name is NULL"); + return (EINVAL); + } + + /* + * Should not get here with "." or ".." anymore. + */ + if ((nmlen == 1 && name[0] == '.') || + (nmlen == 2 && name[0] == '.' && name[1] == '.')) { + DEBUG_ENTER("smbfs_smb_lookup: name is '.' or '..'"); + return (EINVAL); + } + + /* + * 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); + + bzero(fap, sizeof (*fap)); + + /* + * 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) + goto out; + ctx->f_flags |= SMBFS_RDD_FINDSINGLE; + error = smbfs_smb_findnext(ctx, 1, scrp); + if (error == 0) { + *fap = ctx->f_attr; + if (name == NULL) + fap->fa_ino = dnp->n_ino; + if (namep) + *namep = (const char *)smbfs_name_alloc( + ctx->f_name, ctx->f_nmlen); + if (nmlenp) + *nmlenp = ctx->f_nmlen; + } + smbfs_smb_findclose(ctx, scrp); + +out: + smbfs_rw_exit(&dnp->r_lkserlock); + return (error); +} + +/* + * Support functions for get/set security + */ +#ifdef APPLE + +int +smbfs_smb_getsec_int(struct smb_share *ssp, uint16_t fid, + struct smb_cred *scrp, uint32_t selector, + struct ntsecdesc **res, int *reslen) +{ + struct smb_ntrq *ntp; + struct mbchain *mbp; + struct mdchain *mdp; + int error, len; + + error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_QUERY_SECURITY_DESC, + scrp, &ntp); + if (error) + return (error); + mbp = &ntp->nt_tparam; + mb_init(mbp); + mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, selector); + 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); + + mdp = &ntp->nt_rdata; + if (mdp->md_top) { /* XXX md_cur safer than md_top */ + len = m_fixhdr(mdp->md_top); + /* + * 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 (len < *reslen) + *reslen = len; + if (len == *reslen) { + MALLOC(*res, struct ntsecdesc *, len, M_TEMP, M_WAITOK); + md_get_mem(mdp, (caddr_t)*res, len, MB_MSYSTEM); + } else if (len > *reslen) + SMBVDEBUG("len %d *reslen %d fid 0x%x\n", len, *reslen, + letohs(fid)); + } else + SMBVDEBUG("null md_top? fid 0x%x\n", letohs(fid)); +done: + smb_nt_done(ntp); + return (error); +} + +int +smbfs_smb_getsec(struct smb_share *ssp, uint16_t fid, struct smb_cred *scrp, + uint32_t selector, struct ntsecdesc **res) +{ + int error, olen, seclen; + + olen = seclen = 500; /* "overlarge" values => server errors */ + error = smbfs_smb_getsec_int(ssp, fid, scrp, selector, res, &seclen); + if (error && seclen > olen) + error = smbfs_smb_getsec_int(ssp, fid, scrp, selector, res, + &seclen); + return (error); +} + +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) +{ + struct smb_ntrq *ntp; + struct mbchain *mbp; + int error, off; + struct ntsecdesc ntsd; + + error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_SET_SECURITY_DESC, + scrp, &ntp); + if (error) + return (error); + mbp = &ntp->nt_tparam; + mb_init(mbp); + mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, 0); /* reserved */ + mb_put_uint32le(mbp, selector); + mbp = &ntp->nt_tdata; + 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); + } + 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); + ntp->nt_maxpcount = 0; + ntp->nt_maxdcount = 0; + error = smb_nt_request(ntp); + smb_nt_done(ntp); + return (error); +} + +#endif /* APPLE */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c new file mode 100644 index 0000000000..5ef9b5439f --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c @@ -0,0 +1,368 @@ +/* + * 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. + * + * $Id: smbfs_subr.c,v 1.18 2005/02/02 00:22:23 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/sunddi.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#include <sys/utfconv.h> +#include <sys/smb_iconv.h> +#else /* APPLE */ +#include <netsmb/smb_osdep.h> +#endif /* APPLE */ + +#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> + +#ifdef APPLE +MALLOC_DEFINE(M_SMBFSDATA, "SMBFS data", "SMBFS private data"); +#endif /* APPLE */ + +/* + * 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; + +#ifdef APPLE +PRIVSYM int wall_cmos_clock = 0; /* XXX */ +PRIVSYM int adjkerntz = 0; /* XXX */ +#endif /* APPLE */ + +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; + + /* + * 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 (dtp) + *dtp = lastdtime; + if (dhp) + *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; + + *ddp = lastddate; +} + +/* + * 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. + */ + 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; +} + +/* + * In the Darwin code, this function used to compute the full path + * by following the chain of n_parent pointers back to the root. + * In the Solaris port we found the n_parent pointers inconvenient + * because they hold parent nodes busy. We now keep the full path + * in every node, so this function need only marshall the directory + * path, and (if provided) the separator and last component name. + * + * Note that this logic must match that in smbfs_getino + */ +int +smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, + const char *name, int *lenp, u_int8_t sep) +{ + int caseopt = SMB_CS_NONE; + int error, len = 0; + int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0; + + if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0) + caseopt |= SMB_CS_UPPER; + + if (lenp) { + len = *lenp; + *lenp = 0; + } + if (unicode) { + error = mb_put_padbyte(mbp); + if (error) + return (error); + } + error = smb_put_dmem(mbp, vcp, + dnp->n_rpath, dnp->n_rplen, + caseopt, lenp); + if (name) { + /* If not at root, put separator */ + if (dnp->n_rplen > 1) { + if (unicode) + error = mb_put_uint16le(mbp, sep); + else + error = mb_put_uint8(mbp, sep); + if (!error && lenp) + *lenp += (unicode + 1); + if (error) + return (error); + } + /* Put the name */ + error = smb_put_dmem(mbp, vcp, + name, len, caseopt, lenp); + if (error) + return (error); + } + /* Put NULL termination. */ + if (unicode) + error = mb_put_uint16le(mbp, 0); + else + error = mb_put_uint8(mbp, 0); + if (!error && lenp) + *lenp += (unicode + 1); + + return (error); +} + +void +smbfs_fname_tolocal(struct smbfs_fctx *ctx) +{ + int length; + struct smb_vc *vcp = SSTOVC(ctx->f_ssp); + uchar_t *dst; + const ushort_t *src; + size_t inlen, outlen; + int flags = 0; + + if (ctx->f_nmlen == 0) + return; + + /* XXX: This is temporary, right? Need iconv... */ + if (!SMB_UNICODE_STRINGS(vcp)) + return; + + /* + * In Unix, the UTF-8 name can be larger and + * in-place conversions are not supported. + * Note: 3,9 are the maximum UTF-8 expansion + * factors when converting strings from UTF-16 + * XXX: This was removed. REVISIT + */ + if (SMB_UNICODE_STRINGS(vcp)) + length = ctx->f_nmlen * 9; /* why 9 */ + else + length = ctx->f_nmlen * 3; /* why 3 */ + length = min(length, SMB_MAXFNAMELEN); + + dst = kmem_zalloc(length, KM_SLEEP); + outlen = length; + /*LINTED*/ + src = (const ushort_t *)ctx->f_name; + inlen = ctx->f_nmlen / 2; /* need number of UCS-2 characters */ + flags |= UCONV_IN_LITTLE_ENDIAN; + + if (uconv_u16tou8(src, &inlen, dst, &outlen, flags) == 0) { + kmem_free(ctx->f_name, ctx->f_namesz); + ctx->f_name = (char *)dst; + ctx->f_namesz = length; + ctx->f_nmlen = (int)outlen; + } else + kmem_free(dst, length); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h new file mode 100644 index 0000000000..18641420f0 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h @@ -0,0 +1,260 @@ +/* + * 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. + * + * $Id: smbfs_subr.h,v 1.25 2005/03/17 01:23:40 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _FS_SMBFS_SMBFS_SUBR_H_ +#define _FS_SMBFS_SMBFS_SUBR_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* This defines terms used in the error messages */ +#include <sys/cmn_err.h> + +#if defined(DEBUG) || defined(lint) +#define SMB_VNODE_DEBUG 1 +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (1) +#endif + +/* + * Let's use C99 standard variadic macros! + * Also the C99 __func__ (function name) feature. + */ +#define SMBFSERR(...) \ + smb_errmsg(CE_NOTE, __func__, __VA_ARGS__) +#define SMBVDEBUG(...) \ + smb_errmsg(CE_CONT, __func__, __VA_ARGS__) + +/* + * Possible lock commands + */ +#define SMB_LOCK_EXCL 0 +#define SMB_LOCK_SHARED 1 +#define SMB_LOCK_RELEASE 2 + +struct mbchain; +struct smb_cred; +struct smb_vc; +struct statvfs; +struct timespec; + + +/* + * Context to perform findfirst/findnext/findclose operations + */ +#define SMBFS_RDD_FINDFIRST 0x01 +#define SMBFS_RDD_EOF 0x02 +#define SMBFS_RDD_FINDSINGLE 0x04 +#define SMBFS_RDD_USESEARCH 0x08 +#define SMBFS_RDD_NOCLOSE 0x10 +#define SMBFS_RDD_GOTRNAME 0x1000 + +/* + * Search context supplied by server + */ +#define SMB_SKEYLEN 21 /* search context */ +#define SMB_DENTRYLEN (SMB_SKEYLEN + 22) /* entire entry */ + +struct smbfs_fctx { + /* + * Setable values + */ + int f_flags; /* SMBFS_RDD_ */ + /* + * Return values + */ + struct smbfattr f_attr; /* current attributes */ + char *f_name; /* current file name */ + int f_nmlen; /* name len */ + int f_namesz; /* memory allocated */ + /* + * Internal variables + */ + uint16_t f_limit; /* maximum number of entries */ + uint16_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; + union { + struct smb_rq *uf_rq; + struct smb_t2rq *uf_t2; + } f_urq; + 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 */ +}; +typedef struct smbfs_fctx smbfs_fctx_t; + +#define f_rq f_urq.uf_rq +#define f_t2 f_urq.uf_t2 + + +/* + * smb level + */ +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_qfsattr(struct smb_share *ssp, uint32_t *attrp, + struct smb_cred *scrp); +int smbfs_smb_statfs(struct smb_share *ssp, statvfs64_t *sbp, + struct smb_cred *scrp); +int smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize, + struct smb_cred *scrp); + +int smbfs_smb_getfattr(struct smbnode *np, struct smbfattr *fap, + struct smb_cred *scrp); + +int smbfs_smb_setfattr(struct smbnode *np, uint16_t fid, + uint32_t attr, struct timespec *mtime, struct timespec *atime, + struct smb_cred *scrp); + +int smbfs_smb_setpattr(struct smbnode *np, + uint32_t attr, struct timespec *mtime, struct timespec *atime, + struct smb_cred *scrp); + +int smbfs_smb_open(struct smbnode *np, uint32_t rights, struct smb_cred *scrp, + int *attrcacheupdated, uint16_t *fidp, const char *name, int nmlen, + int xattr, len_t *sizep, uint32_t *rightsp); +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); +int smbfs_smb_create(struct smbnode *dnp, const char *name, int len, + struct smb_cred *scrp, uint16_t *fidp, uint32_t disp, int xattr); +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, struct smbnode *tdnp, + const char *tname, int tnmlen, struct smb_cred *scrp, int overwrite); +int smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp, + const char *tname, int tnmlen, uint16_t flags, 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 *nmlenp, 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); + +#ifdef NOT_YET +int smbfs_smb_getsec(struct smb_share *ssp, uint16_t fid, + struct smb_cred *scrp, uint32_t selector, struct ntsecdesc **res); +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); +int smbfs_smb_qstreaminfo(struct smbnode *np, struct smb_cred *scrp, + uio_t uio, size_t *sizep); +#endif /* NOT_YET */ + +void smbfs_fname_tolocal(struct smbfs_fctx *ctx); + +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, int tzoff, struct timespec *tsp); +void smb_time_local2NT(struct timespec *tsp, int tzoff, 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); + +/* Stuff borrowed from NFS (and then hacked) */ +vnode_t *smbfs_make_node(vfs_t *vfsp, + const char *dir, int dirlen, + const char *name, int nmlen, + struct smbfattr *fap); +void smb_addfree(smbnode_t *sp); +void smb_addhash(smbnode_t *sp); +void smb_rmhash(smbnode_t *); + +int smbfs_subrinit(void); +void smbfs_subrfini(void); +int smbfs_clntinit(void); +void smbfs_clntfini(void); +void smbfs_zonelist_add(smbmntinfo_t *smi); +void smbfs_zonelist_remove(smbmntinfo_t *smi); +void smbfs_destroy_table(struct vfs *vfsp); +int smbfs_readvnode(vnode_t *, uio_t *, cred_t *, struct vattr *); +int smbfs_writevnode(vnode_t *vp, uio_t *uiop, cred_t *cr, + int ioflag, int timo); +int smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr); + +/* For Solaris, interruptible rwlock */ +int smbfs_rw_enter_sig(smbfs_rwlock_t *l, krw_t rw, int intr); +int smbfs_rw_tryenter(smbfs_rwlock_t *l, krw_t rw); +void smbfs_rw_exit(smbfs_rwlock_t *l); +int smbfs_rw_lock_held(smbfs_rwlock_t *l, krw_t rw); +void smbfs_rw_init(smbfs_rwlock_t *l, char *name, krw_type_t type, void *arg); +void smbfs_rw_destroy(smbfs_rwlock_t *l); + +#endif /* !_FS_SMBFS_SMBFS_SUBR_H_ */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c new file mode 100644 index 0000000000..c7ae9131a1 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr2.c @@ -0,0 +1,1001 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Node hash implementation borrowed from NFS. + * See: uts/common/fs/nfs/nfs_subr.c + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <sys/bitmap.h> +#include <sys/dnlc.h> +#include <sys/kmem.h> +#include <sys/sunddi.h> + +#ifdef APPLE +#include <sys/smb_apple.h> +#include <sys/utfconv.h> +#include <sys/smb_iconv.h> +#else +#include <netsmb/smb_osdep.h> +#endif + +#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> + +/* + * The hash queues for the access to active and cached smbnodes + * are organized as doubly linked lists. A reader/writer lock + * for each hash bucket is used to control access and to synchronize + * lookups, additions, and deletions from the hash queue. + * + * The smbnode freelist is organized as a doubly linked list with + * a head pointer. Additions and deletions are synchronized via + * a single mutex. + * + * In order to add an smbnode to the free list, it must be hashed into + * a hash queue and the exclusive lock to the hash queue be held. + * If an smbnode is not hashed into a hash queue, then it is destroyed + * because it represents no valuable information that can be reused + * about the file. The exclusive lock to the hash queue must be + * held in order to prevent a lookup in the hash queue from finding + * the smbnode and using it and assuming that the smbnode is not on the + * freelist. The lookup in the hash queue will have the hash queue + * locked, either exclusive or shared. + * + * The vnode reference count for each smbnode is not allowed to drop + * below 1. This prevents external entities, such as the VM + * subsystem, from acquiring references to vnodes already on the + * freelist and then trying to place them back on the freelist + * when their reference is released. This means that the when an + * smbnode is looked up in the hash queues, then either the smbnode + * is removed from the freelist and that reference is tranfered to + * the new reference or the vnode reference count must be incremented + * accordingly. The mutex for the freelist must be held in order to + * accurately test to see if the smbnode is on the freelist or not. + * The hash queue lock might be held shared and it is possible that + * two different threads may race to remove the smbnode from the + * freelist. This race can be resolved by holding the mutex for the + * freelist. Please note that the mutex for the freelist does not + * need to held if the smbnode is not on the freelist. It can not be + * placed on the freelist due to the requirement that the thread + * putting the smbnode on the freelist must hold the exclusive lock + * to the hash queue and the thread doing the lookup in the hash + * queue is holding either a shared or exclusive lock to the hash + * queue. + * + * The lock ordering is: + * + * hash bucket lock -> vnode lock + * hash bucket lock -> freelist lock + */ +static rhashq_t *smbtable; + +static kmutex_t smbfreelist_lock; +static smbnode_t *smbfreelist = NULL; +static ulong_t smbnodenew = 0; +long nsmbnode = 0; + +static int smbtablesize; +static int smbtablemask; +static int smbhashlen = 4; + +static struct kmem_cache *smbnode_cache; + +/* + * Mutex to protect the following variables: + * smbfs_major + * smbfs_minor + */ +kmutex_t smbfs_minor_lock; +int smbfs_major; +int smbfs_minor; + +/* + * Local functions. + * Not static, to aid debugging. + */ +void smb_rmfree(smbnode_t *); +void smbinactive(smbnode_t *); +void smb_rmhash_locked(smbnode_t *); +void smb_destroy_node(smbnode_t *); +void smbfs_kmem_reclaim(void *cdrarg); + +smbnode_t *smbhashfind(struct vfs *, const char *, int, rhashq_t *); +static vnode_t *make_smbnode(vfs_t *, char *, int, rhashq_t *, int *); + + +/* + * Free the resources associated with an smbnode. + * Note: This is different from smbfs_inactive + * + * NFS: nfs_subr.c:rinactive + */ +void +smbinactive(smbnode_t *np) +{ + + if (np->n_rpath) { + kmem_free(np->n_rpath, np->n_rplen + 1); + np->n_rpath = NULL; + } +} + +/* + * Return a vnode for the given CIFS directory and filename. + * If no smbnode exists for this fhandle, create one and put it + * into the hash queues. If the smbnode for this fhandle + * already exists, return it. + * + * Note: make_smbnode() may upgrade the hash bucket lock to exclusive. + * + * NFS: nfs_subr.c:makenfsnode + */ +vnode_t * +smbfs_make_node( + vfs_t *vfsp, + const char *dir, + int dirlen, + const char *name, + int nmlen, + struct smbfattr *fap) +{ + char *rpath; + int rplen, idx; + uint32_t hash; + rhashq_t *rhtp; + smbnode_t *np; + vnode_t *vp; +#ifdef NOT_YET + vattr_t va; +#endif + int newnode; + + /* + * Build the full path name in allocated memory + * so we have it for lookup, etc. + * + * ToDo: Would prefer to allocate a remote path + * only when we will create a new node. + */ + rplen = dirlen; + if (name) { + /* If not at root, we'll add a slash. */ + if (dirlen > 1) + rplen++; + rplen += nmlen; + } + rpath = kmem_alloc(rplen + 1, KM_SLEEP); + + bcopy(dir, rpath, dirlen); + if (name) { + if (dirlen > 1) + rpath[dirlen++] = '\\'; + bcopy(name, &rpath[dirlen], nmlen); + } + rpath[rplen] = 0; + + hash = smbfs_hash(rpath, rplen); + idx = hash & smbtablemask; + rhtp = &smbtable[idx]; + rw_enter(&rhtp->r_lock, RW_READER); + + vp = make_smbnode(vfsp, rpath, rplen, rhtp, &newnode); + np = VTOSMB(vp); + np->n_ino = hash; /* Equivalent to: smbfs_getino() */ + + /* + * Note: make_smbnode keeps a reference to rpath in + * new nodes it creates, so only free when we found + * an existing node. + */ + if (!newnode) { + kmem_free(rpath, rplen + 1); + rpath = NULL; + } + + if (fap == NULL) { +#ifdef NOT_YET + if (newnode) { + PURGE_ATTRCACHE(vp); + } +#endif + rw_exit(&rhtp->r_lock); + return (vp); + } + + /* Have SMB attributes. */ + vp->v_type = (fap->fa_attr & SMB_FA_DIR) ? VDIR : VREG; + /* XXX: np->n_ino = fap->fa_ino; see above */ + np->r_size = fap->fa_size; + /* XXX: np->r_attr = *fap here instead? */ + np->r_atime = fap->fa_atime; + np->r_ctime = fap->fa_mtime; + np->r_mtime = fap->fa_ctime; + +#ifdef NOT_YET + if (!newnode) { + rw_exit(&rhtp->r_lock); + (void) nfs_cache_fattr(vp, attr, &va, t, cr); + } else { + if (attr->na_type < NFNON || attr->na_type > NFSOC) + vp->v_type = VBAD; + else + vp->v_type = n2v_type(attr); + vp->v_rdev = makedevice(attr->rdev.specdata1, + attr->rdev.specdata2); + nfs_attrcache(vp, attr, t); + rw_exit(&rhtp->r_lock); + } +#else + rw_exit(&rhtp->r_lock); +#endif + + return (vp); +} + +/* + * NFS: nfs_subr.c:rtablehash + * We use smbfs_hash(). + */ + +/* + * Find or create an smbnode. + * NFS: nfs_subr.c:make_rnode + */ +static vnode_t * +make_smbnode( + vfs_t *vfsp, + char *rpath, + int rplen, + rhashq_t *rhtp, + int *newnode) +{ + smbnode_t *np; + smbnode_t *tnp; + vnode_t *vp; + smbmntinfo_t *mi; + + ASSERT(RW_READ_HELD(&rhtp->r_lock)); + + mi = VFTOSMI(vfsp); + +start: + np = smbhashfind(vfsp, rpath, rplen, rhtp); + if (np != NULL) { + vp = SMBTOV(np); + *newnode = 0; + return (vp); + } + + /* Note: will retake this lock below. */ + rw_exit(&rhtp->r_lock); + + /* + * see if we can find something on the freelist + */ + mutex_enter(&smbfreelist_lock); + if (smbfreelist != NULL && smbnodenew >= nsmbnode) { + np = smbfreelist; + smb_rmfree(np); + mutex_exit(&smbfreelist_lock); + + vp = SMBTOV(np); + + if (np->r_flags & RHASHED) { + rw_enter(&np->r_hashq->r_lock, RW_WRITER); + mutex_enter(&vp->v_lock); + if (vp->v_count > 1) { + vp->v_count--; + mutex_exit(&vp->v_lock); + rw_exit(&np->r_hashq->r_lock); + rw_enter(&rhtp->r_lock, RW_READER); + goto start; + } + mutex_exit(&vp->v_lock); + smb_rmhash_locked(np); + rw_exit(&np->r_hashq->r_lock); + } + + smbinactive(np); + + mutex_enter(&vp->v_lock); + if (vp->v_count > 1) { + vp->v_count--; + mutex_exit(&vp->v_lock); + rw_enter(&rhtp->r_lock, RW_READER); + goto start; + } + mutex_exit(&vp->v_lock); + vn_invalid(vp); + /* + * destroy old locks before bzero'ing and + * recreating the locks below. + */ + smbfs_rw_destroy(&np->r_rwlock); + smbfs_rw_destroy(&np->r_lkserlock); + mutex_destroy(&np->r_statelock); + cv_destroy(&np->r_cv); + /* + * Make sure that if smbnode is recycled then + * VFS count is decremented properly before + * reuse. + */ + VFS_RELE(vp->v_vfsp); + vn_reinit(vp); + } else { + /* + * allocate and initialize a new smbnode + */ + vnode_t *new_vp; + + mutex_exit(&smbfreelist_lock); + + np = kmem_cache_alloc(smbnode_cache, KM_SLEEP); + new_vp = vn_alloc(KM_SLEEP); + + atomic_add_long((ulong_t *)&smbnodenew, 1); + vp = new_vp; + } + + /* Initialize smbnode_t */ + bzero(np, sizeof (*np)); + + smbfs_rw_init(&np->r_rwlock, NULL, RW_DEFAULT, NULL); + smbfs_rw_init(&np->r_lkserlock, NULL, RW_DEFAULT, NULL); + mutex_init(&np->r_statelock, NULL, MUTEX_DEFAULT, NULL); + cv_init(&np->r_cv, NULL, CV_DEFAULT, NULL); + /* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */ + + np->r_vnode = vp; + np->n_mount = mi; + np->r_hashq = rhtp; + np->n_direof = -1; + np->n_fid = SMB_FID_UNUSED; + np->n_uid = UID_NOBODY; + np->n_gid = GID_NOBODY; + /* XXX: make attributes stale? */ + +#if 0 /* XXX dircache */ + /* + * We don't know if it's a directory yet. + * Let the caller do this? XXX + */ + avl_create(&np->r_dir, compar, sizeof (rddir_cache), + offsetof(rddir_cache, tree)); +#endif + + /* Now fill in the vnode. */ + vn_setops(vp, smbfs_vnodeops); + vp->v_data = (caddr_t)np; + VFS_HOLD(vfsp); + vp->v_vfsp = vfsp; + vp->v_type = VNON; + + /* + * There is a race condition if someone else + * alloc's the smbnode while no locks are held, so we + * check again and recover if found. + */ + rw_enter(&rhtp->r_lock, RW_WRITER); + tnp = smbhashfind(vfsp, rpath, rplen, rhtp); + if (tnp != NULL) { + vp = SMBTOV(tnp); + *newnode = 0; + rw_exit(&rhtp->r_lock); + /* The node we were building goes on the free list. */ + smb_addfree(np); + rw_enter(&rhtp->r_lock, RW_READER); + return (vp); + } + + /* + * Hash search identifies nodes by the full pathname, + * so store that before linking in the hash list. + * Note: caller allocates the rpath, and knows + * about this reference when *newnode is set. + */ + np->n_rpath = rpath; + np->n_rplen = rplen; + + smb_addhash(np); + *newnode = 1; + return (vp); +} + +/* + * smb_addfree + * Put a smbnode on the free list. + * + * Normally called by smbfs_inactive, but also + * called in here during cleanup operations. + * + * Smbnodes which were allocated above and beyond the normal limit + * are immediately freed. + * + * NFS: nfs_subr.c:rp_addfree + */ +void +smb_addfree(smbnode_t *np) +{ + vnode_t *vp; + struct vfs *vfsp; + + vp = SMBTOV(np); + ASSERT(vp->v_count >= 1); + ASSERT(np->r_freef == NULL && np->r_freeb == NULL); + + /* + * If we have too many smbnodes allocated and there are no + * references to this smbnode, or if the smbnode is no longer + * accessible by it does not reside in the hash queues, + * or if an i/o error occurred while writing to the file, + * then just free it instead of putting it on the smbnode + * freelist. + */ + vfsp = vp->v_vfsp; + if (((smbnodenew > nsmbnode || !(np->r_flags & RHASHED) || + np->r_error || (vfsp->vfs_flag & VFS_UNMOUNTED)) && + np->r_count == 0)) { + if (np->r_flags & RHASHED) { + rw_enter(&np->r_hashq->r_lock, RW_WRITER); + mutex_enter(&vp->v_lock); + if (vp->v_count > 1) { + vp->v_count--; + mutex_exit(&vp->v_lock); + rw_exit(&np->r_hashq->r_lock); + return; + /* + * Will get another call later, + * via smbfs_inactive. + */ + } + mutex_exit(&vp->v_lock); + smb_rmhash_locked(np); + rw_exit(&np->r_hashq->r_lock); + } + + smbinactive(np); + + /* + * Recheck the vnode reference count. We need to + * make sure that another reference has not been + * acquired while we were not holding v_lock. The + * smbnode is not in the smbnode hash queues, so the + * only way for a reference to have been acquired + * is for a VOP_PUTPAGE because the smbnode was marked + * with RDIRTY or for a modified page. This + * reference may have been acquired before our call + * to smbinactive. The i/o may have been completed, + * thus allowing smbinactive to complete, but the + * reference to the vnode may not have been released + * yet. In any case, the smbnode can not be destroyed + * until the other references to this vnode have been + * released. The other references will take care of + * either destroying the smbnode or placing it on the + * smbnode freelist. If there are no other references, + * then the smbnode may be safely destroyed. + */ + mutex_enter(&vp->v_lock); + if (vp->v_count > 1) { + vp->v_count--; + mutex_exit(&vp->v_lock); + return; + } + mutex_exit(&vp->v_lock); + + smb_destroy_node(np); + return; + } + /* + * Lock the hash queue and then recheck the reference count + * to ensure that no other threads have acquired a reference + * to indicate that the smbnode should not be placed on the + * freelist. If another reference has been acquired, then + * just release this one and let the other thread complete + * the processing of adding this smbnode to the freelist. + */ + rw_enter(&np->r_hashq->r_lock, RW_WRITER); + + mutex_enter(&vp->v_lock); + if (vp->v_count > 1) { + vp->v_count--; + mutex_exit(&vp->v_lock); + rw_exit(&np->r_hashq->r_lock); + return; + } + mutex_exit(&vp->v_lock); + + /* + * If there is no cached data or metadata for this file, then + * put the smbnode on the front of the freelist so that it will + * be reused before other smbnodes which may have cached data or + * metadata associated with them. + */ + mutex_enter(&smbfreelist_lock); + if (smbfreelist == NULL) { + np->r_freef = np; + np->r_freeb = np; + smbfreelist = np; + } else { + np->r_freef = smbfreelist; + np->r_freeb = smbfreelist->r_freeb; + smbfreelist->r_freeb->r_freef = np; + smbfreelist->r_freeb = np; + } + mutex_exit(&smbfreelist_lock); + + rw_exit(&np->r_hashq->r_lock); +} + +/* + * Remove an smbnode from the free list. + * + * The caller must be holding smbfreelist_lock and the smbnode + * must be on the freelist. + * + * NFS: nfs_subr.c:rp_rmfree + */ +void +smb_rmfree(smbnode_t *np) +{ + + ASSERT(MUTEX_HELD(&smbfreelist_lock)); + ASSERT(np->r_freef != NULL && np->r_freeb != NULL); + + if (np == smbfreelist) { + smbfreelist = np->r_freef; + if (np == smbfreelist) + smbfreelist = NULL; + } + + np->r_freeb->r_freef = np->r_freef; + np->r_freef->r_freeb = np->r_freeb; + + np->r_freef = np->r_freeb = NULL; +} + +/* + * Put a smbnode in the hash table. + * + * The caller must be holding the exclusive hash queue lock. + * + * NFS: nfs_subr.c:rp_addhash + */ +void +smb_addhash(smbnode_t *np) +{ + + ASSERT(RW_WRITE_HELD(&np->r_hashq->r_lock)); + ASSERT(!(np->r_flags & RHASHED)); + + np->r_hashf = np->r_hashq->r_hashf; + np->r_hashq->r_hashf = np; + np->r_hashb = (smbnode_t *)np->r_hashq; + np->r_hashf->r_hashb = np; + + mutex_enter(&np->r_statelock); + np->r_flags |= RHASHED; + mutex_exit(&np->r_statelock); +} + +/* + * Remove a smbnode from the hash table. + * + * The caller must be holding the hash queue lock. + * + * NFS: nfs_subr.c:rp_rmhash_locked + */ +void +smb_rmhash_locked(smbnode_t *np) +{ + + ASSERT(RW_WRITE_HELD(&np->r_hashq->r_lock)); + ASSERT(np->r_flags & RHASHED); + + np->r_hashb->r_hashf = np->r_hashf; + np->r_hashf->r_hashb = np->r_hashb; + + mutex_enter(&np->r_statelock); + np->r_flags &= ~RHASHED; + mutex_exit(&np->r_statelock); +} + +/* + * Remove a smbnode from the hash table. + * + * The caller must not be holding the hash queue lock. + */ +void +smb_rmhash(smbnode_t *np) +{ + + rw_enter(&np->r_hashq->r_lock, RW_WRITER); + smb_rmhash_locked(np); + rw_exit(&np->r_hashq->r_lock); +} + +/* + * Lookup a smbnode by fhandle. + * + * The caller must be holding the hash queue lock, either shared or exclusive. + * XXX: make static? + * + * NFS: nfs_subr.c:rfind + */ +smbnode_t * +smbhashfind( + struct vfs *vfsp, + const char *rpath, + int rplen, + rhashq_t *rhtp) +{ + smbnode_t *np; + vnode_t *vp; + + ASSERT(RW_LOCK_HELD(&rhtp->r_lock)); + + for (np = rhtp->r_hashf; np != (smbnode_t *)rhtp; np = np->r_hashf) { + vp = SMBTOV(np); + if (vp->v_vfsp == vfsp && + np->n_rplen == rplen && + bcmp(np->n_rpath, rpath, rplen) == 0) { + /* + * remove smbnode from free list, if necessary. + */ + if (np->r_freef != NULL) { + mutex_enter(&smbfreelist_lock); + /* + * If the smbnode is on the freelist, + * then remove it and use that reference + * as the new reference. Otherwise, + * need to increment the reference count. + */ + if (np->r_freef != NULL) { + smb_rmfree(np); + mutex_exit(&smbfreelist_lock); + } else { + mutex_exit(&smbfreelist_lock); + VN_HOLD(vp); + } + } else + VN_HOLD(vp); + return (np); + } + } + return (NULL); +} + +#ifdef SMB_VNODE_DEBUG +int smb_check_table_debug = 1; +#else /* SMB_VNODE_DEBUG */ +int smb_check_table_debug = 0; +#endif /* SMB_VNODE_DEBUG */ + + +/* + * Return 1 if there is a active vnode belonging to this vfs in the + * smbtable cache. + * + * Several of these checks are done without holding the usual + * locks. This is safe because destroy_smbtable(), smb_addfree(), + * etc. will redo the necessary checks before actually destroying + * any smbnodes. + * + * NFS: nfs_subr.c:check_rtable + * + * Debugging changes here relative to NFS. + * Relatively harmless, so left 'em in. + */ +int +smb_check_table(struct vfs *vfsp, smbnode_t *rtnp) +{ + smbnode_t *np; + vnode_t *vp; + int index; + int busycnt = 0; + + for (index = 0; index < smbtablesize; index++) { + rw_enter(&smbtable[index].r_lock, RW_READER); + for (np = smbtable[index].r_hashf; + np != (smbnode_t *)(&smbtable[index]); + np = np->r_hashf) { + if (np == rtnp) + continue; /* skip the root */ + vp = SMBTOV(np); + if (vp->v_vfsp != vfsp) + continue; /* skip other mount */ + + /* Now the 'busy' checks: */ + /* Not on the free list? */ + if (np->r_freef == NULL) { + SMBVDEBUG("!r_freef: node=0x%p, v_path=%s\n", + (void *)np, vp->v_path); + busycnt++; + } + + /* Has dirty pages? */ + if (vn_has_cached_data(vp) && + (np->r_flags & RDIRTY)) { + SMBVDEBUG("is dirty: node=0x%p, v_path=%s\n", + (void *)np, vp->v_path); + busycnt++; + } + + /* Other refs? (not reflected in v_count) */ + if (np->r_count > 0) { + SMBVDEBUG("+r_count: node=0x%p, v_path=%s\n", + (void *)np, vp->v_path); + busycnt++; + } + + if (busycnt && !smb_check_table_debug) + break; + + } + rw_exit(&smbtable[index].r_lock); + } + return (busycnt); +} + +/* + * Destroy inactive vnodes from the hash queues which belong to this + * vfs. It is essential that we destroy all inactive vnodes during a + * forced unmount as well as during a normal unmount. + * + * NFS: nfs_subr.c:destroy_rtable + */ +void +smbfs_destroy_table(struct vfs *vfsp) +{ + int index; + smbnode_t *np; + smbnode_t *rlist; + smbnode_t *r_hashf; + vnode_t *vp; + + rlist = NULL; + + for (index = 0; index < smbtablesize; index++) { + rw_enter(&smbtable[index].r_lock, RW_WRITER); + for (np = smbtable[index].r_hashf; + np != (smbnode_t *)(&smbtable[index]); + np = r_hashf) { + /* save the hash pointer before destroying */ + r_hashf = np->r_hashf; + vp = SMBTOV(np); + if (vp->v_vfsp == vfsp) { + mutex_enter(&smbfreelist_lock); + if (np->r_freef != NULL) { + smb_rmfree(np); + mutex_exit(&smbfreelist_lock); + smb_rmhash_locked(np); + np->r_hashf = rlist; + rlist = np; + } else + mutex_exit(&smbfreelist_lock); + } + } + rw_exit(&smbtable[index].r_lock); + } + + for (np = rlist; np != NULL; np = rlist) { + rlist = np->r_hashf; + /* + * This call to smb_addfree will end up destroying the + * smbnode, but in a safe way with the appropriate set + * of checks done. + */ + smb_addfree(np); + } + +} + +/* + * This routine destroys all the resources associated with the smbnode + * and then the smbnode itself. + * + * NFS: nfs_subr.c:destroy_rnode + */ +void +smb_destroy_node(smbnode_t *np) +{ + vnode_t *vp; + vfs_t *vfsp; + + vp = SMBTOV(np); + vfsp = vp->v_vfsp; + + ASSERT(vp->v_count == 1); + ASSERT(np->r_count == 0); + ASSERT(np->r_mapcnt == 0); + ASSERT(!(np->r_flags & RHASHED)); + ASSERT(np->r_freef == NULL && np->r_freeb == NULL); + atomic_add_long((ulong_t *)&smbnodenew, -1); + vn_invalid(vp); + vn_free(vp); + kmem_cache_free(smbnode_cache, np); + VFS_RELE(vfsp); +} + +/* rflush? */ +/* access cache */ +/* client handles */ + +/* + * initialize resources that are used by smbfs_subr.c + * this is called from the _init() routine (by the way of smbfs_clntinit()) + * + * allocate and initialze smbfs hash table + * NFS: nfs_subr.c:nfs_subrinit + */ +int +smbfs_subrinit(void) +{ + int i; + ulong_t nsmbnode_max; + + /* + * Allocate and initialize the smbnode hash queues + */ + if (nsmbnode <= 0) + nsmbnode = ncsize; /* dnlc.h */ + nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) / + sizeof (struct smbnode)); + if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) { + zcmn_err(GLOBAL_ZONEID, CE_NOTE, + "setting nsmbnode to max value of %ld", nsmbnode_max); + nsmbnode = nsmbnode_max; + } + + smbtablesize = 1 << highbit(nsmbnode / smbhashlen); + smbtablemask = smbtablesize - 1; + smbtable = kmem_alloc(smbtablesize * sizeof (*smbtable), KM_SLEEP); + for (i = 0; i < smbtablesize; i++) { + smbtable[i].r_hashf = (smbnode_t *)(&smbtable[i]); + smbtable[i].r_hashb = (smbnode_t *)(&smbtable[i]); + rw_init(&smbtable[i].r_lock, NULL, RW_DEFAULT, NULL); + } + smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t), + 0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0); + + /* + * Initialize the various mutexes and reader/writer locks + */ + mutex_init(&smbfreelist_lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&smbfs_minor_lock, NULL, MUTEX_DEFAULT, NULL); + + /* + * Assign unique major number for all smbfs mounts + */ + if ((smbfs_major = getudev()) == -1) { + zcmn_err(GLOBAL_ZONEID, CE_WARN, + "smbfs: init: can't get unique device number"); + smbfs_major = 0; + } + smbfs_minor = 0; + + return (0); +} + +/* + * free smbfs hash table, etc. + * NFS: nfs_subr.c:nfs_subrfini + */ +void +smbfs_subrfini(void) +{ + int i; + + /* + * Deallocate the smbnode hash queues + */ + kmem_cache_destroy(smbnode_cache); + + for (i = 0; i < smbtablesize; i++) + rw_destroy(&smbtable[i].r_lock); + kmem_free(smbtable, smbtablesize * sizeof (*smbtable)); + + /* + * Destroy the various mutexes and reader/writer locks + */ + mutex_destroy(&smbfreelist_lock); + mutex_destroy(&smbfs_minor_lock); +} + +/* rddir_cache ? */ + +/* + * Support functions for smbfs_kmem_reclaim + */ + +static int +smbfs_node_reclaim(void) +{ + int freed; + smbnode_t *np; + vnode_t *vp; + + freed = 0; + mutex_enter(&smbfreelist_lock); + while ((np = smbfreelist) != NULL) { + smb_rmfree(np); + mutex_exit(&smbfreelist_lock); + if (np->r_flags & RHASHED) { + vp = SMBTOV(np); + rw_enter(&np->r_hashq->r_lock, RW_WRITER); + mutex_enter(&vp->v_lock); + if (vp->v_count > 1) { + vp->v_count--; + mutex_exit(&vp->v_lock); + rw_exit(&np->r_hashq->r_lock); + mutex_enter(&smbfreelist_lock); + continue; + } + mutex_exit(&vp->v_lock); + smb_rmhash_locked(np); + rw_exit(&np->r_hashq->r_lock); + } + /* + * This call to smb_addfree will end up destroying the + * smbnode, but in a safe way with the appropriate set + * of checks done. + */ + smb_addfree(np); + mutex_enter(&smbfreelist_lock); + } + mutex_exit(&smbfreelist_lock); + return (freed); +} + +/* + * Called by kmem_cache_alloc ask us if we could + * "Please give back some memory!" + * + * Todo: dump nodes from the free list? + */ +/*ARGSUSED*/ +void +smbfs_kmem_reclaim(void *cdrarg) +{ + (void) smbfs_node_reclaim(); +} + +/* nfs failover stuff */ +/* nfs_rw_xxx - see smbfs_rwlock.c */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c new file mode 100644 index 0000000000..f391dedd41 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c @@ -0,0 +1,915 @@ +/* + * 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. + * + * $Id: smbfs_vfsops.c,v 1.73.64.1 2005/05/27 02:35:28 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/systm.h> +#include <sys/cred.h> +#include <sys/vfs.h> +#include <sys/vnode.h> +#include <fs/fs_subr.h> +#include <sys/sysmacros.h> +#include <sys/kmem.h> +#include <sys/mkdev.h> +#include <sys/mount.h> +#include <sys/statvfs.h> +#include <sys/errno.h> +#include <sys/debug.h> +#include <sys/cmn_err.h> +#include <sys/modctl.h> +#include <sys/policy.h> +#include <sys/atomic.h> +#include <sys/zone.h> +#include <sys/vfs_opreg.h> +#include <sys/mntent.h> +#include <sys/priv.h> +#include <sys/tsol/label.h> +#include <sys/tsol/tndb.h> +#include <inet/ip.h> + +#include <netsmb/smb_osdep.h> +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> + +#include <smbfs/smbfs.h> +#include <smbfs/smbfs_node.h> +#include <smbfs/smbfs_subr.h> + +/* + * Local functions definitions. + */ +int smbfsinit(int fstyp, char *name); +void smbfsfini(); +static int smbfs_mount_label_policy(vfs_t *, void *, int, cred_t *); + +static vfsdef_t vfw = { + VFSDEF_VERSION, + "smbfs", /* type name string */ + smbfsinit, /* init routine */ + VSW_NOTZONESAFE, /* flags */ + NULL /* mount options table prototype */ +}; + +static struct modlfs modlfs = { + &mod_fsops, + "SMBFS filesystem v" SMBFS_VER_STR, + &vfw +}; + +static struct modlinkage modlinkage = { + MODREV_1, (void *)&modlfs, NULL +}; + +/* + * Mutex to protect the following variables: + * smbfs_major + * smbfs_minor + */ +extern kmutex_t smbfs_minor_lock; +extern int smbfs_major; +extern int smbfs_minor; + +/* + * Prevent unloads while we have mounts + */ +uint32_t smbfs_mountcount; + +/* + * smbfs vfs operations. + */ +static int smbfs_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *); +static int smbfs_unmount(vfs_t *, int, cred_t *); +static int smbfs_root(vfs_t *, vnode_t **); +static int smbfs_statvfs(vfs_t *, statvfs64_t *); +static int smbfs_sync(vfs_t *, short, cred_t *); +static void smbfs_freevfs(vfs_t *); + +/* + * Module loading + */ + +/* + * This routine is invoked automatically when the kernel module + * containing this routine is loaded. This allows module specific + * initialization to be done when the module is loaded. + */ +int +_init(void) +{ + int status; + + /* + * Check compiled-in version of "nsmb" + * that we're linked with. (paranoid) + */ + if (nsmb_version != NSMB_VERSION) { + cmn_err(CE_WARN, "_init: nsmb version mismatch"); + return (ENOTTY); + } + + smbfs_mountcount = 0; + + if ((status = smbfs_clntinit()) != 0) { + cmn_err(CE_WARN, "_init: smbfs_clntinit failed"); + return (status); + } + + status = mod_install((struct modlinkage *)&modlinkage); + return (status); +} + +/* + * Free kernel module resources that were allocated in _init + * and remove the linkage information into the kernel + */ +int +_fini(void) +{ + int error; + + /* + * If a forcedly unmounted instance is still hanging around, + * we cannot allow the module to be unloaded because that would + * cause panics once the VFS framework decides it's time to call + * into VFS_FREEVFS(). + */ + if (smbfs_mountcount) + return (EBUSY); + + error = mod_remove(&modlinkage); + if (error) + return (error); + + /* + * Free the allocated smbnodes, etc. + */ + smbfs_clntfini(); + + /* + * Free the ops vectors + */ + smbfsfini(); + return (0); +} + +/* + * Return information about the module + */ +int +_info(struct modinfo *modinfop) +{ + return (mod_info((struct modlinkage *)&modlinkage, modinfop)); +} + +/* + * Initialize the vfs structure + */ + +int smbfsfstyp; +vfsops_t *smbfs_vfsops = NULL; + +static const fs_operation_def_t smbfs_vfsops_template[] = { + { VFSNAME_MOUNT, { .vfs_mount = smbfs_mount } }, + { VFSNAME_UNMOUNT, { .vfs_unmount = smbfs_unmount } }, + { VFSNAME_ROOT, { .vfs_root = smbfs_root } }, + { VFSNAME_STATVFS, { .vfs_statvfs = smbfs_statvfs } }, + { VFSNAME_SYNC, { .vfs_sync = smbfs_sync } }, + { VFSNAME_VGET, { .error = fs_nosys } }, + { VFSNAME_MOUNTROOT, { .error = fs_nosys } }, + { VFSNAME_FREEVFS, { .vfs_freevfs = smbfs_freevfs } }, + { NULL, NULL } +}; + +int +smbfsinit(int fstyp, char *name) +{ + int error; + + error = vfs_setfsops(fstyp, smbfs_vfsops_template, &smbfs_vfsops); + if (error != 0) { + zcmn_err(GLOBAL_ZONEID, CE_WARN, + "smbfsinit: bad vfs ops template"); + return (error); + } + + 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, + "smbfsinit: bad vnode ops template"); + return (error); + } + + smbfsfstyp = fstyp; + + return (0); +} + +void +smbfsfini() +{ + if (smbfs_vfsops) { + (void) vfs_freevfsops_by_type(smbfsfstyp); + smbfs_vfsops = NULL; + } + if (smbfs_vnodeops) { + vn_freevnodeops(smbfs_vnodeops); + smbfs_vnodeops = NULL; + } +} + +void +smbfs_free_smi(smbmntinfo_t *smi) +{ + if (smi) { + smbfs_zonelist_remove(smi); + kmem_free(smi, sizeof (smbmntinfo_t)); + } +} + +/* + * smbfs mount vfsop + * Set up mount info record and attach it to vfs struct. + */ +static int +smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) +{ + char *data = uap->dataptr; + int error; + vnode_t *rtvp = NULL; /* root of this fs */ + smbmntinfo_t *smi = NULL; + dev_t smbfs_dev; + int version; + int devfd; + zone_t *zone = curproc->p_zone; + zone_t *mntzone = NULL; + smb_share_t *ssp = NULL; + smb_cred_t scred; + + STRUCT_DECL(smbfs_args, args); /* smbfs mount arguments */ + + if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) + return (error); + + if (mvp->v_type != VDIR) + return (ENOTDIR); + + /* + * get arguments + * + * uap->datalen might be different from sizeof (args) + * in a compatible situation. + */ + 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); + + /* + * Check mount program version + */ + version = STRUCT_FGET(args, version); + if (version != SMBFS_VERSION) { + cmn_err(CE_WARN, "mount version mismatch:" + " kernel=%d, mount=%d\n", + SMBFS_VERSION, version); + return (EINVAL); + } + + if (uap->flags & MS_REMOUNT) { + cmn_err(CE_WARN, "MS_REMOUNT not implemented"); + return (ENOTSUP); + } + + /* + * Check for busy + */ + mutex_enter(&mvp->v_lock); + if (!(uap->flags & MS_OVERLAY) && + (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { + mutex_exit(&mvp->v_lock); + return (EBUSY); + } + mutex_exit(&mvp->v_lock); + + /* + * Get the "share" from the netsmb driver (ssp). + * It is returned with a "ref" (hold) for us. + * Release this hold: at errout below, or in + * smbfs_freevfs(). + */ + devfd = STRUCT_FGET(args, devfd); + error = smb_dev2share(devfd, &ssp); + if (error) { + cmn_err(CE_WARN, "invalid device handle %d (%d)\n", + devfd, error); + return (error); + } + + /* + * We don't have data structures to support multiple mounts of + * the same share object by the same owner, so don't allow it. + */ + if (ssp->ss_mount != NULL) { + smb_share_rele(ssp); + return (EBUSY); + } + + smb_credinit(&scred, curproc, cr); + + /* + * Use "goto errout" from here on. + * See: ssp, smi, rtvp, mntzone + */ + + /* + * Determine the zone we're being mounted into. + */ + zone_hold(mntzone = zone); /* start with this assumption */ + if (getzoneid() == GLOBAL_ZONEID) { + zone_rele(mntzone); + mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt)); + ASSERT(mntzone != NULL); + if (mntzone != zone) { + error = EBUSY; + goto errout; + } + } + + /* + * Stop the mount from going any further if the zone is going away. + */ + if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) { + error = EBUSY; + goto errout; + } + + /* + * On a Trusted Extensions client, we may have to force read-only + * for read-down mounts. + */ + if (is_system_labeled()) { + void *addr; + int ipvers = 0; + struct smb_vc *vcp; + + vcp = SSTOVC(ssp); + addr = smb_vc_getipaddr(vcp, &ipvers); + error = smbfs_mount_label_policy(vfsp, addr, ipvers, cr); + + if (error > 0) + goto errout; + + if (error == -1) { + /* change mount to read-only to prevent write-down */ + vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0); + } + } + + /* + * Get root vnode. + */ +proceed: + + /* + * Create a mount record and link it to the vfs struct. + * Compare with NFS: nfsrootvp() + */ + smi = kmem_zalloc(sizeof (smbmntinfo_t), KM_SLEEP); + + smi->smi_share = ssp; + ssp->ss_mount = smi; + smi->smi_zone = mntzone; + + /* + * XXX If not root, get uid/gid from the covered vnode. + */ + smi->smi_args.dir_mode = STRUCT_FGET(args, dir_mode); + smi->smi_args.file_mode = STRUCT_FGET(args, file_mode); + smi->smi_args.uid = STRUCT_FGET(args, uid); + smi->smi_args.gid = STRUCT_FGET(args, gid); + + error = smbfs_smb_qfsattr(ssp, &smi->smi_fsattr, &scred); + if (error) { + SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error); + } + +#ifdef NOT_YET + /* Once acls are implemented, remove the ifdefs */ + else if (smbfs_aclsflunksniff(smi, &scred)) { + mutex_enter(&smi->smi_lock); + smi->smi_fsattr &= ~FILE_PERSISTENT_ACLS; + mutex_exit(&smi->smi_lock); + } +#endif /* NOT_YET */ + + /* + * Assign a unique device id to the mount + */ + mutex_enter(&smbfs_minor_lock); + do { + smbfs_minor = (smbfs_minor + 1) & MAXMIN32; + smbfs_dev = makedevice(smbfs_major, smbfs_minor); + } while (vfs_devismounted(smbfs_dev)); + mutex_exit(&smbfs_minor_lock); + + vfsp->vfs_dev = smbfs_dev; + vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfsfstyp); + vfsp->vfs_data = (caddr_t)smi; + vfsp->vfs_fstype = smbfsfstyp; + vfsp->vfs_bsize = MAXBSIZE; + vfsp->vfs_bcount = 0; + + smi->smi_flags = SMI_INT | SMI_LLOCK; + smi->smi_vfsp = vfsp; + smbfs_zonelist_add(smi); + + /* + * Create the root vnode, which we need in unmount + * for the call to smb_check_table(), etc. + */ + rtvp = smbfs_make_node(vfsp, "\\", 1, NULL, 0, NULL); + if (!rtvp) { + cmn_err(CE_WARN, "smbfs_mount: make_node failed\n"); + return (ENOENT); + } + rtvp->v_type = VDIR; + rtvp->v_flag |= VROOT; + + /* + * Could get attributes here, but that can wait + * until someone does a getattr call. + * + * NFS does other stuff here too: + * async worker threads + * init kstats + * + * End of code from NFS nfsrootvp() + */ + + smb_credrele(&scred); + + smi->smi_root = VTOSMB(rtvp); + + atomic_inc_32(&smbfs_mountcount); + + return (0); + +errout: + + ASSERT(rtvp == NULL); + + vfsp->vfs_data = NULL; + if (smi) + smbfs_free_smi(smi); + + if (mntzone != NULL) + zone_rele(mntzone); + + if (ssp) + smb_share_rele(ssp); + + smb_credrele(&scred); + + /* args, if we allocated */ + + return (error); +} + +/* + * vfs operations + */ +static int +smbfs_unmount(vfs_t *vfsp, int flag, cred_t *cr) +{ + smbmntinfo_t *smi; + smbnode_t *rtnp; + + smi = VFTOSMI(vfsp); + + if (secpolicy_fs_unmount(cr, vfsp) != 0) + return (EPERM); + + if ((flag & MS_FORCE) == 0) { +#ifdef APPLE + smbfs_rflush(vfsp, cr); +#endif + + /* + * If there are any active vnodes on this file system, + * (other than the root vnode) then the file system is + * busy and can't be umounted. + */ + if (smb_check_table(vfsp, smi->smi_root)) + return (EBUSY); + + /* + * We normally hold a ref to the root vnode, so + * check for references beyond the one we expect: + * smbmntinfo_t -> smi_root + * Note that NFS does not hold the root vnode. + */ + if (smi->smi_root && + smi->smi_root->r_vnode->v_count > 1) + return (EBUSY); + } + + /* + * common code for both forced and non-forced + * + * Setting VFS_UNMOUNTED prevents new operations. + * Operations already underway may continue, + * but not for long. + */ + vfsp->vfs_flag |= VFS_UNMOUNTED; + + /* + * Shutdown any outstanding I/O requests on this share, + * and force a tree disconnect. The share object will + * continue to hang around until smb_share_rele(). + * This should also cause most active nodes to be + * released as their operations fail with EIO. + */ + smb_share_kill(smi->smi_share); + + /* + * If we hold the root VP (and we normally do) + * then it's safe to release it now. + */ + if (smi->smi_root) { + rtnp = smi->smi_root; + smi->smi_root = NULL; + VN_RELE(rtnp->r_vnode); /* release root vnode */ + } + + /* + * Remove all nodes from the node hash tables. + * This (indirectly) calls: smb_addfree, smbinactive, + * which will try to flush dirty pages, etc. so + * don't destroy the underlying share just yet. + * + * Also, with a forced unmount, some nodes may + * remain active, and those will get cleaned up + * after their last vn_rele. + */ + smbfs_destroy_table(vfsp); + + /* + * Delete our kstats... + * + * Doing it here, rather than waiting until + * smbfs_freevfs so these are not visible + * after the unmount. + */ + if (smi->smi_io_kstats) { + kstat_delete(smi->smi_io_kstats); + smi->smi_io_kstats = NULL; + } + if (smi->smi_ro_kstats) { + kstat_delete(smi->smi_ro_kstats); + smi->smi_ro_kstats = NULL; + } + + /* + * Note: the smb_share_rele() + * happens in smbfs_freevfs() + */ + + return (0); +} + + +/* + * find root of smbfs + */ +static int +smbfs_root(vfs_t *vfsp, vnode_t **vpp) +{ + smbmntinfo_t *smi; + vnode_t *vp; + + smi = VFTOSMI(vfsp); + + if (curproc->p_zone != smi->smi_zone) + return (EPERM); + + if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + /* + * The root vp is created in mount and held + * until unmount, so this is paranoia. + */ + if (smi->smi_root == NULL) + return (EIO); + + /* Just take a reference and return it. */ + vp = SMBTOV(smi->smi_root); + VN_HOLD(vp); + *vpp = vp; + + return (0); +} + +/* + * Get file system statistics. + */ +static int +smbfs_statvfs(vfs_t *vfsp, statvfs64_t *sbp) +{ + int error; + smbmntinfo_t *smi = VFTOSMI(vfsp); + smb_share_t *ssp = smi->smi_share; + statvfs64_t stvfs; + hrtime_t now; + smb_cred_t scred; + + if (curproc->p_zone != smi->smi_zone) + return (EPERM); + + if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + mutex_enter(&smi->smi_lock); + + /* + * Use cached result if still valid. + */ +recheck: + now = gethrtime(); + if (now < smi->smi_statfstime) { + goto cache_hit; + } + + /* + * FS attributes are stale, so someone + * needs to do an OTW call to get them. + * Serialize here so only one thread + * does the OTW call. + */ + if (smi->smi_status & SM_STATUS_STATFS_BUSY) { + smi->smi_status |= SM_STATUS_STATFS_WANT; + if (!cv_wait_sig(&smi->smi_statvfs_cv, &smi->smi_lock)) { + mutex_exit(&smi->smi_lock); + return (EINTR); + } + /* Hope status is valid now. */ + goto recheck; + } + smi->smi_status |= SM_STATUS_STATFS_BUSY; + mutex_exit(&smi->smi_lock); + + /* + * Do the OTW call. Note: lock NOT held. + */ + smb_credinit(&scred, curproc, NULL); + bzero(&stvfs, sizeof (stvfs)); + error = smbfs_smb_statfs(ssp, &stvfs, &scred); + smb_credrele(&scred); + + mutex_enter(&smi->smi_lock); + if (smi->smi_status & SM_STATUS_STATFS_WANT) + cv_broadcast(&smi->smi_statvfs_cv); + smi->smi_status &= ~(SM_STATUS_STATFS_BUSY | SM_STATUS_STATFS_WANT); + + if (error) { + SMBVDEBUG("statfs error=%d\n", error); + mutex_exit(&smi->smi_lock); + return (error); + } + + /* + * Set a few things the OTW call didn't get. + */ + stvfs.f_frsize = stvfs.f_bsize; + stvfs.f_favail = stvfs.f_ffree; + stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0]; + strncpy(stvfs.f_basetype, vfw.name, FSTYPSZ); + stvfs.f_flag = vf_to_stf(vfsp->vfs_flag); + stvfs.f_namemax = (uint32_t)MAXNAMELEN - 1; + + /* + * Save the result, update lifetime + */ + now = gethrtime(); + smi->smi_statfstime = now + + (SM_MAX_STATFSTIME * (hrtime_t)NANOSEC); + smi->smi_statvfsbuf = stvfs; /* struct assign! */ + + /* + * Copy the statvfs data to caller's buf. + * Note: struct assignment + */ +cache_hit: + *sbp = smi->smi_statvfsbuf; + mutex_exit(&smi->smi_lock); + return (error); +} + +static kmutex_t smbfs_syncbusy; + +/* + * Flush dirty smbfs files for file system vfsp. + * If vfsp == NULL, all smbfs files are flushed. + */ +/*ARGSUSED*/ +static int +smbfs_sync(vfs_t *vfsp, short flag, cred_t *cr) +{ + /* + * Cross-zone calls are OK here, since this translates to a + * VOP_PUTPAGE(B_ASYNC), which gets picked up by the right zone. + */ +#ifdef APPLE + if (!(flag & SYNC_ATTR) && mutex_tryenter(&smbfs_syncbusy) != 0) { + smbfs_rflush(vfsp, cr); + mutex_exit(&smbfs_syncbusy); + } +#endif /* APPLE */ + return (0); +} + +/* + * Initialization routine for VFS routines. Should only be called once + */ +int +smbfs_vfsinit(void) +{ + mutex_init(&smbfs_syncbusy, NULL, MUTEX_DEFAULT, NULL); + return (0); +} + +/* + * Shutdown routine for VFS routines. Should only be called once + */ +void +smbfs_vfsfini(void) +{ + mutex_destroy(&smbfs_syncbusy); +} + +void +smbfs_freevfs(vfs_t *vfsp) +{ + smbmntinfo_t *smi; + smb_share_t *ssp; + + /* free up the resources */ + smi = VFTOSMI(vfsp); + + /* + * By this time we should have already deleted the + * smi kstats in the unmount code. If they are still around + * something is wrong + */ + ASSERT(smi->smi_io_kstats == NULL); + + /* + * Drop our reference to the share. + * This usually leads to VC close. + */ + ssp = smi->smi_share; + smi->smi_share = NULL; + ssp->ss_mount = NULL; + + smb_share_rele(ssp); + + zone_rele(smi->smi_zone); + + smbfs_free_smi(smi); + + /* + * Allow _fini() to succeed now, if so desired. + */ + atomic_dec_32(&smbfs_mountcount); +} + +/* + * smbfs_mount_label_policy: + * Determine whether the mount is allowed according to MAC check, + * by comparing (where appropriate) label of the remote server + * against the label of the zone being mounted into. + * + * Returns: + * 0 : access allowed + * -1 : read-only access allowed (i.e., read-down) + * >0 : error code, such as EACCES + * + * NB: + * NFS supports Cipso labels by parsing the vfs_resource + * to see what the Solaris server global zone has shared. + * We can't support that for CIFS since resource names + * contain share names, not paths. + */ +static int +smbfs_mount_label_policy(vfs_t *vfsp, void *ipaddr, int addr_type, cred_t *cr) +{ + bslabel_t *server_sl, *mntlabel; + zone_t *mntzone = NULL; + ts_label_t *zlabel; + tsol_tpc_t *tp; + ts_label_t *tsl = NULL; + int retv; + + /* + * Get the zone's label. Each zone on a labeled system has a label. + */ + mntzone = zone_find_by_any_path(refstr_value(vfsp->vfs_mntpt), B_FALSE); + zlabel = mntzone->zone_slabel; + ASSERT(zlabel != NULL); + label_hold(zlabel); + + retv = EACCES; /* assume the worst */ + + /* + * Next, get the assigned label of the remote server. + */ + tp = find_tpc(ipaddr, addr_type, B_FALSE); + if (tp == NULL) + goto out; /* error getting host entry */ + + if (tp->tpc_tp.tp_doi != zlabel->tsl_doi) + goto rel_tpc; /* invalid domain */ + if ((tp->tpc_tp.host_type != UNLABELED)) + goto rel_tpc; /* invalid hosttype */ + + server_sl = &tp->tpc_tp.tp_def_label; + mntlabel = label2bslabel(zlabel); + + /* + * Now compare labels to complete the MAC check. If the labels + * are equal or if the requestor is in the global zone and has + * NET_MAC_AWARE, then allow read-write access. (Except for + * mounts into the global zone itself; restrict these to + * read-only.) + * + * If the requestor is in some other zone, but his label + * dominates the server, then allow read-down. + * + * Otherwise, access is denied. + */ + if (blequal(mntlabel, server_sl) || + (crgetzoneid(cr) == GLOBAL_ZONEID && + getpflags(NET_MAC_AWARE, cr) != 0)) { + if ((mntzone == global_zone) || + !blequal(mntlabel, server_sl)) + retv = -1; /* read-only */ + else + retv = 0; /* access OK */ + } else if (bldominates(mntlabel, server_sl)) { + retv = -1; /* read-only */ + } else { + retv = EACCES; + } + + if (tsl != NULL) + label_rele(tsl); + +rel_tpc: + /*LINTED*/ + TPC_RELE(tp); +out: + if (mntzone) + zone_rele(mntzone); + label_rele(zlabel); + return (retv); +} diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c new file mode 100644 index 0000000000..63754da4f4 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c @@ -0,0 +1,2498 @@ +/* + * 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. + * + * $Id: smbfs_vnops.c,v 1.128.36.1 2005/05/27 02:35:28 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/systm.h> +#include <sys/cred.h> +#include <sys/vnode.h> +#include <sys/vfs.h> +#include <sys/uio.h> +#include <sys/dirent.h> +#include <sys/errno.h> +#include <sys/sysmacros.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/dnlc.h> +#include <sys/vfs_opreg.h> +#include <sys/policy.h> + +#include <netsmb/smb_osdep.h> +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> + +#include <smbfs/smbfs.h> +#include <smbfs/smbfs_node.h> +#include <smbfs/smbfs_subr.h> + +#include <fs/fs_subr.h> + +/* + * These characters are illegal in NTFS file names. + * ref: http://support.microsoft.com/kb/147438 + */ +static const char illegal_chars[] = { + '\\', /* back slash */ + '/', /* slash */ + ':', /* colon */ + '*', /* asterisk */ + '?', /* question mark */ + '"', /* double quote */ + '<', /* less than sign */ + '>', /* greater than sign */ + '|', /* vertical bar */ + 0 +}; + +/* + * Turning this on causes nodes to be created in the cache + * during directory listings. The "fast" claim is debatable, + * and the effects on the cache can be undesirable. + */ + +/* local static function defines */ + +static int smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, + int dnlc, caller_context_t *); +static int smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, + cred_t *cr, caller_context_t *); +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 *); +/* + * These are the vnode ops routines which implement the vnode interface to + * the networked file system. These routines just take their parameters, + * make them look networkish by putting the right info into interface structs, + * and then calling the appropriate remote routine(s) to do the work. + * + * Note on directory name lookup cacheing: If we detect a stale fhandle, + * we purge the directory cache relative to that vnode. This way, the + * user won't get burned by the cache repeatedly. See <smbfs/smbnode.h> for + * more details on smbnode locking. + */ + +static int smbfs_open(vnode_t **, int, cred_t *, caller_context_t *); +static int smbfs_close(vnode_t *, int, int, offset_t, cred_t *, + caller_context_t *); +static int smbfs_read(vnode_t *, struct uio *, int, cred_t *, + caller_context_t *); +static int smbfs_write(vnode_t *, struct uio *, int, cred_t *, + caller_context_t *); +static int smbfs_getattr(vnode_t *, struct vattr *, int, cred_t *, + caller_context_t *); +static int smbfs_setattr(vnode_t *, struct vattr *, int, cred_t *, + caller_context_t *); +static int smbfs_access(vnode_t *, int, int, cred_t *, caller_context_t *); +static int smbfs_fsync(vnode_t *, int, cred_t *, caller_context_t *); +static void smbfs_inactive(vnode_t *, cred_t *, caller_context_t *); +static int smbfs_lookup(vnode_t *, char *, vnode_t **, struct pathname *, + int, vnode_t *, cred_t *, caller_context_t *, + int *, pathname_t *); +static int smbfs_create(vnode_t *, char *, struct vattr *, enum vcexcl, + int, vnode_t **, cred_t *, int, caller_context_t *, + vsecattr_t *); +static int smbfs_remove(vnode_t *, char *, cred_t *, caller_context_t *, + int); +static int smbfs_rename(vnode_t *, char *, vnode_t *, char *, cred_t *, + caller_context_t *, int); +static int smbfs_mkdir(vnode_t *, char *, struct vattr *, vnode_t **, + cred_t *, caller_context_t *, int, vsecattr_t *); +static int smbfs_rmdir(vnode_t *, char *, vnode_t *, cred_t *, + caller_context_t *, int); +static int smbfs_readdir(vnode_t *, struct uio *, cred_t *, int *, + caller_context_t *, int); +static int smbfs_rwlock(vnode_t *, int, caller_context_t *); +static void smbfs_rwunlock(vnode_t *, int, caller_context_t *); +static int smbfs_seek(vnode_t *, offset_t, offset_t *, caller_context_t *); +static int smbfs_frlock(vnode_t *, int, struct flock64 *, int, offset_t, + struct flk_callback *, cred_t *, caller_context_t *); +static int smbfs_space(vnode_t *, int, struct flock64 *, int, offset_t, + cred_t *, caller_context_t *); +static int smbfs_pathconf(vnode_t *, int, ulong_t *, cred_t *, + caller_context_t *); +static int smbfs_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *, + caller_context_t *); + +/* Dummy function to use until correct function is ported in */ +int noop_vnodeop() { + return (0); +} + +struct vnodeops *smbfs_vnodeops = NULL; + +/* + * Most unimplemented ops will return ENOSYS because of fs_nosys(). + * The only ops where that won't work are ACCESS (due to open(2) + * failures) and GETSECATTR (due to acl(2) failures). + */ +const fs_operation_def_t smbfs_vnodeops_template[] = { + { VOPNAME_OPEN, { .vop_open = smbfs_open } }, + { VOPNAME_CLOSE, { .vop_close = smbfs_close } }, + { VOPNAME_READ, { .vop_read = smbfs_read } }, + { VOPNAME_WRITE, { .vop_write = smbfs_write } }, + { VOPNAME_IOCTL, { .error = fs_nosys } }, /* smbfs_ioctl, */ + { VOPNAME_GETATTR, { .vop_getattr = smbfs_getattr } }, + { VOPNAME_SETATTR, { .vop_setattr = smbfs_setattr } }, + { VOPNAME_ACCESS, { .vop_access = smbfs_access } }, + { VOPNAME_LOOKUP, { .vop_lookup = smbfs_lookup } }, + { VOPNAME_CREATE, { .vop_create = smbfs_create } }, + { VOPNAME_REMOVE, { .vop_remove = smbfs_remove } }, + { VOPNAME_LINK, { .error = fs_nosys } }, /* smbfs_link, */ + { VOPNAME_RENAME, { .vop_rename = smbfs_rename } }, + { VOPNAME_MKDIR, { .vop_mkdir = smbfs_mkdir } }, + { VOPNAME_RMDIR, { .vop_rmdir = smbfs_rmdir } }, + { VOPNAME_READDIR, { .vop_readdir = smbfs_readdir } }, + { VOPNAME_SYMLINK, { .error = fs_nosys } }, /* smbfs_symlink, */ + { VOPNAME_READLINK, { .error = fs_nosys } }, /* smbfs_readlink, */ + { VOPNAME_FSYNC, { .vop_fsync = smbfs_fsync } }, + { VOPNAME_INACTIVE, { .vop_inactive = smbfs_inactive } }, + { VOPNAME_FID, { .error = fs_nosys } }, /* smbfs_fid, */ + { VOPNAME_RWLOCK, { .vop_rwlock = smbfs_rwlock } }, + { VOPNAME_RWUNLOCK, { .vop_rwunlock = smbfs_rwunlock } }, + { VOPNAME_SEEK, { .vop_seek = smbfs_seek } }, + { VOPNAME_FRLOCK, { .vop_frlock = smbfs_frlock } }, + { VOPNAME_SPACE, { .vop_space = smbfs_space } }, + { VOPNAME_REALVP, { .error = fs_nosys } }, /* smbfs_realvp, */ + { VOPNAME_GETPAGE, { .error = fs_nosys } }, /* smbfs_getpage, */ + { VOPNAME_PUTPAGE, { .error = fs_nosys } }, /* smbfs_putpage, */ + { VOPNAME_MAP, { .error = fs_nosys } }, /* smbfs_map, */ + { VOPNAME_ADDMAP, { .error = fs_nosys } }, /* smbfs_addmap, */ + { VOPNAME_DELMAP, { .error = fs_nosys } }, /* smbfs_delmap, */ + { VOPNAME_DUMP, { .error = fs_nosys } }, /* smbfs_dump, */ + { VOPNAME_PATHCONF, { .vop_pathconf = smbfs_pathconf } }, + { VOPNAME_PAGEIO, { .error = fs_nosys } }, /* smbfs_pageio, */ + { VOPNAME_SETSECATTR, { .error = fs_nosys } }, /* smbfs_setsecattr, */ + { VOPNAME_GETSECATTR, { .error = noop_vnodeop } }, + /* smbfs_getsecattr, */ + { VOPNAME_SHRLOCK, { .vop_shrlock = smbfs_shrlock } }, + { NULL, NULL } +}; + +/* + * XXX + * When new and relevant functionality is enabled, we should be + * calling vfs_set_feature() to inform callers that pieces of + * functionality are available, per PSARC 2007/227, e.g. + * + * VFSFT_XVATTR Supports xvattr for attrs + * VFSFT_CASEINSENSITIVE Supports case-insensitive + * VFSFT_NOCASESENSITIVE NOT case-sensitive + * VFSFT_DIRENTFLAGS Supports dirent flags + * VFSFT_ACLONCREATE Supports ACL on create + * VFSFT_ACEMASKONACCESS Can use ACEMASK for access + */ +/* ARGSUSED */ +static int +smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) +{ + struct vattr va; + smbnode_t *np; + vnode_t *vp; + u_int32_t rights, rightsrcvd; + u_int16_t fid, oldfid; + struct smb_cred scred; + smbmntinfo_t *smi; + cred_t *oldcr; + int attrcacheupdated = 0; + int tmperror; + int error = 0; + + vp = *vpp; + np = VTOSMB(vp); + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + if (vp->v_type != VREG && vp->v_type != VDIR) { /* XXX VLNK? */ + SMBVDEBUG("open eacces vtype=%d\n", vp->v_type); + return (EACCES); + } + + /* + * Get exclusive access to n_fid and related stuff. + * No returns after this until out. + */ + if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, SMBINTR(vp))) + return (EINTR); + smb_credinit(&scred, curproc, cr); + + /* + * Directory open is easy. + */ + if (vp->v_type == VDIR) { + np->n_dirrefs++; + goto have_fid; + } + + /* + * If caller specified O_TRUNC/FTRUNC, then be sure to set + * FWRITE (to drive successful setattr(size=0) after open) + */ + if (flag & FTRUNC) + flag |= FWRITE; + + /* + * If we already have it open, check to see if current rights + * are sufficient for this open. + */ + if (np->n_fidrefs) { + int upgrade = 0; + + /* BEGIN CSTYLED */ + if ((flag & FWRITE) && + !(np->n_rights & (SA_RIGHT_FILE_WRITE_DATA | + GENERIC_RIGHT_ALL_ACCESS | + GENERIC_RIGHT_WRITE_ACCESS))) + upgrade = 1; + if ((flag & FREAD) && + !(np->n_rights & (SA_RIGHT_FILE_READ_DATA | + GENERIC_RIGHT_ALL_ACCESS | + GENERIC_RIGHT_READ_ACCESS))) + upgrade = 1; + /* END CSTYLED */ + if (!upgrade) { + /* + * the existing open is good enough + */ + np->n_fidrefs++; + goto have_fid; + } + } + rights = np->n_fidrefs ? np->n_rights : 0; + + /* + * we always ask for READ_CONTROL so we can always get the + * owner/group IDs to satisfy a stat. + * XXX: verify that works with "drop boxes" + */ + rights |= STD_RIGHT_READ_CONTROL_ACCESS; + if ((flag & FREAD)) + rights |= SA_RIGHT_FILE_READ_DATA; + if ((flag & FWRITE)) + rights |= SA_RIGHT_FILE_APPEND_DATA | SA_RIGHT_FILE_WRITE_DATA; + + /* XXX: open gets the current size, but we don't use it. */ + error = smbfs_smb_open(np, rights, &scred, &attrcacheupdated, &fid, + NULL, 0, 0, NULL, &rightsrcvd); + if (error) + goto out; + + /* + * We have a new FID and access rights. + */ + oldfid = np->n_fid; + np->n_fid = fid; + np->n_rights = rightsrcvd; + np->n_fidrefs++; + if (np->n_fidrefs > 1) { + /* + * We already had it open (presumably because + * it was open with insufficient rights.) + * Close old wire-open. + */ + tmperror = smbfs_smb_close(smi->smi_share, + oldfid, &np->n_mtime, &scred); + if (tmperror) + SMBVDEBUG("error %d closing %s\n", + tmperror, np->n_rpath); + } + + /* + * This thread did the open. + * Save our credentials too. + */ + mutex_enter(&np->r_statelock); + oldcr = np->r_cred; + np->r_cred = cr; + crhold(cr); + if (oldcr) + crfree(oldcr); + mutex_exit(&np->r_statelock); + +have_fid: + /* Get attributes (maybe). */ + + + /* Darwin (derived) code. */ + + va.va_mask = AT_MTIME; + if (np->n_flag & NMODIFIED) + smbfs_attr_cacheremove(np); + + /* + * Try to get attributes, but don't bail on error. + * We already hold r_lkserlock/reader so note: + * this call will recursively take r_lkserlock. + */ + tmperror = smbfsgetattr(vp, &va, cr); + if (tmperror) + SMBERROR("getattr failed, error=%d", tmperror); + else + np->n_mtime.tv_sec = va.va_mtime.tv_sec; + +out: + smb_credrele(&scred); + smbfs_rw_exit(&np->r_lkserlock); + return (error); +} + +/*ARGSUSED*/ +static int +smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, + caller_context_t *ct) +{ + smbnode_t *np; + int error = 0; + struct smb_cred scred; + + np = VTOSMB(vp); + + /* + * Don't "bail out" for VFS_UNMOUNTED here, + * as we want to do cleanup, etc. + */ + + /* + * zone_enter(2) prevents processes from changing zones with SMBFS files + * open; if we happen to get here from the wrong zone we can't do + * anything over the wire. + */ + if (VTOSMI(vp)->smi_zone != curproc->p_zone) { + /* + * We could attempt to clean up locks, except we're sure + * that the current process didn't acquire any locks on + * the file: any attempt to lock a file belong to another zone + * will fail, and one can't lock an SMBFS file and then change + * zones, as that fails too. + * + * Returning an error here is the sane thing to do. A + * subsequent call to VN_RELE() which translates to a + * smbfs_inactive() will clean up state: if the zone of the + * vnode's origin is still alive and kicking, an async worker + * thread will handle the request (from the correct zone), and + * everything (minus the final smbfs_getattr_otw() call) should + * be OK. If the zone is going away smbfs_async_inactive() will + * throw away cached pages inline. + */ + return (EIO); + } + + /* + * If we are using local locking for this filesystem, then + * release all of the SYSV style record locks. Otherwise, + * we are doing network locking and we need to release all + * of the network locks. All of the locks held by this + * process on this file are released no matter what the + * incoming reference count is. + */ + if (VTOSMI(vp)->smi_flags & SMI_LLOCK) { + cleanlocks(vp, ttoproc(curthread)->p_pid, 0); + cleanshares(vp, ttoproc(curthread)->p_pid); + } + + if (count > 1) + return (0); + /* + * OK, do "last close" stuff. + */ + + + /* + * Do the CIFS close. + * Darwin code + */ + + /* + * Exclusive lock for modifying n_fid stuff. + * Don't want this one ever interruptible. + */ + (void) smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, 0); + smb_credinit(&scred, curproc, cr); + + error = 0; + if (vp->v_type == VDIR) { + struct smbfs_fctx *fctx; + ASSERT(np->n_dirrefs > 0); + if (--np->n_dirrefs) + goto out; + if ((fctx = np->n_dirseq) != NULL) { + np->n_dirseq = NULL; + error = smbfs_smb_findclose(fctx, &scred); + } + } else { + uint16_t ofid; + ASSERT(np->n_fidrefs > 0); + if (--np->n_fidrefs) + goto out; + if ((ofid = np->n_fid) != SMB_FID_UNUSED) { + np->n_fid = SMB_FID_UNUSED; + error = smbfs_smb_close(np->n_mount->smi_share, + ofid, NULL, &scred); + } + } + if (error) { + SMBERROR("error %d closing %s\n", + error, np->n_rpath); + } + + if (np->n_flag & NATTRCHANGED) + smbfs_attr_cacheremove(np); + +out: + smb_credrele(&scred); + smbfs_rw_exit(&np->r_lkserlock); + + /* don't return any errors */ + return (0); +} + +/* ARGSUSED */ +static int +smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, + caller_context_t *ct) +{ + int error; + struct vattr va; + smbmntinfo_t *smi; + smbnode_t *np; + /* u_offset_t off; */ + /* offset_t diff; */ + + np = VTOSMB(vp); + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_READER)); + + if (vp->v_type != VREG) + return (EISDIR); + + if (uiop->uio_resid == 0) + return (0); + + /* + * Like NFS3, just check for 63-bit overflow. + * Our SMB layer takes care to return EFBIG + * when it has to fallback to a 32-bit call. + */ + if (uiop->uio_loffset < 0 || + uiop->uio_loffset + uiop->uio_resid < 0) + return (EINVAL); + + /* Shared lock for n_fid use in smbfs_readvnode */ + if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) + return (EINTR); + + /* get vnode attributes from server */ + va.va_mask = AT_SIZE | AT_MTIME; + if (error = smbfsgetattr(vp, &va, cr)) + goto out; + + /* should probably update mtime with mtime from server here */ + + /* + * Darwin had a loop here that handled paging stuff. + * Solaris does paging differently, so no loop needed. + */ + error = smbfs_readvnode(vp, uiop, cr, &va); + +out: + smbfs_rw_exit(&np->r_lkserlock); + return (error); + +} + + +/* ARGSUSED */ +static int +smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, + caller_context_t *ct) +{ + int error; + smbmntinfo_t *smi; + smbnode_t *np; + int timo = SMBWRTTIMO; + + np = VTOSMB(vp); + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_WRITER)); + + if (vp->v_type != VREG) + return (EISDIR); + + if (uiop->uio_resid == 0) + return (0); + + /* Shared lock for n_fid use in smbfs_writevnode */ + if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) + return (EINTR); + + + /* + * Darwin had a loop here that handled paging stuff. + * Solaris does paging differently, so no loop needed. + */ + error = smbfs_writevnode(vp, uiop, cr, ioflag, timo); + + smbfs_rw_exit(&np->r_lkserlock); + return (error); + +} + + +/* + * Return either cached or remote attributes. If get remote attr + * use them to check and invalidate caches, then cache the new attributes. + * + * XXX + * This op should eventually support PSARC 2007/315, Extensible Attribute + * Interfaces, for richer metadata. + */ +/* ARGSUSED */ +static int +smbfs_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + smbnode_t *np; + smbmntinfo_t *smi; + + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + /* + * If it has been specified that the return value will + * just be used as a hint, and we are only being asked + * for size, fsid or rdevid, then return the client's + * notion of these values without checking to make sure + * that the attribute cache is up to date. + * The whole point is to avoid an over the wire GETATTR + * call. + */ + np = VTOSMB(vp); + if (flags & ATTR_HINT) { + if (vap->va_mask == + (vap->va_mask & (AT_SIZE | AT_FSID | AT_RDEV))) { + mutex_enter(&np->r_statelock); + if (vap->va_mask | AT_SIZE) + vap->va_size = np->r_size; + if (vap->va_mask | AT_FSID) + vap->va_fsid = np->r_attr.va_fsid; + if (vap->va_mask | AT_RDEV) + vap->va_rdev = np->r_attr.va_rdev; + mutex_exit(&np->r_statelock); + return (0); + } + } + + + return (smbfsgetattr(vp, vap, cr)); +} + +/* + * Mostly from Darwin smbfs_getattr() + */ +int +smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr) +{ + int error; + smbnode_t *np; + struct smb_cred scred; + struct smbfattr fattr; + + ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone); + + np = VTOSMB(vp); + + /* + * If we've got cached attributes, we're done, otherwise go + * to the server to get attributes, which will update the cache + * in the process. + * + * This section from Darwin smbfs_getattr, + * but then modified a lot. + */ + error = smbfs_attr_cachelookup(vp, vap); + if (error != ENOENT) + return (error); + + /* Shared lock for (possible) n_fid use. */ + if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) + return (EINTR); + smb_credinit(&scred, curproc, cr); + + error = smbfs_smb_getfattr(np, &fattr, &scred); + + smb_credrele(&scred); + smbfs_rw_exit(&np->r_lkserlock); + + if (!error) { + smbfs_attr_cacheenter(vp, &fattr); + error = smbfs_attr_cachelookup(vp, vap); + } + return (error); +} + +/* + * XXX + * This op should eventually support PSARC 2007/315, Extensible Attribute + * Interfaces, for richer metadata. + */ +/*ARGSUSED4*/ +static int +smbfs_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, + caller_context_t *ct) +{ + int error; + uint_t mask; + struct vattr oldva; + smbmntinfo_t *smi; + + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + mask = vap->va_mask; + if (mask & AT_NOSET) + return (EINVAL); + + oldva.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; + error = smbfsgetattr(vp, &oldva, cr); + if (error) + return (error); + + error = secpolicy_vnode_setattr(cr, vp, vap, &oldva, flags, + smbfs_accessx, vp); + if (error) + return (error); + + return (smbfssetattr(vp, vap, flags, cr)); +} + +/* + * Mostly from Darwin smbfs_setattr() + * but then modified a lot. + */ +/* ARGSUSED */ +static int +smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) +{ + int error = 0; + smbnode_t *np = VTOSMB(vp); + smbmntinfo_t *smi = VTOSMI(vp); + 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; + uint32_t rights = 0; + + ASSERT(curproc->p_zone == smi->smi_zone); + + /* + * If our caller is trying to set multiple attributes, they + * can make no assumption about what order they are done in. + * Here we try to do them in order of decreasing likelihood + * of failure, just to minimize the chance we'll wind up + * 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, curproc, cr); + + /* + * Will we need an open handle for this setattr? + * If so, what rights will we need? + */ + if (mask & (AT_ATIME | AT_MTIME)) { + rights |= + SA_RIGHT_FILE_WRITE_ATTRIBUTES | + GENERIC_RIGHT_ALL_ACCESS | + GENERIC_RIGHT_WRITE_ACCESS; + } + if (mask & AT_SIZE) { + rights |= + SA_RIGHT_FILE_WRITE_DATA | + SA_RIGHT_FILE_APPEND_DATA; + /* + * Only SIZE requires a handle. + * XXX May be more reliable to just + * always get the file handle here. + */ + error = smbfs_smb_tmpopen(np, rights, &scred, &fid); + if (error) { + SMBVDEBUG("error %d opening %s\n", + error, np->n_rpath); + goto out; + } + have_fid = 1; + } + + + /* + * If the server supports the UNIX extensions, right here is where + * we'd support changes to uid, gid, mode, and possibly va_flags. + * For now we claim to have made any such changes. + */ + + if (mask & AT_SIZE) { + /* + * If the new file size is less than what the client sees as + * the file size, then just change the size and invalidate + * the pages. + * I am commenting this code at present because the function + * smbfs_putapage() is not yet implemented. + */ + + /* + * Set the file size to vap->va_size. + */ + ASSERT(have_fid); + error = smbfs_smb_setfsize(np, fid, vap->va_size, &scred); + if (error) { + SMBVDEBUG("setsize error %d file %s\n", + error, np->n_rpath); + } else { + /* + * Darwin had code here to zero-extend. + * Tests indicate the server will zero-fill, + * so looks like we don't need to do this. + * Good thing, as this could take forever. + */ + mutex_enter(&np->r_statelock); + np->r_size = vap->va_size; + mutex_exit(&np->r_statelock); + modified = 1; + } + } + + /* + * XXX: When Solaris has create_time, set that too. + * Note: create_time is different from ctime. + */ + mtime = ((mask & AT_MTIME) ? &vap->va_mtime : 0); + atime = ((mask & AT_ATIME) ? &vap->va_atime : 0); + + if (mtime || atime) { + /* + * If file is opened with write-attributes capability, + * we use handle-based calls. If not, we use path-based ones. + */ + if (have_fid) { + error = smbfs_smb_setfattr(np, fid, + np->n_dosattr, mtime, atime, &scred); + } else { + error = smbfs_smb_setpattr(np, + np->n_dosattr, mtime, atime, &scred); + } + if (error) { + SMBVDEBUG("set times error %d file %s\n", + error, np->n_rpath); + } else { + /* XXX: set np->n_mtime, etc? */ + modified = 1; + } + } + +out: + if (modified) { + /* + * Invalidate attribute cache in case if server doesn't set + * required attributes. + */ + smbfs_attr_cacheremove(np); + /* + * XXX Darwin called _getattr here to + * update the mtime. Should we? + */ + } + + if (have_fid) { + cerror = smbfs_smb_tmpclose(np, fid, &scred); + if (cerror) + SMBERROR("error %d closing %s\n", + cerror, np->n_rpath); + } + + smb_credrele(&scred); + smbfs_rw_exit(&np->r_lkserlock); + + return (error); +} + +/* + * smbfs_access_rwx() + * Common function for smbfs_access, etc. + * + * The security model implemented by the FS is unusual + * due to our "single user mounts" restriction. + * + * All access under a given mount point uses the CIFS + * credentials established by the owner of the mount. + * The Unix uid/gid/mode information is not (easily) + * provided by CIFS, and is instead fabricated using + * settings held in the mount structure. + * + * Most access checking is handled by the CIFS server, + * but we need sufficient Unix access checks here to + * prevent other local Unix users from having access + * to objects under this mount that the uid/gid/mode + * settings in the mount would not allow. + * + * With this model, there is a case where we need the + * ability to do an access check before we have the + * vnode for an object. This function takes advantage + * of the fact that the uid/gid/mode is per mount, and + * avoids the need for a vnode. + * + * We still (sort of) need a vnode when we call + * secpolicy_vnode_access, but that only uses + * the vtype field, so we can use a pair of fake + * vnodes that have only v_type filled in. + * + * XXX: Later, add a new secpolicy_vtype_access() + * that takes the vtype instead of a vnode, and + * get rid of the tmpl_vxxx fake vnodes below. + */ +static int +smbfs_access_rwx(vfs_t *vfsp, int vtype, int mode, cred_t *cr) +{ + /* See the secpolicy call below. */ + static const vnode_t tmpl_vdir = { .v_type = VDIR }; + static const vnode_t tmpl_vreg = { .v_type = VREG }; + vattr_t va; + vnode_t *tvp; + struct smbmntinfo *smi = VFTOSMI(vfsp); + int shift = 0; + + /* + * Build our (fabricated) vnode attributes. + * XXX: Could make these templates in the + * per-mount struct and use them here. + */ + bzero(&va, sizeof (va)); + va.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID; + va.va_type = vtype; + va.va_mode = (vtype == VDIR) ? + smi->smi_args.dir_mode : + smi->smi_args.file_mode; + va.va_uid = smi->smi_args.uid; + va.va_gid = smi->smi_args.gid; + + /* + * Disallow write attempts on read-only file systems, + * unless the file is a device or fifo node. Note: + * Inline vn_is_readonly and IS_DEVVP here because + * we may not have a vnode ptr. Original expr. was: + * (mode & VWRITE) && vn_is_readonly(vp) && !IS_DEVVP(vp)) + */ + if ((mode & VWRITE) && + (vfsp->vfs_flag & VFS_RDONLY) && + !(vtype == VCHR || vtype == VBLK || vtype == VFIFO)) + return (EROFS); + + /* + * Disallow attempts to access mandatory lock files. + * Similarly, expand MANDLOCK here. + * XXX: not sure we need this. + */ + if ((mode & (VWRITE | VREAD | VEXEC)) && + va.va_type == VREG && MANDMODE(va.va_mode)) + return (EACCES); + + /* + * Access check is based on only + * one of owner, group, public. + * If not owner, then check group. + * If not a member of the group, + * then check public access. + */ + if (crgetuid(cr) != va.va_uid) { + shift += 3; + if (!groupmember(va.va_gid, cr)) + shift += 3; + } + mode &= ~(va.va_mode << shift); + if (mode == 0) + return (0); + + /* + * We need a vnode for secpolicy_vnode_access, + * but the only thing it looks at is v_type, + * so pass one of the templates above. + */ + tvp = (va.va_type == VDIR) ? + (vnode_t *)&tmpl_vdir : + (vnode_t *)&tmpl_vreg; + return (secpolicy_vnode_access(cr, tvp, va.va_uid, mode)); +} + +/* + * See smbfs_setattr + */ +static int +smbfs_accessx(void *arg, int mode, cred_t *cr) +{ + vnode_t *vp = arg; + /* + * Note: The caller has checked the current zone, + * the SMI_DEAD and VFS_UNMOUNTED flags, etc. + */ + return (smbfs_access_rwx(vp->v_vfsp, vp->v_type, mode, cr)); +} + +/* + * XXX + * This op should support PSARC 2007/403, Modified Access Checks for CIFS + */ +/* ARGSUSED */ +static int +smbfs_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct) +{ + vfs_t *vfsp; + smbmntinfo_t *smi; + + vfsp = vp->v_vfsp; + smi = VFTOSMI(vfsp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + return (smbfs_access_rwx(vfsp, vp->v_type, mode, cr)); +} + + +/* + * Flush local dirty pages to stable storage on the server. + * + * If FNODSYNC is specified, then there is nothing to do because + * metadata changes are not cached on the client before being + * sent to the server. + * + * Currently, this is a no-op since we don't cache data, either. + */ +/* ARGSUSED */ +static int +smbfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) +{ + int error = 0; + smbmntinfo_t *smi; + + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + if ((syncflag & FNODSYNC) || IS_SWAPVP(vp)) + return (0); + + return (error); +} + +/* + * Last reference to vnode went away. + */ +/* ARGSUSED */ +static void +smbfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) +{ + smbnode_t *np; + + /* + * Don't "bail out" for VFS_UNMOUNTED here, + * as we want to do cleanup, etc. + * See also pcfs_inactive + */ + + np = VTOSMB(vp); + + /* + * If this is coming from the wrong zone, we let someone in the right + * zone take care of it asynchronously. We can get here due to + * VN_RELE() being called from pageout() or fsflush(). This call may + * potentially turn into an expensive no-op if, for instance, v_count + * gets incremented in the meantime, but it's still correct. + */ + + /* + * Some paranoia from the Darwin code: + * Make sure the FID was closed. + * If we see this, it's a bug! + * + * No rw_enter here, as this should be the + * last ref, and we're just looking... + */ + if (np->n_fidrefs > 0) { + SMBVDEBUG("opencount %d fid %d file %s\n", + np->n_fidrefs, np->n_fid, np->n_rpath); + } + if (np->n_dirrefs > 0) { + uint_t fid = (np->n_dirseq) ? + np->n_dirseq->f_Sid : 0; + SMBVDEBUG("opencount %d fid %d dir %s\n", + np->n_dirrefs, fid, np->n_rpath); + } + + smb_addfree(np); +} + +/* + * Remote file system operations having to do with directory manipulation. + */ +/* ARGSUSED */ +static int +smbfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp, + int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct, + int *direntflags, pathname_t *realpnp) +{ + int error; + smbnode_t *dnp; + smbmntinfo_t *smi; + + smi = VTOSMI(dvp); + + if (curproc->p_zone != smi->smi_zone) + return (EPERM); + + if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + dnp = VTOSMB(dvp); + if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_READER, SMBINTR(dvp))) { + error = EINTR; + goto out; + } + + error = smbfslookup(dvp, nm, vpp, cr, 1, ct); + + smbfs_rw_exit(&dnp->r_rwlock); + +out: + return (error); +} + +/* ARGSUSED */ +static int +smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc, + caller_context_t *ct) +{ + int error; + int supplen; /* supported length */ + vnode_t *vp; + smbnode_t *dnp; + smbmntinfo_t *smi; + /* struct smb_vc *vcp; */ + const char *name = (const char *)nm; + int nmlen = strlen(nm); + int rplen; + struct smb_cred scred; + struct smbfattr fa; + + smi = VTOSMI(dvp); + dnp = VTOSMB(dvp); + + ASSERT(curproc->p_zone == smi->smi_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. + * XXX: Can we check without looking directly + * inside the struct smbfs_rwlock_t? + */ + ASSERT(dnp->r_rwlock.count != 0); + + /* + * If lookup is for "", just return dvp. Don't need + * to send it over the wire, look it up in the dnlc, + * or perform any access checks. + */ + if (nmlen == 0) { + VN_HOLD(dvp); + *vpp = dvp; + return (0); + } + + /* if the name is longer that what is supported, return an error */ + if (nmlen > supplen) + return (ENAMETOOLONG); + + /* + * Avoid surprises with characters that are + * illegal in Windows file names. + * Todo: CATIA mappings XXX + */ + if (strpbrk(nm, illegal_chars)) + return (EINVAL); + + /* if the dvp is not a directory, return an error */ + if (dvp->v_type != VDIR) + return (ENOTDIR); + + /* Need search permission in the directory. */ + error = smbfs_access(dvp, VEXEC, 0, cr, ct); + if (error) + return (error); + + /* + * If lookup is for ".", just return dvp. Don't need + * to send it over the wire or look it up in the dnlc, + * just need to check access (done above). + */ + if (nmlen == 1 && name[0] == '.') { + VN_HOLD(dvp); + *vpp = dvp; + return (0); + } + +#ifdef NOT_YET + if (dnlc) { + /* + * NOTE: search the dnlc here + */ + } +#endif + + /* + * Handle lookup of ".." which is quite tricky, + * because the protocol gives us little help. + * + * We keep full pathnames (as seen on the server) + * so we can just trim off the last component to + * get the full pathname of the parent. Note: + * We don't actually copy and modify, but just + * compute the trimmed length and pass that with + * the current dir path (not null terminated). + * + * We don't go over-the-wire to get attributes + * for ".." because we know it's a directory, + * and we can just leave the rest "stale" + * until someone does a getattr. + */ + if (nmlen == 2 && name[0] == '.' && name[1] == '.') { + if (dvp->v_flag & VROOT) { + /* + * Already at the root. This can happen + * with directory listings at the root, + * which lookup "." and ".." to get the + * inode numbers. Let ".." be the same + * as "." in the FS root. + */ + VN_HOLD(dvp); + *vpp = dvp; + return (0); + } + + /* + * Find the parent path length. + */ + rplen = dnp->n_rplen; + ASSERT(rplen > 0); + while (--rplen >= 0) { + if (dnp->n_rpath[rplen] == '\\') + break; + } + if (rplen == 0) { + /* Found our way to the root. */ + vp = SMBTOV(smi->smi_root); + VN_HOLD(vp); + *vpp = vp; + return (0); + } + vp = smbfs_make_node(dvp->v_vfsp, + dnp->n_rpath, rplen, + NULL, 0, NULL); + if (vp == NULL) { + return (ENOENT); + } + vp->v_type = VDIR; + + /* Success! */ + *vpp = vp; + return (0); + } + + /* + * Normal lookup of a child node. + * Note we handled "." and ".." above. + * + * First, go over-the-wire to get the + * node type (and attributes). + */ + smb_credinit(&scred, curproc, cr); + /* Note: this can allocate a new "name" */ + error = smbfs_smb_lookup(dnp, &name, &nmlen, &fa, &scred); + smb_credrele(&scred); + if (error) + goto out; + + /* + * Find or create the node. + */ + error = smbfs_nget(dvp, name, nmlen, &fa, &vp); + if (error) + goto out; + + /* Success! */ + *vpp = vp; + +out: + /* smbfs_smb_lookup may have allocated name. */ + if (name != nm) + smbfs_name_free(name, nmlen); + + return (error); +} + +/* + * XXX + * vsecattr_t is new to build 77, and we need to eventually support + * it in order to create an ACL when an object is created. + * + * This op should support the new FIGNORECASE flag for case-insensitive + * lookups, per PSARC 2007/244. + */ +/* ARGSUSED */ +static int +smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, + int mode, vnode_t **vpp, cred_t *cr, int lfaware, caller_context_t *ct, + vsecattr_t *vsecp) +{ + int error; + int cerror; + vfs_t *vfsp; + vnode_t *vp; +#ifdef NOT_YET + smbnode_t *np; +#endif + smbnode_t *dnp; + smbmntinfo_t *smi; + struct vattr vattr; + struct smbfattr fattr; + struct smb_cred scred; + const char *name = (const char *)nm; + int nmlen = strlen(nm); + uint32_t disp; + uint16_t fid; + + vfsp = dvp->v_vfsp; + smi = VFTOSMI(vfsp); + dnp = VTOSMB(dvp); + vp = NULL; + + if (curproc->p_zone != smi->smi_zone) + return (EPERM); + + if (smi->smi_flags & SMI_DEAD || vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + /* + * Note: this may break mknod(2) calls to create a directory, + * but that's obscure use. Some other filesystems do this. + * XXX: Later, redirect VDIR type here to _mkdir. + */ + if (va->va_type != VREG) + return (EINVAL); + + /* + * If the pathname is "", just use dvp, no checks. + * Do this outside of the rwlock (like zfs). + */ + if (nmlen == 0) { + VN_HOLD(dvp); + *vpp = dvp; + return (0); + } + + /* Don't allow "." or ".." through here. */ + if ((nmlen == 1 && name[0] == '.') || + (nmlen == 2 && name[0] == '.' && name[1] == '.')) + return (EISDIR); + + /* + * We make a copy of the attributes because the caller does not + * expect us to change what va points to. + */ + vattr = *va; + + if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp))) + return (EINTR); + smb_credinit(&scred, curproc, cr); + + /* + * XXX: Do we need r_lkserlock too? + * No use of any shared fid or fctx... + */ + + /* + * NFS needs to go over the wire, just to be sure whether the + * file exists or not. Using the DNLC can be dangerous in + * this case when making a decision regarding existence. + * + * The SMB protocol does NOT really need to go OTW here + * thanks to the expressive NTCREATE disposition values. + * Unfortunately, to do Unix access checks correctly, + * we need to know if the object already exists. + * When the object does not exist, we need VWRITE on + * the directory. Note: smbfslookup() checks VEXEC. + */ + error = smbfslookup(dvp, nm, &vp, cr, 0, ct); + if (error == 0) { + /* + * file already exists + */ + if (exclusive == EXCL) { + error = EEXIST; + goto out; + } + /* + * Verify requested access. + */ + error = smbfs_access(vp, mode, 0, cr, ct); + if (error) + goto out; + + /* + * Truncate (if requested). + */ + if ((vattr.va_mask & AT_SIZE) && vattr.va_size == 0) { + vattr.va_mask = AT_SIZE; + error = smbfssetattr(vp, &vattr, 0, cr); + if (error) + goto out; + } + /* Success! */ +#ifdef NOT_YET + vnevent_create(vp, ct); +#endif + *vpp = vp; + goto out; + } + + /* + * The file did not exist. Need VWRITE in the directory. + */ + error = smbfs_access(dvp, VWRITE, 0, cr, ct); + if (error) + goto out; + + /* + * Now things get tricky. We also need to check the + * requested open mode against the file we may create. + * See comments at smbfs_access_rwx + */ + error = smbfs_access_rwx(vfsp, VREG, mode, cr); + if (error) + goto out; + +#ifdef NOT_YET + /* remove the entry from the negative entry from the dnlc */ + dnlc_remove(dvp, name); +#endif + + /* + * Now the code derived from Darwin, + * but with greater use of NT_CREATE + * disposition options. Much changed. + * + * Create (or open) a new child node. + * Note we handled "." and ".." above. + */ + + if (exclusive == EXCL) + disp = NTCREATEX_DISP_CREATE; + else { + /* Truncate regular files if requested. */ + if ((va->va_type == VREG) && + (va->va_mask & AT_SIZE) && + (va->va_size == 0)) + disp = NTCREATEX_DISP_OVERWRITE_IF; + else + disp = NTCREATEX_DISP_OPEN_IF; + } + error = smbfs_smb_create(dnp, name, nmlen, &scred, &fid, disp, 0); + if (error) + goto out; + + /* + * XXX: Missing some code here to deal with + * the case where we opened an existing file, + * it's size is larger than 32-bits, and we're + * setting the size from a process that's not + * aware of large file offsets. i.e. + * from the NFS3 code: + */ +#if NOT_YET /* XXX */ + if ((vattr.va_mask & AT_SIZE) && + vp->v_type == VREG) { + np = VTOSMB(vp); + /* + * Check here for large file handled + * by LF-unaware process (as + * ufs_create() does) + */ + if (!(lfaware & FOFFMAX)) { + mutex_enter(&np->r_statelock); + if (np->r_size > MAXOFF32_T) + error = EOVERFLOW; + mutex_exit(&np->r_statelock); + } + if (!error) { + vattr.va_mask = AT_SIZE; + error = smbfssetattr(vp, + &vattr, 0, cr); + } + } +#endif /* XXX */ + /* + * 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) + SMBERROR("error %d closing %s\\%s\n", + cerror, dnp->n_rpath, name); + + /* + * In the open case, the name may differ a little + * from what we passed to create (case, etc.) + * so call lookup to get the (opened) name. + * + * XXX: Could avoid this extra lookup if the + * "createact" result from NT_CREATE says we + * created the object. + */ + error = smbfs_smb_lookup(dnp, &name, &nmlen, &fattr, &scred); + if (error) + goto out; + + /* update attr and directory cache */ + smbfs_attr_touchdir(dnp); + + error = smbfs_nget(dvp, name, nmlen, &fattr, &vp); + if (error) + goto out; + +#ifdef NOT_YET + dnlc_update(dvp, name, vp); + /* XXX invalidate pages if we truncated? */ +#endif + + /* Success! */ + *vpp = vp; + error = 0; + +out: + smb_credrele(&scred); + if (name != nm) + smbfs_name_free(name, nmlen); + smbfs_rw_exit(&dnp->r_rwlock); + return (error); +} + +/* + * XXX + * This op should support the new FIGNORECASE flag for case-insensitive + * lookups, per PSARC 2007/244. + */ +/* ARGSUSED */ +static int +smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, + int flags) +{ + int error; + vnode_t *vp; + smbnode_t *np; + smbnode_t *dnp; + struct smb_cred scred; + /* enum smbfsstat status; */ + smbmntinfo_t *smi; + + smi = VTOSMI(dvp); + + if (curproc->p_zone != smi->smi_zone) + return (EPERM); + + if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + dnp = VTOSMB(dvp); + if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp))) + return (EINTR); + + /* + * Verify access to the dirctory. + */ + error = smbfs_access(dvp, VWRITE|VEXEC, 0, cr, ct); + if (error) + goto out; + + /* + * NOTE: the darwin code gets the "vp" passed in so it looks + * like the "vp" has probably been "lookup"ed by the VFS layer. + * It looks like we will need to lookup the vp to check the + * caches and check if the object being deleted is a directory. + */ + error = smbfslookup(dvp, nm, &vp, cr, 0, ct); + if (error) + goto out; + + /* Never allow link/unlink directories on CIFS. */ + if (vp->v_type == VDIR) { + VN_RELE(vp); + error = EPERM; + goto out; + } + +#ifdef NOT_YET + /* + * First just remove the entry from the name cache, as it + * is most likely the only entry for this vp. + */ + dnlc_remove(dvp, nm); + + /* + * If the file has a v_count > 1 then there may be more than one + * entry in the name cache due multiple links or an open file, + * but we don't have the real reference count so flush all + * possible entries. + */ + if (vp->v_count > 1) + dnlc_purge_vp(vp); +#endif /* NOT_YET */ + + /* + * Now we have the real reference count on the vnode + */ + np = VTOSMB(vp); + mutex_enter(&np->r_statelock); + if (vp->v_count > 1) { + /* + * NFS does a rename on remove here. + * Probably not applicable for SMB. + * Like Darwin, just return EBUSY. + * + * XXX: Todo - Ask the server to set the + * set the delete-on-close flag. + */ + mutex_exit(&np->r_statelock); + error = EBUSY; + goto out; + } else { + mutex_exit(&np->r_statelock); + + smb_credinit(&scred, curproc, cr); + error = smbfs_smb_delete(np, &scred, NULL, 0, 0); + smb_credrele(&scred); + + } + + VN_RELE(vp); + +out: + smbfs_rw_exit(&dnp->r_rwlock); + + return (error); +} + + +/* + * XXX + * This op should support the new FIGNORECASE flag for case-insensitive + * lookups, per PSARC 2007/244. + */ +/* ARGSUSED */ +static int +smbfs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, + caller_context_t *ct, int flags) +{ + /* vnode_t *realvp; */ + + if (curproc->p_zone != VTOSMI(odvp)->smi_zone || + curproc->p_zone != VTOSMI(ndvp)->smi_zone) + return (EPERM); + + if (VTOSMI(odvp)->smi_flags & SMI_DEAD || + VTOSMI(ndvp)->smi_flags & SMI_DEAD || + odvp->v_vfsp->vfs_flag & VFS_UNMOUNTED || + ndvp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + return (smbfsrename(odvp, onm, ndvp, nnm, cr, ct)); +} + +/* + * smbfsrename does the real work of renaming in SMBFS + */ +/* ARGSUSED */ +static int +smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, + caller_context_t *ct) +{ + int error; + int nvp_locked = 0; + vnode_t *nvp = NULL; + vnode_t *ovp = NULL; + smbnode_t *onp; + smbnode_t *odnp; + smbnode_t *ndnp; + struct smb_cred scred; + /* enum smbfsstat status; */ + + ASSERT(curproc->p_zone == VTOSMI(odvp)->smi_zone); + + if (strcmp(onm, ".") == 0 || strcmp(onm, "..") == 0 || + strcmp(nnm, ".") == 0 || strcmp(nnm, "..") == 0) + return (EINVAL); + + /* + * Check that everything is on the same filesystem. + * vn_rename checks the fsid's, but in case we don't + * fill those in correctly, check here too. + */ + if (odvp->v_vfsp != ndvp->v_vfsp) + return (EXDEV); + + odnp = VTOSMB(odvp); + ndnp = VTOSMB(ndvp); + + /* + * Avoid deadlock here on old vs new directory nodes + * by always taking the locks in order of address. + * The order is arbitrary, but must be consistent. + */ + if (odnp < ndnp) { + if (smbfs_rw_enter_sig(&odnp->r_rwlock, RW_WRITER, + SMBINTR(odvp))) + return (EINTR); + if (smbfs_rw_enter_sig(&ndnp->r_rwlock, RW_WRITER, + SMBINTR(ndvp))) { + smbfs_rw_exit(&odnp->r_rwlock); + return (EINTR); + } + } else { + if (smbfs_rw_enter_sig(&ndnp->r_rwlock, RW_WRITER, + SMBINTR(ndvp))) + return (EINTR); + if (smbfs_rw_enter_sig(&odnp->r_rwlock, RW_WRITER, + SMBINTR(odvp))) { + smbfs_rw_exit(&ndnp->r_rwlock); + return (EINTR); + } + } + /* + * No returns after this point (goto out) + */ + + /* + * Need write access on source and target. + * Server takes care of most checks. + */ + error = smbfs_access(odvp, VWRITE|VEXEC, 0, cr, ct); + if (error) + goto out; + if (odvp != ndvp) { + error = smbfs_access(ndvp, VWRITE, 0, cr, ct); + if (error) + goto out; + } + + /* + * Lookup the source name. Must already exist. + */ + error = smbfslookup(odvp, onm, &ovp, cr, 0, ct); + if (error) + goto out; + + /* + * Lookup the target file. If it exists, it needs to be + * checked to see whether it is a mount point and whether + * it is active (open). + */ + error = smbfslookup(ndvp, nnm, &nvp, cr, 0, ct); + if (!error) { + /* + * Target (nvp) already exists. Check that it + * has the same type as the source. The server + * will check this also, (and more reliably) but + * this lets us return the correct error codes. + */ + if (ovp->v_type == VDIR) { + if (nvp->v_type != VDIR) { + error = ENOTDIR; + goto out; + } + } else { + if (nvp->v_type == VDIR) { + error = EISDIR; + goto out; + } + } + + /* + * POSIX dictates that when the source and target + * entries refer to the same file object, rename + * must do nothing and exit without error. + */ + if (ovp == nvp) { + error = 0; + goto out; + } + + /* + * Also must ensure the target is not a mount point, + * and keep mount/umount away until we're done. + */ + if (vn_vfsrlock(nvp)) { + error = EBUSY; + goto out; + } + nvp_locked = 1; + if (vn_mountedvfs(nvp) != NULL) { + error = EBUSY; + goto out; + } + +#ifdef NOT_YET + /* + * Purge the name cache of all references to this vnode + * so that we can check the reference count to infer + * whether it is active or not. + */ + /* + * First just remove the entry from the name cache, as it + * is most likely the only entry for this vp. + */ + dnlc_remove(ndvp, nnm); + /* + * If the file has a v_count > 1 then there may be more + * than one entry in the name cache due multiple links + * or an open file, but we don't have the real reference + * count so flush all possible entries. + */ + if (nvp->v_count > 1) + dnlc_purge_vp(nvp); +#endif + + if (nvp->v_count > 1 && nvp->v_type != VDIR) { + /* + * The target file exists, is not the same as + * the source file, and is active. Other FS + * implementations unlink the target here. + * For SMB, we don't assume we can remove an + * open file. Return an error instead. + * Darwin returned an error here too. + */ + error = EEXIST; + goto out; + } + } /* nvp */ + +#ifdef NOT_YET + dnlc_remove(odvp, onm); + dnlc_remove(ndvp, nnm); +#endif + + onp = VTOSMB(ovp); + smb_credinit(&scred, curproc, cr); + error = smbfs_smb_rename(onp, ndnp, nnm, strlen(nnm), &scred); + smb_credrele(&scred); + + +out: + if (nvp) { + if (nvp_locked) + vn_vfsunlock(nvp); + VN_RELE(nvp); + } + if (ovp) + VN_RELE(ovp); + + smbfs_rw_exit(&odnp->r_rwlock); + smbfs_rw_exit(&ndnp->r_rwlock); + + return (error); +} + +/* + * XXX + * vsecattr_t is new to build 77, and we need to eventually support + * it in order to create an ACL when an object is created. + * + * This op should support the new FIGNORECASE flag for case-insensitive + * lookups, per PSARC 2007/244. + */ +/* ARGSUSED */ +static int +smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, + cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp) +{ + vnode_t *vp; + struct smbnode *dnp = VTOSMB(dvp); + struct smbmntinfo *smi = VTOSMI(dvp); + struct smb_cred scred; + struct smbfattr fattr; + const char *name = (const char *) nm; + int nmlen = strlen(name); + int error, hiderr; + + if (curproc->p_zone != smi->smi_zone) + return (EPERM); + + if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + if ((nmlen == 1 && name[0] == '.') || + (nmlen == 2 && name[0] == '.' && name[1] == '.')) + return (EEXIST); + + if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp))) + return (EINTR); + smb_credinit(&scred, curproc, cr); + + /* + * XXX: Do we need r_lkserlock too? + * No use of any shared fid or fctx... + */ + + /* + * Require write access in the containing directory. + */ + error = smbfs_access(dvp, VWRITE, 0, cr, ct); + if (error) + goto out; + + error = smbfs_smb_mkdir(dnp, name, nmlen, &scred); + if (error) + goto out; + + error = smbfs_smb_lookup(dnp, &name, &nmlen, &fattr, &scred); + if (error) + goto out; + + smbfs_attr_touchdir(dnp); + + error = smbfs_nget(dvp, name, nmlen, &fattr, &vp); + if (error) + goto out; + +#ifdef NOT_YET + dnlc_update(dvp, name, vp); +#endif + + if (name[0] == '.') + if ((hiderr = smbfs_smb_hideit(VTOSMB(vp), NULL, 0, &scred))) + SMBVDEBUG("hide failure %d\n", hiderr); + + /* Success! */ + *vpp = vp; + error = 0; +out: + smb_credrele(&scred); + smbfs_rw_exit(&dnp->r_rwlock); + + if (name != nm) + smbfs_name_free(name, nmlen); + + return (error); +} + +/* + * XXX + * This op should support the new FIGNORECASE flag for case-insensitive + * lookups, per PSARC 2007/244. + */ +/* ARGSUSED */ +static int +smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, + caller_context_t *ct, int flags) +{ + 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) + return (EPERM); + + 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, curproc, cr); + + /* + * Require w/x access in the containing directory. + * Server handles all other access checks. + */ + error = smbfs_access(dvp, VEXEC|VWRITE, 0, cr, ct); + if (error) + goto out; + + /* + * First lookup the entry to be removed. + */ + error = smbfslookup(dvp, nm, &vp, cr, 0, ct); + if (error) + goto out; + np = VTOSMB(vp); + + /* + * Disallow rmdir of "." or current dir, or the FS root. + * Also make sure it's a directory, not a mount point, + * and lock to keep mount/umount away until we're done. + */ + if ((vp == dvp) || (vp == cdir) || (vp->v_flag & VROOT)) { + error = EINVAL; + goto out; + } + if (vp->v_type != VDIR) { + error = ENOTDIR; + goto out; + } + if (vn_vfsrlock(vp)) { + error = EBUSY; + goto out; + } + vp_locked = 1; + if (vn_mountedvfs(vp) != NULL) { + error = EBUSY; + goto out; + } + + error = smbfs_smb_rmdir(np, &scred); + if (error) + goto out; + + mutex_enter(&np->r_statelock); + dnp->n_flag |= NMODIFIED; + mutex_exit(&np->r_statelock); + smbfs_attr_touchdir(dnp); +#ifdef NOT_YET + dnlc_remove(dvp, nm); + dnlc_purge_vp(vp); +#endif + smb_rmhash(np); + +out: + if (vp) { + if (vp_locked) + vn_vfsunlock(vp); + VN_RELE(vp); + } + smb_credrele(&scred); + smbfs_rw_exit(&dnp->r_rwlock); + + return (error); +} + + +/* ARGSUSED */ +static int +smbfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp, + caller_context_t *ct, int flags) +{ + struct smbnode *np = VTOSMB(vp); + int error = 0; + smbmntinfo_t *smi; + + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + /* + * Require read access in the directory. + */ + error = smbfs_access(vp, VREAD, 0, cr, ct); + if (error) + return (error); + + ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_READER)); + + /* + * XXX: Todo readdir cache here + * Note: NFS code is just below this. + * + * I am serializing the entire readdir opreation + * now since we have not yet implemented readdir + * cache. This fix needs to be revisited once + * we implement readdir cache. + */ + if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, SMBINTR(vp))) + return (EINTR); + + error = smbfs_readvdir(vp, uiop, cr, eofp, ct); + + smbfs_rw_exit(&np->r_lkserlock); + + return (error); +} + +/* ARGSUSED */ +static int +smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, + caller_context_t *ct) +{ + size_t dbufsiz; + struct dirent64 *dp; + struct smb_cred scred; + vnode_t *newvp; + struct smbnode *np = VTOSMB(vp); + int nmlen, reclen, error = 0; + long offset, limit; + struct smbfs_fctx *ctx; + + ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone); + + /* Make sure we serialize for n_dirseq use. */ + ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_WRITER)); + + /* Min size is DIRENT64_RECLEN(256) rounded up. */ + if (uio->uio_resid < 512 || uio->uio_offset < 0) + return (EINVAL); + + /* + * This dnlc_purge_vp ensures that name cache for this dir will be + * current - it'll only have the items for which the smbfs_nget + * MAKEENTRY happened. + */ +#ifdef NOT_YET + if (smbfs_fastlookup) + dnlc_purge_vp(vp); +#endif + SMBVDEBUG("dirname='%s'\n", np->n_rpath); + smb_credinit(&scred, curproc, cr); + dbufsiz = DIRENT64_RECLEN(MAXNAMELEN); + dp = kmem_alloc(dbufsiz, KM_SLEEP); + + offset = uio->uio_offset; /* NB: "cookie" */ + limit = uio->uio_resid / DIRENT64_RECLEN(1); + SMBVDEBUG("offset=0x%ld, limit=0x%ld\n", offset, limit); + + if (offset == 0) { + /* Don't know EOF until findclose */ + np->n_direof = -1; + } else if (offset == np->n_direof) { + /* Arrived at end of directory. */ + goto out; + } + + /* + * Generate the "." and ".." entries here so we can + * (1) make sure they appear (but only once), and + * (2) deal with getting their I numbers which the + * findnext below does only for normal names. + */ + while (limit && offset < 2) { + limit--; + reclen = DIRENT64_RECLEN(offset + 1); + bzero(dp, reclen); + /*LINTED*/ + dp->d_reclen = reclen; + /* Tricky: offset 0 is ".", offset 1 is ".." */ + dp->d_name[0] = '.'; + dp->d_name[1] = '.'; + dp->d_name[offset + 1] = '\0'; + /* + * Want the real I-numbers for the "." and ".." + * entries. For these two names, we know that + * smbfslookup can do this all locally. + */ + error = smbfslookup(vp, dp->d_name, &newvp, cr, 1, ct); + if (error) { + dp->d_ino = np->n_ino + offset; /* fiction */ + } else { + dp->d_ino = VTOSMB(newvp)->n_ino; + VN_RELE(newvp); + } + dp->d_off = offset + 1; /* see d_off below */ + error = uiomove(dp, dp->d_reclen, UIO_READ, uio); + if (error) + goto out; + uio->uio_offset = ++offset; + } + if (limit == 0) + goto out; + if (offset != np->n_dirofs || np->n_dirseq == NULL) { + SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs); + if (np->n_dirseq) { + (void) smbfs_smb_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + } + np->n_dirofs = 2; + error = smbfs_smb_findopen(np, "*", 1, + SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, + &scred, &ctx); + if (error) { + SMBVDEBUG("can not open search, error = %d", error); + goto out; + } + np->n_dirseq = ctx; + } else + ctx = np->n_dirseq; + while (np->n_dirofs < offset) { + if (smbfs_smb_findnext(ctx, offset - np->n_dirofs++, + &scred) != 0) { + (void) smbfs_smb_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + np->n_direof = np->n_dirofs; + np->n_dirofs = 0; + *eofp = 1; + error = 0; + goto out; + } + } + error = 0; + for (; limit; limit--) { + error = smbfs_smb_findnext(ctx, limit, &scred); + if (error) { + if (error == EBADRPC) + error = ENOENT; + (void) smbfs_smb_findclose(np->n_dirseq, &scred); + np->n_dirseq = NULL; + np->n_direof = np->n_dirofs; + np->n_dirofs = 0; + *eofp = 1; + error = 0; + break; + } + np->n_dirofs++; + /* Sanity check the name length. */ + nmlen = ctx->f_nmlen; + if (nmlen > (MAXNAMELEN - 1)) { + nmlen = MAXNAMELEN - 1; + SMBVDEBUG("Truncating name: %s\n", ctx->f_name); + } + reclen = DIRENT64_RECLEN(nmlen); + if (uio->uio_resid < reclen) + break; + bzero(dp, reclen); + /*LINTED*/ + dp->d_reclen = reclen; + dp->d_ino = ctx->f_attr.fa_ino; + /* + * Note: d_off is the offset that a user-level program + * should seek to for reading the _next_ directory entry. + * See libc: readdir, telldir, seekdir + */ + dp->d_off = offset + 1; + bcopy(ctx->f_name, dp->d_name, nmlen); + dp->d_name[nmlen] = '\0'; +#ifdef NOT_YET + if (smbfs_fastlookup) { + if (smbfs_nget(vp, ctx->f_name, + ctx->f_nmlen, &ctx->f_attr, &newvp) == 0) + VN_RELE(newvp); + } +#endif /* NOT_YET */ + error = uiomove(dp, dp->d_reclen, UIO_READ, uio); + if (error) + break; + uio->uio_offset = ++offset; + } + if (error == ENOENT) + error = 0; +out: + kmem_free(dp, dbufsiz); + smb_credrele(&scred); + return (error); +} + + +/* + * The pair of functions VOP_RWLOCK, VOP_RWUNLOCK + * are optional functions that are called by: + * getdents, before/after VOP_READDIR + * pread, before/after ... VOP_READ + * pwrite, before/after ... VOP_WRITE + * (other places) + * + * Careful here: None of the above check for any + * error returns from VOP_RWLOCK / VOP_RWUNLOCK! + * In fact, the return value from _rwlock is NOT + * an error code, but V_WRITELOCK_TRUE / _FALSE. + * + * Therefore, it's up to _this_ code to make sure + * the lock state remains balanced, which means + * we can't "bail out" on interrupts, etc. + */ + +/* ARGSUSED2 */ +static int +smbfs_rwlock(vnode_t *vp, int write_lock, caller_context_t *ctp) +{ + smbnode_t *np = VTOSMB(vp); + + if (!write_lock) { + (void) smbfs_rw_enter_sig(&np->r_rwlock, RW_READER, FALSE); + return (V_WRITELOCK_FALSE); + } + + + (void) smbfs_rw_enter_sig(&np->r_rwlock, RW_WRITER, FALSE); + return (V_WRITELOCK_TRUE); +} + +/* ARGSUSED */ +static void +smbfs_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ctp) +{ + smbnode_t *np = VTOSMB(vp); + + smbfs_rw_exit(&np->r_rwlock); +} + + +/* ARGSUSED */ +static int +smbfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) +{ + smbmntinfo_t *smi; + + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EPERM); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + /* + * Because we stuff the readdir cookie into the offset field + * someone may attempt to do an lseek with the cookie which + * we want to succeed. + */ + if (vp->v_type == VDIR) + return (0); + + /* Like NFS3, just check for 63-bit overflow. */ + if (*noffp < 0) + return (EINVAL); + + return (0); +} + + +/* + * XXX + * This op may need to support PSARC 2007/440, nbmand changes for CIFS Service. + */ +static int +smbfs_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, + offset_t offset, struct flk_callback *flk_cbp, cred_t *cr, + caller_context_t *ct) +{ + if (curproc->p_zone != VTOSMI(vp)->smi_zone) + return (EIO); + + if (VTOSMI(vp)->smi_flags & SMI_LLOCK) + return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); + else + return (ENOSYS); +} + +/* + * Free storage space associated with the specified vnode. The portion + * to be freed is specified by bfp->l_start and bfp->l_len (already + * normalized to a "whence" of 0). + * + * Called by fcntl(fd, F_FREESP, lkp) for libc:ftruncate, etc. + */ +/* ARGSUSED */ +static int +smbfs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, + offset_t offset, cred_t *cr, caller_context_t *ct) +{ + int error; + smbmntinfo_t *smi; + + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + ASSERT(vp->v_type == VREG); + if (cmd != F_FREESP) + return (EINVAL); + + /* + * Like NFS3, no 32-bit offset checks here. + * Our SMB layer takes care to return EFBIG + * when it has to fallback to a 32-bit call. + */ + + error = convoff(vp, bfp, 0, offset); + if (!error) { + ASSERT(bfp->l_start >= 0); + if (bfp->l_len == 0) { + struct vattr va; + + /* + * ftruncate should not change the ctime and + * mtime if we truncate the file to its + * previous size. + */ + va.va_mask = AT_SIZE; + error = smbfsgetattr(vp, &va, cr); + if (error || va.va_size == bfp->l_start) + return (error); + va.va_mask = AT_SIZE; + va.va_size = bfp->l_start; + error = smbfssetattr(vp, &va, 0, cr); + } else + error = EINVAL; + } + + return (error); +} + +/* ARGSUSED */ +static int +smbfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, + caller_context_t *ct) +{ + smbmntinfo_t *smi; + struct smb_share *ssp; + + smi = VTOSMI(vp); + + if (curproc->p_zone != smi->smi_zone) + return (EIO); + + if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) + return (EIO); + + switch (cmd) { + case _PC_FILESIZEBITS: + ssp = smi->smi_share; + if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) + *valp = 64; + else + *valp = 32; + break; + + case _PC_LINK_MAX: + /* We only ever report one link to an object */ + *valp = 1; + break; + + case _PC_SYMLINK_MAX: /* No symlinks until we do Unix extensions */ + case _PC_ACL_ENABLED: /* No ACLs yet - see FILE_PERSISTENT_ACLS bit */ + case _PC_XATTR_EXISTS: /* No xattrs yet */ + *valp = 0; + break; + + default: + return (fs_pathconf(vp, cmd, valp, cr, ct)); + } + return (0); +} + + + +/* + * XXX + * This op should eventually support PSARC 2007/268. + */ +static int +smbfs_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr, + caller_context_t *ct) +{ + if (curproc->p_zone != VTOSMI(vp)->smi_zone) + return (EIO); + + if (VTOSMI(vp)->smi_flags & SMI_LLOCK) + return (fs_shrlock(vp, cmd, shr, flag, cr, ct)); + else + return (ENOSYS); +} diff --git a/usr/src/uts/common/netsmb/mchain.h b/usr/src/uts/common/netsmb/mchain.h new file mode 100644 index 0000000000..e6fb362a9c --- /dev/null +++ b/usr/src/uts/common/netsmb/mchain.h @@ -0,0 +1,204 @@ +/* + * 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. + * + * $FreeBSD: src/sys/sys/mchain.h,v 1.1 2001/02/24 15:44:30 bp Exp $ + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MCHAIN_H_ +#define _MCHAIN_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/isa_defs.h> +#include <sys/byteorder.h> + +#ifdef _LITTLE_ENDIAN + +/* little-endian values on little-endian */ +#define htoles(x) ((uint16_t)(x)) +#define letohs(x) ((uint16_t)(x)) +#define htolel(x) ((uint32_t)(x)) +#define letohl(x) ((uint32_t)(x)) +#define htoleq(x) ((uint64_t)(x)) +#define letohq(x) ((uint64_t)(x)) + +/* + * big-endian values on little-endian (swap) + * + * Use the BSWAP macros because they're fastest, and they're + * available in all environments where we use this header. + */ +#define htobes(x) BSWAP_16(x) +#define betohs(x) BSWAP_16(x) +#define htobel(x) BSWAP_32(x) +#define betohl(x) BSWAP_32(x) +#define htobeq(x) BSWAP_64(x) +#define betohq(x) BSWAP_64(x) + +#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 letohq(x) BSWAP_64(x) +#define htoleq(x) BSWAP_64(x) + +/* big-endian values on big-endian */ +#define htobes(x) ((uint16_t)(x)) +#define betohs(x) ((uint16_t)(x)) +#define htobel(x) ((uint32_t)(x)) +#define betohl(x) ((uint32_t)(x)) +#define htobeq(x) ((uint64_t)(x)) +#define betohq(x) ((uint64_t)(x)) +#endif /* (BYTE_ORDER == LITTLE_ENDIAN) */ + + +#ifdef _KERNEL + +/* BEGIN CSTYLED */ +/* + * BSD-style mbufs, vs SysV-style mblks: + * One big difference: the mbuf payload is: + * 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 + * m->m_data m->b_rptr + * m->m_len MBLKL(m) + * m->m_dat[] m->b_datap->db_base + * &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. + */ +/* END CSTYLED */ + +#include <sys/stream.h> /* mblk_t */ + +/* + * Type of copy for mb_{put|get}_mem() + */ +#define MB_MSYSTEM 0 /* use bcopy() */ +#define MB_MUSER 1 /* use copyin()/copyout() */ +#define MB_MINLINE 2 /* use an inline copy loop */ +#define MB_MZERO 3 /* bzero(), mb_put_mem only */ +#define MB_MCUSTOM 4 /* use an user defined function */ + +struct mbchain { + mblk_t *mb_top; + mblk_t *mb_cur; + uint_t mb_count; +}; +typedef struct mbchain mbchain_t; + +struct mdchain { + mblk_t *md_top; /* head of mblk chain */ + mblk_t *md_cur; /* current mblk */ + uchar_t *md_pos; /* offset in the current mblk */ +}; +typedef struct mdchain mdchain_t; + +int m_fixhdr(mblk_t *m); + +int mb_init(struct mbchain *mbp); +void mb_initm(struct mbchain *mbp, mblk_t *m); +void mb_done(struct mbchain *mbp); +mblk_t *mb_detach(struct mbchain *mbp); +int mb_fixhdr(struct mbchain *mbp); +void *mb_reserve(struct mbchain *mbp, int size); + +int mb_put_padbyte(struct mbchain *mbp); +int mb_put_uint8(struct mbchain *mbp, uint8_t x); +int mb_put_uint16be(struct mbchain *mbp, uint16_t x); +int mb_put_uint16le(struct mbchain *mbp, uint16_t x); +int mb_put_uint32be(struct mbchain *mbp, uint32_t x); +int mb_put_uint32le(struct mbchain *mbp, uint32_t x); +int mb_put_uint64be(struct mbchain *mbp, uint64_t x); +int mb_put_uint64le(struct mbchain *mbp, uint64_t x); +int mb_put_mem(struct mbchain *mbp, const char *src, int size, int type); + +int mb_put_mblk(struct mbchain *mbp, mblk_t *m); +int mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size); + +int md_init(struct mdchain *mdp); +void md_initm(struct mdchain *mbp, mblk_t *m); +void md_done(struct mdchain *mdp); +void md_append_record(struct mdchain *mdp, mblk_t *top); +int md_next_record(struct mdchain *mdp); +int md_get_uint8(struct mdchain *mdp, uint8_t *x); +int md_get_uint16(struct mdchain *mdp, uint16_t *x); +int md_get_uint16le(struct mdchain *mdp, uint16_t *x); +int md_get_uint16be(struct mdchain *mdp, uint16_t *x); +int md_get_uint32(struct mdchain *mdp, uint32_t *x); +int md_get_uint32be(struct mdchain *mdp, uint32_t *x); +int md_get_uint32le(struct mdchain *mdp, uint32_t *x); +int md_get_uint64(struct mdchain *mdp, uint64_t *x); +int md_get_uint64be(struct mdchain *mdp, uint64_t *x); +int md_get_uint64le(struct mdchain *mdp, uint64_t *x); +int md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type); +int md_get_mblk(struct mdchain *mdp, int size, mblk_t **m); +int md_get_uio(struct mdchain *mdp, uio_t *uiop, int size); + +/* + * Additions for Solaris to replace things that came from + * <sys/mbuf.h> in the Darwin code. These are mostly just + * wrappers for streams functions. See: subr_mchain.c + */ + +#define mtod(m, t) ((t)((m)->b_rptr)) + +/* length to m_copym to copy all */ +#define M_COPYALL -1 + +mblk_t *m_copym(mblk_t *, int, int, int); +mblk_t *m_pullup(mblk_t *, int); +mblk_t *m_split(mblk_t *, int, int); +void m_cat(mblk_t *, mblk_t *); +#define m_freem(x) freemsg(x) +mblk_t *m_getblk(int, int); + +#endif /* ifdef _KERNEL */ +#endif /* !_MCHAIN_H_ */ diff --git a/usr/src/uts/common/netsmb/netbios.h b/usr/src/uts/common/netsmb/netbios.h new file mode 100644 index 0000000000..b61f19c2dd --- /dev/null +++ b/usr/src/uts/common/netsmb/netbios.h @@ -0,0 +1,160 @@ +/* + * 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. + * + * $Id: netbios.h,v 1.5 2004/03/19 01:49:45 lindak Exp $ + */ + +#ifndef _NETSMB_NETBIOS_H_ +#define _NETSMB_NETBIOS_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _NETINET_IN_H_ +#include <netinet/in.h> +#endif + +/* + * This is a fake address family number, used to + * recognize our fake sockaddr_nb objects. + * This is never handed to bind or connect. + */ +#ifndef AF_NETBIOS +#define AF_NETBIOS (AF_MAX+2) +#endif + +#define PF_NETBIOS AF_NETBIOS + +/* + * NetBIOS port numbers by the names used in the Darwin code. + * XXX: Change the code to use IPPORT_xxx from in.h directly. + * XXX: Add IPPORT_SMB_OVER_TCP or some such (port 445) + */ +#define NBNS_UDP_PORT IPPORT_NETBIOS_NS /* 137 */ +#define SMB_TCP_PORT IPPORT_NETBIOS_SSN /* 139 */ + +#define NBPROTO_TCPSSN 1 /* NETBIOS session over TCP */ + +#define NB_NAMELEN 16 +#define NB_ENCNAMELEN NB_NAMELEN * 2 +#define NB_MAXLABLEN 63 + +#define NB_MINSALEN (sizeof (struct sockaddr_nb)) + +/* + * name types + */ +#define NBT_WKSTA 0x00 +#define NBT_CLIENT 0x03 +#define NBT_RASSRVR 0x06 +#define NBT_DMB 0x1B +#define NBT_IP 0x1C +#define NBT_MB 0x1D +#define NBT_BS 0x1E +#define NBT_NETDDE 0x1F +#define NBT_SERVER 0x20 +#define NBT_RASCLNT 0x21 +#define NBT_NMAGENT 0xBE +#define NBT_NMUTIL 0xBF + +/* + * Session packet types + */ +#define NB_SSN_MESSAGE 0x0 +#define NB_SSN_REQUEST 0x81 +#define NB_SSN_POSRESP 0x82 +#define NB_SSN_NEGRESP 0x83 +#define NB_SSN_RTGRESP 0x84 +#define NB_SSN_KEEPALIVE 0x85 + +/* + * resolver: Opcodes + */ +#define NBNS_OPCODE_QUERY 0x00 +#define NBNS_OPCODE_REGISTER 0x05 +#define NBNS_OPCODE_RELEASE 0x06 +#define NBNS_OPCODE_WACK 0x07 +#define NBNS_OPCODE_REFRESH 0x08 +#define NBNS_OPCODE_RESPONSE 0x10 /* or'ed with other opcodes */ + +/* + * resolver: NM_FLAGS + */ +#define NBNS_NMFLAG_BCAST 0x01 +#define NBNS_NMFLAG_RA 0x08 /* recursion available */ +#define NBNS_NMFLAG_RD 0x10 /* recursion desired */ +#define NBNS_NMFLAG_TC 0x20 /* truncation occured */ +#define NBNS_NMFLAG_AA 0x40 /* authoritative answer */ + +/* + * resolver: Question types + */ +#define NBNS_QUESTION_TYPE_NB 0x0020 +#define NBNS_QUESTION_TYPE_NBSTAT 0x0021 + +/* + * resolver: Question class + */ +#define NBNS_QUESTION_CLASS_IN 0x0001 + +/* + * resolver: Limits + */ +#define NBNS_MAXREDIRECTS 3 /* max number of accepted redirects */ +#define NBDG_MAXSIZE 576 /* maximum nbns datagram size */ + +/* + * NETBIOS addressing + */ + +struct nb_name { + uint_t nn_type; + char nn_name[NB_NAMELEN]; + char *nn_scope; +}; +typedef struct nb_name nb_name_t; + +/* + * Our private NetBIOS socket address format. + * Note that it's LARGER than sockaddr. + * + * XXX: Also note that the library code is sloppy about + * casting this to sockaddr_in so let's keep snb_ipaddr + * at the same offset, at least until that's fixed. + */ +struct sockaddr_nb { + sa_family_t snb_family; /* address family */ + uint16_t snb_flags; /* NBNS_GROUPFLG, etc. */ + uint32_t snb_ipaddr; /* always IPv4 */ + char snb_name[NB_NAMELEN]; /* NOT encoded */ +}; +typedef struct sockaddr_nb sockaddr_nb_t; + +#endif /* !_NETSMB_NETBIOS_H_ */ diff --git a/usr/src/uts/common/netsmb/smb.h b/usr/src/uts/common/netsmb/smb.h new file mode 100644 index 0000000000..40c3522cfc --- /dev/null +++ b/usr/src/uts/common/netsmb/smb.h @@ -0,0 +1,1522 @@ +/* + * Copyright (c) 2000-2001 Boris Popov + * All rights reserved. + * + * Now many of these defines are from samba4 code, by Andrew Tridgell. + * (Permission given to Conrad Minshall at CIFS plugfest Aug 13 2003.) + * (Note the main decision was whether to use defines found in MS includes + * and web pages, versus Samba, and the deciding factor is which developers + * are more likely to be looking at this code base.) + * + * 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. + * + * $Id: smb.h,v 1.36.90.1 2005/05/27 02:35:29 lindak Exp $ + */ + +/* + * Common definintions and structures for SMB/CIFS protocol + */ + +#ifndef _NETSMB_SMB_H_ +#define _NETSMB_SMB_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file should be purely SMB protocol definition stuff. + * (Please don't make it a catch-all:) + */ + +/* + * SMB dialects that we have to deal with. + */ +enum smb_dialects { + SMB_DIALECT_NONE, + SMB_DIALECT_CORE, /* PC NETWORK PROGRAM 1.0, PCLAN1.0 */ + SMB_DIALECT_COREPLUS, /* MICROSOFT NETWORKS 1.03 */ + 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 */ +}; + +/* + * Formats of data/string buffers + */ +#define SMB_DT_DATA 1 +#define SMB_DT_DIALECT 2 +#define SMB_DT_PATHNAME 3 +#define SMB_DT_ASCII 4 +#define SMB_DT_VARIABLE 5 + +/* + * SMB header + */ +#define SMB_SIGNATURE "\xFFSMB" +#define SMB_SIGLEN 4 +#define SMB_HDRCMD(p) (*((uchar_t *)(p) + SMB_SIGLEN)) +#define SMB_HDRMID(p) (letohs(*(ushort_t *)((uchar_t *)(p) + 30))) +#define SMB_HDRLEN 32 +/* + * bits in the smb_flags field + */ +#define SMB_FLAGS_SUPPORT_LOCKREAD 0x01 +#define SMB_FLAGS_CLIENT_BUF_AVAIL 0x02 +#define SMB_FLAGS_CASELESS 0x08 +#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10 +#define SMB_FLAGS_REQUEST_OPLOCK 0x20 +#define SMB_FLAGS_REQUEST_BATCH_OPLOCK 0x40 +#define SMB_FLAGS_SERVER_RESP 0x80 + +/* + * bits in the smb_flags2 field + */ +#define SMB_FLAGS2_KNOWS_LONG_NAMES 0x0001 +#define SMB_FLAGS2_KNOWS_EAS 0x0002 /* client know about EAs */ +#define SMB_FLAGS2_SECURITY_SIGNATURE 0x0004 /* check SMB integrity */ +#define SMB_FLAGS2_IS_LONG_NAME 0x0040 /* any path name is long name */ +#define SMB_FLAGS2_EXT_SEC 0x0800 /* client aware of Extended */ + /* Security negotiation */ +#define SMB_FLAGS2_DFS 0x1000 /* resolve paths in DFS */ +#define SMB_FLAGS2_PAGING_IO 0x2000 /* for exec */ +#define SMB_FLAGS2_ERR_STATUS 0x4000 /* 1 - status.status */ +#define SMB_FLAGS2_UNICODE 0x8000 /* use Unicode for strings */ + +#define SMB_UID_UNKNOWN 0xffff +#define SMB_TID_UNKNOWN 0xffff +#define SMB_FID_UNUSED 0xffff + +/* + * Security mode bits + */ +#define SMB_SM_USER 0x01 /* server in the user security mode */ +#define SMB_SM_ENCRYPT 0x02 /* use challenge/responce */ +#define SMB_SM_SIGS 0x04 +#define SMB_SM_SIGS_REQUIRE 0x08 + +/* + * Action bits in session setup reply + */ +#define SMB_ACT_GUEST 0x01 + +/* + * NTLM capabilities + */ +#define SMB_CAP_RAW_MODE 0x0001 +#define SMB_CAP_MPX_MODE 0x0002 +#define SMB_CAP_UNICODE 0x0004 +#define SMB_CAP_LARGE_FILES 0x0008 /* 64 bit offsets supported */ +#define SMB_CAP_NT_SMBS 0x0010 +#define SMB_CAP_RPC_REMOTE_APIS 0x0020 +#define SMB_CAP_STATUS32 0x0040 +#define SMB_CAP_LEVEL_II_OPLOCKS 0x0080 +#define SMB_CAP_LOCK_AND_READ 0x0100 +#define SMB_CAP_NT_FIND 0x0200 +#define SMB_CAP_DFS 0x1000 +#define SMB_CAP_INFOLEVEL_PASSTHRU 0x2000 +#define SMB_CAP_LARGE_READX 0x4000 +#define SMB_CAP_LARGE_WRITEX 0x8000 +#define SMB_CAP_UNIX 0x00800000 +#define SMB_CAP_BULK_TRANSFER 0x20000000 +#define SMB_CAP_COMPRESSED_DATA 0x40000000 +#define SMB_CAP_EXT_SECURITY 0x80000000 + +/* + * File attributes + */ +#define SMB_FA_RDONLY 0x01 +#define SMB_FA_HIDDEN 0x02 +#define SMB_FA_SYSTEM 0x04 +#define SMB_FA_VOLUME 0x08 +#define SMB_FA_DIR 0x10 +#define SMB_FA_ARCHIVE 0x20 + +/* + * Extended file attributes + */ +#define SMB_EFA_RDONLY 0x00000001 +#define SMB_EFA_HIDDEN 0x00000002 +#define SMB_EFA_SYSTEM 0x00000004 +#define SMB_EFA_VOLUME 0x00000008 +#define SMB_EFA_DIRECTORY 0x00000010 +#define SMB_EFA_ARCHIVE 0x00000020 +#define SMB_EFA_DEVICE 0x00000040 +#define SMB_EFA_NORMAL 0x00000080 +#define SMB_EFA_TEMPORARY 0x00000100 +#define SMB_EFA_SPARSE 0x00000200 +#define SMB_EFA_REPARSE_POINT 0x00000400 +#define SMB_EFA_COMPRESSED 0x00000800 +#define SMB_EFA_OFFLINE 0x00001000 +#define SMB_EFA_NONINDEXED 0x00002000 +#define SMB_EFA_ENCRYPTED 0x00004000 +#define SMB_EFA_POSIX_SEMANTICS 0x01000000 +#define SMB_EFA_BACKUP_SEMANTICS 0x02000000 +#define SMB_EFA_DELETE_ON_CLOSE 0x04000000 +#define SMB_EFA_SEQUENTIAL_SCAN 0x08000000 +#define SMB_EFA_RANDOM_ACCESS 0x10000000 +#define SMB_EFA_NO_BUFFERING 0x20000000 +#define SMB_EFA_WRITE_THROUGH 0x80000000 + +/* + * Access Mode Encoding + */ +#define SMB_AM_OPENREAD 0x0000 +#define SMB_AM_OPENWRITE 0x0001 +#define SMB_AM_OPENRW 0x0002 +#define SMB_AM_OPENEXEC 0x0003 +#define SMB_AM_OPENMODE 0x0003 /* mask for access mode bits */ +#define SMB_SM_COMPAT 0x0000 +#define SMB_SM_EXCLUSIVE 0x0010 +#define SMB_SM_DENYWRITE 0x0020 +#define SMB_SM_DENYREADEXEC 0x0030 +#define SMB_SM_DENYNONE 0x0040 + +/* NT_CREATE_ANDX flags */ +#define NTCREATEX_FLAGS_REQUEST_OPLOCK 0x02 +#define NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK 0x04 +#define NTCREATEX_FLAGS_OPEN_DIRECTORY 0x08 +#define NTCREATEX_FLAGS_EXTENDED 0x10 + +/* NT_CREATE_ANDX share_access (share mode) */ +#define NTCREATEX_SHARE_ACCESS_NONE 0 +#define NTCREATEX_SHARE_ACCESS_READ 1 +#define NTCREATEX_SHARE_ACCESS_WRITE 2 +#define NTCREATEX_SHARE_ACCESS_DELETE 4 +#define NTCREATEX_SHARE_ACCESS_ALL 7 + +/* NT_CREATE_ANDX open_disposition */ +#define NTCREATEX_DISP_SUPERSEDE 0 /* if file exists supersede it */ +#define NTCREATEX_DISP_OPEN 1 /* exists ? open it : fail */ +#define NTCREATEX_DISP_CREATE 2 /* exists ? fail : create it */ +#define NTCREATEX_DISP_OPEN_IF 3 /* exists ? open it : create it */ +#define NTCREATEX_DISP_OVERWRITE 4 /* exists ? overwrite : fail */ +#define NTCREATEX_DISP_OVERWRITE_IF 5 /* exists ? overwrite : create */ + +/* NT_CREATE_ANDX create_options */ +#define NTCREATEX_OPTIONS_DIRECTORY 0x0001 +#define NTCREATEX_OPTIONS_WRITE_THROUGH 0x0002 +#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY 0x0004 +#define NTCREATEX_OPTIONS_SYNC_ALERT 0x0010 +#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020 +#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040 +#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200 +#define NTCREATEX_OPTIONS_EIGHT_DOT_THREE_ONLY 0x0400 +#define NTCREATEX_OPTIONS_RANDOM_ACCESS 0x0800 +#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE 0x1000 +#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID 0x2000 + +/* NT_CREATE_ANDX "impersonation" */ +#define NTCREATEX_IMPERSONATION_ANONYMOUS 0 +#define NTCREATEX_IMPERSONATION_IDENTIFICATION 1 +#define NTCREATEX_IMPERSONATION_IMPERSONATION 2 +#define NTCREATEX_IMPERSONATION_DELEGATION 3 + +/* NT_CREATE_ANDX security flags */ +#define NTCREATEX_SECURITY_DYNAMIC 1 +#define NTCREATEX_SECURITY_ALL 2 + +/* NT_CREATE_ANDX create_action in reply */ +#define NTCREATEX_ACTION_EXISTED 1 +#define NTCREATEX_ACTION_CREATED 2 +#define NTCREATEX_ACTION_TRUNCATED 3 + +/* SMB_TRANS2_FIND_FIRST2/SMB_TRANS2_FIND_NEXT2 flags */ +#define FIND2_CLOSE_AFTER_REQUEST 0x0001 +#define FIND2_CLOSE_ON_EOS 0x0002 +#define FIND2_RETURN_RESUME_KEYS 0x0004 +#define FIND2_CONTINUE_SEARCH 0x0008 +#define FIND2_BACKUP_INTENT 0x0010 + +/* + * SMB commands + */ +#define SMB_COM_CREATE_DIRECTORY 0x00 +#define SMB_COM_DELETE_DIRECTORY 0x01 +#define SMB_COM_OPEN 0x02 +#define SMB_COM_CREATE 0x03 +#define SMB_COM_CLOSE 0x04 +#define SMB_COM_FLUSH 0x05 +#define SMB_COM_DELETE 0x06 +#define SMB_COM_RENAME 0x07 +#define SMB_COM_QUERY_INFORMATION 0x08 +#define SMB_COM_SET_INFORMATION 0x09 +#define SMB_COM_READ 0x0A +#define SMB_COM_WRITE 0x0B +#define SMB_COM_LOCK_BYTE_RANGE 0x0C +#define SMB_COM_UNLOCK_BYTE_RANGE 0x0D +#define SMB_COM_CREATE_TEMPORARY 0x0E +#define SMB_COM_CREATE_NEW 0x0F +#define SMB_COM_CHECK_DIRECTORY 0x10 +#define SMB_COM_PROCESS_EXIT 0x11 +#define SMB_COM_SEEK 0x12 +#define SMB_COM_LOCK_AND_READ 0x13 +#define SMB_COM_WRITE_AND_UNLOCK 0x14 +#define SMB_COM_READ_RAW 0x1A +#define SMB_COM_READ_MPX 0x1B +#define SMB_COM_READ_MPX_SECONDARY 0x1C +#define SMB_COM_WRITE_RAW 0x1D +#define SMB_COM_WRITE_MPX 0x1E +#define SMB_COM_WRITE_COMPLETE 0x20 +#define SMB_COM_SET_INFORMATION2 0x22 +#define SMB_COM_QUERY_INFORMATION2 0x23 +#define SMB_COM_LOCKING_ANDX 0x24 +#define SMB_COM_TRANSACTION 0x25 +#define SMB_COM_TRANSACTION_SECONDARY 0x26 +#define SMB_COM_IOCTL 0x27 +#define SMB_COM_IOCTL_SECONDARY 0x28 +#define SMB_COM_COPY 0x29 +#define SMB_COM_MOVE 0x2A +#define SMB_COM_ECHO 0x2B +#define SMB_COM_WRITE_AND_CLOSE 0x2C +#define SMB_COM_OPEN_ANDX 0x2D +#define SMB_COM_READ_ANDX 0x2E +#define SMB_COM_WRITE_ANDX 0x2F +#define SMB_COM_CLOSE_AND_TREE_DISC 0x31 +#define SMB_COM_TRANSACTION2 0x32 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 +#define SMB_COM_FIND_NOTIFY_CLOSE 0x35 +#define SMB_COM_TREE_CONNECT 0x70 +#define SMB_COM_TREE_DISCONNECT 0x71 +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_QUERY_INFORMATION_DISK 0x80 +#define SMB_COM_SEARCH 0x81 +#define SMB_COM_FIND 0x82 +#define SMB_COM_FIND_UNIQUE 0x83 +#define SMB_COM_NT_TRANSACT 0xA0 +#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 +#define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 +#define SMB_COM_OPEN_PRINT_FILE 0xC0 +#define SMB_COM_WRITE_PRINT_FILE 0xC1 +#define SMB_COM_CLOSE_PRINT_FILE 0xC2 +#define SMB_COM_GET_PRINT_QUEUE 0xC3 +#define SMB_COM_READ_BULK 0xD8 +#define SMB_COM_WRITE_BULK 0xD9 +#define SMB_COM_WRITE_BULK_DATA 0xDA + +/* + * SMB_COM_TRANSACTION2 subcommands + */ +#define SMB_TRANS2_OPEN2 0x00 +#define SMB_TRANS2_FIND_FIRST2 0x01 +#define SMB_TRANS2_FIND_NEXT2 0x02 +#define SMB_TRANS2_QUERY_FS_INFORMATION 0x03 +#define SMB_TRANS2_SETFSINFO 0x04 +#define SMB_TRANS2_QUERY_PATH_INFORMATION 0x05 +#define SMB_TRANS2_SET_PATH_INFORMATION 0x06 +#define SMB_TRANS2_QUERY_FILE_INFORMATION 0x07 +#define SMB_TRANS2_SET_FILE_INFORMATION 0x08 +#define SMB_TRANS2_FSCTL 0x09 +#define SMB_TRANS2_IOCTL2 0x0A +#define SMB_TRANS2_FIND_NOTIFY_FIRST 0x0B +#define SMB_TRANS2_FIND_NOTIFY_NEXT 0x0C +#define SMB_TRANS2_CREATE_DIRECTORY 0x0D +#define SMB_TRANS2_SESSION_SETUP 0x0E +#define SMB_TRANS2_GET_DFS_REFERRAL 0x10 +#define SMB_TRANS2_REPORT_DFS_INCONSISTENCY 0x11 + +/* + * SMB_COM_NT_TRANSACT subcommands + */ +#define NT_TRANSACT_CREATE 0x01 +#define NT_TRANSACT_IOCTL 0x02 +#define NT_TRANSACT_SET_SECURITY_DESC 0x03 +#define NT_TRANSACT_NOTIFY_CHANGE 0x04 +#define NT_TRANSACT_RENAME 0x05 +#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 +#define NT_TRANSACT_GET_USER_QUOTA 0x07 +#define NT_TRANSACT_SET_USER_QUOTA 0x08 + +/* + * SMB_TRANS2_QUERY_FS_INFORMATION levels + */ +#define SMB_QFS_ALLOCATION 1 +#define SMB_QFS_VOLUME 2 +#define SMB_QFS_LABEL_INFO 0x101 +#define SMB_QFS_VOLUME_INFO 0x102 +#define SMB_QFS_SIZE_INFO 0x103 +#define SMB_QFS_DEVICE_INFO 0x104 +#define SMB_QFS_ATTRIBUTE_INFO 0x105 +#define SMB_QFS_UNIX_INFO 0x200 +#define SMB_QFS_MAC_FS_INFO 0x301 +#define SMB_QFS_VOLUME_INFORMATION 1001 +#define SMB_QFS_SIZE_INFORMATION 1003 +#define SMB_QFS_DEVICE_INFORMATION 1004 +#define SMB_QFS_ATTRIBUTE_INFORMATION 1005 +#define SMB_QFS_QUOTA_INFORMATION 1006 +#define SMB_QFS_FULL_SIZE_INFORMATION 1007 +#define SMB_QFS_OBJECTID_INFORMATION 1008 + + +/* + * SMB_QFS_ATTRIBUTE_INFO bits. + * The following info found in msdn + * (http://msdn.microsoft.com/library/default.asp? + * url=/library/en-us/wmisdk/wmi/win32_cdromdrive.asp) + * Naming is mostly as in samba, to help Those Who Google. + */ +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_LONG_NAMES 0x00004000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_NAMED_STREAMS 0x00040000 + +/* + * SMB_TRANS2_QUERY_PATH levels + */ +#define SMB_QFILEINFO_STANDARD 1 +#define SMB_QFILEINFO_EA_SIZE 2 +#define SMB_QFILEINFO_EAS_FROM_LIST 3 +#define SMB_QFILEINFO_ALL_EAS 4 +#define SMB_QFILEINFO_IS_NAME_VALID 6 /* QPATHINFO only? */ +#define SMB_QFILEINFO_BASIC_INFO 0x101 +#define SMB_QFILEINFO_STANDARD_INFO 0x102 +#define SMB_QFILEINFO_EA_INFO 0x103 +#define SMB_QFILEINFO_NAME_INFO 0x104 +#define SMB_QFILEINFO_ALLOCATION_INFO 0x105 +#define SMB_QFILEINFO_END_OF_FILE_INFO 0x106 +#define SMB_QFILEINFO_ALL_INFO 0x107 +#define SMB_QFILEINFO_ALT_NAME_INFO 0x108 +#define SMB_QFILEINFO_STREAM_INFO 0x109 +#define SMB_QFILEINFO_COMPRESSION_INFO 0x10b +#define SMB_QFILEINFO_UNIX_BASIC 0x200 +#define SMB_QFILEINFO_UNIX_LINK 0x201 +#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_BASIC_INFORMATION 1004 +#define SMB_QFILEINFO_STANDARD_INFORMATION 1005 +#define SMB_QFILEINFO_INTERNAL_INFORMATION 1006 +#define SMB_QFILEINFO_EA_INFORMATION 1007 +#define SMB_QFILEINFO_ACCESS_INFORMATION 1008 +#define SMB_QFILEINFO_NAME_INFORMATION 1009 +#define SMB_QFILEINFO_POSITION_INFORMATION 1014 +#define SMB_QFILEINFO_MODE_INFORMATION 1016 +#define SMB_QFILEINFO_ALIGNMENT_INFORMATION 1017 +#define SMB_QFILEINFO_ALL_INFORMATION 1018 +#define SMB_QFILEINFO_ALT_NAME_INFORMATION 1021 +#define SMB_QFILEINFO_STREAM_INFORMATION 1022 +#define SMB_QFILEINFO_COMPRESSION_INFORMATION 1028 +#define SMB_QFILEINFO_NETWORK_OPEN_INFORMATION 1034 +#define SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035 + +/* + * SMB_TRANS2_FIND_FIRST2 information levels + */ +#define SMB_FIND_STANDARD 1 +#define SMB_FIND_EA_SIZE 2 +#define SMB_FIND_EAS_FROM_LIST 3 +#define SMB_FIND_DIRECTORY_INFO 0x101 +#define SMB_FIND_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_NAME_INFO 0x103 +#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_UNIX_INFO 0x200 + +/* + * Selectors for NT_TRANSACT_QUERY_SECURITY_DESC and + * NT_TRANSACT_SET_SECURITY_DESC. Details found in the MSDN + * library by searching on security_information. + * Note the protected/unprotected bits did not exist in NT. + */ + +#define OWNER_SECURITY_INFORMATION 0x00000001 +#define GROUP_SECURITY_INFORMATION 0x00000002 +#define DACL_SECURITY_INFORMATION 0x00000004 +#define SACL_SECURITY_INFORMATION 0x00000008 +#define UNPROTECTED_SACL_SECURITY_INFORMATION 0x10000000 +#define UNPROTECTED_DACL_SECURITY_INFORMATION 0x20000000 +#define PROTECTED_SACL_SECURITY_INFORMATION 0x40000000 +#define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000 + +/* + * security descriptor header + * it is followed by the optional SIDs and ACLs + * note this is "raw", ie little-endian + */ +struct ntsecdesc { + uint8_t sd_revision; /* 0x01 observed between W2K */ + uint8_t sd_pad1; + uint16_t sd_flags; + uint32_t sd_owneroff; /* offset to owner SID */ + uint32_t sd_groupoff; /* offset to group SID */ + uint32_t sd_sacloff; /* offset to system/audit ACL */ + uint32_t sd_dacloff; /* offset to discretionary ACL */ +}; /* XXX: __attribute__((__packed__)); */ +typedef struct ntsecdesc ntsecdesc_t; + +#define wset_sdrevision(s) ((s)->sd_revision = 0x01) +#define sdflags(s) (letohs((s)->sd_flags)) +#define wset_sdflags(s, f) ((s)->sd_flags = letohs(f)) +#define sdowner(s) \ + ((struct ntsid *)((s)->sd_owneroff ? \ + (char *)(s) + letohl((s)->sd_owneroff) : \ + NULL)) +#define wset_sdowneroff(s, o) ((s)->sd_owneroff = htolel(o)) +#define sdgroup(s) \ + ((struct ntsid *)((s)->sd_groupoff ? \ + (char *)(s) + letohl((s)->sd_groupoff) : \ + NULL)) +#define wset_sdgroupoff(s, o) ((s)->sd_groupoff = htolel(o)) +#define sdsacl(s) \ + ((struct ntacl *)((s)->sd_sacloff ? \ + (char *)(s) + letohl((s)->sd_sacloff) : \ + NULL)) +#define wset_sdsacloff(s, o) ((s)->sd_sacloff = htolel(o)) +#define sddacl(s) \ + ((struct ntacl *)((s)->sd_dacloff ? \ + (char *)(s) + letohl((s)->sd_dacloff) : \ + NULL)) +#define wset_sddacloff(s, o) ((s)->sd_dacloff = htolel(o)) + +/* + * sd_flags bits + */ +#define SD_OWNER_DEFAULTED 0x0001 +#define SD_GROUP_DEFAULTED 0x0002 +#define SD_DACL_PRESENT 0x0004 +#define SD_DACL_DEFAULTED 0x0008 +#define SD_SACL_PRESENT 0x0010 +#define SD_SACL_DEFAULTED 0x0020 +#define SD_DACL_TRUSTED 0x0040 +#define SD_SERVER_SECURITY 0x0080 +#define SD_DACL_AUTO_INHERIT_REQ 0x0100 +#define SD_SACL_AUTO_INHERIT_REQ 0x0200 +#define SD_DACL_AUTO_INHERITED 0x0400 +#define SD_SACL_AUTO_INHERITED 0x0800 +#define SD_DACL_PROTECTED 0x1000 +#define SD_SACL_PROTECTED 0x2000 +#define SD_RM_CONTROL_VALID 0x4000 +#define SD_SELF_RELATIVE 0x8000 + +/* + * access control list header + * it is followed by the ACEs + * note this is "raw", ie little-endian + */ +struct ntacl { + uint8_t acl_revision; /* 0x02 observed with W2K */ + uint8_t acl_pad1; + uint16_t acl_len; /* bytes; includes this header */ + uint16_t acl_acecount; + uint16_t acl_pad2; +}; /* XXX: __attribute__((__packed__)); */ +typedef struct ntacl ntacl_t; + +#define wset_aclrevision(a) ((a)->acl_revision = 0x02) +#define acllen(a) (letohs((a)->acl_len)) +#define wset_acllen(a, l) ((a)->acl_len = htoles(l)) +#define aclacecount(a) (letohs((a)->acl_acecount)) +#define wset_aclacecount(a, c) ((a)->acl_acecount = htoles(c)) +#define aclace(a) ((struct ntace *)((char *)(a) + sizeof (struct ntacl))) + +/* + * access control entry header + * it is followed by type-specific ace data, + * which for the simple types is just a SID + * note this is "raw", ie little-endian + */ +struct ntace { + uint8_t ace_type; + uint8_t ace_flags; + uint16_t ace_len; /* bytes; includes this header */ + uint32_t ace_rights; /* generic, standard, specific, etc */ +}; /* XXX: __attribute__((__packed__)); */ + +#define acetype(a) ((a)->ace_type) +#define wset_acetype(a, t) ((a)->ace_type = (t)) +#define aceflags(a) ((a)->ace_flags) +#define wset_aceflags(a, f) ((a)->ace_flags = (f)) +#define acelen(a) (letohs((a)->ace_len)) +#define wset_acelen(a, l) ((a)->ace_len = htoles(l)) +#define acerights(a) (letohl((a)->ace_rights)) +#define wset_acerights(a, r) ((a)->ace_rights = htolel(r)) +#define aceace(a) ((struct ntace *)((char *)(a) + acelen(a))) +#define acesid(a) ((struct ntsid *)((char *)(a) + sizeof (struct ntace))) + +/* + * ace_rights + * (Samba bit names are used here, with permission, as the shorter Windows + * names are more likely to cause namespace collisions) + */ +#define SA_RIGHT_FILE_READ_DATA 0x00000001 +#define SA_RIGHT_FILE_WRITE_DATA 0x00000002 +#define SA_RIGHT_FILE_APPEND_DATA 0x00000004 +#define SA_RIGHT_FILE_READ_EA 0x00000008 +#define SA_RIGHT_FILE_WRITE_EA 0x00000010 +#define SA_RIGHT_FILE_EXECUTE 0x00000020 +#define SA_RIGHT_FILE_DELETE_CHILD 0x00000040 +#define SA_RIGHT_FILE_READ_ATTRIBUTES 0x00000080 +#define SA_RIGHT_FILE_WRITE_ATTRIBUTES 0x00000100 +#define SA_RIGHT_FILE_ALL_ACCESS 0x000001FF + +#define STD_RIGHT_DELETE_ACCESS 0x00010000 +#define STD_RIGHT_READ_CONTROL_ACCESS 0x00020000 +#define STD_RIGHT_WRITE_DAC_ACCESS 0x00040000 +#define STD_RIGHT_WRITE_OWNER_ACCESS 0x00080000 +#define STD_RIGHT_SYNCHRONIZE_ACCESS 0x00100000 +#define STD_RIGHT_ALL_ACCESS 0x001F0000 + +#define SEC_RIGHT_SYSTEM_SECURITY 0x01000000 +/* + * Don't use MAXIMUM_ALLOWED as Samba (2.2.3 at least) will + * return NT_STATUS_INVALID_LOCK_SEQUENCE + */ +#define SEC_RIGHT_MAXIMUM_ALLOWED 0x02000000 + +#define GENERIC_RIGHT_ALL_ACCESS 0x10000000 +#define GENERIC_RIGHT_EXECUTE_ACCESS 0x20000000 +#define GENERIC_RIGHT_WRITE_ACCESS 0x40000000 +#define GENERIC_RIGHT_READ_ACCESS 0x80000000 + +/* + * these mappings are from Windows sample code but are likely incomplete + * + * GENERIC_RIGHT_READ_ACCESS : + * STD_RIGHT_SYNCHRONIZE_ACCESS | + * STD_RIGHT_READ_CONTROL_ACCESS | + * SA_RIGHT_FILE_READ_ATTRIBUTES | + * SA_RIGHT_FILE_READ_EA | + * SA_RIGHT_FILE_READ_DATA + * GENERIC_RIGHT_WRITE_ACCESS : + * STD_RIGHT_SYNCHRONIZE_ACCESS | + * STD_RIGHT_READ_CONTROL_ACCESS | + * SA_RIGHT_FILE_WRITE_ATTRIBUTES | + * SA_RIGHT_FILE_WRITE_EA | + * SA_RIGHT_FILE_APPEND_DATA | + * SA_RIGHT_FILE_WRITE_DATA + * GENERIC_RIGHT_EXECUTE_ACCESS : + * STD_RIGHT_SYNCHRONIZE_ACCESS | + * STD_RIGHT_READ_CONTROL_ACCESS | + * SA_RIGHT_FILE_READ_ATTRIBUTES | + * SA_RIGHT_FILE_EXECUTE + * GENERIC_RIGHT_ALL_ACCESS : + * STD_RIGHT_SYNCHRONIZE_ACCESS | + * STD_RIGHT_WRITE_OWNER_ACCESS | + * STD_RIGHT_WRITE_DAC_ACCESS | + * STD_RIGHT_READ_CONTROL_ACCESS | + * STD_RIGHT_DELETE_ACCESS | + * SA_RIGHT_FILE_ALL_ACCESS + */ + +/* + * security identifier header + * it is followed by sid_numauth sub-authorities, + * which are 32 bits each. + * note the subauths are little-endian on the wire, but + * need to be big-endian for memberd/DS + */ +#define SIDAUTHSIZE 6 +struct ntsid { + uint8_t sid_revision; + uint8_t sid_subauthcount; + uint8_t sid_authority[SIDAUTHSIZE]; /* ie not little endian */ +}; /* XXX: __attribute__((__packed__)); */ +typedef struct ntsid ntsid_t; + +#define sidsubauthcount(s) (s->sid_subauthcount) +#define sidlen(s) (sizeof (struct ntsid) + 4 * (s)->sid_subauthcount) +#define MAXSIDLEN (sizeof (struct ntsid) + 4 * KAUTH_NTSID_MAX_AUTHORITIES) +#define sidsub(s) ((uint32_t *)((char *)(s) + sizeof (struct ntsid))) + +/* + * MS' defined values for ace_type + */ +#define ACCESS_ALLOWED_ACE_TYPE 0x0 +#define ACCESS_DENIED_ACE_TYPE 0x1 +#define SYSTEM_AUDIT_ACE_TYPE 0x2 +#define SYSTEM_ALARM_ACE_TYPE 0x3 +#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x4 +#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x5 +#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x6 +#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x7 +#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x8 +#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x9 +#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0xA +#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0xB +#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0xC +#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0xD +#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0xE +#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0xF +#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 + +/* + * MS' defined values for ace_flags + */ +#define OBJECT_INHERIT_ACE_FLAG 0x01 +#define CONTAINER_INHERIT_ACE_FLAG 0x02 +#define NO_PROPAGATE_INHERIT_ACE_FLAG 0x04 +#define INHERIT_ONLY_ACE_FLAG 0x08 +#define INHERITED_ACE_FLAG 0x10 +#define UNDEF_ACE_FLAG 0x20 /* MS doesn't define it */ +#define VALID_INHERIT_ACE_FLAGS 0x1F +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + +/* + * Set PATH/FILE information levels + */ +#define SMB_SFILEINFO_STANDARD 1 +#define SMB_SFILEINFO_EA_SET 2 +#define SMB_SFILEINFO_BASIC_INFO 0x101 +#define SMB_SFILEINFO_DISPOSITION_INFO 0x102 +#define SMB_SFILEINFO_ALLOCATION_INFO 0x103 +#define SMB_SFILEINFO_END_OF_FILE_INFO 0x104 +#define SMB_SFILEINFO_UNIX_BASIC 0x200 +#define SMB_SFILEINFO_UNIX_LINK 0x201 +#define SMB_SFILEINFO_UNIX_HLINK 0x203 +#define SMB_SFILEINFO_DIRECTORY_INFORMATION 1001 +#define SMB_SFILEINFO_FULL_DIRECTORY_INFORMATION 1002 +#define SMB_SFILEINFO_BOTH_DIRECTORY_INFORMATION 1003 +#define SMB_SFILEINFO_BASIC_INFORMATION 1004 +#define SMB_SFILEINFO_STANDARD_INFORMATION 1005 +#define SMB_SFILEINFO_INTERNAL_INFORMATION 1006 +#define SMB_SFILEINFO_EA_INFORMATION 1007 +#define SMB_SFILEINFO_ACCESS_INFORMATION 1008 +#define SMB_SFILEINFO_NAME_INFORMATION 1009 +#define SMB_SFILEINFO_RENAME_INFORMATION 1010 +#define SMB_SFILEINFO_LINK_INFORMATION 1011 +#define SMB_SFILEINFO_NAMES_INFORMATION 1012 +#define SMB_SFILEINFO_DISPOSITION_INFORMATION 1013 +#define SMB_SFILEINFO_POSITION_INFORMATION 1014 +#define SMB_SFILEINFO_1015 1015 /* ? */ +#define SMB_SFILEINFO_MODE_INFORMATION 1016 +#define SMB_SFILEINFO_ALIGNMENT_INFORMATION 1017 +#define SMB_SFILEINFO_ALL_INFORMATION 1018 +#define SMB_SFILEINFO_ALLOCATION_INFORMATION 1019 +#define SMB_SFILEINFO_END_OF_FILE_INFORMATION 1020 +#define SMB_SFILEINFO_ALT_NAME_INFORMATION 1021 +#define SMB_SFILEINFO_STREAM_INFORMATION 1022 +#define SMB_SFILEINFO_PIPE_INFORMATION 1023 +#define SMB_SFILEINFO_PIPE_LOCAL_INFORMATION 1024 +#define SMB_SFILEINFO_PIPE_REMOTE_INFORMATION 1025 +#define SMB_SFILEINFO_MAILSLOT_QUERY_INFORMATION 1026 +#define SMB_SFILEINFO_MAILSLOT_SET_INFORMATION 1027 +#define SMB_SFILEINFO_COMPRESSION_INFORMATION 1028 +#define SMB_SFILEINFO_OBJECT_ID_INFORMATION 1029 +#define SMB_SFILEINFO_COMPLETION_INFORMATION 1030 +#define SMB_SFILEINFO_MOVE_CLUSTER_INFORMATION 1031 +#define SMB_SFILEINFO_QUOTA_INFORMATION 1032 +#define SMB_SFILEINFO_REPARSE_POINT_INFORMATION 1033 +#define SMB_SFILEINFO_NETWORK_OPEN_INFORMATION 1034 +#define SMB_SFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035 +#define SMB_SFILEINFO_TRACKING_INFORMATION 1036 +#define SMB_SFILEINFO_MAXIMUM_INFORMATION 1037 + +/* + * LOCKING_ANDX LockType flags + */ +#define SMB_LOCKING_ANDX_SHARED_LOCK 0x01 +#define SMB_LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define SMB_LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define SMB_LOCKING_ANDX_CANCEL_LOCK 0x08 +#define SMB_LOCKING_ANDX_LARGE_FILES 0x10 + +/* + * Some names length limitations. Some of them aren't declared by specs, + * but we need reasonable limits. + */ +#define SMB_MAXSRVNAMELEN 15 /* NetBIOS limit */ +#define SMB_MAXUSERNAMELEN 128 +#define SMB_MAXPASSWORDLEN 128 +#define SMB_MAXSHARENAMELEN 128 +#define SMB_MAXPKTLEN 0x1FFFF +#define SMB_MAXCHALLENGELEN 8 +#define SMB_MAXFNAMELEN 255 /* Keep in sync with MAXNAMLEN */ + +#define SMB_RCNDELAY 2 /* seconds between reconnect attempts */ +/* + * leave this zero - we can't ssecond guess server side effects of + * duplicate ops, this isn't nfs! + */ +#define SMBMAXRESTARTS 0 +#define SMB_MAXSETUPWORDS 3 /* max # of setup words in trans/t2 */ + +/* + * Error classes + */ +#define SMBSUCCESS 0x00 +#define ERRDOS 0x01 +#define ERRSRV 0x02 +#define ERRHRD 0x03 /* Error is an hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* + * Error codes for the ERRDOS class + */ +#define ERRbadfunc 1 /* Invalid function */ +#define ERRbadfile 2 /* File not found (last component) */ +#define ERRbadpath 3 /* Directory invalid */ +#define ERRnofids 4 /* Too many open files */ +#define ERRnoaccess 5 /* Access denied */ +#define ERRbadfid 6 /* Invalid file handle */ +#define ERRbadmcb 7 /* Memory control blocks destroyed (huh ?) */ +#define ERRnomem 8 /* Insufficient memory */ +#define ERRbadmem 9 /* Invalid memory block address */ +#define ERRbadenv 10 /* Invalid environment */ +#define ERRbadformat 11 /* Invalid format */ +#define ERRbadaccess 12 /* Invalid open mode */ +#define ERRbaddata 13 /* Invalid data */ +#define ERRoutofmem 14 /* out of memory */ +#define ERRbaddrive 15 /* Invalid drive specified */ +#define ERRremcd 16 /* An attempt to delete current directory */ +#define ERRdiffdevice 17 /* cross fs rename/move */ +#define ERRnofiles 18 /* no more files found in file search */ +#define ERRwriteprotect 19 +#define ERRnotready 21 +#define ERRbadcmd 22 +#define ERRcrc 23 +#define ERRbadlength 24 +#define ERRsectornotfound 27 +#define ERRbadshare 32 /* Share mode can't be granted */ +#define ERRlock 33 /* Lock conflicts with existing lock */ +#define ERRwrongdisk 34 +#define ERRhandleeof 38 +#define ERRunsup 50 /* unsupported - Win 95 */ +#define ERRnetnamedel 64 +#define ERRnoipc 66 /* ipc unsupported */ +#define ERRnosuchshare 67 /* invalid share name */ +#define ERRtoomanynames 68 +#define ERRfilexists 80 /* requested file name already exists */ +#define ERRinvalidparam 87 +#define ERRcannotopen 110 /* cannot open the file */ +#define ERRinsufficientbuffer 122 +#define ERRinvalidname 123 +#define ERRunknownlevel 124 +#define ERRdirnotempty 145 +#define ERRnotlocked 158 /* region was not locked by this context */ +#define ERRrename 183 +#define ERRbadpipe 230 /* named pipe invalid */ +#define ERRpipebusy 231 /* all pipe instances are busy */ +#define ERRpipeclosing 232 /* close in progress */ +#define ERRnotconnected 233 /* nobody on other end of pipe */ +#define ERRmoredata 234 /* more data to be returned */ +#define ERRnomoreitems 259 +#define ERRbaddirectory 267 /* invalid directory name */ +#define ERReasunsupported 282 /* extended attributes not supported */ +#define ERRlogonfailure 1326 +#define ERRbuftoosmall 2123 +#define ERRunknownipc 2142 +#define ERRnosuchprintjob 2151 +#define ERRinvgroup 2455 + +/* + * Error codes for the ERRSRV class + */ +#define ERRerror 1 /* Non-specific error code */ +#define ERRbadpw 2 /* Bad password */ +#define ERRbadtype 3 /* reserved */ +#define ERRaccess 4 /* client doesn't have enough access rights */ +#define ERRinvnid 5 /* The Tid specified in a command is invalid */ +#define ERRinvnetname 6 /* Invalid server name in the tree connect */ +#define ERRinvdevice 7 /* Printer and not printer devices are mixed */ +#define ERRqfull 49 /* Print queue full */ +#define ERRqtoobig 50 /* Print queue full - no space */ +#define ERRinvpfid 52 /* Invalid print file FID */ +#define ERRsmbcmd 64 /* The server did not recognise the command */ +#define ERRsrverror 65 /* The server encountered and internal error */ +#define ERRfilespecs 67 /* The Fid and path name contains an */ + /* invalid combination */ +#define ERRbadpermits 69 /* Access mode invalid */ +#define ERRsetattrmode 71 /* Attribute mode invalid */ +#define ERRpaused 81 /* Server is paused */ +#define ERRmsgoff 82 /* Not receiving messages */ +#define ERRnoroom 83 /* No room to buffer message */ +#define ERRrmuns 87 /* Too many remote user names */ +#define ERRtimeout 88 /* Operation timed out */ +#define ERRnoresource 89 /* No resources currently available for req */ +#define ERRtoomanyuids 90 /* Too many UIDs active on this session */ +#define ERRbaduid 91 /* The UID is not known in this session */ +#define ERRusempx 250 /* Temporarily unable to support Raw, */ + /* use MPX mode */ +#define ERRusestd 251 /* Temporarily unable to support Raw, */ + /* use stdandard r/w */ +#define ERRcontmpx 252 /* Continue in MPX mode */ +#define ERRacctexpired 2239 +#define ERRnosupport 65535 /* Invalid function */ + +/* + * Error codes for the ERRHRD class + */ +#define ERRnowrite 19 /* write protected media */ +#define ERRbadunit 20 /* Unknown unit */ +#define ERRnotready 21 /* Drive not ready */ +#define ERRbadcmd 22 /* Unknown command */ +#define ERRdata 23 /* Data error (CRC) */ +#define ERRbadreq 24 /* Bad request structure length */ +#define ERRseek 25 /* Seek error */ +#define ERRbadmedia 26 /* Unknown media type */ +#define ERRbadsector 27 /* Sector not found */ +#define ERRnopaper 28 /* Printer out of paper */ +#define ERRwrite 29 /* Write fault */ +#define ERRread 30 /* Read fault */ +#define ERRgeneral 31 /* General failure */ +#define ERRbadshare 32 /* A open conflicts with an existing open */ +#define ERRlock 33 /* lock/unlock conflict */ +#define ERRwrongdisk 34 /* The wrong disk was found in a drive */ +#define ERRFCBunavail 35 /* No FCBs available */ +#define ERRsharebufexc 36 /* A sharing buffer has been exceeded */ +#define ERRdiskfull 39 + +/* + * RAP error codes (it seems that they returned not only by RAP) + */ +#define SMB_ERROR_ACCESS_DENIED 5 +#define SMB_ERROR_NETWORK_ACCESS_DENIED 65 +#define SMB_ERROR_MORE_DATA ERRmoredata + +/* + * An INCOMPLETE list of 32 bit error codes + * For more detail see MSDN and ntstatus.h in the MS DDK + * + * XXX - these should have the severity and "customer defined" fields + * added back in, and smb_maperr32() shouldn't mask those fields out; + * 0x80000005 is STATUS_BUFFER_OVERFLOW, with 0xC0000000 is + * STATUS_ACCESS_VIOLATION, and we need to distinguish between them. + * We use STATUS_BUFFER_OVERFLOW, and need to know its exact value, + * so we #define it correctly here; don't strip off the leading + * 0x80000000 from it! + */ +#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 +#define NT_STATUS_UNSUCCESSFUL 0x0001 +#define NT_STATUS_NOT_IMPLEMENTED 0x0002 +#define NT_STATUS_INVALID_INFO_CLASS 0x0003 +#define NT_STATUS_INFO_LENGTH_MISMATCH 0x0004 +#define NT_STATUS_ACCESS_VIOLATION 0x0005 +#define NT_STATUS_IN_PAGE_ERROR 0x0006 +#define NT_STATUS_PAGEFILE_QUOTA 0x0007 +#define NT_STATUS_INVALID_HANDLE 0x0008 +#define NT_STATUS_BAD_INITIAL_STACK 0x0009 +#define NT_STATUS_BAD_INITIAL_PC 0x000a +#define NT_STATUS_INVALID_CID 0x000b +#define NT_STATUS_TIMER_NOT_CANCELED 0x000c +#define NT_STATUS_INVALID_PARAMETER 0x000d +#define NT_STATUS_NO_SUCH_DEVICE 0x000e +#define NT_STATUS_NO_SUCH_FILE 0x000f +#define NT_STATUS_INVALID_DEVICE_REQUEST 0x0010 +#define NT_STATUS_END_OF_FILE 0x0011 +#define NT_STATUS_WRONG_VOLUME 0x0012 +#define NT_STATUS_NO_MEDIA_IN_DEVICE 0x0013 +#define NT_STATUS_UNRECOGNIZED_MEDIA 0x0014 +#define NT_STATUS_NONEXISTENT_SECTOR 0x0015 +#define NT_STATUS_MORE_PROCESSING_REQUIRED 0x0016 +#define NT_STATUS_NO_MEMORY 0x0017 +#define NT_STATUS_CONFLICTING_ADDRESSES 0x0018 +#define NT_STATUS_NOT_MAPPED_VIEW 0x0019 +#define NT_STATUS_UNABLE_TO_FREE_VM 0x001a +#define NT_STATUS_UNABLE_TO_DELETE_SECTION 0x001b +#define NT_STATUS_INVALID_SYSTEM_SERVICE 0x001c +#define NT_STATUS_ILLEGAL_INSTRUCTION 0x001d +#define NT_STATUS_INVALID_LOCK_SEQUENCE 0x001e +#define NT_STATUS_INVALID_VIEW_SIZE 0x001f +#define NT_STATUS_INVALID_FILE_FOR_SECTION 0x0020 +#define NT_STATUS_ALREADY_COMMITTED 0x0021 +#define NT_STATUS_ACCESS_DENIED 0x0022 +#define NT_STATUS_BUFFER_TOO_SMALL 0x0023 +#define NT_STATUS_OBJECT_TYPE_MISMATCH 0x0024 +#define NT_STATUS_NONCONTINUABLE_EXCEPTION 0x0025 +#define NT_STATUS_INVALID_DISPOSITION 0x0026 +#define NT_STATUS_UNWIND 0x0027 +#define NT_STATUS_BAD_STACK 0x0028 +#define NT_STATUS_INVALID_UNWIND_TARGET 0x0029 +#define NT_STATUS_NOT_LOCKED 0x002a +#define NT_STATUS_PARITY_ERROR 0x002b +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM 0x002c +#define NT_STATUS_NOT_COMMITTED 0x002d +#define NT_STATUS_INVALID_PORT_ATTRIBUTES 0x002e +#define NT_STATUS_PORT_MESSAGE_TOO_LONG 0x002f +#define NT_STATUS_INVALID_PARAMETER_MIX 0x0030 +#define NT_STATUS_INVALID_QUOTA_LOWER 0x0031 +#define NT_STATUS_DISK_CORRUPT_ERROR 0x0032 +#define NT_STATUS_OBJECT_NAME_INVALID 0x0033 +#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0x0034 +#define NT_STATUS_OBJECT_NAME_COLLISION 0x0035 +#define NT_STATUS_HANDLE_NOT_WAITABLE 0x0036 +#define NT_STATUS_PORT_DISCONNECTED 0x0037 +#define NT_STATUS_DEVICE_ALREADY_ATTACHED 0x0038 +#define NT_STATUS_OBJECT_PATH_INVALID 0x0039 +#define NT_STATUS_OBJECT_PATH_NOT_FOUND 0x003a +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD 0x003b +#define NT_STATUS_DATA_OVERRUN 0x003c +#define NT_STATUS_DATA_LATE_ERROR 0x003d +#define NT_STATUS_DATA_ERROR 0x003e +#define NT_STATUS_CRC_ERROR 0x003f +#define NT_STATUS_SECTION_TOO_BIG 0x0040 +#define NT_STATUS_PORT_CONNECTION_REFUSED 0x0041 +#define NT_STATUS_INVALID_PORT_HANDLE 0x0042 +#define NT_STATUS_SHARING_VIOLATION 0x0043 +#define NT_STATUS_QUOTA_EXCEEDED 0x0044 +#define NT_STATUS_INVALID_PAGE_PROTECTION 0x0045 +#define NT_STATUS_MUTANT_NOT_OWNED 0x0046 +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED 0x0047 +#define NT_STATUS_PORT_ALREADY_SET 0x0048 +#define NT_STATUS_SECTION_NOT_IMAGE 0x0049 +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED 0x004a +#define NT_STATUS_THREAD_IS_TERMINATING 0x004b +#define NT_STATUS_BAD_WORKING_SET_LIMIT 0x004c +#define NT_STATUS_INCOMPATIBLE_FILE_MAP 0x004d +#define NT_STATUS_SECTION_PROTECTION 0x004e +#define NT_STATUS_EAS_NOT_SUPPORTED 0x004f +#define NT_STATUS_EA_TOO_LARGE 0x0050 +#define NT_STATUS_NONEXISTENT_EA_ENTRY 0x0051 +#define NT_STATUS_NO_EAS_ON_FILE 0x0052 +#define NT_STATUS_EA_CORRUPT_ERROR 0x0053 +#define NT_STATUS_FILE_LOCK_CONFLICT 0x0054 +#define NT_STATUS_LOCK_NOT_GRANTED 0x0055 +#define NT_STATUS_DELETE_PENDING 0x0056 +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED 0x0057 +#define NT_STATUS_UNKNOWN_REVISION 0x0058 +#define NT_STATUS_REVISION_MISMATCH 0x0059 +#define NT_STATUS_INVALID_OWNER 0x005a +#define NT_STATUS_INVALID_PRIMARY_GROUP 0x005b +#define NT_STATUS_NO_IMPERSONATION_TOKEN 0x005c +#define NT_STATUS_CANT_DISABLE_MANDATORY 0x005d +#define NT_STATUS_NO_LOGON_SERVERS 0x005e +#define NT_STATUS_NO_SUCH_LOGON_SESSION 0x005f +#define NT_STATUS_NO_SUCH_PRIVILEGE 0x0060 +#define NT_STATUS_PRIVILEGE_NOT_HELD 0x0061 +#define NT_STATUS_INVALID_ACCOUNT_NAME 0x0062 +#define NT_STATUS_USER_EXISTS 0x0063 +#define NT_STATUS_NO_SUCH_USER 0x0064 +#define NT_STATUS_GROUP_EXISTS 0x0065 +#define NT_STATUS_NO_SUCH_GROUP 0x0066 +#define NT_STATUS_MEMBER_IN_GROUP 0x0067 +#define NT_STATUS_MEMBER_NOT_IN_GROUP 0x0068 +#define NT_STATUS_LAST_ADMIN 0x0069 +#define NT_STATUS_WRONG_PASSWORD 0x006a +#define NT_STATUS_ILL_FORMED_PASSWORD 0x006b +#define NT_STATUS_PASSWORD_RESTRICTION 0x006c +#define NT_STATUS_LOGON_FAILURE 0x006d +#define NT_STATUS_ACCOUNT_RESTRICTION 0x006e +#define NT_STATUS_INVALID_LOGON_HOURS 0x006f +#define NT_STATUS_INVALID_WORKSTATION 0x0070 +#define NT_STATUS_PASSWORD_EXPIRED 0x0071 +#define NT_STATUS_ACCOUNT_DISABLED 0x0072 +#define NT_STATUS_NONE_MAPPED 0x0073 +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED 0x0074 +#define NT_STATUS_LUIDS_EXHAUSTED 0x0075 +#define NT_STATUS_INVALID_SUB_AUTHORITY 0x0076 +#define NT_STATUS_INVALID_ACL 0x0077 +#define NT_STATUS_INVALID_SID 0x0078 +#define NT_STATUS_INVALID_SECURITY_DESCR 0x0079 +#define NT_STATUS_PROCEDURE_NOT_FOUND 0x007a +#define NT_STATUS_INVALID_IMAGE_FORMAT 0x007b +#define NT_STATUS_NO_TOKEN 0x007c +#define NT_STATUS_BAD_INHERITANCE_ACL 0x007d +#define NT_STATUS_RANGE_NOT_LOCKED 0x007e +#define NT_STATUS_DISK_FULL 0x007f +#define NT_STATUS_SERVER_DISABLED 0x0080 +#define NT_STATUS_SERVER_NOT_DISABLED 0x0081 +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED 0x0082 +#define NT_STATUS_GUIDS_EXHAUSTED 0x0083 +#define NT_STATUS_INVALID_ID_AUTHORITY 0x0084 +#define NT_STATUS_AGENTS_EXHAUSTED 0x0085 +#define NT_STATUS_INVALID_VOLUME_LABEL 0x0086 +#define NT_STATUS_SECTION_NOT_EXTENDED 0x0087 +#define NT_STATUS_NOT_MAPPED_DATA 0x0088 +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND 0x0089 +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND 0x008a +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND 0x008b +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED 0x008c +#define NT_STATUS_FLOAT_DENORMAL_OPERAND 0x008d +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO 0x008e +#define NT_STATUS_FLOAT_INEXACT_RESULT 0x008f +#define NT_STATUS_FLOAT_INVALID_OPERATION 0x0090 +#define NT_STATUS_FLOAT_OVERFLOW 0x0091 +#define NT_STATUS_FLOAT_STACK_CHECK 0x0092 +#define NT_STATUS_FLOAT_UNDERFLOW 0x0093 +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO 0x0094 +#define NT_STATUS_INTEGER_OVERFLOW 0x0095 +#define NT_STATUS_PRIVILEGED_INSTRUCTION 0x0096 +#define NT_STATUS_TOO_MANY_PAGING_FILES 0x0097 +#define NT_STATUS_FILE_INVALID 0x0098 +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED 0x0099 +#define NT_STATUS_INSUFFICIENT_RESOURCES 0x009a +#define NT_STATUS_DFS_EXIT_PATH_FOUND 0x009b +#define NT_STATUS_DEVICE_DATA_ERROR 0x009c +#define NT_STATUS_DEVICE_NOT_CONNECTED 0x009d +#define NT_STATUS_DEVICE_POWER_FAILURE 0x009e +#define NT_STATUS_FREE_VM_NOT_AT_BASE 0x009f +#define NT_STATUS_MEMORY_NOT_ALLOCATED 0x00a0 +#define NT_STATUS_WORKING_SET_QUOTA 0x00a1 +#define NT_STATUS_MEDIA_WRITE_PROTECTED 0x00a2 +#define NT_STATUS_DEVICE_NOT_READY 0x00a3 +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES 0x00a4 +#define NT_STATUS_BAD_IMPERSONATION_LEVEL 0x00a5 +#define NT_STATUS_CANT_OPEN_ANONYMOUS 0x00a6 +#define NT_STATUS_BAD_VALIDATION_CLASS 0x00a7 +#define NT_STATUS_BAD_TOKEN_TYPE 0x00a8 +#define NT_STATUS_BAD_MASTER_BOOT_RECORD 0x00a9 +#define NT_STATUS_INSTRUCTION_MISALIGNMENT 0x00aa +#define NT_STATUS_INSTANCE_NOT_AVAILABLE 0x00ab +#define NT_STATUS_PIPE_NOT_AVAILABLE 0x00ac +#define NT_STATUS_INVALID_PIPE_STATE 0x00ad +#define NT_STATUS_PIPE_BUSY 0x00ae +#define NT_STATUS_ILLEGAL_FUNCTION 0x00af +#define NT_STATUS_PIPE_DISCONNECTED 0x00b0 +#define NT_STATUS_PIPE_CLOSING 0x00b1 +#define NT_STATUS_PIPE_CONNECTED 0x00b2 +#define NT_STATUS_PIPE_LISTENING 0x00b3 +#define NT_STATUS_INVALID_READ_MODE 0x00b4 +#define NT_STATUS_IO_TIMEOUT 0x00b5 +#define NT_STATUS_FILE_FORCED_CLOSED 0x00b6 +#define NT_STATUS_PROFILING_NOT_STARTED 0x00b7 +#define NT_STATUS_PROFILING_NOT_STOPPED 0x00b8 +#define NT_STATUS_COULD_NOT_INTERPRET 0x00b9 +#define NT_STATUS_FILE_IS_A_DIRECTORY 0x00ba +#define NT_STATUS_NOT_SUPPORTED 0x00bb +#define NT_STATUS_REMOTE_NOT_LISTENING 0x00bc +#define NT_STATUS_DUPLICATE_NAME 0x00bd +#define NT_STATUS_BAD_NETWORK_PATH 0x00be +#define NT_STATUS_NETWORK_BUSY 0x00bf +#define NT_STATUS_DEVICE_DOES_NOT_EXIST 0x00c0 +#define NT_STATUS_TOO_MANY_COMMANDS 0x00c1 +#define NT_STATUS_ADAPTER_HARDWARE_ERROR 0x00c2 +#define NT_STATUS_INVALID_NETWORK_RESPONSE 0x00c3 +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR 0x00c4 +#define NT_STATUS_BAD_REMOTE_ADAPTER 0x00c5 +#define NT_STATUS_PRINT_QUEUE_FULL 0x00c6 +#define NT_STATUS_NO_SPOOL_SPACE 0x00c7 +#define NT_STATUS_PRINT_CANCELLED 0x00c8 +#define NT_STATUS_NETWORK_NAME_DELETED 0x00c9 +#define NT_STATUS_NETWORK_ACCESS_DENIED 0x00ca +#define NT_STATUS_BAD_DEVICE_TYPE 0x00cb +#define NT_STATUS_BAD_NETWORK_NAME 0x00cc +#define NT_STATUS_TOO_MANY_NAMES 0x00cd +#define NT_STATUS_TOO_MANY_SESSIONS 0x00ce +#define NT_STATUS_SHARING_PAUSED 0x00cf +#define NT_STATUS_REQUEST_NOT_ACCEPTED 0x00d0 +#define NT_STATUS_REDIRECTOR_PAUSED 0x00d1 +#define NT_STATUS_NET_WRITE_FAULT 0x00d2 +#define NT_STATUS_PROFILING_AT_LIMIT 0x00d3 +#define NT_STATUS_NOT_SAME_DEVICE 0x00d4 +#define NT_STATUS_FILE_RENAMED 0x00d5 +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED 0x00d6 +#define NT_STATUS_NO_SECURITY_ON_OBJECT 0x00d7 +#define NT_STATUS_CANT_WAIT 0x00d8 +#define NT_STATUS_PIPE_EMPTY 0x00d9 +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO 0x00da +#define NT_STATUS_CANT_TERMINATE_SELF 0x00db +#define NT_STATUS_INVALID_SERVER_STATE 0x00dc +#define NT_STATUS_INVALID_DOMAIN_STATE 0x00dd +#define NT_STATUS_INVALID_DOMAIN_ROLE 0x00de +#define NT_STATUS_NO_SUCH_DOMAIN 0x00df +#define NT_STATUS_DOMAIN_EXISTS 0x00e0 +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED 0x00e1 +#define NT_STATUS_OPLOCK_NOT_GRANTED 0x00e2 +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL 0x00e3 +#define NT_STATUS_INTERNAL_DB_CORRUPTION 0x00e4 +#define NT_STATUS_INTERNAL_ERROR 0x00e5 +#define NT_STATUS_GENERIC_NOT_MAPPED 0x00e6 +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT 0x00e7 +#define NT_STATUS_INVALID_USER_BUFFER 0x00e8 +#define NT_STATUS_UNEXPECTED_IO_ERROR 0x00e9 +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR 0x00ea +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR 0x00eb +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR 0x00ec +#define NT_STATUS_NOT_LOGON_PROCESS 0x00ed +#define NT_STATUS_LOGON_SESSION_EXISTS 0x00ee +#define NT_STATUS_INVALID_PARAMETER_1 0x00ef +#define NT_STATUS_INVALID_PARAMETER_2 0x00f0 +#define NT_STATUS_INVALID_PARAMETER_3 0x00f1 +#define NT_STATUS_INVALID_PARAMETER_4 0x00f2 +#define NT_STATUS_INVALID_PARAMETER_5 0x00f3 +#define NT_STATUS_INVALID_PARAMETER_6 0x00f4 +#define NT_STATUS_INVALID_PARAMETER_7 0x00f5 +#define NT_STATUS_INVALID_PARAMETER_8 0x00f6 +#define NT_STATUS_INVALID_PARAMETER_9 0x00f7 +#define NT_STATUS_INVALID_PARAMETER_10 0x00f8 +#define NT_STATUS_INVALID_PARAMETER_11 0x00f9 +#define NT_STATUS_INVALID_PARAMETER_12 0x00fa +#define NT_STATUS_REDIRECTOR_NOT_STARTED 0x00fb +#define NT_STATUS_REDIRECTOR_STARTED 0x00fc +#define NT_STATUS_STACK_OVERFLOW 0x00fd +#define NT_STATUS_NO_SUCH_PACKAGE 0x00fe +#define NT_STATUS_BAD_FUNCTION_TABLE 0x00ff +#define NT_STATUS_VARIABLE_NOT_FOUND 0x0100 +#define NT_STATUS_DIRECTORY_NOT_EMPTY 0x0101 +#define NT_STATUS_FILE_CORRUPT_ERROR 0x0102 +#define NT_STATUS_NOT_A_DIRECTORY 0x0103 +#define NT_STATUS_BAD_LOGON_SESSION_STATE 0x0104 +#define NT_STATUS_LOGON_SESSION_COLLISION 0x0105 +#define NT_STATUS_NAME_TOO_LONG 0x0106 +#define NT_STATUS_FILES_OPEN 0x0107 +#define NT_STATUS_CONNECTION_IN_USE 0x0108 +#define NT_STATUS_MESSAGE_NOT_FOUND 0x0109 +#define NT_STATUS_PROCESS_IS_TERMINATING 0x010a +#define NT_STATUS_INVALID_LOGON_TYPE 0x010b +#define NT_STATUS_NO_GUID_TRANSLATION 0x010c +#define NT_STATUS_CANNOT_IMPERSONATE 0x010d +#define NT_STATUS_IMAGE_ALREADY_LOADED 0x010e +#define NT_STATUS_ABIOS_NOT_PRESENT 0x010f +#define NT_STATUS_ABIOS_LID_NOT_EXIST 0x0110 +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED 0x0111 +#define NT_STATUS_ABIOS_NOT_LID_OWNER 0x0112 +#define NT_STATUS_ABIOS_INVALID_COMMAND 0x0113 +#define NT_STATUS_ABIOS_INVALID_LID 0x0114 +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE 0x0115 +#define NT_STATUS_ABIOS_INVALID_SELECTOR 0x0116 +#define NT_STATUS_NO_LDT 0x0117 +#define NT_STATUS_INVALID_LDT_SIZE 0x0118 +#define NT_STATUS_INVALID_LDT_OFFSET 0x0119 +#define NT_STATUS_INVALID_LDT_DESCRIPTOR 0x011a +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT 0x011b +#define NT_STATUS_RXACT_INVALID_STATE 0x011c +#define NT_STATUS_RXACT_COMMIT_FAILURE 0x011d +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO 0x011e +#define NT_STATUS_TOO_MANY_OPENED_FILES 0x011f +#define NT_STATUS_CANCELLED 0x0120 +#define NT_STATUS_CANNOT_DELETE 0x0121 +#define NT_STATUS_INVALID_COMPUTER_NAME 0x0122 +#define NT_STATUS_FILE_DELETED 0x0123 +#define NT_STATUS_SPECIAL_ACCOUNT 0x0124 +#define NT_STATUS_SPECIAL_GROUP 0x0125 +#define NT_STATUS_SPECIAL_USER 0x0126 +#define NT_STATUS_MEMBERS_PRIMARY_GROUP 0x0127 +#define NT_STATUS_FILE_CLOSED 0x0128 +#define NT_STATUS_TOO_MANY_THREADS 0x0129 +#define NT_STATUS_THREAD_NOT_IN_PROCESS 0x012a +#define NT_STATUS_TOKEN_ALREADY_IN_USE 0x012b +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED 0x012c +#define NT_STATUS_COMMITMENT_LIMIT 0x012d +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT 0x012e +#define NT_STATUS_INVALID_IMAGE_NOT_MZ 0x012f +#define NT_STATUS_INVALID_IMAGE_PROTECT 0x0130 +#define NT_STATUS_INVALID_IMAGE_WIN_16 0x0131 +#define NT_STATUS_LOGON_SERVER_CONFLICT 0x0132 +#define NT_STATUS_TIME_DIFFERENCE_AT_DC 0x0133 +#define NT_STATUS_SYNCHRONIZATION_REQUIRED 0x0134 +#define NT_STATUS_DLL_NOT_FOUND 0x0135 +#define NT_STATUS_OPEN_FAILED 0x0136 +#define NT_STATUS_IO_PRIVILEGE_FAILED 0x0137 +#define NT_STATUS_ORDINAL_NOT_FOUND 0x0138 +#define NT_STATUS_ENTRYPOINT_NOT_FOUND 0x0139 +#define NT_STATUS_CONTROL_C_EXIT 0x013a +#define NT_STATUS_LOCAL_DISCONNECT 0x013b +#define NT_STATUS_REMOTE_DISCONNECT 0x013c +#define NT_STATUS_REMOTE_RESOURCES 0x013d +#define NT_STATUS_LINK_FAILED 0x013e +#define NT_STATUS_LINK_TIMEOUT 0x013f +#define NT_STATUS_INVALID_CONNECTION 0x0140 +#define NT_STATUS_INVALID_ADDRESS 0x0141 +#define NT_STATUS_DLL_INIT_FAILED 0x0142 +#define NT_STATUS_MISSING_SYSTEMFILE 0x0143 +#define NT_STATUS_UNHANDLED_EXCEPTION 0x0144 +#define NT_STATUS_APP_INIT_FAILURE 0x0145 +#define NT_STATUS_PAGEFILE_CREATE_FAILED 0x0146 +#define NT_STATUS_NO_PAGEFILE 0x0147 +#define NT_STATUS_INVALID_LEVEL 0x0148 +#define NT_STATUS_WRONG_PASSWORD_CORE 0x0149 +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT 0x014a +#define NT_STATUS_PIPE_BROKEN 0x014b +#define NT_STATUS_REGISTRY_CORRUPT 0x014c +#define NT_STATUS_REGISTRY_IO_FAILED 0x014d +#define NT_STATUS_NO_EVENT_PAIR 0x014e +#define NT_STATUS_UNRECOGNIZED_VOLUME 0x014f +#define NT_STATUS_SERIAL_NO_DEVICE_INITED 0x0150 +#define NT_STATUS_NO_SUCH_ALIAS 0x0151 +#define NT_STATUS_MEMBER_NOT_IN_ALIAS 0x0152 +#define NT_STATUS_MEMBER_IN_ALIAS 0x0153 +#define NT_STATUS_ALIAS_EXISTS 0x0154 +#define NT_STATUS_LOGON_NOT_GRANTED 0x0155 +#define NT_STATUS_TOO_MANY_SECRETS 0x0156 +#define NT_STATUS_SECRET_TOO_LONG 0x0157 +#define NT_STATUS_INTERNAL_DB_ERROR 0x0158 +#define NT_STATUS_FULLSCREEN_MODE 0x0159 +#define NT_STATUS_TOO_MANY_CONTEXT_IDS 0x015a +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED 0x015b +#define NT_STATUS_NOT_REGISTRY_FILE 0x015c +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED 0x015d +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR 0x015e +#define NT_STATUS_FT_MISSING_MEMBER 0x015f +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY 0x0160 +#define NT_STATUS_ILLEGAL_CHARACTER 0x0161 +#define NT_STATUS_UNMAPPABLE_CHARACTER 0x0162 +#define NT_STATUS_UNDEFINED_CHARACTER 0x0163 +#define NT_STATUS_FLOPPY_VOLUME 0x0164 +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND 0x0165 +#define NT_STATUS_FLOPPY_WRONG_CYLINDER 0x0166 +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR 0x0167 +#define NT_STATUS_FLOPPY_BAD_REGISTERS 0x0168 +#define NT_STATUS_DISK_RECALIBRATE_FAILED 0x0169 +#define NT_STATUS_DISK_OPERATION_FAILED 0x016a +#define NT_STATUS_DISK_RESET_FAILED 0x016b +#define NT_STATUS_SHARED_IRQ_BUSY 0x016c +#define NT_STATUS_FT_ORPHANING 0x016d +#define NT_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT 0x016e +#define NT_STATUS_16F 0x016f +#define NT_STATUS_170 0x0170 +#define NT_STATUS_171 0x0171 +#define NT_STATUS_PARTITION_FAILURE 0x0172 +#define NT_STATUS_INVALID_BLOCK_LENGTH 0x0173 +#define NT_STATUS_DEVICE_NOT_PARTITIONED 0x0174 +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA 0x0175 +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA 0x0176 +#define NT_STATUS_EOM_OVERFLOW 0x0177 +#define NT_STATUS_NO_MEDIA 0x0178 +#define NT_STATUS_179 0x0179 +#define NT_STATUS_NO_SUCH_MEMBER 0x017a +#define NT_STATUS_INVALID_MEMBER 0x017b +#define NT_STATUS_KEY_DELETED 0x017c +#define NT_STATUS_NO_LOG_SPACE 0x017d +#define NT_STATUS_TOO_MANY_SIDS 0x017e +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED 0x017f +#define NT_STATUS_KEY_HAS_CHILDREN 0x0180 +#define NT_STATUS_CHILD_MUST_BE_VOLATILE 0x0181 +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR 0x0182 +#define NT_STATUS_DRIVER_INTERNAL_ERROR 0x0183 +#define NT_STATUS_INVALID_DEVICE_STATE 0x0184 +#define NT_STATUS_IO_DEVICE_ERROR 0x0185 +#define NT_STATUS_DEVICE_PROTOCOL_ERROR 0x0186 +#define NT_STATUS_BACKUP_CONTROLLER 0x0187 +#define NT_STATUS_LOG_FILE_FULL 0x0188 +#define NT_STATUS_TOO_LATE 0x0189 +#define NT_STATUS_NO_TRUST_LSA_SECRET 0x018a +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT 0x018b +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE 0x018c +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE 0x018d +#define NT_STATUS_EVENTLOG_FILE_CORRUPT 0x018e +#define NT_STATUS_EVENTLOG_CANT_START 0x018f +#define NT_STATUS_TRUST_FAILURE 0x0190 +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED 0x0191 +#define NT_STATUS_NETLOGON_NOT_STARTED 0x0192 +#define NT_STATUS_ACCOUNT_EXPIRED 0x0193 +#define NT_STATUS_POSSIBLE_DEADLOCK 0x0194 +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT 0x0195 +#define NT_STATUS_REMOTE_SESSION_LIMIT 0x0196 +#define NT_STATUS_EVENTLOG_FILE_CHANGED 0x0197 +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT 0x0198 +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT 0x0199 +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT 0x019a +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT 0x019b +#define NT_STATUS_FS_DRIVER_REQUIRED 0x019c +#define NT_STATUS_NO_USER_SESSION_KEY 0x0202 +#define NT_STATUS_USER_SESSION_DELETED 0x0203 +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND 0x0204 +#define NT_STATUS_INSUFF_SERVER_RESOURCES 0x0205 +#define NT_STATUS_INVALID_BUFFER_SIZE 0x0206 +#define NT_STATUS_INVALID_ADDRESS_COMPONENT 0x0207 +#define NT_STATUS_INVALID_ADDRESS_WILDCARD 0x0208 +#define NT_STATUS_TOO_MANY_ADDRESSES 0x0209 +#define NT_STATUS_ADDRESS_ALREADY_EXISTS 0x020a +#define NT_STATUS_ADDRESS_CLOSED 0x020b +#define NT_STATUS_CONNECTION_DISCONNECTED 0x020c +#define NT_STATUS_CONNECTION_RESET 0x020d +#define NT_STATUS_TOO_MANY_NODES 0x020e +#define NT_STATUS_TRANSACTION_ABORTED 0x020f +#define NT_STATUS_TRANSACTION_TIMED_OUT 0x0210 +#define NT_STATUS_TRANSACTION_NO_RELEASE 0x0211 +#define NT_STATUS_TRANSACTION_NO_MATCH 0x0212 +#define NT_STATUS_TRANSACTION_RESPONDED 0x0213 +#define NT_STATUS_TRANSACTION_INVALID_ID 0x0214 +#define NT_STATUS_TRANSACTION_INVALID_TYPE 0x0215 +#define NT_STATUS_NOT_SERVER_SESSION 0x0216 +#define NT_STATUS_NOT_CLIENT_SESSION 0x0217 +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE 0x0218 +#define NT_STATUS_DEBUG_ATTACH_FAILED 0x0219 +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED 0x021a +#define NT_STATUS_DATA_NOT_ACCEPTED 0x021b +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND 0x021c +#define NT_STATUS_VDM_HARD_ERROR 0x021d +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT 0x021e +#define NT_STATUS_REPLY_MESSAGE_MISMATCH 0x021f +#define NT_STATUS_MAPPED_ALIGNMENT 0x0220 +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH 0x0221 +#define NT_STATUS_LOST_WRITEBEHIND_DATA 0x0222 +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID 0x0223 +#define NT_STATUS_PASSWORD_MUST_CHANGE 0x0224 +#define NT_STATUS_NOT_FOUND 0x0225 +#define NT_STATUS_NOT_TINY_STREAM 0x0226 +#define NT_STATUS_RECOVERY_FAILURE 0x0227 +#define NT_STATUS_STACK_OVERFLOW_READ 0x0228 +#define NT_STATUS_FAIL_CHECK 0x0229 +#define NT_STATUS_DUPLICATE_OBJECTID 0x022a +#define NT_STATUS_OBJECTID_EXISTS 0x022b +#define NT_STATUS_CONVERT_TO_LARGE 0x022c +#define NT_STATUS_RETRY 0x022d +#define NT_STATUS_FOUND_OUT_OF_SCOPE 0x022e +#define NT_STATUS_ALLOCATE_BUCKET 0x022f +#define NT_STATUS_PROPSET_NOT_FOUND 0x0230 +#define NT_STATUS_MARSHALL_OVERFLOW 0x0231 +#define NT_STATUS_INVALID_VARIANT 0x0232 +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND 0x0233 +#define NT_STATUS_ACCOUNT_LOCKED_OUT 0x0234 +#define NT_STATUS_HANDLE_NOT_CLOSABLE 0x0235 +#define NT_STATUS_CONNECTION_REFUSED 0x0236 +#define NT_STATUS_GRACEFUL_DISCONNECT 0x0237 +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED 0x0238 +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED 0x0239 +#define NT_STATUS_CONNECTION_INVALID 0x023a +#define NT_STATUS_CONNECTION_ACTIVE 0x023b +#define NT_STATUS_NETWORK_UNREACHABLE 0x023c +#define NT_STATUS_HOST_UNREACHABLE 0x023d +#define NT_STATUS_PROTOCOL_UNREACHABLE 0x023e +#define NT_STATUS_PORT_UNREACHABLE 0x023f +#define NT_STATUS_REQUEST_ABORTED 0x0240 +#define NT_STATUS_CONNECTION_ABORTED 0x0241 +#define NT_STATUS_BAD_COMPRESSION_BUFFER 0x0242 +#define NT_STATUS_USER_MAPPED_FILE 0x0243 +#define NT_STATUS_AUDIT_FAILED 0x0244 +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET 0x0245 +#define NT_STATUS_CONNECTION_COUNT_LIMIT 0x0246 +#define NT_STATUS_LOGIN_TIME_RESTRICTION 0x0247 +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION 0x0248 +#define NT_STATUS_IMAGE_MP_UP_MISMATCH 0x0249 +#define NT_STATUS_INSUFFICIENT_LOGON_INFO 0x0250 +#define NT_STATUS_BAD_DLL_ENTRYPOINT 0x0251 +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT 0x0252 +#define NT_STATUS_LPC_REPLY_LOST 0x0253 +#define NT_STATUS_IP_ADDRESS_CONFLICT1 0x0254 +#define NT_STATUS_IP_ADDRESS_CONFLICT2 0x0255 +#define NT_STATUS_REGISTRY_QUOTA_LIMIT 0x0256 +#define NT_STATUS_PATH_NOT_COVERED 0x0257 +#define NT_STATUS_NO_CALLBACK_ACTIVE 0x0258 +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED 0x0259 +#define NT_STATUS_PWD_TOO_SHORT 0x025a +#define NT_STATUS_PWD_TOO_RECENT 0x025b +#define NT_STATUS_PWD_HISTORY_CONFLICT 0x025c +#define NT_STATUS_PLUGPLAY_NO_DEVICE 0x025e +#define NT_STATUS_UNSUPPORTED_COMPRESSION 0x025f +#define NT_STATUS_INVALID_HW_PROFILE 0x0260 +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH 0x0261 +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND 0x0262 +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND 0x0263 +#define NT_STATUS_RESOURCE_NOT_OWNED 0x0264 +#define NT_STATUS_TOO_MANY_LINKS 0x0265 +#define NT_STATUS_QUOTA_LIST_INCONSISTENT 0x0266 +#define NT_STATUS_FILE_IS_OFFLINE 0x0267 + +#define NT_STATUS_LICENSE_VIOLATION 0x026a + +#define NT_STATUS_DFS_UNAVAILABLE 0x026d +#define NT_STATUS_VOLUME_DISMOUNTED 0x026e + +#define NT_STATUS_NOT_A_REPARSE_POINT 0x0275 + +#define NT_STATUS_REPARSE_POINT_NOT_RESOLVED 0x0280 +#define NT_STATUS_DIRECTORY_IS_A_REPARSE_POINT 0x0281 + +#define NT_STATUS_ENCRYPTION_FAILED 0x028a +#define NT_STATUS_DECRYPTION_FAILED 0x028b +#define NT_STATUS_RANGE_NOT_FOUND 0x028c +#define NT_STATUS_NO_RECOVERY_POLICY 0x028d +#define NT_STATUS_NO_EFS 0x028e +#define NT_STATUS_WRONG_EFS 0x028f +#define NT_STATUS_NO_USER_KEYS 0x0290 +#define NT_STATUS_FILE_NOT_ENCRYPTED 0x0291 + +#define NT_STATUS_FILE_ENCRYPTED 0x0293 + +#define NT_STATUS_VOLUME_NOT_UPGRADED 0x029c + +#define NT_STATUS_KDC_CERT_EXPIRED 0x040e +/* + * 0x00010000-0x0001ffff are "DBG" errors + * 0x00020000-0x0003ffff are "RPC" errors + * 0x00040000-0x0004ffff are "PNP" errors + * 0x000A0000-0x000Affff are "CTX" errors + * 0x00130000-0x0013ffff are "CLUSTER" errors + * 0x00140000-0x0014ffff are "ACPI" errors + * 0x00150000-0x0015ffff are "SXS" errors + */ + +/* + * size of the GUID returned in an extended security negotiate response + */ +#define SMB_GUIDLEN 16 + +typedef uint16_t smbfh; + +/* + * NTLMv2 blob header structure. + */ +struct ntlmv2_blobhdr { + uint32_t header; + uint32_t reserved; + uint64_t timestamp; + uint64_t client_nonce; + uint32_t unknown1; +}; +typedef struct ntlmv2_blobhdr ntlmv2_blobhdr_t; + +/* + * NTLMv2 name header structure, for names in a blob. + */ +struct ntlmv2_namehdr { + uint16_t type; + uint16_t len; +}; +typedef struct ntlmv2_namehdr ntlmv2_namehdr_t; + +#define NAMETYPE_EOL 0x0000 /* end of list of names */ +#define NAMETYPE_MACHINE_NB 0x0001 /* NetBIOS machine name */ +#define NAMETYPE_DOMAIN_NB 0x0002 /* NetBIOS domain name */ +#define NAMETYPE_MACHINE_DNS 0x0003 /* DNS machine name */ +#define NAMETYPE_DOMAIN_DNS 0x0004 /* DNS Active Directory domain name */ + +/* + * Named pipe commands. + */ +#define TRANS_CALL_NAMED_PIPE 0x54 /* open/write/read/close pipe */ +#define TRANS_WAIT_NAMED_PIPE 0x53 /* wait for pipe to be !busy */ +#define TRANS_PEEK_NAMED_PIPE 0x23 /* read but don't remove data */ +#define TRANS_Q_NAMED_PIPE_HAND_STATE 0x21 /* query pipe handle modes */ +#define TRANS_SET_NAMED_PIPE_HAND_STATE 0x01 /* set pipe handle modes */ +#define TRANS_Q_NAMED_PIPE_INFO 0x22 /* query pipe attributes */ +#define TRANS_TRANSACT_NAMED_PIPE 0x26 /* r/w operation on pipe */ +#define TRANS_READ_NAMED_PIPE 0x11 /* read pipe in "raw" mode */ + /* (non message mode) */ +#define TRANS_WRITE_NAMED_PIPE 0x31 /* write pipe "raw" mode */ + /* (non message mode) */ + +/* + * Share types, visible via NetShareEnum + */ +#define STYPE_DISKTREE 0x00000000 +#define STYPE_PRINTQ 0x00000001 +#define STYPE_DEVICE 0x00000002 +#define STYPE_IPC 0x00000003 +#define STYPE_UNKNOWN 0x00000004 +#define STYPE_MASK 0x0000000F +#define STYPE_TEMPORARY 0x40000000 +#define STYPE_HIDDEN 0x80000000 + +#endif /* _NETSMB_SMB_H_ */ diff --git a/usr/src/uts/common/netsmb/smb_dev.h b/usr/src/uts/common/netsmb/smb_dev.h new file mode 100644 index 0000000000..b414bcf9b8 --- /dev/null +++ b/usr/src/uts/common/netsmb/smb_dev.h @@ -0,0 +1,420 @@ +/* + * 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. + * + * $Id: smb_dev.h,v 1.10.178.1 2005/05/27 02:35:29 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _NETSMB_DEV_H_ +#define _NETSMB_DEV_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines an internal ABI for the "nsmb" driver, + * particularly the various data structures passed to ioctl. + * In order to avoid some messy 32-bit to 64-bit conversions + * in the driver, we take pains to define all data structures + * that pass across the user/kernel boundary in a way that + * makes them invariant across 32-bit and 64-bit ABIs. + * This invariance is checked during the driver build + * using a mechanism similar to genassym.h builds. + * + * If you change any of the ioctl data structures in + * this file, YOU MUST ALSO edit this file: + * uts/common/fs/smbclnt/netsmb/offsets.in + * and then verify the invariance describe above. + * + * Also, remember to "bump" NSMB_VER below when + * any part of this user/kernel I/F changes. + */ + +#ifndef _KERNEL +#include <sys/types.h> +#endif + +#include <sys/socket_impl.h> +#include <netsmb/smb.h> +#include <netsmb/netbios.h> + +#define NSMB_NAME "nsmb" + +/* + * Update NSMB_VER* if any of the ioctl codes and/or + * associated structures change in ways that would + * make them incompatible with an old driver. + */ +#define NSMB_VERMAJ 1 +#define NSMB_VERMIN 3500 +#define NSMB_VERSION (NSMB_VERMAJ * 100000 + NSMB_VERMIN) +#define NSMB_VER_STR "1.35" + +#define NSMBFL_OPEN 0x0001 +#define NSMBFL_NEWVC 0x0002 + +/* + * Hack-ish errno values we need to expose to the library. + * EBADRPC is used for message decoding errors. + * EAUTH is used for CIFS authentication errors. + */ +#ifndef EBADRPC +#define EBADRPC 113 /* XXX */ +#endif +#ifndef EAUTH +#define EAUTH 114 /* XXX */ +#endif + +/* + * "Level" in the connection object hierarchy + */ +#define SMBL_SM 0 +#define SMBL_VC 1 +#define SMBL_SHARE 2 +#define SMBL_NUM 3 +#define SMBL_NONE (-1) + +/* + * Upper/lower case options + */ +#define SMB_CS_NONE 0x0000 +#define SMB_CS_UPPER 0x0001 /* convert passed string to upper case */ +#define SMB_CS_LOWER 0x0002 /* convert passed string to lower case */ + +/* + * access mode stuff (see also smb_lib.h) + */ +#define SMBM_ANY_OWNER ((uid_t)-1) +#define SMBM_ANY_GROUP ((gid_t)-1) +#define SMBM_MASK 0777 +#define SMBM_EXACT 010000 /* check for specified mode exactly */ +#ifdef _KERNEL +/* In-kernel, we prefer the vnode.h names. */ +#define SMBM_READ VREAD /* (S_IRUSR) read conn attrs. */ +#define SMBM_WRITE VWRITE /* (S_IWUSR) modify conn attrs */ +#define SMBM_EXEC VEXEC /* (S_IXUSR) can send SMB requests */ +#endif + +/* + * Option flags in smbioc_ossn.ioc_opt + * and vcspec.optflags + */ +#define SMBVOPT_CREATE 0x0001 /* create object if necessary */ +#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 */ +/* XXX: How about a separate field for these? */ +#define SMBVOPT_MINAUTH 0x7000 /* min. auth. level (mask) */ +#define SMBVOPT_MINAUTH_NONE 0x0000 /* any authentication OK */ +#define SMBVOPT_MINAUTH_LM 0x1000 /* no plaintext passwords */ +#define SMBVOPT_MINAUTH_NTLM 0x2000 /* don't send LM reply */ +#define SMBVOPT_MINAUTH_NTLMV2 0x3000 /* don't fall back to NTLMv1 */ +#define SMBVOPT_MINAUTH_KERBEROS 0x4000 /* don't do NTLMv1 or v2 */ + +/* + * Option flags in smbioc_oshare.ioc_opt + * and sharespec.optflags + */ +#define SMBSOPT_CREATE SMBVOPT_CREATE +#define SMBSOPT_PERMANENT SMBVOPT_PERMANENT + +#define MAX_STR_LEN 8 /* Maxilum length of the minor device name */ + +/* + * We're now using structures that are invariant + * across 32-bit vs 64-bit compilers for all + * member sizes and offsets. Scalar members + * simply have to use fixed-size types. + * Pointers are a little harder... + * We use this union for all pointers that + * must pass between user and kernel. + */ +typedef union lptr { + uint64_t lp_ll; +#ifdef _LP64 + void *lp_ptr; +#endif +#ifdef _ILP32 + void *_lp_p2[2]; +#ifdef _LITTLE_ENDIAN +#define lp_ptr _lp_p2[0] +#define lp_pad _lp_p2[1] +#else /* _ENDIAN */ +#define lp_pad _lp_p2[0] +#define lp_ptr _lp_p2[1] +#endif /* _ENDIAN */ +#endif /* _ILP32 */ +} lptr_t; + +/* + * Handy union of sockaddr types we use. + * Type discriminator is sa_family + */ +union sockaddr_any { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_nb nb; +}; + + +/* + * SMBIOC_LOOKUP flags + */ +#define SMBLK_CREATE SMBVOPT_CREATE + +#define DEF_SEC_TOKEN_LEN 2048 + +struct smbioc_ossn { + union sockaddr_any ioc_server; + union sockaddr_any ioc_local; + char ioc_localcs[16]; /* local charset */ + char ioc_servercs[16]; /* server charset */ + char ioc_srvname[SMB_MAXSRVNAMELEN + 1]; + char ioc_user[SMB_MAXUSERNAMELEN + 1]; + char ioc_workgroup[SMB_MAXUSERNAMELEN + 1]; + char ioc_password[SMB_MAXPASSWORDLEN + 1]; + int32_t ioc_opt; + int32_t ioc_timeout; /* ignored?! XXX */ + int32_t ioc_retrycount; /* number of retries before giveup */ + uid_t ioc_owner; /* proposed owner */ + gid_t ioc_group; /* proposed group */ + mode_t ioc_mode; /* desired access mode */ + mode_t ioc_rights; /* SMBM_* */ + int32_t ioc_intoklen; + int32_t ioc_outtoklen; + /* copyout ends at this offset */ + lptr_t _ioc_intok; + lptr_t _ioc_outtok; +}; +typedef struct smbioc_ossn smbioc_ossn_t; +#define ioc_intok _ioc_intok.lp_ptr +#define ioc_outtok _ioc_outtok.lp_ptr + + +struct smbioc_oshare { + char ioc_share[SMB_MAXSHARENAMELEN + 1]; + char ioc_password[SMB_MAXPASSWORDLEN + 1]; + int32_t ioc_opt; + int32_t ioc_stype; /* share type */ + uid_t ioc_owner; /* proposed owner of share */ + gid_t ioc_group; /* proposed group of share */ + mode_t ioc_mode; /* desired access mode to share */ + mode_t ioc_rights; /* SMBM_* */ + /* + * Hack: need the size of this to be 8-byte aligned + * so that the ioc_ossn following it in smbioc_lookup + * is correctly aligned... + */ + int32_t ioc__pad; +}; +typedef struct smbioc_oshare smbioc_oshare_t; + +typedef struct smbioc_rq { + uchar_t ioc_cmd; + uchar_t ioc_twc; /* _twords */ + ushort_t ioc_tbc; /* _tbytes */ + int32_t ioc_rpbufsz; /* _rpbuf */ + uchar_t ioc__pad1; + uchar_t ioc_rwc; + ushort_t ioc_rbc; + uchar_t ioc__pad2; + uint8_t ioc_errclass; + uint16_t ioc_serror; + uint32_t ioc_error; + uint32_t ioc__pad3; + /* + * Copyout all but the pointers, which + * we may have set to kernel memory. + * See ..._COPYOUT_SIZE + */ + lptr_t _ioc_twords; + lptr_t _ioc_tbytes; + lptr_t _ioc_rpbuf; +} smbioc_rq_t; +#define ioc_twords _ioc_twords.lp_ptr +#define ioc_tbytes _ioc_tbytes.lp_ptr +#define ioc_rpbuf _ioc_rpbuf.lp_ptr +#define SMBIOC_RQ_COPYOUT_SIZE \ + (offsetof(smbioc_rq_t, _ioc_twords)) + + +typedef struct smbioc_t2rq { + uint16_t ioc_setup[SMB_MAXSETUPWORDS]; + int32_t ioc_setupcnt; + char ioc_name[128]; + 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; + /* + * Copyout all but the pointers, which + * we may have set to kernel memory. + * See ..._COPYOUT_SIZE + */ + 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 +#define SMBIOC_T2RQ_COPYOUT_SIZE \ + (offsetof(smbioc_t2rq_t, _ioc_tparam)) + + +typedef struct smbioc_flags { + int32_t ioc_level; /* 0 - session, 1 - share */ + int32_t ioc_mask; + int32_t ioc_flags; +} smbioc_flags_t; + +typedef struct smbioc_lookup { + int32_t ioc_level; + int32_t ioc_flags; + struct smbioc_oshare ioc_sh; + struct smbioc_ossn ioc_ssn; +} smbioc_lookup_t; +#define SMBIOC_LOOK_COPYOUT_SIZE \ + (offsetof(smbioc_lookup_t, ioc_ssn._ioc_intok)) + +typedef struct smbioc_rw { + uint16_t ioc_fh; + uint32_t ioc_cnt; + lloff_t _ioc_offset; + lptr_t _ioc_base; +} smbioc_rw_t; +#define ioc_offset _ioc_offset._f +#define ioc_base _ioc_base.lp_ptr +#define SMBIOC_RW_COPYOUT_SIZE \ + (offsetof(smbioc_rw_t, _ioc_base)) + +/* Password Keychain (PK) support. */ +#define SMBIOC_PK_MAXLEN 255 +typedef struct smbioc_pk { + uid_t pk_uid; /* UID for PAM use */ + char pk_dom[SMBIOC_PK_MAXLEN+1]; /* CIFS domain name */ + char pk_usr[SMBIOC_PK_MAXLEN+1]; /* CIFS user name */ + char pk_pass[SMBIOC_PK_MAXLEN+1]; /* CIFS password */ +} smbioc_pk_t; + + +/* + * Device IOCTLs + * + * Define ioctl codes the way ZFS does. + * The "base" value is arbitrary, and can + * occupy the high word if we like, because + * our driver does its own copyin/copyout. + * Keep GETVERS first and use it to verify + * driver compatibility with the library. + */ +#define SMBIOC_BASE ((('n' << 8) | 's') << 8) +typedef enum nsmb_ioc { + SMBIOC_GETVERS = SMBIOC_BASE, + SMBIOC_REQUEST, + SMBIOC_T2RQ, + SMBIOC_LOOKUP, + SMBIOC_READ, + SMBIOC_WRITE, + SMBIOC_NEGOTIATE, + SMBIOC_SSNSETUP, + SMBIOC_TCON, + SMBIOC_TDIS, + SMBIOC_FLAGS2, + /* Password Keychain (PK) support. */ + SMBIOC_PK_ADD, /* Add/Modify a password entry */ + SMBIOC_PK_CHK, /* Check for a password entry */ + SMBIOC_PK_DEL, /* Delete specified password entry */ + SMBIOC_PK_DEL_OWNER, /* all owned by the caller */ + SMBIOC_PK_DEL_EVERYONE /* all owned by everyone */ +} nsmb_ioc_t; + +#ifdef _KERNEL +#include <sys/dditypes.h> /* for dev_info_t */ + +#define SMBST_CONNECTED 1 + +/* Size of storage for p/w hashes. */ +#define SMB_PWH_MAX 24 + +extern const uint32_t nsmb_version; + +struct smb_cred; +struct smb_share; +struct smb_vc; + +typedef struct smb_dev { + int sd_opened; /* Opened or not */ + int sd_level; /* Future use */ + struct smb_vc *sd_vc; /* Reference to VC */ + struct smb_share *sd_share; /* Reference to share if any */ + int sd_poll; /* Future use */ + int sd_seq; /* Kind of minor number/instance no */ + int sd_flags; /* State of connection */ + zoneid_t zoneid; /* Zone id */ + dev_info_t *smb_dip; /* ptr to dev_info node */ + void *sd_devfs; /* Dont know how to use this. but */ + struct cred *smb_cred; /* per dev credentails. Future use */ +} smb_dev_t; + +/* + * Compound user interface + */ +int smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc **vcpp); +int smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc *vcp); +int smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred, + struct smb_vc *vcp, struct smb_share **sspp); +int smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *data, + struct smb_cred *scred); +int smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *data, + struct smb_cred *scred); +int smb_usr_rw(struct smb_share *ssp, smbioc_rw_t *dp, + int cmd, struct smb_cred *scred); +int smb_dev2share(int fd, struct smb_share **sspp); + +#endif /* _KERNEL */ +#endif /* _NETSMB_DEV_H_ */ diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c index d5e83e8e6a..b79374602a 100644 --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -2096,3 +2096,24 @@ secpolicy_vscan(const cred_t *cr) return (0); } + +/* + * secpolicy_smbfs_login + * + * Determines if the caller can add and delete the smbfs login + * password in the the nsmb kernel module for the CIFS client. + * + * Returns: + * 0 access is allowed. + * EPERM access is NOT allowed. + */ +int +secpolicy_smbfs_login(const cred_t *cr, uid_t uid) +{ + uid_t cruid = crgetruid(cr); + + if (cruid == uid) + return (0); + return (PRIV_POLICY(cr, PRIV_PROC_OWNER, B_FALSE, + EPERM, NULL)); +} diff --git a/usr/src/uts/common/os/vfs_conf.c b/usr/src/uts/common/os/vfs_conf.c index b15c834fbe..6e433284b4 100644 --- a/usr/src/uts/common/os/vfs_conf.c +++ b/usr/src/uts/common/os/vfs_conf.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -81,6 +81,7 @@ struct vfssw vfssw[] = { { "objfs" }, /* OBJFS */ { "sharefs" }, /* SHAREFS */ { "dcfs" }, /* DCFS */ + { "smbfs" }, /* SMBFS */ { "" }, /* reserved for loadable fs */ { "" }, { "" }, @@ -94,7 +95,6 @@ struct vfssw vfssw[] = { { "" }, { "" }, { "" }, - { "" }, }; const int nfstype = (sizeof (vfssw) / sizeof (vfssw[0])); diff --git a/usr/src/uts/common/sys/fs/smbfs_mount.h b/usr/src/uts/common/sys/fs/smbfs_mount.h new file mode 100644 index 0000000000..6eeb8f909f --- /dev/null +++ b/usr/src/uts/common/sys/fs/smbfs_mount.h @@ -0,0 +1,109 @@ +/* + * 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. + * + * $Id: smbfs.h,v 1.30.100.1 2005/05/27 02:35:28 lindak Exp $ + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SMBFS_MOUNT_H +#define _SMBFS_MOUNT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This file defines the interface used by mount_smbfs. + * Some of this came from the Darwin file: + * smb-217.2/kernel/fs/smbfs/smbfs.h + */ + +#define SMBFS_VERMAJ 1 +#define SMBFS_VERMIN 3200 +#define SMBFS_VERSION (SMBFS_VERMAJ*100000 + SMBFS_VERMIN) +#define SMBFS_VER_STR "1.32" + +#define SMBFS_VFSNAME "smbfs" + +/* Values for flags */ +#define SMBFS_MOUNT_SOFT 0x0001 +#define SMBFS_MOUNT_INTR 0x0002 +#define SMBFS_MOUNT_STRONG 0x0004 +#define SMBFS_MOUNT_HAVE_NLS 0x0008 +#define SMBFS_MOUNT_NO_LONG 0x0010 +#define SMBFS_MOUNT_HOSTNAME 0x020 +#define SMBFS_MOUNT_SEMISOFT 0x200000 /* read soft, modify hard */ +#define SMBFS_MOUNT_NOPRINT 0x400000 /* don't print messages */ + +#define MNT_RDONLY 0x0001 +#define MNT_NODEV 0x0002 +#define MNT_NOEXEC 0x0004 +#define MNT_NOSUID 0x0008 +#define MNT_UNION 0x0010 +#define MNT_DONTBROWSE 0x0020 +#define MNT_AUTOMOUNTED 0x0040 + +/* Layout of the mount control block for an smb file system. */ +struct smbfs_args { + int version; /* smbfs mount version */ + int devfd; /* file descriptor */ + uint_t flags; /* mount options, eg: soft */ + mode_t file_mode; /* octal srwx for files */ + mode_t dir_mode; /* octal srwx for dirs */ + int caseopt; /* convert upper|lower|none */ + caddr_t addr; /* file server address */ + caddr_t hostname; /* server's hostname */ + caddr_t sharename; /* server's sharename */ + uid_t uid; /* octal user id */ + gid_t gid; /* octal group id */ +}; + +#ifdef _SYSCALL32 + +/* Layout of the mount control block for an smb file system. */ +struct smbfs_args32 { + int32_t version; /* smbfs mount version */ + int32_t devfd; /* file descriptor */ + uint_t flags; /* mount options, eg: soft */ + mode_t file_mode; /* octal srwx for files */ + mode_t dir_mode; /* octal srwx for dirs */ + int32_t caseopt; /* convert upper|lower|none */ + caddr32_t addr; /* file server address */ + caddr32_t hostname; /* server's hostname */ + caddr32_t sharename; /* server's sharename */ + uid32_t uid; /* octal user id */ + gid32_t gid; /* octal group id */ +}; + +#endif /* _SYSCALL32 */ +#endif /* _SMBFS_MOUNT_H */ diff --git a/usr/src/uts/common/sys/mntent.h b/usr/src/uts/common/sys/mntent.h index ee35c0561c..7340a0d4f2 100644 --- a/usr/src/uts/common/sys/mntent.h +++ b/usr/src/uts/common/sys/mntent.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T @@ -41,6 +41,7 @@ extern "C" { #define MNTTYPE_ZFS "zfs" /* ZFS file system */ #define MNTTYPE_UFS "ufs" /* Unix file system */ +#define MNTTYPE_SMBFS "smbfs" /* SMBFS file system */ #define MNTTYPE_NFS "nfs" /* NFS file system */ #define MNTTYPE_NFS3 "nfs3" /* NFS Version 3 file system */ #define MNTTYPE_NFS4 "nfs4" /* NFS Version 4 file system */ diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h index 6e7d1a169e..757ba3abc3 100644 --- a/usr/src/uts/common/sys/policy.h +++ b/usr/src/uts/common/sys/policy.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -130,6 +130,7 @@ int secpolicy_rsm_access(const cred_t *, uid_t, mode_t); int secpolicy_setpriority(const cred_t *); int secpolicy_settime(const cred_t *); int secpolicy_smb(const cred_t *); +int secpolicy_smbfs_login(const cred_t *, uid_t); int secpolicy_spec_open(const cred_t *, struct vnode *, int); int secpolicy_sti(const cred_t *); int secpolicy_swapctl(const cred_t *); diff --git a/usr/src/uts/intel/Makefile b/usr/src/uts/intel/Makefile index 231223883d..dbebc6fd66 100644 --- a/usr/src/uts/intel/Makefile +++ b/usr/src/uts/intel/Makefile @@ -20,7 +20,7 @@ # # uts/intel/Makefile # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -33,7 +33,9 @@ UTSBASE = .. include Makefile.intel -LINT_KMODLIBS = $(LINT_KMODS:e1000g=) +LINT_KMODS_X1 = $(LINT_KMODS:nsmb=) +LINT_KMODS_X2 = $(LINT_KMODS_X1:smbfs=) +LINT_KMODLIBS = $(LINT_KMODS_X2:e1000g=) LINT_LIBS = $(LINT_LIB) $(GEN_LINT_LIB) \ $(LINT_KMODLIBS:%=$(LINT_LIB_DIR)/llib-l%.ln) \ $(CLOSED_LINT_KMODS:%=$(LINT_LIB_DIR)/llib-l%.ln) diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index 6a3ab2e8b9..151efe1d3a 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -323,6 +323,7 @@ DRV_KMODS += wpi DRV_KMODS += xge DRV_KMODS += zcons DRV_KMODS += chxge +DRV_KMODS += nsmb # # Don't build some of these for OpenSolaris, since they will be @@ -467,6 +468,7 @@ SCHED_KMODS += IA RT TS RT_DPTBL TS_DPTBL FSS FX FX_DPTBL FS_KMODS += autofs cachefs ctfs dev devfs fdfs fifofs hsfs lofs FS_KMODS += lx_afs lx_proc mntfs namefs nfs objfs zfs FS_KMODS += pcfs procfs sockfs specfs tmpfs udfs ufs sharefs +FS_KMODS += smbfs # # Streams Modules (/kernel/strmod): diff --git a/usr/src/uts/intel/nsmb/Makefile b/usr/src/uts/intel/nsmb/Makefile new file mode 100644 index 0000000000..3b8d751ac2 --- /dev/null +++ b/usr/src/uts/intel/nsmb/Makefile @@ -0,0 +1,108 @@ +# +# 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 +# +# +# uts/intel/nsmb/Makefile +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# intel architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = nsmb +OBJECTS = $(NSMB_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(NSMB_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(USR_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/fs/smbclnt/netsmb +OFFSETS_SRC = $(CONF_SRCDIR)/offsets.in +IOC_CHECK_H = $(OBJS_DIR)/ioc_check.h + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# Overrides. +# +MODSTUBS_DIR = $(OBJS_DIR) +$(MODSTUBS_O) := AS_CPPFLAGS += -DNSMB_MODULE +CLEANFILES += $(MODSTUBS_O) $(IOC_CHECK_H) +C99MODE = $(C99_ENABLE) +CERRWARN += -erroff=E_STATEMENT_NOT_REACHED +INC_PATH += -I$(UTSBASE)/common/fs/smbclnt +LDFLAGS += -dy -Ncrypto/md4 -Ncrypto/md5 -Nmisc/tlimod + +LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTTAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Create ioc_check.h and compare with the saved +# ioc_check.ref to ensure 32/64-bit invariance. +# +$(OBJECTS) : $(IOC_CHECK_H) +$(IOC_CHECK_H): $(OFFSETS_SRC) + $(OFFSETS_CREATE) <$(OFFSETS_SRC) >$@.tmp + cmp -s ioc_check.ref $@.tmp && \ + mv -f $@.tmp $@ + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/nsmb/ioc_check.ref b/usr/src/uts/intel/nsmb/ioc_check.ref new file mode 100644 index 0000000000..ead69ec44f --- /dev/null +++ b/usr/src/uts/intel/nsmb/ioc_check.ref @@ -0,0 +1,92 @@ +#define SIZEOF_SOCKADDR_ANY 0x18 +#define SIZEOF_SOCKADDR_IN 0x10 +#define SIZEOF_SOCKADDR_NB 0x18 +#define SIZEOF_SMBIOC_OSSN 0x218 +#define IOC_SERVER 0x0 +#define IOC_LOCAL 0x18 +#define IOC_LOCALCS 0x30 +#define IOC_LOCALCS_INCR 0x1 +#define IOC_SERVERCS 0x40 +#define IOC_SERVERCS_INCR 0x1 +#define IOC_SRVNAME 0x50 +#define IOC_SRVNAME_INCR 0x1 +#define IOC_USER 0x60 +#define IOC_USER_INCR 0x1 +#define IOC_WORKGROUP 0xe1 +#define IOC_WORKGROUP_INCR 0x1 +#define IOC_SSN_PASSWD 0x162 +#define IOC_SSN_PASSWD_INCR 0x1 +#define IOC_SSN_OPT 0x1e4 +#define IOC_TIMEOUT 0x1e8 +#define IOC_RETRYCOUNT 0x1ec +#define IOC_SSN_OWNER 0x1f0 +#define IOC_SSN_GROUP 0x1f4 +#define IOC_SSN_MODE 0x1f8 +#define IOC_SSN_RIGHTS 0x1fc +#define IOC_INTOKLEN 0x200 +#define IOC_OUTTOKLEN 0x204 +#define _IOC_INTOK 0x208 +#define _IOC_OUTTOK 0x210 +#define SIZEOF_SMBIOC_OSHARE 0x120 +#define IOC_SHARE 0x0 +#define IOC_SHARE_INCR 0x1 +#define IOC_SH_PASSWD 0x81 +#define IOC_SH_PASSWD_INCR 0x1 +#define IOC_SH_OPT 0x104 +#define IOC_STYPE 0x108 +#define IOC_SH_OWNER 0x10c +#define IOC_SH_GROUP 0x110 +#define IOC_SH_MODE 0x114 +#define IOC_SH_RIGHTS 0x118 +#define SIZEOF_SMBIOC_RQ 0x30 +#define IOC_CMD 0x0 +#define IOC_TWC 0x1 +#define IOC_TBC 0x2 +#define IOC_RPBUFSZ 0x4 +#define IOC_RWC 0x9 +#define IOC_RBC 0xa +#define IOC_RQ_ERRCLASS 0xd +#define IOC_RQ_SERROR 0xe +#define IOC_RQ_ERROR 0x10 +#define _IOC_TWORDS 0x18 +#define _IOC_TBYTES 0x20 +#define _IOC_RPBUF 0x28 +#define SIZEOF_SMBIOC_T2RQ 0xc0 +#define IOC_SETUP 0x0 +#define IOC_SETUP_INCR 0x2 +#define IOC_SETUPCNT 0x8 +#define IOC_NAME 0xc +#define IOC_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 0x4 +#define IOC_FLAGS 0x8 +#define SIZEOF_SMBIOC_LOOKUP 0x340 +#define IOC_LOOK_LEVEL 0x0 +#define IOC_LOOK_FLAGS 0x4 +#define IOC_SH 0x8 +#define IOC_SSN 0x128 +#define SIZEOF_SMBIOC_RW 0x18 +#define IOC_FH 0x0 +#define IOC_CNT 0x4 +#define _IOC_OFFSET 0x8 +#define _IOC_BASE 0x10 +#define SIZEOF_SMBIOC_PK 0x304 +#define PK_DOM 0x4 +#define PK_DOM_INCR 0x1 +#define PK_USR 0x104 +#define PK_USR_INCR 0x1 +#define PK_PASS 0x204 +#define PK_PASS_INCR 0x1 diff --git a/usr/src/uts/intel/os/minor_perm b/usr/src/uts/intel/os/minor_perm index 58b741b30f..8261fb776f 100644 --- a/usr/src/uts/intel/os/minor_perm +++ b/usr/src/uts/intel/os/minor_perm @@ -166,3 +166,4 @@ dmfe:* 0666 root sys afe:* 0666 root sys mxfe:* 0666 root sys vscan:* 0640 root sys +nsmb:* 0666 root sys diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major index d9fc868ae5..efa9f0428f 100644 --- a/usr/src/uts/intel/os/name_to_major +++ b/usr/src/uts/intel/os/name_to_major @@ -148,3 +148,4 @@ vnic 252 pit_beep 253 vscan 254 softmac 255 +nsmb 256 diff --git a/usr/src/uts/intel/smbfs/Makefile b/usr/src/uts/intel/smbfs/Makefile new file mode 100644 index 0000000000..aff87ca8de --- /dev/null +++ b/usr/src/uts/intel/smbfs/Makefile @@ -0,0 +1,98 @@ +# +# 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 +# +# +# uts/intel/smbfs/Makefile +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the smbfs (Server +# message block file system) kernel module. +# +# intel architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = smbfs +OBJECTS = $(SMBFS_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(SMBFS_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(USR_FS_DIR)/$(MODULE) +ROOTLINK = $(USR_SYS_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) + +# +# Overrides. +# +MODSTUBS_DIR = $(OBJS_DIR) +$(MODSTUBS_O) := AS_CPPFLAGS += -DSMBFS_MODULE +CLEANFILES += $(MODSTUBS_O) +C99MODE = $(C99_ENABLE) +INC_PATH += -I$(UTSBASE)/common/fs/smbclnt +LDFLAGS += -dy -Ndrv/nsmb + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +$(ROOTLINK): $(USR_SYS_DIR) $(ROOTMODULE) + -$(RM) $@; ln $(ROOTMODULE) $@ + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/sparc/Makefile b/usr/src/uts/sparc/Makefile index 50ef3ecbbc..10154b6098 100644 --- a/usr/src/uts/sparc/Makefile +++ b/usr/src/uts/sparc/Makefile @@ -21,7 +21,7 @@ # #ident "%Z%%M% %I% %E% SMI" # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/sparc/Makefile @@ -35,9 +35,11 @@ UTSBASE = .. include Makefile.sparc -LINT_KMODLIBS = $(LINT_KMODS:e1000g=) -LINT_LIBS = $(LINT_LIB) $(GEN_LINT_LIB) \ - $(LINT_KMODLIBS:%=$(LINT_LIB_DIR)/llib-l%.ln) +LINT_KMODS_X1 = $(LINT_KMODS:nsmb=) +LINT_KMODS_X2 = $(LINT_KMODS_X1:smbfs=) +LINT_KMODLIBS = $(LINT_KMODS_X2:e1000g=) +LINT_LIBS = $(LINT_LIB) $(GEN_LINT_LIB) \ + $(LINT_KMODLIBS:%=$(LINT_LIB_DIR)/llib-l%.ln) $(CLOSED_BUILD)LINT_LIBS += $(CLOSED_LINT_KMODS:%=$(LINT_LIB_DIR)/llib-l%.ln) diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 4b7acadf5c..9ff8cc2020 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -236,6 +236,7 @@ DRV_KMODS += rds DRV_KMODS += chxge DRV_KMODS += smbsrv DRV_KMODS += vscan +DRV_KMODS += nsmb # # Don't build some of these for OpenSolaris, since they will be @@ -337,7 +338,7 @@ SCHED_KMODS += RT TS RT_DPTBL TS_DPTBL IA FSS FX FX_DPTBL # FS_KMODS += dev devfs fdfs fifofs hsfs lofs namefs nfs pcfs tmpfs zfs FS_KMODS += specfs udfs ufs autofs cachefs procfs sockfs mntfs -FS_KMODS += ctfs objfs sharefs dcfs +FS_KMODS += ctfs objfs sharefs dcfs smbfs # # Streams Modules (/kernel/strmod): diff --git a/usr/src/uts/sparc/nsmb/Makefile b/usr/src/uts/sparc/nsmb/Makefile new file mode 100644 index 0000000000..85c05e9fcb --- /dev/null +++ b/usr/src/uts/sparc/nsmb/Makefile @@ -0,0 +1,135 @@ +# +# 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 +# +# +# uts/sparc/nsmb/Makefile +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# sparc architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = nsmb +OBJECTS = $(NSMB_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(NSMB_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(USR_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/fs/smbclnt/netsmb +OFFSETS_SRC = $(CONF_SRCDIR)/offsets.in +IOC_CHECK_H = $(OBJS_DIR)/ioc_check.h + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(ALL_TARGET_$(OBJS_DIR)) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(INSTALL_TARGET_$(OBJS_DIR)) + +# +# Overrides. +# +# We need some unusual overrides here so we'll +# build ioc_check.h for both 32-bit/64-bit, +# but only build 64-bit binaries. +# + +# Build 32-bit also... +DEF_BUILDS = $(DEF_BUILDS64) $(DEF_BUILDS32) +ALL_BUILDS = $(ALL_BUILDS64) $(ALL_BUILDS32) +# ... but don't build any 32-bit objects +ALL_TARGET_debug32 = $(IOC_CHECK_H) +ALL_TARGET_debug64 = $(BINARY) $(SRC_CONFILE) +ALL_TARGET_obj32 = $(IOC_CHECK_H) +ALL_TARGET_obj64 = $(BINARY) $(SRC_CONFILE) +# ... and remove -xcg92 (not supported by cw) +CFLAGS_32= +# ... same deal for install targets +INSTALL_TARGET_debug32 = $(IOC_CHECK_H) +INSTALL_TARGET_debug64 = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET_obj32 = $(IOC_CHECK_H) +INSTALL_TARGET_obj64 = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# Now the normal overrides... +# +MODSTUBS_DIR = $(OBJS_DIR) +$(MODSTUBS_O) := AS_CPPFLAGS += -DNSMB_MODULE +CLEANFILES += $(MODSTUBS_O) $(IOC_CHECK_H) +C99MODE = $(C99_ENABLE) +CERRWARN += -erroff=E_STATEMENT_NOT_REACHED +INC_PATH += -I$(UTSBASE)/common/fs/smbclnt +LDFLAGS += -dy -Ncrypto/md4 -Ncrypto/md5 -Nmisc/tlimod + +LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTTAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Create ioc_check.h and compare with the saved +# ioc_check.ref to ensure 32/64-bit invariance. +# We don't need _ASM_INLINES for this, and it +# causes #error "port me" in 32-bit builds. +# +$(OBJECTS) : $(IOC_CHECK_H) +$(IOC_CHECK_H): $(OFFSETS_SRC) + $(OFFSETS_CREATE) -U_ASM_INLINES \ + <$(OFFSETS_SRC) >$@.tmp + cmp -s ioc_check.ref $@.tmp && \ + mv -f $@.tmp $@ + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/nsmb/inc.flg b/usr/src/uts/sparc/nsmb/inc.flg new file mode 100644 index 0000000000..91c4d47cff --- /dev/null +++ b/usr/src/uts/sparc/nsmb/inc.flg @@ -0,0 +1,40 @@ +# +# 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 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Find the CIFS sources. + +find_files "s.*.c" usr/src/uts/common/fs/smbclnt/netsmb + +# Find header files. + +find_files "s.*.h" \ + usr/src/uts/common/fs \ + usr/src/uts/common/netinet \ + usr/src/uts/common/vm \ + usr/src/uts/sparc/sys \ + usr/src/uts/sun/sys + diff --git a/usr/src/uts/sparc/nsmb/ioc_check.ref b/usr/src/uts/sparc/nsmb/ioc_check.ref new file mode 100644 index 0000000000..ead69ec44f --- /dev/null +++ b/usr/src/uts/sparc/nsmb/ioc_check.ref @@ -0,0 +1,92 @@ +#define SIZEOF_SOCKADDR_ANY 0x18 +#define SIZEOF_SOCKADDR_IN 0x10 +#define SIZEOF_SOCKADDR_NB 0x18 +#define SIZEOF_SMBIOC_OSSN 0x218 +#define IOC_SERVER 0x0 +#define IOC_LOCAL 0x18 +#define IOC_LOCALCS 0x30 +#define IOC_LOCALCS_INCR 0x1 +#define IOC_SERVERCS 0x40 +#define IOC_SERVERCS_INCR 0x1 +#define IOC_SRVNAME 0x50 +#define IOC_SRVNAME_INCR 0x1 +#define IOC_USER 0x60 +#define IOC_USER_INCR 0x1 +#define IOC_WORKGROUP 0xe1 +#define IOC_WORKGROUP_INCR 0x1 +#define IOC_SSN_PASSWD 0x162 +#define IOC_SSN_PASSWD_INCR 0x1 +#define IOC_SSN_OPT 0x1e4 +#define IOC_TIMEOUT 0x1e8 +#define IOC_RETRYCOUNT 0x1ec +#define IOC_SSN_OWNER 0x1f0 +#define IOC_SSN_GROUP 0x1f4 +#define IOC_SSN_MODE 0x1f8 +#define IOC_SSN_RIGHTS 0x1fc +#define IOC_INTOKLEN 0x200 +#define IOC_OUTTOKLEN 0x204 +#define _IOC_INTOK 0x208 +#define _IOC_OUTTOK 0x210 +#define SIZEOF_SMBIOC_OSHARE 0x120 +#define IOC_SHARE 0x0 +#define IOC_SHARE_INCR 0x1 +#define IOC_SH_PASSWD 0x81 +#define IOC_SH_PASSWD_INCR 0x1 +#define IOC_SH_OPT 0x104 +#define IOC_STYPE 0x108 +#define IOC_SH_OWNER 0x10c +#define IOC_SH_GROUP 0x110 +#define IOC_SH_MODE 0x114 +#define IOC_SH_RIGHTS 0x118 +#define SIZEOF_SMBIOC_RQ 0x30 +#define IOC_CMD 0x0 +#define IOC_TWC 0x1 +#define IOC_TBC 0x2 +#define IOC_RPBUFSZ 0x4 +#define IOC_RWC 0x9 +#define IOC_RBC 0xa +#define IOC_RQ_ERRCLASS 0xd +#define IOC_RQ_SERROR 0xe +#define IOC_RQ_ERROR 0x10 +#define _IOC_TWORDS 0x18 +#define _IOC_TBYTES 0x20 +#define _IOC_RPBUF 0x28 +#define SIZEOF_SMBIOC_T2RQ 0xc0 +#define IOC_SETUP 0x0 +#define IOC_SETUP_INCR 0x2 +#define IOC_SETUPCNT 0x8 +#define IOC_NAME 0xc +#define IOC_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 0x4 +#define IOC_FLAGS 0x8 +#define SIZEOF_SMBIOC_LOOKUP 0x340 +#define IOC_LOOK_LEVEL 0x0 +#define IOC_LOOK_FLAGS 0x4 +#define IOC_SH 0x8 +#define IOC_SSN 0x128 +#define SIZEOF_SMBIOC_RW 0x18 +#define IOC_FH 0x0 +#define IOC_CNT 0x4 +#define _IOC_OFFSET 0x8 +#define _IOC_BASE 0x10 +#define SIZEOF_SMBIOC_PK 0x304 +#define PK_DOM 0x4 +#define PK_DOM_INCR 0x1 +#define PK_USR 0x104 +#define PK_USR_INCR 0x1 +#define PK_PASS 0x204 +#define PK_PASS_INCR 0x1 diff --git a/usr/src/uts/sparc/os/minor_perm b/usr/src/uts/sparc/os/minor_perm index 6751d9069a..d56b37858a 100644 --- a/usr/src/uts/sparc/os/minor_perm +++ b/usr/src/uts/sparc/os/minor_perm @@ -180,3 +180,4 @@ rtls:* 0666 root sys vnic:* 0666 root sys physmem:* 0600 root sys sdp:sdp 0666 root sys +nsmb:* 0666 root sys diff --git a/usr/src/uts/sparc/os/name_to_major b/usr/src/uts/sparc/os/name_to_major index fc4efd835f..6e1339bf60 100644 --- a/usr/src/uts/sparc/os/name_to_major +++ b/usr/src/uts/sparc/os/name_to_major @@ -221,3 +221,4 @@ ds_pri 272 vnic 273 vscan 274 softmac 275 +nsmb 276 diff --git a/usr/src/uts/sparc/smbfs/Makefile b/usr/src/uts/sparc/smbfs/Makefile new file mode 100644 index 0000000000..55cea236b9 --- /dev/null +++ b/usr/src/uts/sparc/smbfs/Makefile @@ -0,0 +1,93 @@ +# +# 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 +# +# +# uts/sparc/smbfs/Makefile +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the smbfs (Server +# message block file system) kernel module. +# +# sparc architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = smbfs +OBJECTS = $(SMBFS_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(SMBFS_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(USR_FS_DIR)/$(MODULE) +ROOTLINK = $(USR_SYS_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) + +# +# Overrides. +# +MODSTUBS_DIR = $(OBJS_DIR) +$(MODSTUBS_O) := AS_CPPFLAGS += -DSMBFS_MODULE +CLEANFILES += $(MODSTUBS_O) +C99MODE = $(C99_ENABLE) +INC_PATH += -I$(UTSBASE)/common/fs/smbclnt +LDFLAGS += -dy -Ndrv/nsmb + +.KEEP_STATE: + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +$(ROOTLINK): $(USR_SYS_DIR) $(ROOTMODULE) + -$(RM) $@; ln $(ROOTMODULE) $@ + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/smbfs/inc.flg b/usr/src/uts/sparc/smbfs/inc.flg new file mode 100644 index 0000000000..12a251f511 --- /dev/null +++ b/usr/src/uts/sparc/smbfs/inc.flg @@ -0,0 +1,42 @@ +#!/bin/sh +# +# 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 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +# Find the CIFS sources. + +find_files "s.*.c" usr/src/uts/common/fs/smbclnt/smbfs + +# Find header files. + +find_files "s.*.h" \ + usr/src/uts/common/fs \ + usr/src/uts/common/netinet \ + usr/src/uts/common/rpc \ + usr/src/uts/common/sys \ + usr/src/uts/common/vm \ + usr/src/uts/sparc/sys \ + usr/src/uts/sun/sys |