191d632c8Sgwr /*
291d632c8Sgwr  * CDDL HEADER START
391d632c8Sgwr  *
491d632c8Sgwr  * The contents of this file are subject to the terms of the
591d632c8Sgwr  * Common Development and Distribution License (the "License").
691d632c8Sgwr  * You may not use this file except in compliance with the License.
791d632c8Sgwr  *
891d632c8Sgwr  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
991d632c8Sgwr  * or http://www.opensolaris.org/os/licensing.
1091d632c8Sgwr  * See the License for the specific language governing permissions
1191d632c8Sgwr  * and limitations under the License.
1291d632c8Sgwr  *
1391d632c8Sgwr  * When distributing Covered Code, include this CDDL HEADER in each
1491d632c8Sgwr  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1591d632c8Sgwr  * If applicable, add the following below this CDDL HEADER, with the
1691d632c8Sgwr  * fields enclosed by brackets "[]" replaced with your own identifying
1791d632c8Sgwr  * information: Portions Copyright [yyyy] [name of copyright owner]
1891d632c8Sgwr  *
1991d632c8Sgwr  * CDDL HEADER END
2091d632c8Sgwr  */
2191d632c8Sgwr 
2291d632c8Sgwr /*
23*613a2f6bSGordon Ross  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2491d632c8Sgwr  * Use is subject to license terms.
2591d632c8Sgwr  */
2691d632c8Sgwr 
2791d632c8Sgwr /*
2891d632c8Sgwr  * Functions supporting Solaris Extended Attributes,
2991d632c8Sgwr  * used to provide access to CIFS "named streams".
3091d632c8Sgwr  */
3191d632c8Sgwr 
3291d632c8Sgwr #include <sys/systm.h>
3391d632c8Sgwr #include <sys/cred.h>
3491d632c8Sgwr #include <sys/vnode.h>
3591d632c8Sgwr #include <sys/vfs.h>
3691d632c8Sgwr #include <sys/filio.h>
3791d632c8Sgwr #include <sys/uio.h>
3891d632c8Sgwr #include <sys/dirent.h>
3991d632c8Sgwr #include <sys/errno.h>
4091d632c8Sgwr #include <sys/sysmacros.h>
4191d632c8Sgwr #include <sys/kmem.h>
4291d632c8Sgwr #include <sys/stat.h>
4391d632c8Sgwr #include <sys/cmn_err.h>
4491d632c8Sgwr #include <sys/dnlc.h>
4591d632c8Sgwr #include <sys/u8_textprep.h>
4691d632c8Sgwr 
4791d632c8Sgwr #include <netsmb/smb_osdep.h>
4891d632c8Sgwr #include <netsmb/smb.h>
4991d632c8Sgwr #include <netsmb/smb_conn.h>
5091d632c8Sgwr #include <netsmb/smb_subr.h>
5191d632c8Sgwr #include <netsmb/smb_rq.h>
5291d632c8Sgwr 
5391d632c8Sgwr #include <smbfs/smbfs.h>
5491d632c8Sgwr #include <smbfs/smbfs_node.h>
5591d632c8Sgwr #include <smbfs/smbfs_subr.h>
5691d632c8Sgwr 
5791d632c8Sgwr #include <fs/fs_subr.h>
5891d632c8Sgwr 
5991d632c8Sgwr /*
6091d632c8Sgwr  * Solaris wants there to be a directory node to contain
6191d632c8Sgwr  * all the extended attributes.  The SMB protocol does not
6291d632c8Sgwr  * really support a directory here, and uses very different
6391d632c8Sgwr  * operations to list attributes, etc. so we "fake up" an
6491d632c8Sgwr  * smbnode here to represent the attributes directory.
6591d632c8Sgwr  *
6691d632c8Sgwr  * We need to give this (fake) directory a unique identity,
6791d632c8Sgwr  * and since we're using the full remote pathname as the
6891d632c8Sgwr  * unique identity of all nodes, the easiest thing to do
6991d632c8Sgwr  * here is append a colon (:) to the given pathname.
7091d632c8Sgwr  *
7191d632c8Sgwr  * There are several places where smbfs_fullpath and its
7291d632c8Sgwr  * callers must decide what separator to use when building
7391d632c8Sgwr  * a remote path name, and the rule is now as follows:
7491d632c8Sgwr  * 1: When no XATTR involved, use "\\" as the separator.
7591d632c8Sgwr  * 2: Traversal into the (fake) XATTR dir adds one ":"
7691d632c8Sgwr  * 3: Children of the XATTR dir add nothing (sep=0)
7791d632c8Sgwr  * The result should be _one_ colon before the attr name.
7891d632c8Sgwr  */
7991d632c8Sgwr 
8091d632c8Sgwr /* ARGSUSED */
8191d632c8Sgwr int
8291d632c8Sgwr smbfs_get_xattrdir(vnode_t *pvp, vnode_t **vpp, cred_t *cr, int flags)
8391d632c8Sgwr {
8491d632c8Sgwr 	vnode_t *xvp;
8591d632c8Sgwr 	smbnode_t *pnp, *xnp;
8691d632c8Sgwr 
8791d632c8Sgwr 	pnp = VTOSMB(pvp);
8891d632c8Sgwr 
8991d632c8Sgwr 	xvp = smbfs_make_node(pvp->v_vfsp,
9091d632c8Sgwr 	    pnp->n_rpath, pnp->n_rplen,
9191d632c8Sgwr 	    NULL, 0, ':', NULL);
9291d632c8Sgwr 	ASSERT(xvp);
9391d632c8Sgwr 	/* Note: xvp has a VN_HOLD, which our caller expects. */
9491d632c8Sgwr 	xnp = VTOSMB(xvp);
9591d632c8Sgwr 
9691d632c8Sgwr 	/* If it's a new node, initialize. */
9791d632c8Sgwr 	if (xvp->v_type == VNON) {
9891d632c8Sgwr 
9991d632c8Sgwr 		mutex_enter(&xvp->v_lock);
10091d632c8Sgwr 		xvp->v_type = VDIR;
10191d632c8Sgwr 		xvp->v_flag |= V_XATTRDIR;
10291d632c8Sgwr 		mutex_exit(&xvp->v_lock);
10391d632c8Sgwr 
10491d632c8Sgwr 		mutex_enter(&xnp->r_statelock);
10591d632c8Sgwr 		xnp->n_flag |= N_XATTR;
10691d632c8Sgwr 		mutex_exit(&xnp->r_statelock);
10791d632c8Sgwr 	}
10891d632c8Sgwr 
10991d632c8Sgwr 	/* Success! */
11091d632c8Sgwr 	*vpp = xvp;
11191d632c8Sgwr 	return (0);
11291d632c8Sgwr }
11391d632c8Sgwr 
11491d632c8Sgwr /*
11591d632c8Sgwr  * Find the parent of an XATTR directory or file,
11691d632c8Sgwr  * by trimming off the ":attrname" part of rpath.
11791d632c8Sgwr  * Called on XATTR files to get the XATTR dir, and
11891d632c8Sgwr  * called on the XATTR dir to get the real object
11991d632c8Sgwr  * under which the (faked up) XATTR dir lives.
12091d632c8Sgwr  */
12191d632c8Sgwr int
12291d632c8Sgwr smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
12391d632c8Sgwr {
12491d632c8Sgwr 	smbnode_t *np = VTOSMB(vp);
12591d632c8Sgwr 	vnode_t *pvp;
12691d632c8Sgwr 	int rplen;
12791d632c8Sgwr 
12891d632c8Sgwr 	if ((np->n_flag & N_XATTR) == 0)
12991d632c8Sgwr 		return (EINVAL);
13091d632c8Sgwr 
13191d632c8Sgwr 	if (vp->v_flag & V_XATTRDIR) {
13291d632c8Sgwr 		/*
13391d632c8Sgwr 		 * Want the parent of the XATTR directory.
13491d632c8Sgwr 		 * That's easy: just remove trailing ":"
13591d632c8Sgwr 		 */
13691d632c8Sgwr 		rplen = np->n_rplen - 1;
13791d632c8Sgwr 		if (rplen < 1) {
13891d632c8Sgwr 			SMBVDEBUG("rplen < 1?");
13991d632c8Sgwr 			return (ENOENT);
14091d632c8Sgwr 		}
14191d632c8Sgwr 		if (np->n_rpath[rplen] != ':') {
14291d632c8Sgwr 			SMBVDEBUG("last is not colon");
14391d632c8Sgwr 			return (ENOENT);
14491d632c8Sgwr 		}
14591d632c8Sgwr 	} else {
14691d632c8Sgwr 		/*
14791d632c8Sgwr 		 * Want the XATTR directory given
14891d632c8Sgwr 		 * one of its XATTR files (children).
14991d632c8Sgwr 		 * Find the ":" and trim after it.
15091d632c8Sgwr 		 */
15191d632c8Sgwr 		for (rplen = 1; rplen < np->n_rplen; rplen++)
15291d632c8Sgwr 			if (np->n_rpath[rplen] == ':')
15391d632c8Sgwr 				break;
15491d632c8Sgwr 		/* Should have found ":stream_name" */
15591d632c8Sgwr 		if (rplen >= np->n_rplen) {
15691d632c8Sgwr 			SMBVDEBUG("colon not found");
15791d632c8Sgwr 			return (ENOENT);
15891d632c8Sgwr 		}
15991d632c8Sgwr 		rplen++; /* keep the ":" */
16091d632c8Sgwr 		if (rplen >= np->n_rplen) {
16191d632c8Sgwr 			SMBVDEBUG("no stream name");
16291d632c8Sgwr 			return (ENOENT);
16391d632c8Sgwr 		}
16491d632c8Sgwr 	}
16591d632c8Sgwr 
16691d632c8Sgwr 	pvp = smbfs_make_node(vp->v_vfsp,
16791d632c8Sgwr 	    np->n_rpath, rplen,
16891d632c8Sgwr 	    NULL, 0, 0, NULL);
16991d632c8Sgwr 	ASSERT(pvp);
17091d632c8Sgwr 
17191d632c8Sgwr 	/* Note: pvp has a VN_HOLD from _make_node */
17291d632c8Sgwr 	*vpp = pvp;
17391d632c8Sgwr 	return (0);
17491d632c8Sgwr }
17591d632c8Sgwr 
17691d632c8Sgwr /*
17791d632c8Sgwr  * This is called by smbfs_pathconf to find out
17891d632c8Sgwr  * if some file has any extended attributes.
17991d632c8Sgwr  * There's no short-cut way to find out, so we
18091d632c8Sgwr  * just list the attributes the usual way and
18191d632c8Sgwr  * check for an empty result.
18291d632c8Sgwr  *
18391d632c8Sgwr  * Returns 1: (exists) or 0: (none found)
18491d632c8Sgwr  */
18591d632c8Sgwr int
18691d632c8Sgwr smbfs_xa_exists(vnode_t *vp, cred_t *cr)
18791d632c8Sgwr {
18891d632c8Sgwr 	smbnode_t *xnp;
18991d632c8Sgwr 	vnode_t *xvp;
19091d632c8Sgwr 	struct smb_cred scred;
19191d632c8Sgwr 	struct smbfs_fctx ctx;
19291d632c8Sgwr 	int error, rc = 0;
19391d632c8Sgwr 
19491d632c8Sgwr 	/* Get the xattr dir */
19591d632c8Sgwr 	error = smbfs_get_xattrdir(vp, &xvp, cr, LOOKUP_XATTR);
19691d632c8Sgwr 	if (error)
19791d632c8Sgwr 		return (0);
19891d632c8Sgwr 	/* NB: have VN_HOLD on xpv */
19991d632c8Sgwr 	xnp = VTOSMB(xvp);
20091d632c8Sgwr 
201*613a2f6bSGordon Ross 	smb_credinit(&scred, cr);
20291d632c8Sgwr 
20391d632c8Sgwr 	bzero(&ctx, sizeof (ctx));
20491d632c8Sgwr 	ctx.f_flags = SMBFS_RDD_FINDFIRST;
20591d632c8Sgwr 	ctx.f_dnp = xnp;
20691d632c8Sgwr 	ctx.f_scred = &scred;
20791d632c8Sgwr 	ctx.f_ssp = xnp->n_mount->smi_share;
20891d632c8Sgwr 
20991d632c8Sgwr 	error = smbfs_xa_findopen(&ctx, xnp, "*", 1);
21091d632c8Sgwr 	if (error)
21191d632c8Sgwr 		goto out;
21291d632c8Sgwr 
21391d632c8Sgwr 	error = smbfs_xa_findnext(&ctx, 1);
21491d632c8Sgwr 	if (error)
21591d632c8Sgwr 		goto out;
21691d632c8Sgwr 
21791d632c8Sgwr 	/* Have at least one named stream. */
21891d632c8Sgwr 	SMBVDEBUG("ctx.f_name: %s\n", ctx.f_name);
21991d632c8Sgwr 	rc = 1;
22091d632c8Sgwr 
22191d632c8Sgwr out:
22291d632c8Sgwr 	/* NB: Always call findclose, error or not. */
22391d632c8Sgwr 	(void) smbfs_xa_findclose(&ctx);
22491d632c8Sgwr 	smb_credrele(&scred);
22591d632c8Sgwr 	VN_RELE(xvp);
22691d632c8Sgwr 	return (rc);
22791d632c8Sgwr }
22891d632c8Sgwr 
22991d632c8Sgwr 
23091d632c8Sgwr /*
23191d632c8Sgwr  * This is called to get attributes (size, etc.) of either
23291d632c8Sgwr  * the "faked up" XATTR directory or a named stream.
23391d632c8Sgwr  */
23491d632c8Sgwr int
23591d632c8Sgwr smbfs_xa_getfattr(struct smbnode *xnp, struct smbfattr *fap,
23691d632c8Sgwr 	struct smb_cred *scrp)
23791d632c8Sgwr {
23891d632c8Sgwr 	vnode_t *xvp;	/* xattr */
23991d632c8Sgwr 	vnode_t *pvp;	/* parent */
24091d632c8Sgwr 	smbnode_t *pnp;	/* parent */
24191d632c8Sgwr 	int error, nlen;
24291d632c8Sgwr 	const char *name, *sname;
24391d632c8Sgwr 
24491d632c8Sgwr 	xvp = SMBTOV(xnp);
24591d632c8Sgwr 
24691d632c8Sgwr 	/*
24791d632c8Sgwr 	 * Simulate smbfs_smb_getfattr() for a named stream.
24891d632c8Sgwr 	 * OK to leave a,c,m times zero (expected w/ XATTR).
24991d632c8Sgwr 	 * The XATTR directory is easy (all fake).
25091d632c8Sgwr 	 */
25191d632c8Sgwr 	if (xvp->v_flag & V_XATTRDIR) {
25291d632c8Sgwr 		fap->fa_attr = SMB_FA_DIR;
25391d632c8Sgwr 		fap->fa_size = DEV_BSIZE;
25491d632c8Sgwr 		return (0);
25591d632c8Sgwr 	}
25691d632c8Sgwr 
25791d632c8Sgwr 	/*
25891d632c8Sgwr 	 * Do a lookup in the XATTR directory,
25991d632c8Sgwr 	 * using the stream name (last part)
26091d632c8Sgwr 	 * from the xattr node.
26191d632c8Sgwr 	 */
26291d632c8Sgwr 	error = smbfs_xa_parent(xvp, &pvp);
26391d632c8Sgwr 	if (error)
26491d632c8Sgwr 		return (error);
26591d632c8Sgwr 	/* Note: pvp has a VN_HOLD */
26691d632c8Sgwr 	pnp = VTOSMB(pvp);
26791d632c8Sgwr 
26891d632c8Sgwr 	/* Get stream name (ptr and length) */
26991d632c8Sgwr 	ASSERT(xnp->n_rplen > pnp->n_rplen);
27091d632c8Sgwr 	nlen = xnp->n_rplen - pnp->n_rplen;
27191d632c8Sgwr 	name = xnp->n_rpath + pnp->n_rplen;
27291d632c8Sgwr 	sname = name;
27391d632c8Sgwr 
27491d632c8Sgwr 	/* Note: this can allocate a new "name" */
27591d632c8Sgwr 	error = smbfs_smb_lookup(pnp, &name, &nlen, fap, scrp);
27691d632c8Sgwr 	if (error == 0 && name != sname)
27791d632c8Sgwr 		smbfs_name_free(name, nlen);
27891d632c8Sgwr 
27991d632c8Sgwr 	VN_RELE(pvp);
28091d632c8Sgwr 
28191d632c8Sgwr 	return (error);
28291d632c8Sgwr }
28391d632c8Sgwr 
28491d632c8Sgwr /*
28591d632c8Sgwr  * Fetch the entire attribute list here in findopen.
28691d632c8Sgwr  * Will parse the results in findnext.
28791d632c8Sgwr  *
28891d632c8Sgwr  * This is called on the XATTR directory, so we
28991d632c8Sgwr  * have to get the (real) parent object first.
29091d632c8Sgwr  */
29191d632c8Sgwr /* ARGSUSED */
29291d632c8Sgwr int
29391d632c8Sgwr smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
29491d632c8Sgwr 	const char *wildcard, int wclen)
29591d632c8Sgwr {
29691d632c8Sgwr 	vnode_t *pvp;	/* parent */
29791d632c8Sgwr 	smbnode_t *pnp;
29891d632c8Sgwr 	struct smb_t2rq *t2p;
29991d632c8Sgwr 	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
30091d632c8Sgwr 	struct mbchain *mbp;
30191d632c8Sgwr 	int error;
30291d632c8Sgwr 
30391d632c8Sgwr 	ASSERT(dnp->n_flag & N_XATTR);
30491d632c8Sgwr 
30591d632c8Sgwr 	ctx->f_type = ft_XA;
306*613a2f6bSGordon Ross 	ctx->f_namesz = SMB_MAXFNAMELEN + 1;
307*613a2f6bSGordon Ross 	if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp)))
308*613a2f6bSGordon Ross 		ctx->f_namesz *= 2;
309*613a2f6bSGordon Ross 	ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
31091d632c8Sgwr 
31191d632c8Sgwr 	error = smbfs_xa_parent(SMBTOV(dnp), &pvp);
31291d632c8Sgwr 	if (error)
31391d632c8Sgwr 		return (error);
31491d632c8Sgwr 	ASSERT(pvp);
31591d632c8Sgwr 	/* Note: pvp has a VN_HOLD */
31691d632c8Sgwr 	pnp = VTOSMB(pvp);
31791d632c8Sgwr 
31891d632c8Sgwr 	if (ctx->f_t2) {
31991d632c8Sgwr 		smb_t2_done(ctx->f_t2);
32091d632c8Sgwr 		ctx->f_t2 = NULL;
32191d632c8Sgwr 	}
32291d632c8Sgwr 
32391d632c8Sgwr 	error = smb_t2_alloc(SSTOCP(ctx->f_ssp),
32491d632c8Sgwr 	    SMB_TRANS2_QUERY_PATH_INFORMATION,
32591d632c8Sgwr 	    ctx->f_scred, &t2p);
32691d632c8Sgwr 	if (error)
32791d632c8Sgwr 		goto out;
32891d632c8Sgwr 	ctx->f_t2 = t2p;
32991d632c8Sgwr 
33091d632c8Sgwr 	mbp = &t2p->t2_tparam;
33191d632c8Sgwr 	mb_init(mbp);
33291d632c8Sgwr 	mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
33391d632c8Sgwr 	mb_put_uint32le(mbp, 0);
33491d632c8Sgwr 	error = smbfs_fullpath(mbp, vcp, pnp, NULL, NULL, 0);
33591d632c8Sgwr 	if (error)
33691d632c8Sgwr 		goto out;
33791d632c8Sgwr 	t2p->t2_maxpcount = 2;
33891d632c8Sgwr 	t2p->t2_maxdcount = INT16_MAX;
33991d632c8Sgwr 	error = smb_t2_request(t2p);
34091d632c8Sgwr 	if (error) {
34191d632c8Sgwr 		if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER)
34291d632c8Sgwr 			error = ENOTSUP;
34391d632c8Sgwr 	}
34491d632c8Sgwr 	/*
34591d632c8Sgwr 	 * No returned parameters to parse.
34691d632c8Sgwr 	 * Returned data are in t2_rdata,
34791d632c8Sgwr 	 * which we'll parse in _findnext.
34891d632c8Sgwr 	 * However, save the wildcard.
34991d632c8Sgwr 	 */
35091d632c8Sgwr 	ctx->f_wildcard = wildcard;
35191d632c8Sgwr 	ctx->f_wclen = wclen;
35291d632c8Sgwr 
35391d632c8Sgwr out:
35491d632c8Sgwr 	VN_RELE(pvp);
35591d632c8Sgwr 	return (error);
35691d632c8Sgwr }
35791d632c8Sgwr 
35891d632c8Sgwr /*
35991d632c8Sgwr  * Get the next name in an XATTR directory into f_name
36091d632c8Sgwr  */
36191d632c8Sgwr /* ARGSUSED */
36291d632c8Sgwr int
36391d632c8Sgwr smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit)
36491d632c8Sgwr {
36591d632c8Sgwr 	struct mdchain *mdp;
36691d632c8Sgwr 	struct smb_t2rq *t2p;
36791d632c8Sgwr 	uint32_t size, next;
36891d632c8Sgwr 	uint64_t llongint;
36991d632c8Sgwr 	int error, skip, used, nmlen;
37091d632c8Sgwr 
37191d632c8Sgwr 	t2p = ctx->f_t2;
37291d632c8Sgwr 	mdp = &t2p->t2_rdata;
37391d632c8Sgwr 
37491d632c8Sgwr 	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
37591d632c8Sgwr 		ASSERT(ctx->f_wildcard);
37691d632c8Sgwr 		SMBVDEBUG("wildcard: %s\n", ctx->f_wildcard);
37791d632c8Sgwr 	}
37891d632c8Sgwr 
37991d632c8Sgwr again:
38091d632c8Sgwr 	if (ctx->f_flags & SMBFS_RDD_EOF)
38191d632c8Sgwr 		return (ENOENT);
38291d632c8Sgwr 
38391d632c8Sgwr 	/* Parse FILE_STREAM_INFORMATION */
38491d632c8Sgwr 	if ((error = md_get_uint32le(mdp, &next)) != 0)	/* offset to */
38591d632c8Sgwr 		return (ENOENT);
38691d632c8Sgwr 	if ((error = md_get_uint32le(mdp, &size)) != 0) /* name len */
38791d632c8Sgwr 		return (ENOENT);
38891d632c8Sgwr 	md_get_uint64le(mdp, &llongint); /* file size */
38991d632c8Sgwr 	ctx->f_attr.fa_size = llongint;
39091d632c8Sgwr 	md_get_uint64le(mdp, NULL);	/* alloc. size */
39191d632c8Sgwr 	used = 4 + 4 + 8 + 8;	/* how much we consumed */
39291d632c8Sgwr 
39391d632c8Sgwr 	/*
39491d632c8Sgwr 	 * Copy the string, but skip the first char (":")
39591d632c8Sgwr 	 * Watch out for zero-length strings here.
39691d632c8Sgwr 	 */
39791d632c8Sgwr 	if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
39891d632c8Sgwr 		if (size >= 2) {
39991d632c8Sgwr 			size -= 2; used += 2;
40091d632c8Sgwr 			md_get_uint16le(mdp, NULL);
40191d632c8Sgwr 		}
40291d632c8Sgwr 		nmlen = min(size, SMB_MAXFNAMELEN * 2);
40391d632c8Sgwr 	} else {
40491d632c8Sgwr 		if (size >= 1) {
40591d632c8Sgwr 			size -= 1; used += 1;
40691d632c8Sgwr 			md_get_uint8(mdp, NULL);
40791d632c8Sgwr 		}
40891d632c8Sgwr 		nmlen = min(size, SMB_MAXFNAMELEN);
40991d632c8Sgwr 	}
41091d632c8Sgwr 
411*613a2f6bSGordon Ross 	ASSERT(nmlen < ctx->f_namesz);
41291d632c8Sgwr 	ctx->f_nmlen = nmlen;
41391d632c8Sgwr 	error = md_get_mem(mdp, ctx->f_name, nmlen, MB_MSYSTEM);
41491d632c8Sgwr 	if (error)
41591d632c8Sgwr 		return (error);
41691d632c8Sgwr 	used += nmlen;
41791d632c8Sgwr 
41891d632c8Sgwr 	/*
41991d632c8Sgwr 	 * Convert UCS-2 to UTF-8
42091d632c8Sgwr 	 */
42191d632c8Sgwr 	smbfs_fname_tolocal(ctx);
42291d632c8Sgwr 	if (nmlen)
42391d632c8Sgwr 		SMBVDEBUG("name: %s\n", ctx->f_name);
42491d632c8Sgwr 	else
42591d632c8Sgwr 		SMBVDEBUG("null name!\n");
42691d632c8Sgwr 
42791d632c8Sgwr 	/*
42891d632c8Sgwr 	 * Skip padding until next offset
42991d632c8Sgwr 	 */
43091d632c8Sgwr 	if (next > used) {
43191d632c8Sgwr 		skip = next - used;
43291d632c8Sgwr 		md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
43391d632c8Sgwr 	}
43491d632c8Sgwr 	if (next == 0)
43591d632c8Sgwr 		ctx->f_flags |= SMBFS_RDD_EOF;
43691d632c8Sgwr 
43791d632c8Sgwr 	/*
43891d632c8Sgwr 	 * Chop off the trailing ":$DATA"
43991d632c8Sgwr 	 * The 6 here is strlen(":$DATA")
44091d632c8Sgwr 	 */
44191d632c8Sgwr 	if (ctx->f_nmlen >= 6) {
44291d632c8Sgwr 		char *p = ctx->f_name + ctx->f_nmlen - 6;
44391d632c8Sgwr 		if (strncmp(p, ":$DATA", 6) == 0) {
44491d632c8Sgwr 			*p = '\0'; /* Chop! */
44591d632c8Sgwr 			ctx->f_nmlen -= 6;
44691d632c8Sgwr 		}
44791d632c8Sgwr 	}
44891d632c8Sgwr 
44991d632c8Sgwr 	/*
45091d632c8Sgwr 	 * The Chop above will typically leave
45191d632c8Sgwr 	 * an empty name in the first slot,
45291d632c8Sgwr 	 * which we will skip here.
45391d632c8Sgwr 	 */
45491d632c8Sgwr 	if (ctx->f_nmlen == 0)
45591d632c8Sgwr 		goto again;
45691d632c8Sgwr 
45791d632c8Sgwr 	/*
45891d632c8Sgwr 	 * If this is a lookup of a specific name,
45991d632c8Sgwr 	 * skip past any non-matching names.
46091d632c8Sgwr 	 */
46191d632c8Sgwr 	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
46291d632c8Sgwr 		if (ctx->f_wclen != ctx->f_nmlen)
46391d632c8Sgwr 			goto again;
46491d632c8Sgwr 		if (u8_strcmp(ctx->f_wildcard, ctx->f_name,
46591d632c8Sgwr 		    ctx->f_nmlen, U8_STRCMP_CI_LOWER,
46691d632c8Sgwr 		    U8_UNICODE_LATEST, &error) || error)
46791d632c8Sgwr 			goto again;
46891d632c8Sgwr 	}
46991d632c8Sgwr 
47091d632c8Sgwr 	return (0);
47191d632c8Sgwr }
47291d632c8Sgwr 
47391d632c8Sgwr /*
47491d632c8Sgwr  * Find first/next/close for XATTR directories.
47591d632c8Sgwr  * NB: also used by smbfs_smb_lookup
47691d632c8Sgwr  */
47791d632c8Sgwr 
47891d632c8Sgwr int
47991d632c8Sgwr smbfs_xa_findclose(struct smbfs_fctx *ctx)
48091d632c8Sgwr {
48191d632c8Sgwr 
48291d632c8Sgwr 	if (ctx->f_name)
48391d632c8Sgwr 		kmem_free(ctx->f_name, ctx->f_namesz);
48491d632c8Sgwr 	if (ctx->f_t2)
48591d632c8Sgwr 		smb_t2_done(ctx->f_t2);
48691d632c8Sgwr 
48791d632c8Sgwr 	return (0);
48891d632c8Sgwr }
489