/*
 * Copyright (c) 2000-2001 Boris Popov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Boris Popov.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/inttypes.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>

#include <netsmb/smb_osdep.h>

#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_rq.h>

#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>


/*
 * Todo: locking over-the-wire
 */
#if 0	// todo

int
smbfs_smb1_lockandx(struct smbnode *np, int op, uint32_t pid,
	offset_t start, uint64_t len, int largelock,
	struct smb_cred *scrp, uint32_t timeout)
{
	struct smb_share *ssp = np->n_mount->smi_share;
	struct smb_rq rq, *rqp = &rq;
	struct mbchain *mbp;
	uint8_t ltype = 0;
	int error;

	/* Shared lock for n_fid use below. */
	ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));

	/* After reconnect, n_fid is invalid */
	if (np->n_vcgenid != ssp->ss_vcgenid)
		return (ESTALE);

	if (op == SMB_LOCK_SHARED)
		ltype |= SMB_LOCKING_ANDX_SHARED_LOCK;
	/* XXX: if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES)? */
	if (largelock)
		ltype |= SMB_LOCKING_ANDX_LARGE_FILES;
	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scrp);
	if (error)
		return (error);
	smb_rq_getrequest(rqp, &mbp);
	smb_rq_wstart(rqp);
	mb_put_uint8(mbp, 0xff);	/* secondary command */
	mb_put_uint8(mbp, 0);		/* MBZ */
	mb_put_uint16le(mbp, 0);
	mb_put_uint16le(mbp, np->n_fid);
	mb_put_uint8(mbp, ltype);	/* locktype */
	mb_put_uint8(mbp, 0);		/* oplocklevel - 0 seems is NO_OPLOCK */
	mb_put_uint32le(mbp, timeout);	/* 0 nowait, -1 infinite wait */
	mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0);
	mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1);
	smb_rq_wend(rqp);
	smb_rq_bstart(rqp);
	mb_put_uint16le(mbp, pid);
	if (!largelock) {
		mb_put_uint32le(mbp, start);
		mb_put_uint32le(mbp, len);
	} else {
		mb_put_uint16le(mbp, 0); /* pad */
		mb_put_uint32le(mbp, start >> 32); /* OffsetHigh */
		mb_put_uint32le(mbp, start & 0xffffffff); /* OffsetLow */
		mb_put_uint32le(mbp, len >> 32); /* LengthHigh */
		mb_put_uint32le(mbp, len & 0xffffffff); /* LengthLow */
	}
	smb_rq_bend(rqp);
	/*
	 * Don't want to risk missing a successful
	 * unlock send or lock response, or we could
	 * lose track of an outstanding lock.
	 */
	if (op == SMB_LOCK_RELEASE)
		rqp->sr_flags |= SMBR_NOINTR_SEND;
	else
		rqp->sr_flags |= SMBR_NOINTR_RECV;

	error = smb_rq_simple(rqp);
	smb_rq_done(rqp);
	return (error);
}

#endif	// todo

/*
 * Common function for QueryFileInfo, QueryPathInfo.
 */
int
smbfs_smb1_trans2_query(struct smbnode *np,  uint16_t fid,
	struct smbfattr *fap, struct smb_cred *scrp)
{
	struct smb_share *ssp = np->n_mount->smi_share;
	struct smb_vc *vcp = SSTOVC(ssp);
	struct smb_t2rq *t2p;
	struct mbchain *mbp;
	struct mdchain *mdp;
	uint16_t cmd;
	uint16_t infolevel = SMB_QFILEINFO_ALL_INFO;
	int error;

	/*
	 * If we have a valid open FID, use it.
	 */
	if (fid != SMB_FID_UNUSED)
		cmd = SMB_TRANS2_QUERY_FILE_INFORMATION;
	else
		cmd = SMB_TRANS2_QUERY_PATH_INFORMATION;

