1*91d632c8Sgwr /*
2*91d632c8Sgwr  * CDDL HEADER START
3*91d632c8Sgwr  *
4*91d632c8Sgwr  * The contents of this file are subject to the terms of the
5*91d632c8Sgwr  * Common Development and Distribution License (the "License").
6*91d632c8Sgwr  * You may not use this file except in compliance with the License.
7*91d632c8Sgwr  *
8*91d632c8Sgwr  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*91d632c8Sgwr  * or http://www.opensolaris.org/os/licensing.
10*91d632c8Sgwr  * See the License for the specific language governing permissions
11*91d632c8Sgwr  * and limitations under the License.
12*91d632c8Sgwr  *
13*91d632c8Sgwr  * When distributing Covered Code, include this CDDL HEADER in each
14*91d632c8Sgwr  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*91d632c8Sgwr  * If applicable, add the following below this CDDL HEADER, with the
16*91d632c8Sgwr  * fields enclosed by brackets "[]" replaced with your own identifying
17*91d632c8Sgwr  * information: Portions Copyright [yyyy] [name of copyright owner]
18*91d632c8Sgwr  *
19*91d632c8Sgwr  * CDDL HEADER END
20*91d632c8Sgwr  */
21*91d632c8Sgwr 
22*91d632c8Sgwr /*
23*91d632c8Sgwr  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24*91d632c8Sgwr  * Use is subject to license terms.
25*91d632c8Sgwr  */
26*91d632c8Sgwr 
27*91d632c8Sgwr #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*91d632c8Sgwr 
29*91d632c8Sgwr /*
30*91d632c8Sgwr  * Functions supporting Solaris Extended Attributes,
31*91d632c8Sgwr  * used to provide access to CIFS "named streams".
32*91d632c8Sgwr  */
33*91d632c8Sgwr 
34*91d632c8Sgwr #include <sys/systm.h>
35*91d632c8Sgwr #include <sys/cred.h>
36*91d632c8Sgwr #include <sys/vnode.h>
37*91d632c8Sgwr #include <sys/vfs.h>
38*91d632c8Sgwr #include <sys/filio.h>
39*91d632c8Sgwr #include <sys/uio.h>
40*91d632c8Sgwr #include <sys/dirent.h>
41*91d632c8Sgwr #include <sys/errno.h>
42*91d632c8Sgwr #include <sys/sysmacros.h>
43*91d632c8Sgwr #include <sys/kmem.h>
44*91d632c8Sgwr #include <sys/stat.h>
45*91d632c8Sgwr #include <sys/cmn_err.h>
46*91d632c8Sgwr #include <sys/dnlc.h>
47*91d632c8Sgwr #include <sys/vfs_opreg.h>
48*91d632c8Sgwr #include <sys/u8_textprep.h>
49*91d632c8Sgwr 
50*91d632c8Sgwr #include <netsmb/smb_osdep.h>
51*91d632c8Sgwr #include <netsmb/smb.h>
52*91d632c8Sgwr #include <netsmb/smb_conn.h>
53*91d632c8Sgwr #include <netsmb/smb_subr.h>
54*91d632c8Sgwr #include <netsmb/smb_rq.h>
55*91d632c8Sgwr 
56*91d632c8Sgwr #include <smbfs/smbfs.h>
57*91d632c8Sgwr #include <smbfs/smbfs_node.h>
58*91d632c8Sgwr #include <smbfs/smbfs_subr.h>
59*91d632c8Sgwr 
60*91d632c8Sgwr #include <fs/fs_subr.h>
61*91d632c8Sgwr 
62*91d632c8Sgwr /*
63*91d632c8Sgwr  * Solaris wants there to be a directory node to contain
64*91d632c8Sgwr  * all the extended attributes.  The SMB protocol does not
65*91d632c8Sgwr  * really support a directory here, and uses very different
66*91d632c8Sgwr  * operations to list attributes, etc. so we "fake up" an
67*91d632c8Sgwr  * smbnode here to represent the attributes directory.
68*91d632c8Sgwr  *
69*91d632c8Sgwr  * We need to give this (fake) directory a unique identity,
70*91d632c8Sgwr  * and since we're using the full remote pathname as the
71*91d632c8Sgwr  * unique identity of all nodes, the easiest thing to do
72*91d632c8Sgwr  * here is append a colon (:) to the given pathname.
73*91d632c8Sgwr  *
74*91d632c8Sgwr  * There are several places where smbfs_fullpath and its
75*91d632c8Sgwr  * callers must decide what separator to use when building
76*91d632c8Sgwr  * a remote path name, and the rule is now as follows:
77*91d632c8Sgwr  * 1: When no XATTR involved, use "\\" as the separator.
78*91d632c8Sgwr  * 2: Traversal into the (fake) XATTR dir adds one ":"
79*91d632c8Sgwr  * 3: Children of the XATTR dir add nothing (sep=0)
80*91d632c8Sgwr  * The result should be _one_ colon before the attr name.
81*91d632c8Sgwr  */
82*91d632c8Sgwr 
83*91d632c8Sgwr /* ARGSUSED */
84*91d632c8Sgwr int
85*91d632c8Sgwr smbfs_get_xattrdir(vnode_t *pvp, vnode_t **vpp, cred_t *cr, int flags)
86*91d632c8Sgwr {
87*91d632c8Sgwr 	vnode_t *xvp;
88*91d632c8Sgwr 	smbnode_t *pnp, *xnp;
89*91d632c8Sgwr 
90*91d632c8Sgwr 	pnp = VTOSMB(pvp);
91*91d632c8Sgwr 
92*91d632c8Sgwr 	xvp = smbfs_make_node(pvp->v_vfsp,
93*91d632c8Sgwr 	    pnp->n_rpath, pnp->n_rplen,
94*91d632c8Sgwr 	    NULL, 0, ':', NULL);
95*91d632c8Sgwr 	ASSERT(xvp);
96*91d632c8Sgwr 	/* Note: xvp has a VN_HOLD, which our caller expects. */
97*91d632c8Sgwr 	xnp = VTOSMB(xvp);
98*91d632c8Sgwr 
99*91d632c8Sgwr 	/* If it's a new node, initialize. */
100*91d632c8Sgwr 	if (xvp->v_type == VNON) {
101*91d632c8Sgwr 
102*91d632c8Sgwr 		mutex_enter(&xvp->v_lock);
103*91d632c8Sgwr 		xvp->v_type = VDIR;
104*91d632c8Sgwr 		xvp->v_flag |= V_XATTRDIR;
105*91d632c8Sgwr 		mutex_exit(&xvp->v_lock);
106*91d632c8Sgwr 
107*91d632c8Sgwr 		mutex_enter(&xnp->r_statelock);
108*91d632c8Sgwr 		xnp->n_flag |= N_XATTR;
109*91d632c8Sgwr 		mutex_exit(&xnp->r_statelock);
110*91d632c8Sgwr 	}
111*91d632c8Sgwr 
112*91d632c8Sgwr 	/* Success! */
113*91d632c8Sgwr 	*vpp = xvp;
114*91d632c8Sgwr 	return (0);
115*91d632c8Sgwr }
116*91d632c8Sgwr 
117*91d632c8Sgwr /*
118*91d632c8Sgwr  * Find the parent of an XATTR directory or file,
119*91d632c8Sgwr  * by trimming off the ":attrname" part of rpath.
120*91d632c8Sgwr  * Called on XATTR files to get the XATTR dir, and
121*91d632c8Sgwr  * called on the XATTR dir to get the real object
122*91d632c8Sgwr  * under which the (faked up) XATTR dir lives.
123*91d632c8Sgwr  */
124*91d632c8Sgwr int
125*91d632c8Sgwr smbfs_xa_parent(vnode_t *vp, vnode_t **vpp)
126*91d632c8Sgwr {
127*91d632c8Sgwr 	smbnode_t *np = VTOSMB(vp);
128*91d632c8Sgwr 	vnode_t *pvp;
129*91d632c8Sgwr 	int rplen;
130*91d632c8Sgwr 
131*91d632c8Sgwr 	if ((np->n_flag & N_XATTR) == 0)
132*91d632c8Sgwr 		return (EINVAL);
133*91d632c8Sgwr 
134*91d632c8Sgwr 	if (vp->v_flag & V_XATTRDIR) {
135*91d632c8Sgwr 		/*
136*91d632c8Sgwr 		 * Want the parent of the XATTR directory.
137*91d632c8Sgwr 		 * That's easy: just remove trailing ":"
138*91d632c8Sgwr 		 */
139*91d632c8Sgwr 		rplen = np->n_rplen - 1;
140*91d632c8Sgwr 		if (rplen < 1) {
141*91d632c8Sgwr 			SMBVDEBUG("rplen < 1?");
142*91d632c8Sgwr 			return (ENOENT);
143*91d632c8Sgwr 		}
144*91d632c8Sgwr 		if (np->n_rpath[rplen] != ':') {
145*91d632c8Sgwr 			SMBVDEBUG("last is not colon");
146*91d632c8Sgwr 			return (ENOENT);
147*91d632c8Sgwr 		}
148*91d632c8Sgwr 	} else {
149*91d632c8Sgwr 		/*
150*91d632c8Sgwr 		 * Want the XATTR directory given
151*91d632c8Sgwr 		 * one of its XATTR files (children).
152*91d632c8Sgwr 		 * Find the ":" and trim after it.
153*91d632c8Sgwr 		 */
154*91d632c8Sgwr 		for (rplen = 1; rplen < np->n_rplen; rplen++)
155*91d632c8Sgwr 			if (np->n_rpath[rplen] == ':')
156*91d632c8Sgwr 				break;
157*91d632c8Sgwr 		/* Should have found ":stream_name" */
158*91d632c8Sgwr 		if (rplen >= np->n_rplen) {
159*91d632c8Sgwr 			SMBVDEBUG("colon not found");
160*91d632c8Sgwr 			return (ENOENT);
161*91d632c8Sgwr 		}
162*91d632c8Sgwr 		rplen++; /* keep the ":" */
163*91d632c8Sgwr 		if (rplen >= np->n_rplen) {
164*91d632c8Sgwr 			SMBVDEBUG("no stream name");
165*91d632c8Sgwr 			return (ENOENT);
166*91d632c8Sgwr 		}
167*91d632c8Sgwr 	}
168*91d632c8Sgwr 
169*91d632c8Sgwr 	pvp = smbfs_make_node(vp->v_vfsp,
170*91d632c8Sgwr 	    np->n_rpath, rplen,
171*91d632c8Sgwr 	    NULL, 0, 0, NULL);
172*91d632c8Sgwr 	ASSERT(pvp);
173*91d632c8Sgwr 
174*91d632c8Sgwr 	/* Note: pvp has a VN_HOLD from _make_node */
175*91d632c8Sgwr 	*vpp = pvp;
176*91d632c8Sgwr 	return (0);
177*91d632c8Sgwr }
178*91d632c8Sgwr 
179*91d632c8Sgwr /*
180*91d632c8Sgwr  * This is called by smbfs_pathconf to find out
181*91d632c8Sgwr  * if some file has any extended attributes.
182*91d632c8Sgwr  * There's no short-cut way to find out, so we
183*91d632c8Sgwr  * just list the attributes the usual way and
184*91d632c8Sgwr  * check for an empty result.
185*91d632c8Sgwr  *
186*91d632c8Sgwr  * Returns 1: (exists) or 0: (none found)
187*91d632c8Sgwr  */
188*91d632c8Sgwr int
189*91d632c8Sgwr smbfs_xa_exists(vnode_t *vp, cred_t *cr)
190*91d632c8Sgwr {
191*91d632c8Sgwr 	smbnode_t *xnp;
192*91d632c8Sgwr 	vnode_t *xvp;
193*91d632c8Sgwr 	struct smb_cred scred;
194*91d632c8Sgwr 	struct smbfs_fctx ctx;
195*91d632c8Sgwr 	int error, rc = 0;
196*91d632c8Sgwr 
197*91d632c8Sgwr 	/* Get the xattr dir */
198*91d632c8Sgwr 	error = smbfs_get_xattrdir(vp, &xvp, cr, LOOKUP_XATTR);
199*91d632c8Sgwr 	if (error)
200*91d632c8Sgwr 		return (0);
201*91d632c8Sgwr 	/* NB: have VN_HOLD on xpv */
202*91d632c8Sgwr 	xnp = VTOSMB(xvp);
203*91d632c8Sgwr 
204*91d632c8Sgwr 	smb_credinit(&scred, curproc, cr);
205*91d632c8Sgwr 
206*91d632c8Sgwr 	bzero(&ctx, sizeof (ctx));
207*91d632c8Sgwr 	ctx.f_flags = SMBFS_RDD_FINDFIRST;
208*91d632c8Sgwr 	ctx.f_dnp = xnp;
209*91d632c8Sgwr 	ctx.f_scred = &scred;
210*91d632c8Sgwr 	ctx.f_ssp = xnp->n_mount->smi_share;
211*91d632c8Sgwr 
212*91d632c8Sgwr 	error = smbfs_xa_findopen(&ctx, xnp, "*", 1);
213*91d632c8Sgwr 	if (error)
214*91d632c8Sgwr 		goto out;
215*91d632c8Sgwr 
216*91d632c8Sgwr 	error = smbfs_xa_findnext(&ctx, 1);
217*91d632c8Sgwr 	if (error)
218*91d632c8Sgwr 		goto out;
219*91d632c8Sgwr 
220*91d632c8Sgwr 	/* Have at least one named stream. */
221*91d632c8Sgwr 	SMBVDEBUG("ctx.f_name: %s\n", ctx.f_name);
222*91d632c8Sgwr 	rc = 1;
223*91d632c8Sgwr 
224*91d632c8Sgwr out:
225*91d632c8Sgwr 	/* NB: Always call findclose, error or not. */
226*91d632c8Sgwr 	(void) smbfs_xa_findclose(&ctx);
227*91d632c8Sgwr 	smb_credrele(&scred);
228*91d632c8Sgwr 	VN_RELE(xvp);
229*91d632c8Sgwr 	return (rc);
230*91d632c8Sgwr }
231*91d632c8Sgwr 
232*91d632c8Sgwr 
233*91d632c8Sgwr /*
234*91d632c8Sgwr  * This is called to get attributes (size, etc.) of either
235*91d632c8Sgwr  * the "faked up" XATTR directory or a named stream.
236*91d632c8Sgwr  */
237*91d632c8Sgwr int
238*91d632c8Sgwr smbfs_xa_getfattr(struct smbnode *xnp, struct smbfattr *fap,
239*91d632c8Sgwr 	struct smb_cred *scrp)
240*91d632c8Sgwr {
241*91d632c8Sgwr 	vnode_t *xvp;	/* xattr */
242*91d632c8Sgwr 	vnode_t *pvp;	/* parent */
243*91d632c8Sgwr 	smbnode_t *pnp;	/* parent */
244*91d632c8Sgwr 	int error, nlen;
245*91d632c8Sgwr 	const char *name, *sname;
246*91d632c8Sgwr 
247*91d632c8Sgwr 	xvp = SMBTOV(xnp);
248*91d632c8Sgwr 
249*91d632c8Sgwr 	/*
250*91d632c8Sgwr 	 * Simulate smbfs_smb_getfattr() for a named stream.
251*91d632c8Sgwr 	 * OK to leave a,c,m times zero (expected w/ XATTR).
252*91d632c8Sgwr 	 * The XATTR directory is easy (all fake).
253*91d632c8Sgwr 	 */
254*91d632c8Sgwr 	if (xvp->v_flag & V_XATTRDIR) {
255*91d632c8Sgwr 		fap->fa_attr = SMB_FA_DIR;
256*91d632c8Sgwr 		fap->fa_size = DEV_BSIZE;
257*91d632c8Sgwr 		return (0);
258*91d632c8Sgwr 	}
259*91d632c8Sgwr 
260*91d632c8Sgwr 	/*
261*91d632c8Sgwr 	 * Do a lookup in the XATTR directory,
262*91d632c8Sgwr 	 * using the stream name (last part)
263*91d632c8Sgwr 	 * from the xattr node.
264*91d632c8Sgwr 	 */
265*91d632c8Sgwr 	error = smbfs_xa_parent(xvp, &pvp);
266*91d632c8Sgwr 	if (error)
267*91d632c8Sgwr 		return (error);
268*91d632c8Sgwr 	/* Note: pvp has a VN_HOLD */
269*91d632c8Sgwr 	pnp = VTOSMB(pvp);
270*91d632c8Sgwr 
271*91d632c8Sgwr 	/* Get stream name (ptr and length) */
272*91d632c8Sgwr 	ASSERT(xnp->n_rplen > pnp->n_rplen);
273*91d632c8Sgwr 	nlen = xnp->n_rplen - pnp->n_rplen;
274*91d632c8Sgwr 	name = xnp->n_rpath + pnp->n_rplen;
275*91d632c8Sgwr 	sname = name;
276*91d632c8Sgwr 
277*91d632c8Sgwr 	/* Note: this can allocate a new "name" */
278*91d632c8Sgwr 	error = smbfs_smb_lookup(pnp, &name, &nlen, fap, scrp);
279*91d632c8Sgwr 	if (error == 0 && name != sname)
280*91d632c8Sgwr 		smbfs_name_free(name, nlen);
281*91d632c8Sgwr 
282*91d632c8Sgwr 	VN_RELE(pvp);
283*91d632c8Sgwr 
284*91d632c8Sgwr 	return (error);
285*91d632c8Sgwr }
286*91d632c8Sgwr 
287*91d632c8Sgwr /*
288*91d632c8Sgwr  * Fetch the entire attribute list here in findopen.
289*91d632c8Sgwr  * Will parse the results in findnext.
290*91d632c8Sgwr  *
291*91d632c8Sgwr  * This is called on the XATTR directory, so we
292*91d632c8Sgwr  * have to get the (real) parent object first.
293*91d632c8Sgwr  */
294*91d632c8Sgwr /* ARGSUSED */
295*91d632c8Sgwr int
296*91d632c8Sgwr smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
297*91d632c8Sgwr 	const char *wildcard, int wclen)
298*91d632c8Sgwr {
299*91d632c8Sgwr 	vnode_t *pvp;	/* parent */
300*91d632c8Sgwr 	smbnode_t *pnp;
301*91d632c8Sgwr 	struct smb_t2rq *t2p;
302*91d632c8Sgwr 	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
303*91d632c8Sgwr 	struct mbchain *mbp;
304*91d632c8Sgwr 	int error;
305*91d632c8Sgwr 
306*91d632c8Sgwr 	ASSERT(dnp->n_flag & N_XATTR);
307*91d632c8Sgwr 
308*91d632c8Sgwr 	ctx->f_type = ft_XA;
309*91d632c8Sgwr 
310*91d632c8Sgwr 	error = smbfs_xa_parent(SMBTOV(dnp), &pvp);
311*91d632c8Sgwr 	if (error)
312*91d632c8Sgwr 		return (error);
313*91d632c8Sgwr 	ASSERT(pvp);
314*91d632c8Sgwr 	/* Note: pvp has a VN_HOLD */
315*91d632c8Sgwr 	pnp = VTOSMB(pvp);
316*91d632c8Sgwr 
317*91d632c8Sgwr 	if (ctx->f_t2) {
318*91d632c8Sgwr 		smb_t2_done(ctx->f_t2);
319*91d632c8Sgwr 		ctx->f_t2 = NULL;
320*91d632c8Sgwr 	}
321*91d632c8Sgwr 
322*91d632c8Sgwr 	error = smb_t2_alloc(SSTOCP(ctx->f_ssp),
323*91d632c8Sgwr 	    SMB_TRANS2_QUERY_PATH_INFORMATION,
324*91d632c8Sgwr 	    ctx->f_scred, &t2p);
325*91d632c8Sgwr 	if (error)
326*91d632c8Sgwr 		goto out;
327*91d632c8Sgwr 	ctx->f_t2 = t2p;
328*91d632c8Sgwr 
329*91d632c8Sgwr 	mbp = &t2p->t2_tparam;
330*91d632c8Sgwr 	mb_init(mbp);
331*91d632c8Sgwr 	mb_put_uint16le(mbp, SMB_QFILEINFO_STREAM_INFO);
332*91d632c8Sgwr 	mb_put_uint32le(mbp, 0);
333*91d632c8Sgwr 	error = smbfs_fullpath(mbp, vcp, pnp, NULL, NULL, 0);
334*91d632c8Sgwr 	if (error)
335*91d632c8Sgwr 		goto out;
336*91d632c8Sgwr 	t2p->t2_maxpcount = 2;
337*91d632c8Sgwr 	t2p->t2_maxdcount = INT16_MAX;
338*91d632c8Sgwr 	error = smb_t2_request(t2p);
339*91d632c8Sgwr 	if (error) {
340*91d632c8Sgwr 		if (smb_t2_err(t2p) == NT_STATUS_INVALID_PARAMETER)
341*91d632c8Sgwr 			error = ENOTSUP;
342*91d632c8Sgwr 	}
343*91d632c8Sgwr 	/*
344*91d632c8Sgwr 	 * No returned parameters to parse.
345*91d632c8Sgwr 	 * Returned data are in t2_rdata,
346*91d632c8Sgwr 	 * which we'll parse in _findnext.
347*91d632c8Sgwr 	 * However, save the wildcard.
348*91d632c8Sgwr 	 */
349*91d632c8Sgwr 	ctx->f_wildcard = wildcard;
350*91d632c8Sgwr 	ctx->f_wclen = wclen;
351*91d632c8Sgwr 
352*91d632c8Sgwr out:
353*91d632c8Sgwr 	VN_RELE(pvp);
354*91d632c8Sgwr 	return (error);
355*91d632c8Sgwr }
356*91d632c8Sgwr 
357*91d632c8Sgwr /*
358*91d632c8Sgwr  * Get the next name in an XATTR directory into f_name
359*91d632c8Sgwr  */
360*91d632c8Sgwr /* ARGSUSED */
361*91d632c8Sgwr int
362*91d632c8Sgwr smbfs_xa_findnext(struct smbfs_fctx *ctx, uint16_t limit)
363*91d632c8Sgwr {
364*91d632c8Sgwr 	struct mdchain *mdp;
365*91d632c8Sgwr 	struct smb_t2rq *t2p;
366*91d632c8Sgwr 	uint32_t size, next;
367*91d632c8Sgwr 	uint64_t llongint;
368*91d632c8Sgwr 	int error, skip, used, nmlen;
369*91d632c8Sgwr 
370*91d632c8Sgwr 	t2p = ctx->f_t2;
371*91d632c8Sgwr 	mdp = &t2p->t2_rdata;
372*91d632c8Sgwr 
373*91d632c8Sgwr 	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
374*91d632c8Sgwr 		ASSERT(ctx->f_wildcard);
375*91d632c8Sgwr 		SMBVDEBUG("wildcard: %s\n", ctx->f_wildcard);
376*91d632c8Sgwr 	}
377*91d632c8Sgwr 
378*91d632c8Sgwr again:
379*91d632c8Sgwr 	if (ctx->f_flags & SMBFS_RDD_EOF)
380*91d632c8Sgwr 		return (ENOENT);
381*91d632c8Sgwr 
382*91d632c8Sgwr 	/* Parse FILE_STREAM_INFORMATION */
383*91d632c8Sgwr 	if ((error = md_get_uint32le(mdp, &next)) != 0)	/* offset to */
384*91d632c8Sgwr 		return (ENOENT);
385*91d632c8Sgwr 	if ((error = md_get_uint32le(mdp, &size)) != 0) /* name len */
386*91d632c8Sgwr 		return (ENOENT);
387*91d632c8Sgwr 	md_get_uint64le(mdp, &llongint); /* file size */
388*91d632c8Sgwr 	ctx->f_attr.fa_size = llongint;
389*91d632c8Sgwr 	md_get_uint64le(mdp, NULL);	/* alloc. size */
390*91d632c8Sgwr 	used = 4 + 4 + 8 + 8;	/* how much we consumed */
391*91d632c8Sgwr 
392*91d632c8Sgwr 	/*
393*91d632c8Sgwr 	 * Copy the string, but skip the first char (":")
394*91d632c8Sgwr 	 * Watch out for zero-length strings here.
395*91d632c8Sgwr 	 */
396*91d632c8Sgwr 	if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) {
397*91d632c8Sgwr 		if (size >= 2) {
398*91d632c8Sgwr 			size -= 2; used += 2;
399*91d632c8Sgwr 			md_get_uint16le(mdp, NULL);
400*91d632c8Sgwr 		}
401*91d632c8Sgwr 		nmlen = min(size, SMB_MAXFNAMELEN * 2);
402*91d632c8Sgwr 	} else {
403*91d632c8Sgwr 		if (size >= 1) {
404*91d632c8Sgwr 			size -= 1; used += 1;
405*91d632c8Sgwr 			md_get_uint8(mdp, NULL);
406*91d632c8Sgwr 		}
407*91d632c8Sgwr 		nmlen = min(size, SMB_MAXFNAMELEN);
408*91d632c8Sgwr 	}
409*91d632c8Sgwr 
410*91d632c8Sgwr 	if (ctx->f_name)
411*91d632c8Sgwr 		kmem_free(ctx->f_name, ctx->f_namesz);
412*91d632c8Sgwr 	ctx->f_nmlen = nmlen;
413*91d632c8Sgwr 	/* Add one to prevent allocating size zero. */
414*91d632c8Sgwr 	ctx->f_namesz = nmlen + 1;
415*91d632c8Sgwr 	ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
416*91d632c8Sgwr 	error = md_get_mem(mdp, ctx->f_name, nmlen, MB_MSYSTEM);
417*91d632c8Sgwr 	if (error)
418*91d632c8Sgwr 		return (error);
419*91d632c8Sgwr 	used += nmlen;
420*91d632c8Sgwr 
421*91d632c8Sgwr 	/*
422*91d632c8Sgwr 	 * Convert UCS-2 to UTF-8
423*91d632c8Sgwr 	 */
424*91d632c8Sgwr 	smbfs_fname_tolocal(ctx);
425*91d632c8Sgwr 	if (nmlen)
426*91d632c8Sgwr 		SMBVDEBUG("name: %s\n", ctx->f_name);
427*91d632c8Sgwr 	else
428*91d632c8Sgwr 		SMBVDEBUG("null name!\n");
429*91d632c8Sgwr 
430*91d632c8Sgwr 	/*
431*91d632c8Sgwr 	 * Skip padding until next offset
432*91d632c8Sgwr 	 */
433*91d632c8Sgwr 	if (next > used) {
434*91d632c8Sgwr 		skip = next - used;
435*91d632c8Sgwr 		md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
436*91d632c8Sgwr 	}
437*91d632c8Sgwr 	if (next == 0)
438*91d632c8Sgwr 		ctx->f_flags |= SMBFS_RDD_EOF;
439*91d632c8Sgwr 
440*91d632c8Sgwr 	/*
441*91d632c8Sgwr 	 * Chop off the trailing ":$DATA"
442*91d632c8Sgwr 	 * The 6 here is strlen(":$DATA")
443*91d632c8Sgwr 	 */
444*91d632c8Sgwr 	if (ctx->f_nmlen >= 6) {
445*91d632c8Sgwr 		char *p = ctx->f_name + ctx->f_nmlen - 6;
446*91d632c8Sgwr 		if (strncmp(p, ":$DATA", 6) == 0) {
447*91d632c8Sgwr 			*p = '\0'; /* Chop! */
448*91d632c8Sgwr 			ctx->f_nmlen -= 6;
449*91d632c8Sgwr 		}
450*91d632c8Sgwr 	}
451*91d632c8Sgwr 
452*91d632c8Sgwr 	/*
453*91d632c8Sgwr 	 * The Chop above will typically leave
454*91d632c8Sgwr 	 * an empty name in the first slot,
455*91d632c8Sgwr 	 * which we will skip here.
456*91d632c8Sgwr 	 */
457*91d632c8Sgwr 	if (ctx->f_nmlen == 0)
458*91d632c8Sgwr 		goto again;
459*91d632c8Sgwr 
460*91d632c8Sgwr 	/*
461*91d632c8Sgwr 	 * If this is a lookup of a specific name,
462*91d632c8Sgwr 	 * skip past any non-matching names.
463*91d632c8Sgwr 	 */
464*91d632c8Sgwr 	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
465*91d632c8Sgwr 		if (ctx->f_wclen != ctx->f_nmlen)
466*91d632c8Sgwr 			goto again;
467*91d632c8Sgwr 		if (u8_strcmp(ctx->f_wildcard, ctx->f_name,
468*91d632c8Sgwr 		    ctx->f_nmlen, U8_STRCMP_CI_LOWER,
469*91d632c8Sgwr 		    U8_UNICODE_LATEST, &error) || error)
470*91d632c8Sgwr 			goto again;
471*91d632c8Sgwr 	}
472*91d632c8Sgwr 
473*91d632c8Sgwr 	return (0);
474*91d632c8Sgwr }
475*91d632c8Sgwr 
476*91d632c8Sgwr /*
477*91d632c8Sgwr  * Find first/next/close for XATTR directories.
478*91d632c8Sgwr  * NB: also used by smbfs_smb_lookup
479*91d632c8Sgwr  */
480*91d632c8Sgwr 
481*91d632c8Sgwr int
482*91d632c8Sgwr smbfs_xa_findclose(struct smbfs_fctx *ctx)
483*91d632c8Sgwr {
484*91d632c8Sgwr 
485*91d632c8Sgwr 	if (ctx->f_name)
486*91d632c8Sgwr 		kmem_free(ctx->f_name, ctx->f_namesz);
487*91d632c8Sgwr 	if (ctx->f_t2)
488*91d632c8Sgwr 		smb_t2_done(ctx->f_t2);
489*91d632c8Sgwr 
490*91d632c8Sgwr 	return (0);
491*91d632c8Sgwr }
492