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
smb2_create(smb_request_t * sr)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
smb2_decode_create_ctx(mbuf_chain_t * in_mbc,smb2_create_ctx_t * cc)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
smb2_encode_create_ctx(mbuf_chain_t * mbc,smb2_create_ctx_t * cc)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
smb2_encode_create_ctx_elem(mbuf_chain_t * out_mbc,smb2_create_ctx_elem_t * cce,uint32_t id)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
smb2_free_create_ctx(smb2_create_ctx_t * cc)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