	error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p);
	if (error)
		return (error);
	mbp = &t2p->t2_tparam;
	mb_init(mbp);

	if (cmd == SMB_TRANS2_QUERY_FILE_INFORMATION)
		mb_put_uint16le(mbp, fid);

	mb_put_uint16le(mbp, infolevel);

	if (cmd == SMB_TRANS2_QUERY_PATH_INFORMATION) {
		mb_put_uint32le(mbp, 0);
		/* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */
		error = smbfs_fullpath(mbp, vcp, np, NULL, 0, '\\');
		if (error)
			goto out;
	}

	t2p->t2_maxpcount = 2;
	t2p->t2_maxdcount = vcp->vc_txmax;
	error = smb_t2_request(t2p);
	if (error)
		goto out;

	/*
	 * Parse the SMB_QFILEINFO_ALL_INFO
	 */
	mdp = &t2p->t2_rdata;
	error = smbfs_decode_file_all_info(ssp, mdp, fap);

out:
	smb_t2_done(t2p);

	return (error);
}

/*
 * Get some FS information
 */
static int
smbfs_smb1_query_fs_info(struct smb_share *ssp, struct mdchain *info_mdp,
	uint16_t level, struct smb_cred *scrp)
{
	struct smb_t2rq *t2p;
	struct mbchain *mbp;
	struct mdchain *mdp;
	int error;

	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION,
	    scrp, &t2p);
	if (error)
		return (error);
	mbp = &t2p->t2_tparam;
	mb_init(mbp);
	mb_put_uint16le(mbp, level);
	t2p->t2_maxpcount = 4;
	t2p->t2_maxdcount = 1024;
	error = smb_t2_request(t2p);
	if (error)
		goto out;

	mdp = &t2p->t2_rdata;
	*info_mdp = *mdp;
	bzero(mdp, sizeof (*mdp));

out:
	smb_t2_done(t2p);
	return (error);
}

/*
 * Get FILE_FS_ATTRIBUTE_INFORMATION
 */
int
smbfs_smb1_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *fsa,
	struct smb_cred *scrp)
{
	struct mdchain info_mdc, *mdp = &info_mdc;
	int error;

	bzero(mdp, sizeof (*mdp));

	error = smbfs_smb1_query_fs_info(ssp, mdp,
	    SMB_QFS_ATTRIBUTE_INFO, scrp);
	if (error)
		goto out;
	error = smbfs_decode_fs_attr_info(ssp, mdp, fsa);

out:
	md_done(mdp);

	return (error);
}

/*
 * Get FileFsFullSizeInformation and
 * parse into *info
 */
int
smbfs_smb1_statfs(struct smb_share *ssp,
	struct smb_fs_size_info *info,
	struct smb_cred *scrp)
{
	struct mdchain info_mdc, *mdp = &info_mdc;
	struct smb_vc *vcp = SSTOVC(ssp);
	uint16_t level;
	int error;

	bzero(mdp, sizeof (*mdp));

	if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
		level = SMB_QFS_FULL_SIZE_INFORMATION;
	else
		level = SMB_QFS_SIZE_INFO;
	error = smbfs_smb1_query_fs_info(ssp, mdp, level, scrp);
	if (error)
		goto out;

	md_get_uint64le(mdp, &info->total_units);
	md_get_uint64le(mdp, &info->caller_avail);
	if (level == SMB_QFS_FULL_SIZE_INFORMATION)
		md_get_uint64le(mdp, &info->actual_avail);
	else
		info->actual_avail = info->caller_avail;

	md_get_uint32le(mdp, &info->sect_per_unit);
	error = md_get_uint32le(mdp, &info->bytes_per_sect);

out:
	md_done(mdp);

	return (error);
}

