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