xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_create.c (revision 80bd8a585c4ef2f19c09c9c4379635e411c91fe6)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 /*
17  * Dispatch function for SMB2_CREATE
18  * [MS-SMB2] 2.2.13
19  */
20 
21 #include <smbsrv/smb2_kproto.h>
22 #include <smbsrv/smb_fsops.h>
23 
24 /*
25  * Some flags used locally to keep track of which Create Context
26  * names have been provided and/or requested.
27  */
28 #define	CCTX_EA_BUFFER			1
29 #define	CCTX_SD_BUFFER			2
30 #define	CCTX_DH_REQUEST			4
31 #define	CCTX_DH_RECONNECT		8
32 #define	CCTX_ALLOCATION_SIZE		0x10
33 #define	CCTX_QUERY_MAX_ACCESS		0x20
34 #define	CCTX_TIMEWARP_TOKEN		0x40
35 #define	CCTX_QUERY_ON_DISK_ID		0x80
36 #define	CCTX_REQUEST_LEASE		0x100
37 #define	CCTX_AAPL_EXT			0x200
38 
39 typedef struct smb2_create_ctx_elem {
40 	uint32_t cce_len;
41 	mbuf_chain_t cce_mbc;
42 } smb2_create_ctx_elem_t;
43 
44 typedef struct smb2_create_ctx {
45 	uint_t	cc_in_flags;	/* CCTX_... */
46 	uint_t	cc_out_flags;	/* CCTX_... */
47 	/* Elements we may see in the request. */
48 	smb2_create_ctx_elem_t cc_in_ext_attr;
49 	smb2_create_ctx_elem_t cc_in_sec_desc;
50 	smb2_create_ctx_elem_t cc_in_dh_request;
51 	smb2_create_ctx_elem_t cc_in_dh_reconnect;
52 	smb2_create_ctx_elem_t cc_in_alloc_size;
53 	smb2_create_ctx_elem_t cc_in_time_warp;
54 	smb2_create_ctx_elem_t cc_in_req_lease;
55 	smb2_create_ctx_elem_t cc_in_aapl;
56 	/* Elements we my place in the response */
57 	smb2_create_ctx_elem_t cc_out_max_access;
58 	smb2_create_ctx_elem_t cc_out_file_id;
59 	smb2_create_ctx_elem_t cc_out_aapl;
60 } smb2_create_ctx_t;
61 
62 static uint32_t smb2_decode_create_ctx(
63 	mbuf_chain_t *,	smb2_create_ctx_t *);
64 static uint32_t smb2_encode_create_ctx(
65 	mbuf_chain_t *, smb2_create_ctx_t *);
66 static int smb2_encode_create_ctx_elem(
67 	mbuf_chain_t *, smb2_create_ctx_elem_t *, uint32_t);
68 static void smb2_free_create_ctx(smb2_create_ctx_t *);
69 
70 smb_sdrc_t
71 smb2_create(smb_request_t *sr)
72 {
73 	smb_attr_t *attr;
74 	smb2_create_ctx_elem_t *cce;
75 	smb2_create_ctx_t cctx;
76 	mbuf_chain_t cc_mbc;
77 	smb_arg_open_t *op = &sr->arg.open;
78 	smb_ofile_t *of = NULL;
79 	uint16_t StructSize;
80 	uint8_t SecurityFlags;
81 	uint8_t OplockLevel;
82 	uint32_t ImpersonationLevel;
83 	uint64_t SmbCreateFlags;
84 	uint64_t Reserved4;
85 	uint16_t NameOffset;
86 	uint16_t NameLength;
87 	uint32_t CreateCtxOffset;
88 	uint32_t CreateCtxLength;
89 	smb2fid_t smb2fid;
90 	uint32_t status;
91 	int skip;
92 	int rc = 0;
93 
94 	bzero(&cctx, sizeof (cctx));
95 	bzero(&cc_mbc, sizeof (cc_mbc));
96 
97 	/*
98 	 * Paranoia.  This will set sr->fid_ofile, so
99 	 * if we already have one, release it now.
100 	 */
101 	if (sr->fid_ofile != NULL) {
102 		smb_ofile_request_complete(sr->fid_ofile);
103 		smb_ofile_release(sr->fid_ofile);
104 		sr->fid_ofile = NULL;
105 	}
106 
107 	/*
108 	 * SMB2 Create request
109 	 */
110 	rc = smb_mbc_decodef(
111 	    &sr->smb_data, "wbblqqlllllwwll",
112 	    &StructSize,		/* w */
113 	    &SecurityFlags,		/* b */
114 	    &OplockLevel,		/* b */
115 	    &ImpersonationLevel,	/* l */
116 	    &SmbCreateFlags,		/* q */
117 	    &Reserved4,			/* q */
118 	    &op->desired_access,	/* l */
119 	    &op->dattr,			/* l */
120 	    &op->share_access,		/* l */
121 	    &op->create_disposition,	/* l */
122 	    &op->create_options,	/* l */
123 	    &NameOffset,		/* w */
124 	    &NameLength,		/* w */
125 	    &CreateCtxOffset,		/* l */
126 	    &CreateCtxLength);		/* l */
127 	if (rc != 0 || StructSize != 57)
128 		return (SDRC_ERROR);
129 
130 	/*
131 	 * We're normally positioned at the path name now,
132 	 * but there could be some padding before it.
133 	 */
134 	skip = (NameOffset + sr->smb2_cmd_hdr) -
135 	    sr->smb_data.chain_offset;
136 	if (skip < 0) {
137 		status = NT_STATUS_OBJECT_PATH_INVALID;
138 		goto errout;
139 	}
140 	if (skip > 0)
141 		(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
142 
143 	/*
144 	 * Get the path name
145 	 */
146 	if (NameLength >= SMB_MAXPATHLEN) {
147 		status = NT_STATUS_OBJECT_PATH_INVALID;
148 		goto errout;
149 	}
150 	if (NameLength == 0) {
151 		op->fqi.fq_path.pn_path = "\\";
152 	} else {
153 		rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
154 		    NameLength, &op->fqi.fq_path.pn_path);
155 		if (rc) {
156 			status = NT_STATUS_OBJECT_PATH_INVALID;
157 			goto errout;
158 		}
159 	}
160 	op->fqi.fq_dnode = sr->tid_tree->t_snode;
161 
162 	switch (OplockLevel) {
163 	case SMB2_OPLOCK_LEVEL_NONE:
164 		op->op_oplock_level = SMB_OPLOCK_NONE;
165 		break;
166 	case SMB2_OPLOCK_LEVEL_II:
167 		op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
168 		break;
169 	case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
170 		op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
171 		break;
172 	case SMB2_OPLOCK_LEVEL_BATCH:
173 		op->op_oplock_level = SMB_OPLOCK_BATCH;
174 		break;
175 	case SMB2_OPLOCK_LEVEL_LEASE:
176 		status = NT_STATUS_INVALID_PARAMETER;
177 		goto errout;
178 	}
179 	op->op_oplock_levelII = B_TRUE;
180 
181 	/*
182 	 * ImpersonationLevel (spec. says ignore)
183 	 * SmbCreateFlags (spec. says ignore)
184 	 */
185 
186 	if ((op->create_options & FILE_DELETE_ON_CLOSE) &&
187 	    !(op->desired_access & DELETE)) {
188 		status = NT_STATUS_INVALID_PARAMETER;
189 		goto errout;
190 	}
191 	if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) {
192 		status = NT_STATUS_INVALID_PARAMETER;
193 		goto errout;
194 	}
195 
196 	if (op->dattr & FILE_FLAG_WRITE_THROUGH)
197 		op->create_options |= FILE_WRITE_THROUGH;
198 	if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE)
199 		op->create_options |= FILE_DELETE_ON_CLOSE;
200 	if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS)
201 		op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
202 	if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT)
203 		sr->user_cr = smb_user_getprivcred(sr->uid_user);
204 
205 	/*
206 	 * If there is a "Create Context" payload, decode it.
207 	 * This may carry things like a security descriptor,
208 	 * extended attributes, etc. to be used in create.
209 	 *
210 	 * The create ctx buffer must start after the headers
211 	 * and file name, and must be 8-byte aligned.
212 	 */
213 	if (CreateCtxLength != 0) {
214 		if ((CreateCtxOffset & 7) != 0 ||
215 		    (CreateCtxOffset + sr->smb2_cmd_hdr) <
216 		    sr->smb_data.chain_offset) {
217 			status = NT_STATUS_INVALID_PARAMETER;
218 			goto errout;
219 		}
220 
221 		rc = MBC_SHADOW_CHAIN(&cc_mbc, &sr->smb_data,
222 		    sr->smb2_cmd_hdr + CreateCtxOffset, CreateCtxLength);
223 		if (rc) {
224 			status = NT_STATUS_INVALID_PARAMETER;
225 			goto errout;
226 		}
227 		status = smb2_decode_create_ctx(&cc_mbc, &cctx);
228 		if (status)
229 			goto errout;
230 
231 		if (cctx.cc_in_flags & CCTX_EA_BUFFER) {
232 			status = NT_STATUS_EAS_NOT_SUPPORTED;
233 			goto errout;
234 		}
235 
236 		if (cctx.cc_in_flags & CCTX_SD_BUFFER) {
237 			smb_sd_t sd;
238 			cce = &cctx.cc_in_sec_desc;
239 			status = smb_decode_sd(
240 			    &cce->cce_mbc, &sd);
241 			if (status)
242 				goto errout;
243 			op->sd = kmem_alloc(sizeof (sd), KM_SLEEP);
244 			*op->sd = sd;
245 		}
246 
247 		if (cctx.cc_in_flags & CCTX_ALLOCATION_SIZE) {
248 			cce = &cctx.cc_in_alloc_size;
249 			rc = smb_mbc_decodef(&cce->cce_mbc, "q", &op->dsize);
250 			if (rc) {
251 				status = NT_STATUS_INVALID_PARAMETER;
252 				goto errout;
253 			}
254 		}
255 
256 		/*
257 		 * Support for opening "Previous Versions".
258 		 * [MS-SMB2] 2.2.13.2.7  Data is an NT time.
259 		 */
260 		if (cctx.cc_in_flags & CCTX_TIMEWARP_TOKEN) {
261 			uint64_t timewarp;
262 			cce = &cctx.cc_in_time_warp;
263 			status = smb_mbc_decodef(&cce->cce_mbc,
264 			    "q", &timewarp);
265 			if (status)
266 				goto errout;
267 			smb_time_nt_to_unix(timewarp, &op->timewarp);
268 			op->create_timewarp = B_TRUE;
269 		}
270 	}
271 
272 	/*
273 	 * The real open call.   Note: this gets attributes into
274 	 * op->fqi.fq_fattr (SMB_AT_ALL).  We need those below.
275 	 */
276 	status = smb_common_open(sr);
277 	if (status != NT_STATUS_SUCCESS)
278 		goto errout;
279 	attr = &op->fqi.fq_fattr;
280 
281 	/*
282 	 * Convert the negotiate Oplock level back into
283 	 * SMB2 encoding form.
284 	 */
285 	switch (op->op_oplock_level) {
286 	default:
287 	case SMB_OPLOCK_NONE:
288 		OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
289 		break;
290 	case SMB_OPLOCK_LEVEL_II:
291 		OplockLevel = SMB2_OPLOCK_LEVEL_II;
292 		break;
293 	case SMB_OPLOCK_EXCLUSIVE:
294 		OplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
295 		break;
296 	case SMB_OPLOCK_BATCH:
297 		OplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
298 		break;
299 	}
300 
301 	/*
302 	 * NB: after the above smb_common_open() success,
303 	 * we have a handle allocated (sr->fid_ofile).
304 	 * If we don't return success, we must close it.
305 	 *
306 	 * Using sr->smb_fid as the file handle for now,
307 	 * though it could later be something larger,
308 	 * (16 bytes) similar to an NFSv4 open handle.
309 	 */
310 	of = sr->fid_ofile;
311 	smb2fid.persistent = 0;
312 	smb2fid.temporal = sr->smb_fid;
313 
314 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
315 	case STYPE_DISKTREE:
316 	case STYPE_PRINTQ:
317 		if (op->create_options & FILE_DELETE_ON_CLOSE)
318 			smb_ofile_set_delete_on_close(of);
319 		break;
320 	}
321 
322 	/*
323 	 * Build the Create Context to return; first the
324 	 * per-element parts, then the aggregated buffer.
325 	 *
326 	 * No response for these:
327 	 *	CCTX_EA_BUFFER
328 	 *	CCTX_SD_BUFFER
329 	 *	CCTX_ALLOCATION_SIZE
330 	 *	CCTX_TIMEWARP_TOKEN
331 	 *
332 	 * We don't handle these yet.
333 	 *	CCTX_DH_REQUEST
334 	 *	CCTX_DH_RECONNECT
335 	 *	CCTX_REQUEST_LEASE
336 	 */
337 	if (cctx.cc_in_flags & CCTX_QUERY_MAX_ACCESS) {
338 		cce = &cctx.cc_out_max_access;
339 		uint32_t MaxAccess = 0;
340 		if (of->f_node != NULL) {
341 			smb_fsop_eaccess(sr, of->f_cr, of->f_node, &MaxAccess);
342 		}
343 		MaxAccess |= of->f_granted_access;
344 		cce->cce_len = 8;
345 		cce->cce_mbc.max_bytes = 8;
346 		(void) smb_mbc_encodef(&cce->cce_mbc,
347 		    "ll", 0, MaxAccess);
348 		cctx.cc_out_flags |= CCTX_QUERY_MAX_ACCESS;
349 	}
350 	if ((cctx.cc_in_flags & CCTX_QUERY_ON_DISK_ID) != 0 &&
351 	    of->f_node != NULL) {
352 		cce = &cctx.cc_out_file_id;
353 		fsid_t fsid;
354 
355 		fsid = SMB_NODE_FSID(of->f_node);
356 
357 		cce->cce_len = 32;
358 		cce->cce_mbc.max_bytes = 32;
359 		(void) smb_mbc_encodef(
360 		    &cce->cce_mbc, "qll.15.",
361 		    op->fileid,		/* q */
362 		    fsid.val[0],	/* l */
363 		    fsid.val[1]);	/* l */
364 		/* reserved (16 bytes)  .15. */
365 		cctx.cc_out_flags |= CCTX_QUERY_ON_DISK_ID;
366 	}
367 	if ((cctx.cc_in_flags & CCTX_AAPL_EXT) != 0) {
368 		cce = &cctx.cc_out_aapl;
369 		/*
370 		 * smb2_aapl_crctx has a variable response depending on
371 		 * what the incoming context looks like, so it does all
372 		 * the work of building cc_out_aapl, including setting
373 		 * cce_len, cce_mbc.max_bytes, and smb_mbc_encode.
374 		 * If we see errors getting this, simply omit it from
375 		 * the collection of returned create contexts.
376 		 */
377 		status = smb2_aapl_crctx(sr,
378 		    &cctx.cc_in_aapl.cce_mbc, &cce->cce_mbc);
379 		if (status == 0) {
380 			cce->cce_len = cce->cce_mbc.chain_offset;
381 			cctx.cc_out_flags |= CCTX_AAPL_EXT;
382 		}
383 		status = 0;
384 	}
385 	if (cctx.cc_out_flags) {
386 		sr->raw_data.max_bytes = smb2_max_trans;
387 		status = smb2_encode_create_ctx(&sr->raw_data, &cctx);
388 		if (status)
389 			goto errout;
390 	}
391 
392 	/*
393 	 * SMB2 Create reply
394 	 */
395 	rc = smb_mbc_encodef(
396 	    &sr->reply,
397 	    "wb.lTTTTqqllqqll",
398 	    89,	/* StructSize */	/* w */
399 	    OplockLevel,		/* b */
400 	    op->action_taken,		/* l */
401 	    &attr->sa_crtime,		/* T */
402 	    &attr->sa_vattr.va_atime,	/* T */
403 	    &attr->sa_vattr.va_mtime,	/* T */
404 	    &attr->sa_vattr.va_ctime,	/* T */
405 	    attr->sa_allocsz,		/* q */
406 	    attr->sa_vattr.va_size,	/* q */
407 	    attr->sa_dosattr,		/* l */
408 	    0, /* reserved2 */		/* l */
409 	    smb2fid.persistent,		/* q */
410 	    smb2fid.temporal,		/* q */
411 	    0,  /* CreateCtxOffset	   l */
412 	    0); /* CreateCtxLength	   l */
413 	if (rc != 0) {
414 		status = NT_STATUS_UNSUCCESSFUL;
415 		goto errout;
416 	}
417 
418 	CreateCtxOffset = sr->reply.chain_offset - sr->smb2_reply_hdr;
419 	CreateCtxLength = MBC_LENGTH(&sr->raw_data);
420 	if (CreateCtxLength != 0) {
421 		/*
422 		 * Overwrite CreateCtxOffset, CreateCtxLength, pad
423 		 */
424 		sr->reply.chain_offset -= 8;
425 		rc = smb_mbc_encodef(
426 		    &sr->reply,
427 		    "ll#C",
428 		    CreateCtxOffset,	/* l */
429 		    CreateCtxLength,	/* l */
430 		    CreateCtxLength,	/* # */
431 		    &sr->raw_data);	/* C */
432 		if (rc != 0) {
433 			status = NT_STATUS_UNSUCCESSFUL;
434 			goto errout;
435 		}
436 	} else {
437 		(void) smb_mbc_encodef(&sr->reply, ".");
438 	}
439 	return (SDRC_SUCCESS);
440 
441 errout:
442 	if (of != NULL)
443 		smb_ofile_close(of, 0);
444 	if (cctx.cc_out_flags)
445 		smb2_free_create_ctx(&cctx);
446 	smb2sr_put_error(sr, status);
447 	return (SDRC_SUCCESS);
448 }
449 
450 /*
451  * Decode an SMB2 Create Context buffer into our internal form.
452  * No policy decisions about what's supported here, just decode.
453  */
454 static uint32_t
455 smb2_decode_create_ctx(mbuf_chain_t *in_mbc, smb2_create_ctx_t *cc)
456 {
457 	smb2_create_ctx_elem_t *cce;
458 	mbuf_chain_t name_mbc;
459 	union {
460 		uint32_t i;
461 		char ch[4];
462 	} cc_name;
463 	uint32_t status;
464 	int32_t next_off;
465 	uint32_t data_len;
466 	uint16_t data_off;
467 	uint16_t name_off;
468 	uint16_t name_len;
469 	int top_offset;
470 	int rc;
471 
472 	status = NT_STATUS_INVALID_PARAMETER;
473 	for (;;) {
474 		cce = NULL;
475 		top_offset = in_mbc->chain_offset;
476 		rc = smb_mbc_decodef(
477 		    in_mbc,
478 		    "lww..wl",
479 		    &next_off,	/* l */
480 		    &name_off,	/* w */
481 		    &name_len,	/* w */
482 		    /* reserved	  .. */
483 		    &data_off,	/* w */
484 		    &data_len); /* l */
485 		if (rc)
486 			break;
487 
488 		/*
489 		 * The Create Context "name", per [MS-SMB] 2.2.13.2
490 		 * They're defined as network-order integers for our
491 		 * switch below.  We don't have routines to decode
492 		 * native order, so read as char[4] then ntohl.
493 		 * NB: in SMB3, some of these are 8 bytes.
494 		 */
495 		if ((top_offset + name_off) < in_mbc->chain_offset)
496 			break;
497 		rc = MBC_SHADOW_CHAIN(&name_mbc, in_mbc,
498 		    top_offset + name_off, name_len);
499 		if (rc)
500 			break;
501 		rc = smb_mbc_decodef(&name_mbc, "4c", &cc_name);
502 		if (rc)
503 			break;
504 		cc_name.i = ntohl(cc_name.i);
505 
506 		switch (cc_name.i) {
507 		case SMB2_CREATE_EA_BUFFER:		/* ("ExtA") */
508 			cc->cc_in_flags |= CCTX_EA_BUFFER;
509 			cce = &cc->cc_in_ext_attr;
510 			break;
511 		case SMB2_CREATE_SD_BUFFER:		/* ("SecD") */
512 			cc->cc_in_flags |= CCTX_SD_BUFFER;
513 			cce = &cc->cc_in_sec_desc;
514 			break;
515 		case SMB2_CREATE_DURABLE_HANDLE_REQUEST: /* ("DHnQ") */
516 			cc->cc_in_flags |= CCTX_DH_REQUEST;
517 			cce = &cc->cc_in_dh_request;
518 			break;
519 		case SMB2_CREATE_DURABLE_HANDLE_RECONNECT: /* ("DHnC") */
520 			cc->cc_in_flags |= CCTX_DH_RECONNECT;
521 			cce = &cc->cc_in_dh_reconnect;
522 			break;
523 		case SMB2_CREATE_ALLOCATION_SIZE:	/* ("AISi") */
524 			cc->cc_in_flags |= CCTX_ALLOCATION_SIZE;
525 			cce = &cc->cc_in_alloc_size;
526 			break;
527 		case SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ: /* ("MxAc") */
528 			cc->cc_in_flags |= CCTX_QUERY_MAX_ACCESS;
529 			/* no input data for this */
530 			break;
531 		case SMB2_CREATE_TIMEWARP_TOKEN:	/* ("TWrp") */
532 			cc->cc_in_flags |= CCTX_TIMEWARP_TOKEN;
533 			cce = &cc->cc_in_time_warp;
534 			break;
535 		case SMB2_CREATE_QUERY_ON_DISK_ID:	/* ("QFid") */
536 			cc->cc_in_flags |= CCTX_QUERY_ON_DISK_ID;
537 			/* no input data for this */
538 			break;
539 		case SMB2_CREATE_REQUEST_LEASE:		/* ("RqLs") */
540 			cc->cc_in_flags |= CCTX_REQUEST_LEASE;
541 			cce = &cc->cc_in_req_lease;
542 			break;
543 		case SMB2_CREATE_CTX_AAPL:		/* ("AAPL") */
544 			cc->cc_in_flags |= CCTX_AAPL_EXT;
545 			cce = &cc->cc_in_aapl;
546 			break;
547 		default:
548 			/*
549 			 * Unknown create context values are normal, and
550 			 * should be ignored.  However, in debug mode,
551 			 * let's log them so we know which ones we're
552 			 * not handling (and may want to add).
553 			 */
554 #ifdef	DEBUG
555 			cmn_err(CE_NOTE, "unknown create context ID 0x%x",
556 			    cc_name.i);
557 #endif
558 			cce = NULL;
559 			break;
560 		}
561 
562 		if (cce != NULL && data_len != 0) {
563 			if ((data_off & 7) != 0)
564 				break;
565 			if ((top_offset + data_off) < in_mbc->chain_offset)
566 				break;
567 			rc = MBC_SHADOW_CHAIN(&cce->cce_mbc, in_mbc,
568 			    top_offset + data_off, data_len);
569 			if (rc)
570 				break;
571 			cce->cce_len = data_len;
572 		}
573 
574 		if (next_off == 0) {
575 			/* Normal loop termination */
576 			status = 0;
577 			break;
578 		}
579 
580 		if ((next_off & 7) != 0)
581 			break;
582 		if ((top_offset + next_off) < in_mbc->chain_offset)
583 			break;
584 		if ((top_offset + next_off) > in_mbc->max_bytes)
585 			break;
586 		in_mbc->chain_offset = top_offset + next_off;
587 	}
588 
589 	return (status);
590 }
591 
592 /*
593  * Encode an SMB2 Create Context buffer from our internal form.
594  */
595 /* ARGSUSED */
596 static uint32_t
597 smb2_encode_create_ctx(mbuf_chain_t *mbc, smb2_create_ctx_t *cc)
598 {
599 	smb2_create_ctx_elem_t *cce;
600 	int last_top = -1;
601 	int rc;
602 
603 	if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
604 		cce = &cc->cc_out_max_access;
605 		last_top = mbc->chain_offset;
606 		rc = smb2_encode_create_ctx_elem(mbc, cce,
607 		    SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQ);
608 		if (rc)
609 			return (NT_STATUS_INTERNAL_ERROR);
610 		(void) smb_mbc_poke(mbc, last_top, "l",
611 		    mbc->chain_offset - last_top);
612 	}
613 
614 	if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
615 		cce = &cc->cc_out_file_id;
616 		last_top = mbc->chain_offset;
617 		rc = smb2_encode_create_ctx_elem(mbc, cce,
618 		    SMB2_CREATE_QUERY_ON_DISK_ID);
619 		if (rc)
620 			return (NT_STATUS_INTERNAL_ERROR);
621 		(void) smb_mbc_poke(mbc, last_top, "l",
622 		    mbc->chain_offset - last_top);
623 	}
624 
625 	if (cc->cc_out_flags & CCTX_AAPL_EXT) {
626 		cce = &cc->cc_out_aapl;
627 		last_top = mbc->chain_offset;
628 		rc = smb2_encode_create_ctx_elem(mbc, cce,
629 		    SMB2_CREATE_CTX_AAPL);
630 		if (rc)
631 			return (NT_STATUS_INTERNAL_ERROR);
632 		(void) smb_mbc_poke(mbc, last_top, "l",
633 		    mbc->chain_offset - last_top);
634 	}
635 
636 	if (last_top >= 0)
637 		(void) smb_mbc_poke(mbc, last_top, "l", 0);
638 
639 	return (0);
640 }
641 
642 static int
643 smb2_encode_create_ctx_elem(mbuf_chain_t *out_mbc,
644 	smb2_create_ctx_elem_t *cce, uint32_t id)
645 {
646 	union {
647 		uint32_t i;
648 		char ch[4];
649 	} cc_name;
650 	int rc;
651 
652 	/* as above */
653 	cc_name.i = htonl(id);
654 
655 	/*
656 	 * This is the header, per [MS-SMB2] 2.2.13.2
657 	 * Sorry about the fixed offsets.  We know we'll
658 	 * layout the data part as [name, payload] and
659 	 * name is a fixed length, so this easy.
660 	 * The final layout looks like this:
661 	 * 	a: this header (16 bytes)
662 	 *	b: the name (4 bytes, 4 pad)
663 	 *	c: the payload (variable)
664 	 *
665 	 * Note that "Next elem." is filled in later.
666 	 */
667 	rc = smb_mbc_encodef(
668 	    out_mbc, "lwwwwl",
669 	    0,		/* Next offset	l */
670 	    16,		/* NameOffset	w */
671 	    4,		/* NameLength	w */
672 	    0,		/* Reserved	w */
673 	    24,		/* DataOffset	w */
674 	    cce->cce_len);	/*	l */
675 	if (rc)
676 		return (rc);
677 
678 	/*
679 	 * Now the "name" and payload.
680 	 */
681 	rc = smb_mbc_encodef(
682 	    out_mbc, "4c4.#C",
683 	    cc_name.ch,		/* 4c4. */
684 	    cce->cce_len,	/* # */
685 	    &cce->cce_mbc);	/* C */
686 
687 	return (rc);
688 }
689 
690 static void
691 smb2_free_create_ctx(smb2_create_ctx_t *cc)
692 {
693 	smb2_create_ctx_elem_t *cce;
694 
695 	if (cc->cc_out_flags & CCTX_QUERY_MAX_ACCESS) {
696 		cce = &cc->cc_out_max_access;
697 		MBC_FLUSH(&cce->cce_mbc);
698 	}
699 	if (cc->cc_out_flags & CCTX_QUERY_ON_DISK_ID) {
700 		cce = &cc->cc_out_file_id;
701 		MBC_FLUSH(&cce->cce_mbc);
702 	}
703 	if (cc->cc_out_flags & CCTX_AAPL_EXT) {
704 		cce = &cc->cc_out_aapl;
705 		MBC_FLUSH(&cce->cce_mbc);
706 	}
707 }
708