int
smbfs_smb1_flush(struct smb_share *ssp, uint16_t fid, struct smb_cred *scrp)
{
	struct smb_rq rq, *rqp = &rq;
	struct mbchain *mbp;
	int error;

	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_FLUSH, scrp);
	if (error)
		return (error);
	smb_rq_getrequest(rqp, &mbp);
	smb_rq_wstart(rqp);
	mb_put_uint16le(mbp, fid);
	smb_rq_wend(rqp);
	smb_rq_bstart(rqp);
	smb_rq_bend(rqp);
	error = smb_rq_simple(rqp);
	smb_rq_done(rqp);
	return (error);
}

/*
 * Set file info via an open handle.
 * Caller provides payload, info level.
 */
static int
smbfs_smb1_setinfo_file(struct smb_share *ssp, uint16_t fid,
	struct mbchain *info_mbp, uint16_t level, struct smb_cred *scrp)
{
	struct smb_t2rq *t2p = NULL;
	struct mbchain *mbp;
	uint16_t cmd = SMB_TRANS2_SET_FILE_INFORMATION;
	int error;

	ASSERT(fid != SMB_FID_UNUSED);

	error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p);
	if (error)
		return (error);
	mbp = &t2p->t2_tparam;
	mb_init(mbp);
	mb_put_uint16le(mbp, fid);
	mb_put_uint16le(mbp, level);
	mb_put_uint16le(mbp, 0); /* pad */

	/* put the payload */
	mbp = &t2p->t2_tdata;
	mb_init(mbp);
	error = mb_put_mbchain(mbp, info_mbp);
	if (error)
		goto out;

	t2p->t2_maxpcount = 2;
	t2p->t2_maxdcount = 0;
	error = smb_t2_request(t2p);

out:
	smb_t2_done(t2p);

	return (error);
}

int
smbfs_smb1_seteof(struct smb_share *ssp, uint16_t fid,
	uint64_t newsize, struct smb_cred *scrp)
{
	struct mbchain data_mb, *mbp = &data_mb;
	uint16_t level;
	int error;

	if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
		level = SMB_SFILEINFO_END_OF_FILE_INFORMATION;
	else
		level = SMB_SFILEINFO_END_OF_FILE_INFO;

	mb_init(mbp);
	error = mb_put_uint64le(mbp, newsize);
	if (error)
		goto out;
	error = smbfs_smb1_setinfo_file(ssp, fid, mbp, level, scrp);

out:
	mb_done(mbp);
	return (error);
}

int
smbfs_smb1_setdisp(struct smb_share *ssp, uint16_t fid,
	uint8_t newdisp, struct smb_cred *scrp)
{
	struct mbchain data_mb, *mbp = &data_mb;
	uint16_t level;
	int error;

	if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
		level = SMB_SFILEINFO_DISPOSITION_INFORMATION;
	else
		level = SMB_SFILEINFO_DISPOSITION_INFO;

	mb_init(mbp);
	error = mb_put_uint8(mbp, newdisp);
	if (error)
		goto out;
	error = smbfs_smb1_setinfo_file(ssp, fid, mbp, level, scrp);

out:
	mb_done(mbp);

	return (error);
}

/*
 * Set FileBasicInformation on an open handle
 * Caller builds the mbchain.
 * Always have a FID here.
 */
int
smbfs_smb1_setfattr(struct smb_share *ssp, uint16_t fid,
	struct mbchain *mbp, struct smb_cred *scrp)
{
	uint16_t level;
	int error;

	if (SSTOVC(ssp)->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)
		level = SMB_SFILEINFO_BASIC_INFORMATION;
	else
		level = SMB_SFILEINFO_BASIC_INFO;
	error = smbfs_smb1_setinfo_file(ssp, fid, mbp, level, scrp);

	return (error);
}

/*
 * On SMB1, the trans2 rename only allows a rename where the
 * source and target are in the same directory.  If you give
 * the server any separators, you get "status not supported".
 *
 * Why bother using this instead of smbfs_smb1_oldrename?
 * Because it works with an open file, and some servers don't
 * allow oldrename of a file that's currently open.  We call
 * this when deleting an open file in smbfsremove(), where
 * the rename is always in the same directory.
 */
