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
68int
69smbfs_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 */
86int
87smbfs_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 */
117static int
118smbfs_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
195out:
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 */
206int
207smbfs_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
223out:
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 */
236static int
237smbfs_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
267out:
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 */
278int
279smbfs_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
293out:
294	md_done(mdp);
295
296	return (error);
297}
298
299/*
300 * Get FileFsFullSizeInformation and
301 * parse into *info
302 */
303int
304smbfs_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
325out:
326	md_done(mdp);
327
328	return (error);
329}
330
331int
332smbfs_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 */
365static int
366smbfs_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
413out:
414	smb_rq_done(rqp);
415
416	return (error);
417}
418
419int
420smbfs_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
436int
437smbfs_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 */
457int
458smbfs_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 */
472int
473smbfs_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
505out:
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 */
521static int
522smbfs_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
665out:
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
680int
681smbfs_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
729errout:
730	if (fhp != NULL)
731		smb_fh_rele(fhp);
732	return (error);
733}
734
735int
736smbfs_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 */
757int
758smbfs_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 */
793int
794smbfs_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
830out:
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 */
844int
845smbfs_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
866out:
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 */
879int
880smbfs_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