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