/*ARGSUSED*/
int
smbfs_smb1_t2rename(struct smbnode *np,
	const char *tname, int tnlen,
	uint16_t fid, struct smb_cred *scrp)
{
	struct smb_share *ssp = np->n_mount->smi_share;
	struct mbchain data_mb, *mbp = &data_mb;
	struct smb_vc *vcp = SSTOVC(ssp);
	uint32_t *name_lenp;
	uint16_t level = SMB_SFILEINFO_RENAME_INFORMATION;
	int base, len;
	int error;

	mb_init(mbp);
	mb_put_uint32le(mbp, 0); /* don't overwrite */
	mb_put_uint32le(mbp, 0); /* obsolete target dir fid */
	name_lenp = mb_reserve(mbp, 4);	/* name len */

	/* New name */
	base = mbp->mb_count;
	error = smb_put_dmem(mbp, vcp, tname, tnlen, SMB_CS_NONE, NULL);
	if (error)
		goto out;
	len = mbp->mb_count - base;
	*name_lenp = htolel(len);

	error = smbfs_smb1_setinfo_file(ssp, fid, mbp, level, scrp);

out:
	mb_done(mbp);
	return (error);
}

/*
 * Do an SMB1 (old style) rename using a full dest. path.
 * This is used when renaming to a different directory,
 * because the (preferred) t2rename can't do that.
 */
int
smbfs_smb1_oldrename(struct smbnode *src, struct smbnode *tdnp,
    const char *tname, int tnmlen, struct smb_cred *scrp)
{
	struct smb_rq rq, *rqp = &rq;
	struct smb_share *ssp = src->n_mount->smi_share;
	struct mbchain *mbp;
	int error;
	uint16_t fa;
	char sep;

	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scrp);
	if (error)
		return (error);
	smb_rq_getrequest(rqp, &mbp);
	smb_rq_wstart(rqp);
	/* freebsd bug: Let directories be renamed - Win98 requires DIR bit */
	fa = (SMBTOV(src)->v_type == VDIR) ? SMB_FA_DIR : 0;
	fa |= SMB_FA_SYSTEM | SMB_FA_HIDDEN;
	mb_put_uint16le(mbp, fa);
	smb_rq_wend(rqp);
	smb_rq_bstart(rqp);

	/*
	 * When we're not adding any component name, the
	 * passed sep is ignored, so just pass sep=0.
	 */
	mb_put_uint8(mbp, SMB_DT_ASCII);
	error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0, 0);
	if (error)
		goto out;

	/*
	 * After XATTR directories, separator is ":"
	 */
	sep = (src->n_flag & N_XATTR) ? ':' : '\\';
	mb_put_uint8(mbp, SMB_DT_ASCII);
	error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen, sep);
	if (error)
		goto out;

	smb_rq_bend(rqp);
	error = smb_rq_simple(rqp);
out:
	smb_rq_done(rqp);
	return (error);
}


/*
 * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect
 */
