xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb2.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
1 /*
2  * Copyright (c) 2000-2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
35  * Use is subject to license terms.
36  *
37  * Copyright (c) 2011 - 2013 Apple Inc. All rights reserved.
38  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
39  */
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/inttypes.h>
44 #include <sys/time.h>
45 #include <sys/vnode.h>
46 #include <sys/sunddi.h>
47 #include <sys/cmn_err.h>
48 
49 #include <netsmb/smb_osdep.h>
50 
51 #include <netsmb/smb.h>
52 #include <netsmb/smb2.h>
53 #include <netsmb/smb_conn.h>
54 #include <netsmb/smb_subr.h>
55 #include <netsmb/smb_rq.h>
56 #include <netsmb/smb2_rq.h>
57 
58 #include <smbfs/smbfs.h>
59 #include <smbfs/smbfs_node.h>
60 #include <smbfs/smbfs_subr.h>
61 
62 
63 /*
64  * Todo: locking over-the-wire
65  */
66 #if 0	// todo
67 
68 int
69 smbfs_smb2_locking(struct smbnode *np, int op, uint32_t pid,
70 	offset_t start, uint64_t len, int largelock,
71 	struct smb_cred *scrp, uint32_t timeout)
72 {
73 	return (ENOTSUP);
74 }
75 
76 #endif	// todo
77 
78 /*
79  * Helper for smbfs_getattr_otw
80  * used when we don't have an open FID
81  *
82  * For SMB2 we need to do an attribute-only open.  The
83  * data returned by open gets us everything we need, so
84  * just close the handle and we're done.
85  */
86 int
87 smbfs_smb2_getpattr(
88 	struct smbnode *np,
89 	struct smbfattr *fap,
90 	struct smb_cred *scrp)
91 {
92 	smb_fh_t tmp_fh;
93 	struct smb_share *ssp = np->n_mount->smi_share;
94 	uint32_t rights = (STD_RIGHT_READ_CONTROL_ACCESS |
95 	    SA_RIGHT_FILE_READ_ATTRIBUTES);
96 	int error;
97 
98 	bzero(&tmp_fh, sizeof (tmp_fh));
99 	error = smbfs_smb_ntcreatex(np,
100 	    NULL, 0, 0,	/* name nmlen xattr */
101 	    rights, SMB_EFA_NORMAL,
102 	    NTCREATEX_SHARE_ACCESS_ALL,
103 	    NTCREATEX_DISP_OPEN,
104 	    0, /* create options */
105 	    scrp, &tmp_fh,
106 	    NULL, fap);
107 	if (error == 0) {
108 		(void) smb_smb_close(ssp, &tmp_fh, scrp);
109 	}
110 
111 	return (error);
112 }
113 
114 /*
115  * Common SMB2 query file info
116  */
117 static int
118 smbfs_smb2_query_info(struct smb_share *ssp, smb2fid_t *fid,
119 	struct mdchain *info_mdp, uint32_t *iolen,
120 	uint8_t type, uint8_t level, uint32_t addl_info,
121 	struct smb_cred *scrp)
122 {
123 	struct smb_rq *rqp = NULL;
124 	struct mbchain *mbp;
125 	struct mdchain *mdp;
126 	uint32_t dlen = 0;
127 	uint16_t doff = 0;
128 	uint16_t ssize = 0;
129 	int error;
130 
131 	error = smb_rq_alloc(SSTOCP(ssp), SMB2_QUERY_INFO, scrp, &rqp);
132 	if (error)
133 		goto out;
134 
135 	/*
136 	 * Build the SMB 2 Query Info req.
137 	 */
138 	smb_rq_getrequest(rqp, &mbp);
139 	mb_put_uint16le(mbp, 41);		// struct size
140 	mb_put_uint8(mbp, type);
141 	mb_put_uint8(mbp, level);
142 	mb_put_uint32le(mbp, *iolen);		// out buf len
143 	mb_put_uint16le(mbp, 0);		// in buf off
144 	mb_put_uint16le(mbp, 0);		// reserved
145 	mb_put_uint32le(mbp, 0);		// in buf len
146 	mb_put_uint32le(mbp, addl_info);
147 	mb_put_uint32le(mbp, 0);		// flags
148 	mb_put_uint64le(mbp, fid->fid_persistent);
149 	mb_put_uint64le(mbp, fid->fid_volatile);
150 
151 	error = smb2_rq_simple(rqp);
152 	if (error) {
153 		if (rqp->sr_error == NT_STATUS_INVALID_PARAMETER)
154 			error = ENOTSUP;
155 		goto out;
156 	}
157 
158 	/*
159 	 * Parse SMB 2 Query Info response
160 	 */
161 	smb_rq_getreply(rqp, &mdp);
162 
163 	/* Check structure size is 9 */
164 	md_get_uint16le(mdp, &ssize);
165 	if (ssize != 9) {
166 		error = EBADRPC;
167 		goto out;
168 	}
169 
170 	/* Get data off, len */
171 	md_get_uint16le(mdp, &doff);
172 	md_get_uint32le(mdp, &dlen);
173 	*iolen = dlen;
174 
175 	/*
176 	 * Skip ahead to the payload, as needed.
177 	 * Current offset is SMB2_HDRLEN + 8.
178 	 */
179 	if (dlen != 0) {
180 		mblk_t *m = NULL;
181 		int skip = (int)doff - (SMB2_HDRLEN + 8);
182 		if (skip < 0) {
183 			error = EBADRPC;
184 			goto out;
185 		}
186 		if (skip > 0) {
187 			md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
188 		}
189 		error = md_get_mbuf(mdp, dlen, &m);
190 		if (error)
191 			goto out;
192 		md_initm(info_mdp, m);
193 	}
194 
195 out:
196 	smb_rq_done(rqp);
197 
198 	return (error);
199 }
200 
201 
202 /*
203  * Get FileAllInformation for an open file
204  * and parse into *fap
205  */
206 int
207 smbfs_smb2_qfileinfo(struct smb_share *ssp, smb2fid_t *fid,
208 	struct smbfattr *fap, struct smb_cred *scrp)
209 {
210 	struct mdchain info_mdc, *mdp = &info_mdc;
211 	uint32_t iolen = 1024;
212 	int error;
213 
214 	bzero(mdp, sizeof (*mdp));
215 
216 	error = smbfs_smb2_query_info(ssp, fid, mdp, &iolen,
217 	    SMB2_0_INFO_FILE, FileAllInformation, 0, scrp);
218 	if (error)
219 		goto out;
220 
221 	error = smbfs_decode_file_all_info(ssp, mdp, fap);
222 
223 out:
224 	md_done(mdp);
225 
226 	return (error);
227 }
228 
229 /*
230  * Get some SMB2_0_INFO_FILESYSTEM info
231  *
232  * Note: This can be called during mount.  We don't have any
233  * smbfs_node_t or pathname, so do our own attr. open on
234  * the root of the share to get a handle for this request.
235  */
236 static int
237 smbfs_smb2_query_fs_info(struct smb_share *ssp, struct mdchain *mdp,
238 	uint8_t level, struct smb_cred *scrp)
239 {
240 	smb2fid_t fid;
241 	uint32_t iolen = 1024;
242 	boolean_t opened = B_FALSE;
243 	int error;
244 
245 	/*
246 	 * Need a FID for smb2, and this is called during mount
247 	 * so "go behind" the usual open/close functions.
248 	 */
249 	error = smb2_smb_ntcreate(
250 	    ssp, NULL,	// name
251 	    NULL, NULL, // create ctx in, out
252 	    0,	/* NTCREATEX_FLAGS... */
253 	    SA_RIGHT_FILE_READ_ATTRIBUTES,
254 	    SMB_EFA_NORMAL,
255 	    NTCREATEX_SHARE_ACCESS_ALL,
256 	    NTCREATEX_DISP_OPEN,
257 	    0, /* create options */
258 	    NTCREATEX_IMPERSONATION_IMPERSONATION,
259 	    scrp, &fid, NULL, NULL);
260 	if (error != 0)
261 		goto out;
262 	opened = B_TRUE;
263 
264 	error = smbfs_smb2_query_info(ssp, &fid, mdp, &iolen,
265 	    SMB2_0_INFO_FILESYSTEM, level, 0, scrp);
266 
267 out:
268 	if (opened)
269 		(void) smb2_smb_close(ssp, &fid, scrp);
270 
271 	return (error);
272 }
273 
274 /*
275  * Get FileFsAttributeInformation and
276  * parse into *info
277  */
278 int
279 smbfs_smb2_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *info,
280 	struct smb_cred *scrp)
281 {
282 	struct mdchain info_mdc, *mdp = &info_mdc;
283 	int error;
284 
285 	bzero(mdp, sizeof (*mdp));
286 
287 	error = smbfs_smb2_query_fs_info(ssp, mdp,
288 	    FileFsAttributeInformation, scrp);
289 	if (error)
290 		goto out;
291 	error = smbfs_decode_fs_attr_info(ssp, mdp, info);
292 
293 out:
294 	md_done(mdp);
295 
296 	return (error);
297 }
298 
299 /*
300  * Get FileFsFullSizeInformation and
301  * parse into *info
302  */
303 int
304 smbfs_smb2_statfs(struct smb_share *ssp,
305 	struct smb_fs_size_info *info,
306 	struct smb_cred *scrp)
307 {
308 	struct mdchain info_mdc, *mdp = &info_mdc;
309 	int error;
310 
311 	bzero(mdp, sizeof (*mdp));
312 
313 	error = smbfs_smb2_query_fs_info(ssp, mdp,
314 	    FileFsFullSizeInformation, scrp);
315 	if (error)
316 		goto out;
317 
318 	md_get_uint64le(mdp, &info->total_units);
319 	md_get_uint64le(mdp, &info->caller_avail);
320 	md_get_uint64le(mdp, &info->actual_avail);
321 
322 	md_get_uint32le(mdp, &info->sect_per_unit);
323 	error = md_get_uint32le(mdp, &info->bytes_per_sect);
324 
325 out:
326 	md_done(mdp);
327 
328 	return (error);
329 }
330 
331 int
332 smbfs_smb2_flush(struct smb_share *ssp, smb2fid_t *fid,
333 	struct smb_cred *scrp)
334 {
335 	struct smb_rq *rqp;
336 	struct mbchain *mbp;
337 	int error;
338 
339 	error = smb_rq_alloc(SSTOCP(ssp), SMB2_FLUSH, scrp, &rqp);
340 	if (error)
341 		return (error);
342 
343 	/*
344 	 * Build the SMB 2 Flush Request
345 	 */
346 	smb_rq_getrequest(rqp, &mbp);
347 	mb_put_uint16le(mbp, 24);	/* struct size */
348 	mb_put_uint16le(mbp, 0);	/* reserved */
349 	mb_put_uint32le(mbp, 0);	/* reserved */
350 
351 	mb_put_uint64le(mbp, fid->fid_persistent);
352 	mb_put_uint64le(mbp, fid->fid_volatile);
353 
354 	rqp->sr_flags |= SMBR_NORECONNECT;
355 	error = smb2_rq_simple(rqp);
356 	smb_rq_done(rqp);
357 
358 	return (error);
359 }
360 
361 /*
362  * Set file info via an open handle.
363  * Caller provides payload, info level.
364  */
365 static int
366 smbfs_smb2_set_info(struct smb_share *ssp, smb2fid_t *fid,
367 	struct mbchain *info_mbp, uint8_t type, uint8_t level,
368 	uint32_t addl_info, struct smb_cred *scrp)
369 {
370 	struct smb_rq *rqp = NULL;
371 	struct mbchain *mbp;
372 	uint32_t *buffer_lenp;
373 	int base, len;
374 	int error;
375 
376 	error = smb_rq_alloc(SSTOCP(ssp), SMB2_SET_INFO, scrp, &rqp);
377 	if (error)
378 		goto out;
379 
380 	/*
381 	 * Build the SMB 2 Set Info req.
382 	 */
383 	smb_rq_getrequest(rqp, &mbp);
384 	mb_put_uint16le(mbp, 33);		// struct size
385 	mb_put_uint8(mbp, type);
386 	mb_put_uint8(mbp, level);
387 	buffer_lenp = mb_reserve(mbp, sizeof (uint32_t));
388 	mb_put_uint16le(mbp, SMB2_HDRLEN + 32);	// Buffer Offset
389 	mb_put_uint16le(mbp, 0);		// Reserved
390 	mb_put_uint32le(mbp, addl_info);	// Additional Info
391 
392 	mb_put_uint64le(mbp, fid->fid_persistent);
393 	mb_put_uint64le(mbp, fid->fid_volatile);
394 
395 	/*
396 	 * Now the payload
397 	 */
398 	base = mbp->mb_count;
399 	error = mb_put_mbchain(mbp, info_mbp);
400 	if (error)
401 		goto out;
402 	len = mbp->mb_count - base;
403 	*buffer_lenp = htolel(len);
404 	if (error)
405 		goto out;
406 
407 	/*
408 	 * Run the request.
409 	 * Don't care about the (empty) reply.
410 	 */
411 	error = smb2_rq_simple(rqp);
412 
413 out:
414 	smb_rq_done(rqp);
415 
416 	return (error);
417 }
418 
419 int
420 smbfs_smb2_seteof(struct smb_share *ssp, smb2fid_t *fid,
421 	uint64_t newsize, struct smb_cred *scrp)
422 {
423 	struct mbchain data_mb, *mbp = &data_mb;
424 	uint8_t level = FileEndOfFileInformation;
425 	int error;
426 
427 	mb_init(mbp);
428 	mb_put_uint64le(mbp, newsize);
429 	error = smbfs_smb2_set_info(ssp, fid, mbp,
430 	    SMB2_0_INFO_FILE, level, 0, scrp);
431 	mb_done(mbp);
432 
433 	return (error);
434 }
435 
436 int
437 smbfs_smb2_setdisp(struct smb_share *ssp, smb2fid_t *fid,
438 	uint8_t newdisp, struct smb_cred *scrp)
439 {
440 	struct mbchain data_mb, *mbp = &data_mb;
441 	uint8_t level = FileDispositionInformation;
442 	int error;
443 
444 	mb_init(mbp);
445 	mb_put_uint8(mbp, newdisp);
446 	error = smbfs_smb2_set_info(ssp, fid, mbp,
447 	    SMB2_0_INFO_FILE,  level, 0, scrp);
448 	mb_done(mbp);
449 
450 	return (error);
451 }
452 
453 /*
454  * Set FileBasicInformation on an open handle
455  * Caller builds the mbchain.
456  */
457 int
458 smbfs_smb2_setfattr(struct smb_share *ssp, smb2fid_t *fid,
459 	struct mbchain *mbp, struct smb_cred *scrp)
460 {
461 	uint8_t level = FileBasicInformation;
462 	int error;
463 
464 	error = smbfs_smb2_set_info(ssp, fid, mbp,
465 	    SMB2_0_INFO_FILE,  level, 0, scrp);
466 	return (error);
467 }
468 
469 /*
470  * Build a FileRenameInformation and call setinfo
471  */
472 int
473 smbfs_smb2_rename(struct smbnode *np, struct smbnode *tdnp,
474 	const char *tname, int tnlen, int overwrite,
475 	smb2fid_t *fid, struct smb_cred *scrp)
476 {
477 	struct smb_share *ssp = np->n_mount->smi_share;
478 	struct mbchain data_mb, *mbp = &data_mb;
479 	uint32_t *name_lenp;
480 	uint8_t level = FileRenameInformation;
481 	int base, len;
482 	int error;
483 
484 	mb_init(mbp);
485 
486 	mb_put_uint32le(mbp, (overwrite & 1));
487 	mb_put_uint32le(mbp, 0);		// reserved
488 	mb_put_uint64le(mbp, 0);		// Root Dir
489 	name_lenp = mb_reserve(mbp, 4);
490 
491 	/* Target name (full path) */
492 	base = mbp->mb_count;
493 	if (tnlen > 0) {
494 		error = smbfs_fullpath(mbp, SSTOVC(ssp),
495 		    tdnp, tname, tnlen, '\\');
496 		if (error)
497 			goto out;
498 	}
499 	len = mbp->mb_count - base;
500 	*name_lenp = htolel(len);
501 
502 	error = smbfs_smb2_set_info(ssp, fid, mbp,
503 	    SMB2_0_INFO_FILE,  level, 0, scrp);
504 
505 out:
506 	mb_done(mbp);
507 
508 	return (error);
509 }
510 
511 /*
512  * Later servers have maxtransact at a megabyte or more,
513  * but we don't want to buffer up that much data, so use
514  * the lesser of that or 64k.
515  */
516 #define	SMBFS_QDIR_MAX_BUF	(1<<16)
517 
518 /*
519  * SMB2 query directory
520  */
521 static int
522 smbfs_smb2_qdir(struct smbfs_fctx *ctx)
523 {
524 	smb_fh_t *fhp = ctx->f_fhp;
525 	smb_share_t *ssp = ctx->f_ssp;
526 	smb_vc_t *vcp = SSTOVC(ssp);
527 	struct smb_rq *rqp;
528 	struct mbchain *mbp;
529 	struct mdchain *mdp;
530 	uint16_t *name_lenp;
531 	uint8_t level, flags;
532 	uint16_t ssize = 0;
533 	uint16_t obuf_off = 0;
534 	uint32_t obuf_len = 0;
535 	uint32_t obuf_req;
536 	int error;
537 
538 	level = (uint8_t)ctx->f_infolevel;
539 	flags = 0;
540 	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE)
541 		flags |= SMB2_QDIR_FLAG_SINGLE;
542 	if (ctx->f_flags & SMBFS_RDD_FINDFIRST)
543 		ctx->f_rkey = 0;
544 	else
545 		flags |= SMB2_QDIR_FLAG_INDEX;
546 
547 	obuf_req = SMBFS_QDIR_MAX_BUF;
548 	if (obuf_req > vcp->vc_sopt.sv2_maxtransact)
549 		obuf_req = vcp->vc_sopt.sv2_maxtransact;
550 
551 	if (ctx->f_rq) {
552 		smb_rq_done(ctx->f_rq);
553 		ctx->f_rq = NULL;
554 	}
555 	error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB2_QUERY_DIRECTORY,
556 	    ctx->f_scred, &rqp);
557 	if (error)
558 		return (error);
559 	ctx->f_rq = rqp;
560 
561 	/*
562 	 * Build an SMB2 Query Dir req.
563 	 */
564 	smb_rq_getrequest(rqp, &mbp);
565 
566 	mb_put_uint16le(mbp, 33);			/* Struct size */
567 	mb_put_uint8(mbp, level);
568 	mb_put_uint8(mbp, flags);
569 	mb_put_uint32le(mbp, ctx->f_rkey);		/* FileIndex */
570 
571 	mb_put_uint64le(mbp, fhp->fh_fid2.fid_persistent);
572 	mb_put_uint64le(mbp, fhp->fh_fid2.fid_volatile);
573 
574 	mb_put_uint16le(mbp, 96);
575 	name_lenp = mb_reserve(mbp, sizeof (uint16_t));	/* FileNameLen */
576 	mb_put_uint32le(mbp, obuf_req);			/* Output Buf Len */
577 
578 	/* Add in the name if any */
579 	if (ctx->f_wclen > 0) {
580 		int base, len;
581 
582 		/* Put the match pattern. */
583 		base = mbp->mb_count;
584 		error = smb_put_dmem(mbp, vcp,
585 		    ctx->f_wildcard, ctx->f_wclen,
586 		    SMB_CS_NONE, NULL);
587 		if (error)
588 			return (error);
589 
590 		/* Update the FileNameLen */
591 		len = mbp->mb_count - base;
592 		*name_lenp = htoles(len);
593 	} else {
594 		/* Empty string */
595 		mb_put_uint16le(mbp, 0);
596 		*name_lenp = 0;
597 	}
598 
599 	error = smb2_rq_simple(rqp);
600 	if (error != 0)
601 		goto out;
602 
603 	/*
604 	 * Parse the SMB2 Query Dir response
605 	 */
606 	smb_rq_getreply(rqp, &mdp);
607 
608 	/* Check structure size is 9 */
609 	md_get_uint16le(mdp, &ssize);
610 	if (ssize != 9) {
611 		error = EBADRPC;
612 		goto out;
613 	}
614 
615 	/* Get output buffer offset, length */
616 	md_get_uint16le(mdp, &obuf_off);
617 	md_get_uint32le(mdp, &obuf_len);
618 
619 	/*
620 	 * After read at EOF we'll have just one word:
621 	 * NextEntryOffset == 0  Allow some padding.
622 	 */
623 	if (obuf_len < 8) {
624 		error = ENOENT;
625 		goto out;
626 	}
627 
628 	/*
629 	 * If this reply is shorter than requested by 1k
630 	 * or more, we must have reached EOF.
631 	 */
632 	if ((obuf_len + 1024) < obuf_req)
633 		ctx->f_flags |= SMBFS_RDD_EOF;
634 
635 	/*
636 	 * Have data. Put the payload in ctx->f_mdchain
637 	 * Current offset is SMB2_HDRLEN + 8.
638 	 */
639 	{
640 		mblk_t *m = NULL;
641 		int skip = (int)obuf_off - (SMB2_HDRLEN + 8);
642 		if (skip < 0) {
643 			error = EBADRPC;
644 			goto out;
645 		}
646 		if (skip > 0) {
647 			md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
648 		}
649 		error = md_get_mbuf(mdp, obuf_len, &m);
650 		if (error)
651 			goto out;
652 		md_done(&ctx->f_mdchain);
653 		md_initm(&ctx->f_mdchain, m);
654 	}
655 
656 	/*
657 	 * SMB2 Query Directory does not provie an EntryCount.
658 	 * Instead, we'll advance f_eofs (entry offset)
659 	 * through the range [0..f_left]
660 	 */
661 	ctx->f_left = obuf_len;
662 	ctx->f_eofs = 0;
663 	return (0);
664 
665 out:
666 	if (error != 0) {
667 		/*
668 		 * Failed parsing the FindFirst or FindNext response.
669 		 * Force this directory listing closed, otherwise the
670 		 * calling process may hang in an infinite loop.
671 		 */
672 		ctx->f_left = 0;
673 		ctx->f_eofs = 0;
674 		ctx->f_flags |= SMBFS_RDD_EOF;
675 	}
676 
677 	return (error);
678 }
679 
680 int
681 smbfs_smb2_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp,
682     const char *wildcard, int wclen, uint32_t attr)
683 {
684 	smb_fh_t *fhp = NULL;
685 	uint32_t rights =
686 	    STD_RIGHT_READ_CONTROL_ACCESS |
687 	    SA_RIGHT_FILE_READ_ATTRIBUTES |
688 	    SA_RIGHT_FILE_READ_DATA;
689 	int error;
690 
691 	/*
692 	 * Set f_type no matter what, so cleanup will call
693 	 * smbfs_smb2_findclose, error or not.
694 	 */
695 	ctx->f_type = ft_SMB2;
696 	ASSERT(ctx->f_dnp == dnp);
697 
698 	/*
699 	 * Get a file handle on the directory
700 	 */
701 	error = smb_fh_create(ctx->f_ssp, &fhp);
702 	if (error != 0)
703 		goto errout;
704 
705 	error = smbfs_smb_ntcreatex(dnp,
706 	    NULL, 0, 0,	/* name nmlen xattr */
707 	    rights, SMB_EFA_NORMAL,
708 	    NTCREATEX_SHARE_ACCESS_ALL,
709 	    NTCREATEX_DISP_OPEN,
710 	    0, /* create options */
711 	    ctx->f_scred, fhp,
712 	    NULL, NULL); /* cr_act_p fa_p */
713 	if (error != 0)
714 		goto errout;
715 
716 	fhp->fh_rights = rights;
717 	smb_fh_opened(fhp);
718 	ctx->f_fhp = fhp;
719 
720 	ctx->f_namesz = SMB_MAXFNAMELEN + 1;
721 	ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP);
722 	ctx->f_infolevel = FileFullDirectoryInformation;
723 	ctx->f_attrmask = attr;
724 	ctx->f_wildcard = wildcard;
725 	ctx->f_wclen = wclen;
726 
727 	return (0);
728 
729 errout:
730 	if (fhp != NULL)
731 		smb_fh_rele(fhp);
732 	return (error);
733 }
734 
735 int
736 smbfs_smb2_findclose(struct smbfs_fctx *ctx)
737 {
738 	smb_fh_t *fhp = NULL;
739 
740 	if ((fhp = ctx->f_fhp) != NULL) {
741 		ctx->f_fhp = NULL;
742 		smb_fh_rele(fhp);
743 	}
744 	if (ctx->f_name)
745 		kmem_free(ctx->f_name, ctx->f_namesz);
746 	if (ctx->f_rq)
747 		smb_rq_done(ctx->f_rq);
748 	md_done(&ctx->f_mdchain);
749 
750 	return (0);
751 }
752 
753 /*
754  * Get a buffer of directory entries (if we don't already have
755  * some remaining in the current buffer) then decode one.
756  */
757 int
758 smbfs_smb2_findnext(struct smbfs_fctx *ctx, uint16_t limit)
759 {
760 	int error;
761 
762 	/*
763 	 * If we've scanned to the end of the current buffer
764 	 * try to read anohther buffer of dir entries.
765 	 * Treat anything less than 8 bytes as an "empty"
766 	 * buffer to ensure we can read something.
767 	 * (There may be up to 8 bytes of padding.)
768 	 */
769 	if ((ctx->f_eofs + 8) > ctx->f_left) {
770 		/* Scanned the whole buffer. */
771 		if (ctx->f_flags & SMBFS_RDD_EOF)
772 			return (ENOENT);
773 		ctx->f_limit = limit;
774 		error = smbfs_smb2_qdir(ctx);
775 		if (error)
776 			return (error);
777 		ctx->f_otws++;
778 	}
779 
780 	/*
781 	 * Decode one entry
782 	 */
783 	error = smbfs_decode_dirent(ctx);
784 
785 	return (error);
786 }
787 
788 
789 /*
790  * Helper for smbfs_xa_get_streaminfo
791  * Query stream info
792  */
793 int
794 smbfs_smb2_get_streaminfo(smbnode_t *np, struct mdchain *mdp,
795 	struct smb_cred *scrp)
796 {
797 	smb_share_t *ssp = np->n_mount->smi_share;
798 	smb_fh_t *fhp = NULL;
799 	uint32_t rights =
800 	    STD_RIGHT_READ_CONTROL_ACCESS |
801 	    SA_RIGHT_FILE_READ_ATTRIBUTES;
802 	uint32_t iolen = INT16_MAX;
803 	int error;
804 
805 	/*
806 	 * Get a file handle on the object
807 	 * with read attr. rights.
808 	 */
809 	error = smb_fh_create(ssp, &fhp);
810 	if (error != 0)
811 		goto out;
812 	error = smbfs_smb_ntcreatex(np,
813 	    NULL, 0, 0,	/* name nmlen xattr */
814 	    rights, SMB_EFA_NORMAL,
815 	    NTCREATEX_SHARE_ACCESS_ALL,
816 	    NTCREATEX_DISP_OPEN,
817 	    0, /* create options */
818 	    scrp, fhp, NULL, NULL);
819 	if (error != 0)
820 		goto out;
821 
822 	smb_fh_opened(fhp);
823 
824 	/*
825 	 * Query stream info
826 	 */
827 	error = smbfs_smb2_query_info(ssp, &fhp->fh_fid2, mdp, &iolen,
828 	    SMB2_0_INFO_FILE, FileStreamInformation, 0, scrp);
829 
830 out:
831 	if (fhp != NULL)
832 		smb_fh_rele(fhp);
833 	return (error);
834 }
835 
836 
837 /*
838  * OTW function to Get a security descriptor (SD).
839  *
840  * The *reslen param is bufsize(in) / length(out)
841  * Note: On success, this fills in mdp->md_top,
842  * which the caller should free.
843  */
844 int
845 smbfs_smb2_getsec(struct smb_share *ssp, smb2fid_t *fid,
846 	uint32_t selector, mblk_t **res, uint32_t *reslen,
847 	struct smb_cred *scrp)
848 {
849 	struct mdchain info_mdc, *mdp = &info_mdc;
850 	int error;
851 
852 	bzero(mdp, sizeof (*mdp));
853 
854 	error = smbfs_smb2_query_info(ssp, fid, mdp, reslen,
855 	    SMB2_0_INFO_SECURITY, 0, selector, scrp);
856 	if (error)
857 		goto out;
858 
859 	if (mdp->md_top == NULL) {
860 		error = EBADRPC;
861 		goto out;
862 	}
863 	*res = mdp->md_top;
864 	mdp->md_top = NULL;
865 
866 out:
867 	md_done(mdp);
868 	return (error);
869 }
870 
871 
872 /*
873  * OTW function to Set a security descriptor (SD).
874  * Caller data are carried in an mbchain_t.
875  *
876  * Note: This normally consumes mbp->mb_top, and clears
877  * that pointer when it does.
878  */
879 int
880 smbfs_smb2_setsec(struct smb_share *ssp, smb2fid_t *fid,
881 	uint32_t selector, mblk_t **mp, struct smb_cred *scrp)
882 {
883 	struct mbchain info_mbp, *mbp = &info_mbp;
884 	int error;
885 
886 	ASSERT(*mp != NULL);
887 	mb_initm(mbp, *mp);
888 	*mp = NULL; /* consumed */
889 
890 	error = smbfs_smb2_set_info(ssp, fid, mbp,
891 	    SMB2_0_INFO_SECURITY, 0, selector, scrp);
892 
893 	mb_done(mbp);
894 
895 	return (error);
896 }
897