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 }