static int
smbfs_smb1_trans2find2(struct smbfs_fctx *ctx)
{
	struct smb_t2rq *t2p;
	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
	struct mbchain *mbp;
	struct mdchain *mdp;
	uint16_t ecnt, eos, lno, flags;
	uint16_t amask, limit;
	int error;

	/* smbfs_smb_findnextLM2 sets this */
	limit = ctx->f_limit;
	amask = (uint16_t)ctx->f_attrmask;

	if (ctx->f_t2) {
		smb_t2_done(ctx->f_t2);
		ctx->f_t2 = NULL;
	}
	flags = FIND2_RETURN_RESUME_KEYS | FIND2_CLOSE_ON_EOS;
	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
		flags |= FIND2_CLOSE_AFTER_REQUEST;
		ctx->f_flags |= SMBFS_RDD_NOCLOSE;
	}
	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
		error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2,
		    ctx->f_scred, &t2p);
		if (error)
			return (error);
		ctx->f_t2 = t2p;
		mbp = &t2p->t2_tparam;
		mb_init(mbp);
		mb_put_uint16le(mbp, amask);
		mb_put_uint16le(mbp, limit);
		mb_put_uint16le(mbp, flags);
		mb_put_uint16le(mbp, ctx->f_infolevel);
		mb_put_uint32le(mbp, 0);
		error = smbfs_fullpath(mbp, vcp, ctx->f_dnp,
		    ctx->f_wildcard, ctx->f_wclen, '\\');
		if (error)
			return (error);
	} else	{
		error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2,
		    ctx->f_scred, &t2p);
		if (error)
			return (error);
		ctx->f_t2 = t2p;
		mbp = &t2p->t2_tparam;
		mb_init(mbp);
		mb_put_uint16le(mbp, ctx->f_Sid);
		mb_put_uint16le(mbp, limit);
		mb_put_uint16le(mbp, ctx->f_infolevel);
		/* Send whatever resume key we received... */
		mb_put_uint32le(mbp, ctx->f_rkey);
		mb_put_uint16le(mbp, flags);
		/* ... and the resume name if we have one. */
		if (ctx->f_rname) {
			/* resume file name */
			mb_put_mem(mbp, ctx->f_rname, ctx->f_rnamelen,
			    MB_MSYSTEM);
		}
		/* Add trailing null - 1 byte if ASCII, 2 if Unicode */
		if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
			mb_put_uint8(mbp, 0);	/* 1st byte NULL Unicode char */
		mb_put_uint8(mbp, 0);
	}
	t2p->t2_maxpcount = 5 * 2;
	t2p->t2_maxdcount = 0xF000;	/* 64K less some overhead */
	error = smb_t2_request(t2p);
	if (error)
		return (error);

	/*
	 * This is the "resume name" we just sent.
	 * We want the new one (if any) that may be
	 * found in the response we just received and
	 * will now begin parsing.  Free the old one
	 * now so we'll know if we found a new one.
	 */
	if (ctx->f_rname) {
		kmem_free(ctx->f_rname, ctx->f_rnamelen);
		ctx->f_rname = NULL;
		ctx->f_rnamelen = 0;
	}

	mdp = &t2p->t2_rparam;
	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
		if ((error = md_get_uint16le(mdp, &ctx->f_Sid)) != 0)
			goto nodata;
		ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
	}
	md_get_uint16le(mdp, &ecnt);		/* entry count */
	md_get_uint16le(mdp, &eos);		/* end of search */
	md_get_uint16le(mdp, NULL);		/* EA err. off. */
	error = md_get_uint16le(mdp, &lno);	/* last name off. */
	if (error != 0)
		goto nodata;

	/*
	 * The "end of search" flag from an XP server sometimes
	 * comes back zero when the prior find_next returned exactly
	 * the number of entries requested.  in which case we'd try again
	 * but the search has in fact been closed so an EBADF results.
	 * our circumvention is to check here for a zero entry count.
	 */
	ctx->f_ecnt = ecnt;
	if (eos || ctx->f_ecnt == 0)
		ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
	if (ctx->f_ecnt == 0)
		return (ENOENT);

	/* Last Name Off (LNO) is the entry with the resume name. */
	ctx->f_rnameofs = lno;
	ctx->f_eofs = 0;

	/*
	 * Have data. Put the payload in ctx->f_mdchain
	 * Note struct assignments here.
	 */
	mdp = &t2p->t2_rdata;
	md_done(&ctx->f_mdchain);
	ctx->f_mdchain = *mdp;
	ctx->f_left = m_fixhdr(mdp->md_top);
	bzero(mdp, sizeof (*mdp));

	return (0);

nodata:
	/*
	 * Failed parsing the FindFirst or FindNext response.
	 * Force this directory listing closed, otherwise the
	 * calling process may hang in an infinite loop.
	 */
	ctx->f_ecnt = 0; /* Force closed. */
	ctx->f_flags |= SMBFS_RDD_EOF;
	return (EIO);
}

static int
smbfs_smb1_findclose2(struct smbfs_fctx *ctx)
{
	struct smb_rq rq, *rqp = &rq;
	struct mbchain *mbp;
	int error;

	error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2,
	    ctx->f_scred);
	if (error)
		return (error);
	smb_rq_getrequest(rqp, &mbp);
	smb_rq_wstart(rqp);
	mb_put_uint16le(mbp, ctx->f_Sid);
	smb_rq_wend(rqp);
	smb_rq_bstart(rqp);
	smb_rq_bend(rqp);
	/* Ditto comments at _smb_close */
	rqp->sr_flags |= SMBR_NOINTR_SEND;
	error = smb_rq_simple(rqp);
	smb_rq_done(rqp);
	return (error);
}

/*ARGSUSED*/
int
smbfs_smb_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp,
    const char *wildcard, int wclen, uint32_t attr)
{

	ctx->f_type = ft_LM2;
	ctx->f_namesz = SMB_MAXFNAMELEN + 1;
	ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
	ctx->f_infolevel = SMB_FIND_FULL_DIRECTORY_INFO;
	ctx->f_attrmask = attr;
	ctx->f_wildcard = wildcard;
	ctx->f_wclen = wclen;
	return (0);
}

int
smbfs_smb_findcloseLM2(struct smbfs_fctx *ctx)
{
	int error = 0;
	if (ctx->f_name)
		kmem_free(ctx->f_name, ctx->f_namesz);
	if (ctx->f_t2)
		smb_t2_done(ctx->f_t2);
	md_done(&ctx->f_mdchain);

	/*
	 * If SMBFS_RDD_FINDFIRST is still set, we were opened
	 * but never saw a findfirst, so we don't have any
	 * search handle to close.
	 */
	if ((ctx->f_flags & (SMBFS_RDD_FINDFIRST | SMBFS_RDD_NOCLOSE)) == 0)
		error = smbfs_smb1_findclose2(ctx);
	return (error);
}

/*
 * Get a buffer of directory entries (if we don't already have
 * some remaining in the current buffer) then decode one.
 */
int
smbfs_smb_findnextLM2(struct smbfs_fctx *ctx, uint16_t limit)
{
	int error;

	/*
	 * If we've scanned to the end of the current buffer
	 * try to read anohther buffer of dir entries.
	 * Treat anything less than 8 bytes as an "empty"
	 * buffer to ensure we can read something.
	 * (There may be up to 8 bytes of padding.)
	 */
	if ((ctx->f_eofs + 8) > ctx->f_left) {
		/* Scanned the whole buffer. */
		if (ctx->f_flags & SMBFS_RDD_EOF)
			return (ENOENT);
		ctx->f_limit = limit;
		error = smbfs_smb1_trans2find2(ctx);
		if (error)
			return (error);
		ctx->f_otws++;
	}

	/*
	 * Decode one entry, advance f_eofs
	 */
	error = smbfs_decode_dirent(ctx);

	return (error);
}

/*
 * Helper for smbfs_xa_get_streaminfo
 * Query stream info
 */
int
smbfs_smb1_get_streaminfo(smbnode_t *np, struct mdchain *mdp,
	struct smb_cred *scrp)
{
	smb_share_t *ssp = np->n_mount->smi_share;
	struct smb_vc *vcp = SSTOVC(ssp);
	struct smb_t2rq *t2p = NULL;
	struct mbchain *mbp;
	mblk_t *m;
	uint16_t cmd = SMB_TRANS2_QUERY_PATH_INFORMATION;
	int error;

	error = smb_t2_alloc(SSTOCP(ssp), cmd, scrp, &t2p);
	if (error)
		return (error);

	mbp = &t2p->t2_tparam;
	(void) mb_init(mbp);
	(void) mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
	(void) mb_put_uint32le(mbp, 0);
	error = smbfs_fullpath(mbp, vcp, np, NULL, NULL, 0);
	if (error)
		goto out;

	t2p->t2_maxpcount = 2;
	t2p->t2_maxdcount = INT16_MAX;
	error = smb_t2_request(t2p);
	if (error) {
		if (t2p->t2_sr_error == NT_STATUS_INVALID_PARAMETER)
			error = ENOTSUP;
		goto out;
	}

	/*
	 * Have data.  Move it to *mdp
	 */
	m = t2p->t2_rdata.md_top;
	if (m == NULL) {
		error = EBADRPC;
		goto out;
	}
	t2p->t2_rdata.md_top = NULL;
	md_initm(mdp, m);

out:
	smb_t2_done(t2p);
	return (error);
}

/*
 * OTW function to Get a security descriptor (SD).
 *
 * The *reslen param is bufsize(in) / length(out)
 * Note: On success, this fills in mdp->md_top,
 * which the caller should free.
 */
int
smbfs_smb1_getsec(struct smb_share *ssp, uint16_t fid,
	uint32_t selector, mblk_t **res, uint32_t *reslen,
	struct smb_cred *scrp)
{
	struct smb_ntrq *ntp;
	struct mbchain *mbp;
	struct mdchain *mdp;
	uint32_t dlen;
	int error;

	*res = NULL;

	error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_QUERY_SECURITY_DESC,
	    scrp, &ntp);
	if (error)
		return (error);

	/* Parameters part */
	mbp = &ntp->nt_tparam;
	mb_init(mbp);
	mb_put_uint16le(mbp, fid);
	mb_put_uint16le(mbp, 0); /* reserved */
	mb_put_uint32le(mbp, selector);
	/* Data part (none) */

	/* Max. returned parameters and data. */
	ntp->nt_maxpcount = 4;
	ntp->nt_maxdcount = *reslen;	// out buf size

	error = smb_nt_request(ntp);
	if (error && !(ntp->nt_flags & SMBT2_MOREDATA))
		goto done;

	/* Get data len */
	mdp = &ntp->nt_rparam;
	error = md_get_uint32le(mdp, &dlen);
	if (error)
		goto done;

	/*
	 * if there's more data than we said we could receive,
	 * here is where we pick up the length of it
	 */
	*reslen = dlen;
	if (dlen == 0) {
		error = EBADRPC;
		goto done;
	}

	/*
	 * get the SD data part.
	 */
	mdp = &ntp->nt_rdata;
	error = md_get_mbuf(mdp, dlen, res);

done:
	if (error == 0 && *res == NULL) {
		ASSERT(*res);
		error = EBADRPC;
	}

	smb_nt_done(ntp);
	return (error);
}


/*
 * OTW function to Set a security descriptor (SD).
 * Caller data are carried in an mbchain_t.
 *
 * Note: This normally consumes mbp->mb_top, and clears
 * that pointer when it does.
 */
int
smbfs_smb1_setsec(struct smb_share *ssp, uint16_t fid,
	uint32_t selector, mblk_t **mp, struct smb_cred *scrp)
{
	struct smb_ntrq *ntp;
	struct mbchain *mbp;
	int error;

	error = smb_nt_alloc(SSTOCP(ssp), NT_TRANSACT_SET_SECURITY_DESC,
	    scrp, &ntp);
	if (error)
		return (error);

	/* Parameters part */
	mbp = &ntp->nt_tparam;
	mb_init(mbp);
	mb_put_uint16le(mbp, fid);
	mb_put_uint16le(mbp, 0); /* reserved */
	mb_put_uint32le(mbp, selector);

	/* Data part */
	mbp = &ntp->nt_tdata;
	mb_initm(mbp, *mp);
	*mp = NULL; /* consumed */

	/* No returned parameters or data. */
	ntp->nt_maxpcount = 0;
	ntp->nt_maxdcount = 0;

	error = smb_nt_request(ntp);
	smb_nt_done(ntp);

	return (error);
}